93 lines
3.1 KiB
Rust
93 lines
3.1 KiB
Rust
// Copyright (C) 2024-2026 Whiterun LLC
|
|
//
|
|
// 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, delphi::DELPHI_REDEEM_SCRIPT};
|
|
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<(HashSet<Txid>, HashSet<Txid>)> {
|
|
let cauldron_filter = json!({
|
|
"scriptsig": hex::encode(&V2_CONTRACT_TEMPLATE[(V2_CONTRACT_TEMPLATE.len() - 43)..]), // cauldron spends
|
|
"scriptpubkey": hex::encode([0x6a /* op_return */, 0x06 /* push */, b'S', b'U', b'M', b'M', b'O', b'N']), // new pools (potentially)
|
|
"operation": "union"
|
|
});
|
|
|
|
let oracle_filter = json!({
|
|
"scriptsig": hex::encode(DELPHI_REDEEM_SCRIPT),
|
|
});
|
|
|
|
let fetch_txs = |filter: Value| -> Result<HashSet<Txid>> {
|
|
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())
|
|
};
|
|
|
|
let cauldron_txs = fetch_txs(cauldron_filter)?;
|
|
let oracle_txs = fetch_txs(oracle_filter)?;
|
|
|
|
Ok((cauldron_txs, oracle_txs))
|
|
}
|
|
|
|
/// Fetch blockchain tip from electrum server
|
|
pub fn electrum_get_tx(client: &Client, txid: &Txid) -> Result<Transaction> {
|
|
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)
|
|
}
|