global: snapshot

This commit is contained in:
nym21
2026-04-01 17:51:50 +02:00
parent 96f2e058f7
commit 7172ddb247
17 changed files with 731 additions and 652 deletions

View File

@@ -1,11 +1,8 @@
# BRK CLI
Command-line interface for running a Bitcoin Research Kit instance.
Run your own Bitcoin Research Kit instance. One binary, one command. Full sync in ~4-7h depending on hardware. ~44% disk overhead vs 250% for mempool/electrs.
## Demo
- [bitview.space](https://bitview.space) - web interface
- [bitview.space/api](https://bitview.space/api) - API docs
[bitview.space](https://bitview.space) is the official free hosted instance.
## Requirements

View File

@@ -8454,7 +8454,7 @@ impl BrkClient {
/// Mempool statistics
///
/// Get current mempool statistics including transaction count, total vsize, and total fees.
/// Get current mempool statistics including transaction count, total vsize, total fees, and fee histogram.
///
/// *[Mempool.space docs](https://mempool.space/docs/api/rest#get-mempool)*
///
@@ -8835,7 +8835,7 @@ impl BrkClient {
/// Difficulty adjustment
///
/// Get current difficulty adjustment information including progress through the current epoch, estimated retarget date, and difficulty change prediction.
/// Get current difficulty adjustment progress and estimates.
///
/// *[Mempool.space docs](https://mempool.space/docs/api/rest#get-difficulty-adjustment)*
///
@@ -8846,7 +8846,7 @@ impl BrkClient {
/// Projected mempool blocks
///
/// Get projected blocks from the mempool for fee estimation. Each block contains statistics about transactions that would be included if a block were mined now.
/// Get projected blocks from the mempool for fee estimation.
///
/// *[Mempool.space docs](https://mempool.space/docs/api/rest#get-mempool-blocks-fees)*
///
@@ -8857,7 +8857,7 @@ impl BrkClient {
/// Precise recommended fees
///
/// Get recommended fee rates with up to 3 decimal places, including sub-sat feerates.
/// Get recommended fee rates with up to 3 decimal places.
///
/// *[Mempool.space docs](https://mempool.space/docs/api/rest#get-recommended-fees-precise)*
///
@@ -8868,7 +8868,7 @@ impl BrkClient {
/// Recommended fees
///
/// Get recommended fee rates for different confirmation targets based on current mempool state.
/// Get recommended fee rates for different confirmation targets.
///
/// *[Mempool.space docs](https://mempool.space/docs/api/rest#get-recommended-fees)*
///

View File

@@ -20,6 +20,270 @@ pub trait BlockRoutes {
impl BlockRoutes for ApiRouter<AppState> {
fn add_block_routes(self) -> Self {
self.api_route(
"/api/block/{hash}",
get_with(
async |uri: Uri,
headers: HeaderMap,
Path(path): Path<BlockHashParam>,
State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Static, &uri, move |q| q.block(&path.hash)).await
},
|op| {
op.id("get_block")
.blocks_tag()
.summary("Block information")
.description(
"Retrieve block information by block hash. Returns block metadata including height, timestamp, difficulty, size, weight, and transaction count.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block)*",
)
.ok_response::<BlockInfo>()
.not_modified()
.bad_request()
.not_found()
.server_error()
},
),
)
.api_route(
"/api/v1/block/{hash}",
get_with(
async |uri: Uri, headers: HeaderMap, Path(path): Path<BlockHashParam>, State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Height, &uri, move |q| {
let height = q.height_by_hash(&path.hash)?;
q.block_by_height_v1(height)
}).await
},
|op| {
op.id("get_block_v1")
.blocks_tag()
.summary("Block (v1)")
.description("Returns block details with extras by hash.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-v1)*")
.ok_response::<BlockInfoV1>()
.not_modified()
.not_found()
.server_error()
},
),
)
.api_route(
"/api/block/{hash}/header",
get_with(
async |uri: Uri, headers: HeaderMap, Path(path): Path<BlockHashParam>, State(state): State<AppState>| {
state.cached_text(&headers, CacheStrategy::Height, &uri, move |q| q.block_header_hex(&path.hash)).await
},
|op| {
op.id("get_block_header")
.blocks_tag()
.summary("Block header")
.description("Returns the hex-encoded block header.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-header)*")
.ok_response::<Hex>()
.not_modified()
.not_found()
.server_error()
},
),
)
.api_route(
"/api/block-height/{height}",
get_with(
async |uri: Uri,
headers: HeaderMap,
Path(path): Path<HeightParam>,
State(state): State<AppState>| {
state.cached_text(&headers, CacheStrategy::Height, &uri, move |q| q.block_hash_by_height(path.height).map(|h| h.to_string())).await
},
|op| {
op.id("get_block_by_height")
.blocks_tag()
.summary("Block hash by height")
.description(
"Retrieve the block hash at a given height. Returns the hash as plain text.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-height)*",
)
.ok_response::<BlockHash>()
.not_modified()
.bad_request()
.not_found()
.server_error()
},
),
)
.api_route(
"/api/v1/mining/blocks/timestamp/{timestamp}",
get_with(
async |uri: Uri,
headers: HeaderMap,
Path(path): Path<TimestampParam>,
State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Height, &uri, move |q| q.block_by_timestamp(path.timestamp)).await
},
|op| {
op.id("get_block_by_timestamp")
.blocks_tag()
.summary("Block by timestamp")
.description("Find the block closest to a given UNIX timestamp.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-timestamp)*")
.ok_response::<BlockTimestamp>()
.not_modified()
.bad_request()
.not_found()
.server_error()
},
),
)
.api_route(
"/api/block/{hash}/raw",
get_with(
async |uri: Uri,
headers: HeaderMap,
Path(path): Path<BlockHashParam>,
State(state): State<AppState>| {
state.cached_bytes(&headers, CacheStrategy::Static, &uri, move |q| q.block_raw(&path.hash)).await
},
|op| {
op.id("get_block_raw")
.blocks_tag()
.summary("Raw block")
.description(
"Returns the raw block data in binary format.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-raw)*",
)
.ok_response::<Vec<u8>>()
.not_modified()
.bad_request()
.not_found()
.server_error()
},
),
)
.api_route(
"/api/block/{hash}/status",
get_with(
async |uri: Uri,
headers: HeaderMap,
Path(path): Path<BlockHashParam>,
State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Height, &uri, move |q| q.block_status(&path.hash)).await
},
|op| {
op.id("get_block_status")
.blocks_tag()
.summary("Block status")
.description(
"Retrieve the status of a block. Returns whether the block is in the best chain and, if so, its height and the hash of the next block.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-status)*",
)
.ok_response::<BlockStatus>()
.not_modified()
.bad_request()
.not_found()
.server_error()
},
),
)
.api_route(
"/api/blocks/tip/height",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
state.cached_text(&headers, CacheStrategy::Height, &uri, |q| Ok(q.height().to_string())).await
},
|op| {
op.id("get_block_tip_height")
.blocks_tag()
.summary("Block tip height")
.description("Returns the height of the last block.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-tip-height)*")
.ok_response::<Height>()
.not_modified()
.server_error()
},
),
)
.api_route(
"/api/blocks/tip/hash",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
state.cached_text(&headers, CacheStrategy::Height, &uri, |q| q.block_hash_by_height(q.height()).map(|h| h.to_string())).await
},
|op| {
op.id("get_block_tip_hash")
.blocks_tag()
.summary("Block tip hash")
.description("Returns the hash of the last block.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-tip-hash)*")
.ok_response::<BlockHash>()
.not_modified()
.server_error()
},
),
)
.api_route(
"/api/block/{hash}/txid/{index}",
get_with(
async |uri: Uri,
headers: HeaderMap,
Path(path): Path<BlockHashTxIndex>,
State(state): State<AppState>| {
state.cached_text(&headers, CacheStrategy::Static, &uri, move |q| q.block_txid_at_index(&path.hash, path.index).map(|t| t.to_string())).await
},
|op| {
op.id("get_block_txid")
.blocks_tag()
.summary("Transaction ID at index")
.description(
"Retrieve a single transaction ID at a specific index within a block. Returns plain text txid.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-transaction-id)*",
)
.ok_response::<Txid>()
.not_modified()
.bad_request()
.not_found()
.server_error()
},
),
)
.api_route(
"/api/block/{hash}/txids",
get_with(
async |uri: Uri,
headers: HeaderMap,
Path(path): Path<BlockHashParam>,
State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Static, &uri, move |q| q.block_txids(&path.hash)).await
},
|op| {
op.id("get_block_txids")
.blocks_tag()
.summary("Block transaction IDs")
.description(
"Retrieve all transaction IDs in a block. Returns an array of txids in block order.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-transaction-ids)*",
)
.ok_response::<Vec<Txid>>()
.not_modified()
.bad_request()
.not_found()
.server_error()
},
),
)
.api_route(
"/api/block/{hash}/txs/{start_index}",
get_with(
async |uri: Uri,
headers: HeaderMap,
Path(path): Path<BlockHashStartIndex>,
State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Static, &uri, move |q| q.block_txs(&path.hash, path.start_index)).await
},
|op| {
op.id("get_block_txs")
.blocks_tag()
.summary("Block transactions (paginated)")
.description(&format!(
"Retrieve transactions in a block by block hash, starting from the specified index. Returns up to {} transactions at a time.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-transactions)*",
BLOCK_TXS_PAGE_SIZE
))
.ok_response::<Vec<Transaction>>()
.not_modified()
.bad_request()
.not_found()
.server_error()
},
),
)
.api_route(
"/api/blocks",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
@@ -101,269 +365,5 @@ impl BlockRoutes for ApiRouter<AppState> {
},
),
)
.api_route(
"/api/block-height/{height}",
get_with(
async |uri: Uri,
headers: HeaderMap,
Path(path): Path<HeightParam>,
State(state): State<AppState>| {
state.cached_text(&headers, CacheStrategy::Height, &uri, move |q| q.block_hash_by_height(path.height).map(|h| h.to_string())).await
},
|op| {
op.id("get_block_by_height")
.blocks_tag()
.summary("Block hash by height")
.description(
"Retrieve the block hash at a given height. Returns the hash as plain text.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-height)*",
)
.ok_response::<BlockHash>()
.not_modified()
.bad_request()
.not_found()
.server_error()
},
),
)
.api_route(
"/api/block/{hash}",
get_with(
async |uri: Uri,
headers: HeaderMap,
Path(path): Path<BlockHashParam>,
State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Static, &uri, move |q| q.block(&path.hash)).await
},
|op| {
op.id("get_block")
.blocks_tag()
.summary("Block information")
.description(
"Retrieve block information by block hash. Returns block metadata including height, timestamp, difficulty, size, weight, and transaction count.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block)*",
)
.ok_response::<BlockInfo>()
.not_modified()
.bad_request()
.not_found()
.server_error()
},
),
)
.api_route(
"/api/block/{hash}/status",
get_with(
async |uri: Uri,
headers: HeaderMap,
Path(path): Path<BlockHashParam>,
State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Height, &uri, move |q| q.block_status(&path.hash)).await
},
|op| {
op.id("get_block_status")
.blocks_tag()
.summary("Block status")
.description(
"Retrieve the status of a block. Returns whether the block is in the best chain and, if so, its height and the hash of the next block.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-status)*",
)
.ok_response::<BlockStatus>()
.not_modified()
.bad_request()
.not_found()
.server_error()
},
),
)
.api_route(
"/api/block/{hash}/txids",
get_with(
async |uri: Uri,
headers: HeaderMap,
Path(path): Path<BlockHashParam>,
State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Static, &uri, move |q| q.block_txids(&path.hash)).await
},
|op| {
op.id("get_block_txids")
.blocks_tag()
.summary("Block transaction IDs")
.description(
"Retrieve all transaction IDs in a block. Returns an array of txids in block order.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-transaction-ids)*",
)
.ok_response::<Vec<Txid>>()
.not_modified()
.bad_request()
.not_found()
.server_error()
},
),
)
.api_route(
"/api/block/{hash}/txs/{start_index}",
get_with(
async |uri: Uri,
headers: HeaderMap,
Path(path): Path<BlockHashStartIndex>,
State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Static, &uri, move |q| q.block_txs(&path.hash, path.start_index)).await
},
|op| {
op.id("get_block_txs")
.blocks_tag()
.summary("Block transactions (paginated)")
.description(&format!(
"Retrieve transactions in a block by block hash, starting from the specified index. Returns up to {} transactions at a time.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-transactions)*",
BLOCK_TXS_PAGE_SIZE
))
.ok_response::<Vec<Transaction>>()
.not_modified()
.bad_request()
.not_found()
.server_error()
},
),
)
.api_route(
"/api/block/{hash}/txid/{index}",
get_with(
async |uri: Uri,
headers: HeaderMap,
Path(path): Path<BlockHashTxIndex>,
State(state): State<AppState>| {
state.cached_text(&headers, CacheStrategy::Static, &uri, move |q| q.block_txid_at_index(&path.hash, path.index).map(|t| t.to_string())).await
},
|op| {
op.id("get_block_txid")
.blocks_tag()
.summary("Transaction ID at index")
.description(
"Retrieve a single transaction ID at a specific index within a block. Returns plain text txid.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-transaction-id)*",
)
.ok_response::<Txid>()
.not_modified()
.bad_request()
.not_found()
.server_error()
},
),
)
.api_route(
"/api/block/{hash}/raw",
get_with(
async |uri: Uri,
headers: HeaderMap,
Path(path): Path<BlockHashParam>,
State(state): State<AppState>| {
state.cached_bytes(&headers, CacheStrategy::Static, &uri, move |q| q.block_raw(&path.hash)).await
},
|op| {
op.id("get_block_raw")
.blocks_tag()
.summary("Raw block")
.description(
"Returns the raw block data in binary format.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-raw)*",
)
.ok_response::<Vec<u8>>()
.not_modified()
.bad_request()
.not_found()
.server_error()
},
),
)
.api_route(
"/api/blocks/tip/height",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
state.cached_text(&headers, CacheStrategy::Height, &uri, |q| Ok(q.height().to_string())).await
},
|op| {
op.id("get_block_tip_height")
.blocks_tag()
.summary("Block tip height")
.description("Returns the height of the last block.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-tip-height)*")
.ok_response::<Height>()
.not_modified()
.server_error()
},
),
)
.api_route(
"/api/blocks/tip/hash",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
state.cached_text(&headers, CacheStrategy::Height, &uri, |q| q.block_hash_by_height(q.height()).map(|h| h.to_string())).await
},
|op| {
op.id("get_block_tip_hash")
.blocks_tag()
.summary("Block tip hash")
.description("Returns the hash of the last block.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-tip-hash)*")
.ok_response::<BlockHash>()
.not_modified()
.server_error()
},
),
)
.api_route(
"/api/block/{hash}/header",
get_with(
async |uri: Uri, headers: HeaderMap, Path(path): Path<BlockHashParam>, State(state): State<AppState>| {
state.cached_text(&headers, CacheStrategy::Height, &uri, move |q| q.block_header_hex(&path.hash)).await
},
|op| {
op.id("get_block_header")
.blocks_tag()
.summary("Block header")
.description("Returns the hex-encoded block header.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-header)*")
.ok_response::<Hex>()
.not_modified()
.not_found()
.server_error()
},
),
)
.api_route(
"/api/v1/block/{hash}",
get_with(
async |uri: Uri, headers: HeaderMap, Path(path): Path<BlockHashParam>, State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Height, &uri, move |q| {
let height = q.height_by_hash(&path.hash)?;
q.block_by_height_v1(height)
}).await
},
|op| {
op.id("get_block_v1")
.blocks_tag()
.summary("Block (v1)")
.description("Returns block details with extras by hash.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-v1)*")
.ok_response::<BlockInfoV1>()
.not_modified()
.not_found()
.server_error()
},
),
)
.api_route(
"/api/v1/mining/blocks/timestamp/{timestamp}",
get_with(
async |uri: Uri,
headers: HeaderMap,
Path(path): Path<TimestampParam>,
State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Height, &uri, move |q| q.block_by_timestamp(path.timestamp)).await
},
|op| {
op.id("get_block_by_timestamp")
.blocks_tag()
.summary("Block by timestamp")
.description("Find the block closest to a given UNIX timestamp.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-timestamp)*")
.ok_response::<BlockTimestamp>()
.not_modified()
.bad_request()
.not_found()
.server_error()
},
),
)
}
}

