From a7a878511cab28f4c3fb68a923095857961173c3 Mon Sep 17 00:00:00 2001 From: deadmanoz Date: Tue, 30 Dec 2025 11:13:34 +0800 Subject: [PATCH] Add hidden RPC getrawaddrman Add support for the hidden getrawaddrman RPC (v26) that returns raw address manager table entries. This RPC is marked as experimental and hidden from the API docs by default. The mapped_as and source_mapped_as fields are Optional following the established codebase pattern for version-specific fields. These fields were added in Bitcoin Core v28 and will deserialize as None for earlier versions or when -asmap is not configured. Includes integration test. --- client/src/client_sync/v26/hidden.rs | 22 +++++++++++ client/src/client_sync/v26/mod.rs | 8 ++-- client/src/client_sync/v27/mod.rs | 7 ++-- client/src/client_sync/v28/mod.rs | 7 ++-- client/src/client_sync/v29/mod.rs | 13 ++++--- client/src/client_sync/v30/mod.rs | 11 +++--- integration_test/tests/hidden.rs | 30 ++++++++++++++ types/src/v26/hidden.rs | 52 +++++++++++++++++++++++++ types/src/v26/mod.rs | 2 + types/src/v27/mod.rs | 8 ++-- types/src/v28/hidden.rs | 58 ++++++++++++++++++++++++++++ types/src/v28/mod.rs | 2 + types/src/v29/mod.rs | 10 ++--- types/src/v30/mod.rs | 10 ++--- 14 files changed, 206 insertions(+), 34 deletions(-) create mode 100644 client/src/client_sync/v26/hidden.rs create mode 100644 types/src/v26/hidden.rs create mode 100644 types/src/v28/hidden.rs diff --git a/client/src/client_sync/v26/hidden.rs b/client/src/client_sync/v26/hidden.rs new file mode 100644 index 00000000..458f0ba0 --- /dev/null +++ b/client/src/client_sync/v26/hidden.rs @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: CC0-1.0 + +//! Macros for implementing JSON-RPC methods on a client. +//! +//! Specifically this is `== Hidden ==` methods that are not listed in the +//! API docs of Bitcoin Core `v26`. +//! +//! All macros require `Client` to be in scope. +//! +//! See or use the `define_jsonrpc_bitreq_client!` macro to define a `Client`. + +/// Implements Bitcoin Core JSON-RPC API method `getrawaddrman`. +#[macro_export] +macro_rules! impl_client_v26__get_raw_addrman { + () => { + impl Client { + pub fn get_raw_addrman(&self) -> Result { + self.call("getrawaddrman", &[]) + } + } + }; +} diff --git a/client/src/client_sync/v26/mod.rs b/client/src/client_sync/v26/mod.rs index 75e8067a..59b93a15 100644 --- a/client/src/client_sync/v26/mod.rs +++ b/client/src/client_sync/v26/mod.rs @@ -5,6 +5,7 @@ //! We ignore option arguments unless they effect the shape of the returned JSON data. pub mod blockchain; +pub mod hidden; pub mod mining; pub mod network; pub mod raw_transactions; @@ -84,12 +85,13 @@ crate::impl_client_v17__invalidate_block!(); crate::impl_client_v22__add_connection!(); crate::impl_client_v21__add_peer_address!(); crate::impl_client_v17__estimate_raw_fee!(); +crate::impl_client_v26__get_raw_addrman!(); +crate::impl_client_v20__mock_scheduler!(); +crate::impl_client_v17__reconsider_block!(); +crate::impl_client_v17__sync_with_validation_interface_queue!(); crate::impl_client_v17__wait_for_block!(); crate::impl_client_v17__wait_for_block_height!(); crate::impl_client_v17__wait_for_new_block!(); -crate::impl_client_v17__sync_with_validation_interface_queue!(); -crate::impl_client_v17__reconsider_block!(); -crate::impl_client_v20__mock_scheduler!(); // == Mining == crate::impl_client_v17__get_block_template!(); diff --git a/client/src/client_sync/v27/mod.rs b/client/src/client_sync/v27/mod.rs index 854ac50c..13e18b12 100644 --- a/client/src/client_sync/v27/mod.rs +++ b/client/src/client_sync/v27/mod.rs @@ -80,12 +80,13 @@ crate::impl_client_v17__invalidate_block!(); crate::impl_client_v27__add_connection!(); crate::impl_client_v21__add_peer_address!(); crate::impl_client_v17__estimate_raw_fee!(); +crate::impl_client_v26__get_raw_addrman!(); +crate::impl_client_v20__mock_scheduler!(); +crate::impl_client_v17__reconsider_block!(); +crate::impl_client_v17__sync_with_validation_interface_queue!(); crate::impl_client_v17__wait_for_block!(); crate::impl_client_v17__wait_for_block_height!(); crate::impl_client_v17__wait_for_new_block!(); -crate::impl_client_v17__sync_with_validation_interface_queue!(); -crate::impl_client_v17__reconsider_block!(); -crate::impl_client_v20__mock_scheduler!(); // == Mining == crate::impl_client_v17__get_block_template!(); diff --git a/client/src/client_sync/v28/mod.rs b/client/src/client_sync/v28/mod.rs index 77b8ec45..57c2c661 100644 --- a/client/src/client_sync/v28/mod.rs +++ b/client/src/client_sync/v28/mod.rs @@ -81,12 +81,13 @@ crate::impl_client_v17__invalidate_block!(); crate::impl_client_v27__add_connection!(); crate::impl_client_v21__add_peer_address!(); crate::impl_client_v17__estimate_raw_fee!(); +crate::impl_client_v26__get_raw_addrman!(); +crate::impl_client_v20__mock_scheduler!(); +crate::impl_client_v17__reconsider_block!(); +crate::impl_client_v17__sync_with_validation_interface_queue!(); crate::impl_client_v17__wait_for_block!(); crate::impl_client_v17__wait_for_block_height!(); crate::impl_client_v17__wait_for_new_block!(); -crate::impl_client_v17__sync_with_validation_interface_queue!(); -crate::impl_client_v17__reconsider_block!(); -crate::impl_client_v20__mock_scheduler!(); // == Mining == crate::impl_client_v17__get_block_template!(); diff --git a/client/src/client_sync/v29/mod.rs b/client/src/client_sync/v29/mod.rs index b1e90efa..942202ad 100644 --- a/client/src/client_sync/v29/mod.rs +++ b/client/src/client_sync/v29/mod.rs @@ -79,18 +79,19 @@ crate::impl_client_v20__generate_to_descriptor!(); crate::impl_client_v17__invalidate_block!(); // == Hidden == -crate::impl_client_v29__get_orphan_txs_verbosity_2!(); -crate::impl_client_v29__get_orphan_txs_verbosity_1!(); -crate::impl_client_v29__get_orphan_txs!(); crate::impl_client_v27__add_connection!(); crate::impl_client_v21__add_peer_address!(); crate::impl_client_v17__estimate_raw_fee!(); +crate::impl_client_v29__get_orphan_txs!(); +crate::impl_client_v29__get_orphan_txs_verbosity_1!(); +crate::impl_client_v29__get_orphan_txs_verbosity_2!(); +crate::impl_client_v26__get_raw_addrman!(); +crate::impl_client_v20__mock_scheduler!(); +crate::impl_client_v17__reconsider_block!(); +crate::impl_client_v17__sync_with_validation_interface_queue!(); crate::impl_client_v17__wait_for_block!(); crate::impl_client_v17__wait_for_block_height!(); crate::impl_client_v17__wait_for_new_block!(); -crate::impl_client_v17__sync_with_validation_interface_queue!(); -crate::impl_client_v17__reconsider_block!(); -crate::impl_client_v20__mock_scheduler!(); // == Mining == crate::impl_client_v17__get_block_template!(); diff --git a/client/src/client_sync/v30/mod.rs b/client/src/client_sync/v30/mod.rs index 0e2ab251..718607da 100644 --- a/client/src/client_sync/v30/mod.rs +++ b/client/src/client_sync/v30/mod.rs @@ -79,14 +79,15 @@ crate::impl_client_v20__generate_to_descriptor!(); crate::impl_client_v17__invalidate_block!(); // == Hidden == -crate::impl_client_v29__get_orphan_txs_verbosity_2!(); -crate::impl_client_v29__get_orphan_txs_verbosity_1!(); -crate::impl_client_v29__get_orphan_txs!(); crate::impl_client_v27__add_connection!(); crate::impl_client_v21__add_peer_address!(); -crate::impl_client_v17__sync_with_validation_interface_queue!(); -crate::impl_client_v17__reconsider_block!(); +crate::impl_client_v29__get_orphan_txs!(); +crate::impl_client_v29__get_orphan_txs_verbosity_1!(); +crate::impl_client_v29__get_orphan_txs_verbosity_2!(); +crate::impl_client_v26__get_raw_addrman!(); crate::impl_client_v20__mock_scheduler!(); +crate::impl_client_v17__reconsider_block!(); +crate::impl_client_v17__sync_with_validation_interface_queue!(); // == Mining == crate::impl_client_v17__get_block_template!(); diff --git a/integration_test/tests/hidden.rs b/integration_test/tests/hidden.rs index 8aa3aa8b..1e054a4c 100644 --- a/integration_test/tests/hidden.rs +++ b/integration_test/tests/hidden.rs @@ -238,3 +238,33 @@ fn hidden__mock_scheduler() { let _: () = node.client.mock_scheduler(1).expect("mockscheduler"); } + +#[test] +#[cfg(not(feature = "v25_and_below"))] +fn hidden__get_raw_addrman() { + let node = Node::with_wallet(Wallet::None, &[]); + + // Add a peer address so the address manager has something. + let peer_address = "1.2.3.4"; + let peer_port = 8333; + node.client.add_peer_address(peer_address, peer_port).expect("addpeeraddress"); + + let json: GetRawAddrMan = node.client.get_raw_addrman().expect("getrawaddrman"); + + let entry = json + .new + .values() + .find(|e| e.address == peer_address && e.port == peer_port) + .expect("added peer should appear in the 'new' table"); + + assert_eq!(entry.network, "ipv4"); + assert!(entry.services > 0); + assert!(entry.time > 0); + + // mapped_as fields added in v28, only present with -asmap config. + #[cfg(not(feature = "v27_and_below"))] + { + assert!(entry.mapped_as.is_none(), "mapped_as requires -asmap config"); + assert!(entry.source_mapped_as.is_none(), "source_mapped_as requires -asmap config"); + } +} diff --git a/types/src/v26/hidden.rs b/types/src/v26/hidden.rs new file mode 100644 index 00000000..3a24f507 --- /dev/null +++ b/types/src/v26/hidden.rs @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: CC0-1.0 + +//! The JSON-RPC API for Bitcoin Core `v26` - hidden. +//! +//! Types for methods that are excluded from the API docs by default. + +use alloc::collections::BTreeMap; + +use serde::{Deserialize, Serialize}; + +/// Result of JSON-RPC method `getrawaddrman`. +/// +/// > getrawaddrman +/// > +/// > EXPERIMENTAL warning: this call may be changed in future releases. +/// > +/// > Returns information on all address manager entries for the new and tried tables. +/// +/// This is a hidden RPC, useful for testing and development. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[cfg_attr(feature = "serde-deny-unknown-fields", serde(deny_unknown_fields))] +pub struct GetRawAddrMan { + /// Addresses in the "new" table (potential peers discovered but not yet connected to). + pub new: BTreeMap, + /// Addresses in the "tried" table (peers successfully connected to in the past). + pub tried: BTreeMap, +} + +/// An entry in the address manager table. Part of `getrawaddrman`. +/// +/// The key in the parent map is formatted as "bucket/position" indicating the +/// location in the relevant address manager table. +/// +/// Field order matches Bitcoin Core's RPC response definition. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[cfg_attr(feature = "serde-deny-unknown-fields", serde(deny_unknown_fields))] +pub struct RawAddrManEntry { + /// The address of the node. + pub address: String, + /// The port number of the node. + pub port: u16, + /// The network (ipv4, ipv6, onion, i2p, cjdns) of the address. + pub network: String, + /// The services offered by the node. + pub services: u64, + /// The UNIX epoch time when the node was last seen. + pub time: i64, + /// The address that relayed the address to us. + pub source: String, + /// The network (ipv4, ipv6, onion, i2p, cjdns) of the source address. + pub source_network: String, +} diff --git a/types/src/v26/mod.rs b/types/src/v26/mod.rs index 5a66571f..720a4a91 100644 --- a/types/src/v26/mod.rs +++ b/types/src/v26/mod.rs @@ -250,6 +250,7 @@ mod blockchain; mod control; +mod hidden; mod mining; mod network; mod raw_transactions; @@ -262,6 +263,7 @@ pub use self::{ GetTxOutSetInfo, GetTxOutSetInfoError, LoadTxOutSet, LoadTxOutSetError, }, control::Logging, + hidden::{GetRawAddrMan, RawAddrManEntry}, mining::{GetPrioritisedTransactions, PrioritisedTransaction}, network::{AddrManInfoNetwork, GetAddrManInfo, GetPeerInfo, PeerInfo}, raw_transactions::{ diff --git a/types/src/v27/mod.rs b/types/src/v27/mod.rs index 911d107c..bc36da08 100644 --- a/types/src/v27/mod.rs +++ b/types/src/v27/mod.rs @@ -335,11 +335,11 @@ pub use crate::{ v26::{ AddrManInfoNetwork, ChainState, CreateWallet, DescriptorProcessPsbt, DescriptorProcessPsbtError, DumpTxOutSet, DumpTxOutSetError, GetAddrManInfo, GetBalances, - GetBalancesError, GetChainStates, GetChainStatesError, GetPeerInfo, GetTransaction, - GetTransactionError, GetTxOutSetInfo, GetTxOutSetInfoError, GetWalletInfo, + GetBalancesError, GetChainStates, GetChainStatesError, GetPeerInfo, GetRawAddrMan, + GetTransaction, GetTransactionError, GetTxOutSetInfo, GetTxOutSetInfoError, GetWalletInfo, GetWalletInfoError, GetWalletInfoScanning, LastProcessedBlock, LastProcessedBlockError, - LoadTxOutSet, LoadTxOutSetError, LoadWallet, Logging, PeerInfo, SubmitPackage, - SubmitPackageError, SubmitPackageTxResult, SubmitPackageTxResultError, + LoadTxOutSet, LoadTxOutSetError, LoadWallet, Logging, PeerInfo, RawAddrManEntry, + SubmitPackage, SubmitPackageError, SubmitPackageTxResult, SubmitPackageTxResultError, SubmitPackageTxResultFees, SubmitPackageTxResultFeesError, UnloadWallet, WalletProcessPsbt, WalletProcessPsbtError, }, diff --git a/types/src/v28/hidden.rs b/types/src/v28/hidden.rs new file mode 100644 index 00000000..527125c7 --- /dev/null +++ b/types/src/v28/hidden.rs @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: CC0-1.0 + +//! The JSON-RPC API for Bitcoin Core `v28` - hidden. +//! +//! Types for methods that are excluded from the API docs by default. + +use alloc::collections::BTreeMap; + +use serde::{Deserialize, Serialize}; + +/// Result of JSON-RPC method `getrawaddrman`. +/// +/// > getrawaddrman +/// > +/// > EXPERIMENTAL warning: this call may be changed in future releases. +/// > +/// > Returns information on all address manager entries for the new and tried tables. +/// +/// This is a hidden RPC, useful for testing and development. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[cfg_attr(feature = "serde-deny-unknown-fields", serde(deny_unknown_fields))] +pub struct GetRawAddrMan { + /// Addresses in the "new" table (potential peers discovered but not yet connected to). + pub new: BTreeMap, + /// Addresses in the "tried" table (peers successfully connected to in the past). + pub tried: BTreeMap, +} + +/// An entry in the address manager table. Part of `getrawaddrman`. +/// +/// The key in the parent map is formatted as "bucket/position" indicating the +/// location in the relevant address manager table. +/// +/// Field order matches Bitcoin Core's RPC response definition. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[cfg_attr(feature = "serde-deny-unknown-fields", serde(deny_unknown_fields))] +pub struct RawAddrManEntry { + /// The address of the node. + pub address: String, + /// Mapped AS (Autonomous System) number at the end of the BGP route to the peer. + /// Only present if the -asmap config option is set. + pub mapped_as: Option, + /// The port number of the node. + pub port: u16, + /// The network (ipv4, ipv6, onion, i2p, cjdns) of the address. + pub network: String, + /// The services offered by the node. + pub services: u64, + /// The UNIX epoch time when the node was last seen. + pub time: i64, + /// The address that relayed the address to us. + pub source: String, + /// The network (ipv4, ipv6, onion, i2p, cjdns) of the source address. + pub source_network: String, + /// Mapped AS (Autonomous System) number at the end of the BGP route to the source. + /// Only present if the -asmap config option is set. + pub source_mapped_as: Option, +} diff --git a/types/src/v28/mod.rs b/types/src/v28/mod.rs index e88573eb..94121769 100644 --- a/types/src/v28/mod.rs +++ b/types/src/v28/mod.rs @@ -252,6 +252,7 @@ mod blockchain; mod control; +mod hidden; mod mining; mod network; mod raw_transactions; @@ -261,6 +262,7 @@ mod wallet; pub use self::{ blockchain::{GetBlockchainInfo, ScanTxOutSetStart, ScanTxOutSetUnspent}, control::Logging, + hidden::{GetRawAddrMan, RawAddrManEntry}, mining::GetMiningInfo, network::GetNetworkInfo, raw_transactions::{ diff --git a/types/src/v29/mod.rs b/types/src/v29/mod.rs index a228b0dd..09c4ff2a 100644 --- a/types/src/v29/mod.rs +++ b/types/src/v29/mod.rs @@ -361,10 +361,10 @@ pub use crate::{ v27::{GetPrioritisedTransactions, PrioritisedTransaction}, v28::{ CreateWalletDescriptor, GetAddressInfo, GetAddressInfoEmbedded, GetHdKeys, GetHdKeysError, - GetNetworkInfo, GetTransaction, HdKey, HdKeyDescriptor, ListSinceBlock, - ListSinceBlockError, ListTransactions, Logging, ScanTxOutSetStart, ScanTxOutSetUnspent, - SubmitPackage, SubmitPackageError, SubmitPackageTxResult, SubmitPackageTxResultError, - SubmitPackageTxResultFees, SubmitPackageTxResultFeesError, TransactionItem, - TransactionItemError, + GetNetworkInfo, GetRawAddrMan, GetTransaction, HdKey, HdKeyDescriptor, ListSinceBlock, + ListSinceBlockError, ListTransactions, Logging, RawAddrManEntry, ScanTxOutSetStart, + ScanTxOutSetUnspent, SubmitPackage, SubmitPackageError, SubmitPackageTxResult, + SubmitPackageTxResultError, SubmitPackageTxResultFees, SubmitPackageTxResultFeesError, + TransactionItem, TransactionItemError, }, }; diff --git a/types/src/v30/mod.rs b/types/src/v30/mod.rs index 15ddab2e..737aad8c 100644 --- a/types/src/v30/mod.rs +++ b/types/src/v30/mod.rs @@ -349,11 +349,11 @@ pub use crate::{ v27::{GetPrioritisedTransactions, PrioritisedTransaction}, v28::{ CreateWalletDescriptor, GetAddressInfo, GetAddressInfoEmbedded, GetHdKeys, GetHdKeysError, - GetNetworkInfo, GetTransaction, HdKey, HdKeyDescriptor, ListSinceBlock, - ListSinceBlockError, ListTransactions, Logging, ScanTxOutSetStart, ScanTxOutSetUnspent, - SubmitPackage, SubmitPackageError, SubmitPackageTxResult, SubmitPackageTxResultError, - SubmitPackageTxResultFees, SubmitPackageTxResultFeesError, TransactionItem, - TransactionItemError, + GetNetworkInfo, GetRawAddrMan, GetTransaction, HdKey, HdKeyDescriptor, ListSinceBlock, + ListSinceBlockError, ListTransactions, Logging, RawAddrManEntry, ScanTxOutSetStart, + ScanTxOutSetUnspent, SubmitPackage, SubmitPackageError, SubmitPackageTxResult, + SubmitPackageTxResultError, SubmitPackageTxResultFees, SubmitPackageTxResultFeesError, + TransactionItem, TransactionItemError, }, v29::{ ActivityEntry, ChainState, DeriveAddressesMultipath, GetBlockHeader, GetBlockHeaderError,