Skip to content

Implement version specific types #116

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
7 changes: 7 additions & 0 deletions client/src/client_sync/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ pub enum Error {
ServerVersion(UnexpectedServerVersionError),
/// Missing user/password
MissingUserPassword,
/// Invalid arguments for disconnect_node: Both address and nodeid provided
DisconnectNodeArgsBoth,
/// Invalid arguments for disconnect_node: Neither address nor nodeid provided.
DisconnectNodeArgsNone,
}

impl From<jsonrpc::error::Error> for Error {
Expand Down Expand Up @@ -76,6 +80,8 @@ impl fmt::Display for Error {
Returned(ref s) => write!(f, "the daemon returned an error string: {}", s),
ServerVersion(ref e) => write!(f, "server version: {}", e),
MissingUserPassword => write!(f, "missing user and/or password"),
DisconnectNodeArgsBoth => write!(f, "invalid arguments for disconnect_node: provide either address OR nodeid, not both"),
DisconnectNodeArgsNone => write!(f, "invalid arguments for disconnect_node: provide either address OR nodeid, none given"),
}
}
}
Expand All @@ -95,6 +101,7 @@ impl error::Error for Error {
InvalidAmount(ref e) => Some(e),
ServerVersion(ref e) => Some(e),
InvalidCookieFile | UnexpectedStructure | Returned(_) | MissingUserPassword => None,
DisconnectNodeArgsBoth | DisconnectNodeArgsNone => None,
}
}
}
Expand Down
142 changes: 142 additions & 0 deletions client/src/client_sync/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,3 +262,145 @@ pub enum TemplateRules {
/// Taproot supported.
Taproot,
}

/// Args for the `addnode` method
#[derive(Debug, Clone, Copy, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum AddNodeCommand {
Add,
Remove,
OneTry,
}

/// Args for the `setban` method
#[derive(Debug, Clone, Copy, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum SetBanCommand {
Add,
Remove,
}

/// Args for the `lockunspent` method
#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
pub struct LockUnspentOutput {
/// The transaction id
pub txid: Txid,
/// The output number
pub vout: u32,
}

/// Args for the `scantxoutset`
///
/// Represents the action for the `scantxoutset` RPC call.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum ScanAction {
Start,
Abort,
Status,
}

/// Represents the range for HD descriptor scanning (handles n or [n, n]).
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(untagged)]
pub enum ScanRange {
/// Represents the end index (beginning is 0).
Single(u64),
/// Array represents [begin, end] indexes
Range([u64; 2]),
}

// Helper function for serde default
fn default_scan_range() -> ScanRange {
// Default range is 1000 as per Bitcoin Core docs
ScanRange::Single(1000)
}

/// Represents a scan object for scantxoutset (descriptor string or object).
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(untagged)]
pub enum ScanObject {
/// Plain descriptor string
Descriptor(String),
/// Object containing descriptor and optional range
WithRange {
desc: String,
#[serde(default = "default_scan_range")]
range: ScanRange,
},
}

/// Args for the `importmulti`
///
/// Represents the scriptPubKey field in an importmulti request.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(untagged)]
pub enum ImportMultiScriptPubKey {
/// Script hex string
Script(String),
/// Address object
Address { address: String },
}

/// Represents the timestamp field in an importmulti request.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(untagged)]
pub enum ImportMultiTimestamp {
/// Use current blockchain time
Now(String),
/// Specific UNIX epoch time
Time(u64),
}

impl Default for ImportMultiTimestamp {
fn default() -> Self { ImportMultiTimestamp::Now("now".to_string()) }
}

