global: snap

This commit is contained in:
nym21
2026-04-12 18:00:02 +02:00
parent 18d9c166d8
commit c3cef71aa3
36 changed files with 2366 additions and 371 deletions

View File

@@ -88,13 +88,14 @@ pub fn generate_api_methods(output: &mut String, endpoints: &[Endpoint]) {
let path = build_path_template(&endpoint.path, &endpoint.path_params);
let fetch_call = if endpoint.returns_json() {
"this.getJson(path, { signal, onUpdate })"
} else {
"this.getText(path, { signal })"
};
if endpoint.query_params.is_empty() {
writeln!(
output,
" return this.getJson(`{}`, {{ signal, onUpdate }});",
path
)
.unwrap();
writeln!(output, " const path = `{}`;", path).unwrap();
} else {
writeln!(output, " const params = new URLSearchParams();").unwrap();
for param in &endpoint.query_params {
@@ -122,25 +123,13 @@ pub fn generate_api_methods(output: &mut String, endpoints: &[Endpoint]) {
path
)
.unwrap();
if endpoint.supports_csv {
writeln!(output, " if (format === 'csv') {{").unwrap();
writeln!(output, " return this.getText(path, {{ signal }});").unwrap();
writeln!(output, " }}").unwrap();
writeln!(
output,
" return this.getJson(path, {{ signal, onUpdate }});"
)
.unwrap();
} else {
writeln!(
output,
" return this.getJson(path, {{ signal, onUpdate }});"
)
.unwrap();
}
}
if endpoint.supports_csv {
writeln!(output, " if (format === 'csv') return this.getText(path, {{ signal }});").unwrap();
}
writeln!(output, " return {};", fetch_call).unwrap();
writeln!(output, " }}\n").unwrap();
}
}

View File