View File

@@ -0,0 +1,79 @@
use aide::axum::{ApiRouter, routing::get_with};
use axum::{
extract::State,
http::{HeaderMap, Uri},
};
use brk_types::{MempoolBlock, RecommendedFees};
use crate::extended::TransformResponseExtended;
use super::AppState;
pub trait FeesRoutes {
fn add_fees_routes(self) -> Self;
}
impl FeesRoutes for ApiRouter<AppState> {
fn add_fees_routes(self) -> Self {
self.api_route(
"/api/v1/fees/mempool-blocks",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
state
.cached_json(&headers, state.mempool_cache(), &uri, |q| {
q.mempool_blocks()
})
.await
},
|op| {
op.id("get_mempool_blocks")
.fees_tag()
.summary("Projected mempool blocks")
.description("Get projected blocks from the mempool for fee estimation.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-mempool-blocks-fees)*")
.ok_response::<Vec<MempoolBlock>>()
.server_error()
},
),
)
.api_route(
"/api/v1/fees/recommended",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
state
.cached_json(&headers, state.mempool_cache(), &uri, |q| {
q.recommended_fees()
})
.await
},
|op| {
op.id("get_recommended_fees")
.fees_tag()
.summary("Recommended fees")
.description("Get recommended fee rates for different confirmation targets.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-recommended-fees)*")
.ok_response::<RecommendedFees>()
.server_error()
},
),
)
.api_route(
"/api/v1/fees/precise",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
state
.cached_json(&headers, state.mempool_cache(), &uri, |q| {
q.recommended_fees()
})
.await
},
|op| {
op.id("get_precise_fees")
.fees_tag()
.summary("Precise recommended fees")
.description("Get recommended fee rates with up to 3 decimal places.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-recommended-fees-precise)*")
.ok_response::<RecommendedFees>()
.server_error()
},
),
)
}
}

