mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-24 06:39:58 -07:00
global: snapshot
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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)*
|
||||
///
|
||||
|
||||
@@ -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()
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
79
crates/brk_server/src/api/fees/mod.rs
Normal file
79
crates/brk_server/src/api/fees/mod.rs
Normal 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()
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
89
crates/brk_server/src/api/general/mod.rs
Normal file
89
crates/brk_server/src/api/general/mod.rs
Normal 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()
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
Binary file not shown.
@@ -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()
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user