diff --git a/crates/btcio/src/reader/query.rs b/crates/btcio/src/reader/query.rs index 00b2ee553..9b404e9a7 100644 --- a/crates/btcio/src/reader/query.rs +++ b/crates/btcio/src/reader/query.rs @@ -14,7 +14,7 @@ use strata_state::l1::{ }; use strata_status::StatusTx; use strata_tx_parser::{ - filter::{derive_tx_filter_rules, filter_relevant_txs, TxFilterRule}, + filter::{filter_protocol_op_tx_refs, TxFilterConfig}, messages::{BlockData, L1Event}, }; use tokio::sync::mpsc; @@ -70,13 +70,9 @@ async fn do_reader_task( let cur_best_height = state.best_block_idx(); let poll_span = debug_span!("l1poll", %cur_best_height); - // Maybe this should be called outside loop? - let filters = derive_tx_filter_rules(config.params.rollup())?; - if let Err(err) = poll_for_new_blocks( client, event_tx, - &filters, &mut state, &mut status_updates, config.params.as_ref(), @@ -148,7 +144,6 @@ async fn init_reader_state( async fn poll_for_new_blocks( client: &impl Reader, event_tx: &mpsc::Sender, - filters: &[TxFilterRule], state: &mut ReaderState, status_updates: &mut Vec, params: &Params, @@ -193,7 +188,6 @@ async fn poll_for_new_blocks( event_tx, state, status_updates, - filters, params, ) .await @@ -238,13 +232,13 @@ async fn fetch_and_process_block( event_tx: &mpsc::Sender, state: &mut ReaderState, status_updates: &mut Vec, - filters: &[TxFilterRule], params: &Params, ) -> anyhow::Result { let block = client.get_block_at(height).await?; let txs = block.txdata.len(); - let filtered_txs = filter_relevant_txs(&block, filters); + let filter_config = TxFilterConfig::from_rollup_params(params.rollup())?; + let filtered_txs = filter_protocol_op_tx_refs(&block, filter_config); let block_data = BlockData::new(height, block, filtered_txs); let l1blkid = block_data.block().block_hash(); trace!(%height, %l1blkid, %txs, "fetched block from client"); diff --git a/crates/proof-impl/btc-blockspace/src/filter.rs b/crates/proof-impl/btc-blockspace/src/filter.rs index c77c492ba..3f65d697f 100644 --- a/crates/proof-impl/btc-blockspace/src/filter.rs +++ b/crates/proof-impl/btc-blockspace/src/filter.rs @@ -7,18 +7,19 @@ use strata_state::{ batch::BatchCheckpoint, tx::{DepositInfo, ProtocolOperation}, }; -use strata_tx_parser::filter::{derive_tx_filter_rules, filter_relevant_txs}; +use strata_tx_parser::filter::{filter_protocol_op_tx_refs, TxFilterConfig}; pub fn extract_relevant_info( block: &Block, rollup_params: &RollupParams, ) -> (Vec, Option) { - let filters = derive_tx_filter_rules(rollup_params).expect("derive tx-filter rules"); + let filter_config = + TxFilterConfig::from_rollup_params(rollup_params).expect("derive tx-filter config"); let mut deposits = Vec::new(); let mut prev_checkpoint = None; - let relevant_txs = filter_relevant_txs(block, &filters); + let relevant_txs = filter_protocol_op_tx_refs(block, filter_config); for tx in relevant_txs { match tx.proto_op() { diff --git a/crates/tx-parser/src/filter.rs b/crates/tx-parser/src/filter.rs index 26b6d642b..993dd7cfd 100644 --- a/crates/tx-parser/src/filter.rs +++ b/crates/tx-parser/src/filter.rs @@ -2,6 +2,7 @@ use bitcoin::{Block, Transaction}; use borsh::{BorshDeserialize, BorshSerialize}; use strata_primitives::{ buf::Buf32, + l1::BitcoinAddress, params::{OperatorConfig, RollupParams}, prelude::DepositTxParams, }; @@ -24,6 +25,77 @@ pub enum TxFilterRule { DepositRequest(DepositTxParams), /// Deposit transaction with deposit config and address Deposit(DepositTxParams), + /// Addresses that are spent to + SpentToAddrs(Vec), + /// Blob ids that are expected + BlobIds(Vec), + /// Outpoints + Outpoints(Vec<(Buf32, u32)>), +} + +#[derive(Clone, Debug)] +pub struct TxFilterConfig { + /// For checkpoint update inscriptions. + rollup_name: RollupName, + + /// For addresses that we expect spends to. + expected_addrs: Vec, + + /// For blobs we expect to be written. + expected_blobs: Vec, + + /// For deposits that might be spent from. + expected_outpoints: Vec<(Buf32, u32)>, + // eventually, in future version + // For bridge V1 deposits that do bitmap flagging for the multisig addr. + // operator_key_tbl: PublickeyTable, + /// EE addr length + ee_addr_len: u8, + + /// Deposit denomination + deposit_denomination: u64, // sats + + /// Operators addr + operator_addr: BitcoinAddress, +} + +impl TxFilterConfig { + pub fn from_rollup_params(rollup_params: &RollupParams) -> anyhow::Result { + let operator_wallet_pks = get_operator_wallet_pks(rollup_params); + let address = generate_taproot_address(&operator_wallet_pks, rollup_params.network)?; + + let rollup_name = rollup_params.rollup_name.clone(); + let expected_blobs = Vec::new(); // TODO: this should come from chainstate + let expected_addrs = vec![address.clone()]; + let expected_outpoints = Vec::new(); + + Ok(Self { + rollup_name, + expected_blobs, + expected_addrs, + expected_outpoints, + ee_addr_len: rollup_params.address_length, + deposit_denomination: rollup_params.deposit_amount, + operator_addr: address, + }) + } + + pub fn into_rules(self) -> Vec { + let deposit_params = DepositTxParams { + magic_bytes: self.rollup_name.clone().into_bytes().to_vec(), + address_length: self.ee_addr_len, + deposit_amount: self.deposit_denomination, + address: self.operator_addr, + }; + vec![ + TxFilterRule::RollupInscription(self.rollup_name.clone()), + TxFilterRule::SpentToAddrs(self.expected_addrs), + TxFilterRule::BlobIds(self.expected_blobs), + TxFilterRule::Outpoints(self.expected_outpoints), + TxFilterRule::Deposit(deposit_params.clone()), + TxFilterRule::DepositRequest(deposit_params), + ] + } } type RollupName = String; @@ -47,14 +119,20 @@ pub fn derive_tx_filter_rules(params: &RollupParams) -> anyhow::Result Vec { + +/// Filter protocol operatios as refs from relevant [`Transaction`]s in a block based on given +/// [`TxFilterRule`]s +pub fn filter_protocol_op_tx_refs( + block: &Block, + filter_config: TxFilterConfig, +) -> Vec { + let filter_rules = filter_config.into_rules(); block .txdata .iter() .enumerate() .filter_map(|(i, tx)| { - check_and_extract_relevant_info(tx, filters) + extract_protocol_op(tx, &filter_rules) .map(|relevant_tx| ProtocolOpTxRef::new(i as u32, relevant_tx)) }) .collect() @@ -62,31 +140,24 @@ pub fn filter_relevant_txs(block: &Block, filters: &[TxFilterRule]) -> Vec Option { +fn extract_protocol_op(tx: &Transaction, filters: &[TxFilterRule]) -> Option { filters.iter().find_map(|rel_type| match rel_type { - TxFilterRule::RollupInscription(name) => { - if !tx.input.is_empty() { - if let Some(scr) = tx.input[0].witness.tapscript() { - if let Ok(inscription_data) = parse_inscription_data(&scr.into(), name) { - if let Ok(signed_batch) = borsh::from_slice::( - inscription_data.batch_data(), - ) { - return Some(ProtocolOperation::RollupInscription(signed_batch)); - } - } - } - } - None - } + TxFilterRule::RollupInscription(name) => tx.input.first().and_then(|inp| { + inp.witness + .tapscript() + .and_then(|scr| parse_inscription_data(&scr.into(), name).ok()) + .and_then(|data| borsh::from_slice::(data.batch_data()).ok()) + .map(ProtocolOperation::RollupInscription) + }), TxFilterRule::DepositRequest(config) => extract_deposit_request_info(tx, config) .map(|deposit_req_info| Some(ProtocolOperation::DepositRequest(deposit_req_info)))?, TxFilterRule::Deposit(config) => extract_deposit_info(tx, config) .map(|deposit_info| Some(ProtocolOperation::Deposit(deposit_info)))?, + + // TODO: add others + _ => None, }) } @@ -121,7 +192,7 @@ mod test { build_test_deposit_request_script, build_test_deposit_script, create_transaction_two_outpoints, get_deposit_tx_config, test_taproot_addr, }, - filter::{filter_relevant_txs, TxFilterRule}, + filter::{filter_protocol_op_tx_refs, TxFilterRule}, }; const OTHER_ADDR: &str = "bcrt1q6u6qyya3sryhh42lahtnz2m7zuufe7dlt8j0j5"; @@ -206,7 +277,7 @@ mod test { let block = create_test_block(vec![tx]); let txids: Vec = - filter_relevant_txs(&block, &[TxFilterRule::RollupInscription(rollup_name)]) + filter_protocol_op_tx_refs(&block, &[TxFilterRule::RollupInscription(rollup_name)]) .iter() .map(|op_refs| op_refs.index()) .collect(); @@ -217,7 +288,7 @@ mod test { let rollup_name = "TestRollup".to_string(); let tx = create_inscription_tx(rollup_name.clone()); let block = create_test_block(vec![tx]); - let result = filter_relevant_txs( + let result = filter_protocol_op_tx_refs( &block, &[TxFilterRule::RollupInscription("invalid_name".to_string())], ); @@ -232,7 +303,7 @@ mod test { let rollup_name = "alpenstrata".to_string(); let txids: Vec = - filter_relevant_txs(&block, &[TxFilterRule::RollupInscription(rollup_name)]) + filter_protocol_op_tx_refs(&block, &[TxFilterRule::RollupInscription(rollup_name)]) .iter() .map(|op_refs| op_refs.index()) .collect(); @@ -248,7 +319,7 @@ mod test { let block = create_test_block(vec![tx1, tx2, tx3]); let txids: Vec = - filter_relevant_txs(&block, &[TxFilterRule::RollupInscription(rollup_name)]) + filter_protocol_op_tx_refs(&block, &[TxFilterRule::RollupInscription(rollup_name)]) .iter() .map(|op_refs| op_refs.index()) .collect(); @@ -272,7 +343,7 @@ mod test { let block = create_test_block(vec![tx]); let filters = vec![TxFilterRule::Deposit(config.clone())]; - let result = filter_relevant_txs(&block, &filters); + let result = filter_protocol_op_tx_refs(&block, &filters); assert_eq!(result.len(), 1, "Should find one relevant transaction"); assert_eq!( @@ -315,7 +386,7 @@ mod test { let block = create_test_block(vec![tx]); let relevant_types = vec![TxFilterRule::DepositRequest(config.clone())]; - let result = filter_relevant_txs(&block, &relevant_types); + let result = filter_protocol_op_tx_refs(&block, &relevant_types); assert_eq!(result.len(), 1, "Should find one relevant transaction"); assert_eq!( @@ -350,7 +421,7 @@ mod test { let block = create_test_block(vec![irrelevant_tx]); let relevant_types = vec![TxFilterRule::Deposit(config)]; - let result = filter_relevant_txs(&block, &relevant_types); + let result = filter_protocol_op_tx_refs(&block, &relevant_types); assert!( result.is_empty(), @@ -383,7 +454,7 @@ mod test { let block = create_test_block(vec![tx1, tx2]); let relevant_types = vec![TxFilterRule::Deposit(config.clone())]; - let result = filter_relevant_txs(&block, &relevant_types); + let result = filter_protocol_op_tx_refs(&block, &relevant_types); assert_eq!(result.len(), 2, "Should find two relevant transactions"); assert_eq!(