protocol changes — add provider on map

pull/197/head
larteyoh 9 months ago
parent eb59f49530
commit 9ed138f91d

@ -57,14 +57,15 @@ https://user-images.githubusercontent.com/58671384/219222567-f170f728-be31-43d5-
## Feature Status
- [x] Distributed P2P network (urgently seeking assistance with I2P integration/NAT traversal!!!)
- [ ] Distributed P2P network (urgently seeking assistance with I2P integration/NAT traversal!!!)
- [ ] Buy and sell products and services with Monero
- [x] No KYC
- [x] No censorship (censorship-resistant)
- [ ] No listing fees, sales tax, or any other fees (except for miner transaction fees and shipping costs)
- there will be 0.5% fee for using one of the three payment options, specifically the 2-of-3 escrow system.
- there will be 0.5% fee for using one of the three payment options (specifically the 2-of-3 escrow system).
This is to incentivize arbitrators (who are chosen from sellers with the highest reputation) for disputing 2/3 escrow transactions.
- [x] Pseudonymous identities
- sellers and buyers are identified by their unique ids and/or optional display names
- sellers and buyers are identified by their unique id (monero primary address) and optional display name
- [x] End-to-end encrypted messaging system for communications between sellers and buyers
- generated RSA-4096 private keys will be used to decrypt messages.
- [ ] Subaddress generator for direct payments without an escrow

@ -76,7 +76,7 @@ Popup {
language: languageBox.currentText,//currentIndex,
hide_homepage_button: hideHomepageButtonSwitch.checked,
hide_price_display: priceDisplaySwitch.checked,
hide_wallet_sync_bar: walletSyncBarSwitch.checked,
hide_wallet_sync_bar_on_full: walletSyncBarSwitch.checked,
/*window_width: Script.getJsonRootObject()["window_width"],
window_height: Script.getJsonRootObject()["window_height"],
window_mode: Script.getJsonRootObject()["window_mode"],*/
@ -105,7 +105,7 @@ Popup {
balance_amount_precision: Number(balancePrecisionBox.currentText),
show_currency_sign: showCurrencySignSwitch.checked,
block_explorer: blockExplorerBox.currentText,//currentIndex,
require_password_on_withdrawal: requirePasswordOnWithdrawalSwitch.checked,
////require_password_on_withdrawal: requirePasswordOnWithdrawalSwitch.checked,
},
},
// proxy / privacy
@ -493,7 +493,7 @@ Popup {
id: walletSyncBarSwitch
anchors.right: parent.right; anchors.rightMargin: 5
//width: settingsStack.comboBoxWidth
checked: Script.getJsonRootObject()["hide_wallet_sync_bar"]//false
checked: Script.getJsonRootObject()["hide_wallet_sync_bar_on_full"]//false
radius: 13
backgroundCheckedColor: "#605185"
onToggled: settingsDialog.save()

@ -300,6 +300,8 @@ ApplicationWindow {
if(settingsDialog.hideWalletSyncBarOnFull && (moneroDaemonSyncBar.value >= 1.0)) {
moneroDaemonSyncBar.parent.visible = false
} else {
moneroDaemonSyncBar.parent.visible = true
}
}
}