View File

@@ -0,0 +1,89 @@
use aide::axum::{ApiRouter, routing::get_with};
use axum::{
extract::{Query, State},
http::{HeaderMap, Uri},
};
use brk_types::{
DifficultyAdjustment, HistoricalPrice, OptionalTimestampParam, Prices, Timestamp,
};
use crate::{CacheStrategy, extended::TransformResponseExtended};
use super::AppState;
pub trait GeneralRoutes {
fn add_general_routes(self) -> Self;
}
impl GeneralRoutes for ApiRouter<AppState> {
fn add_general_routes(self) -> Self {
self.api_route(
"/api/v1/difficulty-adjustment",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
state
.cached_json(&headers, CacheStrategy::Height, &uri, |q| {
q.difficulty_adjustment()
})
.await
},
|op| {
op.id("get_difficulty_adjustment")
.general_tag()
.summary("Difficulty adjustment")
.description("Get current difficulty adjustment progress and estimates.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-difficulty-adjustment)*")
.ok_response::<DifficultyAdjustment>()
.not_modified()
.server_error()
},
),
)
.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")
.general_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/v1/historical-price",
get_with(
async |uri: Uri,
headers: HeaderMap,
Query(params): Query<OptionalTimestampParam>,
State(state): State<AppState>| {
state
.cached_json(&headers, CacheStrategy::Height, &uri, move |q| {
q.historical_price(params.timestamp)
})
.await
},
|op| {
op.id("get_historical_price")
.general_tag()
.summary("Historical price")
.description("Get historical BTC/USD price. Optionally specify a UNIX timestamp to get the price at that time.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-historical-price)*")
.ok_response::<HistoricalPrice>()
.not_modified()
.server_error()
},
),
)
}
}

