diff --git a/Cargo.lock b/Cargo.lock index bef4b1040..a5c04fde2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -683,6 +683,7 @@ dependencies = [ name = "brk_grouper" version = "0.0.111" dependencies = [ + "brk_error", "brk_structs", "brk_traversable", "vecdb", diff --git a/crates/brk_grouper/Cargo.toml b/crates/brk_grouper/Cargo.toml index 30030969c..3ddb60329 100644 --- a/crates/brk_grouper/Cargo.toml +++ b/crates/brk_grouper/Cargo.toml @@ -10,6 +10,7 @@ rust-version.workspace = true build = "build.rs" [dependencies] +brk_error = { workspace = true } brk_structs = { workspace = true } brk_traversable = { workspace = true } vecdb = { workspace = true } diff --git a/crates/brk_grouper/src/by_address_type.rs b/crates/brk_grouper/src/by_address_type.rs index 312e5ebee..cd466e102 100644 --- a/crates/brk_grouper/src/by_address_type.rs +++ b/crates/brk_grouper/src/by_address_type.rs @@ -1,11 +1,21 @@ use std::ops::{Add, AddAssign}; +use brk_error::Result; use brk_structs::OutputType; use brk_traversable::{Traversable, TreeNode}; use vecdb::AnyCollectableVec; use super::{Filter, Filtered}; +pub const P2PK65: &str = "p2pk65"; +pub const P2PK33: &str = "p2pk33"; +pub const P2PKH: &str = "p2pkh"; +pub const P2SH: &str = "p2sh"; +pub const P2WPKH: &str = "p2wpkh"; +pub const P2WSH: &str = "p2wsh"; +pub const P2TR: &str = "p2tr"; +pub const P2A: &str = "p2a"; + #[derive(Default, Clone, Debug)] pub struct ByAddressType { pub p2pk65: T, @@ -19,6 +29,22 @@ pub struct ByAddressType { } impl ByAddressType { + pub fn new(f: F) -> Result + where + F: Fn(&'static str) -> Result, + { + Ok(Self { + p2pk65: f(P2PK65)?, + p2pk33: f(P2PK33)?, + p2pkh: f(P2PKH)?, + p2sh: f(P2SH)?, + p2wpkh: f(P2WPKH)?, + p2wsh: f(P2WSH)?, + p2tr: f(P2TR)?, + p2a: f(P2A)?, + }) + } + pub fn get_unwrap(&self, address_type: OutputType) -> &T { self.get(address_type).unwrap() } @@ -200,14 +226,14 @@ impl Traversable for ByAddressType { fn to_tree_node(&self) -> TreeNode { TreeNode::Branch( [ - ("p2pk65", &self.p2pk65), - ("p2pk33", &self.p2pk33), - ("p2pkh", &self.p2pkh), - ("p2sh", &self.p2sh), - ("p2wpkh", &self.p2wpkh), - ("p2wsh", &self.p2wsh), - ("p2tr", &self.p2tr), - ("p2a", &self.p2a), + (P2PK65, &self.p2pk65), + (P2PK33, &self.p2pk33), + (P2PKH, &self.p2pkh), + (P2SH, &self.p2sh), + (P2WPKH, &self.p2wpkh), + (P2WSH, &self.p2wsh), + (P2TR, &self.p2tr), + (P2A, &self.p2a), ] .into_iter() .map(|(name, field)| (name.to_string(), field.to_tree_node())) diff --git a/crates/brk_indexer/examples/indexer.rs b/crates/brk_indexer/examples/indexer.rs index 578b6e98b..57410c2dd 100644 --- a/crates/brk_indexer/examples/indexer.rs +++ b/crates/brk_indexer/examples/indexer.rs @@ -50,6 +50,6 @@ fn main() -> Result<()> { indexer.index(&parser, rpc, &exit, true)?; dbg!(i.elapsed()); - sleep(Duration::from_secs(5 * 60)); + sleep(Duration::from_secs(60)); } } diff --git a/crates/brk_indexer/src/indexes.rs b/crates/brk_indexer/src/indexes.rs index 77f2e7a49..3a4e1ab0c 100644 --- a/crates/brk_indexer/src/indexes.rs +++ b/crates/brk_indexer/src/indexes.rs @@ -1,10 +1,10 @@ use bitcoincore_rpc::Client; use brk_error::{Error, Result}; use brk_structs::{ - BlockHash, CheckedSub, EmptyOutputIndex, Height, TxInIndex, OpReturnIndex, TxOutIndex, - OutputType, P2AAddressIndex, P2MSOutputIndex, P2PK33AddressIndex, P2PK65AddressIndex, - P2PKHAddressIndex, P2SHAddressIndex, P2TRAddressIndex, P2WPKHAddressIndex, P2WSHAddressIndex, - TxIndex, TypeIndex, UnknownOutputIndex, + BlockHash, CheckedSub, EmptyOutputIndex, Height, OpReturnIndex, OutputType, P2AAddressIndex, + P2MSOutputIndex, P2PK33AddressIndex, P2PK65AddressIndex, P2PKHAddressIndex, P2SHAddressIndex, + P2TRAddressIndex, P2WPKHAddressIndex, P2WSHAddressIndex, TxInIndex, TxIndex, TxOutIndex, + TypeIndex, UnknownOutputIndex, }; use vecdb::{ AnyIterableVec, AnyStoredIterableVec, GenericStoredVec, StoredIndex, StoredRaw, VecIterator, @@ -234,7 +234,7 @@ impl TryFrom<(&mut Vecs, &Stores, &Client)> for Indexes { let txinindex = starting_index( &vecs.height_to_first_txinindex, - &vecs.txinindex_to_txoutindex, + &vecs.txinindex_to_outpoint, height, ) .ok_or(Error::Str(""))?; diff --git a/crates/brk_indexer/src/lib.rs b/crates/brk_indexer/src/lib.rs index d997b1e34..c923c165e 100644 --- a/crates/brk_indexer/src/lib.rs +++ b/crates/brk_indexer/src/lib.rs @@ -1,14 +1,14 @@ #![doc = include_str!("../README.md")] -use std::{collections::BTreeMap, path::Path, str::FromStr, time::Instant}; +use std::{collections::BTreeMap, path::Path, str::FromStr, thread, time::Instant}; use bitcoin::{Transaction, TxIn, TxOut}; use brk_error::{Error, Result}; use brk_store::AnyStore; use brk_structs::{ - AddressBytes, AddressBytesHash, BlockHashPrefix, Height, OutputType, Sats, StoredBool, - Timestamp, TxInIndex, TxIndex, TxOutIndex, Txid, TxidPrefix, TypeIndex, - TypeIndexWithOutputindex, Unit, Version, Vin, Vout, + AddressBytes, AddressBytesHash, BlockHashPrefix, Height, OutPoint, OutputType, Sats, + StoredBool, Timestamp, TxInIndex, TxIndex, TxOutIndex, Txid, TxidPrefix, TypeIndex, + TypeIndexAndOutPoint, TypeIndexAndTxIndex, Unit, Version, Vin, Vout, }; use log::{error, info}; use rayon::prelude::*; @@ -39,11 +39,18 @@ impl Indexer { let path = outputs_dir.join("indexed"); - let vecs = Vecs::forced_import(&path, VERSION)?; - info!("Imported vecs"); + let (vecs, stores) = thread::scope(|s| -> Result<_> { + let vecs = s.spawn(|| -> Result<_> { + let vecs = Vecs::forced_import(&path, VERSION)?; + info!("Imported vecs"); + Ok(vecs) + }); - let stores = Stores::forced_import(&path, VERSION)?; - info!("Imported stores"); + let stores = Stores::forced_import(&path, VERSION)?; + info!("Imported stores"); + + Ok((vecs.join().unwrap()?, stores)) + })?; Ok(Self { vecs, stores }) } @@ -103,6 +110,8 @@ impl Indexer { }; let mut txindex_to_first_txoutindex_reader_opt = None; + let mut txoutindex_to_outputtype_reader_opt = None; + let mut txoutindex_to_typeindex_reader_opt = None; let mut p2pk65addressindex_to_p2pk65bytes_reader_opt = None; let mut p2pk33addressindex_to_p2pk33bytes_reader_opt = None; let mut p2pkhaddressindex_to_p2pkhbytes_reader_opt = None; @@ -115,6 +124,8 @@ impl Indexer { let reset_readers = |vecs: &mut Vecs, txindex_to_first_txoutindex_reader_opt: &mut Option>, + txoutindex_to_outputtype_reader_opt: &mut Option>, + txoutindex_to_typeindex_reader_opt: &mut Option>, p2pk65addressindex_to_p2pk65bytes_reader_opt: &mut Option>, p2pk33addressindex_to_p2pk33bytes_reader_opt: &mut Option>, p2pkhaddressindex_to_p2pkhbytes_reader_opt: &mut Option>, @@ -125,6 +136,10 @@ impl Indexer { p2aaddressindex_to_p2abytes_reader_opt: &mut Option>| { txindex_to_first_txoutindex_reader_opt .replace(vecs.txindex_to_first_txoutindex.create_static_reader()); + txoutindex_to_outputtype_reader_opt + .replace(vecs.txoutindex_to_outputtype.create_static_reader()); + txoutindex_to_typeindex_reader_opt + .replace(vecs.txoutindex_to_value.create_static_reader()); p2pk65addressindex_to_p2pk65bytes_reader_opt.replace( vecs.p2pk65addressindex_to_p2pk65bytes .create_static_reader(), @@ -152,6 +167,8 @@ impl Indexer { reset_readers( vecs, &mut txindex_to_first_txoutindex_reader_opt, + &mut txoutindex_to_outputtype_reader_opt, + &mut txoutindex_to_typeindex_reader_opt, &mut p2pk65addressindex_to_p2pk65bytes_reader_opt, &mut p2pk33addressindex_to_p2pk33bytes_reader_opt, &mut p2pkhaddressindex_to_p2pkhbytes_reader_opt, @@ -172,6 +189,8 @@ impl Indexer { idxs.height = height; let txindex_to_first_txoutindex_reader = txindex_to_first_txoutindex_reader_opt.as_ref().unwrap(); + let txoutindex_to_outputtype_reader = txoutindex_to_outputtype_reader_opt.as_ref().unwrap(); + let txoutindex_to_typeindex_reader = txoutindex_to_typeindex_reader_opt.as_ref().unwrap(); let p2pk65addressindex_to_p2pk65bytes_reader = p2pk65addressindex_to_p2pk65bytes_reader_opt.as_ref().unwrap(); let p2pk33addressindex_to_p2pk33bytes_reader = p2pk33addressindex_to_p2pk33bytes_reader_opt.as_ref().unwrap(); let p2pkhaddressindex_to_p2pkhbytes_reader = p2pkhaddressindex_to_p2pkhbytes_reader_opt.as_ref().unwrap(); @@ -246,7 +265,7 @@ impl Indexer { }) .collect::>(); - let input_source_vec = inputs + let txinindex_to_txindata = inputs .into_par_iter() .enumerate() .map(|(block_txinindex, (block_txindex, vin, txin, tx))| -> Result<(TxInIndex, InputSource)> { @@ -283,11 +302,30 @@ impl Indexer { })?.into_owned() + vout; - Ok((txinindex, InputSource::PreviousBlock(( + let outpoint = OutPoint::new(prev_txindex, vout); + + let outputtype = vecs.txoutindex_to_outputtype.get_or_read(txoutindex, txoutindex_to_outputtype_reader)? + .ok_or(Error::Str("Expect outputtype to not be none"))?.into_owned(); + + let mut tuple = ( vin, txindex, - txoutindex, - )))) + outpoint, + None + ); + + // Rare but happens + // https://mempool.space/tx/8ebe1df6ebf008f7ec42ccd022478c9afaec3ca0444322243b745aa2e317c272#flow=&vin=89 + if outputtype.is_not_address() { + return Ok((txinindex, InputSource::PreviousBlock(tuple))); + } + + let typeindex = vecs.txoutindex_to_typeindex.get_or_read(txoutindex, txoutindex_to_typeindex_reader)? + .ok_or(Error::Str("Expect typeindex to not be none"))?.into_owned(); + + tuple.3 = Some((outputtype, typeindex)); + + Ok((txinindex, InputSource::PreviousBlock(tuple))) }) .try_fold(BTreeMap::new, |mut map, tuple| -> Result<_> { let (key, value) = tuple?; @@ -315,7 +353,7 @@ impl Indexer { .map(move |(vout, txout)| (TxIndex::from(index), Vout::from(vout), txout, tx)) }).collect::>(); - let txoutindex_to_txout_outputtype_addressbytes_res_addressindex_opt = outputs.into_par_iter() + let txoutindex_to_txoutdata = outputs.into_par_iter() .enumerate() .map( #[allow(clippy::type_complexity)] @@ -349,7 +387,7 @@ impl Indexer { .get(&AddressBytesHash::from((addressbytes, outputtype))) .unwrap() .map(|v| *v) - // Checking if not in the future + // Checking if not in the future (in case we started before the last processed block) .and_then(|typeindex_local| { (typeindex_local < idxs.to_typeindex(outputtype)).then_some(typeindex_local) }) @@ -449,141 +487,142 @@ impl Indexer { } })?; - let outputs_len = txoutindex_to_txout_outputtype_addressbytes_res_addressindex_opt.len(); - let inputs_len = input_source_vec.len(); + let outputs_len = txoutindex_to_txoutdata.len(); + let inputs_len = txinindex_to_txindata.len(); let tx_len = block.txdata.len(); - let mut new_txindexvout_to_txoutindex: BTreeMap< - (TxIndex, Vout), - TxOutIndex, - > = BTreeMap::new(); - let mut already_added_addressbyteshash: BTreeMap = BTreeMap::new(); - txoutindex_to_txout_outputtype_addressbytes_res_addressindex_opt - .into_iter() - .try_for_each( - |( - txoutindex, - (txout, txindex, vout, outputtype, addressbytes_res, typeindex_opt, _tx), - )| - -> Result<()> { - let sats = Sats::from(txout.value); + let mut outpoint_to_outputtype_and_addressindex: BTreeMap = + txoutindex_to_txoutdata + .into_iter() + .flat_map(|( + txoutindex, + (txout, txindex, vout, outputtype, addressbytes_res, typeindex_opt, _tx), + )| { + let result: Result> = (|| { + let sats = Sats::from(txout.value); - if vout.is_zero() { - vecs.txindex_to_first_txoutindex.push_if_needed(txindex, txoutindex)?; - } + if vout.is_zero() { + vecs.txindex_to_first_txoutindex.push_if_needed(txindex, txoutindex)?; + } - vecs.txoutindex_to_value.push_if_needed(txoutindex, sats)?; + vecs.txoutindex_to_value.push_if_needed(txoutindex, sats)?; - vecs.txoutindex_to_outputtype - .push_if_needed(txoutindex, outputtype)?; + vecs.txoutindex_to_outputtype + .push_if_needed(txoutindex, outputtype)?; - let mut addressbyteshash = None; + let mut addressbyteshash = None; - let typeindex; + let typeindex; - if let Some(typeindex_local) = typeindex_opt.or_else(|| { - addressbytes_res.as_ref().ok().and_then(|addressbytes| { - // Check if address was first seen before in this iterator - // Example: https://mempool.space/address/046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0c - addressbyteshash.replace(AddressBytesHash::from((addressbytes, outputtype))); - already_added_addressbyteshash - .get(addressbyteshash.as_ref().unwrap()) - .cloned() - }) - }) { - typeindex = typeindex_local; - } else { - typeindex = match outputtype { - OutputType::P2PK65 => { - idxs.p2pk65addressindex.copy_then_increment() - }, - OutputType::P2PK33 => { - idxs.p2pk33addressindex.copy_then_increment() - }, - OutputType::P2PKH => { - idxs.p2pkhaddressindex.copy_then_increment() - }, - OutputType::P2MS => { - vecs.p2msoutputindex_to_txindex.push_if_needed(idxs.p2msoutputindex, txindex)?; - idxs.p2msoutputindex.copy_then_increment() - }, - OutputType::P2SH => { - idxs.p2shaddressindex.copy_then_increment() - }, - OutputType::OpReturn => { - vecs.opreturnindex_to_txindex.push_if_needed(idxs.opreturnindex, txindex)?; - idxs.opreturnindex.copy_then_increment() - }, - OutputType::P2WPKH => { - idxs.p2wpkhaddressindex.copy_then_increment() - }, - OutputType::P2WSH => { - idxs.p2wshaddressindex.copy_then_increment() - }, - OutputType::P2TR => { - idxs.p2traddressindex.copy_then_increment() - }, - OutputType::P2A => { - idxs.p2aaddressindex.copy_then_increment() - }, - OutputType::Empty => { - vecs.emptyoutputindex_to_txindex - .push_if_needed(idxs.emptyoutputindex, txindex)?; - idxs.emptyoutputindex.copy_then_increment() - }, - OutputType::Unknown => { - vecs.unknownoutputindex_to_txindex.push_if_needed(idxs.unknownoutputindex, txindex)?; - idxs.unknownoutputindex.copy_then_increment() - }, - _ => unreachable!() - }; + if let Some(typeindex_local) = typeindex_opt.or_else(|| { + addressbytes_res.as_ref().ok().and_then(|addressbytes| { + // Check if address was first seen before in this iterator + // Example: https://mempool.space/address/046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0c + addressbyteshash.replace(AddressBytesHash::from((addressbytes, outputtype))); + already_added_addressbyteshash + .get(addressbyteshash.as_ref().unwrap()) + .cloned() + }) + }) { + typeindex = typeindex_local; + } else { + typeindex = match outputtype { + OutputType::P2PK65 => { + idxs.p2pk65addressindex.copy_then_increment() + }, + OutputType::P2PK33 => { + idxs.p2pk33addressindex.copy_then_increment() + }, + OutputType::P2PKH => { + idxs.p2pkhaddressindex.copy_then_increment() + }, + OutputType::P2MS => { + vecs.p2msoutputindex_to_txindex.push_if_needed(idxs.p2msoutputindex, txindex)?; + idxs.p2msoutputindex.copy_then_increment() + }, + OutputType::P2SH => { + idxs.p2shaddressindex.copy_then_increment() + }, + OutputType::OpReturn => { + vecs.opreturnindex_to_txindex.push_if_needed(idxs.opreturnindex, txindex)?; + idxs.opreturnindex.copy_then_increment() + }, + OutputType::P2WPKH => { + idxs.p2wpkhaddressindex.copy_then_increment() + }, + OutputType::P2WSH => { + idxs.p2wshaddressindex.copy_then_increment() + }, + OutputType::P2TR => { + idxs.p2traddressindex.copy_then_increment() + }, + OutputType::P2A => { + idxs.p2aaddressindex.copy_then_increment() + }, + OutputType::Empty => { + vecs.emptyoutputindex_to_txindex + .push_if_needed(idxs.emptyoutputindex, txindex)?; + idxs.emptyoutputindex.copy_then_increment() + }, + OutputType::Unknown => { + vecs.unknownoutputindex_to_txindex.push_if_needed(idxs.unknownoutputindex, txindex)?; + idxs.unknownoutputindex.copy_then_increment() + }, + _ => unreachable!() + }; - if let Ok(addressbytes) = addressbytes_res { - let addressbyteshash = addressbyteshash.unwrap(); + if let Ok(addressbytes) = addressbytes_res { + let addressbyteshash = addressbyteshash.unwrap(); - already_added_addressbyteshash - .insert(addressbyteshash, typeindex); + already_added_addressbyteshash + .insert(addressbyteshash, typeindex); - stores.addressbyteshash_to_typeindex.insert_if_needed( - addressbyteshash, - typeindex, - height, - ); + stores.addressbyteshash_to_typeindex.insert_if_needed( + addressbyteshash, + typeindex, + height, + ); - vecs.push_bytes_if_needed(typeindex, addressbytes)?; + vecs.push_bytes_if_needed(typeindex, addressbytes)?; + } + } + + vecs.txoutindex_to_typeindex + .push_if_needed(txoutindex, typeindex)?; + + if outputtype.is_unspendable() { + return Ok(None) + } else if outputtype.is_address() { + stores.addresstype_to_typeindex_and_txindex.get_mut(outputtype).unwrap().insert_if_needed(TypeIndexAndTxIndex::from((typeindex, txindex)), Unit, height); + } + + Ok(Some((OutPoint::new(txindex, vout), (outputtype, typeindex)))) + })(); + + match result { + Ok(Some(item)) => Some(Ok(item)), + Ok(None) => None, + Err(e) => Some(Err(e)), } - } - - vecs.txoutindex_to_typeindex - .push_if_needed(txoutindex, typeindex)?; - - new_txindexvout_to_txoutindex - .insert((txindex, vout), txoutindex); - - if outputtype.is_address() { - stores.addresstype_to_typeindex_with_txoutindex.get_mut(outputtype).unwrap().insert_if_needed(TypeIndexWithOutputindex::from((typeindex, txoutindex)), Unit, height); - } - - Ok(()) - }, - )?; + }) + .collect::>>()?; drop(already_added_addressbyteshash); - input_source_vec + txinindex_to_txindata .into_iter() .map( #[allow(clippy::type_complexity)] |(txinindex, input_source)| -> Result<( - TxInIndex, Vin, TxIndex, TxOutIndex + TxInIndex, Vin, TxIndex, OutPoint, Option<(OutputType, TypeIndex)> )> { match input_source { - InputSource::PreviousBlock((vin, txindex, txoutindex)) => Ok((txinindex, vin, txindex, txoutindex)), + InputSource::PreviousBlock((vin, txindex, outpoint, outputtype_typeindex_opt)) => Ok((txinindex, vin, txindex, outpoint, outputtype_typeindex_opt)), InputSource::SameBlock((tx, txindex, txin, vin)) => { if tx.is_coinbase() { - return Ok((txinindex, vin, txindex, TxOutIndex::COINBASE)); + return Ok((txinindex, vin, txindex, OutPoint::COINBASE, None)); } let outpoint = txin.previous_output; @@ -599,44 +638,68 @@ impl Indexer { .2; let prev_txindex = idxs.txindex + block_txindex; - let prev_txoutindex = new_txindexvout_to_txoutindex - .remove(&(prev_txindex, vout)) + let outpoint = OutPoint::new(prev_txindex, vout); + + let mut tuple = (txinindex, vin, txindex, outpoint, None); + + let outputtype_typeindex = outpoint_to_outputtype_and_addressindex + .remove(&outpoint) .ok_or(Error::Str("should have found addressindex from same block")) .inspect_err(|_| { - dbg!(&new_txindexvout_to_txoutindex, txin, prev_txindex, vout, txid); + dbg!(&outpoint_to_outputtype_and_addressindex, txin, prev_txindex, vout, txid); })?; - Ok((txinindex, vin, txindex, prev_txoutindex)) + if outputtype_typeindex.0.is_not_address() { + return Ok(tuple) + } + + tuple.4 = Some(outputtype_typeindex); + + Ok(tuple) } } }, ) .try_for_each(|res| -> Result<()> { - let (txinindex, vin, txindex, txoutindex) = res?; + let (txinindex, vin, txindex, outpoint, outputtype_typeindex_opt) = res?; if vin.is_zero() { vecs.txindex_to_first_txinindex.push_if_needed(txindex, txinindex)?; } - vecs.txinindex_to_txoutindex.push_if_needed(txinindex, txoutindex)?; + vecs.txinindex_to_outpoint.push_if_needed(txinindex, outpoint)?; + + let Some((outputtype, typeindex)) = outputtype_typeindex_opt else { + return Ok(()) + }; + + stores.addresstype_to_typeindex_and_txindex.get_mut(outputtype).unwrap().insert_if_needed(TypeIndexAndTxIndex::from((typeindex, txindex)), Unit, height); + + stores.addresstype_to_typeindex_and_unspentoutpoint.get_mut(outputtype).unwrap().remove_if_needed(TypeIndexAndOutPoint::from((typeindex, outpoint)), height); Ok(()) })?; - drop(new_txindexvout_to_txoutindex); - - let mut txindex_to_tx_and_txid: BTreeMap = BTreeMap::default(); + outpoint_to_outputtype_and_addressindex + .into_iter() + .filter(|(_, (outputtype,_))| outputtype.is_address()) + .for_each(|(outpoint, (outputtype, typeindex))| { + stores.addresstype_to_typeindex_and_unspentoutpoint + .get_mut(outputtype) + .unwrap() + .insert_if_needed(TypeIndexAndOutPoint::from((typeindex, outpoint)), Unit, height); + }); let mut txindex_to_txid_iter = vecs .txindex_to_txid.into_iter(); - txid_prefix_to_txid_and_block_txindex_and_prev_txindex + let txindex_to_tx_and_txid = txid_prefix_to_txid_and_block_txindex_and_prev_txindex .into_iter() - .try_for_each( - |(txid_prefix, (tx, txid, index, prev_txindex_opt))| -> Result<()> { + .map( + |(txid_prefix, (tx, txid, index, prev_txindex_opt))| -> Result<(TxIndex, (&Transaction, Txid))> { let txindex = idxs.txindex + index; - txindex_to_tx_and_txid.insert(txindex, (tx, txid)); + let tuple = (txindex, (tx, txid)); match prev_txindex_opt { None => { @@ -647,11 +710,11 @@ impl Indexer { Some(prev_txindex) => { // In case if we start at an already parsed height if txindex == prev_txindex { - return Ok(()); + return Ok(tuple); } if !check_collisions { - return Ok(()); + return Ok(tuple); } let len = vecs.txindex_to_txid.len(); @@ -689,9 +752,9 @@ impl Indexer { } } - Ok(()) + Ok(tuple) }, - )?; + ).collect::>>()?; drop(txindex_to_txid_iter); @@ -713,6 +776,8 @@ impl Indexer { if should_export(height, false) { txindex_to_first_txoutindex_reader_opt.take(); + txoutindex_to_outputtype_reader_opt.take(); + txoutindex_to_typeindex_reader_opt.take(); p2pk65addressindex_to_p2pk65bytes_reader_opt.take(); p2pk33addressindex_to_p2pk33bytes_reader_opt.take(); p2pkhaddressindex_to_p2pkhbytes_reader_opt.take(); @@ -727,6 +792,8 @@ impl Indexer { reset_readers( vecs, &mut txindex_to_first_txoutindex_reader_opt, + &mut txoutindex_to_outputtype_reader_opt, + &mut txoutindex_to_typeindex_reader_opt, &mut p2pk65addressindex_to_p2pk65bytes_reader_opt, &mut p2pk33addressindex_to_p2pk33bytes_reader_opt, &mut p2pkhaddressindex_to_p2pkhbytes_reader_opt, @@ -770,6 +837,6 @@ impl Indexer { #[derive(Debug)] enum InputSource<'a> { - PreviousBlock((Vin, TxIndex, TxOutIndex)), + PreviousBlock((Vin, TxIndex, OutPoint, Option<(OutputType, TypeIndex)>)), SameBlock((&'a Transaction, TxIndex, &'a TxIn, Vin)), } diff --git a/crates/brk_indexer/src/stores.rs b/crates/brk_indexer/src/stores.rs index f7e0db04a..93a065b16 100644 --- a/crates/brk_indexer/src/stores.rs +++ b/crates/brk_indexer/src/stores.rs @@ -1,11 +1,11 @@ -use std::{borrow::Cow, fs, path::Path, thread}; +use std::{borrow::Cow, fs, path::Path}; use brk_error::Result; use brk_grouper::ByAddressType; use brk_store::{AnyStore, Store}; use brk_structs::{ AddressBytes, AddressBytesHash, BlockHashPrefix, Height, OutputType, StoredString, TxIndex, - TxOutIndex, TxidPrefix, TypeIndex, TypeIndexWithOutputindex, Unit, Version, + TxOutIndex, TxidPrefix, TypeIndex, TypeIndexAndOutPoint, TypeIndexAndTxIndex, Unit, Version, }; use fjall::{PersistMode, TransactionalKeyspace}; use rayon::prelude::*; @@ -23,141 +23,90 @@ pub struct Stores { pub blockhashprefix_to_height: Store, pub height_to_coinbase_tag: Store, pub txidprefix_to_txindex: Store, - // Do address_type_to_typeindex_with_txindex_to_vin_and_vout_instead ? - // and should vin be txoutindex or txinindex? - pub addresstype_to_typeindex_with_txoutindex: - ByAddressType>, + pub addresstype_to_typeindex_and_txindex: ByAddressType>, + pub addresstype_to_typeindex_and_unspentoutpoint: + ByAddressType>, } impl Stores { pub fn forced_import(parent: &Path, version: Version) -> Result { - let path = parent.join("stores"); + let pathbuf = parent.join("stores"); + let path = pathbuf.as_path(); - fs::create_dir_all(&path)?; + fs::create_dir_all(&pathbuf)?; - let keyspace = match brk_store::open_keyspace(&path) { + let keyspace = match brk_store::open_keyspace(path) { Ok(keyspace) => keyspace, Err(_) => { - fs::remove_dir_all(&path)?; - return Self::forced_import(&path, version); + fs::remove_dir_all(path)?; + return Self::forced_import(path, version); } }; - thread::scope(|scope| { - let addressbyteshash_to_typeindex = scope.spawn(|| { - Store::import( - &keyspace, - &path, - "addressbyteshash_to_typeindex", - version, - None, - ) - }); - let blockhashprefix_to_height = scope.spawn(|| { - Store::import(&keyspace, &path, "blockhashprefix_to_height", version, None) - }); - let txidprefix_to_txindex = scope - .spawn(|| Store::import(&keyspace, &path, "txidprefix_to_txindex", version, None)); - let p2aaddressindex_with_txoutindex = scope.spawn(|| { - Store::import( - &keyspace, - &path, - "p2aaddressindex_with_txoutindex", - version, - Some(false), - ) - }); - let p2pk33addressindex_with_txoutindex = scope.spawn(|| { - Store::import( - &keyspace, - &path, - "p2pk33addressindex_with_txoutindex", - version, - Some(false), - ) - }); - let p2pk65addressindex_with_txoutindex = scope.spawn(|| { - Store::import( - &keyspace, - &path, - "p2pk65addressindex_with_txoutindex", - version, - Some(false), - ) - }); - let p2pkhaddressindex_with_txoutindex = scope.spawn(|| { - Store::import( - &keyspace, - &path, - "p2pkhaddressindex_with_txoutindex", - version, - Some(false), - ) - }); - let p2shaddressindex_with_txoutindex = scope.spawn(|| { - Store::import( - &keyspace, - &path, - "p2shaddressindex_with_txoutindex", - version, - Some(false), - ) - }); - let p2traddressindex_with_txoutindex = scope.spawn(|| { - Store::import( - &keyspace, - &path, - "p2traddressindex_with_txoutindex", - version, - Some(false), - ) - }); - let p2wpkhaddressindex_with_txoutindex = scope.spawn(|| { - Store::import( - &keyspace, - &path, - "p2wpkhaddressindex_with_txoutindex", - version, - Some(false), - ) - }); - let p2wshaddressindex_with_txoutindex = scope.spawn(|| { - Store::import( - &keyspace, - &path, - "p2wshaddressindex_with_txoutindex", - version, - Some(false), - ) - }); + let keyspace_ref = &keyspace; - let height_to_coinbase_tag = - Store::import(&keyspace, &path, "height_to_coinbase_tag", version, None)?; + let create_addressindex_and_txindex_store = |cohort| { + Store::import( + keyspace_ref, + path, + &format!("{}addressindex_and_txindex", cohort), + version, + Some(false), + ) + }; - Ok(Self { - keyspace: keyspace.clone(), + let create_addressindex_and_unspentoutpoint_store = |cohort| { + Store::import( + keyspace_ref, + path, + &format!("{}addressindex_and_unspentoutpoint", cohort), + version, + Some(false), + ) + }; - height_to_coinbase_tag, - addressbyteshash_to_typeindex: addressbyteshash_to_typeindex.join().unwrap()?, - blockhashprefix_to_height: blockhashprefix_to_height.join().unwrap()?, - txidprefix_to_txindex: txidprefix_to_txindex.join().unwrap()?, - addresstype_to_typeindex_with_txoutindex: ByAddressType { - p2pk65: p2pk65addressindex_with_txoutindex.join().unwrap()?, - p2pk33: p2pk33addressindex_with_txoutindex.join().unwrap()?, - p2pkh: p2pkhaddressindex_with_txoutindex.join().unwrap()?, - p2sh: p2shaddressindex_with_txoutindex.join().unwrap()?, - p2wpkh: p2wpkhaddressindex_with_txoutindex.join().unwrap()?, - p2wsh: p2wshaddressindex_with_txoutindex.join().unwrap()?, - p2tr: p2traddressindex_with_txoutindex.join().unwrap()?, - p2a: p2aaddressindex_with_txoutindex.join().unwrap()?, - }, - }) + Ok(Self { + keyspace: keyspace.clone(), + + height_to_coinbase_tag: Store::import( + keyspace_ref, + path, + "height_to_coinbase_tag", + version, + None, + )?, + addressbyteshash_to_typeindex: Store::import( + keyspace_ref, + path, + "addressbyteshash_to_typeindex", + version, + None, + )?, + blockhashprefix_to_height: Store::import( + keyspace_ref, + path, + "blockhashprefix_to_height", + version, + None, + )?, + txidprefix_to_txindex: Store::import( + keyspace_ref, + path, + "txidprefix_to_txindex", + version, + None, + )?, + addresstype_to_typeindex_and_txindex: ByAddressType::new( + create_addressindex_and_txindex_store, + )?, + addresstype_to_typeindex_and_unspentoutpoint: ByAddressType::new( + create_addressindex_and_unspentoutpoint_store, + )?, }) } pub fn starting_height(&self) -> Height { - self.as_slice() - .into_iter() + self.iter_any_store() .map(|store| { // let height = store.height().map(Height::incremented).unwrap_or_default() @@ -168,8 +117,8 @@ impl Stores { } pub fn commit(&mut self, height: Height) -> Result<()> { - self.as_mut_slice() - .into_par_iter() + self.iter_mut_any_store() + .par_bridge() .try_for_each(|store| store.commit(height))?; self.keyspace @@ -177,38 +126,44 @@ impl Stores { .map_err(|e| e.into()) } - fn as_slice(&self) -> [&(dyn AnyStore + Send + Sync); 12] { + fn iter_any_store(&self) -> impl Iterator { [ - &self.addressbyteshash_to_typeindex, - &self.addresstype_to_typeindex_with_txoutindex.p2a, - &self.addresstype_to_typeindex_with_txoutindex.p2pk33, - &self.addresstype_to_typeindex_with_txoutindex.p2pk65, - &self.addresstype_to_typeindex_with_txoutindex.p2pkh, - &self.addresstype_to_typeindex_with_txoutindex.p2sh, - &self.addresstype_to_typeindex_with_txoutindex.p2tr, - &self.addresstype_to_typeindex_with_txoutindex.p2wpkh, - &self.addresstype_to_typeindex_with_txoutindex.p2wsh, + &self.addressbyteshash_to_typeindex as &dyn AnyStore, &self.blockhashprefix_to_height, &self.height_to_coinbase_tag, &self.txidprefix_to_txindex, ] + .into_iter() + .chain( + self.addresstype_to_typeindex_and_txindex + .iter() + .map(|s| s as &dyn AnyStore), + ) + .chain( + self.addresstype_to_typeindex_and_unspentoutpoint + .iter() + .map(|s| s as &dyn AnyStore), + ) } - fn as_mut_slice(&mut self) -> [&mut (dyn AnyStore + Send + Sync); 12] { + fn iter_mut_any_store(&mut self) -> impl Iterator { [ - &mut self.addressbyteshash_to_typeindex, - &mut self.addresstype_to_typeindex_with_txoutindex.p2a, - &mut self.addresstype_to_typeindex_with_txoutindex.p2pk33, - &mut self.addresstype_to_typeindex_with_txoutindex.p2pk65, - &mut self.addresstype_to_typeindex_with_txoutindex.p2pkh, - &mut self.addresstype_to_typeindex_with_txoutindex.p2sh, - &mut self.addresstype_to_typeindex_with_txoutindex.p2tr, - &mut self.addresstype_to_typeindex_with_txoutindex.p2wpkh, - &mut self.addresstype_to_typeindex_with_txoutindex.p2wsh, + &mut self.addressbyteshash_to_typeindex as &mut dyn AnyStore, &mut self.blockhashprefix_to_height, &mut self.height_to_coinbase_tag, &mut self.txidprefix_to_txindex, ] + .into_iter() + .chain( + self.addresstype_to_typeindex_and_txindex + .iter_mut() + .map(|s| s as &mut dyn AnyStore), + ) + .chain( + self.addresstype_to_typeindex_and_unspentoutpoint + .iter_mut() + .map(|s| s as &mut dyn AnyStore), + ) } pub fn rollback_if_needed( @@ -221,37 +176,19 @@ impl Stores { && self.txidprefix_to_txindex.is_empty()? && self.height_to_coinbase_tag.is_empty()? && self - .addresstype_to_typeindex_with_txoutindex - .p2a - .is_empty()? + .addresstype_to_typeindex_and_txindex + .iter() + .map(|s| s.is_empty()) + .collect::>>()? + .into_iter() + .all(|empty| empty) && self - .addresstype_to_typeindex_with_txoutindex - .p2pk33 - .is_empty()? - && self - .addresstype_to_typeindex_with_txoutindex - .p2pk65 - .is_empty()? - && self - .addresstype_to_typeindex_with_txoutindex - .p2pkh - .is_empty()? - && self - .addresstype_to_typeindex_with_txoutindex - .p2sh - .is_empty()? - && self - .addresstype_to_typeindex_with_txoutindex - .p2tr - .is_empty()? - && self - .addresstype_to_typeindex_with_txoutindex - .p2wpkh - .is_empty()? - && self - .addresstype_to_typeindex_with_txoutindex - .p2wsh - .is_empty()? + .addresstype_to_typeindex_and_unspentoutpoint + .iter() + .map(|s| s.is_empty()) + .collect::>>()? + .into_iter() + .all(|empty| empty) { return Ok(()); } @@ -456,39 +393,28 @@ impl Stores { } if starting_indexes.txoutindex != TxOutIndex::ZERO { - let mut txoutindex_to_typeindex_iter = vecs.txoutindex_to_typeindex.into_iter(); - vecs.txoutindex_to_outputtype - .iter_at(starting_indexes.txoutindex) - .filter(|(_, outputtype)| outputtype.is_address()) - .for_each(|(txoutindex, outputtype)| { - let outputtype = outputtype.into_owned(); + todo!(); + // let mut txoutindex_to_typeindex_iter = vecs.txoutindex_to_typeindex.into_iter(); + // vecs.txoutindex_to_outputtype + // .iter_at(starting_indexes.txoutindex) + // .filter(|(_, outputtype)| outputtype.is_address()) + // .for_each(|(txoutindex, outputtype)| { + // let outputtype = outputtype.into_owned(); - let typeindex = txoutindex_to_typeindex_iter.unwrap_get_inner(txoutindex); + // let typeindex = txoutindex_to_typeindex_iter.unwrap_get_inner(txoutindex); - self.addresstype_to_typeindex_with_txoutindex - .get_mut(outputtype) - .unwrap() - .remove(TypeIndexWithOutputindex::from((typeindex, txoutindex))); - }); + // self.addresstype_to_typeindex_and_unspentoutpoint + // .get_mut(outputtype) + // .unwrap() + // .remove(TypeIndexAndTxIndex::from((typeindex, txoutindex))); + // }); } else { - self.addresstype_to_typeindex_with_txoutindex.p2a.reset()?; - self.addresstype_to_typeindex_with_txoutindex - .p2pk33 - .reset()?; - self.addresstype_to_typeindex_with_txoutindex - .p2pk65 - .reset()?; - self.addresstype_to_typeindex_with_txoutindex - .p2pkh - .reset()?; - self.addresstype_to_typeindex_with_txoutindex.p2sh.reset()?; - self.addresstype_to_typeindex_with_txoutindex.p2tr.reset()?; - self.addresstype_to_typeindex_with_txoutindex - .p2wpkh - .reset()?; - self.addresstype_to_typeindex_with_txoutindex - .p2wsh - .reset()?; + self.addresstype_to_typeindex_and_txindex + .iter_mut() + .try_for_each(|s| s.reset())?; + self.addresstype_to_typeindex_and_unspentoutpoint + .iter_mut() + .try_for_each(|s| s.reset())?; } self.commit(starting_indexes.height.decremented().unwrap_or_default())?; diff --git a/crates/brk_indexer/src/vecs.rs b/crates/brk_indexer/src/vecs.rs index e6285144b..62ac4b0dc 100644 --- a/crates/brk_indexer/src/vecs.rs +++ b/crates/brk_indexer/src/vecs.rs @@ -2,12 +2,12 @@ use std::path::Path; use brk_error::Result; use brk_structs::{ - AddressBytes, BlockHash, EmptyOutputIndex, Height, OpReturnIndex, OutputType, P2AAddressIndex, - P2ABytes, P2MSOutputIndex, P2PK33AddressIndex, P2PK33Bytes, P2PK65AddressIndex, P2PK65Bytes, - P2PKHAddressIndex, P2PKHBytes, P2SHAddressIndex, P2SHBytes, P2TRAddressIndex, P2TRBytes, - P2WPKHAddressIndex, P2WPKHBytes, P2WSHAddressIndex, P2WSHBytes, RawLockTime, Sats, StoredBool, - StoredF64, StoredU32, StoredU64, Timestamp, TxInIndex, TxIndex, TxOutIndex, TxVersion, Txid, - TypeIndex, UnknownOutputIndex, Version, Weight, + AddressBytes, BlockHash, EmptyOutputIndex, Height, OpReturnIndex, OutPoint, OutputType, + P2AAddressIndex, P2ABytes, P2MSOutputIndex, P2PK33AddressIndex, P2PK33Bytes, + P2PK65AddressIndex, P2PK65Bytes, P2PKHAddressIndex, P2PKHBytes, P2SHAddressIndex, P2SHBytes, + P2TRAddressIndex, P2TRBytes, P2WPKHAddressIndex, P2WPKHBytes, P2WSHAddressIndex, P2WSHBytes, + RawLockTime, Sats, StoredBool, StoredF64, StoredU32, StoredU64, Timestamp, TxInIndex, TxIndex, + TxOutIndex, TxVersion, Txid, TypeIndex, UnknownOutputIndex, Version, Weight, }; use brk_traversable::Traversable; use rayon::prelude::*; @@ -40,8 +40,7 @@ pub struct Vecs { pub height_to_timestamp: CompressedVec, pub height_to_total_size: CompressedVec, pub height_to_weight: CompressedVec, - /// If txoutindex == TxOutIndex::MAX then it's coinbase - pub txinindex_to_txoutindex: RawVec, + pub txinindex_to_outpoint: RawVec, pub opreturnindex_to_txindex: CompressedVec, pub txoutindex_to_outputtype: RawVec, pub txoutindex_to_typeindex: RawVec, @@ -152,7 +151,7 @@ impl Vecs { height_to_timestamp: CompressedVec::forced_import(&db, "timestamp", version)?, height_to_total_size: CompressedVec::forced_import(&db, "total_size", version)?, height_to_weight: CompressedVec::forced_import(&db, "weight", version)?, - txinindex_to_txoutindex: RawVec::forced_import(&db, "txoutindex", version)?, + txinindex_to_outpoint: RawVec::forced_import(&db, "outpoint", version)?, opreturnindex_to_txindex: CompressedVec::forced_import(&db, "txindex", version)?, txoutindex_to_outputtype: RawVec::forced_import(&db, "outputtype", version)?, txoutindex_to_typeindex: RawVec::forced_import(&db, "typeindex", version)?, @@ -266,7 +265,7 @@ impl Vecs { .truncate_if_needed_with_stamp(height, stamp)?; self.height_to_weight .truncate_if_needed_with_stamp(height, stamp)?; - self.txinindex_to_txoutindex + self.txinindex_to_outpoint .truncate_if_needed_with_stamp(txinindex, stamp)?; self.opreturnindex_to_txindex .truncate_if_needed_with_stamp(opreturnindex, stamp)?; @@ -347,7 +346,7 @@ impl Vecs { } pub fn flush(&mut self, height: Height) -> Result<()> { - self.iter_mut() + self.iter_mut_any_stored_vec() .par_bridge() .try_for_each(|vec| vec.stamped_flush(Stamp::from(height)))?; self.db.flush()?; @@ -355,7 +354,7 @@ impl Vecs { } pub fn starting_height(&mut self) -> Height { - self.iter_mut() + self.iter_mut_any_stored_vec() .map(|vec| { let h = Height::from(vec.stamp()); if h > Height::ZERO { h.incremented() } else { h } @@ -419,7 +418,7 @@ impl Vecs { // .into_iter() // } - fn iter_mut(&mut self) -> impl Iterator { + fn iter_mut_any_stored_vec(&mut self) -> impl Iterator { [ &mut self.emptyoutputindex_to_txindex as &mut dyn AnyStoredVec, &mut self.height_to_blockhash, @@ -442,7 +441,7 @@ impl Vecs { &mut self.height_to_timestamp, &mut self.height_to_total_size, &mut self.height_to_weight, - &mut self.txinindex_to_txoutindex, + &mut self.txinindex_to_outpoint, &mut self.opreturnindex_to_txindex, &mut self.txoutindex_to_outputtype, &mut self.txoutindex_to_typeindex, diff --git a/crates/brk_store/src/any.rs b/crates/brk_store/src/any.rs index d116af82e..f4eff3c87 100644 --- a/crates/brk_store/src/any.rs +++ b/crates/brk_store/src/any.rs @@ -1,7 +1,7 @@ use brk_error::Result; use brk_structs::{Height, Version}; -pub trait AnyStore { +pub trait AnyStore: Send + Sync { fn commit(&mut self, height: Height) -> Result<()>; fn persist(&self) -> Result<()>; fn reset(&mut self) -> Result<()>; diff --git a/crates/brk_store/src/lib.rs b/crates/brk_store/src/lib.rs index bbaa32d88..462f7c22d 100644 --- a/crates/brk_store/src/lib.rs +++ b/crates/brk_store/src/lib.rs @@ -46,11 +46,11 @@ pub fn open_keyspace(path: &Path) -> fjall::Result { .open_transactional() } -impl<'a, K, V> Store +impl Store where - K: Debug + Clone + From + Ord + 'a, + K: Debug + Clone + From + Ord, V: Debug + Clone + From, - ByteView: From + From<&'a K> + From, + ByteView: From + From, { pub fn import( keyspace: &TransactionalKeyspace, @@ -87,7 +87,10 @@ where }) } - pub fn get(&'_ self, key: &'a K) -> Result>> { + pub fn get<'a>(&'a self, key: &'a K) -> Result>> + where + ByteView: From<&'a K>, + { if let Some(v) = self.puts.get(key) { Ok(Some(Cow::Borrowed(v))) } else if let Some(slice) = self @@ -159,18 +162,24 @@ where // return Ok(()); // } - if !self.puts.is_empty() { - unreachable!("Shouldn't reach this"); - } + // if !self.puts.is_empty() { + // unreachable!("Shouldn't reach this"); + // } - if !self.dels.insert(key.clone()) { - dbg!(key, &self.meta.path()); + if (self.puts.is_empty() || self.puts.remove(&key).is_none()) && !self.dels.insert(key) { + dbg!(&self.meta.path()); unreachable!(); } // Ok(()) } + pub fn remove_if_needed(&mut self, key: K, height: Height) { + if self.needs(height) { + self.remove(key) + } + } + // pub fn retain_or_del(&mut self, retain: F) // where // F: Fn(&K, &mut V) -> bool, @@ -245,13 +254,22 @@ where Ok(()) } + + fn has(&self, height: Height) -> bool { + self.meta.has(height) + } + + fn needs(&self, height: Height) -> bool { + self.meta.needs(height) + } } -impl<'a, K, V> AnyStore for Store +impl AnyStore for Store where - K: Debug + Clone + From + Ord + 'a, + K: Debug + Clone + From + Ord, V: Debug + Clone + From, - ByteView: From + From<&'a K> + From, + ByteView: From + From, + Self: Send + Sync, { fn commit(&mut self, height: Height) -> Result<()> { if self.puts.is_empty() && self.dels.is_empty() { @@ -298,10 +316,11 @@ where } fn has(&self, height: Height) -> bool { - self.meta.has(height) + self.has(height) } + fn needs(&self, height: Height) -> bool { - self.meta.needs(height) + self.needs(height) } fn version(&self) -> Version { diff --git a/crates/brk_structs/src/lib.rs b/crates/brk_structs/src/lib.rs index d7701ffaf..e024916a8 100644 --- a/crates/brk_structs/src/lib.rs +++ b/crates/brk_structs/src/lib.rs @@ -43,6 +43,7 @@ mod metrics; mod monthindex; mod ohlc; mod opreturnindex; +mod outpoint; mod outputtype; mod p2aaddressindex; mod p2abytes; @@ -91,7 +92,8 @@ mod txoutindex; mod txstatus; mod txversion; mod typeindex; -mod typeindex_with_txoutindex; +mod typeindexandoutpoint; +mod typeindexandtxindex; mod unit; mod unknownoutputindex; mod vin; @@ -139,6 +141,7 @@ pub use metrics::*; pub use monthindex::*; pub use ohlc::*; pub use opreturnindex::*; +pub use outpoint::*; pub use outputtype::*; pub use p2aaddressindex::*; pub use p2abytes::*; @@ -187,7 +190,8 @@ pub use txoutindex::*; pub use txstatus::*; pub use txversion::*; pub use typeindex::*; -pub use typeindex_with_txoutindex::*; +pub use typeindexandoutpoint::*; +pub use typeindexandtxindex::*; pub use unit::*; pub use unknownoutputindex::*; pub use vin::*; @@ -196,6 +200,19 @@ pub use weekindex::*; pub use weight::*; pub use yearindex::*; +#[allow(clippy::result_unit_err)] +pub fn copy_first_2bytes(slice: &[u8]) -> Result<[u8; 2]> { + let mut buf: [u8; 2] = [0; 2]; + let buf_len = buf.len(); + if slice.len() < buf_len { + return Err(Error::Str("Buffer is too small to convert to 8 bytes")); + } + slice.iter().take(buf_len).enumerate().for_each(|(i, r)| { + buf[i] = *r; + }); + Ok(buf) +} + #[allow(clippy::result_unit_err)] pub fn copy_first_4bytes(slice: &[u8]) -> Result<[u8; 4]> { let mut buf: [u8; 4] = [0; 4]; diff --git a/crates/brk_structs/src/outpoint.rs b/crates/brk_structs/src/outpoint.rs new file mode 100644 index 000000000..97e973ecf --- /dev/null +++ b/crates/brk_structs/src/outpoint.rs @@ -0,0 +1,79 @@ +use allocative::Allocative; +use schemars::JsonSchema; +use serde::Serialize; +use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout}; + +use crate::{TxIndex, Vout}; + +#[derive( + Debug, + PartialEq, + Eq, + PartialOrd, + Ord, + Clone, + Copy, + Default, + FromBytes, + Immutable, + IntoBytes, + KnownLayout, + Serialize, + Allocative, + JsonSchema, +)] +pub struct OutPoint { + txindex: TxIndex, + vout: Vout, + _padding: u16, +} + +impl OutPoint { + pub const COINBASE: Self = Self { + txindex: TxIndex::COINBASE, + vout: Vout::MAX, + _padding: 0, + }; + + pub fn new(txindex: TxIndex, vout: Vout) -> Self { + Self { + txindex, + vout, + _padding: 0, + } + } + + pub fn txindex(&self) -> TxIndex { + self.txindex + } + + pub fn vout(&self) -> Vout { + self.vout + } + + pub fn is_coinbase(self) -> bool { + self == Self::COINBASE + } + + pub fn to_be_bytes(&self) -> [u8; 6] { + let txindex = self.txindex.to_be_bytes(); + let vout = self.vout.to_be_bytes(); + [ + txindex[0], txindex[1], txindex[2], txindex[3], vout[0], vout[1], + ] + } +} + +impl From<&[u8]> for OutPoint { + fn from(value: &[u8]) -> Self { + let txindex = TxIndex::from(&value[4..8]); + let vout = Vout::from(&value[8..10]); + Self::new(txindex, vout) + } +} + +impl std::fmt::Display for OutPoint { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "txindex: {}, vout: {}", self.txindex, self.vout) + } +} diff --git a/crates/brk_structs/src/outputtype.rs b/crates/brk_structs/src/outputtype.rs index fef1a694e..4732fefcc 100644 --- a/crates/brk_structs/src/outputtype.rs +++ b/crates/brk_structs/src/outputtype.rs @@ -36,492 +36,736 @@ pub enum OutputType { P2WSH, P2TR, P2A, + #[doc(hidden)] #[schemars(skip)] Dummy10, + #[doc(hidden)] #[schemars(skip)] Dummy11, + #[doc(hidden)] #[schemars(skip)] Dummy12, + #[doc(hidden)] #[schemars(skip)] Dummy13, + #[doc(hidden)] #[schemars(skip)] Dummy14, + #[doc(hidden)] #[schemars(skip)] Dummy15, + #[doc(hidden)] #[schemars(skip)] Dummy16, + #[doc(hidden)] #[schemars(skip)] Dummy17, + #[doc(hidden)] #[schemars(skip)] Dummy18, + #[doc(hidden)] #[schemars(skip)] Dummy19, + #[doc(hidden)] #[schemars(skip)] Dummy20, + #[doc(hidden)] #[schemars(skip)] Dummy21, + #[doc(hidden)] #[schemars(skip)] Dummy22, + #[doc(hidden)] #[schemars(skip)] Dummy23, + #[doc(hidden)] #[schemars(skip)] Dummy24, + #[doc(hidden)] #[schemars(skip)] Dummy25, + #[doc(hidden)] #[schemars(skip)] Dummy26, + #[doc(hidden)] #[schemars(skip)] Dummy27, + #[doc(hidden)] #[schemars(skip)] Dummy28, + #[doc(hidden)] #[schemars(skip)] Dummy29, + #[doc(hidden)] #[schemars(skip)] Dummy30, + #[doc(hidden)] #[schemars(skip)] Dummy31, + #[doc(hidden)] #[schemars(skip)] Dummy32, + #[doc(hidden)] #[schemars(skip)] Dummy33, + #[doc(hidden)] #[schemars(skip)] Dummy34, + #[doc(hidden)] #[schemars(skip)] Dummy35, + #[doc(hidden)] #[schemars(skip)] Dummy36, + #[doc(hidden)] #[schemars(skip)] Dummy37, + #[doc(hidden)] #[schemars(skip)] Dummy38, + #[doc(hidden)] #[schemars(skip)] Dummy39, + #[doc(hidden)] #[schemars(skip)] Dummy40, + #[doc(hidden)] #[schemars(skip)] Dummy41, + #[doc(hidden)] #[schemars(skip)] Dummy42, + #[doc(hidden)] #[schemars(skip)] Dummy43, + #[doc(hidden)] #[schemars(skip)] Dummy44, + #[doc(hidden)] #[schemars(skip)] Dummy45, + #[doc(hidden)] #[schemars(skip)] Dummy46, + #[doc(hidden)] #[schemars(skip)] Dummy47, + #[doc(hidden)] #[schemars(skip)] Dummy48, + #[doc(hidden)] #[schemars(skip)] Dummy49, + #[doc(hidden)] #[schemars(skip)] Dummy50, + #[doc(hidden)] #[schemars(skip)] Dummy51, + #[doc(hidden)] #[schemars(skip)] Dummy52, + #[doc(hidden)] #[schemars(skip)] Dummy53, + #[doc(hidden)] #[schemars(skip)] Dummy54, + #[doc(hidden)] #[schemars(skip)] Dummy55, + #[doc(hidden)] #[schemars(skip)] Dummy56, + #[doc(hidden)] #[schemars(skip)] Dummy57, + #[doc(hidden)] #[schemars(skip)] Dummy58, + #[doc(hidden)] #[schemars(skip)] Dummy59, + #[doc(hidden)] #[schemars(skip)] Dummy60, + #[doc(hidden)] #[schemars(skip)] Dummy61, + #[doc(hidden)] #[schemars(skip)] Dummy62, + #[doc(hidden)] #[schemars(skip)] Dummy63, + #[doc(hidden)] #[schemars(skip)] Dummy64, + #[doc(hidden)] #[schemars(skip)] Dummy65, + #[doc(hidden)] #[schemars(skip)] Dummy66, + #[doc(hidden)] #[schemars(skip)] Dummy67, + #[doc(hidden)] #[schemars(skip)] Dummy68, + #[doc(hidden)] #[schemars(skip)] Dummy69, + #[doc(hidden)] #[schemars(skip)] Dummy70, + #[doc(hidden)] #[schemars(skip)] Dummy71, + #[doc(hidden)] #[schemars(skip)] Dummy72, + #[doc(hidden)] #[schemars(skip)] Dummy73, + #[doc(hidden)] #[schemars(skip)] Dummy74, + #[doc(hidden)] #[schemars(skip)] Dummy75, + #[doc(hidden)] #[schemars(skip)] Dummy76, + #[doc(hidden)] #[schemars(skip)] Dummy77, + #[doc(hidden)] #[schemars(skip)] Dummy78, + #[doc(hidden)] #[schemars(skip)] Dummy79, + #[doc(hidden)] #[schemars(skip)] Dummy80, + #[doc(hidden)] #[schemars(skip)] Dummy81, + #[doc(hidden)] #[schemars(skip)] Dummy82, + #[doc(hidden)] #[schemars(skip)] Dummy83, + #[doc(hidden)] #[schemars(skip)] Dummy84, + #[doc(hidden)] #[schemars(skip)] Dummy85, + #[doc(hidden)] #[schemars(skip)] Dummy86, + #[doc(hidden)] #[schemars(skip)] Dummy87, + #[doc(hidden)] #[schemars(skip)] Dummy88, + #[doc(hidden)] #[schemars(skip)] Dummy89, + #[doc(hidden)] #[schemars(skip)] Dummy90, + #[doc(hidden)] #[schemars(skip)] Dummy91, + #[doc(hidden)] #[schemars(skip)] Dummy92, + #[doc(hidden)] #[schemars(skip)] Dummy93, + #[doc(hidden)] #[schemars(skip)] Dummy94, + #[doc(hidden)] #[schemars(skip)] Dummy95, + #[doc(hidden)] #[schemars(skip)] Dummy96, + #[doc(hidden)] #[schemars(skip)] Dummy97, + #[doc(hidden)] #[schemars(skip)] Dummy98, + #[doc(hidden)] #[schemars(skip)] Dummy99, + #[doc(hidden)] #[schemars(skip)] Dummy100, + #[doc(hidden)] #[schemars(skip)] Dummy101, + #[doc(hidden)] #[schemars(skip)] Dummy102, + #[doc(hidden)] #[schemars(skip)] Dummy103, + #[doc(hidden)] #[schemars(skip)] Dummy104, + #[doc(hidden)] #[schemars(skip)] Dummy105, + #[doc(hidden)] #[schemars(skip)] Dummy106, + #[doc(hidden)] #[schemars(skip)] Dummy107, + #[doc(hidden)] #[schemars(skip)] Dummy108, + #[doc(hidden)] #[schemars(skip)] Dummy109, + #[doc(hidden)] #[schemars(skip)] Dummy110, + #[doc(hidden)] #[schemars(skip)] Dummy111, + #[doc(hidden)] #[schemars(skip)] Dummy112, + #[doc(hidden)] #[schemars(skip)] Dummy113, + #[doc(hidden)] #[schemars(skip)] Dummy114, + #[doc(hidden)] #[schemars(skip)] Dummy115, + #[doc(hidden)] #[schemars(skip)] Dummy116, + #[doc(hidden)] #[schemars(skip)] Dummy117, + #[doc(hidden)] #[schemars(skip)] Dummy118, + #[doc(hidden)] #[schemars(skip)] Dummy119, + #[doc(hidden)] #[schemars(skip)] Dummy120, + #[doc(hidden)] #[schemars(skip)] Dummy121, + #[doc(hidden)] #[schemars(skip)] Dummy122, + #[doc(hidden)] #[schemars(skip)] Dummy123, + #[doc(hidden)] #[schemars(skip)] Dummy124, + #[doc(hidden)] #[schemars(skip)] Dummy125, + #[doc(hidden)] #[schemars(skip)] Dummy126, + #[doc(hidden)] #[schemars(skip)] Dummy127, + #[doc(hidden)] #[schemars(skip)] Dummy128, + #[doc(hidden)] #[schemars(skip)] Dummy129, + #[doc(hidden)] #[schemars(skip)] Dummy130, + #[doc(hidden)] #[schemars(skip)] Dummy131, + #[doc(hidden)] #[schemars(skip)] Dummy132, + #[doc(hidden)] #[schemars(skip)] Dummy133, + #[doc(hidden)] #[schemars(skip)] Dummy134, + #[doc(hidden)] #[schemars(skip)] Dummy135, + #[doc(hidden)] #[schemars(skip)] Dummy136, + #[doc(hidden)] #[schemars(skip)] Dummy137, + #[doc(hidden)] #[schemars(skip)] Dummy138, + #[doc(hidden)] #[schemars(skip)] Dummy139, + #[doc(hidden)] #[schemars(skip)] Dummy140, + #[doc(hidden)] #[schemars(skip)] Dummy141, + #[doc(hidden)] #[schemars(skip)] Dummy142, + #[doc(hidden)] #[schemars(skip)] Dummy143, + #[doc(hidden)] #[schemars(skip)] Dummy144, + #[doc(hidden)] #[schemars(skip)] Dummy145, + #[doc(hidden)] #[schemars(skip)] Dummy146, + #[doc(hidden)] #[schemars(skip)] Dummy147, + #[doc(hidden)] #[schemars(skip)] Dummy148, + #[doc(hidden)] #[schemars(skip)] Dummy149, + #[doc(hidden)] #[schemars(skip)] Dummy150, + #[doc(hidden)] #[schemars(skip)] Dummy151, + #[doc(hidden)] #[schemars(skip)] Dummy152, + #[doc(hidden)] #[schemars(skip)] Dummy153, + #[doc(hidden)] #[schemars(skip)] Dummy154, + #[doc(hidden)] #[schemars(skip)] Dummy155, + #[doc(hidden)] #[schemars(skip)] Dummy156, + #[doc(hidden)] #[schemars(skip)] Dummy157, + #[doc(hidden)] #[schemars(skip)] Dummy158, + #[doc(hidden)] #[schemars(skip)] Dummy159, + #[doc(hidden)] #[schemars(skip)] Dummy160, + #[doc(hidden)] #[schemars(skip)] Dummy161, + #[doc(hidden)] #[schemars(skip)] Dummy162, + #[doc(hidden)] #[schemars(skip)] Dummy163, + #[doc(hidden)] #[schemars(skip)] Dummy164, + #[doc(hidden)] #[schemars(skip)] Dummy165, + #[doc(hidden)] #[schemars(skip)] Dummy166, + #[doc(hidden)] #[schemars(skip)] Dummy167, + #[doc(hidden)] #[schemars(skip)] Dummy168, + #[doc(hidden)] #[schemars(skip)] Dummy169, + #[doc(hidden)] #[schemars(skip)] Dummy170, + #[doc(hidden)] #[schemars(skip)] Dummy171, + #[doc(hidden)] #[schemars(skip)] Dummy172, + #[doc(hidden)] #[schemars(skip)] Dummy173, + #[doc(hidden)] #[schemars(skip)] Dummy174, + #[doc(hidden)] #[schemars(skip)] Dummy175, + #[doc(hidden)] #[schemars(skip)] Dummy176, + #[doc(hidden)] #[schemars(skip)] Dummy177, + #[doc(hidden)] #[schemars(skip)] Dummy178, + #[doc(hidden)] #[schemars(skip)] Dummy179, + #[doc(hidden)] #[schemars(skip)] Dummy180, + #[doc(hidden)] #[schemars(skip)] Dummy181, + #[doc(hidden)] #[schemars(skip)] Dummy182, + #[doc(hidden)] #[schemars(skip)] Dummy183, + #[doc(hidden)] #[schemars(skip)] Dummy184, + #[doc(hidden)] #[schemars(skip)] Dummy185, + #[doc(hidden)] #[schemars(skip)] Dummy186, + #[doc(hidden)] #[schemars(skip)] Dummy187, + #[doc(hidden)] #[schemars(skip)] Dummy188, + #[doc(hidden)] #[schemars(skip)] Dummy189, + #[doc(hidden)] #[schemars(skip)] Dummy190, + #[doc(hidden)] #[schemars(skip)] Dummy191, + #[doc(hidden)] #[schemars(skip)] Dummy192, + #[doc(hidden)] #[schemars(skip)] Dummy193, + #[doc(hidden)] #[schemars(skip)] Dummy194, + #[doc(hidden)] #[schemars(skip)] Dummy195, + #[doc(hidden)] #[schemars(skip)] Dummy196, + #[doc(hidden)] #[schemars(skip)] Dummy197, + #[doc(hidden)] #[schemars(skip)] Dummy198, + #[doc(hidden)] #[schemars(skip)] Dummy199, + #[doc(hidden)] #[schemars(skip)] Dummy200, + #[doc(hidden)] #[schemars(skip)] Dummy201, + #[doc(hidden)] #[schemars(skip)] Dummy202, + #[doc(hidden)] #[schemars(skip)] Dummy203, + #[doc(hidden)] #[schemars(skip)] Dummy204, + #[doc(hidden)] #[schemars(skip)] Dummy205, + #[doc(hidden)] #[schemars(skip)] Dummy206, + #[doc(hidden)] #[schemars(skip)] Dummy207, + #[doc(hidden)] #[schemars(skip)] Dummy208, + #[doc(hidden)] #[schemars(skip)] Dummy209, + #[doc(hidden)] #[schemars(skip)] Dummy210, + #[doc(hidden)] #[schemars(skip)] Dummy211, + #[doc(hidden)] #[schemars(skip)] Dummy212, + #[doc(hidden)] #[schemars(skip)] Dummy213, + #[doc(hidden)] #[schemars(skip)] Dummy214, + #[doc(hidden)] #[schemars(skip)] Dummy215, + #[doc(hidden)] #[schemars(skip)] Dummy216, + #[doc(hidden)] #[schemars(skip)] Dummy217, + #[doc(hidden)] #[schemars(skip)] Dummy218, + #[doc(hidden)] #[schemars(skip)] Dummy219, + #[doc(hidden)] #[schemars(skip)] Dummy220, + #[doc(hidden)] #[schemars(skip)] Dummy221, + #[doc(hidden)] #[schemars(skip)] Dummy222, + #[doc(hidden)] #[schemars(skip)] Dummy223, + #[doc(hidden)] #[schemars(skip)] Dummy224, + #[doc(hidden)] #[schemars(skip)] Dummy225, + #[doc(hidden)] #[schemars(skip)] Dummy226, + #[doc(hidden)] #[schemars(skip)] Dummy227, + #[doc(hidden)] #[schemars(skip)] Dummy228, + #[doc(hidden)] #[schemars(skip)] Dummy229, + #[doc(hidden)] #[schemars(skip)] Dummy230, + #[doc(hidden)] #[schemars(skip)] Dummy231, + #[doc(hidden)] #[schemars(skip)] Dummy232, + #[doc(hidden)] #[schemars(skip)] Dummy233, + #[doc(hidden)] #[schemars(skip)] Dummy234, + #[doc(hidden)] #[schemars(skip)] Dummy235, + #[doc(hidden)] #[schemars(skip)] Dummy236, + #[doc(hidden)] #[schemars(skip)] Dummy237, + #[doc(hidden)] #[schemars(skip)] Dummy238, + #[doc(hidden)] #[schemars(skip)] Dummy239, + #[doc(hidden)] #[schemars(skip)] Dummy240, + #[doc(hidden)] #[schemars(skip)] Dummy241, + #[doc(hidden)] #[schemars(skip)] Dummy242, + #[doc(hidden)] #[schemars(skip)] Dummy243, + #[doc(hidden)] #[schemars(skip)] Dummy244, + #[doc(hidden)] #[schemars(skip)] Dummy245, + #[doc(hidden)] #[schemars(skip)] Dummy246, + #[doc(hidden)] #[schemars(skip)] Dummy247, + #[doc(hidden)] #[schemars(skip)] Dummy248, + #[doc(hidden)] #[schemars(skip)] Dummy249, + #[doc(hidden)] #[schemars(skip)] Dummy250, + #[doc(hidden)] #[schemars(skip)] Dummy251, + #[doc(hidden)] #[schemars(skip)] Dummy252, + #[doc(hidden)] #[schemars(skip)] Dummy253, Empty = 254, diff --git a/crates/brk_structs/src/txindex.rs b/crates/brk_structs/src/txindex.rs index 2ec0f3821..eaf237416 100644 --- a/crates/brk_structs/src/txindex.rs +++ b/crates/brk_structs/src/txindex.rs @@ -36,6 +36,7 @@ pub struct TxIndex(u32); impl TxIndex { pub const ZERO: Self = Self(0); + pub const COINBASE: Self = Self(u32::MAX); pub fn new(txindex: u32) -> Self { Self(txindex) @@ -44,6 +45,10 @@ impl TxIndex { pub fn incremented(self) -> Self { Self(*self + 1) } + + pub fn to_be_bytes(&self) -> [u8; 4] { + self.0.to_be_bytes() + } } impl Add for TxIndex { @@ -78,6 +83,12 @@ impl From for TxIndex { } } +impl From for u32 { + fn from(value: TxIndex) -> Self { + value.0 + } +} + impl From for TxIndex { fn from(value: u64) -> Self { Self(value as u32) @@ -117,6 +128,12 @@ impl From for StoredU32 { } } +impl From<&[u8]> for TxIndex { + fn from(value: &[u8]) -> Self { + Self(u32::from_be_bytes(copy_first_4bytes(value).unwrap())) + } +} + impl PrintableIndex for TxIndex { fn to_string() -> &'static str { "txindex" diff --git a/crates/brk_structs/src/txinindex.rs b/crates/brk_structs/src/txinindex.rs index d080fa10d..edf8f90da 100644 --- a/crates/brk_structs/src/txinindex.rs +++ b/crates/brk_structs/src/txinindex.rs @@ -105,7 +105,7 @@ impl PrintableIndex for TxInIndex { } fn to_possible_strings() -> &'static [&'static str] { - &["in", "txinindex"] + &["txi", "txin", "txinindex"] } } diff --git a/crates/brk_structs/src/txoutindex.rs b/crates/brk_structs/src/txoutindex.rs index cabb7bfb9..a76d6e2cc 100644 --- a/crates/brk_structs/src/txoutindex.rs +++ b/crates/brk_structs/src/txoutindex.rs @@ -121,7 +121,7 @@ impl PrintableIndex for TxOutIndex { } fn to_possible_strings() -> &'static [&'static str] { - &["out", "txoutindex"] + &["txo", "txout", "txoutindex"] } } diff --git a/crates/brk_structs/src/typeindex_with_txoutindex.rs b/crates/brk_structs/src/typeindex_with_txoutindex.rs deleted file mode 100644 index 80313537c..000000000 --- a/crates/brk_structs/src/typeindex_with_txoutindex.rs +++ /dev/null @@ -1,47 +0,0 @@ -use byteview::ByteView; -use serde::Serialize; - -use super::{TxOutIndex, TypeIndex}; - -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default, Serialize)] -pub struct TypeIndexWithOutputindex { - typeindex: TypeIndex, - txoutindex: TxOutIndex, -} - -impl From<(TypeIndex, TxOutIndex)> for TypeIndexWithOutputindex { - fn from(value: (TypeIndex, TxOutIndex)) -> Self { - Self { - typeindex: value.0, - txoutindex: value.1, - } - } -} - -impl From for TypeIndexWithOutputindex { - fn from(value: ByteView) -> Self { - let typeindex = TypeIndex::from(&value[0..4]); - let txoutindex = TxOutIndex::from(&value[4..12]); - Self { - typeindex, - txoutindex, - } - } -} - -impl From for ByteView { - fn from(value: TypeIndexWithOutputindex) -> Self { - ByteView::from(&value) - } -} -impl From<&TypeIndexWithOutputindex> for ByteView { - fn from(value: &TypeIndexWithOutputindex) -> Self { - ByteView::from( - [ - u32::from(value.typeindex).to_be_bytes().as_slice(), - u64::from(value.txoutindex).to_be_bytes().as_slice(), - ] - .concat(), - ) - } -} diff --git a/crates/brk_structs/src/typeindexandoutpoint.rs b/crates/brk_structs/src/typeindexandoutpoint.rs new file mode 100644 index 000000000..61a6479e5 --- /dev/null +++ b/crates/brk_structs/src/typeindexandoutpoint.rs @@ -0,0 +1,45 @@ +use byteview::ByteView; +use serde::Serialize; + +use super::{OutPoint, TypeIndex}; + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default, Serialize)] +pub struct TypeIndexAndOutPoint { + typeindex: TypeIndex, + outpoint: OutPoint, +} + +impl From<(TypeIndex, OutPoint)> for TypeIndexAndOutPoint { + fn from(value: (TypeIndex, OutPoint)) -> Self { + Self { + typeindex: value.0, + outpoint: value.1, + } + } +} + +impl From for TypeIndexAndOutPoint { + fn from(value: ByteView) -> Self { + Self { + typeindex: TypeIndex::from(&value[0..4]), + outpoint: OutPoint::from(&value[4..]), + } + } +} + +impl From for ByteView { + fn from(value: TypeIndexAndOutPoint) -> Self { + ByteView::from(&value) + } +} +impl From<&TypeIndexAndOutPoint> for ByteView { + fn from(value: &TypeIndexAndOutPoint) -> Self { + ByteView::from( + [ + u32::from(value.typeindex).to_be_bytes().as_slice(), + value.outpoint.to_be_bytes().as_slice(), + ] + .concat(), + ) + } +} diff --git a/crates/brk_structs/src/typeindexandtxindex.rs b/crates/brk_structs/src/typeindexandtxindex.rs new file mode 100644 index 000000000..1f0027013 --- /dev/null +++ b/crates/brk_structs/src/typeindexandtxindex.rs @@ -0,0 +1,44 @@ +use byteview::ByteView; +use serde::Serialize; + +use super::{TxIndex, TypeIndex}; + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default, Serialize)] +pub struct TypeIndexAndTxIndex { + typeindex: TypeIndex, + txindex: TxIndex, +} + +impl From<(TypeIndex, TxIndex)> for TypeIndexAndTxIndex { + fn from(value: (TypeIndex, TxIndex)) -> Self { + Self { + typeindex: value.0, + txindex: value.1, + } + } +} + +impl From for TypeIndexAndTxIndex { + fn from(value: ByteView) -> Self { + let typeindex = TypeIndex::from(&value[0..4]); + let txindex = TxIndex::from(&value[4..8]); + Self { typeindex, txindex } + } +} + +impl From for ByteView { + fn from(value: TypeIndexAndTxIndex) -> Self { + ByteView::from(&value) + } +} +impl From<&TypeIndexAndTxIndex> for ByteView { + fn from(value: &TypeIndexAndTxIndex) -> Self { + ByteView::from( + [ + u32::from(value.typeindex).to_be_bytes().as_slice(), + u32::from(value.txindex).to_be_bytes().as_slice(), + ] + .concat(), + ) + } +} diff --git a/crates/brk_structs/src/vin.rs b/crates/brk_structs/src/vin.rs index a26e0cfa1..ebaf27c1f 100644 --- a/crates/brk_structs/src/vin.rs +++ b/crates/brk_structs/src/vin.rs @@ -1,7 +1,7 @@ use derive_deref::Deref; #[derive(Debug, Deref, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub struct Vin(u32); +pub struct Vin(u16); impl Vin { pub const ZERO: Self = Vin(0); @@ -12,15 +12,23 @@ impl Vin { } } +const U16_MAX_AS_U32: u32 = u16::MAX as u32; impl From for Vin { fn from(value: u32) -> Self { - Self(value) + if value > U16_MAX_AS_U32 { + panic!() + } + Self(value as u16) } } +const U16_MAX_AS_USIZE: usize = u16::MAX as usize; impl From for Vin { fn from(value: usize) -> Self { - Self(value as u32) + if value > U16_MAX_AS_USIZE { + panic!() + } + Self(value as u16) } } diff --git a/crates/brk_structs/src/vout.rs b/crates/brk_structs/src/vout.rs index 01b9dd770..ac6583843 100644 --- a/crates/brk_structs/src/vout.rs +++ b/crates/brk_structs/src/vout.rs @@ -1,28 +1,62 @@ +use allocative::Allocative; use derive_deref::Deref; use schemars::JsonSchema; use serde::Serialize; +use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout}; + +use crate::copy_first_2bytes; /// Index of the output being spent in the previous transaction -#[derive(Debug, Deref, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, JsonSchema)] -pub struct Vout(u32); +#[derive( + Debug, + Default, + Deref, + Clone, + Copy, + PartialEq, + Eq, + PartialOrd, + Ord, + FromBytes, + Immutable, + IntoBytes, + KnownLayout, + Serialize, + Allocative, + JsonSchema, +)] +pub struct Vout(u16); impl Vout { - const ZERO: Self = Vout(0); + pub const ZERO: Self = Vout(0); + pub const MAX: Self = Vout(u16::MAX); pub fn is_zero(&self) -> bool { *self == Self::ZERO } -} -impl From for Vout { - fn from(value: u32) -> Self { - Self(value) + pub fn to_be_bytes(&self) -> [u8; 2] { + self.0.to_be_bytes() } } +const U16_MAX_AS_U32: u32 = u16::MAX as u32; +impl From for Vout { + fn from(value: u32) -> Self { + if value > U16_MAX_AS_U32 { + panic!() + } + Self(value as u16) + } +} + +const U16_MAX_AS_USIZE: usize = u16::MAX as usize; impl From for Vout { fn from(value: usize) -> Self { - Self(value as u32) + if value > U16_MAX_AS_USIZE { + panic!() + } + Self(value as u16) } } @@ -31,3 +65,15 @@ impl From for u64 { value.0 as u64 } } + +impl From<&[u8]> for Vout { + fn from(value: &[u8]) -> Self { + Self(u16::from_be_bytes(copy_first_2bytes(value).unwrap())) + } +} + +impl std::fmt::Display for Vout { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} diff --git a/websites/bitview/index.html b/websites/bitview/index.html index 9c6f086fe..f09b310e7 100644 --- a/websites/bitview/index.html +++ b/websites/bitview/index.html @@ -3,6 +3,7 @@ +