From 4cd2e290ef7c3d7aeac1ade30211e566cfb31e23 Mon Sep 17 00:00:00 2001 From: mark Date: Mon, 25 Mar 2024 23:56:21 +0300 Subject: [PATCH 01/15] address index --- src/index.rs | 31 ++++++++ src/index/updater.rs | 3 + src/index/updater/inscription_updater.rs | 90 +++++++++++++++++++++++- src/options.rs | 2 + src/settings.rs | 14 ++++ 5 files changed, 138 insertions(+), 2 deletions(-) diff --git a/src/index.rs b/src/index.rs index 582b7519be..c174588a21 100644 --- a/src/index.rs +++ b/src/index.rs @@ -83,6 +83,7 @@ define_table! { STATISTIC_TO_COUNT, u64, u64 } define_table! { TRANSACTION_ID_TO_RUNE, &TxidValue, u128 } define_table! { TRANSACTION_ID_TO_TRANSACTION, &TxidValue, &[u8] } define_table! { WRITE_TRANSACTION_STARTING_BLOCK_COUNT_TO_TIMESTAMP, u32, u128 } +define_table! { ADDRESS_TO_INSCRIPTION_IDS, &[u8], Vec } #[derive(Copy, Clone)] pub(crate) enum Statistic { @@ -101,6 +102,7 @@ pub(crate) enum Statistic { IndexTransactions = 12, IndexSpentSats = 13, InitialSyncTime = 14, + IndexAddresses = 15, } impl Statistic { @@ -217,6 +219,7 @@ pub struct Index { index_sats: bool, index_spent_sats: bool, index_transactions: bool, + index_addresses: bool, settings: Settings, path: PathBuf, started: DateTime, @@ -339,6 +342,7 @@ impl Index { tx.open_table(SEQUENCE_NUMBER_TO_SATPOINT)?; tx.open_table(TRANSACTION_ID_TO_RUNE)?; tx.open_table(WRITE_TRANSACTION_STARTING_BLOCK_COUNT_TO_TIMESTAMP)?; + tx.open_table(ADDRESS_TO_INSCRIPTION_IDS)?; { let mut outpoint_to_sat_ranges = tx.open_table(OUTPOINT_TO_SAT_RANGES)?; @@ -372,6 +376,12 @@ impl Index { u64::from(settings.index_transactions()), )?; + Self::set_statistic( + &mut statistics, + Statistic::IndexAddresses, + u64::from(settings.index_addresses()), + )?; + Self::set_statistic(&mut statistics, Statistic::Schema, SCHEMA_VERSION)?; } @@ -386,6 +396,7 @@ impl Index { let index_sats; let index_spent_sats; let index_transactions; + let index_addresses; { let tx = database.begin_read()?; @@ -394,6 +405,7 @@ impl Index { index_sats = Self::is_statistic_set(&statistics, Statistic::IndexSats)?; index_spent_sats = Self::is_statistic_set(&statistics, Statistic::IndexSpentSats)?; index_transactions = Self::is_statistic_set(&statistics, Statistic::IndexTransactions)?; + index_addresses = Self::is_statistic_set(&statistics, Statistic::IndexAddresses)?; } let genesis_block_coinbase_transaction = @@ -412,6 +424,7 @@ impl Index { index_sats, index_spent_sats, index_transactions, + index_addresses, settings: settings.clone(), path, started: Utc::now(), @@ -2008,6 +2021,24 @@ impl Index { } } + pub(crate) fn get_inscription_ids_by_address( + &self, + address: Address, + ) -> Result> { + Ok( + self + .database + .begin_read()? + .open_table(ADDRESS_TO_INSCRIPTION_IDS)? + .get(address.to_string().as_bytes())? + .unwrap() + .value() + .iter() + .map(|entry| InscriptionId::load(*entry)) + .collect(), + ) + } + fn inscriptions_on_output<'a: 'tx, 'tx>( satpoint_to_sequence_number: &'a impl ReadableMultimapTable<&'static SatPointValue, u32>, sequence_number_to_inscription_entry: &'a impl ReadableTable, diff --git a/src/index/updater.rs b/src/index/updater.rs index bf1507b0ca..d3f90b664b 100644 --- a/src/index/updater.rs +++ b/src/index/updater.rs @@ -386,6 +386,7 @@ impl<'index> Updater<'index> { let mut sequence_number_to_satpoint = wtx.open_table(SEQUENCE_NUMBER_TO_SATPOINT)?; let mut statistic_to_count = wtx.open_table(STATISTIC_TO_COUNT)?; let mut transaction_id_to_transaction = wtx.open_table(TRANSACTION_ID_TO_TRANSACTION)?; + let mut address_to_inscription_ids = wtx.open_table(ADDRESS_TO_INSCRIPTION_IDS)?; let mut lost_sats = statistic_to_count .get(&Statistic::LostSats.key())? @@ -441,6 +442,8 @@ impl<'index> Updater<'index> { timestamp: block.header.time, transaction_buffer: Vec::new(), transaction_id_to_transaction: &mut transaction_id_to_transaction, + address_to_inscription_ids: &mut address_to_inscription_ids, + index_addresses: self.index.index_addresses, unbound_inscriptions, value_cache, value_receiver, diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index 41dd42c538..86d06bf557 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -49,6 +49,7 @@ pub(super) struct InscriptionUpdater<'a, 'tx> { pub(super) home_inscriptions: &'a mut Table<'tx, u32, InscriptionIdValue>, pub(super) id_to_sequence_number: &'a mut Table<'tx, InscriptionIdValue, u32>, pub(super) index_transactions: bool, + pub(super) index_addresses: bool, pub(super) inscription_number_to_sequence_number: &'a mut Table<'tx, i32, u32>, pub(super) lost_sats: u64, pub(super) next_sequence_number: u32, @@ -65,6 +66,7 @@ pub(super) struct InscriptionUpdater<'a, 'tx> { pub(super) unbound_inscriptions: u64, pub(super) value_cache: &'a mut HashMap, pub(super) value_receiver: &'a mut Receiver, + pub(super) address_to_inscription_ids: &'a mut Table<'tx, &'static [u8], Vec>, } impl<'a, 'tx> InscriptionUpdater<'a, 'tx> { @@ -345,7 +347,7 @@ impl<'a, 'tx> InscriptionUpdater<'a, 'tx> { _ => new_satpoint, }; - self.update_inscription_location(input_sat_ranges, flotsam, new_satpoint)?; + self.update_inscription_location(input_sat_ranges, flotsam, new_satpoint, tx)?; } if is_coinbase { @@ -354,7 +356,7 @@ impl<'a, 'tx> InscriptionUpdater<'a, 'tx> { outpoint: OutPoint::null(), offset: self.lost_sats + flotsam.offset - output_value, }; - self.update_inscription_location(input_sat_ranges, flotsam, new_satpoint)?; + self.update_inscription_location(input_sat_ranges, flotsam, new_satpoint, tx)?; } self.lost_sats += self.reward - output_value; Ok(()) @@ -392,6 +394,7 @@ impl<'a, 'tx> InscriptionUpdater<'a, 'tx> { input_sat_ranges: Option<&VecDeque<(u64, u64)>>, flotsam: Flotsam, new_satpoint: SatPoint, + transaction: &Transaction, ) -> Result { let inscription_id = flotsam.inscription_id; let (unbound, sequence_number) = match flotsam.origin { @@ -416,6 +419,8 @@ impl<'a, 'tx> InscriptionUpdater<'a, 'tx> { })?; } + self.move_inscription_to_address(flotsam.clone(), transaction, new_satpoint)?; + (false, sequence_number) } Origin::New { @@ -541,6 +546,8 @@ impl<'a, 'tx> InscriptionUpdater<'a, 'tx> { } } + self.move_inscription_to_address(flotsam.clone(), transaction, new_satpoint)?; + (unbound, sequence_number) } }; @@ -565,4 +572,83 @@ impl<'a, 'tx> InscriptionUpdater<'a, 'tx> { Ok(()) } + + fn move_inscription_to_address( + &mut self, + floatsam: Flotsam, + transaction: &Transaction, + new_satpoint: SatPoint, + ) -> Result { + if !self.index_addresses || !self.index_transactions { + return Ok(()); + } + + self.remove_inscription_from_address(floatsam.clone())?; + + let address = self.transaction_to_address(transaction, new_satpoint.outpoint.vout.into_usize())?; + + let mut inscription_ids = self.get_inscription_ids_by_address(&address)?; + inscription_ids.push(floatsam.inscription_id.store()); + inscription_ids.sort(); + + self + .address_to_inscription_ids + .insert(&address.as_bytes(), inscription_ids)?; + + Ok(()) + } + + fn remove_inscription_from_address(&mut self, floatsam: Flotsam) -> Result { + if !self.index_addresses || !self.index_transactions { + return Ok(()); + } + + if let Origin::Old { old_satpoint } = floatsam.origin { + if let Some(transaction) = self + .transaction_id_to_transaction + .get(&old_satpoint.outpoint.txid.store())? + { + let transaction: Transaction = consensus::encode::deserialize(transaction.value())?; + + let address_entry = + self.transaction_to_address(&transaction, old_satpoint.outpoint.vout.into_usize())?; + + let mut inscription_ids = self.get_inscription_ids_by_address(&address_entry)?; + inscription_ids.retain(|&id| id != floatsam.inscription_id.store()); + + self + .address_to_inscription_ids + .insert(&address_entry.as_bytes(), inscription_ids)?; + } + } + + Ok(()) + } + + fn get_transaction(&self, txid: &Txid) -> Result { + let transaction = self + .transaction_id_to_transaction + .get(&txid.store())? + .unwrap(); + + Ok(consensus::encode::deserialize(transaction.value())?) + } + + #[inline] + fn transaction_to_address(&self, transaction: &Transaction, vout: usize) -> Result { + let tx_out = transaction.output.iter().nth(vout).unwrap(); + + let address = self.chain.address_from_script(&tx_out.script_pubkey)?; + + Ok(address.script_pubkey()) + } + + #[inline] + fn get_inscription_ids_by_address(&self, address: &ScriptBuf) -> Result> { + if let Some(entry) = self.address_to_inscription_ids.get(&address.as_bytes())? { + return Ok(entry.value().clone()); + } + + Ok(Vec::new()) + } } diff --git a/src/options.rs b/src/options.rs index b77002d6d6..0771ec6eee 100644 --- a/src/options.rs +++ b/src/options.rs @@ -61,6 +61,8 @@ pub struct Options { pub(crate) index_spent_sats: bool, #[arg(long, help = "Store transactions in index.")] pub(crate) index_transactions: bool, + #[arg(long, help = "Store addresses inscriptions in index.")] + pub(crate) index_addresses: bool, #[arg(long, help = "Run in integration test mode.")] pub(crate) integration_test: bool, #[arg(long, help = "Minify JSON output.")] diff --git a/src/settings.rs b/src/settings.rs index f3c7d3b0eb..7de414501d 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -22,6 +22,7 @@ pub struct Settings { index_sats: bool, index_spent_sats: bool, index_transactions: bool, + index_addresses: bool, integration_test: bool, no_index_inscriptions: bool, server_password: Option, @@ -136,6 +137,7 @@ impl Settings { index_sats: self.index_sats || source.index_sats, index_spent_sats: self.index_spent_sats || source.index_spent_sats, index_transactions: self.index_transactions || source.index_transactions, + index_addresses: self.index_addresses || source.index_addresses, integration_test: self.integration_test || source.integration_test, no_index_inscriptions: self.no_index_inscriptions || source.no_index_inscriptions, server_password: self.server_password.or(source.server_password), @@ -170,6 +172,7 @@ impl Settings { index_sats: options.index_sats, index_spent_sats: options.index_spent_sats, index_transactions: options.index_transactions, + index_addresses: options.index_addresses, integration_test: options.integration_test, no_index_inscriptions: options.no_index_inscriptions, server_password: options.server_password, @@ -248,6 +251,7 @@ impl Settings { index_sats: get_bool("INDEX_SATS"), index_spent_sats: get_bool("INDEX_SPENT_SATS"), index_transactions: get_bool("INDEX_TRANSACTIONS"), + index_addresses: get_bool("INDEX_ADDRESSES"), integration_test: get_bool("INTEGRATION_TEST"), no_index_inscriptions: get_bool("NO_INDEX_INSCRIPTIONS"), server_password: get_string("SERVER_PASSWORD"), @@ -277,6 +281,7 @@ impl Settings { index_sats: true, index_spent_sats: false, index_transactions: false, + index_addresses: false, integration_test: false, no_index_inscriptions: false, server_password: None, @@ -356,6 +361,7 @@ impl Settings { index_sats: self.index_sats, index_spent_sats: self.index_spent_sats, index_transactions: self.index_transactions, + index_addresses: self.index_addresses, integration_test: self.integration_test, no_index_inscriptions: self.no_index_inscriptions, server_password: self.server_password, @@ -530,6 +536,10 @@ impl Settings { self.index_transactions } + pub(crate) fn index_addresses(&self) -> bool { + self.index_addresses + } + pub(crate) fn integration_test(&self) -> bool { self.integration_test } @@ -998,6 +1008,7 @@ mod tests { ("INDEX_SATS", "1"), ("INDEX_SPENT_SATS", "1"), ("INDEX_TRANSACTIONS", "1"), + ("INDEX_ADDRESSES", "1"), ("INTEGRATION_TEST", "1"), ("NO_INDEX_INSCRIPTIONS", "1"), ("SERVER_PASSWORD", "server password"), @@ -1041,6 +1052,7 @@ mod tests { index_sats: true, index_spent_sats: true, index_transactions: true, + index_addresses: true, integration_test: true, no_index_inscriptions: true, server_password: Some("server password".into()), @@ -1073,6 +1085,7 @@ mod tests { "--index-sats", "--index-spent-sats", "--index-transactions", + "--index-addresses", "--index=index", "--integration-test", "--no-index-inscriptions", @@ -1101,6 +1114,7 @@ mod tests { index_sats: true, index_spent_sats: true, index_transactions: true, + index_addresses: true, integration_test: true, no_index_inscriptions: true, server_password: Some("server password".into()), From 686e864bde888068e776aeea8890a8cc0c56e898 Mon Sep 17 00:00:00 2001 From: mark Date: Tue, 26 Mar 2024 00:10:33 +0300 Subject: [PATCH 02/15] address index --- src/index.rs | 1 + src/index/updater/inscription_updater.rs | 58 ++++++++---------------- tests/settings.rs | 1 + 3 files changed, 21 insertions(+), 39 deletions(-) diff --git a/src/index.rs b/src/index.rs index c174588a21..f5d87a4097 100644 --- a/src/index.rs +++ b/src/index.rs @@ -2021,6 +2021,7 @@ impl Index { } } + #[allow(dead_code)] pub(crate) fn get_inscription_ids_by_address( &self, address: Address, diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index 86d06bf557..b2529fb04b 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -419,15 +419,13 @@ impl<'a, 'tx> InscriptionUpdater<'a, 'tx> { })?; } - self.move_inscription_to_address(flotsam.clone(), transaction, new_satpoint)?; - (false, sequence_number) } Origin::New { cursed, fee, hidden, - parents, + ref parents, pointer: _, reinscription, unbound, @@ -509,7 +507,7 @@ impl<'a, 'tx> InscriptionUpdater<'a, 'tx> { charms, inscription_id, location: (!unbound).then_some(new_satpoint), - parent_inscription_ids: parents, + parent_inscription_ids: parents.to_vec(), sequence_number, })?; } @@ -546,12 +544,12 @@ impl<'a, 'tx> InscriptionUpdater<'a, 'tx> { } } - self.move_inscription_to_address(flotsam.clone(), transaction, new_satpoint)?; - (unbound, sequence_number) } }; + self.index_address_to_inscription(&flotsam, transaction, new_satpoint)?; + let satpoint = if unbound { let new_unbound_satpoint = SatPoint { outpoint: unbound_outpoint(), @@ -573,9 +571,9 @@ impl<'a, 'tx> InscriptionUpdater<'a, 'tx> { Ok(()) } - fn move_inscription_to_address( + fn index_address_to_inscription( &mut self, - floatsam: Flotsam, + flotsam: &Flotsam, transaction: &Transaction, new_satpoint: SatPoint, ) -> Result { @@ -583,27 +581,7 @@ impl<'a, 'tx> InscriptionUpdater<'a, 'tx> { return Ok(()); } - self.remove_inscription_from_address(floatsam.clone())?; - - let address = self.transaction_to_address(transaction, new_satpoint.outpoint.vout.into_usize())?; - - let mut inscription_ids = self.get_inscription_ids_by_address(&address)?; - inscription_ids.push(floatsam.inscription_id.store()); - inscription_ids.sort(); - - self - .address_to_inscription_ids - .insert(&address.as_bytes(), inscription_ids)?; - - Ok(()) - } - - fn remove_inscription_from_address(&mut self, floatsam: Flotsam) -> Result { - if !self.index_addresses || !self.index_transactions { - return Ok(()); - } - - if let Origin::Old { old_satpoint } = floatsam.origin { + if let Origin::Old { old_satpoint } = flotsam.origin { if let Some(transaction) = self .transaction_id_to_transaction .get(&old_satpoint.outpoint.txid.store())? @@ -614,7 +592,7 @@ impl<'a, 'tx> InscriptionUpdater<'a, 'tx> { self.transaction_to_address(&transaction, old_satpoint.outpoint.vout.into_usize())?; let mut inscription_ids = self.get_inscription_ids_by_address(&address_entry)?; - inscription_ids.retain(|&id| id != floatsam.inscription_id.store()); + inscription_ids.retain(|&id| id != flotsam.inscription_id.store()); self .address_to_inscription_ids @@ -622,21 +600,23 @@ impl<'a, 'tx> InscriptionUpdater<'a, 'tx> { } } - Ok(()) - } + let address = + self.transaction_to_address(transaction, new_satpoint.outpoint.vout.into_usize())?; - fn get_transaction(&self, txid: &Txid) -> Result { - let transaction = self - .transaction_id_to_transaction - .get(&txid.store())? - .unwrap(); + let mut inscription_ids = self.get_inscription_ids_by_address(&address)?; + inscription_ids.push(flotsam.inscription_id.store()); + inscription_ids.sort(); - Ok(consensus::encode::deserialize(transaction.value())?) + self + .address_to_inscription_ids + .insert(&address.as_bytes(), inscription_ids)?; + + Ok(()) } #[inline] fn transaction_to_address(&self, transaction: &Transaction, vout: usize) -> Result { - let tx_out = transaction.output.iter().nth(vout).unwrap(); + let tx_out = transaction.output.get(vout).unwrap(); let address = self.chain.address_from_script(&tx_out.script_pubkey)?; diff --git a/tests/settings.rs b/tests/settings.rs index bfd271d28b..5f3ef014cb 100644 --- a/tests/settings.rs +++ b/tests/settings.rs @@ -25,6 +25,7 @@ fn default() { "index_sats": false, "index_spent_sats": false, "index_transactions": false, + "index_addresses": false, "integration_test": false, "no_index_inscriptions": false, "server_password": null, From 7bfa88bdbb880781c5335481e8e613734c251910 Mon Sep 17 00:00:00 2001 From: mark Date: Tue, 26 Mar 2024 01:03:38 +0300 Subject: [PATCH 03/15] test --- src/index.rs | 129 ++++++++++++++++++++--- src/index/updater/inscription_updater.rs | 23 ++-- 2 files changed, 129 insertions(+), 23 deletions(-) diff --git a/src/index.rs b/src/index.rs index f5d87a4097..b8cc1537cd 100644 --- a/src/index.rs +++ b/src/index.rs @@ -2026,18 +2026,23 @@ impl Index { &self, address: Address, ) -> Result> { - Ok( - self - .database - .begin_read()? - .open_table(ADDRESS_TO_INSCRIPTION_IDS)? - .get(address.to_string().as_bytes())? - .unwrap() - .value() - .iter() - .map(|entry| InscriptionId::load(*entry)) - .collect(), - ) + let entry = self + .database + .begin_read()? + .open_table(ADDRESS_TO_INSCRIPTION_IDS)? + .get(address.to_string().as_bytes())?; + + if let Some(entry) = entry { + return Ok( + entry + .value() + .iter() + .map(|entry| InscriptionId::load(*entry)) + .collect(), + ); + } + + Ok(vec![]) } fn inscriptions_on_output<'a: 'tx, 'tx>( @@ -6213,4 +6218,104 @@ mod tests { } ); } + + #[test] + fn inscription_get_attached_to_address() { + let context = Context::builder() + .arg("--index-sats") + .arg("--index-transactions") + .arg("--index-addresses") + .build(); + + context.mine_blocks(1); + + let txid = context.rpc_server.broadcast_tx(TransactionTemplate { + inputs: &[(1, 0, 0, inscription("text/plain", "hello").to_witness())], + ..Default::default() + }); + + let inscription_id = InscriptionId { txid, index: 0 }; + + assert_eq!( + context + .index + .get_inscriptions_on_output(OutPoint { txid, vout: 0 }) + .unwrap(), + [] + ); + + context.mine_blocks(1); + + { + let transaction = context.index.get_transaction(txid).unwrap().unwrap(); + + let address = context + .index + .settings + .chain() + .address_from_script(&transaction.output[0].script_pubkey) + .unwrap(); + + assert_eq!( + context + .index + .get_inscription_ids_by_address(address) + .unwrap(), + [inscription_id] + ); + } + + assert_eq!( + context + .index + .get_inscriptions_on_output(OutPoint { txid, vout: 0 }) + .unwrap(), + [inscription_id] + ); + + let send_id = context.rpc_server.broadcast_tx(TransactionTemplate { + inputs: &[(2, 1, 0, Default::default())], + ..Default::default() + }); + + context.mine_blocks(1); + + { + let transaction = context.index.get_transaction(send_id).unwrap().unwrap(); + + let address = context + .index + .settings + .chain() + .address_from_script(&transaction.output[0].script_pubkey) + .unwrap(); + + assert_eq!( + context + .index + .get_inscription_ids_by_address(address) + .unwrap(), + [inscription_id] + ); + } + + assert_eq!( + context + .index + .get_inscriptions_on_output(OutPoint { txid, vout: 0 }) + .unwrap(), + [] + ); + + assert_eq!( + context + .index + .get_inscriptions_on_output(OutPoint { + txid: send_id, + vout: 0, + }) + .unwrap(), + [inscription_id] + ); + } } diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index b2529fb04b..b6f0f61c57 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -571,7 +571,7 @@ impl<'a, 'tx> InscriptionUpdater<'a, 'tx> { Ok(()) } - fn index_address_to_inscription( + fn index_address_to_inscription<'address>( &mut self, flotsam: &Flotsam, transaction: &Transaction, @@ -588,15 +588,15 @@ impl<'a, 'tx> InscriptionUpdater<'a, 'tx> { { let transaction: Transaction = consensus::encode::deserialize(transaction.value())?; - let address_entry = + let address = self.transaction_to_address(&transaction, old_satpoint.outpoint.vout.into_usize())?; - let mut inscription_ids = self.get_inscription_ids_by_address(&address_entry)?; + let mut inscription_ids = self.get_inscription_ids_by_address(&address)?; inscription_ids.retain(|&id| id != flotsam.inscription_id.store()); self .address_to_inscription_ids - .insert(&address_entry.as_bytes(), inscription_ids)?; + .insert(address.to_string().as_bytes(), inscription_ids)?; } } @@ -609,23 +609,24 @@ impl<'a, 'tx> InscriptionUpdater<'a, 'tx> { self .address_to_inscription_ids - .insert(&address.as_bytes(), inscription_ids)?; + .insert(address.to_string().as_bytes(), inscription_ids)?; Ok(()) } #[inline] - fn transaction_to_address(&self, transaction: &Transaction, vout: usize) -> Result { + fn transaction_to_address(&self, transaction: &Transaction, vout: usize) -> Result
{ let tx_out = transaction.output.get(vout).unwrap(); - let address = self.chain.address_from_script(&tx_out.script_pubkey)?; - - Ok(address.script_pubkey()) + Ok(self.chain.address_from_script(&tx_out.script_pubkey)?) } #[inline] - fn get_inscription_ids_by_address(&self, address: &ScriptBuf) -> Result> { - if let Some(entry) = self.address_to_inscription_ids.get(&address.as_bytes())? { + fn get_inscription_ids_by_address(&self, address: &Address) -> Result> { + if let Some(entry) = self + .address_to_inscription_ids + .get(address.to_string().as_bytes())? + { return Ok(entry.value().clone()); } From 9070716612c1023ed060b52a601310ae4362bedd Mon Sep 17 00:00:00 2001 From: mark Date: Tue, 26 Mar 2024 01:53:51 +0300 Subject: [PATCH 04/15] send to another address --- src/index.rs | 69 +++++++++++++----------- src/index/updater/inscription_updater.rs | 3 +- 2 files changed, 39 insertions(+), 33 deletions(-) diff --git a/src/index.rs b/src/index.rs index b8cc1537cd..68e64e444b 100644 --- a/src/index.rs +++ b/src/index.rs @@ -2024,7 +2024,7 @@ impl Index { #[allow(dead_code)] pub(crate) fn get_inscription_ids_by_address( &self, - address: Address, + address: &Address, ) -> Result> { let entry = self .database @@ -6246,24 +6246,22 @@ mod tests { context.mine_blocks(1); - { - let transaction = context.index.get_transaction(txid).unwrap().unwrap(); + let first_transaction = context.index.get_transaction(txid).unwrap().unwrap(); - let address = context - .index - .settings - .chain() - .address_from_script(&transaction.output[0].script_pubkey) - .unwrap(); + let first_address = context + .index + .settings + .chain() + .address_from_script(&first_transaction.output[0].script_pubkey) + .unwrap(); - assert_eq!( - context - .index - .get_inscription_ids_by_address(address) - .unwrap(), - [inscription_id] - ); - } + assert_eq!( + context + .index + .get_inscription_ids_by_address(&first_address) + .unwrap(), + [inscription_id] + ); assert_eq!( context @@ -6275,29 +6273,36 @@ mod tests { let send_id = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(2, 1, 0, Default::default())], + p2tr: true, ..Default::default() }); context.mine_blocks(1); - { - let transaction = context.index.get_transaction(send_id).unwrap().unwrap(); + let second_transaction = context.index.get_transaction(send_id).unwrap().unwrap(); - let address = context + let second_address = context + .index + .settings + .chain() + .address_from_script(&second_transaction.output[0].script_pubkey) + .unwrap(); + + assert_eq!( + context .index - .settings - .chain() - .address_from_script(&transaction.output[0].script_pubkey) - .unwrap(); + .get_inscription_ids_by_address(&second_address) + .unwrap(), + [inscription_id] + ); - assert_eq!( - context - .index - .get_inscription_ids_by_address(address) - .unwrap(), - [inscription_id] - ); - } + assert_eq!( + context + .index + .get_inscription_ids_by_address(&first_address) + .unwrap(), + [] + ); assert_eq!( context diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index b6f0f61c57..b46dce6723 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -592,7 +592,8 @@ impl<'a, 'tx> InscriptionUpdater<'a, 'tx> { self.transaction_to_address(&transaction, old_satpoint.outpoint.vout.into_usize())?; let mut inscription_ids = self.get_inscription_ids_by_address(&address)?; - inscription_ids.retain(|&id| id != flotsam.inscription_id.store()); + let inscription_id_entry = flotsam.inscription_id.store(); + inscription_ids.retain(|&id| id != inscription_id_entry); self .address_to_inscription_ids From 495f514298f0fcdd2b81b7d73e5698feaf4136da Mon Sep 17 00:00:00 2001 From: mark Date: Tue, 26 Mar 2024 10:56:53 +0300 Subject: [PATCH 05/15] endpoint --- src/index/updater/inscription_updater.rs | 2 +- src/subcommand/server.rs | 37 ++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index b46dce6723..30736ee6ad 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -571,7 +571,7 @@ impl<'a, 'tx> InscriptionUpdater<'a, 'tx> { Ok(()) } - fn index_address_to_inscription<'address>( + fn index_address_to_inscription( &mut self, flotsam: &Flotsam, transaction: &Transaction, diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 1522cb70bf..e931900b6b 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -261,6 +261,7 @@ impl Server { .route("/status", get(Self::status)) .route("/tx/:txid", get(Self::transaction)) .route("/update", get(Self::update)) + .route("/address/:address", get(Self::address)) .fallback(Self::fallback) .layer(Extension(index)) .layer(Extension(server_config.clone())) @@ -897,6 +898,42 @@ impl Server { }) } + async fn address( + Extension(settings): Extension>, + Extension(index): Extension>, + Path(address): Path, + AcceptJson(accept_json): AcceptJson, + ) -> ServerResult { + task::block_in_place(|| { + if !settings.index_addresses() { + return Ok(StatusCode::OK.into_response()); + } + + let address = Address::from_str(&address) + .unwrap() + .require_network(settings.chain().network()) + .unwrap(); + + let inscriptions = index.get_inscription_ids_by_address(&address)?; + + Ok(if accept_json { + Json(api::Inscriptions { + ids: inscriptions, + page_index: 0, + more: false, + }) + .into_response() + } else { + InscriptionsHtml { + inscriptions, + prev: None, + next: None, + } + .into_response() + }) + }) + } + async fn update( Extension(settings): Extension>, Extension(index): Extension>, From 939dea35e9514acd6393871ad1561b16b6b08f07 Mon Sep 17 00:00:00 2001 From: mark Date: Tue, 26 Mar 2024 16:13:29 +0300 Subject: [PATCH 06/15] html --- templates/inscription.html | 4 ++-- templates/output.html | 4 ++-- templates/transaction.html | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/templates/inscription.html b/templates/inscription.html index 09000ac48a..15e48e20ff 100644 --- a/templates/inscription.html +++ b/templates/inscription.html @@ -58,9 +58,9 @@