View File

@@ -1,14 +1,11 @@
use aide::axum::{ApiRouter, routing::get_with};
use axum::{
extract::{Query, State},
extract::State,
http::{HeaderMap, Uri},
};
use brk_types::{
Dollars, HistoricalPrice, MempoolBlock, MempoolInfo, MempoolRecentTx, OptionalTimestampParam,
Prices, RecommendedFees, Timestamp, Txid,
};
use brk_types::{Dollars, MempoolInfo, MempoolRecentTx, Txid};
use crate::{CacheStrategy, extended::TransformResponseExtended};
use crate::extended::TransformResponseExtended;
use super::AppState;
@@ -18,160 +15,81 @@ pub trait MempoolRoutes {
impl MempoolRoutes for ApiRouter<AppState> {
fn add_mempool_routes(self) -> Self {
self
.api_route(
"/api/mempool",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
state.cached_json(&headers, state.mempool_cache(), &uri, |q| q.mempool_info()).await
},
|op| {
op.id("get_mempool")
.mempool_tag()
.summary("Mempool statistics")
.description("Get current mempool statistics including transaction count, total vsize, and total fees.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-mempool)*")
.ok_response::<MempoolInfo>()
.server_error()
},
),
)
.api_route(
"/api/mempool/txids",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
state.cached_json(&headers, state.mempool_cache(), &uri, |q| q.mempool_txids()).await
},
|op| {
op.id("get_mempool_txids")
.mempool_tag()
.summary("Mempool transaction IDs")
.description("Get all transaction IDs currently in the mempool.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-mempool-transaction-ids)*")
.ok_response::<Vec<Txid>>()
.server_error()
},
),
)
.api_route(
"/api/mempool/recent",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
state.cached_json(&headers, state.mempool_cache(), &uri, |q| q.mempool_recent()).await
},
|op| {
op.id("get_mempool_recent")
.mempool_tag()
.summary("Recent mempool transactions")
.description("Get the last 10 transactions to enter the mempool.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-mempool-recent)*")
.ok_response::<Vec<MempoolRecentTx>>()
.server_error()
},
),
)
.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(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
state.cached_json(&headers, state.mempool_cache(), &uri, |q| q.live_price()).await
},
|op| {
op.id("get_live_price")
.mempool_tag()
.summary("Live BTC/USD price")
.description(
"Returns the current BTC/USD price in dollars, derived from \
on-chain round-dollar output patterns in the last 12 blocks \
plus mempool.",
)
.ok_response::<Dollars>()
.server_error()
},
),
)
.api_route(
"/api/v1/fees/recommended",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
state.cached_json(&headers, state.mempool_cache(), &uri, |q| q.recommended_fees()).await
},
|op| {
op.id("get_recommended_fees")
.mempool_tag()
.summary("Recommended fees")
.description("Get recommended fee rates for different confirmation targets based on current mempool state.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-recommended-fees)*")
.ok_response::<RecommendedFees>()
.server_error()
},
),
)
.api_route(
"/api/v1/fees/precise",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
state.cached_json(&headers, state.mempool_cache(), &uri, |q| q.recommended_fees()).await
},
|op| {
op.id("get_precise_fees")
.mempool_tag()
.summary("Precise recommended fees")
.description("Get recommended fee rates with up to 3 decimal places, including sub-sat feerates.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-recommended-fees-precise)*")
.ok_response::<RecommendedFees>()
.server_error()
},
),
)
.api_route(
"/api/v1/fees/mempool-blocks",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
state.cached_json(&headers, state.mempool_cache(), &uri, |q| q.mempool_blocks()).await
},
|op| {
op.id("get_mempool_blocks")
.mempool_tag()
.summary("Projected mempool blocks")
.description("Get projected blocks from the mempool for fee estimation. Each block contains statistics about transactions that would be included if a block were mined now.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-mempool-blocks-fees)*")
.ok_response::<Vec<MempoolBlock>>()
.server_error()
},
),
)
.api_route(
"/api/v1/historical-price",
get_with(
async |uri: Uri, headers: HeaderMap, Query(params): Query<OptionalTimestampParam>, State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Height, &uri, move |q| q.historical_price(params.timestamp)).await
},
|op| {
op.id("get_historical_price")
.mempool_tag()
.summary("Historical price")
.description("Get historical BTC/USD price. Optionally specify a UNIX timestamp to get the price at that time.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-historical-price)*")
.ok_response::<HistoricalPrice>()
.not_modified()
.server_error()
},
),
)
self.api_route(
"/api/mempool",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
state
.cached_json(&headers, state.mempool_cache(), &uri, |q| q.mempool_info())
.await
},
|op| {
op.id("get_mempool")
.mempool_tag()
.summary("Mempool statistics")
.description("Get current mempool statistics including transaction count, total vsize, total fees, and fee histogram.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-mempool)*")
.ok_response::<MempoolInfo>()
.server_error()
},
),
)
.api_route(
"/api/mempool/txids",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
state
.cached_json(&headers, state.mempool_cache(), &uri, |q| q.mempool_txids())
.await
},
|op| {
op.id("get_mempool_txids")
.mempool_tag()
.summary("Mempool transaction IDs")
.description("Get all transaction IDs currently in the mempool.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-mempool-transaction-ids)*")
.ok_response::<Vec<Txid>>()
.server_error()
},
),
)
.api_route(
"/api/mempool/recent",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
state
.cached_json(&headers, state.mempool_cache(), &uri, |q| q.mempool_recent())
.await
},
|op| {
op.id("get_mempool_recent")
.mempool_tag()
.summary("Recent mempool transactions")
.description("Get the last 10 transactions to enter the mempool.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-mempool-recent)*")
.ok_response::<Vec<MempoolRecentTx>>()
.server_error()
},
),
)
.api_route(
"/api/mempool/price",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
state
.cached_json(&headers, state.mempool_cache(), &uri, |q| q.live_price())
.await
},
|op| {
op.id("get_live_price")
.mempool_tag()
.summary("Live BTC/USD price")
.description(
"Returns the current BTC/USD price in dollars, derived from \
on-chain round-dollar output patterns in the last 12 blocks \
plus mempool.",
)
.ok_response::<Dollars>()
.server_error()
},
),
)
}
}

