diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ac4a2f9..1bf684f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -235,10 +235,6 @@ elseif (DATABASE STREQUAL "berkeleydb") message(STATUS "Using Berkeley DB as default DB type") add_definitions("-DDEFAULT_DB_TYPE=\"berkeley\"") -elseif (DATABASE STREQUAL "memory") - set(BLOCKCHAIN_DB DB_MEMORY) - message(STATUS "Using Serialised In Memory as default DB type") - add_definitions("-DDEFAULT_DB_TYPE=\"memory\"") else() die("Invalid database type: ${DATABASE}") endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dfa31aa7..70bb215d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -91,6 +91,7 @@ endfunction () add_subdirectory(common) add_subdirectory(crypto) +add_subdirectory(ringct) add_subdirectory(cryptonote_core) add_subdirectory(blockchain_db) add_subdirectory(mnemonics) diff --git a/src/blockchain_db/berkeleydb/db_bdb.cpp b/src/blockchain_db/berkeleydb/db_bdb.cpp index 4d759d15..4ec284e3 100644 --- a/src/blockchain_db/berkeleydb/db_bdb.cpp +++ b/src/blockchain_db/berkeleydb/db_bdb.cpp @@ -361,7 +361,7 @@ void BlockchainBDB::remove_transaction_data(const crypto::hash& tx_hash, const t throw1(DB_ERROR("Failed to add removal of tx outputs to db transaction")); } -void BlockchainBDB::add_output(const crypto::hash& tx_hash, const tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time) +void BlockchainBDB::add_output(const crypto::hash& tx_hash, const tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time, const rct::key *commitment) { LOG_PRINT_L3("BlockchainBDB::" << __func__); check_open(); diff --git a/src/blockchain_db/berkeleydb/db_bdb.h b/src/blockchain_db/berkeleydb/db_bdb.h index 6bc26ed3..f8e9440b 100644 --- a/src/blockchain_db/berkeleydb/db_bdb.h +++ b/src/blockchain_db/berkeleydb/db_bdb.h @@ -364,7 +364,7 @@ private: virtual void remove_transaction_data(const crypto::hash& tx_hash, const transaction& tx); - virtual void add_output(const crypto::hash& tx_hash, const tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time); + virtual void add_output(const crypto::hash& tx_hash, const tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time, const rct::key *commitment); virtual void remove_output(const tx_out& tx_output); diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp index 68f635d1..7eb81d93 100644 --- a/src/blockchain_db/blockchain_db.cpp +++ b/src/blockchain_db/blockchain_db.cpp @@ -46,6 +46,7 @@ void BlockchainDB::pop_block() void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transaction& tx, const crypto::hash* tx_hash_ptr) { + bool miner_tx = false; crypto::hash tx_hash; if (!tx_hash_ptr) { @@ -67,6 +68,7 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transacti else if (tx_input.type() == typeid(txin_gen)) { /* nothing to do here */ + miner_tx = true; } else { @@ -90,7 +92,21 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transacti // we need the index for (uint64_t i = 0; i < tx.vout.size(); ++i) { - amount_output_indices.push_back(add_output(tx_hash, tx.vout[i], i, tx.unlock_time)); + // miner v2 txes have their coinbase output in one single out to save space, + // and we store them as rct outputs with an identity mask + if (miner_tx && tx.version == 2) + { + cryptonote::tx_out vout = tx.vout[i]; + rct::key commitment = rct::zeroCommit(vout.amount); + vout.amount = 0; + amount_output_indices.push_back(add_output(tx_hash, vout, i, tx.unlock_time, + &commitment)); + } + else + { + amount_output_indices.push_back(add_output(tx_hash, tx.vout[i], i, tx.unlock_time, + tx.version > 1 ? &tx.rct_signatures.outPk[i].mask : NULL)); + } } add_tx_amount_output_indices(tx_id, amount_output_indices); } diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index 4c2157a3..d26080a3 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -110,6 +110,7 @@ struct output_data_t crypto::public_key pubkey; //!< the output's public key (for spend verification) uint64_t unlock_time; //!< the output's unlock time (or height) uint64_t height; //!< the height of the block which created the output + rct::key commitment; //!< the output's amount commitment (for spend verification) }; #pragma pack(pop) @@ -407,9 +408,10 @@ private: * @param tx_output the output * @param local_index index of the output in its transaction * @param unlock_time unlock time/height of the output + * @param commitment the rct commitment to the output amount * @return amount output index */ - virtual uint64_t add_output(const crypto::hash& tx_hash, const tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time) = 0; + virtual uint64_t add_output(const crypto::hash& tx_hash, const tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time, const rct::key *commitment) = 0; /** * @brief store amount output indices for a tx's outputs diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 0464ece5..a0a0a041 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -51,6 +51,13 @@ using epee::string_tools::pod_to_hex; namespace { +struct pre_rct_output_data_t +{ + crypto::public_key pubkey; //!< the output's public key (for spend verification) + uint64_t unlock_time; //!< the output's unlock time (or height) + uint64_t height; //!< the height of the block which created the output +}; + template inline void throw0(const T &e) { @@ -242,6 +249,12 @@ typedef struct txindex { tx_data_t data; } txindex; +typedef struct pre_rct_outkey { + uint64_t amount_index; + uint64_t output_id; + pre_rct_output_data_t data; +} pre_rct_outkey; + typedef struct outkey { uint64_t amount_index; uint64_t output_id; @@ -765,7 +778,8 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const uint64_t BlockchainLMDB::add_output(const crypto::hash& tx_hash, const tx_out& tx_output, const uint64_t& local_index, - const uint64_t unlock_time) + const uint64_t unlock_time, + const rct::key *commitment) { LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); @@ -778,6 +792,8 @@ uint64_t BlockchainLMDB::add_output(const crypto::hash& tx_hash, if (tx_output.target.type() != typeid(txout_to_key)) throw0(DB_ERROR("Wrong output type: expected txout_to_key")); + if (tx_output.amount == 0 && !commitment) + throw0(DB_ERROR("RCT output without commitment")); outtx ot = {m_num_outputs, tx_hash, local_index}; MDB_val_set(vot, ot); @@ -806,8 +822,16 @@ uint64_t BlockchainLMDB::add_output(const crypto::hash& tx_hash, ok.data.pubkey = boost::get < txout_to_key > (tx_output.target).key; ok.data.unlock_time = unlock_time; ok.data.height = m_height; + if (tx_output.amount == 0) + { + ok.data.commitment = *commitment; + data.mv_size = sizeof(ok); + } + else + { + data.mv_size = sizeof(pre_rct_outkey); + } data.mv_data = &ok; - data.mv_size = sizeof(ok); if ((result = mdb_cursor_put(m_cur_output_amounts, &val_amount, &data, MDB_APPENDDUP))) throw0(DB_ERROR(lmdb_error("Failed to add output pubkey to db transaction: ", result).c_str())); @@ -1234,6 +1258,7 @@ void BlockchainLMDB::reset() mdb_txn_safe txn; if (auto result = mdb_txn_begin(m_env, NULL, 0, txn)) throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str())); + if (auto result = mdb_drop(txn, m_blocks, 0)) throw0(DB_ERROR(lmdb_error("Failed to drop m_blocks: ", result).c_str())); if (auto result = mdb_drop(txn, m_block_info, 0)) @@ -1242,6 +1267,8 @@ void BlockchainLMDB::reset() throw0(DB_ERROR(lmdb_error("Failed to drop m_block_heights: ", result).c_str())); if (auto result = mdb_drop(txn, m_txs, 0)) throw0(DB_ERROR(lmdb_error("Failed to drop m_txs: ", result).c_str())); + if (auto result = mdb_drop(txn, m_tx_indices, 0)) + throw0(DB_ERROR(lmdb_error("Failed to drop m_tx_indices: ", result).c_str())); if (auto result = mdb_drop(txn, m_tx_outputs, 0)) throw0(DB_ERROR(lmdb_error("Failed to drop m_tx_outputs: ", result).c_str())); if (auto result = mdb_drop(txn, m_output_txs, 0)) @@ -1255,6 +1282,13 @@ void BlockchainLMDB::reset() throw0(DB_ERROR(lmdb_error("Failed to drop m_hf_versions: ", result).c_str())); if (auto result = mdb_drop(txn, m_properties, 0)) throw0(DB_ERROR(lmdb_error("Failed to drop m_properties: ", result).c_str())); + + // init with current version + MDB_val_copy k("version"); + MDB_val_copy v(VERSION); + if (auto result = mdb_put(txn, m_properties, &k, &v, 0)) + throw0(DB_ERROR(lmdb_error("Failed to write version to database: ", result).c_str())); + txn.commit(); m_height = 0; m_num_outputs = 0; @@ -1923,8 +1957,19 @@ output_data_t BlockchainLMDB::get_output_key(const uint64_t& amount, const uint6 throw1(OUTPUT_DNE("Attempting to get output pubkey by index, but key does not exist")); else if (get_result) throw0(DB_ERROR("Error attempting to retrieve an output pubkey from the db")); - outkey *okp = (outkey *)v.mv_data; - output_data_t ret = okp->data; + + output_data_t ret; + if (amount == 0) + { + const outkey *okp = (const outkey *)v.mv_data; + ret = okp->data; + } + else + { + const pre_rct_outkey *okp = (const pre_rct_outkey *)v.mv_data; + memcpy(&ret, &okp->data, sizeof(pre_rct_output_data_t));; + ret.commitment = rct::zeroCommit(amount); + } TXN_POSTFIX_RDONLY(); return ret; } @@ -2547,8 +2592,18 @@ void BlockchainLMDB::get_output_key(const uint64_t &amount, const std::vectordata; + output_data_t data; + if (amount == 0) + { + const outkey *okp = (const outkey *)v.mv_data; + data = okp->data; + } + else + { + const pre_rct_outkey *okp = (const pre_rct_outkey *)v.mv_data; + memcpy(&data, &okp->data, sizeof(pre_rct_output_data_t)); + data.commitment = rct::zeroCommit(amount); + } outputs.push_back(data); } diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index d60701bb..050e9e0a 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -30,6 +30,7 @@ #include "blockchain_db/blockchain_db.h" #include "cryptonote_protocol/blobdatatype.h" // for type blobdata +#include "ringct/rctTypes.h" #include #include @@ -292,7 +293,8 @@ private: virtual uint64_t add_output(const crypto::hash& tx_hash, const tx_out& tx_output, const uint64_t& local_index, - const uint64_t unlock_time + const uint64_t unlock_time, + const rct::key *commitment ); virtual void add_tx_amount_output_indices(const uint64_t tx_id, diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt index 96b30b83..d98c2ce8 100644 --- a/src/blockchain_utilities/CMakeLists.txt +++ b/src/blockchain_utilities/CMakeLists.txt @@ -26,15 +26,6 @@ # 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. -set(blockchain_converter_sources - blockchain_converter.cpp - ) - -set(blockchain_converter_private_headers) - -bitmonero_private_headers(blockchain_converter - ${blockchain_converter_private_headers}) - set(blockchain_import_sources blockchain_import.cpp bootstrap_file.cpp @@ -87,30 +78,6 @@ bitmonero_private_headers(cn_deserialize ${cn_deserialize_private_headers}) -if (BLOCKCHAIN_DB STREQUAL DB_LMDB) -bitmonero_add_executable(blockchain_converter - ${blockchain_converter_sources} - ${blockchain_converter_private_headers}) - -target_link_libraries(blockchain_converter - LINK_PRIVATE - cryptonote_core - p2p - blockchain_db - ${CMAKE_THREAD_LIBS_INIT}) - -if(ARCH_WIDTH) - target_compile_definitions(blockchain_converter - PUBLIC -DARCH_WIDTH=${ARCH_WIDTH}) -endif() - -add_dependencies(blockchain_converter - version) -set_property(TARGET blockchain_converter - PROPERTY - OUTPUT_NAME "blockchain_converter") -endif () - bitmonero_add_executable(blockchain_import ${blockchain_import_sources} ${blockchain_import_private_headers}) diff --git a/src/blockchain_utilities/README.md b/src/blockchain_utilities/README.md index 9c69647a..7df6937a 100644 --- a/src/blockchain_utilities/README.md +++ b/src/blockchain_utilities/README.md @@ -18,9 +18,10 @@ e.g. This is also the default compile setting on the master branch. -By default, the exporter will use the original in-memory database (blockchain.bin) as its source. -This default is to make migrating to an LMDB database easy, without having to recompile anything. -To change the source, adjust `SOURCE_DB` in `src/blockchain_utilities/bootstrap_file.h` according to the comments. +The exporter will use the LMDB database as its source. +If you are still using an old style in-memory database (blockchain.bin), you will +have to either resync from scratch, or use an older version of the tools to export +it and import it. ## Usage: diff --git a/src/blockchain_utilities/blockchain_converter.cpp b/src/blockchain_utilities/blockchain_converter.cpp deleted file mode 100644 index 7c33ec39..00000000 --- a/src/blockchain_utilities/blockchain_converter.cpp +++ /dev/null @@ -1,306 +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. - -#include "include_base_utils.h" -#include "common/util.h" -#include "warnings.h" -#include "crypto/crypto.h" -#include "cryptonote_config.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "misc_language.h" -#include "cryptonote_core/blockchain_storage.h" -#include "blockchain_db/blockchain_db.h" -#include "cryptonote_core/blockchain.h" -#include "blockchain_db/lmdb/db_lmdb.h" -#include "cryptonote_core/tx_pool.h" -#include "common/command_line.h" -#include "serialization/json_utils.h" -#include "include_base_utils.h" -#include "version.h" -#include - - -namespace -{ - -// CONFIG -bool opt_batch = true; -bool opt_resume = true; -bool opt_testnet = false; - -// number of blocks per batch transaction -// adjustable through command-line argument according to available RAM -#if ARCH_WIDTH != 32 -uint64_t db_batch_size_verify = 5000; -#else -// set a lower default batch size for Windows, pending possible LMDB issue with -// large batch size. -uint64_t db_batch_size_verify = 100; -#endif - -// converter only uses verify mode -uint64_t db_batch_size = db_batch_size_verify; - -} - -namespace po = boost::program_options; - -using namespace cryptonote; -using namespace epee; - -struct fake_core -{ - Blockchain dummy; - tx_memory_pool m_pool; - - blockchain_storage m_storage; - -#if !defined(BLOCKCHAIN_DB) - // for multi_db_runtime: - fake_core(const boost::filesystem::path &path, const bool use_testnet) : dummy(m_pool), m_pool(&dummy), m_storage(m_pool) -#else - // for multi_db_compile: - fake_core(const boost::filesystem::path &path, const bool use_testnet) : dummy(m_pool), m_pool(dummy), m_storage(&m_pool) -#endif - { - m_pool.init(path.string()); - m_storage.init(path.string(), use_testnet); - } -}; - -int main(int argc, char* argv[]) -{ - uint64_t height = 0; - uint64_t start_block = 0; - uint64_t end_block = 0; - uint64_t num_blocks = 0; - - boost::filesystem::path default_data_path {tools::get_default_data_dir()}; - boost::filesystem::path default_testnet_data_path {default_data_path / "testnet"}; - - - po::options_description desc_cmd_only("Command line options"); - po::options_description desc_cmd_sett("Command line options and settings options"); - const command_line::arg_descriptor arg_log_level = {"log-level", "", LOG_LEVEL_0}; - const command_line::arg_descriptor arg_batch_size = {"batch-size", "", db_batch_size}; - const command_line::arg_descriptor arg_testnet_on = { - "testnet" - , "Run on testnet." - , opt_testnet - }; - const command_line::arg_descriptor arg_block_number = - {"block-number", "Number of blocks (default: use entire source blockchain)", - 0}; - - command_line::add_arg(desc_cmd_sett, command_line::arg_data_dir, default_data_path.string()); - command_line::add_arg(desc_cmd_sett, command_line::arg_testnet_data_dir, default_testnet_data_path.string()); - command_line::add_arg(desc_cmd_sett, arg_log_level); - command_line::add_arg(desc_cmd_sett, arg_batch_size); - command_line::add_arg(desc_cmd_sett, arg_testnet_on); - command_line::add_arg(desc_cmd_sett, arg_block_number); - - command_line::add_arg(desc_cmd_only, command_line::arg_help); - - const command_line::arg_descriptor arg_batch = {"batch", - "Batch transactions for faster import", true}; - const command_line::arg_descriptor arg_resume = {"resume", - "Resume from current height if output database already exists", true}; - - // call add_options() directly for these arguments since command_line helpers - // support only boolean switch, not boolean argument - desc_cmd_sett.add_options() - (arg_batch.name, make_semantic(arg_batch), arg_batch.description) - (arg_resume.name, make_semantic(arg_resume), arg_resume.description) - ; - - po::options_description desc_options("Allowed options"); - desc_options.add(desc_cmd_only).add(desc_cmd_sett); - - - po::variables_map vm; - bool r = command_line::handle_error_helper(desc_options, [&]() - { - po::store(po::parse_command_line(argc, argv, desc_options), vm); - po::notify(vm); - - return true; - }); - if (!r) - return 1; - - int log_level = command_line::get_arg(vm, arg_log_level); - opt_batch = command_line::get_arg(vm, arg_batch); - opt_resume = command_line::get_arg(vm, arg_resume); - db_batch_size = command_line::get_arg(vm, arg_batch_size); - - if (command_line::get_arg(vm, command_line::arg_help)) - { - std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL; - std::cout << desc_options << std::endl; - return 1; - } - - if (! opt_batch && ! vm["batch-size"].defaulted()) - { - std::cerr << "Error: batch-size set, but batch option not enabled" << ENDL; - return 1; - } - if (! db_batch_size) - { - std::cerr << "Error: batch-size must be > 0" << ENDL; - return 1; - } - - log_space::get_set_log_detalisation_level(true, log_level); - log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL); - LOG_PRINT_L0("Starting..."); - - std::string src_folder; - opt_testnet = command_line::get_arg(vm, arg_testnet_on); - auto data_dir_arg = opt_testnet ? command_line::arg_testnet_data_dir : command_line::arg_data_dir; - src_folder = command_line::get_arg(vm, data_dir_arg); - boost::filesystem::path dest_folder(src_folder); - - num_blocks = command_line::get_arg(vm, arg_block_number); - - if (opt_batch) - { - LOG_PRINT_L0("batch: " << std::boolalpha << opt_batch << std::noboolalpha - << " batch size: " << db_batch_size); - } - else - { - LOG_PRINT_L0("batch: " << std::boolalpha << opt_batch << std::noboolalpha); - } - LOG_PRINT_L0("resume: " << std::boolalpha << opt_resume << std::noboolalpha); - LOG_PRINT_L0("testnet: " << std::boolalpha << opt_testnet << std::noboolalpha); - - fake_core c(src_folder, opt_testnet); - - height = c.m_storage.get_current_blockchain_height(); - - BlockchainDB *blockchain; - blockchain = new BlockchainLMDB(opt_batch); - dest_folder /= blockchain->get_db_name(); - LOG_PRINT_L0("Source blockchain: " << src_folder); - LOG_PRINT_L0("Dest blockchain: " << dest_folder.string()); - LOG_PRINT_L0("Opening dest blockchain (BlockchainDB " << blockchain->get_db_name() << ")"); - blockchain->open(dest_folder.string()); - LOG_PRINT_L0("Source blockchain height: " << height); - LOG_PRINT_L0("Dest blockchain height: " << blockchain->height()); - - if (opt_resume) - // next block number to add is same as current height - start_block = blockchain->height(); - - if (! num_blocks || (start_block + num_blocks > height)) - end_block = height - 1; - else - end_block = start_block + num_blocks - 1; - - LOG_PRINT_L0("start height: " << start_block+1 << " stop height: " << - end_block+1); - - if (start_block > end_block) - { - LOG_PRINT_L0("Finished: no blocks to add"); - delete blockchain; - return 0; - } - - if (opt_batch) - blockchain->batch_start(db_batch_size); - uint64_t i = 0; - for (i = start_block; i < end_block + 1; ++i) - { - // block: i height: i+1 end height: end_block + 1 - if ((i+1) % 10 == 0) - { - std::cout << "\r \r" << "height " << i+1 << "/" << - end_block+1 << " (" << (i+1)*100/(end_block+1)<< "%)" << std::flush; - } - // for debugging: - // std::cout << "height " << i+1 << "/" << end_block+1 - // << " ((" << i+1 << ")*100/(end_block+1))" << "%)" << ENDL; - - block b = c.m_storage.get_block(i); - size_t bsize = c.m_storage.get_block_size(i); - difficulty_type bdiff = c.m_storage.get_block_cumulative_difficulty(i); - uint64_t bcoins = c.m_storage.get_block_coins_generated(i); - std::vector txs; - std::vector missed; - - c.m_storage.get_transactions(b.tx_hashes, txs, missed); - if (missed.size()) - { - std::cout << ENDL; - std::cerr << "Missed transaction(s) for block at height " << i + 1 << ", exiting" << ENDL; - delete blockchain; - return 1; - } - - try - { - blockchain->add_block(b, bsize, bdiff, bcoins, txs); - - if (opt_batch) - { - if ((i < end_block) && ((i + 1) % db_batch_size == 0)) - { - std::cout << "\r \r"; - std::cout << "[- batch commit at height " << i + 1 << " -]" << ENDL; - blockchain->batch_stop(); - blockchain->batch_start(db_batch_size); - std::cout << ENDL; - blockchain->show_stats(); - } - } - } - catch (const std::exception& e) - { - std::cout << ENDL; - std::cerr << "Error adding block " << i << " to new blockchain: " << e.what() << ENDL; - delete blockchain; - return 2; - } - } - if (opt_batch) - { - std::cout << "\r \r" << "height " << i << "/" << - end_block+1 << " (" << (i)*100/(end_block+1)<< "%)" << std::flush; - std::cout << ENDL; - std::cout << "[- batch commit at height " << i << " -]" << ENDL; - blockchain->batch_stop(); - } - std::cout << ENDL; - blockchain->show_stats(); - std::cout << "Finished at height: " << i << " block: " << i-1 << ENDL; - - delete blockchain; - return 0; -} diff --git a/src/blockchain_utilities/blockchain_dump.cpp b/src/blockchain_utilities/blockchain_dump.cpp index 23619eae..626900d4 100644 --- a/src/blockchain_utilities/blockchain_dump.cpp +++ b/src/blockchain_utilities/blockchain_dump.cpp @@ -27,8 +27,8 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_core/blockchain_storage.h" #include "cryptonote_core/blockchain.h" +#include "cryptonote_core/tx_pool.h" #include "blockchain_db/blockchain_db.h" #include "blockchain_db/lmdb/db_lmdb.h" #ifdef BERKELEY_DB @@ -103,7 +103,6 @@ int main(int argc, char* argv[]) const command_line::arg_descriptor arg_output_file = {"output-file", "Specify output file", "", true}; const command_line::arg_descriptor arg_log_level = {"log-level", "", log_level}; const command_line::arg_descriptor arg_block_stop = {"block-stop", "Stop at block number", block_stop}; -#if SOURCE_DB != DB_MEMORY const command_line::arg_descriptor arg_db_type = { "db-type" , "Specify database type" @@ -114,7 +113,6 @@ int main(int argc, char* argv[]) , "Include data that is only in a database version." , false }; -#endif const command_line::arg_descriptor arg_testnet_on = { "testnet" , "Run on testnet." @@ -125,10 +123,8 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_cmd_sett, command_line::arg_data_dir, default_data_path.string()); command_line::add_arg(desc_cmd_sett, command_line::arg_testnet_data_dir, default_testnet_data_path.string()); command_line::add_arg(desc_cmd_sett, arg_output_file); -#if SOURCE_DB != DB_MEMORY command_line::add_arg(desc_cmd_sett, arg_db_type); command_line::add_arg(desc_cmd_sett, arg_include_db_only_data); -#endif command_line::add_arg(desc_cmd_sett, arg_testnet_on); command_line::add_arg(desc_cmd_sett, arg_log_level); command_line::add_arg(desc_cmd_sett, arg_block_stop); @@ -164,9 +160,7 @@ int main(int argc, char* argv[]) LOG_PRINT_L0("Setting log level = " << log_level); bool opt_testnet = command_line::get_arg(vm, arg_testnet_on); -#if SOURCE_DB != DB_MEMORY bool opt_include_db_only_data = command_line::get_arg(vm, arg_include_db_only_data); -#endif std::string m_config_folder; @@ -208,15 +202,6 @@ int main(int argc, char* argv[]) // If we wanted to use the memory pool, we would set up a fake_core. -#if SOURCE_DB == DB_MEMORY - // blockchain_storage* core_storage = NULL; - // tx_memory_pool m_mempool(*core_storage); // is this fake anyway? just passing in NULL! so m_mempool can't be used anyway, right? - // core_storage = new blockchain_storage(&m_mempool); - - blockchain_storage* core_storage = new blockchain_storage(NULL); - LOG_PRINT_L0("Initializing source blockchain (in-memory database)"); - r = core_storage->init(m_config_folder, opt_testnet); -#else // Use Blockchain instead of lower-level BlockchainDB for two reasons: // 1. Blockchain has the init() method for easy setup // 2. exporter needs to use get_current_blockchain_height(), get_block_id_by_height(), get_block_by_hash() @@ -267,7 +252,6 @@ int main(int argc, char* argv[]) return 1; } r = core_storage->init(db, opt_testnet); -#endif CHECK_AND_ASSERT_MES(r, false, "Failed to initialize source blockchain storage"); LOG_PRINT_L0("Source blockchain storage initialized OK"); @@ -340,7 +324,6 @@ int main(int argc, char* argv[]) write_pod(d,key_images[n]); } end_compound(d); -#if SOURCE_DB != DB_MEMORY if (opt_include_db_only_data) { start_struct(d, "block_timestamps", true); @@ -420,7 +403,6 @@ int main(int argc, char* argv[]) write_pod(d, boost::lexical_cast(h), (unsigned int)db->get_hard_fork_version(h)); end_compound(d); } -#endif end_compound(d); CHECK_AND_ASSERT_MES(r, false, "Failed to dump blockchain"); diff --git a/src/blockchain_utilities/blockchain_export.cpp b/src/blockchain_utilities/blockchain_export.cpp index 964c610c..231bea33 100644 --- a/src/blockchain_utilities/blockchain_export.cpp +++ b/src/blockchain_utilities/blockchain_export.cpp @@ -29,6 +29,7 @@ #include "bootstrap_file.h" #include "blocksdat_file.h" #include "common/command_line.h" +#include "cryptonote_core/tx_pool.h" #include "blockchain_db/blockchain_db.h" #include "blockchain_db/lmdb/db_lmdb.h" #if defined(BERKELEY_DB) @@ -53,11 +54,7 @@ std::string join_set_strings(const std::unordered_set& db_types_all int main(int argc, char* argv[]) { -#if defined(BLOCKCHAIN_DB) && (BLOCKCHAIN_DB == DB_MEMORY) - std::string default_db_type = "memory"; -#else std::string default_db_type = "lmdb"; -#endif std::unordered_set db_types_all = cryptonote::blockchain_db_types; db_types_all.insert("memory"); @@ -160,15 +157,6 @@ int main(int argc, char* argv[]) // If we wanted to use the memory pool, we would set up a fake_core. -#if SOURCE_DB == DB_MEMORY - // blockchain_storage* core_storage = NULL; - // tx_memory_pool m_mempool(*core_storage); // is this fake anyway? just passing in NULL! so m_mempool can't be used anyway, right? - // core_storage = new blockchain_storage(&m_mempool); - - blockchain_storage* core_storage = new blockchain_storage(NULL); - LOG_PRINT_L0("Initializing source blockchain (in-memory database)"); - r = core_storage->init(m_config_folder, opt_testnet); -#else // Use Blockchain instead of lower-level BlockchainDB for two reasons: // 1. Blockchain has the init() method for easy setup // 2. exporter needs to use get_current_blockchain_height(), get_block_id_by_height(), get_block_by_hash() @@ -217,7 +205,6 @@ int main(int argc, char* argv[]) return 1; } r = core_storage->init(db, opt_testnet); -#endif CHECK_AND_ASSERT_MES(r, false, "Failed to initialize source blockchain storage"); LOG_PRINT_L0("Source blockchain storage initialized OK"); diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp index 1aaf2bdd..27c9050f 100644 --- a/src/blockchain_utilities/blockchain_import.cpp +++ b/src/blockchain_utilities/blockchain_import.cpp @@ -222,12 +222,8 @@ int pop_blocks(FakeCore& simple_core, int num_blocks) std::vector popped_txs; for (int i=0; i < num_blocks; ++i) { -#if defined(BLOCKCHAIN_DB) && (BLOCKCHAIN_DB == DB_MEMORY) - simple_core.m_storage.debug_pop_block_from_blockchain(); -#else // simple_core.m_storage.pop_block_from_blockchain() is private, so call directly through db simple_core.m_storage.get_db().pop_block(popped_block, popped_txs); -#endif quit = 1; } @@ -244,9 +240,7 @@ int pop_blocks(FakeCore& simple_core, int num_blocks) { simple_core.batch_stop(); } -#if !defined(BLOCKCHAIN_DB) || (BLOCKCHAIN_DB == DB_LMDB) simple_core.m_storage.get_db().show_stats(); -#endif } return num_blocks; @@ -255,11 +249,6 @@ int pop_blocks(FakeCore& simple_core, int num_blocks) template int import_from_file(FakeCore& simple_core, const std::string& import_file_path, uint64_t block_stop=0) { -#if !defined(BLOCKCHAIN_DB) - static_assert(std::is_same::value || std::is_same::value, - "FakeCore constraint error"); -#endif -#if !defined(BLOCKCHAIN_DB) || (BLOCKCHAIN_DB == DB_LMDB) if (std::is_same::value) { // Reset stats, in case we're using newly created db, accumulating stats @@ -267,7 +256,6 @@ int import_from_file(FakeCore& simple_core, const std::string& import_file_path, // This aligns internal db counts with importer counts. simple_core.m_storage.get_db().reset_stats(); } -#endif boost::filesystem::path fs_import_file_path(import_file_path); boost::system::error_code ec; if (!boost::filesystem::exists(fs_import_file_path, ec)) @@ -567,9 +555,7 @@ int import_from_file(FakeCore& simple_core, const std::string& import_file_path, simple_core.batch_stop(); simple_core.batch_start(db_batch_size); std::cout << ENDL; -#if !defined(BLOCKCHAIN_DB) || (BLOCKCHAIN_DB == DB_LMDB) simple_core.m_storage.get_db().show_stats(); -#endif } } } @@ -595,9 +581,7 @@ int import_from_file(FakeCore& simple_core, const std::string& import_file_path, { simple_core.batch_stop(); } -#if !defined(BLOCKCHAIN_DB) || (BLOCKCHAIN_DB == DB_LMDB) simple_core.m_storage.get_db().show_stats(); -#endif LOG_PRINT_L0("Number of blocks imported: " << num_imported); if (h > 0) // TODO: if there was an error, the last added block is probably at zero-based height h-2 @@ -609,13 +593,8 @@ int import_from_file(FakeCore& simple_core, const std::string& import_file_path, int main(int argc, char* argv[]) { -#if defined(BLOCKCHAIN_DB) && (BLOCKCHAIN_DB == DB_MEMORY) - std::string default_db_type = "memory"; - std::string default_db_engine_compiled = "memory"; -#else std::string default_db_type = "lmdb"; std::string default_db_engine_compiled = "blockchain_db"; -#endif std::unordered_set db_types_all = cryptonote::blockchain_db_types; db_types_all.insert("memory"); @@ -817,17 +796,11 @@ int main(int argc, char* argv[]) // circumstance. // for multi_db_runtime: -#if !defined(BLOCKCHAIN_DB) if (db_type == "lmdb" || db_type == "berkeley") { fake_core_db simple_core(m_config_folder, opt_testnet, opt_batch, db_type, db_flags); import_from_file(simple_core, import_file_path, block_stop); } - else if (db_type == "memory") - { - fake_core_memory simple_core(m_config_folder, opt_testnet); - import_from_file(simple_core, import_file_path, block_stop); - } else { std::cerr << "database type unrecognized" << ENDL; @@ -835,17 +808,7 @@ int main(int argc, char* argv[]) } // for multi_db_compile: -#else - if (db_engine_compiled != default_db_engine_compiled) - { - std::cerr << "Invalid database type for compiled version: " << db_type << std::endl; - return 1; - } -#if BLOCKCHAIN_DB == DB_LMDB fake_core_db simple_core(m_config_folder, opt_testnet, opt_batch, db_type, db_flags); -#else - fake_core_memory simple_core(m_config_folder, opt_testnet); -#endif if (! vm["pop-blocks"].defaulted()) { @@ -856,17 +819,14 @@ int main(int argc, char* argv[]) return 0; } -#if !defined(BLOCKCHAIN_DB) || (BLOCKCHAIN_DB == DB_LMDB) if (! vm["drop-hard-fork"].defaulted()) { LOG_PRINT_L0("Dropping hard fork tables..."); simple_core.m_storage.get_db().drop_hard_fork_info(); return 0; } -#endif import_from_file(simple_core, import_file_path, block_stop); -#endif } catch (const DB_ERROR& e) diff --git a/src/blockchain_utilities/blockchain_utilities.h b/src/blockchain_utilities/blockchain_utilities.h index 51c17a9b..6ddda65e 100644 --- a/src/blockchain_utilities/blockchain_utilities.h +++ b/src/blockchain_utilities/blockchain_utilities.h @@ -31,21 +31,6 @@ #include "version.h" -// CONFIG: choose one of the three #define's -// -// DB_MEMORY was a sensible default for users migrating to LMDB, as it allowed -// the exporter to use the in-memory blockchain while the other binaries -// work with LMDB, without recompiling anything. -// -// Now that the majority of users are on 0.9.2, and the converter has largely -// become a generalised database tool, the default has changed to DB_LMDB. -// -// #define SOURCE_DB DB_MEMORY -#define SOURCE_DB DB_LMDB -// to use global compile-time setting (DB_MEMORY or DB_LMDB): -// #define SOURCE_DB BLOCKCHAIN_DB - - // bounds checking is done before writing to buffer, but buffer size // should be a sensible maximum #define BUFFER_SIZE 1000000 diff --git a/src/blockchain_utilities/blocksdat_file.cpp b/src/blockchain_utilities/blocksdat_file.cpp index 81b56d55..926562ba 100644 --- a/src/blockchain_utilities/blocksdat_file.cpp +++ b/src/blockchain_utilities/blocksdat_file.cpp @@ -114,11 +114,7 @@ bool BlocksdatFile::close() } -#if SOURCE_DB == DB_MEMORY -bool BlocksdatFile::store_blockchain_raw(blockchain_storage* _blockchain_storage, tx_memory_pool* _tx_pool, boost::filesystem::path& output_file, uint64_t requested_block_stop) -#else bool BlocksdatFile::store_blockchain_raw(Blockchain* _blockchain_storage, tx_memory_pool* _tx_pool, boost::filesystem::path& output_file, uint64_t requested_block_stop) -#endif { uint64_t num_blocks_written = 0; m_blockchain_storage = _blockchain_storage; diff --git a/src/blockchain_utilities/blocksdat_file.h b/src/blockchain_utilities/blocksdat_file.h index 92bae9fc..ed821cd6 100644 --- a/src/blockchain_utilities/blocksdat_file.h +++ b/src/blockchain_utilities/blocksdat_file.h @@ -36,7 +36,6 @@ #include "cryptonote_core/cryptonote_basic.h" #include "cryptonote_core/cryptonote_boost_serialization.h" -#include "cryptonote_core/blockchain_storage.h" #include "cryptonote_core/blockchain.h" #include "blockchain_db/blockchain_db.h" #include "blockchain_db/lmdb/db_lmdb.h" @@ -60,21 +59,12 @@ class BlocksdatFile { public: -#if SOURCE_DB == DB_MEMORY - bool store_blockchain_raw(cryptonote::blockchain_storage* cs, cryptonote::tx_memory_pool* txp, - boost::filesystem::path& output_file, uint64_t use_block_height=0); -#else bool store_blockchain_raw(cryptonote::Blockchain* cs, cryptonote::tx_memory_pool* txp, boost::filesystem::path& output_file, uint64_t use_block_height=0); -#endif protected: -#if SOURCE_DB == DB_MEMORY - blockchain_storage* m_blockchain_storage; -#else Blockchain* m_blockchain_storage; -#endif std::ofstream * m_raw_data_file; diff --git a/src/blockchain_utilities/bootstrap_file.cpp b/src/blockchain_utilities/bootstrap_file.cpp index da3b4459..61bd35a6 100644 --- a/src/blockchain_utilities/bootstrap_file.cpp +++ b/src/blockchain_utilities/bootstrap_file.cpp @@ -223,31 +223,9 @@ void BootstrapFile::write_block(block& block) { throw std::runtime_error("Aborting: tx == null_hash"); } -#if SOURCE_DB == DB_MEMORY - const transaction* tx = m_blockchain_storage->get_tx(tx_id); -#else transaction tx = m_blockchain_storage->get_db().get_tx(tx_id); -#endif -#if SOURCE_DB == DB_MEMORY - if(tx == NULL) - { - if (! m_tx_pool) - throw std::runtime_error("Aborting: tx == NULL, so memory pool required to get tx, but memory pool isn't enabled"); - else - { - transaction tx; - if(m_tx_pool->get_transaction(tx_id, tx)) - txs.push_back(tx); - else - throw std::runtime_error("Aborting: tx not found in pool"); - } - } - else - txs.push_back(*tx); -#else txs.push_back(tx); -#endif } // these non-coinbase txs will be serialized using this structure @@ -257,15 +235,9 @@ void BootstrapFile::write_block(block& block) bool include_extra_block_data = true; if (include_extra_block_data) { -#if SOURCE_DB == DB_MEMORY - size_t block_size = m_blockchain_storage->get_block_size(block_height); - difficulty_type cumulative_difficulty = m_blockchain_storage->get_block_cumulative_difficulty(block_height); - uint64_t coins_generated = m_blockchain_storage->get_block_coins_generated(block_height); -#else size_t block_size = m_blockchain_storage->get_db().get_block_size(block_height); difficulty_type cumulative_difficulty = m_blockchain_storage->get_db().get_block_cumulative_difficulty(block_height); uint64_t coins_generated = m_blockchain_storage->get_db().get_block_already_generated_coins(block_height); -#endif bp.block_size = block_size; bp.cumulative_difficulty = cumulative_difficulty; @@ -288,11 +260,7 @@ bool BootstrapFile::close() } -#if SOURCE_DB == DB_MEMORY -bool BootstrapFile::store_blockchain_raw(blockchain_storage* _blockchain_storage, tx_memory_pool* _tx_pool, boost::filesystem::path& output_file, uint64_t requested_block_stop) -#else bool BootstrapFile::store_blockchain_raw(Blockchain* _blockchain_storage, tx_memory_pool* _tx_pool, boost::filesystem::path& output_file, uint64_t requested_block_stop) -#endif { uint64_t num_blocks_written = 0; m_max_chunk = 0; diff --git a/src/blockchain_utilities/bootstrap_file.h b/src/blockchain_utilities/bootstrap_file.h index dd134f7a..0bbe28f1 100644 --- a/src/blockchain_utilities/bootstrap_file.h +++ b/src/blockchain_utilities/bootstrap_file.h @@ -35,7 +35,6 @@ #include #include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_core/blockchain_storage.h" #include "cryptonote_core/blockchain.h" #include @@ -60,21 +59,12 @@ public: uint64_t count_blocks(const std::string& dir_path); uint64_t seek_to_first_chunk(std::ifstream& import_file); -#if SOURCE_DB == DB_MEMORY - bool store_blockchain_raw(cryptonote::blockchain_storage* cs, cryptonote::tx_memory_pool* txp, - boost::filesystem::path& output_file, uint64_t use_block_height=0); -#else bool store_blockchain_raw(cryptonote::Blockchain* cs, cryptonote::tx_memory_pool* txp, boost::filesystem::path& output_file, uint64_t use_block_height=0); -#endif protected: -#if SOURCE_DB == DB_MEMORY - blockchain_storage* m_blockchain_storage; -#else Blockchain* m_blockchain_storage; -#endif tx_memory_pool* m_tx_pool; typedef std::vector buffer_type; diff --git a/src/blockchain_utilities/fake_core.h b/src/blockchain_utilities/fake_core.h index 2fb5031d..ba1c2ed7 100644 --- a/src/blockchain_utilities/fake_core.h +++ b/src/blockchain_utilities/fake_core.h @@ -30,7 +30,7 @@ #include #include "cryptonote_core/blockchain.h" // BlockchainDB -#include "cryptonote_core/blockchain_storage.h" // in-memory DB +#include "cryptonote_core/tx_pool.h" #include "blockchain_db/blockchain_db.h" #include "blockchain_db/lmdb/db_lmdb.h" #if defined(BERKELEY_DB) @@ -48,8 +48,6 @@ namespace } -#if !defined(BLOCKCHAIN_DB) || BLOCKCHAIN_DB == DB_LMDB - struct fake_core_db { Blockchain m_storage; @@ -59,12 +57,7 @@ struct fake_core_db bool support_add_block; // for multi_db_runtime: -#if !defined(BLOCKCHAIN_DB) - fake_core_db(const boost::filesystem::path &path, const bool use_testnet=false, const bool do_batch=true, const std::string& db_type="lmdb", const int db_flags=0) : m_pool(&m_storage), m_storage(m_pool) - // for multi_db_compile: -#else fake_core_db(const boost::filesystem::path &path, const bool use_testnet=false, const bool do_batch=true, const std::string& db_type="lmdb", const int db_flags=0) : m_pool(m_storage), m_storage(m_pool) -#endif { m_pool.init(path.string()); @@ -137,59 +130,4 @@ struct fake_core_db } }; -#endif -#if !defined(BLOCKCHAIN_DB) || BLOCKCHAIN_DB == DB_MEMORY - -struct fake_core_memory -{ - blockchain_storage m_storage; - tx_memory_pool m_pool; - bool support_batch; - bool support_add_block; - - // for multi_db_runtime: -#if !defined(BLOCKCHAIN_DB) - fake_core_memory(const boost::filesystem::path &path, const bool use_testnet=false) : m_pool(&m_storage), m_storage(m_pool) -#else - // for multi_db_compile: - fake_core_memory(const boost::filesystem::path &path, const bool use_testnet=false) : m_pool(m_storage), m_storage(&m_pool) -#endif - { - m_pool.init(path.string()); - m_storage.init(path.string(), use_testnet); - support_batch = false; - support_add_block = false; - } - ~fake_core_memory() - { - LOG_PRINT_L3("fake_core_memory() destructor called - want to see it ripple down"); - m_storage.deinit(); - } - - uint64_t add_block(const block& blk - , const size_t& block_size - , const difficulty_type& cumulative_difficulty - , const uint64_t& coins_generated - , const std::vector& txs - ) - { - // TODO: - // would need to refactor handle_block_to_main_chain() to have a direct add_block() method like Blockchain class - throw std::runtime_error("direct add_block() method not implemented for in-memory db"); - return 2; - } - - void batch_start(uint64_t batch_num_blocks = 0) - { - LOG_PRINT_L0("WARNING: [batch_start] opt_batch set, but this database doesn't support/need transactions - ignoring"); - } - - void batch_stop() - { - LOG_PRINT_L0("WARNING: [batch_stop] opt_batch set, but this database doesn't support/need transactions - ignoring"); - } - -}; - -#endif diff --git a/src/common/json_util.h b/src/common/json_util.h index 6f8b4c18..45046a4f 100644 --- a/src/common/json_util.h +++ b/src/common/json_util.h @@ -28,8 +28,8 @@ #pragma once -#define GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, name, type, jtype, mandatory) \ - type field_##name; \ +#define GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, name, type, jtype, mandatory, def) \ + type field_##name = def; \ bool field_##name##_found = false; \ (void)field_##name##_found; \ do if (json.HasMember(#name)) \ diff --git a/src/crypto/crypto-ops.c b/src/crypto/crypto-ops.c index a9b659a6..1b390e40 100644 --- a/src/crypto/crypto-ops.c +++ b/src/crypto/crypto-ops.c @@ -40,17 +40,15 @@ DISABLE_VS_WARNINGS(4146 4244) static void fe_mul(fe, const fe, const fe); static void fe_sq(fe, const fe); -static void fe_tobytes(unsigned char *, const fe); static void ge_madd(ge_p1p1 *, const ge_p3 *, const ge_precomp *); static void ge_msub(ge_p1p1 *, const ge_p3 *, const ge_precomp *); static void ge_p2_0(ge_p2 *); static void ge_p3_dbl(ge_p1p1 *, const ge_p3 *); -static void ge_sub(ge_p1p1 *, const ge_p3 *, const ge_cached *); static void fe_divpowm1(fe, const fe, const fe); /* Common functions */ -static uint64_t load_3(const unsigned char *in) { +uint64_t load_3(const unsigned char *in) { uint64_t result; result = (uint64_t) in[0]; result |= ((uint64_t) in[1]) << 8; @@ -58,7 +56,7 @@ static uint64_t load_3(const unsigned char *in) { return result; } -static uint64_t load_4(const unsigned char *in) +uint64_t load_4(const unsigned char *in) { uint64_t result; result = (uint64_t) in[0]; @@ -120,7 +118,7 @@ Postconditions: |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. */ -static void fe_add(fe h, const fe f, const fe g) { +void fe_add(fe h, const fe f, const fe g) { int32_t f0 = f[0]; int32_t f1 = f[1]; int32_t f2 = f[2]; @@ -258,7 +256,7 @@ static void fe_copy(fe h, const fe f) { /* From fe_invert.c */ -static void fe_invert(fe out, const fe z) { +void fe_invert(fe out, const fe z) { fe t0; fe t1; fe t2; @@ -1031,7 +1029,7 @@ Proof: so floor(2^(-255)(h + 19 2^(-25) h9 + 2^(-1))) = q. */ -static void fe_tobytes(unsigned char *s, const fe h) { +void fe_tobytes(unsigned char *s, const fe h) { int32_t h0 = h[0]; int32_t h1 = h[1]; int32_t h2 = h[2]; @@ -1591,7 +1589,7 @@ void ge_scalarmult_base(ge_p3 *h, const unsigned char *a) { r = p - q */ -static void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) { +void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) { fe t0; fe_add(r->X, p->Y, p->X); fe_sub(r->Y, p->Y, p->X); diff --git a/src/crypto/crypto-ops.h b/src/crypto/crypto-ops.h index cdc5ac1e..4986499f 100644 --- a/src/crypto/crypto-ops.h +++ b/src/crypto/crypto-ops.h @@ -143,3 +143,11 @@ void sc_sub(unsigned char *, const unsigned char *, const unsigned char *); void sc_mulsub(unsigned char *, const unsigned char *, const unsigned char *, const unsigned char *); int sc_check(const unsigned char *); int sc_isnonzero(const unsigned char *); /* Doesn't normalize */ + +// internal +uint64_t load_3(const unsigned char *in); +uint64_t load_4(const unsigned char *in); +void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q); +void fe_add(fe h, const fe f, const fe g); +void fe_tobytes(unsigned char *, const fe); +void fe_invert(fe out, const fe z); diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp index f5e65527..250779ac 100644 --- a/src/crypto/crypto.cpp +++ b/src/crypto/crypto.cpp @@ -151,7 +151,7 @@ namespace crypto { return true; } - static void derivation_to_scalar(const key_derivation &derivation, size_t output_index, ec_scalar &res) { + void crypto_ops::derivation_to_scalar(const key_derivation &derivation, size_t output_index, ec_scalar &res) { struct { key_derivation derivation; char output_index[(sizeof(size_t) * 8 + 6) / 7]; @@ -230,7 +230,7 @@ namespace crypto { buf.h = prefix_hash; buf.key = pub; if (ge_frombytes_vartime(&tmp3, &pub) != 0) { - abort(); + return false; } if (sc_check(&sig.c) != 0 || sc_check(&sig.r) != 0) { return false; @@ -364,7 +364,7 @@ POP_WARNINGS return false; } if (ge_frombytes_vartime(&tmp3, &*pubs[i]) != 0) { - abort(); + return false; } ge_double_scalarmult_base_vartime(&tmp2, &sig[i].c, &tmp3, &sig[i].r); ge_tobytes(&buf->ab[i].a, &tmp2); diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index fa55c2aa..b396fc7d 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -64,6 +64,22 @@ namespace crypto { friend class crypto_ops; }; + POD_CLASS public_keyV { + std::vector keys; + int rows; + }; + + POD_CLASS secret_keyV { + std::vector keys; + int rows; + }; + + POD_CLASS public_keyM { + int cols; + int rows; + std::vector column_vectors; + }; + POD_CLASS key_derivation: ec_point { friend class crypto_ops; }; @@ -97,6 +113,8 @@ namespace crypto { friend bool secret_key_to_public_key(const secret_key &, public_key &); static bool generate_key_derivation(const public_key &, const secret_key &, key_derivation &); friend bool generate_key_derivation(const public_key &, const secret_key &, key_derivation &); + static void derivation_to_scalar(const key_derivation &derivation, size_t output_index, ec_scalar &res); + friend void derivation_to_scalar(const key_derivation &derivation, size_t output_index, ec_scalar &res); static bool derive_public_key(const key_derivation &, std::size_t, const public_key &, public_key &); friend bool derive_public_key(const key_derivation &, std::size_t, const public_key &, public_key &); static void derive_secret_key(const key_derivation &, std::size_t, const secret_key &, secret_key &); @@ -165,6 +183,9 @@ namespace crypto { const public_key &base, public_key &derived_key) { return crypto_ops::derive_public_key(derivation, output_index, base, derived_key); } + inline void derivation_to_scalar(const key_derivation &derivation, size_t output_index, ec_scalar &res) { + return crypto_ops::derivation_to_scalar(derivation, output_index, res); + } inline void derive_secret_key(const key_derivation &derivation, std::size_t output_index, const secret_key &base, secret_key &derived_key) { crypto_ops::derive_secret_key(derivation, output_index, base, derived_key); diff --git a/src/crypto/keccak.c b/src/crypto/keccak.c index 3ee2a887..090d563a 100644 --- a/src/crypto/keccak.c +++ b/src/crypto/keccak.c @@ -73,11 +73,11 @@ void keccakf(uint64_t st[25], int rounds) // compute a keccak hash (md) of given byte length from "in" typedef uint64_t state_t[25]; -int keccak(const uint8_t *in, int inlen, uint8_t *md, int mdlen) +int keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen) { state_t st; uint8_t temp[144]; - int i, rsiz, rsizw; + size_t i, rsiz, rsizw; rsiz = sizeof(state_t) == mdlen ? HASH_DATA_AREA : 200 - 2 * mdlen; rsizw = rsiz / 8; @@ -106,7 +106,7 @@ int keccak(const uint8_t *in, int inlen, uint8_t *md, int mdlen) return 0; } -void keccak1600(const uint8_t *in, int inlen, uint8_t *md) +void keccak1600(const uint8_t *in, size_t inlen, uint8_t *md) { keccak(in, inlen, md, sizeof(state_t)); } diff --git a/src/crypto/keccak.h b/src/crypto/keccak.h index 4f7f8572..fbd8e190 100644 --- a/src/crypto/keccak.h +++ b/src/crypto/keccak.h @@ -16,11 +16,11 @@ #endif // compute a keccak hash (md) of given byte length from "in" -int keccak(const uint8_t *in, int inlen, uint8_t *md, int mdlen); +int keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen); // update the state void keccakf(uint64_t st[25], int norounds); -void keccak1600(const uint8_t *in, int inlen, uint8_t *md); +void keccak1600(const uint8_t *in, size_t inlen, uint8_t *md); #endif diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 1a093b50..50f61e34 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -41,7 +41,7 @@ #define CRYPTONOTE_MAX_TX_SIZE 1000000000 #define CRYPTONOTE_PUBLIC_ADDRESS_TEXTBLOB_VER 0 #define CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW 60 -#define CURRENT_TRANSACTION_VERSION 1 +#define CURRENT_TRANSACTION_VERSION 2 #define CURRENT_BLOCK_MAJOR_VERSION 1 #define CURRENT_BLOCK_MINOR_VERSION 0 #define CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT 60*60*2 diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt index 20535679..4ebfd01e 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 @@ -80,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} diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index cc6b48b6..ef72dac5 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 @@ -98,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; @@ -127,7 +132,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__); @@ -207,7 +212,7 @@ bool Blockchain::scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, vi output_index = m_db->get_output_key(tx_in_to_key.amount, i); // 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, output_index.commitment)) { LOG_PRINT_L0("Failed to handle_output for output no = " << count << ", with absolute offset " << i); return false; @@ -958,7 +963,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"); @@ -969,7 +974,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; @@ -1086,14 +1091,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) @@ -1117,7 +1132,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) @@ -1126,7 +1143,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); @@ -1597,6 +1614,139 @@ 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, 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(amount, i); + oen.out_key = data.pubkey; + oen.commitment = data.commitment; +} +//------------------------------------------------------------------ +// 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, 0, 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, 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; +} +//------------------------------------------------------------------ bool Blockchain::get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res) const { LOG_PRINT_L3("Blockchain::" << __func__); @@ -1607,11 +1757,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; } @@ -2012,7 +2162,7 @@ bool Blockchain::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vectorget_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; + } } } } @@ -2076,13 +2241,83 @@ bool Blockchain::have_tx_keyimges_as_spent(const transaction &tx) const } return false; } +bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_prefix_hash, const 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.p.MGs.resize(1); + rv.p.MGs[0].II.resize(tx.vin.size()); + for (size_t n = 0; n < tx.vin.size(); ++n) + 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.p.MGs.size() == tx.vin.size(), false, "Bad MGs size"); + for (size_t n = 0; n < tx.vin.size(); ++n) + { + rv.p.MGs[n].II.resize(1); + rv.p.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; @@ -2091,30 +2326,44 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context 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(); + 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 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 <= min_mixin) + ++n_unmixable; + else + ++n_mixable; + } if (in_to_key.key_offsets.size() - 1 < mixin) mixin = in_to_key.key_offsets.size() - 1; } } - if (mixin < 2) + + if (mixin < min_mixin) { if (n_unmixable == 0) { @@ -2129,6 +2378,22 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context 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); @@ -2140,7 +2405,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); @@ -2189,28 +2454,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); @@ -2222,28 +2490,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() - { - LOG_PRINT_L1("*pmax_used_block_height: " << *pmax_used_block_height); - } - - return false; + // 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]) + { + 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() + { + 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++; @@ -2251,30 +2522,170 @@ 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 + { + if (!expand_transaction_2(tx, tx_prefix_hash, pubkeys)) + { + LOG_PRINT_L1("Failed to expand rct signatures!"); + return false; } - if (failed) + // 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... + const rct::rctSig &rv = tx.rct_signatures; + switch (rv.type) { - LOG_PRINT_L1("Failed to check ring signatures!, t_loop: " << t_t1); + 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 + { + 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() != rv.mixRing[i].size()) + { + 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(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(rv.mixRing[n][m].mask)) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched commitment at vin " << n << ", index " << m); + return false; + } + } + } + } + + 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.p.MGs[n].II[0], 32)) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched key image"); + return false; + } + } + + if (!rct::verRctSimple(rv)) + { + LOG_PRINT_L1("Failed to check ringct signatures!"); + return false; + } + break; + } + case rct::RCTTypeFull: { + // 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() == 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"); + 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(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(rv.mixRing[m][n].mask)) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched commitment at vin " << n << ", index " << m); + return false; + } + } + } + } + + 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.p.MGs[0].II[n], 32)) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched II/vin sizes"); + return false; + } + } + + if (!rct::verRct(rv)) + { + LOG_PRINT_L1("Failed to check ringct signatures!"); + return false; + } + break; + } + default: + LOG_PRINT_L1("Unsupported rct type: " << rv.type); 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) { @@ -2285,7 +2696,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; @@ -2321,7 +2733,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__); @@ -2331,13 +2743,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)) @@ -2351,7 +2763,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; } }; @@ -2360,7 +2772,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; @@ -2371,7 +2783,10 @@ 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()); + } + // rct_signatures will be expanded after this return true; } //------------------------------------------------------------------ @@ -2453,6 +2868,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) { @@ -2461,7 +2878,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/blockchain.h b/src/cryptonote_core/blockchain.h index 1524693b..94701608 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 * @@ -483,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 @@ -492,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 @@ -867,11 +885,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 @@ -883,15 +902,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 @@ -899,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 @@ -912,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 @@ -1053,6 +1075,15 @@ 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 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, uint64_t amount, size_t i) const; + /** * @brief checks if a transaction is unlocked (its outputs spendable) * @@ -1172,7 +1203,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 @@ -1182,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/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp deleted file mode 100644 index a829b7cb..00000000 --- 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 878202cf..00000000 --- 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 ed62e26f..f54b8c2b 100644 --- a/src/cryptonote_core/cryptonote_basic.h +++ b/src/cryptonote_core/cryptonote_basic.h @@ -49,9 +49,7 @@ #include "crypto/hash.h" #include "misc_language.h" #include "tx_extra.h" - -#define DB_MEMORY 1 -#define DB_LMDB 2 +#include "ringct/rctTypes.h" namespace cryptonote { @@ -175,14 +173,14 @@ 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) FIELD(extra) END_SERIALIZE() - protected: + public: transaction_prefix(){} }; @@ -190,6 +188,7 @@ namespace cryptonote { public: std::vector > signatures; //count signatures always the same as inputs count + rct::rctSig rct_signatures; transaction(); virtual ~transaction(); @@ -198,34 +197,59 @@ 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) - { - if (0 == signature_size) - continue; - else - return false; - } - - PREPARE_CUSTOM_VECTOR_SERIALIZATION(signature_size, signatures[i]); - if (signature_size != signatures[i].size()) + 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; - FIELDS(signatures[i]); + for (size_t i = 0; i < vin.size(); ++i) + { + size_t signature_size = get_signature_size(vin[i]); + if (signatures_not_expected) + { + if (0 == signature_size) + continue; + else + return false; + } - if (vin.size() - i > 1) - ar.delimit_array(); + PREPARE_CUSTOM_VECTOR_SERIALIZATION(signature_size, signatures[i]); + if (signature_size != signatures[i].size()) + return false; + + FIELDS(signatures[i]); + + if (vin.size() - i > 1) + ar.delimit_array(); + } + ar.end_array(); + } + else + { + 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; + 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; + } } - ar.end_array(); END_SERIALIZE() private: @@ -248,12 +272,13 @@ namespace cryptonote inline void transaction::set_null() { - version = 0; + version = 1; unlock_time = 0; vin.clear(); 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 79ceec5b..c91f78c5 100644 --- a/src/cryptonote_core/cryptonote_boost_serialization.h +++ b/src/cryptonote_core/cryptonote_boost_serialization.h @@ -37,9 +37,13 @@ #include #include #include +#include +#include #include "cryptonote_basic.h" #include "common/unordered_containers_boost_serialization.h" #include "crypto/crypto.h" +#include "ringct/rctTypes.h" +#include "ringct/rctOps.h" //namespace cryptonote { namespace boost @@ -139,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) { @@ -147,7 +161,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; } @@ -163,6 +180,107 @@ 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; // not serialized, we can recover it from the tx vin + } + + template + inline void serialize(Archive &a, rct::ecdhTuple &x, const boost::serialization::version_type ver) + { + a & x.mask; + a & x.amount; + // 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) + { + 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::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 + // 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; + } + + template + 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 + // 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_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 26748ace..511f5061 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) @@ -57,11 +58,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 +254,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 +401,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 @@ -505,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"); @@ -560,6 +562,14 @@ 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; + } + } if(!check_money_overflow(tx)) { @@ -567,15 +577,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) { @@ -714,6 +728,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); @@ -780,18 +799,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 +933,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 +942,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 +960,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 63969bc2..d16bd655 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" @@ -483,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 @@ -498,7 +502,6 @@ namespace cryptonote */ void resume_mine(); -#if BLOCKCHAIN_DB == DB_LMDB /** * @brief gets the Blockchain instance * @@ -512,10 +515,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 +764,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/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index ff752ae4..32b96f1f 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 @@ -100,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; } @@ -135,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; } @@ -145,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); } } @@ -181,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 = CURRENT_TRANSACTION_VERSION; + 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); @@ -252,6 +264,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) @@ -315,6 +332,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); @@ -447,13 +469,16 @@ 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) { + std::vector amount_keys; tx.vin.clear(); tx.vout.clear(); tx.signatures.clear(); + tx.rct_signatures = rct::rctSig(); + amount_keys.clear(); - tx.version = CURRENT_TRANSACTION_VERSION; + tx.version = rct ? 2 : 1; tx.unlock_time = unlock_time; tx.extra = extra; @@ -509,7 +534,6 @@ namespace cryptonote }; std::vector in_contexts; - uint64_t summary_inputs_money = 0; //fill inputs BOOST_FOREACH(const tx_source_entry& src_entr, sources) @@ -529,7 +553,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,12 +583,18 @@ 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); 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 << ")"); @@ -586,33 +616,148 @@ 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 + { + 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()); - 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); + // 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) + { + // 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; + // 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 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; + 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)); + outamounts.push_back(tx.vout[i].amount); + amount_out += tx.vout[i].amount; + } + + 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 + { + 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 (!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) + { + if (sources[i].rct) + boost::get(tx.vin[i]).amount = 0; + } + 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); + 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, (const rct::keyV&)amount_keys, index, outSk); + else + 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"); + + 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 +806,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; @@ -776,20 +924,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 056940e9..e6a3bfba 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,17 @@ 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 + 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)}))); } }; struct tx_destination_entry @@ -70,7 +75,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 &tx_key, bool rct = false); template bool find_tx_extra_field_by_type(const std::vector& tx_extra_fields, T& field) @@ -85,6 +90,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); @@ -106,6 +112,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); diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 3d5ab86e..46fab4dc 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,21 +70,22 @@ 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) { + if (tx.version == 0) + { + // v0 never accepted + LOG_PRINT_L1("transaction version 0 is invalid"); + 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()) @@ -106,25 +103,34 @@ namespace cryptonote 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); - - 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; - } - // fee per kilobyte, size rounded up. - uint64_t fee = inputs_amount - outputs_amount; + uint64_t fee; + + if (tx.version == 1) + { + 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); + 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; + } + + fee = inputs_amount - outputs_amount; + } + else + { + fee = tx.rct_signatures.txnFee; + } + uint64_t needed_fee = blob_size / 1024; needed_fee += (blob_size % 1024) ? 1 : 0; needed_fee *= FEE_PER_KB; @@ -161,7 +167,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; @@ -169,11 +175,9 @@ 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 + 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) { @@ -181,11 +185,10 @@ 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 = 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; @@ -203,12 +206,11 @@ 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 = 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; @@ -518,12 +520,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); @@ -649,6 +647,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(); ) { diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index c7aab7f0..0e280872 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 }; } diff --git a/src/cryptonote_core/verification_context.h b/src/cryptonote_core/verification_context.h index e58291ea..0bb635e8 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 diff --git a/src/ringct/CMakeLists.txt b/src/ringct/CMakeLists.txt new file mode 100644 index 00000000..078199bb --- /dev/null +++ b/src/ringct/CMakeLists.txt @@ -0,0 +1,59 @@ +# Copyright (c) 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. + +set(ringct_sources + rctOps.cpp + rctSigs.cpp + rctTypes.cpp + rctCryptoOps.c) + +set(ringct_headers) + +set(ringct_private_headers + rctOps.h + rctSigs.h + rctTypes.h) + +bitmonero_private_headers(ringct + ${crypto_private_headers}) +bitmonero_add_library(ringct + ${ringct_sources} + ${ringct_headers} + ${ringct_private_headers}) +target_link_libraries(ringct + LINK_PUBLIC + common + crypto + ${Boost_DATE_TIME_LIBRARY} + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${Boost_SERIALIZATION_LIBRARY} + LINK_PRIVATE + ${Boost_FILESYSTEM_LIBRARY} + ${Boost_SYSTEM_LIBRARY} + ${Boost_THREAD_LIBRARY} + ${EXTRA_LIBRARIES}) diff --git a/src/ringct/rctCryptoOps.c b/src/ringct/rctCryptoOps.c new file mode 100644 index 00000000..9bb9a689 --- /dev/null +++ b/src/ringct/rctCryptoOps.c @@ -0,0 +1,221 @@ +// 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 "crypto/crypto-ops.h" + +//DISABLE_VS_WARNINGS(4146 4244) + +void sc_reduce32copy(unsigned char * scopy, const unsigned char *s) { + int64_t s0 = 2097151 & load_3(s); + int64_t s1 = 2097151 & (load_4(s + 2) >> 5); + int64_t s2 = 2097151 & (load_3(s + 5) >> 2); + int64_t s3 = 2097151 & (load_4(s + 7) >> 7); + int64_t s4 = 2097151 & (load_4(s + 10) >> 4); + int64_t s5 = 2097151 & (load_3(s + 13) >> 1); + int64_t s6 = 2097151 & (load_4(s + 15) >> 6); + int64_t s7 = 2097151 & (load_3(s + 18) >> 3); + int64_t s8 = 2097151 & load_3(s + 21); + int64_t s9 = 2097151 & (load_4(s + 23) >> 5); + int64_t s10 = 2097151 & (load_3(s + 26) >> 2); + int64_t s11 = (load_4(s + 28) >> 7); + int64_t s12 = 0; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + int64_t carry10; + int64_t carry11; + + carry0 = (s0 + (1<<20)) >> 21; + s1 += carry0; + s0 -= carry0 << 21; + carry2 = (s2 + (1<<20)) >> 21; + s3 += carry2; + s2 -= carry2 << 21; + carry4 = (s4 + (1<<20)) >> 21; + s5 += carry4; + s4 -= carry4 << 21; + carry6 = (s6 + (1<<20)) >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry8 = (s8 + (1<<20)) >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry10 = (s10 + (1<<20)) >> 21; + s11 += carry10; + s10 -= carry10 << 21; + + carry1 = (s1 + (1<<20)) >> 21; + s2 += carry1; + s1 -= carry1 << 21; + carry3 = (s3 + (1<<20)) >> 21; + s4 += carry3; + s3 -= carry3 << 21; + carry5 = (s5 + (1<<20)) >> 21; + s6 += carry5; + s5 -= carry5 << 21; + carry7 = (s7 + (1<<20)) >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry9 = (s9 + (1<<20)) >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry11 = (s11 + (1<<20)) >> 21; + s12 += carry11; + s11 -= carry11 << 21; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = s0 >> 21; + s1 += carry0; + s0 -= carry0 << 21; + carry1 = s1 >> 21; + s2 += carry1; + s1 -= carry1 << 21; + carry2 = s2 >> 21; + s3 += carry2; + s2 -= carry2 << 21; + carry3 = s3 >> 21; + s4 += carry3; + s3 -= carry3 << 21; + carry4 = s4 >> 21; + s5 += carry4; + s4 -= carry4 << 21; + carry5 = s5 >> 21; + s6 += carry5; + s5 -= carry5 << 21; + carry6 = s6 >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry7 = s7 >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry8 = s8 >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry9 = s9 >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry10 = s10 >> 21; + s11 += carry10; + s10 -= carry10 << 21; + carry11 = s11 >> 21; + s12 += carry11; + s11 -= carry11 << 21; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + + carry0 = s0 >> 21; + s1 += carry0; + s0 -= carry0 << 21; + carry1 = s1 >> 21; + s2 += carry1; + s1 -= carry1 << 21; + carry2 = s2 >> 21; + s3 += carry2; + s2 -= carry2 << 21; + carry3 = s3 >> 21; + s4 += carry3; + s3 -= carry3 << 21; + carry4 = s4 >> 21; + s5 += carry4; + s4 -= carry4 << 21; + carry5 = s5 >> 21; + s6 += carry5; + s5 -= carry5 << 21; + carry6 = s6 >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry7 = s7 >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry8 = s8 >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry9 = s9 >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry10 = s10 >> 21; + s11 += carry10; + s10 -= carry10 << 21; + + scopy[0] = s0 >> 0; + scopy[1] = s0 >> 8; + scopy[2] = (s0 >> 16) | (s1 << 5); + scopy[3] = s1 >> 3; + scopy[4] = s1 >> 11; + scopy[5] = (s1 >> 19) | (s2 << 2); + scopy[6] = s2 >> 6; + scopy[7] = (s2 >> 14) | (s3 << 7); + scopy[8] = s3 >> 1; + scopy[9] = s3 >> 9; + scopy[10] = (s3 >> 17) | (s4 << 4); + scopy[11] = s4 >> 4; + scopy[12] = s4 >> 12; + scopy[13] = (s4 >> 20) | (s5 << 1); + scopy[14] = s5 >> 7; + scopy[15] = (s5 >> 15) | (s6 << 6); + scopy[16] = s6 >> 2; + scopy[17] = s6 >> 10; + scopy[18] = (s6 >> 18) | (s7 << 3); + scopy[19] = s7 >> 5; + scopy[20] = s7 >> 13; + scopy[21] = s8 >> 0; + scopy[22] = s8 >> 8; + scopy[23] = (s8 >> 16) | (s9 << 5); + scopy[24] = s9 >> 3; + scopy[25] = s9 >> 11; + scopy[26] = (s9 >> 19) | (s10 << 2); + scopy[27] = s10 >> 6; + scopy[28] = (s10 >> 14) | (s11 << 7); + scopy[29] = s11 >> 1; + scopy[30] = s11 >> 9; + scopy[31] = s11 >> 17; +} diff --git a/src/ringct/rctCryptoOps.h b/src/ringct/rctCryptoOps.h new file mode 100644 index 00000000..58c6964d --- /dev/null +++ b/src/ringct/rctCryptoOps.h @@ -0,0 +1,37 @@ +// 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 + +extern "C" { +#include "crypto/crypto-ops.h" +} + +void sc_reduce32copy(unsigned char * scopy, const unsigned char *s); diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp new file mode 100644 index 00000000..488e47ca --- /dev/null +++ b/src/ringct/rctOps.cpp @@ -0,0 +1,475 @@ +// Copyright (c) 2016, Monero Research Labs +// +// Author: Shen Noether +// +// 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. + +#include "misc_log_ex.h" +#include "rctOps.h" +using namespace crypto; +using namespace std; + +namespace rct { + + //Various key initialization functions + + //Creates a zero scalar + void zero(key &zero) { + memset(&zero, 0, 32); + } + + //Creates a zero scalar + key zero() { + static const key z = { {0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 } }; + return z; + } + + //Creates a zero elliptic curve point + void identity(key &Id) { + Id[0] = (unsigned char)(0x01); + memset(Id.bytes+1, 0, 31); + } + + //Creates a zero elliptic curve point + key identity() { + key Id; + Id[0] = (unsigned char)(0x01); + memset(Id.bytes+1, 0, 31); + return Id; + } + + //copies a scalar or point + void copy(key &AA, const key &A) { + memcpy(&AA, &A, 32); + } + + //copies a scalar or point + key copy(const key &A) { + key AA; + memcpy(&AA, &A, 32); + return AA; + } + + + //initializes a key matrix; + //first parameter is rows, + //second is columns + keyM keyMInit(int rows, int cols) { + keyM rv(cols); + int i = 0; + for (i = 0 ; i < cols ; i++) { + rv[i] = keyV(rows); + } + return rv; + } + + + + + //Various key generation functions + + //generates a random scalar which can be used as a secret key or mask + void skGen(key &sk) { + sk = crypto::rand(); + sc_reduce32(sk.bytes); + } + + //generates a random scalar which can be used as a secret key or mask + key skGen() { + key sk = crypto::rand(); + sc_reduce32(sk.bytes); + return sk; + } + + //Generates a vector of secret key + //Mainly used in testing + keyV skvGen(int rows ) { + keyV rv(rows); + int i = 0; + for (i = 0 ; i < rows ; i++) { + skGen(rv[i]); + } + return rv; + } + + //generates a random curve point (for testing) + key pkGen() { + key sk = skGen(); + key pk = scalarmultBase(sk); + return pk; + } + + //generates a random secret and corresponding public key + void skpkGen(key &sk, key &pk) { + skGen(sk); + scalarmultBase(pk, sk); + } + + //generates a random secret and corresponding public key + tuple skpkGen() { + key sk = skGen(); + key pk = scalarmultBase(sk); + return make_tuple(sk, pk); + } + + //generates C =aG + bH from b, a is given.. + void genC(key & C, const key & a, xmr_amount amount) { + key bH = scalarmultH(d2h(amount)); + addKeys1(C, a, bH); + } + + //generates a / Pedersen commitment to the amount + tuple ctskpkGen(xmr_amount amount) { + ctkey sk, pk; + skpkGen(sk.dest, pk.dest); + skpkGen(sk.mask, pk.mask); + key am = d2h(amount); + key bH = scalarmultH(am); + addKeys(pk.mask, pk.mask, bH); + return make_tuple(sk, pk); + } + + + //generates a / Pedersen commitment but takes bH as input + tuple ctskpkGen(key bH) { + ctkey sk, pk; + skpkGen(sk.dest, pk.dest); + skpkGen(sk.mask, pk.mask); + addKeys(pk.mask, pk.mask, bH); + return make_tuple(sk, pk); + } + + key zeroCommit(xmr_amount amount) { + key mask = identity(); + mask = scalarmultBase(mask); + key am = d2h(amount); + key bH = scalarmultH(am); + addKeys(mask, mask, bH); + return mask; + } + + key commit(xmr_amount amount, key mask) { + mask = scalarmultBase(mask); + key am = d2h(amount); + key bH = scalarmultH(am); + addKeys(mask, mask, bH); + return mask; + } + + //generates a random uint long long (for testing) + xmr_amount randXmrAmount(xmr_amount upperlimit) { + return h2d(skGen()) % (upperlimit); + } + + //Scalar multiplications of curve points + + //does a * G where a is a scalar and G is the curve basepoint + void scalarmultBase(key &aG,const key &a) { + ge_p3 point; + sc_reduce32copy(aG.bytes, a.bytes); //do this beforehand! + ge_scalarmult_base(&point, aG.bytes); + ge_p3_tobytes(aG.bytes, &point); + } + + //does a * G where a is a scalar and G is the curve basepoint + key scalarmultBase(const key & a) { + ge_p3 point; + key aG; + sc_reduce32copy(aG.bytes, a.bytes); //do this beforehand + ge_scalarmult_base(&point, aG.bytes); + ge_p3_tobytes(aG.bytes, &point); + return aG; + } + + //does a * P where a is a scalar and P is an arbitrary point + void scalarmultKey(key & aP, const key &P, const key &a) { + ge_p3 A; + ge_p2 R; + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&A, P.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast(__LINE__)); + ge_scalarmult(&R, a.bytes, &A); + ge_tobytes(aP.bytes, &R); + } + + //does a * P where a is a scalar and P is an arbitrary point + key scalarmultKey(const key & P, const key & a) { + ge_p3 A; + ge_p2 R; + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&A, P.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast(__LINE__)); + ge_scalarmult(&R, a.bytes, &A); + key aP; + ge_tobytes(aP.bytes, &R); + return aP; + } + + + //Computes aH where H= toPoint(cn_fast_hash(G)), G the basepoint + key scalarmultH(const key & a) { + ge_p3 A; + ge_p2 R; + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&A, H.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast(__LINE__)); + ge_scalarmult(&R, a.bytes, &A); + key aP; + ge_tobytes(aP.bytes, &R); + return aP; + } + + //Curve addition / subtractions + + //for curve points: AB = A + B + void addKeys(key &AB, const key &A, const key &B) { + ge_p3 B2, A2; + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&B2, B.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast(__LINE__)); + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&A2, A.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast(__LINE__)); + ge_cached tmp2; + ge_p3_to_cached(&tmp2, &B2); + ge_p1p1 tmp3; + ge_add(&tmp3, &A2, &tmp2); + ge_p1p1_to_p3(&A2, &tmp3); + ge_p3_tobytes(AB.bytes, &A2); + } + + + //addKeys1 + //aGB = aG + B where a is a scalar, G is the basepoint, and B is a point + void addKeys1(key &aGB, const key &a, const key & B) { + key aG = scalarmultBase(a); + addKeys(aGB, aG, B); + } + + //addKeys2 + //aGbB = aG + bB where a, b are scalars, G is the basepoint and B is a point + void addKeys2(key &aGbB, const key &a, const key &b, const key & B) { + ge_p2 rv; + ge_p3 B2; + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&B2, B.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast(__LINE__)); + ge_double_scalarmult_base_vartime(&rv, b.bytes, &B2, a.bytes); + ge_tobytes(aGbB.bytes, &rv); + } + + //Does some precomputation to make addKeys3 more efficient + // input B a curve point and output a ge_dsmp which has precomputation applied + void precomp(ge_dsmp rv, const key & B) { + ge_p3 B2; + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&B2, B.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast(__LINE__)); + ge_dsm_precomp(rv, &B2); + } + + //addKeys3 + //aAbB = a*A + b*B where a, b are scalars, A, B are curve points + //B must be input after applying "precomp" + void addKeys3(key &aAbB, const key &a, const key &A, const key &b, const ge_dsmp B) { + ge_p2 rv; + ge_p3 A2; + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&A2, A.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast(__LINE__)); + ge_double_scalarmult_precomp_vartime(&rv, a.bytes, &A2, b.bytes, B); + ge_tobytes(aAbB.bytes, &rv); + } + + + //subtract Keys (subtracts curve points) + //AB = A - B where A, B are curve points + void subKeys(key & AB, const key &A, const key &B) { + ge_p3 B2, A2; + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&B2, B.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast(__LINE__)); + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&A2, A.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast(__LINE__)); + ge_cached tmp2; + ge_p3_to_cached(&tmp2, &B2); + ge_p1p1 tmp3; + ge_sub(&tmp3, &A2, &tmp2); + ge_p1p1_to_p3(&A2, &tmp3); + ge_p3_tobytes(AB.bytes, &A2); + } + + //checks if A, B are equal as curve points + //without doing curve operations + bool equalKeys(const key & a, const key & b) { + bool rv = true; + for (int i = 0; i < 32; ++i) { + if (a.bytes[i] != b.bytes[i]) { + rv = false; + } + } + return rv; + } + + //Hashing - cn_fast_hash + //be careful these are also in crypto namespace + //cn_fast_hash for arbitrary multiples of 32 bytes + void cn_fast_hash(key &hash, const void * data, const std::size_t l) { + keccak((uint8_t *)data, l, hash.bytes, 32); + } + + void hash_to_scalar(key &hash, const void * data, const std::size_t l) { + cn_fast_hash(hash, data, l); + sc_reduce32(hash.bytes); + } + + //cn_fast_hash for a 32 byte key + void cn_fast_hash(key & hash, const key & in) { + keccak((uint8_t *)in.bytes, 32, hash.bytes, 32); + } + + void hash_to_scalar(key & hash, const key & in) { + cn_fast_hash(hash, in); + sc_reduce32(hash.bytes); + } + + //cn_fast_hash for a 32 byte key + key cn_fast_hash(const key & in) { + key hash; + keccak((uint8_t *)in.bytes, 32, hash.bytes, 32); + return hash; + } + + key hash_to_scalar(const key & in) { + key hash = cn_fast_hash(in); + sc_reduce32(hash.bytes); + return hash; + } + + //cn_fast_hash for a 128 byte unsigned char + key cn_fast_hash128(const void * in) { + key hash; + keccak((uint8_t *)in, 128, hash.bytes, 32); + return hash; + } + + key hash_to_scalar128(const void * in) { + key hash = cn_fast_hash128(in); + sc_reduce32(hash.bytes); + return hash; + } + + //cn_fast_hash for multisig purpose + //This takes the outputs and commitments + //and hashes them into a 32 byte sized key + key cn_fast_hash(ctkeyV PC) { + key rv = identity(); + std::size_t l = (std::size_t)PC.size(); + size_t i = 0, j = 0; + vector m(l * 64); + for (i = 0 ; i < l ; i++) { + memcpy(&m[i * 64], &PC[i].dest, 32); + memcpy(&m[i * 64 + 32], &PC[i].mask, 32); + } + cn_fast_hash(rv, &m[0], 64*l); + return rv; + } + + key hash_to_scalar(ctkeyV PC) { + key rv = cn_fast_hash(PC); + sc_reduce32(rv.bytes); + return rv; + } + + //cn_fast_hash for a key-vector of arbitrary length + //this is useful since you take a number of keys + //put them in the key vector and it concatenates them + //and then hashes them + key cn_fast_hash(const keyV &keys) { + size_t l = keys.size(); + vector m(l * 32); + size_t i; + for (i = 0 ; i < l ; i++) { + memcpy(&m[i * 32], keys[i].bytes, 32); + } + key rv; + cn_fast_hash(rv, &m[0], 32 * l); + //dp(rv); + return rv; + } + + key hash_to_scalar(const keyV &keys) { + key rv = cn_fast_hash(keys); + sc_reduce32(rv.bytes); + return rv; + } + + key hashToPointSimple(const key & hh) { + key pointk; + ge_p1p1 point2; + ge_p2 point; + ge_p3 res; + key h = cn_fast_hash(hh); + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&res, h.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast(__LINE__)); + ge_p3_to_p2(&point, &res); + ge_mul8(&point2, &point); + ge_p1p1_to_p3(&res, &point2); + ge_p3_tobytes(pointk.bytes, &res); + return pointk; + } + + key hashToPoint(const key & hh) { + key pointk; + ge_p2 point; + ge_p1p1 point2; + ge_p3 res; + key h = cn_fast_hash(hh); + ge_fromfe_frombytes_vartime(&point, h.bytes); + ge_mul8(&point2, &point); + ge_p1p1_to_p3(&res, &point2); + ge_p3_tobytes(pointk.bytes, &res); + return pointk; + } + + void hashToPoint(key & pointk, const key & hh) { + ge_p2 point; + ge_p1p1 point2; + ge_p3 res; + key h = cn_fast_hash(hh); + ge_fromfe_frombytes_vartime(&point, h.bytes); + ge_mul8(&point2, &point); + ge_p1p1_to_p3(&res, &point2); + ge_p3_tobytes(pointk.bytes, &res); + } + + //sums a vector of curve points (for scalars use sc_add) + void sumKeys(key & Csum, const keyV & Cis) { + identity(Csum); + size_t i = 0; + for (i = 0; i < Cis.size(); i++) { + addKeys(Csum, Csum, Cis[i]); + } + } + + //Elliptic Curve Diffie Helman: encodes and decodes the amount b and mask a + // where C= aG + bH + void ecdhEncode(ecdhTuple & unmasked, const key & sharedSec) { + key sharedSec1 = hash_to_scalar(sharedSec); + key sharedSec2 = hash_to_scalar(sharedSec1); + //encode + sc_add(unmasked.mask.bytes, unmasked.mask.bytes, sharedSec1.bytes); + sc_add(unmasked.amount.bytes, unmasked.amount.bytes, sharedSec2.bytes); + } + void ecdhDecode(ecdhTuple & masked, const key & sharedSec) { + key sharedSec1 = hash_to_scalar(sharedSec); + key sharedSec2 = hash_to_scalar(sharedSec1); + //decode + sc_sub(masked.mask.bytes, masked.mask.bytes, sharedSec1.bytes); + sc_sub(masked.amount.bytes, masked.amount.bytes, sharedSec2.bytes); + } +} diff --git a/src/ringct/rctOps.h b/src/ringct/rctOps.h new file mode 100644 index 00000000..1e71c645 --- /dev/null +++ b/src/ringct/rctOps.h @@ -0,0 +1,171 @@ +//#define DBG +// Copyright (c) 2016, Monero Research Labs +// +// Author: Shen Noether +// +// 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. + +#pragma once + +#ifndef RCTOPS_H +#define RCTOPS_H + +#include +#include +#include +#include + +#include "crypto/generic-ops.h" + +extern "C" { +#include "crypto/random.h" +#include "crypto/keccak.h" +#include "rctCryptoOps.h" +} +#include "crypto/crypto.h" + +#include "rctTypes.h" + +//Define this flag when debugging to get additional info on the console +#ifdef DBG +#define DP(x) dp(x) +#else +#define DP(x) +#endif + +using namespace std; +using namespace crypto; + +namespace rct { + + //Various key initialization functions + + //Creates a zero scalar + key zero(); + void zero(key &z); + //Creates a zero elliptic curve point + key identity(); + void identity(key &Id); + //copies a scalar or point + void copy(key &AA, const key &A); + key copy(const key & AA); + //initializes a key matrix; + //first parameter is rows, + //second is columns + keyM keyMInit(int, int); + + //Various key generation functions + + //generates a random scalar which can be used as a secret key or mask + key skGen(); + void skGen(key &); + + //generates a vector of secret keys of size "int" + keyV skvGen(int ); + + //generates a random curve point (for testing) + key pkGen(); + //generates a random secret and corresponding public key + void skpkGen(key &sk, key &pk); + tuple skpkGen(); + //generates a / Pedersen commitment to the amount + tuple ctskpkGen(xmr_amount amount); + //generates C =aG + bH from b, a is random + void genC(key & C, const key & a, xmr_amount amount); + //this one is mainly for testing, can take arbitrary amounts.. + tuple ctskpkGen(key bH); + // make a pedersen commitment with given key + key commit(xmr_amount amount, key mask); + // make a pedersen commitment with zero key + key zeroCommit(xmr_amount amount); + //generates a random uint long long + xmr_amount randXmrAmount(xmr_amount upperlimit); + + //Scalar multiplications of curve points + + //does a * G where a is a scalar and G is the curve basepoint + void scalarmultBase(key & aG, const key &a); + key scalarmultBase(const key & a); + //does a * P where a is a scalar and P is an arbitrary point + void scalarmultKey(key &aP, const key &P, const key &a); + key scalarmultKey(const key &P, const key &a); + //Computes aH where H= toPoint(cn_fast_hash(G)), G the basepoint + key scalarmultH(const key & a); + + //Curve addition / subtractions + + //for curve points: AB = A + B + void addKeys(key &AB, const key &A, const key &B); + //aGB = aG + B where a is a scalar, G is the basepoint, and B is a point + void addKeys1(key &aGB, const key &a, const key & B); + //aGbB = aG + bB where a, b are scalars, G is the basepoint and B is a point + void addKeys2(key &aGbB, const key &a, const key &b, const key &B); + //Does some precomputation to make addKeys3 more efficient + // input B a curve point and output a ge_dsmp which has precomputation applied + void precomp(ge_dsmp rv, const key &B); + //aAbB = a*A + b*B where a, b are scalars, A, B are curve points + //B must be input after applying "precomp" + void addKeys3(key &aAbB, const key &a, const key &A, const key &b, const ge_dsmp B); + //AB = A - B where A, B are curve points + void subKeys(key &AB, const key &A, const key &B); + //checks if A, B are equal as curve points + bool equalKeys(const key & A, const key & B); + + //Hashing - cn_fast_hash + //be careful these are also in crypto namespace + //cn_fast_hash for arbitrary l multiples of 32 bytes + void cn_fast_hash(key &hash, const void * data, const size_t l); + void hash_to_scalar(key &hash, const void * data, const size_t l); + //cn_fast_hash for a 32 byte key + void cn_fast_hash(key &hash, const key &in); + void hash_to_scalar(key &hash, const key &in); + //cn_fast_hash for a 32 byte key + key cn_fast_hash(const key &in); + key hash_to_scalar(const key &in); + //for mg sigs + key cn_fast_hash128(const void * in); + key hash_to_scalar128(const void * in); + key cn_fast_hash(ctkeyV PC); + key hash_to_scalar(ctkeyV PC); + //for mg sigs + key cn_fast_hash(const keyV &keys); + key hash_to_scalar(const keyV &keys); + + //returns hashToPoint as described in https://github.com/ShenNoether/ge_fromfe_writeup + key hashToPointSimple(const key &in); + key hashToPoint(const key &in); + void hashToPoint(key &out, const key &in); + + //sums a vector of curve points (for scalars use sc_add) + void sumKeys(key & Csum, const key &Cis); + + //Elliptic Curve Diffie Helman: encodes and decodes the amount b and mask a + // where C= aG + bH + void ecdhEncode(ecdhTuple & unmasked, const key & sharedSec); + void ecdhDecode(ecdhTuple & masked, const key & sharedSec); +} +#endif /* RCTOPS_H */ diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp new file mode 100644 index 00000000..f4dbd65c --- /dev/null +++ b/src/ringct/rctSigs.cpp @@ -0,0 +1,875 @@ +// Copyright (c) 2016, Monero Research Labs +// +// Author: Shen Noether +// +// 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. + +#include "misc_log_ex.h" +#include "rctSigs.h" +#include "cryptonote_core/cryptonote_format_utils.h" + +using namespace crypto; +using namespace std; + +namespace rct { + + //Schnorr Non-linkable + //Gen Gives a signature (L1, s1, s2) proving that the sender knows "x" such that xG = one of P1 or P2 + //Ver Verifies that signer knows an "x" such that xG = one of P1 or P2 + //These are called in the below ASNL sig generation + + void GenSchnorrNonLinkable(key & L1, key & s1, key & s2, const key & x, const key & P1, const key & P2, int index) { + key c1, c2, L2; + key a = skGen(); + if (index == 0) { + scalarmultBase(L1, a); + hash_to_scalar(c2, L1); + skGen(s2); + addKeys2(L2, s2, c2, P2); + hash_to_scalar(c1, L2); + //s1 = a - x * c1 + sc_mulsub(s1.bytes, x.bytes, c1.bytes, a.bytes); + } + else if (index == 1) { + scalarmultBase(L2, a); + hash_to_scalar(c1, L2); + skGen(s1); + addKeys2(L1, s1, c1, P1); + hash_to_scalar(c2, L1); + sc_mulsub(s2.bytes, x.bytes, c2.bytes, a.bytes); + } + else { + throw std::runtime_error("GenSchnorrNonLinkable: invalid index (should be 0 or 1)"); + } + } + + //Schnorr Non-linkable + //Gen Gives a signature (L1, s1, s2) proving that the sender knows "x" such that xG = one of P1 or P2 + //Ver Verifies that signer knows an "x" such that xG = one of P1 or P2 + //These are called in the below ASNL sig generation + bool VerSchnorrNonLinkable(const key & P1, const key & P2, const key & L1, const key & s1, const key & s2) { + key c2, L2, c1, L1p; + hash_to_scalar(c2, L1); + addKeys2(L2, s2, c2, P2); + hash_to_scalar(c1, L2); + addKeys2(L1p, s1, c1, P1); + + return equalKeys(L1, L1p); + } + + //Aggregate Schnorr Non-linkable Ring Signature (ASNL) + // c.f. http://eprint.iacr.org/2015/1098 section 5. + // These are used in range proofs (alternatively Borromean could be used) + // Gen gives a signature which proves the signer knows, for each i, + // an x[i] such that x[i]G = one of P1[i] or P2[i] + // Ver Verifies the signer knows a key for one of P1[i], P2[i] at each i + asnlSig GenASNL(key64 x, key64 P1, key64 P2, bits indices) { + DP("Generating Aggregate Schnorr Non-linkable Ring Signature\n"); + key64 s1; + int j = 0; + asnlSig rv; + rv.s = zero(); + for (j = 0; j < ATOMS; j++) { + GenSchnorrNonLinkable(rv.L1[j], s1[j], rv.s2[j], x[j], P1[j], P2[j], (int)indices[j]); + sc_add(rv.s.bytes, rv.s.bytes, s1[j].bytes); + } + return rv; + } + + //Aggregate Schnorr Non-linkable Ring Signature (ASNL) + // c.f. http://eprint.iacr.org/2015/1098 section 5. + // These are used in range proofs (alternatively Borromean could be used) + // Gen gives a signature which proves the signer knows, for each i, + // an x[i] such that x[i]G = one of P1[i] or P2[i] + // Ver Verifies the signer knows a key for one of P1[i], P2[i] at each i + bool VerASNL(const key64 P1, const key64 P2, const asnlSig &as) { + DP("Verifying Aggregate Schnorr Non-linkable Ring Signature\n"); + key LHS = identity(); + key RHS = scalarmultBase(as.s); + key c2, L2, c1; + int j = 0; + for (j = 0; j < ATOMS; j++) { + hash_to_scalar(c2, as.L1[j]); + addKeys2(L2, as.s2[j], c2, P2[j]); + addKeys(LHS, LHS, as.L1[j]); + hash_to_scalar(c1, L2); + addKeys(RHS, RHS, scalarmultKey(P1[j], c1)); + } + key cc; + sc_sub(cc.bytes, LHS.bytes, RHS.bytes); + return sc_isnonzero(cc.bytes) == 0; + } + + //Multilayered Spontaneous Anonymous Group Signatures (MLSAG signatures) + //These are aka MG signatutes in earlier drafts of the ring ct paper + // c.f. http://eprint.iacr.org/2015/1098 section 2. + // keyImageV just does I[i] = xx[i] * Hash(xx[i] * G) for each i + // Gen creates a signature which proves that for some column in the keymatrix "pk" + // the signer knows a secret key for each row in that column + // Ver verifies that the MG sig was created correctly + keyV keyImageV(const keyV &xx) { + keyV II(xx.size()); + size_t i = 0; + for (i = 0; i < xx.size(); i++) { + II[i] = scalarmultKey(hashToPoint(scalarmultBase(xx[i])), xx[i]); + } + return II; + } + + + //Multilayered Spontaneous Anonymous Group Signatures (MLSAG signatures) + //This is a just slghtly more efficient version than the ones described below + //(will be explained in more detail in Ring Multisig paper + //These are aka MG signatutes in earlier drafts of the ring ct paper + // c.f. http://eprint.iacr.org/2015/1098 section 2. + // keyImageV just does I[i] = xx[i] * Hash(xx[i] * G) for each i + // Gen creates a signature which proves that for some column in the keymatrix "pk" + // the signer knows a secret key for each row in that column + // Ver verifies that the MG sig was created correctly + mgSig MLSAG_Gen(key message, const keyM & pk, const keyV & xx, const unsigned int index, size_t dsRows) { + mgSig rv; + size_t cols = pk.size(); + CHECK_AND_ASSERT_THROW_MES(cols >= 2, "Error! What is c if cols = 1!"); + CHECK_AND_ASSERT_THROW_MES(index < cols, "Index out of range"); + size_t rows = pk[0].size(); + CHECK_AND_ASSERT_THROW_MES(rows >= 1, "Empty pk"); + for (size_t i = 1; i < cols; ++i) { + CHECK_AND_ASSERT_THROW_MES(pk[i].size() == rows, "pk is not rectangular"); + } + CHECK_AND_ASSERT_THROW_MES(xx.size() == rows, "Bad xx size"); + CHECK_AND_ASSERT_THROW_MES(dsRows <= rows, "Bad dsRows size"); + + size_t i = 0, j = 0, ii = 0; + key c, c_old, L, R, Hi; + sc_0(c_old.bytes); + vector Ip(dsRows); + rv.II = keyV(dsRows); + keyV alpha(rows); + keyV aG(rows); + rv.ss = keyM(cols, aG); + keyV aHP(dsRows); + keyV toHash(1 + 3 * dsRows + 2 * (rows - dsRows)); + toHash[0] = message; + DP("here1"); + for (i = 0; i < dsRows; i++) { + skpkGen(alpha[i], aG[i]); //need to save alphas for later.. + Hi = hashToPoint(pk[index][i]); + aHP[i] = scalarmultKey(Hi, alpha[i]); + toHash[3 * i + 1] = pk[index][i]; + toHash[3 * i + 2] = aG[i]; + toHash[3 * i + 3] = aHP[i]; + rv.II[i] = scalarmultKey(Hi, xx[i]); + precomp(Ip[i].k, rv.II[i]); + } + size_t ndsRows = 3 * dsRows; //non Double Spendable Rows (see identity chains paper) + for (i = dsRows, ii = 0 ; i < rows ; i++, ii++) { + skpkGen(alpha[i], aG[i]); //need to save alphas for later.. + toHash[ndsRows + 2 * ii + 1] = pk[index][i]; + toHash[ndsRows + 2 * ii + 2] = aG[i]; + } + + c_old = hash_to_scalar(toHash); + + + i = (index + 1) % cols; + if (i == 0) { + copy(rv.cc, c_old); + } + while (i != index) { + + rv.ss[i] = skvGen(rows); + sc_0(c.bytes); + for (j = 0; j < dsRows; j++) { + addKeys2(L, rv.ss[i][j], c_old, pk[i][j]); + hashToPoint(Hi, pk[i][j]); + addKeys3(R, rv.ss[i][j], Hi, c_old, Ip[j].k); + toHash[3 * j + 1] = pk[i][j]; + toHash[3 * j + 2] = L; + toHash[3 * j + 3] = R; + } + for (j = dsRows, ii = 0; j < rows; j++, ii++) { + addKeys2(L, rv.ss[i][j], c_old, pk[i][j]); + toHash[ndsRows + 2 * ii + 1] = pk[i][j]; + toHash[ndsRows + 2 * ii + 2] = L; + } + c = hash_to_scalar(toHash); + copy(c_old, c); + i = (i + 1) % cols; + + if (i == 0) { + copy(rv.cc, c_old); + } + } + for (j = 0; j < rows; j++) { + sc_mulsub(rv.ss[index][j].bytes, c.bytes, xx[j].bytes, alpha[j].bytes); + } + return rv; + } + + //Multilayered Spontaneous Anonymous Group Signatures (MLSAG signatures) + //This is a just slghtly more efficient version than the ones described below + //(will be explained in more detail in Ring Multisig paper + //These are aka MG signatutes in earlier drafts of the ring ct paper + // c.f. http://eprint.iacr.org/2015/1098 section 2. + // keyImageV just does I[i] = xx[i] * Hash(xx[i] * G) for each i + // Gen creates a signature which proves that for some column in the keymatrix "pk" + // the signer knows a secret key for each row in that column + // Ver verifies that the MG sig was created correctly + bool MLSAG_Ver(key message, const keyM & pk, const mgSig & rv, size_t dsRows) { + + size_t cols = pk.size(); + CHECK_AND_ASSERT_MES(cols >= 2, false, "Error! What is c if cols = 1!"); + size_t rows = pk[0].size(); + CHECK_AND_ASSERT_MES(rows >= 1, false, "Empty pk"); + for (size_t i = 1; i < cols; ++i) { + CHECK_AND_ASSERT_MES(pk[i].size() == rows, false, "pk is not rectangular"); + } + CHECK_AND_ASSERT_MES(rv.II.size() == dsRows, false, "Bad II size"); + CHECK_AND_ASSERT_MES(rv.ss.size() == cols, false, "Bad rv.ss size"); + for (size_t i = 0; i < cols; ++i) { + CHECK_AND_ASSERT_MES(rv.ss[i].size() == rows, false, "rv.ss is not rectangular"); + } + CHECK_AND_ASSERT_MES(dsRows <= rows, false, "Bad dsRows value"); + + size_t i = 0, j = 0, ii = 0; + key c, L, R, Hi; + key c_old = copy(rv.cc); + vector Ip(dsRows); + for (i = 0 ; i < dsRows ; i++) { + precomp(Ip[i].k, rv.II[i]); + } + size_t ndsRows = 3 * dsRows; //non Double Spendable Rows (see identity chains paper + keyV toHash(1 + 3 * dsRows + 2 * (rows - dsRows)); + toHash[0] = message; + i = 0; + while (i < cols) { + sc_0(c.bytes); + for (j = 0; j < dsRows; j++) { + addKeys2(L, rv.ss[i][j], c_old, pk[i][j]); + hashToPoint(Hi, pk[i][j]); + addKeys3(R, rv.ss[i][j], Hi, c_old, Ip[j].k); + toHash[3 * j + 1] = pk[i][j]; + toHash[3 * j + 2] = L; + toHash[3 * j + 3] = R; + } + for (j = dsRows, ii = 0 ; j < rows ; j++, ii++) { + addKeys2(L, rv.ss[i][j], c_old, pk[i][j]); + toHash[ndsRows + 2 * ii + 1] = pk[i][j]; + toHash[ndsRows + 2 * ii + 2] = L; + } + c = hash_to_scalar(toHash); + copy(c_old, c); + i = (i + 1); + } + sc_sub(c.bytes, c_old.bytes, rv.cc.bytes); + return sc_isnonzero(c.bytes) == 0; + } + + + + //proveRange and verRange + //proveRange gives C, and mask such that \sumCi = C + // c.f. http://eprint.iacr.org/2015/1098 section 5.1 + // and Ci is a commitment to either 0 or 2^i, i=0,...,63 + // thus this proves that "amount" is in [0, 2^64] + // mask is a such that C = aG + bH, and b = amount + //verRange verifies that \sum Ci = C and that each Ci is a commitment to 0 or 2^i + rangeSig proveRange(key & C, key & mask, const xmr_amount & amount) { + sc_0(mask.bytes); + identity(C); + bits b; + d2b(b, amount); + rangeSig sig; + key64 ai; + key64 CiH; + int i = 0; + for (i = 0; i < ATOMS; i++) { + skGen(ai[i]); + if (b[i] == 0) { + scalarmultBase(sig.Ci[i], ai[i]); + } + if (b[i] == 1) { + addKeys1(sig.Ci[i], ai[i], H2[i]); + } + subKeys(CiH[i], sig.Ci[i], H2[i]); + sc_add(mask.bytes, mask.bytes, ai[i].bytes); + addKeys(C, C, sig.Ci[i]); + } + sig.asig = GenASNL(ai, sig.Ci, CiH, b); + return sig; + } + + //proveRange and verRange + //proveRange gives C, and mask such that \sumCi = C + // c.f. http://eprint.iacr.org/2015/1098 section 5.1 + // and Ci is a commitment to either 0 or 2^i, i=0,...,63 + // thus this proves that "amount" is in [0, 2^64] + // mask is a such that C = aG + bH, and b = amount + //verRange verifies that \sum Ci = C and that each Ci is a commitment to 0 or 2^i + bool verRange(const key & C, const rangeSig & as) { + key64 CiH; + int i = 0; + key Ctmp = identity(); + for (i = 0; i < 64; i++) { + subKeys(CiH[i], as.Ci[i], H2[i]); + addKeys(Ctmp, Ctmp, as.Ci[i]); + } + if (!equalKeys(C, Ctmp)) + return false; + if (!VerASNL(as.Ci, CiH, as.asig)) + return false; + return true; + } + + key get_pre_mlsag_hash(const rctSig &rv) + { + keyV hashes; + hashes.push_back(rv.message); + crypto::hash h; + cryptonote::get_blob_hash(cryptonote::t_serializable_object_to_blob((const rctSigBase&)rv), h); + hashes.push_back(hash2rct(h)); + keyV kv; + for (auto r: rv.p.rangeSigs) + { + for (size_t n = 0; n < 64; ++n) + kv.push_back(r.asig.L1[n]); + for (size_t n = 0; n < 64; ++n) + kv.push_back(r.asig.s2[n]); + kv.push_back(r.asig.s); + for (size_t n = 0; n < 64; ++n) + kv.push_back(r.Ci[n]); + } + hashes.push_back(cn_fast_hash(kv)); + + return cn_fast_hash(hashes); + } + + //Ring-ct MG sigs + //Prove: + // c.f. http://eprint.iacr.org/2015/1098 section 4. definition 10. + // This does the MG sig on the "dest" part of the given key matrix, and + // the last row is the sum of input commitments from that column - sum output commitments + // this shows that sum inputs = sum outputs + //Ver: + // verifies the above sig is created corretly + mgSig proveRctMG(const key &message, const ctkeyM & pubs, const ctkeyV & inSk, const ctkeyV &outSk, const ctkeyV & outPk, unsigned int index, key txnFeeKey) { + mgSig mg; + //setup vars + size_t cols = pubs.size(); + CHECK_AND_ASSERT_THROW_MES(cols >= 1, "Empty pubs"); + size_t rows = pubs[0].size(); + CHECK_AND_ASSERT_THROW_MES(rows >= 1, "Empty pubs"); + for (size_t i = 1; i < cols; ++i) { + CHECK_AND_ASSERT_THROW_MES(pubs[i].size() == rows, "pubs is not rectangular"); + } + CHECK_AND_ASSERT_THROW_MES(inSk.size() == rows, "Bad inSk size"); + CHECK_AND_ASSERT_THROW_MES(outSk.size() == outPk.size(), "Bad outSk/outPk size"); + + keyV sk(rows + 1); + keyV tmp(rows + 1); + size_t i = 0, j = 0; + for (i = 0; i < rows + 1; i++) { + sc_0(sk[i].bytes); + identity(tmp[i]); + } + keyM M(cols, tmp); + //create the matrix to mg sig + for (i = 0; i < cols; i++) { + M[i][rows] = identity(); + for (j = 0; j < rows; j++) { + M[i][j] = pubs[i][j].dest; + addKeys(M[i][rows], M[i][rows], pubs[i][j].mask); //add input commitments in last row + } + } + sc_0(sk[rows].bytes); + for (j = 0; j < rows; j++) { + sk[j] = copy(inSk[j].dest); + sc_add(sk[rows].bytes, sk[rows].bytes, inSk[j].mask.bytes); //add masks in last row + } + for (i = 0; i < cols; i++) { + for (size_t j = 0; j < outPk.size(); j++) { + subKeys(M[i][rows], M[i][rows], outPk[j].mask); //subtract output Ci's in last row + } + //subtract txn fee output in last row + subKeys(M[i][rows], M[i][rows], txnFeeKey); + } + for (size_t j = 0; j < outPk.size(); j++) { + sc_sub(sk[rows].bytes, sk[rows].bytes, outSk[j].mask.bytes); //subtract output masks in last row.. + } + return MLSAG_Gen(message, M, sk, index, rows); + } + + + //Ring-ct MG sigs Simple + // Simple version for when we assume only + // post rct inputs + // here pubs is a vector of (P, C) length mixin + // inSk is x, a_in corresponding to signing index + // a_out, Cout is for the output commitment + // index is the signing index.. + mgSig proveRctMGSimple(const key &message, const ctkeyV & pubs, const ctkey & inSk, const key &a , const key &Cout, unsigned int index) { + mgSig mg; + //setup vars + size_t rows = 1; + size_t cols = pubs.size(); + CHECK_AND_ASSERT_THROW_MES(cols >= 1, "Empty pubs"); + keyV tmp(rows + 1); + keyV sk(rows + 1); + size_t i; + keyM M(cols, tmp); + for (i = 0; i < cols; i++) { + M[i][0] = pubs[i].dest; + subKeys(M[i][1], pubs[i].mask, Cout); + sk[0] = copy(inSk.dest); + sc_sub(sk[1].bytes, inSk.mask.bytes, a.bytes); + } + return MLSAG_Gen(message, M, sk, index, rows); + } + + + //Ring-ct MG sigs + //Prove: + // c.f. http://eprint.iacr.org/2015/1098 section 4. definition 10. + // This does the MG sig on the "dest" part of the given key matrix, and + // the last row is the sum of input commitments from that column - sum output commitments + // this shows that sum inputs = sum outputs + //Ver: + // verifies the above sig is created corretly + bool verRctMG(const mgSig &mg, const ctkeyM & pubs, const ctkeyV & outPk, key txnFeeKey, const key &message) { + //setup vars + size_t cols = pubs.size(); + CHECK_AND_ASSERT_MES(cols >= 1, false, "Empty pubs"); + size_t rows = pubs[0].size(); + CHECK_AND_ASSERT_MES(rows >= 1, false, "Empty pubs"); + for (size_t i = 1; i < cols; ++i) { + CHECK_AND_ASSERT_MES(pubs[i].size() == rows, false, "pubs is not rectangular"); + } + + keyV tmp(rows + 1); + size_t i = 0, j = 0; + for (i = 0; i < rows + 1; i++) { + identity(tmp[i]); + } + keyM M(cols, tmp); + + //create the matrix to mg sig + for (j = 0; j < rows; j++) { + for (i = 0; i < cols; i++) { + M[i][j] = pubs[i][j].dest; + addKeys(M[i][rows], M[i][rows], pubs[i][j].mask); //add Ci in last row + } + } + for (i = 0; i < cols; i++) { + for (j = 0; j < outPk.size(); j++) { + subKeys(M[i][rows], M[i][rows], outPk[j].mask); //subtract output Ci's in last row + } + //subtract txn fee output in last row + subKeys(M[i][rows], M[i][rows], txnFeeKey); + } + return MLSAG_Ver(message, M, mg, rows); + } + + //Ring-ct Simple MG sigs + //Ver: + //This does a simplified version, assuming only post Rct + //inputs + bool verRctMGSimple(const key &message, const mgSig &mg, const ctkeyV & pubs, const key & C) { + //setup vars + size_t rows = 1; + size_t cols = pubs.size(); + CHECK_AND_ASSERT_MES(cols >= 1, false, "Empty pubs"); + keyV tmp(rows + 1); + size_t i; + keyM M(cols, tmp); + //create the matrix to mg sig + for (i = 0; i < cols; i++) { + M[i][0] = pubs[i].dest; + subKeys(M[i][1], pubs[i].mask, C); + } + //DP(C); + return MLSAG_Ver(message, M, mg, rows); + } + + //These functions get keys from blockchain + //replace these when connecting blockchain + //getKeyFromBlockchain grabs a key from the blockchain at "reference_index" to mix with + //populateFromBlockchain creates a keymatrix with "mixin" columns and one of the columns is inPk + // the return value are the key matrix, and the index where inPk was put (random). + void getKeyFromBlockchain(ctkey & a, size_t reference_index) { + a.mask = pkGen(); + a.dest = pkGen(); + } + + //These functions get keys from blockchain + //replace these when connecting blockchain + //getKeyFromBlockchain grabs a key from the blockchain at "reference_index" to mix with + //populateFromBlockchain creates a keymatrix with "mixin" + 1 columns and one of the columns is inPk + // the return value are the key matrix, and the index where inPk was put (random). + tuple populateFromBlockchain(ctkeyV inPk, int mixin) { + int rows = inPk.size(); + ctkeyM rv(mixin + 1, inPk); + int index = randXmrAmount(mixin); + int i = 0, j = 0; + for (i = 0; i <= mixin; i++) { + if (i != index) { + for (j = 0; j < rows; j++) { + getKeyFromBlockchain(rv[i][j], (size_t)randXmrAmount); + } + } + } + return make_tuple(rv, index); + } + + //These functions get keys from blockchain + //replace these when connecting blockchain + //getKeyFromBlockchain grabs a key from the blockchain at "reference_index" to mix with + //populateFromBlockchain creates a keymatrix with "mixin" columns and one of the columns is inPk + // the return value are the key matrix, and the index where inPk was put (random). + xmr_amount populateFromBlockchainSimple(ctkeyV & mixRing, const ctkey & inPk, int mixin) { + int index = randXmrAmount(mixin); + int i = 0; + for (i = 0; i <= mixin; i++) { + if (i != index) { + getKeyFromBlockchain(mixRing[i], (size_t)randXmrAmount(1000)); + } else { + mixRing[i] = inPk; + } + } + return index; + } + + //RingCT protocol + //genRct: + // creates an rctSig with all data necessary to verify the rangeProofs and that the signer owns one of the + // columns that are claimed as inputs, and that the sum of inputs = sum of outputs. + // Also contains masked "amount" and "mask" so the receiver can see how much they received + //verRct: + // verifies that all signatures (rangeProogs, MG sig, sum inputs = outputs) are correct + //decodeRct: (c.f. http://eprint.iacr.org/2015/1098 section 5.1.1) + // uses the attached ecdh info to find the amounts represented by each output commitment + // must know the destination private key to find the correct amount, else will return a random number + // Note: For txn fees, the last index in the amounts vector should contain that + // Thus the amounts vector will be "one" longer than the destinations vectort + rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector & amounts, const ctkeyM &mixRing, const keyV &amount_keys, unsigned int index, ctkeyV &outSk) { + CHECK_AND_ASSERT_THROW_MES(amounts.size() == destinations.size() || amounts.size() == destinations.size() + 1, "Different number of amounts/destinations"); + CHECK_AND_ASSERT_THROW_MES(index < mixRing.size(), "Bad index into mixRing"); + for (size_t n = 0; n < mixRing.size(); ++n) { + CHECK_AND_ASSERT_THROW_MES(mixRing[n].size() == inSk.size(), "Bad mixRing size"); + } + + rctSig rv; + rv.type = RCTTypeFull; + rv.message = message; + rv.outPk.resize(destinations.size()); + rv.p.rangeSigs.resize(destinations.size()); + rv.ecdhInfo.resize(destinations.size()); + + size_t i = 0; + keyV masks(destinations.size()); //sk mask.. + outSk.resize(destinations.size()); + for (i = 0; i < destinations.size(); i++) { + //add destination to sig + rv.outPk[i].dest = copy(destinations[i]); + //compute range proof + rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, amounts[i]); + #ifdef DBG + CHECK_AND_ASSERT_THROW_MES(verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]), "verRange failed on newly created proof"); + #endif + + //mask amount and mask + rv.ecdhInfo[i].mask = copy(outSk[i].mask); + rv.ecdhInfo[i].amount = d2h(amounts[i]); + ecdhEncode(rv.ecdhInfo[i], amount_keys[i]); + + } + + //set txn fee + if (amounts.size() > destinations.size()) + { + rv.txnFee = amounts[destinations.size()]; + } + else + { + rv.txnFee = 0; + } + key txnFeeKey = scalarmultH(d2h(rv.txnFee)); + + rv.mixRing = mixRing; + rv.p.MGs.push_back(proveRctMG(get_pre_mlsag_hash(rv), rv.mixRing, inSk, outSk, rv.outPk, index, txnFeeKey)); + return rv; + } + + rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector & amounts, const keyV &amount_keys, const int mixin) { + unsigned int index; + ctkeyM mixRing; + ctkeyV outSk; + tie(mixRing, index) = populateFromBlockchain(inPk, mixin); + return genRct(message, inSk, destinations, amounts, mixRing, amount_keys, index, outSk); + } + + //RCT simple + //for post-rct only + rctSig genRctSimple(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector &inamounts, const vector &outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector & index, ctkeyV &outSk) { + CHECK_AND_ASSERT_THROW_MES(inamounts.size() > 0, "Empty inamounts"); + CHECK_AND_ASSERT_THROW_MES(inamounts.size() == inSk.size(), "Different number of inamounts/inSk"); + CHECK_AND_ASSERT_THROW_MES(outamounts.size() == destinations.size(), "Different number of amounts/destinations"); + CHECK_AND_ASSERT_THROW_MES(index.size() == inSk.size(), "Different number of index/inSk"); + CHECK_AND_ASSERT_THROW_MES(mixRing.size() == inSk.size(), "Different number of mixRing/inSk"); + for (size_t n = 0; n < mixRing.size(); ++n) { + CHECK_AND_ASSERT_THROW_MES(index[n] < mixRing[n].size(), "Bad index into mixRing"); + } + + rctSig rv; + rv.type = RCTTypeSimple; + rv.message = message; + rv.outPk.resize(destinations.size()); + rv.p.rangeSigs.resize(destinations.size()); + rv.ecdhInfo.resize(destinations.size()); + + size_t i; + keyV masks(destinations.size()); //sk mask.. + outSk.resize(destinations.size()); + key sumout = zero(); + for (i = 0; i < destinations.size(); i++) { + + //add destination to sig + rv.outPk[i].dest = copy(destinations[i]); + //compute range proof + rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, outamounts[i]); + #ifdef DBG + verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); + #endif + + sc_add(sumout.bytes, outSk[i].mask.bytes, sumout.bytes); + + //mask amount and mask + rv.ecdhInfo[i].mask = copy(outSk[i].mask); + rv.ecdhInfo[i].amount = d2h(outamounts[i]); + ecdhEncode(rv.ecdhInfo[i], amount_keys[i]); + } + + //set txn fee + rv.txnFee = txnFee; +// TODO: unused ?? +// key txnFeeKey = scalarmultH(d2h(rv.txnFee)); + rv.mixRing = mixRing; + rv.pseudoOuts.resize(inamounts.size()); + rv.p.MGs.resize(inamounts.size()); + key sumpouts = zero(); //sum pseudoOut masks + keyV a(inamounts.size()); + for (i = 0 ; i < inamounts.size() - 1; i++) { + skGen(a[i]); + sc_add(sumpouts.bytes, a[i].bytes, sumpouts.bytes); + genC(rv.pseudoOuts[i], a[i], inamounts[i]); + } + rv.mixRing = mixRing; + sc_sub(a[i].bytes, sumout.bytes, sumpouts.bytes); + genC(rv.pseudoOuts[i], a[i], inamounts[i]); + DP(rv.pseudoOuts[i]); + + key full_message = get_pre_mlsag_hash(rv); + for (i = 0 ; i < inamounts.size(); i++) { + rv.p.MGs[i] = proveRctMGSimple(full_message, rv.mixRing[i], inSk[i], a[i], rv.pseudoOuts[i], index[i]); + } + return rv; + } + + rctSig genRctSimple(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector &inamounts, const vector &outamounts, const keyV &amount_keys, xmr_amount txnFee, unsigned int mixin) { + std::vector index; + index.resize(inPk.size()); + ctkeyM mixRing; + ctkeyV outSk; + mixRing.resize(inPk.size()); + for (size_t i = 0; i < inPk.size(); ++i) { + mixRing[i].resize(mixin+1); + index[i] = populateFromBlockchainSimple(mixRing[i], inPk[i], mixin); + } + return genRctSimple(message, inSk, destinations, inamounts, outamounts, txnFee, mixRing, amount_keys, index, outSk); + } + + //RingCT protocol + //genRct: + // creates an rctSig with all data necessary to verify the rangeProofs and that the signer owns one of the + // columns that are claimed as inputs, and that the sum of inputs = sum of outputs. + // Also contains masked "amount" and "mask" so the receiver can see how much they received + //verRct: + // verifies that all signatures (rangeProogs, MG sig, sum inputs = outputs) are correct + //decodeRct: (c.f. http://eprint.iacr.org/2015/1098 section 5.1.1) + // uses the attached ecdh info to find the amounts represented by each output commitment + // must know the destination private key to find the correct amount, else will return a random number + bool verRct(const rctSig & rv) { + CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "verRct called on non-full rctSig"); + CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs"); + CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo"); + CHECK_AND_ASSERT_MES(rv.p.MGs.size() == 1, false, "full rctSig has not one MG"); + + // some rct ops can throw + try + { + size_t i = 0; + bool tmp; + DP("range proofs verified?"); + for (i = 0; i < rv.outPk.size(); i++) { + tmp = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); + DP(tmp); + if (!tmp) { + LOG_ERROR("Range proof verification failed for input " << i); + return false; + } + } + //compute txn fee + key txnFeeKey = scalarmultH(d2h(rv.txnFee)); + bool mgVerd = verRctMG(rv.p.MGs[0], rv.mixRing, rv.outPk, txnFeeKey, get_pre_mlsag_hash(rv)); + DP("mg sig verified?"); + DP(mgVerd); + if (!mgVerd) { + LOG_ERROR("MG signature verification failed"); + return false; + } + + return true; + } + catch(...) + { + return false; + } + } + + //ver RingCT simple + //assumes only post-rct style inputs (at least for max anonymity) + bool verRctSimple(const rctSig & rv) { + size_t i = 0; + + CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple, false, "verRctSimple called on non simple rctSig"); + CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs"); + CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo"); + CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.p.MGs.size(), false, "Mismatched sizes of rv.pseudoOuts and rv.p.MGs"); + CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.pseudoOuts and mixRing"); + + key sumOutpks = identity(); + for (i = 0; i < rv.outPk.size(); i++) { + if (!verRange(rv.outPk[i].mask, rv.p.rangeSigs[i])) { + LOG_ERROR("Range proof verified failed for input " << i); + return false; + } + addKeys(sumOutpks, sumOutpks, rv.outPk[i].mask); + } + DP(sumOutpks); + key txnFeeKey = scalarmultH(d2h(rv.txnFee)); + addKeys(sumOutpks, txnFeeKey, sumOutpks); + + bool tmpb = false; + key message = get_pre_mlsag_hash(rv); + key sumPseudoOuts = identity(); + for (i = 0 ; i < rv.mixRing.size() ; i++) { + tmpb = verRctMGSimple(message, rv.p.MGs[i], rv.mixRing[i], rv.pseudoOuts[i]); + addKeys(sumPseudoOuts, sumPseudoOuts, rv.pseudoOuts[i]); + DP(tmpb); + if (!tmpb) { + LOG_ERROR("verRctMGSimple failed for input " << i); + return false; + } + } + DP(sumPseudoOuts); + + //check pseudoOuts vs Outs.. + if (!equalKeys(sumPseudoOuts, sumOutpks)) { + LOG_ERROR("Sum check failed"); + return false; + } + + return true; + } + + //RingCT protocol + //genRct: + // creates an rctSig with all data necessary to verify the rangeProofs and that the signer owns one of the + // columns that are claimed as inputs, and that the sum of inputs = sum of outputs. + // Also contains masked "amount" and "mask" so the receiver can see how much they received + //verRct: + // verifies that all signatures (rangeProogs, MG sig, sum inputs = outputs) are correct + //decodeRct: (c.f. http://eprint.iacr.org/2015/1098 section 5.1.1) + // uses the attached ecdh info to find the amounts represented by each output commitment + // must know the destination private key to find the correct amount, else will return a random number + xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask) { + CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "decodeRct called on non-full rctSig"); + CHECK_AND_ASSERT_THROW_MES(rv.p.rangeSigs.size() > 0, "Empty rv.p.rangeSigs"); + CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.p.rangeSigs.size(), "Mismatched sizes of rv.outPk and rv.p.rangeSigs"); + CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index"); + + //mask amount and mask + ecdhTuple ecdh_info = rv.ecdhInfo[i]; + ecdhDecode(ecdh_info, sk); + mask = ecdh_info.mask; + key amount = ecdh_info.amount; + key C = rv.outPk[i].mask; + DP("C"); + DP(C); + key Ctmp; + addKeys2(Ctmp, mask, amount, H); + DP("Ctmp"); + DP(Ctmp); + if (equalKeys(C, Ctmp) == false) { + CHECK_AND_ASSERT_THROW_MES(false, "warning, amount decoded incorrectly, will be unable to spend"); + } + return h2d(amount); + } + + xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i) { + key mask; + return decodeRct(rv, sk, i, mask); + } + + xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key &mask) { + CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple, false, "decodeRct called on non simple rctSig"); + CHECK_AND_ASSERT_THROW_MES(rv.p.rangeSigs.size() > 0, "Empty rv.p.rangeSigs"); + CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.p.rangeSigs.size(), "Mismatched sizes of rv.outPk and rv.p.rangeSigs"); + CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index"); + + //mask amount and mask + ecdhTuple ecdh_info = rv.ecdhInfo[i]; + ecdhDecode(ecdh_info, sk); + mask = ecdh_info.mask; + key amount = ecdh_info.amount; + key C = rv.outPk[i].mask; + DP("C"); + DP(C); + key Ctmp; + addKeys2(Ctmp, mask, amount, H); + DP("Ctmp"); + DP(Ctmp); + if (equalKeys(C, Ctmp) == false) { + CHECK_AND_ASSERT_THROW_MES(false, "warning, amount decoded incorrectly, will be unable to spend"); + } + return h2d(amount); + } + + xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i) { + key mask; + return decodeRctSimple(rv, sk, i, mask); + } +} diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h new file mode 100644 index 00000000..f1c906d5 --- /dev/null +++ b/src/ringct/rctSigs.h @@ -0,0 +1,150 @@ +// Copyright (c) 2016, Monero Research Labs +// +// Author: Shen Noether +// +// 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. + +#pragma once + +//#define DBG + +#ifndef RCTSIGS_H +#define RCTSIGS_H + +#include +#include +#include +#include + +#include "crypto/generic-ops.h" + +extern "C" { +#include "crypto/random.h" +#include "crypto/keccak.h" +} +#include "crypto/crypto.h" + + +#include "rctTypes.h" +#include "rctOps.h" + +//Define this flag when debugging to get additional info on the console +#ifdef DBG +#define DP(x) dp(x) +#else +#define DP(x) +#endif + + + +using namespace std; +using namespace crypto; + +namespace rct { + + //Schnorr Non-linkable + //Gen Gives a signature (L1, s1, s2) proving that the sender knows "x" such that xG = one of P1 or P2 + //Ver Verifies that signer knows an "x" such that xG = one of P1 or P2 + //These are called in the below ASNL sig generation + void GenSchnorrNonLinkable(key & L1, key & s1, key & s2, const key & x, const key & P1, const key & P2, int index); + bool VerSchnorrNonLinkable(const key & P1, const key & P2, const key & L1, const key & s1, const key & s2); + + //Aggregate Schnorr Non-linkable Ring Signature (ASNL) + // c.f. http://eprint.iacr.org/2015/1098 section 5. + // These are used in range proofs (alternatively Borromean could be used) + // Gen gives a signature which proves the signer knows, for each i, + // an x[i] such that x[i]G = one of P1[i] or P2[i] + // Ver Verifies the signer knows a key for one of P1[i], P2[i] at each i + asnlSig GenASNL(key64 x, key64 P1, key64 P2, bits indices); + bool VerASNL(const key64 P1, const key64 P2, const asnlSig &as); + + //Multilayered Spontaneous Anonymous Group Signatures (MLSAG signatures) + //These are aka MG signatutes in earlier drafts of the ring ct paper + // c.f. http://eprint.iacr.org/2015/1098 section 2. + // keyImageV just does I[i] = xx[i] * HashToPoint(xx[i] * G) for each i + // Gen creates a signature which proves that for some column in the keymatrix "pk" + // the signer knows a secret key for each row in that column + // Ver verifies that the MG sig was created correctly + keyV keyImageV(const keyV &xx); + mgSig MLSAG_Gen(key message, const keyM & pk, const keyV & xx, const unsigned int index, size_t dsRows); + bool MLSAG_Ver(key message, const keyM &pk, const mgSig &sig, size_t dsRows); + //mgSig MLSAG_Gen_Old(const keyM & pk, const keyV & xx, const int index); + + //proveRange and verRange + //proveRange gives C, and mask such that \sumCi = C + // c.f. http://eprint.iacr.org/2015/1098 section 5.1 + // and Ci is a commitment to either 0 or 2^i, i=0,...,63 + // thus this proves that "amount" is in [0, 2^64] + // mask is a such that C = aG + bH, and b = amount + //verRange verifies that \sum Ci = C and that each Ci is a commitment to 0 or 2^i + rangeSig proveRange(key & C, key & mask, const xmr_amount & amount); + bool verRange(const key & C, const rangeSig & as); + + //Ring-ct MG sigs + //Prove: + // c.f. http://eprint.iacr.org/2015/1098 section 4. definition 10. + // This does the MG sig on the "dest" part of the given key matrix, and + // the last row is the sum of input commitments from that column - sum output commitments + // this shows that sum inputs = sum outputs + //Ver: + // verifies the above sig is created corretly + mgSig proveRctMG(const ctkeyM & pubs, const ctkeyV & inSk, const keyV &outMasks, const ctkeyV & outPk, unsigned int index, key txnFee, const key &message); + mgSig proveRctMGSimple(const key & message, const ctkeyV & pubs, const ctkey & inSk, const key &a , const key &Cout, unsigned int index); + bool verRctMG(const mgSig &mg, const ctkeyM & pubs, const ctkeyV & outPk, key txnFee, const key &message); + bool verRctMGSimple(const key &message, const mgSig &mg, const ctkeyV & pubs, const key & C); + + //These functions get keys from blockchain + //replace these when connecting blockchain + //getKeyFromBlockchain grabs a key from the blockchain at "reference_index" to mix with + //populateFromBlockchain creates a keymatrix with "mixin" columns and one of the columns is inPk + // the return value are the key matrix, and the index where inPk was put (random). + void getKeyFromBlockchain(ctkey & a, size_t reference_index); + tuple populateFromBlockchain(ctkeyV inPk, int mixin); + + //RingCT protocol + //genRct: + // creates an rctSig with all data necessary to verify the rangeProofs and that the signer owns one of the + // columns that are claimed as inputs, and that the sum of inputs = sum of outputs. + // Also contains masked "amount" and "mask" so the receiver can see how much they received + //verRct: + // verifies that all signatures (rangeProogs, MG sig, sum inputs = outputs) are correct + //decodeRct: (c.f. http://eprint.iacr.org/2015/1098 section 5.1.1) + // uses the attached ecdh info to find the amounts represented by each output commitment + // must know the destination private key to find the correct amount, else will return a random number + rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector & amounts, const ctkeyM &mixRing, const keyV &amount_keys, unsigned int index, ctkeyV &outSk); + rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector & amounts, const keyV &amount_keys, const int mixin); + rctSig genRctSimple(const key & message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector & inamounts, const vector & outamounts, const keyV &amount_keys, xmr_amount txnFee, unsigned int mixin); + rctSig genRctSimple(const key & message, const ctkeyV & inSk, const keyV & destinations, const vector & inamounts, const vector & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector & index, ctkeyV &outSk); + bool verRct(const rctSig & rv); + bool verRctSimple(const rctSig & rv); + xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask); + xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i); + xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key & mask); + xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i); +} +#endif /* RCTSIGS_H */ + diff --git a/src/ringct/rctTypes.cpp b/src/ringct/rctTypes.cpp new file mode 100644 index 00000000..e773c604 --- /dev/null +++ b/src/ringct/rctTypes.cpp @@ -0,0 +1,208 @@ +// Copyright (c) 2016, Monero Research Labs +// +// Author: Shen Noether +// +// 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. + +#include "rctTypes.h" +using namespace crypto; +using namespace std; + +namespace rct { + + //dp + //Debug printing for the above types + //Actually use DP(value) and #define DBG + + void dp(key a) { + int j = 0; + printf("\""); + for (j = 0; j < 32; j++) { + printf("%02x", (unsigned char)a.bytes[j]); + } + printf("\""); + printf("\n"); + } + + void dp(bool a) { + printf(" ... %s ... ", a ? "true" : "false"); + printf("\n"); + } + + void dp(const char * a, int l) { + int j = 0; + printf("\""); + for (j = 0; j < l; j++) { + printf("%02x", (unsigned char)a[j]); + } + printf("\""); + printf("\n"); + } + void dp(keyV a) { + size_t j = 0; + printf("["); + for (j = 0; j < a.size(); j++) { + dp(a[j]); + if (j < a.size() - 1) { + printf(","); + } + } + printf("]"); + printf("\n"); + } + void dp(keyM a) { + size_t j = 0; + printf("["); + for (j = 0; j < a.size(); j++) { + dp(a[j]); + if (j < a.size() - 1) { + printf(","); + } + } + printf("]"); + printf("\n"); + } + void dp(xmr_amount vali) { + printf("x: "); + std::cout << vali; + printf("\n\n"); + } + + void dp(int vali) { + printf("x: %d\n", vali); + printf("\n"); + } + void dp(bits amountb) { + for (int i = 0; i < 64; i++) { + printf("%d", amountb[i]); + } + printf("\n"); + + } + + void dp(const char * st) { + printf("%s\n", st); + } + + //Various Conversions + + //uint long long to 32 byte key + void d2h(key & amounth, const xmr_amount in) { + sc_0(amounth.bytes); + xmr_amount val = in; + int i = 0; + while (val != 0) { + amounth[i] = (unsigned char)(val & 0xFF); + i++; + val /= (xmr_amount)256; + } + } + + //uint long long to 32 byte key + key d2h(const xmr_amount in) { + key amounth; + sc_0(amounth.bytes); + xmr_amount val = in; + int i = 0; + while (val != 0) { + amounth[i] = (unsigned char)(val & 0xFF); + i++; + val /= (xmr_amount)256; + } + return amounth; + } + + //uint long long to int[64] + void d2b(bits amountb, xmr_amount val) { + int i = 0; + while (val != 0) { + amountb[i] = val & 1; + i++; + val >>= 1; + } + while (i < 64) { + amountb[i] = 0; + i++; + } + } + + //32 byte key to uint long long + // if the key holds a value > 2^64 + // then the value in the first 8 bytes is returned + xmr_amount h2d(const key & test) { + xmr_amount vali = 0; + int j = 0; + for (j = 7; j >= 0; j--) { + vali = (xmr_amount)(vali * 256 + (unsigned char)test.bytes[j]); + } + return vali; + } + + //32 byte key to int[64] + void h2b(bits amountb2, const key & test) { + int val = 0, i = 0, j = 0; + for (j = 0; j < 8; j++) { + val = (unsigned char)test.bytes[j]; + i = 8 * j; + while (val != 0) { + amountb2[i] = val & 1; + i++; + val >>= 1; + } + while (i < 8 * (j + 1)) { + amountb2[i] = 0; + } + } + } + + //int[64] to 32 byte key + void b2h(key & amountdh, const bits amountb2) { + int byte, i, j; + for (j = 0; j < 8; j++) { + byte = 0; + i = 8 * j; + for (i = 7; i > -1; i--) { + byte = byte * 2 + amountb2[8 * j + i]; + } + amountdh[j] = (unsigned char)byte; + } + for (j = 8; j < 32; j++) { + amountdh[j] = (unsigned char)(0x00); + } + } + + //int[64] to uint long long + xmr_amount b2d(bits amountb) { + xmr_amount vali = 0; + int j = 0; + for (j = 63; j >= 0; j--) { + vali = (xmr_amount)(vali * 2 + amountb[j]); + } + return vali; + } + +} diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h new file mode 100644 index 00000000..f231d30f --- /dev/null +++ b/src/ringct/rctTypes.h @@ -0,0 +1,405 @@ +// Copyright (c) 2016, Monero Research Labs +// +// Author: Shen Noether +// +// 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. + +#pragma once +#ifndef RCT_TYPES_H +#define RCT_TYPES_H + +#include +#include +#include +#include +#include +#include + +extern "C" { +#include "crypto/generic-ops.h" +#include "crypto/crypto-ops.h" +#include "crypto/random.h" +#include "crypto/keccak.h" +} +#include "crypto/crypto.h" + +#include "serialization/serialization.h" +#include "serialization/debug_archive.h" +#include "serialization/binary_archive.h" +#include "serialization/json_archive.h" + + +//Define this flag when debugging to get additional info on the console +#ifdef DBG +#define DP(x) dp(x) +#else +#define DP(x) +#endif + +//atomic units of moneros +#define ATOMS 64 + +//for printing large ints + +using namespace std; +using namespace crypto; + +//Namespace specifically for ring ct code +namespace rct { + //basic ops containers + typedef unsigned char * Bytes; + + // Can contain a secret or public key + // similar to secret_key / public_key of crypto-ops, + // but uses unsigned chars, + // also includes an operator for accessing the i'th byte. + struct key { + unsigned char & operator[](int i) { + return bytes[i]; + } + unsigned char operator[](int i) const { + return bytes[i]; + } + bool operator==(const key &k) const { return !memcmp(bytes, k.bytes, sizeof(bytes)); } + unsigned char bytes[32]; + }; + typedef vector keyV; //vector of keys + typedef vector keyM; //matrix of keys (indexed by column first) + + //containers For CT operations + //if it's representing a private ctkey then "dest" contains the secret key of the address + // while "mask" contains a where C = aG + bH is CT pedersen commitment and b is the amount + // (store b, the amount, separately + //if it's representing a public ctkey, then "dest" = P the address, mask = C the commitment + struct ctkey { + key dest; + key mask; //C here if public + }; + typedef vector ctkeyV; + typedef vector ctkeyM; + + //data for passing the amount to the receiver secretly + // If the pedersen commitment to an amount is C = aG + bH, + // "mask" contains a 32 byte key a + // "amount" contains a hex representation (in 32 bytes) of a 64 bit number + // "senderPk" is not the senders actual public key, but a one-time public key generated for + // the purpose of the ECDH exchange + struct ecdhTuple { + key mask; + key amount; + key senderPk; + + BEGIN_SERIALIZE_OBJECT() + FIELD(mask) + FIELD(amount) + // FIELD(senderPk) // not serialized, as we do not use it in monero currently + END_SERIALIZE() + }; + + //containers for representing amounts + typedef uint64_t xmr_amount; + typedef unsigned int bits[ATOMS]; + typedef key key64[64]; + + //just contains the necessary keys to represent asnlSigs + //c.f. http://eprint.iacr.org/2015/1098 + struct asnlSig { + key64 L1; + key64 s2; + key s; + }; + + //Container for precomp + struct geDsmp { + ge_dsmp k; + }; + + //just contains the necessary keys to represent MLSAG sigs + //c.f. http://eprint.iacr.org/2015/1098 + struct mgSig { + keyM ss; + key cc; + keyV II; + + BEGIN_SERIALIZE_OBJECT() + FIELD(ss) + FIELD(cc) + // FIELD(II) - not serialized, it can be reconstructed + END_SERIALIZE() + }; + //contains the data for an asnl sig + // also contains the "Ci" values such that + // \sum Ci = C + // and the signature proves that each Ci is either + // a Pedersen commitment to 0 or to 2^i + //thus proving that C is in the range of [0, 2^64] + struct rangeSig { + asnlSig asig; + key64 Ci; + + BEGIN_SERIALIZE_OBJECT() + FIELD(asig) + FIELD(Ci) + END_SERIALIZE() + }; + //A container to hold all signatures necessary for RingCT + // rangeSigs holds all the rangeproof data of a transaction + // MG holds the MLSAG signature of a transaction + // mixRing holds all the public keypairs (P, C) for a transaction + // ecdhInfo holds an encoded mask / amount to be passed to each receiver + // outPk contains public keypairs which are destinations (P, C), + // P = address, C = commitment to amount + enum { + RCTTypeNull = 0, + RCTTypeFull = 1, + RCTTypeSimple = 2, + }; + struct rctSigBase { + uint8_t type; + key message; + ctkeyM mixRing; //the set of all pubkeys / copy + //pairs that you mix with + keyV pseudoOuts; //C - for simple rct + vector ecdhInfo; + ctkeyV outPk; + xmr_amount txnFee; // contains b + + BEGIN_SERIALIZE() + FIELD(type) + if (type == RCTTypeNull) + return true; + // FIELD(message) - not serialized, it can be reconstructed + // FIELD(mixRing) - not serialized, it can be reconstructed + if (type == RCTTypeSimple) + FIELD(pseudoOuts) + FIELD(ecdhInfo) + if (typename Archive::is_saving()) { + keyV outPk(this->outPk.size()); + for (size_t n = 0; n < outPk.size(); ++n) + outPk[n] = this->outPk[n].mask; + FIELD(outPk) + } + else { + keyV outPk; + FIELD(outPk) + this->outPk.resize(outPk.size()); + for (size_t n = 0; n < outPk.size(); ++n) + this->outPk[n].mask = outPk[n]; + } + VARINT_FIELD(txnFee) + END_SERIALIZE() + }; + struct rctSigPrunable { + vector rangeSigs; + vector MGs; // simple rct has N, full has 1 + + BEGIN_SERIALIZE() + FIELD(rangeSigs) + FIELD(MGs) + END_SERIALIZE() + }; + struct rctSig: public rctSigBase { + rctSigPrunable p; + + BEGIN_SERIALIZE_OBJECT() + FIELDS(*static_cast(this)) + if (type == RCTTypeNull) + return true; + FIELDS(p); + END_SERIALIZE() + }; + + //other basepoint H = toPoint(cn_fast_hash(G)), G the basepoint + static const key H = { {0x8b, 0x65, 0x59, 0x70, 0x15, 0x37, 0x99, 0xaf, 0x2a, 0xea, 0xdc, 0x9f, 0xf1, 0xad, 0xd0, 0xea, 0x6c, 0x72, 0x51, 0xd5, 0x41, 0x54, 0xcf, 0xa9, 0x2c, 0x17, 0x3a, 0x0d, 0xd3, 0x9c, 0x1f, 0x94} }; + + //H2 contains 2^i H in each index, i.e. H, 2H, 4H, 8H, ... + //This is used for the range proofG + //You can regenerate this by running python2 Test.py HPow2 in the MiniNero repo + static const key64 H2 = {{0x8b, 0x65, 0x59, 0x70, 0x15, 0x37, 0x99, 0xaf, 0x2a, 0xea, 0xdc, 0x9f, 0xf1, 0xad, 0xd0, 0xea, 0x6c, 0x72, 0x51, 0xd5, 0x41, 0x54, 0xcf, 0xa9, 0x2c, 0x17, 0x3a, 0x0d, 0xd3, 0x9c, 0x1f, 0x94}, + {0x8f, 0xaa, 0x44, 0x8a, 0xe4, 0xb3, 0xe2, 0xbb, 0x3d, 0x4d, 0x13, 0x09, 0x09, 0xf5, 0x5f, 0xcd, 0x79, 0x71, 0x1c, 0x1c, 0x83, 0xcd, 0xbc, 0xca, 0xdd, 0x42, 0xcb, 0xe1, 0x51, 0x5e, 0x87, 0x12}, + {0x12, 0xa7, 0xd6, 0x2c, 0x77, 0x91, 0x65, 0x4a, 0x57, 0xf3, 0xe6, 0x76, 0x94, 0xed, 0x50, 0xb4, 0x9a, 0x7d, 0x9e, 0x3f, 0xc1, 0xe4, 0xc7, 0xa0, 0xbd, 0xe2, 0x9d, 0x18, 0x7e, 0x9c, 0xc7, 0x1d}, + {0x78, 0x9a, 0xb9, 0x93, 0x4b, 0x49, 0xc4, 0xf9, 0xe6, 0x78, 0x5c, 0x6d, 0x57, 0xa4, 0x98, 0xb3, 0xea, 0xd4, 0x43, 0xf0, 0x4f, 0x13, 0xdf, 0x11, 0x0c, 0x54, 0x27, 0xb4, 0xf2, 0x14, 0xc7, 0x39}, + {0x77, 0x1e, 0x92, 0x99, 0xd9, 0x4f, 0x02, 0xac, 0x72, 0xe3, 0x8e, 0x44, 0xde, 0x56, 0x8a, 0xc1, 0xdc, 0xb2, 0xed, 0xc6, 0xed, 0xb6, 0x1f, 0x83, 0xca, 0x41, 0x8e, 0x10, 0x77, 0xce, 0x3d, 0xe8}, + {0x73, 0xb9, 0x6d, 0xb4, 0x30, 0x39, 0x81, 0x9b, 0xda, 0xf5, 0x68, 0x0e, 0x5c, 0x32, 0xd7, 0x41, 0x48, 0x88, 0x84, 0xd1, 0x8d, 0x93, 0x86, 0x6d, 0x40, 0x74, 0xa8, 0x49, 0x18, 0x2a, 0x8a, 0x64}, + {0x8d, 0x45, 0x8e, 0x1c, 0x2f, 0x68, 0xeb, 0xeb, 0xcc, 0xd2, 0xfd, 0x5d, 0x37, 0x9f, 0x5e, 0x58, 0xf8, 0x13, 0x4d, 0xf3, 0xe0, 0xe8, 0x8c, 0xad, 0x3d, 0x46, 0x70, 0x10, 0x63, 0xa8, 0xd4, 0x12}, + {0x09, 0x55, 0x1e, 0xdb, 0xe4, 0x94, 0x41, 0x8e, 0x81, 0x28, 0x44, 0x55, 0xd6, 0x4b, 0x35, 0xee, 0x8a, 0xc0, 0x93, 0x06, 0x8a, 0x5f, 0x16, 0x1f, 0xa6, 0x63, 0x75, 0x59, 0x17, 0x7e, 0xf4, 0x04}, + {0xd0, 0x5a, 0x88, 0x66, 0xf4, 0xdf, 0x8c, 0xee, 0x1e, 0x26, 0x8b, 0x1d, 0x23, 0xa4, 0xc5, 0x8c, 0x92, 0xe7, 0x60, 0x30, 0x97, 0x86, 0xcd, 0xac, 0x0f, 0xed, 0xa1, 0xd2, 0x47, 0xa9, 0xc9, 0xa7}, + {0x55, 0xcd, 0xaa, 0xd5, 0x18, 0xbd, 0x87, 0x1d, 0xd1, 0xeb, 0x7b, 0xc7, 0x02, 0x3e, 0x1d, 0xc0, 0xfd, 0xf3, 0x33, 0x98, 0x64, 0xf8, 0x8f, 0xdd, 0x2d, 0xe2, 0x69, 0xfe, 0x9e, 0xe1, 0x83, 0x2d}, + {0xe7, 0x69, 0x7e, 0x95, 0x1a, 0x98, 0xcf, 0xd5, 0x71, 0x2b, 0x84, 0xbb, 0xe5, 0xf3, 0x4e, 0xd7, 0x33, 0xe9, 0x47, 0x3f, 0xcb, 0x68, 0xed, 0xa6, 0x6e, 0x37, 0x88, 0xdf, 0x19, 0x58, 0xc3, 0x06}, + {0xf9, 0x2a, 0x97, 0x0b, 0xae, 0x72, 0x78, 0x29, 0x89, 0xbf, 0xc8, 0x3a, 0xdf, 0xaa, 0x92, 0xa4, 0xf4, 0x9c, 0x7e, 0x95, 0x91, 0x8b, 0x3b, 0xba, 0x3c, 0xdc, 0x7f, 0xe8, 0x8a, 0xcc, 0x8d, 0x47}, + {0x1f, 0x66, 0xc2, 0xd4, 0x91, 0xd7, 0x5a, 0xf9, 0x15, 0xc8, 0xdb, 0x6a, 0x6d, 0x1c, 0xb0, 0xcd, 0x4f, 0x7d, 0xdc, 0xd5, 0xe6, 0x3d, 0x3b, 0xa9, 0xb8, 0x3c, 0x86, 0x6c, 0x39, 0xef, 0x3a, 0x2b}, + {0x3e, 0xec, 0x98, 0x84, 0xb4, 0x3f, 0x58, 0xe9, 0x3e, 0xf8, 0xde, 0xea, 0x26, 0x00, 0x04, 0xef, 0xea, 0x2a, 0x46, 0x34, 0x4f, 0xc5, 0x96, 0x5b, 0x1a, 0x7d, 0xd5, 0xd1, 0x89, 0x97, 0xef, 0xa7}, + {0xb2, 0x9f, 0x8f, 0x0c, 0xcb, 0x96, 0x97, 0x7f, 0xe7, 0x77, 0xd4, 0x89, 0xd6, 0xbe, 0x9e, 0x7e, 0xbc, 0x19, 0xc4, 0x09, 0xb5, 0x10, 0x35, 0x68, 0xf2, 0x77, 0x61, 0x1d, 0x7e, 0xa8, 0x48, 0x94}, + {0x56, 0xb1, 0xf5, 0x12, 0x65, 0xb9, 0x55, 0x98, 0x76, 0xd5, 0x8d, 0x24, 0x9d, 0x0c, 0x14, 0x6d, 0x69, 0xa1, 0x03, 0x63, 0x66, 0x99, 0x87, 0x4d, 0x3f, 0x90, 0x47, 0x35, 0x50, 0xfe, 0x3f, 0x2c}, + {0x1d, 0x7a, 0x36, 0x57, 0x5e, 0x22, 0xf5, 0xd1, 0x39, 0xff, 0x9c, 0xc5, 0x10, 0xfa, 0x13, 0x85, 0x05, 0x57, 0x6b, 0x63, 0x81, 0x5a, 0x94, 0xe4, 0xb0, 0x12, 0xbf, 0xd4, 0x57, 0xca, 0xaa, 0xda}, + {0xd0, 0xac, 0x50, 0x7a, 0x86, 0x4e, 0xcd, 0x05, 0x93, 0xfa, 0x67, 0xbe, 0x7d, 0x23, 0x13, 0x43, 0x92, 0xd0, 0x0e, 0x40, 0x07, 0xe2, 0x53, 0x48, 0x78, 0xd9, 0xb2, 0x42, 0xe1, 0x0d, 0x76, 0x20}, + {0xf6, 0xc6, 0x84, 0x0b, 0x9c, 0xf1, 0x45, 0xbb, 0x2d, 0xcc, 0xf8, 0x6e, 0x94, 0x0b, 0xe0, 0xfc, 0x09, 0x8e, 0x32, 0xe3, 0x10, 0x99, 0xd5, 0x6f, 0x7f, 0xe0, 0x87, 0xbd, 0x5d, 0xeb, 0x50, 0x94}, + {0x28, 0x83, 0x1a, 0x33, 0x40, 0x07, 0x0e, 0xb1, 0xdb, 0x87, 0xc1, 0x2e, 0x05, 0x98, 0x0d, 0x5f, 0x33, 0xe9, 0xef, 0x90, 0xf8, 0x3a, 0x48, 0x17, 0xc9, 0xf4, 0xa0, 0xa3, 0x32, 0x27, 0xe1, 0x97}, + {0x87, 0x63, 0x22, 0x73, 0xd6, 0x29, 0xcc, 0xb7, 0xe1, 0xed, 0x1a, 0x76, 0x8f, 0xa2, 0xeb, 0xd5, 0x17, 0x60, 0xf3, 0x2e, 0x1c, 0x0b, 0x86, 0x7a, 0x5d, 0x36, 0x8d, 0x52, 0x71, 0x05, 0x5c, 0x6e}, + {0x5c, 0x7b, 0x29, 0x42, 0x43, 0x47, 0x96, 0x4d, 0x04, 0x27, 0x55, 0x17, 0xc5, 0xae, 0x14, 0xb6, 0xb5, 0xea, 0x27, 0x98, 0xb5, 0x73, 0xfc, 0x94, 0xe6, 0xe4, 0x4a, 0x53, 0x21, 0x60, 0x0c, 0xfb}, + {0xe6, 0x94, 0x50, 0x42, 0xd7, 0x8b, 0xc2, 0xc3, 0xbd, 0x6e, 0xc5, 0x8c, 0x51, 0x1a, 0x9f, 0xe8, 0x59, 0xc0, 0xad, 0x63, 0xfd, 0xe4, 0x94, 0xf5, 0x03, 0x9e, 0x0e, 0x82, 0x32, 0x61, 0x2b, 0xd5}, + {0x36, 0xd5, 0x69, 0x07, 0xe2, 0xec, 0x74, 0x5d, 0xb6, 0xe5, 0x4f, 0x0b, 0x2e, 0x1b, 0x23, 0x00, 0xab, 0xcb, 0x42, 0x2e, 0x71, 0x2d, 0xa5, 0x88, 0xa4, 0x0d, 0x3f, 0x1e, 0xbb, 0xbe, 0x02, 0xf6}, + {0x34, 0xdb, 0x6e, 0xe4, 0xd0, 0x60, 0x8e, 0x5f, 0x78, 0x36, 0x50, 0x49, 0x5a, 0x3b, 0x2f, 0x52, 0x73, 0xc5, 0x13, 0x4e, 0x52, 0x84, 0xe4, 0xfd, 0xf9, 0x66, 0x27, 0xbb, 0x16, 0xe3, 0x1e, 0x6b}, + {0x8e, 0x76, 0x59, 0xfb, 0x45, 0xa3, 0x78, 0x7d, 0x67, 0x4a, 0xe8, 0x67, 0x31, 0xfa, 0xa2, 0x53, 0x8e, 0xc0, 0xfd, 0xf4, 0x42, 0xab, 0x26, 0xe9, 0xc7, 0x91, 0xfa, 0xda, 0x08, 0x94, 0x67, 0xe9}, + {0x30, 0x06, 0xcf, 0x19, 0x8b, 0x24, 0xf3, 0x1b, 0xb4, 0xc7, 0xe6, 0x34, 0x60, 0x00, 0xab, 0xc7, 0x01, 0xe8, 0x27, 0xcf, 0xbb, 0x5d, 0xf5, 0x2d, 0xcf, 0xa4, 0x2e, 0x9c, 0xa9, 0xff, 0x08, 0x02}, + {0xf5, 0xfd, 0x40, 0x3c, 0xb6, 0xe8, 0xbe, 0x21, 0x47, 0x2e, 0x37, 0x7f, 0xfd, 0x80, 0x5a, 0x8c, 0x60, 0x83, 0xea, 0x48, 0x03, 0xb8, 0x48, 0x53, 0x89, 0xcc, 0x3e, 0xbc, 0x21, 0x5f, 0x00, 0x2a}, + {0x37, 0x31, 0xb2, 0x60, 0xeb, 0x3f, 0x94, 0x82, 0xe4, 0x5f, 0x1c, 0x3f, 0x3b, 0x9d, 0xcf, 0x83, 0x4b, 0x75, 0xe6, 0xee, 0xf8, 0xc4, 0x0f, 0x46, 0x1e, 0xa2, 0x7e, 0x8b, 0x6e, 0xd9, 0x47, 0x3d}, + {0x9f, 0x9d, 0xab, 0x09, 0xc3, 0xf5, 0xe4, 0x28, 0x55, 0xc2, 0xde, 0x97, 0x1b, 0x65, 0x93, 0x28, 0xa2, 0xdb, 0xc4, 0x54, 0x84, 0x5f, 0x39, 0x6f, 0xfc, 0x05, 0x3f, 0x0b, 0xb1, 0x92, 0xf8, 0xc3}, + {0x5e, 0x05, 0x5d, 0x25, 0xf8, 0x5f, 0xdb, 0x98, 0xf2, 0x73, 0xe4, 0xaf, 0xe0, 0x84, 0x64, 0xc0, 0x03, 0xb7, 0x0f, 0x1e, 0xf0, 0x67, 0x7b, 0xb5, 0xe2, 0x57, 0x06, 0x40, 0x0b, 0xe6, 0x20, 0xa5}, + {0x86, 0x8b, 0xcf, 0x36, 0x79, 0xcb, 0x6b, 0x50, 0x0b, 0x94, 0x41, 0x8c, 0x0b, 0x89, 0x25, 0xf9, 0x86, 0x55, 0x30, 0x30, 0x3a, 0xe4, 0xe4, 0xb2, 0x62, 0x59, 0x18, 0x65, 0x66, 0x6a, 0x45, 0x90}, + {0xb3, 0xdb, 0x6b, 0xd3, 0x89, 0x7a, 0xfb, 0xd1, 0xdf, 0x3f, 0x96, 0x44, 0xab, 0x21, 0xc8, 0x05, 0x0e, 0x1f, 0x00, 0x38, 0xa5, 0x2f, 0x7c, 0xa9, 0x5a, 0xc0, 0xc3, 0xde, 0x75, 0x58, 0xcb, 0x7a}, + {0x81, 0x19, 0xb3, 0xa0, 0x59, 0xff, 0x2c, 0xac, 0x48, 0x3e, 0x69, 0xbc, 0xd4, 0x1d, 0x6d, 0x27, 0x14, 0x94, 0x47, 0x91, 0x42, 0x88, 0xbb, 0xea, 0xee, 0x34, 0x13, 0xe6, 0xdc, 0xc6, 0xd1, 0xeb}, + {0x10, 0xfc, 0x58, 0xf3, 0x5f, 0xc7, 0xfe, 0x7a, 0xe8, 0x75, 0x52, 0x4b, 0xb5, 0x85, 0x00, 0x03, 0x00, 0x5b, 0x7f, 0x97, 0x8c, 0x0c, 0x65, 0xe2, 0xa9, 0x65, 0x46, 0x4b, 0x6d, 0x00, 0x81, 0x9c}, + {0x5a, 0xcd, 0x94, 0xeb, 0x3c, 0x57, 0x83, 0x79, 0xc1, 0xea, 0x58, 0xa3, 0x43, 0xec, 0x4f, 0xcf, 0xf9, 0x62, 0x77, 0x6f, 0xe3, 0x55, 0x21, 0xe4, 0x75, 0xa0, 0xe0, 0x6d, 0x88, 0x7b, 0x2d, 0xb9}, + {0x33, 0xda, 0xf3, 0xa2, 0x14, 0xd6, 0xe0, 0xd4, 0x2d, 0x23, 0x00, 0xa7, 0xb4, 0x4b, 0x39, 0x29, 0x0d, 0xb8, 0x98, 0x9b, 0x42, 0x79, 0x74, 0xcd, 0x86, 0x5d, 0xb0, 0x11, 0x05, 0x5a, 0x29, 0x01}, + {0xcf, 0xc6, 0x57, 0x2f, 0x29, 0xaf, 0xd1, 0x64, 0xa4, 0x94, 0xe6, 0x4e, 0x6f, 0x1a, 0xeb, 0x82, 0x0c, 0x3e, 0x7d, 0xa3, 0x55, 0x14, 0x4e, 0x51, 0x24, 0xa3, 0x91, 0xd0, 0x6e, 0x9f, 0x95, 0xea}, + {0xd5, 0x31, 0x2a, 0x4b, 0x0e, 0xf6, 0x15, 0xa3, 0x31, 0xf6, 0x35, 0x2c, 0x2e, 0xd2, 0x1d, 0xac, 0x9e, 0x7c, 0x36, 0x39, 0x8b, 0x93, 0x9a, 0xec, 0x90, 0x1c, 0x25, 0x7f, 0x6c, 0xbc, 0x9e, 0x8e}, + {0x55, 0x1d, 0x67, 0xfe, 0xfc, 0x7b, 0x5b, 0x9f, 0x9f, 0xdb, 0xf6, 0xaf, 0x57, 0xc9, 0x6c, 0x8a, 0x74, 0xd7, 0xe4, 0x5a, 0x00, 0x20, 0x78, 0xa7, 0xb5, 0xba, 0x45, 0xc6, 0xfd, 0xe9, 0x3e, 0x33}, + {0xd5, 0x0a, 0xc7, 0xbd, 0x5c, 0xa5, 0x93, 0xc6, 0x56, 0x92, 0x8f, 0x38, 0x42, 0x80, 0x17, 0xfc, 0x7b, 0xa5, 0x02, 0x85, 0x4c, 0x43, 0xd8, 0x41, 0x49, 0x50, 0xe9, 0x6e, 0xcb, 0x40, 0x5d, 0xc3}, + {0x07, 0x73, 0xe1, 0x8e, 0xa1, 0xbe, 0x44, 0xfe, 0x1a, 0x97, 0xe2, 0x39, 0x57, 0x3c, 0xfa, 0xe3, 0xe4, 0xe9, 0x5e, 0xf9, 0xaa, 0x9f, 0xaa, 0xbe, 0xac, 0x12, 0x74, 0xd3, 0xad, 0x26, 0x16, 0x04}, + {0xe9, 0xaf, 0x0e, 0x7c, 0xa8, 0x93, 0x30, 0xd2, 0xb8, 0x61, 0x5d, 0x1b, 0x41, 0x37, 0xca, 0x61, 0x7e, 0x21, 0x29, 0x7f, 0x2f, 0x0d, 0xed, 0x8e, 0x31, 0xb7, 0xd2, 0xea, 0xd8, 0x71, 0x46, 0x60}, + {0x7b, 0x12, 0x45, 0x83, 0x09, 0x7f, 0x10, 0x29, 0xa0, 0xc7, 0x41, 0x91, 0xfe, 0x73, 0x78, 0xc9, 0x10, 0x5a, 0xcc, 0x70, 0x66, 0x95, 0xed, 0x14, 0x93, 0xbb, 0x76, 0x03, 0x42, 0x26, 0xa5, 0x7b}, + {0xec, 0x40, 0x05, 0x7b, 0x99, 0x54, 0x76, 0x65, 0x0b, 0x3d, 0xb9, 0x8e, 0x9d, 0xb7, 0x57, 0x38, 0xa8, 0xcd, 0x2f, 0x94, 0xd8, 0x63, 0xb9, 0x06, 0x15, 0x0c, 0x56, 0xaa, 0xc1, 0x9c, 0xaa, 0x6b}, + {0x01, 0xd9, 0xff, 0x72, 0x9e, 0xfd, 0x39, 0xd8, 0x37, 0x84, 0xc0, 0xfe, 0x59, 0xc4, 0xae, 0x81, 0xa6, 0x70, 0x34, 0xcb, 0x53, 0xc9, 0x43, 0xfb, 0x81, 0x8b, 0x9d, 0x8a, 0xe7, 0xfc, 0x33, 0xe5}, + {0x00, 0xdf, 0xb3, 0xc6, 0x96, 0x32, 0x8c, 0x76, 0x42, 0x45, 0x19, 0xa7, 0xbe, 0xfe, 0x8e, 0x0f, 0x6c, 0x76, 0xf9, 0x47, 0xb5, 0x27, 0x67, 0x91, 0x6d, 0x24, 0x82, 0x3f, 0x73, 0x5b, 0xaf, 0x2e}, + {0x46, 0x1b, 0x79, 0x9b, 0x4d, 0x9c, 0xee, 0xa8, 0xd5, 0x80, 0xdc, 0xb7, 0x6d, 0x11, 0x15, 0x0d, 0x53, 0x5e, 0x16, 0x39, 0xd1, 0x60, 0x03, 0xc3, 0xfb, 0x7e, 0x9d, 0x1f, 0xd1, 0x30, 0x83, 0xa8}, + {0xee, 0x03, 0x03, 0x94, 0x79, 0xe5, 0x22, 0x8f, 0xdc, 0x55, 0x1c, 0xbd, 0xe7, 0x07, 0x9d, 0x34, 0x12, 0xea, 0x18, 0x6a, 0x51, 0x7c, 0xcc, 0x63, 0xe4, 0x6e, 0x9f, 0xcc, 0xe4, 0xfe, 0x3a, 0x6c}, + {0xa8, 0xcf, 0xb5, 0x43, 0x52, 0x4e, 0x7f, 0x02, 0xb9, 0xf0, 0x45, 0xac, 0xd5, 0x43, 0xc2, 0x1c, 0x37, 0x3b, 0x4c, 0x9b, 0x98, 0xac, 0x20, 0xce, 0xc4, 0x17, 0xa6, 0xdd, 0xb5, 0x74, 0x4e, 0x94}, + {0x93, 0x2b, 0x79, 0x4b, 0xf8, 0x9c, 0x6e, 0xda, 0xf5, 0xd0, 0x65, 0x0c, 0x7c, 0x4b, 0xad, 0x92, 0x42, 0xb2, 0x56, 0x26, 0xe3, 0x7e, 0xad, 0x5a, 0xa7, 0x5e, 0xc8, 0xc6, 0x4e, 0x09, 0xdd, 0x4f}, + {0x16, 0xb1, 0x0c, 0x77, 0x9c, 0xe5, 0xcf, 0xef, 0x59, 0xc7, 0x71, 0x0d, 0x2e, 0x68, 0x44, 0x1e, 0xa6, 0xfa, 0xcb, 0x68, 0xe9, 0xb5, 0xf7, 0xd5, 0x33, 0xae, 0x0b, 0xb7, 0x8e, 0x28, 0xbf, 0x57}, + {0x0f, 0x77, 0xc7, 0x67, 0x43, 0xe7, 0x39, 0x6f, 0x99, 0x10, 0x13, 0x9f, 0x49, 0x37, 0xd8, 0x37, 0xae, 0x54, 0xe2, 0x10, 0x38, 0xac, 0x5c, 0x0b, 0x3f, 0xd6, 0xef, 0x17, 0x1a, 0x28, 0xa7, 0xe4}, + {0xd7, 0xe5, 0x74, 0xb7, 0xb9, 0x52, 0xf2, 0x93, 0xe8, 0x0d, 0xde, 0x90, 0x5e, 0xb5, 0x09, 0x37, 0x3f, 0x3f, 0x6c, 0xd1, 0x09, 0xa0, 0x22, 0x08, 0xb3, 0xc1, 0xe9, 0x24, 0x08, 0x0a, 0x20, 0xca}, + {0x45, 0x66, 0x6f, 0x8c, 0x38, 0x1e, 0x3d, 0xa6, 0x75, 0x56, 0x3f, 0xf8, 0xba, 0x23, 0xf8, 0x3b, 0xfa, 0xc3, 0x0c, 0x34, 0xab, 0xdd, 0xe6, 0xe5, 0xc0, 0x97, 0x5e, 0xf9, 0xfd, 0x70, 0x0c, 0xb9}, + {0xb2, 0x46, 0x12, 0xe4, 0x54, 0x60, 0x7e, 0xb1, 0xab, 0xa4, 0x47, 0xf8, 0x16, 0xd1, 0xa4, 0x55, 0x1e, 0xf9, 0x5f, 0xa7, 0x24, 0x7f, 0xb7, 0xc1, 0xf5, 0x03, 0x02, 0x0a, 0x71, 0x77, 0xf0, 0xdd}, + {0x7e, 0x20, 0x88, 0x61, 0x85, 0x6d, 0xa4, 0x2c, 0x8b, 0xb4, 0x6a, 0x75, 0x67, 0xf8, 0x12, 0x13, 0x62, 0xd9, 0xfb, 0x24, 0x96, 0xf1, 0x31, 0xa4, 0xaa, 0x90, 0x17, 0xcf, 0x36, 0x6c, 0xdf, 0xce}, + {0x5b, 0x64, 0x6b, 0xff, 0x6a, 0xd1, 0x10, 0x01, 0x65, 0x03, 0x7a, 0x05, 0x56, 0x01, 0xea, 0x02, 0x35, 0x8c, 0x0f, 0x41, 0x05, 0x0f, 0x9d, 0xfe, 0x3c, 0x95, 0xdc, 0xcb, 0xd3, 0x08, 0x7b, 0xe0}, + {0x74, 0x6d, 0x1d, 0xcc, 0xfe, 0xd2, 0xf0, 0xff, 0x1e, 0x13, 0xc5, 0x1e, 0x2d, 0x50, 0xd5, 0x32, 0x43, 0x75, 0xfb, 0xd5, 0xbf, 0x7c, 0xa8, 0x2a, 0x89, 0x31, 0x82, 0x8d, 0x80, 0x1d, 0x43, 0xab}, + {0xcb, 0x98, 0x11, 0x0d, 0x4a, 0x6b, 0xb9, 0x7d, 0x22, 0xfe, 0xad, 0xbc, 0x6c, 0x0d, 0x89, 0x30, 0xc5, 0xf8, 0xfc, 0x50, 0x8b, 0x2f, 0xc5, 0xb3, 0x53, 0x28, 0xd2, 0x6b, 0x88, 0xdb, 0x19, 0xae}, + {0x60, 0xb6, 0x26, 0xa0, 0x33, 0xb5, 0x5f, 0x27, 0xd7, 0x67, 0x6c, 0x40, 0x95, 0xea, 0xba, 0xbc, 0x7a, 0x2c, 0x7e, 0xde, 0x26, 0x24, 0xb4, 0x72, 0xe9, 0x7f, 0x64, 0xf9, 0x6b, 0x8c, 0xfc, 0x0e}, + {0xe5, 0xb5, 0x2b, 0xc9, 0x27, 0x46, 0x8d, 0xf7, 0x18, 0x93, 0xeb, 0x81, 0x97, 0xef, 0x82, 0x0c, 0xf7, 0x6c, 0xb0, 0xaa, 0xf6, 0xe8, 0xe4, 0xfe, 0x93, 0xad, 0x62, 0xd8, 0x03, 0x98, 0x31, 0x04}, + {0x05, 0x65, 0x41, 0xae, 0x5d, 0xa9, 0x96, 0x1b, 0xe2, 0xb0, 0xa5, 0xe8, 0x95, 0xe5, 0xc5, 0xba, 0x15, 0x3c, 0xbb, 0x62, 0xdd, 0x56, 0x1a, 0x42, 0x7b, 0xad, 0x0f, 0xfd, 0x41, 0x92, 0x31, 0x99}, + {0xf8, 0xfe, 0xf0, 0x5a, 0x3f, 0xa5, 0xc9, 0xf3, 0xeb, 0xa4, 0x16, 0x38, 0xb2, 0x47, 0xb7, 0x11, 0xa9, 0x9f, 0x96, 0x0f, 0xe7, 0x3a, 0xa2, 0xf9, 0x01, 0x36, 0xae, 0xb2, 0x03, 0x29, 0xb8, 0x88}}; + + //Debug printing for the above types + //Actually use DP(value) and #define DBG + void dp(key a); + void dp(bool a); + void dp(const char * a, int l); + void dp(keyV a); + void dp(keyM a); + void dp(xmr_amount vali); + void dp(int vali); + void dp(bits amountb); + void dp(const char * st); + + //various conversions + + //uint long long to 32 byte key + void d2h(key & amounth, xmr_amount val); + key d2h(xmr_amount val); + //uint long long to int[64] + void d2b(bits amountb, xmr_amount val); + //32 byte key to uint long long + // if the key holds a value > 2^64 + // then the value in the first 8 bytes is returned + xmr_amount h2d(const key &test); + //32 byte key to int[64] + void h2b(bits amountb2, key & test); + //int[64] to 32 byte key + void b2h(key & amountdh, bits amountb2); + //int[64] to uint long long + xmr_amount b2d(bits amountb); + + static inline const rct::key pk2rct(const crypto::public_key &pk) { return (const rct::key&)pk; } + static inline const rct::key sk2rct(const crypto::secret_key &sk) { return (const rct::key&)sk; } + static inline const rct::key ki2rct(const crypto::key_image &ki) { return (const rct::key&)ki; } + static inline const rct::key hash2rct(const crypto::hash &h) { return (const rct::key&)h; } + static inline const crypto::public_key rct2pk(const rct::key &k) { return (const crypto::public_key&)k; } + static inline const crypto::secret_key rct2sk(const rct::key &k) { return (const crypto::secret_key&)k; } + static inline const crypto::key_image rct2ki(const rct::key &k) { return (const crypto::key_image&)k; } + static inline const crypto::hash rct2hash(const rct::key &k) { return (const crypto::hash&)k; } + static inline bool operator==(const rct::key &k0, const crypto::public_key &k1) { return !memcmp(&k0, &k1, 32); } + static inline bool operator!=(const rct::key &k0, const crypto::public_key &k1) { return memcmp(&k0, &k1, 32); } +} + + +namespace cryptonote { + static inline bool operator==(const crypto::public_key &k0, const rct::key &k1) { return !memcmp(&k0, &k1, 32); } + static inline bool operator!=(const crypto::public_key &k0, const rct::key &k1) { return memcmp(&k0, &k1, 32); } + static inline bool operator==(const crypto::secret_key &k0, const rct::key &k1) { return !memcmp(&k0, &k1, 32); } + static inline bool operator!=(const crypto::secret_key &k0, const rct::key &k1) { return memcmp(&k0, &k1, 32); } +} + +template std::ostream &print256(std::ostream &o, const T &v); +inline std::ostream &operator <<(std::ostream &o, const rct::key &v) { return print256(o, v); } + + +BLOB_SERIALIZER(rct::key); +BLOB_SERIALIZER(rct::key64); +BLOB_SERIALIZER(rct::ctkey); +BLOB_SERIALIZER(rct::asnlSig); + +VARIANT_TAG(debug_archive, rct::key, "rct::key"); +VARIANT_TAG(debug_archive, rct::key64, "rct::key64"); +VARIANT_TAG(debug_archive, rct::keyV, "rct::keyV"); +VARIANT_TAG(debug_archive, rct::keyM, "rct::keyM"); +VARIANT_TAG(debug_archive, rct::ctkey, "rct::ctkey"); +VARIANT_TAG(debug_archive, rct::ctkeyV, "rct::ctkeyV"); +VARIANT_TAG(debug_archive, rct::ctkeyM, "rct::ctkeyM"); +VARIANT_TAG(debug_archive, rct::ecdhTuple, "rct::ecdhTuple"); +VARIANT_TAG(debug_archive, rct::mgSig, "rct::mgSig"); +VARIANT_TAG(debug_archive, rct::rangeSig, "rct::rangeSig"); +VARIANT_TAG(debug_archive, rct::asnlSig, "rct::asnlSig"); +VARIANT_TAG(debug_archive, rct::rctSig, "rct::rctSig"); + +VARIANT_TAG(binary_archive, rct::key, 0x90); +VARIANT_TAG(binary_archive, rct::key64, 0x91); +VARIANT_TAG(binary_archive, rct::keyV, 0x92); +VARIANT_TAG(binary_archive, rct::keyM, 0x93); +VARIANT_TAG(binary_archive, rct::ctkey, 0x94); +VARIANT_TAG(binary_archive, rct::ctkeyV, 0x95); +VARIANT_TAG(binary_archive, rct::ctkeyM, 0x96); +VARIANT_TAG(binary_archive, rct::ecdhTuple, 0x97); +VARIANT_TAG(binary_archive, rct::mgSig, 0x98); +VARIANT_TAG(binary_archive, rct::rangeSig, 0x99); +VARIANT_TAG(binary_archive, rct::asnlSig, 0x9a); +VARIANT_TAG(binary_archive, rct::rctSig, 0x9b); + +VARIANT_TAG(json_archive, rct::key, "rct_key"); +VARIANT_TAG(json_archive, rct::key64, "rct_key64"); +VARIANT_TAG(json_archive, rct::keyV, "rct_keyV"); +VARIANT_TAG(json_archive, rct::keyM, "rct_keyM"); +VARIANT_TAG(json_archive, rct::ctkey, "rct_ctkey"); +VARIANT_TAG(json_archive, rct::ctkeyV, "rct_ctkeyV"); +VARIANT_TAG(json_archive, rct::ctkeyM, "rct_ctkeyM"); +VARIANT_TAG(json_archive, rct::ecdhTuple, "rct_ecdhTuple"); +VARIANT_TAG(json_archive, rct::mgSig, "rct_mgSig"); +VARIANT_TAG(json_archive, rct::rangeSig, "rct_rangeSig"); +VARIANT_TAG(json_archive, rct::asnlSig, "rct_asnlSig"); +VARIANT_TAG(json_archive, rct::rctSig, "rct_rctSig"); + +#endif /* RCTTYPES_H */ diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index e6fc1106..5aa3591a 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -159,9 +159,25 @@ namespace cryptonote { res.blocks.resize(res.blocks.size()+1); res.blocks.back().block = block_to_blob(b.first); + res.output_indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices()); + res.output_indices.back().indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::tx_output_indices()); + bool r = m_core.get_tx_outputs_gindexs(get_transaction_hash(b.first.miner_tx), res.output_indices.back().indices.back().indices); + if (!r) + { + res.status = "Failed"; + return false; + } + size_t txidx = 0; BOOST_FOREACH(auto& t, b.second) { res.blocks.back().txs.push_back(tx_to_blob(t)); + res.output_indices.back().indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::tx_output_indices()); + bool r = m_core.get_tx_outputs_gindexs(b.first.tx_hashes[txidx++], res.output_indices.back().indices.back().indices); + if (!r) + { + res.status = "Failed"; + return false; + } } } @@ -250,6 +266,30 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) + { + CHECK_CORE_BUSY(); + res.status = "Failed"; + if(!m_core.get_random_rct_outs(req, res)) + { + return true; + } + + res.status = CORE_RPC_STATUS_OK; + std::stringstream ss; + typedef COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry out_entry; + CHECK_AND_ASSERT_MES(res.outs.size(), true, "internal error: res.outs.size() is empty"); + std::for_each(res.outs.begin(), res.outs.end(), [&](out_entry& oe) + { + ss << oe.global_amount_index << " "; + }); + ss << ENDL; + std::string s = ss.str(); + LOG_PRINT_L2("COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS: " << ENDL << s); + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res) { CHECK_CORE_BUSY(); @@ -459,6 +499,8 @@ namespace cryptonote res.reason = "overspend"; if ((res.fee_too_low = tvc.m_fee_too_low)) res.reason = "fee too low"; + if ((res.not_rct = tvc.m_not_rct)) + res.reason = "tx is not ringct"; return true; } @@ -1023,7 +1065,6 @@ namespace cryptonote return false; } -#if BLOCKCHAIN_DB == DB_LMDB const Blockchain &blockchain = m_core.get_blockchain_storage(); uint8_t version = req.version > 0 ? req.version : blockchain.get_next_hard_fork_version(); res.version = blockchain.get_current_hard_fork_version(); @@ -1031,11 +1072,6 @@ namespace cryptonote res.state = blockchain.get_hard_fork_state(); res.status = CORE_RPC_STATUS_OK; return true; -#else - error_resp.code = CORE_RPC_ERROR_CODE_UNSUPPORTED_RPC; - error_resp.message = "Hard fork inoperative in memory mode."; - return false; -#endif } //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_bans(const COMMAND_RPC_GETBANS::request& req, COMMAND_RPC_GETBANS::response& res, epee::json_rpc::error& error_resp) diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 6ebf41ab..9fcf3abf 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -79,6 +79,7 @@ namespace cryptonote MAP_URI_AUTO_BIN2("/get_o_indexes.bin", on_get_indexes, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES) MAP_URI_AUTO_BIN2("/getrandom_outs.bin", on_get_random_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS) MAP_URI_AUTO_BIN2("/get_outs.bin", on_get_outs, COMMAND_RPC_GET_OUTPUTS) + MAP_URI_AUTO_BIN2("/getrandom_rctouts.bin", on_get_random_rct_outs, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS) MAP_URI_AUTO_JON2("/gettransactions", on_get_transactions, COMMAND_RPC_GET_TRANSACTIONS) MAP_URI_AUTO_JON2("/is_key_image_spent", on_is_key_image_spent, COMMAND_RPC_IS_KEY_IMAGE_SPENT) MAP_URI_AUTO_JON2("/sendrawtransaction", on_send_raw_tx, COMMAND_RPC_SEND_RAW_TX) @@ -128,6 +129,7 @@ namespace cryptonote bool on_mining_status(const COMMAND_RPC_MINING_STATUS::request& req, COMMAND_RPC_MINING_STATUS::response& res); bool on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res); bool on_get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res); + bool on_get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res); bool on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res); bool on_save_bc(const COMMAND_RPC_SAVE_BC::request& req, COMMAND_RPC_SAVE_BC::response& res); bool on_get_peer_list(const COMMAND_RPC_GET_PEER_LIST::request& req, COMMAND_RPC_GET_PEER_LIST::response& res); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 22c3590e..8cba5394 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -41,7 +41,7 @@ namespace cryptonote #define CORE_RPC_STATUS_BUSY "BUSY" #define CORE_RPC_STATUS_NOT_MINING "NOT MINING" -#define CORE_RPC_VERSION 2 +#define CORE_RPC_VERSION 3 struct COMMAND_RPC_GET_HEIGHT { @@ -76,18 +76,38 @@ namespace cryptonote END_KV_SERIALIZE_MAP() }; + struct tx_output_indices + { + std::vector indices; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(indices) + END_KV_SERIALIZE_MAP() + }; + + struct block_output_indices + { + std::vector indices; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(indices) + END_KV_SERIALIZE_MAP() + }; + struct response { std::list blocks; uint64_t start_height; uint64_t current_height; std::string status; + std::vector output_indices; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(blocks) KV_SERIALIZE(start_height) KV_SERIALIZE(current_height) KV_SERIALIZE(status) + KV_SERIALIZE(output_indices) END_KV_SERIALIZE_MAP() }; }; @@ -296,10 +316,12 @@ namespace cryptonote struct outkey { crypto::public_key key; + rct::key mask; bool unlocked; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE_VAL_POD_AS_BLOB(key) + KV_SERIALIZE_VAL_POD_AS_BLOB(mask) KV_SERIALIZE(unlocked) END_KV_SERIALIZE_MAP() }; @@ -315,6 +337,37 @@ namespace cryptonote END_KV_SERIALIZE_MAP() }; }; + + struct COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS + { + struct request + { + uint64_t outs_count; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(outs_count) + END_KV_SERIALIZE_MAP() + }; + +#pragma pack (push, 1) + struct out_entry + { + uint64_t amount; + uint64_t global_amount_index; + crypto::public_key out_key; + rct::key commitment; + }; +#pragma pack(pop) + + struct response + { + std::list outs; + std::string status; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(outs) + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; //----------------------------------------------- struct COMMAND_RPC_SEND_RAW_TX { @@ -345,6 +398,7 @@ namespace cryptonote bool too_big; bool overspend; bool fee_too_low; + bool not_rct; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) @@ -357,6 +411,7 @@ namespace cryptonote KV_SERIALIZE(too_big) KV_SERIALIZE(overspend) KV_SERIALIZE(fee_too_low) + KV_SERIALIZE(not_rct) END_KV_SERIALIZE_MAP() }; }; diff --git a/src/serialization/crypto.h b/src/serialization/crypto.h index 3644b0e0..9a7e89c4 100644 --- a/src/serialization/crypto.h +++ b/src/serialization/crypto.h @@ -92,3 +92,4 @@ VARIANT_TAG(debug_archive, crypto::secret_key, "secret_key"); VARIANT_TAG(debug_archive, crypto::key_derivation, "key_derivation"); VARIANT_TAG(debug_archive, crypto::key_image, "key_image"); VARIANT_TAG(debug_archive, crypto::signature, "signature"); + diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h index 8e277a46..177cdf33 100644 --- a/src/serialization/serialization.h +++ b/src/serialization/serialization.h @@ -102,6 +102,12 @@ inline bool do_serialize(Archive &ar, T &v) { return ::serializer::serialize(ar, v); } +template +inline bool do_serialize(Archive &ar, bool &v) +{ + ar.serialize_blob(&v, sizeof(v)); + return true; +} // Never used in the code base // #ifndef __GNUC__ diff --git a/src/serialization/vector.h b/src/serialization/vector.h index a2a69f84..7f2bc78b 100644 --- a/src/serialization/vector.h +++ b/src/serialization/vector.h @@ -32,6 +32,11 @@ #include "serialization.h" +template