-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
l1-filter: Add TxFilterConfig #458
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
use borsh::{BorshDeserialize, BorshSerialize}; | ||
use strata_primitives::{ | ||
buf::Buf32, | ||
l1::BitcoinAddress, | ||
params::{OperatorConfig, RollupParams}, | ||
prelude::DepositTxParams, | ||
}; | ||
|
@@ -24,6 +25,77 @@ | |
DepositRequest(DepositTxParams), | ||
/// Deposit transaction with deposit config and address | ||
Deposit(DepositTxParams), | ||
/// Addresses that are spent to | ||
SpentToAddrs(Vec<BitcoinAddress>), | ||
/// Blob ids that are expected | ||
BlobIds(Vec<Buf32>), | ||
/// 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<BitcoinAddress>, | ||
|
||
/// For blobs we expect to be written. | ||
expected_blobs: Vec<Buf32>, | ||
|
||
/// For deposits that might be spent from. | ||
expected_outpoints: Vec<(Buf32, u32)>, | ||
Comment on lines
+47
to
+48
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should use a dedicated type for this. |
||
// 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, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should be max length. |
||
|
||
/// Deposit denomination | ||
deposit_denomination: u64, // sats | ||
Comment on lines
+55
to
+56
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't bake this in as a single value, it should be capable of being extended to support multiple denominations easily even if we don't support this today. |
||
|
||
/// Operators addr | ||
operator_addr: BitcoinAddress, | ||
Comment on lines
+58
to
+59
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Possibly redundant, this would be part of |
||
} | ||
|
||
impl TxFilterConfig { | ||
pub fn from_rollup_params(rollup_params: &RollupParams) -> anyhow::Result<Self> { | ||
let operator_wallet_pks = get_operator_wallet_pks(rollup_params); | ||
let address = generate_taproot_address(&operator_wallet_pks, rollup_params.network)?; | ||
|
||
Comment on lines
+63
to
+66
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We will be be computing the rules based on the last checkpoint's bridge state and the rollup params. I'm not sure this function should be on this type. |
||
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<TxFilterRule> { | ||
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,46 +119,45 @@ | |
TxFilterRule::Deposit(deposit_provider), | ||
]) | ||
} | ||
/// Filter all the relevant [`Transaction`]s in a block based on given [`TxFilterRule`]s | ||
pub fn filter_relevant_txs(block: &Block, filters: &[TxFilterRule]) -> Vec<ProtocolOpTxRef> { | ||
|
||
/// 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<ProtocolOpTxRef> { | ||
let filter_rules = filter_config.into_rules(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The config is converted to existing rules with added variants. We can add complex logic here if needed. But this should probably suffice. |
||
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() | ||
} | ||
|
||
/// if a [`Transaction`] is relevant based on given [`RelevantTxType`]s then we extract relevant | ||
/// info | ||
fn check_and_extract_relevant_info( | ||
tx: &Transaction, | ||
filters: &[TxFilterRule], | ||
) -> Option<ProtocolOperation> { | ||
fn extract_protocol_op(tx: &Transaction, filters: &[TxFilterRule]) -> Option<ProtocolOperation> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rework this to operate with the config directly. |
||
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::<SignedBatchCheckpoint>( | ||
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::<SignedBatchCheckpoint>(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 @@ | |
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 @@ | |
let block = create_test_block(vec![tx]); | ||
|
||
let txids: Vec<u32> = | ||
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 @@ | |
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 @@ | |
let rollup_name = "alpenstrata".to_string(); | ||
|
||
let txids: Vec<u32> = | ||
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 @@ | |
let block = create_test_block(vec![tx1, tx2, tx3]); | ||
|
||
let txids: Vec<u32> = | ||
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 @@ | |
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 @@ | |
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 @@ | |
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 @@ | |
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!( | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These vecs can be sorted and then we can do binary search.
Having
Vec
instead of Individual items lets us group the items, sort and do binary search as we want.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't make sense to have a list of rules where there there's multiple entries of the same variant. And in most cases we expect that all variants will end up being present exactly once. The ticket specified using a struct for this instead of a list of enum instances, and we'd do the filtering based on that directly.