stuff
This commit is contained in:
3
TODO.txt
3
TODO.txt
@@ -1 +1,2 @@
|
||||
Eventually move the Proof of Work to RandomX. Keep the SHA-256 for Block IDs, TX IDs, etc. But nonce and mining should be RandomX. This will make it more resistant to ASICs and more accessible for CPU mining.
|
||||
Move to a GPU algo. RandomX is a good candidate, but CPU mining is not that attractive to anyone but people who actually want to support the project.
|
||||
It won't incentivize people who want to profit, which let's be fair, is the majority of miners.
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <randomx/librx_wrapper.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct {
|
||||
uint64_t blockNumber;
|
||||
@@ -27,11 +28,13 @@ typedef struct {
|
||||
|
||||
block_t* Block_Create();
|
||||
void Block_CalculateHash(const block_t* block, uint8_t* outHash);
|
||||
void Block_CalculateMerkleRoot(const block_t* block, uint8_t* outHash);
|
||||
void Block_CalculateRandomXHash(const block_t* block, uint8_t* outHash);
|
||||
void Block_AddTransaction(block_t* block, signed_transaction_t* tx);
|
||||
void Block_RemoveTransaction(block_t* block, uint8_t* txHash);
|
||||
bool Block_HasValidProofOfWork(const block_t* block);
|
||||
bool Block_AllTransactionsValid(const block_t* block);
|
||||
void Block_Destroy(block_t* block);
|
||||
void Block_Print(const block_t* block);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -6,7 +6,10 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
#define DECIMALS 1000000000000ULL
|
||||
#define EMISSION_SPEED_FACTOR 20
|
||||
#define DIFFICULTY_ADJUSTMENT_INTERVAL 960 // Every 960 blocks (roughly every 24 hours with a 90 second block time)
|
||||
// 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 RANDOMX_KEY_ROTATION_INTERVAL 6720 // 1 week at 90s block time
|
||||
#define TARGET_BLOCK_TIME 90 // Target block time in seconds
|
||||
static const uint64_t M_CAP = 18446744073709551615ULL; // Max uint64
|
||||
static const uint64_t TAIL_EMISSION = DECIMALS; // Emission floor is 1.0 coins per block
|
||||
// No max supply. Instead of halving, it'll follow a more gradual, Monero-like emission curve.
|
||||
@@ -26,8 +29,11 @@ static inline uint64_t CalculateBlockReward(uint256_t currentSupply, uint64_t he
|
||||
|
||||
uint64_t supply_64 = currentSupply.limbs[0];
|
||||
|
||||
// Formula: (M - Supply) >> 2^k - lifted from Monero's codebase (thanks guys!)
|
||||
uint64_t reward = (M_CAP - supply_64) >> EMISSION_SPEED_FACTOR;
|
||||
// Formula: ((M - Supply) >> 20) * 181 / 256
|
||||
// Use 128-bit intermediate to avoid overflow while preserving integer math.
|
||||
__uint128_t rewardWide = (((__uint128_t)(M_CAP - supply_64) >> 20) * 181u) >> 8;
|
||||
uint64_t reward = (rewardWide > UINT64_MAX) ? UINT64_MAX : (uint64_t)rewardWide;
|
||||
// At a block time of ~90s and a floor of 1.0 coins, this will make a curve of ~8.5 years
|
||||
|
||||
// Check if the calculated reward has fallen below the floor
|
||||
if (reward < TAIL_EMISSION) {
|
||||
|
||||
@@ -16,40 +16,113 @@ block_t* Block_Create() {
|
||||
}
|
||||
|
||||
void Block_CalculateHash(const block_t* block, uint8_t* outHash) {
|
||||
if (!block || !outHash || !block->transactions || DynArr_size(block->transactions) <= 0) {
|
||||
if (!block || !outHash) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Merkle root TODO
|
||||
|
||||
// Flatten the block header and transactions into a single buffer for hashing (assume that txs are verified - usually on receive)
|
||||
uint8_t buffer[sizeof(block_header_t) + (DynArr_size(block->transactions) * DynArr_elemSize(block->transactions))];
|
||||
memcpy(buffer, &block->header, sizeof(block_header_t));
|
||||
for (size_t i = 0; i < DynArr_size(block->transactions); i++) {
|
||||
void* txPtr = (char*)DynArr_at(block->transactions, i);
|
||||
memcpy(buffer + sizeof(block_header_t) + (i * DynArr_elemSize(block->transactions)), txPtr, DynArr_elemSize(block->transactions));
|
||||
}
|
||||
|
||||
SHA256((const unsigned char*)buffer, sizeof(buffer), outHash);
|
||||
// Canonical block hash commits to header fields, including merkleRoot.
|
||||
SHA256((const unsigned char*)&block->header, sizeof(block_header_t), outHash);
|
||||
SHA256(outHash, 32, outHash); // Double-Hash
|
||||
}
|
||||
|
||||
void Block_CalculateRandomXHash(const block_t* block, uint8_t* outHash) {
|
||||
if (!block || !outHash || !block->transactions || DynArr_size(block->transactions) <= 0) {
|
||||
void Block_CalculateMerkleRoot(const block_t* block, uint8_t* outHash) {
|
||||
if (!block || !block->transactions || !outHash) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Merkle root TODO
|
||||
|
||||
// Flatten the block header and transactions into a single buffer for hashing (assume that txs are verified - usually on receive)
|
||||
uint8_t buffer[sizeof(block_header_t) + (DynArr_size(block->transactions) * DynArr_elemSize(block->transactions))];
|
||||
memcpy(buffer, &block->header, sizeof(block_header_t));
|
||||
for (size_t i = 0; i < DynArr_size(block->transactions); i++) {
|
||||
void* txPtr = (char*)DynArr_at(block->transactions, i);
|
||||
memcpy(buffer + sizeof(block_header_t) + (i * DynArr_elemSize(block->transactions)), txPtr, DynArr_elemSize(block->transactions));
|
||||
const size_t txCount = DynArr_size(block->transactions);
|
||||
if (txCount == 0) {
|
||||
memset(outHash, 0, 32);
|
||||
return;
|
||||
}
|
||||
if (txCount == 1) {
|
||||
signed_transaction_t* tx = (signed_transaction_t*)DynArr_at(block->transactions, 0);
|
||||
Transaction_CalculateHash(tx, outHash);
|
||||
return;
|
||||
}
|
||||
|
||||
RandomX_CalculateHash(buffer, sizeof(buffer), outHash);
|
||||
// TODO: Make this not shit
|
||||
DynArr* hashes1 = DynArr_create(sizeof(uint8_t) * 32, 1);
|
||||
DynArr* hashes2 = DynArr_create(sizeof(uint8_t) * 32, 1);
|
||||
if (!hashes1 || !hashes2) {
|
||||
if (hashes1) DynArr_destroy(hashes1);
|
||||
if (hashes2) DynArr_destroy(hashes2);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle the transactions
|
||||
for (size_t i = 0; i < txCount - 1; i++) {
|
||||
signed_transaction_t* tx = (signed_transaction_t*)DynArr_at(block->transactions, i);
|
||||
signed_transaction_t* txNext = (signed_transaction_t*)DynArr_at(block->transactions, i + 1);
|
||||
uint8_t buf1[32] = {0}; uint8_t buf2[32] = {0}; // Zeroed out
|
||||
|
||||
// Unless if by some miracle the hash just so happens to be all zeros,
|
||||
// I think we can safely assume that a 1 : 2^256 chance will NEVER be hit
|
||||
Transaction_CalculateHash(tx, buf1);
|
||||
Transaction_CalculateHash(txNext, buf2);
|
||||
|
||||
// Concat the two hashes
|
||||
uint8_t dataInBuffer[64] = {0};
|
||||
uint8_t* nextStart = dataInBuffer;
|
||||
nextStart += 32;
|
||||
memcpy(dataInBuffer, buf1, 32);
|
||||
if (txNext) { memcpy(nextStart, buf2, 32); }
|
||||
|
||||
// Double hash that tx set
|
||||
uint8_t outHash[32];
|
||||
SHA256((const unsigned char*)dataInBuffer, 64, outHash);
|
||||
SHA256(outHash, 32, outHash);
|
||||
|
||||
// Copy to the hashes dynarr
|
||||
DynArr_push_back(hashes1, outHash);
|
||||
}
|
||||
|
||||
// Move to hashing the existing ones until only one remains
|
||||
do {
|
||||
for (size_t i = 0; i < DynArr_size(hashes1) - 1; i++) {
|
||||
uint8_t* hash1 = (uint8_t*)DynArr_at(hashes1, i); uint8_t* hash2 = (uint8_t*)DynArr_at(hashes1, i + 1);
|
||||
|
||||
// Concat the two hashes
|
||||
uint8_t dataInBuffer[64] = {0};
|
||||
uint8_t* nextStart = dataInBuffer;
|
||||
nextStart += 32;
|
||||
memcpy(dataInBuffer, hash1, 32);
|
||||
memcpy(nextStart, hash2, 32);
|
||||
|
||||
// Double hash that tx set
|
||||
uint8_t outHash[32];
|
||||
SHA256((const unsigned char*)dataInBuffer, 64, outHash);
|
||||
SHA256(outHash, 32, outHash);
|
||||
|
||||
DynArr_push_back(hashes2, outHash);
|
||||
}
|
||||
|
||||
DynArr_erase(hashes1);
|
||||
for (size_t i = 0; i < DynArr_size(hashes2); i++) {
|
||||
DynArr_push_back(hashes1, (uint8_t*)DynArr_at(hashes2, i));
|
||||
}
|
||||
DynArr_erase(hashes2);
|
||||
} while (DynArr_size(hashes1) > 1);
|
||||
|
||||
// Final Merkle
|
||||
uint8_t* merkle = (uint8_t*)DynArr_at(hashes1, 0);
|
||||
if (merkle) {
|
||||
memcpy(outHash, merkle, 32);
|
||||
} else {
|
||||
memset(outHash, 0, 32);
|
||||
}
|
||||
|
||||
DynArr_destroy(hashes1);
|
||||
DynArr_destroy(hashes2);
|
||||
}
|
||||
|
||||
void Block_CalculateRandomXHash(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);
|
||||
}
|
||||
|
||||
void Block_AddTransaction(block_t* block, signed_transaction_t* tx) {
|
||||
@@ -162,3 +235,34 @@ void Block_Destroy(block_t* block) {
|
||||
DynArr_destroy(block->transactions);
|
||||
free(block);
|
||||
}
|
||||
|
||||
void Block_Print(const block_t* block) {
|
||||
if (!block) return;
|
||||
|
||||
printf("Block #%llu\n", block->header.blockNumber);
|
||||
printf("Timestamp: %llu\n", block->header.timestamp);
|
||||
printf("Nonce: %llu\n", block->header.nonce);
|
||||
printf("Difficulty Target: 0x%08x\n", block->header.difficultyTarget);
|
||||
printf("Version: %u\n", block->header.version);
|
||||
printf("Previous Hash: ");
|
||||
for (size_t i = 0; i < 32; i++) {
|
||||
printf("%02x", block->header.prevHash[i]);
|
||||
}
|
||||
printf("\n");
|
||||
printf("Merkle Root: ");
|
||||
for (size_t i = 0; i < 32; i++) {
|
||||
printf("%02x", block->header.merkleRoot[i]);
|
||||
}
|
||||
printf("\n");
|
||||
if (block->transactions) {
|
||||
printf("Transactions (%zu):\n", DynArr_size(block->transactions));
|
||||
for (size_t i = 0; i < DynArr_size(block->transactions); i++) {
|
||||
signed_transaction_t* tx = (signed_transaction_t*)DynArr_at(block->transactions, i);
|
||||
if (tx) {
|
||||
printf(" Tx #%zu: %llu -> %llu, fee %llu\n", i, tx->transaction.amount, tx->transaction.fee, tx->transaction.amount + tx->transaction.fee);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
printf("No transactions (or none loaded)\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,23 @@ static bool BuildPath(char* out, size_t outSize, const char* dirpath, const char
|
||||
return written > 0 && (size_t)written < outSize;
|
||||
}
|
||||
|
||||
static void Chain_ClearBlocks(blockchain_t* chain) {
|
||||
if (!chain || !chain->blocks) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < DynArr_size(chain->blocks); i++) {
|
||||
block_t* blk = (block_t*)DynArr_at(chain->blocks, i);
|
||||
if (blk && blk->transactions) {
|
||||
DynArr_destroy(blk->transactions);
|
||||
blk->transactions = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
DynArr_erase(chain->blocks);
|
||||
chain->size = 0;
|
||||
}
|
||||
|
||||
blockchain_t* Chain_Create() {
|
||||
blockchain_t* ptr = (blockchain_t*)malloc(sizeof(blockchain_t));
|
||||
if (!ptr) {
|
||||
@@ -47,6 +64,7 @@ blockchain_t* Chain_Create() {
|
||||
void Chain_Destroy(blockchain_t* chain) {
|
||||
if (chain) {
|
||||
if (chain->blocks) {
|
||||
Chain_ClearBlocks(chain);
|
||||
DynArr_destroy(chain->blocks);
|
||||
}
|
||||
free(chain);
|
||||
@@ -113,10 +131,7 @@ bool Chain_IsValid(blockchain_t* chain) {
|
||||
}
|
||||
|
||||
void Chain_Wipe(blockchain_t* chain) {
|
||||
if (chain && chain->blocks) {
|
||||
DynArr_erase(chain->blocks);
|
||||
chain->size = 0;
|
||||
}
|
||||
Chain_ClearBlocks(chain);
|
||||
}
|
||||
|
||||
bool Chain_SaveToFile(blockchain_t* chain, const char* dirpath, uint256_t currentSupply) {
|
||||
@@ -247,7 +262,7 @@ bool Chain_LoadFromFile(blockchain_t* chain, const char* dirpath, uint256_t* out
|
||||
fclose(metaFile);
|
||||
|
||||
// TODO: Might add a flag to allow reading from a point onward, but just rewrite for now
|
||||
DynArr_erase(chain->blocks); // Clear current chain blocks, but keep allocated memory for efficiency, since we will likely be loading a similar number of blocks as currently in memory.
|
||||
Chain_ClearBlocks(chain); // Clear current chain blocks and free owned transaction buffers before reload.
|
||||
|
||||
// Load blocks
|
||||
for (size_t i = 0; i < savedSize; i++) {
|
||||
@@ -280,7 +295,7 @@ bool Chain_LoadFromFile(blockchain_t* chain, const char* dirpath, uint256_t* out
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < txSize; j++) {
|
||||
/*for (size_t j = 0; j < txSize; j++) {
|
||||
signed_transaction_t tx;
|
||||
if (fread(&tx, sizeof(signed_transaction_t), 1, blockFile) != 1) {
|
||||
fclose(blockFile);
|
||||
@@ -288,10 +303,16 @@ bool Chain_LoadFromFile(blockchain_t* chain, const char* dirpath, uint256_t* out
|
||||
return false;
|
||||
}
|
||||
Block_AddTransaction(blk, &tx);
|
||||
}
|
||||
fclose(blockFile);
|
||||
}*/ // Transactions are not read, we use the merkle root for validity
|
||||
|
||||
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->size = savedSize;
|
||||
|
||||
178
src/main.c
178
src/main.c
@@ -87,12 +87,12 @@ static bool MineBlock(block_t* block) {
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
int main(int argc, char* argv[]) {
|
||||
signal(SIGINT, handle_sigint);
|
||||
|
||||
const char* chainDataDir = CHAIN_DATA_DIR;
|
||||
const uint64_t blocksToMine = 10;
|
||||
const double targetSeconds = 90.0;
|
||||
const double targetSeconds = TARGET_BLOCK_TIME;
|
||||
|
||||
uint256_t currentSupply = uint256_from_u64(0);
|
||||
|
||||
@@ -134,91 +134,117 @@ int main(void) {
|
||||
}
|
||||
}
|
||||
|
||||
const double hps = MeasureRandomXHashrate();
|
||||
const double expectedHashes = (hps > 0.0) ? (hps * targetSeconds) : 65536.0;
|
||||
const uint32_t calibratedBits = CompactTargetForExpectedHashes(expectedHashes);
|
||||
// Get flag from argv "-mine" to mine blocks
|
||||
if (argc > 1 && strcmp(argv[1], "-mine") == 0) {
|
||||
printf("Mining %llu blocks with target time %.0fs...\n", (unsigned long long)blocksToMine, targetSeconds);
|
||||
|
||||
printf("RandomX benchmark: %.2f H/s, target %.0fs, nBits=0x%08x\n",
|
||||
hps,
|
||||
targetSeconds,
|
||||
calibratedBits);
|
||||
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
|
||||
|
||||
uint8_t minerAddress[32];
|
||||
SHA256((const unsigned char*)"minicoin-miner-1", strlen("minicoin-miner-1"), minerAddress);
|
||||
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
|
||||
|
||||
for (uint64_t mined = 0; mined < blocksToMine; ++mined) {
|
||||
block_t* block = Block_Create();
|
||||
if (!block) {
|
||||
fprintf(stderr, "failed to create block\n");
|
||||
Chain_Destroy(chain);
|
||||
RandomX_Destroy();
|
||||
return 1;
|
||||
}
|
||||
uint8_t minerAddress[32];
|
||||
SHA256((const unsigned char*)"minicoin-miner-1", strlen("minicoin-miner-1"), minerAddress);
|
||||
|
||||
block->header.version = 1;
|
||||
block->header.blockNumber = (uint64_t)Chain_Size(chain);
|
||||
if (Chain_Size(chain) > 0) {
|
||||
block_t* lastBlock = Chain_GetBlock(chain, Chain_Size(chain) - 1);
|
||||
if (lastBlock) {
|
||||
Block_CalculateHash(lastBlock, block->header.prevHash);
|
||||
for (uint64_t mined = 0; mined < blocksToMine; ++mined) {
|
||||
block_t* block = Block_Create();
|
||||
if (!block) {
|
||||
fprintf(stderr, "failed to create block\n");
|
||||
Chain_Destroy(chain);
|
||||
RandomX_Destroy();
|
||||
return 1;
|
||||
}
|
||||
|
||||
block->header.version = 1;
|
||||
block->header.blockNumber = (uint64_t)Chain_Size(chain);
|
||||
if (Chain_Size(chain) > 0) {
|
||||
block_t* lastBlock = Chain_GetBlock(chain, Chain_Size(chain) - 1);
|
||||
if (lastBlock) {
|
||||
Block_CalculateHash(lastBlock, block->header.prevHash);
|
||||
} else {
|
||||
memset(block->header.prevHash, 0, sizeof(block->header.prevHash));
|
||||
}
|
||||
} else {
|
||||
memset(block->header.prevHash, 0, sizeof(block->header.prevHash));
|
||||
}
|
||||
block->header.timestamp = (uint64_t)time(NULL);
|
||||
block->header.difficultyTarget = calibratedBits;
|
||||
block->header.nonce = 0;
|
||||
|
||||
signed_transaction_t coinbaseTx;
|
||||
memset(&coinbaseTx, 0, sizeof(coinbaseTx));
|
||||
coinbaseTx.transaction.version = 1;
|
||||
coinbaseTx.transaction.amount = CalculateBlockReward(currentSupply, block->header.blockNumber);
|
||||
coinbaseTx.transaction.fee = 0;
|
||||
memcpy(coinbaseTx.transaction.recipientAddress, minerAddress, sizeof(minerAddress));
|
||||
memset(coinbaseTx.transaction.compressedPublicKey, 0, sizeof(coinbaseTx.transaction.compressedPublicKey));
|
||||
memset(coinbaseTx.transaction.senderAddress, 0xFF, sizeof(coinbaseTx.transaction.senderAddress));
|
||||
Block_AddTransaction(block, &coinbaseTx);
|
||||
|
||||
uint8_t merkleRoot[32];
|
||||
Block_CalculateMerkleRoot(block, merkleRoot);
|
||||
memcpy(block->header.merkleRoot, merkleRoot, sizeof(block->header.merkleRoot));
|
||||
|
||||
if (!MineBlock(block)) {
|
||||
fprintf(stderr, "failed to mine block within nonce range\n");
|
||||
Block_Destroy(block);
|
||||
Chain_Destroy(chain);
|
||||
RandomX_Destroy();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!Chain_AddBlock(chain, block)) {
|
||||
fprintf(stderr, "failed to append block to chain\n");
|
||||
Block_Destroy(block);
|
||||
Chain_Destroy(chain);
|
||||
RandomX_Destroy();
|
||||
return 1;
|
||||
}
|
||||
|
||||
(void)uint256_add_u64(¤tSupply, 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",
|
||||
(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],
|
||||
block->header.merkleRoot[0], block->header.merkleRoot[1], block->header.merkleRoot[2], block->header.merkleRoot[3],
|
||||
blockHash[0], blockHash[1], blockHash[2], blockHash[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_SaveToFile(chain, chainDataDir, currentSupply)) {
|
||||
fprintf(stderr, "failed to save chain to %s\n", chainDataDir);
|
||||
} else {
|
||||
memset(block->header.prevHash, 0, sizeof(block->header.prevHash));
|
||||
printf("Saved chain with %zu blocks to %s (supply=%llu)\n",
|
||||
Chain_Size(chain),
|
||||
chainDataDir,
|
||||
(unsigned long long)currentSupply.limbs[0]);
|
||||
}
|
||||
memset(block->header.merkleRoot, 0, sizeof(block->header.merkleRoot));
|
||||
block->header.timestamp = (uint64_t)time(NULL);
|
||||
block->header.difficultyTarget = calibratedBits;
|
||||
block->header.nonce = 0;
|
||||
|
||||
signed_transaction_t coinbaseTx;
|
||||
memset(&coinbaseTx, 0, sizeof(coinbaseTx));
|
||||
coinbaseTx.transaction.version = 1;
|
||||
coinbaseTx.transaction.amount = CalculateBlockReward(currentSupply, block->header.blockNumber);
|
||||
coinbaseTx.transaction.fee = 0;
|
||||
memcpy(coinbaseTx.transaction.recipientAddress, minerAddress, sizeof(minerAddress));
|
||||
memset(coinbaseTx.transaction.compressedPublicKey, 0, sizeof(coinbaseTx.transaction.compressedPublicKey));
|
||||
memset(coinbaseTx.transaction.senderAddress, 0xFF, sizeof(coinbaseTx.transaction.senderAddress));
|
||||
Block_AddTransaction(block, &coinbaseTx);
|
||||
|
||||
if (!MineBlock(block)) {
|
||||
fprintf(stderr, "failed to mine block within nonce range\n");
|
||||
Block_Destroy(block);
|
||||
Chain_Destroy(chain);
|
||||
RandomX_Destroy();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!Chain_AddBlock(chain, block)) {
|
||||
fprintf(stderr, "failed to append block to chain\n");
|
||||
Block_Destroy(block);
|
||||
Chain_Destroy(chain);
|
||||
RandomX_Destroy();
|
||||
return 1;
|
||||
}
|
||||
|
||||
(void)uint256_add_u64(¤tSupply, coinbaseTx.transaction.amount);
|
||||
|
||||
uint8_t blockHash[32];
|
||||
Block_CalculateHash(block, blockHash);
|
||||
printf("Mined block %llu/%llu (height=%llu) nonce=%llu reward=%llu supply=%llu hash=%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],
|
||||
blockHash[0], blockHash[1], blockHash[2], blockHash[3]);
|
||||
} else {
|
||||
printf("Current chain has %zu blocks, total supply %llu\n", Chain_Size(chain), (unsigned long long)currentSupply.limbs[0]);
|
||||
}
|
||||
|
||||
if (!Chain_SaveToFile(chain, chainDataDir, currentSupply)) {
|
||||
fprintf(stderr, "failed to save chain to %s\n", chainDataDir);
|
||||
} else {
|
||||
printf("Saved chain with %zu blocks to %s (supply=%llu)\n",
|
||||
Chain_Size(chain),
|
||||
chainDataDir,
|
||||
(unsigned long long)currentSupply.limbs[0]);
|
||||
// Print chain
|
||||
for (size_t i = 0; i < Chain_Size(chain); i++) {
|
||||
block_t* blk = Chain_GetBlock(chain, i);
|
||||
if (blk) {
|
||||
Block_Print(blk);
|
||||
}
|
||||
}
|
||||
|
||||
Chain_Destroy(chain);
|
||||
|
||||
Reference in New Issue
Block a user