2019-03-19 04:30:04 +01:00
|
|
|
#include "swarm.h"
|
|
|
|
#include "http_connection.h"
|
|
|
|
|
|
|
|
#include "service_node.h"
|
|
|
|
|
|
|
|
#include <boost/log/trivial.hpp>
|
2019-03-28 06:56:20 +01:00
|
|
|
#include <stdlib.h>
|
2019-03-19 04:30:04 +01:00
|
|
|
|
|
|
|
namespace loki {
|
|
|
|
|
2019-03-19 04:47:55 +01:00
|
|
|
static bool swarm_exists(const all_swarms_t& all_swarms,
|
|
|
|
const swarm_id_t& swarm) {
|
2019-03-19 04:30:04 +01:00
|
|
|
|
2019-03-19 04:47:55 +01:00
|
|
|
const auto it = std::find_if(
|
|
|
|
all_swarms.begin(), all_swarms.end(),
|
|
|
|
[&swarm](const SwarmInfo& si) { return si.swarm_id == swarm; });
|
2019-03-19 04:30:04 +01:00
|
|
|
|
|
|
|
return it != all_swarms.end();
|
|
|
|
}
|
|
|
|
|
|
|
|
Swarm::~Swarm() = default;
|
|
|
|
|
|
|
|
SwarmEvents Swarm::update_swarms(const all_swarms_t& swarms) {
|
|
|
|
|
|
|
|
/// TODO: need a fast check that the swarms are the same and exit early
|
|
|
|
|
|
|
|
SwarmEvents events = {};
|
|
|
|
|
|
|
|
/// Find us:
|
2019-04-08 04:02:14 +02:00
|
|
|
const auto our_swarm_it = std::find_if(
|
|
|
|
swarms.begin(), swarms.end(), [this](const SwarmInfo& swarm_info) {
|
|
|
|
const auto& snodes = swarm_info.snodes;
|
|
|
|
return std::find(snodes.begin(), snodes.end(), our_address_) !=
|
|
|
|
snodes.end();
|
|
|
|
});
|
|
|
|
|
|
|
|
if (our_swarm_it == swarms.end()) {
|
2019-03-19 04:30:04 +01:00
|
|
|
BOOST_LOG_TRIVIAL(error) << "ERROR: WE ARE NOT IN ANY SWARM";
|
|
|
|
return events;
|
|
|
|
}
|
|
|
|
|
2019-04-08 04:02:14 +02:00
|
|
|
const auto& our_swarm_snodes = our_swarm_it->snodes;
|
|
|
|
const auto our_swarm_id = our_swarm_it->swarm_id;
|
|
|
|
|
2019-04-10 02:25:47 +02:00
|
|
|
if (cur_swarm_id_ == UINT64_MAX) {
|
2019-03-19 04:30:04 +01:00
|
|
|
|
2019-03-19 04:47:55 +01:00
|
|
|
BOOST_LOG_TRIVIAL(info)
|
2019-04-08 04:02:14 +02:00
|
|
|
<< "EVENT: started SN in swarm: " << our_swarm_id;
|
2019-03-19 04:30:04 +01:00
|
|
|
|
|
|
|
} else {
|
|
|
|
|
2019-03-19 04:47:55 +01:00
|
|
|
/// Are we in a new swarm?
|
2019-04-08 04:02:14 +02:00
|
|
|
if (cur_swarm_id_ != our_swarm_id) {
|
2019-03-19 04:30:04 +01:00
|
|
|
|
2019-04-08 04:02:14 +02:00
|
|
|
BOOST_LOG_TRIVIAL(info)
|
|
|
|
<< "EVENT: got moved into a new swarm: " << our_swarm_id;
|
2019-03-19 04:30:04 +01:00
|
|
|
|
|
|
|
/// Check that our old swarm still exists
|
|
|
|
if (!swarm_exists(swarms, cur_swarm_id_)) {
|
|
|
|
|
2019-03-19 04:47:55 +01:00
|
|
|
BOOST_LOG_TRIVIAL(info)
|
|
|
|
<< "EVENT: our old swarm got DISSOLVED!";
|
2019-03-19 04:30:04 +01:00
|
|
|
events.decommissioned = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-21 06:51:03 +01:00
|
|
|
/// don't bother checking the rest
|
|
|
|
if (!events.decommissioned) {
|
2019-03-19 04:30:04 +01:00
|
|
|
|
2019-03-21 06:51:03 +01:00
|
|
|
/// See if anyone joined our swarm
|
2019-04-08 04:02:14 +02:00
|
|
|
for (const auto& sn : our_swarm_snodes) {
|
2019-03-19 04:30:04 +01:00
|
|
|
|
2019-04-08 04:02:14 +02:00
|
|
|
const auto it =
|
2019-03-25 00:35:21 +01:00
|
|
|
std::find(swarm_peers_.begin(), swarm_peers_.end(), sn);
|
2019-03-21 06:51:03 +01:00
|
|
|
|
2019-04-09 09:37:38 +02:00
|
|
|
if (it == swarm_peers_.end() && sn != our_address_) {
|
2019-04-08 07:28:20 +02:00
|
|
|
BOOST_LOG_TRIVIAL(info) << "EVENT: detected new SN: " << sn;
|
2019-03-21 06:51:03 +01:00
|
|
|
events.new_snodes.push_back(sn);
|
|
|
|
}
|
2019-03-19 04:30:04 +01:00
|
|
|
}
|
|
|
|
|
2019-03-21 06:51:03 +01:00
|
|
|
/// See if there are any new swarms
|
2019-03-19 04:30:04 +01:00
|
|
|
|
2019-03-21 06:51:03 +01:00
|
|
|
for (const auto& swarm_info : swarms) {
|
2019-03-19 04:30:04 +01:00
|
|
|
|
2019-04-08 04:02:14 +02:00
|
|
|
const bool found = std::any_of(
|
|
|
|
all_cur_swarms_.begin(), all_cur_swarms_.end(),
|
|
|
|
[&swarm_info](const SwarmInfo& cur_swarm_info) {
|
|
|
|
return cur_swarm_info.swarm_id == swarm_info.swarm_id;
|
|
|
|
});
|
2019-03-19 04:30:04 +01:00
|
|
|
|
2019-03-21 06:51:03 +01:00
|
|
|
if (!found) {
|
2019-03-25 00:35:21 +01:00
|
|
|
BOOST_LOG_TRIVIAL(info) << "EVENT: detected a new swarm: "
|
|
|
|
<< swarm_info.swarm_id;
|
2019-03-21 06:51:03 +01:00
|
|
|
events.new_swarms.push_back(swarm_info.swarm_id);
|
|
|
|
}
|
2019-03-19 04:30:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// NOTE: need to be careful and make sure we don't miss any
|
|
|
|
/// swarm update (e.g. if we don't update frequently enough)
|
|
|
|
|
2019-04-08 04:02:14 +02:00
|
|
|
cur_swarm_id_ = our_swarm_id;
|
2019-03-19 04:30:04 +01:00
|
|
|
all_cur_swarms_ = swarms;
|
2019-04-08 06:00:48 +02:00
|
|
|
|
|
|
|
swarm_peers_.clear();
|
|
|
|
std::copy_if(
|
|
|
|
our_swarm_snodes.begin(), our_swarm_snodes.end(),
|
|
|
|
std::back_inserter(swarm_peers_),
|
|
|
|
[this](const sn_record_t& record) { return record != our_address_; });
|
2019-03-19 04:30:04 +01:00
|
|
|
|
|
|
|
return events;
|
|
|
|
}
|
|
|
|
|
2019-03-29 04:53:50 +01:00
|
|
|
static uint64_t hex_to_u64(const std::string& pk) {
|
2019-03-19 04:30:04 +01:00
|
|
|
|
2019-03-28 06:56:20 +01:00
|
|
|
if (pk.size() != 66) {
|
|
|
|
throw std::invalid_argument("invalid pub key size");
|
|
|
|
}
|
|
|
|
|
2019-03-29 04:53:50 +01:00
|
|
|
/// Create a buffer for 16 characters null terminated
|
|
|
|
char buf[17] = {};
|
2019-03-28 06:56:20 +01:00
|
|
|
|
|
|
|
/// Note: pk is expected to contain two leading characters
|
|
|
|
/// (05 for the messenger) that do not participate in mapping
|
|
|
|
|
|
|
|
/// Note: if conversion is not possible, we will still
|
|
|
|
/// get a value in res (possibly 0 or UINT64_MAX), which
|
|
|
|
/// we are not handling at the moment
|
2019-03-29 04:53:50 +01:00
|
|
|
uint64_t res = 0;
|
|
|
|
for (auto it = pk.begin() + 2; it < pk.end(); it += 16) {
|
|
|
|
memcpy(buf, &(*it), 16);
|
|
|
|
res ^= strtoull(buf, nullptr, 16);
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2019-04-29 08:18:18 +02:00
|
|
|
bool Swarm::is_pubkey_for_us(const std::string& pk) const {
|
|
|
|
return cur_swarm_id_ == get_swarm_by_pk(all_cur_swarms_, pk);
|
2019-03-22 01:16:34 +01:00
|
|
|
}
|
|
|
|
|
2019-03-29 04:53:50 +01:00
|
|
|
swarm_id_t get_swarm_by_pk(const std::vector<SwarmInfo>& all_swarms,
|
|
|
|
const std::string& pk) {
|
|
|
|
|
|
|
|
const uint64_t res = hex_to_u64(pk);
|
2019-03-19 04:30:04 +01:00
|
|
|
|
2019-03-28 06:56:20 +01:00
|
|
|
/// We reserve UINT64_MAX as a sentinel swarm id for unassigned snodes
|
|
|
|
constexpr swarm_id_t MAX_ID = std::numeric_limits<uint64_t>::max() - 1;
|
|
|
|
constexpr swarm_id_t SENTINEL_ID = std::numeric_limits<uint64_t>::max();
|
2019-03-19 04:30:04 +01:00
|
|
|
|
2019-03-28 06:56:20 +01:00
|
|
|
swarm_id_t cur_best = SENTINEL_ID;
|
|
|
|
uint64_t cur_min = SENTINEL_ID;
|
2019-03-19 04:30:04 +01:00
|
|
|
|
2019-03-28 06:56:20 +01:00
|
|
|
/// We don't require that all_swarms is sorted, so we find
|
|
|
|
/// the smallest/largest elements in the same loop
|
|
|
|
swarm_id_t leftmost_id = SENTINEL_ID;
|
|
|
|
swarm_id_t rightmost_id = 0;
|
2019-03-19 04:30:04 +01:00
|
|
|
|
|
|
|
for (const auto& si : all_swarms) {
|
|
|
|
|
2019-03-19 04:47:55 +01:00
|
|
|
uint64_t dist =
|
|
|
|
(si.swarm_id > res) ? (si.swarm_id - res) : (res - si.swarm_id);
|
2019-03-19 04:30:04 +01:00
|
|
|
if (dist < cur_min) {
|
|
|
|
cur_best = si.swarm_id;
|
|
|
|
cur_min = dist;
|
|
|
|
}
|
|
|
|
|
2019-03-28 06:56:20 +01:00
|
|
|
/// Find the letfmost
|
|
|
|
if (si.swarm_id < leftmost_id) {
|
|
|
|
leftmost_id = si.swarm_id;
|
|
|
|
}
|
2019-03-19 04:30:04 +01:00
|
|
|
|
2019-03-28 06:56:20 +01:00
|
|
|
if (si.swarm_id > rightmost_id) {
|
|
|
|
rightmost_id = si.swarm_id;
|
|
|
|
}
|
|
|
|
}
|
2019-03-19 04:30:04 +01:00
|
|
|
|
2019-03-28 06:56:20 +01:00
|
|
|
// handle special case
|
2019-03-29 04:53:50 +01:00
|
|
|
if (res > rightmost_id) {
|
|
|
|
// since rightmost is at least as large as leftmost,
|
|
|
|
// res >= leftmost_id in this branch, so the value will
|
|
|
|
// not overflow; the same logic applies to the else branch
|
|
|
|
const uint64_t dist = (MAX_ID - res) + leftmost_id;
|
2019-03-28 06:56:20 +01:00
|
|
|
if (dist < cur_min) {
|
|
|
|
cur_best = leftmost_id;
|
|
|
|
}
|
2019-03-29 04:53:50 +01:00
|
|
|
} else if (res < leftmost_id) {
|
|
|
|
const uint64_t dist = res + (MAX_ID - rightmost_id);
|
2019-03-19 04:30:04 +01:00
|
|
|
if (dist < cur_min) {
|
2019-03-28 06:56:20 +01:00
|
|
|
cur_best = rightmost_id;
|
2019-03-19 04:30:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return cur_best;
|
|
|
|
}
|
|
|
|
|
2019-04-08 07:05:31 +02:00
|
|
|
const std::vector<sn_record_t>& Swarm::other_nodes() const {
|
|
|
|
return swarm_peers_;
|
2019-03-19 04:30:04 +01:00
|
|
|
}
|
|
|
|
|
2019-03-19 04:47:55 +01:00
|
|
|
} // namespace loki
|