@@ -4799,7 +4799,6 @@ impl SeriesTree_Indexes_Addr_OpReturn {
/// Series tree node.
pub struct SeriesTree_Indexes_Height {
pub identity: SeriesPattern18<Height>,
pub minute10: SeriesPattern18<Minute10>,
pub minute30: SeriesPattern18<Minute30>,
pub hour1: SeriesPattern18<Hour1>,
@@ -4821,7 +4820,6 @@ pub struct SeriesTree_Indexes_Height {
impl SeriesTree_Indexes_Height {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
identity: SeriesPattern18::new(client.clone(), "height".to_string()),
minute10: SeriesPattern18::new(client.clone(), "minute10".to_string()),
minute30: SeriesPattern18::new(client.clone(), "minute30".to_string()),
hour1: SeriesPattern18::new(client.clone(), "hour1".to_string()),
@@ -4844,31 +4842,25 @@ impl SeriesTree_Indexes_Height {
/// Series tree node.
pub struct SeriesTree_Indexes_Epoch {
pub identity: SeriesPattern17<Epoch>,
pub first_height: SeriesPattern17<Height>,
pub height_count: SeriesPattern17<StoredU64>,
}
impl SeriesTree_Indexes_Epoch {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
identity: SeriesPattern17::new(client.clone(), "epoch".to_string()),
first_height: SeriesPattern17::new(client.clone(), "first_height".to_string()),
height_count: SeriesPattern17::new(client.clone(), "height_count".to_string()),
}
}
}
/// Series tree node.
pub struct SeriesTree_Indexes_Halving {
pub identity: SeriesPattern16<Halving>,
pub first_height: SeriesPattern16<Height>,
}
impl SeriesTree_Indexes_Halving {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
identity: SeriesPattern16::new(client.clone(), "halving".to_string()),
first_height: SeriesPattern16::new(client.clone(), "first_height".to_string()),
}
}
@@ -4876,14 +4868,12 @@ impl SeriesTree_Indexes_Halving {
/// Series tree node.
pub struct SeriesTree_Indexes_Minute10 {
pub identity: SeriesPattern3<Minute10>,
pub first_height: SeriesPattern3<Height>,
}
impl SeriesTree_Indexes_Minute10 {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
identity: SeriesPattern3::new(client.clone(), "minute10_index".to_string()),
first_height: SeriesPattern3::new(client.clone(), "first_height".to_string()),
}
}
@@ -4891,14 +4881,12 @@ impl SeriesTree_Indexes_Minute10 {
/// Series tree node.
pub struct SeriesTree_Indexes_Minute30 {
pub identity: SeriesPattern4<Minute30>,
pub first_height: SeriesPattern4<Height>,
}
impl SeriesTree_Indexes_Minute30 {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
identity: SeriesPattern4::new(client.clone(), "minute30_index".to_string()),
first_height: SeriesPattern4::new(client.clone(), "first_height".to_string()),
}
}
@@ -4906,14 +4894,12 @@ impl SeriesTree_Indexes_Minute30 {
/// Series tree node.
pub struct SeriesTree_Indexes_Hour1 {
pub identity: SeriesPattern5<Hour1>,
pub first_height: SeriesPattern5<Height>,
}
impl SeriesTree_Indexes_Hour1 {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
identity: SeriesPattern5::new(client.clone(), "hour1_index".to_string()),
first_height: SeriesPattern5::new(client.clone(), "first_height".to_string()),
}
}
@@ -4921,14 +4907,12 @@ impl SeriesTree_Indexes_Hour1 {
/// Series tree node.
pub struct SeriesTree_Indexes_Hour4 {
pub identity: SeriesPattern6<Hour4>,
pub first_height: SeriesPattern6<Height>,
}
impl SeriesTree_Indexes_Hour4 {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
identity: SeriesPattern6::new(client.clone(), "hour4_index".to_string()),
first_height: SeriesPattern6::new(client.clone(), "first_height".to_string()),
}
}
@@ -4936,14 +4920,12 @@ impl SeriesTree_Indexes_Hour4 {
/// Series tree node.
pub struct SeriesTree_Indexes_Hour12 {
pub identity: SeriesPattern7<Hour12>,
pub first_height: SeriesPattern7<Height>,
}
impl SeriesTree_Indexes_Hour12 {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
identity: SeriesPattern7::new(client.clone(), "hour12_index".to_string()),
first_height: SeriesPattern7::new(client.clone(), "first_height".to_string()),
}
}
@@ -4951,33 +4933,29 @@ impl SeriesTree_Indexes_Hour12 {
/// Series tree node.
pub struct SeriesTree_Indexes_Day1 {
pub identity: SeriesPattern8<Day1>,
pub date: SeriesPattern8<Date>,
pub first_height: SeriesPattern8<Height>,
pub height_count: SeriesPattern8<StoredU64>,
}
impl SeriesTree_Indexes_Day1 {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
identity: SeriesPattern8::new(client.clone(), "day1_index".to_string()),
date: SeriesPattern8::new(client.clone(), "date".to_string()),
first_height: SeriesPattern8::new(client.clone(), "first_height".to_string()),
height_count: SeriesPattern8::new(client.clone(), "height_count".to_string()),
}
}
}
/// Series tree node.
pub struct SeriesTree_Indexes_Day3 {
pub identity: SeriesPattern9<Day3>,
pub date: SeriesPattern9<Date>,
pub first_height: SeriesPattern9<Height>,
}
impl SeriesTree_Indexes_Day3 {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
identity: SeriesPattern9::new(client.clone(), "day3_index".to_string()),
date: SeriesPattern9::new(client.clone(), "date".to_string()),
first_height: SeriesPattern9::new(client.clone(), "first_height".to_string()),
}
}
@@ -4985,7 +4963,6 @@ impl SeriesTree_Indexes_Day3 {
/// Series tree node.
pub struct SeriesTree_Indexes_Week1 {
pub identity: SeriesPattern10<Week1>,
pub date: SeriesPattern10<Date>,
pub first_height: SeriesPattern10<Height>,
}
@@ -4993,7 +4970,6 @@ pub struct SeriesTree_Indexes_Week1 {
impl SeriesTree_Indexes_Week1 {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
identity: SeriesPattern10::new(client.clone(), "week1_index".to_string()),
date: SeriesPattern10::new(client.clone(), "date".to_string()),
first_height: SeriesPattern10::new(client.clone(), "first_height".to_string()),
}
@@ -5002,7 +4978,6 @@ impl SeriesTree_Indexes_Week1 {
/// Series tree node.
pub struct SeriesTree_Indexes_Month1 {
pub identity: SeriesPattern11<Month1>,
pub date: SeriesPattern11<Date>,
pub first_height: SeriesPattern11<Height>,
}
@@ -5010,7 +4985,6 @@ pub struct SeriesTree_Indexes_Month1 {
impl SeriesTree_Indexes_Month1 {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
identity: SeriesPattern11::new(client.clone(), "month1_index".to_string()),
date: SeriesPattern11::new(client.clone(), "date".to_string()),
first_height: SeriesPattern11::new(client.clone(), "first_height".to_string()),
}
@@ -5019,7 +4993,6 @@ impl SeriesTree_Indexes_Month1 {
/// Series tree node.
pub struct SeriesTree_Indexes_Month3 {
pub identity: SeriesPattern12<Month3>,
pub date: SeriesPattern12<Date>,
pub first_height: SeriesPattern12<Height>,
}
@@ -5027,7 +5000,6 @@ pub struct SeriesTree_Indexes_Month3 {
impl SeriesTree_Indexes_Month3 {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
identity: SeriesPattern12::new(client.clone(), "month3_index".to_string()),
date: SeriesPattern12::new(client.clone(), "date".to_string()),
first_height: SeriesPattern12::new(client.clone(), "first_height".to_string()),
}
@@ -5036,7 +5008,6 @@ impl SeriesTree_Indexes_Month3 {
/// Series tree node.
pub struct SeriesTree_Indexes_Month6 {
pub identity: SeriesPattern13<Month6>,
pub date: SeriesPattern13<Date>,
pub first_height: SeriesPattern13<Height>,
}
@@ -5044,7 +5015,6 @@ pub struct SeriesTree_Indexes_Month6 {
impl SeriesTree_Indexes_Month6 {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
identity: SeriesPattern13::new(client.clone(), "month6_index".to_string()),
date: SeriesPattern13::new(client.clone(), "date".to_string()),
first_height: SeriesPattern13::new(client.clone(), "first_height".to_string()),
}
@@ -5053,7 +5023,6 @@ impl SeriesTree_Indexes_Month6 {
/// Series tree node.
pub struct SeriesTree_Indexes_Year1 {
pub identity: SeriesPattern14<Year1>,
pub date: SeriesPattern14<Date>,
pub first_height: SeriesPattern14<Height>,
}
@@ -5061,7 +5030,6 @@ pub struct SeriesTree_Indexes_Year1 {
impl SeriesTree_Indexes_Year1 {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
identity: SeriesPattern14::new(client.clone(), "year1_index".to_string()),
date: SeriesPattern14::new(client.clone(), "date".to_string()),
first_height: SeriesPattern14::new(client.clone(), "first_height".to_string()),
}
@@ -5070,7 +5038,6 @@ impl SeriesTree_Indexes_Year1 {
/// Series tree node.
pub struct SeriesTree_Indexes_Year10 {
pub identity: SeriesPattern15<Year10>,
pub date: SeriesPattern15<Date>,
pub first_height: SeriesPattern15<Height>,
}
@@ -5078,7 +5045,6 @@ pub struct SeriesTree_Indexes_Year10 {
impl SeriesTree_Indexes_Year10 {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
identity: SeriesPattern15::new(client.clone(), "year10_index".to_string()),
date: SeriesPattern15::new(client.clone(), "date".to_string()),
first_height: SeriesPattern15::new(client.clone(), "first_height".to_string()),
}
@@ -8712,6 +8678,15 @@ impl BrkClient {
self.base.get_json(&format!("/api/server/sync"))
}
/// Txid by index
///
/// Retrieve the transaction ID (txid) at a given global transaction index. Returns the txid as plain text.
///
/// Endpoint: `GET /api/tx-index/{index}`
pub fn get_tx_by_index(&self, index: TxIndex) -> Result<String> {
self.base.get_text(&format!("/api/tx-index/{index}"))
}
/// Transaction information
///
/// Retrieve complete transaction data by transaction ID (txid). Returns inputs, outputs, fee, size, and confirmation status.

View File

@@ -417,7 +417,11 @@ impl Query {
first_seen: None,
};
blocks.push(BlockInfoV1 { info, extras });
blocks.push(BlockInfoV1 {
info,
stale: false,
extras,
});
}
Ok(blocks)

View File

@@ -3,10 +3,10 @@ use std::{collections::BTreeMap, sync::LazyLock};
use brk_error::{Error, Result};
use brk_traversable::TreeNode;
use brk_types::{
Date, DetailedSeriesCount, Epoch, Etag, Format, Halving, Height, Index, IndexInfo, LegacyValue,
Limit, Output, OutputLegacy, PaginatedSeries, Pagination, PaginationIndex, RangeIndex,
RangeMap, SearchQuery, SeriesData, SeriesInfo, SeriesName, SeriesOutput, SeriesOutputLegacy,
SeriesSelection, Timestamp, Version,
BlockHashPrefix, Date, DetailedSeriesCount, Epoch, Etag, Format, Halving, Height, Index,
IndexInfo, LegacyValue, Limit, Output, OutputLegacy, PaginatedSeries, Pagination,
PaginationIndex, RangeIndex, RangeMap, SearchQuery, SeriesData, SeriesInfo, SeriesName,
SeriesOutput, SeriesOutputLegacy, SeriesSelection, Timestamp, Version,
};
use parking_lot::RwLock;
use vecdb::{AnyExportableVec, ReadableVec};
@@ -204,7 +204,7 @@ impl Query {
total,
start,
end,
height: *self.height(),
hash_prefix: self.tip_hash_prefix(),
})
}
@@ -458,12 +458,12 @@ pub struct ResolvedQuery {
pub total: usize,
pub start: usize,
pub end: usize,
pub height: u32,
pub hash_prefix: BlockHashPrefix,
}
impl ResolvedQuery {
pub fn etag(&self) -> Etag {
Etag::from_series(self.version, self.total, self.start, self.end, self.height)
Etag::from_series(self.version, self.total, self.end, self.hash_prefix)
}
pub fn format(&self) -> Format {

View File

@@ -4,7 +4,7 @@ use brk_types::{
BlockHash, Height, MerkleProof, Timestamp, Transaction, TxInIndex, TxIndex, TxOutIndex,
TxOutspend, TxStatus, Txid, TxidPrefix, Vin, Vout,
};
use vecdb::{ReadableVec, VecIndex};
use vecdb::{AnyVec, ReadableVec, VecIndex};
use crate::Query;
@@ -23,6 +23,19 @@ impl Query {
.ok_or(Error::UnknownTxid)
}
pub fn txid_by_index(&self, index: TxIndex) -> Result<Txid> {
let len = self.indexer().vecs.transactions.txid.len();
if index.to_usize() >= len {
return Err(Error::OutOfRange("Transaction index out of range".into()));
}
self.indexer()
.vecs
.transactions
.txid
.collect_one(index)
.ok_or_else(|| Error::OutOfRange("Transaction index out of range".into()))
}
/// Resolve a txid to (TxIndex, Height).
pub fn resolve_tx(&self, txid: &Txid) -> Result<(TxIndex, Height)> {
let tx_index = self.resolve_tx_index(txid)?;

View File

@@ -12,7 +12,7 @@ use crate::{
AppState, CacheStrategy,
cache::CacheParams,
extended::{ResponseExtended, TransformResponseExtended},
params::{TxidParam, TxidVout, TxidsParam},
params::{TxIndexParam, TxidParam, TxidVout, TxidsParam},
};
pub trait TxRoutes {
@@ -23,6 +23,24 @@ impl TxRoutes for ApiRouter<AppState> {
fn add_tx_routes(self) -> Self {
self
.api_route(
"/api/tx-index/{index}",
get_with(
async |uri: Uri, headers: HeaderMap, Path(param): Path<TxIndexParam>, 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
},
|op| op
.id("get_tx_by_index")
.transactions_tag()
.summary("Txid by index")
.description("Retrieve the transaction ID (txid) at a given global transaction index. Returns the txid as plain text.")
.text_response()
.not_modified()
.bad_request()
.not_found()
.server_error(),
),
)
.api_route(
"/api/v1/cpfp/{txid}",
get_with(
async |uri: Uri, headers: HeaderMap, Path(param): Path<TxidParam>, State(state): State<AppState>| {

View File

@@ -197,7 +197,6 @@ impl Server {
let router = router
.with_state(state)
.merge(website_router)
.layer(compression_layer)
.layer(response_time_layer)
.layer(trace_layer)
.layer(CatchPanicLayer::custom(|panic: Box<dyn Any + Send>| {
@@ -213,6 +212,7 @@ impl Server {
Duration::from_secs(5),
))
.layer(json_error_layer)
.layer(compression_layer)
.layer(CorsLayer::permissive())
.layer(axum::middleware::from_fn(
async |request: Request<Body>, next: Next| -> Response<Body> {

View File

@@ -11,6 +11,7 @@ mod pool_slug_param;
mod series_param;
mod time_period_param;
mod timestamp_param;
mod tx_index_param;
mod txid_param;
mod txid_vout;
mod txids_param;
@@ -29,6 +30,7 @@ pub use pool_slug_param::*;
pub use series_param::*;
pub use time_period_param::*;
pub use timestamp_param::*;
pub use tx_index_param::*;
pub use txid_param::*;
pub use txid_vout::*;
pub use txids_param::*;

View File

@@ -0,0 +1,10 @@
use schemars::JsonSchema;
use serde::Deserialize;
use brk_types::TxIndex;
/// Transaction index path parameter
#[derive(Deserialize, JsonSchema)]
pub struct TxIndexParam {
pub index: TxIndex,
}

View File

@@ -72,7 +72,7 @@ impl AddrValidation {
let output_type = OutputType::from(&script);
let script_hex = script.as_bytes().to_lower_hex_string();
let is_script = matches!(output_type, OutputType::P2SH);
let is_script = matches!(output_type, OutputType::P2SH | OutputType::P2TR);
let is_witness = matches!(
output_type,
OutputType::P2WPKH | OutputType::P2WSH | OutputType::P2TR | OutputType::P2A

View File

@@ -10,6 +10,10 @@ pub struct BlockInfoV1 {
#[serde(flatten)]
pub info: BlockInfo,
/// Whether this block has been replaced by a longer chain
#[serde(default)]
pub stale: bool,
/// Extended block data
pub extras: BlockExtras,
}

View File

@@ -52,4 +52,5 @@ impl DataRange {
pub fn limit(&self) -> Option<Limit> {
self.limit
}
}

View File

@@ -1,5 +1,7 @@
use std::fmt;
use super::{BlockHashPrefix, Version};
/// HTTP ETag value.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Etag(String);
@@ -40,27 +42,20 @@ impl From<&str> for Etag {
}
impl Etag {
/// Create ETag from series data response info.
///
/// Format varies based on whether the slice touches the end:
/// - Slice ends before total: `{version:x}-{start}-{end}` (len irrelevant, data won't change if series grows)
/// - Slice reaches the end: `{version:x}-{start}-{total}-{height}` (includes height since last value may be recomputed each block)
///
/// `version` is the series version for single queries, or the sum of versions for bulk queries.
/// Tail uses hash prefix (changes per-block and on reorgs),
/// non-tail uses total (changes per-block).
pub fn from_series(
version: super::Version,
version: Version,
total: usize,
start: usize,
end: usize,
height: u32,
hash_prefix: BlockHashPrefix,
) -> Self {
let v = u32::from(version);
if end < total {
// Fixed window not at the end - len doesn't matter
Self(format!("{v:x}-{start}-{end}"))
if end >= total {
let h = *hash_prefix;
Self(format!("v{v}-{h:x}"))
} else {
// Fetching up to current end - include height since last value may change each block
Self(format!("{v:x}-{start}-{total}-{height}"))
Self(format!("v{v}-{total}"))
}
}
}