Skip to content
This repository has been archived by the owner on Mar 3, 2020. It is now read-only.

Commit

Permalink
KEP-574: pbft configurations
Browse files Browse the repository at this point in the history
  • Loading branch information
paularchard committed Oct 19, 2018
1 parent 4a819c8 commit b78df46
Show file tree
Hide file tree
Showing 10 changed files with 717 additions and 29 deletions.
5 changes: 4 additions & 1 deletion pbft/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ add_library(pbft STATIC
pbft_operation.hpp
pbft_operation.cpp

pbft_configuration.hpp
pbft_configuration.cpp

dummy_pbft_service.cpp
dummy_pbft_service.hpp
pbft_service_base.hpp

pbft_failure_detector.cpp
pbft_failure_detector.hpp
pbft_failure_detector_base.hpp)
pbft_failure_detector_base.hpp pbft_configuration.hpp pbft_configuration.cpp test/pbft_configuration_test.cpp pbft_config_store.hpp pbft_config_store.cpp test/pbft_config_store_test.cpp)

target_link_libraries(pbft utils proto)
target_include_directories(pbft PRIVATE ${JSONCPP_INCLUDE_DIRS} ${PROTO_INCLUDE_DIR})
Expand Down
68 changes: 43 additions & 25 deletions pbft/pbft.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,26 +46,7 @@ pbft::pbft(
throw std::runtime_error("No peers found!");
}

// We cannot directly sort the peers list or a copy of it because peer addresses have const members
std::vector<peer_address_t> unordered_peers_list;
std::copy(peers.begin(), peers.end(), std::back_inserter(unordered_peers_list));

std::vector<size_t> indicies(peers.size());
std::iota(indicies.begin(), indicies.end(), 0);

std::sort(indicies.begin(), indicies.end(),
[&unordered_peers_list](const auto& i1, const auto& i2)
{
return unordered_peers_list[i1].uuid < unordered_peers_list[i2].uuid;
}
);

std::transform(indicies.begin(), indicies.end(), std::back_inserter(this->peer_index),
[&unordered_peers_list](auto& peer_index)
{
return unordered_peers_list[peer_index];
}
);
this->initialize_configuration(peers);

// TODO: stable checkpoint should be read from disk first: KEP-494
this->low_water_mark = this->stable_checkpoint.first;
Expand Down Expand Up @@ -313,7 +294,7 @@ pbft::broadcast(const bzn::encoded_message& msg)
{
auto msg_ptr = std::make_shared<bzn::encoded_message>(msg);

for (const auto& peer : this->peer_index)
for (const auto& peer : this->current_peers())
{
this->node->send_message_str(make_endpoint(peer), msg_ptr);
}
Expand Down Expand Up @@ -415,7 +396,7 @@ pbft::is_primary() const
const peer_address_t&
pbft::get_primary() const
{
return this->peer_index[this->view % this->peer_index.size()];
return this->current_peers()[this->view % this->current_peers().size()];
}

// Find this node's record of an operation (creating a new record for it if this is the first time we've heard of it)
Expand Down Expand Up @@ -443,7 +424,7 @@ pbft::find_operation(uint64_t view, uint64_t sequence, const pbft_request& reque
<< request.ShortDebugString();

std::shared_ptr<pbft_operation> op = std::make_shared<pbft_operation>(view, sequence, request,
std::make_shared<std::vector<peer_address_t>>(this->peer_index));
this->current_peers_ptr());
auto result = operations.emplace(std::piecewise_construct, std::forward_as_tuple(std::move(key)), std::forward_as_tuple(op));

assert(result.second);
Expand Down Expand Up @@ -645,7 +626,7 @@ pbft::quorum_size() const
size_t
pbft::max_faulty_nodes() const
{
return this->peer_index.size()/3;
return this->current_peers().size()/3;
}

void
Expand Down Expand Up @@ -730,7 +711,7 @@ pbft::get_status()
status["view"] = this->view;

status["peer_index"] = bzn::json_message();
for(const auto& p : this->peer_index)
for(const auto& p : this->current_peers())
{
bzn::json_message peer;
peer["host"] = p.host;
Expand All @@ -744,3 +725,40 @@ pbft::get_status()
return status;
}

bool
pbft::initialize_configuration(const bzn::peers_list_t& peers)
{
auto config = std::make_shared<pbft_configuration>();
bool config_good = true;
for (auto p : peers)
{
config_good &= config->add_peer(p);
}

if (!config_good)
{
LOG(warning) << "One or more peers could not be added to configuration";
}

this->configurations.add(config);
this->configurations.enable(config->get_hash());
this->configurations.set_current(config->get_hash());

return config_good;
}

std::shared_ptr<const std::vector<bzn::peer_address_t>>
pbft::current_peers_ptr() const
{
auto config = this->configurations.current();
if (config)
return config->get_peers();

throw std::runtime_error("No current configuration!");
}

const std::vector<bzn::peer_address_t>&
pbft::current_peers() const
{
return *(this->current_peers_ptr());
}
8 changes: 6 additions & 2 deletions pbft/pbft.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <pbft/pbft_base.hpp>
#include <pbft/pbft_failure_detector.hpp>
#include <pbft/pbft_service_base.hpp>
#include <pbft/pbft_config_store.hpp>
#include <status/status_provider_base.hpp>
#include <mutex>

Expand Down Expand Up @@ -123,6 +124,10 @@ namespace bzn
void clear_checkpoint_messages_until(const checkpoint_t&);
void clear_operations_until(const checkpoint_t&);

bool initialize_configuration(const bzn::peers_list_t& peers);
std::shared_ptr<const std::vector<bzn::peer_address_t>> current_peers_ptr() const;
const std::vector<bzn::peer_address_t>& current_peers() const;

// Using 1 as first value here to distinguish from default value of 0 in protobuf
uint64_t view = 1;
uint64_t next_issued_sequence_number = 1;
Expand All @@ -132,8 +137,6 @@ namespace bzn

std::shared_ptr<bzn::node_base> node;

std::vector<bzn::peer_address_t> peer_index;

const bzn::uuid_t uuid;
std::shared_ptr<pbft_service_base> service;

Expand All @@ -156,6 +159,7 @@ namespace bzn

std::set<checkpoint_t> local_unstable_checkpoints;
std::map<checkpoint_t, std::unordered_map<uuid_t, std::string>> unstable_checkpoint_proofs;
pbft_config_store configurations;
};

} // namespace bzn
Expand Down
104 changes: 104 additions & 0 deletions pbft/pbft_config_store.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Copyright (C) 2018 Bluzelle
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License, version 3,
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

