Skip to content

Commit d81bcbf

Browse files
committed
feat(rpc): getpeerinfo + getconnectioncount surface real registry data
Wire RPC context to the shared p2p peer registry and return registry-backed network peer data. Op: extend
1 parent 33ca5e5 commit d81bcbf

7 files changed

Lines changed: 100 additions & 7 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/node/src/run.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ pub fn run(mut config: Config) -> Result<()> {
130130
state.transactions(),
131131
state.network(),
132132
state.mining_template_id(),
133+
state.peers(),
133134
);
134135
let rpc_handler = Arc::new(bitcoin_rs_rpc::Handler::new(Arc::new(rpc_context)));
135136
let rpc_server = bitcoin_rs_rpc::RpcServer::bind(

crates/node/tests/rpc_wiring.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ fn rpc_context_shares_arc_identity_with_node_state() -> Result<()> {
2626
let transactions = state.transactions();
2727
let network = state.network();
2828
let mining_template_id = state.mining_template_id();
29+
let peers = state.peers();
2930

3031
let ctx = Context::from_handles(
3132
Arc::clone(&chain_tip),
@@ -34,6 +35,7 @@ fn rpc_context_shares_arc_identity_with_node_state() -> Result<()> {
3435
Arc::clone(&transactions),
3536
Arc::clone(&network),
3637
Arc::clone(&mining_template_id),
38+
Arc::clone(&peers),
3739
);
3840

3941
assert!(
@@ -60,6 +62,7 @@ fn rpc_context_shares_arc_identity_with_node_state() -> Result<()> {
6062
Arc::ptr_eq(&ctx.mining_template_id, &mining_template_id),
6163
"mining_template_id must share identity"
6264
);
65+
assert!(Arc::ptr_eq(&ctx.peers, &peers), "peers must share identity");
6366

6467
Ok(())
6568
}

crates/rpc/Cargo.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ workspace = true
1313

1414
[features]
1515
default = []
16-
rocksdb = ["bitcoin-rs-storage/rocksdb"]
17-
fjall = ["bitcoin-rs-storage/fjall"]
18-
redb = ["bitcoin-rs-storage/redb"]
16+
rocksdb = ["bitcoin-rs-storage/rocksdb", "bitcoin-rs-p2p/rocksdb"]
17+
fjall = ["bitcoin-rs-storage/fjall", "bitcoin-rs-p2p/fjall"]
18+
redb = ["bitcoin-rs-storage/redb", "bitcoin-rs-p2p/redb"]
1919
mdbx = ["bitcoin-rs-storage/mdbx"]
2020

2121
[dependencies]
@@ -24,6 +24,7 @@ bitcoin-rs-consensus.workspace = true
2424
bitcoin-rs-chain.workspace = true
2525
bitcoin-rs-index.workspace = true
2626
bitcoin-rs-mempool.workspace = true
27+
bitcoin-rs-p2p.workspace = true
2728
bitcoin-rs-utxo.workspace = true
2829
bitcoin-rs-filters.workspace = true
2930
bitcoin-rs-coinstats.workspace = true

crates/rpc/src/context.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ pub struct Context {
8484
pub transactions: Arc<RwLock<HashMap<Txid, Transaction>>>,
8585
/// Network counters and peers.
8686
pub network: Arc<RwLock<NetworkState>>,
87+
/// Shared registry of currently-handshook peers.
88+
pub peers: Arc<RwLock<Vec<bitcoin_rs_p2p::PeerInfo>>>,
8789
/// Current getblocktemplate long-poll id.
8890
pub mining_template_id: Arc<ArcSwap<CompactString>>,
8991
/// Receiver notified when mining template inputs change.
@@ -114,6 +116,7 @@ impl Context {
114116
blocks: Arc::new(RwLock::new(Vec::new())),
115117
transactions: Arc::new(RwLock::new(HashMap::new())),
116118
network: Arc::new(RwLock::new(NetworkState::default())),
119+
peers: Arc::new(RwLock::new(Vec::new())),
117120
mining_template_id: Arc::new(ArcSwap::from_pointee(CompactString::new("0"))),
118121
mining_notifications,
119122
mining_sender,
@@ -134,6 +137,7 @@ impl Context {
134137
transactions: Arc<RwLock<HashMap<Txid, Transaction>>>,
135138
network: Arc<RwLock<NetworkState>>,
136139
mining_template_id: Arc<ArcSwap<CompactString>>,
140+
peers: Arc<RwLock<Vec<bitcoin_rs_p2p::PeerInfo>>>,
137141
) -> Self {
138142
let (mining_sender, mining_notifications) = unbounded();
139143
Self {
@@ -142,6 +146,7 @@ impl Context {
142146
blocks,
143147
transactions,
144148
network,
149+
peers,
145150
mining_template_id,
146151
mining_notifications,
147152
mining_sender,
@@ -228,6 +233,7 @@ mod tests {
228233
Arc::new(RwLock::new(HashMap::new())),
229234
Arc::new(RwLock::new(NetworkState::default())),
230235
Arc::new(ArcSwap::from_pointee(CompactString::new("0"))),
236+
Arc::new(RwLock::new(Vec::new())),
231237
);
232238
assert!(
233239
Arc::ptr_eq(&ctx.chain_tip, &chain_tip),

crates/rpc/src/handlers/network.rs

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,44 @@ pub(crate) fn getnetworkinfo(_ctx: &Arc<Context>, params: &Value) -> Result<Valu
3232
}))
3333
}
3434

35-
pub(crate) fn getpeerinfo(_ctx: &Arc<Context>, params: &Value) -> Result<Value, RpcError> {
35+
pub(crate) fn getpeerinfo(ctx: &Arc<Context>, params: &Value) -> Result<Value, RpcError> {
3636
ensure_no_params(params)?;
37-
Ok(json!([]))
37+
let peers = ctx.peers.read();
38+
let mut array = Vec::with_capacity(peers.len());
39+
for (id, peer) in peers.iter().enumerate() {
40+
array.push(json!({
41+
"id": id,
42+
"addr": peer.addr.to_string(),
43+
"addrbind": peer.addr.to_string(),
44+
"services": format!("{:016x}", peer.services),
45+
"servicesnames": Vec::<String>::new(),
46+
"relaytxes": true,
47+
"lastsend": 0,
48+
"lastrecv": 0,
49+
"bytessent": 0,
50+
"bytesrecv": 0,
51+
"conntime": peer.conn_time,
52+
"timeoffset": 0,
53+
"pingtime": 0.0,
54+
"minping": 0.0,
55+
"version": peer.version,
56+
"subver": peer.user_agent.clone(),
57+
"inbound": peer.inbound,
58+
"startingheight": peer.start_height,
59+
"presynced_headers": -1,
60+
"synced_headers": -1,
61+
"synced_blocks": -1,
62+
"inflight": Vec::<u32>::new(),
63+
"addr_processed": 0,
64+
"addr_rate_limited": 0,
65+
"permissions": Vec::<String>::new(),
66+
"minfeefilter": 0.0,
67+
"bytessent_per_msg": serde_json::Map::<String, serde_json::Value>::new(),
68+
"bytesrecv_per_msg": serde_json::Map::<String, serde_json::Value>::new(),
69+
"connection_type": if peer.inbound { "inbound" } else { "outbound" },
70+
}));
71+
}
72+
Ok(json!(array))
3873
}
3974

4075
pub(crate) fn addnode(_ctx: &Arc<Context>, params: &Value) -> Result<Value, RpcError> {
@@ -50,7 +85,8 @@ pub(crate) fn disconnectnode(_ctx: &Arc<Context>, params: &Value) -> Result<Valu
5085

5186
pub(crate) fn getconnectioncount(ctx: &Arc<Context>, params: &Value) -> Result<Value, RpcError> {
5287
ensure_no_params(params)?;
53-
Ok(json!(ctx.network.read().connection_count))
88+
let count = ctx.peers.read().len();
89+
Ok(json!(count))
5490
}
5591

5692
pub(crate) fn getnettotals(ctx: &Arc<Context>, params: &Value) -> Result<Value, RpcError> {

crates/rpc/tests/handler_smoke.rs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,21 @@
22
extern crate alloc;
33

44
use alloc::sync::Arc;
5+
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
56

7+
use arc_swap::{ArcSwap, ArcSwapOption};
68
use bitcoin::consensus::encode::serialize_hex;
79
use bitcoin::hashes::Hash as _;
810
use bitcoin::{Amount, OutPoint, ScriptBuf, Sequence, Transaction, TxIn, TxOut, Txid, Witness};
911
use bitcoin_rs_chain::{ChainWork, NodeId, TipSnapshot};
1012
use bitcoin_rs_mempool::MempoolEntry;
13+
use bitcoin_rs_mempool::{Mempool, MempoolLimits};
14+
use bitcoin_rs_p2p::PeerInfo;
1115
use bitcoin_rs_primitives::Hash256;
12-
use bitcoin_rs_rpc::{BlockRecord, Context, Handler, RpcError};
16+
use bitcoin_rs_rpc::{BlockRecord, Context, Handler, NetworkState, RpcError};
17+
use compact_str::CompactString;
18+
use hashbrown::HashMap;
19+
use parking_lot::RwLock;
1320
use sonic_rs::{JsonContainerTrait as _, JsonValueTrait as _, json};
1421

1522
#[test]
@@ -102,6 +109,32 @@ fn all_required_handlers_return_core_shapes() -> Result<(), Box<dyn std::error::
102109
Ok(())
103110
}
104111

112+
#[test]
113+
fn network_peer_methods_read_shared_peer_registry() -> Result<(), Box<dyn std::error::Error>> {
114+
let peers = Arc::new(RwLock::new(vec![PeerInfo {
115+
addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 8333),
116+
version: 70_016,
117+
services: 0,
118+
user_agent: "/test/".into(),
119+
start_height: 0,
120+
conn_time: 0,
121+
inbound: true,
122+
}]));
123+
let handler = Handler::new(context_with_peers(peers));
124+
125+
let count = handler.dispatch("getconnectioncount", &json!([]))?;
126+
assert_eq!(count.as_u64(), Some(1));
127+
128+
let peer_info = handler.dispatch("getpeerinfo", &json!([]))?;
129+
let peer_info = peer_info
130+
.as_array()
131+
.ok_or("getpeerinfo must return array")?;
132+
let peer = peer_info.get(0).ok_or("getpeerinfo must return one peer")?;
133+
assert_eq!(peer_info.len(), 1);
134+
assert_eq!(peer.get("version").as_u64(), Some(70_016));
135+
Ok(())
136+
}
137+
105138
#[test]
106139
fn signing_methods_are_disabled() -> Result<(), Box<dyn std::error::Error>> {
107140
let handler = Handler::new(Arc::new(Context::new()));
@@ -148,6 +181,18 @@ impl Fixture {
148181
}
149182
}
150183

184+
fn context_with_peers(peers: Arc<RwLock<Vec<PeerInfo>>>) -> Arc<Context> {
185+
Arc::new(Context::from_handles(
186+
Arc::new(ArcSwapOption::empty()),
187+
Arc::new(RwLock::new(Mempool::new(MempoolLimits::default()))),
188+
Arc::new(RwLock::new(Vec::new())),
189+
Arc::new(RwLock::new(HashMap::new())),
190+
Arc::new(RwLock::new(NetworkState::default())),
191+
Arc::new(ArcSwap::from_pointee(CompactString::new("0"))),
192+
peers,
193+
))
194+
}
195+
151196
fn tx(label: u8, script_pubkey: ScriptBuf) -> Transaction {
152197
Transaction {
153198
version: bitcoin::transaction::Version::TWO,

0 commit comments

Comments
 (0)