diff --git a/chain/src/chain.rs b/chain/src/chain.rs index 32635804e..ab8ae30dd 100644 --- a/chain/src/chain.rs +++ b/chain/src/chain.rs @@ -72,7 +72,7 @@ impl Chain { pub fn init( db_root: String, adapter: Arc, - gen_block: Option, + genesis: Block, pow_verifier: fn(&BlockHeader, u32) -> bool, ) -> Result { let chain_store = store::ChainKVStore::new(db_root.clone())?; @@ -81,29 +81,30 @@ impl Chain { let head = match chain_store.head() { Ok(tip) => tip, Err(NotFoundErr) => { - if let None = gen_block { - return Err(Error::GenesisBlockRequired); - } - - let gen = gen_block.unwrap(); - chain_store.save_block(&gen)?; - chain_store.setup_height(&gen.header)?; + chain_store.save_block(&genesis)?; + chain_store.setup_height(&genesis.header)?; // saving a new tip based on genesis - let tip = Tip::new(gen.hash()); + let tip = Tip::new(genesis.hash()); chain_store.save_head(&tip)?; info!( LOGGER, - "Saved genesis block with hash: {:?}, nonce: {:?}, pow: {:?}", - gen.hash(), - gen.header.nonce, - gen.header.pow, + "Saved genesis block: {:?}, nonce: {:?}, pow: {:?}", + genesis.hash(), + genesis.header.nonce, + genesis.header.pow, ); tip } Err(e) => return Err(Error::StoreErr(e, "chain init load head".to_owned())), }; + info!( + LOGGER, + "Chain init: {:?}", + head, + ); + let store = Arc::new(chain_store); let sumtrees = sumtree::SumTrees::open(db_root, store.clone())?; diff --git a/chain/tests/mine_simple_chain.rs b/chain/tests/mine_simple_chain.rs index d9fb0a65a..499aa407a 100644 --- a/chain/tests/mine_simple_chain.rs +++ b/chain/tests/mine_simple_chain.rs @@ -44,10 +44,7 @@ fn setup(dir_name: &str) -> Chain { let _ = env_logger::init(); clean_output_dir(dir_name); global::set_mining_mode(ChainTypes::AutomatedTesting); - let mut genesis_block = None; - if !chain::Chain::chain_exists(dir_name.to_string()) { - genesis_block = pow::mine_genesis_block(None); - } + let genesis_block = pow::mine_genesis_block(None).unwrap(); chain::Chain::init( dir_name.to_string(), Arc::new(NoopAdapter {}), diff --git a/chain/tests/test_coinbase_maturity.rs b/chain/tests/test_coinbase_maturity.rs index 5ec4f8922..60c1bb085 100644 --- a/chain/tests/test_coinbase_maturity.rs +++ b/chain/tests/test_coinbase_maturity.rs @@ -44,10 +44,8 @@ fn test_coinbase_maturity() { clean_output_dir(".grin"); global::set_mining_mode(ChainTypes::AutomatedTesting); - let mut genesis_block = None; - if !chain::Chain::chain_exists(".grin".to_string()) { - genesis_block = pow::mine_genesis_block(None); - } + let genesis_block = pow::mine_genesis_block(None).unwrap(); + let chain = chain::Chain::init( ".grin".to_string(), Arc::new(NoopAdapter {}), diff --git a/grin/src/server.rs b/grin/src/server.rs index 3d6235eb1..1ede3346e 100644 --- a/grin/src/server.rs +++ b/grin/src/server.rs @@ -89,20 +89,20 @@ impl Server { let chain_adapter = Arc::new(ChainToPoolAndNetAdapter::new(tx_pool.clone())); - let mut genesis_block = None; - if !chain::Chain::chain_exists(config.db_root.clone()) { - let chain_type = config.chain_type.clone(); - if chain_type == global::ChainTypes::Testnet1 { - genesis_block = Some(genesis::genesis_testnet1()); - } else { - genesis_block = pow::mine_genesis_block(config.mining_config.clone()); - } - } + let genesis = match config.chain_type { + global::ChainTypes::Testnet1 => genesis::genesis_testnet1(), + _ => pow::mine_genesis_block(config.mining_config.clone())?, + }; + info!( + LOGGER, + "Starting server, genesis block: {}", + genesis.hash(), + ); let shared_chain = Arc::new(chain::Chain::init( config.db_root.clone(), chain_adapter.clone(), - genesis_block, + genesis.clone(), pow::verify_size, )?); @@ -114,11 +114,14 @@ impl Server { tx_pool.clone(), peer_store.clone(), )); + let p2p_server = Arc::new(p2p::Server::new( config.capabilities, config.p2p_config.unwrap(), net_adapter.clone(), + genesis.hash(), )); + chain_adapter.init(p2p_server.clone()); pool_net_adapter.init(p2p_server.clone()); diff --git a/grin/src/types.rs b/grin/src/types.rs index ed2de313a..c74a94762 100644 --- a/grin/src/types.rs +++ b/grin/src/types.rs @@ -36,6 +36,7 @@ pub enum Error { API(api::Error), /// Error originating from wallet API. Wallet(wallet::Error), + Cuckoo(pow::cuckoo::Error), } impl From for Error { @@ -50,6 +51,12 @@ impl From for Error { } } +impl From for Error { + fn from(e: pow::cuckoo::Error) -> Error { + Error::Cuckoo(e) + } +} + impl From for Error { fn from(e: store::Error) -> Error { Error::Store(e) diff --git a/p2p/src/handshake.rs b/p2p/src/handshake.rs index 4eb23b869..0d8be2fec 100644 --- a/p2p/src/handshake.rs +++ b/p2p/src/handshake.rs @@ -22,7 +22,7 @@ use rand::os::OsRng; use tokio_core::net::TcpStream; use core::core::target::Difficulty; -use core::ser; +use core::core::hash::Hash; use msg::*; use types::*; use protocol::ProtocolV1; @@ -36,6 +36,9 @@ pub struct Handshake { /// Ring buffer of nonces sent to detect self connections without requiring /// a node id. nonces: Arc>>, + /// The genesis block header of the chain seen by this node. + /// We only want to connect to other nodes seeing the same chain (forks are ok). + genesis: Hash, } unsafe impl Sync for Handshake {} @@ -43,9 +46,10 @@ unsafe impl Send for Handshake {} impl Handshake { /// Creates a new handshake handler - pub fn new() -> Handshake { + pub fn new(genesis: Hash) -> Handshake { Handshake { nonces: Arc::new(RwLock::new(VecDeque::with_capacity(NONCES_CAP))), + genesis: genesis, } } @@ -64,34 +68,35 @@ impl Handshake { Ok(pa) => pa, Err(e) => return Box::new(futures::future::err(Error::Connection(e))), }; - debug!( - LOGGER, - "handshake connect with nonce - {}, sender - {:?}, receiver - {:?}", - nonce, - self_addr, - peer_addr, - ); let hand = Hand { version: PROTOCOL_VERSION, capabilities: capab, nonce: nonce, + genesis: self.genesis, total_difficulty: total_difficulty, sender_addr: SockAddr(self_addr), receiver_addr: SockAddr(peer_addr), user_agent: USER_AGENT.to_string(), }; + let genesis = self.genesis; + // write and read the handshake response Box::new( write_msg(conn, hand, Type::Hand) .and_then(|conn| read_msg::(conn)) .and_then(move |(conn, shake)| { - if shake.version != 1 { - Err(Error::Serialization(ser::Error::UnexpectedData { - expected: vec![PROTOCOL_VERSION as u8], - received: vec![shake.version as u8], - })) + if shake.version != PROTOCOL_VERSION { + Err(Error::ProtocolMismatch { + us: PROTOCOL_VERSION, + peer: shake.version, + }) + } else if shake.genesis != genesis { + Err(Error::GenesisMismatch { + us: genesis, + peer: shake.genesis, + }) } else { let peer_info = PeerInfo { capabilities: shake.capabilities, @@ -102,6 +107,7 @@ impl Handshake { }; info!(LOGGER, "Connected to peer {:?}", peer_info); + // when more than one protocol version is supported, choosing should go here Ok((conn, ProtocolV1::new(), peer_info)) } @@ -118,30 +124,25 @@ impl Handshake { conn: TcpStream, ) -> Box> { let nonces = self.nonces.clone(); + let genesis = self.genesis.clone(); Box::new( read_msg::(conn) .and_then(move |(conn, hand)| { - if hand.version != 1 { - return Err(Error::Serialization(ser::Error::UnexpectedData { - expected: vec![PROTOCOL_VERSION as u8], - received: vec![hand.version as u8], - })); - } - { - // check the nonce to see if we could be trying to connect to ourselves + if hand.version != PROTOCOL_VERSION { + return Err(Error::ProtocolMismatch { + us: PROTOCOL_VERSION, + peer: hand.version, + }); + } else if hand.genesis != genesis { + return Err(Error::GenesisMismatch { + us: genesis, + peer: hand.genesis, + }); + } else { + // check the nonce to see if we are trying to connect to ourselves let nonces = nonces.read().unwrap(); - debug!( - LOGGER, - "checking the nonce - {}, {:?}", - &hand.nonce, - nonces, - ); if nonces.contains(&hand.nonce) { - debug!(LOGGER, "***** nonce matches! Avoiding connecting to ourselves"); - return Err(Error::Serialization(ser::Error::UnexpectedData { - expected: vec![], - received: vec![], - })); + return Err(Error::PeerWithSelf); } } @@ -157,6 +158,7 @@ impl Handshake { let shake = Shake { version: PROTOCOL_VERSION, capabilities: capab, + genesis: genesis, total_difficulty: total_difficulty, user_agent: USER_AGENT.to_string(), }; @@ -165,7 +167,7 @@ impl Handshake { .and_then(|(conn, shake, peer_info)| { debug!(LOGGER, "Success handshake with {}.", peer_info.addr); write_msg(conn, shake, Type::Shake) - // when more than one protocol version is supported, choosing should go here + // when more than one protocol version is supported, choosing should go here .map(|conn| (conn, ProtocolV1::new(), peer_info)) }), ) @@ -193,8 +195,6 @@ fn extract_ip(advertised: &SocketAddr, conn: &TcpStream) -> SocketAddr { match advertised { &SocketAddr::V4(v4sock) => { let ip = v4sock.ip(); - debug!(LOGGER, "extract_ip - {:?}, {:?}", ip, conn.peer_addr()); - if ip.is_loopback() || ip.is_unspecified() { if let Ok(addr) = conn.peer_addr() { return SocketAddr::new(addr.ip(), advertised.port()); diff --git a/p2p/src/msg.rs b/p2p/src/msg.rs index 41d50b77f..a5961d95f 100644 --- a/p2p/src/msg.rs +++ b/p2p/src/msg.rs @@ -131,7 +131,7 @@ pub struct MsgHeader { magic: [u8; 2], /// Type of the message. pub msg_type: Type, - /// Tota length of the message in bytes. + /// Total length of the message in bytes. pub msg_len: u64, } @@ -189,6 +189,8 @@ pub struct Hand { pub capabilities: Capabilities, /// randomly generated for each handshake, helps detect self pub nonce: u64, + /// genesis block of our chain, only connect to peers on the same chain + pub genesis: Hash, /// total difficulty accumulated by the sender, used to check whether sync /// may be needed pub total_difficulty: Difficulty, @@ -211,23 +213,27 @@ impl Writeable for Hand { self.total_difficulty.write(writer).unwrap(); self.sender_addr.write(writer).unwrap(); self.receiver_addr.write(writer).unwrap(); - writer.write_bytes(&self.user_agent) + writer.write_bytes(&self.user_agent).unwrap(); + self.genesis.write(writer).unwrap(); + Ok(()) } } impl Readable for Hand { fn read(reader: &mut Reader) -> Result { let (version, capab, nonce) = ser_multiread!(reader, read_u32, read_u32, read_u64); + let capabilities = try!(Capabilities::from_bits(capab).ok_or(ser::Error::CorruptedData,)); let total_diff = try!(Difficulty::read(reader)); let sender_addr = try!(SockAddr::read(reader)); let receiver_addr = try!(SockAddr::read(reader)); let ua = try!(reader.read_vec()); let user_agent = try!(String::from_utf8(ua).map_err(|_| ser::Error::CorruptedData)); - let capabilities = try!(Capabilities::from_bits(capab).ok_or(ser::Error::CorruptedData,)); + let genesis = try!(Hash::read(reader)); Ok(Hand { version: version, capabilities: capabilities, nonce: nonce, + genesis: genesis, total_difficulty: total_diff, sender_addr: sender_addr, receiver_addr: receiver_addr, @@ -243,6 +249,8 @@ pub struct Shake { pub version: u32, /// sender capabilities pub capabilities: Capabilities, + /// genesis block of our chain, only connect to peers on the same chain + pub genesis: Hash, /// total difficulty accumulated by the sender, used to check whether sync /// may be needed pub total_difficulty: Difficulty, @@ -259,6 +267,7 @@ impl Writeable for Shake { ); self.total_difficulty.write(writer).unwrap(); writer.write_bytes(&self.user_agent).unwrap(); + self.genesis.write(writer).unwrap(); Ok(()) } } @@ -266,13 +275,15 @@ impl Writeable for Shake { impl Readable for Shake { fn read(reader: &mut Reader) -> Result { let (version, capab) = ser_multiread!(reader, read_u32, read_u32); + let capabilities = try!(Capabilities::from_bits(capab).ok_or(ser::Error::CorruptedData,)); let total_diff = try!(Difficulty::read(reader)); let ua = try!(reader.read_vec()); let user_agent = try!(String::from_utf8(ua).map_err(|_| ser::Error::CorruptedData)); - let capabilities = try!(Capabilities::from_bits(capab).ok_or(ser::Error::CorruptedData,)); + let genesis = try!(Hash::read(reader)); Ok(Shake { version: version, capabilities: capabilities, + genesis: genesis, total_difficulty: total_diff, user_agent: user_agent, }) diff --git a/p2p/src/server.rs b/p2p/src/server.rs index c502f5aa5..b09d6006b 100644 --- a/p2p/src/server.rs +++ b/p2p/src/server.rs @@ -77,12 +77,17 @@ unsafe impl Send for Server {} // TODO TLS impl Server { /// Creates a new idle p2p server with no peers - pub fn new(capab: Capabilities, config: P2PConfig, adapter: Arc) -> Server { + pub fn new( + capab: Capabilities, + config: P2PConfig, + adapter: Arc, + genesis: Hash, + ) -> Server { Server { config: config, capabilities: capab, peers: Arc::new(RwLock::new(HashMap::new())), - handshake: Arc::new(Handshake::new()), + handshake: Arc::new(Handshake::new(genesis)), adapter: adapter, stop: RefCell::new(None), } @@ -180,8 +185,6 @@ impl Server { let capab = self.capabilities.clone(); let self_addr = SocketAddr::new(self.config.host, self.config.port); - debug!(LOGGER, "{} connecting to {}", self_addr, addr); - let socket = TcpStream::connect(&addr, &h).map_err(|e| Error::Connection(e)); let h2 = h.clone(); let request = socket diff --git a/p2p/src/types.rs b/p2p/src/types.rs index 8dc5276e8..53d29fe15 100644 --- a/p2p/src/types.rs +++ b/p2p/src/types.rs @@ -45,6 +45,15 @@ pub enum Error { Connection(io::Error), ConnectionClose, Timeout, + PeerWithSelf, + ProtocolMismatch { + us: u32, + peer: u32, + }, + GenesisMismatch { + us: Hash, + peer: Hash, + }, } impl From for Error { diff --git a/p2p/tests/peer_handshake.rs b/p2p/tests/peer_handshake.rs index 93f6ea500..508745d78 100644 --- a/p2p/tests/peer_handshake.rs +++ b/p2p/tests/peer_handshake.rs @@ -26,6 +26,7 @@ use tokio_core::net::TcpStream; use tokio_core::reactor::{self, Core}; use core::core::target::Difficulty; +use core::core::hash::Hash; use p2p::Peer; // Starts a server and connects a client peer to it to check handshake, @@ -36,7 +37,12 @@ fn peer_handshake() { let handle = evtlp.handle(); let p2p_conf = p2p::P2PConfig::default(); let net_adapter = Arc::new(p2p::DummyAdapter {}); - let server = p2p::Server::new(p2p::UNKNOWN, p2p_conf, net_adapter.clone()); + let server = p2p::Server::new( + p2p::UNKNOWN, + p2p_conf, + net_adapter.clone(), + Hash::from_vec(vec![]), + ); let run_server = server.start(handle.clone()); let my_addr = "127.0.0.1:5000".parse().unwrap(); @@ -59,7 +65,7 @@ fn peer_handshake() { p2p::UNKNOWN, Difficulty::one(), my_addr, - Arc::new(p2p::handshake::Handshake::new()), + Arc::new(p2p::handshake::Handshake::new(Hash::from_vec(vec![]))), net_adapter.clone(), ) }) diff --git a/pow/src/lib.rs b/pow/src/lib.rs index 774cb2a51..9488c83a5 100644 --- a/pow/src/lib.rs +++ b/pow/src/lib.rs @@ -95,8 +95,9 @@ pub fn pow20( /// Mines a genesis block, using the config specified miner if specified. /// Otherwise, uses the internal miner -pub fn mine_genesis_block(miner_config: Option) -> Option { - info!(util::LOGGER, "Genesis block not found, initializing..."); +pub fn mine_genesis_block( + miner_config: Option, +) -> Result { let mut gen = genesis::genesis_dev(); let diff = gen.header.difficulty.clone(); @@ -114,7 +115,7 @@ pub fn mine_genesis_block(miner_config: Option) -> Option Box::new(cuckoo::Miner::new(consensus::EASINESS, sz, proof_size)), }; pow_size(&mut *miner, &mut gen.header, diff, sz as u32).unwrap(); - Some(gen) + Ok(gen) } /// Runs a proof of work computation over the provided block using the provided