diff --git a/Cargo.lock b/Cargo.lock index 44782c4127..f2c7b8e250 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -564,6 +564,7 @@ dependencies = [ name = "ckb-rpc" version = "0.5.0-pre" dependencies = [ + "build-info 0.5.0-pre", "ckb-chain 0.5.0-pre", "ckb-core 0.5.0-pre", "ckb-db 0.5.0-pre", diff --git a/network/src/identify_service.rs b/network/src/identify_service.rs index 03cba6f094..0299613ddc 100644 --- a/network/src/identify_service.rs +++ b/network/src/identify_service.rs @@ -23,6 +23,8 @@ use std::time::Instant; use tokio::io::{AsyncRead, AsyncWrite}; use tokio::timer::Interval; +const MAX_LISTEND_ADDRS: usize = 10; + pub struct IdentifyService { pub client_version: String, pub protocol_version: String, @@ -71,10 +73,7 @@ impl IdentifyService { // get an external addrs for our node if let Some(ext_addr) = transport.nat_traversal(original_address, &observed_addr) { debug!(target: "network", "get new external address {:?}", ext_addr); - let mut listened_addresses = network.listened_addresses.write(); - if !listened_addresses.iter().any(|a| a == &ext_addr) { - listened_addresses.push(ext_addr.clone()); - } + network.discovery_listened_address(ext_addr.to_owned()); } } @@ -127,7 +126,11 @@ where public_key: network.local_public_key().clone(), protocol_version: format!("ckb/{}", self.protocol_version).to_owned(), agent_version: format!("ckb/{}", self.client_version).to_owned(), - listen_addrs: network.listened_addresses.read().clone(), + listen_addrs: network + .listened_addresses(MAX_LISTEND_ADDRS) + .into_iter() + .map(|(addr, _)| addr) + .collect(), protocols: vec![], // TODO FIXME: report local protocols }, &addr, diff --git a/network/src/network.rs b/network/src/network.rs index b141f6e7cf..17ac978962 100644 --- a/network/src/network.rs +++ b/network/src/network.rs @@ -17,6 +17,7 @@ use crate::NetworkConfig; use crate::{Error, ErrorKind, PeerIndex, ProtocolId}; use bytes::Bytes; use ckb_util::{Mutex, RwLock}; +use fnv::FnvHashMap; use futures::future::{self, select_all, Future}; use futures::sync::mpsc::UnboundedSender; use futures::sync::oneshot; @@ -66,7 +67,7 @@ impl PeerInfo { pub struct Network { peers_registry: RwLock, peer_store: Arc>, - pub(crate) listened_addresses: RwLock>, + listened_addresses: RwLock>, pub(crate) original_listened_addresses: RwLock>, pub(crate) ckb_protocols: CKBProtocols>, local_private_key: secio::SecioKeyPair, @@ -86,6 +87,21 @@ impl Network { &self.local_peer_id } + pub(crate) fn discovery_listened_address(&self, addr: Multiaddr) { + let mut listened_addresses = self.listened_addresses.write(); + let score = listened_addresses.entry(addr).or_insert(0); + *score = score.saturating_add(1); + } + + pub(crate) fn listened_addresses(&self, count: usize) -> Vec<(Multiaddr, u8)> { + let listened_addresses = self.listened_addresses.read(); + listened_addresses + .iter() + .take(count) + .map(|(addr, score)| (addr.to_owned(), *score)) + .collect() + } + pub(crate) fn get_peer_index(&self, peer_id: &PeerId) -> Option { let peers_registry = self.peers_registry.read(); peers_registry @@ -189,19 +205,31 @@ impl Network { self.local_private_key.to_public_key() } - pub fn external_url(&self) -> Option { - self.original_listened_addresses - .read() - .get(0) - .map(|addr| self.to_external_url(addr)) + pub fn external_urls(&self, max_urls: usize) -> Vec<(String, u8)> { + let mut urls = Vec::with_capacity(max_urls); + let original_listened_addresses = self.original_listened_addresses.read(); + + urls.append( + &mut self + .listened_addresses(max_urls.saturating_sub(original_listened_addresses.len())) + .into_iter() + .map(|(addr, score)| (self.to_external_url(&addr), score)) + .collect(), + ); + urls.append( + &mut original_listened_addresses + .iter() + .map(|addr| (self.to_external_url(addr), 1)) + .collect(), + ); + urls + } + pub fn node_id(&self) -> String { + self.local_private_key.to_peer_id().to_base58() } fn to_external_url(&self, addr: &Multiaddr) -> String { - format!( - "{}/p2p/{}", - addr, - self.local_private_key.to_peer_id().to_base58() - ) + format!("{}/p2p/{}", addr, self.node_id()) } pub(crate) fn send( @@ -470,7 +498,17 @@ impl Network { Some(private_key) => private_key?, None => return Err(ErrorKind::Other("secret_key not set".to_owned()).into()), }; - let listened_addresses = config.public_addresses.clone(); + let listened_addresses = { + let mut listened_addresses = FnvHashMap::with_capacity_and_hasher( + config.public_addresses.len(), + Default::default(), + ); + // set max score to public addresses + for addr in &config.public_addresses { + listened_addresses.insert(addr.to_owned(), std::u8::MAX); + } + listened_addresses + }; let peer_store: Arc> = { let mut peer_store = SqlitePeerStore::default(); let bootnodes = config.bootnodes()?; diff --git a/network/src/network_service.rs b/network/src/network_service.rs index a42b89bafe..d3a84e5df7 100644 --- a/network/src/network_service.rs +++ b/network/src/network_service.rs @@ -26,8 +26,13 @@ impl Drop for NetworkService { impl NetworkService { #[inline] - pub fn external_url(&self) -> Option { - self.network.external_url() + pub fn external_urls(&self, max_urls: usize) -> Vec<(String, u8)> { + self.network.external_urls(max_urls) + } + + #[inline] + pub fn node_id(&self) -> String { + self.network.node_id() } pub fn with_protocol_context(&self, protocol_id: ProtocolId, f: F) -> Option @@ -86,7 +91,7 @@ impl NetworkService { // This method will not wait for the server stopped, you should use server_future or // thread_handle to achieve that. fn shutdown(&mut self) -> Result<(), IoError> { - debug!(target: "network", "shutdown network service self: {:?}", self.external_url()); + debug!(target: "network", "shutdown network service self: {:?}", self.external_urls(1).get(0).map(|(addr, _)|addr.to_owned())); if let Some(close_tx) = self.close_tx.take() { let _ = close_tx .send(()) diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index 74975cdb2d..aaf7a8ac20 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -30,6 +30,7 @@ flatbuffers = "0.5.0" num_cpus = "1.0" faster-hex = "0.3" jsonrpc-types = { path = "../util/jsonrpc-types" } +build-info = { path = "../util/build-info" } [dev-dependencies] ckb-db = { path = "../db" } diff --git a/rpc/doc.md b/rpc/doc.md index 6c410402de..2a09ff449e 100644 --- a/rpc/doc.md +++ b/rpc/doc.md @@ -288,21 +288,30 @@ curl -d '{"id": 2, "jsonrpc": "2.0", "method":"get_tip_block_number","params": [ } ``` -# local_node_id +# local_node_info -Returns the local node id. +Returns the local node information. ## Examples ```shell -curl -d '{"id": 2, "jsonrpc": "2.0", "method":"local_node_id","params": []}' -H 'content-type:application/json' 'http://localhost:8114' +curl -d '{"id": 2, "jsonrpc": "2.0", "method":"local_node_info","params": []}' -H 'content-type:application/json' 'http://localhost:8114' ``` ```json { "jsonrpc": "2.0", - "result": "/ip4/0.0.0.0/tcp/8115/p2p/QmdSxB6iTcbhj6gbZNthvJrwRkJrwnsohNpVixY4FtcZwv", - "id": 2 + "result": { + "addresses": [ + { + "address": "/ip4/0.0.0.0/tcp/12344/p2p/QmWRU2NSro4wKgVbFX6y8SPFkcJ1tE2X5xzk9msMhdRmdS", + "score": 1 + } + ], + "node_id": "QmWRU2NSro4wKgVbFX6y8SPFkcJ1tE2X5xzk9msMhdRmdS", + "version": "0.5.0" + } + "id": 2, } ``` diff --git a/rpc/src/module/net.rs b/rpc/src/module/net.rs index c9c147ad27..ef0951ac2f 100644 --- a/rpc/src/module/net.rs +++ b/rpc/src/module/net.rs @@ -1,13 +1,17 @@ +use build_info::{get_version, Version}; use ckb_network::NetworkService; use jsonrpc_core::Result; use jsonrpc_macros::build_rpc_trait; +use jsonrpc_types::{LocalNode, NodeAddress}; use std::sync::Arc; +const MAX_ADDRS: usize = 50; + build_rpc_trait! { pub trait NetworkRpc { - // curl -d '{"id": 2, "jsonrpc": "2.0", "method":"send_transaction","params": [{"version":2, "deps":[], "inputs":[], "outputs":[]}]}' -H 'content-type:application/json' 'http://localhost:8114' - #[rpc(name = "local_node_id")] - fn local_node_id(&self) -> Result>; + // curl -d '{"id": 2, "jsonrpc": "2.0", "method":"local_node_info","params": []}' -H 'content-type:application/json' 'http://localhost:8114' + #[rpc(name = "local_node_info")] + fn local_node_info(&self) -> Result; } } @@ -16,7 +20,16 @@ pub(crate) struct NetworkRpcImpl { } impl NetworkRpc for NetworkRpcImpl { - fn local_node_id(&self) -> Result> { - Ok(self.network.external_url()) + fn local_node_info(&self) -> Result { + Ok(LocalNode { + version: get_version!().to_string(), + node_id: self.network.node_id(), + addresses: self + .network + .external_urls(MAX_ADDRS) + .into_iter() + .map(|(address, score)| NodeAddress { address, score }) + .collect(), + }) } } diff --git a/util/jsonrpc-types/src/lib.rs b/util/jsonrpc-types/src/lib.rs index acf7c23de5..ede9fa366f 100644 --- a/util/jsonrpc-types/src/lib.rs +++ b/util/jsonrpc-types/src/lib.rs @@ -2,6 +2,7 @@ mod block_template; mod blockchain; mod bytes; mod cell; +mod local_node; mod proposal_short_id; pub use self::block_template::{ @@ -10,4 +11,5 @@ pub use self::block_template::{ pub use self::blockchain::{Block, Header, OutPoint, Transaction, UncleBlock}; pub use self::bytes::Bytes; pub use self::cell::{CellOutputWithOutPoint, CellWithStatus}; +pub use self::local_node::{LocalNode, NodeAddress}; pub use jsonrpc_core::types::{error, id, params, request, response, version}; diff --git a/util/jsonrpc-types/src/local_node.rs b/util/jsonrpc-types/src/local_node.rs new file mode 100644 index 0000000000..dee1f776be --- /dev/null +++ b/util/jsonrpc-types/src/local_node.rs @@ -0,0 +1,14 @@ +use serde_derive::{Deserialize, Serialize}; + +#[derive(Clone, Default, Serialize, Deserialize, PartialEq, Eq, Hash, Debug)] +pub struct LocalNode { + pub version: String, + pub node_id: String, + pub addresses: Vec, +} + +#[derive(Clone, Default, Serialize, Deserialize, PartialEq, Eq, Hash, Debug)] +pub struct NodeAddress { + pub address: String, + pub score: u8, +}