mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-11 11:08:13 -07:00
global: fixes
This commit is contained in:
@@ -1,14 +1,14 @@
|
||||
use aide::axum::{ApiRouter, routing::get_with};
|
||||
use axum::{
|
||||
extract::{Path, Query, State},
|
||||
extract::{Path, State},
|
||||
http::{HeaderMap, Uri},
|
||||
};
|
||||
use brk_types::{AddrStats, AddrValidation, Transaction, Txid, Utxo, Version};
|
||||
use brk_types::{AddrStats, AddrValidation, Transaction, Utxo, Version};
|
||||
|
||||
use crate::{
|
||||
AppState, CacheStrategy,
|
||||
extended::TransformResponseExtended,
|
||||
params::{AddrParam, AddrTxidsParam, Empty, ValidateAddrParam},
|
||||
params::{AddrAfterTxidParam, AddrParam, Empty, ValidateAddrParam},
|
||||
};
|
||||
|
||||
pub trait AddrRoutes {
|
||||
@@ -46,16 +46,16 @@ impl AddrRoutes for ApiRouter<AppState> {
|
||||
uri: Uri,
|
||||
headers: HeaderMap,
|
||||
Path(path): Path<AddrParam>,
|
||||
Query(params): Query<AddrTxidsParam>,
|
||||
_: Empty,
|
||||
State(state): State<AppState>
|
||||
| {
|
||||
let strategy = state.addr_strategy(Version::ONE, &path.addr, false);
|
||||
state.respond_json(&headers, strategy, &uri, move |q| q.addr_txs(path.addr, params.after_txid, 50)).await
|
||||
state.respond_json(&headers, strategy, &uri, move |q| q.addr_txs(path.addr, 50, 25)).await
|
||||
}, |op| op
|
||||
.id("get_address_txs")
|
||||
.addrs_tag()
|
||||
.summary("Address transactions")
|
||||
.description("Get transaction history for an address, sorted with newest first. Returns up to 50 mempool transactions plus the first 25 confirmed transactions. Use ?after_txid=<txid> for pagination.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-address-transactions)*")
|
||||
.description("Get transaction history for an address, sorted with newest first. Returns up to 50 mempool transactions plus the first 25 confirmed transactions. To paginate further confirmed transactions, use `/address/{address}/txs/chain/{last_seen_txid}`.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-address-transactions)*")
|
||||
.json_response::<Vec<Transaction>>()
|
||||
.not_modified()
|
||||
.bad_request()
|
||||
@@ -69,16 +69,39 @@ impl AddrRoutes for ApiRouter<AppState> {
|
||||
uri: Uri,
|
||||
headers: HeaderMap,
|
||||
Path(path): Path<AddrParam>,
|
||||
Query(params): Query<AddrTxidsParam>,
|
||||
_: Empty,
|
||||
State(state): State<AppState>
|
||||
| {
|
||||
let strategy = state.addr_strategy(Version::ONE, &path.addr, true);
|
||||
state.respond_json(&headers, strategy, &uri, move |q| q.addr_txs(path.addr, params.after_txid, 25)).await
|
||||
state.respond_json(&headers, strategy, &uri, move |q| q.addr_txs_chain(&path.addr, None, 25)).await
|
||||
}, |op| op
|
||||
.id("get_address_confirmed_txs")
|
||||
.addrs_tag()
|
||||
.summary("Address confirmed transactions")
|
||||
.description("Get confirmed transactions for an address, 25 per page. Use ?after_txid=<txid> for pagination.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-address-transactions-chain)*")
|
||||
.description("Get the first 25 confirmed transactions for an address. For pagination, use the path-style form `/txs/chain/{last_seen_txid}`.\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-address-transactions-chain)*")
|
||||
.json_response::<Vec<Transaction>>()
|
||||
.not_modified()
|
||||
.bad_request()
|
||||
.not_found()
|
||||
.server_error()
|
||||
),
|
||||
)
|
||||
.api_route(
|
||||
"/api/address/{address}/txs/chain/{after_txid}",
|
||||
get_with(async |
|
||||
uri: Uri,
|
||||
headers: HeaderMap,
|
||||
Path(path): Path<AddrAfterTxidParam>,
|
||||
_: Empty,
|
||||
State(state): State<AppState>
|
||||
| {
|
||||
let strategy = state.addr_strategy(Version::ONE, &path.addr, true);
|
||||
state.respond_json(&headers, strategy, &uri, move |q| q.addr_txs_chain(&path.addr, Some(path.after_txid), 25)).await
|
||||
}, |op| op
|
||||
.id("get_address_confirmed_txs_after")
|
||||
.addrs_tag()
|
||||
.summary("Address confirmed transactions (paginated)")
|
||||
.description("Get the next 25 confirmed transactions strictly older than `after_txid` (Esplora-canonical pagination form, matches mempool.space).\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-address-transactions-chain)*")
|
||||
.json_response::<Vec<Transaction>>()
|
||||
.not_modified()
|
||||
.bad_request()
|
||||
@@ -95,14 +118,14 @@ impl AddrRoutes for ApiRouter<AppState> {
|
||||
_: Empty,
|
||||
State(state): State<AppState>
|
||||
| {
|
||||
let hash = state.sync(|q| q.addr_mempool_hash(&path.addr));
|
||||
state.respond_json(&headers, CacheStrategy::MempoolHash(hash), &uri, move |q| q.addr_mempool_txids(path.addr)).await
|
||||
let hash = state.sync(|q| q.addr_mempool_hash(&path.addr)).unwrap_or(0);
|
||||
state.respond_json(&headers, CacheStrategy::MempoolHash(hash), &uri, move |q| q.addr_mempool_txs(&path.addr, 50)).await
|
||||
}, |op| op
|
||||
.id("get_address_mempool_txs")
|
||||
.addrs_tag()
|
||||
.summary("Address mempool transactions")
|
||||
.description("Get unconfirmed transaction IDs for an address from the mempool (up to 50).\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-address-transactions-mempool)*")
|
||||
.json_response::<Vec<Txid>>()
|
||||
.description("Get unconfirmed transactions for an address from the mempool, newest first (up to 50).\n\n*[Mempool.space docs](https://mempool.space/docs/api/rest#get-address-transactions-mempool)*")
|
||||
.json_response::<Vec<Transaction>>()
|
||||
.not_modified()
|
||||
.bad_request()
|
||||
.not_found()
|
||||
@@ -119,7 +142,7 @@ impl AddrRoutes for ApiRouter<AppState> {
|
||||
State(state): State<AppState>
|
||||
| {
|
||||
let strategy = state.addr_strategy(Version::ONE, &path.addr, false);
|
||||
state.respond_json(&headers, strategy, &uri, move |q| q.addr_utxos(path.addr)).await
|
||||
state.respond_json(&headers, strategy, &uri, move |q| q.addr_utxos(path.addr, 1000)).await
|
||||
}, |op| op
|
||||
.id("get_address_utxos")
|
||||
.addrs_tag()
|
||||
|
||||
@@ -40,7 +40,7 @@ pub(super) async fn serve(
|
||||
let max_weight = state.max_weight;
|
||||
let resolved = state.run(move |q| q.resolve(params, max_weight)).await?;
|
||||
|
||||
let format = resolved.format();
|
||||
let format = resolved.format;
|
||||
let csv_filename = resolved.csv_filename();
|
||||
let cache_params = CacheParams::series(
|
||||
resolved.version,
|
||||
|
||||
@@ -100,7 +100,7 @@ fn cost_basis_formatted(
|
||||
value: CostBasisValue,
|
||||
) -> BrkResult<CostBasisFormatted> {
|
||||
let raw = q.urpd_raw(cohort, date)?;
|
||||
let day1 = Day1::try_from(date).map_err(|e| Error::Parse(e.to_string()))?;
|
||||
let day1 = Day1::try_from(date)?;
|
||||
let spot_cents = q
|
||||
.computer()
|
||||
.prices
|
||||
|
||||
@@ -61,6 +61,7 @@ fn error_status(e: &BrkError) -> StatusCode {
|
||||
| BrkError::NotFound(_)
|
||||
| BrkError::NoData
|
||||
| BrkError::OutOfRange(_)
|
||||
| BrkError::UnindexableDate
|
||||
| BrkError::SeriesNotFound(_) => StatusCode::NOT_FOUND,
|
||||
|
||||
BrkError::AuthFailed => StatusCode::FORBIDDEN,
|
||||
@@ -85,6 +86,7 @@ fn error_code(e: &BrkError) -> &'static str {
|
||||
BrkError::UnknownTxid => "unknown_txid",
|
||||
BrkError::NotFound(_) => "not_found",
|
||||
BrkError::OutOfRange(_) => "out_of_range",
|
||||
BrkError::UnindexableDate => "unindexable_date",
|
||||
BrkError::NoData => "no_data",
|
||||
BrkError::SeriesNotFound(_) => "series_not_found",
|
||||
BrkError::MempoolNotAvailable => "mempool_not_available",
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
|
||||
use brk_types::{Addr, Txid};
|
||||
|
||||
/// Bitcoin address + last-seen txid path parameters (Esplora-style pagination)
|
||||
#[derive(Deserialize, JsonSchema)]
|
||||
pub struct AddrAfterTxidParam {
|
||||
#[serde(rename = "address")]
|
||||
pub addr: Addr,
|
||||
|
||||
/// Last txid from the previous page (return transactions strictly older than this)
|
||||
pub after_txid: Txid,
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
|
||||
use brk_types::Txid;
|
||||
|
||||
#[derive(Debug, Default, Deserialize, JsonSchema)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct AddrTxidsParam {
|
||||
/// Txid to paginate from (return transactions before this one)
|
||||
pub after_txid: Option<Txid>,
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
mod addr_after_txid_param;
|
||||
mod addr_param;
|
||||
mod addr_txids_param;
|
||||
mod block_count_param;
|
||||
mod blockhash_param;
|
||||
mod blockhash_start_index;
|
||||
@@ -17,8 +17,8 @@ mod txids_param;
|
||||
mod urpd_params;
|
||||
mod validate_addr_param;
|
||||
|
||||
pub use addr_after_txid_param::*;
|
||||
pub use addr_param::*;
|
||||
pub use addr_txids_param::*;
|
||||
pub use block_count_param::*;
|
||||
pub use blockhash_param::*;
|
||||
pub use blockhash_start_index::*;
|
||||
|
||||
@@ -75,11 +75,10 @@ impl AppState {
|
||||
/// - Unknown address → `Tip`
|
||||
pub fn addr_strategy(&self, version: Version, addr: &Addr, chain_only: bool) -> CacheStrategy {
|
||||
self.sync(|q| {
|
||||
if !chain_only {
|
||||
let mempool_hash = q.addr_mempool_hash(addr);
|
||||
if mempool_hash != 0 {
|
||||
return CacheStrategy::MempoolHash(mempool_hash);
|
||||
}
|
||||
if !chain_only
|
||||
&& let Some(mempool_hash) = q.addr_mempool_hash(addr)
|
||||
{
|
||||
return CacheStrategy::MempoolHash(mempool_hash);
|
||||
}
|
||||
q.addr_last_activity_height(addr)
|
||||
.and_then(|h| {
|
||||
|
||||
Reference in New Issue
Block a user