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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -789,6 +789,13 @@
|
||||
* @property {number} blockCount - Total blocks in the time period
|
||||
* @property {number} lastEstimatedHashrate - Estimated network hashrate (hashes per second)
|
||||
*/
|
||||
/**
|
||||
* Current price response matching mempool.space /api/v1/prices format
|
||||
*
|
||||
* @typedef {Object} Prices
|
||||
* @property {Timestamp} time
|
||||
* @property {Dollars} uSD
|
||||
*/
|
||||
/**
|
||||
* A range boundary: integer index, date, or timestamp.
|
||||
*
|
||||
@@ -1063,10 +1070,6 @@
|
||||
* @property {Txid} txid - Transaction ID
|
||||
* @property {Vout} vout - Output index
|
||||
*/
|
||||
/**
|
||||
* @typedef {Object} TxidsParam
|
||||
* @property {Txid[]} txId[]
|
||||
*/
|
||||
/**
|
||||
* Index within its type (e.g., 0 for first P2WPKH address)
|
||||
*
|
||||
@@ -10098,6 +10101,22 @@ class BrkClient extends BrkClientBase {
|
||||
return this.getJson(`/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`
|
||||
*
|
||||
* @param {Txid} txid
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async getTxMerkleblockProof(txid) {
|
||||
return this.getJson(`/api/tx/${txid}/merkleblock-proof`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Output spend status
|
||||
*
|
||||
@@ -10582,6 +10601,20 @@ class BrkClient extends BrkClientBase {
|
||||
return this.getJson(`/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`
|
||||
* @returns {Promise<Prices>}
|
||||
*/
|
||||
async getPrices() {
|
||||
return this.getJson(`/api/v1/prices`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transaction first-seen times
|
||||
*
|
||||
@@ -10590,16 +10623,10 @@ class BrkClient extends BrkClientBase {
|
||||
* *[Mempool.space docs](https://mempool.space/docs/api/rest#get-transaction-times)*
|
||||
*
|
||||
* Endpoint: `GET /api/v1/transaction-times`
|
||||
*
|
||||
* @param {Txid[]} [txId[]]
|
||||
* @returns {Promise<number[]>}
|
||||
*/
|
||||
async getTransactionTimes(txId) {
|
||||
const params = new URLSearchParams();
|
||||
params.set('txId[]', String(txId));
|
||||
const query = params.toString();
|
||||
const path = `/api/v1/transaction-times${query ? '?' + query : ''}`;
|
||||
return this.getJson(path);
|
||||
async getTransactionTimes() {
|
||||
return this.getJson(`/api/v1/transaction-times`);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1078,6 +1078,13 @@ class PoolsSummary(TypedDict):
|
||||
blockCount: int
|
||||
lastEstimatedHashrate: int
|
||||
|
||||
class Prices(TypedDict):
|
||||
"""
|
||||
Current price response matching mempool.space /api/v1/prices format
|
||||
"""
|
||||
time: Timestamp
|
||||
USD: Dollars
|
||||
|
||||
class RecommendedFees(TypedDict):
|
||||
"""
|
||||
Recommended fee rates in sat/vB
|
||||
@@ -1314,9 +1321,6 @@ class TxidVout(TypedDict):
|
||||
txid: Txid
|
||||
vout: Vout
|
||||
|
||||
class TxidsParam(TypedDict):
|
||||
txId: List[Txid]
|
||||
|
||||
class Utxo(TypedDict):
|
||||
"""
|
||||
Unspent transaction output
|
||||
@@ -7495,6 +7499,16 @@ class BrkClient(BrkClientBase):
|
||||
Endpoint: `GET /api/tx/{txid}/merkle-proof`"""
|
||||
return self.get_json(f'/api/tx/{txid}/merkle-proof')
|
||||
|
||||
def get_tx_merkleblock_proof(self, txid: Txid) -> str:
|
||||
"""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`"""
|
||||
return self.get_json(f'/api/tx/{txid}/merkleblock-proof')
|
||||
|
||||
def get_tx_outspend(self, txid: Txid, vout: Vout) -> TxOutspend:
|
||||
"""Output spend status.
|
||||
|
||||
@@ -7809,7 +7823,17 @@ class BrkClient(BrkClientBase):
|
||||
Endpoint: `GET /api/v1/mining/reward-stats/{block_count}`"""
|
||||
return self.get_json(f'/api/v1/mining/reward-stats/{block_count}')
|
||||
|
||||
def get_transaction_times(self, txId: List[Txid]) -> List[float]:
|
||||
def get_prices(self) -> Prices:
|
||||
"""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`"""
|
||||
return self.get_json('/api/v1/prices')
|
||||
|
||||
def get_transaction_times(self) -> List[float]:
|
||||
"""Transaction first-seen times.
|
||||
|
||||
Returns timestamps when transactions were first seen in the mempool. Returns 0 for mined or unknown transactions.
|
||||
@@ -7817,11 +7841,7 @@ class BrkClient(BrkClientBase):
|
||||
*[Mempool.space docs](https://mempool.space/docs/api/rest#get-transaction-times)*
|
||||
|
||||
Endpoint: `GET /api/v1/transaction-times`"""
|
||||
params = []
|
||||
params.append(f'txId[]={txId}')
|
||||
query = '&'.join(params)
|
||||
path = f'/api/v1/transaction-times{"?" + query if query else ""}'
|
||||
return self.get_json(path)
|
||||
return self.get_json('/api/v1/transaction-times')
|
||||
|
||||
def validate_address(self, address: str) -> AddrValidation:
|
||||
"""Validate address.
|
||||
|
||||
Reference in New Issue
Block a user