difficulty calculation, move from randomx to autolykos2

This commit is contained in:
2026-03-30 15:42:28 +02:00
parent c358115af4
commit b47ff30bc7
18 changed files with 1163 additions and 224 deletions

225
src/autolykos2/autolykos2.c Normal file
View File

@@ -0,0 +1,225 @@
#include <autolykos2/autolykos2.h>
#include "../../include/blake2/blake2.h"
#include <stdlib.h>
#include <string.h>
typedef struct {
uint8_t* buf;
size_t cap;
size_t len;
} dag_buffer_t;
struct Autolykos2Context {
dag_buffer_t dag;
void* backend;
};
#ifdef MINICOIN_AUTOLYKOS2_REF_AVAILABLE
extern void* minicoin_autolykos2_ref_create(void);
extern void minicoin_autolykos2_ref_destroy(void* handle);
extern bool minicoin_autolykos2_ref_check_target(
void* handle,
const uint8_t message32[32],
uint64_t nonce,
uint32_t height,
const uint8_t target32[32]
);
#endif
static bool Autolykos2_FallbackHash(
const Autolykos2Context* ctx,
const uint8_t* message,
size_t messageLen,
uint64_t nonce,
uint32_t height,
uint8_t outHash[32]
) {
uint8_t nonceBytes[8];
uint8_t heightBytes[4];
memcpy(nonceBytes, &nonce, sizeof(nonceBytes));
memcpy(heightBytes, &height, sizeof(heightBytes));
size_t dagChunkLen = 0;
const uint8_t* dagChunk = NULL;
if (ctx && ctx->dag.buf && ctx->dag.len > 0) {
dagChunk = ctx->dag.buf;
dagChunkLen = ctx->dag.len > 64 ? 64 : ctx->dag.len;
}
const size_t totalLen = messageLen + sizeof(nonceBytes) + sizeof(heightBytes) + dagChunkLen;
uint8_t* material = (uint8_t*)malloc(totalLen == 0 ? 1 : totalLen);
if (!material) {
return false;
}
size_t off = 0;
if (messageLen > 0) {
memcpy(material + off, message, messageLen);
off += messageLen;
}
memcpy(material + off, nonceBytes, sizeof(nonceBytes));
off += sizeof(nonceBytes);
memcpy(material + off, heightBytes, sizeof(heightBytes));
off += sizeof(heightBytes);
if (dagChunkLen > 0) {
memcpy(material + off, dagChunk, dagChunkLen);
}
const bool ok = Blake2b_Hash(material, totalLen, outHash, 32);
free(material);
return ok;
}
static int Cmp256BE(const uint8_t a[32], const uint8_t b[32]) {
for (size_t i = 0; i < 32; ++i) {
if (a[i] < b[i]) return -1;
if (a[i] > b[i]) return 1;
}
return 0;
}
Autolykos2Context* Autolykos2_Create(void) {
Autolykos2Context* ctx = (Autolykos2Context*)calloc(1, sizeof(Autolykos2Context));
if (!ctx) {
return NULL;
}
#ifdef MINICOIN_AUTOLYKOS2_REF_AVAILABLE
ctx->backend = minicoin_autolykos2_ref_create();
#endif
return ctx;
}
void Autolykos2_Destroy(Autolykos2Context* ctx) {
if (!ctx) {
return;
}
#ifdef MINICOIN_AUTOLYKOS2_REF_AVAILABLE
if (ctx->backend) {
minicoin_autolykos2_ref_destroy(ctx->backend);
ctx->backend = NULL;
}
#endif
free(ctx->dag.buf);
free(ctx);
}
bool Autolykos2_DagAllocate(Autolykos2Context* ctx, size_t bytes) {
if (!ctx) {
return false;
}
uint8_t* newBuf = (uint8_t*)realloc(ctx->dag.buf, bytes == 0 ? 1 : bytes);
if (!newBuf) {
return false;
}
ctx->dag.buf = newBuf;
ctx->dag.cap = bytes;
if (ctx->dag.len > bytes) {
ctx->dag.len = bytes;
}
if (bytes > 0) {
memset(ctx->dag.buf, 0, bytes);
}
return true;
}
bool Autolykos2_DagAppend(Autolykos2Context* ctx, const uint8_t* data, size_t len) {
if (!ctx || !data || len == 0) {
return false;
}
if (ctx->dag.len + len > ctx->dag.cap) {
return false;
}
memcpy(ctx->dag.buf + ctx->dag.len, data, len);
ctx->dag.len += len;
return true;
}
void Autolykos2_DagClear(Autolykos2Context* ctx) {
if (!ctx || !ctx->dag.buf) {
return;
}
memset(ctx->dag.buf, 0, ctx->dag.cap);
ctx->dag.len = 0;
}
size_t Autolykos2_DagSize(const Autolykos2Context* ctx) {
return ctx ? ctx->dag.len : 0;
}
bool Autolykos2_Hash(
Autolykos2Context* ctx,
const uint8_t* message,
size_t messageLen,
uint64_t nonce,
uint32_t height,
uint8_t outHash[32]
) {
if (!ctx || !message || !outHash) {
return false;
}
return Autolykos2_FallbackHash(ctx, message, messageLen, nonce, height, outHash);
}
bool Autolykos2_CheckTarget(
Autolykos2Context* ctx,
const uint8_t message32[32],
uint64_t nonce,
uint32_t height,
const uint8_t target32[32],
uint8_t outHash[32]
) {
if (!ctx || !message32 || !target32 || !outHash) {
return false;
}
#ifdef MINICOIN_AUTOLYKOS2_REF_AVAILABLE
if (ctx->backend) {
const bool ok = minicoin_autolykos2_ref_check_target(ctx->backend, message32, nonce, height, target32);
if (Autolykos2_FallbackHash(ctx, message32, 32, nonce, height, outHash)) {
return ok;
}
return false;
}
#endif
if (!Autolykos2_FallbackHash(ctx, message32, 32, nonce, height, outHash)) {
return false;
}
return Cmp256BE(outHash, target32) <= 0;
}
bool Autolykos2_FindNonceSingleCore(
Autolykos2Context* ctx,
const uint8_t message32[32],
uint32_t height,
const uint8_t target32[32],
uint64_t startNonce,
uint64_t maxIterations,
uint64_t* outNonce,
uint8_t outHash[32]
) {
if (!ctx || !message32 || !target32 || !outNonce || !outHash) {
return false;
}
uint64_t nonce = startNonce;
for (uint64_t i = 0; i < maxIterations; ++i, ++nonce) {
if (Autolykos2_CheckTarget(ctx, message32, nonce, height, target32, outHash)) {
*outNonce = nonce;
return true;
}
}
return false;
}

