diff options
Diffstat (limited to 'src')
243 files changed, 11499 insertions, 2744 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fc7d60e4c..d83242a3c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -99,19 +99,26 @@ endfunction () add_subdirectory(common) add_subdirectory(crypto) add_subdirectory(ringct) +add_subdirectory(cryptonote_basic) add_subdirectory(cryptonote_core) -add_subdirectory(blockchain_db) +if(NOT IOS) + add_subdirectory(blockchain_db) +endif() add_subdirectory(mnemonics) -add_subdirectory(rpc) +if(NOT IOS) + add_subdirectory(rpc) +endif() add_subdirectory(wallet) -add_subdirectory(p2p) +if(NOT IOS) + add_subdirectory(p2p) +endif() add_subdirectory(cryptonote_protocol) - -add_subdirectory(simplewallet) -add_subdirectory(daemonizer) -add_subdirectory(daemon) - -add_subdirectory(blockchain_utilities) +if(NOT IOS) + add_subdirectory(simplewallet) + add_subdirectory(daemonizer) + add_subdirectory(daemon) + add_subdirectory(blockchain_utilities) +endif() if(PER_BLOCK_CHECKPOINT) add_subdirectory(blocks) diff --git a/src/blockchain_db/CMakeLists.txt b/src/blockchain_db/CMakeLists.txt index cefe93ebe..c27aa73b0 100644 --- a/src/blockchain_db/CMakeLists.txt +++ b/src/blockchain_db/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -63,6 +63,7 @@ target_link_libraries(blockchain_db PUBLIC common crypto + ringct ${LMDB_LIBRARY} ${BDB_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} diff --git a/src/blockchain_db/berkeleydb/db_bdb.cpp b/src/blockchain_db/berkeleydb/db_bdb.cpp index 57d8371bd..c954a7751 100644 --- a/src/blockchain_db/berkeleydb/db_bdb.cpp +++ b/src/blockchain_db/berkeleydb/db_bdb.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are @@ -31,7 +31,7 @@ #include <memory> // std::unique_ptr #include <cstring> // memcpy -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_format_utils.h" #include "crypto/crypto.h" #include "profile_tools.h" diff --git a/src/blockchain_db/berkeleydb/db_bdb.h b/src/blockchain_db/berkeleydb/db_bdb.h index 266e780c6..a040a70ef 100644 --- a/src/blockchain_db/berkeleydb/db_bdb.h +++ b/src/blockchain_db/berkeleydb/db_bdb.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp index bd1a38ec3..136d4fa80 100644 --- a/src/blockchain_db/blockchain_db.cpp +++ b/src/blockchain_db/blockchain_db.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -29,7 +29,7 @@ #include <boost/range/adaptor/reversed.hpp> #include "blockchain_db.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_format_utils.h" #include "profile_tools.h" #undef MONERO_DEFAULT_LOG_CATEGORY @@ -130,12 +130,6 @@ uint64_t BlockchainDB::add_block( const block& blk uint64_t prev_height = height(); - // call out to subclass implementation to add the block & metadata - time1 = epee::misc_utils::get_tick_count(); - add_block(blk, block_size, cumulative_difficulty, coins_generated, blk_hash); - TIME_MEASURE_FINISH(time1); - time_add_block1 += time1; - // call out to add the transactions time1 = epee::misc_utils::get_tick_count(); @@ -151,6 +145,12 @@ uint64_t BlockchainDB::add_block( const block& blk TIME_MEASURE_FINISH(time1); time_add_transaction += time1; + // call out to subclass implementation to add the block & metadata + time1 = epee::misc_utils::get_tick_count(); + add_block(blk, block_size, cumulative_difficulty, coins_generated, blk_hash); + TIME_MEASURE_FINISH(time1); + time_add_block1 += time1; + m_hardfork->add(blk, prev_height); block_txn_stop(); @@ -200,6 +200,45 @@ void BlockchainDB::remove_transaction(const crypto::hash& tx_hash) remove_transaction_data(tx_hash, tx); } +block BlockchainDB::get_block_from_height(const uint64_t& height) const +{ + blobdata bd = get_block_blob_from_height(height); + block b; + if (!parse_and_validate_block_from_blob(bd, b)) + throw new DB_ERROR("Failed to parse block from blob retrieved from the db"); + + return b; +} + +block BlockchainDB::get_block(const crypto::hash& h) const +{ + blobdata bd = get_block_blob(h); + block b; + if (!parse_and_validate_block_from_blob(bd, b)) + throw new DB_ERROR("Failed to parse block from blob retrieved from the db"); + + return b; +} + +bool BlockchainDB::get_tx(const crypto::hash& h, cryptonote::transaction &tx) const +{ + blobdata bd; + if (!get_tx_blob(h, bd)) + return false; + if (!parse_and_validate_tx_from_blob(bd, tx)) + throw new DB_ERROR("Failed to parse transaction from blob retrieved from the db"); + + return true; +} + +transaction BlockchainDB::get_tx(const crypto::hash& h) const +{ + transaction tx; + if (!get_tx(h, tx)) + throw new TX_DNE(std::string("tx with hash ").append(epee::string_tools::pod_to_hex(h)).append(" not found in db").c_str()); + return tx; +} + void BlockchainDB::reset_stats() { num_calls = 0; diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index 455e0c811..f5710550b 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -34,9 +34,10 @@ #include <string> #include <exception> #include "crypto/hash.h" -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_core/difficulty.h" -#include "cryptonote_core/hardfork.h" +#include "cryptonote_protocol/blobdatatype.h" +#include "cryptonote_basic/cryptonote_basic.h" +#include "cryptonote_basic/difficulty.h" +#include "cryptonote_basic/hardfork.h" /** \file * Cryptonote Blockchain Database Interface @@ -754,7 +755,20 @@ public: * * @return the block requested */ - virtual block get_block(const crypto::hash& h) const = 0; + virtual cryptonote::blobdata get_block_blob(const crypto::hash& h) const = 0; + + /** + * @brief fetches the block with the given hash + * + * Returns the requested block. + * + * If the block does not exist, the subclass should throw BLOCK_DNE + * + * @param h the hash to look for + * + * @return the block requested + */ + virtual block get_block(const crypto::hash& h) const; /** * @brief gets the height of the block with a given hash @@ -784,7 +798,7 @@ public: virtual block_header get_block_header(const crypto::hash& h) const = 0; /** - * @brief fetch a block by height + * @brief fetch a block blob by height * * The subclass should return the block at the given height. * @@ -793,9 +807,21 @@ public: * * @param height the height to look for * + * @return the block blob + */ + virtual cryptonote::blobdata get_block_blob_from_height(const uint64_t& height) const = 0; + + /** + * @brief fetch a block by height + * + * If the block does not exist, that is to say if the blockchain is not + * that high, then the subclass should throw BLOCK_DNE + * + * @param height the height to look for + * * @return the block */ - virtual block get_block_from_height(const uint64_t& height) const = 0; + virtual block get_block_from_height(const uint64_t& height) const; /** * @brief fetch a block's timestamp @@ -1009,20 +1035,28 @@ public: /** * @brief fetches the transaction with the given hash * - * The subclass should return the transaction stored which has the given - * hash. - * * If the transaction does not exist, the subclass should throw TX_DNE. * * @param h the hash to look for * * @return the transaction with the given hash */ - virtual transaction get_tx(const crypto::hash& h) const = 0; + virtual transaction get_tx(const crypto::hash& h) const; /** * @brief fetches the transaction with the given hash * + * If the transaction does not exist, the subclass should return false. + * + * @param h the hash to look for + * + * @return true iff the transaction was found + */ + virtual bool get_tx(const crypto::hash& h, transaction &tx) const; + + /** + * @brief fetches the transaction blob with the given hash + * * The subclass should return the transaction stored which has the given * hash. * @@ -1032,7 +1066,7 @@ public: * * @return true iff the transaction was found */ - virtual bool get_tx(const crypto::hash& h, transaction &tx) const = 0; + virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const = 0; /** * @brief fetches the total number of transactions ever @@ -1184,7 +1218,7 @@ public: * @param offsets a list of amount-specific output indices * @param outputs return-by-reference a list of outputs' metadata */ - virtual void get_output_key(const uint64_t &amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs) = 0; + virtual void get_output_key(const uint64_t &amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs, bool allow_partial = false) = 0; /* * FIXME: Need to check with git blame and ask what this does to diff --git a/src/blockchain_db/db_types.h b/src/blockchain_db/db_types.h index 67afe0405..6c21b029e 100644 --- a/src/blockchain_db/db_types.h +++ b/src/blockchain_db/db_types.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 1cfcf3a9e..dab09e8f4 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are @@ -34,7 +34,7 @@ #include <cstring> // memcpy #include <random> -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_format_utils.h" #include "crypto/crypto.h" #include "profile_tools.h" @@ -377,7 +377,50 @@ void mdb_txn_safe::allow_new_txns() creation_gate.clear(); } +void lmdb_resized(MDB_env *env) +{ + mdb_txn_safe::prevent_new_txns(); + + MGINFO("LMDB map resize detected."); + + MDB_envinfo mei; + + mdb_env_info(env, &mei); + uint64_t old = mei.me_mapsize; + + mdb_txn_safe::wait_no_active_txns(); + + int result = mdb_env_set_mapsize(env, 0); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to set new mapsize: ", result).c_str())); + + mdb_env_info(env, &mei); + uint64_t new_mapsize = mei.me_mapsize; + + MGINFO("LMDB Mapsize increased." << " Old: " << old / (1024 * 1024) << "MiB" << ", New: " << new_mapsize / (1024 * 1024) << "MiB"); + + mdb_txn_safe::allow_new_txns(); +} +inline int lmdb_txn_begin(MDB_env *env, MDB_txn *parent, unsigned int flags, MDB_txn **txn) +{ + int res = mdb_txn_begin(env, parent, flags, txn); + if (res == MDB_MAP_RESIZED) { + lmdb_resized(env); + res = mdb_txn_begin(env, parent, flags, txn); + } + return res; +} + +inline int lmdb_txn_renew(MDB_txn *txn) +{ + int res = mdb_txn_renew(txn); + if (res == MDB_MAP_RESIZED) { + lmdb_resized(mdb_txn_env(txn)); + res = mdb_txn_renew(txn); + } + return res; +} void BlockchainLMDB::do_resize(uint64_t increase_size) { @@ -561,7 +604,7 @@ uint64_t BlockchainLMDB::get_estimated_batch_size(uint64_t batch_num_blocks) con { LOG_PRINT_L1("No existing blocks to check for average block size"); } - else if (m_cum_count) + else if (m_cum_count >= num_prev_blocks) { avg_block_size = m_cum_size / m_cum_count; LOG_PRINT_L1("average block size across recent " << m_cum_count << " blocks: " << avg_block_size); @@ -570,6 +613,9 @@ uint64_t BlockchainLMDB::get_estimated_batch_size(uint64_t batch_num_blocks) con } else { + MDB_txn *rtxn; + mdb_txn_cursors *rcurs; + block_rtxn_start(&rtxn, &rcurs); for (uint64_t block_num = block_start; block_num <= block_stop; ++block_num) { uint32_t block_size = get_block_size(block_num); @@ -578,6 +624,7 @@ uint64_t BlockchainLMDB::get_estimated_batch_size(uint64_t batch_num_blocks) con // some blocks were to be skipped for being outliers. ++num_blocks_used; } + block_rtxn_stop(); avg_block_size = total_block_size / num_blocks_used; LOG_PRINT_L1("average block size across recent " << num_blocks_used << " blocks: " << avg_block_size); } @@ -628,6 +675,7 @@ void BlockchainLMDB::add_block(const block& blk, const size_t& block_size, const CURSOR(blocks) CURSOR(block_info) + // this call to mdb_cursor_put will change height() MDB_val_copy<blobdata> blob(block_to_blob(blk)); result = mdb_cursor_put(m_cur_blocks, &key, &blob, MDB_APPEND); if (result) @@ -701,7 +749,7 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons uint64_t m_height = height(); int result; - uint64_t tx_id = m_num_txs; + uint64_t tx_id = get_tx_count(); CURSOR(txs) CURSOR(tx_indices) @@ -734,7 +782,6 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons if (result) throw0(DB_ERROR(lmdb_error("Failed to add tx blob to db transaction: ", result).c_str())); - m_num_txs++; return tx_id; } @@ -782,8 +829,6 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const // Don't delete the tx_indices entry until the end, after we're done with val_tx_id if (mdb_cursor_del(m_cur_tx_indices, 0)) throw1(DB_ERROR("Failed to add removal of tx index to db transaction")); - - m_num_txs--; } uint64_t BlockchainLMDB::add_output(const crypto::hash& tx_hash, @@ -796,6 +841,7 @@ uint64_t BlockchainLMDB::add_output(const crypto::hash& tx_hash, check_open(); mdb_txn_cursors *m_cursors = &m_wcursors; uint64_t m_height = height(); + uint64_t m_num_outputs = num_outputs(); int result = 0; @@ -848,7 +894,6 @@ uint64_t BlockchainLMDB::add_output(const crypto::hash& tx_hash, 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())); - m_num_outputs++; return ok.amount_index; } @@ -933,8 +978,6 @@ void BlockchainLMDB::remove_output(const uint64_t amount, const uint64_t& out_in result = mdb_cursor_del(m_cur_output_amounts, 0); if (result) throw0(DB_ERROR(lmdb_error(std::string("Error deleting amount for output index ").append(boost::lexical_cast<std::string>(out_index).append(": ")).c_str(), result).c_str())); - - m_num_outputs--; } void BlockchainLMDB::add_spent_key(const crypto::key_image& k_image) @@ -999,7 +1042,7 @@ tx_out BlockchainLMDB::output_from_blob(const blobdata& blob) const void BlockchainLMDB::check_open() const { - LOG_PRINT_L3("BlockchainLMDB::" << __func__); +// LOG_PRINT_L3("BlockchainLMDB::" << __func__); if (!m_open) throw0(DB_ERROR("DB operation attempted on a not-open DB instance")); } @@ -1154,16 +1197,6 @@ void BlockchainLMDB::open(const std::string& filename, const int mdb_flags) LOG_PRINT_L2("Setting m_height to: " << db_stats.ms_entries); uint64_t m_height = db_stats.ms_entries; - // get and keep current number of txs - if ((result = mdb_stat(txn, m_txs, &db_stats))) - throw0(DB_ERROR(lmdb_error("Failed to query m_txs: ", result).c_str())); - m_num_txs = db_stats.ms_entries; - - // get and keep current number of outputs - if ((result = mdb_stat(txn, m_output_txs, &db_stats))) - throw0(DB_ERROR(lmdb_error("Failed to query m_output_txs: ", result).c_str())); - m_num_outputs = db_stats.ms_entries; - bool compatible = true; MDB_val_copy<const char*> k("version"); @@ -1269,7 +1302,7 @@ void BlockchainLMDB::reset() check_open(); mdb_txn_safe txn; - if (auto result = mdb_txn_begin(m_env, NULL, 0, txn)) + if (auto result = lmdb_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)) @@ -1303,7 +1336,6 @@ void BlockchainLMDB::reset() throw0(DB_ERROR(lmdb_error("Failed to write version to database: ", result).c_str())); txn.commit(); - m_num_outputs = 0; m_cum_size = 0; m_cum_count = 0; } @@ -1353,7 +1385,7 @@ void BlockchainLMDB::unlock() txn_ptr = m_write_txn; \ else \ { \ - if (auto mdb_res = mdb_txn_begin(m_env, NULL, flags, auto_txn)) \ + if (auto mdb_res = lmdb_txn_begin(m_env, NULL, flags, auto_txn)) \ throw0(DB_ERROR(lmdb_error(std::string("Failed to create a transaction for the db in ")+__FUNCTION__+": ", mdb_res).c_str())); \ } \ @@ -1387,7 +1419,7 @@ void BlockchainLMDB::unlock() txn_ptr = m_write_txn; \ else \ { \ - if (auto mdb_res = mdb_txn_begin(m_env, NULL, flags, auto_txn)) \ + if (auto mdb_res = lmdb_txn_begin(m_env, NULL, flags, auto_txn)) \ throw0(DB_ERROR(lmdb_error(std::string("Failed to create a transaction for the db in ")+__FUNCTION__+": ", mdb_res).c_str())); \ } \ @@ -1428,12 +1460,12 @@ bool BlockchainLMDB::block_exists(const crypto::hash& h, uint64_t *height) const return ret; } -block BlockchainLMDB::get_block(const crypto::hash& h) const +cryptonote::blobdata BlockchainLMDB::get_block_blob(const crypto::hash& h) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); - return get_block_from_height(get_block_height(h)); + return get_block_blob_from_height(get_block_height(h)); } uint64_t BlockchainLMDB::get_block_height(const crypto::hash& h) const @@ -1466,7 +1498,7 @@ block_header BlockchainLMDB::get_block_header(const crypto::hash& h) const return get_block(h); } -block BlockchainLMDB::get_block_from_height(const uint64_t& height) const +cryptonote::blobdata BlockchainLMDB::get_block_blob_from_height(const uint64_t& height) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); @@ -1487,13 +1519,9 @@ block BlockchainLMDB::get_block_from_height(const uint64_t& height) const blobdata bd; bd.assign(reinterpret_cast<char*>(result.mv_data), result.mv_size); - block b; - if (!parse_and_validate_block_from_blob(bd, b)) - throw0(DB_ERROR("Failed to parse block from blob retrieved from the db")); - TXN_POSTFIX_RDONLY(); - return b; + return bd; } uint64_t BlockchainLMDB::get_block_timestamp(const uint64_t& height) const @@ -1713,6 +1741,20 @@ uint64_t BlockchainLMDB::height() const return db_stats.ms_entries; } +uint64_t BlockchainLMDB::num_outputs() const +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + check_open(); + TXN_PREFIX_RDONLY(); + int result; + + // get current height + MDB_stat db_stats; + if ((result = mdb_stat(m_txn, m_output_txs, &db_stats))) + throw0(DB_ERROR(lmdb_error("Failed to query m_output_txs: ", result).c_str())); + return db_stats.ms_entries; +} + bool BlockchainLMDB::tx_exists(const crypto::hash& h) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); @@ -1808,7 +1850,7 @@ uint64_t BlockchainLMDB::get_tx_unlock_time(const crypto::hash& h) const return ret; } -bool BlockchainLMDB::get_tx(const crypto::hash& h, transaction &tx) const +bool BlockchainLMDB::get_tx_blob(const crypto::hash& h, cryptonote::blobdata &bd) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); @@ -1831,36 +1873,24 @@ bool BlockchainLMDB::get_tx(const crypto::hash& h, transaction &tx) const else if (get_result) throw0(DB_ERROR(lmdb_error("DB error attempting to fetch tx from hash", get_result).c_str())); - blobdata bd; bd.assign(reinterpret_cast<char*>(result.mv_data), result.mv_size); - if (!parse_and_validate_tx_from_blob(bd, tx)) - throw0(DB_ERROR("Failed to parse tx from blob retrieved from the db")); - TXN_POSTFIX_RDONLY(); return true; } -transaction BlockchainLMDB::get_tx(const crypto::hash& h) const -{ - transaction tx; - - if (!get_tx(h, tx)) - throw1(TX_DNE(std::string("tx with hash ").append(epee::string_tools::pod_to_hex(h)).append(" not found in db").c_str())); - return tx; -} - uint64_t BlockchainLMDB::get_tx_count() const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); TXN_PREFIX_RDONLY(); + int result; MDB_stat db_stats; - if (mdb_stat(m_txn, m_tx_indices, &db_stats)) - throw0(DB_ERROR("Failed to query m_tx_indices")); + if ((result = mdb_stat(m_txn, m_txs, &db_stats))) + throw0(DB_ERROR(lmdb_error("Failed to query m_txs: ", result).c_str())); TXN_POSTFIX_RDONLY(); @@ -2278,7 +2308,7 @@ bool BlockchainLMDB::batch_start(uint64_t batch_num_blocks) m_write_batch_txn = new mdb_txn_safe(); // NOTE: need to make sure it's destroyed properly when done - if (auto mdb_res = mdb_txn_begin(m_env, NULL, 0, *m_write_batch_txn)) + if (auto mdb_res = lmdb_txn_begin(m_env, NULL, 0, *m_write_batch_txn)) { delete m_write_batch_txn; m_write_batch_txn = nullptr; @@ -2397,12 +2427,12 @@ bool BlockchainLMDB::block_rtxn_start(MDB_txn **mtxn, mdb_txn_cursors **mcur) co m_tinfo.reset(new mdb_threadinfo); memset(&m_tinfo->m_ti_rcursors, 0, sizeof(m_tinfo->m_ti_rcursors)); memset(&m_tinfo->m_ti_rflags, 0, sizeof(m_tinfo->m_ti_rflags)); - if (auto mdb_res = mdb_txn_begin(m_env, NULL, MDB_RDONLY, &m_tinfo->m_ti_rtxn)) + if (auto mdb_res = lmdb_txn_begin(m_env, NULL, MDB_RDONLY, &m_tinfo->m_ti_rtxn)) throw0(DB_ERROR_TXN_START(lmdb_error("Failed to create a read transaction for the db: ", mdb_res).c_str())); ret = true; } else if (!m_tinfo->m_ti_rflags.m_rf_txn) { - if (auto mdb_res = mdb_txn_renew(m_tinfo->m_ti_rtxn)) + if (auto mdb_res = lmdb_txn_renew(m_tinfo->m_ti_rtxn)) throw0(DB_ERROR_TXN_START(lmdb_error("Failed to renew a read transaction for the db: ", mdb_res).c_str())); ret = true; } @@ -2435,12 +2465,12 @@ void BlockchainLMDB::block_txn_start(bool readonly) m_tinfo.reset(new mdb_threadinfo); memset(&m_tinfo->m_ti_rcursors, 0, sizeof(m_tinfo->m_ti_rcursors)); memset(&m_tinfo->m_ti_rflags, 0, sizeof(m_tinfo->m_ti_rflags)); - if (auto mdb_res = mdb_txn_begin(m_env, NULL, MDB_RDONLY, &m_tinfo->m_ti_rtxn)) + if (auto mdb_res = lmdb_txn_begin(m_env, NULL, MDB_RDONLY, &m_tinfo->m_ti_rtxn)) throw0(DB_ERROR_TXN_START(lmdb_error("Failed to create a read transaction for the db: ", mdb_res).c_str())); didit = true; } else if (!m_tinfo->m_ti_rflags.m_rf_txn) { - if (auto mdb_res = mdb_txn_renew(m_tinfo->m_ti_rtxn)) + if (auto mdb_res = lmdb_txn_renew(m_tinfo->m_ti_rtxn)) throw0(DB_ERROR_TXN_START(lmdb_error("Failed to renew a read transaction for the db: ", mdb_res).c_str())); didit = true; } @@ -2466,14 +2496,15 @@ void BlockchainLMDB::block_txn_start(bool readonly) { m_writer = boost::this_thread::get_id(); m_write_txn = new mdb_txn_safe(); - if (auto mdb_res = mdb_txn_begin(m_env, NULL, 0, *m_write_txn)) + if (auto mdb_res = lmdb_txn_begin(m_env, NULL, 0, *m_write_txn)) { delete m_write_txn; m_write_txn = nullptr; throw0(DB_ERROR_TXN_START(lmdb_error("Failed to create a transaction for the db: ", mdb_res).c_str())); } memset(&m_wcursors, 0, sizeof(m_wcursors)); - } + } else if (m_writer != boost::this_thread::get_id()) + throw0(DB_ERROR_TXN_START((std::string("Attempted to start new write txn when batch txn already exists in ")+__FUNCTION__).c_str())); } void BlockchainLMDB::block_txn_stop() @@ -2544,8 +2575,6 @@ uint64_t BlockchainLMDB::add_block(const block& blk, const size_t& block_size, c } } - uint64_t num_txs = m_num_txs; - uint64_t num_outputs = m_num_outputs; try { BlockchainDB::add_block(blk, block_size, cumulative_difficulty, coins_generated, txs); @@ -2556,8 +2585,6 @@ uint64_t BlockchainLMDB::add_block(const block& blk, const size_t& block_size, c } catch (...) { - m_num_txs = num_txs; - m_num_outputs = num_outputs; block_txn_abort(); throw; } @@ -2572,8 +2599,6 @@ void BlockchainLMDB::pop_block(block& blk, std::vector<transaction>& txs) block_txn_start(false); - uint64_t num_txs = m_num_txs; - uint64_t num_outputs = m_num_outputs; try { BlockchainDB::pop_block(blk, txs); @@ -2581,8 +2606,6 @@ void BlockchainLMDB::pop_block(block& blk, std::vector<transaction>& txs) } catch (...) { - m_num_txs = num_txs; - m_num_outputs = num_outputs; block_txn_abort(); throw; } @@ -2616,7 +2639,7 @@ void BlockchainLMDB::get_output_tx_and_index_from_global(const std::vector<uint6 TXN_POSTFIX_RDONLY(); } -void BlockchainLMDB::get_output_key(const uint64_t &amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs) +void BlockchainLMDB::get_output_key(const uint64_t &amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs, bool allow_partial) { LOG_PRINT_L3("BlockchainLMDB::" << __func__); TIME_MEASURE_START(db3); @@ -2634,7 +2657,14 @@ void BlockchainLMDB::get_output_key(const uint64_t &amount, const std::vector<ui auto get_result = mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_GET_BOTH); if (get_result == MDB_NOTFOUND) + { + if (allow_partial) + { + MDEBUG("Partial result: " << outputs.size() << "/" << offsets.size()); + break; + } throw1(OUTPUT_DNE((std::string("Attempting to get output pubkey by global index (amount ") + boost::lexical_cast<std::string>(amount) + ", index " + boost::lexical_cast<std::string>(index) + ", count " + boost::lexical_cast<std::string>(get_num_outputs(amount)) + "), but key does not exist").c_str())); + } else if (get_result) throw0(DB_ERROR(lmdb_error("Error attempting to retrieve an output pubkey from the db", get_result).c_str())); @@ -3256,8 +3286,6 @@ void BlockchainLMDB::migrate_0_1() lmdb_db_open(txn, LMDB_OUTPUT_AMOUNTS, MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED | MDB_CREATE, m_output_amounts, "Failed to open db handle for m_output_amounts"); mdb_set_dupsort(txn, m_output_amounts, compare_uint64); txn.commit(); - m_num_txs = 0; - m_num_outputs = 0; } while(0); do { @@ -3328,12 +3356,9 @@ void BlockchainLMDB::migrate_0_1() throw0(DB_ERROR(lmdb_error("Failed to open a cursor for txs: ", result).c_str())); if (!i) { MDB_stat ms; - mdb_stat(txn, m_output_txs, &ms); - m_num_outputs = ms.ms_entries; mdb_stat(txn, m_txs, &ms); - m_num_txs = i = ms.ms_entries; + i = ms.ms_entries; if (i) { - m_num_txs = i; MDB_val_set(pk, "txblk"); result = mdb_cursor_get(c_props, &pk, &k, MDB_SET); if (result) diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index e7faf8cdc..8a5677566 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are @@ -170,13 +170,13 @@ public: virtual bool block_exists(const crypto::hash& h, uint64_t *height = NULL) const; - virtual block get_block(const crypto::hash& h) const; - virtual uint64_t get_block_height(const crypto::hash& h) const; virtual block_header get_block_header(const crypto::hash& h) const; - virtual block get_block_from_height(const uint64_t& height) const; + virtual cryptonote::blobdata get_block_blob(const crypto::hash& h) const; + + virtual cryptonote::blobdata get_block_blob_from_height(const uint64_t& height) const; virtual uint64_t get_block_timestamp(const uint64_t& height) const; @@ -207,9 +207,7 @@ public: virtual uint64_t get_tx_unlock_time(const crypto::hash& h) const; - virtual transaction get_tx(const crypto::hash& h) const; - - virtual bool get_tx(const crypto::hash& h, transaction &tx) const; + virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const; virtual uint64_t get_tx_count() const; @@ -221,7 +219,7 @@ public: virtual output_data_t get_output_key(const uint64_t& amount, const uint64_t& index); virtual output_data_t get_output_key(const uint64_t& global_index) const; - virtual void get_output_key(const uint64_t &amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs); + virtual void get_output_key(const uint64_t &amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs, bool allow_partial = false); virtual tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const; virtual void get_output_tx_and_index_from_global(const std::vector<uint64_t> &global_indices, @@ -312,6 +310,8 @@ private: virtual void remove_spent_key(const crypto::key_image& k_image); + uint64_t num_outputs() const; + // Hard fork virtual void set_hard_fork_version(uint64_t height, uint8_t version); virtual uint8_t get_hard_fork_version(uint64_t height) const; @@ -369,10 +369,8 @@ private: MDB_dbi m_properties; - uint64_t m_num_txs; - uint64_t m_num_outputs; mutable uint64_t m_cum_size; // used in batch size estimation - mutable int m_cum_count; + mutable unsigned int m_cum_count; std::string m_folder; mdb_txn_safe* m_write_txn; // may point to either a short-lived txn or a batch txn mdb_txn_safe* m_write_batch_txn; // persist batch txn outside of BlockchainLMDB diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt index b26fe04cc..bb23cdc8b 100644 --- a/src/blockchain_utilities/CMakeLists.txt +++ b/src/blockchain_utilities/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -119,6 +119,7 @@ monero_add_executable(cn_deserialize target_link_libraries(cn_deserialize LINK_PRIVATE cryptonote_core + blockchain_db p2p epee ${CMAKE_THREAD_LIBS_INIT}) diff --git a/src/blockchain_utilities/README.md b/src/blockchain_utilities/README.md index 809df3bfa..d18dcba8c 100644 --- a/src/blockchain_utilities/README.md +++ b/src/blockchain_utilities/README.md @@ -1,6 +1,6 @@ # Monero Blockchain Utilities -Copyright (c) 2014-2016, The Monero Project +Copyright (c) 2014-2017, The Monero Project ## Introduction diff --git a/src/blockchain_utilities/blockchain_export.cpp b/src/blockchain_utilities/blockchain_export.cpp index b4411fbba..f145bc107 100644 --- a/src/blockchain_utilities/blockchain_export.cpp +++ b/src/blockchain_utilities/blockchain_export.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -57,6 +57,10 @@ std::string join_set_strings(const std::unordered_set<std::string>& db_types_all int main(int argc, char* argv[]) { + TRY_ENTRY(); + + epee::string_tools::set_module_name_and_folder(argv[0]); + std::string default_db_type = "lmdb"; std::unordered_set<std::string> db_types_all = cryptonote::blockchain_db_types; @@ -78,7 +82,7 @@ int main(int argc, char* argv[]) 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<std::string> arg_output_file = {"output-file", "Specify output file", "", true}; - const command_line::arg_descriptor<uint32_t> arg_log_level = {"log-level", "", log_level}; + const command_line::arg_descriptor<std::string> arg_log_level = {"log-level", "0-4 or categories", ""}; const command_line::arg_descriptor<uint64_t> arg_block_stop = {"block-stop", "Stop at block number", block_stop}; const command_line::arg_descriptor<bool> arg_testnet_on = { "testnet" @@ -122,10 +126,13 @@ int main(int argc, char* argv[]) return 1; } - log_level = command_line::get_arg(vm, arg_log_level); + mlog_configure(mlog_get_default_log_path("monero-blockchain-export.log"), true); + if (!vm["log-level"].defaulted()) + mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str()); + else + mlog_set_log(std::string(std::to_string(log_level) + ",bcutil:INFO").c_str()); block_stop = command_line::get_arg(vm, arg_block_stop); - mlog_configure("monero-blockchain-export", true); LOG_PRINT_L0("Starting..."); bool opt_testnet = command_line::get_arg(vm, arg_testnet_on); @@ -223,4 +230,7 @@ int main(int argc, char* argv[]) } CHECK_AND_ASSERT_MES(r, false, "Failed to export blockchain raw data"); LOG_PRINT_L0("Blockchain raw data exported OK"); + return 0; + + CATCH_ENTRY("Export error", 1); } diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp index f21673b89..b2c217ca1 100644 --- a/src/blockchain_utilities/blockchain_import.cpp +++ b/src/blockchain_utilities/blockchain_import.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -34,7 +34,7 @@ #include <boost/filesystem.hpp> #include "bootstrap_file.h" #include "bootstrap_serialization.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_format_utils.h" #include "serialization/binary_utils.h" // dump_binary(), parse_binary() #include "serialization/json_utils.h" // dump_json() #include "include_base_utils.h" @@ -374,7 +374,7 @@ int import_from_file(FakeCore& simple_core, const std::string& import_file_path, return 2; } bytes_read += chunk_size; - MINFO("Total bytes read: " << bytes_read); + MDEBUG("Total bytes read: " << bytes_read); if (h + NUM_BLOCKS_PER_CHUNK < start_height + 1) { @@ -596,6 +596,10 @@ int import_from_file(FakeCore& simple_core, const std::string& import_file_path, int main(int argc, char* argv[]) { + TRY_ENTRY(); + + epee::string_tools::set_module_name_and_folder(argv[0]); + std::string default_db_type = "lmdb"; std::string default_db_engine_compiled = "blockchain_db"; @@ -620,7 +624,7 @@ int main(int argc, char* argv[]) 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<std::string> arg_input_file = {"input-file", "Specify input file", "", true}; - const command_line::arg_descriptor<uint32_t> arg_log_level = {"log-level", "", log_level}; + const command_line::arg_descriptor<std::string> arg_log_level = {"log-level", "0-4 or categories", ""}; const command_line::arg_descriptor<uint64_t> arg_block_stop = {"block-stop", "Stop at block number", block_stop}; const command_line::arg_descriptor<uint64_t> arg_batch_size = {"batch-size", "", db_batch_size}; const command_line::arg_descriptor<uint64_t> arg_pop_blocks = {"pop-blocks", "Remove blocks from end of blockchain", num_blocks}; @@ -680,7 +684,6 @@ int main(int argc, char* argv[]) if (! r) return 1; - log_level = command_line::get_arg(vm, arg_log_level); opt_verify = command_line::get_arg(vm, arg_verify); opt_batch = command_line::get_arg(vm, arg_batch); opt_resume = command_line::get_arg(vm, arg_resume); @@ -722,7 +725,12 @@ int main(int argc, char* argv[]) m_config_folder = command_line::get_arg(vm, data_dir_arg); db_arg_str = command_line::get_arg(vm, arg_database); - mlog_configure("monero-blockchain-import", true); + mlog_configure(mlog_get_default_log_path("monero-blockchain-import.log"), true); + if (!vm["log-level"].defaulted()) + mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str()); + else + mlog_set_log(std::string(std::to_string(log_level) + ",bcutil:INFO").c_str()); + MINFO("Starting..."); boost::filesystem::path fs_import_file_path; @@ -759,7 +767,11 @@ int main(int argc, char* argv[]) return 1; } - if ((db_type == "lmdb") || (db_type == "berkeley")) + if ((db_type == "lmdb") +#if defined(BERKELEY_DB) + || (db_type == "berkeley") +#endif + ) { db_engine_compiled = "blockchain_db"; } @@ -796,13 +808,11 @@ int main(int argc, char* argv[]) // properties to do so. Both ways work, but fake core isn't necessary in that // circumstance. - // for multi_db_runtime: - 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 != "lmdb" +#if defined(BERKELEY_DB) + && db_type != "berkeley" +#endif + ) { std::cerr << "database type unrecognized" << ENDL; return 1; @@ -847,4 +857,6 @@ int main(int argc, char* argv[]) // calls delete on its BlockchainDB derived class' object, which closes its // files. return 0; + + CATCH_ENTRY("Import error", 1); } diff --git a/src/blockchain_utilities/blockchain_utilities.h b/src/blockchain_utilities/blockchain_utilities.h index 6ddda65ec..af934bf29 100644 --- a/src/blockchain_utilities/blockchain_utilities.h +++ b/src/blockchain_utilities/blockchain_utilities.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/blocksdat_file.cpp b/src/blockchain_utilities/blocksdat_file.cpp index 7ccb9a145..63224225f 100644 --- a/src/blockchain_utilities/blocksdat_file.cpp +++ b/src/blockchain_utilities/blocksdat_file.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/blocksdat_file.h b/src/blockchain_utilities/blocksdat_file.h index ed821cd6d..4e9d8173b 100644 --- a/src/blockchain_utilities/blocksdat_file.h +++ b/src/blockchain_utilities/blocksdat_file.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -34,8 +34,8 @@ #include <boost/iostreams/filtering_streambuf.hpp> -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_core/cryptonote_boost_serialization.h" +#include "cryptonote_basic/cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_boost_serialization.h" #include "cryptonote_core/blockchain.h" #include "blockchain_db/blockchain_db.h" #include "blockchain_db/lmdb/db_lmdb.h" diff --git a/src/blockchain_utilities/bootstrap_file.cpp b/src/blockchain_utilities/bootstrap_file.cpp index add2c65e6..d5bb37d93 100644 --- a/src/blockchain_utilities/bootstrap_file.cpp +++ b/src/blockchain_utilities/bootstrap_file.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -458,7 +458,7 @@ uint64_t BootstrapFile::count_blocks(const std::string& import_file_path) bytes_read += chunk_size; // std::cout << refresh_string; - MINFO("Number bytes scanned: " << bytes_read); + MDEBUG("Number bytes scanned: " << bytes_read); } import_file.close(); diff --git a/src/blockchain_utilities/bootstrap_file.h b/src/blockchain_utilities/bootstrap_file.h index 0bbe28f17..1a646b54b 100644 --- a/src/blockchain_utilities/bootstrap_file.h +++ b/src/blockchain_utilities/bootstrap_file.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -34,7 +34,7 @@ #include <boost/iostreams/filtering_streambuf.hpp> -#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_basic.h" #include "cryptonote_core/blockchain.h" #include <algorithm> diff --git a/src/blockchain_utilities/bootstrap_serialization.h b/src/blockchain_utilities/bootstrap_serialization.h index a7d88907f..f9f50f00b 100644 --- a/src/blockchain_utilities/bootstrap_serialization.h +++ b/src/blockchain_utilities/bootstrap_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -28,8 +28,8 @@ #pragma once -#include "cryptonote_core/cryptonote_boost_serialization.h" -#include "cryptonote_core/difficulty.h" +#include "cryptonote_basic/cryptonote_boost_serialization.h" +#include "cryptonote_basic/difficulty.h" namespace cryptonote diff --git a/src/blockchain_utilities/cn_deserialize.cpp b/src/blockchain_utilities/cn_deserialize.cpp index ae8e38435..b178e4e03 100644 --- a/src/blockchain_utilities/cn_deserialize.cpp +++ b/src/blockchain_utilities/cn_deserialize.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -26,8 +26,8 @@ // 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 "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_core/tx_extra.h" +#include "cryptonote_basic/cryptonote_basic.h" +#include "cryptonote_basic/tx_extra.h" #include "cryptonote_core/blockchain.h" #include "blockchain_utilities.h" #include "common/command_line.h" diff --git a/src/blockchain_utilities/fake_core.h b/src/blockchain_utilities/fake_core.h index 814139602..70144184b 100644 --- a/src/blockchain_utilities/fake_core.h +++ b/src/blockchain_utilities/fake_core.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/blocks/CMakeLists.txt b/src/blocks/CMakeLists.txt index f65503fbc..3a866af5b 100644 --- a/src/blocks/CMakeLists.txt +++ b/src/blocks/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -30,8 +30,8 @@ if(APPLE) add_library(blocks STATIC blockexports.c) set_target_properties(blocks PROPERTIES LINKER_LANGUAGE C) else() - add_custom_command(OUTPUT blocks.o COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocks.o blocks.dat) - add_custom_command(OUTPUT testnet_blocks.o COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/testnet_blocks.o testnet_blocks.dat) + add_custom_command(OUTPUT blocks.o MAIN_DEPENDENCY blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocks.o blocks.dat) + add_custom_command(OUTPUT testnet_blocks.o MAIN_DEPENDENCY testnet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/testnet_blocks.o testnet_blocks.dat) add_library(blocks STATIC blocks.o testnet_blocks.o blockexports.c) set_target_properties(blocks PROPERTIES LINKER_LANGUAGE C) endif() diff --git a/src/blocks/blockexports.c b/src/blocks/blockexports.c index 26adaad62..477167a12 100644 --- a/src/blocks/blockexports.c +++ b/src/blocks/blockexports.c @@ -2,12 +2,19 @@ #if defined(__APPLE__) #include <mach-o/getsect.h> - +#ifdef BUILD_SHARED_LIBS +#if !defined(__LP64__) +const struct mach_header _mh_execute_header; +#else +const struct mach_header_64 _mh_execute_header; +#endif +#else #if !defined(__LP64__) extern const struct mach_header _mh_execute_header; #else extern const struct mach_header_64 _mh_execute_header; #endif +#endif const unsigned char *get_blocks_dat_start(int testnet) { diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 1864c7835..0a3e8cd19 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -26,15 +26,20 @@ # 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_directories(SYSTEM ${OPENSSL_INCLUDE_DIR}) + set(common_sources base58.cpp command_line.cpp dns_utils.cpp + download.cpp util.cpp i18n.cpp + password.cpp perf_timer.cpp task_region.cpp - thread_group.cpp) + thread_group.cpp + updates.cpp) if (STACK_TRACE) list(APPEND common_sources stack_trace.cpp) @@ -46,7 +51,9 @@ set(common_private_headers base58.h boost_serialization_helper.h command_line.h + common_fwd.h dns_utils.h + download.h http_connection.h int-util.h pod-class.h @@ -56,10 +63,12 @@ set(common_private_headers util.h varint.h i18n.h + password.h perf_timer.h stack_trace.h task_region.h - thread_group.h) + thread_group.h + updates.h) monero_private_headers(common ${common_private_headers}) @@ -69,6 +78,7 @@ monero_add_library(common ${common_private_headers}) target_link_libraries(common PUBLIC + epee crypto ${UNBOUND_LIBRARY} ${LIBUNWIND_LIBRARIES} @@ -78,6 +88,7 @@ target_link_libraries(common ${Boost_THREAD_LIBRARY} ${Boost_REGEX_LIBRARY} PRIVATE + ${OPENSSL_LIBRARIES} ${EXTRA_LIBRARIES}) #monero_install_headers(common diff --git a/src/common/base58.cpp b/src/common/base58.cpp index 355d1e209..64cb7c0de 100644 --- a/src/common/base58.cpp +++ b/src/common/base58.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/common/base58.h b/src/common/base58.h index df2ac2e9b..6dd850c03 100644 --- a/src/common/base58.h +++ b/src/common/base58.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/common/boost_serialization_helper.h b/src/common/boost_serialization_helper.h index 88dccbde7..4a503d830 100644 --- a/src/common/boost_serialization_helper.h +++ b/src/common/boost_serialization_helper.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/common/command_line.cpp b/src/common/command_line.cpp index d95859256..f71b3e576 100644 --- a/src/common/command_line.cpp +++ b/src/common/command_line.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -76,7 +76,6 @@ namespace command_line const arg_descriptor<bool> arg_version = {"version", "Output version information"}; const arg_descriptor<std::string> arg_data_dir = {"data-dir", "Specify data directory"}; const arg_descriptor<std::string> arg_testnet_data_dir = {"testnet-data-dir", "Specify testnet data directory"}; - const arg_descriptor<std::string> arg_user_agent = {"user-agent", "Restrict RPC use to clients using this user agent"}; const arg_descriptor<bool> arg_test_drop_download = {"test-drop-download", "For net tests: in download, discard ALL blocks instead checking/saving them (very fast)"}; const arg_descriptor<uint64_t> arg_test_drop_download_height = {"test-drop-download-height", "Like test-drop-download but disards only after around certain height", 0}; const arg_descriptor<int> arg_test_dbg_lock_sleep = {"test-dbg-lock-sleep", "Sleep time in ms, defaults to 0 (off), used to debug before/after locking mutex. Values 100 to 1000 are good for tests."}; @@ -121,4 +120,9 @@ namespace command_line , "How many blocks to sync at once during chain synchronization." , BLOCKS_SYNCHRONIZING_DEFAULT_COUNT }; + const command_line::arg_descriptor<std::string> arg_check_updates = { + "check-updates" + , "Check for new versions of monero: [disabled|notify|download|update]" + , "notify" + }; } diff --git a/src/common/command_line.h b/src/common/command_line.h index 3f0919e99..2110b8849 100644 --- a/src/common/command_line.h +++ b/src/common/command_line.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -168,7 +168,7 @@ namespace command_line { return parser(); } - catch (std::exception& e) + catch (const std::exception& e) { std::cerr << "Failed to parse arguments: " << e.what() << std::endl; std::cerr << desc << std::endl; @@ -207,7 +207,6 @@ namespace command_line extern const arg_descriptor<bool> arg_version; extern const arg_descriptor<std::string> arg_data_dir; extern const arg_descriptor<std::string> arg_testnet_data_dir; - extern const arg_descriptor<std::string> arg_user_agent; extern const arg_descriptor<bool> arg_test_drop_download; extern const arg_descriptor<uint64_t> arg_test_drop_download_height; extern const arg_descriptor<int> arg_test_dbg_lock_sleep; @@ -219,4 +218,5 @@ namespace command_line extern const arg_descriptor<uint64_t> arg_prep_blocks_threads; extern const arg_descriptor<uint64_t> arg_show_time_stats; extern const arg_descriptor<size_t> arg_block_sync_size; + extern const arg_descriptor<std::string> arg_check_updates; } diff --git a/src/common/common_fwd.h b/src/common/common_fwd.h new file mode 100644 index 000000000..5d67251b1 --- /dev/null +++ b/src/common/common_fwd.h @@ -0,0 +1,41 @@ +// Copyright (c) 2014-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +namespace tools +{ + class DNSResolver; + struct login; + class password_container; + class t_http_connection; + class task_region; + class thread_group; +} diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp index 83259bc70..ab38cbbae 100644 --- a/src/common/dns_utils.cpp +++ b/src/common/dns_utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -29,7 +29,7 @@ #include "common/command_line.h" #include "common/i18n.h" #include "common/dns_utils.h" -#include "cryptonote_core/cryptonote_basic_impl.h" +#include "cryptonote_basic/cryptonote_basic_impl.h" #include <cstring> #include <sstream> // check local first (in the event of static or in-source compilation of libunbound) @@ -37,6 +37,7 @@ #include <stdlib.h> #include "include_base_utils.h" +#include <random> #include <boost/filesystem/fstream.hpp> using namespace epee; namespace bf = boost::filesystem; @@ -404,21 +405,23 @@ std::vector<std::string> addresses_from_url(const std::string& url, bool& dnssec return addresses; } -std::string get_account_address_as_str_from_url(const std::string& url, bool& dnssec_valid) +std::string get_account_address_as_str_from_url(const std::string& url, bool& dnssec_valid, bool cli_confirm) { // attempt to get address from dns query auto addresses = addresses_from_url(url, dnssec_valid); if (addresses.empty()) { - std::cout << tr("wrong address: ") << url; + LOG_ERROR("wrong address: " << url); return {}; } // for now, move on only if one address found if (addresses.size() > 1) { - std::cout << tr("not yet supported: Multiple Monero addresses found for given URL: ") << url; + LOG_ERROR("not yet supported: Multiple Monero addresses found for given URL: " << url); return {}; } + if (!cli_confirm) + return addresses[0]; // prompt user for confirmation. // inform user of DNSSEC validation status as well. std::string dnssec_str; @@ -451,6 +454,110 @@ std::string get_account_address_as_str_from_url(const std::string& url, bool& dn return addresses[0]; } +namespace +{ + bool dns_records_match(const std::vector<std::string>& a, const std::vector<std::string>& b) + { + if (a.size() != b.size()) return false; + + for (const auto& record_in_a : a) + { + bool ok = false; + for (const auto& record_in_b : b) + { + if (record_in_a == record_in_b) + { + ok = true; + break; + } + } + if (!ok) return false; + } + + return true; + } +} + +bool load_txt_records_from_dns(std::vector<std::string> &good_records, const std::vector<std::string> &dns_urls) +{ + // Prevent infinite recursion when distributing + if (dns_urls.empty()) return false; + + std::vector<std::vector<std::string> > records; + records.resize(dns_urls.size()); + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<int> dis(0, dns_urls.size() - 1); + size_t first_index = dis(gen); + + bool avail, valid; + size_t cur_index = first_index; + do + { + std::string url = dns_urls[cur_index]; + + records[cur_index] = tools::DNSResolver::instance().get_txt_record(url, avail, valid); + if (!avail) + { + records[cur_index].clear(); + LOG_PRINT_L2("DNSSEC not available for checkpoint update at URL: " << url << ", skipping."); + } + if (!valid) + { + records[cur_index].clear(); + LOG_PRINT_L2("DNSSEC validation failed for checkpoint update at URL: " << url << ", skipping."); + } + + cur_index++; + if (cur_index == dns_urls.size()) + { + cur_index = 0; + } + } while (cur_index != first_index); + + size_t num_valid_records = 0; + + for( const auto& record_set : records) + { + if (record_set.size() != 0) + { + num_valid_records++; + } + } + + if (num_valid_records < 2) + { + LOG_PRINT_L0("WARNING: no two valid MoneroPulse DNS checkpoint records were received"); + return false; + } + + int good_records_index = -1; + for (size_t i = 0; i < records.size() - 1; ++i) + { + if (records[i].size() == 0) continue; + + for (size_t j = i + 1; j < records.size(); ++j) + { + if (dns_records_match(records[i], records[j])) + { + good_records_index = i; + break; + } + } + if (good_records_index >= 0) break; + } + + if (good_records_index < 0) + { + LOG_PRINT_L0("WARNING: no two MoneroPulse DNS checkpoint records matched"); + return false; + } + + good_records = records[good_records_index]; + return true; +} + } // namespace tools::dns_utils } // namespace tools diff --git a/src/common/dns_utils.h b/src/common/dns_utils.h index 8a63a8129..53c0c1c7b 100644 --- a/src/common/dns_utils.h +++ b/src/common/dns_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -29,7 +29,7 @@ #include <vector> #include <string> -#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_basic.h" namespace tools { @@ -163,7 +163,9 @@ namespace dns_utils std::string address_from_txt_record(const std::string& s); std::vector<std::string> addresses_from_url(const std::string& url, bool& dnssec_valid); -std::string get_account_address_as_str_from_url(const std::string& url, bool& dnssec_valid); +std::string get_account_address_as_str_from_url(const std::string& url, bool& dnssec_valid, bool cli_confirm = true); + +bool load_txt_records_from_dns(std::vector<std::string> &records, const std::vector<std::string> &dns_urls); } // namespace tools::dns_utils diff --git a/src/common/download.cpp b/src/common/download.cpp new file mode 100644 index 000000000..28aac5a59 --- /dev/null +++ b/src/common/download.cpp @@ -0,0 +1,270 @@ +// Copyright (c) 2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <string> +#include <atomic> +#include <boost/filesystem.hpp> +#include <boost/asio.hpp> +#include <boost/thread/thread.hpp> +#include "cryptonote_config.h" +#include "include_base_utils.h" +#include "net/http_client.h" +#include "download.h" + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "net.dl" + +namespace tools +{ + struct download_thread_control + { + const std::string path; + const std::string uri; + std::function<void(const std::string&, const std::string&, bool)> result_cb; + std::function<bool(const std::string&, const std::string&, size_t, ssize_t)> progress_cb; + bool stop; + bool stopped; + bool success; + boost::thread thread; + boost::mutex mutex; + + download_thread_control(const std::string &path, const std::string &uri, std::function<void(const std::string&, const std::string&, bool)> result_cb, std::function<bool(const std::string&, const std::string&, size_t, ssize_t)> progress_cb): + path(path), uri(uri), result_cb(result_cb), progress_cb(progress_cb), stop(false), stopped(false), success(false) {} + ~download_thread_control() { if (thread.joinable()) thread.detach(); } + }; + + static void download_thread(download_async_handle control) + { + static std::atomic<unsigned int> thread_id(0); + + MLOG_SET_THREAD_NAME("DL" + std::to_string(thread_id++)); + + struct stopped_setter + { + stopped_setter(const download_async_handle &control): control(control) {} + ~stopped_setter() { control->stopped = true; } + download_async_handle control; + } stopped_setter(control); + + try + { + boost::unique_lock<boost::mutex> lock(control->mutex); + MINFO("Downloading " << control->uri << " to " << control->path); + std::ofstream f; + f.open(control->path, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc); + if (!f.good()) { + MERROR("Failed to open file " << control->path); + control->result_cb(control->path, control->uri, control->success); + return; + } + class download_client: public epee::net_utils::http::http_simple_client + { + public: + download_client(download_async_handle control, std::ofstream &f): + control(control), f(f), content_length(-1), total(0) {} + virtual ~download_client() { f.close(); } + virtual bool on_header(const epee::net_utils::http::http_response_info &headers) + { + ssize_t length; + if (epee::string_tools::get_xtype_from_string(length, headers.m_header_info.m_content_length) && length >= 0) + { + MINFO("Content-Length: " << length); + content_length = length; + boost::filesystem::path path(control->path); + boost::filesystem::space_info si = boost::filesystem::space(path); + if (si.available < (size_t)content_length) + { + const uint64_t avail = (si.available + 1023) / 1024, needed = (content_length + 1023) / 1024; + MERROR("Not enough space to download " << needed << " kB to " << path << " (" << avail << " kB available)"); + return false; + } + } + return true; + } + virtual bool handle_target_data(std::string &piece_of_transfer) + { + try + { + boost::lock_guard<boost::mutex> lock(control->mutex); + if (control->stop) + return false; + f << piece_of_transfer; + total += piece_of_transfer.size(); + if (control->progress_cb && !control->progress_cb(control->path, control->uri, total, content_length)) + return false; + return f.good(); + } + catch (const std::exception &e) + { + MERROR("Error writing data: " << e.what()); + return false; + } + } + private: + download_async_handle control; + std::ofstream &f; + ssize_t content_length; + size_t total; + } client(control, f); + epee::net_utils::http::url_content u_c; + if (!epee::net_utils::parse_url(control->uri, u_c)) + { + MERROR("Failed to parse URL " << control->uri); + control->result_cb(control->path, control->uri, control->success); + return; + } + if (u_c.host.empty()) + { + MERROR("Failed to determine address from URL " << control->uri); + control->result_cb(control->path, control->uri, control->success); + return; + } + + lock.unlock(); + + uint16_t port = u_c.port ? u_c.port : 80; + MDEBUG("Connecting to " << u_c.host << ":" << port); + client.set_server(u_c.host, std::to_string(port), boost::none); + if (!client.connect(std::chrono::seconds(30))) + { + boost::lock_guard<boost::mutex> lock(control->mutex); + MERROR("Failed to connect to " << control->uri); + control->result_cb(control->path, control->uri, control->success); + return; + } + MDEBUG("GETting " << u_c.uri); + const epee::net_utils::http::http_response_info *info = NULL; + if (!client.invoke_get(u_c.uri, std::chrono::seconds(30), "", &info)) + { + boost::lock_guard<boost::mutex> lock(control->mutex); + MERROR("Failed to connect to " << control->uri); + client.disconnect(); + control->result_cb(control->path, control->uri, control->success); + return; + } + if (control->stop) + { + boost::lock_guard<boost::mutex> lock(control->mutex); + MDEBUG("Download cancelled"); + client.disconnect(); + control->result_cb(control->path, control->uri, control->success); + return; + } + if (!info) + { + boost::lock_guard<boost::mutex> lock(control->mutex); + MERROR("Failed invoking GET command to " << control->uri << ", no status info returned"); + client.disconnect(); + control->result_cb(control->path, control->uri, control->success); + return; + } + MDEBUG("response code: " << info->m_response_code); + MDEBUG("response length: " << info->m_header_info.m_content_length); + MDEBUG("response comment: " << info->m_response_comment); + MDEBUG("response body: " << info->m_body); + for (const auto &f: info->m_additional_fields) + MDEBUG("additional field: " << f.first << ": " << f.second); + if (info->m_response_code != 200) + { + boost::lock_guard<boost::mutex> lock(control->mutex); + MERROR("Status code " << info->m_response_code); + client.disconnect(); + control->result_cb(control->path, control->uri, control->success); + return; + } + client.disconnect(); + f.close(); + MDEBUG("Download complete"); + lock.lock(); + control->success = true; + control->result_cb(control->path, control->uri, control->success); + return; + } + catch (const std::exception &e) + { + MERROR("Exception in download thread: " << e.what()); + // fall through and call result_cb not from the catch block to avoid another exception + } + boost::lock_guard<boost::mutex> lock(control->mutex); + control->result_cb(control->path, control->uri, control->success); + } + + bool download(const std::string &path, const std::string &url, std::function<bool(const std::string&, const std::string&, size_t, ssize_t)> cb) + { + bool success = false; + download_async_handle handle = download_async(path, url, [&success](const std::string&, const std::string&, bool result) {success = result;}, cb); + download_wait(handle); + return success; + } + + download_async_handle download_async(const std::string &path, const std::string &url, std::function<void(const std::string&, const std::string&, bool)> result, std::function<bool(const std::string&, const std::string&, size_t, ssize_t)> progress) + { + download_async_handle control = std::make_shared<download_thread_control>(path, url, result, progress); + control->thread = boost::thread([control](){ download_thread(control); }); + return control; + } + + bool download_finished(const download_async_handle &control) + { + CHECK_AND_ASSERT_MES(control != 0, false, "NULL async download handle"); + boost::lock_guard<boost::mutex> lock(control->mutex); + return control->stopped; + } + + bool download_error(const download_async_handle &control) + { + CHECK_AND_ASSERT_MES(control != 0, false, "NULL async download handle"); + boost::lock_guard<boost::mutex> lock(control->mutex); + return !control->success; + } + + bool download_wait(const download_async_handle &control) + { + CHECK_AND_ASSERT_MES(control != 0, false, "NULL async download handle"); + { + boost::lock_guard<boost::mutex> lock(control->mutex); + if (control->stopped) + return true; + } + control->thread.join(); + return true; + } + + bool download_cancel(const download_async_handle &control) + { + CHECK_AND_ASSERT_MES(control != 0, false, "NULL async download handle"); + { + boost::lock_guard<boost::mutex> lock(control->mutex); + if (control->stopped) + return true; + control->stop = true; + } + control->thread.join(); + return true; + } +} diff --git a/src/common/download.h b/src/common/download.h new file mode 100644 index 000000000..917cb2278 --- /dev/null +++ b/src/common/download.h @@ -0,0 +1,44 @@ +// Copyright (c) 2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include <string> + +namespace tools +{ + struct download_thread_control; + typedef std::shared_ptr<download_thread_control> download_async_handle; + + bool download(const std::string &path, const std::string &url, std::function<bool(const std::string&, const std::string&, size_t, ssize_t)> progress = NULL); + download_async_handle download_async(const std::string &path, const std::string &url, std::function<void(const std::string&, const std::string&, bool)> result, std::function<bool(const std::string&, const std::string&, size_t, ssize_t)> progress = NULL); + bool download_error(const download_async_handle &h); + bool download_finished(const download_async_handle &h); + bool download_wait(const download_async_handle &h); + bool download_cancel(const download_async_handle &h); +} diff --git a/src/common/http_connection.h b/src/common/http_connection.h index 8a786361a..0357a90a0 100644 --- a/src/common/http_connection.h +++ b/src/common/http_connection.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/common/i18n.cpp b/src/common/i18n.cpp index 05eea3b66..4a76e76fc 100644 --- a/src/common/i18n.cpp +++ b/src/common/i18n.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/common/i18n.h b/src/common/i18n.h index 332602185..5169cf9f7 100644 --- a/src/common/i18n.h +++ b/src/common/i18n.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/common/int-util.h b/src/common/int-util.h index 9ac20e582..34288805a 100644 --- a/src/common/int-util.h +++ b/src/common/int-util.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/wallet/password_container.cpp b/src/common/password.cpp index 832b93a1a..bdc9c69c0 100644 --- a/src/wallet/password_container.cpp +++ b/src/common/password.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -28,7 +28,7 @@ // // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers -#include "password_container.h" +#include "password.h" #include <iostream> #include <memory.h> @@ -245,4 +245,27 @@ namespace tools return boost::none; } + + boost::optional<login> login::parse(std::string&& userpass, bool verify, const char* message) + { + login out{}; + password_container wipe{std::move(userpass)}; + + const auto loc = wipe.password().find(':'); + if (loc == std::string::npos) + { + auto result = tools::password_container::prompt(verify, message); + if (!result) + return boost::none; + + out.password = std::move(*result); + } + else + { + out.password = password_container{wipe.password().substr(loc + 1)}; + } + + out.username = wipe.password().substr(0, loc); + return {std::move(out)}; + } } diff --git a/src/wallet/password_container.h b/src/common/password.h index 9c6faf9c8..12f715df4 100644 --- a/src/wallet/password_container.h +++ b/src/common/password.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -64,4 +64,33 @@ namespace tools //! TODO Custom allocator that locks to RAM? std::string m_password; }; + + struct login + { + login() = default; + + /*! + Extracts username and password from the format `username:password`. A + blank username or password is allowed. If the `:` character is not + present, `password_container::prompt` will be called by forwarding the + `verify` and `message` arguments. + + \param userpass Is "consumed", and the memory contents are wiped. + \param verify is passed to `password_container::prompt` if necessary. + \param message is passed to `password_container::prompt` if necessary. + + \return The username and password, or boost::none if + `password_container::prompt` fails. + */ + static boost::optional<login> parse(std::string&& userpass, bool verify, const char* message = "Password"); + + login(const login&) = delete; + login(login&&) = default; + ~login() = default; + login& operator=(const login&) = delete; + login& operator=(login&&) = default; + + std::string username; + password_container password; + }; } diff --git a/src/common/pod-class.h b/src/common/pod-class.h index f3f241552..3896d5c29 100644 --- a/src/common/pod-class.h +++ b/src/common/pod-class.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/common/rpc_client.h b/src/common/rpc_client.h index f5ecc8b50..8494b4a60 100644 --- a/src/common/rpc_client.h +++ b/src/common/rpc_client.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -28,10 +28,13 @@ #pragma once +#include <boost/optional/optional.hpp> + #include "common/http_connection.h" #include "common/scoped_message_writer.h" #include "rpc/core_rpc_server_commands_defs.h" #include "storages/http_abstract_invoke.h" +#include "net/http_auth.h" #include "net/http_client.h" #include "string_tools.h" @@ -45,11 +48,12 @@ namespace tools t_rpc_client( uint32_t ip , uint16_t port + , boost::optional<epee::net_utils::http::login> user ) : m_http_client{} { m_http_client.set_server( - epee::string_tools::get_ip_string_from_int32(ip), std::to_string(port) + epee::string_tools::get_ip_string_from_int32(ip), std::to_string(port), std::move(user) ); } diff --git a/src/common/scoped_message_writer.h b/src/common/scoped_message_writer.h index f82181926..7ee4f1379 100644 --- a/src/common/scoped_message_writer.h +++ b/src/common/scoped_message_writer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/common/stack_trace.cpp b/src/common/stack_trace.cpp index 67065aae7..ef64c20c5 100644 --- a/src/common/stack_trace.cpp +++ b/src/common/stack_trace.cpp @@ -26,7 +26,7 @@ // 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. -#if !defined __GNUC__ || defined __MINGW32__ || defined __MINGW64__ +#if !defined __GNUC__ || defined __MINGW32__ || defined __MINGW64__ || defined __ANDROID__ #define USE_UNWIND #endif diff --git a/src/common/task_region.cpp b/src/common/task_region.cpp index b53a8376a..9b4620c6e 100644 --- a/src/common/task_region.cpp +++ b/src/common/task_region.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/common/task_region.h b/src/common/task_region.h index e4d210661..30972cce3 100644 --- a/src/common/task_region.h +++ b/src/common/task_region.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/common/thread_group.cpp b/src/common/thread_group.cpp index 4e1cc8964..860d0b732 100644 --- a/src/common/thread_group.cpp +++ b/src/common/thread_group.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/common/thread_group.h b/src/common/thread_group.h index 10add89e0..48fd4cd56 100644 --- a/src/common/thread_group.h +++ b/src/common/thread_group.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/common/unordered_containers_boost_serialization.h b/src/common/unordered_containers_boost_serialization.h index b2d5b27a6..4d82a1364 100644 --- a/src/common/unordered_containers_boost_serialization.h +++ b/src/common/unordered_containers_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/common/updates.cpp b/src/common/updates.cpp new file mode 100644 index 000000000..5b1acf5fa --- /dev/null +++ b/src/common/updates.cpp @@ -0,0 +1,115 @@ +// Copyright (c) 2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "util.h" +#include "dns_utils.h" +#include "updates.h" + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "updates" + +namespace tools +{ + bool check_updates(const std::string &software, const std::string &buildtag, std::string &version, std::string &hash) + { + std::vector<std::string> records; + bool found = false; + + MDEBUG("Checking updates for " << buildtag << " " << software); + + // All four MoneroPulse domains have DNSSEC on and valid + static const std::vector<std::string> dns_urls = { + "updates.moneropulse.org", + "updates.moneropulse.net", + "updates.moneropulse.co", + "updates.moneropulse.se" + }; + + if (!tools::dns_utils::load_txt_records_from_dns(records, dns_urls)) + return false; + + for (const auto& record : records) + { + std::vector<std::string> fields; + boost::split(fields, record, boost::is_any_of(":")); + if (fields.size() != 4) + { + MWARNING("Updates record does not have 4 fields: " << record); + continue; + } + + if (software != fields[0] || buildtag != fields[1]) + continue; + + bool alnum = true; + for (auto c: hash) + if (!isalnum(c)) + alnum = false; + if (hash.size() != 64 && !alnum) + { + MWARNING("Invalid hash: " << hash); + continue; + } + + // use highest version + if (found) + { + int cmp = vercmp(version.c_str(), fields[2].c_str()); + if (cmp > 0) + continue; + if (cmp == 0 && hash != fields[3]) + MWARNING("Two matches found for " << software << " version " << version << " on " << buildtag); + } + + version = fields[2]; + hash = fields[3]; + + MINFO("Found new version " << version << " with hash " << hash); + found = true; + } + return found; + } + + std::string get_update_url(const std::string &software, const std::string &subdir, const std::string &buildtag, const std::string &version, bool user) + { + const char *base = user ? "https://downloads.getmonero.org/" : "http://updates.getmonero.org/"; +#ifdef _WIN32 + static const char extension[] = ".zip"; +#else + static const char extension[] = ".tar.bz2"; +#endif + + std::string url; + + url = base; + if (!subdir.empty()) + url += subdir + "/"; + url = url + software + "-" + buildtag + "-v" + version + extension; + return url; + } +} diff --git a/src/common/updates.h b/src/common/updates.h new file mode 100644 index 000000000..e494ed7ac --- /dev/null +++ b/src/common/updates.h @@ -0,0 +1,37 @@ +// Copyright (c) 2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include <string> + +namespace tools +{ + bool check_updates(const std::string &software, const std::string &buildtag, std::string &version, std::string &hash); + std::string get_update_url(const std::string &software, const std::string &subdir, const std::string &buildtag, const std::string &version, bool user); +} diff --git a/src/common/util.cpp b/src/common/util.cpp index bfcf86bc6..046961b06 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -31,6 +31,7 @@ #include <cstdio> #include "include_base_utils.h" +#include "file_io_utils.h" using namespace epee; #include "util.h" @@ -46,7 +47,7 @@ using namespace epee; #endif #include <boost/filesystem.hpp> #include <boost/asio.hpp> - +#include <openssl/sha.h> namespace tools { @@ -568,4 +569,65 @@ std::string get_nix_version_display_string() MDEBUG("Address '" << address << "' is not local"); return false; } + int vercmp(const char *v0, const char *v1) + { + std::vector<std::string> f0, f1; + boost::split(f0, v0, boost::is_any_of(".")); + boost::split(f1, v1, boost::is_any_of(".")); + while (f0.size() < f1.size()) + f0.push_back("0"); + while (f1.size() < f0.size()) + f1.push_back("0"); + for (size_t i = 0; i < f0.size(); ++i) { + int f0i = atoi(f0[i].c_str()), f1i = atoi(f1[i].c_str()); + int n = f0i - f1i; + if (n) + return n; + } + return 0; + } + + bool sha256sum(const uint8_t *data, size_t len, crypto::hash &hash) + { + SHA256_CTX ctx; + if (!SHA256_Init(&ctx)) + return false; + if (!SHA256_Update(&ctx, data, len)) + return false; + if (!SHA256_Final((unsigned char*)hash.data, &ctx)) + return false; + return true; + } + + bool sha256sum(const std::string &filename, crypto::hash &hash) + { + if (!epee::file_io_utils::is_file_exist(filename)) + return false; + std::ifstream f; + f.exceptions(std::ifstream::failbit | std::ifstream::badbit); + f.open(filename, std::ios_base::binary | std::ios_base::in | std::ios::ate); + if (!f) + return false; + std::ifstream::pos_type file_size = f.tellg(); + SHA256_CTX ctx; + if (!SHA256_Init(&ctx)) + return false; + size_t size_left = file_size; + f.seekg(0, std::ios::beg); + while (size_left) + { + char buf[4096]; + std::ifstream::pos_type read_size = size_left > sizeof(buf) ? sizeof(buf) : size_left; + f.read(buf, read_size); + if (!f || !f.good()) + return false; + if (!SHA256_Update(&ctx, buf, read_size)) + return false; + size_left -= read_size; + } + f.close(); + if (!SHA256_Final((unsigned char*)hash.data, &ctx)) + return false; + return true; + } } diff --git a/src/common/util.h b/src/common/util.h index c2ffc44ca..4291d7e18 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -183,4 +183,8 @@ namespace tools unsigned get_max_concurrency(); bool is_local_address(const std::string &address); + int vercmp(const char *v0, const char *v1); // returns < 0, 0, > 0, similar to strcmp, but more human friendly than lexical - does not attempt to validate + + bool sha256sum(const uint8_t *data, size_t len, crypto::hash &hash); + bool sha256sum(const std::string &filename, crypto::hash &hash); } diff --git a/src/common/varint.h b/src/common/varint.h index ffaa682c5..cb785e61a 100644 --- a/src/common/varint.h +++ b/src/common/varint.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt index 1e037a07d..277ee64c2 100644 --- a/src/crypto/CMakeLists.txt +++ b/src/crypto/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -93,7 +93,7 @@ endif() # Because of the way Qt works on android with JNI, the code does not live in the main android thread # So this code runs with a 1 MB default stack size. # This will force the use of the heap for the allocation of the scratchpad -if (ANDROID) +if (ANDROID OR IOS) if( BUILD_GUI_DEPS ) add_definitions(-DFORCE_USE_HEAP=1) endif() diff --git a/src/crypto/blake256.c b/src/crypto/blake256.c index 1cb1cf344..1e43f9c4d 100644 --- a/src/crypto/blake256.c +++ b/src/crypto/blake256.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/blake256.h b/src/crypto/blake256.h index e262d1b4b..921fcd2fd 100644 --- a/src/crypto/blake256.h +++ b/src/crypto/blake256.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/chacha8.h b/src/crypto/chacha8.h index 94c0ba721..80557e9f5 100644 --- a/src/crypto/chacha8.h +++ b/src/crypto/chacha8.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto-ops-data.c b/src/crypto/crypto-ops-data.c index 60ee38096..4bd75b77c 100644 --- a/src/crypto/crypto-ops-data.c +++ b/src/crypto/crypto-ops-data.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto-ops.c b/src/crypto/crypto-ops.c index 1b390e402..4edfee0ce 100644 --- a/src/crypto/crypto-ops.c +++ b/src/crypto/crypto-ops.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto-ops.h b/src/crypto/crypto-ops.h index 4986499f4..37edf5b6d 100644 --- a/src/crypto/crypto-ops.h +++ b/src/crypto/crypto-ops.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp index 6ceb944cd..98da466cc 100644 --- a/src/crypto/crypto.cpp +++ b/src/crypto/crypto.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index 9ca835d9e..3b8c7996b 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto_ops_builder/README.md b/src/crypto/crypto_ops_builder/README.md index eec3e21e7..3b87966f5 100644 --- a/src/crypto/crypto_ops_builder/README.md +++ b/src/crypto/crypto_ops_builder/README.md @@ -1,6 +1,6 @@ # Monero -Copyright (c) 2014-2016, The Monero Project +Copyright (c) 2014-2017, The Monero Project ## Crypto Ops Builder diff --git a/src/crypto/crypto_ops_builder/crypto-ops-data.c b/src/crypto/crypto_ops_builder/crypto-ops-data.c index 60ee38096..4bd75b77c 100644 --- a/src/crypto/crypto_ops_builder/crypto-ops-data.c +++ b/src/crypto/crypto_ops_builder/crypto-ops-data.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto_ops_builder/crypto-ops-old.c b/src/crypto/crypto_ops_builder/crypto-ops-old.c index 910801c57..b7a290b4a 100644 --- a/src/crypto/crypto_ops_builder/crypto-ops-old.c +++ b/src/crypto/crypto_ops_builder/crypto-ops-old.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto_ops_builder/crypto-ops.h b/src/crypto/crypto_ops_builder/crypto-ops.h index 84ef12ae2..47d5b46ae 100644 --- a/src/crypto/crypto_ops_builder/crypto-ops.h +++ b/src/crypto/crypto_ops_builder/crypto-ops.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto_ops_builder/ref10CommentedCombined/MakeCryptoOps.py b/src/crypto/crypto_ops_builder/ref10CommentedCombined/MakeCryptoOps.py index 9d996f9d1..5f8776a49 100644 --- a/src/crypto/crypto_ops_builder/ref10CommentedCombined/MakeCryptoOps.py +++ b/src/crypto/crypto_ops_builder/ref10CommentedCombined/MakeCryptoOps.py @@ -15,7 +15,7 @@ print("maybe someone smart can replace the sed with perl..") a = "" license = textwrap.dedent("""\ - // Copyright (c) 2014-2016, The Monero Project + // Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto_ops_builder/ref10CommentedCombined/crypto-ops.h b/src/crypto/crypto_ops_builder/ref10CommentedCombined/crypto-ops.h index cdc5ac1ee..b432efade 100644 --- a/src/crypto/crypto_ops_builder/ref10CommentedCombined/crypto-ops.h +++ b/src/crypto/crypto_ops_builder/ref10CommentedCombined/crypto-ops.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/generic-ops.h b/src/crypto/generic-ops.h index a8d8fdb19..1a135ffcf 100644 --- a/src/crypto/generic-ops.h +++ b/src/crypto/generic-ops.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/groestl.h b/src/crypto/groestl.h index ac749d1d8..89a073a4c 100644 --- a/src/crypto/groestl.h +++ b/src/crypto/groestl.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/groestl_tables.h b/src/crypto/groestl_tables.h index c271fd367..8fa6d7a83 100644 --- a/src/crypto/groestl_tables.h +++ b/src/crypto/groestl_tables.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash-extra-blake.c b/src/crypto/hash-extra-blake.c index 15357dc7a..236479880 100644 --- a/src/crypto/hash-extra-blake.c +++ b/src/crypto/hash-extra-blake.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash-extra-groestl.c b/src/crypto/hash-extra-groestl.c index 69546ee44..b15075306 100644 --- a/src/crypto/hash-extra-groestl.c +++ b/src/crypto/hash-extra-groestl.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash-extra-jh.c b/src/crypto/hash-extra-jh.c index 5da0894f6..8950687d3 100644 --- a/src/crypto/hash-extra-jh.c +++ b/src/crypto/hash-extra-jh.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash-extra-skein.c b/src/crypto/hash-extra-skein.c index babf5006f..e63e7da20 100644 --- a/src/crypto/hash-extra-skein.c +++ b/src/crypto/hash-extra-skein.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h index 612d94efe..6e3a5c6c9 100644 --- a/src/crypto/hash-ops.h +++ b/src/crypto/hash-ops.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash.c b/src/crypto/hash.c index 93f7353e4..ed95391d8 100644 --- a/src/crypto/hash.c +++ b/src/crypto/hash.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash.h b/src/crypto/hash.h index 803992078..22991e513 100644 --- a/src/crypto/hash.h +++ b/src/crypto/hash.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/initializer.h b/src/crypto/initializer.h index fefc3b7c3..619038ae6 100644 --- a/src/crypto/initializer.h +++ b/src/crypto/initializer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/random.c b/src/crypto/random.c index 6a9f63c12..691c31f62 100644 --- a/src/crypto/random.c +++ b/src/crypto/random.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/random.h b/src/crypto/random.h index b0d2303b6..75d23fd04 100644 --- a/src/crypto/random.h +++ b/src/crypto/random.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/skein_port.h b/src/crypto/skein_port.h index 0b3d071ee..a06ef30a2 100644 --- a/src/crypto/skein_port.h +++ b/src/crypto/skein_port.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index 43b9619f3..6afa28934 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -44,6 +44,9 @@ #define INIT_SIZE_BLK 8 #define INIT_SIZE_BYTE (INIT_SIZE_BLK * AES_BLOCK_SIZE) +extern int aesb_single_round(const uint8_t *in, uint8_t*out, const uint8_t *expandedKey); +extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *expandedKey); + #if defined(__x86_64__) || (defined(_MSC_VER) && defined(_WIN64)) // Optimised code below, uses x86-specific intrinsics, SSE2, AES-NI // Fall back to more portable code is down at the bottom @@ -138,9 +141,6 @@ #define THREADV __thread #endif -extern int aesb_single_round(const uint8_t *in, uint8_t*out, const uint8_t *expandedKey); -extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *expandedKey); - #pragma pack(push, 1) union cn_slow_hash_state { @@ -494,7 +494,7 @@ void slow_hash_free_state(void) * buffer of pseudorandom data by hashing the supplied data. It then uses this * random data to fill a large 2MB buffer with pseudorandom data by iteratively * encrypting it using 10 rounds of AES per entry. After this initialization, - * it executes 500,000 rounds of mixing through the random 2MB buffer using + * it executes 524,288 rounds of mixing through the random 2MB buffer using * AES (typically provided in hardware on modern CPUs) and a 64 bit multiply. * Finally, it re-mixes this large buffer back into * the 200 byte "text" buffer, and then hashes this buffer using one of four @@ -530,7 +530,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash) size_t i, j; uint64_t *p = NULL; - oaes_ctx *aes_ctx; + oaes_ctx *aes_ctx = NULL; int useAes = !force_software_aes() && check_aes_hw(); static void (*const extra_hashes[4])(const void *, size_t, char *) = @@ -578,8 +578,8 @@ void cn_slow_hash(const void *data, size_t length, char *hash) U64(b)[0] = U64(&state.k[16])[0] ^ U64(&state.k[48])[0]; U64(b)[1] = U64(&state.k[16])[1] ^ U64(&state.k[48])[1]; - /* CryptoNight Step 3: Bounce randomly 1 million times through the mixing buffer, - * using 500,000 iterations of the following mixing function. Each execution + /* CryptoNight Step 3: Bounce randomly 1,048,576 times (1<<20) through the mixing buffer, + * using 524,288 iterations of the following mixing function. Each execution * performs two reads and writes from the mixing buffer. */ @@ -722,32 +722,24 @@ union cn_slow_hash_state * key schedule. Don't try to use this for vanilla AES. */ static void aes_expand_key(const uint8_t *key, uint8_t *expandedKey) { -__asm__("mov x2, %1\n\t" : : "r"(key), "r"(expandedKey)); +static const int rcon[] = { + 0x01,0x01,0x01,0x01, + 0x0c0f0e0d,0x0c0f0e0d,0x0c0f0e0d,0x0c0f0e0d, // rotate-n-splat + 0x1b,0x1b,0x1b,0x1b }; __asm__( -" adr x3,Lrcon\n" -"\n" " eor v0.16b,v0.16b,v0.16b\n" -" ld1 {v3.16b},[x0],#16\n" -" ld1 {v1.4s,v2.4s},[x3],#32\n" -" b L256\n" -".align 5\n" -"Lrcon:\n" -".long 0x01,0x01,0x01,0x01\n" -".long 0x0c0f0e0d,0x0c0f0e0d,0x0c0f0e0d,0x0c0f0e0d // rotate-n-splat\n" -".long 0x1b,0x1b,0x1b,0x1b\n" -"\n" -".align 4\n" -"L256:\n" -" ld1 {v4.16b},[x0]\n" -" mov w1,#5\n" -" st1 {v3.4s},[x2],#16\n" +" ld1 {v3.16b},[%0],#16\n" +" ld1 {v1.4s,v2.4s},[%2],#32\n" +" ld1 {v4.16b},[%0]\n" +" mov w2,#5\n" +" st1 {v3.4s},[%1],#16\n" "\n" -"Loop256:\n" +"1:\n" " tbl v6.16b,{v4.16b},v2.16b\n" " ext v5.16b,v0.16b,v3.16b,#12\n" -" st1 {v4.4s},[x2],#16\n" +" st1 {v4.4s},[%1],#16\n" " aese v6.16b,v0.16b\n" -" subs w1,w1,#1\n" +" subs w2,w2,#1\n" "\n" " eor v3.16b,v3.16b,v5.16b\n" " ext v5.16b,v0.16b,v5.16b,#12\n" @@ -757,8 +749,8 @@ __asm__( " eor v3.16b,v3.16b,v5.16b\n" " shl v1.16b,v1.16b,#1\n" " eor v3.16b,v3.16b,v6.16b\n" -" st1 {v3.4s},[x2],#16\n" -" b.eq Ldone\n" +" st1 {v3.4s},[%1],#16\n" +" b.eq 2f\n" "\n" " dup v6.4s,v3.s[3] // just splat\n" " ext v5.16b,v0.16b,v4.16b,#12\n" @@ -771,9 +763,9 @@ __asm__( " eor v4.16b,v4.16b,v5.16b\n" "\n" " eor v4.16b,v4.16b,v6.16b\n" -" b Loop256\n" +" b 1b\n" "\n" -"Ldone:\n"); +"2:\n" : : "r"(key), "r"(expandedKey), "r"(rcon)); } /* An ordinary AES round is a sequence of SubBytes, ShiftRows, MixColumns, AddRoundKey. There @@ -895,8 +887,8 @@ void cn_slow_hash(const void *data, size_t length, char *hash) U64(b)[0] = U64(&state.k[16])[0] ^ U64(&state.k[48])[0]; U64(b)[1] = U64(&state.k[16])[1] ^ U64(&state.k[48])[1]; - /* CryptoNight Step 3: Bounce randomly 1 million times through the mixing buffer, - * using 500,000 iterations of the following mixing function. Each execution + /* CryptoNight Step 3: Bounce randomly 1,048,576 times (1<<20) through the mixing buffer, + * using 524,288 iterations of the following mixing function. Each execution * performs two reads and writes from the mixing buffer. */ diff --git a/src/crypto/tree-hash.c b/src/crypto/tree-hash.c index 5cdaa8c94..eb98c31b7 100644 --- a/src/crypto/tree-hash.c +++ b/src/crypto/tree-hash.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt new file mode 100644 index 000000000..2b8ad365a --- /dev/null +++ b/src/cryptonote_basic/CMakeLists.txt @@ -0,0 +1,73 @@ +# Copyright (c) 2014-2017, The Monero Project +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are +# permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list +# of conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be +# used to endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +set(cryptonote_basic_sources + account.cpp + checkpoints.cpp + cryptonote_basic_impl.cpp + cryptonote_format_utils.cpp + difficulty.cpp + hardfork.cpp + miner.cpp) + +set(cryptonote_basic_headers) + +set(cryptonote_basic_private_headers + account.h + account_boost_serialization.h + checkpoints.h + connection_context.h + cryptonote_basic.h + cryptonote_basic_impl.h + cryptonote_boost_serialization.h + cryptonote_format_utils.h + cryptonote_stat_info.h + difficulty.h + hardfork.h + miner.h + tx_extra.h + verification_context.h) + +monero_private_headers(cryptonote_basic + ${crypto_private_headers}) +monero_add_library(cryptonote_basic + ${cryptonote_basic_sources} + ${cryptonote_basic_headers} + ${cryptonote_basic_private_headers}) +target_link_libraries(cryptonote_basic + PUBLIC + common + crypto + ${Boost_DATE_TIME_LIBRARY} + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${Boost_SERIALIZATION_LIBRARY} + ${Boost_FILESYSTEM_LIBRARY} + ${Boost_SYSTEM_LIBRARY} + ${Boost_THREAD_LIBRARY} + PRIVATE + ${EXTRA_LIBRARIES}) diff --git a/src/cryptonote_core/account.cpp b/src/cryptonote_basic/account.cpp index 8f2db6863..dd875402f 100644 --- a/src/cryptonote_core/account.cpp +++ b/src/cryptonote_basic/account.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -38,8 +38,8 @@ extern "C" { #include "crypto/keccak.h" } -#include "cryptonote_core/cryptonote_basic_impl.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic_impl.h" +#include "cryptonote_format_utils.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "account" diff --git a/src/cryptonote_core/account.h b/src/cryptonote_basic/account.h index 41a119b07..e0d5447a2 100644 --- a/src/cryptonote_core/account.h +++ b/src/cryptonote_basic/account.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -30,7 +30,7 @@ #pragma once -#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_basic.h" #include "crypto/crypto.h" #include "serialization/keyvalue_serialization.h" diff --git a/src/cryptonote_core/account_boost_serialization.h b/src/cryptonote_basic/account_boost_serialization.h index 4151d9b50..d2f541638 100644 --- a/src/cryptonote_core/account_boost_serialization.h +++ b/src/cryptonote_basic/account_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -31,7 +31,7 @@ #pragma once #include "account.h" -#include "cryptonote_core/cryptonote_boost_serialization.h" +#include "cryptonote_boost_serialization.h" //namespace cryptonote { namespace boost diff --git a/src/cryptonote_core/checkpoints.cpp b/src/cryptonote_basic/checkpoints.cpp index 3cf804ede..103a4a33e 100644 --- a/src/cryptonote_core/checkpoints.cpp +++ b/src/cryptonote_basic/checkpoints.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -42,30 +42,6 @@ using namespace epee; #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "checkpoints" -namespace -{ - bool dns_records_match(const std::vector<std::string>& a, const std::vector<std::string>& b) - { - if (a.size() != b.size()) return false; - - for (const auto& record_in_a : a) - { - bool ok = false; - for (const auto& record_in_b : b) - { - if (record_in_a == record_in_b) - { - ok = true; - break; - } - } - if (!ok) return false; - } - - return true; - } -} // anonymous namespace - namespace cryptonote { //--------------------------------------------------------------------------- @@ -230,6 +206,8 @@ namespace cryptonote bool checkpoints::load_checkpoints_from_dns(bool testnet) { + std::vector<std::string> records; + // All four MoneroPulse domains have DNSSEC on and valid static const std::vector<std::string> dns_urls = { "checkpoints.moneropulse.se" , "checkpoints.moneropulse.org" @@ -243,87 +221,10 @@ namespace cryptonote , "testpoints.moneropulse.co" }; - std::vector<std::vector<std::string> > records; - records.resize(dns_urls.size()); - - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_int_distribution<int> dis(0, dns_urls.size() - 1); - size_t first_index = dis(gen); - - bool avail, valid; - size_t cur_index = first_index; - do - { - std::string url; - if (testnet) - { - url = testnet_dns_urls[cur_index]; - } - else - { - url = dns_urls[cur_index]; - } - - records[cur_index] = tools::DNSResolver::instance().get_txt_record(url, avail, valid); - if (!avail) - { - records[cur_index].clear(); - LOG_PRINT_L2("DNSSEC not available for checkpoint update at URL: " << url << ", skipping."); - } - if (!valid) - { - records[cur_index].clear(); - LOG_PRINT_L2("DNSSEC validation failed for checkpoint update at URL: " << url << ", skipping."); - } - - cur_index++; - if (cur_index == dns_urls.size()) - { - cur_index = 0; - } - records[cur_index].clear(); - } while (cur_index != first_index); - - size_t num_valid_records = 0; - - for( const auto& record_set : records) - { - if (record_set.size() != 0) - { - num_valid_records++; - } - } - - if (num_valid_records < 2) - { - LOG_PRINT_L0("WARNING: no two valid MoneroPulse DNS checkpoint records were received"); - return true; - } - - int good_records_index = -1; - for (size_t i = 0; i < records.size() - 1; ++i) - { - if (records[i].size() == 0) continue; - - for (size_t j = i + 1; j < records.size(); ++j) - { - if (dns_records_match(records[i], records[j])) - { - good_records_index = i; - break; - } - } - if (good_records_index >= 0) break; - } - - if (good_records_index < 0) - { - LOG_PRINT_L0("WARNING: no two MoneroPulse DNS checkpoint records matched"); - return true; - } + if (!tools::dns_utils::load_txt_records_from_dns(records, testnet ? testnet_dns_urls : dns_urls)) + return true; // why true ? - for (auto& record : records[good_records_index]) + for (const auto& record : records) { auto pos = record.find(":"); if (pos != std::string::npos) diff --git a/src/cryptonote_core/checkpoints.h b/src/cryptonote_basic/checkpoints.h index 71727753e..3a74d8a69 100644 --- a/src/cryptonote_core/checkpoints.h +++ b/src/cryptonote_basic/checkpoints.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_core/connection_context.h b/src/cryptonote_basic/connection_context.h index 7e62e77b9..8d739900e 100644 --- a/src/cryptonote_core/connection_context.h +++ b/src/cryptonote_basic/connection_context.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_core/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index da069a21a..c4adf1fcb 100644 --- a/src/cryptonote_core/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -35,6 +35,7 @@ #include <vector> #include <cstring> // memcmp #include <sstream> +#include <atomic> #include "serialization/serialization.h" #include "serialization/variant.h" #include "serialization/vector.h" @@ -43,7 +44,6 @@ #include "serialization/debug_archive.h" #include "serialization/crypto.h" #include "serialization/keyvalue_serialization.h" // eepe named serialization -#include "string_tools.h" #include "cryptonote_config.h" #include "crypto/crypto.h" #include "crypto/hash.h" @@ -186,15 +186,37 @@ namespace cryptonote class transaction: public transaction_prefix { + private: + // hash cash + mutable std::atomic<bool> hash_valid; + mutable std::atomic<bool> blob_size_valid; + public: std::vector<std::vector<crypto::signature> > signatures; //count signatures always the same as inputs count rct::rctSig rct_signatures; + // hash cash + mutable crypto::hash hash; + mutable size_t blob_size; + transaction(); + transaction(const transaction &t): transaction_prefix(t), hash_valid(false), blob_size_valid(false), signatures(t.signatures), rct_signatures(t.rct_signatures) { if (t.is_hash_valid()) { hash = t.hash; set_hash_valid(true); } if (t.is_blob_size_valid()) { blob_size = t.blob_size; set_blob_size_valid(true); } } + transaction &operator=(const transaction &t) { transaction_prefix::operator=(t); set_hash_valid(false); set_blob_size_valid(false); signatures = t.signatures; rct_signatures = t.rct_signatures; if (t.is_hash_valid()) { hash = t.hash; set_hash_valid(true); } if (t.is_blob_size_valid()) { blob_size = t.blob_size; set_blob_size_valid(true); } return *this; } virtual ~transaction(); void set_null(); + void invalidate_hashes(); + bool is_hash_valid() const { return hash_valid.load(std::memory_order_acquire); } + void set_hash_valid(bool v) const { hash_valid.store(v,std::memory_order_release); } + bool is_blob_size_valid() const { return blob_size_valid.load(std::memory_order_acquire); } + void set_blob_size_valid(bool v) const { blob_size_valid.store(v,std::memory_order_release); } BEGIN_SERIALIZE_OBJECT() + if (!typename Archive<W>::is_saving()) + { + set_hash_valid(false); + set_blob_size_valid(false); + } + FIELDS(*static_cast<transaction_prefix *>(this)) if (version == 1) @@ -250,6 +272,28 @@ namespace cryptonote } END_SERIALIZE() + template<bool W, template <bool> class Archive> + bool serialize_base(Archive<W> &ar) + { + FIELDS(*static_cast<transaction_prefix *>(this)) + + if (version == 1) + { + } + else + { + ar.tag("rct_signatures"); + if (!vin.empty()) + { + ar.begin_object(); + bool r = rct_signatures.serialize_rctsig_base(ar, vin.size(), vout.size()); + if (!r || !ar.stream().good()) return false; + ar.end_object(); + } + } + return true; + } + private: static size_t get_signature_size(const txin_v& tx_in); }; @@ -277,6 +321,15 @@ namespace cryptonote extra.clear(); signatures.clear(); rct_signatures.type = rct::RCTTypeNull; + set_hash_valid(false); + set_blob_size_valid(false); + } + + inline + void transaction::invalidate_hashes() + { + set_hash_valid(false); + set_blob_size_valid(false); } inline @@ -317,10 +370,28 @@ namespace cryptonote struct block: public block_header { + private: + // hash cash + mutable std::atomic<bool> hash_valid; + + public: + block(): block_header(), hash_valid(false) {} + block(const block &b): block_header(b), hash_valid(false), miner_tx(b.miner_tx), tx_hashes(b.tx_hashes) { if (b.is_hash_valid()) { hash = b.hash; set_hash_valid(true); } } + block &operator=(const block &b) { block_header::operator=(b); hash_valid = false; miner_tx = b.miner_tx; tx_hashes = b.tx_hashes; if (b.is_hash_valid()) { hash = b.hash; set_hash_valid(true); } return *this; } + void invalidate_hashes() { set_hash_valid(false); } + bool is_hash_valid() const { return hash_valid.load(std::memory_order_acquire); } + void set_hash_valid(bool v) const { hash_valid.store(v,std::memory_order_release); } + transaction miner_tx; std::vector<crypto::hash> tx_hashes; + // hash cash + mutable crypto::hash hash; + BEGIN_SERIALIZE_OBJECT() + if (!typename Archive<W>::is_saving()) + set_hash_valid(false); + FIELDS(*static_cast<block_header *>(this)) FIELD(miner_tx) FIELD(tx_hashes) diff --git a/src/cryptonote_core/cryptonote_basic_impl.cpp b/src/cryptonote_basic/cryptonote_basic_impl.cpp index 338210f01..edd67793f 100644 --- a/src/cryptonote_core/cryptonote_basic_impl.cpp +++ b/src/cryptonote_basic/cryptonote_basic_impl.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -67,6 +67,15 @@ namespace cryptonote { /* Cryptonote helper functions */ /************************************************************************/ //----------------------------------------------------------------------------------------------- + size_t get_min_block_size(uint8_t version) + { + if (version < 2) + return CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1; + if (version < 5) + return CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2; + return CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5; + } + //----------------------------------------------------------------------------------------------- size_t get_max_block_size() { return CRYPTONOTE_MAX_BLOCK_SIZE; @@ -89,7 +98,7 @@ namespace cryptonote { base_reward = FINAL_SUBSIDY_PER_MINUTE*target_minutes; } - uint64_t full_reward_zone = version < 2 ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 : CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2; + uint64_t full_reward_zone = get_min_block_size(version); //make it soft if (median_size < full_reward_zone) { @@ -299,12 +308,13 @@ namespace cryptonote { , crypto::hash8& payment_id , bool testnet , const std::string& str_or_url + , bool cli_confirm ) { if (get_account_integrated_address_from_str(address, has_payment_id, payment_id, testnet, str_or_url)) return true; bool dnssec_valid; - std::string address_str = tools::dns_utils::get_account_address_as_str_from_url(str_or_url, dnssec_valid); + std::string address_str = tools::dns_utils::get_account_address_as_str_from_url(str_or_url, dnssec_valid, cli_confirm); return !address_str.empty() && get_account_integrated_address_from_str(address, has_payment_id, payment_id, testnet, address_str); } @@ -313,11 +323,12 @@ namespace cryptonote { cryptonote::account_public_address& address , bool testnet , const std::string& str_or_url + , bool cli_confirm ) { bool has_payment_id; crypto::hash8 payment_id; - return get_account_address_from_str_or_url(address, has_payment_id, payment_id, testnet, str_or_url); + return get_account_address_from_str_or_url(address, has_payment_id, payment_id, testnet, str_or_url, cli_confirm); } //-------------------------------------------------------------------------------- bool operator ==(const cryptonote::transaction& a, const cryptonote::transaction& b) { diff --git a/src/cryptonote_core/cryptonote_basic_impl.h b/src/cryptonote_basic/cryptonote_basic_impl.h index 5703a7d75..14c03ac4c 100644 --- a/src/cryptonote_core/cryptonote_basic_impl.h +++ b/src/cryptonote_basic/cryptonote_basic_impl.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -33,6 +33,8 @@ #include "cryptonote_basic.h" #include "crypto/crypto.h" #include "crypto/hash.h" +#include "hex.h" +#include "span.h" namespace cryptonote { @@ -69,6 +71,7 @@ namespace cryptonote { /************************************************************************/ /* Cryptonote helper functions */ /************************************************************************/ + size_t get_min_block_size(uint8_t version); size_t get_max_block_size(); size_t get_max_tx_size(); bool get_block_reward(size_t median_size, size_t current_block_size, uint64_t already_generated_coins, uint64_t &reward, uint8_t version); @@ -106,12 +109,14 @@ namespace cryptonote { , crypto::hash8& payment_id , bool testnet , const std::string& str_or_url + , bool cli_confirm = true ); bool get_account_address_from_str_or_url( cryptonote::account_public_address& address , bool testnet , const std::string& str_or_url + , bool cli_confirm = true ); bool is_coinbase(const transaction& tx); @@ -120,23 +125,28 @@ namespace cryptonote { bool operator ==(const cryptonote::block& a, const cryptonote::block& b); } -template <class T> -std::ostream &print256(std::ostream &o, const T &v) { - return o << "<" << epee::string_tools::pod_to_hex(v) << ">"; -} -template <class T> -std::ostream &print64(std::ostream &o, const T &v) { - return o << "<" << epee::string_tools::pod_to_hex(v) << ">"; -} - bool parse_hash256(const std::string str_hash, crypto::hash& hash); namespace crypto { - inline std::ostream &operator <<(std::ostream &o, const crypto::public_key &v) { return print256(o, v); } - inline std::ostream &operator <<(std::ostream &o, const crypto::secret_key &v) { return print256(o, v); } - inline std::ostream &operator <<(std::ostream &o, const crypto::key_derivation &v) { return print256(o, v); } - inline std::ostream &operator <<(std::ostream &o, const crypto::key_image &v) { return print256(o, v); } - inline std::ostream &operator <<(std::ostream &o, const crypto::signature &v) { return print256(o, v); } - inline std::ostream &operator <<(std::ostream &o, const crypto::hash &v) { return print256(o, v); } - inline std::ostream &operator <<(std::ostream &o, const crypto::hash8 &v) { return print64(o, v); } + inline std::ostream &operator <<(std::ostream &o, const crypto::public_key &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + inline std::ostream &operator <<(std::ostream &o, const crypto::secret_key &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + inline std::ostream &operator <<(std::ostream &o, const crypto::key_derivation &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + inline std::ostream &operator <<(std::ostream &o, const crypto::key_image &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + inline std::ostream &operator <<(std::ostream &o, const crypto::signature &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + inline std::ostream &operator <<(std::ostream &o, const crypto::hash &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + inline std::ostream &operator <<(std::ostream &o, const crypto::hash8 &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } } diff --git a/src/cryptonote_core/cryptonote_boost_serialization.h b/src/cryptonote_basic/cryptonote_boost_serialization.h index 409b9798c..6e4ac9b72 100644 --- a/src/cryptonote_core/cryptonote_boost_serialization.h +++ b/src/cryptonote_basic/cryptonote_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 2e6917878..745dfb72e 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -31,9 +31,9 @@ #include "include_base_utils.h" using namespace epee; +#include <atomic> #include "cryptonote_format_utils.h" #include "cryptonote_config.h" -#include "miner.h" #include "crypto/crypto.h" #include "crypto/hash.h" #include "ringct/rctSigs.h" @@ -43,6 +43,8 @@ using namespace epee; #define ENCRYPTED_PAYMENT_ID_TAIL 0x8d +// #define ENABLE_HASH_CASH_INTEGRITY_CHECK + static const uint64_t valid_decomposed_outputs[] = { (uint64_t)1, (uint64_t)2, (uint64_t)3, (uint64_t)4, (uint64_t)5, (uint64_t)6, (uint64_t)7, (uint64_t)8, (uint64_t)9, // 1 piconero (uint64_t)10, (uint64_t)20, (uint64_t)30, (uint64_t)40, (uint64_t)50, (uint64_t)60, (uint64_t)70, (uint64_t)80, (uint64_t)90, @@ -66,6 +68,13 @@ static const uint64_t valid_decomposed_outputs[] = { (uint64_t)10000000000000000000ull }; +static std::atomic<unsigned int> default_decimal_point(CRYPTONOTE_DISPLAY_DECIMAL_POINT); + +static std::atomic<uint64_t> tx_hashes_calculated_count(0); +static std::atomic<uint64_t> tx_hashes_cached_count(0); +static std::atomic<uint64_t> block_hashes_calculated_count(0); +static std::atomic<uint64_t> block_hashes_cached_count(0); + namespace cryptonote { //--------------------------------------------------------------- @@ -91,6 +100,17 @@ namespace cryptonote binary_archive<false> ba(ss); bool r = ::serialization::serialize(ba, tx); CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob"); + tx.invalidate_hashes(); + return true; + } + //--------------------------------------------------------------- + bool parse_and_validate_tx_base_from_blob(const blobdata& tx_blob, transaction& tx) + { + std::stringstream ss; + ss << tx_blob; + binary_archive<false> ba(ss); + bool r = tx.serialize_base(ba); + CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob"); return true; } //--------------------------------------------------------------- @@ -101,6 +121,7 @@ namespace cryptonote binary_archive<false> ba(ss); bool r = ::serialization::serialize(ba, tx); CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob"); + tx.invalidate_hashes(); //TODO: validate tx get_transaction_hash(tx, tx_hash); @@ -108,102 +129,6 @@ namespace cryptonote return true; } //--------------------------------------------------------------- - bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce, size_t max_outs, uint8_t hard_fork_version) { - tx.vin.clear(); - tx.vout.clear(); - tx.extra.clear(); - - keypair txkey = keypair::generate(); - add_tx_pub_key_to_extra(tx, txkey.pub); - if(!extra_nonce.empty()) - if(!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce)) - return false; - - txin_gen in; - in.height = height; - - uint64_t block_reward; - if(!get_block_reward(median_size, current_block_size, already_generated_coins, block_reward, hard_fork_version)) - { - LOG_PRINT_L0("Block is too big"); - return false; - } - -#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - LOG_PRINT_L1("Creating block template: reward " << block_reward << - ", fee " << fee); -#endif - block_reward += fee; - - // 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 - // 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; - } - - std::vector<uint64_t> out_amounts; - decompose_amount_into_digits(block_reward, hard_fork_version >= 2 ? 0 : ::config::DEFAULT_DUST_THRESHOLD, - [&out_amounts](uint64_t a_chunk) { out_amounts.push_back(a_chunk); }, - [&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 || 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.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); - } - } - else - { - CHECK_AND_ASSERT_MES(max_outs >= out_amounts.size(), false, "max_out exceeded"); - } - - uint64_t summary_amounts = 0; - for (size_t no = 0; no < out_amounts.size(); no++) - { - crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);; - crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key); - bool r = crypto::generate_key_derivation(miner_address.m_view_public_key, txkey.sec, derivation); - CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to generate_key_derivation(" << miner_address.m_view_public_key << ", " << txkey.sec << ")"); - - r = crypto::derive_public_key(derivation, no, miner_address.m_spend_public_key, out_eph_public_key); - CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to derive_public_key(" << derivation << ", " << no << ", "<< miner_address.m_spend_public_key << ")"); - - txout_to_key tk; - tk.key = out_eph_public_key; - - tx_out out; - summary_amounts += out.amount = out_amounts[no]; - out.target = tk; - tx.vout.push_back(out); - } - - CHECK_AND_ASSERT_MES(summary_amounts == block_reward, false, "Failed to construct miner tx, summary_amounts = " << summary_amounts << " not equal block_reward = " << block_reward); - - 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); - //LOG_PRINT("MINER_TX generated ok, block_reward=" << print_money(block_reward) << "(" << print_money(block_reward - fee) << "+" << print_money(fee) - // << "), current_block_size=" << current_block_size << ", already_generated_coins=" << already_generated_coins << ", tx_id=" << get_transaction_hash(tx), LOG_LEVEL_2); - return true; - } - //--------------------------------------------------------------- bool generate_key_image_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki) { crypto::key_derivation recv_derivation = AUTO_VAL_INIT(recv_derivation); @@ -239,12 +164,12 @@ namespace cryptonote if (std::string::npos != point_index) { fraction_size = str_amount.size() - point_index - 1; - while (CRYPTONOTE_DISPLAY_DECIMAL_POINT < fraction_size && '0' == str_amount.back()) + while (default_decimal_point < fraction_size && '0' == str_amount.back()) { str_amount.erase(str_amount.size() - 1, 1); --fraction_size; } - if (CRYPTONOTE_DISPLAY_DECIMAL_POINT < fraction_size) + if (default_decimal_point < fraction_size) return false; str_amount.erase(point_index, 1); } @@ -256,9 +181,9 @@ namespace cryptonote if (str_amount.empty()) return false; - if (fraction_size < CRYPTONOTE_DISPLAY_DECIMAL_POINT) + if (fraction_size < default_decimal_point) { - str_amount.append(CRYPTONOTE_DISPLAY_DECIMAL_POINT - fraction_size, '0'); + str_amount.append(default_decimal_point - fraction_size, '0'); } return string_tools::get_xtype_from_string(amount, str_amount); @@ -435,22 +360,6 @@ namespace cryptonote return true; } //--------------------------------------------------------------- - crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const account_keys &sender_keys) - { - if (destinations.empty()) - return null_pkey; - for (size_t n = 1; n < destinations.size(); ++n) - { - if (!memcmp(&destinations[n].addr, &sender_keys.m_account_address, sizeof(destinations[0].addr))) - continue; - if (destinations[n].amount == 0) - continue; - if (memcmp(&destinations[n].addr, &destinations[0].addr, sizeof(destinations[0].addr))) - return null_pkey; - } - return destinations[0].addr.m_view_public_key; - } - //--------------------------------------------------------------- bool encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key) { crypto::key_derivation derivation; @@ -475,313 +384,6 @@ 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<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct) - { - std::vector<rct::key> amount_keys; - tx.set_null(); - amount_keys.clear(); - - tx.version = rct ? 2 : 1; - tx.unlock_time = unlock_time; - - tx.extra = extra; - keypair txkey = keypair::generate(); - remove_field_from_tx_extra(tx.extra, typeid(tx_extra_pub_key)); - add_tx_pub_key_to_extra(tx, txkey.pub); - tx_key = txkey.sec; - - // if we have a stealth payment id, find it and encrypt it with the tx key now - std::vector<tx_extra_field> tx_extra_fields; - if (parse_tx_extra(tx.extra, tx_extra_fields)) - { - tx_extra_nonce extra_nonce; - if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) - { - crypto::hash8 payment_id = null_hash8; - if (get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) - { - LOG_PRINT_L2("Encrypting payment id " << payment_id); - crypto::public_key view_key_pub = get_destination_view_key_pub(destinations, sender_account_keys); - if (view_key_pub == null_pkey) - { - LOG_ERROR("Destinations have to have exactly one output to support encrypted payment ids"); - return false; - } - - if (!encrypt_payment_id(payment_id, view_key_pub, txkey.sec)) - { - LOG_ERROR("Failed to encrypt payment id"); - return false; - } - - std::string extra_nonce; - set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id); - remove_field_from_tx_extra(tx.extra, typeid(tx_extra_nonce)); - if (!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce)) - { - LOG_ERROR("Failed to add encrypted payment id to tx extra"); - return false; - } - LOG_PRINT_L1("Encrypted payment ID: " << payment_id); - } - } - } - else - { - LOG_ERROR("Failed to parse tx extra"); - return false; - } - - struct input_generation_context_data - { - keypair in_ephemeral; - }; - std::vector<input_generation_context_data> in_contexts; - - uint64_t summary_inputs_money = 0; - //fill inputs - int idx = -1; - for(const tx_source_entry& src_entr: sources) - { - ++idx; - if(src_entr.real_output >= src_entr.outputs.size()) - { - LOG_ERROR("real_output index (" << src_entr.real_output << ")bigger than output_keys.size()=" << src_entr.outputs.size()); - return false; - } - summary_inputs_money += src_entr.amount; - - //key_derivation recv_derivation; - in_contexts.push_back(input_generation_context_data()); - keypair& in_ephemeral = in_contexts.back().in_ephemeral; - crypto::key_image img; - if(!generate_key_image_helper(sender_account_keys, src_entr.real_out_tx_key, src_entr.real_output_in_tx_index, in_ephemeral, img)) - return false; - - //check that derivated key is equal with real output key - if( !(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].second.dest) ) - { - LOG_ERROR("derived public key mismatch with output public key at index " << idx << ", real out " << src_entr.real_output << "! "<< ENDL << "derived_key:" - << string_tools::pod_to_hex(in_ephemeral.pub) << ENDL << "real output_public_key:" - << string_tools::pod_to_hex(src_entr.outputs[src_entr.real_output].second) ); - LOG_ERROR("amount " << src_entr.amount << ", rct " << src_entr.rct); - LOG_ERROR("tx pubkey " << src_entr.real_out_tx_key << ", real_output_in_tx_index " << src_entr.real_output_in_tx_index); - return false; - } - - //put key image into tx input - txin_to_key input_to_key; - input_to_key.amount = src_entr.amount; - input_to_key.k_image = img; - - //fill outputs array and use relative offsets - for(const tx_source_entry::output_entry& out_entry: src_entr.outputs) - input_to_key.key_offsets.push_back(out_entry.first); - - input_to_key.key_offsets = absolute_output_offsets_to_relative(input_to_key.key_offsets); - tx.vin.push_back(input_to_key); - } - - // "Shuffle" outs - std::vector<tx_destination_entry> shuffled_dsts(destinations); - std::sort(shuffled_dsts.begin(), shuffled_dsts.end(), [](const tx_destination_entry& de1, const tx_destination_entry& de2) { return de1.amount < de2.amount; } ); - - uint64_t summary_outs_money = 0; - //fill outputs - size_t output_index = 0; - for(const tx_destination_entry& dst_entr: shuffled_dsts) - { - 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(rct::sk2rct(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 << ")"); - - tx_out out; - out.amount = dst_entr.amount; - txout_to_key tk; - tk.key = out_eph_public_key; - out.target = tk; - tx.vout.push_back(out); - output_index++; - summary_outs_money += dst_entr.amount; - } - - //check money - if(summary_outs_money > summary_inputs_money ) - { - LOG_ERROR("Transaction inputs money ("<< summary_inputs_money << ") less than outputs money (" << summary_outs_money << ")"); - return false; - } - - // check for watch only wallet - bool zero_secret_key = true; - for (size_t i = 0; i < sizeof(sender_account_keys.m_spend_secret_key); ++i) - zero_secret_key &= (sender_account_keys.m_spend_secret_key.data[i] == 0); - if (zero_secret_key) - { - MDEBUG("Null secret key, skipping signatures"); - } - - if (tx.version == 1) - { - //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; - for(const tx_source_entry& src_entr: sources) - { - ss_ring_s << "pub_keys:" << ENDL; - std::vector<const crypto::public_key*> keys_ptrs; - std::vector<crypto::public_key> keys(src_entr.outputs.size()); - size_t ii = 0; - for(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<crypto::signature>()); - std::vector<crypto::signature>& sigs = tx.signatures.back(); - sigs.resize(src_entr.outputs.size()); - if (!zero_secret_key) - crypto::generate_ring_signature(tx_prefix_hash, boost::get<txin_to_key>(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++; - } - - MCINFO("construct_tx", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL << ss_ring_s.str()); - } - else - { - size_t n_total_outs = sources[0].outputs.size(); // only for non-simple rct - - // 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 - for(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<uint64_t> inamounts, outamounts; - std::vector<unsigned int> 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<txout_to_key>(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<txin_to_key>(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, amount_keys, index, outSk); - else - tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, 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"); - - MCINFO("construct_tx", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL); - } - - return true; - } - //--------------------------------------------------------------- - bool construct_tx(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time) - { - crypto::secret_key tx_key; - return construct_tx_and_get_tx_key(sender_account_keys, sources, destinations, extra, tx, unlock_time, tx_key); - } - //--------------------------------------------------------------- bool get_inputs_money_amount(const transaction& tx, uint64_t& money) { money = 0; @@ -924,14 +526,59 @@ namespace cryptonote cn_fast_hash(blob.data(), blob.size(), res); } //--------------------------------------------------------------- - std::string print_money(uint64_t amount) + void set_default_decimal_point(unsigned int decimal_point) { + switch (decimal_point) + { + case 12: + case 9: + case 6: + case 3: + case 0: + default_decimal_point = decimal_point; + break; + default: + ASSERT_MES_AND_THROW("Invalid decimal point specification: " << decimal_point); + } + } + //--------------------------------------------------------------- + unsigned int get_default_decimal_point() + { + return default_decimal_point; + } + //--------------------------------------------------------------- + std::string get_unit(unsigned int decimal_point) + { + if (decimal_point == (unsigned int)-1) + decimal_point = default_decimal_point; + switch (std::atomic_load(&default_decimal_point)) + { + case 12: + return "monero"; + case 9: + return "millinero"; + case 6: + return "micronero"; + case 3: + return "nanonero"; + case 0: + return "piconero"; + default: + ASSERT_MES_AND_THROW("Invalid decimal point specification: " << default_decimal_point); + } + } + //--------------------------------------------------------------- + std::string print_money(uint64_t amount, unsigned int decimal_point) + { + if (decimal_point == (unsigned int)-1) + decimal_point = default_decimal_point; std::string s = std::to_string(amount); - if(s.size() < CRYPTONOTE_DISPLAY_DECIMAL_POINT+1) + if(s.size() < decimal_point+1) { - s.insert(0, CRYPTONOTE_DISPLAY_DECIMAL_POINT+1 - s.size(), '0'); + s.insert(0, decimal_point+1 - s.size(), '0'); } - s.insert(s.size() - CRYPTONOTE_DISPLAY_DECIMAL_POINT, "."); + if (decimal_point > 0) + s.insert(s.size() - decimal_point, "."); return s; } //--------------------------------------------------------------- @@ -954,7 +601,7 @@ namespace cryptonote return get_transaction_hash(t, res, NULL); } //--------------------------------------------------------------- - bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t* blob_size) + bool calculate_transaction_hash(const transaction& t, crypto::hash& res, size_t* blob_size) { // v1 transactions hash the entire blob if (t.version == 1) @@ -1009,6 +656,40 @@ namespace cryptonote return true; } //--------------------------------------------------------------- + bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t* blob_size) + { + if (t.is_hash_valid()) + { +#ifdef ENABLE_HASH_CASH_INTEGRITY_CHECK + CHECK_AND_ASSERT_THROW_MES(!calculate_transaction_hash(t, res, blob_size) || t.hash == res, "tx hash cash integrity failure"); +#endif + res = t.hash; + if (blob_size) + { + if (!t.is_blob_size_valid()) + { + t.blob_size = get_object_blobsize(t); + t.set_blob_size_valid(true); + } + *blob_size = t.blob_size; + } + ++tx_hashes_cached_count; + return true; + } + ++tx_hashes_calculated_count; + bool ret = calculate_transaction_hash(t, res, blob_size); + if (!ret) + return false; + t.hash = res; + t.set_hash_valid(true); + if (blob_size) + { + t.blob_size = *blob_size; + t.set_blob_size_valid(true); + } + return true; + } + //--------------------------------------------------------------- bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t& blob_size) { return get_transaction_hash(t, res, &blob_size); @@ -1023,7 +704,7 @@ namespace cryptonote return blob; } //--------------------------------------------------------------- - bool get_block_hash(const block& b, crypto::hash& res) + bool calculate_block_hash(const block& b, crypto::hash& res) { // EXCEPTION FOR BLOCK 202612 const std::string correct_blob_hash_202612 = "3a8a2b3a29b50fc86ff73dd087ea43c6f0d6b8f936c849194d5c84c737903966"; @@ -1050,6 +731,26 @@ namespace cryptonote return hash_result; } //--------------------------------------------------------------- + bool get_block_hash(const block& b, crypto::hash& res) + { + if (b.is_hash_valid()) + { +#ifdef ENABLE_HASH_CASH_INTEGRITY_CHECK + CHECK_AND_ASSERT_THROW_MES(!calculate_block_hash(b, res) || b.hash == res, "block hash cash integrity failure"); +#endif + res = b.hash; + ++block_hashes_cached_count; + return true; + } + ++block_hashes_calculated_count; + bool ret = calculate_block_hash(b, res); + if (!ret) + return false; + b.hash = res; + b.set_hash_valid(true); + return true; + } + //--------------------------------------------------------------- crypto::hash get_block_hash(const block& b) { crypto::hash p = null_hash; @@ -1057,36 +758,6 @@ namespace cryptonote return p; } //--------------------------------------------------------------- - bool generate_genesis_block( - block& bl - , std::string const & genesis_tx - , uint32_t nonce - ) - { - //genesis block - bl = boost::value_initialized<block>(); - - - account_public_address ac = boost::value_initialized<account_public_address>(); - std::vector<size_t> sz; - construct_miner_tx(0, 0, 0, 0, 0, ac, bl.miner_tx); // zero fee in genesis - blobdata txb = tx_to_blob(bl.miner_tx); - std::string hex_tx_represent = string_tools::buff_to_hex_nodelimer(txb); - - std::string genesis_coinbase_tx_hex = config::GENESIS_TX; - - blobdata tx_bl; - string_tools::parse_hexstr_to_binbuff(genesis_coinbase_tx_hex, tx_bl); - bool r = parse_and_validate_tx_from_blob(tx_bl, bl.miner_tx); - CHECK_AND_ASSERT_MES(r, false, "failed to parse coinbase tx from hard coded blob"); - bl.major_version = CURRENT_BLOCK_MAJOR_VERSION; - bl.minor_version = CURRENT_BLOCK_MINOR_VERSION; - bl.timestamp = 0; - bl.nonce = nonce; - miner::find_nonce_for_given_block(bl, 1, 0); - return true; - } - //--------------------------------------------------------------- bool get_block_longhash(const block& b, crypto::hash& res, uint64_t height) { // block 202612 bug workaround @@ -1096,7 +767,6 @@ namespace cryptonote string_tools::hex_to_pod(longhash_202612, res); return true; } - block b_local = b; //workaround to avoid const errors with do_serialize blobdata bd = get_block_hashing_blob(b); crypto::cn_slow_hash(bd.data(), bd.size(), res); return true; @@ -1136,6 +806,8 @@ namespace cryptonote binary_archive<false> ba(ss); bool r = ::serialization::serialize(ba, b); CHECK_AND_ASSERT_MES(r, false, "Failed to parse block from blob"); + b.invalidate_hashes(); + b.miner_tx.invalidate_hashes(); return true; } //--------------------------------------------------------------- @@ -1190,4 +862,11 @@ namespace cryptonote return std::binary_search(begin, end, amount); } //--------------------------------------------------------------- + void get_hash_stats(uint64_t &tx_hashes_calculated, uint64_t &tx_hashes_cached, uint64_t &block_hashes_calculated, uint64_t & block_hashes_cached) + { + tx_hashes_calculated = tx_hashes_calculated_count; + tx_hashes_cached = tx_hashes_cached_count; + block_hashes_calculated = block_hashes_calculated_count; + block_hashes_cached = block_hashes_cached_count; + } } diff --git a/src/cryptonote_core/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h index a6610e60d..5c10907fd 100644 --- a/src/cryptonote_core/cryptonote_format_utils.h +++ b/src/cryptonote_basic/cryptonote_format_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -30,14 +30,12 @@ #pragma once #include "cryptonote_protocol/cryptonote_protocol_defs.h" -#include "cryptonote_core/cryptonote_basic_impl.h" +#include "cryptonote_basic_impl.h" #include "account.h" #include "include_base_utils.h" #include "crypto/crypto.h" #include "crypto/hash.h" #include "ringct/rctOps.h" -#include <boost/serialization/vector.hpp> -#include <boost/serialization/utility.hpp> namespace cryptonote { @@ -46,43 +44,10 @@ namespace cryptonote crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx); bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash); bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx); - bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce = blobdata(), size_t max_outs = 999, uint8_t hard_fork_version = 1); + bool parse_and_validate_tx_base_from_blob(const blobdata& tx_blob, transaction& tx); bool encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key); bool decrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key); - struct tx_source_entry - { - typedef std::pair<uint64_t, rct::ctkey> output_entry; - - std::vector<output_entry> 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 - { - uint64_t amount; //money - account_public_address addr; //destination address - - tx_destination_entry() : amount(0), addr(AUTO_VAL_INIT(addr)) { } - tx_destination_entry(uint64_t a, const account_public_address &ad) : amount(a), addr(ad) { } - - BEGIN_SERIALIZE_OBJECT() - VARINT_FIELD(amount) - FIELD(addr) - END_SERIALIZE() - }; - - //--------------------------------------------------------------- - bool construct_tx(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time); - bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct = false); - template<typename T> bool find_tx_extra_field_by_type(const std::vector<tx_extra_field>& tx_extra_fields, T& field, size_t index = 0) { @@ -120,16 +85,13 @@ namespace cryptonote 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); + bool calculate_transaction_hash(const transaction& t, crypto::hash& res, size_t* blob_size); blobdata get_block_hashing_blob(const block& b); + bool calculate_block_hash(const block& b, crypto::hash& res); bool get_block_hash(const block& b, crypto::hash& res); crypto::hash get_block_hash(const block& b); bool get_block_longhash(const block& b, crypto::hash& res, uint64_t height); crypto::hash get_block_longhash(const block& b, uint64_t height); - bool generate_genesis_block( - block& bl - , std::string const & genesis_tx - , uint32_t nonce - ); bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b); bool get_inputs_money_amount(const transaction& tx, uint64_t& money); uint64_t get_outs_money_amount(const transaction& tx); @@ -143,7 +105,10 @@ namespace cryptonote uint64_t get_block_height(const block& b); std::vector<uint64_t> relative_output_offsets_to_absolute(const std::vector<uint64_t>& off); std::vector<uint64_t> absolute_output_offsets_to_relative(const std::vector<uint64_t>& off); - std::string print_money(uint64_t amount); + void set_default_decimal_point(unsigned int decimal_point = CRYPTONOTE_DISPLAY_DECIMAL_POINT); + unsigned int get_default_decimal_point(); + std::string get_unit(unsigned int decimal_point = -1); + std::string print_money(uint64_t amount, unsigned int decimal_point = -1); //--------------------------------------------------------------- template<class t_object> bool t_serializable_object_to_blob(const t_object& to, blobdata& b_blob) @@ -246,29 +211,10 @@ namespace cryptonote crypto::hash get_tx_tree_hash(const std::vector<crypto::hash>& tx_hashes); crypto::hash get_tx_tree_hash(const block& b); bool is_valid_decomposed_amount(uint64_t amount); + void get_hash_stats(uint64_t &tx_hashes_calculated, uint64_t &tx_hashes_cached, uint64_t &block_hashes_calculated, uint64_t & block_hashes_cached); #define CHECKED_GET_SPECIFIC_VARIANT(variant_var, specific_type, variable_name, fail_return_val) \ CHECK_AND_ASSERT_MES(variant_var.type() == typeid(specific_type), fail_return_val, "wrong variant type: " << variant_var.type().name() << ", expected " << typeid(specific_type).name()); \ specific_type& variable_name = boost::get<specific_type>(variant_var); } - -BOOST_CLASS_VERSION(cryptonote::tx_source_entry, 0) - -namespace boost -{ - namespace serialization - { - template <class Archive> - inline void serialize(Archive &a, cryptonote::tx_source_entry &x, const boost::serialization::version_type ver) - { - a & x.outputs; - a & x.real_output; - a & x.real_out_tx_key; - a & x.real_output_in_tx_index; - a & x.amount; - a & x.rct; - a & x.mask; - } - } -} diff --git a/src/cryptonote_core/cryptonote_stat_info.h b/src/cryptonote_basic/cryptonote_stat_info.h index d44904b6d..7ebf86878 100644 --- a/src/cryptonote_core/cryptonote_stat_info.h +++ b/src/cryptonote_basic/cryptonote_stat_info.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_core/difficulty.cpp b/src/cryptonote_basic/difficulty.cpp index 1c5b9cfbd..863aa4359 100644 --- a/src/cryptonote_core/difficulty.cpp +++ b/src/cryptonote_basic/difficulty.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_core/difficulty.h b/src/cryptonote_basic/difficulty.h index 910f97035..aed6cb289 100644 --- a/src/cryptonote_core/difficulty.h +++ b/src/cryptonote_basic/difficulty.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_core/hardfork.cpp b/src/cryptonote_basic/hardfork.cpp index 13d7d717d..546af2076 100644 --- a/src/cryptonote_core/hardfork.cpp +++ b/src/cryptonote_basic/hardfork.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -29,7 +29,7 @@ #include <algorithm> #include <cstdio> -#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_basic.h" #include "blockchain_db/blockchain_db.h" #include "hardfork.h" @@ -191,10 +191,10 @@ void HardFork::init() } catch (...) { populate = true; } if (populate) { - LOG_PRINT_L0("The DB has no hard fork info, reparsing from start"); + MINFO("The DB has no hard fork info, reparsing from start"); height = 1; } - LOG_PRINT_L1("reorganizing from " << height); + MDEBUG("reorganizing from " << height); if (populate) { reorganize_from_chain_height(height); // reorg will not touch the genesis block, use this as a flag for populating done @@ -203,7 +203,7 @@ void HardFork::init() else { rescan_from_chain_height(height); } - LOG_PRINT_L1("reorganization done"); + MDEBUG("reorganization done"); } uint8_t HardFork::get_block_version(uint64_t height) const @@ -222,7 +222,7 @@ bool HardFork::reorganize_from_block_height(uint64_t height) return false; db.set_batch_transactions(true); - db.batch_start(); + bool stop_batch = db.batch_start(); versions.clear(); @@ -250,7 +250,8 @@ bool HardFork::reorganize_from_block_height(uint64_t height) add(db.get_block_from_height(h), h); } - db.batch_stop(); + if (stop_batch) + db.batch_stop(); return true; } diff --git a/src/cryptonote_core/hardfork.h b/src/cryptonote_basic/hardfork.h index 8d2edfa2a..6c6fbcb84 100644 --- a/src/cryptonote_core/hardfork.h +++ b/src/cryptonote_basic/hardfork.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -29,7 +29,7 @@ #pragma once #include "syncobj.h" -#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_basic.h" namespace cryptonote { diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp new file mode 100644 index 000000000..eeb7b6094 --- /dev/null +++ b/src/cryptonote_basic/miner.cpp @@ -0,0 +1,853 @@ +// Copyright (c) 2014-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#include <sstream> +#include <numeric> +#include <boost/utility/value_init.hpp> +#include <boost/interprocess/detail/atomic.hpp> +#include <boost/limits.hpp> +#include "misc_language.h" +#include "include_base_utils.h" +#include "cryptonote_basic_impl.h" +#include "cryptonote_format_utils.h" +#include "file_io_utils.h" +#include "common/command_line.h" +#include "string_coding.h" +#include "storages/portable_storage_template_helper.h" +#include "boost/logic/tribool.hpp" + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "miner" + +using namespace epee; + +#include "miner.h" + + +extern "C" void slow_hash_allocate_state(); +extern "C" void slow_hash_free_state(); +namespace cryptonote +{ + + namespace + { + const command_line::arg_descriptor<std::string> arg_extra_messages = {"extra-messages-file", "Specify file for extra messages to include into coinbase transactions", "", true}; + const command_line::arg_descriptor<std::string> arg_start_mining = {"start-mining", "Specify wallet address to mining for", "", true}; + const command_line::arg_descriptor<uint32_t> arg_mining_threads = {"mining-threads", "Specify mining threads count", 0, true}; + const command_line::arg_descriptor<bool> arg_bg_mining_enable = {"bg-mining-enable", "enable/disable background mining", true, true}; + const command_line::arg_descriptor<bool> arg_bg_mining_ignore_battery = {"bg-mining-ignore-battery", "if true, assumes plugged in when unable to query system power status", false, true}; + const command_line::arg_descriptor<uint64_t> arg_bg_mining_min_idle_interval_seconds = {"bg-mining-min-idle-interval", "Specify min lookback interval in seconds for determining idle state", miner::BACKGROUND_MINING_DEFAULT_MIN_IDLE_INTERVAL_IN_SECONDS, true}; + const command_line::arg_descriptor<uint8_t> arg_bg_mining_idle_threshold_percentage = {"bg-mining-idle-threshold", "Specify minimum avg idle percentage over lookback interval", miner::BACKGROUND_MINING_DEFAULT_IDLE_THRESHOLD_PERCENTAGE, true}; + const command_line::arg_descriptor<uint8_t> arg_bg_mining_miner_target_percentage = {"bg-mining-miner-target", "Specificy maximum percentage cpu use by miner(s)", miner::BACKGROUND_MINING_DEFAULT_MINING_TARGET_PERCENTAGE, true}; + } + + + miner::miner(i_miner_handler* phandler):m_stop(1), + m_template(boost::value_initialized<block>()), + m_template_no(0), + m_diffic(0), + m_thread_index(0), + m_phandler(phandler), + m_height(0), + m_pausers_count(0), + m_threads_total(0), + m_starter_nonce(0), + m_last_hr_merge_time(0), + m_hashes(0), + m_do_print_hashrate(false), + m_do_mining(false), + m_current_hash_rate(0), + m_is_background_mining_enabled(false), + m_min_idle_seconds(BACKGROUND_MINING_DEFAULT_MIN_IDLE_INTERVAL_IN_SECONDS), + m_idle_threshold(BACKGROUND_MINING_DEFAULT_IDLE_THRESHOLD_PERCENTAGE), + m_mining_target(BACKGROUND_MINING_DEFAULT_MINING_TARGET_PERCENTAGE), + m_miner_extra_sleep(BACKGROUND_MINING_DEFAULT_MINER_EXTRA_SLEEP_MILLIS) + { + + } + //----------------------------------------------------------------------------------------------------- + miner::~miner() + { + stop(); + } + //----------------------------------------------------------------------------------------------------- + bool miner::set_block_template(const block& bl, const difficulty_type& di, uint64_t height) + { + CRITICAL_REGION_LOCAL(m_template_lock); + m_template = bl; + m_diffic = di; + m_height = height; + ++m_template_no; + m_starter_nonce = crypto::rand<uint32_t>(); + return true; + } + //----------------------------------------------------------------------------------------------------- + bool miner::on_block_chain_update() + { + if(!is_mining()) + return true; + + return request_block_template(); + } + //----------------------------------------------------------------------------------------------------- + bool miner::request_block_template() + { + block bl = AUTO_VAL_INIT(bl); + difficulty_type di = AUTO_VAL_INIT(di); + uint64_t height = AUTO_VAL_INIT(height); + uint64_t expected_reward; //only used for RPC calls - could possibly be useful here too? + + cryptonote::blobdata extra_nonce; + if(m_extra_messages.size() && m_config.current_extra_message_index < m_extra_messages.size()) + { + extra_nonce = m_extra_messages[m_config.current_extra_message_index]; + } + + if(!m_phandler->get_block_template(bl, m_mine_address, di, height, expected_reward, extra_nonce)) + { + LOG_ERROR("Failed to get_block_template(), stopping mining"); + return false; + } + set_block_template(bl, di, height); + return true; + } + //----------------------------------------------------------------------------------------------------- + bool miner::on_idle() + { + m_update_block_template_interval.do_call([&](){ + if(is_mining())request_block_template(); + return true; + }); + + m_update_merge_hr_interval.do_call([&](){ + merge_hr(); + return true; + }); + + return true; + } + //----------------------------------------------------------------------------------------------------- + void miner::do_print_hashrate(bool do_hr) + { + m_do_print_hashrate = do_hr; + } + //----------------------------------------------------------------------------------------------------- + void miner::merge_hr() + { + if(m_last_hr_merge_time && is_mining()) + { + m_current_hash_rate = m_hashes * 1000 / ((misc_utils::get_tick_count() - m_last_hr_merge_time + 1)); + CRITICAL_REGION_LOCAL(m_last_hash_rates_lock); + m_last_hash_rates.push_back(m_current_hash_rate); + if(m_last_hash_rates.size() > 19) + m_last_hash_rates.pop_front(); + if(m_do_print_hashrate) + { + uint64_t total_hr = std::accumulate(m_last_hash_rates.begin(), m_last_hash_rates.end(), 0); + float hr = static_cast<float>(total_hr)/static_cast<float>(m_last_hash_rates.size()); + std::cout << "hashrate: " << std::setprecision(4) << std::fixed << hr << ENDL; + } + } + m_last_hr_merge_time = misc_utils::get_tick_count(); + m_hashes = 0; + } + //----------------------------------------------------------------------------------------------------- + void miner::init_options(boost::program_options::options_description& desc) + { + command_line::add_arg(desc, arg_extra_messages); + command_line::add_arg(desc, arg_start_mining); + command_line::add_arg(desc, arg_mining_threads); + command_line::add_arg(desc, arg_bg_mining_enable); + command_line::add_arg(desc, arg_bg_mining_ignore_battery); + command_line::add_arg(desc, arg_bg_mining_min_idle_interval_seconds); + command_line::add_arg(desc, arg_bg_mining_idle_threshold_percentage); + command_line::add_arg(desc, arg_bg_mining_miner_target_percentage); + } + //----------------------------------------------------------------------------------------------------- + bool miner::init(const boost::program_options::variables_map& vm, bool testnet) + { + if(command_line::has_arg(vm, arg_extra_messages)) + { + std::string buff; + bool r = file_io_utils::load_file_to_string(command_line::get_arg(vm, arg_extra_messages), buff); + CHECK_AND_ASSERT_MES(r, false, "Failed to load file with extra messages: " << command_line::get_arg(vm, arg_extra_messages)); + std::vector<std::string> extra_vec; + boost::split(extra_vec, buff, boost::is_any_of("\n"), boost::token_compress_on ); + m_extra_messages.resize(extra_vec.size()); + for(size_t i = 0; i != extra_vec.size(); i++) + { + string_tools::trim(extra_vec[i]); + if(!extra_vec[i].size()) + continue; + std::string buff = string_encoding::base64_decode(extra_vec[i]); + if(buff != "0") + m_extra_messages[i] = buff; + } + m_config_folder_path = boost::filesystem::path(command_line::get_arg(vm, arg_extra_messages)).parent_path().string(); + m_config = AUTO_VAL_INIT(m_config); + epee::serialization::load_t_from_json_file(m_config, m_config_folder_path + "/" + MINER_CONFIG_FILE_NAME); + MINFO("Loaded " << m_extra_messages.size() << " extra messages, current index " << m_config.current_extra_message_index); + } + + if(command_line::has_arg(vm, arg_start_mining)) + { + if(!cryptonote::get_account_address_from_str(m_mine_address, testnet, command_line::get_arg(vm, arg_start_mining))) + { + LOG_ERROR("Target account address " << command_line::get_arg(vm, arg_start_mining) << " has wrong format, starting daemon canceled"); + return false; + } + m_threads_total = 1; + m_do_mining = true; + if(command_line::has_arg(vm, arg_mining_threads)) + { + m_threads_total = command_line::get_arg(vm, arg_mining_threads); + } + } + + // Background mining parameters + // Let init set all parameters even if background mining is not enabled, they can start later with params set + if(command_line::has_arg(vm, arg_bg_mining_enable)) + set_is_background_mining_enabled( command_line::get_arg(vm, arg_bg_mining_enable) ); + if(command_line::has_arg(vm, arg_bg_mining_ignore_battery)) + set_ignore_battery( command_line::get_arg(vm, arg_bg_mining_ignore_battery) ); + if(command_line::has_arg(vm, arg_bg_mining_min_idle_interval_seconds)) + set_min_idle_seconds( command_line::get_arg(vm, arg_bg_mining_min_idle_interval_seconds) ); + if(command_line::has_arg(vm, arg_bg_mining_idle_threshold_percentage)) + set_idle_threshold( command_line::get_arg(vm, arg_bg_mining_idle_threshold_percentage) ); + if(command_line::has_arg(vm, arg_bg_mining_miner_target_percentage)) + set_mining_target( command_line::get_arg(vm, arg_bg_mining_miner_target_percentage) ); + + return true; + } + //----------------------------------------------------------------------------------------------------- + bool miner::is_mining() const + { + return !m_stop; + } + //----------------------------------------------------------------------------------------------------- + const account_public_address& miner::get_mining_address() const + { + return m_mine_address; + } + //----------------------------------------------------------------------------------------------------- + uint32_t miner::get_threads_count() const { + return m_threads_total; + } + //----------------------------------------------------------------------------------------------------- + bool miner::start(const account_public_address& adr, size_t threads_count, const boost::thread::attributes& attrs, bool do_background, bool ignore_battery) + { + m_mine_address = adr; + m_threads_total = static_cast<uint32_t>(threads_count); + m_starter_nonce = crypto::rand<uint32_t>(); + CRITICAL_REGION_LOCAL(m_threads_lock); + if(is_mining()) + { + LOG_ERROR("Starting miner but it's already started"); + return false; + } + + if(!m_threads.empty()) + { + LOG_ERROR("Unable to start miner because there are active mining threads"); + return false; + } + + if(!m_template_no) + request_block_template();//lets update block template + + boost::interprocess::ipcdetail::atomic_write32(&m_stop, 0); + boost::interprocess::ipcdetail::atomic_write32(&m_thread_index, 0); + set_is_background_mining_enabled(do_background); + set_ignore_battery(ignore_battery); + + for(size_t i = 0; i != threads_count; i++) + { + m_threads.push_back(boost::thread(attrs, boost::bind(&miner::worker_thread, this))); + } + + LOG_PRINT_L0("Mining has started with " << threads_count << " threads, good luck!" ); + + if( get_is_background_mining_enabled() ) + { + m_background_mining_thread = boost::thread(attrs, boost::bind(&miner::background_worker_thread, this)); + LOG_PRINT_L0("Background mining controller thread started" ); + } + + return true; + } + //----------------------------------------------------------------------------------------------------- + uint64_t miner::get_speed() const + { + if(is_mining()) { + return m_current_hash_rate; + } + else { + return 0; + } + } + //----------------------------------------------------------------------------------------------------- + void miner::send_stop_signal() + { + boost::interprocess::ipcdetail::atomic_write32(&m_stop, 1); + } + //----------------------------------------------------------------------------------------------------- + bool miner::stop() + { + MTRACE("Miner has received stop signal"); + + if (!is_mining()) + { + MDEBUG("Not mining - nothing to stop" ); + return true; + } + + send_stop_signal(); + CRITICAL_REGION_LOCAL(m_threads_lock); + + // In case background mining was active and the miner threads are waiting + // on the background miner to signal start. + m_is_background_mining_started_cond.notify_all(); + + for(boost::thread& th: m_threads) + th.join(); + + // The background mining thread could be sleeping for a long time, so we + // interrupt it just in case + m_background_mining_thread.interrupt(); + m_background_mining_thread.join(); + + MINFO("Mining has been stopped, " << m_threads.size() << " finished" ); + m_threads.clear(); + return true; + } + //----------------------------------------------------------------------------------------------------- + bool miner::find_nonce_for_given_block(block& bl, const difficulty_type& diffic, uint64_t height) + { + for(; bl.nonce != std::numeric_limits<uint32_t>::max(); bl.nonce++) + { + crypto::hash h; + get_block_longhash(bl, h, height); + + if(check_hash(h, diffic)) + { + bl.invalidate_hashes(); + return true; + } + } + bl.invalidate_hashes(); + return false; + } + //----------------------------------------------------------------------------------------------------- + void miner::on_synchronized() + { + if(m_do_mining) + { + boost::thread::attributes attrs; + attrs.set_stack_size(THREAD_STACK_SIZE); + + start(m_mine_address, m_threads_total, attrs, get_is_background_mining_enabled()); + } + } + //----------------------------------------------------------------------------------------------------- + void miner::pause() + { + CRITICAL_REGION_LOCAL(m_miners_count_lock); + MDEBUG("miner::pause: " << m_pausers_count << " -> " << (m_pausers_count + 1)); + ++m_pausers_count; + if(m_pausers_count == 1 && is_mining()) + MDEBUG("MINING PAUSED"); + } + //----------------------------------------------------------------------------------------------------- + void miner::resume() + { + CRITICAL_REGION_LOCAL(m_miners_count_lock); + MDEBUG("miner::resume: " << m_pausers_count << " -> " << (m_pausers_count - 1)); + --m_pausers_count; + if(m_pausers_count < 0) + { + m_pausers_count = 0; + MERROR("Unexpected miner::resume() called"); + } + if(!m_pausers_count && is_mining()) + MDEBUG("MINING RESUMED"); + } + //----------------------------------------------------------------------------------------------------- + bool miner::worker_thread() + { + uint32_t th_local_index = boost::interprocess::ipcdetail::atomic_inc32(&m_thread_index); + MGINFO("Miner thread was started ["<< th_local_index << "]"); + MLOG_SET_THREAD_NAME(std::string("[miner ") + std::to_string(th_local_index) + "]"); + uint32_t nonce = m_starter_nonce + th_local_index; + uint64_t height = 0; + difficulty_type local_diff = 0; + uint32_t local_template_ver = 0; + block b; + slow_hash_allocate_state(); + while(!m_stop) + { + if(m_pausers_count)//anti split workaround + { + misc_utils::sleep_no_w(100); + continue; + } + else if( m_is_background_mining_enabled ) + { + misc_utils::sleep_no_w(m_miner_extra_sleep); + while( !m_is_background_mining_started ) + { + MGINFO("background mining is enabled, but not started, waiting until start triggers"); + boost::unique_lock<boost::mutex> started_lock( m_is_background_mining_started_mutex ); + m_is_background_mining_started_cond.wait( started_lock ); + if( m_stop ) break; + } + + if( m_stop ) continue; + } + + if(local_template_ver != m_template_no) + { + CRITICAL_REGION_BEGIN(m_template_lock); + b = m_template; + local_diff = m_diffic; + height = m_height; + CRITICAL_REGION_END(); + local_template_ver = m_template_no; + nonce = m_starter_nonce + th_local_index; + } + + if(!local_template_ver)//no any set_block_template call + { + LOG_PRINT_L2("Block template not set yet"); + epee::misc_utils::sleep_no_w(1000); + continue; + } + + b.nonce = nonce; + crypto::hash h; + get_block_longhash(b, h, height); + + if(check_hash(h, local_diff)) + { + //we lucky! + ++m_config.current_extra_message_index; + MGINFO_GREEN("Found block for difficulty: " << local_diff); + if(!m_phandler->handle_block_found(b)) + { + --m_config.current_extra_message_index; + }else + { + //success update, lets update config + if (!m_config_folder_path.empty()) + epee::serialization::store_t_to_json_file(m_config, m_config_folder_path + "/" + MINER_CONFIG_FILE_NAME); + } + } + nonce+=m_threads_total; + ++m_hashes; + } + slow_hash_free_state(); + MGINFO("Miner thread stopped ["<< th_local_index << "]"); + return true; + } + //----------------------------------------------------------------------------------------------------- + bool miner::get_is_background_mining_enabled() const + { + return m_is_background_mining_enabled; + } + //----------------------------------------------------------------------------------------------------- + bool miner::get_ignore_battery() const + { + return m_ignore_battery; + } + //----------------------------------------------------------------------------------------------------- + /** + * This has differing behaviour depending on if mining has been started/etc. + * Note: add documentation + */ + bool miner::set_is_background_mining_enabled(bool is_background_mining_enabled) + { + m_is_background_mining_enabled = is_background_mining_enabled; + // Extra logic will be required if we make this function public in the future + // and allow toggling smart mining without start/stop + //m_is_background_mining_enabled_cond.notify_one(); + return true; + } + //----------------------------------------------------------------------------------------------------- + void miner::set_ignore_battery(bool ignore_battery) + { + m_ignore_battery = ignore_battery; + } + //----------------------------------------------------------------------------------------------------- + uint64_t miner::get_min_idle_seconds() const + { + return m_min_idle_seconds; + } + //----------------------------------------------------------------------------------------------------- + bool miner::set_min_idle_seconds(uint64_t min_idle_seconds) + { + if(min_idle_seconds > BACKGROUND_MINING_MAX_MIN_IDLE_INTERVAL_IN_SECONDS) return false; + if(min_idle_seconds < BACKGROUND_MINING_MIN_MIN_IDLE_INTERVAL_IN_SECONDS) return false; + m_min_idle_seconds = min_idle_seconds; + return true; + } + //----------------------------------------------------------------------------------------------------- + uint8_t miner::get_idle_threshold() const + { + return m_idle_threshold; + } + //----------------------------------------------------------------------------------------------------- + bool miner::set_idle_threshold(uint8_t idle_threshold) + { + if(idle_threshold > BACKGROUND_MINING_MAX_IDLE_THRESHOLD_PERCENTAGE) return false; + if(idle_threshold < BACKGROUND_MINING_MIN_IDLE_THRESHOLD_PERCENTAGE) return false; + m_idle_threshold = idle_threshold; + return true; + } + //----------------------------------------------------------------------------------------------------- + uint8_t miner::get_mining_target() const + { + return m_mining_target; + } + //----------------------------------------------------------------------------------------------------- + bool miner::set_mining_target(uint8_t mining_target) + { + if(mining_target > BACKGROUND_MINING_MAX_MINING_TARGET_PERCENTAGE) return false; + if(mining_target < BACKGROUND_MINING_MIN_MINING_TARGET_PERCENTAGE) return false; + m_mining_target = mining_target; + return true; + } + //----------------------------------------------------------------------------------------------------- + bool miner::background_worker_thread() + { + uint64_t prev_total_time, current_total_time; + uint64_t prev_idle_time, current_idle_time; + uint64_t previous_process_time = 0, current_process_time = 0; + m_is_background_mining_started = false; + + if(!get_system_times(prev_total_time, prev_idle_time)) + { + LOG_ERROR("get_system_times call failed, background mining will NOT work!"); + return false; + } + + while(!m_stop) + { + + try + { + // Commenting out the below since we're going with privatizing the bg mining enabled + // function, but I'll leave the code/comments here for anyone that wants to modify the + // patch in the future + // ------------------------------------------------------------------------------------- + // All of this might be overkill if we just enforced some simple requirements + // about changing this variable before/after the miner starts, but I envision + // in the future a checkbox that you can tick on/off for background mining after + // you've clicked "start mining". There's still an issue here where if background + // mining is disabled when start is called, this thread is never created, and so + // enabling after does nothing, something I have to fix in the future. However, + // this should take care of the case where mining is started with bg-enabled, + // and then the user decides to un-check background mining, and just do + // regular full-speed mining. I might just be over-doing it and thinking up + // non-existant use-cases, so if the concensus is to simplify, we can remove all this fluff. + /* + while( !m_is_background_mining_enabled ) + { + MGINFO("background mining is disabled, waiting until enabled!"); + boost::unique_lock<boost::mutex> enabled_lock( m_is_background_mining_enabled_mutex ); + m_is_background_mining_enabled_cond.wait( enabled_lock ); + } + */ + + // If we're already mining, then sleep for the miner monitor interval. + // If we're NOT mining, then sleep for the idle monitor interval + uint64_t sleep_for_seconds = BACKGROUND_MINING_MINER_MONITOR_INVERVAL_IN_SECONDS; + if( !m_is_background_mining_started ) sleep_for_seconds = get_min_idle_seconds(); + boost::this_thread::sleep_for(boost::chrono::seconds(sleep_for_seconds)); + } + catch(const boost::thread_interrupted&) + { + MDEBUG("background miner thread interrupted "); + continue; // if interrupted because stop called, loop should end .. + } + + boost::tribool battery_powered(on_battery_power()); + bool on_ac_power = false; + if(indeterminate( battery_powered )) + { + // name could be better, only ignores battery requirement if we failed + // to get the status of the system + if( m_ignore_battery ) + { + on_ac_power = true; + } + } + else + { + on_ac_power = !battery_powered; + } + + if( m_is_background_mining_started ) + { + // figure out if we need to stop, and monitor mining usage + + // If we get here, then previous values are initialized. + // Let's get some current data for comparison. + + if(!get_system_times(current_total_time, current_idle_time)) + { + MERROR("get_system_times call failed"); + continue; + } + + if(!get_process_time(current_process_time)) + { + MERROR("get_process_time call failed!"); + continue; + } + + uint64_t total_diff = (current_total_time - prev_total_time); + uint64_t idle_diff = (current_idle_time - prev_idle_time); + uint64_t process_diff = (current_process_time - previous_process_time); + uint8_t idle_percentage = get_percent_of_total(idle_diff, total_diff); + uint8_t process_percentage = get_percent_of_total(process_diff, total_diff); + + MGINFO("idle percentage is " << unsigned(idle_percentage) << "\%, miner percentage is " << unsigned(process_percentage) << "\%, ac power : " << on_ac_power); + if( idle_percentage + process_percentage < get_idle_threshold() || !on_ac_power ) + { + MGINFO("cpu is " << unsigned(idle_percentage) << "% idle, idle threshold is " << unsigned(get_idle_threshold()) << "\%, ac power : " << on_ac_power << ", background mining stopping, thanks for your contribution!"); + m_is_background_mining_started = false; + + // reset process times + previous_process_time = 0; + current_process_time = 0; + } + else + { + previous_process_time = current_process_time; + + // adjust the miner extra sleep variable + int64_t miner_extra_sleep_change = (-1 * (get_mining_target() - process_percentage) ); + int64_t new_miner_extra_sleep = m_miner_extra_sleep + miner_extra_sleep_change; + // if you start the miner with few threads on a multicore system, this could + // fall below zero because all the time functions aggregate across all processors. + // I'm just hard limiting to 5 millis min sleep here, other options? + m_miner_extra_sleep = std::max( new_miner_extra_sleep , (int64_t)5 ); + MDEBUG("m_miner_extra_sleep " << m_miner_extra_sleep); + } + + prev_total_time = current_total_time; + prev_idle_time = current_idle_time; + } + else if( on_ac_power ) + { + // figure out if we need to start + + if(!get_system_times(current_total_time, current_idle_time)) + { + MERROR("get_system_times call failed"); + continue; + } + + uint64_t total_diff = (current_total_time - prev_total_time); + uint64_t idle_diff = (current_idle_time - prev_idle_time); + uint8_t idle_percentage = get_percent_of_total(idle_diff, total_diff); + + MGINFO("idle percentage is " << unsigned(idle_percentage)); + if( idle_percentage >= get_idle_threshold() && on_ac_power ) + { + MGINFO("cpu is " << unsigned(idle_percentage) << "% idle, idle threshold is " << unsigned(get_idle_threshold()) << "\%, ac power : " << on_ac_power << ", background mining started, good luck!"); + m_is_background_mining_started = true; + m_is_background_mining_started_cond.notify_all(); + + // Wait for a little mining to happen .. + boost::this_thread::sleep_for(boost::chrono::seconds( 1 )); + + // Starting data ... + if(!get_process_time(previous_process_time)) + { + m_is_background_mining_started = false; + MERROR("get_process_time call failed!"); + } + } + + prev_total_time = current_total_time; + prev_idle_time = current_idle_time; + } + } + + return true; + } + //----------------------------------------------------------------------------------------------------- + bool miner::get_system_times(uint64_t& total_time, uint64_t& idle_time) + { + #ifdef _WIN32 + + FILETIME idleTime; + FILETIME kernelTime; + FILETIME userTime; + if ( GetSystemTimes( &idleTime, &kernelTime, &userTime ) != -1 ) + { + total_time = + ( (((uint64_t)(kernelTime.dwHighDateTime)) << 32) | ((uint64_t)kernelTime.dwLowDateTime) ) + + ( (((uint64_t)(userTime.dwHighDateTime)) << 32) | ((uint64_t)userTime.dwLowDateTime) ); + + idle_time = ( (((uint64_t)(idleTime.dwHighDateTime)) << 32) | ((uint64_t)idleTime.dwLowDateTime) ); + + return true; + } + + #elif defined(__linux__) + + const std::string STR_CPU("cpu"); + const std::size_t STR_CPU_LEN = STR_CPU.size(); + const std::string STAT_FILE_PATH = "/proc/stat"; + + if( !epee::file_io_utils::is_file_exist(STAT_FILE_PATH) ) + { + LOG_ERROR("'" << STAT_FILE_PATH << "' file does not exist"); + return false; + } + + std::ifstream stat_file_stream(STAT_FILE_PATH); + if( stat_file_stream.fail() ) + { + LOG_ERROR("failed to open '" << STAT_FILE_PATH << "'"); + return false; + } + + std::string line; + std::getline(stat_file_stream, line); + std::istringstream stat_file_iss(line); + stat_file_iss.ignore(65536, ' '); // skip cpu label ... + uint64_t utime, ntime, stime, itime; + if( !(stat_file_iss >> utime && stat_file_iss >> ntime && stat_file_iss >> stime && stat_file_iss >> itime) ) + { + LOG_ERROR("failed to read '" << STAT_FILE_PATH << "'"); + return false; + } + + idle_time = itime; + total_time = utime + ntime + stime + itime; + + return true; + + #endif + + return false; // unsupported systemm.. + } + //----------------------------------------------------------------------------------------------------- + bool miner::get_process_time(uint64_t& total_time) + { + #ifdef _WIN32 + + FILETIME createTime; + FILETIME exitTime; + FILETIME kernelTime; + FILETIME userTime; + if ( GetProcessTimes( GetCurrentProcess(), &createTime, &exitTime, &kernelTime, &userTime ) != -1 ) + { + total_time = + ( (((uint64_t)(kernelTime.dwHighDateTime)) << 32) | ((uint64_t)kernelTime.dwLowDateTime) ) + + ( (((uint64_t)(userTime.dwHighDateTime)) << 32) | ((uint64_t)userTime.dwLowDateTime) ); + + return true; + } + + #elif defined(__linux__) && defined(_SC_CLK_TCK) + + struct tms tms; + if ( times(&tms) != (clock_t)-1 ) + { + total_time = tms.tms_utime + tms.tms_stime; + return true; + } + + #endif + + return false; // unsupported system.. + } + //----------------------------------------------------------------------------------------------------- + uint8_t miner::get_percent_of_total(uint64_t other, uint64_t total) + { + return (uint8_t)( ceil( (other * 1.f / total * 1.f) * 100) ); + } + //----------------------------------------------------------------------------------------------------- + boost::logic::tribool miner::on_battery_power() + { + #ifdef _WIN32 + + SYSTEM_POWER_STATUS power_status; + if ( GetSystemPowerStatus( &power_status ) != 0 ) + { + return boost::logic::tribool(power_status.ACLineStatus != 1); + } + + #elif defined(__linux__) + + // i've only tested on UBUNTU, these paths might be different on other systems + // need to figure out a way to make this more flexible + std::string power_supply_path = ""; + const std::string POWER_SUPPLY_STATUS_PATHS[] = + { + "/sys/class/power_supply/ACAD/online", + "/sys/class/power_supply/AC/online", + "/sys/class/power_supply/AC0/online", + "/sys/class/power_supply/ADP0/online" + }; + + for(const std::string& path : POWER_SUPPLY_STATUS_PATHS) + { + if( epee::file_io_utils::is_file_exist(path) ) + { + power_supply_path = path; + break; + } + } + + if( power_supply_path.empty() ) + { + LOG_ERROR("Couldn't find battery/power status file, can't determine if plugged in!"); + return boost::logic::tribool(boost::logic::indeterminate);; + } + + std::ifstream power_stream(power_supply_path); + if( power_stream.fail() ) + { + LOG_ERROR("failed to open '" << power_supply_path << "'"); + return boost::logic::tribool(boost::logic::indeterminate);; + } + + return boost::logic::tribool( (power_stream.get() != '1') ); + + #endif + + LOG_ERROR("couldn't query power status"); + return boost::logic::tribool(boost::logic::indeterminate); + } +} diff --git a/src/cryptonote_core/miner.h b/src/cryptonote_basic/miner.h index f24f6e960..964ee6a36 100644 --- a/src/cryptonote_core/miner.h +++ b/src/cryptonote_basic/miner.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -31,11 +31,19 @@ #pragma once #include <boost/program_options.hpp> +#include <boost/logic/tribool_fwd.hpp> #include <atomic> #include "cryptonote_basic.h" #include "difficulty.h" #include "math_helper.h" - +#ifdef _WIN32 +#include <windows.h> +#elif defined(__linux__) +#include <unistd.h> +#include <sys/resource.h> +#include <sys/times.h> +#include <time.h> +#endif namespace cryptonote { @@ -43,7 +51,7 @@ namespace cryptonote struct i_miner_handler { virtual bool handle_block_found(block& b) = 0; - virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) = 0; + virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce) = 0; protected: ~i_miner_handler(){}; }; @@ -60,7 +68,7 @@ namespace cryptonote static void init_options(boost::program_options::options_description& desc); bool set_block_template(const block& bl, const difficulty_type& diffic, uint64_t height); bool on_block_chain_update(); - bool start(const account_public_address& adr, size_t threads_count, const boost::thread::attributes& attrs); + bool start(const account_public_address& adr, size_t threads_count, const boost::thread::attributes& attrs, bool do_background = false, bool ignore_battery = false); uint64_t get_speed() const; uint32_t get_threads_count() const; void send_stop_signal(); @@ -74,6 +82,27 @@ namespace cryptonote void pause(); void resume(); void do_print_hashrate(bool do_hr); + bool get_is_background_mining_enabled() const; + bool get_ignore_battery() const; + uint64_t get_min_idle_seconds() const; + bool set_min_idle_seconds(uint64_t min_idle_seconds); + uint8_t get_idle_threshold() const; + bool set_idle_threshold(uint8_t idle_threshold); + uint8_t get_mining_target() const; + bool set_mining_target(uint8_t mining_target); + + static constexpr uint8_t BACKGROUND_MINING_DEFAULT_IDLE_THRESHOLD_PERCENTAGE = 90; + static constexpr uint8_t BACKGROUND_MINING_MIN_IDLE_THRESHOLD_PERCENTAGE = 50; + static constexpr uint8_t BACKGROUND_MINING_MAX_IDLE_THRESHOLD_PERCENTAGE = 99; + static constexpr uint16_t BACKGROUND_MINING_DEFAULT_MIN_IDLE_INTERVAL_IN_SECONDS = 10; + static constexpr uint16_t BACKGROUND_MINING_MIN_MIN_IDLE_INTERVAL_IN_SECONDS = 10; + static constexpr uint16_t BACKGROUND_MINING_MAX_MIN_IDLE_INTERVAL_IN_SECONDS = 3600; + static constexpr uint8_t BACKGROUND_MINING_DEFAULT_MINING_TARGET_PERCENTAGE = 40; + static constexpr uint8_t BACKGROUND_MINING_MIN_MINING_TARGET_PERCENTAGE = 5; + static constexpr uint8_t BACKGROUND_MINING_MAX_MINING_TARGET_PERCENTAGE = 50; + static constexpr uint8_t BACKGROUND_MINING_MINER_MONITOR_INVERVAL_IN_SECONDS = 10; + static constexpr uint64_t BACKGROUND_MINING_DEFAULT_MINER_EXTRA_SLEEP_MILLIS = 400; // ramp up + static constexpr uint64_t BACKGROUND_MINING_MIN_MINER_EXTRA_SLEEP_MILLIS = 5; private: bool worker_thread(); @@ -119,8 +148,26 @@ namespace cryptonote bool m_do_print_hashrate; bool m_do_mining; + // background mining stuffs .. + + bool set_is_background_mining_enabled(bool is_background_mining_enabled); + void set_ignore_battery(bool ignore_battery); + bool background_worker_thread(); + std::atomic<bool> m_is_background_mining_enabled; + bool m_ignore_battery; + boost::mutex m_is_background_mining_enabled_mutex; + boost::condition_variable m_is_background_mining_enabled_cond; + std::atomic<bool> m_is_background_mining_started; + boost::mutex m_is_background_mining_started_mutex; + boost::condition_variable m_is_background_mining_started_cond; + boost::thread m_background_mining_thread; + uint64_t m_min_idle_seconds; + uint8_t m_idle_threshold; + uint8_t m_mining_target; + std::atomic<uint64_t> m_miner_extra_sleep; + static bool get_system_times(uint64_t& total_time, uint64_t& idle_time); + static bool get_process_time(uint64_t& total_time); + static uint8_t get_percent_of_total(uint64_t some_time, uint64_t total_time); + static boost::logic::tribool on_battery_power(); }; } - - - diff --git a/src/cryptonote_core/tx_extra.h b/src/cryptonote_basic/tx_extra.h index 6f5fbe466..5a6c3176d 100644 --- a/src/cryptonote_core/tx_extra.h +++ b/src/cryptonote_basic/tx_extra.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_core/verification_context.h b/src/cryptonote_basic/verification_context.h index 0bb635e84..ce885ec1d 100644 --- a/src/cryptonote_core/verification_context.h +++ b/src/cryptonote_basic/verification_context.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 3f15cee3f..8dbf1b53b 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -57,6 +57,7 @@ #define CRYPTONOTE_REWARD_BLOCKS_WINDOW 100 #define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 60000 //size of block (bytes) after which reward for block calculated using block size #define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 20000 //size of block (bytes) after which reward for block calculated using block size - before first fork +#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 300000 //size of block (bytes) after which reward for block calculated using block size - second change, from v5 #define CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE 600 #define CRYPTONOTE_DISPLAY_DECIMAL_POINT 12 // COIN - number of smallest units in one coin @@ -66,6 +67,7 @@ #define FEE_PER_KB ((uint64_t)2000000000) // 2 * pow(10, 9) #define DYNAMIC_FEE_PER_KB_BASE_FEE ((uint64_t)2000000000) // 2 * pow(10,9) #define DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD ((uint64_t)10000000000000) // 10 * pow(10,12) +#define DYNAMIC_FEE_PER_KB_BASE_FEE_V5 ((uint64_t)2000000000 * (uint64_t)CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5) #define ORPHANED_BLOCKS_MAX_COUNT 100 @@ -129,6 +131,10 @@ #define THREAD_STACK_SIZE 5 * 1024 * 1024 #define HF_VERSION_DYNAMIC_FEE 4 +#define HF_VERSION_MIN_MIXIN_4 6 +#define HF_VERSION_ENFORCE_RCT 6 + +#define PER_KB_FEE_QUANTIZATION_DECIMALS 8 // New constants are intended to go here namespace config diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt index ad1745b7f..5944ddcd1 100644 --- a/src/cryptonote_core/CMakeLists.txt +++ b/src/cryptonote_core/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -27,38 +27,19 @@ # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. set(cryptonote_core_sources - account.cpp blockchain.cpp - checkpoints.cpp - cryptonote_basic_impl.cpp cryptonote_core.cpp - cryptonote_format_utils.cpp - difficulty.cpp - miner.cpp tx_pool.cpp - hardfork.cpp) + cryptonote_tx_utils.cpp) set(cryptonote_core_headers) set(cryptonote_core_private_headers - account.h - account_boost_serialization.h blockchain_storage_boost_serialization.h blockchain.h - checkpoints.h - connection_context.h - cryptonote_basic.h - cryptonote_basic_impl.h - cryptonote_boost_serialization.h cryptonote_core.h - cryptonote_format_utils.h - cryptonote_stat_info.h - difficulty.h - miner.h - tx_extra.h tx_pool.h - verification_context.h - hardfork.h) + cryptonote_tx_utils.h) if(PER_BLOCK_CHECKPOINT) set(Blocks "blocks") diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index b344c5597..74c5b3b83 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -34,14 +34,13 @@ #include <boost/range/adaptor/reversed.hpp> #include "include_base_utils.h" -#include "cryptonote_basic_impl.h" +#include "cryptonote_basic/cryptonote_basic_impl.h" #include "tx_pool.h" #include "blockchain.h" #include "blockchain_db/blockchain_db.h" -#include "cryptonote_format_utils.h" -#include "cryptonote_boost_serialization.h" +#include "cryptonote_basic/cryptonote_boost_serialization.h" #include "cryptonote_config.h" -#include "miner.h" +#include "cryptonote_basic/miner.h" #include "misc_language.h" #include "profile_tools.h" #include "file_io_utils.h" @@ -49,8 +48,8 @@ #include "common/boost_serialization_helper.h" #include "warnings.h" #include "crypto/hash.h" -#include "cryptonote_core/checkpoints.h" -#include "cryptonote_core/cryptonote_core.h" +#include "cryptonote_basic/checkpoints.h" +#include "cryptonote_core.h" #include "ringct/rctSigs.h" #include "common/perf_timer.h" #if defined(PER_BLOCK_CHECKPOINT) @@ -98,8 +97,8 @@ static const struct { // version 4 starts from block 1220516, which is on or around the 5th of January, 2017. Fork time finalised on 2016-09-18. { 4, 1220516, 0, 1483574400 }, - // version 5 starts from block 1406997, which is on or around the 20th of September, 2017. Fork time finalised on 2016-09-18. - { 5, 1406997, 0, 1505865600 }, + // version 5 starts from block 1288616, which is on or around the 15th of April, 2017. Fork time finalised on 2017-03-14. + { 5, 1288616, 0, 1489520158 }, }; static const uint64_t mainnet_hard_fork_version_1_till = 1009826; @@ -118,7 +117,7 @@ static const struct { // versions 3-5 were passed in rapid succession from September 18th, 2016 { 3, 800500, 0, 1472415034 }, { 4, 801219, 0, 1472415035 }, - { 5, 802660, 0, 1472415036 }, + { 5, 802660, 0, 1472415036 + 86400*180 }, // add 5 months on testnet to shut the update warning up since there's a large gap to v6 }; static const uint64_t testnet_hard_fork_version_1_till = 624633; @@ -1073,7 +1072,7 @@ uint64_t Blockchain::get_current_cumulative_blocksize_limit() const // in a lot of places. That flag is not referenced in any of the code // nor any of the makefiles, howeve. Need to look into whether or not it's // necessary at all. -bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) +bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce) { LOG_PRINT_L3("Blockchain::" << __func__); size_t median_size; @@ -1097,7 +1096,7 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m size_t txs_size; uint64_t fee; - if (!m_tx_pool.fill_block_template(b, median_size, already_generated_coins, txs_size, fee, m_hardfork->get_current_version())) + if (!m_tx_pool.fill_block_template(b, median_size, already_generated_coins, txs_size, fee, expected_reward, m_hardfork->get_current_version())) { return false; } @@ -1432,7 +1431,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id return true; } //------------------------------------------------------------------ -bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks, std::list<transaction>& txs) const +bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks, std::list<cryptonote::blobdata>& txs) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -1444,17 +1443,17 @@ bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::list<block return false; } - for(const block& blk : blocks) + for(const auto& blk : blocks) { std::list<crypto::hash> missed_ids; - get_transactions(blk.tx_hashes, txs, missed_ids); + get_transactions_blobs(blk.second.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::get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks) const +bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -1463,7 +1462,12 @@ bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::list<block for(size_t i = start_offset; i < start_offset + count && i < m_db->height();i++) { - blocks.push_back(m_db->get_block_from_height(i)); + blocks.push_back(std::make_pair(m_db->get_block_blob_from_height(i), block())); + if (!parse_and_validate_block_from_blob(blocks.back().first, blocks.back().second)) + { + LOG_ERROR("Invalid block"); + return false; + } } return true; } @@ -1481,22 +1485,22 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO CRITICAL_REGION_LOCAL(m_blockchain_lock); m_db->block_txn_start(true); rsp.current_blockchain_height = get_current_blockchain_height(); - std::list<block> blocks; + std::list<std::pair<cryptonote::blobdata,block>> blocks; get_blocks(arg.blocks, blocks, rsp.missed_ids); for (const auto& bl: blocks) { std::list<crypto::hash> missed_tx_ids; - std::list<transaction> txs; + std::list<cryptonote::blobdata> txs; // FIXME: s/rsp.missed_ids/missed_tx_id/ ? Seems like rsp.missed_ids // is for missed blocks, not missed transactions as well. - get_transactions(bl.tx_hashes, txs, missed_tx_ids); + get_transactions_blobs(bl.second.tx_hashes, txs, missed_tx_ids); if (missed_tx_ids.size() != 0) { LOG_ERROR("Error retrieving blocks, missed " << missed_tx_ids.size() - << " transactions for block with hash: " << get_block_hash(bl) + << " transactions for block with hash: " << get_block_hash(bl.second) << std::endl ); @@ -1511,17 +1515,17 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO rsp.blocks.push_back(block_complete_entry()); block_complete_entry& e = rsp.blocks.back(); //pack block - e.block = t_serializable_object_to_blob(bl); + e.block = bl.first; //pack transactions - for (transaction& tx: txs) - e.txs.push_back(t_serializable_object_to_blob(tx)); + for (const cryptonote::blobdata& tx: txs) + e.txs.push_back(tx); } //get another transactions, if need - std::list<transaction> txs; - get_transactions(arg.txs, txs, rsp.missed_ids); + std::list<cryptonote::blobdata> txs; + get_transactions_blobs(arg.txs, txs, rsp.missed_ids); //pack aside transactions for (const auto& tx: txs) - rsp.txs.push_back(t_serializable_object_to_blob(tx)); + rsp.txs.push_back(tx); m_db->block_txn_stop(); return true; @@ -1890,7 +1894,12 @@ bool Blockchain::get_blocks(const t_ids_container& block_ids, t_blocks_container { try { - blocks.push_back(m_db->get_block(block_hash)); + blocks.push_back(std::make_pair(m_db->get_block_blob(block_hash), block())); + if (!parse_and_validate_block_from_blob(blocks.back().first, blocks.back().second)) + { + LOG_ERROR("Invalid block"); + return false; + } } catch (const BLOCK_DNE& e) { @@ -1907,7 +1916,7 @@ bool Blockchain::get_blocks(const t_ids_container& block_ids, t_blocks_container //TODO: return type should be void, throw on exception // alternatively, return true only if no transactions missed template<class t_ids_container, class t_tx_container, class t_missed_container> -bool Blockchain::get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) const +bool Blockchain::get_transactions_blobs(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -1916,8 +1925,8 @@ bool Blockchain::get_transactions(const t_ids_container& txs_ids, t_tx_container { try { - transaction tx; - if (m_db->get_tx(tx_hash, tx)) + cryptonote::blobdata tx; + if (m_db->get_tx_blob(tx_hash, tx)) txs.push_back(std::move(tx)); else missed_txs.push_back(tx_hash); @@ -1930,6 +1939,37 @@ bool Blockchain::get_transactions(const t_ids_container& txs_ids, t_tx_container return true; } //------------------------------------------------------------------ +template<class t_ids_container, class t_tx_container, class t_missed_container> +bool Blockchain::get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) const +{ + LOG_PRINT_L3("Blockchain::" << __func__); + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + for (const auto& tx_hash : txs_ids) + { + try + { + cryptonote::blobdata tx; + if (m_db->get_tx_blob(tx_hash, tx)) + { + txs.push_back(transaction()); + if (!parse_and_validate_tx_from_blob(tx, txs.back())) + { + LOG_ERROR("Invalid transaction"); + return false; + } + } + else + missed_txs.push_back(tx_hash); + } + catch (const std::exception& e) + { + return false; + } + } + return true; +} +//------------------------------------------------------------------ void Blockchain::print_blockchain(uint64_t start_index, uint64_t end_index) const { LOG_PRINT_L3("Blockchain::" << __func__); @@ -1987,12 +2027,14 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc return false; } + m_db->block_txn_start(true); resp.total_height = get_current_blockchain_height(); size_t count = 0; for(size_t i = resp.start_height; i < resp.total_height && count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT; i++, count++) { resp.m_block_ids.push_back(m_db->get_block_hash_from_height(i)); } + m_db->block_txn_stop(); return true; } //------------------------------------------------------------------ @@ -2000,7 +2042,7 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc // find split point between ours and foreign blockchain (or start at // blockchain height <req_start_block>), and return up to max_count FULL // blocks by reference. -bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<block, std::list<transaction> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const +bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<cryptonote::blobdata, std::list<cryptonote::blobdata> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -2023,16 +2065,20 @@ bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, cons } } + m_db->block_txn_start(true); total_height = get_current_blockchain_height(); size_t count = 0; for(size_t i = start_height; i < total_height && count < max_count; i++, count++) { blocks.resize(blocks.size()+1); - blocks.back().first = m_db->get_block_from_height(i); + blocks.back().first = m_db->get_block_blob_from_height(i); + block b; + CHECK_AND_ASSERT_MES(parse_and_validate_block_from_blob(blocks.back().first, b), false, "internal error, invalid block"); std::list<crypto::hash> mis; - get_transactions(blocks.back().first.tx_hashes, blocks.back().second, mis); + get_transactions_blobs(b.tx_hashes, blocks.back().second, mis); CHECK_AND_ASSERT_MES(!mis.size(), false, "internal error, transaction from block not found"); } + m_db->block_txn_stop(); return true; } //------------------------------------------------------------------ @@ -2380,7 +2426,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, { size_t n_unmixable = 0, n_mixable = 0; size_t mixin = std::numeric_limits<size_t>::max(); - const size_t min_mixin = hf_version >= 5 ? 4 : 2; + const size_t min_mixin = hf_version >= HF_VERSION_MIN_MIXIN_4 ? 4 : 2; for (const auto& txin : tx.vin) { // non txin_to_key inputs will be rejected below @@ -2433,7 +2479,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, tvc.m_verifivation_failed = true; return false; } - const size_t min_tx_version = (n_unmixable > 0 ? 1 : (hf_version >= 5) ? 2 : 1); + const size_t min_tx_version = (n_unmixable > 0 ? 1 : (hf_version >= HF_VERSION_ENFORCE_RCT) ? 2 : 1); if (tx.version < min_tx_version) { MERROR_VER("transaction version " << (unsigned)tx.version << " is lower than min accepted version " << min_tx_version); @@ -2750,21 +2796,43 @@ void Blockchain::check_ring_signature(const crypto::hash &tx_prefix_hash, const } //------------------------------------------------------------------ -uint64_t Blockchain::get_dynamic_per_kb_fee(uint64_t block_reward, size_t median_block_size) +static uint64_t get_fee_quantization_mask() { - if (median_block_size < CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2) - median_block_size = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2; + static uint64_t mask = 0; + if (mask == 0) + { + mask = 1; + for (size_t n = PER_KB_FEE_QUANTIZATION_DECIMALS; n < CRYPTONOTE_DISPLAY_DECIMAL_POINT; ++n) + mask *= 10; + } + return mask; +} + +//------------------------------------------------------------------ +uint64_t Blockchain::get_dynamic_per_kb_fee(uint64_t block_reward, size_t median_block_size, uint8_t version) +{ + const uint64_t min_block_size = get_min_block_size(version); + const uint64_t fee_per_kb_base = version >= 5 ? DYNAMIC_FEE_PER_KB_BASE_FEE_V5 : DYNAMIC_FEE_PER_KB_BASE_FEE; + + if (median_block_size < min_block_size) + median_block_size = min_block_size; - uint64_t unscaled_fee_per_kb = (DYNAMIC_FEE_PER_KB_BASE_FEE * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / median_block_size); + uint64_t unscaled_fee_per_kb = (fee_per_kb_base * min_block_size / median_block_size); uint64_t hi, lo = mul128(unscaled_fee_per_kb, block_reward, &hi); static_assert(DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD % 1000000 == 0, "DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD must be divisible by 1000000"); static_assert(DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD / 1000000 <= std::numeric_limits<uint32_t>::max(), "DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD is too large"); + // divide in two steps, since the divisor must be 32 bits, but DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD isn't div128_32(hi, lo, DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD / 1000000, &hi, &lo); div128_32(hi, lo, 1000000, &hi, &lo); assert(hi == 0); - return lo; + // quantize fee up to 8 decimals + uint64_t mask = get_fee_quantization_mask(); + uint64_t qlo = (lo + mask - 1) / mask * mask; + MDEBUG("lo " << print_money(lo) << ", qlo " << print_money(qlo) << ", mask " << mask); + + return qlo; } //------------------------------------------------------------------ @@ -2784,7 +2852,7 @@ bool Blockchain::check_fee(size_t blob_size, uint64_t fee) const uint64_t base_reward; if (!get_block_reward(median, 1, already_generated_coins, base_reward, version)) return false; - fee_per_kb = get_dynamic_per_kb_fee(base_reward, median); + fee_per_kb = get_dynamic_per_kb_fee(base_reward, median, version); } MDEBUG("Using " << print_money(fee) << "/kB fee"); @@ -2792,7 +2860,7 @@ bool Blockchain::check_fee(size_t blob_size, uint64_t fee) const needed_fee += (blob_size % 1024) ? 1 : 0; needed_fee *= fee_per_kb; - if (fee < needed_fee) + if (fee < needed_fee * 0.98) // keep a little buffer on acceptance { MERROR_VER("transaction fee is not enough: " << print_money(fee) << ", minimum fee: " << print_money(needed_fee)); return false; @@ -2811,14 +2879,15 @@ uint64_t Blockchain::get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks) cons if (grace_blocks >= CRYPTONOTE_REWARD_BLOCKS_WINDOW) grace_blocks = CRYPTONOTE_REWARD_BLOCKS_WINDOW - 1; + const uint64_t min_block_size = get_min_block_size(version); std::vector<size_t> sz; get_last_n_blocks_sizes(sz, CRYPTONOTE_REWARD_BLOCKS_WINDOW - grace_blocks); for (size_t i = 0; i < grace_blocks; ++i) - sz.push_back(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2); + sz.push_back(min_block_size); uint64_t median = epee::misc_utils::median(sz); - if(median <= CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2) - median = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2; + if(median <= min_block_size) + median = min_block_size; uint64_t already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0; uint64_t base_reward; @@ -2828,7 +2897,7 @@ uint64_t Blockchain::get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks) cons base_reward = BLOCK_REWARD_OVERESTIMATE; } - uint64_t fee = get_dynamic_per_kb_fee(base_reward, median); + uint64_t fee = get_dynamic_per_kb_fee(base_reward, median, version); MDEBUG("Estimating " << grace_blocks << "-block fee at " << print_money(fee) << "/kB"); return fee; } @@ -3347,7 +3416,7 @@ leave: //------------------------------------------------------------------ bool Blockchain::update_next_cumulative_size_limit() { - uint64_t full_reward_zone = get_current_hard_fork_version() < 2 ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 : CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2; + uint64_t full_reward_zone = get_min_block_size(get_current_hard_fork_version()); LOG_PRINT_L3("Blockchain::" << __func__); std::vector<size_t> sz; @@ -3541,7 +3610,7 @@ void Blockchain::output_scan_worker(const uint64_t amount, const std::vector<uin { try { - m_db->get_output_key(amount, offsets, outputs); + m_db->get_output_key(amount, offsets, outputs, true); } catch (const std::exception& e) { @@ -3596,6 +3665,8 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::list<block_complete_e std::vector<std::unordered_map<crypto::hash, crypto::hash>> maps(threads); std::vector < std::vector < block >> blocks(threads); auto it = blocks_entry.begin(); + boost::thread::attributes attrs; + attrs.set_stack_size(THREAD_STACK_SIZE); for (uint64_t i = 0; i < threads; i++) { @@ -3655,7 +3726,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::list<block_complete_e m_blocks_longhash_table.clear(); for (uint64_t i = 0; i < threads; i++) { - thread_list.push_back(new boost::thread(&Blockchain::block_longhash_worker, this, height + (i * batches), std::cref(blocks[i]), std::ref(maps[i]))); + thread_list.push_back(new boost::thread(attrs, boost::bind(&Blockchain::block_longhash_worker, this, height + (i * batches), std::cref(blocks[i]), std::ref(maps[i])))); } for (size_t j = 0; j < thread_list.size(); j++) @@ -3949,10 +4020,37 @@ void Blockchain::cancel() } #if defined(PER_BLOCK_CHECKPOINT) +static const char expected_block_hashes_hash[] = "23d8a8c73de7b2383c72a016d9a6034e69d62dd48077d1c414e064ceab6daa94"; void Blockchain::load_compiled_in_block_hashes() { - if (m_fast_sync && get_blocks_dat_start(m_testnet) != nullptr) + if (m_fast_sync && get_blocks_dat_start(m_testnet) != nullptr && get_blocks_dat_size(m_testnet) > 0) { + MINFO("Loading precomputed blocks (" << get_blocks_dat_size(m_testnet) << " bytes)"); + + if (!m_testnet) + { + // first check hash + crypto::hash hash; + if (!tools::sha256sum(get_blocks_dat_start(m_testnet), get_blocks_dat_size(m_testnet), hash)) + { + MERROR("Failed to hash precomputed blocks data"); + return; + } + MINFO("precomputed blocks hash: " << hash << ", expected " << expected_block_hashes_hash); + cryptonote::blobdata expected_hash_data; + if (!epee::string_tools::parse_hexstr_to_binbuff(std::string(expected_block_hashes_hash), expected_hash_data) || expected_hash_data.size() != sizeof(crypto::hash)) + { + MERROR("Failed to parse expected block hashes hash"); + return; + } + const crypto::hash expected_hash = *reinterpret_cast<const crypto::hash*>(expected_hash_data.data()); + if (hash != expected_hash) + { + MERROR("Block hash data does not match expected hash"); + return; + } + } + if (get_blocks_dat_size(m_testnet) > 4) { const unsigned char *p = get_blocks_dat_start(m_testnet); @@ -3960,7 +4058,6 @@ void Blockchain::load_compiled_in_block_hashes() const size_t size_needed = 4 + nblocks * sizeof(crypto::hash); if(nblocks > 0 && nblocks > m_db->height() && get_blocks_dat_size(m_testnet) >= size_needed) { - MINFO("Loading precomputed blocks: " << nblocks); p += sizeof(uint32_t); for (uint32_t i = 0; i < nblocks; i++) { @@ -3969,6 +4066,7 @@ void Blockchain::load_compiled_in_block_hashes() p += sizeof(hash.data); m_blocks_hash_check.push_back(hash); } + MINFO(nblocks << " block hashes loaded"); // FIXME: clear tx_pool because the process might have been // terminated and caused it to store txs kept by blocks. @@ -4012,3 +4110,7 @@ bool Blockchain::for_all_outputs(std::function<bool(uint64_t amount, const crypt { return m_db->for_all_outputs(f);; } + +namespace cryptonote { +template bool Blockchain::get_transactions(const std::vector<crypto::hash>&, std::list<transaction>&, std::list<crypto::hash>&) const; +} diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index cd452faf4..46f7ac682 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -29,6 +29,7 @@ // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #pragma once +#include <boost/asio/io_service.hpp> #include <boost/serialization/serialization.hpp> #include <boost/serialization/version.hpp> #include <boost/serialization/list.hpp> @@ -42,16 +43,16 @@ #include "syncobj.h" #include "string_tools.h" -#include "cryptonote_basic.h" +#include "cryptonote_basic/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 "cryptonote_basic/difficulty.h" +#include "cryptonote_tx_utils.h" +#include "cryptonote_basic/verification_context.h" #include "crypto/hash.h" -#include "checkpoints.h" -#include "hardfork.h" +#include "cryptonote_basic/checkpoints.h" +#include "cryptonote_basic/hardfork.h" #include "blockchain_db/blockchain_db.h" namespace cryptonote @@ -153,7 +154,7 @@ namespace cryptonote * * @return false if start_offset > blockchain height, else true */ - bool get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks, std::list<transaction>& txs) const; + bool get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks, std::list<cryptonote::blobdata>& txs) const; /** * @brief get blocks from blocks based on start height and count @@ -164,7 +165,7 @@ namespace cryptonote * * @return false if start_offset > blockchain height, else true */ - bool get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks) const; + bool get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks) const; /** * @brief compiles a list of all blocks stored as alternative chains @@ -322,11 +323,12 @@ namespace cryptonote * @param miner_address address new coins for the block will go to * @param di return-by-reference tells the miner what the difficulty target is * @param height return-by-reference tells the miner what height it's mining against + * @param expected_reward return-by-reference the total reward awarded to the miner finding this block, including transaction fees * @param ex_nonce extra data to be added to the miner transaction's extra * * @return true if block template filled in successfully, else false */ - bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, const blobdata& ex_nonce); + bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce); /** * @brief checks if a block is known about with a given hash @@ -407,7 +409,7 @@ namespace cryptonote * * @return true if a block found in common or req_start_block specified, else false */ - bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<block, std::list<transaction> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const; + bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<cryptonote::blobdata, std::list<cryptonote::blobdata> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const; /** * @brief retrieves a set of blocks and their transactions, and possibly other transactions @@ -520,10 +522,11 @@ namespace cryptonote * * @param block_reward the current block reward * @param median_block_size the median blob's size in the past window + * @param version hard fork version for rules and constants to use * * @return the per kB fee */ - static uint64_t get_dynamic_per_kb_fee(uint64_t block_reward, size_t median_block_size); + static uint64_t get_dynamic_per_kb_fee(uint64_t block_reward, size_t median_block_size, uint8_t version); /** * @brief get dynamic per kB fee estimate for the next few blocks @@ -621,9 +624,10 @@ namespace cryptonote * @return false if an unexpected exception occurs, else true */ template<class t_ids_container, class t_tx_container, class t_missed_container> + bool get_transactions_blobs(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) const; + template<class t_ids_container, class t_tx_container, class t_missed_container> bool get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) const; - //debug functions /** diff --git a/src/cryptonote_core/blockchain_storage_boost_serialization.h b/src/cryptonote_core/blockchain_storage_boost_serialization.h index 0518b9aa7..e87a51f10 100644 --- a/src/cryptonote_core/blockchain_storage_boost_serialization.h +++ b/src/cryptonote_core/blockchain_storage_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 900dc58ba..9c122f511 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -35,14 +35,16 @@ using namespace epee; #include "cryptonote_core.h" #include "common/command_line.h" #include "common/util.h" +#include "common/updates.h" +#include "common/download.h" #include "warnings.h" #include "crypto/crypto.h" #include "cryptonote_config.h" -#include "cryptonote_format_utils.h" +#include "cryptonote_tx_utils.h" #include "misc_language.h" #include <csignal> #include <p2p/net_node.h> -#include "cryptonote_core/checkpoints.h" +#include "cryptonote_basic/checkpoints.h" #include "ringct/rctTypes.h" #include "blockchain_db/blockchain_db.h" #include "blockchain_db/lmdb/db_lmdb.h" @@ -58,6 +60,8 @@ DISABLE_VS_WARNINGS(4355) #define MERROR_VER(x) MCERROR("verify", x) +#define BAD_SEMANTICS_TXES_MAX_SIZE 100 + namespace cryptonote { @@ -71,7 +75,8 @@ namespace cryptonote m_target_blockchain_height(0), m_checkpoints_path(""), m_last_dns_checkpoints_update(0), - m_last_json_checkpoints_update(0) + m_last_json_checkpoints_update(0), + m_update_download(0) { set_cryptonote_protocol(pprotocol); } @@ -130,6 +135,15 @@ namespace cryptonote void core::stop() { m_blockchain_storage.cancel(); + + tools::download_async_handle handle; + { + boost::lock_guard<boost::mutex> lock(m_update_mutex); + handle = m_update_download; + m_update_download = 0; + } + if (handle) + tools::download_cancel(handle); } //----------------------------------------------------------------------------------- void core::init_options(boost::program_options::options_description& desc) @@ -148,6 +162,13 @@ namespace cryptonote command_line::add_arg(desc, command_line::arg_db_sync_mode); command_line::add_arg(desc, command_line::arg_show_time_stats); command_line::add_arg(desc, command_line::arg_block_sync_size); + command_line::add_arg(desc, command_line::arg_check_updates); + + // we now also need some of net_node's options (p2p bind arg, for separate data dir) + command_line::add_arg(desc, nodetool::arg_testnet_p2p_bind_port, false); + command_line::add_arg(desc, nodetool::arg_p2p_bind_port, false); + + miner::init_options(desc); } //----------------------------------------------------------------------------------------------- bool core::handle_command_line(const boost::program_options::variables_map& vm) @@ -195,62 +216,44 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- - bool core::get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks, std::list<transaction>& txs) const + bool core::get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks, std::list<cryptonote::blobdata>& txs) const { return m_blockchain_storage.get_blocks(start_offset, count, blocks, txs); } //----------------------------------------------------------------------------------------------- - bool core::get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks) const + bool core::get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks) const { return m_blockchain_storage.get_blocks(start_offset, count, blocks); - } //----------------------------------------------------------------------------------------------- - bool core::get_transactions(const std::vector<crypto::hash>& txs_ids, std::list<transaction>& txs, std::list<crypto::hash>& missed_txs) const + } + //----------------------------------------------------------------------------------------------- + bool core::get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks) const { - return m_blockchain_storage.get_transactions(txs_ids, txs, missed_txs); + std::list<std::pair<cryptonote::blobdata, cryptonote::block>> bs; + if (!m_blockchain_storage.get_blocks(start_offset, count, bs)) + return false; + for (const auto &b: bs) + blocks.push_back(b.second); + return true; } //----------------------------------------------------------------------------------------------- - bool core::get_alternative_blocks(std::list<block>& blocks) const + bool core::get_transactions(const std::vector<crypto::hash>& txs_ids, std::list<cryptonote::blobdata>& txs, std::list<crypto::hash>& missed_txs) const { - return m_blockchain_storage.get_alternative_blocks(blocks); + return m_blockchain_storage.get_transactions_blobs(txs_ids, txs, missed_txs); } //----------------------------------------------------------------------------------------------- - size_t core::get_alternative_blocks_count() const + bool core::get_transactions(const std::vector<crypto::hash>& txs_ids, std::list<transaction>& txs, std::list<crypto::hash>& missed_txs) const { - return m_blockchain_storage.get_alternative_blocks_count(); + return m_blockchain_storage.get_transactions(txs_ids, txs, missed_txs); } //----------------------------------------------------------------------------------------------- - bool core::lock_db_directory(const boost::filesystem::path &path) + bool core::get_alternative_blocks(std::list<block>& blocks) const { - // boost doesn't like locking directories... - const boost::filesystem::path lock_path = path / ".daemon_lock"; - - try - { - // ensure the file exists - std::ofstream(lock_path.string(), std::ios::out).close(); - - db_lock = boost::interprocess::file_lock(lock_path.string().c_str()); - LOG_PRINT_L1("Locking " << lock_path.string()); - if (!db_lock.try_lock()) - { - LOG_ERROR("Failed to lock " << lock_path.string()); - return false; - } - return true; - } - catch (const std::exception &e) - { - LOG_ERROR("Error trying to lock " << lock_path.string() << ": " << e.what()); - return false; - } + return m_blockchain_storage.get_alternative_blocks(blocks); } //----------------------------------------------------------------------------------------------- - bool core::unlock_db_directory() + size_t core::get_alternative_blocks_count() const { - db_lock.unlock(); - db_lock = boost::interprocess::file_lock(); - LOG_PRINT_L1("Blockchain directory successfully unlocked"); - return true; + return m_blockchain_storage.get_alternative_blocks_count(); } //----------------------------------------------------------------------------------------------- bool core::init(const boost::program_options::variables_map& vm, const cryptonote::test_options *test_options) @@ -276,6 +279,7 @@ namespace cryptonote 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; uint64_t blocks_threads = command_line::get_arg(vm, command_line::arg_prep_blocks_threads); + std::string check_updates_string = command_line::get_arg(vm, command_line::arg_check_updates); boost::filesystem::path folder(m_config_folder); if (m_fakechain) @@ -284,11 +288,6 @@ namespace cryptonote // make sure the data directory exists, and try to lock it CHECK_AND_ASSERT_MES (boost::filesystem::exists(folder) || boost::filesystem::create_directories(folder), false, std::string("Failed to create directory ").append(folder.string()).c_str()); - if (!lock_db_directory (folder)) - { - LOG_ERROR ("Failed to lock " << folder); - return false; - } // check for blockchain.bin try @@ -418,6 +417,20 @@ namespace cryptonote // with respect to what blocks we already have CHECK_AND_ASSERT_MES(update_checkpoints(), false, "One or more checkpoints loaded from json or dns conflicted with existing checkpoints."); + // DNS versions checking + if (check_updates_string == "disabled") + check_updates_level = UPDATES_DISABLED; + else if (check_updates_string == "notify") + check_updates_level = UPDATES_NOTIFY; + else if (check_updates_string == "download") + check_updates_level = UPDATES_DOWNLOAD; + else if (check_updates_string == "update") + check_updates_level = UPDATES_UPDATE; + else { + MERROR("Invalid argument to --dns-versions-check: " << check_updates_string); + return false; + } + r = m_miner.init(vm, m_testnet); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize miner instance"); @@ -440,7 +453,6 @@ namespace cryptonote m_miner.stop(); m_mempool.deinit(); m_blockchain_storage.deinit(); - unlock_db_directory(); return true; } //----------------------------------------------------------------------------------------------- @@ -496,11 +508,14 @@ namespace cryptonote } //std::cout << "!"<< tx.vin.size() << std::endl; - if (bad_semantics_txes.find(tx_hash) != bad_semantics_txes.end()) + for (int idx = 0; idx < 2; ++idx) { - LOG_PRINT_L1("Transaction already seen with bad semantics, rejected"); - tvc.m_verifivation_failed = true; - return false; + if (bad_semantics_txes[idx].find(tx_hash) != bad_semantics_txes[idx].end()) + { + LOG_PRINT_L1("Transaction already seen with bad semantics, rejected"); + tvc.m_verifivation_failed = true; + return false; + } } uint8_t version = m_blockchain_storage.get_current_hard_fork_version(); @@ -551,8 +566,13 @@ namespace cryptonote if(!check_tx_semantic(tx, keeped_by_block)) { LOG_PRINT_L1("WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " semantic, rejected"); - bad_semantics_txes.insert(tx_hash); tvc.m_verifivation_failed = true; + bad_semantics_txes[0].insert(tx_hash); + if (bad_semantics_txes[0].size() >= BAD_SEMANTICS_TXES_MAX_SIZE) + { + std::swap(bad_semantics_txes[0], bad_semantics_txes[1]); + bad_semantics_txes[0].clear(); + } return false; } @@ -639,6 +659,12 @@ namespace cryptonote return false; } + if (!check_tx_inputs_keyimages_domain(tx)) + { + MERROR_VER("tx uses key image not in the valid domain"); + return false; + } + if (tx.version >= 2) { const rct::rctSig &rv = tx.rct_signatures; @@ -722,6 +748,18 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- + bool core::check_tx_inputs_keyimages_domain(const transaction& tx) const + { + std::unordered_set<crypto::key_image> ki; + for(const auto& in: tx.vin) + { + CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, false); + if (!(rct::scalarmultKey(rct::ki2rct(tokey_in.k_image), rct::curveOrder()) == rct::identity())) + return false; + } + return true; + } + //----------------------------------------------------------------------------------------------- bool core::add_new_tx(const transaction& tx, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { crypto::hash tx_hash = get_transaction_hash(tx); @@ -758,7 +796,7 @@ namespace cryptonote { // we attempt to relay txes that should be relayed, but were not std::list<std::pair<crypto::hash, cryptonote::transaction>> txs; - if (m_mempool.get_relayable_transactions(txs)) + if (m_mempool.get_relayable_transactions(txs) && !txs.empty()) { cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context); tx_verification_context tvc = AUTO_VAL_INIT(tvc); @@ -789,9 +827,9 @@ namespace cryptonote m_mempool.set_relayed(txs); } //----------------------------------------------------------------------------------------------- - bool core::get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) + bool core::get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce) { - return m_blockchain_storage.create_block_template(b, adr, diffic, height, ex_nonce); + return m_blockchain_storage.create_block_template(b, adr, diffic, height, expected_reward, ex_nonce); } //----------------------------------------------------------------------------------------------- bool core::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const @@ -799,7 +837,7 @@ namespace cryptonote return m_blockchain_storage.find_blockchain_supplement(qblock_ids, resp); } //----------------------------------------------------------------------------------------------- - bool core::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<block, std::list<transaction> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const + bool core::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<cryptonote::blobdata, std::list<cryptonote::blobdata> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const { return m_blockchain_storage.find_blockchain_supplement(req_start_block, qblock_ids, blocks, total_height, start_height, max_count); } @@ -849,11 +887,28 @@ namespace cryptonote m_miner.resume(); } //----------------------------------------------------------------------------------------------- + block_complete_entry get_block_complete_entry(block& b, tx_memory_pool &pool) + { + block_complete_entry bce; + bce.block = cryptonote::block_to_blob(b); + for (const auto &tx_hash: b.tx_hashes) + { + cryptonote::transaction tx; + CHECK_AND_ASSERT_THROW_MES(pool.get_transaction(tx_hash, tx), "Transaction not found in pool"); + bce.txs.push_back(cryptonote::tx_to_blob(tx)); + } + return bce; + } + //----------------------------------------------------------------------------------------------- bool core::handle_block_found(block& b) { block_verification_context bvc = boost::value_initialized<block_verification_context>(); m_miner.pause(); + std::list<block_complete_entry> blocks; + blocks.push_back(get_block_complete_entry(b, m_mempool)); + prepare_handle_incoming_blocks(blocks); m_blockchain_storage.add_new_block(b, bvc); + cleanup_handle_incoming_blocks(true); //anyway - update miner template update_miner_block_template(); m_miner.resume(); @@ -867,8 +922,8 @@ namespace cryptonote arg.hop = 0; arg.current_blockchain_height = m_blockchain_storage.get_current_blockchain_height(); std::list<crypto::hash> missed_txs; - std::list<transaction> txs; - m_blockchain_storage.get_transactions(b.tx_hashes, txs, missed_txs); + std::list<cryptonote::blobdata> txs; + m_blockchain_storage.get_transactions_blobs(b.tx_hashes, txs, missed_txs); if(missed_txs.size() && m_blockchain_storage.get_block_id_by_height(get_block_height(b)) != get_block_hash(b)) { LOG_PRINT_L1("Block found but, seems that reorganize just happened after that, do not relay this block"); @@ -880,7 +935,7 @@ namespace cryptonote block_to_blob(b, arg.b.block); //pack transactions for(auto& tx: txs) - arg.b.txs.push_back(t_serializable_object_to_blob(tx)); + arg.b.txs.push_back(tx); m_pprotocol->relay_block(arg, exclude_context); } @@ -981,7 +1036,13 @@ namespace cryptonote m_mempool.get_transactions(txs); return true; } - //----------------------------------------------------------------------------------------------- + //----------------------------------------------------------------------------------------------- + bool core::get_pool_transaction_hashes(std::vector<crypto::hash>& txs) const + { + m_mempool.get_transaction_hashes(txs); + return true; + } + //----------------------------------------------------------------------------------------------- bool core::get_pool_transaction(const crypto::hash &id, transaction& tx) const { return m_mempool.get_transaction(id, tx); @@ -1040,6 +1101,7 @@ namespace cryptonote 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_check_updates_interval.do_call(boost::bind(&core::check_updates, this)); m_miner.on_idle(); m_mempool.on_idle(); return true; @@ -1067,6 +1129,99 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- + bool core::check_updates() + { + static const char software[] = "monero"; + static const char subdir[] = "cli"; // because it can never be simple +#ifdef BUILD_TAG + static const char buildtag[] = BOOST_PP_STRINGIZE(BUILD_TAG); +#else + static const char buildtag[] = "source"; +#endif + + if (check_updates_level == UPDATES_DISABLED) + return true; + + std::string version, hash; + MCDEBUG("updates", "Checking for a new " << software << " version for " << buildtag); + if (!tools::check_updates(software, buildtag, version, hash)) + return false; + + if (tools::vercmp(version.c_str(), MONERO_VERSION) <= 0) + return true; + + std::string url = tools::get_update_url(software, subdir, buildtag, version, true); + MCLOG_CYAN(el::Level::Info, "global", "Version " << version << " of " << software << " for " << buildtag << " is available: " << url << ", SHA256 hash " << hash); + + if (check_updates_level == UPDATES_NOTIFY) + return true; + + url = tools::get_update_url(software, subdir, buildtag, version, false); + std::string filename; + const char *slash = strrchr(url.c_str(), '/'); + if (slash) + filename = slash + 1; + else + filename = std::string(software) + "-update-" + version; + boost::filesystem::path path(epee::string_tools::get_current_module_folder()); + path /= filename; + + boost::unique_lock<boost::mutex> lock(m_update_mutex); + + if (m_update_download != 0) + { + MCDEBUG("updates", "Already downloading update"); + return true; + } + + crypto::hash file_hash; + if (!tools::sha256sum(path.string(), file_hash) || (hash != epee::string_tools::pod_to_hex(file_hash))) + { + MCDEBUG("updates", "We don't have that file already, downloading"); + m_last_update_length = 0; + m_update_download = tools::download_async(path.string(), url, [this, hash](const std::string &path, const std::string &uri, bool success) { + if (success) + { + crypto::hash file_hash; + if (!tools::sha256sum(path, file_hash)) + { + MCERROR("updates", "Failed to hash " << path); + } + if (hash != epee::string_tools::pod_to_hex(file_hash)) + { + MCERROR("updates", "Download from " << uri << " does not match the expected hash"); + } + MCLOG_CYAN(el::Level::Info, "updates", "New version downloaded to " << path); + } + else + { + MCERROR("updates", "Failed to download " << uri); + } + boost::unique_lock<boost::mutex> lock(m_update_mutex); + m_update_download = 0; + }, [this](const std::string &path, const std::string &uri, size_t length, ssize_t content_length) { + if (length >= m_last_update_length + 1024 * 1024 * 10) + { + m_last_update_length = length; + MCDEBUG("updates", "Downloaded " << length << "/" << (content_length ? std::to_string(content_length) : "unknown")); + } + return true; + }); + } + else + { + MCDEBUG("updates", "We already have " << path << " with expected hash"); + } + + lock.unlock(); + + if (check_updates_level == UPDATES_DOWNLOAD) + return true; + + MCERROR("updates", "Download/update not implemented yet"); + return true; + } + //----------------------------------------------------------------------------------------------- void core::set_target_blockchain_height(uint64_t target_blockchain_height) { m_target_blockchain_height = target_blockchain_height; diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 1b9518c96..e56c2dcf1 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -39,11 +39,12 @@ #include "p2p/net_node_common.h" #include "cryptonote_protocol/cryptonote_protocol_handler_common.h" #include "storages/portable_storage_template_helper.h" +#include "common/download.h" #include "tx_pool.h" #include "blockchain.h" -#include "miner.h" -#include "connection_context.h" -#include "cryptonote_core/cryptonote_stat_info.h" +#include "cryptonote_basic/miner.h" +#include "cryptonote_basic/connection_context.h" +#include "cryptonote_basic/cryptonote_stat_info.h" #include "warnings.h" #include "crypto/hash.h" @@ -179,7 +180,7 @@ namespace cryptonote * * @note see Blockchain::create_block_template */ - virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce); + virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce); /** * @brief called when a transaction is relayed @@ -287,16 +288,23 @@ namespace cryptonote bool get_blockchain_top(uint64_t& height, crypto::hash& top_id) const; /** - * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::list<block>&, std::list<transaction>&) const + * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::list<std::pair<cryptonote::blobdata,block>>&, std::list<transaction>&) const * - * @note see Blockchain::get_blocks(uint64_t, size_t, std::list<block>&, std::list<transaction>&) const + * @note see Blockchain::get_blocks(uint64_t, size_t, std::list<std::pair<cryptonote::blobdata,block>>&, std::list<transaction>&) const */ - bool get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks, std::list<transaction>& txs) const; + bool get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks, std::list<cryptonote::blobdata>& txs) const; /** - * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::list<block>&) const + * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::list<std::pair<cryptonote::blobdata,block>>&) const * - * @note see Blockchain::get_blocks(uint64_t, size_t, std::list<block>&) const + * @note see Blockchain::get_blocks(uint64_t, size_t, std::list<std::pair<cryptonote::blobdata,block>>&) const + */ + bool get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks) const; + + /** + * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::list<std::pair<cryptonote::blobdata,block>>&) const + * + * @note see Blockchain::get_blocks(uint64_t, size_t, std::list<std::pair<cryptonote::blobdata,block>>&) const */ bool get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks) const; @@ -323,6 +331,13 @@ namespace cryptonote * * @note see Blockchain::get_transactions */ + bool get_transactions(const std::vector<crypto::hash>& txs_ids, std::list<cryptonote::blobdata>& txs, std::list<crypto::hash>& missed_txs) const; + + /** + * @copydoc Blockchain::get_transactions + * + * @note see Blockchain::get_transactions + */ bool get_transactions(const std::vector<crypto::hash>& txs_ids, std::list<transaction>& txs, std::list<crypto::hash>& missed_txs) const; /** @@ -382,6 +397,13 @@ namespace cryptonote bool get_pool_transactions(std::list<transaction>& txs) const; /** + * @copydoc tx_memory_pool::get_transactions + * + * @note see tx_memory_pool::get_transactions + */ + bool get_pool_transaction_hashes(std::vector<crypto::hash>& txs) const; + + /** * @copydoc tx_memory_pool::get_transaction * * @note see tx_memory_pool::get_transaction @@ -431,11 +453,11 @@ namespace cryptonote bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const; /** - * @copydoc Blockchain::find_blockchain_supplement(const uint64_t, const std::list<crypto::hash>&, std::list<std::pair<block, std::list<transaction> > >&, uint64_t&, uint64_t&, size_t) const + * @copydoc Blockchain::find_blockchain_supplement(const uint64_t, const std::list<crypto::hash>&, std::list<std::pair<cryptonote::blobdata, std::list<cryptonote::blobdata> > >&, uint64_t&, uint64_t&, size_t) const * - * @note see Blockchain::find_blockchain_supplement(const uint64_t, const std::list<crypto::hash>&, std::list<std::pair<block, std::list<transaction> > >&, uint64_t&, uint64_t&, size_t) const + * @note see Blockchain::find_blockchain_supplement(const uint64_t, const std::list<crypto::hash>&, std::list<std::pair<cryptonote::blobdata, std::list<transaction> > >&, uint64_t&, uint64_t&, size_t) const */ - bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<block, std::list<transaction> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const; + bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<cryptonote::blobdata, std::list<cryptonote::blobdata> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const; /** * @brief gets some stats about the daemon @@ -745,6 +767,16 @@ namespace cryptonote bool check_tx_inputs_keyimages_diff(const transaction& tx) const; /** + * @brief verify that each input key image in a transaction is in + * the valid domain + * + * @param tx the transaction to check + * + * @return false if any key image is not in the valid domain, otherwise true + */ + bool check_tx_inputs_keyimages_domain(const transaction& tx) const; + + /** * @brief checks HardFork status and prints messages about it * * Checks the status of HardFork and logs/prints if an update to @@ -764,22 +796,11 @@ namespace cryptonote bool relay_txpool_transactions(); /** - * @brief locks a file in the BlockchainDB directory - * - * @param path the directory in which to place the file - * - * @return true if lock acquired successfully, otherwise false - */ - bool lock_db_directory(const boost::filesystem::path &path); - - /** - * @brief unlocks the db directory - * - * @note see lock_db_directory() + * @brief checks DNS versions * - * @return true + * @return true on success, false otherwise */ - bool unlock_db_directory(); + bool check_updates(); bool m_test_drop_download = true; //!< whether or not to drop incoming blocks (for testing) @@ -801,10 +822,10 @@ namespace cryptonote cryptonote_protocol_stub m_protocol_stub; //!< cryptonote protocol stub instance epee::math_helper::once_a_time_seconds<60*60*12, false> m_store_blockchain_interval; //!< interval for manual storing of Blockchain, if enabled - epee::math_helper::once_a_time_seconds<60*60*2, false> m_fork_moaner; //!< interval for checking HardFork status + epee::math_helper::once_a_time_seconds<60*60*2, true> m_fork_moaner; //!< interval for checking HardFork status epee::math_helper::once_a_time_seconds<60*2, false> m_txpool_auto_relayer; //!< interval for checking re-relaying txpool transactions + epee::math_helper::once_a_time_seconds<60*60*12, true> m_check_updates_interval; //!< interval for checking for new versions - friend class tx_validate_inputs; std::atomic<bool> m_starter_message_showed; //!< has the "daemon will sync now" message been shown? uint64_t m_target_blockchain_height; //!< blockchain height target @@ -819,13 +840,22 @@ namespace cryptonote std::atomic_flag m_checkpoints_updating; //!< set if checkpoints are currently updating to avoid multiple threads attempting to update at once - boost::interprocess::file_lock db_lock; //!< a lock object for a file lock in the db directory - size_t block_sync_size; time_t start_time; - std::unordered_set<crypto::hash> bad_semantics_txes; + std::unordered_set<crypto::hash> bad_semantics_txes[2]; + + enum { + UPDATES_DISABLED, + UPDATES_NOTIFY, + UPDATES_DOWNLOAD, + UPDATES_UPDATE, + } check_updates_level; + + tools::download_async_handle m_update_download; + size_t m_last_update_length; + boost::mutex m_update_mutex; }; } diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp new file mode 100644 index 000000000..26d5fb767 --- /dev/null +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -0,0 +1,499 @@ +// Copyright (c) 2014-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#include "include_base_utils.h" +using namespace epee; + +#include "cryptonote_tx_utils.h" +#include "cryptonote_config.h" +#include "cryptonote_basic/miner.h" +#include "crypto/crypto.h" +#include "crypto/hash.h" +#include "ringct/rctSigs.h" + +namespace cryptonote +{ + //--------------------------------------------------------------- + bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce, size_t max_outs, uint8_t hard_fork_version) { + tx.vin.clear(); + tx.vout.clear(); + tx.extra.clear(); + + keypair txkey = keypair::generate(); + add_tx_pub_key_to_extra(tx, txkey.pub); + if(!extra_nonce.empty()) + if(!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce)) + return false; + + txin_gen in; + in.height = height; + + uint64_t block_reward; + if(!get_block_reward(median_size, current_block_size, already_generated_coins, block_reward, hard_fork_version)) + { + LOG_PRINT_L0("Block is too big"); + return false; + } + +#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) + LOG_PRINT_L1("Creating block template: reward " << block_reward << + ", fee " << fee); +#endif + block_reward += fee; + + // 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 + // 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; + } + + std::vector<uint64_t> out_amounts; + decompose_amount_into_digits(block_reward, hard_fork_version >= 2 ? 0 : ::config::DEFAULT_DUST_THRESHOLD, + [&out_amounts](uint64_t a_chunk) { out_amounts.push_back(a_chunk); }, + [&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 || 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.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); + } + } + else + { + CHECK_AND_ASSERT_MES(max_outs >= out_amounts.size(), false, "max_out exceeded"); + } + + uint64_t summary_amounts = 0; + for (size_t no = 0; no < out_amounts.size(); no++) + { + crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);; + crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key); + bool r = crypto::generate_key_derivation(miner_address.m_view_public_key, txkey.sec, derivation); + CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to generate_key_derivation(" << miner_address.m_view_public_key << ", " << txkey.sec << ")"); + + r = crypto::derive_public_key(derivation, no, miner_address.m_spend_public_key, out_eph_public_key); + CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to derive_public_key(" << derivation << ", " << no << ", "<< miner_address.m_spend_public_key << ")"); + + txout_to_key tk; + tk.key = out_eph_public_key; + + tx_out out; + summary_amounts += out.amount = out_amounts[no]; + out.target = tk; + tx.vout.push_back(out); + } + + CHECK_AND_ASSERT_MES(summary_amounts == block_reward, false, "Failed to construct miner tx, summary_amounts = " << summary_amounts << " not equal block_reward = " << block_reward); + + 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); + + tx.invalidate_hashes(); + + //LOG_PRINT("MINER_TX generated ok, block_reward=" << print_money(block_reward) << "(" << print_money(block_reward - fee) << "+" << print_money(fee) + // << "), current_block_size=" << current_block_size << ", already_generated_coins=" << already_generated_coins << ", tx_id=" << get_transaction_hash(tx), LOG_LEVEL_2); + return true; + } + //--------------------------------------------------------------- + crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const account_keys &sender_keys) + { + if (destinations.empty()) + return null_pkey; + for (size_t n = 1; n < destinations.size(); ++n) + { + if (!memcmp(&destinations[n].addr, &sender_keys.m_account_address, sizeof(destinations[0].addr))) + continue; + if (destinations[n].amount == 0) + continue; + if (memcmp(&destinations[n].addr, &destinations[0].addr, sizeof(destinations[0].addr))) + return null_pkey; + } + return destinations[0].addr.m_view_public_key; + } + //--------------------------------------------------------------- + bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct) + { + std::vector<rct::key> amount_keys; + tx.set_null(); + amount_keys.clear(); + + tx.version = rct ? 2 : 1; + tx.unlock_time = unlock_time; + + tx.extra = extra; + keypair txkey = keypair::generate(); + remove_field_from_tx_extra(tx.extra, typeid(tx_extra_pub_key)); + add_tx_pub_key_to_extra(tx, txkey.pub); + tx_key = txkey.sec; + + // if we have a stealth payment id, find it and encrypt it with the tx key now + std::vector<tx_extra_field> tx_extra_fields; + if (parse_tx_extra(tx.extra, tx_extra_fields)) + { + tx_extra_nonce extra_nonce; + if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) + { + crypto::hash8 payment_id = null_hash8; + if (get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) + { + LOG_PRINT_L2("Encrypting payment id " << payment_id); + crypto::public_key view_key_pub = get_destination_view_key_pub(destinations, sender_account_keys); + if (view_key_pub == null_pkey) + { + LOG_ERROR("Destinations have to have exactly one output to support encrypted payment ids"); + return false; + } + + if (!encrypt_payment_id(payment_id, view_key_pub, txkey.sec)) + { + LOG_ERROR("Failed to encrypt payment id"); + return false; + } + + std::string extra_nonce; + set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id); + remove_field_from_tx_extra(tx.extra, typeid(tx_extra_nonce)); + if (!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce)) + { + LOG_ERROR("Failed to add encrypted payment id to tx extra"); + return false; + } + LOG_PRINT_L1("Encrypted payment ID: " << payment_id); + } + } + } + else + { + LOG_ERROR("Failed to parse tx extra"); + return false; + } + + struct input_generation_context_data + { + keypair in_ephemeral; + }; + std::vector<input_generation_context_data> in_contexts; + + uint64_t summary_inputs_money = 0; + //fill inputs + int idx = -1; + for(const tx_source_entry& src_entr: sources) + { + ++idx; + if(src_entr.real_output >= src_entr.outputs.size()) + { + LOG_ERROR("real_output index (" << src_entr.real_output << ")bigger than output_keys.size()=" << src_entr.outputs.size()); + return false; + } + summary_inputs_money += src_entr.amount; + + //key_derivation recv_derivation; + in_contexts.push_back(input_generation_context_data()); + keypair& in_ephemeral = in_contexts.back().in_ephemeral; + crypto::key_image img; + if(!generate_key_image_helper(sender_account_keys, src_entr.real_out_tx_key, src_entr.real_output_in_tx_index, in_ephemeral, img)) + return false; + + //check that derivated key is equal with real output key + if( !(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].second.dest) ) + { + LOG_ERROR("derived public key mismatch with output public key at index " << idx << ", real out " << src_entr.real_output << "! "<< ENDL << "derived_key:" + << string_tools::pod_to_hex(in_ephemeral.pub) << ENDL << "real output_public_key:" + << string_tools::pod_to_hex(src_entr.outputs[src_entr.real_output].second) ); + LOG_ERROR("amount " << src_entr.amount << ", rct " << src_entr.rct); + LOG_ERROR("tx pubkey " << src_entr.real_out_tx_key << ", real_output_in_tx_index " << src_entr.real_output_in_tx_index); + return false; + } + + //put key image into tx input + txin_to_key input_to_key; + input_to_key.amount = src_entr.amount; + input_to_key.k_image = img; + + //fill outputs array and use relative offsets + for(const tx_source_entry::output_entry& out_entry: src_entr.outputs) + input_to_key.key_offsets.push_back(out_entry.first); + + input_to_key.key_offsets = absolute_output_offsets_to_relative(input_to_key.key_offsets); + tx.vin.push_back(input_to_key); + } + + // "Shuffle" outs + std::vector<tx_destination_entry> shuffled_dsts(destinations); + std::sort(shuffled_dsts.begin(), shuffled_dsts.end(), [](const tx_destination_entry& de1, const tx_destination_entry& de2) { return de1.amount < de2.amount; } ); + + uint64_t summary_outs_money = 0; + //fill outputs + size_t output_index = 0; + for(const tx_destination_entry& dst_entr: shuffled_dsts) + { + 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(rct::sk2rct(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 << ")"); + + tx_out out; + out.amount = dst_entr.amount; + txout_to_key tk; + tk.key = out_eph_public_key; + out.target = tk; + tx.vout.push_back(out); + output_index++; + summary_outs_money += dst_entr.amount; + } + + //check money + if(summary_outs_money > summary_inputs_money ) + { + LOG_ERROR("Transaction inputs money ("<< summary_inputs_money << ") less than outputs money (" << summary_outs_money << ")"); + return false; + } + + // check for watch only wallet + bool zero_secret_key = true; + for (size_t i = 0; i < sizeof(sender_account_keys.m_spend_secret_key); ++i) + zero_secret_key &= (sender_account_keys.m_spend_secret_key.data[i] == 0); + if (zero_secret_key) + { + MDEBUG("Null secret key, skipping signatures"); + } + + if (tx.version == 1) + { + //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; + for(const tx_source_entry& src_entr: sources) + { + ss_ring_s << "pub_keys:" << ENDL; + std::vector<const crypto::public_key*> keys_ptrs; + std::vector<crypto::public_key> keys(src_entr.outputs.size()); + size_t ii = 0; + for(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<crypto::signature>()); + std::vector<crypto::signature>& sigs = tx.signatures.back(); + sigs.resize(src_entr.outputs.size()); + if (!zero_secret_key) + crypto::generate_ring_signature(tx_prefix_hash, boost::get<txin_to_key>(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 << ENDL; + i++; + } + + MCINFO("construct_tx", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL << ss_ring_s.str()); + } + else + { + size_t n_total_outs = sources[0].outputs.size(); // only for non-simple rct + + // 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 + for(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<uint64_t> inamounts, outamounts; + std::vector<unsigned int> 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<txout_to_key>(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<txin_to_key>(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, amount_keys, index, outSk); + else + tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, 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"); + + MCINFO("construct_tx", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL); + } + + tx.invalidate_hashes(); + + return true; + } + //--------------------------------------------------------------- + bool construct_tx(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time) + { + crypto::secret_key tx_key; + return construct_tx_and_get_tx_key(sender_account_keys, sources, destinations, extra, tx, unlock_time, tx_key); + } + //--------------------------------------------------------------- + bool generate_genesis_block( + block& bl + , std::string const & genesis_tx + , uint32_t nonce + ) + { + //genesis block + bl = boost::value_initialized<block>(); + + + account_public_address ac = boost::value_initialized<account_public_address>(); + std::vector<size_t> sz; + construct_miner_tx(0, 0, 0, 0, 0, ac, bl.miner_tx); // zero fee in genesis + blobdata txb = tx_to_blob(bl.miner_tx); + std::string hex_tx_represent = string_tools::buff_to_hex_nodelimer(txb); + + std::string genesis_coinbase_tx_hex = config::GENESIS_TX; + + blobdata tx_bl; + string_tools::parse_hexstr_to_binbuff(genesis_coinbase_tx_hex, tx_bl); + bool r = parse_and_validate_tx_from_blob(tx_bl, bl.miner_tx); + CHECK_AND_ASSERT_MES(r, false, "failed to parse coinbase tx from hard coded blob"); + bl.major_version = CURRENT_BLOCK_MAJOR_VERSION; + bl.minor_version = CURRENT_BLOCK_MINOR_VERSION; + bl.timestamp = 0; + bl.nonce = nonce; + miner::find_nonce_for_given_block(bl, 1, 0); + bl.invalidate_hashes(); + return true; + } + //--------------------------------------------------------------- +} diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h new file mode 100644 index 000000000..933070e1e --- /dev/null +++ b/src/cryptonote_core/cryptonote_tx_utils.h @@ -0,0 +1,101 @@ +// Copyright (c) 2014-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once +#include "cryptonote_basic/cryptonote_format_utils.h" +#include <boost/serialization/vector.hpp> +#include <boost/serialization/utility.hpp> + +namespace cryptonote +{ + //--------------------------------------------------------------- + bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce = blobdata(), size_t max_outs = 999, uint8_t hard_fork_version = 1); + + struct tx_source_entry + { + typedef std::pair<uint64_t, rct::ctkey> output_entry; + + std::vector<output_entry> 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 + { + uint64_t amount; //money + account_public_address addr; //destination address + + tx_destination_entry() : amount(0), addr(AUTO_VAL_INIT(addr)) { } + tx_destination_entry(uint64_t a, const account_public_address &ad) : amount(a), addr(ad) { } + + BEGIN_SERIALIZE_OBJECT() + VARINT_FIELD(amount) + FIELD(addr) + END_SERIALIZE() + }; + + //--------------------------------------------------------------- + crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const account_keys &sender_keys); + bool construct_tx(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time); + bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct = false); + + bool generate_genesis_block( + block& bl + , std::string const & genesis_tx + , uint32_t nonce + ); + +} + +BOOST_CLASS_VERSION(cryptonote::tx_source_entry, 0) + +namespace boost +{ + namespace serialization + { + template <class Archive> + inline void serialize(Archive &a, cryptonote::tx_source_entry &x, const boost::serialization::version_type ver) + { + a & x.outputs; + a & x.real_output; + a & x.real_out_tx_key; + a & x.real_output_in_tx_index; + a & x.amount; + a & x.rct; + a & x.mask; + } + } +} diff --git a/src/cryptonote_core/miner.cpp b/src/cryptonote_core/miner.cpp deleted file mode 100644 index 88c631f80..000000000 --- a/src/cryptonote_core/miner.cpp +++ /dev/null @@ -1,414 +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 <sstream> -#include <numeric> -#include <boost/utility/value_init.hpp> -#include <boost/interprocess/detail/atomic.hpp> -#include <boost/limits.hpp> -#include "misc_language.h" -#include "include_base_utils.h" -#include "cryptonote_basic_impl.h" -#include "cryptonote_format_utils.h" -#include "file_io_utils.h" -#include "common/command_line.h" -#include "string_coding.h" -#include "storages/portable_storage_template_helper.h" - -#undef MONERO_DEFAULT_LOG_CATEGORY -#define MONERO_DEFAULT_LOG_CATEGORY "miner" - -using namespace epee; - -#include "miner.h" - - -extern "C" void slow_hash_allocate_state(); -extern "C" void slow_hash_free_state(); -namespace cryptonote -{ - - namespace - { - const command_line::arg_descriptor<std::string> arg_extra_messages = {"extra-messages-file", "Specify file for extra messages to include into coinbase transactions", "", true}; - const command_line::arg_descriptor<std::string> arg_start_mining = {"start-mining", "Specify wallet address to mining for", "", true}; - const command_line::arg_descriptor<uint32_t> arg_mining_threads = {"mining-threads", "Specify mining threads count", 0, true}; - } - - - miner::miner(i_miner_handler* phandler):m_stop(1), - m_template(boost::value_initialized<block>()), - m_template_no(0), - m_diffic(0), - m_thread_index(0), - m_phandler(phandler), - m_height(0), - m_pausers_count(0), - m_threads_total(0), - m_starter_nonce(0), - m_last_hr_merge_time(0), - m_hashes(0), - m_do_print_hashrate(false), - m_do_mining(false), - m_current_hash_rate(0) - { - - } - //----------------------------------------------------------------------------------------------------- - miner::~miner() - { - stop(); - } - //----------------------------------------------------------------------------------------------------- - bool miner::set_block_template(const block& bl, const difficulty_type& di, uint64_t height) - { - CRITICAL_REGION_LOCAL(m_template_lock); - m_template = bl; - m_diffic = di; - m_height = height; - ++m_template_no; - m_starter_nonce = crypto::rand<uint32_t>(); - return true; - } - //----------------------------------------------------------------------------------------------------- - bool miner::on_block_chain_update() - { - if(!is_mining()) - return true; - - return request_block_template(); - } - //----------------------------------------------------------------------------------------------------- - bool miner::request_block_template() - { - block bl = AUTO_VAL_INIT(bl); - difficulty_type di = AUTO_VAL_INIT(di); - uint64_t height = AUTO_VAL_INIT(height); - cryptonote::blobdata extra_nonce; - if(m_extra_messages.size() && m_config.current_extra_message_index < m_extra_messages.size()) - { - extra_nonce = m_extra_messages[m_config.current_extra_message_index]; - } - - if(!m_phandler->get_block_template(bl, m_mine_address, di, height, extra_nonce)) - { - LOG_ERROR("Failed to get_block_template(), stopping mining"); - return false; - } - set_block_template(bl, di, height); - return true; - } - //----------------------------------------------------------------------------------------------------- - bool miner::on_idle() - { - m_update_block_template_interval.do_call([&](){ - if(is_mining())request_block_template(); - return true; - }); - - m_update_merge_hr_interval.do_call([&](){ - merge_hr(); - return true; - }); - - return true; - } - //----------------------------------------------------------------------------------------------------- - void miner::do_print_hashrate(bool do_hr) - { - m_do_print_hashrate = do_hr; - } - //----------------------------------------------------------------------------------------------------- - void miner::merge_hr() - { - if(m_last_hr_merge_time && is_mining()) - { - m_current_hash_rate = m_hashes * 1000 / ((misc_utils::get_tick_count() - m_last_hr_merge_time + 1)); - CRITICAL_REGION_LOCAL(m_last_hash_rates_lock); - m_last_hash_rates.push_back(m_current_hash_rate); - if(m_last_hash_rates.size() > 19) - m_last_hash_rates.pop_front(); - if(m_do_print_hashrate) - { - uint64_t total_hr = std::accumulate(m_last_hash_rates.begin(), m_last_hash_rates.end(), 0); - float hr = static_cast<float>(total_hr)/static_cast<float>(m_last_hash_rates.size()); - std::cout << "hashrate: " << std::setprecision(4) << std::fixed << hr << ENDL; - } - } - m_last_hr_merge_time = misc_utils::get_tick_count(); - m_hashes = 0; - } - //----------------------------------------------------------------------------------------------------- - void miner::init_options(boost::program_options::options_description& desc) - { - command_line::add_arg(desc, arg_extra_messages); - command_line::add_arg(desc, arg_start_mining); - command_line::add_arg(desc, arg_mining_threads); - } - //----------------------------------------------------------------------------------------------------- - bool miner::init(const boost::program_options::variables_map& vm, bool testnet) - { - if(command_line::has_arg(vm, arg_extra_messages)) - { - std::string buff; - bool r = file_io_utils::load_file_to_string(command_line::get_arg(vm, arg_extra_messages), buff); - CHECK_AND_ASSERT_MES(r, false, "Failed to load file with extra messages: " << command_line::get_arg(vm, arg_extra_messages)); - std::vector<std::string> extra_vec; - boost::split(extra_vec, buff, boost::is_any_of("\n"), boost::token_compress_on ); - m_extra_messages.resize(extra_vec.size()); - for(size_t i = 0; i != extra_vec.size(); i++) - { - string_tools::trim(extra_vec[i]); - if(!extra_vec[i].size()) - continue; - std::string buff = string_encoding::base64_decode(extra_vec[i]); - if(buff != "0") - m_extra_messages[i] = buff; - } - m_config_folder_path = boost::filesystem::path(command_line::get_arg(vm, arg_extra_messages)).parent_path().string(); - m_config = AUTO_VAL_INIT(m_config); - epee::serialization::load_t_from_json_file(m_config, m_config_folder_path + "/" + MINER_CONFIG_FILE_NAME); - MINFO("Loaded " << m_extra_messages.size() << " extra messages, current index " << m_config.current_extra_message_index); - } - - if(command_line::has_arg(vm, arg_start_mining)) - { - if(!cryptonote::get_account_address_from_str(m_mine_address, testnet, command_line::get_arg(vm, arg_start_mining))) - { - LOG_ERROR("Target account address " << command_line::get_arg(vm, arg_start_mining) << " has wrong format, starting daemon canceled"); - return false; - } - m_threads_total = 1; - m_do_mining = true; - if(command_line::has_arg(vm, arg_mining_threads)) - { - m_threads_total = command_line::get_arg(vm, arg_mining_threads); - } - } - - return true; - } - //----------------------------------------------------------------------------------------------------- - bool miner::is_mining() const - { - return !m_stop; - } - //----------------------------------------------------------------------------------------------------- - const account_public_address& miner::get_mining_address() const - { - return m_mine_address; - } - //----------------------------------------------------------------------------------------------------- - uint32_t miner::get_threads_count() const { - return m_threads_total; - } - //----------------------------------------------------------------------------------------------------- - bool miner::start(const account_public_address& adr, size_t threads_count, const boost::thread::attributes& attrs) - { - m_mine_address = adr; - m_threads_total = static_cast<uint32_t>(threads_count); - m_starter_nonce = crypto::rand<uint32_t>(); - CRITICAL_REGION_LOCAL(m_threads_lock); - if(is_mining()) - { - LOG_ERROR("Starting miner but it's already started"); - return false; - } - - if(!m_threads.empty()) - { - LOG_ERROR("Unable to start miner because there are active mining threads"); - return false; - } - - if(!m_template_no) - request_block_template();//lets update block template - - boost::interprocess::ipcdetail::atomic_write32(&m_stop, 0); - boost::interprocess::ipcdetail::atomic_write32(&m_thread_index, 0); - - for(size_t i = 0; i != threads_count; i++) - { - m_threads.push_back(boost::thread(attrs, boost::bind(&miner::worker_thread, this))); - } - - LOG_PRINT_L0("Mining has started with " << threads_count << " threads, good luck!" ); - return true; - } - //----------------------------------------------------------------------------------------------------- - uint64_t miner::get_speed() const - { - if(is_mining()) { - return m_current_hash_rate; - } - else { - return 0; - } - } - //----------------------------------------------------------------------------------------------------- - void miner::send_stop_signal() - { - boost::interprocess::ipcdetail::atomic_write32(&m_stop, 1); - } - //----------------------------------------------------------------------------------------------------- - bool miner::stop() - { - MTRACE("Miner has received stop signal"); - - if (!is_mining()) - { - MDEBUG("Not mining - nothing to stop" ); - return true; - } - - send_stop_signal(); - CRITICAL_REGION_LOCAL(m_threads_lock); - - for(boost::thread& th: m_threads) - th.join(); - - MINFO("Mining has been stopped, " << m_threads.size() << " finished" ); - m_threads.clear(); - return true; - } - //----------------------------------------------------------------------------------------------------- - bool miner::find_nonce_for_given_block(block& bl, const difficulty_type& diffic, uint64_t height) - { - for(; bl.nonce != std::numeric_limits<uint32_t>::max(); bl.nonce++) - { - crypto::hash h; - get_block_longhash(bl, h, height); - - if(check_hash(h, diffic)) - { - return true; - } - } - return false; - } - //----------------------------------------------------------------------------------------------------- - void miner::on_synchronized() - { - if(m_do_mining) - { - boost::thread::attributes attrs; - attrs.set_stack_size(THREAD_STACK_SIZE); - - start(m_mine_address, m_threads_total, attrs); - } - } - //----------------------------------------------------------------------------------------------------- - void miner::pause() - { - CRITICAL_REGION_LOCAL(m_miners_count_lock); - ++m_pausers_count; - if(m_pausers_count == 1 && is_mining()) - MDEBUG("MINING PAUSED"); - } - //----------------------------------------------------------------------------------------------------- - void miner::resume() - { - CRITICAL_REGION_LOCAL(m_miners_count_lock); - --m_pausers_count; - if(m_pausers_count < 0) - { - m_pausers_count = 0; - MERROR("Unexpected miner::resume() called"); - } - if(!m_pausers_count && is_mining()) - MDEBUG("MINING RESUMED"); - } - //----------------------------------------------------------------------------------------------------- - bool miner::worker_thread() - { - uint32_t th_local_index = boost::interprocess::ipcdetail::atomic_inc32(&m_thread_index); - MGINFO("Miner thread was started ["<< th_local_index << "]"); - MLOG_SET_THREAD_NAME(std::string("[miner ") + std::to_string(th_local_index) + "]"); - uint32_t nonce = m_starter_nonce + th_local_index; - uint64_t height = 0; - difficulty_type local_diff = 0; - uint32_t local_template_ver = 0; - block b; - slow_hash_allocate_state(); - while(!m_stop) - { - if(m_pausers_count)//anti split workaround - { - misc_utils::sleep_no_w(100); - continue; - } - - if(local_template_ver != m_template_no) - { - CRITICAL_REGION_BEGIN(m_template_lock); - b = m_template; - local_diff = m_diffic; - height = m_height; - CRITICAL_REGION_END(); - local_template_ver = m_template_no; - nonce = m_starter_nonce + th_local_index; - } - - if(!local_template_ver)//no any set_block_template call - { - LOG_PRINT_L2("Block template not set yet"); - epee::misc_utils::sleep_no_w(1000); - continue; - } - - b.nonce = nonce; - crypto::hash h; - get_block_longhash(b, h, height); - - if(check_hash(h, local_diff)) - { - //we lucky! - ++m_config.current_extra_message_index; - MGINFO_GREEN("Found block for difficulty: " << local_diff); - if(!m_phandler->handle_block_found(b)) - { - --m_config.current_extra_message_index; - }else - { - //success update, lets update config - if (!m_config_folder_path.empty()) - epee::serialization::store_t_to_json_file(m_config, m_config_folder_path + "/" + MINER_CONFIG_FILE_NAME); - } - } - nonce+=m_threads_total; - ++m_hashes; - } - slow_hash_free_state(); - MGINFO("Miner thread stopped ["<< th_local_index << "]"); - return true; - } - //----------------------------------------------------------------------------------------------------- -} - diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index e37ddec0d..f78f673c7 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -34,8 +34,8 @@ #include <vector> #include "tx_pool.h" -#include "cryptonote_format_utils.h" -#include "cryptonote_boost_serialization.h" +#include "cryptonote_tx_utils.h" +#include "cryptonote_basic/cryptonote_boost_serialization.h" #include "cryptonote_config.h" #include "blockchain.h" #include "common/boost_serialization_helper.h" @@ -59,8 +59,6 @@ namespace cryptonote // codebase. As it stands, it is at best nontrivial to test // whether or not changing these parameters (or adding new) // will work correctly. - size_t const TRANSACTION_SIZE_LIMIT_V1 = (((CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 * 125) / 100) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE); - size_t const TRANSACTION_SIZE_LIMIT_V2 = (((CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 125) / 100) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE); time_t const MIN_RELAY_TIME = (60 * 5); // only start re-relaying transactions after that many seconds time_t const MAX_RELAY_TIME = (60 * 60 * 4); // at most that many seconds between resends float const ACCEPT_THRESHOLD = 1.0f; @@ -78,6 +76,11 @@ namespace cryptonote { return amount * ACCEPT_THRESHOLD; } + + uint64_t get_transaction_size_limit(uint8_t version) + { + return get_min_block_size(version) * 125 / 100 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; + } } //--------------------------------------------------------------------------------- //--------------------------------------------------------------------------------- @@ -149,7 +152,7 @@ namespace cryptonote return false; } - size_t tx_size_limit = (version < 2 ? TRANSACTION_SIZE_LIMIT_V1 : TRANSACTION_SIZE_LIMIT_V2); + size_t tx_size_limit = get_transaction_size_limit(version); if (!kept_by_block && blob_size >= tx_size_limit) { LOG_PRINT_L1("transaction is too big: " << blob_size << " bytes, maximum size: " << tx_size_limit); @@ -255,6 +258,7 @@ namespace cryptonote m_txs_by_fee_and_receive_time.emplace(std::pair<double, std::time_t>(fee / (double)blob_size, receive_time), id); + MINFO("Transaction added to pool: txid " << id << " bytes: " << blob_size << " fee/byte: " << (fee / (double)blob_size)); return true; } //--------------------------------------------------------------------------------- @@ -419,6 +423,13 @@ namespace cryptonote txs.push_back(tx_vt.second.tx); } //------------------------------------------------------------------ + void tx_memory_pool::get_transaction_hashes(std::vector<crypto::hash>& txs) const + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + for(const auto& tx_vt: m_transactions) + txs.push_back(get_transaction_hash(tx_vt.second.tx)); + } + //------------------------------------------------------------------ //TODO: investigate whether boolean return is appropriate bool tx_memory_pool::get_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos) const { @@ -602,7 +613,7 @@ namespace cryptonote } //--------------------------------------------------------------------------------- //TODO: investigate whether boolean return is appropriate - bool tx_memory_pool::fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee, uint8_t version) + bool tx_memory_pool::fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee, uint64_t &expected_reward, uint8_t version) { // Warning: This function takes already_generated_ // coins as an argument and appears to do nothing @@ -610,7 +621,7 @@ namespace cryptonote CRITICAL_REGION_LOCAL(m_transactions_lock); - uint64_t best_coinbase = 0; + uint64_t best_coinbase = 0, coinbase = 0; total_size = 0; fee = 0; @@ -618,7 +629,9 @@ namespace cryptonote get_block_reward(median_size, total_size, already_generated_coins, best_coinbase, version); - size_t max_total_size = 2 * median_size - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; + size_t max_total_size_pre_v5 = (130 * median_size) / 100 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; + size_t max_total_size_v5 = 2 * median_size - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; + size_t max_total_size = version >= 5 ? max_total_size_v5 : max_total_size_pre_v5; std::unordered_set<crypto::key_image> k_images; LOG_PRINT_L2("Filling block template, median size " << median_size << ", " << m_txs_by_fee_and_receive_time.size() << " txes in the pool"); @@ -636,29 +649,49 @@ namespace cryptonote continue; } - // If we're getting lower coinbase tx, - // stop including more tx - uint64_t block_reward; - if(!get_block_reward(median_size, total_size + tx_it->second.blob_size, already_generated_coins, block_reward, version)) + // start using the optimal filling algorithm from v5 + if (version >= 5) { - LOG_PRINT_L2(" would exceed maximum block size"); - sorted_it++; - continue; + // If we're getting lower coinbase tx, + // stop including more tx + uint64_t block_reward; + if(!get_block_reward(median_size, total_size + tx_it->second.blob_size, already_generated_coins, block_reward, version)) + { + LOG_PRINT_L2(" would exceed maximum block size"); + sorted_it++; + continue; + } + coinbase = block_reward + fee + tx_it->second.fee; + if (coinbase < template_accept_threshold(best_coinbase)) + { + LOG_PRINT_L2(" would decrease coinbase to " << print_money(coinbase)); + sorted_it++; + continue; + } } - uint64_t coinbase = block_reward + fee + tx_it->second.fee; - if (coinbase < template_accept_threshold(best_coinbase)) + else { - LOG_PRINT_L2(" would decrease coinbase to " << print_money(coinbase)); - sorted_it++; - continue; + // If we've exceeded the penalty free size, + // stop including more tx + if (total_size > median_size) + { + LOG_PRINT_L2(" would exceed median block size"); + break; + } } // Skip transactions that are not ready to be // included into the blockchain or that are // missing key images - if (!is_transaction_ready_to_go(tx_it->second) || have_key_images(k_images, tx_it->second.tx)) + if (!is_transaction_ready_to_go(tx_it->second)) { - LOG_PRINT_L2(" not ready to go, or key images already seen"); + LOG_PRINT_L2(" not ready to go"); + sorted_it++; + continue; + } + if (have_key_images(k_images, tx_it->second.tx)) + { + LOG_PRINT_L2(" key images already seen"); sorted_it++; continue; } @@ -672,6 +705,7 @@ namespace cryptonote LOG_PRINT_L2(" added, new block size " << total_size << "/" << max_total_size << ", coinbase " << print_money(best_coinbase)); } + expected_reward = best_coinbase; LOG_PRINT_L2("Block template filled with " << bl.tx_hashes.size() << " txes, size " << total_size << "/" << max_total_size << ", coinbase " << print_money(best_coinbase) << " (including " << print_money(fee) << " in fees)"); @@ -682,15 +716,24 @@ namespace cryptonote { 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); + size_t tx_size_limit = get_transaction_size_limit(version); for (auto it = m_transactions.begin(); it != m_transactions.end(); ) { + bool remove = false; + const crypto::hash &txid = get_transaction_hash(it->second.tx); if (it->second.blob_size >= tx_size_limit) { - LOG_PRINT_L1("Transaction " << get_transaction_hash(it->second.tx) << " is too big (" << it->second.blob_size << " bytes), removing it from pool"); + LOG_PRINT_L1("Transaction " << txid << " is too big (" << it->second.blob_size << " bytes), removing it from pool"); + remove = true; + } + else if (m_blockchain.have_tx(txid)) { + LOG_PRINT_L1("Transaction " << txid << " is in the blockchain, removing it from pool"); + remove = true; + } + if (remove) { remove_transaction_keyimages(it->second.tx); - auto sorted_it = find_tx_in_sorted_container(it->first); + auto sorted_it = find_tx_in_sorted_container(txid); if (sorted_it == m_txs_by_fee_and_receive_time.end()) { - LOG_PRINT_L1("Removing tx " << it->first << " from tx pool, but it was not found in the sorted txs container!"); + LOG_PRINT_L1("Removing tx " << txid << " from tx pool, but it was not found in the sorted txs container!"); } else { diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 2712f75bb..f68bc0bb9 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -41,8 +41,8 @@ #include "string_tools.h" #include "syncobj.h" #include "math_helper.h" -#include "cryptonote_basic_impl.h" -#include "verification_context.h" +#include "cryptonote_basic/cryptonote_basic_impl.h" +#include "cryptonote_basic/verification_context.h" #include "crypto/hash.h" #include "rpc/core_rpc_server_commands_defs.h" @@ -220,11 +220,12 @@ namespace cryptonote * @param already_generated_coins the current total number of coins "minted" * @param total_size return-by-reference the total size of the new block * @param fee return-by-reference the total of fees from the included transactions + * @param expected_reward return-by-reference the total reward awarded to the miner finding this block, including transaction fees * @param version hard fork version to use for consensus rules * * @return true */ - bool fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee, uint8_t version); + bool fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee, uint64_t &expected_reward, uint8_t version); /** * @brief get a list of all transactions in the pool @@ -234,6 +235,13 @@ namespace cryptonote void get_transactions(std::list<transaction>& txs) const; /** + * @brief get a list of all transaction hashes in the pool + * + * @param txs return-by-reference the list of transactions + */ + void get_transaction_hashes(std::vector<crypto::hash>& txs) const; + + /** * @brief get information about all transactions and key images in the pool * * see documentation on tx_info and spent_key_image_info for more details diff --git a/src/cryptonote_protocol/CMakeLists.txt b/src/cryptonote_protocol/CMakeLists.txt index 65473b117..4ce380a48 100644 --- a/src/cryptonote_protocol/CMakeLists.txt +++ b/src/cryptonote_protocol/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -35,6 +35,8 @@ source_group(cryptonote_protocol FILES ${CRYPTONOTE_PROTOCOL}) #monero_private_headers(cryptonote_protocol ${CRYPTONOTE_PROTOCOL}) monero_add_library(cryptonote_protocol ${CRYPTONOTE_PROTOCOL}) target_link_libraries(cryptonote_protocol + PUBLIC + p2p PRIVATE ${EXTRA_LIBRARIES}) add_dependencies(cryptonote_protocol diff --git a/src/cryptonote_protocol/blobdatatype.h b/src/cryptonote_protocol/blobdatatype.h index 17285ee1c..2d12a84af 100644 --- a/src/cryptonote_protocol/blobdatatype.h +++ b/src/cryptonote_protocol/blobdatatype.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h index 7adc69080..fd5b980b8 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_defs.h +++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -32,7 +32,7 @@ #include <list> #include "serialization/keyvalue_serialization.h" -#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_basic.h" #include "cryptonote_protocol/blobdatatype.h" namespace cryptonote { @@ -257,16 +257,16 @@ namespace cryptonote struct request { - block_complete_entry b; + crypto::hash block_hash; uint64_t current_blockchain_height; std::vector<size_t> missing_tx_indices; uint32_t hop; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(b) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(missing_tx_indices) - KV_SERIALIZE(hop) - KV_SERIALIZE(current_blockchain_height) + KV_SERIALIZE_VAL_POD_AS_BLOB(block_hash) + KV_SERIALIZE(current_blockchain_height) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(missing_tx_indices) + KV_SERIALIZE(hop) END_KV_SERIALIZE_MAP() }; }; diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp b/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp index 09c202e79..e31276031 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp +++ b/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp @@ -2,7 +2,7 @@ /// @author rfree (current maintainer in monero.cc project) /// @brief This is the place to implement our handlers for protocol network actions, e.g. for ratelimit for download-requests -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -122,7 +122,7 @@ cryptonote_protocol_handler_base::~cryptonote_protocol_handler_base() { void cryptonote_protocol_handler_base::handler_request_blocks_history(std::list<crypto::hash>& ids) { using namespace epee::net_utils; MDEBUG("### ~~~RRRR~~~~ ### sending request (type 2), limit = " << ids.size()); - MWARNING("RATE LIMIT NOT IMPLEMENTED HERE YET (download at unlimited speed?)"); + MDEBUG("RATE LIMIT NOT IMPLEMENTED HERE YET (download at unlimited speed?)"); // TODO } diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index 4b2de39b9..9d8bc43c2 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -2,7 +2,7 @@ /// @author rfree (current maintainer/user in monero.cc project - most of code is from CryptoNote) /// @brief This is the orginal cryptonote protocol network-events handler, modified by us -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -42,9 +42,9 @@ #include "warnings.h" #include "cryptonote_protocol_defs.h" #include "cryptonote_protocol_handler_common.h" -#include "cryptonote_core/connection_context.h" -#include "cryptonote_core/cryptonote_stat_info.h" -#include "cryptonote_core/verification_context.h" +#include "cryptonote_basic/connection_context.h" +#include "cryptonote_basic/cryptonote_stat_info.h" +#include "cryptonote_basic/verification_context.h" // #include <netinet/in.h> #include <boost/circular_buffer.hpp> @@ -135,6 +135,7 @@ namespace cryptonote std::atomic<bool> m_synchronized; bool m_one_request = true; std::atomic<bool> m_stopping; + epee::critical_section m_sync_lock; boost::mutex m_buffer_mutex; double get_avg_block_size(); diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 1309ff742..79578a34e 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -2,7 +2,7 @@ /// @author rfree (current maintainer/user in monero.cc project - most of code is from CryptoNote) /// @brief This is the orginal cryptonote protocol network-events handler, modified by us -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -39,7 +39,7 @@ #include <list> #include <unordered_map> -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_format_utils.h" #include "profile_tools.h" #include "../../src/p2p/network_throttle-detail.hpp" @@ -265,19 +265,24 @@ namespace cryptonote if(context.m_state == cryptonote_connection_context::state_synchronizing) return true; + uint64_t target = m_core.get_target_blockchain_height(); + if (target == 0) + target = m_core.get_current_blockchain_height(); + if(m_core.have_block(hshd.top_id)) { context.m_state = cryptonote_connection_context::state_normal; - if(is_inital) + if(is_inital && target == m_core.get_current_blockchain_height()) on_connection_synchronized(); return true; } + if (hshd.current_height > target) + { /* As I don't know if accessing hshd from core could be a good practice, I prefer pushing target height to the core at the same time it is pushed to the user. Nz. */ m_core.set_target_blockchain_height(static_cast<int64_t>(hshd.current_height)); - int64_t diff = static_cast<int64_t>(hshd.current_height) - static_cast<int64_t>(m_core.get_current_blockchain_height()); int64_t max_block_height = max(static_cast<int64_t>(hshd.current_height),static_cast<int64_t>(m_core.get_current_blockchain_height())); int64_t last_block_v1 = 1009826; @@ -286,6 +291,7 @@ namespace cryptonote << " [Your node is " << std::abs(diff) << " blocks (" << ((abs(diff) - diff_v2) / (24 * 60 * 60 / DIFFICULTY_TARGET_V1)) + (diff_v2 / (24 * 60 * 60 / DIFFICULTY_TARGET_V2)) << " days) " << (0 <= diff ? std::string("behind") : std::string("ahead")) << "] " << ENDL << "SYNCHRONIZATION started"); + } LOG_PRINT_L1("Remote blockchain height: " << hshd.current_height << ", id: " << hshd.top_id); context.m_state = cryptonote_connection_context::state_synchronizing; context.m_remote_blockchain_height = hshd.current_height; @@ -367,7 +373,7 @@ namespace cryptonote template<class t_core> int t_cryptonote_protocol_handler<t_core>::handle_notify_new_fluffy_block(int command, NOTIFY_NEW_FLUFFY_BLOCK::request& arg, cryptonote_connection_context& context) { - MLOG_P2P_MESSAGE("Received NOTIFY_NEW_FLUFFY_BLOCK (hop " << arg.hop << ", " << arg.b.txs.size() << " txes)"); + MLOG_P2P_MESSAGE("Received NOTIFY_NEW_FLUFFY_BLOCK (height " << arg.current_blockchain_height << ", hop " << arg.hop << ", " << arg.b.txs.size() << " txes)"); if(context.m_state != cryptonote_connection_context::state_normal) return 1; @@ -377,7 +383,7 @@ namespace cryptonote transaction miner_tx; if(parse_and_validate_block_from_blob(arg.b.block, new_block)) { - // This is a seccond notification, we must have asked for some missing tx + // This is a second notification, we must have asked for some missing tx if(!context.m_requested_objects.empty()) { // What we asked for != to what we received .. @@ -475,6 +481,7 @@ namespace cryptonote // sent in our pool, so don't verify again.. if(!m_core.get_pool_transaction(tx_hash, tx)) { + MDEBUG("Incoming tx " << tx_hash << " not in pool, adding"); cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); if(!m_core.handle_incoming_tx(tx_blob, tvc, true, true, false) || tvc.m_verifivation_failed) { @@ -496,9 +503,9 @@ namespace cryptonote { LOG_ERROR_CCONTEXT ( - "sent wrong tx: failed to parse and validate transaction: \r\n" + "sent wrong tx: failed to parse and validate transaction: " << epee::string_tools::buff_to_hex_nodelimer(tx_blob) - << "\r\n dropping connection" + << ", dropping connection" ); m_p2p->drop_connection(context); @@ -535,7 +542,28 @@ namespace cryptonote } else { - need_tx_indices.push_back(tx_idx); + std::vector<crypto::hash> tx_ids; + std::list<transaction> txes; + std::list<crypto::hash> missing; + tx_ids.push_back(tx_hash); + if (m_core.get_transactions(tx_ids, txes, missing) && missing.empty()) + { + if (txes.size() == 1) + { + have_tx.push_back(tx_to_blob(txes.front())); + } + else + { + MERROR("1 tx requested, none not found, but " << txes.size() << " returned"); + m_core.resume_mine(); + return 1; + } + } + else + { + MDEBUG("Tx " << tx_hash << " not found in pool"); + need_tx_indices.push_back(tx_idx); + } } ++tx_idx; @@ -544,8 +572,11 @@ namespace cryptonote if(!need_tx_indices.empty()) // drats, we don't have everything.. { // request non-mempool txs + MDEBUG("We are missing " << need_tx_indices.size() << " txes for this fluffy block"); + for (auto txidx: need_tx_indices) + MDEBUG(" tx " << new_block.tx_hashes[txidx]); NOTIFY_REQUEST_FLUFFY_MISSING_TX::request missing_tx_req; - missing_tx_req.b = arg.b; + missing_tx_req.block_hash = get_block_hash(new_block); missing_tx_req.hop = arg.hop; missing_tx_req.current_blockchain_height = arg.current_blockchain_height; missing_tx_req.missing_tx_indices = std::move(need_tx_indices); @@ -555,6 +586,8 @@ namespace cryptonote } else // whoo-hoo we've got em all .. { + MDEBUG("We have all needed txes for this fluffy block"); + block_complete_entry b; b.block = arg.b.block; b.txs = have_tx; @@ -581,7 +614,7 @@ namespace cryptonote NOTIFY_NEW_BLOCK::request reg_arg = AUTO_VAL_INIT(reg_arg); reg_arg.hop = arg.hop; reg_arg.current_blockchain_height = arg.current_blockchain_height; - reg_arg.b.block = b.block; + reg_arg.b = b; relay_block(reg_arg, context); } else if( bvc.m_marked_as_orphaned ) @@ -598,9 +631,9 @@ namespace cryptonote { LOG_ERROR_CCONTEXT ( - "sent wrong block: failed to parse and validate block: \r\n" + "sent wrong block: failed to parse and validate block: " << epee::string_tools::buff_to_hex_nodelimer(arg.b.block) - << "\r\n dropping connection" + << ", dropping connection" ); m_core.resume_mine(); @@ -615,34 +648,32 @@ namespace cryptonote template<class t_core> int t_cryptonote_protocol_handler<t_core>::handle_request_fluffy_missing_tx(int command, NOTIFY_REQUEST_FLUFFY_MISSING_TX::request& arg, cryptonote_connection_context& context) { - MLOG_P2P_MESSAGE("Received NOTIFY_REQUEST_FLUFFY_MISSING_TX (" << arg.missing_tx_indices.size() << " txes)"); + MLOG_P2P_MESSAGE("Received NOTIFY_REQUEST_FLUFFY_MISSING_TX (" << arg.missing_tx_indices.size() << " txes), block hash " << arg.block_hash); - std::list<block> local_blocks; - std::list<transaction> local_txs; - if(!m_core.get_blocks(arg.current_blockchain_height - 1, 1, local_blocks, local_txs)) - { + std::list<std::pair<cryptonote::blobdata, block>> local_blocks; + std::list<cryptonote::blobdata> local_txs; - LOG_ERROR_CCONTEXT - ( - "Failed to handle request NOTIFY_REQUEST_FLUFFY_MISSING_TX" - << ", get_blocks( start_offset = " << (arg.current_blockchain_height - 1) << " ) failed" - << ", dropping connection" - ); - + block b; + if (!m_core.get_block_by_hash(arg.block_hash, b)) + { + LOG_ERROR_CCONTEXT("failed to find block: " << arg.block_hash << ", dropping connection"); m_p2p->drop_connection(context); - return 1; + return 1; } + for (auto txidx: arg.missing_tx_indices) + MDEBUG(" tx " << b.tx_hashes[txidx]); + + std::vector<crypto::hash> txids; NOTIFY_NEW_FLUFFY_BLOCK::request fluffy_response; - fluffy_response.b = arg.b; - fluffy_response.current_blockchain_height = m_core.get_current_blockchain_height(); + fluffy_response.b.block = t_serializable_object_to_blob(b); + fluffy_response.current_blockchain_height = arg.current_blockchain_height; fluffy_response.hop = arg.hop; - size_t local_txs_count = local_txs.size(); for(auto& tx_idx: arg.missing_tx_indices) { - if(tx_idx < local_txs_count) + if(tx_idx < b.tx_hashes.size()) { - fluffy_response.b.txs.push_back(t_serializable_object_to_blob( *(std::next(local_txs.begin(), tx_idx)) )); + txids.push_back(b.tx_hashes[tx_idx]); } else { @@ -650,7 +681,8 @@ namespace cryptonote ( "Failed to handle request NOTIFY_REQUEST_FLUFFY_MISSING_TX" << ", request is asking for a tx whose index is out of bounds " - << ", tx index = " << tx_idx << ", block_height = " << arg.current_blockchain_height + << ", tx index = " << tx_idx << ", block tx count " << b.tx_hashes.size() + << ", block_height = " << arg.current_blockchain_height << ", dropping connection" ); @@ -658,7 +690,29 @@ namespace cryptonote return 1; } } - + + std::list<cryptonote::transaction> txs; + std::list<crypto::hash> missed; + if (!m_core.get_transactions(txids, txs, missed)) + { + LOG_ERROR_CCONTEXT("Failed to handle request NOTIFY_REQUEST_FLUFFY_MISSING_TX, " + << "failed to get requested transactions"); + m_p2p->drop_connection(context); + return 1; + } + if (!missed.empty() || txs.size() != txids.size()) + { + LOG_ERROR_CCONTEXT("Failed to handle request NOTIFY_REQUEST_FLUFFY_MISSING_TX, " + << missed.size() << " requested transactions not found" << ", dropping connection"); + m_p2p->drop_connection(context); + return 1; + } + + for(auto& tx: txs) + { + fluffy_response.b.txs.push_back(t_serializable_object_to_blob(tx)); + } + LOG_PRINT_CCONTEXT_L2 ( "-->>NOTIFY_RESPONSE_FLUFFY_MISSING_TX: " @@ -790,6 +844,8 @@ namespace cryptonote context.m_remote_blockchain_height = arg.current_blockchain_height; size_t count = 0; + std::vector<crypto::hash> block_hashes; + block_hashes.reserve(arg.blocks.size()); for(const block_complete_entry& block_entry: arg.blocks) { if (m_stopping) @@ -801,15 +857,16 @@ namespace cryptonote block b; if(!parse_and_validate_block_from_blob(block_entry.block, b)) { - LOG_ERROR_CCONTEXT("sent wrong block: failed to parse and validate block: \r\n" - << epee::string_tools::buff_to_hex_nodelimer(block_entry.block) << "\r\n dropping connection"); + LOG_ERROR_CCONTEXT("sent wrong block: failed to parse and validate block: " + << epee::string_tools::buff_to_hex_nodelimer(block_entry.block) << ", dropping connection"); m_p2p->drop_connection(context); return 1; } //to avoid concurrency in core between connections, suspend connections which delivered block later then first one + const crypto::hash block_hash = get_block_hash(b); if(count == 2) { - if(m_core.have_block(get_block_hash(b))) + if(m_core.have_block(block_hash)) { context.m_state = cryptonote_connection_context::state_idle; context.m_needed_objects.clear(); @@ -819,7 +876,7 @@ namespace cryptonote } } - auto req_it = context.m_requested_objects.find(get_block_hash(b)); + auto req_it = context.m_requested_objects.find(block_hash); if(req_it == context.m_requested_objects.end()) { LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << epee::string_tools::pod_to_hex(get_blob_hash(block_entry.block)) @@ -836,6 +893,7 @@ namespace cryptonote } context.m_requested_objects.erase(req_it); + block_hashes.push_back(block_hash); } if(context.m_requested_objects.size()) @@ -848,17 +906,44 @@ namespace cryptonote { - m_core.pause_mine(); - epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler( - boost::bind(&t_core::resume_mine, &m_core)); - MLOG_YELLOW(el::Level::Debug, "Got NEW BLOCKS inside of " << __FUNCTION__ << ": size: " << arg.blocks.size()); if (m_core.get_test_drop_download() && m_core.get_test_drop_download_height()) { // DISCARD BLOCKS for testing - uint64_t previous_height = m_core.get_current_blockchain_height(); + // we lock all the rest to avoid having multiple connections redo a lot + // of the same work, and one of them doing it for nothing: subsequent + // connections will wait until the current one's added its blocks, then + // will add any extra it has, if any + CRITICAL_REGION_LOCAL(m_sync_lock); + + m_core.pause_mine(); + epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler( + boost::bind(&t_core::resume_mine, &m_core)); + + const uint64_t previous_height = m_core.get_current_blockchain_height(); + + // dismiss what another connection might already have done (likely everything) + uint64_t top_height; + crypto::hash top_hash; + if (m_core.get_blockchain_top(top_height, top_hash)) { + uint64_t dismiss = 1; + for (const auto &h: block_hashes) { + if (top_hash == h) { + LOG_DEBUG_CC(context, "Found current top block in synced blocks, dismissing " + << dismiss << "/" << arg.blocks.size() << " blocks"); + while (dismiss--) + arg.blocks.pop_front(); + break; + } + ++dismiss; + } + } + + if (arg.blocks.empty()) + goto skip; m_core.prepare_handle_incoming_blocks(arg.blocks); + for(const block_complete_entry& block_entry: arg.blocks) { if (m_stopping) @@ -875,7 +960,7 @@ namespace cryptonote m_core.handle_incoming_tx(tx_blob, tvc, true, true, false); if(tvc.m_verifivation_failed) { - LOG_ERROR_CCONTEXT("transaction verification failed on NOTIFY_RESPONSE_GET_OBJECTS, \r\ntx_id = " + LOG_ERROR_CCONTEXT("transaction verification failed on NOTIFY_RESPONSE_GET_OBJECTS, tx_id = " << epee::string_tools::pod_to_hex(get_blob_hash(tx_blob)) << ", dropping connection"); m_p2p->drop_connection(context); m_core.cleanup_handle_incoming_blocks(); @@ -922,6 +1007,7 @@ namespace cryptonote } +skip: request_missing_objects(context, true); return 1; } @@ -1071,9 +1157,9 @@ namespace cryptonote context.m_last_response_height = arg.start_height + arg.m_block_ids.size()-1; if(context.m_last_response_height > context.m_remote_blockchain_height) { - LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_CHAIN_ENTRY, with \r\nm_total_height=" << arg.total_height - << "\r\nm_start_height=" << arg.start_height - << "\r\nm_block_ids.size()=" << arg.m_block_ids.size()); + LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_CHAIN_ENTRY, with m_total_height=" << arg.total_height + << ", m_start_height=" << arg.start_height + << ", m_block_ids.size()=" << arg.m_block_ids.size()); m_p2p->drop_connection(context); } @@ -1110,12 +1196,12 @@ namespace cryptonote { if(m_core.get_testnet() && (support_flags & P2P_SUPPORT_FLAG_FLUFFY_BLOCKS)) { - MDEBUG("PEER SUPPORTS FLUFFY BLOCKS - RELAYING THIN/COMPACT WHATEVER BLOCK"); + LOG_DEBUG_CC(context, "PEER SUPPORTS FLUFFY BLOCKS - RELAYING THIN/COMPACT WHATEVER BLOCK"); fluffyConnections.push_back(context.m_connection_id); } else { - MDEBUG("PEER DOESN'T SUPPORT FLUFFY BLOCKS - RELAYING FULL BLOCK"); + LOG_DEBUG_CC(context, "PEER DOESN'T SUPPORT FLUFFY BLOCKS - RELAYING FULL BLOCK"); fullConnections.push_back(context.m_connection_id); } } diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h index 06061f5d0..1163a0fe8 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -32,7 +32,7 @@ #include "p2p/net_node_common.h" #include "cryptonote_protocol/cryptonote_protocol_defs.h" -#include "cryptonote_core/connection_context.h" +#include "cryptonote_basic/connection_context.h" namespace cryptonote { /************************************************************************/ diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt index 1b6363f7b..649823a59 100644 --- a/src/daemon/CMakeLists.txt +++ b/src/daemon/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -29,9 +29,9 @@ set(blocksdat "") if(PER_BLOCK_CHECKPOINT) if(APPLE) - add_custom_command(OUTPUT blocksdat.o COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*) + add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*) else() - add_custom_command(OUTPUT blocksdat.o COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat) + add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat) endif() set(blocksdat "blocksdat.o") endif() diff --git a/src/daemon/command_line_args.h b/src/daemon/command_line_args.h index cb9fb6014..8eb3db195 100644 --- a/src/daemon/command_line_args.h +++ b/src/daemon/command_line_args.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index 27f9d0fd7..a7caeeffc 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -37,11 +37,11 @@ namespace daemonize { t_command_parser_executor::t_command_parser_executor( uint32_t ip , uint16_t port - , const std::string &user_agent + , const boost::optional<tools::login>& login , bool is_rpc , cryptonote::core_rpc_server* rpc_server ) - : m_executor(ip, port, user_agent, is_rpc, rpc_server) + : m_executor(ip, port, login, is_rpc, rpc_server) {} bool t_command_parser_executor::print_peer_list(const std::vector<std::string>& args) @@ -51,6 +51,13 @@ bool t_command_parser_executor::print_peer_list(const std::vector<std::string>& return m_executor.print_peer_list(); } +bool t_command_parser_executor::print_peer_list_stats(const std::vector<std::string>& args) +{ + if (!args.empty()) return false; + + return m_executor.print_peer_list_stats(); +} + bool t_command_parser_executor::save_blockchain(const std::vector<std::string>& args) { if (!args.empty()) return false; @@ -271,17 +278,30 @@ bool t_command_parser_executor::start_mining(const std::vector<std::string>& arg if(testnet) std::cout << "Mining to a testnet address, make sure this is intentional!" << std::endl; uint64_t threads_count = 1; - if(args.size() > 2) + bool do_background_mining = false; + bool ignore_battery = false; + if(args.size() > 4) { return false; } - else if(args.size() == 2) + + if(args.size() == 4) + { + ignore_battery = args[3] == "true"; + } + + if(args.size() >= 3) + { + do_background_mining = args[2] == "true"; + } + + if(args.size() >= 2) { bool ok = epee::string_tools::get_xtype_from_string(threads_count, args[1]); threads_count = (ok && 0 < threads_count) ? threads_count : 1; } - m_executor.start_mining(adr, threads_count, testnet); + m_executor.start_mining(adr, threads_count, testnet, do_background_mining, ignore_battery); return true; } @@ -532,4 +552,30 @@ bool t_command_parser_executor::print_blockchain_dynamic_stats(const std::vector return m_executor.print_blockchain_dynamic_stats(nblocks); } +bool t_command_parser_executor::update(const std::vector<std::string>& args) +{ + if(args.size() != 1) + { + std::cout << "Exactly one parameter is needed: check, download, or update" << std::endl; + return false; + } + + return m_executor.update(args.front()); +} + +bool t_command_parser_executor::relay_tx(const std::vector<std::string>& args) +{ + if (args.size() != 1) return false; + + std::string txid; + crypto::hash hash; + if (!parse_hash256(args[0], hash)) + { + std::cout << "failed to parse tx id" << std::endl; + return true; + } + txid = args[0]; + return m_executor.relay_tx(txid); +} + } // namespace daemonize diff --git a/src/daemon/command_parser_executor.h b/src/daemon/command_parser_executor.h index 15293ade9..a453553f1 100644 --- a/src/daemon/command_parser_executor.h +++ b/src/daemon/command_parser_executor.h @@ -6,7 +6,7 @@ */ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -36,7 +36,10 @@ #pragma once +#include <boost/optional/optional_fwd.hpp> + #include "daemon/rpc_command_executor.h" +#include "common/common_fwd.h" #include "rpc/core_rpc_server.h" namespace daemonize { @@ -49,13 +52,15 @@ public: t_command_parser_executor( uint32_t ip , uint16_t port - , const std::string &user_agent + , const boost::optional<tools::login>& login , bool is_rpc , cryptonote::core_rpc_server* rpc_server = NULL ); bool print_peer_list(const std::vector<std::string>& args); + bool print_peer_list_stats(const std::vector<std::string>& args); + bool save_blockchain(const std::vector<std::string>& args); bool show_hash_rate(const std::vector<std::string>& args); @@ -125,6 +130,10 @@ public: bool alt_chain_info(const std::vector<std::string>& args); bool print_blockchain_dynamic_stats(const std::vector<std::string>& args); + + bool update(const std::vector<std::string>& args); + + bool relay_tx(const std::vector<std::string>& args); }; } // namespace daemonize diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp index 95fd3178c..21f550a85 100644 --- a/src/daemon/command_server.cpp +++ b/src/daemon/command_server.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -40,11 +40,11 @@ namespace p = std::placeholders; t_command_server::t_command_server( uint32_t ip , uint16_t port - , const std::string &user_agent + , const boost::optional<tools::login>& login , bool is_rpc , cryptonote::core_rpc_server* rpc_server ) - : m_parser(ip, port, user_agent, is_rpc, rpc_server) + : m_parser(ip, port, login, is_rpc, rpc_server) , m_command_lookup() , m_is_rpc(is_rpc) { @@ -69,6 +69,11 @@ t_command_server::t_command_server( , "Print peer list" ); m_command_lookup.set_handler( + "print_pl_stats" + , std::bind(&t_command_parser_executor::print_peer_list_stats, &m_parser, p::_1) + , "Print peer list stats" + ); + m_command_lookup.set_handler( "print_cn" , std::bind(&t_command_parser_executor::print_connections, &m_parser, p::_1) , "Print connections" @@ -96,7 +101,7 @@ t_command_server::t_command_server( m_command_lookup.set_handler( "start_mining" , std::bind(&t_command_parser_executor::start_mining, &m_parser, p::_1) - , "Start mining for specified address, start_mining <addr> [<threads>], default 1 thread" + , "Start mining for specified address, start_mining <addr> [<threads>] [do_background_mining] [ignore_battery], default 1 thread, no background mining" ); m_command_lookup.set_handler( "stop_mining" @@ -238,6 +243,16 @@ t_command_server::t_command_server( , std::bind(&t_command_parser_executor::print_blockchain_dynamic_stats, &m_parser, p::_1) , "Print information about current blockchain dynamic state" ); + m_command_lookup.set_handler( + "update" + , std::bind(&t_command_parser_executor::update, &m_parser, p::_1) + , "subcommands: check (check if an update is available), download (download it is there is), update (not implemented)" + ); + m_command_lookup.set_handler( + "relay_tx" + , std::bind(&t_command_parser_executor::relay_tx, &m_parser, p::_1) + , "Relay a given transaction by its txid" + ); } bool t_command_server::process_command_str(const std::string& cmd) diff --git a/src/daemon/command_server.h b/src/daemon/command_server.h index fb1702aae..476b75141 100644 --- a/src/daemon/command_server.h +++ b/src/daemon/command_server.h @@ -9,7 +9,7 @@ Passing RPC commands: */ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -39,6 +39,8 @@ Passing RPC commands: #pragma once +#include <boost/optional/optional_fwd.hpp> +#include "common/common_fwd.h" #include "console_handler.h" #include "daemon/command_parser_executor.h" @@ -54,7 +56,7 @@ public: t_command_server( uint32_t ip , uint16_t port - , const std::string &user_agent + , const boost::optional<tools::login>& login , bool is_rpc = true , cryptonote::core_rpc_server* rpc_server = NULL ); diff --git a/src/daemon/core.h b/src/daemon/core.h index 23f7a9f63..9e6ff5e29 100644 --- a/src/daemon/core.h +++ b/src/daemon/core.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -45,7 +45,6 @@ public: static void init_options(boost::program_options::options_description & option_spec) { cryptonote::core::init_options(option_spec); - cryptonote::miner::init_options(option_spec); } private: typedef cryptonote::t_cryptonote_protocol_handler<cryptonote::core> t_protocol_raw; diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 287c30cb4..241cb3883 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -33,6 +33,7 @@ #include "misc_log_ex.h" #include "daemon/daemon.h" +#include "common/password.h" #include "common/util.h" #include "daemon/core.h" #include "daemon/p2p.h" @@ -123,22 +124,24 @@ bool t_daemon::run(bool interactive) return false; mp_internals->rpc.run(); - daemonize::t_command_server* rpc_commands; + std::unique_ptr<daemonize::t_command_server> rpc_commands; if (interactive) { - rpc_commands = new daemonize::t_command_server(0, 0, "", false, mp_internals->rpc.get_server()); + // The first three variables are not used when the fourth is false + rpc_commands.reset(new daemonize::t_command_server(0, 0, boost::none, false, mp_internals->rpc.get_server())); rpc_commands->start_handling(std::bind(&daemonize::t_daemon::stop_p2p, this)); } mp_internals->p2p.run(); // blocks until p2p goes down - if (interactive) + if (rpc_commands) { rpc_commands->stop_handling(); } mp_internals->rpc.stop(); + mp_internals->core.get().get_miner().stop(); MGINFO("Node stopped."); return true; } @@ -160,6 +163,7 @@ void t_daemon::stop() { throw std::runtime_error{"Can't stop stopped daemon"}; } + mp_internals->core.get().get_miner().stop(); mp_internals->p2p.stop(); mp_internals->rpc.stop(); mp_internals.reset(nullptr); // Ensure resources are cleaned up before we return diff --git a/src/daemon/daemon.h b/src/daemon/daemon.h index c8fae5c28..2b9f18669 100644 --- a/src/daemon/daemon.h +++ b/src/daemon/daemon.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/daemon/executor.cpp b/src/daemon/executor.cpp index ac5803cfb..6130bfa28 100644 --- a/src/daemon/executor.cpp +++ b/src/daemon/executor.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -63,6 +63,13 @@ namespace daemonize return t_daemon{vm}; } + bool t_executor::run_non_interactive( + boost::program_options::variables_map const & vm + ) + { + return t_daemon{vm}.run(false); + } + bool t_executor::run_interactive( boost::program_options::variables_map const & vm ) diff --git a/src/daemon/executor.h b/src/daemon/executor.h index a6b47b93d..137e7209c 100644 --- a/src/daemon/executor.h +++ b/src/daemon/executor.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -56,6 +56,10 @@ namespace daemonize boost::program_options::variables_map const & vm ); + bool run_non_interactive( + boost::program_options::variables_map const & vm + ); + bool run_interactive( boost::program_options::variables_map const & vm ); diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp index e08065ccd..19dd02171 100644 --- a/src/daemon/main.cpp +++ b/src/daemon/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -30,9 +30,10 @@ #include "common/command_line.h" #include "common/scoped_message_writer.h" +#include "common/password.h" #include "common/util.h" #include "cryptonote_core/cryptonote_core.h" -#include "cryptonote_core/miner.h" +#include "cryptonote_basic/miner.h" #include "daemon/command_server.h" #include "daemon/daemon.h" #include "daemon/executor.h" @@ -40,6 +41,7 @@ #include "misc_log_ex.h" #include "p2p/net_node.h" #include "rpc/core_rpc_server.h" +#include "rpc/rpc_args.h" #include "daemon/command_line_args.h" #include "blockchain_db/db_types.h" @@ -166,7 +168,6 @@ int main(int argc, char const * argv[]) // Create data dir if it doesn't exist boost::filesystem::path data_dir = boost::filesystem::absolute( command_line::get_arg(vm, data_dir_arg)); - tools::create_directories_if_necessary(data_dir.string()); // FIXME: not sure on windows implementation default, needs further review //bf::path relative_path_base = daemonizer::get_relative_path_base(vm); @@ -214,19 +215,22 @@ int main(int argc, char const * argv[]) mlog_set_log(command_line::get_arg(vm, daemon_args::arg_log_level).c_str()); } + // after logs initialized + tools::create_directories_if_necessary(data_dir.string()); + // If there are positional options, we're running a daemon command { auto command = command_line::get_arg(vm, daemon_args::arg_command); if (command.size()) { - auto rpc_ip_str = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_rpc_bind_ip); + const cryptonote::rpc_args::descriptors arg{}; + auto rpc_ip_str = command_line::get_arg(vm, arg.rpc_bind_ip); auto rpc_port_str = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_rpc_bind_port); if (testnet_mode) { rpc_port_str = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_testnet_rpc_bind_port); } - auto user_agent = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_user_agent); uint32_t rpc_ip; uint16_t rpc_port; @@ -241,7 +245,20 @@ int main(int argc, char const * argv[]) return 1; } - daemonize::t_command_server rpc_commands{rpc_ip, rpc_port, user_agent}; + boost::optional<tools::login> login{}; + if (command_line::has_arg(vm, arg.rpc_login)) + { + login = tools::login::parse( + command_line::get_arg(vm, arg.rpc_login), false, "Daemon client password" + ); + if (!login) + { + std::cerr << "Failed to obtain password" << std::endl; + return 1; + } + } + + daemonize::t_command_server rpc_commands{rpc_ip, rpc_port, std::move(login)}; if (rpc_commands.process_command_vec(command)) { return 0; diff --git a/src/daemon/p2p.h b/src/daemon/p2p.h index f29c2d822..309eb7453 100644 --- a/src/daemon/p2p.h +++ b/src/daemon/p2p.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/daemon/protocol.h b/src/daemon/protocol.h index 0b0f95988..fc5edbcaa 100644 --- a/src/daemon/protocol.h +++ b/src/daemon/protocol.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -79,7 +79,6 @@ public: m_protocol.deinit(); m_protocol.set_p2p_endpoint(nullptr); MGINFO("Cryptonote protocol stopped successfully"); - tools::success_msg_writer() << "Daemon stopped successfully"; } catch (...) { LOG_ERROR("Failed to stop cryptonote protocol!"); } diff --git a/src/daemon/rpc.h b/src/daemon/rpc.h index 8b0d5808d..0ecfdd120 100644 --- a/src/daemon/rpc.h +++ b/src/daemon/rpc.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index f7d85b5ef..31f432918 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -29,11 +29,12 @@ // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #include "string_tools.h" +#include "common/password.h" #include "common/scoped_message_writer.h" #include "daemon/rpc_command_executor.h" #include "rpc/core_rpc_server_commands_defs.h" #include "cryptonote_core/cryptonote_core.h" -#include "cryptonote_core/hardfork.h" +#include "cryptonote_basic/hardfork.h" #include <boost/format.hpp> #include <ctime> #include <string> @@ -90,12 +91,19 @@ namespace { s = boost::lexical_cast<std::string>(dt/(3600*24)) + " days"; return s + " " + (t > now ? "in the future" : "ago"); } + + std::string make_error(const std::string &base, const std::string &status) + { + if (status == CORE_RPC_STATUS_OK) + return base; + return base + " -- " + status; + } } t_rpc_command_executor::t_rpc_command_executor( uint32_t ip , uint16_t port - , const std::string &user_agent + , const boost::optional<tools::login>& login , bool is_rpc , cryptonote::core_rpc_server* rpc_server ) @@ -103,7 +111,10 @@ t_rpc_command_executor::t_rpc_command_executor( { if (is_rpc) { - m_rpc_client = new tools::t_rpc_client(ip, port); + boost::optional<epee::net_utils::http::login> http_login{}; + if (login) + http_login.emplace(login->username, login->password.password()); + m_rpc_client = new tools::t_rpc_client(ip, port, std::move(http_login)); } else { @@ -158,6 +169,34 @@ bool t_rpc_command_executor::print_peer_list() { return true; } +bool t_rpc_command_executor::print_peer_list_stats() { + cryptonote::COMMAND_RPC_GET_PEER_LIST::request req; + cryptonote::COMMAND_RPC_GET_PEER_LIST::response res; + + std::string failure_message = "Couldn't retrieve peer list"; + if (m_is_rpc) + { + if (!m_rpc_client->rpc_request(req, res, "/get_peer_list", failure_message.c_str())) + { + return false; + } + } + else + { + if (!m_rpc_server->on_get_peer_list(req, res) || res.status != CORE_RPC_STATUS_OK) + { + tools::fail_msg_writer() << failure_message; + return false; + } + } + + tools::msg_writer() + << "White list size: " << res.white_list.size() << "/" << P2P_LOCAL_WHITE_PEERLIST_LIMIT << " (" << res.white_list.size() * 100.0 / P2P_LOCAL_WHITE_PEERLIST_LIMIT << "%)" << std::endl + << "Gray list size: " << res.gray_list.size() << "/" << P2P_LOCAL_GRAY_PEERLIST_LIMIT << " (" << res.gray_list.size() * 100.0 / P2P_LOCAL_GRAY_PEERLIST_LIMIT << "%)"; + + return true; +} + bool t_rpc_command_executor::save_blockchain() { cryptonote::COMMAND_RPC_SAVE_BC::request req; cryptonote::COMMAND_RPC_SAVE_BC::response res; @@ -175,7 +214,7 @@ bool t_rpc_command_executor::save_blockchain() { { if (!m_rpc_server->on_save_bc(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -203,7 +242,7 @@ bool t_rpc_command_executor::show_hash_rate() { { if (!m_rpc_server->on_set_log_hash_rate(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); } } @@ -230,7 +269,7 @@ bool t_rpc_command_executor::hide_hash_rate() { { if (!m_rpc_server->on_set_log_hash_rate(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -257,7 +296,7 @@ bool t_rpc_command_executor::show_difficulty() { { if (!m_rpc_server->on_get_info(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message.c_str(), res.status); return true; } } @@ -341,12 +380,12 @@ bool t_rpc_command_executor::show_status() { { if (!m_rpc_server->on_get_info(ireq, ires) || ires.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, ires.status); return true; } if (!m_rpc_server->on_hard_fork_info(hfreq, hfres, error_resp) || hfres.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, hfres.status); return true; } if (!m_rpc_server->on_mining_status(mreq, mres)) @@ -361,19 +400,19 @@ bool t_rpc_command_executor::show_status() { } else if (mres.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, mres.status); return true; } } std::time_t uptime = std::time(nullptr) - ires.start_time; - tools::success_msg_writer() << boost::format("Height: %llu/%llu (%.1f%%) on %s, %s, net hash %s, v%u%s, %s, %u+%u connections, uptime %ud %uh %um %us") + tools::success_msg_writer() << boost::format("Height: %llu/%llu (%.1f%%) on %s, %s, net hash %s, v%u%s, %s, %u(out)+%u(in) connections, uptime %ud %uh %um %us") % (unsigned long long)ires.height % (unsigned long long)(ires.target_height >= ires.height ? ires.target_height : ires.height) % get_sync_percentage(ires) % (ires.testnet ? "testnet" : "mainnet") - % (mining_busy ? "syncing" : mres.active ? "mining at " + get_mining_speed(mres.speed) : "not mining") + % (mining_busy ? "syncing" : mres.active ? ( ( mres.is_background_mining_enabled ? "smart " : "" ) + std::string("mining at ") + get_mining_speed(mres.speed) ) : "not mining") % get_mining_speed(ires.difficulty / ires.target) % (unsigned)hfres.version % get_fork_extra_info(hfres.earliest_height, ires.height, ires.target) @@ -407,7 +446,7 @@ bool t_rpc_command_executor::print_connections() { { if (!m_rpc_server->on_get_connections(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -472,7 +511,7 @@ bool t_rpc_command_executor::print_blockchain_info(uint64_t start_block_index, u { if (!m_rpc_server->on_get_block_headers_range(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -512,7 +551,7 @@ bool t_rpc_command_executor::set_log_level(int8_t level) { { if (!m_rpc_server->on_set_log_level(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -540,7 +579,7 @@ bool t_rpc_command_executor::set_log_categories(const std::string &categories) { { if (!m_rpc_server->on_set_log_categories(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -567,7 +606,7 @@ bool t_rpc_command_executor::print_height() { { if (!m_rpc_server->on_get_height(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -597,7 +636,7 @@ bool t_rpc_command_executor::print_block_by_hash(crypto::hash block_hash) { { if (!m_rpc_server->on_get_block(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -628,7 +667,7 @@ bool t_rpc_command_executor::print_block_by_height(uint64_t height) { { if (!m_rpc_server->on_get_block(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -657,7 +696,7 @@ bool t_rpc_command_executor::print_transaction(crypto::hash transaction_hash) { { if (!m_rpc_server->on_get_transactions(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -720,7 +759,7 @@ bool t_rpc_command_executor::is_key_image_spent(const crypto::key_image &ki) { { if (!m_rpc_server->on_is_key_image_spent(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -755,7 +794,7 @@ bool t_rpc_command_executor::print_transaction_pool_long() { { if (!m_rpc_server->on_get_transaction_pool(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -774,6 +813,7 @@ bool t_rpc_command_executor::print_transaction_pool_long() { << tx_info.tx_json << std::endl << "blob_size: " << tx_info.blob_size << std::endl << "fee: " << cryptonote::print_money(tx_info.fee) << std::endl + << "fee/byte: " << cryptonote::print_money(tx_info.fee / (double)tx_info.blob_size) << std::endl << "receive_time: " << tx_info.receive_time << " (" << get_human_time_ago(tx_info.receive_time, now) << ")" << std::endl << "relayed: " << [&](const cryptonote::tx_info &tx_info)->std::string { if (!tx_info.relayed) return "no"; return boost::lexical_cast<std::string>(tx_info.last_relayed_time) + " (" + get_human_time_ago(tx_info.last_relayed_time, now) + ")"; } (tx_info) << std::endl << "do_not_relay: " << (tx_info.do_not_relay ? 'T' : 'F') << std::endl @@ -838,7 +878,7 @@ bool t_rpc_command_executor::print_transaction_pool_short() { { if (!m_rpc_server->on_get_transaction_pool(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -855,6 +895,7 @@ bool t_rpc_command_executor::print_transaction_pool_short() { tools::msg_writer() << "id: " << tx_info.id_hash << std::endl << "blob_size: " << tx_info.blob_size << std::endl << "fee: " << cryptonote::print_money(tx_info.fee) << std::endl + << "fee/byte: " << cryptonote::print_money(tx_info.fee / (double)tx_info.blob_size) << std::endl << "receive_time: " << tx_info.receive_time << " (" << get_human_time_ago(tx_info.receive_time, now) << ")" << std::endl << "relayed: " << [&](const cryptonote::tx_info &tx_info)->std::string { if (!tx_info.relayed) return "no"; return boost::lexical_cast<std::string>(tx_info.last_relayed_time) + " (" + get_human_time_ago(tx_info.last_relayed_time, now) + ")"; } (tx_info) << std::endl << "do_not_relay: " << (tx_info.do_not_relay ? 'T' : 'F') << std::endl @@ -886,7 +927,7 @@ bool t_rpc_command_executor::print_transaction_pool_stats() { { if (!m_rpc_server->on_get_transaction_pool(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -919,18 +960,20 @@ bool t_rpc_command_executor::print_transaction_pool_stats() { size_t avg_bytes = n_transactions ? bytes / n_transactions : 0; tools::msg_writer() << n_transactions << " tx(es), " << bytes << " bytes total (min " << min_bytes << ", max " << max_bytes << ", avg " << avg_bytes << ")" << std::endl - << "fees " << cryptonote::print_money(fee) << " (avg " << cryptonote::print_money(n_transactions ? fee / n_transactions : 0) << " per tx)" << std::endl + << "fees " << cryptonote::print_money(fee) << " (avg " << cryptonote::print_money(n_transactions ? fee / n_transactions : 0) << " per tx" << ", " << cryptonote::print_money(bytes ? fee / bytes : 0) << " per byte )" << std::endl << n_not_relayed << " not relayed, " << n_failing << " failing, " << n_10m << " older than 10 minutes (oldest " << (oldest == 0 ? "-" : get_human_time_ago(oldest, now)) << ")" << std::endl; return true; } -bool t_rpc_command_executor::start_mining(cryptonote::account_public_address address, uint64_t num_threads, bool testnet) { +bool t_rpc_command_executor::start_mining(cryptonote::account_public_address address, uint64_t num_threads, bool testnet, bool do_background_mining, bool ignore_battery) { cryptonote::COMMAND_RPC_START_MINING::request req; cryptonote::COMMAND_RPC_START_MINING::response res; req.miner_address = cryptonote::get_account_address_as_str(testnet, address); req.threads_count = num_threads; - + req.do_background_mining = do_background_mining; + req.ignore_battery = ignore_battery; + std::string fail_message = "Mining did not start"; if (m_is_rpc) @@ -944,7 +987,7 @@ bool t_rpc_command_executor::start_mining(cryptonote::account_public_address add { if (!m_rpc_server->on_start_mining(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -969,7 +1012,7 @@ bool t_rpc_command_executor::stop_mining() { { if (!m_rpc_server->on_stop_mining(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -1010,7 +1053,7 @@ bool t_rpc_command_executor::stop_daemon() { if (!m_rpc_server->on_stop_daemon(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -1108,7 +1151,7 @@ bool t_rpc_command_executor::out_peers(uint64_t limit) { if (!m_rpc_server->on_out_peers(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -1136,7 +1179,7 @@ bool t_rpc_command_executor::start_save_graph() { if (!m_rpc_server->on_start_save_graph(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -1162,7 +1205,7 @@ bool t_rpc_command_executor::stop_save_graph() { if (!m_rpc_server->on_stop_save_graph(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -1189,7 +1232,7 @@ bool t_rpc_command_executor::hard_fork_info(uint8_t version) { if (!m_rpc_server->on_hard_fork_info(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -1220,7 +1263,7 @@ bool t_rpc_command_executor::print_bans() { if (!m_rpc_server->on_get_bans(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -1262,7 +1305,7 @@ bool t_rpc_command_executor::ban(const std::string &ip, time_t seconds) { if (!m_rpc_server->on_set_bans(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -1298,7 +1341,7 @@ bool t_rpc_command_executor::unban(const std::string &ip) { if (!m_rpc_server->on_set_bans(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -1325,9 +1368,9 @@ bool t_rpc_command_executor::flush_txpool(const std::string &txid) } else { - if (!m_rpc_server->on_flush_txpool(req, res, error_resp)) + if (!m_rpc_server->on_flush_txpool(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -1356,9 +1399,9 @@ bool t_rpc_command_executor::output_histogram(uint64_t min_count, uint64_t max_c } else { - if (!m_rpc_server->on_get_output_histogram(req, res, error_resp)) + if (!m_rpc_server->on_get_output_histogram(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -1393,9 +1436,9 @@ bool t_rpc_command_executor::print_coinbase_tx_sum(uint64_t height, uint64_t cou } else { - if (!m_rpc_server->on_get_coinbase_tx_sum(req, res, error_resp)) + if (!m_rpc_server->on_get_coinbase_tx_sum(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -1410,6 +1453,8 @@ bool t_rpc_command_executor::print_coinbase_tx_sum(uint64_t height, uint64_t cou bool t_rpc_command_executor::alt_chain_info() { + cryptonote::COMMAND_RPC_GET_INFO::request ireq; + cryptonote::COMMAND_RPC_GET_INFO::response ires; cryptonote::COMMAND_RPC_GET_ALTERNATE_CHAINS::request req; cryptonote::COMMAND_RPC_GET_ALTERNATE_CHAINS::response res; epee::json_rpc::error error_resp; @@ -1418,6 +1463,10 @@ bool t_rpc_command_executor::alt_chain_info() if (m_is_rpc) { + if (!m_rpc_client->rpc_request(ireq, ires, "/getinfo", fail_message.c_str())) + { + return true; + } if (!m_rpc_client->json_rpc_request(req, res, "get_alternate_chains", fail_message.c_str())) { return true; @@ -1425,9 +1474,14 @@ bool t_rpc_command_executor::alt_chain_info() } else { + if (!m_rpc_server->on_get_info(ireq, ires) || ires.status != CORE_RPC_STATUS_OK) + { + tools::fail_msg_writer() << make_error(fail_message, ires.status); + return true; + } if (!m_rpc_server->on_get_alternate_chains(req, res, error_resp)) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -1435,8 +1489,9 @@ bool t_rpc_command_executor::alt_chain_info() tools::msg_writer() << boost::lexical_cast<std::string>(res.chains.size()) << " alternate chains found:"; for (const auto chain: res.chains) { - tools::msg_writer() << chain.length << " blocks long, branching at height " << (chain.height - chain.length + 1) - << ", difficulty " << chain.difficulty << ": " << chain.block_hash; + uint64_t start_height = (chain.height - chain.length + 1); + tools::msg_writer() << chain.length << " blocks long, from height " << start_height << " (" << (ires.height - start_height - 1) + << " deep), diff " << chain.difficulty << ": " << chain.block_hash; } return true; } @@ -1460,7 +1515,7 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks) { return true; } - if (!m_rpc_client->rpc_request(fereq, feres, "/get_fee_estimate", fail_message.c_str())) + if (!m_rpc_client->json_rpc_request(fereq, feres, "get_fee_estimate", fail_message.c_str())) { return true; } @@ -1469,12 +1524,12 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks) { if (!m_rpc_server->on_get_info(ireq, ires) || ires.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, ires.status); return true; } if (!m_rpc_server->on_get_per_kb_fee_estimate(fereq, feres, error_resp) || feres.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, feres.status); return true; } } @@ -1491,7 +1546,7 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks) bhreq.end_height = ires.height - 1; if (m_is_rpc) { - if (!m_rpc_client->rpc_request(bhreq, bhres, "/getblockheadersrange", fail_message.c_str())) + if (!m_rpc_client->json_rpc_request(bhreq, bhres, "getblockheadersrange", fail_message.c_str())) { return true; } @@ -1500,7 +1555,7 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks) { if (!m_rpc_server->on_get_block_headers_range(bhreq, bhres, error_resp) || bhres.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, bhres.status); return true; } } @@ -1546,4 +1601,79 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks) return true; } +bool t_rpc_command_executor::update(const std::string &command) +{ + cryptonote::COMMAND_RPC_UPDATE::request req; + cryptonote::COMMAND_RPC_UPDATE::response res; + epee::json_rpc::error error_resp; + + std::string fail_message = "Problem fetching info"; + + req.command = command; + if (m_is_rpc) + { + if (!m_rpc_client->rpc_request(req, res, "/update", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_update(req, res) || res.status != CORE_RPC_STATUS_OK) + { + tools::fail_msg_writer() << make_error(fail_message, res.status); + return true; + } + } + + if (!res.update) + { + tools::msg_writer() << "No update available"; + return true; + } + + tools::msg_writer() << "Update available: v" << res.version << ": " << res.user_uri << ", hash " << res.hash; + if (command == "check") + return true; + + if (!res.path.empty()) + tools::msg_writer() << "Update downloaded to: " << res.path; + else + tools::msg_writer() << "Update download failed: " << res.status; + if (command == "download") + return true; + + tools::msg_writer() << "'update' not implemented yet"; + + return true; +} + +bool t_rpc_command_executor::relay_tx(const std::string &txid) +{ + cryptonote::COMMAND_RPC_RELAY_TX::request req; + cryptonote::COMMAND_RPC_RELAY_TX::response res; + std::string fail_message = "Unsuccessful"; + epee::json_rpc::error error_resp; + + req.txids.push_back(txid); + + if (m_is_rpc) + { + if (!m_rpc_client->json_rpc_request(req, res, "relay_tx", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_relay_tx(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK) + { + tools::fail_msg_writer() << make_error(fail_message, res.status); + return true; + } + } + + return true; +} + }// namespace daemonize diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h index afcd99d32..3f551bd14 100644 --- a/src/daemon/rpc_command_executor.h +++ b/src/daemon/rpc_command_executor.h @@ -6,7 +6,7 @@ */ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -38,6 +38,9 @@ #pragma once +#include <boost/optional/optional_fwd.hpp> + +#include "common/common_fwd.h" #include "common/rpc_client.h" #include "misc_log_ex.h" #include "cryptonote_core/cryptonote_core.h" @@ -60,7 +63,7 @@ public: t_rpc_command_executor( uint32_t ip , uint16_t port - , const std::string &user_agent + , const boost::optional<tools::login>& user , bool is_rpc = true , cryptonote::core_rpc_server* rpc_server = NULL ); @@ -69,6 +72,8 @@ public: bool print_peer_list(); + bool print_peer_list_stats(); + bool save_blockchain(); bool show_hash_rate(); @@ -103,7 +108,7 @@ public: bool print_transaction_pool_stats(); - bool start_mining(cryptonote::account_public_address address, uint64_t num_threads, bool testnet); + bool start_mining(cryptonote::account_public_address address, uint64_t num_threads, bool testnet, bool do_background_mining = false, bool ignore_battery = false); bool stop_mining(); @@ -146,6 +151,10 @@ public: bool alt_chain_info(); bool print_blockchain_dynamic_stats(uint64_t nblocks); + + bool update(const std::string &command); + + bool relay_tx(const std::string &txid); }; } // namespace daemonize diff --git a/src/daemonizer/CMakeLists.txt b/src/daemonizer/CMakeLists.txt index 964c8cc6f..c8cb1b445 100644 --- a/src/daemonizer/CMakeLists.txt +++ b/src/daemonizer/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # diff --git a/src/daemonizer/daemonizer.h b/src/daemonizer/daemonizer.h index 3e30d85ee..5f53d062b 100644 --- a/src/daemonizer/daemonizer.h +++ b/src/daemonizer/daemonizer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/daemonizer/posix_daemonizer.inl b/src/daemonizer/posix_daemonizer.inl index 926f0a3ac..f8be15dda 100644 --- a/src/daemonizer/posix_daemonizer.inl +++ b/src/daemonizer/posix_daemonizer.inl @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -43,6 +43,10 @@ namespace daemonizer "detach" , "Run as daemon" }; + const command_line::arg_descriptor<bool> arg_non_interactive = { + "non-interactive" + , "Run non-interactive" + }; } inline void init_options( @@ -51,6 +55,7 @@ namespace daemonizer ) { command_line::add_arg(normal_options, arg_detach); + command_line::add_arg(normal_options, arg_non_interactive); } inline boost::filesystem::path get_default_data_dir() @@ -79,6 +84,10 @@ namespace daemonizer auto daemon = executor.create_daemon(vm); return daemon.run(); } + else if (command_line::has_arg(vm, arg_non_interactive)) + { + return executor.run_non_interactive(vm); + } else { //LOG_PRINT_L0("Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL); diff --git a/src/daemonizer/posix_fork.h b/src/daemonizer/posix_fork.h index ef627a43e..459417d25 100644 --- a/src/daemonizer/posix_fork.h +++ b/src/daemonizer/posix_fork.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/daemonizer/windows_daemonizer.inl b/src/daemonizer/windows_daemonizer.inl index d8023944e..012cd1e67 100644 --- a/src/daemonizer/windows_daemonizer.inl +++ b/src/daemonizer/windows_daemonizer.inl @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/daemonizer/windows_service.cpp b/src/daemonizer/windows_service.cpp index f8cc0c6c7..d540f5bf8 100644 --- a/src/daemonizer/windows_service.cpp +++ b/src/daemonizer/windows_service.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/daemonizer/windows_service.h b/src/daemonizer/windows_service.h index 2712d13a3..070434b04 100644 --- a/src/daemonizer/windows_service.h +++ b/src/daemonizer/windows_service.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/daemonizer/windows_service_runner.h b/src/daemonizer/windows_service_runner.h index f4258a215..528d13a53 100644 --- a/src/daemonizer/windows_service_runner.h +++ b/src/daemonizer/windows_service_runner.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/mnemonics/CMakeLists.txt b/src/mnemonics/CMakeLists.txt index 936c43b99..4db19d195 100644 --- a/src/mnemonics/CMakeLists.txt +++ b/src/mnemonics/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -33,7 +33,10 @@ set(mnemonics_headers) set(mnemonics_private_headers electrum-words.h + chinese_simplified.h english.h + dutch.h + french.h german.h italian.h japanese.h @@ -51,5 +54,7 @@ monero_add_library(mnemonics ${mnemonics_headers} ${mnemonics_private_headers}) target_link_libraries(mnemonics + PUBLIC + ${Boost_SYSTEM_LIBRARY} PRIVATE ${EXTRA_LIBRARIES}) diff --git a/src/mnemonics/chinese_simplified.h b/src/mnemonics/chinese_simplified.h new file mode 100644 index 000000000..413186733 --- /dev/null +++ b/src/mnemonics/chinese_simplified.h @@ -0,0 +1,1709 @@ +// Word list originally created by dabura667 and released under The MIT License (MIT)
+//
+// The MIT License (MIT)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Code surrounding the word list is Copyright (c) 2014-2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/*!
+ * \file chinese_simplified.h
+ *
+ * \brief Simplified Chinese word list and map.
+ */
+
+#ifndef CHINESE_SIMPLIFIED_H
+#define CHINESE_SIMPLIFIED_H
+
+#include <vector>
+#include <unordered_map>
+#include "language_base.h"
+#include <string>
+
+/*!
+ * \namespace Language
+ * \brief Mnemonic language related namespace.
+ */
+namespace Language
+{
+ class Chinese_Simplified: public Base
+ {
+ public:
+ Chinese_Simplified(): Base("Chinese (Simplified)", std::vector<std::string>({
+ "的",
+ "一",
+ "是",
+ "在",
+ "不",
+ "了",
+ "有",
+ "和",
+ "人",
+ "这",
+ "中",
+ "大",
+ "为",
+ "上",
+ "个",
+ "国",
+ "我",
+ "以",
+ "要",
+ "他",
+ "时",
+ "来",
+ "用",
+ "们",
+ "生",
+ "到",
+ "作",
+ "地",
+ "于",
+ "出",
+ "就",
+ "分",
+ "对",
+ "成",
+ "会",
+ "可",
+ "主",
+ "发",
+ "年",
+ "动",
+ "同",
+ "工",
+ "也",
+ "能",
+ "下",
+ "过",
+ "子",
+ "说",
+ "产",
+ "种",
+ "面",
+ "而",
+ "方",
+ "后",
+ "多",
+ "定",
+ "行",
+ "学",
+ "法",
+ "所",
+ "民",
+ "得",
+ "经",
+ "十",
+ "三",
+ "之",
+ "进",
+ "着",
+ "等",
+ "部",
+ "度",
+ "家",
+ "电",
+ "力",
+ "里",
+ "如",
+ "水",
+ "化",
+ "高",
+ "自",
+ "二",
+ "理",
+ "起",
+ "小",
+ "物",
+ "现",
+ "实",
+ "加",
+ "量",
+ "都",
+ "两",
+ "体",
+ "制",
+ "机",
+ "当",
+ "使",
+ "点",
+ "从",
+ "业",
+ "本",
+ "去",
+ "把",
+ "性",
+ "好",
+ "应",
+ "开",
+ "它",
+ "合",
+ "还",
+ "因",
+ "由",
+ "其",
+ "些",
+ "然",
+ "前",
+ "外",
+ "天",
+ "政",
+ "四",
+ "日",
+ "那",
+ "社",
+ "义",
+ "事",
+ "平",
+ "形",
+ "相",
+ "全",
+ "表",
+ "间",
+ "样",
+ "与",
+ "关",
+ "各",
+ "重",
+ "新",
+ "线",
+ "内",
+ "数",
+ "正",
+ "心",
+ "反",
+ "你",
+ "明",
+ "看",
+ "原",
+ "又",
+ "么",
+ "利",
+ "比",
+ "或",
+ "但",
+ "质",
+ "气",
+ "第",
+ "向",
+ "道",
+ "命",
+ "此",
+ "变",
+ "条",
+ "只",
+ "没",
+ "结",
+ "解",
+ "问",
+ "意",
+ "建",
+ "月",
+ "公",
+ "无",
+ "系",
+ "军",
+ "很",
+ "情",
+ "者",
+ "最",
+ "立",
+ "代",
+ "想",
+ "已",
+ "通",
+ "并",
+ "提",
+ "直",
+ "题",
+ "党",
+ "程",
+ "展",
+ "五",
+ "果",
+ "料",
+ "象",
+ "员",
+ "革",
+ "位",
+ "入",
+ "常",
+ "文",
+ "总",
+ "次",
+ "品",
+ "式",
+ "活",
+ "设",
+ "及",
+ "管",
+ "特",
+ "件",
+ "长",
+ "求",
+ "老",
+ "头",
+ "基",
+ "资",
+ "边",
+ "流",
+ "路",
+ "级",
+ "少",
+ "图",
+ "山",
+ "统",
+ "接",
+ "知",
+ "较",
+ "将",
+ "组",
+ "见",
+ "计",
+ "别",
+ "她",
+ "手",
+ "角",
+ "期",
+ "根",
+ "论",
+ "运",
+ "农",
+ "指",
+ "几",
+ "九",
+ "区",
+ "强",
+ "放",
+ "决",
+ "西",
+ "被",
+ "干",
+ "做",
+ "必",
+ "战",
+ "先",
+ "回",
+ "则",
+ "任",
+ "取",
+ "据",
+ "处",
+ "队",
+ "南",
+ "给",
+ "色",
+ "光",
+ "门",
+ "即",
+ "保",
+ "治",
+ "北",
+ "造",
+ "百",
+ "规",
+ "热",
+ "领",
+ "七",
+ "海",
+ "口",
+ "东",
+ "导",
+ "器",
+ "压",
+ "志",
+ "世",
+ "金",
+ "增",
+ "争",
+ "济",
+ "阶",
+ "油",
+ "思",
+ "术",
+ "极",
+ "交",
+ "受",
+ "联",
+ "什",
+ "认",
+ "六",
+ "共",
+ "权",
+ "收",
+ "证",
+ "改",
+ "清",
+ "美",
+ "再",
+ "采",
+ "转",
+ "更",
+ "单",
+ "风",
+ "切",
+ "打",
+ "白",
+ "教",
+ "速",
+ "花",
+ "带",
+ "安",
+ "场",
+ "身",
+ "车",
+ "例",
+ "真",
+ "务",
+ "具",
+ "万",
+ "每",
+ "目",
+ "至",
+ "达",
+ "走",
+ "积",
+ "示",
+ "议",
+ "声",
+ "报",
+ "斗",
+ "完",
+ "类",
+ "八",
+ "离",
+ "华",
+ "名",
+ "确",
+ "才",
+ "科",
+ "张",
+ "信",
+ "马",
+ "节",
+ "话",
+ "米",
+ "整",
+ "空",
+ "元",
+ "况",
+ "今",
+ "集",
+ "温",
+ "传",
+ "土",
+ "许",
+ "步",
+ "群",
+ "广",
+ "石",
+ "记",
+ "需",
+ "段",
+ "研",
+ "界",
+ "拉",
+ "林",
+ "律",
+ "叫",
+ "且",
+ "究",
+ "观",
+ "越",
+ "织",
+ "装",
+ "影",
+ "算",
+ "低",
+ "持",
+ "音",
+ "众",
+ "书",
+ "布",
+ "复",
+ "容",
+ "儿",
+ "须",
+ "际",
+ "商",
+ "非",
+ "验",
+ "连",
+ "断",
+ "深",
+ "难",
+ "近",
+ "矿",
+ "千",
+ "周",
+ "委",
+ "素",
+ "技",
+ "备",
+ "半",
+ "办",
+ "青",
+ "省",
+ "列",
+ "习",
+ "响",
+ "约",
+ "支",
+ "般",
+ "史",
+ "感",
+ "劳",
+ "便",
+ "团",
+ "往",
+ "酸",
+ "历",
+ "市",
+ "克",
+ "何",
+ "除",
+ "消",
+ "构",
+ "府",
+ "称",
+ "太",
+ "准",
+ "精",
+ "值",
+ "号",
+ "率",
+ "族",
+ "维",
+ "划",
+ "选",
+ "标",
+ "写",
+ "存",
+ "候",
+ "毛",
+ "亲",
+ "快",
+ "效",
+ "斯",
+ "院",
+ "查",
+ "江",
+ "型",
+ "眼",
+ "王",
+ "按",
+ "格",
+ "养",
+ "易",
+ "置",
+ "派",
+ "层",
+ "片",
+ "始",
+ "却",
+ "专",
+ "状",
+ "育",
+ "厂",
+ "京",
+ "识",
+ "适",
+ "属",
+ "圆",
+ "包",
+ "火",
+ "住",
+ "调",
+ "满",
+ "县",
+ "局",
+ "照",
+ "参",
+ "红",
+ "细",
+ "引",
+ "听",
+ "该",
+ "铁",
+ "价",
+ "严",
+ "首",
+ "底",
+ "液",
+ "官",
+ "德",
+ "随",
+ "病",
+ "苏",
+ "失",
+ "尔",
+ "死",
+ "讲",
+ "配",
+ "女",
+ "黄",
+ "推",
+ "显",
+ "谈",
+ "罪",
+ "神",
+ "艺",
+ "呢",
+ "席",
+ "含",
+ "企",
+ "望",
+ "密",
+ "批",
+ "营",
+ "项",
+ "防",
+ "举",
+ "球",
+ "英",
+ "氧",
+ "势",
+ "告",
+ "李",
+ "台",
+ "落",
+ "木",
+ "帮",
+ "轮",
+ "破",
+ "亚",
+ "师",
+ "围",
+ "注",
+ "远",
+ "字",
+ "材",
+ "排",
+ "供",
+ "河",
+ "态",
+ "封",
+ "另",
+ "施",
+ "减",
+ "树",
+ "溶",
+ "怎",
+ "止",
+ "案",
+ "言",
+ "士",
+ "均",
+ "武",
+ "固",
+ "叶",
+ "鱼",
+ "波",
+ "视",
+ "仅",
+ "费",
+ "紧",
+ "爱",
+ "左",
+ "章",
+ "早",
+ "朝",
+ "害",
+ "续",
+ "轻",
+ "服",
+ "试",
+ "食",
+ "充",
+ "兵",
+ "源",
+ "判",
+ "护",
+ "司",
+ "足",
+ "某",
+ "练",
+ "差",
+ "致",
+ "板",
+ "田",
+ "降",
+ "黑",
+ "犯",
+ "负",
+ "击",
+ "范",
+ "继",
+ "兴",
+ "似",
+ "余",
+ "坚",
+ "曲",
+ "输",
+ "修",
+ "故",
+ "城",
+ "夫",
+ "够",
+ "送",
+ "笔",
+ "船",
+ "占",
+ "右",
+ "财",
+ "吃",
+ "富",
+ "春",
+ "职",
+ "觉",
+ "汉",
+ "画",
+ "功",
+ "巴",
+ "跟",
+ "虽",
+ "杂",
+ "飞",
+ "检",
+ "吸",
+ "助",
+ "升",
+ "阳",
+ "互",
+ "初",
+ "创",
+ "抗",
+ "考",
+ "投",
+ "坏",
+ "策",
+ "古",
+ "径",
+ "换",
+ "未",
+ "跑",
+ "留",
+ "钢",
+ "曾",
+ "端",
+ "责",
+ "站",
+ "简",
+ "述",
+ "钱",
+ "副",
+ "尽",
+ "帝",
+ "射",
+ "草",
+ "冲",
+ "承",
+ "独",
+ "令",
+ "限",
+ "阿",
+ "宣",
+ "环",
+ "双",
+ "请",
+ "超",
+ "微",
+ "让",
+ "控",
+ "州",
+ "良",
+ "轴",
+ "找",
+ "否",
+ "纪",
+ "益",
+ "依",
+ "优",
+ "顶",
+ "础",
+ "载",
+ "倒",
+ "房",
+ "突",
+ "坐",
+ "粉",
+ "敌",
+ "略",
+ "客",
+ "袁",
+ "冷",
+ "胜",
+ "绝",
+ "析",
+ "块",
+ "剂",
+ "测",
+ "丝",
+ "协",
+ "诉",
+ "念",
+ "陈",
+ "仍",
+ "罗",
+ "盐",
+ "友",
+ "洋",
+ "错",
+ "苦",
+ "夜",
+ "刑",
+ "移",
+ "频",
+ "逐",
+ "靠",
+ "混",
+ "母",
+ "短",
+ "皮",
+ "终",
+ "聚",
+ "汽",
+ "村",
+ "云",
+ "哪",
+ "既",
+ "距",
+ "卫",
+ "停",
+ "烈",
+ "央",
+ "察",
+ "烧",
+ "迅",
+ "境",
+ "若",
+ "印",
+ "洲",
+ "刻",
+ "括",
+ "激",
+ "孔",
+ "搞",
+ "甚",
+ "室",
+ "待",
+ "核",
+ "校",
+ "散",
+ "侵",
+ "吧",
+ "甲",
+ "游",
+ "久",
+ "菜",
+ "味",
+ "旧",
+ "模",
+ "湖",
+ "货",
+ "损",
+ "预",
+ "阻",
+ "毫",
+ "普",
+ "稳",
+ "乙",
+ "妈",
+ "植",
+ "息",
+ "扩",
+ "银",
+ "语",
+ "挥",
+ "酒",
+ "守",
+ "拿",
+ "序",
+ "纸",
+ "医",
+ "缺",
+ "雨",
+ "吗",
+ "针",
+ "刘",
+ "啊",
+ "急",
+ "唱",
+ "误",
+ "训",
+ "愿",
+ "审",
+ "附",
+ "获",
+ "茶",
+ "鲜",
+ "粮",
+ "斤",
+ "孩",
+ "脱",
+ "硫",
+ "肥",
+ "善",
+ "龙",
+ "演",
+ "父",
+ "渐",
+ "血",
+ "欢",
+ "械",
+ "掌",
+ "歌",
+ "沙",
+ "刚",
+ "攻",
+ "谓",
+ "盾",
+ "讨",
+ "晚",
+ "粒",
+ "乱",
+ "燃",
+ "矛",
+ "乎",
+ "杀",
+ "药",
+ "宁",
+ "鲁",
+ "贵",
+ "钟",
+ "煤",
+ "读",
+ "班",
+ "伯",
+ "香",
+ "介",
+ "迫",
+ "句",
+ "丰",
+ "培",
+ "握",
+ "兰",
+ "担",
+ "弦",
+ "蛋",
+ "沉",
+ "假",
+ "穿",
+ "执",
+ "答",
+ "乐",
+ "谁",
+ "顺",
+ "烟",
+ "缩",
+ "征",
+ "脸",
+ "喜",
+ "松",
+ "脚",
+ "困",
+ "异",
+ "免",
+ "背",
+ "星",
+ "福",
+ "买",
+ "染",
+ "井",
+ "概",
+ "慢",
+ "怕",
+ "磁",
+ "倍",
+ "祖",
+ "皇",
+ "促",
+ "静",
+ "补",
+ "评",
+ "翻",
+ "肉",
+ "践",
+ "尼",
+ "衣",
+ "宽",
+ "扬",
+ "棉",
+ "希",
+ "伤",
+ "操",
+ "垂",
+ "秋",
+ "宜",
+ "氢",
+ "套",
+ "督",
+ "振",
+ "架",
+ "亮",
+ "末",
+ "宪",
+ "庆",
+ "编",
+ "牛",
+ "触",
+ "映",
+ "雷",
+ "销",
+ "诗",
+ "座",
+ "居",
+ "抓",
+ "裂",
+ "胞",
+ "呼",
+ "娘",
+ "景",
+ "威",
+ "绿",
+ "晶",
+ "厚",
+ "盟",
+ "衡",
+ "鸡",
+ "孙",
+ "延",
+ "危",
+ "胶",
+ "屋",
+ "乡",
+ "临",
+ "陆",
+ "顾",
+ "掉",
+ "呀",
+ "灯",
+ "岁",
+ "措",
+ "束",
+ "耐",
+ "剧",
+ "玉",
+ "赵",
+ "跳",
+ "哥",
+ "季",
+ "课",
+ "凯",
+ "胡",
+ "额",
+ "款",
+ "绍",
+ "卷",
+ "齐",
+ "伟",
+ "蒸",
+ "殖",
+ "永",
+ "宗",
+ "苗",
+ "川",
+ "炉",
+ "岩",
+ "弱",
+ "零",
+ "杨",
+ "奏",
+ "沿",
+ "露",
+ "杆",
+ "探",
+ "滑",
+ "镇",
+ "饭",
+ "浓",
+ "航",
+ "怀",
+ "赶",
+ "库",
+ "夺",
+ "伊",
+ "灵",
+ "税",
+ "途",
+ "灭",
+ "赛",
+ "归",
+ "召",
+ "鼓",
+ "播",
+ "盘",
+ "裁",
+ "险",
+ "康",
+ "唯",
+ "录",
+ "菌",
+ "纯",
+ "借",
+ "糖",
+ "盖",
+ "横",
+ "符",
+ "私",
+ "努",
+ "堂",
+ "域",
+ "枪",
+ "润",
+ "幅",
+ "哈",
+ "竟",
+ "熟",
+ "虫",
+ "泽",
+ "脑",
+ "壤",
+ "碳",
+ "欧",
+ "遍",
+ "侧",
+ "寨",
+ "敢",
+ "彻",
+ "虑",
+ "斜",
+ "薄",
+ "庭",
+ "纳",
+ "弹",
+ "饲",
+ "伸",
+ "折",
+ "麦",
+ "湿",
+ "暗",
+ "荷",
+ "瓦",
+ "塞",
+ "床",
+ "筑",
+ "恶",
+ "户",
+ "访",
+ "塔",
+ "奇",
+ "透",
+ "梁",
+ "刀",
+ "旋",
+ "迹",
+ "卡",
+ "氯",
+ "遇",
+ "份",
+ "毒",
+ "泥",
+ "退",
+ "洗",
+ "摆",
+ "灰",
+ "彩",
+ "卖",
+ "耗",
+ "夏",
+ "择",
+ "忙",
+ "铜",
+ "献",
+ "硬",
+ "予",
+ "繁",
+ "圈",
+ "雪",
+ "函",
+ "亦",
+ "抽",
+ "篇",
+ "阵",
+ "阴",
+ "丁",
+ "尺",
+ "追",
+ "堆",
+ "雄",
+ "迎",
+ "泛",
+ "爸",
+ "楼",
+ "避",
+ "谋",
+ "吨",
+ "野",
+ "猪",
+ "旗",
+ "累",
+ "偏",
+ "典",
+ "馆",
+ "索",
+ "秦",
+ "脂",
+ "潮",
+ "爷",
+ "豆",
+ "忽",
+ "托",
+ "惊",
+ "塑",
+ "遗",
+ "愈",
+ "朱",
+ "替",
+ "纤",
+ "粗",
+ "倾",
+ "尚",
+ "痛",
+ "楚",
+ "谢",
+ "奋",
+ "购",
+ "磨",
+ "君",
+ "池",
+ "旁",
+ "碎",
+ "骨",
+ "监",
+ "捕",
+ "弟",
+ "暴",
+ "割",
+ "贯",
+ "殊",
+ "释",
+ "词",
+ "亡",
+ "壁",
+ "顿",
+ "宝",
+ "午",
+ "尘",
+ "闻",
+ "揭",
+ "炮",
+ "残",
+ "冬",
+ "桥",
+ "妇",
+ "警",
+ "综",
+ "招",
+ "吴",
+ "付",
+ "浮",
+ "遭",
+ "徐",
+ "您",
+ "摇",
+ "谷",
+ "赞",
+ "箱",
+ "隔",
+ "订",
+ "男",
+ "吹",
+ "园",
+ "纷",
+ "唐",
+ "败",
+ "宋",
+ "玻",
+ "巨",
+ "耕",
+ "坦",
+ "荣",
+ "闭",
+ "湾",
+ "键",
+ "凡",
+ "驻",
+ "锅",
+ "救",
+ "恩",
+ "剥",
+ "凝",
+ "碱",
+ "齿",
+ "截",
+ "炼",
+ "麻",
+ "纺",
+ "禁",
+ "废",
+ "盛",
+ "版",
+ "缓",
+ "净",
+ "睛",
+ "昌",
+ "婚",
+ "涉",
+ "筒",
+ "嘴",
+ "插",
+ "岸",
+ "朗",
+ "庄",
+ "街",
+ "藏",
+ "姑",
+ "贸",
+ "腐",
+ "奴",
+ "啦",
+ "惯",
+ "乘",
+ "伙",
+ "恢",
+ "匀",
+ "纱",
+ "扎",
+ "辩",
+ "耳",
+ "彪",
+ "臣",
+ "亿",
+ "璃",
+ "抵",
+ "脉",
+ "秀",
+ "萨",
+ "俄",
+ "网",
+ "舞",
+ "店",
+ "喷",
+ "纵",
+ "寸",
+ "汗",
+ "挂",
+ "洪",
+ "贺",
+ "闪",
+ "柬",
+ "爆",
+ "烯",
+ "津",
+ "稻",
+ "墙",
+ "软",
+ "勇",
+ "像",
+ "滚",
+ "厘",
+ "蒙",
+ "芳",
+ "肯",
+ "坡",
+ "柱",
+ "荡",
+ "腿",
+ "仪",
+ "旅",
+ "尾",
+ "轧",
+ "冰",
+ "贡",
+ "登",
+ "黎",
+ "削",
+ "钻",
+ "勒",
+ "逃",
+ "障",
+ "氨",
+ "郭",
+ "峰",
+ "币",
+ "港",
+ "伏",
+ "轨",
+ "亩",
+ "毕",
+ "擦",
+ "莫",
+ "刺",
+ "浪",
+ "秘",
+ "援",
+ "株",
+ "健",
+ "售",
+ "股",
+ "岛",
+ "甘",
+ "泡",
+ "睡",
+ "童",
+ "铸",
+ "汤",
+ "阀",
+ "休",
+ "汇",
+ "舍",
+ "牧",
+ "绕",
+ "炸",
+ "哲",
+ "磷",
+ "绩",
+ "朋",
+ "淡",
+ "尖",
+ "启",
+ "陷",
+ "柴",
+ "呈",
+ "徒",
+ "颜",
+ "泪",
+ "稍",
+ "忘",
+ "泵",
+ "蓝",
+ "拖",
+ "洞",
+ "授",
+ "镜",
+ "辛",
+ "壮",
+ "锋",
+ "贫",
+ "虚",
+ "弯",
+ "摩",
+ "泰",
+ "幼",
+ "廷",
+ "尊",
+ "窗",
+ "纲",
+ "弄",
+ "隶",
+ "疑",
+ "氏",
+ "宫",
+ "姐",
+ "震",
+ "瑞",
+ "怪",
+ "尤",
+ "琴",
+ "循",
+ "描",
+ "膜",
+ "违",
+ "夹",
+ "腰",
+ "缘",
+ "珠",
+ "穷",
+ "森",
+ "枝",
+ "竹",
+ "沟",
+ "催",
+ "绳",
+ "忆",
+ "邦",
+ "剩",
+ "幸",
+ "浆",
+ "栏",
+ "拥",
+ "牙",
+ "贮",
+ "礼",
+ "滤",
+ "钠",
+ "纹",
+ "罢",
+ "拍",
+ "咱",
+ "喊",
+ "袖",
+ "埃",
+ "勤",
+ "罚",
+ "焦",
+ "潜",
+ "伍",
+ "墨",
+ "欲",
+ "缝",
+ "姓",
+ "刊",
+ "饱",
+ "仿",
+ "奖",
+ "铝",
+ "鬼",
+ "丽",
+ "跨",
+ "默",
+ "挖",
+ "链",
+ "扫",
+ "喝",
+ "袋",
+ "炭",
+ "污",
+ "幕",
+ "诸",
+ "弧",
+ "励",
+ "梅",
+ "奶",
+ "洁",
+ "灾",
+ "舟",
+ "鉴",
+ "苯",
+ "讼",
+ "抱",
+ "毁",
+ "懂",
+ "寒",
+ "智",
+ "埔",
+ "寄",
+ "届",
+ "跃",
+ "渡",
+ "挑",
+ "丹",
+ "艰",
+ "贝",
+ "碰",
+ "拔",
+ "爹",
+ "戴",
+ "码",
+ "梦",
+ "芽",
+ "熔",
+ "赤",
+ "渔",
+ "哭",
+ "敬",
+ "颗",
+ "奔",
+ "铅",
+ "仲",
+ "虎",
+ "稀",
+ "妹",
+ "乏",
+ "珍",
+ "申",
+ "桌",
+ "遵",
+ "允",
+ "隆",
+ "螺",
+ "仓",
+ "魏",
+ "锐",
+ "晓",
+ "氮",
+ "兼",
+ "隐",
+ "碍",
+ "赫",
+ "拨",
+ "忠",
+ "肃",
+ "缸",
+ "牵",
+ "抢",
+ "博",
+ "巧",
+ "壳",
+ "兄",
+ "杜",
+ "讯",
+ "诚",
+ "碧",
+ "祥",
+ "柯",
+ "页",
+ "巡",
+ "矩",
+ "悲",
+ "灌",
+ "龄",
+ "伦",
+ "票",
+ "寻",
+ "桂",
+ "铺",
+ "圣",
+ "恐",
+ "恰",
+ "郑",
+ "趣",
+ "抬",
+ "荒",
+ "腾",
+ "贴",
+ "柔",
+ "滴",
+ "猛",
+ "阔",
+ "辆",
+ "妻",
+ "填",
+ "撤",
+ "储",
+ "签",
+ "闹",
+ "扰",
+ "紫",
+ "砂",
+ "递",
+ "戏",
+ "吊",
+ "陶",
+ "伐",
+ "喂",
+ "疗",
+ "瓶",
+ "婆",
+ "抚",
+ "臂",
+ "摸",
+ "忍",
+ "虾",
+ "蜡",
+ "邻",
+ "胸",
+ "巩",
+ "挤",
+ "偶",
+ "弃",
+ "槽",
+ "劲",
+ "乳",
+ "邓",
+ "吉",
+ "仁",
+ "烂",
+ "砖",
+ "租",
+ "乌",
+ "舰",
+ "伴",
+ "瓜",
+ "浅",
+ "丙",
+ "暂",
+ "燥",
+ "橡",
+ "柳",
+ "迷",
+ "暖",
+ "牌",
+ "秧",
+ "胆",
+ "详",
+ "簧",
+ "踏",
+ "瓷",
+ "谱",
+ "呆",
+ "宾",
+ "糊",
+ "洛",
+ "辉",
+ "愤",
+ "竞",
+ "隙",
+ "怒",
+ "粘",
+ "乃",
+ "绪",
+ "肩",
+ "籍",
+ "敏",
+ "涂",
+ "熙",
+ "皆",
+ "侦",
+ "悬",
+ "掘",
+ "享",
+ "纠",
+ "醒",
+ "狂",
+ "锁",
+ "淀",
+ "恨",
+ "牲",
+ "霸",
+ "爬",
+ "赏",
+ "逆",
+ "玩",
+ "陵",
+ "祝",
+ "秒",
+ "浙",
+ "貌"
+ }), 1)
+ {
+ populate_maps();
+ }
+ };
+}
+
+#endif
diff --git a/src/mnemonics/dutch.h b/src/mnemonics/dutch.h new file mode 100644 index 000000000..0d7a637e6 --- /dev/null +++ b/src/mnemonics/dutch.h @@ -0,0 +1,1686 @@ +// Copyright (c) 2014-2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/*!
+ * \file dutch.h
+ *
+ * \brief New Dutch word list and map.
+ */
+
+#ifndef DUTCH_H
+#define DUTCH_H
+
+#include <vector>
+#include <unordered_map>
+#include "language_base.h"
+#include <string>
+
+/*!
+ * \namespace Language
+ * \brief Mnemonic language related namespace.
+ */
+namespace Language
+{
+ class Dutch: public Base
+ {
+ public:
+ Dutch(): Base("Dutch", std::vector<std::string>({
+ "aalglad",
+ "aalscholver",
+ "aambeeld",
+ "aangeef",
+ "aanlandig",
+ "aanvaard",
+ "aanwakker",
+ "aapmens",
+ "aarten",
+ "abdicatie",
+ "abnormaal",
+ "abrikoos",
+ "accu",
+ "acuut",
+ "adjudant",
+ "admiraal",
+ "advies",
+ "afbidding",
+ "afdracht",
+ "affaire",
+ "affiche",
+ "afgang",
+ "afkick",
+ "afknap",
+ "aflees",
+ "afmijner",
+ "afname",
+ "afpreekt",
+ "afrader",
+ "afspeel",
+ "aftocht",
+ "aftrek",
+ "afzijdig",
+ "ahornboom",
+ "aktetas",
+ "akzo",
+ "alchemist",
+ "alcohol",
+ "aldaar",
+ "alexander",
+ "alfabet",
+ "alfredo",
+ "alice",
+ "alikruik",
+ "allrisk",
+ "altsax",
+ "alufolie",
+ "alziend",
+ "amai",
+ "ambacht",
+ "ambieer",
+ "amina",
+ "amnestie",
+ "amok",
+ "ampul",
+ "amuzikaal",
+ "angela",
+ "aniek",
+ "antje",
+ "antwerpen",
+ "anya",
+ "aorta",
+ "apache",
+ "apekool",
+ "appelaar",
+ "arganolie",
+ "argeloos",
+ "armoede",
+ "arrenslee",
+ "artritis",
+ "arubaan",
+ "asbak",
+ "ascii",
+ "asgrauw",
+ "asjes",
+ "asml",
+ "aspunt",
+ "asurn",
+ "asveld",
+ "aterling",
+ "atomair",
+ "atrium",
+ "atsma",
+ "atypisch",
+ "auping",
+ "aura",
+ "avifauna",
+ "axiaal",
+ "azoriaan",
+ "azteek",
+ "azuur",
+ "bachelor",
+ "badderen",
+ "badhotel",
+ "badmantel",
+ "badsteden",
+ "balie",
+ "ballans",
+ "balvers",
+ "bamibal",
+ "banneling",
+ "barracuda",
+ "basaal",
+ "batelaan",
+ "batje",
+ "beambte",
+ "bedlamp",
+ "bedwelmd",
+ "befaamd",
+ "begierd",
+ "begraaf",
+ "behield",
+ "beijaard",
+ "bejaagd",
+ "bekaaid",
+ "beks",
+ "bektas",
+ "belaad",
+ "belboei",
+ "belderbos",
+ "beloerd",
+ "beluchten",
+ "bemiddeld",
+ "benadeeld",
+ "benijd",
+ "berechten",
+ "beroemd",
+ "besef",
+ "besseling",
+ "best",
+ "betichten",
+ "bevind",
+ "bevochten",
+ "bevraagd",
+ "bewust",
+ "bidplaats",
+ "biefstuk",
+ "biemans",
+ "biezen",
+ "bijbaan",
+ "bijeenkom",
+ "bijfiguur",
+ "bijkaart",
+ "bijlage",
+ "bijpaard",
+ "bijtgaar",
+ "bijweg",
+ "bimmel",
+ "binck",
+ "bint",
+ "biobak",
+ "biotisch",
+ "biseks",
+ "bistro",
+ "bitter",
+ "bitumen",
+ "bizar",
+ "blad",
+ "bleken",
+ "blender",
+ "bleu",
+ "blief",
+ "blijven",
+ "blozen",
+ "bock",
+ "boef",
+ "boei",
+ "boks",
+ "bolder",
+ "bolus",
+ "bolvormig",
+ "bomaanval",
+ "bombarde",
+ "bomma",
+ "bomtapijt",
+ "bookmaker",
+ "boos",
+ "borg",
+ "bosbes",
+ "boshuizen",
+ "bosloop",
+ "botanicus",
+ "bougie",
+ "bovag",
+ "boxspring",
+ "braad",
+ "brasem",
+ "brevet",
+ "brigade",
+ "brinckman",
+ "bruid",
+ "budget",
+ "buffel",
+ "buks",
+ "bulgaar",
+ "buma",
+ "butaan",
+ "butler",
+ "buuf",
+ "cactus",
+ "cafeetje",
+ "camcorder",
+ "cannabis",
+ "canyon",
+ "capoeira",
+ "capsule",
+ "carkit",
+ "casanova",
+ "catalaan",
+ "ceintuur",
+ "celdeling",
+ "celplasma",
+ "cement",
+ "censeren",
+ "ceramisch",
+ "cerberus",
+ "cerebraal",
+ "cesium",
+ "cirkel",
+ "citeer",
+ "civiel",
+ "claxon",
+ "clenbuterol",
+ "clicheren",
+ "clijsen",
+ "coalitie",
+ "coassistentschap",
+ "coaxiaal",
+ "codetaal",
+ "cofinanciering",
+ "cognac",
+ "coltrui",
+ "comfort",
+ "commandant",
+ "condensaat",
+ "confectie",
+ "conifeer",
+ "convector",
+ "copier",
+ "corfu",
+ "correct",
+ "coup",
+ "couvert",
+ "creatie",
+ "credit",
+ "crematie",
+ "cricket",
+ "croupier",
+ "cruciaal",
+ "cruijff",
+ "cuisine",
+ "culemborg",
+ "culinair",
+ "curve",
+ "cyrano",
+ "dactylus",
+ "dading",
+ "dagblind",
+ "dagje",
+ "daglicht",
+ "dagprijs",
+ "dagranden",
+ "dakdekker",
+ "dakpark",
+ "dakterras",
+ "dalgrond",
+ "dambord",
+ "damkat",
+ "damlengte",
+ "damman",
+ "danenberg",
+ "debbie",
+ "decibel",
+ "defect",
+ "deformeer",
+ "degelijk",
+ "degradant",
+ "dejonghe",
+ "dekken",
+ "deppen",
+ "derek",
+ "derf",
+ "derhalve",
+ "detineren",
+ "devalueer",
+ "diaken",
+ "dicht",
+ "dictaat",
+ "dief",
+ "digitaal",
+ "dijbreuk",
+ "dijkmans",
+ "dimbaar",
+ "dinsdag",
+ "diode",
+ "dirigeer",
+ "disbalans",
+ "dobermann",
+ "doenbaar",
+ "doerak",
+ "dogma",
+ "dokhaven",
+ "dokwerker",
+ "doling",
+ "dolphijn",
+ "dolven",
+ "dombo",
+ "dooraderd",
+ "dopeling",
+ "doping",
+ "draderig",
+ "drama",
+ "drenkbak",
+ "dreumes",
+ "drol",
+ "drug",
+ "duaal",
+ "dublin",
+ "duplicaat",
+ "durven",
+ "dusdanig",
+ "dutchbat",
+ "dutje",
+ "dutten",
+ "duur",
+ "duwwerk",
+ "dwaal",
+ "dweil",
+ "dwing",
+ "dyslexie",
+ "ecostroom",
+ "ecotaks",
+ "educatie",
+ "eeckhout",
+ "eede",
+ "eemland",
+ "eencellig",
+ "eeneiig",
+ "eenruiter",
+ "eenwinter",
+ "eerenberg",
+ "eerrover",
+ "eersel",
+ "eetmaal",
+ "efteling",
+ "egaal",
+ "egtberts",
+ "eickhoff",
+ "eidooier",
+ "eiland",
+ "eind",
+ "eisden",
+ "ekster",
+ "elburg",
+ "elevatie",
+ "elfkoppig",
+ "elfrink",
+ "elftal",
+ "elimineer",
+ "elleboog",
+ "elma",
+ "elodie",
+ "elsa",
+ "embleem",
+ "embolie",
+ "emoe",
+ "emonds",
+ "emplooi",
+ "enduro",
+ "enfin",
+ "engageer",
+ "entourage",
+ "entstof",
+ "epileer",
+ "episch",
+ "eppo",
+ "erasmus",
+ "erboven",
+ "erebaan",
+ "erelijst",
+ "ereronden",
+ "ereteken",
+ "erfhuis",
+ "erfwet",
+ "erger",
+ "erica",
+ "ermitage",
+ "erna",
+ "ernie",
+ "erts",
+ "ertussen",
+ "eruitzien",
+ "ervaar",
+ "erven",
+ "erwt",
+ "esbeek",
+ "escort",
+ "esdoorn",
+ "essing",
+ "etage",
+ "eter",
+ "ethanol",
+ "ethicus",
+ "etholoog",
+ "eufonisch",
+ "eurocent",
+ "evacuatie",
+ "exact",
+ "examen",
+ "executant",
+ "exen",
+ "exit",
+ "exogeen",
+ "exotherm",
+ "expeditie",
+ "expletief",
+ "expres",
+ "extase",
+ "extinctie",
+ "faal",
+ "faam",
+ "fabel",
+ "facultair",
+ "fakir",
+ "fakkel",
+ "faliekant",
+ "fallisch",
+ "famke",
+ "fanclub",
+ "fase",
+ "fatsoen",
+ "fauna",
+ "federaal",
+ "feedback",
+ "feest",
+ "feilbaar",
+ "feitelijk",
+ "felblauw",
+ "figurante",
+ "fiod",
+ "fitheid",
+ "fixeer",
+ "flap",
+ "fleece",
+ "fleur",
+ "flexibel",
+ "flits",
+ "flos",
+ "flow",
+ "fluweel",
+ "foezelen",
+ "fokkelman",
+ "fokpaard",
+ "fokvee",
+ "folder",
+ "follikel",
+ "folmer",
+ "folteraar",
+ "fooi",
+ "foolen",
+ "forfait",
+ "forint",
+ "formule",
+ "fornuis",
+ "fosfaat",
+ "foxtrot",
+ "foyer",
+ "fragiel",
+ "frater",
+ "freak",
+ "freddie",
+ "fregat",
+ "freon",
+ "frijnen",
+ "fructose",
+ "frunniken",
+ "fuiven",
+ "funshop",
+ "furieus",
+ "fysica",
+ "gadget",
+ "galder",
+ "galei",
+ "galg",
+ "galvlieg",
+ "galzuur",
+ "ganesh",
+ "gaswet",
+ "gaza",
+ "gazelle",
+ "geaaid",
+ "gebiecht",
+ "gebufferd",
+ "gedijd",
+ "geef",
+ "geflanst",
+ "gefreesd",
+ "gegaan",
+ "gegijzeld",
+ "gegniffel",
+ "gegraaid",
+ "gehikt",
+ "gehobbeld",
+ "gehucht",
+ "geiser",
+ "geiten",
+ "gekaakt",
+ "gekheid",
+ "gekijf",
+ "gekmakend",
+ "gekocht",
+ "gekskap",
+ "gekte",
+ "gelubberd",
+ "gemiddeld",
+ "geordend",
+ "gepoederd",
+ "gepuft",
+ "gerda",
+ "gerijpt",
+ "geseald",
+ "geshockt",
+ "gesierd",
+ "geslaagd",
+ "gesnaaid",
+ "getracht",
+ "getwijfel",
+ "geuit",
+ "gevecht",
+ "gevlagd",
+ "gewicht",
+ "gezaagd",
+ "gezocht",
+ "ghanees",
+ "giebelen",
+ "giechel",
+ "giepmans",
+ "gips",
+ "giraal",
+ "gistachtig",
+ "gitaar",
+ "glaasje",
+ "gletsjer",
+ "gleuf",
+ "glibberen",
+ "glijbaan",
+ "gloren",
+ "gluipen",
+ "gluren",
+ "gluur",
+ "gnoe",
+ "goddelijk",
+ "godgans",
+ "godschalk",
+ "godzalig",
+ "goeierd",
+ "gogme",
+ "goklustig",
+ "gokwereld",
+ "gonggrijp",
+ "gonje",
+ "goor",
+ "grabbel",
+ "graf",
+ "graveer",
+ "grif",
+ "grolleman",
+ "grom",
+ "groosman",
+ "grubben",
+ "gruijs",
+ "grut",
+ "guacamole",
+ "guido",
+ "guppy",
+ "haazen",
+ "hachelijk",
+ "haex",
+ "haiku",
+ "hakhout",
+ "hakken",
+ "hanegem",
+ "hans",
+ "hanteer",
+ "harrie",
+ "hazebroek",
+ "hedonist",
+ "heil",
+ "heineken",
+ "hekhuis",
+ "hekman",
+ "helbig",
+ "helga",
+ "helwegen",
+ "hengelaar",
+ "herkansen",
+ "hermafrodiet",
+ "hertaald",
+ "hiaat",
+ "hikspoors",
+ "hitachi",
+ "hitparade",
+ "hobo",
+ "hoeve",
+ "holocaust",
+ "hond",
+ "honnepon",
+ "hoogacht",
+ "hotelbed",
+ "hufter",
+ "hugo",
+ "huilbier",
+ "hulk",
+ "humus",
+ "huwbaar",
+ "huwelijk",
+ "hype",
+ "iconisch",
+ "idema",
+ "ideogram",
+ "idolaat",
+ "ietje",
+ "ijker",
+ "ijkheid",
+ "ijklijn",
+ "ijkmaat",
+ "ijkwezen",
+ "ijmuiden",
+ "ijsbox",
+ "ijsdag",
+ "ijselijk",
+ "ijskoud",
+ "ilse",
+ "immuun",
+ "impliceer",
+ "impuls",
+ "inbijten",
+ "inbuigen",
+ "indijken",
+ "induceer",
+ "indy",
+ "infecteer",
+ "inhaak",
+ "inkijk",
+ "inluiden",
+ "inmijnen",
+ "inoefenen",
+ "inpolder",
+ "inrijden",
+ "inslaan",
+ "invitatie",
+ "inwaaien",
+ "ionisch",
+ "isaac",
+ "isolatie",
+ "isotherm",
+ "isra",
+ "italiaan",
+ "ivoor",
+ "jacobs",
+ "jakob",
+ "jammen",
+ "jampot",
+ "jarig",
+ "jehova",
+ "jenever",
+ "jezus",
+ "joana",
+ "jobdienst",
+ "josua",
+ "joule",
+ "juich",
+ "jurk",
+ "juut",
+ "kaas",
+ "kabelaar",
+ "kabinet",
+ "kagenaar",
+ "kajuit",
+ "kalebas",
+ "kalm",
+ "kanjer",
+ "kapucijn",
+ "karregat",
+ "kart",
+ "katvanger",
+ "katwijk",
+ "kegelaar",
+ "keiachtig",
+ "keizer",
+ "kenletter",
+ "kerdijk",
+ "keus",
+ "kevlar",
+ "kezen",
+ "kickback",
+ "kieviet",
+ "kijken",
+ "kikvors",
+ "kilheid",
+ "kilobit",
+ "kilsdonk",
+ "kipschnitzel",
+ "kissebis",
+ "klad",
+ "klagelijk",
+ "klak",
+ "klapbaar",
+ "klaver",
+ "klene",
+ "klets",
+ "klijnhout",
+ "klit",
+ "klok",
+ "klonen",
+ "klotefilm",
+ "kluif",
+ "klumper",
+ "klus",
+ "knabbel",
+ "knagen",
+ "knaven",
+ "kneedbaar",
+ "knmi",
+ "knul",
+ "knus",
+ "kokhals",
+ "komiek",
+ "komkommer",
+ "kompaan",
+ "komrij",
+ "komvormig",
+ "koning",
+ "kopbal",
+ "kopklep",
+ "kopnagel",
+ "koppejan",
+ "koptekst",
+ "kopwand",
+ "koraal",
+ "kosmisch",
+ "kostbaar",
+ "kram",
+ "kraneveld",
+ "kras",
+ "kreling",
+ "krengen",
+ "kribbe",
+ "krik",
+ "kruid",
+ "krulbol",
+ "kuijper",
+ "kuipbank",
+ "kuit",
+ "kuiven",
+ "kutsmoes",
+ "kuub",
+ "kwak",
+ "kwatong",
+ "kwetsbaar",
+ "kwezelaar",
+ "kwijnen",
+ "kwik",
+ "kwinkslag",
+ "kwitantie",
+ "lading",
+ "lakbeits",
+ "lakken",
+ "laklaag",
+ "lakmoes",
+ "lakwijk",
+ "lamheid",
+ "lamp",
+ "lamsbout",
+ "lapmiddel",
+ "larve",
+ "laser",
+ "latijn",
+ "latuw",
+ "lawaai",
+ "laxeerpil",
+ "lebberen",
+ "ledeboer",
+ "leefbaar",
+ "leeman",
+ "lefdoekje",
+ "lefhebber",
+ "legboor",
+ "legsel",
+ "leguaan",
+ "leiplaat",
+ "lekdicht",
+ "lekrijden",
+ "leksteen",
+ "lenen",
+ "leraar",
+ "lesbienne",
+ "leugenaar",
+ "leut",
+ "lexicaal",
+ "lezing",
+ "lieten",
+ "liggeld",
+ "lijdzaam",
+ "lijk",
+ "lijmstang",
+ "lijnschip",
+ "likdoorn",
+ "likken",
+ "liksteen",
+ "limburg",
+ "link",
+ "linoleum",
+ "lipbloem",
+ "lipman",
+ "lispelen",
+ "lissabon",
+ "litanie",
+ "liturgie",
+ "lochem",
+ "loempia",
+ "loesje",
+ "logheid",
+ "lonen",
+ "lonneke",
+ "loom",
+ "loos",
+ "losbaar",
+ "loslaten",
+ "losplaats",
+ "loting",
+ "lotnummer",
+ "lots",
+ "louie",
+ "lourdes",
+ "louter",
+ "lowbudget",
+ "luijten",
+ "luikenaar",
+ "luilak",
+ "luipaard",
+ "luizenbos",
+ "lulkoek",
+ "lumen",
+ "lunzen",
+ "lurven",
+ "lutjeboer",
+ "luttel",
+ "lutz",
+ "luuk",
+ "luwte",
+ "luyendijk",
+ "lyceum",
+ "lynx",
+ "maakbaar",
+ "magdalena",
+ "malheid",
+ "manchet",
+ "manfred",
+ "manhaftig",
+ "mank",
+ "mantel",
+ "marion",
+ "marxist",
+ "masmeijer",
+ "massaal",
+ "matsen",
+ "matverf",
+ "matze",
+ "maude",
+ "mayonaise",
+ "mechanica",
+ "meifeest",
+ "melodie",
+ "meppelink",
+ "midvoor",
+ "midweeks",
+ "midzomer",
+ "miezel",
+ "mijnraad",
+ "minus",
+ "mirck",
+ "mirte",
+ "mispakken",
+ "misraden",
+ "miswassen",
+ "mitella",
+ "moker",
+ "molecule",
+ "mombakkes",
+ "moonen",
+ "mopperaar",
+ "moraal",
+ "morgana",
+ "mormel",
+ "mosselaar",
+ "motregen",
+ "mouw",
+ "mufheid",
+ "mutueel",
+ "muzelman",
+ "naaidoos",
+ "naald",
+ "nadeel",
+ "nadruk",
+ "nagy",
+ "nahon",
+ "naima",
+ "nairobi",
+ "napalm",
+ "napels",
+ "napijn",
+ "napoleon",
+ "narigheid",
+ "narratief",
+ "naseizoen",
+ "nasibal",
+ "navigatie",
+ "nawijn",
+ "negatief",
+ "nekletsel",
+ "nekwervel",
+ "neolatijn",
+ "neonataal",
+ "neptunus",
+ "nerd",
+ "nest",
+ "neuzelaar",
+ "nihiliste",
+ "nijenhuis",
+ "nijging",
+ "nijhoff",
+ "nijl",
+ "nijptang",
+ "nippel",
+ "nokkenas",
+ "noordam",
+ "noren",
+ "normaal",
+ "nottelman",
+ "notulant",
+ "nout",
+ "nuance",
+ "nuchter",
+ "nudorp",
+ "nulde",
+ "nullijn",
+ "nulmeting",
+ "nunspeet",
+ "nylon",
+ "obelisk",
+ "object",
+ "oblie",
+ "obsceen",
+ "occlusie",
+ "oceaan",
+ "ochtend",
+ "ockhuizen",
+ "oerdom",
+ "oergezond",
+ "oerlaag",
+ "oester",
+ "okhuijsen",
+ "olifant",
+ "olijfboer",
+ "omaans",
+ "ombudsman",
+ "omdat",
+ "omdijken",
+ "omdoen",
+ "omgebouwd",
+ "omkeer",
+ "omkomen",
+ "ommegaand",
+ "ommuren",
+ "omroep",
+ "omruil",
+ "omslaan",
+ "omsmeden",
+ "omvaar",
+ "onaardig",
+ "onedel",
+ "onenig",
+ "onheilig",
+ "onrecht",
+ "onroerend",
+ "ontcijfer",
+ "onthaal",
+ "ontvallen",
+ "ontzadeld",
+ "onzacht",
+ "onzin",
+ "onzuiver",
+ "oogappel",
+ "ooibos",
+ "ooievaar",
+ "ooit",
+ "oorarts",
+ "oorhanger",
+ "oorijzer",
+ "oorklep",
+ "oorschelp",
+ "oorworm",
+ "oorzaak",
+ "opdagen",
+ "opdien",
+ "opdweilen",
+ "opel",
+ "opgebaard",
+ "opinie",
+ "opjutten",
+ "opkijken",
+ "opklaar",
+ "opkuisen",
+ "opkwam",
+ "opnaaien",
+ "opossum",
+ "opsieren",
+ "opsmeer",
+ "optreden",
+ "opvijzel",
+ "opvlammen",
+ "opwind",
+ "oraal",
+ "orchidee",
+ "orkest",
+ "ossuarium",
+ "ostendorf",
+ "oublie",
+ "oudachtig",
+ "oudbakken",
+ "oudnoors",
+ "oudshoorn",
+ "oudtante",
+ "oven",
+ "over",
+ "oxidant",
+ "pablo",
+ "pacht",
+ "paktafel",
+ "pakzadel",
+ "paljas",
+ "panharing",
+ "papfles",
+ "paprika",
+ "parochie",
+ "paus",
+ "pauze",
+ "paviljoen",
+ "peek",
+ "pegel",
+ "peigeren",
+ "pekela",
+ "pendant",
+ "penibel",
+ "pepmiddel",
+ "peptalk",
+ "periferie",
+ "perron",
+ "pessarium",
+ "peter",
+ "petfles",
+ "petgat",
+ "peuk",
+ "pfeifer",
+ "picknick",
+ "pief",
+ "pieneman",
+ "pijlkruid",
+ "pijnacker",
+ "pijpelink",
+ "pikdonker",
+ "pikeer",
+ "pilaar",
+ "pionier",
+ "pipet",
+ "piscine",
+ "pissebed",
+ "pitchen",
+ "pixel",
+ "plamuren",
+ "plan",
+ "plausibel",
+ "plegen",
+ "plempen",
+ "pleonasme",
+ "plezant",
+ "podoloog",
+ "pofmouw",
+ "pokdalig",
+ "ponywagen",
+ "popachtig",
+ "popidool",
+ "porren",
+ "positie",
+ "potten",
+ "pralen",
+ "prezen",
+ "prijzen",
+ "privaat",
+ "proef",
+ "prooi",
+ "prozawerk",
+ "pruik",
+ "prul",
+ "publiceer",
+ "puck",
+ "puilen",
+ "pukkelig",
+ "pulveren",
+ "pupil",
+ "puppy",
+ "purmerend",
+ "pustjens",
+ "putemmer",
+ "puzzelaar",
+ "queenie",
+ "quiche",
+ "raam",
+ "raar",
+ "raat",
+ "raes",
+ "ralf",
+ "rally",
+ "ramona",
+ "ramselaar",
+ "ranonkel",
+ "rapen",
+ "rapunzel",
+ "rarekiek",
+ "rarigheid",
+ "rattenhol",
+ "ravage",
+ "reactie",
+ "recreant",
+ "redacteur",
+ "redster",
+ "reewild",
+ "regie",
+ "reijnders",
+ "rein",
+ "replica",
+ "revanche",
+ "rigide",
+ "rijbaan",
+ "rijdansen",
+ "rijgen",
+ "rijkdom",
+ "rijles",
+ "rijnwijn",
+ "rijpma",
+ "rijstafel",
+ "rijtaak",
+ "rijzwepen",
+ "rioleer",
+ "ripdeal",
+ "riphagen",
+ "riskant",
+ "rits",
+ "rivaal",
+ "robbedoes",
+ "robot",
+ "rockact",
+ "rodijk",
+ "rogier",
+ "rohypnol",
+ "rollaag",
+ "rolpaal",
+ "roltafel",
+ "roof",
+ "roon",
+ "roppen",
+ "rosbief",
+ "rosharig",
+ "rosielle",
+ "rotan",
+ "rotleven",
+ "rotten",
+ "rotvaart",
+ "royaal",
+ "royeer",
+ "rubato",
+ "ruby",
+ "ruche",
+ "rudge",
+ "ruggetje",
+ "rugnummer",
+ "rugpijn",
+ "rugtitel",
+ "rugzak",
+ "ruilbaar",
+ "ruis",
+ "ruit",
+ "rukwind",
+ "rulijs",
+ "rumoeren",
+ "rumsdorp",
+ "rumtaart",
+ "runnen",
+ "russchen",
+ "ruwkruid",
+ "saboteer",
+ "saksisch",
+ "salade",
+ "salpeter",
+ "sambabal",
+ "samsam",
+ "satelliet",
+ "satineer",
+ "saus",
+ "scampi",
+ "scarabee",
+ "scenario",
+ "schobben",
+ "schubben",
+ "scout",
+ "secessie",
+ "secondair",
+ "seculair",
+ "sediment",
+ "seeland",
+ "settelen",
+ "setwinst",
+ "sheriff",
+ "shiatsu",
+ "siciliaan",
+ "sidderaal",
+ "sigma",
+ "sijben",
+ "silvana",
+ "simkaart",
+ "sinds",
+ "situatie",
+ "sjaak",
+ "sjardijn",
+ "sjezen",
+ "sjor",
+ "skinhead",
+ "skylab",
+ "slamixen",
+ "sleijpen",
+ "slijkerig",
+ "slordig",
+ "slowaak",
+ "sluieren",
+ "smadelijk",
+ "smiecht",
+ "smoel",
+ "smos",
+ "smukken",
+ "snackcar",
+ "snavel",
+ "sneaker",
+ "sneu",
+ "snijdbaar",
+ "snit",
+ "snorder",
+ "soapbox",
+ "soetekouw",
+ "soigneren",
+ "sojaboon",
+ "solo",
+ "solvabel",
+ "somber",
+ "sommatie",
+ "soort",
+ "soppen",
+ "sopraan",
+ "soundbar",
+ "spanen",
+ "spawater",
+ "spijgat",
+ "spinaal",
+ "spionage",
+ "spiraal",
+ "spleet",
+ "splijt",
+ "spoed",
+ "sporen",
+ "spul",
+ "spuug",
+ "spuw",
+ "stalen",
+ "standaard",
+ "star",
+ "stefan",
+ "stencil",
+ "stijf",
+ "stil",
+ "stip",
+ "stopdas",
+ "stoten",
+ "stoven",
+ "straat",
+ "strobbe",
+ "strubbel",
+ "stucadoor",
+ "stuif",
+ "stukadoor",
+ "subhoofd",
+ "subregent",
+ "sudoku",
+ "sukade",
+ "sulfaat",
+ "surinaams",
+ "suus",
+ "syfilis",
+ "symboliek",
+ "sympathie",
+ "synagoge",
+ "synchroon",
+ "synergie",
+ "systeem",
+ "taanderij",
+ "tabak",
+ "tachtig",
+ "tackelen",
+ "taiwanees",
+ "talman",
+ "tamheid",
+ "tangaslip",
+ "taps",
+ "tarkan",
+ "tarwe",
+ "tasman",
+ "tatjana",
+ "taxameter",
+ "teil",
+ "teisman",
+ "telbaar",
+ "telco",
+ "telganger",
+ "telstar",
+ "tenant",
+ "tepel",
+ "terzet",
+ "testament",
+ "ticket",
+ "tiesinga",
+ "tijdelijk",
+ "tika",
+ "tiksel",
+ "tilleman",
+ "timbaal",
+ "tinsteen",
+ "tiplijn",
+ "tippelaar",
+ "tjirpen",
+ "toezeggen",
+ "tolbaas",
+ "tolgeld",
+ "tolhek",
+ "tolo",
+ "tolpoort",
+ "toltarief",
+ "tolvrij",
+ "tomaat",
+ "tondeuse",
+ "toog",
+ "tooi",
+ "toonbaar",
+ "toos",
+ "topclub",
+ "toppen",
+ "toptalent",
+ "topvrouw",
+ "toque",
+ "torment",
+ "tornado",
+ "tosti",
+ "totdat",
+ "toucheer",
+ "toulouse",
+ "tournedos",
+ "tout",
+ "trabant",
+ "tragedie",
+ "trailer",
+ "traject",
+ "traktaat",
+ "trauma",
+ "tray",
+ "trechter",
+ "tred",
+ "tref",
+ "treur",
+ "troebel",
+ "tros",
+ "trucage",
+ "truffel",
+ "tsaar",
+ "tucht",
+ "tuenter",
+ "tuitelig",
+ "tukje",
+ "tuktuk",
+ "tulp",
+ "tuma",
+ "tureluurs",
+ "twijfel",
+ "twitteren",
+ "tyfoon",
+ "typograaf",
+ "ugandees",
+ "uiachtig",
+ "uier",
+ "uisnipper",
+ "ultiem",
+ "unitair",
+ "uranium",
+ "urbaan",
+ "urendag",
+ "ursula",
+ "uurcirkel",
+ "uurglas",
+ "uzelf",
+ "vaat",
+ "vakantie",
+ "vakleraar",
+ "valbijl",
+ "valpartij",
+ "valreep",
+ "valuatie",
+ "vanmiddag",
+ "vanonder",
+ "varaan",
+ "varken",
+ "vaten",
+ "veenbes",
+ "veeteler",
+ "velgrem",
+ "vellekoop",
+ "velvet",
+ "veneberg",
+ "venlo",
+ "vent",
+ "venusberg",
+ "venw",
+ "veredeld",
+ "verf",
+ "verhaaf",
+ "vermaak",
+ "vernaaid",
+ "verraad",
+ "vers",
+ "veruit",
+ "verzaagd",
+ "vetachtig",
+ "vetlok",
+ "vetmesten",
+ "veto",
+ "vetrek",
+ "vetstaart",
+ "vetten",
+ "veurink",
+ "viaduct",
+ "vibrafoon",
+ "vicariaat",
+ "vieux",
+ "vieveen",
+ "vijfvoud",
+ "villa",
+ "vilt",
+ "vimmetje",
+ "vindbaar",
+ "vips",
+ "virtueel",
+ "visdieven",
+ "visee",
+ "visie",
+ "vlaag",
+ "vleugel",
+ "vmbo",
+ "vocht",
+ "voesenek",
+ "voicemail",
+ "voip",
+ "volg",
+ "vork",
+ "vorselaar",
+ "voyeur",
+ "vracht",
+ "vrekkig",
+ "vreten",
+ "vrije",
+ "vrozen",
+ "vrucht",
+ "vucht",
+ "vugt",
+ "vulkaan",
+ "vulmiddel",
+ "vulva",
+ "vuren",
+ "waas",
+ "wacht",
+ "wadvogel",
+ "wafel",
+ "waffel",
+ "walhalla",
+ "walnoot",
+ "walraven",
+ "wals",
+ "walvis",
+ "wandaad",
+ "wanen",
+ "wanmolen",
+ "want",
+ "warklomp",
+ "warm",
+ "wasachtig",
+ "wasteil",
+ "watt",
+ "webhandel",
+ "weblog",
+ "webpagina",
+ "webzine",
+ "wedereis",
+ "wedstrijd",
+ "weeda",
+ "weert",
+ "wegmaaien",
+ "wegscheer",
+ "wekelijks",
+ "wekken",
+ "wekroep",
+ "wektoon",
+ "weldaad",
+ "welwater",
+ "wendbaar",
+ "wenkbrauw",
+ "wens",
+ "wentelaar",
+ "wervel",
+ "wesseling",
+ "wetboek",
+ "wetmatig",
+ "whirlpool",
+ "wijbrands",
+ "wijdbeens",
+ "wijk",
+ "wijnbes",
+ "wijting",
+ "wild",
+ "wimpelen",
+ "wingebied",
+ "winplaats",
+ "winter",
+ "winzucht",
+ "wipstaart",
+ "wisgerhof",
+ "withaar",
+ "witmaker",
+ "wokkel",
+ "wolf",
+ "wonenden",
+ "woning",
+ "worden",
+ "worp",
+ "wortel",
+ "wrat",
+ "wrijf",
+ "wringen",
+ "yoghurt",
+ "ypsilon",
+ "zaaijer",
+ "zaak",
+ "zacharias",
+ "zakelijk",
+ "zakkam",
+ "zakwater",
+ "zalf",
+ "zalig",
+ "zaniken",
+ "zebracode",
+ "zeeblauw",
+ "zeef",
+ "zeegaand",
+ "zeeuw",
+ "zege",
+ "zegje",
+ "zeil",
+ "zesbaans",
+ "zesenhalf",
+ "zeskantig",
+ "zesmaal",
+ "zetbaas",
+ "zetpil",
+ "zeulen",
+ "ziezo",
+ "zigzag",
+ "zijaltaar",
+ "zijbeuk",
+ "zijlijn",
+ "zijmuur",
+ "zijn",
+ "zijwaarts",
+ "zijzelf",
+ "zilt",
+ "zimmerman",
+ "zinledig",
+ "zinnelijk",
+ "zionist",
+ "zitdag",
+ "zitruimte",
+ "zitzak",
+ "zoal",
+ "zodoende",
+ "zoekbots",
+ "zoem",
+ "zoiets",
+ "zojuist",
+ "zondaar",
+ "zotskap",
+ "zottebol",
+ "zucht",
+ "zuivel",
+ "zulk",
+ "zult",
+ "zuster",
+ "zuur",
+ "zweedijk",
+ "zwendel",
+ "zwepen",
+ "zwiep",
+ "zwijmel",
+ "zworen"
+ }), 4)
+ {
+ populate_maps();
+ }
+ };
+}
+
+#endif
diff --git a/src/mnemonics/electrum-words.cpp b/src/mnemonics/electrum-words.cpp index f0e254ba4..501495f0b 100644 --- a/src/mnemonics/electrum-words.cpp +++ b/src/mnemonics/electrum-words.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -51,7 +51,10 @@ #include <boost/crc.hpp> #include <boost/algorithm/string/join.hpp> +#include "chinese_simplified.h" #include "english.h" +#include "dutch.h" +#include "french.h" #include "italian.h" #include "german.h" #include "spanish.h" @@ -82,7 +85,10 @@ namespace { // If there's a new language added, add an instance of it here. std::vector<Language::Base*> language_instances({ + Language::Singleton<Language::Chinese_Simplified>::instance(), Language::Singleton<Language::English>::instance(), + Language::Singleton<Language::Dutch>::instance(), + Language::Singleton<Language::French>::instance(), Language::Singleton<Language::Spanish>::instance(), Language::Singleton<Language::German>::instance(), Language::Singleton<Language::Italian>::instance(), @@ -236,7 +242,7 @@ namespace crypto std::vector<std::string> seed; boost::algorithm::trim(words); - boost::split(seed, words, boost::is_any_of(" ")); + boost::split(seed, words, boost::is_any_of(" "), boost::token_compress_on); // error on non-compliant word list if (seed.size() != seed_length/2 && seed.size() != seed_length && @@ -312,6 +318,14 @@ namespace crypto { language = Language::Singleton<Language::English>::instance(); } + else if (language_name == "Dutch") + { + language = Language::Singleton<Language::Dutch>::instance(); + } + else if (language_name == "French") + { + language = Language::Singleton<Language::French>::instance(); + } else if (language_name == "Spanish") { language = Language::Singleton<Language::Spanish>::instance(); @@ -336,6 +350,10 @@ namespace crypto { language = Language::Singleton<Language::Russian>::instance(); } + else if (language_name == "Chinese (Simplified)") + { + language = Language::Singleton<Language::Chinese_Simplified>::instance(); + } else { return false; @@ -381,7 +399,10 @@ namespace crypto void get_language_list(std::vector<std::string> &languages) { std::vector<Language::Base*> language_instances({ + Language::Singleton<Language::Chinese_Simplified>::instance(), Language::Singleton<Language::English>::instance(), + Language::Singleton<Language::Dutch>::instance(), + Language::Singleton<Language::French>::instance(), Language::Singleton<Language::Spanish>::instance(), Language::Singleton<Language::German>::instance(), Language::Singleton<Language::Italian>::instance(), diff --git a/src/mnemonics/electrum-words.h b/src/mnemonics/electrum-words.h index 29a9f62f2..d1aa65939 100644 --- a/src/mnemonics/electrum-words.h +++ b/src/mnemonics/electrum-words.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/mnemonics/english.h b/src/mnemonics/english.h index c42b752b1..e6cfa8951 100644 --- a/src/mnemonics/english.h +++ b/src/mnemonics/english.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -49,9 +49,7 @@ namespace Language class English: public Base
{
public:
- English()
- {
- word_list = new std::vector<std::string>({
+ English(): Base("English", std::vector<std::string>({
"abbey",
"abducts",
"ability",
@@ -1678,11 +1676,8 @@ namespace Language "zombie",
"zones",
"zoom"
- });
- unique_prefix_length = 3;
- word_map = new std::unordered_map<std::string, uint32_t>;
- trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
- language_name = "English";
+ }), 3)
+ {
populate_maps();
}
};
diff --git a/src/mnemonics/french.h b/src/mnemonics/french.h new file mode 100644 index 000000000..6cf44a197 --- /dev/null +++ b/src/mnemonics/french.h @@ -0,0 +1,1686 @@ +// Copyright (c) 2014-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/*! + * \file french.h + * + * \brief French word list and map. + */ + +#ifndef FRENCH_H +#define FRENCH_H + +#include <vector> +#include <unordered_map> +#include "language_base.h" +#include <string> + +/*! + * \namespace Language + * \brief Mnemonic language related namespace. + */ +namespace Language +{ + class French: public Base + { + public: + French(): Base("French", std::vector<std::string>({ + "abandon", + "abattre", + "aboi", + "abolir", + "aborder", + "abri", + "absence", + "absolu", + "abuser", + "acacia", + "acajou", + "accent", + "accord", + "accrocher", + "accuser", + "acerbe", + "achat", + "acheter", + "acide", + "acier", + "acquis", + "acte", + "action", + "adage", + "adepte", + "adieu", + "admettre", + "admis", + "adorer", + "adresser", + "aduler", + "affaire", + "affirmer", + "afin", + "agacer", + "agent", + "agir", + "agiter", + "agonie", + "agrafe", + "agrume", + "aider", + "aigle", + "aigre", + "aile", + "ailleurs", + "aimant", + "aimer", + "ainsi", + "aise", + "ajouter", + "alarme", + "album", + "alcool", + "alerte", + "algue", + "alibi", + "aller", + "allumer", + "alors", + "amande", + "amener", + "amie", + "amorcer", + "amour", + "ample", + "amuser", + "ananas", + "ancien", + "anglais", + "angoisse", + "animal", + "anneau", + "annoncer", + "apercevoir", + "apparence", + "appel", + "apporter", + "apprendre", + "appuyer", + "arbre", + "arcade", + "arceau", + "arche", + "ardeur", + "argent", + "argile", + "aride", + "arme", + "armure", + "arracher", + "arriver", + "article", + "asile", + "aspect", + "assaut", + "assez", + "assister", + "assurer", + "astre", + "astuce", + "atlas", + "atroce", + "attacher", + "attente", + "attirer", + "aube", + "aucun", + "audace", + "auparavant", + "auquel", + "aurore", + "aussi", + "autant", + "auteur", + "autoroute", + "autre", + "aval", + "avant", + "avec", + "avenir", + "averse", + "aveu", + "avide", + "avion", + "avis", + "avoir", + "avouer", + "avril", + "azote", + "azur", + "badge", + "bagage", + "bague", + "bain", + "baisser", + "balai", + "balcon", + "balise", + "balle", + "bambou", + "banane", + "banc", + "bandage", + "banjo", + "banlieue", + "bannir", + "banque", + "baobab", + "barbe", + "barque", + "barrer", + "bassine", + "bataille", + "bateau", + "battre", + "baver", + "bavoir", + "bazar", + "beau", + "beige", + "berger", + "besoin", + "beurre", + "biais", + "biceps", + "bidule", + "bien", + "bijou", + "bilan", + "billet", + "blanc", + "blason", + "bleu", + "bloc", + "blond", + "bocal", + "boire", + "boiserie", + "boiter", + "bonbon", + "bondir", + "bonheur", + "bordure", + "borgne", + "borner", + "bosse", + "bouche", + "bouder", + "bouger", + "boule", + "bourse", + "bout", + "boxe", + "brader", + "braise", + "branche", + "braquer", + "bras", + "brave", + "brebis", + "brevet", + "brider", + "briller", + "brin", + "brique", + "briser", + "broche", + "broder", + "bronze", + "brosser", + "brouter", + "bruit", + "brute", + "budget", + "buffet", + "bulle", + "bureau", + "buriner", + "buste", + "buter", + "butiner", + "cabas", + "cabinet", + "cabri", + "cacao", + "cacher", + "cadeau", + "cadre", + "cage", + "caisse", + "caler", + "calme", + "camarade", + "camion", + "campagne", + "canal", + "canif", + "capable", + "capot", + "carat", + "caresser", + "carie", + "carpe", + "cartel", + "casier", + "casque", + "casserole", + "cause", + "cavale", + "cave", + "ceci", + "cela", + "celui", + "cendre", + "cent", + "cependant", + "cercle", + "cerise", + "cerner", + "certes", + "cerveau", + "cesser", + "chacun", + "chair", + "chaleur", + "chamois", + "chanson", + "chaque", + "charge", + "chasse", + "chat", + "chaud", + "chef", + "chemin", + "cheveu", + "chez", + "chicane", + "chien", + "chiffre", + "chiner", + "chiot", + "chlore", + "choc", + "choix", + "chose", + "chou", + "chute", + "cibler", + "cidre", + "ciel", + "cigale", + "cinq", + "cintre", + "cirage", + "cirque", + "ciseau", + "citation", + "citer", + "citron", + "civet", + "clairon", + "clan", + "classe", + "clavier", + "clef", + "climat", + "cloche", + "cloner", + "clore", + "clos", + "clou", + "club", + "cobra", + "cocon", + "coiffer", + "coin", + "colline", + "colon", + "combat", + "comme", + "compte", + "conclure", + "conduire", + "confier", + "connu", + "conseil", + "contre", + "convenir", + "copier", + "cordial", + "cornet", + "corps", + "cosmos", + "coton", + "couche", + "coude", + "couler", + "coupure", + "cour", + "couteau", + "couvrir", + "crabe", + "crainte", + "crampe", + "cran", + "creuser", + "crever", + "crier", + "crime", + "crin", + "crise", + "crochet", + "croix", + "cruel", + "cuisine", + "cuite", + "culot", + "culte", + "cumul", + "cure", + "curieux", + "cuve", + "dame", + "danger", + "dans", + "davantage", + "debout", + "dedans", + "dehors", + "delta", + "demain", + "demeurer", + "demi", + "dense", + "dent", + "depuis", + "dernier", + "descendre", + "dessus", + "destin", + "dette", + "deuil", + "deux", + "devant", + "devenir", + "devin", + "devoir", + "dicton", + "dieu", + "difficile", + "digestion", + "digue", + "diluer", + "dimanche", + "dinde", + "diode", + "dire", + "diriger", + "discours", + "disposer", + "distance", + "divan", + "divers", + "docile", + "docteur", + "dodu", + "dogme", + "doigt", + "dominer", + "donation", + "donjon", + "donner", + "dopage", + "dorer", + "dormir", + "doseur", + "douane", + "double", + "douche", + "douleur", + "doute", + "doux", + "douzaine", + "draguer", + "drame", + "drap", + "dresser", + "droit", + "duel", + "dune", + "duper", + "durant", + "durcir", + "durer", + "eaux", + "effacer", + "effet", + "effort", + "effrayant", + "elle", + "embrasser", + "emmener", + "emparer", + "empire", + "employer", + "emporter", + "enclos", + "encore", + "endive", + "endormir", + "endroit", + "enduit", + "enfant", + "enfermer", + "enfin", + "enfler", + "enfoncer", + "enfuir", + "engager", + "engin", + "enjeu", + "enlever", + "ennemi", + "ennui", + "ensemble", + "ensuite", + "entamer", + "entendre", + "entier", + "entourer", + "entre", + "envelopper", + "envie", + "envoyer", + "erreur", + "escalier", + "espace", + "espoir", + "esprit", + "essai", + "essor", + "essuyer", + "estimer", + "exact", + "examiner", + "excuse", + "exemple", + "exiger", + "exil", + "exister", + "exode", + "expliquer", + "exposer", + "exprimer", + "extase", + "fable", + "facette", + "facile", + "fade", + "faible", + "faim", + "faire", + "fait", + "falloir", + "famille", + "faner", + "farce", + "farine", + "fatigue", + "faucon", + "faune", + "faute", + "faux", + "faveur", + "favori", + "faxer", + "feinter", + "femme", + "fendre", + "fente", + "ferme", + "festin", + "feuille", + "feutre", + "fiable", + "fibre", + "ficher", + "fier", + "figer", + "figure", + "filet", + "fille", + "filmer", + "fils", + "filtre", + "final", + "finesse", + "finir", + "fiole", + "firme", + "fixe", + "flacon", + "flair", + "flamme", + "flan", + "flaque", + "fleur", + "flocon", + "flore", + "flot", + "flou", + "fluide", + "fluor", + "flux", + "focus", + "foin", + "foire", + "foison", + "folie", + "fonction", + "fondre", + "fonte", + "force", + "forer", + "forger", + "forme", + "fort", + "fosse", + "fouet", + "fouine", + "foule", + "four", + "foyer", + "frais", + "franc", + "frapper", + "freiner", + "frimer", + "friser", + "frite", + "froid", + "froncer", + "fruit", + "fugue", + "fuir", + "fuite", + "fumer", + "fureur", + "furieux", + "fuser", + "fusil", + "futile", + "futur", + "gagner", + "gain", + "gala", + "galet", + "galop", + "gamme", + "gant", + "garage", + "garde", + "garer", + "gauche", + "gaufre", + "gaule", + "gaver", + "gazon", + "geler", + "genou", + "genre", + "gens", + "gercer", + "germer", + "geste", + "gibier", + "gicler", + "gilet", + "girafe", + "givre", + "glace", + "glisser", + "globe", + "gloire", + "gluant", + "gober", + "golf", + "gommer", + "gorge", + "gosier", + "goutte", + "grain", + "gramme", + "grand", + "gras", + "grave", + "gredin", + "griffure", + "griller", + "gris", + "gronder", + "gros", + "grotte", + "groupe", + "grue", + "guerrier", + "guetter", + "guider", + "guise", + "habiter", + "hache", + "haie", + "haine", + "halte", + "hamac", + "hanche", + "hangar", + "hanter", + "haras", + "hareng", + "harpe", + "hasard", + "hausse", + "haut", + "havre", + "herbe", + "heure", + "hibou", + "hier", + "histoire", + "hiver", + "hochet", + "homme", + "honneur", + "honte", + "horde", + "horizon", + "hormone", + "houle", + "housse", + "hublot", + "huile", + "huit", + "humain", + "humble", + "humide", + "humour", + "hurler", + "idole", + "igloo", + "ignorer", + "illusion", + "image", + "immense", + "immobile", + "imposer", + "impression", + "incapable", + "inconnu", + "index", + "indiquer", + "infime", + "injure", + "inox", + "inspirer", + "instant", + "intention", + "intime", + "inutile", + "inventer", + "inviter", + "iode", + "iris", + "issue", + "ivre", + "jade", + "jadis", + "jamais", + "jambe", + "janvier", + "jardin", + "jauge", + "jaunisse", + "jeter", + "jeton", + "jeudi", + "jeune", + "joie", + "joindre", + "joli", + "joueur", + "journal", + "judo", + "juge", + "juillet", + "juin", + "jument", + "jungle", + "jupe", + "jupon", + "jurer", + "juron", + "jury", + "jusque", + "juste", + "kayak", + "ketchup", + "kilo", + "kiwi", + "koala", + "label", + "lacet", + "lacune", + "laine", + "laisse", + "lait", + "lame", + "lancer", + "lande", + "laque", + "lard", + "largeur", + "larme", + "larve", + "lasso", + "laver", + "lendemain", + "lentement", + "lequel", + "lettre", + "leur", + "lever", + "levure", + "liane", + "libre", + "lien", + "lier", + "lieutenant", + "ligne", + "ligoter", + "liguer", + "limace", + "limer", + "limite", + "lingot", + "lion", + "lire", + "lisser", + "litre", + "livre", + "lobe", + "local", + "logis", + "loin", + "loisir", + "long", + "loque", + "lors", + "lotus", + "louer", + "loup", + "lourd", + "louve", + "loyer", + "lubie", + "lucide", + "lueur", + "luge", + "luire", + "lundi", + "lune", + "lustre", + "lutin", + "lutte", + "luxe", + "machine", + "madame", + "magie", + "magnifique", + "magot", + "maigre", + "main", + "mairie", + "maison", + "malade", + "malheur", + "malin", + "manche", + "manger", + "manier", + "manoir", + "manquer", + "marche", + "mardi", + "marge", + "mariage", + "marquer", + "mars", + "masque", + "masse", + "matin", + "mauvais", + "meilleur", + "melon", + "membre", + "menacer", + "mener", + "mensonge", + "mentir", + "menu", + "merci", + "merlu", + "mesure", + "mettre", + "meuble", + "meunier", + "meute", + "miche", + "micro", + "midi", + "miel", + "miette", + "mieux", + "milieu", + "mille", + "mimer", + "mince", + "mineur", + "ministre", + "minute", + "mirage", + "miroir", + "miser", + "mite", + "mixte", + "mobile", + "mode", + "module", + "moins", + "mois", + "moment", + "momie", + "monde", + "monsieur", + "monter", + "moquer", + "moral", + "morceau", + "mordre", + "morose", + "morse", + "mortier", + "morue", + "motif", + "motte", + "moudre", + "moule", + "mourir", + "mousse", + "mouton", + "mouvement", + "moyen", + "muer", + "muette", + "mugir", + "muguet", + "mulot", + "multiple", + "munir", + "muret", + "muse", + "musique", + "muter", + "nacre", + "nager", + "nain", + "naissance", + "narine", + "narrer", + "naseau", + "nasse", + "nation", + "nature", + "naval", + "navet", + "naviguer", + "navrer", + "neige", + "nerf", + "nerveux", + "neuf", + "neutre", + "neuve", + "neveu", + "niche", + "nier", + "niveau", + "noble", + "noce", + "nocif", + "noir", + "nomade", + "nombre", + "nommer", + "nord", + "norme", + "notaire", + "notice", + "notre", + "nouer", + "nougat", + "nourrir", + "nous", + "nouveau", + "novice", + "noyade", + "noyer", + "nuage", + "nuance", + "nuire", + "nuit", + "nulle", + "nuque", + "oasis", + "objet", + "obliger", + "obscur", + "observer", + "obtenir", + "obus", + "occasion", + "occuper", + "ocre", + "octet", + "odeur", + "odorat", + "offense", + "officier", + "offrir", + "ogive", + "oiseau", + "olive", + "ombre", + "onctueux", + "onduler", + "ongle", + "onze", + "opter", + "option", + "orageux", + "oral", + "orange", + "orbite", + "ordinaire", + "ordre", + "oreille", + "organe", + "orgie", + "orgueil", + "orient", + "origan", + "orner", + "orteil", + "ortie", + "oser", + "osselet", + "otage", + "otarie", + "ouate", + "oublier", + "ouest", + "ours", + "outil", + "outre", + "ouvert", + "ouvrir", + "ovale", + "ozone", + "pacte", + "page", + "paille", + "pain", + "paire", + "paix", + "palace", + "palissade", + "palmier", + "palpiter", + "panda", + "panneau", + "papa", + "papier", + "paquet", + "parc", + "pardi", + "parfois", + "parler", + "parmi", + "parole", + "partir", + "parvenir", + "passer", + "pastel", + "patin", + "patron", + "paume", + "pause", + "pauvre", + "paver", + "pavot", + "payer", + "pays", + "peau", + "peigne", + "peinture", + "pelage", + "pelote", + "pencher", + "pendre", + "penser", + "pente", + "percer", + "perdu", + "perle", + "permettre", + "personne", + "perte", + "peser", + "pesticide", + "petit", + "peuple", + "peur", + "phase", + "photo", + "phrase", + "piano", + "pied", + "pierre", + "pieu", + "pile", + "pilier", + "pilote", + "pilule", + "piment", + "pincer", + "pinson", + "pinte", + "pion", + "piquer", + "pirate", + "pire", + "piste", + "piton", + "pitre", + "pivot", + "pizza", + "placer", + "plage", + "plaire", + "plan", + "plaque", + "plat", + "plein", + "pleurer", + "pliage", + "plier", + "plonger", + "plot", + "pluie", + "plume", + "plus", + "pneu", + "poche", + "podium", + "poids", + "poil", + "point", + "poire", + "poison", + "poitrine", + "poivre", + "police", + "pollen", + "pomme", + "pompier", + "poncer", + "pondre", + "pont", + "portion", + "poser", + "position", + "possible", + "poste", + "potage", + "potin", + "pouce", + "poudre", + "poulet", + "poumon", + "poupe", + "pour", + "pousser", + "poutre", + "pouvoir", + "prairie", + "premier", + "prendre", + "presque", + "preuve", + "prier", + "primeur", + "prince", + "prison", + "priver", + "prix", + "prochain", + "produire", + "profond", + "proie", + "projet", + "promener", + "prononcer", + "propre", + "prose", + "prouver", + "prune", + "public", + "puce", + "pudeur", + "puiser", + "pull", + "pulpe", + "puma", + "punir", + "purge", + "putois", + "quand", + "quartier", + "quasi", + "quatre", + "quel", + "question", + "queue", + "quiche", + "quille", + "quinze", + "quitter", + "quoi", + "rabais", + "raboter", + "race", + "racheter", + "racine", + "racler", + "raconter", + "radar", + "radio", + "rafale", + "rage", + "ragot", + "raideur", + "raie", + "rail", + "raison", + "ramasser", + "ramener", + "rampe", + "rance", + "rang", + "rapace", + "rapide", + "rapport", + "rarement", + "rasage", + "raser", + "rasoir", + "rassurer", + "rater", + "ratio", + "rature", + "ravage", + "ravir", + "rayer", + "rayon", + "rebond", + "recevoir", + "recherche", + "record", + "reculer", + "redevenir", + "refuser", + "regard", + "regretter", + "rein", + "rejeter", + "rejoindre", + "relation", + "relever", + "religion", + "remarquer", + "remettre", + "remise", + "remonter", + "remplir", + "remuer", + "rencontre", + "rendre", + "renier", + "renoncer", + "rentrer", + "renverser", + "repas", + "repli", + "reposer", + "reproche", + "requin", + "respect", + "ressembler", + "reste", + "retard", + "retenir", + "retirer", + "retour", + "retrouver", + "revenir", + "revoir", + "revue", + "rhume", + "ricaner", + "riche", + "rideau", + "ridicule", + "rien", + "rigide", + "rincer", + "rire", + "risquer", + "rituel", + "rivage", + "rive", + "robe", + "robot", + "robuste", + "rocade", + "roche", + "rodeur", + "rogner", + "roman", + "rompre", + "ronce", + "rondeur", + "ronger", + "roque", + "rose", + "rosir", + "rotation", + "rotule", + "roue", + "rouge", + "rouler", + "route", + "ruban", + "rubis", + "ruche", + "rude", + "ruelle", + "ruer", + "rugby", + "rugir", + "ruine", + "rumeur", + "rural", + "ruse", + "rustre", + "sable", + "sabot", + "sabre", + "sacre", + "sage", + "saint", + "saisir", + "salade", + "salive", + "salle", + "salon", + "salto", + "salut", + "salve", + "samba", + "sandale", + "sanguin", + "sapin", + "sarcasme", + "satisfaire", + "sauce", + "sauf", + "sauge", + "saule", + "sauna", + "sauter", + "sauver", + "savoir", + "science", + "scoop", + "score", + "second", + "secret", + "secte", + "seigneur", + "sein", + "seize", + "selle", + "selon", + "semaine", + "sembler", + "semer", + "semis", + "sensuel", + "sentir", + "sept", + "serpe", + "serrer", + "sertir", + "service", + "seuil", + "seulement", + "short", + "sien", + "sigle", + "signal", + "silence", + "silo", + "simple", + "singe", + "sinon", + "sinus", + "sioux", + "sirop", + "site", + "situation", + "skier", + "snob", + "sobre", + "social", + "socle", + "sodium", + "soigner", + "soir", + "soixante", + "soja", + "solaire", + "soldat", + "soleil", + "solide", + "solo", + "solvant", + "sombre", + "somme", + "somnoler", + "sondage", + "songeur", + "sonner", + "sorte", + "sosie", + "sottise", + "souci", + "soudain", + "souffrir", + "souhaiter", + "soulever", + "soumettre", + "soupe", + "sourd", + "soustraire", + "soutenir", + "souvent", + "soyeux", + "spectacle", + "sport", + "stade", + "stagiaire", + "stand", + "star", + "statue", + "stock", + "stop", + "store", + "style", + "suave", + "subir", + "sucre", + "suer", + "suffire", + "suie", + "suite", + "suivre", + "sujet", + "sulfite", + "supposer", + "surf", + "surprendre", + "surtout", + "surveiller", + "tabac", + "table", + "tabou", + "tache", + "tacler", + "tacot", + "tact", + "taie", + "taille", + "taire", + "talon", + "talus", + "tandis", + "tango", + "tanin", + "tant", + "taper", + "tapis", + "tard", + "tarif", + "tarot", + "tarte", + "tasse", + "taureau", + "taux", + "taverne", + "taxer", + "taxi", + "tellement", + "temple", + "tendre", + "tenir", + "tenter", + "tenu", + "terme", + "ternir", + "terre", + "test", + "texte", + "thym", + "tibia", + "tiers", + "tige", + "tipi", + "tique", + "tirer", + "tissu", + "titre", + "toast", + "toge", + "toile", + "toiser", + "toiture", + "tomber", + "tome", + "tonne", + "tonte", + "toque", + "torse", + "tortue", + "totem", + "toucher", + "toujours", + "tour", + "tousser", + "tout", + "toux", + "trace", + "train", + "trame", + "tranquille", + "travail", + "trembler", + "trente", + "tribu", + "trier", + "trio", + "tripe", + "triste", + "troc", + "trois", + "tromper", + "tronc", + "trop", + "trotter", + "trouer", + "truc", + "truite", + "tuba", + "tuer", + "tuile", + "turbo", + "tutu", + "tuyau", + "type", + "union", + "unique", + "unir", + "unisson", + "untel", + "urne", + "usage", + "user", + "usiner", + "usure", + "utile", + "vache", + "vague", + "vaincre", + "valeur", + "valoir", + "valser", + "valve", + "vampire", + "vaseux", + "vaste", + "veau", + "veille", + "veine", + "velours", + "velu", + "vendre", + "venir", + "vent", + "venue", + "verbe", + "verdict", + "version", + "vertige", + "verve", + "veste", + "veto", + "vexer", + "vice", + "victime", + "vide", + "vieil", + "vieux", + "vigie", + "vigne", + "ville", + "vingt", + "violent", + "virer", + "virus", + "visage", + "viser", + "visite", + "visuel", + "vitamine", + "vitrine", + "vivant", + "vivre", + "vocal", + "vodka", + "vogue", + "voici", + "voile", + "voir", + "voisin", + "voiture", + "volaille", + "volcan", + "voler", + "volt", + "votant", + "votre", + "vouer", + "vouloir", + "vous", + "voyage", + "voyou", + "vrac", + "vrai", + "yacht", + "yeti", + "yeux", + "yoga", + "zeste", + "zinc", + "zone", + "zoom" + }), 4) + { + populate_maps(); + } + }; +} + +#endif diff --git a/src/mnemonics/german.h b/src/mnemonics/german.h index 675531453..1a1e6b467 100644 --- a/src/mnemonics/german.h +++ b/src/mnemonics/german.h @@ -1,6 +1,6 @@ // Word list created by Monero contributor Shrikez
//
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -51,9 +51,7 @@ namespace Language class German: public Base
{
public:
- German()
- {
- word_list = new std::vector<std::string>({
+ German(): Base("German", std::vector<std::string>({
"Abakus",
"Abart",
"abbilden",
@@ -1680,11 +1678,8 @@ namespace Language "Zündung",
"Zweck",
"Zyklop"
- });
- unique_prefix_length = 4;
- word_map = new std::unordered_map<std::string, uint32_t>;
- trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
- language_name = "German";
+ }), 4) + { populate_maps();
}
};
diff --git a/src/mnemonics/italian.h b/src/mnemonics/italian.h index 08852a97b..28cee9d9a 100644 --- a/src/mnemonics/italian.h +++ b/src/mnemonics/italian.h @@ -1,6 +1,6 @@ // Word list created by Monero contributor Shrikez
//
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -51,9 +51,7 @@ namespace Language class Italian: public Base
{
public:
- Italian()
- {
- word_list = new std::vector<std::string>({
+ Italian(): Base("Italian", std::vector<std::string>({
"abbinare",
"abbonato",
"abisso",
@@ -1680,11 +1678,8 @@ namespace Language "zolfo",
"zombie",
"zucchero"
- });
- unique_prefix_length = 4;
- word_map = new std::unordered_map<std::string, uint32_t>;
- trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
- language_name = "Italian";
+ }), 4) + { populate_maps();
}
};
diff --git a/src/mnemonics/japanese.h b/src/mnemonics/japanese.h index 421188893..d5ab4bc84 100644 --- a/src/mnemonics/japanese.h +++ b/src/mnemonics/japanese.h @@ -1,23 +1,43 @@ -// Word list originally created by dabura667
-//
-// Copyright (c) 2014-2016, The Monero Project
-//
+// Word list originally created by dabura667 and released under The MIT License (MIT)
+//
+// The MIT License (MIT)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Code surrounding the word list is Copyright (c) 2014-2017, The Monero Project
// All rights reserved.
-//
+//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
-//
+//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
-//
+//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
-//
+//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
-//
+//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
@@ -51,9 +71,7 @@ namespace Language class Japanese: public Base
{
public:
- Japanese()
- {
- word_list = new std::vector<std::string>({
+ Japanese(): Base("Japanese", std::vector<std::string>({
"あいこくしん",
"あいさつ",
"あいだ",
@@ -1680,11 +1698,8 @@ namespace Language "ひさん",
"びじゅつかん",
"ひしょ"
- });
- unique_prefix_length = 3;
- word_map = new std::unordered_map<std::string, uint32_t>;
- trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
- language_name = "Japanese";
+ }), 3)
+ {
populate_maps();
}
};
diff --git a/src/mnemonics/language_base.h b/src/mnemonics/language_base.h index e0297c847..8f0a7a9d3 100644 --- a/src/mnemonics/language_base.h +++ b/src/mnemonics/language_base.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -38,6 +38,7 @@ #include <vector>
#include <unordered_map>
#include <string>
+#include "misc_log_ex.h"
/*!
* \namespace Language
@@ -73,44 +74,62 @@ namespace Language class Base
{
protected:
- std::vector<std::string> *word_list; /*!< A pointer to the array of words */
- std::unordered_map<std::string, uint32_t> *word_map; /*!< hash table to find word's index */
- std::unordered_map<std::string, uint32_t> *trimmed_word_map; /*!< hash table to find word's trimmed index */
+ enum {
+ ALLOW_SHORT_WORDS = 1<<0,
+ ALLOW_DUPLICATE_PREFIXES = 1<<1,
+ };
+ const std::vector<std::string> word_list; /*!< A pointer to the array of words */
+ std::unordered_map<std::string, uint32_t> word_map; /*!< hash table to find word's index */
+ std::unordered_map<std::string, uint32_t> trimmed_word_map; /*!< hash table to find word's trimmed index */
std::string language_name; /*!< Name of language */
uint32_t unique_prefix_length; /*!< Number of unique starting characters to trim the wordlist to when matching */
/*!
* \brief Populates the word maps after the list is ready.
*/
- void populate_maps()
+ void populate_maps(uint32_t flags = 0)
{
int ii;
- std::vector<std::string>::iterator it;
- for (it = word_list->begin(), ii = 0; it != word_list->end(); it++, ii++)
+ std::vector<std::string>::const_iterator it;
+ if (word_list.size () != 1626)
+ throw std::runtime_error("Wrong word list length for " + language_name);
+ for (it = word_list.begin(), ii = 0; it != word_list.end(); it++, ii++)
{
- (*word_map)[*it] = ii;
+ word_map[*it] = ii;
+ if ((*it).size() < unique_prefix_length)
+ {
+ if (flags & ALLOW_SHORT_WORDS)
+ MWARNING(language_name << " word '" << *it << "' is shorter than its prefix length, " << unique_prefix_length);
+ else
+ throw std::runtime_error("Too short word in " + language_name + " word list: " + *it);
+ }
+ std::string trimmed;
if (it->length() > unique_prefix_length)
{
- (*trimmed_word_map)[utf8prefix(*it, unique_prefix_length)] = ii;
+ trimmed = utf8prefix(*it, unique_prefix_length);
}
else
{
- (*trimmed_word_map)[*it] = ii;
+ trimmed = *it;
+ }
+ if (trimmed_word_map.find(trimmed) != trimmed_word_map.end())
+ {
+ if (flags & ALLOW_DUPLICATE_PREFIXES)
+ MWARNING("Duplicate prefix in " << language_name << " word list: " << trimmed);
+ else
+ throw std::runtime_error("Duplicate prefix in " + language_name + " word list: " + trimmed);
}
+ trimmed_word_map[trimmed] = ii;
}
}
public:
- Base()
+ Base(const char *language_name, const std::vector<std::string> &words, uint32_t prefix_length):
+ word_list(words),
+ unique_prefix_length(prefix_length),
+ language_name(language_name)
{
- word_list = new std::vector<std::string>;
- word_map = new std::unordered_map<std::string, uint32_t>;
- trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
- unique_prefix_length = 4;
}
virtual ~Base()
{
- delete word_list;
- delete word_map;
- delete trimmed_word_map;
}
/*!
* \brief Returns a pointer to the word list.
@@ -118,7 +137,7 @@ namespace Language */
const std::vector<std::string>& get_word_list() const
{
- return *word_list;
+ return word_list;
}
/*!
* \brief Returns a pointer to the word map.
@@ -126,7 +145,7 @@ namespace Language */
const std::unordered_map<std::string, uint32_t>& get_word_map() const
{
- return *word_map;
+ return word_map;
}
/*!
* \brief Returns a pointer to the trimmed word map.
@@ -134,13 +153,13 @@ namespace Language */
const std::unordered_map<std::string, uint32_t>& get_trimmed_word_map() const
{
- return *trimmed_word_map;
+ return trimmed_word_map;
}
/*!
* \brief Returns the name of the language.
* \return Name of the language.
*/
- std::string get_language_name() const
+ const std::string &get_language_name() const
{
return language_name;
}
diff --git a/src/mnemonics/old_english.h b/src/mnemonics/old_english.h index cd4aa7629..21ac95de3 100644 --- a/src/mnemonics/old_english.h +++ b/src/mnemonics/old_english.h @@ -1,6 +1,6 @@ // Word list originally created as part of the Electrum project, Copyright (C) 2014 Thomas Voegtlin
//
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -51,9 +51,7 @@ namespace Language class OldEnglish: public Base
{
public:
- OldEnglish()
- {
- word_list = new std::vector<std::string>({
+ OldEnglish(): Base("OldEnglish", std::vector<std::string>({
"like",
"just",
"love",
@@ -1680,12 +1678,9 @@ namespace Language "unseen",
"weapon",
"weary"
- });
- unique_prefix_length = 4;
- word_map = new std::unordered_map<std::string, uint32_t>;
- trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
- language_name = "OldEnglish";
- populate_maps();
+ }), 4)
+ {
+ populate_maps(ALLOW_DUPLICATE_PREFIXES | ALLOW_SHORT_WORDS);
}
};
}
diff --git a/src/mnemonics/portuguese.h b/src/mnemonics/portuguese.h index 782a2ad5d..f9d66afc4 100644 --- a/src/mnemonics/portuguese.h +++ b/src/mnemonics/portuguese.h @@ -1,21 +1,44 @@ -// Copyright (c) 2014-2016, The Monero Project
-//
+// Word list originally created by dabura667 and released under The MIT License (MIT)
+//
+// The MIT License (MIT)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Code surrounding the word list is Copyright (c) 2014-2017, The Monero Project
+//
// All rights reserved.
-//
+//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
-//
+//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
-//
+//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
-//
+//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
-//
+//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
@@ -49,9 +72,7 @@ namespace Language class Portuguese: public Base
{
public:
- Portuguese()
- {
- word_list = new std::vector<std::string>({
+ Portuguese(): Base("Portuguese", std::vector<std::string>({
"abaular",
"abdominal",
"abeto",
@@ -1678,11 +1699,8 @@ namespace Language "zeloso",
"zenite",
"zumbi"
- });
- unique_prefix_length = 4;
- word_map = new std::unordered_map<std::string, uint32_t>;
- trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
- language_name = "Portuguese";
+ }), 4)
+ {
populate_maps();
}
};
diff --git a/src/mnemonics/russian.h b/src/mnemonics/russian.h index 6d98ca4b8..b3db4aa4c 100644 --- a/src/mnemonics/russian.h +++ b/src/mnemonics/russian.h @@ -1,6 +1,6 @@ // Word list created by Monero contributor sammy007
//
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -51,9 +51,7 @@ namespace Language class Russian: public Base
{
public:
- Russian()
- {
- word_list = new std::vector<std::string>({
+ Russian(): Base("Russian", std::vector<std::string>({
"абажур",
"абзац",
"абонент",
@@ -1680,11 +1678,8 @@ namespace Language "яхта",
"ячейка",
"ящик"
- });
- unique_prefix_length = 4;
- word_map = new std::unordered_map<std::string, uint32_t>;
- trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
- language_name = "Russian";
+ }), 4) + { populate_maps();
}
};
diff --git a/src/mnemonics/singleton.h b/src/mnemonics/singleton.h index 48b217a0c..5ba9269b1 100644 --- a/src/mnemonics/singleton.h +++ b/src/mnemonics/singleton.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/spanish.h b/src/mnemonics/spanish.h index 7275f4c42..117890ada 100644 --- a/src/mnemonics/spanish.h +++ b/src/mnemonics/spanish.h @@ -1,23 +1,44 @@ -// Word list originally created as part of the Electrum project, Copyright (C) 2014 Thomas Voegtlin
-//
-// Copyright (c) 2014-2016, The Monero Project
-//
+// Word list originally created by dabura667 and released under The MIT License (MIT)
+//
+// The MIT License (MIT)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Code surrounding the word list is Copyright (c) 2014-2017, The Monero Project
+//
// All rights reserved.
-//
+//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
-//
+//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
-//
+//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
-//
+//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
-//
+//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
@@ -51,9 +72,7 @@ namespace Language class Spanish: public Base
{
public:
- Spanish()
- {
- word_list = new std::vector<std::string>({
+ Spanish(): Base("Spanish", std::vector<std::string>({
"ábaco",
"abdomen",
"abeja",
@@ -1680,14 +1699,11 @@ namespace Language "risa",
"ritmo",
"rito"
- });
- unique_prefix_length = 4;
- word_map = new std::unordered_map<std::string, uint32_t>;
- trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
- language_name = "Spanish";
- populate_maps();
+ }), 4)
+ {
+ populate_maps(ALLOW_SHORT_WORDS);
}
};
}
-#endif +#endif
diff --git a/src/p2p/CMakeLists.txt b/src/p2p/CMakeLists.txt index b5b7d87ff..0704940b5 100644 --- a/src/p2p/CMakeLists.txt +++ b/src/p2p/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -38,6 +38,7 @@ source_group(p2p FILES ${P2P}) monero_add_library(p2p ${P2P}) target_link_libraries(p2p PUBLIC + epee ${UPNP_LIBRARIES} ${Boost_CHRONO_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} diff --git a/src/p2p/connection_basic.cpp b/src/p2p/connection_basic.cpp index bdb239ca8..df34379e2 100644 --- a/src/p2p/connection_basic.cpp +++ b/src/p2p/connection_basic.cpp @@ -2,7 +2,7 @@ /// @author rfree (current maintainer in monero.cc project) /// @brief base for connection, contains e.g. the ratelimit hooks -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/p2p/connection_basic.hpp b/src/p2p/connection_basic.hpp index 5452fa6fb..bea2df1cd 100644 --- a/src/p2p/connection_basic.hpp +++ b/src/p2p/connection_basic.hpp @@ -8,7 +8,7 @@ // ! (how ever if in some wonderful juristdictions that is not the case, then why not make another sub-class withat that members and licence it as epee part) // ! Working on above premise, IF this is valid in your juristdictions, then consider this code as released as: -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 51ab9b3f2..13cd3f5b0 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -112,7 +112,7 @@ namespace nodetool if (ver == 0) { // from v1, we do not store the peer id anymore - peerid_type peer_id; + peerid_type peer_id = AUTO_VAL_INIT (peer_id); a & peer_id; } } @@ -193,7 +193,6 @@ namespace nodetool bool parse_peer_from_string(nodetool::net_address& pe, const std::string& node_addr); bool handle_command_line( const boost::program_options::variables_map& vm - , bool testnet ); bool idle_worker(); bool handle_remote_peerlist(const std::list<peerlist_entry>& peerlist, time_t local_time, const epee::net_utils::connection_context_base& context); @@ -222,6 +221,7 @@ namespace nodetool void cache_connect_fail_info(const net_address& addr); bool is_addr_recently_failed(const net_address& addr); bool is_priority_node(const net_address& na); + std::set<std::string> get_seed_nodes(bool testnet) const; template <class Container> bool connect_to_peerlist(const Container& peers); @@ -325,6 +325,8 @@ namespace nodetool epee::critical_section m_ip_fails_score_lock; std::map<uint32_t, uint64_t> m_ip_fails_score; + + bool m_testnet; }; } diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 568b8208e..5c903b1f5 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -65,6 +65,7 @@ #define NET_MAKE_IP(b1,b2,b3,b4) ((LPARAM)(((DWORD)(b1)<<24)+((DWORD)(b2)<<16)+((DWORD)(b3)<<8)+((DWORD)(b4)))) +#define MIN_WANTED_SEED_NODES 12 namespace nodetool { @@ -108,8 +109,8 @@ namespace nodetool void node_server<t_payload_net_handler>::init_options(boost::program_options::options_description& desc) { command_line::add_arg(desc, arg_p2p_bind_ip); - command_line::add_arg(desc, arg_p2p_bind_port); - command_line::add_arg(desc, arg_testnet_p2p_bind_port); + command_line::add_arg(desc, arg_p2p_bind_port, false); + command_line::add_arg(desc, arg_testnet_p2p_bind_port, false); command_line::add_arg(desc, arg_p2p_external_port); command_line::add_arg(desc, arg_p2p_allow_local_ip); command_line::add_arg(desc, arg_p2p_add_peer); @@ -287,10 +288,9 @@ namespace nodetool template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::handle_command_line( const boost::program_options::variables_map& vm - , bool testnet ) { - auto p2p_bind_arg = testnet ? arg_testnet_p2p_bind_port : arg_p2p_bind_port; + auto p2p_bind_arg = m_testnet ? arg_testnet_p2p_bind_port : arg_p2p_bind_port; m_bind_ip = command_line::get_arg(vm, arg_p2p_bind_ip); m_port = command_line::get_arg(vm, p2p_bind_arg); @@ -309,7 +309,7 @@ namespace nodetool bool r = parse_peer_from_string(pe.adr, pr_str); CHECK_AND_ASSERT_MES(r, false, "Failed to parse address from string: " << pr_str); if (pe.adr.port == 0) - pe.adr.port = testnet ? ::config::testnet::P2P_DEFAULT_PORT : ::config::P2P_DEFAULT_PORT; + pe.adr.port = m_testnet ? ::config::testnet::P2P_DEFAULT_PORT : ::config::P2P_DEFAULT_PORT; m_command_line_peers.push_back(pe); } } @@ -398,17 +398,42 @@ namespace nodetool //----------------------------------------------------------------------------------- template<class t_payload_net_handler> + std::set<std::string> node_server<t_payload_net_handler>::get_seed_nodes(bool testnet) const + { + std::set<std::string> full_addrs; + if (testnet) + { + full_addrs.insert("212.83.175.67:28080"); + full_addrs.insert("5.9.100.248:28080"); + full_addrs.insert("163.172.182.165:28080"); + full_addrs.insert("195.154.123.123:28080"); + full_addrs.insert("212.83.172.165:28080"); + } + else + { + full_addrs.insert("107.152.130.98:18080"); + full_addrs.insert("212.83.175.67:18080"); + full_addrs.insert("5.9.100.248:18080"); + full_addrs.insert("163.172.182.165:18080"); + full_addrs.insert("161.67.132.39:18080"); + full_addrs.insert("198.74.231.92:18080"); + full_addrs.insert("195.154.123.123:28080"); + full_addrs.insert("212.83.172.165:28080"); + } + return full_addrs; + } + + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::init(const boost::program_options::variables_map& vm) { std::set<std::string> full_addrs; - bool testnet = command_line::get_arg(vm, command_line::arg_testnet_on); + m_testnet = command_line::get_arg(vm, command_line::arg_testnet_on); - if (testnet) + if (m_testnet) { memcpy(&m_network_id, &::config::testnet::NETWORK_ID, 16); - full_addrs.insert("163.172.182.165:28080"); - full_addrs.insert("204.12.248.66:28080"); - full_addrs.insert("5.9.100.248:28080"); + full_addrs = get_seed_nodes(true); } else { @@ -481,14 +506,16 @@ namespace nodetool ++i; } - if (!full_addrs.size()) + // append the fallback nodes if we have too few seed nodes to start with + if (full_addrs.size() < MIN_WANTED_SEED_NODES) { - MINFO("DNS seed node lookup either timed out or failed, falling back to defaults"); - full_addrs.insert("198.74.231.92:18080"); - full_addrs.insert("161.67.132.39:18080"); - full_addrs.insert("163.172.182.165:18080"); - full_addrs.insert("204.12.248.66:18080"); - full_addrs.insert("5.9.100.248:18080"); + if (full_addrs.empty()) + MINFO("DNS seed node lookup either timed out or failed, falling back to defaults"); + else + MINFO("Not enough DNS seed nodes found, using fallback defaults too"); + + for (const auto &peer: get_seed_nodes(false)) + full_addrs.insert(peer); } } @@ -499,14 +526,14 @@ namespace nodetool } MDEBUG("Number of seed nodes: " << m_seed_nodes.size()); - bool res = handle_command_line(vm, testnet); + bool res = handle_command_line(vm); CHECK_AND_ASSERT_MES(res, false, "Failed to handle command line"); - auto config_arg = testnet ? command_line::arg_testnet_data_dir : command_line::arg_data_dir; + auto config_arg = m_testnet ? command_line::arg_testnet_data_dir : command_line::arg_data_dir; m_config_folder = command_line::get_arg(vm, config_arg); - if ((!testnet && m_port != std::to_string(::config::P2P_DEFAULT_PORT)) - || (testnet && m_port != std::to_string(::config::testnet::P2P_DEFAULT_PORT))) { + if ((!m_testnet && m_port != std::to_string(::config::P2P_DEFAULT_PORT)) + || (m_testnet && m_port != std::to_string(::config::testnet::P2P_DEFAULT_PORT))) { m_config_folder = m_config_folder + "/" + m_port; } @@ -1083,7 +1110,14 @@ namespace nodetool while(rand_count < (max_random_index+1)*3 && try_count < 10 && !m_net_server.is_stop_signal_sent()) { ++rand_count; - size_t random_index = get_random_index_with_fixed_probability(max_random_index); + size_t random_index; + + if (use_white_list) { + random_index = get_random_index_with_fixed_probability(max_random_index); + } else { + random_index = crypto::rand<size_t>() % m_peerlist.get_gray_peers_count(); + } + CHECK_AND_ASSERT_MES(random_index < local_peers_count, false, "random_starter_index < peers_local.size() failed!!"); if(tried_peers.count(random_index)) @@ -1135,6 +1169,7 @@ namespace nodetool { size_t try_count = 0; size_t current_index = crypto::rand<size_t>()%m_seed_nodes.size(); + bool fallback_nodes_added = false; while(true) { if(m_net_server.is_stop_signal_sent()) @@ -1144,8 +1179,22 @@ namespace nodetool break; if(++try_count > m_seed_nodes.size()) { - MWARNING("Failed to connect to any of seed peers, continuing without seeds"); - break; + if (!fallback_nodes_added) + { + MWARNING("Failed to connect to any of seed peers, trying fallback seeds"); + for (const auto &peer: get_seed_nodes(m_testnet)) + { + MDEBUG("Fallback seed node: " << peer); + append_net_address(m_seed_nodes, peer); + } + fallback_nodes_added = true; + // continue for another few cycles + } + else + { + MWARNING("Failed to connect to any of seed peers, continuing without seeds"); + break; + } } if(++current_index >= m_seed_nodes.size()) current_index = 0; @@ -1734,7 +1783,6 @@ namespace nodetool bool node_server<t_payload_net_handler>::parse_peers_and_add_to_container(const boost::program_options::variables_map& vm, const command_line::arg_descriptor<std::vector<std::string> > & arg, Container& container) { std::vector<std::string> perrs = command_line::get_arg(vm, arg); - bool testnet = command_line::get_arg(vm, command_line::arg_testnet_on); for(const std::string& pr_str: perrs) { @@ -1742,7 +1790,7 @@ namespace nodetool bool r = parse_peer_from_string(na, pr_str); CHECK_AND_ASSERT_MES(r, false, "Failed to parse address from string: " << pr_str); if (na.port == 0) - na.port = testnet ? ::config::testnet::P2P_DEFAULT_PORT : ::config::P2P_DEFAULT_PORT; + na.port = m_testnet ? ::config::testnet::P2P_DEFAULT_PORT : ::config::P2P_DEFAULT_PORT; container.push_back(na); } diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h index 69bee890c..6aba7aa24 100644 --- a/src/p2p/net_node_common.h +++ b/src/p2p/net_node_common.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h index 9af3694b6..de0f51cc3 100644 --- a/src/p2p/net_peerlist.h +++ b/src/p2p/net_peerlist.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -446,13 +446,10 @@ namespace nodetool return false; } - size_t x = crypto::rand<size_t>() % (m_peers_gray.size() + 1); - size_t res = (x * x * x) / (m_peers_gray.size() * m_peers_gray.size()); //parabola \/ - - LOG_PRINT_L3("Random gray peer index=" << res << "(x="<< x << ", max_index=" << m_peers_gray.size() << ")"); + size_t random_index = crypto::rand<size_t>() % m_peers_gray.size(); peers_indexed::index<by_time>::type& by_time_index = m_peers_gray.get<by_time>(); - pe = *epee::misc_utils::move_it_backward(--by_time_index.end(), res); + pe = *epee::misc_utils::move_it_backward(--by_time_index.end(), random_index); return true; diff --git a/src/p2p/net_peerlist_boost_serialization.h b/src/p2p/net_peerlist_boost_serialization.h index 9a0d12c2e..6891ac80c 100644 --- a/src/p2p/net_peerlist_boost_serialization.h +++ b/src/p2p/net_peerlist_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/p2p/network_throttle-detail.cpp b/src/p2p/network_throttle-detail.cpp index d4fe356a9..0747b6f36 100644 --- a/src/p2p/network_throttle-detail.cpp +++ b/src/p2p/network_throttle-detail.cpp @@ -2,7 +2,7 @@ /// @author rfree (current maintainer in monero.cc project) /// @brief implementaion for throttling of connection (count and rate-limit speed etc) -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -216,7 +216,7 @@ void network_throttle::_handle_trafic_exact(size_t packet_size, size_t orginal_s std::ostringstream oss; oss << "["; for (auto sample: m_history) oss << sample.m_size << " "; oss << "]" << std::ends; std::string history_str = oss.str(); - MDEBUG("Throttle " << m_name << ": packet of ~"<<packet_size<<"b " << " (from "<<orginal_size<<" b)" + MTRACE("Throttle " << m_name << ": packet of ~"<<packet_size<<"b " << " (from "<<orginal_size<<" b)" << " Speed AVG=" << std::setw(4) << ((long int)(cts .average/1024)) <<"[w="<<cts .window<<"]" << " " << std::setw(4) << ((long int)(cts2.average/1024)) <<"[w="<<cts2.window<<"]" <<" / " << " Limit="<< ((long int)(m_target_speed/1024)) <<" KiB/sec " @@ -306,7 +306,7 @@ void network_throttle::calculate_times(size_t packet_size, calculate_times_struc if (dbg) { std::ostringstream oss; oss << "["; for (auto sample: m_history) oss << sample.m_size << " "; oss << "]" << std::ends; std::string history_str = oss.str(); - MDEBUG((cts.delay > 0 ? "SLEEP" : "") + MTRACE((cts.delay > 0 ? "SLEEP" : "") << "dbg " << m_name << ": " << "speed is A=" << std::setw(8) <<cts.average<<" vs " << "Max=" << std::setw(8) <<M<<" " diff --git a/src/p2p/network_throttle-detail.hpp b/src/p2p/network_throttle-detail.hpp index ef7227eb9..c514db450 100644 --- a/src/p2p/network_throttle-detail.hpp +++ b/src/p2p/network_throttle-detail.hpp @@ -2,7 +2,7 @@ /// @author rfree (current maintainer in monero.cc project) /// @brief implementaion for throttling of connection (count and rate-limit speed etc) -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/p2p/network_throttle.cpp b/src/p2p/network_throttle.cpp index 30538bb3c..6d68f3286 100644 --- a/src/p2p/network_throttle.cpp +++ b/src/p2p/network_throttle.cpp @@ -26,7 +26,7 @@ Throttling work by: */ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/p2p/network_throttle.hpp b/src/p2p/network_throttle.hpp index 4f6cbe9cf..a747cdd71 100644 --- a/src/p2p/network_throttle.hpp +++ b/src/p2p/network_throttle.hpp @@ -2,7 +2,7 @@ /// @author rfree (current maintainer in monero.cc project) /// @brief interface for throttling of connection (count and rate-limit speed etc) -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h index dc738d92c..5ec012714 100644 --- a/src/p2p/p2p_protocol_defs.h +++ b/src/p2p/p2p_protocol_defs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/p2p/stdafx.h b/src/p2p/stdafx.h index 77daa484d..5e9baa895 100644 --- a/src/p2p/stdafx.h +++ b/src/p2p/stdafx.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/platform/mingw/alloca.h b/src/platform/mingw/alloca.h index 131ad3487..f850d1cdf 100644 --- a/src/platform/mingw/alloca.h +++ b/src/platform/mingw/alloca.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/platform/msc/alloca.h b/src/platform/msc/alloca.h index 0007d32db..2f3f79a3f 100644 --- a/src/platform/msc/alloca.h +++ b/src/platform/msc/alloca.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/platform/msc/inline_c.h b/src/platform/msc/inline_c.h index f28d70733..030604ef8 100644 --- a/src/platform/msc/inline_c.h +++ b/src/platform/msc/inline_c.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/platform/msc/stdbool.h b/src/platform/msc/stdbool.h index 22e34c24a..05d2e7a38 100644 --- a/src/platform/msc/stdbool.h +++ b/src/platform/msc/stdbool.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/platform/msc/sys/param.h b/src/platform/msc/sys/param.h index 4201c8784..db6eca28b 100644 --- a/src/platform/msc/sys/param.h +++ b/src/platform/msc/sys/param.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/ringct/CMakeLists.txt b/src/ringct/CMakeLists.txt index 334a7f350..e15af205a 100644 --- a/src/ringct/CMakeLists.txt +++ b/src/ringct/CMakeLists.txt @@ -49,5 +49,6 @@ target_link_libraries(ringct PUBLIC common crypto + cryptonote_basic PRIVATE ${EXTRA_LIBRARIES}) diff --git a/src/ringct/rctCryptoOps.c b/src/ringct/rctCryptoOps.c index 9bb9a6891..f69d692f6 100644 --- a/src/ringct/rctCryptoOps.c +++ b/src/ringct/rctCryptoOps.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/ringct/rctCryptoOps.h b/src/ringct/rctCryptoOps.h index 58c6964d8..2674c2243 100644 --- a/src/ringct/rctCryptoOps.h +++ b/src/ringct/rctCryptoOps.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp index 0e1715072..d0e0964b6 100644 --- a/src/ringct/rctOps.cpp +++ b/src/ringct/rctOps.cpp @@ -36,6 +36,8 @@ using namespace std; #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "ringct" +#define CHECK_AND_ASSERT_THROW_MES_L1(expr, message) {if(!(expr)) {MWARNING(message); throw std::runtime_error(message);}} + namespace rct { //Various key initialization functions @@ -175,7 +177,7 @@ namespace rct { 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<std::string>(__LINE__)); + CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&A, P.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); ge_scalarmult(&R, a.bytes, &A); ge_tobytes(aP.bytes, &R); } @@ -184,7 +186,7 @@ namespace rct { 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<std::string>(__LINE__)); + CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&A, P.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); ge_scalarmult(&R, a.bytes, &A); key aP; ge_tobytes(aP.bytes, &R); @@ -196,7 +198,7 @@ namespace rct { 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<std::string>(__LINE__)); + CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&A, H.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); ge_scalarmult(&R, a.bytes, &A); key aP; ge_tobytes(aP.bytes, &R); @@ -208,8 +210,8 @@ namespace rct { //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<std::string>(__LINE__)); - CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&A2, A.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); + CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&B2, B.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); + CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&A2, A.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); ge_cached tmp2; ge_p3_to_cached(&tmp2, &B2); ge_p1p1 tmp3; @@ -231,7 +233,7 @@ namespace rct { 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<std::string>(__LINE__)); + CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&B2, B.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); ge_double_scalarmult_base_vartime(&rv, b.bytes, &B2, a.bytes); ge_tobytes(aGbB.bytes, &rv); } @@ -240,7 +242,7 @@ namespace rct { // 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<std::string>(__LINE__)); + CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&B2, B.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); ge_dsm_precomp(rv, &B2); } @@ -250,7 +252,7 @@ namespace rct { 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<std::string>(__LINE__)); + CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&A2, A.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); ge_double_scalarmult_precomp_vartime(&rv, a.bytes, &A2, b.bytes, B); ge_tobytes(aAbB.bytes, &rv); } @@ -260,8 +262,8 @@ namespace rct { //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<std::string>(__LINE__)); - CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&A2, A.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); + CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&B2, B.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); + CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&A2, A.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); ge_cached tmp2; ge_p3_to_cached(&tmp2, &B2); ge_p1p1 tmp3; @@ -381,7 +383,7 @@ namespace rct { 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<std::string>(__LINE__)); + CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&res, h.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); ge_p3_to_p2(&point, &res); ge_mul8(&point2, &point); ge_p1p1_to_p3(&res, &point2); diff --git a/src/ringct/rctOps.h b/src/ringct/rctOps.h index 90f54b050..cb19bbbd6 100644 --- a/src/ringct/rctOps.h +++ b/src/ringct/rctOps.h @@ -66,6 +66,7 @@ namespace rct { 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 } }; static const key I = { {0x01, 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 } }; + static const key L = { {0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 } }; //Creates a zero scalar inline key zero() { return Z; } @@ -73,6 +74,9 @@ namespace rct { //Creates a zero elliptic curve point inline key identity() { return I; } inline void identity(key &Id) { memcpy(&Id, &I, 32); } + //Creates a key equal to the curve order + inline key curveOrder() { return L; } + inline void curveOrder(key &l) { l = L; } //copies a scalar or point inline void copy(key &AA, const key &A) { memcpy(&AA, &A, 32); } inline key copy(const key & A) { key AA; memcpy(&AA, &A, 32); return AA; } diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index b8f3596e8..8efd6a07c 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -34,7 +34,7 @@ #include "common/thread_group.h" #include "common/util.h" #include "rctSigs.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_format_utils.h" using namespace crypto; using namespace std; @@ -873,8 +873,7 @@ namespace rct { // 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(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo"); CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index"); //mask amount and mask @@ -902,8 +901,7 @@ namespace rct { 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(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo"); CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index"); //mask amount and mask diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index 0c27745e1..c820fb297 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -47,6 +47,8 @@ extern "C" { #include "crypto/generic-ops.h" #include "crypto/crypto.h" +#include "hex.h" +#include "span.h" #include "serialization/serialization.h" #include "serialization/debug_archive.h" #include "serialization/binary_archive.h" @@ -443,8 +445,9 @@ namespace cryptonote { static inline bool operator!=(const crypto::secret_key &k0, const rct::key &k1) { return memcmp(&k0, &k1, 32); } } -template<typename T> std::ostream &print256(std::ostream &o, const T &v); -inline std::ostream &operator <<(std::ostream &o, const rct::key &v) { return print256(o, v); } +inline std::ostream &operator <<(std::ostream &o, const rct::key &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; +} BLOB_SERIALIZER(rct::key); diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt index 6df93cde1..f6037d7e3 100644 --- a/src/rpc/CMakeLists.txt +++ b/src/rpc/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -27,9 +27,11 @@ # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. set(rpc_sources - core_rpc_server.cpp) + core_rpc_server.cpp + rpc_args.cpp) -set(rpc_headers) +set(rpc_headers + rpc_args.h) set(rpc_private_headers core_rpc_server.h @@ -44,9 +46,11 @@ monero_add_library(rpc ${rpc_private_headers}) target_link_libraries(rpc PUBLIC + common cryptonote_core cryptonote_protocol epee + ${Boost_REGEX_LIBRARY} ${Boost_THREAD_LIBRARY} PRIVATE ${EXTRA_LIBRARIES}) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index b2e8e6716..76a69fbf6 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -33,13 +33,20 @@ using namespace epee; #include "core_rpc_server.h" #include "common/command_line.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_core/account.h" -#include "cryptonote_core/cryptonote_basic_impl.h" +#include "common/updates.h" +#include "common/download.h" +#include "common/util.h" +#include "cryptonote_basic/cryptonote_format_utils.h" +#include "cryptonote_basic/account.h" +#include "cryptonote_basic/cryptonote_basic_impl.h" #include "misc_language.h" #include "crypto/hash.h" +#include "rpc/rpc_args.h" #include "core_rpc_server_error_codes.h" +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "daemon.rpc" + #define MAX_RESTRICTED_FAKE_OUTS_COUNT 40 #define MAX_RESTRICTED_GLOBAL_FAKE_OUTS_COUNT 500 @@ -49,11 +56,10 @@ namespace cryptonote //----------------------------------------------------------------------------------- void core_rpc_server::init_options(boost::program_options::options_description& desc) { - command_line::add_arg(desc, arg_rpc_bind_ip); command_line::add_arg(desc, arg_rpc_bind_port); command_line::add_arg(desc, arg_testnet_rpc_bind_port); command_line::add_arg(desc, arg_restricted_rpc); - command_line::add_arg(desc, arg_user_agent); + cryptonote::rpc_args::init_options(desc); } //------------------------------------------------------------------------------------------------------------------------------ core_rpc_server::core_rpc_server( @@ -64,29 +70,29 @@ namespace cryptonote , m_p2p(p2p) {} //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::handle_command_line( + bool core_rpc_server::init( const boost::program_options::variables_map& vm ) { + m_testnet = command_line::get_arg(vm, command_line::arg_testnet_on); + m_net_server.set_threads_prefix("RPC"); + auto p2p_bind_arg = m_testnet ? arg_testnet_rpc_bind_port : arg_rpc_bind_port; - m_bind_ip = command_line::get_arg(vm, arg_rpc_bind_ip); - m_port = command_line::get_arg(vm, p2p_bind_arg); + auto rpc_config = cryptonote::rpc_args::process(vm); + if (!rpc_config) + return false; + m_restricted = command_line::get_arg(vm, arg_restricted_rpc); - return true; - } - //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::init( - const boost::program_options::variables_map& vm - ) - { - m_testnet = command_line::get_arg(vm, command_line::arg_testnet_on); - std::string m_user_agent = command_line::get_arg(vm, command_line::arg_user_agent); - m_net_server.set_threads_prefix("RPC"); - bool r = handle_command_line(vm); - CHECK_AND_ASSERT_MES(r, false, "Failed to process command line in core_rpc_server"); - return epee::http_server_impl_base<core_rpc_server, connection_context>::init(m_port, m_bind_ip, m_user_agent); + boost::optional<epee::net_utils::http::login> http_login{}; + std::string port = command_line::get_arg(vm, p2p_bind_arg); + if (rpc_config->login) + http_login.emplace(std::move(rpc_config->login->username), std::move(rpc_config->login->password).password()); + + return epee::http_server_impl_base<core_rpc_server, connection_context>::init( + std::move(port), std::move(rpc_config->bind_ip), std::move(http_login) + ); } //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::check_core_busy() @@ -148,10 +154,27 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ + static cryptonote::blobdata get_pruned_tx_blob(const cryptonote::blobdata &blobdata) + { + cryptonote::transaction tx; + + if (!cryptonote::parse_and_validate_tx_from_blob(blobdata, tx)) + { + MERROR("Failed to parse and validate tx from blob"); + return blobdata; + } + + std::stringstream ss; + binary_archive<true> ba(ss); + bool r = tx.serialize_base(ba); + CHECK_AND_ASSERT_MES(r, blobdata, "Failed to serialize rct signatures base"); + return ss.str(); + } + //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res) { CHECK_CORE_BUSY(); - std::list<std::pair<block, std::list<transaction> > > bs; + std::list<std::pair<cryptonote::blobdata, std::list<cryptonote::blobdata> > > bs; if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)) { @@ -159,24 +182,39 @@ namespace cryptonote return false; } - for(auto& b: bs) + size_t pruned_size = 0, unpruned_size = 0, ntxes = 0; + for(auto& bd: bs) { res.blocks.resize(res.blocks.size()+1); - res.blocks.back().block = block_to_blob(b.first); + res.blocks.back().block = bd.first; + pruned_size += bd.first.size(); + unpruned_size += bd.first.size(); 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); + block b; + if (!parse_and_validate_block_from_blob(bd.first, b)) + { + res.status = "Invalid block"; + return false; + } + bool r = m_core.get_tx_outputs_gindexs(get_transaction_hash(b.miner_tx), res.output_indices.back().indices.back().indices); if (!r) { res.status = "Failed"; return false; } size_t txidx = 0; - for(auto& t: b.second) + ntxes += bd.second.size(); + for(const auto& t: bd.second) { - res.blocks.back().txs.push_back(tx_to_blob(t)); + if (req.prune) + res.blocks.back().txs.push_back(get_pruned_tx_blob(t)); + else + res.blocks.back().txs.push_back(t); + pruned_size += res.blocks.back().txs.back().size(); + unpruned_size += t.size(); 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); + bool r = m_core.get_tx_outputs_gindexs(b.tx_hashes[txidx++], res.output_indices.back().indices.back().indices); if (!r) { res.status = "Failed"; @@ -185,6 +223,7 @@ namespace cryptonote } } + MDEBUG("on_get_blocks: " << bs.size() << " blocks, " << ntxes << " txes, pruned size " << pruned_size << ", unpruned size " << unpruned_size); res.status = CORE_RPC_STATUS_OK; return true; } @@ -635,7 +674,7 @@ namespace cryptonote boost::thread::attributes attrs; attrs.set_stack_size(THREAD_STACK_SIZE); - if(!m_core.get_miner().start(adr, static_cast<size_t>(req.threads_count), attrs)) + if(!m_core.get_miner().start(adr, static_cast<size_t>(req.threads_count), attrs, req.do_background_mining, req.ignore_battery)) { res.status = "Failed, mining not started"; LOG_PRINT_L0(res.status); @@ -659,10 +698,11 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_mining_status(const COMMAND_RPC_MINING_STATUS::request& req, COMMAND_RPC_MINING_STATUS::response& res) { - CHECK_CORE_READY(); + CHECK_CORE_BUSY(); const miner& lMiner = m_core.get_miner(); res.active = lMiner.is_mining(); + res.is_background_mining_enabled = lMiner.get_is_background_mining_enabled(); if ( lMiner.is_mining() ) { res.speed = lMiner.get_speed(); @@ -736,7 +776,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_set_log_categories(const COMMAND_RPC_SET_LOG_CATEGORIES::request& req, COMMAND_RPC_SET_LOG_CATEGORIES::response& res) { - mlog_set_categories(req.categories.c_str()); + mlog_set_log(req.categories.c_str()); res.status = CORE_RPC_STATUS_OK; return true; } @@ -749,6 +789,14 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_get_transaction_pool_hashes(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::response& res) + { + CHECK_CORE_BUSY(); + m_core.get_pool_transaction_hashes(res.tx_hashes); + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res) { // FIXME: replace back to original m_p2p.send_stop_signal() after @@ -835,7 +883,7 @@ namespace cryptonote block b = AUTO_VAL_INIT(b); cryptonote::blobdata blob_reserve; blob_reserve.resize(req.reserve_size, 0); - if(!m_core.get_block_template(b, acc, res.difficulty, res.height, blob_reserve)) + if(!m_core.get_block_template(b, acc, res.difficulty, res.height, res.expected_reward, blob_reserve)) { error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; error_resp.message = "Internal error: failed to create block template"; @@ -1386,6 +1434,7 @@ namespace cryptonote std::pair<uint64_t, uint64_t> amounts = m_core.get_coinbase_tx_sum(req.height, req.count); res.emission_amount = amounts.first; res.fee_amount = amounts.second; + res.status = CORE_RPC_STATUS_OK; return true; } //------------------------------------------------------------------------------------------------------------------------------ @@ -1436,21 +1485,155 @@ namespace cryptonote bool core_rpc_server::on_start_save_graph(const COMMAND_RPC_START_SAVE_GRAPH::request& req, COMMAND_RPC_START_SAVE_GRAPH::response& res) { m_p2p.set_save_graph(true); + res.status = CORE_RPC_STATUS_OK; return true; } //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_stop_save_graph(const COMMAND_RPC_STOP_SAVE_GRAPH::request& req, COMMAND_RPC_STOP_SAVE_GRAPH::response& res) { m_p2p.set_save_graph(false); + res.status = CORE_RPC_STATUS_OK; return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res) + { + static const char software[] = "monero"; +#ifdef BUILD_TAG + static const char buildtag[] = BOOST_PP_STRINGIZE(BUILD_TAG); +#else + static const char buildtag[] = "source"; +#endif - const command_line::arg_descriptor<std::string> core_rpc_server::arg_rpc_bind_ip = { - "rpc-bind-ip" - , "IP for RPC server" - , "127.0.0.1" - }; + if (req.command != "check" && req.command != "download" && req.command != "update") + { + res.status = std::string("unknown command: '") + req.command + "'"; + return true; + } + + std::string version, hash; + if (!tools::check_updates(software, buildtag, version, hash)) + { + res.status = "Error checking for updates"; + return true; + } + if (tools::vercmp(version.c_str(), MONERO_VERSION) <= 0) + { + res.update = false; + res.status = CORE_RPC_STATUS_OK; + return true; + } + res.update = true; + res.version = version; + res.user_uri = tools::get_update_url(software, "cli", buildtag, version, true); + res.auto_uri = tools::get_update_url(software, "cli", buildtag, version, false); + res.hash = hash; + if (req.command == "check") + { + res.status = CORE_RPC_STATUS_OK; + return true; + } + + boost::filesystem::path path; + if (req.path.empty()) + { + std::string filename; + const char *slash = strrchr(res.auto_uri.c_str(), '/'); + if (slash) + filename = slash + 1; + else + filename = std::string(software) + "-update-" + version; + path = epee::string_tools::get_current_module_folder(); + path /= filename; + } + else + { + path = req.path; + } + + crypto::hash file_hash; + if (!tools::sha256sum(path.string(), file_hash) || (hash != epee::string_tools::pod_to_hex(file_hash))) + { + MDEBUG("We don't have that file already, downloading"); + if (!tools::download(path.string(), res.auto_uri)) + { + MERROR("Failed to download " << res.auto_uri); + return false; + } + if (!tools::sha256sum(path.string(), file_hash)) + { + MERROR("Failed to hash " << path); + return false; + } + if (hash != epee::string_tools::pod_to_hex(file_hash)) + { + MERROR("Download from " << res.auto_uri << " does not match the expected hash"); + return false; + } + MINFO("New version downloaded to " << path); + } + else + { + MDEBUG("We already have " << path << " with expected hash"); + } + res.path = path.string(); + + if (req.command == "download") + { + res.status = CORE_RPC_STATUS_OK; + return true; + } + + res.status = "'update' not implemented yet"; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_relay_tx(const COMMAND_RPC_RELAY_TX::request& req, COMMAND_RPC_RELAY_TX::response& res, epee::json_rpc::error& error_resp) + { + if(!check_core_busy()) + { + error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; + error_resp.message = "Core is busy."; + return false; + } + + bool failed = false; + for (const auto &str: req.txids) + { + cryptonote::blobdata txid_data; + if(!epee::string_tools::parse_hexstr_to_binbuff(str, txid_data)) + { + res.status = std::string("Invalid transaction id: ") + str; + failed = true; + } + crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data()); + + cryptonote::transaction tx; + bool r = m_core.get_pool_transaction(txid, tx); + if (r) + { + cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context); + NOTIFY_NEW_TRANSACTIONS::request r; + r.txs.push_back(cryptonote::tx_to_blob(tx)); + m_core.get_protocol()->relay_transactions(r, fake_context); + //TODO: make sure that tx has reached other nodes here, probably wait to receive reflections from other nodes + } + else + { + res.status = std::string("Transaction not found in pool: ") + str; + failed = true; + } + } + + if (failed) + { + return false; + } + + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ const command_line::arg_descriptor<std::string> core_rpc_server::arg_rpc_bind_port = { "rpc-bind-port" @@ -1469,11 +1652,4 @@ namespace cryptonote , "Restrict RPC to view only commands" , false }; - - const command_line::arg_descriptor<std::string> core_rpc_server::arg_user_agent = { - "user-agent" - , "Restrict RPC to clients using this user agent" - , "" - }; - } // namespace cryptonote diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 767bcc715..0aa222c87 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -52,11 +52,9 @@ namespace cryptonote { public: - static const command_line::arg_descriptor<std::string> arg_rpc_bind_ip; static const command_line::arg_descriptor<std::string> arg_rpc_bind_port; static const command_line::arg_descriptor<std::string> arg_testnet_rpc_bind_port; static const command_line::arg_descriptor<bool> arg_restricted_rpc; - static const command_line::arg_descriptor<std::string> arg_user_agent; typedef epee::net_utils::connection_context_base connection_context; @@ -94,12 +92,14 @@ namespace cryptonote MAP_URI_AUTO_JON2_IF("/set_log_level", on_set_log_level, COMMAND_RPC_SET_LOG_LEVEL, !m_restricted) MAP_URI_AUTO_JON2_IF("/set_log_categories", on_set_log_categories, COMMAND_RPC_SET_LOG_CATEGORIES, !m_restricted) MAP_URI_AUTO_JON2("/get_transaction_pool", on_get_transaction_pool, COMMAND_RPC_GET_TRANSACTION_POOL) + MAP_URI_AUTO_JON2("/get_transaction_pool_hashes.bin", on_get_transaction_pool_hashes, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES) MAP_URI_AUTO_JON2_IF("/stop_daemon", on_stop_daemon, COMMAND_RPC_STOP_DAEMON, !m_restricted) MAP_URI_AUTO_JON2("/getinfo", on_get_info, COMMAND_RPC_GET_INFO) MAP_URI_AUTO_JON2_IF("/out_peers", on_out_peers, COMMAND_RPC_OUT_PEERS, !m_restricted) MAP_URI_AUTO_JON2_IF("/start_save_graph", on_start_save_graph, COMMAND_RPC_START_SAVE_GRAPH, !m_restricted) MAP_URI_AUTO_JON2_IF("/stop_save_graph", on_stop_save_graph, COMMAND_RPC_STOP_SAVE_GRAPH, !m_restricted) MAP_URI_AUTO_JON2("/get_outs", on_get_outs, COMMAND_RPC_GET_OUTPUTS) + MAP_URI_AUTO_JON2_IF("/update", on_update, COMMAND_RPC_UPDATE, !m_restricted) BEGIN_JSON_RPC_MAP("/json_rpc") MAP_JON_RPC("getblockcount", on_getblockcount, COMMAND_RPC_GETBLOCKCOUNT) MAP_JON_RPC_WE("on_getblockhash", on_getblockhash, COMMAND_RPC_GETBLOCKHASH) @@ -121,6 +121,7 @@ namespace cryptonote MAP_JON_RPC_WE("get_coinbase_tx_sum", on_get_coinbase_tx_sum, COMMAND_RPC_GET_COINBASE_TX_SUM) MAP_JON_RPC_WE("get_fee_estimate", on_get_per_kb_fee_estimate, COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE) MAP_JON_RPC_WE_IF("get_alternate_chains",on_get_alternate_chains, COMMAND_RPC_GET_ALTERNATE_CHAINS, !m_restricted) + MAP_JON_RPC_WE_IF("relay_tx", on_relay_tx, COMMAND_RPC_RELAY_TX, !m_restricted) END_JSON_RPC_MAP() END_URI_MAP2() @@ -146,10 +147,12 @@ namespace cryptonote bool on_set_log_level(const COMMAND_RPC_SET_LOG_LEVEL::request& req, COMMAND_RPC_SET_LOG_LEVEL::response& res); bool on_set_log_categories(const COMMAND_RPC_SET_LOG_CATEGORIES::request& req, COMMAND_RPC_SET_LOG_CATEGORIES::response& res); bool on_get_transaction_pool(const COMMAND_RPC_GET_TRANSACTION_POOL::request& req, COMMAND_RPC_GET_TRANSACTION_POOL::response& res); + bool on_get_transaction_pool_hashes(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::response& res); bool on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res); bool on_out_peers(const COMMAND_RPC_OUT_PEERS::request& req, COMMAND_RPC_OUT_PEERS::response& res); bool on_start_save_graph(const COMMAND_RPC_START_SAVE_GRAPH::request& req, COMMAND_RPC_START_SAVE_GRAPH::response& res); bool on_stop_save_graph(const COMMAND_RPC_STOP_SAVE_GRAPH::request& req, COMMAND_RPC_STOP_SAVE_GRAPH::response& res); + bool on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res); //json_rpc bool on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res); @@ -172,13 +175,10 @@ namespace cryptonote bool on_get_coinbase_tx_sum(const COMMAND_RPC_GET_COINBASE_TX_SUM::request& req, COMMAND_RPC_GET_COINBASE_TX_SUM::response& res, epee::json_rpc::error& error_resp); bool on_get_per_kb_fee_estimate(const COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::request& req, COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::response& res, epee::json_rpc::error& error_resp); bool on_get_alternate_chains(const COMMAND_RPC_GET_ALTERNATE_CHAINS::request& req, COMMAND_RPC_GET_ALTERNATE_CHAINS::response& res, epee::json_rpc::error& error_resp); + bool on_relay_tx(const COMMAND_RPC_RELAY_TX::request& req, COMMAND_RPC_RELAY_TX::response& res, epee::json_rpc::error& error_resp); //----------------------- private: - - bool handle_command_line( - const boost::program_options::variables_map& vm - ); bool check_core_busy(); bool check_core_ready(); @@ -188,8 +188,6 @@ private: core& m_core; nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core> >& m_p2p; - std::string m_port; - std::string m_bind_ip; bool m_testnet; bool m_restricted; }; diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 0fc230d11..9bdadf0d1 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -30,8 +30,8 @@ #pragma once #include "cryptonote_protocol/cryptonote_protocol_defs.h" -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_core/difficulty.h" +#include "cryptonote_basic/cryptonote_basic.h" +#include "cryptonote_basic/difficulty.h" #include "crypto/hash.h" namespace cryptonote @@ -49,7 +49,7 @@ namespace cryptonote // advance which version they will stop working with // Don't go over 32767 for any of these #define CORE_RPC_VERSION_MAJOR 1 -#define CORE_RPC_VERSION_MINOR 6 +#define CORE_RPC_VERSION_MINOR 10 #define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR) @@ -80,9 +80,11 @@ namespace cryptonote { std::list<crypto::hash> block_ids; //*first 10 blocks id goes sequential, next goes in pow(2,n) offset, like 2, 4, 8, 16, 32, 64 and so on, and the last one is always genesis block */ uint64_t start_height; + bool prune; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE_CONTAINER_POD_AS_BLOB(block_ids) KV_SERIALIZE(start_height) + KV_SERIALIZE(prune) END_KV_SERIALIZE_MAP() }; @@ -500,10 +502,14 @@ namespace cryptonote { std::string miner_address; uint64_t threads_count; + bool do_background_mining; + bool ignore_battery; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(miner_address) KV_SERIALIZE(threads_count) + KV_SERIALIZE(do_background_mining) + KV_SERIALIZE(ignore_battery) END_KV_SERIALIZE_MAP() }; @@ -608,6 +614,7 @@ namespace cryptonote uint64_t speed; uint32_t threads_count; std::string address; + bool is_background_mining_enabled; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) @@ -615,6 +622,7 @@ namespace cryptonote KV_SERIALIZE(speed) KV_SERIALIZE(threads_count) KV_SERIALIZE(address) + KV_SERIALIZE(is_background_mining_enabled) END_KV_SERIALIZE_MAP() }; }; @@ -684,6 +692,7 @@ namespace cryptonote uint64_t difficulty; uint64_t height; uint64_t reserved_offset; + uint64_t expected_reward; std::string prev_hash; blobdata blocktemplate_blob; blobdata blockhashing_blob; @@ -693,6 +702,7 @@ namespace cryptonote KV_SERIALIZE(difficulty) KV_SERIALIZE(height) KV_SERIALIZE(reserved_offset) + KV_SERIALIZE(expected_reward) KV_SERIALIZE(prev_hash) KV_SERIALIZE(blocktemplate_blob) KV_SERIALIZE(blockhashing_blob) @@ -1017,6 +1027,26 @@ namespace cryptonote }; }; + struct COMMAND_RPC_GET_TRANSACTION_POOL_HASHES + { + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + std::vector<crypto::hash> tx_hashes; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(tx_hashes) + END_KV_SERIALIZE_MAP() + }; + }; + struct COMMAND_RPC_GET_CONNECTIONS { struct request @@ -1432,4 +1462,60 @@ namespace cryptonote END_KV_SERIALIZE_MAP() }; }; + + struct COMMAND_RPC_UPDATE + { + struct request + { + std::string command; + std::string path; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(command); + KV_SERIALIZE(path); + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + bool update; + std::string version; + std::string user_uri; + std::string auto_uri; + std::string hash; + std::string path; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + KV_SERIALIZE(update) + KV_SERIALIZE(version) + KV_SERIALIZE(user_uri) + KV_SERIALIZE(auto_uri) + KV_SERIALIZE(hash) + KV_SERIALIZE(path) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_RELAY_TX + { + struct request + { + std::list<std::string> txids; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(txids) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; } diff --git a/src/rpc/core_rpc_server_error_codes.h b/src/rpc/core_rpc_server_error_codes.h index 4ac48a1c1..269cce2b1 100644 --- a/src/rpc/core_rpc_server_error_codes.h +++ b/src/rpc/core_rpc_server_error_codes.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/rpc/rpc_args.cpp b/src/rpc/rpc_args.cpp new file mode 100644 index 000000000..4435f74d1 --- /dev/null +++ b/src/rpc/rpc_args.cpp @@ -0,0 +1,96 @@ +// Copyright (c) 2014-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +#include "rpc_args.h" + +#include <boost/asio/ip/address.hpp> +#include "common/command_line.h" +#include "common/i18n.h" + +namespace cryptonote +{ + rpc_args::descriptors::descriptors() + : rpc_bind_ip({"rpc-bind-ip", rpc_args::tr("Specify ip to bind rpc server"), "127.0.0.1"}) + , rpc_login({"rpc-login", rpc_args::tr("Specify username[:password] required for RPC server"), "", true}) + , confirm_external_bind({"confirm-external-bind", rpc_args::tr("Confirm rpc-bind-ip value is NOT a loopback (local) IP")}) + {} + + const char* rpc_args::tr(const char* str) { return i18n_translate(str, "cryptonote::rpc_args"); } + + void rpc_args::init_options(boost::program_options::options_description& desc) + { + const descriptors arg{}; + command_line::add_arg(desc, arg.rpc_bind_ip); + command_line::add_arg(desc, arg.rpc_login); + command_line::add_arg(desc, arg.confirm_external_bind); + } + + boost::optional<rpc_args> rpc_args::process(const boost::program_options::variables_map& vm) + { + const descriptors arg{}; + rpc_args config{}; + + config.bind_ip = command_line::get_arg(vm, arg.rpc_bind_ip); + if (!config.bind_ip.empty()) + { + // always parse IP here for error consistency + boost::system::error_code ec{}; + const auto parsed_ip = boost::asio::ip::address::from_string(config.bind_ip, ec); + if (ec) + { + LOG_ERROR(tr("Invalid IP address given for --") << arg.rpc_bind_ip.name); + return boost::none; + } + + if (!parsed_ip.is_loopback() && !command_line::get_arg(vm, arg.confirm_external_bind)) + { + LOG_ERROR( + "--" << arg.rpc_bind_ip.name << + tr(" permits inbound unencrypted external connections. Consider SSH tunnel or SSL proxy instead. Override with --") << + arg.confirm_external_bind.name + ); + return boost::none; + } + } + + if (command_line::has_arg(vm, arg.rpc_login)) + { + config.login = tools::login::parse(command_line::get_arg(vm, arg.rpc_login), true, "RPC server password"); + if (!config.login) + return boost::none; + + if (config.login->username.empty()) + { + LOG_ERROR(tr("Username specified with --") << arg.rpc_login.name << tr(" cannot be empty")); + return boost::none; + } + } + + return {std::move(config)}; + } +} diff --git a/src/rpc/rpc_args.h b/src/rpc/rpc_args.h new file mode 100644 index 000000000..d6e7bab07 --- /dev/null +++ b/src/rpc/rpc_args.h @@ -0,0 +1,67 @@ +// Copyright (c) 2014-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +#pragma once + +#include <boost/optional/optional.hpp> +#include <boost/program_options/options_description.hpp> +#include <boost/program_options/variables_map.hpp> +#include <string> + +#include "common/command_line.h" +#include "common/password.h" + +namespace cryptonote +{ + //! Processes command line arguments related to server-side RPC + struct rpc_args + { + // non-static construction prevents initialization order issues + struct descriptors + { + descriptors(); + descriptors(const descriptors&) = delete; + descriptors(descriptors&&) = delete; + descriptors& operator=(const descriptors&) = delete; + descriptors& operator=(descriptors&&) = delete; + + const command_line::arg_descriptor<std::string> rpc_bind_ip; + const command_line::arg_descriptor<std::string> rpc_login; + const command_line::arg_descriptor<bool> confirm_external_bind; + }; + + static const char* tr(const char* str); + static void init_options(boost::program_options::options_description& desc); + + //! \return Arguments specified by user, or `boost::none` if error + static boost::optional<rpc_args> process(const boost::program_options::variables_map& vm); + + std::string bind_ip; + boost::optional<tools::login> login; // currently `boost::none` if unspecified by user + }; +} diff --git a/src/serialization/binary_archive.h b/src/serialization/binary_archive.h index 3a5343c9c..0a267b081 100644 --- a/src/serialization/binary_archive.h +++ b/src/serialization/binary_archive.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/serialization/binary_utils.h b/src/serialization/binary_utils.h index ab4a86c68..08eba41da 100644 --- a/src/serialization/binary_utils.h +++ b/src/serialization/binary_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/serialization/crypto.h b/src/serialization/crypto.h index 9a7e89c49..4213f2e58 100644 --- a/src/serialization/crypto.h +++ b/src/serialization/crypto.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/serialization/debug_archive.h b/src/serialization/debug_archive.h index f46a7ee2b..c5365aab7 100644 --- a/src/serialization/debug_archive.h +++ b/src/serialization/debug_archive.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/serialization/json_archive.h b/src/serialization/json_archive.h index 629a37311..8f74e26b1 100644 --- a/src/serialization/json_archive.h +++ b/src/serialization/json_archive.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/serialization/json_utils.h b/src/serialization/json_utils.h index 5b7871072..32e7b69cf 100644 --- a/src/serialization/json_utils.h +++ b/src/serialization/json_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h index dac43720b..639240820 100644 --- a/src/serialization/serialization.h +++ b/src/serialization/serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/serialization/string.h b/src/serialization/string.h index 2a5228a3f..b94f43dd8 100644 --- a/src/serialization/string.h +++ b/src/serialization/string.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/serialization/variant.h b/src/serialization/variant.h index a2cce9fa1..9048e2963 100644 --- a/src/serialization/variant.h +++ b/src/serialization/variant.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/serialization/vector.h b/src/serialization/vector.h index 7f2bc78ba..598cfb92e 100644 --- a/src/serialization/vector.h +++ b/src/serialization/vector.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/simplewallet/CMakeLists.txt b/src/simplewallet/CMakeLists.txt index 259008ac5..18a8bac68 100644 --- a/src/simplewallet/CMakeLists.txt +++ b/src/simplewallet/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index a8f1d177f..6c2df4b22 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -50,7 +50,7 @@ #include "p2p/net_node.h" #include "cryptonote_protocol/cryptonote_protocol_handler.h" #include "simplewallet.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_format_utils.h" #include "storages/http_abstract_invoke.h" #include "rpc/core_rpc_server_commands_defs.h" #include "crypto/crypto.h" // for crypto::secret_key definition @@ -101,6 +101,7 @@ enum TransferType { namespace { + const auto allowed_priority_strings = {"default", "unimportant", "normal", "elevated", "priority"}; const auto arg_wallet_file = wallet_args::arg_wallet_file(); const command_line::arg_descriptor<std::string> arg_generate_new_wallet = {"generate-new-wallet", sw::tr("Generate new wallet and save it to <arg>"), ""}; const command_line::arg_descriptor<std::string> arg_generate_from_view_key = {"generate-from-view-key", sw::tr("Generate incoming-only wallet from view key"), ""}; @@ -377,12 +378,12 @@ bool simple_wallet::change_password(const std::vector<std::string> &args) if(orig_pwd_container == boost::none) { fail_msg_writer() << tr("Your original password was incorrect."); - return false; + return true; } - // prompts for a new password, this is not a new wallet so pass in false. - const auto pwd_container = tools::wallet2::password_prompt(false); - + // prompts for a new password, pass true to verify the password + const auto pwd_container = tools::wallet2::password_prompt(true); + try { m_wallet->rewrite(m_wallet_file, pwd_container->password()); @@ -391,7 +392,7 @@ bool simple_wallet::change_password(const std::vector<std::string> &args) catch (const tools::error::wallet_logic_error& e) { fail_msg_writer() << tr("Error with wallet rewrite: ") << e.what(); - return false; + return true; } return true; @@ -482,16 +483,11 @@ bool simple_wallet::set_default_mixin(const std::vector<std::string> &args/* = s bool simple_wallet::set_default_priority(const std::vector<std::string> &args/* = std::vector<std::string>()*/) { int priority = 0; - if (m_wallet->watch_only()) - { - fail_msg_writer() << tr("wallet is watch-only and cannot transfer"); - return true; - } try { if (strchr(args[1].c_str(), '-')) { - fail_msg_writer() << tr("priority must be 0, 1, 2, or 3 "); + fail_msg_writer() << tr("priority must be 0, 1, 2, 3, or 4 "); return true; } if (args[1] == "0") @@ -501,9 +497,9 @@ bool simple_wallet::set_default_priority(const std::vector<std::string> &args/* else { priority = boost::lexical_cast<int>(args[1]); - if (priority != 1 && priority != 2 && priority != 3) + if (priority < 1 || priority > 4) { - fail_msg_writer() << tr("priority must be 0, 1, 2, or 3"); + fail_msg_writer() << tr("priority must be 0, 1, 2, 3,or 4"); return true; } } @@ -518,7 +514,7 @@ bool simple_wallet::set_default_priority(const std::vector<std::string> &args/* } catch(const boost::bad_lexical_cast &) { - fail_msg_writer() << tr("priority must be 0, 1, 2 or 3"); + fail_msg_writer() << tr("priority must be 0, 1, 2 3,or 4"); return true; } catch(...) @@ -564,27 +560,98 @@ bool simple_wallet::set_refresh_type(const std::vector<std::string> &args/* = st bool simple_wallet::set_confirm_missing_payment_id(const std::vector<std::string> &args/* = std::vector<std::string>()*/) { - if (m_wallet->watch_only()) + const auto pwd_container = get_and_verify_password(); + if (pwd_container) { - fail_msg_writer() << tr("wallet is watch-only and cannot transfer"); + m_wallet->confirm_missing_payment_id(is_it_true(args[1])); + m_wallet->rewrite(m_wallet_file, pwd_container->password()); + } + return true; +} + +bool simple_wallet::set_ask_password(const std::vector<std::string> &args/* = std::vector<std::string>()*/) +{ + const auto pwd_container = get_and_verify_password(); + if (pwd_container) + { + m_wallet->ask_password(is_it_true(args[1])); + m_wallet->rewrite(m_wallet_file, pwd_container->password()); + } + return true; +} + +bool simple_wallet::set_unit(const std::vector<std::string> &args/* = std::vector<std::string>()*/) +{ + const std::string &unit = args[1]; + unsigned int decimal_point = CRYPTONOTE_DISPLAY_DECIMAL_POINT; + + if (unit == "monero") + decimal_point = CRYPTONOTE_DISPLAY_DECIMAL_POINT; + else if (unit == "millinero") + decimal_point = CRYPTONOTE_DISPLAY_DECIMAL_POINT - 3; + else if (unit == "micronero") + decimal_point = CRYPTONOTE_DISPLAY_DECIMAL_POINT - 6; + else if (unit == "nanonero") + decimal_point = CRYPTONOTE_DISPLAY_DECIMAL_POINT - 9; + else if (unit == "piconero") + decimal_point = 0; + else + { + fail_msg_writer() << tr("invalid unit"); return true; } const auto pwd_container = get_and_verify_password(); if (pwd_container) { - m_wallet->confirm_missing_payment_id(is_it_true(args[1])); + m_wallet->set_default_decimal_point(decimal_point); m_wallet->rewrite(m_wallet_file, pwd_container->password()); } return true; } -bool simple_wallet::set_ask_password(const std::vector<std::string> &args/* = std::vector<std::string>()*/) +bool simple_wallet::set_min_output_count(const std::vector<std::string> &args/* = std::vector<std::string>()*/) { + uint32_t count; + if (!string_tools::get_xtype_from_string(count, args[1])) + { + fail_msg_writer() << tr("invalid count: must be an unsigned integer"); + return true; + } + const auto pwd_container = get_and_verify_password(); if (pwd_container) { - m_wallet->ask_password(is_it_true(args[1])); + m_wallet->set_min_output_count(count); + m_wallet->rewrite(m_wallet_file, pwd_container->password()); + } + return true; +} + +bool simple_wallet::set_min_output_value(const std::vector<std::string> &args/* = std::vector<std::string>()*/) +{ + uint64_t value; + if (!cryptonote::parse_amount(value, args[1])) + { + fail_msg_writer() << tr("invalid value"); + return true; + } + + const auto pwd_container = get_and_verify_password(); + if (pwd_container) + { + m_wallet->set_min_output_value(value); + m_wallet->rewrite(m_wallet_file, pwd_container->password()); + } + return true; +} + +bool simple_wallet::set_merge_destinations(const std::vector<std::string> &args/* = std::vector<std::string>()*/) +{ + const auto pwd_container = get_and_verify_password(); + if (pwd_container) + { + m_wallet->merge_destinations(is_it_true(args[1])); m_wallet->rewrite(m_wallet_file, pwd_container->password()); } return true; @@ -612,11 +679,12 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("incoming_transfers", boost::bind(&simple_wallet::show_incoming_transfers, this, _1), tr("incoming_transfers [available|unavailable] - Show incoming transfers, all or filtered by availability")); m_cmd_binder.set_handler("payments", boost::bind(&simple_wallet::show_payments, this, _1), tr("payments <PID_1> [<PID_2> ... <PID_N>] - Show payments for given payment ID[s]")); m_cmd_binder.set_handler("bc_height", boost::bind(&simple_wallet::show_blockchain_height, this, _1), tr("Show blockchain height")); - m_cmd_binder.set_handler("transfer_original", boost::bind(&simple_wallet::transfer, this, _1), tr("transfer [<mixin_count>] <addr_1> <amount_1> [<addr_2> <amount_2> ... <addr_N> <amount_N>] [payment_id] - Transfer <amount_1>,... <amount_N> to <address_1>,... <address_N>, respectively. <mixin_count> is the number of extra inputs to include for untraceability (from 2 to maximum available)")); - m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer_new, this, _1), tr("Same as transfer_original, but using a new transaction building algorithm")); + m_cmd_binder.set_handler("transfer_original", boost::bind(&simple_wallet::transfer, this, _1), tr("Same as transfer, but using an older transaction building algorithm")); + m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer_new, this, _1), tr("transfer [<priority>] [<mixin_count>] <address> <amount> [<payment_id>] - Transfer <amount> to <address>. <priority> is the priority of the transaction. The higher the priority, the higher the fee of the transaction. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <mixin_count> is the number of extra inputs to include for untraceability. Multiple payments can be made at once by adding <address_2> <amount_2> etcetera (before the payment ID, if it's included)")); m_cmd_binder.set_handler("locked_transfer", boost::bind(&simple_wallet::locked_transfer, this, _1), tr("locked_transfer [<mixin_count>] <addr> <amount> <lockblocks>(Number of blocks to lock the transaction for, max 1000000) [<payment_id>]")); m_cmd_binder.set_handler("sweep_unmixable", boost::bind(&simple_wallet::sweep_unmixable, this, _1), tr("Send all unmixable outputs to yourself with mixin 0")); - m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1), tr("sweep_all [mixin] address [payment_id] - Send all unlocked balance an address")); + m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1), tr("sweep_all [mixin] address [payment_id] - Send all unlocked balance to an address")); + m_cmd_binder.set_handler("sweep_below", boost::bind(&simple_wallet::sweep_below, this, _1), tr("sweep_below <amount_threshold> [mixin] address [payment_id] - Send all unlocked outputs below the threshold to an address")); m_cmd_binder.set_handler("donate", boost::bind(&simple_wallet::donate, this, _1), tr("donate [<mixin_count>] <amount> [payment_id] - Donate <amount> to the development team (donate.getmonero.org)")); m_cmd_binder.set_handler("sign_transfer", boost::bind(&simple_wallet::sign_transfer, this, _1), tr("Sign a transaction from a file")); m_cmd_binder.set_handler("submit_transfer", boost::bind(&simple_wallet::submit_transfer, this, _1), tr("Submit a signed transaction from a file")); @@ -629,7 +697,7 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("viewkey", boost::bind(&simple_wallet::viewkey, this, _1), tr("Display private view key")); m_cmd_binder.set_handler("spendkey", boost::bind(&simple_wallet::spendkey, this, _1), tr("Display private spend key")); m_cmd_binder.set_handler("seed", boost::bind(&simple_wallet::seed, this, _1), tr("Display Electrum-style mnemonic seed")); - m_cmd_binder.set_handler("set", boost::bind(&simple_wallet::set_variable, this, _1), tr("Available options: seed language - set wallet seed language; always-confirm-transfers <1|0> - whether to confirm unsplit txes; print-ring-members <1|0> - whether to print detailed information about ring members during confirmation; store-tx-info <1|0> - whether to store outgoing tx info (destination address, payment ID, tx secret key) for future reference; default-mixin <n> - set default mixin (default is 4); auto-refresh <1|0> - whether to automatically sync new blocks from the daemon; refresh-type <full|optimize-coinbase|no-coinbase|default> - set wallet refresh behaviour; priority [1|2|3] - normal/elevated/priority fee; confirm-missing-payment-id <1|0>")); + m_cmd_binder.set_handler("set", boost::bind(&simple_wallet::set_variable, this, _1), tr("Available options: seed language - set wallet seed language; always-confirm-transfers <1|0> - whether to confirm unsplit txes; print-ring-members <1|0> - whether to print detailed information about ring members during confirmation; store-tx-info <1|0> - whether to store outgoing tx info (destination address, payment ID, tx secret key) for future reference; default-mixin <n> - set default mixin (default is 4); auto-refresh <1|0> - whether to automatically sync new blocks from the daemon; refresh-type <full|optimize-coinbase|no-coinbase|default> - set wallet refresh behaviour; priority [0|1|2|3|4] - default/unimportant/normal/elevated/priority fee; confirm-missing-payment-id <1|0>; ask-password <1|0>; unit <monero|millinero|micronero|nanonero|piconero> - set default monero (sub-)unit; min-output-count [n] - try to keep at least that many outputs of value at least min-output-value; min-output-value [n] - try to keep at least min-output-count outputs of at least that value - merge-destinations <1|0> - whether to merge multiple payments to the same destination address")); m_cmd_binder.set_handler("rescan_spent", boost::bind(&simple_wallet::rescan_spent, this, _1), tr("Rescan blockchain for spent outputs")); m_cmd_binder.set_handler("get_tx_key", boost::bind(&simple_wallet::get_tx_key, this, _1), tr("Get transaction key (r) for a given <txid>")); m_cmd_binder.set_handler("check_tx_key", boost::bind(&simple_wallet::check_tx_key, this, _1), tr("Check amount going to <address> in <txid>")); @@ -646,6 +714,7 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("export_outputs", boost::bind(&simple_wallet::export_outputs, this, _1), tr("Export a set of outputs owned by this wallet")); m_cmd_binder.set_handler("import_outputs", boost::bind(&simple_wallet::import_outputs, this, _1), tr("Import set of outputs owned by this wallet")); m_cmd_binder.set_handler("show_transfer", boost::bind(&simple_wallet::show_transfer, this, _1), tr("Show information about a transfer to/from this address")); + m_cmd_binder.set_handler("password", boost::bind(&simple_wallet::change_password, this, _1), tr("Change wallet password")); m_cmd_binder.set_handler("help", boost::bind(&simple_wallet::help, this, _1), tr("Show this help")); } //---------------------------------------------------------------------------------------------------- @@ -663,10 +732,29 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args) success_msg_writer() << "priority = " << m_wallet->get_default_priority(); success_msg_writer() << "confirm-missing-payment-id = " << m_wallet->confirm_missing_payment_id(); success_msg_writer() << "ask-password = " << m_wallet->ask_password(); + success_msg_writer() << "unit = " << cryptonote::get_unit(m_wallet->get_default_decimal_point()); + success_msg_writer() << "min-outputs-count = " << m_wallet->get_min_output_count(); + success_msg_writer() << "min-outputs-value = " << cryptonote::print_money(m_wallet->get_min_output_value()); + success_msg_writer() << "merge-destinations = " << m_wallet->merge_destinations(); return true; } else { + +#define CHECK_SIMPLE_VARIABLE(name, f, help) do \ + if (args[0] == name) { \ + if (args.size() <= 1) \ + { \ + fail_msg_writer() << "set " << #name << ": " << tr("needs an argument") << " (" << help << ")"; \ + return true; \ + } \ + else \ + { \ + f(args); \ + return true; \ + } \ + } while(0) + if (args[0] == "seed") { if (args.size() == 1) @@ -680,124 +768,19 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args) return true; } } - else if (args[0] == "always-confirm-transfers") - { - if (args.size() <= 1) - { - fail_msg_writer() << tr("set always-confirm-transfers: needs an argument (0 or 1)"); - return true; - } - else - { - set_always_confirm_transfers(args); - return true; - } - } - else if (args[0] == "print-ring-members") - { - if (args.size() <= 1) - { - fail_msg_writer() << tr("set print-ring-members: needs an argument (0 or 1)"); - return true; - } - else - { - set_print_ring_members(args); - return true; - } - } - else if (args[0] == "store-tx-info") - { - if (args.size() <= 1) - { - fail_msg_writer() << tr("set store-tx-info: needs an argument (0 or 1)"); - return true; - } - else - { - set_store_tx_info(args); - return true; - } - } - else if (args[0] == "default-mixin") - { - if (args.size() <= 1) - { - fail_msg_writer() << tr("set default-mixin: needs an argument (integer >= 2)"); - return true; - } - else - { - set_default_mixin(args); - return true; - } - } - else if (args[0] == "auto-refresh") - { - if (args.size() <= 1) - { - fail_msg_writer() << tr("set auto-refresh: needs an argument (0 or 1)"); - return true; - } - else - { - set_auto_refresh(args); - return true; - } - } - else if (args[0] == "refresh-type") - { - if (args.size() <= 1) - { - fail_msg_writer() << tr("set refresh-type: needs an argument:") << - tr("full (slowest, no assumptions); optimize-coinbase (fast, assumes the whole coinbase is paid to a single address); no-coinbase (fastest, assumes we receive no coinbase transaction), default (same as optimize-coinbase)"); - return true; - } - else - { - set_refresh_type(args); - return true; - } - } - else if (args[0] == "priority") - { - if (args.size() <= 1) - { - fail_msg_writer() << tr("set priority: needs an argument: 0, 1, 2, or 3"); - return true; - } - else - { - set_default_priority(args); - return true; - } - } - else if (args[0] == "confirm-missing-payment-id") - { - if (args.size() <= 1) - { - fail_msg_writer() << tr("set confirm-missing-payment-id: needs an argument (0 or 1)"); - return true; - } - else - { - set_confirm_missing_payment_id(args); - return true; - } - } - else if (args[0] == "ask-password") - { - if (args.size() <= 1) - { - fail_msg_writer() << tr("set ask-password: needs an argument (0 or 1)"); - return true; - } - else - { - set_ask_password(args); - return true; - } - } + CHECK_SIMPLE_VARIABLE("always-confirm-transfers", set_always_confirm_transfers, tr("0 or 1")); + CHECK_SIMPLE_VARIABLE("print-ring-members", set_print_ring_members, tr("0 or 1")); + CHECK_SIMPLE_VARIABLE("store-tx-info", set_store_tx_info, tr("0 or 1")); + CHECK_SIMPLE_VARIABLE("default-mixin", set_default_mixin, tr("integer >= 2")); + CHECK_SIMPLE_VARIABLE("auto-refresh", set_auto_refresh, tr("0 or 1")); + CHECK_SIMPLE_VARIABLE("refresh-type", set_refresh_type, tr("full (slowest, no assumptions); optimize-coinbase (fast, assumes the whole coinbase is paid to a single address); no-coinbase (fastest, assumes we receive no coinbase transaction), default (same as optimize-coinbase)")); + CHECK_SIMPLE_VARIABLE("priority", set_default_priority, tr("0, 1, 2, 3, or 4")); + CHECK_SIMPLE_VARIABLE("confirm-missing-payment-id", set_confirm_missing_payment_id, tr("0 or 1")); + CHECK_SIMPLE_VARIABLE("ask-password", set_ask_password, tr("0 or 1")); + CHECK_SIMPLE_VARIABLE("unit", set_unit, tr("monero, millinero, micronero, nanop, piconero")); + CHECK_SIMPLE_VARIABLE("min-outputs-count", set_min_output_count, tr("unsigned integer")); + CHECK_SIMPLE_VARIABLE("min-outputs-value", set_min_output_value, tr("amount")); + CHECK_SIMPLE_VARIABLE("merge-destinations", set_merge_destinations, tr("0 or 1")); } fail_msg_writer() << tr("set: unrecognized argument(s)"); return true; @@ -811,21 +794,7 @@ bool simple_wallet::set_log(const std::vector<std::string> &args) fail_msg_writer() << tr("usage: set_log <log_level_number_0-4> | <categories>"); return true; } - uint16_t l = 0; - if(epee::string_tools::get_xtype_from_string(l, args[0])) - { - if(4 < l) - { - fail_msg_writer() << tr("wrong number range, use: set_log <log_level_number_0-4>"); - return true; - } - - mlog_set_log_level(l); - } - else - { - mlog_set_categories(args.front().c_str()); - } + mlog_set_log(args[0].c_str()); return true; } //---------------------------------------------------------------------------------------------------- @@ -923,6 +892,15 @@ void simple_wallet::print_seed(std::string seed) std::cout << seed << std::endl; } //---------------------------------------------------------------------------------------------------- +static bool might_be_partial_seed(std::string words) +{ + std::vector<std::string> seed; + + boost::algorithm::trim(words); + boost::split(seed, words, boost::is_any_of(" "), boost::token_compress_on); + return seed.size() < 24; +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::init(const boost::program_options::variables_map& vm) { if (!handle_command_line(vm)) @@ -952,14 +930,20 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) if (m_electrum_seed.empty()) { - m_electrum_seed = command_line::input_line("Specify Electrum seed: "); - if (std::cin.eof()) - return false; - if (m_electrum_seed.empty()) + m_electrum_seed = ""; + do { - fail_msg_writer() << tr("specify a recovery parameter with the --electrum-seed=\"words list here\""); - return false; - } + const char *prompt = m_electrum_seed.empty() ? "Specify Electrum seed: " : "Electrum seed continued: "; + std::string electrum_seed = command_line::input_line(prompt); + if (std::cin.eof()) + return false; + if (electrum_seed.empty()) + { + fail_msg_writer() << tr("specify a recovery parameter with the --electrum-seed=\"words list here\""); + return false; + } + m_electrum_seed += electrum_seed + " "; + } while (might_be_partial_seed(m_electrum_seed)); } if (!crypto::ElectrumWords::words_to_bytes(m_electrum_seed, m_recovery_key, old_language)) @@ -997,7 +981,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) return false; } cryptonote::blobdata viewkey_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(viewkey_string, viewkey_data)) + if(!epee::string_tools::parse_hexstr_to_binbuff(viewkey_string, viewkey_data) || viewkey_data.size() != sizeof(crypto::secret_key)) { fail_msg_writer() << tr("failed to parse view key secret key"); return false; @@ -1049,7 +1033,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) return false; } cryptonote::blobdata spendkey_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(spendkey_string, spendkey_data)) + if(!epee::string_tools::parse_hexstr_to_binbuff(spendkey_string, spendkey_data) || spendkey_data.size() != sizeof(crypto::secret_key)) { fail_msg_writer() << tr("failed to parse spend key secret key"); return false; @@ -1065,7 +1049,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) return false; } cryptonote::blobdata viewkey_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(viewkey_string, viewkey_data)) + if(!epee::string_tools::parse_hexstr_to_binbuff(viewkey_string, viewkey_data) || viewkey_data.size() != sizeof(crypto::secret_key)) { fail_msg_writer() << tr("failed to parse view key secret key"); return false; @@ -1192,7 +1176,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) } catch (const std::exception &e) { } - m_http_client.set_server(m_wallet->get_daemon_address()); + m_http_client.set_server(m_wallet->get_daemon_address(), m_wallet->get_daemon_login()); m_wallet->callback(this); return true; } @@ -1282,7 +1266,7 @@ std::string simple_wallet::get_mnemonic_language() fail_msg_writer() << tr("invalid language choice passed. Please try again.\n"); } } - catch (std::exception &e) + catch (const std::exception &e) { fail_msg_writer() << tr("invalid language choice passed. Please try again.\n"); } @@ -1561,16 +1545,15 @@ bool simple_wallet::start_mining(const std::vector<std::string>& args) return true; assert(m_wallet); - COMMAND_RPC_START_MINING::request req; + COMMAND_RPC_START_MINING::request req = AUTO_VAL_INIT(req); req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->testnet()); bool ok = true; size_t max_mining_threads_count = (std::max)(tools::get_max_concurrency(), static_cast<unsigned>(2)); - if (0 == args.size()) - { - req.threads_count = 1; - } - else if (1 == args.size()) + size_t arg_size = args.size(); + if(arg_size >= 3) req.ignore_battery = args[2] == "true"; + if(arg_size >= 2) req.do_background_mining = args[1] == "true"; + if(arg_size >= 1) { uint16_t num = 1; ok = string_tools::get_xtype_from_string(num, args[0]); @@ -1579,12 +1562,12 @@ bool simple_wallet::start_mining(const std::vector<std::string>& args) } else { - ok = false; + req.threads_count = 1; } if (!ok) { - fail_msg_writer() << tr("invalid arguments. Please use start_mining [<number_of_threads>], " + fail_msg_writer() << tr("invalid arguments. Please use start_mining [<number_of_threads>] [do_bg_mining] [ignore_battery], " "<number_of_threads> should be from 1 to ") << max_mining_threads_count; return true; } @@ -1639,11 +1622,11 @@ void simple_wallet::on_new_block(uint64_t height, const cryptonote::block& block m_refresh_progress_reporter.update(height, false); } //---------------------------------------------------------------------------------------------------- -void simple_wallet::on_money_received(uint64_t height, const cryptonote::transaction& tx, uint64_t amount) +void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount) { message_writer(console_color_green, false) << "\r" << tr("Height ") << height << ", " << - tr("transaction ") << get_transaction_hash(tx) << ", " << + tr("transaction ") << txid << ", " << tr("received ") << print_money(amount); if (m_auto_refresh_refreshing) m_cmd_binder.print_prompt(); @@ -1651,16 +1634,16 @@ void simple_wallet::on_money_received(uint64_t height, const cryptonote::transac m_refresh_progress_reporter.update(height, true); } //---------------------------------------------------------------------------------------------------- -void simple_wallet::on_unconfirmed_money_received(uint64_t height, const cryptonote::transaction& tx, uint64_t amount) +void simple_wallet::on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount) { // Not implemented in CLI wallet } //---------------------------------------------------------------------------------------------------- -void simple_wallet::on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx) +void simple_wallet::on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx) { message_writer(console_color_magenta, false) << "\r" << tr("Height ") << height << ", " << - tr("transaction ") << get_transaction_hash(spend_tx) << ", " << + tr("transaction ") << txid << ", " << tr("spent ") << print_money(amount); if (m_auto_refresh_refreshing) m_cmd_binder.print_prompt(); @@ -1668,11 +1651,11 @@ void simple_wallet::on_money_spent(uint64_t height, const cryptonote::transactio m_refresh_progress_reporter.update(height, true); } //---------------------------------------------------------------------------------------------------- -void simple_wallet::on_skip_transaction(uint64_t height, const cryptonote::transaction& tx) +void simple_wallet::on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx) { message_writer(console_color_red, true) << "\r" << tr("Height ") << height << ", " << - tr("transaction ") << get_transaction_hash(tx) << ", " << + tr("transaction ") << txid << ", " << tr("unsupported transaction format"); if (m_auto_refresh_refreshing) m_cmd_binder.print_prompt(); @@ -2091,6 +2074,18 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri std::vector<std::string> local_args = args_; + int priority = 0; + if(local_args.size() > 0) { + auto priority_pos = std::find( + allowed_priority_strings.begin(), + allowed_priority_strings.end(), + local_args[0]); + if(priority_pos != allowed_priority_strings.end()) { + local_args.erase(local_args.begin()); + priority = std::distance(allowed_priority_strings.begin(), priority_pos); + } + } + size_t fake_outs_count; if(local_args.size() > 0) { if(!epee::string_tools::get_xtype_from_string(fake_outs_count, local_args[0])) @@ -2175,7 +2170,10 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri bool has_payment_id; crypto::hash8 new_payment_id; if (!cryptonote::get_account_address_from_str_or_url(de.addr, has_payment_id, new_payment_id, m_wallet->testnet(), local_args[i])) + { + fail_msg_writer() << tr("failed to parse address"); return true; + } if (has_payment_id) { @@ -2217,8 +2215,6 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri { fail_msg_writer() << tr("transaction cancelled."); - // would like to return false, because no tx made, but everything else returns true - // and I don't know what returning false might adversely affect. *sigh* return true; } } @@ -2239,18 +2235,24 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri return true; } unlock_block = bc_height + locked_blocks; - ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, unlock_block /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon); + ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_trusted_daemon); break; case TransferNew: - ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon); + ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, priority, extra, m_trusted_daemon); break; default: LOG_ERROR("Unknown transfer method, using original"); case TransferOriginal: - ptx_vector = m_wallet->create_transactions(dsts, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon); + ptx_vector = m_wallet->create_transactions(dsts, fake_outs_count, 0 /* unlock_time */, priority, extra, m_trusted_daemon); break; } + if (ptx_vector.empty()) + { + fail_msg_writer() << tr("No outputs found, or daemon is not ready"); + return true; + } + // if more than one tx necessary, prompt user to confirm if (m_wallet->always_confirm_transfers() || ptx_vector.size() > 1) { @@ -2306,8 +2308,6 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri { fail_msg_writer() << tr("transaction cancelled."); - // would like to return false, because no tx made, but everything else returns true - // and I don't know what returning false might adversely affect. *sigh* return true; } } @@ -2486,8 +2486,6 @@ bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_) { fail_msg_writer() << tr("transaction cancelled."); - // would like to return false, because no tx made, but everything else returns true - // and I don't know what returning false might adversely affect. *sigh* return true; } @@ -2603,7 +2601,7 @@ bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_) return true; } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::sweep_all(const std::vector<std::string> &args_) +bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &args_) { if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } if (!try_connect_to_daemon()) @@ -2670,7 +2668,10 @@ bool simple_wallet::sweep_all(const std::vector<std::string> &args_) crypto::hash8 new_payment_id; cryptonote::account_public_address address; if (!cryptonote::get_account_address_from_str_or_url(address, has_payment_id, new_payment_id, m_wallet->testnet(), local_args[0])) + { + fail_msg_writer() << tr("failed to parse address"); return true; + } if (has_payment_id) { @@ -2701,8 +2702,6 @@ bool simple_wallet::sweep_all(const std::vector<std::string> &args_) { fail_msg_writer() << tr("transaction cancelled."); - // would like to return false, because no tx made, but everything else returns true - // and I don't know what returning false might adversely affect. *sigh* return true; } } @@ -2710,11 +2709,11 @@ bool simple_wallet::sweep_all(const std::vector<std::string> &args_) try { // figure out what tx will be necessary - auto ptx_vector = m_wallet->create_transactions_all(address, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon); + auto ptx_vector = m_wallet->create_transactions_all(below, address, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon); if (ptx_vector.empty()) { - fail_msg_writer() << tr("No outputs found"); + fail_msg_writer() << tr("No outputs found, or daemon is not ready"); return true; } @@ -2748,8 +2747,6 @@ bool simple_wallet::sweep_all(const std::vector<std::string> &args_) { fail_msg_writer() << tr("transaction cancelled."); - // would like to return false, because no tx made, but everything else returns true - // and I don't know what returning false might adversely affect. *sigh* return true; } @@ -2865,9 +2862,29 @@ bool simple_wallet::sweep_all(const std::vector<std::string> &args_) return true; } //---------------------------------------------------------------------------------------------------- +bool simple_wallet::sweep_all(const std::vector<std::string> &args_) +{ + return sweep_main(0, args_); +} +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::sweep_below(const std::vector<std::string> &args_) +{ + uint64_t below = 0; + if (args_.size() < 1) + { + fail_msg_writer() << tr("missing amount threshold"); + return true; + } + if (!cryptonote::parse_amount(below, args_[0])) + { + fail_msg_writer() << tr("invalid amount threshold"); + return true; + } + return sweep_main(below, std::vector<std::string>(++args_.begin(), args_.end())); +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::donate(const std::vector<std::string> &args_) { - if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } std::vector<std::string> local_args = args_; if(local_args.empty() || local_args.size() > 3) { @@ -2915,6 +2932,7 @@ bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes, size_t min_mixin = ~0; std::unordered_map<std::string, uint64_t> dests; const std::string wallet_address = m_wallet->get_account().get_public_address_str(m_wallet->testnet()); + int first_known_non_zero_change_index = -1; for (size_t n = 0; n < get_num_txes(); ++n) { const tools::wallet2::tx_construction_data &cd = get_tx(n); @@ -2949,10 +2967,15 @@ bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes, fail_msg_writer() << tr("Claimed change is larger than payment to the change address"); return false; } - if (memcmp(&cd.change_dts.addr, &get_tx(0).change_dts.addr, sizeof(cd.change_dts.addr))) + if (cd.change_dts.amount > 0) { - fail_msg_writer() << tr("Change does to more than one address"); - return false; + if (first_known_non_zero_change_index == -1) + first_known_non_zero_change_index = n; + if (memcmp(&cd.change_dts.addr, &get_tx(first_known_non_zero_change_index).change_dts.addr, sizeof(cd.change_dts.addr))) + { + fail_msg_writer() << tr("Change goes to more than one address"); + return false; + } } change += cd.change_dts.amount; it->second -= cd.change_dts.amount; @@ -3160,10 +3183,10 @@ bool simple_wallet::get_tx_key(const std::vector<std::string> &args_) if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } cryptonote::blobdata txid_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(local_args.front(), txid_data)) + if(!epee::string_tools::parse_hexstr_to_binbuff(local_args.front(), txid_data) || txid_data.size() != sizeof(crypto::hash)) { fail_msg_writer() << tr("failed to parse txid"); - return false; + return true; } crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data()); @@ -3197,7 +3220,7 @@ bool simple_wallet::check_tx_key(const std::vector<std::string> &args_) assert(m_wallet); cryptonote::blobdata txid_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(local_args[0], txid_data)) + if(!epee::string_tools::parse_hexstr_to_binbuff(local_args[0], txid_data) || txid_data.size() != sizeof(crypto::hash)) { fail_msg_writer() << tr("failed to parse txid"); return true; @@ -3213,7 +3236,7 @@ bool simple_wallet::check_tx_key(const std::vector<std::string> &args_) } crypto::secret_key tx_key; cryptonote::blobdata tx_key_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(local_args[1], tx_key_data)) + if(!epee::string_tools::parse_hexstr_to_binbuff(local_args[1], tx_key_data) || tx_key_data.size() != sizeof(crypto::secret_key)) { fail_msg_writer() << tr("failed to parse tx key"); return true; @@ -3845,10 +3868,10 @@ bool simple_wallet::set_tx_note(const std::vector<std::string> &args) } cryptonote::blobdata txid_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(args.front(), txid_data)) + if(!epee::string_tools::parse_hexstr_to_binbuff(args.front(), txid_data) || txid_data.size() != sizeof(crypto::hash)) { fail_msg_writer() << tr("failed to parse txid"); - return false; + return true; } crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data()); @@ -3873,10 +3896,10 @@ bool simple_wallet::get_tx_note(const std::vector<std::string> &args) } cryptonote::blobdata txid_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(args.front(), txid_data)) + if(!epee::string_tools::parse_hexstr_to_binbuff(args.front(), txid_data) || txid_data.size() != sizeof(crypto::hash)) { fail_msg_writer() << tr("failed to parse txid"); - return false; + return true; } crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data()); @@ -4003,7 +4026,7 @@ bool simple_wallet::export_key_images(const std::vector<std::string> &args) return true; } } - catch (std::exception &e) + catch (const std::exception &e) { LOG_ERROR("Error exporting key images: " << e.what()); fail_msg_writer() << "Error exporting key images: " << e.what(); @@ -4173,10 +4196,10 @@ bool simple_wallet::show_transfer(const std::vector<std::string> &args) } cryptonote::blobdata txid_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(args.front(), txid_data)) + if(!epee::string_tools::parse_hexstr_to_binbuff(args.front(), txid_data) || txid_data.size() != sizeof(crypto::hash)) { fail_msg_writer() << tr("failed to parse txid"); - return false; + return true; } crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data()); @@ -4328,7 +4351,8 @@ int main(int argc, char* argv[]) argc, argv, "monero-wallet-cli [--wallet-file=<file>|--generate-new-wallet=<file>] [<COMMAND>]", desc_params, - positional_options + positional_options, + "monero-wallet-cli.log" ); if (!vm) diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index ce0a24be7..1b58ff32a 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -40,11 +40,11 @@ #include <boost/optional/optional.hpp> #include <boost/program_options/variables_map.hpp> -#include "cryptonote_core/account.h" -#include "cryptonote_core/cryptonote_basic_impl.h" +#include "cryptonote_basic/account.h" +#include "cryptonote_basic/cryptonote_basic_impl.h" #include "wallet/wallet2.h" #include "console_handler.h" -#include "wallet/password_container.h" +#include "common/password.h" #include "crypto/crypto.h" // for definition of crypto::secret_key #undef MONERO_DEFAULT_LOG_CATEGORY @@ -116,6 +116,10 @@ namespace cryptonote bool set_refresh_type(const std::vector<std::string> &args = std::vector<std::string>()); bool set_confirm_missing_payment_id(const std::vector<std::string> &args = std::vector<std::string>()); bool set_ask_password(const std::vector<std::string> &args = std::vector<std::string>()); + bool set_unit(const std::vector<std::string> &args = std::vector<std::string>()); + bool set_min_output_count(const std::vector<std::string> &args = std::vector<std::string>()); + bool set_min_output_value(const std::vector<std::string> &args = std::vector<std::string>()); + bool set_merge_destinations(const std::vector<std::string> &args = std::vector<std::string>()); bool help(const std::vector<std::string> &args = std::vector<std::string>()); bool start_mining(const std::vector<std::string> &args); bool stop_mining(const std::vector<std::string> &args); @@ -130,7 +134,9 @@ namespace cryptonote bool transfer(const std::vector<std::string> &args); bool transfer_new(const std::vector<std::string> &args); bool locked_transfer(const std::vector<std::string> &args); + bool sweep_main(uint64_t below, const std::vector<std::string> &args); bool sweep_all(const std::vector<std::string> &args); + bool sweep_below(const std::vector<std::string> &args); bool sweep_unmixable(const std::vector<std::string> &args); bool donate(const std::vector<std::string> &args); bool sign_transfer(const std::vector<std::string> &args); @@ -190,10 +196,10 @@ namespace cryptonote //----------------- i_wallet2_callback --------------------- virtual void on_new_block(uint64_t height, const cryptonote::block& block); - virtual void on_money_received(uint64_t height, const cryptonote::transaction& tx, uint64_t amount); - virtual void on_unconfirmed_money_received(uint64_t height, const cryptonote::transaction& tx, uint64_t amount); - virtual void on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx); - virtual void on_skip_transaction(uint64_t height, const cryptonote::transaction& tx); + virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount); + virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount); + virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx); + virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx); //---------------------------------------------------------- friend class refresh_progress_reporter_t; diff --git a/src/version.cmake b/src/version.cmake index d60673ae2..623d3cf1f 100644 --- a/src/version.cmake +++ b/src/version.cmake @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # diff --git a/src/version.h.in b/src/version.h.in index 43e046f24..852c8023b 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,4 +1,4 @@ #define MONERO_VERSION_TAG "@VERSIONTAG@" -#define MONERO_VERSION "0.10.1.0" +#define MONERO_VERSION "0.10.3.1" #define MONERO_RELEASE_NAME "Wolfram Warptangent" #define MONERO_VERSION_FULL MONERO_VERSION "-" MONERO_VERSION_TAG diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt index 922464a3c..2e7610b64 100644 --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -31,7 +31,6 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(wallet_sources - password_container.cpp wallet2.cpp wallet_args.cpp node_rpc_proxy.cpp @@ -49,7 +48,6 @@ set(wallet_api_headers set(wallet_private_headers - password_container.h wallet2.h wallet_args.h wallet_errors.h @@ -74,6 +72,7 @@ monero_add_library(wallet ${wallet_private_headers}) target_link_libraries(wallet PUBLIC + common cryptonote_core mnemonics p2p @@ -127,14 +126,19 @@ endif() # build and install libwallet_merged only if we building for GUI if (BUILD_GUI_DEPS) - set(libs_to_merge wallet cryptonote_core mnemonics common crypto ringct) + set(libs_to_merge wallet cryptonote_core cryptonote_basic mnemonics common crypto ringct) foreach(lib ${libs_to_merge}) list(APPEND objlibs $<TARGET_OBJECTS:obj_${lib}>) # matches naming convention in src/CMakeLists.txt endforeach() add_library(wallet_merged STATIC ${objlibs}) + if(IOS) + set(lib_folder lib-${ARCH}) + else() + set(lib_folder lib) + endif() install(TARGETS wallet_merged - ARCHIVE DESTINATION lib) + ARCHIVE DESTINATION ${lib_folder}) install(FILES ${wallet_api_headers} DESTINATION include/wallet) diff --git a/src/wallet/api/address_book.cpp b/src/wallet/api/address_book.cpp index e397dac04..28f835ebd 100644 --- a/src/wallet/api/address_book.cpp +++ b/src/wallet/api/address_book.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -180,4 +180,4 @@ AddressBookImpl::~AddressBookImpl() } // namespace -namespace Bitmonero = Monero;
\ No newline at end of file +namespace Bitmonero = Monero; diff --git a/src/wallet/api/address_book.h b/src/wallet/api/address_book.h index 5f72c5860..25f59128b 100644 --- a/src/wallet/api/address_book.h +++ b/src/wallet/api/address_book.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp index 760c84f4f..9798d66c6 100644 --- a/src/wallet/api/pending_transaction.cpp +++ b/src/wallet/api/pending_transaction.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -32,9 +32,8 @@ #include "wallet.h" #include "common_defines.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_core/cryptonote_basic_impl.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_basic_impl.h" #include <memory> #include <vector> @@ -125,7 +124,7 @@ bool PendingTransactionImpl::commit(const std::string &filename, bool overwrite) m_errorString = writer.str(); if (!reason.empty()) m_errorString += string(tr(". Reason: ")) + reason; - } catch (std::exception &e) { + } catch (const std::exception &e) { m_errorString = string(tr("Unknown exception: ")) + e.what(); m_status = Status_Error; } catch (...) { diff --git a/src/wallet/api/pending_transaction.h b/src/wallet/api/pending_transaction.h index d85a686fd..0c3e95a85 100644 --- a/src/wallet/api/pending_transaction.h +++ b/src/wallet/api/pending_transaction.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/transaction_history.cpp b/src/wallet/api/transaction_history.cpp index 1e50652c6..85f2b05ce 100644 --- a/src/wallet/api/transaction_history.cpp +++ b/src/wallet/api/transaction_history.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/transaction_history.h b/src/wallet/api/transaction_history.h index 37c9ea0e4..4987bdab2 100644 --- a/src/wallet/api/transaction_history.h +++ b/src/wallet/api/transaction_history.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/transaction_info.cpp b/src/wallet/api/transaction_info.cpp index 576ae8532..79a8fe9b5 100644 --- a/src/wallet/api/transaction_info.cpp +++ b/src/wallet/api/transaction_info.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/transaction_info.h b/src/wallet/api/transaction_info.h index af9696daf..16fa5da7a 100644 --- a/src/wallet/api/transaction_info.h +++ b/src/wallet/api/transaction_info.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/unsigned_transaction.cpp b/src/wallet/api/unsigned_transaction.cpp index 84ec2d9d2..1d9ef5d7c 100644 --- a/src/wallet/api/unsigned_transaction.cpp +++ b/src/wallet/api/unsigned_transaction.cpp @@ -32,9 +32,8 @@ #include "wallet.h" #include "common_defines.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_core/cryptonote_basic_impl.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_basic_impl.h" #include <memory> #include <vector> @@ -105,6 +104,7 @@ bool UnsignedTransactionImpl::checkLoadedTx(const std::function<size_t()> get_nu size_t min_mixin = ~0; std::unordered_map<std::string, uint64_t> dests; const std::string wallet_address = m_wallet.m_wallet->get_account().get_public_address_str(m_wallet.m_wallet->testnet()); + int first_known_non_zero_change_index = -1; for (size_t n = 0; n < get_num_txes(); ++n) { const tools::wallet2::tx_construction_data &cd = get_tx(n); @@ -141,11 +141,16 @@ bool UnsignedTransactionImpl::checkLoadedTx(const std::function<size_t()> get_nu m_errorString = tr("Claimed change is larger than payment to the change address"); return false; } - if (memcmp(&cd.change_dts.addr, &get_tx(0).change_dts.addr, sizeof(cd.change_dts.addr))) + if (cd.change_dts.amount > 0) { - m_status = Status_Error; - m_errorString = tr("Change does to more than one address"); - return false; + if (first_known_non_zero_change_index == -1) + first_known_non_zero_change_index = n; + if (memcmp(&cd.change_dts.addr, &get_tx(first_known_non_zero_change_index).change_dts.addr, sizeof(cd.change_dts.addr))) + { + m_status = Status_Error; + m_errorString = tr("Change goes to more than one address"); + return false; + } } change += cd.change_dts.amount; it->second -= cd.change_dts.amount; diff --git a/src/wallet/api/unsigned_transaction.h b/src/wallet/api/unsigned_transaction.h index 8038334e4..9c442f503 100644 --- a/src/wallet/api/unsigned_transaction.h +++ b/src/wallet/api/unsigned_transaction.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/utils.cpp b/src/wallet/api/utils.cpp index c369427b4..a9646e038 100644 --- a/src/wallet/api/utils.cpp +++ b/src/wallet/api/utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 830f98909..21760ac49 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -44,6 +44,9 @@ using namespace std; using namespace cryptonote; +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "WalletAPI" + namespace Monero { namespace { @@ -96,10 +99,10 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback } } - virtual void on_money_received(uint64_t height, const cryptonote::transaction& tx, uint64_t amount) + virtual void on_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount) { - std::string tx_hash = epee::string_tools::pod_to_hex(get_transaction_hash(tx)); + std::string tx_hash = epee::string_tools::pod_to_hex(txid); LOG_PRINT_L3(__FUNCTION__ << ": money received. height: " << height << ", tx: " << tx_hash @@ -111,10 +114,10 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback } } - virtual void on_unconfirmed_money_received(uint64_t height, const cryptonote::transaction& tx, uint64_t amount) + virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount) { - std::string tx_hash = epee::string_tools::pod_to_hex(get_transaction_hash(tx)); + std::string tx_hash = epee::string_tools::pod_to_hex(txid); LOG_PRINT_L3(__FUNCTION__ << ": unconfirmed money received. height: " << height << ", tx: " << tx_hash @@ -126,11 +129,11 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback } } - virtual void on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, uint64_t amount, + virtual void on_money_spent(uint64_t height, const crypto::hash &txid, uint64_t amount, const cryptonote::transaction& spend_tx) { // TODO; - std::string tx_hash = epee::string_tools::pod_to_hex(get_transaction_hash(spend_tx)); + std::string tx_hash = epee::string_tools::pod_to_hex(txid); LOG_PRINT_L3(__FUNCTION__ << ": money spent. height: " << height << ", tx: " << tx_hash << ", amount: " << print_money(amount)); @@ -141,7 +144,7 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback } } - virtual void on_skip_transaction(uint64_t height, const cryptonote::transaction& tx) + virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid) { // TODO; } @@ -255,6 +258,14 @@ uint64_t Wallet::maximumAllowedAmount() return std::numeric_limits<uint64_t>::max(); } +void Wallet::init(const char *argv0, const char *default_log_base_name) { + epee::string_tools::set_module_name_and_folder(argv0); + mlog_configure(mlog_get_default_log_path(default_log_base_name), true); +} + +void Wallet::debug(const std::string &str) { + MDEBUG(str); +} ///////////////////////// WalletImpl implementation //////////////////////// WalletImpl::WalletImpl(bool testnet) @@ -645,9 +656,11 @@ string WalletImpl::keysFilename() const return m_wallet->get_keys_file(); } -bool WalletImpl::init(const std::string &daemon_address, uint64_t upper_transaction_size_limit) +bool WalletImpl::init(const std::string &daemon_address, uint64_t upper_transaction_size_limit, const std::string &daemon_username, const std::string &daemon_password) { clearStatus(); + if(daemon_username != "") + m_daemon_login.emplace(daemon_username, daemon_password); return doInit(daemon_address, upper_transaction_size_limit); } @@ -820,7 +833,7 @@ bool WalletImpl::exportKeyImages(const string &filename) return false; } } - catch (std::exception &e) + catch (const std::exception &e) { LOG_ERROR("Error exporting key images: " << e.what()); m_errorString = e.what(); @@ -939,7 +952,7 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const static_cast<uint32_t>(priority), extra, m_trustedDaemon); } else { - transaction->m_pending_tx = m_wallet->create_transactions_all(addr, fake_outs_count, 0 /* unlock_time */, + transaction->m_pending_tx = m_wallet->create_transactions_all(0, addr, fake_outs_count, 0 /* unlock_time */, static_cast<uint32_t>(priority), extra, m_trustedDaemon); } @@ -1151,7 +1164,7 @@ void WalletImpl::setDefaultMixin(uint32_t arg) bool WalletImpl::setUserNote(const std::string &txid, const std::string ¬e) { cryptonote::blobdata txid_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data)) + if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data) || txid_data.size() != sizeof(crypto::hash)) return false; const crypto::hash htxid = *reinterpret_cast<const crypto::hash*>(txid_data.data()); @@ -1162,7 +1175,7 @@ bool WalletImpl::setUserNote(const std::string &txid, const std::string ¬e) std::string WalletImpl::getUserNote(const std::string &txid) const { cryptonote::blobdata txid_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data)) + if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data) || txid_data.size() != sizeof(crypto::hash)) return ""; const crypto::hash htxid = *reinterpret_cast<const crypto::hash*>(txid_data.data()); @@ -1172,7 +1185,7 @@ std::string WalletImpl::getUserNote(const std::string &txid) const std::string WalletImpl::getTxKey(const std::string &txid) const { cryptonote::blobdata txid_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data)) + if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data) || txid_data.size() != sizeof(crypto::hash)) { return ""; } @@ -1354,7 +1367,7 @@ bool WalletImpl::isNewWallet() const bool WalletImpl::doInit(const string &daemon_address, uint64_t upper_transaction_size_limit) { - if (!m_wallet->init(daemon_address, upper_transaction_size_limit)) + if (!m_wallet->init(daemon_address, m_daemon_login, upper_transaction_size_limit)) return false; // in case new wallet, this will force fast-refresh (pulling hashes instead of blocks) @@ -1400,6 +1413,18 @@ bool WalletImpl::rescanSpent() } return true; } + + +void WalletImpl::hardForkInfo(uint8_t &version, uint64_t &earliest_height) const +{ + m_wallet->get_hard_fork_info(version, earliest_height); +} + +bool WalletImpl::useForkRules(uint8_t version, int64_t early_blocks) const +{ + return m_wallet->use_fork_rules(version,early_blocks); +} + } // namespace namespace Bitmonero = Monero; diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 3994afaa3..c376dd6c1 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -78,7 +78,7 @@ public: bool store(const std::string &path); std::string filename() const; std::string keysFilename() const; - bool init(const std::string &daemon_address, uint64_t upper_transaction_size_limit = 0); + bool init(const std::string &daemon_address, uint64_t upper_transaction_size_limit = 0, const std::string &daemon_username = "", const std::string &daemon_password = ""); bool connectToDaemon(); ConnectionStatus connected() const; void setTrustedDaemon(bool arg); @@ -99,7 +99,8 @@ public: bool watchOnly() const; bool rescanSpent(); bool testnet() const {return m_wallet->testnet();} - + void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const; + bool useForkRules(uint8_t version, int64_t early_blocks) const; PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id, optional<uint64_t> amount, uint32_t mixin_count, @@ -170,6 +171,7 @@ private: std::atomic<bool> m_rebuildWalletCache; // cache connection status to avoid unnecessary RPC calls mutable std::atomic<bool> m_is_connected; + boost::optional<epee::net_utils::http::login> m_daemon_login{}; }; diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp index c761cc6d2..b2f947972 100644 --- a/src/wallet/api/wallet_manager.cpp +++ b/src/wallet/api/wallet_manager.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -33,11 +33,16 @@ #include "wallet.h" #include "common_defines.h" #include "common/dns_utils.h" +#include "common/util.h" +#include "common/updates.h" +#include "version.h" #include "net/http_client.h" #include <boost/filesystem.hpp> #include <boost/regex.hpp> +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "WalletAPI" namespace epee { unsigned int g_test_dbg_lock_sleep = 0; @@ -48,7 +53,7 @@ namespace { bool connect_and_invoke(const std::string& address, const std::string& path, const Request& request, Response& response) { epee::net_utils::http::http_simple_client client{}; - return client.set_server(address) && epee::net_utils::invoke_http_json(path, request, response, client); + return client.set_server(address, boost::none) && epee::net_utils::invoke_http_json(path, request, response, client); } } @@ -182,7 +187,7 @@ bool WalletManagerImpl::checkPayment(const std::string &address_text, const std: { error = ""; cryptonote::blobdata txid_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(txid_text, txid_data)) + if(!epee::string_tools::parse_hexstr_to_binbuff(txid_text, txid_data) || txid_data.size() != sizeof(crypto::hash)) { error = tr("failed to parse txid"); return false; @@ -196,7 +201,7 @@ bool WalletManagerImpl::checkPayment(const std::string &address_text, const std: } crypto::secret_key tx_key; cryptonote::blobdata tx_key_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(txkey_text, tx_key_data)) + if(!epee::string_tools::parse_hexstr_to_binbuff(txkey_text, tx_key_data) || tx_key_data.size() != sizeof(crypto::hash)) { error = tr("failed to parse tx key"); return false; @@ -372,26 +377,6 @@ double WalletManagerImpl::miningHashRate() const return mres.speed; } -void WalletManagerImpl::hardForkInfo(uint8_t &version, uint64_t &earliest_height) const -{ - epee::json_rpc::request<cryptonote::COMMAND_RPC_HARD_FORK_INFO::request> req_t = AUTO_VAL_INIT(req_t); - epee::json_rpc::response<cryptonote::COMMAND_RPC_HARD_FORK_INFO::response, std::string> resp_t = AUTO_VAL_INIT(resp_t); - - version = 0; - earliest_height = 0; - - epee::net_utils::http::http_simple_client http_client; - req_t.jsonrpc = "2.0"; - req_t.id = epee::serialization::storage_entry(0); - req_t.method = "hard_fork_info"; - req_t.params.version = 0; - bool r = connect_and_invoke(m_daemonAddress, "/json_rpc", req_t, resp_t); - if (!r || resp_t.result.status != CORE_RPC_STATUS_OK) - return; - version = resp_t.result.version; - earliest_height = resp_t.result.earliest_height; -} - uint64_t WalletManagerImpl::blockTarget() const { cryptonote::COMMAND_RPC_GET_INFO::request ireq; @@ -412,13 +397,15 @@ bool WalletManagerImpl::isMining() const return mres.active; } -bool WalletManagerImpl::startMining(const std::string &address, uint32_t threads) +bool WalletManagerImpl::startMining(const std::string &address, uint32_t threads, bool background_mining, bool ignore_battery) { cryptonote::COMMAND_RPC_START_MINING::request mreq; cryptonote::COMMAND_RPC_START_MINING::response mres; mreq.miner_address = address; mreq.threads_count = threads; + mreq.ignore_battery = ignore_battery; + mreq.do_background_mining = background_mining; if (!connect_and_invoke(m_daemonAddress, "/start_mining", mreq, mres)) return false; @@ -443,6 +430,29 @@ std::string WalletManagerImpl::resolveOpenAlias(const std::string &address, bool return addresses.front(); } +std::tuple<bool, std::string, std::string, std::string, std::string> WalletManager::checkUpdates(const std::string &software, const std::string &subdir) +{ +#ifdef BUILD_TAG + static const char buildtag[] = BOOST_PP_STRINGIZE(BUILD_TAG); +#else + static const char buildtag[] = "source"; +#endif + + std::string version, hash; + MDEBUG("Checking for a new " << software << " version for " << buildtag); + if (!tools::check_updates(software, buildtag, version, hash)) + return std::make_tuple(false, "", "", "", ""); + + if (tools::vercmp(version.c_str(), MONERO_VERSION) > 0) + { + std::string user_url = tools::get_update_url(software, subdir, buildtag, version, true); + std::string auto_url = tools::get_update_url(software, subdir, buildtag, version, false); + MGINFO("Version " << version << " of " << software << " for " << buildtag << " is available: " << user_url << ", SHA256 hash " << hash); + return std::make_tuple(true, version, hash, user_url, auto_url); + } + return std::make_tuple(false, "", "", "", ""); +} + ///////////////////// WalletManagerFactory implementation ////////////////////// WalletManager *WalletManagerFactory::getWalletManager() @@ -451,7 +461,6 @@ WalletManager *WalletManagerFactory::getWalletManager() static WalletManagerImpl * g_walletManager = nullptr; if (!g_walletManager) { - mlog_configure("monero-wallet-gui.log", false); g_walletManager = new WalletManagerImpl(); } @@ -465,7 +474,7 @@ void WalletManagerFactory::setLogLevel(int level) void WalletManagerFactory::setLogCategories(const std::string &categories) { - mlog_set_categories(categories.c_str()); + mlog_set_log(categories.c_str()); } diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h index ce9b70e96..033e8108f 100644 --- a/src/wallet/api/wallet_manager.h +++ b/src/wallet/api/wallet_manager.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -59,10 +59,9 @@ public: uint64_t blockchainTargetHeight() const; uint64_t networkDifficulty() const; double miningHashRate() const; - void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const; uint64_t blockTarget() const; bool isMining() const; - bool startMining(const std::string &address, uint32_t threads = 1); + bool startMining(const std::string &address, uint32_t threads = 1, bool background_mining = false, bool ignore_battery = true); bool stopMining(); std::string resolveOpenAlias(const std::string &address, bool &dnssec_valid) const; diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp index cc249b5cc..03e1bbd98 100644 --- a/src/wallet/node_rpc_proxy.cpp +++ b/src/wallet/node_rpc_proxy.cpp @@ -36,6 +36,8 @@ using namespace epee; namespace tools { +static const std::chrono::seconds rpc_timeout = std::chrono::minutes(3) + std::chrono::seconds(30); + NodeRPCProxy::NodeRPCProxy(epee::net_utils::http::http_simple_client &http_client, boost::mutex &mutex) : m_http_client(http_client) , m_daemon_rpc_mutex(mutex) @@ -45,8 +47,43 @@ NodeRPCProxy::NodeRPCProxy(epee::net_utils::http::http_simple_client &http_clien , m_dynamic_per_kb_fee_estimate(0) , m_dynamic_per_kb_fee_estimate_cached_height(0) , m_dynamic_per_kb_fee_estimate_grace_blocks(0) + , m_rpc_version(0) {} +void NodeRPCProxy::invalidate() +{ + m_height = 0; + m_height_time = 0; + for (size_t n = 0; n < 256; ++n) + m_earliest_height[n] = 0; + m_dynamic_per_kb_fee_estimate = 0; + m_dynamic_per_kb_fee_estimate_cached_height = 0; + m_dynamic_per_kb_fee_estimate_grace_blocks = 0; + m_rpc_version = 0; +} + +boost::optional<std::string> NodeRPCProxy::get_rpc_version(uint32_t &rpc_version) +{ + const time_t now = time(NULL); + if (m_rpc_version == 0) + { + epee::json_rpc::request<cryptonote::COMMAND_RPC_GET_VERSION::request> req_t = AUTO_VAL_INIT(req_t); + epee::json_rpc::response<cryptonote::COMMAND_RPC_GET_VERSION::response, std::string> resp_t = AUTO_VAL_INIT(resp_t); + req_t.jsonrpc = "2.0"; + req_t.id = epee::serialization::storage_entry(0); + req_t.method = "get_version"; + m_daemon_rpc_mutex.lock(); + bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client, rpc_timeout); + m_daemon_rpc_mutex.unlock(); + CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon"); + CHECK_AND_ASSERT_MES(resp_t.result.status != CORE_RPC_STATUS_BUSY, resp_t.result.status, "Failed to connect to daemon"); + CHECK_AND_ASSERT_MES(resp_t.result.status == CORE_RPC_STATUS_OK, resp_t.result.status, "Failed to get daemon RPC version"); + m_rpc_version = resp_t.result.version; + } + rpc_version = m_rpc_version; + return boost::optional<std::string>(); +} + boost::optional<std::string> NodeRPCProxy::get_height(uint64_t &height) { const time_t now = time(NULL); @@ -56,7 +93,7 @@ boost::optional<std::string> NodeRPCProxy::get_height(uint64_t &height) cryptonote::COMMAND_RPC_GET_HEIGHT::response res = AUTO_VAL_INIT(res); m_daemon_rpc_mutex.lock(); - bool r = net_utils::invoke_http_json("/getheight", req, res, m_http_client); + bool r = net_utils::invoke_http_json("/getheight", req, res, m_http_client, rpc_timeout); m_daemon_rpc_mutex.unlock(); CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon"); CHECK_AND_ASSERT_MES(res.status != CORE_RPC_STATUS_BUSY, res.status, "Failed to connect to daemon"); @@ -85,7 +122,7 @@ boost::optional<std::string> NodeRPCProxy::get_earliest_height(uint8_t version, req_t.id = epee::serialization::storage_entry(0); req_t.method = "hard_fork_info"; req_t.params.version = version; - bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client); + bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client, rpc_timeout); m_daemon_rpc_mutex.unlock(); CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon"); CHECK_AND_ASSERT_MES(resp_t.result.status != CORE_RPC_STATUS_BUSY, resp_t.result.status, "Failed to connect to daemon"); @@ -115,7 +152,7 @@ boost::optional<std::string> NodeRPCProxy::get_dynamic_per_kb_fee_estimate(uint6 req_t.id = epee::serialization::storage_entry(0); req_t.method = "get_fee_estimate"; req_t.params.grace_blocks = grace_blocks; - bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client); + bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client, rpc_timeout); m_daemon_rpc_mutex.unlock(); CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon"); CHECK_AND_ASSERT_MES(resp_t.result.status != CORE_RPC_STATUS_BUSY, resp_t.result.status, "Failed to connect to daemon"); diff --git a/src/wallet/node_rpc_proxy.h b/src/wallet/node_rpc_proxy.h index e2f42d541..02d1d8d93 100644 --- a/src/wallet/node_rpc_proxy.h +++ b/src/wallet/node_rpc_proxy.h @@ -41,6 +41,9 @@ class NodeRPCProxy public: NodeRPCProxy(epee::net_utils::http::http_simple_client &http_client, boost::mutex &mutex); + void invalidate(); + + boost::optional<std::string> get_rpc_version(uint32_t &version); boost::optional<std::string> get_height(uint64_t &height); void set_height(uint64_t h); boost::optional<std::string> get_earliest_height(uint8_t version, uint64_t &earliest_height); @@ -56,6 +59,7 @@ private: uint64_t m_dynamic_per_kb_fee_estimate; uint64_t m_dynamic_per_kb_fee_estimate_cached_height; uint64_t m_dynamic_per_kb_fee_estimate_grace_blocks; + uint32_t m_rpc_version; }; } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index c52281a2b..9069789ca 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -39,10 +39,10 @@ using namespace epee; #include "cryptonote_config.h" #include "wallet2.h" #include "wallet2_api.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_format_utils.h" #include "rpc/core_rpc_server_commands_defs.h" #include "misc_language.h" -#include "cryptonote_core/cryptonote_basic_impl.h" +#include "cryptonote_basic/cryptonote_basic_impl.h" #include "common/boost_serialization_helper.h" #include "common/command_line.h" #include "profile_tools.h" @@ -82,8 +82,8 @@ using namespace cryptonote; #define UNSIGNED_TX_PREFIX "Monero unsigned tx set\003" #define SIGNED_TX_PREFIX "Monero signed tx set\003" -#define RECENT_OUTPUT_RATIO (0.25) // 25% of outputs are from the recent zone -#define RECENT_OUTPUT_ZONE (5 * 86400) // last 5 days are the recent zone +#define RECENT_OUTPUT_RATIO (0.5) // 50% of outputs are from the recent zone +#define RECENT_OUTPUT_ZONE ((time_t)(1.8 * 86400)) // last 1.8 day makes up the recent zone (taken from monerolink.pdf, Miller et al) #define FEE_ESTIMATE_GRACE_BLOCKS 10 // estimate fee valid for that many blocks @@ -105,9 +105,10 @@ namespace struct options { const command_line::arg_descriptor<std::string> daemon_address = {"daemon-address", tools::wallet2::tr("Use daemon instance at <host>:<port>"), ""}; const command_line::arg_descriptor<std::string> daemon_host = {"daemon-host", tools::wallet2::tr("Use daemon instance at host <arg> instead of localhost"), ""}; - const command_line::arg_descriptor<std::string> password = {"password", tools::wallet2::tr("Wallet password"), "", true}; + const command_line::arg_descriptor<std::string> password = {"password", tools::wallet2::tr("Wallet password (escape/quote as needed)"), "", true}; const command_line::arg_descriptor<std::string> password_file = {"password-file", tools::wallet2::tr("Wallet password file"), "", true}; const command_line::arg_descriptor<int> daemon_port = {"daemon-port", tools::wallet2::tr("Use daemon instance at port <arg> instead of 18081"), 0}; + const command_line::arg_descriptor<std::string> daemon_login = {"daemon-login", tools::wallet2::tr("Specify username[:password] for daemon RPC client"), "", true}; const command_line::arg_descriptor<bool> testnet = {"testnet", tools::wallet2::tr("For testnet. Daemon must also be launched with --testnet flag"), false}; const command_line::arg_descriptor<bool> restricted = {"restricted-rpc", tools::wallet2::tr("Restricts to view-only commands"), false}; }; @@ -152,6 +153,18 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl return nullptr; } + boost::optional<epee::net_utils::http::login> login{}; + if (command_line::has_arg(vm, opts.daemon_login)) + { + auto parsed = tools::login::parse( + command_line::get_arg(vm, opts.daemon_login), false, "Daemon client password" + ); + if (!parsed) + return nullptr; + + login.emplace(std::move(parsed->username), std::move(parsed->password).password()); + } + if (daemon_host.empty()) daemon_host = "localhost"; @@ -164,7 +177,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port); std::unique_ptr<tools::wallet2> wallet(new tools::wallet2(testnet, restricted)); - wallet->init(std::move(daemon_address)); + wallet->init(std::move(daemon_address), std::move(login)); return wallet; } @@ -200,8 +213,10 @@ boost::optional<tools::password_container> get_password(const boost::program_opt return tools::wallet2::password_prompt(verify); } -std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, bool testnet, bool restricted) +std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, const boost::program_options::variables_map& vm, const options& opts) { + const bool testnet = command_line::get_arg(vm, opts.testnet); + /* GET_FIELD_FROM_JSON_RETURN_ON_ERROR Is a generic macro that can return false. Gcc will coerce this into unique_ptr(nullptr), but clang correctly fails. This large wrapper is for the use of that macro */ @@ -238,7 +253,7 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, if (field_viewkey_found) { cryptonote::blobdata viewkey_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(field_viewkey, viewkey_data)) + if(!epee::string_tools::parse_hexstr_to_binbuff(field_viewkey, viewkey_data) || viewkey_data.size() != sizeof(crypto::secret_key)) { tools::fail_msg_writer() << tools::wallet2::tr("failed to parse view key secret key"); return false; @@ -256,7 +271,7 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, if (field_spendkey_found) { cryptonote::blobdata spendkey_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(field_spendkey, spendkey_data)) + if(!epee::string_tools::parse_hexstr_to_binbuff(field_spendkey, spendkey_data) || spendkey_data.size() != sizeof(crypto::secret_key)) { tools::fail_msg_writer() << tools::wallet2::tr("failed to parse spend key secret key"); return false; @@ -342,7 +357,7 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, return false; } - wallet.reset(new tools::wallet2(testnet, restricted)); + wallet.reset(make_basic(vm, opts).release()); wallet->set_refresh_from_block_height(field_scan_from_height); try @@ -434,14 +449,15 @@ void wallet2::init_options(boost::program_options::options_description& desc_par command_line::add_arg(desc_params, opts.password); command_line::add_arg(desc_params, opts.password_file); command_line::add_arg(desc_params, opts.daemon_port); + command_line::add_arg(desc_params, opts.daemon_login); command_line::add_arg(desc_params, opts.testnet); command_line::add_arg(desc_params, opts.restricted); } -boost::optional<password_container> wallet2::password_prompt(const bool is_new_wallet) +boost::optional<password_container> wallet2::password_prompt(const bool new_password) { auto pwd_container = tools::password_container::prompt( - is_new_wallet, (is_new_wallet ? tr("Enter a password for your new wallet") : tr("Wallet password")) + new_password, (new_password ? tr("Enter new wallet password") : tr("Wallet password")) ); if (!pwd_container) { @@ -453,7 +469,7 @@ boost::optional<password_container> wallet2::password_prompt(const bool is_new_w std::unique_ptr<wallet2> wallet2::make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file) { const options opts{}; - return generate_from_json(json_file, command_line::get_arg(vm, opts.testnet), command_line::get_arg(vm, opts.restricted)); + return generate_from_json(json_file, vm, opts); } std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_from_file( @@ -484,14 +500,21 @@ std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_new(const return {make_basic(vm, opts), std::move(*pwd)}; } +std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::variables_map& vm) +{ + const options opts{}; + return make_basic(vm, opts); +} + //---------------------------------------------------------------------------------------------------- -bool wallet2::init(std::string daemon_address, uint64_t upper_transaction_size_limit) +bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_size_limit) { if(m_http_client.is_connected()) m_http_client.disconnect(); m_upper_transaction_size_limit = upper_transaction_size_limit; m_daemon_address = std::move(daemon_address); - return m_http_client.set_server(get_daemon_address()); + m_daemon_login = std::move(daemon_login); + return m_http_client.set_server(get_daemon_address(), get_daemon_login()); } //---------------------------------------------------------------------------------------------------- bool wallet2::is_deterministic() const @@ -618,28 +641,13 @@ bool wallet2::wallet_generate_key_image_helper(const cryptonote::account_keys& a return true; } //---------------------------------------------------------------------------------------------------- -void wallet2::process_new_transaction(const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool) +void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool) { - class lazy_txid_getter - { - const cryptonote::transaction &tx; - crypto::hash lazy_txid; - bool computed; - public: - lazy_txid_getter(const transaction &tx): tx(tx), computed(false) {} - const crypto::hash &operator()() - { - if (!computed) - { - lazy_txid = cryptonote::get_transaction_hash(tx); - computed = true; - } - return lazy_txid; - } - } txid(tx); + // In this function, tx (probably) only contains the base information + // (that is, the prunable stuff may or may not be included) if (!miner_tx) - process_unconfirmed(tx, height); + process_unconfirmed(txid, tx, height); std::vector<size_t> outs; uint64_t tx_money_got_in_outs = 0; crypto::public_key tx_pub_key = null_pkey; @@ -648,7 +656,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s if(!parse_tx_extra(tx.extra, tx_extra_fields)) { // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key - LOG_PRINT_L0("Transaction extra has unsupported format: " << txid()); + LOG_PRINT_L0("Transaction extra has unsupported format: " << txid); } // Don't try to extract tx public key if tx has no ouputs @@ -662,9 +670,9 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s { if (pk_index > 1) break; - LOG_PRINT_L0("Public key wasn't found in the transaction extra. Skipping transaction " << txid()); + LOG_PRINT_L0("Public key wasn't found in the transaction extra. Skipping transaction " << txid); if(0 != m_callback) - m_callback->on_skip_transaction(height, tx); + m_callback->on_skip_transaction(height, txid, tx); return; } @@ -864,7 +872,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s td.m_internal_output_index = o; td.m_global_output_index = o_indices[o]; td.m_tx = (const cryptonote::transaction_prefix&)tx; - td.m_txid = txid(); + td.m_txid = txid; td.m_key_image = ki[o]; td.m_key_image_known = !m_watch_only; td.m_amount = tx.vout[o].amount; @@ -888,9 +896,9 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s set_unspent(m_transfers.size()-1); m_key_images[td.m_key_image] = m_transfers.size()-1; m_pub_keys[in_ephemeral[o].pub] = m_transfers.size()-1; - LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid()); + LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid); if (0 != m_callback) - m_callback->on_money_received(height, tx, td.m_amount); + m_callback->on_money_received(height, txid, tx, td.m_amount); } } else if (m_transfers[kit->second].m_spent || m_transfers[kit->second].amount() >= tx.vout[o].amount) @@ -915,7 +923,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s td.m_internal_output_index = o; td.m_global_output_index = o_indices[o]; td.m_tx = (const cryptonote::transaction_prefix&)tx; - td.m_txid = txid(); + td.m_txid = txid; td.m_amount = tx.vout[o].amount; td.m_pk_index = pk_index - 1; if (td.m_amount == 0) @@ -937,9 +945,9 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s THROW_WALLET_EXCEPTION_IF(td.get_public_key() != in_ephemeral[o].pub, error::wallet_internal_error, "Inconsistent public keys"); THROW_WALLET_EXCEPTION_IF(td.m_spent, error::wallet_internal_error, "Inconsistent spent status"); - LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid()); + LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid); if (0 != m_callback) - m_callback->on_money_received(height, tx, td.m_amount); + m_callback->on_money_received(height, txid, tx, td.m_amount); } } } @@ -967,17 +975,17 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s tx_money_spent_in_ins += amount; if (!pool) { - LOG_PRINT_L0("Spent money: " << print_money(amount) << ", with tx: " << txid()); + LOG_PRINT_L0("Spent money: " << print_money(amount) << ", with tx: " << txid); set_spent(it->second, height); if (0 != m_callback) - m_callback->on_money_spent(height, tx, amount, tx); + m_callback->on_money_spent(height, txid, tx, amount, tx); } } } if (tx_money_spent_in_ins > 0) { - process_outgoing(tx, height, ts, tx_money_spent_in_ins, tx_money_got_in_outs); + process_outgoing(txid, tx, height, ts, tx_money_spent_in_ins, tx_money_got_in_outs); } uint64_t received = (tx_money_spent_in_ins < tx_money_got_in_outs) ? tx_money_got_in_outs - tx_money_spent_in_ins : 0; @@ -1023,7 +1031,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s } payment_details payment; - payment.m_tx_hash = txid(); + payment.m_tx_hash = txid; payment.m_amount = received; payment.m_block_height = height; payment.m_unlock_time = tx.unlock_time; @@ -1031,7 +1039,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s if (pool) { m_unconfirmed_payments.emplace(payment_id, payment); if (0 != m_callback) - m_callback->on_unconfirmed_money_received(height, tx, payment.m_amount); + m_callback->on_unconfirmed_money_received(height, txid, tx, payment.m_amount); } else m_payments.emplace(payment_id, payment); @@ -1039,12 +1047,11 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s } } //---------------------------------------------------------------------------------------------------- -void wallet2::process_unconfirmed(const cryptonote::transaction& tx, uint64_t height) +void wallet2::process_unconfirmed(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height) { if (m_unconfirmed_txs.empty()) return; - crypto::hash txid = get_transaction_hash(tx); auto unconf_it = m_unconfirmed_txs.find(txid); if(unconf_it != m_unconfirmed_txs.end()) { if (store_tx_info()) { @@ -1060,9 +1067,8 @@ void wallet2::process_unconfirmed(const cryptonote::transaction& tx, uint64_t he } } //---------------------------------------------------------------------------------------------------- -void wallet2::process_outgoing(const cryptonote::transaction &tx, uint64_t height, uint64_t ts, uint64_t spent, uint64_t received) +void wallet2::process_outgoing(const crypto::hash &txid, const cryptonote::transaction &tx, uint64_t height, uint64_t ts, uint64_t spent, uint64_t received) { - crypto::hash txid = get_transaction_hash(tx); std::pair<std::unordered_map<crypto::hash, confirmed_transfer_details>::iterator, bool> entry = m_confirmed_txs.insert(std::make_pair(txid, confirmed_transfer_details())); // fill with the info we know, some info might already be there if (entry.second) @@ -1105,16 +1111,19 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry if(b.timestamp + 60*60*24 > m_account.get_createtime() && height >= m_refresh_from_block_height) { TIME_MEASURE_START(miner_tx_handle_time); - process_new_transaction(b.miner_tx, o_indices.indices[txidx++].indices, height, b.timestamp, true, false); + process_new_transaction(get_transaction_hash(b.miner_tx), b.miner_tx, o_indices.indices[txidx++].indices, height, b.timestamp, true, false); TIME_MEASURE_FINISH(miner_tx_handle_time); TIME_MEASURE_START(txs_handle_time); - for(auto& txblob: bche.txs) + THROW_WALLET_EXCEPTION_IF(bche.txs.size() != b.tx_hashes.size(), error::wallet_internal_error, "Wrong amount of transactions for block"); + size_t idx = 0; + for (const auto& txblob: bche.txs) { cryptonote::transaction tx; - bool r = parse_and_validate_tx_from_blob(txblob, tx); + bool r = parse_and_validate_tx_base_from_blob(txblob, tx); THROW_WALLET_EXCEPTION_IF(!r, error::tx_parse_error, txblob); - process_new_transaction(tx, o_indices.indices[txidx++].indices, height, b.timestamp, false, false); + process_new_transaction(b.tx_hashes[idx], tx, o_indices.indices[txidx++].indices, height, b.timestamp, false, false); + ++idx; } TIME_MEASURE_FINISH(txs_handle_time); LOG_PRINT_L2("Processed block: " << bl_id << ", height " << height << ", " << miner_tx_handle_time + txs_handle_time << "(" << miner_tx_handle_time << "/" << txs_handle_time <<")ms"); @@ -1170,6 +1179,34 @@ void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response res = AUTO_VAL_INIT(res); req.block_ids = short_chain_history; + uint32_t rpc_version; + boost::optional<std::string> result = m_node_rpc_proxy.get_rpc_version(rpc_version); + // no error + if (!!result) + { + // empty string -> not connection + THROW_WALLET_EXCEPTION_IF(result->empty(), tools::error::no_connection_to_daemon, "getversion"); + THROW_WALLET_EXCEPTION_IF(*result == CORE_RPC_STATUS_BUSY, tools::error::daemon_busy, "getversion"); + if (*result != CORE_RPC_STATUS_OK) + { + MDEBUG("Cannot determined daemon RPC version, not asking for pruned blocks"); + req.prune = false; // old daemon + } + } + else + { + if (rpc_version >= MAKE_CORE_RPC_VERSION(1, 7)) + { + MDEBUG("Daemon is recent enough, asking for pruned blocks"); + req.prune = true; + } + else + { + MDEBUG("Daemon is too old, not asking for pruned blocks"); + req.prune = false; + } + } + req.start_height = start_height; m_daemon_rpc_mutex.lock(); bool r = net_utils::invoke_http_bin("/getblocks.bin", req, res, m_http_client, rpc_timeout); @@ -1352,25 +1389,28 @@ void wallet2::pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_hei //---------------------------------------------------------------------------------------------------- void wallet2::update_pool_state() { + MDEBUG("update_pool_state start"); + // get the pool state - cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL::request req; - cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL::response res; + cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::request req; + cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::response res; m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_json("/get_transaction_pool", req, res, m_http_client, rpc_timeout); + bool r = epee::net_utils::invoke_http_json("/get_transaction_pool_hashes.bin", req, res, m_http_client, rpc_timeout); m_daemon_rpc_mutex.unlock(); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_transaction_pool"); - THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_transaction_pool"); + THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_transaction_pool_hashes.bin"); + THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_transaction_pool_hashes.bin"); THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_tx_pool_error); + MDEBUG("update_pool_state got pool"); // remove any pending tx that's not in the pool std::unordered_map<crypto::hash, wallet2::unconfirmed_transfer_details>::iterator it = m_unconfirmed_txs.begin(); while (it != m_unconfirmed_txs.end()) { - const std::string txid = epee::string_tools::pod_to_hex(it->first); + const crypto::hash &txid = it->first; bool found = false; - for (auto it2: res.transactions) + for (const auto &it2: res.tx_hashes) { - if (it2.id_hash == txid) + if (it2 == txid) { found = true; break; @@ -1415,16 +1455,17 @@ void wallet2::update_pool_state() } } } + MDEBUG("update_pool_state done first loop"); // remove pool txes to us that aren't in the pool anymore std::unordered_map<crypto::hash, wallet2::payment_details>::iterator uit = m_unconfirmed_payments.begin(); while (uit != m_unconfirmed_payments.end()) { - const std::string txid = string_tools::pod_to_hex(uit->first); + const crypto::hash &txid = uit->second.m_tx_hash; bool found = false; - for (auto it2: res.transactions) + for (const auto &it2: res.tx_hashes) { - if (it2.id_hash == txid) + if (it2 == txid) { found = true; break; @@ -1433,102 +1474,122 @@ void wallet2::update_pool_state() auto pit = uit++; if (!found) { + MDEBUG("Removing " << txid << " from unconfirmed payments, not found in pool"); m_unconfirmed_payments.erase(pit); } } + MDEBUG("update_pool_state done second loop"); - // add new pool txes to us - for (auto it: res.transactions) + // gather txids of new pool txes to us + std::vector<crypto::hash> txids; + for (const auto &txid: res.tx_hashes) { - cryptonote::blobdata txid_data; - if(epee::string_tools::parse_hexstr_to_binbuff(it.id_hash, txid_data)) + if (m_scanned_pool_txs[0].find(txid) != m_scanned_pool_txs[0].end() || m_scanned_pool_txs[1].find(txid) != m_scanned_pool_txs[1].end()) + { + LOG_PRINT_L2("Already seen " << txid << ", skipped"); + continue; + } + if (m_unconfirmed_payments.find(txid) == m_unconfirmed_payments.end()) { - const crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data()); - if (m_unconfirmed_payments.find(txid) == m_unconfirmed_payments.end()) + LOG_PRINT_L1("Found new pool tx: " << txid); + bool found = false; + for (const auto &i: m_unconfirmed_txs) { - LOG_PRINT_L1("Found new pool tx: " << txid); - bool found = false; - for (const auto &i: m_unconfirmed_txs) + if (i.first == txid) { - if (i.first == txid) - { - found = true; - break; - } + found = true; + break; } - if (!found) + } + if (!found) + { + // not one of those we sent ourselves + txids.push_back(txid); + } + else + { + LOG_PRINT_L1("We sent that one"); + } + } + else + { + LOG_PRINT_L1("Already saw that one, it's for us"); + } + } + + // get those txes + if (!txids.empty()) + { + cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request req; + cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response res; + for (const auto &txid: txids) + req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid)); + MDEBUG("asking for " << txids.size() << " transactions"); + req.decode_as_json = false; + m_daemon_rpc_mutex.lock(); + bool r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout); + m_daemon_rpc_mutex.unlock(); + MDEBUG("Got " << r << " and " << res.status); + if (r && res.status == CORE_RPC_STATUS_OK) + { + if (res.txs.size() == txids.size()) + { + size_t n = 0; + for (const auto &txid: txids) { - // not one of those we sent ourselves - cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request req; - cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response res; - req.txs_hashes.push_back(it.id_hash); - req.decode_as_json = false; - m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout); - m_daemon_rpc_mutex.unlock(); - if (r && res.status == CORE_RPC_STATUS_OK) + // might have just been put in a block + if (res.txs[n].in_pool) { - if (res.txs.size() == 1) + cryptonote::transaction tx; + cryptonote::blobdata bd; + crypto::hash tx_hash, tx_prefix_hash; + if (epee::string_tools::parse_hexstr_to_binbuff(res.txs[n].as_hex, bd)) { - // might have just been put in a block - if (res.txs[0].in_pool) + if (cryptonote::parse_and_validate_tx_from_blob(bd, tx, tx_hash, tx_prefix_hash)) { - cryptonote::transaction tx; - cryptonote::blobdata bd; - crypto::hash tx_hash, tx_prefix_hash; - if (epee::string_tools::parse_hexstr_to_binbuff(res.txs[0].as_hex, bd)) + if (tx_hash == txid) { - if (cryptonote::parse_and_validate_tx_from_blob(bd, tx, tx_hash, tx_prefix_hash)) - { - if (tx_hash == txid) - { - process_new_transaction(tx, std::vector<uint64_t>(), 0, time(NULL), false, true); - } - else - { - LOG_PRINT_L0("Mismatched txids when processing unconfimed txes from pool"); - } - } - else + process_new_transaction(txid, tx, std::vector<uint64_t>(), 0, time(NULL), false, true); + m_scanned_pool_txs[0].insert(txid); + if (m_scanned_pool_txs[0].size() > 5000) { - LOG_PRINT_L0("failed to validate transaction from daemon"); + std::swap(m_scanned_pool_txs[0], m_scanned_pool_txs[1]); + m_scanned_pool_txs[0].clear(); } } else { - LOG_PRINT_L0("Failed to parse tx " << txid); + LOG_PRINT_L0("Mismatched txids when processing unconfimed txes from pool"); } } else { - LOG_PRINT_L1("Tx " << txid << " was in pool, but is no more"); + LOG_PRINT_L0("failed to validate transaction from daemon"); } } else { - LOG_PRINT_L0("Expected 1 tx, got " << res.txs.size()); + LOG_PRINT_L0("Failed to parse tx " << txid); } } else { - LOG_PRINT_L0("Error calling gettransactions daemon RPC: r " << r << ", status " << res.status); + LOG_PRINT_L1("Tx " << txid << " was in pool, but is no more"); } - } - else - { - LOG_PRINT_L1("We sent that one"); + ++n; } } else { - LOG_PRINT_L1("Already saw that one"); + LOG_PRINT_L0("Expected " << txids.size() << " tx(es), got " << res.txs.size()); } } else { - LOG_PRINT_L0("Failed to parse txid"); + LOG_PRINT_L0("Error calling gettransactions daemon RPC: r " << r << ", status " << res.status); } } + MDEBUG("update_pool_state end"); } //---------------------------------------------------------------------------------------------------- void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history) @@ -1865,6 +1926,18 @@ bool wallet2::store_keys(const std::string& keys_file_name, const std::string& p value2.SetInt(m_ask_password ? 1 :0); json.AddMember("ask_password", value2, json.GetAllocator()); + value2.SetUint(m_min_output_count); + json.AddMember("min_output_count", value2, json.GetAllocator()); + + value2.SetUint64(m_min_output_value); + json.AddMember("min_output_value", value2, json.GetAllocator()); + + value2.SetInt(cryptonote::get_default_decimal_point()); + json.AddMember("default_decimal_point", value2, json.GetAllocator()); + + value2.SetInt(m_merge_destinations ? 1 :0); + json.AddMember("merge_destinations", value2, json.GetAllocator()); + // Serialize the JSON object rapidjson::StringBuffer buffer; rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); @@ -1933,6 +2006,9 @@ bool wallet2::load_keys(const std::string& keys_file_name, const std::string& pa m_refresh_type = RefreshType::RefreshDefault; m_confirm_missing_payment_id = true; m_ask_password = true; + m_min_output_count = 0; + m_min_output_value = 0; + m_merge_destinations = false; } else { @@ -1995,6 +2071,14 @@ bool wallet2::load_keys(const std::string& keys_file_name, const std::string& pa m_confirm_missing_payment_id = field_confirm_missing_payment_id; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ask_password, int, Int, false, true); m_ask_password = field_ask_password; + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_decimal_point, int, Int, false, CRYPTONOTE_DISPLAY_DECIMAL_POINT); + cryptonote::set_default_decimal_point(field_default_decimal_point); + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, min_output_count, uint32_t, Uint, false, 0); + m_min_output_count = field_min_output_count; + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, min_output_value, uint64_t, Uint64, false, 0); + m_min_output_value = field_min_output_value; + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, merge_destinations, int, Int, false, false); + m_merge_destinations = field_merge_destinations; } const cryptonote::account_keys& keys = m_account.get_keys(); @@ -2259,6 +2343,16 @@ bool wallet2::parse_payment_id(const std::string& payment_id_str, crypto::hash& return false; } //---------------------------------------------------------------------------------------------------- +void wallet2::set_default_decimal_point(unsigned int decimal_point) +{ + cryptonote::set_default_decimal_point(decimal_point); +} +//---------------------------------------------------------------------------------------------------- +unsigned int wallet2::get_default_decimal_point() const +{ + return cryptonote::get_default_decimal_point(); +} +//---------------------------------------------------------------------------------------------------- bool wallet2::prepare_file_names(const std::string& file_path) { do_prepare_file_names(file_path, m_keys_file, m_wallet_file); @@ -2271,6 +2365,7 @@ bool wallet2::check_connection(uint32_t *version, uint32_t timeout) if(!m_http_client.is_connected()) { + m_node_rpc_proxy.invalidate(); if (!m_http_client.connect(std::chrono::milliseconds(timeout))) return false; } @@ -2283,7 +2378,11 @@ bool wallet2::check_connection(uint32_t *version, uint32_t timeout) req_t.id = epee::serialization::storage_entry(0); req_t.method = "get_version"; bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client); - if (!r || resp_t.result.status != CORE_RPC_STATUS_OK) + if(!r) { + *version = 0; + return false; + } + if (resp_t.result.status != CORE_RPC_STATUS_OK) *version = 0; else *version = resp_t.result.version; @@ -2590,28 +2689,31 @@ void wallet2::get_unconfirmed_payments(std::list<std::pair<crypto::hash,wallet2: //---------------------------------------------------------------------------------------------------- void wallet2::rescan_spent() { - std::vector<std::string> key_images; - - // make a list of key images for all our outputs - for (size_t i = 0; i < m_transfers.size(); ++i) - { - const transfer_details& td = m_transfers[i]; - key_images.push_back(string_tools::pod_to_hex(td.m_key_image)); + // This is RPC call that can take a long time if there are many outputs, + // so we call it several times, in stripes, so we don't time out spuriously + std::vector<int> spent_status; + spent_status.reserve(m_transfers.size()); + const size_t chunk_size = 1000; + for (size_t start_offset = 0; start_offset < m_transfers.size(); start_offset += chunk_size) + { + const size_t n_outputs = std::min<size_t>(chunk_size, m_transfers.size() - start_offset); + MDEBUG("Calling is_key_image_spent on " << start_offset << " - " << (start_offset + n_outputs - 1) << ", out of " << m_transfers.size()); + COMMAND_RPC_IS_KEY_IMAGE_SPENT::request req = AUTO_VAL_INIT(req); + COMMAND_RPC_IS_KEY_IMAGE_SPENT::response daemon_resp = AUTO_VAL_INIT(daemon_resp); + for (size_t n = start_offset; n < start_offset + n_outputs; ++n) + req.key_images.push_back(string_tools::pod_to_hex(m_transfers[n].m_key_image)); + m_daemon_rpc_mutex.lock(); + bool r = epee::net_utils::invoke_http_json("/is_key_image_spent", req, daemon_resp, m_http_client, rpc_timeout); + m_daemon_rpc_mutex.unlock(); + THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "is_key_image_spent"); + THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "is_key_image_spent"); + THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::is_key_image_spent_error, daemon_resp.status); + THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != n_outputs, error::wallet_internal_error, + "daemon returned wrong response for is_key_image_spent, wrong amounts count = " + + std::to_string(daemon_resp.spent_status.size()) + ", expected " + std::to_string(n_outputs)); + std::copy(daemon_resp.spent_status.begin(), daemon_resp.spent_status.end(), std::back_inserter(spent_status)); } - COMMAND_RPC_IS_KEY_IMAGE_SPENT::request req = AUTO_VAL_INIT(req); - COMMAND_RPC_IS_KEY_IMAGE_SPENT::response daemon_resp = AUTO_VAL_INIT(daemon_resp); - req.key_images = key_images; - m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_json("/is_key_image_spent", req, daemon_resp, m_http_client, rpc_timeout); - m_daemon_rpc_mutex.unlock(); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "is_key_image_spent"); - THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "is_key_image_spent"); - THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::is_key_image_spent_error, daemon_resp.status); - THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != key_images.size(), error::wallet_internal_error, - "daemon returned wrong response for is_key_image_spent, wrong amounts count = " + - std::to_string(daemon_resp.spent_status.size()) + ", expected " + std::to_string(key_images.size())); - // update spent status for (size_t i = 0; i < m_transfers.size(); ++i) { @@ -2619,7 +2721,7 @@ void wallet2::rescan_spent() // a view wallet may not know about key images if (!td.m_key_image_known) continue; - if (td.m_spent != (daemon_resp.spent_status[i] != COMMAND_RPC_IS_KEY_IMAGE_SPENT::UNSPENT)) + if (td.m_spent != (spent_status[i] != COMMAND_RPC_IS_KEY_IMAGE_SPENT::UNSPENT)) { if (td.m_spent) { @@ -3271,20 +3373,35 @@ bool wallet2::load_tx(const std::string &signed_filename, std::vector<tools::wal return true; } //---------------------------------------------------------------------------------------------------- -uint64_t wallet2::get_fee_multiplier(uint32_t priority, bool use_new_fee) const +uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm) const { static const uint64_t old_multipliers[3] = {1, 2, 3}; static const uint64_t new_multipliers[3] = {1, 20, 166}; + static const uint64_t newer_multipliers[4] = {1, 4, 20, 166}; - // 0 -> default (here, x1) + // 0 -> default (here, x1 till fee algorithm 2, x4 from it) if (priority == 0) priority = m_default_priority; if (priority == 0) - priority = 1; + { + if (fee_algorithm >= 2) + priority = 2; + else + priority = 1; + } - // 1 to 3 are allowed as priorities - if (priority >= 1 && priority <= 3) - return (use_new_fee ? new_multipliers : old_multipliers)[priority-1]; + // 1 to 3/4 are allowed as priorities + uint32_t max_priority = (fee_algorithm >= 2) ? 4 : 3; + if (priority >= 1 && priority <= max_priority) + { + switch (fee_algorithm) + { + case 0: return old_multipliers[priority-1]; + case 1: return new_multipliers[priority-1]; + case 2: return newer_multipliers[priority-1]; + default: THROW_WALLET_EXCEPTION_IF (true, error::invalid_priority); + } + } THROW_WALLET_EXCEPTION_IF (false, error::invalid_priority); return 1; @@ -3309,17 +3426,26 @@ uint64_t wallet2::get_per_kb_fee() return get_dynamic_per_kb_fee_estimate(); } //---------------------------------------------------------------------------------------------------- +int wallet2::get_fee_algorithm() +{ + // changes at v3 and v5 + if (use_fork_rules(5, 0)) + return 2; + if (use_fork_rules(3, -720 * 14)) + return 1; + return 0; +} +//---------------------------------------------------------------------------------------------------- // separated the call(s) to wallet2::transfer into their own function // // this function will make multiple calls to wallet2::transfer if multiple // transactions will be required std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon) { - const std::vector<size_t> unused_transfers_indices = select_available_outputs_from_histogram(fake_outs_count + 1, true, true, trusted_daemon); + const std::vector<size_t> unused_transfers_indices = select_available_outputs_from_histogram(fake_outs_count + 1, true, true, true, trusted_daemon); - const bool use_new_fee = use_fork_rules(3, -720 * 14); const uint64_t fee_per_kb = get_per_kb_fee(); - const uint64_t fee_multiplier = get_fee_multiplier(priority, use_new_fee); + const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm()); // failsafe split attempt counter size_t attempt_count = 0; @@ -3438,7 +3564,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> req_t.params.amounts.resize(std::distance(req_t.params.amounts.begin(), end)); req_t.params.unlocked = true; req_t.params.recent_cutoff = time(NULL) - RECENT_OUTPUT_ZONE; - bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client); + bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected"); THROW_WALLET_EXCEPTION_IF(resp_t.result.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram"); @@ -3525,7 +3651,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> uint64_t i; if (num_found - 1 < recent_outputs_count) // -1 to account for the real one we seeded with { - // equiprobable distribution over the recent outs + // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53); double frac = std::sqrt((double)r / ((uint64_t)1 << 53)); i = (uint64_t)(frac*num_recent_outs) + num_outs - num_recent_outs; @@ -3984,6 +4110,14 @@ static size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs) return size; } +static size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs) +{ + if (use_rct) + return estimate_rct_tx_size(n_inputs, mixin, n_outputs + 1); + else + return n_inputs * (mixin+1) * APPROXIMATE_INPUT_BYTES; +} + std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money) const { std::vector<size_t> picks; @@ -4044,7 +4178,7 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money) co return picks; } -static bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) +bool wallet2::should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const { if (!use_rct) return false; @@ -4052,9 +4186,52 @@ static bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const return false; if (unused_dust_indices.empty() && unused_transfers_indices.empty()) return false; + // we want at least one free rct output to avoid a corner case where + // we'd choose a non rct output which doesn't have enough "siblings" + // value-wise on the chain, and thus can't be mixed + bool found = false; + for (auto i: unused_dust_indices) + { + if (m_transfers[i].is_rct()) + { + found = true; + break; + } + } + if (!found) for (auto i: unused_transfers_indices) + { + if (m_transfers[i].is_rct()) + { + found = true; + break; + } + } + if (!found) + return false; return true; } +std::vector<size_t> wallet2::get_only_rct(const std::vector<size_t> &unused_dust_indices, const std::vector<size_t> &unused_transfers_indices) const +{ + std::vector<size_t> indices; + for (size_t n: unused_dust_indices) + if (m_transfers[n].is_rct()) + indices.push_back(n); + for (size_t n: unused_transfers_indices) + if (m_transfers[n].is_rct()) + indices.push_back(n); + return indices; +} + +static uint32_t get_count_above(const std::vector<wallet2::transfer_details> &transfers, const std::vector<size_t> &indices, uint64_t threshold) +{ + uint32_t count = 0; + for (size_t idx: indices) + if (transfers[idx].amount() >= threshold) + ++count; + return count; +} + // Another implementation of transaction creation that is hopefully better // While there is anything left to pay, it goes through random outputs and tries // to fill the next destination/amount. If it fully fills it, it will use the @@ -4083,13 +4260,23 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp pending_tx ptx; size_t bytes; - void add(const account_public_address &addr, uint64_t amount) { - std::vector<cryptonote::tx_destination_entry>::iterator i; - i = std::find_if(dsts.begin(), dsts.end(), [&](const cryptonote::tx_destination_entry &d) { return !memcmp (&d.addr, &addr, sizeof(addr)); }); - if (i == dsts.end()) - dsts.push_back(tx_destination_entry(amount,addr)); - else + void add(const account_public_address &addr, uint64_t amount, unsigned int original_output_index, bool merge_destinations) { + if (merge_destinations) + { + std::vector<cryptonote::tx_destination_entry>::iterator i; + i = std::find_if(dsts.begin(), dsts.end(), [&](const cryptonote::tx_destination_entry &d) { return !memcmp (&d.addr, &addr, sizeof(addr)); }); + if (i == dsts.end()) + dsts.push_back(tx_destination_entry(0,addr)); i->amount += amount; + } + else + { + THROW_WALLET_EXCEPTION_IF(original_output_index > dsts.size(), error::wallet_internal_error, "original_output_index too large"); + if (original_output_index == dsts.size()) + dsts.push_back(tx_destination_entry(0,addr)); + THROW_WALLET_EXCEPTION_IF(memcmp(&dsts[original_output_index].addr, &addr, sizeof(addr)), error::wallet_internal_error, "Mismatched destination address"); + dsts[original_output_index].amount += amount; + } } }; std::vector<TX> txes; @@ -4098,9 +4285,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp uint64_t upper_transaction_size_limit = get_upper_tranaction_size_limit(); const bool use_rct = use_fork_rules(4, 0); - const bool use_new_fee = use_fork_rules(3, -720 * 14); const uint64_t fee_per_kb = get_per_kb_fee(); - const uint64_t fee_multiplier = get_fee_multiplier(priority, use_new_fee); + const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm()); // throw if attempting a transaction with no destinations THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination); @@ -4120,8 +4306,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp THROW_WALLET_EXCEPTION_IF(needed_money == 0, error::zero_destination); // gather all our dust and non dust outputs - const std::vector<size_t> unused_indices = select_available_outputs_from_histogram(fake_outs_count + 1, true, true, trusted_daemon); - for (size_t i: unused_indices) + for (size_t i = 0; i < m_transfers.size(); ++i) { const transfer_details& td = m_transfers[i]; if (!td.m_spent && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td)) @@ -4180,6 +4365,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp // - we have something to send // - or we need to gather more fee // - or we have just one input in that tx, which is rct (to try and make all/most rct txes 2/2) + unsigned int original_output_index = 0; while ((!dsts.empty() && dsts[0].amount > 0) || adding_fee || should_pick_a_second_output(use_rct, txes.back().selected_transfers.size(), unused_transfers_indices, unused_dust_indices)) { TX &tx = txes.back(); @@ -4202,22 +4388,33 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp // get a random unspent output and use it to pay part (or all) of the current destination (and maybe next one, etc) // This could be more clever, but maybe at the cost of making probabilistic inferences easier size_t idx; - if ((dsts.empty() || dsts[0].amount == 0) && !adding_fee) + if ((dsts.empty() || dsts[0].amount == 0) && !adding_fee) { // the "make rct txes 2/2" case - we pick a small value output to "clean up" the wallet too - idx = pop_best_value(unused_dust_indices.empty() ? unused_transfers_indices : unused_dust_indices, tx.selected_transfers, true); - else if (!prefered_inputs.empty()) { - idx = pop_back(prefered_inputs); - pop_if_present(unused_transfers_indices, idx); - pop_if_present(unused_dust_indices, idx); + std::vector<size_t> indices = get_only_rct(unused_dust_indices, unused_transfers_indices); + idx = pop_best_value(indices, tx.selected_transfers, true); + + // we might not want to add it if it's a large output and we don't have many left + if (m_transfers[idx].amount() >= m_min_output_value) { + if (get_count_above(m_transfers, unused_transfers_indices, m_min_output_value) < m_min_output_count) { + LOG_PRINT_L2("Second output was not strictly needed, and we're running out of outputs above " << print_money(m_min_output_value) << ", not adding"); + break; + } + } // since we're trying to add a second output which is not strictly needed, // we only add it if it's unrelated enough to the first one float relatedness = get_output_relatedness(m_transfers[idx], m_transfers[tx.selected_transfers.front()]); if (relatedness > SECOND_OUTPUT_RELATEDNESS_THRESHOLD) { - LOG_PRINT_L2("Second outout was not strictly needed, and relatedness " << relatedness << ", not adding"); + LOG_PRINT_L2("Second output was not strictly needed, and relatedness " << relatedness << ", not adding"); break; } + pop_if_present(unused_transfers_indices, idx); + pop_if_present(unused_dust_indices, idx); + } else if (!prefered_inputs.empty()) { + idx = pop_back(prefered_inputs); + pop_if_present(unused_transfers_indices, idx); + pop_if_present(unused_dust_indices, idx); } else idx = pop_best_value(unused_transfers_indices.empty() ? unused_dust_indices : unused_transfers_indices, tx.selected_transfers); @@ -4239,22 +4436,23 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp } else { - while (!dsts.empty() && dsts[0].amount <= available_amount) + while (!dsts.empty() && dsts[0].amount <= available_amount && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()) < TX_SIZE_TARGET(upper_transaction_size_limit)) { // we can fully pay that destination LOG_PRINT_L2("We can fully pay " << get_account_address_as_str(m_testnet, dsts[0].addr) << " for " << print_money(dsts[0].amount)); - tx.add(dsts[0].addr, dsts[0].amount); + tx.add(dsts[0].addr, dsts[0].amount, original_output_index, m_merge_destinations); available_amount -= dsts[0].amount; dsts[0].amount = 0; pop_index(dsts, 0); + ++original_output_index; } - if (available_amount > 0 && !dsts.empty()) { + if (available_amount > 0 && !dsts.empty() && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()) < TX_SIZE_TARGET(upper_transaction_size_limit)) { // we can partially fill that destination LOG_PRINT_L2("We can partially pay " << get_account_address_as_str(m_testnet, dsts[0].addr) << " for " << print_money(available_amount) << "/" << print_money(dsts[0].amount)); - tx.add(dsts[0].addr, available_amount); + tx.add(dsts[0].addr, available_amount, original_output_index, m_merge_destinations); dsts[0].amount -= available_amount; available_amount = 0; } @@ -4271,11 +4469,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp } else { - size_t estimated_rct_tx_size; - if (use_rct) - estimated_rct_tx_size = estimate_rct_tx_size(tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 1); - else - estimated_rct_tx_size = tx.selected_transfers.size() * (fake_outs_count+1) * APPROXIMATE_INPUT_BYTES; + const size_t estimated_rct_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()); try_tx = dsts.empty() || (estimated_rct_tx_size >= TX_SIZE_TARGET(upper_transaction_size_limit)); } @@ -4387,7 +4581,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp return ptx_vector; } -std::vector<wallet2::pending_tx> wallet2::create_transactions_all(const cryptonote::account_public_address &address, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon) +std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon) { std::vector<size_t> unused_transfers_indices; std::vector<size_t> unused_dust_indices; @@ -4399,10 +4593,13 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(const cryptono const transfer_details& td = m_transfers[i]; if (!td.m_spent && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td)) { - if (td.is_rct() || is_valid_decomposed_amount(td.amount())) - unused_transfers_indices.push_back(i); - else - unused_dust_indices.push_back(i); + if (below == 0 || td.amount() < below) + { + if (td.is_rct() || is_valid_decomposed_amount(td.amount())) + unused_transfers_indices.push_back(i); + else + unused_dust_indices.push_back(i); + } } } @@ -4425,9 +4622,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton std::vector<std::vector<get_outs_entry>> outs; const bool use_rct = fake_outs_count > 0 && use_fork_rules(4, 0); - const bool use_new_fee = use_fork_rules(3, -720 * 14); const uint64_t fee_per_kb = get_per_kb_fee(); - const uint64_t fee_multiplier = get_fee_multiplier(priority, use_new_fee); + const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm()); LOG_PRINT_L2("Starting with " << unused_transfers_indices.size() << " non-dust outputs and " << unused_dust_indices.size() << " dust outputs"); @@ -4464,11 +4660,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton // here, check if we need to sent tx and start a new one LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit " << upper_transaction_size_limit); - size_t estimated_rct_tx_size; - if (use_rct) - estimated_rct_tx_size = estimate_rct_tx_size(tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 1); - else - estimated_rct_tx_size = tx.selected_transfers.size() * (fake_outs_count+1) * APPROXIMATE_INPUT_BYTES; + const size_t estimated_rct_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 1); bool try_tx = (unused_dust_indices.empty() && unused_transfers_indices.empty()) || ( estimated_rct_tx_size >= TX_SIZE_TARGET(upper_transaction_size_limit)); if (try_tx) { @@ -4588,7 +4780,7 @@ uint64_t wallet2::get_upper_tranaction_size_limit() { if (m_upper_transaction_size_limit > 0) return m_upper_transaction_size_limit; - uint64_t full_reward_zone = use_fork_rules(2, 10) ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 : CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1; + uint64_t full_reward_zone = use_fork_rules(5, 10) ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : use_fork_rules(2, 10) ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 : CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1; return ((full_reward_zone * 125) / 100) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; } //---------------------------------------------------------------------------------------------------- @@ -4614,7 +4806,7 @@ std::vector<uint64_t> wallet2::get_unspent_amounts_vector() for (const auto &td: m_transfers) { if (!td.m_spent) - set.insert(td.amount()); + set.insert(td.is_rct() ? 0 : td.amount()); } std::vector<uint64_t> vector; vector.reserve(set.size()); @@ -4625,7 +4817,7 @@ std::vector<uint64_t> wallet2::get_unspent_amounts_vector() return vector; } //---------------------------------------------------------------------------------------------------- -std::vector<size_t> wallet2::select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool trusted_daemon) +std::vector<size_t> wallet2::select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct, bool trusted_daemon) { epee::json_rpc::request<cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request> req_t = AUTO_VAL_INIT(req_t); epee::json_rpc::response<cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response, std::string> resp_t = AUTO_VAL_INIT(resp_t); @@ -4638,9 +4830,9 @@ std::vector<size_t> wallet2::select_available_outputs_from_histogram(uint64_t co req_t.params.min_count = count; req_t.params.max_count = 0; req_t.params.unlocked = unlocked; - bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client); + bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client, rpc_timeout); m_daemon_rpc_mutex.unlock(); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "select_available_unmixable_outputs"); + THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "select_available_outputs_from_histogram"); THROW_WALLET_EXCEPTION_IF(resp_t.result.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram"); THROW_WALLET_EXCEPTION_IF(resp_t.result.status != CORE_RPC_STATUS_OK, error::get_histogram_error, resp_t.result.status); @@ -4650,10 +4842,10 @@ std::vector<size_t> wallet2::select_available_outputs_from_histogram(uint64_t co mixable.insert(i.amount); } - return select_available_outputs([mixable, atleast](const transfer_details &td) { - if (td.is_rct()) + return select_available_outputs([mixable, atleast, allow_rct](const transfer_details &td) { + if (!allow_rct && td.is_rct()) return false; - const uint64_t amount = td.amount(); + const uint64_t amount = td.is_rct() ? 0 : td.amount(); if (atleast) { if (mixable.find(amount) != mixable.end()) return true; @@ -4677,7 +4869,7 @@ uint64_t wallet2::get_num_rct_outputs() req_t.params.amounts.push_back(0); req_t.params.min_count = 0; req_t.params.max_count = 0; - bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client); + bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_num_rct_outputs"); THROW_WALLET_EXCEPTION_IF(resp_t.result.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram"); @@ -4698,14 +4890,14 @@ std::vector<size_t> wallet2::select_available_unmixable_outputs(bool trusted_dae { // request all outputs with less than 3 instances const size_t min_mixin = use_fork_rules(5, 10) ? 4 : 2; // v5 increases min mixin from 2 to 4 - return select_available_outputs_from_histogram(min_mixin + 1, false, true, trusted_daemon); + return select_available_outputs_from_histogram(min_mixin + 1, false, true, false, trusted_daemon); } //---------------------------------------------------------------------------------------------------- std::vector<size_t> wallet2::select_available_mixable_outputs(bool trusted_daemon) { // request all outputs with at least 3 instances, so we can use mixin 2 with const size_t min_mixin = use_fork_rules(5, 10) ? 4 : 2; // v5 increases min mixin from 2 to 4 - return select_available_outputs_from_histogram(min_mixin + 1, true, true, trusted_daemon); + return select_available_outputs_from_histogram(min_mixin + 1, true, true, true, trusted_daemon); } //---------------------------------------------------------------------------------------------------- std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions(bool trusted_daemon) @@ -4714,7 +4906,6 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions(bo const bool hf1_rules = use_fork_rules(2, 10); // first hard fork has version 2 tx_dust_policy dust_policy(hf1_rules ? 0 : ::config::DEFAULT_DUST_THRESHOLD); - const bool use_new_fee = use_fork_rules(3, -720 * 14); const uint64_t fee_per_kb = get_per_kb_fee(); // may throw @@ -5409,7 +5600,7 @@ uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, ui height_mid, height_max }; - bool r = net_utils::invoke_http_bin("/getblocks_by_height.bin", req, res, m_http_client); + bool r = net_utils::invoke_http_bin("/getblocks_by_height.bin", req, res, m_http_client, rpc_timeout); if (!r || res.status != CORE_RPC_STATUS_OK) { std::ostringstream oss; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 567292d30..03c6a431b 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -39,13 +39,14 @@ #include <atomic> #include "include_base_utils.h" -#include "cryptonote_core/account.h" -#include "cryptonote_core/account_boost_serialization.h" -#include "cryptonote_core/cryptonote_basic_impl.h" +#include "cryptonote_basic/account.h" +#include "cryptonote_basic/account_boost_serialization.h" +#include "cryptonote_basic/cryptonote_basic_impl.h" #include "net/http_client.h" #include "storages/http_abstract_invoke.h" #include "rpc/core_rpc_server_commands_defs.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_format_utils.h" +#include "cryptonote_core/cryptonote_tx_utils.h" #include "common/unordered_containers_boost_serialization.h" #include "crypto/chacha8.h" #include "crypto/hash.h" @@ -53,7 +54,7 @@ #include "ringct/rctOps.h" #include "wallet_errors.h" -#include "password_container.h" +#include "common/password.h" #include "node_rpc_proxy.h" #include <iostream> @@ -69,10 +70,10 @@ namespace tools { public: virtual void on_new_block(uint64_t height, const cryptonote::block& block) {} - virtual void on_money_received(uint64_t height, const cryptonote::transaction& tx, uint64_t amount) {} - virtual void on_unconfirmed_money_received(uint64_t height, const cryptonote::transaction& tx, uint64_t amount) {} - virtual void on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx) {} - virtual void on_skip_transaction(uint64_t height, const cryptonote::transaction& tx) {} + virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount) {} + virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount) {} + virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx) {} + virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx) {} virtual ~i_wallet2_callback() {} }; @@ -104,7 +105,7 @@ namespace tools }; private: - wallet2(const wallet2&) : m_run(true), m_callback(0), m_testnet(false), m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true), m_ask_password(true), m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex) {} + wallet2(const wallet2&) : m_run(true), m_callback(0), m_testnet(false), m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true), m_ask_password(true), m_min_output_count(0), m_min_output_value(0), m_merge_destinations(false), m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex) {} public: static const char* tr(const char* str); @@ -113,7 +114,7 @@ namespace tools static void init_options(boost::program_options::options_description& desc_params); //! \return Password retrieved from prompt. Logs error on failure. - static boost::optional<password_container> password_prompt(const bool is_new_wallet); + static boost::optional<password_container> password_prompt(const bool new_password); //! Uses stdin and stdout. Returns a wallet2 if no errors. static std::unique_ptr<wallet2> make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file); @@ -125,7 +126,11 @@ namespace tools //! Uses stdin and stdout. Returns a wallet2 and password for wallet with no file if no errors. static std::pair<std::unique_ptr<wallet2>, password_container> make_new(const boost::program_options::variables_map& vm); - wallet2(bool testnet = false, bool restricted = false) : m_run(true), m_callback(0), m_testnet(testnet), m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true), m_ask_password(true), m_restricted(restricted), is_old_file_format(false), m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex) {} + //! Just parses variables. + static std::unique_ptr<wallet2> make_dummy(const boost::program_options::variables_map& vm); + + wallet2(bool testnet = false, bool restricted = false) : m_run(true), m_callback(0), m_testnet(testnet), m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true), m_ask_password(true), m_min_output_count(0), m_min_output_value(0), m_merge_destinations(false), m_restricted(restricted), is_old_file_format(false), m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex) {} + struct transfer_details { uint64_t m_block_height; @@ -343,7 +348,8 @@ namespace tools // into account the current median block size rather than // the minimum block size. bool deinit(); - bool init(std::string daemon_address = "http://localhost:8080", uint64_t upper_transaction_size_limit = 0); + bool init(std::string daemon_address = "http://localhost:8080", + boost::optional<epee::net_utils::http::login> daemon_login = boost::none, uint64_t upper_transaction_size_limit = 0); void stop() { m_run.store(false, std::memory_order_relaxed); } @@ -408,7 +414,7 @@ namespace tools bool load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func = NULL); std::vector<pending_tx> create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon); std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon); - std::vector<wallet2::pending_tx> create_transactions_all(const cryptonote::account_public_address &address, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon); + std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon); std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon); std::vector<pending_tx> create_unmixable_sweep_transactions(bool trusted_daemon); bool check_connection(uint32_t *version = NULL, uint32_t timeout = 200000); @@ -454,7 +460,14 @@ namespace tools a & m_tx_notes; if(ver < 13) return; - a & m_unconfirmed_payments; + if (ver < 17) + { + // we're loading an old version, where m_unconfirmed_payments was a std::map + std::unordered_map<crypto::hash, payment_details> m; + a & m; + for (std::unordered_map<crypto::hash, payment_details>::const_iterator i = m.begin(); i != m.end(); ++i) + m_unconfirmed_payments.insert(*i); + } if(ver < 14) return; if(ver < 15) @@ -473,6 +486,13 @@ namespace tools if(ver < 16) return; a & m_address_book; + if(ver < 17) + return; + a & m_unconfirmed_payments; + if(ver < 18) + return; + a & m_scanned_pool_txs[0]; + a & m_scanned_pool_txs[1]; } /*! @@ -508,6 +528,14 @@ namespace tools void confirm_missing_payment_id(bool always) { m_confirm_missing_payment_id = always; } bool ask_password() const { return m_ask_password; } void ask_password(bool always) { m_ask_password = always; } + void set_default_decimal_point(unsigned int decimal_point); + unsigned int get_default_decimal_point() const; + void set_min_output_count(uint32_t count) { m_min_output_count = count; } + uint32_t get_min_output_count() const { return m_min_output_count; } + void set_min_output_value(uint64_t value) { m_min_output_value = value; } + uint64_t get_min_output_value() const { return m_min_output_value; } + void merge_destinations(bool merge) { m_merge_destinations = merge; } + bool merge_destinations() const { return m_merge_destinations; } bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key) const; @@ -523,17 +551,19 @@ namespace tools void get_hard_fork_info(uint8_t version, uint64_t &earliest_height); bool use_fork_rules(uint8_t version, int64_t early_blocks = 0); + int get_fee_algorithm(); std::string get_wallet_file() const; std::string get_keys_file() const; std::string get_daemon_address() const; + const boost::optional<epee::net_utils::http::login>& get_daemon_login() const { return m_daemon_login; } uint64_t get_daemon_blockchain_height(std::string& err); uint64_t get_daemon_blockchain_target_height(std::string& err); /*! * \brief Calculates the approximate blockchain height from current date/time. */ uint64_t get_approximate_blockchain_height() const; - std::vector<size_t> select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool trusted_daemon); + std::vector<size_t> select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct, bool trusted_daemon); std::vector<size_t> select_available_outputs(const std::function<bool(const transfer_details &td)> &f); std::vector<size_t> select_available_unmixable_outputs(bool trusted_daemon); std::vector<size_t> select_available_mixable_outputs(bool trusted_daemon); @@ -582,7 +612,7 @@ namespace tools * \param password Password of wallet file */ bool load_keys(const std::string& keys_file_name, const std::string& password); - void process_new_transaction(const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool); + void process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool); void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height, const cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices &o_indices); void detach_blockchain(uint64_t height); void get_short_chain_history(std::list<crypto::hash>& ids) const; @@ -595,8 +625,8 @@ namespace tools void process_blocks(uint64_t start_height, const std::list<cryptonote::block_complete_entry> &blocks, const std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices, uint64_t& blocks_added); uint64_t select_transfers(uint64_t needed_money, std::vector<size_t> unused_transfers_indices, std::list<size_t>& selected_transfers, bool trusted_daemon); bool prepare_file_names(const std::string& file_path); - void process_unconfirmed(const cryptonote::transaction& tx, uint64_t height); - void process_outgoing(const cryptonote::transaction& tx, uint64_t height, uint64_t ts, uint64_t spent, uint64_t received); + void process_unconfirmed(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height); + void process_outgoing(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height, uint64_t ts, uint64_t spent, uint64_t received); void add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t amount_in, const std::vector<cryptonote::tx_destination_entry> &dests, const crypto::hash &payment_id, uint64_t change_amount); void generate_genesis(cryptonote::block& b); void check_genesis(const crypto::hash& genesis_hash) const; //throws @@ -607,7 +637,7 @@ namespace tools void parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const; uint64_t get_upper_tranaction_size_limit(); std::vector<uint64_t> get_unspent_amounts_vector(); - uint64_t get_fee_multiplier(uint32_t priority, bool use_new_fee) const; + uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm) const; uint64_t get_dynamic_per_kb_fee_estimate(); uint64_t get_per_kb_fee(); float get_output_relatedness(const transfer_details &td0, const transfer_details &td1) const; @@ -617,8 +647,11 @@ namespace tools void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::list<size_t> &selected_transfers, size_t fake_outputs_count); bool wallet_generate_key_image_helper(const cryptonote::account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, cryptonote::keypair& in_ephemeral, crypto::key_image& ki); crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const; + bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const; + std::vector<size_t> get_only_rct(const std::vector<size_t> &unused_dust_indices, const std::vector<size_t> &unused_transfers_indices) const; cryptonote::account_base m_account; + boost::optional<epee::net_utils::http::login> m_daemon_login; std::string m_daemon_address; std::string m_wallet_file; std::string m_keys_file; @@ -627,7 +660,7 @@ namespace tools std::atomic<uint64_t> m_local_bc_height; //temporary workaround std::unordered_map<crypto::hash, unconfirmed_transfer_details> m_unconfirmed_txs; std::unordered_map<crypto::hash, confirmed_transfer_details> m_confirmed_txs; - std::unordered_map<crypto::hash, payment_details> m_unconfirmed_payments; + std::unordered_multimap<crypto::hash, payment_details> m_unconfirmed_payments; std::unordered_map<crypto::hash, crypto::secret_key> m_tx_keys; transfer_container m_transfers; @@ -659,10 +692,14 @@ namespace tools uint64_t m_refresh_from_block_height; bool m_confirm_missing_payment_id; bool m_ask_password; + uint32_t m_min_output_count; + uint64_t m_min_output_value; + bool m_merge_destinations; NodeRPCProxy m_node_rpc_proxy; + std::unordered_set<crypto::hash> m_scanned_pool_txs[2]; }; } -BOOST_CLASS_VERSION(tools::wallet2, 16) +BOOST_CLASS_VERSION(tools::wallet2, 18) BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 7) BOOST_CLASS_VERSION(tools::wallet2::payment_details, 1) BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 6) diff --git a/src/wallet/wallet2_api.h b/src/wallet/wallet2_api.h index 883da7da2..17d0caf7d 100644 --- a/src/wallet/wallet2_api.h +++ b/src/wallet/wallet2_api.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -296,7 +296,10 @@ struct Wallet virtual std::string address() const = 0; virtual std::string path() const = 0; virtual bool testnet() const = 0; - + //! returns current hard fork info + virtual void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const = 0; + //! check if hard fork rules should be used + virtual bool useForkRules(uint8_t version, int64_t early_blocks) const = 0; /*! * \brief integratedAddress - returns integrated address for current wallet address and given payment_id. * if passed "payment_id" param is an empty string or not-valid payment id string @@ -340,7 +343,7 @@ struct Wallet * \param upper_transaction_size_limit * \return - true on success */ - virtual bool init(const std::string &daemon_address, uint64_t upper_transaction_size_limit) = 0; + virtual bool init(const std::string &daemon_address, uint64_t upper_transaction_size_limit, const std::string &daemon_username = "", const std::string &daemon_password = "") = 0; /*! * \brief createWatchOnly - Creates a watch only wallet @@ -428,6 +431,9 @@ struct Wallet static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, bool testnet, std::string &error); static std::string paymentIdFromAddress(const std::string &str, bool testnet); static uint64_t maximumAllowedAmount(); + // Easylogger wrapper + static void init(const char *argv0, const char *default_log_base_name); + static void debug(const std::string &str); /** * @brief StartRefresh - Start/resume refresh thread (refresh every 10 seconds) @@ -683,9 +689,6 @@ struct WalletManager //! returns current mining hash rate (0 if not mining) virtual double miningHashRate() const = 0; - //! returns current hard fork info - virtual void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const = 0; - //! returns current block target virtual uint64_t blockTarget() const = 0; @@ -693,13 +696,16 @@ struct WalletManager virtual bool isMining() const = 0; //! starts mining with the set number of threads - virtual bool startMining(const std::string &address, uint32_t threads = 1) = 0; + virtual bool startMining(const std::string &address, uint32_t threads = 1, bool background_mining = false, bool ignore_battery = true) = 0; //! stops mining virtual bool stopMining() = 0; //! resolves an OpenAlias address to a monero address virtual std::string resolveOpenAlias(const std::string &address, bool &dnssec_valid) const = 0; + + //! checks for an update and returns version, hash and url + static std::tuple<bool, std::string, std::string, std::string, std::string> checkUpdates(const std::string &software, const std::string &subdir); }; diff --git a/src/wallet/wallet_args.cpp b/src/wallet/wallet_args.cpp index 12799f613..1508f3791 100644 --- a/src/wallet/wallet_args.cpp +++ b/src/wallet/wallet_args.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -73,6 +73,7 @@ namespace wallet_args const char* const usage, boost::program_options::options_description desc_params, const boost::program_options::positional_options_description& positional_options, + const char *default_log_name, bool log_to_console) { @@ -85,6 +86,7 @@ namespace wallet_args const command_line::arg_descriptor<std::string> arg_log_level = {"log-level", "0-4 or categories", ""}; const command_line::arg_descriptor<uint32_t> arg_max_concurrency = {"max-concurrency", wallet_args::tr("Max number of threads to use for a parallel job"), DEFAULT_MAX_CONCURRENCY}; const command_line::arg_descriptor<std::string> arg_log_file = {"log-file", wallet_args::tr("Specify log file"), ""}; + const command_line::arg_descriptor<std::string> arg_config_file = {"config-file", wallet_args::tr("Config file"), "", true}; std::string lang = i18n_get_language(); @@ -100,6 +102,7 @@ namespace wallet_args command_line::add_arg(desc_params, arg_log_file, ""); command_line::add_arg(desc_params, arg_log_level); command_line::add_arg(desc_params, arg_max_concurrency); + command_line::add_arg(desc_params, arg_config_file); i18n_set_language("translations", "monero", lang); @@ -108,43 +111,58 @@ namespace wallet_args po::variables_map vm; bool r = command_line::handle_error_helper(desc_all, [&]() { - po::store(command_line::parse_command_line(argc, argv, desc_general, true), vm); + auto parser = po::command_line_parser(argc, argv).options(desc_all).positional(positional_options); + po::store(parser.run(), vm); - if (command_line::get_arg(vm, command_line::arg_help)) - { - tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; - tools::msg_writer() << wallet_args::tr("Usage:") << ' ' << usage; - tools::msg_writer() << desc_all; - return false; - } - else if (command_line::get_arg(vm, command_line::arg_version)) + if(command_line::has_arg(vm, arg_config_file)) { - tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; - return false; + std::string config = command_line::get_arg(vm, arg_config_file); + bf::path config_path(config); + boost::system::error_code ec; + if (bf::exists(config_path, ec)) + { + po::store(po::parse_config_file<char>(config_path.string<std::string>().c_str(), desc_params), vm); + } + else + { + tools::fail_msg_writer() << wallet_args::tr("Can't find config file ") << config; + return false; + } } - auto parser = po::command_line_parser(argc, argv).options(desc_params).positional(positional_options); - po::store(parser.run(), vm); po::notify(vm); return true; }); if (!r) return boost::none; - if(command_line::has_arg(vm, arg_max_concurrency)) - tools::set_max_concurrency(command_line::get_arg(vm, arg_max_concurrency)); - std::string log_path; if (!vm["log-file"].defaulted()) log_path = command_line::get_arg(vm, arg_log_file); else - log_path = mlog_get_default_log_path("monero-wallet-cli.log"); + log_path = mlog_get_default_log_path(default_log_name); mlog_configure(log_path, log_to_console); if (!vm["log-level"].defaulted()) { mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str()); } + if (command_line::get_arg(vm, command_line::arg_help)) + { + tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; + tools::msg_writer() << wallet_args::tr("Usage:") << ' ' << usage; + tools::msg_writer() << desc_all; + return boost::none; + } + else if (command_line::get_arg(vm, command_line::arg_version)) + { + tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; + return boost::none; + } + + if(command_line::has_arg(vm, arg_max_concurrency)) + tools::set_max_concurrency(command_line::get_arg(vm, arg_max_concurrency)); + tools::scoped_message_writer(epee::console_color_white, true) << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; if (!vm["log-level"].defaulted()) diff --git a/src/wallet/wallet_args.h b/src/wallet/wallet_args.h index e0719d203..cf23ffded 100644 --- a/src/wallet/wallet_args.h +++ b/src/wallet/wallet_args.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -50,5 +50,5 @@ namespace wallet_args const char* const usage, boost::program_options::options_description desc_params, const boost::program_options::positional_options_description& positional_options, - bool log_to_console = false); + const char *default_log_name, bool log_to_console = false); } diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index 785a72e4b..3e3578149 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -35,7 +35,7 @@ #include <string> #include <vector> -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_format_utils.h" #include "rpc/core_rpc_server_commands_defs.h" #include "include_base_utils.h" diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 22f5f8bb6..f2b3dcaf5 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -38,13 +38,16 @@ using namespace epee; #include "common/command_line.h" #include "common/i18n.h" #include "common/util.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_core/account.h" +#include "cryptonote_basic/cryptonote_format_utils.h" +#include "cryptonote_basic/account.h" #include "wallet_rpc_server_commands_defs.h" #include "misc_language.h" #include "string_coding.h" #include "string_tools.h" #include "crypto/hash.h" +#include "mnemonics/electrum-words.h" +#include "rpc/rpc_args.h" +#include "rpc/core_rpc_server_commands_defs.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "wallet.rpc" @@ -52,10 +55,9 @@ using namespace epee; namespace { const command_line::arg_descriptor<std::string, true> arg_rpc_bind_port = {"rpc-bind-port", "Sets bind port for server"}; - const command_line::arg_descriptor<std::string> arg_rpc_bind_ip = {"rpc-bind-ip", "Specify ip to bind rpc server", "127.0.0.1"}; - const command_line::arg_descriptor<std::string> arg_rpc_login = {"rpc-login", "Specify username[:password] required for RPC connection"}; - const command_line::arg_descriptor<bool> arg_disable_rpc_login = {"disable-rpc-login", "Disable HTTP authentication for RPC"}; - const command_line::arg_descriptor<bool> arg_confirm_external_bind = {"confirm-external-bind", "Confirm rcp-bind-ip value is NOT a loopback (local) IP"}; + const command_line::arg_descriptor<bool> arg_disable_rpc_login = {"disable-rpc-login", "Disable HTTP authentication for RPC connections served by this process"}; + const command_line::arg_descriptor<bool> arg_trusted_daemon = {"trusted-daemon", "Enable commands which rely on a trusted daemon", false}; + const command_line::arg_descriptor<std::string> arg_wallet_dir = {"wallet-dir", "Directory for newly created wallets"}; constexpr const char default_rpc_username[] = "monero"; } @@ -68,8 +70,9 @@ namespace tools } //------------------------------------------------------------------------------------------------------------------------------ - wallet_rpc_server::wallet_rpc_server(wallet2& w):m_wallet(w), rpc_login_filename(), m_stop(false) - {} + wallet_rpc_server::wallet_rpc_server():m_wallet(NULL), rpc_login_filename(), m_stop(false), m_trusted_daemon(false) + { + } //------------------------------------------------------------------------------------------------------------------------------ wallet_rpc_server::~wallet_rpc_server() { @@ -81,12 +84,17 @@ namespace tools catch (...) {} } //------------------------------------------------------------------------------------------------------------------------------ + void wallet_rpc_server::set_wallet(wallet2 *cr) + { + m_wallet = cr; + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::run() { m_stop = false; m_net_server.add_idle_handler([this](){ try { - m_wallet.refresh(); + if (m_wallet) m_wallet->refresh(); } catch (const std::exception& ex) { LOG_ERROR("Exception at while refreshing, what=" << ex.what()); } @@ -105,77 +113,83 @@ namespace tools return epee::http_server_impl_base<wallet_rpc_server, connection_context>::run(1, true); } //------------------------------------------------------------------------------------------------------------------------------ - bool wallet_rpc_server::init(const boost::program_options::variables_map& vm) + void wallet_rpc_server::stop() { - std::string bind_ip = command_line::get_arg(vm, arg_rpc_bind_ip); - if (!bind_ip.empty()) + if (m_wallet) { - // always parse IP here for error consistency - boost::system::error_code ec{}; - const auto parsed_ip = boost::asio::ip::address::from_string(bind_ip, ec); - if (ec) - { - LOG_ERROR(tr("Invalid IP address given for rpc-bind-ip argument")); - return false; - } + m_wallet->store(); + delete m_wallet; + m_wallet = NULL; + } + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::init(const boost::program_options::variables_map *vm) + { + auto rpc_config = cryptonote::rpc_args::process(*vm); + if (!rpc_config) + return false; + + m_vm = vm; + tools::wallet2 *walvars; + std::unique_ptr<tools::wallet2> tmpwal; - if (!parsed_ip.is_loopback() && !command_line::get_arg(vm, arg_confirm_external_bind)) + if (m_wallet) + walvars = m_wallet; + else + { + tmpwal = tools::wallet2::make_dummy(*m_vm); + walvars = tmpwal.get(); + } + boost::optional<epee::net_utils::http::login> http_login{}; + std::string bind_port = command_line::get_arg(*m_vm, arg_rpc_bind_port); + const bool disable_auth = command_line::get_arg(*m_vm, arg_disable_rpc_login); + m_trusted_daemon = command_line::get_arg(*m_vm, arg_trusted_daemon); + if (!command_line::has_arg(*m_vm, arg_trusted_daemon)) + { + if (tools::is_local_address(walvars->get_daemon_address())) { - LOG_ERROR( - tr("The rpc-bind-ip value is listening for unencrypted external connections. Consider SSH tunnel or SSL proxy instead. Override with --confirm-external-bind") - ); - return false; + MINFO(tr("Daemon is local, assuming trusted")); + m_trusted_daemon = true; } } - - epee::net_utils::http::login login{}; - - const bool disable_auth = command_line::get_arg(vm, arg_disable_rpc_login); - const std::string user_pass = command_line::get_arg(vm, arg_rpc_login); - const std::string bind_port = command_line::get_arg(vm, arg_rpc_bind_port); + if (command_line::has_arg(*m_vm, arg_wallet_dir)) + { + m_wallet_dir = command_line::get_arg(*m_vm, arg_wallet_dir); +#ifdef _WIN32 +#define MKDIR(path, mode) mkdir(path) +#else +#define MKDIR(path, mode) mkdir(path, mode) +#endif + MKDIR(m_wallet_dir.c_str(), 0700); + } if (disable_auth) { - if (!user_pass.empty()) + if (rpc_config->login) { - LOG_ERROR(tr("Cannot specify --") << arg_disable_rpc_login.name << tr(" and --") << arg_rpc_login.name); + const cryptonote::rpc_args::descriptors arg{}; + LOG_ERROR(tr("Cannot specify --") << arg_disable_rpc_login.name << tr(" and --") << arg.rpc_login.name); return false; } } else // auth enabled { - if (user_pass.empty()) + if (!rpc_config->login) { - login.username = default_rpc_username; - std::array<std::uint8_t, 16> rand_128bit{{}}; crypto::rand(rand_128bit.size(), rand_128bit.data()); - login.password = string_encoding::base64_encode(rand_128bit.data(), rand_128bit.size()); + http_login.emplace( + default_rpc_username, + string_encoding::base64_encode(rand_128bit.data(), rand_128bit.size()) + ); } - else // user password + else { - const auto loc = user_pass.find(':'); - login.username = user_pass.substr(0, loc); - if (loc != std::string::npos) - { - login.password = user_pass.substr(loc + 1); - } - else - { - login.password = tools::password_container::prompt(true, "RPC password").value_or( - tools::password_container{} - ).password(); - } - - if (login.username.empty() || login.password.empty()) - { - LOG_ERROR(tr("Blank username or password not permitted for RPC authenticaion")); - return false; - } + http_login.emplace( + std::move(rpc_config->login->username), std::move(rpc_config->login->password).password() + ); } - - assert(!login.username.empty()); - assert(!login.password.empty()); + assert(bool(http_login)); std::string temp = "monero-wallet-rpc." + bind_port + ".login"; const auto cookie = tools::create_private_file(temp); @@ -186,9 +200,9 @@ namespace tools } rpc_login_filename.swap(temp); // nothrow guarantee destructor cleanup temp = rpc_login_filename; - std::fputs(login.username.c_str(), cookie.get()); + std::fputs(http_login->username.c_str(), cookie.get()); std::fputc(':', cookie.get()); - std::fputs(login.password.c_str(), cookie.get()); + std::fputs(http_login->password.c_str(), cookie.get()); std::fflush(cookie.get()); if (std::ferror(cookie.get())) { @@ -198,12 +212,21 @@ namespace tools LOG_PRINT_L0(tr("RPC username/password is stored in file ") << temp); } // end auth enabled + m_http_client.set_server(walvars->get_daemon_address(), walvars->get_daemon_login()); + m_net_server.set_threads_prefix("RPC"); return epee::http_server_impl_base<wallet_rpc_server, connection_context>::init( - std::move(bind_port), std::move(bind_ip), std::string{}, boost::make_optional(!disable_auth, std::move(login)) + std::move(bind_port), std::move(rpc_config->bind_ip), std::move(http_login) ); } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::not_open(epee::json_rpc::error& er) + { + er.code = WALLET_RPC_ERROR_CODE_NOT_OPEN; + er.message = "No wallet file"; + return false; + } + //------------------------------------------------------------------------------------------------------------------------------ void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const crypto::hash &payment_id, const tools::wallet2::payment_details &pd) { entry.txid = string_tools::pod_to_hex(pd.m_tx_hash); @@ -214,7 +237,7 @@ namespace tools entry.timestamp = pd.m_timestamp; entry.amount = pd.m_amount; entry.fee = 0; // TODO - entry.note = m_wallet.get_tx_note(pd.m_tx_hash); + entry.note = m_wallet->get_tx_note(pd.m_tx_hash); entry.type = "in"; } //------------------------------------------------------------------------------------------------------------------------------ @@ -229,13 +252,13 @@ namespace tools entry.fee = pd.m_amount_in - pd.m_amount_out; uint64_t change = pd.m_change == (uint64_t)-1 ? 0 : pd.m_change; // change may not be known entry.amount = pd.m_amount_in - change - entry.fee; - entry.note = m_wallet.get_tx_note(txid); + entry.note = m_wallet->get_tx_note(txid); for (const auto &d: pd.m_dests) { entry.destinations.push_back(wallet_rpc::transfer_destination()); wallet_rpc::transfer_destination &td = entry.destinations.back(); td.amount = d.amount; - td.address = get_account_address_as_str(m_wallet.testnet(), d.addr); + td.address = get_account_address_as_str(m_wallet->testnet(), d.addr); } entry.type = "out"; @@ -253,7 +276,7 @@ namespace tools entry.timestamp = pd.m_timestamp; entry.fee = pd.m_amount_in - pd.m_amount_out; entry.amount = pd.m_amount_in - pd.m_change - entry.fee; - entry.note = m_wallet.get_tx_note(txid); + entry.note = m_wallet->get_tx_note(txid); entry.type = is_failed ? "failed" : "pending"; } //------------------------------------------------------------------------------------------------------------------------------ @@ -267,18 +290,19 @@ namespace tools entry.timestamp = pd.m_timestamp; entry.amount = pd.m_amount; entry.fee = 0; // TODO - entry.note = m_wallet.get_tx_note(pd.m_tx_hash); + entry.note = m_wallet->get_tx_note(pd.m_tx_hash); entry.type = "pool"; } //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); try { - res.balance = m_wallet.balance(); - res.unlocked_balance = m_wallet.unlocked_balance(); + res.balance = m_wallet->balance(); + res.unlocked_balance = m_wallet->unlocked_balance(); } - catch (std::exception& e) + catch (const std::exception& e) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = e.what(); @@ -289,11 +313,12 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_getaddress(const wallet_rpc::COMMAND_RPC_GET_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); try { - res.address = m_wallet.get_account().get_public_address_str(m_wallet.testnet()); + res.address = m_wallet->get_account().get_public_address_str(m_wallet->testnet()); } - catch (std::exception& e) + catch (const std::exception& e) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = e.what(); @@ -304,11 +329,12 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_getheight(const wallet_rpc::COMMAND_RPC_GET_HEIGHT::request& req, wallet_rpc::COMMAND_RPC_GET_HEIGHT::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); try { - res.height = m_wallet.get_blockchain_current_height(); + res.height = m_wallet->get_blockchain_current_height(); } - catch (std::exception& e) + catch (const std::exception& e) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = e.what(); @@ -326,7 +352,7 @@ namespace tools cryptonote::tx_destination_entry de; bool has_payment_id; crypto::hash8 new_payment_id; - if(!get_account_integrated_address_from_str(de.addr, has_payment_id, new_payment_id, m_wallet.testnet(), it->address)) + if(!get_account_address_from_str_or_url(de.addr, has_payment_id, new_payment_id, m_wallet->testnet(), it->address, false)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + it->address; @@ -397,7 +423,8 @@ namespace tools std::vector<uint8_t> extra; LOG_PRINT_L3("on_transfer_split starts"); - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -413,11 +440,11 @@ namespace tools try { uint64_t mixin = req.mixin; - if (mixin < 2 && m_wallet.use_fork_rules(2, 10)) { + if (mixin < 2 && m_wallet->use_fork_rules(2, 10)) { LOG_PRINT_L1("Requested mixin " << req.mixin << " too low for hard fork 2, using 2"); mixin = 2; } - std::vector<wallet2::pending_tx> ptx_vector = m_wallet.create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, req.trusted_daemon); + std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, m_trusted_daemon); // reject proposed transactions if there are more than one. see on_transfer_split below. if (ptx_vector.size() != 1) @@ -427,7 +454,7 @@ namespace tools return false; } - m_wallet.commit_tx(ptx_vector); + m_wallet->commit_tx(ptx_vector); // populate response with tx hash res.tx_hash = epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx_vector.back().tx)); @@ -465,7 +492,8 @@ namespace tools std::vector<cryptonote::tx_destination_entry> dsts; std::vector<uint8_t> extra; - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -481,17 +509,17 @@ namespace tools try { uint64_t mixin = req.mixin; - if (mixin < 2 && m_wallet.use_fork_rules(2, 10)) { + if (mixin < 2 && m_wallet->use_fork_rules(2, 10)) { LOG_PRINT_L1("Requested mixin " << req.mixin << " too low for hard fork 2, using 2"); mixin = 2; } std::vector<wallet2::pending_tx> ptx_vector; LOG_PRINT_L2("on_transfer_split calling create_transactions_2"); - ptx_vector = m_wallet.create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, req.trusted_daemon); + ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, m_trusted_daemon); LOG_PRINT_L2("on_transfer_split called create_transactions_2"); LOG_PRINT_L2("on_transfer_split calling commit_txyy"); - m_wallet.commit_tx(ptx_vector); + m_wallet->commit_tx(ptx_vector); LOG_PRINT_L2("on_transfer_split called commit_txyy"); // populate response with tx hashes @@ -530,7 +558,8 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_sweep_dust(const wallet_rpc::COMMAND_RPC_SWEEP_DUST::request& req, wallet_rpc::COMMAND_RPC_SWEEP_DUST::response& res, epee::json_rpc::error& er) { - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -539,9 +568,9 @@ namespace tools try { - std::vector<wallet2::pending_tx> ptx_vector = m_wallet.create_unmixable_sweep_transactions(req.trusted_daemon); + std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_unmixable_sweep_transactions(m_trusted_daemon); - m_wallet.commit_tx(ptx_vector); + m_wallet->commit_tx(ptx_vector); // populate response with tx hashes for (auto & ptx : ptx_vector) @@ -582,7 +611,8 @@ namespace tools std::vector<cryptonote::tx_destination_entry> dsts; std::vector<uint8_t> extra; - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -601,9 +631,9 @@ namespace tools try { - std::vector<wallet2::pending_tx> ptx_vector = m_wallet.create_transactions_all(dsts[0].addr, req.mixin, req.unlock_time, req.priority, extra, req.trusted_daemon); + std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, req.mixin, req.unlock_time, req.priority, extra, m_trusted_daemon); - m_wallet.commit_tx(ptx_vector); + m_wallet->commit_tx(ptx_vector); // populate response with tx hashes for (auto & ptx : ptx_vector) @@ -641,6 +671,7 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_make_integrated_address(const wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); try { crypto::hash8 payment_id; @@ -658,11 +689,11 @@ namespace tools } } - res.integrated_address = m_wallet.get_account().get_public_integrated_address_str(payment_id, m_wallet.testnet()); + res.integrated_address = m_wallet->get_account().get_public_integrated_address_str(payment_id, m_wallet->testnet()); res.payment_id = epee::string_tools::pod_to_hex(payment_id); return true; } - catch (std::exception &e) + catch (const std::exception &e) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = e.what(); @@ -673,13 +704,14 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_split_integrated_address(const wallet_rpc::COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); try { cryptonote::account_public_address address; crypto::hash8 payment_id; bool has_payment_id; - if(!get_account_integrated_address_from_str(address, has_payment_id, payment_id, m_wallet.testnet(), req.integrated_address)) + if(!get_account_integrated_address_from_str(address, has_payment_id, payment_id, m_wallet->testnet(), req.integrated_address)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; er.message = "Invalid address"; @@ -691,11 +723,11 @@ namespace tools er.message = "Address is not an integrated address"; return false; } - res.standard_address = get_account_address_as_str(m_wallet.testnet(),address); + res.standard_address = get_account_address_as_str(m_wallet->testnet(),address); res.payment_id = epee::string_tools::pod_to_hex(payment_id); return true; } - catch (std::exception &e) + catch (const std::exception &e) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = e.what(); @@ -706,7 +738,8 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er) { - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -715,9 +748,9 @@ namespace tools try { - m_wallet.store(); + m_wallet->store(); } - catch (std::exception& e) + catch (const std::exception& e) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = e.what(); @@ -728,6 +761,7 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); crypto::hash payment_id; crypto::hash8 payment_id8; cryptonote::blobdata payment_id_blob; @@ -757,7 +791,7 @@ namespace tools res.payments.clear(); std::list<wallet2::payment_details> payment_list; - m_wallet.get_payments(payment_id, payment_list); + m_wallet->get_payments(payment_id, payment_list); for (auto & payment : payment_list) { wallet_rpc::payment_details rpc_payment; @@ -775,12 +809,13 @@ namespace tools bool wallet_rpc_server::on_get_bulk_payments(const wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::response& res, epee::json_rpc::error& er) { res.payments.clear(); + if (!m_wallet) return not_open(er); /* If the payment ID list is empty, we get payments to any payment ID (or lack thereof) */ if (req.payment_ids.empty()) { std::list<std::pair<crypto::hash,wallet2::payment_details>> payment_list; - m_wallet.get_payments(payment_list, req.min_block_height); + m_wallet->get_payments(payment_list, req.min_block_height); for (auto & payment : payment_list) { @@ -829,7 +864,7 @@ namespace tools } std::list<wallet2::payment_details> payment_list; - m_wallet.get_payments(payment_id, payment_list, req.min_block_height); + m_wallet->get_payments(payment_id, payment_list, req.min_block_height); for (auto & payment : payment_list) { @@ -848,6 +883,7 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_incoming_transfers(const wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); if(req.transfer_type.compare("all") != 0 && req.transfer_type.compare("available") != 0 && req.transfer_type.compare("unavailable") != 0) { er.code = WALLET_RPC_ERROR_CODE_TRANSFER_TYPE; @@ -869,7 +905,7 @@ namespace tools } wallet2::transfer_container transfers; - m_wallet.get_transfers(transfers); + m_wallet->get_transfers(transfers); bool transfers_found = false; for (const auto& td : transfers) @@ -896,7 +932,8 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_query_key(const wallet_rpc::COMMAND_RPC_QUERY_KEY::request& req, wallet_rpc::COMMAND_RPC_QUERY_KEY::response& res, epee::json_rpc::error& er) { - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -905,7 +942,7 @@ namespace tools if (req.key_type.compare("mnemonic") == 0) { - if (!m_wallet.get_seed(res.key)) + if (!m_wallet->get_seed(res.key)) { er.message = "The wallet is non-deterministic. Cannot display seed."; return false; @@ -913,7 +950,7 @@ namespace tools } else if(req.key_type.compare("view_key") == 0) { - res.key = string_tools::pod_to_hex(m_wallet.get_account().get_keys().m_view_secret_key); + res.key = string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key); } else { @@ -926,7 +963,8 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_rescan_blockchain(const wallet_rpc::COMMAND_RPC_RESCAN_BLOCKCHAIN::request& req, wallet_rpc::COMMAND_RPC_RESCAN_BLOCKCHAIN::response& res, epee::json_rpc::error& er) { - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -935,9 +973,9 @@ namespace tools try { - m_wallet.rescan_blockchain(); + m_wallet->rescan_blockchain(); } - catch (std::exception& e) + catch (const std::exception& e) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = e.what(); @@ -948,20 +986,22 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_sign(const wallet_rpc::COMMAND_RPC_SIGN::request& req, wallet_rpc::COMMAND_RPC_SIGN::response& res, epee::json_rpc::error& er) { - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; return false; } - res.signature = m_wallet.sign(req.data); + res.signature = m_wallet->sign(req.data); return true; } //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_verify(const wallet_rpc::COMMAND_RPC_VERIFY::request& req, wallet_rpc::COMMAND_RPC_VERIFY::response& res, epee::json_rpc::error& er) { - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -971,20 +1011,21 @@ namespace tools cryptonote::account_public_address address; bool has_payment_id; crypto::hash8 payment_id; - if(!get_account_integrated_address_from_str(address, has_payment_id, payment_id, m_wallet.testnet(), req.address)) + if(!get_account_address_from_str_or_url(address, has_payment_id, payment_id, m_wallet->testnet(), req.address, false)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; er.message = ""; return false; } - res.good = m_wallet.verify(req.data, address, req.signature); + res.good = m_wallet->verify(req.data, address, req.signature); return true; } //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_stop_wallet(const wallet_rpc::COMMAND_RPC_STOP_WALLET::request& req, wallet_rpc::COMMAND_RPC_STOP_WALLET::response& res, epee::json_rpc::error& er) { - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -993,10 +1034,10 @@ namespace tools try { - m_wallet.store(); + m_wallet->store(); m_stop.store(true, std::memory_order_relaxed); } - catch (std::exception& e) + catch (const std::exception& e) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = e.what(); @@ -1007,6 +1048,14 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_set_tx_notes(const wallet_rpc::COMMAND_RPC_SET_TX_NOTES::request& req, wallet_rpc::COMMAND_RPC_SET_TX_NOTES::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) + { + er.code = WALLET_RPC_ERROR_CODE_DENIED; + er.message = "Command unavailable in restricted mode."; + return false; + } + if (req.txids.size() != req.notes.size()) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; @@ -1019,7 +1068,7 @@ namespace tools while (i != req.txids.end()) { cryptonote::blobdata txid_blob; - if(!epee::string_tools::parse_hexstr_to_binbuff(*i++, txid_blob)) + if(!epee::string_tools::parse_hexstr_to_binbuff(*i++, txid_blob) || txid_blob.size() != sizeof(crypto::hash)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID; er.message = "TX ID has invalid format"; @@ -1034,7 +1083,7 @@ namespace tools std::list<std::string>::const_iterator in = req.notes.begin(); while (il != txids.end()) { - m_wallet.set_tx_note(*il++, *in++); + m_wallet->set_tx_note(*il++, *in++); } return true; @@ -1043,13 +1092,14 @@ namespace tools bool wallet_rpc_server::on_get_tx_notes(const wallet_rpc::COMMAND_RPC_GET_TX_NOTES::request& req, wallet_rpc::COMMAND_RPC_GET_TX_NOTES::response& res, epee::json_rpc::error& er) { res.notes.clear(); + if (!m_wallet) return not_open(er); std::list<crypto::hash> txids; std::list<std::string>::const_iterator i = req.txids.begin(); while (i != req.txids.end()) { cryptonote::blobdata txid_blob; - if(!epee::string_tools::parse_hexstr_to_binbuff(*i++, txid_blob)) + if(!epee::string_tools::parse_hexstr_to_binbuff(*i++, txid_blob) || txid_blob.size() != sizeof(crypto::hash)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID; er.message = "TX ID has invalid format"; @@ -1063,14 +1113,15 @@ namespace tools std::list<crypto::hash>::const_iterator il = txids.begin(); while (il != txids.end()) { - res.notes.push_back(m_wallet.get_tx_note(*il++)); + res.notes.push_back(m_wallet->get_tx_note(*il++)); } return true; } //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFERS::response& res, epee::json_rpc::error& er) { - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -1087,7 +1138,7 @@ namespace tools if (req.in) { std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments; - m_wallet.get_payments(payments, min_height, max_height); + m_wallet->get_payments(payments, min_height, max_height); for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) { res.in.push_back(wallet_rpc::transfer_entry()); fill_transfer_entry(res.in.back(), i->second.m_tx_hash, i->first, i->second); @@ -1097,7 +1148,7 @@ namespace tools if (req.out) { std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments; - m_wallet.get_payments_out(payments, min_height, max_height); + m_wallet->get_payments_out(payments, min_height, max_height); for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) { res.out.push_back(wallet_rpc::transfer_entry()); fill_transfer_entry(res.out.back(), i->first, i->second); @@ -1106,7 +1157,7 @@ namespace tools if (req.pending || req.failed) { std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> upayments; - m_wallet.get_unconfirmed_payments_out(upayments); + m_wallet->get_unconfirmed_payments_out(upayments); for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) { const tools::wallet2::unconfirmed_transfer_details &pd = i->second; bool is_failed = pd.m_state == tools::wallet2::unconfirmed_transfer_details::failed; @@ -1120,10 +1171,10 @@ namespace tools if (req.pool) { - m_wallet.update_pool_state(); + m_wallet->update_pool_state(); std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments; - m_wallet.get_unconfirmed_payments(payments); + m_wallet->get_unconfirmed_payments(payments); for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) { res.pool.push_back(wallet_rpc::transfer_entry()); fill_transfer_entry(res.pool.back(), i->first, i->second); @@ -1135,7 +1186,8 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_get_transfer_by_txid(const wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID::response& res, epee::json_rpc::error& er) { - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -1163,9 +1215,9 @@ namespace tools } std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments; - m_wallet.get_payments(payments, 0); + m_wallet->get_payments(payments, 0); for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) { - if (i->first == txid) + if (i->second.m_tx_hash == txid) { fill_transfer_entry(res.transfer, i->second.m_tx_hash, i->first, i->second); return true; @@ -1173,7 +1225,7 @@ namespace tools } std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments_out; - m_wallet.get_payments_out(payments_out, 0); + m_wallet->get_payments_out(payments_out, 0); for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments_out.begin(); i != payments_out.end(); ++i) { if (i->first == txid) { @@ -1183,7 +1235,7 @@ namespace tools } std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> upayments; - m_wallet.get_unconfirmed_payments_out(upayments); + m_wallet->get_unconfirmed_payments_out(upayments); for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) { if (i->first == txid) { @@ -1192,10 +1244,10 @@ namespace tools } } - m_wallet.update_pool_state(); + m_wallet->update_pool_state(); std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> pool_payments; - m_wallet.get_unconfirmed_payments(pool_payments); + m_wallet->get_unconfirmed_payments(pool_payments); for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) { if (i->second.m_tx_hash == txid) { @@ -1211,9 +1263,10 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_export_key_images(const wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); try { - std::vector<std::pair<crypto::key_image, crypto::signature>> ski = m_wallet.export_key_images(); + std::vector<std::pair<crypto::key_image, crypto::signature>> ski = m_wallet->export_key_images(); res.signed_key_images.resize(ski.size()); for (size_t n = 0; n < ski.size(); ++n) { @@ -1234,6 +1287,14 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_import_key_images(const wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) + { + er.code = WALLET_RPC_ERROR_CODE_DENIED; + er.message = "Command unavailable in restricted mode."; + return false; + } + try { std::vector<std::pair<crypto::key_image, crypto::signature>> ski; @@ -1242,7 +1303,7 @@ namespace tools { cryptonote::blobdata bd; - if(!epee::string_tools::parse_hexstr_to_binbuff(req.signed_key_images[n].key_image, bd)) + if(!epee::string_tools::parse_hexstr_to_binbuff(req.signed_key_images[n].key_image, bd) || bd.size() != sizeof(crypto::key_image)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE; er.message = "failed to parse key image"; @@ -1250,7 +1311,7 @@ namespace tools } ski[n].first = *reinterpret_cast<const crypto::key_image*>(bd.data()); - if(!epee::string_tools::parse_hexstr_to_binbuff(req.signed_key_images[n].signature, bd)) + if(!epee::string_tools::parse_hexstr_to_binbuff(req.signed_key_images[n].signature, bd) || bd.size() != sizeof(crypto::signature)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_SIGNATURE; er.message = "failed to parse signature"; @@ -1259,7 +1320,7 @@ namespace tools ski[n].second = *reinterpret_cast<const crypto::signature*>(bd.data()); } uint64_t spent = 0, unspent = 0; - uint64_t height = m_wallet.import_key_images(ski, spent, unspent); + uint64_t height = m_wallet->import_key_images(ski, spent, unspent); res.spent = spent; res.unspent = unspent; res.height = height; @@ -1278,7 +1339,7 @@ namespace tools bool wallet_rpc_server::on_make_uri(const wallet_rpc::COMMAND_RPC_MAKE_URI::request& req, wallet_rpc::COMMAND_RPC_MAKE_URI::response& res, epee::json_rpc::error& er) { std::string error; - std::string uri = m_wallet.make_uri(req.address, req.payment_id, req.amount, req.tx_description, req.recipient_name, error); + std::string uri = m_wallet->make_uri(req.address, req.payment_id, req.amount, req.tx_description, req.recipient_name, error); if (uri.empty()) { er.code = WALLET_RPC_ERROR_CODE_WRONG_URI; @@ -1292,8 +1353,9 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_parse_uri(const wallet_rpc::COMMAND_RPC_PARSE_URI::request& req, wallet_rpc::COMMAND_RPC_PARSE_URI::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); std::string error; - if (!m_wallet.parse_uri(req.uri, res.uri.address, res.uri.payment_id, res.uri.amount, res.uri.tx_description, res.uri.recipient_name, res.unknown_parameters, error)) + if (!m_wallet->parse_uri(req.uri, res.uri.address, res.uri.payment_id, res.uri.amount, res.uri.tx_description, res.uri.recipient_name, res.unknown_parameters, error)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_URI; er.message = "Error parsing URI: " + error; @@ -1304,12 +1366,13 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_get_address_book(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er) { - const auto ab = m_wallet.get_address_book(); + if (!m_wallet) return not_open(er); + const auto ab = m_wallet->get_address_book(); if (req.entries.empty()) { uint64_t idx = 0; for (const auto &entry: ab) - res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx++, get_account_address_as_str(m_wallet.testnet(), entry.m_address), epee::string_tools::pod_to_hex(entry.m_payment_id), entry.m_description}); + res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx++, get_account_address_as_str(m_wallet->testnet(), entry.m_address), epee::string_tools::pod_to_hex(entry.m_payment_id), entry.m_description}); } else { @@ -1322,7 +1385,7 @@ namespace tools return false; } const auto &entry = ab[idx]; - res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx, get_account_address_as_str(m_wallet.testnet(), entry.m_address), epee::string_tools::pod_to_hex(entry.m_payment_id), entry.m_description}); + res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx, get_account_address_as_str(m_wallet->testnet(), entry.m_address), epee::string_tools::pod_to_hex(entry.m_payment_id), entry.m_description}); } } return true; @@ -1330,11 +1393,19 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_add_address_book(const wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) + { + er.code = WALLET_RPC_ERROR_CODE_DENIED; + er.message = "Command unavailable in restricted mode."; + return false; + } + cryptonote::account_public_address address; bool has_payment_id; crypto::hash8 payment_id8; crypto::hash payment_id = cryptonote::null_hash; - if(!get_account_integrated_address_from_str(address, has_payment_id, payment_id8, m_wallet.testnet(), req.address)) + if(!get_account_address_from_str_or_url(address, has_payment_id, payment_id8, m_wallet->testnet(), req.address, false)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + req.address; @@ -1372,26 +1443,34 @@ namespace tools } } } - if (!m_wallet.add_address_book_row(address, payment_id, req.description)) + if (!m_wallet->add_address_book_row(address, payment_id, req.description)) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = "Failed to add address book entry"; return false; } - res.index = m_wallet.get_address_book().size(); + res.index = m_wallet->get_address_book().size(); return true; } //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_delete_address_book(const wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er) { - const auto ab = m_wallet.get_address_book(); + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) + { + er.code = WALLET_RPC_ERROR_CODE_DENIED; + er.message = "Command unavailable in restricted mode."; + return false; + } + + const auto ab = m_wallet->get_address_book(); if (req.index >= ab.size()) { er.code = WALLET_RPC_ERROR_CODE_WRONG_INDEX; er.message = "Index out of range: " + std::to_string(req.index); return false; } - if (!m_wallet.delete_address_book_row(req.index)) + if (!m_wallet->delete_address_book_row(req.index)) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = "Failed to delete address book entry"; @@ -1400,6 +1479,224 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_rescan_spent(const wallet_rpc::COMMAND_RPC_RESCAN_SPENT::request& req, wallet_rpc::COMMAND_RPC_RESCAN_SPENT::response& res, epee::json_rpc::error& er) + { + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) + { + er.code = WALLET_RPC_ERROR_CODE_DENIED; + er.message = "Command unavailable in restricted mode."; + return false; + } + try + { + m_wallet->rescan_spent(); + return true; + } + catch (const std::exception &e) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = e.what(); + return false; + } + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_start_mining(const wallet_rpc::COMMAND_RPC_START_MINING::request& req, wallet_rpc::COMMAND_RPC_START_MINING::response& res, epee::json_rpc::error& er) + { + if (!m_wallet) return not_open(er); + if (!m_trusted_daemon) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "This command requires a trusted daemon."; + return false; + } + + size_t max_mining_threads_count = (std::max)(tools::get_max_concurrency(), static_cast<unsigned>(2)); + if (req.threads_count < 1 || max_mining_threads_count < req.threads_count) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "The specified number of threads is inappropriate."; + return false; + } + + cryptonote::COMMAND_RPC_START_MINING::request daemon_req = AUTO_VAL_INIT(daemon_req); + daemon_req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->testnet()); + daemon_req.threads_count = req.threads_count; + daemon_req.do_background_mining = req.do_background_mining; + daemon_req.ignore_battery = req.ignore_battery; + + cryptonote::COMMAND_RPC_START_MINING::response daemon_res; + bool r = net_utils::invoke_http_json("/start_mining", daemon_req, daemon_res, m_http_client); + if (!r || daemon_res.status != CORE_RPC_STATUS_OK) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Couldn't start mining due to unknown error."; + return false; + } + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_stop_mining(const wallet_rpc::COMMAND_RPC_STOP_MINING::request& req, wallet_rpc::COMMAND_RPC_STOP_MINING::response& res, epee::json_rpc::error& er) + { + cryptonote::COMMAND_RPC_STOP_MINING::request daemon_req; + cryptonote::COMMAND_RPC_STOP_MINING::response daemon_res; + bool r = net_utils::invoke_http_json("/stop_mining", daemon_req, daemon_res, m_http_client); + if (!r || daemon_res.status != CORE_RPC_STATUS_OK) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Couldn't stop mining due to unknown error."; + return false; + } + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_get_languages(const wallet_rpc::COMMAND_RPC_GET_LANGUAGES::request& req, wallet_rpc::COMMAND_RPC_GET_LANGUAGES::response& res, epee::json_rpc::error& er) + { + crypto::ElectrumWords::get_language_list(res.languages); + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_create_wallet(const wallet_rpc::COMMAND_RPC_CREATE_WALLET::request& req, wallet_rpc::COMMAND_RPC_CREATE_WALLET::response& res, epee::json_rpc::error& er) + { + if (m_wallet_dir.empty()) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "No wallet dir configured"; + return false; + } + + namespace po = boost::program_options; + po::variables_map vm2; + const char *ptr = strchr(req.filename.c_str(), '/'); +#ifdef _WIN32 + if (!ptr) + ptr = strchr(req.filename.c_str(), '\\'); + if (!ptr) + ptr = strchr(req.filename.c_str(), ':'); +#endif + if (ptr) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Invalid filename"; + return false; + } + std::string wallet_file = m_wallet_dir + "/" + req.filename; + { + std::vector<std::string> languages; + crypto::ElectrumWords::get_language_list(languages); + std::vector<std::string>::iterator it; + std::string wallet_file; + char *ptr; + + it = std::find(languages.begin(), languages.end(), req.language); + if (it == languages.end()) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Unknown language"; + return false; + } + } + { + po::options_description desc("dummy"); + const command_line::arg_descriptor<std::string, true> arg_password = {"password", "password"}; + const char *argv[4]; + int argc = 3; + argv[0] = "wallet-rpc"; + argv[1] = "--password"; + argv[2] = req.password.c_str(); + argv[3] = NULL; + vm2 = *m_vm; + command_line::add_arg(desc, arg_password); + po::store(po::parse_command_line(argc, argv, desc), vm2); + } + std::unique_ptr<tools::wallet2> wal = tools::wallet2::make_new(vm2).first; + if (!wal) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Failed to create wallet"; + return false; + } + wal->set_seed_language(req.language); + cryptonote::COMMAND_RPC_GET_HEIGHT::request hreq; + cryptonote::COMMAND_RPC_GET_HEIGHT::response hres; + hres.height = 0; + bool r = net_utils::invoke_http_json("/getheight", hreq, hres, m_http_client); + wal->set_refresh_from_block_height(hres.height); + crypto::secret_key dummy_key; + try { + wal->generate(wallet_file, req.password, dummy_key, false, false); + } + catch (const std::exception& e) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Failed to generate wallet"; + return false; + } + if (m_wallet) + delete m_wallet; + m_wallet = wal.release(); + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_open_wallet(const wallet_rpc::COMMAND_RPC_OPEN_WALLET::request& req, wallet_rpc::COMMAND_RPC_OPEN_WALLET::response& res, epee::json_rpc::error& er) + { + if (m_wallet_dir.empty()) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "No wallet dir configured"; + return false; + } + + namespace po = boost::program_options; + po::variables_map vm2; + const char *ptr = strchr(req.filename.c_str(), '/'); +#ifdef _WIN32 + if (!ptr) + ptr = strchr(req.filename.c_str(), '\\'); + if (!ptr) + ptr = strchr(req.filename.c_str(), ':'); +#endif + if (ptr) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Invalid filename"; + return false; + } + std::string wallet_file = m_wallet_dir + "/" + req.filename; + { + po::options_description desc("dummy"); + const command_line::arg_descriptor<std::string, true> arg_password = {"password", "password"}; + const char *argv[4]; + int argc = 3; + argv[0] = "wallet-rpc"; + argv[1] = "--password"; + argv[2] = req.password.c_str(); + argv[3] = NULL; + vm2 = *m_vm; + command_line::add_arg(desc, arg_password); + po::store(po::parse_command_line(argc, argv, desc), vm2); + } + std::unique_ptr<tools::wallet2> wal; + try { + wal = tools::wallet2::make_from_file(vm2, wallet_file).first; + } + catch (const std::exception& e) + { + wal = nullptr; + } + if (!wal) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Failed to open wallet"; + return false; + } + if (m_wallet) + delete m_wallet; + m_wallet = wal.release(); + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ } int main(int argc, char** argv) { @@ -1410,19 +1707,21 @@ int main(int argc, char** argv) { po::options_description desc_params(wallet_args::tr("Wallet options")); tools::wallet2::init_options(desc_params); - command_line::add_arg(desc_params, arg_rpc_bind_ip); command_line::add_arg(desc_params, arg_rpc_bind_port); - command_line::add_arg(desc_params, arg_rpc_login); command_line::add_arg(desc_params, arg_disable_rpc_login); - command_line::add_arg(desc_params, arg_confirm_external_bind); + command_line::add_arg(desc_params, arg_trusted_daemon); + cryptonote::rpc_args::init_options(desc_params); command_line::add_arg(desc_params, arg_wallet_file); command_line::add_arg(desc_params, arg_from_json); + command_line::add_arg(desc_params, arg_wallet_dir); + const auto vm = wallet_args::main( argc, argv, - "monero-wallet-rpc [--wallet-file=<file>|--generate-from-json=<file>] [--rpc-bind-port=<port>]", + "monero-wallet-rpc [--wallet-file=<file>|--generate-from-json=<file>|--wallet-dir=<directory>] [--rpc-bind-port=<port>]", desc_params, po::positional_options_description(), + "monero-wallet-rpc.log", true ); if (!vm) @@ -1435,6 +1734,7 @@ int main(int argc, char** argv) { { const auto wallet_file = command_line::get_arg(*vm, arg_wallet_file); const auto from_json = command_line::get_arg(*vm, arg_from_json); + const auto wallet_dir = command_line::get_arg(*vm, arg_wallet_dir); if(!wallet_file.empty() && !from_json.empty()) { @@ -1442,9 +1742,15 @@ int main(int argc, char** argv) { return 1; } + if (!wallet_dir.empty()) + { + wal = NULL; + goto just_dir; + } + if (wallet_file.empty() && from_json.empty()) { - LOG_ERROR(tools::wallet_rpc_server::tr("Must specify --wallet-file or --generate-from-json")); + LOG_ERROR(tools::wallet_rpc_server::tr("Must specify --wallet-file or --generate-from-json or --wallet-dir")); return 1; } @@ -1485,8 +1791,10 @@ int main(int argc, char** argv) { LOG_ERROR(tools::wallet_rpc_server::tr("Wallet initialization failed: ") << e.what()); return 1; } - tools::wallet_rpc_server wrpc(*wal); - bool r = wrpc.init(*vm); +just_dir: + tools::wallet_rpc_server wrpc; + if (wal) wrpc.set_wallet(wal.release()); + bool r = wrpc.init(&(vm.get())); CHECK_AND_ASSERT_MES(r, 1, tools::wallet_rpc_server::tr("Failed to initialize wallet rpc server")); tools::signal_handler::install([&wrpc, &wal](int) { wrpc.send_stop_signal(); @@ -1497,7 +1805,7 @@ int main(int argc, char** argv) { try { LOG_PRINT_L0(tools::wallet_rpc_server::tr("Storing wallet...")); - wal->store(); + wrpc.stop(); LOG_PRINT_L0(tools::wallet_rpc_server::tr("Stored ok")); } catch (const std::exception& e) diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 98e6e72ed..230dcee5b 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -52,11 +52,14 @@ namespace tools static const char* tr(const char* str); - wallet_rpc_server(wallet2& cr); + wallet_rpc_server(); ~wallet_rpc_server(); - bool init(const boost::program_options::variables_map& vm); + bool init(const boost::program_options::variables_map *vm); bool run(); + void stop(); + void set_wallet(wallet2 *cr); + private: CHAIN_HTTP_TO_MAP2(connection_context); //forward http requests to uri map @@ -92,6 +95,12 @@ namespace tools MAP_JON_RPC_WE("get_address_book", on_get_address_book, wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY) MAP_JON_RPC_WE("add_address_book", on_add_address_book, wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY) MAP_JON_RPC_WE("delete_address_book",on_delete_address_book,wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY) + MAP_JON_RPC_WE("rescan_spent", on_rescan_spent, wallet_rpc::COMMAND_RPC_RESCAN_SPENT) + MAP_JON_RPC_WE("start_mining", on_start_mining, wallet_rpc::COMMAND_RPC_START_MINING) + MAP_JON_RPC_WE("stop_mining", on_stop_mining, wallet_rpc::COMMAND_RPC_STOP_MINING) + MAP_JON_RPC_WE("get_languages", on_get_languages, wallet_rpc::COMMAND_RPC_GET_LANGUAGES) + MAP_JON_RPC_WE("create_wallet", on_create_wallet, wallet_rpc::COMMAND_RPC_CREATE_WALLET) + MAP_JON_RPC_WE("open_wallet", on_open_wallet, wallet_rpc::COMMAND_RPC_OPEN_WALLET) END_JSON_RPC_MAP() END_URI_MAP2() @@ -125,6 +134,12 @@ namespace tools bool on_get_address_book(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er); bool on_add_address_book(const wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er); bool on_delete_address_book(const wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er); + bool on_rescan_spent(const wallet_rpc::COMMAND_RPC_RESCAN_SPENT::request& req, wallet_rpc::COMMAND_RPC_RESCAN_SPENT::response& res, epee::json_rpc::error& er); + bool on_start_mining(const wallet_rpc::COMMAND_RPC_START_MINING::request& req, wallet_rpc::COMMAND_RPC_START_MINING::response& res, epee::json_rpc::error& er); + bool on_stop_mining(const wallet_rpc::COMMAND_RPC_STOP_MINING::request& req, wallet_rpc::COMMAND_RPC_STOP_MINING::response& res, epee::json_rpc::error& er); + bool on_get_languages(const wallet_rpc::COMMAND_RPC_GET_LANGUAGES::request& req, wallet_rpc::COMMAND_RPC_GET_LANGUAGES::response& res, epee::json_rpc::error& er); + bool on_create_wallet(const wallet_rpc::COMMAND_RPC_CREATE_WALLET::request& req, wallet_rpc::COMMAND_RPC_CREATE_WALLET::response& res, epee::json_rpc::error& er); + bool on_open_wallet(const wallet_rpc::COMMAND_RPC_OPEN_WALLET::request& req, wallet_rpc::COMMAND_RPC_OPEN_WALLET::response& res, epee::json_rpc::error& er); //json rpc v2 bool on_query_key(const wallet_rpc::COMMAND_RPC_QUERY_KEY::request& req, wallet_rpc::COMMAND_RPC_QUERY_KEY::response& res, epee::json_rpc::error& er); @@ -134,9 +149,14 @@ namespace tools void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::confirmed_transfer_details &pd); void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::unconfirmed_transfer_details &pd); void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &payment_id, const tools::wallet2::payment_details &pd); + bool not_open(epee::json_rpc::error& er); - wallet2& m_wallet; + wallet2 *m_wallet; + std::string m_wallet_dir; std::string rpc_login_filename; std::atomic<bool> m_stop; + bool m_trusted_daemon; + epee::net_utils::http::http_simple_client m_http_client; + const boost::program_options::variables_map *m_vm; }; } diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index e132b4e2b..3c10dc41f 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -30,7 +30,7 @@ #pragma once #include "cryptonote_protocol/cryptonote_protocol_defs.h" -#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_basic.h" #include "crypto/hash.h" #include "wallet_rpc_server_error_codes.h" @@ -119,7 +119,6 @@ namespace wallet_rpc uint64_t unlock_time; std::string payment_id; bool get_tx_key; - bool trusted_daemon; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(destinations) @@ -128,7 +127,6 @@ namespace wallet_rpc KV_SERIALIZE(unlock_time) KV_SERIALIZE(payment_id) KV_SERIALIZE(get_tx_key) - KV_SERIALIZE(trusted_daemon) END_KV_SERIALIZE_MAP() }; @@ -158,7 +156,6 @@ namespace wallet_rpc uint64_t unlock_time; std::string payment_id; bool get_tx_keys; - bool trusted_daemon; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(destinations) @@ -167,7 +164,6 @@ namespace wallet_rpc KV_SERIALIZE(unlock_time) KV_SERIALIZE(payment_id) KV_SERIALIZE(get_tx_keys) - KV_SERIALIZE(trusted_daemon) END_KV_SERIALIZE_MAP() }; @@ -199,11 +195,9 @@ namespace wallet_rpc struct request { bool get_tx_keys; - bool trusted_daemon; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(get_tx_keys) - KV_SERIALIZE(trusted_daemon) END_KV_SERIALIZE_MAP() }; @@ -240,7 +234,7 @@ namespace wallet_rpc uint64_t unlock_time; std::string payment_id; bool get_tx_keys; - bool trusted_daemon; + uint64_t below_amount; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(address) @@ -249,7 +243,7 @@ namespace wallet_rpc KV_SERIALIZE(unlock_time) KV_SERIALIZE(payment_id) KV_SERIALIZE(get_tx_keys) - KV_SERIALIZE(trusted_daemon) + KV_SERIALIZE(below_amount) END_KV_SERIALIZE_MAP() }; @@ -865,5 +859,113 @@ namespace wallet_rpc }; }; + struct COMMAND_RPC_RESCAN_SPENT + { + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_START_MINING + { + struct request + { + uint64_t threads_count; + bool do_background_mining; + bool ignore_battery; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(threads_count) + KV_SERIALIZE(do_background_mining) + KV_SERIALIZE(ignore_battery) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_STOP_MINING + { + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_GET_LANGUAGES + { + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + struct response + { + std::vector<std::string> languages; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(languages) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_CREATE_WALLET + { + struct request + { + std::string filename; + std::string password; + std::string language; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(filename) + KV_SERIALIZE(password) + KV_SERIALIZE(language) + END_KV_SERIALIZE_MAP() + }; + struct response + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_OPEN_WALLET + { + struct request + { + std::string filename; + std::string password; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(filename) + KV_SERIALIZE(password) + END_KV_SERIALIZE_MAP() + }; + struct response + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + }; } } diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h index 7b541c8f6..3c79c0ac3 100644 --- a/src/wallet/wallet_rpc_server_error_codes.h +++ b/src/wallet/wallet_rpc_server_error_codes.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -43,3 +43,4 @@ #define WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE -10 #define WALLET_RPC_ERROR_CODE_WRONG_URI -11 #define WALLET_RPC_ERROR_CODE_WRONG_INDEX -12 +#define WALLET_RPC_ERROR_CODE_NOT_OPEN -13 |