199 lines
7.2 KiB
C
199 lines
7.2 KiB
C
#include <block/chain.h>
|
|
#include <block/transaction.h>
|
|
#include <openssl/sha.h>
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <signal.h>
|
|
|
|
#include <constants.h>
|
|
|
|
#ifndef CHAIN_DATA_DIR
|
|
#define CHAIN_DATA_DIR "chain_data"
|
|
#endif
|
|
|
|
void handle_sigint(int sig) {
|
|
printf("Caught signal %d, exiting...\n", sig);
|
|
Block_ShutdownPowContext();
|
|
exit(0);
|
|
}
|
|
|
|
uint32_t difficultyTarget = INITIAL_DIFFICULTY;
|
|
|
|
static bool MineBlock(block_t* block) {
|
|
if (!block) {
|
|
return false;
|
|
}
|
|
|
|
for (uint64_t nonce = 0;; ++nonce) {
|
|
block->header.nonce = nonce;
|
|
if (Block_HasValidProofOfWork(block)) {
|
|
return true;
|
|
}
|
|
|
|
if (nonce == UINT64_MAX) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
int main(int argc, char* argv[]) {
|
|
signal(SIGINT, handle_sigint);
|
|
|
|
const char* chainDataDir = CHAIN_DATA_DIR;
|
|
const uint64_t blocksToMine = 1000;
|
|
const double targetSeconds = TARGET_BLOCK_TIME;
|
|
|
|
uint256_t currentSupply = uint256_from_u64(0);
|
|
|
|
blockchain_t* chain = Chain_Create();
|
|
if (!chain) {
|
|
fprintf(stderr, "failed to create chain\n");
|
|
return 1;
|
|
}
|
|
|
|
if (!Chain_LoadFromFile(chain, chainDataDir, ¤tSupply, &difficultyTarget)) {
|
|
printf("No existing chain loaded from %s\n", chainDataDir);
|
|
}
|
|
|
|
if (Chain_Size(chain) > 0) {
|
|
if (Chain_IsValid(chain)) {
|
|
printf("Loaded chain with %zu blocks from disk\n", Chain_Size(chain));
|
|
} else {
|
|
fprintf(stderr, "loaded chain is invalid, scrapping, resyncing.\n"); // TODO: Actually implement resyncing from peers instead of just scrapping the chain
|
|
const size_t badSize = Chain_Size(chain);
|
|
|
|
// Delete files (wipe dir)
|
|
for (size_t i = 0; i < badSize; i++) {
|
|
char filePath[256];
|
|
snprintf(filePath, sizeof(filePath), "%s/block_%zu.dat", chainDataDir, i);
|
|
remove(filePath);
|
|
}
|
|
|
|
char metaPath[256];
|
|
snprintf(metaPath, sizeof(metaPath), "%s/chain.meta", chainDataDir);
|
|
remove(metaPath);
|
|
|
|
Chain_Wipe(chain);
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
|
|
uint8_t minerAddress[32];
|
|
SHA256((const unsigned char*)"minicoin-miner-1", strlen("minicoin-miner-1"), minerAddress);
|
|
|
|
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);
|
|
Block_ShutdownPowContext();
|
|
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 = difficultyTarget;
|
|
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);
|
|
Block_ShutdownPowContext();
|
|
return 1;
|
|
}
|
|
|
|
if (!Chain_AddBlock(chain, block)) {
|
|
fprintf(stderr, "failed to append block to chain\n");
|
|
Block_Destroy(block);
|
|
Chain_Destroy(chain);
|
|
Block_ShutdownPowContext();
|
|
return 1;
|
|
}
|
|
|
|
(void)uint256_add_u64(¤tSupply, coinbaseTx.transaction.amount);
|
|
|
|
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],
|
|
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)) {
|
|
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]);
|
|
}
|
|
} else {
|
|
printf("Current chain has %zu blocks, total supply %llu\n", Chain_Size(chain), (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);
|
|
Block_ShutdownPowContext();
|
|
return 0;
|
|
}
|