diff --git a/Cargo.lock b/Cargo.lock index 61ea179ef..e37c1ebd6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1884,7 +1884,6 @@ dependencies = [ "lru", "sov-db", "sov-rollup-interface", - "sov-stf-runner", "tokio", ] @@ -1946,6 +1945,7 @@ dependencies = [ "borsh", "chrono", "citrea-evm", + "citrea-primitives", "citrea-stf", "deadpool-postgres", "digest 0.10.7", @@ -8081,6 +8081,7 @@ name = "sequencer-client" version = "0.4.0-rc.3" dependencies = [ "anyhow", + "citrea-primitives", "hex", "jsonrpsee", "reth-primitives", diff --git a/bin/citrea/provers/risc0/guest-bitcoin/Cargo.lock b/bin/citrea/provers/risc0/guest-bitcoin/Cargo.lock index 285fa8f59..a1836e856 100644 --- a/bin/citrea/provers/risc0/guest-bitcoin/Cargo.lock +++ b/bin/citrea/provers/risc0/guest-bitcoin/Cargo.lock @@ -902,7 +902,6 @@ version = "0.4.0-rc.3" dependencies = [ "anyhow", "sov-rollup-interface", - "sov-stf-runner", ] [[package]] diff --git a/bin/citrea/src/rollup/mod.rs b/bin/citrea/src/rollup/mod.rs index 9b3d1adc5..83238e722 100644 --- a/bin/citrea/src/rollup/mod.rs +++ b/bin/citrea/src/rollup/mod.rs @@ -43,11 +43,6 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { let mut storage_manager = self.create_storage_manager(&rollup_config)?; let prover_storage = storage_manager.create_finalized_storage()?; - let prev_root = ledger_db - .get_head_soft_batch()? - .map(|(number, _)| prover_storage.get_root_hash(number.0 + 1)) - .transpose()?; - // TODO(https://github.com/Sovereign-Labs/sovereign-sdk/issues/1218) let rpc_methods = self.create_rpc_methods(&prover_storage, &ledger_db, &da_service, None)?; @@ -56,10 +51,16 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { let genesis_root = prover_storage.get_root_hash(1); - let init_variant = match prev_root { - Some(root_hash) => InitVariant::Initialized(root_hash), + let prev_data = match ledger_db.get_head_soft_batch()? { + Some((number, soft_batch)) => { + Some((prover_storage.get_root_hash(number.0 + 1)?, soft_batch.hash)) + } + None => None, + }; + let init_variant = match prev_data { + Some((root_hash, batch_hash)) => InitVariant::Initialized((root_hash, batch_hash)), None => match genesis_root { - Ok(root_hash) => InitVariant::Initialized(root_hash), + Ok(root_hash) => InitVariant::Initialized((root_hash, [0; 32])), _ => InitVariant::Genesis(genesis_config), }, }; @@ -108,11 +109,6 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { let mut storage_manager = self.create_storage_manager(&rollup_config)?; let prover_storage = storage_manager.create_finalized_storage()?; - let prev_root = ledger_db - .get_head_soft_batch()? - .map(|(number, _)| prover_storage.get_root_hash(number.0 + 1)) - .transpose()?; - let runner_config = rollup_config.runner.expect("Runner config is missing"); // TODO(https://github.com/Sovereign-Labs/sovereign-sdk/issues/1218) let rpc_methods = self.create_rpc_methods( @@ -126,10 +122,16 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { let genesis_root = prover_storage.get_root_hash(1); - let init_variant = match prev_root { - Some(root_hash) => InitVariant::Initialized(root_hash), + let prev_data = match ledger_db.get_head_soft_batch()? { + Some((number, soft_batch)) => { + Some((prover_storage.get_root_hash(number.0 + 1)?, soft_batch.hash)) + } + None => None, + }; + let init_variant = match prev_data { + Some((root_hash, batch_hash)) => InitVariant::Initialized((root_hash, batch_hash)), None => match genesis_root { - Ok(root_hash) => InitVariant::Initialized(root_hash), + Ok(root_hash) => InitVariant::Initialized((root_hash, [0; 32])), _ => InitVariant::Genesis(genesis_config), }, }; @@ -185,11 +187,6 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { let mut storage_manager = self.create_storage_manager(&rollup_config)?; let prover_storage = storage_manager.create_finalized_storage()?; - let prev_root = ledger_db - .get_head_soft_batch()? - .map(|(number, _)| prover_storage.get_root_hash(number.0 + 1)) - .transpose()?; - let runner_config = rollup_config.runner.expect("Runner config is missing"); // TODO(https://github.com/Sovereign-Labs/sovereign-sdk/issues/1218) let rpc_methods = self.create_rpc_methods( @@ -203,10 +200,16 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { let genesis_root = prover_storage.get_root_hash(1); - let init_variant = match prev_root { - Some(root_hash) => InitVariant::Initialized(root_hash), + let prev_data = match ledger_db.get_head_soft_batch()? { + Some((number, soft_batch)) => { + Some((prover_storage.get_root_hash(number.0 + 1)?, soft_batch.hash)) + } + None => None, + }; + let init_variant = match prev_data { + Some((root_hash, batch_hash)) => InitVariant::Initialized((root_hash, batch_hash)), None => match genesis_root { - Ok(root_hash) => InitVariant::Initialized(root_hash), + Ok(root_hash) => InitVariant::Initialized((root_hash, [0; 32])), _ => InitVariant::Genesis(genesis_config), }, }; diff --git a/bin/citrea/src/test_rpc.rs b/bin/citrea/src/test_rpc.rs index 3e9f98333..a68cf781b 100644 --- a/bin/citrea/src/test_rpc.rs +++ b/bin/citrea/src/test_rpc.rs @@ -134,10 +134,10 @@ fn regular_test_helper(payload: serde_json::Value, expected: &serde_json::Value) da_slot_height: 0, da_slot_hash: ::SlotHash::from([0u8; 32]), da_slot_txs_commitment: ::SlotHash::from([1u8; 32]), - pre_state_root: vec![], - post_state_root: vec![], + state_root: vec![], soft_confirmation_signature: vec![], - batch_hash: ::sha2::Sha256::digest(b"batch_receipt").into(), + hash: ::sha2::Sha256::digest(b"batch_receipt").into(), + prev_hash: ::sha2::Sha256::digest(b"prev_batch_receipt").into(), tx_receipts: vec![ TransactionReceipt:: { tx_hash: ::sha2::Sha256::digest(b"tx1").into(), @@ -168,10 +168,10 @@ fn regular_test_helper(payload: serde_json::Value, expected: &serde_json::Value) da_slot_height: 1, da_slot_hash: ::SlotHash::from([2; 32]), da_slot_txs_commitment: ::SlotHash::from([3; 32]), - pre_state_root: vec![], - post_state_root: vec![], + state_root: vec![], soft_confirmation_signature: vec![], - batch_hash: ::sha2::Sha256::digest(b"batch_receipt2").into(), + hash: ::sha2::Sha256::digest(b"batch_receipt2").into(), + prev_hash: ::sha2::Sha256::digest(b"prev_batch_receipt2").into(), tx_receipts: batch2_tx_receipts(), phantom_data: PhantomData, pub_key: vec![], @@ -183,7 +183,8 @@ fn regular_test_helper(payload: serde_json::Value, expected: &serde_json::Value) let batches = vec![ BatchReceipt { - batch_hash: ::sha2::Sha256::digest(b"batch_receipt").into(), + hash: ::sha2::Sha256::digest(b"batch_receipt").into(), + prev_hash: ::sha2::Sha256::digest(b"prev_batch_receipt").into(), tx_receipts: vec![ TransactionReceipt:: { tx_hash: ::sha2::Sha256::digest(b"tx1").into(), @@ -204,7 +205,8 @@ fn regular_test_helper(payload: serde_json::Value, expected: &serde_json::Value) phantom_data: PhantomData, }, BatchReceipt { - batch_hash: ::sha2::Sha256::digest(b"batch_receipt2").into(), + hash: ::sha2::Sha256::digest(b"batch_receipt2").into(), + prev_hash: ::sha2::Sha256::digest(b"prev_batch_receipt2").into(), tx_receipts: batch2_tx_receipts(), phantom_data: PhantomData, }, @@ -327,7 +329,7 @@ fn test_get_batches() { fn test_get_soft_batch() { // Get the first soft batch by number let payload = jsonrpc_req!("ledger_getSoftBatchByNumber", [1]); - let expected = jsonrpc_result!({"daSlotHeight":0,"daSlotHash":"0000000000000000000000000000000000000000000000000000000000000000","daSlotTxsCommitment":"0101010101010101010101010101010101010101010101010101010101010101","depositData": ["616161616162", "65656565656565656565"],"hash":"b5515a80204963f7db40e98af11aedb49a394b1c7e3d8b5b7a33346b8627444f","l2Height":1, "txs":["74783120626f6479", "74783220626f6479"],"preStateRoot":"","postStateRoot":"","softConfirmationSignature":"","pubKey":"", "l1FeeRate":0, "timestamp": 0}); + let expected = jsonrpc_result!({"daSlotHeight":0,"daSlotHash":"0000000000000000000000000000000000000000000000000000000000000000","daSlotTxsCommitment":"0101010101010101010101010101010101010101010101010101010101010101","depositData": ["616161616162", "65656565656565656565"],"hash":"b5515a80204963f7db40e98af11aedb49a394b1c7e3d8b5b7a33346b8627444f","l2Height":1, "txs":["74783120626f6479", "74783220626f6479"],"prevHash":"0209d4aa08c40ed0fcb2bb6eb276481f2ad045914c3065e13e4f1657e97638b1","stateRoot":"","softConfirmationSignature":"","pubKey":"", "l1FeeRate":0, "timestamp": 0}); regular_test_helper(payload, &expected); // Get the first soft batch by hash @@ -344,7 +346,7 @@ fn test_get_soft_batch() { .map(|tx_receipt| tx_receipt.body_to_save.unwrap().encode_hex::()) .collect::>(); let expected = jsonrpc_result!( - {"daSlotHeight":1,"daSlotHash":"0202020202020202020202020202020202020202020202020202020202020202","daSlotTxsCommitment":"0303030303030303030303030303030303030303030303030303030303030303","depositData": ["633434343434"],"hash":"f85fe0cb36fdaeca571c896ed476b49bb3c8eff00d935293a8967e1e9a62071e","l2Height":2, "txs": txs, "preStateRoot":"","postStateRoot":"","softConfirmationSignature":"","pubKey":"","l1FeeRate":0, "timestamp": 0} + {"daSlotHeight":1,"daSlotHash":"0202020202020202020202020202020202020202020202020202020202020202","daSlotTxsCommitment":"0303030303030303030303030303030303030303030303030303030303030303","depositData": ["633434343434"],"hash":"f85fe0cb36fdaeca571c896ed476b49bb3c8eff00d935293a8967e1e9a62071e","l2Height":2, "txs": txs, "prevHash":"11ec8b9896aa1f400cc1dbd1b0ab3dcc97f2025b3d309b70ec249f687a807d1d","stateRoot":"","softConfirmationSignature":"","pubKey":"","l1FeeRate":0, "timestamp": 0} ); regular_test_helper(payload, &expected); @@ -364,8 +366,8 @@ fn test_get_soft_batch() { .collect::>(); let expected = jsonrpc_result!( [ - {"daSlotHeight":0,"daSlotHash":"0000000000000000000000000000000000000000000000000000000000000000","daSlotTxsCommitment":"0101010101010101010101010101010101010101010101010101010101010101","depositData": ["616161616162", "65656565656565656565"],"hash":"b5515a80204963f7db40e98af11aedb49a394b1c7e3d8b5b7a33346b8627444f","l2Height":1,"txs":["74783120626f6479", "74783220626f6479"],"preStateRoot":"","postStateRoot":"","softConfirmationSignature":"","pubKey":"","l1FeeRate":0, "timestamp": 0}, - {"daSlotHeight":1,"daSlotHash":"0202020202020202020202020202020202020202020202020202020202020202","daSlotTxsCommitment":"0303030303030303030303030303030303030303030303030303030303030303","depositData": ["633434343434"],"hash":"f85fe0cb36fdaeca571c896ed476b49bb3c8eff00d935293a8967e1e9a62071e","l2Height":2,"txs": txs, "preStateRoot":"","postStateRoot":"","softConfirmationSignature":"","pubKey":"","l1FeeRate":0, "timestamp": 0} + {"daSlotHeight":0,"daSlotHash":"0000000000000000000000000000000000000000000000000000000000000000","daSlotTxsCommitment":"0101010101010101010101010101010101010101010101010101010101010101","depositData": ["616161616162", "65656565656565656565"],"hash":"b5515a80204963f7db40e98af11aedb49a394b1c7e3d8b5b7a33346b8627444f","l2Height":1,"txs":["74783120626f6479", "74783220626f6479"],"prevHash":"0209d4aa08c40ed0fcb2bb6eb276481f2ad045914c3065e13e4f1657e97638b1", "stateRoot":"","softConfirmationSignature":"","pubKey":"","l1FeeRate":0, "timestamp": 0}, + {"daSlotHeight":1,"daSlotHash":"0202020202020202020202020202020202020202020202020202020202020202","daSlotTxsCommitment":"0303030303030303030303030303030303030303030303030303030303030303","depositData": ["633434343434"],"hash":"f85fe0cb36fdaeca571c896ed476b49bb3c8eff00d935293a8967e1e9a62071e","l2Height":2,"txs": txs, "prevHash": "11ec8b9896aa1f400cc1dbd1b0ab3dcc97f2025b3d309b70ec249f687a807d1d", "stateRoot":"","softConfirmationSignature":"","pubKey":"","l1FeeRate":0, "timestamp": 0} ] ); regular_test_helper(payload, &expected); @@ -553,7 +555,7 @@ proptest!( let first_tx_num = curr_tx_num; let curr_batch = curr_slot_batches.get(batch_index).unwrap(); let last_tx_num = first_tx_num + curr_batch.tx_receipts.len(); - let batch_hash = hex::encode(curr_batch.batch_hash); + let batch_hash = hex::encode(curr_batch.hash); let _batch_receipt= 0; let tx_hashes: Vec = curr_batch.tx_receipts.iter().map(|tx| { hex::encode(tx.tx_hash) diff --git a/bin/citrea/tests/e2e/mod.rs b/bin/citrea/tests/e2e/mod.rs index a8e46ea5f..c154732fa 100644 --- a/bin/citrea/tests/e2e/mod.rs +++ b/bin/citrea/tests/e2e/mod.rs @@ -2355,7 +2355,7 @@ async fn sequencer_crash_restore_mempool() -> Result<(), anyhow::Error> { Ok(()) } -#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +#[tokio::test(flavor = "multi_thread")] async fn test_db_get_proof() { // citrea::initialize_logging(tracing::Level::INFO); @@ -2479,7 +2479,7 @@ async fn test_db_get_proof() { prover_node_task.abort(); } -#[tokio::test(flavor = "multi_thread", worker_threads = 3)] +#[tokio::test(flavor = "multi_thread")] async fn full_node_verify_proof_and_store() { // citrea::initialize_logging(tracing::Level::INFO); @@ -3173,7 +3173,7 @@ async fn test_ledger_get_head_soft_batch() { .unwrap(); assert_eq!(latest_block.header.number.unwrap(), 2); assert_eq!( - head_soft_batch.post_state_root.as_slice(), + head_soft_batch.state_root.as_slice(), latest_block.header.state_root.as_slice() ); assert_eq!(head_soft_batch.l2_height, 2); diff --git a/crates/citrea-stf/src/verifier.rs b/crates/citrea-stf/src/verifier.rs index 43e2ea16b..73123e9e4 100644 --- a/crates/citrea-stf/src/verifier.rs +++ b/crates/citrea-stf/src/verifier.rs @@ -46,18 +46,6 @@ where data.completeness_proof, )?; - assert_eq!( - data.initial_state_root.as_ref(), - data.soft_confirmations - .front() - .expect("At least one set of soft confirmations") - .first() - .expect("At least one soft confirmation") - .pre_state_root() - .as_slice(), - "Invalid initial state root" - ); - println!("going into apply_soft_confirmations_from_sequencer_commitments"); let (final_state_root, state_diff) = self .app @@ -65,6 +53,7 @@ where data.sequencer_public_key.as_ref(), data.sequencer_da_public_key.as_ref(), &data.initial_state_root, + data.initial_batch_hash, pre_state, data.da_data, data.state_transition_witnesses, @@ -83,6 +72,7 @@ where let out: StateTransition = StateTransition { initial_state_root: data.initial_state_root, final_state_root, + initial_batch_hash: data.initial_batch_hash, validity_condition, // TODO: not sure about what to do with this yet state_diff, da_slot_hash: data.da_block_header_of_commitments.hash(), diff --git a/crates/fullnode/src/runner.rs b/crates/fullnode/src/runner.rs index 3afea0fd8..dd394ba46 100644 --- a/crates/fullnode/src/runner.rs +++ b/crates/fullnode/src/runner.rs @@ -7,6 +7,7 @@ use anyhow::{anyhow, bail}; use backoff::future::retry as retry_backoff; use backoff::ExponentialBackoffBuilder; use borsh::de::BorshDeserialize; +use citrea_primitives::types::SoftConfirmationHash; use citrea_primitives::{get_da_block_at_height, L1BlockCache, SyncError}; use jsonrpsee::core::client::Error as JsonrpseeError; use jsonrpsee::RpcModule; @@ -52,6 +53,7 @@ where /// made pub so that sequencer can clone it pub ledger_db: LedgerDB, state_root: StateRoot, + batch_hash: SoftConfirmationHash, rpc_config: RpcConfig, sequencer_client: SequencerClient, sequencer_pub_key: Vec, @@ -97,10 +99,10 @@ where code_commitment: Vm::CodeCommitment, sync_blocks_count: u64, ) -> Result { - let prev_state_root = match init_variant { - InitVariant::Initialized(state_root) => { + let (prev_state_root, prev_batch_hash) = match init_variant { + InitVariant::Initialized((state_root, batch_hash)) => { debug!("Chain is already initialized. Skipping initialization."); - state_root + (state_root, batch_hash) } InitVariant::Genesis(params) => { info!("No history detected. Initializing chain..."); @@ -112,7 +114,7 @@ where "Chain initialization is done. Genesis root: 0x{}", hex::encode(genesis_root.as_ref()), ); - genesis_root + (genesis_root, [0; 32]) } }; @@ -131,6 +133,7 @@ where storage_manager, ledger_db, state_root: prev_state_root, + batch_hash: prev_batch_hash, rpc_config, sequencer_client: SequencerClient::new(runner_config.sequencer_client_url), sequencer_pub_key: public_keys.sequencer_public_key, @@ -286,17 +289,20 @@ where }; let l2_height = proven_commitments[0].l2_start_block_number; - let soft_batches = self + // Fetch the block prior to the one at l2_height so compare state roots + let prior_soft_batch = self .ledger_db - .get_soft_batch_range(&(BatchNumber(l2_height)..BatchNumber(l2_height + 1)))?; - - let soft_batch = soft_batches.first().unwrap(); - if soft_batch.pre_state_root.as_slice() != state_transition.initial_state_root.as_ref() { - return Err(anyhow!( - "Proof verification: For a known and verified sequencer commitment. Pre state root mismatch - expected 0x{} but got 0x{}. Skipping proof.", - hex::encode(&soft_batch.pre_state_root), - hex::encode(&state_transition.initial_state_root) - ).into()); + .get_soft_batch_by_number(&(BatchNumber(l2_height - 1)))?; + if let Some(prior_soft_batch) = prior_soft_batch { + if prior_soft_batch.state_root.as_slice() + != state_transition.initial_state_root.as_ref() + { + return Err(anyhow!( + "Proof verification: For a known and verified sequencer commitment. Pre state root mismatch - expected 0x{} but got 0x{}. Skipping proof.", + hex::encode(&prior_soft_batch.state_root), + hex::encode(&state_transition.initial_state_root) + ).into()); + } } for commitment in proven_commitments { @@ -396,6 +402,10 @@ where current_l1_block.header().height() ); + if self.batch_hash != soft_batch.prev_hash { + bail!("Previous hash mismatch at height: {}", l2_height); + } + let mut data_to_commit = SlotCommit::new(current_l1_block.clone()); let pre_state = self @@ -415,7 +425,7 @@ where let next_state_root = slot_result.state_root; // Check if post state root is the same as the one in the soft batch - if next_state_root.as_ref().to_vec() != soft_batch.post_state_root { + if next_state_root.as_ref().to_vec() != soft_batch.state_root { bail!("Post state root mismatch at height: {}", l2_height) } @@ -426,10 +436,10 @@ where let batch_receipt = data_to_commit.batch_receipts()[0].clone(); let soft_batch_receipt = SoftBatchReceipt::<_, _, Da::Spec> { - pre_state_root: self.state_root.as_ref().to_vec(), - post_state_root: next_state_root.as_ref().to_vec(), + state_root: next_state_root.as_ref().to_vec(), phantom_data: PhantomData::, - batch_hash: batch_receipt.batch_hash, + hash: soft_batch.hash, + prev_hash: soft_batch.prev_hash, da_slot_hash: current_l1_block.header().hash(), da_slot_height: current_l1_block.header().height(), da_slot_txs_commitment: current_l1_block.header().txs_commitment(), @@ -455,6 +465,7 @@ where .commit_soft_batch(soft_batch_receipt, self.include_tx_body)?; self.state_root = next_state_root; + self.batch_hash = soft_batch.hash; info!( "New State Root after soft confirmation #{} is: {:?}", diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index a4c92d945..10b31e0c1 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -10,7 +10,6 @@ publish = false # Sov SDK deps sov-db = { path = "../sovereign-sdk/full-node/db/sov-db", optional = true } sov-rollup-interface = { path = "../sovereign-sdk/rollup-interface" } -sov-stf-runner = { path = "../sovereign-sdk/full-node/sov-stf-runner" } # 3rd-party deps anyhow = { workspace = true } diff --git a/crates/primitives/src/error.rs b/crates/primitives/src/error.rs index cb5225b3f..9b9635e83 100644 --- a/crates/primitives/src/error.rs +++ b/crates/primitives/src/error.rs @@ -1,5 +1,4 @@ use sov_db::schema::types::BatchNumber; -use sov_stf_runner::ProverServiceError; #[derive(Debug)] pub enum SyncError { @@ -12,9 +11,3 @@ impl From for SyncError { Self::Error(e) } } - -impl From for SyncError { - fn from(e: ProverServiceError) -> Self { - Self::Error(e.into()) - } -} diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 252ea867e..dc53eab26 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -5,6 +5,7 @@ mod constants; mod da; #[cfg(feature = "native")] mod error; +pub mod types; #[cfg(feature = "native")] pub use cache::*; diff --git a/crates/primitives/src/types.rs b/crates/primitives/src/types.rs new file mode 100644 index 000000000..61d8239df --- /dev/null +++ b/crates/primitives/src/types.rs @@ -0,0 +1 @@ +pub type SoftConfirmationHash = [u8; 32]; diff --git a/crates/prover/Cargo.toml b/crates/prover/Cargo.toml index 91b7aef9e..6e22105cb 100644 --- a/crates/prover/Cargo.toml +++ b/crates/prover/Cargo.toml @@ -10,7 +10,7 @@ repository.workspace = true [dependencies] # Citrea Deps -citrea-primitives = { path = "../primitives" } +citrea-primitives = { path = "../primitives", features = ["native"] } sequencer-client = { path = "../sequencer-client" } shared-backup-db = { path = "../shared-backup-db" } diff --git a/crates/prover/src/runner.rs b/crates/prover/src/runner.rs index 8b39df30b..303fba253 100644 --- a/crates/prover/src/runner.rs +++ b/crates/prover/src/runner.rs @@ -9,6 +9,7 @@ use anyhow::{anyhow, bail}; use backoff::exponential::ExponentialBackoffBuilder; use backoff::future::retry as retry_backoff; use borsh::de::BorshDeserialize; +use citrea_primitives::types::SoftConfirmationHash; use citrea_primitives::{get_da_block_at_height, L1BlockCache}; use jsonrpsee::core::client::Error as JsonrpseeError; use jsonrpsee::RpcModule; @@ -59,6 +60,7 @@ where /// made pub so that sequencer can clone it pub ledger_db: LedgerDB, state_root: StateRoot, + batch_hash: SoftConfirmationHash, rpc_config: RpcConfig, #[allow(dead_code)] prover_service: Option, @@ -107,10 +109,10 @@ where code_commitment: Vm::CodeCommitment, sync_blocks_count: u64, ) -> Result { - let prev_state_root = match init_variant { - InitVariant::Initialized(state_root) => { + let (prev_state_root, prev_batch_hash) = match init_variant { + InitVariant::Initialized((state_root, batch_hash)) => { debug!("Chain is already initialized. Skipping initialization."); - state_root + (state_root, batch_hash) } InitVariant::Genesis(params) => { info!("No history detected. Initializing chain..."); @@ -122,7 +124,7 @@ where "Chain initialization is done. Genesis root: 0x{}", hex::encode(genesis_root.as_ref()), ); - genesis_root + (genesis_root, [0; 32]) } }; @@ -139,6 +141,7 @@ where storage_manager, ledger_db, state_root: prev_state_root, + batch_hash: prev_batch_hash, rpc_config, prover_service, sequencer_client: SequencerClient::new(runner_config.sequencer_client_url), @@ -310,6 +313,10 @@ where current_l1_block.header().height() ); + if self.batch_hash != soft_batch.prev_hash { + bail!("Previous hash mismatch at height: {}", l2_height); + } + let mut data_to_commit = SlotCommit::new(current_l1_block.clone()); let pre_state = self @@ -329,7 +336,7 @@ where let next_state_root = slot_result.state_root; // Check if post state root is the same as the one in the soft batch - if next_state_root.as_ref().to_vec() != soft_batch.post_state_root { + if next_state_root.as_ref().to_vec() != soft_batch.state_root { bail!("Post state root mismatch at height: {}", l2_height) } @@ -347,10 +354,10 @@ where let batch_receipt = data_to_commit.batch_receipts()[0].clone(); let soft_batch_receipt = SoftBatchReceipt::<_, _, Da::Spec> { - pre_state_root: self.state_root.as_ref().to_vec(), - post_state_root: next_state_root.as_ref().to_vec(), + state_root: next_state_root.as_ref().to_vec(), phantom_data: PhantomData::, - batch_hash: batch_receipt.batch_hash, + hash: soft_batch.hash, + prev_hash: soft_batch.prev_hash, da_slot_hash: current_l1_block.header().hash(), da_slot_height: current_l1_block.header().height(), da_slot_txs_commitment: current_l1_block.header().txs_commitment(), @@ -370,6 +377,7 @@ where )?; self.state_root = next_state_root; + self.batch_hash = soft_batch.hash; // save state root after applying l2 with l2_height self.ledger_db @@ -459,11 +467,20 @@ where .await?; let da_block_header_of_commitments = l1_block.header().clone(); + let hash = da_block_header_of_commitments.hash(); let initial_state_root = self .ledger_db .get_l2_state_root::(first_l2_height_of_l1 - 1)? .expect("There should be a state root"); + let initial_batch_hash = self + .ledger_db + .get_soft_batch_by_number(&BatchNumber(first_l2_height_of_l1))? + .ok_or(anyhow!( + "Could not find soft batch at height {}", + first_l2_height_of_l1 + ))? + .prev_hash; let final_state_root = self .ledger_db @@ -479,6 +496,7 @@ where StateTransitionData { initial_state_root, final_state_root, + initial_batch_hash, da_data, da_block_header_of_commitments, inclusion_proof, diff --git a/crates/sequencer-client/Cargo.toml b/crates/sequencer-client/Cargo.toml index f27c7a270..17b3a973e 100644 --- a/crates/sequencer-client/Cargo.toml +++ b/crates/sequencer-client/Cargo.toml @@ -12,21 +12,25 @@ readme = "README.md" resolver = "2" [dependencies] -sov-rollup-interface = { path = "../sovereign-sdk/rollup-interface" } - +# 3rd-party dependencies anyhow = { workspace = true } -jsonrpsee = { workspace = true, features = ["http-client"] } -tracing = { workspace = true } - hex = { workspace = true } +jsonrpsee = { workspace = true, features = ["http-client"] } serde = { workspace = true } serde_json = { workspace = true } - tokio = { workspace = true } +tracing = { workspace = true } +# Reth Deps reth-primitives = { workspace = true } reth-rpc-types = { workspace = true } +# Sovereign-SDK deps +sov-rollup-interface = { path = "../sovereign-sdk/rollup-interface" } + +# Citrea Deps +citrea-primitives = { path = "../primitives" } + [dev-dependencies] tokio = { workspace = true } diff --git a/crates/sequencer-client/src/lib.rs b/crates/sequencer-client/src/lib.rs index 464a0de6a..81d342da3 100644 --- a/crates/sequencer-client/src/lib.rs +++ b/crates/sequencer-client/src/lib.rs @@ -1,5 +1,6 @@ use std::ops::Range; +use citrea_primitives::types::SoftConfirmationHash; use jsonrpsee::core::client::{ClientT, Error}; use jsonrpsee::http_client::{HttpClient, HttpClientBuilder}; use jsonrpsee::rpc_params; @@ -104,7 +105,9 @@ impl SequencerClient { #[serde(rename_all = "camelCase")] pub struct GetSoftBatchResponse { #[serde(with = "hex::serde")] - pub hash: [u8; 32], + pub hash: SoftConfirmationHash, + #[serde(with = "hex::serde")] + pub prev_hash: SoftConfirmationHash, pub da_slot_height: u64, #[serde(with = "hex::serde")] pub da_slot_hash: [u8; 32], @@ -113,9 +116,7 @@ pub struct GetSoftBatchResponse { #[serde(skip_serializing_if = "Option::is_none")] pub txs: Option>, #[serde(with = "hex::serde")] - pub pre_state_root: Vec, - #[serde(with = "hex::serde")] - pub post_state_root: Vec, + pub state_root: Vec, #[serde(with = "hex::serde")] pub soft_confirmation_signature: Vec, pub deposit_data: Vec, // Vec wrapper around deposit data @@ -129,10 +130,10 @@ impl From for SignedSoftConfirmationBatch { fn from(val: GetSoftBatchResponse) -> Self { SignedSoftConfirmationBatch::new( val.hash, + val.prev_hash, val.da_slot_height, val.da_slot_hash, val.da_slot_txs_commitment, - val.pre_state_root, val.l1_fee_rate, val.txs .unwrap_or_default() diff --git a/crates/sequencer/Cargo.toml b/crates/sequencer/Cargo.toml index 7be8fbffb..6f8853db1 100644 --- a/crates/sequencer/Cargo.toml +++ b/crates/sequencer/Cargo.toml @@ -59,6 +59,7 @@ sov-stf-runner = { path = "../sovereign-sdk/full-node/sov-stf-runner" } # Citrea Deps citrea-evm = { path = "../evm", features = ["native"] } +citrea-primitives = { path = "../primitives" } citrea-stf = { path = "../citrea-stf", features = ["native"] } shared-backup-db = { path = "../shared-backup-db" } diff --git a/crates/sequencer/src/sequencer.rs b/crates/sequencer/src/sequencer.rs index 03638b121..7d2e12398 100644 --- a/crates/sequencer/src/sequencer.rs +++ b/crates/sequencer/src/sequencer.rs @@ -8,6 +8,7 @@ use std::vec; use anyhow::anyhow; use citrea_evm::{CallMessage, Evm, RlpEvmTransaction, MIN_TRANSACTION_GAS}; +use citrea_primitives::types::SoftConfirmationHash; use citrea_stf::runtime::Runtime; use digest::Digest; use futures::channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}; @@ -84,6 +85,7 @@ where deposit_mempool: Arc>, storage_manager: Sm, state_root: StateRoot, + batch_hash: SoftConfirmationHash, sequencer_pub_key: Vec, rpc_config: RpcConfig, soft_confirmation_rule_enforcer: SoftConfirmationRuleEnforcer, @@ -123,10 +125,10 @@ where ) -> anyhow::Result { let (l2_force_block_tx, l2_force_block_rx) = unbounded(); - let prev_state_root = match init_variant { - InitVariant::Initialized(state_root) => { + let (prev_state_root, prev_batch_hash) = match init_variant { + InitVariant::Initialized((state_root, batch_hash)) => { debug!("Chain is already initialized. Skipping initialization."); - state_root + (state_root, batch_hash) } InitVariant::Genesis(params) => { info!("No history detected. Initializing chain...",); @@ -138,7 +140,7 @@ where "Chain initialization is done. Genesis root: 0x{}", hex::encode(genesis_root.as_ref()), ); - genesis_root + (genesis_root, [0; 32]) } }; @@ -171,6 +173,7 @@ where deposit_mempool, storage_manager, state_root: prev_state_root, + batch_hash: prev_batch_hash, sequencer_pub_key: public_keys.sequencer_public_key, rpc_config, soft_confirmation_rule_enforcer, @@ -249,7 +252,6 @@ where dyn BestTransactions>>, >, pub_key: &[u8], - state_root: ::Spec>>::StateRoot, prestate: ::Spec>>::NativeStorage, da_block_header: <::Spec as DaSpec>::BlockHeader, mut signed_batch: SignedSoftConfirmationBatch, @@ -257,7 +259,7 @@ where ) -> anyhow::Result<(Vec, Vec)> { match self.stf.begin_soft_batch( pub_key, - &state_root, + &self.state_root, prestate.clone(), Default::default(), &da_block_header, @@ -403,7 +405,6 @@ where .dry_run_transactions( evm_txs, &pub_key, - self.state_root.clone(), prestate.clone(), da_block.header().clone(), signed_batch.clone(), @@ -441,14 +442,14 @@ where da_block.header().height(), da_block.header().hash().into(), da_block.header().txs_commitment().into(), - self.state_root.clone().as_ref().to_vec(), txs, deposit_data.clone(), l1_fee_rate, timestamp, ); - let mut signed_soft_batch = self.sign_soft_confirmation_batch(unsigned_batch)?; + let mut signed_soft_batch = + self.sign_soft_confirmation_batch(unsigned_batch, self.batch_hash)?; let (batch_receipt, checkpoint) = self.stf.end_soft_batch( self.sequencer_pub_key.as_ref(), @@ -492,10 +493,10 @@ where let next_state_root = slot_result.state_root; let soft_batch_receipt = SoftBatchReceipt::<_, _, Da::Spec> { - pre_state_root: self.state_root.as_ref().to_vec(), - post_state_root: next_state_root.as_ref().to_vec(), + state_root: next_state_root.as_ref().to_vec(), phantom_data: PhantomData::, - batch_hash: batch_receipt.batch_hash, + hash: signed_soft_batch.hash(), + prev_hash: signed_soft_batch.prev_hash(), da_slot_hash: da_block.header().hash(), da_slot_height: da_block.header().height(), da_slot_txs_commitment: da_block.header().txs_commitment(), @@ -531,6 +532,7 @@ where ); self.state_root = next_state_root; + self.batch_hash = signed_soft_batch.hash(); let mut txs_to_remove = self.db_provider.last_block_tx_hashes()?; txs_to_remove.extend(l1_fee_failed_txs); @@ -947,6 +949,7 @@ where fn sign_soft_confirmation_batch( &mut self, soft_confirmation: UnsignedSoftConfirmationBatch, + prev_soft_confirmation_hash: [u8; 32], ) -> anyhow::Result { let raw = borsh::to_vec(&soft_confirmation).map_err(|e| anyhow!(e))?; @@ -956,10 +959,10 @@ where let pub_key = self.sov_tx_signer_priv_key.pub_key(); Ok(SignedSoftConfirmationBatch::new( hash, + prev_soft_confirmation_hash, soft_confirmation.da_slot_height(), soft_confirmation.da_slot_hash(), soft_confirmation.da_slot_txs_commitment(), - soft_confirmation.pre_state_root(), soft_confirmation.l1_fee_rate(), soft_confirmation.txs(), soft_confirmation.deposit_data(), diff --git a/crates/soft-confirmation-rule-enforcer/src/tests/hooks_tests.rs b/crates/soft-confirmation-rule-enforcer/src/tests/hooks_tests.rs index 16cbd102c..01e862ce1 100644 --- a/crates/soft-confirmation-rule-enforcer/src/tests/hooks_tests.rs +++ b/crates/soft-confirmation-rule-enforcer/src/tests/hooks_tests.rs @@ -3,6 +3,7 @@ use std::str::FromStr; use anyhow::anyhow; use sov_mock_da::MockDaSpec; use sov_modules_api::default_context::DefaultContext; +use sov_modules_api::hooks::HookSoftConfirmationInfo; use sov_modules_api::utils::generate_address; use sov_modules_api::{Context, Module, Spec, StateValueAccessor}; use sov_rollup_interface::soft_confirmation::SignedSoftConfirmationBatch; @@ -34,11 +35,11 @@ fn begin_soft_confirmation_hook_checks_max_l2_blocks_per_l1() { .unwrap(); let signed_soft_confirmation_batch = SignedSoftConfirmationBatch::new( + [0; 32], [0; 32], 0, [0; 32], [0; 32], - vec![], 1, vec![], vec![], @@ -51,7 +52,10 @@ fn begin_soft_confirmation_hook_checks_max_l2_blocks_per_l1() { for i in 0..11 { if soft_confirmation_rule_enforcer .begin_soft_confirmation_hook( - &mut signed_soft_confirmation_batch.clone().into(), + &mut HookSoftConfirmationInfo::new( + signed_soft_confirmation_batch.clone(), + vec![0; 32], + ), &mut working_set, ) .is_err() @@ -68,11 +72,11 @@ fn begin_soft_confirmation_hook_checks_l1_fee_rate() { get_soft_confirmation_rule_enforcer::(&TEST_CONFIG); let mut signed_soft_confirmation_batch = SignedSoftConfirmationBatch::new( + [0; 32], [0; 32], 0, [0; 32], [0; 32], - vec![], 100, vec![], vec![], @@ -83,7 +87,7 @@ fn begin_soft_confirmation_hook_checks_l1_fee_rate() { // call first with 100 fee rate to set last_l1_fee_rate let res = soft_confirmation_rule_enforcer.begin_soft_confirmation_hook( - &mut signed_soft_confirmation_batch.clone().into(), + &mut HookSoftConfirmationInfo::new(signed_soft_confirmation_batch.clone(), vec![0; 32]), &mut working_set, ); @@ -94,7 +98,7 @@ fn begin_soft_confirmation_hook_checks_l1_fee_rate() { signed_soft_confirmation_batch.set_l1_fee_rate(111); let res = soft_confirmation_rule_enforcer.begin_soft_confirmation_hook( - &mut signed_soft_confirmation_batch.clone().into(), + &mut HookSoftConfirmationInfo::new(signed_soft_confirmation_batch.clone(), vec![0; 32]), &mut working_set, ); @@ -119,7 +123,7 @@ fn begin_soft_confirmation_hook_checks_l1_fee_rate() { signed_soft_confirmation_batch.set_l1_fee_rate(110); let res = soft_confirmation_rule_enforcer.begin_soft_confirmation_hook( - &mut signed_soft_confirmation_batch.clone().into(), + &mut HookSoftConfirmationInfo::new(signed_soft_confirmation_batch.clone(), vec![0; 32]), &mut working_set, ); @@ -129,7 +133,7 @@ fn begin_soft_confirmation_hook_checks_l1_fee_rate() { signed_soft_confirmation_batch.set_l1_fee_rate(122); let res = soft_confirmation_rule_enforcer.begin_soft_confirmation_hook( - &mut signed_soft_confirmation_batch.clone().into(), + &mut HookSoftConfirmationInfo::new(signed_soft_confirmation_batch.clone(), vec![0; 32]), &mut working_set, ); @@ -138,7 +142,7 @@ fn begin_soft_confirmation_hook_checks_l1_fee_rate() { signed_soft_confirmation_batch.set_l1_fee_rate(121); let res = soft_confirmation_rule_enforcer.begin_soft_confirmation_hook( - &mut signed_soft_confirmation_batch.clone().into(), + &mut HookSoftConfirmationInfo::new(signed_soft_confirmation_batch.clone(), vec![0; 32]), &mut working_set, ); @@ -148,13 +152,13 @@ fn begin_soft_confirmation_hook_checks_l1_fee_rate() { signed_soft_confirmation_batch.set_l1_fee_rate(109); let res = soft_confirmation_rule_enforcer.begin_soft_confirmation_hook( - &mut signed_soft_confirmation_batch.clone().into(), + &mut HookSoftConfirmationInfo::new(signed_soft_confirmation_batch.clone(), vec![0; 32]), &mut working_set, ); assert!(res.is_ok()); signed_soft_confirmation_batch.set_l1_fee_rate(100); let res = soft_confirmation_rule_enforcer.begin_soft_confirmation_hook( - &mut signed_soft_confirmation_batch.clone().into(), + &mut HookSoftConfirmationInfo::new(signed_soft_confirmation_batch.clone(), vec![0; 32]), &mut working_set, ); assert!(res.is_ok()); @@ -166,7 +170,7 @@ fn begin_soft_confirmation_hook_checks_l1_fee_rate() { signed_soft_confirmation_batch.set_l1_fee_rate(89); let res = soft_confirmation_rule_enforcer.begin_soft_confirmation_hook( - &mut signed_soft_confirmation_batch.clone().into(), + &mut HookSoftConfirmationInfo::new(signed_soft_confirmation_batch.clone(), vec![0; 32]), &mut working_set, ); @@ -190,7 +194,7 @@ fn begin_soft_confirmation_hook_checks_l1_fee_rate() { signed_soft_confirmation_batch.set_l1_fee_rate(90); let res = soft_confirmation_rule_enforcer.begin_soft_confirmation_hook( - &mut signed_soft_confirmation_batch.clone().into(), + &mut HookSoftConfirmationInfo::new(signed_soft_confirmation_batch.clone(), vec![0; 32]), &mut working_set, ); @@ -201,7 +205,7 @@ fn begin_soft_confirmation_hook_checks_l1_fee_rate() { signed_soft_confirmation_batch.set_l1_fee_rate(89); let res = soft_confirmation_rule_enforcer.begin_soft_confirmation_hook( - &mut signed_soft_confirmation_batch.clone().into(), + &mut HookSoftConfirmationInfo::new(signed_soft_confirmation_batch.clone(), vec![0; 32]), &mut working_set, ); @@ -216,11 +220,11 @@ fn begin_soft_confirmation_hook_checks_timestamp() { let original_timestamp = chrono::Local::now().timestamp() as u64; let signed_soft_confirmation_batch = SignedSoftConfirmationBatch::new( + [0; 32], [0; 32], 0, [0; 32], [0; 32], - vec![], 100, vec![], vec![], @@ -231,7 +235,7 @@ fn begin_soft_confirmation_hook_checks_timestamp() { // call first with `original_timestamp` let res = soft_confirmation_rule_enforcer.begin_soft_confirmation_hook( - &mut signed_soft_confirmation_batch.clone().into(), + &mut HookSoftConfirmationInfo::new(signed_soft_confirmation_batch.clone(), vec![0; 32]), &mut working_set, ); @@ -240,11 +244,11 @@ fn begin_soft_confirmation_hook_checks_timestamp() { // now call with a timestamp before the original one. // should fail let signed_soft_confirmation_batch = SignedSoftConfirmationBatch::new( + [0; 32], [0; 32], 0, [0; 32], [0; 32], - vec![], 100, vec![], vec![], @@ -254,7 +258,7 @@ fn begin_soft_confirmation_hook_checks_timestamp() { ); let res = soft_confirmation_rule_enforcer.begin_soft_confirmation_hook( - &mut signed_soft_confirmation_batch.clone().into(), + &mut HookSoftConfirmationInfo::new(signed_soft_confirmation_batch.clone(), vec![0; 32]), &mut working_set, ); @@ -278,11 +282,11 @@ fn begin_soft_confirmation_hook_checks_timestamp() { // now call with a timestamp after the original one. // should fail let signed_soft_confirmation_batch = SignedSoftConfirmationBatch::new( + [0; 32], [0; 32], 0, [0; 32], [0; 32], - vec![], 100, vec![], vec![], @@ -292,7 +296,7 @@ fn begin_soft_confirmation_hook_checks_timestamp() { ); let res = soft_confirmation_rule_enforcer.begin_soft_confirmation_hook( - &mut signed_soft_confirmation_batch.clone().into(), + &mut HookSoftConfirmationInfo::new(signed_soft_confirmation_batch.clone(), vec![0; 32]), &mut working_set, ); diff --git a/crates/soft-confirmation-rule-enforcer/src/tests/query_tests.rs b/crates/soft-confirmation-rule-enforcer/src/tests/query_tests.rs index c8a13050f..a85cf8688 100644 --- a/crates/soft-confirmation-rule-enforcer/src/tests/query_tests.rs +++ b/crates/soft-confirmation-rule-enforcer/src/tests/query_tests.rs @@ -1,4 +1,5 @@ use sov_mock_da::MockDaSpec; +use sov_modules_api::hooks::HookSoftConfirmationInfo; use sov_modules_api::StateMapAccessor; use sov_rollup_interface::soft_confirmation::SignedSoftConfirmationBatch; @@ -10,11 +11,11 @@ fn block_count_per_da_hash_must_be_correct() { get_soft_confirmation_rule_enforcer::(&TEST_CONFIG); let mut signed_soft_confirmation_batch = SignedSoftConfirmationBatch::new( + [0; 32], [0; 32], 0, [0; 32], [0; 32], - vec![], 1, vec![], vec![], @@ -26,7 +27,10 @@ fn block_count_per_da_hash_must_be_correct() { for _ in 0..3 { soft_confirmation_rule_enforcer .begin_soft_confirmation_hook( - &mut signed_soft_confirmation_batch.clone().into(), + &mut HookSoftConfirmationInfo::new( + signed_soft_confirmation_batch.clone(), + vec![0; 32], + ), &mut working_set, ) .unwrap(); @@ -45,7 +49,7 @@ fn block_count_per_da_hash_must_be_correct() { // call with a different da hash soft_confirmation_rule_enforcer .begin_soft_confirmation_hook( - &mut signed_soft_confirmation_batch.clone().into(), + &mut HookSoftConfirmationInfo::new(signed_soft_confirmation_batch.clone(), vec![0; 32]), &mut working_set, ) .unwrap(); @@ -72,11 +76,11 @@ fn get_max_l1_fee_rate_change_percentage_must_be_correct() { ); let signed_soft_confirmation_batch = SignedSoftConfirmationBatch::new( + [0; 32], [0; 32], 0, [0; 32], [0; 32], - vec![], 1, vec![], vec![], @@ -87,7 +91,7 @@ fn get_max_l1_fee_rate_change_percentage_must_be_correct() { soft_confirmation_rule_enforcer .begin_soft_confirmation_hook( - &mut signed_soft_confirmation_batch.clone().into(), + &mut HookSoftConfirmationInfo::new(signed_soft_confirmation_batch.clone(), vec![0; 32]), &mut working_set, ) .unwrap(); @@ -114,11 +118,11 @@ fn get_last_l1_fee_rate_must_be_correct() { ); let signed_soft_confirmation_batch = SignedSoftConfirmationBatch::new( + [0; 32], [0; 32], 0, [0; 32], [0; 32], - vec![], 1, vec![], vec![], @@ -128,7 +132,7 @@ fn get_last_l1_fee_rate_must_be_correct() { ); soft_confirmation_rule_enforcer .begin_soft_confirmation_hook( - &mut signed_soft_confirmation_batch.clone().into(), + &mut HookSoftConfirmationInfo::new(signed_soft_confirmation_batch.clone(), vec![0; 32]), &mut working_set, ) .unwrap(); @@ -156,11 +160,11 @@ fn get_last_timestamp_must_be_correct() { let timestamp = chrono::Local::now().timestamp() as u64; let signed_soft_confirmation_batch = SignedSoftConfirmationBatch::new( + [0; 32], [0; 32], 0, [0; 32], [0; 32], - vec![], 1, vec![], vec![], @@ -170,7 +174,7 @@ fn get_last_timestamp_must_be_correct() { ); soft_confirmation_rule_enforcer .begin_soft_confirmation_hook( - &mut signed_soft_confirmation_batch.clone().into(), + &mut HookSoftConfirmationInfo::new(signed_soft_confirmation_batch.clone(), vec![0; 32]), &mut working_set, ) .unwrap(); diff --git a/crates/sovereign-sdk/adapters/mock-zkvm/src/lib.rs b/crates/sovereign-sdk/adapters/mock-zkvm/src/lib.rs index c930f5832..ad192ea34 100644 --- a/crates/sovereign-sdk/adapters/mock-zkvm/src/lib.rs +++ b/crates/sovereign-sdk/adapters/mock-zkvm/src/lib.rs @@ -185,6 +185,7 @@ impl sov_rollup_interface::zk::ZkvmHost Ok(sov_rollup_interface::zk::StateTransition { initial_state_root: st.initial_state_root, final_state_root: st.final_state_root, + initial_batch_hash: st.initial_batch_hash, validity_condition: data.validity_condition, state_diff: Default::default(), da_slot_hash: st.da_block_header_of_commitments.hash(), diff --git a/crates/sovereign-sdk/examples/demo-simple-stf/src/lib.rs b/crates/sovereign-sdk/examples/demo-simple-stf/src/lib.rs index f4646f705..acf829067 100644 --- a/crates/sovereign-sdk/examples/demo-simple-stf/src/lib.rs +++ b/crates/sovereign-sdk/examples/demo-simple-stf/src/lib.rs @@ -80,6 +80,7 @@ impl StateTransitionFunction StateTransitionFunction { - batch_hash: hash, + hash, + prev_hash, tx_receipts: vec![], phantom_data: PhantomData, }); @@ -132,6 +134,7 @@ impl StateTransitionFunction::BlobTransaction>, _witnesses: std::collections::VecDeque>, diff --git a/crates/sovereign-sdk/full-node/db/sov-db/src/ledger_db/mod.rs b/crates/sovereign-sdk/full-node/db/sov-db/src/ledger_db/mod.rs index 6ed1d7820..990242d68 100644 --- a/crates/sovereign-sdk/full-node/db/sov-db/src/ledger_db/mod.rs +++ b/crates/sovereign-sdk/full-node/db/sov-db/src/ledger_db/mod.rs @@ -156,6 +156,15 @@ impl LedgerDB { self.get_data_range::(range) } + /// Gets all soft confirmations by numbers + #[instrument(level = "trace", skip(self), err)] + pub fn get_soft_batch_by_number( + &self, + number: &BatchNumber, + ) -> Result, anyhow::Error> { + self.db.get::(number) + } + /// Gets all soft confirmations with numbers `range.start` to `range.end`. If `range.end` is outside /// the range of the database, the result will smaller than the requested range. /// Note that this method blindly preallocates for the requested range, so it should not be exposed @@ -323,11 +332,11 @@ impl LedgerDB { l2_height: current_item_numbers.soft_batch_number, da_slot_hash: batch_receipt.da_slot_hash.into(), da_slot_txs_commitment: batch_receipt.da_slot_txs_commitment.into(), - hash: batch_receipt.batch_hash, + hash: batch_receipt.hash, + prev_hash: batch_receipt.prev_hash, tx_range: TxNumber(first_tx_number)..TxNumber(last_tx_number), txs, - pre_state_root: batch_receipt.pre_state_root, - post_state_root: batch_receipt.post_state_root, + state_root: batch_receipt.state_root, soft_confirmation_signature: batch_receipt.soft_confirmation_signature, pub_key: batch_receipt.pub_key, deposit_data: batch_receipt.deposit_data, @@ -396,7 +405,7 @@ impl LedgerDB { // Insert batch let batch_to_store = StoredBatch { - hash: batch_receipt.batch_hash, + hash: batch_receipt.hash, txs: TxNumber(first_tx_number)..TxNumber(last_tx_number), }; self.put_batch( diff --git a/crates/sovereign-sdk/full-node/db/sov-db/src/schema/types.rs b/crates/sovereign-sdk/full-node/db/sov-db/src/schema/types.rs index a3801cc1a..97f6904db 100644 --- a/crates/sovereign-sdk/full-node/db/sov-db/src/schema/types.rs +++ b/crates/sovereign-sdk/full-node/db/sov-db/src/schema/types.rs @@ -170,16 +170,16 @@ pub struct StoredSoftBatch { pub da_slot_txs_commitment: [u8; 32], /// The hash of the batch pub hash: DbHash, + /// The hash of the previous batch + pub prev_hash: DbHash, /// The range of transactions which occurred in this batch. pub tx_range: std::ops::Range, /// The transactions which occurred in this batch. pub txs: Vec, /// Deposit data coming from the L1 chain pub deposit_data: Vec>, - /// Pre state root - pub pre_state_root: Vec, - /// Post state root - pub post_state_root: Vec, + /// State root + pub state_root: Vec, /// Sequencer signature pub soft_confirmation_signature: Vec, /// Sequencer public key @@ -194,10 +194,10 @@ impl From for SignedSoftConfirmationBatch { fn from(value: StoredSoftBatch) -> Self { SignedSoftConfirmationBatch::new( value.hash, + value.prev_hash, value.da_slot_height, value.da_slot_hash, value.da_slot_txs_commitment, - value.pre_state_root, value.l1_fee_rate, value.txs.into_iter().map(|tx| tx.body.unwrap()).collect(), value.deposit_data, @@ -221,6 +221,7 @@ impl TryFrom for SoftBatchResponse { da_slot_height: value.da_slot_height, da_slot_txs_commitment: value.da_slot_txs_commitment, hash: value.hash, + prev_hash: value.prev_hash, txs: Some( value .txs @@ -228,8 +229,7 @@ impl TryFrom for SoftBatchResponse { .filter_map(|tx| tx.body.map(Into::into)) .collect(), ), // Rollup full nodes don't store tx bodies - pre_state_root: value.pre_state_root, - post_state_root: value.post_state_root, + state_root: value.state_root, soft_confirmation_signature: value.soft_confirmation_signature, pub_key: value.pub_key, deposit_data: value diff --git a/crates/sovereign-sdk/full-node/sov-stf-runner/src/mock/mod.rs b/crates/sovereign-sdk/full-node/sov-stf-runner/src/mock/mod.rs index 34d683a52..37a99b7dd 100644 --- a/crates/sovereign-sdk/full-node/sov-stf-runner/src/mock/mod.rs +++ b/crates/sovereign-sdk/full-node/sov-stf-runner/src/mock/mod.rs @@ -53,7 +53,8 @@ impl StateTransitionFunction StateTransitionFunction::BlobTransaction>, _witnesses: std::collections::VecDeque>, diff --git a/crates/sovereign-sdk/full-node/sov-stf-runner/src/runner.rs b/crates/sovereign-sdk/full-node/sov-stf-runner/src/runner.rs index a28d1344b..dc1190287 100644 --- a/crates/sovereign-sdk/full-node/sov-stf-runner/src/runner.rs +++ b/crates/sovereign-sdk/full-node/sov-stf-runner/src/runner.rs @@ -38,6 +38,7 @@ use crate::{ProverConfig, ProverService, RollupPublicKeys, RpcConfig, RunnerConf type StateRoot = >::StateRoot; type GenesisParams = >::GenesisParams; +type SoftConfirmationHash = [u8; 32]; /// Combines `DaService` with `StateTransitionFunction` and "runs" the rollup. pub struct StateTransitionRunner @@ -57,6 +58,7 @@ where /// made pub so that sequencer can clone it pub ledger_db: LedgerDB, state_root: StateRoot, + batch_hash: SoftConfirmationHash, rpc_config: RpcConfig, #[allow(dead_code)] prover_service: Option, @@ -89,8 +91,8 @@ where /// How [`StateTransitionRunner`] is initialized pub enum InitVariant, Vm: Zkvm, Da: DaSpec> { - /// From give state root - Initialized(Stf::StateRoot), + /// From given state root and soft confirmation hash + Initialized((Stf::StateRoot, SoftConfirmationHash)), /// From empty state root /// Genesis params for Stf::init Genesis(GenesisParams), @@ -130,10 +132,10 @@ where prover_config: Option, code_commitment: Vm::CodeCommitment, ) -> Result { - let prev_state_root = match init_variant { - InitVariant::Initialized(state_root) => { + let (prev_state_root, prev_batch_hash) = match init_variant { + InitVariant::Initialized((state_root, batch_hash)) => { debug!("Chain is already initialized. Skipping initialization."); - state_root + (state_root, batch_hash) } InitVariant::Genesis(params) => { info!("No history detected. Initializing chain..."); @@ -145,7 +147,7 @@ where "Chain initialization is done. Genesis root: 0x{}", hex::encode(genesis_root.as_ref()), ); - genesis_root + (genesis_root, [0; 32]) } }; @@ -162,6 +164,7 @@ where storage_manager, ledger_db, state_root: prev_state_root, + batch_hash: prev_batch_hash, rpc_config, prover_service, sequencer_client: SequencerClient::new(runner_config.sequencer_client_url), @@ -362,6 +365,7 @@ where ); let initial_state_root = self.state_root.clone(); + let initial_batch_hash = self.batch_hash; let mut da_data = self.da_service.extract_relevant_blobs(&filtered_block); let da_block_header_of_commitments = filtered_block.header().clone(); @@ -497,15 +501,15 @@ where let next_state_root = slot_result.state_root; // Check if post state root is the same as the one in the soft batch - if next_state_root.as_ref().to_vec() != soft_batch.post_state_root { + if next_state_root.as_ref().to_vec() != soft_batch.state_root { bail!("Post state root mismatch") } let soft_batch_receipt = SoftBatchReceipt::<_, _, Da::Spec> { - pre_state_root: self.state_root.as_ref().to_vec(), - post_state_root: next_state_root.as_ref().to_vec(), + state_root: next_state_root.as_ref().to_vec(), phantom_data: PhantomData::, - batch_hash: batch_receipt.batch_hash, + hash: batch_receipt.hash, + prev_hash: batch_receipt.prev_hash, da_slot_hash: filtered_block.header().hash(), da_slot_height: filtered_block.header().height(), da_slot_txs_commitment: filtered_block.header().txs_commitment(), @@ -524,6 +528,7 @@ where )?; self.state_root = next_state_root; + self.batch_hash = soft_batch.hash; debug!( "New State Root after soft confirmation #{} is: {:?}", @@ -545,6 +550,7 @@ where StateTransitionData { initial_state_root, final_state_root: self.state_root.clone(), + initial_batch_hash, da_data, da_block_header_of_commitments, inclusion_proof, @@ -907,17 +913,17 @@ where }; let l2_height = proven_commitments[0].l2_start_block_number; - let soft_batches = self.ledger_db.get_soft_batch_range( - &(BatchNumber(l2_height)..BatchNumber(l2_height + 1)), + let prior_soft_batches = self.ledger_db.get_soft_batch_range( + &(BatchNumber(l2_height - 1)..BatchNumber(l2_height)), )?; - let soft_batch = soft_batches.first().unwrap(); - if soft_batch.pre_state_root.as_slice() + let prior_soft_batch = prior_soft_batches.first().unwrap(); + if prior_soft_batch.state_root.as_slice() != state_transition.initial_state_root.as_ref() { tracing::warn!( "Proof verification: For a known and verified sequencer commitment. Pre state root mismatch - expected 0x{} but got 0x{}. Skipping proof.", - hex::encode(&soft_batch.pre_state_root), + hex::encode(&prior_soft_batch.state_root), hex::encode(&state_transition.initial_state_root) ); continue; @@ -1021,7 +1027,7 @@ where let next_state_root = slot_result.state_root; // Check if post state root is the same as the one in the soft batch - if next_state_root.as_ref().to_vec() != soft_batch.post_state_root { + if next_state_root.as_ref().to_vec() != soft_batch.state_root { warn!("Post state root mismatch at height: {}", height); continue; } @@ -1036,10 +1042,10 @@ where let batch_receipt = data_to_commit.batch_receipts()[0].clone(); let soft_batch_receipt = SoftBatchReceipt::<_, _, Da::Spec> { - pre_state_root: self.state_root.as_ref().to_vec(), - post_state_root: next_state_root.as_ref().to_vec(), + state_root: next_state_root.as_ref().to_vec(), phantom_data: PhantomData::, - batch_hash: batch_receipt.batch_hash, + hash: soft_batch.hash, + prev_hash: soft_batch.prev_hash, da_slot_hash: cur_l1_block.header().hash(), da_slot_height: cur_l1_block.header().height(), da_slot_txs_commitment: cur_l1_block.header().txs_commitment(), @@ -1059,6 +1065,7 @@ where )?; self.state_root = next_state_root; + self.batch_hash = soft_batch.hash; info!( "New State Root after soft confirmation #{} is: {:?}", diff --git a/crates/sovereign-sdk/full-node/sov-stf-runner/src/verifier.rs b/crates/sovereign-sdk/full-node/sov-stf-runner/src/verifier.rs index 43e2ea16b..73123e9e4 100644 --- a/crates/sovereign-sdk/full-node/sov-stf-runner/src/verifier.rs +++ b/crates/sovereign-sdk/full-node/sov-stf-runner/src/verifier.rs @@ -46,18 +46,6 @@ where data.completeness_proof, )?; - assert_eq!( - data.initial_state_root.as_ref(), - data.soft_confirmations - .front() - .expect("At least one set of soft confirmations") - .first() - .expect("At least one soft confirmation") - .pre_state_root() - .as_slice(), - "Invalid initial state root" - ); - println!("going into apply_soft_confirmations_from_sequencer_commitments"); let (final_state_root, state_diff) = self .app @@ -65,6 +53,7 @@ where data.sequencer_public_key.as_ref(), data.sequencer_da_public_key.as_ref(), &data.initial_state_root, + data.initial_batch_hash, pre_state, data.da_data, data.state_transition_witnesses, @@ -83,6 +72,7 @@ where let out: StateTransition = StateTransition { initial_state_root: data.initial_state_root, final_state_root, + initial_batch_hash: data.initial_batch_hash, validity_condition, // TODO: not sure about what to do with this yet state_diff, da_slot_hash: data.da_block_header_of_commitments.hash(), diff --git a/crates/sovereign-sdk/full-node/sov-stf-runner/tests/hash_stf.rs b/crates/sovereign-sdk/full-node/sov-stf-runner/tests/hash_stf.rs index 6548eec9d..665f3b026 100644 --- a/crates/sovereign-sdk/full-node/sov-stf-runner/tests/hash_stf.rs +++ b/crates/sovereign-sdk/full-node/sov-stf-runner/tests/hash_stf.rs @@ -227,6 +227,7 @@ impl StateTransitionFunction::BlobTransaction>, _witnesses: std::collections::VecDeque>, diff --git a/crates/sovereign-sdk/full-node/sov-stf-runner/tests/prover_tests.rs b/crates/sovereign-sdk/full-node/sov-stf-runner/tests/prover_tests.rs index fee86c8a9..8c91b3f71 100644 --- a/crates/sovereign-sdk/full-node/sov-stf-runner/tests/prover_tests.rs +++ b/crates/sovereign-sdk/full-node/sov-stf-runner/tests/prover_tests.rs @@ -209,6 +209,7 @@ fn make_transition_data( StateTransitionData { initial_state_root: [], final_state_root: [], + initial_batch_hash: [0; 32], inclusion_proof: [0; 32], completeness_proof: (), da_data: vec![], diff --git a/crates/sovereign-sdk/full-node/sov-stf-runner/tests/runner_initialization_tests.rs b/crates/sovereign-sdk/full-node/sov-stf-runner/tests/runner_initialization_tests.rs index b1ad41e1a..ea0c4df3e 100644 --- a/crates/sovereign-sdk/full-node/sov-stf-runner/tests/runner_initialization_tests.rs +++ b/crates/sovereign-sdk/full-node/sov-stf-runner/tests/runner_initialization_tests.rs @@ -29,7 +29,8 @@ async fn init_and_restart() { *runner.get_state_root() }; - let init_variant_2: MockInitVariant = InitVariant::Initialized(state_root_after_genesis); + let init_variant_2: MockInitVariant = + InitVariant::Initialized((state_root_after_genesis, [0; 32])); let runner_2 = initialize_runner(tmpdir.path(), init_variant_2); diff --git a/crates/sovereign-sdk/module-system/sov-modules-api/src/hooks.rs b/crates/sovereign-sdk/module-system/sov-modules-api/src/hooks.rs index 593be47de..aa872ab31 100644 --- a/crates/sovereign-sdk/module-system/sov-modules-api/src/hooks.rs +++ b/crates/sovereign-sdk/module-system/sov-modules-api/src/hooks.rs @@ -133,17 +133,20 @@ pub struct HookSoftConfirmationInfo { pub timestamp: u64, } -impl From for HookSoftConfirmationInfo { - fn from(signed_soft_confirmation_batch: SignedSoftConfirmationBatch) -> Self { +impl HookSoftConfirmationInfo { + pub fn new( + signed_soft_confirmation: SignedSoftConfirmationBatch, + pre_state_root: Vec, + ) -> Self { HookSoftConfirmationInfo { - da_slot_height: signed_soft_confirmation_batch.da_slot_height(), - da_slot_hash: signed_soft_confirmation_batch.da_slot_hash(), - da_slot_txs_commitment: signed_soft_confirmation_batch.da_slot_txs_commitment(), - pre_state_root: signed_soft_confirmation_batch.pre_state_root(), - pub_key: signed_soft_confirmation_batch.sequencer_pub_key().to_vec(), - deposit_data: signed_soft_confirmation_batch.deposit_data(), - l1_fee_rate: signed_soft_confirmation_batch.l1_fee_rate(), - timestamp: signed_soft_confirmation_batch.timestamp(), + da_slot_height: signed_soft_confirmation.da_slot_height(), + da_slot_hash: signed_soft_confirmation.da_slot_hash(), + da_slot_txs_commitment: signed_soft_confirmation.da_slot_txs_commitment(), + pre_state_root: pre_state_root.to_vec(), + pub_key: signed_soft_confirmation.sequencer_pub_key().to_vec(), + deposit_data: signed_soft_confirmation.deposit_data(), + l1_fee_rate: signed_soft_confirmation.l1_fee_rate(), + timestamp: signed_soft_confirmation.timestamp(), } } } @@ -151,11 +154,11 @@ impl From for HookSoftConfirmationInfo { impl From for SignedSoftConfirmationBatch { fn from(val: HookSoftConfirmationInfo) -> Self { SignedSoftConfirmationBatch::new( + [0u8; 32], [0u8; 32], val.da_slot_height, val.da_slot_hash(), val.da_slot_txs_commitment(), - val.pre_state_root(), val.l1_fee_rate, vec![], val.deposit_data, diff --git a/crates/sovereign-sdk/module-system/sov-modules-rollup-blueprint/src/lib.rs b/crates/sovereign-sdk/module-system/sov-modules-rollup-blueprint/src/lib.rs index 8ff7ac289..65bd5f2e5 100644 --- a/crates/sovereign-sdk/module-system/sov-modules-rollup-blueprint/src/lib.rs +++ b/crates/sovereign-sdk/module-system/sov-modules-rollup-blueprint/src/lib.rs @@ -150,10 +150,13 @@ pub trait RollupBlueprint: Sized + Send + Sync { let mut storage_manager = self.create_storage_manager(&rollup_config)?; let prover_storage = storage_manager.create_finalized_storage()?; - let prev_root = ledger_db - .get_head_soft_batch()? - .map(|(number, _)| prover_storage.get_root_hash(number.0 + 1)) - .transpose()?; + let head_soft_batch = ledger_db.get_head_soft_batch()?; + let prev_data = match head_soft_batch { + Some((number, soft_batch)) => { + Some((prover_storage.get_root_hash(number.0 + 1)?, soft_batch.hash)) + } + None => None, + }; let runner_config = rollup_config.runner.expect("Runner config is missing"); // TODO(https://github.com/Sovereign-Labs/sovereign-sdk/issues/1218) @@ -168,10 +171,10 @@ pub trait RollupBlueprint: Sized + Send + Sync { let genesis_root = prover_storage.get_root_hash(1); - let init_variant = match prev_root { - Some(root_hash) => InitVariant::Initialized(root_hash), + let init_variant = match prev_data { + Some((root_hash, batch_hash)) => InitVariant::Initialized((root_hash, batch_hash)), None => match genesis_root { - Ok(root_hash) => InitVariant::Initialized(root_hash), + Ok(root_hash) => InitVariant::Initialized((root_hash, [0; 32])), _ => InitVariant::Genesis(genesis_config), }, }; diff --git a/crates/sovereign-sdk/module-system/sov-modules-stf-blueprint/src/lib.rs b/crates/sovereign-sdk/module-system/sov-modules-stf-blueprint/src/lib.rs index 9947631cd..a81a77e52 100644 --- a/crates/sovereign-sdk/module-system/sov-modules-stf-blueprint/src/lib.rs +++ b/crates/sovereign-sdk/module-system/sov-modules-stf-blueprint/src/lib.rs @@ -179,7 +179,7 @@ where fn begin_soft_batch( &self, sequencer_public_key: &[u8], - pre_state_root: &::Root, + pre_state_root: &Self::StateRoot, pre_state: ::Storage, witness: <::Storage as Storage>::Witness, slot_header: &::BlockHeader, @@ -208,16 +208,9 @@ where "DA slot hashes must match" ); - // then verify pre state root matches - assert_eq!( - soft_batch.pre_state_root(), - pre_state_root.as_ref(), - "pre state roots must match" - ); - let checkpoint = StateCheckpoint::with_witness(pre_state, witness); - self.begin_soft_confirmation_inner(checkpoint, soft_batch) + self.begin_soft_confirmation_inner(checkpoint, soft_batch, pre_state_root) } fn apply_soft_batch_txs( @@ -239,7 +232,6 @@ where soft_batch.da_slot_height(), soft_batch.da_slot_hash(), soft_batch.da_slot_txs_commitment(), - soft_batch.pre_state_root(), soft_batch.txs(), soft_batch.deposit_data(), soft_batch.l1_fee_rate(), @@ -472,6 +464,7 @@ where sequencer_public_key: &[u8], sequencer_da_public_key: &[u8], initial_state_root: &Self::StateRoot, + initial_batch_hash: [u8; 32], pre_state: Self::PreState, da_data: Vec<::BlobTransaction>, witnesses: std::collections::VecDeque>, @@ -498,6 +491,7 @@ where // Then verify these soft confirmations. let mut current_state_root = initial_state_root.clone(); + let mut previous_batch_hash = initial_batch_hash; // should panic if number of sequencer commitments, soft confirmations, slot headers and witnesses don't match for (((sequencer_commitment, soft_confirmations), da_block_headers), witnesses) in @@ -512,6 +506,12 @@ where let mut index_soft_confirmation = 0; let mut current_da_height = da_block_headers[index_headers].height(); + assert_eq!( + soft_confirmations[index_soft_confirmation].prev_hash(), + previous_batch_hash, + "Soft confirmation previous hash must match the hash of the block before" + ); + assert_eq!( soft_confirmations[index_soft_confirmation].da_slot_hash(), da_block_headers[index_headers].hash().into(), @@ -524,6 +524,7 @@ where "Soft confirmation DA slot height must match DA block header height" ); + previous_batch_hash = soft_confirmations[index_soft_confirmation].hash(); index_soft_confirmation += 1; while index_soft_confirmation < soft_confirmations.len() { @@ -539,6 +540,13 @@ where "Soft confirmation DA slot height must match DA block header height" ); + assert_eq!( + soft_confirmations[index_soft_confirmation].prev_hash(), + previous_batch_hash, + "Soft confirmation previous hash must match the hash of the block before" + ); + + previous_batch_hash = soft_confirmations[index_soft_confirmation].hash(); index_soft_confirmation += 1; } else { index_headers += 1; @@ -571,6 +579,13 @@ where "Soft confirmation DA slot height must match DA block header height" ); + assert_eq!( + soft_confirmations[index_soft_confirmation].prev_hash(), + previous_batch_hash, + "Soft confirmation previous hash must match the hash of the block before" + ); + + previous_batch_hash = soft_confirmations[index_soft_confirmation].hash(); index_soft_confirmation += 1; } } diff --git a/crates/sovereign-sdk/module-system/sov-modules-stf-blueprint/src/stf_blueprint.rs b/crates/sovereign-sdk/module-system/sov-modules-stf-blueprint/src/stf_blueprint.rs index 51d81c655..0246ec2d7 100644 --- a/crates/sovereign-sdk/module-system/sov-modules-stf-blueprint/src/stf_blueprint.rs +++ b/crates/sovereign-sdk/module-system/sov-modules-stf-blueprint/src/stf_blueprint.rs @@ -6,6 +6,7 @@ use sov_modules_api::{ }; use sov_rollup_interface::soft_confirmation::SignedSoftConfirmationBatch; use sov_rollup_interface::stf::{BatchReceipt, TransactionReceipt}; +use sov_state::Storage; #[cfg(all(target_os = "zkvm", feature = "bench"))] use sov_zk_cycle_macros::cycle_tracker; #[cfg(feature = "native")] @@ -154,6 +155,7 @@ where &self, checkpoint: StateCheckpoint, soft_batch: &mut SignedSoftConfirmationBatch, + pre_state_root: &::Root, ) -> (Result<(), ApplySoftConfirmationError>, WorkingSet) { native_debug!( "Beginning soft batch 0x{} from sequencer: 0x{}", @@ -165,7 +167,10 @@ where // ApplySoftConfirmationHook: begin if let Err(e) = self.runtime.begin_soft_confirmation_hook( - &mut HookSoftConfirmationInfo::from(soft_batch.clone()), + &mut HookSoftConfirmationInfo::new( + soft_batch.clone(), + pre_state_root.as_ref().to_vec(), + ), &mut batch_workspace, ) { native_error!( @@ -210,7 +215,8 @@ where ( Ok(BatchReceipt { - batch_hash: soft_batch.hash(), + hash: soft_batch.hash(), + prev_hash: soft_batch.prev_hash(), tx_receipts, phantom_data: PhantomData, }), @@ -223,8 +229,9 @@ where &self, checkpoint: StateCheckpoint, soft_batch: &mut SignedSoftConfirmationBatch, + pre_state_root: &::Root, ) -> (ApplySoftConfirmationResult, StateCheckpoint) { - match self.begin_soft_confirmation_inner(checkpoint, soft_batch) { + match self.begin_soft_confirmation_inner(checkpoint, soft_batch, pre_state_root) { (Ok(()), batch_workspace) => { // TODO: wait for txs here, apply_sov_txs can be called multiple times let (batch_workspace, tx_receipts) = diff --git a/crates/sovereign-sdk/rollup-interface/src/node/rpc/mod.rs b/crates/sovereign-sdk/rollup-interface/src/node/rpc/mod.rs index 682e1b767..f374d5dc7 100644 --- a/crates/sovereign-sdk/rollup-interface/src/node/rpc/mod.rs +++ b/crates/sovereign-sdk/rollup-interface/src/node/rpc/mod.rs @@ -208,15 +208,15 @@ pub struct SoftBatchResponse { /// The hash of the soft batch. #[serde(with = "hex::serde")] pub hash: [u8; 32], + /// The hash of the previous soft batch. + #[serde(with = "hex::serde")] + pub prev_hash: [u8; 32], /// The transactions in this batch. #[serde(skip_serializing_if = "Option::is_none")] pub txs: Option>, - /// Pre-state root of the soft batch. - #[serde(with = "hex::serde")] - pub pre_state_root: Vec, - /// Post-state root of the soft batch. + /// State root of the soft batch. #[serde(with = "hex::serde")] - pub post_state_root: Vec, + pub state_root: Vec, /// Signature of the batch #[serde(with = "hex::serde")] pub soft_confirmation_signature: Vec, diff --git a/crates/sovereign-sdk/rollup-interface/src/state_machine/soft_confirmation.rs b/crates/sovereign-sdk/rollup-interface/src/state_machine/soft_confirmation.rs index 1695b268e..6d43dd93b 100644 --- a/crates/sovereign-sdk/rollup-interface/src/state_machine/soft_confirmation.rs +++ b/crates/sovereign-sdk/rollup-interface/src/state_machine/soft_confirmation.rs @@ -15,7 +15,6 @@ pub struct UnsignedSoftConfirmationBatch { da_slot_height: u64, da_slot_hash: [u8; 32], da_slot_txs_commitment: [u8; 32], - pre_state_root: Vec, txs: Vec>, deposit_data: Vec>, l1_fee_rate: u128, @@ -29,7 +28,6 @@ impl UnsignedSoftConfirmationBatch { da_slot_height: u64, da_slot_hash: [u8; 32], da_slot_txs_commitment: [u8; 32], - pre_state_root: Vec, txs: Vec>, deposit_data: Vec>, l1_fee_rate: u128, @@ -39,7 +37,6 @@ impl UnsignedSoftConfirmationBatch { da_slot_height, da_slot_hash, da_slot_txs_commitment, - pre_state_root, txs, deposit_data, l1_fee_rate, @@ -58,10 +55,6 @@ impl UnsignedSoftConfirmationBatch { pub fn da_slot_txs_commitment(&self) -> [u8; 32] { self.da_slot_txs_commitment } - /// Previous batch's pre state root - pub fn pre_state_root(&self) -> Vec { - self.pre_state_root.clone() - } /// Raw transactions. pub fn txs(&self) -> Vec> { self.txs.clone() @@ -85,10 +78,10 @@ impl UnsignedSoftConfirmationBatch { #[derive(Debug, PartialEq, Clone, BorshDeserialize, BorshSerialize, Serialize, Deserialize, Eq)] pub struct SignedSoftConfirmationBatch { hash: [u8; 32], + prev_hash: [u8; 32], da_slot_height: u64, da_slot_hash: [u8; 32], da_slot_txs_commitment: [u8; 32], - pre_state_root: Vec, l1_fee_rate: u128, txs: Vec>, signature: Vec, @@ -102,10 +95,10 @@ impl SignedSoftConfirmationBatch { #[allow(clippy::too_many_arguments)] pub fn new( hash: [u8; 32], + prev_hash: [u8; 32], da_slot_height: u64, da_slot_hash: [u8; 32], da_slot_txs_commitment: [u8; 32], - pre_state_root: Vec, l1_fee_rate: u128, txs: Vec>, deposit_data: Vec>, @@ -115,10 +108,10 @@ impl SignedSoftConfirmationBatch { ) -> SignedSoftConfirmationBatch { Self { hash, + prev_hash, da_slot_height, da_slot_hash, da_slot_txs_commitment, - pre_state_root, l1_fee_rate, txs, deposit_data, @@ -128,11 +121,16 @@ impl SignedSoftConfirmationBatch { } } - /// Hash of the unsigned batch + /// Hash of the signed batch pub fn hash(&self) -> [u8; 32] { self.hash } + /// Hash of the previous signed batch + pub fn prev_hash(&self) -> [u8; 32] { + self.prev_hash + } + /// DA block this soft confirmation was given for pub fn da_slot_height(&self) -> u64 { self.da_slot_height @@ -148,11 +146,6 @@ impl SignedSoftConfirmationBatch { self.da_slot_txs_commitment } - /// Previous batch's pre state root - pub fn pre_state_root(&self) -> Vec { - self.pre_state_root.clone() - } - /// Public key of signer pub fn sequencer_pub_key(&self) -> &[u8] { self.pub_key.as_ref() diff --git a/crates/sovereign-sdk/rollup-interface/src/state_machine/stf.rs b/crates/sovereign-sdk/rollup-interface/src/state_machine/stf.rs index 2cd0045dd..581defdcd 100644 --- a/crates/sovereign-sdk/rollup-interface/src/state_machine/stf.rs +++ b/crates/sovereign-sdk/rollup-interface/src/state_machine/stf.rs @@ -73,7 +73,9 @@ pub struct TransactionReceipt { /// A receipt giving the outcome of a batch of transactions pub struct BatchReceipt { /// The canonical hash of this batch - pub batch_hash: [u8; 32], + pub hash: [u8; 32], + /// The canonical hash of previous batch + pub prev_hash: [u8; 32], /// The receipts of all the transactions in this batch. pub tx_receipts: Vec>, /// Any additional structured data to be saved in the database and served over RPC @@ -90,15 +92,15 @@ pub struct SoftBatchReceipt /// DA layer transactions commitment pub da_slot_txs_commitment: ::SlotHash, /// The canonical hash of this batch - pub batch_hash: [u8; 32], + pub hash: [u8; 32], + /// The canonical hash of the previous batch + pub prev_hash: [u8; 32], /// The receipts of all the transactions in this batch. pub tx_receipts: Vec>, /// Any additional structured data to be saved in the database and served over RPC pub phantom_data: PhantomData, - /// Pre state root - pub pre_state_root: Vec, - /// Post state root - pub post_state_root: Vec, + /// state root + pub state_root: Vec, /// Soft confirmation signature computed from borsh serialization of da_slot_height, da_slot_hash, pre_state_root, txs pub soft_confirmation_signature: Vec, /// Sequencer public key @@ -253,6 +255,7 @@ pub trait StateTransitionFunction { sequencer_public_key: &[u8], sequencer_da_public_key: &[u8], initial_state_root: &Self::StateRoot, + initial_batch_hash: [u8; 32], pre_state: Self::PreState, da_data: Vec<::BlobTransaction>, witnesses: VecDeque>, diff --git a/crates/sovereign-sdk/rollup-interface/src/state_machine/stf/fuzzing.rs b/crates/sovereign-sdk/rollup-interface/src/state_machine/stf/fuzzing.rs index c9e7ed62c..61c1a0064 100644 --- a/crates/sovereign-sdk/rollup-interface/src/state_machine/stf/fuzzing.rs +++ b/crates/sovereign-sdk/rollup-interface/src/state_machine/stf/fuzzing.rs @@ -191,7 +191,8 @@ impl Arbitrary for BatchReceipt< None => batch_hash, }; Self { - batch_hash, + hash: batch_hash, + prev_hash: batch_hash, tx_receipts: txs, phantom_data: PhantomData, } diff --git a/crates/sovereign-sdk/rollup-interface/src/state_machine/zk/mod.rs b/crates/sovereign-sdk/rollup-interface/src/state_machine/zk/mod.rs index b9180be97..30f45d9a0 100644 --- a/crates/sovereign-sdk/rollup-interface/src/state_machine/zk/mod.rs +++ b/crates/sovereign-sdk/rollup-interface/src/state_machine/zk/mod.rs @@ -124,6 +124,8 @@ pub struct StateTransition { pub initial_state_root: Root, /// The state of the rollup after the transition pub final_state_root: Root, + /// The hash before the state transition + pub initial_batch_hash: [u8; 32], /// State diff of L2 blocks in the processed sequencer commitments. pub state_diff: CumulativeStateDiff, /// The DA slot hash that the sequencer commitments causing this state transition were found in. @@ -132,7 +134,6 @@ pub struct StateTransition { pub sequencer_public_key: Vec, /// Sequencer DA public key. pub sequencer_da_public_key: Vec, - /// An additional validity condition for the state transition which needs /// to be checked outside of the zkVM circuit. This typically corresponds to /// some claim about the DA layer history, such as (X) is a valid block on the DA layer @@ -166,6 +167,8 @@ pub struct StateTransitionData { pub initial_state_root: StateRoot, /// The state root after the state transition pub final_state_root: StateRoot, + /// The hash before the state transition + pub initial_batch_hash: [u8; 32], /// The `crate::da::DaData` that are being processed as blobs. Everything that's not `crate::da::DaData::SequencerCommitment` will be ignored. pub da_data: Vec, /// DA block header that the sequencer commitments were found in.