mirror of
https://github.com/mimblewimble/grin.git
synced 2025-02-01 17:01:09 +03:00
compare genesis during peering handshake (#327)
* wip - send genesis in handshake * error if genesis mismatch on handshake * fix the tests * preserve order of existing fields in hand/shake
This commit is contained in:
parent
1f0808fc24
commit
00d82f2c04
11 changed files with 116 additions and 80 deletions
|
@ -72,7 +72,7 @@ impl Chain {
|
|||
pub fn init(
|
||||
db_root: String,
|
||||
adapter: Arc<ChainAdapter>,
|
||||
gen_block: Option<Block>,
|
||||
genesis: Block,
|
||||
pow_verifier: fn(&BlockHeader, u32) -> bool,
|
||||
) -> Result<Chain, Error> {
|
||||
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())?;
|
||||
|
||||
|
|
|
@ -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 {}),
|
||||
|
|
|
@ -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 {}),
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ pub enum Error {
|
|||
API(api::Error),
|
||||
/// Error originating from wallet API.
|
||||
Wallet(wallet::Error),
|
||||
Cuckoo(pow::cuckoo::Error),
|
||||
}
|
||||
|
||||
impl From<chain::Error> for Error {
|
||||
|
@ -50,6 +51,12 @@ impl From<p2p::Error> for Error {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<pow::cuckoo::Error> for Error {
|
||||
fn from(e: pow::cuckoo::Error) -> Error {
|
||||
Error::Cuckoo(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<store::Error> for Error {
|
||||
fn from(e: store::Error) -> Error {
|
||||
Error::Store(e)
|
||||
|
|
|
@ -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<RwLock<VecDeque<u64>>>,
|
||||
/// 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::<Shake>(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<Future<Item = (TcpStream, ProtocolV1, PeerInfo), Error = Error>> {
|
||||
let nonces = self.nonces.clone();
|
||||
let genesis = self.genesis.clone();
|
||||
Box::new(
|
||||
read_msg::<Hand>(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(),
|
||||
};
|
||||
|
@ -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());
|
||||
|
|
|
@ -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<Hand, ser::Error> {
|
||||
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<Shake, ser::Error> {
|
||||
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,
|
||||
})
|
||||
|
|
|
@ -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<NetAdapter>) -> Server {
|
||||
pub fn new(
|
||||
capab: Capabilities,
|
||||
config: P2PConfig,
|
||||
adapter: Arc<NetAdapter>,
|
||||
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
|
||||
|
|
|
@ -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<ser::Error> for Error {
|
||||
|
|
|
@ -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(),
|
||||
)
|
||||
})
|
||||
|
|
|
@ -95,8 +95,9 @@ pub fn pow20<T: MiningWorker>(
|
|||
|
||||
/// Mines a genesis block, using the config specified miner if specified.
|
||||
/// Otherwise, uses the internal miner
|
||||
pub fn mine_genesis_block(miner_config: Option<types::MinerConfig>) -> Option<core::core::Block> {
|
||||
info!(util::LOGGER, "Genesis block not found, initializing...");
|
||||
pub fn mine_genesis_block(
|
||||
miner_config: Option<types::MinerConfig>,
|
||||
) -> Result<core::core::Block, Error> {
|
||||
let mut gen = genesis::genesis_dev();
|
||||
let diff = gen.header.difficulty.clone();
|
||||
|
||||
|
@ -114,7 +115,7 @@ pub fn mine_genesis_block(miner_config: Option<types::MinerConfig>) -> Option<co
|
|||
None => 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
|
||||
|
|
Loading…
Reference in a new issue