global: snapshot

This commit is contained in:
nym21
2026-04-01 15:50:49 +02:00
parent 8782944191
commit 96f2e058f7
10 changed files with 185 additions and 36 deletions

View File

@@ -144,7 +144,15 @@ pub fn generate_api_methods(output: &mut String, endpoints: &[Endpoint]) {
writeln!(output, " let mut query = Vec::new();").unwrap();
for param in &endpoint.query_params {
let ident = sanitize_ident(&param.name);
if param.required {
let is_array = param.param_type.ends_with("[]");
if is_array {
writeln!(
output,
" for v in {} {{ query.push(format!(\"{}={{}}\", v)); }}",
ident, param.name
)
.unwrap();
} else if param.required {
writeln!(
output,
" query.push(format!(\"{}={{}}\", {}));",

View File

@@ -8734,6 +8734,17 @@ impl BrkClient {
self.base.get_json(&format!("/api/tx/{txid}/merkle-proof"))
}
/// Transaction merkleblock proof
///
/// Get the merkleblock proof for a transaction (BIP37 format, hex encoded).
///
/// *[Mempool.space docs](https://mempool.space/docs/api/rest#get-transaction-merkleblock-proof)*
///
/// Endpoint: `GET /api/tx/{txid}/merkleblock-proof`
pub fn get_tx_merkleblock_proof(&self, txid: Txid) -> Result<String> {
self.base.get_json(&format!("/api/tx/{txid}/merkleblock-proof"))
}
/// Output spend status
///
/// Get the spending status of a transaction output. Returns whether the output has been spent and, if so, the spending transaction details.
@@ -9079,6 +9090,17 @@ impl BrkClient {
self.base.get_json(&format!("/api/v1/mining/reward-stats/{block_count}"))
}
/// Current BTC price
///
/// Returns bitcoin latest price (on-chain derived, USD only).
///
/// *[Mempool.space docs](https://mempool.space/docs/api/rest#get-price)*
///
/// Endpoint: `GET /api/v1/prices`
pub fn get_prices(&self) -> Result<Prices> {
self.base.get_json(&format!("/api/v1/prices"))
}
/// Transaction first-seen times
///
/// Returns timestamps when transactions were first seen in the mempool. Returns 0 for mined or unknown transactions.
@@ -9086,12 +9108,8 @@ impl BrkClient {
/// *[Mempool.space docs](https://mempool.space/docs/api/rest#get-transaction-times)*
///
/// Endpoint: `GET /api/v1/transaction-times`
pub fn get_transaction_times(&self, txId: &[Txid]) -> Result<Vec<f64>> {
let mut query = Vec::new();
query.push(format!("txId[]={}", txId));
let query_str = if query.is_empty() { String::new() } else { format!("?{}", query.join("&")) };
let path = format!("/api/v1/transaction-times{}", query_str);
self.base.get_json(&path)
pub fn get_transaction_times(&self) -> Result<Vec<f64>> {
self.base.get_json(&format!("/api/v1/transaction-times"))
}
/// Validate address

View File

@@ -58,10 +58,10 @@ impl BlockProcessor<'_> {
let mut sw_size = 0usize;
let mut sw_weight = 0usize;
for tx in txs {
for (i, tx) in txs.iter().enumerate() {
total_size += tx.total_size as usize;
weight += tx.weight();
if tx.is_segwit() {
if i > 0 && tx.is_segwit() {
sw_txs += 1;
sw_size += tx.total_size as usize;
sw_weight += tx.weight();

View File

@@ -5,7 +5,7 @@ use axum::{
};
use brk_types::{
Dollars, HistoricalPrice, MempoolBlock, MempoolInfo, MempoolRecentTx, OptionalTimestampParam,
RecommendedFees, Txid,
Prices, RecommendedFees, Timestamp, Txid,
};
use crate::{CacheStrategy, extended::TransformResponseExtended};
@@ -67,6 +67,27 @@ impl MempoolRoutes for ApiRouter<AppState> {
},
),
)
.api_route(
"/api/v1/prices",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
state.cached_json(&headers, state.mempool_cache(), &uri, |q| {
Ok(Prices {
time: Timestamp::now(),
usd: q.live_price()?,
})
}).await
},
|op| {
op.id("get_prices")
.mempool_tag()
.summary("Current BTC price")
.description("Returns bitcoin latest price (on-chain derived, USD only).\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-price)*")
.ok_response::<Prices>()
.server_error()
},
),
)
.api_route(
"/api/mempool/price",
get_with(

View File

@@ -6,7 +6,6 @@ use axum::{
extract::{Path, State},
http::{HeaderMap, Uri},
};
use axum::extract::Query;
use brk_types::{
CpfpInfo, Hex, MerkleProof, Transaction, TxOutspend, TxStatus, Txid, TxidParam, TxidVout,
TxidsParam,
@@ -169,6 +168,24 @@ impl TxRoutes for ApiRouter<AppState> {
},
),
)
.api_route(
"/api/tx/{txid}/merkleblock-proof",
get_with(
async |uri: Uri, headers: HeaderMap, Path(txid): Path<TxidParam>, State(state): State<AppState>| {
state.cached_text(&headers, CacheStrategy::Height, &uri, move |q| q.merkleblock_proof(txid)).await
},
|op| op
.id("get_tx_merkleblock_proof")
.transactions_tag()
.summary("Transaction merkleblock proof")
.description("Get the merkleblock proof for a transaction (BIP37 format, hex encoded).\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-transaction-merkleblock-proof)*")
.ok_response::<String>()
.not_modified()
.bad_request()
.not_found()
.server_error(),
),
)
.api_route(
"/api/tx/{txid}/raw",
get_with(
@@ -224,7 +241,8 @@ impl TxRoutes for ApiRouter<AppState> {
.api_route(
"/api/v1/transaction-times",
get_with(
async |uri: Uri, headers: HeaderMap, Query(params): Query<TxidsParam>, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
let params = TxidsParam::from_query(uri.query().unwrap_or(""));
state.cached_json(&headers, CacheStrategy::MempoolHash(0), &uri, move |q| q.transaction_times(&params.txids)).await
},
|op| op

View File

@@ -1,7 +1,15 @@
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::Dollars;
use crate::{Dollars, Timestamp};
/// Current price response matching mempool.space /api/v1/prices format
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct Prices {
pub time: Timestamp,
#[serde(rename = "USD")]
pub usd: Dollars,
}
/// Historical price response
#[derive(Debug, Serialize, Deserialize, JsonSchema)]

View File

@@ -63,6 +63,14 @@ impl fmt::Display for Txid {
}
}
impl FromStr for Txid {
type Err = bitcoin::hashes::hex::HexToArrayError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
bitcoin::Txid::from_str(s).map(Self::from)
}
}
impl Serialize for Txid {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where

View File

@@ -1,10 +1,31 @@
use std::str::FromStr;
use schemars::JsonSchema;
use serde::Deserialize;
use crate::Txid;
#[derive(Deserialize, JsonSchema)]
/// Query parameter for transaction-times endpoint.
#[derive(JsonSchema)]
pub struct TxidsParam {
#[serde(rename = "txId[]")]
pub txids: Vec<Txid>,
}
impl TxidsParam {
/// Parsed manually from URI since serde_urlencoded doesn't support repeated keys.
pub fn from_query(query: &str) -> Self {
Self {
txids: query
.split('&')
.filter_map(|pair| {
let (key, val) = pair.split_once('=')?;
if key == "txId[]" || key == "txId%5B%5D" {
Txid::from_str(val).ok()
} else {
None
}
})
.collect(),
}
}
}