use std::sync::Arc;
use crate::{
chaindef::{OutPointHash, ScriptHash},
query::{queryutil::outpoints_are_spent, BUFFER_SIZE, CHUNK_SIZE},
store::{DBContents, DBStore},
};
use super::{queryfilter::QueryFilter, status::scan_for_outputs};
use anyhow::Result;
use futures::{stream, StreamExt};
#[derive(Serialize)]
pub struct ScriptHashVolume {
confirmed_sats_in: u64,
confirmed_sats_out: u64,
mempool_sats_in: u64,
mempool_sats_out: u64,
}
pub async fn historical_volume(
confirmed: &Arc<DBStore>,
mempool: &Arc<DBStore>,
scripthash: ScriptHash,
filter: &QueryFilter,
) -> Result<ScriptHashVolume> {
assert!(confirmed.contents == DBContents::ConfirmedIndex);
assert!(mempool.contents == DBContents::MempoolIndex);
let (confirmed_in, confirmed_out, confirmed_unspent) = {
let (outputs_query, outputs_stream) = scan_for_outputs(confirmed, scripthash, filter).await;
let (c_in, c_out, unspent) = outputs_stream
.map(|(o, _)| (o.value(), o.take_hash()))
.ready_chunks(CHUNK_SIZE)
.then(|chunk| {
let (values, outpoints): (Vec<u64>, Vec<OutPointHash>) = chunk.into_iter().unzip();
let db = Arc::clone(confirmed);
async move {
let spent_flags = outpoints_are_spent(&db, &outpoints).await;
stream::iter(values.into_iter().zip(outpoints).zip(spent_flags).map(
|((val, op), spent)| {
if spent {
(val, val, None)
} else {
(val, 0, Some((op, val)))
}
},
))
}
})
.flatten_unordered(BUFFER_SIZE)
.fold(
(0u64, 0u64, Vec::new()),
|(sum_in, sum_out, mut unspent), (vin, vout, maybe_op_val)| async move {
if let Some(op_val) = maybe_op_val {
unspent.push(op_val);
}
(sum_in + vin, sum_out + vout, unspent)
},
)
.await;
outputs_query.await??;
(c_in, c_out, unspent)
};
let mempool_out_from_confirmed = if confirmed_unspent.is_empty() {
0u64
} else {
let (ops, amounts): (Vec<OutPointHash>, Vec<u64>) =
confirmed_unspent.iter().cloned().unzip();
let spent_flags = outpoints_are_spent(mempool, &ops).await;
let mut total_spent_in_mempool = 0u64;
for (val, spent) in amounts.into_iter().zip(spent_flags) {
if spent {
total_spent_in_mempool += val;
}
}
total_spent_in_mempool
};
let (mempool_in, mempool_out) = {
let (mp_query, mp_stream) = scan_for_outputs(mempool, scripthash, filter).await;
let (m_in, m_out) = mp_stream
.map(|(o, _)| (o.value(), o.take_hash()))
.ready_chunks(CHUNK_SIZE)
.then(|chunk| {
let (values, outpoints): (Vec<u64>, Vec<OutPointHash>) = chunk.into_iter().unzip();
let db = Arc::clone(mempool);
async move {
let spent_flags = outpoints_are_spent(&db, &outpoints).await;
stream::iter(values.into_iter().zip(spent_flags).map(|(val, spent)| {
if spent {
(val, val)
} else {
(val, 0u64)
}
}))
}
})
.flatten_unordered(BUFFER_SIZE)
.fold((0u64, 0u64), |(sum_in, sum_out), (vin, vout)| async move {
(sum_in + vin, sum_out + vout)
})
.await;
mp_query.await??;
(m_in, m_out)
};
Ok(ScriptHashVolume {
confirmed_sats_in: confirmed_in,
confirmed_sats_out: confirmed_out,
mempool_sats_in: mempool_in,
mempool_sats_out: mempool_out + mempool_out_from_confirmed,
})
}