Compare commits

...

5 Commits

6
.gitmodules vendored

@ -7,9 +7,6 @@
[submodule "external/json"]
path = external/json
url = https://github.com/nlohmann/json.git
[submodule "external/raft"]
path = external/raft
url = https://github.com/willemt/raft.git
[submodule "external/stduuid"]
path = external/stduuid
url = https://github.com/mariusbancila/stduuid.git
@ -19,6 +16,3 @@
[submodule "external/i2pd"]
path = external/i2pd
url = https://github.com/PurpleI2P/i2pd.git
[submodule "external/i2psam"]
path = external/i2psam
url = https://github.com/i2p/i2psam.git

@ -12,6 +12,8 @@ Item {
property bool selectedNodeStatus: (listView.currentItem == null) ? "" : listView.currentItem.children[2].selectedNodeStatus
property alias currentIndex: listView.currentIndex
property alias model: listView.model
property alias count: listView.count
property alias list: listView
ColumnLayout {
anchors.fill: parent

@ -983,10 +983,12 @@ Item {
}
RowLayout {
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
width: 500
// Default row spacing is 5 so 3 items=2 spaces=width is reduced by 10
TextField {
id: moneroNodeIPField
Layout.preferredWidth: (moneroNodePortField.width * 3) - parent.spacing // Default row spacing is 5 so the width is reduced by 5
Layout.preferredWidth: 315
Layout.preferredHeight: 50
placeholderText: qsTr("Custom node IP address"); placeholderTextColor: (NeroshopComponents.Style.darkTheme) ? "#a9a9a9" : "#696969"
color: (NeroshopComponents.Style.darkTheme) ? "#ffffff" : "#000000"
@ -996,12 +998,12 @@ Item {
color: (NeroshopComponents.Style.darkTheme) ? "#101010" : "#ffffff"
border.color: (NeroshopComponents.Style.darkTheme) ? "#a9a9a9" : "#696969"
radius: 3
}
}
}
TextField {
id: moneroNodePortField
Layout.preferredWidth: (500 / 4)
Layout.preferredWidth: 125
Layout.preferredHeight: 50
placeholderText: Wallet.getNetworkPort()
placeholderTextColor: (NeroshopComponents.Style.darkTheme) ? "#a9a9a9" : "#696969"
@ -1013,7 +1015,70 @@ Item {
border.color: (NeroshopComponents.Style.darkTheme) ? "#a9a9a9" : "#696969"
radius: 3
}
}
}
Button {
id: addMoneroNodeButton
Layout.preferredWidth: 50
Layout.preferredHeight: 50
Layout.fillWidth: true
text: qsTr("+")
background: Rectangle {
color: parent.hovered ? "#698b22" : "#506a1a"
radius: 5
}
contentItem: Text {
text: parent.text
color: "#ffffff"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
onClicked: {
if(moneroNodeIPField.length == 0) {
return
}
let remoteNodeArray = moneroRemoteNodeList.model
if (!Array.isArray(remoteNodeArray)) return;
let node_address = (moneroNodePortField.length > 0) ? (moneroNodeIPField.text + ":" + moneroNodePortField.text) : (moneroNodeIPField.text + ":" + moneroNodePortField.placeholderText)// moneroNodeIPField.text.replace(/^(https?:|)\/\//, '')
let isDuplicate = remoteNodeArray.some(item => {
if (typeof item === "string") {
return item === node_address;
} else {
return item.address === node_address;
}
});
if(isDuplicate) {
console.log(node_address + " has already been added to node list")
return
}
if(typeof remoteNodeArray[0] === "string") {
remoteNodeArray.push(node_address)
} else {
remoteNodeArray.push({
available: false,
address: node_address,
last_height: "- -"
})
}
moneroRemoteNodeList.model = remoteNodeArray
moneroRemoteNodeList.currentIndex = moneroRemoteNodeList.count - 1
moneroRemoteNodeList.list.positionViewAtIndex(moneroRemoteNodeList.currentIndex, ListView.Contain)
settingsDialog.save()
moneroNodeIPField.text = ""
moneroNodePortField.text = ""
}
MouseArea {
anchors.fill: parent
onPressed: mouse.accepted = false
cursorShape: Qt.PointingHandCursor
}
}
}
RowLayout {
@ -1025,9 +1090,9 @@ Item {
Layout.preferredWidth: parent.width////contentItem.contentWidth + 30
Layout.preferredHeight: contentItem.contentHeight + 30
text: qsTr("Connect")
property bool disabled: !Wallet.opened//{ if(!Wallet.isOpened()) return true; return Wallet.isDaemonSynced() }
enabled: Wallet.opened
background: Rectangle {
color: remoteNodeConnectButton.disabled ? NeroshopComponents.Style.moneroGrayColor : NeroshopComponents.Style.moneroOrangeColor
color: !remoteNodeConnectButton.enabled ? NeroshopComponents.Style.moneroGrayColor : NeroshopComponents.Style.moneroOrangeColor
radius: 3//6
}
contentItem: Text {
@ -1037,7 +1102,7 @@ Item {
verticalAlignment: Text.AlignVCenter
}
onClicked: {
if(remoteNodeConnectButton.disabled) return;
if(!remoteNodeConnectButton.enabled) return;
if(!Wallet.isOpened()) {messageBox.text="Wallet must be opened first before connecting to a node";messageBox.open();return;}
let remote_node = (moneroNodeIPField.length > 0) ? (moneroNodePortField.length > 0 ? (moneroNodeIPField.text + ":" + moneroNodePortField.text) : (moneroNodeIPField.text + ":" + moneroNodePortField.placeholderText)) : moneroRemoteNodeList.selectedNode.replace(/^(https?:|)\/\//, '')//console.log("remote_node", remote_node)
let remote_node_ip = remote_node.split(":")[0]

@ -201,20 +201,12 @@ neroshop::CartError neroshop::Cart::add(const std::string& user_id, const std::s
return CartError::Ok;
}
////////////////////
void neroshop::Cart::add(const std::string& user_id, const neroshop::Product& item, int quantity) {
add(user_id, item.get_id(), quantity);
}
////////////////////
void neroshop::Cart::remove(const std::string& user_id, const std::string& listing_key, int quantity) {
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
}
////////////////////
void neroshop::Cart::remove(const std::string& user_id, const neroshop::Product& item, int quantity) {
remove(user_id, item.get_id(), quantity);
}
////////////////////
void neroshop::Cart::empty() {
if(id.empty()) { neroshop::print("No cart found", 1); return; }
neroshop::db::Sqlite3 * database = neroshop::get_database();
@ -223,7 +215,7 @@ void neroshop::Cart::empty() {
contents.clear();
}
////////////////////
void neroshop::Cart::change_quantity(const std::string& user_id, const neroshop::Product& item, int quantity) {
void neroshop::Cart::change_quantity(const std::string& user_id, const std::string& listing_key, int quantity) {
}
////////////////////
////////////////////
@ -310,10 +302,6 @@ bool neroshop::Cart::in_cart(const std::string& listing_key) const {
return false;
}
////////////////////
bool neroshop::Cart::in_cart(const neroshop::Product& item) const {
return in_cart(item.get_id());
}
////////////////////
////////////////////
////////////////////
////////////////////

@ -1,4 +1,5 @@
//#pragma once
#pragma once
#ifndef CART_HPP_NEROSHOP
#define CART_HPP_NEROSHOP
@ -33,18 +34,16 @@ public:
Cart();
~Cart();
CartError add(const std::string& user_id, const std::string& listing_key, int quantity = 1);
void add(const std::string& user_id, const neroshop::Product& item, int quantity = 1);
void remove(const std::string& user_id, const std::string& listing_key, int quantity = 1);
void remove(const std::string& user_id, const neroshop::Product& item, int quantity = 1);
void empty(); // remove all items from cart
void change_quantity(const std::string& user_id, const neroshop::Product& item, int quantity); // set_quantity is private so you can only change item quantity from this function
void change_quantity(const std::string& user_id, const std::string& listing_key, int quantity);
//void move_to_wishlist();
//void save_for_later();
//void shift_up(const neroshop::Product& item);
//void shift_down(const neroshop::Product& item);
//void swap_positions(const neroshop::Product& item1, const neroshop::Product& item2);
//void checkout(); // user's cart contents impact inventory availability. Only after purchase will actual inventory decrease
//void shift_up(const std::string& listing_key);
//void shift_down(const std::string& listing_key);
//void swap_positions(const std::string& listing_key1, const std::string& listing_key2);
//void checkout(); // Only after purchase will actual inventory decrease
void print_cart();
// setters
// getters
@ -73,8 +72,6 @@ public:
bool is_empty() const;
bool is_full() const; // cart is full (has reached max items)
bool in_cart(const std::string& listing_key) const;
bool in_cart(const neroshop::Product& item) const;
//bool validate_item(const neroshop::Product& item) const;
// friends - can access cart's private members
friend class User;
friend class Buyer;
@ -88,7 +85,7 @@ private:
std::vector<CartItem> contents;
static unsigned int max_items; // cart can only hold up to 10 unique items
static unsigned int max_quantity; // the max quantity each item can add up to is 100, so 10 items can each have a quantity of 10, making the total number of items 100
void load(const std::string& user_id); // loads cart data in-memory (called on user login)
void load(const std::string& user_id); // loads cart data from database to memory (called on user login)
void set_id(const std::string& id);
void set_owner_id(const std::string& owner_id);
void set_contents(const std::vector<CartItem>& contents);

@ -49,7 +49,7 @@ const std::vector<Category> predefined_categories = {
};
const std::vector<Subcategory> predefined_subcategories = {
// For each category is a subcategory (for example, the books, movies, and music categories can have the digital goods subcategory if they are digital rather than physical)
// For each category is a subcategory (for example, the books, movies, and music categories fall under the digital goods subcategory if they are digital rather than physical)
// Digital Goods
{ static_cast<unsigned int>(predefined_categories.size() + 0), predefined_categories[5].name, predefined_categories[5].description, predefined_categories[5].thumbnail, 7 },
{ static_cast<unsigned int>(predefined_categories.size() + 1), predefined_categories[5].name, predefined_categories[5].description, predefined_categories[5].thumbnail, 8 },

@ -1,10 +1,8 @@
#include "serializer.hpp"
#include "../../crypto/sha3.hpp"
//#include "../../crypto/rsa.hpp" // for signing and verifying data
#include "../../tools/string.hpp"
#include "../../tools/timestamp.hpp"
//#include "../../tools/base64.hpp"
#include "../../wallet/wallet.hpp"
#include "../../settings.hpp"
@ -12,38 +10,14 @@
#include <nlohmann/json.hpp>
/** Thoughts and ideas: I think cart and wishlist data should be stored locally in the database file:
1. Data privacy: By storing cart and wishlist data locally, you can ensure that sensitive information is not shared across the network or exposed to unauthorized users. This can help to protect users' privacy and ensure that their data is secure.
2. Performance: Retrieving data from a local database file can be faster than accessing data stored in a DHT, since it does not require network communication. This can help to improve the performance of your application and provide a better user experience.
3. Ease of implementation: Storing data locally in a database file can be easier to implement and maintain than using a DHT. You can use a variety of database technologies and libraries to store and manage your data, and you may be able to take advantage of existing tools and frameworks to simplify development.
Yes, you are correct. In a DHT, it's important that the keys for storing and retrieving data are consistent with the key used for routing. The node ID is typically a cryptographic hash and is used for routing, so the key used for storing and retrieving data should also be a hash to ensure that the DHT functions properly.
In your case, if you want to use a UUID as the primary identifier for products, you could hash the UUID to generate a key that can be used in the DHT. Alternatively, you could include the UUID as a field within the product dictionary and hash the entire dictionary to generate the key for the DHT.
**/
namespace neroshop_crypto = neroshop::crypto;
namespace neroshop_string = neroshop::string;
// both info hashes (keys) and node ids should use the same hash function to ensure that the distance metric used in the Kademlia routing table is consistent and nodes can effectively search for and store information. Additionally, it simplifies the implementation of the routing table and node lookup algorithms, as only one hash function needs to be used throughout the system.
//-----------------------------------------------------------------------------
std::pair<std::string, std::string/*std::vector<uint8_t>*/> neroshop::Serializer::serialize(const neroshop::Object& obj) {
nlohmann::json json_object = {};
if(std::holds_alternative<User>(obj)) {
const User& seller = std::get<User>(obj);
json_object["display_name"] = seller.get_name();
json_object["monero_address"] = seller.get_id(); // used as identifier
std::string public_key = seller.get_public_key();
if(!public_key.empty()) json_object["public_key"] = public_key;
//json_object["avatar"] = ; // TODO: Avatars
//json_object["signature"] = seller.get_wallet()->sign_message(user_id, monero_message_signature_type::SIGN_WITH_SPEND_KEY);
json_object["metadata"] = "user";
}
if(std::holds_alternative<Listing>(obj)) {
const Listing& listing = std::get<Listing>(obj);
json_object["id"] = listing.get_id();
@ -172,7 +146,7 @@ std::pair<std::string, std::string/*std::vector<uint8_t>*/> neroshop::Serializer
cart_item_obj["product_id"] = item.key;
cart_item_obj["quantity"] = item.quantity;
cart_item_obj["seller_id"] = item.seller_id;
json_object["contents"].push_back(cart_item_obj); // cart_items // TODO: encrypt cart contents
json_object["contents"].push_back(cart_item_obj);
}
json_object["metadata"] = "cart";
}
@ -239,8 +213,6 @@ std::pair<std::string, std::string/*std::vector<uint8_t>*/> neroshop::Serializer
json_object["timestamp"] = neroshop::timestamp::get_current_utc_timestamp();
json_object["metadata"] = "seller_rating";
}
// TODO: Favorites (wishlists). EDIT: Favorites won't be stored in the DHT but will be stored locally
// Use versioning or timestamping for updating values in DHT. Keys remain the same.
//-------------------------------------------------------------
// Get the `value`
std::string value = json_object.dump();
@ -501,52 +473,3 @@ std::shared_ptr<neroshop::User> neroshop::Serializer::deserialize_user(const std
//-----------------------------------------------------------------------------
template <typename... Args>
std::string neroshop::Serializer::to_json(Args&&... args) {
nlohmann::json json_object = nlohmann::json::object({std::forward<Args>(args)...});
return json_object.dump();
}
/*int main() {
std::cout << neroshop::Serializer::to_json(std::make_pair("key1", 123),
std::make_pair("key2", "value2"),
std::make_pair("key3", true)) << "\n";
std::cout << "\n";
neroshop::Object obj = neroshop::Product { "my_product_uuid", "Nintendo Switch Lite", "A really fun gaming system",
{
{.color = "yellow" , .size = "lite", .weight = .61, .product_code = "00045496882303"}, // 0.61 lbs
{.color = "gray" , .size = "lite", .weight = .61, .product_code = "00045496882280"},
{.color = "turquoise", .size = "lite", .weight = .61, .product_code = "00045496882266"},
{.color = "coral" , .size = "lite", .weight = .61, .product_code = "00045496882662"},
{.color = "blue" , .size = "lite", .weight = .61, .product_code = "00045496882716"},
},
"",//"my_product_code",
0, -1,
{"video games", "nintendo", "switch lite"}
};
neroshop::ProductRating pr = { "user1", "Good product", "signed", "product123", 4 };
neroshop::SellerRating sr = { "user2", "Fast shipping", "signed", "seller456", 1 };
std::pair<std::string, std::vector<uint8_t>> result = neroshop::Serializer::serialize(obj); // pass the variant to the serialize function
std::string key = result.first;
nlohmann::json value_json = nlohmann::json::from_msgpack(result.second);
//std::cout << key << ": " << value_json << "\n";
assert(neroshop_crypto::sha3_256(value_json.dump()) == key); // key must be hash of value
auto obj2 = neroshop::Serializer::deserialize(result);
std::pair<std::string, std::vector<uint8_t>> result2 = neroshop::Serializer::serialize(*obj2); // pass the variant to the serialize function
std::string key2 = result2.first;
nlohmann::json value_json2 = nlohmann::json::from_msgpack(result2.second);
//std::cout << key2 << ": " << value_json2 << "\n";
assert(neroshop_crypto::sha3_256(value_json2.dump()) == key2); // key must be hash of value
return 0;
}*/
// g++ serializer.cpp crypto/sha3_256.cpp tools/logger.cpp price/*.cpp config.cpp script.cpp cart.cpp product.cpp listing.cpp order.cpp database/database.cpp database/sqlite.cpp -o serialize -lcrypto -lssl -I../../external/json/single_include -Icrypto -I../../external/stduuid/include -I../../external/stduuid/ -I../../external/lua/src -L../../build -lsqlite3 -lcurl -llua

@ -1,7 +1,9 @@
#pragma once
#include <iostream>
#include <memory>
#include <string>
#include <utility>
#include <variant>
#include <vector>
@ -21,11 +23,8 @@ private:
public:
static std::pair<std::string, std::string/*std::vector<uint8_t>*/> serialize(const Object& object);
static std::pair<std::string, std::string> serialize(const User& user);
static std::shared_ptr<Object> deserialize(const std::pair<std::string, std::string/*std::vector<uint8_t>*/>& data);//static const Object& deserialize(const std::string& json_object);
static std::shared_ptr<Object> deserialize(const std::pair<std::string, std::string/*std::vector<uint8_t>*/>& data);
static std::shared_ptr<User> deserialize_user(const std::pair<std::string, std::string>& data);
template <typename... Args>
static std::string to_json(Args&&... args);
};
}

@ -122,6 +122,7 @@ int main(int argc, char *argv[])
engine.rootContext()->setContextProperty("DaemonManager", daemonManager);
ProxyManager * proxyManager = new ProxyManager(&engine);
engine.rootContext()->setContextProperty("ProxyManager", proxyManager);
engine.setNetworkAccessManagerFactory(proxyManager);
// we can also register an instance of a class instead of the class itself
WalletController *wallet = new WalletController(&engine);
engine.rootContext()->setContextProperty("Wallet", wallet);//new WalletController());//qmlRegisterUncreatableType<WalletProxy>("neroshop.Wallet", 1, 0, "Wallet", "Wallet cannot be instantiated directly.");//qmlRegisterType<WalletProxy>("neroshop.Wallet", 1, 0, "Wallet"); // Usage: import neroshop.Wallet ... Wallet { id: wallet }

@ -1,6 +1,7 @@
#include "proxy_manager.hpp"
#include <QFile>
#include <QNetworkDiskCache>
#include <QNetworkProxy>
#include <QNetworkProxyFactory>
#include <QNetworkProxyQuery>
@ -37,6 +38,24 @@ neroshop::ProxyManager::ProxyManager(QObject* parent) : QObject(parent), m_torEn
neroshop::ProxyManager::~ProxyManager() {}
QNetworkAccessManager * neroshop::ProxyManager::create(QObject *parent) {
QNetworkAccessManager *networkAccessManager = new QNetworkAccessManager(parent);
QNetworkDiskCache *diskCache = new QNetworkDiskCache(parent);
diskCache->setCacheDirectory("requestCache");
networkAccessManager->setCache(diskCache);
if(isTorEnabled() && isTorRunning()) {
QNetworkProxy torProxy;
torProxy.setType(QNetworkProxy::Socks5Proxy);
torProxy.setHostName("127.0.0.1");
torProxy.setPort(9050);
networkAccessManager->setProxy(torProxy);
// Standard QML components like Image or XmlHttpRequest can now make network requests over Tor
}
return networkAccessManager;
}
void neroshop::ProxyManager::useDefaultProxy() {
QNetworkProxy::setApplicationProxy(QNetworkProxy());
@ -330,6 +349,8 @@ void neroshop::ProxyManager::onReplyFinished(QNetworkReply * reply) {
// Reply received successfully
QByteArray data = reply->readAll();
////qDebug() << "Data received:" << data;
QVariant fromCache = reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute);
qDebug() << "page from cache?" << fromCache.toBool();
} else {
// Error occurred
qDebug() << "Network error: " << reply->errorString();

@ -5,12 +5,13 @@
#include <QObject>
#include <QNetworkAccessManager>
#include <QQmlNetworkAccessManagerFactory>
#include <QNetworkReply>
#include <QProcess>
namespace neroshop {
class ProxyManager : public QObject {
class ProxyManager : public QObject, public QQmlNetworkAccessManagerFactory {
Q_OBJECT
Q_PROPERTY(bool torEnabled READ isTorEnabled NOTIFY networkProxyChanged)
Q_PROPERTY(QString torOutput READ getTorOutput NOTIFY torOutputChanged)
@ -18,6 +19,8 @@ public:
ProxyManager(QObject *parent = nullptr);
~ProxyManager();
QNetworkAccessManager * create(QObject *parent) override;
Q_INVOKABLE void useDefaultProxy();
Q_INVOKABLE void useTorProxy();
Q_INVOKABLE void useI2PProxy();

Loading…
Cancel
Save