1use std::{
16 collections::{HashMap, HashSet, VecDeque},
17 sync::Arc,
18};
19
20#[cfg(not(feature = "embedded-db"))]
21use anyhow::Context;
22use ark_serialize::CanonicalDeserialize;
23use async_trait::async_trait;
24use futures::stream::TryStreamExt;
25use hotshot_types::traits::node_implementation::NodeType;
26use jf_merkle_tree_compat::{
27 DigestAlgorithm, MerkleCommitment, ToTraversalPath,
28 prelude::{MerkleNode, MerkleProof},
29};
30use sqlx::types::{BitVec, JsonValue};
31
32use super::{
33 super::transaction::{Transaction, TransactionMode, Write, query_as},
34 DecodeError, QueryBuilder,
35};
36#[cfg(feature = "embedded-db")]
37use crate::data_source::storage::sql::build_where_in;
38use crate::{
39 QueryError, QueryResult,
40 data_source::storage::{
41 MerklizedStateHeightStorage, MerklizedStateStorage, pruning::PrunedHeightStorage,
42 sql::sqlx::Row,
43 },
44 merklized_state::{MerklizedState, Snapshot},
45};
46
47#[async_trait]
48impl<Mode, Types, State, const ARITY: usize> MerklizedStateStorage<Types, State, ARITY>
49 for Transaction<Mode>
50where
51 Mode: TransactionMode,
52 Types: NodeType,
53 State: MerklizedState<Types, ARITY> + 'static,
54{
55 async fn get_path(
57 &mut self,
58 snapshot: Snapshot<Types, State, ARITY>,
59 key: State::Key,
60 ) -> QueryResult<MerkleProof<State::Entry, State::Key, State::T, ARITY>> {
61 let state_type = State::state_type();
62 let tree_height = State::tree_height();
63
64 let traversal_path = State::Key::to_traversal_path(&key, tree_height);
66 let (created, merkle_commitment) = self.snapshot_info(snapshot).await?;
67
68 let (query, sql) = build_get_path_query(state_type, traversal_path.clone(), created)?;
71 let rows = query.query(&sql).fetch_all(self.as_mut()).await?;
72
73 let nodes: Vec<Node> = rows.into_iter().map(|r| r.into()).collect();
74
75 #[cfg(not(feature = "embedded-db"))]
82 let nodes = if let Some(legacy_table) = state_type.strip_suffix("_bigint") {
83 let mut query = QueryBuilder::default();
84 let paths_param = query.bind(traversal_path_values(&traversal_path, tree_height))?;
85 let created_param = query.bind(created)?;
86 let sql = format!(
87 "SELECT n.path, n.created, n.hash_id::BIGINT AS hash_id, n.children, \
88 n.children_bitvec, n.idx, n.entry FROM unnest({paths_param}::jsonb[]) AS \
89 p(path), LATERAL (SELECT * FROM {legacy_table} WHERE {legacy_table}.path = \
90 p.path AND {legacy_table}.created <= {created_param} ORDER BY \
91 {legacy_table}.path DESC, {legacy_table}.created DESC LIMIT 1) AS n"
92 );
93 let legacy_rows = query
94 .query(&sql)
95 .fetch_all(self.as_mut())
96 .await
97 .map_err(|e| QueryError::Error {
98 message: format!("merkle path fallback lookup failed: {e}"),
99 })?;
100
101 let mut latest: HashMap<String, Node> = HashMap::new();
102 for node in nodes
103 .into_iter()
104 .chain(legacy_rows.into_iter().map(Node::from))
105 {
106 let key = node.path.to_string();
107 if latest
108 .get(&key)
109 .is_none_or(|cur| node.created > cur.created)
110 {
111 latest.insert(key, node);
112 }
113 }
114 let mut nodes: Vec<Node> = latest.into_values().collect();
115 nodes.sort_by_key(|n| {
117 std::cmp::Reverse(n.path.as_array().map(|v| v.len()).unwrap_or(0))
118 });
119 nodes
120 } else {
121 nodes
122 };
123
124 let mut hash_ids = HashSet::new();
127 for node in nodes.iter() {
128 hash_ids.insert(node.hash_id);
129 if let Some(children) = &node.children {
130 let children: Vec<i64> =
131 serde_json::from_value(children.clone()).map_err(|e| QueryError::Error {
132 message: format!("Error deserializing 'children' into Vec<i64>: {e}"),
133 })?;
134 hash_ids.extend(children);
135 }
136 }
137
138 let hashes: HashMap<i64, Vec<u8>> = if !hash_ids.is_empty() {
141 #[cfg(not(feature = "embedded-db"))]
142 {
143 let hash_ids_arr: Vec<i64> = hash_ids.iter().copied().collect();
144 let mut result: HashMap<i64, Vec<u8>> = sqlx::query_as(
145 "SELECT id::BIGINT, value FROM hash_bigint WHERE id = ANY($1::BIGINT[])",
146 )
147 .bind(&hash_ids_arr)
148 .fetch_all(self.as_mut())
149 .await
150 .map_err(|e| QueryError::Error {
151 message: format!("hash lookup failed: {e}"),
152 })?
153 .into_iter()
154 .collect();
155
156 let missing: Vec<i64> = hash_ids
157 .iter()
158 .filter(|id| !result.contains_key(id))
159 .copied()
160 .collect();
161
162 if !missing.is_empty() {
163 let rows: Vec<(i64, Vec<u8>)> = sqlx::query_as(
164 "SELECT id::BIGINT, value FROM hash WHERE id = ANY($1::BIGINT[])",
165 )
166 .bind(&missing)
167 .fetch_all(self.as_mut())
168 .await
169 .map_err(|e| QueryError::Error {
170 message: format!("hash fallback lookup failed: {e}"),
171 })?;
172 result.extend(rows);
173 }
174 result
175 }
176
177 #[cfg(feature = "embedded-db")]
178 {
179 let (query, sql) =
180 build_where_in("SELECT id, value FROM hash_bigint", "id", hash_ids)?;
181 query
182 .query_as(&sql)
183 .fetch(self.as_mut())
184 .try_collect::<HashMap<i64, Vec<u8>>>()
185 .await?
186 }
187 } else {
188 HashMap::new()
189 };
190
191 let mut proof_path = VecDeque::with_capacity(State::tree_height());
192 for Node {
193 hash_id,
194 children,
195 children_bitvec,
196 idx,
197 entry,
198 ..
199 } in nodes.iter()
200 {
201 {
202 let value = hashes.get(hash_id).ok_or(QueryError::Error {
203 message: format!("node's value references non-existent hash {hash_id}"),
204 })?;
205
206 match (children, children_bitvec, idx, entry) {
207 (Some(children), Some(children_bitvec), None, None) => {
209 let children: Vec<i64> =
210 serde_json::from_value(children.clone()).map_err(|e| {
211 QueryError::Error {
212 message: format!(
213 "Error deserializing 'children' into Vec<i64>: {e}"
214 ),
215 }
216 })?;
217 let mut children = children.iter();
218
219 let child_nodes = children_bitvec
222 .iter()
223 .map(|bit| {
224 if bit {
225 let hash_id = children.next().ok_or(QueryError::Error {
226 message: "node has fewer children than set bits".into(),
227 })?;
228 let value = hashes.get(hash_id).ok_or(QueryError::Error {
229 message: format!(
230 "node's child references non-existent hash {hash_id}"
231 ),
232 })?;
233 Ok(Arc::new(MerkleNode::ForgettenSubtree {
234 value: State::T::deserialize_compressed(value.as_slice())
235 .decode_error("malformed merkle node value")?,
236 }))
237 } else {
238 Ok(Arc::new(MerkleNode::Empty))
239 }
240 })
241 .collect::<QueryResult<Vec<_>>>()?;
242 proof_path.push_back(MerkleNode::Branch {
244 value: State::T::deserialize_compressed(value.as_slice())
245 .decode_error("malformed merkle node value")?,
246 children: child_nodes,
247 });
248 },
249 (None, None, Some(index), Some(entry)) => {
251 proof_path.push_back(MerkleNode::Leaf {
252 value: State::T::deserialize_compressed(value.as_slice())
253 .decode_error("malformed merkle node value")?,
254 pos: serde_json::from_value(index.clone())
255 .decode_error("malformed merkle node index")?,
256 elem: serde_json::from_value(entry.clone())
257 .decode_error("malformed merkle element")?,
258 });
259 },
260 (None, None, Some(_), None) => {
262 proof_path.push_back(MerkleNode::Empty);
263 },
264 _ => {
265 return Err(QueryError::Error {
266 message: "Invalid type of merkle node found".to_string(),
267 });
268 },
269 }
270 }
271 }
272
273 let init = if let Some(MerkleNode::Leaf { value, .. }) = proof_path.front() {
275 *value
276 } else {
277 while proof_path.len() <= State::tree_height() {
283 proof_path.push_front(MerkleNode::Empty);
284 }
285 State::T::default()
286 };
287 let commitment_from_path = traversal_path
288 .iter()
289 .zip(proof_path.iter().skip(1))
290 .try_fold(init, |val, (branch, node)| -> QueryResult<State::T> {
291 match node {
292 MerkleNode::Branch { value: _, children } => {
293 let data = children
294 .iter()
295 .map(|node| match node.as_ref() {
296 MerkleNode::ForgettenSubtree { value } => Ok(*value),
297 MerkleNode::Empty => Ok(State::T::default()),
298 _ => Err(QueryError::Error {
299 message: "Invalid child node".to_string(),
300 }),
301 })
302 .collect::<QueryResult<Vec<_>>>()?;
303
304 if data[*branch] != val {
305 tracing::warn!(
309 ?key,
310 parent = ?data[*branch],
311 child = ?val,
312 branch = %*branch,
313 %created,
314 %merkle_commitment,
315 "missing data in merklized state; parent-child mismatch",
316 );
317 return Err(QueryError::Missing);
318 }
319
320 State::Digest::digest(&data).map_err(|err| QueryError::Error {
321 message: format!("failed to update digest: {err:#}"),
322 })
323 },
324 MerkleNode::Empty => Ok(init),
325 _ => Err(QueryError::Error {
326 message: "Invalid type of Node in the proof".to_string(),
327 }),
328 }
329 })?;
330
331 if commitment_from_path != merkle_commitment.digest() {
332 return Err(QueryError::Error {
333 message: format!(
334 "Commitment calculated from merkle path ({commitment_from_path:?}) does not \
335 match the commitment in the header ({:?})",
336 merkle_commitment.digest()
337 ),
338 });
339 }
340
341 Ok(MerkleProof {
342 pos: key,
343 proof: proof_path.into(),
344 })
345 }
346}
347
348#[async_trait]
349impl<Mode: TransactionMode> MerklizedStateHeightStorage for Transaction<Mode> {
350 async fn get_last_state_height(&mut self) -> QueryResult<usize> {
351 let Some((height,)) = query_as::<(i64,)>("SELECT height from last_merklized_state_height")
352 .fetch_optional(self.as_mut())
353 .await?
354 else {
355 return Ok(0);
356 };
357 Ok(height as usize)
358 }
359}
360
361impl<Mode: TransactionMode> Transaction<Mode> {
362 async fn snapshot_info<Types, State, const ARITY: usize>(
368 &mut self,
369 snapshot: Snapshot<Types, State, ARITY>,
370 ) -> QueryResult<(i64, State::Commit)>
371 where
372 Types: NodeType,
373 State: MerklizedState<Types, ARITY>,
374 {
375 let header_state_commitment_field = State::header_state_commitment_field();
376
377 let (created, commit) = match snapshot {
378 Snapshot::Commit(commit) => {
379 let (height,) = query_as(&format!(
385 "SELECT height
386 FROM header
387 WHERE {header_state_commitment_field} = $1
388 LIMIT 1"
389 ))
390 .bind(commit.to_string())
391 .fetch_one(self.as_mut())
392 .await?;
393
394 (height, commit)
395 },
396 Snapshot::Index(created) => {
397 let created = created as i64;
398 let (commit,) = query_as::<(String,)>(&format!(
399 "SELECT {header_state_commitment_field} AS root_commitment
400 FROM header
401 WHERE height = $1
402 LIMIT 1"
403 ))
404 .bind(created)
405 .fetch_one(self.as_mut())
406 .await?;
407 let commit = serde_json::from_value(commit.into())
408 .decode_error("malformed state commitment")?;
409 (created, commit)
410 },
411 };
412
413 let height = self.get_last_state_height().await?;
415
416 if height < (created as usize) {
417 return Err(QueryError::NotFound);
418 }
419
420 let pruned_height =
421 self.load_state_pruned_height()
422 .await
423 .map_err(|e| QueryError::Error {
424 message: format!("failed to load pruned height: {e}"),
425 })?;
426
427 if pruned_height.is_some_and(|h| created <= h as i64) {
428 return Err(QueryError::NotFound);
429 }
430
431 Ok((created, commit))
432 }
433}
434
435#[cfg(feature = "embedded-db")]
437pub(crate) fn build_hash_batch_insert(
438 hashes: &[Vec<u8>],
439) -> QueryResult<(QueryBuilder<'_>, String)> {
440 let mut query = QueryBuilder::default();
441 let params = hashes
442 .iter()
443 .map(|hash| Ok(format!("({})", query.bind(hash)?)))
444 .collect::<QueryResult<Vec<String>>>()?;
445 let sql = format!(
446 "INSERT INTO hash_bigint(value) values {} ON CONFLICT (value) DO UPDATE SET value = \
447 EXCLUDED.value returning value, id",
448 params.join(",")
449 );
450 Ok((query, sql))
451}
452
453#[cfg(not(feature = "embedded-db"))]
456pub(crate) async fn batch_insert_hashes(
457 hashes: Vec<Vec<u8>>,
458 tx: &mut Transaction<Write>,
459) -> QueryResult<HashMap<Vec<u8>, i64>> {
460 if hashes.is_empty() {
461 return Ok(HashMap::new());
462 }
463
464 let sql = "INSERT INTO hash_bigint(value) SELECT * FROM UNNEST($1::bytea[]) ON CONFLICT \
467 (value) DO UPDATE SET value = EXCLUDED.value RETURNING value, id::BIGINT";
468
469 let result: HashMap<Vec<u8>, i64> = sqlx::query_as(sql)
470 .bind(&hashes)
471 .fetch(tx.as_mut())
472 .try_collect()
473 .await
474 .map_err(|e| QueryError::Error {
475 message: format!("batch hash insert failed: {e}"),
476 })?;
477
478 Ok(result)
479}
480
481pub(crate) type ProofWithPath<Entry, Key, T, const ARITY: usize> =
483 (MerkleProof<Entry, Key, T, ARITY>, Vec<usize>);
484
485pub(crate) fn collect_nodes_from_proofs<Entry, Key, T, const ARITY: usize>(
488 proofs: &[ProofWithPath<Entry, Key, T, ARITY>],
489) -> QueryResult<(Vec<NodeWithHashes>, HashSet<Vec<u8>>)>
490where
491 Entry: jf_merkle_tree_compat::Element + serde::Serialize,
492 Key: jf_merkle_tree_compat::Index + serde::Serialize,
493 T: jf_merkle_tree_compat::NodeValue,
494{
495 let mut nodes = Vec::new();
496 let mut hashes = HashSet::new();
497
498 for (proof, traversal_path) in proofs {
499 let pos = &proof.pos;
500 let path = &proof.proof;
501 let mut trav_path = traversal_path.iter().map(|n| *n as i32);
502
503 for node in path.iter() {
504 match node {
505 MerkleNode::Empty => {
506 let index =
507 serde_json::to_value(pos.clone()).map_err(|e| QueryError::Error {
508 message: format!("malformed merkle position: {e}"),
509 })?;
510 let node_path: Vec<i32> = trav_path.clone().rev().collect();
511 nodes.push((
512 Node {
513 path: node_path.into(),
514 idx: Some(index),
515 ..Default::default()
516 },
517 None,
518 [0_u8; 32].to_vec(),
519 ));
520 hashes.insert([0_u8; 32].to_vec());
521 },
522 MerkleNode::ForgettenSubtree { .. } => {
523 return Err(QueryError::Error {
524 message: "Node in the Merkle path contains a forgotten subtree".into(),
525 });
526 },
527 MerkleNode::Leaf { value, pos, elem } => {
528 let mut leaf_commit = Vec::new();
529 value.serialize_compressed(&mut leaf_commit).map_err(|e| {
530 QueryError::Error {
531 message: format!("malformed merkle leaf commitment: {e}"),
532 }
533 })?;
534
535 let node_path: Vec<i32> = trav_path.clone().rev().collect();
536
537 let index =
538 serde_json::to_value(pos.clone()).map_err(|e| QueryError::Error {
539 message: format!("malformed merkle position: {e}"),
540 })?;
541 let entry = serde_json::to_value(elem).map_err(|e| QueryError::Error {
542 message: format!("malformed merkle element: {e}"),
543 })?;
544
545 nodes.push((
546 Node {
547 path: node_path.into(),
548 idx: Some(index),
549 entry: Some(entry),
550 ..Default::default()
551 },
552 None,
553 leaf_commit.clone(),
554 ));
555
556 hashes.insert(leaf_commit);
557 },
558 MerkleNode::Branch { value, children } => {
559 let mut branch_hash = Vec::new();
560 value.serialize_compressed(&mut branch_hash).map_err(|e| {
561 QueryError::Error {
562 message: format!("malformed merkle branch hash: {e}"),
563 }
564 })?;
565
566 let mut children_bitvec = BitVec::new();
567 let mut children_values = Vec::new();
568 for child in children {
569 let child = child.as_ref();
570 match child {
571 MerkleNode::Empty => {
572 children_bitvec.push(false);
573 },
574 MerkleNode::Branch { value, .. }
575 | MerkleNode::Leaf { value, .. }
576 | MerkleNode::ForgettenSubtree { value } => {
577 let mut hash = Vec::new();
578 value.serialize_compressed(&mut hash).map_err(|e| {
579 QueryError::Error {
580 message: format!("malformed merkle node hash: {e}"),
581 }
582 })?;
583
584 children_values.push(hash);
585 children_bitvec.push(true);
586 },
587 }
588 }
589
590 let node_path: Vec<i32> = trav_path.clone().rev().collect();
591 nodes.push((
592 Node {
593 path: node_path.into(),
594 children: None,
595 children_bitvec: Some(children_bitvec),
596 ..Default::default()
597 },
598 Some(children_values.clone()),
599 branch_hash.clone(),
600 ));
601 hashes.insert(branch_hash);
602 hashes.extend(children_values);
603 },
604 }
605
606 trav_path.next();
607 }
608 }
609
610 Ok((nodes, hashes))
611}
612
613#[derive(Debug, Default, Clone)]
615pub(crate) struct Node {
616 pub(crate) path: JsonValue,
617 pub(crate) created: i64,
618 pub(crate) hash_id: i64,
619 pub(crate) children: Option<JsonValue>,
620 pub(crate) children_bitvec: Option<BitVec>,
621 pub(crate) idx: Option<JsonValue>,
622 pub(crate) entry: Option<JsonValue>,
623}
624
625pub(crate) type NodeWithHashes = (Node, Option<Vec<Vec<u8>>>, Vec<u8>);
628
629#[cfg(feature = "embedded-db")]
630impl From<sqlx::sqlite::SqliteRow> for Node {
631 fn from(row: sqlx::sqlite::SqliteRow) -> Self {
632 let bit_string: Option<String> = row.get_unchecked("children_bitvec");
633 let children_bitvec: Option<BitVec> =
634 bit_string.map(|b| b.chars().map(|c| c == '1').collect());
635
636 Self {
637 path: row.get_unchecked("path"),
638 created: row.get_unchecked("created"),
639 hash_id: row.get_unchecked("hash_id"),
640 children: row.get_unchecked("children"),
641 children_bitvec,
642 idx: row.get_unchecked("idx"),
643 entry: row.get_unchecked("entry"),
644 }
645 }
646}
647
648#[cfg(not(feature = "embedded-db"))]
649impl From<sqlx::postgres::PgRow> for Node {
650 fn from(row: sqlx::postgres::PgRow) -> Self {
651 Self {
652 path: row.get_unchecked("path"),
653 created: row.get_unchecked("created"),
654 hash_id: row.get_unchecked("hash_id"),
655 children: row.get_unchecked("children"),
656 children_bitvec: row.get_unchecked("children_bitvec"),
657 idx: row.get_unchecked("idx"),
658 entry: row.get_unchecked("entry"),
659 }
660 }
661}
662
663impl Node {
664 pub(crate) async fn upsert(
665 name: &str,
666 nodes: impl IntoIterator<Item = Self>,
667 tx: &mut Transaction<Write>,
668 ) -> anyhow::Result<()> {
669 let nodes: Vec<_> = nodes.into_iter().collect();
670
671 #[cfg(not(feature = "embedded-db"))]
673 return Self::upsert_batch_unnest(name, nodes, tx).await;
674
675 #[cfg(feature = "embedded-db")]
676 {
677 for node_chunk in nodes.chunks(20) {
678 let rows: Vec<_> = node_chunk
679 .iter()
680 .map(|n| {
681 let children_bitvec: Option<String> = n
682 .children_bitvec
683 .clone()
684 .map(|b| b.iter().map(|bit| if bit { '1' } else { '0' }).collect());
685
686 (
687 n.path.clone(),
688 n.created,
689 n.hash_id,
690 n.children.clone(),
691 children_bitvec,
692 n.idx.clone(),
693 n.entry.clone(),
694 )
695 })
696 .collect();
697
698 tx.upsert(
699 name,
700 [
701 "path",
702 "created",
703 "hash_id",
704 "children",
705 "children_bitvec",
706 "idx",
707 "entry",
708 ],
709 ["path", "created"],
710 rows,
711 )
712 .await?;
713 }
714 Ok(())
715 }
716 }
717
718 #[cfg(not(feature = "embedded-db"))]
719 async fn upsert_batch_unnest(
720 name: &str,
721 nodes: Vec<Self>,
722 tx: &mut Transaction<Write>,
723 ) -> anyhow::Result<()> {
724 if nodes.is_empty() {
725 return Ok(());
726 }
727
728 let mut deduped = HashMap::new();
731 for node in nodes {
732 deduped.insert((node.path.to_string(), node.created), node);
733 }
734
735 let mut paths = Vec::with_capacity(deduped.len());
736 let mut createds = Vec::with_capacity(deduped.len());
737 let mut hash_ids = Vec::with_capacity(deduped.len());
738 let mut childrens = Vec::with_capacity(deduped.len());
739 let mut children_bitvecs = Vec::with_capacity(deduped.len());
740 let mut idxs = Vec::with_capacity(deduped.len());
741 let mut entries = Vec::with_capacity(deduped.len());
742
743 for node in deduped.into_values() {
744 paths.push(node.path);
745 createds.push(node.created);
746 hash_ids.push(node.hash_id);
747 childrens.push(node.children);
748 children_bitvecs.push(node.children_bitvec);
749 idxs.push(node.idx);
750 entries.push(node.entry);
751 }
752
753 let sql = format!(
754 r#"
755 INSERT INTO "{name}" (path, created, hash_id, children, children_bitvec, idx, entry)
756 SELECT * FROM UNNEST($1::jsonb[], $2::bigint[], $3::bigint[], $4::jsonb[], $5::bit varying[], $6::jsonb[], $7::jsonb[])
757 ON CONFLICT (path, created) DO UPDATE SET
758 hash_id = EXCLUDED.hash_id,
759 children = EXCLUDED.children,
760 children_bitvec = EXCLUDED.children_bitvec,
761 idx = EXCLUDED.idx,
762 entry = EXCLUDED.entry
763 "#
764 );
765
766 sqlx::query(&sql)
767 .bind(&paths)
768 .bind(&createds)
769 .bind(&hash_ids)
770 .bind(&childrens)
771 .bind(&children_bitvecs)
772 .bind(&idxs)
773 .bind(&entries)
774 .execute(tx.as_mut())
775 .await
776 .context("batch upsert with UNNEST failed")?;
777
778 Ok(())
779 }
780}
781
782fn traversal_path_values(traversal_path: &[usize], tree_height: usize) -> Vec<serde_json::Value> {
786 let len = tree_height;
787 let mut result = Vec::with_capacity(len + 1);
788 let mut trav = traversal_path.iter().map(|x| *x as i32);
789 for _ in 0..=len {
790 let path: Vec<i32> = trav.clone().rev().collect();
791 result.push(serde_json::Value::from(path));
792 trav.next();
793 }
794 result
795}
796
797fn build_get_path_query<'q>(
798 table: &'static str,
799 traversal_path: Vec<usize>,
800 created: i64,
801) -> QueryResult<(QueryBuilder<'q>, String)> {
802 let mut query = QueryBuilder::default();
803 let paths = traversal_path_values(&traversal_path, traversal_path.len());
804
805 #[cfg(not(feature = "embedded-db"))]
806 let sql = {
807 let paths_param = query.bind(paths)?;
808 let created_param = query.bind(created)?;
809 format!(
813 "SELECT n.* FROM unnest({paths_param}::jsonb[]) AS p(path), LATERAL (SELECT * FROM \
814 {table} WHERE {table}.path = p.path AND {table}.created <= {created_param} ORDER BY \
815 {table}.path DESC, {table}.created DESC LIMIT 1) AS n ORDER BY n.path DESC"
816 )
817 };
818
819 #[cfg(feature = "embedded-db")]
820 let sql = {
821 let created_param = query.bind(created)?;
824 let mut sub_queries = Vec::with_capacity(paths.len());
825 for path in paths {
826 let node_path = query.bind(path)?;
827 sub_queries.push(format!(
828 "SELECT * FROM (SELECT * FROM {table} WHERE path = {node_path} AND created <= \
829 {created_param} ORDER BY path DESC, created DESC LIMIT 1) AS latest_node",
830 ));
831 }
832 format!(
833 "SELECT * FROM ({}) as t ORDER BY length(t.path) DESC",
834 sub_queries.join(" UNION ")
835 )
836 };
837
838 Ok((query, sql))
839}
840
841#[cfg(test)]
842mod test {
843 use futures::stream::StreamExt;
844 use jf_merkle_tree_compat::{
845 LookupResult, MerkleTreeScheme, UniversalMerkleTreeScheme,
846 universal_merkle_tree::UniversalMerkleTree,
847 };
848 use rand::{RngCore, seq::IteratorRandom};
849
850 use super::*;
851 use crate::{
852 data_source::{
853 VersionedDataSource,
854 storage::sql::{testing::TmpDb, *},
855 },
856 merklized_state::UpdateStateData,
857 testing::mocks::{MockMerkleTree, MockTypes},
858 };
859
860 #[test_log::test(tokio::test(flavor = "multi_thread"))]
861 async fn test_merklized_state_storage() {
862 let db = TmpDb::init().await;
866 let storage = SqlStorage::connect(db.config(), StorageConnectionType::Query)
867 .await
868 .unwrap();
869
870 let mut test_tree: UniversalMerkleTree<_, _, _, 8, _> =
872 MockMerkleTree::new(MockMerkleTree::tree_height());
873 let block_height = 1;
874
875 let mut tx = storage.write().await.unwrap();
878 for i in 0..27 {
879 test_tree.update(i, i).unwrap();
880
881 let test_data = serde_json::json!({ MockMerkleTree::header_state_commitment_field() : serde_json::to_value(test_tree.commitment()).unwrap()});
883 tx.upsert(
884 "header",
885 [
886 "height",
887 "hash",
888 "payload_hash",
889 "timestamp",
890 "ns_table",
891 "data",
892 ],
893 ["height"],
894 [(
895 block_height as i64,
896 format!("randomHash{i}"),
897 "t".to_string(),
898 0,
899 "ns_table".to_string(),
900 test_data,
901 )],
902 )
903 .await
904 .unwrap();
905 let (_, proof) = test_tree.lookup(i).expect_ok().unwrap();
907 let traversal_path =
909 <usize as ToTraversalPath<8>>::to_traversal_path(&i, test_tree.height());
910
911 UpdateStateData::<_, MockMerkleTree, 8>::insert_merkle_nodes(
912 &mut tx,
913 proof.clone(),
914 traversal_path.clone(),
915 block_height as u64,
916 )
917 .await
918 .expect("failed to insert nodes");
919 }
920 UpdateStateData::<_, MockMerkleTree, 8>::set_last_state_height(&mut tx, block_height)
922 .await
923 .unwrap();
924 tx.commit().await.unwrap();
925
926 for i in 0..27 {
928 let mut tx = storage.read().await.unwrap();
930 let merkle_path = tx
931 .get_path(
932 Snapshot::<_, MockMerkleTree, 8>::Index(block_height as u64),
933 i,
934 )
935 .await
936 .unwrap();
937
938 let (_, proof) = test_tree.lookup(i).expect_ok().unwrap();
939
940 tracing::info!("merkle path {:?}", merkle_path);
941
942 assert_eq!(merkle_path, proof.clone(), "merkle paths mismatch");
944 }
945
946 let (_, proof_bh_1) = test_tree.lookup(0).expect_ok().unwrap();
948 test_tree.update(0, 99).unwrap();
952 let mut tx = storage.write().await.unwrap();
956 let test_data = serde_json::json!({ MockMerkleTree::header_state_commitment_field() : serde_json::to_value(test_tree.commitment()).unwrap()});
957 tx.upsert(
958 "header",
959 [
960 "height",
961 "hash",
962 "payload_hash",
963 "timestamp",
964 "data",
965 "ns_table",
966 ],
967 ["height"],
968 [(
969 2i64,
970 "randomstring".to_string(),
971 "t".to_string(),
972 0,
973 test_data,
974 "ns_table".to_string(),
975 )],
976 )
977 .await
978 .unwrap();
979 let (_, proof_bh_2) = test_tree.lookup(0).expect_ok().unwrap();
980 let traversal_path =
982 <usize as ToTraversalPath<8>>::to_traversal_path(&0, test_tree.height());
983 UpdateStateData::<_, MockMerkleTree, 8>::insert_merkle_nodes(
986 &mut tx,
987 proof_bh_2.clone(),
988 traversal_path.clone(),
989 2,
990 )
991 .await
992 .expect("failed to insert nodes");
993 UpdateStateData::<_, MockMerkleTree, 8>::set_last_state_height(&mut tx, 2)
995 .await
996 .unwrap();
997 tx.commit().await.unwrap();
998
999 let node_path = traversal_path
1000 .into_iter()
1001 .rev()
1002 .map(|n| n as i32)
1003 .collect::<Vec<_>>();
1004
1005 let mut tx = storage.read().await.unwrap();
1007 let rows = query("SELECT * from test_tree where path = $1 ORDER BY created")
1008 .bind(serde_json::to_value(node_path).unwrap())
1009 .fetch(tx.as_mut());
1010
1011 let nodes: Vec<Node> = rows.map(|res| res.unwrap().into()).collect().await;
1012 assert!(nodes.len() == 2, "incorrect number of nodes");
1014 assert_eq!(nodes[0].created, 1, "wrong block height");
1015 assert_eq!(nodes[1].created, 2, "wrong block height");
1016
1017 let path_with_bh_2 = storage
1022 .read()
1023 .await
1024 .unwrap()
1025 .get_path(Snapshot::<_, MockMerkleTree, 8>::Index(2), 0)
1026 .await
1027 .unwrap();
1028
1029 assert_eq!(path_with_bh_2, proof_bh_2);
1030 let path_with_bh_1 = storage
1031 .read()
1032 .await
1033 .unwrap()
1034 .get_path(Snapshot::<_, MockMerkleTree, 8>::Index(1), 0)
1035 .await
1036 .unwrap();
1037 assert_eq!(path_with_bh_1, proof_bh_1);
1038 }
1039
1040 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1041 async fn test_merklized_state_non_membership_proof() {
1042 let db = TmpDb::init().await;
1049 let storage = SqlStorage::connect(db.config(), StorageConnectionType::Query)
1050 .await
1051 .unwrap();
1052
1053 let mut test_tree = MockMerkleTree::new(MockMerkleTree::tree_height());
1055 let block_height = 1;
1056 test_tree.update(0, 0).unwrap();
1058 let commitment = test_tree.commitment();
1059
1060 let test_data = serde_json::json!({ MockMerkleTree::header_state_commitment_field() : serde_json::to_value(commitment).unwrap()});
1061 let mut tx = storage.write().await.unwrap();
1063 tx.upsert(
1064 "header",
1065 [
1066 "height",
1067 "hash",
1068 "payload_hash",
1069 "timestamp",
1070 "data",
1071 "ns_table",
1072 ],
1073 ["height"],
1074 [(
1075 block_height as i64,
1076 "randomString".to_string(),
1077 "t".to_string(),
1078 0,
1079 test_data,
1080 "ns_table".to_string(),
1081 )],
1082 )
1083 .await
1084 .unwrap();
1085 let (_, proof_before_remove) = test_tree.lookup(0).expect_ok().unwrap();
1087 let traversal_path =
1089 <usize as ToTraversalPath<8>>::to_traversal_path(&0, test_tree.height());
1090 UpdateStateData::<_, MockMerkleTree, 8>::insert_merkle_nodes(
1092 &mut tx,
1093 proof_before_remove.clone(),
1094 traversal_path.clone(),
1095 block_height as u64,
1096 )
1097 .await
1098 .expect("failed to insert nodes");
1099 UpdateStateData::<_, MockMerkleTree, 8>::set_last_state_height(&mut tx, block_height)
1101 .await
1102 .unwrap();
1103 tx.commit().await.unwrap();
1104 let merkle_path = storage
1106 .read()
1107 .await
1108 .unwrap()
1109 .get_path(
1110 Snapshot::<_, MockMerkleTree, 8>::Index(block_height as u64),
1111 0,
1112 )
1113 .await
1114 .unwrap();
1115
1116 assert_eq!(
1118 merkle_path,
1119 proof_before_remove.clone(),
1120 "merkle paths mismatch"
1121 );
1122
1123 test_tree.remove(0).expect("failed to delete index 0 ");
1125
1126 let proof_after_remove = test_tree.universal_lookup(0).expect_not_found().unwrap();
1129
1130 let mut tx = storage.write().await.unwrap();
1131 UpdateStateData::<_, MockMerkleTree, 8>::insert_merkle_nodes(
1132 &mut tx,
1133 proof_after_remove.clone(),
1134 traversal_path.clone(),
1135 2_u64,
1136 )
1137 .await
1138 .expect("failed to insert nodes");
1139 tx.upsert(
1141 "header",
1142 ["height", "hash", "payload_hash", "timestamp", "data", "ns_table"],
1143 ["height"],
1144 [(
1145 2i64,
1146 "randomString2".to_string(),
1147 "t".to_string(),
1148 0,
1149 serde_json::json!({ MockMerkleTree::header_state_commitment_field() : serde_json::to_value(test_tree.commitment()).unwrap()}),
1150 "ns_table".to_string(),
1151 )],
1152 )
1153 .await
1154 .unwrap();
1155 UpdateStateData::<_, MockMerkleTree, 8>::set_last_state_height(&mut tx, 2)
1157 .await
1158 .unwrap();
1159 tx.commit().await.unwrap();
1160 let non_membership_path = storage
1162 .read()
1163 .await
1164 .unwrap()
1165 .get_path(Snapshot::<_, MockMerkleTree, 8>::Index(2_u64), 0)
1166 .await
1167 .unwrap();
1168 assert_eq!(
1170 non_membership_path, proof_after_remove,
1171 "merkle paths dont match"
1172 );
1173
1174 let proof_bh_1 = storage
1179 .read()
1180 .await
1181 .unwrap()
1182 .get_path(Snapshot::<_, MockMerkleTree, 8>::Index(1_u64), 0)
1183 .await
1184 .unwrap();
1185 assert_eq!(proof_bh_1, proof_before_remove, "merkle paths dont match");
1186 }
1187
1188 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1189 async fn test_merklized_state_non_membership_proof_unseen_entry() {
1190 let db = TmpDb::init().await;
1191 let storage = SqlStorage::connect(db.config(), StorageConnectionType::Query)
1192 .await
1193 .unwrap();
1194
1195 let mut test_tree = MockMerkleTree::new(MockMerkleTree::tree_height());
1197
1198 for i in 0..=2 {
1201 tracing::info!(i, ?test_tree, "testing non-membership proof");
1202 let mut tx = storage.write().await.unwrap();
1203
1204 tx.upsert(
1206 "header",
1207 ["height", "hash", "payload_hash", "timestamp", "data", "ns_table"],
1208 ["height"],
1209 [(
1210 i as i64,
1211 format!("hash{i}"),
1212 "t".to_string(),
1213 0,
1214 serde_json::json!({ MockMerkleTree::header_state_commitment_field() : serde_json::to_value(test_tree.commitment()).unwrap()}),
1215 "ns_table".to_string(),
1216 )],
1217 )
1218 .await
1219 .unwrap();
1220 UpdateStateData::<_, MockMerkleTree, 8>::set_last_state_height(&mut tx, i)
1222 .await
1223 .unwrap();
1224 tx.commit().await.unwrap();
1225
1226 let proof = storage
1228 .read()
1229 .await
1230 .unwrap()
1231 .get_path(
1232 Snapshot::<MockTypes, MockMerkleTree, 8>::Index(i as u64),
1233 100,
1234 )
1235 .await
1236 .unwrap();
1237 assert_eq!(proof.elem(), None);
1238
1239 assert!(
1240 MockMerkleTree::non_membership_verify(test_tree.commitment(), 100, proof).unwrap()
1241 );
1242
1243 test_tree.update(i, i).unwrap();
1245 let (_, proof) = test_tree.lookup(i).expect_ok().unwrap();
1246 let traversal_path = ToTraversalPath::<8>::to_traversal_path(&i, test_tree.height());
1247 let mut tx = storage.write().await.unwrap();
1248 UpdateStateData::<_, MockMerkleTree, 8>::insert_merkle_nodes(
1249 &mut tx,
1250 proof,
1251 traversal_path,
1252 (i + 1) as u64,
1253 )
1254 .await
1255 .expect("failed to insert nodes");
1256 tx.commit().await.unwrap();
1257 }
1258 }
1259
1260 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1261 async fn test_merklized_storage_with_commit() {
1262 let db = TmpDb::init().await;
1265 let storage = SqlStorage::connect(db.config(), StorageConnectionType::Query)
1266 .await
1267 .unwrap();
1268
1269 let mut test_tree = MockMerkleTree::new(MockMerkleTree::tree_height());
1271 let block_height = 1;
1272 test_tree.update(0, 0).unwrap();
1274 let commitment = test_tree.commitment();
1275
1276 let test_data = serde_json::json!({ MockMerkleTree::header_state_commitment_field() : serde_json::to_value(commitment).unwrap()});
1277 let mut tx = storage.write().await.unwrap();
1279 tx.upsert(
1280 "header",
1281 [
1282 "height",
1283 "hash",
1284 "payload_hash",
1285 "timestamp",
1286 "data",
1287 "ns_table",
1288 ],
1289 ["height"],
1290 [(
1291 block_height as i64,
1292 "randomString".to_string(),
1293 "t".to_string(),
1294 0,
1295 test_data,
1296 "ns_table".to_string(),
1297 )],
1298 )
1299 .await
1300 .unwrap();
1301 let (_, proof) = test_tree.lookup(0).expect_ok().unwrap();
1303 let traversal_path =
1305 <usize as ToTraversalPath<8>>::to_traversal_path(&0, test_tree.height());
1306 UpdateStateData::<_, MockMerkleTree, 8>::insert_merkle_nodes(
1308 &mut tx,
1309 proof.clone(),
1310 traversal_path.clone(),
1311 block_height as u64,
1312 )
1313 .await
1314 .expect("failed to insert nodes");
1315 UpdateStateData::<_, MockMerkleTree, 8>::set_last_state_height(&mut tx, block_height)
1317 .await
1318 .unwrap();
1319 tx.commit().await.unwrap();
1320
1321 let merkle_proof = storage
1322 .read()
1323 .await
1324 .unwrap()
1325 .get_path(Snapshot::<_, MockMerkleTree, 8>::Commit(commitment), 0)
1326 .await
1327 .unwrap();
1328
1329 let (_, proof) = test_tree.lookup(0).expect_ok().unwrap();
1330
1331 assert_eq!(merkle_proof, proof.clone(), "merkle paths mismatch");
1332 }
1333 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1334 async fn test_merklized_state_missing_state() {
1335 let db = TmpDb::init().await;
1343 let storage = SqlStorage::connect(db.config(), StorageConnectionType::Query)
1344 .await
1345 .unwrap();
1346
1347 let mut test_tree = MockMerkleTree::new(MockMerkleTree::tree_height());
1349 let block_height = 1;
1350 let mut tx = storage.write().await.unwrap();
1353 for i in 0..27 {
1354 test_tree.update(i, i).unwrap();
1355 let test_data = serde_json::json!({ MockMerkleTree::header_state_commitment_field() : serde_json::to_value(test_tree.commitment()).unwrap()});
1356 tx.upsert(
1358 "header",
1359 [
1360 "height",
1361 "hash",
1362 "payload_hash",
1363 "timestamp",
1364 "data",
1365 "ns_table",
1366 ],
1367 ["height"],
1368 [(
1369 block_height as i64,
1370 format!("rarndomString{i}"),
1371 "t".to_string(),
1372 0,
1373 test_data,
1374 "ns_table".to_string(),
1375 )],
1376 )
1377 .await
1378 .unwrap();
1379 let (_, proof) = test_tree.lookup(i).expect_ok().unwrap();
1381 let traversal_path =
1383 <usize as ToTraversalPath<8>>::to_traversal_path(&i, test_tree.height());
1384 UpdateStateData::<_, MockMerkleTree, 8>::insert_merkle_nodes(
1386 &mut tx,
1387 proof.clone(),
1388 traversal_path.clone(),
1389 block_height as u64,
1390 )
1391 .await
1392 .expect("failed to insert nodes");
1393 UpdateStateData::<_, MockMerkleTree, 8>::set_last_state_height(&mut tx, block_height)
1395 .await
1396 .unwrap();
1397 }
1398
1399 test_tree.update(1, 100).unwrap();
1400 let traversal_path =
1402 <usize as ToTraversalPath<8>>::to_traversal_path(&1, test_tree.height());
1403 let (_, proof) = test_tree.lookup(1).expect_ok().unwrap();
1404
1405 UpdateStateData::<_, MockMerkleTree, 8>::insert_merkle_nodes(
1407 &mut tx,
1408 proof.clone(),
1409 traversal_path.clone(),
1410 block_height as u64,
1411 )
1412 .await
1413 .expect("failed to insert nodes");
1414 tx.commit().await.unwrap();
1415
1416 let merkle_path = storage
1417 .read()
1418 .await
1419 .unwrap()
1420 .get_path(
1421 Snapshot::<_, MockMerkleTree, 8>::Index(block_height as u64),
1422 1,
1423 )
1424 .await;
1425 assert!(merkle_path.is_err());
1426
1427 let test_data = serde_json::json!({ MockMerkleTree::header_state_commitment_field() : serde_json::to_value(test_tree.commitment()).unwrap()});
1428 let mut tx = storage.write().await.unwrap();
1430 tx.upsert(
1431 "header",
1432 [
1433 "height",
1434 "hash",
1435 "payload_hash",
1436 "timestamp",
1437 "data",
1438 "ns_table",
1439 ],
1440 ["height"],
1441 [(
1442 block_height as i64,
1443 "randomStringgg".to_string(),
1444 "t".to_string(),
1445 0,
1446 test_data,
1447 "ns_table".to_string(),
1448 )],
1449 )
1450 .await
1451 .unwrap();
1452 tx.commit().await.unwrap();
1453 let merkle_proof = storage
1455 .read()
1456 .await
1457 .unwrap()
1458 .get_path(
1459 Snapshot::<_, MockMerkleTree, 8>::Index(block_height as u64),
1460 1,
1461 )
1462 .await
1463 .unwrap();
1464 assert_eq!(merkle_proof, proof, "path dont match");
1465
1466 test_tree.update(1, 200).unwrap();
1469
1470 let (_, proof) = test_tree.lookup(1).expect_ok().unwrap();
1471 let test_data = serde_json::json!({ MockMerkleTree::header_state_commitment_field() : serde_json::to_value(test_tree.commitment()).unwrap()});
1472
1473 let mut tx = storage.write().await.unwrap();
1475 tx.upsert(
1476 "header",
1477 [
1478 "height",
1479 "hash",
1480 "payload_hash",
1481 "timestamp",
1482 "data",
1483 "ns_table",
1484 ],
1485 ["height"],
1486 [(
1487 2i64,
1488 "randomHashString".to_string(),
1489 "t".to_string(),
1490 0,
1491 test_data,
1492 "ns_table".to_string(),
1493 )],
1494 )
1495 .await
1496 .unwrap();
1497 UpdateStateData::<_, MockMerkleTree, 8>::insert_merkle_nodes(
1498 &mut tx,
1499 proof.clone(),
1500 traversal_path.clone(),
1501 2_u64,
1502 )
1503 .await
1504 .expect("failed to insert nodes");
1505
1506 let node_path = traversal_path
1508 .iter()
1509 .skip(1)
1510 .rev()
1511 .map(|n| *n as i32)
1512 .collect::<Vec<_>>();
1513 tx.execute(
1514 query(&format!(
1515 "DELETE FROM {} WHERE created = 2 and path = $1",
1516 MockMerkleTree::state_type()
1517 ))
1518 .bind(serde_json::to_value(node_path).unwrap()),
1519 )
1520 .await
1521 .expect("failed to delete internal node");
1522 tx.commit().await.unwrap();
1523
1524 let merkle_path = storage
1525 .read()
1526 .await
1527 .unwrap()
1528 .get_path(Snapshot::<_, MockMerkleTree, 8>::Index(2_u64), 1)
1529 .await;
1530
1531 assert!(merkle_path.is_err());
1532 }
1533
1534 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1535 async fn test_merklized_state_snapshot() {
1536 let db = TmpDb::init().await;
1537 let storage = SqlStorage::connect(db.config(), StorageConnectionType::Query)
1538 .await
1539 .unwrap();
1540
1541 let mut test_tree = MockMerkleTree::new(MockMerkleTree::tree_height());
1543
1544 const RESERVED_KEY: usize = (u32::MAX as usize) + 1;
1547
1548 #[tracing::instrument(skip(tree, expected))]
1551 fn randomize(tree: &mut MockMerkleTree, expected: &mut HashMap<usize, Option<usize>>) {
1552 let mut rng = rand::thread_rng();
1553 tracing::info!("randomizing tree");
1554
1555 for _ in 0..50 {
1556 if !expected.values().any(|v| v.is_some()) || rng.next_u32().is_multiple_of(2) {
1559 let key = rng.next_u32() as usize;
1561 let val = rng.next_u32() as usize;
1562 tracing::info!(key, val, "inserting");
1563
1564 tree.update(key, val).unwrap();
1565 expected.insert(key, Some(val));
1566 } else {
1567 let key = expected
1569 .iter()
1570 .filter_map(|(k, v)| if v.is_some() { Some(k) } else { None })
1571 .choose(&mut rng)
1572 .unwrap();
1573 tracing::info!(key, "deleting");
1574
1575 tree.remove(key).unwrap();
1576 expected.insert(*key, None);
1577 }
1578 }
1579 }
1580
1581 #[tracing::instrument(skip(storage, tree, expected))]
1583 async fn store(
1584 storage: &SqlStorage,
1585 tree: &MockMerkleTree,
1586 expected: &HashMap<usize, Option<usize>>,
1587 block_height: u64,
1588 ) {
1589 tracing::info!("persisting tree");
1590 let mut tx = storage.write().await.unwrap();
1591
1592 for key in expected.keys() {
1593 let proof = match tree.universal_lookup(key) {
1594 LookupResult::Ok(_, proof) => proof,
1595 LookupResult::NotFound(proof) => proof,
1596 LookupResult::NotInMemory => panic!("failed to find key {key}"),
1597 };
1598 let traversal_path = ToTraversalPath::<8>::to_traversal_path(key, tree.height());
1599 UpdateStateData::<_, MockMerkleTree, 8>::insert_merkle_nodes(
1600 &mut tx,
1601 proof,
1602 traversal_path,
1603 block_height,
1604 )
1605 .await
1606 .unwrap();
1607 }
1608 tx
1610 .upsert("header", ["height", "hash", "payload_hash", "timestamp", "data", "ns_table"], ["height"],
1611 [(
1612 block_height as i64,
1613 format!("hash{block_height}"),
1614 "hash".to_string(),
1615 0i64,
1616 serde_json::json!({ MockMerkleTree::header_state_commitment_field() : serde_json::to_value(tree.commitment()).unwrap()}),
1617 "ns_table".to_string(),
1618 )],
1619 )
1620 .await
1621 .unwrap();
1622 UpdateStateData::<MockTypes, MockMerkleTree, 8>::set_last_state_height(
1623 &mut tx,
1624 block_height as usize,
1625 )
1626 .await
1627 .unwrap();
1628 tx.commit().await.unwrap();
1629 }
1630
1631 #[tracing::instrument(skip(storage, tree, expected))]
1632 async fn validate(
1633 storage: &SqlStorage,
1634 tree: &MockMerkleTree,
1635 expected: &HashMap<usize, Option<usize>>,
1636 block_height: u64,
1637 ) {
1638 tracing::info!("validating snapshot");
1639
1640 let snapshot = Snapshot::<_, MockMerkleTree, 8>::Index(block_height);
1642
1643 for (key, val) in expected {
1644 let proof = match tree.universal_lookup(key) {
1645 LookupResult::Ok(_, proof) => proof,
1646 LookupResult::NotFound(proof) => proof,
1647 LookupResult::NotInMemory => panic!("failed to find key {key}"),
1648 };
1649 assert_eq!(
1650 proof,
1651 storage
1652 .read()
1653 .await
1654 .unwrap()
1655 .get_path(snapshot, *key)
1656 .await
1657 .unwrap()
1658 );
1659 assert_eq!(val.as_ref(), proof.elem());
1660 if val.is_some() {
1662 MockMerkleTree::verify(tree.commitment(), key, proof)
1663 .unwrap()
1664 .unwrap();
1665 } else {
1666 assert!(
1667 MockMerkleTree::non_membership_verify(tree.commitment(), key, proof)
1668 .unwrap()
1669 );
1670 }
1671 }
1672
1673 let proof = match tree.universal_lookup(RESERVED_KEY) {
1675 LookupResult::Ok(_, proof) => proof,
1676 LookupResult::NotFound(proof) => proof,
1677 LookupResult::NotInMemory => panic!("failed to find reserved key {RESERVED_KEY}"),
1678 };
1679 assert_eq!(
1680 proof,
1681 storage
1682 .read()
1683 .await
1684 .unwrap()
1685 .get_path(snapshot, RESERVED_KEY)
1686 .await
1687 .unwrap()
1688 );
1689 assert_eq!(proof.elem(), None);
1690 assert!(
1692 MockMerkleTree::non_membership_verify(tree.commitment(), RESERVED_KEY, proof)
1693 .unwrap()
1694 );
1695 }
1696
1697 let mut expected = HashMap::<usize, Option<usize>>::new();
1699 randomize(&mut test_tree, &mut expected);
1700
1701 store(&storage, &test_tree, &expected, 1).await;
1703 validate(&storage, &test_tree, &expected, 1).await;
1704
1705 let mut expected2 = expected.clone();
1707 let mut test_tree2 = test_tree.clone();
1708 randomize(&mut test_tree2, &mut expected2);
1709 store(&storage, &test_tree2, &expected2, 2).await;
1710 validate(&storage, &test_tree2, &expected2, 2).await;
1711
1712 validate(&storage, &test_tree, &expected, 1).await;
1714 }
1715
1716 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1717 async fn test_merklized_state_missing_leaf() {
1718 for tree_size in 1..=3 {
1725 let db = TmpDb::init().await;
1726 let storage = SqlStorage::connect(db.config(), StorageConnectionType::Query)
1727 .await
1728 .unwrap();
1729
1730 let mut test_tree = MockMerkleTree::new(MockMerkleTree::tree_height());
1732 for i in 0..tree_size {
1733 test_tree.update(i, i).unwrap();
1734 }
1735
1736 let mut tx = storage.write().await.unwrap();
1737
1738 tx.upsert(
1740 "header",
1741 ["height", "hash", "payload_hash", "timestamp", "data", "ns_table"],
1742 ["height"],
1743 [(
1744 0i64,
1745 "hash".to_string(),
1746 "hash".to_string(),
1747 0,
1748 serde_json::json!({ MockMerkleTree::header_state_commitment_field() : serde_json::to_value(test_tree.commitment()).unwrap()}),
1749 "ns_table".to_string(),
1750 )],
1751 )
1752 .await
1753 .unwrap();
1754
1755 for i in 0..tree_size {
1757 let proof = test_tree.lookup(i).expect_ok().unwrap().1;
1758 let traversal_path =
1759 ToTraversalPath::<8>::to_traversal_path(&i, test_tree.height());
1760 UpdateStateData::<_, MockMerkleTree, 8>::insert_merkle_nodes(
1761 &mut tx,
1762 proof,
1763 traversal_path,
1764 0,
1765 )
1766 .await
1767 .unwrap();
1768 }
1769 UpdateStateData::<_, MockMerkleTree, 8>::set_last_state_height(&mut tx, 0)
1770 .await
1771 .unwrap();
1772 tx.commit().await.unwrap();
1773
1774 let snapshot = Snapshot::<MockTypes, MockMerkleTree, 8>::Index(0);
1776 for i in 0..tree_size {
1777 let proof = test_tree.lookup(i).expect_ok().unwrap().1;
1778 assert_eq!(
1779 proof,
1780 storage
1781 .read()
1782 .await
1783 .unwrap()
1784 .get_path(snapshot, i)
1785 .await
1786 .unwrap()
1787 );
1788 assert_eq!(*proof.elem().unwrap(), i);
1789 }
1790
1791 let index = serde_json::to_value(tree_size - 1).unwrap();
1793 let mut tx = storage.write().await.unwrap();
1794
1795 tx.execute(
1796 query(&format!(
1797 "DELETE FROM {} WHERE idx = $1",
1798 MockMerkleTree::state_type()
1799 ))
1800 .bind(serde_json::to_value(index).unwrap()),
1801 )
1802 .await
1803 .unwrap();
1804 tx.commit().await.unwrap();
1805
1806 for i in 0..tree_size - 1 {
1808 let proof = test_tree.lookup(i).expect_ok().unwrap().1;
1809 assert_eq!(
1810 proof,
1811 storage
1812 .read()
1813 .await
1814 .unwrap()
1815 .get_path(snapshot, i)
1816 .await
1817 .unwrap()
1818 );
1819 assert_eq!(*proof.elem().unwrap(), i);
1820 }
1821
1822 let err = storage
1824 .read()
1825 .await
1826 .unwrap()
1827 .get_path(snapshot, tree_size - 1)
1828 .await
1829 .unwrap_err();
1830 assert!(matches!(err, QueryError::Missing));
1831 }
1832 }
1833}