bomputer: init

This commit is contained in:
nym21
2025-01-31 11:43:14 +01:00
parent 8c610f8a83
commit ad34d9d402
67 changed files with 498 additions and 249 deletions
+4 -1
View File
@@ -8,7 +8,7 @@ target
*\ copy*
# Ignored
ignore
_ignore
# Editors
.vscode
@@ -29,3 +29,6 @@ docker/kibo
# Types
paths.d.ts
# Outputs
_outputs
+8 -8
View File
@@ -1,6 +1,6 @@
<!--
# v0.X.Y | WIP
![Image of the kibō Web App version 0.X.Y](https://github.com/kibo-money/kibo/blob/main/assets/v0.X.Y.jpg)
![Image of the kibō Web App version 0.X.Y](https://github.com/kibo-money/kibo/blob/main/_assets/v0.X.Y.jpg)
-->
# v0.6.0 | WIP
@@ -33,7 +33,7 @@ Added git tags for each version though Markdown won't display formatted on Githu
# [v0.5.0](https://github.com/kibo-money/kibo/tree/eea56d394bf92c62c81da8b78b8c47ea730683f5) | [873199](https://mempool.space/block/0000000000000000000270925aa6a565be92e13164565a3f7994ca1966e48050) - 2024/12/04
![Image of the kibō Web App version 0.5.0](https://github.com/kibo-money/kibo/blob/main/assets/v0.5.0.jpg)
![Image of the kibō Web App version 0.5.0](https://github.com/kibo-money/kibo/blob/main/_assets/v0.5.0.jpg)
## Datasets
@@ -100,7 +100,7 @@ Added git tags for each version though Markdown won't display formatted on Githu
# [v0.4.0](https://github.com/kibo-money/kibo/tree/a64c544815d9ef785e2fc1323582f774f16b9200) | [861950](https://mempool.space/block/00000000000000000000530d0e30ccf7deeace122dcc99f2668a06c6dad83629) - 2024/09/19
![Image of the kibō Web App version 0.4.0](https://github.com/kibo-money/kibo/blob/main/assets/v0.4.0.jpg)
![Image of the kibō Web App version 0.4.0](https://github.com/kibo-money/kibo/blob/main/_assets/v0.4.0.jpg)
## Brand
@@ -137,7 +137,7 @@ Added git tags for each version though Markdown won't display formatted on Githu
# [v0.3.0](https://github.com/kibo-money/kibo/tree/b68b016091c45b071218fba01bac5b76e8eaf18c) | [853930](https://mempool.space/block/00000000000000000002eb5e9a7950ca2d5d98bd1ed28fc9098aa630d417985d) - 2024/07/26
![Image of the Satonomics Web App version 0.3.0](https://github.com/kibo-money/kibo/blob/main/assets/v0.3.0.jpg)
![Image of the Satonomics Web App version 0.3.0](https://github.com/kibo-money/kibo/blob/main/_assets/v0.3.0.jpg)
## Parser
@@ -216,7 +216,7 @@ Added git tags for each version though Markdown won't display formatted on Githu
# [v0.2.0](https://github.com/kibo-money/kibo/tree/248187889283597c5dbb806292297453c25e97b8) | [851286](https://mempool.space/block/0000000000000000000281ca7f1bf8c50702bfca168c7af1bdc67c977c1ac8ed) - 2024/07/08
![Image of the Satonomics Web App version 0.2.0](https://github.com/kibo-money/kibo/blob/main/assets/v0.2.0.jpg)
![Image of the Satonomics Web App version 0.2.0](https://github.com/kibo-money/kibo/blob/main/_assets/v0.2.0.jpg)
## App
@@ -252,7 +252,7 @@ Added git tags for each version though Markdown won't display formatted on Githu
# [v0.1.1](https://github.com/kibo-money/kibo/tree/e55b5195a9de9aea306903c94ed63cb1720fda5f) | [849240](https://mempool.space/block/000000000000000000002b8653988655071c07bb5f7181c038f9326bc86db741) - 2024/06/24
![Image of the Satonomics Web App version 0.1.1](https://github.com/kibo-money/kibo/blob/main/assets/v0.1.1.jpg)
![Image of the Satonomics Web App version 0.1.1](https://github.com/kibo-money/kibo/blob/main/_assets/v0.1.1.jpg)
## Parser
@@ -302,8 +302,8 @@ Added git tags for each version though Markdown won't display formatted on Githu
# [v0.1.0](https://github.com/kibo-money/kibo/tree/a1a576d088c8f83ed32d48753a7611f70a964574) | [848642](https://mempool.space/block/000000000000000000020be5761d70751252219a9557f55e91ecdfb86c4e026a) - 2024/06/19
![Image of the Satonomics Web App version 0.1.0](https://github.com/kibo-money/kibo/blob/main/assets/v0.1.0.jpg)
![Image of the Satonomics Web App version 0.1.0](https://github.com/kibo-money/kibo/blob/main/_assets/v0.1.0.jpg)
# v0.0.1 | [835444](https://mempool.space/block/000000000000000000009f93907a0dd83c080d5585cc7ec82c076d45f6d7c872) - 2024/03/20
![Image of the Satonomics Web App version 0.0.X](https://github.com/kibo-money/kibo/blob/main/assets/v0.0.X.jpg)
![Image of the Satonomics Web App version 0.0.X](https://github.com/kibo-money/kibo/blob/main/_assets/v0.0.X.jpg)
Generated
+3
View File
@@ -215,8 +215,11 @@ dependencies = [
"color-eyre",
"derive_deref",
"exit",
"fjall",
"jiff",
"rayon",
"storable_vec",
"unsafe_slice_serde",
]
[[package]]
+1
View File
@@ -18,6 +18,7 @@ bomputer = { path = "computer" }
color-eyre = "0.6.3"
derive_deref = "1.1.1"
exit = { path = "exit" }
fjall = "2.5.0"
jiff = "0.1.28"
rayon = "1.10.0"
storable_vec = { path = "storable_vec" }
+3 -3
View File
@@ -1,8 +1,8 @@
<a href="https://kibo.money" target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/kibo-money/kibo/main/assets/logo-long-text-dark.svg">
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/kibo-money/kibo/main/assets/logo-long-text-light.svg">
<img alt="kibō" src="https://raw.githubusercontent.com/kibo-money/kibo/main/assets/logo-long-text-light.svg" width="210" height="auto">
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/kibo-money/kibo/main/_assets/logo-long-text-dark.svg">
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/kibo-money/kibo/main/_assets/logo-long-text-light.svg">
<img alt="kibō" src="https://raw.githubusercontent.com/kibo-money/kibo/main/_assets/logo-long-text-light.svg" width="210" height="auto">
</picture>
</a>

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Before

Width:  |  Height:  |  Size: 1.8 MiB

After

Width:  |  Height:  |  Size: 1.8 MiB

Before

Width:  |  Height:  |  Size: 1.8 MiB

After

Width:  |  Height:  |  Size: 1.8 MiB

Before

Width:  |  Height:  |  Size: 496 KiB

After

Width:  |  Height:  |  Size: 496 KiB

Before

Width:  |  Height:  |  Size: 564 KiB

After

Width:  |  Height:  |  Size: 564 KiB

Before

Width:  |  Height:  |  Size: 592 KiB

After

Width:  |  Height:  |  Size: 592 KiB

Before

Width:  |  Height:  |  Size: 453 KiB

After

Width:  |  Height:  |  Size: 453 KiB

Before

Width:  |  Height:  |  Size: 526 KiB

After

Width:  |  Height:  |  Size: 526 KiB

Before

Width:  |  Height:  |  Size: 208 KiB

After

Width:  |  Height:  |  Size: 208 KiB

Before

Width:  |  Height:  |  Size: 386 KiB

After

Width:  |  Height:  |  Size: 386 KiB

View File
View File
+4 -1
View File
@@ -4,10 +4,13 @@ version = "0.1.0"
edition = "2021"
[dependencies]
biter = { workspace = true }
bindex = { workspace = true }
biter = { workspace = true }
color-eyre = { workspace = true }
derive_deref = { workspace = true }
exit = { workspace = true }
fjall = { workspace = true }
jiff = { workspace = true }
rayon = { workspace = true }
storable_vec = { workspace = true }
unsafe_slice_serde = { workspace = true }
+54 -19
View File
@@ -1,36 +1,71 @@
use std::path::Path;
use std::path::{Path, PathBuf};
use bindex::Indexer;
use bindex::{Height, Indexer};
use biter::rpc;
use exit::Exit;
mod storage;
mod structs;
use storage::{Fjalls, StorableVecs};
use structs::*;
pub struct Bomputer;
impl Bomputer {
pub fn compute() {}
pub struct Computer {
outputs_dir: PathBuf,
vecs: StorableVecs,
trees: Fjalls,
}
pub fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
impl Computer {
pub fn import(outputs_dir: &Path) -> color_eyre::Result<Self> {
let outputs_dir = outputs_dir.to_owned();
let computed_dir = outputs_dir.join("computed");
let vecs = StorableVecs::import(&computed_dir.join("vecs"))?;
let trees = Fjalls::import(&computed_dir.join("fjall"))?;
Ok(Self {
outputs_dir,
vecs,
trees,
})
}
let data_dir = Path::new("../../bitcoin");
let rpc = rpc::Client::new(
"http://localhost:8332",
rpc::Auth::CookieFile(Path::new(data_dir).join(".cookie")),
)?;
let exit = Exit::new();
pub fn compute(&mut self, bitcoin_dir: &Path, rpc: rpc::Client, exit: &Exit) -> color_eyre::Result<()> {
let mut indexer = Indexer::import(&self.outputs_dir.join("indexes"))?;
let i = std::time::Instant::now();
if false {
indexer.index(bitcoin_dir, rpc, exit)?;
}
let mut indexer = Indexer::import(Path::new("indexes"))?;
// TODO: Remove all outdated
indexer.index(data_dir, rpc, &exit)?;
// Compute txindex to X
dbg!(i.elapsed());
// Compute height to X
indexer
.vecs()
.height_to_timestamp
.read_from_(self.vecs.height_to_date.len(), |(_height, timestamp)| {
self.vecs
.height_to_date
.push_if_needed(Height::from(_height), Date::from(timestamp))
})?;
self.vecs
.height_to_date
.read_from_(self.vecs.date_to_first_height.len(), |(_height, date)| {
self.vecs
.date_to_first_height
.push_if_needed(*date, Height::from(_height))
})?;
Ok(())
// Compute date to X
// ...
// Compute month to X
// ...
// Compute year to X
// ...
Ok(())
}
}
+22 -7
View File
@@ -1,13 +1,28 @@
use structs::Date;
use std::path::Path;
use biter::rpc;
use bomputer::Computer;
use exit::Exit;
mod structs;
pub fn main() -> color_eyre::Result<()> {
let date1 = Date::from(jiff::civil::Date::constant(2009, 1, 9));
let date2 = Date::from(jiff::civil::Date::constant(2009, 1, 31));
let date3 = Date::from(jiff::civil::Date::constant(2019, 1, 9));
dbg!(usize::try_from(date1))?;
dbg!(usize::try_from(date2))?;
dbg!(usize::try_from(date3))?;
color_eyre::install()?;
let data_dir = Path::new("../../bitcoin");
let rpc = rpc::Client::new(
"http://localhost:8332",
rpc::Auth::CookieFile(Path::new(data_dir).join(".cookie")),
)?;
let exit = Exit::new();
let i = std::time::Instant::now();
let mut computer = Computer::import(Path::new("../_outputs"))?;
computer.compute(data_dir, rpc, &exit)?;
dbg!(i.elapsed());
Ok(())
}
+22
View File
@@ -0,0 +1,22 @@
use std::path::Path;
use bindex::{Store, Version};
use crate::structs::{AddressindexTxoutindex, Unit};
pub struct Fjalls {
pub address_txoutindex_in: Store<AddressindexTxoutindex, Unit>,
pub address_txoutindex_out: Store<AddressindexTxoutindex, Unit>,
}
impl Fjalls {
pub fn import(path: &Path) -> color_eyre::Result<Self> {
let address_txoutindex_in = Store::import(&path.join("address_txoutindex_in"), Version::from(1))?;
let address_txoutindex_out = Store::import(&path.join("address_txoutindex_out"), Version::from(1))?;
Ok(Self {
address_txoutindex_in,
address_txoutindex_out,
})
}
}
+5
View File
@@ -0,0 +1,5 @@
mod fjalls;
mod storable_vecs;
pub use fjalls::*;
pub use storable_vecs::*;
@@ -0,0 +1,74 @@
use std::{
fmt::Debug,
io,
ops::{Deref, DerefMut},
path::Path,
};
use bindex::{Indexer, Version};
use crate::Computer;
pub struct StorableVec<I, T> {
vec: bindex::StorableVec<I, T>,
f: Box<dyn Fn(&Indexer, &Computer) -> storable_vec::Result<Vec<(I, T)>>>,
}
impl<I, T> StorableVec<I, T>
where
I: TryInto<usize>,
T: Sized + Debug + Clone,
{
pub fn import<F>(path: &Path, version: Version, f: F) -> io::Result<Self>
where
F: Fn(&Indexer, &Computer) -> storable_vec::Result<Vec<(I, T)>> + 'static,
{
let vec = bindex::StorableVec::import(path, version)?;
Ok(Self { vec, f: Box::new(f) })
}
pub fn compute(&mut self, indexer: &Indexer, computer: &Computer) -> storable_vec::Result<()> {
(self.f)(indexer, computer)?
.into_iter()
.try_for_each(|(i, v)| self.push_if_needed(i, v))
}
// pub fn fill(&mut self) {
// self
// .vecs()
// .height_to_timestamp
// .read_iter(move |(_height, timestamp)| {
// let height = Height::from(_height);
// let date = Date::from(timestamp);
// self.vecs.date_to_first_height.push_if_needed(date, height)?;
// Ok(())
// })?;
// }
}
impl<I, T> Deref for StorableVec<I, T> {
type Target = bindex::StorableVec<I, T>;
fn deref(&self) -> &Self::Target {
&self.vec
}
}
impl<I, T> DerefMut for StorableVec<I, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.vec
}
}
pub trait AnyComputedStorableVec {
fn compute(&mut self, indexer: &Indexer, computer: &Computer) -> storable_vec::Result<()>;
}
impl<I, T> AnyComputedStorableVec for StorableVec<I, T>
where
I: TryInto<usize>,
T: Sized + Debug + Clone,
{
fn compute(&mut self, indexer: &Indexer, computer: &Computer) -> storable_vec::Result<()> {
self.compute(indexer, computer)
}
}
+77
View File
@@ -0,0 +1,77 @@
use std::{fs, path::Path};
use bindex::{Addressindex, Amount, Height, StorableVec, Timestamp, Txindex, Txinindex, Txoutindex, Version};
use crate::structs::{Date, Feerate};
// mod base;
// use base::*;
pub struct StorableVecs {
pub date_to_first_height: StorableVec<Date, Height>,
// pub height_to_block_interval: StorableVec<Height, Timestamp>,
pub height_to_date: StorableVec<Height, Date>,
// pub height_to_fee: StorableVec<Txindex, Amount>,
// pub height_to_inputcount: StorableVec<Txindex, u32>,
// pub height_to_last_addressindex: StorableVec<Height, Addressindex>,
// pub height_to_last_txindex: StorableVec<Height, Txindex>,
// pub height_to_last_txoutindex: StorableVec<Height, Txoutindex>,
// pub height_to_maxfeerate: StorableVec<Txindex, Feerate>,
// pub height_to_medianfeerate: StorableVec<Txindex, Feerate>,
// pub height_to_minfeerate: StorableVec<Txindex, Feerate>,
// pub height_to_outputcount: StorableVec<Txindex, u32>,
// pub height_to_subsidy: StorableVec<Txindex, u32>,
// pub height_to_totalfees: StorableVec<Height, Amount>,
// pub height_to_txcount: StorableVec<Txindex, u32>,
pub txindex_to_fee: StorableVec<Txindex, Amount>,
// pub txindex_to_feerate: StorableVec<Txindex, Feerate>,
pub txindex_to_inputcount: StorableVec<Txindex, u32>,
pub txindex_to_last_txinindex: StorableVec<Txindex, Txinindex>,
pub txindex_to_last_txoutindex: StorableVec<Txindex, Txoutindex>,
pub txindex_to_outputcount: StorableVec<Txindex, u32>,
}
impl StorableVecs {
pub fn import(path: &Path) -> color_eyre::Result<Self> {
fs::create_dir_all(path)?;
Ok(Self {
date_to_first_height: StorableVec::import(&path.join("date_to_first_height"), Version::from(1))?,
// height_to_block_interval: StorableVec::import(&path.join("height_to_block_interval"), Version::from(1))?,
height_to_date: StorableVec::import(&path.join("height_to_date"), Version::from(1))?,
// height_to_fee: StorableVec::import(&path.join("height_to_fee"), Version::from(1))?,
// height_to_inputcount: StorableVec::import(&path.join("height_to_inputcount"), Version::from(1))?,
// height_to_last_addressindex: StorableVec::import(
// &path.join("height_to_last_addressindex"),
// Version::from(1),
// )?,
// height_to_last_txindex: StorableVec::import(&path.join("height_to_last_txindex"), Version::from(1))?,
// height_to_last_txoutindex: StorableVec::import(&path.join("height_to_last_txoutindex"), Version::from(1))?,
// height_to_maxfeerate: StorableVec::import(&path.join("height_to_maxfeerate"), Version::from(1))?,
// height_to_medianfeerate: StorableVec::import(&path.join("height_to_medianfeerate"), Version::from(1))?,
// height_to_minfeerate: StorableVec::import(&path.join("height_to_minfeerate"), Version::from(1))?,
// height_to_outputcount: StorableVec::import(&path.join("height_to_outputcount"), Version::from(1))?,
// height_to_subsidy: StorableVec::import(&path.join("height_to_subsidy"), Version::from(1))?,
// height_to_totalfees: StorableVec::import(&path.join("height_to_totalfees"), Version::from(1))?,
// height_to_txcount: StorableVec::import(&path.join("height_to_txcount"), Version::from(1))?,
txindex_to_fee: StorableVec::import(&path.join("txindex_to_fee"), Version::from(1))?,
// txindex_to_feerate: StorableVec::import(&path.join("txindex_to_feerate"), Version::from(1))?,
txindex_to_inputcount: StorableVec::import(&path.join("txindex_to_inputcount"), Version::from(1))?,
txindex_to_last_txinindex: StorableVec::import(&path.join("txindex_to_last_txinindex"), Version::from(1))?,
txindex_to_last_txoutindex: StorableVec::import(
&path.join("txindex_to_last_txoutindex"),
Version::from(1),
)?,
txindex_to_outputcount: StorableVec::import(&path.join("txindex_to_outputcount"), Version::from(1))?,
})
}
// pub fn as_slice(&self) -> [&dyn AnyComputedStorableVec; 1] {
// [&self.date_to_first_height]
// }
// pub fn as_mut_slice(&mut self) -> [&mut dyn AnyComputedStorableVec; 1] {
// [&mut self.date_to_first_height]
// }
}
@@ -0,0 +1,21 @@
use bindex::{Addressindex, Txoutindex};
use fjall::Slice;
use unsafe_slice_serde::UnsafeSliceSerde;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct AddressindexTxoutindex {
addressindex: Addressindex,
txoutindex: Txoutindex,
}
impl TryFrom<Slice> for AddressindexTxoutindex {
type Error = unsafe_slice_serde::Error;
fn try_from(value: Slice) -> Result<Self, Self::Error> {
Ok(*Self::unsafe_try_from_slice(&value)?)
}
}
impl From<AddressindexTxoutindex> for Slice {
fn from(value: AddressindexTxoutindex) -> Self {
Self::new(value.unsafe_as_slice())
}
}
+1 -1
View File
@@ -3,7 +3,7 @@ use color_eyre::eyre::eyre;
use derive_deref::Deref;
use jiff::{civil::Date as _Date, tz::TimeZone};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deref)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Deref)]
pub struct Date(_Date);
impl Date {
+4
View File
@@ -1,5 +1,9 @@
mod addressindextxoutindex;
mod date;
mod feerate;
mod unit;
pub use addressindextxoutindex::*;
pub use date::*;
pub use feerate::*;
pub use unit::*;
+13
View File
@@ -0,0 +1,13 @@
use fjall::Slice;
pub struct Unit();
impl From<Slice> for Unit {
fn from(_: Slice) -> Self {
Self()
}
}
impl From<Unit> for Slice {
fn from(_: Unit) -> Self {
Self::new(&[])
}
}
-2
View File
@@ -1,2 +0,0 @@
/database
/indexes
+1 -1
View File
@@ -10,7 +10,7 @@ biter = { workspace = true }
color-eyre = { workspace = true }
derive_deref = { workspace = true }
exit = { workspace = true }
fjall = "2.5.0"
fjall = { workspace = true }
jiff = { workspace = true }
rapidhash = "1.3.0"
rayon = { workspace = true }
+33 -25
View File
@@ -17,7 +17,8 @@ use rayon::prelude::*;
mod storage;
mod structs;
pub use biter;
pub use storage::{AnyStorableVec, StorableVec, Store, StoreMeta};
pub use structs::Version;
use storage::{Fjalls, StorableVecs};
pub use structs::{
@@ -30,27 +31,34 @@ const SNAPSHOT_BLOCK_RANGE: usize = 1000;
pub struct Indexer {
vecs: StorableVecs,
parts: Fjalls,
trees: Fjalls,
}
impl Indexer {
pub fn import(indexes_dir: &Path) -> color_eyre::Result<Self> {
Ok(Self {
vecs: StorableVecs::import(&indexes_dir.join("vecs"))?,
parts: Fjalls::import(&indexes_dir.join("fjall"))?,
})
let vecs = StorableVecs::import(&indexes_dir.join("vecs"))?;
let trees = Fjalls::import(&indexes_dir.join("fjall"))?;
Ok(Self { vecs, trees })
}
pub fn vecs(&self) -> &StorableVecs {
&self.vecs
}
pub fn trees(&self) -> &Fjalls {
&self.trees
}
pub fn index(&mut self, bitcoin_dir: &Path, rpc: rpc::Client, exit: &Exit) -> color_eyre::Result<()> {
let check_collisions = true;
let vecs = &mut self.vecs;
let parts = &mut self.parts;
let trees = &mut self.trees;
let mut height = vecs
.min_height()
.unwrap_or_default()
.min(parts.min_height())
.min(trees.min_height())
.and_then(|h| h.checked_sub(UNSAFE_BLOCKS))
.map(Height::from)
.unwrap_or_default();
@@ -72,14 +80,14 @@ impl Indexer {
let mut p2wpkhindex_global = vecs.height_to_first_p2wpkhindex.get_or_default(height)?;
let mut p2wshindex_global = vecs.height_to_first_p2wshindex.get_or_default(height)?;
let export = |parts: &mut Fjalls, vecs: &mut StorableVecs, height: Height| -> color_eyre::Result<()> {
let export = |trees: &mut Fjalls, vecs: &mut StorableVecs, height: Height| -> color_eyre::Result<()> {
println!("Exporting...");
exit.block();
thread::scope(|scope| -> color_eyre::Result<()> {
let vecs_handle = scope.spawn(|| vecs.flush(height));
parts.commit(height)?;
trees.commit(height)?;
vecs_handle.join().unwrap()?;
Ok(())
})?;
@@ -95,18 +103,17 @@ impl Indexer {
println!("Processing block {_height}...");
height = Height::from(_height);
let timestamp = Timestamp::try_from(block.header.time)?;
if let Some(saved_blockhash) = vecs.height_to_blockhash.get(height)? {
if &blockhash != saved_blockhash.as_ref() {
todo!("Rollback not implemented");
// parts.rollback_from(&mut rtx, height, &exit)?;
// trees.rollback_from(&mut rtx, height, &exit)?;
}
}
let blockhash_prefix = BlockHashPrefix::try_from(&blockhash)?;
if parts
if trees
.blockhash_prefix_to_height
.get(&blockhash_prefix)?
.is_some_and(|prev_height| *prev_height != height)
@@ -115,12 +122,13 @@ impl Indexer {
return Err(eyre!("Collision, expect prefix to need be set yet"));
}
parts
trees
.blockhash_prefix_to_height
.insert_if_needed(blockhash_prefix, height, height);
vecs.height_to_blockhash.push_if_needed(height, blockhash)?;
vecs.height_to_timestamp.push_if_needed(height, timestamp)?;
vecs.height_to_difficulty.push_if_needed(height, block.header.difficulty_float())?;
vecs.height_to_timestamp.push_if_needed(height, Timestamp::try_from(block.header.time)?)?;
vecs.height_to_size.push_if_needed(height, block.total_size())?;
vecs.height_to_weight.push_if_needed(height, block.weight())?;
vecs.height_to_first_txindex.push_if_needed(height, txindex_global)?;
@@ -193,9 +201,9 @@ impl Indexer {
let txid_prefix = TxidPrefix::try_from(&txid)?;
let prev_txindex_opt =
if check_collisions && parts.txid_prefix_to_txindex.needs(height) {
if check_collisions && trees.txid_prefix_to_txindex.needs(height) {
// Should only find collisions for two txids (duplicates), see below
parts.txid_prefix_to_txindex.get(&txid_prefix)?.map(|v| *v)
trees.txid_prefix_to_txindex.get(&txid_prefix)?.map(|v| *v)
} else {
None
};
@@ -234,7 +242,7 @@ impl Indexer {
return Ok((txinindex, InputSource::SameBlock((tx, txindex, txin, vin))));
}
let prev_txindex = if let Some(txindex) = parts
let prev_txindex = if let Some(txindex) = trees
.txid_prefix_to_txindex
.get(&TxidPrefix::try_from(&outpoint.txid)?)?
.map(|v| *v)
@@ -312,7 +320,7 @@ impl Indexer {
});
let addressindex_opt = addressbytes_res.as_ref().ok().and_then(|addressbytes| {
parts
trees
.addresshash_to_addressindex
.get(&AddressHash::from((addressbytes, addresstype)))
.unwrap()
@@ -344,9 +352,9 @@ impl Indexer {
let prev_addressbytes =
prev_addressbytes_opt.as_ref().context("Expect to have addressbytes")?;
if (vecs.addressindex_to_addresstype.hasnt(addressindex)
if (vecs.addressindex_to_addresstype.hasnt(addressindex)?
&& addresstype != prev_addresstype)
|| (parts.addresshash_to_addressindex.needs(height)
|| (trees.addresshash_to_addressindex.needs(height)
&& prev_addressbytes != addressbytes)
{
let txid = tx.compute_txid();
@@ -496,7 +504,7 @@ impl Indexer {
already_added_addresshash
.insert(addresshash, addressindex);
parts.addresshash_to_addressindex.insert_if_needed(
trees.addresshash_to_addressindex.insert_if_needed(
addresshash,
addressindex,
height,
@@ -583,7 +591,7 @@ impl Indexer {
match prev_txindex_opt {
None => {
parts
trees
.txid_prefix_to_txindex
.insert_if_needed(txid_prefix, txindex, height);
}
@@ -649,13 +657,13 @@ impl Indexer {
let should_snapshot = _height != 0 && _height % SNAPSHOT_BLOCK_RANGE == 0 && !exit.active();
if should_snapshot {
export(parts, vecs, height)?;
export(trees, vecs, height)?;
}
Ok(())
})?;
export(parts, vecs, height)?;
export(trees, vecs, height)?;
sleep(Duration::from_millis(100));
+1 -1
View File
@@ -16,7 +16,7 @@ fn main() -> color_eyre::Result<()> {
let i = std::time::Instant::now();
let mut indexer = Indexer::import(Path::new("indexes"))?;
let mut indexer = Indexer::import(Path::new("../_outputs/indexes"))?;
indexer.index(data_dir, rpc, &exit)?;
+8 -5
View File
@@ -9,24 +9,24 @@ use unsafe_slice_serde::UnsafeSliceSerde;
use crate::structs::{Height, Version};
use super::Meta;
use super::StoreMeta;
pub struct Partition<Key, Value> {
meta: Meta,
pub struct Store<Key, Value> {
meta: StoreMeta,
keyspace: TransactionalKeyspace,
part: TransactionalPartitionHandle,
rtx: ReadTransaction,
puts: BTreeMap<Key, Value>,
}
impl<K, V> Partition<K, V>
impl<K, V> Store<K, V>
where
K: Into<Slice> + Ord,
V: Into<Slice> + TryFrom<Slice>,
<V as TryFrom<Slice>>::Error: error::Error + Send + Sync + 'static,
{
pub fn import(path: &Path, version: Version) -> color_eyre::Result<Self> {
let meta = Meta::checked_open(path, version)?;
let meta = StoreMeta::checked_open(path, version)?;
let keyspace = if let Ok(keyspace) = Self::open_keyspace(path) {
keyspace
} else {
@@ -95,6 +95,9 @@ where
pub fn len(&self) -> usize {
self.meta.len() + self.puts.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn has(&self, height: Height) -> bool {
self.meta.has(height)
+5 -2
View File
@@ -7,14 +7,14 @@ use unsafe_slice_serde::UnsafeSliceSerde;
use super::{Height, Version};
pub struct Meta {
pub struct StoreMeta {
pathbuf: PathBuf,
version: Version,
height: Option<Height>,
len: usize,
}
impl Meta {
impl StoreMeta {
pub fn checked_open(path: &Path, version: Version) -> color_eyre::Result<Self> {
fs::create_dir_all(path)?;
@@ -40,6 +40,9 @@ impl Meta {
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn export(&mut self, len: usize, height: Height) -> io::Result<()> {
self.len = len;
+12 -11
View File
@@ -5,24 +5,25 @@ use crate::{structs::Version, AddressHash, Addressindex, BlockHashPrefix, Height
mod base;
mod meta;
use base::*;
use meta::*;
pub use base::*;
pub use meta::*;
pub struct Fjalls {
pub addresshash_to_addressindex: Partition<AddressHash, Addressindex>,
pub blockhash_prefix_to_height: Partition<BlockHashPrefix, Height>,
pub txid_prefix_to_txindex: Partition<TxidPrefix, Txindex>,
pub addresshash_to_addressindex: Store<AddressHash, Addressindex>,
pub blockhash_prefix_to_height: Store<BlockHashPrefix, Height>,
pub txid_prefix_to_txindex: Store<TxidPrefix, Txindex>,
}
impl Fjalls {
pub fn import(path: &Path) -> color_eyre::Result<Self> {
let addresshash_to_addressindex = Store::import(&path.join("addresshash_to_addressindex"), Version::from(1))?;
let blockhash_prefix_to_height = Store::import(&path.join("blockhash_prefix_to_height"), Version::from(1))?;
let txid_prefix_to_txindex = Store::import(&path.join("txid_prefix_to_txindex"), Version::from(1))?;
Ok(Self {
addresshash_to_addressindex: Partition::import(
&path.join("addresshash_to_addressindex"),
Version::from(1),
)?,
blockhash_prefix_to_height: Partition::import(&path.join("blockhash_prefix_to_height"), Version::from(1))?,
txid_prefix_to_txindex: Partition::import(&path.join("txid_prefix_to_txindex"), Version::from(1))?,
addresshash_to_addressindex,
blockhash_prefix_to_height,
txid_prefix_to_txindex,
})
}
+4 -3
View File
@@ -7,6 +7,7 @@ use std::{
use super::{Height, Version};
#[derive(Debug)]
pub struct StorableVec<I, T> {
height: Option<Height>,
pathbuf: PathBuf,
@@ -16,7 +17,7 @@ pub struct StorableVec<I, T> {
impl<I, T> StorableVec<I, T>
where
I: Into<usize>,
I: TryInto<usize>,
T: Sized + Debug + Clone,
{
pub fn import(path: &Path, version: Version) -> io::Result<Self> {
@@ -99,12 +100,12 @@ impl<I, T> DerefMut for StorableVec<I, T> {
}
}
pub trait AnyBindexVec {
pub trait AnyStorableVec {
fn height(&self) -> color_eyre::Result<Height>;
fn flush(&mut self, height: Height) -> io::Result<()>;
}
impl<I, T> AnyBindexVec for StorableVec<I, T>
impl<I, T> AnyStorableVec for StorableVec<I, T>
where
I: Into<usize>,
T: Sized + Debug + Clone,
+9 -22
View File
@@ -12,13 +12,14 @@ use crate::structs::{
mod base;
use base::*;
pub use base::*;
pub struct StorableVecs {
pub addressindex_to_addresstype: StorableVec<Addressindex, Addresstype>,
pub addressindex_to_addresstypeindex: StorableVec<Addressindex, Addresstypeindex>,
pub addressindex_to_height: StorableVec<Addressindex, Height>,
pub height_to_blockhash: StorableVec<Height, BlockHash>,
pub height_to_difficulty: StorableVec<Height, f64>,
pub height_to_first_addressindex: StorableVec<Height, Addressindex>,
pub height_to_first_emptyindex: StorableVec<Height, Addresstypeindex>,
pub height_to_first_multisigindex: StorableVec<Height, Addresstypeindex>,
@@ -54,23 +55,6 @@ pub struct StorableVecs {
pub txinindex_to_txoutindex: StorableVec<Txinindex, Txoutindex>,
pub txoutindex_to_addressindex: StorableVec<Txoutindex, Addressindex>,
pub txoutindex_to_amount: StorableVec<Txoutindex, Amount>,
// Can be computed later:
// pub height_to_date: StorableVec<Height, Date>,
// pub height_to_totalfees: StorableVec<Height, Amount>,
// pub height_to_inputcount: StorableVec<Txindex, u32>,
// pub height_to_last_addressindex: StorableVec<Height, Addressindex>,
// pub height_to_last_txindex: StorableVec<Height, Txindex>,
// pub height_to_last_txoutindex: StorableVec<Height, Txoutindex>,
// pub height_to_outputcount: StorableVec<Txindex, u32>,
// pub height_to_txcount: StorableVec<Txindex, u32>,
// pub height_to_subsidy: StorableVec<Txindex, u32>,
// pub height_to_minfeerate: StorableVec<Txindex, Feerate>,
// pub height_to_maxfeerate: StorableVec<Txindex, Feerate>,
// pub height_to_medianfeerate: StorableVec<Txindex, Feerate>,
// pub txindex_to_feerate: StorableVec<Txindex, Feerate>,
// pub txindex_to_inputcount: StorableVec<Txindex, u32>,
// pub txindex_to_outputcount: StorableVec<Txindex, u32>,
// pub txindex_to_last_txoutindex: StorableVec<Txindex, Txoutindex>,
}
// const UNSAFE_BLOCKS: usize = 100;
@@ -90,6 +74,7 @@ impl StorableVecs {
)?,
addressindex_to_height: StorableVec::import(&path.join("addressindex_to_height"), Version::from(1))?,
height_to_blockhash: StorableVec::import(&path.join("height_to_blockhash"), Version::from(1))?,
height_to_difficulty: StorableVec::import(&path.join("height_to_difficulty"), Version::from(1))?,
height_to_first_addressindex: StorableVec::import(
&path.join("height_to_first_addressindex"),
Version::from(1),
@@ -383,12 +368,13 @@ impl StorableVecs {
.min())
}
pub fn as_slice(&self) -> [&dyn AnyBindexVec; 39] {
pub fn as_slice(&self) -> [&dyn AnyStorableVec; 40] {
[
&self.addressindex_to_addresstype as &dyn AnyBindexVec,
&self.addressindex_to_addresstype as &dyn AnyStorableVec,
&self.addressindex_to_addresstypeindex,
&self.addressindex_to_height,
&self.height_to_blockhash,
&self.height_to_difficulty,
&self.height_to_first_addressindex,
&self.height_to_first_emptyindex,
&self.height_to_first_multisigindex,
@@ -427,12 +413,13 @@ impl StorableVecs {
]
}
pub fn as_mut_slice(&mut self) -> [&mut (dyn AnyBindexVec + Send + Sync); 39] {
pub fn as_mut_slice(&mut self) -> [&mut (dyn AnyStorableVec + Send + Sync); 40] {
[
&mut self.addressindex_to_addresstype as &mut (dyn AnyBindexVec + Send + Sync),
&mut self.addressindex_to_addresstype as &mut (dyn AnyStorableVec + Send + Sync),
&mut self.addressindex_to_addresstypeindex,
&mut self.addressindex_to_height,
&mut self.height_to_blockhash,
&mut self.height_to_difficulty,
&mut self.height_to_first_addressindex,
&mut self.height_to_first_emptyindex,
&mut self.height_to_first_multisigindex,
+4 -5
View File
@@ -1,10 +1,9 @@
use derive_deref::{Deref, DerefMut};
// use snkrj::{direct_repr, Storable, UnsizedStorable};
use fjall::Slice;
use unsafe_slice_serde::UnsafeSliceSerde;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Deref, DerefMut, Default)]
pub struct Addressindex(u32);
// direct_repr!(Addressindex);
impl Addressindex {
pub const BYTES: usize = size_of::<Self>();
@@ -50,13 +49,13 @@ impl From<Addressindex> for usize {
}
}
impl TryFrom<fjall::Slice> for Addressindex {
impl TryFrom<Slice> for Addressindex {
type Error = unsafe_slice_serde::Error;
fn try_from(value: fjall::Slice) -> Result<Self, Self::Error> {
fn try_from(value: Slice) -> Result<Self, Self::Error> {
Ok(*Self::unsafe_try_from_slice(&value)?)
}
}
impl From<Addressindex> for fjall::Slice {
impl From<Addressindex> for Slice {
fn from(value: Addressindex) -> Self {
Self::new(value.unsafe_as_slice())
}
-2
View File
@@ -1,9 +1,7 @@
use derive_deref::{Deref, DerefMut};
// use snkrj::{direct_repr, Storable, UnsizedStorable};
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Deref, DerefMut, Default)]
pub struct Addresstypeindex(u32);
// direct_repr!(Addresstypeindex);
impl Addresstypeindex {
pub fn decremented(self) -> Self {
-2
View File
@@ -5,13 +5,11 @@ use std::{
use biter::bitcoin;
use derive_deref::{Deref, DerefMut};
// use snkrj::{direct_repr, Storable, UnsizedStorable};
use super::Height;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Deref, DerefMut, Default)]
pub struct Amount(bitcoin::Amount);
// direct_repr!(Amount);
impl Amount {
pub const ZERO: Self = Self(bitcoin::Amount::ZERO);
+13 -16
View File
@@ -2,14 +2,13 @@ use std::hash::Hasher;
use biter::bitcoin::{BlockHash, Txid};
use derive_deref::Deref;
// use snkrj::{direct_repr, Storable, UnsizedStorable};
use fjall::Slice;
use unsafe_slice_serde::UnsafeSliceSerde;
use super::{Addressbytes, Addresstype, SliceExtended};
#[derive(Debug, Deref, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct AddressHash([u8; 8]);
// direct_repr!(AddressHash);
impl From<(&Addressbytes, Addresstype)> for AddressHash {
fn from((addressbytes, addresstype): (&Addressbytes, Addresstype)) -> Self {
let mut hasher = rapidhash::RapidHasher::default();
@@ -24,18 +23,18 @@ impl From<[u8; 8]> for AddressHash {
Self(value)
}
}
impl TryFrom<fjall::Slice> for AddressHash {
impl TryFrom<Slice> for AddressHash {
type Error = color_eyre::Report;
fn try_from(value: fjall::Slice) -> Result<Self, Self::Error> {
fn try_from(value: Slice) -> Result<Self, Self::Error> {
Ok(*Self::unsafe_try_from_slice(&value)?)
}
}
impl From<&AddressHash> for fjall::Slice {
impl From<&AddressHash> for Slice {
fn from(value: &AddressHash) -> Self {
Self::new(value.unsafe_as_slice())
}
}
impl From<AddressHash> for fjall::Slice {
impl From<AddressHash> for Slice {
fn from(value: AddressHash) -> Self {
Self::from(&value)
}
@@ -43,25 +42,24 @@ impl From<AddressHash> for fjall::Slice {
#[derive(Debug, Deref, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct BlockHashPrefix([u8; 8]);
// direct_repr!(BlockHashPrefix);
impl TryFrom<&BlockHash> for BlockHashPrefix {
type Error = color_eyre::Report;
fn try_from(value: &BlockHash) -> Result<Self, Self::Error> {
Ok(Self((&value[..]).read_8x_u8()?))
}
}
impl TryFrom<fjall::Slice> for BlockHashPrefix {
impl TryFrom<Slice> for BlockHashPrefix {
type Error = color_eyre::Report;
fn try_from(value: fjall::Slice) -> Result<Self, Self::Error> {
fn try_from(value: Slice) -> Result<Self, Self::Error> {
Ok(*Self::unsafe_try_from_slice(&value)?)
}
}
impl From<&BlockHashPrefix> for fjall::Slice {
impl From<&BlockHashPrefix> for Slice {
fn from(value: &BlockHashPrefix) -> Self {
Self::new(value.unsafe_as_slice())
}
}
impl From<BlockHashPrefix> for fjall::Slice {
impl From<BlockHashPrefix> for Slice {
fn from(value: BlockHashPrefix) -> Self {
Self::from(&value)
}
@@ -69,25 +67,24 @@ impl From<BlockHashPrefix> for fjall::Slice {
#[derive(Debug, Deref, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct TxidPrefix([u8; 8]);
// direct_repr!(TxidPrefix);
impl TryFrom<&Txid> for TxidPrefix {
type Error = color_eyre::Report;
fn try_from(value: &Txid) -> Result<Self, Self::Error> {
Ok(Self((&value[..]).read_8x_u8()?))
}
}
impl TryFrom<fjall::Slice> for TxidPrefix {
impl TryFrom<Slice> for TxidPrefix {
type Error = color_eyre::Report;
fn try_from(value: fjall::Slice) -> Result<Self, Self::Error> {
fn try_from(value: Slice) -> Result<Self, Self::Error> {
Ok(*Self::unsafe_try_from_slice(&value)?)
}
}
impl From<&TxidPrefix> for fjall::Slice {
impl From<&TxidPrefix> for Slice {
fn from(value: &TxidPrefix) -> Self {
Self::new(value.unsafe_as_slice())
}
}
impl From<TxidPrefix> for fjall::Slice {
impl From<TxidPrefix> for Slice {
fn from(value: TxidPrefix) -> Self {
Self::from(&value)
}
+4 -5
View File
@@ -6,12 +6,11 @@ use std::{
use biter::rpc::{self, RpcApi};
use derive_deref::{Deref, DerefMut};
// use snkrj::{direct_repr, Storable, UnsizedStorable};
use fjall::Slice;
use unsafe_slice_serde::UnsafeSliceSerde;
#[derive(Debug, Clone, Copy, Deref, DerefMut, PartialEq, Eq, PartialOrd, Ord, Default)]
pub struct Height(u32);
// direct_repr!(Height);
impl Height {
pub fn write(&self, path: &Path) -> Result<(), io::Error> {
@@ -120,13 +119,13 @@ impl TryFrom<&rpc::Client> for Height {
}
}
impl TryFrom<fjall::Slice> for Height {
impl TryFrom<Slice> for Height {
type Error = unsafe_slice_serde::Error;
fn try_from(value: fjall::Slice) -> Result<Self, Self::Error> {
fn try_from(value: Slice) -> Result<Self, Self::Error> {
Ok(*Self::unsafe_try_from_slice(&value)?)
}
}
impl From<Height> for fjall::Slice {
impl From<Height> for Slice {
fn from(value: Height) -> Self {
Self::new(value.unsafe_as_slice())
}
+1 -1
View File
@@ -1,6 +1,6 @@
use derive_deref::Deref;
#[derive(Debug, Deref, Clone)]
#[derive(Debug, Deref, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Timestamp(jiff::Timestamp);
impl TryFrom<u32> for Timestamp {
+4 -5
View File
@@ -1,12 +1,11 @@
use std::ops::{Add, AddAssign};
use derive_deref::{Deref, DerefMut};
// use snkrj::{direct_repr, Storable, UnsizedStorable};
use fjall::Slice;
use unsafe_slice_serde::UnsafeSliceSerde;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Deref, DerefMut, Default)]
pub struct Txindex(u32);
// direct_repr!(Txindex);
impl Txindex {
pub fn incremented(self) -> Self {
@@ -59,13 +58,13 @@ impl From<Txindex> for usize {
}
}
impl TryFrom<fjall::Slice> for Txindex {
impl TryFrom<Slice> for Txindex {
type Error = unsafe_slice_serde::Error;
fn try_from(value: fjall::Slice) -> Result<Self, Self::Error> {
fn try_from(value: Slice) -> Result<Self, Self::Error> {
Ok(*Self::unsafe_try_from_slice(&value)?)
}
}
impl From<Txindex> for fjall::Slice {
impl From<Txindex> for Slice {
fn from(value: Txindex) -> Self {
Self::new(value.unsafe_as_slice())
}
-2
View File
@@ -1,13 +1,11 @@
use std::ops::{Add, AddAssign};
use derive_deref::{Deref, DerefMut};
// use snkrj::{direct_repr, Storable, UnsizedStorable};
use super::Vout;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Deref, DerefMut, Default)]
pub struct Txinindex(u64);
// direct_repr!(Txinindex);
impl Txinindex {
pub fn incremented(self) -> Self {
-2
View File
@@ -1,13 +1,11 @@
use std::ops::{Add, AddAssign};
use derive_deref::{Deref, DerefMut};
// use snkrj::{direct_repr, Storable, UnsizedStorable};
use super::Vout;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Deref, DerefMut, Default)]
pub struct Txoutindex(u64);
// direct_repr!(Txoutindex);
impl Txoutindex {
pub const COINBASE: Self = Self(u64::MAX);
+4 -3
View File
@@ -1,5 +1,6 @@
use std::{fs, io, path::Path};
use fjall::Slice;
use unsafe_slice_serde::UnsafeSliceSerde;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
@@ -24,13 +25,13 @@ impl TryFrom<&Path> for Version {
}
}
impl TryFrom<fjall::Slice> for Version {
impl TryFrom<Slice> for Version {
type Error = color_eyre::Report;
fn try_from(value: fjall::Slice) -> Result<Self, Self::Error> {
fn try_from(value: Slice) -> Result<Self, Self::Error> {
Ok(*Self::unsafe_try_from_slice(&value)?)
}
}
impl From<Version> for fjall::Slice {
impl From<Version> for Slice {
fn from(value: Version) -> Self {
Self::new(value.unsafe_as_slice())
}
+15 -10
View File
@@ -59,6 +59,15 @@ impl BlkIndexToBlkRecap {
self.tree.remove(&blk_index);
});
while self.tree.last_entry().map(|last| *last.key()).is_some_and(|key| {
if key >= self.tree.len() {
self.tree.pop_last();
true
} else {
false
}
}) {}
self.last_safe_height = self.tree.values().map(|recap| recap.height()).max();
}
@@ -68,13 +77,12 @@ impl BlkIndexToBlkRecap {
if last_value.height() < start {
return Some((*last_key, *last_value));
} else if let Some((blk_index, _)) = self
.tree
.iter()
.find(|(_, blk_recap)| blk_recap.is_younger_than(start))
} else if let Some((blk_index, _)) =
self.tree.iter().find(|(_, blk_recap)| blk_recap.is_younger_than(start))
{
if *blk_index != 0 {
let blk_index = *blk_index - 1;
// Temporary fix, need to rethink the whole thing
let blk_index = (*blk_index).checked_sub(3).unwrap_or_default();
return Some((blk_index, *self.tree.get(&blk_index).unwrap()));
}
}
@@ -103,13 +111,10 @@ impl BlkIndexToBlkRecap {
unreachable!();
}
self.tree
.insert(blk_index, BlkRecap::first(blk_metadata_and_block));
self.tree.insert(blk_index, BlkRecap::first(blk_metadata_and_block));
}
if self
.last_safe_height
.map_or(true, |safe_height| height >= safe_height)
if self.last_safe_height.map_or(true, |safe_height| height >= safe_height)
&& (height % TARGET_BLOCKS_PER_MONTH) == 0
{
self.export();
+5 -34
View File
@@ -98,7 +98,7 @@ pub fn new(
thread::spawn(move || {
blk_index_to_blk_path
.iter()
.filter(|(blk_index, _)| blk_index >= &&starting_blk_index)
.filter(|(blk_index, _)| **blk_index >= starting_blk_index)
.try_for_each(move |(blk_index, blk_path)| {
let blk_metadata = BlkMetadata::new(*blk_index, blk_path);
@@ -147,38 +147,6 @@ pub fn new(
})
});
// thread::spawn(move || {
// recv_block_reader.iter().par_bridge().try_for_each(
// move |(blk_metadata, mut block_state)| {
// let raw_block = match block_state {
// BlockState::Raw(vec) => vec,
// _ => unreachable!(),
// };
// let mut cursor = Cursor::new(raw_block);
// block_state = BlockState::Decoded(Block::consensus_decode(&mut cursor).unwrap());
// if send_block
// .send(BlkMetadataAndBlock::new(
// blk_metadata,
// match block_state {
// BlockState::Decoded(block) => block,
// _ => unreachable!(),
// },
// ))
// .is_err()
// {
// return ControlFlow::Break(());
// }
// ControlFlow::Continue(())
// },
// );
// });
// Can't use the previous code because .send() blocks all the threads if full
// And other .par_iter() are also stuck because of that
thread::spawn(move || {
let mut bulk = vec![];
@@ -192,7 +160,9 @@ pub fn new(
let mut cursor = Cursor::new(raw_block);
*block_state = BlockState::Decoded(Block::consensus_decode(&mut cursor).unwrap());
let block = Block::consensus_decode(&mut cursor).unwrap();
*block_state = BlockState::Decoded(block);
});
bulk.drain(..).try_for_each(|(blk_metadata, block_state)| {
@@ -219,6 +189,7 @@ pub fn new(
return ControlFlow::Continue(());
}
// Sending in bulk to not lock threads in standby
drain_and_send(&mut bulk)
});
+2 -2
View File
@@ -5,13 +5,13 @@ use bitcoincore_rpc::{Auth, Client};
fn main() {
let i = std::time::Instant::now();
let data_dir = Path::new("../../../bitcoin");
let data_dir = Path::new("../../bitcoin");
let url = "http://localhost:8332";
let cookie = Path::new(data_dir).join(".cookie");
let auth = Auth::CookieFile(cookie);
let rpc = Client::new(url, auth).unwrap();
let start = Some(810078);
let start = Some(749900);
let end = None;
biter::new(data_dir, start, end, rpc)
-25
View File
@@ -1,25 +0,0 @@
#!/usr/bin/env bash
# https://stackoverflow.com/questions/31389483/find-and-delete-file-or-folder-older-than-x-days
if command -v ulimit &> /dev/null; then
echo "Increasing limit of opened files..."
# ulimit -n 1000000 # Can't be $(ulimit -Hn), bitcoind needs some too !
# Needed because the datasets tree is too big lol
echo "Increasing stack size..."
ulimit -s $(ulimit -Hs)
fi
# For Mac OS users
if [ "$(uname)" == "Darwin" ]; then
if mdutil -s / | grep "enabled"; then
echo "Disabling spotlight indexing..."
sudo mdutil -a -i off &> /dev/null
fi
echo "Thinning local TimeMachine snapshots..."
tmutil thinlocalsnapshots / &> /dev/null
fi
cargo run -r -- "$@"
+56 -23
View File
@@ -50,7 +50,7 @@ const MAX_CACHE_SIZE: usize = 100 * ONE_MB;
impl<I, T> StorableVec<I, T>
where
I: Into<usize>,
I: TryInto<usize>,
T: Sized + Debug + Clone,
{
pub const SIZE_OF_T: usize = size_of::<T>();
@@ -60,11 +60,11 @@ where
pub const CACHE_LENGTH: usize = MAX_CACHE_SIZE / Self::PAGE_SIZE;
pub fn import(path: &Path) -> Result<Self, io::Error> {
let file = Self::open_file(path)?;
let file = Self::open_file_(path)?;
let mut this = Self {
pathbuf: path.to_owned(),
disk_len: Self::byte_index_to_index(file.metadata()?.len() as usize),
disk_len: Self::byte_index_to_index(Self::file_len(&file)?),
file,
cache: vec![],
pushed: vec![],
@@ -81,6 +81,10 @@ where
Ok(this)
}
fn file_len(file: &File) -> io::Result<usize> {
Ok(file.metadata()?.len() as usize)
}
fn reset_cache(&mut self) {
// let len = (self.disk_len as f64 / Self::PER_PAGE as f64).ceil() as usize;
// self.cache.clear();
@@ -100,7 +104,10 @@ where
}
}
fn open_file(path: &Path) -> Result<File, io::Error> {
fn open_file(&self) -> Result<File, Error> {
Self::open_file_(&self.pathbuf).map_err(Error::IO)
}
fn open_file_(path: &Path) -> Result<File, io::Error> {
OpenOptions::new()
.read(true)
.create(true)
@@ -140,7 +147,7 @@ where
#[inline]
pub fn get(&self, index: I) -> Result<Option<Value<'_, T>>> {
self.get_(index.into())
self.get_(index.try_into().map_err(|_| Error::FailedKeyTryIntoUsize)?)
}
pub fn get_(&self, index: usize) -> Result<Option<Value<'_, T>>> {
match self.index_to_pushed_index(index) {
@@ -163,9 +170,7 @@ where
let page_index = index / Self::PER_PAGE;
let last_index = self.disk_len - 1;
let max_page_index = last_index / Self::PER_PAGE;
let min_page_index = (max_page_index + 1)
.checked_sub(self.cache.len())
.unwrap_or_default();
let min_page_index = (max_page_index + 1).checked_sub(self.cache.len()).unwrap_or_default();
// let min_open_page = self.min.load(AtomicOrdering::SeqCst);
@@ -196,14 +201,11 @@ where
T::unsafe_try_from_slice(slice).map_err(Error::UnsafeSliceSerde)?,
)))
} else {
let mut file = Self::open_file(&self.pathbuf).map_err(Error::IO)?;
file.seek(SeekFrom::Start(byte_index as u64))
.map_err(Error::IO)?;
let mut file = self.open_file()?;
let mut buf = vec![0; Self::SIZE_OF_T];
file.read_exact(&mut buf).map_err(Error::IO)?;
file.seek(SeekFrom::Start(byte_index as u64)).map_err(Error::IO)?;
file.read_exact(&mut buf).map_err(Error::IO)?;
let value = T::unsafe_try_from_slice(&buf[..]).map_err(Error::UnsafeSliceSerde)?;
Ok(Some(Value::Owned(value.to_owned())))
@@ -213,10 +215,39 @@ where
where
T: Default + Clone,
{
Ok(self
.get(index)?
.map(|v| (*v).clone())
.unwrap_or(Default::default()))
Ok(self.get(index)?.map(|v| (*v).clone()).unwrap_or(Default::default()))
}
pub fn read_iter<F>(&self, f: F) -> Result<()>
where
F: FnMut((usize, &T)) -> Result<()>,
{
self.read_from_(0, f)
}
pub fn read_from_<F>(&self, index: usize, mut f: F) -> Result<()>
where
F: FnMut((usize, &T)) -> Result<()>,
{
let mut file = self.open_file()?;
let mut buf = vec![0; Self::SIZE_OF_T];
let byte_index = Self::index_to_byte_index(index);
file.seek(SeekFrom::Start(byte_index as u64)).map_err(Error::IO)?;
let mut i = 0;
loop {
if file.read_exact(&mut buf).map_err(Error::IO).is_err() {
break;
}
let v = T::unsafe_try_from_slice(&buf[..]).map_err(Error::UnsafeSliceSerde)?;
f((i, v))?;
i += 1;
}
Ok(())
}
#[allow(unused)]
@@ -238,7 +269,7 @@ where
}
pub fn push_if_needed(&mut self, index: I, value: T) -> Result<()> {
self.push_if_needed_(index.into(), value)
self.push_if_needed_(index.try_into().map_err(|_| Error::FailedKeyTryIntoUsize)?, value)
}
pub fn push_if_needed_(&mut self, index: usize, value: T) -> Result<()> {
let len = self.len();
@@ -307,15 +338,15 @@ where
self.len() == 0
}
pub fn has(&self, index: I) -> bool {
self.has_(index.into())
pub fn has(&self, index: I) -> Result<bool> {
Ok(self.has_(index.try_into().map_err(|_| Error::FailedKeyTryIntoUsize)?))
}
pub fn has_(&self, index: usize) -> bool {
index < self.len()
}
pub fn hasnt(&self, index: I) -> bool {
self.hasnt_(index.into())
pub fn hasnt(&self, index: I) -> Result<bool> {
Ok(self.hasnt_(index.try_into().map_err(|_| Error::FailedKeyTryIntoUsize)?))
}
pub fn hasnt_(&self, index: usize) -> bool {
!self.has_(index)
@@ -414,6 +445,7 @@ pub enum Error {
IndexTooHigh,
ExpectFileToHaveIndex,
ExpectVecToHaveIndex,
FailedKeyTryIntoUsize,
}
impl fmt::Display for Error {
// This trait requires `fmt` with this exact signature.
@@ -425,6 +457,7 @@ impl fmt::Display for Error {
Error::IndexTooHigh => write!(f, "Index too high"),
Error::ExpectFileToHaveIndex => write!(f, "Expect file to have index"),
Error::ExpectVecToHaveIndex => write!(f, "Expect vec to have index"),
Error::FailedKeyTryIntoUsize => write!(f, "Failed to convert key to usize"),
}
}
}
+1
View File
@@ -1,3 +1,4 @@
[package]
name = "unsafe_slice_serde"
version = "0.1.0"
edition = "2021"