Skip to content

Add support for bitcoin core 29.0 #131

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

Merged
merged 1 commit into from
May 22, 2025
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
3 changes: 1 addition & 2 deletions .github/workflows/rust.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -210,14 +210,13 @@ jobs:
matrix:
feature:
[
"29_0,download",
"28_1,download",
"28_0,download",
"27_2,download",
"27_1,download",
"27_0,download",
"26_2,download",
"26_1,download",
"26_0,download",
"25_2,download",
"24_2,download",
"23_2,download",
Expand Down
1 change: 1 addition & 0 deletions client/src/client_sync/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub mod v25;
pub mod v26;
pub mod v27;
pub mod v28;
pub mod v29;

use std::fs::File;
use std::io::{BufRead, BufReader};
Expand Down
26 changes: 26 additions & 0 deletions client/src/client_sync/v29/blockchain.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// SPDX-License-Identifier: CC0-1.0

//! Macros for implementing JSON-RPC methods on a client.
//!
//! Specifically this is methods found under the `== Blockchain ==` section of the
//! API docs of Bitcoin Core `v29`.
//!
//! All macros require `Client` to be in scope.
//!
//! See or use the `define_jsonrpc_minreq_client!` macro to define a `Client`.

/// Implements Bitcoin Core JSON-RPC API method `getdescriptoractivity`
#[macro_export]
macro_rules! impl_client_v29__getdescriptoractivity {
() => {
impl Client {
pub fn get_descriptor_activity(&self) -> Result<GetDescriptorActivity> {
let block_hashes: &[BlockHash] = &[];
let scan_objects: &[&str] = &[];
// FIXME: Core errors if we don't pass empty arrays?
let params = vec![json!(block_hashes), json!(scan_objects)];
self.call("getdescriptoractivity", &params)
}
}
};
}
172 changes: 172 additions & 0 deletions client/src/client_sync/v29/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
// SPDX-License-Identifier: CC0-1.0

//! A JSON-RPC client for testing against Bitcoin Core `v29`.
//!
//! We ignore option arguments unless they effect the shape of the returned JSON data.

pub mod blockchain;

use std::collections::BTreeMap;
use std::path::Path;

use bitcoin::address::{Address, NetworkChecked};
use bitcoin::{sign_message, Amount, Block, BlockHash, PublicKey, Txid};
use serde::{Deserialize, Serialize};
use serde_json::json;

use crate::client_sync::into_json;
use crate::types::v29::*;

#[rustfmt::skip] // Keep public re-exports separate.
pub use crate::client_sync::{
v17::{Input, Output, WalletCreateFundedPsbtInput},
v23::AddressType,
};

crate::define_jsonrpc_minreq_client!("v29");
crate::impl_client_check_expected_server_version!({ [290000] });

// == Blockchain ==
crate::impl_client_v17__getbestblockhash!();
crate::impl_client_v17__getblock!();
crate::impl_client_v17__getblockchaininfo!();
crate::impl_client_v17__getblockcount!();
crate::impl_client_v19__getblockfilter!();
crate::impl_client_v17__getblockhash!();
crate::impl_client_v17__getblockheader!();
crate::impl_client_v17__getblockstats!();
crate::impl_client_v17__getchaintips!();
crate::impl_client_v17__getchaintxstats!();
crate::impl_client_v29__getdescriptoractivity!();
crate::impl_client_v17__getdifficulty!();
crate::impl_client_v17__getmempoolancestors!();
crate::impl_client_v17__getmempooldescendants!();
crate::impl_client_v17__getmempoolentry!();
crate::impl_client_v17__getmempoolinfo!();
crate::impl_client_v17__getrawmempool!();
crate::impl_client_v17__gettxout!();
crate::impl_client_v17__gettxoutproof!();
crate::impl_client_v26__gettxoutsetinfo!();
crate::impl_client_v17__preciousblock!();
crate::impl_client_v17__pruneblockchain!();
crate::impl_client_v23__savemempool!();
crate::impl_client_v17__verifychain!();
crate::impl_client_v17__verifytxoutproof!();

// == Control ==
crate::impl_client_v17__getmemoryinfo!();
crate::impl_client_v18__getrpcinfo!();
crate::impl_client_v17__help!();
crate::impl_client_v17__logging!();
crate::impl_client_v17__stop!();
crate::impl_client_v17__uptime!();

// == Generating ==
crate::impl_client_v17__generatetoaddress!();
crate::impl_client_v17__invalidateblock!();

// == Mining ==
crate::impl_client_v17__getblocktemplate!();
crate::impl_client_v17__getmininginfo!();
crate::impl_client_v17__getnetworkhashps!();
crate::impl_client_v26__get_prioritised_transactions!();
crate::impl_client_v17__prioritisetransaction!();
crate::impl_client_v17__submitblock!();

// == Network ==
crate::impl_client_v17__getaddednodeinfo!();
crate::impl_client_v17__getnettotals!();
crate::impl_client_v17__getnetworkinfo!();
crate::impl_client_v18__getnodeaddresses!();
crate::impl_client_v17__getpeerinfo!();

// == Rawtransactions ==
crate::impl_client_v18__analyzepsbt!();
crate::impl_client_v17__combinepsbt!();
crate::impl_client_v17__combinerawtransaction!();
crate::impl_client_v17__converttopsbt!();
crate::impl_client_v17__createpsbt!();
crate::impl_client_v17__createrawtransaction!();
crate::impl_client_v17__decodepsbt!();
crate::impl_client_v17__decoderawtransaction!();
crate::impl_client_v17__decodescript!();
crate::impl_client_v17__finalizepsbt!();
crate::impl_client_v17__fundrawtransaction!();
crate::impl_client_v17__getrawtransaction!();
crate::impl_client_v18__joinpsbts!();
crate::impl_client_v17__sendrawtransaction!();
crate::impl_client_v17__signrawtransaction!();
crate::impl_client_v17__signrawtransactionwithkey!();
crate::impl_client_v28__submitpackage!();
crate::impl_client_v17__testmempoolaccept!();
crate::impl_client_v18__utxoupdatepsbt!();

// == Util ==
crate::impl_client_v17__createmultisig!();
crate::impl_client_v17__estimatesmartfee!();
crate::impl_client_v17__signmessagewithprivkey!();
crate::impl_client_v17__validateaddress!();
crate::impl_client_v17__verifymessage!();

// == Wallet ==
crate::impl_client_v17__addmultisigaddress!();
crate::impl_client_v17__bumpfee!();
crate::impl_client_v23__createwallet!();
crate::impl_client_v17__dumpprivkey!();
crate::impl_client_v17__dumpwallet!();
crate::impl_client_v17__getaddressesbylabel!();
crate::impl_client_v17__getaddressinfo!();
crate::impl_client_v17__getbalance!();
crate::impl_client_v19__getbalances!();
crate::impl_client_v17__getnewaddress!();
crate::impl_client_v17__getrawchangeaddress!();
crate::impl_client_v17__getreceivedbyaddress!();
crate::impl_client_v17__gettransaction!();
crate::impl_client_v17__getunconfirmedbalance!();
crate::impl_client_v17__getwalletinfo!();
crate::impl_client_v17__listaddressgroupings!();
crate::impl_client_v17__listlabels!();
crate::impl_client_v17__listlockunspent!();
crate::impl_client_v17__listreceivedbyaddress!();
crate::impl_client_v17__listsinceblock!();
crate::impl_client_v17__listtransactions!();
crate::impl_client_v17__listunspent!();
crate::impl_client_v17__listwallets!();
crate::impl_client_v22__loadwallet!();
crate::impl_client_v17__rescanblockchain!();
crate::impl_client_v17__sendmany!();
crate::impl_client_v17__sendtoaddress!();
crate::impl_client_v17__signmessage!();
crate::impl_client_v17__signrawtransactionwithwallet!();
crate::impl_client_v21__unloadwallet!();
crate::impl_client_v17__walletcreatefundedpsbt!();
crate::impl_client_v17__walletprocesspsbt!();

/// Arg for the `getblocktemplate` method. (v29+).
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
pub struct TemplateRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub mode: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub capabilities: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub rules: Vec<TemplateRules>,
#[serde(skip_serializing_if = "Option::is_none")]
pub longpollid: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<String>,
}