View File

@@ -0,0 +1,86 @@
#include <cstdint>
#include <cstring>
#include <cpuAutolykos.h>
#include <definitions.h>
// Reference implementation dependency from request.cc. We provide it locally
// to avoid pulling the network/request stack.
uint32_t calcN(uint32_t Hblock) {
uint32_t headerHeight = 0;
((uint8_t*)&headerHeight)[0] = ((uint8_t*)&Hblock)[3];
((uint8_t*)&headerHeight)[1] = ((uint8_t*)&Hblock)[2];
((uint8_t*)&headerHeight)[2] = ((uint8_t*)&Hblock)[1];
((uint8_t*)&headerHeight)[3] = ((uint8_t*)&Hblock)[0];
uint32_t newN = INIT_N_LEN;
if (headerHeight < IncreaseStart) {
newN = INIT_N_LEN;
} else if (headerHeight >= IncreaseEnd) {
newN = MAX_N_LEN;
} else {
uint32_t itersNumber = (headerHeight - IncreaseStart) / IncreasePeriodForN + 1;
for (uint32_t i = 0; i < itersNumber; i++) {
newN = newN / 100 * 105;
}
}
return newN;
}
struct minicoin_autolykos2_ref_handle {
AutolykosAlg* alg;
};
extern "C" void* minicoin_autolykos2_ref_create(void) {
minicoin_autolykos2_ref_handle* h = new minicoin_autolykos2_ref_handle();
h->alg = new AutolykosAlg();
return h;
}
extern "C" void minicoin_autolykos2_ref_destroy(void* handle) {
if (!handle) {
return;
}
auto* h = static_cast<minicoin_autolykos2_ref_handle*>(handle);
delete h->alg;
delete h;
}
extern "C" bool minicoin_autolykos2_ref_check_target(
void* handle,
const uint8_t message32[32],
uint64_t nonce,
uint32_t height,
const uint8_t target32[32]
) {
if (!handle || !message32 || !target32) {
return false;
}
auto* h = static_cast<minicoin_autolykos2_ref_handle*>(handle);
if (!h->alg) {
return false;
}
uint8_t nonceBytes[8];
uint8_t heightBytes[4];
std::memcpy(nonceBytes, &nonce, sizeof(nonceBytes));
// RunAlg expects height bytes; keep deterministic network order.
heightBytes[0] = static_cast<uint8_t>((height >> 24) & 0xffu);
heightBytes[1] = static_cast<uint8_t>((height >> 16) & 0xffu);
heightBytes[2] = static_cast<uint8_t>((height >> 8) & 0xffu);
heightBytes[3] = static_cast<uint8_t>(height & 0xffu);
uint8_t poolBound[32];
std::memcpy(poolBound, target32, sizeof(poolBound));
return h->alg->RunAlg(
const_cast<uint8_t*>(message32),
nonceBytes,
poolBound,
heightBytes
);
}

