Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/floresta-node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ tracing-subscriber = { version = "0.3.20", features = ["chrono", "ansi", "env-fi
console-subscriber = { version = "0.4", optional = true }
tracing = "0.1.41"
tracing-appender = "0.2.3"
corepc-types = "0.10.1"

[target.'cfg(target_env = "gnu")'.dependencies]
libc = "0.2.169"
Expand Down
66 changes: 15 additions & 51 deletions crates/floresta-node/src/json_rpc/blockchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ use bitcoin::OutPoint;
use bitcoin::Script;
use bitcoin::ScriptBuf;
use bitcoin::Txid;
use corepc_types::v29::GetTxOut;
use corepc_types::ScriptPubkey;
use miniscript::descriptor::checksum;
use serde::Deserialize;
use serde::Serialize;
use serde_json::json;
use serde_json::Value;
use tracing::debug;
Expand All @@ -25,47 +25,6 @@ use super::server::RpcChain;
use super::server::RpcImpl;
use crate::json_rpc::res::RescanConfidence;

#[derive(Debug, Serialize, Deserialize)]
/// Struct helper for RpcGetTxOut
pub struct ScriptPubkeyDescription {
/// Disassembly of the output script
asm: String,

/// Inferred descriptor for the output
desc: String,

/// The raw output script bytes, hex-encoded
hex: String,

/// The type, eg pubkeyhash
#[serde(rename = "type")]
type_field: String,

/// The Bitcoin address (only if a well-defined address exists)
#[serde(skip_serializing_if = "Option::is_none")]
address: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
/// Struct helper for serialize gettxout rpc
pub struct GetTxOut {
/// The hash of the block at the tip of the chain
bestblock: BlockHash,

/// The number of confirmations
confirmations: u32,

/// The transaction value in BTC
value: f64,

#[serde(rename = "scriptPubKey")]
/// Script Public Key struct
script_pubkey: ScriptPubkeyDescription,

/// Coinbase or not
coinbase: bool,
}

impl<Blockchain: RpcChain> RpcImpl<Blockchain> {
async fn get_block_inner(&self, hash: BlockHash) -> Result<Block, JsonRpcError> {
let is_genesis = self.chain.get_block_hash(0).unwrap().eq(&hash);
Expand Down Expand Up @@ -518,22 +477,27 @@ impl<Blockchain: RpcChain> RpcImpl<Blockchain> {
let network = self.chain.get_params().network;
let address = Address::from_script(script, network).ok();

let desc = Self::get_script_type_descriptor(script, &address);
let checksum_desc = checksum::desc_checksum(&desc)
.map(|checksum| format!("{desc}#{checksum}"))
.map_err(|_| JsonRpcError::InvalidDescriptor)?;
let base_descriptor = Self::get_script_type_descriptor(script, &address);
let descriptor: Option<String> = match checksum::desc_checksum(&base_descriptor) {
Ok(checksum) => Some(format!("{base_descriptor}#{checksum}")),
Err(_) => None,
};

let asm = Self::to_core_asm_string(&txout.script_pubkey)?;
let script_pubkey = ScriptPubkeyDescription {
let script_pubkey = ScriptPubkey {
asm,
hex: txout.script_pubkey.to_hex_string(),
desc: checksum_desc,
descriptor,
address: address.as_ref().map(ToString::to_string),
type_field: Self::get_script_type_label(script).to_string(),
type_: Self::get_script_type_label(script).to_string(),
// Deprecated in Bitcoin Core v22, require flags in Bitcoin Core.
// Set to None as not required for consensus.
addresses: None,
required_signatures: None,
};

Some(GetTxOut {
bestblock: bestblock_hash,
best_block: bestblock_hash.to_string(),
confirmations: bestblock_height - height + 1,
value: txout.value.to_btc(),
script_pubkey,
Expand Down
1 change: 1 addition & 0 deletions crates/floresta-rpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
jsonrpc = { version = "0.18.0", features = ["minreq_http"], optional = true }
clap = { version = "4.0.29", features = ["derive"], optional = true }
corepc-types = "0.10.1"

[features]
default = ["with-jsonrpc"]
Expand Down
13 changes: 9 additions & 4 deletions crates/floresta-rpc/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::fmt::Debug;
use bitcoin::block::Header as BlockHeader;
use bitcoin::BlockHash;
use bitcoin::Txid;
use corepc_types::v29::GetTxOut;
use serde_json::Number;
use serde_json::Value;

Expand Down Expand Up @@ -98,7 +99,7 @@ pub trait FlorestaRPC {
/// This method returns a cached transaction output. If the output is not in the cache,
/// or is spent, an empty object is returned. If you want to find a utxo that's not in
/// the cache, you can use the findtxout method.
fn get_tx_out(&self, tx_id: Txid, outpoint: u32) -> Result<Value>;
fn get_tx_out(&self, tx_id: Txid, outpoint: u32) -> Result<GetTxOut>;
/// Stops the florestad process
///
/// This can be used to gracefully stop the florestad process.
Expand Down Expand Up @@ -252,14 +253,18 @@ impl<T: JsonRPCClient> FlorestaRPC for T {
self.call("getblockcount", &[])
}

fn get_tx_out(&self, tx_id: Txid, outpoint: u32) -> Result<Value> {
self.call(
fn get_tx_out(&self, tx_id: Txid, outpoint: u32) -> Result<GetTxOut> {
let result: serde_json::Value = self.call(
"gettxout",
&[
Value::String(tx_id.to_string()),
Value::Number(Number::from(outpoint)),
],
)
)?;
if result.is_null() {
return Err(Error::TxOutNotFound);
}
serde_json::from_value(result).map_err(Error::Serde)
}

fn get_txout_proof(&self, txids: Vec<Txid>, blockhash: Option<BlockHash>) -> Option<String> {
Expand Down
4 changes: 4 additions & 0 deletions crates/floresta-rpc/src/rpc_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,9 @@ pub enum Error {

/// The user requested a rescan based on invalid values.
InvalidRescanVal,

/// The requested transaction output was not found
TxOutNotFound,
}

impl From<serde_json::Error> for Error {
Expand All @@ -343,6 +346,7 @@ impl Display for Error {
Error::EmptyResponse => write!(f, "got an empty response from server"),
Error::InvalidVerbosity => write!(f, "invalid verbosity level"),
Error::InvalidRescanVal => write!(f, "Invalid rescan values"),
Error::TxOutNotFound => write!(f, "Transaction output was not found"),
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion tests/floresta-cli/gettxout.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ def run_test(self):
tx, vout=0, include_mempool=False
)

for key in ("bestblock", "coinbase", "value"):
for key in ("bestblock", "coinbase", "value", "confirmations"):
self.assertEqual(txout_floresta[key], txout_bitcoind[key])

for key in ("address", "desc", "hex", "type", "asm"):
Expand Down
Loading