mirror of
https://github.com/alexgo-io/bitcoin-indexer.git
synced 2026-01-12 16:52:57 +08:00
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
This commit is contained in:
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -89,6 +89,7 @@ impl Brc20RevealBuilder {
|
||||
"9bb2314d666ae0b1db8161cb373fcc1381681f71445c4e0335aa80ea9c37fcdd:0:0".to_string(),
|
||||
curse_type: None,
|
||||
charms: 0,
|
||||
unbound_sequence: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ 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,
|
||||
@@ -23,7 +24,7 @@ use crate::{
|
||||
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;
|
||||
|
||||
@@ -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(
|
||||
@@ -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,
|
||||
@@ -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)",
|
||||
@@ -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<'_>,
|
||||
@@ -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;
|
||||
|
||||
@@ -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")]
|
||||
@@ -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(),
|
||||
@@ -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?;
|
||||
|
||||
@@ -14,10 +14,12 @@ use crate::{
|
||||
try_info,
|
||||
utils::format_outpoint_to_watch,
|
||||
};
|
||||
use ord::height::Height;
|
||||
|
||||
use super::inscription_sequencing::get_bitcoin_network;
|
||||
|
||||
pub const UNBOUND_INSCRIPTION_SATPOINT: &str =
|
||||
"0000000000000000000000000000000000000000000000000000000000000000:0";
|
||||
|
||||
#[derive(Clone, Debug, Ord, PartialOrd, PartialEq, Eq)]
|
||||
pub struct WatchedSatpoint {
|
||||
pub ordinal_number: u64,
|
||||
@@ -30,7 +32,7 @@ pub fn parse_output_and_offset_from_satpoint(
|
||||
let parts: Vec<&str> = satpoint.split(':').collect();
|
||||
let tx_id = parts
|
||||
.get(0)
|
||||
.ok_or("get_output_and_offset_from_satpoint: inscription_id not found")?;
|
||||
.ok_or("get_output_and_offset_from_satpoint: tx_id not found")?;
|
||||
let output = parts
|
||||
.get(1)
|
||||
.ok_or("get_output_and_offset_from_satpoint: output not found")?;
|
||||
@@ -50,18 +52,12 @@ pub async fn augment_block_with_transfers(
|
||||
ctx: &Context,
|
||||
) -> Result<(), String> {
|
||||
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 = 0;
|
||||
for (tx_index, tx) in block.transactions.iter_mut().enumerate() {
|
||||
let _ = augment_transaction_with_ordinal_transfers(
|
||||
tx,
|
||||
tx_index,
|
||||
&block.block_identifier,
|
||||
&network,
|
||||
&coinbase_tx,
|
||||
coinbase_subsidy,
|
||||
&mut cumulated_fees,
|
||||
db_tx,
|
||||
ctx,
|
||||
)
|
||||
@@ -75,9 +71,6 @@ pub fn compute_satpoint_post_transfer(
|
||||
input_index: usize,
|
||||
relative_pointer_value: u64,
|
||||
network: &Network,
|
||||
coinbase_tx: &BitcoinTransactionData,
|
||||
coinbase_subsidy: u64,
|
||||
cumulated_fees: &mut u64,
|
||||
ctx: &Context,
|
||||
) -> (OrdinalInscriptionTransferDestination, String, Option<u64>) {
|
||||
let inputs: Vec<u64> = tx
|
||||
@@ -131,37 +124,11 @@ pub fn compute_satpoint_post_transfer(
|
||||
Some(tx.metadata.outputs[output_index].value),
|
||||
)
|
||||
}
|
||||
SatPosition::Fee(offset) => {
|
||||
// Get Coinbase TX
|
||||
let total_offset = coinbase_subsidy + *cumulated_fees + offset;
|
||||
let outputs = coinbase_tx
|
||||
.metadata
|
||||
.outputs
|
||||
.iter()
|
||||
.map(|o| o.value)
|
||||
.collect();
|
||||
let post_transfer_data = compute_next_satpoint_data(
|
||||
0,
|
||||
&vec![total_offset],
|
||||
&outputs,
|
||||
total_offset,
|
||||
Some(ctx),
|
||||
);
|
||||
|
||||
// Identify the correct output
|
||||
let (output_index, offset) = match post_transfer_data {
|
||||
SatPosition::Output(pos) => pos,
|
||||
_ => {
|
||||
try_info!(ctx, "unable to locate satoshi in coinbase outputs");
|
||||
(0, total_offset)
|
||||
}
|
||||
};
|
||||
|
||||
let outpoint =
|
||||
format_outpoint_to_watch(&coinbase_tx.transaction_identifier, output_index);
|
||||
SatPosition::Fee(_) => {
|
||||
// Unbound inscription satpoints will be updated later with an unbound sequence number.
|
||||
(
|
||||
outpoint,
|
||||
offset,
|
||||
UNBOUND_INSCRIPTION_SATPOINT.into(),
|
||||
0,
|
||||
OrdinalInscriptionTransferDestination::SpentInFees,
|
||||
None,
|
||||
)
|
||||
@@ -181,9 +148,6 @@ pub async fn augment_transaction_with_ordinal_transfers(
|
||||
tx_index: usize,
|
||||
block_identifier: &BlockIdentifier,
|
||||
network: &Network,
|
||||
coinbase_tx: &BitcoinTransactionData,
|
||||
coinbase_subsidy: u64,
|
||||
cumulated_fees: &mut u64,
|
||||
db_tx: &Transaction<'_>,
|
||||
ctx: &Context,
|
||||
) -> Result<Vec<OrdinalInscriptionTransferData>, String> {
|
||||
@@ -224,9 +188,6 @@ pub async fn augment_transaction_with_ordinal_transfers(
|
||||
input_index,
|
||||
watched_satpoint.offset,
|
||||
network,
|
||||
coinbase_tx,
|
||||
coinbase_subsidy,
|
||||
cumulated_fees,
|
||||
ctx,
|
||||
);
|
||||
|
||||
@@ -253,7 +214,6 @@ pub async fn augment_transaction_with_ordinal_transfers(
|
||||
.push(OrdinalOperation::InscriptionTransferred(transfer_data));
|
||||
}
|
||||
}
|
||||
*cumulated_fees += tx.metadata.fee;
|
||||
|
||||
Ok(transfers)
|
||||
}
|
||||
@@ -275,21 +235,10 @@ mod test {
|
||||
.add_input(TestTxInBuilder::new().value(10_000).build())
|
||||
.add_output(TestTxOutBuilder::new().value(2_000).build())
|
||||
.build();
|
||||
let coinbase_tx = &TestTransactionBuilder::new()
|
||||
.add_output(TestTxOutBuilder::new().value(312_500_000 + 5_000).build())
|
||||
.build();
|
||||
|
||||
let (destination, satpoint, value) = compute_satpoint_post_transfer(
|
||||
tx,
|
||||
0,
|
||||
// This offset will make it go to fees.
|
||||
5_000,
|
||||
&Network::Bitcoin,
|
||||
coinbase_tx,
|
||||
312_500_000,
|
||||
&mut 0,
|
||||
&ctx,
|
||||
);
|
||||
// This 5000 offset will make it go to fees.
|
||||
let (destination, satpoint, value) =
|
||||
compute_satpoint_post_transfer(tx, 0, 5_000, &Network::Bitcoin, &ctx);
|
||||
|
||||
assert_eq!(
|
||||
destination,
|
||||
@@ -297,8 +246,7 @@ mod test {
|
||||
);
|
||||
assert_eq!(
|
||||
satpoint,
|
||||
"b61b0172d95e266c18aea0c624db987e971a5d6d4ebc2aaed85da4642d635735:0:312503000"
|
||||
.to_string()
|
||||
"0000000000000000000000000000000000000000000000000000000000000000:0:0"
|
||||
);
|
||||
assert_eq!(value, None);
|
||||
}
|
||||
@@ -316,20 +264,9 @@ mod test {
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
let coinbase_tx = &TestTransactionBuilder::new()
|
||||
.add_output(TestTxOutBuilder::new().value(312_500_000).build())
|
||||
.build();
|
||||
|
||||
let (destination, satpoint, value) = compute_satpoint_post_transfer(
|
||||
tx,
|
||||
0,
|
||||
5_000,
|
||||
&Network::Bitcoin,
|
||||
coinbase_tx,
|
||||
312_500_000,
|
||||
&mut 0,
|
||||
&ctx,
|
||||
);
|
||||
let (destination, satpoint, value) =
|
||||
compute_satpoint_post_transfer(tx, 0, 5_000, &Network::Bitcoin, &ctx);
|
||||
|
||||
assert_eq!(
|
||||
destination,
|
||||
|
||||
@@ -16,6 +16,7 @@ pub struct SequenceCursor {
|
||||
pos_cursor: Option<i64>,
|
||||
neg_cursor: Option<i64>,
|
||||
jubilee_cursor: Option<i64>,
|
||||
unbound_cursor: Option<i64>,
|
||||
current_block_height: u64,
|
||||
}
|
||||
|
||||
@@ -25,6 +26,7 @@ impl SequenceCursor {
|
||||
jubilee_cursor: None,
|
||||
pos_cursor: None,
|
||||
neg_cursor: None,
|
||||
unbound_cursor: None,
|
||||
current_block_height: 0,
|
||||
}
|
||||
}
|
||||
@@ -33,6 +35,7 @@ impl SequenceCursor {
|
||||
self.pos_cursor = None;
|
||||
self.neg_cursor = None;
|
||||
self.jubilee_cursor = None;
|
||||
self.unbound_cursor = None;
|
||||
self.current_block_height = 0;
|
||||
}
|
||||
|
||||
@@ -76,6 +79,12 @@ impl SequenceCursor {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn increment_unbound<T: GenericClient>(&mut self, client: &T) -> Result<i64, String> {
|
||||
let next = self.pick_next_unbound(client).await?;
|
||||
self.unbound_cursor = Some(next);
|
||||
Ok(next)
|
||||
}
|
||||
|
||||
async fn pick_next_pos_classic<T: GenericClient>(&mut self, client: &T) -> Result<i64, String> {
|
||||
match self.pos_cursor {
|
||||
None => {
|
||||
@@ -122,6 +131,19 @@ impl SequenceCursor {
|
||||
}
|
||||
}
|
||||
|
||||
async fn pick_next_unbound<T: GenericClient>(&mut self, client: &T) -> Result<i64, String> {
|
||||
match self.unbound_cursor {
|
||||
None => match ordinals_pg::get_highest_unbound_inscription_sequence(client).await? {
|
||||
Some(unbound_sequence) => {
|
||||
self.unbound_cursor = Some(unbound_sequence);
|
||||
Ok(unbound_sequence + 1)
|
||||
}
|
||||
_ => Ok(0),
|
||||
},
|
||||
Some(value) => Ok(value + 1),
|
||||
}
|
||||
}
|
||||
|
||||
async fn increment_neg_classic<T: GenericClient>(&mut self, client: &T) -> Result<(), String> {
|
||||
self.neg_cursor = Some(self.pick_next_neg_classic(client).await?);
|
||||
Ok(())
|
||||
@@ -146,6 +168,7 @@ mod test {
|
||||
use bitcoin::Network;
|
||||
use chainhook_postgres::{pg_begin, pg_pool_client};
|
||||
|
||||
use chainhook_types::OrdinalOperation;
|
||||
use test_case::test_case;
|
||||
|
||||
use crate::{
|
||||
@@ -204,4 +227,30 @@ mod test {
|
||||
pg_reset_db(&mut pg_client).await?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[test_case(None => Ok(0); "without sequence")]
|
||||
#[test_case(Some(21) => Ok(22); "with current sequence")]
|
||||
#[tokio::test]
|
||||
async fn picks_next_unbound_sequence(curr_sequence: Option<i64>) -> Result<i64, String> {
|
||||
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?;
|
||||
|
||||
let mut tx = TestTransactionBuilder::new_with_operation().build();
|
||||
if let OrdinalOperation::InscriptionRevealed(data) =
|
||||
&mut tx.metadata.ordinal_operations[0]
|
||||
{
|
||||
data.unbound_sequence = curr_sequence;
|
||||
};
|
||||
let block = TestBlockBuilder::new().transactions(vec![tx]).build();
|
||||
insert_block(&block, &client).await?;
|
||||
|
||||
let mut cursor = SequenceCursor::new();
|
||||
cursor.increment_unbound(&client).await?
|
||||
};
|
||||
pg_reset_db(&mut pg_client).await?;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,6 +105,7 @@ impl TestTransactionBuilder {
|
||||
satpoint_post_inscription: "b61b0172d95e266c18aea0c624db987e971a5d6d4ebc2aaed85da4642d635735:0:0".to_string(),
|
||||
curse_type: None,
|
||||
charms: 0,
|
||||
unbound_sequence: None,
|
||||
},
|
||||
)];
|
||||
tx
|
||||
|
||||
@@ -33,6 +33,7 @@ pub struct DbInscription {
|
||||
pub delegate: Option<String>,
|
||||
pub timestamp: PgBigIntU32,
|
||||
pub charms: PgBigIntU32,
|
||||
pub unbound_sequence: Option<i64>,
|
||||
}
|
||||
|
||||
impl DbInscription {
|
||||
@@ -84,6 +85,7 @@ impl DbInscription {
|
||||
delegate: reveal.delegate.clone(),
|
||||
timestamp: PgBigIntU32(timestamp),
|
||||
charms: PgBigIntU32(reveal.charms as u32),
|
||||
unbound_sequence: reveal.unbound_sequence,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,6 +116,7 @@ impl FromPgRow for DbInscription {
|
||||
delegate: row.get("delegate"),
|
||||
timestamp: row.get("timestamp"),
|
||||
charms: row.get("charms"),
|
||||
unbound_sequence: row.get("unbound_sequence"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ mod test {
|
||||
satpoint_post_inscription: "e47a70a218dfa746ba410b1c057403bb481523d830562fd8dec61ec4d2915e5f:0:0".to_string(),
|
||||
curse_type: None,
|
||||
charms: 0,
|
||||
unbound_sequence: None,
|
||||
};
|
||||
let recursions = DbInscriptionRecursion::from_reveal(&reveal).unwrap();
|
||||
assert_eq!(2, recursions.len());
|
||||
|
||||
@@ -98,6 +98,20 @@ pub async fn get_lowest_cursed_classic_inscription_number<T: GenericClient>(
|
||||
Ok(min)
|
||||
}
|
||||
|
||||
pub async fn get_highest_unbound_inscription_sequence<T: GenericClient>(
|
||||
client: &T,
|
||||
) -> Result<Option<i64>, String> {
|
||||
let row = client
|
||||
.query_opt("SELECT MAX(unbound_sequence) AS max FROM inscriptions", &[])
|
||||
.await
|
||||
.map_err(|e| format!("get_highest_unbound_inscription_sequence: {e}"))?;
|
||||
let Some(row) = row else {
|
||||
return Ok(None);
|
||||
};
|
||||
let max: Option<i64> = row.get("max");
|
||||
Ok(max)
|
||||
}
|
||||
|
||||
pub async fn get_reinscriptions_for_block<T: GenericClient>(
|
||||
inscriptions_data: &mut BTreeMap<(TransactionIdentifier, usize, u64), TraversalResult>,
|
||||
client: &T,
|
||||
@@ -261,15 +275,16 @@ async fn insert_inscriptions<T: GenericClient>(
|
||||
params.push(&row.delegate);
|
||||
params.push(&row.timestamp);
|
||||
params.push(&row.charms);
|
||||
params.push(&row.unbound_sequence);
|
||||
}
|
||||
client
|
||||
.query(
|
||||
&format!("INSERT INTO inscriptions
|
||||
(inscription_id, ordinal_number, number, classic_number, block_height, block_hash, tx_id, tx_index, address,
|
||||
mime_type, content_type, content_length, content, fee, curse_type, recursive, input_index, pointer, metadata,
|
||||
metaprotocol, delegate, timestamp, charms)
|
||||
metaprotocol, delegate, timestamp, charms, unbound_sequence)
|
||||
VALUES {}
|
||||
ON CONFLICT (number) DO NOTHING", utils::multi_row_query_param_str(chunk.len(), 23)),
|
||||
ON CONFLICT (number) DO NOTHING", utils::multi_row_query_param_str(chunk.len(), 24)),
|
||||
¶ms,
|
||||
)
|
||||
.await
|
||||
@@ -1206,6 +1221,7 @@ mod test {
|
||||
satpoint_post_inscription: "b61b0172d95e266c18aea0c624db987e971a5d6d4ebc2aaed85da4642d635735:0:0".to_string(),
|
||||
curse_type: None,
|
||||
charms: 0,
|
||||
unbound_sequence: None,
|
||||
},
|
||||
))
|
||||
.build()
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE inscriptions ADD COLUMN unbound_sequence BIGINT UNIQUE;
|
||||
Reference in New Issue
Block a user