Skip to content

Commit adeb378

Browse files
committed
Implement version-specific types for omitted methods
1 parent f0586c0 commit adeb378

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+4128
-589
lines changed

client/src/client_sync/error.rs

+7
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ pub enum Error {
2424
ServerVersion(UnexpectedServerVersionError),
2525
/// Missing user/password
2626
MissingUserPassword,
27+
/// Invalid arguments for disconnect_node: Both address and nodeid provided
28+
DisconnectNodeArgsBoth,
29+
/// Invalid arguments for disconnect_node: Neither address nor nodeid provided.
30+
DisconnectNodeArgsNone,
2731
}
2832

2933
impl From<jsonrpc::error::Error> for Error {
@@ -76,6 +80,8 @@ impl fmt::Display for Error {
7680
Returned(ref s) => write!(f, "the daemon returned an error string: {}", s),
7781
ServerVersion(ref e) => write!(f, "server version: {}", e),
7882
MissingUserPassword => write!(f, "missing user and/or password"),
83+
DisconnectNodeArgsBoth => write!(f, "invalid arguments for disconnect_node: provide either address OR nodeid, not both"),
84+
DisconnectNodeArgsNone => write!(f, "invalid arguments for disconnect_node: provide either address OR nodeid, none given"),
7985
}
8086
}
8187
}
@@ -95,6 +101,7 @@ impl error::Error for Error {
95101
InvalidAmount(ref e) => Some(e),
96102
ServerVersion(ref e) => Some(e),
97103
InvalidCookieFile | UnexpectedStructure | Returned(_) | MissingUserPassword => None,
104+
DisconnectNodeArgsBoth | DisconnectNodeArgsNone => None,
98105
}
99106
}
100107
}

client/src/client_sync/mod.rs

+142
Original file line numberDiff line numberDiff line change
@@ -262,3 +262,145 @@ pub enum TemplateRules {
262262
/// Taproot supported.
263263
Taproot,
264264
}
265+
266+
/// Args for the `addnode` method
267+
#[derive(Debug, Clone, Copy, Serialize)]
268+
#[serde(rename_all = "lowercase")]
269+
pub enum AddNodeCommand {
270+
Add,
271+
Remove,
272+
OneTry,
273+
}
274+
275+
/// Args for the `setban` method
276+
#[derive(Debug, Clone, Copy, Serialize)]
277+
#[serde(rename_all = "lowercase")]
278+
pub enum SetBanCommand {
279+
Add,
280+
Remove,
281+
}
282+
283+
/// Args for the `lockunspent` method
284+
#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
285+
pub struct LockUnspentOutput {
286+
/// The transaction id
287+
pub txid: Txid,
288+
/// The output number
289+
pub vout: u32,
290+
}
291+
292+
/// Args for the `scantxoutset`
293+
///
294+
/// Represents the action for the `scantxoutset` RPC call.
295+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
296+
#[serde(rename_all = "lowercase")]
297+
pub enum ScanAction {
298+
Start,
299+
Abort,
300+
Status,
301+
}
302+
303+
/// Represents the range for HD descriptor scanning (handles n or [n, n]).
304+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
305+
#[serde(untagged)]
306+
pub enum ScanRange {
307+
/// Represents the end index (beginning is 0).
308+
Single(u64),
309+
/// Array represents [begin, end] indexes
310+
Range([u64; 2]),
311+
}
312+
313+
// Helper function for serde default
314+
fn default_scan_range() -> ScanRange {
315+
// Default range is 1000 as per Bitcoin Core docs
316+
ScanRange::Single(1000)
317+
}
318+
319+
/// Represents a scan object for scantxoutset (descriptor string or object).
320+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
321+
#[serde(untagged)]
322+
pub enum ScanObject {
323+
/// Plain descriptor string
324+
Descriptor(String),
325+
/// Object containing descriptor and optional range
326+
WithRange {
327+
desc: String,
328+
#[serde(default = "default_scan_range")]
329+
range: ScanRange,
330+
},
331+
}
332+
333+
/// Args for the `importmulti`
334+
///
335+
/// Represents the scriptPubKey field in an importmulti request.
336+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
337+
#[serde(untagged)]
338+
pub enum ImportMultiScriptPubKey {
339+
/// Script hex string
340+
Script(String),
341+
/// Address object
342+
Address { address: String },
343+
}
344+
345+
/// Represents the timestamp field in an importmulti request.
346+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
347+
#[serde(untagged)]
348+
pub enum ImportMultiTimestamp {
349+
/// Use current blockchain time
350+
Now(String),
351+
/// Specific UNIX epoch time
352+
Time(u64),
353+
}
354+
355+
impl Default for ImportMultiTimestamp {
356+
fn default() -> Self { ImportMultiTimestamp::Now("now".to_string()) }
357+
}
358+
359+
/// Represents a single request object within the importmulti call.
360+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
361+
pub struct ImportMultiRequest {
362+
/// Descriptor to import (optional, mutually exclusive with scriptPubKey/address etc.) (v18+)
363+
#[serde(skip_serializing_if = "Option::is_none")]
364+
pub desc: Option<String>,
365+
/// ScriptPubKey or address object (required unless desc is provided)
366+
#[serde(rename = "scriptPubKey", skip_serializing_if = "Option::is_none")]
367+
pub script_pub_key: Option<ImportMultiScriptPubKey>,
368+
/// Creation time of the key
369+
pub timestamp: ImportMultiTimestamp,
370+
/// Redeem script (P2SH/P2SH-P2WSH only)
371+
#[serde(skip_serializing_if = "Option::is_none")]
372+
pub redeemscript: Option<String>,
373+
/// Witness script (P2WSH/P2SH-P2WSH only) (v18+)
374+
#[serde(skip_serializing_if = "Option::is_none")]
375+
pub witnessscript: Option<String>,
376+
/// Pubkeys to import (cannot be used with descriptor)
377+
#[serde(default, skip_serializing_if = "Vec::is_empty")]
378+
pub pubkeys: Vec<String>,
379+
/// Private keys to import (WIF format)
380+
#[serde(default, skip_serializing_if = "Vec::is_empty")]
381+
pub keys: Vec<String>,
382+
/// Range for ranged descriptors (v18+)
383+
#[serde(skip_serializing_if = "Option::is_none")]
384+
pub range: Option<ScanRange>,
385+
/// Treat matching outputs as change
386+
#[serde(skip_serializing_if = "Option::is_none")]
387+
pub internal: Option<bool>,
388+
/// Treat matching outputs as watchonly
389+
#[serde(skip_serializing_if = "Option::is_none")]
390+
pub watchonly: Option<bool>,
391+
/// Label for address (use "" for default)
392+
#[serde(skip_serializing_if = "Option::is_none")]
393+
pub label: Option<String>,
394+
/// Add pubkeys to keypool (only when private keys disabled) (v18+)
395+
#[serde(skip_serializing_if = "Option::is_none")]
396+
pub keypool: Option<bool>,
397+
}
398+
399+
/// Represents the optional options object for importmulti
400+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
401+
/// Rescan after import (default true)
402+
pub struct ImportMultiOptions {
403+
/// Rescan after import (default true)
404+
#[serde(skip_serializing_if = "Option::is_none")]
405+
pub rescan: Option<bool>,
406+
}

