ns-indexer 0.6.0

Name & Service Protocol indexer service in Rust
Documentation
use axum::{
    extract::{Query, State},
    Extension,
};
use bitcoin::{Address, AddressType};
use std::{collections::BTreeMap, str::FromStr, sync::Arc};
use validator::Validate;

use ns_axum_web::{
    context::ReqContext,
    erring::{HTTPError, SuccessResponse},
    object::PackObject,
};

use crate::api::{IndexerAPI, QueryAddress};
use crate::db;
use crate::utxo::UTXO;

pub struct UtxoAPI;

impl UtxoAPI {
    pub async fn list(
        State(app): State<Arc<IndexerAPI>>,
        Extension(ctx): Extension<Arc<ReqContext>>,
        to: PackObject<()>,
        input: Query<QueryAddress>,
    ) -> Result<PackObject<SuccessResponse<Vec<UTXO>>>, HTTPError> {
        input.validate()?;

        let address = Address::from_str(input.address.as_str())
            .map_err(|_| HTTPError::new(400, format!("invalid address: {}", input.address)))?
            .assume_checked();

        match address.address_type() {
            Some(AddressType::P2tr) | Some(AddressType::P2wpkh) => {}
            other => {
                return Err(HTTPError::new(
                    400,
                    format!("only support p2tr address, got: {:?}", other),
                ));
            }
        }

        ctx.set_kvs(vec![("action", "list_utxos_by_address".into())])
            .await;

        let address = address.script_pubkey().as_bytes().to_vec();
        let utxos = db::Utxo::list(&app.scylla, &address).await?;
        let mut utxos: BTreeMap<(&Vec<u8>, u32), UTXO> = utxos
            .iter()
            .map(|utxo| ((&utxo.txid, utxo.vout as u32), utxo.to_utxo()))
            .collect();

        let confirming_utxos = app.state.confirming_utxos.read().await;
        for utxo in confirming_utxos.iter() {
            for spent in &utxo.1 {
                utxos.remove(&(&spent.txid, spent.vout));
            }
            for (addr, unspent) in &utxo.2 {
                if addr == &address {
                    utxos.insert((&unspent.txid, unspent.vout), unspent.clone());
                }
            }
        }

        let mut utxos = utxos.into_values().collect::<Vec<_>>();
        utxos.sort_by(|a, b| a.amount.partial_cmp(&b.amount).unwrap());
        Ok(to.with(SuccessResponse::new(utxos)))
    }
}