Skip to content

Commit 798a62e

Browse files
committed
Add support for bitcoin core 29.0
1 parent f0586c0 commit 798a62e

File tree

39 files changed

+2868
-91
lines changed

39 files changed

+2868
-91
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_1",
215216
"27_0",

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::fs::File;
2021
use std::io::{BufRead, BufReader};
@@ -262,3 +263,18 @@ pub enum TemplateRules {
262263
/// Taproot supported.
263264
Taproot,
264265
}
266+
267+
/// Arg for the `getblocktemplate` method. (v29+).
268+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
269+
pub struct TemplateRequestV29 {
270+
#[serde(skip_serializing_if = "Option::is_none")]
271+
pub mode: Option<String>,
272+
#[serde(default, skip_serializing_if = "Vec::is_empty")]
273+
pub capabilities: Vec<String>,
274+
#[serde(default, skip_serializing_if = "Vec::is_empty")]
275+
pub rules: Vec<String>,
276+
#[serde(skip_serializing_if = "Option::is_none")]
277+
pub longpollid: Option<String>,
278+
#[serde(skip_serializing_if = "Option::is_none")]
279+
pub data: Option<String>,
280+
}
+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

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
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::v28::*;
18+
use crate::types::v29::{
19+
GetBlockHeader, GetBlockHeaderVerbose, GetBlockVerboseOne, GetBlockVerboseZero,
20+
GetDescriptorActivity, GetMiningInfo,
21+
};
22+
23+
#[rustfmt::skip] // Keep public re-exports separate.
24+
pub use crate::client_sync::{v23::AddressType, WalletCreateFundedPsbtInput};
25+
26+
crate::define_jsonrpc_minreq_client!("v29");
27+
crate::impl_client_check_expected_server_version!({ [290000] });
28+
29+
// == Blockchain ==
30+
crate::impl_client_v17__getbestblockhash!();
31+
crate::impl_client_v29__getblock!();
32+
crate::impl_client_v29__getblockchaininfo!();
33+
crate::impl_client_v17__getblockcount!();
34+
crate::impl_client_v19__getblockfilter!();
35+
crate::impl_client_v17__getblockhash!();
36+
crate::impl_client_v17__getblockheader!();
37+
crate::impl_client_v17__getblockstats!();
38+
crate::impl_client_v17__getchaintips!();
39+
crate::impl_client_v17__getchaintxstats!();
40+
crate::impl_client_v17__getdifficulty!();
41+
crate::impl_client_v19__getmempoolancestors!();
42+
crate::impl_client_v19__getmempooldescendants!();
43+
crate::impl_client_v19__getmempoolentry!();
44+
crate::impl_client_v17__getmempoolinfo!();
45+
crate::impl_client_v17__getrawmempool!();
46+
crate::impl_client_v22__gettxout!();
47+
crate::impl_client_v17__gettxoutproof!();
48+
crate::impl_client_v26__gettxoutsetinfo!();
49+
crate::impl_client_v17__preciousblock!();
50+
crate::impl_client_v17__verifytxoutproof!();
51+
crate::impl_client_v29__getdescriptoractivity!();
52+
53+
// == Control ==
54+
crate::impl_client_v17__getmemoryinfo!();
55+
crate::impl_client_v18__getrpcinfo!();
56+
crate::impl_client_v17__help!();
57+
crate::impl_client_v17__logging!();
58+
crate::impl_client_v17__stop!();
59+
crate::impl_client_v17__uptime!();
60+
61+
// == Generating ==
62+
crate::impl_client_v17__generatetoaddress!();
63+
crate::impl_client_v17__invalidateblock!();
64+
65+
// == Mining ==
66+
crate::impl_client_v29__getblocktemplate!();
67+
crate::impl_client_v29__getmininginfo!();
68+
crate::impl_client_v17__getnetworkhashps!();
69+
crate::impl_client_v26__get_prioritised_transactions!();
70+
crate::impl_client_v17__prioritisetransaction!();
71+
crate::impl_client_v17__submitblock!();
72+
73+
// == Network ==
74+
crate::impl_client_v17__getaddednodeinfo!();
75+
crate::impl_client_v17__getnettotals!();
76+
crate::impl_client_v17__getnetworkinfo!();
77+
crate::impl_client_v17__getpeerinfo!();
78+
79+
// == Rawtransactions ==
80+
crate::impl_client_v17__createrawtransaction!();
81+
crate::impl_client_v17__fundrawtransaction!();
82+
crate::impl_client_v17__sendrawtransaction!();
83+
crate::impl_client_v28__submitpackage!();
84+
85+
// == Wallet ==
86+
crate::impl_client_v17__addmultisigaddress!();
87+
crate::impl_client_v17__bumpfee!();
88+
crate::impl_client_v23__createwallet!();
89+
crate::impl_client_v17__dumpprivkey!();
90+
crate::impl_client_v17__dumpwallet!();
91+
crate::impl_client_v17__getaddressesbylabel!();
92+
crate::impl_client_v17__getaddressinfo!();
93+
crate::impl_client_v17__getbalance!();
94+
crate::impl_client_v19__getbalances!();
95+
crate::impl_client_v17__getnewaddress!();
96+
crate::impl_client_v17__getrawchangeaddress!();
97+
crate::impl_client_v17__getreceivedbyaddress!();
98+
crate::impl_client_v17__gettransaction!();
99+
crate::impl_client_v17__getunconfirmedbalance!();
100+
crate::impl_client_v17__getwalletinfo!();
101+
crate::impl_client_v17__listaddressgroupings!();
102+
crate::impl_client_v17__listlabels!();
103+
crate::impl_client_v17__listlockunspent!();
104+
crate::impl_client_v17__listreceivedbyaddress!();
105+
crate::impl_client_v17__listsinceblock!();
106+
crate::impl_client_v17__listtransactions!();
107+
crate::impl_client_v17__listunspent!();
108+
crate::impl_client_v17__listwallets!();
109+
crate::impl_client_v22__loadwallet!();
110+
crate::impl_client_v17__rescanblockchain!();
111+
crate::impl_client_v17__sendmany!();
112+
crate::impl_client_v17__sendtoaddress!();
113+
crate::impl_client_v17__signmessage!();
114+
crate::impl_client_v17__signrawtransactionwithwallet!();
115+
crate::impl_client_v21__unloadwallet!();
116+
crate::impl_client_v17__walletcreatefundedpsbt!();
117+
crate::impl_client_v17__walletprocesspsbt!();

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

