mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-28 08:39:59 -07:00
mempool: snapshot 5 + query: new tools + server: endpoints
This commit is contained in:
@@ -5,7 +5,8 @@ use axum::{
|
||||
response::{Redirect, Response},
|
||||
routing::get,
|
||||
};
|
||||
use brk_types::{Address, AddressStats, AddressTxidsParam, Txid, Utxo};
|
||||
use brk_query::validate_address;
|
||||
use brk_types::{Address, AddressStats, AddressTxidsParam, AddressValidation, Txid, Utxo};
|
||||
|
||||
use crate::{
|
||||
VERSION,
|
||||
@@ -93,5 +94,71 @@ impl AddressRoutes for ApiRouter<AppState> {
|
||||
.server_error()
|
||||
),
|
||||
)
|
||||
.api_route(
|
||||
"/api/address/{address}/txs/mempool",
|
||||
get_with(async |
|
||||
headers: HeaderMap,
|
||||
Path(address): Path<Address>,
|
||||
State(state): State<AppState>
|
||||
| {
|
||||
let etag = format!("{VERSION}-{}", state.get_height().await);
|
||||
if headers.has_etag(&etag) {
|
||||
return Response::new_not_modified();
|
||||
}
|
||||
state.get_address_mempool_txids(address).await.to_json_response(&etag)
|
||||
}, |op| op
|
||||
.addresses_tag()
|
||||
.summary("Address mempool transactions")
|
||||
.description("Get unconfirmed transaction IDs for an address from the mempool (up to 50).")
|
||||
.ok_response::<Vec<Txid>>()
|
||||
.not_modified()
|
||||
.bad_request()
|
||||
.not_found()
|
||||
.server_error()
|
||||
),
|
||||
)
|
||||
.api_route(
|
||||
"/api/address/{address}/txs/chain/{after_txid}",
|
||||
get_with(async |
|
||||
headers: HeaderMap,
|
||||
Path((address, after_txid)): Path<(Address, Option<Txid>)>,
|
||||
State(state): State<AppState>
|
||||
| {
|
||||
let etag = format!("{VERSION}-{}", state.get_height().await);
|
||||
if headers.has_etag(&etag) {
|
||||
return Response::new_not_modified();
|
||||
}
|
||||
state.get_address_txids(address, after_txid, 25).await.to_json_response(&etag)
|
||||
}, |op| op
|
||||
.addresses_tag()
|
||||
.summary("Address confirmed transactions")
|
||||
.description("Get confirmed transaction IDs for an address, 25 per page. Use after_txid for pagination.")
|
||||
.ok_response::<Vec<Txid>>()
|
||||
.not_modified()
|
||||
.bad_request()
|
||||
.not_found()
|
||||
.server_error()
|
||||
),
|
||||
)
|
||||
.api_route(
|
||||
"/api/v1/validate-address/{address}",
|
||||
get_with(async |
|
||||
headers: HeaderMap,
|
||||
Path(address): Path<String>,
|
||||
State(state): State<AppState>
|
||||
| {
|
||||
let etag = VERSION;
|
||||
if headers.has_etag(etag) {
|
||||
return Response::new_not_modified();
|
||||
}
|
||||
Response::new_json(validate_address(&address), etag)
|
||||
}, |op| op
|
||||
.addresses_tag()
|
||||
.summary("Validate address")
|
||||
.description("Validate a Bitcoin address and get information about its type and scriptPubKey.")
|
||||
.ok_response::<AddressValidation>()
|
||||
.not_modified()
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,11 @@ use axum::{
|
||||
response::{Redirect, Response},
|
||||
routing::get,
|
||||
};
|
||||
use brk_types::{BlockHashPath, BlockInfo, BlockStatus, Height, HeightPath, StartHeightPath, Txid};
|
||||
use brk_query::BLOCK_TXS_PAGE_SIZE;
|
||||
use brk_types::{
|
||||
BlockHashPath, BlockHashStartIndexPath, BlockHashTxIndexPath, BlockInfo, BlockStatus,
|
||||
BlockTimestamp, Height, HeightPath, StartHeightPath, TimestampPath, Transaction, Txid,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
VERSION,
|
||||
@@ -161,5 +165,111 @@ impl BlockRoutes for ApiRouter<AppState> {
|
||||
},
|
||||
),
|
||||
)
|
||||
.api_route(
|
||||
"/api/block/{hash}/txs/{start_index}",
|
||||
get_with(
|
||||
async |headers: HeaderMap,
|
||||
Path(path): Path<BlockHashStartIndexPath>,
|
||||
State(state): State<AppState>| {
|
||||
let etag = format!("{VERSION}-{}", state.get_height().await);
|
||||
if headers.has_etag(&etag) {
|
||||
return Response::new_not_modified();
|
||||
}
|
||||
state.get_block_txs(path.hash, path.start_index).await.to_json_response(&etag)
|
||||
},
|
||||
|op| {
|
||||
op.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.",
|
||||
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 |headers: HeaderMap,
|
||||
Path(path): Path<BlockHashTxIndexPath>,
|
||||
State(state): State<AppState>| {
|
||||
let etag = format!("{VERSION}-{}", state.get_height().await);
|
||||
if headers.has_etag(&etag) {
|
||||
return Response::new_not_modified();
|
||||
}
|
||||
state.get_block_txid_at_index(path.hash, path.index).await.to_display_response(&etag)
|
||||
},
|
||||
|op| {
|
||||
op.blocks_tag()
|
||||
.summary("Transaction ID at index")
|
||||
.description(
|
||||
"Retrieve a single transaction ID at a specific index within a block. Returns plain text txid.",
|
||||
)
|
||||
.ok_response::<Txid>()
|
||||
.not_modified()
|
||||
.bad_request()
|
||||
.not_found()
|
||||
.server_error()
|
||||
},
|
||||
),
|
||||
)
|
||||
.api_route(
|
||||
"/api/v1/mining/blocks/timestamp/{timestamp}",
|
||||
get_with(
|
||||
async |headers: HeaderMap,
|
||||
Path(path): Path<TimestampPath>,
|
||||
State(state): State<AppState>| {
|
||||
let etag = format!("{VERSION}-{}", state.get_height().await);
|
||||
if headers.has_etag(&etag) {
|
||||
return Response::new_not_modified();
|
||||
}
|
||||
state
|
||||
.get_block_by_timestamp(path.timestamp)
|
||||
.await
|
||||
.to_json_response(&etag)
|
||||
},
|
||||
|op| {
|
||||
op.blocks_tag()
|
||||
.summary("Block by timestamp")
|
||||
.description("Find the block closest to a given UNIX timestamp.")
|
||||
.ok_response::<BlockTimestamp>()
|
||||
.not_modified()
|
||||
.bad_request()
|
||||
.not_found()
|
||||
.server_error()
|
||||
},
|
||||
),
|
||||
)
|
||||
.api_route(
|
||||
"/api/block/{hash}/raw",
|
||||
get_with(
|
||||
async |headers: HeaderMap,
|
||||
Path(path): Path<BlockHashPath>,
|
||||
State(state): State<AppState>| {
|
||||
let etag = format!("{VERSION}-{}", state.get_height().await);
|
||||
if headers.has_etag(&etag) {
|
||||
return Response::new_not_modified();
|
||||
}
|
||||
state.get_block_raw(path.hash).await.to_bytes_response(&etag)
|
||||
},
|
||||
|op| {
|
||||
op.blocks_tag()
|
||||
.summary("Raw block")
|
||||
.description(
|
||||
"Returns the raw block data in binary format.",
|
||||
)
|
||||
.ok_response::<Vec<u8>>()
|
||||
.not_modified()
|
||||
.bad_request()
|
||||
.not_found()
|
||||
.server_error()
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use axum::{
|
||||
response::{Redirect, Response},
|
||||
routing::get,
|
||||
};
|
||||
use brk_types::{MempoolInfo, RecommendedFees, Txid};
|
||||
use brk_types::{MempoolBlock, MempoolInfo, RecommendedFees, Txid};
|
||||
|
||||
use crate::{
|
||||
VERSION,
|
||||
@@ -82,5 +82,25 @@ impl MempoolRoutes for ApiRouter<AppState> {
|
||||
},
|
||||
),
|
||||
)
|
||||
.api_route(
|
||||
"/api/v1/fees/mempool-blocks",
|
||||
get_with(
|
||||
async |headers: HeaderMap, State(state): State<AppState>| {
|
||||
let etag = format!("{VERSION}-{}", state.get_height().await);
|
||||
if headers.has_etag(&etag) {
|
||||
return Response::new_not_modified();
|
||||
}
|
||||
state.get_mempool_blocks().await.to_json_response(&etag)
|
||||
},
|
||||
|op| {
|
||||
op.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.")
|
||||
.ok_response::<Vec<MempoolBlock>>()
|
||||
.not_modified()
|
||||
.server_error()
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
use aide::axum::{ApiRouter, routing::get_with};
|
||||
use axum::{
|
||||
extract::State,
|
||||
http::HeaderMap,
|
||||
response::{Redirect, Response},
|
||||
routing::get,
|
||||
};
|
||||
use brk_types::DifficultyAdjustment;
|
||||
|
||||
use crate::{
|
||||
VERSION,
|
||||
extended::{HeaderMapExtended, ResponseExtended, ResultExtended, TransformResponseExtended},
|
||||
};
|
||||
|
||||
use super::AppState;
|
||||
|
||||
pub trait MiningRoutes {
|
||||
fn add_mining_routes(self) -> Self;
|
||||
}
|
||||
|
||||
impl MiningRoutes for ApiRouter<AppState> {
|
||||
fn add_mining_routes(self) -> Self {
|
||||
self.route(
|
||||
"/api/v1/mining",
|
||||
get(Redirect::temporary("/api#tag/mining")),
|
||||
)
|
||||
.api_route(
|
||||
"/api/v1/difficulty-adjustment",
|
||||
get_with(
|
||||
async |headers: HeaderMap, State(state): State<AppState>| {
|
||||
let etag = format!("{VERSION}-{}", state.get_height().await);
|
||||
if headers.has_etag(&etag) {
|
||||
return Response::new_not_modified();
|
||||
}
|
||||
state
|
||||
.get_difficulty_adjustment()
|
||||
.await
|
||||
.to_json_response(&etag)
|
||||
},
|
||||
|op| {
|
||||
op.mining_tag()
|
||||
.summary("Difficulty adjustment")
|
||||
.description("Get current difficulty adjustment information including progress through the current epoch, estimated retarget date, and difficulty change prediction.")
|
||||
.ok_response::<DifficultyAdjustment>()
|
||||
.not_modified()
|
||||
.server_error()
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ use crate::{
|
||||
VERSION,
|
||||
api::{
|
||||
addresses::AddressRoutes, blocks::BlockRoutes, mempool::MempoolRoutes,
|
||||
metrics::ApiMetricsRoutes, transactions::TxRoutes,
|
||||
metrics::ApiMetricsRoutes, mining::MiningRoutes, transactions::TxRoutes,
|
||||
},
|
||||
extended::{HeaderMapExtended, ResponseExtended, TransformResponseExtended},
|
||||
};
|
||||
@@ -27,6 +27,7 @@ mod addresses;
|
||||
mod blocks;
|
||||
mod mempool;
|
||||
mod metrics;
|
||||
mod mining;
|
||||
mod openapi;
|
||||
mod transactions;
|
||||
|
||||
@@ -41,6 +42,7 @@ impl ApiRoutes for ApiRouter<AppState> {
|
||||
self.add_addresses_routes()
|
||||
.add_block_routes()
|
||||
.add_mempool_routes()
|
||||
.add_mining_routes()
|
||||
.add_tx_routes()
|
||||
.add_metrics_routes()
|
||||
.route("/api/server", get(Redirect::temporary("/api#tag/server")))
|
||||
|
||||
@@ -5,7 +5,7 @@ use axum::{
|
||||
response::{Redirect, Response},
|
||||
routing::get,
|
||||
};
|
||||
use brk_types::{Transaction, TxStatus, TxidPath};
|
||||
use brk_types::{Transaction, TxOutspend, TxStatus, TxidPath, TxidVoutPath};
|
||||
|
||||
use crate::{
|
||||
VERSION,
|
||||
@@ -104,5 +104,60 @@ impl TxRoutes for ApiRouter<AppState> {
|
||||
.server_error(),
|
||||
),
|
||||
)
|
||||
.api_route(
|
||||
"/api/tx/{txid}/outspend/{vout}",
|
||||
get_with(
|
||||
async |
|
||||
headers: HeaderMap,
|
||||
Path(path): Path<TxidVoutPath>,
|
||||
State(state): State<AppState>
|
||||
| {
|
||||
let etag = format!("{VERSION}-{}", state.get_height().await);
|
||||
if headers.has_etag(&etag) {
|
||||
return Response::new_not_modified();
|
||||
}
|
||||
let txid = TxidPath { txid: path.txid };
|
||||
state.get_tx_outspend(txid, path.vout).await.to_json_response(&etag)
|
||||
},
|
||||
|op| op
|
||||
.transactions_tag()
|
||||
.summary("Output spend status")
|
||||
.description(
|
||||
"Get the spending status of a transaction output. Returns whether the output has been spent and, if so, the spending transaction details.",
|
||||
)
|
||||
.ok_response::<TxOutspend>()
|
||||
.not_modified()
|
||||
.bad_request()
|
||||
.not_found()
|
||||
.server_error(),
|
||||
),
|
||||
)
|
||||
.api_route(
|
||||
"/api/tx/{txid}/outspends",
|
||||
get_with(
|
||||
async |
|
||||
headers: HeaderMap,
|
||||
Path(txid): Path<TxidPath>,
|
||||
State(state): State<AppState>
|
||||
| {
|
||||
let etag = format!("{VERSION}-{}", state.get_height().await);
|
||||
if headers.has_etag(&etag) {
|
||||
return Response::new_not_modified();
|
||||
}
|
||||
state.get_tx_outspends(txid).await.to_json_response(&etag)
|
||||
},
|
||||
|op| op
|
||||
.transactions_tag()
|
||||
.summary("All output spend statuses")
|
||||
.description(
|
||||
"Get the spending status of all outputs in a transaction. Returns an array with the spend status for each output.",
|
||||
)
|
||||
.ok_response::<Vec<TxOutspend>>()
|
||||
.not_modified()
|
||||
.bad_request()
|
||||
.not_found()
|
||||
.server_error(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user