Skip to content

Commit

Permalink
fix: display unbound inscription satpoints as all zeros with unbound …
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
rafaelcr authored Feb 21, 2025
1 parent 41438ac commit 6815878
Show file tree
Hide file tree
Showing 12 changed files with 247 additions and 105 deletions.
2 changes: 1 addition & 1 deletion components/chainhook-sdk/src/indexer/bitcoin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use bitcoincore_rpc::jsonrpc::error::RpcError;
use bitcoincore_rpc_json::GetRawTransactionResultVoutScriptPubKey;
use chainhook_types::bitcoin::{OutPoint, TxIn, TxOut};
use chainhook_types::{
BitcoinBlockData, BitcoinBlockMetadata, BitcoinBlockSignaling, BitcoinNetwork,
BitcoinBlockData, BitcoinBlockMetadata, BitcoinNetwork,
BitcoinTransactionData,BitcoinTransactionMetadata, BlockHeader, BlockIdentifier,
TransactionIdentifier,
};
Expand Down
1 change: 1 addition & 0 deletions components/chainhook-types-rs/src/ordinals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ pub struct OrdinalInscriptionRevealData {
pub satpoint_post_inscription: String,
pub curse_type: Option<OrdinalInscriptionCurseType>,
pub charms: u16,
pub unbound_sequence: Option<i64>,
}

