Skip to content

Commit 8c2b16c

Browse files
author
Ludo Galabru
committed
feat: introduce migration script
1 parent a13351d commit 8c2b16c

File tree

3 files changed

+124
-51
lines changed

3 files changed

+124
-51
lines changed

components/hord-cli/src/cli/mod.rs

+62-21
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::db::{
66
delete_data_in_hord_db, find_last_block_inserted, find_lazy_block_at_block_height,
77
find_watched_satpoint_for_inscription, initialize_hord_db, open_readonly_hord_db_conn,
88
open_readonly_hord_db_conn_rocks_db, open_readwrite_hord_db_conn,
9-
open_readwrite_hord_db_conn_rocks_db, retrieve_satoshi_point_using_lazy_storage,
9+
open_readwrite_hord_db_conn_rocks_db, retrieve_satoshi_point_using_lazy_storage, find_all_inscriptions_in_block, remove_entry_from_inscriptions, insert_entry_in_locations,
1010
};
1111
use crate::hord::{
1212
self, new_traversals_lazy_cache, retrieve_inscribed_satoshi_points_from_block,
@@ -26,6 +26,7 @@ use std::io::{BufReader, Read};
2626
use std::path::PathBuf;
2727
use std::process;
2828
use std::sync::Arc;
29+
use std::sync::mpsc::channel;
2930

3031
#[derive(Parser, Debug)]
3132
#[clap(author, version, about, long_about = None)]
@@ -238,6 +239,8 @@ struct UpdateHordDbCommand {
238239
/// Load config file path
239240
#[clap(long = "config-path")]
240241
pub config_path: Option<String>,
242+
/// Transfers only
243+
pub transfers_only: bool,
241244
}
242245

243246
#[derive(Parser, PartialEq, Clone, Debug)]
@@ -507,31 +510,69 @@ async fn handle_command(opts: Opts, ctx: Context) -> Result<(), String> {
507510
}
508511
Command::Db(HordDbCommand::Rewrite(cmd)) => {
509512
let config = Config::default(false, false, false, &cmd.config_path)?;
510-
// Delete data, if any
511-
{
512-
let blocks_db_rw =
513-
open_readwrite_hord_db_conn_rocks_db(&config.expected_cache_path(), &ctx)?;
514-
let inscriptions_db_conn_rw =
515-
open_readwrite_hord_db_conn(&config.expected_cache_path(), &ctx)?;
513+
if cmd.transfers_only {
514+
let mut inscriptions_db_conn_rw =
515+
open_readwrite_hord_db_conn(&config.expected_cache_path(), &ctx)?;
516+
let inscriptions_db_conn =
517+
open_readonly_hord_db_conn(&config.expected_cache_path(), &ctx)?;
516518

517-
delete_data_in_hord_db(
519+
for cursor in cmd.start_block..cmd.end_block {
520+
let inscriptions = find_all_inscriptions_in_block(&cursor, &inscriptions_db_conn, &ctx);
521+
let transaction = inscriptions_db_conn_rw.transaction().unwrap();
522+
for (_, entry) in inscriptions.iter() {
523+
remove_entry_from_inscriptions(&entry.get_inscription_id(), &transaction, &ctx);
524+
insert_entry_in_locations(&entry.get_inscription_id(), cursor,&entry.transfer_data, &transaction, &ctx)
525+
}
526+
transaction.commit().unwrap();
527+
}
528+
529+
let bitcoin_config = BitcoinConfig {
530+
username: config.network.bitcoind_rpc_username.clone(),
531+
password: config.network.bitcoind_rpc_password.clone(),
532+
rpc_url: config.network.bitcoind_rpc_url.clone(),
533+
network: config.network.bitcoin_network.clone(),
534+
bitcoin_block_signaling: config.network.bitcoin_block_signaling.clone(),
535+
};
536+
537+
let (tx, rx) = channel();
538+
for cursor in cmd.start_block..cmd.end_block {
539+
let block = fetch_and_standardize_block(cursor, &bitcoin_config, &ctx).await.unwrap();
540+
let _ = tx.send(block);
541+
}
542+
543+
let inscriptions_db_conn_rw =
544+
open_readwrite_hord_db_conn(&config.expected_cache_path(), &ctx)?;
545+
let mut storage = Storage::Sqlite(&inscriptions_db_conn_rw);
546+
while let Ok(mut block) = rx.recv() {
547+
update_storage_and_augment_bitcoin_block_with_inscription_transfer_data(&mut block, &mut storage, &ctx).unwrap();
548+
}
549+
} else {
550+
// Delete data, if any
551+
{
552+
let blocks_db_rw =
553+
open_readwrite_hord_db_conn_rocks_db(&config.expected_cache_path(), &ctx)?;
554+
let inscriptions_db_conn_rw =
555+
open_readwrite_hord_db_conn(&config.expected_cache_path(), &ctx)?;
556+
557+
delete_data_in_hord_db(
558+
cmd.start_block,
559+
cmd.end_block,
560+
&blocks_db_rw,
561+
&inscriptions_db_conn_rw,
562+
&ctx,
563+
)?;
564+
}
565+
// Update data
566+
hord::perform_hord_db_update(
518567
cmd.start_block,
519568
cmd.end_block,
520-
&blocks_db_rw,
521-
&inscriptions_db_conn_rw,
569+
&config.get_hord_config(),
570+
&config,
571+
None,
522572
&ctx,
523-
)?;
573+
)
574+
.await?;
524575
}
525-
// Update data
526-
hord::perform_hord_db_update(
527-
cmd.start_block,
528-
cmd.end_block,
529-
&config.get_hord_config(),
530-
&config,
531-
None,
532-
&ctx,
533-
)
534-
.await?;
535576
}
536577
Command::Db(HordDbCommand::Check(cmd)) => {
537578
let config = Config::default(false, false, false, &cmd.config_path)?;

components/hord-cli/src/db/mod.rs

+61-24
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use hiro_system_kit::slog;
1515
use rand::{thread_rng, Rng};
1616

1717
use rocksdb::DB;
18-
use rusqlite::{Connection, OpenFlags, ToSql};
18+
use rusqlite::{Connection, OpenFlags, ToSql, Transaction};
1919
use std::io::Cursor;
2020
use std::io::{Read, Write};
2121
use threadpool::ThreadPool;
@@ -487,21 +487,24 @@ pub fn find_initial_inscription_transfer_data(
487487
inscription_id: &str,
488488
inscriptions_db_conn: &Connection,
489489
_ctx: &Context,
490-
) -> Result<Option<(TransactionIdentifier, usize, u64)>, String> {
490+
) -> Result<Option<TransferData>, String> {
491491
let args: &[&dyn ToSql] = &[&inscription_id.to_sql().unwrap()];
492492
let mut stmt = inscriptions_db_conn
493-
.prepare("SELECT outpoint_to_watch, offset FROM locations WHERE inscription_id = ? ORDER BY block_height ASC, tx_index ASC LIMIT 1")
493+
.prepare("SELECT outpoint_to_watch, offset, tx_index FROM locations WHERE inscription_id = ? ORDER BY block_height ASC, tx_index ASC LIMIT 1")
494494
.unwrap();
495495
let mut rows = stmt.query(args).unwrap();
496496
while let Ok(Some(row)) = rows.next() {
497497
let outpoint_to_watch: String = row.get(0).unwrap();
498-
let (transaction_identifier, output_index) = parse_outpoint_to_watch(&outpoint_to_watch);
498+
let (transaction_identifier_location, output_index) =
499+
parse_outpoint_to_watch(&outpoint_to_watch);
499500
let inscription_offset_intra_output: u64 = row.get(1).unwrap();
500-
return Ok(Some((
501-
transaction_identifier,
501+
let tx_index: u64 = row.get(2).unwrap();
502+
return Ok(Some(TransferData {
503+
transaction_identifier_location,
502504
output_index,
503505
inscription_offset_intra_output,
504-
)));
506+
tx_index,
507+
}));
505508
}
506509
Ok(None)
507510
}
@@ -534,6 +537,7 @@ pub struct TransferData {
534537
pub inscription_offset_intra_output: u64,
535538
pub transaction_identifier_location: TransactionIdentifier,
536539
pub output_index: usize,
540+
pub tx_index: u64,
537541
}
538542

539543
pub fn find_all_transfers_in_block(
@@ -551,12 +555,14 @@ pub fn find_all_transfers_in_block(
551555
let inscription_id: String = row.get(0).unwrap();
552556
let inscription_offset_intra_output: u64 = row.get(1).unwrap();
553557
let outpoint_to_watch: String = row.get(2).unwrap();
558+
let tx_index: u64 = row.get(3).unwrap();
554559
let (transaction_identifier_location, output_index) =
555560
parse_outpoint_to_watch(&outpoint_to_watch);
556561
let transfer = TransferData {
557562
inscription_offset_intra_output,
558563
transaction_identifier_location,
559564
output_index,
565+
tx_index,
560566
};
561567
results
562568
.entry(inscription_id)
@@ -650,7 +656,7 @@ pub fn find_inscription_with_id(
650656
};
651657
let mut rows = stmt.query(args).unwrap();
652658

653-
if let Some((transaction_identifier_location, output_index, inscription_offset_intra_output)) =
659+
if let Some(transfer_data) =
654660
find_initial_inscription_transfer_data(inscription_id, inscriptions_db_conn, ctx)?
655661
{
656662
while let Ok(Some(row)) = rows.next() {
@@ -665,11 +671,7 @@ pub fn find_inscription_with_id(
665671
inscription_input_index,
666672
transaction_identifier_inscription,
667673
transfers: 0,
668-
transfer_data: TransferData {
669-
inscription_offset_intra_output,
670-
transaction_identifier_location,
671-
output_index,
672-
},
674+
transfer_data,
673675
};
674676
return Ok(Some(traversal));
675677
}
@@ -681,12 +683,12 @@ pub fn find_all_inscriptions_in_block(
681683
block_height: &u64,
682684
inscriptions_db_conn: &Connection,
683685
ctx: &Context,
684-
) -> BTreeMap<u64, Vec<(TransactionIdentifier, TraversalResult)>> {
686+
) -> Vec<(TransactionIdentifier, TraversalResult)> {
685687
let args: &[&dyn ToSql] = &[&block_height.to_sql().unwrap()];
686688
let mut stmt = inscriptions_db_conn
687689
.prepare("SELECT inscription_number, ordinal_number, inscription_id FROM inscriptions where block_height = ? ORDER BY inscription_number ASC")
688690
.unwrap();
689-
let mut results: BTreeMap<u64, Vec<(TransactionIdentifier, TraversalResult)>> = BTreeMap::new();
691+
let mut results = vec![];
690692
let mut rows = stmt.query(args).unwrap();
691693

692694
let transfers_data = find_all_transfers_in_block(block_height, inscriptions_db_conn, ctx);
@@ -710,15 +712,7 @@ pub fn find_all_inscriptions_in_block(
710712
transaction_identifier_inscription: transaction_identifier_inscription.clone(),
711713
transfer_data: transfer_data,
712714
};
713-
results
714-
.entry(*block_height)
715-
.and_modify(|v| {
716-
v.push((
717-
transaction_identifier_inscription.clone(),
718-
traversal.clone(),
719-
))
720-
})
721-
.or_insert(vec![(transaction_identifier_inscription, traversal)]);
715+
results.push((transaction_identifier_inscription, traversal));
722716
}
723717
return results;
724718
}
@@ -827,6 +821,38 @@ pub fn remove_entry_from_inscriptions(
827821
}
828822
}
829823

824+
pub fn remove_entry_from_locations(
825+
inscription_id: &str,
826+
inscriptions_db_rw_conn: &Transaction,
827+
ctx: &Context,
828+
) {
829+
if let Err(e) = inscriptions_db_rw_conn.execute(
830+
"DELETE FROM locations WHERE inscription_id = ?1",
831+
rusqlite::params![&inscription_id],
832+
) {
833+
ctx.try_log(|logger| slog::error!(logger, "{}", e.to_string()));
834+
}
835+
}
836+
837+
pub fn insert_entry_in_locations(
838+
inscription_id: &str,
839+
block_height: u64,
840+
transfer_data: &TransferData,
841+
inscriptions_db_rw_conn: &Transaction,
842+
ctx: &Context,
843+
) {
844+
let outpoint_to_watch = format_outpoint_to_watch(
845+
&transfer_data.transaction_identifier_location,
846+
transfer_data.output_index,
847+
);
848+
if let Err(e) = inscriptions_db_rw_conn.execute(
849+
"INSERT INTO locations (inscription_id, outpoint_to_watch, offset, block_height, tx_index) VALUES (?1, ?2, ?3, ?4, ?5)",
850+
rusqlite::params![&inscription_id, &outpoint_to_watch, &transfer_data.inscription_offset_intra_output, &block_height, &transfer_data.tx_index],
851+
) {
852+
ctx.try_log(|logger| slog::error!(logger, "{}", e.to_string()));
853+
}
854+
}
855+
830856
pub fn delete_data_in_hord_db(
831857
start_block: u64,
832858
end_block: u64,
@@ -1090,6 +1116,14 @@ impl TraversalResult {
10901116
let sat = Sat(self.ordinal_number);
10911117
self.ordinal_number - sat.height().starting_sat().n()
10921118
}
1119+
1120+
pub fn get_inscription_id(&self) -> String {
1121+
format!(
1122+
"{}i{}",
1123+
self.transaction_identifier_inscription.get_hash_bytes_str(),
1124+
self.inscription_input_index
1125+
)
1126+
}
10931127
}
10941128

10951129
pub fn format_satpoint_to_watch(
@@ -1280,6 +1314,7 @@ pub fn retrieve_satoshi_point_using_lazy_storage(
12801314
inscription_offset_intra_output,
12811315
transaction_identifier_location: transaction_identifier.clone(),
12821316
output_index: inscription_output_index,
1317+
tx_index: 0,
12831318
},
12841319
});
12851320
}
@@ -1404,6 +1439,7 @@ pub fn retrieve_satoshi_point_using_lazy_storage(
14041439
inscription_offset_intra_output,
14051440
transaction_identifier_location: transaction_identifier.clone(),
14061441
output_index: inscription_output_index,
1442+
tx_index: 0,
14071443
},
14081444
});
14091445
}
@@ -1423,6 +1459,7 @@ pub fn retrieve_satoshi_point_using_lazy_storage(
14231459
inscription_offset_intra_output,
14241460
transaction_identifier_location: transaction_identifier.clone(),
14251461
output_index: inscription_output_index,
1462+
tx_index: 0,
14261463
},
14271464
})
14281465
}

components/hord-cli/src/scan/bitcoin.rs

+1-6
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,7 @@ pub async fn scan_bitcoin_chainstate_via_rpc_using_predicate(
128128
if let Some(ref inscriptions_db_conn) = inscriptions_db_conn {
129129
// Evaluating every single block is required for also keeping track of transfers.
130130
let local_traverals =
131-
match find_all_inscriptions_in_block(&cursor, &inscriptions_db_conn, &ctx)
132-
.remove(&cursor)
133-
{
134-
Some(entry) => entry,
135-
None => vec![],
136-
};
131+
find_all_inscriptions_in_block(&cursor, &inscriptions_db_conn, &ctx);
137132
for (transaction_identifier, traversal_result) in local_traverals.into_iter() {
138133
traversals.insert(
139134
(

0 commit comments

Comments
 (0)