1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//! Miniscript RPC methods
//!
//! Provides RPC methods for miniscript functionality:
//! - getdescriptorinfo: Parse and validate descriptors
//! - analyzepsbt: Analyze PSBT scripts using miniscript
//!
//! Feature-gated: Only available when `miniscript` feature is enabled.
#[cfg(feature = "miniscript")]
pub mod miniscript_rpc {
use super::super::errors::RpcError;
use crate::miniscript::miniscript_support;
use serde_json::{json, Value};
use std::str::FromStr;
/// RPC method: getdescriptorinfo
///
/// Analyzes a descriptor and returns information about it.
///
/// # Arguments
/// * `params` - JSON-RPC parameters containing descriptor string
///
/// # Returns
/// Descriptor information (isvalid, descriptor, checksum, etc.)
pub async fn get_descriptor_info(params: &Value) -> Result<Value, RpcError> {
// Extract descriptor from params
let descriptor_str = params
.get(0)
.and_then(|v| v.as_str())
.ok_or_else(|| RpcError::invalid_params("descriptor string required"))?;
// Parse descriptor using miniscript
match miniscript_support::parse_descriptor(descriptor_str) {
Ok(descriptor) => {
// Get script from descriptor
let script = descriptor.script_pubkey();
let script_bytes: Vec<u8> = script.into();
// Analyze script
let analysis = miniscript_support::analyze_script(
&blvm_protocol::ByteString::from(script_bytes),
)
.map_err(|e| RpcError::internal_error(e.to_string()))?;
// Calculate checksum and detect range
let checksum = miniscript_support::calculate_descriptor_checksum(descriptor_str);
let is_range = miniscript_support::is_range_descriptor(descriptor_str);
// Return descriptor info (standard compatible format)
Ok(json!({
"descriptor": descriptor_str,
"checksum": checksum,
"isrange": is_range,
"issolvable": analysis.is_miniscript,
"hasprivatekeys": false, // Descriptors don't contain private keys
}))
}
Err(e) => {
// Invalid descriptor
Ok(json!({
"descriptor": descriptor_str,
"checksum": "",
"isrange": false,
"issolvable": false,
"hasprivatekeys": false,
"error": e.to_string(),
}))
}
}
}
/// RPC method: analyzepsbt
///
/// Analyzes a PSBT and provides information about it, including script analysis.
///
/// # Arguments
/// * `params` - JSON-RPC parameters containing PSBT string
///
/// # Returns
/// PSBT analysis including script information
pub async fn analyze_psbt(params: &Value) -> Result<Value, RpcError> {
// Extract PSBT from params
let psbt_str = params
.get(0)
.and_then(|v| v.as_str())
.ok_or_else(|| RpcError::invalid_params("PSBT string required"))?;
// Decode base64 PSBT
use base64::{engine::general_purpose, Engine as _};
let psbt_bytes = general_purpose::STANDARD
.decode(psbt_str)
.map_err(|e| RpcError::invalid_params(&format!("Invalid PSBT base64: {}", e)))?;
use miniscript::bitcoin::psbt::Psbt;
let psbt = Psbt::deserialize(&psbt_bytes)
.map_err(|e| RpcError::internal_error(&format!("Failed to parse PSBT: {}", e)))?;
// Analyze inputs
let mut input_analyses = Vec::new();
for (_idx, input) in psbt.inputs.iter().enumerate() {
let mut input_info = json!({
"has_utxo": input.non_witness_utxo.is_some() || input.witness_utxo.is_some(),
"has_final_script_sig": input.final_script_sig.is_some(),
"has_final_script_witness": input.final_script_witness.is_some(),
});
// Analyze scripts if available
if let Some(ref utxo) = input.witness_utxo {
let script_bytes = utxo.script_pubkey.as_bytes();
let analysis = miniscript_support::analyze_script(
&blvm_protocol::ByteString::from(script_bytes.to_vec()),
)
.ok();
if let Some(analysis) = analysis {
input_info["script_type"] = json!(analysis.script_type);
input_info["is_miniscript"] = json!(analysis.is_miniscript);
}
}
input_analyses.push(input_info);
}
// Calculate estimated vsize (simplified)
let estimated_vsize = psbt.unsigned_tx.weight().to_wu() as u64 / 4;
// Determine next step
let next = if psbt
.inputs
.iter()
.all(|i| i.final_script_sig.is_some() || i.final_script_witness.is_some())
{
"finalizer"
} else if psbt.inputs.iter().any(|i| !i.partial_sigs.is_empty()) {
"signer"
} else {
"updater"
};
Ok(json!({
"inputs": input_analyses,
"estimated_vsize": estimated_vsize,
"estimated_feerate": 0, // Would need fee calculation
"fee": 0, // Would need fee calculation
"next": next,
}))
}
}