dash_sdk/core/
dash_core_client.rsuse crate::error::Error;
use dashcore_rpc::{
    dashcore::{hashes::Hash, Amount, QuorumHash},
    dashcore_rpc_json as json,
    json::{ProTxList, ProTxListType},
    Auth, Client, RpcApi,
};
use dpp::dashcore::ProTxHash;
use dpp::prelude::CoreBlockHeight;
use drive_proof_verifier::error::ContextProviderError;
use std::{fmt::Debug, sync::Mutex};
use zeroize::Zeroizing;
pub struct LowLevelDashCoreClient {
    core: Mutex<Client>,
    server_address: String,
    core_user: String,
    core_password: Zeroizing<String>,
    core_port: u16,
}
impl Clone for LowLevelDashCoreClient {
    fn clone(&self) -> Self {
        LowLevelDashCoreClient::new(
            &self.server_address,
            self.core_port,
            &self.core_user,
            &self.core_password,
        )
        .expect("Failed to clone CoreClient when cloning, this should not happen")
    }
}
impl Debug for LowLevelDashCoreClient {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("CoreClient")
            .field("server_address", &self.server_address)
            .field("core_user", &self.core_user)
            .field("core_port", &self.core_port)
            .finish()
    }
}
impl LowLevelDashCoreClient {
    pub fn new(
        server_address: &str,
        core_port: u16,
        core_user: &str,
        core_password: &str,
    ) -> Result<Self, Error> {
        let addr = format!("http://{}:{}", server_address, core_port);
        let core = Client::new(
            &addr,
            Auth::UserPass(core_user.to_string(), core_password.to_string()),
        )
        .map_err(Error::CoreClientError)?;
        Ok(Self {
            core: Mutex::new(core),
            server_address: server_address.to_string(),
            core_user: core_user.to_string(),
            core_password: core_password.to_string().into(),
            core_port,
        })
    }
}
impl LowLevelDashCoreClient {
    #[allow(dead_code)]
    #[deprecated(note = "This function is marked as unused.")]
    pub fn list_unspent(
        &self,
        minimum_sum_satoshi: Option<u64>,
    ) -> Result<Vec<dashcore_rpc::json::ListUnspentResultEntry>, Error> {
        let options = json::ListUnspentQueryOptions {
            minimum_sum_amount: minimum_sum_satoshi.map(Amount::from_sat),
            ..Default::default()
        };
        self.core
            .lock()
            .expect("Core lock poisoned")
            .list_unspent(None, None, None, None, Some(options))
            .map_err(Error::CoreClientError)
    }
    #[allow(dead_code)]
    #[deprecated(note = "This function is marked as unused.")]
    pub fn get_balance(&self) -> Result<Amount, Error> {
        self.core
            .lock()
            .expect("Core lock poisoned")
            .get_balance(None, None)
            .map_err(Error::CoreClientError)
    }
    pub fn get_quorum_public_key(
        &self,
        quorum_type: u32,
        quorum_hash: [u8; 32],
    ) -> Result<[u8; 48], ContextProviderError> {
        let quorum_hash = QuorumHash::from_slice(&quorum_hash)
            .map_err(|e| ContextProviderError::InvalidQuorum(e.to_string()))?;
        let core = self.core.lock().expect("Core lock poisoned");
        let quorum_info = core
            .get_quorum_info(json::QuorumType::from(quorum_type), &quorum_hash, None)
            .map_err(|e: dashcore_rpc::Error| ContextProviderError::Generic(e.to_string()))?;
        let key = quorum_info.quorum_public_key;
        let pubkey = <Vec<u8> as TryInto<[u8; 48]>>::try_into(key).map_err(|_e| {
            ContextProviderError::InvalidQuorum(
                "quorum public key is not 48 bytes long".to_string(),
            )
        })?;
        Ok(pubkey)
    }
    pub fn get_platform_activation_height(&self) -> Result<CoreBlockHeight, ContextProviderError> {
        let core = self.core.lock().expect("Core lock poisoned");
        let fork_info = core
            .get_blockchain_info()
            .map(|blockchain_info| blockchain_info.softforks.get("mn_rr").cloned())
            .map_err(|e: dashcore_rpc::Error| ContextProviderError::Generic(e.to_string()))?
            .ok_or(ContextProviderError::ActivationForkError(
                "no fork info for mn_rr".to_string(),
            ))?;
        fork_info
            .height
            .ok_or(ContextProviderError::ActivationForkError(
                "unknown fork height".to_string(),
            ))
    }
    #[allow(unused)]
    pub fn protx_list(
        &self,
        height: Option<u32>,
        protx_type: Option<ProTxListType>,
    ) -> Result<Vec<ProTxHash>, Error> {
        let core = self.core.lock().expect("Core lock poisoned");
        let pro_tx_hashes =
            core.get_protx_list(protx_type, Some(false), height)
                .map(|x| match x {
                    ProTxList::Hex(hex) => hex,
                    ProTxList::Info(info) => info.into_iter().map(|v| v.pro_tx_hash).collect(),
                })?;
        Ok(pro_tx_hashes)
    }
}