Skip to content

Commit

Permalink
Refactor index checks (#2541)
Browse files Browse the repository at this point in the history
  • Loading branch information
casey committed Oct 17, 2023
1 parent 4219f35 commit fadeda8
Show file tree
Hide file tree
Showing 13 changed files with 294 additions and 293 deletions.
245 changes: 116 additions & 129 deletions src/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ mod updater;
#[cfg(test)]
pub(crate) mod testing;

const SCHEMA_VERSION: u64 = 8;
const SCHEMA_VERSION: u64 = 9;

macro_rules! define_table {
($name:ident, $key:ty, $value:ty) => {
Expand Down Expand Up @@ -75,16 +75,17 @@ pub enum List {
}

#[derive(Copy, Clone)]
#[repr(u64)]
pub(crate) enum Statistic {
Schema = 0,
Commits = 1,
LostSats = 2,
OutputsTraversed = 3,
SatRanges = 4,
UnboundInscriptions = 5,
CursedInscriptions = 6,
BlessedInscriptions = 7,
BlessedInscriptions,
Commits,
CursedInscriptions,
IndexRunes,
IndexSats,
LostSats,
OutputsTraversed,
SatRanges,
Schema,
UnboundInscriptions,
}

impl Statistic {
Expand Down Expand Up @@ -154,6 +155,8 @@ pub(crate) struct Index {
genesis_block_coinbase_transaction: Transaction,
genesis_block_coinbase_txid: Txid,
height_limit: Option<u64>,
index_runes: bool,
index_sats: bool,
options: Options,
path: PathBuf,
unrecoverably_reorged: AtomicBool,
Expand Down Expand Up @@ -207,19 +210,23 @@ impl Index {
redb::Durability::Immediate
};

let index_runes;
let index_sats;

let database = match Database::builder()
.set_cache_size(db_cache_size)
.open(&path)
{
Ok(database) => {
let schema_version = database
.begin_read()?
.open_table(STATISTIC_TO_COUNT)?
.get(&Statistic::Schema.key())?
.map(|x| x.value())
.unwrap_or(0);

match schema_version.cmp(&SCHEMA_VERSION) {
{
let tx = database.begin_read()?;
let schema_version = tx
.open_table(STATISTIC_TO_COUNT)?
.get(&Statistic::Schema.key())?
.map(|x| x.value())
.unwrap_or(0);

match schema_version.cmp(&SCHEMA_VERSION) {
cmp::Ordering::Less =>
bail!(
"index at `{}` appears to have been built with an older, incompatible version of ord, consider deleting and rebuilding the index: index schema {schema_version}, ord schema {SCHEMA_VERSION}",
Expand All @@ -234,6 +241,20 @@ impl Index {
}
}

let statistics = tx.open_table(STATISTIC_TO_COUNT)?;

index_runes = statistics
.get(&Statistic::IndexRunes.key())?
.unwrap()
.value()
!= 0;
index_sats = statistics
.get(&Statistic::IndexSats.key())?
.unwrap()
.value()
!= 0;
}

database
}
Err(_) => {
Expand All @@ -253,23 +274,31 @@ impl Index {
tx.open_table(INSCRIPTION_ID_TO_INSCRIPTION_ENTRY)?;
tx.open_table(INSCRIPTION_ID_TO_SATPOINT)?;
tx.open_table(INSCRIPTION_NUMBER_TO_INSCRIPTION_ID)?;
tx.open_table(OUTPOINT_TO_RUNE_BALANCES)?;
tx.open_table(OUTPOINT_TO_VALUE)?;
tx.open_table(RUNE_ID_TO_RUNE_ENTRY)?;
tx.open_table(RUNE_TO_RUNE_ID)?;
tx.open_table(SAT_TO_SATPOINT)?;
tx.open_table(SEQUENCE_NUMBER_TO_INSCRIPTION_ID)?;
tx.open_table(WRITE_TRANSACTION_STARTING_BLOCK_COUNT_TO_TIMESTAMP)?;

tx.open_table(STATISTIC_TO_COUNT)?
.insert(&Statistic::Schema.key(), &SCHEMA_VERSION)?;
{
let mut outpoint_to_sat_ranges = tx.open_table(OUTPOINT_TO_SAT_RANGES)?;
let mut statistics = tx.open_table(STATISTIC_TO_COUNT)?;

if options.index_runes() {
tx.open_table(OUTPOINT_TO_RUNE_BALANCES)?;
tx.open_table(RUNE_ID_TO_RUNE_ENTRY)?;
tx.open_table(RUNE_TO_RUNE_ID)?;
}
if options.index_sats {
outpoint_to_sat_ranges.insert(&OutPoint::null().store(), [].as_slice())?;
}

index_runes = options.index_runes();
index_sats = options.index_sats;

if options.index_sats {
tx.open_table(OUTPOINT_TO_SAT_RANGES)?
.insert(&OutPoint::null().store(), [].as_slice())?;
statistics.insert(
&Statistic::IndexRunes.key(),
&u64::from(options.index_runes()),
)?;
statistics.insert(&Statistic::IndexSats.key(), &u64::from(options.index_sats))?;
statistics.insert(&Statistic::Schema.key(), &SCHEMA_VERSION)?;
}

tx.commit()?;
Expand All @@ -290,6 +319,8 @@ impl Index {
genesis_block_coinbase_transaction,
height_limit: options.height_limit,
options: options.clone(),
index_runes,
index_sats,
path,
unrecoverably_reorged: AtomicBool::new(false),
})
Expand Down Expand Up @@ -358,28 +389,8 @@ impl Index {
.collect()
}

pub(crate) fn has_rune_index(&self) -> Result<bool> {
match self.begin_read()?.0.open_table(RUNE_ID_TO_RUNE_ENTRY) {
Ok(_) => Ok(true),
Err(redb::TableError::TableDoesNotExist(_)) => Ok(false),
Err(err) => Err(err.into()),
}
}

pub(crate) fn has_sat_index(&self) -> Result<bool> {
match self.begin_read()?.0.open_table(OUTPOINT_TO_SAT_RANGES) {
Ok(_) => Ok(true),
Err(redb::TableError::TableDoesNotExist(_)) => Ok(false),
Err(err) => Err(err.into()),
}
}

fn require_sat_index(&self, feature: &str) -> Result {
if !self.has_sat_index()? {
bail!("{feature} requires index created with `--index-sats` flag")
}

Ok(())
pub(crate) fn has_sat_index(&self) -> bool {
self.index_sats
}

pub(crate) fn info(&self) -> Result<Info> {
Expand Down Expand Up @@ -602,86 +613,66 @@ impl Index {
Ok(blocks)
}

pub(crate) fn rare_sat_satpoints(&self) -> Result<Option<Vec<(Sat, SatPoint)>>> {
if self.has_sat_index()? {
let rtx = self.database.begin_read()?;

let sat_to_satpoint = rtx.open_table(SAT_TO_SATPOINT)?;
pub(crate) fn rare_sat_satpoints(&self) -> Result<Vec<(Sat, SatPoint)>> {
let rtx = self.database.begin_read()?;

let mut result = Vec::with_capacity(sat_to_satpoint.len()?.try_into().unwrap());
let sat_to_satpoint = rtx.open_table(SAT_TO_SATPOINT)?;

for range in sat_to_satpoint.range(0..)? {
let (sat, satpoint) = range?;
result.push((Sat(sat.value()), Entry::load(*satpoint.value())));
}
let mut result = Vec::with_capacity(sat_to_satpoint.len()?.try_into().unwrap());

Ok(Some(result))
} else {
Ok(None)
for range in sat_to_satpoint.range(0..)? {
let (sat, satpoint) = range?;
result.push((Sat(sat.value()), Entry::load(*satpoint.value())));
}

Ok(result)
}

pub(crate) fn rare_sat_satpoint(&self, sat: Sat) -> Result<Option<SatPoint>> {
if self.has_sat_index()? {
Ok(
self
.database
.begin_read()?
.open_table(SAT_TO_SATPOINT)?
.get(&sat.n())?
.map(|satpoint| Entry::load(*satpoint.value())),
)
} else {
Ok(None)
}
Ok(
self
.database
.begin_read()?
.open_table(SAT_TO_SATPOINT)?
.get(&sat.n())?
.map(|satpoint| Entry::load(*satpoint.value())),
)
}

pub(crate) fn rune(&self, rune: Rune) -> Result<Option<(RuneId, RuneEntry)>> {
if self.has_rune_index()? {
let rtx = self.database.begin_read()?;

let entry = match rtx.open_table(RUNE_TO_RUNE_ID)?.get(rune.0)? {
Some(id) => rtx
.open_table(RUNE_ID_TO_RUNE_ENTRY)?
.get(id.value())?
.map(|entry| (RuneId::load(id.value()), RuneEntry::load(entry.value()))),
None => None,
};
let rtx = self.database.begin_read()?;

Ok(entry)
} else {
Ok(None)
}
}
let entry = match rtx.open_table(RUNE_TO_RUNE_ID)?.get(rune.0)? {
Some(id) => rtx
.open_table(RUNE_ID_TO_RUNE_ENTRY)?
.get(id.value())?
.map(|entry| (RuneId::load(id.value()), RuneEntry::load(entry.value()))),
None => None,
};

pub(crate) fn runes(&self) -> Result<Option<Vec<(RuneId, RuneEntry)>>> {
if self.has_rune_index()? {
let mut entries = Vec::new();
Ok(entry)
}

for result in self
.database
.begin_read()?
.open_table(RUNE_ID_TO_RUNE_ENTRY)?
.iter()?
{
let (id, entry) = result?;
entries.push((RuneId::load(id.value()), RuneEntry::load(entry.value())));
}
pub(crate) fn runes(&self) -> Result<Vec<(RuneId, RuneEntry)>> {
let mut entries = Vec::new();

Ok(Some(entries))
} else {
Ok(None)
for result in self
.database
.begin_read()?
.open_table(RUNE_ID_TO_RUNE_ENTRY)?
.iter()?
{
let (id, entry) = result?;
entries.push((RuneId::load(id.value()), RuneEntry::load(entry.value())));
}

Ok(entries)
}

pub(crate) fn get_rune_balances_for_outpoint(
&self,
outpoint: OutPoint,
) -> Result<Vec<(Rune, Pile)>> {
if !self.has_rune_index()? {
return Ok(Vec::new());
}

let rtx = &self.database.begin_read()?;

let outpoint_to_balances = rtx.open_table(OUTPOINT_TO_RUNE_BALANCES)?;
Expand Down Expand Up @@ -964,8 +955,6 @@ impl Index {
}

pub(crate) fn find(&self, sat: u64) -> Result<Option<SatPoint>> {
self.require_sat_index("find")?;

let rtx = self.begin_read()?;

if rtx.block_count()? <= Sat(sat).height().n() {
Expand Down Expand Up @@ -997,8 +986,6 @@ impl Index {
range_start: u64,
range_end: u64,
) -> Result<Option<Vec<FindRangeOutput>>> {
self.require_sat_index("find")?;

let rtx = self.begin_read()?;

if rtx.block_count()? < Sat(range_end - 1).height().n() + 1 {
Expand Down Expand Up @@ -1057,7 +1044,9 @@ impl Index {
}

pub(crate) fn list(&self, outpoint: OutPoint) -> Result<Option<List>> {
self.require_sat_index("list")?;
if !self.index_sats {
return Ok(None);
}

let array = outpoint.store();

Expand Down Expand Up @@ -1301,7 +1290,7 @@ impl Index {

match sat {
Some(sat) => {
if self.has_sat_index().unwrap() {
if self.index_sats {
// unbound inscriptions should not be assigned to a sat
assert!(satpoint.outpoint != unbound_outpoint());
assert!(rtx
Expand Down Expand Up @@ -1329,7 +1318,7 @@ impl Index {
}
}
None => {
if self.has_sat_index().unwrap() {
if self.index_sats {
assert!(satpoint.outpoint == unbound_outpoint())
}
}
Expand Down Expand Up @@ -1378,19 +1367,17 @@ impl Index {
}
}

if self.has_sat_index().unwrap() {
for range in rtx
.open_multimap_table(SAT_TO_INSCRIPTION_ID)
.unwrap()
.iter()
.into_iter()
{
for entry in range.into_iter() {
let (_sat, ids) = entry.unwrap();
assert!(!ids
.into_iter()
.any(|id| InscriptionId::load(*id.unwrap().value()) == inscription_id))
}
for range in rtx
.open_multimap_table(SAT_TO_INSCRIPTION_ID)
.unwrap()
.iter()
.into_iter()
{
for entry in range.into_iter() {
let (_sat, ids) = entry.unwrap();
assert!(!ids
.into_iter()
.any(|id| InscriptionId::load(*id.unwrap().value()) == inscription_id))
}
}
}
Expand Down
5 changes: 0 additions & 5 deletions src/index/testing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,6 @@ impl ContextBuilder {
self
}

pub(crate) fn chain(mut self, chain: Chain) -> Self {
self.chain = chain;
self
}

pub(crate) fn tempdir(mut self, tempdir: TempDir) -> Self {
self.tempdir = Some(tempdir);
self
Expand Down
Loading

0 comments on commit fadeda8

Please sign in to comment.