/// Represents a single request object within the importmulti call.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
pub struct ImportMultiRequest {
/// Descriptor to import (optional, mutually exclusive with scriptPubKey/address etc.) (v18+)
#[serde(skip_serializing_if = "Option::is_none")]
pub desc: Option<String>,
/// ScriptPubKey or address object (required unless desc is provided)
#[serde(rename = "scriptPubKey", skip_serializing_if = "Option::is_none")]
pub script_pub_key: Option<ImportMultiScriptPubKey>,
/// Creation time of the key
pub timestamp: ImportMultiTimestamp,
/// Redeem script (P2SH/P2SH-P2WSH only)
#[serde(skip_serializing_if = "Option::is_none")]
pub redeemscript: Option<String>,
/// Witness script (P2WSH/P2SH-P2WSH only) (v18+)
#[serde(skip_serializing_if = "Option::is_none")]
pub witnessscript: Option<String>,
/// Pubkeys to import (cannot be used with descriptor)
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub pubkeys: Vec<String>,
/// Private keys to import (WIF format)
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub keys: Vec<String>,
/// Range for ranged descriptors (v18+)
#[serde(skip_serializing_if = "Option::is_none")]
pub range: Option<ScanRange>,
/// Treat matching outputs as change
#[serde(skip_serializing_if = "Option::is_none")]
pub internal: Option<bool>,
/// Treat matching outputs as watchonly
#[serde(skip_serializing_if = "Option::is_none")]
pub watchonly: Option<bool>,
/// Label for address (use "" for default)
#[serde(skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
/// Add pubkeys to keypool (only when private keys disabled) (v18+)
#[serde(skip_serializing_if = "Option::is_none")]
pub keypool: Option<bool>,
}

/// Represents the optional options object for importmulti
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
/// Rescan after import (default true)
pub struct ImportMultiOptions {
/// Rescan after import (default true)
#[serde(skip_serializing_if = "Option::is_none")]
pub rescan: Option<bool>,
}
98 changes: 98 additions & 0 deletions client/src/client_sync/v17/blockchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,3 +301,101 @@ macro_rules! impl_client_v17__verifytxoutproof {
}
};
}

/// Implements Bitcoin Core JSON-RPC API method `pruneblockchain`
#[macro_export]
macro_rules! impl_client_v17__pruneblockchain {
() => {
impl Client {
/// Instructs the node to prune the blockchain up to a specified height or timestamp.
pub fn prune_blockchain(&self, target: u64) -> Result<PruneBlockchain> {
self.call("pruneblockchain", &[target.into()])
}
}
};
}

/// Implements Bitcoin Core JSON-RPC API method `savemempool`
#[macro_export]
macro_rules! impl_client_v17__savemempool {
() => {
impl Client {
// Dumps the mempool to disk (v17 - v22)
pub fn save_mempool(&self) -> Result<SaveMempool> {
match self.call("savemempool", &[]) {
Ok(serde_json::Value::Null) => Ok(SaveMempool),
Ok(_) => Ok(SaveMempool),
Err(e) => Err(e.into()),
}
}
}
};
}

/// Implements Bitcoin Core JSON-RPC API method `verifychain`
#[macro_export]
macro_rules! impl_client_v17__verifychain {
() => {
impl Client {
/// Verifies blockchain database using default checklevel and nblocks.
/// Returns true if verification finised successfully.
pub fn verify_chain_default(&self) -> Result<VerifyChain> {
// Core returns a boolean directly
self.call("verifychain", &[])
}

/// Verifies blockchain database with specified checklevel and nblocks.
///
/// # Arguments
/// * `checklevel`: (0-4) How thorough the verification is (optional, defaults to 3).
/// * `nblocks`: Number of blocks to check (optional, defaults to 6, 0=all).
///
/// Returns true if verification finished successfully.
pub fn verify_chain(
&self,
checklevel: Option<u32>,
nblocks: Option<u32>,
) -> Result<VerifyChain> {
// Construct params carefully for optional args
// Bitcoin Core often uses positional nulls for omitted optional args
let params = match (checklevel, nblocks) {
(Some(level), Some(num)) => vec![level.into(), num.into()],
(Some(level), None) => vec![level.into()],
(None, Some(num)) => vec![serde_json::Value::Null, num.into()],
(None, None) => vec![],
};

self.call("verifychain", &params)
}
}
};
}

