mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-05-19 14:24: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,18 +89,14 @@ 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()
|
|
||||||
.chain(std::iter::once((
|
|
||||||
OutputType::OpReturn,
|
OutputType::OpReturn,
|
||||||
&self.unspendable.op_return,
|
&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()
|
|
||||||
.chain(std::iter::once((
|
|
||||||
OutputType::OpReturn,
|
OutputType::OpReturn,
|
||||||
&mut self.unspendable.op_return,
|
&mut self.unspendable.op_return,
|
||||||
)))
|
)))
|
||||||
|
|||||||
@@ -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,9 +92,8 @@ 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| {
|
||||||
@@ -108,18 +107,15 @@ impl AddrEventsVecs {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
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,7 +225,8 @@ 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
|
||||||
|
.compute_count_ratio(
|
||||||
&self.output_to_reused_addr_count.all,
|
&self.output_to_reused_addr_count.all,
|
||||||
&outputs_by_type.spendable_output_count,
|
&outputs_by_type.spendable_output_count,
|
||||||
starting_indexes.height,
|
starting_indexes.height,
|
||||||
@@ -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,8 +76,7 @@ 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,
|
||||||
|
|||||||
@@ -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,9 +16,8 @@ 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"),
|
||||||
@@ -28,9 +25,8 @@ impl Vecs {
|
|||||||
indexes,
|
indexes,
|
||||||
cached_starts,
|
cached_starts,
|
||||||
)?;
|
)?;
|
||||||
let tx_count = WithInputTypes::<
|
let tx_count =
|
||||||
PerBlockCumulativeRolling<StoredU64, StoredU64>,
|
WithInputTypes::<PerBlockCumulativeRolling<StoredU64, StoredU64>>::forced_import_with(
|
||||||
>::forced_import_with(
|
|
||||||
db,
|
db,
|
||||||
"non_coinbase_tx_count",
|
"non_coinbase_tx_count",
|
||||||
|t| format!("tx_count_with_{t}_prevout"),
|
|t| format!("tx_count_with_{t}_prevout"),
|
||||||
|
|||||||
@@ -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,9 +16,8 @@ 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"),
|
||||||
@@ -26,9 +25,8 @@ impl Vecs {
|
|||||||
indexes,
|
indexes,
|
||||||
cached_starts,
|
cached_starts,
|
||||||
)?;
|
)?;
|
||||||
let tx_count = WithOutputTypes::<
|
let tx_count =
|
||||||
PerBlockCumulativeRolling<StoredU64, StoredU64>,
|
WithOutputTypes::<PerBlockCumulativeRolling<StoredU64, StoredU64>>::forced_import_with(
|
||||||
>::forced_import_with(
|
|
||||||
db,
|
db,
|
||||||
"tx_count_bis",
|
"tx_count_bis",
|
||||||
|t| format!("tx_count_with_{t}_output"),
|
|t| format!("tx_count_with_{t}_output"),
|
||||||
|
|||||||
@@ -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,10 +37,7 @@ 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]),
|
|
||||||
TxidPrefix::from(&txids[p2]),
|
|
||||||
]
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,8 +190,7 @@ 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)
|
||||||
|
|||||||
@@ -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('=')?;
|
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" {
|
if key == "txId[]" || key == "txId%5B%5D" {
|
||||||
Txid::from_str(val).ok()
|
let txid = Txid::from_str(val).map_err(|e| format!("invalid txid `{val}`: {e}"))?;
|
||||||
|
txids.push(txid);
|
||||||
} else {
|
} else {
|
||||||
None
|
return Err(format!(
|
||||||
}
|
"unknown query parameter `{key}`, expected `txId[]`"
|
||||||
})
|
));
|
||||||
.collect(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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
@@ -1,56 +0,0 @@
|
|||||||
use schemars::JsonSchema;
|
|
||||||
use serde::Deserialize;
|
|
||||||
|
|
||||||
use crate::{Limit, RangeIndex, de_unquote_limit};
|
|
||||||
|
|
||||||
/// Range parameters for slicing data
|
|
||||||
#[derive(Default, Debug, Deserialize, JsonSchema)]
|
|
||||||
pub struct DataRange {
|
|
||||||
/// Inclusive start: integer index, date (YYYY-MM-DD), or timestamp (ISO 8601). Negative integers count from end. Aliases: `from`, `f`, `s`
|
|
||||||
#[serde(default, alias = "s", alias = "from", alias = "f")]
|
|
||||||
start: Option<RangeIndex>,
|
|
||||||
|
|
||||||
/// Exclusive end: integer index, date (YYYY-MM-DD), or timestamp (ISO 8601). Negative integers count from end. Aliases: `to`, `t`, `e`
|
|
||||||
#[serde(default, alias = "e", alias = "to", alias = "t")]
|
|
||||||
end: Option<RangeIndex>,
|
|
||||||
|
|
||||||
/// Maximum number of values to return (ignored if `end` is set). Aliases: `count`, `c`, `l`
|
|
||||||
#[serde(
|
|
||||||
default,
|
|
||||||
alias = "l",
|
|
||||||
alias = "count",
|
|
||||||
alias = "c",
|
|
||||||
deserialize_with = "de_unquote_limit"
|
|
||||||
)]
|
|
||||||
limit: Option<Limit>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DataRange {
|
|
||||||
pub fn set_start(mut self, start: i64) -> Self {
|
|
||||||
self.start.replace(RangeIndex::Int(start));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_end(mut self, end: i64) -> Self {
|
|
||||||
self.end.replace(RangeIndex::Int(end));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_limit(mut self, limit: Limit) -> Self {
|
|
||||||
self.limit.replace(limit);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn start(&self) -> Option<RangeIndex> {
|
|
||||||
self.start
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn end(&self) -> Option<RangeIndex> {
|
|
||||||
self.end
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn limit(&self) -> Option<Limit> {
|
|
||||||
self.limit
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user