impl OrdinalInscriptionNumber {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ impl Brc20RevealBuilder {
"9bb2314d666ae0b1db8161cb373fcc1381681f71445c4e0335aa80ea9c37fcdd:0:0".to_string(),
curse_type: None,
charms: 0,
unbound_sequence: None,
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ pub fn parse_inscriptions_from_witness(
satpoint_post_inscription: format!(""),
curse_type,
charms: 0,
unbound_sequence: None,
};
inscriptions.push((reveal_data, envelope.payload));
}
Expand Down
181 changes: 156 additions & 25 deletions components/ordhook-core/src/core/protocol/inscription_sequencing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ use dashmap::DashMap;
use deadpool_postgres::Transaction;
use fxhash::FxHasher;

use crate::core::protocol::satoshi_tracking::UNBOUND_INSCRIPTION_SATPOINT;
use crate::{
config::Config,
core::resolve_absolute_pointer,
db::{self, cursor::TransactionBytesCursor, ordinals_pg},
try_debug, try_error, try_info,
utils::format_inscription_id,
};
use ord::{charm::Charm, height::Height, sat::Sat};
use ord::{charm::Charm, sat::Sat};

use std::sync::mpsc::channel;

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

for (tx_index, tx) in block.transactions.iter_mut().enumerate() {
update_tx_inscriptions_with_consensus_sequence_data(
Expand All @@ -416,9 +414,6 @@ pub async fn update_block_inscriptions_with_consensus_sequence_data(
sequence_cursor,
&network,
inscriptions_data,
coinbase_tx,
coinbase_subsidy,
&mut cumulated_fees,
&mut sat_overflows,
&mut reinscriptions_data,
db_tx,
Expand All @@ -427,19 +422,28 @@ pub async fn update_block_inscriptions_with_consensus_sequence_data(
.await?;
}

// Assign inscription numbers to remaining unbound inscriptions.
while let Some((tx_index, op_index)) = sat_overflows.pop_front() {
let OrdinalOperation::InscriptionRevealed(ref mut inscription_data) =
block.transactions[tx_index].metadata.ordinal_operations[op_index]
else {
continue;
};

let is_cursed = inscription_data.curse_type.is_some();
let inscription_number = sequence_cursor
.pick_next(is_cursed, block.block_identifier.index, &network, db_tx)
.await?;
inscription_data.inscription_number = inscription_number;

sequence_cursor.increment(is_cursed, db_tx).await?;

// Also assign an unbound sequence number and set outpoint to all zeros, just like `ord`.
let unbound_sequence = sequence_cursor.increment_unbound(db_tx).await?;
inscription_data.satpoint_post_inscription =
format!("{UNBOUND_INSCRIPTION_SATPOINT}:{unbound_sequence}");
inscription_data.ordinal_offset = unbound_sequence as u64;
inscription_data.unbound_sequence = Some(unbound_sequence);

try_info!(
ctx,
"Unbound inscription {} (#{}) detected on Satoshi {} (block #{}, {} transfers)",
Expand All @@ -464,9 +468,6 @@ async fn update_tx_inscriptions_with_consensus_sequence_data(
sequence_cursor: &mut SequenceCursor,
network: &Network,
inscriptions_data: &mut BTreeMap<(TransactionIdentifier, usize, u64), TraversalResult>,
coinbase_tx: &BitcoinTransactionData,
coinbase_subsidy: u64,
cumulated_fees: &mut u64,
sats_overflows: &mut VecDeque<(usize, usize)>,
reinscriptions_data: &mut HashMap<u64, String>,
db_tx: &Transaction<'_>,
Expand Down Expand Up @@ -559,16 +560,8 @@ async fn update_tx_inscriptions_with_consensus_sequence_data(
}
}

let (destination, satpoint_post_transfer, output_value) = compute_satpoint_post_transfer(
&&*tx,
input_index,
relative_offset,
network,
coinbase_tx,
coinbase_subsidy,
cumulated_fees,
ctx,
);
let (destination, satpoint_post_transfer, output_value) =
compute_satpoint_post_transfer(&&*tx, input_index, relative_offset, network, ctx);
inscription.satpoint_post_inscription = satpoint_post_transfer;
inscription_subindex += 1;

Expand Down Expand Up @@ -637,11 +630,150 @@ mod test {
protocol::{satoshi_numbering::TraversalResult, sequence_cursor::SequenceCursor},
test_builders::{TestBlockBuilder, TestTransactionBuilder},
},
db::{ordinals_pg, pg_reset_db, pg_test_connection, pg_test_connection_pool},
db::{
ordinals_pg::{self, insert_block},
pg_reset_db, pg_test_connection, pg_test_connection_pool,
},
};

use super::update_block_inscriptions_with_consensus_sequence_data;

#[test_case(None => Ok(("0000000000000000000000000000000000000000000000000000000000000000:0:0".into(), Some(0))); "first unbound sequence")]
#[test_case(Some(230) => Ok(("0000000000000000000000000000000000000000000000000000000000000000:0:231".into(), Some(231))); "next unbound sequence")]
#[tokio::test]
async fn unbound_inscription_sequence(
curr_sequence: Option<i64>,
) -> Result<(String, Option<i64>), String> {
let ctx = Context::empty();
let mut sequence_cursor = SequenceCursor::new();
let mut cache_l1 = BTreeMap::new();
let tx_id = TransactionIdentifier {
hash: "0xb4722ad74e7092a194e367f2ec0609994ef7a006db4f9b9d055b46cfb6514e06".into(),
};
let input_index = 1;

cache_l1.insert(
(tx_id.clone(), input_index, 0),
TraversalResult {
inscription_number: OrdinalInscriptionNumber {
classic: 0,
jubilee: 0,
},
inscription_input_index: input_index,
transaction_identifier_inscription: tx_id.clone(),
ordinal_number: 817263817263,
transfers: 0,
},
);
let mut pg_client = pg_test_connection().await;
ordinals_pg::migrate(&mut pg_client).await?;
let result = {
let mut ord_client = pg_pool_client(&pg_test_connection_pool()).await?;
let client = pg_begin(&mut ord_client).await?;

if let Some(curr_sequence) = curr_sequence {
// Simulate previous unbound sequence
let mut tx = TestTransactionBuilder::new_with_operation().build();
if let OrdinalOperation::InscriptionRevealed(data) =
&mut tx.metadata.ordinal_operations[0]
{
data.unbound_sequence = Some(curr_sequence);
};
let block = TestBlockBuilder::new().transactions(vec![tx]).build();
insert_block(&block, &client).await?;
}

// Insert new block
let mut block = TestBlockBuilder::new()
.height(878878)
// Coinbase
.add_transaction(TestTransactionBuilder::new().build())
.add_transaction(
TestTransactionBuilder::new()
.hash(tx_id.hash.clone())
// Normal input
.add_input(TxIn {
previous_output: OutPoint {
txid: TransactionIdentifier { hash: "0xf181aa98f2572879bd02278c72c83c7eaac2db82af713d1d239fc41859b2a26e".into() },
vout: 0,
value: 8000,
block_height: 884200,
},
script_sig: "0x00".into(),
sequence: 0,
witness: vec!["0x00".into()],
})
// Goes to fees
.add_input(TxIn {
previous_output: OutPoint {
txid: TransactionIdentifier { hash: "0xf181aa98f2572879bd02278c72c83c7eaac2db82af713d1d239fc41859b2a26e".into() },
vout: 1,
value: 250,
block_height: 884200,
},
script_sig: "0x00".into(),
sequence: 0,
witness: vec!["0x00".into()],
})
.add_output(TxOut { value: 8000, script_pubkey: "0x5120694b38ea24908e86a857279105c376a82cd1556f51655abb2ebef398b57daa8b".into() })
.add_ordinal_operation(OrdinalOperation::InscriptionRevealed(
OrdinalInscriptionRevealData {
content_bytes: "0x101010".into(),
content_type: "text/plain".into(),
content_length: 3,
inscription_number: OrdinalInscriptionNumber {
classic: 0,
jubilee: 0,
},
inscription_fee: 0,
inscription_output_value: 0,
inscription_id: "".into(),
inscription_input_index: input_index,
inscription_pointer: Some(8000),
inscriber_address: Some("bc1pd99n363yjz8gd2zhy7gstsmk4qkdz4t029j44wewhmee3dta429sm5xqrd".into()),
delegate: None,
metaprotocol: None,
metadata: None,
parents: vec![],
ordinal_number: 0,
ordinal_block_height: 0,
ordinal_offset: 0,
tx_index: 1,
transfers_pre_inscription: 0,
satpoint_post_inscription: "".into(),
curse_type: Some(OrdinalInscriptionCurseType::DuplicateField),
charms: 0,
unbound_sequence: None,
},
))
.build(),
)
.build();

update_block_inscriptions_with_consensus_sequence_data(
&mut block,
&mut sequence_cursor,
&mut cache_l1,
&client,
&ctx,
)
.await?;

let result = &block.transactions[1].metadata.ordinal_operations[0];
let data = match result {
OrdinalOperation::InscriptionRevealed(data) => data,
_ => unreachable!(),
};
Ok((
data.satpoint_post_inscription.clone(),
data.unbound_sequence,
))
};
pg_reset_db(&mut pg_client).await?;

result
}

#[test_case((884207, false, 1262349832364434, "0x5120694b38ea24908e86a857279105c376a82cd1556f51655abb2ebef398b57daa8b".into()) => Ok(vec![]); "common sat")]
#[test_case((884207, false, 0, "0x5120694b38ea24908e86a857279105c376a82cd1556f51655abb2ebef398b57daa8b".into()) => Ok(vec![Charm::Coin, Charm::Mythic, Charm::Palindrome]); "mythic sat")]
#[test_case((884207, false, 1050000000000000, "0x5120694b38ea24908e86a857279105c376a82cd1556f51655abb2ebef398b57daa8b".into()) => Ok(vec![Charm::Coin, Charm::Epic]); "epic sat")]
Expand Down Expand Up @@ -727,6 +859,7 @@ mod test {
satpoint_post_inscription: "".into(),
curse_type: if cursed { Some(OrdinalInscriptionCurseType::Generic) } else { None },
charms: 0,
unbound_sequence: None,
},
))
.build(),
Expand All @@ -743,12 +876,10 @@ mod test {
.await?;

let result = &block.transactions[1].metadata.ordinal_operations[0];
// println!("{:?}", result);
let charms = match result {
OrdinalOperation::InscriptionRevealed(data) => data.charms,
_ => unreachable!(),
};
// println!("{:?}", Charm::charms(charms));
Ok(Charm::charms(charms))
};
pg_reset_db(&mut pg_client).await?;
Expand Down
Loading

0 comments on commit 6815878

Please sign in to comment.