This commit is contained in:
Weiliang Li 2019-08-13 16:38:55 +09:00 committed by Andreas Fackler
parent 10748c4d4a
commit 63b9ab8857
10 changed files with 139 additions and 63 deletions

4
.gitignore vendored
View File

@ -24,4 +24,6 @@ Cargo.lock
/bak /bak
*.gz *.gz
massif* massif*
.vscode/

1
.rustfmt.toml Normal file
View File

@ -0,0 +1 @@
edition = "2018"

View File

@ -7,18 +7,18 @@ Tolerant consensus algorithm](https://github.com/poanetwork/hbbft).
### Running a test peer ### Running a test peer
1. `git clone https://github.com/poanetwork/hydrabadger` 1. `git clone https://github.com/poanetwork/hydrabadger`
2. `cd hydrabadger` 2. `cd hydrabadger`
3. `./run-node 0` 3. `./run-node 0`
#### Additional peers #### Additional peers
1. Open a new terminal window. 1. Open a new terminal window.
2. `cd {...}/hydrabadger` 2. `cd {...}/hydrabadger`
3. `./run-node 1` 3. `./run-node 1`
4. (Repeat 1 and 2), `./run-node 2`, `./run-node 3`, `./run-node 4` 4. (Repeat 1 and 2), `./run-node 2`, `./run-node 3`, `./run-node 4`
* Note: If your terminal has tabs, open multiple tabs and use - Note: If your terminal has tabs, open multiple tabs and use
ctrl-pgup/pgdown to cycle between tabs quickly. ctrl-pgup/pgdown to cycle between tabs quickly.
Each peer will generate a number of random transactions at regular intervals, Each peer will generate a number of random transactions at regular intervals,
process them accordingly, and output complete batches. If your terminal is process them accordingly, and output complete batches. If your terminal is
@ -43,16 +43,16 @@ very well yet.
### Unimplemented ### Unimplemented
* **Many edge cases and exceptions:** disconnects, reconnects, etc. - **Many edge cases and exceptions:** disconnects, reconnects, etc.
* Connecting to a network which is in the process of key generation causes - Connecting to a network which is in the process of key generation causes
the entire network to fail. For now, wait until the network starts the entire network to fail. For now, wait until the network starts
outputting batches before connecting additional peer nodes. outputting batches before connecting additional peer nodes.
* **Error handling** is atrocious, most errors are simply printed to the log. - **Error handling** is atrocious, most errors are simply printed to the log.
* **Usage as a library** is still a work in progress as the API settles. - **Usage as a library** is still a work in progress as the API settles.
* **Much, much more...** - **Much, much more...**
### License ### License
[![License: LGPL v3.0](https://img.shields.io/badge/License-LGPL%20v3-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0) [![License: LGPL v3.0](https://img.shields.io/badge/License-LGPL%20v3-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0)
This project is licensed under the GNU Lesser General Public License v3.0. See the [LICENSE](LICENSE) file for details. This project is licensed under the GNU Lesser General Public License v3.0. See the [LICENSE](LICENSE) file for details.

View File

@ -11,7 +11,7 @@ extern crate serde_derive;
use chrono::Local; use chrono::Local;
use clap::{App, Arg, ArgMatches}; use clap::{App, Arg, ArgMatches};
use hydrabadger::{Blockchain, Config, Hydrabadger, MiningError, Uid}; use hydrabadger::{Blockchain, Config, Hydrabadger, MiningError, Uid};
use rand::{Rng, distributions::Standard}; use rand::{distributions::Standard, Rng};
use std::collections::HashSet; use std::collections::HashSet;
use std::env; use std::env;
use std::io::Write; use std::io::Write;
@ -97,7 +97,12 @@ pub struct Transaction(pub Vec<u8>);
impl Transaction { impl Transaction {
fn random(len: usize) -> Transaction { fn random(len: usize) -> Transaction {
Transaction(rand::thread_rng().sample_iter(&Standard).take(len).collect()) Transaction(
rand::thread_rng()
.sample_iter(&Standard)
.take(len)
.collect(),
)
} }
} }

View File

@ -9,7 +9,7 @@ use super::{Error, Hydrabadger, InputOrMessage, State, StateDsct, StateMachine};
use crate::peer::Peers; use crate::peer::Peers;
use crate::{ use crate::{
key_gen, BatchTx, Contribution, InAddr, InternalMessage, InternalMessageKind, InternalRx, key_gen, BatchTx, Contribution, InAddr, InternalMessage, InternalMessageKind, InternalRx,
NetworkState, OutAddr, Step, Uid, WireMessage, WireMessageKind, NodeId, NetworkState, NodeId, OutAddr, Step, Uid, WireMessage, WireMessageKind,
}; };
use crossbeam::queue::SegQueue; use crossbeam::queue::SegQueue;
use hbbft::{ use hbbft::{
@ -90,7 +90,11 @@ impl<C: Contribution, N: NodeId> Handler<C, N> {
Ok(()) Ok(())
} }
fn handle_iom(&self, iom: InputOrMessage<C, N>, state: &mut StateMachine<C, N>) -> Result<(), Error> { fn handle_iom(
&self,
iom: InputOrMessage<C, N>,
state: &mut StateMachine<C, N>,
) -> Result<(), Error> {
trace!("hydrabadger::Handler: About to handle_iom: {:?}", iom); trace!("hydrabadger::Handler: About to handle_iom: {:?}", iom);
if let Some(step_res) = state.handle_iom(iom) { if let Some(step_res) = state.handle_iom(iom) {
let step = step_res.map_err(Error::HbStep)?; let step = step_res.map_err(Error::HbStep)?;
@ -518,7 +522,7 @@ impl<C: Contribution, N: NodeId> Handler<C, N> {
let peers = self.hdb.peers(); let peers = self.hdb.peers();
let new_id = Uid::new(); let new_id = Uid::new();
// tx.unbounded_send(key_gen::Message::instance_id().unwrap(); // tx.unbounded_send(key_gen::Message::instance_id().unwrap();
let instance_id = key_gen::InstanceId::User(new_id.clone()); let instance_id = key_gen::InstanceId::User(new_id);
let key_gen = key_gen::Machine::generate( let key_gen = key_gen::Machine::generate(
self.hdb.node_id(), self.hdb.node_id(),
self.hdb.secret_key().clone(), self.hdb.secret_key().clone(),
@ -541,9 +545,10 @@ impl<C: Contribution, N: NodeId> Handler<C, N> {
) => { ) => {
debug!("Received hello from {:?}", src_nid_new); debug!("Received hello from {:?}", src_nid_new);
let mut peers = self.hdb.peers_mut(); let mut peers = self.hdb.peers_mut();
match peers match peers.establish_validator(
.establish_validator(src_out_addr, (src_nid_new.clone(), src_in_addr, src_pk)) src_out_addr,
{ (src_nid_new.clone(), src_in_addr, src_pk),
) {
true => debug_assert!(src_nid_new == src_nid.unwrap()), true => debug_assert!(src_nid_new == src_nid.unwrap()),
false => debug_assert!(src_nid.is_none()), false => debug_assert!(src_nid.is_none()),
} }
@ -738,7 +743,10 @@ impl<C: Contribution, N: NodeId> Future for Handler<C, N> {
); );
} }
Target::All => { Target::All => {
peers.wire_to_all(WireMessage::message(self.hdb.node_id().clone(), hb_msg.message)); peers.wire_to_all(WireMessage::message(
self.hdb.node_id().clone(),
hb_msg.message,
));
} }
} }
} }

View File

@ -1,12 +1,11 @@
//! A hydrabadger consensus node. //! A hydrabadger consensus node.
//! //!
use serde::de::DeserializeOwned;
use super::{Error, Handler, StateDsct, StateMachine}; use super::{Error, Handler, StateDsct, StateMachine};
use crate::peer::{PeerHandler, Peers}; use crate::peer::{PeerHandler, Peers};
use crate::{ use crate::{
key_gen, BatchRx, Change, Contribution, EpochRx, EpochTx, InAddr, InternalMessage, InternalTx, key_gen, BatchRx, Change, Contribution, EpochRx, EpochTx, InAddr, InternalMessage, InternalTx,
OutAddr, WireMessage, WireMessageKind, WireMessages, NodeId, NodeId, OutAddr, WireMessage, WireMessageKind, WireMessages,
}; };
use futures::{ use futures::{
future::{self, Either}, future::{self, Either},
@ -14,7 +13,7 @@ use futures::{
}; };
use hbbft::crypto::{PublicKey, SecretKey}; use hbbft::crypto::{PublicKey, SecretKey};
use parking_lot::{Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard}; use parking_lot::{Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard};
use rand::{self, Rng}; use serde::de::DeserializeOwned;
use std::{ use std::{
collections::HashSet, collections::HashSet,
net::SocketAddr, net::SocketAddr,
@ -122,7 +121,7 @@ pub struct Hydrabadger<C: Contribution, N: NodeId> {
impl<C: Contribution, N: NodeId + DeserializeOwned + 'static> Hydrabadger<C, N> { impl<C: Contribution, N: NodeId + DeserializeOwned + 'static> Hydrabadger<C, N> {
/// Returns a new Hydrabadger node. /// Returns a new Hydrabadger node.
pub fn new(addr: SocketAddr, cfg: Config, nid: N) -> Self { pub fn new(addr: SocketAddr, cfg: Config, nid: N) -> Self {
let secret_key: SecretKey = rand::rngs::OsRng::new().expect("Unable to create rng").gen(); let secret_key = SecretKey::random();
let (peer_internal_tx, peer_internal_rx) = mpsc::unbounded(); let (peer_internal_tx, peer_internal_rx) = mpsc::unbounded();
let (batch_tx, batch_rx) = mpsc::unbounded(); let (batch_tx, batch_rx) = mpsc::unbounded();
@ -310,7 +309,8 @@ impl<C: Contribution, N: NodeId + DeserializeOwned + 'static> Hydrabadger<C, N>
/// Returns a future that handles incoming connections on `socket`. /// Returns a future that handles incoming connections on `socket`.
fn handle_incoming(self, socket: TcpStream) -> impl Future<Item = (), Error = ()> { fn handle_incoming(self, socket: TcpStream) -> impl Future<Item = (), Error = ()> {
info!("Incoming connection from '{}'", socket.peer_addr().unwrap()); info!("Incoming connection from '{}'", socket.peer_addr().unwrap());
let wire_msgs: WireMessages<C, N> = WireMessages::new(socket, self.inner.secret_key.clone()); let wire_msgs: WireMessages<C, N> =
WireMessages::new(socket, self.inner.secret_key.clone());
wire_msgs wire_msgs
.into_future() .into_future()

View File

@ -3,7 +3,7 @@
use super::Error; use super::Error;
use crate::hydrabadger::hydrabadger::Hydrabadger; use crate::hydrabadger::hydrabadger::Hydrabadger;
use crate::peer::Peers; use crate::peer::Peers;
use crate::{Contribution, NetworkState, Uid, WireMessage, NodeId}; use crate::{Contribution, NetworkState, NodeId, Uid, WireMessage};
use crossbeam::queue::SegQueue; use crossbeam::queue::SegQueue;
use futures::sync::mpsc; use futures::sync::mpsc;
use hbbft::{ use hbbft::{
@ -64,7 +64,6 @@ pub(super) enum State<N> {
sync_key_gen: Option<SyncKeyGen<N>>, sync_key_gen: Option<SyncKeyGen<N>>,
public_key: Option<PublicKey>, public_key: Option<PublicKey>,
public_keys: BTreeMap<N, PublicKey>, public_keys: BTreeMap<N, PublicKey>,
part_count: usize, part_count: usize,
ack_count: usize, ack_count: usize,
}, },
@ -75,7 +74,12 @@ pub(super) enum State<N> {
} }
/// Forwards an `Ack` to a `SyncKeyGen` instance. /// Forwards an `Ack` to a `SyncKeyGen` instance.
fn handle_ack<N: NodeId>(nid: &N, ack: Ack, ack_count: &mut usize, sync_key_gen: &mut SyncKeyGen<N>) { fn handle_ack<N: NodeId>(
nid: &N,
ack: Ack,
ack_count: &mut usize,
sync_key_gen: &mut SyncKeyGen<N>,
) {
trace!("KEY GENERATION: Handling ack from '{:?}'...", nid); trace!("KEY GENERATION: Handling ack from '{:?}'...", nid);
let ack_outcome = sync_key_gen let ack_outcome = sync_key_gen
.handle_ack(nid, ack.clone()) .handle_ack(nid, ack.clone())

View File

@ -7,7 +7,7 @@
use super::{key_gen, Config, Error, InputOrMessage}; use super::{key_gen, Config, Error, InputOrMessage};
use crate::peer::Peers; use crate::peer::Peers;
use crate::{ActiveNetworkInfo, Contribution, NetworkNodeInfo, NetworkState, Step, NodeId}; use crate::{ActiveNetworkInfo, Contribution, NetworkNodeInfo, NetworkState, NodeId, Step};
use crossbeam::queue::SegQueue; use crossbeam::queue::SegQueue;
use hbbft::{ use hbbft::{
crypto::{PublicKey, SecretKey}, crypto::{PublicKey, SecretKey},
@ -208,8 +208,12 @@ impl<C: Contribution, N: NodeId> StateMachine<C, N> {
State::DeterminingNetworkState { State::DeterminingNetworkState {
ref mut iom_queue, .. ref mut iom_queue, ..
} => { } => {
let (dhb, dhb_step) = let (dhb, dhb_step) = DynamicHoneyBadger::new_joining(
DynamicHoneyBadger::new_joining(local_nid, local_sk, jp, &mut StdRng::from_entropy())?; local_nid,
local_sk,
jp,
&mut StdRng::from_entropy(),
)?;
step_queue.push(dhb_step); step_queue.push(dhb_step);
iom_queue_ret = iom_queue.take().unwrap(); iom_queue_ret = iom_queue.take().unwrap();
@ -275,7 +279,12 @@ impl<C: Contribution, N: NodeId> StateMachine<C, N> {
let mut node_ids: BTreeMap<N, PublicKey> = peers let mut node_ids: BTreeMap<N, PublicKey> = peers
.validators() .validators()
.map(|p| (p.node_id().cloned().unwrap(), p.public_key().cloned().unwrap())) .map(|p| {
(
p.node_id().cloned().unwrap(),
p.public_key().cloned().unwrap(),
)
})
.collect(); .collect();
node_ids.insert(local_nid.clone(), local_sk.public_key()); node_ids.insert(local_nid.clone(), local_sk.public_key());
@ -391,8 +400,11 @@ impl<C: Contribution, N: NodeId> StateMachine<C, N> {
let peer_infos = peers let peer_infos = peers
.peers() .peers()
.filter_map(|peer| { .filter_map(|peer| {
peer.pub_info() peer.pub_info().map(|(nid, &in_addr, &pk)| NetworkNodeInfo {
.map(|(nid, &in_addr, &pk)| NetworkNodeInfo { nid: nid.clone(), in_addr, pk }) nid: nid.clone(),
in_addr,
pk,
})
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
match self.state { match self.state {
@ -467,7 +479,9 @@ impl<C: Contribution, N: NodeId> StateMachine<C, N> {
match iom { match iom {
InputOrMessage::Contribution(contrib) => dhb.propose(contrib, &mut rng), InputOrMessage::Contribution(contrib) => dhb.propose(contrib, &mut rng),
InputOrMessage::Change(change) => dhb.vote_for(change), InputOrMessage::Change(change) => dhb.vote_for(change),
InputOrMessage::Message(src_nid, msg) => dhb.handle_message(&src_nid, msg, &mut rng), InputOrMessage::Message(src_nid, msg) => {
dhb.handle_message(&src_nid, msg, &mut rng)
}
} }
}); });

View File

@ -23,8 +23,8 @@ extern crate env_logger;
extern crate log; extern crate log;
#[macro_use] #[macro_use]
extern crate failure; extern crate failure;
extern crate crossbeam;
extern crate chrono; extern crate chrono;
extern crate crossbeam;
extern crate crypto; extern crate crypto;
extern crate num_bigint; extern crate num_bigint;
extern crate num_traits; extern crate num_traits;
@ -69,7 +69,10 @@ use hbbft::{
sync_key_gen::{Ack, Part}, sync_key_gen::{Ack, Part},
Contribution as HbbftContribution, CpStep as MessagingStep, NodeIdT, Contribution as HbbftContribution, CpStep as MessagingStep, NodeIdT,
}; };
use rand::{distributions::{Standard, Distribution}, Rng}; use rand::{
distributions::{Distribution, Standard},
Rng,
};
use serde::{de::DeserializeOwned, Serialize}; use serde::{de::DeserializeOwned, Serialize};
use std::{ use std::{
collections::BTreeMap, collections::BTreeMap,
@ -146,8 +149,14 @@ pub struct Uid(pub(crate) Uuid);
impl Uid { impl Uid {
/// Returns a new, random `Uid`. /// Returns a new, random `Uid`.
pub fn new() -> Uid { pub fn new() -> Self {
Uid(Uuid::new_v4()) Self::default()
}
}
impl Default for Uid {
fn default() -> Self {
Self(Uuid::new_v4())
} }
} }
@ -215,7 +224,11 @@ pub struct NetworkNodeInfo<N> {
pub(crate) pk: PublicKey, pub(crate) pk: PublicKey,
} }
type ActiveNetworkInfo<N> = (Vec<NetworkNodeInfo<N>>, PublicKeySet, BTreeMap<N, PublicKey>); type ActiveNetworkInfo<N> = (
Vec<NetworkNodeInfo<N>>,
PublicKeySet,
BTreeMap<N, PublicKey>,
);
/// The current state of the network. /// The current state of the network.
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
@ -474,7 +487,10 @@ impl<C: Contribution, N: NodeId> InternalMessage<C, N> {
} }
/// Returns a new `InternalMessage` without a uid. /// Returns a new `InternalMessage` without a uid.
pub fn new_without_uid(src_addr: OutAddr, kind: InternalMessageKind<C, N>) -> InternalMessage<C, N> { pub fn new_without_uid(
src_addr: OutAddr,
kind: InternalMessageKind<C, N>,
) -> InternalMessage<C, N> {
InternalMessage::new(None, src_addr, kind) InternalMessage::new(None, src_addr, kind)
} }

View File

@ -4,14 +4,11 @@
use crate::hydrabadger::{Error, Hydrabadger}; use crate::hydrabadger::{Error, Hydrabadger};
use crate::{ use crate::{
Contribution, InAddr, InternalMessage, OutAddr, Uid, WireMessage, WireMessageKind, Contribution, InAddr, InternalMessage, NodeId, OutAddr, Uid, WireMessage, WireMessageKind,
WireMessages, WireRx, WireTx, NodeId, WireMessages, WireRx, WireTx,
}; };
use futures::sync::mpsc; use futures::sync::mpsc;
use hbbft::{ use hbbft::{crypto::PublicKey, dynamic_honey_badger::Input as HbInput};
crypto::PublicKey,
dynamic_honey_badger::Input as HbInput,
};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{ use std::{
borrow::Borrow, borrow::Borrow,
@ -135,7 +132,11 @@ impl<C: Contribution, N: NodeId> Future for PeerHandler<C, N> {
self.hdb.send_internal(InternalMessage::wire( self.hdb.send_internal(InternalMessage::wire(
Some(src_nid.clone()), Some(src_nid.clone()),
self.out_addr, self.out_addr,
WireMessage::welcome_received_change_add(src_nid.clone(), pk, net_state), WireMessage::welcome_received_change_add(
src_nid.clone(),
pk,
net_state,
),
)); ));
} }
WireMessageKind::HelloFromValidator(src_nid, in_addr, pk, net_state) => { WireMessageKind::HelloFromValidator(src_nid, in_addr, pk, net_state) => {
@ -144,7 +145,12 @@ impl<C: Contribution, N: NodeId> Future for PeerHandler<C, N> {
self.hdb.send_internal(InternalMessage::wire( self.hdb.send_internal(InternalMessage::wire(
Some(src_nid.clone()), Some(src_nid.clone()),
self.out_addr, self.out_addr,
WireMessage::hello_from_validator(src_nid.clone(), in_addr, pk, net_state), WireMessage::hello_from_validator(
src_nid.clone(),
in_addr,
pk,
net_state,
),
)); ));
} }
WireMessageKind::Message(src_nid, msg) => { WireMessageKind::Message(src_nid, msg) => {
@ -274,9 +280,15 @@ impl<C: Contribution, N: NodeId> Peer<C, N> {
/// Sets a peer state to `State::EstablishedObserver` and stores public info. /// Sets a peer state to `State::EstablishedObserver` and stores public info.
fn establish_observer(&mut self) { fn establish_observer(&mut self) {
self.state = match self.state { self.state = match self.state {
State::PendingJoinInfo { ref nid, in_addr, pk } => { State::PendingJoinInfo {
State::EstablishedObserver { nid: nid.clone(), in_addr, pk } ref nid,
} in_addr,
pk,
} => State::EstablishedObserver {
nid: nid.clone(),
in_addr,
pk,
},
_ => panic!( _ => panic!(
"Peer::establish_observer: Can only establish observer when \ "Peer::establish_observer: Can only establish observer when \
peer state is`PendingJoinInfo`." peer state is`PendingJoinInfo`."
@ -300,14 +312,22 @@ impl<C: Contribution, N: NodeId> Peer<C, N> {
); );
} }
}, },
State::EstablishedObserver { ref nid, in_addr, pk } => { State::EstablishedObserver {
ref nid,
in_addr,
pk,
} => {
if pub_info.is_some() { if pub_info.is_some() {
panic!( panic!(
"Peer::establish_validator: `pub_info` must be `None` \ "Peer::establish_validator: `pub_info` must be `None` \
when upgrading an observer node." when upgrading an observer node."
); );
} }
State::EstablishedValidator { nid: nid.clone(), in_addr, pk } State::EstablishedValidator {
nid: nid.clone(),
in_addr,
pk,
}
} }
_ => panic!( _ => panic!(
"Peer::establish_validator: Can only establish validator when \ "Peer::establish_validator: Can only establish validator when \
@ -457,7 +477,10 @@ impl<C: Contribution, N: NodeId> Peers<C, N> {
No peer found with outgoing address: {}", No peer found with outgoing address: {}",
out_addr.borrow() out_addr.borrow()
)); ));
match self.out_addrs.insert(pub_info.0.clone(), *out_addr.borrow()) { match self
.out_addrs
.insert(pub_info.0.clone(), *out_addr.borrow())
{
Some(_out_addr_pub) => { Some(_out_addr_pub) => {
let pi_pub = peer let pi_pub = peer
.pub_info() .pub_info()
@ -512,7 +535,10 @@ impl<C: Contribution, N: NodeId> Peers<C, N> {
No peer found with outgoing address: {}", No peer found with outgoing address: {}",
out_addr.borrow() out_addr.borrow()
)); ));
match self.out_addrs.insert(pub_info.0.clone(), *out_addr.borrow()) { match self
.out_addrs
.insert(pub_info.0.clone(), *out_addr.borrow())
{
Some(_out_addr_pub) => { Some(_out_addr_pub) => {
let pi_pub = peer let pi_pub = peer
.pub_info() .pub_info()