Skip to content

Commit 2c87b10

Browse files
authored
Index addresses (ordinals#3757)
1 parent a3cd33e commit 2c87b10

19 files changed

+489
-103
lines changed

deploy/ord.service

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ ExecStart=/usr/local/bin/ord \
1313
--chain ${CHAIN} \
1414
--config-dir /var/lib/ord \
1515
--datadir /var/lib/ord \
16+
--index-addresses \
1617
--index-runes \
1718
--index-sats \
1819
server \

ord.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ hidden:
1818
- 6fb976ab49dcec017f1e201e84395983204ae1a7c2abf7ced0a85d692e442799i0
1919
- 703e5f7c49d82aab99e605af306b9a30e991e57d42f982908a962a81ac439832i0
2020
index: /var/lib/ord/index.redb
21+
index_addresses: true
2122
index_cache_size: 1000000000
2223
index_runes: true
2324
index_sats: true

src/index.rs

Lines changed: 137 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use {
22
self::{
33
entry::{
44
Entry, HeaderValue, InscriptionEntry, InscriptionEntryValue, InscriptionIdValue,
5-
OutPointValue, RuneEntryValue, RuneIdValue, SatPointValue, SatRange, TxidValue,
5+
OutPointValue, RuneEntryValue, RuneIdValue, SatPointValue, SatRange, TxOutValue, TxidValue,
66
},
77
event::Event,
88
lot::Lot,
@@ -48,11 +48,12 @@ mod updater;
4848
#[cfg(test)]
4949
pub(crate) mod testing;
5050

51-
const SCHEMA_VERSION: u64 = 25;
51+
const SCHEMA_VERSION: u64 = 26;
5252

5353
define_multimap_table! { SATPOINT_TO_SEQUENCE_NUMBER, &SatPointValue, u32 }
5454
define_multimap_table! { SAT_TO_SEQUENCE_NUMBER, u64, u32 }
5555
define_multimap_table! { SEQUENCE_NUMBER_TO_CHILDREN, u32, u32 }
56+
define_multimap_table! { SCRIPT_PUBKEY_TO_OUTPOINT, &[u8], OutPointValue }
5657
define_table! { CONTENT_TYPE_TO_COUNT, Option<&[u8]>, u64 }
5758
define_table! { HEIGHT_TO_BLOCK_HEADER, u32, &HeaderValue }
5859
define_table! { HEIGHT_TO_LAST_SEQUENCE_NUMBER, u32, u32 }
@@ -61,7 +62,7 @@ define_table! { INSCRIPTION_ID_TO_SEQUENCE_NUMBER, InscriptionIdValue, u32 }
6162
define_table! { INSCRIPTION_NUMBER_TO_SEQUENCE_NUMBER, i32, u32 }
6263
define_table! { OUTPOINT_TO_RUNE_BALANCES, &OutPointValue, &[u8] }
6364
define_table! { OUTPOINT_TO_SAT_RANGES, &OutPointValue, &[u8] }
64-
define_table! { OUTPOINT_TO_VALUE, &OutPointValue, u64}
65+
define_table! { OUTPOINT_TO_TXOUT, &OutPointValue, TxOutValue }
6566
define_table! { RUNE_ID_TO_RUNE_ENTRY, RuneIdValue, RuneEntryValue }
6667
define_table! { RUNE_TO_RUNE_ID, u128, RuneIdValue }
6768
define_table! { SAT_TO_SATPOINT, u64, &SatPointValue }
@@ -90,6 +91,7 @@ pub(crate) enum Statistic {
9091
IndexTransactions = 12,
9192
IndexSpentSats = 13,
9293
InitialSyncTime = 14,
94+
IndexAddresses = 15,
9395
}
9496

9597
impl Statistic {
@@ -189,12 +191,13 @@ pub struct Index {
189191
genesis_block_coinbase_transaction: Transaction,
190192
genesis_block_coinbase_txid: Txid,
191193
height_limit: Option<u32>,
194+
index_addresses: bool,
192195
index_runes: bool,
193196
index_sats: bool,
194197
index_spent_sats: bool,
195198
index_transactions: bool,
196-
settings: Settings,
197199
path: PathBuf,
200+
settings: Settings,
198201
started: DateTime<Utc>,
199202
unrecoverably_reorged: AtomicBool,
200203
}
@@ -298,6 +301,7 @@ impl Index {
298301

299302
tx.open_multimap_table(SATPOINT_TO_SEQUENCE_NUMBER)?;
300303
tx.open_multimap_table(SAT_TO_SEQUENCE_NUMBER)?;
304+
tx.open_multimap_table(SCRIPT_PUBKEY_TO_OUTPOINT)?;
301305
tx.open_multimap_table(SEQUENCE_NUMBER_TO_CHILDREN)?;
302306
tx.open_table(CONTENT_TYPE_TO_COUNT)?;
303307
tx.open_table(HEIGHT_TO_BLOCK_HEADER)?;
@@ -306,7 +310,7 @@ impl Index {
306310
tx.open_table(INSCRIPTION_ID_TO_SEQUENCE_NUMBER)?;
307311
tx.open_table(INSCRIPTION_NUMBER_TO_SEQUENCE_NUMBER)?;
308312
tx.open_table(OUTPOINT_TO_RUNE_BALANCES)?;
309-
tx.open_table(OUTPOINT_TO_VALUE)?;
313+
tx.open_table(OUTPOINT_TO_TXOUT)?;
310314
tx.open_table(RUNE_ID_TO_RUNE_ENTRY)?;
311315
tx.open_table(RUNE_TO_RUNE_ID)?;
312316
tx.open_table(SAT_TO_SATPOINT)?;
@@ -324,6 +328,12 @@ impl Index {
324328
outpoint_to_sat_ranges.insert(&OutPoint::null().store(), [].as_slice())?;
325329
}
326330

331+
Self::set_statistic(
332+
&mut statistics,
333+
Statistic::IndexAddresses,
334+
u64::from(settings.index_addresses()),
335+
)?;
336+
327337
Self::set_statistic(
328338
&mut statistics,
329339
Statistic::IndexRunes,
@@ -402,6 +412,7 @@ impl Index {
402412
Err(error) => bail!("failed to open index: {error}"),
403413
};
404414

415+
let index_addresses;
405416
let index_runes;
406417
let index_sats;
407418
let index_spent_sats;
@@ -410,6 +421,7 @@ impl Index {
410421
{
411422
let tx = database.begin_read()?;
412423
let statistics = tx.open_table(STATISTIC_TO_COUNT)?;
424+
index_addresses = Self::is_statistic_set(&statistics, Statistic::IndexAddresses)?;
413425
index_runes = Self::is_statistic_set(&statistics, Statistic::IndexRunes)?;
414426
index_sats = Self::is_statistic_set(&statistics, Statistic::IndexSats)?;
415427
index_spent_sats = Self::is_statistic_set(&statistics, Statistic::IndexSpentSats)?;
@@ -428,6 +440,7 @@ impl Index {
428440
first_inscription_height: settings.first_inscription_height(),
429441
genesis_block_coinbase_transaction,
430442
height_limit: settings.height_limit(),
443+
index_addresses,
431444
index_runes,
432445
index_sats,
433446
index_spent_sats,
@@ -449,12 +462,16 @@ impl Index {
449462
self
450463
.database
451464
.begin_read()?
452-
.open_table(OUTPOINT_TO_VALUE)?
465+
.open_table(OUTPOINT_TO_TXOUT)?
453466
.get(&output.store())?
454467
.is_some(),
455468
)
456469
}
457470

471+
pub(crate) fn has_address_index(&self) -> bool {
472+
self.index_addresses
473+
}
474+
458475
pub(crate) fn has_rune_index(&self) -> bool {
459476
self.index_runes
460477
}
@@ -501,6 +518,7 @@ impl Index {
501518
content_type_counts.sort_by_key(|(_content_type, count)| Reverse(*count));
502519

503520
Ok(StatusHtml {
521+
address_index: self.has_address_index(),
504522
blessed_inscriptions,
505523
chain: self.settings.chain(),
506524
content_type_counts,
@@ -513,9 +531,9 @@ impl Index {
513531
self.settings.chain().network(),
514532
Height(next_height),
515533
),
516-
rune_index: statistic(Statistic::IndexRunes)? != 0,
534+
rune_index: self.has_rune_index(),
517535
runes: statistic(Statistic::Runes)?,
518-
sat_index: statistic(Statistic::IndexSats)? != 0,
536+
sat_index: self.has_sat_index(),
519537
started: self.started,
520538
transaction_index: statistic(Statistic::IndexTransactions)? != 0,
521539
unrecoverably_reorged: self.unrecoverably_reorged.load(atomic::Ordering::Relaxed),
@@ -970,7 +988,7 @@ impl Index {
970988
Ok(((id, balance), len))
971989
}
972990

973-
pub(crate) fn get_rune_balances_for_outpoint(
991+
pub(crate) fn get_rune_balances_for_output(
974992
&self,
975993
outpoint: OutPoint,
976994
) -> Result<BTreeMap<SpacedRune, Pile>> {
@@ -1502,7 +1520,7 @@ impl Index {
15021520
)
15031521
}
15041522

1505-
pub(crate) fn get_inscriptions_on_output(
1523+
pub(crate) fn get_inscriptions_for_output(
15061524
&self,
15071525
outpoint: OutPoint,
15081526
) -> Result<Vec<InscriptionId>> {
@@ -1636,10 +1654,19 @@ impl Index {
16361654
Ok(
16371655
outpoint != OutPoint::null()
16381656
&& outpoint != self.settings.chain().genesis_coinbase_outpoint()
1639-
&& self
1640-
.client
1641-
.get_tx_out(&outpoint.txid, outpoint.vout, Some(true))?
1642-
.is_none(),
1657+
&& if self.settings.index_addresses() {
1658+
self
1659+
.database
1660+
.begin_read()?
1661+
.open_table(OUTPOINT_TO_TXOUT)?
1662+
.get(&outpoint.store())?
1663+
.is_none()
1664+
} else {
1665+
self
1666+
.client
1667+
.get_tx_out(&outpoint.txid, outpoint.vout, Some(true))?
1668+
.is_none()
1669+
},
16431670
)
16441671
}
16451672

@@ -2186,6 +2213,20 @@ impl Index {
21862213
)
21872214
}
21882215

2216+
pub(crate) fn get_address_info(&self, address: &Address) -> Result<Vec<OutPoint>> {
2217+
self
2218+
.database
2219+
.begin_read()?
2220+
.open_multimap_table(SCRIPT_PUBKEY_TO_OUTPOINT)?
2221+
.get(address.script_pubkey().as_bytes())?
2222+
.map(|result| {
2223+
result
2224+
.map_err(|err| anyhow!(err))
2225+
.map(|value| OutPoint::load(value.value()))
2226+
})
2227+
.collect()
2228+
}
2229+
21892230
pub(crate) fn get_output_info(&self, outpoint: OutPoint) -> Result<Option<(api::Output, TxOut)>> {
21902231
let sat_ranges = self.list(outpoint)?;
21912232

@@ -2213,16 +2254,16 @@ impl Index {
22132254
return Ok(None);
22142255
};
22152256

2216-
let Some(output) = tx.output.into_iter().nth(outpoint.vout as usize) else {
2257+
let Some(txout) = tx.output.into_iter().nth(outpoint.vout as usize) else {
22172258
return Ok(None);
22182259
};
22192260

2220-
output
2261+
txout
22212262
};
22222263

2223-
let inscriptions = self.get_inscriptions_on_output(outpoint)?;
2264+
let inscriptions = self.get_inscriptions_for_output(outpoint)?;
22242265

2225-
let runes = self.get_rune_balances_for_outpoint(outpoint)?;
2266+
let runes = self.get_rune_balances_for_output(outpoint)?;
22262267

22272268
let spent = self.is_output_spent(outpoint)?;
22282269

@@ -3372,7 +3413,7 @@ mod tests {
33723413
assert_eq!(
33733414
context
33743415
.index
3375-
.get_inscriptions_on_output(OutPoint { txid, vout: 0 })
3416+
.get_inscriptions_for_output(OutPoint { txid, vout: 0 })
33763417
.unwrap(),
33773418
[]
33783419
);
@@ -3382,7 +3423,7 @@ mod tests {
33823423
assert_eq!(
33833424
context
33843425
.index
3385-
.get_inscriptions_on_output(OutPoint { txid, vout: 0 })
3426+
.get_inscriptions_for_output(OutPoint { txid, vout: 0 })
33863427
.unwrap(),
33873428
[inscription_id]
33883429
);
@@ -3397,15 +3438,15 @@ mod tests {
33973438
assert_eq!(
33983439
context
33993440
.index
3400-
.get_inscriptions_on_output(OutPoint { txid, vout: 0 })
3441+
.get_inscriptions_for_output(OutPoint { txid, vout: 0 })
34013442
.unwrap(),
34023443
[]
34033444
);
34043445

34053446
assert_eq!(
34063447
context
34073448
.index
3408-
.get_inscriptions_on_output(OutPoint {
3449+
.get_inscriptions_for_output(OutPoint {
34093450
txid: send_id,
34103451
vout: 0,
34113452
})
@@ -3435,7 +3476,7 @@ mod tests {
34353476
assert_eq!(
34363477
context
34373478
.index
3438-
.get_inscriptions_on_output(OutPoint {
3479+
.get_inscriptions_for_output(OutPoint {
34393480
txid: first,
34403481
vout: 0
34413482
})
@@ -6268,6 +6309,79 @@ mod tests {
62686309
.unwrap());
62696310
}
62706311

6312+
#[test]
6313+
fn output_addresses_are_updated() {
6314+
let context = Context::builder()
6315+
.arg("--index-addresses")
6316+
.arg("--index-sats")
6317+
.build();
6318+
6319+
context.mine_blocks(2);
6320+
6321+
let txid = context.core.broadcast_tx(TransactionTemplate {
6322+
inputs: &[(1, 0, 0, Witness::new()), (2, 0, 0, Witness::new())],
6323+
outputs: 2,
6324+
..Default::default()
6325+
});
6326+
6327+
context.mine_blocks(1);
6328+
6329+
let transaction = context.index.get_transaction(txid).unwrap().unwrap();
6330+
6331+
let first_address = context
6332+
.index
6333+
.settings
6334+
.chain()
6335+
.address_from_script(&transaction.output[0].script_pubkey)
6336+
.unwrap();
6337+
6338+
let first_address_second_output = OutPoint {
6339+
txid: transaction.txid(),
6340+
vout: 1,
6341+
};
6342+
6343+
assert_eq!(
6344+
context.index.get_address_info(&first_address).unwrap(),
6345+
[
6346+
OutPoint {
6347+
txid: transaction.txid(),
6348+
vout: 0
6349+
},
6350+
first_address_second_output
6351+
]
6352+
);
6353+
6354+
let txid = context.core.broadcast_tx(TransactionTemplate {
6355+
inputs: &[(3, 1, 0, Witness::new())],
6356+
p2tr: true,
6357+
..Default::default()
6358+
});
6359+
6360+
context.mine_blocks(1);
6361+
6362+
let transaction = context.index.get_transaction(txid).unwrap().unwrap();
6363+
6364+
let second_address = context
6365+
.index
6366+
.settings
6367+
.chain()
6368+
.address_from_script(&transaction.output[0].script_pubkey)
6369+
.unwrap();
6370+
6371+
assert_eq!(
6372+
context.index.get_address_info(&first_address).unwrap(),
6373+
[first_address_second_output]
6374+
);
6375+
6376+
assert_eq!(
6377+
context.index.get_address_info(&second_address).unwrap(),
6378+
[OutPoint {
6379+
txid: transaction.txid(),
6380+
vout: 0
6381+
}]
6382+
);
6383+
}
6384+
62716385
#[test]
62726386
fn fee_spent_inscriptions_are_numbered_last_in_block() {
62736387
for context in Context::configurations() {

0 commit comments

Comments
 (0)