+13-3
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,15 @@ fn blockchain__get_block__modelled() {
2828
model.expect("GetBlock into model");
2929

3030
let json: GetBlockVerboseOne = node.client.get_block_verbose_one(block_hash).expect("getblock verbose=1");
31-
let model: Result<mtype::GetBlockVerboseOne, GetBlockVerboseOneError> = json.into_model();
31+
let model = json.into_model();
3232
model.expect("GetBlockVerbose into model");
3333

3434
// TODO: Test getblock 2
3535
// let json = node.client.get_block_with_verbosity(block_hash, 2).expect("getblock verbosity 2");
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, &[]);
@@ -86,8 +87,8 @@ fn blockchain__get_block_header__modelled() {
8687
model.unwrap();
8788

8889
// verbose = true
89-
let json:GetBlockHeaderVerbose = node.client.get_block_header_verbose(&block_hash).expect("getblockheader");
90-
let model: Result<mtype::GetBlockHeaderVerbose, GetBlockHeaderVerboseError> = json.into_model();
90+
let json: GetBlockHeaderVerbose = node.client.get_block_header_verbose(&block_hash).expect("getblockheader");
91+
let model = json.into_model();
9192
model.unwrap();
9293
}
9394

@@ -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

+26-7
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,44 @@
66

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

1312
#[test]
1413
fn mining__get_block_template__modelled() {
1514
// Requires connected nodes otherwise the RPC call errors.
1615
let (node1, node2, node3) = integration_test::three_node_network();
1716

18-
// Use the nodes otherwise they get dropped.
1917
node1.mine_a_block();
2018
node2.mine_a_block();
2119
node3.mine_a_block();
20+
std::thread::sleep(std::time::Duration::from_millis(500));
2221

23-
let options = TemplateRequest { rules: vec![TemplateRules::Segwit] };
22+
#[cfg(not(feature = "v29"))]
23+
{
24+
use node::client::client_sync::{TemplateRequest, TemplateRules};
25+
26+
let options = TemplateRequest { rules: vec![TemplateRules::Segwit] };
27+
let result: GetBlockTemplate = node1.client.get_block_template(&options)
28+
.expect("get_block_template RPC failed (pre-v29)");
29+
let _ = result.into_model();
30+
}
2431

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();
32+
#[cfg(feature = "v29")]
33+
{
34+
use node::client::client_sync::TemplateRequestV29;
35+
36+
let request_v29 = TemplateRequestV29 {
37+
rules: vec!["segwit".to_string()],
38+
mode: Some("template".to_string()),
39+
..Default::default()
40+
};
41+
42+
let result_v29 = node1.client.get_block_template(&request_v29)
43+
.expect("get_block_template RPC failed (v29)");
44+
45+
let _ = result_v29.into_model();
46+
}
2847
}
2948

3049
#[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() {
2526
let node = Node::with_wallet(Wallet::None, &[]);

0 commit comments

Comments
 (0)