orphans and wallet files
This commit is contained in:
@@ -14,6 +14,9 @@ extern uint256_t currentSupply;
|
|||||||
extern uint64_t currentReward;
|
extern uint64_t currentReward;
|
||||||
extern uint32_t difficultyTarget;
|
extern uint32_t difficultyTarget;
|
||||||
extern const char* chainDataDir;
|
extern const char* chainDataDir;
|
||||||
|
extern unsigned short listenPort;
|
||||||
|
extern bool echoPeersEnabled;
|
||||||
|
extern bool forceOrphanReorgEnabled;
|
||||||
|
|
||||||
// Global synchronization primitives for runtime state
|
// Global synchronization primitives for runtime state
|
||||||
extern pthread_rwlock_t chainLock; // protects chain structure and related mutations
|
extern pthread_rwlock_t chainLock; // protects chain structure and related mutations
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ static inline bool GenerateTestMinerIdentity(uint8_t privateKey[32], uint8_t com
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool GenerateRandomTestAddress(uint8_t outAddress[32]) {
|
static inline bool GenerateRandomTestAddress(uint8_t outAddress[32], uint8_t outPrivateKey[32], uint8_t outCompressedPubkey[33]) {
|
||||||
if (!outAddress) {
|
if (!outAddress) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -200,11 +200,18 @@ static inline bool GenerateRandomTestAddress(uint8_t outAddress[32]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AddressFromCompressedPubkey(compressedPubkey, outAddress);
|
AddressFromCompressedPubkey(compressedPubkey, outAddress);
|
||||||
|
if (outPrivateKey) {
|
||||||
|
memcpy(outPrivateKey, privateKey, 32);
|
||||||
|
}
|
||||||
|
if (outCompressedPubkey) {
|
||||||
|
memcpy(outCompressedPubkey, compressedPubkey, 33);
|
||||||
|
}
|
||||||
secp256k1_context_destroy(ctx);
|
secp256k1_context_destroy(ctx);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
secp256k1_context_destroy(ctx);
|
secp256k1_context_destroy(ctx);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -467,118 +467,134 @@ bool Chain_SaveToFile(blockchain_t* chain, const char* dirpath, uint256_t curren
|
|||||||
if (!BuildPath(tablePath, sizeof(tablePath), dirpath, "chain.table")) {
|
if (!BuildPath(tablePath, sizeof(tablePath), dirpath, "chain.table")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find metadata file (create if not exists) to get the saved chain size (+ other things)
|
|
||||||
FILE* metaFile = fopen(metaPath, "rb+");
|
|
||||||
FILE* chainFile = fopen(chainPath, "rb+");
|
|
||||||
FILE* tableFile = fopen(tablePath, "rb+");
|
|
||||||
if (!metaFile || !chainFile || !tableFile) {
|
|
||||||
// Just overwrite everything
|
|
||||||
metaFile = fopen(metaPath, "wb+");
|
|
||||||
if (!metaFile) { return false; }
|
|
||||||
|
|
||||||
// Initialize metadata with size 0
|
char metaTmpPath[512];
|
||||||
size_t initialSize = 0;
|
char chainTmpPath[512];
|
||||||
fwrite(&initialSize, sizeof(size_t), 1, metaFile);
|
char tableTmpPath[512];
|
||||||
// Write last block hash (32 bytes of zeros for now)
|
if (!BuildPath(metaTmpPath, sizeof(metaTmpPath), dirpath, "chain.meta.tmp") ||
|
||||||
uint8_t zeroHash[32] = {0};
|
!BuildPath(chainTmpPath, sizeof(chainTmpPath), dirpath, "chain.data.tmp") ||
|
||||||
fwrite(zeroHash, sizeof(uint8_t), 32, metaFile);
|
!BuildPath(tableTmpPath, sizeof(tableTmpPath), dirpath, "chain.table.tmp")) {
|
||||||
uint256_t zeroSupply = {0};
|
|
||||||
fwrite(&zeroSupply, sizeof(uint256_t), 1, metaFile);
|
|
||||||
uint32_t initialTarget = INITIAL_DIFFICULTY;
|
|
||||||
fwrite(&initialTarget, sizeof(uint32_t), 1, metaFile);
|
|
||||||
uint64_t initialReward = 0;
|
|
||||||
fwrite(&initialReward, sizeof(uint64_t), 1, metaFile);
|
|
||||||
|
|
||||||
chainFile = fopen(chainPath, "wb+");
|
|
||||||
if (!chainFile) { return false; }
|
|
||||||
|
|
||||||
tableFile = fopen(tablePath, "wb+");
|
|
||||||
if (!tableFile) { return false; }
|
|
||||||
|
|
||||||
// TODO: Potentially some other things here, we'll see
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read
|
|
||||||
size_t savedSize = 0;
|
|
||||||
fread(&savedSize, sizeof(size_t), 1, metaFile);
|
|
||||||
uint8_t lastSavedHash[32];
|
|
||||||
fread(lastSavedHash, sizeof(uint8_t), 32, metaFile);
|
|
||||||
|
|
||||||
// Assume chain saved is valid, and that the chain in memory is valid (as LoadFromFile will verify the saved one)
|
|
||||||
if (savedSize > DynArr_size(chain->blocks)) {
|
|
||||||
// Saved chain is longer than current chain, this should not happen if we are always saving the current chain, but just in case, fail to save to avoid overwriting a potentially valid longer chain with a shorter one.
|
|
||||||
fclose(metaFile);
|
|
||||||
fclose(chainFile);
|
|
||||||
fclose(tableFile);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filename format: dirpath/chain.data
|
|
||||||
// File format: ([block_header][num_transactions][transactions...])[*length] - since block_header is fixed size, LoadFromFile will only read those by default
|
|
||||||
|
|
||||||
fseek(chainFile, 0, SEEK_END); // Seek to the end of those files
|
|
||||||
fseek(tableFile, 0, SEEK_END);
|
|
||||||
long pos = ftell(chainFile);
|
|
||||||
if (pos < 0) {
|
|
||||||
fclose(metaFile);
|
|
||||||
fclose(chainFile);
|
|
||||||
fclose(tableFile);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
pthread_rwlock_wrlock(&chainLock);
|
||||||
for (size_t i = savedSize; i < DynArr_size(chain->blocks); i++) {
|
|
||||||
|
FILE* metaFile = fopen(metaTmpPath, "wb+");
|
||||||
|
FILE* chainFile = fopen(chainTmpPath, "wb+");
|
||||||
|
FILE* tableFile = fopen(tableTmpPath, "wb+");
|
||||||
|
if (!metaFile || !chainFile || !tableFile) {
|
||||||
|
if (metaFile) fclose(metaFile);
|
||||||
|
if (chainFile) fclose(chainFile);
|
||||||
|
if (tableFile) fclose(tableFile);
|
||||||
|
pthread_rwlock_unlock(&chainLock);
|
||||||
|
remove(metaTmpPath);
|
||||||
|
remove(chainTmpPath);
|
||||||
|
remove(tableTmpPath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t chainSize = DynArr_size(chain->blocks);
|
||||||
|
uint64_t byteCount = 0;
|
||||||
|
for (size_t i = 0; i < chainSize; ++i) {
|
||||||
block_t* blk = (block_t*)DynArr_at(chain->blocks, i);
|
block_t* blk = (block_t*)DynArr_at(chain->blocks, i);
|
||||||
if (!blk) {
|
if (!blk) {
|
||||||
pthread_rwlock_unlock(&chainLock);
|
|
||||||
fclose(metaFile);
|
fclose(metaFile);
|
||||||
fclose(chainFile);
|
fclose(chainFile);
|
||||||
fclose(tableFile);
|
fclose(tableFile);
|
||||||
|
pthread_rwlock_unlock(&chainLock);
|
||||||
|
remove(metaTmpPath);
|
||||||
|
remove(chainTmpPath);
|
||||||
|
remove(tableTmpPath);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t preIncrementByteSize = byteCount;
|
block_t* diskCopy = blk;
|
||||||
|
bool loadedTemp = false;
|
||||||
// Construct file path
|
if (!diskCopy->transactions) {
|
||||||
// Write block header
|
if (!Chain_LoadBlockFromFile(dirpath, (uint64_t)i, true, &diskCopy, NULL) || !diskCopy || !diskCopy->transactions) {
|
||||||
fwrite(&blk->header, sizeof(block_header_t), 1, chainFile);
|
if (loadedTemp && diskCopy) {
|
||||||
size_t txSize = DynArr_size(blk->transactions);
|
Block_Destroy(diskCopy);
|
||||||
fwrite(&txSize, sizeof(size_t), 1, chainFile); // Write number of transactions
|
}
|
||||||
byteCount += sizeof(block_header_t) + sizeof(size_t);
|
|
||||||
// Write transactions
|
|
||||||
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(metaFile);
|
||||||
|
fclose(chainFile);
|
||||||
fclose(tableFile);
|
fclose(tableFile);
|
||||||
|
pthread_rwlock_unlock(&chainLock);
|
||||||
|
remove(metaTmpPath);
|
||||||
|
remove(chainTmpPath);
|
||||||
|
remove(tableTmpPath);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
loadedTemp = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint64_t blockStart = byteCount;
|
||||||
|
if (fwrite(&diskCopy->header, sizeof(block_header_t), 1, chainFile) != 1) {
|
||||||
|
if (loadedTemp) Block_Destroy(diskCopy);
|
||||||
|
fclose(metaFile);
|
||||||
|
fclose(chainFile);
|
||||||
|
fclose(tableFile);
|
||||||
|
pthread_rwlock_unlock(&chainLock);
|
||||||
|
remove(metaTmpPath);
|
||||||
|
remove(chainTmpPath);
|
||||||
|
remove(tableTmpPath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t txSize = DynArr_size(diskCopy->transactions);
|
||||||
|
if (fwrite(&txSize, sizeof(size_t), 1, chainFile) != 1) {
|
||||||
|
if (loadedTemp) Block_Destroy(diskCopy);
|
||||||
|
fclose(metaFile);
|
||||||
|
fclose(chainFile);
|
||||||
|
fclose(tableFile);
|
||||||
|
pthread_rwlock_unlock(&chainLock);
|
||||||
|
remove(metaTmpPath);
|
||||||
|
remove(chainTmpPath);
|
||||||
|
remove(tableTmpPath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
byteCount += sizeof(block_header_t) + sizeof(size_t);
|
||||||
|
|
||||||
|
for (size_t j = 0; j < txSize; ++j) {
|
||||||
|
signed_transaction_t* tx = (signed_transaction_t*)DynArr_at(diskCopy->transactions, j);
|
||||||
|
if (!tx || fwrite(tx, sizeof(signed_transaction_t), 1, chainFile) != 1) {
|
||||||
|
if (loadedTemp) Block_Destroy(diskCopy);
|
||||||
|
fclose(metaFile);
|
||||||
|
fclose(chainFile);
|
||||||
|
fclose(tableFile);
|
||||||
|
pthread_rwlock_unlock(&chainLock);
|
||||||
|
remove(metaTmpPath);
|
||||||
|
remove(chainTmpPath);
|
||||||
|
remove(tableTmpPath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
byteCount += sizeof(signed_transaction_t);
|
byteCount += sizeof(signed_transaction_t);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create an entry in the block table
|
|
||||||
block_table_entry_t entry;
|
block_table_entry_t entry;
|
||||||
entry.blockNumber = i;
|
entry.blockNumber = i;
|
||||||
entry.byteNumber = preIncrementByteSize;
|
entry.byteNumber = blockStart;
|
||||||
entry.blockSize = byteCount - preIncrementByteSize;
|
entry.blockSize = byteCount - blockStart;
|
||||||
fwrite(&entry, sizeof(block_table_entry_t), 1, tableFile);
|
if (fwrite(&entry, sizeof(block_table_entry_t), 1, tableFile) != 1) {
|
||||||
|
if (loadedTemp) Block_Destroy(diskCopy);
|
||||||
|
fclose(metaFile);
|
||||||
|
fclose(chainFile);
|
||||||
|
fclose(tableFile);
|
||||||
|
pthread_rwlock_unlock(&chainLock);
|
||||||
|
remove(metaTmpPath);
|
||||||
|
remove(chainTmpPath);
|
||||||
|
remove(tableTmpPath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
DynArr_destroy(blk->transactions);
|
if (loadedTemp) {
|
||||||
blk->transactions = NULL; // Clear transactions to save memory since they're now saved on disk
|
Block_Destroy(diskCopy);
|
||||||
|
} else if (blk->transactions) {
|
||||||
|
DynArr_destroy(blk->transactions);
|
||||||
|
blk->transactions = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_rwlock_unlock(&chainLock);
|
size_t newSize = chainSize;
|
||||||
|
|
||||||
// Update metadata with new size and last block hash
|
|
||||||
size_t newSize = DynArr_size(chain->blocks);
|
|
||||||
fseek(metaFile, 0, SEEK_SET);
|
fseek(metaFile, 0, SEEK_SET);
|
||||||
fwrite(&newSize, sizeof(size_t), 1, metaFile);
|
fwrite(&newSize, sizeof(size_t), 1, metaFile);
|
||||||
uint32_t difficultyTarget = INITIAL_DIFFICULTY;
|
uint32_t difficultyTarget = INITIAL_DIFFICULTY;
|
||||||
@@ -596,16 +612,23 @@ bool Chain_SaveToFile(blockchain_t* chain, const char* dirpath, uint256_t curren
|
|||||||
fwrite(&difficultyTarget, sizeof(uint32_t), 1, metaFile);
|
fwrite(&difficultyTarget, sizeof(uint32_t), 1, metaFile);
|
||||||
fwrite(¤tReward, sizeof(uint64_t), 1, metaFile);
|
fwrite(¤tReward, sizeof(uint64_t), 1, metaFile);
|
||||||
|
|
||||||
// Safety
|
|
||||||
fflush(metaFile);
|
fflush(metaFile);
|
||||||
fflush(chainFile);
|
fflush(chainFile);
|
||||||
fflush(tableFile);
|
fflush(tableFile);
|
||||||
|
|
||||||
// Close all pointers
|
|
||||||
fclose(metaFile);
|
fclose(metaFile);
|
||||||
fclose(chainFile);
|
fclose(chainFile);
|
||||||
fclose(tableFile);
|
fclose(tableFile);
|
||||||
|
|
||||||
|
if (rename(metaTmpPath, metaPath) != 0 || rename(chainTmpPath, chainPath) != 0 || rename(tableTmpPath, tablePath) != 0) {
|
||||||
|
pthread_rwlock_unlock(&chainLock);
|
||||||
|
remove(metaTmpPath);
|
||||||
|
remove(chainTmpPath);
|
||||||
|
remove(tableTmpPath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_rwlock_unlock(&chainLock);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
157
src/main.c
157
src/main.c
@@ -28,6 +28,9 @@
|
|||||||
|
|
||||||
blockchain_t* currentChain = NULL;
|
blockchain_t* currentChain = NULL;
|
||||||
const char* chainDataDir = CHAIN_DATA_DIR;
|
const char* chainDataDir = CHAIN_DATA_DIR;
|
||||||
|
unsigned short listenPort = LISTEN_PORT;
|
||||||
|
bool echoPeersEnabled = ECHO_PEERS != 0;
|
||||||
|
bool forceOrphanReorgEnabled = false;
|
||||||
uint256_t currentSupply = {{0, 0, 0, 0}};
|
uint256_t currentSupply = {{0, 0, 0, 0}};
|
||||||
uint64_t currentReward = 750000000000ULL;
|
uint64_t currentReward = 750000000000ULL;
|
||||||
|
|
||||||
@@ -42,6 +45,32 @@ void handle_sigint(int sig) {
|
|||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ApplyRuntimeConfigFromEnv(void) {
|
||||||
|
const char* dataDir = getenv("SKALACOIN_CHAIN_DATA_DIR");
|
||||||
|
if (dataDir && dataDir[0] != '\0') {
|
||||||
|
chainDataDir = dataDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* portStr = getenv("SKALACOIN_LISTEN_PORT");
|
||||||
|
if (portStr && portStr[0] != '\0') {
|
||||||
|
char* end = NULL;
|
||||||
|
long parsed = strtol(portStr, &end, 10);
|
||||||
|
if (end != portStr && *end == '\0' && parsed > 0 && parsed <= 65535) {
|
||||||
|
listenPort = (unsigned short)parsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* echoStr = getenv("SKALACOIN_ECHO_PEERS");
|
||||||
|
if (echoStr && echoStr[0] != '\0') {
|
||||||
|
echoPeersEnabled = (strcmp(echoStr, "0") != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* forceOrphanStr = getenv("SKALACOIN_FORCE_ORPHAN_REORG");
|
||||||
|
if (forceOrphanStr && forceOrphanStr[0] != '\0') {
|
||||||
|
forceOrphanReorgEnabled = (strcmp(forceOrphanStr, "0") != 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t difficultyTarget = INITIAL_DIFFICULTY;
|
uint32_t difficultyTarget = INITIAL_DIFFICULTY;
|
||||||
|
|
||||||
static bool MineBlock(block_t* block) {
|
static bool MineBlock(block_t* block) {
|
||||||
@@ -295,6 +324,14 @@ static bool MineAndAppendBlock(blockchain_t* chain,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t coinbaseAmount = 0;
|
||||||
|
if (block->transactions && DynArr_size(block->transactions) > 0) {
|
||||||
|
signed_transaction_t* firstTx = (signed_transaction_t*)DynArr_at(block->transactions, 0);
|
||||||
|
if (firstTx && Address_IsCoinbase(firstTx->transaction.senderAddress)) {
|
||||||
|
coinbaseAmount = firstTx->transaction.amount1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// After successfully appending a block, attempt to attach any orphans.
|
// After successfully appending a block, attempt to attach any orphans.
|
||||||
size_t attached = OrphanPool_AttemptAttach(chain);
|
size_t attached = OrphanPool_AttemptAttach(chain);
|
||||||
if (attached > 0) {
|
if (attached > 0) {
|
||||||
@@ -304,14 +341,6 @@ static bool MineAndAppendBlock(blockchain_t* chain,
|
|||||||
BalanceSheet_SaveToFile(chainDataDir);
|
BalanceSheet_SaveToFile(chainDataDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t coinbaseAmount = 0;
|
|
||||||
if (block->transactions && DynArr_size(block->transactions) > 0) {
|
|
||||||
signed_transaction_t* firstTx = (signed_transaction_t*)DynArr_at(block->transactions, 0);
|
|
||||||
if (firstTx && Address_IsCoinbase(firstTx->transaction.senderAddress)) {
|
|
||||||
coinbaseAmount = firstTx->transaction.amount1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(void)uint256_add_u64(currentSupply, coinbaseAmount);
|
(void)uint256_add_u64(currentSupply, coinbaseAmount);
|
||||||
|
|
||||||
uint8_t canonicalHash[32];
|
uint8_t canonicalHash[32];
|
||||||
@@ -486,6 +515,16 @@ static bool VerifyChainFully(blockchain_t* chain) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use when error
|
||||||
|
void KillEverythingAndExit(net_node_t* node, blockchain_t* chain) {
|
||||||
|
Node_Destroy(node);
|
||||||
|
currentChain = NULL;
|
||||||
|
Chain_Destroy(chain);
|
||||||
|
Block_ShutdownPowContext();
|
||||||
|
BalanceSheet_Destroy();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
//(void)argc;
|
//(void)argc;
|
||||||
//(void)argv;
|
//(void)argv;
|
||||||
@@ -510,6 +549,8 @@ int main(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ApplyRuntimeConfigFromEnv();
|
||||||
|
|
||||||
signal(SIGINT, handle_sigint);
|
signal(SIGINT, handle_sigint);
|
||||||
srand((unsigned int)time(NULL));
|
srand((unsigned int)time(NULL));
|
||||||
|
|
||||||
@@ -577,9 +618,79 @@ int main(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Separate loading into its own header
|
||||||
|
// Load the wallet from disk or generate new random identity
|
||||||
|
|
||||||
uint8_t minerAddress[32];
|
uint8_t minerAddress[32];
|
||||||
uint8_t minerPrivateKey[32];
|
uint8_t minerPrivateKey[32];
|
||||||
uint8_t minerCompressedPubkey[33];
|
uint8_t minerCompressedPubkey[33];
|
||||||
|
bool loadedWallet = false;
|
||||||
|
|
||||||
|
// Attempt load
|
||||||
|
char* path = "chain_data/wallet.data"; // TODO: Don't hardcode path
|
||||||
|
FILE* walletFile = fopen(path, "rb");
|
||||||
|
if (walletFile) {
|
||||||
|
size_t read = fread(minerPrivateKey, 1, 32, walletFile);
|
||||||
|
if (read != 32) {
|
||||||
|
fprintf(stderr, "failed to read wallet file\n");
|
||||||
|
fclose(walletFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
read = fread(minerCompressedPubkey, 1, 33, walletFile);
|
||||||
|
if (read != 33) {
|
||||||
|
fprintf(stderr, "failed to read wallet file\n");
|
||||||
|
fclose(walletFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
read = fread(minerAddress, 1, 32, walletFile);
|
||||||
|
if (read != 32) {
|
||||||
|
fprintf(stderr, "failed to read wallet file\n");
|
||||||
|
fclose(walletFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(walletFile);
|
||||||
|
loadedWallet = true;
|
||||||
|
} else if (errno != ENOENT || errno != EISDIR || errno != EACCES || errno != EROFS || !loadedWallet) {
|
||||||
|
fprintf(stderr, "failed to open wallet file: %s\n generating new wallet...\n", strerror(errno));
|
||||||
|
if (!GenerateRandomTestAddress(minerAddress, minerPrivateKey, minerCompressedPubkey)) {
|
||||||
|
fprintf(stderr, "failed to generate test miner keypair\n");
|
||||||
|
KillEverythingAndExit(node, chain);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the generated wallet to disk for future runs
|
||||||
|
walletFile = fopen(path, "wb");
|
||||||
|
if (!walletFile) {
|
||||||
|
fprintf(stderr, "failed to create wallet file: %s\n", strerror(errno));
|
||||||
|
KillEverythingAndExit(node, chain);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t written = fwrite(minerPrivateKey, 1, 32, walletFile);
|
||||||
|
if (written != 32) {
|
||||||
|
fprintf(stderr, "failed to write wallet file\n");
|
||||||
|
fclose(walletFile);
|
||||||
|
KillEverythingAndExit(node, chain);
|
||||||
|
}
|
||||||
|
|
||||||
|
written = fwrite(minerCompressedPubkey, 1, 33, walletFile);
|
||||||
|
if (written != 33) {
|
||||||
|
fprintf(stderr, "failed to write wallet file\n");
|
||||||
|
fclose(walletFile);
|
||||||
|
KillEverythingAndExit(node, chain);
|
||||||
|
}
|
||||||
|
|
||||||
|
written = fwrite(minerAddress, 1, 32, walletFile);
|
||||||
|
if (written != 32) {
|
||||||
|
fprintf(stderr, "failed to write wallet file\n");
|
||||||
|
fclose(walletFile);
|
||||||
|
KillEverythingAndExit(node, chain);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(walletFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*uint8_t minerAddress[32];
|
||||||
|
uint8_t minerPrivateKey[32];
|
||||||
|
uint8_t minerCompressedPubkey[33];
|
||||||
if (!GenerateTestMinerIdentity(minerPrivateKey, minerCompressedPubkey, minerAddress)) {
|
if (!GenerateTestMinerIdentity(minerPrivateKey, minerCompressedPubkey, minerAddress)) {
|
||||||
fprintf(stderr, "failed to generate test miner keypair\n");
|
fprintf(stderr, "failed to generate test miner keypair\n");
|
||||||
Node_Destroy(node);
|
Node_Destroy(node);
|
||||||
@@ -588,7 +699,7 @@ int main(int argc, char* argv[]) {
|
|||||||
Block_ShutdownPowContext();
|
Block_ShutdownPowContext();
|
||||||
BalanceSheet_Destroy();
|
BalanceSheet_Destroy();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
char minerAddressHex[65];
|
char minerAddressHex[65];
|
||||||
AddressToHexString(minerAddress, minerAddressHex);
|
AddressToHexString(minerAddress, minerAddressHex);
|
||||||
@@ -1084,9 +1195,10 @@ int main(int argc, char* argv[]) {
|
|||||||
|
|
||||||
if (strcmp(cmd, "connect") == 0) {
|
if (strcmp(cmd, "connect") == 0) {
|
||||||
char* ipStr = strtok(NULL, " \t");
|
char* ipStr = strtok(NULL, " \t");
|
||||||
|
char* portStr = strtok(NULL, " \t");
|
||||||
char* extra = strtok(NULL, " \t");
|
char* extra = strtok(NULL, " \t");
|
||||||
if (!ipStr || extra) {
|
if (!ipStr || extra) {
|
||||||
printf("usage: connect <ipv4>\n");
|
printf("usage: connect <ipv4> [port]\n");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1095,16 +1207,31 @@ int main(int argc, char* argv[]) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Node_ConnectPeer(node, ipStr, LISTEN_PORT) != 0) {
|
unsigned short peerPort = listenPort;
|
||||||
|
if (portStr) {
|
||||||
|
char* end = NULL;
|
||||||
|
long parsedPort = strtol(portStr, &end, 10);
|
||||||
|
if (*portStr == '\0' || portStr[0] == '-' || (end && *end != '\0') || parsedPort <= 0 || parsedPort > 65535) {
|
||||||
|
printf("invalid port\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
peerPort = (unsigned short)parsedPort;
|
||||||
|
if (strtok(NULL, " \t")) {
|
||||||
|
printf("usage: connect <ipv4> [port]\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Node_ConnectPeer(node, ipStr, peerPort) != 0) {
|
||||||
if (errno == ETIMEDOUT) {
|
if (errno == ETIMEDOUT) {
|
||||||
printf("failed to connect to %s:%u (timeout)\n", ipStr, (unsigned int)LISTEN_PORT);
|
printf("failed to connect to %s:%u (timeout)\n", ipStr, (unsigned int)peerPort);
|
||||||
} else {
|
} else {
|
||||||
printf("failed to connect to %s:%u\n", ipStr, (unsigned int)LISTEN_PORT);
|
printf("failed to connect to %s:%u\n", ipStr, (unsigned int)peerPort);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("connect requested to %s:%u\n", ipStr, (unsigned int)LISTEN_PORT);
|
printf("connect requested to %s:%u\n", ipStr, (unsigned int)peerPort);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1166,7 +1293,7 @@ int main(int argc, char* argv[]) {
|
|||||||
|
|
||||||
if (strcmp(cmd, "genaddr") == 0) {
|
if (strcmp(cmd, "genaddr") == 0) {
|
||||||
uint8_t testAddress[32];
|
uint8_t testAddress[32];
|
||||||
if (!GenerateRandomTestAddress(testAddress)) {
|
if (!GenerateRandomTestAddress(testAddress, NULL, NULL)) {
|
||||||
printf("failed to generate address\n");
|
printf("failed to generate address\n");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,6 +122,13 @@ static node_block_accept_result_t Node_ParseAndAcceptBlock(const unsigned char*
|
|||||||
return NODE_BLOCK_REJECTED;
|
return NODE_BLOCK_REJECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Temporary debug mode: force network-received blocks through the orphan pool to exercise reorg handling.
|
||||||
|
if (forceOrphanReorgEnabled && blk->header.blockNumber > 0) {
|
||||||
|
OrphanPool_Insert(blk, blockHeight);
|
||||||
|
printf("Forced orphan BLOCK_DATA at height %" PRIu64 "\n", blockHeight);
|
||||||
|
return NODE_BLOCK_ORPHAN_QUEUED;
|
||||||
|
}
|
||||||
|
|
||||||
// If parent is missing, insert into orphan pool instead of rejecting immediately.
|
// If parent is missing, insert into orphan pool instead of rejecting immediately.
|
||||||
uint64_t chainSize = Chain_Size(currentChain);
|
uint64_t chainSize = Chain_Size(currentChain);
|
||||||
if (blk->header.blockNumber > chainSize) {
|
if (blk->header.blockNumber > chainSize) {
|
||||||
@@ -232,7 +239,7 @@ net_node_t* Node_Create() {
|
|||||||
pthread_mutex_init(&node->outboundLock, NULL);
|
pthread_mutex_init(&node->outboundLock, NULL);
|
||||||
node->seenBlocks = DynSet_Create(32); // 32-byte canonical hashes
|
node->seenBlocks = DynSet_Create(32); // 32-byte canonical hashes
|
||||||
|
|
||||||
TcpServer_Init(node->server, LISTEN_PORT, "0.0.0.0");
|
TcpServer_Init(node->server, listenPort, "0.0.0.0");
|
||||||
|
|
||||||
node->server->owner = node;
|
node->server->owner = node;
|
||||||
node->server->on_connect = Node_Server_OnConnect;
|
node->server->on_connect = Node_Server_OnConnect;
|
||||||
@@ -382,14 +389,13 @@ void Node_Server_OnConnect(tcp_connection_t* client) {
|
|||||||
Node_ForwardConnect(node, client);
|
Node_ForwardConnect(node, client);
|
||||||
printf("Inbound node connected: %u\n", client ? client->connectionId : 0U);
|
printf("Inbound node connected: %u\n", client ? client->connectionId : 0U);
|
||||||
|
|
||||||
#if ECHO_PEERS
|
if (echoPeersEnabled && node && client) {
|
||||||
if (node && client) {
|
// Attempt to create an outbound connection back to the peer's IP on our configured port.
|
||||||
// Attempt to create an outbound connection back to the peer's IP on our LISTEN_PORT.
|
|
||||||
// We avoid connecting if we already have an outbound to the same IP.
|
// We avoid connecting if we already have an outbound to the same IP.
|
||||||
char ipbuf[INET_ADDRSTRLEN];
|
char ipbuf[INET_ADDRSTRLEN];
|
||||||
if (inet_ntop(AF_INET, &client->peerAddr.sin_addr, ipbuf, sizeof(ipbuf))) {
|
if (inet_ntop(AF_INET, &client->peerAddr.sin_addr, ipbuf, sizeof(ipbuf))) {
|
||||||
// Use LISTEN_PORT as target port for peer's listening service, not the ephemeral source port.
|
// Use the configured port as the target port for the peer's listening service.
|
||||||
unsigned short targetPort = LISTEN_PORT;
|
unsigned short targetPort = listenPort;
|
||||||
|
|
||||||
int shouldConnect = 1;
|
int shouldConnect = 1;
|
||||||
pthread_mutex_lock(&node->outboundLock);
|
pthread_mutex_lock(&node->outboundLock);
|
||||||
@@ -410,7 +416,6 @@ void Node_Server_OnConnect(tcp_connection_t* client) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Node_Server_OnData(tcp_connection_t* client) {
|
void Node_Server_OnData(tcp_connection_t* client) {
|
||||||
|
|||||||
@@ -38,6 +38,71 @@ void OrphanPool_Insert(block_t* block, uint64_t height) {
|
|||||||
(void)DynArr_push_back(g_orphans, &e);
|
(void)DynArr_push_back(g_orphans, &e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t OrphanPool_TryAdoptBranch(blockchain_t* chain, uint64_t forkHeight) {
|
||||||
|
if (!g_orphans || !chain) return 0;
|
||||||
|
|
||||||
|
DynArr* seq = DYNARR_CREATE(block_t*, 8);
|
||||||
|
if (!seq) return 0;
|
||||||
|
|
||||||
|
size_t cursor = forkHeight;
|
||||||
|
while (1) {
|
||||||
|
bool found = false;
|
||||||
|
size_t count = DynArr_size(g_orphans);
|
||||||
|
for (size_t i = 0; i < count; ++i) {
|
||||||
|
orphan_entry_t* entry = (orphan_entry_t*)DynArr_at(g_orphans, i);
|
||||||
|
if (!entry || !entry->block) continue;
|
||||||
|
if (entry->height == cursor) {
|
||||||
|
(void)DynArr_push_back(seq, &entry->block);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) break;
|
||||||
|
cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t seqCount = DynArr_size(seq);
|
||||||
|
if (seqCount == 0) {
|
||||||
|
DynArr_destroy(seq);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t currentTipHeight = Chain_Size(chain) == 0 ? 0 : Chain_Size(chain) - 1;
|
||||||
|
size_t seqTopHeight = forkHeight + seqCount - 1;
|
||||||
|
if (seqTopHeight <= currentTipHeight) {
|
||||||
|
DynArr_destroy(seq);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t rollbackHeight = (forkHeight == 0) ? 0 : (forkHeight - 1);
|
||||||
|
if (!Chain_RollbackToHeight(chain, rollbackHeight)) {
|
||||||
|
DynArr_destroy(seq);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t attached = 0;
|
||||||
|
for (size_t i = 0; i < seqCount; ++i) {
|
||||||
|
block_t* bptr = *(block_t**)DynArr_at(seq, i);
|
||||||
|
if (!bptr || !Chain_AddBlock(chain, bptr)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t count = DynArr_size(g_orphans);
|
||||||
|
for (size_t j = 0; j < count; ++j) {
|
||||||
|
orphan_entry_t* entry = (orphan_entry_t*)DynArr_at(g_orphans, j);
|
||||||
|
if (entry && entry->block == bptr) {
|
||||||
|
DynArr_remove(g_orphans, j);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attached++;
|
||||||
|
}
|
||||||
|
|
||||||
|
DynArr_destroy(seq);
|
||||||
|
return attached;
|
||||||
|
}
|
||||||
|
|
||||||
size_t OrphanPool_AttemptAttach(blockchain_t* chain) {
|
size_t OrphanPool_AttemptAttach(blockchain_t* chain) {
|
||||||
if (!g_orphans || !chain) return 0;
|
if (!g_orphans || !chain) return 0;
|
||||||
size_t attached = 0;
|
size_t attached = 0;
|
||||||
@@ -67,6 +132,30 @@ size_t OrphanPool_AttemptAttach(blockchain_t* chain) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (parentExists) {
|
if (parentExists) {
|
||||||
|
if (e->height < Chain_Size(chain)) {
|
||||||
|
block_t* local = NULL;
|
||||||
|
if (Chain_GetBlockCopy(chain, (size_t)e->height, &local) && local) {
|
||||||
|
uint8_t localHash[32];
|
||||||
|
uint8_t orphanHash[32];
|
||||||
|
Block_CalculateHash(local, localHash);
|
||||||
|
Block_CalculateHash(e->block, orphanHash);
|
||||||
|
Block_Destroy(local);
|
||||||
|
|
||||||
|
if (memcmp(localHash, orphanHash, 32) != 0) {
|
||||||
|
size_t adopted = OrphanPool_TryAdoptBranch(chain, e->height);
|
||||||
|
if (adopted > 0) {
|
||||||
|
attached += adopted;
|
||||||
|
madeProgress = true;
|
||||||
|
n = DynArr_size(g_orphans);
|
||||||
|
i = (size_t)-1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (local) {
|
||||||
|
Block_Destroy(local);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Verify that the parent's hash matches the orphan's prevHash before attaching.
|
// Verify that the parent's hash matches the orphan's prevHash before attaching.
|
||||||
bool parentMatches = false;
|
bool parentMatches = false;
|
||||||
if (e->height == 0) {
|
if (e->height == 0) {
|
||||||
@@ -84,65 +173,17 @@ size_t OrphanPool_AttemptAttach(blockchain_t* chain) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!parentMatches) {
|
if (!parentMatches) {
|
||||||
// Parent exists but does not match this orphan's prevHash.
|
// Parent exists but does not match this orphan's prevHash.
|
||||||
// Attempt to detect a longer alternate chain in the orphan pool starting at this height.
|
size_t adopted = OrphanPool_TryAdoptBranch(chain, e->height);
|
||||||
// Build a consecutive sequence of orphans from this height upward.
|
if (adopted > 0) {
|
||||||
DynArr* seq = DYNARR_CREATE(block_t*, 8);
|
attached += adopted;
|
||||||
size_t h = e->height;
|
madeProgress = true;
|
||||||
while (1) {
|
n = DynArr_size(g_orphans);
|
||||||
bool found = false;
|
i = (size_t)-1;
|
||||||
size_t gn = DynArr_size(g_orphans);
|
break;
|
||||||
for (size_t gi = 0; gi < gn; ++gi) {
|
}
|
||||||
orphan_entry_t* oe = (orphan_entry_t*)DynArr_at(g_orphans, gi);
|
|
||||||
if (!oe || !oe->block) continue;
|
|
||||||
if (oe->height == h) {
|
|
||||||
(void)DynArr_push_back(seq, &oe->block);
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) break;
|
|
||||||
h++;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t seqCount = DynArr_size(seq);
|
continue;
|
||||||
if (seqCount > 0) {
|
|
||||||
size_t seqTopHeight = e->height + seqCount - 1;
|
|
||||||
if (seqTopHeight >= Chain_Size(chain)) {
|
|
||||||
// Found a candidate longer branch. Perform rollback to fork height and attach sequence.
|
|
||||||
if (Chain_RollbackToHeight(chain, (size_t)e->height)) {
|
|
||||||
// Attach in-order
|
|
||||||
for (size_t si = 0; si < seqCount; ++si) {
|
|
||||||
block_t* bptr = *(block_t**)DynArr_at(seq, si);
|
|
||||||
if (!Chain_AddBlock(chain, bptr)) {
|
|
||||||
// failed to add; stop attempting further
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Remove the attached orphan from pool but keep the block object to preserve transactions in-memory (consistent with existing behavior)
|
|
||||||
// Find and remove corresponding orphan entry
|
|
||||||
size_t gn2 = DynArr_size(g_orphans);
|
|
||||||
for (size_t gi2 = 0; gi2 < gn2; ++gi2) {
|
|
||||||
orphan_entry_t* oe2 = (orphan_entry_t*)DynArr_at(g_orphans, gi2);
|
|
||||||
if (oe2 && oe2->block == bptr) {
|
|
||||||
DynArr_remove(g_orphans, gi2);
|
|
||||||
gn2 = DynArr_size(g_orphans);
|
|
||||||
gi2 = (size_t)-1; // restart search if needed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
attached += seqCount;
|
|
||||||
madeProgress = true;
|
|
||||||
DynArr_destroy(seq);
|
|
||||||
// reset outer loop
|
|
||||||
n = DynArr_size(g_orphans);
|
|
||||||
i = (size_t)-1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DynArr_destroy(seq);
|
|
||||||
// If we didn't perform a reorg/attach, skip for now.
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to add to chain
|
// Try to add to chain
|
||||||
|
|||||||
Reference in New Issue
Block a user