View File

@@ -7,7 +7,7 @@ use axum::{
};
use brk_types::{
BlockCountParam, BlockFeesEntry, BlockInfoV1, BlockRewardsEntry, BlockSizesWeights,
DifficultyAdjustment, DifficultyAdjustmentEntry, HashrateSummary, PoolDetail,
DifficultyAdjustmentEntry, HashrateSummary, PoolDetail,
PoolHashrateEntry, PoolInfo, PoolSlugAndHeightParam, PoolSlugParam, PoolsSummary,
RewardStats, TimePeriodParam,
};
@@ -26,23 +26,6 @@ impl MiningRoutes for ApiRouter<AppState> {
"/api/v1/mining",
get(Redirect::temporary("/api#tag/mining")),
)
.api_route(
"/api/v1/difficulty-adjustment",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Height, &uri, |q| q.difficulty_adjustment()).await
},
|op| {
op.id("get_difficulty_adjustment")
.mining_tag()
.summary("Difficulty adjustment")
.description("Get current difficulty adjustment information including progress through the current epoch, estimated retarget date, and difficulty change prediction.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-difficulty-adjustment)*")
.ok_response::<DifficultyAdjustment>()
.not_modified()
.server_error()
},
),
)
.api_route(
"/api/v1/mining/pools",
get_with(
@@ -96,6 +79,58 @@ impl MiningRoutes for ApiRouter<AppState> {
},
),
)
.api_route(
"/api/v1/mining/hashrate/pools",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Height, &uri, |q| q.pools_hashrate(None)).await
},
|op| {
op.id("get_pools_hashrate")
.mining_tag()
.summary("All pools hashrate (all time)")
.description("Get hashrate data for all mining pools.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-mining-pool-hashrates)*")
.ok_response::<Vec<PoolHashrateEntry>>()
.not_modified()
.server_error()
},
),
)
.api_route(
"/api/v1/mining/hashrate/pools/{time_period}",
get_with(
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Height, &uri, move |q| q.pools_hashrate(Some(path.time_period))).await
},
|op| {
op.id("get_pools_hashrate_by_period")
.mining_tag()
.summary("All pools hashrate")
.description("Get hashrate data for all mining pools for a time period. Valid periods: 1m, 3m, 6m, 1y, 2y, 3y\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-mining-pool-hashrates)*")
.ok_response::<Vec<PoolHashrateEntry>>()
.not_modified()
.server_error()
},
),
)
.api_route(
"/api/v1/mining/pool/{slug}/hashrate",
get_with(
async |uri: Uri, headers: HeaderMap, Path(path): Path<PoolSlugParam>, State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Height, &uri, move |q| q.pool_hashrate(path.slug)).await
},
|op| {
op.id("get_pool_hashrate")
.mining_tag()
.summary("Mining pool hashrate")
.description("Get hashrate history for a specific mining pool.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-mining-pool-hashrate)*")
.ok_response::<Vec<PoolHashrateEntry>>()
.not_modified()
.not_found()
.server_error()
},
),
)
.api_route(
"/api/v1/mining/pool/{slug}/blocks",
get_with(
@@ -132,58 +167,6 @@ impl MiningRoutes for ApiRouter<AppState> {
},
),
)
.api_route(
"/api/v1/mining/pool/{slug}/hashrate",
get_with(
async |uri: Uri, headers: HeaderMap, Path(path): Path<PoolSlugParam>, State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Height, &uri, move |q| q.pool_hashrate(path.slug)).await
},
|op| {
op.id("get_pool_hashrate")
.mining_tag()
.summary("Mining pool hashrate")
.description("Get hashrate history for a specific mining pool.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-mining-pool-hashrate)*")
.ok_response::<Vec<PoolHashrateEntry>>()
.not_modified()
.not_found()
.server_error()
},
),
)
.api_route(
"/api/v1/mining/hashrate/pools",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Height, &uri, |q| q.pools_hashrate(None)).await
},
|op| {
op.id("get_pools_hashrate")
.mining_tag()
.summary("All pools hashrate (all time)")
.description("Get hashrate data for all mining pools.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-mining-pool-hashrates)*")
.ok_response::<Vec<PoolHashrateEntry>>()
.not_modified()
.server_error()
},
),
)
.api_route(
"/api/v1/mining/hashrate/pools/{time_period}",
get_with(
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Height, &uri, move |q| q.pools_hashrate(Some(path.time_period))).await
},
|op| {
op.id("get_pools_hashrate_by_period")
.mining_tag()
.summary("All pools hashrate")
.description("Get hashrate data for all mining pools for a time period. Valid periods: 1m, 3m, 6m, 1y, 2y, 3y\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-mining-pool-hashrates)*")
.ok_response::<Vec<PoolHashrateEntry>>()
.not_modified()
.server_error()
},
),
)
.api_route(
"/api/v1/mining/hashrate",
get_with(
@@ -252,6 +235,23 @@ impl MiningRoutes for ApiRouter<AppState> {
},
),
)
.api_route(
"/api/v1/mining/reward-stats/{block_count}",
get_with(
async |uri: Uri, headers: HeaderMap, Path(path): Path<BlockCountParam>, State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Height, &uri, move |q| q.reward_stats(path.block_count)).await
},
|op| {
op.id("get_reward_stats")
.mining_tag()
.summary("Mining reward statistics")
.description("Get mining reward statistics for the last N blocks including total rewards, fees, and transaction count.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-reward-stats)*")
.ok_response::<RewardStats>()
.not_modified()
.server_error()
},
),
)
.api_route(
"/api/v1/mining/blocks/fees/{time_period}",
get_with(
@@ -318,22 +318,5 @@ impl MiningRoutes for ApiRouter<AppState> {
},
),
)
.api_route(
"/api/v1/mining/reward-stats/{block_count}",
get_with(
async |uri: Uri, headers: HeaderMap, Path(path): Path<BlockCountParam>, State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Height, &uri, move |q| q.reward_stats(path.block_count)).await
},
|op| {
op.id("get_reward_stats")
.mining_tag()
.summary("Mining reward statistics")
.description("Get mining reward statistics for the last N blocks including total rewards, fees, and transaction count.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-reward-stats)*")
.ok_response::<RewardStats>()
.not_modified()
.server_error()
},
),
)
}
}

