segfaults and orphans
This commit is contained in:
@@ -151,6 +151,15 @@ bool Chain_AddBlock(blockchain_t* chain, block_t* block) {
|
||||
pthread_rwlock_wrlock(&chainLock);
|
||||
pthread_mutex_lock(&balanceSheetLock);
|
||||
|
||||
// Ensure the incoming block's header.blockNumber matches the index it will be appended at.
|
||||
size_t expectedIndex = DynArr_size(chain->blocks);
|
||||
if (block->header.blockNumber != expectedIndex) {
|
||||
// Mismatched block number; reject to avoid duplicate indices or inconsistent headers.
|
||||
pthread_mutex_unlock(&balanceSheetLock);
|
||||
pthread_rwlock_unlock(&chainLock);
|
||||
return false;
|
||||
}
|
||||
|
||||
do {
|
||||
// First pass: ensure all non-coinbase senders can cover the full spend
|
||||
// (amount1 + amount2 + fee) before mutating the chain or balance sheet.
|
||||
@@ -521,9 +530,12 @@ bool Chain_SaveToFile(blockchain_t* chain, const char* dirpath, uint256_t curren
|
||||
uint64_t byteCount = (uint64_t)pos; // Get the size
|
||||
|
||||
// Save blocks that are not yet saved
|
||||
// Acquire write lock to protect block transaction pointers from concurrent freeing.
|
||||
pthread_rwlock_wrlock(&chainLock);
|
||||
for (size_t i = savedSize; i < DynArr_size(chain->blocks); i++) {
|
||||
block_t* blk = (block_t*)DynArr_at(chain->blocks, i);
|
||||
if (!blk) {
|
||||
pthread_rwlock_unlock(&chainLock);
|
||||
fclose(metaFile);
|
||||
fclose(chainFile);
|
||||
fclose(tableFile);
|
||||
@@ -542,6 +554,7 @@ bool Chain_SaveToFile(blockchain_t* chain, const char* dirpath, uint256_t curren
|
||||
for (size_t j = 0; j < txSize; j++) {
|
||||
signed_transaction_t* tx = (signed_transaction_t*)DynArr_at(blk->transactions, j);
|
||||
if (fwrite(tx, sizeof(signed_transaction_t), 1, chainFile) != 1) {
|
||||
pthread_rwlock_unlock(&chainLock);
|
||||
fclose(chainFile);
|
||||
fclose(metaFile);
|
||||
fclose(tableFile);
|
||||
@@ -562,6 +575,8 @@ bool Chain_SaveToFile(blockchain_t* chain, const char* dirpath, uint256_t curren
|
||||
blk->transactions = NULL; // Clear transactions to save memory since they're now saved on disk
|
||||
}
|
||||
|
||||
pthread_rwlock_unlock(&chainLock);
|
||||
|
||||
// Update metadata with new size and last block hash
|
||||
size_t newSize = DynArr_size(chain->blocks);
|
||||
fseek(metaFile, 0, SEEK_SET);
|
||||
|
||||
@@ -123,17 +123,40 @@ static node_block_accept_result_t Node_ParseAndAcceptBlock(const unsigned char*
|
||||
}
|
||||
|
||||
// If parent is missing, insert into orphan pool instead of rejecting immediately.
|
||||
if (blk->header.blockNumber > 0) {
|
||||
uint64_t parentIndex = blk->header.blockNumber - 1;
|
||||
block_t* parentCopy = NULL;
|
||||
if (parentIndex >= Chain_Size(currentChain) || !Chain_GetBlockCopy(currentChain, (size_t)parentIndex, &parentCopy) || !parentCopy) {
|
||||
// Insert into orphan pool and take ownership of blk
|
||||
OrphanPool_Insert(blk, blockHeight);
|
||||
if (parentCopy) Block_Destroy(parentCopy);
|
||||
printf("Queued orphan BLOCK_DATA at height %" PRIu64 "\n", blockHeight);
|
||||
return NODE_BLOCK_ORPHAN_QUEUED;
|
||||
uint64_t chainSize = Chain_Size(currentChain);
|
||||
if (blk->header.blockNumber > chainSize) {
|
||||
// Parent(s) missing; queue as orphan
|
||||
OrphanPool_Insert(blk, blockHeight);
|
||||
printf("Queued orphan BLOCK_DATA at height %" PRIu64 "\n", blockHeight);
|
||||
return NODE_BLOCK_ORPHAN_QUEUED;
|
||||
} else if (blk->header.blockNumber < chainSize) {
|
||||
// Older block than current chain tip: reject
|
||||
printf("Rejected BLOCK_DATA at height %" PRIu64 ": older than current chain\n", blockHeight);
|
||||
DynArr_destroy(blk->transactions);
|
||||
free(blk);
|
||||
return NODE_BLOCK_REJECTED;
|
||||
} else {
|
||||
// blk->header.blockNumber == chainSize -> candidate to append. Ensure prevHash matches current tip.
|
||||
if (chainSize > 0) {
|
||||
block_t* last = NULL;
|
||||
if (!Chain_GetBlockCopy(currentChain, (size_t)(chainSize - 1), &last) || !last) {
|
||||
// Can't verify parent; queue as orphan conservatively
|
||||
OrphanPool_Insert(blk, blockHeight);
|
||||
printf("Queued orphan BLOCK_DATA at height %" PRIu64 " (unable to verify parent)\n", blockHeight);
|
||||
if (last) Block_Destroy(last);
|
||||
return NODE_BLOCK_ORPHAN_QUEUED;
|
||||
}
|
||||
uint8_t lastHash[32];
|
||||
Block_CalculateHash(last, lastHash);
|
||||
if (memcmp(lastHash, blk->header.prevHash, 32) != 0) {
|
||||
// Conflicting block at same height; queue as orphan until resolved by a subsequent extension.
|
||||
OrphanPool_Insert(blk, blockHeight);
|
||||
Block_Destroy(last);
|
||||
printf("Queued conflicting BLOCK_DATA at same height %" PRIu64 " as orphan\n", blockHeight);
|
||||
return NODE_BLOCK_ORPHAN_QUEUED;
|
||||
}
|
||||
Block_Destroy(last);
|
||||
}
|
||||
Block_Destroy(parentCopy);
|
||||
}
|
||||
|
||||
if (!Chain_AddBlock(currentChain, blk)) {
|
||||
|
||||
@@ -67,6 +67,27 @@ size_t OrphanPool_AttemptAttach(blockchain_t* chain) {
|
||||
}
|
||||
|
||||
if (parentExists) {
|
||||
// Verify that the parent's hash matches the orphan's prevHash before attaching.
|
||||
bool parentMatches = false;
|
||||
if (e->height == 0) {
|
||||
parentMatches = (Chain_Size(chain) == 0);
|
||||
} else {
|
||||
block_t* parent = NULL;
|
||||
if (Chain_GetBlockCopy(chain, (size_t)parentIndex, &parent) && parent) {
|
||||
uint8_t parentHash[32];
|
||||
Block_CalculateHash(parent, parentHash);
|
||||
parentMatches = (memcmp(parentHash, e->block->header.prevHash, 32) == 0);
|
||||
Block_Destroy(parent);
|
||||
} else {
|
||||
parentMatches = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!parentMatches) {
|
||||
// Parent exists but does not match this orphan's prevHash; skip attaching now.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to add to chain
|
||||
if (Chain_AddBlock(chain, e->block)) {
|
||||
attached++;
|
||||
|
||||
Reference in New Issue
Block a user