Skip to content
This repository has been archived by the owner on Nov 18, 2021. It is now read-only.

Commit

Permalink
Bootstrap v3 Support (#2406)
Browse files Browse the repository at this point in the history
  • Loading branch information
ejfitzgerald authored Feb 7, 2020
1 parent 4d0282c commit b709784
Show file tree
Hide file tree
Showing 7 changed files with 280 additions and 36 deletions.
179 changes: 165 additions & 14 deletions apps/constellation/bootstrap_monitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,20 @@
namespace fetch {
namespace {

using variant::Variant;
using variant::Extract;
using network::Uri;
using http::JsonClient;
using byte_array::ConstByteArray;

char const * BOOTSTRAP_HOST = "https://bootstrap.fetch.ai";
using StringSet = std::unordered_set<ConstByteArray>;

char const * BOOTSTRAP_HOST = "http://127.0.0.1:8000";
const std::chrono::seconds UPDATE_INTERVAL{30};
constexpr char const * LOGGING_NAME = "bootstrap";

StringSet const VALID_PARAMETERS{"-block-interval", "-lanes", "-slices",
"-experimental", "-aeon-period", "-pos"};

/**
* Helper object containing all the fields required to make the attestation
*/
Expand Down Expand Up @@ -104,6 +108,9 @@ http::JsonClient::Headers BuildHeaders(std::string const &token)
headers["Authorization"] = "Token " + token;
}

// signal that we want to have the V2 response from the server
headers["Accept"] = "application/vnd.fetch.bootstrap.v2+json";

return headers;
}

Expand Down Expand Up @@ -133,7 +140,7 @@ BootstrapMonitor::BootstrapMonitor(ProverPtr entity, uint16_t p2p_port, std::str
state_machine_->RegisterHandler(State::Notify, this, &BootstrapMonitor::OnNotify);
}

bool BootstrapMonitor::DiscoverPeers(UriSet &peers, std::string const &external_address)
bool BootstrapMonitor::DiscoverPeers(DiscoveryResult &output, std::string const &external_address)
{
FETCH_LOG_INFO(LOGGING_NAME, "Bootstrapping network node @ ", BOOTSTRAP_HOST);

Expand All @@ -149,7 +156,7 @@ bool BootstrapMonitor::DiscoverPeers(UriSet &peers, std::string const &external_
}

// request the peers list
if (!RunDiscovery(peers))
if (!RunDiscovery(output))
{
FETCH_LOG_WARN(LOGGING_NAME, "Failed to discover initial peers from the bootstrap server");
return false;
Expand Down Expand Up @@ -191,10 +198,8 @@ bool BootstrapMonitor::UpdateExternalAddress()
return success;
}

bool BootstrapMonitor::RunDiscovery(UriSet &peers)
bool BootstrapMonitor::RunDiscovery(DiscoveryResult &output)
{
bool success{false};

// make the response
std::ostringstream oss;
oss << "/discovery/";
Expand Down Expand Up @@ -276,20 +281,98 @@ bool BootstrapMonitor::RunDiscovery(UriSet &peers)
}

auto const &result = response["result"];
if (!result.IsArray())

// payload detection
if (result.IsArray())
{
FETCH_LOG_WARN(LOGGING_NAME, "Malformed response from bootstrap server (result not array)");
FETCH_LOG_WARN(LOGGING_NAME, "Server Response: ", response);
if (!ParseDiscoveryV1(result, output))
{
FETCH_LOG_WARN(LOGGING_NAME,
"Malformed response from bootstrap server (unable to parse v1 response)");
return false;
}
}
else if (result.IsObject())
{
// parse the version number from the field
int64_t version{0};
if (!Extract(result, "version", version))
{
FETCH_LOG_WARN(LOGGING_NAME, "Malformed response from bootstrap server (no version field)");
return false;
}

if (version == 2)
{
if (!ParseDiscoveryV2(result, output))
{
FETCH_LOG_WARN(LOGGING_NAME,
"Malformed response from bootstrap server (can't parse V2 response)");
return false;
}
}
else
{
FETCH_LOG_WARN(LOGGING_NAME, "Malformed response from bootstrap server (version ", version,
" not supported)");
return false;
}
}
else
{
FETCH_LOG_WARN(LOGGING_NAME,
"Malformed response from bootstrap server (unable to identify payload)");
return false;
}

// assume all goes well
success = true;
return true;
}

bool BootstrapMonitor::ParseDiscoveryV1(Variant const &arr, DiscoveryResult &result)
{
return ParseNodeList(arr, result.uris);
}

bool BootstrapMonitor::ParseDiscoveryV2(Variant const &obj, DiscoveryResult &result)
{
if (!obj.IsObject())
{
return false;
}

bool const all_fields_present = obj.Has("genesis") && obj.Has("nodes");
if (!all_fields_present)
{
return false;
}

auto const &genesis = obj["genesis"];
auto const &nodes = obj["nodes"];

bool all_sub_fields_present =
nodes.IsArray() && genesis.IsObject() && genesis.Has("contents") && genesis.Has("parameters");
if (!all_sub_fields_present)
{
return false;
}

return ParseNodeList(nodes, result.uris) &&
ParseGenesisConfiguration(genesis["contents"], result.genesis) &&
ParseConfigurationUpdates(genesis["parameters"], result.config_updates);
}

bool BootstrapMonitor::ParseNodeList(Variant const &arr, UriSet &peers)
{
if (!arr.IsArray())
{
return false;
}

// loop through all the results
for (std::size_t i = 0, size = result.size(); i < size; ++i)
for (std::size_t i = 0, size = arr.size(); i < size; ++i)
{
auto const &peer_object = result[i];
auto const &peer_object = arr[i];

// formatting is correct check
if (!peer_object.IsObject())
Expand All @@ -303,7 +386,6 @@ bool BootstrapMonitor::RunDiscovery(UriSet &peers)
if (!(Extract(peer_object, "host", host) && Extract(peer_object, "port", port)))
{
FETCH_LOG_WARN(LOGGING_NAME, "Malformed response from bootstrap server (no host, no port)");
FETCH_LOG_WARN(LOGGING_NAME, "Server Response: ", response);
return false;
}

Expand All @@ -319,6 +401,75 @@ bool BootstrapMonitor::RunDiscovery(UriSet &peers)
peers.emplace(std::move(uri));
}

return true;
}

bool BootstrapMonitor::ParseGenesisConfiguration(Variant const &obj, std::string &genesis)
{
if (!obj.IsObject())
{
return false;
}

try
{
std::ostringstream oss;
oss << obj;

genesis = oss.str();
}
catch (std::exception const &ex)
{
FETCH_LOG_WARN(LOGGING_NAME, "Failed to process genesis configuration: ", ex.what());
return false;
}

return true;
}

bool BootstrapMonitor::ParseConfigurationUpdates(Variant const &obj, ConfigUpdates &updates)
{
bool success{false};

if (obj.IsObject())
{
success = true;

obj.IterateObject([&updates, &success](ConstByteArray const &key, Variant const &value) {
// the type of the value must be a string to be valid
if (!value.IsString())
{
success = false;
return false;
}

// the key of the parameter must be a part of the valid set
if (VALID_PARAMETERS.find(key) == VALID_PARAMETERS.end())
{
success = false;
return false;
}

// add the value to the configuration updates
updates.emplace(key, value.As<std::string>());

return true;
});
}
else if (obj.IsNull())
{
// the configuration updates can be null to signal that no updates are required
success = true;
}

// ensure if we are not succesful that any partial updates are not stored in the output variable
if (!success)
{
FETCH_LOG_WARN(LOGGING_NAME,
"Failed to parse configuration updates section of bootstrap config");
updates.clear();
}

return success;
}

Expand Down
30 changes: 22 additions & 8 deletions apps/constellation/bootstrap_monitor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,18 @@ namespace fetch {
class BootstrapMonitor
{
public:
using UriSet = constellation::Constellation::UriSet;
using Prover = crypto::Prover;
using ProverPtr = std::shared_ptr<Prover>;
using Identity = crypto::Identity;
using UriSet = constellation::Constellation::UriSet;
using Prover = crypto::Prover;
using ProverPtr = std::shared_ptr<Prover>;
using Identity = crypto::Identity;
using ConfigUpdates = std::unordered_map<std::string, std::string>;

struct DiscoveryResult
{
UriSet uris{};
std::string genesis{};
ConfigUpdates config_updates{};
};

// Construction / Destruction
BootstrapMonitor(ProverPtr entity, uint16_t p2p_port, std::string network_name, bool discoverable,
Expand All @@ -53,7 +61,7 @@ class BootstrapMonitor
BootstrapMonitor(BootstrapMonitor &&) = delete;
~BootstrapMonitor() = default;

bool DiscoverPeers(UriSet &peers, std::string const &external_address);
bool DiscoverPeers(DiscoveryResult &output, std::string const &external_address);

std::string const &external_address() const;

Expand All @@ -69,6 +77,7 @@ class BootstrapMonitor
Notify,
};

using Variant = variant::Variant;
using ConstByteArray = byte_array::ConstByteArray;
using StateMachine = core::StateMachine<State>;
using StateMachinePtr = std::shared_ptr<StateMachine>;
Expand All @@ -80,9 +89,14 @@ class BootstrapMonitor

/// @name Actions
/// @{
bool UpdateExternalAddress();
bool RunDiscovery(UriSet &peers);
bool NotifyNode();
bool UpdateExternalAddress();
bool RunDiscovery(DiscoveryResult &output);
static bool ParseDiscoveryV1(Variant const &arr, DiscoveryResult &result);
static bool ParseDiscoveryV2(Variant const &obj, DiscoveryResult &result);
static bool ParseNodeList(Variant const &arr, UriSet &peers);
static bool ParseGenesisConfiguration(Variant const &obj, std::string &genesis);
static bool ParseConfigurationUpdates(Variant const &obj, ConfigUpdates &updates);
bool NotifyNode();
/// @}

static char const *ToString(State state);
Expand Down
Loading

0 comments on commit b709784

Please sign in to comment.