Full DAG size, epoch scaling etc.
This commit is contained in:
@@ -4,6 +4,7 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <constants.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -16,6 +17,7 @@ void Autolykos2_Destroy(Autolykos2Context* ctx);
|
|||||||
|
|
||||||
bool Autolykos2_DagAllocate(Autolykos2Context* ctx, size_t bytes);
|
bool Autolykos2_DagAllocate(Autolykos2Context* ctx, size_t bytes);
|
||||||
bool Autolykos2_DagAppend(Autolykos2Context* ctx, const uint8_t* data, size_t len);
|
bool Autolykos2_DagAppend(Autolykos2Context* ctx, const uint8_t* data, size_t len);
|
||||||
|
bool Autolykos2_DagGenerate(Autolykos2Context* ctx, const uint8_t seed32[32]);
|
||||||
void Autolykos2_DagClear(Autolykos2Context* ctx);
|
void Autolykos2_DagClear(Autolykos2Context* ctx);
|
||||||
size_t Autolykos2_DagSize(const Autolykos2Context* ctx);
|
size_t Autolykos2_DagSize(const Autolykos2Context* ctx);
|
||||||
|
|
||||||
@@ -24,15 +26,17 @@ bool Autolykos2_Hash(
|
|||||||
const uint8_t* message,
|
const uint8_t* message,
|
||||||
size_t messageLen,
|
size_t messageLen,
|
||||||
uint64_t nonce,
|
uint64_t nonce,
|
||||||
uint32_t height,
|
uint64_t height,
|
||||||
uint8_t outHash[32]
|
uint8_t outHash[32]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
bool Autolykos2_LightHash(const uint8_t* seed, blockchain_t* chain, uint64_t nonce, uint8_t* out);
|
||||||
|
|
||||||
bool Autolykos2_CheckTarget(
|
bool Autolykos2_CheckTarget(
|
||||||
Autolykos2Context* ctx,
|
Autolykos2Context* ctx,
|
||||||
const uint8_t message32[32],
|
const uint8_t message32[32],
|
||||||
uint64_t nonce,
|
uint64_t nonce,
|
||||||
uint32_t height,
|
uint64_t height,
|
||||||
const uint8_t target32[32],
|
const uint8_t target32[32],
|
||||||
uint8_t outHash[32]
|
uint8_t outHash[32]
|
||||||
);
|
);
|
||||||
@@ -40,7 +44,7 @@ bool Autolykos2_CheckTarget(
|
|||||||
bool Autolykos2_FindNonceSingleCore(
|
bool Autolykos2_FindNonceSingleCore(
|
||||||
Autolykos2Context* ctx,
|
Autolykos2Context* ctx,
|
||||||
const uint8_t message32[32],
|
const uint8_t message32[32],
|
||||||
uint32_t height,
|
uint64_t height,
|
||||||
const uint8_t target32[32],
|
const uint8_t target32[32],
|
||||||
uint64_t startNonce,
|
uint64_t startNonce,
|
||||||
uint64_t maxIterations,
|
uint64_t maxIterations,
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ block_t* Block_Create();
|
|||||||
void Block_CalculateHash(const block_t* block, uint8_t* outHash);
|
void Block_CalculateHash(const block_t* block, uint8_t* outHash);
|
||||||
void Block_CalculateMerkleRoot(const block_t* block, uint8_t* outHash);
|
void Block_CalculateMerkleRoot(const block_t* block, uint8_t* outHash);
|
||||||
void Block_CalculateAutolykos2Hash(const block_t* block, uint8_t* outHash);
|
void Block_CalculateAutolykos2Hash(const block_t* block, uint8_t* outHash);
|
||||||
|
bool Block_RebuildAutolykos2Dag(size_t dagBytes, const uint8_t seed32[32]);
|
||||||
void Block_AddTransaction(block_t* block, signed_transaction_t* tx);
|
void Block_AddTransaction(block_t* block, signed_transaction_t* tx);
|
||||||
void Block_RemoveTransaction(block_t* block, uint8_t* txHash);
|
void Block_RemoveTransaction(block_t* block, uint8_t* txHash);
|
||||||
bool Block_HasValidProofOfWork(const block_t* block);
|
bool Block_HasValidProofOfWork(const block_t* block);
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
// Max adjustment per is x2. So if blocks are coming in too fast, the difficulty will at most double every 24 hours, and vice versa if they're coming in too slow.
|
// Max adjustment per is x2. So if blocks are coming in too fast, the difficulty will at most double every 24 hours, and vice versa if they're coming in too slow.
|
||||||
#define TARGET_BLOCK_TIME 90 // Target block time in seconds
|
#define TARGET_BLOCK_TIME 90 // Target block time in seconds
|
||||||
//#define INITIAL_DIFFICULTY 0x1f0c1422 // Default compact target used by Autolykos2 PoW (This is ridiculously low)
|
//#define INITIAL_DIFFICULTY 0x1f0c1422 // Default compact target used by Autolykos2 PoW (This is ridiculously low)
|
||||||
#define INITIAL_DIFFICULTY 0x1d1b7c51 // This takes 90s on my machine with a single thread, good for testing
|
#define INITIAL_DIFFICULTY 0x1f1b7c51 // This takes 90s on my machine with a single thread, good for testing
|
||||||
|
|
||||||
// Reward schedule acceleration: 1 means normal-speed progression.
|
// Reward schedule acceleration: 1 means normal-speed progression.
|
||||||
#define EMISSION_ACCELERATION_FACTOR 1ULL
|
#define EMISSION_ACCELERATION_FACTOR 1ULL
|
||||||
@@ -29,16 +29,14 @@
|
|||||||
|
|
||||||
// Future Autolykos2 constants:
|
// Future Autolykos2 constants:
|
||||||
#define EPOCH_LENGTH 350000 // ~1 year at 90s
|
#define EPOCH_LENGTH 350000 // ~1 year at 90s
|
||||||
#define BASE_DAG_SIZE (2ULL << 30) // 2 GB
|
#define DAG_BASE_GROWTH (1ULL << 30) // 1 GB per epoch, adjusted by acceleration
|
||||||
#define DAG_BASE_GROWTH (1ULL << 30) // 1 GB, calculated fully later
|
#define DAG_BASE_SIZE (6ULL << 30) // 6 GB, adjusted per cycle based off DAG_BASE_GROWTH
|
||||||
#define DAG_BASE_CAP (8ULL << 30) // 8 GB, adjusted per cycle based off DAG_BASE_GROWTH
|
|
||||||
// Swings - calculated as MIN(percentage, absolute GB) to prevent absurd swings from low hashrate or very large DAG growth
|
// Swings - calculated as MIN(percentage, absolute GB) to prevent absurd swings from low hashrate or very large DAG growth
|
||||||
#define DAG_MAX_UP_SWING_PERCENTAGE 1.3 // 30%
|
#define DAG_MAX_UP_SWING_PERCENTAGE 1.15 // 15%
|
||||||
#define DAG_MAX_DOWN_SWING_PERCENTAGE 0.85 // 15%
|
#define DAG_MAX_DOWN_SWING_PERCENTAGE 0.90 // 10%
|
||||||
#define DAG_MAX_UP_SWING_GB (4ULL << 30) // 4 GB
|
#define DAG_MAX_UP_SWING_GB (2ULL << 30) // 2 GB
|
||||||
#define DAG_MAX_DOWN_SWING_GB (1ULL << 30) // 1 GB
|
#define DAG_MAX_DOWN_SWING_GB (1ULL << 30) // 1 GB
|
||||||
#define KICKOUT_TARGET_PERCENTAGE 75
|
#define DAG_GENESIS_SEED 0x00 // Genesis seed is zeroes, every epoch's seed is the hash of the previous block, therefore unpredictable until the block is mined
|
||||||
#define KICKOUT_TARGET_BLOCK 30000 // 1 month at 90s block time
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Each epoch has 2 phases, connected logarithmically:
|
* Each epoch has 2 phases, connected logarithmically:
|
||||||
@@ -149,4 +147,64 @@ static inline uint64_t CalculateBlockReward(uint256_t currentSupply, blockchain_
|
|||||||
return GetInflationRateReward(currentSupply, chain);
|
return GetInflationRateReward(currentSupply, chain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hashing DAG
|
||||||
|
#include <math.h>
|
||||||
|
static inline size_t CalculateTargetDAGSize(blockchain_t* chain) {
|
||||||
|
// Base size plus (base growth * difficulty factor), adjusted by acceleration
|
||||||
|
if (!chain || !chain->blocks) { return 0; } // Invalid
|
||||||
|
uint64_t height = (uint64_t)Chain_Size(chain);
|
||||||
|
|
||||||
|
if (height < EPOCH_LENGTH) {
|
||||||
|
return DAG_BASE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the height - EPOCH_LENGTH block and the last block;
|
||||||
|
block_t* lastBlock = Chain_GetBlock(chain, Chain_Size(chain) - 1);
|
||||||
|
block_t* epochStartBlock = Chain_GetBlock(chain, (size_t)(Chain_Size(chain) - 1 - EPOCH_LENGTH));
|
||||||
|
if (!lastBlock || !epochStartBlock) {
|
||||||
|
return 0; // Invalid
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t difficultyDelta = (int64_t)epochStartBlock->header.difficultyTarget - (int64_t)lastBlock->header.difficultyTarget;
|
||||||
|
int64_t growth = (DAG_BASE_GROWTH * difficultyDelta); // Can be negative if difficulty has decreased, which is why we use int64_t
|
||||||
|
|
||||||
|
// Clamp
|
||||||
|
if (growth > 0) {
|
||||||
|
// Difficulty increased -> Clamp the UPWARD swing
|
||||||
|
int64_t maxUp = (int64_t)((DAG_BASE_SIZE * 15) / 100); // 15%
|
||||||
|
if (growth > maxUp) growth = maxUp;
|
||||||
|
if (growth > (int64_t)DAG_MAX_UP_SWING_GB) growth = DAG_MAX_UP_SWING_GB;
|
||||||
|
} else {
|
||||||
|
// Difficulty decreased -> Clamp the DOWNWARD swing
|
||||||
|
int64_t maxDown = (int64_t)((DAG_BASE_SIZE * 10) / 100); // 10%
|
||||||
|
if (-growth > maxDown) growth = -maxDown;
|
||||||
|
if (-growth > (int64_t)DAG_MAX_DOWN_SWING_GB) growth = -(int64_t)DAG_MAX_DOWN_SWING_GB;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t targetSize = (int64_t)DAG_BASE_SIZE + growth;
|
||||||
|
if (targetSize <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (size_t)targetSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void GetNextDAGSeed(blockchain_t* chain, uint8_t outSeed[32]) {
|
||||||
|
if (!chain || !chain->blocks || !outSeed) { return; } // Invalid
|
||||||
|
uint64_t height = (uint64_t)Chain_Size(chain);
|
||||||
|
|
||||||
|
if (height < EPOCH_LENGTH) {
|
||||||
|
memset(outSeed, DAG_GENESIS_SEED, 32);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
block_t* prevBlock = Chain_GetBlock(chain, Chain_Size(chain) - 1);
|
||||||
|
if (!prevBlock) {
|
||||||
|
memset(outSeed, 0x00, 32); // Fallback to zeroes if we can't get the previous block for some reason; The caller should treat this as an error if height >= EPOCH_LENGTH
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Block_CalculateHash(prevBlock, outSeed);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -29,32 +29,31 @@ extern bool minicoin_autolykos2_ref_check_target(
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
static bool Autolykos2_FallbackHash(
|
static bool Autolykos2_FallbackHash(
|
||||||
const Autolykos2Context* ctx,
|
const uint8_t seed32[32],
|
||||||
const uint8_t* message,
|
const uint8_t* message,
|
||||||
size_t messageLen,
|
size_t messageLen,
|
||||||
uint64_t nonce,
|
uint64_t nonce,
|
||||||
uint32_t height,
|
uint64_t height,
|
||||||
uint8_t outHash[32]
|
uint8_t outHash[32]
|
||||||
) {
|
) {
|
||||||
|
if (!seed32 || !outHash) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t nonceBytes[8];
|
uint8_t nonceBytes[8];
|
||||||
uint8_t heightBytes[4];
|
uint8_t heightBytes[8];
|
||||||
memcpy(nonceBytes, &nonce, sizeof(nonceBytes));
|
memcpy(nonceBytes, &nonce, sizeof(nonceBytes));
|
||||||
memcpy(heightBytes, &height, sizeof(heightBytes));
|
memcpy(heightBytes, &height, sizeof(heightBytes));
|
||||||
|
|
||||||
size_t dagChunkLen = 0;
|
const size_t totalLen = 32 + messageLen + sizeof(nonceBytes) + sizeof(heightBytes);
|
||||||
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);
|
uint8_t* material = (uint8_t*)malloc(totalLen == 0 ? 1 : totalLen);
|
||||||
if (!material) {
|
if (!material) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t off = 0;
|
size_t off = 0;
|
||||||
|
memcpy(material + off, seed32, 32);
|
||||||
|
off += 32;
|
||||||
if (messageLen > 0) {
|
if (messageLen > 0) {
|
||||||
memcpy(material + off, message, messageLen);
|
memcpy(material + off, message, messageLen);
|
||||||
off += messageLen;
|
off += messageLen;
|
||||||
@@ -62,10 +61,6 @@ static bool Autolykos2_FallbackHash(
|
|||||||
memcpy(material + off, nonceBytes, sizeof(nonceBytes));
|
memcpy(material + off, nonceBytes, sizeof(nonceBytes));
|
||||||
off += sizeof(nonceBytes);
|
off += sizeof(nonceBytes);
|
||||||
memcpy(material + off, heightBytes, sizeof(heightBytes));
|
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);
|
const bool ok = Blake2b_Hash(material, totalLen, outHash, 32);
|
||||||
free(material);
|
free(material);
|
||||||
@@ -80,6 +75,172 @@ static int Cmp256BE(const uint8_t a[32], const uint8_t b[32]) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void WriteU64LE(uint64_t v, uint8_t out[8]) {
|
||||||
|
out[0] = (uint8_t)(v & 0xffu);
|
||||||
|
out[1] = (uint8_t)((v >> 8) & 0xffu);
|
||||||
|
out[2] = (uint8_t)((v >> 16) & 0xffu);
|
||||||
|
out[3] = (uint8_t)((v >> 24) & 0xffu);
|
||||||
|
out[4] = (uint8_t)((v >> 32) & 0xffu);
|
||||||
|
out[5] = (uint8_t)((v >> 40) & 0xffu);
|
||||||
|
out[6] = (uint8_t)((v >> 48) & 0xffu);
|
||||||
|
out[7] = (uint8_t)((v >> 56) & 0xffu);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void WriteU32LE(uint32_t v, uint8_t out[4]) {
|
||||||
|
out[0] = (uint8_t)(v & 0xffu);
|
||||||
|
out[1] = (uint8_t)((v >> 8) & 0xffu);
|
||||||
|
out[2] = (uint8_t)((v >> 16) & 0xffu);
|
||||||
|
out[3] = (uint8_t)((v >> 24) & 0xffu);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool DeriveSeedFromMessage(const uint8_t* message, size_t messageLen, uint8_t outSeed[32]) {
|
||||||
|
if (!message || !outSeed) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Blake2b_Hash(message, messageLen, outSeed, 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ComputeLaneIndex(
|
||||||
|
const uint8_t seed32[32],
|
||||||
|
uint64_t nonce,
|
||||||
|
uint64_t height,
|
||||||
|
uint32_t round,
|
||||||
|
uint32_t laneCount,
|
||||||
|
uint32_t* outLane
|
||||||
|
) {
|
||||||
|
if (!seed32 || !outLane || laneCount == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t input[32 + 8 + 8 + 4];
|
||||||
|
uint8_t digest[32];
|
||||||
|
uint8_t nonceBytes[8];
|
||||||
|
uint8_t heightBytes[8];
|
||||||
|
uint8_t roundBytes[4];
|
||||||
|
|
||||||
|
WriteU64LE(nonce, nonceBytes);
|
||||||
|
WriteU64LE(height, heightBytes);
|
||||||
|
WriteU32LE(round, roundBytes);
|
||||||
|
|
||||||
|
memcpy(input, seed32, 32);
|
||||||
|
memcpy(input + 32, nonceBytes, sizeof(nonceBytes));
|
||||||
|
memcpy(input + 40, heightBytes, sizeof(heightBytes));
|
||||||
|
memcpy(input + 48, roundBytes, sizeof(roundBytes));
|
||||||
|
|
||||||
|
if (!Blake2b_Hash(input, sizeof(input), digest, sizeof(digest))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t raw =
|
||||||
|
((uint32_t)digest[0]) |
|
||||||
|
((uint32_t)digest[1] << 8) |
|
||||||
|
((uint32_t)digest[2] << 16) |
|
||||||
|
((uint32_t)digest[3] << 24);
|
||||||
|
*outLane = raw % laneCount;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ReadDagLaneFromContext(const Autolykos2Context* ctx, uint32_t laneIndex, uint8_t outLane[32]) {
|
||||||
|
if (!ctx || !ctx->dag.buf || !outLane) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t offset = (size_t)laneIndex * 32u;
|
||||||
|
if (ctx->dag.len < offset + 32u) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(outLane, ctx->dag.buf + offset, 32);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ReadDagLaneFromSeed(const uint8_t seed32[32], uint32_t laneIndex, uint8_t outLane[32]) {
|
||||||
|
if (!seed32 || !outLane) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint64_t counter = (uint64_t)laneIndex / 2u;
|
||||||
|
const bool upperHalf = (laneIndex & 1u) != 0u;
|
||||||
|
uint8_t input[32 + 8];
|
||||||
|
uint8_t digest[64];
|
||||||
|
uint8_t counterBytes[8];
|
||||||
|
|
||||||
|
WriteU64LE(counter, counterBytes);
|
||||||
|
memcpy(input, seed32, 32);
|
||||||
|
memcpy(input + 32, counterBytes, sizeof(counterBytes));
|
||||||
|
|
||||||
|
if (!Blake2b_Hash(input, sizeof(input), digest, sizeof(digest))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(outLane, digest + (upperHalf ? 32 : 0), 32);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool Autolykos2_HashCore(
|
||||||
|
const uint8_t seed32[32],
|
||||||
|
const uint8_t* message,
|
||||||
|
size_t messageLen,
|
||||||
|
uint64_t nonce,
|
||||||
|
uint64_t height,
|
||||||
|
uint32_t laneCount,
|
||||||
|
const Autolykos2Context* ctx,
|
||||||
|
bool useContextDag,
|
||||||
|
uint8_t outHash[32]
|
||||||
|
) {
|
||||||
|
if (!seed32 || !message || !outHash || laneCount == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t acc[32];
|
||||||
|
memset(acc, 0, sizeof(acc));
|
||||||
|
|
||||||
|
for (uint32_t round = 0; round < 32; ++round) {
|
||||||
|
uint32_t laneIndex = 0;
|
||||||
|
uint8_t lane[32];
|
||||||
|
|
||||||
|
if (!ComputeLaneIndex(seed32, nonce, height, round, laneCount, &laneIndex)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useContextDag) {
|
||||||
|
if (!ReadDagLaneFromContext(ctx, laneIndex, lane)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!ReadDagLaneFromSeed(seed32, laneIndex, lane)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 32; ++i) {
|
||||||
|
acc[i] ^= lane[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t baseHash[32];
|
||||||
|
uint8_t accHash[32];
|
||||||
|
if (!Autolykos2_FallbackHash(seed32, message, messageLen, nonce, height, baseHash)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!Blake2b_Hash(acc, sizeof(acc), accHash, sizeof(accHash))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t finalInput[32 + 32 + 8 + 8];
|
||||||
|
uint8_t nonceBytes[8];
|
||||||
|
uint8_t heightBytes[8];
|
||||||
|
WriteU64LE(nonce, nonceBytes);
|
||||||
|
WriteU64LE(height, heightBytes);
|
||||||
|
|
||||||
|
memcpy(finalInput, baseHash, 32);
|
||||||
|
memcpy(finalInput + 32, accHash, 32);
|
||||||
|
memcpy(finalInput + 64, nonceBytes, 8);
|
||||||
|
memcpy(finalInput + 72, heightBytes, 8);
|
||||||
|
return Blake2b_Hash(finalInput, sizeof(finalInput), outHash, 32);
|
||||||
|
}
|
||||||
|
|
||||||
Autolykos2Context* Autolykos2_Create(void) {
|
Autolykos2Context* Autolykos2_Create(void) {
|
||||||
Autolykos2Context* ctx = (Autolykos2Context*)calloc(1, sizeof(Autolykos2Context));
|
Autolykos2Context* ctx = (Autolykos2Context*)calloc(1, sizeof(Autolykos2Context));
|
||||||
if (!ctx) {
|
if (!ctx) {
|
||||||
@@ -144,6 +305,38 @@ bool Autolykos2_DagAppend(Autolykos2Context* ctx, const uint8_t* data, size_t le
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Autolykos2_DagGenerate(Autolykos2Context* ctx, const uint8_t seed32[32]) {
|
||||||
|
if (!ctx || !seed32 || !ctx->dag.buf || ctx->dag.cap == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t input[32 + 8];
|
||||||
|
uint8_t digest[64];
|
||||||
|
size_t offset = 0;
|
||||||
|
uint64_t counter = 0;
|
||||||
|
|
||||||
|
while (offset < ctx->dag.cap) {
|
||||||
|
WriteU64LE(counter, input + 32);
|
||||||
|
memcpy(input, seed32, 32);
|
||||||
|
|
||||||
|
if (!Blake2b_Hash(input, sizeof(input), digest, sizeof(digest))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t chunk = ctx->dag.cap - offset;
|
||||||
|
if (chunk > sizeof(digest)) {
|
||||||
|
chunk = sizeof(digest);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(ctx->dag.buf + offset, digest, chunk);
|
||||||
|
offset += chunk;
|
||||||
|
++counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->dag.len = ctx->dag.cap;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void Autolykos2_DagClear(Autolykos2Context* ctx) {
|
void Autolykos2_DagClear(Autolykos2Context* ctx) {
|
||||||
if (!ctx || !ctx->dag.buf) {
|
if (!ctx || !ctx->dag.buf) {
|
||||||
return;
|
return;
|
||||||
@@ -161,21 +354,75 @@ bool Autolykos2_Hash(
|
|||||||
const uint8_t* message,
|
const uint8_t* message,
|
||||||
size_t messageLen,
|
size_t messageLen,
|
||||||
uint64_t nonce,
|
uint64_t nonce,
|
||||||
uint32_t height,
|
uint64_t height,
|
||||||
uint8_t outHash[32]
|
uint8_t outHash[32]
|
||||||
) {
|
) {
|
||||||
if (!ctx || !message || !outHash) {
|
if (!ctx || !message || !outHash) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Autolykos2_FallbackHash(ctx, message, messageLen, nonce, height, outHash);
|
if (!ctx->dag.buf || ctx->dag.len < 32 || (ctx->dag.len % 32) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t seed32[32];
|
||||||
|
if (!DeriveSeedFromMessage(message, messageLen, seed32)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t laneCount64 = ctx->dag.len / 32u;
|
||||||
|
if (laneCount64 == 0 || laneCount64 > UINT32_MAX) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Autolykos2_HashCore(
|
||||||
|
seed32,
|
||||||
|
message,
|
||||||
|
messageLen,
|
||||||
|
nonce,
|
||||||
|
height,
|
||||||
|
(uint32_t)laneCount64,
|
||||||
|
ctx,
|
||||||
|
true,
|
||||||
|
outHash
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Autolykos2_LightHash(const uint8_t* seed, blockchain_t* chain, uint64_t nonce, uint8_t* out) {
|
||||||
|
if (!seed || !chain || !out) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint64_t height = (uint64_t)Chain_Size(chain);
|
||||||
|
const size_t dagBytes = CalculateTargetDAGSize(chain);
|
||||||
|
if (dagBytes < 32 || (dagBytes % 32) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t laneCount64 = dagBytes / 32u;
|
||||||
|
if (laneCount64 == 0 || laneCount64 > UINT32_MAX) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Light path derives the needed DAG lanes from seed on-demand, no large DAG allocation required.
|
||||||
|
return Autolykos2_HashCore(
|
||||||
|
seed,
|
||||||
|
seed,
|
||||||
|
32,
|
||||||
|
nonce,
|
||||||
|
height,
|
||||||
|
(uint32_t)laneCount64,
|
||||||
|
NULL,
|
||||||
|
false,
|
||||||
|
out
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Autolykos2_CheckTarget(
|
bool Autolykos2_CheckTarget(
|
||||||
Autolykos2Context* ctx,
|
Autolykos2Context* ctx,
|
||||||
const uint8_t message32[32],
|
const uint8_t message32[32],
|
||||||
uint64_t nonce,
|
uint64_t nonce,
|
||||||
uint32_t height,
|
uint64_t height,
|
||||||
const uint8_t target32[32],
|
const uint8_t target32[32],
|
||||||
uint8_t outHash[32]
|
uint8_t outHash[32]
|
||||||
) {
|
) {
|
||||||
@@ -186,14 +433,14 @@ bool Autolykos2_CheckTarget(
|
|||||||
#ifdef MINICOIN_AUTOLYKOS2_REF_AVAILABLE
|
#ifdef MINICOIN_AUTOLYKOS2_REF_AVAILABLE
|
||||||
if (ctx->backend) {
|
if (ctx->backend) {
|
||||||
const bool ok = minicoin_autolykos2_ref_check_target(ctx->backend, message32, nonce, height, target32);
|
const bool ok = minicoin_autolykos2_ref_check_target(ctx->backend, message32, nonce, height, target32);
|
||||||
if (Autolykos2_FallbackHash(ctx, message32, 32, nonce, height, outHash)) {
|
if (Autolykos2_Hash(ctx, message32, 32, nonce, height, outHash)) {
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!Autolykos2_FallbackHash(ctx, message32, 32, nonce, height, outHash)) {
|
if (!Autolykos2_Hash(ctx, message32, 32, nonce, height, outHash)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return Cmp256BE(outHash, target32) <= 0;
|
return Cmp256BE(outHash, target32) <= 0;
|
||||||
@@ -202,7 +449,7 @@ bool Autolykos2_CheckTarget(
|
|||||||
bool Autolykos2_FindNonceSingleCore(
|
bool Autolykos2_FindNonceSingleCore(
|
||||||
Autolykos2Context* ctx,
|
Autolykos2Context* ctx,
|
||||||
const uint8_t message32[32],
|
const uint8_t message32[32],
|
||||||
uint32_t height,
|
uint64_t height,
|
||||||
const uint8_t target32[32],
|
const uint8_t target32[32],
|
||||||
uint64_t startNonce,
|
uint64_t startNonce,
|
||||||
uint64_t maxIterations,
|
uint64_t maxIterations,
|
||||||
|
|||||||
@@ -7,6 +7,11 @@ static Autolykos2Context* g_autolykos2Ctx = NULL;
|
|||||||
static Autolykos2Context* GetAutolykos2Ctx(void) {
|
static Autolykos2Context* GetAutolykos2Ctx(void) {
|
||||||
if (!g_autolykos2Ctx) {
|
if (!g_autolykos2Ctx) {
|
||||||
g_autolykos2Ctx = Autolykos2_Create();
|
g_autolykos2Ctx = Autolykos2_Create();
|
||||||
|
if (!g_autolykos2Ctx) {
|
||||||
|
fprintf(stderr, "Failed to create Autolykos2 context\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
Autolykos2_DagAllocate(g_autolykos2Ctx, DAG_BASE_SIZE);
|
||||||
}
|
}
|
||||||
return g_autolykos2Ctx;
|
return g_autolykos2Ctx;
|
||||||
}
|
}
|
||||||
@@ -18,6 +23,24 @@ void Block_ShutdownPowContext(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Block_RebuildAutolykos2Dag(size_t dagBytes, const uint8_t seed32[32]) {
|
||||||
|
if (!seed32 || dagBytes == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Autolykos2Context* ctx = GetAutolykos2Ctx();
|
||||||
|
if (!ctx) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Autolykos2_DagClear(ctx);
|
||||||
|
if (!Autolykos2_DagAllocate(ctx, dagBytes)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Autolykos2_DagGenerate(ctx, seed32);
|
||||||
|
}
|
||||||
|
|
||||||
block_t* Block_Create() {
|
block_t* Block_Create() {
|
||||||
block_t* block = (block_t*)malloc(sizeof(block_t));
|
block_t* block = (block_t*)malloc(sizeof(block_t));
|
||||||
if (!block) {
|
if (!block) {
|
||||||
@@ -271,9 +294,9 @@ void Block_Destroy(block_t* block) {
|
|||||||
void Block_Print(const block_t* block) {
|
void Block_Print(const block_t* block) {
|
||||||
if (!block) return;
|
if (!block) return;
|
||||||
|
|
||||||
printf("Block #%llu\n", block->header.blockNumber);
|
printf("Block #%llu\n", (unsigned long long)block->header.blockNumber);
|
||||||
printf("Timestamp: %llu\n", block->header.timestamp);
|
printf("Timestamp: %llu\n", (unsigned long long)block->header.timestamp);
|
||||||
printf("Nonce: %llu\n", block->header.nonce);
|
printf("Nonce: %llu\n", (unsigned long long)block->header.nonce);
|
||||||
printf("Difficulty Target: 0x%08x\n", block->header.difficultyTarget);
|
printf("Difficulty Target: 0x%08x\n", block->header.difficultyTarget);
|
||||||
printf("Version: %u\n", block->header.version);
|
printf("Version: %u\n", block->header.version);
|
||||||
printf("Previous Hash: ");
|
printf("Previous Hash: ");
|
||||||
@@ -292,7 +315,13 @@ void Block_Print(const block_t* block) {
|
|||||||
signed_transaction_t* tx = (signed_transaction_t*)DynArr_at(block->transactions, i);
|
signed_transaction_t* tx = (signed_transaction_t*)DynArr_at(block->transactions, i);
|
||||||
if (tx) {
|
if (tx) {
|
||||||
printf(" Tx #%zu: 1: %llu -> %02x%02x...%02x%02x, fee %llu\n 2: %llu -> %02x%02x...%02x%02x, fee %llu\n",
|
printf(" Tx #%zu: 1: %llu -> %02x%02x...%02x%02x, fee %llu\n 2: %llu -> %02x%02x...%02x%02x, fee %llu\n",
|
||||||
i, tx->transaction.amount1, tx->transaction.recipientAddress1[0], tx->transaction.recipientAddress1[1], tx->transaction.recipientAddress1[30], tx->transaction.recipientAddress1[31], tx->transaction.fee, tx->transaction.amount2, tx->transaction.recipientAddress2[0], tx->transaction.recipientAddress2[1], tx->transaction.recipientAddress2[30], tx->transaction.recipientAddress2[31], tx->transaction.fee);
|
i,
|
||||||
|
(unsigned long long)tx->transaction.amount1,
|
||||||
|
tx->transaction.recipientAddress1[0], tx->transaction.recipientAddress1[1], tx->transaction.recipientAddress1[30], tx->transaction.recipientAddress1[31],
|
||||||
|
(unsigned long long)tx->transaction.fee,
|
||||||
|
(unsigned long long)tx->transaction.amount2,
|
||||||
|
tx->transaction.recipientAddress2[0], tx->transaction.recipientAddress2[1], tx->transaction.recipientAddress2[30], tx->transaction.recipientAddress2[31],
|
||||||
|
(unsigned long long)tx->transaction.fee);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
27
src/main.c
27
src/main.c
@@ -11,6 +11,7 @@
|
|||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
||||||
#include <constants.h>
|
#include <constants.h>
|
||||||
|
#include <autolykos2/autolykos2.h>
|
||||||
|
|
||||||
#ifndef CHAIN_DATA_DIR
|
#ifndef CHAIN_DATA_DIR
|
||||||
#define CHAIN_DATA_DIR "chain_data"
|
#define CHAIN_DATA_DIR "chain_data"
|
||||||
@@ -110,10 +111,23 @@ int main(int argc, char* argv[]) {
|
|||||||
printf("No existing chain loaded from %s\n", chainDataDir);
|
printf("No existing chain loaded from %s\n", chainDataDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentReward == 0) {
|
const uint64_t effectivePhase1Blocks =
|
||||||
|
(PHASE1_TARGET_BLOCKS / EMISSION_ACCELERATION_FACTOR) > 0
|
||||||
|
? (PHASE1_TARGET_BLOCKS / EMISSION_ACCELERATION_FACTOR)
|
||||||
|
: 1;
|
||||||
|
|
||||||
|
// During phase 1, reward is deterministic from (supply,height), so always recompute.
|
||||||
|
// This avoids using stale on-disk cached rewards (e.g. floor reward after genesis).
|
||||||
|
if ((uint64_t)Chain_Size(chain) < effectivePhase1Blocks || currentReward == 0) {
|
||||||
currentReward = CalculateBlockReward(currentSupply, chain);
|
currentReward = CalculateBlockReward(currentSupply, chain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
uint8_t dagSeed[32];
|
||||||
|
GetNextDAGSeed(chain, dagSeed);
|
||||||
|
(void)Block_RebuildAutolykos2Dag(CalculateTargetDAGSize(chain), dagSeed);
|
||||||
|
}
|
||||||
|
|
||||||
if (Chain_Size(chain) > 0) {
|
if (Chain_Size(chain) > 0) {
|
||||||
if (Chain_IsValid(chain)) {
|
if (Chain_IsValid(chain)) {
|
||||||
printf("Loaded chain with %zu blocks from disk\n", Chain_Size(chain));
|
printf("Loaded chain with %zu blocks from disk\n", Chain_Size(chain));
|
||||||
@@ -222,15 +236,22 @@ int main(int argc, char* argv[]) {
|
|||||||
|
|
||||||
free(block); // chain stores blocks by value; transactions are owned by chain copy
|
free(block); // chain stores blocks by value; transactions are owned by chain copy
|
||||||
|
|
||||||
|
currentReward = CalculateBlockReward(currentSupply, chain); // Update the global currentReward for the next block
|
||||||
|
|
||||||
// Save chain after each mined block; NOTE: In reality, blocks will appear every ~90s, so this won't be a realistic bottleneck on the mainnet
|
// Save chain after each mined block; NOTE: In reality, blocks will appear every ~90s, so this won't be a realistic bottleneck on the mainnet
|
||||||
|
// Persist the reward for the *next* block so restart behavior is correct.
|
||||||
printf("Saving chain at height %zu...\n", Chain_Size(chain));
|
printf("Saving chain at height %zu...\n", Chain_Size(chain));
|
||||||
Chain_SaveToFile(chain, chainDataDir, currentSupply, currentReward);
|
Chain_SaveToFile(chain, chainDataDir, currentSupply, currentReward);
|
||||||
|
|
||||||
currentReward = CalculateBlockReward(currentSupply, chain); // Update the global currentReward for the next block
|
|
||||||
|
|
||||||
if (Chain_Size(chain) % DIFFICULTY_ADJUSTMENT_INTERVAL == 0) {
|
if (Chain_Size(chain) % DIFFICULTY_ADJUSTMENT_INTERVAL == 0) {
|
||||||
difficultyTarget = Chain_ComputeNextTarget(chain, difficultyTarget);
|
difficultyTarget = Chain_ComputeNextTarget(chain, difficultyTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Chain_Size(chain) % EPOCH_LENGTH == 0 && Chain_Size(chain) > 0) {
|
||||||
|
uint8_t dagSeed[32];
|
||||||
|
GetNextDAGSeed(chain, dagSeed);
|
||||||
|
(void)Block_RebuildAutolykos2Dag(CalculateTargetDAGSize(chain), dagSeed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Chain_SaveToFile(chain, chainDataDir, currentSupply, currentReward)) {
|
if (!Chain_SaveToFile(chain, chainDataDir, currentSupply, currentReward)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user