Skip to content

Commit fd5da52

Browse files
author
Ludo Galabru
committed
feat: handle transfer
1 parent af280b7 commit fd5da52

File tree

4 files changed

+142
-69
lines changed

4 files changed

+142
-69
lines changed

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,8 @@ use chainhook_event_observer::chainhooks::types::{
1515
use chainhook_event_observer::hord::db::{
1616
delete_data_in_hord_db, fetch_and_cache_blocks_in_hord_db, find_last_block_inserted,
1717
find_lazy_block_at_block_height, find_watched_satpoint_for_inscription, initialize_hord_db,
18-
insert_entry_in_blocks, open_readonly_hord_db_conn, open_readonly_hord_db_conn_rocks_db,
19-
open_readwrite_hord_db_conn, open_readwrite_hord_db_conn_rocks_db,
20-
retrieve_satoshi_point_using_lazy_storage, LazyBlock,
18+
open_readonly_hord_db_conn, open_readonly_hord_db_conn_rocks_db, open_readwrite_hord_db_conn,
19+
open_readwrite_hord_db_conn_rocks_db, retrieve_satoshi_point_using_lazy_storage,
2120
};
2221
use chainhook_event_observer::hord::{
2322
new_traversals_lazy_cache, retrieve_inscribed_satoshi_points_from_block,
@@ -643,6 +642,7 @@ async fn handle_command(opts: Opts, ctx: Context) -> Result<(), String> {
643642
&block_identifier,
644643
&transaction_identifier,
645644
0,
645+
0,
646646
Arc::new(traversals_cache),
647647
&ctx,
648648
)?;

components/chainhook-event-observer/src/hord/db/mod.rs

+82-10
Original file line numberDiff line numberDiff line change
@@ -425,17 +425,21 @@ pub fn find_inscription_with_id(
425425
) -> Option<TraversalResult> {
426426
let args: &[&dyn ToSql] = &[&inscription_id.to_sql().unwrap()];
427427
let mut stmt = inscriptions_db_conn
428-
.prepare("SELECT inscription_number, ordinal_number, block_hash FROM inscriptions WHERE inscription_id = ?")
428+
.prepare("SELECT inscription_number, ordinal_number, block_hash, offset, outpoint_to_watch FROM inscriptions WHERE inscription_id = ?")
429429
.unwrap();
430430
let mut rows = stmt.query(args).unwrap();
431431
while let Ok(Some(row)) = rows.next() {
432432
let inscription_block_hash: String = row.get(2).unwrap();
433433
if block_hash.eq(&inscription_block_hash) {
434434
let inscription_number: i64 = row.get(0).unwrap();
435435
let ordinal_number: u64 = row.get(1).unwrap();
436+
let inscription_offset: u64 = row.get(3).unwrap();
437+
let outpoint_to_watch: String = row.get(4).unwrap();
436438
let traversal = TraversalResult {
437439
inscription_number,
438440
ordinal_number,
441+
inscription_offset,
442+
outpoint_to_watch,
439443
transfers: 0,
440444
};
441445
return Some(traversal);
@@ -449,7 +453,7 @@ pub fn find_all_inscriptions(
449453
) -> BTreeMap<u64, Vec<(TransactionIdentifier, TraversalResult)>> {
450454
let args: &[&dyn ToSql] = &[];
451455
let mut stmt = inscriptions_db_conn
452-
.prepare("SELECT inscription_number, ordinal_number, block_height, inscription_id FROM inscriptions ORDER BY inscription_number ASC")
456+
.prepare("SELECT inscription_number, ordinal_number, block_height, inscription_id, offset, outpoint_to_watch FROM inscriptions ORDER BY inscription_number ASC")
453457
.unwrap();
454458
let mut results: BTreeMap<u64, Vec<(TransactionIdentifier, TraversalResult)>> = BTreeMap::new();
455459
let mut rows = stmt.query(args).unwrap();
@@ -463,10 +467,14 @@ pub fn find_all_inscriptions(
463467
hash: format!("0x{}", &inscription_id[0..inscription_id.len() - 2]),
464468
}
465469
};
470+
let inscription_offset: u64 = row.get(4).unwrap();
471+
let outpoint_to_watch: String = row.get(5).unwrap();
466472
let traversal = TraversalResult {
467473
inscription_number,
468474
ordinal_number,
469475
transfers: 0,
476+
inscription_offset,
477+
outpoint_to_watch,
470478
};
471479
results
472480
.entry(block_height)
@@ -817,6 +825,8 @@ pub async fn fetch_and_cache_blocks_in_hord_db(
817825
#[derive(Clone, Debug)]
818826
pub struct TraversalResult {
819827
pub inscription_number: i64,
828+
pub inscription_offset: u64,
829+
pub outpoint_to_watch: String,
820830
pub ordinal_number: u64,
821831
pub transfers: u32,
822832
}
@@ -880,6 +890,7 @@ pub fn retrieve_satoshi_point_using_lazy_storage(
880890
blocks_db: &DB,
881891
block_identifier: &BlockIdentifier,
882892
transaction_identifier: &TransactionIdentifier,
893+
input_index: usize,
883894
inscription_number: i64,
884895
traversals_cache: Arc<
885896
DashMap<(u32, [u8; 8]), LazyBlockTransaction, BuildHasherDefault<FxHasher>>,
@@ -894,16 +905,13 @@ pub fn retrieve_satoshi_point_using_lazy_storage(
894905
block_identifier.index
895906
)
896907
});
897-
908+
let mut inscription_localized = false;
909+
let mut inscription_offset = 0;
910+
let mut inscription_output_index: usize = 0;
898911
let mut ordinal_offset = 0;
899912
let mut ordinal_block_number = block_identifier.index as u32;
900-
let txid = {
901-
let bytes = hex::decode(&transaction_identifier.hash[2..]).unwrap();
902-
[
903-
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
904-
]
905-
};
906-
let mut tx_cursor = (txid, 0);
913+
let txid = transaction_identifier.get_8_hash_bytes();
914+
let mut tx_cursor = (txid, input_index);
907915
let mut hops: u32 = 0;
908916
loop {
909917
hops += 1;
@@ -916,6 +924,31 @@ pub fn retrieve_satoshi_point_using_lazy_storage(
916924

917925
if let Some(cached_tx) = traversals_cache.get(&(ordinal_block_number, tx_cursor.0)) {
918926
let tx = cached_tx.value();
927+
928+
if !inscription_localized {
929+
inscription_localized = true;
930+
let mut sats_ranges = vec![];
931+
let mut bound = 0;
932+
for output_value in tx.outputs.iter() {
933+
sats_ranges.push((bound, bound + output_value));
934+
bound += output_value;
935+
}
936+
let mut input_offset = 0;
937+
for (i, input) in tx.inputs.iter().enumerate() {
938+
if i == input_index {
939+
break;
940+
}
941+
input_offset += input.txin_value;
942+
}
943+
944+
for (i, (min, max)) in sats_ranges.into_iter().enumerate() {
945+
if input_offset >= min && input_offset < max {
946+
inscription_output_index = i;
947+
inscription_offset = input_offset - min;
948+
}
949+
}
950+
}
951+
919952
let mut next_found_in_cache = false;
920953
let mut sats_out = 0;
921954
for (index, output_value) in tx.outputs.iter().enumerate() {
@@ -955,6 +988,11 @@ pub fn retrieve_satoshi_point_using_lazy_storage(
955988
inscription_number: 0,
956989
ordinal_number: 0,
957990
transfers: 0,
991+
inscription_offset,
992+
outpoint_to_watch: format_outpoint_to_watch(
993+
&transaction_identifier,
994+
inscription_output_index,
995+
),
958996
});
959997
}
960998
}
@@ -1022,6 +1060,30 @@ pub fn retrieve_satoshi_point_using_lazy_storage(
10221060
None => unreachable!(),
10231061
};
10241062

1063+
if !inscription_localized {
1064+
inscription_localized = true;
1065+
let mut sats_ranges = vec![];
1066+
let mut bound = 0;
1067+
for output_value in lazy_tx.outputs.iter() {
1068+
sats_ranges.push((bound, bound + output_value));
1069+
bound += output_value;
1070+
}
1071+
let mut input_offset = 0;
1072+
for (i, input) in lazy_tx.inputs.iter().enumerate() {
1073+
if i == input_index {
1074+
break;
1075+
}
1076+
input_offset += input.txin_value;
1077+
}
1078+
1079+
for (i, (min, max)) in sats_ranges.into_iter().enumerate() {
1080+
if input_offset >= min && input_offset < max {
1081+
inscription_output_index = i;
1082+
inscription_offset = input_offset - min;
1083+
}
1084+
}
1085+
}
1086+
10251087
let mut sats_out = 0;
10261088
for (index, output_value) in lazy_tx.outputs.iter().enumerate() {
10271089
if index == tx_cursor.1 {
@@ -1056,6 +1118,11 @@ pub fn retrieve_satoshi_point_using_lazy_storage(
10561118
inscription_number: 0,
10571119
ordinal_number: 0,
10581120
transfers: 0,
1121+
inscription_offset,
1122+
outpoint_to_watch: format_outpoint_to_watch(
1123+
&transaction_identifier,
1124+
inscription_output_index,
1125+
),
10591126
});
10601127
}
10611128
}
@@ -1068,6 +1135,11 @@ pub fn retrieve_satoshi_point_using_lazy_storage(
10681135
inscription_number,
10691136
ordinal_number,
10701137
transfers: hops,
1138+
inscription_offset,
1139+
outpoint_to_watch: format_outpoint_to_watch(
1140+
&transaction_identifier,
1141+
inscription_output_index,
1142+
),
10711143
})
10721144
}
10731145

components/chainhook-event-observer/src/hord/mod.rs

+56-56
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use std::sync::mpsc::channel;
2222
use std::sync::Arc;
2323
use threadpool::ThreadPool;
2424

25+
use crate::hord::db::format_outpoint_to_watch;
2526
use crate::indexer::bitcoin::BitcoinTransactionFullBreakdown;
2627
use crate::{
2728
hord::{
@@ -87,48 +88,27 @@ pub fn parse_ordinal_operations(
8788
None
8889
};
8990

91+
let payload = OrdinalInscriptionRevealData {
92+
content_type: inscription.content_type().unwrap_or("unknown").to_string(),
93+
content_bytes: format!("0x{}", hex::encode(&inscription_content_bytes)),
94+
content_length: inscription_content_bytes.len(),
95+
inscription_id: inscription_id.to_string(),
96+
inscriber_address,
97+
inscription_output_value,
98+
inscription_fee: 0,
99+
inscription_input_index: input_index,
100+
inscription_number: 0,
101+
ordinal_number: 0,
102+
ordinal_block_height: 0,
103+
ordinal_offset: 0,
104+
transfers_pre_inscription: 0,
105+
satpoint_post_inscription: format!("{}:0:0", tx.txid.clone()),
106+
};
107+
90108
if input_index == 0 {
91-
operations.push(OrdinalOperation::InscriptionRevealed(
92-
OrdinalInscriptionRevealData {
93-
content_type: inscription
94-
.content_type()
95-
.unwrap_or("unknown")
96-
.to_string(),
97-
content_bytes: format!("0x{}", hex::encode(&inscription_content_bytes)),
98-
content_length: inscription_content_bytes.len(),
99-
inscription_id: inscription_id.to_string(),
100-
inscriber_address,
101-
inscription_output_value,
102-
inscription_fee: 0,
103-
inscription_number: 0,
104-
ordinal_number: 0,
105-
ordinal_block_height: 0,
106-
ordinal_offset: 0,
107-
transfers_pre_inscription: 0,
108-
satpoint_post_inscription: format!("{}:0:0", tx.txid.clone()),
109-
},
110-
))
109+
operations.push(OrdinalOperation::InscriptionRevealed(payload));
111110
} else {
112-
operations.push(OrdinalOperation::CursedInscriptionRevealed(
113-
OrdinalInscriptionRevealData {
114-
content_type: inscription
115-
.content_type()
116-
.unwrap_or("unknown")
117-
.to_string(),
118-
content_bytes: format!("0x{}", hex::encode(&inscription_content_bytes)),
119-
content_length: inscription_content_bytes.len(),
120-
inscription_id: inscription_id.to_string(),
121-
inscriber_address,
122-
inscription_output_value,
123-
inscription_fee: 0,
124-
inscription_number: 0,
125-
ordinal_number: 0,
126-
ordinal_block_height: 0,
127-
ordinal_offset: 0,
128-
transfers_pre_inscription: 0,
129-
satpoint_post_inscription: format!("{}:0:0", tx.txid.clone()),
130-
},
131-
))
111+
operations.push(OrdinalOperation::CursedInscriptionRevealed(payload));
132112
}
133113
}
134114
}
@@ -221,23 +201,38 @@ pub fn retrieve_inscribed_satoshi_points_from_block(
221201
for tx in block.transactions.iter().skip(1) {
222202
// Have a new inscription been revealed, if so, are looking at a re-inscription
223203
for ordinal_event in tx.metadata.ordinal_operations.iter() {
224-
if let OrdinalOperation::InscriptionRevealed(inscription_data) = ordinal_event {
225-
if let Some(inscriptions_db_conn) = inscriptions_db_conn {
226-
if let Some(traversal) = find_inscription_with_id(
227-
&inscription_data.inscription_id,
228-
&block.block_identifier.hash,
229-
inscriptions_db_conn,
230-
ctx,
231-
) {
232-
traversals.insert(tx.transaction_identifier.clone(), traversal);
233-
} else {
234-
// Enqueue for traversals
235-
transactions_ids.push(tx.transaction_identifier.clone());
236-
}
204+
let (inscription_data, _is_cursed) = match ordinal_event {
205+
OrdinalOperation::InscriptionRevealed(inscription_data) => {
206+
(inscription_data, false)
207+
}
208+
OrdinalOperation::CursedInscriptionRevealed(inscription_data) => {
209+
(inscription_data, false)
210+
}
211+
OrdinalOperation::InscriptionTransferred(_) => {
212+
continue;
213+
}
214+
};
215+
if let Some(inscriptions_db_conn) = inscriptions_db_conn {
216+
if let Some(traversal) = find_inscription_with_id(
217+
&inscription_data.inscription_id,
218+
&block.block_identifier.hash,
219+
inscriptions_db_conn,
220+
ctx,
221+
) {
222+
traversals.insert(tx.transaction_identifier.clone(), traversal);
237223
} else {
238224
// Enqueue for traversals
239-
transactions_ids.push(tx.transaction_identifier.clone());
225+
transactions_ids.push((
226+
tx.transaction_identifier.clone(),
227+
inscription_data.inscription_input_index,
228+
));
240229
}
230+
} else {
231+
// Enqueue for traversals
232+
transactions_ids.push((
233+
tx.transaction_identifier.clone(),
234+
inscription_data.inscription_input_index,
235+
));
241236
}
242237
}
243238
}
@@ -249,7 +244,7 @@ pub fn retrieve_inscribed_satoshi_points_from_block(
249244

250245
let mut rng = thread_rng();
251246
transactions_ids.shuffle(&mut rng);
252-
for transaction_id in transactions_ids.into_iter() {
247+
for (transaction_id, input_index) in transactions_ids.into_iter() {
253248
let moved_traversal_tx = traversal_tx.clone();
254249
let moved_ctx = ctx.clone();
255250
let block_identifier = block.block_identifier.clone();
@@ -262,6 +257,7 @@ pub fn retrieve_inscribed_satoshi_points_from_block(
262257
&blocks_db,
263258
&block_identifier,
264259
&transaction_id,
260+
input_index,
265261
0,
266262
local_cache,
267263
&moved_ctx,
@@ -466,6 +462,10 @@ pub fn update_storage_and_augment_bitcoin_block_with_inscription_reveal_data(
466462
inscription.inscription_number = traversal.inscription_number;
467463
inscription.transfers_pre_inscription = traversal.transfers;
468464
inscription.inscription_fee = new_tx.metadata.fee;
465+
inscription.satpoint_post_inscription = format!(
466+
"{}:{}",
467+
traversal.outpoint_to_watch, traversal.inscription_offset
468+
);
469469

470470
match storage {
471471
Storage::Sqlite(rw_hord_db_conn) => {
@@ -644,7 +644,7 @@ pub fn update_storage_and_augment_bitcoin_block_with_inscription_transfer_data(
644644
) = match post_transfer_output {
645645
Some(index) => {
646646
let outpoint =
647-
format!("{}:{}", &new_tx.transaction_identifier.hash[2..], index);
647+
format_outpoint_to_watch(&new_tx.transaction_identifier, index);
648648
let offset = (sats_in_offset + watched_satpoint.offset) - sats_out_offset;
649649
let script_pub_key_hex =
650650
new_tx.metadata.outputs[index].get_script_pubkey_hex();

components/chainhook-types-rs/src/rosetta.rs

+1
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@ pub struct OrdinalInscriptionRevealData {
327327
pub inscription_fee: u64,
328328
pub inscription_output_value: u64,
329329
pub inscription_id: String,
330+
pub inscription_input_index: usize,
330331
pub inscriber_address: Option<String>,
331332
pub ordinal_number: u64,
332333
pub ordinal_block_height: u64,

0 commit comments

Comments
 (0)