diff options
Diffstat (limited to 'src')
40 files changed, 1105 insertions, 370 deletions
diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp index 7ac046bc9..c61a81379 100644 --- a/src/blockchain_db/blockchain_db.cpp +++ b/src/blockchain_db/blockchain_db.cpp @@ -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 3fdb62a7b..fc1f98ce0 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -34,6 +34,7 @@ #include <string> #include <exception> #include "crypto/hash.h" +#include "cryptonote_protocol/blobdatatype.h" #include "cryptonote_basic/cryptonote_basic.h" #include "cryptonote_basic/difficulty.h" #include "cryptonote_basic/hardfork.h" @@ -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 + */ + 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; + 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; + 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 + */ + 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/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 0a35325e4..f6a4534fb 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -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); } @@ -702,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 = num_txs(); CURSOR(txs) CURSOR(tx_indices) @@ -735,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; } @@ -783,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, @@ -797,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; @@ -849,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; } @@ -934,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) @@ -1000,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")); } @@ -1155,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"); @@ -1270,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)) @@ -1304,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; } @@ -1354,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())); \ } \ @@ -1388,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())); \ } \ @@ -1429,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 @@ -1467,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(); @@ -1488,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 @@ -1714,6 +1741,34 @@ uint64_t BlockchainLMDB::height() const return db_stats.ms_entries; } +uint64_t BlockchainLMDB::num_txs() 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_txs, &db_stats))) + throw0(DB_ERROR(lmdb_error("Failed to query m_txs: ", result).c_str())); + 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__); @@ -1809,7 +1864,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(); @@ -1832,26 +1887,13 @@ 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__); @@ -2279,7 +2321,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; @@ -2398,12 +2440,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; } @@ -2436,12 +2478,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; } @@ -2467,7 +2509,7 @@ 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; @@ -2545,8 +2587,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); @@ -2557,8 +2597,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; } @@ -2573,8 +2611,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); @@ -2582,8 +2618,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; } @@ -2617,7 +2651,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); @@ -2635,7 +2669,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())); @@ -3257,8 +3298,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 { @@ -3329,12 +3368,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..5ea642d23 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -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,9 @@ private: virtual void remove_spent_key(const crypto::key_image& k_image); + uint64_t num_txs() const; + 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 +370,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/common/CMakeLists.txt b/src/common/CMakeLists.txt index c63d9d0ae..eb4d4c25d 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -30,12 +30,14 @@ 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) @@ -49,6 +51,7 @@ set(common_private_headers command_line.h common_fwd.h dns_utils.h + download.h http_connection.h int-util.h pod-class.h @@ -62,7 +65,8 @@ set(common_private_headers perf_timer.h stack_trace.h task_region.h - thread_group.h) + thread_group.h + updates.h) monero_private_headers(common ${common_private_headers}) @@ -74,6 +78,7 @@ target_link_libraries(common PUBLIC epee crypto + -lcrypto ${UNBOUND_LIBRARY} ${LIBUNWIND_LIBRARIES} ${Boost_DATE_TIME_LIBRARY} diff --git a/src/common/command_line.cpp b/src/common/command_line.cpp index c3df5c096..8739a93cd 100644 --- a/src/common/command_line.cpp +++ b/src/common/command_line.cpp @@ -120,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 a09365a6b..f10e68e13 100644 --- a/src/common/command_line.h +++ b/src/common/command_line.h @@ -218,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/dns_utils.cpp b/src/common/dns_utils.cpp index 5ff39574c..f7655e3c7 100644 --- a/src/common/dns_utils.cpp +++ b/src/common/dns_utils.cpp @@ -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; @@ -451,6 +452,107 @@ 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) +{ + 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 6ecf5595c..2e881f0e0 100644 --- a/src/common/dns_utils.h +++ b/src/common/dns_utils.h @@ -165,6 +165,8 @@ std::vector<std::string> addresses_from_url(const std::string& url, bool& dnssec std::string get_account_address_as_str_from_url(const std::string& url, bool& dnssec_valid); +bool load_txt_records_from_dns(std::vector<std::string> &records, const std::vector<std::string> &dns_urls); + } // namespace tools::dns_utils } // namespace tools diff --git a/src/common/download.cpp b/src/common/download.cpp new file mode 100644 index 000000000..c5ee797d0 --- /dev/null +++ b/src/common/download.cpp @@ -0,0 +1,138 @@ +// 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 <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 +{ + static bool download_thread(const std::string &path, const std::string &url) + { + try + { + MINFO("Downloading " << url << " to " << path); + std::ofstream f; + f.open(path, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc); + if (!f.good()) { + MERROR("Failed to open file " << path); + return false; + } + class download_client: public epee::net_utils::http::http_simple_client + { + public: + download_client(std::ofstream &f): f(f) {} + virtual ~download_client() { f.close(); } + virtual bool handle_target_data(std::string &piece_of_transfer) + { + try + { + f << piece_of_transfer; + return f.good(); + } + catch (const std::exception &e) + { + MERROR("Error writing data: " << e.what()); + return false; + } + } + private: + std::ofstream &f; + } client(f); + epee::net_utils::http::url_content u_c; + if (!epee::net_utils::parse_url(url, u_c)) + { + MWARNING("Failed to parse URL " << url); + return false; + } + if (u_c.host.empty()) + { + MWARNING("Failed to determine address from URL " << url); + return false; + } + 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))) + { + MERROR("Failed to connect to " << url); + return false; + } + 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)) + { + MERROR("Failed to connect to " << url); + client.disconnect(); + return false; + } + if (!info) + { + MERROR("Failed invoking GET command to " << url << ", no status info returned"); + client.disconnect(); + return false; + } + MDEBUG("response code: " << info->m_response_code); + 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) + { + MERROR("Status code " << info->m_response_code); + client.disconnect(); + return false; + } + client.disconnect(); + f.close(); + MDEBUG("Download complete"); + return true; + } + catch (const std::exception &e) + { + MERROR("Exception in download thread: " << e.what()); + return false; + } + } + + bool download(const std::string &path, const std::string &url) + { + bool success; + std::unique_ptr<boost::thread> thread(new boost::thread([&](){ success = download_thread(path, url); })); + thread->join(); + return success; + } +} diff --git a/src/common/download.h b/src/common/download.h new file mode 100644 index 000000000..ab7644689 --- /dev/null +++ b/src/common/download.h @@ -0,0 +1,36 @@ +// 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 download(const std::string &path, const std::string &url); +} diff --git a/src/common/updates.cpp b/src/common/updates.cpp new file mode 100644 index 000000000..936106881 --- /dev/null +++ b/src/common/updates.cpp @@ -0,0 +1,114 @@ +// 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, bool testnet, 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 = { + }; + + static const std::vector<std::string> testnet_dns_urls = { "testver.moneropulse.net" + }; + + if (!tools::dns_utils::load_txt_records_from_dns(records, testnet ? testnet_dns_urls : 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) + { + static const char base[] = "https://downloads.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..1a70e06fd --- /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, bool bestnet, 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); +} diff --git a/src/common/util.cpp b/src/common/util.cpp index bfcf86bc6..90748ddb1 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -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,53 @@ 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 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..8ab469129 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -183,4 +183,7 @@ 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 std::string &filename, crypto::hash &hash); } diff --git a/src/cryptonote_basic/checkpoints.cpp b/src/cryptonote_basic/checkpoints.cpp index 3cf804ede..1e7754886 100644 --- a/src/cryptonote_basic/checkpoints.cpp +++ b/src/cryptonote_basic/checkpoints.cpp @@ -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_basic/hardfork.cpp b/src/cryptonote_basic/hardfork.cpp index 9b2434970..33188e66d 100644 --- a/src/cryptonote_basic/hardfork.cpp +++ b/src/cryptonote_basic/hardfork.cpp @@ -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 diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp index 117c81878..70389f917 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -41,6 +41,7 @@ #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" @@ -61,6 +62,7 @@ namespace cryptonote 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}; @@ -181,6 +183,7 @@ namespace cryptonote 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); @@ -230,6 +233,8 @@ namespace cryptonote // 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)) @@ -254,7 +259,7 @@ namespace cryptonote 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 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); @@ -278,6 +283,7 @@ namespace cryptonote 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++) { @@ -469,6 +475,11 @@ namespace cryptonote 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 @@ -482,6 +493,11 @@ namespace cryptonote 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; @@ -564,10 +580,9 @@ namespace cryptonote // If we're already mining, then sleep for the miner monitor interval. // If we're NOT mining, then sleep for the idle monitor interval - boost::this_thread::sleep_for( - m_is_background_mining_started ? - boost::chrono::seconds( BACKGROUND_MINING_MINER_MONITOR_INVERVAL_IN_SECONDS ) : - boost::chrono::seconds( get_min_idle_seconds() ) ); + 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&) { @@ -575,7 +590,21 @@ namespace cryptonote continue; // if interrupted because stop called, loop should end .. } - bool on_ac_power = !on_battery_power(); + 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 ) { @@ -763,45 +792,54 @@ namespace cryptonote return (uint8_t)( ceil( (other * 1.f / total * 1.f) * 100) ); } //----------------------------------------------------------------------------------------------------- - bool miner::on_battery_power() + boost::logic::tribool miner::on_battery_power() { #ifdef _WIN32 SYSTEM_POWER_STATUS power_status; if ( GetSystemPowerStatus( &power_status ) != 0 ) { - return power_status.ACLineStatus != 1; + 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 - const std::string POWER_SUPPLY_STATUS_PATH = "/sys/class/power_supply/ACAD/online"; - - if( !epee::file_io_utils::is_file_exist(POWER_SUPPLY_STATUS_PATH) ) + std::string power_supply_path = ""; + const std::string POWER_SUPPLY_STATUS_PATHS[] = { - LOG_ERROR("'" << POWER_SUPPLY_STATUS_PATH << "' file does not exist, can't determine if on AC power"); - return false; + "/sys/class/power_supply/ACAD/online", + "/sys/class/power_supply/AC/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_STATUS_PATH); + std::ifstream power_stream(power_supply_path); if( power_stream.fail() ) { - LOG_ERROR("failed to open '" << POWER_SUPPLY_STATUS_PATH << "'"); - return false; + LOG_ERROR("failed to open '" << power_supply_path << "'"); + return boost::logic::tribool(boost::logic::indeterminate);; } - return power_stream.get() != '1'; + return boost::logic::tribool( (power_stream.get() != '1') ); #endif LOG_ERROR("couldn't query power status"); - return false; // shouldn't get here unless no support for querying battery status - // TODO: return enum with ability to signify failure in querying for power status - // and change bg-mining logic so that it stops. As @vtnerd states, with the current - // setup "If someone enabled background mining on a system that fails to grab ac - // status, it will just continually check with little hope of ever being resolved - // automagically". This is also the case for time/idle stats functions. + return boost::logic::tribool(boost::logic::indeterminate); } } diff --git a/src/cryptonote_basic/miner.h b/src/cryptonote_basic/miner.h index a66083ead..02987b9d9 100644 --- a/src/cryptonote_basic/miner.h +++ b/src/cryptonote_basic/miner.h @@ -31,6 +31,7 @@ #pragma once #include <boost/program_options.hpp> +#include <boost/logic/tribool_fwd.hpp> #include <atomic> #include "cryptonote_basic.h" #include "difficulty.h" @@ -67,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 do_background = false); + 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(); @@ -82,6 +83,7 @@ namespace cryptonote 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; @@ -149,8 +151,10 @@ namespace cryptonote // 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; @@ -164,6 +168,6 @@ namespace cryptonote 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 bool on_battery_power(); + static boost::logic::tribool on_battery_power(); }; } diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index a81286632..f10e14b83 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1431,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); @@ -1443,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); @@ -1462,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; } @@ -1480,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 ); @@ -1510,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; @@ -1889,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) { @@ -1906,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); @@ -1915,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); @@ -1929,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__); @@ -1986,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; } //------------------------------------------------------------------ @@ -1999,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); @@ -2022,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; } //------------------------------------------------------------------ @@ -3540,7 +3587,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) { @@ -4011,3 +4058,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 c806c3505..7315ea735 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -153,7 +153,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 +164,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 @@ -407,7 +407,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 @@ -621,9 +621,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/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index b940dba3c..fc34b3adc 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -35,6 +35,8 @@ 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" @@ -148,6 +150,7 @@ 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); } //----------------------------------------------------------------------------------------------- bool core::handle_command_line(const boost::program_options::variables_map& vm) @@ -195,62 +198,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 +261,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 +270,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 +399,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 +435,6 @@ namespace cryptonote m_miner.stop(); m_mempool.deinit(); m_blockchain_storage.deinit(); - unlock_db_directory(); return true; } //----------------------------------------------------------------------------------------------- @@ -639,6 +633,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 +722,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); @@ -799,7 +811,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); } @@ -867,8 +879,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 +892,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); } @@ -1040,6 +1052,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 +1080,66 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- + bool core::check_updates() + { + static const char software[] = "monerod"; + 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; + MDEBUG("Checking for a new " << software << " version for " << buildtag); + if (!tools::check_updates(software, buildtag, m_testnet, 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); + MGINFO("Version " << version << " of " << software << " for " << buildtag << " is available: " << url << ", SHA256 hash " << hash); + + if (check_updates_level == UPDATES_NOTIFY) + return true; + + 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; + if (!tools::download(path.string(), url)) + { + MERROR("Failed to download " << url); + return false; + } + crypto::hash file_hash; + 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 " << url << " does not match the expected hash"); + return false; + } + MGINFO("New version downloaded to " << path); + + if (check_updates_level == UPDATES_DOWNLOAD) + return true; + + MERROR("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 fe1d11916..93604bf94 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -287,16 +287,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 +330,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; /** @@ -431,11 +445,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 +759,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 +788,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 + * @brief checks DNS versions * - * @note see lock_db_directory() - * - * @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,8 +814,9 @@ 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? @@ -826,6 +840,13 @@ namespace cryptonote time_t start_time; std::unordered_set<crypto::hash> bad_semantics_txes; + + enum { + UPDATES_DISABLED, + UPDATES_NOTIFY, + UPDATES_DOWNLOAD, + UPDATES_UPDATE, + } check_updates_level; }; } diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 42e612e2f..8beb390fa 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -339,7 +339,7 @@ namespace cryptonote 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; + 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++; } diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index f48be1ff1..baceb1cb2 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -619,7 +619,11 @@ namespace cryptonote get_block_reward(median_size, total_size, already_generated_coins, best_coinbase, version); +#if 1 + size_t max_total_size = (130 * median_size) / 100 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; +#else size_t max_total_size = 2 * median_size - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; +#endif 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"); @@ -637,6 +641,15 @@ namespace cryptonote continue; } +#if 1 + // 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; + } +#else // If we're getting lower coinbase tx, // stop including more tx uint64_t block_reward; @@ -653,6 +666,7 @@ namespace cryptonote sorted_it++; continue; } +#endif // Skip transactions that are not ready to be // included into the blockchain or that are @@ -667,7 +681,9 @@ namespace cryptonote bl.tx_hashes.push_back(tx_it->first); total_size += tx_it->second.blob_size; fee += tx_it->second.fee; +#if 0 best_coinbase = coinbase; +#endif append_key_images(k_images, tx_it->second.tx); sorted_it++; LOG_PRINT_L2(" added, new block size " << total_size << "/" << max_total_size << ", coinbase " << print_money(best_coinbase)); diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 0d3158eaf..89f207131 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -638,6 +638,9 @@ namespace cryptonote 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), block hash " << arg.block_hash); + + std::list<std::pair<cryptonote::blobdata, block>> local_blocks; + std::list<cryptonote::blobdata> local_txs; block b; if (!m_core.get_block_by_hash(arg.block_hash, b)) @@ -892,22 +895,22 @@ 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; @@ -925,6 +928,9 @@ namespace cryptonote } } + if (arg.blocks.empty()) + goto skip; + m_core.prepare_handle_incoming_blocks(arg.blocks); for(const block_complete_entry& block_entry: arg.blocks) @@ -990,6 +996,7 @@ namespace cryptonote } +skip: request_missing_objects(context, true); return 1; } diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index 8ed529737..e5fa5fece 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -272,12 +272,18 @@ bool t_command_parser_executor::start_mining(const std::vector<std::string>& arg std::cout << "Mining to a testnet address, make sure this is intentional!" << std::endl; uint64_t threads_count = 1; bool do_background_mining = false; - if(args.size() > 3) + bool ignore_battery = false; + if(args.size() > 4) { return false; } - if(args.size() == 3) + if(args.size() == 4) + { + ignore_battery = args[3] == "true"; + } + + if(args.size() >= 3) { do_background_mining = args[2] == "true"; } @@ -288,7 +294,7 @@ bool t_command_parser_executor::start_mining(const std::vector<std::string>& arg threads_count = (ok && 0 < threads_count) ? threads_count : 1; } - m_executor.start_mining(adr, threads_count, testnet, do_background_mining); + m_executor.start_mining(adr, threads_count, testnet, do_background_mining, ignore_battery); return true; } diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp index 4133b90d9..365a7d03b 100644 --- a/src/daemon/command_server.cpp +++ b/src/daemon/command_server.cpp @@ -96,7 +96,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>] [do_background_mining], default 1 thread, no background mining" + , "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" diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 19013cbc8..670c33128 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -124,18 +124,18 @@ 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) { // The first three variables are not used when the fourth is false - rpc_commands = new daemonize::t_command_server(0, 0, boost::none, false, mp_internals->rpc.get_server()); + 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(); } diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 00b349b15..219a7a1c4 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -929,13 +929,14 @@ bool t_rpc_command_executor::print_transaction_pool_stats() { return true; } -bool t_rpc_command_executor::start_mining(cryptonote::account_public_address address, uint64_t num_threads, bool testnet, bool do_background_mining = false) { +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) diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h index 5b7b76448..d9e874767 100644 --- a/src/daemon/rpc_command_executor.h +++ b/src/daemon/rpc_command_executor.h @@ -106,7 +106,7 @@ public: bool print_transaction_pool_stats(); - bool start_mining(cryptonote::account_public_address address, uint64_t num_threads, bool testnet, bool do_background_mining); + 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(); diff --git a/src/p2p/network_throttle-detail.cpp b/src/p2p/network_throttle-detail.cpp index d4fe356a9..2c8bdabea 100644 --- a/src/p2p/network_throttle-detail.cpp +++ b/src/p2p/network_throttle-detail.cpp @@ -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/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/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 8165dacbc..4570a3715 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -151,7 +151,7 @@ namespace cryptonote 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 +159,30 @@ namespace cryptonote return false; } - for(auto& b: bs) + 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; 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) + for(const auto& t: bd.second) { - res.blocks.back().txs.push_back(tx_to_blob(t)); + res.blocks.back().txs.push_back(t); res.output_indices.back().indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::tx_output_indices()); - bool r = m_core.get_tx_outputs_gindexs(b.first.tx_hashes[txidx++], res.output_indices.back().indices.back().indices); + bool r = m_core.get_tx_outputs_gindexs(b.tx_hashes[txidx++], res.output_indices.back().indices.back().indices); if (!r) { res.status = "Failed"; @@ -635,7 +641,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, req.do_background_mining)) + 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); @@ -1387,6 +1393,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; } //------------------------------------------------------------------------------------------------------------------------------ @@ -1437,12 +1444,14 @@ 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; } //------------------------------------------------------------------------------------------------------------------------------ diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 3ab1ea175..8b744c26a 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -501,11 +501,13 @@ 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() }; diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 41665e4d8..b466251ab 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -997,7 +997,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 +1049,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 +1065,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; @@ -3166,7 +3166,7 @@ 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; @@ -3203,7 +3203,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; @@ -3219,7 +3219,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; @@ -3851,7 +3851,7 @@ 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; @@ -3879,7 +3879,7 @@ 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; @@ -4179,7 +4179,7 @@ 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; diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index c46de6b06..326ca26a0 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1157,7 +1157,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()); @@ -1168,7 +1168,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()); @@ -1178,7 +1178,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 ""; } diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp index 4104e7884..6feec75bd 100644 --- a/src/wallet/api/wallet_manager.cpp +++ b/src/wallet/api/wallet_manager.cpp @@ -182,7 +182,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 +196,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; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index e7a175dc7..56eee6ecf 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -251,7 +251,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; @@ -269,7 +269,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; @@ -1456,7 +1456,7 @@ void wallet2::update_pool_state() for (auto it: res.transactions) { cryptonote::blobdata txid_data; - if(epee::string_tools::parse_hexstr_to_binbuff(it.id_hash, txid_data)) + if(epee::string_tools::parse_hexstr_to_binbuff(it.id_hash, txid_data) && txid_data.size() == sizeof(crypto::hash)) { const crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data()); if (m_unconfirmed_payments.find(txid) == m_unconfirmed_payments.end()) @@ -4217,13 +4217,9 @@ 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); // 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 @@ -4233,6 +4229,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp LOG_PRINT_L2("Second outout was not strictly needed, and relatedness " << relatedness << ", not adding"); break; } + } 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); diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 881279e42..ee50c3cdb 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -983,7 +983,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"; @@ -1013,7 +1013,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"; @@ -1206,7 +1206,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"; @@ -1214,7 +1214,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"; |