/// Client side supported softfork deployment.
#[derive(Copy, Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum TemplateRules {
/// SegWit v0 supported.
Segwit,
/// Signet supported.
Signet,
/// CSV supported.
Csv,
/// Taproot supported.
Taproot,
}
1 change: 1 addition & 0 deletions contrib/run-bitcoind.sh
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ CONFIGURATION

- Examples

v29 29.0 290 /opt/bitcoin-29.0/bin/bitcoind
v28 28.1 281 /opt/bitcoin-28.1/bin/bitcoind
v24 24.2 242 /opt/bitcoin-24.2/bin/bitcoind
v21 0.21.2 212 /opt/bitcoin-0.21.2/bin/bitcoind
Expand Down
6 changes: 3 additions & 3 deletions integration_test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,14 @@ download = ["node/download"]

# Enable the same feature in `node` and the version feature here.
# All minor releases of the latest three versions.
29_0 = ["v29", "node/29_0"]
28_1 = ["v28", "node/28_1"]
28_0 = ["v28", "node/28_0"]
27_2 = ["v27", "node/27_2"]
27_1 = ["v27", "node/27_1"]
27_0 = ["v27", "node/27_0"]
26_2 = ["v26", "node/26_2"]
26_1 = ["v26", "node/26_1"]
26_0 = ["v26", "node/26_0"]
# Only the latest minor version for older versions.
26_2 = ["v26", "node/26_2"]
25_2 = ["v25", "node/25_2"]
24_2 = ["v24", "node/24_2"]
23_2 = ["v23", "node/23_2"]
Expand All @@ -36,6 +35,7 @@ download = ["node/download"]

