mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-24 06:39:58 -07:00
global: snapshot
This commit is contained in:
@@ -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(¶m.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!(\"{}={{}}\", {}));",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(¶ms.txids)).await
|
||||
},
|
||||
|op| op
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user