From 277168da7773895a3235ecd2a3d994aea3ada1d6 Mon Sep 17 00:00:00 2001 From: Dakoda Greaves Date: Sun, 21 May 2023 17:02:35 -0700 Subject: [PATCH] consensus: add arith_uint256, auxpow, pow, uint256 and validation files --- Makefile.am | 10 ++ include/dogecoin/arith_uint256.h | 60 +++++++++ include/dogecoin/auxpow.h | 49 +++++++ include/dogecoin/block.h | 15 ++- include/dogecoin/chainparams.h | 3 + include/dogecoin/consensus.h | 171 ++++++++++++++++++++++++ include/dogecoin/hash.h | 39 ++++++ include/dogecoin/pow.h | 42 ++++++ include/dogecoin/sha2.h | 1 + include/dogecoin/uint256.h | 198 ++++++++++++++++++++++++++++ include/dogecoin/utils.h | 5 + include/dogecoin/validation.h | 46 +++++++ src/arith_uint256.c | 67 ++++++++++ src/auxpow.c | 215 +++++++++++++++++++++++++++++++ src/block.c | 207 +++++++++++++++++++++++++++-- src/chainparams.c | 10 ++ src/consensus.c | 193 +++++++++++++++++++++++++++ src/headersdb_file.c | 4 + src/pow.c | 48 +++++++ src/serialize.c | 2 +- src/sha2.c | 8 ++ src/utils.c | 57 ++++++++ src/validation.c | 124 ++++++++++++++++++ 23 files changed, 1563 insertions(+), 11 deletions(-) create mode 100644 include/dogecoin/arith_uint256.h create mode 100644 include/dogecoin/auxpow.h create mode 100644 include/dogecoin/consensus.h create mode 100644 include/dogecoin/pow.h create mode 100644 include/dogecoin/uint256.h create mode 100644 include/dogecoin/validation.h create mode 100644 src/arith_uint256.c create mode 100644 src/auxpow.c create mode 100644 src/consensus.c create mode 100644 src/pow.c create mode 100644 src/validation.c diff --git a/Makefile.am b/Makefile.am index ece164107..e34291ee5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -23,6 +23,8 @@ noinst_HEADERS = \ include/bip39/index.h \ include/dogecoin/address.h \ include/dogecoin/aes.h \ + include/dogecoin/arith_uint256.h \ + include/dogecoin/auxpow.h \ include/dogecoin/base58.h \ include/dogecoin/bip32.h \ include/dogecoin/bip39.h \ @@ -33,6 +35,7 @@ noinst_HEADERS = \ include/dogecoin/byteswap.h \ include/dogecoin/chainparams.h \ include/dogecoin/common.h \ + include/dogecoin/consensus.h \ include/dogecoin/cstr.h \ include/dogecoin/ctaes.h \ include/dogecoin/ecc.h \ @@ -44,6 +47,7 @@ noinst_HEADERS = \ include/dogecoin/mem.h \ include/dogecoin/moon.h \ include/dogecoin/portable_endian.h \ + include/dogecoin/pow.h \ include/qr/png.h \ include/qr/qr.h \ include/qr/jpeg.h \ @@ -58,6 +62,7 @@ noinst_HEADERS = \ include/dogecoin/transaction.h \ include/dogecoin/tx.h \ include/dogecoin/utils.h \ + include/dogecoin/validation.h \ include/dogecoin/vector.h \ include/dogecoin/wow.h @@ -67,6 +72,8 @@ pkgconfig_DATA = libdogecoin.pc libdogecoin_la_SOURCES = \ src/address.c \ src/aes.c \ + src/arith_uint256.c \ + src/auxpow.c \ src/base58.c \ src/bip32.c \ src/bip39.c \ @@ -74,6 +81,7 @@ libdogecoin_la_SOURCES = \ src/block.c \ src/buffer.c \ src/chainparams.c \ + src/consensus.c \ src/cstr.c \ src/ctaes.c \ src/ecc.c \ @@ -83,6 +91,7 @@ libdogecoin_la_SOURCES = \ src/map.c \ src/mem.c \ src/moon.c \ + src/pow.c \ src/png.c \ src/jpeg.c \ src/qrengine.c \ @@ -98,6 +107,7 @@ libdogecoin_la_SOURCES = \ src/transaction.c \ src/tx.c \ src/utils.c \ + src/validation.c \ src/vector.c if USE_SSE2 diff --git a/include/dogecoin/arith_uint256.h b/include/dogecoin/arith_uint256.h new file mode 100644 index 000000000..4b9ee7244 --- /dev/null +++ b/include/dogecoin/arith_uint256.h @@ -0,0 +1,60 @@ +/* + + The MIT License (MIT) + + Copyright (c) 2023 bluezr + Copyright (c) 2023 The Dogecoin Foundation + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#ifndef __LIBDOGECOIN_ARITH_UINT256_H__ +#define __LIBDOGECOIN_ARITH_UINT256_H__ + +#include +#include +#include +#include + +#include + +LIBDOGECOIN_BEGIN_DECL + +struct uint_err { + const char* str; +}; + +static unsigned int BITS; + +typedef struct base_uint_ { + int WIDTH; // BITS / 32 + uint32_t pn[]; // pn[WIDTH] +} base_uint_; + +typedef base_uint_ arith_uint256; + +arith_uint256 set_compact(arith_uint256 hash, uint32_t compact, dogecoin_bool *pf_negative, dogecoin_bool *pf_overflow); +uint256* arith_to_uint256(const arith_uint256 a); +arith_uint256 uint_to_arith(const uint256* a); +uint64_t get_low64(arith_uint256 pn); + +LIBDOGECOIN_END_DECL + +#endif // __LIBDOGECOIN_ARITH_UINT256_H__ diff --git a/include/dogecoin/auxpow.h b/include/dogecoin/auxpow.h new file mode 100644 index 000000000..35b908e03 --- /dev/null +++ b/include/dogecoin/auxpow.h @@ -0,0 +1,49 @@ +/* + + The MIT License (MIT) + + Copyright (c) 2023 bluezr + Copyright (c) 2023 The Dogecoin Foundation + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#ifndef __LIBDOGECOIN_AUXPOW__ +#define __LIBDOGECOIN_AUXPOW__ + +#include +#include +#include +#include + +LIBDOGECOIN_BEGIN_DECL + +#define BLOCK_VERSION_AUXPOW_BIT 0x100 + +/** Header for merge-mining data in the coinbase. */ +static const unsigned char pchMergedMiningHeader[] = { 0xfa, 0xbe, 'm', 'm' }; + +int get_expected_index(uint32_t nNonce, int nChainId, unsigned h); +uint256* check_merkle_branch(uint256 hash, const vector* parent_coinbase_merkle, int n_index); +void init_aux_pow(dogecoin_block_header* block); + +LIBDOGECOIN_END_DECL + +#endif // __LIBDOGECOIN_AUXPOW__ diff --git a/include/dogecoin/block.h b/include/dogecoin/block.h index 6ed618c33..a4d459d50 100644 --- a/include/dogecoin/block.h +++ b/include/dogecoin/block.h @@ -41,13 +41,20 @@ LIBDOGECOIN_BEGIN_DECL #include #include +typedef struct _auxpow { + dogecoin_bool is; + dogecoin_bool (*check)(void* ctx, uint256* hash, uint32_t chainid, dogecoin_chainparams* params); + void *ctx; +} auxpow; + typedef struct dogecoin_block_header_ { - int32_t version; + uint32_t version; uint256 prev_block; uint256 merkle_root; uint32_t timestamp; uint32_t bits; uint32_t nonce; + auxpow auxpow[1]; } dogecoin_block_header; typedef struct dogecoin_auxpow_block_ { @@ -55,12 +62,18 @@ typedef struct dogecoin_auxpow_block_ { dogecoin_tx* parent_coinbase; uint256 parent_hash; uint8_t parent_merkle_count; + uint256* parent_coinbase_merkle; uint32_t parent_merkle_index; uint8_t aux_merkle_count; uint32_t aux_merkle_index; dogecoin_block_header* parent_header; } dogecoin_auxpow_block; +typedef struct dogecoin_block_ { + dogecoin_auxpow_block* block; + vector* vtx; // vector of transactions +} dogecoin_block; + 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(); diff --git a/include/dogecoin/chainparams.h b/include/dogecoin/chainparams.h index 4690bacf2..846b9354a 100644 --- a/include/dogecoin/chainparams.h +++ b/include/dogecoin/chainparams.h @@ -49,6 +49,9 @@ typedef struct dogecoin_chainparams_ { uint256 genesisblockhash; int default_port; dogecoin_dns_seed dnsseeds[8]; + dogecoin_bool strict_id; + dogecoin_bool auxpow_id; + uint256 pow_limit; } dogecoin_chainparams; typedef struct dogecoin_checkpoint_ { diff --git a/include/dogecoin/consensus.h b/include/dogecoin/consensus.h new file mode 100644 index 000000000..c4ef429f4 --- /dev/null +++ b/include/dogecoin/consensus.h @@ -0,0 +1,171 @@ +/* + + The MIT License (MIT) + + Copyright (c) 2023 bluezr + Copyright (c) 2023 The Dogecoin Foundation + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#ifndef __LIBDOGECOIN_CONSENSUS_H__ +#define __LIBDOGECOIN_CONSENSUS_H__ + +#include + +#include +#include +#include +#include +#include + +LIBDOGECOIN_BEGIN_DECL + +/** The maximum allowed size for a serialized block, in bytes (only for buffer size limits) */ +static const unsigned int MAX_BLOCK_SERIALIZED_SIZE = 4000000; +/** The maximum allowed weight for a block, see BIP 141 (network rule) */ +static const unsigned int MAX_BLOCK_WEIGHT = 4000000; +/** The maximum allowed size for a block excluding witness data, in bytes (network rule) */ +static const unsigned int MAX_BLOCK_BASE_SIZE = 1000000; +/** The maximum allowed number of signature check operations in a block (network rule) */ +static const int64_t MAX_BLOCK_SIGOPS_COST = 80000; + +/** Flags for nSequence and nLockTime locks */ +enum { + /* Interpret sequence numbers as relative lock-time constraints. */ + LOCKTIME_VERIFY_SEQUENCE = (1 << 0), + + /* Use GetMedianTimePast() instead of nTime for end point timestamp. */ + LOCKTIME_MEDIAN_TIME_PAST = (1 << 1), +}; + +/** Merkle */ +uint256* compute_merkle_root(const vector* leaves, dogecoin_bool* mutated); +vector* compute_merkle_branch(const vector* leaves, uint32_t position); +uint256* compute_merkle_root_from_branch(const uint256* leaf, const vector* branch, uint32_t position); + +/* + * Compute the Merkle root of the transactions in a block. + * *mutated is set to true if a duplicated subtree was found. + */ +uint256* block_merkle_root(const dogecoin_block* block, dogecoin_bool* mutated); + +/* + * Compute the Merkle branch for the tree of transactions in a block, for a + * given position. + * This can be verified using compute_merkle_root_from_branch. + */ +vector* block_merkle_branch(const dogecoin_block* block, uint32_t position); + +/** Validation */ +/** "reject" message codes */ +static const unsigned char REJECT_MALFORMED = 0x01; +static const unsigned char REJECT_INVALID = 0x10; +static const unsigned char REJECT_OBSOLETE = 0x11; +static const unsigned char REJECT_DUPLICATE = 0x12; +static const unsigned char REJECT_NONSTANDARD = 0x40; +static const unsigned char REJECT_DUST = 0x41; +static const unsigned char REJECT_INSUFFICIENTFEE = 0x42; +static const unsigned char REJECT_CHECKPOINT = 0x43; + +static enum { + MODE_VALID, //!< everything ok + MODE_INVALID, //!< network rule violation (DoS value may be set) + MODE_ERROR, //!< run-time error +} mode; + +typedef struct validation_state { + int mode; + int n_dos; + char* str_reject_reason; + unsigned int ch_reject_code; + dogecoin_bool corruption_possible; + char* str_debug_message; + void (*DoS)(struct validation_state* state, int level, bool ret, + unsigned int ch_reject_code_in, const char* str_reject_reason_in, + bool corruption_in, const char* str_debug_message_in); + dogecoin_bool (*invalid)(struct validation_state* state, bool ret, + unsigned int _ch_reject_code, const char* _str_reject_reason, + const char* _str_debug_message); + dogecoin_bool (*err)(struct validation_state* state, const char* str_reject_reason_in); +} validation_state; + +dogecoin_bool DoS(struct validation_state* state, int level, bool ret, + unsigned int ch_reject_code_in, const char* str_reject_reason_in, + bool corruption_in, const char* str_debug_message_in) { + state->ch_reject_code = ch_reject_code_in; + state->str_reject_reason = (char*)str_reject_reason_in; + state->corruption_possible = corruption_in; + state->str_debug_message = (char*)str_debug_message_in; + if (state->mode == MODE_ERROR) + return ret; + state->n_dos += level; + state->mode = MODE_INVALID; + return ret; +} +dogecoin_bool invalid(struct validation_state* state, bool ret, + unsigned int _ch_reject_code, const char* _str_reject_reason, + const char* _str_debug_message) { + return DoS(state, 0, ret, _ch_reject_code, _str_reject_reason, false, _str_debug_message); +} +dogecoin_bool err(struct validation_state* state, const char* str_reject_reason_in) { + if (state->mode == MODE_VALID) + state->str_reject_reason = (char*)str_reject_reason_in; + state->mode = MODE_ERROR; + return false; +} +const dogecoin_bool is_valid(struct validation_state* state) { + return state->mode == MODE_VALID; +} +const dogecoin_bool return_invalid(struct validation_state* state) { + return state->mode == MODE_INVALID; +} +const dogecoin_bool is_err(struct validation_state* state) { + return state->mode == MODE_ERROR; +} +const dogecoin_bool is_invalid(struct validation_state* state, int *n_dos_out) { + if (return_invalid(state)) { + n_dos_out = state->n_dos; + return true; + } + return false; +} +const dogecoin_bool corruption_possible(struct validation_state* state) { + return state->corruption_possible; +} +void set_corruption_possible(struct validation_state* state) { + state->corruption_possible = true; +} +const unsigned int get_reject_code(struct validation_state* state) { return state->ch_reject_code; } +const char* get_reject_reason(struct validation_state* state) { return state->str_reject_reason; } +const char* get_debug_message(struct validation_state* state) { return state->str_debug_message; } + +validation_state* init_validation_state() { + validation_state* state = dogecoin_calloc(1, sizeof(*state)); + state->mode = MODE_VALID; + state->n_dos = 0; + state->ch_reject_code = 0; + state->corruption_possible = false; + return state; +} + +LIBDOGECOIN_END_DECL + +#endif // __LIBDOGECOIN_CONSENSUS_H__ diff --git a/include/dogecoin/hash.h b/include/dogecoin/hash.h index 4c2d34ae3..ba99775c8 100644 --- a/include/dogecoin/hash.h +++ b/include/dogecoin/hash.h @@ -32,7 +32,9 @@ #include #include #include +#include #include +#include #include LIBDOGECOIN_BEGIN_DECL @@ -75,6 +77,43 @@ LIBDOGECOIN_API static inline void dogecoin_hash_sngl_sha256(const unsigned char sha256_raw(datain, length, hashout); // single sha256 hash } +LIBDOGECOIN_API static inline void dogecoin_get_auxpow_hash(const uint32_t version, uint256 hashout) +{ + scrypt_1024_1_1_256(BEGIN(version), BEGIN(hashout)); +} + +typedef uint256 chain_code; + +typedef struct _chash256 { + sha256_context* sha; + void (*finalize)(sha256_context* ctx, unsigned char hash[SHA256_DIGEST_LENGTH]); + void (*write)(sha256_context* ctx, const uint8_t* data, size_t len); + void (*reset)(); +} chash256; + +static inline chash256* dogecoin_chash256_init() { + chash256* chash = dogecoin_calloc(1, sizeof(*chash)); + sha256_context* ctx = NULL; + sha256_init(ctx); + chash->sha = ctx; + chash->write = sha256_write; + chash->finalize = sha256_finalize; + chash->reset = sha256_reset; + return chash; +} + +static inline uint256* Hash(const uint256 p1begin, const uint256 p1end, + const uint256 p2begin, const uint256 p2end) { + static const unsigned char pblank[1]; + uint256* result = dogecoin_uint256_vla(1); + chash256* chash = dogecoin_chash256_init(); + chash->write(chash->sha, p1begin == p1end ? pblank : (const unsigned char*)&p1begin[0], (p1end - p1begin) * sizeof(p1begin[0])); + chash->write(chash->sha, p2begin == p2end ? pblank : (const unsigned char*)&p2begin[0], (p2end - p2begin) * sizeof(p2begin[0])); + chash->finalize(chash->sha, (unsigned char*)result); + chash->reset(chash->sha); + return result; +} + LIBDOGECOIN_END_DECL #endif // __LIBDOGECOIN_HASH_H__ diff --git a/include/dogecoin/pow.h b/include/dogecoin/pow.h new file mode 100644 index 000000000..4750d59b7 --- /dev/null +++ b/include/dogecoin/pow.h @@ -0,0 +1,42 @@ +/* + + The MIT License (MIT) + + Copyright (c) 2023 bluezr + Copyright (c) 2023 The Dogecoin Foundation + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#ifndef __LIBDOGECOIN_POW_H__ +#define __LIBDOGECOIN_POW_H__ + +#include +#include +#include +#include + +LIBDOGECOIN_BEGIN_DECL + +dogecoin_bool check_pow(uint256* hash, unsigned int nbits, dogecoin_chainparams *params); + +LIBDOGECOIN_END_DECL + +#endif // __LIBDOGECOIN_POW_H__ diff --git a/include/dogecoin/sha2.h b/include/dogecoin/sha2.h index 15469b6ef..b898a2b9b 100644 --- a/include/dogecoin/sha2.h +++ b/include/dogecoin/sha2.h @@ -64,6 +64,7 @@ LIBDOGECOIN_API void sha256_init(sha256_context*); LIBDOGECOIN_API void sha256_write(sha256_context*, const uint8_t*, size_t); LIBDOGECOIN_API void sha256_finalize(sha256_context*, uint8_t[SHA256_DIGEST_LENGTH]); LIBDOGECOIN_API void sha256_raw(const uint8_t*, size_t, uint8_t[SHA256_DIGEST_LENGTH]); +LIBDOGECOIN_API void sha256_reset(sha256_context*); LIBDOGECOIN_API void sha512_init(sha512_context*); LIBDOGECOIN_API void sha512_write(sha512_context*, const uint8_t*, size_t); diff --git a/include/dogecoin/uint256.h b/include/dogecoin/uint256.h new file mode 100644 index 000000000..faf9cc3d9 --- /dev/null +++ b/include/dogecoin/uint256.h @@ -0,0 +1,198 @@ +/* + The MIT License (MIT) + + Copyright (c) 2023 bluezr + Copyright (c) 2023 The Dogecoin Foundation + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef __LIBDOGECOIN_UINT256_H__ +#define __LIBDOGECOIN_UINT256_H__ + +#include +#include +#include + +#include +#include +#include +#include + +LIBDOGECOIN_BEGIN_DECL + +static const unsigned int BITS = {0x0000100}; +static const unsigned int WIDTH = BITS/8; + +typedef struct base_blob { + uint8_t data[32]; + void (*set_data)(struct base_blob* blob, uint8_t data[WIDTH]); + dogecoin_bool (*is_null)(uint8_t data[WIDTH]); + void (*set_null)(uint8_t data[WIDTH]); + unsigned char* (*begin)(struct base_blob blob); + unsigned char* (*end)(struct base_blob blob); + unsigned int (*size)(struct base_blob blob); + uint64_t (*get_uint64)(struct base_blob blob, int pos); + int (*compare)(const struct base_blob* blob, const struct base_blob* other); + dogecoin_bool (*eq)(const struct base_blob* a, const struct base_blob* b); + dogecoin_bool (*neq)(const struct base_blob* a, const struct base_blob* b); + dogecoin_bool (*lt)(const struct base_blob* a, const struct base_blob* b); + char* (*get_hex)(const struct base_blob blob); + void (*set_hex)(struct base_blob blob, const char* psz); + char* (*to_str)(struct base_blob blob); + void (*serialize)(cstring* s, const void* p, size_t len); + int (*unserialize)(void* po, struct const_buffer* buf, size_t len); +} base_blob; + +static void set_data(struct base_blob* blob, uint8_t data[WIDTH]) { + unsigned int i = 0; + for (; i < WIDTH; i++) { + memcpy_safe(blob->data[i], data[i], sizeof(uint8_t)); + } + return true; +} + +static dogecoin_bool is_null(uint8_t data[WIDTH]) { + unsigned int i = 0; + for (; i < WIDTH; i++) if (data[i] != 0) return false; + return true; +} + +static void set_null(uint8_t data[WIDTH]) { + memset_safe(data, 0, sizeof(*data), 0); +} + +static unsigned char* begin(struct base_blob blob) { + return (unsigned char*)blob.data[0]; +} + +static unsigned char* end(struct base_blob blob) { + return (unsigned char*)blob.data[WIDTH]; +} + +static unsigned int size(struct base_blob blob) { + return sizeof(blob.data); +} + +static uint64_t get_uint64(struct base_blob blob, int pos) { + const uint8_t* ptr = blob.data + pos * 8; + return ((uint64_t)ptr[0]) | \ + ((uint64_t)ptr[1]) << 8 | \ + ((uint64_t)ptr[2]) << 16 | \ + ((uint64_t)ptr[3]) << 24 | \ + ((uint64_t)ptr[4]) << 32 | \ + ((uint64_t)ptr[5]) << 40 | \ + ((uint64_t)ptr[6]) << 48 | \ + ((uint64_t)ptr[7]) << 56; +} + +static int compare(const struct base_blob* blob, const struct base_blob* other) { + return memcmp(blob->data, other->data, sizeof(blob->data)); +} + +static dogecoin_bool eq(const struct base_blob* a, const struct base_blob* b) { + return compare(a, b) == 0; +} + +static dogecoin_bool neq(const struct base_blob* a, const struct base_blob* b) { + return compare(a, b) != 0; +} + +static dogecoin_bool lt(const struct base_blob* a, const struct base_blob* b) { + return compare(a, b) < 0; +} + +static char* get_hex(struct base_blob blob) { + char* psz = dogecoin_char_vla(sizeof(blob.data) * 2 + 1); + unsigned int i = 0; + for (; i < sizeof(blob.data); i++) + sprintf(psz + i * 2, "%02x", blob.data[sizeof(blob.data) - i - 1]); + return psz; +} + +static void set_hex(const struct base_blob blob, const char* psz) { + memset_safe(blob.data, 0, sizeof(blob.data), 0); + + // skip leading spaces + while (isspace(*psz)) + psz++; + + // skip 0x + if (psz[0] == '0' && tolower(psz[1]) == 'x') + psz += 2; + + // hex string to uint + const char* pbegin = psz; + while (utils_hex_digit(*psz) != -1) + psz++; + psz--; + unsigned char* p1 = (unsigned char*)blob.data; + unsigned char* pend = p1 + WIDTH; + while (psz >= pbegin && p1 < pend) { + *p1 = utils_hex_digit(*psz--); + if (psz >= pbegin) { + *p1 |= ((unsigned char)utils_hex_digit(*psz--) << 4); + p1++; + } + } +}; + +static char* to_str(const struct base_blob blob) { + return get_hex(blob); +} + +static void set_compact_blob(struct base_blob* blob, uint32_t compact, dogecoin_bool *pf_negative, dogecoin_bool *pf_overflow) { + int size = compact >> 24; + uint32_t word = compact & 0x007fffff; + if (size <= 3) { + word >>= 8 * (3 - size); + memcpy_safe(blob->data, &word, sizeof word); + } else { + word <<= 8 * (size - 3); + memcpy_safe(blob->data, &word, sizeof word); + } + if (pf_negative) *pf_negative = word != 0 && (compact & 0x00800000) != 0; + if (pf_overflow) *pf_overflow = word != 0 && ((size > 34) || + (word > 0xff && size > 33) || + (word > 0xffff && size > 32)); +} + +static struct base_blob* init_blob() { + struct base_blob* blob = (struct base_blob*)dogecoin_calloc(1, sizeof(*blob)); + blob->set_data = set_data; + blob->is_null = is_null; + blob->set_null = set_null; + blob->begin = begin; + blob->end = end; + blob->size = size; + blob->get_uint64 = get_uint64; + blob->compare = compare; + blob->eq = eq; + blob->neq = neq; + blob->lt = lt; + blob->get_hex = get_hex; + blob->set_hex = set_hex; + blob->to_str = to_str; + blob->serialize = ser_bytes; + blob->unserialize = deser_bytes; + blob->set_null(blob->data); + return blob; +} + +LIBDOGECOIN_END_DECL + +#endif // __LIBDOGECOIN_UINT256_H__ diff --git a/include/dogecoin/utils.h b/include/dogecoin/utils.h index 9e176e6e1..192efd3cc 100644 --- a/include/dogecoin/utils.h +++ b/include/dogecoin/utils.h @@ -62,6 +62,11 @@ LIBDOGECOIN_API void utils_uint256_sethex(char* psz, uint8_t* out); LIBDOGECOIN_API uint256* uint256S(const char *str); LIBDOGECOIN_API unsigned char* parse_hex(const char* psz); LIBDOGECOIN_API void swap_bytes(uint8_t *buf, int buf_size); +LIBDOGECOIN_API const char *find_needle(const char *haystack, size_t haystack_length, const char *needle, size_t needle_length); +LIBDOGECOIN_API uint8_t* bytes_find(uint8_t* haystack, size_t haystackLen, uint8_t* needle, size_t needleLen); +LIBDOGECOIN_API char* to_string(uint8_t* x); +LIBDOGECOIN_API char* hash_to_string(uint8_t* x); +LIBDOGECOIN_API uint8_t* hash_to_bytes(uint8_t* x); LIBDOGECOIN_API void* safe_malloc(size_t size); LIBDOGECOIN_API void dogecoin_cheap_random_bytes(uint8_t* buf, size_t len); LIBDOGECOIN_API void dogecoin_get_default_datadir(cstring* path_out); diff --git a/include/dogecoin/validation.h b/include/dogecoin/validation.h new file mode 100644 index 000000000..4b1bb924b --- /dev/null +++ b/include/dogecoin/validation.h @@ -0,0 +1,46 @@ +/* + + The MIT License (MIT) + + Copyright (c) 2023 bluezr + Copyright (c) 2023 The Dogecoin Foundation + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#ifndef __LIBDOGECOIN_VALIDATION_H__ +#define __LIBDOGECOIN_VALIDATION_H__ + +#include +#include + +LIBDOGECOIN_BEGIN_DECL + +static int32_t VERSION_AUXPOW = (1 << 8); + +LIBDOGECOIN_API uint32_t get_chainid(uint32_t version); +LIBDOGECOIN_API dogecoin_bool is_auxpow(uint32_t version); +LIBDOGECOIN_API dogecoin_bool is_legacy(uint32_t version); +LIBDOGECOIN_API dogecoin_bool check_auxpow(dogecoin_auxpow_block block, dogecoin_chainparams* params); +LIBDOGECOIN_API dogecoin_bool dogecoin_block_header_scrypt_hash(cstring* s, uint256 hash); + +LIBDOGECOIN_END_DECL + +#endif // __LIBDOGECOIN_VALIDATION_H__ diff --git a/src/arith_uint256.c b/src/arith_uint256.c new file mode 100644 index 000000000..39e148e22 --- /dev/null +++ b/src/arith_uint256.c @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2023 bluezr + * Copyright (c) 2023 The Dogecoin Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include +#include + +arith_uint256 set_compact(arith_uint256 hash, uint32_t compact, dogecoin_bool *pf_negative, dogecoin_bool *pf_overflow) { + int size = compact >> 24; + uint32_t word = compact & 0x007fffff; + if (size <= 3) { + word >>= 8 * (3 - size); + memcpy_safe(&hash, &word, sizeof word); + } else { + word <<= 8 * (size - 3); + memcpy_safe(&hash, &word, sizeof word); + } + if (pf_negative) *pf_negative = word != 0 && (compact & 0x00800000) != 0; + if (pf_overflow) *pf_overflow = word != 0 && ((size > 34) || + (word > 0xff && size > 33) || + (word > 0xffff && size > 32)); + return hash; +} + +arith_uint256 uint_to_arith(const uint256* a) +{ + arith_uint256 b; + b.WIDTH = BITS / 32; + int x = 0; + for(; x < b.WIDTH; ++x) + b.pn[x] = read_le32((const unsigned char*)a + x * 4); + return b; +} + +uint256* arith_to_uint256(const arith_uint256 a) { + uint256* b = dogecoin_uint256_vla(1); + int x = 0; + for(; x < a.WIDTH; ++x) + write_le32((unsigned char*)b + x * 4, a.pn[x]); + return b; +} + +uint64_t get_low64(arith_uint256 a) { + assert(a.WIDTH >= 2); + return a.pn[0] | (uint64_t)a.pn[1] << 32; +} diff --git a/src/auxpow.c b/src/auxpow.c new file mode 100644 index 000000000..c40703582 --- /dev/null +++ b/src/auxpow.c @@ -0,0 +1,215 @@ +// #include +// #include +#include +#include + +// const uint256* ABANDON_HASH(...uint256S("0000000000000000000000000000000000000000000000000000000000000001")); + +// void SetMerkleBranch(dogecoin_blockindex* pindex, int posInBlock) { +// // Update the tx's hashBlock +// uint256 hash; +// dogecoin_block_header_hash(&pindex->header, hash); + +// // set the position of the transaction in the block +// nIndex = posInBlock; +// } + +// void InitMerkleBranch(const dogecoin_blockheader* block, int posInBlock) +// { +// hashBlock = block.GetHash(); +// nIndex = posInBlock; +// vMerkleBranch = BlockMerkleBranch(block, nIndex); +// } + +// int GetDepthInMainChain(const dogecoin_blockindex* &pindexRet) const +// { +// if (hashUnset()) +// return 0; + +// AssertLockHeld(cs_main); + +// // Find the block it claims to be in +// BlockMap::iterator mi = mapBlockIndex.find(hashBlock); +// if (mi == mapBlockIndex.end()) +// return 0; +// dogecoin_blockindex* pindex = (*mi).second; +// if (!pindex || !chainActive.Contains(pindex)) +// return 0; + +// pindexRet = pindex; +// return ((nIndex == -1) ? (-1) : 1) * (chainActive.Height() - pindex->nHeight + 1); +// } + +// int GetBlocksToMaturity() const +// { +// if (!IsCoinBase()) +// return 0; +// int nCoinbaseMaturity = Params().GetConsensus(chainActive.Height()).nCoinbaseMaturity; +// return std::max(0, (nCoinbaseMaturity + 1) - GetDepthInMainChain()); +// } + + +// bool AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state) +// { +// return ::AcceptToMemoryPool(mempool, state, tx, true, NULL, NULL, false, nAbsurdFee); +// } + +// bool +// CAuxPow::check(const uint256& hashAuxBlock, int nChainId, +// const Consensus::Params& params) const +// { +// if (nIndex != 0) +// return error("AuxPow is not a generate"); + +// if (params.fStrictChainId && parentBlock.GetChainId () == nChainId) +// return error("Aux POW parent has our chain ID"); + +// if (vChainMerkleBranch.size() > 30) +// return error("Aux POW chain merkle branch too long"); + +// // Check that the chain merkle root is in the coinbase +// const uint256 nRootHash +// = CheckMerkleBranch(hashAuxBlock, vChainMerkleBranch, nChainIndex); +// std::vector vchRootHash(nRootHash.begin(), nRootHash.end()); +// std::reverse(vchRootHash.begin(), vchRootHash.end()); // correct endian + +// // Check that we are in the parent block merkle tree +// if (CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) +// != parentBlock.hashMerkleRoot) +// return error("Aux POW merkle root incorrect"); + +// const CScript script = tx->vin[0].scriptSig; + +// // Check that the same work is not submitted twice to our chain. +// // + +// CScript::const_iterator pcHead = +// std::search(script.begin(), script.end(), UBEGIN(pchMergedMiningHeader), UEND(pchMergedMiningHeader)); + +// CScript::const_iterator pc = +// std::search(script.begin(), script.end(), vchRootHash.begin(), vchRootHash.end()); + +// if (pc == script.end()) +// return error("Aux POW missing chain merkle root in parent coinbase"); + +// if (pcHead != script.end()) +// { +// // Enforce only one chain merkle root by checking that a single instance of the merged +// // mining header exists just before. +// if (script.end() != std::search(pcHead + 1, script.end(), UBEGIN(pchMergedMiningHeader), UEND(pchMergedMiningHeader))) +// return error("Multiple merged mining headers in coinbase"); +// if (pcHead + sizeof(pchMergedMiningHeader) != pc) +// return error("Merged mining header is not just before chain merkle root"); +// } +// else +// { +// // For backward compatibility. +// // Enforce only one chain merkle root by checking that it starts early in the coinbase. +// // 8-12 bytes are enough to encode extraNonce and nBits. +// if (pc - script.begin() > 20) +// return error("Aux POW chain merkle root must start in the first 20 bytes of the parent coinbase"); +// } + + +// // Ensure we are at a deterministic point in the merkle leaves by hashing +// // a nonce and our chain ID and comparing to the index. +// pc += vchRootHash.size(); +// if (script.end() - pc < 8) +// return error("Aux POW missing chain merkle tree size and nonce in parent coinbase"); + +// uint32_t nSize; +// memcpy(&nSize, &pc[0], 4); +// nSize = le32toh(nSize); +// const unsigned merkleHeight = vChainMerkleBranch.size(); +// if (nSize != (1u << merkleHeight)) +// return error("Aux POW merkle branch size does not match parent coinbase"); + +// uint32_t nNonce; +// memcpy(&nNonce, &pc[4], 4); +// nNonce = le32toh (nNonce); +// if (nChainIndex != get_expected_index (nNonce, nChainId, merkleHeight)) +// return error("Aux POW wrong index"); + +// return true; +// } + +int get_expected_index (uint32_t nNonce, int nChainId, unsigned h) +{ + // Choose a pseudo-random slot in the chain merkle tree + // but have it be fixed for a size/nonce/chain combination. + // + // This prevents the same work from being used twice for the + // same chain while reducing the chance that two chains clash + // for the same slot. + + /* This computation can overflow the uint32 used. This is not an issue, + though, since we take the mod against a power-of-two in the end anyway. + This also ensures that the computation is, actually, consistent + even if done in 64 bits as it was in the past on some systems. + + Note that h is always <= 30 (enforced by the maximum allowed chain + merkle branch length), so that 32 bits are enough for the computation. */ + + uint32_t rand = nNonce; + rand = rand * 1103515245 + 12345; + rand += nChainId; + rand = rand * 1103515245 + 12345; + + return rand % (1 << h); +} + +uint256* check_merkle_branch(uint256 hash, const vector* parent_coinbase_merkle, int n_index) { + if (n_index == -1) return dogecoin_uint256_vla(1); + unsigned int i = n_index; + for (; i < n_index; i++) { + uint256 pcm; + memcpy(pcm, vector_idx(parent_coinbase_merkle, i), 32); + if (i & 1) + hash = Hash(BEGIN(*pcm), END(*pcm), BEGIN(hash), END(hash)); + else + hash = Hash(BEGIN(hash), END(hash), BEGIN(*pcm), END(*pcm)); + i >>= 1; + } + return hash; +} + +void init_aux_pow(dogecoin_block_header* header) { + /* Set auxpow flag right now, since we take the block hash below. */ + header->auxpow->is = true; + + /* Build a minimal coinbase script input for merge-mining. */ + const uint256 block_hash; + dogecoin_block_header_hash(header, (uint8_t*)block_hash); + vector* input = vector_new(1, free); + char* hexbuf = utils_uint8_to_hex((const uint8_t*)block_hash, DOGECOIN_HASH_LENGTH); + utils_reverse_hex(hexbuf, DOGECOIN_HASH_LENGTH*2); + memcpy_safe((void*)block_hash, utils_hex_to_uint8(hexbuf), 32); + vector_add(input, (void*)block_hash); + + /* Fake a parent-block coinbase with just the required input + script and no outputs. */ + dogecoin_tx* coinbase = dogecoin_tx_new(); + vector_resize(coinbase->vin, 1); + const uint256 empty_hash; + dogecoin_tx_in* tx_in = coinbase->vin->data[0]; + memcpy_safe(tx_in->prevout.hash, empty_hash, 32); + dogecoin_script script; + tx_in->script_sig = input->data[0]; + assert(coinbase->vout->len==0); + // CTransactionRef coinbaseRef = MakeTransactionRef(coinbase); + + // /* Build a fake parent block with the coinbase. */ + // CBlock parent; + // parent.nVersion = 1; + // parent.vtx.resize(1); + // parent.vtx[0] = coinbaseRef; + // parent.hashMerkleRoot = BlockMerkleRoot(parent); + + // /* Construct the auxpow object. */ + // header.SetAuxpow(new CAuxPow(coinbaseRef)); + // assert (header.auxpow->vChainMerkleBranch.empty()); + // header.auxpow->nChainIndex = 0; + // assert (header.auxpow->vMerkleBranch.empty()); + // header.auxpow->nIndex = 0; + // header.auxpow->parentBlock = parent; +} diff --git a/src/block.c b/src/block.c index 162fd4c52..89255f176 100644 --- a/src/block.c +++ b/src/block.c @@ -31,14 +31,121 @@ #include #include #include +#include -#include +#include #include #include #include #include +#include -#define BLOCK_VERSION_AUXPOW_BIT 0x100 +typedef struct pattern_match { + size_t index; + char* data; + void (*set_pattern_match)(struct pattern_match* pm, size_t index, char* data); +} pattern_match; + +void set_pattern_match(struct pattern_match* pm, size_t index, char* data) { + pm->index = index; + memcpy_safe(pm->data, data, strlen(data)); +} + +pattern_match* init_pattern_match(size_t length) { + pattern_match* pm = dogecoin_calloc(1, sizeof(*pm)); + dogecoin_mem_zero(pm->data, length); + pm->index = 0; + pm->set_pattern_match = set_pattern_match; + return pm; +} + +dogecoin_bool check(void *ctx, uint256* hash, uint32_t chainid, dogecoin_chainparams* params) { + dogecoin_auxpow_block* block = (dogecoin_auxpow_block*)ctx; + + if ((block->parent_merkle_index || block->aux_merkle_index) != 0) { + printf("Auxpow is not a generate\n"); + return false; + } + + uint32_t parent_chainid = get_chainid(block->parent_header->version); + if (params->strict_id && parent_chainid == chainid) { + printf("Aux POW parent has our chain ID"); + return false; + } + + vector* parent_merkle = vector_new(8, free); + size_t p = 0; + for (; p < block->parent_merkle_count; p++) { + vector_add(parent_merkle, block->parent_coinbase_merkle[p]); + } + + // Check that the chain merkle root is in the coinbase + uint256* n_roothash = check_merkle_branch(hash, parent_merkle, parent_merkle->len); + unsigned char* vch_roothash = (unsigned char*)hash_to_string((uint8_t*)n_roothash); // correct endian + + dogecoin_tx_in* tx_in = dogecoin_tx_in_new(); + tx_in = vector_idx(block->parent_coinbase->vin, 0); + char* script_sig = utils_uint8_to_hex((uint8_t*)tx_in->script_sig->str, tx_in->script_sig->len); + + size_t haystack_index = 0, ct = 0; + for (; haystack_index < tx_in->script_sig->len; haystack_index++) { + + if (memcmp(&tx_in->script_sig->str[haystack_index], pchMergedMiningHeader, 4)==0) { + size_t j = tx_in->script_sig->len - haystack_index; + ct++; // multiple merged mining headers found if more than 1 + + if (haystack_index > 45) { + printf("total length: %zu index: %zu j: %zu char: %s\n", tx_in->script_sig->len, haystack_index, j, utils_uint8_to_hex((uint8_t*)&tx_in->script_sig->str[haystack_index], 4)); + printf("Merged mining header found too late in coinbase!\n"); + return false; + } + + char* merkle_root = to_string(&tx_in->script_sig->str[haystack_index + 4]); + utils_reverse_hex(merkle_root, 32*2); + if (strncmp(to_string((uint8_t*)hash), merkle_root, 32) != 0) { + printf("not equal\n"); + return false; + } + + char* leftover = utils_uint8_to_hex((uint8_t*)&tx_in->script_sig->str[haystack_index + 4 + 32], j - 36); + + char merkle_size[9]; + dogecoin_mem_zero(merkle_size, 5); + slice(leftover, merkle_size, 0, 8); + + char merkle_nonce[9]; + dogecoin_mem_zero(merkle_nonce, 5); + slice(leftover, merkle_nonce, 8, 16); + + if ((strlen(merkle_size) + strlen(merkle_nonce)) / 2 < 8) { + printf("Aux POW missing chain merkle tree size and nonce in parent coinbase\n"); + return false; + } + + uint32_t nSize; + memcpy(&nSize, &tx_in->script_sig->str[haystack_index + 4 + 32], 4); + nSize = le32toh(nSize); + const unsigned merkleHeight = sizeof(vch_roothash); + if ((nSize * 256) != (1u << merkleHeight)) { + printf("Aux POW merkle branch size does not match parent coinbase\n"); + return false; + } + + uint32_t nNonce; + memcpy(&nNonce, &tx_in->script_sig->str[haystack_index + 4 + 32 + 4], 4); + nNonce = le32toh(nNonce); + uint32_t expected_index = get_expected_index(nNonce, chainid, merkleHeight); + + if (56 != expected_index) { + printf("total length: %zu index: %zu j: %zu char: %s expected index: %u\n", tx_in->script_sig->len, haystack_index, j, utils_uint8_to_hex((uint8_t*)&tx_in->script_sig->str[haystack_index], 4), expected_index); + printf("Aux POW wrong index\n"); + return false; + } + } + } + + return true; +} /** * @brief This function allocates a new dogecoin block header-> @@ -61,6 +168,8 @@ dogecoin_auxpow_block* dogecoin_auxpow_block_new() { block->header = dogecoin_block_header_new(); block->parent_coinbase = dogecoin_tx_new(); block->parent_header = dogecoin_block_header_new(); + block->header->auxpow->check = check; + block->header->auxpow->ctx = block; return block; } @@ -94,6 +203,77 @@ void dogecoin_auxpow_block_free(dogecoin_auxpow_block* block) { dogecoin_free(block); } +void print_transaction(dogecoin_tx* x) { + // serialize tx & print raw hex: + cstring* tx = cstr_new_sz(1024); + dogecoin_tx_serialize(tx, x); + char tx_hex[tx->len*2]; + utils_bin_to_hex((unsigned char *)tx->str, tx->len, tx_hex); + printf("block->parent_coinbase (hex): %s\n", tx_hex); // uncomment to see raw hexadecimal transactions + + // begin deconstruction into objects: + printf("block->parent_coinbase->version: %d\n", x->version); + + // parse inputs: + unsigned int i = 0; + for (; i < x->vin->len; i++) { + printf("block->parent_coinbase->tx_in->i: %d\n", i); + dogecoin_tx_in* tx_in = vector_idx(x->vin, i); + printf("block->parent_coinbase->vin->prevout.n: %d\n", tx_in->prevout.n); + char* hex_utxo_txid = utils_uint8_to_hex(tx_in->prevout.hash, sizeof tx_in->prevout.hash); + printf("block->parent_coinbase->tx_in->prevout.hash: %s\n", hex_utxo_txid); + char* script_sig = utils_uint8_to_hex((const uint8_t*)tx_in->script_sig->str, tx_in->script_sig->len); + printf("block->parent_coinbase->tx_in->script_sig: %s\n", script_sig); + + printf("block->parent_coinbase->tx_in->sequence: %x\n", tx_in->sequence); + } + + // parse outputs: + i = 0; + for (; i < x->vout->len; i++) { + printf("block->parent_coinbase->tx_out->i: %d\n", i); + dogecoin_tx_out* tx_out = vector_idx(x->vout, i); + printf("block->parent_coinbase->tx_out->script_pubkey: %s\n", utils_uint8_to_hex((const uint8_t*)tx_out->script_pubkey->str, tx_out->script_pubkey->len)); + printf("block->parent_coinbase->tx_out->value: %" PRId64 "\n", tx_out->value); + } + printf("block->parent_coinbase->locktime: %d\n", x->locktime); + cstr_free(tx, true); +} + +void print_block_header(dogecoin_block_header* header) { + printf("block->header->version: %i\n", header->version); + printf("block->header->prev_block: %s\n", to_string(header->prev_block)); + printf("block->header->merkle_root: %s\n", to_string(header->merkle_root)); + printf("block->header->timestamp: %u\n", header->timestamp); + printf("block->header->bits: %x\n", header->bits); + printf("block->header->nonce: %x\n", header->nonce); +} + +void print_parent_header(dogecoin_auxpow_block* block) { + printf("block->parent_hash: %s\n", to_string(block->parent_hash)); + printf("block->parent_merkle_count: %d\n", block->parent_merkle_count); + size_t j = 0; + for (; j < block->parent_merkle_count; j++) { + printf("block->parent_coinbase_merkle[%zu]: " + "%s\n", j, to_string((uint8_t*)block->parent_coinbase_merkle[j])); + } + printf("block->parent_merkle_index: %d\n", block->parent_merkle_index); + printf("block->aux_merkle_count: %d\n", block->aux_merkle_count); + printf("block->aux_merkle_index: %d\n", block->aux_merkle_index); + printf("block->parent_header.version: %i\n", block->parent_header->version); + printf("block->parent_header.prev_block: %s\n", to_string(block->parent_header->prev_block)); + printf("block->parent_header.merkle_root: %s\n", to_string(block->parent_header->merkle_root)); + printf("block->parent_header.timestamp: %u\n", block->parent_header->timestamp); + printf("block->parent_header.bits: %x\n", block->parent_header->bits); + printf("block->parent_header.nonce: %u\n\n", block->parent_header->nonce); +} + +void print_block(dogecoin_auxpow_block* block) { + print_block_header(block->header); + print_transaction(block->parent_coinbase); + print_parent_header(block); +} + /** * @brief This function takes a raw buffer and deserializes * it into a dogecoin block header object += auxpow data. @@ -105,7 +285,7 @@ void dogecoin_auxpow_block_free(dogecoin_auxpow_block* block) { */ int dogecoin_block_header_deserialize(dogecoin_block_header* header, struct const_buffer* buf) { dogecoin_auxpow_block* block = dogecoin_auxpow_block_new(); - if (!deser_s32(&block->header->version, buf)) + if (!deser_u32(&block->header->version, buf)) return false; if (!deser_u256(block->header->prev_block, buf)) return false; @@ -119,7 +299,9 @@ int dogecoin_block_header_deserialize(dogecoin_block_header* header, struct cons return false; dogecoin_block_header_copy(header, block->header); if ((block->header->version & BLOCK_VERSION_AUXPOW_BIT) != 0) { - deserialize_dogecoin_auxpow_block(block, buf); + if (!deserialize_dogecoin_auxpow_block(block, buf)) { + printf("%d:%s:%s\n", __LINE__, __func__, "failed :("); + } } dogecoin_auxpow_block_free(block); return true; @@ -139,17 +321,18 @@ int deserialize_dogecoin_auxpow_block(dogecoin_auxpow_block* block, struct const if (!deser_skip(buffer, consumedlength)) return false; + block->header->auxpow->is = (block->header->version & BLOCK_VERSION_AUXPOW_BIT) == 256; + if (!deser_u256(block->parent_hash, buffer)) return false; if (!deser_varlen((uint32_t*)&block->parent_merkle_count, buffer)) return false; uint8_t i = 0; + block->parent_coinbase_merkle = dogecoin_calloc(block->parent_merkle_count, sizeof(uint256)); for (; i < block->parent_merkle_count; i++) { - hash* parent_cb_merkle_branch = new_hash(); - if (!deser_u256((uint8_t*)parent_cb_merkle_branch->data.u8, buffer)) { + if (!deser_u256(block->parent_coinbase_merkle[i], buffer)) { return false; } - dogecoin_free(parent_cb_merkle_branch); } if (!deser_u32(&block->parent_merkle_index, buffer)) return false; @@ -166,13 +349,19 @@ int deserialize_dogecoin_auxpow_block(dogecoin_auxpow_block* block, struct const if (!deser_u32(&block->aux_merkle_index, buffer)) return false; - if (!deser_s32(&block->parent_header->version, buffer)) return false; + if (!deser_u32(&block->parent_header->version, buffer)) return false; if (!deser_u256(block->parent_header->prev_block, buffer)) return false; if (!deser_u256(block->parent_header->merkle_root, buffer)) return false; if (!deser_u32(&block->parent_header->timestamp, buffer)) return false; if (!deser_u32(&block->parent_header->bits, buffer)) return false; if (!deser_u32(&block->parent_header->nonce, buffer)) return false; + // print_block(block); + if (!check_auxpow(*block, (dogecoin_chainparams*)&dogecoin_chainparams_main)) { + printf("check_auxpow failed!\n"); + return false; + } + return true; } @@ -186,7 +375,7 @@ int deserialize_dogecoin_auxpow_block(dogecoin_auxpow_block* block, struct const * @return Nothing. */ void dogecoin_block_header_serialize(cstring* s, const dogecoin_block_header* header) { - ser_s32(s, header->version); + ser_u32(s, header->version); ser_u256(s, header->prev_block); ser_u256(s, header->merkle_root); ser_u32(s, header->timestamp); diff --git a/src/chainparams.c b/src/chainparams.c index 6f5ca9bc5..ec341a1a2 100644 --- a/src/chainparams.c +++ b/src/chainparams.c @@ -27,6 +27,7 @@ */ #include +#include const dogecoin_chainparams dogecoin_chainparams_main = { "main", @@ -40,6 +41,9 @@ const dogecoin_chainparams dogecoin_chainparams_main = { {0x91, 0x56, 0x35, 0x2c, 0x18, 0x18, 0xb3, 0x2e, 0x90, 0xc9, 0xe7, 0x92, 0xef, 0xd6, 0xa1, 0x1a, 0x82, 0xfe, 0x79, 0x56, 0xa6, 0x30, 0xf0, 0x3b, 0xbe, 0xe2, 0x36, 0xce, 0xda, 0xe3, 0x91, 0x1a}, 22556, {{"seed.multidoge.org"}, {{1}}}, + true, + 0x0062, + {0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} // pow limit }; const dogecoin_chainparams dogecoin_chainparams_test = { @@ -54,6 +58,9 @@ const dogecoin_chainparams dogecoin_chainparams_test = { {0x9e, 0x55, 0x50, 0x73, 0xd0, 0xc4, 0xf3, 0x64, 0x56, 0xdb, 0x89, 0x51, 0xf4, 0x49, 0x70, 0x4d, 0x54, 0x4d, 0x28, 0x26, 0xd9, 0xaa, 0x60, 0x63, 0x6b, 0x40, 0x37, 0x46, 0x26, 0x78, 0x0a, 0xbb}, 44556, {{"testseed.jrn.me.uk"}, {{0}}}, + false, + 0x0062, + {0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} // pow limit }; const dogecoin_chainparams dogecoin_chainparams_regtest = { @@ -68,6 +75,9 @@ const dogecoin_chainparams dogecoin_chainparams_regtest = { {0xa5, 0x73, 0xe9, 0x1c, 0x17, 0x72, 0x07, 0x6c, 0x0d, 0x40, 0xf7, 0x0e, 0x44, 0x08, 0xc8, 0x3a, 0x31, 0x70, 0x5f, 0x29, 0x6a, 0xe6, 0xe7, 0x62, 0x9d, 0x4a, 0xdc, 0xb5, 0xa3, 0x60, 0x21, 0x3d}, 18332, {{"testseed.jrn.me.uk"}, {{0}}}, + true, + 0x0062, + {0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} // pow limit }; const dogecoin_checkpoint dogecoin_mainnet_checkpoint_array[] = { diff --git a/src/consensus.c b/src/consensus.c new file mode 100644 index 000000000..eb87ddffd --- /dev/null +++ b/src/consensus.c @@ -0,0 +1,193 @@ +// Copyright (c) 2015-2016 The Bitcoin Core developers +// Copyright (c) 2023 bluezr +// Copyright (c) 2023 The Dogecoin Foundation +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include + +/* WARNING! If you're reading this because you're learning about crypto + and/or designing a new system that will use merkle trees, keep in mind + that the following merkle tree algorithm has a serious flaw related to + duplicate txids, resulting in a vulnerability (CVE-2012-2459). + + The reason is that if the number of hashes in the list at a given time + is odd, the last one is duplicated before computing the next level (which + is unusual in Merkle trees). This results in certain sequences of + transactions leading to the same merkle root. For example, these two + trees: + + A A + / \ / \ + B C B C + / \ | / \ / \ + D E F D E F F + / \ / \ / \ / \ / \ / \ / \ + 1 2 3 4 5 6 1 2 3 4 5 6 5 6 + + for transaction lists [1,2,3,4,5,6] and [1,2,3,4,5,6,5,6] (where 5 and + 6 are repeated) result in the same root hash A (because the hash of both + of (F) and (F,F) is C). + + The vulnerability results from being able to send a block with such a + transaction list, with the same merkle root, and the same block hash as + the original without duplication, resulting in failed validation. If the + receiving node proceeds to mark that block as permanently invalid + however, it will fail to accept further unmodified (and thus potentially + valid) versions of the same block. We defend against this by detecting + the case where we would hash two identical hashes at the end of the list + together, and treating that identically to the block having an invalid + merkle root. Assuming no double-SHA256 collisions, this will detect all + known ways of changing the transactions without affecting the merkle + root. +*/ + +/* This implements a constant-space merkle root/path calculator, limited to 2^32 leaves. */ +static void merkle_computation(const vector* leaves, uint256* proot, dogecoin_bool* pmutated, uint32_t branchpos, vector* pbranch) { + if (pbranch) vector_remove_range(pbranch, 0, pbranch->len);; + if (leaves->len == 0) { + if (pmutated) *pmutated = false; + if (proot) memcpy_safe(*proot, dogecoin_uint256_vla(1), 32); + return; + } + bool mutated = false; + // count is the number of leaves processed so far. + uint32_t count = 0; + // inner is an array of eagerly computed subtree hashes, indexed by tree + // level (0 being the leaves). + // For example, when count is 25 (11001 in binary), inner[4] is the hash of + // the first 16 leaves, inner[3] of the next 8 leaves, and inner[0] equal to + // the last leaf. The other inner entries are undefined. + uint256 inner[32]; + // Which position in inner is a hash that depends on the matching leaf. + int matchlevel = -1; + // First process all leaves into 'inner' values. + while (count < leaves->len) { + uint256 h; + memcpy_safe(h, leaves->data[count], 32); + bool matchh = count == branchpos; + count++; + int level; + // For each of the lower bits in count that are 0, do 1 step. Each + // corresponds to an inner value that existed before processing the + // current leaf, and each needs a hash to combine it. + for (level = 0; !(count & (((uint32_t)1) << level)); level++) { + if (pbranch) { + if (matchh) { + vector_add(pbranch, inner[level]); + } else if (matchlevel == level) { + vector_add(pbranch, h); + matchh = true; + } + } + mutated |= (inner[level] == h); + chash256* chash = dogecoin_chash256_init(); + chash->write(chash->sha, UBEGIN(inner[level]), 32); + chash->write(chash->sha, UBEGIN(h), 32); + chash->finalize(chash->sha, UBEGIN(h)); + } + // Store the resulting hash at inner position level. + memcpy_safe(inner[level], h, 32); + if (matchh) { + matchlevel = level; + } + } + // Do a final 'sweep' over the rightmost branch of the tree to process + // odd levels, and reduce everything to a single top value. + // Level is the level (counted from the bottom) up to which we've sweeped. + int level = 0; + // As long as bit number level in count is zero, skip it. It means there + // is nothing left at this level. + while (!(count & (((uint32_t)1) << level))) { + level++; + } + uint256 h; + memcpy_safe(h, inner[level], 32); + bool matchh = matchlevel == level; + while (count != (((uint32_t)1) << level)) { + // If we reach this point, h is an inner value that is not the top. + // We combine it with itself (Bitcoin's special rule for odd levels in + // the tree) to produce a higher level one. + if (pbranch && matchh) { + vector_add(pbranch, h); + } + chash256* chash = dogecoin_chash256_init(); + chash->write(chash->sha, UBEGIN(h), 32); + chash->write(chash->sha, UBEGIN(h), 32); + chash->finalize(chash->sha, UBEGIN(h)); + // Increment count to the value it would have if two entries at this + // level had existed. + count += (((uint32_t)1) << level); + level++; + // And propagate the result upwards accordingly. + while (!(count & (((uint32_t)1) << level))) { + if (pbranch) { + if (matchh) { + vector_add(pbranch, inner[level]); + } else if (matchlevel == level) { + vector_add(pbranch, h); + matchh = true; + } + } + chash->write(chash->sha, UBEGIN(inner[level]), 32); + chash->write(chash->sha, UBEGIN(h), 32); + chash->finalize(chash->sha, UBEGIN(h)); + level++; + } + } + // Return result. + if (pmutated) *pmutated = mutated; + if (proot) memcpy_safe(*proot, h, 32); +} + +uint256* compute_merkle_root(const vector* leaves, dogecoin_bool* mutated) { + uint256* hash = dogecoin_uint256_vla(1); + merkle_computation(leaves, hash, mutated, -1, NULL); + return hash; +} + +vector* compute_merkle_branch(const vector* leaves, uint32_t position) { + vector* ret = vector_new(1, free); + merkle_computation(leaves, NULL, NULL, position, ret); + return ret; +} + +uint256* compute_merkle_root_from_branch(const uint256* leaf, const vector* merkle_branch_vec, uint32_t n_index) { + uint256* hash = dogecoin_uint256_vla(1); + memcpy_safe(hash, leaf, 32); + unsigned int i = 0; + for (; i < n_index; ++i) { + uint256 vmb; + memcpy(vmb, vector_idx(merkle_branch_vec, i), 32); + if (n_index & 1) { + hash = Hash(BEGIN(*vmb), END(*vmb), BEGIN(hash), END(hash)); + } else { + hash = Hash(BEGIN(hash), END(hash), BEGIN(*vmb), END(*vmb)); + } + n_index >>= 1; + } + return hash; +} + +uint256* block_merkle_root(const dogecoin_block* block, dogecoin_bool* mutated) +{ + vector* leaves = vector_new(1, free); + vector_resize(leaves, block->vtx->len); + size_t s = 0; + for (; s < block->vtx->len; s++) { + dogecoin_hash(block->vtx->data[s], 32, leaves->data[s]); + } + return compute_merkle_root(leaves, mutated); +} + +vector* block_merkle_branch(const dogecoin_block* block, uint32_t position) +{ + vector* leaves = vector_new(1, free); + vector_resize(leaves, block->vtx->len); + size_t s = 0; + for (; s < block->vtx->len; s++) { + dogecoin_hash(block->vtx->data[s], 32, leaves->data[s]); + } + return compute_merkle_branch(leaves, position); +} diff --git a/src/headersdb_file.c b/src/headersdb_file.c index 3c954abb1..67b32b73d 100644 --- a/src/headersdb_file.c +++ b/src/headersdb_file.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -281,6 +282,9 @@ dogecoin_blockindex * dogecoin_headers_db_connect_hdr(dogecoin_headers_db* db, s if (connect_at != NULL) { /* TODO: check claimed PoW */ + // if (!check_pow(blockindex->hash, blockindex->header.bits, &dogecoin_chainparams_main)) + // printf("%s: check_pow failed: %s\n", __func__, utils_uint8_to_hex(blockindex->hash, 32)); + blockindex->prev = connect_at; blockindex->height = connect_at->height+1; diff --git a/src/pow.c b/src/pow.c new file mode 100644 index 000000000..cfa28940b --- /dev/null +++ b/src/pow.c @@ -0,0 +1,48 @@ +/* + + The MIT License (MIT) + + Copyright (c) 2023 bluezr + Copyright (c) 2023 The Dogecoin Foundation + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + */ + +#include +#include +#include + +dogecoin_bool check_pow(uint256* hash, unsigned int nbits, dogecoin_chainparams *params) { + dogecoin_bool f_negative, f_overflow; + arith_uint256 target; + target = set_compact(target, nbits, &f_negative, &f_overflow); + arith_uint256 h; + h = uint_to_arith((const uint256*)hash); + char* hash_str = utils_uint8_to_hex((const uint8_t*)&h, 32); + char* target_str = utils_uint8_to_hex((const uint8_t*)&target, 32); + if (f_negative || (const uint8_t*)&target == 0 || f_overflow || memcmp(&target.pn[0], ¶ms->pow_limit, 32) > 0) { + printf("%d:%s: f_negative: %d target == 0: %d f_overflow: %d memcmp target powlimit: %d\n", + __LINE__, __func__, f_negative, (const uint8_t*)&target == 0, f_overflow, memcmp(&target, ¶ms->pow_limit, 4) > 0); + return false; + } + if (strcmp(hash_str, target_str) > 0) + return false; + return true; +} diff --git a/src/serialize.c b/src/serialize.c index 7afa02143..01ade12e6 100644 --- a/src/serialize.c +++ b/src/serialize.c @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -26,7 +27,6 @@ void ser_bytes(cstring* s, const void* p, size_t len) cstr_append_buf(s, p, len); } - /** * @brief This function takes 2 unsigned bytes and * appends them to an existing cstring by converting diff --git a/src/sha2.c b/src/sha2.c index 9c580b60c..823626e56 100644 --- a/src/sha2.c +++ b/src/sha2.c @@ -641,6 +641,14 @@ void sha256_raw(const sha2_byte* data, size_t len, uint8_t digest[SHA256_DIGEST_ sha256_finalize(&context, digest); } +void sha256_reset(sha256_context* ctx) { + if (ctx == (sha256_context*)0) + return; + MEMCPY_BCOPY(ctx->state, sha256_initial_hash_value, SHA256_DIGEST_LENGTH); + MEMSET_BZERO(ctx->buffer, SHA256_BLOCK_LENGTH); + ctx->bitcount = 0; +} + /*** SHA-512: *********************************************************/ void sha512_init(sha512_context* context) { diff --git a/src/utils.c b/src/utils.c index 4158cd665..fb825622f 100644 --- a/src/utils.c +++ b/src/utils.c @@ -369,6 +369,63 @@ void swap_bytes(uint8_t *buf, int buf_size) { } } +// Returns a pointer to the first byte of needle inside haystack, +uint8_t* bytes_find(uint8_t* haystack, size_t haystackLen, uint8_t* needle, size_t needleLen) { + if (needleLen > haystackLen) { + return false; + } + uint8_t* match = memchr(haystack, needle[0], haystackLen); + if (match != NULL) { + size_t remaining = haystackLen - ((uint8_t*)match - haystack); + if (needleLen <= remaining) { + if (memcmp(match, needle, needleLen) == 0) { + return match; + } + } + } + return NULL; +} + +const char *find_needle(const char *haystack, size_t haystack_length, const char *needle, size_t needle_length) { + size_t haystack_index = 0; + for (; haystack_index < haystack_length; haystack_index++) { + + bool needle_found = true; + size_t needle_index = 0; + for (; needle_index < needle_length; needle_index++) { + const auto haystack_character = haystack[haystack_index + needle_index]; + const auto needle_character = needle[needle_index]; + if (haystack_character == needle_character) { + continue; + } else { + needle_found = false; + break; + } + } + + if (needle_found) { + return &haystack[haystack_index]; + } + } + + return NULL; +} + +char* to_string(uint8_t* x) { + return utils_uint8_to_hex(x, 32); +} + +char* hash_to_string(uint8_t* x) { + char* hexbuf = to_string(x); + utils_reverse_hex(hexbuf, DOGECOIN_HASH_LENGTH*2); + return hexbuf; +} + +uint8_t* hash_to_bytes(uint8_t* x) { + char* hexbuf = hash_to_string(x); + return utils_hex_to_uint8(hexbuf); +} + /** * @brief This function executes malloc() but exits the * program if unsuccessful. diff --git a/src/validation.c b/src/validation.c new file mode 100644 index 000000000..1c6f2ef76 --- /dev/null +++ b/src/validation.c @@ -0,0 +1,124 @@ +/* + + The MIT License (MIT) + + Copyright (c) 2023 bluezr + Copyright (c) 2023 The Dogecoin Foundation + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + */ + +#include +#include +#include + +#include +#include +#include + + +/** + * @brief This function takes a block header and generates its + * Scrypt hash. + * + * @param header The pointer to the block header to hash. + * @param hash The Scrypt hash of the block header + * + * @return True. + */ +dogecoin_bool dogecoin_block_header_scrypt_hash(cstring* s, uint256 hash) { + char scratchpad[SCRYPT_SCRATCHPAD_SIZE]; + unsigned char inputbytes[80]; + memcpy_safe(inputbytes, parse_hex(utils_uint8_to_hex((uint8_t*)s->str, s->len)), s->len); + scrypt_1024_1_1_256_sp_generic((const char*)&inputbytes[0], BEGIN(hash), scratchpad); + return true; + } + +uint32_t get_chainid(uint32_t version) { + return version >> 16; +} + +dogecoin_bool is_auxpow(uint32_t version) { + return (version & VERSION_AUXPOW) == 256; +} + +dogecoin_bool is_legacy(uint32_t version) { + return version == 1 + // Dogecoin: We have a random v2 block with no AuxPoW, treat as legacy + || (version == 2 && get_chainid(version) == 0); +} + +dogecoin_bool check_auxpow(dogecoin_auxpow_block block, dogecoin_chainparams* params) { + /* Except for legacy blocks with full version 1, ensure that + the chain ID is correct. Legacy blocks are not allowed since + the merge-mining start, which is checked in AcceptBlockHeader + where the height is known. */ + if (!is_legacy(block.header->version) && params->strict_id && get_chainid(block.header->version) != params->auxpow_id) { + printf("%s:%d:%s : block does not have our chain ID" + " (got %d, expected %d, full nVersion %d) : %s\n", + __FILE__, __LINE__, __func__, get_chainid(block.header->version), + params->auxpow_id, block.header->version, strerror(errno)); + return false; + } + + /* If there is no auxpow, just check the block hash. */ + if (!block.header->auxpow->is) { + if (is_auxpow(block.header->version)) { + printf("%s:%d:%s : no auxpow on block with auxpow version : %s\n", __FILE__, __LINE__, __func__, strerror(errno)); + return false; + } + + uint256 auxpow_hash; + dogecoin_get_auxpow_hash(block.header->version, auxpow_hash); + if (!check_pow(&auxpow_hash, block.header->bits, params)) { + printf("%s:%d:%s : non-AUX proof of work failed : %s\n", __FILE__, __LINE__, __func__, strerror(errno)); + return false; + } + + return true; + } + + /* We have auxpow. Check it. */ + + if (!is_auxpow(block.header->version)) { + printf("%s:%d:%s : auxpow on block with non-auxpow version : %s\n", __FILE__, __LINE__, __func__, strerror(errno)); + return false; + } + + uint256 block_header_hash; + dogecoin_block_header_hash(block.header, block_header_hash); + uint32_t chainid = get_chainid(block.header->version); + if (!block.header->auxpow->check(&block, &block_header_hash, chainid, params)) { + printf("%s:%d:%s : AUX POW is not valid : %s\n", __FILE__, __LINE__, __func__, strerror(errno)); + return false; + } + + uint256 parent_hash; + cstring* s = cstr_new_sz(80); + dogecoin_block_header_serialize(s, block.parent_header); + dogecoin_block_header_scrypt_hash(s, parent_hash); + cstr_free(s, true); + // swap_bytes((uint8_t*)&parent_hash, DOGECOIN_HASH_LENGTH); + if (!check_pow(&parent_hash, block.header->bits, params)) { + return false; + } + + return true; +}