/// Implements Bitcoin Core JSON-RPC API method `scantxoutset`
#[macro_export]
macro_rules! impl_client_v17__scantxoutset {
() => {
impl Client {
pub fn scan_tx_out_set(
&self,
action: ScanAction,
scan_objects: Option<&[ScanObject]>,
) -> Result<ScanTxOutSet> {
let params = match action {
ScanAction::Start => match scan_objects {
Some(objects) =>
vec![serde_json::to_value(action)?, serde_json::to_value(objects)?],
None =>
return Err(Error::Returned(
"scan_objects required for 'start'".to_string(),
)),
},
ScanAction::Abort | ScanAction::Status => {
vec![serde_json::to_value(action)?]
}
};
self.call("scantxoutset", &params)
}
}
};
}
44 changes: 43 additions & 1 deletion client/src/client_sync/v17/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ use bitcoin::address::{Address, NetworkChecked};
use bitcoin::{Amount, Block, BlockHash, PublicKey, Txid};
use serde::{Deserialize, Serialize};

use crate::client_sync::into_json;
use crate::client_sync::{
into_json, AddNodeCommand, ImportMultiOptions, ImportMultiRequest, ScanAction, ScanObject,
SetBanCommand,
};
use crate::types::v17::*;

#[rustfmt::skip] // Keep public re-exports separate.
Expand Down Expand Up @@ -49,6 +52,10 @@ crate::impl_client_v17__gettxoutproof!();
crate::impl_client_v17__gettxoutsetinfo!();
crate::impl_client_v17__preciousblock!();
crate::impl_client_v17__verifytxoutproof!();
crate::impl_client_v17__pruneblockchain!();
crate::impl_client_v17__savemempool!();
crate::impl_client_v17__verifychain!();
crate::impl_client_v17__scantxoutset!();

// == Control ==
crate::impl_client_v17__getmemoryinfo!();
Expand All @@ -74,6 +81,14 @@ crate::impl_client_v17__getaddednodeinfo!();
crate::impl_client_v17__getnettotals!();
crate::impl_client_v17__getnetworkinfo!();
crate::impl_client_v17__getpeerinfo!();
crate::impl_client_v17__addnode!();
crate::impl_client_v17__clearbanned!();
crate::impl_client_v17__setban!();
crate::impl_client_v17__listbanned!();
crate::impl_client_v17__disconnectnode!();
crate::impl_client_v17__getconnectioncount!();
crate::impl_client_v17__ping!();
crate::impl_client_v17__setnetworkactive!();

// == Rawtransactions ==
crate::impl_client_v17__createrawtransaction!();
Expand Down Expand Up @@ -112,6 +127,24 @@ crate::impl_client_v17__signrawtransactionwithwallet!();
crate::impl_client_v17__unloadwallet!();
crate::impl_client_v17__walletcreatefundedpsbt!();
crate::impl_client_v17__walletprocesspsbt!();
crate::impl_client_v17__abandontransaction!();
crate::impl_client_v17__abortrescan!();
crate::impl_client_v17__backupwallet!();
crate::impl_client_v17__encryptwallet!();
crate::impl_client_v17__importaddress!();
crate::impl_client_v17__importprivkey!();
crate::impl_client_v17__importprunedfunds!();
crate::impl_client_v17__importpubkey!();
crate::impl_client_v17__importwallet!();
crate::impl_client_v17__keypoolrefill!();
crate::impl_client_v17__lockunspent!();
crate::impl_client_v17__removeprunedfunds!();
crate::impl_client_v17__sethdseed!();
crate::impl_client_v17__settxfee!();
crate::impl_client_v17__walletlock!();
crate::impl_client_v17__walletpassphrase!();
crate::impl_client_v17__walletpassphrasechange!();
crate::impl_client_v17__importmulti!();

/// Argument to the `Client::get_new_address_with_type` function.
///
Expand All @@ -136,3 +169,12 @@ impl fmt::Display for AddressType {
fmt::Display::fmt(s, f)
}
}

const SATS_PER_BTC_F64: f64 = 100_000_000.0;

pub fn fee_rate_to_rpc_arg(fee_rate: bitcoin::FeeRate) -> f64 {
let sat_per_kwu = fee_rate.to_sat_per_kwu();

let sat_per_kvb = (sat_per_kwu as f64) / 4.0;
sat_per_kvb / SATS_PER_BTC_F64
}
Loading