client/src/client_sync/v17/blockchain.rs

+98
Original file line numberDiff line numberDiff line change
@@ -301,3 +301,101 @@ macro_rules! impl_client_v17__verifytxoutproof {
301301
}
302302
};
303303
}
304+
305+
/// Implements Bitcoin Core JSON-RPC API method `pruneblockchain`
306+
#[macro_export]
307+
macro_rules! impl_client_v17__pruneblockchain {
308+
() => {
309+
impl Client {
310+
/// Instructs the node to prune the blockchain up to a specified height or timestamp.
311+
pub fn prune_blockchain(&self, target: u64) -> Result<PruneBlockchain> {
312+
self.call("pruneblockchain", &[target.into()])
313+
}
314+
}
315+
};
316+
}
317+
318+
/// Implements Bitcoin Core JSON-RPC API method `savemempool`
319+
#[macro_export]
320+
macro_rules! impl_client_v17__savemempool {
321+
() => {
322+
impl Client {
323+
// Dumps the mempool to disk (v17 - v22)
324+
pub fn save_mempool(&self) -> Result<SaveMempool> {
325+
match self.call("savemempool", &[]) {
326+
Ok(serde_json::Value::Null) => Ok(SaveMempool),
327+
Ok(_) => Ok(SaveMempool),
328+
Err(e) => Err(e.into()),
329+
}
330+
}
331+
}
332+
};
333+
}
334+
335+
/// Implements Bitcoin Core JSON-RPC API method `verifychain`
336+
#[macro_export]
337+
macro_rules! impl_client_v17__verifychain {
338+
() => {
339+
impl Client {
340+
/// Verifies blockchain database using default checklevel and nblocks.
341+
/// Returns true if verification finised successfully.
342+
pub fn verify_chain_default(&self) -> Result<VerifyChain> {
343+
// Core returns a boolean directly
344+
self.call("verifychain", &[])
345+
}
346+
347+
/// Verifies blockchain database with specified checklevel and nblocks.
348+
///
349+
/// # Arguments
350+
/// * `checklevel`: (0-4) How thorough the verification is (optional, defaults to 3).
351+
/// * `nblocks`: Number of blocks to check (optional, defaults to 6, 0=all).
352+
///
353+
/// Returns true if verification finished successfully.
354+
pub fn verify_chain(
355+
&self,
356+
checklevel: Option<u32>,
357+
nblocks: Option<u32>,
358+
) -> Result<VerifyChain> {
359+
// Construct params carefully for optional args
360+
// Bitcoin Core often uses positional nulls for omitted optional args
361+
let params = match (checklevel, nblocks) {
362+
(Some(level), Some(num)) => vec![level.into(), num.into()],
363+
(Some(level), None) => vec![level.into()],
364+
(None, Some(num)) => vec![serde_json::Value::Null, num.into()],
365+
(None, None) => vec![],
366+
};
367+
368+
self.call("verifychain", &params)
369+
}
370+
}
371+
};
372+
}
373+
374+
/// Implements Bitcoin Core JSON-RPC API method `scantxoutset`
375+
#[macro_export]
376+
macro_rules! impl_client_v17__scantxoutset {
377+
() => {
378+
impl Client {
379+
pub fn scan_tx_out_set(
380+
&self,
381+
action: ScanAction,
382+
scan_objects: Option<&[ScanObject]>,
383+
) -> Result<ScanTxOutSet> {
384+
let params = match action {
385+
ScanAction::Start => match scan_objects {
386+
Some(objects) =>
387+
vec![serde_json::to_value(action)?, serde_json::to_value(objects)?],
388+
None =>
389+
return Err(Error::Returned(
390+
"scan_objects required for 'start'".to_string(),
391+
)),
392+
},
393+
ScanAction::Abort | ScanAction::Status => {
394+
vec![serde_json::to_value(action)?]
395+
}
396+
};
397+
self.call("scantxoutset", &params)
398+
}
399+
}
400+
};
401+
}

