use std::sync::Arc;
use crate::chaindef::{BlockHash, ScriptHash};
use crate::edition::Edition;
use crate::errors::rpc_not_found;
use crate::mempool::{Tracker, MEMPOOL_HEIGHT};
use crate::query::balance::{
balance_at_tip, historical_confirmed_balance, historical_unconfirmed_balance,
};
use crate::query::queryfilter::QueryFilter;
use crate::query::status::{
by_block_height, scripthash_history, scripthash_listunspent, unconfirmed_history,
};
use crate::query::volume::historical_volume;
use crate::query::Query;
use crate::rpc::encoding::blockhash_to_hex;
use crate::store::DBStore;
use anyhow::Result;
use bitcoin_hashes::hex::ToHex;
use bitcoin_hashes::Hash;
use rayon::slice::ParallelSliceMut;
use serde_json::Value;
pub async fn get_balance(
query: &Query,
scripthash: ScriptHash,
filter: &QueryFilter,
) -> Result<Value> {
if !filter.has_height_filter() {
let (confirmed, _ctokens, unconfirmed, _utokens) = balance_at_tip(
query.confirmed_index(),
query.unconfirmed_index().index(),
&scripthash,
filter,
&None,
)
.await?;
return Ok(json!({
"confirmed": confirmed,
"unconfirmed": unconfirmed,
}));
}
let confirmed = query.confirmed_index().clone();
let (confirmed, confirmed_outputs) =
historical_confirmed_balance(&confirmed, scripthash, filter).await?;
let unconfirmed = query.unconfirmed_index().index().clone();
let unconfirmed =
historical_unconfirmed_balance(&unconfirmed, confirmed_outputs, scripthash, filter).await?;
Ok(json!({
"confirmed": confirmed,
"unconfirmed": unconfirmed,
}))
}
pub async fn get_first_use(
query: &Query,
scripthash: ScriptHash,
filter: &QueryFilter,
edition: Edition,
) -> Result<Value> {
let firstuse = query.scripthash_first_use(scripthash, filter).await?;
if firstuse.is_none() {
if edition == Edition::Edition2027Pre {
return Ok(Value::Null);
}
return Err(rpc_not_found(format!(
"scripthash '{}' not found",
scripthash
)));
}
let firstuse = firstuse.unwrap();
let blockhash = if firstuse.0 == MEMPOOL_HEIGHT {
BlockHash::all_zeros()
} else {
let h = query.get_headers(&[firstuse.0 as usize]).await;
if h.is_empty() {
warn!("expected to find header for height {}", firstuse.0);
BlockHash::all_zeros()
} else {
h[0].block_hash()
}
};
let height = if firstuse.0 == MEMPOOL_HEIGHT {
0
} else {
firstuse.0
};
Ok(json!({
"block_hash": blockhash_to_hex(&blockhash),
"height": height,
"block_height": height, "tx_hash": firstuse.1.to_hex()
}))
}
pub async fn get_first_spend(
query: &Query,
scripthash: ScriptHash,
filter: &QueryFilter,
) -> Result<Value> {
let Some((height, txid)) = query.scripthash_first_spend(scripthash, filter).await? else {
return Ok(Value::Null);
};
let blockhash = if height == MEMPOOL_HEIGHT {
BlockHash::all_zeros()
} else {
let h = query.get_headers(&[height as usize]).await;
if h.is_empty() {
warn!("expected to find header for height {}", height);
BlockHash::all_zeros()
} else {
h[0].block_hash()
}
};
let result_height = if height == MEMPOOL_HEIGHT { 0 } else { height };
Ok(json!({
"block_hash": blockhash_to_hex(&blockhash),
"height": result_height,
"block_height": result_height, "tx_hash": txid.to_hex()
}))
}
pub async fn get_history(
store: &Arc<DBStore>,
mempool: &Tracker,
scripthash: ScriptHash,
filter: QueryFilter,
) -> Result<Value> {
Ok(json!(
scripthash_history(store, mempool, scripthash, filter, None).await?
))
}
pub(crate) async fn get_mempool(
query: &Query,
scripthash: ScriptHash,
filter: QueryFilter,
) -> Result<Value> {
let mut history =
unconfirmed_history(query.unconfirmed_index(), scripthash, filter, None).await?;
history.par_sort_by(by_block_height);
filter.filter_limit_offset(&mut history);
Ok(json!(history))
}
pub async fn listunspent(
query: &Query,
scripthash: ScriptHash,
filter: QueryFilter,
) -> Result<Value> {
Ok(json!(
scripthash_listunspent(
query.confirmed_index(),
query.unconfirmed_index().index(),
scripthash,
filter,
None,
)
.await?
))
}
pub async fn get_volume(
query: &Query,
scripthash: ScriptHash,
filter: &QueryFilter,
) -> Result<Value> {
Ok(json!(
historical_volume(
query.confirmed_index(),
query.unconfirmed_index().index(),
scripthash,
filter
)
.await?
))
}