Skip to content

Commit

Permalink
byzantine strategy: null proposal leader
Browse files Browse the repository at this point in the history
  • Loading branch information
pysel committed Oct 17, 2023
1 parent c43d846 commit 932c5ce
Show file tree
Hide file tree
Showing 12 changed files with 102 additions and 18 deletions.
39 changes: 38 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,44 @@ launch-default:
@rm -rf output.txt
@./scripts/generate_config.sh 10
./scripts/launch_with_X_nodes.sh 10 8 ${CURTIME}


# launch-null-proposal works the same as launch, but sets the leader strategy to null proposal
# In null_proposal strategy, a leader does not propose any value
# In this scenario, all honest followers should output default NULL value
launch-null-proposal:
@rm -rf output.txt
@./scripts/generate_config.sh ${NODES}
@./scripts/set_leader_strategy.sh leader_null_proposal
./scripts/launch_with_X_nodes.sh ${NODES} ${F} ${CURTIME}

# launch-null-proposal-default launches a protocol with null_proposal byzantine leader and 9 followers
launch-null-proposal-default:
@rm -rf output.txt
@./scripts/generate_config.sh 10
@./scripts/set_leader_strategy.sh leader_null_proposal
./scripts/launch_with_X_nodes.sh 10 8 ${CURTIME}

# generate-config generates a config file with NODES amount of nodes
# example: make NODES=10 generate-config
generate-config:
@./scripts/generate_config.sh ${NODES}

# set-leader-strategy sets the leader strategy to STRATEGY
# example: make STRATEGY=leader_null_proposal set-leader-strategy
# available strategies: leader (aka honest), leader_null_proposal
set-leader-strategy:
@./scripts/set_leader_strategy.sh ${STRATEGY}

# launch-custom-config launches a protocol with a custom config file
# example: make NODES=10 F=8 launch-custom-config
# Prior to running this target:
# * Generate a custom config with `make generate-config`
# * Set a desired leader strategy with `make set-leader-strategy`
# NOTE: not workind yet
launch-custom-config:
@rm -rf output.txt
./scripts/launch_with_X_nodes.sh ${NODES} ${F} ${CURTIME}

test-unit:
@clear
@cargo test
Expand Down
16 changes: 16 additions & 0 deletions scripts/set_leader_strategy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/bash
# This script is used to set the leader strategy in config file.
# Usage: ./set_leader_strategy.sh <strategy>
# <strategy> can be one of the following:
# 1. "leader" (corresponds to honest behavior)
# 2. "leader_null_proposal" (corresponds to malicious behavior: no proposal send out during genesis stage)

strategy=$1
valid_strategies=("leader" "leader_null_proposal")

if [[ ! " ${valid_strategies[@]} " =~ " ${strategy} " ]]; then
echo -e "\033[31mERROR:\033[0m Invalid strategy: got $strategy, expected one of [${valid_strategies[@]}]" >&2
exit 1
fi

awk 'NR==1 { $2="'"$strategy"'" } 1' config.txt > tmp.txt && mv tmp.txt config.txt
6 changes: 6 additions & 0 deletions src/communication.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ pub mod exported;
pub enum Mode {
LEADER,
FOLLOWER,
ByzantineLeader(LeaderByzantine),
}

#[derive(Debug, PartialEq, Clone, Copy)]
pub enum LeaderByzantine {
NULLPROPOSAL,
}

// Communication contains keypair used when signing messages and a configuration struct
Expand Down
5 changes: 3 additions & 2 deletions src/communication/config.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::communication::LeaderByzantine;
use crate::communication::peer::new_peer;
use crate::utils::fs::{parse_mode, parse_config_lines};

Expand Down Expand Up @@ -137,12 +138,12 @@ impl Config {
}

