use std::sync::Arc;
use crate::chaindef::OutPointHash;
use crate::chaindef::TokenID;
use crate::chaindef::Transaction;
use crate::indexes::heightindex::HeightIndexRow;
use crate::indexes::inputindex::InputIndexRow;
use crate::indexes::outputindex::OutputIndexRow;
use crate::indexes::outputtokenindex::OutputTokenIndexRow;
use crate::indexes::DBRow;
use crate::mempool::MEMPOOL_HEIGHT;
use crate::query::tx::TxQuery;
use crate::store::{DBStore, Row};
use anyhow::*;
use bitcoin_hashes::Hash;
use bitcoincash::hash_types::Txid;
use futures_util::StreamExt;
pub(crate) async fn get_row<T: DBRow>(store: &DBStore, key: Vec<u8>) -> Option<T> {
let (key, value) = store.get(T::CF, key).await;
let row = Row {
key: key.into_boxed_slice(),
value: value?.into_boxed_slice(),
};
Some(T::from_row(&row))
}
pub(crate) async fn get_row_by_key_prefix<T: DBRow>(
store: &DBStore,
key: Vec<u8>,
) -> Result<Option<T>> {
let (query, mut iter) = store.scan(T::CF, key, None).await;
let res = iter.next().await.map(|r| T::from_row(&r));
query.await??;
Ok(res)
}
pub(crate) async fn height_by_txid(store: &DBStore, txid: &Txid) -> Option<u32> {
let key = txid.to_vec();
let (_key, value) = store.get(HeightIndexRow::CF, key).await;
let height = HeightIndexRow::height_from_dbvalue(&value?);
if height == MEMPOOL_HEIGHT {
None
} else {
Some(height)
}
}
pub async fn get_utxo(store: &DBStore, outpointhash: &OutPointHash) -> Option<OutputIndexRow> {
let key = outpointhash.to_vec();
let (key, value) = store.get(OutputIndexRow::CF, key).await;
Some(OutputIndexRow::from_row(&Row {
key: key.into_boxed_slice(),
value: value?.into_boxed_slice(),
}))
}
pub(crate) async fn multi_get_utxo(
store: &DBStore,
outpointhash: &[OutPointHash],
) -> Vec<Option<OutputIndexRow>> {
let keys: Vec<Vec<u8>> = outpointhash.iter().map(|o| o.to_vec()).collect();
let keys_clone = keys.clone();
let values = store.multi_get(OutputIndexRow::CF, keys).await;
keys_clone
.into_iter()
.zip(values)
.map(|(key, value)| {
Some(OutputIndexRow::from_row(&Row {
key: key.into_boxed_slice(),
value: value?.into_boxed_slice(),
}))
})
.collect()
}
pub(crate) async fn token_from_outpoint(
store: &DBStore,
outpointhash: &OutPointHash,
) -> Result<Option<(TokenID, i64, Option<Vec<u8>>)>> {
let key = OutputTokenIndexRow::filter_by_outpointhash(outpointhash);
Ok(
match get_row_by_key_prefix::<OutputTokenIndexRow>(store, key).await? {
Some(token_row) => {
let amount = token_row.token_amount();
#[cfg(nexa)]
{
Some((token_row.into_token_id(), amount, None))
}
#[cfg(bch)]
{
let (token_id, commitment) = token_row.into_token_id_and_commitment();
Some((
token_id,
amount,
if commitment.is_empty() {
None
} else {
Some(commitment)
},
))
}
}
None => None,
},
)
}
pub(crate) async fn get_tx_spending_prevout(
store: &DBStore,
txquery: &TxQuery,
outpoint: &OutPointHash,
) -> Result<
Option<(
Transaction,
u32, /* input index */
u32, /* confirmation height */
)>,
> {
let spender = tx_spending_outpoint(store, outpoint).await;
if spender.is_none() {
return Ok(None);
};
let spender = spender.unwrap();
let txid = spender.txid();
let height = height_by_txid(store, &txid).await;
let tx = txquery.get(&txid, None, height, false).await?;
Ok(Some((tx, spender.index(), height.unwrap_or_default())))
}
#[inline]
pub(crate) async fn outpoint_is_spent(store: &Arc<DBStore>, outpoint: OutPointHash) -> bool {
store
.exists_32bit_key(InputIndexRow::CF, outpoint.into_inner())
.await
}
#[inline]
pub(crate) async fn outpoints_are_spent(
store: &Arc<DBStore>,
outpoints: &[OutPointHash],
) -> Vec<bool> {
store
.exists_multi_32bit_keys(
InputIndexRow::CF,
outpoints.iter().map(|o| o.into_inner()).collect(),
)
.await
}
pub(crate) async fn tx_spending_outpoint(
store: &DBStore,
outpoint: &OutPointHash,
) -> Option<InputIndexRow> {
let key = InputIndexRow::filter_by_outpointhash(outpoint);
let (key, value) = store.get(InputIndexRow::CF, key).await;
Some(InputIndexRow::from_row(&Row {
key: key.into_boxed_slice(),
value: value?.into_boxed_slice(),
}))
}
pub async fn get_tx_funding_prevout(
store: &DBStore,
txquery: &TxQuery,
outpoint: &OutPointHash,
) -> Result<
Option<(
Transaction,
u32, /* input index */
u32, /* confirmation height */
)>,
> {
let spender = tx_funding_outpoint(store, outpoint).await;
if spender.is_none() {
return Ok(None);
};
let spender = spender.unwrap();
let txid = spender.txid();
let height = height_by_txid(store, &txid).await;
let tx = txquery.get(&txid, None, height, false).await?;
Ok(Some((tx, spender.index(), height.unwrap_or_default())))
}
pub async fn tx_funding_outpoint(
store: &DBStore,
outpoint: &OutPointHash,
) -> Option<OutputIndexRow> {
let key = OutputIndexRow::filter_by_outpointhash(outpoint);
let (key, value) = store.get(OutputIndexRow::CF, key).await;
Some(OutputIndexRow::from_row(&Row {
key: key.into_boxed_slice(),
value: value?.into_boxed_slice(),
}))
}