diff --git a/src/cn_utils/crypto/hash-ops.h b/src/cn_utils/crypto/hash-ops.h index 14f678e5e..699ed51b4 100644 --- a/src/cn_utils/crypto/hash-ops.h +++ b/src/cn_utils/crypto/hash-ops.h @@ -87,3 +87,7 @@ void hash_extra_jh(const void *data, size_t length, char *hash); void hash_extra_skein(const void *data, size_t length, char *hash); void tree_hash(const char (*hashes)[HASH_SIZE], size_t count, char *root_hash); + +size_t tree_depth(size_t count); +void tree_branch(const char (*hashes)[HASH_SIZE], size_t count, char (*branch)[HASH_SIZE]); +void tree_hash_from_branch(const char (*branch)[HASH_SIZE], size_t depth, const char* leaf, const void* path, char* root_hash); diff --git a/src/cn_utils/crypto/tree-hash.c b/src/cn_utils/crypto/tree-hash.c index b2dc3ffb2..aeb67f401 100644 --- a/src/cn_utils/crypto/tree-hash.c +++ b/src/cn_utils/crypto/tree-hash.c @@ -1,21 +1,21 @@ // Copyright (c) 2014-2018, The Monero Project -// +// // All rights reserved. -// +// // Redistribution and use in source and binary forms, with or without modification, are // permitted provided that the following conditions are met: -// +// // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. -// +// // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other // materials provided with the distribution. -// +// // 3. Neither the name of the copyright holder nor the names of its contributors may be // used to endorse or promote products derived from this software without specific // prior written permission. -// +// // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL @@ -25,7 +25,7 @@ // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// +// // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #include @@ -43,7 +43,7 @@ #include #endif -/*** +/*** * Round to power of two, for count>=3 and for count being not too large (as reasonable for tree hash calculations) */ size_t tree_hash_cnt(size_t count) { @@ -112,3 +112,98 @@ void tree_hash(const char (*hashes)[HASH_SIZE], size_t count, char *root_hash) { cn_fast_hash(ints[0], 64, root_hash); } } + +/*******************************************************************************************/ +/* Helper functions for merged mining - for merkle tree branch compuation. */ + +size_t tree_depth(size_t count) +{ + size_t i; + size_t depth = 0; + assert(count > 0); + for (i = sizeof(size_t) << 2; i > 0; i >>= 1) + { + if (count >> i > 0) + { + count >>= i; + depth += i; + } + } + return depth; +} + +void tree_branch(const char (*hashes)[HASH_SIZE], size_t count, char (*branch)[HASH_SIZE]) +{ + size_t i, j; + size_t cnt = 1; + size_t depth = 0; + char (*ints)[HASH_SIZE]; + assert(count > 0); + for (i = sizeof(size_t) << 2; i > 0; i >>= 1) + { + if (cnt << i <= count) + { + cnt <<= i; + depth += i; + } + } + assert(cnt == 1ULL << depth); + assert(depth == tree_depth(count)); + ints = alloca((cnt - 1) * HASH_SIZE); + memcpy(ints, hashes + 1, (2 * cnt - count - 1) * HASH_SIZE); + for (i = 2 * cnt - count, j = 2 * cnt - count - 1; j < cnt - 1; i += 2, ++j) + { + cn_fast_hash(hashes[i], 2 * HASH_SIZE, ints[j]); + } + assert(i == count); + while (depth > 0) + { + assert(cnt == 1ULL << depth); + cnt >>= 1; + --depth; + memcpy(branch[depth], ints[0], HASH_SIZE); + for (i = 1, j = 0; j < cnt - 1; i += 2, ++j) + { + cn_fast_hash(ints[i], 2 * HASH_SIZE, ints[j]); + } + } +} + +void tree_hash_from_branch(const char (*branch)[HASH_SIZE], size_t depth, const char* leaf, const void* path, char* root_hash) +{ + if (depth == 0) + { + memcpy(root_hash, leaf, HASH_SIZE); + } + else + { + char buffer[2][HASH_SIZE]; + int from_leaf = 1; + char *leaf_path, *branch_path; + while (depth > 0) + { + --depth; + if (path && (((const char*) path)[depth >> 3] & (1 << (depth & 7))) != 0) + { + leaf_path = buffer[1]; + branch_path = buffer[0]; + } + else + { + leaf_path = buffer[0]; + branch_path = buffer[1]; + } + if (from_leaf) + { + memcpy(leaf_path, leaf, HASH_SIZE); + from_leaf = 0; + } + else + { + cn_fast_hash(buffer, 2 * HASH_SIZE, leaf_path); + } + memcpy(branch_path, branch[depth], HASH_SIZE); + } + cn_fast_hash(buffer, 2 * HASH_SIZE, root_hash); + } +} diff --git a/src/cn_utils/cryptonote_basic/cryptonote_format_utils.cpp b/src/cn_utils/cryptonote_basic/cryptonote_format_utils.cpp index a7bdb0395..149c9d9e9 100644 --- a/src/cn_utils/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cn_utils/cryptonote_basic/cryptonote_format_utils.cpp @@ -618,6 +618,32 @@ namespace cryptonote return find_tx_extra_field_by_type(tx_extra_fields, keva_block); } //--------------------------------------------------------------- + bool append_keva_blockhash_to_extra(std::vector& tx_extra, const tx_extra_keva_blockhash& keva_blockhash) + { + // convert to variant + tx_extra_field field = tx_extra_keva_blockhash{ keva_blockhash }; + // serialize + std::ostringstream oss; + binary_archive ar(oss); + bool r = ::do_serialize(ar, field); + CHECK_AND_NO_ASSERT_MES_L1(r, false, "failed to serialize tx extra keva blockhash"); + // append + std::string tx_extra_str = oss.str(); + size_t pos = tx_extra.size(); + tx_extra.resize(tx_extra.size() + tx_extra_str.size()); + memcpy(&tx_extra[pos], tx_extra_str.data(), tx_extra_str.size()); + return true; + } + //--------------------------------------------------------------- + bool get_keva_blockhash_from_extra(const std::vector& tx_extra, tx_extra_keva_blockhash& keva_blockhash) + { + std::vector tx_extra_fields; + if (!parse_tx_extra(tx_extra, tx_extra_fields)) + return false; + + return find_tx_extra_field_by_type(tx_extra_fields, keva_blockhash); + } + //--------------------------------------------------------------- bool get_inputs_money_amount(const transaction& tx, uint64_t& money) { money = 0; diff --git a/src/cn_utils/cryptonote_basic/cryptonote_format_utils.h b/src/cn_utils/cryptonote_basic/cryptonote_format_utils.h index b943edaee..aa9792d39 100644 --- a/src/cn_utils/cryptonote_basic/cryptonote_format_utils.h +++ b/src/cn_utils/cryptonote_basic/cryptonote_format_utils.h @@ -76,6 +76,8 @@ namespace cryptonote bool add_extra_nonce_to_tx_extra(std::vector& tx_extra, const blobdata& extra_nonce); bool append_keva_block_to_extra(std::vector& tx_extra, const tx_extra_keva_block& keva_block); bool get_keva_block_from_extra(const std::vector& tx_extra, tx_extra_keva_block& keva_block); + bool append_keva_blockhash_to_extra(std::vector& tx_extra, const tx_extra_keva_blockhash& keva_blockhash); + bool get_keva_blockhash_from_extra(const std::vector& tx_extra, tx_extra_keva_blockhash& keva_blockhash); bool remove_field_from_tx_extra(std::vector& tx_extra, const std::type_info &type); void set_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash& payment_id); void set_encrypted_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash8& payment_id); diff --git a/src/cn_utils/cryptonote_basic/tx_extra.h b/src/cn_utils/cryptonote_basic/tx_extra.h index 431ce201c..57789edaa 100644 --- a/src/cn_utils/cryptonote_basic/tx_extra.h +++ b/src/cn_utils/cryptonote_basic/tx_extra.h @@ -38,10 +38,17 @@ #define TX_EXTRA_TAG_PUBKEY 0x01 #define TX_EXTRA_NONCE 0x02 #define TX_EXTRA_MERGE_MINING_TAG 0x03 + +// TX_EXTRA_KEVA_BLOCK_HASH_TAG is exactly the same as +// TX_EXTRA_MERGE_MINING_TAG +#define TX_EXTRA_KEVA_BLOCKHASH_TAG 0x03 + #define TX_EXTRA_TAG_ADDITIONAL_PUBKEYS 0x04 #define TX_EXTRA_MYSTERIOUS_MINERGATE_TAG 0xDE -#define TX_EXTRA_KEVA_BLOCK_TAG 0xa1 +// TX_EXTRA_KEVA_BLOCK_TAG is exactly the same as +// TX_EXTRA_MYSTERIOUS_MINERGATE_TAG +#define TX_EXTRA_KEVA_BLOCK_TAG 0xDE #define TX_EXTRA_NONCE_PAYMENT_ID 0x00 #define TX_EXTRA_NONCE_ENCRYPTED_PAYMENT_ID 0x01 @@ -162,6 +169,8 @@ namespace cryptonote } }; + typedef tx_extra_merge_mining_tag tx_extra_keva_blockhash; + // per-output additional tx pubkey for multi-destination transfers involving at least one subaddress struct tx_extra_additional_pub_keys { @@ -181,21 +190,13 @@ namespace cryptonote END_SERIALIZE() }; - struct tx_extra_keva_block - { - std::string keva_block; - - BEGIN_SERIALIZE() - FIELD(keva_block) - END_SERIALIZE() - }; - + typedef tx_extra_mysterious_minergate tx_extra_keva_block; // tx_extra_field format, except tx_extra_padding and tx_extra_pub_key: // varint tag; // varint size; // varint data[]; - typedef boost::variant tx_extra_field; + typedef boost::variant tx_extra_field; } VARIANT_TAG(binary_archive, cryptonote::tx_extra_padding, TX_EXTRA_TAG_PADDING); @@ -204,4 +205,3 @@ VARIANT_TAG(binary_archive, cryptonote::tx_extra_nonce, TX_EXTRA_NONCE); VARIANT_TAG(binary_archive, cryptonote::tx_extra_merge_mining_tag, TX_EXTRA_MERGE_MINING_TAG); VARIANT_TAG(binary_archive, cryptonote::tx_extra_additional_pub_keys, TX_EXTRA_TAG_ADDITIONAL_PUBKEYS); VARIANT_TAG(binary_archive, cryptonote::tx_extra_mysterious_minergate, TX_EXTRA_MYSTERIOUS_MINERGATE_TAG); -VARIANT_TAG(binary_archive, cryptonote::tx_extra_keva_block, TX_EXTRA_KEVA_BLOCK_TAG); diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp index 0a64b4c7c..3d4623f05 100644 --- a/src/primitives/block.cpp +++ b/src/primitives/block.cpp @@ -14,6 +14,8 @@ extern "C" void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed, uint64_t height); +const uint256 CBlockHeader::DIFFICULTY_1 = uint256S("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + uint256 CBlockHeader::GetHash() const { CHashWriter hashWriter(SER_GETHASH, PROTOCOL_VERSION); @@ -30,13 +32,43 @@ uint256 CBlockHeader::GetPoWHash() const return thash; } - // prev_id of CN header is used to store the kevacoin block hash. - // The value of prev_id and block hash must be the same to prove - // that PoW has been properly done. - if (GetHash() != cnHeader.prev_id) { - memset(thash.begin(), 0xff, thash.size()); + if (!IsAuxpow()) { + // prev_id of CN header is used to store the kevacoin block hash. + // The value of prev_id and block hash must be the same to prove + // that PoW has been properly done. + if (GetHash() != cnHeader.prev_id) { + return DIFFICULTY_1; + } + cryptonote::blobdata blob = cryptonote::t_serializable_object_to_blob(cnHeader); + cn_slow_hash(blob.data(), blob.size(), BEGIN(thash), 2, 0, 0); return thash; } + + // Merged mining. + cryptonote::tx_extra_keva_blockhash keva_blockhash; + if (!cryptonote::get_keva_blockhash_from_extra(cnHeader.aux_pow->miner_tx.extra, keva_blockhash)) { + return DIFFICULTY_1; + } + uint256 actualHash = GetHash(); + if (memcmp(keva_blockhash.merkle_root.data, actualHash.begin(), thash.size()) != 0) { + return DIFFICULTY_1; + } + + crypto::hash miner_tx_hash; + if (!cryptonote::get_transaction_hash(cnHeader.aux_pow->miner_tx, miner_tx_hash)) { + return DIFFICULTY_1; + } + + crypto::hash aux_blocks_merkle_root; + crypto::tree_hash_from_branch( + reinterpret_cast(cnHeader.aux_pow->merkle_branch.data()), + cnHeader.aux_pow->merkle_branch.size(), + reinterpret_cast(&miner_tx_hash), 0, + reinterpret_cast(&aux_blocks_merkle_root)); + + if (memcmp(aux_blocks_merkle_root.data, cnHeader.merkle_root.begin(), cnHeader.merkle_root.size()) != 0) { + return DIFFICULTY_1; + } cryptonote::blobdata blob = cryptonote::t_serializable_object_to_blob(cnHeader); cn_slow_hash(blob.data(), blob.size(), BEGIN(thash), 2, 0, 0); return thash; diff --git a/src/primitives/block.h b/src/primitives/block.h index 588603049..ec7f9100b 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -12,6 +12,56 @@ #include +class CAuxPow +{ +public: + // Cryptnote coinbase tx, which contains the block hash of kevacoin block. + cryptonote::transaction miner_tx; + + // Merkle branch is used to establish that miner_tx is part of the + // merkel tree whose root is merkle_root. + std::vector merkle_branch; + + uint256 CheckMerkleBranch (uint256 hash, + const std::vector& vMerkleBranch, + int nIndex) + { + if (nIndex == -1) { + return uint256(); + } +#if 0 + for (std::vector::const_iterator it(vMerkleBranch.begin()); + it != vMerkleBranch.end(); ++it) { + if (nIndex & 1) { + hash = Hash(BEGIN(*it), END(*it), BEGIN(hash), END(hash)); + } else { + hash = Hash(BEGIN(hash), END(hash), BEGIN(*it), END(*it)); + } + nIndex >>= 1; + } + return hash; +#endif + } + + // load + template