View File

@@ -14,9 +14,9 @@ use axum::{
use crate::{
Error,
api::{
addrs::AddrRoutes, blocks::BlockRoutes, mempool::MempoolRoutes,
metrics_legacy::ApiMetricsLegacyRoutes, mining::MiningRoutes, series::ApiSeriesRoutes,
server::ServerRoutes, transactions::TxRoutes,
addrs::AddrRoutes, blocks::BlockRoutes, fees::FeesRoutes, general::GeneralRoutes,
mempool::MempoolRoutes, metrics_legacy::ApiMetricsLegacyRoutes, mining::MiningRoutes,
series::ApiSeriesRoutes, server::ServerRoutes, transactions::TxRoutes,
},
extended::{ResponseExtended, TransformResponseExtended},
};
@@ -25,6 +25,8 @@ use super::AppState;
mod addrs;
mod blocks;
mod fees;
mod general;
mod mempool;
mod metrics_legacy;
mod mining;
@@ -44,11 +46,13 @@ impl ApiRoutes for ApiRouter<AppState> {
self.add_server_routes()
.add_series_routes()
.add_metrics_legacy_routes()
.add_block_routes()
.add_tx_routes()
.add_general_routes()
.add_addr_routes()
.add_mempool_routes()
.add_block_routes()
.add_mining_routes()
.add_fees_routes()
.add_mempool_routes()
.add_tx_routes()
.route("/api/server", get(Redirect::temporary("/api#tag/server")))
.api_route(
"/openapi.json",

File diff suppressed because one or more lines are too long

View File

@@ -23,6 +23,22 @@ impl TxRoutes for ApiRouter<AppState> {
fn add_tx_routes(self) -> Self {
self
.api_route(
"/api/v1/cpfp/{txid}",
get_with(
async |uri: Uri, headers: HeaderMap, Path(txid): Path<TxidParam>, State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::MempoolHash(0), &uri, move |q| q.cpfp(txid)).await
},
|op| op
.id("get_cpfp")
.transactions_tag()
.summary("CPFP info")
.description("Returns ancestors and descendants for a CPFP transaction.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-children-pay-for-parent)*")
.ok_response::<CpfpInfo>()
.not_found()
.server_error(),
),
)
.api_route(
"/api/tx/{txid}",
get_with(
async |
@@ -47,31 +63,6 @@ impl TxRoutes for ApiRouter<AppState> {
.server_error(),
),
)
.api_route(
"/api/tx/{txid}/status",
get_with(
async |
uri: Uri,
headers: HeaderMap,
Path(txid): Path<TxidParam>,
State(state): State<AppState>
| {
state.cached_json(&headers, CacheStrategy::Height, &uri, move |q| q.transaction_status(txid)).await
},
|op| op
.id("get_tx_status")
.transactions_tag()
.summary("Transaction status")
.description(
"Retrieve the confirmation status of a transaction. Returns whether the transaction is confirmed and, if so, the block height, hash, and timestamp.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-transaction-status)*",
)
.ok_response::<TxStatus>()
.not_modified()
.bad_request()
.not_found()
.server_error(),
),
)
.api_route(
"/api/tx/{txid}/hex",
get_with(
@@ -97,6 +88,42 @@ impl TxRoutes for ApiRouter<AppState> {
.server_error(),
),
)
.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}/merkle-proof",
get_with(
async |uri: Uri, headers: HeaderMap, Path(txid): Path<TxidParam>, State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Height, &uri, move |q| q.merkle_proof(txid)).await
},
|op| op
.id("get_tx_merkle_proof")
.transactions_tag()
.summary("Transaction merkle proof")
.description("Get the merkle inclusion proof for a transaction.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-transaction-merkle-proof)*")
.ok_response::<MerkleProof>()
.not_modified()
.bad_request()
.not_found()
.server_error(),
),
)
.api_route(
"/api/tx/{txid}/outspend/{vout}",
get_with(
@@ -148,44 +175,6 @@ impl TxRoutes for ApiRouter<AppState> {
.server_error(),
),
)
.api_route(
"/api/tx",
post_with(
async |State(state): State<AppState>, body: String| {
let hex = body.trim().to_string();
state.sync(|q| q.broadcast_transaction(&hex))
.map(|txid| txid.to_string())
.map_err(crate::Error::from)
},
|op| {
op.id("post_tx")
.transactions_tag()
.summary("Broadcast transaction")
.description("Broadcast a raw transaction to the network. The transaction should be provided as hex in the request body. The txid will be returned on success.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#post-transaction)*")
.ok_response::<Txid>()
.bad_request()
.server_error()
},
),
)
.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(
@@ -205,39 +194,30 @@ impl TxRoutes for ApiRouter<AppState> {
),
)
.api_route(
"/api/tx/{txid}/merkle-proof",
"/api/tx/{txid}/status",
get_with(
async |uri: Uri, headers: HeaderMap, Path(txid): Path<TxidParam>, State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Height, &uri, move |q| q.merkle_proof(txid)).await
async |
uri: Uri,
headers: HeaderMap,
Path(txid): Path<TxidParam>,
State(state): State<AppState>
| {
state.cached_json(&headers, CacheStrategy::Height, &uri, move |q| q.transaction_status(txid)).await
},
|op| op
.id("get_tx_merkle_proof")
.id("get_tx_status")
.transactions_tag()
.summary("Transaction merkle proof")
.description("Get the merkle inclusion proof for a transaction.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-transaction-merkle-proof)*")
.ok_response::<MerkleProof>()
.summary("Transaction status")
.description(
"Retrieve the confirmation status of a transaction. Returns whether the transaction is confirmed and, if so, the block height, hash, and timestamp.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-transaction-status)*",
)
.ok_response::<TxStatus>()
.not_modified()
.bad_request()
.not_found()
.server_error(),
),
)
.api_route(
"/api/v1/cpfp/{txid}",
get_with(
async |uri: Uri, headers: HeaderMap, Path(txid): Path<TxidParam>, State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::MempoolHash(0), &uri, move |q| q.cpfp(txid)).await
},
|op| op
.id("get_cpfp")
.transactions_tag()
.summary("CPFP info")
.description("Returns ancestors and descendants for a CPFP transaction.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-children-pay-for-parent)*")
.ok_response::<CpfpInfo>()
.not_found()
.server_error(),
),
)
.api_route(
"/api/v1/transaction-times",
get_with(
@@ -254,5 +234,25 @@ impl TxRoutes for ApiRouter<AppState> {
.server_error(),
),
)
.api_route(
"/api/tx",
post_with(
async |State(state): State<AppState>, body: String| {
let hex = body.trim().to_string();
state.sync(|q| q.broadcast_transaction(&hex))
.map(|txid| txid.to_string())
.map_err(crate::Error::from)
},
|op| {
op.id("post_tx")
.transactions_tag()
.summary("Broadcast transaction")
.description("Broadcast a raw transaction to the network. The transaction should be provided as hex in the request body. The txid will be returned on success.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#post-transaction)*")
.ok_response::<Txid>()
.bad_request()
.server_error()
},
),
)
}
}

