/* We want to be able to return received outputs. We do that by iterating over the inputs to find an address format we recognize, then setting that address as the address to return to. Since inputs only contain the script signatures, yet addresses are for script public keys, we need to pull up the output spent by an input and read the script public key from that. While we could use `txindex=1`, and an asynchronous call to the Bitcoin node, we: 1) Can maintain a much smaller index ourselves 2) Don't want the asynchronous call (which would require the flow be async, allowed to potentially error, and more latent) 3) Don't want to risk Bitcoin's `txindex` corruptions (frequently observed on testnet) This task builds that index. */ use serai_db::{DbTxn, Db}; use primitives::task::ContinuallyRan; use scanner::ScannerFeed; use crate::{db, rpc::Rpc, hash_bytes}; pub(crate) struct TxIndexTask(Rpc); #[async_trait::async_trait] impl ContinuallyRan for TxIndexTask { async fn run_iteration(&mut self) -> Result { let latest_block_number = self .0 .rpc .get_latest_block_number() .await .map_err(|e| format!("couldn't fetch latest block number: {e:?}"))?; let latest_block_number = u64::try_from(latest_block_number).unwrap(); // `CONFIRMATIONS - 1` as any on-chain block inherently has one confirmation (itself) let finalized_block_number = latest_block_number.checked_sub(Rpc::::CONFIRMATIONS - 1).ok_or(format!( "blockchain only just started and doesn't have {} blocks yet", Rpc::::CONFIRMATIONS ))?; let finalized_block_number_in_db = db::LatestBlockToYieldAsFinalized::get(&self.0.db); let next_block = finalized_block_number_in_db.map_or(0, |block| block + 1); let mut iterated = false; for b in next_block ..= finalized_block_number { iterated = true; // Fetch the block let block_hash = self .0 .rpc .get_block_hash(b.try_into().unwrap()) .await .map_err(|e| format!("couldn't fetch block hash for block {b}: {e:?}"))?; let block = self .0 .rpc .get_block(&block_hash) .await .map_err(|e| format!("couldn't fetch block {b}: {e:?}"))?; let mut txn = self.0.db.txn(); for tx in &block.txdata[1 ..] { let txid = hash_bytes(tx.compute_txid().to_raw_hash()); for (o, output) in tx.output.iter().enumerate() { let o = u32::try_from(o).unwrap(); // Set the script public key for this transaction db::ScriptPubKey::set(&mut txn, txid, o, &output.script_pubkey.clone().into_bytes()); } } db::LatestBlockToYieldAsFinalized::set(&mut txn, &b); txn.commit(); } Ok(iterated) } }