Skip to content

Commit 6815878

Browse files
authored
fix: display unbound inscription satpoints as all zeros with unbound sequence as offset (#445)
* feat: start indexing brc20 balance history * fix: api support * style: revert * remove extra * track unbound seq * test sequence * remove old coinbase calculations
1 parent 41438ac commit 6815878

File tree

12 files changed

+247
-105
lines changed

12 files changed

+247
-105
lines changed

components/chainhook-sdk/src/indexer/bitcoin/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use bitcoincore_rpc::jsonrpc::error::RpcError;
99
use bitcoincore_rpc_json::GetRawTransactionResultVoutScriptPubKey;
1010
use chainhook_types::bitcoin::{OutPoint, TxIn, TxOut};
1111
use chainhook_types::{
12-
BitcoinBlockData, BitcoinBlockMetadata, BitcoinBlockSignaling, BitcoinNetwork,
12+
BitcoinBlockData, BitcoinBlockMetadata, BitcoinNetwork,
1313
BitcoinTransactionData,BitcoinTransactionMetadata, BlockHeader, BlockIdentifier,
1414
TransactionIdentifier,
1515
};

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

+1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ pub struct OrdinalInscriptionRevealData {
6363
pub satpoint_post_inscription: String,
6464
pub curse_type: Option<OrdinalInscriptionCurseType>,
6565
pub charms: u16,
66+
pub unbound_sequence: Option<i64>,
6667
}
6768

6869
impl OrdinalInscriptionNumber {

components/ordhook-core/src/core/meta_protocols/brc20/test_utils.rs

+1
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ impl Brc20RevealBuilder {
8989
"9bb2314d666ae0b1db8161cb373fcc1381681f71445c4e0335aa80ea9c37fcdd:0:0".to_string(),
9090
curse_type: None,
9191
charms: 0,
92+
unbound_sequence: None,
9293
}
9394
}
9495
}

components/ordhook-core/src/core/protocol/inscription_parsing.rs

+1
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ pub fn parse_inscriptions_from_witness(
103103
satpoint_post_inscription: format!(""),
104104
curse_type,
105105
charms: 0,
106+
unbound_sequence: None,
106107
};
107108
inscriptions.push((reveal_data, envelope.payload));
108109
}

components/ordhook-core/src/core/protocol/inscription_sequencing.rs

+156-25
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,15 @@ use dashmap::DashMap;
1616
use deadpool_postgres::Transaction;
1717
use fxhash::FxHasher;
1818

19+
use crate::core::protocol::satoshi_tracking::UNBOUND_INSCRIPTION_SATPOINT;
1920
use crate::{
2021
config::Config,
2122
core::resolve_absolute_pointer,
2223
db::{self, cursor::TransactionBytesCursor, ordinals_pg},
2324
try_debug, try_error, try_info,
2425
utils::format_inscription_id,
2526
};
26-
use ord::{charm::Charm, height::Height, sat::Sat};
27+
use ord::{charm::Charm, sat::Sat};
2728

2829
use std::sync::mpsc::channel;
2930