Inscription {{ self.number }}

%% } %% if let Some(output) = &self.output { -%% if let Ok(address) = self.chain.address_from_script(&output.script_pubkey ) { +%% if let Ok(address) = self.chain.address_from_script(&output.script_pubkey) {
address
-
{{ address }}
+
{{ address }}
%% }
value
{{ output.value }}
diff --git a/templates/output.html b/templates/output.html index e5e6f55d60..c30b83b860 100644 --- a/templates/output.html +++ b/templates/output.html @@ -27,8 +27,8 @@

Output {{self.outpoint}}

%% }
value
{{ self.output.value }}
script pubkey
{{ self.output.script_pubkey.to_asm_string() }}
-%% if let Ok(address) = self.chain.address_from_script(&self.output.script_pubkey ) { -
address
{{ address }}
+%% if let Ok(address) = self.chain.address_from_script(&self.output.script_pubkey) { +
address
{{ address }}
%% }
transaction
{{ self.outpoint.txid }}
spent
{{ self.spent }}
diff --git a/templates/transaction.html b/templates/transaction.html index d1e9cc29bf..7d75ecf567 100644 --- a/templates/transaction.html +++ b/templates/transaction.html @@ -31,7 +31,7 @@

{{"Output".tally(self.transaction.output.len())}}

value
{{ output.value }}
script pubkey
{{ output.script_pubkey.to_asm_string() }}
%% if let Ok(address) = self.chain.address_from_script(&output.script_pubkey) { -
address
{{ address }}
+
address
{{ address }}
%% } From 0170ae13bf7cbcb72baf44839911a5424c8fc37f Mon Sep 17 00:00:00 2001 From: mark Date: Tue, 26 Mar 2024 23:13:23 +0300 Subject: [PATCH 07/15] html test --- src/templates/inscription.rs | 2 +- src/templates/output.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/templates/inscription.rs b/src/templates/inscription.rs index 353a6748b2..fb56f6743c 100644 --- a/src/templates/inscription.rs +++ b/src/templates/inscription.rs @@ -107,7 +107,7 @@ mod tests {
.*
address
-
bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4
+
bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4
value
1
.* diff --git a/src/templates/output.rs b/src/templates/output.rs index ef13b6e2b8..a520f97c82 100644 --- a/src/templates/output.rs +++ b/src/templates/output.rs @@ -41,7 +41,7 @@ mod tests {
value
3
script pubkey
OP_DUP OP_HASH160 OP_PUSHBYTES_20 0{40} OP_EQUALVERIFY OP_CHECKSIG
-
address
1111111111111111111114oLvT2
+
address
1111111111111111111114oLvT2
transaction
1{64}
spent
false
@@ -100,7 +100,7 @@ mod tests {
value
3
script pubkey
OP_DUP OP_HASH160 OP_PUSHBYTES_20 0{40} OP_EQUALVERIFY OP_CHECKSIG
-
address
1111111111111111111114oLvT2
+
address
1111111111111111111114oLvT2
transaction
1{64}
spent
true
@@ -132,7 +132,7 @@ mod tests {
value
3
script pubkey
OP_DUP OP_HASH160 OP_PUSHBYTES_20 0{40} OP_EQUALVERIFY OP_CHECKSIG
-
address
1111111111111111111114oLvT2
+
address
1111111111111111111114oLvT2
transaction
1{64}
spent
false
From 9a9c8ec42edd0bf9cb3c89d483f0fd5e6ee4232b Mon Sep 17 00:00:00 2001 From: mark Date: Tue, 26 Mar 2024 23:35:22 +0300 Subject: [PATCH 08/15] html test --- tests/server.rs | 4 ++-- tests/wallet/inscribe.rs | 6 +++--- tests/wallet/send.rs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/server.rs b/tests/server.rs index 2b3c3b3f10..14d692c46e 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -60,7 +60,7 @@ fn inscription_page() {
id
{inscription}
address
-
bc1.*
+
bc1.*
value
10000
preview
@@ -190,7 +190,7 @@ fn inscription_page_after_send() { ord_rpc_server.assert_response_regex( format!("/inscription/{inscription}"), format!( - r".*

Inscription 0

.*
address
\s*
bc1qcqgs2pps4u4yedfyl5pysdjjncs8et5utseepv
.*
location
\s*
{txid}:0:0
.*", + r".*

Inscription 0

.*
address
\s*
.*
.*
location
\s*
{txid}:0:0
.*", ), ) } diff --git a/tests/wallet/inscribe.rs b/tests/wallet/inscribe.rs index f117511f00..9975f2d0d8 100644 --- a/tests/wallet/inscribe.rs +++ b/tests/wallet/inscribe.rs @@ -1600,7 +1600,7 @@ inscriptions: format!("/inscription/{}", output.inscriptions[0].id), ".*
address
-
bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4
.*", +
bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4
.*", ); ord_rpc_server.assert_response_regex( @@ -1608,7 +1608,7 @@ inscriptions: format!( ".*
address
-
{}
.*", +
{0}
.*", bitcoin_rpc_server.state().change_addresses[0], ), ); @@ -1617,7 +1617,7 @@ inscriptions: format!("/inscription/{}", output.inscriptions[2].id), ".*
address
-
bc1pxwww0ct9ue7e8tdnlmug5m2tamfn7q06sahstg39ys4c9f3340qqxrdu9k
.*", +
bc1pxwww0ct9ue7e8tdnlmug5m2tamfn7q06sahstg39ys4c9f3340qqxrdu9k
.*", ); } diff --git a/tests/wallet/send.rs b/tests/wallet/send.rs index bbdb93bfb6..39e9211c16 100644 --- a/tests/wallet/send.rs +++ b/tests/wallet/send.rs @@ -157,7 +157,7 @@ fn send_inscription_by_sat() { ord_rpc_server.assert_response_regex( format!("/inscription/{inscription}"), format!( - ".*

Inscription 0

.*
address
.*
{address}
.*
location
.*
{send_txid}:0:0
.*", + ".*

Inscription 0

.*
address
.*{address}.*
location
.*
{send_txid}:0:0
.*", ), ); } From 67d85f3db68ebfa11f9dfc3120ce32a8b402654b Mon Sep 17 00:00:00 2001 From: mark Date: Wed, 27 Mar 2024 12:25:16 +0300 Subject: [PATCH 09/15] address html page --- src/api.rs | 8 +++++++- src/subcommand/server.rs | 27 ++++++++++++++------------- src/templates.rs | 2 ++ 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/api.rs b/src/api.rs index 2867fdfae3..63d9fd8693 100644 --- a/src/api.rs +++ b/src/api.rs @@ -8,6 +8,12 @@ pub use crate::templates::{ TransactionHtml as Transaction, }; +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub struct Address { + pub address: bitcoin::Address, + pub inscriptions: Vec, +} + #[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct Block { pub hash: BlockHash, @@ -119,7 +125,7 @@ pub struct Inscriptions { #[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct Output { - pub address: Option>, + pub address: Option>, pub indexed: bool, pub inscriptions: Vec, pub runes: Vec<(SpacedRune, Pile)>, diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index e931900b6b..273c367c0c 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -6,9 +6,9 @@ use { }, super::*, crate::templates::{ - BlockHtml, BlocksHtml, ChildrenHtml, ClockSvg, CollectionsHtml, HomeHtml, InputHtml, - InscriptionHtml, InscriptionsBlockHtml, InscriptionsHtml, OutputHtml, PageContent, PageHtml, - ParentsHtml, PreviewAudioHtml, PreviewCodeHtml, PreviewFontHtml, PreviewImageHtml, + AddressHtml, BlockHtml, BlocksHtml, ChildrenHtml, ClockSvg, CollectionsHtml, HomeHtml, + InputHtml, InscriptionHtml, InscriptionsBlockHtml, InscriptionsHtml, OutputHtml, PageContent, + PageHtml, ParentsHtml, PreviewAudioHtml, PreviewCodeHtml, PreviewFontHtml, PreviewImageHtml, PreviewMarkdownHtml, PreviewModelHtml, PreviewPdfHtml, PreviewTextHtml, PreviewUnknownHtml, PreviewVideoHtml, RangeHtml, RareTxt, RuneBalancesHtml, RuneHtml, RunesHtml, SatHtml, TransactionHtml, @@ -909,25 +909,26 @@ impl Server { return Ok(StatusCode::OK.into_response()); } - let address = Address::from_str(&address) - .unwrap() + let address_unchecked = + Address::from_str(&address).map_err(|err| ServerError::BadRequest(err.to_string()))?; + + let address = address_unchecked + .clone() .require_network(settings.chain().network()) - .unwrap(); + .map_err(|err| ServerError::BadRequest(err.to_string()))?; let inscriptions = index.get_inscription_ids_by_address(&address)?; Ok(if accept_json { - Json(api::Inscriptions { - ids: inscriptions, - page_index: 0, - more: false, + Json(api::Address { + inscriptions, + address: address_unchecked, }) .into_response() } else { - InscriptionsHtml { + AddressHtml { inscriptions, - prev: None, - next: None, + address, } .into_response() }) diff --git a/src/templates.rs b/src/templates.rs index e5491dcc0a..7406c1e32a 100644 --- a/src/templates.rs +++ b/src/templates.rs @@ -2,6 +2,7 @@ use {super::*, boilerplate::Boilerplate}; pub(crate) use { crate::subcommand::server::ServerConfig, + address::AddressHtml, block::BlockHtml, children::ChildrenHtml, clock::ClockSvg, @@ -30,6 +31,7 @@ pub use { transaction::TransactionHtml, }; +pub mod address; pub mod block; pub mod blocks; mod children; From 5adf2f64df5dd4ec64c1cd56b1327d02d7d073f0 Mon Sep 17 00:00:00 2001 From: mark Date: Wed, 27 Mar 2024 12:25:20 +0300 Subject: [PATCH 10/15] address html page --- src/templates/address.rs | 39 +++++++++++++++++++++++++++++++++++++++ templates/address.html | 6 ++++++ 2 files changed, 45 insertions(+) create mode 100644 src/templates/address.rs create mode 100644 templates/address.html diff --git a/src/templates/address.rs b/src/templates/address.rs new file mode 100644 index 0000000000..a577a64094 --- /dev/null +++ b/src/templates/address.rs @@ -0,0 +1,39 @@ +use super::*; + +#[derive(Boilerplate)] +pub(crate) struct AddressHtml { + pub(crate) inscriptions: Vec, + pub(crate) address: Address, +} + +impl PageContent for AddressHtml { + fn title(&self) -> String { + "Address".into() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn positive() { + assert_regex_match!( + AddressHtml { + inscriptions: vec![inscription_id(1), inscription_id(2)], + address: Address::from_str( + "bc1phuq0vkls6w926zdaem6x9n02z2gg7j2xfudgwddyey7uyquarlgsh40ev8" + ) + .unwrap() + .require_network(Network::Bitcoin) + .unwrap(), + }, + "

Address bc1phuq0vkls6w926zdaem6x9n02z2gg7j2xfudgwddyey7uyquarlgsh40ev8

+
+ + +
.*" + .unindent() + ); + } +} diff --git a/templates/address.html b/templates/address.html new file mode 100644 index 0000000000..c0002c00b1 --- /dev/null +++ b/templates/address.html @@ -0,0 +1,6 @@ +

Address {{ self.address }}

+
+%% for id in &self.inscriptions { + {{Iframe::thumbnail(*id)}} +%% } +
\ No newline at end of file From e58d4785354bc1f3eca5949d05f5276cc564f1da Mon Sep 17 00:00:00 2001 From: mark Date: Wed, 27 Mar 2024 12:39:09 +0300 Subject: [PATCH 11/15] addresshtml to page --- src/subcommand/server.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 273c367c0c..d0e21a5b8d 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -930,6 +930,7 @@ impl Server { inscriptions, address, } + .page(server_config) .into_response() }) }) From 2da9e6e1984c561ef276aae59daf63cbb4aba93a Mon Sep 17 00:00:00 2001 From: mark Date: Wed, 27 Mar 2024 12:42:41 +0300 Subject: [PATCH 12/15] fix --- src/subcommand/server.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index d0e21a5b8d..8651e7b5b2 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -899,6 +899,7 @@ impl Server { } async fn address( + Extension(server_config): Extension>, Extension(settings): Extension>, Extension(index): Extension>, Path(address): Path, From b1b9395b90282f433a21a5c5c170cd4fa66e2ed8 Mon Sep 17 00:00:00 2001 From: mark Date: Wed, 27 Mar 2024 12:59:16 +0300 Subject: [PATCH 13/15] update schema --- src/index.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.rs b/src/index.rs index 68e64e444b..88bfc35c1f 100644 --- a/src/index.rs +++ b/src/index.rs @@ -46,7 +46,7 @@ mod updater; #[cfg(test)] pub(crate) mod testing; -const SCHEMA_VERSION: u64 = 24; +const SCHEMA_VERSION: u64 = 25; macro_rules! define_table { ($name:ident, $key:ty, $value:ty) => { From ea1988a75b600e90e7a6921bd7f7869ed1b0de5a Mon Sep 17 00:00:00 2001 From: mark Date: Wed, 27 Mar 2024 13:52:52 +0300 Subject: [PATCH 14/15] status page, bail on transactions index not set --- src/index.rs | 7 +++++++ src/index/updater/inscription_updater.rs | 2 +- src/options.rs | 5 ++++- src/subcommand/server.rs | 2 ++ src/templates/status.rs | 1 + templates/status.html | 2 ++ tests/json_api.rs | 1 + 7 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/index.rs b/src/index.rs index 88bfc35c1f..048244b64d 100644 --- a/src/index.rs +++ b/src/index.rs @@ -235,6 +235,12 @@ impl Index { settings: &Settings, event_sender: Option>, ) -> Result { + if settings.index_addresses() && !settings.index_transactions() { + bail!( + "cannot index addresses without transactions, consider adding --index-transactions flag" + ); + } + let client = settings.bitcoin_rpc_client(None)?; let path = settings.index().to_owned(); @@ -511,6 +517,7 @@ impl Index { sat_index: statistic(Statistic::IndexSats)? != 0, started: self.started, transaction_index: statistic(Statistic::IndexTransactions)? != 0, + address_index: statistic(Statistic::IndexAddresses)? != 0, unrecoverably_reorged: self.unrecoverably_reorged.load(atomic::Ordering::Relaxed), uptime: (Utc::now() - self.started).to_std()?, }) diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index 30736ee6ad..067f831654 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -577,7 +577,7 @@ impl<'a, 'tx> InscriptionUpdater<'a, 'tx> { transaction: &Transaction, new_satpoint: SatPoint, ) -> Result { - if !self.index_addresses || !self.index_transactions { + if !self.index_addresses { return Ok(()); } diff --git a/src/options.rs b/src/options.rs index 0771ec6eee..562b6fe9b0 100644 --- a/src/options.rs +++ b/src/options.rs @@ -61,7 +61,10 @@ pub struct Options { pub(crate) index_spent_sats: bool, #[arg(long, help = "Store transactions in index.")] pub(crate) index_transactions: bool, - #[arg(long, help = "Store addresses inscriptions in index.")] + #[arg( + long, + help = "Store addresses inscriptions in index. Relies on transactions index." + )] pub(crate) index_addresses: bool, #[arg(long, help = "Run in integration test mode.")] pub(crate) integration_test: bool, diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 8651e7b5b2..a1cb2fe6c1 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -3240,6 +3240,8 @@ mod tests {
false
transaction index
false
+
address index
+
false
git branch
.*
git commit
diff --git a/src/templates/status.rs b/src/templates/status.rs index 1ea12b0e1d..18bde4d82a 100644 --- a/src/templates/status.rs +++ b/src/templates/status.rs @@ -16,6 +16,7 @@ pub struct StatusHtml { pub sat_index: bool, pub started: DateTime, pub transaction_index: bool, + pub address_index: bool, pub unrecoverably_reorged: bool, pub uptime: Duration, } diff --git a/templates/status.html b/templates/status.html index 0d2c3957b7..658837934e 100644 --- a/templates/status.html +++ b/templates/status.html @@ -34,6 +34,8 @@

Status

{{ self.sat_index }}
transaction index
{{ self.transaction_index }}
+
address index
+
{{ self.address_index }}
%% if !env!("GIT_BRANCH").is_empty() {
git branch
{{ env!("GIT_BRANCH") }}
diff --git a/tests/json_api.rs b/tests/json_api.rs index abc8b635ed..b2a5ebc078 100644 --- a/tests/json_api.rs +++ b/tests/json_api.rs @@ -512,6 +512,7 @@ fn get_status() { sat_index: true, started: dummy_started, transaction_index: false, + address_index: false, unrecoverably_reorged: false, uptime: dummy_duration, } From adb4629fa0601c6bcb6b3013c02db7451bb5785a Mon Sep 17 00:00:00 2001 From: mark Date: Wed, 27 Mar 2024 14:32:21 +0300 Subject: [PATCH 15/15] bump ci