View File

@@ -0,0 +1,3 @@
#include <easylogging++.h>
INITIALIZE_EASYLOGGINGPP

50
src/blake2/blake2.c Normal file
View File

@@ -0,0 +1,50 @@
#include "../../include/blake2/blake2.h"
#include <openssl/evp.h>
#include <string.h>
static bool Blake2_HashInternal(
const EVP_MD* md,
size_t maxDigestLen,
const uint8_t* input,
size_t inputLen,
uint8_t* out,
size_t outLen
) {
if (!md || !out || outLen == 0 || outLen > maxDigestLen) {
return false;
}
uint8_t digest[EVP_MAX_MD_SIZE];
unsigned int digestLen = 0;
EVP_MD_CTX* ctx = EVP_MD_CTX_new();
if (!ctx) {
return false;
}
bool ok = EVP_DigestInit_ex(ctx, md, NULL) == 1;
if (ok && inputLen > 0) {
ok = EVP_DigestUpdate(ctx, input, inputLen) == 1;
}
if (ok) {
ok = EVP_DigestFinal_ex(ctx, digest, &digestLen) == 1;
}
EVP_MD_CTX_free(ctx);
if (!ok || digestLen < outLen) {
return false;
}
memcpy(out, digest, outLen);
return true;
}
bool Blake2b_Hash(const uint8_t* input, size_t inputLen, uint8_t* out, size_t outLen) {
return Blake2_HashInternal(EVP_blake2b512(), MINICOIN_BLAKE2B_OUTBYTES, input, inputLen, out, outLen);
}
bool Blake2s_Hash(const uint8_t* input, size_t inputLen, uint8_t* out, size_t outLen) {
return Blake2_HashInternal(EVP_blake2s256(), MINICOIN_BLAKE2S_OUTBYTES, input, inputLen, out, outLen);
}

View File

