mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-05-19 06:14:47 -07:00
global: fixes
This commit is contained in:
@@ -126,7 +126,11 @@ pub fn generate_api_methods(output: &mut String, endpoints: &[Endpoint]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if endpoint.supports_csv {
|
if endpoint.supports_csv {
|
||||||
writeln!(output, " if (format === 'csv') return this.getText(path, {{ signal, onUpdate }});").unwrap();
|
writeln!(
|
||||||
|
output,
|
||||||
|
" if (format === 'csv') return this.getText(path, {{ signal, onUpdate }});"
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
writeln!(output, " return {};", fetch_call).unwrap();
|
writeln!(output, " return {};", fetch_call).unwrap();
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -89,21 +89,17 @@ impl<T> ByType<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter_typed(&self) -> impl Iterator<Item = (OutputType, &T)> {
|
pub fn iter_typed(&self) -> impl Iterator<Item = (OutputType, &T)> {
|
||||||
self.spendable
|
self.spendable.iter_typed().chain(std::iter::once((
|
||||||
.iter_typed()
|
OutputType::OpReturn,
|
||||||
.chain(std::iter::once((
|
&self.unspendable.op_return,
|
||||||
OutputType::OpReturn,
|
)))
|
||||||
&self.unspendable.op_return,
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter_typed_mut(&mut self) -> impl Iterator<Item = (OutputType, &mut T)> {
|
pub fn iter_typed_mut(&mut self) -> impl Iterator<Item = (OutputType, &mut T)> {
|
||||||
self.spendable
|
self.spendable.iter_typed_mut().chain(std::iter::once((
|
||||||
.iter_typed_mut()
|
OutputType::OpReturn,
|
||||||
.chain(std::iter::once((
|
&mut self.unspendable.op_return,
|
||||||
OutputType::OpReturn,
|
)))
|
||||||
&mut self.unspendable.op_return,
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ pub struct Vecs<M: StorageMode = Rw> {
|
|||||||
pub _9m: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 270d
|
pub _9m: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 270d
|
||||||
pub _350d: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
pub _350d: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||||
pub _12m: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 360d
|
pub _12m: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 360d
|
||||||
pub _1y: CachedVec<M::Stored<EagerVec<PcoVec<Height, Height>>>>, // 365d
|
pub _1y: CachedVec<M::Stored<EagerVec<PcoVec<Height, Height>>>>, // 365d
|
||||||
pub _14m: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 420d
|
pub _14m: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 420d
|
||||||
pub _2y: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 730d
|
pub _2y: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 730d
|
||||||
pub _26m: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 780d
|
pub _26m: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 780d
|
||||||
|
|||||||
@@ -165,9 +165,7 @@ impl ActivityCountVecs {
|
|||||||
self.reactivated.block.push(counts.reactivated.into());
|
self.reactivated.block.push(counts.reactivated.into());
|
||||||
self.sending.block.push(counts.sending.into());
|
self.sending.block.push(counts.sending.into());
|
||||||
self.receiving.block.push(counts.receiving.into());
|
self.receiving.block.push(counts.receiving.into());
|
||||||
self.bidirectional
|
self.bidirectional.block.push(counts.bidirectional.into());
|
||||||
.block
|
|
||||||
.push(counts.bidirectional.into());
|
|
||||||
let active = counts.sending + counts.receiving - counts.bidirectional;
|
let active = counts.sending + counts.receiving - counts.bidirectional;
|
||||||
self.active.block.push(active.into());
|
self.active.block.push(active.into());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,7 @@ use super::TotalAddrCountVecs;
|
|||||||
/// New address count per block (global + per-type).
|
/// New address count per block (global + per-type).
|
||||||
#[derive(Deref, DerefMut, Traversable)]
|
#[derive(Deref, DerefMut, Traversable)]
|
||||||
pub struct NewAddrCountVecs<M: StorageMode = Rw>(
|
pub struct NewAddrCountVecs<M: StorageMode = Rw>(
|
||||||
#[traversable(flatten)]
|
#[traversable(flatten)] pub WithAddrTypes<PerBlockCumulativeRolling<StoredU64, StoredU64, M>>,
|
||||||
pub WithAddrTypes<PerBlockCumulativeRolling<StoredU64, StoredU64, M>>,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
impl NewAddrCountVecs {
|
impl NewAddrCountVecs {
|
||||||
@@ -28,7 +27,11 @@ impl NewAddrCountVecs {
|
|||||||
Ok(Self(WithAddrTypes::<
|
Ok(Self(WithAddrTypes::<
|
||||||
PerBlockCumulativeRolling<StoredU64, StoredU64>,
|
PerBlockCumulativeRolling<StoredU64, StoredU64>,
|
||||||
>::forced_import(
|
>::forced_import(
|
||||||
db, "new_addr_count", version, indexes, cached_starts
|
db,
|
||||||
|
"new_addr_count",
|
||||||
|
version,
|
||||||
|
indexes,
|
||||||
|
cached_starts,
|
||||||
)?))
|
)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -92,34 +92,30 @@ impl AddrEventsVecs {
|
|||||||
cached_starts,
|
cached_starts,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let import_percent = |name: &str| -> Result<WithAddrTypes<
|
let import_percent =
|
||||||
PercentCumulativeRolling<BasisPoints16>,
|
|name: &str| -> Result<WithAddrTypes<PercentCumulativeRolling<BasisPoints16>>> {
|
||||||
>> {
|
Ok(WithAddrTypes {
|
||||||
Ok(WithAddrTypes {
|
all: PercentCumulativeRolling::forced_import(db, name, version, indexes)?,
|
||||||
all: PercentCumulativeRolling::forced_import(db, name, version, indexes)?,
|
by_addr_type: ByAddrType::new_with_name(|type_name| {
|
||||||
by_addr_type: ByAddrType::new_with_name(|type_name| {
|
PercentCumulativeRolling::forced_import(
|
||||||
PercentCumulativeRolling::forced_import(
|
db,
|
||||||
db,
|
&format!("{type_name}_{name}"),
|
||||||
&format!("{type_name}_{name}"),
|
version,
|
||||||
version,
|
indexes,
|
||||||
indexes,
|
)
|
||||||
)
|
})?,
|
||||||
})?,
|
})
|
||||||
})
|
};
|
||||||
};
|
|
||||||
|
|
||||||
let output_to_reused_addr_count =
|
let output_to_reused_addr_count = import_count(&format!("output_to_{name}_addr_count"))?;
|
||||||
import_count(&format!("output_to_{name}_addr_count"))?;
|
let output_to_reused_addr_share = import_percent(&format!("output_to_{name}_addr_share"))?;
|
||||||
let output_to_reused_addr_share =
|
|
||||||
import_percent(&format!("output_to_{name}_addr_share"))?;
|
|
||||||
let spendable_output_to_reused_addr_share = PercentCumulativeRolling::forced_import(
|
let spendable_output_to_reused_addr_share = PercentCumulativeRolling::forced_import(
|
||||||
db,
|
db,
|
||||||
&format!("spendable_output_to_{name}_addr_share"),
|
&format!("spendable_output_to_{name}_addr_share"),
|
||||||
version,
|
version,
|
||||||
indexes,
|
indexes,
|
||||||
)?;
|
)?;
|
||||||
let input_from_reused_addr_count =
|
let input_from_reused_addr_count = import_count(&format!("input_from_{name}_addr_count"))?;
|
||||||
import_count(&format!("input_from_{name}_addr_count"))?;
|
|
||||||
let input_from_reused_addr_share =
|
let input_from_reused_addr_share =
|
||||||
import_percent(&format!("input_from_{name}_addr_share"))?;
|
import_percent(&format!("input_from_{name}_addr_share"))?;
|
||||||
|
|
||||||
@@ -229,12 +225,13 @@ impl AddrEventsVecs {
|
|||||||
starting_indexes.height,
|
starting_indexes.height,
|
||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
self.spendable_output_to_reused_addr_share.compute_count_ratio(
|
self.spendable_output_to_reused_addr_share
|
||||||
&self.output_to_reused_addr_count.all,
|
.compute_count_ratio(
|
||||||
&outputs_by_type.spendable_output_count,
|
&self.output_to_reused_addr_count.all,
|
||||||
starting_indexes.height,
|
&outputs_by_type.spendable_output_count,
|
||||||
exit,
|
starting_indexes.height,
|
||||||
)?;
|
exit,
|
||||||
|
)?;
|
||||||
self.input_from_reused_addr_share.all.compute_count_ratio(
|
self.input_from_reused_addr_share.all.compute_count_ratio(
|
||||||
&self.input_from_reused_addr_count.all,
|
&self.input_from_reused_addr_count.all,
|
||||||
&inputs_by_type.input_count.all,
|
&inputs_by_type.input_count.all,
|
||||||
@@ -246,7 +243,9 @@ impl AddrEventsVecs {
|
|||||||
.by_addr_type
|
.by_addr_type
|
||||||
.get_mut_unwrap(otype)
|
.get_mut_unwrap(otype)
|
||||||
.compute_count_ratio(
|
.compute_count_ratio(
|
||||||
self.output_to_reused_addr_count.by_addr_type.get_unwrap(otype),
|
self.output_to_reused_addr_count
|
||||||
|
.by_addr_type
|
||||||
|
.get_unwrap(otype),
|
||||||
outputs_by_type.output_count.by_type.get(otype),
|
outputs_by_type.output_count.by_type.get(otype),
|
||||||
starting_indexes.height,
|
starting_indexes.height,
|
||||||
exit,
|
exit,
|
||||||
@@ -255,7 +254,9 @@ impl AddrEventsVecs {
|
|||||||
.by_addr_type
|
.by_addr_type
|
||||||
.get_mut_unwrap(otype)
|
.get_mut_unwrap(otype)
|
||||||
.compute_count_ratio(
|
.compute_count_ratio(
|
||||||
self.input_from_reused_addr_count.by_addr_type.get_unwrap(otype),
|
self.input_from_reused_addr_count
|
||||||
|
.by_addr_type
|
||||||
|
.get_unwrap(otype),
|
||||||
inputs_by_type.input_count.by_type.get(otype),
|
inputs_by_type.input_count.by_type.get(otype),
|
||||||
starting_indexes.height,
|
starting_indexes.height,
|
||||||
exit,
|
exit,
|
||||||
|
|||||||
@@ -2,9 +2,7 @@ use brk_types::{FundedAddrData, Height, OutputType, Sats};
|
|||||||
|
|
||||||
use crate::distribution::{block::TrackingStatus, vecs::AddrMetricsVecs};
|
use crate::distribution::{block::TrackingStatus, vecs::AddrMetricsVecs};
|
||||||
|
|
||||||
use super::{
|
use super::{AddrTypeToActivityCounts, AddrTypeToAddrCount, ExposedAddrState, ReusedAddrState};
|
||||||
AddrTypeToActivityCounts, AddrTypeToAddrCount, ExposedAddrState, ReusedAddrState,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Bundle of per-block runtime state for the full address-metrics pipeline.
|
/// Bundle of per-block runtime state for the full address-metrics pipeline.
|
||||||
/// Feeds `process_received` / `process_sent` and is pushed to [`AddrMetricsVecs`]
|
/// Feeds `process_received` / `process_sent` and is pushed to [`AddrMetricsVecs`]
|
||||||
@@ -162,7 +160,8 @@ impl AddrMetricsState {
|
|||||||
also_received,
|
also_received,
|
||||||
will_be_empty,
|
will_be_empty,
|
||||||
);
|
);
|
||||||
self.exposed.on_send(output_type, addr_data, pre, will_be_empty);
|
self.exposed
|
||||||
|
.on_send(output_type, addr_data, pre, will_be_empty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -67,8 +67,7 @@ pub(crate) fn process_funded_addrs(
|
|||||||
|
|
||||||
// Pure pushes - no holes remain
|
// Pure pushes - no holes remain
|
||||||
addrs_data.funded.reserve_pushed(pushes_iter.len());
|
addrs_data.funded.reserve_pushed(pushes_iter.len());
|
||||||
for (next_index, (addr_type, type_index, data)) in
|
for (next_index, (addr_type, type_index, data)) in (addrs_data.funded.len()..).zip(pushes_iter)
|
||||||
(addrs_data.funded.len()..).zip(pushes_iter)
|
|
||||||
{
|
{
|
||||||
addrs_data.funded.push(data);
|
addrs_data.funded.push(data);
|
||||||
result.get_mut(addr_type).unwrap().insert(
|
result.get_mut(addr_type).unwrap().insert(
|
||||||
@@ -138,9 +137,7 @@ pub(crate) fn process_empty_addrs(
|
|||||||
|
|
||||||
// Pure pushes - no holes remain
|
// Pure pushes - no holes remain
|
||||||
addrs_data.empty.reserve_pushed(pushes_iter.len());
|
addrs_data.empty.reserve_pushed(pushes_iter.len());
|
||||||
for (next_index, (addr_type, type_index, data)) in
|
for (next_index, (addr_type, type_index, data)) in (addrs_data.empty.len()..).zip(pushes_iter) {
|
||||||
(addrs_data.empty.len()..).zip(pushes_iter)
|
|
||||||
{
|
|
||||||
addrs_data.empty.push(data);
|
addrs_data.empty.push(data);
|
||||||
result.get_mut(addr_type).unwrap().insert(
|
result.get_mut(addr_type).unwrap().insert(
|
||||||
type_index,
|
type_index,
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
use std::{cmp::Reverse, collections::BinaryHeap, fs, path::Path};
|
use std::{cmp::Reverse, collections::BinaryHeap, fs, path::Path};
|
||||||
|
|
||||||
use brk_cohort::{AGE_RANGE_NAMES, CohortContext, Filtered, PROFITABILITY_RANGE_COUNT, TERM_NAMES};
|
use brk_cohort::{AGE_RANGE_NAMES, CohortContext, Filtered, PROFITABILITY_RANGE_COUNT, TERM_NAMES};
|
||||||
use rayon::prelude::*;
|
|
||||||
use brk_error::Result;
|
use brk_error::Result;
|
||||||
use brk_types::{BasisPoints16, Cents, CentsCompact, UrpdRaw, Date, Dollars, Sats};
|
use brk_types::{BasisPoints16, Cents, CentsCompact, Date, Dollars, Sats, UrpdRaw};
|
||||||
|
use rayon::prelude::*;
|
||||||
|
|
||||||
use crate::distribution::metrics::{CostBasis, ProfitabilityMetrics};
|
use crate::distribution::metrics::{CostBasis, ProfitabilityMetrics};
|
||||||
|
|
||||||
|
|||||||
@@ -190,7 +190,10 @@ pub(crate) fn process_blocks(
|
|||||||
.first_index
|
.first_index
|
||||||
.collect_range_at(start_usize, end_usize);
|
.collect_range_at(start_usize, end_usize);
|
||||||
|
|
||||||
debug!("recovering addr metrics state from height {}", starting_height);
|
debug!(
|
||||||
|
"recovering addr metrics state from height {}",
|
||||||
|
starting_height
|
||||||
|
);
|
||||||
let mut state = AddrMetricsState::from((&vecs.addrs, starting_height));
|
let mut state = AddrMetricsState::from((&vecs.addrs, starting_height));
|
||||||
debug!("addr metrics state recovered");
|
debug!("addr metrics state recovered");
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use crate::{
|
|||||||
metrics::ImportConfig,
|
metrics::ImportConfig,
|
||||||
state::{CohortState, CostBasisOps, RealizedOps},
|
state::{CohortState, CostBasisOps, RealizedOps},
|
||||||
},
|
},
|
||||||
internal::{ValuePerBlockCumulativeRolling, PerBlockCumulativeRolling},
|
internal::{PerBlockCumulativeRolling, ValuePerBlockCumulativeRolling},
|
||||||
prices,
|
prices,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ use vecdb::{BytesVec, BytesVecValue, Database, ImportableVec};
|
|||||||
use crate::{
|
use crate::{
|
||||||
indexes,
|
indexes,
|
||||||
internal::{
|
internal::{
|
||||||
ValuePerBlock, ValuePerBlockCumulative, ValuePerBlockCumulativeRolling, FiatType,
|
FiatPerBlock, FiatPerBlockCumulativeWithSums, FiatType, NumericValue, PerBlock,
|
||||||
FiatPerBlock, FiatPerBlockCumulativeWithSums, NumericValue, PerBlock,
|
|
||||||
PerBlockCumulativeRolling, PercentPerBlock, PercentRollingWindows, Price,
|
PerBlockCumulativeRolling, PercentPerBlock, PercentRollingWindows, Price,
|
||||||
PriceWithRatioExtendedPerBlock, PriceWithRatioPerBlock, RatioPerBlock,
|
PriceWithRatioExtendedPerBlock, PriceWithRatioPerBlock, RatioPerBlock,
|
||||||
RollingWindow24hPerBlock, RollingWindows, RollingWindowsFrom1w, WindowStartVec, Windows,
|
RollingWindow24hPerBlock, RollingWindows, RollingWindowsFrom1w, ValuePerBlock,
|
||||||
|
ValuePerBlockCumulative, ValuePerBlockCumulativeRolling, WindowStartVec, Windows,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -201,7 +201,10 @@ impl CostBasis {
|
|||||||
if invested_raw == 0 {
|
if invested_raw == 0 {
|
||||||
return (h, spot);
|
return (h, spot);
|
||||||
}
|
}
|
||||||
(h, Cents::new((capitalized_cap.inner() / invested_raw) as u64))
|
(
|
||||||
|
h,
|
||||||
|
Cents::new((capitalized_cap.inner() / invested_raw) as u64),
|
||||||
|
)
|
||||||
},
|
},
|
||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
@@ -215,7 +218,10 @@ impl CostBasis {
|
|||||||
if invested_raw == 0 {
|
if invested_raw == 0 {
|
||||||
return (h, spot);
|
return (h, spot);
|
||||||
}
|
}
|
||||||
(h, Cents::new((capitalized_cap.inner() / invested_raw) as u64))
|
(
|
||||||
|
h,
|
||||||
|
Cents::new((capitalized_cap.inner() / invested_raw) as u64),
|
||||||
|
)
|
||||||
},
|
},
|
||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use vecdb::{AnyStoredVec, AnyVec, Database, Exit, Rw, StorageMode, WritableVec};
|
|||||||
use crate::{
|
use crate::{
|
||||||
indexes,
|
indexes,
|
||||||
internal::{
|
internal::{
|
||||||
ValuePerBlock, ValuePerBlockWithDeltas, PerBlock, RatioPerBlock, WindowStartVec, Windows,
|
PerBlock, RatioPerBlock, ValuePerBlock, ValuePerBlockWithDeltas, WindowStartVec, Windows,
|
||||||
},
|
},
|
||||||
prices,
|
prices,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ use crate::{
|
|||||||
blocks,
|
blocks,
|
||||||
distribution::state::{CohortState, CostBasisData, RealizedState, WithCapital},
|
distribution::state::{CohortState, CostBasisData, RealizedState, WithCapital},
|
||||||
internal::{
|
internal::{
|
||||||
ValuePerBlockCumulativeRolling, FiatPerBlockCumulativeWithSums, PercentPerBlock,
|
FiatPerBlockCumulativeWithSums, PercentPerBlock, PercentRollingWindows,
|
||||||
PercentRollingWindows, PriceWithRatioExtendedPerBlock, RatioCents64, RatioCentsBp32,
|
PriceWithRatioExtendedPerBlock, RatioCents64, RatioCentsBp32, RatioCentsSignedCentsBps32,
|
||||||
RatioCentsSignedCentsBps32, RatioCentsSignedDollarsBps32, RatioDollarsBp32,
|
RatioCentsSignedDollarsBps32, RatioDollarsBp32, RatioPerBlockPercentiles,
|
||||||
RatioPerBlockPercentiles, RatioPerBlockStdDevBands, RatioSma, RollingWindows,
|
RatioPerBlockStdDevBands, RatioSma, RollingWindows, RollingWindowsFrom1w,
|
||||||
RollingWindowsFrom1w,
|
ValuePerBlockCumulativeRolling,
|
||||||
},
|
},
|
||||||
prices,
|
prices,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -76,13 +76,12 @@ impl SupplyBase {
|
|||||||
all_supply_sats: &impl ReadableVec<Height, Sats>,
|
all_supply_sats: &impl ReadableVec<Height, Sats>,
|
||||||
exit: &Exit,
|
exit: &Exit,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.dominance
|
self.dominance.compute_binary::<Sats, Sats, RatioSatsBp16>(
|
||||||
.compute_binary::<Sats, Sats, RatioSatsBp16>(
|
max_from,
|
||||||
max_from,
|
&self.total.sats.height,
|
||||||
&self.total.sats.height,
|
all_supply_sats,
|
||||||
all_supply_sats,
|
exit,
|
||||||
exit,
|
)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn compute_from_stateful(
|
pub(crate) fn compute_from_stateful(
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
|
|||||||
use crate::{distribution::state::UnrealizedState, prices};
|
use crate::{distribution::state::UnrealizedState, prices};
|
||||||
|
|
||||||
use crate::internal::{
|
use crate::internal::{
|
||||||
ValuePerBlock, HalveCents, HalveDollars, HalveSats, HalveSatsToBitcoin, LazyValuePerBlock,
|
HalveCents, HalveDollars, HalveSats, HalveSatsToBitcoin, LazyValuePerBlock, ValuePerBlock,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::distribution::metrics::ImportConfig;
|
use crate::distribution::metrics::ImportConfig;
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ mod avg_amount;
|
|||||||
mod base;
|
mod base;
|
||||||
mod core;
|
mod core;
|
||||||
|
|
||||||
pub use avg_amount::AvgAmountMetrics;
|
|
||||||
pub use self::core::SupplyCore;
|
pub use self::core::SupplyCore;
|
||||||
|
pub use avg_amount::AvgAmountMetrics;
|
||||||
pub use base::SupplyBase;
|
pub use base::SupplyBase;
|
||||||
|
|||||||
@@ -215,8 +215,12 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
|
|||||||
pre.prev_capitalized_cap,
|
pre.prev_capitalized_cap,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.cost_basis
|
self.cost_basis.decrement(
|
||||||
.decrement(pre.prev_price, pre.sats, pre.prev_ps, pre.prev_capitalized_cap);
|
pre.prev_price,
|
||||||
|
pre.sats,
|
||||||
|
pre.prev_ps,
|
||||||
|
pre.prev_capitalized_cap,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn send_utxo(
|
pub(crate) fn send_utxo(
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use brk_error::{Error, Result};
|
use brk_error::{Error, Result};
|
||||||
use brk_types::{
|
use brk_types::{Cents, CentsCompact, CentsSats, CentsSquaredSats, Height, Sats, UrpdRaw};
|
||||||
Cents, CentsCompact, CentsSats, CentsSquaredSats, UrpdRaw, Height, Sats,
|
|
||||||
};
|
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use vecdb::{Bytes, unlikely};
|
use vecdb::{Bytes, unlikely};
|
||||||
|
|
||||||
use super::{Accumulate, CachedUnrealizedState, UnrealizedState};
|
use super::{Accumulate, CachedUnrealizedState, UnrealizedState};
|
||||||
use crate::distribution::state::pending::{PendingCapDelta, PendingDelta, PendingCapitalizedCapRawDelta};
|
use crate::distribution::state::pending::{
|
||||||
|
PendingCapDelta, PendingCapitalizedCapRawDelta, PendingDelta,
|
||||||
|
};
|
||||||
|
|
||||||
/// Type alias for the price-to-sats map used in cost basis data.
|
/// Type alias for the price-to-sats map used in cost basis data.
|
||||||
pub(super) type CostBasisMap = BTreeMap<CentsCompact, Sats>;
|
pub(super) type CostBasisMap = BTreeMap<CentsCompact, Sats>;
|
||||||
|
|||||||
@@ -200,12 +200,14 @@ impl RealizedOps for CoreRealizedState {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn increment_snapshot(&mut self, price_sats: CentsSats, _capitalized_cap: CentsSquaredSats) {
|
fn increment_snapshot(&mut self, price_sats: CentsSats, _capitalized_cap: CentsSquaredSats) {
|
||||||
self.minimal.increment_snapshot(price_sats, _capitalized_cap);
|
self.minimal
|
||||||
|
.increment_snapshot(price_sats, _capitalized_cap);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn decrement_snapshot(&mut self, price_sats: CentsSats, _capitalized_cap: CentsSquaredSats) {
|
fn decrement_snapshot(&mut self, price_sats: CentsSats, _capitalized_cap: CentsSquaredSats) {
|
||||||
self.minimal.decrement_snapshot(price_sats, _capitalized_cap);
|
self.minimal
|
||||||
|
.decrement_snapshot(price_sats, _capitalized_cap);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -301,7 +303,8 @@ impl RealizedOps for RealizedState {
|
|||||||
fn increment(&mut self, price: Cents, sats: Sats) {
|
fn increment(&mut self, price: Cents, sats: Sats) {
|
||||||
self.core.increment(price, sats);
|
self.core.increment(price, sats);
|
||||||
if sats.is_not_zero() {
|
if sats.is_not_zero() {
|
||||||
self.capitalized_cap_raw += CentsSats::from_price_sats(price, sats).to_capitalized_cap(price);
|
self.capitalized_cap_raw +=
|
||||||
|
CentsSats::from_price_sats(price, sats).to_capitalized_cap(price);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -220,8 +220,7 @@ impl Vecs {
|
|||||||
let addr_count = AddrCountsVecs::forced_import(&db, "addr_count", version, indexes)?;
|
let addr_count = AddrCountsVecs::forced_import(&db, "addr_count", version, indexes)?;
|
||||||
let empty_addr_count =
|
let empty_addr_count =
|
||||||
AddrCountsVecs::forced_import(&db, "empty_addr_count", version, indexes)?;
|
AddrCountsVecs::forced_import(&db, "empty_addr_count", version, indexes)?;
|
||||||
let addr_activity =
|
let addr_activity = AddrActivityVecs::forced_import(&db, version, indexes, cached_starts)?;
|
||||||
AddrActivityVecs::forced_import(&db, version, indexes, cached_starts)?;
|
|
||||||
|
|
||||||
// Stored total = addr_count + empty_addr_count (global + per-type, with all derived indexes)
|
// Stored total = addr_count + empty_addr_count (global + per-type, with all derived indexes)
|
||||||
let total_addr_count = TotalAddrCountVecs::forced_import(&db, version, indexes)?;
|
let total_addr_count = TotalAddrCountVecs::forced_import(&db, version, indexes)?;
|
||||||
@@ -548,7 +547,9 @@ impl Vecs {
|
|||||||
self.addrs.empty.compute_rest(starting_indexes, exit)?;
|
self.addrs.empty.compute_rest(starting_indexes, exit)?;
|
||||||
let t = &self.utxo_cohorts.type_;
|
let t = &self.utxo_cohorts.type_;
|
||||||
let type_supply_sats = ByAddrType::new(|filter| {
|
let type_supply_sats = ByAddrType::new(|filter| {
|
||||||
let Filter::Type(ot) = filter else { unreachable!() };
|
let Filter::Type(ot) = filter else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
&t.get(ot).metrics.supply.total.sats.height
|
&t.get(ot).metrics.supply.total.sats.height
|
||||||
});
|
});
|
||||||
let all_supply_sats = &self.utxo_cohorts.all.metrics.supply.total.sats.height;
|
let all_supply_sats = &self.utxo_cohorts.all.metrics.supply.total.sats.height;
|
||||||
|
|||||||
@@ -61,7 +61,11 @@ impl Vecs {
|
|||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
|agg| {
|
|agg| {
|
||||||
push_block(&mut self.input_count, agg.entries_all, &agg.entries_per_type);
|
push_block(
|
||||||
|
&mut self.input_count,
|
||||||
|
agg.entries_all,
|
||||||
|
&agg.entries_per_type,
|
||||||
|
);
|
||||||
push_block(&mut self.tx_count, agg.txs_all, &agg.txs_per_type);
|
push_block(&mut self.tx_count, agg.txs_all, &agg.txs_per_type);
|
||||||
|
|
||||||
if self.input_count.all.block.batch_limit_reached() {
|
if self.input_count.all.block.batch_limit_reached() {
|
||||||
@@ -81,8 +85,7 @@ impl Vecs {
|
|||||||
|
|
||||||
self.input_count
|
self.input_count
|
||||||
.compute_rest(starting_indexes.height, exit)?;
|
.compute_rest(starting_indexes.height, exit)?;
|
||||||
self.tx_count
|
self.tx_count.compute_rest(starting_indexes.height, exit)?;
|
||||||
.compute_rest(starting_indexes.height, exit)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (otype, source) in self.input_count.by_type.iter_typed() {
|
for (otype, source) in self.input_count.by_type.iter_typed() {
|
||||||
|
|||||||
@@ -6,9 +6,7 @@ use vecdb::Database;
|
|||||||
use super::{Vecs, WithInputTypes};
|
use super::{Vecs, WithInputTypes};
|
||||||
use crate::{
|
use crate::{
|
||||||
indexes,
|
indexes,
|
||||||
internal::{
|
internal::{PerBlockCumulativeRolling, PercentCumulativeRolling, WindowStartVec, Windows},
|
||||||
PerBlockCumulativeRolling, PercentCumulativeRolling, WindowStartVec, Windows,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
impl Vecs {
|
impl Vecs {
|
||||||
@@ -18,26 +16,24 @@ impl Vecs {
|
|||||||
indexes: &indexes::Vecs,
|
indexes: &indexes::Vecs,
|
||||||
cached_starts: &Windows<&WindowStartVec>,
|
cached_starts: &Windows<&WindowStartVec>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let input_count = WithInputTypes::<
|
let input_count =
|
||||||
PerBlockCumulativeRolling<StoredU64, StoredU64>,
|
WithInputTypes::<PerBlockCumulativeRolling<StoredU64, StoredU64>>::forced_import_with(
|
||||||
>::forced_import_with(
|
db,
|
||||||
db,
|
"input_count_bis",
|
||||||
"input_count_bis",
|
|t| format!("{t}_prevout_count"),
|
||||||
|t| format!("{t}_prevout_count"),
|
version,
|
||||||
version,
|
indexes,
|
||||||
indexes,
|
cached_starts,
|
||||||
cached_starts,
|
)?;
|
||||||
)?;
|
let tx_count =
|
||||||
let tx_count = WithInputTypes::<
|
WithInputTypes::<PerBlockCumulativeRolling<StoredU64, StoredU64>>::forced_import_with(
|
||||||
PerBlockCumulativeRolling<StoredU64, StoredU64>,
|
db,
|
||||||
>::forced_import_with(
|
"non_coinbase_tx_count",
|
||||||
db,
|
|t| format!("tx_count_with_{t}_prevout"),
|
||||||
"non_coinbase_tx_count",
|
version,
|
||||||
|t| format!("tx_count_with_{t}_prevout"),
|
indexes,
|
||||||
version,
|
cached_starts,
|
||||||
indexes,
|
)?;
|
||||||
cached_starts,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let input_share = SpendableType::try_new(|_, name| {
|
let input_share = SpendableType::try_new(|_, name| {
|
||||||
PercentCumulativeRolling::forced_import(
|
PercentCumulativeRolling::forced_import(
|
||||||
|
|||||||
@@ -68,7 +68,9 @@ where
|
|||||||
dep_version: Version,
|
dep_version: Version,
|
||||||
at_height: Height,
|
at_height: Height,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.all.block.validate_and_truncate(dep_version, at_height)?;
|
self.all
|
||||||
|
.block
|
||||||
|
.validate_and_truncate(dep_version, at_height)?;
|
||||||
for v in self.by_type.iter_mut() {
|
for v in self.by_type.iter_mut() {
|
||||||
v.block.validate_and_truncate(dep_version, at_height)?;
|
v.block.validate_and_truncate(dep_version, at_height)?;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,8 +20,7 @@ impl Vecs {
|
|||||||
self.spent.compute(indexer, starting_indexes, exit)?;
|
self.spent.compute(indexer, starting_indexes, exit)?;
|
||||||
self.count
|
self.count
|
||||||
.compute(indexer, indexes, blocks, starting_indexes, exit)?;
|
.compute(indexer, indexes, blocks, starting_indexes, exit)?;
|
||||||
self.per_sec
|
self.per_sec.compute(&self.count, starting_indexes, exit)?;
|
||||||
.compute(&self.count, starting_indexes, exit)?;
|
|
||||||
self.by_type.compute(indexer, starting_indexes, exit)?;
|
self.by_type.compute(indexer, starting_indexes, exit)?;
|
||||||
|
|
||||||
let exit = exit.clone();
|
let exit = exit.clone();
|
||||||
|
|||||||
@@ -81,4 +81,3 @@ pub(crate) fn walk_blocks(
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use vecdb::{Database, Exit, Rw, StorageMode};
|
|||||||
use crate::{
|
use crate::{
|
||||||
indexes,
|
indexes,
|
||||||
internal::{
|
internal::{
|
||||||
FiatType, FiatBlock, FiatPerBlock, LazyRollingSumsFiatFromHeight, WindowStartVec, Windows,
|
FiatBlock, FiatPerBlock, FiatType, LazyRollingSumsFiatFromHeight, WindowStartVec, Windows,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use crate::{
|
|||||||
internal::{BpsType, LazyRollingDeltasFiatFromHeight, WindowStartVec, Windows},
|
internal::{BpsType, LazyRollingDeltasFiatFromHeight, WindowStartVec, Windows},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{FiatType, FiatPerBlockCumulativeWithSums};
|
use super::{FiatPerBlockCumulativeWithSums, FiatType};
|
||||||
|
|
||||||
#[derive(Deref, DerefMut, Traversable)]
|
#[derive(Deref, DerefMut, Traversable)]
|
||||||
pub struct FiatPerBlockCumulativeWithSumsAndDeltas<C, CS, B, M: StorageMode = Rw>
|
pub struct FiatPerBlockCumulativeWithSumsAndDeltas<C, CS, B, M: StorageMode = Rw>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use vecdb::{DeltaSub, LazyDeltaVec, LazyVecFrom1, ReadOnlyClone, ReadableCloneab
|
|||||||
use crate::{
|
use crate::{
|
||||||
indexes,
|
indexes,
|
||||||
internal::{
|
internal::{
|
||||||
FiatType, DerivedResolutions, LazyPerBlock, LazyRollingSumFromHeight, Resolutions,
|
DerivedResolutions, FiatType, LazyPerBlock, LazyRollingSumFromHeight, Resolutions,
|
||||||
WindowStartVec, Windows,
|
WindowStartVec, Windows,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -19,9 +19,7 @@ pub struct LazyRollingSumFiatFromHeight<C: FiatType> {
|
|||||||
|
|
||||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||||
#[traversable(transparent)]
|
#[traversable(transparent)]
|
||||||
pub struct LazyRollingSumsFiatFromHeight<C: FiatType>(
|
pub struct LazyRollingSumsFiatFromHeight<C: FiatType>(pub Windows<LazyRollingSumFiatFromHeight<C>>);
|
||||||
pub Windows<LazyRollingSumFiatFromHeight<C>>,
|
|
||||||
);
|
|
||||||
|
|
||||||
impl<C: FiatType> LazyRollingSumsFiatFromHeight<C> {
|
impl<C: FiatType> LazyRollingSumsFiatFromHeight<C> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use crate::{
|
|||||||
internal::{BpsType, LazyRollingDeltasFiatFromHeight, WindowStartVec, Windows},
|
internal::{BpsType, LazyRollingDeltasFiatFromHeight, WindowStartVec, Windows},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{FiatType, FiatPerBlock};
|
use super::{FiatPerBlock, FiatType};
|
||||||
|
|
||||||
#[derive(Deref, DerefMut, Traversable)]
|
#[derive(Deref, DerefMut, Traversable)]
|
||||||
pub struct FiatPerBlockWithDeltas<C, CS, B, M: StorageMode = Rw>
|
pub struct FiatPerBlockWithDeltas<C, CS, B, M: StorageMode = Rw>
|
||||||
|
|||||||
@@ -44,7 +44,9 @@ where
|
|||||||
Self {
|
Self {
|
||||||
height: LazyVecFrom1::transformed::<F>(name, version, height_source),
|
height: LazyVecFrom1::transformed::<F>(name, version, height_source),
|
||||||
resolutions: Box::new(DerivedResolutions::from_derived_computed::<F>(
|
resolutions: Box::new(DerivedResolutions::from_derived_computed::<F>(
|
||||||
name, version, resolutions,
|
name,
|
||||||
|
version,
|
||||||
|
resolutions,
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ use vecdb::{BinaryTransform, Database, Exit, ReadableVec, Rw, StorageMode, VecVa
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
indexes,
|
indexes,
|
||||||
internal::{BpsType, PerBlockCumulativeRolling, PercentPerBlock, PercentRollingWindows, RatioU64Bp16},
|
internal::{
|
||||||
|
BpsType, PerBlockCumulativeRolling, PercentPerBlock, PercentRollingWindows, RatioU64Bp16,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Traversable)]
|
#[derive(Traversable)]
|
||||||
|
|||||||
@@ -27,8 +27,7 @@ impl<B: BpsType> LazyPercentCumulativeRolling<B> {
|
|||||||
version: Version,
|
version: Version,
|
||||||
source: &PercentCumulativeRolling<B>,
|
source: &PercentCumulativeRolling<B>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let cumulative =
|
let cumulative = LazyPercentPerBlock::from_percent::<F>(name, version, &source.cumulative);
|
||||||
LazyPercentPerBlock::from_percent::<F>(name, version, &source.cumulative);
|
|
||||||
let rolling = LazyPercentRollingWindows::from_rolling::<F>(name, version, &source.rolling);
|
let rolling = LazyPercentRollingWindows::from_rolling::<F>(name, version, &source.rolling);
|
||||||
Self {
|
Self {
|
||||||
cumulative,
|
cumulative,
|
||||||
|
|||||||
@@ -46,12 +46,7 @@ where
|
|||||||
starts_version,
|
starts_version,
|
||||||
move || cached.cached(),
|
move || cached.cached(),
|
||||||
);
|
);
|
||||||
let resolutions = Resolutions::forced_import(
|
let resolutions = Resolutions::forced_import(&full_name, avg.clone(), version, indexes);
|
||||||
&full_name,
|
|
||||||
avg.clone(),
|
|
||||||
version,
|
|
||||||
indexes,
|
|
||||||
);
|
|
||||||
LazyRollingAvgFromHeight {
|
LazyRollingAvgFromHeight {
|
||||||
height: avg,
|
height: avg,
|
||||||
resolutions: Box::new(resolutions),
|
resolutions: Box::new(resolutions),
|
||||||
|
|||||||
@@ -369,12 +369,8 @@ where
|
|||||||
move || cached.cached()
|
move || cached.cached()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
let change_resolutions = Resolutions::forced_import(
|
let change_resolutions =
|
||||||
¢s_name,
|
Resolutions::forced_import(¢s_name, change_vec.clone(), version, indexes);
|
||||||
change_vec.clone(),
|
|
||||||
version,
|
|
||||||
indexes,
|
|
||||||
);
|
|
||||||
let cents = LazyDeltaFromHeight {
|
let cents = LazyDeltaFromHeight {
|
||||||
height: change_vec,
|
height: change_vec,
|
||||||
resolutions: Box::new(change_resolutions),
|
resolutions: Box::new(change_resolutions),
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode};
|
|||||||
use crate::{
|
use crate::{
|
||||||
indexes,
|
indexes,
|
||||||
internal::{
|
internal::{
|
||||||
ValuePerBlockCumulative, LazyRollingAvgsAmountFromHeight, LazyRollingSumsAmountFromHeight,
|
LazyRollingAvgsAmountFromHeight, LazyRollingSumsAmountFromHeight, ValuePerBlockCumulative,
|
||||||
WindowStartVec, Windows,
|
WindowStartVec, Windows,
|
||||||
},
|
},
|
||||||
prices,
|
prices,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode};
|
|||||||
use crate::{
|
use crate::{
|
||||||
indexes,
|
indexes,
|
||||||
internal::{
|
internal::{
|
||||||
ValuePerBlockCumulativeRolling, RollingDistributionValuePerBlock, WindowStartVec,
|
RollingDistributionValuePerBlock, ValuePerBlockCumulativeRolling, WindowStartVec,
|
||||||
WindowStarts, Windows,
|
WindowStarts, Windows,
|
||||||
},
|
},
|
||||||
prices,
|
prices,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use derive_more::{Deref, DerefMut};
|
|||||||
use vecdb::UnaryTransform;
|
use vecdb::UnaryTransform;
|
||||||
|
|
||||||
use crate::internal::{
|
use crate::internal::{
|
||||||
ValuePerBlock, Identity, LazyValue, LazyValueDerivedResolutions, SatsToBitcoin,
|
Identity, LazyValue, LazyValueDerivedResolutions, SatsToBitcoin, ValuePerBlock,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Lazy value wrapper with height + all derived last transforms from ValuePerBlock.
|
/// Lazy value wrapper with height + all derived last transforms from ValuePerBlock.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use brk_traversable::Traversable;
|
|||||||
use brk_types::{Bitcoin, Cents, Dollars, Sats, Version};
|
use brk_types::{Bitcoin, Cents, Dollars, Sats, Version};
|
||||||
use vecdb::UnaryTransform;
|
use vecdb::UnaryTransform;
|
||||||
|
|
||||||
use crate::internal::{ValuePerBlock, DerivedResolutions};
|
use crate::internal::{DerivedResolutions, ValuePerBlock};
|
||||||
|
|
||||||
#[derive(Clone, Traversable)]
|
#[derive(Clone, Traversable)]
|
||||||
pub struct LazyValueDerivedResolutions {
|
pub struct LazyValueDerivedResolutions {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode};
|
|||||||
use crate::{
|
use crate::{
|
||||||
indexes,
|
indexes,
|
||||||
internal::{
|
internal::{
|
||||||
ValuePerBlock, DistributionStats, WindowStarts, Windows,
|
DistributionStats, ValuePerBlock, WindowStarts, Windows,
|
||||||
algo::compute_rolling_distribution_from_starts,
|
algo::compute_rolling_distribution_from_starts,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ use brk_indexer::Indexer;
|
|||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
use brk_types::{Indexes, TxIndex, VSize};
|
use brk_types::{Indexes, TxIndex, VSize};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use vecdb::{Database, EagerVec, Exit, ImportableVec, PcoVec, ReadableVec, Rw, StorageMode, Version};
|
use vecdb::{
|
||||||
|
Database, EagerVec, Exit, ImportableVec, PcoVec, ReadableVec, Rw, StorageMode, Version,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
indexes,
|
indexes,
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use vecdb::{AnyStoredVec, AnyVec, Database, EagerVec, Exit, PcoVec, WritableVec}
|
|||||||
use crate::{indexes, prices};
|
use crate::{indexes, prices};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
ValuePerBlock, BpsType, NumericValue, PerBlock, PerBlockCumulativeRolling, PercentPerBlock,
|
BpsType, NumericValue, PerBlock, PerBlockCumulativeRolling, PercentPerBlock, ValuePerBlock,
|
||||||
WindowStartVec, Windows,
|
WindowStartVec, Windows,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -83,11 +83,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Compute `all.height` as the per-block sum of the per-type vecs.
|
/// Compute `all.height` as the per-block sum of the per-type vecs.
|
||||||
pub(crate) fn compute_rest(
|
pub(crate) fn compute_rest(&mut self, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
|
||||||
&mut self,
|
|
||||||
starting_indexes: &Indexes,
|
|
||||||
exit: &Exit,
|
|
||||||
) -> Result<()> {
|
|
||||||
let sources: Vec<&EagerVec<PcoVec<Height, T>>> =
|
let sources: Vec<&EagerVec<PcoVec<Height, T>>> =
|
||||||
self.by_addr_type.values().map(|v| &v.height).collect();
|
self.by_addr_type.values().map(|v| &v.height).collect();
|
||||||
self.all
|
self.all
|
||||||
@@ -109,13 +105,8 @@ where
|
|||||||
indexes: &indexes::Vecs,
|
indexes: &indexes::Vecs,
|
||||||
cached_starts: &Windows<&WindowStartVec>,
|
cached_starts: &Windows<&WindowStartVec>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let all = PerBlockCumulativeRolling::forced_import(
|
let all =
|
||||||
db,
|
PerBlockCumulativeRolling::forced_import(db, name, version, indexes, cached_starts)?;
|
||||||
name,
|
|
||||||
version,
|
|
||||||
indexes,
|
|
||||||
cached_starts,
|
|
||||||
)?;
|
|
||||||
let by_addr_type = ByAddrType::new_with_name(|type_name| {
|
let by_addr_type = ByAddrType::new_with_name(|type_name| {
|
||||||
PerBlockCumulativeRolling::forced_import(
|
PerBlockCumulativeRolling::forced_import(
|
||||||
db,
|
db,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use super::{ByDcaCagr, ByDcaClass, ByDcaPeriod, Vecs};
|
|||||||
use crate::{
|
use crate::{
|
||||||
indexes,
|
indexes,
|
||||||
internal::{
|
internal::{
|
||||||
ValuePerBlock, PercentPerBlock, Price,
|
PercentPerBlock, Price, ValuePerBlock,
|
||||||
db_utils::{finalize_db, open_db},
|
db_utils::{finalize_db, open_db},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use brk_types::{BasisPointsSigned32, Cents, Height, Sats};
|
|||||||
use vecdb::{Database, EagerVec, PcoVec, Rw, StorageMode};
|
use vecdb::{Database, EagerVec, PcoVec, Rw, StorageMode};
|
||||||
|
|
||||||
use super::{ByDcaCagr, ByDcaClass, ByDcaPeriod};
|
use super::{ByDcaCagr, ByDcaClass, ByDcaPeriod};
|
||||||
use crate::internal::{ValuePerBlock, PerBlock, PercentPerBlock, Price};
|
use crate::internal::{PerBlock, PercentPerBlock, Price, ValuePerBlock};
|
||||||
|
|
||||||
#[derive(Traversable)]
|
#[derive(Traversable)]
|
||||||
pub struct PeriodVecs<M: StorageMode = Rw> {
|
pub struct PeriodVecs<M: StorageMode = Rw> {
|
||||||
|
|||||||
@@ -87,9 +87,8 @@ impl Computer {
|
|||||||
|
|
||||||
let cached_starts = blocks.lookback.cached_window_starts();
|
let cached_starts = blocks.lookback.cached_window_starts();
|
||||||
|
|
||||||
let (inputs, outputs, mining, transactions, pools, cointime) = timed(
|
let (inputs, outputs, mining, transactions, pools, cointime) =
|
||||||
"Imported inputs/outputs/mining/tx/pools/cointime",
|
timed("Imported inputs/outputs/mining/tx/pools/cointime", || {
|
||||||
|| {
|
|
||||||
thread::scope(|s| -> Result<_> {
|
thread::scope(|s| -> Result<_> {
|
||||||
let inputs_handle = big_thread().spawn_scoped(s, || -> Result<_> {
|
let inputs_handle = big_thread().spawn_scoped(s, || -> Result<_> {
|
||||||
Ok(Box::new(inputs::Vecs::forced_import(
|
Ok(Box::new(inputs::Vecs::forced_import(
|
||||||
@@ -152,8 +151,7 @@ impl Computer {
|
|||||||
|
|
||||||
Ok((inputs, outputs, mining, transactions, pools, cointime))
|
Ok((inputs, outputs, mining, transactions, pools, cointime))
|
||||||
})
|
})
|
||||||
},
|
})?;
|
||||||
)?;
|
|
||||||
|
|
||||||
// Market, indicators, and distribution are independent; import in parallel.
|
// Market, indicators, and distribution are independent; import in parallel.
|
||||||
// Supply depends on distribution so it runs after.
|
// Supply depends on distribution so it runs after.
|
||||||
@@ -271,9 +269,8 @@ impl Computer {
|
|||||||
{
|
{
|
||||||
info!("Removing obsolete database folder: {}", name);
|
info!("Removing obsolete database folder: {}", name);
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
fs::remove_dir_all(&path).map_err(|e| {
|
fs::remove_dir_all(&path)
|
||||||
std::io::Error::other(format!("remove_dir_all {path:?}: {e}"))
|
.map_err(|e| std::io::Error::other(format!("remove_dir_all {path:?}: {e}")))?;
|
||||||
})?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ use super::Vecs;
|
|||||||
use crate::{
|
use crate::{
|
||||||
indexes,
|
indexes,
|
||||||
internal::{
|
internal::{
|
||||||
ValuePerBlockCumulative, ValuePerBlockCumulativeRolling, ValuePerBlockFull,
|
|
||||||
LazyPercentCumulativeRolling, OneMinusBp16, PercentCumulativeRolling, RatioRollingWindows,
|
LazyPercentCumulativeRolling, OneMinusBp16, PercentCumulativeRolling, RatioRollingWindows,
|
||||||
WindowStartVec, Windows,
|
ValuePerBlockCumulative, ValuePerBlockCumulativeRolling, ValuePerBlockFull, WindowStartVec,
|
||||||
|
Windows,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ use brk_types::{BasisPoints16, BasisPoints32, Height, Sats};
|
|||||||
use vecdb::{EagerVec, PcoVec, Rw, StorageMode};
|
use vecdb::{EagerVec, PcoVec, Rw, StorageMode};
|
||||||
|
|
||||||
use crate::internal::{
|
use crate::internal::{
|
||||||
ValuePerBlockCumulative, ValuePerBlockCumulativeRolling, ValuePerBlockFull,
|
|
||||||
LazyPercentCumulativeRolling, PercentCumulativeRolling, RatioRollingWindows,
|
LazyPercentCumulativeRolling, PercentCumulativeRolling, RatioRollingWindows,
|
||||||
|
ValuePerBlockCumulative, ValuePerBlockCumulativeRolling, ValuePerBlockFull,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Traversable)]
|
#[derive(Traversable)]
|
||||||
|
|||||||
@@ -68,10 +68,14 @@ impl Vecs {
|
|||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
|agg| {
|
|agg| {
|
||||||
push_block(&mut self.output_count, agg.entries_all, &agg.entries_per_type);
|
push_block(
|
||||||
|
&mut self.output_count,
|
||||||
|
agg.entries_all,
|
||||||
|
&agg.entries_per_type,
|
||||||
|
);
|
||||||
push_block(&mut self.tx_count, agg.txs_all, &agg.txs_per_type);
|
push_block(&mut self.tx_count, agg.txs_all, &agg.txs_per_type);
|
||||||
let spendable_total = agg.entries_all
|
let spendable_total =
|
||||||
- agg.entries_per_type[OutputType::OpReturn as usize];
|
agg.entries_all - agg.entries_per_type[OutputType::OpReturn as usize];
|
||||||
self.spendable_output_count
|
self.spendable_output_count
|
||||||
.block
|
.block
|
||||||
.push(StoredU64::from(spendable_total));
|
.push(StoredU64::from(spendable_total));
|
||||||
@@ -97,8 +101,7 @@ impl Vecs {
|
|||||||
.compute_rest(starting_indexes.height, exit)?;
|
.compute_rest(starting_indexes.height, exit)?;
|
||||||
self.spendable_output_count
|
self.spendable_output_count
|
||||||
.compute_rest(starting_indexes.height, exit)?;
|
.compute_rest(starting_indexes.height, exit)?;
|
||||||
self.tx_count
|
self.tx_count.compute_rest(starting_indexes.height, exit)?;
|
||||||
.compute_rest(starting_indexes.height, exit)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (otype, source) in self.output_count.by_type.iter_typed() {
|
for (otype, source) in self.output_count.by_type.iter_typed() {
|
||||||
|
|||||||
@@ -16,26 +16,24 @@ impl Vecs {
|
|||||||
indexes: &indexes::Vecs,
|
indexes: &indexes::Vecs,
|
||||||
cached_starts: &Windows<&WindowStartVec>,
|
cached_starts: &Windows<&WindowStartVec>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let output_count = WithOutputTypes::<
|
let output_count =
|
||||||
PerBlockCumulativeRolling<StoredU64, StoredU64>,
|
WithOutputTypes::<PerBlockCumulativeRolling<StoredU64, StoredU64>>::forced_import_with(
|
||||||
>::forced_import_with(
|
db,
|
||||||
db,
|
"output_count_bis",
|
||||||
"output_count_bis",
|
|t| format!("{t}_output_count"),
|
||||||
|t| format!("{t}_output_count"),
|
version,
|
||||||
version,
|
indexes,
|
||||||
indexes,
|
cached_starts,
|
||||||
cached_starts,
|
)?;
|
||||||
)?;
|
let tx_count =
|
||||||
let tx_count = WithOutputTypes::<
|
WithOutputTypes::<PerBlockCumulativeRolling<StoredU64, StoredU64>>::forced_import_with(
|
||||||
PerBlockCumulativeRolling<StoredU64, StoredU64>,
|
db,
|
||||||
>::forced_import_with(
|
"tx_count_bis",
|
||||||
db,
|
|t| format!("tx_count_with_{t}_output"),
|
||||||
"tx_count_bis",
|
version,
|
||||||
|t| format!("tx_count_with_{t}_output"),
|
indexes,
|
||||||
version,
|
cached_starts,
|
||||||
indexes,
|
)?;
|
||||||
cached_starts,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let spendable_output_count = PerBlockCumulativeRolling::forced_import(
|
let spendable_output_count = PerBlockCumulativeRolling::forced_import(
|
||||||
db,
|
db,
|
||||||
|
|||||||
@@ -67,7 +67,9 @@ where
|
|||||||
dep_version: Version,
|
dep_version: Version,
|
||||||
at_height: Height,
|
at_height: Height,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.all.block.validate_and_truncate(dep_version, at_height)?;
|
self.all
|
||||||
|
.block
|
||||||
|
.validate_and_truncate(dep_version, at_height)?;
|
||||||
for v in self.by_type.iter_mut() {
|
for v in self.by_type.iter_mut() {
|
||||||
v.block.validate_and_truncate(dep_version, at_height)?;
|
v.block.validate_and_truncate(dep_version, at_height)?;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,8 +22,7 @@ impl Vecs {
|
|||||||
|
|
||||||
self.count
|
self.count
|
||||||
.compute(indexer, indexes, blocks, starting_indexes, exit)?;
|
.compute(indexer, indexes, blocks, starting_indexes, exit)?;
|
||||||
self.per_sec
|
self.per_sec.compute(&self.count, starting_indexes, exit)?;
|
||||||
.compute(&self.count, starting_indexes, exit)?;
|
|
||||||
self.value
|
self.value
|
||||||
.compute(indexer, prices, starting_indexes, exit)?;
|
.compute(indexer, prices, starting_indexes, exit)?;
|
||||||
self.by_type.compute(indexer, starting_indexes, exit)?;
|
self.by_type.compute(indexer, starting_indexes, exit)?;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use vecdb::{BinaryTransform, Database, Exit, ReadableVec, Rw, StorageMode, Versi
|
|||||||
use crate::{
|
use crate::{
|
||||||
blocks, indexes,
|
blocks, indexes,
|
||||||
internal::{
|
internal::{
|
||||||
ValuePerBlockCumulativeRolling, MaskSats, PercentRollingWindows, RatioU64Bp16,
|
MaskSats, PercentRollingWindows, RatioU64Bp16, ValuePerBlockCumulativeRolling,
|
||||||
WindowStartVec, Windows,
|
WindowStartVec, Windows,
|
||||||
},
|
},
|
||||||
mining, prices,
|
mining, prices,
|
||||||
|
|||||||
@@ -14,9 +14,7 @@ impl PoolHeights {
|
|||||||
let mut map: FxHashMap<PoolSlug, Vec<Height>> = FxHashMap::default();
|
let mut map: FxHashMap<PoolSlug, Vec<Height>> = FxHashMap::default();
|
||||||
let reader = pool.reader();
|
let reader = pool.reader();
|
||||||
for h in 0..len {
|
for h in 0..len {
|
||||||
map.entry(reader.get(h))
|
map.entry(reader.get(h)).or_default().push(Height::from(h));
|
||||||
.or_default()
|
|
||||||
.push(Height::from(h));
|
|
||||||
}
|
}
|
||||||
Self(Arc::new(RwLock::new(map)))
|
Self(Arc::new(RwLock::new(map)))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,7 +84,11 @@ impl Vecs {
|
|||||||
.height
|
.height
|
||||||
.len()
|
.len()
|
||||||
.min(starting_indexes.height.to_usize());
|
.min(starting_indexes.height.to_usize());
|
||||||
self.spot.cents.height.inner.truncate_if_needed_at(truncate_to)?;
|
self.spot
|
||||||
|
.cents
|
||||||
|
.height
|
||||||
|
.inner
|
||||||
|
.truncate_if_needed_at(truncate_to)?;
|
||||||
|
|
||||||
if self.spot.cents.height.len() < START_HEIGHT {
|
if self.spot.cents.height.len() < START_HEIGHT {
|
||||||
for line in brk_oracle::PRICES
|
for line in brk_oracle::PRICES
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use brk_types::Version;
|
|||||||
use crate::{
|
use crate::{
|
||||||
cointime, distribution, indexes,
|
cointime, distribution, indexes,
|
||||||
internal::{
|
internal::{
|
||||||
LazyValuePerBlock, LazyFiatPerBlock, LazyRollingDeltasFiatFromHeight, PercentPerBlock,
|
LazyFiatPerBlock, LazyRollingDeltasFiatFromHeight, LazyValuePerBlock, PercentPerBlock,
|
||||||
RollingWindows, WindowStartVec, Windows,
|
RollingWindows, WindowStartVec, Windows,
|
||||||
db_utils::{finalize_db, open_db},
|
db_utils::{finalize_db, open_db},
|
||||||
},
|
},
|
||||||
@@ -63,11 +63,8 @@ impl Vecs {
|
|||||||
indexes,
|
indexes,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let hodled_or_lost = LazyValuePerBlock::identity(
|
let hodled_or_lost =
|
||||||
"hodled_or_lost_supply",
|
LazyValuePerBlock::identity("hodled_or_lost_supply", &cointime.supply.vaulted, version);
|
||||||
&cointime.supply.vaulted,
|
|
||||||
version,
|
|
||||||
);
|
|
||||||
|
|
||||||
let this = Self {
|
let this = Self {
|
||||||
db,
|
db,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use vecdb::{Database, Rw, StorageMode};
|
|||||||
|
|
||||||
use super::{burned, velocity};
|
use super::{burned, velocity};
|
||||||
use crate::internal::{
|
use crate::internal::{
|
||||||
LazyValuePerBlock, LazyFiatPerBlock, LazyRollingDeltasFiatFromHeight, PercentPerBlock,
|
LazyFiatPerBlock, LazyRollingDeltasFiatFromHeight, LazyValuePerBlock, PercentPerBlock,
|
||||||
RollingWindows,
|
RollingWindows,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use vecdb::Database;
|
|||||||
use super::Vecs;
|
use super::Vecs;
|
||||||
use crate::{
|
use crate::{
|
||||||
indexes,
|
indexes,
|
||||||
internal::{ValuePerBlockCumulativeRolling, PerBlock, WindowStartVec, Windows},
|
internal::{PerBlock, ValuePerBlockCumulativeRolling, WindowStartVec, Windows},
|
||||||
};
|
};
|
||||||
|
|
||||||
impl Vecs {
|
impl Vecs {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use brk_traversable::Traversable;
|
|||||||
use brk_types::StoredF32;
|
use brk_types::StoredF32;
|
||||||
use vecdb::{Rw, StorageMode};
|
use vecdb::{Rw, StorageMode};
|
||||||
|
|
||||||
use crate::internal::{ValuePerBlockCumulativeRolling, PerBlock, Windows};
|
use crate::internal::{PerBlock, ValuePerBlockCumulativeRolling, Windows};
|
||||||
|
|
||||||
#[derive(Traversable)]
|
#[derive(Traversable)]
|
||||||
pub struct Vecs<M: StorageMode = Rw> {
|
pub struct Vecs<M: StorageMode = Rw> {
|
||||||
|
|||||||
@@ -62,10 +62,7 @@ impl Fetcher {
|
|||||||
|
|
||||||
/// Parent txids referenced by `new_raws` inputs that aren't already
|
/// Parent txids referenced by `new_raws` inputs that aren't already
|
||||||
/// resolvable: not in the mempool store, not in `new_raws` itself.
|
/// resolvable: not in the mempool store, not in `new_raws` itself.
|
||||||
fn unique_confirmed_parents(
|
fn unique_confirmed_parents(new_raws: &FxHashMap<Txid, RawTx>, known: &TxStore) -> Vec<Txid> {
|
||||||
new_raws: &FxHashMap<Txid, RawTx>,
|
|
||||||
known: &TxStore,
|
|
||||||
) -> Vec<Txid> {
|
|
||||||
let mut set: FxHashSet<Txid> = FxHashSet::default();
|
let mut set: FxHashSet<Txid> = FxHashSet::default();
|
||||||
for raw in new_raws.values() {
|
for raw in new_raws.values() {
|
||||||
for txin in &raw.tx.input {
|
for txin in &raw.tx.input {
|
||||||
|
|||||||
@@ -37,12 +37,9 @@ fn synthetic_mempool(n: usize) -> Vec<Option<Entry>> {
|
|||||||
_ if i > 1 => {
|
_ if i > 1 => {
|
||||||
let p1 = (i.wrapping_mul(7919)) % i;
|
let p1 = (i.wrapping_mul(7919)) % i;
|
||||||
let p2 = (i.wrapping_mul(6151)) % i;
|
let p2 = (i.wrapping_mul(6151)) % i;
|
||||||
[
|
[TxidPrefix::from(&txids[p1]), TxidPrefix::from(&txids[p2])]
|
||||||
TxidPrefix::from(&txids[p1]),
|
.into_iter()
|
||||||
TxidPrefix::from(&txids[p2]),
|
.collect()
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
_ => SmallVec::new(),
|
_ => SmallVec::new(),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -52,7 +52,12 @@ pub fn linearize_clusters(graph: &Graph) -> Vec<Package> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (chunk_order, chunk) in sfl::linearize(&cluster).iter().enumerate() {
|
for (chunk_order, chunk) in sfl::linearize(&cluster).iter().enumerate() {
|
||||||
packages.push(chunk_to_package(&cluster, chunk, cluster_id, chunk_order as u32));
|
packages.push(chunk_to_package(
|
||||||
|
&cluster,
|
||||||
|
chunk,
|
||||||
|
cluster_id,
|
||||||
|
chunk_order as u32,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -327,7 +327,10 @@ fn random_dag(n: usize, seed: u64) -> FvAndEdges {
|
|||||||
(fees_vsizes, edges)
|
(fees_vsizes, edges)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(dead_code, reason = "kept for ad-hoc oracle sweeps; called via uncommented stress tests")]
|
#[expect(
|
||||||
|
dead_code,
|
||||||
|
reason = "kept for ad-hoc oracle sweeps; called via uncommented stress tests"
|
||||||
|
)]
|
||||||
fn assert_optimal_on_random(n: usize, seed: u64) {
|
fn assert_optimal_on_random(n: usize, seed: u64) {
|
||||||
let (fv, edges) = random_dag(n, seed);
|
let (fv, edges) = random_dag(n, seed);
|
||||||
let cluster = super::make_cluster(&fv, &edges);
|
let cluster = super::make_cluster(&fv, &edges);
|
||||||
@@ -376,7 +379,11 @@ fn optimality_gap_of(got: &[(u64, u64)], want: &[(u64, u64)]) -> Option<u128> {
|
|||||||
worst_gap = worst_gap.max(fb - fa);
|
worst_gap = worst_gap.max(fb - fa);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if worst_gap == 0 { None } else { Some(worst_gap) }
|
if worst_gap == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(worst_gap)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gap for the production linearizer on one random DAG.
|
/// Gap for the production linearizer on one random DAG.
|
||||||
@@ -479,10 +486,7 @@ fn perf_linearize() {
|
|||||||
} else {
|
} else {
|
||||||
format!("{} ns", avg_ns)
|
format!("{} ns", avg_ns)
|
||||||
};
|
};
|
||||||
eprintln!(
|
eprintln!(" {:<4} {:<8} {:<10} {:.2?}", n, calls, pretty, elapsed);
|
||||||
" {:<4} {:<8} {:<10} {:.2?}",
|
|
||||||
n, calls, pretty, elapsed
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
eprintln!();
|
eprintln!();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,10 +15,7 @@ const LOOK_AHEAD_COUNT: usize = 100;
|
|||||||
/// Look-ahead respects intra-cluster order: a chunk is only taken once
|
/// Look-ahead respects intra-cluster order: a chunk is only taken once
|
||||||
/// every earlier-rate chunk of the same cluster has been placed, so a
|
/// every earlier-rate chunk of the same cluster has been placed, so a
|
||||||
/// child chunk never lands in an earlier block than its parent chunk.
|
/// child chunk never lands in an earlier block than its parent chunk.
|
||||||
pub fn partition_into_blocks(
|
pub fn partition_into_blocks(mut packages: Vec<Package>, num_blocks: usize) -> Vec<Vec<Package>> {
|
||||||
mut packages: Vec<Package>,
|
|
||||||
num_blocks: usize,
|
|
||||||
) -> Vec<Vec<Package>> {
|
|
||||||
// Stable sort preserves SFL's per-cluster non-increasing-rate emission
|
// Stable sort preserves SFL's per-cluster non-increasing-rate emission
|
||||||
// order in the global list, which is what `cluster_next` relies on.
|
// order in the global list, which is what `cluster_next` relies on.
|
||||||
packages.sort_by_key(|p| Reverse(p.fee_rate));
|
packages.sort_by_key(|p| Reverse(p.fee_rate));
|
||||||
@@ -67,7 +64,13 @@ fn fill_normal_blocks(
|
|||||||
let remaining_space = BLOCK_VSIZE.saturating_sub(current_vsize);
|
let remaining_space = BLOCK_VSIZE.saturating_sub(current_vsize);
|
||||||
|
|
||||||
if pkg.vsize <= remaining_space {
|
if pkg.vsize <= remaining_space {
|
||||||
take(slots, idx, &mut current_block, &mut current_vsize, cluster_next);
|
take(
|
||||||
|
slots,
|
||||||
|
idx,
|
||||||
|
&mut current_block,
|
||||||
|
&mut current_vsize,
|
||||||
|
cluster_next,
|
||||||
|
);
|
||||||
idx += 1;
|
idx += 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -75,7 +78,13 @@ fn fill_normal_blocks(
|
|||||||
if current_block.is_empty() {
|
if current_block.is_empty() {
|
||||||
// Oversized package with no partial block to preserve; take it
|
// Oversized package with no partial block to preserve; take it
|
||||||
// anyway so we don't stall on a package larger than BLOCK_VSIZE.
|
// anyway so we don't stall on a package larger than BLOCK_VSIZE.
|
||||||
take(slots, idx, &mut current_block, &mut current_vsize, cluster_next);
|
take(
|
||||||
|
slots,
|
||||||
|
idx,
|
||||||
|
&mut current_block,
|
||||||
|
&mut current_vsize,
|
||||||
|
cluster_next,
|
||||||
|
);
|
||||||
idx += 1;
|
idx += 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,12 +45,7 @@ impl Verifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn live_entry(
|
fn live_entry(entries: &[Option<Entry>], tx_index: TxIndex, b: usize, p: usize) -> &Entry {
|
||||||
entries: &[Option<Entry>],
|
|
||||||
tx_index: TxIndex,
|
|
||||||
b: usize,
|
|
||||||
p: usize,
|
|
||||||
) -> &Entry {
|
|
||||||
entries[tx_index.as_usize()]
|
entries[tx_index.as_usize()]
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap_or_else(|| panic!("block {b} pkg {p}: dead tx_index {tx_index:?}"))
|
.unwrap_or_else(|| panic!("block {b} pkg {p}: dead tx_index {tx_index:?}"))
|
||||||
@@ -65,10 +60,7 @@ impl Verifier {
|
|||||||
) {
|
) {
|
||||||
for parent in &entry.depends {
|
for parent in &entry.depends {
|
||||||
if in_pool.contains(parent) && !placed.contains(parent) {
|
if in_pool.contains(parent) && !placed.contains(parent) {
|
||||||
panic!(
|
panic!("block {b} pkg {p}: {} placed before its parent", entry.txid);
|
||||||
"block {b} pkg {p}: {} placed before its parent",
|
|
||||||
entry.txid
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,10 @@ impl EntryPool {
|
|||||||
/// Remove an entry by its txid prefix, returning it if present.
|
/// Remove an entry by its txid prefix, returning it if present.
|
||||||
pub fn remove(&mut self, prefix: &TxidPrefix) -> Option<Entry> {
|
pub fn remove(&mut self, prefix: &TxidPrefix) -> Option<Entry> {
|
||||||
let idx = self.prefix_to_idx.remove(prefix)?;
|
let idx = self.prefix_to_idx.remove(prefix)?;
|
||||||
let entry = self.entries.get_mut(idx.as_usize()).and_then(Option::take)?;
|
let entry = self
|
||||||
|
.entries
|
||||||
|
.get_mut(idx.as_usize())
|
||||||
|
.and_then(Option::take)?;
|
||||||
self.free_slots.push(idx);
|
self.free_slots.push(idx);
|
||||||
Some(entry)
|
Some(entry)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,12 @@ pub struct Tombstone {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Tombstone {
|
impl Tombstone {
|
||||||
pub(super) fn new(tx: Transaction, entry: Entry, removal: Removal, removed_at: Instant) -> Self {
|
pub(super) fn new(
|
||||||
|
tx: Transaction,
|
||||||
|
entry: Entry,
|
||||||
|
removal: Removal,
|
||||||
|
removed_at: Instant,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
tx,
|
tx,
|
||||||
entry,
|
entry,
|
||||||
|
|||||||
@@ -36,9 +36,9 @@ impl TxGraveyard {
|
|||||||
&'a self,
|
&'a self,
|
||||||
replacer: &'a Txid,
|
replacer: &'a Txid,
|
||||||
) -> impl Iterator<Item = (&'a Txid, &'a Tombstone)> {
|
) -> impl Iterator<Item = (&'a Txid, &'a Tombstone)> {
|
||||||
self.tombstones
|
self.tombstones.iter().filter_map(move |(txid, ts)| {
|
||||||
.iter()
|
(ts.replaced_by() == Some(replacer)).then_some((txid, ts))
|
||||||
.filter_map(move |(txid, ts)| (ts.replaced_by() == Some(replacer)).then_some((txid, ts)))
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bury(&mut self, txid: Txid, tx: Transaction, entry: Entry, removal: Removal) {
|
pub fn bury(&mut self, txid: Txid, tx: Transaction, entry: Entry, removal: Removal) {
|
||||||
|
|||||||
@@ -85,9 +85,9 @@ impl Query {
|
|||||||
tx_count: addr_data.tx_count,
|
tx_count: addr_data.tx_count,
|
||||||
realized_price,
|
realized_price,
|
||||||
},
|
},
|
||||||
mempool_stats: self.mempool().and_then(|m| {
|
mempool_stats: self
|
||||||
m.addrs().get(&bytes).map(|(stats, _)| stats.clone())
|
.mempool()
|
||||||
}),
|
.and_then(|m| m.addrs().get(&bytes).map(|(stats, _)| stats.clone())),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,12 +57,7 @@ impl Query {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let height = Height::from(best_height);
|
let height = Height::from(best_height);
|
||||||
let blockhash = indexer
|
let blockhash = indexer.vecs.blocks.blockhash.collect_one(height).data()?;
|
||||||
.vecs
|
|
||||||
.blocks
|
|
||||||
.blockhash
|
|
||||||
.collect_one(height)
|
|
||||||
.data()?;
|
|
||||||
|
|
||||||
// Convert timestamp to ISO 8601 format
|
// Convert timestamp to ISO 8601 format
|
||||||
let ts_secs: i64 = (*best_ts).into();
|
let ts_secs: i64 = (*best_ts).into();
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ use std::cmp::Ordering;
|
|||||||
use brk_error::{Error, Result};
|
use brk_error::{Error, Result};
|
||||||
use brk_mempool::{Entry, EntryPool, Removal, Tombstone, TxGraveyard, TxStore};
|
use brk_mempool::{Entry, EntryPool, Removal, Tombstone, TxGraveyard, TxStore};
|
||||||
use brk_types::{
|
use brk_types::{
|
||||||
CheckedSub, CpfpEntry, CpfpInfo, FeeRate, MempoolBlock, MempoolInfo, MempoolRecentTx, OutputType,
|
CheckedSub, CpfpEntry, CpfpInfo, FeeRate, MempoolBlock, MempoolInfo, MempoolRecentTx,
|
||||||
RbfResponse, RbfTx, RecommendedFees, ReplacementNode, Sats, Timestamp, Transaction, TxOut,
|
OutputType, RbfResponse, RbfTx, RecommendedFees, ReplacementNode, Sats, Timestamp, Transaction,
|
||||||
TxOutIndex, Txid, TxidPrefix, TypeIndex, VSize, Weight,
|
TxOut, TxOutIndex, Txid, TxidPrefix, TypeIndex, VSize, Weight,
|
||||||
};
|
};
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
use vecdb::VecIndex;
|
use vecdb::VecIndex;
|
||||||
@@ -178,7 +178,8 @@ impl Query {
|
|||||||
let graveyard = mempool.graveyard();
|
let graveyard = mempool.graveyard();
|
||||||
|
|
||||||
let mut root_txid = txid.clone();
|
let mut root_txid = txid.clone();
|
||||||
while let Some(Removal::Replaced { by }) = graveyard.get(&root_txid).map(Tombstone::reason) {
|
while let Some(Removal::Replaced { by }) = graveyard.get(&root_txid).map(Tombstone::reason)
|
||||||
|
{
|
||||||
root_txid = by.clone();
|
root_txid = by.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,8 +189,8 @@ impl Query {
|
|||||||
.collect();
|
.collect();
|
||||||
let replaces = (!replaces_vec.is_empty()).then_some(replaces_vec);
|
let replaces = (!replaces_vec.is_empty()).then_some(replaces_vec);
|
||||||
|
|
||||||
let replacements = Self::build_rbf_node(&root_txid, None, &txs, &entries, &graveyard)
|
let replacements =
|
||||||
.map(|mut node| {
|
Self::build_rbf_node(&root_txid, None, &txs, &entries, &graveyard).map(|mut node| {
|
||||||
node.tx.full_rbf = Some(node.full_rbf);
|
node.tx.full_rbf = Some(node.full_rbf);
|
||||||
node.interval = None;
|
node.interval = None;
|
||||||
node
|
node
|
||||||
@@ -210,14 +211,10 @@ impl Query {
|
|||||||
entries: &'a EntryPool,
|
entries: &'a EntryPool,
|
||||||
graveyard: &'a TxGraveyard,
|
graveyard: &'a TxGraveyard,
|
||||||
) -> Option<(&'a Transaction, &'a Entry)> {
|
) -> Option<(&'a Transaction, &'a Entry)> {
|
||||||
if let (Some(tx), Some(entry)) =
|
if let (Some(tx), Some(entry)) = (txs.get(txid), entries.get(&TxidPrefix::from(txid))) {
|
||||||
(txs.get(txid), entries.get(&TxidPrefix::from(txid)))
|
|
||||||
{
|
|
||||||
return Some((tx, entry));
|
return Some((tx, entry));
|
||||||
}
|
}
|
||||||
graveyard
|
graveyard.get(txid).map(|tomb| (&tomb.tx, &tomb.entry))
|
||||||
.get(txid)
|
|
||||||
.map(|tomb| (&tomb.tx, &tomb.entry))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Recursively build an RBF tree node rooted at `txid`.
|
/// Recursively build an RBF tree node rooted at `txid`.
|
||||||
|
|||||||
@@ -78,7 +78,9 @@ impl BlockWindow {
|
|||||||
.collect_range_at(self.start, self.end);
|
.collect_range_at(self.start, self.end);
|
||||||
let all_prices: Vec<Cents> = computer
|
let all_prices: Vec<Cents> = computer
|
||||||
.prices
|
.prices
|
||||||
.spot.cents.height
|
.spot
|
||||||
|
.cents
|
||||||
|
.height
|
||||||
.collect_range_at(self.start, self.end);
|
.collect_range_at(self.start, self.end);
|
||||||
let read_start = self.start.saturating_sub(1);
|
let read_start = self.start.saturating_sub(1);
|
||||||
let all_cum = cumulative.collect_range_at(read_start, self.end);
|
let all_cum = cumulative.collect_range_at(read_start, self.end);
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ use std::{collections::BTreeMap, sync::LazyLock};
|
|||||||
use brk_error::{Error, Result};
|
use brk_error::{Error, Result};
|
||||||
use brk_traversable::TreeNode;
|
use brk_traversable::TreeNode;
|
||||||
use brk_types::{
|
use brk_types::{
|
||||||
BlockHashPrefix, Date, DetailedSeriesCount, Epoch, Format, Halving, Height, Index,
|
BlockHashPrefix, Date, DetailedSeriesCount, Epoch, Format, Halving, Height, Index, IndexInfo,
|
||||||
IndexInfo, LegacyValue, Limit, Output, OutputLegacy, PaginatedSeries, Pagination,
|
LegacyValue, Limit, Output, OutputLegacy, PaginatedSeries, Pagination, PaginationIndex,
|
||||||
PaginationIndex, RangeIndex, RangeMap, SearchQuery, SeriesData, SeriesInfo, SeriesName,
|
RangeIndex, RangeMap, SearchQuery, SeriesData, SeriesInfo, SeriesName, SeriesOutput,
|
||||||
SeriesOutput, SeriesOutputLegacy, SeriesSelection, Timestamp, Version,
|
SeriesOutputLegacy, SeriesSelection, Timestamp, Version,
|
||||||
};
|
};
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use vecdb::{AnyExportableVec, ReadableVec};
|
use vecdb::{AnyExportableVec, ReadableVec};
|
||||||
|
|||||||
@@ -190,16 +190,15 @@ impl Query {
|
|||||||
let spending_txid = txid_reader.get(spending_tx_index.to_usize());
|
let spending_txid = txid_reader.get(spending_tx_index.to_usize());
|
||||||
let spending_height: Height = tx_heights.get_shared(spending_tx_index).data()?;
|
let spending_height: Height = tx_heights.get_shared(spending_tx_index).data()?;
|
||||||
|
|
||||||
let (block_hash, block_time) =
|
let (block_hash, block_time) = if let Some((h, ref bh, bt)) = cached_status
|
||||||
if let Some((h, ref bh, bt)) = cached_status
|
&& h == spending_height
|
||||||
&& h == spending_height
|
{
|
||||||
{
|
(bh.clone(), bt)
|
||||||
(bh.clone(), bt)
|
} else {
|
||||||
} else {
|
let (bh, bt) = self.block_hash_and_time(spending_height)?;
|
||||||
let (bh, bt) = self.block_hash_and_time(spending_height)?;
|
cached_status = Some((spending_height, bh.clone(), bt));
|
||||||
cached_status = Some((spending_height, bh.clone(), bt));
|
(bh, bt)
|
||||||
(bh, bt)
|
};
|
||||||
};
|
|
||||||
|
|
||||||
outspends.push(TxOutspend {
|
outspends.push(TxOutspend {
|
||||||
spent: true,
|
spent: true,
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ use brk_types::Height;
|
|||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
BlkIndexToBlkPath, OUT_OF_ORDER_FILE_BACKOFF, XORBytes, XORIndex,
|
BlkIndexToBlkPath, OUT_OF_ORDER_FILE_BACKOFF, XORBytes, XORIndex, parse::HEADER_LEN,
|
||||||
parse::HEADER_LEN, scan::find_magic,
|
scan::find_magic,
|
||||||
};
|
};
|
||||||
|
|
||||||
const PROBE_BUF_LEN: usize = 4096;
|
const PROBE_BUF_LEN: usize = 4096;
|
||||||
|
|||||||
@@ -28,7 +28,9 @@ impl BlkIndexToBlkPath {
|
|||||||
let Some(file_name) = path.file_name().and_then(|n| n.to_str()) else {
|
let Some(file_name) = path.file_name().and_then(|n| n.to_str()) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
let Some(index_str) = file_name.strip_prefix(BLK).and_then(|s| s.strip_suffix(DOT_DAT))
|
let Some(index_str) = file_name
|
||||||
|
.strip_prefix(BLK)
|
||||||
|
.and_then(|s| s.strip_suffix(DOT_DAT))
|
||||||
else {
|
else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -47,8 +47,14 @@ pub(super) fn pipeline_forward(
|
|||||||
}
|
}
|
||||||
drop(parser_recv);
|
drop(parser_recv);
|
||||||
|
|
||||||
let read_result =
|
let read_result = read_and_dispatch(
|
||||||
read_and_dispatch(paths, first_blk_index, xor_bytes, canonical, &parser_send, &stop);
|
paths,
|
||||||
|
first_blk_index,
|
||||||
|
xor_bytes,
|
||||||
|
canonical,
|
||||||
|
&parser_send,
|
||||||
|
&stop,
|
||||||
|
);
|
||||||
drop(parser_send);
|
drop(parser_send);
|
||||||
read_result
|
read_result
|
||||||
})?;
|
})?;
|
||||||
@@ -125,8 +131,7 @@ fn read_and_dispatch(
|
|||||||
else {
|
else {
|
||||||
return ControlFlow::Continue(());
|
return ControlFlow::Continue(());
|
||||||
};
|
};
|
||||||
if !canonical
|
if !canonical.verify_prev(canonical_offset, &BlockHash::from(header.prev_blockhash))
|
||||||
.verify_prev(canonical_offset, &BlockHash::from(header.prev_blockhash))
|
|
||||||
{
|
{
|
||||||
let _ = stop.set(Stop::Failed(Error::Internal(
|
let _ = stop.set(Stop::Failed(Error::Internal(
|
||||||
"forward pipeline: canonical batch stitched across a reorg",
|
"forward pipeline: canonical batch stitched across a reorg",
|
||||||
|
|||||||
@@ -5,10 +5,7 @@ use brk_rpc::Client;
|
|||||||
use brk_types::{Height, ReadBlock};
|
use brk_types::{Height, ReadBlock};
|
||||||
use crossbeam::channel::{Receiver, bounded};
|
use crossbeam::channel::{Receiver, bounded};
|
||||||
|
|
||||||
use crate::{
|
use crate::{BlkIndexToBlkPath, ReaderInner, XORBytes, bisect, canonical::CanonicalRange};
|
||||||
BlkIndexToBlkPath, ReaderInner, XORBytes, bisect,
|
|
||||||
canonical::CanonicalRange,
|
|
||||||
};
|
|
||||||
|
|
||||||
mod forward;
|
mod forward;
|
||||||
mod reorder;
|
mod reorder;
|
||||||
|
|||||||
@@ -74,9 +74,7 @@ pub(super) fn pipeline_tail(
|
|||||||
if slots[offset as usize].is_some() {
|
if slots[offset as usize].is_some() {
|
||||||
return ControlFlow::Continue(());
|
return ControlFlow::Continue(());
|
||||||
}
|
}
|
||||||
if !canonical
|
if !canonical.verify_prev(offset, &BlockHash::from(header.prev_blockhash)) {
|
||||||
.verify_prev(offset, &BlockHash::from(header.prev_blockhash))
|
|
||||||
{
|
|
||||||
parse_failure = Some(Error::Internal(
|
parse_failure = Some(Error::Internal(
|
||||||
"tail pipeline: canonical batch stitched across a reorg",
|
"tail pipeline: canonical batch stitched across a reorg",
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
use std::{thread::sleep, time::Duration};
|
use std::{thread::sleep, time::Duration};
|
||||||
|
|
||||||
use brk_error::{Error, Result};
|
use brk_error::{Error, Result};
|
||||||
use corepc_jsonrpc::{
|
use corepc_jsonrpc::{Client as JsonRpcClient, Request, error::Error as JsonRpcError, simple_http};
|
||||||
Client as JsonRpcClient, Request, error::Error as JsonRpcError, simple_http,
|
|
||||||
};
|
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::{Value, value::RawValue};
|
use serde_json::{Value, value::RawValue};
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ use serde::Deserialize;
|
|||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use tracing::{debug, info};
|
use tracing::{debug, info};
|
||||||
|
|
||||||
use crate::{BlockHeaderInfo, BlockInfo, BlockTemplateTx, BlockchainInfo, Client, RawTx, TxOutInfo};
|
use crate::{
|
||||||
|
BlockHeaderInfo, BlockInfo, BlockTemplateTx, BlockchainInfo, Client, RawTx, TxOutInfo,
|
||||||
|
};
|
||||||
|
|
||||||
/// Per-batch request count for `get_block_hashes_range`. Sized so the
|
/// Per-batch request count for `get_block_hashes_range`. Sized so the
|
||||||
/// JSON request body stays well under a megabyte and bitcoind doesn't
|
/// JSON request body stays well under a megabyte and bitcoind doesn't
|
||||||
@@ -44,10 +46,9 @@ impl Client {
|
|||||||
&'a H: Into<&'a bitcoin::BlockHash>,
|
&'a H: Into<&'a bitcoin::BlockHash>,
|
||||||
{
|
{
|
||||||
let hash: &bitcoin::BlockHash = hash.into();
|
let hash: &bitcoin::BlockHash = hash.into();
|
||||||
let r: GetBlockVerboseZero = self.0.call_with_retry(
|
let r: GetBlockVerboseZero = self
|
||||||
"getblock",
|
.0
|
||||||
&[serde_json::to_value(hash)?, Value::from(0u8)],
|
.call_with_retry("getblock", &[serde_json::to_value(hash)?, Value::from(0u8)])?;
|
||||||
)?;
|
|
||||||
r.block()
|
r.block()
|
||||||
.map_err(|e| Error::Parse(format!("decode getblock: {e}")))
|
.map_err(|e| Error::Parse(format!("decode getblock: {e}")))
|
||||||
}
|
}
|
||||||
@@ -57,10 +58,9 @@ impl Client {
|
|||||||
&'a H: Into<&'a bitcoin::BlockHash>,
|
&'a H: Into<&'a bitcoin::BlockHash>,
|
||||||
{
|
{
|
||||||
let hash: &bitcoin::BlockHash = hash.into();
|
let hash: &bitcoin::BlockHash = hash.into();
|
||||||
let r: GetBlockVerboseOne = self.0.call_with_retry(
|
let r: GetBlockVerboseOne = self
|
||||||
"getblock",
|
.0
|
||||||
&[serde_json::to_value(hash)?, Value::from(1u8)],
|
.call_with_retry("getblock", &[serde_json::to_value(hash)?, Value::from(1u8)])?;
|
||||||
)?;
|
|
||||||
Ok(BlockInfo {
|
Ok(BlockInfo {
|
||||||
height: r.height as usize,
|
height: r.height as usize,
|
||||||
confirmations: r.confirmations,
|
confirmations: r.confirmations,
|
||||||
@@ -241,7 +241,10 @@ impl Client {
|
|||||||
pub fn get_mempool_raw_tx(&self, txid: &Txid) -> Result<RawTx> {
|
pub fn get_mempool_raw_tx(&self, txid: &Txid) -> Result<RawTx> {
|
||||||
let hex = self.get_raw_transaction_hex(txid, None as Option<&BlockHash>)?;
|
let hex = self.get_raw_transaction_hex(txid, None as Option<&BlockHash>)?;
|
||||||
let tx = encode::deserialize_hex::<bitcoin::Transaction>(&hex)?;
|
let tx = encode::deserialize_hex::<bitcoin::Transaction>(&hex)?;
|
||||||
Ok(RawTx { tx, hex: hex.into() })
|
Ok(RawTx {
|
||||||
|
tx,
|
||||||
|
hex: hex.into(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Batched `getrawtransaction` over a slice of txids. Returns a map keyed
|
/// Batched `getrawtransaction` over a slice of txids. Returns a map keyed
|
||||||
@@ -250,10 +253,7 @@ impl Client {
|
|||||||
/// are logged and dropped so a single bad entry doesn't kill the batch.
|
/// are logged and dropped so a single bad entry doesn't kill the batch.
|
||||||
///
|
///
|
||||||
/// Chunked at `BATCH_CHUNK` requests per round-trip.
|
/// Chunked at `BATCH_CHUNK` requests per round-trip.
|
||||||
pub fn get_raw_transactions(
|
pub fn get_raw_transactions(&self, txids: &[Txid]) -> Result<FxHashMap<Txid, RawTx>> {
|
||||||
&self,
|
|
||||||
txids: &[Txid],
|
|
||||||
) -> Result<FxHashMap<Txid, RawTx>> {
|
|
||||||
let mut out: FxHashMap<Txid, RawTx> =
|
let mut out: FxHashMap<Txid, RawTx> =
|
||||||
FxHashMap::with_capacity_and_hasher(txids.len(), Default::default());
|
FxHashMap::with_capacity_and_hasher(txids.len(), Default::default());
|
||||||
|
|
||||||
@@ -271,7 +271,10 @@ impl Client {
|
|||||||
for (txid, res) in chunk.iter().zip(results) {
|
for (txid, res) in chunk.iter().zip(results) {
|
||||||
match res.and_then(|hex| {
|
match res.and_then(|hex| {
|
||||||
let tx = encode::deserialize_hex::<bitcoin::Transaction>(&hex)?;
|
let tx = encode::deserialize_hex::<bitcoin::Transaction>(&hex)?;
|
||||||
Ok::<_, Error>(RawTx { tx, hex: hex.into() })
|
Ok::<_, Error>(RawTx {
|
||||||
|
tx,
|
||||||
|
hex: hex.into(),
|
||||||
|
})
|
||||||
}) {
|
}) {
|
||||||
Ok(raw) => {
|
Ok(raw) => {
|
||||||
out.insert(txid.clone(), raw);
|
out.insert(txid.clone(), raw);
|
||||||
@@ -279,7 +282,9 @@ impl Client {
|
|||||||
// Silenced: users without `-txindex` expect -5 for
|
// Silenced: users without `-txindex` expect -5 for
|
||||||
// every confirmed tx. Downgraded so the mempool
|
// every confirmed tx. Downgraded so the mempool
|
||||||
// parent-fetch loop doesn't spam the log each cycle.
|
// parent-fetch loop doesn't spam the log each cycle.
|
||||||
Err(e) => debug!(txid = %txid, error = %e, "getrawtransaction batch: item failed"),
|
Err(e) => {
|
||||||
|
debug!(txid = %txid, error = %e, "getrawtransaction batch: item failed")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use brk_types::{AddrStats, AddrValidation, Transaction, Txid, Utxo, Version};
|
|||||||
use crate::{
|
use crate::{
|
||||||
AppState, CacheStrategy,
|
AppState, CacheStrategy,
|
||||||
extended::TransformResponseExtended,
|
extended::TransformResponseExtended,
|
||||||
params::{AddrParam, AddrTxidsParam, ValidateAddrParam},
|
params::{AddrParam, AddrTxidsParam, Empty, ValidateAddrParam},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait AddrRoutes {
|
pub trait AddrRoutes {
|
||||||
@@ -28,6 +28,7 @@ impl AddrRoutes for ApiRouter<AppState> {
|
|||||||
uri: Uri,
|
uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
Path(path): Path<AddrParam>,
|
Path(path): Path<AddrParam>,
|
||||||
|
_: Empty,
|
||||||
State(state): State<AppState>
|
State(state): State<AppState>
|
||||||
| {
|
| {
|
||||||
let strategy = state.addr_cache(Version::ONE, &path.addr, false);
|
let strategy = state.addr_cache(Version::ONE, &path.addr, false);
|
||||||
@@ -96,6 +97,7 @@ impl AddrRoutes for ApiRouter<AppState> {
|
|||||||
uri: Uri,
|
uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
Path(path): Path<AddrParam>,
|
Path(path): Path<AddrParam>,
|
||||||
|
_: Empty,
|
||||||
State(state): State<AppState>
|
State(state): State<AppState>
|
||||||
| {
|
| {
|
||||||
let hash = state.sync(|q| q.addr_mempool_hash(&path.addr));
|
let hash = state.sync(|q| q.addr_mempool_hash(&path.addr));
|
||||||
@@ -118,6 +120,7 @@ impl AddrRoutes for ApiRouter<AppState> {
|
|||||||
uri: Uri,
|
uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
Path(path): Path<AddrParam>,
|
Path(path): Path<AddrParam>,
|
||||||
|
_: Empty,
|
||||||
State(state): State<AppState>
|
State(state): State<AppState>
|
||||||
| {
|
| {
|
||||||
let strategy = state.addr_cache(Version::ONE, &path.addr, false);
|
let strategy = state.addr_cache(Version::ONE, &path.addr, false);
|
||||||
@@ -140,6 +143,7 @@ impl AddrRoutes for ApiRouter<AppState> {
|
|||||||
uri: Uri,
|
uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
Path(path): Path<ValidateAddrParam>,
|
Path(path): Path<ValidateAddrParam>,
|
||||||
|
_: Empty,
|
||||||
State(state): State<AppState>
|
State(state): State<AppState>
|
||||||
| {
|
| {
|
||||||
state.cached_json(&headers, CacheStrategy::Deploy, &uri, move |_q| Ok(AddrValidation::from_addr(&path.addr))).await
|
state.cached_json(&headers, CacheStrategy::Deploy, &uri, move |_q| Ok(AddrValidation::from_addr(&path.addr))).await
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ use brk_types::{
|
|||||||
use crate::{
|
use crate::{
|
||||||
AppState, CacheStrategy,
|
AppState, CacheStrategy,
|
||||||
extended::TransformResponseExtended,
|
extended::TransformResponseExtended,
|
||||||
params::{BlockHashParam, BlockHashStartIndex, BlockHashTxIndex, HeightParam, TimestampParam},
|
params::{
|
||||||
|
BlockHashParam, BlockHashStartIndex, BlockHashTxIndex, Empty, HeightParam, TimestampParam,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait BlockRoutes {
|
pub trait BlockRoutes {
|
||||||
@@ -26,7 +28,7 @@ impl BlockRoutes for ApiRouter<AppState> {
|
|||||||
async |uri: Uri,
|
async |uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
Path(path): Path<BlockHashParam>,
|
Path(path): Path<BlockHashParam>,
|
||||||
State(state): State<AppState>| {
|
_: Empty, State(state): State<AppState>| {
|
||||||
let strategy = state.block_cache(Version::ONE, &path.hash);
|
let strategy = state.block_cache(Version::ONE, &path.hash);
|
||||||
state.cached_json(&headers, strategy, &uri, move |q| q.block(&path.hash)).await
|
state.cached_json(&headers, strategy, &uri, move |q| q.block(&path.hash)).await
|
||||||
},
|
},
|
||||||
@@ -48,7 +50,7 @@ impl BlockRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/v1/block/{hash}",
|
"/api/v1/block/{hash}",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, Path(path): Path<BlockHashParam>, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, Path(path): Path<BlockHashParam>, _: Empty, State(state): State<AppState>| {
|
||||||
let strategy = state.block_cache(Version::ONE, &path.hash);
|
let strategy = state.block_cache(Version::ONE, &path.hash);
|
||||||
state.cached_json(&headers, strategy, &uri, move |q| {
|
state.cached_json(&headers, strategy, &uri, move |q| {
|
||||||
let height = q.height_by_hash(&path.hash)?;
|
let height = q.height_by_hash(&path.hash)?;
|
||||||
@@ -71,7 +73,7 @@ impl BlockRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/block/{hash}/header",
|
"/api/block/{hash}/header",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, Path(path): Path<BlockHashParam>, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, Path(path): Path<BlockHashParam>, _: Empty, State(state): State<AppState>| {
|
||||||
let strategy = state.block_cache(Version::ONE, &path.hash);
|
let strategy = state.block_cache(Version::ONE, &path.hash);
|
||||||
state.cached_text(&headers, strategy, &uri, move |q| q.block_header_hex(&path.hash)).await
|
state.cached_text(&headers, strategy, &uri, move |q| q.block_header_hex(&path.hash)).await
|
||||||
},
|
},
|
||||||
@@ -94,7 +96,7 @@ impl BlockRoutes for ApiRouter<AppState> {
|
|||||||
async |uri: Uri,
|
async |uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
Path(path): Path<HeightParam>,
|
Path(path): Path<HeightParam>,
|
||||||
State(state): State<AppState>| {
|
_: Empty, State(state): State<AppState>| {
|
||||||
state.cached_text(&headers, state.height_cache(Version::ONE, path.height), &uri, move |q| q.block_hash_by_height(path.height).map(|h| h.to_string())).await
|
state.cached_text(&headers, state.height_cache(Version::ONE, path.height), &uri, move |q| q.block_hash_by_height(path.height).map(|h| h.to_string())).await
|
||||||
},
|
},
|
||||||
|op| {
|
|op| {
|
||||||
@@ -118,7 +120,7 @@ impl BlockRoutes for ApiRouter<AppState> {
|
|||||||
async |uri: Uri,
|
async |uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
Path(path): Path<TimestampParam>,
|
Path(path): Path<TimestampParam>,
|
||||||
State(state): State<AppState>| {
|
_: Empty, State(state): State<AppState>| {
|
||||||
state.cached_json(&headers, state.timestamp_cache(Version::ONE, path.timestamp), &uri, move |q| q.block_by_timestamp(path.timestamp)).await
|
state.cached_json(&headers, state.timestamp_cache(Version::ONE, path.timestamp), &uri, move |q| q.block_by_timestamp(path.timestamp)).await
|
||||||
},
|
},
|
||||||
|op| {
|
|op| {
|
||||||
@@ -140,7 +142,7 @@ impl BlockRoutes for ApiRouter<AppState> {
|
|||||||
async |uri: Uri,
|
async |uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
Path(path): Path<BlockHashParam>,
|
Path(path): Path<BlockHashParam>,
|
||||||
State(state): State<AppState>| {
|
_: Empty, State(state): State<AppState>| {
|
||||||
let strategy = state.block_cache(Version::ONE, &path.hash);
|
let strategy = state.block_cache(Version::ONE, &path.hash);
|
||||||
state.cached_bytes(&headers, strategy, &uri, move |q| q.block_raw(&path.hash)).await
|
state.cached_bytes(&headers, strategy, &uri, move |q| q.block_raw(&path.hash)).await
|
||||||
},
|
},
|
||||||
@@ -165,7 +167,7 @@ impl BlockRoutes for ApiRouter<AppState> {
|
|||||||
async |uri: Uri,
|
async |uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
Path(path): Path<BlockHashParam>,
|
Path(path): Path<BlockHashParam>,
|
||||||
State(state): State<AppState>| {
|
_: Empty, State(state): State<AppState>| {
|
||||||
state.cached_json(&headers, state.block_status_cache(Version::ONE, &path.hash), &uri, move |q| q.block_status(&path.hash)).await
|
state.cached_json(&headers, state.block_status_cache(Version::ONE, &path.hash), &uri, move |q| q.block_status(&path.hash)).await
|
||||||
},
|
},
|
||||||
|op| {
|
|op| {
|
||||||
@@ -186,7 +188,7 @@ impl BlockRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/blocks/tip/height",
|
"/api/blocks/tip/height",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
|
||||||
state.cached_text(&headers, CacheStrategy::Tip, &uri, |q| Ok(q.indexed_height().to_string())).await
|
state.cached_text(&headers, CacheStrategy::Tip, &uri, |q| Ok(q.indexed_height().to_string())).await
|
||||||
},
|
},
|
||||||
|op| {
|
|op| {
|
||||||
@@ -203,7 +205,7 @@ impl BlockRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/blocks/tip/hash",
|
"/api/blocks/tip/hash",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
|
||||||
state.cached_text(&headers, CacheStrategy::Tip, &uri, |q| Ok(q.tip_blockhash().to_string())).await
|
state.cached_text(&headers, CacheStrategy::Tip, &uri, |q| Ok(q.tip_blockhash().to_string())).await
|
||||||
},
|
},
|
||||||
|op| {
|
|op| {
|
||||||
@@ -223,7 +225,7 @@ impl BlockRoutes for ApiRouter<AppState> {
|
|||||||
async |uri: Uri,
|
async |uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
Path(path): Path<BlockHashTxIndex>,
|
Path(path): Path<BlockHashTxIndex>,
|
||||||
State(state): State<AppState>| {
|
_: Empty, State(state): State<AppState>| {
|
||||||
let strategy = state.block_cache(Version::ONE, &path.hash);
|
let strategy = state.block_cache(Version::ONE, &path.hash);
|
||||||
state.cached_text(&headers, strategy, &uri, move |q| q.block_txid_at_index(&path.hash, path.index).map(|t| t.to_string())).await
|
state.cached_text(&headers, strategy, &uri, move |q| q.block_txid_at_index(&path.hash, path.index).map(|t| t.to_string())).await
|
||||||
},
|
},
|
||||||
@@ -248,7 +250,7 @@ impl BlockRoutes for ApiRouter<AppState> {
|
|||||||
async |uri: Uri,
|
async |uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
Path(path): Path<BlockHashParam>,
|
Path(path): Path<BlockHashParam>,
|
||||||
State(state): State<AppState>| {
|
_: Empty, State(state): State<AppState>| {
|
||||||
let strategy = state.block_cache(Version::ONE, &path.hash);
|
let strategy = state.block_cache(Version::ONE, &path.hash);
|
||||||
state.cached_json(&headers, strategy, &uri, move |q| q.block_txids(&path.hash)).await
|
state.cached_json(&headers, strategy, &uri, move |q| q.block_txids(&path.hash)).await
|
||||||
},
|
},
|
||||||
@@ -273,7 +275,7 @@ impl BlockRoutes for ApiRouter<AppState> {
|
|||||||
async |uri: Uri,
|
async |uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
Path(path): Path<BlockHashParam>,
|
Path(path): Path<BlockHashParam>,
|
||||||
State(state): State<AppState>| {
|
_: Empty, State(state): State<AppState>| {
|
||||||
let strategy = state.block_cache(Version::ONE, &path.hash);
|
let strategy = state.block_cache(Version::ONE, &path.hash);
|
||||||
state.cached_json(&headers, strategy, &uri, move |q| q.block_txs(&path.hash, TxIndex::default())).await
|
state.cached_json(&headers, strategy, &uri, move |q| q.block_txs(&path.hash, TxIndex::default())).await
|
||||||
},
|
},
|
||||||
@@ -299,7 +301,7 @@ impl BlockRoutes for ApiRouter<AppState> {
|
|||||||
async |uri: Uri,
|
async |uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
Path(path): Path<BlockHashStartIndex>,
|
Path(path): Path<BlockHashStartIndex>,
|
||||||
State(state): State<AppState>| {
|
_: Empty, State(state): State<AppState>| {
|
||||||
let strategy = state.block_cache(Version::ONE, &path.hash);
|
let strategy = state.block_cache(Version::ONE, &path.hash);
|
||||||
state.cached_json(&headers, strategy, &uri, move |q| q.block_txs(&path.hash, path.start_index)).await
|
state.cached_json(&headers, strategy, &uri, move |q| q.block_txs(&path.hash, path.start_index)).await
|
||||||
},
|
},
|
||||||
@@ -322,7 +324,7 @@ impl BlockRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/blocks",
|
"/api/blocks",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
|
||||||
state
|
state
|
||||||
.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.blocks(None))
|
.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.blocks(None))
|
||||||
.await
|
.await
|
||||||
@@ -344,7 +346,7 @@ impl BlockRoutes for ApiRouter<AppState> {
|
|||||||
async |uri: Uri,
|
async |uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
Path(path): Path<HeightParam>,
|
Path(path): Path<HeightParam>,
|
||||||
State(state): State<AppState>| {
|
_: Empty, State(state): State<AppState>| {
|
||||||
state.cached_json(&headers, state.height_cache(Version::ONE, path.height), &uri, move |q| q.blocks(Some(path.height))).await
|
state.cached_json(&headers, state.height_cache(Version::ONE, path.height), &uri, move |q| q.blocks(Some(path.height))).await
|
||||||
},
|
},
|
||||||
|op| {
|
|op| {
|
||||||
@@ -364,7 +366,7 @@ impl BlockRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/v1/blocks",
|
"/api/v1/blocks",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
|
||||||
state
|
state
|
||||||
.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.blocks_v1(None))
|
.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.blocks_v1(None))
|
||||||
.await
|
.await
|
||||||
@@ -386,7 +388,7 @@ impl BlockRoutes for ApiRouter<AppState> {
|
|||||||
async |uri: Uri,
|
async |uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
Path(path): Path<HeightParam>,
|
Path(path): Path<HeightParam>,
|
||||||
State(state): State<AppState>| {
|
_: Empty, State(state): State<AppState>| {
|
||||||
state.cached_json(&headers, state.height_cache(Version::ONE, path.height), &uri, move |q| q.blocks_v1(Some(path.height))).await
|
state.cached_json(&headers, state.height_cache(Version::ONE, path.height), &uri, move |q| q.blocks_v1(Some(path.height))).await
|
||||||
},
|
},
|
||||||
|op| {
|
|op| {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use axum::{
|
|||||||
};
|
};
|
||||||
use brk_types::{MempoolBlock, RecommendedFees};
|
use brk_types::{MempoolBlock, RecommendedFees};
|
||||||
|
|
||||||
use crate::{AppState, extended::TransformResponseExtended};
|
use crate::{AppState, extended::TransformResponseExtended, params::Empty};
|
||||||
|
|
||||||
pub trait FeesRoutes {
|
pub trait FeesRoutes {
|
||||||
fn add_fees_routes(self) -> Self;
|
fn add_fees_routes(self) -> Self;
|
||||||
@@ -16,7 +16,7 @@ impl FeesRoutes for ApiRouter<AppState> {
|
|||||||
self.api_route(
|
self.api_route(
|
||||||
"/api/v1/fees/mempool-blocks",
|
"/api/v1/fees/mempool-blocks",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
|
||||||
state
|
state
|
||||||
.cached_json(&headers, state.mempool_cache(), &uri, |q| {
|
.cached_json(&headers, state.mempool_cache(), &uri, |q| {
|
||||||
q.mempool_blocks()
|
q.mempool_blocks()
|
||||||
@@ -37,7 +37,7 @@ impl FeesRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/v1/fees/recommended",
|
"/api/v1/fees/recommended",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
|
||||||
state
|
state
|
||||||
.cached_json(&headers, state.mempool_cache(), &uri, |q| {
|
.cached_json(&headers, state.mempool_cache(), &uri, |q| {
|
||||||
q.recommended_fees()
|
q.recommended_fees()
|
||||||
@@ -58,7 +58,7 @@ impl FeesRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/v1/fees/precise",
|
"/api/v1/fees/precise",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
|
||||||
state
|
state
|
||||||
.cached_json(&headers, state.mempool_cache(), &uri, |q| {
|
.cached_json(&headers, state.mempool_cache(), &uri, |q| {
|
||||||
q.recommended_fees()
|
q.recommended_fees()
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ use axum::{
|
|||||||
use brk_types::{DifficultyAdjustment, HistoricalPrice, Prices, Timestamp, Version};
|
use brk_types::{DifficultyAdjustment, HistoricalPrice, Prices, Timestamp, Version};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AppState, CacheStrategy, extended::TransformResponseExtended, params::OptionalTimestampParam,
|
AppState, CacheStrategy,
|
||||||
|
extended::TransformResponseExtended,
|
||||||
|
params::{Empty, OptionalTimestampParam},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait GeneralRoutes {
|
pub trait GeneralRoutes {
|
||||||
@@ -18,7 +20,7 @@ impl GeneralRoutes for ApiRouter<AppState> {
|
|||||||
self.api_route(
|
self.api_route(
|
||||||
"/api/v1/difficulty-adjustment",
|
"/api/v1/difficulty-adjustment",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
|
||||||
state
|
state
|
||||||
.cached_json(&headers, CacheStrategy::Tip, &uri, |q| {
|
.cached_json(&headers, CacheStrategy::Tip, &uri, |q| {
|
||||||
q.difficulty_adjustment()
|
q.difficulty_adjustment()
|
||||||
@@ -39,7 +41,7 @@ impl GeneralRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/v1/prices",
|
"/api/v1/prices",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
|
||||||
state
|
state
|
||||||
.cached_json(&headers, state.mempool_cache(), &uri, |q| {
|
.cached_json(&headers, state.mempool_cache(), &uri, |q| {
|
||||||
Ok(Prices {
|
Ok(Prices {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use axum::{
|
|||||||
};
|
};
|
||||||
use brk_types::{Dollars, MempoolInfo, MempoolRecentTx, Txid};
|
use brk_types::{Dollars, MempoolInfo, MempoolRecentTx, Txid};
|
||||||
|
|
||||||
use crate::{AppState, extended::TransformResponseExtended};
|
use crate::{AppState, extended::TransformResponseExtended, params::Empty};
|
||||||
|
|
||||||
pub trait MempoolRoutes {
|
pub trait MempoolRoutes {
|
||||||
fn add_mempool_routes(self) -> Self;
|
fn add_mempool_routes(self) -> Self;
|
||||||
@@ -16,7 +16,7 @@ impl MempoolRoutes for ApiRouter<AppState> {
|
|||||||
self.api_route(
|
self.api_route(
|
||||||
"/api/mempool",
|
"/api/mempool",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
|
||||||
state
|
state
|
||||||
.cached_json(&headers, state.mempool_cache(), &uri, |q| q.mempool_info())
|
.cached_json(&headers, state.mempool_cache(), &uri, |q| q.mempool_info())
|
||||||
.await
|
.await
|
||||||
@@ -35,7 +35,7 @@ impl MempoolRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/mempool/txids",
|
"/api/mempool/txids",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
|
||||||
state
|
state
|
||||||
.cached_json(&headers, state.mempool_cache(), &uri, |q| q.mempool_txids())
|
.cached_json(&headers, state.mempool_cache(), &uri, |q| q.mempool_txids())
|
||||||
.await
|
.await
|
||||||
@@ -54,7 +54,7 @@ impl MempoolRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/mempool/recent",
|
"/api/mempool/recent",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
|
||||||
state
|
state
|
||||||
.cached_json(&headers, state.mempool_cache(), &uri, |q| q.mempool_recent())
|
.cached_json(&headers, state.mempool_cache(), &uri, |q| q.mempool_recent())
|
||||||
.await
|
.await
|
||||||
@@ -73,7 +73,7 @@ impl MempoolRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/mempool/price",
|
"/api/mempool/price",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
|
||||||
state
|
state
|
||||||
.cached_json(&headers, state.mempool_cache(), &uri, |q| q.live_price())
|
.cached_json(&headers, state.mempool_cache(), &uri, |q| q.live_price())
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ use brk_types::{
|
|||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{CacheStrategy, Error, extended::TransformResponseExtended};
|
use crate::{CacheStrategy, Error, extended::TransformResponseExtended, params::Empty};
|
||||||
|
|
||||||
use super::AppState;
|
use super::AppState;
|
||||||
use super::series_legacy;
|
use super::series_legacy;
|
||||||
@@ -46,7 +46,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/metrics",
|
"/api/metrics",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
|
||||||
state.cached_json(&headers, CacheStrategy::Deploy, &uri, |q| Ok(q.series_catalog().clone())).await
|
state.cached_json(&headers, CacheStrategy::Deploy, &uri, |q| Ok(q.series_catalog().clone())).await
|
||||||
},
|
},
|
||||||
|op| op
|
|op| op
|
||||||
@@ -68,6 +68,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter<AppState> {
|
|||||||
async |
|
async |
|
||||||
uri: Uri,
|
uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
|
_: Empty,
|
||||||
State(state): State<AppState>
|
State(state): State<AppState>
|
||||||
| {
|
| {
|
||||||
state.cached_json(&headers, CacheStrategy::Deploy, &uri, |q| Ok(q.series_count())).await
|
state.cached_json(&headers, CacheStrategy::Deploy, &uri, |q| Ok(q.series_count())).await
|
||||||
@@ -91,6 +92,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter<AppState> {
|
|||||||
async |
|
async |
|
||||||
uri: Uri,
|
uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
|
_: Empty,
|
||||||
State(state): State<AppState>
|
State(state): State<AppState>
|
||||||
| {
|
| {
|
||||||
state.cached_json(&headers, CacheStrategy::Deploy, &uri, |q| Ok(q.indexes().to_vec())).await
|
state.cached_json(&headers, CacheStrategy::Deploy, &uri, |q| Ok(q.indexes().to_vec())).await
|
||||||
@@ -186,6 +188,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter<AppState> {
|
|||||||
async |
|
async |
|
||||||
uri: Uri,
|
uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
|
_: Empty,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Path(path): Path<LegacySeriesParam>
|
Path(path): Path<LegacySeriesParam>
|
||||||
| {
|
| {
|
||||||
@@ -273,6 +276,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter<AppState> {
|
|||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri,
|
async |uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
|
_: Empty,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Path(path): Path<LegacySeriesWithIndex>| {
|
Path(path): Path<LegacySeriesWithIndex>| {
|
||||||
state
|
state
|
||||||
@@ -299,6 +303,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter<AppState> {
|
|||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri,
|
async |uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
|
_: Empty,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Path(path): Path<LegacySeriesWithIndex>| {
|
Path(path): Path<LegacySeriesWithIndex>| {
|
||||||
state
|
state
|
||||||
@@ -325,6 +330,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter<AppState> {
|
|||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri,
|
async |uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
|
_: Empty,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Path(path): Path<LegacySeriesWithIndex>| {
|
Path(path): Path<LegacySeriesWithIndex>| {
|
||||||
state
|
state
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ use brk_types::{
|
|||||||
use crate::{
|
use crate::{
|
||||||
AppState, CacheStrategy,
|
AppState, CacheStrategy,
|
||||||
extended::TransformResponseExtended,
|
extended::TransformResponseExtended,
|
||||||
params::{BlockCountParam, PoolSlugAndHeightParam, PoolSlugParam, TimePeriodParam},
|
params::{BlockCountParam, Empty, PoolSlugAndHeightParam, PoolSlugParam, TimePeriodParam},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait MiningRoutes {
|
pub trait MiningRoutes {
|
||||||
@@ -30,7 +30,7 @@ impl MiningRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/v1/mining/pools",
|
"/api/v1/mining/pools",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
|
||||||
// Pool list is compiled-in, only changes on deploy
|
// Pool list is compiled-in, only changes on deploy
|
||||||
state.cached_json(&headers, CacheStrategy::Deploy, &uri, |q| Ok(q.all_pools())).await
|
state.cached_json(&headers, CacheStrategy::Deploy, &uri, |q| Ok(q.all_pools())).await
|
||||||
},
|
},
|
||||||
@@ -48,7 +48,7 @@ impl MiningRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/v1/mining/pools/{time_period}",
|
"/api/v1/mining/pools/{time_period}",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, _: Empty, State(state): State<AppState>| {
|
||||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.mining_pools(path.time_period)).await
|
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.mining_pools(path.time_period)).await
|
||||||
},
|
},
|
||||||
|op| {
|
|op| {
|
||||||
@@ -66,7 +66,7 @@ impl MiningRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/v1/mining/pool/{slug}",
|
"/api/v1/mining/pool/{slug}",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, Path(path): Path<PoolSlugParam>, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, Path(path): Path<PoolSlugParam>, _: Empty, State(state): State<AppState>| {
|
||||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.pool_detail(path.slug)).await
|
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.pool_detail(path.slug)).await
|
||||||
},
|
},
|
||||||
|op| {
|
|op| {
|
||||||
@@ -84,7 +84,7 @@ impl MiningRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/v1/mining/hashrate/pools",
|
"/api/v1/mining/hashrate/pools",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
|
||||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, |q| q.pools_hashrate(None)).await
|
state.cached_json(&headers, CacheStrategy::Tip, &uri, |q| q.pools_hashrate(None)).await
|
||||||
},
|
},
|
||||||
|op| {
|
|op| {
|
||||||
@@ -101,7 +101,7 @@ impl MiningRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/v1/mining/hashrate/pools/{time_period}",
|
"/api/v1/mining/hashrate/pools/{time_period}",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, _: Empty, State(state): State<AppState>| {
|
||||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.pools_hashrate(Some(path.time_period))).await
|
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.pools_hashrate(Some(path.time_period))).await
|
||||||
},
|
},
|
||||||
|op| {
|
|op| {
|
||||||
@@ -119,7 +119,7 @@ impl MiningRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/v1/mining/pool/{slug}/hashrate",
|
"/api/v1/mining/pool/{slug}/hashrate",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, Path(path): Path<PoolSlugParam>, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, Path(path): Path<PoolSlugParam>, _: Empty, State(state): State<AppState>| {
|
||||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.pool_hashrate(path.slug)).await
|
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.pool_hashrate(path.slug)).await
|
||||||
},
|
},
|
||||||
|op| {
|
|op| {
|
||||||
@@ -137,7 +137,7 @@ impl MiningRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/v1/mining/pool/{slug}/blocks",
|
"/api/v1/mining/pool/{slug}/blocks",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, Path(path): Path<PoolSlugParam>, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, Path(path): Path<PoolSlugParam>, _: Empty, State(state): State<AppState>| {
|
||||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.pool_blocks(path.slug, None)).await
|
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.pool_blocks(path.slug, None)).await
|
||||||
},
|
},
|
||||||
|op| {
|
|op| {
|
||||||
@@ -155,7 +155,7 @@ impl MiningRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/v1/mining/pool/{slug}/blocks/{height}",
|
"/api/v1/mining/pool/{slug}/blocks/{height}",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, Path(PoolSlugAndHeightParam {slug, height}): Path<PoolSlugAndHeightParam>, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, Path(PoolSlugAndHeightParam {slug, height}): Path<PoolSlugAndHeightParam>, _: Empty, State(state): State<AppState>| {
|
||||||
state.cached_json(&headers, state.height_cache(Version::ONE, height), &uri, move |q| q.pool_blocks(slug, Some(height))).await
|
state.cached_json(&headers, state.height_cache(Version::ONE, height), &uri, move |q| q.pool_blocks(slug, Some(height))).await
|
||||||
},
|
},
|
||||||
|op| {
|
|op| {
|
||||||
@@ -173,7 +173,7 @@ impl MiningRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/v1/mining/hashrate",
|
"/api/v1/mining/hashrate",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
|
||||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, |q| q.hashrate(None)).await
|
state.cached_json(&headers, CacheStrategy::Tip, &uri, |q| q.hashrate(None)).await
|
||||||
},
|
},
|
||||||
|op| {
|
|op| {
|
||||||
@@ -190,7 +190,7 @@ impl MiningRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/v1/mining/hashrate/{time_period}",
|
"/api/v1/mining/hashrate/{time_period}",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, _: Empty, State(state): State<AppState>| {
|
||||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.hashrate(Some(path.time_period))).await
|
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.hashrate(Some(path.time_period))).await
|
||||||
},
|
},
|
||||||
|op| {
|
|op| {
|
||||||
@@ -208,7 +208,7 @@ impl MiningRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/v1/mining/difficulty-adjustments",
|
"/api/v1/mining/difficulty-adjustments",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
|
||||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, |q| q.difficulty_adjustments(None)).await
|
state.cached_json(&headers, CacheStrategy::Tip, &uri, |q| q.difficulty_adjustments(None)).await
|
||||||
},
|
},
|
||||||
|op| {
|
|op| {
|
||||||
@@ -225,7 +225,7 @@ impl MiningRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/v1/mining/difficulty-adjustments/{time_period}",
|
"/api/v1/mining/difficulty-adjustments/{time_period}",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, _: Empty, State(state): State<AppState>| {
|
||||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.difficulty_adjustments(Some(path.time_period))).await
|
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.difficulty_adjustments(Some(path.time_period))).await
|
||||||
},
|
},
|
||||||
|op| {
|
|op| {
|
||||||
@@ -243,7 +243,7 @@ impl MiningRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/v1/mining/reward-stats/{block_count}",
|
"/api/v1/mining/reward-stats/{block_count}",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, Path(path): Path<BlockCountParam>, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, Path(path): Path<BlockCountParam>, _: Empty, State(state): State<AppState>| {
|
||||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.reward_stats(path.block_count)).await
|
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.reward_stats(path.block_count)).await
|
||||||
},
|
},
|
||||||
|op| {
|
|op| {
|
||||||
@@ -261,7 +261,7 @@ impl MiningRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/v1/mining/blocks/fees/{time_period}",
|
"/api/v1/mining/blocks/fees/{time_period}",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, _: Empty, State(state): State<AppState>| {
|
||||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.block_fees(path.time_period)).await
|
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.block_fees(path.time_period)).await
|
||||||
},
|
},
|
||||||
|op| {
|
|op| {
|
||||||
@@ -279,7 +279,7 @@ impl MiningRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/v1/mining/blocks/rewards/{time_period}",
|
"/api/v1/mining/blocks/rewards/{time_period}",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, _: Empty, State(state): State<AppState>| {
|
||||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.block_rewards(path.time_period)).await
|
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.block_rewards(path.time_period)).await
|
||||||
},
|
},
|
||||||
|op| {
|
|op| {
|
||||||
@@ -297,7 +297,7 @@ impl MiningRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/v1/mining/blocks/fee-rates/{time_period}",
|
"/api/v1/mining/blocks/fee-rates/{time_period}",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, _: Empty, State(state): State<AppState>| {
|
||||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.block_fee_rates(path.time_period)).await
|
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.block_fee_rates(path.time_period)).await
|
||||||
},
|
},
|
||||||
|op| {
|
|op| {
|
||||||
@@ -315,7 +315,7 @@ impl MiningRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/v1/mining/blocks/sizes-weights/{time_period}",
|
"/api/v1/mining/blocks/sizes-weights/{time_period}",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, _: Empty, State(state): State<AppState>| {
|
||||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.block_sizes_weights(path.time_period)).await
|
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.block_sizes_weights(path.time_period)).await
|
||||||
},
|
},
|
||||||
|op| {
|
|op| {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ use brk_types::{
|
|||||||
use crate::{
|
use crate::{
|
||||||
AppState, CacheParams, CacheStrategy, Result,
|
AppState, CacheParams, CacheStrategy, Result,
|
||||||
extended::{HeaderMapExtended, TransformResponseExtended},
|
extended::{HeaderMapExtended, TransformResponseExtended},
|
||||||
params::SeriesParam,
|
params::{Empty, SeriesParam},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Shared response pipeline for every series endpoint.
|
/// Shared response pipeline for every series endpoint.
|
||||||
@@ -42,9 +42,7 @@ pub(super) async fn serve(
|
|||||||
to_bytes: impl FnOnce(&BrkQuery, ResolvedQuery) -> BrkResult<Bytes> + Send + 'static,
|
to_bytes: impl FnOnce(&BrkQuery, ResolvedQuery) -> BrkResult<Bytes> + Send + 'static,
|
||||||
) -> Result<Response> {
|
) -> Result<Response> {
|
||||||
let max_weight = state.max_weight_for(&addr);
|
let max_weight = state.max_weight_for(&addr);
|
||||||
let resolved = state
|
let resolved = state.run(move |q| q.resolve(params, max_weight)).await?;
|
||||||
.run(move |q| q.resolve(params, max_weight))
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let format = resolved.format();
|
let format = resolved.format();
|
||||||
let csv_filename = resolved.csv_filename();
|
let csv_filename = resolved.csv_filename();
|
||||||
@@ -114,7 +112,7 @@ impl ApiSeriesRoutes for ApiRouter<AppState> {
|
|||||||
self.api_route(
|
self.api_route(
|
||||||
"/api/series",
|
"/api/series",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
|
||||||
state.cached_json(&headers, CacheStrategy::Deploy, &uri, |q| Ok(q.series_catalog().clone())).await
|
state.cached_json(&headers, CacheStrategy::Deploy, &uri, |q| Ok(q.series_catalog().clone())).await
|
||||||
},
|
},
|
||||||
|op| op
|
|op| op
|
||||||
@@ -135,6 +133,7 @@ impl ApiSeriesRoutes for ApiRouter<AppState> {
|
|||||||
async |
|
async |
|
||||||
uri: Uri,
|
uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
|
_: Empty,
|
||||||
State(state): State<AppState>
|
State(state): State<AppState>
|
||||||
| {
|
| {
|
||||||
state.cached_json(&headers, CacheStrategy::Deploy, &uri, |q| Ok(q.series_count())).await
|
state.cached_json(&headers, CacheStrategy::Deploy, &uri, |q| Ok(q.series_count())).await
|
||||||
@@ -154,6 +153,7 @@ impl ApiSeriesRoutes for ApiRouter<AppState> {
|
|||||||
async |
|
async |
|
||||||
uri: Uri,
|
uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
|
_: Empty,
|
||||||
State(state): State<AppState>
|
State(state): State<AppState>
|
||||||
| {
|
| {
|
||||||
state.cached_json(&headers, CacheStrategy::Deploy, &uri, |q| Ok(q.indexes().to_vec())).await
|
state.cached_json(&headers, CacheStrategy::Deploy, &uri, |q| Ok(q.indexes().to_vec())).await
|
||||||
@@ -216,6 +216,7 @@ impl ApiSeriesRoutes for ApiRouter<AppState> {
|
|||||||
async |
|
async |
|
||||||
uri: Uri,
|
uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
|
_: Empty,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Path(path): Path<SeriesParam>
|
Path(path): Path<SeriesParam>
|
||||||
| {
|
| {
|
||||||
@@ -309,6 +310,7 @@ impl ApiSeriesRoutes for ApiRouter<AppState> {
|
|||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri,
|
async |uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
|
_: Empty,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Path(path): Path<SeriesNameWithIndex>| {
|
Path(path): Path<SeriesNameWithIndex>| {
|
||||||
state
|
state
|
||||||
@@ -334,6 +336,7 @@ impl ApiSeriesRoutes for ApiRouter<AppState> {
|
|||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri,
|
async |uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
|
_: Empty,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Path(path): Path<SeriesNameWithIndex>| {
|
Path(path): Path<SeriesNameWithIndex>| {
|
||||||
state
|
state
|
||||||
@@ -357,6 +360,7 @@ impl ApiSeriesRoutes for ApiRouter<AppState> {
|
|||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri,
|
async |uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
|
_: Empty,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Path(path): Path<SeriesNameWithIndex>| {
|
Path(path): Path<SeriesNameWithIndex>| {
|
||||||
state
|
state
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ use vecdb::ReadableOptionVec;
|
|||||||
use crate::{
|
use crate::{
|
||||||
AppState, CacheStrategy, Result,
|
AppState, CacheStrategy, Result,
|
||||||
extended::{HeaderMapExtended, TransformResponseExtended},
|
extended::{HeaderMapExtended, TransformResponseExtended},
|
||||||
|
params::Empty,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const SUNSET: &str = "2027-01-01T00:00:00Z";
|
pub const SUNSET: &str = "2027-01-01T00:00:00Z";
|
||||||
@@ -43,7 +44,8 @@ pub async fn handler(
|
|||||||
Query(params): Query<SeriesSelection>,
|
Query(params): Query<SeriesSelection>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> Result<Response> {
|
) -> Result<Response> {
|
||||||
let mut response = super::series::serve(state, uri, headers, addr, params, legacy_bytes).await?;
|
let mut response =
|
||||||
|
super::series::serve(state, uri, headers, addr, params, legacy_bytes).await?;
|
||||||
if response.status() == StatusCode::OK {
|
if response.status() == StatusCode::OK {
|
||||||
response.headers_mut().insert_deprecation(SUNSET);
|
response.headers_mut().insert_deprecation(SUNSET);
|
||||||
}
|
}
|
||||||
@@ -151,7 +153,7 @@ impl ApiSeriesLegacyRoutes for ApiRouter<AppState> {
|
|||||||
self.api_route(
|
self.api_route(
|
||||||
"/api/series/cost-basis",
|
"/api/series/cost-basis",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
|
||||||
state
|
state
|
||||||
.cached_json(&headers, CacheStrategy::Deploy, &uri, |q| q.urpd_cohorts())
|
.cached_json(&headers, CacheStrategy::Deploy, &uri, |q| q.urpd_cohorts())
|
||||||
.await
|
.await
|
||||||
@@ -177,6 +179,7 @@ impl ApiSeriesLegacyRoutes for ApiRouter<AppState> {
|
|||||||
async |uri: Uri,
|
async |uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
Path(params): Path<CostBasisCohortParam>,
|
Path(params): Path<CostBasisCohortParam>,
|
||||||
|
_: Empty,
|
||||||
State(state): State<AppState>| {
|
State(state): State<AppState>| {
|
||||||
state
|
state
|
||||||
.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| {
|
.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use axum::{
|
|||||||
};
|
};
|
||||||
use brk_types::{DiskUsage, Health, SyncStatus};
|
use brk_types::{DiskUsage, Health, SyncStatus};
|
||||||
|
|
||||||
use crate::{CacheStrategy, VERSION, extended::TransformResponseExtended};
|
use crate::{CacheStrategy, VERSION, extended::TransformResponseExtended, params::Empty};
|
||||||
|
|
||||||
use super::AppState;
|
use super::AppState;
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ impl ServerRoutes for ApiRouter<AppState> {
|
|||||||
self.api_route(
|
self.api_route(
|
||||||
"/health",
|
"/health",
|
||||||
get_with(
|
get_with(
|
||||||
async |State(state): State<AppState>| -> axum::Json<Health> {
|
async |_: Empty, State(state): State<AppState>| -> axum::Json<Health> {
|
||||||
let uptime = state.started_instant.elapsed();
|
let uptime = state.started_instant.elapsed();
|
||||||
let started_at = state.started_at.to_string();
|
let started_at = state.started_at.to_string();
|
||||||
let sync = state
|
let sync = state
|
||||||
@@ -55,7 +55,7 @@ impl ServerRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/version",
|
"/version",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
|
||||||
state
|
state
|
||||||
.cached_json(&headers, CacheStrategy::Deploy, &uri, |_| {
|
.cached_json(&headers, CacheStrategy::Deploy, &uri, |_| {
|
||||||
Ok(env!("CARGO_PKG_VERSION"))
|
Ok(env!("CARGO_PKG_VERSION"))
|
||||||
@@ -75,7 +75,7 @@ impl ServerRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/server/sync",
|
"/api/server/sync",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
|
||||||
state
|
state
|
||||||
.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| {
|
.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| {
|
||||||
let tip_height = q.client().get_last_height()?;
|
let tip_height = q.client().get_last_height()?;
|
||||||
@@ -99,7 +99,7 @@ impl ServerRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/server/disk",
|
"/api/server/disk",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
|
||||||
let brk_path = state.data_path.clone();
|
let brk_path = state.data_path.clone();
|
||||||
state
|
state
|
||||||
.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| {
|
.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| {
|
||||||
|
|||||||
@@ -5,15 +5,16 @@ use aide::axum::{
|
|||||||
use axum::{
|
use axum::{
|
||||||
extract::{Path, State},
|
extract::{Path, State},
|
||||||
http::{HeaderMap, Uri},
|
http::{HeaderMap, Uri},
|
||||||
|
response::Response,
|
||||||
};
|
};
|
||||||
use brk_types::{
|
use brk_types::{
|
||||||
CpfpInfo, MerkleProof, RbfResponse, Transaction, TxOutspend, TxStatus, Txid, Version,
|
CpfpInfo, MerkleProof, RbfResponse, Transaction, TxOutspend, TxStatus, Txid, Version,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AppState, CacheStrategy,
|
AppState, CacheStrategy, Error, Result,
|
||||||
extended::TransformResponseExtended,
|
extended::TransformResponseExtended,
|
||||||
params::{TxIndexParam, TxidParam, TxidVout, TxidsParam},
|
params::{Empty, TxIndexParam, TxidParam, TxidVout, TxidsParam},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait TxRoutes {
|
pub trait TxRoutes {
|
||||||
@@ -26,7 +27,7 @@ impl TxRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/tx-index/{index}",
|
"/api/tx-index/{index}",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, Path(param): Path<TxIndexParam>, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, Path(param): Path<TxIndexParam>, _: Empty, State(state): State<AppState>| {
|
||||||
state.cached_text(&headers, CacheStrategy::Immutable(Version::ONE), &uri, move |q| q.txid_by_index(param.index).map(|t| t.to_string())).await
|
state.cached_text(&headers, CacheStrategy::Immutable(Version::ONE), &uri, move |q| q.txid_by_index(param.index).map(|t| t.to_string())).await
|
||||||
},
|
},
|
||||||
|op| op
|
|op| op
|
||||||
@@ -44,7 +45,7 @@ impl TxRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/v1/cpfp/{txid}",
|
"/api/v1/cpfp/{txid}",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, Path(param): Path<TxidParam>, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, Path(param): Path<TxidParam>, _: Empty, State(state): State<AppState>| {
|
||||||
state.cached_json(&headers, state.tx_cache(Version::ONE, ¶m.txid), &uri, move |q| q.cpfp(¶m.txid)).await
|
state.cached_json(&headers, state.tx_cache(Version::ONE, ¶m.txid), &uri, move |q| q.cpfp(¶m.txid)).await
|
||||||
},
|
},
|
||||||
|op| op
|
|op| op
|
||||||
@@ -62,7 +63,7 @@ impl TxRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/v1/tx/{txid}/rbf",
|
"/api/v1/tx/{txid}/rbf",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, Path(param): Path<TxidParam>, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, Path(param): Path<TxidParam>, _: Empty, State(state): State<AppState>| {
|
||||||
state.cached_json(&headers, state.mempool_cache(), &uri, move |q| q.tx_rbf(¶m.txid)).await
|
state.cached_json(&headers, state.mempool_cache(), &uri, move |q| q.tx_rbf(¶m.txid)).await
|
||||||
},
|
},
|
||||||
|op| op
|
|op| op
|
||||||
@@ -84,6 +85,7 @@ impl TxRoutes for ApiRouter<AppState> {
|
|||||||
uri: Uri,
|
uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
Path(param): Path<TxidParam>,
|
Path(param): Path<TxidParam>,
|
||||||
|
_: Empty,
|
||||||
State(state): State<AppState>
|
State(state): State<AppState>
|
||||||
| {
|
| {
|
||||||
state.cached_json(&headers, state.tx_cache(Version::ONE, ¶m.txid), &uri, move |q| q.transaction(¶m.txid)).await
|
state.cached_json(&headers, state.tx_cache(Version::ONE, ¶m.txid), &uri, move |q| q.transaction(¶m.txid)).await
|
||||||
@@ -109,6 +111,7 @@ impl TxRoutes for ApiRouter<AppState> {
|
|||||||
uri: Uri,
|
uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
Path(param): Path<TxidParam>,
|
Path(param): Path<TxidParam>,
|
||||||
|
_: Empty,
|
||||||
State(state): State<AppState>
|
State(state): State<AppState>
|
||||||
| {
|
| {
|
||||||
state.cached_text(&headers, state.tx_cache(Version::ONE, ¶m.txid), &uri, move |q| q.transaction_hex(¶m.txid)).await
|
state.cached_text(&headers, state.tx_cache(Version::ONE, ¶m.txid), &uri, move |q| q.transaction_hex(¶m.txid)).await
|
||||||
@@ -130,7 +133,7 @@ impl TxRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/tx/{txid}/merkleblock-proof",
|
"/api/tx/{txid}/merkleblock-proof",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, Path(param): Path<TxidParam>, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, Path(param): Path<TxidParam>, _: Empty, State(state): State<AppState>| {
|
||||||
state.cached_text(&headers, state.tx_cache(Version::ONE, ¶m.txid), &uri, move |q| q.merkleblock_proof(¶m.txid)).await
|
state.cached_text(&headers, state.tx_cache(Version::ONE, ¶m.txid), &uri, move |q| q.merkleblock_proof(¶m.txid)).await
|
||||||
},
|
},
|
||||||
|op| op
|
|op| op
|
||||||
@@ -148,7 +151,7 @@ impl TxRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/tx/{txid}/merkle-proof",
|
"/api/tx/{txid}/merkle-proof",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, Path(param): Path<TxidParam>, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, Path(param): Path<TxidParam>, _: Empty, State(state): State<AppState>| {
|
||||||
state.cached_json(&headers, state.tx_cache(Version::ONE, ¶m.txid), &uri, move |q| q.merkle_proof(¶m.txid)).await
|
state.cached_json(&headers, state.tx_cache(Version::ONE, ¶m.txid), &uri, move |q| q.merkle_proof(¶m.txid)).await
|
||||||
},
|
},
|
||||||
|op| op
|
|op| op
|
||||||
@@ -170,6 +173,7 @@ impl TxRoutes for ApiRouter<AppState> {
|
|||||||
uri: Uri,
|
uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
Path(path): Path<TxidVout>,
|
Path(path): Path<TxidVout>,
|
||||||
|
_: Empty,
|
||||||
State(state): State<AppState>
|
State(state): State<AppState>
|
||||||
| {
|
| {
|
||||||
let v = Version::ONE;
|
let v = Version::ONE;
|
||||||
@@ -204,6 +208,7 @@ impl TxRoutes for ApiRouter<AppState> {
|
|||||||
uri: Uri,
|
uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
Path(param): Path<TxidParam>,
|
Path(param): Path<TxidParam>,
|
||||||
|
_: Empty,
|
||||||
State(state): State<AppState>
|
State(state): State<AppState>
|
||||||
| {
|
| {
|
||||||
let v = Version::ONE;
|
let v = Version::ONE;
|
||||||
@@ -232,7 +237,7 @@ impl TxRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/tx/{txid}/raw",
|
"/api/tx/{txid}/raw",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, Path(param): Path<TxidParam>, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, Path(param): Path<TxidParam>, _: Empty, State(state): State<AppState>| {
|
||||||
state.cached_bytes(&headers, state.tx_cache(Version::ONE, ¶m.txid), &uri, move |q| q.transaction_raw(¶m.txid)).await
|
state.cached_bytes(&headers, state.tx_cache(Version::ONE, ¶m.txid), &uri, move |q| q.transaction_raw(¶m.txid)).await
|
||||||
},
|
},
|
||||||
|op| op
|
|op| op
|
||||||
@@ -254,6 +259,7 @@ impl TxRoutes for ApiRouter<AppState> {
|
|||||||
uri: Uri,
|
uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
Path(param): Path<TxidParam>,
|
Path(param): Path<TxidParam>,
|
||||||
|
_: Empty,
|
||||||
State(state): State<AppState>
|
State(state): State<AppState>
|
||||||
| {
|
| {
|
||||||
state.cached_json(&headers, state.tx_cache(Version::ONE, ¶m.txid), &uri, move |q| q.transaction_status(¶m.txid)).await
|
state.cached_json(&headers, state.tx_cache(Version::ONE, ¶m.txid), &uri, move |q| q.transaction_status(¶m.txid)).await
|
||||||
@@ -275,9 +281,10 @@ impl TxRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/v1/transaction-times",
|
"/api/v1/transaction-times",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| -> Result<Response> {
|
||||||
let params = TxidsParam::from_query(uri.query().unwrap_or(""));
|
let params = TxidsParam::from_query(uri.query().unwrap_or(""))
|
||||||
state.cached_json(&headers, state.mempool_cache(), &uri, move |q| q.transaction_times(¶ms.txids)).await
|
.map_err(Error::bad_request)?;
|
||||||
|
Ok(state.cached_json(&headers, state.mempool_cache(), &uri, move |q| q.transaction_times(¶ms.txids)).await)
|
||||||
},
|
},
|
||||||
|op| op
|
|op| op
|
||||||
.id("get_transaction_times")
|
.id("get_transaction_times")
|
||||||
@@ -292,12 +299,12 @@ impl TxRoutes for ApiRouter<AppState> {
|
|||||||
.api_route(
|
.api_route(
|
||||||
"/api/tx",
|
"/api/tx",
|
||||||
post_with(
|
post_with(
|
||||||
async |State(state): State<AppState>, body: String| {
|
async |_: Empty, State(state): State<AppState>, body: String| {
|
||||||
let hex = body.trim().to_string();
|
let hex = body.trim().to_string();
|
||||||
state.run(move |q| q.broadcast_transaction(&hex))
|
state.run(move |q| q.broadcast_transaction(&hex))
|
||||||
.await
|
.await
|
||||||
.map(|txid| txid.to_string())
|
.map(|txid| txid.to_string())
|
||||||
.map_err(crate::Error::from)
|
.map_err(Error::from)
|
||||||
},
|
},
|
||||||
|op| {
|
|op| {
|
||||||
op.id("post_tx")
|
op.id("post_tx")
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use brk_types::{Cohort, Date, Urpd, Version};
|
|||||||
use crate::{
|
use crate::{
|
||||||
CacheStrategy,
|
CacheStrategy,
|
||||||
extended::TransformResponseExtended,
|
extended::TransformResponseExtended,
|
||||||
params::{UrpdCohortParam, UrpdParams, UrpdQuery},
|
params::{Empty, UrpdCohortParam, UrpdParams, UrpdQuery},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::AppState;
|
use super::AppState;
|
||||||
@@ -22,7 +22,7 @@ impl ApiUrpdRoutes for ApiRouter<AppState> {
|
|||||||
self.api_route(
|
self.api_route(
|
||||||
"/api/urpd",
|
"/api/urpd",
|
||||||
get_with(
|
get_with(
|
||||||
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
|
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
|
||||||
state
|
state
|
||||||
.cached_json(&headers, CacheStrategy::Deploy, &uri, |q| q.urpd_cohorts())
|
.cached_json(&headers, CacheStrategy::Deploy, &uri, |q| q.urpd_cohorts())
|
||||||
.await
|
.await
|
||||||
@@ -47,6 +47,7 @@ impl ApiUrpdRoutes for ApiRouter<AppState> {
|
|||||||
async |uri: Uri,
|
async |uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
Path(params): Path<UrpdCohortParam>,
|
Path(params): Path<UrpdCohortParam>,
|
||||||
|
_: Empty,
|
||||||
State(state): State<AppState>| {
|
State(state): State<AppState>| {
|
||||||
state
|
state
|
||||||
.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| {
|
.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| {
|
||||||
|
|||||||
6
crates/brk_server/src/cache/mode.rs
vendored
6
crates/brk_server/src/cache/mode.rs
vendored
@@ -52,9 +52,5 @@ pub(crate) fn init(mode: CdnCacheMode) {
|
|||||||
/// Cached-tier directive for stable responses. Defaults to `Live` if [`init`]
|
/// Cached-tier directive for stable responses. Defaults to `Live` if [`init`]
|
||||||
/// was never called (tests, library use without a `Server`).
|
/// was never called (tests, library use without a `Server`).
|
||||||
pub(super) fn cdn_cached() -> &'static str {
|
pub(super) fn cdn_cached() -> &'static str {
|
||||||
CDN_CACHE_MODE
|
CDN_CACHE_MODE.get().copied().unwrap_or_default().as_str()
|
||||||
.get()
|
|
||||||
.copied()
|
|
||||||
.unwrap_or_default()
|
|
||||||
.as_str()
|
|
||||||
}
|
}
|
||||||
|
|||||||
27
crates/brk_server/src/params/empty.rs
Normal file
27
crates/brk_server/src/params/empty.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
use aide::OperationInput;
|
||||||
|
use axum::{extract::FromRequestParts, http::request::Parts};
|
||||||
|
|
||||||
|
use crate::Error;
|
||||||
|
|
||||||
|
/// Extractor that rejects requests carrying any query string.
|
||||||
|
/// Used on path-only endpoints to prevent cache-busting via injected
|
||||||
|
/// query params (the cache key includes the URI).
|
||||||
|
pub struct Empty;
|
||||||
|
|
||||||
|
impl<S> FromRequestParts<S> for Empty
|
||||||
|
where
|
||||||
|
S: Send + Sync,
|
||||||
|
{
|
||||||
|
type Rejection = Error;
|
||||||
|
|
||||||
|
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
|
||||||
|
match parts.uri.query() {
|
||||||
|
Some(q) if !q.is_empty() => Err(Error::bad_request(format!(
|
||||||
|
"this endpoint does not accept query parameters (got `?{q}`)"
|
||||||
|
))),
|
||||||
|
_ => Ok(Empty),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OperationInput for Empty {}
|
||||||
@@ -4,6 +4,7 @@ mod block_count_param;
|
|||||||
mod blockhash_param;
|
mod blockhash_param;
|
||||||
mod blockhash_start_index;
|
mod blockhash_start_index;
|
||||||
mod blockhash_tx_index;
|
mod blockhash_tx_index;
|
||||||
|
mod empty;
|
||||||
mod height_param;
|
mod height_param;
|
||||||
mod pool_slug_param;
|
mod pool_slug_param;
|
||||||
mod series_param;
|
mod series_param;
|
||||||
@@ -22,6 +23,7 @@ pub use block_count_param::*;
|
|||||||
pub use blockhash_param::*;
|
pub use blockhash_param::*;
|
||||||
pub use blockhash_start_index::*;
|
pub use blockhash_start_index::*;
|
||||||
pub use blockhash_tx_index::*;
|
pub use blockhash_tx_index::*;
|
||||||
|
pub use empty::*;
|
||||||
pub use height_param::*;
|
pub use height_param::*;
|
||||||
pub use pool_slug_param::*;
|
pub use pool_slug_param::*;
|
||||||
pub use series_param::*;
|
pub use series_param::*;
|
||||||
|
|||||||
@@ -13,19 +13,25 @@ pub struct TxidsParam {
|
|||||||
|
|
||||||
impl TxidsParam {
|
impl TxidsParam {
|
||||||
/// Parsed manually from URI since serde_urlencoded doesn't support repeated keys.
|
/// Parsed manually from URI since serde_urlencoded doesn't support repeated keys.
|
||||||
pub fn from_query(query: &str) -> Self {
|
/// Rejects unknown keys to prevent cache-busting via injected query params.
|
||||||
Self {
|
pub fn from_query(query: &str) -> Result<Self, String> {
|
||||||
txids: query
|
if query.is_empty() {
|
||||||
.split('&')
|
return Ok(Self { txids: Vec::new() });
|
||||||
.filter_map(|pair| {
|
|
||||||
let (key, val) = pair.split_once('=')?;
|
|
||||||
if key == "txId[]" || key == "txId%5B%5D" {
|
|
||||||
Txid::from_str(val).ok()
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
}
|
}
|
||||||
|
let mut txids = Vec::new();
|
||||||
|
for pair in query.split('&') {
|
||||||
|
let (key, val) = pair.split_once('=').ok_or_else(|| {
|
||||||
|
format!("malformed query parameter `{pair}`, expected `txId[]=<txid>`")
|
||||||
|
})?;
|
||||||
|
if key == "txId[]" || key == "txId%5B%5D" {
|
||||||
|
let txid = Txid::from_str(val).map_err(|e| format!("invalid txid `{val}`: {e}"))?;
|
||||||
|
txids.push(txid);
|
||||||
|
} else {
|
||||||
|
return Err(format!(
|
||||||
|
"unknown query parameter `{key}`, expected `txId[]`"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Self { txids })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -197,10 +197,9 @@ impl AppState {
|
|||||||
let encoding = ContentEncoding::negotiate(headers);
|
let encoding = ContentEncoding::negotiate(headers);
|
||||||
let cache_key = format!("{}-{}-{}", uri, params.etag, encoding.as_str());
|
let cache_key = format!("{}-{}-{}", uri, params.etag, encoding.as_str());
|
||||||
let result = self
|
let result = self
|
||||||
.get_or_insert(
|
.get_or_insert(&cache_key, async move {
|
||||||
&cache_key,
|
self.run(move |q| f(q, encoding)).await
|
||||||
async move { self.run(move |q| f(q, encoding)).await },
|
})
|
||||||
)
|
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
@@ -296,7 +295,10 @@ impl AppState {
|
|||||||
uri,
|
uri,
|
||||||
params,
|
params,
|
||||||
|h| {
|
|h| {
|
||||||
h.insert(header::CONTENT_TYPE, HeaderValue::from_static("application/json"));
|
h.insert(
|
||||||
|
header::CONTENT_TYPE,
|
||||||
|
HeaderValue::from_static("application/json"),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
move |_q, enc| {
|
move |_q, enc| {
|
||||||
let value = value_result?;
|
let value = value_result?;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user