#include #include #include static Autolykos2Context* g_autolykos2Ctx = NULL; static Autolykos2Context* GetAutolykos2Ctx(void) { if (!g_autolykos2Ctx) { g_autolykos2Ctx = Autolykos2_Create(); } return g_autolykos2Ctx; } void Block_ShutdownPowContext(void) { if (g_autolykos2Ctx) { Autolykos2_Destroy(g_autolykos2Ctx); g_autolykos2Ctx = NULL; } } block_t* Block_Create() { block_t* block = (block_t*)malloc(sizeof(block_t)); if (!block) { return NULL; } memset(&block->header, 0, sizeof(block_header_t)); block->transactions = DYNARR_CREATE(signed_transaction_t, 1); if (!block->transactions) { free(block); return NULL; } return block; } void Block_CalculateHash(const block_t* block, uint8_t* outHash) { if (!block || !outHash) { return; } // 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_CalculateMerkleRoot(const block_t* block, uint8_t* outHash) { if (!block || !block->transactions || !outHash) { return; } 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; } // 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_CalculateAutolykos2Hash(const block_t* block, uint8_t* outHash) { if (!block || !outHash) { return; } // PoW hash is computed from the block header, while canonical block hash remains SHA256. Autolykos2Context* ctx = GetAutolykos2Ctx(); if (!ctx) { memset(outHash, 0, 32); return; } if (!Autolykos2_Hash( ctx, (const uint8_t*)&block->header, sizeof(block_header_t), block->header.nonce, (uint32_t)block->header.blockNumber, outHash )) { memset(outHash, 0, 32); } } void Block_AddTransaction(block_t* block, signed_transaction_t* tx) { if (!block || !tx || !block->transactions) { return; } DynArr_push_back(block->transactions, tx); } void Block_RemoveTransaction(block_t* block, uint8_t* txHash) { if (!block || !txHash || !block->transactions) { return; } for (size_t i = 0; i < DynArr_size(block->transactions); i++) { signed_transaction_t* currentTx = (signed_transaction_t*)DynArr_at(block->transactions, i); if (memcmp(currentTx->signature.txHash, txHash, 32) == 0) { DynArr_remove(block->transactions, i); return; } } } static int Uint256_CompareBE(const uint8_t a[32], const uint8_t b[32]) { for (int i = 0; i < 32; ++i) { if (a[i] < b[i]) return -1; if (a[i] > b[i]) return 1; } return 0; } static bool DecodeCompactTarget(uint32_t nBits, uint8_t target[32]) { memset(target, 0, 32); uint32_t exponent = nBits >> 24; uint32_t mantissa = nBits & 0x007fffff; // ignore sign bit for now bool negative = (nBits & 0x00800000) != 0; if (negative || mantissa == 0) { return false; } // Compute: target = mantissa * 256^(exponent - 3) if (exponent <= 3) { mantissa >>= 8 * (3 - exponent); target[29] = (mantissa >> 16) & 0xff; target[30] = (mantissa >> 8) & 0xff; target[31] = mantissa & 0xff; } else { uint32_t byte_index = exponent - 3; // number of zero-bytes appended on right if (byte_index > 29) { return false; // overflow 256 bits } target[32 - byte_index - 3] = (mantissa >> 16) & 0xff; target[32 - byte_index - 2] = (mantissa >> 8) & 0xff; target[32 - byte_index - 1] = mantissa & 0xff; } return true; } bool Block_HasValidProofOfWork(const block_t* block) { if (!block) { return false; } uint8_t target[32]; if (!DecodeCompactTarget(block->header.difficultyTarget, target)) { return false; } uint8_t hash[32]; Block_CalculateAutolykos2Hash(block, hash); return Uint256_CompareBE(hash, target) <= 0; } bool Block_AllTransactionsValid(const block_t* block) { if (!block || !block->transactions) { return false; } bool hasCoinbase = false; 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 && Address_IsCoinbase(tx->transaction.senderAddress)) { if (hasCoinbase) { return false; // More than one coinbase transaction } hasCoinbase = true; continue; // Coinbase transactions are valid since the miner has the right to create coins. Only rule is one per block. } if (!Transaction_Verify(tx)) { return false; } } return true && hasCoinbase && DynArr_size(block->transactions) > 0; // Every block must have at least one transaction (the coinbase) } void Block_Destroy(block_t* block) { if (!block) return; 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"); } }