Skip to content

Conversation

@deadmanoz
Copy link
Contributor

Add support for the hidden getrawaddrman RPC that returns raw address manager table entries (added v26). This RPC is marked as experimental and hidden from the API docs by default.

The mapped_as and source_mapped_as fields are Option<u32> following the established pattern for version-specific fields (no into.rs required because these are additions rather than removals or changed types). These fields were added in Bitcoin Core v28 and will deserialise as None for earlier versions or when -asmap is not configured.

Note: will have conflicts with #435 and #433, happy to rebase after either merges

@deadmanoz
Copy link
Contributor Author

Ok so I learned that only the public RPCs should be added to the rustdoc table in the vX/mod.rs!

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<u32> 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.
Comment on lines +31 to +32
/// The key in the parent map is formatted as "bucket/position" indicating the
/// location in the relevant address manager table.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This key is a bit weird (not in your implementation, but in the Bitcoin Core implement). IIRC, it was done this way, as the key should be seen as Bitcoin Core implementation specific. However, RPC users might want to consume and act on it. I guess probably best to write a parser for it in downstream apps and not here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, so I'm not changing anything, the key is just a String like "255/50" as you get from the RPC directly.

Comment on lines +40 to +48
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");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addpeeraddress supports an additional argument for adding things to the tried table. However that wasn't implemented in #334.

I think for this test it's fine to just test the new table.

.find(|e| e.address == peer_address && e.port == peer_port)
.expect("added peer should appear in the 'new' table");

assert_eq!(entry.network, "ipv4");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
assert_eq!(entry.network, "ipv4");
assert_eq!(entry.network, "ipv4");
assert!(entry.services > 0);
assert!(entry.time > 0);

I don't recall what source is set to when using addpeeraddress, but maybe that can be checked as well.

assert_eq!(entry.network, "ipv4");

// mapped_as field added in v28, only present with -asmap config.
assert!(entry.mapped_as.is_none(), "mapped_as requires -asmap config");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
assert!(entry.mapped_as.is_none(), "mapped_as requires -asmap config");
assert!(entry.mapped_as.is_none(), "mapped_as requires -asmap config");
assert!(entry.source_mapped_as.is_none(), "mapped_as requires -asmap config");

This might break once/if asmap get's included and enabled by default. But that's probably a few versions out.

@0xB10C
Copy link
Contributor

0xB10C commented Dec 30, 2025

Looks good to me!

https://github.com/0xB10C/addrman-observer still uses a custom rust-bitcoincore-rpc getrawaddrman implementation. Would be nice to replace that with this PR! I might do this to test this PR.

@deadmanoz
Copy link
Contributor Author

Looks good to me!

https://github.com/0xB10C/addrman-observer still uses a custom rust-bitcoincore-rpc getrawaddrman implementation. Would be nice to replace that with this PR! I might do this to test this PR.

I was going to propagate the change once all good here, but go for it!

/// 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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub struct RawAddrmanEntry {
pub struct RawAddrManEntry {

And same everywhere else that rawaddrman is converted to a Rust type.

Comment on lines +40 to +43
/// Mapped AS (Autonomous System) number at the end of the BGP route to the peer.
/// Only present if the -asmap config option is set.
/// Added in Bitcoin Core v28.
pub mapped_as: Option<u32>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this and props for getting as far as you did in this convoluted repo. The version specific types are designed to be exactly correct for the version i.e., v26::RawAddrManEntry should be exactly the shape returned by Core v26. This means no optional fields. Rather anytime a new field is added a new version of the struct is added, so we should have a v28::RawAddrMan (and v28::RawAddrManEntry).

(If there was an associated struct in model, where there is not in this case, it would use optional fields because the model types are version in-specific.)

Copy link
Member

@tcharding tcharding Dec 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FTR looks like you have the client stuff correct, because the v28 changes do not effect the client so the v26 client macro works just fine. I mentioned that this repo was convoluted, right?

@tcharding
Copy link
Member

Everything else looks good. Reviewed: bafede8

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants