// Copyright (C) 2024 Riften Labs AS // // This software is licensed under the GNU Affero General Public License (AGPL), version 3.0 or later. // A copy of the license can be found in the LICENSE file or at https://www.gnu.org/licenses/agpl-3.0.html use std::{collections::HashSet, str::FromStr}; use anyhow::{Context, Result}; use bitcoin_hashes::hex::ToHex; use bitcoincash::{consensus::deserialize, BlockHeader, Transaction, Txid}; use electrum_client_netagnostic::{Client, ElectrumApi, Param}; use log::info; use riftenlabs_defi::cauldron::V2_CONTRACT_TEMPLATE; use serde_json::{json, Value}; /// Fetch blockchain tip from electrum server pub fn electrum_get_tip(client: &Client) -> Result<(BlockHeader, u64)> { let tip: Value = serde_json::from_str(&client.raw_call("blockchain.headers.tip", [])?.to_string())?; let height = tip .get("height") .context("no height")? .as_i64() .context("no int")?; let header = tip .get("hex") .context("no hex in header")? .as_str() .context("hex not str")?; Ok((deserialize(&hex::decode(header)?)?, height as u64)) } /// Fetch cauldron mempool transactions pub fn electrum_fetch_mempool(client: &Client) -> Result> { let filter = json!({ "scriptsig": hex::encode(&V2_CONTRACT_TEMPLATE[(V2_CONTRACT_TEMPLATE.len() - 43)..]) }); let response = client.raw_call("mempool.get", [Param::Value(filter)])?; let txs = response .get("transactions") .context("no txs in mempool get")? .as_array() .context("txs not array")?; Ok(txs .iter() .filter_map(|txid| match txid.as_str() { Some(txid_hex) => match Txid::from_str(txid_hex) { Ok(txid) => Some(txid), Err(e) => { info!("Txid not hex: {}", e); None } }, None => { info!("Failed to read txid from electrum response {:?}", txid); None } }) .collect()) } /// Fetch blockchain tip from electrum server pub fn electrum_get_tx(client: &Client, txid: &Txid) -> Result { let tx: Value = serde_json::from_str( &client .raw_call("blockchain.transaction.get", [Param::String(txid.to_hex())])? .to_string(), )?; let tx: Transaction = deserialize( &hex::decode(tx.as_str().context("no tx in response")?).context("failed to decode tx")?, )?; Ok(tx) }