stuff
This commit is contained in:
@@ -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