client/src/client_sync/v17/mod.rs

+43-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ use bitcoin::address::{Address, NetworkChecked};
1919
use bitcoin::{Amount, Block, BlockHash, PublicKey, Txid};
2020
use serde::{Deserialize, Serialize};
2121

22-
use crate::client_sync::into_json;
22+
use crate::client_sync::{
23+
into_json, AddNodeCommand, ImportMultiOptions, ImportMultiRequest, ScanAction, ScanObject,
24+
SetBanCommand,
25+
};
2326
use crate::types::v17::*;
2427

2528
#[rustfmt::skip] // Keep public re-exports separate.
@@ -49,6 +52,10 @@ crate::impl_client_v17__gettxoutproof!();
4952
crate::impl_client_v17__gettxoutsetinfo!();
5053
crate::impl_client_v17__preciousblock!();
5154
crate::impl_client_v17__verifytxoutproof!();
55+
crate::impl_client_v17__pruneblockchain!();
56+
crate::impl_client_v17__savemempool!();
57+
crate::impl_client_v17__verifychain!();
58+
crate::impl_client_v17__scantxoutset!();
5259

5360
// == Control ==
5461
crate::impl_client_v17__getmemoryinfo!();
@@ -74,6 +81,14 @@ crate::impl_client_v17__getaddednodeinfo!();
7481
crate::impl_client_v17__getnettotals!();
7582
crate::impl_client_v17__getnetworkinfo!();
7683
crate::impl_client_v17__getpeerinfo!();
84+
crate::impl_client_v17__addnode!();
85+
crate::impl_client_v17__clearbanned!();
86+
crate::impl_client_v17__setban!();
87+
crate::impl_client_v17__listbanned!();
88+
crate::impl_client_v17__disconnectnode!();
89+
crate::impl_client_v17__getconnectioncount!();
90+
crate::impl_client_v17__ping!();
91+
crate::impl_client_v17__setnetworkactive!();
7792

7893
// == Rawtransactions ==
7994
crate::impl_client_v17__createrawtransaction!();
@@ -112,6 +127,24 @@ crate::impl_client_v17__signrawtransactionwithwallet!();
112127
crate::impl_client_v17__unloadwallet!();
113128
crate::impl_client_v17__walletcreatefundedpsbt!();
114129
crate::impl_client_v17__walletprocesspsbt!();
130+
crate::impl_client_v17__abandontransaction!();
131+
crate::impl_client_v17__abortrescan!();
132+
crate::impl_client_v17__backupwallet!();
133+
crate::impl_client_v17__encryptwallet!();
134+
crate::impl_client_v17__importaddress!();
135+
crate::impl_client_v17__importprivkey!();
136+
crate::impl_client_v17__importprunedfunds!();
137+
crate::impl_client_v17__importpubkey!();
138+
crate::impl_client_v17__importwallet!();
139+
crate::impl_client_v17__keypoolrefill!();
140+
crate::impl_client_v17__lockunspent!();
141+
crate::impl_client_v17__removeprunedfunds!();
142+
crate::impl_client_v17__sethdseed!();
143+
crate::impl_client_v17__settxfee!();
144+
crate::impl_client_v17__walletlock!();
145+
crate::impl_client_v17__walletpassphrase!();
146+
crate::impl_client_v17__walletpassphrasechange!();
147+
crate::impl_client_v17__importmulti!();
115148

116149
/// Argument to the `Client::get_new_address_with_type` function.
117150
///
@@ -136,3 +169,12 @@ impl fmt::Display for AddressType {
136169
fmt::Display::fmt(s, f)
137170
}
138171
}
172+
173+
const SATS_PER_BTC_F64: f64 = 100_000_000.0;
174+
175+
pub fn fee_rate_to_rpc_arg(fee_rate: bitcoin::FeeRate) -> f64 {
176+
let sat_per_kwu = fee_rate.to_sat_per_kwu();
177+
178+
let sat_per_kvb = (sat_per_kwu as f64) / 4.0;
179+
sat_per_kvb / SATS_PER_BTC_F64
180+
}

0 commit comments

Comments
 (0)