cache.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2026 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (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 Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use std::collections::HashMap;
  19. use darkfi::{blockchain::HeaderHash, Error, Result};
  20. use darkfi_sdk::{
  21. crypto::{
  22. pasta_prelude::PrimeField,
  23. smt::{PoseidonFp, SparseMerkleTree, StorageAdapter, SMT_FP_DEPTH},
  24. MerkleTree, SecretKey,
  25. },
  26. error::{ContractError, ContractResult},
  27. pasta::pallas,
  28. };
  29. use darkfi_serial::{deserialize, serialize};
  30. use num_bigint::BigUint;
  31. use sled_overlay::{sled, SledDbOverlay, SledDbOverlayStateDiff};
  32. use tracing::error;
  33. pub const SLED_SCANNED_BLOCKS_TREE: &[u8] = b"_scanned_blocks";
  34. pub const SLED_STATE_INVERSE_DIFF_TREE: &[u8] = b"_state_inverse_diff";
  35. pub const SLED_MERKLE_TREES_TREE: &[u8] = b"_merkle_trees";
  36. pub const SLED_MONEY_SMT_TREE: &[u8] = b"_money_smt";
  37. /// Structure holding all sled trees that define the blockchain cache.
  38. #[derive(Clone)]
  39. pub struct Cache {
  40. /// Main pointer to the sled db connection
  41. pub sled_db: sled::Db,
  42. /// The `sled` tree storing the scanned blocks from the blockchain,
  43. /// where the key is the height number, and the value is the blocks'
  44. /// hash.
  45. pub scanned_blocks: sled::Tree,
  46. /// The `sled` tree storing each blocks' full database state inverse
  47. /// changes, where the key is the block height number, and the value
  48. /// is the serialized database inverse diff.
  49. pub state_inverse_diff: sled::Tree,
  50. /// The `sled` tree storing the merkle trees of the blockchain,
  51. /// where the key is the tree name, and the value is the serialized
  52. /// merkle tree itself.
  53. pub merkle_trees: sled::Tree,
  54. /// The `sled` tree storing the Sparse Merkle Tree of the Money
  55. /// contract.
  56. // TODO: this could be a map of trees so more contracts can open
  57. // SMTs if needed
  58. pub money_smt: sled::Tree,
  59. // TODO: Perhaps we should also move transactions history here
  60. }
  61. impl Cache {
  62. /// Instantiate a new `Cache` with the given `sled` database.
  63. pub fn new(db: &sled::Db) -> Result<Self> {
  64. let scanned_blocks = db.open_tree(SLED_SCANNED_BLOCKS_TREE)?;
  65. let state_inverse_diff = db.open_tree(SLED_STATE_INVERSE_DIFF_TREE)?;
  66. let merkle_trees = db.open_tree(SLED_MERKLE_TREES_TREE)?;
  67. let money_smt = db.open_tree(SLED_MONEY_SMT_TREE)?;
  68. Ok(Self {
  69. sled_db: db.clone(),
  70. scanned_blocks,
  71. state_inverse_diff,
  72. merkle_trees,
  73. money_smt,
  74. })
  75. }
  76. /// Execute an atomic sled batch corresponding to inserts to the
  77. /// merkle trees tree. For each record, the bytes slice is used as
  78. /// the key, and the serialized merkle tree is used as value.
  79. pub fn insert_merkle_trees(&self, trees: &[(&[u8], &MerkleTree)]) -> Result<()> {
  80. let mut batch = sled::Batch::default();
  81. for (key, tree) in trees {
  82. batch.insert(*key, serialize(*tree));
  83. }
  84. self.merkle_trees.apply_batch(batch)?;
  85. Ok(())
  86. }
  87. /// Insert a `u32` and a block inverse diff into store's inverse
  88. /// diffs tree. The block height is used as the key, and the
  89. /// serialized database inverse diff is used as value.
  90. pub fn insert_state_inverse_diff(
  91. &self,
  92. height: &u32,
  93. diff: &SledDbOverlayStateDiff,
  94. ) -> Result<()> {
  95. self.state_inverse_diff.insert(height.to_be_bytes(), serialize(diff))?;
  96. Ok(())
  97. }
  98. /// Fetch given block height number from the store's state inverse
  99. /// diffs tree. The function will fail if the block height number
  100. /// was not found.
  101. pub fn get_state_inverse_diff(&self, height: &u32) -> Result<SledDbOverlayStateDiff> {
  102. match self.state_inverse_diff.get(height.to_be_bytes())? {
  103. Some(found) => Ok(deserialize(&found)?),
  104. None => Err(Error::BlockStateInverseDiffNotFound(*height)),
  105. }
  106. }
  107. }
  108. /// Overlay structure over a [`Cache`] instance.
  109. pub struct CacheOverlay(pub SledDbOverlay);
  110. impl CacheOverlay {
  111. /// Instantiate a new `CacheOverlay` over the given [`Cache`] instance.
  112. pub fn new(cache: &Cache) -> Result<CacheOverlay> {
  113. // Here we configure all our cache sled trees to be protected in the overlay
  114. let protected_trees = vec![
  115. SLED_SCANNED_BLOCKS_TREE,
  116. SLED_STATE_INVERSE_DIFF_TREE,
  117. SLED_MERKLE_TREES_TREE,
  118. SLED_MONEY_SMT_TREE,
  119. ];
  120. let mut overlay = SledDbOverlay::new(&cache.sled_db, protected_trees);
  121. // Open all our cache sled trees in the overlay
  122. overlay.open_tree(SLED_SCANNED_BLOCKS_TREE, true)?;
  123. overlay.open_tree(SLED_STATE_INVERSE_DIFF_TREE, true)?;
  124. overlay.open_tree(SLED_MERKLE_TREES_TREE, true)?;
  125. overlay.open_tree(SLED_MONEY_SMT_TREE, true)?;
  126. Ok(Self(overlay))
  127. }
  128. /// Insert a `u32`, a block hash and an optional signing key into
  129. /// overlay's scanned blocks tree. The block height is used as the
  130. /// key, while the serialized blockhash and key strings are used as
  131. /// the value.
  132. pub fn insert_scanned_block(
  133. &mut self,
  134. height: &u32,
  135. hash: &HeaderHash,
  136. signing_key: &Option<SecretKey>,
  137. ) -> Result<()> {
  138. let block_signing_key = match signing_key {
  139. Some(key) => key.to_string(),
  140. None => String::from("-"),
  141. };
  142. self.0.insert(
  143. SLED_SCANNED_BLOCKS_TREE,
  144. &height.to_be_bytes(),
  145. &serialize(&(hash.to_string(), block_signing_key)),
  146. )?;
  147. Ok(())
  148. }
  149. }
  150. pub type CacheSmt = SparseMerkleTree<
  151. 'static,
  152. SMT_FP_DEPTH,
  153. { SMT_FP_DEPTH + 1 },
  154. pallas::Base,
  155. PoseidonFp,
  156. CacheSmtStorage,
  157. >;
  158. pub struct CacheSmtStorage {
  159. pub overlay: CacheOverlay,
  160. tree: Vec<u8>,
  161. }
  162. impl CacheSmtStorage {
  163. pub fn new(overlay: CacheOverlay, tree: &[u8]) -> Self {
  164. Self { overlay, tree: tree.to_vec() }
  165. }
  166. pub fn snapshot(&self) -> Result<HashMap<BigUint, pallas::Base>> {
  167. let mut smt = HashMap::new();
  168. for record in self.overlay.0.iter(&self.tree)? {
  169. let (key, value) = record?;
  170. let mut repr = [0; 32];
  171. repr.copy_from_slice(&value);
  172. let Some(value) = pallas::Base::from_repr(repr).into() else {
  173. return Err(Error::ParseFailed(
  174. "[cache::CacheSmtStorage::snapshot] Value conversion failed",
  175. ))
  176. };
  177. smt.insert(BigUint::from_bytes_le(&key), value);
  178. }
  179. Ok(smt)
  180. }
  181. }
  182. impl StorageAdapter for CacheSmtStorage {
  183. type Value = pallas::Base;
  184. fn put(&mut self, key: BigUint, value: pallas::Base) -> ContractResult {
  185. if let Err(e) = self.overlay.0.insert(&self.tree, &key.to_bytes_le(), &value.to_repr()) {
  186. error!(target: "cache::StorageAdapter::put", "Inserting key {key:?}, value {value:?} into DB failed: {e}");
  187. return Err(ContractError::SmtPutFailed)
  188. }
  189. Ok(())
  190. }
  191. fn get(&self, key: &BigUint) -> Option<pallas::Base> {
  192. let value = match self.overlay.0.get(&self.tree, &key.to_bytes_le()) {
  193. Ok(v) => v,
  194. Err(e) => {
  195. error!(target: "cache::StorageAdapter::get", "Fetching key {key:?} from DB failed: {e}");
  196. return None
  197. }
  198. };
  199. let value = value?;
  200. let mut repr = [0; 32];
  201. repr.copy_from_slice(&value);
  202. pallas::Base::from_repr(repr).into()
  203. }
  204. fn del(&mut self, key: &BigUint) -> ContractResult {
  205. if let Err(e) = self.overlay.0.remove(&self.tree, &key.to_bytes_le()) {
  206. error!(target: "cache::StorageAdapter::del", "Removing key {key:?} from DB failed: {e}");
  207. return Err(ContractError::SmtDelFailed)
  208. }
  209. Ok(())
  210. }
  211. }
  212. #[cfg(test)]
  213. mod tests {
  214. use darkfi::{zk::halo2::Field, Result};
  215. use darkfi_sdk::{
  216. crypto::smt::{gen_empty_nodes, util::FieldHasher, PoseidonFp, SparseMerkleTree},
  217. pasta::pallas,
  218. };
  219. use rand::rngs::OsRng;
  220. use sled_overlay::sled;
  221. use crate::cache::{Cache, CacheOverlay, CacheSmtStorage, SLED_MONEY_SMT_TREE};
  222. #[test]
  223. fn test_cache_smt() -> Result<()> {
  224. // Setup cache and its overlay
  225. let sled_db = sled::Config::new().temporary(true).open()?;
  226. let cache = Cache::new(&sled_db)?;
  227. let overlay = CacheOverlay::new(&cache)?;
  228. // Setup SMT
  229. const HEIGHT: usize = 3;
  230. let hasher = PoseidonFp::new();
  231. let empty_leaf = pallas::Base::ZERO;
  232. let empty_nodes = gen_empty_nodes::<{ HEIGHT + 1 }, _, _>(&hasher, empty_leaf);
  233. let store = CacheSmtStorage::new(overlay, SLED_MONEY_SMT_TREE);
  234. let mut smt = SparseMerkleTree::<HEIGHT, { HEIGHT + 1 }, _, _, _>::new(
  235. store,
  236. hasher.clone(),
  237. &empty_nodes,
  238. );
  239. // Verify database is empty
  240. assert!(cache.money_smt.is_empty());
  241. let leaves = vec![
  242. (pallas::Base::from(1), pallas::Base::random(&mut OsRng)),
  243. (pallas::Base::from(2), pallas::Base::random(&mut OsRng)),
  244. (pallas::Base::from(3), pallas::Base::random(&mut OsRng)),
  245. ];
  246. smt.insert_batch(leaves.clone()).unwrap();
  247. let hash1 = leaves[0].1;
  248. let hash2 = leaves[1].1;
  249. let hash3 = leaves[2].1;
  250. let hash = |l, r| hasher.hash([l, r]);
  251. let hash01 = hash(empty_nodes[3], hash1);
  252. let hash23 = hash(hash2, hash3);
  253. let hash0123 = hash(hash01, hash23);
  254. let root = hash(hash0123, empty_nodes[1]);
  255. assert_eq!(root, smt.root());
  256. // Now try to construct a membership proof for leaf 3
  257. let pos = leaves[2].0;
  258. let path = smt.prove_membership(&pos);
  259. assert_eq!(path.path[0], empty_nodes[1]);
  260. assert_eq!(path.path[1], hash01);
  261. assert_eq!(path.path[2], hash2);
  262. assert_eq!(hash23, hash(path.path[2], hash3));
  263. assert_eq!(hash0123, hash(path.path[1], hash(path.path[2], hash3)));
  264. assert_eq!(root, hash(hash(path.path[1], hash(path.path[2], hash3)), path.path[0]));
  265. assert!(path.verify(&root, &hash3, &pos));
  266. // Grab the overlay diff
  267. let diff = smt.store.overlay.0.diff(&[])?;
  268. // Apply the overlay
  269. smt.store.overlay.0.apply_diff(&diff)?;
  270. // Verify database contains keys
  271. assert!(!cache.money_smt.is_empty());
  272. // We are now going to rollback the changes
  273. smt.store.overlay.0.apply_diff(&diff.inverse())?;
  274. // Verify database is empty again
  275. assert!(cache.money_smt.is_empty());
  276. Ok(())
  277. }
  278. }