Monero-style emission
This commit is contained in:
@@ -7,6 +7,8 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <constants.h>
|
||||||
|
#include <uint256.h>
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
DynArr* blocks;
|
DynArr* blocks;
|
||||||
@@ -22,7 +24,7 @@ bool Chain_IsValid(blockchain_t* chain);
|
|||||||
void Chain_Wipe(blockchain_t* chain);
|
void Chain_Wipe(blockchain_t* chain);
|
||||||
|
|
||||||
// I/O
|
// I/O
|
||||||
bool Chain_SaveToFile(blockchain_t* chain, const char* dirpath);
|
bool Chain_SaveToFile(blockchain_t* chain, const char* dirpath, uint256_t currentSupply);
|
||||||
bool Chain_LoadFromFile(blockchain_t* chain, const char* dirpath);
|
bool Chain_LoadFromFile(blockchain_t* chain, const char* dirpath, uint256_t* outCurrentSupply);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -7,21 +7,24 @@
|
|||||||
|
|
||||||
#define DECIMALS 1000000000000ULL
|
#define DECIMALS 1000000000000ULL
|
||||||
#define EMISSION_SPEED_FACTOR 20
|
#define EMISSION_SPEED_FACTOR 20
|
||||||
const uint64_t M_CAP = 18446744073709551615ULL; // Max uint64
|
static const uint64_t M_CAP = 18446744073709551615ULL; // Max uint64
|
||||||
const uint64_t TAIL_EMISSION = (uint64_t)(1.0 * DECIMALS); // Emission floor is 1.0 coins per block
|
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.
|
// No max supply. Instead of halving, it'll follow a more gradual, Monero-like emission curve.
|
||||||
|
|
||||||
|
static uint256_t currentSupply = {{0, 0, 0, 0}}; // Global variable to track total supply; updated with each block mined
|
||||||
|
|
||||||
static inline uint64_t CalculateBlockReward(uint256_t currentSupply, uint64_t height) {
|
static inline uint64_t CalculateBlockReward(uint256_t currentSupply, uint64_t height) {
|
||||||
// Inclusive of block 0
|
// Inclusive of block 0
|
||||||
|
(void)height;
|
||||||
|
|
||||||
if (current_supply.limbs[1] > 0 ||
|
if (currentSupply.limbs[1] > 0 ||
|
||||||
current_supply.limbs[2] > 0 ||
|
currentSupply.limbs[2] > 0 ||
|
||||||
current_supply.limbs[3] > 0 ||
|
currentSupply.limbs[3] > 0 ||
|
||||||
current_supply.limbs[0] >= M_CAP) {
|
currentSupply.limbs[0] >= M_CAP) {
|
||||||
return TAIL_EMISSION;
|
return TAIL_EMISSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t supply_64 = current_supply.limbs[0];
|
uint64_t supply_64 = currentSupply.limbs[0];
|
||||||
|
|
||||||
// Formula: (M - Supply) >> 2^k - lifted from Monero's codebase (thanks guys!)
|
// Formula: (M - Supply) >> 2^k - lifted from Monero's codebase (thanks guys!)
|
||||||
uint64_t reward = (M_CAP - supply_64) >> EMISSION_SPEED_FACTOR;
|
uint64_t reward = (M_CAP - supply_64) >> EMISSION_SPEED_FACTOR;
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ void Chain_Wipe(blockchain_t* chain) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Chain_SaveToFile(blockchain_t* chain, const char* dirpath) {
|
bool Chain_SaveToFile(blockchain_t* chain, const char* dirpath, uint256_t currentSupply) {
|
||||||
// To avoid stalling the chain from peers, write after every block addition (THAT IS VERIFIED)
|
// To avoid stalling the chain from peers, write after every block addition (THAT IS VERIFIED)
|
||||||
|
|
||||||
if (!chain || !chain->blocks || !EnsureDirectoryExists(dirpath)) {
|
if (!chain || !chain->blocks || !EnsureDirectoryExists(dirpath)) {
|
||||||
@@ -145,6 +145,8 @@ bool Chain_SaveToFile(blockchain_t* chain, const char* dirpath) {
|
|||||||
// Write last block hash (32 bytes of zeros for now)
|
// Write last block hash (32 bytes of zeros for now)
|
||||||
uint8_t zeroHash[32] = {0};
|
uint8_t zeroHash[32] = {0};
|
||||||
fwrite(zeroHash, sizeof(uint8_t), 32, metaFile);
|
fwrite(zeroHash, sizeof(uint8_t), 32, metaFile);
|
||||||
|
uint256_t zeroSupply = {0};
|
||||||
|
fwrite(&zeroSupply, sizeof(uint256_t), 1, metaFile);
|
||||||
|
|
||||||
// TODO: Potentially some other things here, we'll see
|
// TODO: Potentially some other things here, we'll see
|
||||||
}
|
}
|
||||||
@@ -210,13 +212,14 @@ bool Chain_SaveToFile(blockchain_t* chain, const char* dirpath) {
|
|||||||
Block_CalculateHash(lastBlock, lastHash);
|
Block_CalculateHash(lastBlock, lastHash);
|
||||||
fwrite(lastHash, sizeof(uint8_t), 32, metaFile);
|
fwrite(lastHash, sizeof(uint8_t), 32, metaFile);
|
||||||
}
|
}
|
||||||
|
fwrite(¤tSupply, sizeof(uint256_t), 1, metaFile);
|
||||||
fclose(metaFile);
|
fclose(metaFile);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Chain_LoadFromFile(blockchain_t* chain, const char* dirpath) {
|
bool Chain_LoadFromFile(blockchain_t* chain, const char* dirpath, uint256_t* outCurrentSupply) {
|
||||||
if (!chain || !chain->blocks || !dirpath) {
|
if (!chain || !chain->blocks || !dirpath || !outCurrentSupply) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,6 +243,7 @@ bool Chain_LoadFromFile(blockchain_t* chain, const char* dirpath) {
|
|||||||
fread(&savedSize, sizeof(size_t), 1, metaFile);
|
fread(&savedSize, sizeof(size_t), 1, metaFile);
|
||||||
uint8_t lastSavedHash[32];
|
uint8_t lastSavedHash[32];
|
||||||
fread(lastSavedHash, sizeof(uint8_t), 32, metaFile);
|
fread(lastSavedHash, sizeof(uint8_t), 32, metaFile);
|
||||||
|
fread(outCurrentSupply, sizeof(uint256_t), 1, metaFile);
|
||||||
fclose(metaFile);
|
fclose(metaFile);
|
||||||
|
|
||||||
// TODO: Might add a flag to allow reading from a point onward, but just rewrite for now
|
// TODO: Might add a flag to allow reading from a point onward, but just rewrite for now
|
||||||
|
|||||||
251
src/main.c
251
src/main.c
@@ -1,16 +1,18 @@
|
|||||||
#include <block/chain.h>
|
#include <block/chain.h>
|
||||||
#include <block/transaction.h>
|
#include <block/transaction.h>
|
||||||
#include <openssl/sha.h>
|
#include <openssl/sha.h>
|
||||||
#include <secp256k1.h>
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <randomx/librx_wrapper.h>
|
#include <randomx/librx_wrapper.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
||||||
|
#include <constants.h>
|
||||||
|
|
||||||
#ifndef CHAIN_DATA_DIR
|
#ifndef CHAIN_DATA_DIR
|
||||||
#define CHAIN_DATA_DIR "chain_data"
|
#define CHAIN_DATA_DIR "chain_data"
|
||||||
#endif
|
#endif
|
||||||
@@ -68,43 +70,6 @@ static uint32_t CompactTargetForExpectedHashes(double expectedHashes) {
|
|||||||
return (0x1fU << 24) | (mantissa & 0x007fffffU);
|
return (0x1fU << 24) | (mantissa & 0x007fffffU);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool GenerateKeypair(
|
|
||||||
const secp256k1_context* ctx,
|
|
||||||
uint8_t outPrivateKey[32],
|
|
||||||
uint8_t outCompressedPublicKey[33]
|
|
||||||
) {
|
|
||||||
if (!ctx || !outPrivateKey || !outCompressedPublicKey) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
secp256k1_pubkey pubkey;
|
|
||||||
for (size_t i = 0; i < 1024; ++i) {
|
|
||||||
arc4random_buf(outPrivateKey, 32);
|
|
||||||
if (!secp256k1_ec_seckey_verify(ctx, outPrivateKey)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!secp256k1_ec_pubkey_create(ctx, &pubkey, outPrivateKey)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t serializedLen = 33;
|
|
||||||
if (!secp256k1_ec_pubkey_serialize(
|
|
||||||
ctx,
|
|
||||||
outCompressedPublicKey,
|
|
||||||
&serializedLen,
|
|
||||||
&pubkey,
|
|
||||||
SECP256K1_EC_COMPRESSED
|
|
||||||
)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return serializedLen == 33;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool MineBlock(block_t* block) {
|
static bool MineBlock(block_t* block) {
|
||||||
if (!block) {
|
if (!block) {
|
||||||
return false;
|
return false;
|
||||||
@@ -126,6 +91,10 @@ int main(void) {
|
|||||||
signal(SIGINT, handle_sigint);
|
signal(SIGINT, handle_sigint);
|
||||||
|
|
||||||
const char* chainDataDir = CHAIN_DATA_DIR;
|
const char* chainDataDir = CHAIN_DATA_DIR;
|
||||||
|
const uint64_t blocksToMine = 10;
|
||||||
|
const double targetSeconds = 90.0;
|
||||||
|
|
||||||
|
uint256_t currentSupply = uint256_from_u64(0);
|
||||||
|
|
||||||
// Init RandomX
|
// 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.
|
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.
|
||||||
@@ -139,8 +108,7 @@ int main(void) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt read
|
if (!Chain_LoadFromFile(chain, chainDataDir, ¤tSupply)) {
|
||||||
if (!Chain_LoadFromFile(chain, chainDataDir)) {
|
|
||||||
printf("No existing chain loaded from %s\n", chainDataDir);
|
printf("No existing chain loaded from %s\n", chainDataDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,149 +134,94 @@ int main(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
secp256k1_context* secpCtx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
|
|
||||||
if (!secpCtx) {
|
|
||||||
fprintf(stderr, "failed to create secp256k1 context\n");
|
|
||||||
Chain_Destroy(chain);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t senderPrivateKey[32];
|
|
||||||
uint8_t receiverPrivateKey[32];
|
|
||||||
uint8_t senderCompressedPublicKey[33];
|
|
||||||
uint8_t receiverCompressedPublicKey[33];
|
|
||||||
|
|
||||||
if (!GenerateKeypair(secpCtx, senderPrivateKey, senderCompressedPublicKey) ||
|
|
||||||
!GenerateKeypair(secpCtx, receiverPrivateKey, receiverCompressedPublicKey)) {
|
|
||||||
fprintf(stderr, "failed to generate keypairs\n");
|
|
||||||
secp256k1_context_destroy(secpCtx);
|
|
||||||
Chain_Destroy(chain);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Coinbase TX - no signature needed, one per block
|
|
||||||
signed_transaction_t coinbaseTx;
|
|
||||||
memset(&coinbaseTx, 0, sizeof(coinbaseTx));
|
|
||||||
coinbaseTx.transaction.version = 1;
|
|
||||||
coinbaseTx.transaction.amount = 50; // Block reward
|
|
||||||
coinbaseTx.transaction.fee = 0;
|
|
||||||
SHA256(receiverCompressedPublicKey, 33, coinbaseTx.transaction.recipientAddress);
|
|
||||||
memset(coinbaseTx.transaction.compressedPublicKey, 0x00, 33); // No public key for coinbase
|
|
||||||
memset(coinbaseTx.transaction.senderAddress, 0xFF, 32); // Coinbase marker
|
|
||||||
|
|
||||||
// Test TX
|
|
||||||
signed_transaction_t tx;
|
|
||||||
memset(&tx, 0, sizeof(tx));
|
|
||||||
tx.transaction.version = 1;
|
|
||||||
tx.transaction.amount = 100;
|
|
||||||
tx.transaction.fee = 1;
|
|
||||||
|
|
||||||
SHA256(senderCompressedPublicKey, 33, tx.transaction.senderAddress);
|
|
||||||
SHA256(receiverCompressedPublicKey, 33, tx.transaction.recipientAddress);
|
|
||||||
memcpy(tx.transaction.compressedPublicKey, senderCompressedPublicKey, 33);
|
|
||||||
|
|
||||||
Transaction_Sign(&tx, senderPrivateKey);
|
|
||||||
if (!Transaction_Verify(&tx)) {
|
|
||||||
fprintf(stderr, "signed transaction did not verify\n");
|
|
||||||
secp256k1_context_destroy(secpCtx);
|
|
||||||
Chain_Destroy(chain);
|
|
||||||
RandomX_Destroy();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
block_t* block = Block_Create();
|
|
||||||
if (!block) {
|
|
||||||
fprintf(stderr, "failed to create block\n");
|
|
||||||
secp256k1_context_destroy(secpCtx);
|
|
||||||
Chain_Destroy(chain);
|
|
||||||
RandomX_Destroy();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
block->header.version = 1;
|
|
||||||
block->header.blockNumber = (uint64_t)Chain_Size(chain);
|
|
||||||
// Get prevHash from last block if exists
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
memset(block->header.merkleRoot, 0, sizeof(block->header.merkleRoot));
|
|
||||||
block->header.timestamp = (uint64_t)time(NULL);
|
|
||||||
|
|
||||||
const double hps = MeasureRandomXHashrate();
|
const double hps = MeasureRandomXHashrate();
|
||||||
const double targetSeconds = 10.0;
|
|
||||||
const double expectedHashes = (hps > 0.0) ? (hps * targetSeconds) : 65536.0;
|
const double expectedHashes = (hps > 0.0) ? (hps * targetSeconds) : 65536.0;
|
||||||
block->header.difficultyTarget = CompactTargetForExpectedHashes(expectedHashes);
|
const uint32_t calibratedBits = CompactTargetForExpectedHashes(expectedHashes);
|
||||||
block->header.nonce = 0;
|
|
||||||
|
|
||||||
printf("RandomX benchmark: %.2f H/s, target %.0fs, nBits=0x%08x\n",
|
printf("RandomX benchmark: %.2f H/s, target %.0fs, nBits=0x%08x\n",
|
||||||
hps,
|
hps,
|
||||||
targetSeconds,
|
targetSeconds,
|
||||||
block->header.difficultyTarget);
|
calibratedBits);
|
||||||
|
|
||||||
Block_AddTransaction(block, &coinbaseTx);
|
uint8_t minerAddress[32];
|
||||||
printf("Added coinbase transaction to block: recipient %02x... -> amount %lu\n",
|
SHA256((const unsigned char*)"minicoin-miner-1", strlen("minicoin-miner-1"), minerAddress);
|
||||||
coinbaseTx.transaction.recipientAddress[0], coinbaseTx.transaction.recipientAddress[31],
|
|
||||||
coinbaseTx.transaction.amount);
|
|
||||||
Block_AddTransaction(block, &tx);
|
|
||||||
printf("Added transaction to block: sender %02x... -> recipient %02x..., amount %lu, fee %lu\n",
|
|
||||||
tx.transaction.senderAddress[0], tx.transaction.senderAddress[31],
|
|
||||||
tx.transaction.recipientAddress[0], tx.transaction.recipientAddress[31],
|
|
||||||
tx.transaction.amount, tx.transaction.fee);
|
|
||||||
|
|
||||||
if (!MineBlock(block)) {
|
for (uint64_t mined = 0; mined < blocksToMine; ++mined) {
|
||||||
fprintf(stderr, "failed to mine block within nonce range\n");
|
block_t* block = Block_Create();
|
||||||
Block_Destroy(block);
|
if (!block) {
|
||||||
secp256k1_context_destroy(secpCtx);
|
fprintf(stderr, "failed to create block\n");
|
||||||
Chain_Destroy(chain);
|
Chain_Destroy(chain);
|
||||||
RandomX_Destroy();
|
RandomX_Destroy();
|
||||||
return 1;
|
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));
|
||||||
|
}
|
||||||
|
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]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Chain_AddBlock(chain, block)) {
|
if (!Chain_SaveToFile(chain, chainDataDir, currentSupply)) {
|
||||||
fprintf(stderr, "failed to append block to chain\n");
|
|
||||||
Block_Destroy(block);
|
|
||||||
secp256k1_context_destroy(secpCtx);
|
|
||||||
Chain_Destroy(chain);
|
|
||||||
RandomX_Destroy();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Mined block %llu with nonce %llu and chain size %zu\n",
|
|
||||||
(unsigned long long)block->header.blockNumber,
|
|
||||||
(unsigned long long)block->header.nonce,
|
|
||||||
Chain_Size(chain));
|
|
||||||
|
|
||||||
printf("Block hash (SHA256): ");
|
|
||||||
uint8_t blockHash[32];
|
|
||||||
Block_CalculateHash(block, blockHash);
|
|
||||||
for (size_t i = 0; i < 32; ++i) {
|
|
||||||
printf("%02x", blockHash[i]);
|
|
||||||
}
|
|
||||||
printf("\nBlock hash (RandomX): ");
|
|
||||||
uint8_t randomXHash[32];
|
|
||||||
Block_CalculateRandomXHash(block, randomXHash);
|
|
||||||
for (size_t i = 0; i < 32; ++i) {
|
|
||||||
printf("%02x", randomXHash[i]);
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
if (!Chain_SaveToFile(chain, chainDataDir)) {
|
|
||||||
fprintf(stderr, "failed to save chain to %s\n", chainDataDir);
|
fprintf(stderr, "failed to save chain to %s\n", chainDataDir);
|
||||||
} else {
|
} else {
|
||||||
printf("Saved chain with %zu blocks to %s\n", Chain_Size(chain), chainDataDir);
|
printf("Saved chain with %zu blocks to %s (supply=%llu)\n",
|
||||||
|
Chain_Size(chain),
|
||||||
|
chainDataDir,
|
||||||
|
(unsigned long long)currentSupply.limbs[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chain currently stores a copy of block_t that references the same tx array pointer,
|
|
||||||
// so we do not destroy `block` here to avoid invalidating chain data.
|
|
||||||
secp256k1_context_destroy(secpCtx);
|
|
||||||
|
|
||||||
Chain_Destroy(chain);
|
Chain_Destroy(chain);
|
||||||
|
RandomX_Destroy();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user