Skip to content

Commit 5bf883a

Browse files
committed
Add support for bitcoin core 29.0
1 parent 3a059f8 commit 5bf883a

31 files changed

+2503
-19
lines changed

.github/workflows/rust.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ jobs:
210210
matrix:
211211
feature:
212212
[
213+
"29_0",
213214
"28_0",
214215
"27_2",
215216
"27_1",

client/src/client_sync/mod.rs

+16
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub mod v25;
1515
pub mod v26;
1616
pub mod v27;
1717
pub mod v28;
18+
pub mod v29;
1819

1920
use std::collections::HashMap;
2021
use std::fs::File;
@@ -280,3 +281,18 @@ pub enum TemplateRules {
280281
/// Taproot supported.
281282
Taproot,
282283
}
284+
285+
/// Arg for the `getblocktemplate` method. (v29+).
286+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
287+
pub struct TemplateRequestV29 {
288+
#[serde(skip_serializing_if = "Option::is_none")]
289+
pub mode: Option<String>,
290+
#[serde(default, skip_serializing_if = "Vec::is_empty")]
291+
pub capabilities: Vec<String>,
292+
#[serde(default, skip_serializing_if = "Vec::is_empty")]
293+
pub rules: Vec<String>,
294+
#[serde(skip_serializing_if = "Option::is_none")]
295+
pub longpollid: Option<String>,
296+
#[serde(skip_serializing_if = "Option::is_none")]
297+
pub data: Option<String>,
298+
}
+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// SPDX-License-Identifier: CC0-1.0
2+
3+
//! Macros for implementing JSON-RPC methods on a client.
4+
//!
5+
//! Specifically this is methods found under the `== Blockchain ==` section of the
6+
//! API docs of Bitcoin Core `v29`.
7+
//!
8+
//! All macros require `Client` to be in scope.
9+
//!
10+
//! See or use the `define_jsonrpc_minreq_client!` macro to define a `Client`.
11+
12+
/// Implements Bitcoin Core JSON-RPC API method `getblock`
13+
#[macro_export]
14+
macro_rules! impl_client_v29__getblock {
15+
() => {
16+
impl Client {
17+
/// Gets a block by blockhash.
18+
pub fn get_block(&self, hash: BlockHash) -> Result<Block> {
19+
let json = self.get_block_verbose_zero(hash)?;
20+
Ok(json.block()?)
21+
}
22+
23+
/// Gets a block by blockhash with verbose set to 0.
24+
pub fn get_block_verbose_zero(&self, hash: BlockHash) -> Result<GetBlockVerboseZero> {
25+
self.call("getblock", &[into_json(hash)?, 0.into()])
26+
}
27+
28+
/// Gets a block by blockhash with verbose set to 1.
29+
pub fn get_block_verbose_one(&self, hash: BlockHash) -> Result<GetBlockVerboseOne> {
30+
self.call("getblock", &[into_json(hash)?, 1.into()])
31+
}
32+
}
33+
};
34+
}
35+
36+
/// Implements Bitcoin Core JSON-RPC API method `getblockheader`
37+
#[macro_export]
38+
macro_rules! impl_client_v29__getblockheader {
39+
() => {
40+
impl Client {
41+
pub fn get_block_header(&self, hash: &BlockHash) -> Result<GetBlockHeader> {
42+
self.call("getblockheader", &[into_json(hash)?, into_json(false)?])
43+
}
44+
45+
// This is the same as calling getblockheader with verbose==true.
46+
pub fn get_block_header_verbose(
47+
&self,
48+
hash: &BlockHash,
49+
) -> Result<GetBlockHeaderVerbose> {
50+
self.call("getblockheader", &[into_json(hash)?])
51+
}
52+
}
53+
};
54+
}
55+
56+
/// Implements Bitcoin Core JSON-RPC API method `getblockchaininfo`
57+
#[macro_export]
58+
macro_rules! impl_client_v29__getblockchaininfo {
59+
() => {
60+
impl Client {
61+
pub fn get_blockchain_info(&self) -> Result<GetBlockchainInfo> {
62+
self.call("getblockchaininfo", &[])
63+
}
64+
}
65+
};
66+
}
67+
68+
/// Implements Bitcoin Core JSON-RPC API method `getdescriptoractivity`
69+
#[macro_export]
70+
macro_rules! impl_client_v29__getdescriptoractivity {
71+
() => {
72+
impl Client {
73+
pub fn get_descriptor_activity(
74+
&self,
75+
blockhashes: Option<&[BlockHash]>,
76+
scan_objects: Option<&[&str]>,
77+
include_mempool: Option<bool>,
78+
) -> Result<GetDescriptorActivity> {
79+
let blockhashes_val = json!(blockhashes.unwrap_or(&[]));
80+
let scan_objects_val = json!(scan_objects.unwrap_or(&[]));
81+
let include_mempool_val = json!(include_mempool.unwrap_or(false));
82+
83+
let params = vec![blockhashes_val, scan_objects_val, include_mempool_val];
84+
85+
self.call("getdescriptoractivity", &params)
86+
}
87+
}
88+
};
89+
}

client/src/client_sync/v29/mining.rs

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// SPDX-License-Identifier: CC0-1.0
2+
3+
//! Macros for implementing JSON-RPC methods on a client.
4+
//!
5+
//! Specifically this is methods found under the `== Mining ==` section of the
6+
//! API docs of Bitcoin Core `v29`.
7+
//!
8+
//! All macros require `Client` to be in scope.
9+
//!
10+
//! See or use the `define_jsonrpc_minreq_client!` macro to define a `Client`.
11+
12+
/// Implements Bitcoin Core JSON-RPC API method `getmininginfo`
13+
#[macro_export]
14+
macro_rules! impl_client_v29__getmininginfo {
15+
() => {
16+
impl Client {
17+
pub fn get_mining_info(&self) -> Result<GetMiningInfo> {
18+
self.call("getmininginfo", &[])
19+
}
20+
}
21+
};
22+
}
23+
24+
#[macro_export]
25+
macro_rules! impl_client_v29__getblocktemplate {
26+
() => {
27+
impl Client {
28+
pub fn get_block_template(
29+
&self,
30+
request: &$crate::client_sync::TemplateRequestV29,
31+
) -> Result<GetBlockTemplate> {
32+
self.call("getblocktemplate", &[into_json(request)?])
33+
}
34+
}
35+
};
36+
}

client/src/client_sync/v29/mod.rs

+128
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
// SPDX-License-Identifier: CC0-1.0
2+
3+
//! A JSON-RPC client for testing against Bitcoin Core `v29`.
4+
//!
5+
//! We ignore option arguments unless they effect the shape of the returned JSON data.
6+
pub mod blockchain;
7+
pub mod mining;
8+
9+
use std::collections::BTreeMap;
10+
use std::path::Path;
11+
12+
use bitcoin::address::{Address, NetworkChecked};
13+
use bitcoin::{Amount, Block, BlockHash, PublicKey, Txid};
14+
use serde_json::json;
15+
16+
use crate::client_sync::into_json;
17+
use crate::types::v29::*;
18+
19+
#[rustfmt::skip] // Keep public re-exports separate.
20+
pub use crate::client_sync::{v23::AddressType, WalletCreateFundedPsbtInput};
21+
22+
crate::define_jsonrpc_minreq_client!("v29");
23+
crate::impl_client_check_expected_server_version!({ [290000] });
24+
25+
// == Blockchain ==
26+
crate::impl_client_v17__getbestblockhash!();
27+
crate::impl_client_v29__getblock!();
28+
crate::impl_client_v29__getblockchaininfo!();
29+
crate::impl_client_v17__getblockcount!();
30+
crate::impl_client_v19__getblockfilter!();
31+
crate::impl_client_v17__getblockhash!();
32+
crate::impl_client_v17__getblockheader!();
33+
crate::impl_client_v17__getblockstats!();
34+
crate::impl_client_v17__getchaintips!();
35+
crate::impl_client_v17__getchaintxstats!();
36+
crate::impl_client_v29__getdescriptoractivity!();
37+
crate::impl_client_v17__getdifficulty!();
38+
crate::impl_client_v19__getmempoolancestors!();
39+
crate::impl_client_v19__getmempooldescendants!();
40+
crate::impl_client_v19__getmempoolentry!();
41+
crate::impl_client_v17__getmempoolinfo!();
42+
crate::impl_client_v17__getrawmempool!();
43+
crate::impl_client_v22__gettxout!();
44+
crate::impl_client_v17__gettxoutproof!();
45+
crate::impl_client_v26__gettxoutsetinfo!();
46+
crate::impl_client_v17__preciousblock!();
47+
crate::impl_client_v17__verifytxoutproof!();
48+
49+
// == Control ==
50+
crate::impl_client_v17__getmemoryinfo!();
51+
crate::impl_client_v18__getrpcinfo!();
52+
crate::impl_client_v17__help!();
53+
crate::impl_client_v17__logging!();
54+
crate::impl_client_v17__stop!();
55+
crate::impl_client_v17__uptime!();
56+
57+
// == Generating ==
58+
crate::impl_client_v17__generatetoaddress!();
59+
crate::impl_client_v17__invalidateblock!();
60+
61+
// == Mining ==
62+
crate::impl_client_v29__getblocktemplate!();
63+
crate::impl_client_v29__getmininginfo!();
64+
crate::impl_client_v17__getnetworkhashps!();
65+
crate::impl_client_v26__get_prioritised_transactions!();
66+
crate::impl_client_v17__prioritisetransaction!();
67+
crate::impl_client_v17__submitblock!();
68+
69+
// == Network ==
70+
crate::impl_client_v17__getaddednodeinfo!();
71+
crate::impl_client_v17__getnettotals!();
72+
crate::impl_client_v17__getnetworkinfo!();
73+
crate::impl_client_v17__getpeerinfo!();
74+
75+
// == Rawtransactions ==
76+
crate::impl_client_v18__analyzepsbt!();
77+
crate::impl_client_v17__combinepsbt!();
78+
crate::impl_client_v17__combinerawtransaction!();
79+
crate::impl_client_v17__converttopsbt!();
80+
crate::impl_client_v17__createpsbt!();
81+
crate::impl_client_v17__createrawtransaction!();
82+
crate::impl_client_v17__decodepsbt!();
83+
crate::impl_client_v17__decoderawtransaction!();
84+
crate::impl_client_v17__decodescript!();
85+
crate::impl_client_v17__finalizepsbt!();
86+
crate::impl_client_v17__fundrawtransaction!();
87+
crate::impl_client_v17__getrawtransaction!();
88+
crate::impl_client_v18__joinpsbts!();
89+
crate::impl_client_v17__sendrawtransaction!();
90+
crate::impl_client_v17__signrawtransaction!();
91+
crate::impl_client_v17__signrawtransactionwithkey!();
92+
crate::impl_client_v28__submitpackage!();
93+
crate::impl_client_v17__testmempoolaccept!();
94+
crate::impl_client_v18__utxoupdatepsbt!();
95+
96+
// == Wallet ==
97+
crate::impl_client_v17__addmultisigaddress!();
98+
crate::impl_client_v17__bumpfee!();
99+
crate::impl_client_v23__createwallet!();
100+
crate::impl_client_v17__dumpprivkey!();
101+
crate::impl_client_v17__dumpwallet!();
102+
crate::impl_client_v17__getaddressesbylabel!();
103+
crate::impl_client_v17__getaddressinfo!();
104+
crate::impl_client_v17__getbalance!();
105+
crate::impl_client_v19__getbalances!();
106+
crate::impl_client_v17__getnewaddress!();
107+
crate::impl_client_v17__getrawchangeaddress!();
108+
crate::impl_client_v17__getreceivedbyaddress!();
109+
crate::impl_client_v17__gettransaction!();
110+
crate::impl_client_v17__getunconfirmedbalance!();
111+
crate::impl_client_v17__getwalletinfo!();
112+
crate::impl_client_v17__listaddressgroupings!();
113+
crate::impl_client_v17__listlabels!();
114+
crate::impl_client_v17__listlockunspent!();
115+
crate::impl_client_v17__listreceivedbyaddress!();
116+
crate::impl_client_v17__listsinceblock!();
117+
crate::impl_client_v17__listtransactions!();
118+
crate::impl_client_v17__listunspent!();
119+
crate::impl_client_v17__listwallets!();
120+
crate::impl_client_v22__loadwallet!();
121+
crate::impl_client_v17__rescanblockchain!();
122+
crate::impl_client_v17__sendmany!();
123+
crate::impl_client_v17__sendtoaddress!();
124+
crate::impl_client_v17__signmessage!();
125+
crate::impl_client_v17__signrawtransactionwithwallet!();
126+
crate::impl_client_v21__unloadwallet!();
127+
crate::impl_client_v17__walletcreatefundedpsbt!();
128+
crate::impl_client_v17__walletprocesspsbt!();

contrib/run-bitcoind.sh

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ CONFIGURATION
4242
4343
- Examples
4444
45+
v29 29.0 290 /opt/bitcoin-29.0/bin/bitcoind
4546
v28 28.1 281 /opt/bitcoin-28.0/bin/bitcoind
4647
v24 24.2 242 /opt/bitcoin-24.2/bin/bitcoind
4748
v21 0.21.2 212 /opt/bitcoin-0.21.2/bin/bitcoind

integration_test/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ edition = "2021"
1313
[features]
1414
# Enable the same feature in `node` and the version feature here.
1515
# All minor releases of the latest three versions.
16+
29_0 = ["v29", "node/29_0"]
1617
28_0 = ["v28", "node/28_0"]
1718
27_2 = ["v27", "node/27_2"]
1819
27_1 = ["v27", "node/27_1"]
@@ -33,6 +34,7 @@ edition = "2021"
3334

3435
# These features are just for internal use (feature gating).
3536
# Each major version is tested with the same client.
37+
v29 = []
3638
v28 = []
3739
v27 = []
3840
v26 = []

integration_test/tests/blockchain.rs

+10
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ fn blockchain__get_block__modelled() {
3636
// assert!(json.into_model().is_ok());
3737
}
3838

39+
#[cfg(not(feature = "v29"))]
3940
#[test]
4041
fn blockchain__get_blockchain_info__modelled() {
4142
let node = Node::with_wallet(Wallet::None, &[]);
@@ -290,3 +291,12 @@ fn verify_tx_out_proof(node: &Node) -> Result<(), client_sync::Error> {
290291

291292
Ok(())
292293
}
294+
295+
#[test]
296+
#[cfg(feature = "v29")]
297+
fn blockchain__get_descriptor_activity__modelled() {
298+
let node = Node::with_wallet(Wallet::None, &["-coinstatsindex=1", "-txindex=1"]);
299+
300+
let res: GetDescriptorActivity = node.client.get_descriptor_activity(None, None, None).expect("get_descriptor_activity(None, None, None) failed");
301+
let _: Result<mtype::GetDescriptorActivity, GetDescriptorActivityError> = res.into_model();
302+
}

integration_test/tests/mining.rs

+22-6
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
use bitcoin::SignedAmount;
88
use integration_test::{Node, NodeExt as _, Wallet};
9-
use node::client::client_sync::{TemplateRequest, TemplateRules};
9+
use node::client::client_sync::{TemplateRequest, TemplateRules, TemplateRequestV29};
1010
use node::vtype::*; // All the version specific types.
1111
use node::mtype;
1212

@@ -15,16 +15,32 @@ fn mining__get_block_template__modelled() {
1515
// Requires connected nodes otherwise the RPC call errors.
1616
let (node1, node2, node3) = integration_test::three_node_network();
1717

18-
// Use the nodes otherwise they get dropped.
1918
node1.mine_a_block();
2019
node2.mine_a_block();
2120
node3.mine_a_block();
21+
std::thread::sleep(std::time::Duration::from_millis(500));
22+
23+
#[cfg(not(feature = "v29"))]
24+
{
25+
let options = TemplateRequest { rules: vec![TemplateRules::Segwit] };
26+
let json: GetBlockTemplate = node1.client.get_block_template(&options)
27+
.expect("get_block_template RPC failed (pre-v29)");
28+
let _: Result<mtype::GetBlockTemplate, GetBlockTemplateError> = json.into_model();
29+
}
2230

23-
let options = TemplateRequest { rules: vec![TemplateRules::Segwit] };
31+
#[cfg(feature = "v29")]
32+
{
33+
let request_v29 = TemplateRequestV29 {
34+
rules: vec!["segwit".to_string()],
35+
mode: Some("template".to_string()),
36+
..Default::default()
37+
};
2438

25-
let json: GetBlockTemplate = node1.client.get_block_template(&options).expect("rpc");
26-
let model: Result<mtype::GetBlockTemplate, GetBlockTemplateError> = json.into_model();
27-
model.unwrap();
39+
let json: GetBlockTemplate = node1.client.get_block_template(&request_v29)
40+
.expect("get_block_template RPC failed (v29)");
41+
42+
let _: Result<mtype::GetBlockTemplate, GetBlockTemplateError> = json.into_model();
43+
}
2844
}
2945

3046
#[test]

integration_test/tests/network.rs

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ fn network__get_net_totals() {
2020
let _: GetNetTotals = node.client.get_net_totals().expect("getnettotals");
2121
}
2222

23+
#[cfg(not(feature = "v29"))]
2324
#[test]
2425
fn network__get_network_info__modelled() {
2526
let node = Node::with_wallet(Wallet::None, &[]);

node/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ default = ["0_17_2"]
4545
download = ["anyhow", "bitcoin_hashes", "flate2", "tar", "minreq", "zip"]
4646

4747
# We support all minor releases of the latest three versions.
48+
29_0 = ["28_0"]
4849
28_0 = ["27_2"]
4950
27_2 = ["27_1"]
5051
27_1 = ["27_0"]

0 commit comments

Comments
 (0)