@@ -401,12 +402,9 @@ pub async fn update_block_inscriptions_with_consensus_sequence_data(
401402
// Check if we've previously inscribed over any satoshi being inscribed to in this new block. This would be a reinscription.
402403
let mut reinscriptions_data =
403404
ordinals_pg::get_reinscriptions_for_block(inscriptions_data, db_tx).await?;
404-
// Keep a reference of inscribed satoshis that fall outside of this block's total sats. These would be unbound inscriptions.
405+
// Keep a reference of inscribed satoshis that will go towards miner fees. These would be unbound inscriptions.
405406
let mut sat_overflows = VecDeque::new();
406407
let network = get_bitcoin_network(&block.metadata.network);
407-
let coinbase_subsidy = Height(block.block_identifier.index as u32).subsidy();
408-
let coinbase_tx = &block.transactions[0].clone();
409-
let mut cumulated_fees = 0u64;
410408

411409
for (tx_index, tx) in block.transactions.iter_mut().enumerate() {
412410
update_tx_inscriptions_with_consensus_sequence_data(
@@ -416,9 +414,6 @@ pub async fn update_block_inscriptions_with_consensus_sequence_data(
416414
sequence_cursor,
417415
&network,
418416
inscriptions_data,
419-
coinbase_tx,
420-
coinbase_subsidy,
421-
&mut cumulated_fees,
422417
&mut sat_overflows,
423418
&mut reinscriptions_data,
424419
db_tx,
@@ -427,19 +422,28 @@ pub async fn update_block_inscriptions_with_consensus_sequence_data(
427422
.await?;
428423
}
429424

425+
// Assign inscription numbers to remaining unbound inscriptions.
430426
while let Some((tx_index, op_index)) = sat_overflows.pop_front() {
431427
let OrdinalOperation::InscriptionRevealed(ref mut inscription_data) =
432428
block.transactions[tx_index].metadata.ordinal_operations[op_index]
433429
else {
434430
continue;
435431
};
432+
436433
let is_cursed = inscription_data.curse_type.is_some();
437434
let inscription_number = sequence_cursor
438435
.pick_next(is_cursed, block.block_identifier.index, &network, db_tx)
439436
.await?;
440437
inscription_data.inscription_number = inscription_number;
441-
442438
sequence_cursor.increment(is_cursed, db_tx).await?;
439+
440+
// Also assign an unbound sequence number and set outpoint to all zeros, just like `ord`.
441+
let unbound_sequence = sequence_cursor.increment_unbound(db_tx).await?;
442+
inscription_data.satpoint_post_inscription =
443+
format!("{UNBOUND_INSCRIPTION_SATPOINT}:{unbound_sequence}");
444+
inscription_data.ordinal_offset = unbound_sequence as u64;
445+
inscription_data.unbound_sequence = Some(unbound_sequence);
446+
443447
try_info!(
444448
ctx,
445449
"Unbound inscription {} (#{}) detected on Satoshi {} (block #{}, {} transfers)",
@@ -464,9 +468,6 @@ async fn update_tx_inscriptions_with_consensus_sequence_data(
464468
sequence_cursor: &mut SequenceCursor,
465469
network: &Network,
466470
inscriptions_data: &mut BTreeMap<(TransactionIdentifier, usize, u64), TraversalResult>,
467-
coinbase_tx: &BitcoinTransactionData,
468-
coinbase_subsidy: u64,
469-
cumulated_fees: &mut u64,
470471
sats_overflows: &mut VecDeque<(usize, usize)>,
471472
reinscriptions_data: &mut HashMap<u64, String>,
472473
db_tx: &Transaction<'_>,
@@ -559,16 +560,8 @@ async fn update_tx_inscriptions_with_consensus_sequence_data(
559560
}
560561
}
561562

562-
let (destination, satpoint_post_transfer, output_value) = compute_satpoint_post_transfer(
563-
&&*tx,
564-
input_index,
565-
relative_offset,
566-
network,
567-
coinbase_tx,
568-
coinbase_subsidy,
569-
cumulated_fees,
570-
ctx,
571-
);
563+
let (destination, satpoint_post_transfer, output_value) =
564+
compute_satpoint_post_transfer(&&*tx, input_index, relative_offset, network, ctx);
572565
inscription.satpoint_post_inscription = satpoint_post_transfer;
573566
inscription_subindex += 1;
574567

@@ -637,11 +630,150 @@ mod test {
637630
protocol::{satoshi_numbering::TraversalResult, sequence_cursor::SequenceCursor},
638631
test_builders::{TestBlockBuilder, TestTransactionBuilder},
639632
},
640-
db::{ordinals_pg, pg_reset_db, pg_test_connection, pg_test_connection_pool},
633+
db::{
634+
ordinals_pg::{self, insert_block},
635+
pg_reset_db, pg_test_connection, pg_test_connection_pool,
636+
},
641637
};
642638

643639
use super::update_block_inscriptions_with_consensus_sequence_data;
644640

641+
#[test_case(None => Ok(("0000000000000000000000000000000000000000000000000000000000000000:0:0".into(), Some(0))); "first unbound sequence")]
642+
#[test_case(Some(230) => Ok(("0000000000000000000000000000000000000000000000000000000000000000:0:231".into(), Some(231))); "next unbound sequence")]
643+
#[tokio::test]
644+
async fn unbound_inscription_sequence(
645+
curr_sequence: Option<i64>,
646+
) -> Result<(String, Option<i64>), String> {
647+
let ctx = Context::empty();
648+
let mut sequence_cursor = SequenceCursor::new();
649+
let mut cache_l1 = BTreeMap::new();
650+
let tx_id = TransactionIdentifier {
651+
hash: "0xb4722ad74e7092a194e367f2ec0609994ef7a006db4f9b9d055b46cfb6514e06".into(),
652+
};
653+
let input_index = 1;
654+
655+
cache_l1.insert(
656+
(tx_id.clone(), input_index, 0),
657+
TraversalResult {
658+
inscription_number: OrdinalInscriptionNumber {
659+
classic: 0,
660+
jubilee: 0,
661+
},
662+
inscription_input_index: input_index,
663+
transaction_identifier_inscription: tx_id.clone(),
664+
ordinal_number: 817263817263,
665+
transfers: 0,
666+
},
667+
);
668+
let mut pg_client = pg_test_connection().await;
669+
ordinals_pg::migrate(&mut pg_client).await?;
670+
let result = {
671+
let mut ord_client = pg_pool_client(&pg_test_connection_pool()).await?;
672+
let client = pg_begin(&mut ord_client).await?;
673+
674+
if let Some(curr_sequence) = curr_sequence {
675+
// Simulate previous unbound sequence
676+
let mut tx = TestTransactionBuilder::new_with_operation().build();
677+
if let OrdinalOperation::InscriptionRevealed(data) =
678+
&mut tx.metadata.ordinal_operations[0]
679+
{
680+
data.unbound_sequence = Some(curr_sequence);
681+
};
682+
let block = TestBlockBuilder::new().transactions(vec![tx]).build();
683+
insert_block(&block, &client).await?;
684+
}
685+
686+
// Insert new block
687+
let mut block = TestBlockBuilder::new()
688+
.height(878878)
689+
// Coinbase
690+
.add_transaction(TestTransactionBuilder::new().build())
691+
.add_transaction(
692+
TestTransactionBuilder::new()
693+
.hash(tx_id.hash.clone())
694+
// Normal input
695+
.add_input(TxIn {
696+
previous_output: OutPoint {
697+
txid: TransactionIdentifier { hash: "0xf181aa98f2572879bd02278c72c83c7eaac2db82af713d1d239fc41859b2a26e".into() },
698+
vout: 0,
699+
value: 8000,
700+
block_height: 884200,
701+
},
702+
script_sig: "0x00".into(),
703+
sequence: 0,
704+
witness: vec!["0x00".into()],
705+
})
706+
// Goes to fees
707+
.add_input(TxIn {
708+
previous_output: OutPoint {
709+
txid: TransactionIdentifier { hash: "0xf181aa98f2572879bd02278c72c83c7eaac2db82af713d1d239fc41859b2a26e".into() },
710+
vout: 1,
711+
value: 250,
712+
block_height: 884200,
713+
},
714+
script_sig: "0x00".into(),
715+
sequence: 0,
716+
witness: vec!["0x00".into()],
717+
})
718+
.add_output(TxOut { value: 8000, script_pubkey: "0x5120694b38ea24908e86a857279105c376a82cd1556f51655abb2ebef398b57daa8b".into() })
719+
.add_ordinal_operation(OrdinalOperation::InscriptionRevealed(
720+
OrdinalInscriptionRevealData {
721+
content_bytes: "0x101010".into(),
722+
content_type: "text/plain".into(),
723+
content_length: 3,
724+
inscription_number: OrdinalInscriptionNumber {
725+
classic: 0,
726+
jubilee: 0,
727+
},
728+
inscription_fee: 0,
729+
inscription_output_value: 0,
730+
inscription_id: "".into(),
731+
inscription_input_index: input_index,
732+
inscription_pointer: Some(8000),
733+
inscriber_address: Some("bc1pd99n363yjz8gd2zhy7gstsmk4qkdz4t029j44wewhmee3dta429sm5xqrd".into()),
734+
delegate: None,
735+
metaprotocol: None,
736+
metadata: None,
737+
parents: vec![],
738+
ordinal_number: 0,
739+
ordinal_block_height: 0,
740+
ordinal_offset: 0,
741+
tx_index: 1,
742+
transfers_pre_inscription: 0,
743+
satpoint_post_inscription: "".into(),
744+
curse_type: Some(OrdinalInscriptionCurseType::DuplicateField),
745+
charms: 0,
746+
unbound_sequence: None,
747+
},
748+
))
749+
.build(),
750+
)
751+
.build();
752+
753+
update_block_inscriptions_with_consensus_sequence_data(
754+
&mut block,
755+
&mut sequence_cursor,
756+
&mut cache_l1,
757+
&client,
758+
&ctx,
759+
)
760+
.await?;
761+
762+
let result = &block.transactions[1].metadata.ordinal_operations[0];
763+
let data = match result {
764+
OrdinalOperation::InscriptionRevealed(data) => data,
765+
_ => unreachable!(),
766+
};
767+
Ok((
768+
data.satpoint_post_inscription.clone(),
769+
data.unbound_sequence,
770+
))
771+
};
772+
pg_reset_db(&mut pg_client).await?;
773+
774+
result
775+
}
776+
645777
#[test_case((884207, false, 1262349832364434, "0x5120694b38ea24908e86a857279105c376a82cd1556f51655abb2ebef398b57daa8b".into()) => Ok(vec![]); "common sat")]
646778
#[test_case((884207, false, 0, "0x5120694b38ea24908e86a857279105c376a82cd1556f51655abb2ebef398b57daa8b".into()) => Ok(vec![Charm::Coin, Charm::Mythic, Charm::Palindrome]); "mythic sat")]
647779
#[test_case((884207, false, 1050000000000000, "0x5120694b38ea24908e86a857279105c376a82cd1556f51655abb2ebef398b57daa8b".into()) => Ok(vec![Charm::Coin, Charm::Epic]); "epic sat")]
@@ -727,6 +859,7 @@ mod test {
727859
satpoint_post_inscription: "".into(),
728860
curse_type: if cursed { Some(OrdinalInscriptionCurseType::Generic) } else { None },
729861
charms: 0,
862+
unbound_sequence: None,
730863
},
731864
))
732865
.build(),
@@ -743,12 +876,10 @@ mod test {
743876
.await?;
744877

745878
let result = &block.transactions[1].metadata.ordinal_operations[0];
746-
// println!("{:?}", result);
747879
let charms = match result {
748880
OrdinalOperation::InscriptionRevealed(data) => data.charms,
749881
_ => unreachable!(),
750882
};
751-
// println!("{:?}", Charm::charms(charms));
752883
Ok(Charm::charms(charms))
753884
};
754885
pg_reset_db(&mut pg_client).await?;

0 commit comments

Comments
 (0)