From 04957067d2151952ea94d24641941fac0445295b Mon Sep 17 00:00:00 2001 From: junderw Date: Mon, 7 Oct 2024 19:02:41 +0900 Subject: [PATCH] Remove clones and limit max address count --- src/new_index/mempool.rs | 14 ++++----- src/new_index/schema.rs | 14 ++++----- src/rest.rs | 62 ++++++++++++++++++++++++++++++++++------ 3 files changed, 67 insertions(+), 23 deletions(-) diff --git a/src/new_index/mempool.rs b/src/new_index/mempool.rs index c60e4894..32d27c1f 100644 --- a/src/new_index/mempool.rs +++ b/src/new_index/mempool.rs @@ -179,7 +179,7 @@ impl Mempool { pub fn history_group( &self, - scripthashes: Vec<[u8; 32]>, + scripthashes: &[[u8; 32]], last_seen_txid: Option<&Txid>, limit: usize, ) -> Vec { @@ -188,7 +188,7 @@ impl Mempool { .with_label_values(&["history_group"]) .start_timer(); scripthashes - .into_iter() + .iter() .filter_map(|scripthash| self.history.get(&scripthash[..])) .flat_map(|entries| entries.iter()) .map(|e| e.get_txid()) @@ -208,12 +208,12 @@ impl Mempool { .collect() } - pub fn history_txids_iter_group( - &self, - scripthashes: Vec<[u8; 32]>, - ) -> impl Iterator + '_ { + pub fn history_txids_iter_group<'a>( + &'a self, + scripthashes: &'a [[u8; 32]], + ) -> impl Iterator + 'a { scripthashes - .into_iter() + .iter() .filter_map(move |scripthash| self.history.get(&scripthash[..])) .flat_map(|entries| entries.iter()) .map(|entry| entry.get_txid()) diff --git a/src/new_index/schema.rs b/src/new_index/schema.rs index fed2c1e1..566bc6b8 100644 --- a/src/new_index/schema.rs +++ b/src/new_index/schema.rs @@ -650,14 +650,14 @@ impl ChainQuery { pub fn summary_group( &self, - scripthashes: Vec<[u8; 32]>, + scripthashes: &[[u8; 32]], last_seen_txid: Option<&Txid>, limit: usize, ) -> Vec { // scripthash lookup let _timer_scan = self.start_timer("address_group_summary"); let rows = self - .history_iter_scan_group_reverse(b'H', &scripthashes) + .history_iter_scan_group_reverse(b'H', scripthashes) .map(TxHistoryRow::from_row); self.collate_summaries(rows, last_seen_txid, limit) @@ -730,7 +730,7 @@ impl ChainQuery { pub fn history_group( &self, - scripthashes: Vec<[u8; 32]>, + scripthashes: &[[u8; 32]], last_seen_txid: Option<&Txid>, limit: usize, ) -> Vec<(Transaction, BlockId)> { @@ -740,9 +740,9 @@ impl ChainQuery { pub fn history_txids_iter_group( &self, - scripthashes: Vec<[u8; 32]>, + scripthashes: &[[u8; 32]], ) -> impl Iterator + '_ { - self.history_iter_scan_group_reverse(b'H', &scripthashes) + self.history_iter_scan_group_reverse(b'H', scripthashes) .map(|row| TxHistoryRow::from_row(row).get_txid()) .unique() } @@ -750,14 +750,14 @@ impl ChainQuery { fn _history_group( &self, code: u8, - hashes: Vec<[u8; 32]>, + hashes: &[[u8; 32]], last_seen_txid: Option<&Txid>, limit: usize, ) -> Vec<(Transaction, BlockId)> { print!("limit {} | last_seen {:?}", limit, last_seen_txid); let _timer_scan = self.start_timer("history_group"); let txs_conf = self - .history_iter_scan_group_reverse(code, &hashes) + .history_iter_scan_group_reverse(code, hashes) .map(|row| TxHistoryRow::from_row(row).get_txid()) // XXX: unique() requires keeping an in-memory list of all txids, can we avoid that? .unique() diff --git a/src/rest.rs b/src/rest.rs index a7666754..cc2cca01 100644 --- a/src/rest.rs +++ b/src/rest.rs @@ -42,6 +42,8 @@ use std::thread; use url::form_urlencoded; const ADDRESS_SEARCH_LIMIT: usize = 10; +// Limit to 300 addresses +const MULTI_ADDRESS_LIMIT: usize = 300; #[cfg(feature = "liquid")] const ASSETS_PER_PAGE: usize = 25; @@ -944,8 +946,24 @@ fn handle_request( _ => "", }; - let script_hashes: Vec<[u8; 32]> = serde_json::from_slice::>(&body) - .map_err(|err| HttpError::from(err.to_string()))? + if multi_address_too_long(&body) { + return Err(HttpError( + StatusCode::UNPROCESSABLE_ENTITY, + String::from("body too long"), + )); + } + + let script_hashes: Vec = + serde_json::from_slice(&body).map_err(|err| HttpError::from(err.to_string()))?; + + if script_hashes.len() > MULTI_ADDRESS_LIMIT { + return Err(HttpError( + StatusCode::UNPROCESSABLE_ENTITY, + String::from("body too long"), + )); + } + + let script_hashes: Vec<[u8; 32]> = script_hashes .iter() .filter_map(|script_str| { to_scripthash(script_type, script_str, config.network_type).ok() @@ -965,14 +983,14 @@ fn handle_request( if let Some(given_txid) = &after_txid { let is_mempool = query .mempool() - .history_txids_iter_group(script_hashes.clone()) + .history_txids_iter_group(&script_hashes) .any(|txid| given_txid == &txid); let is_confirmed = if is_mempool { false } else { query .chain() - .history_txids_iter_group(script_hashes.clone()) + .history_txids_iter_group(&script_hashes) .any(|txid| given_txid == &txid) }; if !is_mempool && !is_confirmed { @@ -985,7 +1003,7 @@ fn handle_request( txs.extend( query .mempool() - .history_group(script_hashes.clone(), after_txid.as_ref(), max_txs) + .history_group(&script_hashes, after_txid.as_ref(), max_txs) .into_iter() .map(|tx| (tx, None)), ); @@ -1001,7 +1019,7 @@ fn handle_request( txs.extend( query .chain() - .history_group(script_hashes, after_txid_ref, max_txs - txs.len()) + .history_group(&script_hashes, after_txid_ref, max_txs - txs.len()) .into_iter() .map(|(tx, blockid)| (tx, Some(blockid))), ); @@ -1095,8 +1113,25 @@ fn handle_request( "scripthashes" => "scripthash", _ => "", }; - let script_hashes: Vec<[u8; 32]> = serde_json::from_slice::>(&body) - .map_err(|err| HttpError::from(err.to_string()))? + + if multi_address_too_long(&body) { + return Err(HttpError( + StatusCode::UNPROCESSABLE_ENTITY, + String::from("body too long"), + )); + } + + let script_hashes: Vec = + serde_json::from_slice(&body).map_err(|err| HttpError::from(err.to_string()))?; + + if script_hashes.len() > MULTI_ADDRESS_LIMIT { + return Err(HttpError( + StatusCode::UNPROCESSABLE_ENTITY, + String::from("body too long"), + )); + } + + let script_hashes: Vec<[u8; 32]> = script_hashes .iter() .filter_map(|script_str| { to_scripthash(script_type, script_str, config.network_type).ok() @@ -1115,7 +1150,7 @@ fn handle_request( let summary = query .chain() - .summary_group(script_hashes, last_seen_txid.as_ref(), max_txs); + .summary_group(&script_hashes, last_seen_txid.as_ref(), max_txs); json_response(summary, TTL_SHORT) } @@ -1823,6 +1858,15 @@ fn parse_scripthash(scripthash: &str) -> Result { } } +#[inline] +fn multi_address_too_long(body: &hyper::body::Bytes) -> bool { + // ("",) (3) (quotes and comma between each entry) + // (\n ) (5) (allows for pretty printed JSON with 4 space indent) + // The opening [] and whatnot don't need to be accounted for, we give more than enough leeway + // p2tr and p2wsh are 55 length, scripthashes are 64. + body.len() > (8 + 64) * MULTI_ADDRESS_LIMIT +} + #[derive(Debug)] struct HttpError(StatusCode, String);