indexer: moved to addri->txindex and addri->outpoint indexing from addri->txoutindex

This commit is contained in:
nym21
2025-10-17 01:02:26 +02:00
parent d3b8520c41
commit 6cce92af22
22 changed files with 940 additions and 447 deletions

1
Cargo.lock generated
View File

@@ -683,6 +683,7 @@ dependencies = [
name = "brk_grouper"
version = "0.0.111"
dependencies = [
"brk_error",
"brk_structs",
"brk_traversable",
"vecdb",

View File

@@ -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 }

View File

@@ -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<T> {
pub p2pk65: T,
@@ -19,6 +29,22 @@ pub struct ByAddressType<T> {
}
impl<T> ByAddressType<T> {
pub fn new<F>(f: F) -> Result<Self>
where
F: Fn(&'static str) -> Result<T>,
{
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<T: Traversable> Traversable for ByAddressType<T> {
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()))

View File

@@ -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));
}
}

View File

@@ -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(""))?;

View File

@@ -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<Reader<'static>>,
txoutindex_to_outputtype_reader_opt: &mut Option<Reader<'static>>,
txoutindex_to_typeindex_reader_opt: &mut Option<Reader<'static>>,
p2pk65addressindex_to_p2pk65bytes_reader_opt: &mut Option<Reader<'static>>,
p2pk33addressindex_to_p2pk33bytes_reader_opt: &mut Option<Reader<'static>>,
p2pkhaddressindex_to_p2pkhbytes_reader_opt: &mut Option<Reader<'static>>,
@@ -125,6 +136,10 @@ impl Indexer {
p2aaddressindex_to_p2abytes_reader_opt: &mut Option<Reader<'static>>| {
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::<Vec<_>>();
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::<Vec<_>>();
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<AddressBytesHash, TypeIndex> = 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<OutPoint, (OutputType, TypeIndex)> =
txoutindex_to_txoutdata
.into_iter()
.flat_map(|(
txoutindex,
(txout, txindex, vout, outputtype, addressbytes_res, typeindex_opt, _tx),
)| {
let result: Result<Option<(OutPoint, (OutputType, TypeIndex))>> = (|| {
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::<Result<BTreeMap<_, _>>>()?;
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<TxIndex, (&Transaction, Txid)> = 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::<Result<BTreeMap<_, _>>>()?;
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)),
}

View File

@@ -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<BlockHashPrefix, Height>,
pub height_to_coinbase_tag: Store<Height, StoredString>,
pub txidprefix_to_txindex: Store<TxidPrefix, TxIndex>,
// 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<Store<TypeIndexWithOutputindex, Unit>>,
pub addresstype_to_typeindex_and_txindex: ByAddressType<Store<TypeIndexAndTxIndex, Unit>>,
pub addresstype_to_typeindex_and_unspentoutpoint:
ByAddressType<Store<TypeIndexAndOutPoint, Unit>>,
}
impl Stores {
pub fn forced_import(parent: &Path, version: Version) -> Result<Self> {
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<Item = &dyn AnyStore> {
[
&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<Item = &mut dyn AnyStore> {
[
&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::<Result<Vec<_>>>()?
.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::<Result<Vec<_>>>()?
.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())?;

View File

@@ -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<Height, Timestamp>,
pub height_to_total_size: CompressedVec<Height, StoredU64>,
pub height_to_weight: CompressedVec<Height, Weight>,
/// If txoutindex == TxOutIndex::MAX then it's coinbase
pub txinindex_to_txoutindex: RawVec<TxInIndex, TxOutIndex>,
pub txinindex_to_outpoint: RawVec<TxInIndex, OutPoint>,
pub opreturnindex_to_txindex: CompressedVec<OpReturnIndex, TxIndex>,
pub txoutindex_to_outputtype: RawVec<TxOutIndex, OutputType>,
pub txoutindex_to_typeindex: RawVec<TxOutIndex, TypeIndex>,
@@ -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<Item = &mut dyn AnyStoredVec> {
fn iter_mut_any_stored_vec(&mut self) -> impl Iterator<Item = &mut dyn AnyStoredVec> {
[
&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,

View File

@@ -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<()>;

View File

@@ -46,11 +46,11 @@ pub fn open_keyspace(path: &Path) -> fjall::Result<TransactionalKeyspace> {
.open_transactional()
}
impl<'a, K, V> Store<K, V>
impl<K, V> Store<K, V>
where
K: Debug + Clone + From<ByteView> + Ord + 'a,
K: Debug + Clone + From<ByteView> + Ord,
V: Debug + Clone + From<ByteView>,
ByteView: From<K> + From<&'a K> + From<V>,
ByteView: From<K> + From<V>,
{
pub fn import(
keyspace: &TransactionalKeyspace,
@@ -87,7 +87,10 @@ where
})
}
pub fn get(&'_ self, key: &'a K) -> Result<Option<Cow<'_, V>>> {
pub fn get<'a>(&'a self, key: &'a K) -> Result<Option<Cow<'a, V>>>
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<F>(&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<K, V>
impl<K, V> AnyStore for Store<K, V>
where
K: Debug + Clone + From<ByteView> + Ord + 'a,
K: Debug + Clone + From<ByteView> + Ord,
V: Debug + Clone + From<ByteView>,
ByteView: From<K> + From<&'a K> + From<V>,
ByteView: From<K> + From<V>,
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 {

View File

@@ -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];

View File

@@ -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)
}
}

View File

@@ -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,

View File

@@ -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<TxIndex> for TxIndex {
@@ -78,6 +83,12 @@ impl From<u32> for TxIndex {
}
}
impl From<TxIndex> for u32 {
fn from(value: TxIndex) -> Self {
value.0
}
}
impl From<u64> for TxIndex {
fn from(value: u64) -> Self {
Self(value as u32)
@@ -117,6 +128,12 @@ impl From<TxIndex> 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"

View File

@@ -105,7 +105,7 @@ impl PrintableIndex for TxInIndex {
}
fn to_possible_strings() -> &'static [&'static str] {
&["in", "txinindex"]
&["txi", "txin", "txinindex"]
}
}

View File

@@ -121,7 +121,7 @@ impl PrintableIndex for TxOutIndex {
}
fn to_possible_strings() -> &'static [&'static str] {
&["out", "txoutindex"]
&["txo", "txout", "txoutindex"]
}
}

View File

@@ -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<ByteView> 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<TypeIndexWithOutputindex> 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(),
)
}
}

View File

@@ -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<ByteView> for TypeIndexAndOutPoint {
fn from(value: ByteView) -> Self {
Self {
typeindex: TypeIndex::from(&value[0..4]),
outpoint: OutPoint::from(&value[4..]),
}
}
}
impl From<TypeIndexAndOutPoint> 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(),
)
}
}

View File

@@ -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<ByteView> 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<TypeIndexAndTxIndex> 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(),
)
}
}

View File

@@ -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<u32> 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<usize> for Vin {
fn from(value: usize) -> Self {
Self(value as u32)
if value > U16_MAX_AS_USIZE {
panic!()
}
Self(value as u16)
}
}

View File

@@ -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<u32> 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<u32> 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<usize> 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<Vout> 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)
}
}

View File

@@ -3,6 +3,7 @@
<head>
<meta charset="utf-8" />
<meta name="description" content="Bitcoin transparency, amplified" />
<meta name="referrer" content="no-referrer" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"