ObsidianDragon - DragonX ImGui Wallet
Full-node GUI wallet for DragonX cryptocurrency. Built with Dear ImGui, SDL3, and OpenGL3/DX11. Features: - Send/receive shielded and transparent transactions - Autoshield with merged transaction display - Built-in CPU mining (xmrig) - Peer management and network monitoring - Wallet encryption with PIN lock - QR code generation for receive addresses - Transaction history with pagination - Console for direct RPC commands - Cross-platform (Linux, Windows)
This commit is contained in:
147
src/util/payment_uri.cpp
Normal file
147
src/util/payment_uri.cpp
Normal file
@@ -0,0 +1,147 @@
|
||||
// DragonX Wallet - ImGui Edition
|
||||
// Copyright 2024-2026 The Hush Developers
|
||||
// Released under the GPLv3
|
||||
|
||||
#include "payment_uri.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
|
||||
namespace dragonx {
|
||||
namespace util {
|
||||
|
||||
std::string urlDecode(const std::string& encoded)
|
||||
{
|
||||
std::string result;
|
||||
result.reserve(encoded.size());
|
||||
|
||||
for (size_t i = 0; i < encoded.size(); i++) {
|
||||
if (encoded[i] == '%' && i + 2 < encoded.size()) {
|
||||
// Decode hex pair
|
||||
char hex[3] = { encoded[i + 1], encoded[i + 2], '\0' };
|
||||
char* end;
|
||||
long val = strtol(hex, &end, 16);
|
||||
if (end == hex + 2) {
|
||||
result += static_cast<char>(val);
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
} else if (encoded[i] == '+') {
|
||||
result += ' ';
|
||||
continue;
|
||||
}
|
||||
result += encoded[i];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool isPaymentURI(const std::string& str)
|
||||
{
|
||||
// Check for drgx: or hush: prefix (case insensitive)
|
||||
std::string lower = str;
|
||||
std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
|
||||
return lower.substr(0, 5) == "drgx:" || lower.substr(0, 5) == "hush:";
|
||||
}
|
||||
|
||||
PaymentURI parsePaymentURI(const std::string& uri)
|
||||
{
|
||||
PaymentURI result;
|
||||
result.valid = false;
|
||||
|
||||
// Check prefix
|
||||
if (!isPaymentURI(uri)) {
|
||||
result.error = "Invalid URI scheme. Expected drgx: or hush:";
|
||||
return result;
|
||||
}
|
||||
|
||||
// Skip the scheme prefix (drgx: or hush:)
|
||||
size_t schemeEnd = uri.find(':');
|
||||
if (schemeEnd == std::string::npos) {
|
||||
result.error = "Invalid URI format";
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string remainder = uri.substr(schemeEnd + 1);
|
||||
|
||||
// Handle double-slash format (drgx://address)
|
||||
if (remainder.size() >= 2 && remainder[0] == '/' && remainder[1] == '/') {
|
||||
remainder = remainder.substr(2);
|
||||
}
|
||||
|
||||
// Split address and query string
|
||||
size_t queryStart = remainder.find('?');
|
||||
if (queryStart == std::string::npos) {
|
||||
// No query parameters, just the address
|
||||
result.address = remainder;
|
||||
} else {
|
||||
result.address = remainder.substr(0, queryStart);
|
||||
std::string queryString = remainder.substr(queryStart + 1);
|
||||
|
||||
// Parse query parameters
|
||||
std::istringstream queryStream(queryString);
|
||||
std::string param;
|
||||
|
||||
while (std::getline(queryStream, param, '&')) {
|
||||
size_t eqPos = param.find('=');
|
||||
if (eqPos == std::string::npos) continue;
|
||||
|
||||
std::string key = param.substr(0, eqPos);
|
||||
std::string value = urlDecode(param.substr(eqPos + 1));
|
||||
|
||||
// Convert key to lowercase for case-insensitive matching
|
||||
std::transform(key.begin(), key.end(), key.begin(), ::tolower);
|
||||
|
||||
if (key == "amount" || key == "amt") {
|
||||
try {
|
||||
result.amount = std::stod(value);
|
||||
} catch (...) {
|
||||
// Invalid amount, keep as 0
|
||||
}
|
||||
} else if (key == "label") {
|
||||
result.label = value;
|
||||
} else if (key == "memo") {
|
||||
result.memo = value;
|
||||
} else if (key == "message" || key == "msg") {
|
||||
result.message = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate address
|
||||
if (result.address.empty()) {
|
||||
result.error = "No address specified in URI";
|
||||
return result;
|
||||
}
|
||||
|
||||
// Basic address format check
|
||||
bool validFormat = false;
|
||||
|
||||
// z-address: starts with 'zs' and is 78+ chars
|
||||
if (result.address[0] == 'z' && result.address.size() >= 78) {
|
||||
validFormat = true;
|
||||
}
|
||||
// t-address: starts with 'R' (DragonX) or 't' (HUSH) and is ~34 chars
|
||||
else if ((result.address[0] == 'R' || result.address[0] == 't') &&
|
||||
result.address.size() >= 26 && result.address.size() <= 36) {
|
||||
validFormat = true;
|
||||
}
|
||||
|
||||
if (!validFormat) {
|
||||
result.error = "Invalid address format";
|
||||
return result;
|
||||
}
|
||||
|
||||
// Validate amount if present
|
||||
if (result.amount < 0) {
|
||||
result.error = "Invalid negative amount";
|
||||
return result;
|
||||
}
|
||||
|
||||
result.valid = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
} // namespace dragonx
|
||||
Reference in New Issue
Block a user