parser: switch to biter

This commit is contained in:
k
2024-08-03 15:17:57 +02:00
parent afacea3fbb
commit 9067c28d24
35 changed files with 494 additions and 1589 deletions
+4
View File
@@ -11,3 +11,7 @@ TODO.md
.stfolder
/charts
/price
*\ copy*
/dist
/start-node.sh
+4
View File
@@ -4,7 +4,11 @@
![Image of the Satonomics Web App version 0.X.Y](./assets/v0.X.Y.jpg)
### Parser
- Change the block iterator from a custom version of [bitcoin-explorer](https://crates.io/crates/bitcoin-explorer) to the homemade [biter](https://crates.io/crates/biter) which allows the parser to run alongside `bitcoind`
- Use the Bitcoin RPC server for various calls instead of running commands and then parsing the JSON from the output
- Updated the config, run with `-h` to see
## v. 0.3.0 | [853930](https://mempool.space/block/00000000000000000002eb5e9a7950ca2d5d98bd1ed28fc9098aa630d417985d) - 2024/07/26
@@ -165,6 +165,20 @@ export function SettingsFrame({
url: "https://primal.net/p/npub1mnwjn40hr042rsmzu64rsnwsw07uegg4tjkv620c94p6e797wkvq3qeujc",
amount: 5_000,
},
{
name: "lassdas",
url: "https://primal.net/p/npub1gmhctt2hmjqz8ay2x8h5f8fl3h4fpfcezwqneal3usu3u65qca4s8094ea",
amount: 210_000,
},
{
name: "anon",
amount: 21_000,
},
{
name: "xplbzx",
url: "https://primal.net/p/npub1e0f808a350rxrhppu4zylzljt3arfpvrrpqdg6ft78xy6u49kq5slf0g92",
amount: 10_110,
},
]
.sort((a, b) =>
b.amount !== a.amount
+156 -82
View File
@@ -27,7 +27,7 @@ dependencies = [
"getrandom",
"once_cell",
"version_check",
"zerocopy",
"zerocopy 0.7.35",
]
[[package]]
@@ -152,6 +152,12 @@ dependencies = [
"bitcoin_hashes",
]
[[package]]
name = "base64"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]]
name = "base64"
version = "0.22.0"
@@ -236,6 +242,45 @@ dependencies = [
"serde",
]
[[package]]
name = "bitcoincore-rpc"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aedd23ae0fd321affb4bbbc36126c6f49a32818dc6b979395d24da8c9d4e80ee"
dependencies = [
"bitcoincore-rpc-json",
"jsonrpc",
"log",
"serde",
"serde_json",
]
[[package]]
name = "bitcoincore-rpc-json"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8909583c5fab98508e80ef73e5592a651c954993dc6b7739963257d19f0e71a"
dependencies = [
"bitcoin",
"serde",
"serde_json",
]
[[package]]
name = "biter"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd2ea6b4c4046feaaf73f3dff1cc648328af021d50be95946e0e7a807c985b47"
dependencies = [
"bitcoin",
"bitcoincore-rpc",
"crossbeam",
"derived-deref",
"rayon",
"serde",
"serde_json",
]
[[package]]
name = "bitflags"
version = "1.3.2"
@@ -339,15 +384,6 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
[[package]]
name = "cmake"
version = "0.1.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130"
dependencies = [
"cc",
]
[[package]]
name = "color-eyre"
version = "0.6.3"
@@ -477,12 +513,6 @@ dependencies = [
"parking_lot_core 0.9.10",
]
[[package]]
name = "db-key"
version = "0.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b72465f46d518f6015d9cf07f7f3013a95dd6b9c2747c3d65ae0cce43929d14f"
[[package]]
name = "derive_deref"
version = "1.1.1"
@@ -494,6 +524,17 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "derived-deref"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "805ef2023ccd65425743a91ecd11fc020979a0b01921db3104fb606d18a7b43e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
]
[[package]]
name = "either"
version = "1.12.0"
@@ -560,12 +601,6 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
[[package]]
name = "ffi-opaque"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec54ac60a7f2ee9a97cad9946f9bf629a3bc6a7ae59e68983dc9318f5a54b81a"
[[package]]
name = "fnv"
version = "1.0.7"
@@ -966,35 +1001,24 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "jsonrpc"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3662a38d341d77efecb73caf01420cfa5aa63c0253fd7bc05289ef9f6616e1bf"
dependencies = [
"base64 0.13.1",
"minreq",
"serde",
"serde_json",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "leveldb"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32651baaaa5596b3a6e0bee625e73fd0334c167db0ea5ac68750ef9a629a2d6a"
dependencies = [
"db-key",
"leveldb-sys",
"libc",
]
[[package]]
name = "leveldb-sys"
version = "2.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dd94a4d0242a437e5e41a27c782b69a624469ca1c4d1e5cb3c337f74a8031d4"
dependencies = [
"cmake",
"ffi-opaque",
"libc",
"num_cpus",
]
[[package]]
name = "libc"
version = "0.2.155"
@@ -1064,14 +1088,26 @@ dependencies = [
]
[[package]]
name = "mio"
version = "0.8.11"
name = "minreq"
version = "2.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
checksum = "763d142cdff44aaadd9268bebddb156ef6c65a0e13486bb81673cf2d8739f9b0"
dependencies = [
"log",
"serde",
"serde_json",
]
[[package]]
name = "mio"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4"
dependencies = [
"hermit-abi",
"libc",
"wasi",
"windows-sys 0.48.0",
"windows-sys 0.52.0",
]
[[package]]
@@ -1111,16 +1147,6 @@ dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "object"
version = "0.32.2"
@@ -1195,16 +1221,6 @@ version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
[[package]]
name = "par-iter-sync"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efa981aaed94bf59211f644922155e8a33bcb01ed662cd63426653187f562790"
dependencies = [
"crossbeam",
"num_cpus",
]
[[package]]
name = "parking_lot"
version = "0.11.2"
@@ -1249,20 +1265,16 @@ version = "0.3.0"
dependencies = [
"allocative",
"bincode",
"bitcoin",
"bitcoin_hashes",
"byteorder",
"biter",
"chrono",
"clap",
"color-eyre",
"db-key",
"derive_deref",
"inferno",
"itertools",
"leveldb",
"memory-stats",
"ordered-float",
"par-iter-sync",
"rayon",
"reqwest",
"sanakirja",
@@ -1315,6 +1327,15 @@ version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
[[package]]
name = "ppv-lite86"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dee4364d9f3b902ef14fab8a1ddffb783a1cb6b4bba3bfc1fa3922732c7de97f"
dependencies = [
"zerocopy 0.6.6",
]
[[package]]
name = "proc-macro2"
version = "1.0.85"
@@ -1342,6 +1363,36 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "rayon"
version = "1.10.0"
@@ -1386,7 +1437,7 @@ version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37"
dependencies = [
"base64",
"base64 0.22.0",
"bytes",
"encoding_rs",
"futures-channel",
@@ -1486,7 +1537,7 @@ version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d"
dependencies = [
"base64",
"base64 0.22.0",
"rustls-pki-types",
]
@@ -1556,6 +1607,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e0cc0f1cf93f4969faf3ea1c7d8a9faed25918d96affa959720823dfe86d4f3"
dependencies = [
"bitcoin_hashes",
"rand",
"secp256k1-sys",
"serde",
]
@@ -1614,11 +1666,12 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.120"
version = "1.0.122"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5"
checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
]
@@ -1810,9 +1863,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.37.0"
version = "1.39.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787"
checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1"
dependencies = [
"backtrace",
"bytes",
@@ -1820,7 +1873,7 @@ dependencies = [
"mio",
"pin-project-lite",
"socket2",
"windows-sys 0.48.0",
"windows-sys 0.52.0",
]
[[package]]
@@ -2315,13 +2368,34 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "zerocopy"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "854e949ac82d619ee9a14c66a1b674ac730422372ccb759ce0c39cabcf2bf8e6"
dependencies = [
"byteorder",
"zerocopy-derive 0.6.6",
]
[[package]]
name = "zerocopy"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
"zerocopy-derive",
"zerocopy-derive 0.7.35",
]
[[package]]
name = "zerocopy-derive"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "125139de3f6b9d625c39e2efdd73d41bdac468ccd556556440e322be0e1bbd91"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
]
[[package]]
+1 -5
View File
@@ -11,20 +11,16 @@ edition = "2021"
[dependencies]
allocative = "0.3.3"
bincode = { git = "https://github.com/bincode-org/bincode.git" }
bitcoin = { version = "0.32.2", features = ["serde"] }
biter = "0.1.0"
bitcoin_hashes = { version = "0.14.0" }
byteorder = "1.5.0"
chrono = { version = "0.4.38", features = ["serde"] }
clap = { version = "4.5.11", features = ["derive"] }
color-eyre = "0.6.3"
db-key = "=0.0.5"
derive_deref = "1.1.1"
inferno = "0.11.20"
itertools = "0.13.0"
leveldb = "0.8.6"
memory-stats = "1.2.0"
ordered-float = "4.2.1"
par-iter-sync = "0.1.11"
rayon = "1.10.0"
reqwest = { version = "0.12.5", features = ["blocking", "json"] }
sanakirja = "1.4.2"
+32 -10
View File
@@ -8,15 +8,22 @@ use parse::ParseData;
use crate::{
actions::{export, find_first_inserted_unsafe_height, parse},
bitcoin::BitcoinDB,
create_rpc,
databases::Databases,
datasets::{AllDatasets, ComputeData},
states::{AddressCohortsDurableStates, States, UTXOCohortsDurableStates},
structs::{Date, DateData, MapKey},
utils::{generate_allocation_files, log, time},
Config, Height,
};
pub fn iter_blocks(bitcoin_db: &BitcoinDB, block_count: usize) -> color_eyre::Result<()> {
pub fn iter_blocks(
config: &Config,
rpc: &biter::bitcoincore_rpc::Client,
approx_block_count: usize,
) -> color_eyre::Result<()> {
let export_dir = "./target/outputs";
let should_insert = true;
let should_export = true;
let study_ram_usage = false;
@@ -44,11 +51,19 @@ pub fn iter_blocks(bitcoin_db: &BitcoinDB, block_count: usize) -> color_eyre::Re
log(&format!("Starting parsing at height: {height}"));
let mut block_iter = bitcoin_db.iter_block(height.to_usize(), block_count);
let mut next_block_opt = None;
let mut blocks_loop_date = None;
let block_receiver = biter::new(
config.datadir.as_ref().unwrap(),
export_dir,
Some(height.to_usize()),
None,
create_rpc(config).unwrap(),
);
let mut block_iter = block_receiver.iter();
'parsing: loop {
let instant = Instant::now();
@@ -67,15 +82,22 @@ pub fn iter_blocks(bitcoin_db: &BitcoinDB, block_count: usize) -> color_eyre::Re
next_block_opt = block_iter.next();
if let Some(current_block) = current_block_opt {
if let Some((_current_block_height, current_block, _current_block_hash)) =
current_block_opt
{
let timestamp = current_block.header.time;
let current_block_date = Date::from_timestamp(timestamp);
let current_block_height = height + blocks_loop_i;
let current_block_height: Height = height + blocks_loop_i;
if current_block_height.to_usize() != _current_block_height {
dbg!(current_block_height, _current_block_height);
panic!()
}
let next_block_date = next_block_opt
.as_ref()
.map(|next_block| Date::from_timestamp(next_block.header.time));
.map(|(_, next_block, _)| Date::from_timestamp(next_block.header.time));
// Always run for the first block of the loop
if blocks_loop_date.is_none() {
@@ -139,7 +161,7 @@ pub fn iter_blocks(bitcoin_db: &BitcoinDB, block_count: usize) -> color_eyre::Re
}
parse(ParseData {
bitcoin_db,
rpc,
block: current_block,
block_index: blocks_loop_i,
compute_addresses,
@@ -162,7 +184,7 @@ pub fn iter_blocks(bitcoin_db: &BitcoinDB, block_count: usize) -> color_eyre::Re
let is_new_month = next_block_date
.map_or(true, |next_block_date| next_block_date.day() == 1);
if is_new_month || height.is_close_to_end(block_count) {
if is_new_month || height.is_close_to_end(approx_block_count) {
break 'days;
}
@@ -196,7 +218,7 @@ pub fn iter_blocks(bitcoin_db: &BitcoinDB, block_count: usize) -> color_eyre::Re
}
if should_export {
let is_safe = height.is_safe(block_count);
let is_safe = height.is_safe(approx_block_count);
export(ExportedData {
databases: is_safe.then_some(&mut databases),
+22 -8
View File
@@ -1,12 +1,14 @@
use std::{collections::BTreeMap, ops::ControlFlow, thread};
use bitcoin::{Block, Txid};
use biter::{
bitcoin::{Block, Txid},
bitcoincore_rpc::RpcApi,
};
use itertools::Itertools;
use rayon::prelude::*;
use crate::{
bitcoin::BitcoinDB,
databases::{
AddressIndexToAddressData, AddressIndexToEmptyAddressData, AddressToAddressIndex,
Databases, TxidToTxData, TxoutIndexToAddressIndex, TxoutIndexToAmount,
@@ -23,7 +25,7 @@ use crate::{
};
pub struct ParseData<'a> {
pub bitcoin_db: &'a BitcoinDB,
// pub bitcoin_cli: &'a BitcoinCli,
pub block: Block,
pub block_index: usize,
pub compute_addresses: bool,
@@ -33,13 +35,13 @@ pub struct ParseData<'a> {
pub first_date_height: Height,
pub height: Height,
pub is_date_last_block: bool,
pub rpc: &'a biter::bitcoincore_rpc::Client,
pub states: &'a mut States,
pub timestamp: u32,
}
pub fn parse(
ParseData {
bitcoin_db,
block,
block_index,
compute_addresses,
@@ -49,6 +51,7 @@ pub fn parse(
first_date_height,
height,
is_date_last_block,
rpc,
states,
timestamp,
}: ParseData,
@@ -334,10 +337,16 @@ pub fn parse(
// Can be none because 0 sats inputs happen
// https://mempool.space/tx/f329e55c2de9b821356e6f2c4bba923ea7030cad61120f5ced5d4429f5c86fda#vin=27
if input_tx_data.is_none() {
if !enable_check_if_txout_value_is_zero_in_db
|| bitcoin_db
.check_if_txout_value_is_zero(&input_txid, input_vout as usize)
|| rpc
.get_tx_out(&input_txid, input_vout, None)
.unwrap()
.unwrap()
.value
.to_sat()
== 0
{
return ControlFlow::Continue::<()>(());
}
@@ -374,8 +383,13 @@ pub fn parse(
if input_amount_and_address_index.is_none() {
if !enable_check_if_txout_value_is_zero_in_db
|| bitcoin_db
.check_if_txout_value_is_zero(&input_txid, input_vout as usize)
|| rpc
.get_tx_out(&input_txid, input_vout as u32, None)
.unwrap()
.unwrap()
.value
.to_sat()
== 0
{
return ControlFlow::Continue::<()>(());
}
-3
View File
@@ -1,3 +0,0 @@
mod multisig;
pub use multisig::*;
-2
View File
@@ -1,2 +0,0 @@
pub const NUMBER_OF_UNSAFE_BLOCKS: usize = 100;
pub const TARGET_BLOCKS_PER_DAY: usize = 144;
-137
View File
@@ -1,137 +0,0 @@
use std::{process::Command, thread::sleep, time::Duration};
use color_eyre::eyre::eyre;
use serde_json::Value;
use crate::{
structs::Height,
utils::{log, log_output, retry},
Config,
};
struct BlockchainInfo {
pub headers: u64,
pub blocks: u64,
}
pub struct BitcoinDaemon {
config: Config,
}
impl BitcoinDaemon {
pub fn new(config: &Config) -> Self {
Self {
config: config.clone(),
}
}
pub fn start(&self) {
sleep(Duration::from_secs(1));
let mut command = Command::new("bitcoind");
command
.arg(self.datadir_arg())
.arg("-txindex=1")
.arg("-daemon");
if self.config.blocksonly.unwrap() {
command.arg("-blocksonly");
}
if let Some(ip) = self.config.rpcconnect.as_ref() {
command.arg(format!("-rpcconnect={}", ip));
}
// bitcoind -datadir=/Users/k/Developer/bitcoin -blocksonly -txindex=1 -daemon
let output = command
.output()
.expect("bitcoind to be able to properly start");
log_output(&output);
}
pub fn stop(&self) {
// bitcoin-cli -datadir=/Users/k/Developer/bitcoin stop
let output = Command::new("bitcoin-cli")
.arg(self.datadir_arg())
.arg("stop")
.output()
.unwrap();
if output.status.success() {
log_output(&output);
sleep(Duration::from_secs(15));
}
}
pub fn wait_sync(&self) {
while !self.check_if_fully_synced() {
sleep(Duration::from_secs(5))
}
}
pub fn wait_for_new_block(&self, last_block_height: Height) {
log("Waiting for new block...");
while last_block_height == self.get_blockchain_info().headers {
sleep(Duration::from_secs(5))
}
}
pub fn check_if_fully_synced(&self) -> bool {
let BlockchainInfo { blocks, headers } = self.get_blockchain_info();
let synced = blocks == headers;
if synced {
log(&format!("Synced ! ({blocks} blocks)"));
} else {
log(&format!("Syncing... ({} remaining)", headers - blocks));
}
synced
}
fn get_blockchain_info(&self) -> BlockchainInfo {
retry(
|_| {
// bitcoin-cli -datadir=/Users/k/Developer/bitcoin getblockchaininfo
let output = Command::new("bitcoin-cli")
.arg(self.datadir_arg())
.arg("getblockchaininfo")
.output()?;
let output = String::from_utf8_lossy(&output.stdout);
let json: Value = serde_json::from_str(&output)?;
let json = json.as_object().ok_or(eyre!("json as object failed"))?;
let blocks = json
.get("blocks")
.ok_or(eyre!("get field 'blocks' from json failed"))?
.as_u64()
.ok_or(eyre!("blocks to u64 failed"))?;
let headers = json
.get("headers")
.ok_or(eyre!("get field 'headers' from json failed"))?
.as_u64()
.ok_or(eyre!("blocks to u64 failed"))?;
Ok(BlockchainInfo { headers, blocks })
},
1,
usize::MAX,
)
.unwrap()
}
fn datadir_arg(&self) -> String {
if self.config.datadir.is_none() {
unreachable!();
}
format!("-datadir={}", self.config.datadir.as_ref().unwrap())
}
}
-152
View File
@@ -1,152 +0,0 @@
use std::{
collections::HashMap,
convert::From,
fs::{self, DirEntry, File},
io::{self, BufReader, Seek, SeekFrom},
path::{Path, PathBuf},
};
use bitcoin::{io::Cursor, Block, Transaction};
use derive_deref::{Deref, DerefMut};
use super::{
errors::{OpError, OpErrorKind, OpResult},
reader::BlockchainRead,
};
///
/// An index of all blk files found.
///
#[derive(Debug, Clone, Deref, DerefMut)]
pub struct BlkFiles(HashMap<i32, PathBuf>);
impl BlkFiles {
///
/// Construct an index of all blk files.
///
pub fn new(path: &Path) -> OpResult<Self> {
Ok(Self(Self::scan_path(path)?))
}
///
/// Read a Block from blk file.
///
#[inline]
pub fn read_raw_block(&self, n_file: i32, offset: u32) -> OpResult<Vec<u8>> {
if let Some(blk_path) = self.get(&n_file) {
let mut r = BufReader::new(File::open(blk_path)?);
r.seek(SeekFrom::Start(offset as u64 - 4))?;
let block_size = r.read_u32()?;
let block = r.read_u8_vec(block_size)?;
Ok(block)
} else {
Err(OpError::from("blk file not found, sync with bitcoin core"))
}
}
///
/// Read a Block from blk file.
///
pub fn read_block(&self, n_file: i32, offset: u32) -> OpResult<Block> {
Cursor::new(self.read_raw_block(n_file, offset)?).read_block()
}
///
/// Read a transaction from blk file.
///
pub fn read_transaction(
&self,
n_file: i32,
n_pos: u32,
n_tx_offset: u32,
) -> OpResult<Transaction> {
if let Some(blk_path) = self.get(&n_file) {
let mut r = BufReader::new(File::open(blk_path)?);
// the size of a header is 80.
r.seek(SeekFrom::Start(n_pos as u64 + n_tx_offset as u64 + 80))?;
r.read_transaction()
} else {
Err(OpError::from("blk file not found, sync with bitcoin core"))
}
}
///
/// Scan blk folder to build an index of all blk files.
///
fn scan_path(path: &Path) -> OpResult<HashMap<i32, PathBuf>> {
let mut collected = HashMap::with_capacity(4000);
for entry in fs::read_dir(path)? {
match entry {
Ok(de) => {
let path = Self::resolve_path(&de)?;
if !path.is_file() {
continue;
};
if let Some(file_name) = path.as_path().file_name() {
if let Some(file_name) = file_name.to_str() {
if let Some(index) = Self::parse_blk_index(file_name) {
collected.insert(index, path);
}
}
}
}
Err(msg) => {
return Err(OpError::from(msg));
}
}
}
collected.shrink_to_fit();
if collected.is_empty() {
Err(OpError::new(OpErrorKind::RuntimeError).join_msg("No blk files found!"))
} else {
Ok(collected)
}
}
///
/// Resolve symlink.
///
fn resolve_path(entry: &DirEntry) -> io::Result<PathBuf> {
if entry.file_type()?.is_symlink() {
fs::read_link(entry.path())
} else {
Ok(entry.path())
}
}
///
/// Extract index from block file name.
///
fn parse_blk_index(file_name: &str) -> Option<i32> {
let prefix = "blk";
let ext = ".dat";
if file_name.starts_with(prefix) && file_name.ends_with(ext) {
file_name[prefix.len()..(file_name.len() - ext.len())]
.parse::<i32>()
.ok()
} else {
None
}
}
}
// #[cfg(test)]
// mod tests {
// use super::*;
// #[test]
// fn test_parse_blk_index() {
// assert_eq!(0, BlkFiles::parse_blk_index("blk00000.dat").unwrap());
// assert_eq!(6, BlkFiles::parse_blk_index("blk6.dat").unwrap());
// assert_eq!(1202, BlkFiles::parse_blk_index("blk1202.dat").unwrap());
// assert_eq!(
// 13412451,
// BlkFiles::parse_blk_index("blk13412451.dat").unwrap()
// );
// assert!(BlkFiles::parse_blk_index("blkindex.dat").is_none());
// assert!(BlkFiles::parse_blk_index("invalid.dat").is_none());
// }
// }
-45
View File
@@ -1,45 +0,0 @@
//!
//! View development note of iter_connected.rs for implementation
//! details of iter_block.rs, which follows similar principles.
//!
use bitcoin::Block;
use par_iter_sync::{IntoParallelIteratorSync, ParIterSync};
use super::BitcoinDB;
pub struct BlockIter(ParIterSync<Block>);
impl BlockIter {
/// the worker threads are dispatched in this `new` constructor!
pub fn new<T>(db: &BitcoinDB, heights: T) -> Self
where
T: IntoIterator<Item = usize> + Send + 'static,
<T as IntoIterator>::IntoIter: Send + 'static,
{
let db_ref = db.clone();
BlockIter(
heights.into_par_iter_sync(move |h| match db_ref.get_block(h) {
Ok(blk) => Ok(blk),
Err(_) => Err(()),
}),
)
}
/// the worker threads are dispatched in this `new` constructor!
pub fn from_range(db: &BitcoinDB, start: usize, end: usize) -> Self {
if end <= start {
BlockIter::new(db, Vec::new())
} else {
BlockIter::new(db, start..end)
}
}
}
impl Iterator for BlockIter {
type Item = Block;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
-211
View File
@@ -1,211 +0,0 @@
use std::{collections::BTreeMap, fmt, path::Path};
use bitcoin::{block::Header, io::Cursor, BlockHash};
use derive_deref::{Deref, DerefMut};
use leveldb::{
database::{iterator::LevelDBIterator, Database},
iterator::Iterable,
options::{Options, ReadOptions},
};
use crate::utils::log;
use super::{BlockchainRead, OpResult};
///
/// See Bitcoin Core repository for definition.
///
const BLOCK_VALID_HEADER: u32 = 1;
const BLOCK_VALID_TREE: u32 = 2;
const BLOCK_VALID_TRANSACTIONS: u32 = 3;
const BLOCK_VALID_CHAIN: u32 = 4;
const BLOCK_VALID_SCRIPTS: u32 = 5;
const BLOCK_VALID_MASK: u32 = BLOCK_VALID_HEADER
| BLOCK_VALID_TREE
| BLOCK_VALID_TRANSACTIONS
| BLOCK_VALID_CHAIN
| BLOCK_VALID_SCRIPTS;
const BLOCK_HAVE_DATA: u32 = 8;
const BLOCK_HAVE_UNDO: u32 = 16;
///
/// - Map from block height to block hash (records)
/// - Map from block hash to block height (hash_to_height)
///
#[derive(Clone, Deref, DerefMut)]
pub struct BlocksIndexes(Box<[BlockIndexRecord]>);
///
/// BLOCK_INDEX RECORD as defined in Bitcoin Core.
///
#[derive(Clone)]
pub struct BlockIndexRecord {
pub n_version: i32,
pub n_height: i32,
pub n_status: u32,
pub n_tx: u32,
pub n_file: i32,
pub n_data_pos: u32,
pub n_undo_pos: u32,
pub header: Header,
}
impl BlocksIndexes {
///
/// Build a collections of block index.
///
pub(crate) fn new(p: &Path) -> OpResult<Self> {
Ok(Self(load_block_index(p)?.into_boxed_slice()))
}
}
///
/// Load all block index in memory from leveldb (i.e. `blocks/index` path).
///
/// Map from block height to block index record.
///
pub fn load_block_index(path: &Path) -> OpResult<Vec<BlockIndexRecord>> {
let mut block_index_by_block_hash = BTreeMap::new();
log("Start loading block_index");
let mut options = Options::new();
options.create_if_missing = false;
let db: Database<BlockKey> = Database::open(path, options)?;
let options = ReadOptions::new();
let mut iter = db.iter(options);
let mut max_height_block_hash = Option::<(BlockHash, i32)>::None;
while iter.advance() {
let k = iter.key();
let v = iter.value();
if is_block_index_record(&k.key) {
let record = BlockIndexRecord::from(&v)?;
// only add valid block index record that has block data.
if record.n_height == 0
|| (record.n_status & BLOCK_VALID_MASK >= BLOCK_VALID_SCRIPTS
&& record.n_status & BLOCK_HAVE_DATA > 0)
{
let block_hash = record.header.block_hash();
// find the block with max height
if let Some((hash, height)) = max_height_block_hash.as_mut() {
if record.n_height > *height {
*hash = block_hash;
*height = record.n_height;
}
} else {
max_height_block_hash = Some((block_hash, record.n_height));
}
block_index_by_block_hash.insert(block_hash, record);
}
}
}
// build the longest chain
if let Some((hash, height)) = max_height_block_hash {
let mut block_index = Vec::with_capacity(height as usize + 1);
let mut current_hash = hash;
let mut current_height = height;
// recursively build block index from max height block.
while current_height >= 0 {
let blk = block_index_by_block_hash
.remove(&current_hash)
.expect("block hash not found in block index!");
assert_eq!(
current_height, blk.n_height,
"some block info missing from block index levelDB,\
delete Bitcoin folder and re-download!"
);
current_hash = blk.header.prev_blockhash;
current_height -= 1;
block_index.push(blk);
}
block_index.reverse();
Ok(block_index)
} else {
Ok(Vec::with_capacity(0))
}
}
/// levelDB key util
struct BlockKey {
key: Vec<u8>,
}
/// levelDB key util
impl db_key::Key for BlockKey {
fn from_u8(key: &[u8]) -> Self {
BlockKey {
key: Vec::from(key),
}
}
fn as_slice<T, F: Fn(&[u8]) -> T>(&self, f: F) -> T {
f(&self.key)
}
}
impl BlockIndexRecord {
///
/// Decode levelDB value for Block Index Record.
///
fn from(values: &[u8]) -> OpResult<Self> {
let mut reader = Cursor::new(values);
let n_version = reader.read_varint()? as i32;
let n_height = reader.read_varint()? as i32;
let n_status = reader.read_varint()? as u32;
let n_tx = reader.read_varint()? as u32;
let n_file = if n_status & (BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO) > 0 {
reader.read_varint()? as i32
} else {
-1
};
let n_data_pos = if n_status & BLOCK_HAVE_DATA > 0 {
reader.read_varint()? as u32
} else {
u32::MAX
};
let n_undo_pos = if n_status & BLOCK_HAVE_UNDO > 0 {
reader.read_varint()? as u32
} else {
u32::MAX
};
let header = reader.read_block_header()?;
Ok(BlockIndexRecord {
n_version,
n_height,
n_status,
n_tx,
n_file,
n_data_pos,
n_undo_pos,
header,
})
}
}
#[inline]
fn is_block_index_record(data: &[u8]) -> bool {
data.first() == Some(&b'b')
}
impl fmt::Debug for BlockIndexRecord {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BlockIndexRecord")
.field("version", &self.n_version)
.field("height", &self.n_height)
.field("status", &self.n_status)
.field("n_tx", &self.n_tx)
.field("n_file", &self.n_file)
.field("n_data_pos", &self.n_data_pos)
.field("header", &self.header)
.finish()
}
}
-135
View File
@@ -1,135 +0,0 @@
use std::convert::{self, From};
use std::error;
use std::fmt;
use std::io;
use std::string;
use std::sync;
pub type OpResult<T> = Result<T, OpError>;
#[derive(Debug)]
/// Custom error type
pub struct OpError {
pub kind: OpErrorKind,
pub message: String,
}
impl OpError {
pub fn new(kind: OpErrorKind) -> Self {
OpError {
kind,
message: String::new(),
}
}
/// Joins the Error with a new message and returns it
pub fn join_msg(mut self, msg: &str) -> Self {
self.message.push_str(msg);
OpError {
kind: self.kind,
message: self.message,
}
}
}
impl fmt::Display for OpError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.message.is_empty() {
write!(f, "{}", &self.kind)
} else {
write!(f, "{} {}", &self.message, &self.kind)
}
}
}
impl error::Error for OpError {
fn description(&self) -> &str {
self.message.as_ref()
}
fn cause(&self) -> Option<&dyn error::Error> {
self.kind.source()
}
}
#[derive(Debug)]
pub enum OpErrorKind {
None,
IoError(io::Error),
Utf8Error(string::FromUtf8Error),
RuntimeError,
PoisonError,
SendError,
}
impl fmt::Display for OpErrorKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
OpErrorKind::IoError(ref err) => write!(f, "I/O Error: {}", err),
OpErrorKind::Utf8Error(ref err) => write!(f, "Utf8 Conversion: {}", err),
ref err @ OpErrorKind::PoisonError => write!(f, "Threading Error: {}", err),
ref err @ OpErrorKind::SendError => write!(f, "Sync: {}", err),
ref err @ OpErrorKind::RuntimeError => write!(f, "RuntimeError: {}", err),
OpErrorKind::None => write!(f, ""),
}
}
}
impl error::Error for OpErrorKind {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match *self {
OpErrorKind::IoError(ref err) => Some(err),
OpErrorKind::Utf8Error(ref err) => Some(err),
ref err @ OpErrorKind::PoisonError => Some(err),
ref err @ OpErrorKind::SendError => Some(err),
_ => None,
}
}
}
impl From<io::Error> for OpError {
fn from(err: io::Error) -> Self {
Self::new(OpErrorKind::IoError(err))
}
}
impl From<bitcoin::consensus::encode::Error> for OpError {
fn from(_: bitcoin::consensus::encode::Error) -> Self {
Self::from("block decode error")
}
}
impl convert::From<i32> for OpError {
fn from(err_code: i32) -> Self {
Self::from(io::Error::from_raw_os_error(err_code))
}
}
impl convert::From<&str> for OpError {
fn from(err: &str) -> Self {
Self::new(OpErrorKind::None).join_msg(err)
}
}
impl<T> convert::From<sync::PoisonError<T>> for OpError {
fn from(_: sync::PoisonError<T>) -> Self {
Self::new(OpErrorKind::PoisonError)
}
}
impl<T> convert::From<sync::mpsc::SendError<T>> for OpError {
fn from(_: sync::mpsc::SendError<T>) -> Self {
Self::new(OpErrorKind::SendError)
}
}
impl convert::From<string::FromUtf8Error> for OpError {
fn from(err: string::FromUtf8Error) -> Self {
Self::new(OpErrorKind::Utf8Error(err))
}
}
impl convert::From<leveldb::error::Error> for OpError {
fn from(err: leveldb::error::Error) -> Self {
Self::from(err.to_string().as_ref())
}
}
-172
View File
@@ -1,172 +0,0 @@
//!
//! Mostly a stripped down copy pasta of bitcoin-explorer
//!
//! Huge props to https://github.com/Congyuwang
//!
//! Crates APIs, essential structs, functions, methods are all here!
//!
//! To quickly understand how to use this crate, have a look at the
//! documentation for `bitcoin_explorer::BitcoinDB`!!.
//!
mod blk_files;
mod block_iter;
mod blocks_indexes;
mod errors;
mod reader;
mod txdb;
use blk_files::*;
use blocks_indexes::*;
use errors::*;
use reader::*;
use txdb::*;
use std::ops::Deref;
use std::path::Path;
use std::sync::Arc;
use bitcoin::{Block, Transaction, Txid};
pub use block_iter::BlockIter;
pub struct InnerDB {
pub blocks_indexes: BlocksIndexes,
pub blk_files: BlkFiles,
pub tx_db: TxDB,
}
///
/// This is the main struct of this crate!! Click and read the doc.
///
/// All queries start from initializing `BitcoinDB`.
///
/// Note: This is an Arc wrap around `InnerDB`.
///
#[derive(Clone)]
pub struct BitcoinDB(Arc<InnerDB>);
impl Deref for BitcoinDB {
type Target = InnerDB;
fn deref(&self) -> &Self::Target {
self.0.deref()
}
}
impl BitcoinDB {
///
/// This is the main structure for reading Bitcoin blockchain data.
///
/// Instantiating this class by passing the `-datadir` directory of
/// Bitcoin core to the `new()` method.
/// `tx_index`: whether to try to open tx_index levelDB.
///
pub fn new(p: &Path, tx_index: bool) -> OpResult<BitcoinDB> {
if !p.exists() {
return Err(OpError::from("data_dir does not exist"));
}
let blk_path = p.join("blocks");
let index_path = blk_path.join("index");
let blocks_indexes = BlocksIndexes::new(index_path.as_path())?;
let tx_db = if tx_index {
let tx_index_path = p.join("indexes").join("txindex");
TxDB::new(&tx_index_path)
} else {
TxDB::null()
};
let inner = InnerDB {
blocks_indexes,
blk_files: BlkFiles::new(blk_path.as_path())?,
tx_db,
};
Ok(BitcoinDB(Arc::new(inner)))
}
///
/// Get the maximum number of blocks downloaded.
///
/// This API guarantee that block 0 to `get_block_count() - 1`
/// have been downloaded and available for query.
///
pub fn get_block_count(&self) -> usize {
let records = self.blocks_indexes.len();
for h in 0..records {
// n_tx == 0 indicates that the block is not downloaded
if self.blocks_indexes.get(h).unwrap().n_tx == 0 {
return h;
}
}
records
}
///
/// Get a block
///
pub fn get_block(&self, height: usize) -> OpResult<Block> {
if let Some(index) = self.blocks_indexes.get(height) {
Ok(self.blk_files.read_block(index.n_file, index.n_data_pos)?)
} else {
Err(OpError::from("height not found"))
}
}
///
/// Get a transaction by providing txid.
///
/// This function requires `txindex` to be set to `true` for `BitcoinDB`,
/// and requires that flag `txindex=1` has been enabled when
/// running Bitcoin Core.
///
/// A transaction cannot be found using this function if it is
/// not yet indexed using `txindex`.
///
pub fn get_transaction(&self, txid: &Txid) -> OpResult<Transaction> {
if !self.tx_db.is_open() {
return Err(OpError::from("TxDB not open"));
}
// give special treatment for genesis transaction
if self.tx_db.is_genesis_tx(txid) {
return Ok(self.get_block(0)?.txdata.swap_remove(0));
}
let record = self.tx_db.get_tx_record(txid)?;
self.blk_files
.read_transaction(record.n_file, record.n_pos, record.n_tx_offset)
}
///
/// Iterate through all blocks from `start` to `end` (excluded).
///
/// # Performance
///
/// This iterator is implemented to read the blocks in concurrency,
/// but the result is still produced in sequential order.
/// Results read are stored in a synced queue for `next()`
/// to get.
///
/// The iterator stops automatically when a block cannot be
/// read (i.e., when the max height in the database met).
///
/// This is a very efficient implementation.
/// Using SSD and intel core i7 (4 core, 8 threads)
/// Iterating from height 0 to 700000 takes about 10 minutes.
///
pub fn iter_block(&self, start: usize, end: usize) -> BlockIter {
BlockIter::from_range(self, start, end)
}
pub fn check_if_txout_value_is_zero(&self, txid: &Txid, vout: usize) -> bool {
self.get_transaction(txid)
.unwrap()
.output
.get(vout)
.unwrap()
.to_owned()
.value
.to_sat()
== 0
}
}
-90
View File
@@ -1,90 +0,0 @@
use std::{fs::File, io::BufReader};
use bitcoin::{block::Header, consensus::Decodable, io::Cursor, Block, Transaction};
use byteorder::{LittleEndian, ReadBytesExt};
use super::OpResult;
///
/// binary file read utilities.
///
pub trait BlockchainRead {
#[inline]
fn read_varint(&mut self) -> OpResult<usize>
where
Self: bitcoin::io::Read,
{
let mut n = 0;
loop {
let ch_data = self.read_u8()?;
n = (n << 7) | (ch_data & 0x7F) as usize;
if ch_data & 0x80 > 0 {
n += 1;
} else {
break;
}
}
Ok(n)
}
#[inline]
fn read_u8(&mut self) -> OpResult<u8>
where
Self: bitcoin::io::Read,
{
let mut slice = [0u8; 1];
self.read_exact(&mut slice).unwrap();
Ok(slice[0])
}
#[inline]
fn read_u32(&mut self) -> OpResult<u32>
where
Self: std::io::Read,
{
let u = ReadBytesExt::read_u32::<LittleEndian>(self)?;
Ok(u)
}
#[inline]
fn read_u8_vec(&mut self, count: u32) -> OpResult<Vec<u8>>
where
Self: bitcoin::io::Read,
{
let mut arr = vec![0u8; count as usize];
self.read_exact(&mut arr).unwrap();
Ok(arr)
}
#[inline]
fn read_block(&mut self) -> OpResult<Block>
where
Self: bitcoin::io::BufRead,
{
Ok(Block::consensus_decode(self)?)
}
#[inline]
fn read_transaction(&mut self) -> OpResult<Transaction>
where
Self: bitcoin::io::BufRead,
{
Ok(Transaction::consensus_decode(self)?)
}
#[inline]
fn read_block_header(&mut self) -> OpResult<Header>
where
Self: bitcoin::io::BufRead,
{
Ok(Header::consensus_decode(self)?)
}
}
impl<T> BlockchainRead for Cursor<T> {}
impl BlockchainRead for BufReader<File> {}
-147
View File
@@ -1,147 +0,0 @@
use std::{path::Path, str::FromStr};
use bitcoin::{hashes::Hash, io::Cursor, Txid};
use leveldb::{
database::Database,
kv::KV,
options::{Options, ReadOptions},
};
use crate::utils::log;
use super::{BlockchainRead, OpError, OpResult};
const GENESIS_TXID: &str = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b";
///
/// tx-index: looking up transaction position using txid.
///
/// This is possible if Bitcoin Core has `txindex=1`.
///
pub struct TxDB {
db: Option<Database<TxKey>>,
genesis_txid: Txid,
}
/// Records transaction storage on disk
pub struct TransactionRecord {
pub txid: Txid,
pub n_file: i32,
pub n_pos: u32,
pub n_tx_offset: u32,
}
impl TransactionRecord {
fn from(key: &[u8], values: &[u8]) -> OpResult<Self> {
let mut reader = Cursor::new(values);
Ok(TransactionRecord {
txid: Txid::from_slice(key).unwrap(),
n_file: reader.read_varint()? as i32,
n_pos: reader.read_varint()? as u32,
n_tx_offset: reader.read_varint()? as u32,
})
}
}
impl TxDB {
/// initialize TxDB for transaction queries
pub fn new(path: &Path) -> TxDB {
let option_db = TxDB::try_open_db(path);
if let Some(db) = option_db {
TxDB {
db: Some(db),
genesis_txid: Txid::from_str(GENESIS_TXID).unwrap(),
}
} else {
TxDB::null()
}
}
#[inline]
pub fn is_open(&self) -> bool {
self.db.is_some()
}
#[inline]
pub fn null() -> TxDB {
TxDB {
db: None,
genesis_txid: Txid::from_str(GENESIS_TXID).unwrap(),
}
}
#[inline]
///
/// genesis tx is not included in UTXO because of Bitcoin Core Bug
///
pub fn is_genesis_tx(&self, txid: &Txid) -> bool {
txid == &self.genesis_txid
}
fn try_open_db(path: &Path) -> Option<Database<TxKey>> {
if !path.exists() {
log("Failed to open tx_index DB: tx_index not built");
return None;
}
let options = Options::new();
match Database::open(path, options) {
Ok(db) => {
log("Successfully opened tx_index DB!");
Some(db)
}
Err(e) => {
log(&format!("Failed to open tx_index DB: {:?}", e));
None
}
}
}
/// note that this function cannot find genesis block, which needs special treatment
pub fn get_tx_record(&self, txid: &Txid) -> OpResult<TransactionRecord> {
if let Some(db) = &self.db {
let inner = txid.as_byte_array();
let mut key = Vec::with_capacity(inner.len() + 1);
key.push(b't');
key.extend(inner);
let key = TxKey { key };
let read_options = ReadOptions::new();
match db.get(read_options, &key) {
Ok(value) => {
if let Some(value) = value {
Ok(TransactionRecord::from(&key.key[1..], value.as_slice())?)
} else {
Err(OpError::from(
format!("value not found for txid: {}", txid).as_str(),
))
}
}
Err(e) => Err(OpError::from(
format!("value not found for txid: {}", e).as_str(),
)),
}
} else {
Err(OpError::from("TxDB not open"))
}
}
}
/// levelDB key utility
struct TxKey {
key: Vec<u8>,
}
/// levelDB key utility
impl db_key::Key for TxKey {
fn from_u8(key: &[u8]) -> Self {
TxKey {
key: Vec::from(key),
}
}
fn as_slice<T, F: Fn(&[u8]) -> T>(&self, f: F) -> T {
f(&self.key)
}
}
-9
View File
@@ -1,9 +0,0 @@
mod addresses;
mod consts;
mod daemon;
mod db;
pub use addresses::*;
pub use consts::*;
pub use daemon::*;
pub use db::*;
+1 -1
View File
@@ -5,7 +5,7 @@ use std::{
};
use allocative::Allocative;
use bitcoin::Txid;
use biter::bitcoin::Txid;
use rayon::prelude::*;
use crate::structs::{Date, Height, TxData};
+4 -2
View File
@@ -3,13 +3,15 @@ use itertools::Itertools;
use ordered_float::OrderedFloat;
use crate::{
bitcoin::TARGET_BLOCKS_PER_DAY,
datasets::AnyDataset,
structs::{
date_map_vec_to_any_date_map_vec, date_map_vec_to_mut_any_date_map_vec, Amount, AnyBiMap,
AnyDateMap, AnyHeightMap, BiMap, DateMap, Height, HeightMap, MapKey,
},
utils::{BYTES_IN_MB, ONE_DAY_IN_DAYS, ONE_MONTH_IN_DAYS, ONE_WEEK_IN_DAYS, ONE_YEAR_IN_DAYS},
utils::{
BYTES_IN_MB, ONE_DAY_IN_DAYS, ONE_MONTH_IN_DAYS, ONE_WEEK_IN_DAYS, ONE_YEAR_IN_DAYS,
TARGET_BLOCKS_PER_DAY,
},
};
use super::{
+1 -3
View File
@@ -1,5 +1,4 @@
mod actions;
mod bitcoin;
mod databases;
mod datasets;
mod io;
@@ -10,12 +9,11 @@ mod utils;
pub use crate::{
actions::iter_blocks,
bitcoin::{BitcoinDB, BitcoinDaemon},
datasets::OHLC,
io::{Binary, Json, Serialization},
structs::{
Config, Date, DateMap, Height, HeightMap, MapChunkId, SerializedBTreeMap, SerializedVec,
HEIGHT_MAP_CHUNK_SIZE,
},
utils::log,
utils::{create_rpc, log},
};
+13 -34
View File
@@ -1,51 +1,30 @@
use std::{path::Path, thread::sleep, time::Duration};
use std::{thread::sleep, time::Duration};
use parser::{iter_blocks, log, BitcoinDB, BitcoinDaemon, Config, Height};
use biter::bitcoincore_rpc::RpcApi;
use parser::{create_rpc, iter_blocks, log, Config};
fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
let config = Config::read();
let config = Config::import();
if config.datadir.is_none() {
println!(
"You need to set the --datadir parameter at least once to run the parser.\nRun the program with '-h' for help."
);
std::process::exit(1);
}
config.write()?;
let daemon = BitcoinDaemon::new(&config);
let rpc = create_rpc(&config).unwrap();
loop {
daemon.stop();
let block_count = rpc.get_blockchain_info().unwrap().blocks as usize;
// Scoped to free bitcoin's lock
let block_count = {
let bitcoin_db = BitcoinDB::new(Path::new(&config.datadir.as_ref().unwrap()), true)?;
log(&format!("{block_count} blocks found."));
// let block_count = 200_000;
let block_count = bitcoin_db.get_block_count();
log(&format!("{block_count} blocks found."));
iter_blocks(&bitcoin_db, block_count)?;
block_count
};
daemon.start();
if daemon.check_if_fully_synced() {
daemon.wait_for_new_block(Height::new(block_count as u32 - 1));
} else {
daemon.wait_sync();
}
iter_blocks(&config, &rpc, block_count)?;
if let Some(delay) = config.delay {
sleep(Duration::from_secs(delay))
}
log("Waiting for new block...");
while block_count == rpc.get_blockchain_info().unwrap().blocks as usize {
sleep(Duration::from_secs(5))
}
}
// Ok(())
+2 -2
View File
@@ -1,10 +1,10 @@
use bitcoin::TxOut;
use bitcoin_hashes::{hash160, Hash};
use biter::bitcoin::TxOut;
use itertools::Itertools;
use crate::{
bitcoin::multisig_addresses,
databases::{U8x19, U8x31, SANAKIRJA_MAX_KEY_SIZE},
utils::multisig_addresses,
};
use super::{AddressType, Counter};
+1 -1
View File
@@ -10,7 +10,7 @@ use bincode::{
error::{DecodeError, EncodeError},
BorrowDecode, Decode, Encode,
};
use bitcoin::Amount as BitcoinAmount;
use biter::bitcoin::Amount as BitcoinAmount;
use derive_deref::{Deref, DerefMut};
use sanakirja::{direct_repr, Storable, UnsizedStorable};
use serde::{Deserialize, Serialize};
+1 -1
View File
@@ -5,7 +5,7 @@ use std::{
use allocative::Allocative;
use crate::{bitcoin::TARGET_BLOCKS_PER_DAY, utils::LossyFrom};
use crate::utils::{LossyFrom, TARGET_BLOCKS_PER_DAY};
use super::{AnyDateMap, AnyHeightMap, AnyMap, Date, DateMap, Height, HeightMap, MapValue};
+53 -15
View File
@@ -6,17 +6,21 @@ use serde::{Deserialize, Serialize};
#[derive(Parser, Debug, Clone, Default, Serialize, Deserialize)]
#[command(version, about, long_about = None)]
pub struct Config {
/// bitcoind `-datadir=<dir>` argument
/// Bitcoin data directory path
#[arg(long, value_name = "DIR")]
pub datadir: Option<String>,
/// bitcoind `-blocksonly` argument, default: true
#[arg(long)]
pub blocksonly: Option<bool>,
/// Bitcoin RPC port, default: 8332
#[arg(long, value_name = "PORT")]
pub rpcport: Option<u16>,
/// bitcoind `-rpcconnect=<ip>` argument
#[arg(long, value_name = "IP")]
pub rpcconnect: Option<String>,
/// Bitcoin RPC username
#[arg(long, value_name = "USERNAME")]
pub rpcuser: Option<String>,
/// Bitcoin RPC password
#[arg(long, value_name = "PASSWORD")]
pub rpcpassword: Option<String>,
/// Delay between runs, default: 0
#[arg(long, value_name = "SECONDS")]
@@ -26,7 +30,7 @@ pub struct Config {
impl Config {
const PATH: &'static str = "config.toml";
pub fn read() -> Self {
pub fn import() -> Self {
let mut config_saved = fs::read_to_string(Self::PATH)
.map_or(Config::default(), |contents| {
toml::from_str(&contents).unwrap_or_default()
@@ -38,24 +42,58 @@ impl Config {
config_saved.datadir = Some(datadir);
}
if let Some(blocksonly) = config_args.blocksonly {
config_saved.blocksonly = Some(blocksonly);
if let Some(rpcport) = config_args.rpcport {
config_saved.rpcport = Some(rpcport);
} else {
config_saved.blocksonly = Some(true);
config_saved.rpcport = Some(8332);
}
if let Some(rpcconnect) = config_args.rpcconnect {
config_saved.rpcconnect = Some(rpcconnect);
if let Some(rpcuser) = config_args.rpcuser {
config_saved.rpcuser = Some(rpcuser);
}
if let Some(rpcpassword) = config_args.rpcpassword {
config_saved.rpcpassword = Some(rpcpassword);
}
if let Some(delay) = config_args.delay {
config_saved.delay = Some(delay);
}
config_saved
// Done importing
let config = config_saved;
config.check();
config.write().unwrap();
config
}
pub fn write(&self) -> std::io::Result<()> {
fn check(&self) {
if self.datadir.is_none() {
Self::exit("datadir");
}
if self.rpcuser.is_none() {
Self::exit("rpcuser");
}
if self.rpcpassword.is_none() {
Self::exit("rpcpassword");
}
}
fn exit(attribute: &str) {
println!(
"You need to set the --{} parameter at least once to run the parser.\nRun the program with '-h' for help.", attribute
);
std::process::exit(1);
}
fn write(&self) -> std::io::Result<()> {
fs::write(Self::PATH, toml::to_string(self).unwrap())
}
}
+2 -1
View File
@@ -5,10 +5,11 @@ use std::{
use allocative::Allocative;
use bincode::{Decode, Encode};
use biter::NUMBER_OF_UNSAFE_BLOCKS;
use derive_deref::{Deref, DerefMut};
use serde::{Deserialize, Serialize};
use crate::{bitcoin::NUMBER_OF_UNSAFE_BLOCKS, HEIGHT_MAP_CHUNK_SIZE};
use crate::HEIGHT_MAP_CHUNK_SIZE;
use super::{HeightMapChunkId, MapKey};
-219
View File
@@ -1,219 +0,0 @@
use std::{
iter::Sum,
ops::{Add, AddAssign, Div, Mul, Sub, SubAssign},
};
use itertools::Itertools;
use ordered_float::{FloatCore, OrderedFloat};
use super::ToF32;
pub trait ArrayOperations<T> {
fn transform<F>(&self, transform: F) -> Vec<T>
where
T: Copy + Default,
F: Fn((usize, &T, &[T])) -> T;
fn add(&self, other: &[T]) -> Vec<T>
where
T: Add<Output = T> + Copy + Default;
fn subtract(&self, other: &[T]) -> Vec<T>
where
T: Sub<Output = T> + Copy + Default;
fn multiply(&self, other: &[T]) -> Vec<T>
where
T: Mul<Output = T> + Copy + Default;
fn divide(&self, other: &[T]) -> Vec<T>
where
T: Div<Output = T> + Copy + Default;
fn match_size<'a>(&'a self, other: &'a [T]) -> &'a [T];
fn cumulate(&self) -> Vec<T>
where
T: Sum + Copy + Default + AddAssign;
fn last_x_sum(&self, x: usize) -> Vec<T>
where
T: Sum + Copy + Default + AddAssign + SubAssign;
fn moving_average(&self, x: usize) -> Vec<f32>
where
T: Sum + Copy + Default + AddAssign + SubAssign + ToF32;
fn net_change(&self, offset: usize) -> Vec<T>
where
T: Copy + Default + Sub<Output = T>;
fn median(&self, size: usize) -> Vec<Option<T>>
where
T: FloatCore;
}
impl<T> ArrayOperations<T> for &[T] {
fn transform<F>(&self, transform: F) -> Vec<T>
where
T: Copy + Default,
F: Fn((usize, &T, &[T])) -> T,
{
self.iter()
.enumerate()
.map(|(index, value)| transform((index, value, self)))
.collect_vec()
}
fn add(&self, other: &[T]) -> Vec<T>
where
T: Add<Output = T> + Copy + Default,
{
self.match_size(other)
.transform(|(index, value, _)| *value + *other.get(index).unwrap())
}
fn subtract(&self, other: &[T]) -> Vec<T>
where
T: Sub<Output = T> + Copy + Default,
{
self.match_size(other)
.transform(|(index, value, _)| *value - *other.get(index).unwrap())
}
fn multiply(&self, other: &[T]) -> Vec<T>
where
T: Mul<Output = T> + Copy + Default,
{
self.match_size(other)
.transform(|(index, value, _)| *value * *other.get(index).unwrap())
}
fn divide(&self, other: &[T]) -> Vec<T>
where
T: Div<Output = T> + Copy + Default,
{
self.match_size(other)
.transform(|(index, value, _)| *value / *other.get(index).unwrap())
}
fn match_size(&self, other: &[T]) -> &[T] {
let len = other.len();
if self.len() > len {
&self[..len]
} else {
self
}
}
fn cumulate(&self) -> Vec<T>
where
T: Sum + Copy + Default + AddAssign,
{
let mut sum = T::default();
self.iter()
.map(|value| {
sum += *value;
sum
})
.collect_vec()
}
fn last_x_sum(&self, x: usize) -> Vec<T>
where
T: Sum + Copy + Default + AddAssign + SubAssign,
{
let mut sum = T::default();
self.iter()
.enumerate()
.map(|(index, value)| {
sum += *value;
if index >= x - 1 {
let previous_index = index + 1 - x;
sum -= *self.get(previous_index).unwrap()
}
sum
})
.collect_vec()
}
fn moving_average(&self, x: usize) -> Vec<f32>
where
T: Sum + Copy + Default + AddAssign + SubAssign + ToF32,
{
let mut sum = T::default();
self.iter()
.enumerate()
.map(|(index, value)| {
sum += *value;
if index >= x - 1 {
sum -= *self.get(index + 1 - x).unwrap()
}
sum.to_f32() / x as f32
})
.collect_vec()
}
fn net_change(&self, offset: usize) -> Vec<T>
where
T: Copy + Default + Sub<Output = T>,
{
self.transform(|(index, value, arr)| {
let previous = {
if let Some(previous_index) = index.checked_sub(offset) {
*arr.get(previous_index).unwrap()
} else {
T::default()
}
};
*value - previous
})
}
fn median(&self, size: usize) -> Vec<Option<T>>
where
T: FloatCore,
{
let even = size % 2 == 0;
let median_index = size / 2;
if size < 3 {
panic!("Computing a median for a size lower than 3 is useless");
}
self.iter()
.enumerate()
.map(|(index, _)| {
if index >= size - 1 {
let mut arr = self[index - (size - 1)..index + 1]
.iter()
.map(|value| OrderedFloat(*value))
.collect_vec();
arr.sort_unstable();
if even {
Some(
(**arr.get(median_index).unwrap()
+ **arr.get(median_index - 1).unwrap())
/ T::from(2.0).unwrap(),
)
} else {
Some(**arr.get(median_index).unwrap())
}
} else {
None
}
})
.collect()
}
}
-1
View File
@@ -1 +0,0 @@
pub const BYTES_IN_MB: usize = 1_000_000;
@@ -1,3 +1,7 @@
pub const BYTES_IN_MB: usize = 1_000_000;
pub const TARGET_BLOCKS_PER_DAY: usize = 144;
pub const ONE_DAY_IN_DAYS: usize = 1;
pub const ONE_WEEK_IN_DAYS: usize = 7;
pub const TWO_WEEK_IN_DAYS: usize = 2 * ONE_WEEK_IN_DAYS;
+1 -11
View File
@@ -1,4 +1,4 @@
use std::{fs::OpenOptions, io::Write, process::Output};
use std::{fs::OpenOptions, io::Write};
use chrono::Local;
use color_eyre::owo_colors::OwoColorize;
@@ -23,13 +23,3 @@ pub fn log(str: &str) {
println!("{} {}", date_time.bright_black(), line);
});
}
pub fn log_output(output: &Output) {
if !output.stdout.is_empty() {
log(&String::from_utf8_lossy(&output.stdout));
}
if !output.stderr.is_empty() {
log(&String::from_utf8_lossy(&output.stderr));
}
}
+6 -4
View File
@@ -1,17 +1,19 @@
mod bytes;
mod date;
mod consts;
mod flamegraph;
mod log;
mod lossy;
mod multisig;
mod percentile;
mod retry;
mod rpc;
mod time;
pub use bytes::*;
pub use date::*;
pub use consts::*;
pub use flamegraph::*;
pub use log::*;
pub use lossy::*;
pub use multisig::*;
pub use percentile::*;
pub use retry::*;
pub use rpc::*;
pub use time::*;
@@ -2,7 +2,7 @@
// Code from bitcoin-explorer now deprecated
//
use bitcoin::{
use biter::bitcoin::{
blockdata::{
opcodes::all,
script::Instruction::{self, Op, PushBytes},
+13
View File
@@ -0,0 +1,13 @@
use biter::bitcoincore_rpc::{Auth, Client};
use crate::Config;
pub fn create_rpc(config: &Config) -> color_eyre::Result<Client> {
Ok(Client::new(
&format!("http://localhost:{}", config.rpcport.unwrap()),
Auth::UserPass(
config.rpcuser.clone().unwrap(),
config.rpcpassword.clone().unwrap(),
),
)?)
}
+158 -85
View File
@@ -27,7 +27,7 @@ dependencies = [
"getrandom",
"once_cell",
"version_check",
"zerocopy",
"zerocopy 0.7.35",
]
[[package]]
@@ -258,6 +258,12 @@ dependencies = [
"bitcoin_hashes",
]
[[package]]
name = "base64"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]]
name = "base64"
version = "0.22.0"
@@ -342,6 +348,45 @@ dependencies = [
"serde",
]
[[package]]
name = "bitcoincore-rpc"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aedd23ae0fd321affb4bbbc36126c6f49a32818dc6b979395d24da8c9d4e80ee"
dependencies = [
"bitcoincore-rpc-json",
"jsonrpc",
"log",
"serde",
"serde_json",
]
[[package]]
name = "bitcoincore-rpc-json"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8909583c5fab98508e80ef73e5592a651c954993dc6b7739963257d19f0e71a"
dependencies = [
"bitcoin",
"serde",
"serde_json",
]
[[package]]
name = "biter"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd2ea6b4c4046feaaf73f3dff1cc648328af021d50be95946e0e7a807c985b47"
dependencies = [
"bitcoin",
"bitcoincore-rpc",
"crossbeam",
"derived-deref",
"rayon",
"serde",
"serde_json",
]
[[package]]
name = "bitflags"
version = "1.3.2"
@@ -470,15 +515,6 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
[[package]]
name = "cmake"
version = "0.1.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130"
dependencies = [
"cc",
]
[[package]]
name = "color-eyre"
version = "0.6.3"
@@ -617,12 +653,6 @@ dependencies = [
"parking_lot_core 0.9.9",
]
[[package]]
name = "db-key"
version = "0.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b72465f46d518f6015d9cf07f7f3013a95dd6b9c2747c3d65ae0cce43929d14f"
[[package]]
name = "derive_deref"
version = "1.1.1"
@@ -634,6 +664,17 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "derived-deref"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "805ef2023ccd65425743a91ecd11fc020979a0b01921db3104fb606d18a7b43e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.60",
]
[[package]]
name = "either"
version = "1.12.0"
@@ -700,12 +741,6 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
[[package]]
name = "ffi-opaque"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec54ac60a7f2ee9a97cad9946f9bf629a3bc6a7ae59e68983dc9318f5a54b81a"
[[package]]
name = "flate2"
version = "1.0.28"
@@ -1132,35 +1167,24 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "jsonrpc"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3662a38d341d77efecb73caf01420cfa5aa63c0253fd7bc05289ef9f6616e1bf"
dependencies = [
"base64 0.13.1",
"minreq",
"serde",
"serde_json",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "leveldb"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32651baaaa5596b3a6e0bee625e73fd0334c167db0ea5ac68750ef9a629a2d6a"
dependencies = [
"db-key",
"leveldb-sys",
"libc",
]
[[package]]
name = "leveldb-sys"
version = "2.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dd94a4d0242a437e5e41a27c782b69a624469ca1c4d1e5cb3c337f74a8031d4"
dependencies = [
"cmake",
"ffi-opaque",
"libc",
"num_cpus",
]
[[package]]
name = "libc"
version = "0.2.153"
@@ -1236,14 +1260,26 @@ dependencies = [
]
[[package]]
name = "mio"
version = "0.8.11"
name = "minreq"
version = "2.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
checksum = "763d142cdff44aaadd9268bebddb156ef6c65a0e13486bb81673cf2d8739f9b0"
dependencies = [
"log",
"serde",
"serde_json",
]
[[package]]
name = "mio"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4"
dependencies = [
"hermit-abi",
"libc",
"wasi",
"windows-sys 0.48.0",
"windows-sys 0.52.0",
]
[[package]]
@@ -1283,16 +1319,6 @@ dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "object"
version = "0.32.2"
@@ -1367,16 +1393,6 @@ version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
[[package]]
name = "par-iter-sync"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efa981aaed94bf59211f644922155e8a33bcb01ed662cd63426653187f562790"
dependencies = [
"crossbeam",
"num_cpus",
]
[[package]]
name = "parking_lot"
version = "0.11.2"
@@ -1431,20 +1447,16 @@ version = "0.3.0"
dependencies = [
"allocative",
"bincode",
"bitcoin",
"bitcoin_hashes",
"byteorder",
"biter",
"chrono",
"clap",
"color-eyre",
"db-key",
"derive_deref",
"inferno",
"itertools",
"leveldb",
"memory-stats",
"ordered-float",
"par-iter-sync",
"rayon",
"reqwest",
"sanakirja",
@@ -1497,6 +1509,15 @@ version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
[[package]]
name = "ppv-lite86"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dee4364d9f3b902ef14fab8a1ddffb783a1cb6b4bba3bfc1fa3922732c7de97f"
dependencies = [
"zerocopy 0.6.6",
]
[[package]]
name = "proc-macro2"
version = "1.0.81"
@@ -1524,6 +1545,36 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "rayon"
version = "1.10.0"
@@ -1597,7 +1648,7 @@ version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37"
dependencies = [
"base64",
"base64 0.22.0",
"bytes",
"encoding_rs",
"futures-channel",
@@ -1697,7 +1748,7 @@ version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d"
dependencies = [
"base64",
"base64 0.22.0",
"rustls-pki-types",
]
@@ -1773,6 +1824,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e0cc0f1cf93f4969faf3ea1c7d8a9faed25918d96affa959720823dfe86d4f3"
dependencies = [
"bitcoin_hashes",
"rand",
"secp256k1-sys",
"serde",
]
@@ -1831,11 +1883,12 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.120"
version = "1.0.122"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5"
checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
]
@@ -2070,28 +2123,27 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.38.0"
version = "1.39.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a"
checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1"
dependencies = [
"backtrace",
"bytes",
"libc",
"mio",
"num_cpus",
"parking_lot 0.12.1",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys 0.48.0",
"windows-sys 0.52.0",
]
[[package]]
name = "tokio-macros"
version = "2.3.0"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
dependencies = [
"proc-macro2",
"quote",
@@ -2610,13 +2662,34 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "zerocopy"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "854e949ac82d619ee9a14c66a1b674ac730422372ccb759ce0c39cabcf2bf8e6"
dependencies = [
"byteorder",
"zerocopy-derive 0.6.6",
]
[[package]]
name = "zerocopy"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
"zerocopy-derive",
"zerocopy-derive 0.7.35",
]
[[package]]
name = "zerocopy-derive"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "125139de3f6b9d625c39e2efdd73d41bdac468ccd556556440e322be0e1bbd91"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.60",
]
[[package]]