From adce8eb288cfee9f428e4f4c362b1854c65b5cc7 Mon Sep 17 00:00:00 2001 From: David Craven Date: Tue, 5 Feb 2019 18:13:52 +0100 Subject: [PATCH] Add configuration system. --- Cargo.lock | 12 ++++++ Cargo.toml | 6 +++ src/config.rs | 94 ++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 7 ++-- src/p2p/behaviour.rs | 63 ++++------------------------- src/p2p/mod.rs | 12 +++--- src/p2p/transport.rs | 7 ++-- 7 files changed, 133 insertions(+), 68 deletions(-) create mode 100644 src/config.rs diff --git a/Cargo.lock b/Cargo.lock index fbda4495..b28757e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -609,9 +609,15 @@ dependencies = [ "libp2p 0.3.0", "multibase 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "multihash 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-multiaddr 0.1.0", "parity-multihash 0.1.0", "protobuf 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.86 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.86 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2136,6 +2142,11 @@ dependencies = [ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "xdg" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "yamux" version = "0.1.6" @@ -2360,4 +2371,5 @@ dependencies = [ "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +"checksum xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" "checksum yamux 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e25561b512df3c287cf52404cab0b07ea43d095cb96230e9e2cb635db72d75f0" diff --git a/Cargo.toml b/Cargo.toml index a499e977..5890fc5f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,12 @@ futures = "*" libp2p = { version = "*", path = "../rust-libp2p" } multibase = "*" multihash = "*" +parity-multiaddr = { version = "*", path = "../rust-libp2p/misc/multiaddr" } parity-multihash = { version = "*", path = "../rust-libp2p/misc/multihash" } protobuf = "2.0.2" +rand = "0.6" +serde = "1.0" +serde_derive = "1.0" +serde_json = "1.0" tokio = "*" +xdg = "*" \ No newline at end of file diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 00000000..67a83c65 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,94 @@ +use libp2p::{Multiaddr, PeerId}; +use libp2p::multiaddr::Protocol; +use libp2p::secio::SecioKeyPair; +use rand::{Rng, rngs::EntropyRng}; +use serde_derive::{Serialize, Deserialize}; +use std::fs; +use std::path::Path; + +const APP_NAME: &'static str = "rust-ipfs"; +const CONFIG_FILE: &'static str = "config.json"; + +const BOOTSTRAP_NODES: &[&'static str] = &[ + "/ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", + "/ip4/104.236.179.241/tcp/4001/p2p/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM", + "/ip4/104.236.76.40/tcp/4001/p2p/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64", + "/ip4/128.199.219.111/tcp/4001/p2p/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu", + "/ip4/178.62.158.247/tcp/4001/p2p/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd", + "/ip6/2400:6180:0:d0::151:6001/tcp/4001/p2p/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu", + "/ip6/2604:a880:1:20::203:d001/tcp/4001/p2p/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM", + "/ip6/2604:a880:800:10::4a:5001/tcp/4001/p2p/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64", + "/ip6/2a03:b0c0:0:1010::23:1001/tcp/4001/p2p/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd", +]; + +#[derive(Serialize, Deserialize)] +pub struct Configuration { + raw_key: [u8; 32], + bootstrap: Vec, +} + +impl Configuration { + pub fn new() -> Self { + let xdg_dirs = xdg::BaseDirectories::with_prefix(APP_NAME).unwrap(); + let path = xdg_dirs.place_config_file(CONFIG_FILE).unwrap(); + Configuration::from_file(path) + } + + pub fn secio_key_pair(&self) -> SecioKeyPair { + SecioKeyPair::ed25519_raw_key(&self.raw_key).unwrap() + } + + pub fn bootstrap(&self) -> Vec<(Multiaddr, PeerId)> { + let mut bootstrap = Vec::new(); + for addr in &self.bootstrap { + let mut addr = addr.to_owned(); + let peer_id = match addr.pop() { + Some(Protocol::P2p(hash)) => PeerId::from_multihash(hash).unwrap(), + _ => panic!("No peer id for addr"), + }; + bootstrap.push((addr, peer_id)); + } + bootstrap + } + + pub fn generate() -> Self { + let raw_key: [u8; 32] = EntropyRng::new().gen(); + let bootstrap = BOOTSTRAP_NODES.iter().map(|node| { + node.parse().unwrap() + }).collect(); + Configuration { + raw_key, + bootstrap, + } + } + + pub fn from_file>(path: P) -> Self { + fs::read_to_string(&path).map(|content| { + serde_json::from_str(&content).unwrap() + }).unwrap_or_else(|_| { + let config = Configuration::generate(); + let string = serde_json::to_string(&config).unwrap(); + fs::write(path, string).unwrap(); + config + }) + } +} + +pub struct NetworkConfig { + pub key_pair: SecioKeyPair, + pub peer_id: PeerId, + pub bootstrap: Vec<(Multiaddr, PeerId)>, +} + +impl From<&Configuration> for NetworkConfig { + fn from(config: &Configuration) -> Self { + let key_pair = config.secio_key_pair(); + let peer_id = key_pair.to_peer_id(); + let bootstrap = config.bootstrap(); + NetworkConfig { + key_pair, + peer_id, + bootstrap, + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 7d00ed4e..4498674d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,16 +4,17 @@ #![feature(drain_filter)] use futures::prelude::*; use futures::try_ready; -use libp2p::secio::SecioKeyPair; mod bitswap; pub mod block; +mod config; mod future; mod p2p; mod repo; use self::bitswap::{strategy::AltruisticStrategy, Strategy}; pub use self::block::{Block, Cid}; +use self::config::{Configuration, NetworkConfig}; use self::future::BlockFuture; use self::p2p::{create_swarm, Swarm}; use self::repo::Repo; @@ -30,10 +31,10 @@ pub struct Ipfs { impl Ipfs { /// Creates a new ipfs node. pub fn new() -> Self { + let config = Configuration::new(); let repo = Repo::new(); - let local_key = SecioKeyPair::ed25519_generated().unwrap(); let strategy = AltruisticStrategy::new(repo.clone()); - let swarm = create_swarm(local_key); + let swarm = create_swarm(NetworkConfig::from(&config)); Ipfs { repo, diff --git a/src/p2p/behaviour.rs b/src/p2p/behaviour.rs index cf754d19..c7d63b9c 100644 --- a/src/p2p/behaviour.rs +++ b/src/p2p/behaviour.rs @@ -1,5 +1,6 @@ use crate::bitswap::{Bitswap, BitswapEvent}; use crate::block::{Block, Cid}; +use crate::config::NetworkConfig; use libp2p::{NetworkBehaviour, PeerId}; use libp2p::core::swarm::NetworkBehaviourEventProcess; use libp2p::core::muxing::{StreamMuxerBox, SubstreamRef}; @@ -8,46 +9,6 @@ use parity_multihash::Multihash; use std::sync::Arc; use tokio::prelude::*; -/// IPFS bootstrap nodes. -const BOOTSTRAP_NODES: &[(&'static str, &'static str)] = &[ - ( - "QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", - "/ip4/104.131.131.82/tcp/4001", - ), - ( - "QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM", - "/ip4/104.236.179.241/tcp/4001", - ), - ( - "QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64", - "/ip4/104.236.76.40/tcp/4001", - ), - ( - "QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu", - "/ip4/128.199.219.111/tcp/4001", - ), - ( - "QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd", - "/ip4/178.62.158.247/tcp/4001", - ), - /*( - "QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu", - "/ip6/2400:6180:0:d0::151:6001/tcp/4001", - ), - ( - "QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM", - "/ip6/2604:a880:1:20::203:d001/tcp/4001", - ), - ( - "QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64", - "/ip6/2604:a880:800:10::4a:5001/tcp/4001", - ), - ( - "QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd", - "/ip6/2a03:b0c0:0:1010::23:1001/tcp/4001", - ),*/ -]; - /// Behaviour type. #[derive(NetworkBehaviour)] pub struct Behaviour { @@ -103,21 +64,13 @@ impl impl Behaviour { /// Create a Kademlia behaviour with the IPFS bootstrap nodes. - pub fn new(local_peer_id: PeerId) -> Self { - println!("Local peer id: {}", &local_peer_id.to_base58()); + pub fn new(config: &NetworkConfig) -> Self { + println!("Local peer id: {}", config.peer_id.to_base58()); - // Note that normally the Kademlia process starts by performing lots of - // request in order to insert our local node in the DHT. However here we use - // `without_init` because this example is very ephemeral and we don't want - // to pollute the DHT. In a real world application, you want to use `new` - // instead. - let mut kademlia = Kademlia::without_init(local_peer_id); + let mut kademlia = Kademlia::new(config.peer_id.to_owned()); - for (identity, location) in BOOTSTRAP_NODES { - kademlia.add_address( - &identity.parse().unwrap(), - location.parse().unwrap(), - ); + for (addr, peer_id) in &config.bootstrap { + kademlia.add_address(peer_id, addr.to_owned()); } let bitswap = Bitswap::new(); @@ -153,6 +106,6 @@ impl Behaviour pub type TBehaviour = Behaviour>>; /// Create a IPFS behaviour with the IPFS bootstrap nodes. -pub fn build_behaviour(local_peer_id: PeerId) -> TBehaviour { - Behaviour::new(local_peer_id) +pub fn build_behaviour(config: &NetworkConfig) -> TBehaviour { + Behaviour::new(config) } diff --git a/src/p2p/mod.rs b/src/p2p/mod.rs index f2532c78..930f7618 100644 --- a/src/p2p/mod.rs +++ b/src/p2p/mod.rs @@ -1,5 +1,5 @@ //! P2P handling for IPFS nodes. -pub use libp2p::secio::SecioKeyPair; +use crate::config::NetworkConfig; mod behaviour; mod transport; @@ -7,15 +7,13 @@ mod transport; pub type Swarm = libp2p::core::Swarm; /// Creates a new IPFS swarm. -pub fn create_swarm(local_private_key: SecioKeyPair) -> Swarm { - let local_peer_id = local_private_key.to_peer_id(); - +pub fn create_swarm(config: NetworkConfig) -> Swarm { // Set up an encrypted TCP transport over the Mplex protocol. - let transport = transport::build_transport(local_private_key); + let transport = transport::build_transport(&config); // Create a Kademlia behaviour - let behaviour = behaviour::build_behaviour(local_peer_id.clone()); + let behaviour = behaviour::build_behaviour(&config); // Create a Swarm - libp2p::core::Swarm::new(transport, behaviour, local_peer_id) + libp2p::core::Swarm::new(transport, behaviour, config.peer_id) } diff --git a/src/p2p/transport.rs b/src/p2p/transport.rs index 2e0f69dc..99292f83 100644 --- a/src/p2p/transport.rs +++ b/src/p2p/transport.rs @@ -1,10 +1,11 @@ +use crate::config::NetworkConfig; use futures::future::Future; use libp2p::{PeerId, Transport}; use libp2p::core::muxing::StreamMuxerBox; use libp2p::core::transport::boxed::Boxed; use libp2p::core::upgrade::{self, InboundUpgradeExt, OutboundUpgradeExt}; use libp2p::mplex::MplexConfig; -use libp2p::secio::{SecioConfig, SecioKeyPair}; +use libp2p::secio::SecioConfig; use libp2p::tcp::TcpConfig; use std::io::{Error, ErrorKind}; use std::time::Duration; @@ -15,9 +16,9 @@ pub type TTransport = Boxed<(PeerId, StreamMuxerBox), Error>; /// Builds the transport that serves as a common ground for all connections. /// /// Set up an encrypted TCP transport over the Mplex protocol. -pub fn build_transport(local_private_key: SecioKeyPair) -> TTransport { +pub fn build_transport(config: &NetworkConfig) -> TTransport { let transport = TcpConfig::new(); - let secio_config = SecioConfig::new(local_private_key); + let secio_config = SecioConfig::new(config.key_pair.to_owned()); let mplex_config = MplexConfig::new(); transport