From 66def746e6cb4f2bf5fee29e2be1b7cc5fd0bbf6 Mon Sep 17 00:00:00 2001 From: Oleksandr Pravdyvyi Date: Thu, 29 May 2025 12:00:50 +0300 Subject: [PATCH 1/6] feat: periodic snapshooting --- accounts/src/account_core/mod.rs | 1 + accounts/src/key_management/mod.rs | 3 +- accounts/src/key_management/secret_holders.rs | 5 +- node_core/src/chain_storage/block_store.rs | 30 ++++++++++ node_core/src/chain_storage/mod.rs | 60 ++++++++++++++++++- node_core/src/config.rs | 2 + node_core/src/lib.rs | 4 +- node_runner/configs/debug/node_config.json | 3 +- 8 files changed, 99 insertions(+), 9 deletions(-) diff --git a/accounts/src/account_core/mod.rs b/accounts/src/account_core/mod.rs index 32c5359..c990407 100644 --- a/accounts/src/account_core/mod.rs +++ b/accounts/src/account_core/mod.rs @@ -16,6 +16,7 @@ use crate::key_management::{ pub type PublicKey = AffinePoint; pub type AccountAddress = TreeHashType; +#[derive(Debug, Serialize)] pub struct Account { pub key_holder: AddressKeyHolder, pub address: AccountAddress, diff --git a/accounts/src/key_management/mod.rs b/accounts/src/key_management/mod.rs index 474bd99..0f7a7a0 100644 --- a/accounts/src/key_management/mod.rs +++ b/accounts/src/key_management/mod.rs @@ -6,6 +6,7 @@ use ephemeral_key_holder::EphemeralKeyHolder; use k256::AffinePoint; use log::info; use secret_holders::{SeedHolder, TopSecretKeyHolder, UTXOSecretKeyHolder}; +use serde::Serialize; use crate::account_core::PublicKey; @@ -13,7 +14,7 @@ pub mod constants_types; pub mod ephemeral_key_holder; pub mod secret_holders; -#[derive(Clone)] +#[derive(Debug, Serialize, Clone)] ///Entrypoint to key management pub struct AddressKeyHolder { //Will be useful in future diff --git a/accounts/src/key_management/secret_holders.rs b/accounts/src/key_management/secret_holders.rs index 399ef44..eee512e 100644 --- a/accounts/src/key_management/secret_holders.rs +++ b/accounts/src/key_management/secret_holders.rs @@ -2,6 +2,7 @@ use common::merkle_tree_public::TreeHashType; use elliptic_curve::PrimeField; use k256::{AffinePoint, FieldBytes, Scalar}; use rand::{rngs::OsRng, RngCore}; +use serde::Serialize; use sha2::{digest::FixedOutput, Digest}; use super::constants_types::{NULLIFIER_SECRET_CONST, VIEWING_SECRET_CONST}; @@ -13,13 +14,13 @@ pub struct SeedHolder { seed: Scalar, } -#[derive(Debug, Clone)] +#[derive(Debug, Serialize, Clone)] ///Secret spending key holder. Produces `UTXOSecretKeyHolder` objects. pub struct TopSecretKeyHolder { pub secret_spending_key: Scalar, } -#[derive(Debug, Clone)] +#[derive(Debug, Serialize, Clone)] ///Nullifier secret key and viewing secret key holder. Produces public keys. Can produce address. Can produce shared secret for recepient. pub struct UTXOSecretKeyHolder { pub nullifier_secret_key: Scalar, diff --git a/node_core/src/chain_storage/block_store.rs b/node_core/src/chain_storage/block_store.rs index 2956381..38c185e 100644 --- a/node_core/src/chain_storage/block_store.rs +++ b/node_core/src/chain_storage/block_store.rs @@ -2,6 +2,7 @@ use std::path::Path; use anyhow::{anyhow, Result}; use common::block::Block; +use log::warn; use storage::sc_db_utils::{DataBlob, DataBlobChangeVariant}; use storage::RocksDBIO; @@ -56,6 +57,35 @@ impl NodeBlockStore { pub fn get_sc_sc_state(&self, sc_addr: &str) -> Result> { Ok(self.dbio.get_sc_sc_state(sc_addr)?) } + + pub fn put_snapshot_at_block_id( + &self, + id: u64, + accounts_ser: Vec, + comm_ser: Vec, + txs_ser: Vec, + nullifiers_ser: Vec, + ) -> Result<()> { + self.dbio + .put_snapshot_block_id_db(id) + .inspect_err(|err| warn!("Failed to store snapshot block id with error {err:#?}"))?; + self.dbio + .put_snapshot_account_db(accounts_ser) + .inspect_err(|err| warn!("Failed to store snapshot accounts with error {err:#?}"))?; + self.dbio + .put_snapshot_commitement_db(comm_ser) + .inspect_err(|err| warn!("Failed to store snapshot commitments with error {err:#?}"))?; + self.dbio + .put_snapshot_transaction_db(txs_ser) + .inspect_err(|err| { + warn!("Failed to store snapshot transactions with error {err:#?}") + })?; + self.dbio + .put_snapshot_account_db(nullifiers_ser) + .inspect_err(|err| warn!("Failed to store snapshot nullifiers with error {err:#?}"))?; + + Ok(()) + } } #[cfg(test)] diff --git a/node_core/src/chain_storage/mod.rs b/node_core/src/chain_storage/mod.rs index 26bb809..f166a10 100644 --- a/node_core/src/chain_storage/mod.rs +++ b/node_core/src/chain_storage/mod.rs @@ -13,10 +13,11 @@ use common::{ utxo_commitment::UTXOCommitment, }; use k256::AffinePoint; +use log::{info, warn}; use public_context::PublicSCContext; use utxo::utxo_core::UTXO; -use crate::ActionData; +use crate::{config::NodeConfig, ActionData}; pub mod accounts_store; pub mod block_store; @@ -28,10 +29,11 @@ pub struct NodeChainStore { pub nullifier_store: HashSet, pub utxo_commitments_store: UTXOCommitmentsMerkleTree, pub pub_tx_store: PublicTransactionMerkleTree, + pub node_config: NodeConfig, } impl NodeChainStore { - pub fn new_with_genesis(home_dir: &Path, genesis_block: Block) -> Self { + pub fn new_with_genesis(config: NodeConfig, genesis_block: Block) -> Self { let acc_map = HashMap::new(); let nullifier_store = HashSet::new(); let utxo_commitments_store = UTXOCommitmentsMerkleTree::new(vec![]); @@ -40,7 +42,7 @@ impl NodeChainStore { //Sequencer should panic if unable to open db, //as fixing this issue may require actions non-native to program scope let block_store = - NodeBlockStore::open_db_with_genesis(&home_dir.join("rocksdb"), Some(genesis_block)) + NodeBlockStore::open_db_with_genesis(&config.home.join("rocksdb"), Some(genesis_block)) .unwrap(); Self { @@ -49,10 +51,13 @@ impl NodeChainStore { nullifier_store, utxo_commitments_store, pub_tx_store, + node_config: config, } } pub fn dissect_insert_block(&mut self, block: Block) -> Result<()> { + let block_id = block.block_id; + for tx in &block.transactions { if !tx.execution_input.is_empty() { let public_action = serde_json::from_slice::(&tx.execution_input); @@ -136,6 +141,55 @@ impl NodeChainStore { self.block_store.put_block_at_id(block)?; + //Snapshot + if block_id % self.node_config.shapshot_frequency_in_blocks == 0 { + //Serializing all important data structures + + //If we fail snapshot, it is not the reason to stop running + //Logging on warn level in this cases + + let accounts_ser = serde_json::to_vec(&self.acc_map).inspect_err(|err| { + warn!("Failed to serialize accounts data {err:#?}"); + }); + + let comm_tree_serialized = serde_json::to_vec(&self.utxo_commitments_store) + .inspect_err(|err| { + warn!("Failed to serialize commitments {err:#?}"); + }); + + let tx_tree_serialized = serde_json::to_vec(&self.pub_tx_store).inspect_err(|err| { + warn!("Failed to serialize transactions {err:#?}"); + }); + + let nullifiers_serialized = + serde_json::to_vec(&self.nullifier_store).inspect_err(|err| { + warn!("Failed to serialize nullifiers {err:#?}"); + }); + + match ( + accounts_ser, + comm_tree_serialized, + tx_tree_serialized, + nullifiers_serialized, + ) { + (Ok(accounts_ser), Ok(comm_ser), Ok(txs_ser), Ok(nullifiers_ser)) => { + let snapshot_trace = self.block_store.put_snapshot_at_block_id( + block_id, + accounts_ser, + comm_ser, + txs_ser, + nullifiers_ser, + ); + + info!( + "Snapshot executed at {:?} with results {snapshot_trace:#?}", + block_id + ); + } + _ => warn!("Failed to serialize node data for snapshot"), + } + } + Ok(()) } diff --git a/node_core/src/config.rs b/node_core/src/config.rs index d92e358..935a803 100644 --- a/node_core/src/config.rs +++ b/node_core/src/config.rs @@ -49,4 +49,6 @@ pub struct NodeConfig { pub port: u16, ///Gas config pub gas_config: GasConfig, + ///Frequency of snapshots + pub shapshot_frequency_in_blocks: u64, } diff --git a/node_core/src/lib.rs b/node_core/src/lib.rs index ca412c4..147e69e 100644 --- a/node_core/src/lib.rs +++ b/node_core/src/lib.rs @@ -94,11 +94,11 @@ impl NodeCore { let client = Arc::new(SequencerClient::new(config.clone())?); let genesis_id = client.get_genesis_id().await?; - info!("Gesesis id is {genesis_id:?}"); + info!("Genesis id is {genesis_id:?}"); let genesis_block = client.get_block(genesis_id.genesis_id).await?.block; - let mut storage = NodeChainStore::new_with_genesis(&config.home, genesis_block); + let mut storage = NodeChainStore::new_with_genesis(config.clone(), genesis_block); pre_start::setup_empty_sc_states(&storage).await?; diff --git a/node_runner/configs/debug/node_config.json b/node_runner/configs/debug/node_config.json index f8c064e..c948b82 100644 --- a/node_runner/configs/debug/node_config.json +++ b/node_runner/configs/debug/node_config.json @@ -12,5 +12,6 @@ "gas_cost_deploy": 1000, "gas_limit_deploy": 30000000, "gas_limit_runtime": 30000000 - } + }, + "shapshot_frequency_in_blocks": 10 } \ No newline at end of file From 9d3bda9bce5bb03f502a9a7b46d7145aab7d31b1 Mon Sep 17 00:00:00 2001 From: Oleksandr Pravdyvyi Date: Fri, 30 May 2025 08:50:35 +0300 Subject: [PATCH 2/6] fix: fmt --- node_core/src/chain_storage/block_store.rs | 13 ++-- node_core/src/chain_storage/mod.rs | 71 +++++++++------------- 2 files changed, 37 insertions(+), 47 deletions(-) diff --git a/node_core/src/chain_storage/block_store.rs b/node_core/src/chain_storage/block_store.rs index 38c185e..553594e 100644 --- a/node_core/src/chain_storage/block_store.rs +++ b/node_core/src/chain_storage/block_store.rs @@ -2,7 +2,7 @@ use std::path::Path; use anyhow::{anyhow, Result}; use common::block::Block; -use log::warn; +use log::{error, warn}; use storage::sc_db_utils::{DataBlob, DataBlobChangeVariant}; use storage::RocksDBIO; @@ -66,23 +66,24 @@ impl NodeBlockStore { txs_ser: Vec, nullifiers_ser: Vec, ) -> Result<()> { + //Error notification for writing into DB error self.dbio .put_snapshot_block_id_db(id) - .inspect_err(|err| warn!("Failed to store snapshot block id with error {err:#?}"))?; + .inspect_err(|err| error!("Failed to store snapshot block id with error {err:#?}"))?; self.dbio .put_snapshot_account_db(accounts_ser) - .inspect_err(|err| warn!("Failed to store snapshot accounts with error {err:#?}"))?; + .inspect_err(|err| error!("Failed to store snapshot accounts with error {err:#?}"))?; self.dbio .put_snapshot_commitement_db(comm_ser) - .inspect_err(|err| warn!("Failed to store snapshot commitments with error {err:#?}"))?; + .inspect_err(|err| error!("Failed to store snapshot commitments with error {err:#?}"))?; self.dbio .put_snapshot_transaction_db(txs_ser) .inspect_err(|err| { - warn!("Failed to store snapshot transactions with error {err:#?}") + error!("Failed to store snapshot transactions with error {err:#?}") })?; self.dbio .put_snapshot_account_db(nullifiers_ser) - .inspect_err(|err| warn!("Failed to store snapshot nullifiers with error {err:#?}"))?; + .inspect_err(|err| error!("Failed to store snapshot nullifiers with error {err:#?}"))?; Ok(()) } diff --git a/node_core/src/chain_storage/mod.rs b/node_core/src/chain_storage/mod.rs index f166a10..1fbe284 100644 --- a/node_core/src/chain_storage/mod.rs +++ b/node_core/src/chain_storage/mod.rs @@ -1,7 +1,4 @@ -use std::{ - collections::{BTreeMap, HashMap, HashSet}, - path::Path, -}; +use std::collections::{BTreeMap, HashMap, HashSet}; use accounts::account_core::{Account, AccountAddress}; use anyhow::Result; @@ -145,48 +142,40 @@ impl NodeChainStore { if block_id % self.node_config.shapshot_frequency_in_blocks == 0 { //Serializing all important data structures - //If we fail snapshot, it is not the reason to stop running + //If we fail serialization, it is not the reason to stop running //Logging on warn level in this cases - let accounts_ser = serde_json::to_vec(&self.acc_map).inspect_err(|err| { + if let Ok(accounts_ser) = serde_json::to_vec(&self.acc_map).inspect_err(|err| { warn!("Failed to serialize accounts data {err:#?}"); - }); + }) { + if let Ok(comm_ser) = + serde_json::to_vec(&self.utxo_commitments_store).inspect_err(|err| { + warn!("Failed to serialize commitments {err:#?}"); + }) + { + if let Ok(txs_ser) = serde_json::to_vec(&self.pub_tx_store).inspect_err(|err| { + warn!("Failed to serialize transactions {err:#?}"); + }) { + if let Ok(nullifiers_ser) = serde_json::to_vec(&self.nullifier_store) + .inspect_err(|err| { + warn!("Failed to serialize nullifiers {err:#?}"); + }) + { + let snapshot_trace = self.block_store.put_snapshot_at_block_id( + block_id, + accounts_ser, + comm_ser, + txs_ser, + nullifiers_ser, + ); - let comm_tree_serialized = serde_json::to_vec(&self.utxo_commitments_store) - .inspect_err(|err| { - warn!("Failed to serialize commitments {err:#?}"); - }); - - let tx_tree_serialized = serde_json::to_vec(&self.pub_tx_store).inspect_err(|err| { - warn!("Failed to serialize transactions {err:#?}"); - }); - - let nullifiers_serialized = - serde_json::to_vec(&self.nullifier_store).inspect_err(|err| { - warn!("Failed to serialize nullifiers {err:#?}"); - }); - - match ( - accounts_ser, - comm_tree_serialized, - tx_tree_serialized, - nullifiers_serialized, - ) { - (Ok(accounts_ser), Ok(comm_ser), Ok(txs_ser), Ok(nullifiers_ser)) => { - let snapshot_trace = self.block_store.put_snapshot_at_block_id( - block_id, - accounts_ser, - comm_ser, - txs_ser, - nullifiers_ser, - ); - - info!( - "Snapshot executed at {:?} with results {snapshot_trace:#?}", - block_id - ); + info!( + "Snapshot executed at {:?} with results {snapshot_trace:#?}", + block_id + ); + } + } } - _ => warn!("Failed to serialize node data for snapshot"), } } From 894ba1934548c6463912bae44b86d52e66f41397 Mon Sep 17 00:00:00 2001 From: Oleksandr Pravdyvyi Date: Fri, 30 May 2025 09:06:39 +0300 Subject: [PATCH 3/6] fix: fmt --- node_core/src/chain_storage/block_store.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/node_core/src/chain_storage/block_store.rs b/node_core/src/chain_storage/block_store.rs index 553594e..208a7b3 100644 --- a/node_core/src/chain_storage/block_store.rs +++ b/node_core/src/chain_storage/block_store.rs @@ -75,7 +75,9 @@ impl NodeBlockStore { .inspect_err(|err| error!("Failed to store snapshot accounts with error {err:#?}"))?; self.dbio .put_snapshot_commitement_db(comm_ser) - .inspect_err(|err| error!("Failed to store snapshot commitments with error {err:#?}"))?; + .inspect_err(|err| { + error!("Failed to store snapshot commitments with error {err:#?}") + })?; self.dbio .put_snapshot_transaction_db(txs_ser) .inspect_err(|err| { From 7e47ecd91e682821f9ed81cef801de71ad1201dd Mon Sep 17 00:00:00 2001 From: Oleksandr Pravdyvyi Date: Fri, 30 May 2025 09:29:56 +0300 Subject: [PATCH 4/6] fix: warn fix --- node_core/src/chain_storage/block_store.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node_core/src/chain_storage/block_store.rs b/node_core/src/chain_storage/block_store.rs index 208a7b3..a14ec19 100644 --- a/node_core/src/chain_storage/block_store.rs +++ b/node_core/src/chain_storage/block_store.rs @@ -2,7 +2,7 @@ use std::path::Path; use anyhow::{anyhow, Result}; use common::block::Block; -use log::{error, warn}; +use log::error; use storage::sc_db_utils::{DataBlob, DataBlobChangeVariant}; use storage::RocksDBIO; From 5df8763166fb230effcaaff25445d127e7823cc8 Mon Sep 17 00:00:00 2001 From: Oleksandr Pravdyvyi Date: Fri, 30 May 2025 14:08:06 +0300 Subject: [PATCH 5/6] fix: suggestion added 1 --- node_core/src/chain_storage/block_store.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node_core/src/chain_storage/block_store.rs b/node_core/src/chain_storage/block_store.rs index a14ec19..0f9d17c 100644 --- a/node_core/src/chain_storage/block_store.rs +++ b/node_core/src/chain_storage/block_store.rs @@ -84,7 +84,7 @@ impl NodeBlockStore { error!("Failed to store snapshot transactions with error {err:#?}") })?; self.dbio - .put_snapshot_account_db(nullifiers_ser) + .put_snapshot_nullifier_db(nullifiers_ser) .inspect_err(|err| error!("Failed to store snapshot nullifiers with error {err:#?}"))?; Ok(()) From 8cbb8089fbb8882690446d942f3a86c5fd5ed6b3 Mon Sep 17 00:00:00 2001 From: Oleksandr Pravdyvyi Date: Fri, 30 May 2025 19:26:59 +0300 Subject: [PATCH 6/6] fix: suggestion added 2 --- node_core/src/chain_storage/block_store.rs | 37 ++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/node_core/src/chain_storage/block_store.rs b/node_core/src/chain_storage/block_store.rs index 0f9d17c..2da64ee 100644 --- a/node_core/src/chain_storage/block_store.rs +++ b/node_core/src/chain_storage/block_store.rs @@ -192,4 +192,41 @@ mod tests { let result = node_store.get_block_at_id(42); assert!(result.is_err()); } + + #[test] + fn test_put_snapshot_at_block_id() { + let temp_dir = tempdir().unwrap(); + let path = temp_dir.path(); + + let genesis_block = create_genesis_block(); + let node_store = NodeBlockStore::open_db_with_genesis(path, Some(genesis_block)).unwrap(); + + let id = 3; + let accounts_ser = vec![1, 2, 3, 4]; + let comm_ser = vec![5, 6, 7, 8]; + let txs_ser = vec![9, 10, 11, 12]; + let nullifiers_ser = vec![13, 14, 15, 16]; + + node_store + .put_snapshot_at_block_id( + id, + accounts_ser.clone(), + comm_ser.clone(), + txs_ser.clone(), + nullifiers_ser.clone(), + ) + .unwrap(); + + assert_eq!(node_store.dbio.get_snapshot_block_id().unwrap(), id); + assert_eq!( + node_store.dbio.get_snapshot_account().unwrap(), + accounts_ser + ); + assert_eq!(node_store.dbio.get_snapshot_commitment().unwrap(), comm_ser); + assert_eq!(node_store.dbio.get_snapshot_transaction().unwrap(), txs_ser); + assert_eq!( + node_store.dbio.get_snapshot_nullifier().unwrap(), + nullifiers_ser + ); + } }