mirror of
https://github.com/romanz/electrs.git
synced 2024-11-19 01:43:29 +01:00
Limit index lookups to prevent DoS for "popular" addresses
This commit is contained in:
parent
321dc23767
commit
31765e3e49
@ -86,7 +86,7 @@ name = "ignore_mempool"
|
||||
doc = "Don't sync mempool - queries will show only confirmed transactions."
|
||||
|
||||
[[param]]
|
||||
name = "txid_limit"
|
||||
name = "index_lookup_limit"
|
||||
type = "usize"
|
||||
doc = "Number of transactions to lookup before returning an error, to prevent 'too popular' addresses from causing the RPC server to get stuck (0 - disable the limit)"
|
||||
default = "100"
|
||||
|
@ -130,6 +130,7 @@ pub struct Config {
|
||||
pub monitoring_addr: SocketAddr,
|
||||
pub wait_duration: Duration,
|
||||
pub index_batch_size: usize,
|
||||
pub index_lookup_limit: Option<usize>,
|
||||
pub ignore_mempool: bool,
|
||||
pub server_banner: String,
|
||||
pub args: Vec<String>,
|
||||
@ -275,6 +276,10 @@ impl Config {
|
||||
_ => log::LevelFilter::Trace,
|
||||
};
|
||||
|
||||
let index_lookup_limit = match config.index_lookup_limit {
|
||||
0 => None,
|
||||
_ => Some(config.index_lookup_limit),
|
||||
};
|
||||
let config = Config {
|
||||
network: config.network,
|
||||
db_path: config.db_dir,
|
||||
@ -286,6 +291,7 @@ impl Config {
|
||||
monitoring_addr,
|
||||
wait_duration: Duration::from_secs(config.wait_duration_secs),
|
||||
index_batch_size: config.index_batch_size,
|
||||
index_lookup_limit,
|
||||
ignore_mempool: config.ignore_mempool,
|
||||
server_banner: config.server_banner,
|
||||
args: args.map(|a| a.into_string().unwrap()).collect(),
|
||||
|
21
src/index.rs
21
src/index.rs
@ -85,12 +85,18 @@ impl IndexResult {
|
||||
/// Confirmed transactions' address index
|
||||
pub struct Index {
|
||||
store: DBStore,
|
||||
lookup_limit: Option<usize>,
|
||||
chain: Chain,
|
||||
stats: Stats,
|
||||
}
|
||||
|
||||
impl Index {
|
||||
pub(crate) fn load(store: DBStore, mut chain: Chain, metrics: &Metrics) -> Result<Self> {
|
||||
pub(crate) fn load(
|
||||
store: DBStore,
|
||||
mut chain: Chain,
|
||||
metrics: &Metrics,
|
||||
lookup_limit: Option<usize>,
|
||||
) -> Result<Self> {
|
||||
if let Some(row) = store.get_tip() {
|
||||
let tip = deserialize(&row).expect("invalid tip");
|
||||
let headers = store
|
||||
@ -103,6 +109,7 @@ impl Index {
|
||||
|
||||
Ok(Index {
|
||||
store,
|
||||
lookup_limit,
|
||||
chain,
|
||||
stats: Stats::new(metrics),
|
||||
})
|
||||
@ -112,6 +119,18 @@ impl Index {
|
||||
&self.chain
|
||||
}
|
||||
|
||||
pub(crate) fn limit_result<T>(&self, entries: impl Iterator<Item = T>) -> Result<Vec<T>> {
|
||||
let mut entries = entries.fuse();
|
||||
let result: Vec<T> = match self.lookup_limit {
|
||||
Some(lookup_limit) => entries.by_ref().take(lookup_limit).collect(),
|
||||
None => entries.by_ref().collect(),
|
||||
};
|
||||
if entries.next().is_some() {
|
||||
bail!(">{} index entries, query may take too long", result.len())
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub(crate) fn filter_by_txid(&self, txid: Txid) -> impl Iterator<Item = BlockHash> + '_ {
|
||||
self.store
|
||||
.iter_txid(TxidRow::scan_prefix(txid))
|
||||
|
@ -288,7 +288,7 @@ impl ScriptHashStatus {
|
||||
type PosTxid = (u32, Txid);
|
||||
let mut result = HashMap::<BlockHash, HashMap<PosTxid, Entry>>::new();
|
||||
|
||||
let funding_blockhashes = index.filter_by_funding(self.scripthash);
|
||||
let funding_blockhashes = index.limit_result(index.filter_by_funding(self.scripthash))?;
|
||||
self.for_new_blocks(funding_blockhashes, daemon, |blockhash, block| {
|
||||
let txids: Vec<Txid> = block.txdata.iter().map(|tx| tx.txid()).collect();
|
||||
for (pos, (tx, txid)) in block.txdata.into_iter().zip(txids.iter()).enumerate() {
|
||||
|
@ -31,7 +31,8 @@ impl Tracker {
|
||||
let store = DBStore::open(Path::new(&config.db_path))?;
|
||||
let chain = Chain::new(config.network);
|
||||
Ok(Self {
|
||||
index: Index::load(store, chain, &metrics).context("failed to open index")?,
|
||||
index: Index::load(store, chain, &metrics, config.index_lookup_limit)
|
||||
.context("failed to open index")?,
|
||||
mempool: Mempool::new(),
|
||||
metrics,
|
||||
index_batch_size: config.index_batch_size,
|
||||
|
@ -40,7 +40,12 @@ echo `$BTC getblockchaininfo | jq -r '"Generated \(.blocks) regtest blocks (\(.s
|
||||
TIP=`$BTC getbestblockhash`
|
||||
|
||||
export RUST_LOG=electrs=debug
|
||||
electrs --db-dir=data/electrs --daemon-dir=data/bitcoin --network=regtest 2> data/electrs/regtest-debug.log &
|
||||
electrs \
|
||||
--index-lookup-limit 200 \
|
||||
--db-dir=data/electrs \
|
||||
--daemon-dir=data/bitcoin \
|
||||
--network=regtest \
|
||||
2> data/electrs/regtest-debug.log &
|
||||
ELECTRS_PID=$!
|
||||
tail_log data/electrs/regtest-debug.log | grep -m1 "serving Electrum RPC"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user