Skip to content

Commit

Permalink
Index addresses (ordinals#3757)
Browse files Browse the repository at this point in the history
  • Loading branch information
raphjaph committed May 22, 2024
1 parent a3cd33e commit 2c87b10
Show file tree
Hide file tree
Showing 19 changed files with 489 additions and 103 deletions.
1 change: 1 addition & 0 deletions deploy/ord.service
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ ExecStart=/usr/local/bin/ord \
--chain ${CHAIN} \
--config-dir /var/lib/ord \
--datadir /var/lib/ord \
--index-addresses \
--index-runes \
--index-sats \
server \
Expand Down
1 change: 1 addition & 0 deletions ord.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ hidden:
- 6fb976ab49dcec017f1e201e84395983204ae1a7c2abf7ced0a85d692e442799i0
- 703e5f7c49d82aab99e605af306b9a30e991e57d42f982908a962a81ac439832i0
index: /var/lib/ord/index.redb
index_addresses: true
index_cache_size: 1000000000
index_runes: true
index_sats: true
Expand Down
160 changes: 137 additions & 23 deletions src/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use {
self::{
entry::{
Entry, HeaderValue, InscriptionEntry, InscriptionEntryValue, InscriptionIdValue,
OutPointValue, RuneEntryValue, RuneIdValue, SatPointValue, SatRange, TxidValue,
OutPointValue, RuneEntryValue, RuneIdValue, SatPointValue, SatRange, TxOutValue, TxidValue,
},
event::Event,
lot::Lot,
Expand Down Expand Up @@ -48,11 +48,12 @@ mod updater;
#[cfg(test)]
pub(crate) mod testing;

const SCHEMA_VERSION: u64 = 25;
const SCHEMA_VERSION: u64 = 26;

define_multimap_table! { SATPOINT_TO_SEQUENCE_NUMBER, &SatPointValue, u32 }
define_multimap_table! { SAT_TO_SEQUENCE_NUMBER, u64, u32 }
define_multimap_table! { SEQUENCE_NUMBER_TO_CHILDREN, u32, u32 }
define_multimap_table! { SCRIPT_PUBKEY_TO_OUTPOINT, &[u8], OutPointValue }
define_table! { CONTENT_TYPE_TO_COUNT, Option<&[u8]>, u64 }
define_table! { HEIGHT_TO_BLOCK_HEADER, u32, &HeaderValue }
define_table! { HEIGHT_TO_LAST_SEQUENCE_NUMBER, u32, u32 }
Expand All @@ -61,7 +62,7 @@ define_table! { INSCRIPTION_ID_TO_SEQUENCE_NUMBER, InscriptionIdValue, u32 }
define_table! { INSCRIPTION_NUMBER_TO_SEQUENCE_NUMBER, i32, u32 }
define_table! { OUTPOINT_TO_RUNE_BALANCES, &OutPointValue, &[u8] }
define_table! { OUTPOINT_TO_SAT_RANGES, &OutPointValue, &[u8] }
define_table! { OUTPOINT_TO_VALUE, &OutPointValue, u64}
define_table! { OUTPOINT_TO_TXOUT, &OutPointValue, TxOutValue }
define_table! { RUNE_ID_TO_RUNE_ENTRY, RuneIdValue, RuneEntryValue }
define_table! { RUNE_TO_RUNE_ID, u128, RuneIdValue }
define_table! { SAT_TO_SATPOINT, u64, &SatPointValue }
Expand Down Expand Up @@ -90,6 +91,7 @@ pub(crate) enum Statistic {
IndexTransactions = 12,
IndexSpentSats = 13,
InitialSyncTime = 14,
IndexAddresses = 15,
}

impl Statistic {
Expand Down Expand Up @@ -189,12 +191,13 @@ pub struct Index {
genesis_block_coinbase_transaction: Transaction,
genesis_block_coinbase_txid: Txid,
height_limit: Option<u32>,
index_addresses: bool,
index_runes: bool,
index_sats: bool,
index_spent_sats: bool,
index_transactions: bool,
settings: Settings,
path: PathBuf,
settings: Settings,
started: DateTime<Utc>,
unrecoverably_reorged: AtomicBool,
}
Expand Down Expand Up @@ -298,6 +301,7 @@ impl Index {

tx.open_multimap_table(SATPOINT_TO_SEQUENCE_NUMBER)?;
tx.open_multimap_table(SAT_TO_SEQUENCE_NUMBER)?;
tx.open_multimap_table(SCRIPT_PUBKEY_TO_OUTPOINT)?;
tx.open_multimap_table(SEQUENCE_NUMBER_TO_CHILDREN)?;
tx.open_table(CONTENT_TYPE_TO_COUNT)?;
tx.open_table(HEIGHT_TO_BLOCK_HEADER)?;
Expand All @@ -306,7 +310,7 @@ impl Index {
tx.open_table(INSCRIPTION_ID_TO_SEQUENCE_NUMBER)?;
tx.open_table(INSCRIPTION_NUMBER_TO_SEQUENCE_NUMBER)?;
tx.open_table(OUTPOINT_TO_RUNE_BALANCES)?;
tx.open_table(OUTPOINT_TO_VALUE)?;
tx.open_table(OUTPOINT_TO_TXOUT)?;
tx.open_table(RUNE_ID_TO_RUNE_ENTRY)?;
tx.open_table(RUNE_TO_RUNE_ID)?;
tx.open_table(SAT_TO_SATPOINT)?;
Expand All @@ -324,6 +328,12 @@ impl Index {
outpoint_to_sat_ranges.insert(&OutPoint::null().store(), [].as_slice())?;
}

Self::set_statistic(
&mut statistics,
Statistic::IndexAddresses,
u64::from(settings.index_addresses()),
)?;

Self::set_statistic(
&mut statistics,
Statistic::IndexRunes,
Expand Down Expand Up @@ -402,6 +412,7 @@ impl Index {
Err(error) => bail!("failed to open index: {error}"),
};

let index_addresses;
let index_runes;
let index_sats;
let index_spent_sats;
Expand All @@ -410,6 +421,7 @@ impl Index {
{
let tx = database.begin_read()?;
let statistics = tx.open_table(STATISTIC_TO_COUNT)?;
index_addresses = Self::is_statistic_set(&statistics, Statistic::IndexAddresses)?;
index_runes = Self::is_statistic_set(&statistics, Statistic::IndexRunes)?;
index_sats = Self::is_statistic_set(&statistics, Statistic::IndexSats)?;
index_spent_sats = Self::is_statistic_set(&statistics, Statistic::IndexSpentSats)?;
Expand All @@ -428,6 +440,7 @@ impl Index {
first_inscription_height: settings.first_inscription_height(),
genesis_block_coinbase_transaction,
height_limit: settings.height_limit(),
index_addresses,
index_runes,
index_sats,
index_spent_sats,
Expand All @@ -449,12 +462,16 @@ impl Index {
self
.database
.begin_read()?
.open_table(OUTPOINT_TO_VALUE)?
.open_table(OUTPOINT_TO_TXOUT)?
.get(&output.store())?
.is_some(),
)
}

pub(crate) fn has_address_index(&self) -> bool {
self.index_addresses
}

pub(crate) fn has_rune_index(&self) -> bool {
self.index_runes
}
Expand Down Expand Up @@ -501,6 +518,7 @@ impl Index {
content_type_counts.sort_by_key(|(_content_type, count)| Reverse(*count));

Ok(StatusHtml {
address_index: self.has_address_index(),
blessed_inscriptions,
chain: self.settings.chain(),
content_type_counts,
Expand All @@ -513,9 +531,9 @@ impl Index {
self.settings.chain().network(),
Height(next_height),
),
rune_index: statistic(Statistic::IndexRunes)? != 0,
rune_index: self.has_rune_index(),
runes: statistic(Statistic::Runes)?,
sat_index: statistic(Statistic::IndexSats)? != 0,
sat_index: self.has_sat_index(),
started: self.started,
transaction_index: statistic(Statistic::IndexTransactions)? != 0,
unrecoverably_reorged: self.unrecoverably_reorged.load(atomic::Ordering::Relaxed),
Expand Down Expand Up @@ -970,7 +988,7 @@ impl Index {
Ok(((id, balance), len))
}

pub(crate) fn get_rune_balances_for_outpoint(
pub(crate) fn get_rune_balances_for_output(
&self,
outpoint: OutPoint,
) -> Result<BTreeMap<SpacedRune, Pile>> {
Expand Down Expand Up @@ -1502,7 +1520,7 @@ impl Index {
)
}

pub(crate) fn get_inscriptions_on_output(
pub(crate) fn get_inscriptions_for_output(
&self,
outpoint: OutPoint,
) -> Result<Vec<InscriptionId>> {
Expand Down Expand Up @@ -1636,10 +1654,19 @@ impl Index {
Ok(
outpoint != OutPoint::null()
&& outpoint != self.settings.chain().genesis_coinbase_outpoint()
&& self
.client
.get_tx_out(&outpoint.txid, outpoint.vout, Some(true))?
.is_none(),
&& if self.settings.index_addresses() {
self
.database
.begin_read()?
.open_table(OUTPOINT_TO_TXOUT)?
.get(&outpoint.store())?
.is_none()
} else {
self
.client
.get_tx_out(&outpoint.txid, outpoint.vout, Some(true))?
.is_none()
},
)
}

Expand Down Expand Up @@ -2186,6 +2213,20 @@ impl Index {
)
}

pub(crate) fn get_address_info(&self, address: &Address) -> Result<Vec<OutPoint>> {
self
.database
.begin_read()?
.open_multimap_table(SCRIPT_PUBKEY_TO_OUTPOINT)?
.get(address.script_pubkey().as_bytes())?
.map(|result| {
result
.map_err(|err| anyhow!(err))
.map(|value| OutPoint::load(value.value()))
})
.collect()
}

pub(crate) fn get_output_info(&self, outpoint: OutPoint) -> Result<Option<(api::Output, TxOut)>> {
let sat_ranges = self.list(outpoint)?;

Expand Down Expand Up @@ -2213,16 +2254,16 @@ impl Index {
return Ok(None);
};

let Some(output) = tx.output.into_iter().nth(outpoint.vout as usize) else {
let Some(txout) = tx.output.into_iter().nth(outpoint.vout as usize) else {
return Ok(None);
};

output
txout
};

let inscriptions = self.get_inscriptions_on_output(outpoint)?;
let inscriptions = self.get_inscriptions_for_output(outpoint)?;

let runes = self.get_rune_balances_for_outpoint(outpoint)?;
let runes = self.get_rune_balances_for_output(outpoint)?;

let spent = self.is_output_spent(outpoint)?;

Expand Down Expand Up @@ -3372,7 +3413,7 @@ mod tests {
assert_eq!(
context
.index
.get_inscriptions_on_output(OutPoint { txid, vout: 0 })
.get_inscriptions_for_output(OutPoint { txid, vout: 0 })
.unwrap(),
[]
);
Expand All @@ -3382,7 +3423,7 @@ mod tests {
assert_eq!(
context
.index
.get_inscriptions_on_output(OutPoint { txid, vout: 0 })
.get_inscriptions_for_output(OutPoint { txid, vout: 0 })
.unwrap(),
[inscription_id]
);
Expand All @@ -3397,15 +3438,15 @@ mod tests {
assert_eq!(
context
.index
.get_inscriptions_on_output(OutPoint { txid, vout: 0 })
.get_inscriptions_for_output(OutPoint { txid, vout: 0 })
.unwrap(),
[]
);

assert_eq!(
context
.index
.get_inscriptions_on_output(OutPoint {
.get_inscriptions_for_output(OutPoint {
txid: send_id,
vout: 0,
})
Expand Down Expand Up @@ -3435,7 +3476,7 @@ mod tests {
assert_eq!(
context
.index
.get_inscriptions_on_output(OutPoint {
.get_inscriptions_for_output(OutPoint {
txid: first,
vout: 0
})
Expand Down Expand Up @@ -6268,6 +6309,79 @@ mod tests {
.unwrap());
}

#[test]
fn output_addresses_are_updated() {
let context = Context::builder()
.arg("--index-addresses")
.arg("--index-sats")
.build();

context.mine_blocks(2);

let txid = context.core.broadcast_tx(TransactionTemplate {
inputs: &[(1, 0, 0, Witness::new()), (2, 0, 0, Witness::new())],
outputs: 2,
..Default::default()
});

context.mine_blocks(1);

let transaction = context.index.get_transaction(txid).unwrap().unwrap();

let first_address = context
.index
.settings
.chain()
.address_from_script(&transaction.output[0].script_pubkey)
.unwrap();

let first_address_second_output = OutPoint {
txid: transaction.txid(),
vout: 1,
};

assert_eq!(
context.index.get_address_info(&first_address).unwrap(),
[
OutPoint {
txid: transaction.txid(),
vout: 0
},
first_address_second_output
]
);

let txid = context.core.broadcast_tx(TransactionTemplate {
inputs: &[(3, 1, 0, Witness::new())],
p2tr: true,
..Default::default()
});

context.mine_blocks(1);

let transaction = context.index.get_transaction(txid).unwrap().unwrap();

let second_address = context
.index
.settings
.chain()
.address_from_script(&transaction.output[0].script_pubkey)
.unwrap();

assert_eq!(
context.index.get_address_info(&first_address).unwrap(),
[first_address_second_output]
);

assert_eq!(
context.index.get_address_info(&second_address).unwrap(),
[OutPoint {
txid: transaction.txid(),
vout: 0
}]
);
}

#[test]
fn fee_spent_inscriptions_are_numbered_last_in_block() {
for context in Context::configurations() {
Expand Down
Loading

0 comments on commit 2c87b10

Please sign in to comment.