@@ -1,6 +1,23 @@
#include <block/block.h>
#include <autolykos2/autolykos2.h>
#include <stdlib.h>
static Autolykos2Context* g_autolykos2Ctx = NULL;
static Autolykos2Context* GetAutolykos2Ctx(void) {
if (!g_autolykos2Ctx) {
g_autolykos2Ctx = Autolykos2_Create();
}
return g_autolykos2Ctx;
}
void Block_ShutdownPowContext(void) {
if (g_autolykos2Ctx) {
Autolykos2_Destroy(g_autolykos2Ctx);
g_autolykos2Ctx = NULL;
}
}
block_t* Block_Create() {
block_t* block = (block_t*)malloc(sizeof(block_t));
if (!block) {
@@ -116,13 +133,28 @@ void Block_CalculateMerkleRoot(const block_t* block, uint8_t* outHash) {
DynArr_destroy(hashes2);
}
void Block_CalculateRandomXHash(const block_t* block, uint8_t* outHash) {
void Block_CalculateAutolykos2Hash(const block_t* block, uint8_t* outHash) {
if (!block || !outHash) {
return;
}
// PoW hash is also computed from the header only.
RandomX_CalculateHash((const uint8_t*)&block->header, sizeof(block_header_t), outHash);
// PoW hash is computed from the block header, while canonical block hash remains SHA256.
Autolykos2Context* ctx = GetAutolykos2Ctx();
if (!ctx) {
memset(outHash, 0, 32);
return;
}
if (!Autolykos2_Hash(
ctx,
(const uint8_t*)&block->header,
sizeof(block_header_t),
block->header.nonce,
(uint32_t)block->header.blockNumber,
outHash
)) {
memset(outHash, 0, 32);
}
}
void Block_AddTransaction(block_t* block, signed_transaction_t* tx) {
@@ -199,7 +231,7 @@ bool Block_HasValidProofOfWork(const block_t* block) {
}
uint8_t hash[32];
Block_CalculateRandomXHash(block, hash);
Block_CalculateAutolykos2Hash(block, hash);
return Uint256_CompareBE(hash, target) <= 0;
}

View File

@@ -162,6 +162,8 @@ bool Chain_SaveToFile(blockchain_t* chain, const char* dirpath, uint256_t curren
fwrite(zeroHash, sizeof(uint8_t), 32, metaFile);
uint256_t zeroSupply = {0};
fwrite(&zeroSupply, sizeof(uint256_t), 1, metaFile);
uint32_t initialTarget = INITIAL_DIFFICULTY;
fwrite(&initialTarget, sizeof(uint32_t), 1, metaFile);
// TODO: Potentially some other things here, we'll see
}
@@ -215,6 +217,9 @@ bool Chain_SaveToFile(blockchain_t* chain, const char* dirpath, uint256_t curren
}
fclose(blockFile);
DynArr_destroy(blk->transactions);
blk->transactions = NULL; // Clear transactions to save memory since they're now saved on disk
}
// Update metadata with new size and last block hash
@@ -228,12 +233,14 @@ bool Chain_SaveToFile(blockchain_t* chain, const char* dirpath, uint256_t curren
fwrite(lastHash, sizeof(uint8_t), 32, metaFile);
}
fwrite(&currentSupply, sizeof(uint256_t), 1, metaFile);
uint32_t difficultyTarget = ((block_t*)DynArr_at(chain->blocks, newSize - 1))->header.difficultyTarget;
fwrite(&difficultyTarget, sizeof(uint32_t), 1, metaFile);
fclose(metaFile);
return true;
}
bool Chain_LoadFromFile(blockchain_t* chain, const char* dirpath, uint256_t* outCurrentSupply) {
bool Chain_LoadFromFile(blockchain_t* chain, const char* dirpath, uint256_t* outCurrentSupply, uint32_t* outDifficultyTarget) {
if (!chain || !chain->blocks || !dirpath || !outCurrentSupply) {
return false;
}
@@ -259,6 +266,7 @@ bool Chain_LoadFromFile(blockchain_t* chain, const char* dirpath, uint256_t* out
uint8_t lastSavedHash[32];
fread(lastSavedHash, sizeof(uint8_t), 32, metaFile);
fread(outCurrentSupply, sizeof(uint256_t), 1, metaFile);
fread(outDifficultyTarget, sizeof(uint32_t), 1, metaFile);
fclose(metaFile);
// TODO: Might add a flag to allow reading from a point onward, but just rewrite for now
@@ -308,11 +316,9 @@ bool Chain_LoadFromFile(blockchain_t* chain, const char* dirpath, uint256_t* out
fclose(blockFile);
Chain_AddBlock(chain, blk);
if (blk->transactions) {
DynArr_destroy(blk->transactions);
blk->transactions = NULL;
}
free(blk); // chain stores block headers/fields by value
// Chain_AddBlock stores blocks by value, so the copied block now owns
// blk->transactions. Only free the temporary wrapper struct here.
free(blk);
}
chain->size = savedSize;
@@ -320,3 +326,78 @@ bool Chain_LoadFromFile(blockchain_t* chain, const char* dirpath, uint256_t* out
// After read, you SHOULD verify chain validity. We're not doing it here since returning false is a bit unclear if the read failed or if the chain is invalid.
return true;
}
uint32_t Chain_ComputeNextTarget(blockchain_t* chain, uint32_t currentTarget) {
if (!chain || !chain->blocks) {
return 0x00; // Impossible difficulty, only valid hash is all zeros (practically impossible)
}
size_t chainSize = DynArr_size(chain->blocks);
if (chainSize < DIFFICULTY_ADJUSTMENT_INTERVAL) {
// Baby-chain, return initial difficulty
return INITIAL_DIFFICULTY;
}
// Assuming block validation validates timestamps, we can assume they're valid and can just read them
block_t* lastBlock = (block_t*)DynArr_at(chain->blocks, chainSize - 1);
block_t* adjustmentBlock = (block_t*)DynArr_at(chain->blocks, chainSize - DIFFICULTY_ADJUSTMENT_INTERVAL);
if (!lastBlock || !adjustmentBlock) {
return 0x00; // Impossible difficulty, only valid hash is all zeros (practically impossible)
}
// Retarget uses whole-window span. Per-block average is implicit:
// (actualTime / interval) / targetBlockTime == actualTime / targetTime.
uint64_t actualTime = 0;
if (lastBlock->header.timestamp > adjustmentBlock->header.timestamp) {
actualTime = lastBlock->header.timestamp - adjustmentBlock->header.timestamp;
}
if (actualTime == 0) {
return currentTarget; // Invalid/non-increasing time window; keep current target
}
const uint64_t targetTime = (uint64_t)TARGET_BLOCK_TIME * (uint64_t)DIFFICULTY_ADJUSTMENT_INTERVAL;
double timeRatio = (double)actualTime / (double)targetTime;
// Clamp per-epoch target movement: at most x2 easier or x2 harder. TODO: Check if the clamp should be more aggressive or looser
if (timeRatio > 2.0) {
timeRatio = 2.0;
} else if (timeRatio < 0.5) {
timeRatio = 0.5;
}
uint32_t exponent = currentTarget >> 24;
uint32_t mantissa = currentTarget & 0x007fffff;
if (mantissa == 0 || exponent == 0) {
return INITIAL_DIFFICULTY;
}
double newMantissa = (double)mantissa * timeRatio;
// Normalize to compact format range.
while (newMantissa > 8388607.0) { // 0x007fffff
newMantissa /= 256.0;
exponent++;
}
while (newMantissa > 0.0 && newMantissa < 32768.0 && exponent > 3) { // Keep coefficient in normal range
newMantissa *= 256.0;
exponent--;
}
if (exponent > 32) {
// Easiest representable target in our decoder range.
return (32u << 24) | 0x007fffff;
}
if (exponent < 1) {
exponent = 1;
}
uint32_t newCoeff = (uint32_t)newMantissa;
if (newCoeff == 0) {
newCoeff = 1;
}
if (newCoeff > 0x007fffff) {
newCoeff = 0x007fffff;
}
return (exponent << 24) | (newCoeff & 0x007fffff);
}

View File

@@ -8,7 +8,6 @@
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <randomx/librx_wrapper.h>
#include <signal.h>
#include <constants.h>
@@ -19,56 +18,11 @@
void handle_sigint(int sig) {
printf("Caught signal %d, exiting...\n", sig);
RandomX_Destroy();
Block_ShutdownPowContext();
exit(0);
}
static double MonotonicSeconds(void) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (double)ts.tv_sec + ((double)ts.tv_nsec / 1000000000.0);
}
static double MeasureRandomXHashrate(void) {
uint8_t input[80] = {0};
uint8_t outHash[32];
uint64_t counter = 0;
const double start = MonotonicSeconds();
double now = start;
do {
memcpy(input, &counter, sizeof(counter));
RandomX_CalculateHash(input, sizeof(input), outHash);
counter++;
now = MonotonicSeconds();
} while ((now - start) < 0.75); // short local benchmark window
const double elapsed = now - start;
if (elapsed <= 0.0 || counter == 0) {
return 0.0;
}
return (double)counter / elapsed;
}
static uint32_t CompactTargetForExpectedHashes(double expectedHashes) {
if (expectedHashes < 1.0) {
expectedHashes = 1.0;
}
// For exponent 0x1f: target = mantissa * 2^(8*(0x1f-3)) = mantissa * 2^224
// So expected hashes ~= 2^256 / target = 2^32 / mantissa.
double mantissaF = 4294967296.0 / expectedHashes;
if (mantissaF < 1.0) {
mantissaF = 1.0;
}
if (mantissaF > 8388607.0) {
mantissaF = 8388607.0; // 0x007fffff
}
const uint32_t mantissa = (uint32_t)mantissaF;
return (0x1fU << 24) | (mantissa & 0x007fffffU);
}
uint32_t difficultyTarget = INITIAL_DIFFICULTY;
static bool MineBlock(block_t* block) {
if (!block) {
@@ -91,24 +45,18 @@ int main(int argc, char* argv[]) {
signal(SIGINT, handle_sigint);
const char* chainDataDir = CHAIN_DATA_DIR;
const uint64_t blocksToMine = 10;
const uint64_t blocksToMine = 1000;
const double targetSeconds = TARGET_BLOCK_TIME;
uint256_t currentSupply = uint256_from_u64(0);
// Init RandomX
if (!RandomX_Init("minicoin", false)) { // TODO: Use a key that is not hardcoded; E.g. hash of the last block, every thousand blocks, difficulty recalibration, etc.
fprintf(stderr, "failed to initialize RandomX\n");
return 1;
}
blockchain_t* chain = Chain_Create();
if (!chain) {
fprintf(stderr, "failed to create chain\n");
return 1;
}
if (!Chain_LoadFromFile(chain, chainDataDir, &currentSupply)) {
if (!Chain_LoadFromFile(chain, chainDataDir, &currentSupply, &difficultyTarget)) {
printf("No existing chain loaded from %s\n", chainDataDir);
}
@@ -138,17 +86,6 @@ int main(int argc, char* argv[]) {
if (argc > 1 && strcmp(argv[1], "-mine") == 0) {
printf("Mining %llu blocks with target time %.0fs...\n", (unsigned long long)blocksToMine, targetSeconds);
const double hps = MeasureRandomXHashrate();
const double expectedHashes = (hps > 0.0) ? (hps * targetSeconds) : 65536.0;
const uint32_t calibratedBits = CompactTargetForExpectedHashes(expectedHashes);
//const uint32_t calibratedBits = 0xffffffff; // Absurdly low diff for testing
printf("RandomX benchmark: %.2f H/s, target %.0fs, nBits=0x%08x, diff=%.2f\n",
hps,
targetSeconds,
calibratedBits,
(double)(1.f / calibratedBits) * 4294967296.0); // Basic representation as a big number for now
uint8_t minerAddress[32];
SHA256((const unsigned char*)"minicoin-miner-1", strlen("minicoin-miner-1"), minerAddress);
@@ -157,7 +94,7 @@ int main(int argc, char* argv[]) {
if (!block) {
fprintf(stderr, "failed to create block\n");
Chain_Destroy(chain);
RandomX_Destroy();
Block_ShutdownPowContext();
return 1;
}
@@ -174,7 +111,7 @@ int main(int argc, char* argv[]) {
memset(block->header.prevHash, 0, sizeof(block->header.prevHash));
}
block->header.timestamp = (uint64_t)time(NULL);
block->header.difficultyTarget = calibratedBits;
block->header.difficultyTarget = difficultyTarget;
block->header.nonce = 0;
signed_transaction_t coinbaseTx;
@@ -195,7 +132,7 @@ int main(int argc, char* argv[]) {
fprintf(stderr, "failed to mine block within nonce range\n");
Block_Destroy(block);
Chain_Destroy(chain);
RandomX_Destroy();
Block_ShutdownPowContext();
return 1;
}
@@ -203,28 +140,36 @@ int main(int argc, char* argv[]) {
fprintf(stderr, "failed to append block to chain\n");
Block_Destroy(block);
Chain_Destroy(chain);
RandomX_Destroy();
Block_ShutdownPowContext();
return 1;
}
(void)uint256_add_u64(&currentSupply, coinbaseTx.transaction.amount);
uint8_t blockHash[32];
Block_CalculateHash(block, blockHash);
printf("Mined block %llu/%llu (height=%llu) nonce=%llu reward=%llu supply=%llu merkle=%02x%02x%02x%02x... hash=%02x%02x%02x%02x...\n",
uint8_t canonicalHash[32];
uint8_t powHash[32];
Block_CalculateHash(block, canonicalHash);
Block_CalculateAutolykos2Hash(block, powHash);
printf("Mined block %llu/%llu (height=%llu) nonce=%llu reward=%llu supply=%llu diff=%#x merkle=%02x%02x%02x%02x... pow=%02x%02x%02x%02x... canonical=%02x%02x%02x%02x...\n",
(unsigned long long)(mined + 1),
(unsigned long long)blocksToMine,
(unsigned long long)block->header.blockNumber,
(unsigned long long)block->header.nonce,
(unsigned long long)coinbaseTx.transaction.amount,
(unsigned long long)currentSupply.limbs[0],
(unsigned int)block->header.difficultyTarget,
block->header.merkleRoot[0], block->header.merkleRoot[1], block->header.merkleRoot[2], block->header.merkleRoot[3],
blockHash[0], blockHash[1], blockHash[2], blockHash[3]);
powHash[0], powHash[1], powHash[2], powHash[3],
canonicalHash[0], canonicalHash[1], canonicalHash[2], canonicalHash[3]);
free(block); // chain stores blocks by value; transactions are owned by chain copy
// Save chain after each mined block
Chain_SaveToFile(chain, chainDataDir, currentSupply);
if (Chain_Size(chain) % DIFFICULTY_ADJUSTMENT_INTERVAL == 0) {
difficultyTarget = Chain_ComputeNextTarget(chain, difficultyTarget);
}
}
if (!Chain_SaveToFile(chain, chainDataDir, currentSupply)) {
@@ -248,6 +193,6 @@ int main(int argc, char* argv[]) {
}
Chain_Destroy(chain);
RandomX_Destroy();
Block_ShutdownPowContext();
return 0;
}

View File

@@ -1,75 +0,0 @@
#include <randomx/librx_wrapper.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
static randomx_cache* rxCache = NULL;
static randomx_dataset* rxDataset = NULL;
static randomx_vm* rxVm = NULL;
bool RandomX_Init(const char* key, bool preferFullMemory) {
if (!key || rxCache || rxVm) {
return false;
}
const randomx_flags baseFlags = randomx_get_flags() | RANDOMX_FLAG_JIT;
randomx_flags vmFlags = baseFlags | RANDOMX_FLAG_FULL_MEM;
rxCache = randomx_alloc_cache(baseFlags);
if (!rxCache) {
return false;
}
randomx_init_cache(rxCache, key, strlen(key));
// Prefer full-memory mode. If dataset allocation fails, fall back to light mode.
if (preferFullMemory) {
rxDataset = randomx_alloc_dataset(vmFlags);
if (rxDataset) {
const unsigned long datasetItems = randomx_dataset_item_count();
randomx_init_dataset(rxDataset, rxCache, 0, datasetItems);
rxVm = randomx_create_vm(vmFlags, NULL, rxDataset);
if (rxVm) {
printf("RandomX initialized in full-memory mode\n");
return true;
}
randomx_release_dataset(rxDataset);
rxDataset = NULL;
}
}
vmFlags = baseFlags;
rxVm = randomx_create_vm(vmFlags, rxCache, NULL);
if (!rxVm) {
randomx_release_cache(rxCache);
rxCache = NULL;
return false;
}
printf("RandomX initialized in light mode\n");
return true;
}
void RandomX_Destroy() {
if (rxVm) {
randomx_destroy_vm(rxVm);
rxVm = NULL;
}
if (rxDataset) {
randomx_release_dataset(rxDataset);
rxDataset = NULL;
}
if (rxCache) {
randomx_release_cache(rxCache);
rxCache = NULL;
}
}
void RandomX_CalculateHash(const uint8_t* input, size_t inputLen, uint8_t* output) {
if (!rxVm || !input || !output) {
return;
}
randomx_calculate_hash(rxVm, input, inputLen, output);
}