diff --git a/src/index.rs b/src/index.rs index eb79d8f2a4..77a818c13c 100644 --- a/src/index.rs +++ b/src/index.rs @@ -131,6 +131,13 @@ impl BitcoinCoreRpcResultExt for Result { } } +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub struct FindRangeOutput { + pub start: u64, + pub size: u64, + pub satpoint: SatPoint, +} + impl Index { pub(crate) fn open(options: &Options) -> Result { let rpc_url = options.rpc_url(); @@ -636,6 +643,48 @@ impl Index { Ok(None) } + pub(crate) fn find_range(&self, search_start: u64, search_end: u64) -> Result>> { + self.require_sat_index("find")?; + + let rtx = self.begin_read()?; + + if rtx.block_count()? <= Sat(search_end - 1).height().n() { + return Ok(None); + } + + let mut remaining_sats = search_end - search_start; + + let outpoint_to_sat_ranges = rtx.0.open_table(OUTPOINT_TO_SAT_RANGES)?; + + let mut result = Vec::new(); + for (key, value) in outpoint_to_sat_ranges.range::<&[u8; 36]>(&[0; 36]..)? { + let mut offset = 0; + for chunk in value.value().chunks_exact(11) { + let (start, end) = SatRange::load(chunk.try_into().unwrap()); + if start < search_end && search_start < end { + let overlap_start = cmp::max(start, search_start); + let overlap_end = cmp::min(search_end, end); + result.push(FindRangeOutput { + start: overlap_start, + size: overlap_end - overlap_start, + satpoint: SatPoint { + outpoint: Entry::load(*key.value()), + offset: offset + overlap_start - start, + }, + }); + + remaining_sats -= overlap_end - overlap_start; + if remaining_sats == 0 { + break; + } + } + offset += end - start; + } + } + + Ok(Some(result)) + } + fn list_inner(&self, outpoint: OutPointValue) -> Result>> { Ok( self diff --git a/src/subcommand/find.rs b/src/subcommand/find.rs index fdf314cc48..66c75dcc65 100644 --- a/src/subcommand/find.rs +++ b/src/subcommand/find.rs @@ -4,6 +4,8 @@ use super::*; pub(crate) struct Find { #[clap(help = "Find output and offset of .")] sat: Sat, + #[clap(help = "Find output and offset of all sats in the range -.")] + end: Option, } #[derive(Debug, PartialEq, Serialize, Deserialize)] @@ -17,12 +19,29 @@ impl Find { index.update()?; - match index.find(self.sat.0)? { - Some(satpoint) => { - print_json(Output { satpoint })?; - Ok(()) + match self.end { + Some(end) => { + if self.sat < end { + match index.find_range(self.sat.0, end.0)? { + Some(result) => { + print_json(result)?; + Ok(()) + } + None => Err(anyhow!("range has not been mined as of index height")), + } + } else { + Err(anyhow!("range is empty")) + } + } + None => { + match index.find(self.sat.0)? { + Some(satpoint) => { + print_json(Output { satpoint })?; + Ok(()) + } + None => Err(anyhow!("sat has not been mined as of index height")), + } } - None => Err(anyhow!("sat has not been mined as of index height")), } } }