mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-24 06:39:58 -07:00
server: ms endpoint fixes
This commit is contained in:
@@ -7,7 +7,7 @@ use axum::{
|
||||
};
|
||||
use brk_types::{
|
||||
AddrParam, AddrStats, AddrTxidsParam, AddrValidation, Transaction, Txid, Utxo,
|
||||
ValidateAddrParam,
|
||||
ValidateAddrParam, Version,
|
||||
};
|
||||
|
||||
use crate::{AppState, CacheStrategy, extended::TransformResponseExtended};
|
||||
@@ -29,7 +29,8 @@ impl AddrRoutes for ApiRouter<AppState> {
|
||||
Path(path): Path<AddrParam>,
|
||||
State(state): State<AppState>
|
||||
| {
|
||||
state.cached_json(&headers, CacheStrategy::Height, &uri, move |q| q.addr(path.addr)).await
|
||||
let strategy = state.addr_cache(Version::ONE, &path.addr);
|
||||
state.cached_json(&headers, strategy, &uri, move |q| q.addr(path.addr)).await
|
||||
}, |op| op
|
||||
.id("get_address")
|
||||
.addrs_tag()
|
||||
@@ -51,7 +52,8 @@ impl AddrRoutes for ApiRouter<AppState> {
|
||||
Query(params): Query<AddrTxidsParam>,
|
||||
State(state): State<AppState>
|
||||
| {
|
||||
state.cached_json(&headers, CacheStrategy::Height, &uri, move |q| q.addr_txs(path.addr, params.after_txid, 25)).await
|
||||
let strategy = state.addr_cache(Version::ONE, &path.addr);
|
||||
state.cached_json(&headers, strategy, &uri, move |q| q.addr_txs(path.addr, params.after_txid, 50)).await
|
||||
}, |op| op
|
||||
.id("get_address_txs")
|
||||
.addrs_tag()
|
||||
@@ -73,7 +75,8 @@ impl AddrRoutes for ApiRouter<AppState> {
|
||||
Query(params): Query<AddrTxidsParam>,
|
||||
State(state): State<AppState>
|
||||
| {
|
||||
state.cached_json(&headers, CacheStrategy::Height, &uri, move |q| q.addr_txs(path.addr, params.after_txid, 25)).await
|
||||
let strategy = state.addr_cache(Version::ONE, &path.addr);
|
||||
state.cached_json(&headers, strategy, &uri, move |q| q.addr_txs(path.addr, params.after_txid, 25)).await
|
||||
}, |op| op
|
||||
.id("get_address_confirmed_txs")
|
||||
.addrs_tag()
|
||||
@@ -115,7 +118,8 @@ impl AddrRoutes for ApiRouter<AppState> {
|
||||
Path(path): Path<AddrParam>,
|
||||
State(state): State<AppState>
|
||||
| {
|
||||
state.cached_json(&headers, CacheStrategy::Height, &uri, move |q| q.addr_utxos(path.addr)).await
|
||||
let strategy = state.addr_cache(Version::ONE, &path.addr);
|
||||
state.cached_json(&headers, strategy, &uri, move |q| q.addr_utxos(path.addr)).await
|
||||
}, |op| op
|
||||
.id("get_address_utxos")
|
||||
.addrs_tag()
|
||||
|
||||
@@ -6,7 +6,7 @@ use axum::{
|
||||
use brk_query::BLOCK_TXS_PAGE_SIZE;
|
||||
use brk_types::{
|
||||
BlockHashParam, BlockHashStartIndex, BlockHashTxIndex, BlockInfo, BlockInfoV1, BlockStatus,
|
||||
BlockTimestamp, HeightParam, TimestampParam, Transaction, TxIndex, Txid,
|
||||
BlockTimestamp, HeightParam, TimestampParam, Transaction, TxIndex, Txid, Version,
|
||||
};
|
||||
|
||||
use crate::{AppState, CacheStrategy, extended::TransformResponseExtended};
|
||||
@@ -24,7 +24,8 @@ impl BlockRoutes for ApiRouter<AppState> {
|
||||
headers: HeaderMap,
|
||||
Path(path): Path<BlockHashParam>,
|
||||
State(state): State<AppState>| {
|
||||
state.cached_json(&headers, CacheStrategy::Static, &uri, move |q| q.block(&path.hash)).await
|
||||
let strategy = state.block_cache(Version::ONE, &path.hash);
|
||||
state.cached_json(&headers, strategy, &uri, move |q| q.block(&path.hash)).await
|
||||
},
|
||||
|op| {
|
||||
op.id("get_block")
|
||||
@@ -45,7 +46,8 @@ impl BlockRoutes for ApiRouter<AppState> {
|
||||
"/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 strategy = state.block_cache(Version::ONE, &path.hash);
|
||||
state.cached_json(&headers, strategy, &uri, move |q| {
|
||||
let height = q.height_by_hash(&path.hash)?;
|
||||
q.block_by_height_v1(height)
|
||||
}).await
|
||||
@@ -66,7 +68,8 @@ impl BlockRoutes for ApiRouter<AppState> {
|
||||
"/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
|
||||
let strategy = state.block_cache(Version::ONE, &path.hash);
|
||||
state.cached_text(&headers, strategy, &uri, move |q| q.block_header_hex(&path.hash)).await
|
||||
},
|
||||
|op| {
|
||||
op.id("get_block_header")
|
||||
@@ -87,7 +90,7 @@ impl BlockRoutes for ApiRouter<AppState> {
|
||||
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
|
||||
state.cached_text(&headers, state.height_cache(Version::ONE, path.height), &uri, move |q| q.block_hash_by_height(path.height).map(|h| h.to_string())).await
|
||||
},
|
||||
|op| {
|
||||
op.id("get_block_by_height")
|
||||
@@ -111,7 +114,7 @@ impl BlockRoutes for ApiRouter<AppState> {
|
||||
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
|
||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.block_by_timestamp(path.timestamp)).await
|
||||
},
|
||||
|op| {
|
||||
op.id("get_block_by_timestamp")
|
||||
@@ -133,7 +136,8 @@ impl BlockRoutes for ApiRouter<AppState> {
|
||||
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
|
||||
let strategy = state.block_cache(Version::ONE, &path.hash);
|
||||
state.cached_bytes(&headers, strategy, &uri, move |q| q.block_raw(&path.hash)).await
|
||||
},
|
||||
|op| {
|
||||
op.id("get_block_raw")
|
||||
@@ -157,7 +161,7 @@ impl BlockRoutes for ApiRouter<AppState> {
|
||||
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
|
||||
state.cached_json(&headers, state.block_status_cache(Version::ONE, &path.hash), &uri, move |q| q.block_status(&path.hash)).await
|
||||
},
|
||||
|op| {
|
||||
op.id("get_block_status")
|
||||
@@ -178,7 +182,7 @@ impl BlockRoutes for ApiRouter<AppState> {
|
||||
"/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
|
||||
state.cached_text(&headers, CacheStrategy::Tip, &uri, |q| Ok(q.height().to_string())).await
|
||||
},
|
||||
|op| {
|
||||
op.id("get_block_tip_height")
|
||||
@@ -195,7 +199,7 @@ impl BlockRoutes for ApiRouter<AppState> {
|
||||
"/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
|
||||
state.cached_text(&headers, CacheStrategy::Tip, &uri, |q| q.block_hash_by_height(q.height()).map(|h| h.to_string())).await
|
||||
},
|
||||
|op| {
|
||||
op.id("get_block_tip_hash")
|
||||
@@ -215,7 +219,8 @@ impl BlockRoutes for ApiRouter<AppState> {
|
||||
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
|
||||
let strategy = state.block_cache(Version::ONE, &path.hash);
|
||||
state.cached_text(&headers, strategy, &uri, move |q| q.block_txid_at_index(&path.hash, path.index).map(|t| t.to_string())).await
|
||||
},
|
||||
|op| {
|
||||
op.id("get_block_txid")
|
||||
@@ -239,7 +244,8 @@ impl BlockRoutes for ApiRouter<AppState> {
|
||||
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
|
||||
let strategy = state.block_cache(Version::ONE, &path.hash);
|
||||
state.cached_json(&headers, strategy, &uri, move |q| q.block_txids(&path.hash)).await
|
||||
},
|
||||
|op| {
|
||||
op.id("get_block_txids")
|
||||
@@ -263,7 +269,8 @@ impl BlockRoutes for ApiRouter<AppState> {
|
||||
headers: HeaderMap,
|
||||
Path(path): Path<BlockHashParam>,
|
||||
State(state): State<AppState>| {
|
||||
state.cached_json(&headers, CacheStrategy::Static, &uri, move |q| q.block_txs(&path.hash, TxIndex::default())).await
|
||||
let strategy = state.block_cache(Version::ONE, &path.hash);
|
||||
state.cached_json(&headers, strategy, &uri, move |q| q.block_txs(&path.hash, TxIndex::default())).await
|
||||
},
|
||||
|op| {
|
||||
op.id("get_block_txs")
|
||||
@@ -288,7 +295,8 @@ impl BlockRoutes for ApiRouter<AppState> {
|
||||
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
|
||||
let strategy = state.block_cache(Version::ONE, &path.hash);
|
||||
state.cached_json(&headers, strategy, &uri, move |q| q.block_txs(&path.hash, path.start_index)).await
|
||||
},
|
||||
|op| {
|
||||
op.id("get_block_txs_from_index")
|
||||
@@ -311,7 +319,7 @@ impl BlockRoutes for ApiRouter<AppState> {
|
||||
get_with(
|
||||
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
|
||||
state
|
||||
.cached_json(&headers, CacheStrategy::Height, &uri, move |q| q.blocks(None))
|
||||
.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.blocks(None))
|
||||
.await
|
||||
},
|
||||
|op| {
|
||||
@@ -332,7 +340,7 @@ impl BlockRoutes for ApiRouter<AppState> {
|
||||
headers: HeaderMap,
|
||||
Path(path): Path<HeightParam>,
|
||||
State(state): State<AppState>| {
|
||||
state.cached_json(&headers, CacheStrategy::Height, &uri, move |q| q.blocks(Some(path.height))).await
|
||||
state.cached_json(&headers, state.height_cache(Version::ONE, path.height), &uri, move |q| q.blocks(Some(path.height))).await
|
||||
},
|
||||
|op| {
|
||||
op.id("get_blocks_from_height")
|
||||
@@ -353,7 +361,7 @@ impl BlockRoutes for ApiRouter<AppState> {
|
||||
get_with(
|
||||
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
|
||||
state
|
||||
.cached_json(&headers, CacheStrategy::Height, &uri, move |q| q.blocks_v1(None))
|
||||
.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.blocks_v1(None))
|
||||
.await
|
||||
},
|
||||
|op| {
|
||||
@@ -374,7 +382,7 @@ impl BlockRoutes for ApiRouter<AppState> {
|
||||
headers: HeaderMap,
|
||||
Path(path): Path<HeightParam>,
|
||||
State(state): State<AppState>| {
|
||||
state.cached_json(&headers, CacheStrategy::Height, &uri, move |q| q.blocks_v1(Some(path.height))).await
|
||||
state.cached_json(&headers, state.height_cache(Version::ONE, path.height), &uri, move |q| q.blocks_v1(Some(path.height))).await
|
||||
},
|
||||
|op| {
|
||||
op.id("get_blocks_v1_from_height")
|
||||
|
||||
@@ -18,7 +18,7 @@ impl GeneralRoutes for ApiRouter<AppState> {
|
||||
get_with(
|
||||
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
|
||||
state
|
||||
.cached_json(&headers, CacheStrategy::Height, &uri, |q| {
|
||||
.cached_json(&headers, CacheStrategy::Tip, &uri, |q| {
|
||||
q.difficulty_adjustment()
|
||||
})
|
||||
.await
|
||||
@@ -65,7 +65,7 @@ impl GeneralRoutes for ApiRouter<AppState> {
|
||||
Query(params): Query<OptionalTimestampParam>,
|
||||
State(state): State<AppState>| {
|
||||
state
|
||||
.cached_json(&headers, CacheStrategy::Height, &uri, move |q| {
|
||||
.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| {
|
||||
q.historical_price(params.timestamp)
|
||||
})
|
||||
.await
|
||||
|
||||
@@ -45,7 +45,7 @@ impl MiningRoutes for ApiRouter<AppState> {
|
||||
"/api/v1/mining/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.mining_pools(path.time_period)).await
|
||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.mining_pools(path.time_period)).await
|
||||
},
|
||||
|op| {
|
||||
op.id("get_pool_stats")
|
||||
@@ -62,7 +62,7 @@ impl MiningRoutes for ApiRouter<AppState> {
|
||||
"/api/v1/mining/pool/{slug}",
|
||||
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_detail(path.slug)).await
|
||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.pool_detail(path.slug)).await
|
||||
},
|
||||
|op| {
|
||||
op.id("get_pool")
|
||||
@@ -80,7 +80,7 @@ impl MiningRoutes for ApiRouter<AppState> {
|
||||
"/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
|
||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, |q| q.pools_hashrate(None)).await
|
||||
},
|
||||
|op| {
|
||||
op.id("get_pools_hashrate")
|
||||
@@ -97,7 +97,7 @@ impl MiningRoutes for ApiRouter<AppState> {
|
||||
"/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
|
||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.pools_hashrate(Some(path.time_period))).await
|
||||
},
|
||||
|op| {
|
||||
op.id("get_pools_hashrate_by_period")
|
||||
@@ -114,7 +114,7 @@ impl MiningRoutes for ApiRouter<AppState> {
|
||||
"/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
|
||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.pool_hashrate(path.slug)).await
|
||||
},
|
||||
|op| {
|
||||
op.id("get_pool_hashrate")
|
||||
@@ -132,7 +132,7 @@ impl MiningRoutes for ApiRouter<AppState> {
|
||||
"/api/v1/mining/pool/{slug}/blocks",
|
||||
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_blocks(path.slug, None)).await
|
||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.pool_blocks(path.slug, None)).await
|
||||
},
|
||||
|op| {
|
||||
op.id("get_pool_blocks")
|
||||
@@ -150,7 +150,7 @@ impl MiningRoutes for ApiRouter<AppState> {
|
||||
"/api/v1/mining/pool/{slug}/blocks/{height}",
|
||||
get_with(
|
||||
async |uri: Uri, headers: HeaderMap, Path(PoolSlugAndHeightParam {slug, height}): Path<PoolSlugAndHeightParam>, State(state): State<AppState>| {
|
||||
state.cached_json(&headers, CacheStrategy::Height, &uri, move |q| q.pool_blocks(slug, Some(height))).await
|
||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.pool_blocks(slug, Some(height))).await
|
||||
},
|
||||
|op| {
|
||||
op.id("get_pool_blocks_from")
|
||||
@@ -168,7 +168,7 @@ impl MiningRoutes for ApiRouter<AppState> {
|
||||
"/api/v1/mining/hashrate",
|
||||
get_with(
|
||||
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
|
||||
state.cached_json(&headers, CacheStrategy::Height, &uri, |q| q.hashrate(None)).await
|
||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, |q| q.hashrate(None)).await
|
||||
},
|
||||
|op| {
|
||||
op.id("get_hashrate")
|
||||
@@ -185,7 +185,7 @@ impl MiningRoutes for ApiRouter<AppState> {
|
||||
"/api/v1/mining/hashrate/{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.hashrate(Some(path.time_period))).await
|
||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.hashrate(Some(path.time_period))).await
|
||||
},
|
||||
|op| {
|
||||
op.id("get_hashrate_by_period")
|
||||
@@ -202,7 +202,7 @@ impl MiningRoutes for ApiRouter<AppState> {
|
||||
"/api/v1/mining/difficulty-adjustments",
|
||||
get_with(
|
||||
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
|
||||
state.cached_json(&headers, CacheStrategy::Height, &uri, |q| q.difficulty_adjustments(None)).await
|
||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, |q| q.difficulty_adjustments(None)).await
|
||||
},
|
||||
|op| {
|
||||
op.id("get_difficulty_adjustments")
|
||||
@@ -219,7 +219,7 @@ impl MiningRoutes for ApiRouter<AppState> {
|
||||
"/api/v1/mining/difficulty-adjustments/{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.difficulty_adjustments(Some(path.time_period))).await
|
||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.difficulty_adjustments(Some(path.time_period))).await
|
||||
},
|
||||
|op| {
|
||||
op.id("get_difficulty_adjustments_by_period")
|
||||
@@ -236,7 +236,7 @@ impl MiningRoutes for ApiRouter<AppState> {
|
||||
"/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
|
||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.reward_stats(path.block_count)).await
|
||||
},
|
||||
|op| {
|
||||
op.id("get_reward_stats")
|
||||
@@ -253,7 +253,7 @@ impl MiningRoutes for ApiRouter<AppState> {
|
||||
"/api/v1/mining/blocks/fees/{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.block_fees(path.time_period)).await
|
||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.block_fees(path.time_period)).await
|
||||
},
|
||||
|op| {
|
||||
op.id("get_block_fees")
|
||||
@@ -270,7 +270,7 @@ impl MiningRoutes for ApiRouter<AppState> {
|
||||
"/api/v1/mining/blocks/rewards/{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.block_rewards(path.time_period)).await
|
||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.block_rewards(path.time_period)).await
|
||||
},
|
||||
|op| {
|
||||
op.id("get_block_rewards")
|
||||
@@ -302,7 +302,7 @@ impl MiningRoutes for ApiRouter<AppState> {
|
||||
"/api/v1/mining/blocks/sizes-weights/{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.block_sizes_weights(path.time_period)).await
|
||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.block_sizes_weights(path.time_period)).await
|
||||
},
|
||||
|op| {
|
||||
op.id("get_block_sizes_weights")
|
||||
|
||||
@@ -7,7 +7,8 @@ use axum::{
|
||||
http::{HeaderMap, Uri},
|
||||
};
|
||||
use brk_types::{
|
||||
CpfpInfo, MerkleProof, Transaction, TxOutspend, TxStatus, Txid, TxidParam, TxidVout, TxidsParam,
|
||||
CpfpInfo, MerkleProof, Transaction, TxOutspend, TxStatus, Txid, TxidParam, TxidVout,
|
||||
TxidsParam, Version,
|
||||
};
|
||||
|
||||
use crate::{AppState, CacheStrategy, extended::TransformResponseExtended};
|
||||
@@ -22,8 +23,8 @@ impl TxRoutes for ApiRouter<AppState> {
|
||||
.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, state.mempool_cache(), &uri, move |q| q.cpfp(txid)).await
|
||||
async |uri: Uri, headers: HeaderMap, Path(param): Path<TxidParam>, State(state): State<AppState>| {
|
||||
state.cached_json(&headers, state.tx_cache(Version::ONE, ¶m.txid), &uri, move |q| q.cpfp(param)).await
|
||||
},
|
||||
|op| op
|
||||
.id("get_cpfp")
|
||||
@@ -41,10 +42,10 @@ impl TxRoutes for ApiRouter<AppState> {
|
||||
async |
|
||||
uri: Uri,
|
||||
headers: HeaderMap,
|
||||
Path(txid): Path<TxidParam>,
|
||||
Path(param): Path<TxidParam>,
|
||||
State(state): State<AppState>
|
||||
| {
|
||||
state.cached_json(&headers, CacheStrategy::Height, &uri, move |q| q.transaction(txid)).await
|
||||
state.cached_json(&headers, state.tx_cache(Version::ONE, ¶m.txid), &uri, move |q| q.transaction(param)).await
|
||||
},
|
||||
|op| op
|
||||
.id("get_tx")
|
||||
@@ -69,7 +70,7 @@ impl TxRoutes for ApiRouter<AppState> {
|
||||
Path(txid): Path<TxidParam>,
|
||||
State(state): State<AppState>
|
||||
| {
|
||||
state.cached_text(&headers, CacheStrategy::Height, &uri, move |q| q.transaction_hex(txid)).await
|
||||
state.cached_text(&headers, state.tx_cache(Version::ONE, &txid.txid), &uri, move |q| q.transaction_hex(txid)).await
|
||||
},
|
||||
|op| op
|
||||
.id("get_tx_hex")
|
||||
@@ -89,7 +90,7 @@ impl TxRoutes for ApiRouter<AppState> {
|
||||
"/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
|
||||
state.cached_text(&headers, state.tx_cache(Version::ONE, &txid.txid), &uri, move |q| q.merkleblock_proof(txid)).await
|
||||
},
|
||||
|op| op
|
||||
.id("get_tx_merkleblock_proof")
|
||||
@@ -107,7 +108,7 @@ impl TxRoutes for ApiRouter<AppState> {
|
||||
"/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
|
||||
state.cached_json(&headers, state.tx_cache(Version::ONE, &txid.txid), &uri, move |q| q.merkle_proof(txid)).await
|
||||
},
|
||||
|op| op
|
||||
.id("get_tx_merkle_proof")
|
||||
@@ -131,7 +132,7 @@ impl TxRoutes for ApiRouter<AppState> {
|
||||
State(state): State<AppState>
|
||||
| {
|
||||
let txid = TxidParam { txid: path.txid };
|
||||
state.cached_json(&headers, CacheStrategy::Height, &uri, move |q| q.outspend(txid, path.vout)).await
|
||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.outspend(txid, path.vout)).await
|
||||
},
|
||||
|op| op
|
||||
.id("get_tx_outspend")
|
||||
@@ -156,7 +157,7 @@ impl TxRoutes for ApiRouter<AppState> {
|
||||
Path(txid): Path<TxidParam>,
|
||||
State(state): State<AppState>
|
||||
| {
|
||||
state.cached_json(&headers, CacheStrategy::Height, &uri, move |q| q.outspends(txid)).await
|
||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.outspends(txid)).await
|
||||
},
|
||||
|op| op
|
||||
.id("get_tx_outspends")
|
||||
@@ -176,7 +177,7 @@ impl TxRoutes for ApiRouter<AppState> {
|
||||
"/api/tx/{txid}/raw",
|
||||
get_with(
|
||||
async |uri: Uri, headers: HeaderMap, Path(txid): Path<TxidParam>, State(state): State<AppState>| {
|
||||
state.cached_bytes(&headers, CacheStrategy::Height, &uri, move |q| q.transaction_raw(txid)).await
|
||||
state.cached_bytes(&headers, state.tx_cache(Version::ONE, &txid.txid), &uri, move |q| q.transaction_raw(txid)).await
|
||||
},
|
||||
|op| op
|
||||
.id("get_tx_raw")
|
||||
@@ -196,10 +197,10 @@ impl TxRoutes for ApiRouter<AppState> {
|
||||
async |
|
||||
uri: Uri,
|
||||
headers: HeaderMap,
|
||||
Path(txid): Path<TxidParam>,
|
||||
Path(param): Path<TxidParam>,
|
||||
State(state): State<AppState>
|
||||
| {
|
||||
state.cached_json(&headers, CacheStrategy::Height, &uri, move |q| q.transaction_status(txid)).await
|
||||
state.cached_json(&headers, state.tx_cache(Version::ONE, ¶m.txid), &uri, move |q| q.transaction_status(param)).await
|
||||
},
|
||||
|op| op
|
||||
.id("get_tx_status")
|
||||
|
||||
@@ -275,7 +275,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter<AppState> {
|
||||
State(state): State<AppState>,
|
||||
Path(path): Path<LegacySeriesWithIndex>| {
|
||||
state
|
||||
.cached_json(&headers, CacheStrategy::Height, &uri, move |q| {
|
||||
.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| {
|
||||
q.latest(&path.metric, path.index)
|
||||
})
|
||||
.await
|
||||
@@ -301,7 +301,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter<AppState> {
|
||||
State(state): State<AppState>,
|
||||
Path(path): Path<LegacySeriesWithIndex>| {
|
||||
state
|
||||
.cached_json(&headers, CacheStrategy::Height, &uri, move |q| {
|
||||
.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| {
|
||||
q.len(&path.metric, path.index)
|
||||
})
|
||||
.await
|
||||
@@ -327,7 +327,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter<AppState> {
|
||||
State(state): State<AppState>,
|
||||
Path(path): Path<LegacySeriesWithIndex>| {
|
||||
state
|
||||
.cached_json(&headers, CacheStrategy::Height, &uri, move |q| {
|
||||
.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| {
|
||||
q.version(&path.metric, path.index)
|
||||
})
|
||||
.await
|
||||
@@ -376,7 +376,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter<AppState> {
|
||||
Path(params): Path<CostBasisCohortParam>,
|
||||
State(state): State<AppState>| {
|
||||
state
|
||||
.cached_json(&headers, CacheStrategy::Height, &uri, move |q| {
|
||||
.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| {
|
||||
q.cost_basis_dates(¶ms.cohort)
|
||||
})
|
||||
.await
|
||||
|
||||
@@ -246,7 +246,7 @@ impl ApiSeriesRoutes for ApiRouter<AppState> {
|
||||
State(state): State<AppState>,
|
||||
Path(path): Path<SeriesNameWithIndex>| {
|
||||
state
|
||||
.cached_json(&headers, CacheStrategy::Height, &uri, move |q| {
|
||||
.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| {
|
||||
q.latest(&path.series, path.index)
|
||||
})
|
||||
.await
|
||||
@@ -270,7 +270,7 @@ impl ApiSeriesRoutes for ApiRouter<AppState> {
|
||||
State(state): State<AppState>,
|
||||
Path(path): Path<SeriesNameWithIndex>| {
|
||||
state
|
||||
.cached_json(&headers, CacheStrategy::Height, &uri, move |q| {
|
||||
.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| {
|
||||
q.len(&path.series, path.index)
|
||||
})
|
||||
.await
|
||||
@@ -292,7 +292,7 @@ impl ApiSeriesRoutes for ApiRouter<AppState> {
|
||||
State(state): State<AppState>,
|
||||
Path(path): Path<SeriesNameWithIndex>| {
|
||||
state
|
||||
.cached_json(&headers, CacheStrategy::Height, &uri, move |q| {
|
||||
.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| {
|
||||
q.version(&path.series, path.index)
|
||||
})
|
||||
.await
|
||||
@@ -352,7 +352,7 @@ impl ApiSeriesRoutes for ApiRouter<AppState> {
|
||||
Path(params): Path<CostBasisCohortParam>,
|
||||
State(state): State<AppState>| {
|
||||
state
|
||||
.cached_json(&headers, CacheStrategy::Height, &uri, move |q| {
|
||||
.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| {
|
||||
q.cost_basis_dates(¶ms.cohort)
|
||||
})
|
||||
.await
|
||||
|
||||
@@ -77,7 +77,7 @@ impl ServerRoutes for ApiRouter<AppState> {
|
||||
get_with(
|
||||
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
|
||||
state
|
||||
.cached_json(&headers, CacheStrategy::Height, &uri, move |q| {
|
||||
.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| {
|
||||
let tip_height = q.client().get_last_height()?;
|
||||
Ok(q.sync_status(tip_height))
|
||||
})
|
||||
@@ -102,7 +102,7 @@ impl ServerRoutes for ApiRouter<AppState> {
|
||||
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
|
||||
let brk_path = state.data_path.clone();
|
||||
state
|
||||
.cached_json(&headers, CacheStrategy::Height, &uri, move |q| {
|
||||
.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| {
|
||||
let brk_bytes = dir_size(&brk_path)?;
|
||||
let bitcoin_bytes = dir_size(q.blocks_dir())?;
|
||||
Ok(DiskUsage::new(brk_bytes, bitcoin_bytes))
|
||||
|
||||
@@ -1,19 +1,28 @@
|
||||
use axum::http::HeaderMap;
|
||||
use brk_types::{BlockHashPrefix, Version};
|
||||
|
||||
use crate::{VERSION, extended::HeaderMapExtended};
|
||||
|
||||
/// Cache strategy for HTTP responses.
|
||||
pub enum CacheStrategy {
|
||||
/// Data that changes with each new block (addresses, mining stats, txs, outspends)
|
||||
/// Etag = VERSION-{height}, Cache-Control: must-revalidate
|
||||
Height,
|
||||
/// Chain-dependent data (addresses, mining stats, txs, outspends).
|
||||
/// Etag = {tip_hash_prefix:x}. Invalidates on any tip change including reorgs.
|
||||
Tip,
|
||||
|
||||
/// Static/immutable data (blocks by hash, validate-address, series catalog)
|
||||
/// Etag = VERSION only, Cache-Control: must-revalidate
|
||||
/// Immutable data identified by hash in the URL (blocks by hash, confirmed tx data).
|
||||
/// Etag = {version}. Permanent; only bumped when response format changes.
|
||||
Immutable(Version),
|
||||
|
||||
/// Static non-chain data (validate-address, series catalog, pool list).
|
||||
/// Etag = CARGO_PKG_VERSION. Invalidates on deploy.
|
||||
Static,
|
||||
|
||||
/// Mempool data - etag from next projected block hash + short max-age
|
||||
/// Etag = VERSION-m{hash:x}, Cache-Control: max-age=1, must-revalidate
|
||||
/// Immutable data bound to a specific block (confirmed tx data, block status).
|
||||
/// Etag = {version}-{block_hash_prefix:x}. Invalidates naturally on reorg.
|
||||
BlockBound(Version, BlockHashPrefix),
|
||||
|
||||
/// Mempool data — etag from next projected block hash.
|
||||
/// Etag = m{hash:x}. Invalidates on mempool change.
|
||||
MempoolHash(u64),
|
||||
}
|
||||
|
||||
@@ -24,9 +33,12 @@ pub struct CacheParams {
|
||||
}
|
||||
|
||||
impl CacheParams {
|
||||
/// Cache params using VERSION as etag
|
||||
pub fn version() -> Self {
|
||||
Self::resolve(&CacheStrategy::Static, || unreachable!())
|
||||
/// Cache params using CARGO_PKG_VERSION as etag (for openapi.json etc.)
|
||||
pub fn static_version() -> Self {
|
||||
Self {
|
||||
etag: Some(format!("s{VERSION}")),
|
||||
cache_control: "public, max-age=1, must-revalidate".into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn etag_str(&self) -> &str {
|
||||
@@ -39,20 +51,28 @@ impl CacheParams {
|
||||
.is_some_and(|etag| headers.has_etag(etag))
|
||||
}
|
||||
|
||||
pub fn resolve(strategy: &CacheStrategy, height: impl FnOnce() -> u32) -> Self {
|
||||
use CacheStrategy::*;
|
||||
pub fn resolve(strategy: &CacheStrategy, tip: impl FnOnce() -> BlockHashPrefix) -> Self {
|
||||
let cache_control = "public, max-age=1, must-revalidate".into();
|
||||
match strategy {
|
||||
Height => Self {
|
||||
etag: Some(format!("{VERSION}-{}", height())),
|
||||
cache_control: "public, max-age=1, must-revalidate".into(),
|
||||
CacheStrategy::Tip => Self {
|
||||
etag: Some(format!("t{:x}", *tip())),
|
||||
cache_control,
|
||||
},
|
||||
Static => Self {
|
||||
etag: Some(VERSION.to_string()),
|
||||
cache_control: "public, max-age=1, must-revalidate".into(),
|
||||
CacheStrategy::Immutable(v) => Self {
|
||||
etag: Some(format!("i{v}")),
|
||||
cache_control,
|
||||
},
|
||||
MempoolHash(hash) => Self {
|
||||
etag: Some(format!("{VERSION}-m{hash:x}")),
|
||||
cache_control: "public, max-age=1, must-revalidate".into(),
|
||||
CacheStrategy::BlockBound(v, prefix) => Self {
|
||||
etag: Some(format!("b{v}-{:x}", **prefix)),
|
||||
cache_control,
|
||||
},
|
||||
CacheStrategy::Static => Self {
|
||||
etag: Some(format!("s{VERSION}")),
|
||||
cache_control,
|
||||
},
|
||||
CacheStrategy::MempoolHash(hash) => Self {
|
||||
etag: Some(format!("m{hash:x}")),
|
||||
cache_control,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ impl ResponseExtended for Response<Body> {
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
let params = CacheParams::version();
|
||||
let params = CacheParams::static_version();
|
||||
if params.matches_etag(headers) {
|
||||
return Self::new_not_modified();
|
||||
}
|
||||
|
||||
@@ -5,13 +5,13 @@ use std::{
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use derive_more::Deref;
|
||||
|
||||
use axum::{
|
||||
body::{Body, Bytes},
|
||||
http::{HeaderMap, HeaderValue, Response, Uri, header},
|
||||
};
|
||||
use brk_query::AsyncQuery;
|
||||
use brk_types::{Addr, BlockHash, BlockHashPrefix, Height, Txid, Version};
|
||||
use derive_more::Deref;
|
||||
use jiff::Timestamp;
|
||||
use quick_cache::sync::{Cache, GuardResult};
|
||||
use serde::Serialize;
|
||||
@@ -33,6 +33,80 @@ pub struct AppState {
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
/// `Immutable` if height is >6 deep, `Tip` otherwise.
|
||||
pub fn height_cache(&self, version: Version, height: Height) -> CacheStrategy {
|
||||
let is_deep = self.sync(|q| (*q.height()).saturating_sub(*height) > 6);
|
||||
if is_deep {
|
||||
CacheStrategy::Immutable(version)
|
||||
} else {
|
||||
CacheStrategy::Tip
|
||||
}
|
||||
}
|
||||
|
||||
/// Smart address caching: checks mempool activity first, then on-chain.
|
||||
/// - Address has mempool txs → `MempoolHash(addr_specific_hash)`
|
||||
/// - No mempool, has on-chain activity → `BlockBound(last_activity_block)`
|
||||
/// - Unknown address → `Tip`
|
||||
pub fn addr_cache(&self, version: Version, addr: &Addr) -> CacheStrategy {
|
||||
self.sync(|q| {
|
||||
let mempool_hash = q.addr_mempool_hash(addr);
|
||||
if mempool_hash != 0 {
|
||||
return CacheStrategy::MempoolHash(mempool_hash);
|
||||
}
|
||||
q.addr_last_activity_height(addr)
|
||||
.and_then(|h| {
|
||||
let block_hash = q.block_hash_by_height(h)?;
|
||||
Ok(CacheStrategy::BlockBound(
|
||||
version,
|
||||
BlockHashPrefix::from(&block_hash),
|
||||
))
|
||||
})
|
||||
.unwrap_or(CacheStrategy::Tip)
|
||||
})
|
||||
}
|
||||
|
||||
/// `Immutable` if the block is >6 deep (status stable), `Tip` otherwise.
|
||||
/// For block status which changes when the next block arrives.
|
||||
pub fn block_status_cache(&self, version: Version, hash: &BlockHash) -> CacheStrategy {
|
||||
self.sync(|q| {
|
||||
q.height_by_hash(hash)
|
||||
.map(|h| {
|
||||
if (*q.height()).saturating_sub(*h) > 6 {
|
||||
CacheStrategy::Immutable(version)
|
||||
} else {
|
||||
CacheStrategy::Tip
|
||||
}
|
||||
})
|
||||
.unwrap_or(CacheStrategy::Tip)
|
||||
})
|
||||
}
|
||||
|
||||
/// `BlockBound` if the block exists (reorg-safe via block hash), `Tip` if not found.
|
||||
pub fn block_cache(&self, version: Version, hash: &BlockHash) -> CacheStrategy {
|
||||
self.sync(|q| {
|
||||
if q.height_by_hash(hash).is_ok() {
|
||||
CacheStrategy::BlockBound(version, BlockHashPrefix::from(hash))
|
||||
} else {
|
||||
CacheStrategy::Tip
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Mempool → `MempoolHash`, confirmed → `BlockBound`, unknown → `Tip`.
|
||||
pub fn tx_cache(&self, version: Version, txid: &Txid) -> CacheStrategy {
|
||||
self.sync(|q| {
|
||||
if q.mempool().is_some_and(|m| m.get_txs().contains(txid)) {
|
||||
let hash = q.mempool().map(|m| m.next_block_hash()).unwrap_or(0);
|
||||
return CacheStrategy::MempoolHash(hash);
|
||||
} else if let Ok((_, height)) = q.resolve_tx(txid)
|
||||
&& let Ok(block_hash) = q.block_hash_by_height(height)
|
||||
{
|
||||
return CacheStrategy::BlockBound(version, BlockHashPrefix::from(&block_hash));
|
||||
}
|
||||
CacheStrategy::Tip
|
||||
})
|
||||
}
|
||||
|
||||
pub fn mempool_cache(&self) -> CacheStrategy {
|
||||
let hash = self.sync(|q| q.mempool().map(|m| m.next_block_hash()).unwrap_or(0));
|
||||
CacheStrategy::MempoolHash(hash)
|
||||
@@ -51,7 +125,7 @@ impl AppState {
|
||||
F: FnOnce(&brk_query::Query, ContentEncoding) -> brk_error::Result<Bytes> + Send + 'static,
|
||||
{
|
||||
let encoding = ContentEncoding::negotiate(headers);
|
||||
let params = CacheParams::resolve(&strategy, || self.sync(|q| q.height().into()));
|
||||
let params = CacheParams::resolve(&strategy, || self.sync(|q| q.tip_hash_prefix()));
|
||||
if params.matches_etag(headers) {
|
||||
return ResponseExtended::new_not_modified();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user