@ -83,8 +83,8 @@ std::vector<uint8_t> neroshop::msgpack::process(const std::vector<uint8_t>& requ
}
}
//-----------------------------------------------------
if(method == "get_peers") {
std::cout << "message type is a get_peers\n"; //
if(method == "get_providers") {
std::cout << "message type is a get_providers\n"; //
assert(request_object["args"].is_object());
auto params_object = request_object["args"];
assert(params_object["info_hash"].is_string()); // info hash
@ -93,7 +93,7 @@ std::vector<uint8_t> neroshop::msgpack::process(const std::vector<uint8_t>& requ
response_object["version"] = std::string(NEROSHOP_DHT_VERSION);
response_object["response"]["id"] = node.get_id();
// Check if the queried node has peers for the requested infohash
std::vector<Peer> peers = node.get_peers(info_hash);
std::vector<Peer> peers = node.get_providers(info_hash);
if(peers.empty()) {
// If the queried node has no peers for the requested infohash,
// return the K closest nodes in the routing table to the requested infohash
@ -127,40 +127,6 @@ std::vector<uint8_t> neroshop::msgpack::process(const std::vector<uint8_t>& requ
response_object["response"]["token"] = token;
}
//-----------------------------------------------------
if(method == "announce_peer") {
std::cout << "message type is a announce_peer\n";
assert(request_object["args"].is_object());
auto params_object = request_object["args"];
assert(params_object["info_hash"].is_string()); // info hash
std::string info_hash = params_object["info_hash"];
assert(params_object["token"].is_string());
std::string token = params_object["token"];
assert(params_object["port"].is_number_integer());
int port = params_object["port"];
// Verify the token
std::string secret = generate_secret(16);
std::string expected_token = generate_token(node.get_id(), info_hash, secret);
if (token != expected_token) {
// Invalid token, return error response
code = static_cast<int>(KadResultCode::InvalidToken);
response_object["error"]["code"] = code;
response_object["error"]["message"] = "Invalid token";
response_object["tid"] = tid;
response = nlohmann::json::to_msgpack(response_object);
return response;
}
// Add the peer to the info_hash_peers unordered_map
node.add_peer(info_hash, {/*ip_address*/"", port});
// Return success response
response_object["version"] = std::string(NEROSHOP_DHT_VERSION);
response_object["response"]["id"] = node.get_id();
response_object["response"]["code"] = code;
response_object["response"]["message"] = "Peer announced"; // not needed
}
//-----------------------------------------------------
if(method == "get") {
if(ipc_mode == false) { // For Processing Get Requests from Other Nodes:
assert(request_object["args"].is_object());

@ -164,7 +164,7 @@ neroshop::Node::Node(Node&& other) noexcept
: id(std::move(other.id)),
version(std::move(other.version)),
data(std::move(other.data)),
info_hash_peers(std::move(other.info_hash_peers)),
providers(std::move(other.providers)),
//server(std::move(other.server)),
sockfd(other.sockfd),
sockin(std::move(other.sockin)),
@ -260,54 +260,6 @@ std::vector<neroshop::Node*> neroshop::Node::find_node(const std::string& target
return closest_nodes;
}
std::vector<neroshop::Peer> neroshop::Node::get_peers(const std::string& info_hash) const {
std::vector<Peer> peers = {};
// Check if info_hash is in info_hash_peers
auto info_hash_it = info_hash_peers.find(info_hash);
if (info_hash_it != info_hash_peers.end()) {
// If info_hash is in info_hash_peers, get the vector of peers
peers = info_hash_it->second;
} else {
std::vector<Node*> nodes = find_node(info_hash, NEROSHOP_DHT_MAX_CLOSEST_NODES);
for (Node* node : nodes) {
// Access the info_hash_peers map for each node and concatenate the vectors of peers
auto node_it = node->info_hash_peers.find(info_hash);
if (node_it != node->info_hash_peers.end()) {
const std::vector<Peer>& node_peers = node_it->second;
peers.insert(peers.end(), node_peers.begin(), node_peers.end());
}
}
}
return peers;
}
void neroshop::Node::announce_peer(const std::string& info_hash, int port, const std::string& token/*, bool implied_port*/) {
}
void neroshop::Node::add_peer(const std::string& info_hash, const Peer& peer) {
// Check if the info_hash is already in the info_hash_peers map
auto it = info_hash_peers.find(info_hash);
if (it != info_hash_peers.end()) {
// If the info_hash is already in the map, add the peer to the vector of peers
it->second.push_back(peer);
} else {
// If the info_hash is not in the map, create a new vector of peers and add the peer
info_hash_peers.emplace(info_hash, std::vector<Peer>{peer});
}
}
void neroshop::Node::remove_peer(const std::string& info_hash) {
// Find the info_hash entry in the info_hash_peers map
auto it = info_hash_peers.find(info_hash);
if (it != info_hash_peers.end()) {
// If the info_hash exists, remove the entry from the map
info_hash_peers.erase(it);
}
}
int neroshop::Node::put(const std::string& key, const std::string& value) {
if(!validate(key, value)) {
return false;
@ -419,6 +371,72 @@ int neroshop::Node::set(const std::string& key, const std::string& value) {
return has_key(key); // boolean
}
void neroshop::Node::add_provider(const std::string& data_hash, const Peer& peer) {
// Check if the data_hash is already in the providers map
auto it = providers.find(data_hash);
if (it != providers.end()) {
// If the data_hash is already in the map, check for duplicates
for (const auto& existing_peer : it->second) {
if (existing_peer.address == peer.address && existing_peer.port == peer.port) {
// Peer with the same address and port already exists, so don't add it again
std::cout << "Provider (\033[36m" << peer.address + ":" + std::to_string(peer.port) << "\033[0m) for hash (" << data_hash << ") already exists" << std::endl;
return;
}
}
// If the data_hash is already in the map, add the peer to the vector of peers
it->second.push_back(peer);
std::cout << "Provider (\033[36m" << peer.address + ":" + std::to_string(peer.port) << "\033[0m) for hash (" << data_hash << ") has been added" << std::endl;
} else {
// If the data_hash is not in the map, create a new vector of peers and add the peer
providers.emplace(data_hash, std::vector<Peer>{peer});
std::cout << "Provider (\033[36m" << peer.address + ":" + std::to_string(peer.port) << "\033[0m) for hash (" << data_hash << ") has been added (0)" << std::endl;
}
}
void neroshop::Node::remove_providers(const std::string& data_hash) {
// Find the data_hash entry in the providers map
auto it = providers.find(data_hash);
if (it != providers.end()) {
// If the data_hash exists, remove the entry from the map
providers.erase(it);
}
}
void neroshop::Node::remove_provider(const std::string& data_hash, const std::string& address, int port) {
// Find the data_hash entry in the providers map
auto it = providers.find(data_hash);
if (it != providers.end()) {
// Iterate through the vector of providers for the data_hash
auto& peers = it->second;
for (auto peer_it = peers.begin(); peer_it != peers.end(); ) {
if (peer_it->address == address && peer_it->port == port) {
// If the address and port match, remove the provider from the vector
peer_it = peers.erase(peer_it);
} else {
++peer_it;
}
}
// If the vector becomes empty after removing the provider, remove the entry from the map
if (peers.empty()) {
providers.erase(it);
}
}
}
std::vector<neroshop::Peer> neroshop::Node::get_providers(const std::string& data_hash) const {
std::vector<Peer> peers = {};
// Check if data_hash is in providers
auto data_hash_it = providers.find(data_hash);
if (data_hash_it != providers.end()) {
// If data_hash is in providers, get the vector of peers
peers = data_hash_it->second;
}
return peers;
}
//-------------------------------------------------------------------------------------
void neroshop::Node::persist_routing_table(const std::string& address, int port) {
@ -566,7 +584,10 @@ bool neroshop::Node::send_ping(const std::string& address, int port) {
query_object["tid"] = transaction_id;
query_object["query"] = "ping";
query_object["args"]["id"] = this->id;
query_object["args"]["ephemeral_port"] = get_port(); // for testing on local network. This cannot be removed since the two primary sockets used in the protocol have different ports with the "ephemeral_port" being the actual port
int my_port = get_port();
if(my_port != NEROSHOP_P2P_DEFAULT_PORT) {
query_object["args"]["port"] = my_port; // for testing on local network. This cannot be removed since the two primary sockets used in the protocol have different ports with the "port" being the actual port
}
query_object["version"] = std::string(NEROSHOP_DHT_VERSION);
auto ping_message = nlohmann::json::to_msgpack(query_object);
@ -639,22 +660,22 @@ std::vector<neroshop::Node*> neroshop::Node::send_find_node(const std::string& t
return nodes;
}
void neroshop::Node::send_get_peers(const std::string& info_hash) {
void neroshop::Node::send_get_providers(const std::string& data_hash) {
std::string transaction_id = msgpack::generate_transaction_id();
/*bencode_dict query;
query["t"] = transaction_id;
query["y"] = "q";
query["q"] = "get_peers";
query["q"] = "get_providers";
std::string get_peers_message = bencode::encode(query);*/
std::string get_providers_message = bencode::encode(query);*/
//-----------------------------------------------------
nlohmann::json query_object;
query_object["tid"] = transaction_id;
query_object["query"] = "get_peers";
query_object["query"] = "get_providers";
query_object["args"]["id"] = this->id;
query_object["args"]["info_hash"] = info_hash;
query_object["args"]["data_hash"] = data_hash;
query_object["version"] = std::string(NEROSHOP_DHT_VERSION);
//-----------------------------------------------
// socket sendto and recvfrom here
@ -667,43 +688,6 @@ void neroshop::Node::send_get_peers(const std::string& info_hash) {
// ...
}
void neroshop::Node::send_announce_peer(const std::string& info_hash, int port, const std::string& token) {
// announce_peer is called by a client to announce that it is downloading a file with a specific infohash and is now a potential peer for that file. This is typically called when a client starts downloading a file or completes downloading a file and wants to start seeding it.
// When a client calls announce_peer, it sends an announce_peer query to the DHT network, which contains the client's node ID, the infohash of the file, and the client's IP address and port number. If the query is successful, the DHT network will store the client's IP address and port number in the list of peers for the specified infohash.
// Other clients who are also downloading or seeding the same file can then query the DHT network for a list of peers for that infohash. The DHT network will respond with a list of IP addresses and port numbers of all the peers who have announced themselves for that infohash. The requesting client can then use this list to connect to other peers and start downloading or uploading data.
std::string transaction_id = msgpack::generate_transaction_id();
/*bencode_dict query;
query["t"] = transaction_id;
query["y"] = "q";
query["q"] = "announce_peer";
std::string announce_peer_message = bencode::encode(query);*/
//---------------------------------------------------------
nlohmann::json query_object;
query_object["tid"] = transaction_id;
query_object["query"] = "announce_peer";
query_object["args"]["id"] = this->id;
query_object["args"]["info_hash"] = info_hash;
query_object["args"]["port"] = port; // the port of the peer that is announcing itself
query_object["args"]["token"] = token;
query_object["args"]["implied_port"] = (port != 0);//implied_port; // optional // set to 1 if the port number is included in the peer list, and 0 otherwise. // refers to the port of the peer that is announcing itself, not the port of the node that receives the announcement // the value of implied_port should be set based on whether the port number is included in the peer list or not, and it should not be set to this->port.
query_object["version"] = std::string(NEROSHOP_DHT_VERSION);
//-----------------------------------------------
// send the query to the nodes in the routing table
//auto receive_buffer = send_query(address, port, _message);
//-----------------------------------------------
// process the response
// ...
//-----------------------------------------------
// get result of put request
// ...
}
void neroshop::Node::send_add_peer(const std::string& info_hash, const Peer& peer) {
}
int neroshop::Node::send_put(const std::string& key, const std::string& value) {
nlohmann::json query_object;
@ -914,6 +898,7 @@ void neroshop::Node::send_map(const std::string& address, int port) {
nlohmann::json query_object;
query_object["query"] = "map";
query_object["args"]["id"] = this->id;
query_object["args"]["port"] = get_port(); // the port of the peer that is announcing itself (map will also be used to "announce" the peer or provider)
query_object["version"] = std::string(NEROSHOP_DHT_VERSION);
bool map_sent = false;
@ -1117,7 +1102,8 @@ void neroshop::Node::on_ping(const std::vector<uint8_t>& buffer, const struct so
if (message.contains("query") && message["query"] == "ping") {
std::string sender_id = message["args"]["id"].get<std::string>();
std::string sender_ip = inet_ntoa(client_addr.sin_addr);
uint16_t sender_port = (message["args"].contains("ephemeral_port")) ? (uint16_t)message["args"]["ephemeral_port"] : ntohs(client_addr.sin_port);//NEROSHOP_P2P_DEFAULT_PORT;
uint16_t sender_port = (message["args"].contains("port")) ? (uint16_t)message["args"]["port"] : NEROSHOP_P2P_DEFAULT_PORT;
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);
@ -1135,6 +1121,22 @@ void neroshop::Node::on_ping(const std::vector<uint8_t>& buffer, const struct so
//-----------------------------------------------------------------------------
void neroshop::Node::on_map(const std::vector<uint8_t>& buffer, const struct sockaddr_in& client_addr) {
if (buffer.size() > 0) {
nlohmann::json message = nlohmann::json::from_msgpack(buffer);
if (message.contains("query") && message["query"] == "map") {
std::string sender_id = message["args"]["id"].get<std::string>();
std::string sender_ip = inet_ntoa(client_addr.sin_addr);
uint16_t sender_port = (message["args"].contains("port")) ? (uint16_t)message["args"]["port"] : NEROSHOP_P2P_DEFAULT_PORT;
std::string data_hash = message["args"]["key"].get<std::string>();
add_provider(data_hash, { (sender_ip == "127.0.0.1") ? this->public_ip_address : sender_ip, sender_port });
}
}
}
//-----------------------------------------------------------------------------
/*void neroshop::Node::on_dead_node(const std::vector<std::string>& node_ids) {
for (const auto& dead_node_id : node_ids) {
//std::cout << "Processing dead node ID: " << dead_node_id << std::endl;
@ -1220,6 +1222,9 @@ void neroshop::Node::run() {
// Add the node that pinged this node to the routing table
on_ping(buffer, client_addr);
// Add provider
on_map(buffer, client_addr);
};
// Create a detached thread to handle the request
@ -1293,6 +1298,9 @@ void neroshop::Node::run_optimized() {
// Add the node that pinged this node to the routing table
on_ping(buffer, client_addr);
// Add provider
on_map(buffer, client_addr);
};
// Create a detached thread to handle the request

@ -29,7 +29,7 @@ private:
std::string id;
std::string version;
std::unordered_map<std::string, std::string> data; // internal hash table that stores key-value pairs
std::unordered_map<std::string, std::vector<Peer>> info_hash_peers; // maps an info_hash to a vector of Peers
std::unordered_map<std::string, std::vector<Peer>> providers; // maps a data's hash (key) to a vector of Peers who have the data
////std::unique_ptr<Server> server;// a node acts as a server so it should have a server object
int sockfd;
struct sockaddr_in sockin; // IPV4
@ -69,16 +69,13 @@ public:
//---------------------------------------------------
bool send_ping(const std::string& address, int port);
std::vector<Node*> send_find_node(const std::string& target_id, const std::string& address, uint16_t port);
void send_get_peers(const std::string& info_hash);
void send_announce_peer(const std::string& info_hash, int port, const std::string& token);
void send_add_peer(const std::string& info_hash, const Peer& peer);
int send_put(const std::string& key, const std::string& value);
int send_store(const std::string& key, const std::string& value);
std::string send_get(const std::string& key);
std::string send_find_value(const std::string& key);
void send_remove(const std::string& key);
void send_map(const std::string& address, int port); // Distributes indexing data to a single node
// announce_peer, get_peers are specific to Bittorent and are not used in standard Kademlia
void send_get_providers(const std::string& data_hash);
//---------------------------------------------------
////std::vector<Node*> lookup(const std::string& key); // In Kademlia, the primary purpose of the lookup function is to find the nodes responsible for storing a particular key in the DHT, rather than retrieving the actual value of the key. The lookup function helps in locating the nodes that are likely to have the key or be able to provide information about it.
//---------------------------------------------------
@ -94,6 +91,7 @@ public:
void rebuild_routing_table(); // Re-builds routing table from data stored on disk
//---------------------------------------------------
void on_ping(const std::vector<uint8_t>& buffer, const struct sockaddr_in& client_addr);
void on_map(const std::vector<uint8_t>& buffer, const struct sockaddr_in& client_addr);
////void on_dead_node(const std::vector<std::string>& node_ids);
////bool on_keyword_blocked(const std::string& keyword);
////bool on_node_blacklisted(const std::string& address);
@ -102,18 +100,16 @@ public:
// DHT Query Types
bool ping(const std::string& address, int port); // A simple query to check if a node is online and responsive.
std::vector<Node*> find_node(const std::string& target_id, int count) const;// override; // A query to find the contact information for a specific node in the DHT. // Finds the node closest to the target_id
std::vector<Peer> get_peers(const std::string& info_hash) const; // A query to get a list of peers for a specific torrent or infohash.
void announce_peer(const std::string& info_hash, int port, const std::string& token); // A query to announce that a peer has joined a specific torrent or infohash.
void add_peer(const std::string& info_hash, const Peer& peer);
void remove_peer(const std::string& info_hash);
int put(const std::string& key, const std::string& value); // A query to store a value in the DHT. // Stores the key-value pair in the DHT
int store(const std::string& key, const std::string& value);
std::string get(const std::string& key) const; // A query to get a specific value stored in the DHT. // Retrieves the value associated with the key from the DHT
std::string find_value(const std::string& key) const;
int remove(const std::string& key); // Remove a key-value pair from the DHT
//---------------------------------------------------
// DHT-based indexing (Inverted indexing)
void map(const std::string& key, const std::string& value); // Maps search terms to keys
void add_provider(const std::string& data_hash, const Peer& peer);
void remove_providers(const std::string& data_hash);
void remove_provider(const std::string& data_hash, const std::string& address, int port);
std::vector<Peer> get_providers(const std::string& data_hash) const; // A query to get a list of peers for a specific torrent or infohash.
//---------------------------------------------------
std::string get_id() const; // get ID of this node
std::string get_ip_address() const;

@ -180,7 +180,7 @@ bool neroshop::create_json() {
root_obj.insert(QString("language"), QJsonValue("English"));
root_obj.insert(QString("hide_homepage_button"), QJsonValue(false));
root_obj.insert(QString("hide_price_display"), QJsonValue(false));
root_obj.insert(QString("hide_wallet_sync_bar"), QJsonValue(false));
root_obj.insert(QString("hide_wallet_sync_bar_on_full"), QJsonValue(false));
root_obj.insert(QString("wallet_directory"), QJsonValue(""));
/*root_obj.insert(QString("window_width"), QJsonValue(1280));
root_obj.insert(QString("window_height"), QJsonValue(900));//720));
@ -192,7 +192,7 @@ bool neroshop::create_json() {
wallet_obj.insert(QString("balance_amount_precision"), QJsonValue(12));
wallet_obj.insert(QString("show_currency_sign"), QJsonValue(false));
wallet_obj.insert(QString("block_explorer"), QJsonValue("xmrchain.net"));
wallet_obj.insert(QString("require_password_on_withdrawal"), QJsonValue(true));
////wallet_obj.insert(QString("require_password_on_withdrawal"), QJsonValue(true));
monero_obj.insert(QString("wallet"), QJsonValue(wallet_obj));
QJsonObject catalog_obj;
catalog_obj.insert(QString("price_display"), QJsonValue("All prices"));
@ -252,7 +252,7 @@ bool neroshop::create_json() {
settings_json["language"] = "English";
settings_json["hide_homepage_button"] = false;
settings_json["hide_price_display"] = false;
settings_json["hide_wallet_sync_bar"] = false;
settings_json["hide_wallet_sync_bar_on_full"] = false;
settings_json["wallet_directory"] = ""; // leave blank to use default
/*settings_json["window_width"] = 1280;
settings_json["window_height"] = 900;//720;
@ -262,7 +262,7 @@ bool neroshop::create_json() {
settings_json["monero"]["wallet"]["balance_amount_precision"] = 12;
settings_json["monero"]["wallet"]["show_currency_sign"] = false;
settings_json["monero"]["wallet"]["block_explorer"] = "xmrchain.net";
settings_json["monero"]["wallet"]["require_password_on_withdrawal"] = true;
////settings_json["monero"]["wallet"]["require_password_on_withdrawal"] = true;
settings_json["catalog"]["price_display"] = "All prices";
settings_json["catalog"]["hide_product_details"] = false;
settings_json["catalog"]["catalog_view"] = "Grid view";

Loading…
Cancel
Save