Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions include/dogecoin/block.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,30 @@ typedef struct _auxpow {
void *ctx;
} auxpow;

/* The AuxPoW proof carried by a merge-mined header, owned by the header it
belongs to.
*
* This deliberately has no back-pointer to its owning header, unlike
* dogecoin_auxpow_block. That struct owns both its header and its parent_header
* and frees them, so a header could not hold one without the two owning each
* other. The payload owns only parent_header, which is a plain 80-byte header
* with no payload of its own, so ownership terminates.
*
* The auxpow.check / auxpow.ctx hook on dogecoin_block_header is unaffected:
* it is a validation hook whose context the caller supplies at call time, not a
* reference to this data. */
typedef struct dogecoin_auxpow_payload_ {
dogecoin_tx* parent_coinbase;
uint256_t parent_hash;
uint8_t parent_merkle_count;
uint256_t* parent_coinbase_merkle;
uint32_t parent_merkle_index;
uint8_t aux_merkle_count;
uint256_t* aux_merkle_branch;
uint32_t aux_merkle_index;
struct dogecoin_block_header_* parent_header;
} dogecoin_auxpow_payload;

typedef struct dogecoin_block_header_ {
int32_t version;
uint256_t prev_block;
Expand All @@ -56,8 +80,20 @@ typedef struct dogecoin_block_header_ {
uint32_t bits;
uint32_t nonce;
auxpow auxpow[1];
/** AuxPoW proof for a merge-mined header, NULL otherwise. Retained so the
header can reproduce the bytes it was parsed from: the deserializer used
to parse this into a local dogecoin_auxpow_block and free it, and
dogecoin_block_header_copy carried only the auxpow hook fields, so the
proof was discarded and anything needing it had to re-parse. */
dogecoin_auxpow_payload* auxpow_payload;
} dogecoin_block_header;

/** Free an AuxPoW payload and everything it owns. */
LIBDOGECOIN_API void dogecoin_auxpow_payload_free(dogecoin_auxpow_payload* payload);

/** Deep-copy an AuxPoW payload. Returns NULL if src is NULL. */
LIBDOGECOIN_API dogecoin_auxpow_payload* dogecoin_auxpow_payload_copy(const dogecoin_auxpow_payload* src);

typedef struct dogecoin_auxpow_block_ {
dogecoin_block_header* header;
dogecoin_tx* parent_coinbase;
Expand All @@ -75,9 +111,53 @@ LIBDOGECOIN_API dogecoin_block_header* dogecoin_block_header_new();
LIBDOGECOIN_API void dogecoin_block_header_free(dogecoin_block_header* header);
LIBDOGECOIN_API dogecoin_auxpow_block* dogecoin_auxpow_block_new();
LIBDOGECOIN_API void dogecoin_auxpow_block_free(dogecoin_auxpow_block* block);
/** Parse a block header off the wire without validating it.
*
* Reads the 80 base fields and, when version bit 0x100 is set, the AuxPoW
* proof, which is retained on the header. Runs no proof-of-work check.
*
* check_auxpow is scrypt work over the parent chain. Doing it during parsing
* means every caller pays for it whether or not it wants the answer yet, and
* before any peer-level gating has happened. Callers that want the fields and
* will decide about validation later use this; callers that want the existing
* parse-and-validate behaviour keep using dogecoin_block_header_deserialize.
*/
LIBDOGECOIN_API int dogecoin_block_header_parse(dogecoin_block_header* header, struct const_buffer* buf, const dogecoin_chainparams *params);

/** Validate a parsed header's AuxPoW and fill @p chainwork.
*
* Returns true immediately for a header with no AuxPoW: its proof of work is
* over the 80 base bytes and is the caller's to verify. Requires the header to
* still own its proof, so it must have come from dogecoin_block_header_parse
* or dogecoin_block_header_deserialize.
*/
LIBDOGECOIN_API int dogecoin_block_header_validate(dogecoin_block_header* header, const dogecoin_chainparams *params, arith_uint256* chainwork);

/** Parse and validate. Unchanged in behaviour and signature: this is
* dogecoin_block_header_parse followed by dogecoin_block_header_validate.
*
* Deliberately kept as the name that validates. Making this the pure parse and
* adding a checked variant would silently stop verifying proof of work for any
* caller of the existing name, with no compile error to catch it.
*/
LIBDOGECOIN_API int dogecoin_block_header_deserialize(dogecoin_block_header* header, struct const_buffer* buf, const dogecoin_chainparams *params, arith_uint256* chainwork);
LIBDOGECOIN_API int deserialize_dogecoin_auxpow_block(dogecoin_auxpow_block* block, struct const_buffer* buffer, const dogecoin_chainparams *params, arith_uint256* chainwork);
/** Serialize the 80 base header fields. This is the pure header: it never
* emits AuxPoW, because its output is what the block hash, the scrypt proof of
* work and the fixed-width headers.db record are computed over. */
LIBDOGECOIN_API void dogecoin_block_header_serialize(cstring* s, const dogecoin_block_header* header);

/** Serialize an AuxPoW proof in wire order. */
LIBDOGECOIN_API void dogecoin_auxpow_payload_serialize(cstring* s, const dogecoin_auxpow_payload* payload);

/** Serialize a header as it appears on the wire: the 80 base bytes, followed by
* the AuxPoW proof when the header carries one.
*
* This is Core's CBlockHeader to dogecoin_block_header_serialize's
* CPureBlockHeader. Messages that carry a whole header -- headers, block,
* cmpctblock -- want this one; anything hashing the header wants the pure form.
*/
LIBDOGECOIN_API void dogecoin_block_header_serialize_full(cstring* s, const dogecoin_block_header* header);
LIBDOGECOIN_API void dogecoin_block_header_copy(dogecoin_block_header* dest, const dogecoin_block_header* src);
LIBDOGECOIN_API dogecoin_bool dogecoin_block_header_hash(dogecoin_block_header* header, uint256_t hash);

Expand Down
177 changes: 173 additions & 4 deletions src/block.c
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ dogecoin_block_header* dogecoin_block_header_new() {
header->auxpow->check = check;
header->auxpow->ctx = header;
header->auxpow->is = false;
header->auxpow_payload = NULL;
return header;
}

Expand Down Expand Up @@ -207,8 +208,61 @@ dogecoin_auxpow_block* dogecoin_auxpow_block_new() {
*
* @return Nothing.
*/
void dogecoin_auxpow_payload_free(dogecoin_auxpow_payload* payload) {
if (!payload) return;
dogecoin_tx_free(payload->parent_coinbase);
dogecoin_free(payload->parent_coinbase_merkle);
dogecoin_free(payload->aux_merkle_branch);
/* parent_header is a plain 80-byte header: its own auxpow_payload is NULL,
so this does not recurse. */
dogecoin_block_header_free(payload->parent_header);
dogecoin_free(payload);
}

dogecoin_auxpow_payload* dogecoin_auxpow_payload_copy(const dogecoin_auxpow_payload* src) {
if (!src) return NULL;
dogecoin_auxpow_payload* dst = dogecoin_calloc(1, sizeof(*dst));
if (!dst) return NULL;

memcpy_safe(dst->parent_hash, src->parent_hash, sizeof(uint256_t));
dst->parent_merkle_count = src->parent_merkle_count;
dst->parent_merkle_index = src->parent_merkle_index;
dst->aux_merkle_count = src->aux_merkle_count;
dst->aux_merkle_index = src->aux_merkle_index;

if (src->parent_coinbase) {
dst->parent_coinbase = dogecoin_tx_new();
if (!dst->parent_coinbase) goto fail;
dogecoin_tx_copy(dst->parent_coinbase, src->parent_coinbase);
}
if (src->parent_merkle_count && src->parent_coinbase_merkle) {
size_t n = (size_t)src->parent_merkle_count * sizeof(uint256_t);
dst->parent_coinbase_merkle = dogecoin_malloc(n);
if (!dst->parent_coinbase_merkle) goto fail;
memcpy_safe(dst->parent_coinbase_merkle, src->parent_coinbase_merkle, n);
}
if (src->aux_merkle_count && src->aux_merkle_branch) {
size_t n = (size_t)src->aux_merkle_count * sizeof(uint256_t);
dst->aux_merkle_branch = dogecoin_malloc(n);
if (!dst->aux_merkle_branch) goto fail;
memcpy_safe(dst->aux_merkle_branch, src->aux_merkle_branch, n);
}
if (src->parent_header) {
dst->parent_header = dogecoin_block_header_new();
if (!dst->parent_header) goto fail;
dogecoin_block_header_copy(dst->parent_header, src->parent_header);
}
return dst;

fail:
dogecoin_auxpow_payload_free(dst);
return NULL;
}

void dogecoin_block_header_free(dogecoin_block_header* header) {
if (!header) return;
dogecoin_auxpow_payload_free(header->auxpow_payload);
header->auxpow_payload = NULL;
header->version = 0;
dogecoin_mem_zero(&header->prev_block, DOGECOIN_HASH_LENGTH);
dogecoin_mem_zero(&header->merkle_root, DOGECOIN_HASH_LENGTH);
Expand Down Expand Up @@ -320,7 +374,9 @@ void print_block(dogecoin_auxpow_block* block) {
*
* @return 1 if deserialization was successful, 0 otherwise.
*/
int dogecoin_block_header_deserialize(dogecoin_block_header* header, struct const_buffer* buf, const dogecoin_chainparams *params, arith_uint256* chainwork) {
static int parse_dogecoin_auxpow_fields(dogecoin_auxpow_block* block, struct const_buffer* buffer, const dogecoin_chainparams *params);

int dogecoin_block_header_parse(dogecoin_block_header* header, struct const_buffer* buf, const dogecoin_chainparams *params) {
dogecoin_auxpow_block* block = dogecoin_auxpow_block_new();
int ret = false;
if (!deser_s32(&block->header->version, buf))
Expand All @@ -337,19 +393,84 @@ int dogecoin_block_header_deserialize(dogecoin_block_header* header, struct cons
goto cleanup;
dogecoin_block_header_copy(header, block->header);
if ((block->header->version & 0x100) != 0 && buf->len) {
if (!deserialize_dogecoin_auxpow_block(block, buf, params, chainwork)) {
if (!parse_dogecoin_auxpow_fields(block, buf, params)) {
printf("%s:%d:%s:%s\n", __FILE__, __LINE__, __func__, strerror(errno));
goto cleanup;
}
dogecoin_block_header_copy(header, block->header);
/* Move the proof onto the header instead of letting cleanup free it.
Ownership transfers: the fields are nulled on the block so
dogecoin_auxpow_block_free does not release what the header now owns. */
dogecoin_auxpow_payload* payload = dogecoin_calloc(1, sizeof(*payload));
if (!payload) goto cleanup;
payload->parent_coinbase = block->parent_coinbase;
memcpy_safe(payload->parent_hash, block->parent_hash, sizeof(uint256_t));
payload->parent_merkle_count = block->parent_merkle_count;
payload->parent_coinbase_merkle = block->parent_coinbase_merkle;
payload->parent_merkle_index = block->parent_merkle_index;
payload->aux_merkle_count = block->aux_merkle_count;
payload->aux_merkle_branch = block->aux_merkle_branch;
payload->aux_merkle_index = block->aux_merkle_index;
payload->parent_header = block->parent_header;
block->parent_coinbase = NULL;
block->parent_coinbase_merkle = NULL;
block->aux_merkle_branch = NULL;
block->parent_header = NULL;
/* No free of a prior payload here: dogecoin_block_header_copy above has
already overwritten the pointer, and header may have arrived as an
uninitialised stack struct. Callers own dest's prior contents, the
same contract every other field in this function follows. */
header->auxpow_payload = payload;
}
ret = true;
cleanup:
dogecoin_auxpow_block_free(block);
return ret;
}

int deserialize_dogecoin_auxpow_block(dogecoin_auxpow_block* block, struct const_buffer* buffer, const dogecoin_chainparams *params, arith_uint256* chainwork) {
int dogecoin_block_header_validate(dogecoin_block_header* header, const dogecoin_chainparams *params, arith_uint256* chainwork) {
if (!header) return false;
/* Nothing to check for a header with no AuxPoW: its proof of work is over
the 80 base bytes, which is the caller's to verify -- headersdb_file.c
does exactly that for the non-AuxPoW case, and fills chainwork itself. */
if (!header->auxpow_payload) return true;

/* check_auxpow wants a dogecoin_auxpow_block. Build one that borrows from
the header and its payload rather than copying: it is never freed, so the
borrowed pointers are not released twice. dogecoin_auxpow_block_free
would take the header and parent_header with it, which is exactly the
ownership tangle the payload type exists to avoid. */
dogecoin_auxpow_payload* p = header->auxpow_payload;
dogecoin_auxpow_block view;
dogecoin_mem_zero(&view, sizeof(view));
view.header = header;
view.parent_coinbase = p->parent_coinbase;
memcpy_safe(view.parent_hash, p->parent_hash, sizeof(uint256_t));
view.parent_merkle_count = p->parent_merkle_count;
view.parent_coinbase_merkle = p->parent_coinbase_merkle;
view.parent_merkle_index = p->parent_merkle_index;
view.aux_merkle_count = p->aux_merkle_count;
view.aux_merkle_branch = p->aux_merkle_branch;
view.aux_merkle_index = p->aux_merkle_index;
view.parent_header = p->parent_header;

if (!check_auxpow(&view, (dogecoin_chainparams*)params, chainwork)) {
printf("check_auxpow failed!\n");
return false;
}
return true;
}

int dogecoin_block_header_deserialize(dogecoin_block_header* header, struct const_buffer* buf, const dogecoin_chainparams *params, arith_uint256* chainwork) {
if (!dogecoin_block_header_parse(header, buf, params)) return false;
return dogecoin_block_header_validate(header, params, chainwork);
}

/* Parse the AuxPoW fields off the wire. No validation: check_auxpow is scrypt
work on the parent chain, and doing it here means every caller pays for it
during parsing whether or not it wants the answer yet. */
static int parse_dogecoin_auxpow_fields(dogecoin_auxpow_block* block, struct const_buffer* buffer, const dogecoin_chainparams *params) {
(void)params;
if (buffer->len > DOGECOIN_MAX_P2P_MSG_SIZE) {
return printf("\ntransaction is invalid or to large.\n\n");
}
Expand Down Expand Up @@ -465,11 +586,18 @@ int deserialize_dogecoin_auxpow_block(dogecoin_auxpow_block* block, struct const
return false;
}

return true;
}

/* Unchanged behaviour and signature: parse, then validate. Callers that want
only the fields use parse_dogecoin_auxpow_fields via
dogecoin_block_header_parse. */
int deserialize_dogecoin_auxpow_block(dogecoin_auxpow_block* block, struct const_buffer* buffer, const dogecoin_chainparams *params, arith_uint256* chainwork) {
if (!parse_dogecoin_auxpow_fields(block, buffer, params)) return false;
if (!check_auxpow(block, (dogecoin_chainparams*)params, chainwork)) {
printf("check_auxpow failed!\n");
return false;
}

return true;
}

Expand All @@ -482,6 +610,36 @@ int deserialize_dogecoin_auxpow_block(dogecoin_auxpow_block* block, struct const
*
* @return Nothing.
*/
void dogecoin_auxpow_payload_serialize(cstring* s, const dogecoin_auxpow_payload* payload) {
if (!s || !payload) return;
/* Wire order mirrors parse_dogecoin_auxpow_fields exactly. */
dogecoin_tx_serialize(s, payload->parent_coinbase);
ser_u256(s, payload->parent_hash);
ser_varlen(s, payload->parent_merkle_count);
uint8_t i;
for (i = 0; i < payload->parent_merkle_count; i++)
ser_u256(s, payload->parent_coinbase_merkle[i]);
ser_u32(s, payload->parent_merkle_index);
ser_varlen(s, payload->aux_merkle_count);
for (i = 0; i < payload->aux_merkle_count; i++)
ser_u256(s, payload->aux_merkle_branch[i]);
ser_u32(s, payload->aux_merkle_index);
/* Parent header is a pure 80-byte header: it carries no AuxPoW of its own. */
dogecoin_block_header_serialize(s, payload->parent_header);
}

void dogecoin_block_header_serialize_full(cstring* s, const dogecoin_block_header* header) {
if (!s || !header) return;
dogecoin_block_header_serialize(s, header);
/* Core's CBlockHeader::SerializationOp appends the AuxPoW whenever
nVersion & VERSION_AUXPOW is set. Mirror that, driven by whether the
proof is actually present rather than by the bit alone, so a header
carrying the bit but no proof serializes as the 80 bytes it really has
instead of emitting a truncated blob. */
if (header->auxpow_payload)
dogecoin_auxpow_payload_serialize(s, header->auxpow_payload);
}

void dogecoin_block_header_serialize(cstring* s, const dogecoin_block_header* header) {
ser_s32(s, header->version);
ser_u256(s, header->prev_block);
Expand Down Expand Up @@ -510,6 +668,17 @@ void dogecoin_block_header_copy(dogecoin_block_header* dest, const dogecoin_bloc
dest->auxpow->check = src->auxpow->check;
dest->auxpow->ctx = src->auxpow->ctx;
dest->auxpow->is = src->auxpow->is;
/* Deep-copy the proof. Carrying only the hook fields is what discarded it
before, so a copied merge-mined header could not be re-serialized or
re-validated without going back to the wire bytes.

Assign, do not free what dest held. Every other field here is a plain
overwrite: this function treats dest as raw memory, and callers pass
uninitialised stack headers to it -- net_tests.c does, via
dogecoin_block_header_deserialize. Freeing dest->auxpow_payload would
dereference whatever the stack happened to contain. A dest that already
owns a payload is the caller's to release, as with every other member. */
dest->auxpow_payload = dogecoin_auxpow_payload_copy(src->auxpow_payload);
}

/**
Expand Down
Loading
Loading