diff options
Diffstat (limited to 'src/cryptonote_core')
-rw-r--r-- | src/cryptonote_core/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/cryptonote_core/blockchain.cpp | 69 | ||||
-rw-r--r-- | src/cryptonote_core/blockchain.h | 33 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_core.cpp | 98 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_core.h | 34 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_tx_utils.cpp | 128 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_tx_utils.h | 13 |
7 files changed, 190 insertions, 186 deletions
diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt index 169a38f0a..eeed881da 100644 --- a/src/cryptonote_core/CMakeLists.txt +++ b/src/cryptonote_core/CMakeLists.txt @@ -59,6 +59,7 @@ target_link_libraries(cryptonote_core common cncrypto blockchain_db + multisig ringct ${Boost_DATE_TIME_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 836856bae..709c5e852 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -305,7 +305,7 @@ uint64_t Blockchain::get_current_blockchain_height() const //------------------------------------------------------------------ //FIXME: possibly move this into the constructor, to avoid accidentally // dereferencing a null BlockchainDB pointer -bool Blockchain::init(BlockchainDB* db, const bool testnet, const cryptonote::test_options *test_options) +bool Blockchain::init(BlockchainDB* db, const bool testnet, bool offline, const cryptonote::test_options *test_options) { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_tx_pool); @@ -321,12 +321,14 @@ bool Blockchain::init(BlockchainDB* db, const bool testnet, const cryptonote::te if (!db->is_open()) { LOG_ERROR("Attempted to init Blockchain with unopened DB"); + delete db; return false; } m_db = db; m_testnet = testnet; + m_offline = offline; if (m_hardfork == nullptr) { if (fakechain) @@ -414,11 +416,11 @@ bool Blockchain::init(BlockchainDB* db, const bool testnet, const cryptonote::te return true; } //------------------------------------------------------------------ -bool Blockchain::init(BlockchainDB* db, HardFork*& hf, const bool testnet) +bool Blockchain::init(BlockchainDB* db, HardFork*& hf, const bool testnet, bool offline) { if (hf != nullptr) m_hardfork = hf; - bool res = init(db, testnet, NULL); + bool res = init(db, testnet, offline, NULL); if (hf == nullptr) hf = m_hardfork; return res; @@ -470,7 +472,7 @@ bool Blockchain::deinit() // memory operation), otherwise we may cause a loop. if (m_db == NULL) { - throw new DB_ERROR("The db pointer is null in Blockchain, the blockchain may be corrupt!"); + throw DB_ERROR("The db pointer is null in Blockchain, the blockchain may be corrupt!"); } try @@ -488,7 +490,9 @@ bool Blockchain::deinit() } delete m_hardfork; + m_hardfork = NULL; delete m_db; + m_db = NULL; return true; } //------------------------------------------------------------------ @@ -2049,49 +2053,6 @@ bool Blockchain::get_transactions(const t_ids_container& txs_ids, t_tx_container return true; } //------------------------------------------------------------------ -void Blockchain::print_blockchain(uint64_t start_index, uint64_t end_index) const -{ - LOG_PRINT_L3("Blockchain::" << __func__); - std::stringstream ss; - CRITICAL_REGION_LOCAL(m_blockchain_lock); - auto h = m_db->height(); - if(start_index > h) - { - MERROR("Wrong starter index set: " << start_index << ", expected max index " << h); - return; - } - - for(size_t i = start_index; i <= h && i != end_index; i++) - { - ss << "height " << i << ", timestamp " << m_db->get_block_timestamp(i) << ", cumul_dif " << m_db->get_block_cumulative_difficulty(i) << ", size " << m_db->get_block_size(i) << "\nid\t\t" << m_db->get_block_hash_from_height(i) << "\ndifficulty\t\t" << m_db->get_block_difficulty(i) << ", nonce " << m_db->get_block_from_height(i).nonce << ", tx_count " << m_db->get_block_from_height(i).tx_hashes.size() << std::endl; - } - MCINFO("globlal", "Current blockchain:" << std::endl << ss.str()); -} -//------------------------------------------------------------------ -void Blockchain::print_blockchain_index() const -{ - LOG_PRINT_L3("Blockchain::" << __func__); - std::stringstream ss; - CRITICAL_REGION_LOCAL(m_blockchain_lock); - auto height = m_db->height(); - if (height != 0) - { - for(uint64_t i = 0; i <= height; i++) - { - ss << "height: " << i << ", hash: " << m_db->get_block_hash_from_height(i); - } - } - - MINFO("Current blockchain index:" << std::endl << ss.str()); -} -//------------------------------------------------------------------ -//TODO: remove this function and references to it -void Blockchain::print_blockchain_outs(const std::string& file) const -{ - LOG_PRINT_L3("Blockchain::" << __func__); - return; -} -//------------------------------------------------------------------ // Find the split point between us and foreign blockchain and return // (by reference) the most recent common block hash along with up to // BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT additional (more recent) hashes. @@ -2337,7 +2298,7 @@ void Blockchain::on_new_tx_from_block(const cryptonote::transaction &tx) TIME_MEASURE_FINISH(a); if(m_show_time_stats) { - size_t ring_size = tx.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(tx.vin[0]).key_offsets.size() : 0; + size_t ring_size = !tx.vin.empty() && tx.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(tx.vin[0]).key_offsets.size() : 0; MINFO("HASH: " << "-" << " I/M/O: " << tx.vin.size() << "/" << ring_size << "/" << tx.vout.size() << " H: " << 0 << " chcktx: " << a); } } @@ -2372,7 +2333,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, uint64_t& max_used_block_heigh TIME_MEASURE_FINISH(a); if(m_show_time_stats) { - size_t ring_size = tx.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(tx.vin[0]).key_offsets.size() : 0; + size_t ring_size = !tx.vin.empty() && tx.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(tx.vin[0]).key_offsets.size() : 0; MINFO("HASH: " << get_transaction_hash(tx) << " I/M/O: " << tx.vin.size() << "/" << ring_size << "/" << tx.vout.size() << " H: " << max_used_block_height << " ms: " << a + m_fake_scan_time << " B: " << get_object_blobsize(tx)); } if (!res) @@ -2465,6 +2426,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr // mixRing - full and simple store it in opposite ways if (rv.type == rct::RCTTypeFull || rv.type == rct::RCTTypeFullBulletproof) { + CHECK_AND_ASSERT_MES(!pubkeys.empty() && !pubkeys[0].empty(), false, "empty pubkeys"); rv.mixRing.resize(pubkeys[0].size()); for (size_t m = 0; m < pubkeys[0].size(); ++m) rv.mixRing[m].clear(); @@ -2479,6 +2441,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr } else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeSimpleBulletproof) { + CHECK_AND_ASSERT_MES(!pubkeys.empty() && !pubkeys[0].empty(), false, "empty pubkeys"); rv.mixRing.resize(pubkeys.size()); for (size_t n = 0; n < pubkeys.size(); ++n) { @@ -2810,7 +2773,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, } for (size_t n = 0; n < tx.vin.size(); ++n) { - if (memcmp(&boost::get<txin_to_key>(tx.vin[n]).k_image, &rv.p.MGs[n].II[0], 32)) + if (rv.p.MGs[n].II.empty() || memcmp(&boost::get<txin_to_key>(tx.vin[n]).k_image, &rv.p.MGs[n].II[0], 32)) { MERROR_VER("Failed to check ringct signatures: mismatched key image"); return false; @@ -2863,7 +2826,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, MERROR_VER("Failed to check ringct signatures: Bad MGs size"); return false; } - if (rv.p.MGs[0].II.size() != tx.vin.size()) + if (rv.p.MGs.empty() || rv.p.MGs[0].II.size() != tx.vin.size()) { MERROR_VER("Failed to check ringct signatures: mismatched II/vin sizes"); return false; @@ -3641,14 +3604,14 @@ bool Blockchain::update_checkpoints(const std::string& file_path, bool check_dns // 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_enforce_dns_checkpoints && check_dns && !m_offline) { if (!m_checkpoints.load_checkpoints_from_dns()) { return false; } } - else if (check_dns) + else if (check_dns && !m_offline) { checkpoints dns_points; dns_points.load_checkpoints_from_dns(); diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index e0936da8f..2d5307ac0 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -112,11 +112,12 @@ namespace cryptonote * * @param db a pointer to the backing store to use for the blockchain * @param testnet true if on testnet, else false + * @param offline true if running offline, else false * @param test_options test parameters * * @return true on success, false if any initialization steps fail */ - bool init(BlockchainDB* db, const bool testnet = false, const cryptonote::test_options *test_options = NULL); + bool init(BlockchainDB* db, const bool testnet = false, bool offline = false, const cryptonote::test_options *test_options = NULL); /** * @brief Initialize the Blockchain state @@ -124,10 +125,11 @@ namespace cryptonote * @param db a pointer to the backing store to use for the blockchain * @param hf a structure containing hardfork information * @param testnet true if on testnet, else false + * @param offline true if running offline, else false * * @return true on success, false if any initialization steps fail */ - bool init(BlockchainDB* db, HardFork*& hf, const bool testnet = false); + bool init(BlockchainDB* db, HardFork*& hf, const bool testnet = false, bool offline = false); /** * @brief Uninitializes the blockchain state @@ -678,32 +680,6 @@ namespace cryptonote //debug functions /** - * @brief prints data about a snippet of the blockchain - * - * if start_index is greater than the blockchain height, do nothing - * - * @param start_index height on chain to start at - * @param end_index height on chain to end at - */ - void print_blockchain(uint64_t start_index, uint64_t end_index) const; - - /** - * @brief prints every block's hash - * - * WARNING: This function will absolutely crush a terminal in prints, so - * it is recommended to redirect this output to a log file (or null sink - * if a log file is already set up, as should be the default) - */ - void print_blockchain_index() const; - - /** - * @brief currently does nothing, candidate for removal - * - * @param file - */ - void print_blockchain_outs(const std::string& file) const; - - /** * @brief check the blockchain against a set of checkpoints * * If a block fails a checkpoint and enforce is enabled, the blockchain @@ -1027,6 +1003,7 @@ namespace cryptonote HardFork *m_hardfork; bool m_testnet; + bool m_offline; std::atomic<bool> m_cancel; diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 5cfa4b3e9..adbc727b0 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -28,7 +28,10 @@ // // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers +#include <boost/algorithm/string.hpp> + #include "include_base_utils.h" +#include "string_tools.h" using namespace epee; #include <unordered_set> @@ -44,12 +47,13 @@ using namespace epee; #include "cryptonote_config.h" #include "cryptonote_tx_utils.h" #include "misc_language.h" +#include "file_io_utils.h" #include <csignal> -#include <p2p/net_node.h> #include "checkpoints/checkpoints.h" #include "ringct/rctTypes.h" #include "blockchain_db/blockchain_db.h" #include "ringct/rctSigs.h" +#include "version.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "cn" @@ -75,6 +79,10 @@ namespace cryptonote , "Run on testnet. The wallet must be launched with --testnet flag." , false }; + const command_line::arg_descriptor<bool> arg_offline = { + "offline" + , "Do not listen for peers, nor connect to any" + }; static const command_line::arg_descriptor<bool> arg_test_drop_download = { "test-drop-download" @@ -227,10 +235,7 @@ namespace cryptonote command_line::add_arg(desc, arg_check_updates); command_line::add_arg(desc, arg_fluffy_blocks); command_line::add_arg(desc, arg_test_dbg_lock_sleep); - - // we now also need some of net_node's options (p2p bind arg, for separate data dir) - command_line::add_arg(desc, nodetool::arg_testnet_p2p_bind_port, false); - command_line::add_arg(desc, nodetool::arg_p2p_bind_port, false); + command_line::add_arg(desc, arg_offline); miner::init_options(desc); BlockchainDB::init_options(desc); @@ -264,6 +269,7 @@ namespace cryptonote set_enforce_dns_checkpoints(command_line::get_arg(vm, arg_dns_checkpoints)); test_drop_download_height(command_line::get_arg(vm, arg_test_drop_download_height)); m_fluffy_blocks_enabled = m_testnet || get_arg(vm, arg_fluffy_blocks); + m_offline = get_arg(vm, arg_offline); if (command_line::get_arg(vm, arg_test_drop_download) == true) test_drop_download(); @@ -329,21 +335,17 @@ namespace cryptonote return m_blockchain_storage.get_alternative_blocks_count(); } //----------------------------------------------------------------------------------------------- - bool core::init(const boost::program_options::variables_map& vm, const cryptonote::test_options *test_options) + bool core::init(const boost::program_options::variables_map& vm, const char *config_subdir, const cryptonote::test_options *test_options) { start_time = std::time(nullptr); m_fakechain = test_options != NULL; bool r = handle_command_line(vm); bool testnet = command_line::get_arg(vm, arg_testnet_on); - auto p2p_bind_arg = testnet ? nodetool::arg_testnet_p2p_bind_port : nodetool::arg_p2p_bind_port; - std::string m_port = command_line::get_arg(vm, p2p_bind_arg); std::string m_config_folder_mempool = m_config_folder; - if ((!testnet && m_port != std::to_string(::config::P2P_DEFAULT_PORT)) - || (testnet && m_port != std::to_string(::config::testnet::P2P_DEFAULT_PORT))) { - m_config_folder_mempool = m_config_folder_mempool + "/" + m_port; - } + if (config_subdir) + m_config_folder_mempool = m_config_folder_mempool + "/" + config_subdir; std::string db_type = command_line::get_arg(vm, cryptonote::arg_db_type); std::string db_sync_mode = command_line::get_arg(vm, cryptonote::arg_db_sync_mode); @@ -376,7 +378,7 @@ namespace cryptonote // folder might not be a directory, etc, etc catch (...) { } - BlockchainDB* db = new_db(db_type); + std::unique_ptr<BlockchainDB> db(new_db(db_type)); if (db == NULL) { LOG_ERROR("Attempted to use non-existent database type"); @@ -467,7 +469,7 @@ namespace cryptonote m_blockchain_storage.set_user_options(blocks_threads, blocks_per_sync, sync_mode, fast_sync); - r = m_blockchain_storage.init(db, m_testnet, test_options); + r = m_blockchain_storage.init(db.release(), m_testnet, m_offline, test_options); r = m_mempool.init(); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool"); @@ -1051,21 +1053,6 @@ namespace cryptonote return m_blockchain_storage.find_blockchain_supplement(req_start_block, qblock_ids, blocks, total_height, start_height, max_count); } //----------------------------------------------------------------------------------------------- - void core::print_blockchain(uint64_t start_index, uint64_t end_index) const - { - m_blockchain_storage.print_blockchain(start_index, end_index); - } - //----------------------------------------------------------------------------------------------- - void core::print_blockchain_index() const - { - m_blockchain_storage.print_blockchain_index(); - } - //----------------------------------------------------------------------------------------------- - void core::print_blockchain_outs(const std::string& file) - { - m_blockchain_storage.print_blockchain_outs(file); - } - //----------------------------------------------------------------------------------------------- bool core::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 { return m_blockchain_storage.get_random_outs_for_amounts(req, res); @@ -1340,11 +1327,16 @@ namespace cryptonote { if(!m_starter_message_showed) { + std::string main_message; + if (m_offline) + main_message = "The daemon is running offline and will not attempt to sync to the Monero network."; + else + main_message = "The daemon will start synchronizing with the network. This may take a long time to complete."; MGINFO_YELLOW(ENDL << "**********************************************************************" << ENDL - << "The daemon will start synchronizing with the network. This may take a long time to complete." << ENDL + << main_message << ENDL << ENDL - << "You can set the level of process detailization* through \"set_log <level|categories>\" command*," << ENDL - << "where <level> is between 0 (no details) and 4 (very verbose), or custom category based levels (eg, *:WARNING)" << ENDL + << "You can set the level of process detailization through \"set_log <level|categories>\" command," << ENDL + << "where <level> is between 0 (no details) and 4 (very verbose), or custom category based levels (eg, *:WARNING)." << ENDL << ENDL << "Use the \"help\" command to see the list of available commands." << ENDL << "Use \"help <command>\" to see a command's documentation." << ENDL @@ -1404,6 +1396,9 @@ namespace cryptonote static const char subdir[] = "source"; // because it can never be simple #endif + if (m_offline) + return true; + if (check_updates_level == UPDATES_DISABLED) return true; @@ -1443,27 +1438,56 @@ namespace cryptonote if (!tools::sha256sum(path.string(), file_hash) || (hash != epee::string_tools::pod_to_hex(file_hash))) { MCDEBUG("updates", "We don't have that file already, downloading"); + const std::string tmppath = path.string() + ".tmp"; + if (epee::file_io_utils::is_file_exist(tmppath)) + { + MCDEBUG("updates", "We have part of the file already, resuming download"); + } m_last_update_length = 0; - m_update_download = tools::download_async(path.string(), url, [this, hash](const std::string &path, const std::string &uri, bool success) { + m_update_download = tools::download_async(tmppath, url, [this, hash, path](const std::string &tmppath, const std::string &uri, bool success) { + bool remove = false, good = true; if (success) { crypto::hash file_hash; - if (!tools::sha256sum(path, file_hash)) + if (!tools::sha256sum(tmppath, file_hash)) { - MCERROR("updates", "Failed to hash " << path); + MCERROR("updates", "Failed to hash " << tmppath); + remove = true; + good = false; } - if (hash != epee::string_tools::pod_to_hex(file_hash)) + else if (hash != epee::string_tools::pod_to_hex(file_hash)) { MCERROR("updates", "Download from " << uri << " does not match the expected hash"); + remove = true; + good = false; } - MCLOG_CYAN(el::Level::Info, "updates", "New version downloaded to " << path); } else { MCERROR("updates", "Failed to download " << uri); + good = false; } boost::unique_lock<boost::mutex> lock(m_update_mutex); m_update_download = 0; + if (success && !remove) + { + std::error_code e = tools::replace_file(tmppath, path.string()); + if (e) + { + MCERROR("updates", "Failed to rename downloaded file"); + good = false; + } + } + else if (remove) + { + if (!boost::filesystem::remove(tmppath)) + { + MCERROR("updates", "Failed to remove invalid downloaded file"); + good = false; + } + } + if (good) + MCLOG_CYAN(el::Level::Info, "updates", "New version downloaded to " << path.string()); }, [this](const std::string &path, const std::string &uri, size_t length, ssize_t content_length) { if (length >= m_last_update_length + 1024 * 1024 * 10) { diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 905e67f6d..adc201fb5 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -36,7 +36,6 @@ #include <boost/program_options/variables_map.hpp> #include <boost/interprocess/sync/file_lock.hpp> -#include "p2p/net_node_common.h" #include "cryptonote_protocol/cryptonote_protocol_handler_common.h" #include "storages/portable_storage_template_helper.h" #include "common/download.h" @@ -62,6 +61,7 @@ namespace cryptonote extern const command_line::arg_descriptor<std::string> arg_data_dir; extern const command_line::arg_descriptor<std::string> arg_testnet_data_dir; extern const command_line::arg_descriptor<bool, false> arg_testnet_on; + extern const command_line::arg_descriptor<bool> arg_offline; /************************************************************************/ /* */ @@ -241,11 +241,12 @@ namespace cryptonote * a miner instance with parameters given on the command line (or defaults) * * @param vm command line parameters + * @param config_subdir subdirectory for config storage * @param test_options configuration options for testing * * @return false if one of the init steps fails, otherwise true */ - bool init(const boost::program_options::variables_map& vm, const test_options *test_options = NULL); + bool init(const boost::program_options::variables_map& vm, const char *config_subdir = NULL, const test_options *test_options = NULL); /** * @copydoc Blockchain::reset_and_set_genesis_block @@ -600,20 +601,6 @@ namespace cryptonote const Blockchain& get_blockchain_storage()const{return m_blockchain_storage;} /** - * @copydoc Blockchain::print_blockchain - * - * @note see Blockchain::print_blockchain - */ - void print_blockchain(uint64_t start_index, uint64_t end_index) const; - - /** - * @copydoc Blockchain::print_blockchain_index - * - * @note see Blockchain::print_blockchain_index - */ - void print_blockchain_index() const; - - /** * @copydoc tx_memory_pool::print_pool * * @note see tx_memory_pool::print_pool @@ -621,13 +608,6 @@ namespace cryptonote std::string print_pool(bool short_format) const; /** - * @copydoc Blockchain::print_blockchain_outs - * - * @note see Blockchain::print_blockchain_outs - */ - void print_blockchain_outs(const std::string& file); - - /** * @copydoc miner::on_synchronized * * @note see miner::on_synchronized @@ -773,6 +753,13 @@ namespace cryptonote */ uint64_t get_free_space() const; + /** + * @brief get whether the core is running offline + * + * @return whether the core is running offline + */ + bool offline() const { return m_offline; } + private: /** @@ -1000,6 +987,7 @@ namespace cryptonote boost::mutex m_update_mutex; bool m_fluffy_blocks_enabled; + bool m_offline; }; } diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 4afa669fd..916b1e05a 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -30,6 +30,7 @@ #include <unordered_set> #include "include_base_utils.h" +#include "string_tools.h" using namespace epee; #include "common/apply_permutation.h" @@ -39,12 +40,39 @@ using namespace epee; #include "crypto/crypto.h" #include "crypto/hash.h" #include "ringct/rctSigs.h" +#include "multisig/multisig.h" using namespace crypto; namespace cryptonote { //--------------------------------------------------------------- + void classify_addresses(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::account_public_address>& change_addr, size_t &num_stdaddresses, size_t &num_subaddresses, account_public_address &single_dest_subaddress) + { + num_stdaddresses = 0; + num_subaddresses = 0; + std::unordered_set<cryptonote::account_public_address> unique_dst_addresses; + for(const tx_destination_entry& dst_entr: destinations) + { + if (change_addr && dst_entr.addr == change_addr) + continue; + if (unique_dst_addresses.count(dst_entr.addr) == 0) + { + unique_dst_addresses.insert(dst_entr.addr); + if (dst_entr.is_subaddress) + { + ++num_subaddresses; + single_dest_subaddress = dst_entr.addr; + } + else + { + ++num_stdaddresses; + } + } + } + LOG_PRINT_L2("destinations include " << num_stdaddresses << " standard addresses and " << num_subaddresses << " subaddresses"); + } + //--------------------------------------------------------------- bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce, size_t max_outs, uint8_t hard_fork_version) { tx.vin.clear(); tx.vout.clear(); @@ -160,19 +188,27 @@ namespace cryptonote return destinations[0].addr.m_view_public_key; } //--------------------------------------------------------------- - bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct, bool bulletproof) + bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct, bool bulletproof, rct::multisig_out *msout) { + if (sources.empty()) + { + LOG_ERROR("Empty sources"); + return false; + } + std::vector<rct::key> amount_keys; tx.set_null(); amount_keys.clear(); + if (msout) + { + msout->c.clear(); + } tx.version = rct ? 2 : 1; tx.unlock_time = unlock_time; tx.extra = extra; - keypair txkey; - txkey.sec = rct::rct2sk(rct::skGen()); - tx_key = txkey.sec; + crypto::public_key txkey_pub; // if we have a stealth payment id, find it and encrypt it with the tx key now std::vector<tx_extra_field> tx_extra_fields; @@ -192,7 +228,7 @@ namespace cryptonote return false; } - if (!encrypt_payment_id(payment_id, view_key_pub, txkey.sec)) + if (!encrypt_payment_id(payment_id, view_key_pub, tx_key)) { LOG_ERROR("Failed to encrypt payment id"); return false; @@ -246,8 +282,8 @@ 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.dest) ) + //check that derivated key is equal with real output key (if non multisig) + if(!msout && !(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].second.dest) ) { LOG_ERROR("derived public key mismatch with output public key at index " << idx << ", real out " << src_entr.real_output << "! "<< ENDL << "derived_key:" << string_tools::pod_to_hex(in_ephemeral.pub) << ENDL << "real output_public_key:" @@ -260,7 +296,7 @@ namespace cryptonote //put key image into tx input txin_to_key input_to_key; input_to_key.amount = src_entr.amount; - input_to_key.k_image = img; + input_to_key.k_image = msout ? rct::rct2ki(src_entr.multisig_kLRki.ki) : img; //fill outputs array and use relative offsets for(const tx_source_entry::output_entry& out_entry: src_entr.outputs) @@ -292,47 +328,29 @@ namespace cryptonote // figure out if we need to make additional tx pubkeys size_t num_stdaddresses = 0; size_t num_subaddresses = 0; - std::unordered_set<cryptonote::account_public_address> unique_dst_addresses; account_public_address single_dest_subaddress; - for(const tx_destination_entry& dst_entr: destinations) - { - if (change_addr && dst_entr.addr == *change_addr) - continue; - if (unique_dst_addresses.count(dst_entr.addr) == 0) - { - unique_dst_addresses.insert(dst_entr.addr); - if (dst_entr.is_subaddress) - { - ++num_subaddresses; - single_dest_subaddress = dst_entr.addr; - } - else - { - ++num_stdaddresses; - } - } - } - LOG_PRINT_L2("destinations include " << num_stdaddresses << " standard addresses and " << num_subaddresses << "subaddresses"); + classify_addresses(destinations, change_addr, num_stdaddresses, num_subaddresses, single_dest_subaddress); // if this is a single-destination transfer to a subaddress, we set the tx pubkey to R=s*D if (num_stdaddresses == 0 && num_subaddresses == 1) { - txkey.pub = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(single_dest_subaddress.m_spend_public_key), rct::sk2rct(txkey.sec))); + txkey_pub = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(single_dest_subaddress.m_spend_public_key), rct::sk2rct(tx_key))); } else { - txkey.pub = rct::rct2pk(rct::scalarmultBase(rct::sk2rct(txkey.sec))); + txkey_pub = rct::rct2pk(rct::scalarmultBase(rct::sk2rct(tx_key))); } remove_field_from_tx_extra(tx.extra, typeid(tx_extra_pub_key)); - add_tx_pub_key_to_extra(tx, txkey.pub); + add_tx_pub_key_to_extra(tx, txkey_pub); std::vector<crypto::public_key> additional_tx_public_keys; - additional_tx_keys.clear(); // we don't need to include additional tx keys if: // - all the destinations are standard addresses // - there's only one destination which is a subaddress bool need_additional_txkeys = num_subaddresses > 0 && (num_stdaddresses > 0 || num_subaddresses > 1); + if (need_additional_txkeys) + CHECK_AND_ASSERT_MES(destinations.size() == additional_tx_keys.size(), false, "Wrong amount of additional tx keys"); uint64_t summary_outs_money = 0; //fill outputs @@ -347,7 +365,7 @@ namespace cryptonote keypair additional_txkey; if (need_additional_txkeys) { - additional_txkey.sec = rct::rct2sk(rct::skGen()); + additional_txkey.sec = additional_tx_keys[output_index]; if (dst_entr.is_subaddress) additional_txkey.pub = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(dst_entr.addr.m_spend_public_key), rct::sk2rct(additional_txkey.sec))); else @@ -358,20 +376,19 @@ namespace cryptonote if (change_addr && dst_entr.addr == *change_addr) { // sending change to yourself; derivation = a*R - r = crypto::generate_key_derivation(txkey.pub, sender_account_keys.m_view_secret_key, derivation); - CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << txkey.pub << ", " << sender_account_keys.m_view_secret_key << ")"); + r = crypto::generate_key_derivation(txkey_pub, sender_account_keys.m_view_secret_key, derivation); + CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << txkey_pub << ", " << sender_account_keys.m_view_secret_key << ")"); } else { // sending to the recipient; derivation = r*A (or s*C in the subaddress scheme) - r = crypto::generate_key_derivation(dst_entr.addr.m_view_public_key, dst_entr.is_subaddress && need_additional_txkeys ? additional_txkey.sec : txkey.sec, derivation); - CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << dst_entr.addr.m_view_public_key << ", " << (dst_entr.is_subaddress && need_additional_txkeys ? additional_txkey.sec : txkey.sec) << ")"); + r = crypto::generate_key_derivation(dst_entr.addr.m_view_public_key, dst_entr.is_subaddress && need_additional_txkeys ? additional_txkey.sec : tx_key, derivation); + CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << dst_entr.addr.m_view_public_key << ", " << (dst_entr.is_subaddress && need_additional_txkeys ? additional_txkey.sec : tx_key) << ")"); } if (need_additional_txkeys) { additional_tx_public_keys.push_back(additional_txkey.pub); - additional_tx_keys.push_back(additional_txkey.sec); } if (tx.version > 1) @@ -392,10 +409,11 @@ namespace cryptonote output_index++; summary_outs_money += dst_entr.amount; } + CHECK_AND_ASSERT_MES(additional_tx_public_keys.size() == additional_tx_keys.size(), false, "Internal error creating additional public keys"); remove_field_from_tx_extra(tx.extra, typeid(tx_extra_additional_pub_keys)); - LOG_PRINT_L2("tx pubkey: " << txkey.pub); + LOG_PRINT_L2("tx pubkey: " << txkey_pub); if (need_additional_txkeys) { LOG_PRINT_L2("additional tx pubkeys: "); @@ -491,6 +509,7 @@ namespace cryptonote rct::keyV destinations; std::vector<uint64_t> inamounts, outamounts; std::vector<unsigned int> index; + std::vector<rct::multisig_kLRki> kLRki; for (size_t i = 0; i < sources.size(); ++i) { rct::ctkey ctkey; @@ -503,6 +522,10 @@ namespace cryptonote inSk.push_back(ctkey); // inPk: (public key, commitment) // will be done when filling in mixRing + if (msout) + { + kLRki.push_back(sources[i].multisig_kLRki); + } } for (size_t i = 0; i < tx.vout.size(); ++i) { @@ -552,9 +575,9 @@ 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, amount_keys, index, outSk, bulletproof); + tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, msout ? &kLRki : NULL, msout, index, outSk, bulletproof); else - tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, amount_keys, sources[0].real_output, outSk, bulletproof); // same index assumption + tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, amount_keys, msout ? &kLRki[0] : NULL, msout, sources[0].real_output, outSk, bulletproof); // same index assumption CHECK_AND_ASSERT_MES(tx.vout.size() == outSk.size(), false, "outSk size does not match vout"); @@ -566,13 +589,34 @@ namespace cryptonote return true; } //--------------------------------------------------------------- + bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct, bool bulletproof, rct::multisig_out *msout) + { + keypair txkey = keypair::generate(); + tx_key = txkey.sec; + + // figure out if we need to make additional tx pubkeys + size_t num_stdaddresses = 0; + size_t num_subaddresses = 0; + account_public_address single_dest_subaddress; + classify_addresses(destinations, change_addr, num_stdaddresses, num_subaddresses, single_dest_subaddress); + bool need_additional_txkeys = num_subaddresses > 0 && (num_stdaddresses > 0 || num_subaddresses > 1); + if (need_additional_txkeys) + { + additional_tx_keys.clear(); + for (const auto &d: destinations) + additional_tx_keys.push_back(keypair::generate().sec); + } + + return construct_tx_with_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct, bulletproof, msout); + } + //--------------------------------------------------------------- bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time) { std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses; subaddresses[sender_account_keys.m_account_address.m_spend_public_key] = {0,0}; crypto::secret_key tx_key; std::vector<crypto::secret_key> additional_tx_keys; - return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys); + return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, false, NULL); } //--------------------------------------------------------------- bool generate_genesis_block( diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h index d72f5d13b..5947522e2 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.h +++ b/src/cryptonote_core/cryptonote_tx_utils.h @@ -51,6 +51,7 @@ namespace cryptonote uint64_t amount; //money bool rct; //true if the output is rct rct::key mask; //ringct amount mask + rct::multisig_kLRki multisig_kLRki; //multisig info 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)}))); } @@ -63,6 +64,7 @@ namespace cryptonote FIELD(amount) FIELD(rct) FIELD(mask) + FIELD(multisig_kLRki) if (real_output >= outputs.size()) return false; @@ -87,8 +89,9 @@ namespace cryptonote //--------------------------------------------------------------- crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const account_keys &sender_keys); - bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time); - bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, bool bulletproof = false); + bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry> &sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time); + bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, bool bulletproof = false, rct::multisig_out *msout = NULL); + bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, bool bulletproof = false, rct::multisig_out *msout = NULL); bool generate_genesis_block( block& bl @@ -98,7 +101,7 @@ namespace cryptonote } -BOOST_CLASS_VERSION(cryptonote::tx_source_entry, 0) +BOOST_CLASS_VERSION(cryptonote::tx_source_entry, 1) BOOST_CLASS_VERSION(cryptonote::tx_destination_entry, 1) namespace boost @@ -115,6 +118,10 @@ namespace boost a & x.amount; a & x.rct; a & x.mask; + if (ver < 1) + return; + a & x.multisig_kLRki; + a & x.real_out_additional_tx_keys; } template <class Archive> |