From 5b449a08e514b9ba9100e33bb4f952374827986c Mon Sep 17 00:00:00 2001 From: Prajwol Gyawali Date: Thu, 19 Mar 2026 17:39:46 +0545 Subject: [PATCH 1/2] refactor(stf): consolidate block validation into compute_asm_transition --- .../statements/src/moho_program/program.rs | 49 +++------------ crates/stf/src/stf.rs | 62 ++++++++++++------- crates/worker/src/state.rs | 23 ++----- 3 files changed, 50 insertions(+), 84 deletions(-) diff --git a/crates/proof/statements/src/moho_program/program.rs b/crates/proof/statements/src/moho_program/program.rs index 353bf67..e8935f5 100644 --- a/crates/proof/statements/src/moho_program/program.rs +++ b/crates/proof/statements/src/moho_program/program.rs @@ -1,18 +1,16 @@ //! [`MohoProgram`] implementation for the ASM STF. //! //! This module contains the [`AsmStfProgram`] type that implements [`MohoProgram`], wiring the -//! ASM state transition function into the Moho runtime. It handles block validation, state -//! commitment via SHA-256, transition execution, and extraction of post-transition artifacts -//! such as predicate updates and export state entries. -use bitcoin::hashes::Hash; +//! ASM state transition function into the Moho runtime. It handles state commitment via SHA-256, +//! transition execution, and extraction of post-transition artifacts such as predicate updates +//! and export state entries. use moho_runtime_interface::MohoProgram; use moho_types::{ExportState, InnerStateCommitment, StateReference}; use sha2::{Digest, Sha256}; -use strata_asm_common::{AnchorState, AsmSpec}; +use strata_asm_common::AnchorState; use strata_asm_logs::{AsmStfUpdate, NewExportEntry}; use strata_asm_spec::StrataAsmSpec; -use strata_asm_stf::{compute_asm_transition, group_txs_by_subprotocol, AsmStfInput, AsmStfOutput}; -use strata_identifiers::Buf32; +use strata_asm_stf::{compute_asm_transition, AsmStfOutput}; use strata_predicate::PredicateKey; use crate::moho_program::input::AsmStepInput; @@ -53,41 +51,8 @@ impl MohoProgram for AsmStfProgram { spec: &StrataAsmSpec, input: &AsmStepInput, ) -> AsmStfOutput { - // TODO: (@prajworlg) Consolidate block validation logic in a single place - // https://alpenlabs.atlassian.net/browse/STR-2619 - - // 1. Validate the input - assert!(input.validate_block()); - - // For blocks without witness data (pre-SegWit or legacy-only transactions), - // the witness merkle root equals the transaction merkle root per Bitcoin protocol. - let wtxids_root: Buf32 = input - .block - .0 - .witness_root() - .map(|root| root.as_raw_hash().to_byte_array()) - .unwrap_or_else(|| { - input - .block - .0 - .header - .merkle_root - .as_raw_hash() - .to_byte_array() - }) - .into(); - - // 2. Restructure the raw input to be formatted according to what we want. - let protocol_txs = group_txs_by_subprotocol(spec.magic_bytes(), &input.block.0.txdata); - let stf_input = AsmStfInput { - protocol_txs, - header: &input.block.0.header, - aux_data: input.aux_data.clone(), - wtxids_root, - }; - - // 3. Actually invoke the ASM state transition function. - compute_asm_transition(spec, pre_state, stf_input).expect("asm: compute transition") + compute_asm_transition(spec, pre_state, &input.block.0, &input.aux_data) + .expect("asm: compute transition") } fn extract_post_state(output: &Self::StepOutput) -> &Self::State { diff --git a/crates/stf/src/stf.rs b/crates/stf/src/stf.rs index 25ff840..8043cb1 100644 --- a/crates/stf/src/stf.rs +++ b/crates/stf/src/stf.rs @@ -3,59 +3,67 @@ //! view into a single deterministic state transition. // TODO rename this module to `transition` +use bitcoin::{Block, hashes::Hash}; use strata_asm_common::{ - AnchorState, AsmError, AsmManifest, AsmResult, AsmSpec, ChainViewState, VerifiedAuxData, + AnchorState, AsmError, AsmManifest, AsmResult, AsmSpec, AuxData, ChainViewState, + VerifiedAuxData, }; +use strata_identifiers::Buf32; use crate::{ + group_txs_by_subprotocol, manager::{AnchorStateLoader, SubprotoManager}, stage::{FinishStage, ProcessStage}, - types::{AsmStfInput, AsmStfOutput}, + types::AsmStfOutput, }; /// Computes the next AnchorState by applying the Anchor State Machine (ASM) state transition /// function (STF) to the given previous state and new L1 block. /// -/// This function performs the main ASM state transition by validating the block header continuity, -/// loading subprotocols with auxiliary input data, processing protocol-specific transactions, -/// handling inter-protocol communication, and constructing the final state with logs. -pub fn compute_asm_transition<'i, S: AsmSpec>( +/// This function performs the main ASM state transition by validating block integrity (merkle root, +/// witness commitment) and header continuity, loading subprotocols with auxiliary input data, +/// processing protocol-specific transactions, handling inter-protocol communication, and +/// constructing the final state with logs. +pub fn compute_asm_transition( spec: &S, pre_state: &AnchorState, - input: AsmStfInput<'i>, + block: &Block, + aux_data: &AuxData, ) -> AsmResult { - // 1. Validate and update PoW header continuity for the new block. + // 1. Validate that the block body merkle is consistent with the header. + assert!(block.check_merkle_root() && block.check_witness_commitment()); + + // 2. Validate and update PoW header continuity for the new block. // This ensures the block header follows proper Bitcoin consensus rules and chain continuity. let (mut pow_state, mut history_accumulator) = pre_state.chain_view.clone().into_parts(); pow_state - .check_and_update(input.header) + .check_and_update(&block.header) .map_err(AsmError::InvalidL1Header)?; let verified_aux_data = - VerifiedAuxData::try_new(&input.aux_data, &pre_state.chain_view.history_accumulator)?; + VerifiedAuxData::try_new(aux_data, &pre_state.chain_view.history_accumulator)?; // After `check_and_update`, `last_verified_block` points to the block we // just validated — i.e. the L1 block whose transactions we are about to // feed into subprotocols. let current_l1ref = &pow_state.last_verified_block; + // 3. Restructure the raw input to be formatted according to what we want. + let protocol_txs = group_txs_by_subprotocol(spec.magic_bytes(), &block.txdata); + let mut manager = SubprotoManager::new(); - // 2. LOAD: Initialize each subprotocol in the subproto manager with aux input data. + // 4. LOAD: Initialize each subprotocol in the subproto manager with aux input data. let mut loader = AnchorStateLoader::new(pre_state, &mut manager); spec.load_subprotocols(&mut loader); - // 3. PROCESS: Feed each subprotocol its filtered transactions for execution. + // 5. PROCESS: Feed each subprotocol its filtered transactions for execution. // This stage performs the actual state transitions for each subprotocol. - let mut process_stage = ProcessStage::new( - &mut manager, - current_l1ref, - input.protocol_txs, - verified_aux_data, - ); + let mut process_stage = + ProcessStage::new(&mut manager, current_l1ref, protocol_txs, verified_aux_data); spec.call_subprotocols(&mut process_stage); - // 4. FINISH: Allow each subprotocol to process buffered inter-protocol messages. + // 6. FINISH: Allow each subprotocol to process buffered inter-protocol messages. // This stage handles cross-protocol communication and finalizes state changes. // TODO probably will have change this to repeat the interproto message // processing phase until we have no more messages to deliver, or some @@ -63,19 +71,27 @@ pub fn compute_asm_transition<'i, S: AsmSpec>( let mut finish_stage = FinishStage::new(&mut manager, &pow_state.last_verified_block); spec.call_subprotocols(&mut finish_stage); - // 5. Construct the manifest with the logs. + // For blocks without witness data (pre-SegWit or legacy-only transactions), + // the witness merkle root equals the transaction merkle root per Bitcoin protocol. + let wtxids_root: Buf32 = block + .witness_root() + .map(|root| root.as_raw_hash().to_byte_array()) + .unwrap_or_else(|| block.header.merkle_root.as_raw_hash().to_byte_array()) + .into(); + + // 7. Construct the manifest with the logs. let (sections, logs) = manager.export_sections_and_logs(); let manifest = AsmManifest::new( current_l1ref.height(), *current_l1ref.blkid(), - input.wtxids_root.into(), + wtxids_root.into(), logs, ); - // 6. Append the manifest to the history accumulator + // 8. Append the manifest to the history accumulator history_accumulator.add_manifest(&manifest)?; - // 7. Construct the final `AnchorState` and output. + // 9. Construct the final `AnchorState` and output. let chain_view = ChainViewState { pow_state, history_accumulator, diff --git a/crates/worker/src/state.rs b/crates/worker/src/state.rs index 769942a..700875b 100644 --- a/crates/worker/src/state.rs +++ b/crates/worker/src/state.rs @@ -1,12 +1,12 @@ use std::sync::Arc; -use bitcoin::{Block, hashes::Hash}; +use bitcoin::Block; use strata_asm_common::{AnchorState, AsmHistoryAccumulatorState, AuxData, ChainViewState}; use strata_asm_params::AsmParams; use strata_asm_spec::StrataAsmSpec; -use strata_asm_stf::{AsmStfInput, AsmStfOutput}; +use strata_asm_stf::AsmStfOutput; use strata_btc_verification::HeaderVerificationState; -use strata_primitives::{Buf32, l1::L1BlockCommitment}; +use strata_primitives::l1::L1BlockCommitment; use strata_service::ServiceState; use strata_state::asm_state::AsmState; use tracing::field::Empty; @@ -118,26 +118,11 @@ impl AsmWorkerServiceState { resolver.resolve(&pre_process.aux_requests)? }; - // For blocks without witness data (pre-SegWit or legacy-only transactions), - // the witness merkle root equals the transaction merkle root per Bitcoin protocol. - let wtxids_root: Buf32 = block - .witness_root() - .map(|root| root.as_raw_hash().to_byte_array()) - .unwrap_or_else(|| block.header.merkle_root.as_raw_hash().to_byte_array()) - .into(); - - let stf_input = AsmStfInput { - protocol_txs: pre_process.txs, - header: &block.header, - wtxids_root, - aux_data: aux_data.clone(), - }; - // Asm transition. let stf_span = tracing::debug_span!("asm.stf.process"); let _stf_guard = stf_span.enter(); - strata_asm_stf::compute_asm_transition(&self.asm_spec, cur_state.state(), stf_input) + strata_asm_stf::compute_asm_transition(&self.asm_spec, cur_state.state(), block, &aux_data) .map(|output| (output, aux_data)) .map_err(WorkerError::AsmError) } From 4d3b8e7a96a2faa354803704c6d578fc117565c7 Mon Sep 17 00:00:00 2001 From: Prajwol Gyawali Date: Thu, 19 Mar 2026 20:02:30 +0545 Subject: [PATCH 2/2] chore(asm-proof-impl): remove unused dep --- crates/proof/statements/Cargo.toml | 3 ++- guest-builder/sp1/guest-asm/Cargo.lock | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/proof/statements/Cargo.toml b/crates/proof/statements/Cargo.toml index 464750b..f4c55e9 100644 --- a/crates/proof/statements/Cargo.toml +++ b/crates/proof/statements/Cargo.toml @@ -18,7 +18,7 @@ strata-asm-spec.workspace = true strata-asm-stf.workspace = true strata-btc-types = { workspace = true, optional = true } strata-btc-verification = { workspace = true, optional = true } -strata-identifiers.workspace = true +strata-identifiers = { workspace = true, optional = true } strata-l1-txfmt = { workspace = true, optional = true } strata-predicate.workspace = true strata-test-utils-btc = { workspace = true, optional = true } @@ -40,6 +40,7 @@ test-utils = [ "dep:strata-btc-verification", "dep:strata-l1-txfmt", "dep:strata-test-utils-btc", + "dep:strata-identifiers", ] [dev-dependencies] diff --git a/guest-builder/sp1/guest-asm/Cargo.lock b/guest-builder/sp1/guest-asm/Cargo.lock index dc8d2af..fc7144f 100644 --- a/guest-builder/sp1/guest-asm/Cargo.lock +++ b/guest-builder/sp1/guest-asm/Cargo.lock @@ -1589,7 +1589,6 @@ dependencies = [ "strata-asm-logs 0.1.0", "strata-asm-spec", "strata-asm-stf 0.1.0", - "strata-identifiers", "strata-predicate", "zkaleido 0.1.0 (git+https://github.com/alpenlabs/zkaleido?rev=750c209f5850af29e34c39c250c24e1c69ffa2aa)", "zkaleido-native-adapter",