pub fn get_stage_leader(&self) -> Option<Peer> {
if self.mode() == Mode::LEADER {
if self.mode() != Mode::FOLLOWER {
return None
}

for peer in &self.peers {
if peer.mode.unwrap() == Mode::LEADER {
if peer.mode.unwrap() == Mode::LEADER || peer.mode.unwrap() == Mode::ByzantineLeader(LeaderByzantine::NULLPROPOSAL) {
return Some(peer.clone())
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/communication/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ impl Display for Value {
match self {
Value::Zero => write!(f, "0"),
Value::One => write!(f, "1"),
Value::DEFAULT => write!(f, "default"),
Value::DEFAULT => write!(f, "NULL"),
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/communication/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ impl communication::Communication {
let num_peers: usize = self.config.peers.len();

// run thread that waits for connections from other nodes
thread::spawn(move || {
let listening_handle = thread::spawn(move || {
let streams: Result<Vec<TcpStream>, Error> = Communication::bind_and_wait_connection(listen_socket, num_peers.try_into().unwrap());
match streams {
Ok(streams) => {
Expand All @@ -56,7 +56,7 @@ impl communication::Communication {
});

// run thread that connect to other nodes
thread::spawn(move || {
let writing_handle = thread::spawn(move || {
let streams: Result<Vec<TcpStream>, Error> = Communication::connect_until_success(&peers);
match streams {
Ok(streams) => {
Expand All @@ -70,7 +70,6 @@ impl communication::Communication {
}
}
});
// println!("Connection threads are running!");

for received in rx {
if received.s_type == StreamType::LISTEN {
Expand All @@ -84,7 +83,8 @@ impl communication::Communication {
}
}

// println!("All connections established!")
listening_handle.join().unwrap();
writing_handle.join().unwrap();
}

// bind_and_wait_Config binds a listening port of this node and waits for other peers to connect to this port
Expand Down
7 changes: 7 additions & 0 deletions src/consensus/genesis.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use crate::communication::{LeaderByzantine, self};
use crate::communication::Mode::{LEADER, FOLLOWER};
use crate::consensus::genesis::strategy::{follower::FollowerStrategy, leader::LeaderStrategy};

use self::strategy::byzantine_leader::NullProposalStrategy;

use super::ConsensusNode;

pub mod strategy;
Expand All @@ -15,6 +18,10 @@ impl ConsensusNode<'_> {
FOLLOWER => {
self.set_genesis_strategy(&FollowerStrategy);
}

communication::Mode::ByzantineLeader(LeaderByzantine::NULLPROPOSAL) => {
self.set_genesis_strategy(&NullProposalStrategy)
}
}
}
}
1 change: 1 addition & 0 deletions src/consensus/genesis/strategy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::consensus::ConsensusNode;

pub mod follower;
pub mod leader;
pub mod byzantine_leader;

pub trait GenesisStrategy {
// genesis_stage is the logic for the genesis stage of the consensus protocol taken by a node depending on it's mode.
Expand Down
12 changes: 12 additions & 0 deletions src/consensus/genesis/strategy/byzantine_leader.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use std::process::exit;

use crate::consensus::ConsensusNode;

use super::GenesisStrategy;

pub struct NullProposalStrategy;

impl GenesisStrategy for NullProposalStrategy {
// no proposal is sent by the leader
fn genesis_stage(&self, mut _self_node: ConsensusNode) { exit(0) }
}
12 changes: 10 additions & 2 deletions src/consensus/genesis/strategy/follower.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use std::process::exit;

use super::GenesisStrategy;
use crate::{consensus::{ConsensusNode, protocol::convincing::ConsensusMsgReceivedTuple}, communication::message::ConsensusMsg};
use crate::{consensus::{ConsensusNode, protocol::convincing::ConsensusMsgReceivedTuple}, communication::message::{ConsensusMsg, Value, types::consensus::ConsensusMsgReceived}};

pub struct FollowerStrategy;

Expand All @@ -10,7 +12,13 @@ impl GenesisStrategy for FollowerStrategy {
if self_node.self_is_leader { panic!("leader node has follower's strategy") } // sanity check

let stage_leader = self_node.stage_leader.unwrap();
let proposal = self_node.receive_consensus_message(&stage_leader).expect("No messages received in first stage");
let proposal: ConsensusMsgReceived = self_node.receive_consensus_message(&stage_leader).unwrap_or_else(
|_| {
self_node.halt(Value::DEFAULT);
exit(0)
}
);

// println!("Received messages during genesis: {:?}", proposal.clone());

let tuple_proposal = vec![ConsensusMsgReceivedTuple(&stage_leader, Some(proposal))];
Expand Down
9 changes: 2 additions & 7 deletions src/consensus/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::net::TcpStream;
use std::process::exit;
use fs2::FileExt;
use std::io::Write;
use std::io::{Read, Error, ErrorKind};
use std::io::{Read, Error};
use crate::communication::message::serde::deserealize;
use crate::communication::message::types::consensus::ConsensusMsgReceived;
use crate::communication::peer::Peer;
Expand Down Expand Up @@ -71,11 +71,6 @@ impl ConsensusNode<'_> {
// println!("Reading {} bytes from {:?}", current_msg_size, peer.socket);

match stream.read_exact(&mut buf) {
// if timeout, return Err
Err(e) if e.kind() == ErrorKind::TimedOut => {
return Err(MessageError::ErrReadExact{ e })
},

Err(e) => {
let e = Error::new(
std::io::ErrorKind::Other,
Expand Down Expand Up @@ -127,7 +122,7 @@ impl ConsensusNode<'_> {
}

// halt stops the node and returns a final decision
fn halt(self, decision: Value) {
pub fn halt(&self, decision: Value) {
// Open or create the file
let mut output_file = OpenOptions::new()
.read(true)
Expand Down
3 changes: 2 additions & 1 deletion src/utils/fs.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::fs::read_to_string;
use std::net::SocketAddr;

use crate::communication::{peer, Mode};
use crate::communication::{peer, Mode, LeaderByzantine};

fn parse_peers(config_lines: &mut Vec<Vec<String>>, config_index: i32) -> Vec<peer::Peer> {
let mut result: Vec<peer::Peer> = Vec::new();
Expand Down Expand Up @@ -35,6 +35,7 @@ pub fn parse_mode(config_lines: Vec<Vec<String>>, config_index: i32) -> Mode {
match mode {
"leader" => Mode::LEADER,
"follower" => Mode::FOLLOWER,
"leader_null_proposal" => Mode::ByzantineLeader(LeaderByzantine::NULLPROPOSAL),
_ => panic!("Invalid mode {mode}, should be either `follower` or `leader`")
}
}
Expand Down

0 comments on commit 932c5ce

Please sign in to comment.