Browse Source

Fail RPC queries for "too popular" addresses

Since electrs is not optimized for this use-case, the query may take a lot
of time (on HDDs, the transaction lookups take most of the time).

The limit can be disabled by passing --txid-limit=0 flag.
skip-invalid-blocks
Roman Zeyde 6 years ago
parent
commit
e1825fdc77
No known key found for this signature in database GPG Key ID: 87CAE5FA46917CBB
  1. 2
      src/bin/electrs.rs
  2. 8
      src/config.rs
  3. 18
      src/query.rs

2
src/bin/electrs.rs

@ -56,7 +56,7 @@ fn run_server(config: &Config) -> Result<()> {
let app = App::new(store, index, daemon, &config)?;
let tx_cache = TransactionCache::new(config.tx_cache_size);
let query = Query::new(app.clone(), &metrics, tx_cache);
let query = Query::new(app.clone(), &metrics, tx_cache, config.txid_limit);
let mut server = None; // Electrum RPC server
loop {

8
src/config.rs

@ -26,6 +26,7 @@ pub struct Config {
pub index_batch_size: usize,
pub bulk_index_threads: usize,
pub tx_cache_size: usize,
pub txid_limit: usize,
pub server_banner: String,
}
@ -109,6 +110,12 @@ impl Config {
.help("Number of transactions to keep in for query LRU cache")
.default_value("10000") // should be enough for a small wallet.
)
.arg(
Arg::with_name("txid_limit")
.long("txid-limit")
.help("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_value("100") // should take a few seconds on a HDD
)
.arg(
Arg::with_name("server_banner")
.long("server-banner")
@ -199,6 +206,7 @@ impl Config {
index_batch_size: value_t_or_exit!(m, "index_batch_size", usize),
bulk_index_threads,
tx_cache_size: value_t_or_exit!(m, "tx_cache_size", usize),
txid_limit: value_t_or_exit!(m, "txid_limit", usize),
server_banner: value_t_or_exit!(m, "server_banner", String),
};
eprintln!("{:?}", config);

18
src/query.rs

@ -203,14 +203,21 @@ pub struct Query {
app: Arc<App>,
tracker: RwLock<Tracker>,
tx_cache: TransactionCache,
txid_limit: usize,
}
impl Query {
pub fn new(app: Arc<App>, metrics: &Metrics, tx_cache: TransactionCache) -> Arc<Query> {
pub fn new(
app: Arc<App>,
metrics: &Metrics,
tx_cache: TransactionCache,
txid_limit: usize,
) -> Arc<Query> {
Arc::new(Query {
app,
tracker: RwLock::new(Tracker::new(metrics)),
tx_cache,
txid_limit,
})
}
@ -291,6 +298,15 @@ impl Query {
let mut spending = vec![];
let read_store = self.app.read_store();
let txid_prefixes = txids_by_script_hash(read_store, script_hash);
// if the limit is enabled
if self.txid_limit > 0 {
if txid_prefixes.len() > self.txid_limit {
bail!(
"{}+ transactions found, query may take a long time",
txid_prefixes.len()
);
}
}
for t in self.load_txns_by_prefix(read_store, txid_prefixes)? {
funding.extend(self.find_funding_outputs(&t, script_hash));
}

Loading…
Cancel
Save