From 150953a1382eb3fa3f39cc5802ddc3bc87938fd1 Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Tue, 1 Feb 2022 17:20:10 +0100 Subject: [PATCH] Add missing networking RPC calls. --- client/src/client.rs | 71 ++++++++++++++++++++++++++++++++++++ integration_test/src/main.rs | 61 +++++++++++++++++++++++++++++++ json/src/lib.rs | 45 +++++++++++++++++++++++ 3 files changed, 177 insertions(+) diff --git a/client/src/client.rs b/client/src/client.rs index 651acd96..cd9f8b21 100644 --- a/client/src/client.rs +++ b/client/src/client.rs @@ -908,6 +908,77 @@ pub trait RpcApi: Sized { ) } + /// Attempts to add a node to the addnode list. + /// Nodes added using addnode (or -connect) are protected from DoS disconnection and are not required to be full nodes/support SegWit as other outbound peers are (though such peers will not be synced from). + fn add_node(&self, addr: &str) -> Result<()> { + self.call("addnode", &[into_json(&addr)?, into_json("add")?]) + } + + /// Attempts to remove a node from the addnode list. + fn remove_node(&self, addr: &str) -> Result<()> { + self.call("addnode", &[into_json(&addr)?, into_json("remove")?]) + } + + /// Attempts to connect to a node without permanently adding it to the addnode list. + fn onetry_node(&self, addr: &str) -> Result<()> { + self.call("addnode", &[into_json(&addr)?, into_json("onetry")?]) + } + + /// Immediately disconnects from the specified peer node. + fn disconnect_node(&self, addr: &str) -> Result<()> { + self.call("disconnectnode", &[into_json(&addr)?]) + } + + fn disconnect_node_by_id(&self, node_id: u32) -> Result<()> { + self.call("disconnectnode", &[into_json("")?, into_json(node_id)?]) + } + + /// Returns information about the given added node, or all added nodes (note that onetry addnodes are not listed here) + fn get_added_node_info(&self, node: Option<&str>) -> Result> { + if let Some(addr) = node { + self.call("getaddednodeinfo", &[into_json(&addr)?]) + } else { + self.call("getaddednodeinfo", &[]) + } + } + + /// Return known addresses which can potentially be used to find new nodes in the network + fn get_node_addresses( + &self, + count: Option, + ) -> Result> { + let cnt = count.unwrap_or(1); + self.call("getnodeaddresses", &[into_json(&cnt)?]) + } + + /// List all banned IPs/Subnets. + fn list_banned(&self) -> Result> { + self.call("listbanned", &[]) + } + + /// Clear all banned IPs. + fn clear_banned(&self) -> Result<()> { + self.call("clearbanned", &[]) + } + + /// Attempts to add an IP/Subnet to the banned list. + fn add_ban(&self, subnet: &str, bantime: u64, absolute: bool) -> Result<()> { + self.call( + "setban", + &[into_json(&subnet)?, into_json("add")?, into_json(&bantime)?, into_json(&absolute)?], + ) + } + + /// Attempts to remove an IP/Subnet from the banned list. + fn remove_ban(&self, subnet: &str) -> Result<()> { + self.call("setban", &[into_json(&subnet)?, into_json("remove")?]) + } + + /// Disable/enable all p2p network activity. + fn set_network_active(&self, state: bool) -> Result { + self.call("setnetworkactive", &[into_json(&state)?]) + } + /// Returns data about each connected network node as an array of /// [`PeerInfo`][] /// diff --git a/integration_test/src/main.rs b/integration_test/src/main.rs index 0e35ee79..6771f45a 100644 --- a/integration_test/src/main.rs +++ b/integration_test/src/main.rs @@ -203,6 +203,12 @@ fn main() { //TODO load_wallet(&self, wallet: &str) -> Result { //TODO unload_wallet(&self, wallet: Option<&str>) -> Result<()> { //TODO backup_wallet(&self, destination: Option<&str>) -> Result<()> { + test_add_node(&cl); + test_get_added_node_info(&cl); + test_get_node_addresses(&cl); + test_disconnect_node(&cl); + test_add_ban(&cl); + test_set_network_active(&cl); test_stop(cl); } @@ -1007,6 +1013,61 @@ fn test_get_chain_tips(cl: &Client) { assert_eq!(tips.len(), 1); } +fn test_add_node(cl: &Client) { + cl.add_node("127.0.0.1:1234").unwrap(); + assert_error_message!(cl.add_node("127.0.0.1:1234"), -23, "Error: Node already added"); + cl.remove_node("127.0.0.1:1234").unwrap(); + cl.onetry_node("127.0.0.1:1234").unwrap(); +} + +fn test_get_added_node_info(cl: &Client) { + cl.add_node("127.0.0.1:1234").unwrap(); + let added_info = cl.get_added_node_info(None).unwrap(); + assert_eq!(added_info.len(), 1); +} + +fn test_get_node_addresses(cl: &Client) { + cl.get_node_addresses(None).unwrap(); +} + +fn test_disconnect_node(cl: &Client) { + assert_error_message!( + cl.disconnect_node("127.0.0.1:1234"), + -29, + "Node not found in connected nodes" + ); + assert_error_message!(cl.disconnect_node_by_id(1), -29, "Node not found in connected nodes"); +} + +fn test_add_ban(cl: &Client) { + cl.add_ban("127.0.0.1", 0, false).unwrap(); + let res = cl.list_banned().unwrap(); + assert_eq!(res.len(), 1); + + cl.remove_ban("127.0.0.1").unwrap(); + let res = cl.list_banned().unwrap(); + assert_eq!(res.len(), 0); + + cl.add_ban("127.0.0.1", 0, false).unwrap(); + let res = cl.list_banned().unwrap(); + assert_eq!(res.len(), 1); + + cl.clear_banned().unwrap(); + let res = cl.list_banned().unwrap(); + assert_eq!(res.len(), 0); + + assert_error_message!( + cl.add_ban("INVALID_STRING", 0, false), + -30, + "Error: Invalid IP/Subnet" + ); +} + +fn test_set_network_active(cl: &Client) { + cl.set_network_active(false).unwrap(); + cl.set_network_active(true).unwrap(); +} + fn test_get_net_totals(cl: &Client) { cl.get_net_totals().unwrap(); } diff --git a/json/src/lib.rs b/json/src/lib.rs index 23c8ecef..22ccb3ee 100644 --- a/json/src/lib.rs +++ b/json/src/lib.rs @@ -1087,6 +1087,51 @@ pub enum GetPeerInfoResultConnectionType { Feeler, } +#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)] +pub struct GetAddedNodeInfoResult { + /// The node IP address or name (as provided to addnode) + #[serde(rename = "addednode")] + pub added_node: String, + /// If connected + pub connected: bool, + /// Only when connected = true + pub addresses: Vec, +} + +#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)] +pub struct GetAddedNodeInfoResultAddress { + /// The bitcoin server IP and port we're connected to + pub address: String, + /// connection, inbound or outbound + pub connected: GetAddedNodeInfoResultAddressType, +} + +#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)] +#[serde(rename_all = "lowercase")] +pub enum GetAddedNodeInfoResultAddressType { + Inbound, + Outbound, +} + +#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)] +pub struct GetNodeAddressesResult { + /// Timestamp in seconds since epoch (Jan 1 1970 GMT) keeping track of when the node was last seen + pub time: u64, + /// The services offered + pub services: usize, + /// The address of the node + pub address: String, + /// The port of the node + pub port: u16, +} + +#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)] +pub struct ListBannedResult { + pub address: String, + pub banned_until: u64, + pub ban_created: u64, +} + /// Models the result of "estimatesmartfee" #[derive(Clone, Debug, Deserialize, Serialize)] pub struct EstimateSmartFeeResult {