# These features are just for internal use (feature gating).
# Each major version is tested with the same client.
v29 = []
v28 = []
v27 = []
v26 = []
Expand Down
10 changes: 10 additions & 0 deletions integration_test/tests/blockchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,16 @@ fn blockchain__get_chain_tx_stats__modelled() {
model.unwrap();
}

#[test]
#[cfg(feature = "v29")]
fn blockchain__get_descriptor_activity__modelled() {
let node = Node::with_wallet(Wallet::None, &["-coinstatsindex=1", "-txindex=1"]);

let json: GetDescriptorActivity = node.client.get_descriptor_activity().expect("getdescriptoractivity");
let model: Result<mtype::GetDescriptorActivity, GetDescriptorActivityError> = json.into_model();
model.unwrap();
}

#[test]
fn blockchain__get_difficulty__modelled() {
let node = Node::with_wallet(Wallet::None, &[]);
Expand Down
57 changes: 52 additions & 5 deletions integration_test/tests/mining.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,64 @@ fn mining__get_block_template__modelled() {
node2.mine_a_block();
node3.mine_a_block();

let options = TemplateRequest { rules: vec![TemplateRules::Segwit] };
let options = match () {
#[cfg(not(feature = "v29"))]
() => TemplateRequest { rules: vec![TemplateRules::Segwit] },
#[cfg(feature = "v29")]
() => TemplateRequest {
rules: vec![TemplateRules::Segwit],
mode: Some("template".to_string()),
..Default::default()
}
};

let json: GetBlockTemplate = node1.client.get_block_template(&options).expect("rpc");
let model: Result<mtype::GetBlockTemplate, GetBlockTemplateError> = json.into_model();
model.unwrap();
let json: GetBlockTemplate = node1.client.get_block_template(&options)
.expect("get_block_template RPC failed");
let _: Result<mtype::GetBlockTemplate, GetBlockTemplateError> = json.into_model();
}

#[test]
fn mining__get_mining_info() {
let node = Node::with_wallet(Wallet::Default, &[]);
let _: GetMiningInfo = node.client.get_mining_info().expect("rpc");

let json: GetMiningInfo = node.client.get_mining_info().expect("rpc");

// Upto v9 there is no error converting into model.
#[cfg(any(
feature = "v17",
feature = "v18",
feature = "v19",
feature = "v20",
feature = "v21",
feature = "v22",
feature = "v23",
feature = "v24",
feature = "v25",
feature = "v26",
feature = "v27",
feature = "v28",
))]
let _: mtype::GetMiningInfo = json.into_model();

// v29 onwards - these feature gates are shit, we need a better way to do this.
#[cfg(not(any(
feature = "v17",
feature = "v18",
feature = "v19",
feature = "v20",
feature = "v21",
feature = "v22",
feature = "v23",
feature = "v24",
feature = "v25",
feature = "v26",
feature = "v27",
feature = "v28",
)))]
{
let model: Result<mtype::GetMiningInfo, GetMiningInfoError> = json.into_model();
model.unwrap();
}
}

#[test]
Expand Down
5 changes: 2 additions & 3 deletions node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,15 @@ default = ["0_17_2"]
download = ["anyhow", "bitcoin_hashes", "flate2", "tar", "minreq", "zip"]

# We support all minor releases of the latest three versions.
29_0 = ["28_1"]
28_1 = ["28_0"]
28_0 = ["27_2"]
27_2 = ["27_1"]
27_1 = ["27_0"]
27_0 = ["26_2"]
26_2 = ["26_1"]
26_1 = ["26_0"]
26_0 = ["25_2"]

# We only support the latest minor version for older versions.
26_2 = ["25_2"]
25_2 = ["24_2"]
24_2 = ["23_2"]
23_2 = ["22_1"]
Expand Down
2 changes: 1 addition & 1 deletion node/contrib/extra_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

set -euox pipefail

FEATURES=("28_1" "28_0" "27_1" "27_0" "26_2" "26_1" "26_0" "25_2" "24_2" \
FEATURES=("29_0" "28_1" "28_0" "27_1" "27_0" "26_2" "25_2" "24_2" \
"23_2" "22_1" "0_21_2" "0_20_2" "0_19_1" "0_18_1" "0_17_2")

# Use the current `Cargo.lock` file without updating it.
Expand Down
Loading