#include <pbft/pbft_config_store.hpp>

using namespace bzn;

pbft_config_store::pbft_config_store()
: current_index(0), next_index(1)
{
}

bool
pbft_config_store::add(pbft_configuration::shared_const_ptr config)
{
// TODO - should we be making a copy here instead?
// currently the added config could be changed externally after being added
return (this->configs.insert(std::make_pair(this->next_index++, std::make_pair(config, false)))).second;
}

pbft_config_store::config_map::const_iterator
pbft_config_store::find_by_hash(hash_t hash) const
{
auto config = std::find_if(this->configs.begin(), this->configs.end(),
[hash](auto c)
{
return c.second.first->get_hash() == hash;
});

return config;
}

bool
pbft_config_store::set_current(const hash_t& hash)
{
auto config = this->find_by_hash(hash);
if (config == this->configs.end())
return false;

this->current_index = config->first;
return true;
}

bool
pbft_config_store::remove_prior_to(const hash_t& hash)
{
auto config = this->find_by_hash(hash);
if (config == this->configs.end())
return false;

this->configs.erase(this->configs.begin(), config);
return true;
}

pbft_configuration::shared_const_ptr
pbft_config_store::get(const hash_t& hash) const
{
auto config = this->find_by_hash(hash);
return config != this->configs.end() ? config->second.first : nullptr;
}

bool
pbft_config_store::enable(const hash_t& hash, bool val)
{
// can't find_by_hash here because we need a non-const
auto config = std::find_if(this->configs.begin(), this->configs.end(),
[hash](auto c)
{
return c.second.first->get_hash() == hash;
});

if (config != this->configs.end())
{
config->second.second = val;
return true;
}

return false;
}

bool
pbft_config_store::is_enabled(const hash_t& hash) const
{
auto config = this->find_by_hash(hash);
return config != this->configs.end() ? config->second.second : false;
}

pbft_configuration::shared_const_ptr
pbft_config_store::current() const
{
auto it = this->configs.find(this->current_index);
return it != this->configs.end() ? it->second.first : nullptr;
}
50 changes: 50 additions & 0 deletions pbft/pbft_config_store.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (C) 2018 Bluzelle
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License, version 3,
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

#pragma once

#include <pbft/pbft_configuration.hpp>
#include <map>

namespace bzn
{
using hash_t = std::string;

class pbft_config_store
{
public:
pbft_config_store();

bool add(pbft_configuration::shared_const_ptr config);
bool remove_prior_to(const hash_t& hash);
pbft_configuration::shared_const_ptr get(const hash_t& hash) const;

bool set_current(const hash_t& hash);
pbft_configuration::shared_const_ptr current() const;

bool enable(const hash_t& hash, bool val = true);
bool is_enabled(const hash_t& hash) const;

private:
using index_t = uint64_t;
using config_map = std::map<index_t, std::pair<pbft_configuration::shared_const_ptr, bool>>;
config_map::const_iterator find_by_hash(hash_t hash) const;

// a map from the config index to a pair of <config, is_config_enabled>
config_map configs;
index_t current_index;
index_t next_index;
};
}

0 comments on commit b78df46

Please sign in to comment.