From 917f336641c891b980986f5a912c9ed1ad282e18 Mon Sep 17 00:00:00 2001 From: Piero Orsoni Date: Tue, 20 Mar 2018 18:07:02 +0100 Subject: [PATCH] Fix for parent block transaction with version number 0 --- src/cryptonote_basic/cryptonote_basic.h | 228 +- .../cryptonote_boost_serialization.h | 16 +- .../cryptonote_format_utils.cpp | 2072 ++++++++--------- 3 files changed, 1158 insertions(+), 1158 deletions(-) diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index 69f622fff6..a51aead9e5 100644 --- a/src/cryptonote_basic/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -1,21 +1,21 @@ // Copyright (c) 2014-2017, 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 #pragma once @@ -53,11 +53,11 @@ namespace cryptonote { - struct block; - class transaction; - struct tx_extra_merge_mining_tag; - // Implemented in cryptonote_format_utils.cpp - bool get_transaction_hash(const transaction& t, crypto::hash& res); + struct block; + class transaction; + struct tx_extra_merge_mining_tag; + // Implemented in cryptonote_format_utils.cpp + bool get_transaction_hash(const transaction& t, crypto::hash& res); bool get_mm_tag_from_extra(const std::vector& tx, tx_extra_merge_mining_tag& mm_tag); const static crypto::hash null_hash = AUTO_VAL_INIT(null_hash); @@ -180,7 +180,7 @@ namespace cryptonote BEGIN_SERIALIZE() VARINT_FIELD(version) - if(version == 0 || (CURRENT_TRANSACTION_VERSION < version && !is_mm_tx)) return false; + if((version == 0 || CURRENT_TRANSACTION_VERSION < version) && !is_mm_tx) return false; VARINT_FIELD(unlock_time) FIELD(vin) FIELD(vout) @@ -226,7 +226,7 @@ namespace cryptonote FIELDS(*static_cast(this)) - if (version == 1) + if (version <= 1) { ar.tag("signatures"); ar.begin_array(); @@ -288,7 +288,7 @@ namespace cryptonote { FIELDS(*static_cast(this)) - if (version == 1) + if (version <= 1) { } else @@ -366,101 +366,101 @@ namespace cryptonote /************************************************************************/ const uint8_t CURRENT_BYTECOIN_BLOCK_MAJOR_VERSION = BLOCK_MAJOR_VERSION_3; - struct bytecoin_block - { - uint8_t major_version; - uint8_t minor_version; - crypto::hash prev_id; - uint32_t nonce; - size_t number_of_transactions; - std::vector miner_tx_branch; - transaction miner_tx; - std::vector blockchain_branch; - }; - - struct serializable_bytecoin_block - { - bytecoin_block& b; - uint64_t& timestamp; - bool hashing_serialization; - bool header_only; - - serializable_bytecoin_block(bytecoin_block& b_, uint64_t& timestamp_, bool hashing_serialization_, bool header_only_) : - b(b_), timestamp(timestamp_), hashing_serialization(hashing_serialization_), header_only(header_only_) - { - b.miner_tx.is_mm_tx = true; - } - - BEGIN_SERIALIZE_OBJECT() - VARINT_FIELD_N("major_version", b.major_version); - VARINT_FIELD_N("minor_version", b.minor_version); - VARINT_FIELD(timestamp); - FIELD_N("prev_id", b.prev_id); - FIELD_N("nonce", b.nonce); - - if (hashing_serialization) - { - crypto::hash miner_tx_hash; - if (!get_transaction_hash(b.miner_tx, miner_tx_hash)) { - MERROR("Failed to get transaction hash for miner tx"); - return false; - } - crypto::hash merkle_root; - crypto::tree_hash_from_branch(b.miner_tx_branch.data(), b.miner_tx_branch.size(), miner_tx_hash, 0, merkle_root); - - FIELD(merkle_root); - } - VARINT_FIELD_N("number_of_transactions", b.number_of_transactions); - if (b.number_of_transactions < 1) { - MERROR("Num tx < 1?"); - return false; - } - - if (!header_only) - { - ar.tag("miner_tx_branch"); - ar.begin_array(); - size_t branch_size = crypto::tree_depth(b.number_of_transactions); - PREPARE_CUSTOM_VECTOR_SERIALIZATION(branch_size, const_cast(b).miner_tx_branch); - if (b.miner_tx_branch.size() != branch_size) { - MERROR("Miner tx branch size != branch size"); - return false; - } - for (size_t i = 0; i < branch_size; ++i) - { - FIELDS(b.miner_tx_branch[i]); - if (i + 1 < branch_size) - ar.delimit_array(); - } - ar.end_array(); - - FIELD(b.miner_tx); - - tx_extra_merge_mining_tag mm_tag; - if (!get_mm_tag_from_extra(b.miner_tx.extra, mm_tag)) { - MERROR("Failed to get mm tag from extra."); - return false; - } - - ar.tag("blockchain_branch"); - ar.begin_array(); - PREPARE_CUSTOM_VECTOR_SERIALIZATION(mm_tag.depth, const_cast(b).blockchain_branch); - if (mm_tag.depth != b.blockchain_branch.size()) { - MERROR("Mm tag depth != blockchian_branch size"); - return false; - } - for (size_t i = 0; i < mm_tag.depth; ++i) - { - FIELDS(b.blockchain_branch[i]); - if (i + 1 < mm_tag.depth) - ar.delimit_array(); - } - ar.end_array(); - } - END_SERIALIZE() - }; - - // Implemented below + struct bytecoin_block + { + uint8_t major_version; + uint8_t minor_version; + crypto::hash prev_id; + uint32_t nonce; + size_t number_of_transactions; + std::vector miner_tx_branch; + transaction miner_tx; + std::vector blockchain_branch; + }; + + struct serializable_bytecoin_block + { + bytecoin_block& b; + uint64_t& timestamp; + bool hashing_serialization; + bool header_only; + + serializable_bytecoin_block(bytecoin_block& b_, uint64_t& timestamp_, bool hashing_serialization_, bool header_only_) : + b(b_), timestamp(timestamp_), hashing_serialization(hashing_serialization_), header_only(header_only_) + { + b.miner_tx.is_mm_tx = true; + } + + BEGIN_SERIALIZE_OBJECT() + VARINT_FIELD_N("major_version", b.major_version); + VARINT_FIELD_N("minor_version", b.minor_version); + VARINT_FIELD(timestamp); + FIELD_N("prev_id", b.prev_id); + FIELD_N("nonce", b.nonce); + + if (hashing_serialization) + { + crypto::hash miner_tx_hash; + if (!get_transaction_hash(b.miner_tx, miner_tx_hash)) { + MERROR("Failed to get transaction hash for miner tx"); + return false; + } + crypto::hash merkle_root; + crypto::tree_hash_from_branch(b.miner_tx_branch.data(), b.miner_tx_branch.size(), miner_tx_hash, 0, merkle_root); + + FIELD(merkle_root); + } + VARINT_FIELD_N("number_of_transactions", b.number_of_transactions); + if (b.number_of_transactions < 1) { + MERROR("Num tx < 1?"); + return false; + } + + if (!header_only) + { + ar.tag("miner_tx_branch"); + ar.begin_array(); + size_t branch_size = crypto::tree_depth(b.number_of_transactions); + PREPARE_CUSTOM_VECTOR_SERIALIZATION(branch_size, const_cast(b).miner_tx_branch); + if (b.miner_tx_branch.size() != branch_size) { + MERROR("Miner tx branch size != branch size"); + return false; + } + for (size_t i = 0; i < branch_size; ++i) + { + FIELDS(b.miner_tx_branch[i]); + if (i + 1 < branch_size) + ar.delimit_array(); + } + ar.end_array(); + + FIELD(b.miner_tx); + + tx_extra_merge_mining_tag mm_tag; + if (!get_mm_tag_from_extra(b.miner_tx.extra, mm_tag)) { + MERROR("Failed to get mm tag from extra."); + return false; + } + + ar.tag("blockchain_branch"); + ar.begin_array(); + PREPARE_CUSTOM_VECTOR_SERIALIZATION(mm_tag.depth, const_cast(b).blockchain_branch); + if (mm_tag.depth != b.blockchain_branch.size()) { + MERROR("Mm tag depth != blockchian_branch size"); + return false; + } + for (size_t i = 0; i < mm_tag.depth; ++i) + { + FIELDS(b.blockchain_branch[i]); + if (i + 1 < mm_tag.depth) + ar.delimit_array(); + } + ar.end_array(); + } + END_SERIALIZE() + }; + + // Implemented below inline serializable_bytecoin_block make_serializable_bytecoin_block(const block& b, bool hashing_serialization, bool header_only); struct block_header @@ -520,10 +520,10 @@ namespace cryptonote END_SERIALIZE() }; - inline serializable_bytecoin_block make_serializable_bytecoin_block(const block& b, bool hashing_serialization, bool header_only) - { - block & block_ref = const_cast(b); - return serializable_bytecoin_block(block_ref.parent_block, block_ref.timestamp, hashing_serialization, header_only); + inline serializable_bytecoin_block make_serializable_bytecoin_block(const block& b, bool hashing_serialization, bool header_only) + { + block & block_ref = const_cast(b); + return serializable_bytecoin_block(block_ref.parent_block, block_ref.timestamp, hashing_serialization, header_only); } /************************************************************************/ diff --git a/src/cryptonote_basic/cryptonote_boost_serialization.h b/src/cryptonote_basic/cryptonote_boost_serialization.h index 6e4ac9b724..88c5fb37ec 100644 --- a/src/cryptonote_basic/cryptonote_boost_serialization.h +++ b/src/cryptonote_basic/cryptonote_boost_serialization.h @@ -1,21 +1,21 @@ // Copyright (c) 2014-2017, 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 #pragma once @@ -161,7 +161,7 @@ namespace boost a & x.vin; a & x.vout; a & x.extra; - if (x.version == 1) + if (x.version <= 1) { a & x.signatures; } diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 53c71f91c9..0762473794 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -1,1036 +1,1036 @@ -// Copyright (c) 2014-2017, 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 -// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// 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 "include_base_utils.h" -using namespace epee; - -#include -#include "cryptonote_format_utils.h" -#include "cryptonote_config.h" -#include "crypto/crypto.h" -#include "crypto/hash.h" -#include "ringct/rctSigs.h" -#include "serialization/binary_utils.h" -#include "cryptonote_core/cryptonote_tx_utils.h" -#include "miner.h" - -#undef MONERO_DEFAULT_LOG_CATEGORY -#define MONERO_DEFAULT_LOG_CATEGORY "cn" - -#define ENCRYPTED_PAYMENT_ID_TAIL 0x8d - -// #define ENABLE_HASH_CASH_INTEGRITY_CHECK - -static const uint64_t valid_decomposed_outputs[] = { - (uint64_t)1, (uint64_t)2, (uint64_t)3, (uint64_t)4, (uint64_t)5, (uint64_t)6, (uint64_t)7, (uint64_t)8, (uint64_t)9, // 1 piconero - (uint64_t)10, (uint64_t)20, (uint64_t)30, (uint64_t)40, (uint64_t)50, (uint64_t)60, (uint64_t)70, (uint64_t)80, (uint64_t)90, - (uint64_t)100, (uint64_t)200, (uint64_t)300, (uint64_t)400, (uint64_t)500, (uint64_t)600, (uint64_t)700, (uint64_t)800, (uint64_t)900, - (uint64_t)1000, (uint64_t)2000, (uint64_t)3000, (uint64_t)4000, (uint64_t)5000, (uint64_t)6000, (uint64_t)7000, (uint64_t)8000, (uint64_t)9000, - (uint64_t)10000, (uint64_t)20000, (uint64_t)30000, (uint64_t)40000, (uint64_t)50000, (uint64_t)60000, (uint64_t)70000, (uint64_t)80000, (uint64_t)90000, - (uint64_t)100000, (uint64_t)200000, (uint64_t)300000, (uint64_t)400000, (uint64_t)500000, (uint64_t)600000, (uint64_t)700000, (uint64_t)800000, (uint64_t)900000, - (uint64_t)1000000, (uint64_t)2000000, (uint64_t)3000000, (uint64_t)4000000, (uint64_t)5000000, (uint64_t)6000000, (uint64_t)7000000, (uint64_t)8000000, (uint64_t)9000000, // 1 micronero - (uint64_t)10000000, (uint64_t)20000000, (uint64_t)30000000, (uint64_t)40000000, (uint64_t)50000000, (uint64_t)60000000, (uint64_t)70000000, (uint64_t)80000000, (uint64_t)90000000, - (uint64_t)100000000, (uint64_t)200000000, (uint64_t)300000000, (uint64_t)400000000, (uint64_t)500000000, (uint64_t)600000000, (uint64_t)700000000, (uint64_t)800000000, (uint64_t)900000000, - (uint64_t)1000000000, (uint64_t)2000000000, (uint64_t)3000000000, (uint64_t)4000000000, (uint64_t)5000000000, (uint64_t)6000000000, (uint64_t)7000000000, (uint64_t)8000000000, (uint64_t)9000000000, - (uint64_t)10000000000, (uint64_t)20000000000, (uint64_t)30000000000, (uint64_t)40000000000, (uint64_t)50000000000, (uint64_t)60000000000, (uint64_t)70000000000, (uint64_t)80000000000, (uint64_t)90000000000, - (uint64_t)100000000000, (uint64_t)200000000000, (uint64_t)300000000000, (uint64_t)400000000000, (uint64_t)500000000000, (uint64_t)600000000000, (uint64_t)700000000000, (uint64_t)800000000000, (uint64_t)900000000000, - (uint64_t)1000000000000, (uint64_t)2000000000000, (uint64_t)3000000000000, (uint64_t)4000000000000, (uint64_t)5000000000000, (uint64_t)6000000000000, (uint64_t)7000000000000, (uint64_t)8000000000000, (uint64_t)9000000000000, // 1 monero - (uint64_t)10000000000000, (uint64_t)20000000000000, (uint64_t)30000000000000, (uint64_t)40000000000000, (uint64_t)50000000000000, (uint64_t)60000000000000, (uint64_t)70000000000000, (uint64_t)80000000000000, (uint64_t)90000000000000, - (uint64_t)100000000000000, (uint64_t)200000000000000, (uint64_t)300000000000000, (uint64_t)400000000000000, (uint64_t)500000000000000, (uint64_t)600000000000000, (uint64_t)700000000000000, (uint64_t)800000000000000, (uint64_t)900000000000000, - (uint64_t)1000000000000000, (uint64_t)2000000000000000, (uint64_t)3000000000000000, (uint64_t)4000000000000000, (uint64_t)5000000000000000, (uint64_t)6000000000000000, (uint64_t)7000000000000000, (uint64_t)8000000000000000, (uint64_t)9000000000000000, - (uint64_t)10000000000000000, (uint64_t)20000000000000000, (uint64_t)30000000000000000, (uint64_t)40000000000000000, (uint64_t)50000000000000000, (uint64_t)60000000000000000, (uint64_t)70000000000000000, (uint64_t)80000000000000000, (uint64_t)90000000000000000, - (uint64_t)100000000000000000, (uint64_t)200000000000000000, (uint64_t)300000000000000000, (uint64_t)400000000000000000, (uint64_t)500000000000000000, (uint64_t)600000000000000000, (uint64_t)700000000000000000, (uint64_t)800000000000000000, (uint64_t)900000000000000000, - (uint64_t)1000000000000000000, (uint64_t)2000000000000000000, (uint64_t)3000000000000000000, (uint64_t)4000000000000000000, (uint64_t)5000000000000000000, (uint64_t)6000000000000000000, (uint64_t)7000000000000000000, (uint64_t)8000000000000000000, (uint64_t)9000000000000000000, // 1 meganero - (uint64_t)10000000000000000000ull -}; - -static std::atomic default_decimal_point(CRYPTONOTE_DISPLAY_DECIMAL_POINT); - -static std::atomic tx_hashes_calculated_count(0); -static std::atomic tx_hashes_cached_count(0); -static std::atomic block_hashes_calculated_count(0); -static std::atomic block_hashes_cached_count(0); - -namespace cryptonote -{ - //--------------------------------------------------------------- - void get_transaction_prefix_hash(const transaction_prefix& tx, crypto::hash& h) - { - std::ostringstream s; - binary_archive a(s); - ::serialization::serialize(a, const_cast(tx)); - crypto::cn_fast_hash(s.str().data(), s.str().size(), h); - } - //--------------------------------------------------------------- - crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx) - { - crypto::hash h = null_hash; - get_transaction_prefix_hash(tx, h); - return h; - } - //--------------------------------------------------------------- - bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx) - { - std::stringstream ss; - ss << tx_blob; - binary_archive ba(ss); - bool r = ::serialization::serialize(ba, tx); - CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob"); - tx.invalidate_hashes(); - return true; - } - //--------------------------------------------------------------- - bool parse_and_validate_tx_base_from_blob(const blobdata& tx_blob, transaction& tx) - { - std::stringstream ss; - ss << tx_blob; - binary_archive ba(ss); - bool r = tx.serialize_base(ba); - CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob"); - return true; - } - //--------------------------------------------------------------- - bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash) - { - std::stringstream ss; - ss << tx_blob; - binary_archive ba(ss); - bool r = ::serialization::serialize(ba, tx); - CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob"); - tx.invalidate_hashes(); - //TODO: validate tx - - get_transaction_hash(tx, tx_hash); - get_transaction_prefix_hash(tx, tx_prefix_hash); - return true; - } - //--------------------------------------------------------------- - bool generate_key_image_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki) - { - crypto::key_derivation recv_derivation = AUTO_VAL_INIT(recv_derivation); - bool r = crypto::generate_key_derivation(tx_public_key, ack.m_view_secret_key, recv_derivation); - CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to generate_key_derivation(" << tx_public_key << ", " << ack.m_view_secret_key << ")"); - - r = crypto::derive_public_key(recv_derivation, real_output_index, ack.m_account_address.m_spend_public_key, in_ephemeral.pub); - CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to derive_public_key(" << recv_derivation << ", " << real_output_index << ", " << ack.m_account_address.m_spend_public_key << ")"); - - crypto::derive_secret_key(recv_derivation, real_output_index, ack.m_spend_secret_key, in_ephemeral.sec); - - crypto::generate_key_image(in_ephemeral.pub, in_ephemeral.sec, ki); - return true; - } - //--------------------------------------------------------------- - uint64_t power_integral(uint64_t a, uint64_t b) - { - if(b == 0) - return 1; - uint64_t total = a; - for(uint64_t i = 1; i != b; i++) - total *= a; - return total; - } - //--------------------------------------------------------------- - bool parse_amount(uint64_t& amount, const std::string& str_amount_) - { - std::string str_amount = str_amount_; - boost::algorithm::trim(str_amount); - - size_t point_index = str_amount.find_first_of('.'); - size_t fraction_size; - if (std::string::npos != point_index) - { - fraction_size = str_amount.size() - point_index - 1; - while (default_decimal_point < fraction_size && '0' == str_amount.back()) - { - str_amount.erase(str_amount.size() - 1, 1); - --fraction_size; - } - if (default_decimal_point < fraction_size) - return false; - str_amount.erase(point_index, 1); - } - else - { - fraction_size = 0; - } - - if (str_amount.empty()) - return false; - - if (fraction_size < default_decimal_point) - { - str_amount.append(default_decimal_point - fraction_size, '0'); - } - - return string_tools::get_xtype_from_string(amount, str_amount); - } - //--------------------------------------------------------------- - bool get_tx_fee(const transaction& tx, uint64_t & fee) - { - if (tx.version > 1) - { - fee = tx.rct_signatures.txnFee; - return true; - } - uint64_t amount_in = 0; - uint64_t amount_out = 0; - for(auto& in: tx.vin) - { - CHECK_AND_ASSERT_MES(in.type() == typeid(txin_to_key), 0, "unexpected type id in transaction"); - amount_in += boost::get(in).amount; - } - for(auto& o: tx.vout) - amount_out += o.amount; - - CHECK_AND_ASSERT_MES(amount_in >= amount_out, false, "transaction spend (" <& tx_extra, std::vector& tx_extra_fields) - { - tx_extra_fields.clear(); - - if(tx_extra.empty()) - return true; - - std::string extra_str(reinterpret_cast(tx_extra.data()), tx_extra.size()); - std::istringstream iss(extra_str); - binary_archive ar(iss); - - bool eof = false; - while (!eof) - { - tx_extra_field field; - bool r = ::do_serialize(ar, field); - CHECK_AND_NO_ASSERT_MES_L1(r, false, "failed to deserialize extra field. extra = " << string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast(tx_extra.data()), tx_extra.size()))); - tx_extra_fields.push_back(field); - - std::ios_base::iostate state = iss.rdstate(); - eof = (EOF == iss.peek()); - iss.clear(state); - } - CHECK_AND_NO_ASSERT_MES_L1(::serialization::check_stream_state(ar), false, "failed to deserialize extra field. extra = " << string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast(tx_extra.data()), tx_extra.size()))); - - return true; - } - //--------------------------------------------------------------- - crypto::public_key get_tx_pub_key_from_extra(const std::vector& tx_extra, size_t pk_index) - { - std::vector tx_extra_fields; - parse_tx_extra(tx_extra, tx_extra_fields); - - tx_extra_pub_key pub_key_field; - if(!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, pk_index)) - return null_pkey; - - return pub_key_field.pub_key; - } - //--------------------------------------------------------------- - crypto::public_key get_tx_pub_key_from_extra(const transaction_prefix& tx_prefix, size_t pk_index) - { - return get_tx_pub_key_from_extra(tx_prefix.extra, pk_index); - } - //--------------------------------------------------------------- - crypto::public_key get_tx_pub_key_from_extra(const transaction& tx, size_t pk_index) - { - return get_tx_pub_key_from_extra(tx.extra, pk_index); - } - //--------------------------------------------------------------- - bool add_tx_pub_key_to_extra(transaction& tx, const crypto::public_key& tx_pub_key) - { - tx.extra.resize(tx.extra.size() + 1 + sizeof(crypto::public_key)); - tx.extra[tx.extra.size() - 1 - sizeof(crypto::public_key)] = TX_EXTRA_TAG_PUBKEY; - *reinterpret_cast(&tx.extra[tx.extra.size() - sizeof(crypto::public_key)]) = tx_pub_key; - return true; - } - //--------------------------------------------------------------- - bool add_extra_nonce_to_tx_extra(std::vector& tx_extra, const blobdata& extra_nonce) - { - CHECK_AND_ASSERT_MES(extra_nonce.size() <= TX_EXTRA_NONCE_MAX_COUNT, false, "extra nonce could be 255 bytes max"); - size_t start_pos = tx_extra.size(); - tx_extra.resize(tx_extra.size() + 2 + extra_nonce.size()); - //write tag - tx_extra[start_pos] = TX_EXTRA_NONCE; - //write len - ++start_pos; - tx_extra[start_pos] = static_cast(extra_nonce.size()); - //write data - ++start_pos; - memcpy(&tx_extra[start_pos], extra_nonce.data(), extra_nonce.size()); - return true; - } - //--------------------------------------------------------------- - bool remove_field_from_tx_extra(std::vector& tx_extra, const std::type_info &type) - { - if (tx_extra.empty()) - return true; - std::string extra_str(reinterpret_cast(tx_extra.data()), tx_extra.size()); - std::istringstream iss(extra_str); - binary_archive ar(iss); - std::ostringstream oss; - binary_archive newar(oss); - - bool eof = false; - while (!eof) - { - tx_extra_field field; - bool r = ::do_serialize(ar, field); - CHECK_AND_NO_ASSERT_MES_L1(r, false, "failed to deserialize extra field. extra = " << string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast(tx_extra.data()), tx_extra.size()))); - if (field.type() != type) - ::do_serialize(newar, field); - - std::ios_base::iostate state = iss.rdstate(); - eof = (EOF == iss.peek()); - iss.clear(state); - } - CHECK_AND_NO_ASSERT_MES_L1(::serialization::check_stream_state(ar), false, "failed to deserialize extra field. extra = " << string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast(tx_extra.data()), tx_extra.size()))); - tx_extra.clear(); - std::string s = oss.str(); - tx_extra.reserve(s.size()); - std::copy(s.begin(), s.end(), std::back_inserter(tx_extra)); - return true; - } - //--------------------------------------------------------------- - bool append_mm_tag_to_extra(std::vector& tx_extra, const tx_extra_merge_mining_tag& mm_tag) - { - blobdata blob; - if (!t_serializable_object_to_blob(mm_tag, blob)) - return false; - - tx_extra.push_back(TX_EXTRA_MERGE_MINING_TAG); - std::copy(reinterpret_cast(blob.data()), reinterpret_cast(blob.data() + blob.size()), std::back_inserter(tx_extra)); - return true; - } - //--------------------------------------------------------------- - bool get_mm_tag_from_extra(const std::vector& tx_extra, tx_extra_merge_mining_tag& mm_tag) - { - 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, mm_tag); - } - //--------------------------------------------------------------- - void set_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash& payment_id) - { - extra_nonce.clear(); - extra_nonce.push_back(TX_EXTRA_NONCE_PAYMENT_ID); - const uint8_t* payment_id_ptr = reinterpret_cast(&payment_id); - std::copy(payment_id_ptr, payment_id_ptr + sizeof(payment_id), std::back_inserter(extra_nonce)); - } - //--------------------------------------------------------------- - void set_encrypted_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash8& payment_id) - { - extra_nonce.clear(); - extra_nonce.push_back(TX_EXTRA_NONCE_ENCRYPTED_PAYMENT_ID); - const uint8_t* payment_id_ptr = reinterpret_cast(&payment_id); - std::copy(payment_id_ptr, payment_id_ptr + sizeof(payment_id), std::back_inserter(extra_nonce)); - } - //--------------------------------------------------------------- - bool get_payment_id_from_tx_extra_nonce(const blobdata& extra_nonce, crypto::hash& payment_id) - { - if(sizeof(crypto::hash) + 1 != extra_nonce.size()) - return false; - if(TX_EXTRA_NONCE_PAYMENT_ID != extra_nonce[0]) - return false; - payment_id = *reinterpret_cast(extra_nonce.data() + 1); - return true; - } - //--------------------------------------------------------------- - bool get_encrypted_payment_id_from_tx_extra_nonce(const blobdata& extra_nonce, crypto::hash8& payment_id) - { - if(sizeof(crypto::hash8) + 1 != extra_nonce.size()) - return false; - if (TX_EXTRA_NONCE_ENCRYPTED_PAYMENT_ID != extra_nonce[0]) - return false; - payment_id = *reinterpret_cast(extra_nonce.data() + 1); - return true; - } - //--------------------------------------------------------------- - bool encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key) - { - crypto::key_derivation derivation; - crypto::hash hash; - char data[33]; /* A hash, and an extra byte */ - - if (!generate_key_derivation(public_key, secret_key, derivation)) - return false; - - memcpy(data, &derivation, 32); - data[32] = ENCRYPTED_PAYMENT_ID_TAIL; - cn_fast_hash(data, 33, hash); - - for (size_t b = 0; b < 8; ++b) - payment_id.data[b] ^= hash.data[b]; - - return true; - } - bool decrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key) - { - // Encryption and decryption are the same operation (xor with a key) - return encrypt_payment_id(payment_id, public_key, secret_key); - } - //--------------------------------------------------------------- - bool get_inputs_money_amount(const transaction& tx, uint64_t& money) - { - money = 0; - for(const auto& in: tx.vin) - { - CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, false); - money += tokey_in.amount; - } - return true; - } - //--------------------------------------------------------------- - uint64_t get_block_height(const block& b) - { - CHECK_AND_ASSERT_MES(b.miner_tx.vin.size() == 1, 0, "wrong miner tx in block: " << get_block_hash(b) << ", b.miner_tx.vin.size() != 1"); - CHECKED_GET_SPECIFIC_VARIANT(b.miner_tx.vin[0], const txin_gen, coinbase_in, 0); - return coinbase_in.height; - } - //--------------------------------------------------------------- - bool check_inputs_types_supported(const transaction& tx) - { - for(const auto& in: tx.vin) - { - CHECK_AND_ASSERT_MES(in.type() == typeid(txin_to_key), false, "wrong variant type: " - << in.type().name() << ", expected " << typeid(txin_to_key).name() - << ", in transaction id=" << get_transaction_hash(tx)); - - } - return true; - } - //----------------------------------------------------------------------------------------------- - bool check_outs_valid(const transaction& tx) - { - for(const tx_out& out: tx.vout) - { - CHECK_AND_ASSERT_MES(out.target.type() == typeid(txout_to_key), false, "wrong variant type: " - << out.target.type().name() << ", expected " << typeid(txout_to_key).name() - << ", in transaction id=" << get_transaction_hash(tx)); - - if (tx.version == 1) - { - CHECK_AND_NO_ASSERT_MES(0 < out.amount, false, "zero amount output in transaction id=" << get_transaction_hash(tx)); - } - - if(!check_key(boost::get(out.target).key)) - return false; - } - return true; - } - //----------------------------------------------------------------------------------------------- - bool check_money_overflow(const transaction& tx) - { - return check_inputs_overflow(tx) && check_outs_overflow(tx); - } - //--------------------------------------------------------------- - bool check_inputs_overflow(const transaction& tx) - { - uint64_t money = 0; - for(const auto& in: tx.vin) - { - CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, false); - if(money > tokey_in.amount + money) - return false; - money += tokey_in.amount; - } - return true; - } - //--------------------------------------------------------------- - bool check_outs_overflow(const transaction& tx) - { - uint64_t money = 0; - for(const auto& o: tx.vout) - { - if(money > o.amount + money) - return false; - money += o.amount; - } - return true; - } - //--------------------------------------------------------------- - uint64_t get_outs_money_amount(const transaction& tx) - { - uint64_t outputs_amount = 0; - for(const auto& o: tx.vout) - outputs_amount += o.amount; - return outputs_amount; - } - //--------------------------------------------------------------- - std::string short_hash_str(const crypto::hash& h) - { - std::string res = string_tools::pod_to_hex(h); - CHECK_AND_ASSERT_MES(res.size() == 64, res, "wrong hash256 with string_tools::pod_to_hex conversion"); - auto erased_pos = res.erase(8, 48); - res.insert(8, "...."); - return res; - } - //--------------------------------------------------------------- - bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::public_key& tx_pub_key, size_t output_index) - { - crypto::key_derivation derivation; - generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation); - crypto::public_key pk; - derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk); - return pk == out_key.key; - } - //--------------------------------------------------------------- - bool is_out_to_acc_precomp(const crypto::public_key& spend_public_key, const txout_to_key& out_key, const crypto::key_derivation& derivation, size_t output_index) - { - crypto::public_key pk; - derive_public_key(derivation, output_index, spend_public_key, pk); - return pk == out_key.key; - } - //--------------------------------------------------------------- - bool lookup_acc_outs(const account_keys& acc, const transaction& tx, std::vector& outs, uint64_t& money_transfered) - { - crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx); - if(null_pkey == tx_pub_key) - return false; - return lookup_acc_outs(acc, tx, tx_pub_key, outs, money_transfered); - } - //--------------------------------------------------------------- - bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, std::vector& outs, uint64_t& money_transfered) - { - money_transfered = 0; - size_t i = 0; - for(const tx_out& o: tx.vout) - { - CHECK_AND_ASSERT_MES(o.target.type() == typeid(txout_to_key), false, "wrong type id in transaction out" ); - if(is_out_to_acc(acc, boost::get(o.target), tx_pub_key, i)) - { - outs.push_back(i); - money_transfered += o.amount; - } - i++; - } - return true; - } - //--------------------------------------------------------------- - void get_blob_hash(const blobdata& blob, crypto::hash& res) - { - cn_fast_hash(blob.data(), blob.size(), res); - } - //--------------------------------------------------------------- - void set_default_decimal_point(unsigned int decimal_point) - { - switch (decimal_point) - { - case 8: - case 6: - case 3: - case 0: - default_decimal_point = decimal_point; - break; - default: - ASSERT_MES_AND_THROW("Invalid decimal point specification: " << decimal_point); - } - } - //--------------------------------------------------------------- - unsigned int get_default_decimal_point() - { - return default_decimal_point; - } - //--------------------------------------------------------------- - std::string get_unit(unsigned int decimal_point) - { - if (decimal_point == (unsigned int)-1) - decimal_point = default_decimal_point; - switch (std::atomic_load(&default_decimal_point)) - { - case 12: - return "monero"; - case 9: - return "millinero"; - case 6: - return "micronero"; - case 3: - return "nanonero"; - case 0: - return "piconero"; - default: - ASSERT_MES_AND_THROW("Invalid decimal point specification: " << default_decimal_point); - } - } - //--------------------------------------------------------------- - std::string print_money(uint64_t amount, unsigned int decimal_point) - { - if (decimal_point == (unsigned int)-1) - decimal_point = default_decimal_point; - std::string s = std::to_string(amount); - if(s.size() < decimal_point+1) - { - s.insert(0, decimal_point+1 - s.size(), '0'); - } - if (decimal_point > 0) - s.insert(s.size() - decimal_point, "."); - return s; - } - //--------------------------------------------------------------- - crypto::hash get_blob_hash(const blobdata& blob) - { - crypto::hash h = null_hash; - get_blob_hash(blob, h); - return h; - } - //--------------------------------------------------------------- - crypto::hash get_transaction_hash(const transaction& t) - { - crypto::hash h = null_hash; - get_transaction_hash(t, h, NULL); - return h; - } - //--------------------------------------------------------------- - bool get_transaction_hash(const transaction& t, crypto::hash& res) - { - return get_transaction_hash(t, res, NULL); - } - //--------------------------------------------------------------- - bool calculate_transaction_hash(const transaction& t, crypto::hash& res, size_t* blob_size) - { - // v1 transactions hash the entire blob - if (t.version == 1) - { - size_t ignored_blob_size, &blob_size_ref = blob_size ? *blob_size : ignored_blob_size; - return get_object_hash(t, res, blob_size_ref); - } - - // v2 transactions hash different parts together, than hash the set of those hashes - crypto::hash hashes[3]; - - // prefix - get_transaction_prefix_hash(t, hashes[0]); - - transaction &tt = const_cast(t); - - // base rct - { - std::stringstream ss; - binary_archive ba(ss); - const size_t inputs = t.vin.size(); - const size_t outputs = t.vout.size(); - bool r = tt.rct_signatures.serialize_rctsig_base(ba, inputs, outputs); - CHECK_AND_ASSERT_MES(r, false, "Failed to serialize rct signatures base"); - cryptonote::get_blob_hash(ss.str(), hashes[1]); - } - - // prunable rct - if (t.rct_signatures.type == rct::RCTTypeNull) - { - hashes[2] = cryptonote::null_hash; - } - else - { - std::stringstream ss; - binary_archive ba(ss); - const size_t inputs = t.vin.size(); - const size_t outputs = t.vout.size(); - const size_t mixin = t.vin.empty() ? 0 : t.vin[0].type() == typeid(txin_to_key) ? boost::get(t.vin[0]).key_offsets.size() - 1 : 0; - bool r = tt.rct_signatures.p.serialize_rctsig_prunable(ba, t.rct_signatures.type, inputs, outputs, mixin); - CHECK_AND_ASSERT_MES(r, false, "Failed to serialize rct signatures prunable"); - cryptonote::get_blob_hash(ss.str(), hashes[2]); - } - - // the tx hash is the hash of the 3 hashes - res = cn_fast_hash(hashes, sizeof(hashes)); - - // we still need the size - if (blob_size) - *blob_size = get_object_blobsize(t); - - return true; - } - //--------------------------------------------------------------- - bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t* blob_size) - { - if (t.is_hash_valid()) - { -#ifdef ENABLE_HASH_CASH_INTEGRITY_CHECK - CHECK_AND_ASSERT_THROW_MES(!calculate_transaction_hash(t, res, blob_size) || t.hash == res, "tx hash cash integrity failure"); -#endif - res = t.hash; - if (blob_size) - { - if (!t.is_blob_size_valid()) - { - t.blob_size = get_object_blobsize(t); - t.set_blob_size_valid(true); - } - *blob_size = t.blob_size; - } - ++tx_hashes_cached_count; - return true; - } - ++tx_hashes_calculated_count; - bool ret = calculate_transaction_hash(t, res, blob_size); - if (!ret) - return false; - t.hash = res; - t.set_hash_valid(true); - if (blob_size) - { - t.blob_size = *blob_size; - t.set_blob_size_valid(true); - } - return true; - } - //--------------------------------------------------------------- - bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t& blob_size) - { - return get_transaction_hash(t, res, &blob_size); - } - //--------------------------------------------------------------- - blobdata get_block_hashing_blob(const block& b) - { - blobdata blob = t_serializable_object_to_blob(static_cast(b)); - crypto::hash tree_root_hash = get_tx_tree_hash(b); - blob.append(reinterpret_cast(&tree_root_hash), sizeof(tree_root_hash)); - blob.append(tools::get_varint_data(b.tx_hashes.size() + 1)); - return blob; - } - //--------------------------------------------------------------- - bool get_block_hashing_blob(const block& b, blobdata& blob) - { - blob = t_serializable_object_to_blob(static_cast(b)); - crypto::hash tree_root_hash = get_tx_tree_hash(b); - blob.append(reinterpret_cast(&tree_root_hash), sizeof(tree_root_hash)); - blob.append(tools::get_varint_data(b.tx_hashes.size()+1)); - return true; - } - //--------------------------------------------------------------- - bool get_bytecoin_block_hashing_blob(const block& b, blobdata& blob) - { - auto sbb = make_serializable_bytecoin_block(b, true, true); - return t_serializable_object_to_blob(sbb, blob); - } - //--------------------------------------------------------------- - bool calculate_block_hash(const block& b, crypto::hash& res) - { - get_blob_hash(block_to_blob(b)); - bool hash_result = get_object_hash(get_block_hashing_blob(b), res); - return hash_result; - } - //--------------------------------------------------------------- - bool get_block_hash(const block& b, crypto::hash& res) - { - if (b.is_hash_valid()) - { -#ifdef ENABLE_HASH_CASH_INTEGRITY_CHECK - CHECK_AND_ASSERT_THROW_MES(!calculate_block_hash(b, res) || b.hash == res, "block hash cash integrity failure"); -#endif - res = b.hash; - ++block_hashes_cached_count; - return true; - } - ++block_hashes_calculated_count; - - blobdata blob; - if (!get_block_hashing_blob(b, blob)) - return false; - - if (BLOCK_MAJOR_VERSION_2 <= b.major_version) - { - blobdata parent_blob; - auto sbb = make_serializable_bytecoin_block(b, true, false); - if (!t_serializable_object_to_blob(sbb, parent_blob)) - return false; - - blob.append(parent_blob); - } - - bool ret = get_object_hash(blob, res); - if (!ret) - return false; - b.hash = res; - b.set_hash_valid(true); - return true; - } - //--------------------------------------------------------------- - bool generate_genesis_block(block& bl) - { - //genesis block - bl = boost::value_initialized(); - - - account_public_address ac = boost::value_initialized(); - std::vector sz; - cryptonote::construct_miner_tx(0, 0, 0, 0, 0, ac, bl.miner_tx); // zero fee in genesis - blobdata txb = tx_to_blob(bl.miner_tx); - std::string hex_tx_represent = string_tools::buff_to_hex_nodelimer(txb); - - //hard code coinbase tx in genesis block, because "tru" generating tx use random, but genesis should be always the same - std::string genesis_coinbase_tx_hex = config::GENESIS_TX; - - blobdata tx_bl; - string_tools::parse_hexstr_to_binbuff(genesis_coinbase_tx_hex, tx_bl); - bool r = parse_and_validate_tx_from_blob(tx_bl, bl.miner_tx); - CHECK_AND_ASSERT_MES(r, false, "failed to parse coinbase tx from hard coded blob"); - bl.major_version = CURRENT_BLOCK_MAJOR_VERSION; - bl.minor_version = CURRENT_BLOCK_MINOR_VERSION; - bl.timestamp = 0; - bl.nonce = config::GENESIS_NONCE; - miner::find_nonce_for_given_block(bl, 1, 0); - return true; - } - //--------------------------------------------------------------- - bool get_genesis_block_hash(crypto::hash& h) - { - static std::atomic cached(false); - static crypto::hash genesis_block_hash; - if (!cached) - { - static std::mutex m; - std::unique_lock lock(m); - if (!cached) - { - block genesis_block; - if (!generate_genesis_block(genesis_block)) - return false; - - if (!get_block_hash(genesis_block, genesis_block_hash)) - return false; - - cached = true; - } - } - - h = genesis_block_hash; - return true; - } - //--------------------------------------------------------------- - bool get_block_header_hash(const block& b, crypto::hash& res) - { - blobdata blob; - if (!get_block_hashing_blob(b, blob)) - return false; - - return get_object_hash(blob, res); - } - //--------------------------------------------------------------- - crypto::hash get_block_hash(const block& b) - { - crypto::hash p = null_hash; - get_block_hash(b, p); - return p; - } - //--------------------------------------------------------------- - bool get_block_longhash(const block& b, crypto::hash& res, uint64_t height) - { - blobdata bd = get_block_hashing_blob(b); - crypto::cn_slow_hash(bd.data(), bd.size(), res); - return true; - } - //--------------------------------------------------------------- - bool get_bytecoin_block_longhash(const block& b, crypto::hash& res) - { - blobdata bd; - if (!get_bytecoin_block_hashing_blob(b, bd)) - return false; - crypto::cn_slow_hash(bd.data(), bd.size(), res); - return true; - } - //--------------------------------------------------------------- - bool check_proof_of_work_v1(const block& bl, difficulty_type current_diffic, crypto::hash& proof_of_work) - { - if (BLOCK_MAJOR_VERSION_1 != bl.major_version) - return false; - - proof_of_work = get_block_longhash(bl, 0); - return check_hash(proof_of_work, current_diffic); - } - //--------------------------------------------------------------- - bool check_proof_of_work_v2(const block& bl, difficulty_type current_diffic, crypto::hash& proof_of_work) - { - MDEBUG("Checking POW V2 - diff " << current_diffic); - if (bl.major_version < BLOCK_MAJOR_VERSION_2) - return false; - - if (!get_bytecoin_block_longhash(bl, proof_of_work)) { - MDEBUG("Failed to get bytecoin block longhash"); - return false; - } - if (!check_hash(proof_of_work, current_diffic)) { - MDEBUG("Failed to check hash for pow"); - return false; - } - - tx_extra_merge_mining_tag mm_tag; - if (!get_mm_tag_from_extra(bl.parent_block.miner_tx.extra, mm_tag)) - { - LOG_ERROR("merge mining tag wasn't found in extra of the parent block miner transaction"); - return false; - } - - crypto::hash genesis_block_hash; - if (!get_genesis_block_hash(genesis_block_hash)) { - MDEBUG("Failed to get genesis block hash"); - return false; - } - - if (8 * sizeof(genesis_block_hash) < bl.parent_block.blockchain_branch.size()) { - MDEBUG("Failed genesis block and parent block branch size comparison"); - return false; - } - - crypto::hash aux_block_header_hash; - if (!get_block_header_hash(bl, aux_block_header_hash)) { - MDEBUG("Failed to get aux header hash"); - return false; - } - - crypto::hash aux_blocks_merkle_root; - crypto::tree_hash_from_branch(bl.parent_block.blockchain_branch.data(), bl.parent_block.blockchain_branch.size(), - aux_block_header_hash, &genesis_block_hash, aux_blocks_merkle_root); - CHECK_AND_NO_ASSERT_MES(aux_blocks_merkle_root == mm_tag.merkle_root, false, "Aux block hash wasn't found in merkle tree"); - - return true; - } - //--------------------------------------------------------------- - bool check_proof_of_work(const block& bl, difficulty_type current_diffic, crypto::hash& proof_of_work) - { - switch (bl.major_version) - { - case BLOCK_MAJOR_VERSION_1: return check_proof_of_work_v1(bl, current_diffic, proof_of_work); - case BLOCK_MAJOR_VERSION_2: - case BLOCK_MAJOR_VERSION_3: - return check_proof_of_work_v2(bl, current_diffic, proof_of_work); - } - - CHECK_AND_ASSERT_MES(false, false, "unknown block major version: " << bl.major_version << "." << bl.minor_version); - } - //--------------------------------------------------------------- - std::vector relative_output_offsets_to_absolute(const std::vector& off) - { - std::vector res = off; - for(size_t i = 1; i < res.size(); i++) - res[i] += res[i-1]; - return res; - } - //--------------------------------------------------------------- - std::vector absolute_output_offsets_to_relative(const std::vector& off) - { - std::vector res = off; - if(!off.size()) - return res; - std::sort(res.begin(), res.end());//just to be sure, actually it is already should be sorted - for(size_t i = res.size()-1; i != 0; i--) - res[i] -= res[i-1]; - - return res; - } - //--------------------------------------------------------------- - crypto::hash get_block_longhash(const block& b, uint64_t height) - { - crypto::hash p = null_hash; - get_block_longhash(b, p, height); - return p; - } - //--------------------------------------------------------------- - bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b) - { - std::stringstream ss; - ss << b_blob; - binary_archive ba(ss); - bool r = ::serialization::serialize(ba, b); - CHECK_AND_ASSERT_MES(r, false, "Failed to parse block from blob"); - b.invalidate_hashes(); - b.miner_tx.invalidate_hashes(); - return true; - } - //--------------------------------------------------------------- - blobdata block_to_blob(const block& b) - { - return t_serializable_object_to_blob(b); - } - //--------------------------------------------------------------- - bool block_to_blob(const block& b, blobdata& b_blob) - { - return t_serializable_object_to_blob(b, b_blob); - } - //--------------------------------------------------------------- - blobdata tx_to_blob(const transaction& tx) - { - return t_serializable_object_to_blob(tx); - } - //--------------------------------------------------------------- - bool tx_to_blob(const transaction& tx, blobdata& b_blob) - { - return t_serializable_object_to_blob(tx, b_blob); - } - //--------------------------------------------------------------- - void get_tx_tree_hash(const std::vector& tx_hashes, crypto::hash& h) - { - tree_hash(tx_hashes.data(), tx_hashes.size(), h); - } - //--------------------------------------------------------------- - crypto::hash get_tx_tree_hash(const std::vector& tx_hashes) - { - crypto::hash h = null_hash; - get_tx_tree_hash(tx_hashes, h); - return h; - } - //--------------------------------------------------------------- - crypto::hash get_tx_tree_hash(const block& b) - { - std::vector txs_ids; - crypto::hash h = null_hash; - size_t bl_sz = 0; - get_transaction_hash(b.miner_tx, h, bl_sz); - txs_ids.push_back(h); - for(auto& th: b.tx_hashes) - txs_ids.push_back(th); - return get_tx_tree_hash(txs_ids); - } - //--------------------------------------------------------------- - bool is_valid_decomposed_amount(uint64_t amount) - { - const uint64_t *begin = valid_decomposed_outputs; - const uint64_t *end = valid_decomposed_outputs + sizeof(valid_decomposed_outputs) / sizeof(valid_decomposed_outputs[0]); - return std::binary_search(begin, end, amount); - } - //--------------------------------------------------------------- - void get_hash_stats(uint64_t &tx_hashes_calculated, uint64_t &tx_hashes_cached, uint64_t &block_hashes_calculated, uint64_t & block_hashes_cached) - { - tx_hashes_calculated = tx_hashes_calculated_count; - tx_hashes_cached = tx_hashes_cached_count; - block_hashes_calculated = block_hashes_calculated_count; - block_hashes_cached = block_hashes_cached_count; - } -} +// Copyright (c) 2014-2017, 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 +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// 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 "include_base_utils.h" +using namespace epee; + +#include +#include "cryptonote_format_utils.h" +#include "cryptonote_config.h" +#include "crypto/crypto.h" +#include "crypto/hash.h" +#include "ringct/rctSigs.h" +#include "serialization/binary_utils.h" +#include "cryptonote_core/cryptonote_tx_utils.h" +#include "miner.h" + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "cn" + +#define ENCRYPTED_PAYMENT_ID_TAIL 0x8d + +// #define ENABLE_HASH_CASH_INTEGRITY_CHECK + +static const uint64_t valid_decomposed_outputs[] = { + (uint64_t)1, (uint64_t)2, (uint64_t)3, (uint64_t)4, (uint64_t)5, (uint64_t)6, (uint64_t)7, (uint64_t)8, (uint64_t)9, // 1 piconero + (uint64_t)10, (uint64_t)20, (uint64_t)30, (uint64_t)40, (uint64_t)50, (uint64_t)60, (uint64_t)70, (uint64_t)80, (uint64_t)90, + (uint64_t)100, (uint64_t)200, (uint64_t)300, (uint64_t)400, (uint64_t)500, (uint64_t)600, (uint64_t)700, (uint64_t)800, (uint64_t)900, + (uint64_t)1000, (uint64_t)2000, (uint64_t)3000, (uint64_t)4000, (uint64_t)5000, (uint64_t)6000, (uint64_t)7000, (uint64_t)8000, (uint64_t)9000, + (uint64_t)10000, (uint64_t)20000, (uint64_t)30000, (uint64_t)40000, (uint64_t)50000, (uint64_t)60000, (uint64_t)70000, (uint64_t)80000, (uint64_t)90000, + (uint64_t)100000, (uint64_t)200000, (uint64_t)300000, (uint64_t)400000, (uint64_t)500000, (uint64_t)600000, (uint64_t)700000, (uint64_t)800000, (uint64_t)900000, + (uint64_t)1000000, (uint64_t)2000000, (uint64_t)3000000, (uint64_t)4000000, (uint64_t)5000000, (uint64_t)6000000, (uint64_t)7000000, (uint64_t)8000000, (uint64_t)9000000, // 1 micronero + (uint64_t)10000000, (uint64_t)20000000, (uint64_t)30000000, (uint64_t)40000000, (uint64_t)50000000, (uint64_t)60000000, (uint64_t)70000000, (uint64_t)80000000, (uint64_t)90000000, + (uint64_t)100000000, (uint64_t)200000000, (uint64_t)300000000, (uint64_t)400000000, (uint64_t)500000000, (uint64_t)600000000, (uint64_t)700000000, (uint64_t)800000000, (uint64_t)900000000, + (uint64_t)1000000000, (uint64_t)2000000000, (uint64_t)3000000000, (uint64_t)4000000000, (uint64_t)5000000000, (uint64_t)6000000000, (uint64_t)7000000000, (uint64_t)8000000000, (uint64_t)9000000000, + (uint64_t)10000000000, (uint64_t)20000000000, (uint64_t)30000000000, (uint64_t)40000000000, (uint64_t)50000000000, (uint64_t)60000000000, (uint64_t)70000000000, (uint64_t)80000000000, (uint64_t)90000000000, + (uint64_t)100000000000, (uint64_t)200000000000, (uint64_t)300000000000, (uint64_t)400000000000, (uint64_t)500000000000, (uint64_t)600000000000, (uint64_t)700000000000, (uint64_t)800000000000, (uint64_t)900000000000, + (uint64_t)1000000000000, (uint64_t)2000000000000, (uint64_t)3000000000000, (uint64_t)4000000000000, (uint64_t)5000000000000, (uint64_t)6000000000000, (uint64_t)7000000000000, (uint64_t)8000000000000, (uint64_t)9000000000000, // 1 monero + (uint64_t)10000000000000, (uint64_t)20000000000000, (uint64_t)30000000000000, (uint64_t)40000000000000, (uint64_t)50000000000000, (uint64_t)60000000000000, (uint64_t)70000000000000, (uint64_t)80000000000000, (uint64_t)90000000000000, + (uint64_t)100000000000000, (uint64_t)200000000000000, (uint64_t)300000000000000, (uint64_t)400000000000000, (uint64_t)500000000000000, (uint64_t)600000000000000, (uint64_t)700000000000000, (uint64_t)800000000000000, (uint64_t)900000000000000, + (uint64_t)1000000000000000, (uint64_t)2000000000000000, (uint64_t)3000000000000000, (uint64_t)4000000000000000, (uint64_t)5000000000000000, (uint64_t)6000000000000000, (uint64_t)7000000000000000, (uint64_t)8000000000000000, (uint64_t)9000000000000000, + (uint64_t)10000000000000000, (uint64_t)20000000000000000, (uint64_t)30000000000000000, (uint64_t)40000000000000000, (uint64_t)50000000000000000, (uint64_t)60000000000000000, (uint64_t)70000000000000000, (uint64_t)80000000000000000, (uint64_t)90000000000000000, + (uint64_t)100000000000000000, (uint64_t)200000000000000000, (uint64_t)300000000000000000, (uint64_t)400000000000000000, (uint64_t)500000000000000000, (uint64_t)600000000000000000, (uint64_t)700000000000000000, (uint64_t)800000000000000000, (uint64_t)900000000000000000, + (uint64_t)1000000000000000000, (uint64_t)2000000000000000000, (uint64_t)3000000000000000000, (uint64_t)4000000000000000000, (uint64_t)5000000000000000000, (uint64_t)6000000000000000000, (uint64_t)7000000000000000000, (uint64_t)8000000000000000000, (uint64_t)9000000000000000000, // 1 meganero + (uint64_t)10000000000000000000ull +}; + +static std::atomic default_decimal_point(CRYPTONOTE_DISPLAY_DECIMAL_POINT); + +static std::atomic tx_hashes_calculated_count(0); +static std::atomic tx_hashes_cached_count(0); +static std::atomic block_hashes_calculated_count(0); +static std::atomic block_hashes_cached_count(0); + +namespace cryptonote +{ + //--------------------------------------------------------------- + void get_transaction_prefix_hash(const transaction_prefix& tx, crypto::hash& h) + { + std::ostringstream s; + binary_archive a(s); + ::serialization::serialize(a, const_cast(tx)); + crypto::cn_fast_hash(s.str().data(), s.str().size(), h); + } + //--------------------------------------------------------------- + crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx) + { + crypto::hash h = null_hash; + get_transaction_prefix_hash(tx, h); + return h; + } + //--------------------------------------------------------------- + bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx) + { + std::stringstream ss; + ss << tx_blob; + binary_archive ba(ss); + bool r = ::serialization::serialize(ba, tx); + CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob"); + tx.invalidate_hashes(); + return true; + } + //--------------------------------------------------------------- + bool parse_and_validate_tx_base_from_blob(const blobdata& tx_blob, transaction& tx) + { + std::stringstream ss; + ss << tx_blob; + binary_archive ba(ss); + bool r = tx.serialize_base(ba); + CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob"); + return true; + } + //--------------------------------------------------------------- + bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash) + { + std::stringstream ss; + ss << tx_blob; + binary_archive ba(ss); + bool r = ::serialization::serialize(ba, tx); + CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob"); + tx.invalidate_hashes(); + //TODO: validate tx + + get_transaction_hash(tx, tx_hash); + get_transaction_prefix_hash(tx, tx_prefix_hash); + return true; + } + //--------------------------------------------------------------- + bool generate_key_image_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki) + { + crypto::key_derivation recv_derivation = AUTO_VAL_INIT(recv_derivation); + bool r = crypto::generate_key_derivation(tx_public_key, ack.m_view_secret_key, recv_derivation); + CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to generate_key_derivation(" << tx_public_key << ", " << ack.m_view_secret_key << ")"); + + r = crypto::derive_public_key(recv_derivation, real_output_index, ack.m_account_address.m_spend_public_key, in_ephemeral.pub); + CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to derive_public_key(" << recv_derivation << ", " << real_output_index << ", " << ack.m_account_address.m_spend_public_key << ")"); + + crypto::derive_secret_key(recv_derivation, real_output_index, ack.m_spend_secret_key, in_ephemeral.sec); + + crypto::generate_key_image(in_ephemeral.pub, in_ephemeral.sec, ki); + return true; + } + //--------------------------------------------------------------- + uint64_t power_integral(uint64_t a, uint64_t b) + { + if(b == 0) + return 1; + uint64_t total = a; + for(uint64_t i = 1; i != b; i++) + total *= a; + return total; + } + //--------------------------------------------------------------- + bool parse_amount(uint64_t& amount, const std::string& str_amount_) + { + std::string str_amount = str_amount_; + boost::algorithm::trim(str_amount); + + size_t point_index = str_amount.find_first_of('.'); + size_t fraction_size; + if (std::string::npos != point_index) + { + fraction_size = str_amount.size() - point_index - 1; + while (default_decimal_point < fraction_size && '0' == str_amount.back()) + { + str_amount.erase(str_amount.size() - 1, 1); + --fraction_size; + } + if (default_decimal_point < fraction_size) + return false; + str_amount.erase(point_index, 1); + } + else + { + fraction_size = 0; + } + + if (str_amount.empty()) + return false; + + if (fraction_size < default_decimal_point) + { + str_amount.append(default_decimal_point - fraction_size, '0'); + } + + return string_tools::get_xtype_from_string(amount, str_amount); + } + //--------------------------------------------------------------- + bool get_tx_fee(const transaction& tx, uint64_t & fee) + { + if (tx.version > 1) + { + fee = tx.rct_signatures.txnFee; + return true; + } + uint64_t amount_in = 0; + uint64_t amount_out = 0; + for(auto& in: tx.vin) + { + CHECK_AND_ASSERT_MES(in.type() == typeid(txin_to_key), 0, "unexpected type id in transaction"); + amount_in += boost::get(in).amount; + } + for(auto& o: tx.vout) + amount_out += o.amount; + + CHECK_AND_ASSERT_MES(amount_in >= amount_out, false, "transaction spend (" <& tx_extra, std::vector& tx_extra_fields) + { + tx_extra_fields.clear(); + + if(tx_extra.empty()) + return true; + + std::string extra_str(reinterpret_cast(tx_extra.data()), tx_extra.size()); + std::istringstream iss(extra_str); + binary_archive ar(iss); + + bool eof = false; + while (!eof) + { + tx_extra_field field; + bool r = ::do_serialize(ar, field); + CHECK_AND_NO_ASSERT_MES_L1(r, false, "failed to deserialize extra field. extra = " << string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast(tx_extra.data()), tx_extra.size()))); + tx_extra_fields.push_back(field); + + std::ios_base::iostate state = iss.rdstate(); + eof = (EOF == iss.peek()); + iss.clear(state); + } + CHECK_AND_NO_ASSERT_MES_L1(::serialization::check_stream_state(ar), false, "failed to deserialize extra field. extra = " << string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast(tx_extra.data()), tx_extra.size()))); + + return true; + } + //--------------------------------------------------------------- + crypto::public_key get_tx_pub_key_from_extra(const std::vector& tx_extra, size_t pk_index) + { + std::vector tx_extra_fields; + parse_tx_extra(tx_extra, tx_extra_fields); + + tx_extra_pub_key pub_key_field; + if(!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, pk_index)) + return null_pkey; + + return pub_key_field.pub_key; + } + //--------------------------------------------------------------- + crypto::public_key get_tx_pub_key_from_extra(const transaction_prefix& tx_prefix, size_t pk_index) + { + return get_tx_pub_key_from_extra(tx_prefix.extra, pk_index); + } + //--------------------------------------------------------------- + crypto::public_key get_tx_pub_key_from_extra(const transaction& tx, size_t pk_index) + { + return get_tx_pub_key_from_extra(tx.extra, pk_index); + } + //--------------------------------------------------------------- + bool add_tx_pub_key_to_extra(transaction& tx, const crypto::public_key& tx_pub_key) + { + tx.extra.resize(tx.extra.size() + 1 + sizeof(crypto::public_key)); + tx.extra[tx.extra.size() - 1 - sizeof(crypto::public_key)] = TX_EXTRA_TAG_PUBKEY; + *reinterpret_cast(&tx.extra[tx.extra.size() - sizeof(crypto::public_key)]) = tx_pub_key; + return true; + } + //--------------------------------------------------------------- + bool add_extra_nonce_to_tx_extra(std::vector& tx_extra, const blobdata& extra_nonce) + { + CHECK_AND_ASSERT_MES(extra_nonce.size() <= TX_EXTRA_NONCE_MAX_COUNT, false, "extra nonce could be 255 bytes max"); + size_t start_pos = tx_extra.size(); + tx_extra.resize(tx_extra.size() + 2 + extra_nonce.size()); + //write tag + tx_extra[start_pos] = TX_EXTRA_NONCE; + //write len + ++start_pos; + tx_extra[start_pos] = static_cast(extra_nonce.size()); + //write data + ++start_pos; + memcpy(&tx_extra[start_pos], extra_nonce.data(), extra_nonce.size()); + return true; + } + //--------------------------------------------------------------- + bool remove_field_from_tx_extra(std::vector& tx_extra, const std::type_info &type) + { + if (tx_extra.empty()) + return true; + std::string extra_str(reinterpret_cast(tx_extra.data()), tx_extra.size()); + std::istringstream iss(extra_str); + binary_archive ar(iss); + std::ostringstream oss; + binary_archive newar(oss); + + bool eof = false; + while (!eof) + { + tx_extra_field field; + bool r = ::do_serialize(ar, field); + CHECK_AND_NO_ASSERT_MES_L1(r, false, "failed to deserialize extra field. extra = " << string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast(tx_extra.data()), tx_extra.size()))); + if (field.type() != type) + ::do_serialize(newar, field); + + std::ios_base::iostate state = iss.rdstate(); + eof = (EOF == iss.peek()); + iss.clear(state); + } + CHECK_AND_NO_ASSERT_MES_L1(::serialization::check_stream_state(ar), false, "failed to deserialize extra field. extra = " << string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast(tx_extra.data()), tx_extra.size()))); + tx_extra.clear(); + std::string s = oss.str(); + tx_extra.reserve(s.size()); + std::copy(s.begin(), s.end(), std::back_inserter(tx_extra)); + return true; + } + //--------------------------------------------------------------- + bool append_mm_tag_to_extra(std::vector& tx_extra, const tx_extra_merge_mining_tag& mm_tag) + { + blobdata blob; + if (!t_serializable_object_to_blob(mm_tag, blob)) + return false; + + tx_extra.push_back(TX_EXTRA_MERGE_MINING_TAG); + std::copy(reinterpret_cast(blob.data()), reinterpret_cast(blob.data() + blob.size()), std::back_inserter(tx_extra)); + return true; + } + //--------------------------------------------------------------- + bool get_mm_tag_from_extra(const std::vector& tx_extra, tx_extra_merge_mining_tag& mm_tag) + { + 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, mm_tag); + } + //--------------------------------------------------------------- + void set_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash& payment_id) + { + extra_nonce.clear(); + extra_nonce.push_back(TX_EXTRA_NONCE_PAYMENT_ID); + const uint8_t* payment_id_ptr = reinterpret_cast(&payment_id); + std::copy(payment_id_ptr, payment_id_ptr + sizeof(payment_id), std::back_inserter(extra_nonce)); + } + //--------------------------------------------------------------- + void set_encrypted_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash8& payment_id) + { + extra_nonce.clear(); + extra_nonce.push_back(TX_EXTRA_NONCE_ENCRYPTED_PAYMENT_ID); + const uint8_t* payment_id_ptr = reinterpret_cast(&payment_id); + std::copy(payment_id_ptr, payment_id_ptr + sizeof(payment_id), std::back_inserter(extra_nonce)); + } + //--------------------------------------------------------------- + bool get_payment_id_from_tx_extra_nonce(const blobdata& extra_nonce, crypto::hash& payment_id) + { + if(sizeof(crypto::hash) + 1 != extra_nonce.size()) + return false; + if(TX_EXTRA_NONCE_PAYMENT_ID != extra_nonce[0]) + return false; + payment_id = *reinterpret_cast(extra_nonce.data() + 1); + return true; + } + //--------------------------------------------------------------- + bool get_encrypted_payment_id_from_tx_extra_nonce(const blobdata& extra_nonce, crypto::hash8& payment_id) + { + if(sizeof(crypto::hash8) + 1 != extra_nonce.size()) + return false; + if (TX_EXTRA_NONCE_ENCRYPTED_PAYMENT_ID != extra_nonce[0]) + return false; + payment_id = *reinterpret_cast(extra_nonce.data() + 1); + return true; + } + //--------------------------------------------------------------- + bool encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key) + { + crypto::key_derivation derivation; + crypto::hash hash; + char data[33]; /* A hash, and an extra byte */ + + if (!generate_key_derivation(public_key, secret_key, derivation)) + return false; + + memcpy(data, &derivation, 32); + data[32] = ENCRYPTED_PAYMENT_ID_TAIL; + cn_fast_hash(data, 33, hash); + + for (size_t b = 0; b < 8; ++b) + payment_id.data[b] ^= hash.data[b]; + + return true; + } + bool decrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key) + { + // Encryption and decryption are the same operation (xor with a key) + return encrypt_payment_id(payment_id, public_key, secret_key); + } + //--------------------------------------------------------------- + bool get_inputs_money_amount(const transaction& tx, uint64_t& money) + { + money = 0; + for(const auto& in: tx.vin) + { + CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, false); + money += tokey_in.amount; + } + return true; + } + //--------------------------------------------------------------- + uint64_t get_block_height(const block& b) + { + CHECK_AND_ASSERT_MES(b.miner_tx.vin.size() == 1, 0, "wrong miner tx in block: " << get_block_hash(b) << ", b.miner_tx.vin.size() != 1"); + CHECKED_GET_SPECIFIC_VARIANT(b.miner_tx.vin[0], const txin_gen, coinbase_in, 0); + return coinbase_in.height; + } + //--------------------------------------------------------------- + bool check_inputs_types_supported(const transaction& tx) + { + for(const auto& in: tx.vin) + { + CHECK_AND_ASSERT_MES(in.type() == typeid(txin_to_key), false, "wrong variant type: " + << in.type().name() << ", expected " << typeid(txin_to_key).name() + << ", in transaction id=" << get_transaction_hash(tx)); + + } + return true; + } + //----------------------------------------------------------------------------------------------- + bool check_outs_valid(const transaction& tx) + { + for(const tx_out& out: tx.vout) + { + CHECK_AND_ASSERT_MES(out.target.type() == typeid(txout_to_key), false, "wrong variant type: " + << out.target.type().name() << ", expected " << typeid(txout_to_key).name() + << ", in transaction id=" << get_transaction_hash(tx)); + + if (tx.version <= 1) + { + CHECK_AND_NO_ASSERT_MES(0 < out.amount, false, "zero amount output in transaction id=" << get_transaction_hash(tx)); + } + + if(!check_key(boost::get(out.target).key)) + return false; + } + return true; + } + //----------------------------------------------------------------------------------------------- + bool check_money_overflow(const transaction& tx) + { + return check_inputs_overflow(tx) && check_outs_overflow(tx); + } + //--------------------------------------------------------------- + bool check_inputs_overflow(const transaction& tx) + { + uint64_t money = 0; + for(const auto& in: tx.vin) + { + CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, false); + if(money > tokey_in.amount + money) + return false; + money += tokey_in.amount; + } + return true; + } + //--------------------------------------------------------------- + bool check_outs_overflow(const transaction& tx) + { + uint64_t money = 0; + for(const auto& o: tx.vout) + { + if(money > o.amount + money) + return false; + money += o.amount; + } + return true; + } + //--------------------------------------------------------------- + uint64_t get_outs_money_amount(const transaction& tx) + { + uint64_t outputs_amount = 0; + for(const auto& o: tx.vout) + outputs_amount += o.amount; + return outputs_amount; + } + //--------------------------------------------------------------- + std::string short_hash_str(const crypto::hash& h) + { + std::string res = string_tools::pod_to_hex(h); + CHECK_AND_ASSERT_MES(res.size() == 64, res, "wrong hash256 with string_tools::pod_to_hex conversion"); + auto erased_pos = res.erase(8, 48); + res.insert(8, "...."); + return res; + } + //--------------------------------------------------------------- + bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::public_key& tx_pub_key, size_t output_index) + { + crypto::key_derivation derivation; + generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation); + crypto::public_key pk; + derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk); + return pk == out_key.key; + } + //--------------------------------------------------------------- + bool is_out_to_acc_precomp(const crypto::public_key& spend_public_key, const txout_to_key& out_key, const crypto::key_derivation& derivation, size_t output_index) + { + crypto::public_key pk; + derive_public_key(derivation, output_index, spend_public_key, pk); + return pk == out_key.key; + } + //--------------------------------------------------------------- + bool lookup_acc_outs(const account_keys& acc, const transaction& tx, std::vector& outs, uint64_t& money_transfered) + { + crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx); + if(null_pkey == tx_pub_key) + return false; + return lookup_acc_outs(acc, tx, tx_pub_key, outs, money_transfered); + } + //--------------------------------------------------------------- + bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, std::vector& outs, uint64_t& money_transfered) + { + money_transfered = 0; + size_t i = 0; + for(const tx_out& o: tx.vout) + { + CHECK_AND_ASSERT_MES(o.target.type() == typeid(txout_to_key), false, "wrong type id in transaction out" ); + if(is_out_to_acc(acc, boost::get(o.target), tx_pub_key, i)) + { + outs.push_back(i); + money_transfered += o.amount; + } + i++; + } + return true; + } + //--------------------------------------------------------------- + void get_blob_hash(const blobdata& blob, crypto::hash& res) + { + cn_fast_hash(blob.data(), blob.size(), res); + } + //--------------------------------------------------------------- + void set_default_decimal_point(unsigned int decimal_point) + { + switch (decimal_point) + { + case 8: + case 6: + case 3: + case 0: + default_decimal_point = decimal_point; + break; + default: + ASSERT_MES_AND_THROW("Invalid decimal point specification: " << decimal_point); + } + } + //--------------------------------------------------------------- + unsigned int get_default_decimal_point() + { + return default_decimal_point; + } + //--------------------------------------------------------------- + std::string get_unit(unsigned int decimal_point) + { + if (decimal_point == (unsigned int)-1) + decimal_point = default_decimal_point; + switch (std::atomic_load(&default_decimal_point)) + { + case 12: + return "monero"; + case 9: + return "millinero"; + case 6: + return "micronero"; + case 3: + return "nanonero"; + case 0: + return "piconero"; + default: + ASSERT_MES_AND_THROW("Invalid decimal point specification: " << default_decimal_point); + } + } + //--------------------------------------------------------------- + std::string print_money(uint64_t amount, unsigned int decimal_point) + { + if (decimal_point == (unsigned int)-1) + decimal_point = default_decimal_point; + std::string s = std::to_string(amount); + if(s.size() < decimal_point+1) + { + s.insert(0, decimal_point+1 - s.size(), '0'); + } + if (decimal_point > 0) + s.insert(s.size() - decimal_point, "."); + return s; + } + //--------------------------------------------------------------- + crypto::hash get_blob_hash(const blobdata& blob) + { + crypto::hash h = null_hash; + get_blob_hash(blob, h); + return h; + } + //--------------------------------------------------------------- + crypto::hash get_transaction_hash(const transaction& t) + { + crypto::hash h = null_hash; + get_transaction_hash(t, h, NULL); + return h; + } + //--------------------------------------------------------------- + bool get_transaction_hash(const transaction& t, crypto::hash& res) + { + return get_transaction_hash(t, res, NULL); + } + //--------------------------------------------------------------- + bool calculate_transaction_hash(const transaction& t, crypto::hash& res, size_t* blob_size) + { + // v1 transactions hash the entire blob + if (t.version <= 1) + { + size_t ignored_blob_size, &blob_size_ref = blob_size ? *blob_size : ignored_blob_size; + return get_object_hash(t, res, blob_size_ref); + } + + // v2 transactions hash different parts together, than hash the set of those hashes + crypto::hash hashes[3]; + + // prefix + get_transaction_prefix_hash(t, hashes[0]); + + transaction &tt = const_cast(t); + + // base rct + { + std::stringstream ss; + binary_archive ba(ss); + const size_t inputs = t.vin.size(); + const size_t outputs = t.vout.size(); + bool r = tt.rct_signatures.serialize_rctsig_base(ba, inputs, outputs); + CHECK_AND_ASSERT_MES(r, false, "Failed to serialize rct signatures base"); + cryptonote::get_blob_hash(ss.str(), hashes[1]); + } + + // prunable rct + if (t.rct_signatures.type == rct::RCTTypeNull) + { + hashes[2] = cryptonote::null_hash; + } + else + { + std::stringstream ss; + binary_archive ba(ss); + const size_t inputs = t.vin.size(); + const size_t outputs = t.vout.size(); + const size_t mixin = t.vin.empty() ? 0 : t.vin[0].type() == typeid(txin_to_key) ? boost::get(t.vin[0]).key_offsets.size() - 1 : 0; + bool r = tt.rct_signatures.p.serialize_rctsig_prunable(ba, t.rct_signatures.type, inputs, outputs, mixin); + CHECK_AND_ASSERT_MES(r, false, "Failed to serialize rct signatures prunable"); + cryptonote::get_blob_hash(ss.str(), hashes[2]); + } + + // the tx hash is the hash of the 3 hashes + res = cn_fast_hash(hashes, sizeof(hashes)); + + // we still need the size + if (blob_size) + *blob_size = get_object_blobsize(t); + + return true; + } + //--------------------------------------------------------------- + bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t* blob_size) + { + if (t.is_hash_valid()) + { +#ifdef ENABLE_HASH_CASH_INTEGRITY_CHECK + CHECK_AND_ASSERT_THROW_MES(!calculate_transaction_hash(t, res, blob_size) || t.hash == res, "tx hash cash integrity failure"); +#endif + res = t.hash; + if (blob_size) + { + if (!t.is_blob_size_valid()) + { + t.blob_size = get_object_blobsize(t); + t.set_blob_size_valid(true); + } + *blob_size = t.blob_size; + } + ++tx_hashes_cached_count; + return true; + } + ++tx_hashes_calculated_count; + bool ret = calculate_transaction_hash(t, res, blob_size); + if (!ret) + return false; + t.hash = res; + t.set_hash_valid(true); + if (blob_size) + { + t.blob_size = *blob_size; + t.set_blob_size_valid(true); + } + return true; + } + //--------------------------------------------------------------- + bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t& blob_size) + { + return get_transaction_hash(t, res, &blob_size); + } + //--------------------------------------------------------------- + blobdata get_block_hashing_blob(const block& b) + { + blobdata blob = t_serializable_object_to_blob(static_cast(b)); + crypto::hash tree_root_hash = get_tx_tree_hash(b); + blob.append(reinterpret_cast(&tree_root_hash), sizeof(tree_root_hash)); + blob.append(tools::get_varint_data(b.tx_hashes.size() + 1)); + return blob; + } + //--------------------------------------------------------------- + bool get_block_hashing_blob(const block& b, blobdata& blob) + { + blob = t_serializable_object_to_blob(static_cast(b)); + crypto::hash tree_root_hash = get_tx_tree_hash(b); + blob.append(reinterpret_cast(&tree_root_hash), sizeof(tree_root_hash)); + blob.append(tools::get_varint_data(b.tx_hashes.size()+1)); + return true; + } + //--------------------------------------------------------------- + bool get_bytecoin_block_hashing_blob(const block& b, blobdata& blob) + { + auto sbb = make_serializable_bytecoin_block(b, true, true); + return t_serializable_object_to_blob(sbb, blob); + } + //--------------------------------------------------------------- + bool calculate_block_hash(const block& b, crypto::hash& res) + { + get_blob_hash(block_to_blob(b)); + bool hash_result = get_object_hash(get_block_hashing_blob(b), res); + return hash_result; + } + //--------------------------------------------------------------- + bool get_block_hash(const block& b, crypto::hash& res) + { + if (b.is_hash_valid()) + { +#ifdef ENABLE_HASH_CASH_INTEGRITY_CHECK + CHECK_AND_ASSERT_THROW_MES(!calculate_block_hash(b, res) || b.hash == res, "block hash cash integrity failure"); +#endif + res = b.hash; + ++block_hashes_cached_count; + return true; + } + ++block_hashes_calculated_count; + + blobdata blob; + if (!get_block_hashing_blob(b, blob)) + return false; + + if (BLOCK_MAJOR_VERSION_2 <= b.major_version) + { + blobdata parent_blob; + auto sbb = make_serializable_bytecoin_block(b, true, false); + if (!t_serializable_object_to_blob(sbb, parent_blob)) + return false; + + blob.append(parent_blob); + } + + bool ret = get_object_hash(blob, res); + if (!ret) + return false; + b.hash = res; + b.set_hash_valid(true); + return true; + } + //--------------------------------------------------------------- + bool generate_genesis_block(block& bl) + { + //genesis block + bl = boost::value_initialized(); + + + account_public_address ac = boost::value_initialized(); + std::vector sz; + cryptonote::construct_miner_tx(0, 0, 0, 0, 0, ac, bl.miner_tx); // zero fee in genesis + blobdata txb = tx_to_blob(bl.miner_tx); + std::string hex_tx_represent = string_tools::buff_to_hex_nodelimer(txb); + + //hard code coinbase tx in genesis block, because "tru" generating tx use random, but genesis should be always the same + std::string genesis_coinbase_tx_hex = config::GENESIS_TX; + + blobdata tx_bl; + string_tools::parse_hexstr_to_binbuff(genesis_coinbase_tx_hex, tx_bl); + bool r = parse_and_validate_tx_from_blob(tx_bl, bl.miner_tx); + CHECK_AND_ASSERT_MES(r, false, "failed to parse coinbase tx from hard coded blob"); + bl.major_version = CURRENT_BLOCK_MAJOR_VERSION; + bl.minor_version = CURRENT_BLOCK_MINOR_VERSION; + bl.timestamp = 0; + bl.nonce = config::GENESIS_NONCE; + miner::find_nonce_for_given_block(bl, 1, 0); + return true; + } + //--------------------------------------------------------------- + bool get_genesis_block_hash(crypto::hash& h) + { + static std::atomic cached(false); + static crypto::hash genesis_block_hash; + if (!cached) + { + static std::mutex m; + std::unique_lock lock(m); + if (!cached) + { + block genesis_block; + if (!generate_genesis_block(genesis_block)) + return false; + + if (!get_block_hash(genesis_block, genesis_block_hash)) + return false; + + cached = true; + } + } + + h = genesis_block_hash; + return true; + } + //--------------------------------------------------------------- + bool get_block_header_hash(const block& b, crypto::hash& res) + { + blobdata blob; + if (!get_block_hashing_blob(b, blob)) + return false; + + return get_object_hash(blob, res); + } + //--------------------------------------------------------------- + crypto::hash get_block_hash(const block& b) + { + crypto::hash p = null_hash; + get_block_hash(b, p); + return p; + } + //--------------------------------------------------------------- + bool get_block_longhash(const block& b, crypto::hash& res, uint64_t height) + { + blobdata bd = get_block_hashing_blob(b); + crypto::cn_slow_hash(bd.data(), bd.size(), res); + return true; + } + //--------------------------------------------------------------- + bool get_bytecoin_block_longhash(const block& b, crypto::hash& res) + { + blobdata bd; + if (!get_bytecoin_block_hashing_blob(b, bd)) + return false; + crypto::cn_slow_hash(bd.data(), bd.size(), res); + return true; + } + //--------------------------------------------------------------- + bool check_proof_of_work_v1(const block& bl, difficulty_type current_diffic, crypto::hash& proof_of_work) + { + if (BLOCK_MAJOR_VERSION_1 != bl.major_version) + return false; + + proof_of_work = get_block_longhash(bl, 0); + return check_hash(proof_of_work, current_diffic); + } + //--------------------------------------------------------------- + bool check_proof_of_work_v2(const block& bl, difficulty_type current_diffic, crypto::hash& proof_of_work) + { + MDEBUG("Checking POW V2 - diff " << current_diffic); + if (bl.major_version < BLOCK_MAJOR_VERSION_2) + return false; + + if (!get_bytecoin_block_longhash(bl, proof_of_work)) { + MDEBUG("Failed to get bytecoin block longhash"); + return false; + } + if (!check_hash(proof_of_work, current_diffic)) { + MDEBUG("Failed to check hash for pow"); + return false; + } + + tx_extra_merge_mining_tag mm_tag; + if (!get_mm_tag_from_extra(bl.parent_block.miner_tx.extra, mm_tag)) + { + LOG_ERROR("merge mining tag wasn't found in extra of the parent block miner transaction"); + return false; + } + + crypto::hash genesis_block_hash; + if (!get_genesis_block_hash(genesis_block_hash)) { + MDEBUG("Failed to get genesis block hash"); + return false; + } + + if (8 * sizeof(genesis_block_hash) < bl.parent_block.blockchain_branch.size()) { + MDEBUG("Failed genesis block and parent block branch size comparison"); + return false; + } + + crypto::hash aux_block_header_hash; + if (!get_block_header_hash(bl, aux_block_header_hash)) { + MDEBUG("Failed to get aux header hash"); + return false; + } + + crypto::hash aux_blocks_merkle_root; + crypto::tree_hash_from_branch(bl.parent_block.blockchain_branch.data(), bl.parent_block.blockchain_branch.size(), + aux_block_header_hash, &genesis_block_hash, aux_blocks_merkle_root); + CHECK_AND_NO_ASSERT_MES(aux_blocks_merkle_root == mm_tag.merkle_root, false, "Aux block hash wasn't found in merkle tree"); + + return true; + } + //--------------------------------------------------------------- + bool check_proof_of_work(const block& bl, difficulty_type current_diffic, crypto::hash& proof_of_work) + { + switch (bl.major_version) + { + case BLOCK_MAJOR_VERSION_1: return check_proof_of_work_v1(bl, current_diffic, proof_of_work); + case BLOCK_MAJOR_VERSION_2: + case BLOCK_MAJOR_VERSION_3: + return check_proof_of_work_v2(bl, current_diffic, proof_of_work); + } + + CHECK_AND_ASSERT_MES(false, false, "unknown block major version: " << bl.major_version << "." << bl.minor_version); + } + //--------------------------------------------------------------- + std::vector relative_output_offsets_to_absolute(const std::vector& off) + { + std::vector res = off; + for(size_t i = 1; i < res.size(); i++) + res[i] += res[i-1]; + return res; + } + //--------------------------------------------------------------- + std::vector absolute_output_offsets_to_relative(const std::vector& off) + { + std::vector res = off; + if(!off.size()) + return res; + std::sort(res.begin(), res.end());//just to be sure, actually it is already should be sorted + for(size_t i = res.size()-1; i != 0; i--) + res[i] -= res[i-1]; + + return res; + } + //--------------------------------------------------------------- + crypto::hash get_block_longhash(const block& b, uint64_t height) + { + crypto::hash p = null_hash; + get_block_longhash(b, p, height); + return p; + } + //--------------------------------------------------------------- + bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b) + { + std::stringstream ss; + ss << b_blob; + binary_archive ba(ss); + bool r = ::serialization::serialize(ba, b); + CHECK_AND_ASSERT_MES(r, false, "Failed to parse block from blob"); + b.invalidate_hashes(); + b.miner_tx.invalidate_hashes(); + return true; + } + //--------------------------------------------------------------- + blobdata block_to_blob(const block& b) + { + return t_serializable_object_to_blob(b); + } + //--------------------------------------------------------------- + bool block_to_blob(const block& b, blobdata& b_blob) + { + return t_serializable_object_to_blob(b, b_blob); + } + //--------------------------------------------------------------- + blobdata tx_to_blob(const transaction& tx) + { + return t_serializable_object_to_blob(tx); + } + //--------------------------------------------------------------- + bool tx_to_blob(const transaction& tx, blobdata& b_blob) + { + return t_serializable_object_to_blob(tx, b_blob); + } + //--------------------------------------------------------------- + void get_tx_tree_hash(const std::vector& tx_hashes, crypto::hash& h) + { + tree_hash(tx_hashes.data(), tx_hashes.size(), h); + } + //--------------------------------------------------------------- + crypto::hash get_tx_tree_hash(const std::vector& tx_hashes) + { + crypto::hash h = null_hash; + get_tx_tree_hash(tx_hashes, h); + return h; + } + //--------------------------------------------------------------- + crypto::hash get_tx_tree_hash(const block& b) + { + std::vector txs_ids; + crypto::hash h = null_hash; + size_t bl_sz = 0; + get_transaction_hash(b.miner_tx, h, bl_sz); + txs_ids.push_back(h); + for(auto& th: b.tx_hashes) + txs_ids.push_back(th); + return get_tx_tree_hash(txs_ids); + } + //--------------------------------------------------------------- + bool is_valid_decomposed_amount(uint64_t amount) + { + const uint64_t *begin = valid_decomposed_outputs; + const uint64_t *end = valid_decomposed_outputs + sizeof(valid_decomposed_outputs) / sizeof(valid_decomposed_outputs[0]); + return std::binary_search(begin, end, amount); + } + //--------------------------------------------------------------- + void get_hash_stats(uint64_t &tx_hashes_calculated, uint64_t &tx_hashes_cached, uint64_t &block_hashes_calculated, uint64_t & block_hashes_cached) + { + tx_hashes_calculated = tx_hashes_calculated_count; + tx_hashes_cached = tx_hashes_cached_count; + block_hashes_calculated = block_hashes_calculated_count; + block_hashes_cached = block_hashes_cached_count; + } +}