Skip to content

Commit

Permalink
headersdb_file: updated dogecoin_headers_db_connect_hdr to reorg
Browse files Browse the repository at this point in the history
arith_uint256: added negate, arithmetic and comparisons from xanimo
arith_uint256: updated div_arith_uint256 for long division
arith_uint256: updated get_low64 byte order
arith_uint256: updated uint_to_arith and arith_to_uint256 to calloc
block: updated dogecoin_block_header with chainwork
block: updated dogecoin_block_header_copy for chainwork and auxpow flag
block: updated dogecoin_block_header_new to initialize chainwork
block: updated dogecoin_block_header_new to initialize auxpow flag
block: removed duplicate check initialization from dogecoin_auxpow_block_new
headersdb_file: updated dogecoin_headers_db_free check for prev and genesis
pow: added nhashes_done to check_pow for chainwork
validation: updated check_pow for chainwork
spv: added escape characters to dogecoin_net_spv_post_cmd for shutdown
spv: added bestknownheight reset on invalid block to dogecoin_net_spv_post_cmd
spvnode: moved "done" print after dogecoin_spv_client_free
test: updated block_test to check chainwork of genesis
test: added test_reorg to spv_test
test: updated unittester to test_reorg
  • Loading branch information
edtubbs committed Jan 1, 2024
1 parent 4999c6b commit 5059d54
Show file tree
Hide file tree
Showing 13 changed files with 602 additions and 38 deletions.
12 changes: 11 additions & 1 deletion include/dogecoin/arith_uint256.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,21 @@ typedef struct base_uint_ {

typedef base_uint_ arith_uint256;

void arith_negate(arith_uint256* input);
arith_uint256* init_arith_uint256();
arith_uint256* set_compact(arith_uint256* hash, uint32_t compact, dogecoin_bool *pf_negative, dogecoin_bool *pf_overflow);
uint8_t* arith_to_uint256(const arith_uint256* a);
arith_uint256* uint_to_arith(const uint256* a);
uint64_t get_low64(arith_uint256 pn);
uint64_t get_low64(arith_uint256* a);
arith_uint256* div_arith_uint256(arith_uint256* a, arith_uint256* b);
arith_uint256* add_arith_uint256(arith_uint256* a, arith_uint256* b);
arith_uint256* sub_arith_uint256(arith_uint256* a, arith_uint256* b);
dogecoin_bool arith_uint256_is_zero(const arith_uint256* a);
dogecoin_bool arith_uint256_equal(const arith_uint256* a, const arith_uint256* b);
dogecoin_bool arith_uint256_greater_than(const arith_uint256* a, const arith_uint256* b);
dogecoin_bool arith_uint256_greater_than_or_equal(const arith_uint256* a, const arith_uint256* b);
dogecoin_bool arith_uint256_less_than(const arith_uint256* a, const arith_uint256* b);
dogecoin_bool arith_uint256_less_than_or_equal(const arith_uint256* a, const arith_uint256* b);

LIBDOGECOIN_END_DECL

Expand Down
1 change: 1 addition & 0 deletions include/dogecoin/block.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ typedef struct dogecoin_block_header_ {
uint32_t bits;
uint32_t nonce;
auxpow auxpow[1];
uint256 chainwork;
} dogecoin_block_header;

typedef struct dogecoin_auxpow_block_ {
Expand Down
2 changes: 1 addition & 1 deletion include/dogecoin/pow.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@

LIBDOGECOIN_BEGIN_DECL

dogecoin_bool check_pow(uint256* hash, unsigned int nbits, const dogecoin_chainparams *params);
dogecoin_bool check_pow(uint256* hash, unsigned int nbits, const dogecoin_chainparams *params, uint256* nhashes_done);

LIBDOGECOIN_END_DECL

Expand Down
123 changes: 116 additions & 7 deletions src/arith_uint256.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@

#include <dogecoin/arith_uint256.h>

void arith_negate(arith_uint256* input) {
// Inverting all bits (one's complement)
for (int i = 0; i < WIDTH; i++) {
input->pn[i] = ~input->pn[i];
}
}

arith_uint256* init_arith_uint256() {
arith_uint256* x = dogecoin_calloc(8, sizeof(uint32_t));
int i = 0;
Expand Down Expand Up @@ -95,17 +102,119 @@ arith_uint256* set_compact(arith_uint256* hash, uint32_t compact, dogecoin_bool

arith_uint256* uint_to_arith(const uint256* a)
{
static arith_uint256 b;
memcpy_safe(b.pn, a, sizeof(b.pn));
return &b;
arith_uint256* b = (arith_uint256*)calloc(1, sizeof(arith_uint256));
if (!b) {
return NULL;
}
memcpy_safe(b->pn, a, sizeof(b->pn));
return b;
}

uint8_t* arith_to_uint256(const arith_uint256* a) {
static uint256 b = {0};
uint8_t* b = (uint8_t*)calloc(1, sizeof(uint256));
if (!b) {
return NULL;
}
memcpy_safe(b, a->pn, sizeof(uint256));
return &b[0];
return b;
}

arith_uint256* div_arith_uint256(arith_uint256* a, arith_uint256* b) {
if (arith_uint256_is_zero(b)) {
// Handle division by zero if necessary
return NULL;
}

arith_uint256* quotient = init_arith_uint256();
arith_uint256* remainder = init_arith_uint256();

for (int i = WIDTH * 32 - 1; i >= 0; i--) {
// Left shift remainder by 1 bit
arith_shift_left(remainder, 1);

// Set the least significant bit of remainder to bit i of a
int word_idx = i / 32;
int bit_idx = i % 32;
if ((a->pn[word_idx] & (1 << bit_idx)) != 0) {
remainder->pn[0] |= 1;
}

// Compare remainder with b
if (arith_uint256_greater_than_or_equal(remainder, b)) {
// Subtract b from remainder
arith_uint256* temp = sub_arith_uint256(remainder, b);
memcpy(remainder, temp, sizeof(arith_uint256));
free(temp);

// Set bit i of quotient
quotient->pn[word_idx] |= (1 << bit_idx);
}
}

free(remainder);
return quotient;
}

arith_uint256* add_arith_uint256(arith_uint256* a, arith_uint256* b) {
arith_uint256* result = init_arith_uint256();
uint64_t carry = 0;
for (int i = WIDTH - 1; i >= 0; i--) {
uint64_t sum = (uint64_t)a->pn[i] + b->pn[i] + carry;
result->pn[i] = sum; // This will only keep the lower 32 bits
carry = sum >> 32; // This will keep the upper 32 bits (carry)
}
return result;
}

arith_uint256* sub_arith_uint256(arith_uint256* a, arith_uint256* b) {
arith_uint256* result = init_arith_uint256();
int64_t carry = 0;
for (int i = WIDTH - 1; i >= 0; i--) {
int64_t diff = (uint64_t)a->pn[i] - b->pn[i] - carry;
result->pn[i] = diff; // This will only keep the lower 32 bits
carry = (diff < 0) ? 1 : 0; // If diff is negative, there's a borrow
}
return result;
}

dogecoin_bool arith_uint256_is_zero(const arith_uint256* a) {
for (int i = 0; i < WIDTH; i++) {
if (a->pn[i] != 0) return false;
}
return true;
}

dogecoin_bool arith_uint256_less_than(const arith_uint256* a, const arith_uint256* b) {
for (int i = WIDTH - 1; i >= 0; i--) {
if (a->pn[i] < b->pn[i]) return true;
else if (a->pn[i] > b->pn[i]) return false;
}
return false;
}

dogecoin_bool arith_uint256_equal(const arith_uint256* a, const arith_uint256* b) {
for (int i = 0; i < WIDTH; i++) {
if (a->pn[i] != b->pn[i]) return false;
}
return true;
}

dogecoin_bool arith_uint256_less_than_or_equal(const arith_uint256* a, const arith_uint256* b) {
return arith_uint256_less_than(a, b) || arith_uint256_equal(a, b);
}

dogecoin_bool arith_uint256_greater_than(const arith_uint256* a, const arith_uint256* b) {
for (int i = 0; i < WIDTH; i++) {
if (a->pn[i] > b->pn[i]) return true;
else if (a->pn[i] < b->pn[i]) return false;
}
return false;
}

dogecoin_bool arith_uint256_greater_than_or_equal(const arith_uint256* a, const arith_uint256* b) {
return !arith_uint256_less_than(a, b);
}

uint64_t get_low64(arith_uint256 a) {
return a.pn[0] | (uint64_t)a.pn[1] << 32;
uint64_t get_low64(arith_uint256* a) {
return ((uint64_t)a->pn[WIDTH - 2]) | (((uint64_t)a->pn[WIDTH - 1]) << 32);
}
6 changes: 5 additions & 1 deletion src/block.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ dogecoin_block_header* dogecoin_block_header_new() {
header->nonce = 0;
header->auxpow->check = check;
header->auxpow->ctx = header;
header->auxpow->is = false;
dogecoin_mem_zero(&header->chainwork, DOGECOIN_HASH_LENGTH);
return header;
}

Expand All @@ -187,7 +189,6 @@ dogecoin_auxpow_block* dogecoin_auxpow_block_new() {
block->aux_merkle_branch = NULL;
block->aux_merkle_index = 0;
block->parent_header = dogecoin_block_header_new();
block->header->auxpow->check = check;
block->header->auxpow->ctx = block;
return block;
}
Expand Down Expand Up @@ -332,6 +333,7 @@ int dogecoin_block_header_deserialize(dogecoin_block_header* header, struct cons
printf("%s:%d:%s:%s\n", __FILE__, __LINE__, __func__, strerror(errno));
return false;
}
dogecoin_block_header_copy(header, block->header);
}
dogecoin_auxpow_block_free(block);
return true;
Expand Down Expand Up @@ -470,6 +472,8 @@ void dogecoin_block_header_copy(dogecoin_block_header* dest, const dogecoin_bloc
dest->nonce = src->nonce;
dest->auxpow->check = src->auxpow->check;
dest->auxpow->ctx = src->auxpow->ctx;
dest->auxpow->is = src->auxpow->is;
memcpy_safe(&dest->chainwork, &src->chainwork, sizeof(src->chainwork));
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/cli/spvnode.c
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,7 @@ int main(int argc, char* argv[]) {
printf("Connecting to the p2p network...\n");
dogecoin_spv_client_runloop(client);
dogecoin_spv_client_free(client);
printf("done\n");
ret = EXIT_SUCCESS;
#if WITH_WALLET
dogecoin_wallet_free(wallet);
Expand Down
109 changes: 93 additions & 16 deletions src/headersdb_file.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,18 +124,15 @@ void dogecoin_headers_db_free(dogecoin_headers_db* db) {
// Free all blockindex structures starting from chaintip to chainbottom
if (db->chaintip) {
dogecoin_blockindex *scan_tip = db->chaintip;
while (scan_tip && scan_tip != db->chainbottom) {
while (scan_tip->prev) {
dogecoin_blockindex *prev = scan_tip->prev;
dogecoin_free(scan_tip);
scan_tip = prev;
}
#ifndef __APPLE__
// If scan_tip is chainbottom, free it
if (scan_tip == db->chainbottom) {
if (scan_tip == db->chainbottom && scan_tip != &db->genesis) {
dogecoin_free(scan_tip);
db->chainbottom = NULL;
}
#endif
}

db->chaintip = NULL;
Expand Down Expand Up @@ -310,45 +307,125 @@ dogecoin_blockindex * dogecoin_headers_db_connect_hdr(dogecoin_headers_db* db, s
// check if we know the prevblock
fork_from_block = dogecoin_headersdb_find(db, blockindex->header.prev_block);
if (fork_from_block) {
printf("Block found on a fork...\n");
connect_at = fork_from_block;
}
}

if (connect_at != NULL) {
/* check claimed PoW */
// Check the proof of work
if (!is_auxpow(blockindex->header.version)) {
uint256 hash = {0};
cstring* s = cstr_new_sz(64);
dogecoin_block_header_serialize(s, &blockindex->header);
dogecoin_block_header_serialize(s, (const dogecoin_block_header*) &blockindex->header);
dogecoin_block_header_scrypt_hash(s, &hash);
cstr_free(s, true);
if (!check_pow(&hash, blockindex->header.bits, db->params)) {
if (!check_pow(&hash, blockindex->header.bits, db->params, &blockindex->header.chainwork)) {
printf("%s:%d:%s : non-AUX proof of work failed : %s\n", __FILE__, __LINE__, __func__, strerror(errno));
dogecoin_free(blockindex);
return false;
}
}

blockindex->prev = connect_at;
blockindex->height = connect_at->height+1;

/* TODO: check if we should switch to the fork with most work (instead of height) */
if (blockindex->height > db->chaintip->height) {
if (fork_from_block) {
/* TODO: walk back to the fork point and call reorg callback */
printf("Switch to the fork!\n");
arith_uint256* connect_at_chainwork = uint_to_arith((const uint256*) &connect_at->header.chainwork);
arith_uint256* blockindex_chainwork = uint_to_arith((const uint256*) &blockindex->header.chainwork);
arith_uint256* chaintip_chainwork = uint_to_arith((const uint256*) &db->chaintip->header.chainwork);

arith_uint256* added_chainwork = add_arith_uint256(connect_at_chainwork, blockindex_chainwork);
uint8_t* final_chainwork = arith_to_uint256(added_chainwork);

memcpy(blockindex->header.chainwork, final_chainwork, sizeof(blockindex->header.chainwork));

// Free the dynamically allocated memory
dogecoin_free(connect_at_chainwork);
dogecoin_free(blockindex_chainwork);
dogecoin_free(final_chainwork);

// Chain reorganization if necessary
if (fork_from_block && arith_uint256_greater_than(added_chainwork, chaintip_chainwork)) {

// Identify the common ancestor
dogecoin_blockindex* common_ancestor = connect_at;
dogecoin_blockindex* chain_tip = db->chaintip;

// Find the common ancestor
while (common_ancestor && chain_tip && common_ancestor->height != chain_tip->height) {
if (common_ancestor->height > chain_tip->height) {
common_ancestor = common_ancestor->prev;
} else {
chain_tip = chain_tip->prev;
}

// Break the loop if either reaches the start of the chain
if (!common_ancestor || !chain_tip) {
fprintf(stderr, "Unable to find common ancestor.\n");
dogecoin_free(blockindex);
dogecoin_free(chaintip_chainwork);
dogecoin_free(added_chainwork);
return NULL;
}
}

// Disconnect blocks from the current chain
while (memcmp(db->chaintip->hash, common_ancestor->hash, DOGECOIN_HASH_LENGTH) != 0) {
dogecoin_headersdb_disconnect_tip(db);
}

// Connect blocks from the new chain
dogecoin_blockindex* current_block = blockindex;
while (current_block && memcmp(current_block->hash, common_ancestor->hash, DOGECOIN_HASH_LENGTH) != 0) {
// current_block->prev points to the previous block in the chain
dogecoin_blockindex* prev_block = current_block->prev;

if (!prev_block) {
fprintf(stderr, "Previous block in the chain not found.\n");

// Add the current_block to the tree if it's not already part of the main chain
if (db->use_binary_tree && current_block != blockindex) {
fprintf(stderr, "Adding block to tree.\n");
dogecoin_btree_tsearch(current_block, &db->tree_root, dogecoin_header_compare);
}

// Free the dynamically allocated memory
dogecoin_free(chaintip_chainwork);
dogecoin_free(added_chainwork);
return NULL;
}

// Update the chain tip to the previous block
db->chaintip = prev_block;
current_block = prev_block;
}

printf("Chain reorganization: %d blocks disconnected, %d blocks connected\n", blockindex->height - common_ancestor->height, blockindex->height - db->chaintip->height);

// Set the new block as the new chain tip
db->chaintip = blockindex;

}
else if (blockindex->height > db->chaintip->height) {
db->chaintip = blockindex;
}
else {
dogecoin_free(chaintip_chainwork);
dogecoin_free(added_chainwork);
dogecoin_free(blockindex);
return NULL;
}

// Free the dynamically allocated memory
dogecoin_free(chaintip_chainwork);
dogecoin_free(added_chainwork);
if (!load_process && db->read_write_file)
{
if (!dogecoin_headers_db_write(db, blockindex)) {
fprintf(stderr, "Error writing blockheader to database\n");
}
}
if (db->use_binary_tree) {
/* TODO: update when fork handling is implemented */
dogecoin_btree_tfind(blockindex, &db->tree_root, dogecoin_header_compare);
dogecoin_btree_tsearch(blockindex, &db->tree_root, dogecoin_header_compare);
}

if (db->max_hdr_in_mem > 0) {
Expand Down
Loading

0 comments on commit 5059d54

Please sign in to comment.