diff --git a/Cargo.lock b/Cargo.lock index 2175f3f88..bef4b1040 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -509,7 +509,7 @@ dependencies = [ "brk_interface", "brk_logger", "brk_mcp", - "brk_parser", + "brk_reader", "brk_server", "brk_store", "brk_structs", @@ -615,7 +615,7 @@ dependencies = [ "brk_indexer", "brk_interface", "brk_logger", - "brk_parser", + "brk_reader", "brk_server", "clap", "color-eyre", @@ -640,7 +640,7 @@ dependencies = [ "brk_grouper", "brk_indexer", "brk_logger", - "brk_parser", + "brk_reader", "brk_store", "brk_structs", "brk_traversable", @@ -697,7 +697,7 @@ dependencies = [ "brk_error", "brk_grouper", "brk_logger", - "brk_parser", + "brk_reader", "brk_store", "brk_structs", "brk_traversable", @@ -716,7 +716,7 @@ dependencies = [ "brk_computer", "brk_error", "brk_indexer", - "brk_parser", + "brk_reader", "brk_structs", "brk_traversable", "derive_deref", @@ -763,7 +763,7 @@ dependencies = [ ] [[package]] -name = "brk_parser" +name = "brk_reader" version = "0.0.111" dependencies = [ "bitcoin", @@ -1183,7 +1183,7 @@ dependencies = [ "brk_interface", "brk_logger", "brk_mcp", - "brk_parser", + "brk_reader", "brk_structs", "brk_traversable", "jiff", @@ -1373,9 +1373,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.48" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae" +checksum = "f4512b90fa68d3a9932cea5184017c5d200f5921df706d45e853537dea51508f" dependencies = [ "clap_builder", "clap_derive", @@ -1383,9 +1383,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.48" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9" +checksum = "0025e98baa12e766c67ba13ff4695a887a1eba19569aad00a472546795bd6730" dependencies = [ "anstream", "anstyle", @@ -1395,9 +1395,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.47" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ "heck", "proc-macro2", @@ -1407,9 +1407,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" [[package]] name = "color-eyre" @@ -1856,9 +1856,9 @@ dependencies = [ [[package]] name = "env_filter" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" dependencies = [ "log", "regex", @@ -2114,9 +2114,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.8" +version = "0.14.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dc8f7d2ded5f9209535e4b3fd4d39c002f30902ff5ce9f64e2c33d549576500" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" dependencies = [ "typenum", "version_check", @@ -2159,9 +2159,9 @@ checksum = "17e2ac29387b1aa07a1e448f7bb4f35b500787971e965b02842b900afa5c8f6f" [[package]] name = "half" -version = "2.7.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e54c115d4f30f52c67202f079c5f9d8b49db4691f460fdb0b4c2e838261b2ba5" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" dependencies = [ "cfg-if", "crunchy", @@ -2500,17 +2500,6 @@ dependencies = [ "compare", ] -[[package]] -name = "io-uring" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" -dependencies = [ - "bitflags 2.9.4", - "cfg-if", - "libc", -] - [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -3964,9 +3953,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.12.1" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a52d8d02cacdb176ef4678de6c052efb4b3da14b78e4db683a4252762be5433" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", @@ -3976,9 +3965,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "722166aa0d7438abbaa4d5cc2c649dac844e8c56d82fb3d33e9c34b5cd268fc6" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", @@ -3987,9 +3976,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3160422bbd54dd5ecfdca71e5fd59b7b8fe2b1697ab2baf64f6d05dcc66d298" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "regress" @@ -4695,27 +4684,24 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.47.1" +version = "1.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" dependencies = [ - "backtrace", "bytes", - "io-uring", "libc", "mio", "pin-project-lite", - "slab", "socket2", "tokio-macros", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", @@ -4939,9 +4925,9 @@ dependencies = [ [[package]] name = "ts-rs" -version = "11.0.1" +version = "11.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ef1b7a6d914a34127ed8e1fa927eb7088903787bcded4fa3eef8f85ee1568be" +checksum = "4994acea2522cd2b3b85c1d9529a55991e3ad5e25cdcd3de9d505972c4379424" dependencies = [ "thiserror 2.0.17", "ts-rs-macros", @@ -4949,9 +4935,9 @@ dependencies = [ [[package]] name = "ts-rs-macros" -version = "11.0.1" +version = "11.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9d4ed7b4c18cc150a6a0a1e9ea1ecfa688791220781af6e119f9599a8502a0a" +checksum = "ee6ff59666c9cbaec3533964505d39154dc4e0a56151fdea30a09ed0301f62e2" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 7a9503fdd..03ad9bc25 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,7 +58,7 @@ brk_interface = { version = "0.0.111", path = "crates/brk_interface" } brk_logger = { version = "0.0.111", path = "crates/brk_logger" } brk_mcp = { version = "0.0.111", path = "crates/brk_mcp" } brk_monitor = { version = "0.0.111", path = "crates/brk_monitor" } -brk_parser = { version = "0.0.111", path = "crates/brk_parser" } +brk_reader = { version = "0.0.111", path = "crates/brk_reader" } brk_server = { version = "0.0.111", path = "crates/brk_server" } brk_store = { version = "0.0.111", path = "crates/brk_store" } brk_structs = { version = "0.0.111", path = "crates/brk_structs" } @@ -78,7 +78,7 @@ serde_bytes = "0.11.19" serde_derive = "1.0.228" serde_json = { version = "1.0.145", features = ["float_roundtrip"] } sonic-rs = "0.5.5" -tokio = { version = "1.47.1", features = ["rt-multi-thread"] } +tokio = { version = "1.48.0", features = ["rt-multi-thread"] } vecdb = { path = "../seqdb/crates/vecdb", features = ["derive"] } # vecdb = { version = "0.2.17", features = ["derive"] } zerocopy = { version = "0.8.27", features = ["derive"] } diff --git a/crates/brk/Cargo.toml b/crates/brk/Cargo.toml index 63bfd9921..fa73849ea 100644 --- a/crates/brk/Cargo.toml +++ b/crates/brk/Cargo.toml @@ -40,7 +40,7 @@ indexer = ["brk_indexer"] interface = ["brk_interface"] logger = ["brk_logger"] mcp = ["brk_mcp"] -parser = ["brk_parser"] +parser = ["brk_reader"] server = ["brk_server"] store = ["brk_store"] structs = ["brk_structs"] @@ -58,7 +58,7 @@ brk_indexer = { workspace = true, optional = true } brk_interface = { workspace = true, optional = true } brk_logger = { workspace = true, optional = true } brk_mcp = { workspace = true, optional = true } -brk_parser = { workspace = true, optional = true } +brk_reader = { workspace = true, optional = true } brk_server = { workspace = true, optional = true } brk_store = { workspace = true, optional = true } brk_structs = { workspace = true, optional = true } diff --git a/crates/brk/README.md b/crates/brk/README.md index d5fd14bc3..9286ea88c 100644 --- a/crates/brk/README.md +++ b/crates/brk/README.md @@ -248,7 +248,7 @@ This pattern ensures: | `interface` | `brk_interface` | Data query interface | | `logger` | `brk_logger` | Enhanced logging | | `mcp` | `brk_mcp` | Model Context Protocol | -| `parser` | `brk_parser` | Block parsing | +| `parser` | `brk_reader` | Block parsing | | `server` | `brk_server` | HTTP server | | `store` | `brk_store` | Key-value storage | | `structs` | `brk_structs` | Data structures | @@ -270,4 +270,4 @@ Documentation is aggregated from all components with `#![doc = include_str!("../ --- -_This README was generated by Claude Code_ \ No newline at end of file +_This README was generated by Claude Code_ diff --git a/crates/brk/src/lib.rs b/crates/brk/src/lib.rs index daf2aa566..1b6c40d1d 100644 --- a/crates/brk/src/lib.rs +++ b/crates/brk/src/lib.rs @@ -46,7 +46,7 @@ pub use brk_mcp as mcp; #[cfg(feature = "parser")] #[doc(inline)] -pub use brk_parser as parser; +pub use brk_reader as parser; #[cfg(feature = "server")] #[doc(inline)] diff --git a/crates/brk_cli/Cargo.toml b/crates/brk_cli/Cargo.toml index 2c69e3b7a..a383624bd 100644 --- a/crates/brk_cli/Cargo.toml +++ b/crates/brk_cli/Cargo.toml @@ -19,10 +19,10 @@ brk_fetcher = { workspace = true } brk_indexer = { workspace = true } brk_interface = { workspace = true } brk_logger = { workspace = true } -brk_parser = { workspace = true } +brk_reader = { workspace = true } brk_server = { workspace = true } vecdb = { workspace = true } -clap = { version = "4.5.48", features = ["derive", "string"] } +clap = { version = "4.5.49", features = ["derive", "string"] } color-eyre = "0.6.5" log = { workspace = true } minreq = { workspace = true } diff --git a/crates/brk_cli/src/lib.rs b/crates/brk_cli/src/lib.rs index ecc51860e..9f8a6c896 100644 --- a/crates/brk_cli/src/lib.rs +++ b/crates/brk_cli/src/lib.rs @@ -15,7 +15,7 @@ use brk_computer::Computer; use brk_error::Result; use brk_indexer::Indexer; use brk_interface::Interface; -use brk_parser::Parser; +use brk_reader::Reader; use brk_server::{Server, VERSION}; use log::info; use vecdb::Exit; @@ -48,7 +48,7 @@ pub fn run() -> color_eyre::Result<()> { let exit = Exit::new(); exit.set_ctrlc_handler(); - let parser = Parser::new(config.blocksdir(), rpc); + let parser = Reader::new(config.blocksdir(), rpc); let mut indexer = Indexer::forced_import(&config.brkdir())?; diff --git a/crates/brk_computer/Cargo.toml b/crates/brk_computer/Cargo.toml index 07b511644..bb8bd3e29 100644 --- a/crates/brk_computer/Cargo.toml +++ b/crates/brk_computer/Cargo.toml @@ -18,7 +18,7 @@ brk_fetcher = { workspace = true } brk_grouper = { workspace = true } brk_indexer = { workspace = true } brk_logger = { workspace = true } -brk_parser = { workspace = true } +brk_reader = { workspace = true } brk_store = { workspace = true } brk_structs = { workspace = true } brk_traversable = { workspace = true } diff --git a/crates/brk_computer/examples/computer.rs b/crates/brk_computer/examples/computer.rs index 0e3886210..d507c0a45 100644 --- a/crates/brk_computer/examples/computer.rs +++ b/crates/brk_computer/examples/computer.rs @@ -8,7 +8,7 @@ use brk_computer::Computer; use brk_error::Result; use brk_fetcher::Fetcher; use brk_indexer::Indexer; -use brk_parser::Parser; +use brk_reader::Reader; use vecdb::Exit; pub fn main() -> Result<()> { @@ -34,7 +34,7 @@ pub fn main() -> Result<()> { let outputs_dir = Path::new(&std::env::var("HOME").unwrap()).join(".brk"); // let outputs_dir = Path::new("../../_outputs"); - let parser = Parser::new(bitcoin_dir.join("blocks"), rpc); + let parser = Reader::new(bitcoin_dir.join("blocks"), rpc); let mut indexer = Indexer::forced_import(&outputs_dir)?; diff --git a/crates/brk_computer/examples/pools.rs b/crates/brk_computer/examples/pools.rs index 8c603ed70..f1a069c42 100644 --- a/crates/brk_computer/examples/pools.rs +++ b/crates/brk_computer/examples/pools.rs @@ -103,7 +103,7 @@ fn main() -> Result<()> { )), _ => None, } - .map(|bytes| Address::try_from(bytes).unwrap()) + .map(|bytes| Address::try_from(&bytes).unwrap()) .and_then(|address| pools.find_from_address(&address)) }) .or_else(|| pools.find_from_coinbase_tag(&coinbase_tag)) diff --git a/crates/brk_computer/src/blks.rs b/crates/brk_computer/src/blks.rs index 15979dd4d..5b0f0db5c 100644 --- a/crates/brk_computer/src/blks.rs +++ b/crates/brk_computer/src/blks.rs @@ -2,7 +2,7 @@ use std::path::Path; use brk_error::Result; use brk_indexer::Indexer; -use brk_parser::Parser; +use brk_reader::Reader; use brk_structs::{BlkPosition, Height, TxIndex, Version}; use brk_traversable::Traversable; use vecdb::{ @@ -56,10 +56,10 @@ impl Vecs { indexer: &Indexer, indexes: &indexes::Vecs, starting_indexes: &Indexes, - parser: &Parser, + reader: &Reader, exit: &Exit, ) -> Result<()> { - self.compute_(indexer, indexes, starting_indexes, parser, exit)?; + self.compute_(indexer, indexes, starting_indexes, reader, exit)?; self.db.flush_then_punch()?; Ok(()) } @@ -69,7 +69,7 @@ impl Vecs { indexer: &Indexer, indexes: &indexes::Vecs, starting_indexes: &Indexes, - parser: &Parser, + parser: &Reader, exit: &Exit, ) -> Result<()> { let min_txindex = @@ -87,7 +87,7 @@ impl Vecs { let mut height_to_first_txindex_iter = indexer.vecs.height_to_first_txindex.iter(); parser - .parse( + .read( Some(min_height), Some((indexer.vecs.height_to_first_txindex.len() - 1).into()), ) diff --git a/crates/brk_computer/src/lib.rs b/crates/brk_computer/src/lib.rs index e353d954a..86a7b1265 100644 --- a/crates/brk_computer/src/lib.rs +++ b/crates/brk_computer/src/lib.rs @@ -5,7 +5,7 @@ use std::path::Path; use brk_error::Result; use brk_fetcher::Fetcher; use brk_indexer::Indexer; -use brk_parser::Parser; +use brk_reader::Reader; use brk_structs::Version; use brk_traversable::Traversable; use log::info; @@ -114,7 +114,7 @@ impl Computer { &mut self, indexer: &Indexer, starting_indexes: brk_indexer::Indexes, - parser: &Parser, + parser: &Reader, exit: &Exit, ) -> Result<()> { info!("Computing indexes..."); diff --git a/crates/brk_computer/src/pools/mod.rs b/crates/brk_computer/src/pools/mod.rs index 21598ac49..d32d3802c 100644 --- a/crates/brk_computer/src/pools/mod.rs +++ b/crates/brk_computer/src/pools/mod.rs @@ -201,7 +201,7 @@ impl Vecs { )), _ => None, } - .map(|bytes| Address::try_from(bytes).unwrap()) + .map(|bytes| Address::try_from(&bytes).unwrap()) .and_then(|address| self.pools.find_from_address(&address)) }) .or_else(|| self.pools.find_from_coinbase_tag(&coinbase_tag)) diff --git a/crates/brk_indexer/Cargo.toml b/crates/brk_indexer/Cargo.toml index d7906635e..b2f5dfb53 100644 --- a/crates/brk_indexer/Cargo.toml +++ b/crates/brk_indexer/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "brk_indexer" -description = "A Bitcoin indexer built on top of brk_parser" +description = "A Bitcoin indexer built on top of brk_reader" version.workspace = true edition.workspace = true license.workspace = true @@ -15,7 +15,7 @@ bitcoincore-rpc = { workspace = true } brk_error = { workspace = true } brk_grouper = { workspace = true } brk_logger = { workspace = true } -brk_parser = { workspace = true } +brk_reader = { workspace = true } brk_store = { workspace = true } brk_structs = { workspace = true } brk_traversable = { workspace = true } diff --git a/crates/brk_indexer/README.md b/crates/brk_indexer/README.md index 3d6caa862..bd831757d 100644 --- a/crates/brk_indexer/README.md +++ b/crates/brk_indexer/README.md @@ -7,7 +7,7 @@ High-performance Bitcoin blockchain indexer with parallel processing and dual st ## Overview -This crate provides a comprehensive Bitcoin blockchain indexer built on top of `brk_parser`. It processes raw Bitcoin blocks in parallel, extracting and indexing transactions, addresses, inputs, outputs, and metadata into optimized storage structures. The indexer maintains two complementary storage systems: columnar vectors for analytics and key-value stores for fast lookups. +This crate provides a comprehensive Bitcoin blockchain indexer built on top of `brk_reader`. It processes raw Bitcoin blocks in parallel, extracting and indexing transactions, addresses, inputs, outputs, and metadata into optimized storage structures. The indexer maintains two complementary storage systems: columnar vectors for analytics and key-value stores for fast lookups. **Key Features:** @@ -36,7 +36,7 @@ cargo add brk_indexer ```rust use brk_indexer::Indexer; -use brk_parser::Parser; +use brk_reader::Parser; use bitcoincore_rpc::{Client, Auth}; use vecdb::Exit; use std::path::Path; @@ -114,7 +114,7 @@ Complete coverage of Bitcoin script types: ```rust use brk_indexer::Indexer; -use brk_parser::Parser; +use brk_reader::Parser; use std::path::Path; // Initialize components diff --git a/crates/brk_indexer/examples/indexer.rs b/crates/brk_indexer/examples/indexer.rs index f2ab4072c..578b6e98b 100644 --- a/crates/brk_indexer/examples/indexer.rs +++ b/crates/brk_indexer/examples/indexer.rs @@ -7,7 +7,7 @@ use std::{ use brk_error::Result; use brk_indexer::Indexer; -use brk_parser::Parser; +use brk_reader::Reader; use vecdb::Exit; fn main() -> Result<()> { @@ -33,7 +33,7 @@ fn main() -> Result<()> { let exit = Exit::new(); exit.set_ctrlc_handler(); - let parser = Parser::new(blocks_dir, rpc); + let parser = Reader::new(blocks_dir, rpc); fs::create_dir_all(&outputs_dir)?; diff --git a/crates/brk_indexer/src/lib.rs b/crates/brk_indexer/src/lib.rs index d02df5d42..998c1b36b 100644 --- a/crates/brk_indexer/src/lib.rs +++ b/crates/brk_indexer/src/lib.rs @@ -4,7 +4,6 @@ use std::{collections::BTreeMap, path::Path, str::FromStr, time::Instant}; use bitcoin::{Transaction, TxIn, TxOut}; use brk_error::{Error, Result}; -use brk_parser::Parser; use brk_store::AnyStore; use brk_structs::{ AddressBytes, AddressBytesHash, BlockHashPrefix, Height, InputIndex, OutputIndex, OutputType, @@ -51,7 +50,7 @@ impl Indexer { pub fn index( &mut self, - parser: &Parser, + reader: &brk_reader::Reader, rpc: &'static bitcoincore_rpc::Client, exit: &Exit, check_collisions: bool, @@ -163,7 +162,7 @@ impl Indexer { &mut p2aaddressindex_to_p2abytes_reader_opt, ); - parser.parse(start, end).iter().try_for_each( + reader.read(start, end).iter().try_for_each( |block| -> Result<()> { let height = block.height(); let blockhash = block.hash(); diff --git a/crates/brk_indexer/src/vecs.rs b/crates/brk_indexer/src/vecs.rs index 9159427f4..88b1b23ed 100644 --- a/crates/brk_indexer/src/vecs.rs +++ b/crates/brk_indexer/src/vecs.rs @@ -320,28 +320,28 @@ impl Vecs { match bytes { AddressBytes::P2PK65(bytes) => self .p2pk65addressindex_to_p2pk65bytes - .push_if_needed(index.into(), bytes)?, + .push_if_needed(index.into(), *bytes)?, AddressBytes::P2PK33(bytes) => self .p2pk33addressindex_to_p2pk33bytes - .push_if_needed(index.into(), bytes)?, + .push_if_needed(index.into(), *bytes)?, AddressBytes::P2PKH(bytes) => self .p2pkhaddressindex_to_p2pkhbytes - .push_if_needed(index.into(), bytes)?, + .push_if_needed(index.into(), *bytes)?, AddressBytes::P2SH(bytes) => self .p2shaddressindex_to_p2shbytes - .push_if_needed(index.into(), bytes)?, + .push_if_needed(index.into(), *bytes)?, AddressBytes::P2WPKH(bytes) => self .p2wpkhaddressindex_to_p2wpkhbytes - .push_if_needed(index.into(), bytes)?, + .push_if_needed(index.into(), *bytes)?, AddressBytes::P2WSH(bytes) => self .p2wshaddressindex_to_p2wshbytes - .push_if_needed(index.into(), bytes)?, + .push_if_needed(index.into(), *bytes)?, AddressBytes::P2TR(bytes) => self .p2traddressindex_to_p2trbytes - .push_if_needed(index.into(), bytes)?, + .push_if_needed(index.into(), *bytes)?, AddressBytes::P2A(bytes) => self .p2aaddressindex_to_p2abytes - .push_if_needed(index.into(), bytes)?, + .push_if_needed(index.into(), *bytes)?, }; Ok(()) } diff --git a/crates/brk_interface/Cargo.toml b/crates/brk_interface/Cargo.toml index b04f1f955..01d0d9fee 100644 --- a/crates/brk_interface/Cargo.toml +++ b/crates/brk_interface/Cargo.toml @@ -15,7 +15,7 @@ bitcoincore-rpc = { workspace = true } brk_computer = { workspace = true } brk_error = { workspace = true } brk_indexer = { workspace = true } -brk_parser = { workspace = true } +brk_reader = { workspace = true } brk_structs = { workspace = true } brk_traversable = { workspace = true } derive_deref = { workspace = true } diff --git a/crates/brk_interface/examples/main.rs b/crates/brk_interface/examples/main.rs index 8e604fa98..d436bf2cb 100644 --- a/crates/brk_interface/examples/main.rs +++ b/crates/brk_interface/examples/main.rs @@ -4,7 +4,7 @@ use brk_computer::Computer; use brk_error::Result; use brk_indexer::Indexer; use brk_interface::{Interface, Params, ParamsOpt}; -use brk_parser::Parser; +use brk_reader::Reader; use brk_structs::Index; use vecdb::Exit; @@ -29,7 +29,7 @@ pub fn main() -> Result<()> { let exit = Exit::new(); exit.set_ctrlc_handler(); - let parser = Parser::new(blocks_dir, rpc); + let parser = Reader::new(blocks_dir, rpc); let indexer = Indexer::forced_import(&outputs_dir)?; diff --git a/crates/brk_interface/src/chain/addresses.rs b/crates/brk_interface/src/chain/addresses.rs index 824a109c2..2532e56b2 100644 --- a/crates/brk_interface/src/chain/addresses.rs +++ b/crates/brk_interface/src/chain/addresses.rs @@ -3,8 +3,8 @@ use std::str::FromStr; use bitcoin::{Network, PublicKey, ScriptBuf}; use brk_error::{Error, Result}; use brk_structs::{ - Address, AddressBytes, AddressBytesHash, AddressStats, AnyAddressDataIndexEnum, Bitcoin, - OutputType, + Address, AddressBytes, AddressBytesHash, AddressChainStats, AddressMempoolStats, AddressStats, + AnyAddressDataIndexEnum, Bitcoin, OutputType, }; use vecdb::{AnyIterableVec, VecIterator}; @@ -104,9 +104,11 @@ pub fn get_address(Address { address }: Address, interface: &Interface) -> Resul .into(), }; - let balance = address_data.balance(); - - todo!(); + Ok(AddressStats { + address: address.into(), + chain_stats: AddressChainStats::default(), + mempool_stats: AddressMempoolStats::default(), + }) // Ok(Address { // address: address.to_string(), diff --git a/crates/brk_interface/src/chain/transactions.rs b/crates/brk_interface/src/chain/transactions.rs index cee48011d..43cbb558e 100644 --- a/crates/brk_interface/src/chain/transactions.rs +++ b/crates/brk_interface/src/chain/transactions.rs @@ -4,15 +4,18 @@ use std::{ str::FromStr, }; -use bitcoin::{Transaction, consensus::Decodable}; +use bitcoin::consensus::Decodable; use brk_error::{Error, Result}; -use brk_parser::XORIndex; -use brk_structs::{Tx, Txid, TxidPath, TxidPrefix}; +use brk_reader::XORIndex; +use brk_structs::{Transaction, Txid, TxidPath, TxidPrefix}; use vecdb::VecIterator; use crate::Interface; -pub fn get_transaction_info(TxidPath { txid }: TxidPath, interface: &Interface) -> Result { +pub fn get_transaction_info( + TxidPath { txid }: TxidPath, + interface: &Interface, +) -> Result { let Ok(txid) = bitcoin::Txid::from_str(&txid) else { return Err(Error::InvalidTxid); }; @@ -72,7 +75,7 @@ pub fn get_transaction_info(TxidPath { txid }: TxidPath, interface: &Interface) xori.bytes(&mut buffer, parser.xor_bytes()); let mut reader = Cursor::new(buffer); - let Ok(_) = Transaction::consensus_decode(&mut reader) else { + let Ok(_) = bitcoin::Transaction::consensus_decode(&mut reader) else { return Err(Error::Str("Failed decode the transaction")); }; diff --git a/crates/brk_interface/src/lib.rs b/crates/brk_interface/src/lib.rs index bab1058aa..82f858f41 100644 --- a/crates/brk_interface/src/lib.rs +++ b/crates/brk_interface/src/lib.rs @@ -5,10 +5,10 @@ use std::collections::BTreeMap; use brk_computer::Computer; use brk_error::Result; use brk_indexer::Indexer; -use brk_parser::Parser; +use brk_reader::Reader; use brk_structs::{ - Address, AddressStats, Format, Height, Index, IndexInfo, Limit, Metric, MetricCount, Tx, - TxidPath, + Address, AddressStats, Format, Height, Index, IndexInfo, Limit, Metric, MetricCount, + Transaction, TxidPath, }; use brk_traversable::TreeNode; use vecdb::{AnyCollectableVec, AnyStoredVec}; @@ -33,13 +33,13 @@ use crate::{ #[allow(dead_code)] pub struct Interface<'a> { vecs: Vecs<'a>, - parser: &'a Parser, + parser: &'a Reader, indexer: &'a Indexer, computer: &'a Computer, } impl<'a> Interface<'a> { - pub fn build(parser: &Parser, indexer: &Indexer, computer: &Computer) -> Self { + pub fn build(parser: &Reader, indexer: &Indexer, computer: &Computer) -> Self { let parser = parser.static_clone(); let indexer = indexer.static_clone(); let computer = computer.static_clone(); @@ -61,7 +61,7 @@ impl<'a> Interface<'a> { get_address(address, self) } - pub fn get_transaction_info(&self, txid: TxidPath) -> Result { + pub fn get_transaction_info(&self, txid: TxidPath) -> Result { get_transaction_info(txid, self) } @@ -254,7 +254,7 @@ impl<'a> Interface<'a> { self.vecs.metric_to_indexes(metric) } - pub fn parser(&self) -> &Parser { + pub fn parser(&self) -> &Reader { self.parser } diff --git a/crates/brk_monitor/src/main.rs b/crates/brk_monitor/examples/mempool.rs similarity index 89% rename from crates/brk_monitor/src/main.rs rename to crates/brk_monitor/examples/mempool.rs index 29c489b86..0ed69bc5a 100644 --- a/crates/brk_monitor/src/main.rs +++ b/crates/brk_monitor/examples/mempool.rs @@ -31,5 +31,7 @@ fn main() { thread::sleep(Duration::from_secs(5)); let txs = mempool.get_txs(); println!("mempool_tx_count: {}", txs.len()); + let addresses = mempool.get_addresses(); + println!("mempool_address_count: {}", addresses.len()); } } diff --git a/crates/brk_monitor/src/lib.rs b/crates/brk_monitor/src/lib.rs index f4841e105..992a734fb 100644 --- a/crates/brk_monitor/src/lib.rs +++ b/crates/brk_monitor/src/lib.rs @@ -1,7 +1,8 @@ use std::{thread, time::Duration}; -use bitcoin::{Transaction, Txid, consensus::encode}; +use bitcoin::consensus::encode; use bitcoincore_rpc::{Client, RpcApi}; +use brk_structs::{AddressBytes, AddressMempoolStats, Transaction, Txid}; use log::error; use parking_lot::{RwLock, RwLockReadGuard}; use rustc_hash::{FxHashMap, FxHashSet}; @@ -11,6 +12,7 @@ const MAX_FETCHES_PER_CYCLE: usize = 10_000; pub struct Mempool { rpc: &'static Client, txs: RwLock>, + addresses: RwLock)>>, } impl Mempool { @@ -18,6 +20,7 @@ impl Mempool { Self { rpc, txs: RwLock::new(FxHashMap::default()), + addresses: RwLock::new(FxHashMap::default()), } } @@ -25,6 +28,12 @@ impl Mempool { self.txs.read() } + pub fn get_addresses( + &self, + ) -> RwLockReadGuard<'_, FxHashMap)>> { + self.addresses.read() + } + pub fn start(&self) { loop { if let Err(e) = self.update() { @@ -34,35 +43,82 @@ impl Mempool { } } - fn update(&self) -> Result<(), Box> { + pub fn update(&self) -> Result<(), Box> { let txids = self .rpc .get_raw_mempool()? .into_iter() + .map(Txid::from) .collect::>(); - let missing_txids = { + let new_txs = { let txs = self.txs.read(); txids .iter() .filter(|txid| !txs.contains_key(*txid)) .take(MAX_FETCHES_PER_CYCLE) + .cloned() .collect::>() - }; - - let new_txs = missing_txids - .into_iter() - .filter_map(|txid| { - self.rpc - .get_raw_transaction_hex(txid, None) - .ok() - .and_then(|hex| encode::deserialize_hex(&hex).ok()) - .map(|tx| (*txid, tx)) - }) - .collect::>(); + } + .into_iter() + .filter_map(|txid| { + self.rpc + .get_raw_transaction_hex(&bitcoin::Txid::from(&txid), None) + .ok() + .and_then(|hex| encode::deserialize_hex::(&hex).ok()) + .map(|tx| Transaction::from_mempool(tx, self.rpc)) + .map(|tx| (txid, tx)) + }) + .collect::>(); let mut txs = self.txs.write(); - txs.retain(|txid, _| txids.contains(txid)); + let mut addresses = self.addresses.write(); + txs.retain(|txid, tx| { + if txids.contains(txid) { + return true; + } + tx.input + .iter() + .flat_map(|txin| txin.prevout.as_ref()) + .flat_map(|txout| txout.address_bytes().map(|bytes| (txout, bytes))) + .for_each(|(txout, bytes)| { + let (stats, set) = addresses.entry(bytes).or_default(); + set.remove(txid); + stats.sent(txout); + stats.update_tx_count(set.len() as u32); + }); + tx.output + .iter() + .flat_map(|txout| txout.address_bytes().map(|bytes| (txout, bytes))) + .for_each(|(txout, bytes)| { + let (stats, set) = addresses.entry(bytes).or_default(); + set.remove(txid); + stats.received(txout); + stats.update_tx_count(set.len() as u32); + }); + false + }); + new_txs.iter().for_each(|(txid, tx)| { + tx.input + .iter() + .flat_map(|txin| txin.prevout.as_ref()) + .flat_map(|txout| txout.address_bytes().map(|bytes| (txout, bytes))) + .for_each(|(txout, bytes)| { + let (stats, set) = addresses.entry(bytes).or_default(); + set.insert(txid.clone()); + stats.sending(txout); + stats.update_tx_count(set.len() as u32); + }); + tx.output + .iter() + .flat_map(|txout| txout.address_bytes().map(|bytes| (txout, bytes))) + .for_each(|(txout, bytes)| { + let (stats, set) = addresses.entry(bytes).or_default(); + set.insert(txid.clone()); + stats.receiving(txout); + stats.update_tx_count(set.len() as u32); + }); + }); txs.extend(new_txs); Ok(()) diff --git a/crates/brk_parser/Cargo.toml b/crates/brk_reader/Cargo.toml similarity index 97% rename from crates/brk_parser/Cargo.toml rename to crates/brk_reader/Cargo.toml index 67f62c810..0c58ac2a2 100644 --- a/crates/brk_parser/Cargo.toml +++ b/crates/brk_reader/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "brk_parser" +name = "brk_reader" description = "A very fast Bitcoin block parser and iterator built on top of bitcoin-rust" keywords = ["bitcoin", "block", "iterator"] categories = ["cryptography::cryptocurrencies", "encoding"] diff --git a/crates/brk_parser/README.md b/crates/brk_reader/README.md similarity index 95% rename from crates/brk_parser/README.md rename to crates/brk_reader/README.md index 6a7ea9051..f5b47b681 100644 --- a/crates/brk_parser/README.md +++ b/crates/brk_reader/README.md @@ -1,9 +1,9 @@ -# brk_parser +# brk_reader High-performance Bitcoin block parser for raw Bitcoin Core block files with XOR encryption support. -[![Crates.io](https://img.shields.io/crates/v/brk_parser.svg)](https://crates.io/crates/brk_parser) -[![Documentation](https://docs.rs/brk_parser/badge.svg)](https://docs.rs/brk_parser) +[![Crates.io](https://img.shields.io/crates/v/brk_reader.svg)](https://crates.io/crates/brk_reader) +[![Documentation](https://docs.rs/brk_reader/badge.svg)](https://docs.rs/brk_reader) ## Overview @@ -29,13 +29,13 @@ This crate provides a multi-threaded Bitcoin block parser that processes raw Bit ## Installation ```bash -cargo add brk_parser +cargo add brk_reader ``` ## Quick Start ```rust -use brk_parser::Parser; +use brk_reader::Parser; use bitcoincore_rpc::{Client, Auth, RpcApi}; use brk_structs::Height; use std::path::PathBuf; @@ -94,7 +94,7 @@ The parser implements a three-stage pipeline: ### Basic Block Iteration ```rust -use brk_parser::Parser; +use brk_reader::Parser; let parser = Parser::new(blocks_dir, Some(output_dir), rpc); @@ -115,7 +115,7 @@ for (height, block, hash) in receiver.iter() { ### Range-Based Processing ```rust -use brk_parser::Parser; +use brk_reader::Parser; let parser = Parser::new(blocks_dir, Some(output_dir), rpc); @@ -139,7 +139,7 @@ println!("Processed 1000 blocks with {} total transactions", total_tx_count); ### Incremental Processing with Metadata ```rust -use brk_parser::Parser; +use brk_reader::Parser; let parser = Parser::new(blocks_dir, Some(output_dir), rpc); diff --git a/crates/brk_parser/build.rs b/crates/brk_reader/build.rs similarity index 100% rename from crates/brk_parser/build.rs rename to crates/brk_reader/build.rs diff --git a/crates/brk_parser/examples/main.rs b/crates/brk_reader/examples/main.rs similarity index 89% rename from crates/brk_parser/examples/main.rs rename to crates/brk_reader/examples/main.rs index f1463ebe2..400bad4e9 100644 --- a/crates/brk_parser/examples/main.rs +++ b/crates/brk_reader/examples/main.rs @@ -2,7 +2,7 @@ use std::path::Path; use bitcoincore_rpc::{Auth, Client}; use brk_error::Result; -use brk_parser::Parser; +use brk_reader::Reader; #[allow(clippy::needless_doctest_main)] fn main() -> Result<()> { @@ -18,12 +18,12 @@ fn main() -> Result<()> { Auth::CookieFile(bitcoin_dir.join(".cookie")), )?)); - let parser = Parser::new(bitcoin_dir.join("blocks"), rpc); + let reader = Reader::new(bitcoin_dir.join("blocks"), rpc); let start = None; // let start = Some(916037_u32.into()); let end = None; - parser.parse(start, end).iter().for_each(|block| { + reader.read(start, end).iter().for_each(|block| { println!("{}: {}", block.height(), block.hash()); }); diff --git a/crates/brk_parser/examples/p2a.rs b/crates/brk_reader/examples/p2a.rs similarity index 94% rename from crates/brk_parser/examples/p2a.rs rename to crates/brk_reader/examples/p2a.rs index 889721438..1b901331d 100644 --- a/crates/brk_parser/examples/p2a.rs +++ b/crates/brk_reader/examples/p2a.rs @@ -1,7 +1,7 @@ use std::path::Path; use bitcoincore_rpc::{Auth, Client}; -use brk_parser::Parser; +use brk_reader::Reader; use brk_structs::{Height, OutputType}; fn main() { @@ -20,7 +20,7 @@ fn main() { // let start = None; // let end = None; - let parser = Parser::new(bitcoin_dir.join("blocks"), rpc); + let parser = Reader::new(bitcoin_dir.join("blocks"), rpc); // parser // .parse(start, end) diff --git a/crates/brk_parser/src/any_block.rs b/crates/brk_reader/src/any_block.rs similarity index 100% rename from crates/brk_parser/src/any_block.rs rename to crates/brk_reader/src/any_block.rs diff --git a/crates/brk_parser/src/blk_index_to_blk_path.rs b/crates/brk_reader/src/blk_index_to_blk_path.rs similarity index 100% rename from crates/brk_parser/src/blk_index_to_blk_path.rs rename to crates/brk_reader/src/blk_index_to_blk_path.rs diff --git a/crates/brk_parser/src/lib.rs b/crates/brk_reader/src/lib.rs similarity index 98% rename from crates/brk_parser/src/lib.rs rename to crates/brk_reader/src/lib.rs index e073875f7..ee97dec8a 100644 --- a/crates/brk_parser/src/lib.rs +++ b/crates/brk_reader/src/lib.rs @@ -34,14 +34,14 @@ const MAGIC_BYTES: [u8; 4] = [249, 190, 180, 217]; const BOUND_CAP: usize = 50; #[derive(Debug, Clone)] -pub struct Parser { +pub struct Reader { blk_index_to_blk_path: Arc>, xor_bytes: XORBytes, blocks_dir: PathBuf, rpc: &'static bitcoincore_rpc::Client, } -impl Parser { +impl Reader { pub fn new(blocks_dir: PathBuf, rpc: &'static bitcoincore_rpc::Client) -> Self { Self { xor_bytes: XORBytes::from(blocks_dir.as_path()), @@ -75,7 +75,7 @@ impl Parser { /// /// For an example checkout `./main.rs` /// - pub fn parse(&self, start: Option, end: Option) -> Receiver { + pub fn read(&self, start: Option, end: Option) -> Receiver { let rpc = self.rpc; let (send_bytes, recv_bytes) = bounded(BOUND_CAP / 2); diff --git a/crates/brk_parser/src/xor_bytes.rs b/crates/brk_reader/src/xor_bytes.rs similarity index 100% rename from crates/brk_parser/src/xor_bytes.rs rename to crates/brk_reader/src/xor_bytes.rs diff --git a/crates/brk_parser/src/xor_index.rs b/crates/brk_reader/src/xor_index.rs similarity index 100% rename from crates/brk_parser/src/xor_index.rs rename to crates/brk_reader/src/xor_index.rs diff --git a/crates/brk_server/Cargo.toml b/crates/brk_server/Cargo.toml index be40975c6..9aeaf9876 100644 --- a/crates/brk_server/Cargo.toml +++ b/crates/brk_server/Cargo.toml @@ -21,7 +21,7 @@ brk_indexer = { workspace = true } brk_interface = { workspace = true } brk_logger = { workspace = true } brk_mcp = { workspace = true } -brk_parser = { workspace = true } +brk_reader = { workspace = true } brk_structs = { workspace = true } brk_traversable = { workspace = true } vecdb = { workspace = true } diff --git a/crates/brk_server/examples/main.rs b/crates/brk_server/examples/main.rs index 9b3f9e48a..01b76be3f 100644 --- a/crates/brk_server/examples/main.rs +++ b/crates/brk_server/examples/main.rs @@ -7,7 +7,7 @@ use brk_error::Result; use brk_fetcher::Fetcher; use brk_indexer::Indexer; use brk_interface::Interface; -use brk_parser::Parser; +use brk_reader::Reader; use brk_server::Server; use vecdb::Exit; @@ -25,7 +25,7 @@ pub fn main() -> Result<()> { let exit = Exit::new(); exit.set_ctrlc_handler(); - let parser = Parser::new(bitcoin_dir.join("blocks"), rpc); + let parser = Reader::new(bitcoin_dir.join("blocks"), rpc); let outputs_dir = Path::new("../../_outputs"); diff --git a/crates/brk_server/src/api/transactions/mod.rs b/crates/brk_server/src/api/transactions/mod.rs index f242027dc..2857d1c7e 100644 --- a/crates/brk_server/src/api/transactions/mod.rs +++ b/crates/brk_server/src/api/transactions/mod.rs @@ -5,7 +5,7 @@ use axum::{ response::{Redirect, Response}, routing::get, }; -use brk_structs::{Tx, TxidPath}; +use brk_structs::{Transaction, TxidPath}; use crate::{ VERSION, @@ -46,7 +46,7 @@ impl TxRoutes for ApiRouter { .description( "Retrieve complete transaction data by transaction ID (txid). Returns the full transaction details including inputs, outputs, and metadata. The transaction data is read directly from the blockchain data files.", ) - .ok_response::() + .ok_response::() .not_modified() .bad_request() .not_found() diff --git a/crates/brk_structs/src/address.rs b/crates/brk_structs/src/address.rs index a6fbd7c6b..a802f7876 100644 --- a/crates/brk_structs/src/address.rs +++ b/crates/brk_structs/src/address.rs @@ -1,6 +1,6 @@ use std::fmt; -use bitcoin::{Network, ScriptBuf, opcodes, script::Builder}; +use bitcoin::{ScriptBuf, opcodes, script::Builder}; use brk_error::Error; use derive_deref::Deref; use schemars::JsonSchema; @@ -29,64 +29,53 @@ impl From for Address { } } -impl TryFrom for Address { +impl TryFrom<&ScriptBuf> for Address { type Error = Error; - fn try_from(script: ScriptBuf) -> Result { - Ok(Self::from(bitcoin::Address::from_script( - &script, - Network::Bitcoin, - )?)) - } -} - -impl From for Address { - fn from(address: bitcoin::Address) -> Self { - Self { - address: address.to_string(), - } + fn try_from(script: &ScriptBuf) -> Result { + Self::try_from(&AddressBytes::try_from(script)?) } } impl TryFrom<(&ScriptBuf, OutputType)> for Address { type Error = Error; fn try_from(tuple: (&ScriptBuf, OutputType)) -> Result { - Self::try_from(AddressBytes::try_from(tuple)?) + Self::try_from(&AddressBytes::try_from(tuple)?) } } -impl TryFrom for Address { +impl TryFrom<&AddressBytes> for Address { type Error = Error; - fn try_from(bytes: AddressBytes) -> Result { + fn try_from(bytes: &AddressBytes) -> Result { let address = match bytes { - AddressBytes::P2PK65(b) => Self::from(bytes_to_hex(&**b)), - AddressBytes::P2PK33(b) => Self::from(bytes_to_hex(&**b)), + AddressBytes::P2PK65(_) => Self::from(bytes_to_hex(bytes.as_slice())), + AddressBytes::P2PK33(_) => Self::from(bytes_to_hex(bytes.as_slice())), AddressBytes::P2PKH(b) => Self::try_from( - Builder::new() + &Builder::new() .push_opcode(opcodes::all::OP_DUP) .push_opcode(opcodes::all::OP_HASH160) - .push_slice(**b) + .push_slice(****b) .push_opcode(opcodes::all::OP_EQUALVERIFY) .push_opcode(opcodes::all::OP_CHECKSIG) .into_script(), )?, AddressBytes::P2SH(b) => Self::try_from( - Builder::new() + &Builder::new() .push_opcode(opcodes::all::OP_HASH160) - .push_slice(**b) + .push_slice(****b) .push_opcode(opcodes::all::OP_EQUAL) .into_script(), )?, AddressBytes::P2WPKH(b) => { - Self::try_from(Builder::new().push_int(0).push_slice(**b).into_script())? + Self::try_from(&Builder::new().push_int(0).push_slice(****b).into_script())? } AddressBytes::P2WSH(b) => { - Self::try_from(Builder::new().push_int(0).push_slice(**b).into_script())? + Self::try_from(&Builder::new().push_int(0).push_slice(****b).into_script())? } AddressBytes::P2TR(b) => { - Self::try_from(Builder::new().push_int(1).push_slice(**b).into_script())? + Self::try_from(&Builder::new().push_int(1).push_slice(****b).into_script())? } AddressBytes::P2A(b) => { - Self::try_from(Builder::new().push_int(1).push_slice(**b).into_script())? + Self::try_from(&Builder::new().push_int(1).push_slice(****b).into_script())? } }; Ok(address) diff --git a/crates/brk_structs/src/addressbytes.rs b/crates/brk_structs/src/addressbytes.rs index cffe4ca86..765ad894a 100644 --- a/crates/brk_structs/src/addressbytes.rs +++ b/crates/brk_structs/src/addressbytes.rs @@ -1,3 +1,5 @@ +use std::fmt; + use bitcoin::ScriptBuf; use brk_error::Error; @@ -6,16 +8,16 @@ use super::{ P2WSHBytes, }; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum AddressBytes { - P2PK65(P2PK65Bytes), - P2PK33(P2PK33Bytes), - P2PKH(P2PKHBytes), - P2SH(P2SHBytes), - P2WPKH(P2WPKHBytes), - P2WSH(P2WSHBytes), - P2TR(P2TRBytes), - P2A(P2ABytes), + P2PK65(Box), // 65 + P2PK33(Box), // 33 + P2PKH(Box), // 20 + P2SH(Box), // 20 + P2WPKH(Box), // 20 + P2WSH(Box), // 32 + P2TR(Box), // 32 + P2A(Box), // 2 } impl AddressBytes { @@ -33,6 +35,19 @@ impl AddressBytes { } } +impl fmt::Display for AddressBytes { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&super::Address::try_from(self).unwrap().to_string()) + } +} + +impl TryFrom<&ScriptBuf> for AddressBytes { + type Error = Error; + fn try_from(script: &ScriptBuf) -> Result { + Self::try_from((script, OutputType::from(script))) + } +} + impl TryFrom<(&ScriptBuf, OutputType)> for AddressBytes { type Error = Error; fn try_from(tuple: (&ScriptBuf, OutputType)) -> Result { @@ -48,7 +63,7 @@ impl TryFrom<(&ScriptBuf, OutputType)> for AddressBytes { return Err(Error::WrongLength); } }; - Ok(Self::P2PK65(P2PK65Bytes::from(bytes))) + Ok(Self::P2PK65(Box::new(P2PK65Bytes::from(bytes)))) } OutputType::P2PK33 => { let bytes = script.as_bytes(); @@ -59,31 +74,31 @@ impl TryFrom<(&ScriptBuf, OutputType)> for AddressBytes { return Err(Error::WrongLength); } }; - Ok(Self::P2PK33(P2PK33Bytes::from(bytes))) + Ok(Self::P2PK33(Box::new(P2PK33Bytes::from(bytes)))) } OutputType::P2PKH => { let bytes = &script.as_bytes()[3..23]; - Ok(Self::P2PKH(P2PKHBytes::from(bytes))) + Ok(Self::P2PKH(Box::new(P2PKHBytes::from(bytes)))) } OutputType::P2SH => { let bytes = &script.as_bytes()[2..22]; - Ok(Self::P2SH(P2SHBytes::from(bytes))) + Ok(Self::P2SH(Box::new(P2SHBytes::from(bytes)))) } OutputType::P2WPKH => { let bytes = &script.as_bytes()[2..]; - Ok(Self::P2WPKH(P2WPKHBytes::from(bytes))) + Ok(Self::P2WPKH(Box::new(P2WPKHBytes::from(bytes)))) } OutputType::P2WSH => { let bytes = &script.as_bytes()[2..]; - Ok(Self::P2WSH(P2WSHBytes::from(bytes))) + Ok(Self::P2WSH(Box::new(P2WSHBytes::from(bytes)))) } OutputType::P2TR => { let bytes = &script.as_bytes()[2..]; - Ok(Self::P2TR(P2TRBytes::from(bytes))) + Ok(Self::P2TR(Box::new(P2TRBytes::from(bytes)))) } OutputType::P2A => { let bytes = &script.as_bytes()[2..]; - Ok(Self::P2A(P2ABytes::from(bytes))) + Ok(Self::P2A(Box::new(P2ABytes::from(bytes)))) } OutputType::P2MS => Err(Error::WrongAddressType), OutputType::Unknown => Err(Error::WrongAddressType), @@ -96,48 +111,48 @@ impl TryFrom<(&ScriptBuf, OutputType)> for AddressBytes { impl From for AddressBytes { fn from(value: P2PK65Bytes) -> Self { - Self::P2PK65(value) + Self::P2PK65(Box::new(value)) } } impl From for AddressBytes { fn from(value: P2PK33Bytes) -> Self { - Self::P2PK33(value) + Self::P2PK33(Box::new(value)) } } impl From for AddressBytes { fn from(value: P2PKHBytes) -> Self { - Self::P2PKH(value) + Self::P2PKH(Box::new(value)) } } impl From for AddressBytes { fn from(value: P2SHBytes) -> Self { - Self::P2SH(value) + Self::P2SH(Box::new(value)) } } impl From for AddressBytes { fn from(value: P2WPKHBytes) -> Self { - Self::P2WPKH(value) + Self::P2WPKH(Box::new(value)) } } impl From for AddressBytes { fn from(value: P2WSHBytes) -> Self { - Self::P2WSH(value) + Self::P2WSH(Box::new(value)) } } impl From for AddressBytes { fn from(value: P2TRBytes) -> Self { - Self::P2TR(value) + Self::P2TR(Box::new(value)) } } impl From for AddressBytes { fn from(value: P2ABytes) -> Self { - Self::P2A(value) + Self::P2A(Box::new(value)) } } diff --git a/crates/brk_structs/src/addresschainstats.rs b/crates/brk_structs/src/addresschainstats.rs index 8f7027957..33f0aca2b 100644 --- a/crates/brk_structs/src/addresschainstats.rs +++ b/crates/brk_structs/src/addresschainstats.rs @@ -5,7 +5,7 @@ use serde::Serialize; /// Address statistics on the blockchain (confirmed transactions only) /// /// Based on mempool.space's format with type_index extension. -#[derive(Debug, Serialize, JsonSchema)] +#[derive(Debug, Default, Serialize, JsonSchema)] pub struct AddressChainStats { /// Total number of transaction outputs that funded this address #[schemars(example = 5)] diff --git a/crates/brk_structs/src/addressmempoolstats.rs b/crates/brk_structs/src/addressmempoolstats.rs index 48d3b4ead..90cb1f70f 100644 --- a/crates/brk_structs/src/addressmempoolstats.rs +++ b/crates/brk_structs/src/addressmempoolstats.rs @@ -1,4 +1,4 @@ -use crate::Sats; +use crate::{Sats, TxOut}; use schemars::JsonSchema; use serde::Serialize; @@ -7,7 +7,7 @@ use serde::Serialize; /// /// Based on mempool.space's format. /// -#[derive(Debug, Serialize, JsonSchema)] +#[derive(Debug, Default, Serialize, JsonSchema)] pub struct AddressMempoolStats { /// Number of unconfirmed transaction outputs funding this address #[schemars(example = 0)] @@ -29,3 +29,29 @@ pub struct AddressMempoolStats { #[schemars(example = 0)] pub tx_count: u32, } + +impl AddressMempoolStats { + pub fn receiving(&mut self, txout: &TxOut) { + self.funded_txo_count += 1; + self.funded_txo_sum += txout.value; + } + + pub fn received(&mut self, txout: &TxOut) { + self.funded_txo_count -= 1; + self.funded_txo_sum -= txout.value; + } + + pub fn sending(&mut self, txout: &TxOut) { + self.spent_txo_count += 1; + self.spent_txo_sum += txout.value; + } + + pub fn sent(&mut self, txout: &TxOut) { + self.spent_txo_count -= 1; + self.spent_txo_sum -= txout.value; + } + + pub fn update_tx_count(&mut self, tx_count: u32) { + self.tx_count = tx_count + } +} diff --git a/crates/brk_structs/src/addressstats.rs b/crates/brk_structs/src/addressstats.rs index 9c1f81ae4..afa9fe163 100644 --- a/crates/brk_structs/src/addressstats.rs +++ b/crates/brk_structs/src/addressstats.rs @@ -1,4 +1,4 @@ -use crate::{AddressChainStats, AddressMempoolStats}; +use crate::{Address, AddressChainStats, AddressMempoolStats}; use schemars::JsonSchema; use serde::Serialize; @@ -9,7 +9,7 @@ pub struct AddressStats { #[schemars( example = "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f" )] - pub address: String, + pub address: Address, /// Statistics for confirmed transactions on the blockchain pub chain_stats: AddressChainStats, diff --git a/crates/brk_structs/src/bytes.rs b/crates/brk_structs/src/bytes.rs index 717a2cb59..dc3282904 100644 --- a/crates/brk_structs/src/bytes.rs +++ b/crates/brk_structs/src/bytes.rs @@ -9,11 +9,14 @@ use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout}; DerefMut, PartialEq, Eq, + PartialOrd, + Ord, Immutable, IntoBytes, KnownLayout, FromBytes, Serialize, + Hash, )] pub struct U8x2([u8; 2]); impl From<&[u8]> for U8x2 { @@ -31,11 +34,14 @@ impl From<&[u8]> for U8x2 { DerefMut, PartialEq, Eq, + PartialOrd, + Ord, Immutable, IntoBytes, KnownLayout, FromBytes, Serialize, + Hash, )] pub struct U8x20([u8; 20]); impl From<&[u8]> for U8x20 { @@ -53,11 +59,14 @@ impl From<&[u8]> for U8x20 { DerefMut, PartialEq, Eq, + PartialOrd, + Ord, Immutable, IntoBytes, KnownLayout, FromBytes, Serialize, + Hash, )] pub struct U8x32([u8; 32]); impl From<&[u8]> for U8x32 { @@ -75,11 +84,14 @@ impl From<&[u8]> for U8x32 { DerefMut, PartialEq, Eq, + PartialOrd, + Ord, Immutable, IntoBytes, KnownLayout, FromBytes, Serialize, + Hash, )] pub struct U8x33(#[serde(with = "serde_bytes")] [u8; 33]); impl From<&[u8]> for U8x33 { @@ -97,11 +109,14 @@ impl From<&[u8]> for U8x33 { DerefMut, PartialEq, Eq, + PartialOrd, + Ord, Immutable, IntoBytes, KnownLayout, FromBytes, Serialize, + Hash, )] pub struct U8x65(#[serde(with = "serde_bytes")] [u8; 65]); impl From<&[u8]> for U8x65 { diff --git a/crates/brk_structs/src/lib.rs b/crates/brk_structs/src/lib.rs index f94f31174..d56075709 100644 --- a/crates/brk_structs/src/lib.rs +++ b/crates/brk_structs/src/lib.rs @@ -85,10 +85,9 @@ mod tx; mod txid; mod txidpath; mod txidprefix; +mod txin; mod txindex; -mod txinput; -mod txoutput; -mod txprevout; +mod txout; mod txstatus; mod txversion; mod typeindex; @@ -182,10 +181,9 @@ pub use tx::*; pub use txid::*; pub use txidpath::*; pub use txidprefix::*; +pub use txin::*; pub use txindex::*; -pub use txinput::*; -pub use txoutput::*; -pub use txprevout::*; +pub use txout::*; pub use txstatus::*; pub use txversion::*; pub use typeindex::*; diff --git a/crates/brk_structs/src/p2abytes.rs b/crates/brk_structs/src/p2abytes.rs index 6e23d20aa..1598660d9 100644 --- a/crates/brk_structs/src/p2abytes.rs +++ b/crates/brk_structs/src/p2abytes.rs @@ -7,7 +7,19 @@ use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout}; use crate::U8x2; #[derive( - Debug, Clone, Deref, PartialEq, Eq, Immutable, IntoBytes, KnownLayout, FromBytes, Serialize, + Debug, + Clone, + Deref, + PartialEq, + Eq, + PartialOrd, + Ord, + Immutable, + IntoBytes, + KnownLayout, + FromBytes, + Serialize, + Hash, )] pub struct P2ABytes(U8x2); diff --git a/crates/brk_structs/src/p2pk33bytes.rs b/crates/brk_structs/src/p2pk33bytes.rs index 939e6de5b..1f2da2710 100644 --- a/crates/brk_structs/src/p2pk33bytes.rs +++ b/crates/brk_structs/src/p2pk33bytes.rs @@ -7,7 +7,19 @@ use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout}; use crate::U8x33; #[derive( - Debug, Clone, Deref, PartialEq, Eq, Immutable, IntoBytes, KnownLayout, FromBytes, Serialize, + Debug, + Clone, + Deref, + PartialEq, + Eq, + PartialOrd, + Ord, + Immutable, + IntoBytes, + KnownLayout, + FromBytes, + Serialize, + Hash, )] pub struct P2PK33Bytes(U8x33); diff --git a/crates/brk_structs/src/p2pk65bytes.rs b/crates/brk_structs/src/p2pk65bytes.rs index cf064f3c8..6daf853fd 100644 --- a/crates/brk_structs/src/p2pk65bytes.rs +++ b/crates/brk_structs/src/p2pk65bytes.rs @@ -7,7 +7,19 @@ use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout}; use crate::U8x65; #[derive( - Debug, Clone, Deref, PartialEq, Eq, Immutable, IntoBytes, KnownLayout, FromBytes, Serialize, + Debug, + Clone, + Deref, + PartialEq, + Eq, + PartialOrd, + Ord, + Immutable, + IntoBytes, + KnownLayout, + FromBytes, + Serialize, + Hash, )] pub struct P2PK65Bytes(U8x65); diff --git a/crates/brk_structs/src/p2pkhbytes.rs b/crates/brk_structs/src/p2pkhbytes.rs index 8e69d8b2f..8001ca37e 100644 --- a/crates/brk_structs/src/p2pkhbytes.rs +++ b/crates/brk_structs/src/p2pkhbytes.rs @@ -7,7 +7,19 @@ use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout}; use crate::U8x20; #[derive( - Debug, Clone, Deref, PartialEq, Eq, Immutable, IntoBytes, KnownLayout, FromBytes, Serialize, + Debug, + Clone, + Deref, + PartialEq, + Eq, + PartialOrd, + Ord, + Immutable, + IntoBytes, + KnownLayout, + FromBytes, + Serialize, + Hash, )] pub struct P2PKHBytes(U8x20); diff --git a/crates/brk_structs/src/p2shbytes.rs b/crates/brk_structs/src/p2shbytes.rs index 7c5ebb75a..662abc557 100644 --- a/crates/brk_structs/src/p2shbytes.rs +++ b/crates/brk_structs/src/p2shbytes.rs @@ -7,7 +7,19 @@ use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout}; use crate::U8x20; #[derive( - Debug, Clone, Deref, PartialEq, Eq, Immutable, IntoBytes, KnownLayout, FromBytes, Serialize, + Debug, + Clone, + Deref, + PartialEq, + Eq, + PartialOrd, + Ord, + Immutable, + IntoBytes, + KnownLayout, + FromBytes, + Serialize, + Hash, )] pub struct P2SHBytes(U8x20); diff --git a/crates/brk_structs/src/p2trbytes.rs b/crates/brk_structs/src/p2trbytes.rs index 108140e2d..52c0007eb 100644 --- a/crates/brk_structs/src/p2trbytes.rs +++ b/crates/brk_structs/src/p2trbytes.rs @@ -7,7 +7,19 @@ use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout}; use crate::U8x32; #[derive( - Debug, Clone, Deref, PartialEq, Eq, Immutable, IntoBytes, KnownLayout, FromBytes, Serialize, + Debug, + Clone, + Deref, + PartialEq, + Eq, + PartialOrd, + Ord, + Immutable, + IntoBytes, + KnownLayout, + FromBytes, + Serialize, + Hash, )] pub struct P2TRBytes(U8x32); diff --git a/crates/brk_structs/src/p2wpkhbytes.rs b/crates/brk_structs/src/p2wpkhbytes.rs index 91d3de6fa..ec9d83e16 100644 --- a/crates/brk_structs/src/p2wpkhbytes.rs +++ b/crates/brk_structs/src/p2wpkhbytes.rs @@ -7,7 +7,19 @@ use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout}; use crate::U8x20; #[derive( - Debug, Clone, Deref, PartialEq, Eq, Immutable, IntoBytes, KnownLayout, FromBytes, Serialize, + Debug, + Clone, + Deref, + PartialEq, + Eq, + PartialOrd, + Ord, + Immutable, + IntoBytes, + KnownLayout, + FromBytes, + Serialize, + Hash, )] pub struct P2WPKHBytes(U8x20); diff --git a/crates/brk_structs/src/p2wshbytes.rs b/crates/brk_structs/src/p2wshbytes.rs index 9ee83a406..3bb91cba1 100644 --- a/crates/brk_structs/src/p2wshbytes.rs +++ b/crates/brk_structs/src/p2wshbytes.rs @@ -7,7 +7,19 @@ use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout}; use crate::U8x32; #[derive( - Debug, Clone, Deref, PartialEq, Eq, Immutable, IntoBytes, KnownLayout, FromBytes, Serialize, + Debug, + Clone, + Deref, + PartialEq, + Eq, + PartialOrd, + Ord, + Immutable, + IntoBytes, + KnownLayout, + FromBytes, + Serialize, + Hash, )] pub struct P2WSHBytes(U8x32); diff --git a/crates/brk_structs/src/tx.rs b/crates/brk_structs/src/tx.rs index 9e5ba78c6..f7f3d375a 100644 --- a/crates/brk_structs/src/tx.rs +++ b/crates/brk_structs/src/tx.rs @@ -1,43 +1,86 @@ -use crate::{RawLockTime, Sats, TxIndex, TxInput, TxOutput, TxStatus, TxVersion, Txid}; +use crate::{RawLockTime, Sats, TxIn, TxIndex, TxOut, TxStatus, TxVersion, Txid, Weight}; +use bitcoincore_rpc::Client; use schemars::JsonSchema; use serde::Serialize; +use vecdb::CheckedSub; #[derive(Debug, Serialize, JsonSchema)] /// Transaction information compatible with mempool.space API format -pub struct Tx { - #[schemars(example = "9a0b3b8305bb30cacf9e8443a90d53a76379fb3305047fdeaa4e4b0934a2a1ba")] - pub txid: Txid, - +pub struct Transaction { #[schemars(example = TxIndex::new(0))] - pub index: TxIndex, + pub index: Option, + + #[schemars(example = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")] + pub txid: Txid, #[schemars(example = 2)] pub version: TxVersion, #[schemars(example = 0)] - pub locktime: RawLockTime, + #[serde(rename = "locktime")] + pub lock_time: RawLockTime, /// Transaction size in bytes #[schemars(example = 222)] - pub size: u32, + #[serde(rename = "size")] + pub total_size: usize, - /// Transaction weight (for SegWit transactions) + /// Transaction weight #[schemars(example = 558)] - pub weight: u32, + pub weight: Weight, /// Number of signature operations #[schemars(example = 1)] - pub sigops: u32, + #[serde(rename = "sigops")] + pub total_sigop_cost: usize, /// Transaction fee in satoshis #[schemars(example = Sats::new(31))] pub fee: Sats, /// Transaction inputs - pub vin: Vec, + #[serde(rename = "vin")] + pub input: Vec, /// Transaction outputs - pub vout: Vec, + #[serde(rename = "vout")] + pub output: Vec, pub status: TxStatus, } + +impl Transaction { + pub fn fee(&self) -> Option { + let in_ = self + .input + .iter() + .map(|txin| txin.prevout.as_ref().map(|txout| txout.value)) + .sum::>()?; + let out = self.output.iter().map(|txout| txout.value).sum::(); + Some(in_.checked_sub(out).unwrap()) + } +} + +impl Transaction { + pub fn from_mempool(tx: bitcoin::Transaction, rpc: &Client) -> Self { + let mut this = Self { + index: None, + txid: tx.compute_txid().into(), + version: tx.version.into(), + total_sigop_cost: tx.total_sigop_cost(|_| None), + weight: tx.weight().into(), + lock_time: tx.lock_time.into(), + total_size: tx.total_size(), + fee: Sats::default(), + input: tx + .input + .into_iter() + .map(|txin| TxIn::from((txin, rpc))) + .collect(), + output: tx.output.into_iter().map(TxOut::from).collect(), + status: TxStatus::UNCOMFIRMED, + }; + this.fee = this.fee().unwrap_or_default(); + this + } +} diff --git a/crates/brk_structs/src/txid.rs b/crates/brk_structs/src/txid.rs index f161e387e..2d89c9cd2 100644 --- a/crates/brk_structs/src/txid.rs +++ b/crates/brk_structs/src/txid.rs @@ -8,7 +8,17 @@ use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout}; /// Transaction ID (hash) #[derive( - Debug, Deref, Clone, PartialEq, Eq, Immutable, IntoBytes, KnownLayout, FromBytes, JsonSchema, + Debug, + Deref, + Clone, + PartialEq, + Eq, + Immutable, + IntoBytes, + KnownLayout, + FromBytes, + JsonSchema, + Hash, )] #[schemars( example = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b", diff --git a/crates/brk_structs/src/txin.rs b/crates/brk_structs/src/txin.rs new file mode 100644 index 000000000..6aed0a2c5 --- /dev/null +++ b/crates/brk_structs/src/txin.rs @@ -0,0 +1,118 @@ +use crate::{TxOut, Txid, Vout}; +use bitcoin::{Script, ScriptBuf}; +use bitcoincore_rpc::{Client, RpcApi}; +use schemars::JsonSchema; +use serde::{Serialize, Serializer, ser::SerializeStruct}; + +#[derive(Debug, JsonSchema)] +/// Transaction input +pub struct TxIn { + /// Transaction ID of the output being spent + #[schemars(example = "0000000000000000000000000000000000000000000000000000000000000000")] + pub txid: Txid, + + #[schemars(example = 0)] + pub vout: Vout, + + /// Information about the previous output being spent + #[schemars(example = None as Option)] + pub prevout: Option, + + /// Signature script (for non-SegWit inputs) + #[schemars( + rename = "scriptsig", + with = "String", + example = "04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73" + )] + pub script_sig: ScriptBuf, + + /// Signature script in assembly format + #[allow(dead_code)] + #[schemars( + rename = "scriptsig_asm", + with = "String", + example = "OP_PUSHBYTES_4 ffff001d OP_PUSHBYTES_1 04 OP_PUSHBYTES_69 5468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73" + )] + script_sig_asm: (), + + // /// Witness data (for SegWit inputs) + // #[schemars(example = vec!["3045022100d0c9936990bf00bdba15f425f0f360a223d5cbf81f4bf8477fe6c6d838fb5fae02207e42a8325a4dd41702bf065aa6e0a1b7b0b8ee92a5e6c182da018b0afc82c40601".to_string()])] + // pub witness: Vec, + // + /// Whether this input is a coinbase (block reward) input + #[schemars(example = false)] + pub is_coinbase: bool, + + /// Input sequence number + #[schemars(example = 429496729)] + pub sequence: u32, + + /// Inner redeemscript in assembly format (for P2SH-wrapped SegWit) + #[allow(dead_code)] + #[schemars( + rename = "inner_redeemscript_asm", + with = "Option", + example = Some("OP_0 OP_PUSHBYTES_20 992a1f7420fc5285070d19c71ff2efb1e356ad2f".to_string()) + )] + inner_redeem_script_asm: (), +} + +impl TxIn { + pub fn script_sig_asm(&self) -> String { + self.script_sig.to_asm_string() + } + + pub fn redeem_script(&self) -> Option<&Script> { + self.script_sig.redeem_script() + } +} + +impl From<(bitcoin::TxIn, &Client)> for TxIn { + fn from((txin, rpc): (bitcoin::TxIn, &Client)) -> Self { + let txout_result = rpc + .get_tx_out( + &txin.previous_output.txid, + txin.previous_output.vout, + Some(true), + ) + .unwrap(); + + let is_coinbase = txout_result.as_ref().is_none_or(|r| r.coinbase); + + // txin.witness + + // txin.script_sig.redeem_script() + + Self { + is_coinbase, + prevout: txout_result.map(TxOut::from), + txid: txin.previous_output.txid.into(), + vout: txin.previous_output.vout.into(), + script_sig: txin.script_sig, + script_sig_asm: (), + sequence: txin.sequence.into(), + // witness: + inner_redeem_script_asm: (), + } + } +} + +impl Serialize for TxIn { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("TxIn", 8)?; + + state.serialize_field("txid", &self.txid)?; + state.serialize_field("vout", &self.vout)?; + state.serialize_field("prevout", &self.prevout)?; + state.serialize_field("scriptsig", &self.script_sig.to_hex_string())?; + state.serialize_field("scriptsig_asm", &self.script_sig_asm())?; + state.serialize_field("is_coinbase", &self.is_coinbase)?; + state.serialize_field("sequence", &self.sequence)?; + state.serialize_field("inner_redeemscript_asm", &self.redeem_script())?; + + state.end() + } +} diff --git a/crates/brk_structs/src/txinput.rs b/crates/brk_structs/src/txinput.rs deleted file mode 100644 index fd481319d..000000000 --- a/crates/brk_structs/src/txinput.rs +++ /dev/null @@ -1,40 +0,0 @@ -use crate::{TxPrevout, Txid, Vout}; -use schemars::JsonSchema; -use serde::Serialize; - -#[derive(Debug, Serialize, JsonSchema)] -/// Transaction input -pub struct TxInput { - /// Transaction ID of the output being spent - pub txid: Txid, - - #[schemars(example = 0)] - pub vout: Vout, - - /// Information about the previous output being spent - pub prevout: Option, - - /// Signature script (for non-SegWit inputs) - #[schemars(example = "")] - pub scriptsig: String, - - /// Signature script in assembly format - #[schemars(example = "")] - pub scriptsig_asm: String, - - /// Witness data (for SegWit inputs) - #[schemars(example = vec!["3045022100d0c9936990bf00bdba15f425f0f360a223d5cbf81f4bf8477fe6c6d838fb5fae02207e42a8325a4dd41702bf065aa6e0a1b7b0b8ee92a5e6c182da018b0afc82c40601".to_string()])] - pub witness: Vec, - - /// Whether this input is a coinbase (block reward) input - #[schemars(example = false)] - pub is_coinbase: bool, - - /// Input sequence number - #[schemars(example = 429496729)] - pub sequence: u32, - - /// Inner redeemscript in assembly format (for P2SH-wrapped SegWit) - #[schemars(example = Some("OP_0 OP_PUSHBYTES_20 992a1f7420fc5285070d19c71ff2efb1e356ad2f".to_string()))] - pub inner_redeemscript_asm: Option, -} diff --git a/crates/brk_structs/src/txout.rs b/crates/brk_structs/src/txout.rs new file mode 100644 index 000000000..483d63004 --- /dev/null +++ b/crates/brk_structs/src/txout.rs @@ -0,0 +1,102 @@ +use crate::{Address, AddressBytes, OutputType, Sats}; +use bitcoin::ScriptBuf; +use bitcoincore_rpc::json::GetTxOutResult; +use schemars::JsonSchema; +use serde::{Serialize, Serializer, ser::SerializeStruct}; + +#[derive(Debug, JsonSchema)] +/// Transaction output +pub struct TxOut { + /// Script pubkey (locking script) + #[serde( + rename = "scriptpubkey", + serialize_with = "serialize_with_script_pubkey" + )] + #[schemars( + with = "String", + example = "00143b064c595a95f977f00352d6e917501267cacdc6" + )] + pub script_pubkey: ScriptBuf, + + /// Script pubkey in assembly format + #[allow(dead_code)] + #[serde(skip, rename = "scriptpubkey_asm")] + #[schemars( + with = "String", + example = "OP_0 OP_PUSHBYTES_20 3b064c595a95f977f00352d6e917501267cacdc6" + )] + script_pubkey_asm: (), + + /// Script type (p2pk, p2pkh, p2sh, p2wpkh, p2wsh, p2tr, op_return, etc.) + #[allow(dead_code)] + #[serde(skip, rename = "scriptpubkey_type")] + #[schemars(with = "OutputType", example = &"v0_p2wpkh")] + script_pubkey_type: (), + + /// Bitcoin address (if applicable, None for OP_RETURN) + #[allow(dead_code)] + #[serde(skip, rename = "scriptpubkey_address")] + #[schemars(with = "Option
", example = Some("bc1q8vryck26jhuh0uqr2ttwj96szfnu4nwxfmu39y".to_string()))] + script_pubkey_address: (), + + /// Value of the output in satoshis + #[schemars(example = Sats::new(7782))] + pub value: Sats, +} + +impl TxOut { + pub fn address(&self) -> Option
{ + Address::try_from(&self.script_pubkey).ok() + } + + pub fn address_bytes(&self) -> Option { + AddressBytes::try_from(&self.script_pubkey).ok() + } + + pub fn type_(&self) -> OutputType { + OutputType::from(&self.script_pubkey) + } + + pub fn script_pubkey_asm(&self) -> String { + self.script_pubkey.to_asm_string() + } +} + +impl From for TxOut { + fn from(txout: bitcoin::TxOut) -> Self { + Self { + script_pubkey: txout.script_pubkey, + value: txout.value.into(), + script_pubkey_asm: (), + script_pubkey_address: (), + script_pubkey_type: (), + } + } +} + +impl From for TxOut { + fn from(value: GetTxOutResult) -> Self { + Self { + script_pubkey: value.script_pub_key.script().unwrap(), + script_pubkey_address: (), + script_pubkey_asm: (), + script_pubkey_type: (), + value: value.value.into(), + } + } +} + +impl Serialize for TxOut { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("TxOut", 5)?; + state.serialize_field("scriptpubkey", &self.script_pubkey.to_hex_string())?; + state.serialize_field("scriptpubkey_asm", &self.script_pubkey_asm())?; + state.serialize_field("scriptpubkey_type", &self.type_())?; + state.serialize_field("scriptpubkey_address", &self.address())?; + state.serialize_field("value", &self.value)?; + state.end() + } +} diff --git a/crates/brk_structs/src/txoutput.rs b/crates/brk_structs/src/txoutput.rs deleted file mode 100644 index 41c5bd145..000000000 --- a/crates/brk_structs/src/txoutput.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::Sats; -use schemars::JsonSchema; -use serde::Serialize; - -#[derive(Debug, Serialize, JsonSchema)] -/// Transaction output -pub struct TxOutput { - /// Script pubkey (locking script) - #[schemars(example = "00143b064c595a95f977f00352d6e917501267cacdc6")] - pub scriptpubkey: String, - - /// Script pubkey in assembly format - #[schemars(example = "OP_0 OP_PUSHBYTES_20 3b064c595a95f977f00352d6e917501267cacdc6")] - pub scriptpubkey_asm: String, - - /// Script type (p2pk, p2pkh, p2sh, p2wpkh, p2wsh, p2tr, op_return, etc.) - #[schemars(example = &"v0_p2wpkh")] - pub scriptpubkey_type: String, - - /// Bitcoin address (if applicable, None for OP_RETURN) - #[schemars(example = Some("bc1q8vryck26jhuh0uqr2ttwj96szfnu4nwxfmu39y".to_string()))] - pub scriptpubkey_address: Option, - - /// Value of the output in satoshis - #[schemars(example = Sats::new(7782))] - pub value: Sats, -} diff --git a/crates/brk_structs/src/txprevout.rs b/crates/brk_structs/src/txprevout.rs deleted file mode 100644 index 56f7c9fad..000000000 --- a/crates/brk_structs/src/txprevout.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::Sats; -use schemars::JsonSchema; -use serde::Serialize; - -#[derive(Debug, Serialize, JsonSchema)] -/// Information about a previous transaction output being spent -pub struct TxPrevout { - /// Script pubkey (locking script) - #[schemars(example = "00143b064c595a95f977f00352d6e917501267cacdc6")] - pub scriptpubkey: String, - - /// Script pubkey in assembly format - #[schemars(example = "OP_0 OP_PUSHBYTES_20 3b064c595a95f977f00352d6e917501267cacdc6")] - pub scriptpubkey_asm: String, - - /// Script type (p2pk, p2pkh, p2sh, p2wpkh, p2wsh, p2tr, etc.) - #[schemars(example = &"v0_p2wpkh")] - pub scriptpubkey_type: String, - - /// Bitcoin address (if applicable) - #[schemars(example = Some("bc1q8vryck26jhuh0uqr2ttwj96szfnu4nwxfmu39y".to_string()))] - pub scriptpubkey_address: Option, - - /// Value of the output in satoshis - #[schemars(example = Sats::new(7813))] - pub value: Sats, -} diff --git a/crates/brk_structs/src/txstatus.rs b/crates/brk_structs/src/txstatus.rs index cd1fe634e..11c0ade32 100644 --- a/crates/brk_structs/src/txstatus.rs +++ b/crates/brk_structs/src/txstatus.rs @@ -22,3 +22,12 @@ pub struct TxStatus { #[schemars(example = Some(1759000868))] pub block_time: Option, } + +impl TxStatus { + pub const UNCOMFIRMED: Self = Self { + confirmed: false, + block_hash: None, + block_height: None, + block_time: None, + }; +} diff --git a/crates/brk_structs/src/weight.rs b/crates/brk_structs/src/weight.rs index c046feafc..c25ff8a5f 100644 --- a/crates/brk_structs/src/weight.rs +++ b/crates/brk_structs/src/weight.rs @@ -2,6 +2,7 @@ use std::ops::{Add, AddAssign, Div}; use allocative::Allocative; use derive_deref::Deref; +use schemars::JsonSchema; use serde::Serialize; use vecdb::StoredCompressed; use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout}; @@ -22,6 +23,7 @@ use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout}; Serialize, StoredCompressed, Allocative, + JsonSchema, )] pub struct Weight(u64); diff --git a/docs/README.md b/docs/README.md index 584fbaa70..5f7b581f6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -143,7 +143,7 @@ Contributions from the Bitcoin community are welcome! Here's how to get involved | [`brk_cli`](https://crates.io/crates/brk_cli) | Command line interface for running BRK instances | | [`brk_server`](https://crates.io/crates/brk_server) | REST API server for data access | | [`brk_mcp`](https://crates.io/crates/brk_mcp) | Model Context Protocol bridge for AI integration | -| [`brk_parser`](https://crates.io/crates/brk_parser) | High-performance Bitcoin block parser | +| [`brk_reader`](https://crates.io/crates/brk_reader) | High-performance Bitcoin block parser | | [`brk_indexer`](https://crates.io/crates/brk_indexer) | Blockchain data indexing engine | | [`brk_computer`](https://crates.io/crates/brk_computer) | Bitcoin metrics and dataset computation | | [`brk_interface`](https://crates.io/crates/brk_interface) | Data formatting and query interface |