protocol changes

pull/247/head
larteyoh 2 months ago
parent 03dcfbd70e
commit 2ffc105beb

@ -95,6 +95,7 @@ std::vector<uint8_t> neroshop::msgpack::process(const std::vector<uint8_t>& requ
// Check if the queried node has peers for the requested infohash
std::vector<Peer> peers = node.get_providers(data_hash);
if(peers.empty()) {
// WARNING!!! THIS CODE BLOCKS THE GUI.
// If the queried node has no peers for the requested infohash,
// return the K closest nodes in the routing table to the requested infohash
std::vector<Node*> closest_nodes = node.find_node(data_hash, NEROSHOP_DHT_MAX_CLOSEST_NODES);
@ -121,10 +122,6 @@ std::vector<uint8_t> neroshop::msgpack::process(const std::vector<uint8_t>& requ
}
response_object["response"]["peers"] = peers_array; // If the queried node has peers for the infohash, they are returned in a key "values" as a list of strings. Each string containing "compact" format peer information for a single peer
}
// Generate and include a token value in the response
auto secret = generate_secret(16);
std::string token = generate_token(node.get_id(), data_hash, secret); // The reason for concatenating the node ID and info hash is to ensure that the generated token is unique and specific to the peer making the request. This helps prevent replay attacks, where an attacker intercepts and reuses a token generated for another peer.
response_object["response"]["token"] = token;
}
//-----------------------------------------------------
if(method == "get") {
@ -143,6 +140,7 @@ std::vector<uint8_t> neroshop::msgpack::process(const std::vector<uint8_t>& requ
response_object["response"]["id"] = node.get_id();
response_object["response"]["value"] = value;
} else {
// WARNING!!! THIS CODE BLOCKS THE GUI.
// If node does not have the key, check the closest nodes to see if they have it
std::vector<Node*> closest_nodes = node.find_node(key, NEROSHOP_DHT_MAX_CLOSEST_NODES);
@ -208,60 +206,29 @@ std::vector<uint8_t> neroshop::msgpack::process(const std::vector<uint8_t>& requ
}
}
//-----------------------------------------------------
if(method == "put") {
// If ipc_mode is false, it means the "put" message is being processed from other nodes. In this case, the key-value pair is stored in the node's own key-value store using the node.store(key, value) function.
if(ipc_mode == false) { // For Processing Put Requests from Other Nodes:
assert(request_object["args"].is_object());
auto params_object = request_object["args"];
assert(params_object["key"].is_string());
std::string key = params_object["key"];
assert(params_object["value"].is_string());
std::string value = params_object["value"];
// Add the key-value pair to the key-value store
code = (node.store(key, value) == false)
? static_cast<int>(KadResultCode::StoreFailed)
: static_cast<int>(KadResultCode::Success);
if(code == 0) {
// Map keys to search terms for efficient search operations
node.map(key, value);
}
if(method == "put" && ipc_mode == false) { // For Processing Put Requests from Other Nodes - If ipc_mode is false, it means the "put" message is being processed from other nodes. In this case, the key-value pair is stored in the node's own key-value store using the node.store(key, value) function.
assert(request_object["args"].is_object());
auto params_object = request_object["args"];
assert(params_object["key"].is_string());
std::string key = params_object["key"];
assert(params_object["value"].is_string());
std::string value = params_object["value"];
// Return success response // TODO: error reply
response_object["version"] = std::string(NEROSHOP_DHT_VERSION);
response_object["response"]["id"] = node.get_id();
response_object["response"]["code"] = code;
response_object["response"]["message"] = (code != 0) ? "Store failed" : "Success";
} else { // For Sending Put Requests to Other Nodes
// On the other hand, if ipc_mode is true, it means the "put" message is being sent from the local IPC client. In this case, the node.send_put(key, value) function is called to send the put message to the closest nodes in the routing table. Additionally, you can add a line of code to store the key-value pair in the local node's own hash table as well
assert(request_object["args"].is_object());
auto params_object = request_object["args"];
assert(params_object["key"].is_string());
std::string key = params_object["key"];
assert(params_object["value"].is_string());
std::string value = params_object["value"];
// Send put messages to the closest nodes in your routing table (IPC mode)
int put_messages_sent = node.send_put(key, value);
code = (put_messages_sent <= 0)
? static_cast<int>(KadResultCode::StoreFailed)
: static_cast<int>(KadResultCode::Success);
std::cout << "Number of nodes you've sent a put message to: " << put_messages_sent << "\n";
// Store the key-value pair in your own node as well
if(node.store(key, value)) {
// Add the key-value pair to the key-value store
code = (node.store(key, value) == false)
? static_cast<int>(KadResultCode::StoreFailed)
: static_cast<int>(KadResultCode::Success);
// Map keys to search terms for efficient search operations
node.map(key, value);
}
// Return success response
response_object["version"] = std::string(NEROSHOP_DHT_VERSION);
response_object["response"]["id"] = node.get_id();
response_object["response"]["code"] = (code != 0) ? static_cast<int>(KadResultCode::StorePartial) : code;
response_object["response"]["message"] = (code != 0) ? "Store failed" : "Success";
if(code == 0) {
// Map keys to search terms for efficient search operations
node.map(key, value);
}
// Return success response // TODO: error reply
response_object["version"] = std::string(NEROSHOP_DHT_VERSION);
response_object["response"]["id"] = node.get_id();
response_object["response"]["code"] = code;
response_object["response"]["message"] = (code != 0) ? "Store failed" : "Success";
}
//-----------------------------------------------------
if(method == "map") {
@ -284,7 +251,7 @@ std::vector<uint8_t> neroshop::msgpack::process(const std::vector<uint8_t>& requ
response_object["response"]["id"] = node.get_id();
}
//-----------------------------------------------------
if(method == "set" && ipc_mode) { // modify/update data
if((method == "set" || method == "put") && ipc_mode) { // For Sending Put Requests to Other Nodes - If ipc_mode is true, it means the "put" message is being sent from the local IPC client. In this case, the node.send_put(key, value) function is called to send the put message to the closest nodes in the routing table. Additionally, you can add a line of code to store the key-value pair in the local node's own hash table as well
assert(request_object["args"].is_object());
auto params_object = request_object["args"];
assert(params_object["key"].is_string());

@ -207,6 +207,7 @@ std::string neroshop::Node::generate_node_id(const std::string& address, int por
// Define the list of bootstrap nodes
std::vector<neroshop::Peer> bootstrap_nodes = {
{"node.neroshop.org", NEROSHOP_P2P_DEFAULT_PORT},
////{"127.0.0.1", NEROSHOP_P2P_DEFAULT_PORT}, // Uncomment to test on local network (you are allowed to create multiple separate daemon instances too)
};
void neroshop::Node::join() {
@ -446,7 +447,7 @@ std::vector<neroshop::Peer> neroshop::Node::get_providers(const std::string& dat
//-------------------------------------------------------------------------------------
void neroshop::Node::persist_routing_table(const std::string& address, int port) {
if(!is_bootstrap_node()) return; // Regular nodes cannot run this function
if(!is_hardcoded()) return; // Regular nodes cannot run this function
db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
@ -463,7 +464,7 @@ void neroshop::Node::persist_routing_table(const std::string& address, int port)
}
void neroshop::Node::rebuild_routing_table() {
if(!is_bootstrap_node()) return; // Regular nodes cannot run this function
if(!is_hardcoded()) return; // Regular nodes cannot run this function
db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
@ -499,7 +500,7 @@ void neroshop::Node::rebuild_routing_table() {
}
auto node = std::make_unique<Node>(ip_address, port, false);
if(!node->is_bootstrap_node()) {
if(!node->is_hardcoded()) {
routing_table->add_node(std::move(node));
}
}
@ -1131,7 +1132,7 @@ void neroshop::Node::periodic_check() {
uint16_t node_port = node->get_port();
// Skip the bootstrap nodes from the periodic checks
if (node->is_bootstrap_node()) continue;
if (node->is_hardcoded()) continue;
std::cout << "Performing periodic check on \033[34m" << node_ip << ":" << node_port << "\033[0m\n";
@ -1216,7 +1217,7 @@ void neroshop::Node::on_ping(const std::vector<uint8_t>& buffer, const struct so
bool node_exists = routing_table->has_node((sender_ip == "127.0.0.1") ? this->public_ip_address : sender_ip, sender_port);
if (!node_exists) {
auto node_that_pinged = std::make_unique<Node>((sender_ip == "127.0.0.1") ? this->public_ip_address : sender_ip, sender_port, false);
if(!node_that_pinged->is_bootstrap_node()) { // To prevent the bootstrap node from being stored in the routing table
if(!node_that_pinged->is_hardcoded()) { // To prevent the bootstrap node from being stored in the routing table
routing_table->add_node(std::move(node_that_pinged)); // Already has internal write_lock
persist_routing_table((sender_ip == "127.0.0.1") ? this->public_ip_address : sender_ip, sender_port);
routing_table->print_table();
@ -1260,7 +1261,7 @@ void neroshop::Node::on_map(const std::vector<uint8_t>& buffer, const struct soc
uint16_t routing_table_node_port = routing_table_node->get_port();
// Skip the bootstrap nodes (as they are not involved in the replacement of dead nodes)
if (routing_table_node->is_bootstrap_node()) continue;
if (routing_table_node->is_hardcoded()) continue;
// Send find_node to make up for dead nodes
auto nodes = send_find_node(dead_node_id, routing_table_node_ip, routing_table_node_port);
@ -1578,8 +1579,25 @@ bool neroshop::Node::is_hardcoded(const std::string& address, uint16_t port) {
return false;
}
bool neroshop::Node::is_hardcoded() const {
for (const auto& bootstrap_node : bootstrap_nodes) {
auto bootstrap_node_ip = neroshop::ip::resolve(bootstrap_node.address);
if (bootstrap_node_ip == this->public_ip_address
&& bootstrap_node.port == this->get_port()) {
return true;
}
if((bootstrap_node_ip == "127.0.0.1" || bootstrap_node_ip == "0.0.0.0")
&& bootstrap_node.port == NEROSHOP_P2P_DEFAULT_PORT
&& bootstrap_node.port == this->get_port()) { // For testing on localhost. Remove this soon!!
return true;
}
}
return false;
}
bool neroshop::Node::is_bootstrap_node() const {
return (bootstrap == true) || is_hardcoded(this->public_ip_address, this->get_port());
return (bootstrap == true) || is_hardcoded();
}
bool neroshop::Node::is_dead() const {

@ -131,6 +131,7 @@ public:
void set_bootstrap(bool bootstrap);
bool is_bootstrap_node() const;
bool is_hardcoded() const;
static bool is_hardcoded(const std::string& address, uint16_t port);
bool has_key(const std::string& key) const;
bool has_value(const std::string& value) const;

@ -14,6 +14,7 @@
#include "../core/tools/logger.hpp"
#include "../core/version.hpp"
#include "../core/tools/timer.hpp"
#include "../core/tools/filesystem.hpp"
#include <cxxopts.hpp>
@ -212,6 +213,14 @@ int main(int argc, char** argv)
ip_address = NEROSHOP_ANY_ADDRESS;
}
//-------------------------------------------------------
// create "datastore" folder within "~/.config/neroshop/" path (to prevent sqlite3_open: out of memory error)
std::string data_dir = NEROSHOP_DEFAULT_DATABASE_PATH;
if(!neroshop::filesystem::is_directory(data_dir)) {
if(!neroshop::filesystem::make_directory(data_dir)) {
throw std::runtime_error("Failed to create neroshop data dir");
}
}
//-------------------------------------------------------
neroshop::Node node("0.0.0.0"/*ip_address*/, NEROSHOP_P2P_DEFAULT_PORT, true);
if(result.count("bootstrap")) {

Loading…
Cancel
Save