Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,11 @@ set_property(TARGET qubic-cli PROPERTY COMPILE_WARNING_AS_ERROR ON)
ADD_LIBRARY(fourq-qubic SHARED fourq_qubic.cpp)
set_property(TARGET fourq-qubic PROPERTY SOVERSION 1)
target_compile_options(fourq-qubic PRIVATE -DBUILD_4Q_LIB)

if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
target_compile_options(qubic-cli PRIVATE -mrdrnd)
target_compile_options(fourq-qubic PRIVATE -mrdrnd)
endif()

install(TARGETS fourq-qubic LIBRARY)

4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ Commands:
[WALLET COMMANDS]
-showkeys
Generate identity, public key and private key from seed. Seed must be passed either from params or configuration file.
-generate-vanity-address <EXPECTED_CHARS> <THREADS> <IS_SUFIX>
Generates a vanity address matching the expected characters using multiple threads, with an option to match as a suffix.
Example: ./qubic-cli -generate-vanity-address ABC 1 false (will generate an address starting with ABC)
./qubic-cli -generate-vanity-address XYZ 4 true (will generate an address ending with XYZ using 4 threads)
-getbalance <IDENTITY>
Balance of an identity (amount of qubic, number of in/out txs)
-getasset <IDENTITY>
Expand Down
12 changes: 12 additions & 0 deletions argparser.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ void print_help()
printf("\n[WALLET COMMANDS]\n");
printf("\t-showkeys\n");
printf("\t\tGenerate identity, public key and private key from seed. Seed must be passed either from params or configuration file.\n");
printf("\t-generate-vanity-address <EXPECTED_CHARS> <THREADS> <IS_SUFIX>\n");
printf("\t\tGenerates a vanity address matching the expected characters using multiple threads, with an option to match as a suffix.\n");
printf("\t-getbalance <IDENTITY>\n");
printf("\t\tBalance of an identity (amount of qubic, number of in/out txs)\n");
printf("\t-getasset <IDENTITY>\n");
Expand Down Expand Up @@ -728,6 +730,16 @@ void parseArgument(int argc, char** argv)
CHECK_OVER_PARAMETERS
break;
}
if (strcmp(argv[i], "-generate-vanity-address") == 0)
{
g_cmd = GENERATE_VANITY_ADDRESS;
g_vanityPattern = argv[i + 1];
g_vanityGenerationThreads = uint32_t(charToNumber(argv[i + 2]));
g_isVanitySuffix = (strcmp(argv[i + 3], "1") == 0 || strcasecmp(argv[i + 3], "true") == 0);
i += 4;
CHECK_OVER_PARAMETERS
break;
}
if (strcmp(argv[i], "-getbalance") == 0)
{
CHECK_NUMBER_OF_PARAMETERS(1)
Expand Down
3 changes: 3 additions & 0 deletions global.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ char g_loggingMode = 0;
char* g_compChatString = nullptr;
uint64_t g_executionFeeMultiplierNumerator = 1;
uint64_t g_executionFeeMultiplierDenominator = 1;
char *g_vanityPattern = nullptr;
unsigned int g_vanityGenerationThreads = 1;
bool g_isVanitySuffix = false;

char* g_dumpBinaryFileInput = nullptr;
char* g_dumpBinaryFileOutput = nullptr;
Expand Down
3 changes: 3 additions & 0 deletions main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ int run(int argc, char* argv[])
sanityCheckSeed(g_seed);
printWalletInfo(g_seed);
break;
case GENERATE_VANITY_ADDRESS:
generateVanityAddress(g_vanityPattern, g_vanityGenerationThreads, g_isVanitySuffix);
break;
case GET_CURRENT_TICK:
sanityCheckNode(g_nodeIp, g_nodePort);
printTickInfoFromNode(g_nodeIp, g_nodePort);
Expand Down
1 change: 1 addition & 0 deletions structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
enum COMMAND
{
SHOW_KEYS,
GENERATE_VANITY_ADDRESS,
GET_CURRENT_TICK,
GET_BALANCE,
GET_ASSET,
Expand Down
119 changes: 110 additions & 9 deletions wallet_utils.cpp
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
#include <chrono>
#include <thread>
#include <cstdint>
#include <cstring>
#include <stdexcept>
#include <thread>
#include <atomic>

#include "utils.h"
#include "node_utils.h"
#include "key_utils.h"
#include "logger.h"
#include "structs.h"
#include "wallet_utils.h"
#include "connection.h"
#include "k12_and_key_utils.h"
#include "key_utils.h"
#include "logger.h"
#include "node_utils.h"
#include "sc_utils.h"
#include "structs.h"
#include "utils.h"

void printWalletInfo(const char* seed)
{
Expand Down Expand Up @@ -125,7 +127,7 @@ void printBalance(const char* publicIdentity, const char* nodeIp, int nodePort)
LOG("Spectum Digest: %s\n", hex);
}

void printReceipt(Transaction& tx, const char* txHash = nullptr, const uint8_t* extraData = nullptr, int moneyFlew = -1)
void printReceipt(Transaction& tx, const char* txHash, const uint8_t* extraData, int moneyFlew)
{
char sourceIdentity[128] = {0};
char dstIdentity[128] = {0};
Expand Down Expand Up @@ -331,7 +333,7 @@ void makeContractTransaction(const char* nodeIp, int nodePort,
int extraDataSize,
const void* extraData,
uint32_t scheduledTickOffset,
QCPtr* qcPtr = nullptr)
QCPtr* qcPtr)
{
QCPtr qc = (!qcPtr) ? make_qc(nodeIp, nodePort) : *qcPtr;

Expand Down Expand Up @@ -393,7 +395,7 @@ bool runContractFunction(const char* nodeIp, int nodePort,
size_t inputSize,
void* outputPtr,
size_t outputSize,
QCPtr* qcPtr = nullptr)
QCPtr* qcPtr)
{
QCPtr qc = (!qcPtr) ? make_qc(nodeIp, nodePort) : *qcPtr;
std::vector<uint8_t> packet(sizeof(RequestResponseHeader) + sizeof(RequestContractFunction) + inputSize);
Expand Down Expand Up @@ -703,3 +705,102 @@ void printActiveIPOs(const char* nodeIp, int nodePort)
LOG("- contract index: %u, asset name: %s\n", ipo.contractIndex, ipo.assetName);
}
}

VanityAddress generateVanityAddress(const char* pattern, unsigned int vanityGenerationThreads, bool isSufix) {
auto isAddressValid = [](const char* identity, const char* pattern, bool isSufix) -> bool {
size_t patternLen = strlen(pattern);
size_t identityLen = strlen(identity);
if (isSufix) {
if (patternLen > identityLen) return false;
return strcmp(identity + (identityLen - patternLen), pattern) == 0;
} else {
if (patternLen > identityLen) return false;
return strncmp(identity, pattern, patternLen) == 0;
}
};

auto get_hw_entropy = []() -> unsigned long long {
unsigned long long val = 0;
// Try RDRAND (Intel/AMD hardware RNG)
if (_rdrand64_step(&val)) {
return val;
}
// Fallback to OS entropy if RDRAND fails or isn't supported
std::random_device rd;
return (static_cast<unsigned long long>(rd()) << 32) | rd();
};

unsigned long long estimatedAttempts = static_cast<unsigned long long>(std::pow(26, strlen(pattern)));
std::atomic<unsigned long long> attemptsCounter(0);

auto startTime = std::chrono::high_resolution_clock::now();
auto lastPrintTime = startTime;

// random seed (55 lowercase char)
constexpr char charset[] = "abcdefghijklmnopqrstuvwxyz";
constexpr size_t charset_len = sizeof(charset) - 1;
auto randomSeed = [&]() -> std::string {
std::string str(55, 0);
for (size_t i = 0; i < 55; ++i) {
// We use a simple rejection sampling to avoid modulo bias
uint64_t rand_val;
do {
rand_val = get_hw_entropy();
} while (rand_val >= (UINT64_MAX - (UINT64_MAX % charset_len)));
str[i] = charset[rand_val % charset_len];
}
return str;
};

auto generateThread = [&](VanityAddress& result, const char* pattern, bool isSufix, std::atomic<bool>& found, int threadId) {
while (!found.load(std::memory_order_acquire)) {
uint8_t publicKey[32] = {0};
char identity[61] = {0};
auto seed = randomSeed();
getPublicKeyFromSeed(seed.c_str(), publicKey);
getIdentityFromPublicKey(publicKey, identity, false);
if (isAddressValid(identity, pattern, isSufix)) {
if (!found.exchange(true)) {
memcpy(result.seed, seed.c_str(), sizeof(result.seed));
memcpy(result.identity, identity, sizeof(result.identity));
}
found.store(true, std::memory_order_release);
break;
}
++attemptsCounter;
auto currentTime = std::chrono::high_resolution_clock::now();
auto durationSinceLastPrint = std::chrono::duration_cast<std::chrono::seconds>(currentTime - lastPrintTime).count();
if (durationSinceLastPrint >= 1 && threadId == 0) {
lastPrintTime = currentTime;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can have data-race, if possible, just do the print on thread #0 only

const auto elapsedTime = std::chrono::duration_cast<std::chrono::seconds>(currentTime - startTime).count();
const auto attempts = static_cast<double>(attemptsCounter.load());
const double seconds = static_cast<double>(elapsedTime) > 0.0
? static_cast<double>(elapsedTime)
: 1.0;
const double attemptsPerSecond = attempts / seconds;
const double percentTried = static_cast<double>(attemptsCounter.load()) / static_cast<double>(estimatedAttempts) * 100.0;
LOG("Attempts: %llu | Estimated Attempts: %llu (%.6f%%) | Attempts/s: %.2f | Elapsed Time: %llus\n",
attemptsCounter.load(), estimatedAttempts, percentTried, attemptsPerSecond, elapsedTime);
}
}
};

// Start multiple threads to speed up the search
const unsigned int numThreads = std::min(vanityGenerationThreads, std::thread::hardware_concurrency());
std::vector<std::thread> threads;
VanityAddress result{};
std::atomic<bool> found{false};
for (unsigned int i = 0; i < numThreads; ++i) {
threads.emplace_back(generateThread, std::ref(result), pattern, isSufix, std::ref(found), i);
}
std::string patternStr = isSufix ? "*" + std::string(pattern) : std::string(pattern) + "*";
printf("-------- Starting vanity address generation with %u threads | Pattern %s --------\n", numThreads, patternStr.c_str());
for (auto& t : threads) {
t.join();
}

printf("Found vanity address!\n");
printf("Seed: %s\n", result.seed);
printf("Identity: %s\n", result.identity);
return result;
}
8 changes: 7 additions & 1 deletion wallet_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
#include "structs.h"
#include "connection.h"

struct VanityAddress {
char seed[55 + 1];
char identity[60 + 1];
};

void printWalletInfo(const char* seed);
void printBalance(const char* publicIdentity, const char* nodeIp, int nodePort);
void makeStandardTransaction(const char* nodeIp, int nodePort, const char* seed,
Expand Down Expand Up @@ -60,4 +65,5 @@ void makeIPOBid(const char* nodeIp, int nodePort,
uint16_t numberOfShare,
uint32_t scheduledTickOffset);
void printIPOStatus(const char* nodeIp, int nodePort, uint32_t contractIndex);
void printActiveIPOs(const char* nodeIp, int nodePort);
void printActiveIPOs(const char* nodeIp, int nodePort);
VanityAddress generateVanityAddress(const char* pattern, unsigned int vanityGenerationThreads, bool isSufix = false);