View File

@@ -6,14 +6,16 @@ use schemars::JsonSchema;
use crate::error::ErrorBody;
pub trait TransformResponseExtended<'t> {
fn general_tag(self) -> Self;
fn addrs_tag(self) -> Self;
fn blocks_tag(self) -> Self;
fn mempool_tag(self) -> Self;
fn metrics_tag(self) -> Self;
fn mining_tag(self) -> Self;
fn series_tag(self) -> Self;
fn server_tag(self) -> Self;
fn fees_tag(self) -> Self;
fn mempool_tag(self) -> Self;
fn transactions_tag(self) -> Self;
fn server_tag(self) -> Self;
fn series_tag(self) -> Self;
fn metrics_tag(self) -> Self;
/// Mark operation as deprecated
fn deprecated(self) -> Self;
@@ -40,6 +42,10 @@ pub trait TransformResponseExtended<'t> {
}
impl<'t> TransformResponseExtended<'t> for TransformOperation<'t> {
fn general_tag(self) -> Self {
self.tag("General")
}
fn addrs_tag(self) -> Self {
self.tag("Addresses")
}
@@ -48,28 +54,32 @@ impl<'t> TransformResponseExtended<'t> for TransformOperation<'t> {
self.tag("Blocks")
}
fn mining_tag(self) -> Self {
self.tag("Mining")
}
fn fees_tag(self) -> Self {
self.tag("Fees")
}
fn mempool_tag(self) -> Self {
self.tag("Mempool")
}
fn metrics_tag(self) -> Self {
self.tag("Metrics")
}
fn series_tag(self) -> Self {
self.tag("Series")
}
fn mining_tag(self) -> Self {
self.tag("Mining")
fn transactions_tag(self) -> Self {
self.tag("Transactions")
}
fn server_tag(self) -> Self {
self.tag("Server")
}
fn transactions_tag(self) -> Self {
self.tag("Transactions")
fn series_tag(self) -> Self {
self.tag("Series")
}
fn metrics_tag(self) -> Self {
self.tag("Metrics")
}
fn ok_response<R>(self) -> Self

View File

@@ -11,15 +11,13 @@
> "Shout out to Bitcoin Research Kit. [...] Couldn't recommend them highly enough."
> — James Check (CheckOnChain), [What Bitcoin Did #1000](https://www.whatbitcoindid.com/episodes/wbd1000-checkmate)
Open-source, self-hostable on-chain analytics for Bitcoin. Block explorer, address index, and thousands of metrics, everything computed from your node, even the price.
Open-source Bitcoin data toolkit that can parse blocks, index the chain, compute metrics, serve data and render it, all from a Bitcoin Core node. It combines what [Glassnode](https://glassnode.com) and [mempool.space](https://mempool.space) do separately into a single self-hostable package, with a built-in price oracle inspired by [UTXO Oracle](https://utxo.live/oracle/).
Similar to [Glassnode](https://glassnode.com) + [mempool.space](https://mempool.space) + [UTXO Oracle](https://utxo.live/oracle/) in a single package.
[Bitview](https://bitview.space) is a free hosted instance of BRK.
[Bitview](https://bitview.space) is the official free hosted instance of BRK.
## Data
**Zero external dependencies.** BRK needs only a Bitcoin Core node. Historical prices are built in, live price from your mempool. Every metric is computed locally from your own copy of the blockchain. Your node, your data.
**Zero external dependencies.** BRK needs only a Bitcoin Core node. 8,000+ metrics across 15 time resolutions, all computed locally from your own copy of the blockchain. Historical prices are built in, live price from your mempool. Your node, your data.
**Blockchain:** Blocks, transactions, addresses, UTXOs.
@@ -41,7 +39,7 @@ Browse metrics and charts at [bitview.space](https://bitview.space), no signup r
curl https://bitview.space/api/mempool/price
```
Query metrics and blockchain data in JSON or CSV.
Query metrics and blockchain data in JSON or CSV. No rate limit.
[Documentation](https://bitview.space/api) · [JavaScript](https://www.npmjs.com/package/brk-client) · [Python](https://pypi.org/project/brk-client) · [Rust](https://crates.io/crates/brk_client) · [llms.txt](https://bitview.space/llms.txt) · [LLM-friendly schema](https://bitview.space/api.json)

View File

@@ -9702,7 +9702,7 @@ class BrkClient extends BrkClientBase {
/**
* Mempool statistics
*
* Get current mempool statistics including transaction count, total vsize, and total fees.
* Get current mempool statistics including transaction count, total vsize, total fees, and fee histogram.
*
* *[Mempool.space docs](https://mempool.space/docs/api/rest#get-mempool)*
*
@@ -10247,7 +10247,7 @@ class BrkClient extends BrkClientBase {
/**
* Difficulty adjustment
*
* Get current difficulty adjustment information including progress through the current epoch, estimated retarget date, and difficulty change prediction.
* Get current difficulty adjustment progress and estimates.
*
* *[Mempool.space docs](https://mempool.space/docs/api/rest#get-difficulty-adjustment)*
*
@@ -10261,7 +10261,7 @@ class BrkClient extends BrkClientBase {
/**
* Projected mempool blocks
*
* Get projected blocks from the mempool for fee estimation. Each block contains statistics about transactions that would be included if a block were mined now.
* Get projected blocks from the mempool for fee estimation.
*
* *[Mempool.space docs](https://mempool.space/docs/api/rest#get-mempool-blocks-fees)*
*
@@ -10275,7 +10275,7 @@ class BrkClient extends BrkClientBase {
/**
* Precise recommended fees
*
* Get recommended fee rates with up to 3 decimal places, including sub-sat feerates.
* Get recommended fee rates with up to 3 decimal places.
*
* *[Mempool.space docs](https://mempool.space/docs/api/rest#get-recommended-fees-precise)*
*
@@ -10289,7 +10289,7 @@ class BrkClient extends BrkClientBase {
/**
* Recommended fees
*
* Get recommended fee rates for different confirmation targets based on current mempool state.
* Get recommended fee rates for different confirmation targets.
*
* *[Mempool.space docs](https://mempool.space/docs/api/rest#get-recommended-fees)*
*

View File

@@ -7250,7 +7250,7 @@ class BrkClient(BrkClientBase):
def get_mempool(self) -> MempoolInfo:
"""Mempool statistics.
Get current mempool statistics including transaction count, total vsize, and total fees.
Get current mempool statistics including transaction count, total vsize, total fees, and fee histogram.
*[Mempool.space docs](https://mempool.space/docs/api/rest#get-mempool)*
@@ -7592,7 +7592,7 @@ class BrkClient(BrkClientBase):
def get_difficulty_adjustment(self) -> DifficultyAdjustment:
"""Difficulty adjustment.
Get current difficulty adjustment information including progress through the current epoch, estimated retarget date, and difficulty change prediction.
Get current difficulty adjustment progress and estimates.
*[Mempool.space docs](https://mempool.space/docs/api/rest#get-difficulty-adjustment)*
@@ -7602,7 +7602,7 @@ class BrkClient(BrkClientBase):
def get_mempool_blocks(self) -> List[MempoolBlock]:
"""Projected mempool blocks.
Get projected blocks from the mempool for fee estimation. Each block contains statistics about transactions that would be included if a block were mined now.
Get projected blocks from the mempool for fee estimation.
*[Mempool.space docs](https://mempool.space/docs/api/rest#get-mempool-blocks-fees)*
@@ -7612,7 +7612,7 @@ class BrkClient(BrkClientBase):
def get_precise_fees(self) -> RecommendedFees:
"""Precise recommended fees.
Get recommended fee rates with up to 3 decimal places, including sub-sat feerates.
Get recommended fee rates with up to 3 decimal places.
*[Mempool.space docs](https://mempool.space/docs/api/rest#get-recommended-fees-precise)*
@@ -7622,7 +7622,7 @@ class BrkClient(BrkClientBase):
def get_recommended_fees(self) -> RecommendedFees:
"""Recommended fees.
Get recommended fee rates for different confirmation targets based on current mempool state.
Get recommended fee rates for different confirmation targets.
*[Mempool.space docs](https://mempool.space/docs/api/rest#get-recommended-fees)*

View File

@@ -146,7 +146,7 @@
</label>
<button id="share-button" title="Share">Share</button>
<button id="invert-button" title="Invert">Invert</button>
<button id="theme-button" title="Invert theme">Theme</button>
</fieldset>
</footer>
</main>

View File

@@ -4,7 +4,9 @@ const preferredColorSchemeMatchMedia = window.matchMedia(
"(prefers-color-scheme: dark)",
);
const stored = readStored("theme");
const initial = stored ? stored === "dark" : preferredColorSchemeMatchMedia.matches;
const initial = stored
? stored === "dark"
: preferredColorSchemeMatchMedia.matches;
export let dark = initial;
@@ -47,4 +49,4 @@ function invert() {
}
}
document.getElementById("invert-button")?.addEventListener("click", invert);
document.getElementById("theme-button")?.addEventListener("click", invert);