From 7b3d8164a327420d87453a9e560bf307bcb31234 Mon Sep 17 00:00:00 2001 From: jfldde <168934971+jfldde@users.noreply.github.com> Date: Thu, 5 Dec 2024 21:19:21 +0000 Subject: [PATCH] Remove HashStf (#1558) --- Cargo.lock | 1 - bin/citrea/tests/bitcoin_e2e/full_node.rs | 80 ++++ bin/citrea/tests/bitcoin_e2e/mod.rs | 1 + crates/fullnode/Cargo.toml | 1 - crates/fullnode/tests/hash_stf.rs | 343 ------------------ .../tests/runner_initialization_tests.rs | 136 ------- .../full-node/sov-ledger-rpc/src/client.rs | 2 +- 7 files changed, 82 insertions(+), 482 deletions(-) create mode 100644 bin/citrea/tests/bitcoin_e2e/full_node.rs delete mode 100644 crates/fullnode/tests/hash_stf.rs delete mode 100644 crates/fullnode/tests/runner_initialization_tests.rs diff --git a/Cargo.lock b/Cargo.lock index 9734bbd4db..73b68d2d50 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1926,7 +1926,6 @@ dependencies = [ "sha2", "sov-db", "sov-mock-da", - "sov-mock-zkvm", "sov-modules-api", "sov-modules-rollup-blueprint", "sov-modules-stf-blueprint", diff --git a/bin/citrea/tests/bitcoin_e2e/full_node.rs b/bin/citrea/tests/bitcoin_e2e/full_node.rs new file mode 100644 index 0000000000..1c4922ac29 --- /dev/null +++ b/bin/citrea/tests/bitcoin_e2e/full_node.rs @@ -0,0 +1,80 @@ +use async_trait::async_trait; +use citrea_e2e::config::TestCaseConfig; +use citrea_e2e::framework::TestFramework; +use citrea_e2e::test_case::{TestCase, TestCaseRunner}; +use citrea_e2e::traits::Restart; +use citrea_e2e::Result; +use sov_ledger_rpc::client::RpcClient; + +use super::get_citrea_path; + +struct FullNodeRestartTest; + +#[async_trait] +impl TestCase for FullNodeRestartTest { + fn test_config() -> TestCaseConfig { + TestCaseConfig { + with_sequencer: true, + with_full_node: true, + ..Default::default() + } + } + + async fn run_test(&mut self, f: &mut TestFramework) -> Result<()> { + let sequencer = f.sequencer.as_ref().unwrap(); + let full_node = f.full_node.as_mut().unwrap(); + + let genesis_state_root = full_node + .client + .http_client() + .get_l2_genesis_state_root() + .await? + .unwrap(); + + full_node.restart(None).await?; + + let genesis_state_root_after = full_node + .client + .http_client() + .get_l2_genesis_state_root() + .await? + .unwrap(); + + // Verify genesis is not reprocessed + assert_eq!(genesis_state_root, genesis_state_root_after); + + sequencer.client.send_publish_batch_request().await?; + full_node.wait_for_l2_height(1, None).await?; + + let state_root_before = full_node + .client + .http_client() + .get_head_soft_confirmation() + .await? + .unwrap() + .state_root; + + full_node.restart(None).await?; + + let state_root_after = full_node + .client + .http_client() + .get_head_soft_confirmation() + .await? + .unwrap() + .state_root; + + // Verify state root persists across restarts + assert_eq!(state_root_before, state_root_after); + + Ok(()) + } +} + +#[tokio::test] +async fn test_full_node_restart() -> Result<()> { + TestCaseRunner::new(FullNodeRestartTest) + .set_citrea_path(get_citrea_path()) + .run() + .await +} diff --git a/bin/citrea/tests/bitcoin_e2e/mod.rs b/bin/citrea/tests/bitcoin_e2e/mod.rs index e453f36bf7..d448e3c3d3 100644 --- a/bin/citrea/tests/bitcoin_e2e/mod.rs +++ b/bin/citrea/tests/bitcoin_e2e/mod.rs @@ -4,6 +4,7 @@ pub mod batch_prover_test; pub mod light_client_test; // pub mod mempool_accept; pub mod bitcoin_test; +pub mod full_node; pub mod sequencer_commitments; pub mod sequencer_test; pub mod tx_chain; diff --git a/crates/fullnode/Cargo.toml b/crates/fullnode/Cargo.toml index 85cfb2d40b..370f0d88eb 100644 --- a/crates/fullnode/Cargo.toml +++ b/crates/fullnode/Cargo.toml @@ -45,6 +45,5 @@ tempfile = { workspace = true } citrea-primitives = { path = "../primitives", features = ["testing"] } sov-mock-da = { path = "../sovereign-sdk/adapters/mock-da", features = ["native"] } -sov-mock-zkvm = { path = "../sovereign-sdk/adapters/mock-zkvm" } sov-prover-storage-manager = { path = "../sovereign-sdk/full-node/sov-prover-storage-manager", features = ["test-utils"] } sov-state = { path = "../sovereign-sdk/module-system/sov-state", features = ["native"] } diff --git a/crates/fullnode/tests/hash_stf.rs b/crates/fullnode/tests/hash_stf.rs deleted file mode 100644 index d08388dbf5..0000000000 --- a/crates/fullnode/tests/hash_stf.rs +++ /dev/null @@ -1,343 +0,0 @@ -use sha2::Digest; -use sov_mock_da::{MockAddress, MockBlob, MockBlock, MockBlockHeader, MockDaSpec}; -use sov_modules_api::default_context::DefaultContext; -use sov_modules_api::hooks::{HookSoftConfirmationInfo, SoftConfirmationError}; -use sov_modules_api::transaction::Transaction; -use sov_modules_api::Context; -use sov_modules_stf_blueprint::StfBlueprintTrait; -use sov_prover_storage_manager::{new_orphan_storage, SnapshotManager}; -use sov_rollup_interface::da::{BlobReaderTrait, BlockHeaderTrait, DaSpec}; -use sov_rollup_interface::spec::SpecId; -use sov_rollup_interface::stf::{ - ApplySequencerCommitmentsOutput, SlotResult, SoftConfirmationReceipt, SoftConfirmationResult, - StateTransitionFunction, -}; -use sov_state::storage::{NativeStorage, StorageKey, StorageValue}; -use sov_state::{ArrayWitness, OrderedReadsAndWrites, Prefix, ProverStorage, Storage}; -pub type Q = SnapshotManager; - -#[derive(Default, Clone)] -pub struct HashStf; - -impl HashStf { - pub fn new() -> Self { - Self {} - } - - fn hash_key() -> StorageKey { - let prefix = Prefix::new(b"root".to_vec()); - StorageKey::singleton(&prefix) - } - - fn save_from_hasher( - hasher: sha2::Sha256, - storage: ProverStorage, - witness: &mut ArrayWitness, - ) -> ([u8; 32], ProverStorage) { - let result = hasher.finalize(); - - let hash_key = HashStf::hash_key(); - let hash_value = StorageValue::from(result.as_slice().to_vec()); - - let ordered_reads_writes = OrderedReadsAndWrites { - ordered_reads: Vec::default(), - ordered_writes: vec![(hash_key.to_cache_key(), Some(hash_value.into_cache_value()))], - }; - - let (state_root_transition, state_update, _) = storage - .compute_state_update(ordered_reads_writes, witness) - .unwrap(); - - storage.commit( - &state_update, - &OrderedReadsAndWrites::default(), - &OrderedReadsAndWrites::default(), - ); - - let mut root_hash = [0u8; 32]; - - for (i, &byte) in state_root_transition - .final_root - .as_ref() - .iter() - .enumerate() - .take(32) - { - root_hash[i] = byte; - } - - (root_hash, storage) - } -} - -impl StfBlueprintTrait for HashStf { - fn begin_soft_confirmation( - &mut self, - _sequencer_public_key: &[u8], - _pre_state: Self::PreState, - _state_witness: <::Storage as Storage>::Witness, - _offchain_witness: <::Storage as Storage>::Witness, - _slot_header: &::BlockHeader, - _soft_confirmation_info: &HookSoftConfirmationInfo, - ) -> ( - Result<(), SoftConfirmationError>, - sov_modules_api::WorkingSet, - ) { - unimplemented!() - } - - fn apply_soft_confirmation_txs( - &mut self, - _soft_confirmation_info: HookSoftConfirmationInfo, - _txs: &[Vec], - _txs_new: &[Self::Transaction], - _batch_workspace: sov_modules_api::WorkingSet, - ) -> ( - sov_modules_api::WorkingSet, - Vec>, - ) { - unimplemented!() - } - - fn end_soft_confirmation( - &mut self, - _current_spec: SpecId, - _pre_state_root: Vec, - _sequencer_public_key: &[u8], - _soft_confirmation: &mut sov_modules_api::SignedSoftConfirmation, - _tx_receipts: Vec< - sov_modules_stf_blueprint::TransactionReceipt, - >, - _batch_workspace: sov_modules_api::WorkingSet, - ) -> ( - Result< - SoftConfirmationReceipt, - SoftConfirmationError, - >, - sov_modules_api::StateCheckpoint, - ) { - unimplemented!() - } - - fn finalize_soft_confirmation( - &self, - _current_spec: SpecId, - _sc_receipt: SoftConfirmationReceipt, - _checkpoint: sov_modules_api::StateCheckpoint, - _pre_state: Self::PreState, - _soft_confirmation: &mut sov_modules_api::SignedSoftConfirmation, - ) -> SoftConfirmationResult< - Self::StateRoot, - Self::ChangeSet, - Self::TxReceiptContents, - Self::Witness, - Da, - > { - unimplemented!() - } -} - -impl StateTransitionFunction for HashStf { - type Transaction = Transaction; - type StateRoot = [u8; 32]; - type GenesisParams = Vec; - type PreState = ProverStorage; - type ChangeSet = ProverStorage; - type TxReceiptContents = (); - type BatchReceiptContents = [u8; 32]; - type Witness = ArrayWitness; - - fn init_chain( - &self, - genesis_state: Self::PreState, - params: Self::GenesisParams, - ) -> (Self::StateRoot, Self::ChangeSet) { - let mut hasher = sha2::Sha256::new(); - hasher.update(params); - - HashStf::save_from_hasher(hasher, genesis_state, &mut ArrayWitness::default()) - } - - fn apply_slot<'a, I>( - &self, - _current_spec: SpecId, - pre_state_root: &Self::StateRoot, - storage: Self::PreState, - mut witness: Self::Witness, - slot_header: &Da::BlockHeader, - blobs: I, - ) -> SlotResult< - Self::StateRoot, - Self::ChangeSet, - Self::BatchReceiptContents, - Self::TxReceiptContents, - Self::Witness, - > - where - I: IntoIterator, - { - tracing::debug!( - "Applying slot in HashStf at height={}", - slot_header.height() - ); - let mut hasher = sha2::Sha256::new(); - - let hash_key = HashStf::hash_key(); - let existing_cache = storage.get(&hash_key, None, &mut witness).unwrap(); - tracing::debug!( - "HashStf provided_state_root={:?}, saved={:?}", - pre_state_root, - existing_cache.value() - ); - hasher.update(existing_cache.value()); - - for blob in blobs { - let data = blob.verified_data(); - hasher.update(data); - } - - let (state_root, storage) = HashStf::save_from_hasher(hasher, storage, &mut witness); - - SlotResult { - state_root, - change_set: storage, - // TODO: Add batch receipts to inspection - batch_receipts: vec![], - witness, - state_diff: vec![], - } - } - - fn apply_soft_confirmation( - &mut self, - _current_spec: SpecId, - _sequencer_public_key: &[u8], - _pre_state_root: &Self::StateRoot, - _pre_state: Self::PreState, - _state_witness: Self::Witness, - _offchain_witness: Self::Witness, - _slot_header: &::BlockHeader, - _soft_confirmation: &mut sov_modules_api::SignedSoftConfirmation, - ) -> Result< - SoftConfirmationResult< - Self::StateRoot, - Self::ChangeSet, - Self::TxReceiptContents, - Self::Witness, - Da, - >, - SoftConfirmationError, - > { - todo!() - } - - fn apply_soft_confirmations_from_sequencer_commitments( - &mut self, - _sequencer_public_key: &[u8], - _sequencer_da_public_key: &[u8], - _initial_state_root: &Self::StateRoot, - _pre_state: Self::PreState, - _da_data: Vec<::BlobTransaction>, - _sequencer_commitments_range: (u32, u32), - _witnesses: std::collections::VecDeque>, - _slot_headers: std::collections::VecDeque::BlockHeader>>, - _soft_confirmations: std::collections::VecDeque< - Vec>, - >, - _preproven_commitment_indicies: Vec, - ) -> ApplySequencerCommitmentsOutput { - todo!() - } -} - -#[test] -fn compare_output() { - let genesis_params: Vec = vec![1, 2, 3, 4, 5]; - - let raw_blobs: Vec>> = vec![ - // Block A - vec![vec![1, 1, 1], vec![2, 2, 2]], - // Block B - vec![vec![3, 3, 3], vec![4, 4, 4], vec![5, 5, 5]], - // Block C - vec![vec![6, 6, 6]], - // Block D - vec![vec![7, 7, 7], vec![8, 8, 8]], - ]; - - let mut blocks = Vec::new(); - - for (idx, raw_block) in raw_blobs.iter().enumerate() { - let mut blobs = Vec::new(); - for raw_blob in raw_block.iter() { - let blob = MockBlob::new( - raw_blob.clone(), - MockAddress::new([11u8; 32]), - [idx as u8; 32], - ); - blobs.push(blob); - } - - let block = MockBlock { - header: MockBlockHeader::from_height((idx + 1) as u64), - is_valid: true, - blobs, - }; - blocks.push(block); - } - - let (state_root, root_hash) = get_result_from_blocks(&genesis_params, &blocks); - - assert!(root_hash.is_some()); - - let recorded_state_root: [u8; 32] = [ - 121, 110, 56, 75, 48, 251, 66, 243, 236, 155, 4, 128, 238, 122, 188, 160, 17, 46, 169, 39, - 160, 142, 220, 208, 15, 213, 221, 250, 108, 52, 7, 46, - ]; - - assert_eq!(recorded_state_root, state_root); -} - -// Returns final data hash and root hash -pub fn get_result_from_blocks( - genesis_params: &[u8], - blocks: &[MockBlock], -) -> ([u8; 32], Option< as Storage>::Root>) { - let tmpdir = tempfile::tempdir().unwrap(); - - let storage = new_orphan_storage(tmpdir.path()).unwrap(); - - let stf = HashStf::new(); - - let (genesis_state_root, mut storage) = - >::init_chain( - &stf, - storage, - genesis_params.to_vec(), - ); - - let mut state_root = genesis_state_root; - - let l = blocks.len(); - - for block in blocks { - let mut blobs = block.blobs.clone(); - - let result = - >::apply_slot::<&mut Vec>( - &stf, - SpecId::Genesis, - &state_root, - storage, - ArrayWitness::default(), - &block.header, - &mut blobs, - ); - - state_root = result.state_root; - storage = result.change_set; - } - - let root_hash = storage.get_root_hash(l as u64).ok(); - (state_root, root_hash) -} diff --git a/crates/fullnode/tests/runner_initialization_tests.rs b/crates/fullnode/tests/runner_initialization_tests.rs deleted file mode 100644 index 32ccad574f..0000000000 --- a/crates/fullnode/tests/runner_initialization_tests.rs +++ /dev/null @@ -1,136 +0,0 @@ -use std::collections::HashMap; -use std::sync::Arc; - -use citrea_common::tasks::manager::TaskManager; -use citrea_common::{FullNodeConfig, RollupPublicKeys, RpcConfig, RunnerConfig, StorageConfig}; -use citrea_fullnode::CitreaFullnode; -use sov_db::ledger_db::LedgerDB; -use sov_db::rocks_db_config::RocksdbConfig; -use sov_mock_da::{MockAddress, MockDaConfig, MockDaService, MockDaSpec}; -use sov_mock_zkvm::{MockCodeCommitment, MockZkvm}; -use sov_prover_storage_manager::ProverStorageManager; -use sov_rollup_interface::fork::{Fork, ForkManager}; -use sov_rollup_interface::spec::SpecId; -use sov_stf_runner::InitVariant; -mod hash_stf; - -use hash_stf::HashStf; -use tokio::sync::broadcast; - -type MockInitVariant = InitVariant; - -type StorageManager = ProverStorageManager; - -#[tokio::test(flavor = "multi_thread")] -async fn init_and_restart() { - let tmpdir = tempfile::tempdir().unwrap(); - let genesis_params = vec![1, 2, 3, 4, 5]; - let init_variant: MockInitVariant = InitVariant::Genesis(genesis_params); - - let state_root_after_genesis = { - let runner = initialize_runner(tmpdir.path(), init_variant); - *runner.get_state_root() - }; - - let init_variant_2: MockInitVariant = - InitVariant::Initialized((state_root_after_genesis, [0; 32])); - - let runner_2 = initialize_runner(tmpdir.path(), init_variant_2); - - let state_root_2 = *runner_2.get_state_root(); - - assert_eq!(state_root_after_genesis, state_root_2); -} - -fn initialize_runner( - storage_path: &std::path::Path, - init_variant: MockInitVariant, -) -> CitreaFullnode< - HashStf, - StorageManager, - MockDaService, - MockZkvm, - sov_modules_api::default_context::DefaultContext, - LedgerDB, -> { - static T_FORKS: &[Fork] = &[Fork::new(SpecId::Genesis, 0)]; - let da_storage_path = storage_path.join("da").to_path_buf(); - let rollup_storage_path = storage_path.join("rollup").to_path_buf(); - - if !std::path::Path::new(&da_storage_path).exists() { - let _ = std::fs::create_dir(da_storage_path.clone()); - } - if !std::path::Path::new(&rollup_storage_path).exists() { - let _ = std::fs::create_dir(rollup_storage_path.clone()); - } - - let address = MockAddress::new([11u8; 32]); - let rollup_config = FullNodeConfig:: { - storage: StorageConfig { - path: rollup_storage_path.clone(), - db_max_open_files: None, - }, - rpc: RpcConfig { - bind_host: "127.0.0.1".to_string(), - bind_port: 0, - max_connections: 100, - max_request_body_size: 10 * 1024 * 1024, - max_response_body_size: 10 * 1024 * 1024, - batch_requests_limit: 50, - enable_subscriptions: true, - max_subscriptions_per_connection: 100, - }, - runner: Some(RunnerConfig { - sequencer_client_url: "http://127.0.0.1:4444".to_string(), - include_tx_body: true, - sync_blocks_count: 10, - pruning_config: None, - }), - da: MockDaConfig { - sender_address: address, - db_path: da_storage_path.clone(), - }, - public_keys: RollupPublicKeys { - sequencer_public_key: vec![], - sequencer_da_pub_key: vec![], - prover_da_pub_key: vec![], - }, - }; - - let da_service = MockDaService::new(address, &da_storage_path); - - let ledger_db = - LedgerDB::with_config(&RocksdbConfig::new(rollup_storage_path.as_path(), None)).unwrap(); - - let stf = HashStf::new(); - - let storage_config = sov_state::config::Config { - path: rollup_storage_path.to_path_buf(), - db_max_open_files: None, - }; - let storage_manager = ProverStorageManager::new(storage_config).unwrap(); - - // let vm = MockZkvm::new(MockValidityCond::default()); - // let verifier = MockDaVerifier::default(); - - let fork_manager = ForkManager::new(T_FORKS, 0); - - let mut code_commitments_by_spec = HashMap::new(); - code_commitments_by_spec.insert(SpecId::Genesis, MockCodeCommitment([1u8; 32])); - - CitreaFullnode::new( - rollup_config.runner.unwrap(), - rollup_config.public_keys, - rollup_config.rpc, - Arc::new(da_service), - ledger_db, - stf, - storage_manager, - init_variant, - code_commitments_by_spec, - fork_manager, - broadcast::channel(1).0, - TaskManager::default(), - ) - .unwrap() -} diff --git a/crates/sovereign-sdk/full-node/sov-ledger-rpc/src/client.rs b/crates/sovereign-sdk/full-node/sov-ledger-rpc/src/client.rs index d2c9eca38b..e3958bc4f5 100644 --- a/crates/sovereign-sdk/full-node/sov-ledger-rpc/src/client.rs +++ b/crates/sovereign-sdk/full-node/sov-ledger-rpc/src/client.rs @@ -51,7 +51,7 @@ pub trait Rpc { /// Gets the L2 genesis state root. #[method(name = "getL2GenesisStateRoot")] - async fn get_l2_state_root(&self) -> RpcResult; + async fn get_l2_genesis_state_root(&self) -> Result>, Error>; /// Gets the commitments in the DA slot with the given height. #[method(name = "getSequencerCommitmentsOnSlotByNumber")]