From 9e82b694da120708652871b55f639d1ef306a7ec Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 26 May 2016 21:48:18 +0100 Subject: remove original Cryptonote blockchain_storage blockchain format --- src/cryptonote_core/CMakeLists.txt | 2 - src/cryptonote_core/blockchain_storage.cpp | 1952 ---------------------------- src/cryptonote_core/blockchain_storage.h | 377 ------ src/cryptonote_core/cryptonote_basic.h | 3 - src/cryptonote_core/cryptonote_core.cpp | 19 - src/cryptonote_core/cryptonote_core.h | 13 - src/cryptonote_core/tx_pool.cpp | 19 - src/cryptonote_core/tx_pool.h | 19 - 8 files changed, 2404 deletions(-) delete mode 100644 src/cryptonote_core/blockchain_storage.cpp delete mode 100644 src/cryptonote_core/blockchain_storage.h (limited to 'src/cryptonote_core') diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt index 205356797..3e50f48ae 100644 --- a/src/cryptonote_core/CMakeLists.txt +++ b/src/cryptonote_core/CMakeLists.txt @@ -28,7 +28,6 @@ set(cryptonote_core_sources account.cpp - blockchain_storage.cpp blockchain.cpp checkpoints.cpp cryptonote_basic_impl.cpp @@ -44,7 +43,6 @@ set(cryptonote_core_headers) set(cryptonote_core_private_headers account.h account_boost_serialization.h - blockchain_storage.h blockchain_storage_boost_serialization.h blockchain.h checkpoints.h diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp deleted file mode 100644 index a829b7cbe..000000000 --- a/src/cryptonote_core/blockchain_storage.cpp +++ /dev/null @@ -1,1952 +0,0 @@ -// Copyright (c) 2014-2016, 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 -#include -#include -#include - -#include "include_base_utils.h" -#include "cryptonote_basic_impl.h" -#include "blockchain_storage.h" -#include "cryptonote_format_utils.h" -#include "cryptonote_boost_serialization.h" -#include "blockchain_storage_boost_serialization.h" -#include "cryptonote_config.h" -#include "miner.h" -#include "misc_language.h" -#include "profile_tools.h" -#include "file_io_utils.h" -#include "common/boost_serialization_helper.h" -#include "warnings.h" -#include "crypto/hash.h" -#include "cryptonote_core/checkpoints.h" -//#include "serialization/json_archive.h" -#include "../../contrib/otshell_utils/utils.hpp" -#include "../../src/p2p/data_logger.hpp" - -using namespace cryptonote; - -DISABLE_VS_WARNINGS(4267) - -//------------------------------------------------------------------ -bool blockchain_storage::have_tx(const crypto::hash &id) const -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - return m_transactions.find(id) != m_transactions.end(); -} -//------------------------------------------------------------------ -bool blockchain_storage::have_tx_keyimg_as_spent(const crypto::key_image &key_im) const -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - return m_spent_keys.find(key_im) != m_spent_keys.end(); -} -//------------------------------------------------------------------ -const transaction *blockchain_storage::get_tx(const crypto::hash &id) const -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - auto it = m_transactions.find(id); - if (it == m_transactions.end()) - return NULL; - - return &it->second.tx; -} -//------------------------------------------------------------------ -uint64_t blockchain_storage::get_current_blockchain_height() const -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - return m_blocks.size(); -} -//------------------------------------------------------------------ -bool blockchain_storage::init(const std::string& config_folder, bool testnet) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - m_config_folder = config_folder; - m_testnet = testnet; - LOG_PRINT_L0("Loading blockchain..."); - const std::string filename = m_config_folder + "/" CRYPTONOTE_BLOCKCHAINDATA_FILENAME; - if(tools::unserialize_obj_from_file(*this, filename)) - { - - // checkpoints - - // mainchain - for (size_t height=0; height < m_blocks.size(); ++height) - { - CHECK_AND_ASSERT_MES((!m_checkpoints.is_in_checkpoint_zone(height)) || m_checkpoints.check_block(height,get_block_hash(m_blocks[height].bl)),false,"checkpoint fail, blockchain.bin invalid"); - } - - // check alt chains - #if 0 - // doesn't work when a checkpoint is added and there are already alt chains. However, the rest of the blockchain code suffers from the same issue, so ignore for now - // see issue #118 - BOOST_FOREACH(blocks_ext_by_hash::value_type& alt_block, m_alternative_chains) - { - CHECK_AND_ASSERT_MES(m_checkpoints.is_alternative_block_allowed(m_blocks.size()-1,alt_block.second.height),false,"stored alternative block not allowed, blockchain.bin invalid"); - } - #endif - } - else - { - LOG_PRINT_L0("Can't load blockchain storage from file, generating genesis block."); - if (!store_genesis_block(testnet, true)) - return false; - } - if(!m_blocks.size()) - { - LOG_PRINT_L0("Blockchain not loaded, generating genesis block."); - - if (!store_genesis_block(testnet, false)) { - return false; - } - } else { - cryptonote::block b; - - if (testnet) - { - generate_genesis_block(b, config::testnet::GENESIS_TX, config::testnet::GENESIS_NONCE); - } - else - { - generate_genesis_block(b, config::GENESIS_TX, config::GENESIS_NONCE); - } - - crypto::hash genesis_hash = get_block_hash(m_blocks[0].bl); - crypto::hash testnet_genesis_hash = get_block_hash(b); - if (genesis_hash != testnet_genesis_hash) { - LOG_ERROR("Failed to init: genesis block mismatch. Probably you set --testnet flag with data dir with non-test blockchain or another network."); - return false; - } - } - uint64_t timestamp_diff = time(NULL) - m_blocks.back().bl.timestamp; - if(!m_blocks.back().bl.timestamp) - timestamp_diff = time(NULL) - 1341378000; - LOG_PRINT_GREEN("Blockchain initialized. last block: " << m_blocks.size() - 1 << ", " << epee::misc_utils::get_time_interval_string(timestamp_diff) << " time ago, current difficulty: " << get_difficulty_for_next_block(), LOG_LEVEL_0); - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::store_genesis_block(bool testnet, bool check_added) { - block bl = ::boost::value_initialized(); - block_verification_context bvc = boost::value_initialized(); - - if (testnet) - { - generate_genesis_block(bl, config::testnet::GENESIS_TX, config::testnet::GENESIS_NONCE); - } - else - { - generate_genesis_block(bl, config::GENESIS_TX, config::GENESIS_NONCE); - } - - add_new_block(bl, bvc); - CHECK_AND_ASSERT_MES(!bvc.m_verifivation_failed && (bvc.m_added_to_main_chain || !check_added), false, "Failed to add genesis block to blockchain"); - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::store_blockchain() -{ - m_is_blockchain_storing = true; - epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_is_blockchain_storing=false;}); - - LOG_PRINT_L0("Storing blockchain..."); - if (!tools::create_directories_if_necessary(m_config_folder)) - { - LOG_PRINT_L0("Failed to create data directory: " << m_config_folder); - return false; - } - - const std::string temp_filename = m_config_folder + "/" CRYPTONOTE_BLOCKCHAINDATA_TEMP_FILENAME; - // There is a chance that temp_filename and filename are hardlinks to the same file - std::remove(temp_filename.c_str()); - if(!tools::serialize_obj_to_file(*this, temp_filename)) - { - //achtung! - LOG_ERROR("Failed to save blockchain data to file: " << temp_filename); - return false; - } - const std::string filename = m_config_folder + "/" CRYPTONOTE_BLOCKCHAINDATA_FILENAME; - std::error_code ec = tools::replace_file(temp_filename, filename); - if (ec) - { - LOG_ERROR("Failed to rename blockchain data file " << temp_filename << " to " << filename << ": " << ec.message() << ':' << ec.value()); - return false; - } - LOG_PRINT_L0("Blockchain stored OK."); - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::deinit() -{ - return store_blockchain(); -} -//------------------------------------------------------------------ -bool blockchain_storage::pop_block_from_blockchain() -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - - CHECK_AND_ASSERT_MES(m_blocks.size() > 1, false, "pop_block_from_blockchain: can't pop from blockchain with size = " << m_blocks.size()); - size_t h = m_blocks.size()-1; - block_extended_info& bei = m_blocks[h]; - //crypto::hash id = get_block_hash(bei.bl); - bool r = purge_block_data_from_blockchain(bei.bl, bei.bl.tx_hashes.size()); - CHECK_AND_ASSERT_MES(r, false, "Failed to purge_block_data_from_blockchain for block " << get_block_hash(bei.bl) << " on height " << h); - - //remove from index - auto bl_ind = m_blocks_index.find(get_block_hash(bei.bl)); - CHECK_AND_ASSERT_MES(bl_ind != m_blocks_index.end(), false, "pop_block_from_blockchain: blockchain id not found in index"); - m_blocks_index.erase(bl_ind); - //pop block from core - m_blocks.pop_back(); - m_tx_pool->on_blockchain_dec(m_blocks.size()-1, get_tail_id()); - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::reset_and_set_genesis_block(const block& b) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - m_transactions.clear(); - m_spent_keys.clear(); - m_blocks.clear(); - m_blocks_index.clear(); - m_alternative_chains.clear(); - m_outputs.clear(); - - block_verification_context bvc = boost::value_initialized(); - add_new_block(b, bvc); - return bvc.m_added_to_main_chain && !bvc.m_verifivation_failed; -} -//------------------------------------------------------------------ -bool blockchain_storage::purge_transaction_keyimages_from_blockchain(const transaction& tx, bool strict_check) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - struct purge_transaction_visitor: public boost::static_visitor - { - key_images_container& m_spent_keys; - bool m_strict_check; - purge_transaction_visitor(key_images_container& spent_keys, bool strict_check):m_spent_keys(spent_keys), m_strict_check(strict_check){} - - bool operator()(const txin_to_key& inp) const - { - //const crypto::key_image& ki = inp.k_image; - auto r = m_spent_keys.find(inp.k_image); - if(r != m_spent_keys.end()) - { - m_spent_keys.erase(r); - }else - { - CHECK_AND_ASSERT_MES(!m_strict_check, false, "purge_block_data_from_blockchain: key image in transaction not found"); - } - return true; - } - bool operator()(const txin_gen& inp) const - { - return true; - } - bool operator()(const txin_to_script& tx) const - { - return false; - } - - bool operator()(const txin_to_scripthash& tx) const - { - return false; - } - }; - - BOOST_FOREACH(const txin_v& in, tx.vin) - { - bool r = boost::apply_visitor(purge_transaction_visitor(m_spent_keys, strict_check), in); - CHECK_AND_ASSERT_MES(!strict_check || r, false, "failed to process purge_transaction_visitor"); - } - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::purge_transaction_from_blockchain(const crypto::hash& tx_id) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - auto tx_index_it = m_transactions.find(tx_id); - CHECK_AND_ASSERT_MES(tx_index_it != m_transactions.end(), false, "purge_block_data_from_blockchain: transaction not found in blockchain index!!"); - transaction& tx = tx_index_it->second.tx; - - purge_transaction_keyimages_from_blockchain(tx, true); - - if(!is_coinbase(tx)) - { - cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); - bool r = m_tx_pool->add_tx(tx, tvc, true, true, 1); - CHECK_AND_ASSERT_MES(r, false, "purge_block_data_from_blockchain: failed to add transaction to transaction pool"); - } - - bool res = pop_transaction_from_global_index(tx, tx_id); - m_transactions.erase(tx_index_it); - LOG_PRINT_L1("Removed transaction from blockchain history:" << tx_id << ENDL); - return res; -} -//------------------------------------------------------------------ -bool blockchain_storage::purge_block_data_from_blockchain(const block& bl, size_t processed_tx_count) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - - bool res = true; - CHECK_AND_ASSERT_MES(processed_tx_count <= bl.tx_hashes.size(), false, "wrong processed_tx_count in purge_block_data_from_blockchain"); - for(size_t count = 0; count != processed_tx_count; count++) - { - res = purge_transaction_from_blockchain(bl.tx_hashes[(processed_tx_count -1)- count]) && res; - } - - res = purge_transaction_from_blockchain(get_transaction_hash(bl.miner_tx)) && res; - - return res; -} -//------------------------------------------------------------------ -crypto::hash blockchain_storage::get_tail_id(uint64_t& height) const -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - height = get_current_blockchain_height()-1; - return get_tail_id(); -} -//------------------------------------------------------------------ -crypto::hash blockchain_storage::get_tail_id() const -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - crypto::hash id = null_hash; - if(m_blocks.size()) - { - get_block_hash(m_blocks.back().bl, id); - } - return id; -} -//------------------------------------------------------------------ -bool blockchain_storage::get_short_chain_history(std::list& ids) const -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - size_t i = 0; - size_t current_multiplier = 1; - size_t sz = m_blocks.size(); - if(!sz) - return true; - size_t current_back_offset = 1; - bool genesis_included = false; - while(current_back_offset < sz) - { - ids.push_back(get_block_hash(m_blocks[sz-current_back_offset].bl)); - if(sz-current_back_offset == 0) - genesis_included = true; - if(i < 10) - { - ++current_back_offset; - }else - { - current_back_offset += current_multiplier *= 2; - } - ++i; - } - if(!genesis_included) - ids.push_back(get_block_hash(m_blocks[0].bl)); - - return true; -} -//------------------------------------------------------------------ -crypto::hash blockchain_storage::get_block_id_by_height(uint64_t height) const -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(height >= m_blocks.size()) - return null_hash; - - return get_block_hash(m_blocks[height].bl); -} -//------------------------------------------------------------------ -bool blockchain_storage::get_block_by_hash(const crypto::hash &h, block &blk) const { - CRITICAL_REGION_LOCAL(m_blockchain_lock); - - // try to find block in main chain - blocks_by_id_index::const_iterator it = m_blocks_index.find(h); - if (m_blocks_index.end() != it) { - blk = m_blocks[it->second].bl; - return true; - } - - // try to find block in alternative chain - blocks_ext_by_hash::const_iterator it_alt = m_alternative_chains.find(h); - if (m_alternative_chains.end() != it_alt) { - blk = it_alt->second.bl; - return true; - } - - return false; -} -//------------------------------------------------------------------ -void blockchain_storage::get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid) const { - CRITICAL_REGION_LOCAL(m_blockchain_lock); - - BOOST_FOREACH(const blocks_by_id_index::value_type &v, m_blocks_index) - main.push_back(v.first); - - BOOST_FOREACH(const blocks_ext_by_hash::value_type &v, m_alternative_chains) - alt.push_back(v.first); - - BOOST_FOREACH(const blocks_ext_by_hash::value_type &v, m_invalid_blocks) - invalid.push_back(v.first); -} -//------------------------------------------------------------------ -difficulty_type blockchain_storage::get_difficulty_for_next_block() const -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - std::vector timestamps; - std::vector commulative_difficulties; - size_t offset = m_blocks.size() - std::min(m_blocks.size(), static_cast(DIFFICULTY_BLOCKS_COUNT)); - if(!offset) - ++offset;//skip genesis block - for(; offset < m_blocks.size(); offset++) - { - timestamps.push_back(m_blocks[offset].bl.timestamp); - commulative_difficulties.push_back(m_blocks[offset].cumulative_difficulty); - } - return next_difficulty(timestamps, commulative_difficulties,DIFFICULTY_TARGET_V1); -} -//------------------------------------------------------------------ -bool blockchain_storage::rollback_blockchain_switching(std::list& original_chain, size_t rollback_height) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - - // fail if rollback_height passed is too high - if (rollback_height > m_blocks.size()) - { - return true; - } - - //remove failed subchain - for(size_t i = m_blocks.size()-1; i >=rollback_height; i--) - { - bool r = pop_block_from_blockchain(); - CHECK_AND_ASSERT_MES(r, false, "PANIC! failed to remove block while chain switching during the rollback!"); - } - //return back original chain - BOOST_FOREACH(auto& bl, original_chain) - { - block_verification_context bvc = boost::value_initialized(); - bool r = handle_block_to_main_chain(bl, bvc); - CHECK_AND_ASSERT_MES(r && bvc.m_added_to_main_chain, false, "PANIC! failed to add (again) block while chain switching during the rollback!"); - } - - LOG_PRINT_L1("Rollback success."); - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::switch_to_alternative_blockchain(std::list& alt_chain, bool discard_disconnected_chain) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - CHECK_AND_ASSERT_MES(alt_chain.size(), false, "switch_to_alternative_blockchain: empty chain passed"); - - size_t split_height = alt_chain.front()->second.height; - CHECK_AND_ASSERT_MES(m_blocks.size() > split_height, false, "switch_to_alternative_blockchain: blockchain size is lower than split height"); - - //disconnecting old chain - std::list disconnected_chain; - for(size_t i = m_blocks.size()-1; i >=split_height; i--) - { - block b = m_blocks[i].bl; - bool r = pop_block_from_blockchain(); - CHECK_AND_ASSERT_MES(r, false, "failed to remove block on chain switching"); - disconnected_chain.push_front(b); - } - - //connecting new alternative chain - for(auto alt_ch_iter = alt_chain.begin(); alt_ch_iter != alt_chain.end(); alt_ch_iter++) - { - auto ch_ent = *alt_ch_iter; - block_verification_context bvc = boost::value_initialized(); - bool r = handle_block_to_main_chain(ch_ent->second.bl, bvc); - if(!r || !bvc.m_added_to_main_chain) - { - LOG_PRINT_L1("Failed to switch to alternative blockchain"); - rollback_blockchain_switching(disconnected_chain, split_height); - add_block_as_invalid(ch_ent->second, get_block_hash(ch_ent->second.bl)); - LOG_PRINT_L1("The block was inserted as invalid while connecting new alternative chain, block_id: " << get_block_hash(ch_ent->second.bl)); - m_alternative_chains.erase(ch_ent); - - for(auto alt_ch_to_orph_iter = ++alt_ch_iter; alt_ch_to_orph_iter != alt_chain.end(); alt_ch_to_orph_iter++) - { - //block_verification_context bvc = boost::value_initialized(); - add_block_as_invalid((*alt_ch_iter)->second, (*alt_ch_iter)->first); - m_alternative_chains.erase(*alt_ch_to_orph_iter); - } - return false; - } - } - - if(!discard_disconnected_chain) - { - //pushing old chain as alternative chain - BOOST_FOREACH(auto& old_ch_ent, disconnected_chain) - { - block_verification_context bvc = boost::value_initialized(); - bool r = handle_alternative_block(old_ch_ent, get_block_hash(old_ch_ent), bvc); - if(!r) - { - LOG_PRINT_L1("Failed to push ex-main chain blocks to alternative chain "); - rollback_blockchain_switching(disconnected_chain, split_height); - return false; - } - } - } - - //removing all_chain entries from alternative chain - BOOST_FOREACH(auto ch_ent, alt_chain) - { - m_alternative_chains.erase(ch_ent); - } - - LOG_PRINT_GREEN("REORGANIZE SUCCESS! on height: " << split_height << ", new blockchain size: " << m_blocks.size(), LOG_LEVEL_0); - return true; -} -//------------------------------------------------------------------ -difficulty_type blockchain_storage::get_next_difficulty_for_alternative_chain(const std::list& alt_chain, block_extended_info& bei) const -{ - std::vector timestamps; - std::vector commulative_difficulties; - if(alt_chain.size()< DIFFICULTY_BLOCKS_COUNT) - { - CRITICAL_REGION_LOCAL(m_blockchain_lock); - size_t main_chain_stop_offset = alt_chain.size() ? alt_chain.front()->second.height : bei.height; - size_t main_chain_count = DIFFICULTY_BLOCKS_COUNT - std::min(static_cast(DIFFICULTY_BLOCKS_COUNT), alt_chain.size()); - main_chain_count = std::min(main_chain_count, main_chain_stop_offset); - size_t main_chain_start_offset = main_chain_stop_offset - main_chain_count; - - if(!main_chain_start_offset) - ++main_chain_start_offset; //skip genesis block - for(; main_chain_start_offset < main_chain_stop_offset; ++main_chain_start_offset) - { - timestamps.push_back(m_blocks[main_chain_start_offset].bl.timestamp); - commulative_difficulties.push_back(m_blocks[main_chain_start_offset].cumulative_difficulty); - } - - CHECK_AND_ASSERT_MES((alt_chain.size() + timestamps.size()) <= DIFFICULTY_BLOCKS_COUNT, false, "Internal error, alt_chain.size()["<< alt_chain.size() - << "] + vtimestampsec.size()[" << timestamps.size() << "] NOT <= DIFFICULTY_WINDOW[]" << DIFFICULTY_BLOCKS_COUNT ); - BOOST_FOREACH(auto it, alt_chain) - { - timestamps.push_back(it->second.bl.timestamp); - commulative_difficulties.push_back(it->second.cumulative_difficulty); - } - }else - { - timestamps.resize(std::min(alt_chain.size(), static_cast(DIFFICULTY_BLOCKS_COUNT))); - commulative_difficulties.resize(std::min(alt_chain.size(), static_cast(DIFFICULTY_BLOCKS_COUNT))); - size_t count = 0; - size_t max_i = timestamps.size()-1; - BOOST_REVERSE_FOREACH(auto it, alt_chain) - { - timestamps[max_i - count] = it->second.bl.timestamp; - commulative_difficulties[max_i - count] = it->second.cumulative_difficulty; - count++; - if(count >= DIFFICULTY_BLOCKS_COUNT) - break; - } - } - return next_difficulty(timestamps, commulative_difficulties,DIFFICULTY_TARGET_V1); -} -//------------------------------------------------------------------ -bool blockchain_storage::prevalidate_miner_transaction(const block& b, uint64_t height) const -{ - CHECK_AND_ASSERT_MES(b.miner_tx.vin.size() == 1, false, "coinbase transaction in the block has no inputs"); - CHECK_AND_ASSERT_MES(b.miner_tx.vin[0].type() == typeid(txin_gen), false, "coinbase transaction in the block has the wrong type"); - if(boost::get(b.miner_tx.vin[0]).height != height) - { - LOG_PRINT_RED_L1("The miner transaction in block has invalid height: " << boost::get(b.miner_tx.vin[0]).height << ", expected: " << height); - return false; - } - CHECK_AND_ASSERT_MES(b.miner_tx.unlock_time == height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, - false, - "coinbase transaction transaction has the wrong unlock time=" << b.miner_tx.unlock_time << ", expected " << height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW); - - //check outs overflow - if(!check_outs_overflow(b.miner_tx)) - { - LOG_PRINT_RED_L1("miner transaction has money overflow in block " << get_block_hash(b)); - return false; - } - - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins) const -{ - //validate reward - uint64_t money_in_use = 0; - BOOST_FOREACH(auto& o, b.miner_tx.vout) - money_in_use += o.amount; - - std::vector last_blocks_sizes; - get_last_n_blocks_sizes(last_blocks_sizes, CRYPTONOTE_REWARD_BLOCKS_WINDOW); - if (!get_block_reward(epee::misc_utils::median(last_blocks_sizes), cumulative_block_size, already_generated_coins, base_reward,0)) { - LOG_PRINT_L1("block size " << cumulative_block_size << " is bigger than allowed for this blockchain"); - return false; - } - if(base_reward + fee < money_in_use) - { - LOG_PRINT_L1("coinbase transaction spend too much money (" << print_money(money_in_use) << "). Block reward is " << print_money(base_reward + fee) << "(" << print_money(base_reward) << "+" << print_money(fee) << ")"); - return false; - } - if(base_reward + fee != money_in_use) - { - LOG_PRINT_L1("coinbase transaction doesn't use full amount of block reward: spent: " - << print_money(money_in_use) << ", block reward " << print_money(base_reward + fee) << "(" << print_money(base_reward) << "+" << print_money(fee) << ")"); - return false; - } - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::get_backward_blocks_sizes(size_t from_height, std::vector& sz, size_t count) const -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - CHECK_AND_ASSERT_MES(from_height < m_blocks.size(), false, "Internal error: get_backward_blocks_sizes called with from_height=" << from_height << ", blockchain height = " << m_blocks.size()); - - size_t start_offset = (from_height+1) - std::min((from_height+1), count); - for(size_t i = start_offset; i != from_height+1; i++) - sz.push_back(m_blocks[i].block_cumulative_size); - - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::get_last_n_blocks_sizes(std::vector& sz, size_t count) const -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(!m_blocks.size()) - return true; - return get_backward_blocks_sizes(m_blocks.size() -1, sz, count); -} -//------------------------------------------------------------------ -uint64_t blockchain_storage::get_current_cumulative_blocksize_limit() const -{ - return m_current_block_cumul_sz_limit; -} -//------------------------------------------------------------------ -bool blockchain_storage::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) const -{ - size_t median_size; - uint64_t already_generated_coins; - - CRITICAL_REGION_BEGIN(m_blockchain_lock); - b.major_version = CURRENT_BLOCK_MAJOR_VERSION; - b.minor_version = CURRENT_BLOCK_MINOR_VERSION; - b.prev_id = get_tail_id(); - b.timestamp = time(NULL); - height = m_blocks.size(); - diffic = get_difficulty_for_next_block(); - CHECK_AND_ASSERT_MES(diffic, false, "difficulty overhead."); - - median_size = m_current_block_cumul_sz_limit / 2; - already_generated_coins = m_blocks.back().already_generated_coins; - - CRITICAL_REGION_END(); - - size_t txs_size; - uint64_t fee; - if (!m_tx_pool->fill_block_template(b, median_size, already_generated_coins, txs_size, fee)) { - return false; - } -#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - size_t real_txs_size = 0; - uint64_t real_fee = 0; - CRITICAL_REGION_BEGIN(m_tx_pool->m_transactions_lock); - BOOST_FOREACH(crypto::hash &cur_hash, b.tx_hashes) { - auto cur_res = m_tx_pool->m_transactions.find(cur_hash); - if (cur_res == m_tx_pool->m_transactions.end()) { - LOG_ERROR("Creating block template: error: transaction not found"); - continue; - } - tx_memory_pool::tx_details &cur_tx = cur_res->second; - real_txs_size += cur_tx.blob_size; - real_fee += cur_tx.fee; - if (cur_tx.blob_size != get_object_blobsize(cur_tx.tx)) { - LOG_ERROR("Creating block template: error: invalid transaction size"); - } - uint64_t inputs_amount; - if (!get_inputs_money_amount(cur_tx.tx, inputs_amount)) { - LOG_ERROR("Creating block template: error: cannot get inputs amount"); - } else if (cur_tx.fee != inputs_amount - get_outs_money_amount(cur_tx.tx)) { - LOG_ERROR("Creating block template: error: invalid fee"); - } - } - if (txs_size != real_txs_size) { - LOG_ERROR("Creating block template: error: wrongly calculated transaction size"); - } - if (fee != real_fee) { - LOG_ERROR("Creating block template: error: wrongly calculated fee"); - } - CRITICAL_REGION_END(); - LOG_PRINT_L1("Creating block template: height " << height << - ", median size " << median_size << - ", already generated coins " << already_generated_coins << - ", transaction size " << txs_size << - ", fee " << fee); -#endif - - /* - two-phase miner transaction generation: we don't know exact block size until we prepare block, but we don't know reward until we know - block size, so first miner transaction generated with fake amount of money, and with phase we know think we know expected block size - */ - //make blocks coin-base tx looks close to real coinbase tx to get truthful blob size - bool r = construct_miner_tx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.miner_tx, ex_nonce, 11); - CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, first chance"); - size_t cumulative_size = txs_size + get_object_blobsize(b.miner_tx); -#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - LOG_PRINT_L1("Creating block template: miner tx size " << get_object_blobsize(b.miner_tx) << - ", cumulative size " << cumulative_size); -#endif - for (size_t try_count = 0; try_count != 10; ++try_count) { - r = construct_miner_tx(height, median_size, already_generated_coins, cumulative_size, fee, miner_address, b.miner_tx, ex_nonce, 11); - - CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, second chance"); - size_t coinbase_blob_size = get_object_blobsize(b.miner_tx); - if (coinbase_blob_size > cumulative_size - txs_size) { - cumulative_size = txs_size + coinbase_blob_size; -#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - LOG_PRINT_L1("Creating block template: miner tx size " << coinbase_blob_size << - ", cumulative size " << cumulative_size << " is greater then before"); -#endif - continue; - } - - if (coinbase_blob_size < cumulative_size - txs_size) { - size_t delta = cumulative_size - txs_size - coinbase_blob_size; -#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - LOG_PRINT_L1("Creating block template: miner tx size " << coinbase_blob_size << - ", cumulative size " << txs_size + coinbase_blob_size << - " is less then before, adding " << delta << " zero bytes"); -#endif - b.miner_tx.extra.insert(b.miner_tx.extra.end(), delta, 0); - //here could be 1 byte difference, because of extra field counter is varint, and it can become from 1-byte len to 2-bytes len. - if (cumulative_size != txs_size + get_object_blobsize(b.miner_tx)) { - CHECK_AND_ASSERT_MES(cumulative_size + 1 == txs_size + get_object_blobsize(b.miner_tx), false, "unexpected case: cumulative_size=" << cumulative_size << " + 1 is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.miner_tx)=" << get_object_blobsize(b.miner_tx)); - b.miner_tx.extra.resize(b.miner_tx.extra.size() - 1); - if (cumulative_size != txs_size + get_object_blobsize(b.miner_tx)) { - //fuck, not lucky, -1 makes varint-counter size smaller, in that case we continue to grow with cumulative_size - LOG_PRINT_RED("Miner tx creation has no luck with delta_extra size = " << delta << " and " << delta - 1 , LOG_LEVEL_2); - cumulative_size += delta - 1; - continue; - } - LOG_PRINT_GREEN("Setting extra for block: " << b.miner_tx.extra.size() << ", try_count=" << try_count, LOG_LEVEL_1); - } - } - CHECK_AND_ASSERT_MES(cumulative_size == txs_size + get_object_blobsize(b.miner_tx), false, "unexpected case: cumulative_size=" << cumulative_size << " is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.miner_tx)=" << get_object_blobsize(b.miner_tx)); -#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - LOG_PRINT_L1("Creating block template: miner tx size " << coinbase_blob_size << - ", cumulative size " << cumulative_size << " is now good"); -#endif - return true; - } - LOG_ERROR("Failed to create_block_template with " << 10 << " tries"); - return false; -} -//------------------------------------------------------------------ -bool blockchain_storage::complete_timestamps_vector(uint64_t start_top_height, std::vector& timestamps) const -{ - - if(timestamps.size() >= BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW) - return true; - - CRITICAL_REGION_LOCAL(m_blockchain_lock); - size_t need_elements = BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW - timestamps.size(); - CHECK_AND_ASSERT_MES(start_top_height < m_blocks.size(), false, "internal error: passed start_height = " << start_top_height << " not less then m_blocks.size()=" << m_blocks.size()); - size_t stop_offset = start_top_height > need_elements ? start_top_height - need_elements:0; - do - { - timestamps.push_back(m_blocks[start_top_height].bl.timestamp); - if(start_top_height == 0) - break; - --start_top_height; - }while(start_top_height != stop_offset); - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::handle_alternative_block(const block& b, const crypto::hash& id, block_verification_context& bvc) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - - uint64_t block_height = get_block_height(b); - if(0 == block_height) - { - LOG_PRINT_L1("Block with id: " << epee::string_tools::pod_to_hex(id) << " (as alternative) has wrong miner transaction"); - bvc.m_verifivation_failed = true; - return false; - } - if (!m_checkpoints.is_alternative_block_allowed(get_current_blockchain_height(), block_height)) - { - LOG_PRINT_RED_L1("Block with id: " << id - << ENDL << " can't be accepted for alternative chain, block height: " << block_height - << ENDL << " blockchain height: " << get_current_blockchain_height()); - bvc.m_verifivation_failed = true; - return false; - } - - //Block is not related with head of main chain - //first of all - look in alternative chains container - auto it_main_prev = m_blocks_index.find(b.prev_id); - auto it_prev = m_alternative_chains.find(b.prev_id); - if(it_prev != m_alternative_chains.end() || it_main_prev != m_blocks_index.end()) - { - //we have new block in alternative chain - - //build alternative subchain, front -> mainchain, back -> alternative head - blocks_ext_by_hash::iterator alt_it = it_prev; //m_alternative_chains.find() - std::list alt_chain; - std::vector timestamps; - while(alt_it != m_alternative_chains.end()) - { - alt_chain.push_front(alt_it); - timestamps.push_back(alt_it->second.bl.timestamp); - alt_it = m_alternative_chains.find(alt_it->second.bl.prev_id); - } - - if(alt_chain.size()) - { - //make sure that it has right connection to main chain - CHECK_AND_ASSERT_MES(m_blocks.size() > alt_chain.front()->second.height, false, "main blockchain wrong height"); - crypto::hash h = null_hash; - get_block_hash(m_blocks[alt_chain.front()->second.height - 1].bl, h); - CHECK_AND_ASSERT_MES(h == alt_chain.front()->second.bl.prev_id, false, "alternative chain has wrong connection to main chain"); - complete_timestamps_vector(alt_chain.front()->second.height - 1, timestamps); - }else - { - CHECK_AND_ASSERT_MES(it_main_prev != m_blocks_index.end(), false, "internal error: broken imperative condition it_main_prev != m_blocks_index.end()"); - complete_timestamps_vector(it_main_prev->second, timestamps); - } - //check timestamp correct - if(!check_block_timestamp(timestamps, b)) - { - LOG_PRINT_RED_L1("Block with id: " << id - << ENDL << " for alternative chain, has invalid timestamp: " << b.timestamp); - //add_block_as_invalid(b, id);//do not add blocks to invalid storage before proof of work check was passed - bvc.m_verifivation_failed = true; - return false; - } - - block_extended_info bei = boost::value_initialized(); - bei.bl = b; - bei.height = alt_chain.size() ? it_prev->second.height + 1 : it_main_prev->second + 1; - - bool is_a_checkpoint; - if(!m_checkpoints.check_block(bei.height, id, is_a_checkpoint)) - { - LOG_ERROR("CHECKPOINT VALIDATION FAILED"); - bvc.m_verifivation_failed = true; - return false; - } - - // Always check PoW for alternative blocks - m_is_in_checkpoint_zone = false; - difficulty_type current_diff = get_next_difficulty_for_alternative_chain(alt_chain, bei); - CHECK_AND_ASSERT_MES(current_diff, false, "!!!!!!! DIFFICULTY OVERHEAD !!!!!!!"); - crypto::hash proof_of_work = null_hash; - get_block_longhash(bei.bl, proof_of_work, bei.height); - if(!check_hash(proof_of_work, current_diff)) - { - LOG_PRINT_RED_L1("Block with id: " << id - << ENDL << " for alternative chain, does not have enough proof of work: " << proof_of_work - << ENDL << " expected difficulty: " << current_diff); - bvc.m_verifivation_failed = true; - return false; - } - - if(!prevalidate_miner_transaction(b, bei.height)) - { - LOG_PRINT_RED_L1("Block with id: " << epee::string_tools::pod_to_hex(id) - << " (as alternative) has incorrect miner transaction."); - bvc.m_verifivation_failed = true; - return false; - - } - - bei.cumulative_difficulty = alt_chain.size() ? it_prev->second.cumulative_difficulty: m_blocks[it_main_prev->second].cumulative_difficulty; - bei.cumulative_difficulty += current_diff; - -#ifdef _DEBUG - auto i_dres = m_alternative_chains.find(id); - CHECK_AND_ASSERT_MES(i_dres == m_alternative_chains.end(), false, "insertion of new alternative block returned as it already exist"); -#endif - auto i_res = m_alternative_chains.insert(blocks_ext_by_hash::value_type(id, bei)); - CHECK_AND_ASSERT_MES(i_res.second, false, "insertion of new alternative block returned as it already exist"); - alt_chain.push_back(i_res.first); - - if(is_a_checkpoint) - { - //do reorganize! - LOG_PRINT_GREEN("###### REORGANIZE on height: " << alt_chain.front()->second.height << " of " << m_blocks.size() - 1 << - ", checkpoint is found in alternative chain on height " << bei.height, LOG_LEVEL_0); - bool r = switch_to_alternative_blockchain(alt_chain, true); - if(r) bvc.m_added_to_main_chain = true; - else bvc.m_verifivation_failed = true; - return r; - }else if(m_blocks.back().cumulative_difficulty < bei.cumulative_difficulty) //check if difficulty bigger then in main chain - { - //do reorganize! - LOG_PRINT_GREEN("###### REORGANIZE on height: " << alt_chain.front()->second.height << " of " << m_blocks.size() - 1 << " with cum_difficulty " << m_blocks.back().cumulative_difficulty - << ENDL << " alternative blockchain size: " << alt_chain.size() << " with cum_difficulty " << bei.cumulative_difficulty, LOG_LEVEL_0); - bool r = switch_to_alternative_blockchain(alt_chain, false); - if(r) bvc.m_added_to_main_chain = true; - else bvc.m_verifivation_failed = true; - return r; - }else - { - LOG_PRINT_BLUE("----- BLOCK ADDED AS ALTERNATIVE ON HEIGHT " << bei.height - << ENDL << "id:\t" << id - << ENDL << "PoW:\t" << proof_of_work - << ENDL << "difficulty:\t" << current_diff, LOG_LEVEL_0); - return true; - } - }else - { - //block orphaned - bvc.m_marked_as_orphaned = true; - LOG_PRINT_RED_L1("Block recognized as orphaned and rejected, id = " << id); - } - - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs) const -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(start_offset >= m_blocks.size()) - return false; - for(size_t i = start_offset; i < start_offset + count && i < m_blocks.size();i++) - { - blocks.push_back(m_blocks[i].bl); - std::list missed_ids; - get_transactions(m_blocks[i].bl.tx_hashes, txs, missed_ids); - CHECK_AND_ASSERT_MES(!missed_ids.size(), false, "has missed transactions in own block in main blockchain"); - } - - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list& blocks) const -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(start_offset >= m_blocks.size()) - return false; - - for(size_t i = start_offset; i < start_offset + count && i < m_blocks.size();i++) - blocks.push_back(m_blocks[i].bl); - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - rsp.current_blockchain_height = get_current_blockchain_height(); - std::list blocks; - get_blocks(arg.blocks, blocks, rsp.missed_ids); - - BOOST_FOREACH(const auto& bl, blocks) - { - std::list missed_tx_id; - std::list txs; - get_transactions(bl.tx_hashes, txs, rsp.missed_ids); - CHECK_AND_ASSERT_MES(!missed_tx_id.size(), false, "Internal error: has missed missed_tx_id.size()=" << missed_tx_id.size() - << ENDL << "for block id = " << get_block_hash(bl)); - rsp.blocks.push_back(block_complete_entry()); - block_complete_entry& e = rsp.blocks.back(); - //pack block - e.block = t_serializable_object_to_blob(bl); - //pack transactions - BOOST_FOREACH(transaction& tx, txs) - e.txs.push_back(t_serializable_object_to_blob(tx)); - - } - //get another transactions, if need - std::list txs; - get_transactions(arg.txs, txs, rsp.missed_ids); - //pack aside transactions - BOOST_FOREACH(const auto& tx, txs) - rsp.txs.push_back(t_serializable_object_to_blob(tx)); - - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::get_alternative_blocks(std::list& blocks) const -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - - BOOST_FOREACH(const auto& alt_bl, m_alternative_chains) - { - blocks.push_back(alt_bl.second.bl); - } - return true; -} -//------------------------------------------------------------------ -size_t blockchain_storage::get_alternative_blocks_count() const -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - return m_alternative_chains.size(); -} -//------------------------------------------------------------------ -bool blockchain_storage::add_out_to_get_random_outs(const std::vector >& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) const -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - transactions_container::const_iterator tx_it = m_transactions.find(amount_outs[i].first); - CHECK_AND_ASSERT_MES(tx_it != m_transactions.end(), false, "internal error: transaction with id " << amount_outs[i].first << ENDL << - ", used in mounts global index for amount=" << amount << ": i=" << i << "not found in transactions index"); - CHECK_AND_ASSERT_MES(tx_it->second.tx.vout.size() > amount_outs[i].second, false, "internal error: in global outs index, transaction out index=" - << amount_outs[i].second << " more than transaction outputs = " << tx_it->second.tx.vout.size() << ", for tx id = " << amount_outs[i].first); - const transaction& tx = tx_it->second.tx; - CHECK_AND_ASSERT_MES(tx.vout[amount_outs[i].second].target.type() == typeid(txout_to_key), false, "unknown tx out type"); - - //check if transaction is unlocked - if(!is_tx_spendtime_unlocked(tx.unlock_time)) - return false; - - COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& oen = *result_outs.outs.insert(result_outs.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry()); - oen.global_amount_index = i; - oen.out_key = boost::get(tx.vout[amount_outs[i].second].target).key; - return true; -} -//------------------------------------------------------------------ -size_t blockchain_storage::find_end_of_allowed_index(const std::vector >& amount_outs) const -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(!amount_outs.size()) - return 0; - size_t i = amount_outs.size(); - do - { - --i; - transactions_container::const_iterator it = m_transactions.find(amount_outs[i].first); - CHECK_AND_ASSERT_MES(it != m_transactions.end(), 0, "internal error: failed to find transaction from outputs index with tx_id=" << amount_outs[i].first); - if(it->second.m_keeper_block_height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE <= get_current_blockchain_height() ) - return i+1; - } while (i != 0); - return 0; -} -//------------------------------------------------------------------ -bool blockchain_storage::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) const -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - BOOST_FOREACH(uint64_t amount, req.amounts) - { - COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs = *res.outs.insert(res.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount()); - result_outs.amount = amount; - auto it = m_outputs.find(amount); - if(it == m_outputs.end()) - { - LOG_PRINT_L1("COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS: not outs for amount " << amount << ", wallet should use some real outs when it lookup for some mix, so, at least one out for this amount should exist"); - continue;//actually this is strange situation, wallet should use some real outs when it lookup for some mix, so, at least one out for this amount should exist - } - const std::vector >& amount_outs = it->second; - //it is not good idea to use top fresh outs, because it increases possibility of transaction canceling on split - //lets find upper bound of not fresh outs - size_t up_index_limit = find_end_of_allowed_index(amount_outs); - CHECK_AND_ASSERT_MES(up_index_limit <= amount_outs.size(), false, "internal error: find_end_of_allowed_index returned wrong index=" << up_index_limit << ", with amount_outs.size = " << amount_outs.size()); - if(amount_outs.size() > req.outs_count) - { - std::set used; - size_t try_count = 0; - for(uint64_t j = 0; j != req.outs_count && try_count < up_index_limit;) - { - // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit - uint64_t r = crypto::rand() % ((uint64_t)1 << 53); - double frac = std::sqrt((double)r / ((uint64_t)1 << 53)); - size_t i = (size_t)(frac*up_index_limit); - // just in case rounding up to 1 occurs after sqrt - if (i == up_index_limit) - --i; - if(used.count(i)) - continue; - bool added = add_out_to_get_random_outs(amount_outs, result_outs, amount, i); - used.insert(i); - if(added) - ++j; - ++try_count; - } - }else - { - for(size_t i = 0; i != up_index_limit; i++) - add_out_to_get_random_outs(amount_outs, result_outs, amount, i); - } - } - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::find_blockchain_supplement(const std::list& qblock_ids, uint64_t& starter_offset) const -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - - if(!qblock_ids.size() /*|| !req.m_total_height*/) - { - LOG_PRINT_L1("Client sent wrong NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << qblock_ids.size() << /*", m_height=" << req.m_total_height <<*/ ", dropping connection"); - return false; - } - //check genesis match - if(qblock_ids.back() != get_block_hash(m_blocks[0].bl)) - { - LOG_PRINT_L1("Client sent wrong NOTIFY_REQUEST_CHAIN: genesis block missmatch: " << ENDL << "id: " - << qblock_ids.back() << ", " << ENDL << "expected: " << get_block_hash(m_blocks[0].bl) - << "," << ENDL << " dropping connection"); - return false; - } - - /* Figure out what blocks we should request to get state_normal */ - size_t i = 0; - auto bl_it = qblock_ids.begin(); - auto block_index_it = m_blocks_index.find(*bl_it); - for(; bl_it != qblock_ids.end(); bl_it++, i++) - { - block_index_it = m_blocks_index.find(*bl_it); - if(block_index_it != m_blocks_index.end()) - break; - } - - if(bl_it == qblock_ids.end()) - { - LOG_PRINT_L1("Internal error handling connection, can't find split point"); - return false; - } - - if(block_index_it == m_blocks_index.end()) - { - //this should NEVER happen, but, dose of paranoia in such cases is not too bad - LOG_PRINT_L1("Internal error handling connection, can't find split point"); - return false; - } - - //we start to put block ids INCLUDING last known id, just to make other side be sure - starter_offset = block_index_it->second; - return true; -} -//------------------------------------------------------------------ -uint64_t blockchain_storage::block_difficulty(size_t i) const -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - CHECK_AND_ASSERT_MES(i < m_blocks.size(), false, "wrong block index i = " << i << " at blockchain_storage::block_difficulty()"); - if(i == 0) - return m_blocks[i].cumulative_difficulty; - - return m_blocks[i].cumulative_difficulty - m_blocks[i-1].cumulative_difficulty; -} -//------------------------------------------------------------------ -double blockchain_storage::get_avg_block_size( size_t count) const -{ - if (count > get_current_blockchain_height()) return 500; - - double average = 0; - _dbg1_c("net/blksize", "HEIGHT: " << get_current_blockchain_height()); - _dbg1_c("net/blksize", "BLOCK ID BY HEIGHT: " << get_block_id_by_height(get_current_blockchain_height()) ); - _dbg1_c("net/blksize", "BLOCK TAIL ID: " << get_tail_id() ); - std::vector size_vector; - - get_backward_blocks_sizes(get_current_blockchain_height() - count, size_vector, count); - - std::vector::iterator it; - it = size_vector.begin(); - while (it != size_vector.end()) { - average += *it; - _dbg2_c("net/blksize", "VECTOR ELEMENT: " << (*it) ); - it++; - } - average = average / count; - _dbg1_c("net/blksize", "VECTOR SIZE: " << size_vector.size() << " average=" << average); - - return average; -} -//------------------------------------------------------------------ -void blockchain_storage::print_blockchain(uint64_t start_index, uint64_t end_index) const -{ - std::stringstream ss; - CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(start_index >=m_blocks.size()) - { - LOG_PRINT_L1("Wrong starter index set: " << start_index << ", expected max index " << m_blocks.size()-1); - return; - } - - for(size_t i = start_index; i != m_blocks.size() && i != end_index; i++) - { - ss << "height " << i << ", timestamp " << m_blocks[i].bl.timestamp << ", cumul_dif " << m_blocks[i].cumulative_difficulty << ", cumul_size " << m_blocks[i].block_cumulative_size - << "\nid\t\t" << get_block_hash(m_blocks[i].bl) - << "\ndifficulty\t\t" << block_difficulty(i) << ", nonce " << m_blocks[i].bl.nonce << ", tx_count " << m_blocks[i].bl.tx_hashes.size() << ENDL; - } - LOG_PRINT_L1("Current blockchain:" << ENDL << ss.str()); - LOG_PRINT_L0("Blockchain printed with log level 1"); -} -//------------------------------------------------------------------ -void blockchain_storage::print_blockchain_index() const -{ - std::stringstream ss; - CRITICAL_REGION_LOCAL(m_blockchain_lock); - BOOST_FOREACH(const blocks_by_id_index::value_type& v, m_blocks_index) - ss << "id\t\t" << v.first << " height" << v.second << ENDL << ""; - - LOG_PRINT_L0("Current blockchain index:" << ENDL << ss.str()); -} -//------------------------------------------------------------------ -void blockchain_storage::print_blockchain_outs(const std::string& file) const -{ - std::stringstream ss; - CRITICAL_REGION_LOCAL(m_blockchain_lock); - BOOST_FOREACH(const outputs_container::value_type& v, m_outputs) - { - const std::vector >& vals = v.second; - if(vals.size()) - { - ss << "amount: " << v.first << ENDL; - for(size_t i = 0; i != vals.size(); i++) - ss << "\t" << vals[i].first << ": " << vals[i].second << ENDL; - } - } - if(epee::file_io_utils::save_string_to_file(file, ss.str())) - { - LOG_PRINT_L0("Current outputs index writen to file: " << file); - }else - { - LOG_PRINT_L0("Failed to write current outputs index to file: " << file); - } -} -//------------------------------------------------------------------ -bool blockchain_storage::find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(!find_blockchain_supplement(qblock_ids, resp.start_height)) - return false; - - resp.total_height = get_current_blockchain_height(); - size_t count = 0; - for(size_t i = resp.start_height; i != m_blocks.size() && count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT; i++, count++) - resp.m_block_ids.push_back(get_block_hash(m_blocks[i].bl)); - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(req_start_block > 0) { - start_height = req_start_block; - } else { - if(!find_blockchain_supplement(qblock_ids, start_height)) - return false; - } - - total_height = get_current_blockchain_height(); - size_t count = 0; - for(size_t i = start_height; i != m_blocks.size() && count < max_count; i++, count++) - { - blocks.resize(blocks.size()+1); - blocks.back().first = m_blocks[i].bl; - std::list mis; - get_transactions(m_blocks[i].bl.tx_hashes, blocks.back().second, mis); - CHECK_AND_ASSERT_MES(!mis.size(), false, "internal error, transaction from block not found"); - } - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::add_block_as_invalid(const block& bl, const crypto::hash& h) -{ - block_extended_info bei = AUTO_VAL_INIT(bei); - bei.bl = bl; - return add_block_as_invalid(bei, h); -} -//------------------------------------------------------------------ -bool blockchain_storage::add_block_as_invalid(const block_extended_info& bei, const crypto::hash& h) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - auto i_res = m_invalid_blocks.insert(std::map::value_type(h, bei)); - CHECK_AND_ASSERT_MES(i_res.second, false, "at insertion invalid by tx returned status existed"); - LOG_PRINT_L1("BLOCK ADDED AS INVALID: " << h << ENDL << ", prev_id=" << bei.bl.prev_id << ", m_invalid_blocks count=" << m_invalid_blocks.size()); - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::have_block(const crypto::hash& id) const -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(m_blocks_index.count(id)) - return true; - if(m_alternative_chains.count(id)) - return true; - /*if(m_orphaned_blocks.get().count(id)) - return true;*/ - - /*if(m_orphaned_by_tx.count(id)) - return true;*/ - if(m_invalid_blocks.count(id)) - return true; - - return false; -} -//------------------------------------------------------------------ -bool blockchain_storage::handle_block_to_main_chain(const block& bl, block_verification_context& bvc) -{ - crypto::hash id = get_block_hash(bl); - return handle_block_to_main_chain(bl, id, bvc); -} -//------------------------------------------------------------------ -bool blockchain_storage::push_transaction_to_global_outs_index(const transaction& tx, const crypto::hash& tx_id, std::vector& global_indexes) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - size_t i = 0; - BOOST_FOREACH(const auto& ot, tx.vout) - { - outputs_container::mapped_type& amount_index = m_outputs[ot.amount]; - amount_index.push_back(std::pair(tx_id, i)); - global_indexes.push_back(amount_index.size()-1); - ++i; - } - return true; -} -//------------------------------------------------------------------ -size_t blockchain_storage::get_total_transactions() const -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - return m_transactions.size(); -} -//------------------------------------------------------------------ -bool blockchain_storage::get_outs(uint64_t amount, std::list& pkeys) const -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - auto it = m_outputs.find(amount); - if(it == m_outputs.end()) - return true; - - BOOST_FOREACH(const auto& out_entry, it->second) - { - auto tx_it = m_transactions.find(out_entry.first); - CHECK_AND_ASSERT_MES(tx_it != m_transactions.end(), false, "transactions outs global index consistency broken: wrong tx id in index"); - CHECK_AND_ASSERT_MES(tx_it->second.tx.vout.size() > out_entry.second, false, "transactions outs global index consistency broken: index in tx_outx more then size"); - CHECK_AND_ASSERT_MES(tx_it->second.tx.vout[out_entry.second].target.type() == typeid(txout_to_key), false, "transactions outs global index consistency broken: index in tx_outx more then size"); - pkeys.push_back(boost::get(tx_it->second.tx.vout[out_entry.second].target).key); - } - - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::pop_transaction_from_global_index(const transaction& tx, const crypto::hash& tx_id) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - size_t i = tx.vout.size()-1; - BOOST_REVERSE_FOREACH(const auto& ot, tx.vout) - { - auto it = m_outputs.find(ot.amount); - CHECK_AND_ASSERT_MES(it != m_outputs.end(), false, "transactions outs global index consistency broken"); - CHECK_AND_ASSERT_MES(it->second.size(), false, "transactions outs global index: empty index for amount: " << ot.amount); - CHECK_AND_ASSERT_MES(it->second.back().first == tx_id , false, "transactions outs global index consistency broken: tx id missmatch"); - CHECK_AND_ASSERT_MES(it->second.back().second == i, false, "transactions outs global index consistency broken: in transaction index missmatch"); - it->second.pop_back(); - --i; - } - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::add_transaction_from_block(const transaction& tx, const crypto::hash& tx_id, const crypto::hash& bl_id, uint64_t bl_height, size_t blob_size) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - struct add_transaction_input_visitor: public boost::static_visitor - { - key_images_container& m_spent_keys; - const crypto::hash& m_tx_id; - const crypto::hash& m_bl_id; - add_transaction_input_visitor(key_images_container& spent_keys, const crypto::hash& tx_id, const crypto::hash& bl_id):m_spent_keys(spent_keys), m_tx_id(tx_id), m_bl_id(bl_id) - {} - bool operator()(const txin_to_key& in) const - { - const crypto::key_image& ki = in.k_image; - auto r = m_spent_keys.insert(ki); - if(!r.second) - { - //double spend detected - LOG_PRINT_L1("tx with id: " << m_tx_id << " in block id: " << m_bl_id << " has input marked as spent with key image: " << ki << ", block declined"); - return false; - } - return true; - } - - bool operator()(const txin_gen& tx) const{return true;} - bool operator()(const txin_to_script& tx) const{return false;} - bool operator()(const txin_to_scripthash& tx) const{return false;} - }; - - BOOST_FOREACH(const txin_v& in, tx.vin) - { - if(!boost::apply_visitor(add_transaction_input_visitor(m_spent_keys, tx_id, bl_id), in)) - { - LOG_PRINT_L1("critical internal error: add_transaction_input_visitor failed. but here key_images should be checked"); - purge_transaction_keyimages_from_blockchain(tx, false); - return false; - } - } - transaction_chain_entry ch_e; - ch_e.m_keeper_block_height = bl_height; - ch_e.m_blob_size = blob_size; - ch_e.tx = tx; - auto i_r = m_transactions.insert(std::pair(tx_id, ch_e)); - if(!i_r.second) - { - LOG_PRINT_L1("tx with id: " << tx_id << " in block id: " << bl_id << " already in blockchain"); - return false; - } - bool r = push_transaction_to_global_outs_index(tx, tx_id, i_r.first->second.m_global_output_indexes); - CHECK_AND_ASSERT_MES(r, false, "failed to return push_transaction_to_global_outs_index tx id " << tx_id); - LOG_PRINT_L2("Added transaction to blockchain history:" << ENDL - << "tx_id: " << tx_id << ENDL - << "inputs: " << tx.vin.size() << ", outs: " << tx.vout.size() << ", spend money: " << print_money(get_outs_money_amount(tx)) << "(fee: " << (is_coinbase(tx) ? "0[coinbase]" : print_money(get_tx_fee(tx))) << ")"); - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs) const -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - auto it = m_transactions.find(tx_id); - if(it == m_transactions.end()) - { - LOG_PRINT_RED_L1("warning: get_tx_outputs_gindexs failed to find transaction with id = " << tx_id); - return false; - } - - CHECK_AND_ASSERT_MES(it->second.m_global_output_indexes.size(), false, "internal error: global indexes for transaction " << tx_id << " is empty"); - indexs = it->second.m_global_output_indexes; - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::check_tx_inputs(const transaction& tx, uint64_t& max_used_block_height, crypto::hash& max_used_block_id) const -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - bool res = check_tx_inputs(tx, &max_used_block_height); - if(!res) return false; - CHECK_AND_ASSERT_MES(max_used_block_height < m_blocks.size(), false, "internal error: max used block index=" << max_used_block_height << " is not less then blockchain size = " << m_blocks.size()); - get_block_hash(m_blocks[max_used_block_height].bl, max_used_block_id); - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::have_tx_keyimges_as_spent(const transaction &tx) const -{ - BOOST_FOREACH(const txin_v& in, tx.vin) - { - CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, in_to_key, true); - if(have_tx_keyimg_as_spent(in_to_key.k_image)) - return true; - } - return false; -} -//------------------------------------------------------------------ -bool blockchain_storage::check_tx_inputs(const transaction& tx, uint64_t* pmax_used_block_height) const -{ - crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx); - return check_tx_inputs(tx, tx_prefix_hash, pmax_used_block_height); -} -//------------------------------------------------------------------ -bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t* pmax_used_block_height) const -{ - size_t sig_index = 0; - if(pmax_used_block_height) - *pmax_used_block_height = 0; - - BOOST_FOREACH(const auto& txin, tx.vin) - { - CHECK_AND_ASSERT_MES(txin.type() == typeid(txin_to_key), false, "wrong type id in tx input at blockchain_storage::check_tx_inputs"); - const txin_to_key& in_to_key = boost::get(txin); - - CHECK_AND_ASSERT_MES(in_to_key.key_offsets.size(), false, "empty in_to_key.key_offsets in transaction with id " << get_transaction_hash(tx)); - - if(have_tx_keyimg_as_spent(in_to_key.k_image)) - { - LOG_PRINT_L1("Key image already spent in blockchain: " << epee::string_tools::pod_to_hex(in_to_key.k_image)); - return false; - } - - CHECK_AND_ASSERT_MES(sig_index < tx.signatures.size(), false, "wrong transaction: not signature entry for input with index= " << sig_index); - if(!check_tx_input(in_to_key, tx_prefix_hash, tx.signatures[sig_index], pmax_used_block_height)) - { - LOG_PRINT_L1("Failed to check ring signature for tx " << get_transaction_hash(tx)); - return false; - } - - sig_index++; - } - - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::is_tx_spendtime_unlocked(uint64_t unlock_time) const -{ - if(unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER) - { - //interpret as block index - if(get_current_blockchain_height()-1 + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS >= unlock_time) - return true; - else - return false; - }else - { - //interpret as time - uint64_t current_time = static_cast(time(NULL)); - if(current_time + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V1 >= unlock_time) - return true; - else - return false; - } - return false; -} -//------------------------------------------------------------------ -bool blockchain_storage::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector& sig, uint64_t* pmax_related_block_height) const -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - - struct outputs_visitor - { - std::vector& m_results_collector; - const blockchain_storage& m_bch; - outputs_visitor(std::vector& results_collector, const blockchain_storage& bch):m_results_collector(results_collector), m_bch(bch) - {} - bool handle_output(const transaction& tx, const tx_out& out) - { - //check tx unlock time - if(!m_bch.is_tx_spendtime_unlocked(tx.unlock_time)) - { - LOG_PRINT_L1("One of outputs for one of inputs has wrong tx.unlock_time = " << tx.unlock_time); - return false; - } - - if(out.target.type() != typeid(txout_to_key)) - { - LOG_PRINT_L1("Output has wrong type id, which=" << out.target.which()); - return false; - } - - m_results_collector.push_back(&boost::get(out.target).key); - return true; - } - }; - - //check ring signature - std::vector output_keys; - outputs_visitor vi(output_keys, *this); - if(!scan_outputkeys_for_indexes(txin, vi, pmax_related_block_height)) - { - LOG_PRINT_L1("Failed to get output keys for tx with amount = " << print_money(txin.amount) << " and count indexes " << txin.key_offsets.size()); - return false; - } - - if(txin.key_offsets.size() != output_keys.size()) - { - LOG_PRINT_L1("Output keys for tx with amount = " << txin.amount << " and count indexes " << txin.key_offsets.size() << " returned wrong keys count " << output_keys.size()); - return false; - } - CHECK_AND_ASSERT_MES(sig.size() == output_keys.size(), false, "internal error: tx signatures count=" << sig.size() << " mismatch with outputs keys count for inputs=" << output_keys.size()); - if(m_is_in_checkpoint_zone) - return true; - return crypto::check_ring_signature(tx_prefix_hash, txin.k_image, output_keys, sig.data()); -} -//------------------------------------------------------------------ -uint64_t blockchain_storage::get_adjusted_time() const -{ - //TODO: add collecting median time - return time(NULL); -} -//------------------------------------------------------------------ -bool blockchain_storage::check_block_timestamp_main(const block& b) const -{ - if(b.timestamp > get_adjusted_time() + CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT) - { - LOG_PRINT_L1("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", bigger than adjusted time + 2 hours"); - return false; - } - - std::vector timestamps; - size_t offset = m_blocks.size() <= BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW ? 0: m_blocks.size()- BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW; - for(;offset!= m_blocks.size(); ++offset) - timestamps.push_back(m_blocks[offset].bl.timestamp); - - return check_block_timestamp(std::move(timestamps), b); -} -//------------------------------------------------------------------ -bool blockchain_storage::check_block_timestamp(std::vector timestamps, const block& b) const -{ - if(timestamps.size() < BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW) - return true; - - uint64_t median_ts = epee::misc_utils::median(timestamps); - - if(b.timestamp < median_ts) - { - LOG_PRINT_L1("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", less than median of last " << BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW << " blocks, " << median_ts); - return false; - } - - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypto::hash& id, block_verification_context& bvc) -{ - TIME_MEASURE_START(block_processing_time); - CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(bl.prev_id != get_tail_id()) - { - LOG_PRINT_L1("Block with id: " << id << ENDL - << "has wrong prev_id: " << bl.prev_id << ENDL - << "expected: " << get_tail_id()); - return false; - } - - if(!check_block_timestamp_main(bl)) - { - LOG_PRINT_L1("Block with id: " << id << ENDL - << "has invalid timestamp: " << bl.timestamp); - //add_block_as_invalid(bl, id);//do not add blocks to invalid storage befor proof of work check was passed - bvc.m_verifivation_failed = true; - return false; - } - - //check proof of work - TIME_MEASURE_START(target_calculating_time); - difficulty_type current_diffic = get_difficulty_for_next_block(); - CHECK_AND_ASSERT_MES(current_diffic, false, "!!!!!!!!! difficulty overhead !!!!!!!!!"); - TIME_MEASURE_FINISH(target_calculating_time); - TIME_MEASURE_START(longhash_calculating_time); - crypto::hash proof_of_work = null_hash; - - // Formerly the code below contained an if loop with the following condition - // !m_checkpoints.is_in_checkpoint_zone(get_current_blockchain_height()) - // however, this caused the daemon to not bother checking PoW for blocks - // before checkpoints, which is very dangerous behaviour. We moved the PoW - // validation out of the next chunk of code to make sure that we correctly - // check PoW now. - proof_of_work = get_block_longhash(bl, m_blocks.size()); - - if(!check_hash(proof_of_work, current_diffic)) - { - LOG_PRINT_L1("Block with id: " << id << ENDL - << "does not have enough proof of work: " << proof_of_work << ENDL - << "unexpected difficulty: " << current_diffic ); - bvc.m_verifivation_failed = true; - return false; - } - - // If we're at a checkpoint, ensure that our hardcoded checkpoint hash - // is correct. - if(m_checkpoints.is_in_checkpoint_zone(get_current_blockchain_height())) - { - if(!m_checkpoints.check_block(get_current_blockchain_height(), id)) - { - LOG_ERROR("CHECKPOINT VALIDATION FAILED"); - bvc.m_verifivation_failed = true; - return false; - } - } - - TIME_MEASURE_FINISH(longhash_calculating_time); - - if(!prevalidate_miner_transaction(bl, m_blocks.size())) - { - LOG_PRINT_L1("Block with id: " << id - << " failed to pass prevalidation"); - bvc.m_verifivation_failed = true; - return false; - } - crypto::hash coinbase_hash = null_hash; - size_t coinbase_blob_size = 0; - get_transaction_hash(bl.miner_tx, coinbase_hash, coinbase_blob_size); - size_t cumulative_block_size = coinbase_blob_size; - //process transactions - if(!add_transaction_from_block(bl.miner_tx, coinbase_hash, id, get_current_blockchain_height(), coinbase_blob_size)) - { - LOG_PRINT_L1("Block with id: " << id << " failed to add transaction to blockchain storage"); - bvc.m_verifivation_failed = true; - return false; - } - size_t tx_processed_count = 0; - uint64_t fee_summary = 0; - BOOST_FOREACH(const crypto::hash& tx_id, bl.tx_hashes) - { - transaction tx; - size_t blob_size = 0; - uint64_t fee = 0; - bool relayed = false; - if(!m_tx_pool->take_tx(tx_id, tx, blob_size, fee, relayed)) - { - LOG_PRINT_L1("Block with id: " << id << "has at least one unknown transaction with id: " << tx_id); - purge_block_data_from_blockchain(bl, tx_processed_count); - //add_block_as_invalid(bl, id); - bvc.m_verifivation_failed = true; - return false; - } - if(!check_tx_inputs(tx)) - { - LOG_PRINT_L1("Block with id: " << id << "has at least one transaction (id: " << tx_id << ") with wrong inputs."); - cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); - bool add_res = m_tx_pool->add_tx(tx, tvc, true, relayed, 1); - CHECK_AND_ASSERT_MES2(add_res, "handle_block_to_main_chain: failed to add transaction back to transaction pool"); - purge_block_data_from_blockchain(bl, tx_processed_count); - add_block_as_invalid(bl, id); - LOG_PRINT_L1("Block with id " << id << " added as invalid becouse of wrong inputs in transactions"); - bvc.m_verifivation_failed = true; - return false; - } - - if(!add_transaction_from_block(tx, tx_id, id, get_current_blockchain_height(), blob_size)) - { - LOG_PRINT_L1("Block with id: " << id << " failed to add transaction to blockchain storage"); - cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); - bool add_res = m_tx_pool->add_tx(tx, tvc, true, relayed, 1); - CHECK_AND_ASSERT_MES2(add_res, "handle_block_to_main_chain: failed to add transaction back to transaction pool"); - purge_block_data_from_blockchain(bl, tx_processed_count); - bvc.m_verifivation_failed = true; - return false; - } - fee_summary += fee; - cumulative_block_size += blob_size; - ++tx_processed_count; - } - uint64_t base_reward = 0; - uint64_t already_generated_coins = m_blocks.size() ? m_blocks.back().already_generated_coins:0; - if(!validate_miner_transaction(bl, cumulative_block_size, fee_summary, base_reward, already_generated_coins)) - { - LOG_PRINT_L1("Block with id: " << id - << " has incorrect miner transaction"); - purge_block_data_from_blockchain(bl, tx_processed_count); - bvc.m_verifivation_failed = true; - return false; - } - - - block_extended_info bei = boost::value_initialized(); - bei.bl = bl; - bei.block_cumulative_size = cumulative_block_size; - bei.cumulative_difficulty = current_diffic; - - // In the "tail" state when the minimum subsidy (implemented in get_block_reward) is in effect, the number of - // coins will eventually exceed MONEY_SUPPLY and overflow a uint64. To prevent overflow, cap already_generated_coins - // at MONEY_SUPPLY. already_generated_coins is only used to compute the block subsidy and MONEY_SUPPLY yields a - // subsidy of 0 under the base formula and therefore the minimum subsidy >0 in the tail state. - - bei.already_generated_coins = base_reward < (MONEY_SUPPLY-already_generated_coins) ? already_generated_coins + base_reward : MONEY_SUPPLY; - - if(m_blocks.size()) - bei.cumulative_difficulty += m_blocks.back().cumulative_difficulty; - - bei.height = m_blocks.size(); - - auto ind_res = m_blocks_index.insert(std::pair(id, bei.height)); - if(!ind_res.second) - { - LOG_PRINT_L1("block with id: " << id << " already in block indexes"); - purge_block_data_from_blockchain(bl, tx_processed_count); - bvc.m_verifivation_failed = true; - return false; - } - - m_blocks.push_back(bei); - update_next_comulative_size_limit(); - TIME_MEASURE_FINISH(block_processing_time); - LOG_PRINT_L1("+++++ BLOCK SUCCESSFULLY ADDED" << ENDL << "id:\t" << id - << ENDL << "PoW:\t" << proof_of_work - << ENDL << "HEIGHT " << bei.height << ", difficulty:\t" << current_diffic - << ENDL << "block reward: " << print_money(fee_summary + base_reward) << "(" << print_money(base_reward) << " + " << print_money(fee_summary) - << "), coinbase_blob_size: " << coinbase_blob_size << ", cumulative size: " << cumulative_block_size - << ", " << block_processing_time << "("<< target_calculating_time << "/" << longhash_calculating_time << ")ms"); - - epee::net_utils::data_logger::get_instance().add_data("blockchain_processing_time", block_processing_time); - - bvc.m_added_to_main_chain = true; - /*if(!m_orphanes_reorganize_in_work) - review_orphaned_blocks_with_new_block_id(id, true);*/ - - m_tx_pool->on_blockchain_inc(bei.height, id); - //LOG_PRINT_L0("BLOCK: " << ENDL << "" << dump_obj_as_json(bei.bl)); - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::update_next_comulative_size_limit() -{ - std::vector sz; - get_last_n_blocks_sizes(sz, CRYPTONOTE_REWARD_BLOCKS_WINDOW); - - uint64_t median = epee::misc_utils::median(sz); - if(median <= CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1) - median = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1; - - m_current_block_cumul_sz_limit = median*2; - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::add_new_block(const block& bl_, block_verification_context& bvc) -{ - //copy block here to let modify block.target - block bl = bl_; - crypto::hash id = get_block_hash(bl); - CRITICAL_REGION_LOCAL(*m_tx_pool);//to avoid deadlock lets lock tx_pool for whole add/reorganize process - CRITICAL_REGION_LOCAL1(m_blockchain_lock); - if(have_block(id)) - { - LOG_PRINT_L3("block with id = " << id << " already exists"); - bvc.m_already_exists = true; - return false; - } - - //check that block refers to chain tail - if(!(bl.prev_id == get_tail_id())) - { - //chain switching or wrong block - bvc.m_added_to_main_chain = false; - return handle_alternative_block(bl, id, bvc); - //never relay alternative blocks - } - - return handle_block_to_main_chain(bl, id, bvc); -} -//------------------------------------------------------------------ -void blockchain_storage::check_against_checkpoints(const checkpoints& points, bool enforce) -{ - const auto& pts = points.get_points(); - - for (const auto& pt : pts) - { - // if the checkpoint is for a block we don't have yet, move on - if (pt.first >= m_blocks.size()) - { - continue; - } - - if (!points.check_block(pt.first, get_block_hash(m_blocks[pt.first].bl))) - { - // if asked to enforce checkpoints, roll back to a couple of blocks before the checkpoint - if (enforce) - { - LOG_ERROR("Local blockchain failed to pass a checkpoint, rolling back!"); - std::list empty; - rollback_blockchain_switching(empty, pt.first - 2); - } - else - { - LOG_ERROR("WARNING: local blockchain failed to pass a MoneroPulse checkpoint, and you could be on a fork. You should either sync up from scratch, OR download a fresh blockchain bootstrap, OR enable checkpoint enforcing with the --enforce-dns-checkpointing command-line option"); - } - } - } -} -//------------------------------------------------------------------ -// returns false if any of the checkpoints loading returns false. -// That should happen only if a checkpoint is added that conflicts -// with an existing checkpoint. -bool blockchain_storage::update_checkpoints(const std::string& file_path, bool check_dns) -{ - if (!m_checkpoints.load_checkpoints_from_json(file_path)) - { - return false; - } - - // if we're checking both dns and json, load checkpoints from dns. - // if we're not hard-enforcing dns checkpoints, handle accordingly - if (m_enforce_dns_checkpoints && check_dns) - { - if (!m_checkpoints.load_checkpoints_from_dns()) - { - return false; - } - } - else if (check_dns) - { - checkpoints dns_points; - dns_points.load_checkpoints_from_dns(m_testnet); - if (m_checkpoints.check_for_conflicts(dns_points)) - { - check_against_checkpoints(dns_points, false); - } - else - { - LOG_PRINT_L0("One or more checkpoints fetched from DNS conflicted with existing checkpoints!"); - } - } - - check_against_checkpoints(m_checkpoints, true); - - return true; -} -//------------------------------------------------------------------ -void blockchain_storage::set_enforce_dns_checkpoints(bool enforce_checkpoints) -{ - m_enforce_dns_checkpoints = enforce_checkpoints; -} -//------------------------------------------------------------------ -bool blockchain_storage::flush_txes_from_pool(const std::list &txids) -{ - bool res = true; - for (const auto &txid: txids) - { - cryptonote::transaction tx; - size_t blob_size; - uint64_t fee; - bool relayed; - LOG_PRINT_L1("Removing txid " << txid << " from the pool"); - if(!m_tx_pool->take_tx(txid, tx, blob_size, fee, relayed)) - { - LOG_PRINT_L0("Failed to remove txid " << txid << " from the pool"); - res = false; - } - } - return res; -} -//------------------------------------------------------------------ -bool blockchain_storage::for_all_key_images(std::function f) const -{ - for (key_images_container::const_iterator i = m_spent_keys.begin(); i != m_spent_keys.end(); ++i) { - if (!f(*i)) - return false; - } - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::for_all_blocks(std::function f) const -{ - for (blocks_container::const_iterator i = m_blocks.begin(); i != m_blocks.end(); ++i) { - crypto::hash hash; - get_block_hash (i->bl, hash); - if (!f(i->height, hash, i->bl)) - return false; - } - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::for_all_transactions(std::function f) const -{ - for (transactions_container::const_iterator i = m_transactions.begin(); i != m_transactions.end(); ++i) { - if (!f(i->first, i->second.tx)) - return false; - } - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::for_all_outputs(std::function f) const -{ - for (outputs_container::const_iterator i = m_outputs.begin(); i != m_outputs.end(); ++i) { - for (size_t n = 0; n < i->second.size(); ++n) { - if (!f(i->first, i->second[n].first, i->second[n].second)) - return false; - } - } - return true; -} diff --git a/src/cryptonote_core/blockchain_storage.h b/src/cryptonote_core/blockchain_storage.h deleted file mode 100644 index 878202cf1..000000000 --- a/src/cryptonote_core/blockchain_storage.h +++ /dev/null @@ -1,377 +0,0 @@ -// Copyright (c) 2014-2016, 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 - -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "syncobj.h" -#include "string_tools.h" -#include "tx_pool.h" -#include "cryptonote_basic.h" -#include "common/util.h" -#include "cryptonote_protocol/cryptonote_protocol_defs.h" -#include "rpc/core_rpc_server_commands_defs.h" -#include "difficulty.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "verification_context.h" -#include "crypto/hash.h" -#include "checkpoints.h" - -namespace cryptonote -{ - - /************************************************************************/ - /* */ - /************************************************************************/ - class blockchain_storage - { - public: - struct transaction_chain_entry - { - transaction tx; - uint64_t m_keeper_block_height; - size_t m_blob_size; - std::vector m_global_output_indexes; - }; - - struct block_extended_info - { - block bl; - uint64_t height; - size_t block_cumulative_size; - difficulty_type cumulative_difficulty; - uint64_t already_generated_coins; - }; - - blockchain_storage(tx_memory_pool* tx_pool):m_tx_pool(tx_pool), m_current_block_cumul_sz_limit(0), m_is_in_checkpoint_zone(false), m_is_blockchain_storing(false), m_enforce_dns_checkpoints(false) - {}; - - bool init() { return init(tools::get_default_data_dir(), true); } - bool init(const std::string& config_folder, bool testnet = false); - bool deinit(); - - void set_checkpoints(checkpoints&& chk_pts) { m_checkpoints = chk_pts; } - - //bool push_new_block(); - bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs) const; - bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks) const; - bool get_alternative_blocks(std::list& blocks) const; - size_t get_alternative_blocks_count() const; - crypto::hash get_block_id_by_height(uint64_t height) const; - bool get_block_by_hash(const crypto::hash &h, block &blk) const; - void get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid) const; - - template - void serialize(archive_t & ar, const unsigned int version); - - bool have_tx(const crypto::hash &id) const; - bool have_tx_keyimges_as_spent(const transaction &tx) const; - bool have_tx_keyimg_as_spent(const crypto::key_image &key_im) const; - const transaction *get_tx(const crypto::hash &id) const; - - uint64_t get_current_blockchain_height() const; - crypto::hash get_tail_id() const; - crypto::hash get_tail_id(uint64_t& height) const; - difficulty_type get_difficulty_for_next_block() const; - bool add_new_block(const block& bl_, block_verification_context& bvc); - bool reset_and_set_genesis_block(const block& b); - bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, const blobdata& ex_nonce) const; - bool have_block(const crypto::hash& id) const; - size_t get_total_transactions() const; - bool get_outs(uint64_t amount, std::list& pkeys) const; - bool get_short_chain_history(std::list& ids) const; - bool find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const; - bool find_blockchain_supplement(const std::list& qblock_ids, uint64_t& starter_offset) const; - bool find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const; - bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp); - bool handle_get_objects(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res); - bool get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) const; - bool get_backward_blocks_sizes(size_t from_height, std::vector& sz, size_t count) const; - bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs) const; - bool store_blockchain(); - - bool check_tx_inputs(const transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id) const; - uint64_t get_current_cumulative_blocksize_limit() const; - bool is_storing_blockchain()const{return m_is_blockchain_storing;} - uint64_t block_difficulty(size_t i) const; - double get_avg_block_size( size_t count) const; - bool flush_txes_from_pool(const std::list &txids); - - template - bool get_blocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) const - { - CRITICAL_REGION_LOCAL(m_blockchain_lock); - - BOOST_FOREACH(const auto& bl_id, block_ids) - { - auto it = m_blocks_index.find(bl_id); - if(it == m_blocks_index.end()) - missed_bs.push_back(bl_id); - else - { - CHECK_AND_ASSERT_MES(it->second < m_blocks.size(), false, "Internal error: bl_id=" << epee::string_tools::pod_to_hex(bl_id) - << " have index record with offset="<second<< ", bigger then m_blocks.size()=" << m_blocks.size()); - blocks.push_back(m_blocks[it->second].bl); - } - } - return true; - } - - template - bool get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) const - { - CRITICAL_REGION_LOCAL(m_blockchain_lock); - - BOOST_FOREACH(const auto& tx_id, txs_ids) - { - auto it = m_transactions.find(tx_id); - if(it == m_transactions.end()) - { - transaction tx; - if(!m_tx_pool->get_transaction(tx_id, tx)) - missed_txs.push_back(tx_id); - else - txs.push_back(tx); - } - else - txs.push_back(it->second.tx); - } - return true; - } - //debug functions - void print_blockchain(uint64_t start_index, uint64_t end_index) const; - void print_blockchain_index() const; - void print_blockchain_outs(const std::string& file) const; - void check_against_checkpoints(const checkpoints& points, bool enforce); - bool update_checkpoints(const std::string& file_path, bool check_dns); - void set_enforce_dns_checkpoints(bool enforce_checkpoints); - - block get_block(uint64_t height) const { return m_blocks[height].bl; } - size_t get_block_size(uint64_t height) const { return m_blocks[height].block_cumulative_size; } - difficulty_type get_block_cumulative_difficulty(uint64_t height) const { return m_blocks[height].cumulative_difficulty; } - uint64_t get_block_coins_generated(uint64_t height) const { return m_blocks[height].already_generated_coins; } - - bool for_all_key_images(std::function) const; - bool for_all_blocks(std::function) const; - bool for_all_transactions(std::function) const; - bool for_all_outputs(std::function) const; - - // use for testing only - bool debug_pop_block_from_blockchain() { return pop_block_from_blockchain(); } - - private: - typedef std::unordered_map blocks_by_id_index; - typedef std::unordered_map transactions_container; - typedef std::unordered_set key_images_container; - typedef std::vector blocks_container; - typedef std::unordered_map blocks_ext_by_hash; - typedef std::unordered_map blocks_by_hash; - typedef std::map>> outputs_container; //crypto::hash - tx hash, size_t - index of out in transaction - - tx_memory_pool* m_tx_pool; - mutable epee::critical_section m_blockchain_lock; // TODO: add here reader/writer lock - - // main chain - blocks_container m_blocks; // height -> block_extended_info - blocks_by_id_index m_blocks_index; // crypto::hash -> height - transactions_container m_transactions; - key_images_container m_spent_keys; - size_t m_current_block_cumul_sz_limit; - - - // all alternative chains - blocks_ext_by_hash m_alternative_chains; // crypto::hash -> block_extended_info - - // some invalid blocks - blocks_ext_by_hash m_invalid_blocks; // crypto::hash -> block_extended_info - outputs_container m_outputs; - - - std::string m_config_folder; - checkpoints m_checkpoints; - std::atomic m_is_in_checkpoint_zone; - std::atomic m_is_blockchain_storing; - - bool m_enforce_dns_checkpoints; - bool m_testnet; - - // made private for consistency with blockchain.h - template - bool scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height = NULL) const; - bool check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector& sig, uint64_t* pmax_related_block_height = NULL) const; - bool check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t* pmax_used_block_height = NULL) const; - bool check_tx_inputs(const transaction& tx, uint64_t* pmax_used_block_height = NULL) const; - - bool switch_to_alternative_blockchain(std::list& alt_chain, bool discard_disconnected_chain); - bool pop_block_from_blockchain(); - bool purge_block_data_from_blockchain(const block& b, size_t processed_tx_count); - bool purge_transaction_from_blockchain(const crypto::hash& tx_id); - bool purge_transaction_keyimages_from_blockchain(const transaction& tx, bool strict_check); - - bool handle_block_to_main_chain(const block& bl, block_verification_context& bvc); - bool handle_block_to_main_chain(const block& bl, const crypto::hash& id, block_verification_context& bvc); - bool handle_alternative_block(const block& b, const crypto::hash& id, block_verification_context& bvc); - difficulty_type get_next_difficulty_for_alternative_chain(const std::list& alt_chain, block_extended_info& bei) const; - bool prevalidate_miner_transaction(const block& b, uint64_t height) const; - bool validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins) const; - bool validate_transaction(const block& b, uint64_t height, const transaction& tx) const; - bool rollback_blockchain_switching(std::list& original_chain, size_t rollback_height); - bool add_transaction_from_block(const transaction& tx, const crypto::hash& tx_id, const crypto::hash& bl_id, uint64_t bl_height, size_t blob_size); - bool push_transaction_to_global_outs_index(const transaction& tx, const crypto::hash& tx_id, std::vector& global_indexes); - bool pop_transaction_from_global_index(const transaction& tx, const crypto::hash& tx_id); - bool get_last_n_blocks_sizes(std::vector& sz, size_t count) const; - bool add_out_to_get_random_outs(const std::vector >& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) const; - bool is_tx_spendtime_unlocked(uint64_t unlock_time) const; - bool add_block_as_invalid(const block& bl, const crypto::hash& h); - bool add_block_as_invalid(const block_extended_info& bei, const crypto::hash& h); - size_t find_end_of_allowed_index(const std::vector >& amount_outs) const; - bool check_block_timestamp_main(const block& b) const; - bool check_block_timestamp(std::vector timestamps, const block& b) const; - uint64_t get_adjusted_time() const; - bool complete_timestamps_vector(uint64_t start_height, std::vector& timestamps) const; - bool update_next_comulative_size_limit(); - bool store_genesis_block(bool testnet, bool check_added = false); - }; - - - /************************************************************************/ - /* */ - /************************************************************************/ - - #define CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER 12 - - template - void blockchain_storage::serialize(archive_t & ar, const unsigned int version) - { - if(version < 11) - return; - CRITICAL_REGION_LOCAL(m_blockchain_lock); - ar & m_blocks; - ar & m_blocks_index; - ar & m_transactions; - ar & m_spent_keys; - ar & m_alternative_chains; - ar & m_outputs; - ar & m_invalid_blocks; - ar & m_current_block_cumul_sz_limit; - /*serialization bug workaround*/ - if(version > 11) - { - uint64_t total_check_count = m_blocks.size() + m_blocks_index.size() + m_transactions.size() + m_spent_keys.size() + m_alternative_chains.size() + m_outputs.size() + m_invalid_blocks.size() + m_current_block_cumul_sz_limit; - if(archive_t::is_saving::value) - { - ar & total_check_count; - }else - { - uint64_t total_check_count_loaded = 0; - ar & total_check_count_loaded; - if(total_check_count != total_check_count_loaded) - { - LOG_ERROR("Blockchain storage data corruption detected. total_count loaded from file = " << total_check_count_loaded << ", expected = " << total_check_count); - - LOG_PRINT_L0("Blockchain storage:" << ENDL << - "m_blocks: " << m_blocks.size() << ENDL << - "m_blocks_index: " << m_blocks_index.size() << ENDL << - "m_transactions: " << m_transactions.size() << ENDL << - "m_spent_keys: " << m_spent_keys.size() << ENDL << - "m_alternative_chains: " << m_alternative_chains.size() << ENDL << - "m_outputs: " << m_outputs.size() << ENDL << - "m_invalid_blocks: " << m_invalid_blocks.size() << ENDL << - "m_current_block_cumul_sz_limit: " << m_current_block_cumul_sz_limit); - - throw std::runtime_error("Blockchain data corruption"); - } - } - } - - - LOG_PRINT_L2("Blockchain storage:" << ENDL << - "m_blocks: " << m_blocks.size() << ENDL << - "m_blocks_index: " << m_blocks_index.size() << ENDL << - "m_transactions: " << m_transactions.size() << ENDL << - "m_spent_keys: " << m_spent_keys.size() << ENDL << - "m_alternative_chains: " << m_alternative_chains.size() << ENDL << - "m_outputs: " << m_outputs.size() << ENDL << - "m_invalid_blocks: " << m_invalid_blocks.size() << ENDL << - "m_current_block_cumul_sz_limit: " << m_current_block_cumul_sz_limit); - } - - //------------------------------------------------------------------ - template - bool blockchain_storage::scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height) const - { - CRITICAL_REGION_LOCAL(m_blockchain_lock); - auto it = m_outputs.find(tx_in_to_key.amount); - if(it == m_outputs.end() || !tx_in_to_key.key_offsets.size()) - return false; - - std::vector absolute_offsets = relative_output_offsets_to_absolute(tx_in_to_key.key_offsets); - - - const std::vector >& amount_outs_vec = it->second; - size_t count = 0; - BOOST_FOREACH(uint64_t i, absolute_offsets) - { - if(i >= amount_outs_vec.size() ) - { - LOG_PRINT_L0("Wrong index in transaction inputs: " << i << ", expected maximum " << amount_outs_vec.size() - 1); - return false; - } - transactions_container::const_iterator tx_it = m_transactions.find(amount_outs_vec[i].first); - CHECK_AND_ASSERT_MES(tx_it != m_transactions.end(), false, "Wrong transaction id in output indexes: " << epee::string_tools::pod_to_hex(amount_outs_vec[i].first)); - CHECK_AND_ASSERT_MES(amount_outs_vec[i].second < tx_it->second.tx.vout.size(), false, - "Wrong index in transaction outputs: " << amount_outs_vec[i].second << ", expected less then " << tx_it->second.tx.vout.size()); - if(!vis.handle_output(tx_it->second.tx, tx_it->second.tx.vout[amount_outs_vec[i].second])) - { - LOG_PRINT_L0("Failed to handle_output for output no = " << count << ", with absolute offset " << i); - return false; - } - if(count++ == absolute_offsets.size()-1 && pmax_related_block_height) - { - if(*pmax_related_block_height < tx_it->second.m_keeper_block_height) - *pmax_related_block_height = tx_it->second.m_keeper_block_height; - } - } - - return true; - } -} - - - -BOOST_CLASS_VERSION(cryptonote::blockchain_storage, CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER) diff --git a/src/cryptonote_core/cryptonote_basic.h b/src/cryptonote_core/cryptonote_basic.h index ed62e26f4..0021413f1 100644 --- a/src/cryptonote_core/cryptonote_basic.h +++ b/src/cryptonote_core/cryptonote_basic.h @@ -50,9 +50,6 @@ #include "misc_language.h" #include "tx_extra.h" -#define DB_MEMORY 1 -#define DB_LMDB 2 - namespace cryptonote { diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 26748aceb..550c55759 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -57,11 +57,7 @@ namespace cryptonote //----------------------------------------------------------------------------------------------- core::core(i_cryptonote_protocol* pprotocol): m_mempool(m_blockchain_storage), -#if BLOCKCHAIN_DB == DB_LMDB m_blockchain_storage(m_mempool), -#else - m_blockchain_storage(&m_mempool), -#endif m_miner(this), m_miner_address(boost::value_initialized()), m_starter_message_showed(false), @@ -257,7 +253,6 @@ namespace cryptonote r = m_mempool.init(m_fakechain ? std::string() : m_config_folder); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool"); -#if BLOCKCHAIN_DB == DB_LMDB std::string db_type = command_line::get_arg(vm, command_line::arg_db_type); std::string db_sync_mode = command_line::get_arg(vm, command_line::arg_db_sync_mode); bool fast_sync = command_line::get_arg(vm, command_line::arg_fast_block_sync) != 0; @@ -405,9 +400,6 @@ namespace cryptonote bool show_time_stats = command_line::get_arg(vm, command_line::arg_show_time_stats) != 0; m_blockchain_storage.set_show_time_stats(show_time_stats); -#else - r = m_blockchain_storage.init(m_config_folder, m_testnet); -#endif CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage"); // load json & DNS checkpoints, and verify them @@ -780,18 +772,14 @@ namespace cryptonote //----------------------------------------------------------------------------------------------- bool core::prepare_handle_incoming_blocks(const std::list &blocks) { -#if BLOCKCHAIN_DB == DB_LMDB m_blockchain_storage.prepare_handle_incoming_blocks(blocks); -#endif return true; } //----------------------------------------------------------------------------------------------- bool core::cleanup_handle_incoming_blocks(bool force_sync) { -#if BLOCKCHAIN_DB == DB_LMDB m_blockchain_storage.cleanup_handle_incoming_blocks(force_sync); -#endif return true; } @@ -918,11 +906,6 @@ namespace cryptonote m_starter_message_showed = true; } -#if BLOCKCHAIN_DB == DB_LMDB - // m_store_blockchain_interval.do_call(boost::bind(&Blockchain::store_blockchain, &m_blockchain_storage)); -#else - m_store_blockchain_interval.do_call(boost::bind(&blockchain_storage::store_blockchain, &m_blockchain_storage)); -#endif m_fork_moaner.do_call(boost::bind(&core::check_fork_time, this)); m_txpool_auto_relayer.do_call(boost::bind(&core::relay_txpool_transactions, this)); m_miner.on_idle(); @@ -932,7 +915,6 @@ namespace cryptonote //----------------------------------------------------------------------------------------------- bool core::check_fork_time() { -#if BLOCKCHAIN_DB == DB_LMDB HardFork::State state = m_blockchain_storage.get_hard_fork_state(); switch (state) { case HardFork::LikelyForked: @@ -951,7 +933,6 @@ namespace cryptonote default: break; } -#endif return true; } //----------------------------------------------------------------------------------------------- diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 63969bc23..84b1f27a8 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -40,11 +40,7 @@ #include "cryptonote_protocol/cryptonote_protocol_handler_common.h" #include "storages/portable_storage_template_helper.h" #include "tx_pool.h" -#if BLOCKCHAIN_DB == DB_LMDB #include "blockchain.h" -#else -#include "blockchain_storage.h" -#endif #include "miner.h" #include "connection_context.h" #include "cryptonote_core/cryptonote_stat_info.h" @@ -498,7 +494,6 @@ namespace cryptonote */ void resume_mine(); -#if BLOCKCHAIN_DB == DB_LMDB /** * @brief gets the Blockchain instance * @@ -512,10 +507,6 @@ namespace cryptonote * @return a const reference to the Blockchain instance */ const Blockchain& get_blockchain_storage()const{return m_blockchain_storage;} -#else - blockchain_storage& get_blockchain_storage(){return m_blockchain_storage;} - const blockchain_storage& get_blockchain_storage()const{return m_blockchain_storage;} -#endif /** * @copydoc Blockchain::print_blockchain @@ -765,11 +756,7 @@ namespace cryptonote uint64_t m_test_drop_download_height = 0; //!< height under which to drop incoming blocks, if doing so tx_memory_pool m_mempool; //!< transaction pool instance -#if BLOCKCHAIN_DB == DB_LMDB Blockchain m_blockchain_storage; //!< Blockchain instance -#else - blockchain_storage m_blockchain_storage; -#endif i_cryptonote_protocol* m_pprotocol; //!< cryptonote protocol instance diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 3d5ab86e1..bb3a6dc3e 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -37,11 +37,7 @@ #include "cryptonote_format_utils.h" #include "cryptonote_boost_serialization.h" #include "cryptonote_config.h" -#if BLOCKCHAIN_DB == DB_LMDB #include "blockchain.h" -#else -#include "blockchain_storage.h" -#endif #include "common/boost_serialization_helper.h" #include "common/int-util.h" #include "misc_language.h" @@ -74,18 +70,11 @@ namespace cryptonote } } //--------------------------------------------------------------------------------- -#if BLOCKCHAIN_DB == DB_LMDB //--------------------------------------------------------------------------------- tx_memory_pool::tx_memory_pool(Blockchain& bchs): m_blockchain(bchs) { } -#else - tx_memory_pool::tx_memory_pool(blockchain_storage& bchs): m_blockchain(bchs) - { - - } -#endif //--------------------------------------------------------------------------------- bool tx_memory_pool::add_tx(const transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, size_t blob_size, tx_verification_context& tvc, bool kept_by_block, bool relayed, uint8_t version) { @@ -169,11 +158,7 @@ namespace cryptonote crypto::hash max_used_block_id = null_hash; uint64_t max_used_block_height = 0; -#if BLOCKCHAIN_DB == DB_LMDB bool ch_inp_res = m_blockchain.check_tx_inputs(tx, max_used_block_height, max_used_block_id, tvc, kept_by_block); -#else - bool ch_inp_res = m_blockchain.check_tx_inputs(tx, max_used_block_height, max_used_block_id); -#endif CRITICAL_REGION_LOCAL(m_transactions_lock); if(!ch_inp_res) { @@ -518,12 +503,8 @@ namespace cryptonote if(txd.last_failed_id == m_blockchain.get_block_id_by_height(txd.last_failed_height)) return false; //check ring signature again, it is possible (with very small chance) that this transaction become again valid -#if BLOCKCHAIN_DB == DB_LMDB tx_verification_context tvc; if(!m_blockchain.check_tx_inputs(txd.tx, txd.max_used_block_height, txd.max_used_block_id, tvc)) -#else - if(!m_blockchain.check_tx_inputs(txd.tx, txd.max_used_block_height, txd.max_used_block_id)) -#endif { txd.last_failed_height = m_blockchain.get_current_blockchain_height()-1; txd.last_failed_id = m_blockchain.get_block_id_by_height(txd.last_failed_height); diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index c7aab7f08..0e280872d 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -48,11 +48,7 @@ namespace cryptonote { -#if BLOCKCHAIN_DB == DB_LMDB class Blockchain; -#else - class blockchain_storage; -#endif /************************************************************************/ /* */ /************************************************************************/ @@ -93,16 +89,12 @@ namespace cryptonote class tx_memory_pool: boost::noncopyable { public: -#if BLOCKCHAIN_DB == DB_LMDB /** * @brief Constructor * * @param bchs a Blockchain class instance, for getting chain info */ tx_memory_pool(Blockchain& bchs); -#else - tx_memory_pool(blockchain_storage& bchs); -#endif /** @@ -492,18 +484,7 @@ namespace cryptonote std::unordered_set m_timed_out_transactions; std::string m_config_folder; //!< the folder to save state to -#if BLOCKCHAIN_DB == DB_LMDB Blockchain& m_blockchain; //!< reference to the Blockchain object -#else - blockchain_storage& m_blockchain; -#endif - -#if BLOCKCHAIN_DB == DB_LMDB -#else -#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - friend class blockchain_storage; -#endif -#endif }; } -- cgit v1.2.3 From 4258dab4d6dcbf563daa054c7e05e8a0027290b8 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 5 Jun 2016 10:46:18 +0100 Subject: core: new /getrandom_rctouts.bin binary RPC call to get random ringct outputs to mix with --- src/cryptonote_core/blockchain.cpp | 98 +++++++++++++++++++++++++++++++++ src/cryptonote_core/blockchain.h | 25 +++++++++ src/cryptonote_core/cryptonote_core.cpp | 5 ++ src/cryptonote_core/cryptonote_core.h | 8 +++ 4 files changed, 136 insertions(+) (limited to 'src/cryptonote_core') diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index cc6b48b6b..46ff7efbb 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1597,6 +1597,104 @@ bool Blockchain::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUT return true; } //------------------------------------------------------------------ +// This function adds the ringct output at index i to the list +// unlocked and other such checks should be done by here. +void Blockchain::add_out_to_get_rct_random_outs(std::list& outs, size_t i) const +{ + LOG_PRINT_L3("Blockchain::" << __func__); + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry& oen = *outs.insert(outs.end(), COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry()); + oen.global_amount_index = i; + output_data_t data = m_db->get_output_key(0, i); + oen.out_key = data.pubkey; + oen.commitment = m_db->get_rct_commitment(i); +} +//------------------------------------------------------------------ +// This function takes an RPC request for mixins and creates an RPC response +// with the requested mixins. +// TODO: figure out why this returns boolean / if we should be returning false +// in some cases +bool Blockchain::get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const +{ + LOG_PRINT_L3("Blockchain::" << __func__); + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + // for each amount that we need to get mixins for, get random outputs + // from BlockchainDB where is req.outs_count (number of mixins). + auto num_outs = m_db->get_num_outputs(0); + // ensure we don't include outputs that aren't yet eligible to be used + // outpouts are sorted by height + while (num_outs > 0) + { + const tx_out_index toi = m_db->get_output_tx_and_index(0, num_outs - 1); + const uint64_t height = m_db->get_tx_block_height(toi.first); + if (height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE <= m_db->height()) + break; + --num_outs; + } + + std::unordered_set seen_indices; + + // if there aren't enough outputs to mix with (or just enough), + // use all of them. Eventually this should become impossible. + if (num_outs <= req.outs_count) + { + for (uint64_t i = 0; i < num_outs; i++) + { + // get tx_hash, tx_out_index from DB + tx_out_index toi = m_db->get_output_tx_and_index(0, i); + + // if tx is unlocked, add output to result_outs + if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first))) + { + add_out_to_get_rct_random_outs(res.outs, i); + } + } + } + else + { + // while we still need more mixins + while (res.outs.size() < req.outs_count) + { + // if we've gone through every possible output, we've gotten all we can + if (seen_indices.size() == num_outs) + { + break; + } + + // get a random output index from the DB. If we've already seen it, + // return to the top of the loop and try again, otherwise add it to the + // list of output indices we've seen. + + // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit + uint64_t r = crypto::rand() % ((uint64_t)1 << 53); + double frac = std::sqrt((double)r / ((uint64_t)1 << 53)); + uint64_t i = (uint64_t)(frac*num_outs); + // just in case rounding up to 1 occurs after sqrt + if (i == num_outs) + --i; + + if (seen_indices.count(i)) + { + continue; + } + seen_indices.emplace(i); + + // get tx_hash, tx_out_index from DB + tx_out_index toi = m_db->get_output_tx_and_index(0, i); + + // if the output's transaction is unlocked, add the output's index to + // our list. + if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first))) + { + add_out_to_get_rct_random_outs(res.outs, i); + } + } + } + return true; +} +//------------------------------------------------------------------ bool Blockchain::get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res) const { LOG_PRINT_L3("Blockchain::" << __func__); diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 1524693b8..701c17d9e 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -454,6 +454,23 @@ namespace cryptonote */ bool get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res) const; + /** + * @brief gets random ringct outputs to mix with + * + * This function takes an RPC request for outputs to mix with + * and creates an RPC response with the resultant output indices + * and the matching keys. + * + * Outputs to mix with are randomly selected from the utxo set + * for each output amount in the request. + * + * @param req the output amounts and number of mixins to select + * @param res return-by-reference the resultant output indices + * + * @return true + */ + bool get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const; + /** * @brief gets the global indices for outputs from a given transaction * @@ -1053,6 +1070,14 @@ namespace cryptonote */ void add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) const; + /** + * @brief adds the given output to the requested set of random ringct outputs + * + * @param outs return-by-reference the set the output is to be added to + * @param i the rct output index + */ + void add_out_to_get_rct_random_outs(std::list& outs, size_t i) const; + /** * @brief checks if a transaction is unlocked (its outputs spendable) * diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 550c55759..b3a7cb98d 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -706,6 +706,11 @@ namespace cryptonote return m_blockchain_storage.get_outs(req, res); } //----------------------------------------------------------------------------------------------- + bool core::get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const + { + return m_blockchain_storage.get_random_rct_outs(req, res); + } + //----------------------------------------------------------------------------------------------- bool core::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs) const { return m_blockchain_storage.get_tx_outputs_gindexs(tx_id, indexs); diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 84b1f27a8..d16bd6553 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -479,6 +479,14 @@ namespace cryptonote */ bool get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res) const; + /** + * + * @copydoc Blockchain::get_random_rct_outs + * + * @note see Blockchain::get_random_rct_outs + */ + bool get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const; + /** * @copydoc miner::pause -- cgit v1.2.3 From 98564439f80391356cf7513facf82181465bcd34 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 10 Jun 2016 20:14:20 +0100 Subject: core: link against libringct --- src/cryptonote_core/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'src/cryptonote_core') diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt index 3e50f48ae..4ebfd01e3 100644 --- a/src/cryptonote_core/CMakeLists.txt +++ b/src/cryptonote_core/CMakeLists.txt @@ -78,6 +78,7 @@ target_link_libraries(cryptonote_core crypto otshell_utils blockchain_db + ringct ${Boost_DATE_TIME_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_SERIALIZATION_LIBRARY} -- cgit v1.2.3 From cc7f449d57a490d0f3f24cf9584ff280063939a2 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 14 Jun 2016 17:25:00 +0100 Subject: make rct tx serialization work It may be suboptimal, but it's a pain to have to rebuild everything when some of this changes. Also, no clue why there seems to be two different code paths for serializing a tx... --- .../cryptonote_boost_serialization.h | 56 ++++++++++++++++++++++ 1 file changed, 56 insertions(+) (limited to 'src/cryptonote_core') diff --git a/src/cryptonote_core/cryptonote_boost_serialization.h b/src/cryptonote_core/cryptonote_boost_serialization.h index 79ceec5bc..6a6ff5a7d 100644 --- a/src/cryptonote_core/cryptonote_boost_serialization.h +++ b/src/cryptonote_core/cryptonote_boost_serialization.h @@ -40,6 +40,7 @@ #include "cryptonote_basic.h" #include "common/unordered_containers_boost_serialization.h" #include "crypto/crypto.h" +#include "ringct/rctTypes.h" //namespace cryptonote { namespace boost @@ -163,6 +164,61 @@ namespace boost a & b.miner_tx; a & b.tx_hashes; } + + template + inline void serialize(Archive &a, rct::key &x, const boost::serialization::version_type ver) + { + a & reinterpret_cast(x); + } + + template + inline void serialize(Archive &a, rct::ctkey &x, const boost::serialization::version_type ver) + { + a & x.dest; + a & x.mask; + } + + template + inline void serialize(Archive &a, rct::rangeSig &x, const boost::serialization::version_type ver) + { + a & x.asig; + a & x.Ci; + } + + template + inline void serialize(Archive &a, rct::asnlSig &x, const boost::serialization::version_type ver) + { + a & x.L1; + a & x.s2; + a & x.s; + } + + template + inline void serialize(Archive &a, rct::mgSig &x, const boost::serialization::version_type ver) + { + a & x.ss; + a & x.cc; + a & x.II; + } + + template + inline void serialize(Archive &a, rct::ecdhTuple &x, const boost::serialization::version_type ver) + { + a & x.mask; + a & x.amount; + a & x.senderPk; + } + + template + inline void serialize(Archive &a, rct::rctSig &x, const boost::serialization::version_type ver) + { + a & x.rangeSigs; + a & x.MG; + a & x.mixRing; + a & x.ecdhInfo; + a & x.outPk; + a & x.txnFee; + } } } -- cgit v1.2.3 From dc4aad7eb5fffa450d4c5eb094cf962e45b2f43a Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 15 Jun 2016 23:37:13 +0100 Subject: add rct to the protocol It is not yet constrained to a fork, so don't use on the real network or you'll be orphaned or rejected. --- src/cryptonote_core/blockchain.cpp | 279 ++++++++++++++++----- src/cryptonote_core/blockchain.h | 12 +- src/cryptonote_core/cryptonote_basic.h | 62 +++-- .../cryptonote_boost_serialization.h | 5 +- src/cryptonote_core/cryptonote_core.cpp | 35 ++- src/cryptonote_core/cryptonote_format_utils.cpp | 143 +++++++++-- src/cryptonote_core/cryptonote_format_utils.h | 10 +- src/cryptonote_core/tx_pool.cpp | 54 ++-- 8 files changed, 451 insertions(+), 149 deletions(-) (limited to 'src/cryptonote_core') diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 46ff7efbb..77794d14d 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -51,6 +51,7 @@ #include "crypto/hash.h" #include "cryptonote_core/checkpoints.h" #include "cryptonote_core/cryptonote_core.h" +#include "ringct/rctSigs.h" #if defined(PER_BLOCK_CHECKPOINT) #include "blocks/blocks.h" #endif @@ -127,7 +128,7 @@ bool Blockchain::have_tx_keyimg_as_spent(const crypto::key_image &key_im) const // and collects the public key for each from the transaction it was included in // via the visitor passed to it. template -bool Blockchain::scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t &vis, const crypto::hash &tx_prefix_hash, uint64_t* pmax_related_block_height) const +bool Blockchain::scan_outputkeys_for_indexes(size_t tx_version, const txin_to_key& tx_in_to_key, visitor_t &vis, const crypto::hash &tx_prefix_hash, uint64_t* pmax_related_block_height) const { LOG_PRINT_L3("Blockchain::" << __func__); @@ -206,8 +207,21 @@ bool Blockchain::scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, vi else output_index = m_db->get_output_key(tx_in_to_key.amount, i); + rct::key commitment; + if (tx_version > 1) + { + if (tx_in_to_key.amount == 0) + commitment = m_db->get_rct_commitment(i); + else + commitment = rct::zeroCommit(tx_in_to_key.amount); + } + else + { + rct::identity(commitment); + } + // call to the passed boost visitor to grab the public key for the output - if (!vis.handle_output(output_index.unlock_time, output_index.pubkey)) + if (!vis.handle_output(output_index.unlock_time, output_index.pubkey, commitment)) { LOG_PRINT_L0("Failed to handle_output for output no = " << count << ", with absolute offset " << i); return false; @@ -1086,14 +1100,24 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m { LOG_ERROR("Creating block template: error: invalid transaction size"); } - uint64_t inputs_amount; - if (!get_inputs_money_amount(cur_tx.tx, inputs_amount)) + if (cur_tx.tx.version == 1) { - LOG_ERROR("Creating block template: error: cannot get inputs amount"); + uint64_t inputs_amount; + if (!get_inputs_money_amount(cur_tx.tx, inputs_amount)) + { + LOG_ERROR("Creating block template: error: cannot get inputs amount"); + } + else if (cur_tx.fee != inputs_amount - get_outs_money_amount(cur_tx.tx)) + { + LOG_ERROR("Creating block template: error: invalid fee"); + } } - else if (cur_tx.fee != inputs_amount - get_outs_money_amount(cur_tx.tx)) + else { - LOG_ERROR("Creating block template: error: invalid fee"); + if (cur_tx.fee != cur_tx.tx.txnFee) + { + LOG_ERROR("Creating block template: error: invalid fee"); + } } } if (txs_size != real_txs_size) @@ -1599,16 +1623,25 @@ bool Blockchain::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUT //------------------------------------------------------------------ // This function adds the ringct output at index i to the list // unlocked and other such checks should be done by here. -void Blockchain::add_out_to_get_rct_random_outs(std::list& outs, size_t i) const +void Blockchain::add_out_to_get_rct_random_outs(std::list& outs, uint64_t amount, size_t i) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry& oen = *outs.insert(outs.end(), COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry()); + oen.amount = amount; oen.global_amount_index = i; - output_data_t data = m_db->get_output_key(0, i); + output_data_t data = m_db->get_output_key(amount, i); oen.out_key = data.pubkey; - oen.commitment = m_db->get_rct_commitment(i); + if (amount == 0) + { + oen.commitment = m_db->get_rct_commitment(i); + } + else + { + // not a rct output, make a fake commitment with zero key + oen.commitment = rct::zeroCommit(amount); + } } //------------------------------------------------------------------ // This function takes an RPC request for mixins and creates an RPC response @@ -1648,7 +1681,7 @@ bool Blockchain::get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::r // if tx is unlocked, add output to result_outs if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first))) { - add_out_to_get_rct_random_outs(res.outs, i); + add_out_to_get_rct_random_outs(res.outs, 0, i); } } } @@ -1688,10 +1721,44 @@ bool Blockchain::get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::r // our list. if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first))) { - add_out_to_get_rct_random_outs(res.outs, i); + add_out_to_get_rct_random_outs(res.outs, 0, i); } } } + + if (res.outs.size() < req.outs_count) + return false; +#if 0 + // if we do not have enough RCT inputs, we can pick from the non RCT ones + // which will have a zero mask + if (res.outs.size() < req.outs_count) + { + LOG_PRINT_L0("Out of RCT inputs (" << res.outs.size() << "/" << req.outs_count << "), using regular ones"); + + // TODO: arbitrary selection, needs better + COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request req2 = AUTO_VAL_INIT(req2); + COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response res2 = AUTO_VAL_INIT(res2); + req2.outs_count = req.outs_count - res.outs.size(); + static const uint64_t amounts[] = {1, 10, 20, 50, 100, 200, 500, 1000, 10000}; + for (uint64_t a: amounts) + req2.amounts.push_back(a); + if (!get_random_outs_for_amounts(req2, res2)) + return false; + + // pick random ones from there + while (res.outs.size() < req.outs_count) + { + int list_idx = rand() % (sizeof(amounts)/sizeof(amounts[0])); + if (!res2.outs[list_idx].outs.empty()) + { + const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry oe = res2.outs[list_idx].outs.back(); + res2.outs[list_idx].outs.pop_back(); + add_out_to_get_rct_random_outs(res.outs, res2.outs[list_idx].amount, oe.global_amount_index); + } + } + } +#endif + return true; } //------------------------------------------------------------------ @@ -2153,9 +2220,24 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context // from hard fork 2, we forbid dust and compound outputs if (m_hardfork->get_current_version() >= 2) { for (auto &o: tx.vout) { - if (!is_valid_decomposed_amount(o.amount)) { - tvc.m_invalid_output = true; - return false; + if (tx.version == 1) + { + if (!is_valid_decomposed_amount(o.amount)) { + tvc.m_invalid_output = true; + return false; + } + } + } + } + + // in a v2 tx, all outputs must have 0 amount + if (m_hardfork->get_current_version() >= 3) { + if (tx.version >= 2) { + for (auto &o: tx.vout) { + if (o.amount != 0) { + tvc.m_invalid_output = true; + return false; + } } } } @@ -2238,7 +2320,7 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context } uint64_t t_t1 = 0; - std::vector> pubkeys(tx.vin.size()); + std::vector> pubkeys(tx.vin.size()); std::vector < uint64_t > results; results.resize(tx.vin.size(), 0); @@ -2287,28 +2369,31 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context return false; } - // basically, make sure number of inputs == number of signatures - CHECK_AND_ASSERT_MES(sig_index < tx.signatures.size(), false, "wrong transaction: not signature entry for input with index= " << sig_index); + if (tx.version == 1) + { + // basically, make sure number of inputs == number of signatures + CHECK_AND_ASSERT_MES(sig_index < tx.signatures.size(), false, "wrong transaction: not signature entry for input with index= " << sig_index); #if defined(CACHE_VIN_RESULTS) - auto itk = it->second.find(in_to_key.k_image); - if(itk != it->second.end()) - { - if(!itk->second) + auto itk = it->second.find(in_to_key.k_image); + if(itk != it->second.end()) { - LOG_PRINT_L1("Failed ring signature for tx " << get_transaction_hash(tx) << " vin key with k_image: " << in_to_key.k_image << " sig_index: " << sig_index); - return false; - } + if(!itk->second) + { + LOG_PRINT_L1("Failed ring signature for tx " << get_transaction_hash(tx) << " vin key with k_image: " << in_to_key.k_image << " sig_index: " << sig_index); + return false; + } - // txin has been verified already, skip - sig_index++; - continue; - } + // txin has been verified already, skip + sig_index++; + continue; + } #endif + } // make sure that output being spent matches up correctly with the // signature spending it. - if (!check_tx_input(in_to_key, tx_prefix_hash, tx.signatures[sig_index], pubkeys[sig_index], pmax_used_block_height)) + if (!check_tx_input(tx.version, in_to_key, tx_prefix_hash, tx.version == 1 ? tx.signatures[sig_index] : std::vector(), tx.rct_signatures, pubkeys[sig_index], pmax_used_block_height)) { it->second[in_to_key.k_image] = false; LOG_PRINT_L1("Failed to check ring signature for tx " << get_transaction_hash(tx) << " vin key with k_image: " << in_to_key.k_image << " sig_index: " << sig_index); @@ -2320,28 +2405,31 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context return false; } - if (threads > 1) + if (tx.version == 1) { - // ND: Speedup - // 1. Thread ring signature verification if possible. - ioservice.dispatch(boost::bind(&Blockchain::check_ring_signature, this, std::cref(tx_prefix_hash), std::cref(in_to_key.k_image), std::cref(pubkeys[sig_index]), std::cref(tx.signatures[sig_index]), std::ref(results[sig_index]))); - } - else - { - check_ring_signature(tx_prefix_hash, in_to_key.k_image, pubkeys[sig_index], tx.signatures[sig_index], results[sig_index]); - if (!results[sig_index]) + if (threads > 1) { - it->second[in_to_key.k_image] = false; - LOG_PRINT_L1("Failed to check ring signature for tx " << get_transaction_hash(tx) << " vin key with k_image: " << in_to_key.k_image << " sig_index: " << sig_index); - - if (pmax_used_block_height) // a default value of NULL is used when called from Blockchain::handle_block_to_main_chain() + // ND: Speedup + // 1. Thread ring signature verification if possible. + ioservice.dispatch(boost::bind(&Blockchain::check_ring_signature, this, std::cref(tx_prefix_hash), std::cref(in_to_key.k_image), std::cref(pubkeys[sig_index]), std::cref(tx.signatures[sig_index]), std::ref(results[sig_index]))); + } + else + { + check_ring_signature(tx_prefix_hash, in_to_key.k_image, pubkeys[sig_index], tx.signatures[sig_index], results[sig_index]); + if (!results[sig_index]) { - LOG_PRINT_L1("*pmax_used_block_height: " << *pmax_used_block_height); - } + it->second[in_to_key.k_image] = false; + LOG_PRINT_L1("Failed to check ring signature for tx " << get_transaction_hash(tx) << " vin key with k_image: " << in_to_key.k_image << " sig_index: " << sig_index); - return false; + if (pmax_used_block_height) // a default value of NULL is used when called from Blockchain::handle_block_to_main_chain() + { + LOG_PRINT_L1("*pmax_used_block_height: " << *pmax_used_block_height); + } + + return false; + } + it->second[in_to_key.k_image] = true; } - it->second[in_to_key.k_image] = true; } sig_index++; @@ -2349,30 +2437,80 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context KILL_IOSERVICE(); - if (threads > 1) + if (tx.version == 1) { - // save results to table, passed or otherwise - bool failed = false; - for (size_t i = 0; i < tx.vin.size(); i++) + if (threads > 1) { - const txin_to_key& in_to_key = boost::get(tx.vin[i]); - it->second[in_to_key.k_image] = results[i]; - if(!failed && !results[i]) - failed = true; + // save results to table, passed or otherwise + bool failed = false; + for (size_t i = 0; i < tx.vin.size(); i++) + { + const txin_to_key& in_to_key = boost::get(tx.vin[i]); + it->second[in_to_key.k_image] = results[i]; + if(!failed && !results[i]) + failed = true; + } + + if (failed) + { + LOG_PRINT_L1("Failed to check ring signatures!, t_loop: " << t_t1); + return false; + } + } + } + else + { + // from version 2, check ringct signatures + + // RCT needs the same mixin for all inputs + for (size_t n = 1; n < pubkeys.size(); ++n) + { + if (pubkeys[n].size() != pubkeys[0].size()) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched ring sizes"); + return false; + } + } + + bool size_matches = true; + for (size_t i = 0; i < pubkeys.size(); ++i) + size_matches &= pubkeys[i].size() == tx.rct_signatures.mixRing.size(); + for (size_t i = 0; i < tx.rct_signatures.mixRing.size(); ++i) + size_matches &= pubkeys.size() == tx.rct_signatures.mixRing[i].size(); + if (!size_matches) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkeys/mixRing size"); + return false; + } + + for (size_t n = 0; n < pubkeys.size(); ++n) + { + for (size_t m = 0; m < pubkeys[n].size(); ++m) + { + if (pubkeys[n][m].dest != rct::rct2pk(tx.rct_signatures.mixRing[m][n].dest)) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkey at vin " << n << ", index " << m); + return false; + } + if (pubkeys[n][m].mask != rct::rct2pk(tx.rct_signatures.mixRing[m][n].mask)) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched commitment at vin " << n << ", index " << m); + return false; + } + } } - if (failed) + if (!rct::verRct(tx.rct_signatures)) { - LOG_PRINT_L1("Failed to check ring signatures!, t_loop: " << t_t1); + LOG_PRINT_L1("Failed to check ringct signatures!"); return false; } } - LOG_PRINT_L1("t_loop: " << t_t1); return true; } //------------------------------------------------------------------ -void Blockchain::check_ring_signature(const crypto::hash &tx_prefix_hash, const crypto::key_image &key_image, const std::vector &pubkeys, const std::vector& sig, uint64_t &result) +void Blockchain::check_ring_signature(const crypto::hash &tx_prefix_hash, const crypto::key_image &key_image, const std::vector &pubkeys, const std::vector& sig, uint64_t &result) { if (m_is_in_checkpoint_zone) { @@ -2383,7 +2521,8 @@ void Blockchain::check_ring_signature(const crypto::hash &tx_prefix_hash, const std::vector p_output_keys; for (auto &key : pubkeys) { - p_output_keys.push_back(&key); + // rct::key and crypto::public_key have the same structure, avoid object ctor/memcpy + p_output_keys.push_back(&(const crypto::public_key&)key.dest); } result = crypto::check_ring_signature(tx_prefix_hash, key_image, p_output_keys, sig.data()) ? 1 : 0; @@ -2419,7 +2558,7 @@ bool Blockchain::is_tx_spendtime_unlocked(uint64_t unlock_time) const // This function locates all outputs associated with a given input (mixins) // and validates that they exist and are usable. It also checks the ring // signature for each input. -bool Blockchain::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector& sig, std::vector &output_keys, uint64_t* pmax_related_block_height) +bool Blockchain::check_tx_input(size_t tx_version, const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector& sig, const rct::rctSig &rct_signatures, std::vector &output_keys, uint64_t* pmax_related_block_height) { LOG_PRINT_L3("Blockchain::" << __func__); @@ -2429,13 +2568,13 @@ bool Blockchain::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_ struct outputs_visitor { - std::vector& m_output_keys; + std::vector& m_output_keys; const Blockchain& m_bch; - outputs_visitor(std::vector& output_keys, const Blockchain& bch) : + outputs_visitor(std::vector& output_keys, const Blockchain& bch) : m_output_keys(output_keys), m_bch(bch) { } - bool handle_output(uint64_t unlock_time, const crypto::public_key &pubkey) + bool handle_output(uint64_t unlock_time, const crypto::public_key &pubkey, const rct::key &commitment) { //check tx unlock time if (!m_bch.is_tx_spendtime_unlocked(unlock_time)) @@ -2449,7 +2588,7 @@ bool Blockchain::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_ // but only txout_to_key outputs are stored in the DB in the first place, done in // Blockchain*::add_output - m_output_keys.push_back(pubkey); + m_output_keys.push_back(rct::ctkey({rct::pk2rct(pubkey), commitment})); return true; } }; @@ -2458,7 +2597,7 @@ bool Blockchain::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_ // collect output keys outputs_visitor vi(output_keys, *this); - if (!scan_outputkeys_for_indexes(txin, vi, tx_prefix_hash, pmax_related_block_height)) + if (!scan_outputkeys_for_indexes(tx_version, txin, vi, tx_prefix_hash, pmax_related_block_height)) { LOG_PRINT_L1("Failed to get output keys for tx with amount = " << print_money(txin.amount) << " and count indexes " << txin.key_offsets.size()); return false; @@ -2469,7 +2608,13 @@ bool Blockchain::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_ LOG_PRINT_L1("Output keys for tx with amount = " << txin.amount << " and count indexes " << txin.key_offsets.size() << " returned wrong keys count " << output_keys.size()); return false; } - CHECK_AND_ASSERT_MES(sig.size() == output_keys.size(), false, "internal error: tx signatures count=" << sig.size() << " mismatch with outputs keys count for inputs=" << output_keys.size()); + if (tx_version == 1) { + CHECK_AND_ASSERT_MES(sig.size() == output_keys.size(), false, "internal error: tx signatures count=" << sig.size() << " mismatch with outputs keys count for inputs=" << output_keys.size()); + } + else + { + CHECK_AND_ASSERT_MES(rct_signatures.mixRing.size() == output_keys.size(), false, "internal error: tx rct signatures count=" << sig.size() << " mismatch with outputs keys count for inputs=" << output_keys.size()); + } return true; } //------------------------------------------------------------------ diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 701c17d9e..b22074c57 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -884,11 +884,12 @@ namespace cryptonote * @param vis an instance of the visitor to use * @param tx_prefix_hash the hash of the associated transaction_prefix * @param pmax_related_block_height return-by-pointer the height of the most recent block in the input set + * @param tx_version version of the tx, if > 1 we also get commitments * * @return false if any keys are not found or any inputs are not unlocked, otherwise true */ template - inline bool scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t &vis, const crypto::hash &tx_prefix_hash, uint64_t* pmax_related_block_height = NULL) const; + inline bool scan_outputkeys_for_indexes(size_t tx_version, const txin_to_key& tx_in_to_key, visitor_t &vis, const crypto::hash &tx_prefix_hash, uint64_t* pmax_related_block_height = NULL) const; /** * @brief collect output public keys of a transaction input set @@ -900,15 +901,17 @@ namespace cryptonote * If pmax_related_block_height is not NULL, its value is set to the height * of the most recent block which contains an output used in the input set * + * @param tx_version the transaction version * @param txin the transaction input * @param tx_prefix_hash the transaction prefix hash, for caching organization * @param sig the input signature * @param output_keys return-by-reference the public keys of the outputs in the input set + * @param rct_signatures the ringCT signatures, which are only valid if tx version > 1 * @param pmax_related_block_height return-by-pointer the height of the most recent block in the input set * * @return false if any output is not yet unlocked, or is missing, otherwise true */ - bool check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector& sig, std::vector &output_keys, uint64_t* pmax_related_block_height); + bool check_tx_input(size_t tx_version,const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector& sig, const rct::rctSig &rct_signatures, std::vector &output_keys, uint64_t* pmax_related_block_height); /** * @brief validate a transaction's inputs and their keys @@ -1074,9 +1077,10 @@ namespace cryptonote * @brief adds the given output to the requested set of random ringct outputs * * @param outs return-by-reference the set the output is to be added to + * @param amount the output amount (0 for rct inputs) * @param i the rct output index */ - void add_out_to_get_rct_random_outs(std::list& outs, size_t i) const; + void add_out_to_get_rct_random_outs(std::list& outs, uint64_t amount, size_t i) const; /** * @brief checks if a transaction is unlocked (its outputs spendable) @@ -1197,7 +1201,7 @@ namespace cryptonote * @param result false if the ring signature is invalid, otherwise true */ void check_ring_signature(const crypto::hash &tx_prefix_hash, const crypto::key_image &key_image, - const std::vector &pubkeys, const std::vector &sig, uint64_t &result); + const std::vector &pubkeys, const std::vector &sig, uint64_t &result); /** * @brief loads block hashes from compiled-in data set diff --git a/src/cryptonote_core/cryptonote_basic.h b/src/cryptonote_core/cryptonote_basic.h index 0021413f1..91bcef8c5 100644 --- a/src/cryptonote_core/cryptonote_basic.h +++ b/src/cryptonote_core/cryptonote_basic.h @@ -49,6 +49,7 @@ #include "crypto/hash.h" #include "misc_language.h" #include "tx_extra.h" +#include "ringct/rctTypes.h" namespace cryptonote { @@ -172,7 +173,7 @@ namespace cryptonote BEGIN_SERIALIZE() VARINT_FIELD(version) - if(CURRENT_TRANSACTION_VERSION < version) return false; + if(version == 0 || CURRENT_TRANSACTION_VERSION < version) return false; VARINT_FIELD(unlock_time) FIELD(vin) FIELD(vout) @@ -187,6 +188,7 @@ namespace cryptonote { public: std::vector > signatures; //count signatures always the same as inputs count + rct::rctSig rct_signatures; transaction(); virtual ~transaction(); @@ -195,34 +197,46 @@ namespace cryptonote BEGIN_SERIALIZE_OBJECT() FIELDS(*static_cast(this)) - ar.tag("signatures"); - ar.begin_array(); - PREPARE_CUSTOM_VECTOR_SERIALIZATION(vin.size(), signatures); - bool signatures_not_expected = signatures.empty(); - if (!signatures_not_expected && vin.size() != signatures.size()) - return false; - - for (size_t i = 0; i < vin.size(); ++i) + if (version == 1) { - size_t signature_size = get_signature_size(vin[i]); - if (signatures_not_expected) + ar.tag("signatures"); + ar.begin_array(); + PREPARE_CUSTOM_VECTOR_SERIALIZATION(vin.size(), signatures); + bool signatures_not_expected = signatures.empty(); + if (!signatures_not_expected && vin.size() != signatures.size()) + return false; + + for (size_t i = 0; i < vin.size(); ++i) { - if (0 == signature_size) - continue; - else + size_t signature_size = get_signature_size(vin[i]); + if (signatures_not_expected) + { + if (0 == signature_size) + continue; + else + return false; + } + + PREPARE_CUSTOM_VECTOR_SERIALIZATION(signature_size, signatures[i]); + if (signature_size != signatures[i].size()) return false; - } - - PREPARE_CUSTOM_VECTOR_SERIALIZATION(signature_size, signatures[i]); - if (signature_size != signatures[i].size()) - return false; - FIELDS(signatures[i]); + FIELDS(signatures[i]); - if (vin.size() - i > 1) - ar.delimit_array(); + if (vin.size() - i > 1) + ar.delimit_array(); + } + ar.end_array(); + } + else + { + FIELD(rct_signatures) + for (size_t i = 0; i < rct_signatures.mixRing.size(); ++i) + { + if (rct_signatures.mixRing[i].size() != vin.size()) + return false; + } } - ar.end_array(); END_SERIALIZE() private: @@ -245,7 +259,7 @@ namespace cryptonote inline void transaction::set_null() { - version = 0; + version = 1; unlock_time = 0; vin.clear(); vout.clear(); diff --git a/src/cryptonote_core/cryptonote_boost_serialization.h b/src/cryptonote_core/cryptonote_boost_serialization.h index 6a6ff5a7d..6eeb66ec8 100644 --- a/src/cryptonote_core/cryptonote_boost_serialization.h +++ b/src/cryptonote_core/cryptonote_boost_serialization.h @@ -148,7 +148,10 @@ namespace boost a & x.vin; a & x.vout; a & x.extra; - a & x.signatures; + if (x.version == 1) + a & x.signatures; + else + a & x.rct_signatures; } diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index b3a7cb98d..5179cc8d2 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -43,6 +43,7 @@ using namespace epee; #include "misc_language.h" #include #include "cryptonote_core/checkpoints.h" +#include "ringct/rctTypes.h" #include "blockchain_db/blockchain_db.h" #include "blockchain_db/lmdb/db_lmdb.h" #if defined(BERKELEY_DB) @@ -552,6 +553,22 @@ namespace cryptonote LOG_PRINT_RED_L1("tx with invalid outputs, rejected for tx id= " << get_transaction_hash(tx)); return false; } + if (tx.version > 1) + { + if (tx.rct_signatures.outPk.size() != tx.vout.size()) + { + LOG_PRINT_RED_L1("tx with mismatched vout/outPk count, rejected for tx id= " << get_transaction_hash(tx)); + return false; + } + for (size_t n = 0; n < tx.vout.size(); ++n) + { + if (tx.rct_signatures.outPk[n].dest != boost::get(tx.vout[n].target).key) + { + LOG_PRINT_RED_L1("tx ringct public key does not match output public key for tx id= " << get_transaction_hash(tx)); + return false; + } + } + } if(!check_money_overflow(tx)) { @@ -559,15 +576,19 @@ namespace cryptonote return false; } - uint64_t amount_in = 0; - get_inputs_money_amount(tx, amount_in); - uint64_t amount_out = get_outs_money_amount(tx); - - if(amount_in <= amount_out) + if (tx.version == 1) { - LOG_PRINT_RED_L1("tx with wrong amounts: ins " << amount_in << ", outs " << amount_out << ", rejected for tx id= " << get_transaction_hash(tx)); - return false; + uint64_t amount_in = 0; + get_inputs_money_amount(tx, amount_in); + uint64_t amount_out = get_outs_money_amount(tx); + + if(amount_in <= amount_out) + { + LOG_PRINT_RED_L1("tx with wrong amounts: ins " << amount_in << ", outs " << amount_out << ", rejected for tx id= " << get_transaction_hash(tx)); + return false; + } } + // for version > 1, ringct signatures check verifies amounts match if(!keeped_by_block && get_object_blobsize(tx) >= m_blockchain_storage.get_current_cumulative_blocksize_limit() - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE) { diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index ff752ae47..9f0693b30 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -37,6 +37,7 @@ using namespace epee; #include "miner.h" #include "crypto/crypto.h" #include "crypto/hash.h" +#include "ringct/rctSigs.h" #define ENCRYPTED_PAYMENT_ID_TAIL 0x8d @@ -181,7 +182,7 @@ namespace cryptonote CHECK_AND_ASSERT_MES(summary_amounts == block_reward, false, "Failed to construct miner tx, summary_amounts = " << summary_amounts << " not equal block_reward = " << block_reward); - tx.version = CURRENT_TRANSACTION_VERSION; + tx.version = 1; //lock tx.unlock_time = height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; tx.vin.push_back(in); @@ -252,6 +253,11 @@ namespace cryptonote //--------------------------------------------------------------- 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; BOOST_FOREACH(auto& in, tx.vin) @@ -447,13 +453,14 @@ namespace cryptonote return encrypt_payment_id(payment_id, public_key, secret_key); } //--------------------------------------------------------------- - bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector& sources, const std::vector& destinations, std::vector extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key) + bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector& sources, const std::vector& destinations, std::vector extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct) { tx.vin.clear(); tx.vout.clear(); tx.signatures.clear(); + tx.rct_signatures = rct::rctSig(); - tx.version = CURRENT_TRANSACTION_VERSION; + tx.version = rct ? 2 : 1; tx.unlock_time = unlock_time; tx.extra = extra; @@ -509,6 +516,18 @@ namespace cryptonote }; std::vector in_contexts; + if (tx.version > 1) + { + // ringct requires all real inputs to be at the same index for all inputs // TODO + BOOST_FOREACH(const tx_source_entry& src_entr, sources) + { + if(src_entr.real_output != sources.begin()->real_output) + { + LOG_ERROR("All inputs must have the same index for ringct"); + return false; + } + } + } uint64_t summary_inputs_money = 0; //fill inputs @@ -529,7 +548,7 @@ namespace cryptonote return false; //check that derivated key is equal with real output key - if( !(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].second) ) + if( !(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].second.dest) ) { LOG_ERROR("derived public key missmatch with output public key! "<< ENDL << "derived_key:" << string_tools::pod_to_hex(in_ephemeral.pub) << ENDL << "real output_public_key:" @@ -559,7 +578,7 @@ namespace cryptonote size_t output_index = 0; BOOST_FOREACH(const tx_destination_entry& dst_entr, shuffled_dsts) { - CHECK_AND_ASSERT_MES(dst_entr.amount > 0, false, "Destination with wrong amount: " << dst_entr.amount); + CHECK_AND_ASSERT_MES(dst_entr.amount > 0 || tx.version > 1, false, "Destination with wrong amount: " << dst_entr.amount); crypto::key_derivation derivation; crypto::public_key out_eph_public_key; bool r = crypto::generate_key_derivation(dst_entr.addr.m_view_public_key, txkey.sec, derivation); @@ -586,33 +605,100 @@ namespace cryptonote } - //generate ring signatures - crypto::hash tx_prefix_hash; - get_transaction_prefix_hash(tx, tx_prefix_hash); - - std::stringstream ss_ring_s; - size_t i = 0; - BOOST_FOREACH(const tx_source_entry& src_entr, sources) + if (tx.version == 1) { - ss_ring_s << "pub_keys:" << ENDL; - std::vector keys_ptrs; - BOOST_FOREACH(const tx_source_entry::output_entry& o, src_entr.outputs) + //generate ring signatures + crypto::hash tx_prefix_hash; + get_transaction_prefix_hash(tx, tx_prefix_hash); + + std::stringstream ss_ring_s; + size_t i = 0; + BOOST_FOREACH(const tx_source_entry& src_entr, sources) { - keys_ptrs.push_back(&o.second); - ss_ring_s << o.second << ENDL; + ss_ring_s << "pub_keys:" << ENDL; + std::vector keys_ptrs; + std::vector keys(src_entr.outputs.size()); + size_t ii = 0; + BOOST_FOREACH(const tx_source_entry::output_entry& o, src_entr.outputs) + { + keys[ii] = rct2pk(o.second.dest); + keys_ptrs.push_back(&keys[ii]); + ss_ring_s << o.second.dest << ENDL; + ++ii; + } + + tx.signatures.push_back(std::vector()); + std::vector& sigs = tx.signatures.back(); + sigs.resize(src_entr.outputs.size()); + crypto::generate_ring_signature(tx_prefix_hash, boost::get(tx.vin[i]).k_image, keys_ptrs, in_contexts[i].in_ephemeral.sec, src_entr.real_output, sigs.data()); + ss_ring_s << "signatures:" << ENDL; + std::for_each(sigs.begin(), sigs.end(), [&](const crypto::signature& s){ss_ring_s << s << ENDL;}); + ss_ring_s << "prefix_hash:" << tx_prefix_hash << ENDL << "in_ephemeral_key: " << in_contexts[i].in_ephemeral.sec << ENDL << "real_output: " << src_entr.real_output; + i++; } - tx.signatures.push_back(std::vector()); - std::vector& sigs = tx.signatures.back(); - sigs.resize(src_entr.outputs.size()); - crypto::generate_ring_signature(tx_prefix_hash, boost::get(tx.vin[i]).k_image, keys_ptrs, in_contexts[i].in_ephemeral.sec, src_entr.real_output, sigs.data()); - ss_ring_s << "signatures:" << ENDL; - std::for_each(sigs.begin(), sigs.end(), [&](const crypto::signature& s){ss_ring_s << s << ENDL;}); - ss_ring_s << "prefix_hash:" << tx_prefix_hash << ENDL << "in_ephemeral_key: " << in_contexts[i].in_ephemeral.sec << ENDL << "real_output: " << src_entr.real_output; - i++; + LOG_PRINT2("construct_tx.log", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL << ss_ring_s.str() , LOG_LEVEL_3); } + else + { + // enforce same mixin for all outputs + size_t n_total_outs = sources[0].outputs.size(); + for (size_t i = 1; i < sources.size(); ++i) { + if (n_total_outs != sources[i].outputs.size()) { + LOG_ERROR("Ringct transaction has varying mixin"); + return false; + } + } - LOG_PRINT2("construct_tx.log", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL << ss_ring_s.str() , LOG_LEVEL_3); + uint64_t amount_in = 0, amount_out = 0; + rct::ctkeyV inSk; + rct::ctkeyM mixRing(n_total_outs); + rct::keyV destinations; + std::vector amounts; + for (size_t i = 0; i < sources.size(); ++i) + { + rct::ctkey ctkey; + amount_in += sources[i].amount; + // inSk: (secret key, mask) + ctkey.dest = rct::sk2rct(in_contexts[i].in_ephemeral.sec); + ctkey.mask = sources[i].mask; + inSk.push_back(ctkey); + // inPk: (public key, commitment) + // will be done when filling in mixRing + } + for (size_t i = 0; i < tx.vout.size(); ++i) + { + destinations.push_back(rct::pk2rct(boost::get(tx.vout[i].target).key)); + amounts.push_back(tx.vout[i].amount); + amount_out += tx.vout[i].amount; + } + for (size_t i = 0; i < n_total_outs; ++i) // same index assumption + { + mixRing[i].resize(sources.size()); + for (size_t n = 0; n < sources.size(); ++n) + { + mixRing[i][n] = sources[n].outputs[i].second; + } + } + + // fee + if (amount_in > amount_out) + amounts.push_back(amount_in - amount_out); + + LOG_PRINT_L1("Signing tx: " << obj_to_json_str(tx)); + tx.rct_signatures = rct::genRct(inSk, destinations, amounts, mixRing, sources[0].real_output); // same index assumption + + // zero out all amounts to mask rct outputs, real amounts are now encrypted + for (size_t i = 0; i < tx.vin.size(); ++i) + { + if (!(sources[i].mask == rct::identity())) + boost::get(tx.vin[i]).amount = 0; + } + for (size_t i = 0; i < tx.vout.size(); ++i) + tx.vout[i].amount = 0; + + LOG_PRINT2("construct_tx.log", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL, LOG_LEVEL_3); + } return true; } @@ -661,7 +747,10 @@ namespace cryptonote << out.target.type().name() << ", expected " << typeid(txout_to_key).name() << ", in transaction id=" << get_transaction_hash(tx)); - CHECK_AND_NO_ASSERT_MES(0 < out.amount, false, "zero amount ouput 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; diff --git a/src/cryptonote_core/cryptonote_format_utils.h b/src/cryptonote_core/cryptonote_format_utils.h index 056940e98..f4fd8bd34 100644 --- a/src/cryptonote_core/cryptonote_format_utils.h +++ b/src/cryptonote_core/cryptonote_format_utils.h @@ -35,6 +35,7 @@ #include "include_base_utils.h" #include "crypto/crypto.h" #include "crypto/hash.h" +#include "ringct/rctOps.h" namespace cryptonote @@ -50,13 +51,16 @@ namespace cryptonote struct tx_source_entry { - typedef std::pair output_entry; + typedef std::pair output_entry; - std::vector outputs; //index + key + std::vector outputs; //index + key + optional ringct commitment size_t real_output; //index in outputs vector of real output_entry crypto::public_key real_out_tx_key; //incoming real tx public key size_t real_output_in_tx_index; //index in transaction outputs vector uint64_t amount; //money + rct::key mask; //ringct amount mask + + void push_output(uint64_t idx, const crypto::public_key &k, uint64_t amount) { outputs.push_back(std::make_pair(idx, rct::ctkey({rct::pk2rct(k), rct::zeroCommit(amount)}))); } }; struct tx_destination_entry @@ -70,7 +74,7 @@ namespace cryptonote //--------------------------------------------------------------- bool construct_tx(const account_keys& sender_account_keys, const std::vector& sources, const std::vector& destinations, std::vector extra, transaction& tx, uint64_t unlock_time); - bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector& sources, const std::vector& destinations, std::vector extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &txkey); + bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector& sources, const std::vector& destinations, std::vector extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &txkey, bool rct = false); template bool find_tx_extra_field_by_type(const std::vector& tx_extra_fields, T& field) diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index bb3a6dc3e..7b7c22887 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -78,6 +78,19 @@ namespace cryptonote //--------------------------------------------------------------------------------- bool tx_memory_pool::add_tx(const transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, size_t blob_size, tx_verification_context& tvc, bool kept_by_block, bool relayed, uint8_t version) { + if (tx.version == 0) + { + // v0 never accepted + tvc.m_verifivation_failed = true; + return false; + } + if (tx.version > 2) // TODO: max 1/2 needs to be conditioned by a hard fork + { + // v2 is the latest one we know + tvc.m_verifivation_failed = true; + return false; + } + // we do not accept transactions that timed out before, unless they're // kept_by_block if (!kept_by_block && m_timed_out_transactions.find(id) != m_timed_out_transactions.end()) @@ -95,25 +108,34 @@ namespace cryptonote return false; } - uint64_t inputs_amount = 0; - if(!get_inputs_money_amount(tx, inputs_amount)) + // fee per kilobyte, size rounded up. + uint64_t fee; + + if (tx.version == 1) { - tvc.m_verifivation_failed = true; - return false; - } + uint64_t inputs_amount = 0; + if(!get_inputs_money_amount(tx, inputs_amount)) + { + tvc.m_verifivation_failed = true; + return false; + } - uint64_t outputs_amount = get_outs_money_amount(tx); + uint64_t outputs_amount = get_outs_money_amount(tx); + if(outputs_amount >= inputs_amount) + { + LOG_PRINT_L1("transaction use more money then it has: use " << print_money(outputs_amount) << ", have " << print_money(inputs_amount)); + tvc.m_verifivation_failed = true; + tvc.m_overspend = true; + return false; + } - if(outputs_amount >= inputs_amount) + fee = inputs_amount - outputs_amount; + } + else { - LOG_PRINT_L1("transaction use more money then it has: use " << print_money(outputs_amount) << ", have " << print_money(inputs_amount)); - tvc.m_verifivation_failed = true; - tvc.m_overspend = true; - return false; + fee = tx.rct_signatures.txnFee; } - // fee per kilobyte, size rounded up. - uint64_t fee = inputs_amount - outputs_amount; uint64_t needed_fee = blob_size / 1024; needed_fee += (blob_size % 1024) ? 1 : 0; needed_fee *= FEE_PER_KB; @@ -150,7 +172,7 @@ namespace cryptonote if (!m_blockchain.check_tx_outputs(tx, tvc)) { - LOG_PRINT_L1("Transaction with id= "<< id << " has at least one invalid outout"); + LOG_PRINT_L1("Transaction with id= "<< id << " has at least one invalid output"); tvc.m_verifivation_failed = true; tvc.m_invalid_output = true; return false; @@ -170,7 +192,7 @@ namespace cryptonote CHECK_AND_ASSERT_MES(txd_p.second, false, "transaction already exists at inserting in memory pool"); txd_p.first->second.blob_size = blob_size; txd_p.first->second.tx = tx; - txd_p.first->second.fee = inputs_amount - outputs_amount; + txd_p.first->second.fee = fee; txd_p.first->second.max_used_block_id = null_hash; txd_p.first->second.max_used_block_height = 0; txd_p.first->second.kept_by_block = kept_by_block; @@ -193,7 +215,7 @@ namespace cryptonote txd_p.first->second.blob_size = blob_size; txd_p.first->second.tx = tx; txd_p.first->second.kept_by_block = kept_by_block; - txd_p.first->second.fee = inputs_amount - outputs_amount; + txd_p.first->second.fee = fee; txd_p.first->second.max_used_block_id = max_used_block_id; txd_p.first->second.max_used_block_height = max_used_block_height; txd_p.first->second.last_failed_height = 0; -- cgit v1.2.3 From ada527558fc2fcd0e8b01f2d76c15bf2d4a72a91 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 25 Jun 2016 22:52:18 +0100 Subject: Use the supplied hard fork version in validate_miner_transaction rather than using the current one. No functional changes, but may save some bugs in the future. --- src/cryptonote_core/blockchain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/cryptonote_core') diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 77794d14d..0d7294ffd 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -983,7 +983,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl std::vector last_blocks_sizes; get_last_n_blocks_sizes(last_blocks_sizes, CRYPTONOTE_REWARD_BLOCKS_WINDOW); - if (!get_block_reward(epee::misc_utils::median(last_blocks_sizes), cumulative_block_size, already_generated_coins, base_reward, get_current_hard_fork_version())) + if (!get_block_reward(epee::misc_utils::median(last_blocks_sizes), cumulative_block_size, already_generated_coins, base_reward, version)) { LOG_PRINT_L1("block size " << cumulative_block_size << " is bigger than allowed for this blockchain"); return false; -- cgit v1.2.3 From 20e50ec7f7f4104b5eb177aa30df4f5d4db9c8c3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 29 Jun 2016 18:18:18 +0100 Subject: ringct: do not serialize what can be reconstructed The mixRing (output keys and commitments) and II fields (key images) can be reconstructed from vin data. This saves some modest amount of space in the tx. --- src/cryptonote_core/blockchain.cpp | 87 +++++++++++++++++----- .../cryptonote_boost_serialization.h | 18 ++++- 2 files changed, 81 insertions(+), 24 deletions(-) (limited to 'src/cryptonote_core') diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 0d7294ffd..82bc6c0d4 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2461,6 +2461,13 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context else { // from version 2, check ringct signatures + rct::ctkeyM reconstructed_mixRing; + rct::keyV reconstructed_II; + + // if the tx already has a non empty mixRing and/or II, use them, + // else reconstruct them + const rct::ctkeyM &mixRing = tx.rct_signatures.mixRing.empty() ? reconstructed_mixRing : tx.rct_signatures.mixRing; + const rct::keyV &II = tx.rct_signatures.MG.II.size() == 1 ? reconstructed_II : tx.rct_signatures.MG.II; // RCT needs the same mixin for all inputs for (size_t n = 1; n < pubkeys.size(); ++n) @@ -2472,35 +2479,74 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context } } - bool size_matches = true; - for (size_t i = 0; i < pubkeys.size(); ++i) - size_matches &= pubkeys[i].size() == tx.rct_signatures.mixRing.size(); - for (size_t i = 0; i < tx.rct_signatures.mixRing.size(); ++i) - size_matches &= pubkeys.size() == tx.rct_signatures.mixRing[i].size(); - if (!size_matches) + if (tx.rct_signatures.mixRing.empty()) { - LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkeys/mixRing size"); - return false; + reconstructed_mixRing.resize(pubkeys[0].size()); + for (size_t n = 0; n < pubkeys.size(); ++n) + { + for (size_t m = 0; m < pubkeys[n].size(); ++m) + { + reconstructed_mixRing[m].push_back(pubkeys[n][m]); + } + } } - for (size_t n = 0; n < pubkeys.size(); ++n) + if (tx.rct_signatures.MG.II.size() == 1) { - for (size_t m = 0; m < pubkeys[n].size(); ++m) + reconstructed_II.resize(tx.vin.size()); + for (size_t n = 0; n < tx.vin.size(); ++n) { - if (pubkeys[n][m].dest != rct::rct2pk(tx.rct_signatures.mixRing[m][n].dest)) - { - LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkey at vin " << n << ", index " << m); - return false; - } - if (pubkeys[n][m].mask != rct::rct2pk(tx.rct_signatures.mixRing[m][n].mask)) + reconstructed_II[n] = rct::ki2rct(boost::get(tx.vin[n]).k_image); + } + reconstructed_II.push_back(tx.rct_signatures.MG.II.back()); + } + + // check all this, either recontructed (so should really pass), or not + { + bool size_matches = true; + for (size_t i = 0; i < pubkeys.size(); ++i) + size_matches &= pubkeys[i].size() == mixRing.size(); + for (size_t i = 0; i < tx.rct_signatures.mixRing.size(); ++i) + size_matches &= pubkeys.size() == mixRing[i].size(); + if (!size_matches) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkeys/mixRing size"); + return false; + } + + for (size_t n = 0; n < pubkeys.size(); ++n) + { + for (size_t m = 0; m < pubkeys[n].size(); ++m) { - LOG_PRINT_L1("Failed to check ringct signatures: mismatched commitment at vin " << n << ", index " << m); - return false; + if (pubkeys[n][m].dest != rct::rct2pk(mixRing[m][n].dest)) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkey at vin " << n << ", index " << m); + return false; + } + if (pubkeys[n][m].mask != rct::rct2pk(mixRing[m][n].mask)) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched commitment at vin " << n << ", index " << m); + return false; + } } } } - if (!rct::verRct(tx.rct_signatures)) + if (II.size() != 1 + tx.vin.size()) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched II/vin sizes"); + return false; + } + for (size_t n = 0; n < tx.vin.size(); ++n) + { + if (memcmp(&boost::get(tx.vin[n]).k_image, &II[n], 32)) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched II/vin sizes"); + return false; + } + } + + if (!rct::verRct(tx.rct_signatures, mixRing, II, rct::hash2rct(tx_prefix_hash))) { LOG_PRINT_L1("Failed to check ringct signatures!"); return false; @@ -2613,7 +2659,8 @@ bool Blockchain::check_tx_input(size_t tx_version, const txin_to_key& txin, cons } else { - CHECK_AND_ASSERT_MES(rct_signatures.mixRing.size() == output_keys.size(), false, "internal error: tx rct signatures count=" << sig.size() << " mismatch with outputs keys count for inputs=" << output_keys.size()); + // rct signatures may be empty (and will be reconstructed later in the caller if so) + CHECK_AND_ASSERT_MES(rct_signatures.mixRing.empty() || rct_signatures.mixRing.size() == output_keys.size(), false, "internal error: tx rct signatures count=" << sig.size() << " mismatch with outputs keys count for inputs=" << output_keys.size()); } return true; } diff --git a/src/cryptonote_core/cryptonote_boost_serialization.h b/src/cryptonote_core/cryptonote_boost_serialization.h index 6eeb66ec8..ccdd55883 100644 --- a/src/cryptonote_core/cryptonote_boost_serialization.h +++ b/src/cryptonote_core/cryptonote_boost_serialization.h @@ -37,6 +37,8 @@ #include #include #include +#include +#include #include "cryptonote_basic.h" #include "common/unordered_containers_boost_serialization.h" #include "crypto/crypto.h" @@ -196,12 +198,19 @@ namespace boost a & x.s; } - template - inline void serialize(Archive &a, rct::mgSig &x, const boost::serialization::version_type ver) + inline void serialize(boost::archive::binary_iarchive &a, rct::mgSig &x, const boost::serialization::version_type ver) + { + a & x.ss; + a & x.cc; + x.II.resize(1); + a & x.II[0]; + } + + inline void serialize(boost::archive::binary_oarchive &a, rct::mgSig &x, const boost::serialization::version_type ver) { a & x.ss; a & x.cc; - a & x.II; + a & x.II.back(); } template @@ -217,10 +226,11 @@ namespace boost { a & x.rangeSigs; a & x.MG; - a & x.mixRing; + // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets a & x.ecdhInfo; a & x.outPk; a & x.txnFee; + // a & x.bash_hash; bash_hash is not serialized, as it can be reconstructed from the tx data } } } -- cgit v1.2.3 From 6d0e47148bc400ca6668860e162fe2f4ad8a3dac Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 28 Jun 2016 16:03:28 +0100 Subject: rct: add the tx prefix hash into the MLSAG to protect the non-signatures parts of the tx from tampering. --- src/cryptonote_core/cryptonote_format_utils.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/cryptonote_core') diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index 9f0693b30..4cc62c165 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -685,9 +685,6 @@ namespace cryptonote if (amount_in > amount_out) amounts.push_back(amount_in - amount_out); - LOG_PRINT_L1("Signing tx: " << obj_to_json_str(tx)); - tx.rct_signatures = rct::genRct(inSk, destinations, amounts, mixRing, sources[0].real_output); // same index assumption - // zero out all amounts to mask rct outputs, real amounts are now encrypted for (size_t i = 0; i < tx.vin.size(); ++i) { @@ -697,6 +694,10 @@ namespace cryptonote for (size_t i = 0; i < tx.vout.size(); ++i) tx.vout[i].amount = 0; + crypto::hash tx_prefix_hash; + get_transaction_prefix_hash(tx, tx_prefix_hash); + tx.rct_signatures = rct::genRct(inSk, destinations, amounts, mixRing, rct::hash2rct(tx_prefix_hash), sources[0].real_output); // same index assumption + LOG_PRINT2("construct_tx.log", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL, LOG_LEVEL_3); } -- cgit v1.2.3 From 59a66e209a844ac6d4221f7c04141b32fa2823c3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 29 Jun 2016 19:55:49 +0100 Subject: move the rct commitments to the output_amounts database Since these are needed at the same time as the output pubkeys, this is a whole lot faster, and takes less space. Only outputs of 0 amount store the commitment. When reading other outputs, a fake commitment is regenerated on the fly. This avoids having to rewrite the database to add space for fake commitments for existing outputs. This code relies on two things: - LMDB must support fixed size records per key, rather than per database (ie, all records on key 0 are the same size, all records for non 0 keys are same size, but records from key 0 and non 0 keys do have different sizes). - the commitment must be directly after the rest of the data in outkey and output_data_t. --- src/cryptonote_core/blockchain.cpp | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) (limited to 'src/cryptonote_core') diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 82bc6c0d4..f46de9ca8 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -207,21 +207,8 @@ bool Blockchain::scan_outputkeys_for_indexes(size_t tx_version, const txin_to_ke else output_index = m_db->get_output_key(tx_in_to_key.amount, i); - rct::key commitment; - if (tx_version > 1) - { - if (tx_in_to_key.amount == 0) - commitment = m_db->get_rct_commitment(i); - else - commitment = rct::zeroCommit(tx_in_to_key.amount); - } - else - { - rct::identity(commitment); - } - // call to the passed boost visitor to grab the public key for the output - if (!vis.handle_output(output_index.unlock_time, output_index.pubkey, commitment)) + if (!vis.handle_output(output_index.unlock_time, output_index.pubkey, output_index.commitment)) { LOG_PRINT_L0("Failed to handle_output for output no = " << count << ", with absolute offset " << i); return false; @@ -1633,15 +1620,7 @@ void Blockchain::add_out_to_get_rct_random_outs(std::listget_output_key(amount, i); oen.out_key = data.pubkey; - if (amount == 0) - { - oen.commitment = m_db->get_rct_commitment(i); - } - else - { - // not a rct output, make a fake commitment with zero key - oen.commitment = rct::zeroCommit(amount); - } + oen.commitment = data.commitment; } //------------------------------------------------------------------ // This function takes an RPC request for mixins and creates an RPC response -- cgit v1.2.3 From f5465d824663ae67d5392a1a0f986027975063b7 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 29 Jun 2016 23:00:20 +0100 Subject: Condition v2 txes on v3 hard fork --- src/cryptonote_core/cryptonote_core.cpp | 9 +++++++++ src/cryptonote_core/tx_pool.cpp | 4 +++- 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'src/cryptonote_core') diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 5179cc8d2..880162ed2 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -498,6 +498,15 @@ namespace cryptonote } //std::cout << "!"<< tx.vin.size() << std::endl; + uint8_t version = m_blockchain_storage.get_current_hard_fork_version(); + const size_t max_tx_version = version == 1 ? 1 : 2; + if (tx.version == 0 || tx.version > max_tx_version) + { + // v2 is the latest one we know + tvc.m_verifivation_failed = true; + return false; + } + if(!check_tx_syntax(tx)) { LOG_PRINT_L1("WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " syntax, rejected"); diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 7b7c22887..c2414f581 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -84,7 +84,9 @@ namespace cryptonote tvc.m_verifivation_failed = true; return false; } - if (tx.version > 2) // TODO: max 1/2 needs to be conditioned by a hard fork + + const size_t max_tx_version = version == 1 ? 1 : 2; + if (tx.version > max_tx_version) { // v2 is the latest one we know tvc.m_verifivation_failed = true; -- cgit v1.2.3 From c2ec6d35b9a8130fe1253d4933741506fee504cf Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 2 Jul 2016 10:02:12 +0100 Subject: mixable transactions must be rct for v3 --- src/cryptonote_core/blockchain.cpp | 34 ++++++++++++++++++++++++------ src/cryptonote_core/verification_context.h | 1 + 2 files changed, 29 insertions(+), 6 deletions(-) (limited to 'src/cryptonote_core') diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index f46de9ca8..bef880627 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2262,17 +2262,39 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context if (txin.type() == typeid(txin_to_key)) { const txin_to_key& in_to_key = boost::get(txin); - uint64_t n_outputs = m_db->get_num_outputs(in_to_key.amount); - LOG_PRINT_L2("output size " << print_money(in_to_key.amount) << ": " << n_outputs << " available"); - // n_outputs includes the output we're considering - if (n_outputs <= 2) - ++n_unmixable; - else + if (in_to_key.amount == 0) + { + // always consider rct inputs mixable. Even if there's not enough rct + // inputs on the chain to mix with, this is going to be the case for + // just a few blocks right after the fork at most ++n_mixable; + } + else + { + uint64_t n_outputs = m_db->get_num_outputs(in_to_key.amount); + LOG_PRINT_L2("output size " << print_money(in_to_key.amount) << ": " << n_outputs << " available"); + // n_outputs includes the output we're considering + if (n_outputs <= 2) + ++n_unmixable; + else + ++n_mixable; + } if (in_to_key.key_offsets.size() - 1 < mixin) mixin = in_to_key.key_offsets.size() - 1; } } + + // for v3, we force txes with all mixable inputs to be rct + if (m_hardfork->get_current_version() >= 3) + { + if (n_unmixable == 0 && tx.version == 1) + { + LOG_PRINT_L1("Tx " << get_transaction_hash(tx) << " is not rct and does not have unmixable inputs"); + tvc.m_not_rct = true; + return false; + } + } + if (mixin < 2) { if (n_unmixable == 0) diff --git a/src/cryptonote_core/verification_context.h b/src/cryptonote_core/verification_context.h index e58291ea9..0bb635e84 100644 --- a/src/cryptonote_core/verification_context.h +++ b/src/cryptonote_core/verification_context.h @@ -47,6 +47,7 @@ namespace cryptonote bool m_too_big; bool m_overspend; bool m_fee_too_low; + bool m_not_rct; }; struct block_verification_context -- cgit v1.2.3 From 4fd01f2bee0a64a72d67129e2f84007125e81ad2 Mon Sep 17 00:00:00 2001 From: Shen Noether Date: Sat, 9 Jul 2016 19:30:28 +0100 Subject: ringct: "simple" ringct variant Allows the fake outs to be in different positions for each ring. For rct inputs only. --- src/cryptonote_core/cryptonote_boost_serialization.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'src/cryptonote_core') diff --git a/src/cryptonote_core/cryptonote_boost_serialization.h b/src/cryptonote_core/cryptonote_boost_serialization.h index ccdd55883..01239c5ae 100644 --- a/src/cryptonote_core/cryptonote_boost_serialization.h +++ b/src/cryptonote_core/cryptonote_boost_serialization.h @@ -232,6 +232,19 @@ namespace boost a & x.txnFee; // a & x.bash_hash; bash_hash is not serialized, as it can be reconstructed from the tx data } + + template + inline void serialize(Archive &a, rct::sRctSig &x, const boost::serialization::version_type ver) + { + // a & x.message; message is not serialized, as it can be reconstructed from the tx data + a & x.rangeSigs; + a & x.MG; + // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets + a & x.pseudoOuts; + a & x.ecdhInfo; + a & x.outPk; + a & x.txnFee; + } } } -- cgit v1.2.3 From a4d4d6194bf9a0826806521d72a7fec5e506a677 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 10 Jul 2016 12:57:22 +0100 Subject: integrate simple rct api --- src/cryptonote_core/blockchain.cpp | 212 +++++++++++++++------ src/cryptonote_core/cryptonote_basic.h | 12 +- .../cryptonote_boost_serialization.h | 21 +- src/cryptonote_core/cryptonote_format_utils.cpp | 86 ++++++--- 4 files changed, 229 insertions(+), 102 deletions(-) (limited to 'src/cryptonote_core') diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index bef880627..456a78eaf 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2462,95 +2462,193 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context else { // from version 2, check ringct signatures - rct::ctkeyM reconstructed_mixRing; - rct::keyV reconstructed_II; - - // if the tx already has a non empty mixRing and/or II, use them, - // else reconstruct them - const rct::ctkeyM &mixRing = tx.rct_signatures.mixRing.empty() ? reconstructed_mixRing : tx.rct_signatures.mixRing; - const rct::keyV &II = tx.rct_signatures.MG.II.size() == 1 ? reconstructed_II : tx.rct_signatures.MG.II; - - // RCT needs the same mixin for all inputs - for (size_t n = 1; n < pubkeys.size(); ++n) + // obviously, the original and simple rct APIs use a mixRing that's indexes + // in opposite orders, because it'd be too simple otherwise... + if (tx.rct_signatures.simple) { - if (pubkeys[n].size() != pubkeys[0].size()) - { - LOG_PRINT_L1("Failed to check ringct signatures: mismatched ring sizes"); - return false; - } - } + rct::ctkeyM reconstructed_mixRing; + std::vector reconstructed_II; - if (tx.rct_signatures.mixRing.empty()) - { - reconstructed_mixRing.resize(pubkeys[0].size()); - for (size_t n = 0; n < pubkeys.size(); ++n) + // if the tx already has a non empty mixRing, use them, + // else reconstruct them + const rct::ctkeyM &mixRing = tx.rct_signatures.mixRing.empty() ? reconstructed_mixRing : tx.rct_signatures.mixRing; + // always do II, because it's split in the simple version + + // all MGs should have the same II size (1) + for (size_t n = 0; n < tx.rct_signatures.MGs.size(); ++n) { - for (size_t m = 0; m < pubkeys[n].size(); ++m) + if (tx.rct_signatures.MGs[n].II.size() != 1) { - reconstructed_mixRing[m].push_back(pubkeys[n][m]); + LOG_PRINT_L1("Failed to check ringct signatures: mismatched MGs II sizes"); + return false; } } - } - if (tx.rct_signatures.MG.II.size() == 1) - { reconstructed_II.resize(tx.vin.size()); for (size_t n = 0; n < tx.vin.size(); ++n) { - reconstructed_II[n] = rct::ki2rct(boost::get(tx.vin[n]).k_image); + reconstructed_II[n].push_back(rct::ki2rct(boost::get(tx.vin[n]).k_image)); + reconstructed_II[n].push_back(tx.rct_signatures.MGs[n].II[0]); } - reconstructed_II.push_back(tx.rct_signatures.MG.II.back()); - } - // check all this, either recontructed (so should really pass), or not - { - bool size_matches = true; - for (size_t i = 0; i < pubkeys.size(); ++i) - size_matches &= pubkeys[i].size() == mixRing.size(); - for (size_t i = 0; i < tx.rct_signatures.mixRing.size(); ++i) - size_matches &= pubkeys.size() == mixRing[i].size(); - if (!size_matches) + if (tx.rct_signatures.mixRing.empty()) { - LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkeys/mixRing size"); - return false; + reconstructed_mixRing.resize(pubkeys.size()); + for (size_t n = 0; n < pubkeys.size(); ++n) + { + for (size_t m = 0; m < pubkeys[n].size(); ++m) + { + reconstructed_mixRing[n].push_back(pubkeys[n][m]); + } + } } - for (size_t n = 0; n < pubkeys.size(); ++n) + // check all this, either recontructed (so should really pass), or not { - for (size_t m = 0; m < pubkeys[n].size(); ++m) + if (pubkeys.size() != mixRing.size()) { - if (pubkeys[n][m].dest != rct::rct2pk(mixRing[m][n].dest)) + LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkeys/mixRing size"); + return false; + } + for (size_t i = 0; i < pubkeys.size(); ++i) + { + if (pubkeys[i].size() != mixRing[i].size()) { - LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkey at vin " << n << ", index " << m); + LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkeys/mixRing size"); return false; } - if (pubkeys[n][m].mask != rct::rct2pk(mixRing[m][n].mask)) + } + + for (size_t n = 0; n < pubkeys.size(); ++n) + { + for (size_t m = 0; m < pubkeys[n].size(); ++m) { - LOG_PRINT_L1("Failed to check ringct signatures: mismatched commitment at vin " << n << ", index " << m); - return false; + if (pubkeys[n][m].dest != rct::rct2pk(mixRing[n][m].dest)) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkey at vin " << n << ", index " << m); + return false; + } + if (pubkeys[n][m].mask != rct::rct2pk(mixRing[n][m].mask)) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched commitment at vin " << n << ", index " << m); + return false; + } } } } - } - if (II.size() != 1 + tx.vin.size()) - { - LOG_PRINT_L1("Failed to check ringct signatures: mismatched II/vin sizes"); - return false; + if (tx.rct_signatures.MGs.size() != tx.vin.size()) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched MGs/vin sizes"); + return false; + } + for (size_t n = 0; n < tx.vin.size(); ++n) + { + if (memcmp(&boost::get(tx.vin[n]).k_image, &reconstructed_II[n][0], 32)) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched key image"); + return false; + } + } + + if (!rct::verRctSimple(tx.rct_signatures, mixRing, &reconstructed_II, rct::hash2rct(tx_prefix_hash))) + { + LOG_PRINT_L1("Failed to check ringct signatures!"); + return false; + } } - for (size_t n = 0; n < tx.vin.size(); ++n) + else { - if (memcmp(&boost::get(tx.vin[n]).k_image, &II[n], 32)) + rct::ctkeyM reconstructed_mixRing; + rct::keyV reconstructed_II; + + // if the tx already has a non empty mixRing and/or II, use them, + // else reconstruct them + const rct::ctkeyM &mixRing = tx.rct_signatures.mixRing.empty() ? reconstructed_mixRing : tx.rct_signatures.mixRing; + const rct::keyV &II = tx.rct_signatures.MG.II.size() == 1 ? reconstructed_II : tx.rct_signatures.MG.II; + + // RCT needs the same mixin for all inputs + for (size_t n = 1; n < pubkeys.size(); ++n) + { + if (pubkeys[n].size() != pubkeys[0].size()) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched ring sizes"); + return false; + } + } + + if (tx.rct_signatures.mixRing.empty()) + { + reconstructed_mixRing.resize(pubkeys[0].size()); + for (size_t n = 0; n < pubkeys.size(); ++n) + { + for (size_t m = 0; m < pubkeys[n].size(); ++m) + { + reconstructed_mixRing[m].push_back(pubkeys[n][m]); + } + } + } + + if (tx.rct_signatures.MG.II.size() == 1) + { + reconstructed_II.resize(tx.vin.size()); + for (size_t n = 0; n < tx.vin.size(); ++n) + { + reconstructed_II[n] = rct::ki2rct(boost::get(tx.vin[n]).k_image); + } + reconstructed_II.push_back(tx.rct_signatures.MG.II.back()); + } + + // check all this, either recontructed (so should really pass), or not + { + bool size_matches = true; + for (size_t i = 0; i < pubkeys.size(); ++i) + size_matches &= pubkeys[i].size() == mixRing.size(); + for (size_t i = 0; i < tx.rct_signatures.mixRing.size(); ++i) + size_matches &= pubkeys.size() == mixRing[i].size(); + if (!size_matches) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkeys/mixRing size"); + return false; + } + + for (size_t n = 0; n < pubkeys.size(); ++n) + { + for (size_t m = 0; m < pubkeys[n].size(); ++m) + { + if (pubkeys[n][m].dest != rct::rct2pk(mixRing[m][n].dest)) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkey at vin " << n << ", index " << m); + return false; + } + if (pubkeys[n][m].mask != rct::rct2pk(mixRing[m][n].mask)) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched commitment at vin " << n << ", index " << m); + return false; + } + } + } + } + + if (II.size() != 1 + tx.vin.size()) { LOG_PRINT_L1("Failed to check ringct signatures: mismatched II/vin sizes"); return false; } - } + for (size_t n = 0; n < tx.vin.size(); ++n) + { + if (memcmp(&boost::get(tx.vin[n]).k_image, &II[n], 32)) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched II/vin sizes"); + return false; + } + } - if (!rct::verRct(tx.rct_signatures, mixRing, II, rct::hash2rct(tx_prefix_hash))) - { - LOG_PRINT_L1("Failed to check ringct signatures!"); - return false; + if (!rct::verRct(tx.rct_signatures, mixRing, II, rct::hash2rct(tx_prefix_hash))) + { + LOG_PRINT_L1("Failed to check ringct signatures!"); + return false; + } } } return true; diff --git a/src/cryptonote_core/cryptonote_basic.h b/src/cryptonote_core/cryptonote_basic.h index 91bcef8c5..afe785eb9 100644 --- a/src/cryptonote_core/cryptonote_basic.h +++ b/src/cryptonote_core/cryptonote_basic.h @@ -231,11 +231,19 @@ namespace cryptonote else { FIELD(rct_signatures) - for (size_t i = 0; i < rct_signatures.mixRing.size(); ++i) + if (rct_signatures.simple) { - if (rct_signatures.mixRing[i].size() != vin.size()) + if (rct_signatures.mixRing.size() && rct_signatures.mixRing.size() != vin.size()) return false; } + else + { + for (size_t i = 0; i < rct_signatures.mixRing.size(); ++i) + { + if (rct_signatures.mixRing[i].size() != vin.size()) + return false; + } + } } END_SERIALIZE() diff --git a/src/cryptonote_core/cryptonote_boost_serialization.h b/src/cryptonote_core/cryptonote_boost_serialization.h index 01239c5ae..35fabe7fb 100644 --- a/src/cryptonote_core/cryptonote_boost_serialization.h +++ b/src/cryptonote_core/cryptonote_boost_serialization.h @@ -224,23 +224,16 @@ namespace boost template inline void serialize(Archive &a, rct::rctSig &x, const boost::serialization::version_type ver) { - a & x.rangeSigs; - a & x.MG; - // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets - a & x.ecdhInfo; - a & x.outPk; - a & x.txnFee; - // a & x.bash_hash; bash_hash is not serialized, as it can be reconstructed from the tx data - } - - template - inline void serialize(Archive &a, rct::sRctSig &x, const boost::serialization::version_type ver) - { + a & x.simple; // a & x.message; message is not serialized, as it can be reconstructed from the tx data a & x.rangeSigs; - a & x.MG; + if (x.simple) + a & x.MGs; + else + a & x.MG; // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets - a & x.pseudoOuts; + if (x.simple) + a & x.pseudoOuts; a & x.ecdhInfo; a & x.outPk; a & x.txnFee; diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index 4cc62c165..6578776b4 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -516,19 +516,6 @@ namespace cryptonote }; std::vector in_contexts; - if (tx.version > 1) - { - // ringct requires all real inputs to be at the same index for all inputs // TODO - BOOST_FOREACH(const tx_source_entry& src_entr, sources) - { - if(src_entr.real_output != sources.begin()->real_output) - { - LOG_ERROR("All inputs must have the same index for ringct"); - return false; - } - } - } - uint64_t summary_inputs_money = 0; //fill inputs BOOST_FOREACH(const tx_source_entry& src_entr, sources) @@ -641,24 +628,46 @@ namespace cryptonote } else { - // enforce same mixin for all outputs - size_t n_total_outs = sources[0].outputs.size(); - for (size_t i = 1; i < sources.size(); ++i) { - if (n_total_outs != sources[i].outputs.size()) { - LOG_ERROR("Ringct transaction has varying mixin"); - return false; + bool all_rct_inputs = true; + size_t n_total_outs = sources[0].outputs.size(); // only for non-simple rct + BOOST_FOREACH(const tx_source_entry& src_entr, sources) + all_rct_inputs &= !(src_entr.mask == rct::identity()); + bool use_simple_rct = all_rct_inputs; + + if (!use_simple_rct) + { + // non simple ringct requires all real inputs to be at the same index for all inputs + BOOST_FOREACH(const tx_source_entry& src_entr, sources) + { + if(src_entr.real_output != sources.begin()->real_output) + { + LOG_ERROR("All inputs must have the same index for non-simple ringct"); + return false; + } + } + + // enforce same mixin for all outputs + for (size_t i = 1; i < sources.size(); ++i) { + if (n_total_outs != sources[i].outputs.size()) { + LOG_ERROR("Non-simple ringct transaction has varying mixin"); + return false; + } } } uint64_t amount_in = 0, amount_out = 0; rct::ctkeyV inSk; - rct::ctkeyM mixRing(n_total_outs); + // mixRing indexing is done the other way round for simple + rct::ctkeyM mixRing(use_simple_rct ? sources.size() : n_total_outs); rct::keyV destinations; - std::vector amounts; + std::vector inamounts, outamounts; + std::vector index; for (size_t i = 0; i < sources.size(); ++i) { rct::ctkey ctkey; amount_in += sources[i].amount; + inamounts.push_back(sources[i].amount); + index.push_back(sources[i].real_output); // inSk: (secret key, mask) ctkey.dest = rct::sk2rct(in_contexts[i].in_ephemeral.sec); ctkey.mask = sources[i].mask; @@ -669,21 +678,37 @@ namespace cryptonote for (size_t i = 0; i < tx.vout.size(); ++i) { destinations.push_back(rct::pk2rct(boost::get(tx.vout[i].target).key)); - amounts.push_back(tx.vout[i].amount); + outamounts.push_back(tx.vout[i].amount); amount_out += tx.vout[i].amount; } - for (size_t i = 0; i < n_total_outs; ++i) // same index assumption + + if (use_simple_rct) + { + // mixRing indexing is done the other way round for simple + for (size_t i = 0; i < sources.size(); ++i) + { + mixRing[i].resize(sources[i].outputs.size()); + for (size_t n = 0; n < sources[i].outputs.size(); ++n) + { + mixRing[i][n] = sources[i].outputs[n].second; + } + } + } + else { - mixRing[i].resize(sources.size()); - for (size_t n = 0; n < sources.size(); ++n) + for (size_t i = 0; i < n_total_outs; ++i) // same index assumption { - mixRing[i][n] = sources[n].outputs[i].second; + mixRing[i].resize(sources.size()); + for (size_t n = 0; n < sources.size(); ++n) + { + mixRing[i][n] = sources[n].outputs[i].second; + } } } // fee - if (amount_in > amount_out) - amounts.push_back(amount_in - amount_out); + if (!use_simple_rct && amount_in > amount_out) + outamounts.push_back(amount_in - amount_out); // zero out all amounts to mask rct outputs, real amounts are now encrypted for (size_t i = 0; i < tx.vin.size(); ++i) @@ -696,7 +721,10 @@ namespace cryptonote crypto::hash tx_prefix_hash; get_transaction_prefix_hash(tx, tx_prefix_hash); - tx.rct_signatures = rct::genRct(inSk, destinations, amounts, mixRing, rct::hash2rct(tx_prefix_hash), sources[0].real_output); // same index assumption + if (use_simple_rct) + tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, index); + else + tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, sources[0].real_output); // same index assumption LOG_PRINT2("construct_tx.log", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL, LOG_LEVEL_3); } -- cgit v1.2.3 From e81a2b2cfabfb1ad9aaade752a863f1448fc89cd Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 11 Jul 2016 23:14:58 +0100 Subject: port get_tx_key/check_tx_key to rct --- src/cryptonote_core/cryptonote_format_utils.cpp | 18 ++++++++++++++---- src/cryptonote_core/cryptonote_format_utils.h | 2 +- 2 files changed, 15 insertions(+), 5 deletions(-) (limited to 'src/cryptonote_core') diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index 6578776b4..5f8e4024f 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -453,12 +453,13 @@ namespace cryptonote return encrypt_payment_id(payment_id, public_key, secret_key); } //--------------------------------------------------------------- - bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector& sources, const std::vector& destinations, std::vector extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct) + bool construct_tx_and_get_tx_keys(const account_keys& sender_account_keys, const std::vector& sources, const std::vector& destinations, std::vector extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector &amount_keys, bool rct) { tx.vin.clear(); tx.vout.clear(); tx.signatures.clear(); tx.rct_signatures = rct::rctSig(); + amount_keys.clear(); tx.version = rct ? 2 : 1; tx.unlock_time = unlock_time; @@ -721,10 +722,18 @@ namespace cryptonote crypto::hash tx_prefix_hash; get_transaction_prefix_hash(tx, tx_prefix_hash); + rct::ctkeyV outSk; if (use_simple_rct) - tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, index); + tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, index, outSk); else - tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, sources[0].real_output); // same index assumption + tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, sources[0].real_output, outSk); // same index assumption + + CHECK_AND_ASSERT_MES(tx.vout.size() == outSk.size(), false, "outSk size does not match vout"); + for (size_t i = 0; i < tx.vout.size(); ++i) + { + amount_keys.push_back(rct::rct2sk(rct::d2h(shuffled_dsts[i].amount))); + amount_keys.push_back(rct::rct2sk(outSk[i].mask)); + } LOG_PRINT2("construct_tx.log", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL, LOG_LEVEL_3); } @@ -735,7 +744,8 @@ namespace cryptonote bool construct_tx(const account_keys& sender_account_keys, const std::vector& sources, const std::vector& destinations, std::vector extra, transaction& tx, uint64_t unlock_time) { crypto::secret_key tx_key; - return construct_tx_and_get_tx_key(sender_account_keys, sources, destinations, extra, tx, unlock_time, tx_key); + std::vector amount_keys; + return construct_tx_and_get_tx_keys(sender_account_keys, sources, destinations, extra, tx, unlock_time, tx_key, amount_keys); } //--------------------------------------------------------------- bool get_inputs_money_amount(const transaction& tx, uint64_t& money) diff --git a/src/cryptonote_core/cryptonote_format_utils.h b/src/cryptonote_core/cryptonote_format_utils.h index f4fd8bd34..6dac8e888 100644 --- a/src/cryptonote_core/cryptonote_format_utils.h +++ b/src/cryptonote_core/cryptonote_format_utils.h @@ -74,7 +74,7 @@ namespace cryptonote //--------------------------------------------------------------- bool construct_tx(const account_keys& sender_account_keys, const std::vector& sources, const std::vector& destinations, std::vector extra, transaction& tx, uint64_t unlock_time); - bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector& sources, const std::vector& destinations, std::vector extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &txkey, bool rct = false); + bool construct_tx_and_get_tx_keys(const account_keys& sender_account_keys, const std::vector& sources, const std::vector& destinations, std::vector extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector &amount_keys, bool rct = false); template bool find_tx_extra_field_by_type(const std::vector& tx_extra_fields, T& field) -- cgit v1.2.3 From 414b424d32684fcf2a560770bf3f87937c8b90bc Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 12 Jul 2016 22:35:49 +0100 Subject: core: always use the new simple rct variant --- src/cryptonote_core/cryptonote_format_utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/cryptonote_core') diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index 5f8e4024f..e0d8d9d08 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -633,7 +633,7 @@ namespace cryptonote size_t n_total_outs = sources[0].outputs.size(); // only for non-simple rct BOOST_FOREACH(const tx_source_entry& src_entr, sources) all_rct_inputs &= !(src_entr.mask == rct::identity()); - bool use_simple_rct = all_rct_inputs; + bool use_simple_rct = true; //all_rct_inputs; if (!use_simple_rct) { -- cgit v1.2.3 From cf33e1a52a0cf20a7cec619d85d68f000b2e1f40 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 23 Jul 2016 12:09:33 +0100 Subject: rct: do not serialize public keys in outPk They can be reconstructed from vout --- src/cryptonote_core/blockchain.cpp | 35 +++++++++++++++++++--- .../cryptonote_boost_serialization.h | 23 +++++++++++++- src/cryptonote_core/cryptonote_core.cpp | 8 ----- 3 files changed, 53 insertions(+), 13 deletions(-) (limited to 'src/cryptonote_core') diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 456a78eaf..9972b25e5 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2468,11 +2468,12 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context { rct::ctkeyM reconstructed_mixRing; std::vector reconstructed_II; + rct::ctkeyV reconstructed_outPk; // if the tx already has a non empty mixRing, use them, // else reconstruct them const rct::ctkeyM &mixRing = tx.rct_signatures.mixRing.empty() ? reconstructed_mixRing : tx.rct_signatures.mixRing; - // always do II, because it's split in the simple version + // always do II, because it's split in the simple version, and always do outPk // all MGs should have the same II size (1) for (size_t n = 0; n < tx.rct_signatures.MGs.size(); ++n) @@ -2491,6 +2492,18 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context reconstructed_II[n].push_back(tx.rct_signatures.MGs[n].II[0]); } + if (tx.rct_signatures.outPk.size() != tx.vout.size()) + { + LOG_PRINT_L1("Failed to check ringct signatures: outPk and vout have different sizes"); + return false; + } + reconstructed_outPk.resize(tx.vout.size()); + for (size_t n = 0; n < tx.vout.size(); ++n) + { + reconstructed_outPk[n].dest = rct::pk2rct(boost::get(tx.vout[n].target).key); + reconstructed_outPk[n].mask = tx.rct_signatures.outPk[n].mask; + } + if (tx.rct_signatures.mixRing.empty()) { reconstructed_mixRing.resize(pubkeys.size()); @@ -2551,7 +2564,7 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context } } - if (!rct::verRctSimple(tx.rct_signatures, mixRing, &reconstructed_II, rct::hash2rct(tx_prefix_hash))) + if (!rct::verRctSimple(tx.rct_signatures, mixRing, &reconstructed_II, reconstructed_outPk, rct::hash2rct(tx_prefix_hash))) { LOG_PRINT_L1("Failed to check ringct signatures!"); return false; @@ -2561,11 +2574,13 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context { rct::ctkeyM reconstructed_mixRing; rct::keyV reconstructed_II; + rct::ctkeyV reconstructed_outPk; // if the tx already has a non empty mixRing and/or II, use them, - // else reconstruct them + // else reconstruct them. Always do outPk. const rct::ctkeyM &mixRing = tx.rct_signatures.mixRing.empty() ? reconstructed_mixRing : tx.rct_signatures.mixRing; const rct::keyV &II = tx.rct_signatures.MG.II.size() == 1 ? reconstructed_II : tx.rct_signatures.MG.II; + const rct::ctkeyV outPk = reconstructed_outPk; // RCT needs the same mixin for all inputs for (size_t n = 1; n < pubkeys.size(); ++n) @@ -2599,6 +2614,18 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context reconstructed_II.push_back(tx.rct_signatures.MG.II.back()); } + if (tx.rct_signatures.outPk.size() != tx.vout.size()) + { + LOG_PRINT_L1("Failed to check ringct signatures: outPk and vout have different sizes"); + return false; + } + reconstructed_outPk.resize(tx.vout.size()); + for (size_t n = 0; n < tx.vout.size(); ++n) + { + reconstructed_outPk[n].dest = rct::pk2rct(boost::get(tx.vout[n].target).key); + reconstructed_outPk[n].mask = tx.rct_signatures.outPk[n].mask; + } + // check all this, either recontructed (so should really pass), or not { bool size_matches = true; @@ -2644,7 +2671,7 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context } } - if (!rct::verRct(tx.rct_signatures, mixRing, II, rct::hash2rct(tx_prefix_hash))) + if (!rct::verRct(tx.rct_signatures, mixRing, II, outPk, rct::hash2rct(tx_prefix_hash))) { LOG_PRINT_L1("Failed to check ringct signatures!"); return false; diff --git a/src/cryptonote_core/cryptonote_boost_serialization.h b/src/cryptonote_core/cryptonote_boost_serialization.h index 35fabe7fb..81f5f081a 100644 --- a/src/cryptonote_core/cryptonote_boost_serialization.h +++ b/src/cryptonote_core/cryptonote_boost_serialization.h @@ -43,6 +43,7 @@ #include "common/unordered_containers_boost_serialization.h" #include "crypto/crypto.h" #include "ringct/rctTypes.h" +#include "ringct/rctOps.h" //namespace cryptonote { namespace boost @@ -221,6 +222,26 @@ namespace boost a & x.senderPk; } + inline void serializeOutPk(boost::archive::binary_iarchive &a, rct::ctkeyV &outPk_, const boost::serialization::version_type ver) + { + rct::keyV outPk; + a & outPk; + outPk_.resize(outPk.size()); + for (size_t n = 0; n < outPk_.size(); ++n) + { + outPk_[n].dest = rct::identity(); + outPk_[n].mask = outPk[n]; + } + } + + inline void serializeOutPk(boost::archive::binary_oarchive &a, rct::ctkeyV &outPk_, const boost::serialization::version_type ver) + { + rct::keyV outPk(outPk_.size()); + for (size_t n = 0; n < outPk_.size(); ++n) + outPk[n] = outPk_[n].mask; + a & outPk; + } + template inline void serialize(Archive &a, rct::rctSig &x, const boost::serialization::version_type ver) { @@ -235,7 +256,7 @@ namespace boost if (x.simple) a & x.pseudoOuts; a & x.ecdhInfo; - a & x.outPk; + serializeOutPk(a, x.outPk, ver); a & x.txnFee; } } diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 880162ed2..511f50616 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -569,14 +569,6 @@ namespace cryptonote LOG_PRINT_RED_L1("tx with mismatched vout/outPk count, rejected for tx id= " << get_transaction_hash(tx)); return false; } - for (size_t n = 0; n < tx.vout.size(); ++n) - { - if (tx.rct_signatures.outPk[n].dest != boost::get(tx.vout[n].target).key) - { - LOG_PRINT_RED_L1("tx ringct public key does not match output public key for tx id= " << get_transaction_hash(tx)); - return false; - } - } } if(!check_money_overflow(tx)) -- cgit v1.2.3 From 9b70856ccb97943249f6e76b19f8abce5cd7aabe Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 24 Jul 2016 17:53:34 +0100 Subject: rct: make the amount key derivable by a third party with the tx key Scheme design from luigi1114. --- src/cryptonote_core/cryptonote_format_utils.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'src/cryptonote_core') diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index e0d8d9d08..ee9a00803 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -681,6 +681,7 @@ namespace cryptonote destinations.push_back(rct::pk2rct(boost::get(tx.vout[i].target).key)); outamounts.push_back(tx.vout[i].amount); amount_out += tx.vout[i].amount; + amount_keys.push_back(rct::rct2sk(rct::hash_to_scalar(rct::scalarmultKey(rct::pk2rct(shuffled_dsts[i].addr.m_view_public_key), rct::sk2rct(txkey.sec))))); } if (use_simple_rct) @@ -724,16 +725,11 @@ namespace cryptonote get_transaction_prefix_hash(tx, tx_prefix_hash); rct::ctkeyV outSk; if (use_simple_rct) - tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, index, outSk); + tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, (const rct::keyV&)amount_keys, index, outSk); else - tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, sources[0].real_output, outSk); // same index assumption + tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, (const rct::keyV&)amount_keys, sources[0].real_output, outSk); // same index assumption CHECK_AND_ASSERT_MES(tx.vout.size() == outSk.size(), false, "outSk size does not match vout"); - for (size_t i = 0; i < tx.vout.size(); ++i) - { - amount_keys.push_back(rct::rct2sk(rct::d2h(shuffled_dsts[i].amount))); - amount_keys.push_back(rct::rct2sk(outSk[i].mask)); - } LOG_PRINT2("construct_tx.log", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL, LOG_LEVEL_3); } -- cgit v1.2.3 From 37bdf6ebe3e0339fb195efb4083022dba8813e85 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 24 Jul 2016 18:30:27 +0100 Subject: change fork settings to allow pre-rct txes for one more fork cycle --- src/cryptonote_core/blockchain.cpp | 2 +- src/cryptonote_core/tx_pool.cpp | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'src/cryptonote_core') diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 9972b25e5..547f2d4c2 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2285,7 +2285,7 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context } // for v3, we force txes with all mixable inputs to be rct - if (m_hardfork->get_current_version() >= 3) + if (m_hardfork->get_current_version() >= 4) { if (n_unmixable == 0 && tx.version == 1) { diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index c2414f581..3666088f1 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -85,10 +85,15 @@ namespace cryptonote return false; } - const size_t max_tx_version = version == 1 ? 1 : 2; + const size_t max_tx_version = (version <= 3) ? 1 : 2; if (tx.version > max_tx_version) { - // v2 is the latest one we know + tvc.m_verifivation_failed = true; + return false; + } + const size_t min_tx_version = (version >= 5) ? 2 : 1; + if (tx.version < min_tx_version) + { tvc.m_verifivation_failed = true; return false; } -- cgit v1.2.3 From 1bf069825b368071db576a6f9139b7773f20f7c1 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 27 Jul 2016 10:10:38 +0100 Subject: tx_pool: log why a transaction was rejected for version checks --- src/cryptonote_core/tx_pool.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/cryptonote_core') diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 3666088f1..e7d3908e5 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -81,6 +81,7 @@ namespace cryptonote if (tx.version == 0) { // v0 never accepted + LOG_PRINT_L1("transaction version 0 is invalid"); tvc.m_verifivation_failed = true; return false; } @@ -88,12 +89,14 @@ namespace cryptonote const size_t max_tx_version = (version <= 3) ? 1 : 2; if (tx.version > max_tx_version) { + LOG_PRINT_L1("transaction version " << (unsigned)tx.version << " is higher than max accepted version " << max_tx_version); tvc.m_verifivation_failed = true; return false; } const size_t min_tx_version = (version >= 5) ? 2 : 1; if (tx.version < min_tx_version) { + LOG_PRINT_L1("transaction version " << (unsigned)tx.version << " is lower than min accepted version " << min_tx_version); tvc.m_verifivation_failed = true; return false; } -- cgit v1.2.3 From b337aea6cce707152143750e14b956a8ae40390f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 28 Jul 2016 20:14:15 +0100 Subject: rct: do not serialize senderPk - it is not used anymore --- src/cryptonote_core/cryptonote_boost_serialization.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/cryptonote_core') diff --git a/src/cryptonote_core/cryptonote_boost_serialization.h b/src/cryptonote_core/cryptonote_boost_serialization.h index 81f5f081a..f222db94e 100644 --- a/src/cryptonote_core/cryptonote_boost_serialization.h +++ b/src/cryptonote_core/cryptonote_boost_serialization.h @@ -219,7 +219,7 @@ namespace boost { a & x.mask; a & x.amount; - a & x.senderPk; + // a & x.senderPk; // not serialized, as we do not use it in monero currently } inline void serializeOutPk(boost::archive::binary_iarchive &a, rct::ctkeyV &outPk_, const boost::serialization::version_type ver) -- cgit v1.2.3 From 0263dd2d23679cf5564d34011de02d5fd5f96650 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 31 Jul 2016 20:09:44 +0100 Subject: core: add some locking around pool use --- src/cryptonote_core/blockchain.cpp | 4 +++- src/cryptonote_core/tx_pool.cpp | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'src/cryptonote_core') diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 547f2d4c2..f6f56b086 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2869,6 +2869,8 @@ void Blockchain::return_tx_to_pool(const std::vector &txs) //------------------------------------------------------------------ bool Blockchain::flush_txes_from_pool(const std::list &txids) { + CRITICAL_REGION_LOCAL(m_tx_pool); + bool res = true; for (const auto &txid: txids) { @@ -2877,7 +2879,7 @@ bool Blockchain::flush_txes_from_pool(const std::list &txids) uint64_t fee; bool relayed; LOG_PRINT_L1("Removing txid " << txid << " from the pool"); - if(!m_tx_pool.take_tx(txid, tx, blob_size, fee, relayed)) + if(m_tx_pool.have_tx(txid) && !m_tx_pool.take_tx(txid, tx, blob_size, fee, relayed)) { LOG_PRINT_L0("Failed to remove txid " << txid << " from the pool"); res = false; diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index e7d3908e5..dcbb7cc04 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -662,6 +662,7 @@ namespace cryptonote //--------------------------------------------------------------------------------- size_t tx_memory_pool::validate(uint8_t version) { + CRITICAL_REGION_LOCAL(m_transactions_lock); size_t n_removed = 0; size_t tx_size_limit = (version < 2 ? TRANSACTION_SIZE_LIMIT_V1 : TRANSACTION_SIZE_LIMIT_V2); for (auto it = m_transactions.begin(); it != m_transactions.end(); ) { -- cgit v1.2.3 From a47ceee83b0e04a3c440d9ca5a75cf0ac1d9256e Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 6 Aug 2016 19:19:25 +0100 Subject: wallet: do not store signatures in the wallet cache Saves some substantial space. Also avoid calculating tx hashes we don't need. --- src/cryptonote_core/cryptonote_basic.h | 2 +- src/cryptonote_core/cryptonote_boost_serialization.h | 10 ++++++++++ src/cryptonote_core/cryptonote_format_utils.cpp | 5 +++++ src/cryptonote_core/cryptonote_format_utils.h | 1 + 4 files changed, 17 insertions(+), 1 deletion(-) (limited to 'src/cryptonote_core') diff --git a/src/cryptonote_core/cryptonote_basic.h b/src/cryptonote_core/cryptonote_basic.h index afe785eb9..e5a5cc6f5 100644 --- a/src/cryptonote_core/cryptonote_basic.h +++ b/src/cryptonote_core/cryptonote_basic.h @@ -180,7 +180,7 @@ namespace cryptonote FIELD(extra) END_SERIALIZE() - protected: + public: transaction_prefix(){} }; diff --git a/src/cryptonote_core/cryptonote_boost_serialization.h b/src/cryptonote_core/cryptonote_boost_serialization.h index f222db94e..41f864803 100644 --- a/src/cryptonote_core/cryptonote_boost_serialization.h +++ b/src/cryptonote_core/cryptonote_boost_serialization.h @@ -143,6 +143,16 @@ namespace boost } + template + inline void serialize(Archive &a, cryptonote::transaction_prefix &x, const boost::serialization::version_type ver) + { + a & x.version; + a & x.unlock_time; + a & x.vin; + a & x.vout; + a & x.extra; + } + template inline void serialize(Archive &a, cryptonote::transaction &x, const boost::serialization::version_type ver) { diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index ee9a00803..ddcab4f05 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -321,6 +321,11 @@ namespace cryptonote return pub_key_field.pub_key; } //--------------------------------------------------------------- + crypto::public_key get_tx_pub_key_from_extra(const transaction_prefix& tx_prefix) + { + return get_tx_pub_key_from_extra(tx_prefix.extra); + } + //--------------------------------------------------------------- crypto::public_key get_tx_pub_key_from_extra(const transaction& tx) { return get_tx_pub_key_from_extra(tx.extra); diff --git a/src/cryptonote_core/cryptonote_format_utils.h b/src/cryptonote_core/cryptonote_format_utils.h index 6dac8e888..e0ffbed67 100644 --- a/src/cryptonote_core/cryptonote_format_utils.h +++ b/src/cryptonote_core/cryptonote_format_utils.h @@ -89,6 +89,7 @@ namespace cryptonote bool parse_tx_extra(const std::vector& tx_extra, std::vector& tx_extra_fields); crypto::public_key get_tx_pub_key_from_extra(const std::vector& tx_extra); + crypto::public_key get_tx_pub_key_from_extra(const transaction_prefix& tx); crypto::public_key get_tx_pub_key_from_extra(const transaction& tx); bool add_tx_pub_key_to_extra(transaction& tx, const crypto::public_key& tx_pub_key); bool add_extra_nonce_to_tx_extra(std::vector& tx_extra, const blobdata& extra_nonce); -- cgit v1.2.3 From c5be4b0beaaa7a703d4e2b84aa9f3c727bf992df Mon Sep 17 00:00:00 2001 From: Shen Noether Date: Mon, 8 Aug 2016 12:54:00 +0100 Subject: rct: avoid the need for the last II element This element is used in the generation of the MLSAG, but isn't needed in verification. Also misc changes in the cryptonote code to match, by mooo. --- src/cryptonote_core/blockchain.cpp | 14 ++++++-------- src/cryptonote_core/cryptonote_boost_serialization.h | 13 +++---------- 2 files changed, 9 insertions(+), 18 deletions(-) (limited to 'src/cryptonote_core') diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index f6f56b086..91a9d5d6f 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2475,12 +2475,12 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context const rct::ctkeyM &mixRing = tx.rct_signatures.mixRing.empty() ? reconstructed_mixRing : tx.rct_signatures.mixRing; // always do II, because it's split in the simple version, and always do outPk - // all MGs should have the same II size (1) + // all MGs should have empty II for (size_t n = 0; n < tx.rct_signatures.MGs.size(); ++n) { - if (tx.rct_signatures.MGs[n].II.size() != 1) + if (tx.rct_signatures.MGs[n].II.size() != 0) { - LOG_PRINT_L1("Failed to check ringct signatures: mismatched MGs II sizes"); + LOG_PRINT_L1("Failed to check ringct signatures: non empty MGs II"); return false; } } @@ -2489,7 +2489,6 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context for (size_t n = 0; n < tx.vin.size(); ++n) { reconstructed_II[n].push_back(rct::ki2rct(boost::get(tx.vin[n]).k_image)); - reconstructed_II[n].push_back(tx.rct_signatures.MGs[n].II[0]); } if (tx.rct_signatures.outPk.size() != tx.vout.size()) @@ -2579,7 +2578,7 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context // if the tx already has a non empty mixRing and/or II, use them, // else reconstruct them. Always do outPk. const rct::ctkeyM &mixRing = tx.rct_signatures.mixRing.empty() ? reconstructed_mixRing : tx.rct_signatures.mixRing; - const rct::keyV &II = tx.rct_signatures.MG.II.size() == 1 ? reconstructed_II : tx.rct_signatures.MG.II; + const rct::keyV &II = tx.rct_signatures.MG.II.empty() ? reconstructed_II : tx.rct_signatures.MG.II; const rct::ctkeyV outPk = reconstructed_outPk; // RCT needs the same mixin for all inputs @@ -2604,14 +2603,13 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context } } - if (tx.rct_signatures.MG.II.size() == 1) + if (tx.rct_signatures.MG.II.empty()) { reconstructed_II.resize(tx.vin.size()); for (size_t n = 0; n < tx.vin.size(); ++n) { reconstructed_II[n] = rct::ki2rct(boost::get(tx.vin[n]).k_image); } - reconstructed_II.push_back(tx.rct_signatures.MG.II.back()); } if (tx.rct_signatures.outPk.size() != tx.vout.size()) @@ -2657,7 +2655,7 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context } } - if (II.size() != 1 + tx.vin.size()) + if (II.size() != tx.vin.size()) { LOG_PRINT_L1("Failed to check ringct signatures: mismatched II/vin sizes"); return false; diff --git a/src/cryptonote_core/cryptonote_boost_serialization.h b/src/cryptonote_core/cryptonote_boost_serialization.h index 41f864803..8a82aa7a7 100644 --- a/src/cryptonote_core/cryptonote_boost_serialization.h +++ b/src/cryptonote_core/cryptonote_boost_serialization.h @@ -209,19 +209,12 @@ namespace boost a & x.s; } - inline void serialize(boost::archive::binary_iarchive &a, rct::mgSig &x, const boost::serialization::version_type ver) - { - a & x.ss; - a & x.cc; - x.II.resize(1); - a & x.II[0]; - } - - inline void serialize(boost::archive::binary_oarchive &a, rct::mgSig &x, const boost::serialization::version_type ver) + template + inline void serialize(Archive &a, rct::mgSig &x, const boost::serialization::version_type ver) { a & x.ss; a & x.cc; - a & x.II.back(); + // a & x.II; // not serialized, we can recover it from the tx vin } template -- cgit v1.2.3 From 3ab2ab3e7691dadf91ef39ed477e12f0144b8278 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 8 Aug 2016 13:49:42 +0100 Subject: rct: change the simple flag to a type for future expansion --- src/cryptonote_core/blockchain.cpp | 12 +++++++++--- src/cryptonote_core/cryptonote_basic.h | 11 +++++++---- src/cryptonote_core/cryptonote_boost_serialization.h | 10 ++++++---- 3 files changed, 22 insertions(+), 11 deletions(-) (limited to 'src/cryptonote_core') diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 91a9d5d6f..c81445f1d 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2464,8 +2464,9 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context // from version 2, check ringct signatures // obviously, the original and simple rct APIs use a mixRing that's indexes // in opposite orders, because it'd be too simple otherwise... - if (tx.rct_signatures.simple) + switch (tx.rct_signatures.type) { + case rct::RCTTypeSimple: { rct::ctkeyM reconstructed_mixRing; std::vector reconstructed_II; rct::ctkeyV reconstructed_outPk; @@ -2568,9 +2569,9 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context LOG_PRINT_L1("Failed to check ringct signatures!"); return false; } + break; } - else - { + case rct::RCTTypeFull: { rct::ctkeyM reconstructed_mixRing; rct::keyV reconstructed_II; rct::ctkeyV reconstructed_outPk; @@ -2674,6 +2675,11 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context LOG_PRINT_L1("Failed to check ringct signatures!"); return false; } + break; + } + default: + LOG_PRINT_L1("Unsupported rct type: " << tx.rct_signatures.type); + return false; } } return true; diff --git a/src/cryptonote_core/cryptonote_basic.h b/src/cryptonote_core/cryptonote_basic.h index e5a5cc6f5..b68040597 100644 --- a/src/cryptonote_core/cryptonote_basic.h +++ b/src/cryptonote_core/cryptonote_basic.h @@ -231,18 +231,21 @@ namespace cryptonote else { FIELD(rct_signatures) - if (rct_signatures.simple) + switch (rct_signatures.type) { + case rct::RCTTypeSimple: if (rct_signatures.mixRing.size() && rct_signatures.mixRing.size() != vin.size()) return false; - } - else - { + break; + case rct::RCTTypeFull: for (size_t i = 0; i < rct_signatures.mixRing.size(); ++i) { if (rct_signatures.mixRing[i].size() != vin.size()) return false; } + break; + default: + return false; } } END_SERIALIZE() diff --git a/src/cryptonote_core/cryptonote_boost_serialization.h b/src/cryptonote_core/cryptonote_boost_serialization.h index 8a82aa7a7..7a7cf8588 100644 --- a/src/cryptonote_core/cryptonote_boost_serialization.h +++ b/src/cryptonote_core/cryptonote_boost_serialization.h @@ -248,15 +248,17 @@ namespace boost template inline void serialize(Archive &a, rct::rctSig &x, const boost::serialization::version_type ver) { - a & x.simple; + a & x.type; + if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple) + throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type"); // a & x.message; message is not serialized, as it can be reconstructed from the tx data a & x.rangeSigs; - if (x.simple) + if (x.type == rct::RCTTypeSimple) a & x.MGs; - else + if (x.type == rct::RCTTypeFull) a & x.MG; // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets - if (x.simple) + if (x.type == rct::RCTTypeSimple) a & x.pseudoOuts; a & x.ecdhInfo; serializeOutPk(a, x.outPk, ver); -- cgit v1.2.3 From d93746b6d37dd5b99d16331ec8e24e8a1f7e4652 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 9 Aug 2016 11:38:54 +0100 Subject: rct: rework the verification preparation process The whole rct data apart from the MLSAGs is now included in the signed message, to avoid malleability issues. Instead of passing the data that's not serialized as extra parameters to the verification API, the transaction is modified to fill all that information. This means the transaction can not be const anymore, but it cleaner in other ways. --- src/cryptonote_core/blockchain.cpp | 222 ++++++++++++++++--------------------- src/cryptonote_core/blockchain.h | 15 ++- src/cryptonote_core/tx_pool.cpp | 10 +- 3 files changed, 114 insertions(+), 133 deletions(-) (limited to 'src/cryptonote_core') diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index c81445f1d..08cb85a55 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2156,7 +2156,7 @@ bool Blockchain::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector> &pubkeys) +{ + CHECK_AND_ASSERT_MES(tx.version == 2, false, "Transaction version is not 2"); + + rct::rctSig &rv = tx.rct_signatures; + + // message - hash of the transaction prefix + rv.message = rct::hash2rct(tx_prefix_hash); + + // mixRing - full and simple store it in opposite ways + if (rv.type == rct::RCTTypeFull) + { + rv.mixRing.resize(pubkeys[0].size()); + for (size_t m = 0; m < pubkeys[0].size(); ++m) + rv.mixRing[m].clear(); + for (size_t n = 0; n < pubkeys.size(); ++n) + { + CHECK_AND_ASSERT_MES(pubkeys[n].size() <= pubkeys[0].size(), false, "More inputs that first ring"); + for (size_t m = 0; m < pubkeys[n].size(); ++m) + { + rv.mixRing[m].push_back(pubkeys[n][m]); + } + } + } + else if (rv.type == rct::RCTTypeSimple) + { + rv.mixRing.resize(pubkeys.size()); + for (size_t n = 0; n < pubkeys.size(); ++n) + { + rv.mixRing[n].clear(); + for (size_t m = 0; m < pubkeys[n].size(); ++m) + { + rv.mixRing[n].push_back(pubkeys[n][m]); + } + } + } + else + { + CHECK_AND_ASSERT_MES(false, false, "Unsupported rct tx type: " + boost::lexical_cast(rv.type)); + } + + // II + if (rv.type == rct::RCTTypeFull) + { + rv.MG.II.resize(tx.vin.size()); + for (size_t n = 0; n < tx.vin.size(); ++n) + rv.MG.II[n] = rct::ki2rct(boost::get(tx.vin[n]).k_image); + } + else if (rv.type == rct::RCTTypeSimple) + { + CHECK_AND_ASSERT_MES(rv.MGs.size() == tx.vin.size(), false, "Bad MGs size"); + for (size_t n = 0; n < tx.vin.size(); ++n) + { + rv.MGs[n].II.resize(1); + rv.MGs[n].II[0] = rct::ki2rct(boost::get(tx.vin[n]).k_image); + } + } + else + { + CHECK_AND_ASSERT_MES(false, false, "Unsupported rct tx type: " + boost::lexical_cast(rv.type)); + } + + // outPk + CHECK_AND_ASSERT_MES(rv.outPk.size() == tx.vout.size(), false, "Bad outPk size"); + for (size_t n = 0; n < tx.rct_signatures.outPk.size(); ++n) + rv.outPk[n].dest = rct::pk2rct(boost::get(tx.vout[n].target).key); + + return true; +} //------------------------------------------------------------------ // This function validates transaction inputs and their keys. // FIXME: consider moving functionality specific to one input into // check_tx_input() rather than here, and use this function simply // to iterate the inputs as necessary (splitting the task // using threads, etc.) -bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context &tvc, uint64_t* pmax_used_block_height) +bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, uint64_t* pmax_used_block_height) { LOG_PRINT_L3("Blockchain::" << __func__); size_t sig_index = 0; @@ -2461,71 +2530,29 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context } else { + if (!expand_transaction_2(tx, tx_prefix_hash, pubkeys)) + { + LOG_PRINT_L1("Failed to expand rct signatures!"); + return false; + } + // from version 2, check ringct signatures // obviously, the original and simple rct APIs use a mixRing that's indexes // in opposite orders, because it'd be too simple otherwise... - switch (tx.rct_signatures.type) + const rct::rctSig &rv = tx.rct_signatures; + switch (rv.type) { case rct::RCTTypeSimple: { - rct::ctkeyM reconstructed_mixRing; - std::vector reconstructed_II; - rct::ctkeyV reconstructed_outPk; - - // if the tx already has a non empty mixRing, use them, - // else reconstruct them - const rct::ctkeyM &mixRing = tx.rct_signatures.mixRing.empty() ? reconstructed_mixRing : tx.rct_signatures.mixRing; - // always do II, because it's split in the simple version, and always do outPk - - // all MGs should have empty II - for (size_t n = 0; n < tx.rct_signatures.MGs.size(); ++n) - { - if (tx.rct_signatures.MGs[n].II.size() != 0) - { - LOG_PRINT_L1("Failed to check ringct signatures: non empty MGs II"); - return false; - } - } - - reconstructed_II.resize(tx.vin.size()); - for (size_t n = 0; n < tx.vin.size(); ++n) - { - reconstructed_II[n].push_back(rct::ki2rct(boost::get(tx.vin[n]).k_image)); - } - - if (tx.rct_signatures.outPk.size() != tx.vout.size()) - { - LOG_PRINT_L1("Failed to check ringct signatures: outPk and vout have different sizes"); - return false; - } - reconstructed_outPk.resize(tx.vout.size()); - for (size_t n = 0; n < tx.vout.size(); ++n) - { - reconstructed_outPk[n].dest = rct::pk2rct(boost::get(tx.vout[n].target).key); - reconstructed_outPk[n].mask = tx.rct_signatures.outPk[n].mask; - } - - if (tx.rct_signatures.mixRing.empty()) - { - reconstructed_mixRing.resize(pubkeys.size()); - for (size_t n = 0; n < pubkeys.size(); ++n) - { - for (size_t m = 0; m < pubkeys[n].size(); ++m) - { - reconstructed_mixRing[n].push_back(pubkeys[n][m]); - } - } - } - // check all this, either recontructed (so should really pass), or not { - if (pubkeys.size() != mixRing.size()) + if (pubkeys.size() != rv.mixRing.size()) { LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkeys/mixRing size"); return false; } for (size_t i = 0; i < pubkeys.size(); ++i) { - if (pubkeys[i].size() != mixRing[i].size()) + if (pubkeys[i].size() != rv.mixRing[i].size()) { LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkeys/mixRing size"); return false; @@ -2536,12 +2563,12 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context { for (size_t m = 0; m < pubkeys[n].size(); ++m) { - if (pubkeys[n][m].dest != rct::rct2pk(mixRing[n][m].dest)) + if (pubkeys[n][m].dest != rct::rct2pk(rv.mixRing[n][m].dest)) { LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkey at vin " << n << ", index " << m); return false; } - if (pubkeys[n][m].mask != rct::rct2pk(mixRing[n][m].mask)) + if (pubkeys[n][m].mask != rct::rct2pk(rv.mixRing[n][m].mask)) { LOG_PRINT_L1("Failed to check ringct signatures: mismatched commitment at vin " << n << ", index " << m); return false; @@ -2550,21 +2577,21 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context } } - if (tx.rct_signatures.MGs.size() != tx.vin.size()) + if (rv.MGs.size() != tx.vin.size()) { LOG_PRINT_L1("Failed to check ringct signatures: mismatched MGs/vin sizes"); return false; } for (size_t n = 0; n < tx.vin.size(); ++n) { - if (memcmp(&boost::get(tx.vin[n]).k_image, &reconstructed_II[n][0], 32)) + if (memcmp(&boost::get(tx.vin[n]).k_image, &rv.MGs[n].II[0], 32)) { LOG_PRINT_L1("Failed to check ringct signatures: mismatched key image"); return false; } } - if (!rct::verRctSimple(tx.rct_signatures, mixRing, &reconstructed_II, reconstructed_outPk, rct::hash2rct(tx_prefix_hash))) + if (!rct::verRctSimple(rv)) { LOG_PRINT_L1("Failed to check ringct signatures!"); return false; @@ -2572,66 +2599,13 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context break; } case rct::RCTTypeFull: { - rct::ctkeyM reconstructed_mixRing; - rct::keyV reconstructed_II; - rct::ctkeyV reconstructed_outPk; - - // if the tx already has a non empty mixRing and/or II, use them, - // else reconstruct them. Always do outPk. - const rct::ctkeyM &mixRing = tx.rct_signatures.mixRing.empty() ? reconstructed_mixRing : tx.rct_signatures.mixRing; - const rct::keyV &II = tx.rct_signatures.MG.II.empty() ? reconstructed_II : tx.rct_signatures.MG.II; - const rct::ctkeyV outPk = reconstructed_outPk; - - // RCT needs the same mixin for all inputs - for (size_t n = 1; n < pubkeys.size(); ++n) - { - if (pubkeys[n].size() != pubkeys[0].size()) - { - LOG_PRINT_L1("Failed to check ringct signatures: mismatched ring sizes"); - return false; - } - } - - if (tx.rct_signatures.mixRing.empty()) - { - reconstructed_mixRing.resize(pubkeys[0].size()); - for (size_t n = 0; n < pubkeys.size(); ++n) - { - for (size_t m = 0; m < pubkeys[n].size(); ++m) - { - reconstructed_mixRing[m].push_back(pubkeys[n][m]); - } - } - } - - if (tx.rct_signatures.MG.II.empty()) - { - reconstructed_II.resize(tx.vin.size()); - for (size_t n = 0; n < tx.vin.size(); ++n) - { - reconstructed_II[n] = rct::ki2rct(boost::get(tx.vin[n]).k_image); - } - } - - if (tx.rct_signatures.outPk.size() != tx.vout.size()) - { - LOG_PRINT_L1("Failed to check ringct signatures: outPk and vout have different sizes"); - return false; - } - reconstructed_outPk.resize(tx.vout.size()); - for (size_t n = 0; n < tx.vout.size(); ++n) - { - reconstructed_outPk[n].dest = rct::pk2rct(boost::get(tx.vout[n].target).key); - reconstructed_outPk[n].mask = tx.rct_signatures.outPk[n].mask; - } - // check all this, either recontructed (so should really pass), or not { bool size_matches = true; for (size_t i = 0; i < pubkeys.size(); ++i) - size_matches &= pubkeys[i].size() == mixRing.size(); - for (size_t i = 0; i < tx.rct_signatures.mixRing.size(); ++i) - size_matches &= pubkeys.size() == mixRing[i].size(); + size_matches &= pubkeys[i].size() == rv.mixRing.size(); + for (size_t i = 0; i < rv.mixRing.size(); ++i) + size_matches &= pubkeys.size() == rv.mixRing[i].size(); if (!size_matches) { LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkeys/mixRing size"); @@ -2642,12 +2616,12 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context { for (size_t m = 0; m < pubkeys[n].size(); ++m) { - if (pubkeys[n][m].dest != rct::rct2pk(mixRing[m][n].dest)) + if (pubkeys[n][m].dest != rct::rct2pk(rv.mixRing[m][n].dest)) { LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkey at vin " << n << ", index " << m); return false; } - if (pubkeys[n][m].mask != rct::rct2pk(mixRing[m][n].mask)) + if (pubkeys[n][m].mask != rct::rct2pk(rv.mixRing[m][n].mask)) { LOG_PRINT_L1("Failed to check ringct signatures: mismatched commitment at vin " << n << ", index " << m); return false; @@ -2656,21 +2630,21 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context } } - if (II.size() != tx.vin.size()) + if (rv.MG.II.size() != tx.vin.size()) { LOG_PRINT_L1("Failed to check ringct signatures: mismatched II/vin sizes"); return false; } for (size_t n = 0; n < tx.vin.size(); ++n) { - if (memcmp(&boost::get(tx.vin[n]).k_image, &II[n], 32)) + if (memcmp(&boost::get(tx.vin[n]).k_image, &rv.MG.II[n], 32)) { LOG_PRINT_L1("Failed to check ringct signatures: mismatched II/vin sizes"); return false; } } - if (!rct::verRct(tx.rct_signatures, mixRing, II, outPk, rct::hash2rct(tx_prefix_hash))) + if (!rct::verRct(rv)) { LOG_PRINT_L1("Failed to check ringct signatures!"); return false; @@ -2678,7 +2652,7 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context break; } default: - LOG_PRINT_L1("Unsupported rct type: " << tx.rct_signatures.type); + LOG_PRINT_L1("Unsupported rct type: " << rv.type); return false; } } @@ -2787,11 +2761,7 @@ bool Blockchain::check_tx_input(size_t tx_version, const txin_to_key& txin, cons if (tx_version == 1) { CHECK_AND_ASSERT_MES(sig.size() == output_keys.size(), false, "internal error: tx signatures count=" << sig.size() << " mismatch with outputs keys count for inputs=" << output_keys.size()); } - else - { - // rct signatures may be empty (and will be reconstructed later in the caller if so) - CHECK_AND_ASSERT_MES(rct_signatures.mixRing.empty() || rct_signatures.mixRing.size() == output_keys.size(), false, "internal error: tx rct signatures count=" << sig.size() << " mismatch with outputs keys count for inputs=" << output_keys.size()); - } + // rct_signatures will be expanded after this return true; } //------------------------------------------------------------------ diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index b22074c57..94701608e 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -500,6 +500,7 @@ namespace cryptonote * validates a transaction's inputs as correctly used and not previously * spent. also returns the hash and height of the most recent block * which contains an output that was used as an input to the transaction. + * The transaction's rct signatures, if any, are expanded. * * @param tx the transaction to validate * @param pmax_used_block_height return-by-reference block height of most recent input @@ -509,7 +510,7 @@ namespace cryptonote * * @return false if any input is invalid, otherwise true */ - bool check_tx_inputs(const transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id, tx_verification_context &tvc, bool kept_by_block = false); + bool check_tx_inputs(transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id, tx_verification_context &tvc, bool kept_by_block = false); /** * @brief check that a transaction's outputs conform to current standards @@ -919,6 +920,7 @@ namespace cryptonote * This function validates transaction inputs and their keys. Previously * it also performed double spend checking, but that has been moved to its * own function. + * The transaction's rct signatures, if any, are expanded. * * If pmax_related_block_height is not NULL, its value is set to the height * of the most recent block which contains an output used in any input set @@ -932,7 +934,7 @@ namespace cryptonote * * @return false if any validation step fails, otherwise true */ - bool check_tx_inputs(const transaction& tx, tx_verification_context &tvc, uint64_t* pmax_used_block_height = NULL); + bool check_tx_inputs(transaction& tx, tx_verification_context &tvc, uint64_t* pmax_used_block_height = NULL); /** * @brief performs a blockchain reorganization according to the longest chain rule @@ -1211,5 +1213,14 @@ namespace cryptonote * a useful state. */ void load_compiled_in_block_hashes(); + + /** + * @brief expands v2 transaction data from blockchain + * + * RingCT transactions do not transmit some of their data if it + * can be reconstituted by the receiver. This function expands + * that implicit data. + */ + bool expand_transaction_2(transaction &tx, const crypto::hash &tx_prefix_hash, const std::vector> &pubkeys); }; } // namespace cryptonote diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index dcbb7cc04..2ed12eb5c 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -190,7 +190,9 @@ namespace cryptonote crypto::hash max_used_block_id = null_hash; uint64_t max_used_block_height = 0; - bool ch_inp_res = m_blockchain.check_tx_inputs(tx, max_used_block_height, max_used_block_id, tvc, kept_by_block); + tx_details txd; + txd.tx = tx; + bool ch_inp_res = m_blockchain.check_tx_inputs(txd.tx, max_used_block_height, max_used_block_id, tvc, kept_by_block); CRITICAL_REGION_LOCAL(m_transactions_lock); if(!ch_inp_res) { @@ -198,10 +200,9 @@ namespace cryptonote // may become valid again, so ignore the failed inputs check. if(kept_by_block) { - auto txd_p = m_transactions.insert(transactions_container::value_type(id, tx_details())); + auto txd_p = m_transactions.insert(transactions_container::value_type(id, txd)); CHECK_AND_ASSERT_MES(txd_p.second, false, "transaction already exists at inserting in memory pool"); txd_p.first->second.blob_size = blob_size; - txd_p.first->second.tx = tx; txd_p.first->second.fee = fee; txd_p.first->second.max_used_block_id = null_hash; txd_p.first->second.max_used_block_height = 0; @@ -220,10 +221,9 @@ namespace cryptonote }else { //update transactions container - auto txd_p = m_transactions.insert(transactions_container::value_type(id, tx_details())); + auto txd_p = m_transactions.insert(transactions_container::value_type(id, txd)); CHECK_AND_ASSERT_MES(txd_p.second, false, "intrnal error: transaction already exists at inserting in memorypool"); txd_p.first->second.blob_size = blob_size; - txd_p.first->second.tx = tx; txd_p.first->second.kept_by_block = kept_by_block; txd_p.first->second.fee = fee; txd_p.first->second.max_used_block_id = max_used_block_id; -- cgit v1.2.3 From 93f5c625f058ee1f81c02c8bb03744b28bbde90a Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 9 Aug 2016 21:34:09 +0100 Subject: rct: rework v2 txes into prunable and non prunable data Nothing is pruned, but this allows easier changes later. --- src/cryptonote_core/blockchain.cpp | 24 ++++++++----- .../cryptonote_boost_serialization.h | 23 +++++++++--- src/cryptonote_core/cryptonote_format_utils.cpp | 41 ++++++++++++++++++---- src/cryptonote_core/cryptonote_format_utils.h | 1 + 4 files changed, 69 insertions(+), 20 deletions(-) (limited to 'src/cryptonote_core') diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 08cb85a55..44dde7759 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2279,17 +2279,18 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr // II if (rv.type == rct::RCTTypeFull) { - rv.MG.II.resize(tx.vin.size()); + rv.p.MGs.resize(1); + rv.p.MGs[0].II.resize(tx.vin.size()); for (size_t n = 0; n < tx.vin.size(); ++n) - rv.MG.II[n] = rct::ki2rct(boost::get(tx.vin[n]).k_image); + rv.p.MGs[0].II[n] = rct::ki2rct(boost::get(tx.vin[n]).k_image); } else if (rv.type == rct::RCTTypeSimple) { - CHECK_AND_ASSERT_MES(rv.MGs.size() == tx.vin.size(), false, "Bad MGs size"); + CHECK_AND_ASSERT_MES(rv.p.MGs.size() == tx.vin.size(), false, "Bad MGs size"); for (size_t n = 0; n < tx.vin.size(); ++n) { - rv.MGs[n].II.resize(1); - rv.MGs[n].II[0] = rct::ki2rct(boost::get(tx.vin[n]).k_image); + rv.p.MGs[n].II.resize(1); + rv.p.MGs[n].II[0] = rct::ki2rct(boost::get(tx.vin[n]).k_image); } } else @@ -2577,14 +2578,14 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, } } - if (rv.MGs.size() != tx.vin.size()) + if (rv.p.MGs.size() != tx.vin.size()) { LOG_PRINT_L1("Failed to check ringct signatures: mismatched MGs/vin sizes"); return false; } for (size_t n = 0; n < tx.vin.size(); ++n) { - if (memcmp(&boost::get(tx.vin[n]).k_image, &rv.MGs[n].II[0], 32)) + if (memcmp(&boost::get(tx.vin[n]).k_image, &rv.p.MGs[n].II[0], 32)) { LOG_PRINT_L1("Failed to check ringct signatures: mismatched key image"); return false; @@ -2630,14 +2631,19 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, } } - if (rv.MG.II.size() != tx.vin.size()) + if (rv.p.MGs.size() != 1) + { + LOG_PRINT_L1("Failed to check ringct signatures: Bad MGs size"); + return false; + } + if (rv.p.MGs[0].II.size() != tx.vin.size()) { LOG_PRINT_L1("Failed to check ringct signatures: mismatched II/vin sizes"); return false; } for (size_t n = 0; n < tx.vin.size(); ++n) { - if (memcmp(&boost::get(tx.vin[n]).k_image, &rv.MG.II[n], 32)) + if (memcmp(&boost::get(tx.vin[n]).k_image, &rv.p.MGs[0].II[n], 32)) { LOG_PRINT_L1("Failed to check ringct signatures: mismatched II/vin sizes"); return false; diff --git a/src/cryptonote_core/cryptonote_boost_serialization.h b/src/cryptonote_core/cryptonote_boost_serialization.h index 7a7cf8588..09e9a7fa7 100644 --- a/src/cryptonote_core/cryptonote_boost_serialization.h +++ b/src/cryptonote_core/cryptonote_boost_serialization.h @@ -246,23 +246,36 @@ namespace boost } template - inline void serialize(Archive &a, rct::rctSig &x, const boost::serialization::version_type ver) + inline void serialize(Archive &a, rct::rctSigBase &x, const boost::serialization::version_type ver) { a & x.type; if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple) throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type"); // a & x.message; message is not serialized, as it can be reconstructed from the tx data - a & x.rangeSigs; + // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets if (x.type == rct::RCTTypeSimple) - a & x.MGs; - if (x.type == rct::RCTTypeFull) - a & x.MG; + a & x.pseudoOuts; + a & x.ecdhInfo; + serializeOutPk(a, x.outPk, ver); + a & x.txnFee; + } + + template + inline void serialize(Archive &a, rct::rctSig &x, const boost::serialization::version_type ver) + { + a & x.type; + if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple) + throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type"); + // a & x.message; message is not serialized, as it can be reconstructed from the tx data // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets if (x.type == rct::RCTTypeSimple) a & x.pseudoOuts; a & x.ecdhInfo; serializeOutPk(a, x.outPk, ver); a & x.txnFee; + //-------------- + a & x.p.rangeSigs; + a & x.p.MGs; } } } diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index ddcab4f05..8f4020829 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -101,7 +101,7 @@ namespace cryptonote CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob"); //TODO: validate tx - crypto::cn_fast_hash(tx_blob.data(), tx_blob.size(), tx_hash); + get_transaction_hash(tx, tx_hash); get_transaction_prefix_hash(tx, tx_prefix_hash); return true; } @@ -905,20 +905,49 @@ namespace cryptonote crypto::hash get_transaction_hash(const transaction& t) { crypto::hash h = null_hash; - size_t blob_size = 0; - get_object_hash(t, h, blob_size); + get_transaction_hash(t, h, NULL); return h; } //--------------------------------------------------------------- bool get_transaction_hash(const transaction& t, crypto::hash& res) { - size_t blob_size = 0; - return get_object_hash(t, res, blob_size); + return get_transaction_hash(t, res, NULL); + } + //--------------------------------------------------------------- + bool get_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]); + + // base rct data + get_blob_hash(t_serializable_object_to_blob((const rct::rctSigBase&)t.rct_signatures), hashes[1]); + + // prunable rct data + get_blob_hash(t_serializable_object_to_blob(t.rct_signatures.p), 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) { - return get_object_hash(t, res, blob_size); + return get_transaction_hash(t, res, &blob_size); } //--------------------------------------------------------------- blobdata get_block_hashing_blob(const block& b) diff --git a/src/cryptonote_core/cryptonote_format_utils.h b/src/cryptonote_core/cryptonote_format_utils.h index e0ffbed67..f70b22573 100644 --- a/src/cryptonote_core/cryptonote_format_utils.h +++ b/src/cryptonote_core/cryptonote_format_utils.h @@ -111,6 +111,7 @@ namespace cryptonote crypto::hash get_transaction_hash(const transaction& t); bool get_transaction_hash(const transaction& t, crypto::hash& res); bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t& blob_size); + bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t* blob_size); blobdata get_block_hashing_blob(const block& b); bool get_block_hash(const block& b, crypto::hash& res); crypto::hash get_block_hash(const block& b); -- cgit v1.2.3 From d4b62a1e295a7fb19de6081733b1d8e0610cbf08 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 10 Aug 2016 12:48:20 +0100 Subject: rct amount key modified as per luigi1111's recommendations This allows the key to be not the same for two outputs sent to the same address (eg, if you pay yourself, and also get change back). Also remove the key amounts lists and return parameters since we don't actually generate random ones, so we don't need to save them as we can recalculate them when needed if we have the correct keys. --- src/cryptonote_core/cryptonote_format_utils.cpp | 13 +++++++++---- src/cryptonote_core/cryptonote_format_utils.h | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) (limited to 'src/cryptonote_core') diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index 8f4020829..6a3172d4e 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -458,8 +458,9 @@ namespace cryptonote return encrypt_payment_id(payment_id, public_key, secret_key); } //--------------------------------------------------------------- - bool construct_tx_and_get_tx_keys(const account_keys& sender_account_keys, const std::vector& sources, const std::vector& destinations, std::vector extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector &amount_keys, bool rct) + bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector& sources, const std::vector& destinations, std::vector extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct) { + std::vector amount_keys; tx.vin.clear(); tx.vout.clear(); tx.signatures.clear(); @@ -577,6 +578,12 @@ namespace cryptonote bool r = crypto::generate_key_derivation(dst_entr.addr.m_view_public_key, txkey.sec, derivation); CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << dst_entr.addr.m_view_public_key << ", " << txkey.sec << ")"); + if (tx.version > 1) + { + crypto::secret_key scalar1; + crypto::derivation_to_scalar(derivation, output_index, scalar1); + amount_keys.push_back(scalar1); + } r = crypto::derive_public_key(derivation, output_index, dst_entr.addr.m_spend_public_key, out_eph_public_key); CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to derive_public_key(" << derivation << ", " << output_index << ", "<< dst_entr.addr.m_spend_public_key << ")"); @@ -686,7 +693,6 @@ namespace cryptonote destinations.push_back(rct::pk2rct(boost::get(tx.vout[i].target).key)); outamounts.push_back(tx.vout[i].amount); amount_out += tx.vout[i].amount; - amount_keys.push_back(rct::rct2sk(rct::hash_to_scalar(rct::scalarmultKey(rct::pk2rct(shuffled_dsts[i].addr.m_view_public_key), rct::sk2rct(txkey.sec))))); } if (use_simple_rct) @@ -745,8 +751,7 @@ namespace cryptonote bool construct_tx(const account_keys& sender_account_keys, const std::vector& sources, const std::vector& destinations, std::vector extra, transaction& tx, uint64_t unlock_time) { crypto::secret_key tx_key; - std::vector amount_keys; - return construct_tx_and_get_tx_keys(sender_account_keys, sources, destinations, extra, tx, unlock_time, tx_key, amount_keys); + return construct_tx_and_get_tx_key(sender_account_keys, sources, destinations, extra, tx, unlock_time, tx_key); } //--------------------------------------------------------------- bool get_inputs_money_amount(const transaction& tx, uint64_t& money) diff --git a/src/cryptonote_core/cryptonote_format_utils.h b/src/cryptonote_core/cryptonote_format_utils.h index f70b22573..5da256921 100644 --- a/src/cryptonote_core/cryptonote_format_utils.h +++ b/src/cryptonote_core/cryptonote_format_utils.h @@ -74,7 +74,7 @@ namespace cryptonote //--------------------------------------------------------------- bool construct_tx(const account_keys& sender_account_keys, const std::vector& sources, const std::vector& destinations, std::vector extra, transaction& tx, uint64_t unlock_time); - bool construct_tx_and_get_tx_keys(const account_keys& sender_account_keys, const std::vector& sources, const std::vector& destinations, std::vector extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector &amount_keys, bool rct = false); + bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector& sources, const std::vector& destinations, std::vector extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct = false); template bool find_tx_extra_field_by_type(const std::vector& tx_extra_fields, T& field) -- cgit v1.2.3 From c3b3260ae5b5acde445fa88a6f0909f582903754 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 12 Aug 2016 18:45:07 +0100 Subject: New "Halfway RingCT" outputs for coinbase transactions When RingCT is enabled, outputs from coinbase transactions are created as a single output, and stored as RingCT output, with a fake mask. Their amount is not hidden on the blockchain itself, but they are then able to be used as fake inputs in a RingCT ring. Since the output amounts are hidden, their "dustiness" is not an obstacle anymore to mixing, and this makes the coinbase transactions a lot smaller, as well as helping the TXO set to grow more slowly. Also add a new "Null" type of rct signature, which decreases the size required when no signatures are to be stored, as in a coinbase tx. --- src/cryptonote_core/blockchain.cpp | 24 +++++++++------------- src/cryptonote_core/cryptonote_basic.h | 3 +++ .../cryptonote_boost_serialization.h | 4 ++++ src/cryptonote_core/cryptonote_format_utils.cpp | 21 ++++++++++++++----- src/cryptonote_core/cryptonote_format_utils.h | 1 + 5 files changed, 34 insertions(+), 19 deletions(-) (limited to 'src/cryptonote_core') diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 44dde7759..82691eb6b 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -959,7 +959,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl money_in_use += o.amount; partial_block_reward = false; - if (version >= 3) { + if (version == 3) { for (auto &o: b.miner_tx.vout) { if (!is_valid_decomposed_amount(o.amount)) { LOG_PRINT_L1("miner tx output " << print_money(o.amount) << " is not a valid decomposed amount"); @@ -1128,7 +1128,9 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m block size, so first miner transaction generated with fake amount of money, and with phase we know think we know expected block size */ //make blocks coin-base tx looks close to real coinbase tx to get truthful blob size - bool r = construct_miner_tx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.miner_tx, ex_nonce, 11, m_hardfork->get_current_version()); + uint8_t hf_version = m_hardfork->get_current_version(); + size_t max_outs = hf_version >= 4 ? 1 : 11; + bool r = construct_miner_tx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version); CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, first chance"); size_t cumulative_size = txs_size + get_object_blobsize(b.miner_tx); #if defined(DEBUG_CREATE_BLOCK_TEMPLATE) @@ -1137,7 +1139,7 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m #endif for (size_t try_count = 0; try_count != 10; ++try_count) { - r = construct_miner_tx(height, median_size, already_generated_coins, cumulative_size, fee, miner_address, b.miner_tx, ex_nonce, 11, m_hardfork->get_current_version()); + r = construct_miner_tx(height, median_size, already_generated_coins, cumulative_size, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version); CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, second chance"); size_t coinbase_blob_size = get_object_blobsize(b.miner_tx); @@ -2354,17 +2356,6 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, } } - // for v3, we force txes with all mixable inputs to be rct - if (m_hardfork->get_current_version() >= 4) - { - if (n_unmixable == 0 && tx.version == 1) - { - LOG_PRINT_L1("Tx " << get_transaction_hash(tx) << " is not rct and does not have unmixable inputs"); - tvc.m_not_rct = true; - return false; - } - } - if (mixin < 2) { if (n_unmixable == 0) @@ -2543,6 +2534,11 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, const rct::rctSig &rv = tx.rct_signatures; switch (rv.type) { + case rct::RCTTypeNull: { + // we only accept no signatures for coinbase txes + LOG_PRINT_L1("Null rct signature on non-coinbase tx"); + return false; + } case rct::RCTTypeSimple: { // check all this, either recontructed (so should really pass), or not { diff --git a/src/cryptonote_core/cryptonote_basic.h b/src/cryptonote_core/cryptonote_basic.h index b68040597..f54b8c2b3 100644 --- a/src/cryptonote_core/cryptonote_basic.h +++ b/src/cryptonote_core/cryptonote_basic.h @@ -233,6 +233,8 @@ namespace cryptonote FIELD(rct_signatures) switch (rct_signatures.type) { + case rct::RCTTypeNull: + break; case rct::RCTTypeSimple: if (rct_signatures.mixRing.size() && rct_signatures.mixRing.size() != vin.size()) return false; @@ -276,6 +278,7 @@ namespace cryptonote vout.clear(); extra.clear(); signatures.clear(); + rct_signatures.type = rct::RCTTypeNull; } inline diff --git a/src/cryptonote_core/cryptonote_boost_serialization.h b/src/cryptonote_core/cryptonote_boost_serialization.h index 09e9a7fa7..c91f78c58 100644 --- a/src/cryptonote_core/cryptonote_boost_serialization.h +++ b/src/cryptonote_core/cryptonote_boost_serialization.h @@ -249,6 +249,8 @@ namespace boost inline void serialize(Archive &a, rct::rctSigBase &x, const boost::serialization::version_type ver) { a & x.type; + if (x.type == rct::RCTTypeNull) + return; if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple) throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type"); // a & x.message; message is not serialized, as it can be reconstructed from the tx data @@ -264,6 +266,8 @@ namespace boost inline void serialize(Archive &a, rct::rctSig &x, const boost::serialization::version_type ver) { a & x.type; + if (x.type == rct::RCTTypeNull) + return; if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple) throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type"); // a & x.message; message is not serialized, as it can be reconstructed from the tx data diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index 6a3172d4e..cbaf11398 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -136,7 +136,10 @@ namespace cryptonote // from hard fork 2, we cut out the low significant digits. This makes the tx smaller, and // keeps the paid amount almost the same. The unpaid remainder gets pushed back to the // emission schedule - if (hard_fork_version >= 2) { + // from hard fork 4, we use a single "dusty" output. This makes the tx even smaller, + // and avoids the quantization. These outputs will be added as rct outputs with identity + // masks, to they can be used as rct inputs. + if (hard_fork_version >= 2 && hard_fork_version < 4) { block_reward = block_reward - block_reward % ::config::BASE_REWARD_CLAMP_THRESHOLD; } @@ -146,12 +149,16 @@ namespace cryptonote [&out_amounts](uint64_t a_dust) { out_amounts.push_back(a_dust); }); CHECK_AND_ASSERT_MES(1 <= max_outs, false, "max_out must be non-zero"); - if (height == 0) + if (height == 0 || hard_fork_version >= 4) { // the genesis block was not decomposed, for unknown reasons while (max_outs < out_amounts.size()) { - out_amounts[out_amounts.size() - 2] += out_amounts.back(); + //out_amounts[out_amounts.size() - 2] += out_amounts.back(); + //out_amounts.resize(out_amounts.size() - 1); + out_amounts[1] += out_amounts[0]; + for (size_t n = 1; n < out_amounts.size(); ++n) + out_amounts[n - 1] = out_amounts[n]; out_amounts.resize(out_amounts.size() - 1); } } @@ -182,7 +189,11 @@ namespace cryptonote CHECK_AND_ASSERT_MES(summary_amounts == block_reward, false, "Failed to construct miner tx, summary_amounts = " << summary_amounts << " not equal block_reward = " << block_reward); - tx.version = 1; + if (hard_fork_version >= 4) + tx.version = 2; + else + tx.version = 1; + //lock tx.unlock_time = height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; tx.vin.push_back(in); @@ -726,7 +737,7 @@ namespace cryptonote // zero out all amounts to mask rct outputs, real amounts are now encrypted for (size_t i = 0; i < tx.vin.size(); ++i) { - if (!(sources[i].mask == rct::identity())) + if (sources[i].rct) boost::get(tx.vin[i]).amount = 0; } for (size_t i = 0; i < tx.vout.size(); ++i) diff --git a/src/cryptonote_core/cryptonote_format_utils.h b/src/cryptonote_core/cryptonote_format_utils.h index 5da256921..e6a3bfba4 100644 --- a/src/cryptonote_core/cryptonote_format_utils.h +++ b/src/cryptonote_core/cryptonote_format_utils.h @@ -58,6 +58,7 @@ namespace cryptonote crypto::public_key real_out_tx_key; //incoming real tx public key size_t real_output_in_tx_index; //index in transaction outputs vector uint64_t amount; //money + bool rct; //true if the output is rct rct::key mask; //ringct amount mask void push_output(uint64_t idx, const crypto::public_key &k, uint64_t amount) { outputs.push_back(std::make_pair(idx, rct::ctkey({rct::pk2rct(k), rct::zeroCommit(amount)}))); } -- cgit v1.2.3 From 0815c72df7ddfc35e46595274d5f463a9d17c7de Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 14 Aug 2016 11:30:44 +0100 Subject: core: allow v1 txes after HF 5 when sweeping unmixable outputs --- src/cryptonote_core/blockchain.cpp | 20 +++++++++++++++++++- src/cryptonote_core/tx_pool.cpp | 15 --------------- 2 files changed, 19 insertions(+), 16 deletions(-) (limited to 'src/cryptonote_core') diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 82691eb6b..c094d1bd9 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2322,9 +2322,11 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx); + const uint8_t hf_version = m_hardfork->get_current_version(); + // from hard fork 2, we require mixin at least 2 unless one output cannot mix with 2 others // if one output cannot mix with 2 others, we accept at most 1 output that can mix - if (m_hardfork->get_current_version() >= 2) + if (hf_version >= 2) { size_t n_unmixable = 0, n_mixable = 0; size_t mixin = std::numeric_limits::max(); @@ -2371,6 +2373,22 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, return false; } } + + // min/max tx version based on HF, and we accept v1 txes if having a non mixable + const size_t max_tx_version = (hf_version <= 3) ? 1 : 2; + if (tx.version > max_tx_version) + { + LOG_PRINT_L1("transaction version " << (unsigned)tx.version << " is higher than max accepted version " << max_tx_version); + tvc.m_verifivation_failed = true; + return false; + } + const size_t min_tx_version = (n_unmixable > 0 ? 1 : (hf_version >= 5) ? 2 : 1); + if (tx.version < min_tx_version) + { + LOG_PRINT_L1("transaction version " << (unsigned)tx.version << " is lower than min accepted version " << min_tx_version); + tvc.m_verifivation_failed = true; + return false; + } } auto it = m_check_txin_table.find(tx_prefix_hash); diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 2ed12eb5c..46fab4dcf 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -86,21 +86,6 @@ namespace cryptonote return false; } - const size_t max_tx_version = (version <= 3) ? 1 : 2; - if (tx.version > max_tx_version) - { - LOG_PRINT_L1("transaction version " << (unsigned)tx.version << " is higher than max accepted version " << max_tx_version); - tvc.m_verifivation_failed = true; - return false; - } - const size_t min_tx_version = (version >= 5) ? 2 : 1; - if (tx.version < min_tx_version) - { - LOG_PRINT_L1("transaction version " << (unsigned)tx.version << " is lower than min accepted version " << min_tx_version); - tvc.m_verifivation_failed = true; - return false; - } - // we do not accept transactions that timed out before, unless they're // kept_by_block if (!kept_by_block && m_timed_out_transactions.find(id) != m_timed_out_transactions.end()) -- cgit v1.2.3 From 4f887dea6eb0ff1b72a829a503e1f4c1ec3d704e Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 14 Aug 2016 19:15:04 +0100 Subject: increase minimum mixin to 4 on hard fork 5 --- src/cryptonote_core/blockchain.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/cryptonote_core') diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index c094d1bd9..8ff286145 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2330,6 +2330,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, { size_t n_unmixable = 0, n_mixable = 0; size_t mixin = std::numeric_limits::max(); + const size_t min_mixin = hf_version >= 5 ? 4 : 2; for (const auto& txin : tx.vin) { // non txin_to_key inputs will be rejected below @@ -2348,7 +2349,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, uint64_t n_outputs = m_db->get_num_outputs(in_to_key.amount); LOG_PRINT_L2("output size " << print_money(in_to_key.amount) << ": " << n_outputs << " available"); // n_outputs includes the output we're considering - if (n_outputs <= 2) + if (n_outputs <= min_mixin) ++n_unmixable; else ++n_mixable; @@ -2358,7 +2359,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, } } - if (mixin < 2) + if (mixin < min_mixin) { if (n_unmixable == 0) { -- cgit v1.2.3 From b951bc8780f9a11ccf8dbc1ee89c2aa550bad101 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 21 Aug 2016 09:56:32 +0100 Subject: wallet: transfer_selected_rct now also selects fake outs --- src/cryptonote_core/blockchain.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/cryptonote_core') diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 8ff286145..097998ec8 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1753,11 +1753,11 @@ bool Blockchain::get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_R for (const auto &i: req.outputs) { // get tx_hash, tx_out_index from DB - crypto::public_key key = m_db->get_output_key(i.amount, i.index).pubkey; + const output_data_t &od = m_db->get_output_key(i.amount, i.index); tx_out_index toi = m_db->get_output_tx_and_index(i.amount, i.index); bool unlocked = is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)); - res.outs.push_back({key, unlocked}); + res.outs.push_back({od.pubkey, od.commitment, unlocked}); } return true; } -- cgit v1.2.3 From a0925e628ebae82a57acffb2824e2e4232b3ebb6 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 27 Aug 2016 10:30:34 +0100 Subject: core: use full rct signatures if just one input It is slightly smaller, but requires all rings to have the real input on the same index, so can only be used for single input txes. --- src/cryptonote_core/cryptonote_format_utils.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/cryptonote_core') diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index cbaf11398..32b96f1fc 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -656,7 +656,10 @@ namespace cryptonote size_t n_total_outs = sources[0].outputs.size(); // only for non-simple rct BOOST_FOREACH(const tx_source_entry& src_entr, sources) all_rct_inputs &= !(src_entr.mask == rct::identity()); - bool use_simple_rct = true; //all_rct_inputs; + + // the non-simple version is slightly smaller, but assumes all real inputs + // are on the same index, so can only be used if there just one ring. + bool use_simple_rct = sources.size() > 1; if (!use_simple_rct) { -- cgit v1.2.3 From 887db9f93670da8c1eb28c9d53d6d4f055486e3b Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 28 Aug 2016 21:20:01 +0100 Subject: blockchain: testnet heights for v3, v4, and v5 --- src/cryptonote_core/blockchain.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/cryptonote_core') diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 097998ec8..ef72dac59 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -99,6 +99,10 @@ static const struct { // version 2 starts from block 624634, which is on or around the 23rd of November, 2015. Fork time finalised on 2015-11-20. No fork voting occurs for the v2 fork. { 2, 624634, 0, 1445355000 }, + + { 3, 800500, 0, 1472415034 }, + { 4, 801220, 0, 1472415035 }, + { 5, 802660, 0, 1472415036 }, }; static const uint64_t testnet_hard_fork_version_1_till = 624633; -- cgit v1.2.3