diff options
Diffstat (limited to 'src')
361 files changed, 3296 insertions, 1296 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d45363e24..07d4d58c3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/blockchain_db/CMakeLists.txt b/src/blockchain_db/CMakeLists.txt index 0a21e4920..77faade17 100644 --- a/src/blockchain_db/CMakeLists.txt +++ b/src/blockchain_db/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp index 336577a07..5c8dece2a 100644 --- a/src/blockchain_db/blockchain_db.cpp +++ b/src/blockchain_db/blockchain_db.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index ac2c55db1..9a321437b 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -1040,6 +1040,16 @@ public: virtual difficulty_type get_block_difficulty(const uint64_t& height) const = 0; /** + * @brief correct blocks cumulative difficulties that were incorrectly calculated due to the 'difficulty drift' bug + * + * If the block does not exist, the subclass should throw BLOCK_DNE + * + * @param start_height the height where the drift starts + * @param new_cumulative_difficulties new cumulative difficulties to be stored + */ + virtual void correct_block_cumulative_difficulties(const uint64_t& start_height, const std::vector<difficulty_type>& new_cumulative_difficulties) = 0; + + /** * @brief fetch a block's already generated coins * * The subclass should return the total coins generated as of the block diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 5cec8879d..6ea55d09d 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are @@ -25,6 +25,13 @@ // 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. +#ifndef _WIN32 +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/mman.h> +#include <fcntl.h> +#endif + #include "db_lmdb.h" #include <boost/filesystem.hpp> @@ -1287,6 +1294,26 @@ BlockchainLMDB::BlockchainLMDB(bool batch_transactions): BlockchainDB() m_hardfork = nullptr; } +void BlockchainLMDB::check_mmap_support() +{ +#ifndef _WIN32 + const boost::filesystem::path mmap_test_file = m_folder / boost::filesystem::unique_path(); + int mmap_test_fd = ::open(mmap_test_file.string().c_str(), O_RDWR | O_CREAT, 0600); + if (mmap_test_fd < 0) + throw0(DB_ERROR((std::string("Failed to check for mmap support: open failed: ") + strerror(errno)).c_str())); + epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([mmap_test_fd, &mmap_test_file]() { + ::close(mmap_test_fd); + boost::filesystem::remove(mmap_test_file.string()); + }); + if (write(mmap_test_fd, "mmaptest", 8) != 8) + throw0(DB_ERROR((std::string("Failed to check for mmap support: write failed: ") + strerror(errno)).c_str())); + void *mmap_res = mmap(NULL, 8, PROT_READ, MAP_SHARED, mmap_test_fd, 0); + if (mmap_res == MAP_FAILED) + throw0(DB_ERROR("This filesystem does not support mmap: use --data-dir to place the blockchain on a filesystem which does")); + munmap(mmap_res, 8); +#endif +} + void BlockchainLMDB::open(const std::string& filename, const int db_flags) { int result; @@ -1328,6 +1355,8 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags) m_folder = filename; + check_mmap_support(); + #ifdef __OpenBSD__ if ((mdb_flags & MDB_WRITEMAP) == 0) { MCLOG_RED(el::Level::Info, "global", "Running on OpenBSD: forcing WRITEMAP"); @@ -2750,6 +2779,44 @@ difficulty_type BlockchainLMDB::get_block_difficulty(const uint64_t& height) con return diff1 - diff2; } +void BlockchainLMDB::correct_block_cumulative_difficulties(const uint64_t& start_height, const std::vector<difficulty_type>& new_cumulative_difficulties) +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + check_open(); + mdb_txn_cursors *m_cursors = &m_wcursors; + + int result = 0; + block_wtxn_start(); + CURSOR(block_info) + + const uint64_t bc_height = height(); + if (start_height + new_cumulative_difficulties.size() != bc_height) + { + block_wtxn_abort(); + throw0(DB_ERROR("Incorrect new_cumulative_difficulties size")); + } + + for (uint64_t height = start_height; height < bc_height; ++height) + { + MDB_val_set(key, height); + result = mdb_cursor_get(m_cur_block_info, (MDB_val *)&zerokval, &key, MDB_GET_BOTH); + if (result) + throw1(BLOCK_DNE(lmdb_error("Failed to get block info: ", result).c_str())); + + mdb_block_info bi = *(mdb_block_info*)key.mv_data; + const difficulty_type d = new_cumulative_difficulties[height - start_height]; + bi.bi_diff_hi = ((d >> 64) & 0xffffffffffffffff).convert_to<uint64_t>(); + bi.bi_diff_lo = (d & 0xffffffffffffffff).convert_to<uint64_t>(); + + MDB_val_set(key2, height); + MDB_val_set(val, bi); + result = mdb_cursor_put(m_cur_block_info, &key2, &val, MDB_CURRENT); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to overwrite block info to db transaction: ", result).c_str())); + } + block_wtxn_stop(); +} + uint64_t BlockchainLMDB::get_block_already_generated_coins(const uint64_t& height) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); @@ -4048,7 +4115,7 @@ void BlockchainLMDB::get_output_tx_and_index_from_global(const std::vector<uint6 void BlockchainLMDB::get_output_key(const epee::span<const uint64_t> &amounts, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs, bool allow_partial) const { if (amounts.size() != 1 && amounts.size() != offsets.size()) - throw0(DB_ERROR("Invalid sizes of amounts and offets")); + throw0(DB_ERROR("Invalid sizes of amounts and offsets")); LOG_PRINT_L3("BlockchainLMDB::" << __func__); TIME_MEASURE_START(db3); diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index 6ddeed671..5abb8014f 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are @@ -229,6 +229,8 @@ public: virtual difficulty_type get_block_difficulty(const uint64_t& height) const; + virtual void correct_block_cumulative_difficulties(const uint64_t& start_height, const std::vector<difficulty_type>& new_cumulative_difficulties); + virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const; virtual uint64_t get_block_long_term_weight(const uint64_t& height) const; @@ -356,6 +358,7 @@ public: static int compare_string(const MDB_val *a, const MDB_val *b); private: + void check_mmap_support(); void do_resize(uint64_t size_increase=0); bool need_resize(uint64_t threshold_size=0) const; diff --git a/src/blockchain_db/locked_txn.h b/src/blockchain_db/locked_txn.h index d14631ddb..1834f56fb 100644 --- a/src/blockchain_db/locked_txn.h +++ b/src/blockchain_db/locked_txn.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_db/testdb.h b/src/blockchain_db/testdb.h index 638dd3b37..92911d081 100644 --- a/src/blockchain_db/testdb.h +++ b/src/blockchain_db/testdb.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -69,8 +69,8 @@ public: virtual cryptonote::blobdata get_block_blob(const crypto::hash& h) const override { return cryptonote::blobdata(); } virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const override { return false; } virtual bool get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const override { return false; } - virtual bool get_pruned_tx_blobs_from(const crypto::hash& h, size_t count, std::vector<cryptonote::blobdata> &bd) const { return false; } - virtual bool get_blocks_from(uint64_t start_height, size_t min_count, size_t max_count, size_t max_size, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata>>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const { return false; } + virtual bool get_pruned_tx_blobs_from(const crypto::hash& h, size_t count, std::vector<cryptonote::blobdata> &bd) const override { return false; } + virtual bool get_blocks_from(uint64_t start_height, size_t min_count, size_t max_count, size_t max_size, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata>>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const override { return false; } virtual bool get_prunable_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const override { return false; } virtual bool get_prunable_tx_hash(const crypto::hash& tx_hash, crypto::hash &prunable_hash) const override { return false; } virtual uint64_t get_block_height(const crypto::hash& h) const override { return 0; } @@ -82,6 +82,7 @@ public: virtual std::vector<uint64_t> get_block_weights(uint64_t start_height, size_t count) const override { return {}; } virtual cryptonote::difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const override { return 10; } virtual cryptonote::difficulty_type get_block_difficulty(const uint64_t& height) const override { return 0; } + virtual void correct_block_cumulative_difficulties(const uint64_t& start_height, const std::vector<difficulty_type>& new_cumulative_difficulties) override {} virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const override { return 10000000000; } virtual uint64_t get_block_long_term_weight(const uint64_t& height) const override { return 128; } virtual std::vector<uint64_t> get_long_term_block_weights(uint64_t start_height, size_t count) const override { return {}; } diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt index 0ba7ee86c..ac533596a 100644 --- a/src/blockchain_utilities/CMakeLists.txt +++ b/src/blockchain_utilities/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/blockchain_utilities/README.md b/src/blockchain_utilities/README.md index 1462e3186..4a233bcf5 100644 --- a/src/blockchain_utilities/README.md +++ b/src/blockchain_utilities/README.md @@ -1,6 +1,6 @@ # Monero Blockchain Utilities -Copyright (c) 2014-2019, The Monero Project +Copyright (c) 2014-2020, The Monero Project ## Introduction diff --git a/src/blockchain_utilities/blockchain_ancestry.cpp b/src/blockchain_utilities/blockchain_ancestry.cpp index db6d6f7d7..89b932e4f 100644 --- a/src/blockchain_utilities/blockchain_ancestry.cpp +++ b/src/blockchain_utilities/blockchain_ancestry.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index 4a5712921..a847f6a9d 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/blockchain_depth.cpp b/src/blockchain_utilities/blockchain_depth.cpp index 153f5f7c6..6199586bf 100644 --- a/src/blockchain_utilities/blockchain_depth.cpp +++ b/src/blockchain_utilities/blockchain_depth.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/blockchain_export.cpp b/src/blockchain_utilities/blockchain_export.cpp index b180f88de..e55930b00 100644 --- a/src/blockchain_utilities/blockchain_export.cpp +++ b/src/blockchain_utilities/blockchain_export.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp index 852e9cf4f..ab2313096 100644 --- a/src/blockchain_utilities/blockchain_import.cpp +++ b/src/blockchain_utilities/blockchain_import.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp b/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp index cee24d4da..f8763710e 100644 --- a/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp +++ b/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/blockchain_stats.cpp b/src/blockchain_utilities/blockchain_stats.cpp index 0d18b8819..c9c815f2a 100644 --- a/src/blockchain_utilities/blockchain_stats.cpp +++ b/src/blockchain_utilities/blockchain_stats.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/blockchain_usage.cpp b/src/blockchain_utilities/blockchain_usage.cpp index 2fa56452b..6e87a0974 100644 --- a/src/blockchain_utilities/blockchain_usage.cpp +++ b/src/blockchain_utilities/blockchain_usage.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/blockchain_utilities.h b/src/blockchain_utilities/blockchain_utilities.h index e8615cf86..035e2397f 100644 --- a/src/blockchain_utilities/blockchain_utilities.h +++ b/src/blockchain_utilities/blockchain_utilities.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/blocksdat_file.cpp b/src/blockchain_utilities/blocksdat_file.cpp index df3c6cafc..b9ef1de7d 100644 --- a/src/blockchain_utilities/blocksdat_file.cpp +++ b/src/blockchain_utilities/blocksdat_file.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/blocksdat_file.h b/src/blockchain_utilities/blocksdat_file.h index 72b7afc17..567505ac1 100644 --- a/src/blockchain_utilities/blocksdat_file.h +++ b/src/blockchain_utilities/blocksdat_file.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -43,7 +43,6 @@ #include <algorithm> #include <cstdio> #include <fstream> -#include <boost/iostreams/copy.hpp> #include <atomic> #include "common/command_line.h" diff --git a/src/blockchain_utilities/bootstrap_file.cpp b/src/blockchain_utilities/bootstrap_file.cpp index 252c79776..a4704626f 100644 --- a/src/blockchain_utilities/bootstrap_file.cpp +++ b/src/blockchain_utilities/bootstrap_file.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/bootstrap_file.h b/src/blockchain_utilities/bootstrap_file.h index 1e6ef5d81..23dbb64d7 100644 --- a/src/blockchain_utilities/bootstrap_file.h +++ b/src/blockchain_utilities/bootstrap_file.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -41,7 +41,6 @@ #include <algorithm> #include <cstdio> #include <fstream> -#include <boost/iostreams/copy.hpp> #include <atomic> #include "common/command_line.h" diff --git a/src/blockchain_utilities/bootstrap_serialization.h b/src/blockchain_utilities/bootstrap_serialization.h index 70b3eea7e..bcc8c7e15 100644 --- a/src/blockchain_utilities/bootstrap_serialization.h +++ b/src/blockchain_utilities/bootstrap_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/blocks/CMakeLists.txt b/src/blocks/CMakeLists.txt index 0b7b02fee..051c60886 100644 --- a/src/blocks/CMakeLists.txt +++ b/src/blocks/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/blocks/checkpoints.dat b/src/blocks/checkpoints.dat Binary files differindex b14f9e8d2..fa58387ab 100644 --- a/src/blocks/checkpoints.dat +++ b/src/blocks/checkpoints.dat diff --git a/src/checkpoints/CMakeLists.txt b/src/checkpoints/CMakeLists.txt index d324f518e..46d6fedf1 100644 --- a/src/checkpoints/CMakeLists.txt +++ b/src/checkpoints/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/checkpoints/checkpoints.cpp b/src/checkpoints/checkpoints.cpp index 4a4b3c5c2..6e7e1acba 100644 --- a/src/checkpoints/checkpoints.cpp +++ b/src/checkpoints/checkpoints.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -34,6 +34,7 @@ #include "string_tools.h" #include "storages/portable_storage_template_helper.h" // epee json include #include "serialization/keyvalue_serialization.h" +#include <functional> #include <vector> using namespace epee; @@ -71,7 +72,7 @@ namespace cryptonote { } //--------------------------------------------------------------------------- - bool checkpoints::add_checkpoint(uint64_t height, const std::string& hash_str) + bool checkpoints::add_checkpoint(uint64_t height, const std::string& hash_str, const std::string& difficulty_str) { crypto::hash h = crypto::null_hash; bool r = epee::string_tools::hex_to_pod(hash_str, h); @@ -83,6 +84,23 @@ namespace cryptonote CHECK_AND_ASSERT_MES(h == m_points[height], false, "Checkpoint at given height already exists, and hash for new checkpoint was different!"); } m_points[height] = h; + if (!difficulty_str.empty()) + { + try + { + difficulty_type difficulty(difficulty_str); + if (m_difficulty_points.count(height)) + { + CHECK_AND_ASSERT_MES(difficulty == m_difficulty_points[height], false, "Difficulty checkpoint at given height already exists, and difficulty for new checkpoint was different!"); + } + m_difficulty_points[height] = difficulty; + } + catch (...) + { + LOG_ERROR("Failed to parse difficulty checkpoint: " << difficulty_str); + return false; + } + } return true; } //--------------------------------------------------------------------------- @@ -133,17 +151,20 @@ namespace cryptonote //--------------------------------------------------------------------------- uint64_t checkpoints::get_max_height() const { - std::map< uint64_t, crypto::hash >::const_iterator highest = - std::max_element( m_points.begin(), m_points.end(), - ( boost::bind(&std::map< uint64_t, crypto::hash >::value_type::first, _1) < - boost::bind(&std::map< uint64_t, crypto::hash >::value_type::first, _2 ) ) ); - return highest->first; + if (m_points.empty()) + return 0; + return m_points.rbegin()->first; } //--------------------------------------------------------------------------- const std::map<uint64_t, crypto::hash>& checkpoints::get_points() const { return m_points; } + //--------------------------------------------------------------------------- + const std::map<uint64_t, difficulty_type>& checkpoints::get_difficulty_points() const + { + return m_difficulty_points; + } bool checkpoints::check_for_conflicts(const checkpoints& other) const { @@ -161,56 +182,60 @@ namespace cryptonote { if (nettype == TESTNET) { - ADD_CHECKPOINT(0, "48ca7cd3c8de5b6a4d53d2861fbdaedca141553559f9be9520068053cda8430b"); - ADD_CHECKPOINT(1000000, "46b690b710a07ea051bc4a6b6842ac37be691089c0f7758cfeec4d5fc0b4a258"); - ADD_CHECKPOINT(1058600, "12904f6b4d9e60fd875674e07147d2c83d6716253f046af7b894c3e81da7e1bd"); + ADD_CHECKPOINT2(0, "48ca7cd3c8de5b6a4d53d2861fbdaedca141553559f9be9520068053cda8430b", "0x1"); + ADD_CHECKPOINT2(1000000, "46b690b710a07ea051bc4a6b6842ac37be691089c0f7758cfeec4d5fc0b4a258", "0x7aaad7153"); + ADD_CHECKPOINT2(1058600, "12904f6b4d9e60fd875674e07147d2c83d6716253f046af7b894c3e81da7e1bd", "0x971efd119"); + ADD_CHECKPOINT2(1450000, "87562ca6786f41556b8d5b48067303a57dc5ca77155b35199aedaeca1550f5a0", "0xa639e2930e"); return true; } if (nettype == STAGENET) { - ADD_CHECKPOINT(0, "76ee3cc98646292206cd3e86f74d88b4dcc1d937088645e9b0cbca84b7ce74eb"); - ADD_CHECKPOINT(10000, "1f8b0ce313f8b9ba9a46108bfd285c45ad7c2176871fd41c3a690d4830ce2fd5"); + ADD_CHECKPOINT2(0, "76ee3cc98646292206cd3e86f74d88b4dcc1d937088645e9b0cbca84b7ce74eb", "0x1"); + ADD_CHECKPOINT2(10000, "1f8b0ce313f8b9ba9a46108bfd285c45ad7c2176871fd41c3a690d4830ce2fd5", "0x1d73ba"); + ADD_CHECKPOINT2(550000, "409f68cddd8e74b37469b41c1e61250d81c5776b42264f416d5d27c4626383ed", "0x5f3d4d03e"); return true; } - ADD_CHECKPOINT(1, "771fbcd656ec1464d3a02ead5e18644030007a0fc664c0a964d30922821a8148"); - ADD_CHECKPOINT(10, "c0e3b387e47042f72d8ccdca88071ff96bff1ac7cde09ae113dbb7ad3fe92381"); - ADD_CHECKPOINT(100, "ac3e11ca545e57c49fca2b4e8c48c03c23be047c43e471e1394528b1f9f80b2d"); - ADD_CHECKPOINT(1000, "5acfc45acffd2b2e7345caf42fa02308c5793f15ec33946e969e829f40b03876"); - ADD_CHECKPOINT(10000, "c758b7c81f928be3295d45e230646de8b852ec96a821eac3fea4daf3fcac0ca2"); - ADD_CHECKPOINT(22231, "7cb10e29d67e1c069e6e11b17d30b809724255fee2f6868dc14cfc6ed44dfb25"); - ADD_CHECKPOINT(29556, "53c484a8ed91e4da621bb2fa88106dbde426fe90d7ef07b9c1e5127fb6f3a7f6"); - ADD_CHECKPOINT(50000, "0fe8758ab06a8b9cb35b7328fd4f757af530a5d37759f9d3e421023231f7b31c"); - ADD_CHECKPOINT(80000, "a62dcd7b536f22e003ebae8726e9e7276f63d594e264b6f0cd7aab27b66e75e3"); - ADD_CHECKPOINT(202612, "bbd604d2ba11ba27935e006ed39c9bfdd99b76bf4a50654bc1e1e61217962698"); - ADD_CHECKPOINT(202613, "e2aa337e78df1f98f462b3b1e560c6b914dec47b610698b7b7d1e3e86b6197c2"); - ADD_CHECKPOINT(202614, "c29e3dc37d8da3e72e506e31a213a58771b24450144305bcba9e70fa4d6ea6fb"); - ADD_CHECKPOINT(205000, "5d3d7a26e6dc7535e34f03def711daa8c263785f73ec1fadef8a45880fde8063"); - ADD_CHECKPOINT(220000, "9613f455933c00e3e33ac315cc6b455ee8aa0c567163836858c2d9caff111553"); - ADD_CHECKPOINT(230300, "bae7a80c46859db355556e3a9204a337ae8f24309926a1312323fdecf1920e61"); - ADD_CHECKPOINT(230700, "93e631240ceac831da1aebfc5dac8f722c430463024763ebafa888796ceaeedf"); - ADD_CHECKPOINT(231350, "b5add137199b820e1ea26640e5c3e121fd85faa86a1e39cf7e6cc097bdeb1131"); - ADD_CHECKPOINT(232150, "955de8e6b6508af2c24f7334f97beeea651d78e9ade3ab18fec3763be3201aa8"); - ADD_CHECKPOINT(249380, "654fb0a81ce3e5caf7e3264a70f447d4bd07586c08fa50f6638cc54da0a52b2d"); - ADD_CHECKPOINT(460000, "75037a7aed3e765db96c75bcf908f59d690a5f3390baebb9edeafd336a1c4831"); - ADD_CHECKPOINT(500000, "2428f0dbe49796be05ed81b347f53e1f7f44aed0abf641446ec2b94cae066b02"); - ADD_CHECKPOINT(600000, "f5828ebf7d7d1cb61762c4dfe3ccf4ecab2e1aad23e8113668d981713b7a54c5"); - ADD_CHECKPOINT(700000, "12be9b3d210b93f574d2526abb9c1ab2a881b479131fd0d4f7dac93875f503cd"); - ADD_CHECKPOINT(825000, "56503f9ad766774b575be3aff73245e9d159be88132c93d1754764f28da2ff60"); - ADD_CHECKPOINT(900000, "d9958d0e7dcf91a5a7b11de225927bf7efc6eb26240315ce12372be902cc1337"); - ADD_CHECKPOINT(913193, "5292d5d56f6ba4de33a58d9a34d263e2cb3c6fee0aed2286fd4ac7f36d53c85f"); - ADD_CHECKPOINT(1000000, "a886ef5149902d8342475fee9bb296341b891ac67c4842f47a833f23c00ed721"); - ADD_CHECKPOINT(1100000, "3fd720c5c8b3072fc1ccda922dec1ef25f9ed88a1e6ad4103d0fe00b180a5903"); - ADD_CHECKPOINT(1150000, "1dd16f626d18e1e988490dfd06de5920e22629c972c58b4d8daddea0038627b2"); - ADD_CHECKPOINT(1200000, "fa7d13a90850882060479d100141ff84286599ae39c3277c8ea784393f882d1f"); - ADD_CHECKPOINT(1300000, "31b34272343a44a9f4ac7de7a8fcf3b7d8a3124d7d6870affd510d2f37e74cd0"); - ADD_CHECKPOINT(1390000, "a8f5649dd4ded60eedab475f2bec8c934681c07e3cf640e9be0617554f13ff6c"); - ADD_CHECKPOINT(1450000, "ac94e8860093bc7c83e4e91215cba1d663421ecf4067a0ae609c3a8b52bcfac2"); - ADD_CHECKPOINT(1530000, "01759bce497ec38e63c78b1038892169203bb78f87e488172f6b854fcd63ba7e"); - ADD_CHECKPOINT(1579000, "7d0d7a2346373afd41ed1e744a939fc5d474a7dbaa257be5c6fff4009e789241"); - ADD_CHECKPOINT(1668900, "ac2dcaf3d2f58ffcf8391639f0f1ebafcb8eac43c49479c7c37f611868d07568"); - ADD_CHECKPOINT(1775600, "1c6e01c661dc22cab939e79ec6a5272190624ce8356d2f7b958e4f9a57fdb05e"); - ADD_CHECKPOINT(1856000, "9b57f17f29c71a3acd8a7904b93c41fa6eb8d2b7c73936ce4f1702d14880ba29"); - ADD_CHECKPOINT(1958000, "98a5d6e51afdf3146e0eefb10a66e8648d8d4d5c2742be8835e976ba217c9bb2"); + ADD_CHECKPOINT2(1, "771fbcd656ec1464d3a02ead5e18644030007a0fc664c0a964d30922821a8148", "0x2"); + ADD_CHECKPOINT2(10, "c0e3b387e47042f72d8ccdca88071ff96bff1ac7cde09ae113dbb7ad3fe92381", "0x2a974"); + ADD_CHECKPOINT2(100, "ac3e11ca545e57c49fca2b4e8c48c03c23be047c43e471e1394528b1f9f80b2d", "0x35d14b"); + ADD_CHECKPOINT2(1000, "5acfc45acffd2b2e7345caf42fa02308c5793f15ec33946e969e829f40b03876", "0x36a0373"); + ADD_CHECKPOINT2(10000, "c758b7c81f928be3295d45e230646de8b852ec96a821eac3fea4daf3fcac0ca2", "0x60a91390"); + ADD_CHECKPOINT2(22231, "7cb10e29d67e1c069e6e11b17d30b809724255fee2f6868dc14cfc6ed44dfb25", "0x1e288793d"); + ADD_CHECKPOINT2(29556, "53c484a8ed91e4da621bb2fa88106dbde426fe90d7ef07b9c1e5127fb6f3a7f6", "0x71f64cce8"); + ADD_CHECKPOINT2(50000, "0fe8758ab06a8b9cb35b7328fd4f757af530a5d37759f9d3e421023231f7b31c", "0x893044b400"); + ADD_CHECKPOINT2(80000, "a62dcd7b536f22e003ebae8726e9e7276f63d594e264b6f0cd7aab27b66e75e3", "0x5cc113f1076"); + ADD_CHECKPOINT2(202612, "bbd604d2ba11ba27935e006ed39c9bfdd99b76bf4a50654bc1e1e61217962698", "0x73310a259eb2"); + ADD_CHECKPOINT2(202613, "e2aa337e78df1f98f462b3b1e560c6b914dec47b610698b7b7d1e3e86b6197c2", "0x733154039b97"); + ADD_CHECKPOINT2(202614, "c29e3dc37d8da3e72e506e31a213a58771b24450144305bcba9e70fa4d6ea6fb", "0x73319dc90cb6"); + ADD_CHECKPOINT2(205000, "5d3d7a26e6dc7535e34f03def711daa8c263785f73ec1fadef8a45880fde8063", "0x75fcc3d85123"); + ADD_CHECKPOINT2(220000, "9613f455933c00e3e33ac315cc6b455ee8aa0c567163836858c2d9caff111553", "0x89cfed0cae3c"); + ADD_CHECKPOINT2(230300, "bae7a80c46859db355556e3a9204a337ae8f24309926a1312323fdecf1920e61", "0x967d13e5baa9"); + ADD_CHECKPOINT2(230700, "93e631240ceac831da1aebfc5dac8f722c430463024763ebafa888796ceaeedf", "0x96fb9663ebe7"); + ADD_CHECKPOINT2(231350, "b5add137199b820e1ea26640e5c3e121fd85faa86a1e39cf7e6cc097bdeb1131", "0x97b9919177bf"); + ADD_CHECKPOINT2(232150, "955de8e6b6508af2c24f7334f97beeea651d78e9ade3ab18fec3763be3201aa8", "0x98a038b612e8"); + ADD_CHECKPOINT2(249380, "654fb0a81ce3e5caf7e3264a70f447d4bd07586c08fa50f6638cc54da0a52b2d", "0xac9739634e6d"); + ADD_CHECKPOINT2(460000, "75037a7aed3e765db96c75bcf908f59d690a5f3390baebb9edeafd336a1c4831", "0x167799549bdda"); + ADD_CHECKPOINT2(500000, "2428f0dbe49796be05ed81b347f53e1f7f44aed0abf641446ec2b94cae066b02", "0x188ce145e4ba9"); + ADD_CHECKPOINT2(600000, "f5828ebf7d7d1cb61762c4dfe3ccf4ecab2e1aad23e8113668d981713b7a54c5", "0x1d9f3759e1554"); + ADD_CHECKPOINT2(700000, "12be9b3d210b93f574d2526abb9c1ab2a881b479131fd0d4f7dac93875f503cd", "0x2201e4ee39c2c"); + ADD_CHECKPOINT2(825000, "56503f9ad766774b575be3aff73245e9d159be88132c93d1754764f28da2ff60", "0x27565a442d5df"); + ADD_CHECKPOINT2(900000, "d9958d0e7dcf91a5a7b11de225927bf7efc6eb26240315ce12372be902cc1337", "0x2a6334031546e"); + ADD_CHECKPOINT2(913193, "5292d5d56f6ba4de33a58d9a34d263e2cb3c6fee0aed2286fd4ac7f36d53c85f", "0x2aefe7f40f5ea"); + ADD_CHECKPOINT2(1000000, "a886ef5149902d8342475fee9bb296341b891ac67c4842f47a833f23c00ed721", "0x2edd71370f0e5"); + ADD_CHECKPOINT2(1100000, "3fd720c5c8b3072fc1ccda922dec1ef25f9ed88a1e6ad4103d0fe00b180a5903", "0x390eb0035c53a"); + ADD_CHECKPOINT2(1150000, "1dd16f626d18e1e988490dfd06de5920e22629c972c58b4d8daddea0038627b2", "0x422d5662e9e37"); + ADD_CHECKPOINT2(1200000, "fa7d13a90850882060479d100141ff84286599ae39c3277c8ea784393f882d1f", "0x4c73503fc4aa3"); + ADD_CHECKPOINT2(1300000, "31b34272343a44a9f4ac7de7a8fcf3b7d8a3124d7d6870affd510d2f37e74cd0", "0x723f49bc249d5"); + ADD_CHECKPOINT2(1390000, "a8f5649dd4ded60eedab475f2bec8c934681c07e3cf640e9be0617554f13ff6c", "0xb4bba65e2841b"); + ADD_CHECKPOINT2(1450000, "ac94e8860093bc7c83e4e91215cba1d663421ecf4067a0ae609c3a8b52bcfac2", "0x11a4aabdca9511"); + ADD_CHECKPOINT2(1530000, "01759bce497ec38e63c78b1038892169203bb78f87e488172f6b854fcd63ba7e", "0x2819ce9f9e91e5"); + ADD_CHECKPOINT2(1579000, "7d0d7a2346373afd41ed1e744a939fc5d474a7dbaa257be5c6fff4009e789241", "0x357a590e7dda83"); + ADD_CHECKPOINT2(1668900, "ac2dcaf3d2f58ffcf8391639f0f1ebafcb8eac43c49479c7c37f611868d07568", "0x474226e475cc3b"); + ADD_CHECKPOINT2(1775600, "1c6e01c661dc22cab939e79ec6a5272190624ce8356d2f7b958e4f9a57fdb05e", "0x5e3b9d206a27c6"); + ADD_CHECKPOINT2(1856000, "9b57f17f29c71a3acd8a7904b93c41fa6eb8d2b7c73936ce4f1702d14880ba29", "0x6bde5e1caccee1"); + ADD_CHECKPOINT2(1958000, "98a5d6e51afdf3146e0eefb10a66e8648d8d4d5c2742be8835e976ba217c9bb2", "0x79dd46d2a0971a"); + ADD_CHECKPOINT2(2046000, "5e867f0b8baefed9244a681df97fc885d8ab36c3dfcd24c7a3abf3b8ac8b8314", "0x9cb8b6ff2978c6"); + ADD_CHECKPOINT2(2092500, "c4e00820c9c7989b49153d5e90ae095a18a11d990e82fcc3be54e6ed785472b5", "0xb4e585a31369cb"); return true; } diff --git a/src/checkpoints/checkpoints.h b/src/checkpoints/checkpoints.h index a55b94bf0..029c50548 100644 --- a/src/checkpoints/checkpoints.h +++ b/src/checkpoints/checkpoints.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -33,8 +33,10 @@ #include "misc_log_ex.h" #include "crypto/hash.h" #include "cryptonote_config.h" +#include "cryptonote_basic/difficulty.h" #define ADD_CHECKPOINT(h, hash) CHECK_AND_ASSERT(add_checkpoint(h, hash), false); +#define ADD_CHECKPOINT2(h, hash, difficulty) CHECK_AND_ASSERT(add_checkpoint(h, hash, difficulty), false); #define JSON_HASH_FILE_NAME "checkpoints.json" @@ -61,12 +63,13 @@ namespace cryptonote * * @param height the height of the block the checkpoint is for * @param hash_str the hash of the block, as a string + * @param difficulty_str the cumulative difficulty of the block, as a string (optional) * * @return false if parsing the hash fails, or if the height is a duplicate * AND the existing checkpoint hash does not match the new one, * otherwise returns true */ - bool add_checkpoint(uint64_t height, const std::string& hash_str); + bool add_checkpoint(uint64_t height, const std::string& hash_str, const std::string& difficulty_str = ""); /** * @brief checks if there is a checkpoint in the future @@ -134,6 +137,13 @@ namespace cryptonote const std::map<uint64_t, crypto::hash>& get_points() const; /** + * @brief gets the difficulty checkpoints container + * + * @return a const reference to the difficulty checkpoints container + */ + const std::map<uint64_t, difficulty_type>& get_difficulty_points() const; + + /** * @brief checks if our checkpoints container conflicts with another * * A conflict refers to a case where both checkpoint sets have a checkpoint @@ -187,6 +197,7 @@ namespace cryptonote private: std::map<uint64_t, crypto::hash> m_points; //!< the checkpoints container + std::map<uint64_t, difficulty_type> m_difficulty_points; //!< the difficulty checkpoints container }; } diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index f06737b31..8e427b6b8 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # @@ -86,7 +86,8 @@ set(common_private_headers updates.h aligned.h timings.h - combinator.h) + combinator.h + utf8.h) monero_private_headers(common ${common_private_headers}) diff --git a/src/common/aligned.c b/src/common/aligned.c index 6982409f7..e3a607f66 100644 --- a/src/common/aligned.c +++ b/src/common/aligned.c @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/apply_permutation.h b/src/common/apply_permutation.h index a4b2c8b78..03effc50e 100644 --- a/src/common/apply_permutation.h +++ b/src/common/apply_permutation.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/base58.cpp b/src/common/base58.cpp index ac1bf4b29..77b6a642c 100644 --- a/src/common/base58.cpp +++ b/src/common/base58.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/base58.h b/src/common/base58.h index 6bf2c3bb7..4a7b02cbc 100644 --- a/src/common/base58.h +++ b/src/common/base58.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/boost_serialization_helper.h b/src/common/boost_serialization_helper.h index 2280f3312..4f9cf0518 100644 --- a/src/common/boost_serialization_helper.h +++ b/src/common/boost_serialization_helper.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/command_line.cpp b/src/common/command_line.cpp index cae744ea5..42370f543 100644 --- a/src/common/command_line.cpp +++ b/src/common/command_line.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/command_line.h b/src/common/command_line.h index b5e3d94a7..b4619905b 100644 --- a/src/common/command_line.h +++ b/src/common/command_line.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/common_fwd.h b/src/common/common_fwd.h index 7eaa6cdee..ac8894ba4 100644 --- a/src/common/common_fwd.h +++ b/src/common/common_fwd.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp index dc1f335a7..c871177b1 100644 --- a/src/common/dns_utils.cpp +++ b/src/common/dns_utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/dns_utils.h b/src/common/dns_utils.h index a6bc7463a..30c4cced2 100644 --- a/src/common/dns_utils.h +++ b/src/common/dns_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/download.cpp b/src/common/download.cpp index 2b6a3f9d3..3dd9c3976 100644 --- a/src/common/download.cpp +++ b/src/common/download.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/download.h b/src/common/download.h index f8656a59c..19a52a786 100644 --- a/src/common/download.h +++ b/src/common/download.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/http_connection.h b/src/common/http_connection.h index 6b4294802..5ee39cdbb 100644 --- a/src/common/http_connection.h +++ b/src/common/http_connection.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/i18n.cpp b/src/common/i18n.cpp index 9ac347263..ebc367b3a 100644 --- a/src/common/i18n.cpp +++ b/src/common/i18n.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/i18n.h b/src/common/i18n.h index 82a07410d..3bf2f8260 100644 --- a/src/common/i18n.h +++ b/src/common/i18n.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/json_util.h b/src/common/json_util.h index 96f4b90e6..38464f7f1 100644 --- a/src/common/json_util.h +++ b/src/common/json_util.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019, The Monero Project +// Copyright (c) 2016-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/notify.cpp b/src/common/notify.cpp index e2df5096d..f31100214 100644 --- a/src/common/notify.cpp +++ b/src/common/notify.cpp @@ -62,7 +62,7 @@ static void replace(std::vector<std::string> &v, const char *tag, const char *s) boost::replace_all(str, tag, s); } -int Notify::notify(const char *tag, const char *s, ...) +int Notify::notify(const char *tag, const char *s, ...) const { std::vector<std::string> margs = args; diff --git a/src/common/notify.h b/src/common/notify.h index f813e8def..65d4e1072 100644 --- a/src/common/notify.h +++ b/src/common/notify.h @@ -38,8 +38,12 @@ class Notify { public: Notify(const char *spec); + Notify(const Notify&) = default; + Notify(Notify&&) = default; + Notify& operator=(const Notify&) = default; + Notify& operator=(Notify&&) = default; - int notify(const char *tag, const char *s, ...); + int notify(const char *tag, const char *s, ...) const; private: std::string filename; @@ -47,3 +51,4 @@ private: }; } + diff --git a/src/common/password.cpp b/src/common/password.cpp index 33e1f48fd..019f04c95 100644 --- a/src/common/password.cpp +++ b/src/common/password.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/password.h b/src/common/password.h index 2837c70f3..54d97e565 100644 --- a/src/common/password.h +++ b/src/common/password.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/perf_timer.cpp b/src/common/perf_timer.cpp index 4408170d1..898701b2f 100644 --- a/src/common/perf_timer.cpp +++ b/src/common/perf_timer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019, The Monero Project +// Copyright (c) 2016-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/perf_timer.h b/src/common/perf_timer.h index 29a37e655..fb89b78c0 100644 --- a/src/common/perf_timer.h +++ b/src/common/perf_timer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019, The Monero Project +// Copyright (c) 2016-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/pod-class.h b/src/common/pod-class.h index 200647590..38eae8948 100644 --- a/src/common/pod-class.h +++ b/src/common/pod-class.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/rpc_client.h b/src/common/rpc_client.h index dab3e562d..26bcea751 100644 --- a/src/common/rpc_client.h +++ b/src/common/rpc_client.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/scoped_message_writer.h b/src/common/scoped_message_writer.h index 546377392..0a5ebe1c9 100644 --- a/src/common/scoped_message_writer.h +++ b/src/common/scoped_message_writer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/sfinae_helpers.h b/src/common/sfinae_helpers.h index e9a98bb63..e0778b10f 100644 --- a/src/common/sfinae_helpers.h +++ b/src/common/sfinae_helpers.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019, The Monero Project +// Copyright (c) 2016-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/stack_trace.cpp b/src/common/stack_trace.cpp index 8d4f8c6f1..828b8f1c8 100644 --- a/src/common/stack_trace.cpp +++ b/src/common/stack_trace.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019, The Monero Project +// Copyright (c) 2016-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/stack_trace.h b/src/common/stack_trace.h index ae6573885..57f939b2d 100644 --- a/src/common/stack_trace.h +++ b/src/common/stack_trace.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019, The Monero Project +// Copyright (c) 2016-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/threadpool.cpp b/src/common/threadpool.cpp index 753bf238c..a1737778c 100644 --- a/src/common/threadpool.cpp +++ b/src/common/threadpool.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/threadpool.h b/src/common/threadpool.h index a49d0e14f..91f9fdf47 100644 --- a/src/common/threadpool.h +++ b/src/common/threadpool.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/unordered_containers_boost_serialization.h b/src/common/unordered_containers_boost_serialization.h index 74e2c3f81..67bde676c 100644 --- a/src/common/unordered_containers_boost_serialization.h +++ b/src/common/unordered_containers_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/updates.cpp b/src/common/updates.cpp index f620bb53a..c51c4320f 100644 --- a/src/common/updates.cpp +++ b/src/common/updates.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/updates.h b/src/common/updates.h index 8fda6d207..0bfe26c3f 100644 --- a/src/common/updates.h +++ b/src/common/updates.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/utf8.h b/src/common/utf8.h new file mode 100644 index 000000000..60247f1b2 --- /dev/null +++ b/src/common/utf8.h @@ -0,0 +1,114 @@ +// Copyright (c) 2019, 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 <cctype> +#include <cwchar> +#include <stdexcept> + +namespace tools +{ + template<typename T, typename Transform> + inline T utf8canonical(const T &s, Transform t = [](wint_t c)->wint_t { return c; }) + { + T sc = ""; + size_t avail = s.size(); + const char *ptr = s.data(); + wint_t cp = 0; + int bytes = 1; + char wbuf[8], *wptr; + while (avail--) + { + if ((*ptr & 0x80) == 0) + { + cp = *ptr++; + bytes = 1; + } + else if ((*ptr & 0xe0) == 0xc0) + { + if (avail < 1) + throw std::runtime_error("Invalid UTF-8"); + cp = (*ptr++ & 0x1f) << 6; + cp |= *ptr++ & 0x3f; + --avail; + bytes = 2; + } + else if ((*ptr & 0xf0) == 0xe0) + { + if (avail < 2) + throw std::runtime_error("Invalid UTF-8"); + cp = (*ptr++ & 0xf) << 12; + cp |= (*ptr++ & 0x3f) << 6; + cp |= *ptr++ & 0x3f; + avail -= 2; + bytes = 3; + } + else if ((*ptr & 0xf8) == 0xf0) + { + if (avail < 3) + throw std::runtime_error("Invalid UTF-8"); + cp = (*ptr++ & 0x7) << 18; + cp |= (*ptr++ & 0x3f) << 12; + cp |= (*ptr++ & 0x3f) << 6; + cp |= *ptr++ & 0x3f; + avail -= 3; + bytes = 4; + } + else + throw std::runtime_error("Invalid UTF-8"); + + cp = t(cp); + if (cp <= 0x7f) + bytes = 1; + else if (cp <= 0x7ff) + bytes = 2; + else if (cp <= 0xffff) + bytes = 3; + else if (cp <= 0x10ffff) + bytes = 4; + else + throw std::runtime_error("Invalid code point UTF-8 transformation"); + + wptr = wbuf; + switch (bytes) + { + case 1: *wptr++ = cp; break; + case 2: *wptr++ = 0xc0 | (cp >> 6); *wptr++ = 0x80 | (cp & 0x3f); break; + case 3: *wptr++ = 0xe0 | (cp >> 12); *wptr++ = 0x80 | ((cp >> 6) & 0x3f); *wptr++ = 0x80 | (cp & 0x3f); break; + case 4: *wptr++ = 0xf0 | (cp >> 18); *wptr++ = 0x80 | ((cp >> 12) & 0x3f); *wptr++ = 0x80 | ((cp >> 6) & 0x3f); *wptr++ = 0x80 | (cp & 0x3f); break; + default: throw std::runtime_error("Invalid UTF-8"); + } + *wptr = 0; + sc.append(wbuf, bytes); + cp = 0; + bytes = 1; + } + return sc; + } +} diff --git a/src/common/util.cpp b/src/common/util.cpp index f1140d1d5..138ac4294 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/util.h b/src/common/util.h index 25137ab64..0c3409ea1 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/varint.h b/src/common/varint.h index a0d79be28..133622a67 100644 --- a/src/common/varint.h +++ b/src/common/varint.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt index 80f1e5d7e..318e6dc57 100644 --- a/src/crypto/CMakeLists.txt +++ b/src/crypto/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/crypto/blake256.c b/src/crypto/blake256.c index bb2c5fb40..831a90302 100644 --- a/src/crypto/blake256.c +++ b/src/crypto/blake256.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/blake256.h b/src/crypto/blake256.h index 309dbf3ec..5abdb79b9 100644 --- a/src/crypto/blake256.h +++ b/src/crypto/blake256.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/c_threads.h b/src/crypto/c_threads.h index 56d680ff8..5b2fcddd3 100644 --- a/src/crypto/c_threads.h +++ b/src/crypto/c_threads.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/chacha.h b/src/crypto/chacha.h index a39823e5a..a1158be7e 100644 --- a/src/crypto/chacha.h +++ b/src/crypto/chacha.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto-ops-data.c b/src/crypto/crypto-ops-data.c index c9530bb2a..d16fd9429 100644 --- a/src/crypto/crypto-ops-data.c +++ b/src/crypto/crypto-ops-data.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto-ops.c b/src/crypto/crypto-ops.c index 5a3d994a6..3110d3ce7 100644 --- a/src/crypto/crypto-ops.c +++ b/src/crypto/crypto-ops.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto-ops.h b/src/crypto/crypto-ops.h index 7137437bc..eeb94669b 100644 --- a/src/crypto/crypto-ops.h +++ b/src/crypto/crypto-ops.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp index 0ec992de9..1e4a6d33f 100644 --- a/src/crypto/crypto.cpp +++ b/src/crypto/crypto.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -294,6 +294,7 @@ namespace crypto { sc_mulsub(&sig.r, &sig.c, &unwrap(sec), &k); if (!sc_isnonzero((const unsigned char*)sig.r.data)) goto try_again; + memwipe(&k, sizeof(k)); } bool crypto_ops::check_signature(const hash &prefix_hash, const public_key &pub, const signature &sig) { @@ -390,6 +391,8 @@ namespace crypto { // sig.r = k - sig.c*r sc_mulsub(&sig.r, &sig.c, &unwrap(r), &k); + + memwipe(&k, sizeof(k)); } bool crypto_ops::check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig) { @@ -560,6 +563,7 @@ POP_WARNINGS random_scalar(sig[i].c); random_scalar(sig[i].r); if (ge_frombytes_vartime(&tmp3, &*pubs[i]) != 0) { + memwipe(&k, sizeof(k)); local_abort("invalid pubkey"); } ge_double_scalarmult_base_vartime(&tmp2, &sig[i].c, &tmp3, &sig[i].r); @@ -573,6 +577,8 @@ POP_WARNINGS hash_to_scalar(buf.get(), rs_comm_size(pubs_count), h); sc_sub(&sig[sec_index].c, &h, &sum); sc_mulsub(&sig[sec_index].r, &sig[sec_index].c, &unwrap(sec), &k); + + memwipe(&k, sizeof(k)); } bool crypto_ops::check_ring_signature(const hash &prefix_hash, const key_image &image, diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index 8ce321f71..70d463a16 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto_ops_builder/README.md b/src/crypto/crypto_ops_builder/README.md index 4bb95cc4a..a3acb11e8 100644 --- a/src/crypto/crypto_ops_builder/README.md +++ b/src/crypto/crypto_ops_builder/README.md @@ -1,6 +1,6 @@ # Monero -Copyright (c) 2014-2019, The Monero Project +Copyright (c) 2014-2020, The Monero Project ## Crypto Ops Builder diff --git a/src/crypto/crypto_ops_builder/crypto-ops-data.c b/src/crypto/crypto_ops_builder/crypto-ops-data.c index 45e9923b1..64fd15070 100644 --- a/src/crypto/crypto_ops_builder/crypto-ops-data.c +++ b/src/crypto/crypto_ops_builder/crypto-ops-data.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto_ops_builder/crypto-ops-old.c b/src/crypto/crypto_ops_builder/crypto-ops-old.c index 89c2ced6e..da85ee534 100644 --- a/src/crypto/crypto_ops_builder/crypto-ops-old.c +++ b/src/crypto/crypto_ops_builder/crypto-ops-old.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto_ops_builder/crypto-ops.h b/src/crypto/crypto_ops_builder/crypto-ops.h index b4fcfca9c..d719743c4 100644 --- a/src/crypto/crypto_ops_builder/crypto-ops.h +++ b/src/crypto/crypto_ops_builder/crypto-ops.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto_ops_builder/ref10CommentedCombined/MakeCryptoOps.py b/src/crypto/crypto_ops_builder/ref10CommentedCombined/MakeCryptoOps.py index dfba583f7..08cead175 100644 --- a/src/crypto/crypto_ops_builder/ref10CommentedCombined/MakeCryptoOps.py +++ b/src/crypto/crypto_ops_builder/ref10CommentedCombined/MakeCryptoOps.py @@ -15,7 +15,7 @@ print("maybe someone smart can replace the sed with perl..") a = "" license = textwrap.dedent("""\ - // Copyright (c) 2014-2019, The Monero Project + // Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto_ops_builder/ref10CommentedCombined/crypto-ops.h b/src/crypto/crypto_ops_builder/ref10CommentedCombined/crypto-ops.h index f62ff441d..613b718f5 100644 --- a/src/crypto/crypto_ops_builder/ref10CommentedCombined/crypto-ops.h +++ b/src/crypto/crypto_ops_builder/ref10CommentedCombined/crypto-ops.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/generic-ops.h b/src/crypto/generic-ops.h index d06726638..9aa7b065a 100644 --- a/src/crypto/generic-ops.h +++ b/src/crypto/generic-ops.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/groestl.h b/src/crypto/groestl.h index 6628947dd..7483db9b6 100644 --- a/src/crypto/groestl.h +++ b/src/crypto/groestl.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/groestl_tables.h b/src/crypto/groestl_tables.h index ca0c4fca6..7bf03afd7 100644 --- a/src/crypto/groestl_tables.h +++ b/src/crypto/groestl_tables.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash-extra-blake.c b/src/crypto/hash-extra-blake.c index 9bada96f3..4cc915cdd 100644 --- a/src/crypto/hash-extra-blake.c +++ b/src/crypto/hash-extra-blake.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash-extra-groestl.c b/src/crypto/hash-extra-groestl.c index 57866bf9d..dec21310d 100644 --- a/src/crypto/hash-extra-groestl.c +++ b/src/crypto/hash-extra-groestl.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash-extra-jh.c b/src/crypto/hash-extra-jh.c index 0dbac4fb5..0604003bd 100644 --- a/src/crypto/hash-extra-jh.c +++ b/src/crypto/hash-extra-jh.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash-extra-skein.c b/src/crypto/hash-extra-skein.c index 78f48609f..55bd4ddec 100644 --- a/src/crypto/hash-extra-skein.c +++ b/src/crypto/hash-extra-skein.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h index d117bb640..7dfc5151d 100644 --- a/src/crypto/hash-ops.h +++ b/src/crypto/hash-ops.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash.c b/src/crypto/hash.c index b66f3b010..a917115fe 100644 --- a/src/crypto/hash.c +++ b/src/crypto/hash.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash.h b/src/crypto/hash.h index 27184fa53..4b99bebaa 100644 --- a/src/crypto/hash.h +++ b/src/crypto/hash.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/initializer.h b/src/crypto/initializer.h index 75d80f054..e9a7d97e2 100644 --- a/src/crypto/initializer.h +++ b/src/crypto/initializer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/keccak.c b/src/crypto/keccak.c index 851c70a25..72d472d8a 100644 --- a/src/crypto/keccak.c +++ b/src/crypto/keccak.c @@ -146,7 +146,6 @@ void keccak1600(const uint8_t *in, size_t inlen, uint8_t *md) #define KECCAK_BLOCKLEN 136 #define KECCAK_WORDS 17 #define KECCAK_DIGESTSIZE 32 -#define IS_ALIGNED_64(p) (0 == (7 & ((const char*)(p) - (const char*)0))) #define KECCAK_PROCESS_BLOCK(st, block) { \ for (int i_ = 0; i_ < KECCAK_WORDS; i_++){ \ ((st))[i_] ^= swap64le(((block))[i_]); \ @@ -178,17 +177,10 @@ void keccak_update(KECCAK_CTX * ctx, const uint8_t *in, size_t inlen){ inlen -= left; } - const bool is_aligned = IS_ALIGNED_64(in); while (inlen >= KECCAK_BLOCKLEN) { - const uint64_t* aligned_message_block; - if (is_aligned) { - aligned_message_block = (uint64_t*)in; - } else { - memcpy(ctx->message, in, KECCAK_BLOCKLEN); - aligned_message_block = ctx->message; - } + memcpy(ctx->message, in, KECCAK_BLOCKLEN); - KECCAK_PROCESS_BLOCK(ctx->hash, aligned_message_block); + KECCAK_PROCESS_BLOCK(ctx->hash, ctx->message); in += KECCAK_BLOCKLEN; inlen -= KECCAK_BLOCKLEN; } diff --git a/src/crypto/random.c b/src/crypto/random.c index 766b5f558..1e3d9beff 100644 --- a/src/crypto/random.c +++ b/src/crypto/random.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/random.h b/src/crypto/random.h index 21a66d776..8b81e7a66 100644 --- a/src/crypto/random.h +++ b/src/crypto/random.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/rx-slow-hash.c b/src/crypto/rx-slow-hash.c index 606c67336..1d7f09cab 100644 --- a/src/crypto/rx-slow-hash.c +++ b/src/crypto/rx-slow-hash.c @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/skein_port.h b/src/crypto/skein_port.h index 1ec07a4d1..94fd44035 100644 --- a/src/crypto/skein_port.h +++ b/src/crypto/skein_port.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index 88eb751a6..5a773f3cf 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/tree-hash.c b/src/crypto/tree-hash.c index 0a5860f3b..643e95121 100644 --- a/src/crypto/tree-hash.c +++ b/src/crypto/tree-hash.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt index 59040d8a2..113fd9d86 100644 --- a/src/cryptonote_basic/CMakeLists.txt +++ b/src/cryptonote_basic/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp index 02eca289e..36ff41684 100644 --- a/src/cryptonote_basic/account.cpp +++ b/src/cryptonote_basic/account.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/account.h b/src/cryptonote_basic/account.h index abf751b6e..5288b9b04 100644 --- a/src/cryptonote_basic/account.h +++ b/src/cryptonote_basic/account.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/account_boost_serialization.h b/src/cryptonote_basic/account_boost_serialization.h index 320a960dc..d97a5a854 100644 --- a/src/cryptonote_basic/account_boost_serialization.h +++ b/src/cryptonote_basic/account_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/blobdatatype.h b/src/cryptonote_basic/blobdatatype.h index 20f6b2421..6906e0c9d 100644 --- a/src/cryptonote_basic/blobdatatype.h +++ b/src/cryptonote_basic/blobdatatype.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/connection_context.h b/src/cryptonote_basic/connection_context.h index a682bebf2..0c3a94054 100644 --- a/src/cryptonote_basic/connection_context.h +++ b/src/cryptonote_basic/connection_context.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index e2286ae8c..bc6a378f2 100644 --- a/src/cryptonote_basic/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/cryptonote_basic_impl.cpp b/src/cryptonote_basic/cryptonote_basic_impl.cpp index 9bafcfc86..541393fa9 100644 --- a/src/cryptonote_basic/cryptonote_basic_impl.cpp +++ b/src/cryptonote_basic/cryptonote_basic_impl.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/cryptonote_basic_impl.h b/src/cryptonote_basic/cryptonote_basic_impl.h index c7198a16f..1303670d2 100644 --- a/src/cryptonote_basic/cryptonote_basic_impl.h +++ b/src/cryptonote_basic/cryptonote_basic_impl.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/cryptonote_boost_serialization.h b/src/cryptonote_basic/cryptonote_boost_serialization.h index 79ce610a9..6f89cc7ae 100644 --- a/src/cryptonote_basic/cryptonote_boost_serialization.h +++ b/src/cryptonote_basic/cryptonote_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 80747dd89..3fd059ac1 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -126,6 +126,20 @@ namespace cryptonote namespace cryptonote { //--------------------------------------------------------------- + void get_transaction_prefix_hash(const transaction_prefix& tx, crypto::hash& h, hw::device &hwdev) + { + hwdev.get_transaction_prefix_hash(tx,h); + } + + //--------------------------------------------------------------- + crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx, hw::device &hwdev) + { + crypto::hash h = null_hash; + get_transaction_prefix_hash(tx, h, hwdev); + return h; + } + + //--------------------------------------------------------------- void get_transaction_prefix_hash(const transaction_prefix& tx, crypto::hash& h) { std::ostringstream s; @@ -146,6 +160,8 @@ namespace cryptonote if (tx.version >= 2 && !is_coinbase(tx)) { rct::rctSig &rv = tx.rct_signatures; + if (rv.type == rct::RCTTypeNull) + return true; if (rv.outPk.size() != tx.vout.size()) { LOG_PRINT_L1("Failed to parse transaction from blob, bad outPk size in tx " << get_transaction_hash(tx)); diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h index 8ed3b0b43..5639e38d0 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.h +++ b/src/cryptonote_basic/cryptonote_format_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -48,6 +48,8 @@ namespace epee namespace cryptonote { //--------------------------------------------------------------- + void get_transaction_prefix_hash(const transaction_prefix& tx, crypto::hash& h, hw::device &hwdev); + crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx, hw::device &hwdev); void get_transaction_prefix_hash(const transaction_prefix& tx, crypto::hash& h); crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx); bool parse_and_validate_tx_prefix_from_blob(const blobdata& tx_blob, transaction_prefix& tx); diff --git a/src/cryptonote_basic/difficulty.cpp b/src/cryptonote_basic/difficulty.cpp index 859173aa5..dbc2e534a 100644 --- a/src/cryptonote_basic/difficulty.cpp +++ b/src/cryptonote_basic/difficulty.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/difficulty.h b/src/cryptonote_basic/difficulty.h index 771deb04c..7f5ea4597 100644 --- a/src/cryptonote_basic/difficulty.h +++ b/src/cryptonote_basic/difficulty.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/events.h b/src/cryptonote_basic/events.h new file mode 100644 index 000000000..6c6742215 --- /dev/null +++ b/src/cryptonote_basic/events.h @@ -0,0 +1,46 @@ +// Copyright (c) 2020, 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 "crypto/hash.h" +#include "cryptonote_basic/cryptonote_basic.h" + +namespace cryptonote +{ + /*! Transactions are expensive to move or copy (lots of 32-byte internal + buffers). This allows `cryptonote::core` to do a single notification for + a vector of transactions, without having to move/copy duplicate or invalid + transactions. */ + struct txpool_event + { + cryptonote::transaction tx; + crypto::hash hash; + bool res; //!< Listeners must ignore `tx` when this is false. + }; +} diff --git a/src/cryptonote_basic/fwd.h b/src/cryptonote_basic/fwd.h new file mode 100644 index 000000000..d54223461 --- /dev/null +++ b/src/cryptonote_basic/fwd.h @@ -0,0 +1,36 @@ +// Copyright (c) 2020, 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 + +namespace cryptonote +{ + struct block; + class transaction; + struct txpool_event; +} diff --git a/src/cryptonote_basic/hardfork.cpp b/src/cryptonote_basic/hardfork.cpp index dfeca27b4..7a5161bc8 100644 --- a/src/cryptonote_basic/hardfork.cpp +++ b/src/cryptonote_basic/hardfork.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/hardfork.h b/src/cryptonote_basic/hardfork.h index 987dcc75a..5800c31b5 100644 --- a/src/cryptonote_basic/hardfork.h +++ b/src/cryptonote_basic/hardfork.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp index c1e8365ac..34a559b83 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/miner.h b/src/cryptonote_basic/miner.h index ce50d674e..3cbc4e5a4 100644 --- a/src/cryptonote_basic/miner.h +++ b/src/cryptonote_basic/miner.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/subaddress_index.h b/src/cryptonote_basic/subaddress_index.h index 99933e229..3f5f120d9 100644 --- a/src/cryptonote_basic/subaddress_index.h +++ b/src/cryptonote_basic/subaddress_index.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/tx_extra.h b/src/cryptonote_basic/tx_extra.h index ecb4c6040..50f2e1438 100644 --- a/src/cryptonote_basic/tx_extra.h +++ b/src/cryptonote_basic/tx_extra.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/verification_context.h b/src/cryptonote_basic/verification_context.h index ec5f604a5..2535cba95 100644 --- a/src/cryptonote_basic/verification_context.h +++ b/src/cryptonote_basic/verification_context.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 401cca1ca..f88f622f2 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt index cb3875878..0be8b544e 100644 --- a/src/cryptonote_core/CMakeLists.txt +++ b/src/cryptonote_core/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 2571e4203..b0726981c 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -32,6 +32,7 @@ #include <cstdio> #include <boost/filesystem.hpp> #include <boost/range/adaptor/reversed.hpp> +#include <boost/format.hpp> #include "include_base_utils.h" #include "cryptonote_basic/cryptonote_basic_impl.h" @@ -86,7 +87,7 @@ DISABLE_VS_WARNINGS(4267) //------------------------------------------------------------------ Blockchain::Blockchain(tx_memory_pool& tx_pool) : - m_db(), m_tx_pool(tx_pool), m_hardfork(NULL), m_timestamps_and_difficulties_height(0), m_current_block_cumul_weight_limit(0), m_current_block_cumul_weight_median(0), + m_db(), m_tx_pool(tx_pool), m_hardfork(NULL), m_timestamps_and_difficulties_height(0), m_reset_timestamps_and_difficulties_height(true), m_current_block_cumul_weight_limit(0), m_current_block_cumul_weight_median(0), m_enforce_dns_checkpoints(false), m_max_prepare_blocks_threads(4), m_db_sync_on_blocks(true), m_db_sync_threshold(1), m_db_sync_mode(db_async), m_db_default_sync(false), m_fast_sync(true), m_show_time_stats(false), m_sync_counter(0), m_bytes_to_sync(0), m_cancel(false), m_long_term_block_weights_window(CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE), m_long_term_effective_median_block_weight(0), @@ -427,6 +428,7 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline if (num_popped_blocks > 0) { m_timestamps_and_difficulties_height = 0; + m_reset_timestamps_and_difficulties_height = true; m_hardfork->reorganize_from_chain_height(get_current_blockchain_height()); uint64_t top_block_height; crypto::hash top_block_hash = get_tail_id(top_block_height); @@ -439,6 +441,15 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline m_long_term_block_weights_cache_rolling_median = epee::misc_utils::rolling_median_t<uint64_t>(m_long_term_block_weights_window); } + bool difficulty_ok; + uint64_t difficulty_recalc_height; + std::tie(difficulty_ok, difficulty_recalc_height) = check_difficulty_checkpoints(); + if (!difficulty_ok) + { + MERROR("Difficulty drift detected!"); + recalculate_difficulties(difficulty_recalc_height); + } + { db_txn_guard txn_guard(m_db, m_db->is_read_only()); if (!update_next_cumulative_weight_limit()) @@ -567,6 +578,7 @@ block Blockchain::pop_block_from_blockchain() CRITICAL_REGION_LOCAL(m_blockchain_lock); m_timestamps_and_difficulties_height = 0; + m_reset_timestamps_and_difficulties_height = true; block popped_block; std::vector<transaction> popped_txs; @@ -644,6 +656,7 @@ bool Blockchain::reset_and_set_genesis_block(const block& b) LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); m_timestamps_and_difficulties_height = 0; + m_reset_timestamps_and_difficulties_height = true; invalidate_block_template_cache(); m_db->reset(); m_db->drop_alt_blocks(); @@ -812,12 +825,20 @@ bool Blockchain::get_block_by_hash(const crypto::hash &h, block &blk, bool *orph // less blocks than desired if there aren't enough. difficulty_type Blockchain::get_difficulty_for_next_block() { + LOG_PRINT_L3("Blockchain::" << __func__); + + std::stringstream ss; + bool print = false; + + int done = 0; + ss << "get_difficulty_for_next_block: height " << m_db->height() << std::endl; if (m_fixed_difficulty) { return m_db->height() ? m_fixed_difficulty : 1; } - LOG_PRINT_L3("Blockchain::" << __func__); +start: + difficulty_type D = 0; crypto::hash top_hash = get_tail_id(); { @@ -826,21 +847,32 @@ difficulty_type Blockchain::get_difficulty_for_next_block() // something a bit out of date, but that's fine since anything which // requires the blockchain lock will have acquired it in the first place, // and it will be unlocked only when called from the getinfo RPC + ss << "Locked, tail id " << top_hash << ", cached is " << m_difficulty_for_next_block_top_hash << std::endl; if (top_hash == m_difficulty_for_next_block_top_hash) - return m_difficulty_for_next_block; + { + ss << "Same, using cached diff " << m_difficulty_for_next_block << std::endl; + D = m_difficulty_for_next_block; + } } CRITICAL_REGION_LOCAL(m_blockchain_lock); std::vector<uint64_t> timestamps; std::vector<difficulty_type> difficulties; uint64_t height; - top_hash = get_tail_id(height); // get it again now that we have the lock - ++height; // top block height to blockchain height + auto new_top_hash = get_tail_id(height); // get it again now that we have the lock + ++height; + if (!(new_top_hash == top_hash)) D=0; + ss << "Re-locked, height " << height << ", tail id " << new_top_hash << (new_top_hash == top_hash ? "" : " (different)") << std::endl; + top_hash = new_top_hash; + // ND: Speedup // 1. Keep a list of the last 735 (or less) blocks that is used to compute difficulty, // then when the next block difficulty is queried, push the latest height data and // pop the oldest one from the list. This only requires 1x read per height instead // of doing 735 (DIFFICULTY_BLOCKS_COUNT). + bool check = false; + if (m_reset_timestamps_and_difficulties_height) + m_timestamps_and_difficulties_height = 0; if (m_timestamps_and_difficulties_height != 0 && ((height - m_timestamps_and_difficulties_height) == 1) && m_timestamps.size() >= DIFFICULTY_BLOCKS_COUNT) { uint64_t index = height - 1; @@ -855,8 +887,12 @@ difficulty_type Blockchain::get_difficulty_for_next_block() m_timestamps_and_difficulties_height = height; timestamps = m_timestamps; difficulties = m_difficulties; + check = true; } - else + //else + std::vector<uint64_t> timestamps_from_cache = timestamps; + std::vector<difficulty_type> difficulties_from_cache = difficulties; + { uint64_t offset = height - std::min <uint64_t> (height, static_cast<uint64_t>(DIFFICULTY_BLOCKS_COUNT)); if (offset == 0) @@ -869,27 +905,179 @@ difficulty_type Blockchain::get_difficulty_for_next_block() timestamps.reserve(height - offset); difficulties.reserve(height - offset); } + ss << "Looking up " << (height - offset) << " from " << offset << std::endl; for (; offset < height; offset++) { timestamps.push_back(m_db->get_block_timestamp(offset)); difficulties.push_back(m_db->get_block_cumulative_difficulty(offset)); } + if (check) if (timestamps != timestamps_from_cache || difficulties !=difficulties_from_cache) + { + ss << "Inconsistency XXX:" << std::endl; + ss << "top hash: "<<top_hash << std::endl; + ss << "timestamps: " << timestamps_from_cache.size() << " from cache, but " << timestamps.size() << " without" << std::endl; + ss << "difficulties: " << difficulties_from_cache.size() << " from cache, but " << difficulties.size() << " without" << std::endl; + ss << "timestamps_from_cache:" << std::endl; for (const auto &v :timestamps_from_cache) ss << " " << v << std::endl; + ss << "timestamps:" << std::endl; for (const auto &v :timestamps) ss << " " << v << std::endl; + ss << "difficulties_from_cache:" << std::endl; for (const auto &v :difficulties_from_cache) ss << " " << v << std::endl; + ss << "difficulties:" << std::endl; for (const auto &v :difficulties) ss << " " << v << std::endl; + + uint64_t dbh = m_db->height(); + uint64_t sh = dbh < 10000 ? 0 : dbh - 10000; + ss << "History from -10k at :" << dbh << ", from " << sh << std::endl; + for (uint64_t h = sh; h < dbh; ++h) + { + uint64_t ts = m_db->get_block_timestamp(h); + difficulty_type d = m_db->get_block_cumulative_difficulty(h); + ss << " " << h << " " << ts << " " << d << std::endl; + } + print = true; + } m_timestamps_and_difficulties_height = height; m_timestamps = timestamps; m_difficulties = difficulties; } + size_t target = get_difficulty_target(); difficulty_type diff = next_difficulty(timestamps, difficulties, target); CRITICAL_REGION_LOCAL1(m_difficulty_lock); m_difficulty_for_next_block_top_hash = top_hash; m_difficulty_for_next_block = diff; + if (D && D != diff) + { + ss << "XXX Mismatch at " << height << "/" << top_hash << "/" << get_tail_id() << ": cached " << D << ", real " << diff << std::endl; + print = true; + } + + ++done; + if (done == 1 && D && D != diff) + { + print = true; + ss << "Might be a race. Let's see what happens if we try again..." << std::endl; + epee::misc_utils::sleep_no_w(100); + goto start; + } + ss << "Diff for " << top_hash << ": " << diff << std::endl; + if (print) + { + MGINFO("START DUMP"); + MGINFO(ss.str()); + MGINFO("END DUMP"); + MGINFO("Please send moneromooo on Freenode the contents of this log, from a couple dozen lines before START DUMP to END DUMP"); + } return diff; } //------------------------------------------------------------------ +std::pair<bool, uint64_t> Blockchain::check_difficulty_checkpoints() const +{ + uint64_t res = 0; + for (const std::pair<uint64_t, difficulty_type>& i : m_checkpoints.get_difficulty_points()) + { + if (i.first >= m_db->height()) + break; + if (m_db->get_block_cumulative_difficulty(i.first) != i.second) + return {false, res}; + res = i.first; + } + return {true, res}; +} +//------------------------------------------------------------------ +size_t Blockchain::recalculate_difficulties(boost::optional<uint64_t> start_height_opt) +{ + if (m_fixed_difficulty) + { + return 0; + } + LOG_PRINT_L3("Blockchain::" << __func__); + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + const uint64_t start_height = start_height_opt ? *start_height_opt : check_difficulty_checkpoints().second; + const uint64_t top_height = m_db->height() - 1; + MGINFO("Recalculating difficulties from height " << start_height << " to height " << top_height); + + std::vector<uint64_t> timestamps; + std::vector<difficulty_type> difficulties; + timestamps.reserve(DIFFICULTY_BLOCKS_COUNT + 1); + difficulties.reserve(DIFFICULTY_BLOCKS_COUNT + 1); + if (start_height > 1) + { + for (uint64_t i = 0; i < DIFFICULTY_BLOCKS_COUNT; ++i) + { + uint64_t height = start_height - 1 - i; + if (height == 0) + break; + timestamps.insert(timestamps.begin(), m_db->get_block_timestamp(height)); + difficulties.insert(difficulties.begin(), m_db->get_block_cumulative_difficulty(height)); + } + } + difficulty_type last_cum_diff = start_height <= 1 ? start_height : difficulties.back(); + uint64_t drift_start_height = 0; + std::vector<difficulty_type> new_cumulative_difficulties; + for (uint64_t height = start_height; height <= top_height; ++height) + { + size_t target = get_ideal_hard_fork_version(height) < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2; + difficulty_type recalculated_diff = next_difficulty(timestamps, difficulties, target); + + boost::multiprecision::uint256_t recalculated_cum_diff_256 = boost::multiprecision::uint256_t(recalculated_diff) + last_cum_diff; + CHECK_AND_ASSERT_THROW_MES(recalculated_cum_diff_256 <= std::numeric_limits<difficulty_type>::max(), "Difficulty overflow!"); + difficulty_type recalculated_cum_diff = recalculated_cum_diff_256.convert_to<difficulty_type>(); + + if (drift_start_height == 0) + { + difficulty_type existing_cum_diff = m_db->get_block_cumulative_difficulty(height); + if (recalculated_cum_diff != existing_cum_diff) + { + drift_start_height = height; + new_cumulative_difficulties.reserve(top_height + 1 - height); + LOG_ERROR("Difficulty drift found at height:" << height << ", hash:" << m_db->get_block_hash_from_height(height) << ", existing:" << existing_cum_diff << ", recalculated:" << recalculated_cum_diff); + } + } + if (drift_start_height > 0) + { + new_cumulative_difficulties.push_back(recalculated_cum_diff); + if (height % 100000 == 0) + LOG_ERROR(boost::format("%llu / %llu (%.1f%%)") % height % top_height % (100 * (height - drift_start_height) / float(top_height - drift_start_height))); + } + + if (height > 0) + { + timestamps.push_back(m_db->get_block_timestamp(height)); + difficulties.push_back(recalculated_cum_diff); + } + if (timestamps.size() > DIFFICULTY_BLOCKS_COUNT) + { + CHECK_AND_ASSERT_THROW_MES(timestamps.size() == DIFFICULTY_BLOCKS_COUNT + 1, "Wrong timestamps size: " << timestamps.size()); + timestamps.erase(timestamps.begin()); + difficulties.erase(difficulties.begin()); + } + last_cum_diff = recalculated_cum_diff; + } + + if (drift_start_height > 0) + { + LOG_ERROR("Writing to the DB..."); + try + { + m_db->correct_block_cumulative_difficulties(drift_start_height, new_cumulative_difficulties); + } + catch (const std::exception& e) + { + LOG_ERROR("Error correcting cumulative difficulties from height " << drift_start_height << ", what = " << e.what()); + } + LOG_ERROR("Corrected difficulties for " << new_cumulative_difficulties.size() << " blocks"); + // clear cache + m_difficulty_for_next_block_top_hash = crypto::null_hash; + m_timestamps_and_difficulties_height = 0; + } + + return new_cumulative_difficulties.size(); +} +//------------------------------------------------------------------ std::vector<time_t> Blockchain::get_last_block_timestamps(unsigned int blocks) const { + CRITICAL_REGION_LOCAL(m_blockchain_lock); uint64_t height = m_db->height(); if (blocks > height) blocks = height; @@ -914,6 +1102,7 @@ bool Blockchain::rollback_blockchain_switching(std::list<block>& original_chain, } m_timestamps_and_difficulties_height = 0; + m_reset_timestamps_and_difficulties_height = true; // remove blocks from blockchain until we get back to where we should be. while (m_db->height() != rollback_height) @@ -950,6 +1139,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<block_extended_info> CRITICAL_REGION_LOCAL(m_blockchain_lock); m_timestamps_and_difficulties_height = 0; + m_reset_timestamps_and_difficulties_height = true; // if empty alt chain passed (not sure how that could happen), return false CHECK_AND_ASSERT_MES(alt_chain.size(), false, "switch_to_alternative_blockchain: empty chain passed"); @@ -1044,10 +1234,15 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<block_extended_info> reorg_notify->notify("%s", std::to_string(split_height).c_str(), "%h", std::to_string(m_db->height()).c_str(), "%n", std::to_string(m_db->height() - split_height).c_str(), "%d", std::to_string(discarded_blocks).c_str(), NULL); - std::shared_ptr<tools::Notify> block_notify = m_block_notify; - if (block_notify) - for (const auto &bei: alt_chain) - block_notify->notify("%s", epee::string_tools::pod_to_hex(get_block_hash(bei.bl)).c_str(), NULL); + for (const auto& notifier : m_block_notifiers) + { + std::size_t notify_height = split_height; + for (const auto& bei: alt_chain) + { + notifier(notify_height, {std::addressof(bei.bl), 1}); + ++notify_height; + } + } MGINFO_GREEN("REORGANIZE SUCCESS! on height: " << split_height << ", new blockchain size: " << m_db->height()); return true; @@ -1639,6 +1834,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); m_timestamps_and_difficulties_height = 0; + m_reset_timestamps_and_difficulties_height = true; uint64_t block_height = get_block_height(b); if(0 == block_height) { @@ -2493,6 +2689,7 @@ bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, cons } db_rtxn_guard rtxn_guard(m_db); + total_height = get_current_blockchain_height(); blocks.reserve(std::min(std::min(max_count, (size_t)10000), (size_t)(total_height - start_height))); CHECK_AND_ASSERT_MES(m_db->get_blocks_from(start_height, 3, max_count, FIND_BLOCKCHAIN_SUPPLEMENT_MAX_SIZE, blocks, pruned, true, get_miner_tx_hash), false, "Error getting blocks"); @@ -4044,12 +4241,9 @@ leave: get_difficulty_for_next_block(); // just to cache it invalidate_block_template_cache(); - if (notify) - { - std::shared_ptr<tools::Notify> block_notify = m_block_notify; - if (block_notify) - block_notify->notify("%s", epee::string_tools::pod_to_hex(id).c_str(), NULL); - } + + for (const auto& notifier: m_block_notifiers) + notifier(new_height - 1, {std::addressof(bl), 1}); return true; } @@ -4317,7 +4511,14 @@ bool Blockchain::cleanup_handle_incoming_blocks(bool force_sync) try { if (m_batch_success) + { m_db->batch_stop(); + if (m_reset_timestamps_and_difficulties_height) + { + m_timestamps_and_difficulties_height = 0; + m_reset_timestamps_and_difficulties_height = false; + } + } else m_db->batch_abort(); success = true; @@ -4933,6 +5134,15 @@ void Blockchain::set_user_options(uint64_t maxthreads, bool sync_on_blocks, uint m_max_prepare_blocks_threads = maxthreads; } +void Blockchain::add_block_notify(boost::function<void(std::uint64_t, epee::span<const block>)>&& notify) +{ + if (notify) + { + CRITICAL_REGION_LOCAL(m_blockchain_lock); + m_block_notifiers.push_back(std::move(notify)); + } +} + void Blockchain::safesyncmode(const bool onoff) { /* all of this is no-op'd if the user set a specific @@ -5028,7 +5238,7 @@ void Blockchain::cancel() } #if defined(PER_BLOCK_CHECKPOINT) -static const char expected_block_hashes_hash[] = "fce1dc7c17f7679f5f447df206b8f5fe2ef6b1a2845e59f650850a0ef00d265f"; +static const char expected_block_hashes_hash[] = "8b48d259d4b1126801b1f329683a26e1d16237420197cd3ccc76af2c55a36e83"; void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints) { if (get_checkpoints == nullptr || !m_fast_sync) diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 3a89cc5df..703dd6400 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -30,6 +30,7 @@ #pragma once #include <boost/asio/io_service.hpp> +#include <boost/function/function_fwd.hpp> #include <boost/serialization/serialization.hpp> #include <boost/serialization/version.hpp> #include <boost/serialization/list.hpp> @@ -310,6 +311,22 @@ namespace cryptonote difficulty_type get_difficulty_for_next_block(); /** + * @brief check currently stored difficulties against difficulty checkpoints + * + * @return {flag, height} flag: true if all difficulty checkpoints pass, height: the last checkpoint height before the difficulty drift bug starts + */ + std::pair<bool, uint64_t> check_difficulty_checkpoints() const; + + /** + * @brief recalculate difficulties for blocks after the last difficulty checkpoints to circumvent the annoying 'difficulty drift' bug + * + * @param start_height: if omitted, starts recalculation from the last difficulty checkpoint + * + * @return number of blocks whose difficulties got corrected + */ + size_t recalculate_difficulties(boost::optional<uint64_t> start_height = boost::none); + + /** * @brief adds a block to the blockchain * * Adds a new block to the blockchain. If the block's parent is not the @@ -748,7 +765,7 @@ namespace cryptonote * * @param notify the notify object to call at every new block */ - void set_block_notify(const std::shared_ptr<tools::Notify> ¬ify) { m_block_notify = notify; } + void add_block_notify(boost::function<void(std::uint64_t, epee::span<const block>)> &¬ify); /** * @brief sets a reorg notify object to call for every reorg @@ -1067,6 +1084,7 @@ namespace cryptonote std::vector<uint64_t> m_timestamps; std::vector<difficulty_type> m_difficulties; uint64_t m_timestamps_and_difficulties_height; + bool m_reset_timestamps_and_difficulties_height; uint64_t m_long_term_block_weights_window; uint64_t m_long_term_effective_median_block_weight; mutable crypto::hash m_long_term_block_weights_cache_tip_hash; @@ -1108,7 +1126,11 @@ namespace cryptonote bool m_batch_success; - std::shared_ptr<tools::Notify> m_block_notify; + /* `boost::function` is used because the implementation never allocates if + the callable object has a single `std::shared_ptr` or `std::weap_ptr` + internally. Whereas, the libstdc++ `std::function` will allocate. */ + + std::vector<boost::function<void(std::uint64_t, epee::span<const block>)>> m_block_notifiers; std::shared_ptr<tools::Notify> m_reorg_notify; // for prepare_handle_incoming_blocks diff --git a/src/cryptonote_core/blockchain_storage_boost_serialization.h b/src/cryptonote_core/blockchain_storage_boost_serialization.h index cfeb5acfc..b5c14d53c 100644 --- a/src/cryptonote_core/blockchain_storage_boost_serialization.h +++ b/src/cryptonote_core/blockchain_storage_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 521734bd2..8e30d6676 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -41,6 +41,7 @@ using namespace epee; #include "common/download.h" #include "common/threadpool.h" #include "common/command_line.h" +#include "cryptonote_basic/events.h" #include "warnings.h" #include "crypto/crypto.h" #include "cryptonote_config.h" @@ -51,6 +52,7 @@ using namespace epee; #include "ringct/rctTypes.h" #include "blockchain_db/blockchain_db.h" #include "ringct/rctSigs.h" +#include "rpc/zmq_pub.h" #include "common/notify.h" #include "hardforks/hardforks.h" #include "version.h" @@ -262,15 +264,22 @@ namespace cryptonote { m_blockchain_storage.set_enforce_dns_checkpoints(enforce_dns); } + //----------------------------------------------------------------------------------- + void core::set_txpool_listener(boost::function<void(std::vector<txpool_event>)> zmq_pub) + { + CRITICAL_REGION_LOCAL(m_incoming_tx_lock); + m_zmq_pub = std::move(zmq_pub); + } + //----------------------------------------------------------------------------------------------- - bool core::update_checkpoints() + bool core::update_checkpoints(const bool skip_dns /* = false */) { if (m_nettype != MAINNET || m_disable_dns_checkpoints) return true; if (m_checkpoints_updating.test_and_set()) return true; bool res = true; - if (time(NULL) - m_last_dns_checkpoints_update >= 3600) + if (!skip_dns && time(NULL) - m_last_dns_checkpoints_update >= 3600) { res = m_blockchain_storage.update_checkpoints(m_checkpoints_path, true); m_last_dns_checkpoints_update = time(NULL); @@ -614,7 +623,20 @@ namespace cryptonote try { if (!command_line::is_arg_defaulted(vm, arg_block_notify)) - m_blockchain_storage.set_block_notify(std::shared_ptr<tools::Notify>(new tools::Notify(command_line::get_arg(vm, arg_block_notify).c_str()))); + { + struct hash_notify + { + tools::Notify cmdline; + + void operator()(std::uint64_t, epee::span<const block> blocks) const + { + for (const block bl : blocks) + cmdline.notify("%s", epee::string_tools::pod_to_hex(get_block_hash(bl)).c_str(), NULL); + } + }; + + m_blockchain_storage.add_block_notify(hash_notify{{command_line::get_arg(vm, arg_block_notify).c_str()}}); + } } catch (const std::exception &e) { @@ -650,7 +672,7 @@ namespace cryptonote r = m_blockchain_storage.init(db.release(), m_nettype, m_offline, regtest ? ®test_test_options : test_options, fixed_difficulty, get_checkpoints); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage"); - r = m_mempool.init(max_txpool_weight); + r = m_mempool.init(max_txpool_weight, m_nettype == FAKECHAIN); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool"); // now that we have a valid m_blockchain_storage, we can clean out any @@ -669,7 +691,8 @@ namespace cryptonote // load json & DNS checkpoints, and verify them // 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."); + const bool skip_dns_checkpoints = !command_line::get_arg(vm, arg_dns_checkpoints); + CHECK_AND_ASSERT_MES(update_checkpoints(skip_dns_checkpoints), false, "One or more checkpoints loaded from json or dns conflicted with existing checkpoints."); // DNS versions checking if (check_updates_string == "disabled") @@ -956,8 +979,7 @@ namespace cryptonote return false; } - struct result { bool res; cryptonote::transaction tx; crypto::hash hash; }; - std::vector<result> results(tx_blobs.size()); + std::vector<txpool_event> results(tx_blobs.size()); CRITICAL_REGION_LOCAL(m_incoming_tx_lock); @@ -1022,6 +1044,7 @@ namespace cryptonote if (!tx_info.empty()) handle_incoming_tx_accumulated_batch(tx_info, tx_relay == relay_method::block); + bool valid_events = false; bool ok = true; it = tx_blobs.begin(); for (size_t i = 0; i < tx_blobs.size(); i++, ++it) { @@ -1044,10 +1067,18 @@ namespace cryptonote {MERROR_VER("Transaction verification impossible: " << results[i].hash);} if(tvc[i].m_added_to_pool) + { MDEBUG("tx added: " << results[i].hash); + valid_events = true; + } + else + results[i].res = false; } - return ok; + if (valid_events && m_zmq_pub && matches_category(tx_relay, relay_category::legacy)) + m_zmq_pub(std::move(results)); + + return ok; CATCH_ENTRY_L0("core::handle_incoming_txs()", false); } //----------------------------------------------------------------------------------------------- @@ -1314,9 +1345,9 @@ namespace cryptonote std::vector<crypto::hash> tx_hashes{}; tx_hashes.resize(tx_blobs.size()); - cryptonote::transaction tx{}; for (std::size_t i = 0; i < tx_blobs.size(); ++i) { + cryptonote::transaction tx{}; if (!parse_and_validate_tx_from_blob(tx_blobs[i], tx, tx_hashes[i])) { LOG_ERROR("Failed to parse relayed transaction"); @@ -1661,42 +1692,17 @@ namespace cryptonote m_starter_message_showed = true; } - 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_check_disk_space_interval.do_call(boost::bind(&core::check_disk_space, this)); m_block_rate_interval.do_call(boost::bind(&core::check_block_rate, this)); m_blockchain_pruning_interval.do_call(boost::bind(&core::update_blockchain_pruning, this)); + m_diff_recalc_interval.do_call(boost::bind(&core::recalculate_difficulties, this)); m_miner.on_idle(); m_mempool.on_idle(); return true; } //----------------------------------------------------------------------------------------------- - bool core::check_fork_time() - { - if (m_nettype == FAKECHAIN) - return true; - - HardFork::State state = m_blockchain_storage.get_hard_fork_state(); - el::Level level; - switch (state) { - case HardFork::LikelyForked: - level = el::Level::Warning; - MCLOG_RED(level, "global", "**********************************************************************"); - MCLOG_RED(level, "global", "Last scheduled hard fork is too far in the past."); - MCLOG_RED(level, "global", "We are most likely forked from the network. Daemon update needed now."); - MCLOG_RED(level, "global", "**********************************************************************"); - break; - case HardFork::UpdateNeeded: - level = el::Level::Info; - MCLOG(level, "global", el::Color::Default, "Last scheduled hard fork time suggests a daemon update will be released within the next couple months."); - break; - default: - break; - } - return true; - } - //----------------------------------------------------------------------------------------------- uint8_t core::get_ideal_hard_fork_version() const { return get_blockchain_storage().get_ideal_hard_fork_version(); @@ -1930,6 +1936,12 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- + bool core::recalculate_difficulties() + { + m_blockchain_storage.recalculate_difficulties(); + return true; + } + //----------------------------------------------------------------------------------------------- void core::flush_bad_txs_cache() { bad_semantics_txes_lock.lock(); diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 255645efc..a53596c2c 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -32,9 +32,11 @@ #include <ctime> +#include <boost/function.hpp> #include <boost/program_options/options_description.hpp> #include <boost/program_options/variables_map.hpp> +#include "cryptonote_basic/fwd.h" #include "cryptonote_core/i_core_events.h" #include "cryptonote_protocol/cryptonote_protocol_handler_common.h" #include "cryptonote_protocol/enums.h" @@ -48,6 +50,7 @@ #include "warnings.h" #include "crypto/hash.h" #include "span.h" +#include "rpc/fwd.h" PUSH_WARNINGS DISABLE_VS_WARNINGS(4355) @@ -446,6 +449,13 @@ namespace cryptonote void set_enforce_dns_checkpoints(bool enforce_dns); /** + * @brief set a listener for txes being added to the txpool + * + * @param callable to notify, or empty function to disable. + */ + void set_txpool_listener(boost::function<void(std::vector<txpool_event>)> zmq_pub); + + /** * @brief set whether or not to enable or disable DNS checkpoints * * @param disble whether to disable DNS checkpoints @@ -699,7 +709,7 @@ namespace cryptonote * * @note see Blockchain::update_checkpoints() */ - bool update_checkpoints(); + bool update_checkpoints(const bool skip_dns = false); /** * @brief tells the daemon to wind down operations and stop running @@ -1001,18 +1011,6 @@ namespace cryptonote 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 - * the daemon is necessary. - * - * @note see Blockchain::get_hard_fork_state and HardFork::State - * - * @return true - */ - bool check_fork_time(); - - /** * @brief attempts to relay any transactions in the mempool which need it * * @return true @@ -1040,6 +1038,13 @@ namespace cryptonote */ bool check_block_rate(); + /** + * @brief recalculate difficulties after the last difficulty checklpoint to circumvent the annoying 'difficulty drift' bug + * + * @return true + */ + bool recalculate_difficulties(); + bool m_test_drop_download = true; //!< whether or not to drop incoming blocks (for testing) uint64_t m_test_drop_download_height = 0; //!< height under which to drop incoming blocks, if doing so @@ -1065,6 +1070,7 @@ namespace cryptonote epee::math_helper::once_a_time_seconds<60*10, true> m_check_disk_space_interval; //!< interval for checking for disk space epee::math_helper::once_a_time_seconds<90, false> m_block_rate_interval; //!< interval for checking block rate epee::math_helper::once_a_time_seconds<60*60*5, true> m_blockchain_pruning_interval; //!< interval for incremental blockchain pruning + epee::math_helper::once_a_time_seconds<60*60*24*7, false> m_diff_recalc_interval; //!< interval for recalculating difficulties std::atomic<bool> m_starter_message_showed; //!< has the "daemon will sync now" message been shown? @@ -1102,7 +1108,12 @@ namespace cryptonote bool m_fluffy_blocks_enabled; bool m_offline; + /* `boost::function` is used because the implementation never allocates if + the callable object has a single `std::shared_ptr` or `std::weap_ptr` + internally. Whereas, the libstdc++ `std::function` will allocate. */ + std::shared_ptr<tools::Notify> m_block_rate_notify; + boost::function<void(std::vector<txpool_event>)> m_zmq_pub; }; } diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index b84a59698..7ea7e81d9 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -590,7 +590,7 @@ namespace cryptonote tx.vout[i].amount = 0; crypto::hash tx_prefix_hash; - get_transaction_prefix_hash(tx, tx_prefix_hash); + get_transaction_prefix_hash(tx, tx_prefix_hash, hwdev); rct::ctkeyV outSk; if (use_simple_rct) tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, msout ? &kLRki : NULL, msout, index, outSk, rct_config, hwdev); diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h index 309e4177f..3622029e0 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.h +++ b/src/cryptonote_core/cryptonote_tx_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_core/i_core_events.h b/src/cryptonote_core/i_core_events.h index f49fbdf07..03394d785 100644 --- a/src/cryptonote_core/i_core_events.h +++ b/src/cryptonote_core/i_core_events.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 542dbeb03..7cb0e4062 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -118,7 +118,7 @@ namespace cryptonote } //--------------------------------------------------------------------------------- //--------------------------------------------------------------------------------- - tx_memory_pool::tx_memory_pool(Blockchain& bchs): m_blockchain(bchs), m_txpool_max_weight(DEFAULT_TXPOOL_MAX_WEIGHT), m_txpool_weight(0), m_cookie(0) + tx_memory_pool::tx_memory_pool(Blockchain& bchs): m_blockchain(bchs), m_cookie(0), m_txpool_max_weight(DEFAULT_TXPOOL_MAX_WEIGHT), m_txpool_weight(0), m_mine_stem_txes(false) { } @@ -623,8 +623,8 @@ namespace cryptonote CRITICAL_REGION_LOCAL1(m_blockchain); m_blockchain.for_all_txpool_txes([this, &hashes, &txes](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata*) { - const auto relay_method = meta.get_relay_method(); - if (relay_method != relay_method::block && relay_method != relay_method::fluff) + const auto tx_relay_method = meta.get_relay_method(); + if (tx_relay_method != relay_method::block && tx_relay_method != relay_method::fluff) return true; const auto i = std::find(hashes.begin(), hashes.end(), txid); if (i == hashes.end()) @@ -1159,7 +1159,7 @@ namespace cryptonote // See `insert_key_images`. if (1 < found->second.size() || *(found->second.cbegin()) != txid) return true; - return m_blockchain.txpool_tx_matches_category(txid, relay_category::broadcasted); + return m_blockchain.txpool_tx_matches_category(txid, relay_category::legacy); } return false; } @@ -1389,13 +1389,18 @@ namespace cryptonote for (; sorted_it != m_txs_by_fee_and_receive_time.end(); ++sorted_it) { txpool_tx_meta_t meta; - if (!m_blockchain.get_txpool_tx_meta(sorted_it->second, meta) && !meta.matches(relay_category::legacy)) + if (!m_blockchain.get_txpool_tx_meta(sorted_it->second, meta)) { MERROR(" failed to find tx meta"); continue; } - LOG_PRINT_L2("Considering " << sorted_it->second << ", weight " << meta.weight << ", current block weight " << total_weight << "/" << max_total_weight << ", current coinbase " << print_money(best_coinbase)); + LOG_PRINT_L2("Considering " << sorted_it->second << ", weight " << meta.weight << ", current block weight " << total_weight << "/" << max_total_weight << ", current coinbase " << print_money(best_coinbase) << ", relay method " << (unsigned)meta.get_relay_method()); + if (!meta.matches(relay_category::legacy) && !(m_mine_stem_txes && meta.get_relay_method() == relay_method::stem)) + { + LOG_PRINT_L2(" tx relay method is " << (unsigned)meta.get_relay_method()); + continue; + } if (meta.pruned) { LOG_PRINT_L2(" tx is pruned"); @@ -1560,7 +1565,7 @@ namespace cryptonote return n_removed; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::init(size_t max_txpool_weight) + bool tx_memory_pool::init(size_t max_txpool_weight, bool mine_stem_txes) { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); @@ -1616,6 +1621,7 @@ namespace cryptonote lock.commit(); } + m_mine_stem_txes = mine_stem_txes; m_cookie = 0; // Ignore deserialization error diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 292d427e2..8955d7551 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -205,10 +205,11 @@ namespace cryptonote * @brief loads pool state (if any) from disk, and initializes pool * * @param max_txpool_weight the max weight in bytes + * @param mine_stem_txes whether to mine txes in stem relay mode * * @return true */ - bool init(size_t max_txpool_weight = 0); + bool init(size_t max_txpool_weight = 0, bool mine_stem_txes = false); /** * @brief attempts to save the transaction pool state to disk @@ -603,6 +604,7 @@ private: size_t m_txpool_max_weight; size_t m_txpool_weight; + bool m_mine_stem_txes; mutable std::unordered_map<crypto::hash, std::tuple<bool, tx_verification_context, uint64_t, crypto::hash>> m_input_cache; diff --git a/src/cryptonote_core/tx_sanity_check.cpp b/src/cryptonote_core/tx_sanity_check.cpp index e99982def..ca870779e 100644 --- a/src/cryptonote_core/tx_sanity_check.cpp +++ b/src/cryptonote_core/tx_sanity_check.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_core/tx_sanity_check.h b/src/cryptonote_core/tx_sanity_check.h index 4a469462f..15c5476ee 100644 --- a/src/cryptonote_core/tx_sanity_check.h +++ b/src/cryptonote_core/tx_sanity_check.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_protocol/CMakeLists.txt b/src/cryptonote_protocol/CMakeLists.txt index bfcf42767..d28b44bb7 100644 --- a/src/cryptonote_protocol/CMakeLists.txt +++ b/src/cryptonote_protocol/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/cryptonote_protocol/block_queue.cpp b/src/cryptonote_protocol/block_queue.cpp index 67f0b3e5d..bbde91c1f 100644 --- a/src/cryptonote_protocol/block_queue.cpp +++ b/src/cryptonote_protocol/block_queue.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_protocol/block_queue.h b/src/cryptonote_protocol/block_queue.h index 93c6532e7..57d2a6490 100644 --- a/src/cryptonote_protocol/block_queue.h +++ b/src/cryptonote_protocol/block_queue.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h index 76b57afd3..9687b07a6 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_defs.h +++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp b/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp index 225418980..42774f2cb 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp +++ b/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp @@ -2,7 +2,7 @@ /// @author rfree (current maintainer in monero.cc project) /// @brief This is the place to implement our handlers for protocol network actions, e.g. for ratelimit for download-requests -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index e2ad3727f..89860fe41 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -2,7 +2,7 @@ /// @author rfree (current maintainer/user in monero.cc project - most of code is from CryptoNote) /// @brief This is the original cryptonote protocol network-events handler, modified by us -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -51,7 +51,8 @@ PUSH_WARNINGS DISABLE_VS_WARNINGS(4355) #define LOCALHOST_INT 2130706433 -#define CURRENCY_PROTOCOL_MAX_OBJECT_REQUEST_COUNT 500 +#define CURRENCY_PROTOCOL_MAX_OBJECT_REQUEST_COUNT 100 +static_assert(CURRENCY_PROTOCOL_MAX_OBJECT_REQUEST_COUNT >= BLOCKS_SYNCHRONIZING_DEFAULT_COUNT_PRE_V4, "Invalid CURRENCY_PROTOCOL_MAX_OBJECT_REQUEST_COUNT"); namespace cryptonote { diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index ef3ea837d..bd14fe0a7 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -2,7 +2,7 @@ /// @author rfree (current maintainer/user in monero.cc project - most of code is from CryptoNote) /// @brief This is the original cryptonote protocol network-events handler, modified by us -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -308,9 +308,9 @@ namespace cryptonote if (version >= 6 && version != hshd.top_version) { if (version < hshd.top_version && version == m_core.get_ideal_hard_fork_version()) - MCLOG_RED(el::Level::Warning, "global", context << " peer claims higher version than we think (" << + MDEBUG(context << " peer claims higher version than we think (" << (unsigned)hshd.top_version << " for " << (hshd.current_height - 1) << " instead of " << (unsigned)version << - ") - we may be forked from the network and a software upgrade may be needed"); + ") - we may be forked from the network and a software upgrade may be needed, or that peer is broken or malicious"); return false; } } @@ -793,6 +793,12 @@ 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); + if (context.m_state == cryptonote_connection_context::state_before_handshake) + { + LOG_ERROR_CCONTEXT("Requested fluffy tx before handshake, dropping connection"); + drop_connection(context, false, false); + return 1; + } std::vector<std::pair<cryptonote::blobdata, block>> local_blocks; std::vector<cryptonote::blobdata> local_txs; @@ -884,6 +890,8 @@ namespace cryptonote int t_cryptonote_protocol_handler<t_core>::handle_notify_get_txpool_complement(int command, NOTIFY_GET_TXPOOL_COMPLEMENT::request& arg, cryptonote_connection_context& context) { MLOG_P2P_MESSAGE("Received NOTIFY_GET_TXPOOL_COMPLEMENT (" << arg.hashes.size() << " txes)"); + if(context.m_state != cryptonote_connection_context::state_normal) + return 1; std::vector<std::pair<cryptonote::blobdata, block>> local_blocks; std::vector<cryptonote::blobdata> local_txs; @@ -997,6 +1005,12 @@ namespace cryptonote template<class t_core> int t_cryptonote_protocol_handler<t_core>::handle_request_get_objects(int command, NOTIFY_REQUEST_GET_OBJECTS::request& arg, cryptonote_connection_context& context) { + if (context.m_state == cryptonote_connection_context::state_before_handshake) + { + LOG_ERROR_CCONTEXT("Requested objects before handshake, dropping connection"); + drop_connection(context, false, false); + return 1; + } MLOG_P2P_MESSAGE("Received NOTIFY_REQUEST_GET_OBJECTS (" << arg.blocks.size() << " blocks)"); if (arg.blocks.size() > CURRENCY_PROTOCOL_MAX_OBJECT_REQUEST_COUNT) { @@ -1727,6 +1741,12 @@ skip: int t_cryptonote_protocol_handler<t_core>::handle_request_chain(int command, NOTIFY_REQUEST_CHAIN::request& arg, cryptonote_connection_context& context) { MLOG_P2P_MESSAGE("Received NOTIFY_REQUEST_CHAIN (" << arg.block_ids.size() << " blocks"); + if (context.m_state == cryptonote_connection_context::state_before_handshake) + { + LOG_ERROR_CCONTEXT("Requested chain before handshake, dropping connection"); + drop_connection(context, false, false); + return 1; + } NOTIFY_RESPONSE_CHAIN_ENTRY::request r; if(!m_core.find_blockchain_supplement(arg.block_ids, !arg.prune, r)) { @@ -1917,6 +1937,10 @@ skip: const uint32_t local_stripe = tools::get_pruning_stripe(m_core.get_blockchain_pruning_seed()); if (local_stripe == 0) return false; + // don't request pre-bulletprooof pruned blocks, we can't reconstruct their weight (yet) + static const uint64_t bp_fork_height = m_core.get_earliest_ideal_height_for_version(8); + if (first_block_height + nblocks - 1 < bp_fork_height) + return false; // assumes the span size is less or equal to the stripe size bool full_data_needed = tools::get_pruning_stripe(first_block_height, context.m_remote_blockchain_height, CRYPTONOTE_PRUNING_LOG_STRIPES) == local_stripe || tools::get_pruning_stripe(first_block_height + nblocks - 1, context.m_remote_blockchain_height, CRYPTONOTE_PRUNING_LOG_STRIPES) == local_stripe; @@ -2093,7 +2117,8 @@ skip: skip_unneeded_hashes(context, false); const uint64_t first_block_height = context.m_last_response_height - context.m_needed_objects.size() + 1; - bool sync_pruned_blocks = m_sync_pruned_blocks && m_core.get_blockchain_pruning_seed(); + static const uint64_t bp_fork_height = m_core.get_earliest_ideal_height_for_version(8); + bool sync_pruned_blocks = m_sync_pruned_blocks && first_block_height >= bp_fork_height && m_core.get_blockchain_pruning_seed(); span = m_block_queue.reserve_span(first_block_height, context.m_last_response_height, count_limit, context.m_connection_id, sync_pruned_blocks, m_core.get_blockchain_pruning_seed(), context.m_pruning_seed, context.m_remote_blockchain_height, context.m_needed_objects); MDEBUG(context << " span from " << first_block_height << ": " << span.first << "/" << span.second); if (span.second > 0) diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h index 11184299d..1c7635fd8 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_protocol/enums.h b/src/cryptonote_protocol/enums.h index 36e8ca04c..c0c495837 100644 --- a/src/cryptonote_protocol/enums.h +++ b/src/cryptonote_protocol/enums.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_protocol/fwd.h b/src/cryptonote_protocol/fwd.h index 616b48be3..e7722c2f4 100644 --- a/src/cryptonote_protocol/fwd.h +++ b/src/cryptonote_protocol/fwd.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_protocol/levin_notify.cpp b/src/cryptonote_protocol/levin_notify.cpp index 0ff194986..7c482156f 100644 --- a/src/cryptonote_protocol/levin_notify.cpp +++ b/src/cryptonote_protocol/levin_notify.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_protocol/levin_notify.h b/src/cryptonote_protocol/levin_notify.h index 641f1f956..957794b12 100644 --- a/src/cryptonote_protocol/levin_notify.h +++ b/src/cryptonote_protocol/levin_notify.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt index d9bfd9a20..b95c0ac88 100644 --- a/src/daemon/CMakeLists.txt +++ b/src/daemon/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/daemon/command_line_args.h b/src/daemon/command_line_args.h index d089d4e47..6c3e163e6 100644 --- a/src/daemon/command_line_args.h +++ b/src/daemon/command_line_args.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -121,6 +121,10 @@ namespace daemon_args return val; } }; + const command_line::arg_descriptor<std::vector<std::string>> arg_zmq_pub = { + "zmq-pub" + , "Address for ZMQ pub - tcp://ip:port or ipc://path" + }; const command_line::arg_descriptor<bool> arg_zmq_rpc_disabled = { "no-zmq" diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index 3a4081e39..504b104b0 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemon/command_parser_executor.h b/src/daemon/command_parser_executor.h index 8b85dcf69..64e4c301b 100644 --- a/src/daemon/command_parser_executor.h +++ b/src/daemon/command_parser_executor.h @@ -6,7 +6,7 @@ */ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp index 7fae77c30..b335116de 100644 --- a/src/daemon/command_server.cpp +++ b/src/daemon/command_server.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemon/command_server.h b/src/daemon/command_server.h index da532223e..946d55b8c 100644 --- a/src/daemon/command_server.h +++ b/src/daemon/command_server.h @@ -9,7 +9,7 @@ Passing RPC commands: */ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemon/core.h b/src/daemon/core.h index 9a3579e20..804d7474d 100644 --- a/src/daemon/core.h +++ b/src/daemon/core.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 056f2f320..99430b2b0 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -34,16 +34,17 @@ #include "misc_log_ex.h" #include "daemon/daemon.h" #include "rpc/daemon_handler.h" +#include "rpc/zmq_pub.h" #include "rpc/zmq_server.h" #include "common/password.h" #include "common/util.h" +#include "cryptonote_basic/events.h" #include "daemon/core.h" #include "daemon/p2p.h" #include "daemon/protocol.h" #include "daemon/rpc.h" #include "daemon/command_server.h" -#include "daemon/command_server.h" #include "daemon/command_line_args.h" #include "net/net_ssl.h" #include "version.h" @@ -57,6 +58,17 @@ using namespace epee; namespace daemonize { +struct zmq_internals +{ + explicit zmq_internals(t_core& core, t_p2p& p2p) + : rpc_handler{core.get(), p2p.get()} + , server{rpc_handler} + {} + + cryptonote::rpc::DaemonHandler rpc_handler; + cryptonote::rpc::ZmqServer server; +}; + struct t_internals { private: t_protocol protocol; @@ -64,6 +76,7 @@ public: t_core core; t_p2p p2p; std::vector<std::unique_ptr<t_rpc>> rpcs; + std::unique_ptr<zmq_internals> zmq; t_internals( boost::program_options::variables_map const & vm @@ -71,6 +84,7 @@ public: : core{vm} , protocol{vm, core, command_line::get_arg(vm, cryptonote::arg_offline)} , p2p{vm, protocol} + , zmq{nullptr} { // Handle circular dependencies protocol.set_p2p_endpoint(p2p.get()); @@ -87,6 +101,28 @@ public: auto restricted_rpc_port = command_line::get_arg(vm, restricted_rpc_port_arg); rpcs.emplace_back(new t_rpc{vm, core, p2p, true, restricted_rpc_port, "restricted", true}); } + + if (!command_line::get_arg(vm, daemon_args::arg_zmq_rpc_disabled)) + { + zmq.reset(new zmq_internals{core, p2p}); + + const std::string zmq_port = command_line::get_arg(vm, daemon_args::arg_zmq_rpc_bind_port); + const std::string zmq_address = command_line::get_arg(vm, daemon_args::arg_zmq_rpc_bind_ip); + + if (!zmq->server.init_rpc(zmq_address, zmq_port)) + throw std::runtime_error{"Failed to add TCP socket(" + zmq_address + ":" + zmq_port + ") to ZMQ RPC Server"}; + + std::shared_ptr<cryptonote::listener::zmq_pub> shared; + const std::vector<std::string> zmq_pub = command_line::get_arg(vm, daemon_args::arg_zmq_pub); + if (!zmq_pub.empty() && !(shared = zmq->server.init_pub(epee::to_span(zmq_pub)))) + throw std::runtime_error{"Failed to initialize zmq_pub"}; + + if (shared) + { + core.get().get_blockchain_storage().add_block_notify(cryptonote::listener::zmq_pub::chain_main{shared}); + core.get().set_txpool_listener(cryptonote::listener::zmq_pub::txpool_add{shared}); + } + } } }; @@ -104,9 +140,6 @@ t_daemon::t_daemon( : mp_internals{new t_internals{vm}}, public_rpc_port(public_rpc_port) { - zmq_rpc_bind_port = command_line::get_arg(vm, daemon_args::arg_zmq_rpc_bind_port); - zmq_rpc_bind_address = command_line::get_arg(vm, daemon_args::arg_zmq_rpc_bind_ip); - zmq_rpc_disabled = command_line::get_arg(vm, daemon_args::arg_zmq_rpc_disabled); } t_daemon::~t_daemon() = default; @@ -170,31 +203,8 @@ bool t_daemon::run(bool interactive) rpc_commands->start_handling(std::bind(&daemonize::t_daemon::stop_p2p, this)); } - cryptonote::rpc::DaemonHandler rpc_daemon_handler(mp_internals->core.get(), mp_internals->p2p.get()); - cryptonote::rpc::ZmqServer zmq_server(rpc_daemon_handler); - - if (!zmq_rpc_disabled) - { - if (!zmq_server.addTCPSocket(zmq_rpc_bind_address, zmq_rpc_bind_port)) - { - LOG_ERROR(std::string("Failed to add TCP Socket (") + zmq_rpc_bind_address - + ":" + zmq_rpc_bind_port + ") to ZMQ RPC Server"); - - if (rpc_commands) - rpc_commands->stop_handling(); - - for(auto& rpc : mp_internals->rpcs) - rpc->stop(); - - return false; - } - - MINFO("Starting ZMQ server..."); - zmq_server.run(); - - MINFO(std::string("ZMQ server started at ") + zmq_rpc_bind_address - + ":" + zmq_rpc_bind_port + "."); - } + if (mp_internals->zmq) + mp_internals->zmq->server.run(); else MINFO("ZMQ server disabled"); @@ -209,8 +219,8 @@ bool t_daemon::run(bool interactive) if (rpc_commands) rpc_commands->stop_handling(); - if (!zmq_rpc_disabled) - zmq_server.stop(); + if (mp_internals->zmq) + mp_internals->zmq->server.stop(); for(auto& rpc : mp_internals->rpcs) rpc->stop(); diff --git a/src/daemon/daemon.h b/src/daemon/daemon.h index c0efb68ee..2eb2019ce 100644 --- a/src/daemon/daemon.h +++ b/src/daemon/daemon.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -44,9 +44,6 @@ private: private: std::unique_ptr<t_internals> mp_internals; uint16_t public_rpc_port; - std::string zmq_rpc_bind_address; - std::string zmq_rpc_bind_port; - bool zmq_rpc_disabled; public: t_daemon( boost::program_options::variables_map const & vm, diff --git a/src/daemon/executor.cpp b/src/daemon/executor.cpp index 3719a253d..f9ba3b493 100644 --- a/src/daemon/executor.cpp +++ b/src/daemon/executor.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemon/executor.h b/src/daemon/executor.h index 61e4e1bbf..a7235711c 100644 --- a/src/daemon/executor.h +++ b/src/daemon/executor.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp index 3e25636d8..f2ae6dcc3 100644 --- a/src/daemon/main.cpp +++ b/src/daemon/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -107,6 +107,20 @@ uint16_t parse_public_rpc_port(const po::variables_map &vm) return rpc_port; } +#ifdef WIN32 +bool isFat32(const wchar_t* root_path) +{ + std::vector<wchar_t> fs(MAX_PATH + 1); + if (!::GetVolumeInformationW(root_path, nullptr, 0, nullptr, 0, nullptr, &fs[0], MAX_PATH)) + { + MERROR("Failed to get '" << root_path << "' filesystem name. Error code: " << ::GetLastError()); + return false; + } + + return wcscmp(L"FAT32", &fs[0]) == 0; +} +#endif + int main(int argc, char const * argv[]) { try { @@ -140,6 +154,7 @@ int main(int argc, char const * argv[]) command_line::add_arg(core_settings, daemon_args::arg_public_node); command_line::add_arg(core_settings, daemon_args::arg_zmq_rpc_bind_ip); command_line::add_arg(core_settings, daemon_args::arg_zmq_rpc_bind_port); + command_line::add_arg(core_settings, daemon_args::arg_zmq_pub); command_line::add_arg(core_settings, daemon_args::arg_zmq_rpc_disabled); daemonizer::init_options(hidden_options, visible_options); @@ -233,6 +248,13 @@ int main(int argc, char const * argv[]) boost::filesystem::path data_dir = boost::filesystem::absolute( command_line::get_arg(vm, cryptonote::arg_data_dir)); +#ifdef WIN32 + if (isFat32(data_dir.root_name().c_str())) + { + MERROR("Data directory resides on FAT32 volume that has 4GiB file size limit, blockchain might get corrupted."); + } +#endif + // FIXME: not sure on windows implementation default, needs further review //bf::path relative_path_base = daemonizer::get_relative_path_base(vm); bf::path relative_path_base = data_dir; diff --git a/src/daemon/p2p.h b/src/daemon/p2p.h index 267b99c89..f68efccc2 100644 --- a/src/daemon/p2p.h +++ b/src/daemon/p2p.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemon/protocol.h b/src/daemon/protocol.h index 51a2bce1f..6b03c169a 100644 --- a/src/daemon/protocol.h +++ b/src/daemon/protocol.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemon/rpc.h b/src/daemon/rpc.h index 6f545a3b7..af48bcc45 100644 --- a/src/daemon/rpc.h +++ b/src/daemon/rpc.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 034d49918..77dfe332f 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -511,7 +511,7 @@ bool t_rpc_command_executor::show_status() { } std::stringstream str; - str << boost::format("Height: %llu/%llu (%.1f%%) on %s%s, %s, net hash %s, v%u%s, %s, %u(out)+%u(in) connections") + str << boost::format("Height: %llu/%llu (%.1f%%) on %s%s, %s, net hash %s, v%u%s, %u(out)+%u(in) connections") % (unsigned long long)ires.height % (unsigned long long)net_height % get_sync_percentage(ires) @@ -521,7 +521,6 @@ bool t_rpc_command_executor::show_status() { % get_mining_speed(cryptonote::difficulty_type(ires.wide_difficulty) / ires.target) % (unsigned)hfres.version % get_fork_extra_info(hfres.earliest_height, net_height, ires.target) - % (hfres.state == cryptonote::HardFork::Ready ? "up to date" : hfres.state == cryptonote::HardFork::UpdateNeeded ? "update needed" : "out of date, likely forked") % (unsigned)ires.outgoing_connections_count % (unsigned)ires.incoming_connections_count ; diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h index 66ada6f1f..6fb5d6903 100644 --- a/src/daemon/rpc_command_executor.h +++ b/src/daemon/rpc_command_executor.h @@ -6,7 +6,7 @@ */ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemonizer/CMakeLists.txt b/src/daemonizer/CMakeLists.txt index d540adc10..462d40531 100644 --- a/src/daemonizer/CMakeLists.txt +++ b/src/daemonizer/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/daemonizer/daemonizer.h b/src/daemonizer/daemonizer.h index a45a2b44f..442861cdb 100644 --- a/src/daemonizer/daemonizer.h +++ b/src/daemonizer/daemonizer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemonizer/posix_daemonizer.inl b/src/daemonizer/posix_daemonizer.inl index 9ec09c678..76ef467ca 100644 --- a/src/daemonizer/posix_daemonizer.inl +++ b/src/daemonizer/posix_daemonizer.inl @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemonizer/posix_fork.h b/src/daemonizer/posix_fork.h index 27b4ac18d..d841df0fc 100644 --- a/src/daemonizer/posix_fork.h +++ b/src/daemonizer/posix_fork.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemonizer/windows_daemonizer.inl b/src/daemonizer/windows_daemonizer.inl index c6cd474fd..67c8a2855 100644 --- a/src/daemonizer/windows_daemonizer.inl +++ b/src/daemonizer/windows_daemonizer.inl @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemonizer/windows_service.cpp b/src/daemonizer/windows_service.cpp index 7d53b07c7..01be6b247 100644 --- a/src/daemonizer/windows_service.cpp +++ b/src/daemonizer/windows_service.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemonizer/windows_service.h b/src/daemonizer/windows_service.h index 12429c78a..091041391 100644 --- a/src/daemonizer/windows_service.h +++ b/src/daemonizer/windows_service.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemonizer/windows_service_runner.h b/src/daemonizer/windows_service_runner.h index 703795ce9..eb72df66d 100644 --- a/src/daemonizer/windows_service_runner.h +++ b/src/daemonizer/windows_service_runner.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/debug_utilities/CMakeLists.txt b/src/debug_utilities/CMakeLists.txt index 03c2b3e20..7b21123f6 100644 --- a/src/debug_utilities/CMakeLists.txt +++ b/src/debug_utilities/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/debug_utilities/cn_deserialize.cpp b/src/debug_utilities/cn_deserialize.cpp index 89c9db02e..dd4701e4f 100644 --- a/src/debug_utilities/cn_deserialize.cpp +++ b/src/debug_utilities/cn_deserialize.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/debug_utilities/dns_checks.cpp b/src/debug_utilities/dns_checks.cpp index 3c9daa769..76b66c6cb 100644 --- a/src/debug_utilities/dns_checks.cpp +++ b/src/debug_utilities/dns_checks.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/debug_utilities/object_sizes.cpp b/src/debug_utilities/object_sizes.cpp index 302f0a206..bffff0882 100644 --- a/src/debug_utilities/object_sizes.cpp +++ b/src/debug_utilities/object_sizes.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device/CMakeLists.txt b/src/device/CMakeLists.txt index 0b0686f61..42dba2ebb 100644 --- a/src/device/CMakeLists.txt +++ b/src/device/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/device/device.cpp b/src/device/device.cpp index fbd77dab9..4821abdcf 100644 --- a/src/device/device.cpp +++ b/src/device/device.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device/device.hpp b/src/device/device.hpp index 215e97eb6..ef973c9f4 100644 --- a/src/device/device.hpp +++ b/src/device/device.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // @@ -56,6 +56,7 @@ namespace cryptonote struct subaddress_index; struct tx_destination_entry; struct keypair; + class transaction_prefix; } namespace hw { @@ -78,7 +79,7 @@ namespace hw { virtual void on_button_request(uint64_t code=0) {} virtual void on_button_pressed() {} virtual boost::optional<epee::wipeable_string> on_pin_request() { return boost::none; } - virtual boost::optional<epee::wipeable_string> on_passphrase_request(bool on_device) { return boost::none; } + virtual boost::optional<epee::wipeable_string> on_passphrase_request(bool & on_device) { on_device = true; return boost::none; } virtual void on_progress(const device_progress& event) {} virtual ~i_device_callback() = default; }; @@ -203,6 +204,8 @@ namespace hw { virtual bool open_tx(crypto::secret_key &tx_key) = 0; + virtual void get_transaction_prefix_hash(const cryptonote::transaction_prefix& tx, crypto::hash& h) = 0; + virtual bool encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key) = 0; bool decrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key) { diff --git a/src/device/device_cold.hpp b/src/device/device_cold.hpp index 22708c46a..d435b448c 100644 --- a/src/device/device_cold.hpp +++ b/src/device/device_cold.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device/device_default.cpp b/src/device/device_default.cpp index 57ac7c1b2..7e054af35 100644 --- a/src/device/device_default.cpp +++ b/src/device/device_default.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // @@ -281,6 +281,10 @@ namespace hw { return true; } + void device_default::get_transaction_prefix_hash(const cryptonote::transaction_prefix& tx, crypto::hash& h) { + cryptonote::get_transaction_prefix_hash(tx, h); + } + bool device_default::generate_output_ephemeral_keys(const size_t tx_version, const cryptonote::account_keys &sender_account_keys, const crypto::public_key &txkey_pub, const crypto::secret_key &tx_key, const cryptonote::tx_destination_entry &dst_entr, const boost::optional<cryptonote::account_public_address> &change_addr, const size_t output_index, diff --git a/src/device/device_default.hpp b/src/device/device_default.hpp index 5252d4129..bdd99f89c 100644 --- a/src/device/device_default.hpp +++ b/src/device/device_default.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // @@ -112,6 +112,7 @@ namespace hw { crypto::signature &sig) override; bool open_tx(crypto::secret_key &tx_key) override; + void get_transaction_prefix_hash(const cryptonote::transaction_prefix& tx, crypto::hash& h) override; bool encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key) override; diff --git a/src/device/device_io.hpp b/src/device/device_io.hpp index fe66736f7..6a7744c11 100644 --- a/src/device/device_io.hpp +++ b/src/device/device_io.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device/device_io_hid.cpp b/src/device/device_io_hid.cpp index 840529c38..7c61c3b1a 100644 --- a/src/device/device_io_hid.cpp +++ b/src/device/device_io_hid.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device/device_io_hid.hpp b/src/device/device_io_hid.hpp index 96cb8d993..e6d76f276 100644 --- a/src/device/device_io_hid.hpp +++ b/src/device/device_io_hid.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index eaa9f910d..4bd3d75b1 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // @@ -259,7 +259,7 @@ namespace hw { static int device_id = 0; - #define PROTOCOL_VERSION 2 + #define PROTOCOL_VERSION 3 #define INS_NONE 0x00 #define INS_RESET 0x02 @@ -296,6 +296,7 @@ namespace hw { #define INS_BLIND 0x78 #define INS_UNBLIND 0x7A #define INS_GEN_TXOUT_KEYS 0x7B + #define INS_PREFIX_HASH 0x7D #define INS_VALIDATE 0x7C #define INS_MLSAG 0x7E #define INS_CLOSE_TX 0x80 @@ -1107,7 +1108,7 @@ namespace hw { for(size_t n=0; n < additional_derivations.size();++n) { if(derivation == additional_derivations[n]) { pkey = &additional_tx_pub_keys[n]; - MDEBUG("conceal derivation with additionnal tx pub key"); + MDEBUG("conceal derivation with additional tx pub key"); break; } } @@ -1414,6 +1415,81 @@ namespace hw { return true; } + void device_ledger::get_transaction_prefix_hash(const cryptonote::transaction_prefix& tx, crypto::hash& h) { + AUTO_LOCK_CMD(); + + int pref_length = 0, pref_offset = 0, offset = 0; + + #ifdef DEBUG_HWDEVICE + crypto::hash h_x; + this->controle_device->get_transaction_prefix_hash(tx,h_x); + MDEBUG("get_transaction_prefix_hash [[IN]] h_x/1 "<<h_x); + #endif + + std::ostringstream s_x; + binary_archive<true> a_x(s_x); + CHECK_AND_ASSERT_THROW_MES(::serialization::serialize(a_x, const_cast<cryptonote::transaction_prefix&>(tx)), + "unable to serialize transaction prefix"); + pref_length = s_x.str().size(); + //auto pref = std::make_unique<unsigned char[]>(pref_length); + auto uprt_pref = std::unique_ptr<unsigned char[]>{ new unsigned char[pref_length] }; + unsigned char* pref = uprt_pref.get(); + memmove(pref, s_x.str().data(), pref_length); + + offset = set_command_header_noopt(INS_PREFIX_HASH,1); + pref_offset = 0; + unsigned char v; + + //version as varint + do { + v = pref[pref_offset]; + this->buffer_send[offset] = v; + offset += 1; + pref_offset += 1; + } while (v&0x80); + + //locktime as var int + do { + v = pref[pref_offset]; + this->buffer_send[offset] = v; + offset += 1; + pref_offset += 1; + } while (v&0x80); + + this->buffer_send[4] = offset-5; + this->length_send = offset; + this->exchange_wait_on_input(); + + //hash remains + int cnt = 0; + while (pref_offset < pref_length) { + int len; + cnt++; + offset = set_command_header(INS_PREFIX_HASH,2,cnt); + len = pref_length - pref_offset; + //options + if (len > (BUFFER_SEND_SIZE-offset-3)) { + len = BUFFER_SEND_SIZE-offset-3; + this->buffer_send[offset] = 0x80; + } else { + this->buffer_send[offset] = 0x00; + } + offset += 1; + //send chunk + memmove(&this->buffer_send[offset], pref+pref_offset, len); + offset += len; + pref_offset += len; + this->buffer_send[4] = offset-5; + this->length_send = offset; + this->exchange(); + } + memmove(h.data, &this->buffer_recv[0], 32); + + #ifdef DEBUG_HWDEVICE + hw::ledger::check8("prefix_hash", "h", h_x.data, h.data); + #endif + } + bool device_ledger::encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key) { AUTO_LOCK_CMD(); @@ -1564,20 +1640,20 @@ namespace hw { //if (tx_version > 1) { - ASSERT_X(recv_len>=32, "Not enought data from device"); + ASSERT_X(recv_len>=32, "Not enough data from device"); crypto::secret_key scalar1; this->receive_secret((unsigned char*)scalar1.data, offset); amount_keys.push_back(rct::sk2rct(scalar1)); recv_len -= 32; } - ASSERT_X(recv_len>=32, "Not enought data from device"); + ASSERT_X(recv_len>=32, "Not enough data from device"); memmove(out_eph_public_key.data, &this->buffer_recv[offset], 32); recv_len -= 32; offset += 32; if (need_additional_txkeys) { - ASSERT_X(recv_len>=32, "Not enought data from device"); + ASSERT_X(recv_len>=32, "Not enough data from device"); memmove(additional_txkey.pub.data, &this->buffer_recv[offset], 32); additional_tx_public_keys.push_back(additional_txkey.pub); offset += 32; diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp index e3e30fba8..4036035c8 100644 --- a/src/device/device_ledger.hpp +++ b/src/device/device_ledger.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // @@ -275,6 +275,8 @@ namespace hw { bool open_tx(crypto::secret_key &tx_key) override; + void get_transaction_prefix_hash(const cryptonote::transaction_prefix& tx, crypto::hash& h) override; + bool encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key) override; rct::key genCommitmentMask(const rct::key &amount_key) override; diff --git a/src/device/log.cpp b/src/device/log.cpp index 616ad8e90..6e62f1dee 100644 --- a/src/device/log.cpp +++ b/src/device/log.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device/log.hpp b/src/device/log.hpp index bfe6e9edc..66c3e06db 100644 --- a/src/device/log.hpp +++ b/src/device/log.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device_trezor/CMakeLists.txt b/src/device_trezor/CMakeLists.txt index 250939da7..f105f68b7 100644 --- a/src/device_trezor/CMakeLists.txt +++ b/src/device_trezor/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/device_trezor/device_trezor.cpp b/src/device_trezor/device_trezor.cpp index a77f6697f..7c3e8785d 100644 --- a/src/device_trezor/device_trezor.cpp +++ b/src/device_trezor/device_trezor.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // @@ -101,7 +101,7 @@ namespace trezor { return device_trezor_base::disconnect(); } - void device_trezor::device_state_reset_unsafe() + void device_trezor::device_state_initialize_unsafe() { require_connected(); if (m_live_refresh_in_progress) @@ -117,7 +117,7 @@ namespace trezor { } m_live_refresh_in_progress = false; - device_trezor_base::device_state_reset_unsafe(); + device_trezor_base::device_state_initialize_unsafe(); } void device_trezor::live_refresh_thread_main() @@ -221,7 +221,7 @@ namespace trezor { CHECK_AND_ASSERT_THROW_MES(!payment_id || !subaddress || subaddress->is_zero(), "Subaddress cannot be integrated"); TREZOR_AUTO_LOCK_CMD(); require_connected(); - device_state_reset_unsafe(); + device_state_initialize_unsafe(); require_initialized(); auto req = std::make_shared<messages::monero::MoneroGetAddress>(); @@ -245,7 +245,7 @@ namespace trezor { const boost::optional<cryptonote::network_type> & network_type){ TREZOR_AUTO_LOCK_CMD(); require_connected(); - device_state_reset_unsafe(); + device_state_initialize_unsafe(); require_initialized(); auto req = std::make_shared<messages::monero::MoneroGetWatchKey>(); @@ -274,7 +274,7 @@ namespace trezor { { TREZOR_AUTO_LOCK_CMD(); require_connected(); - device_state_reset_unsafe(); + device_state_initialize_unsafe(); require_initialized(); auto req = protocol::tx::get_tx_key(tx_aux_data); @@ -294,15 +294,15 @@ namespace trezor { TREZOR_AUTO_LOCK_CMD(); require_connected(); - device_state_reset_unsafe(); + device_state_initialize_unsafe(); require_initialized(); std::shared_ptr<messages::monero::MoneroKeyImageExportInitRequest> req; std::vector<protocol::ki::MoneroTransferDetails> mtds; std::vector<protocol::ki::MoneroExportedKeyImage> kis; - protocol::ki::key_image_data(wallet, transfers, mtds); - protocol::ki::generate_commitment(mtds, transfers, req); + protocol::ki::key_image_data(wallet, transfers, mtds, client_version() <= 1); + protocol::ki::generate_commitment(mtds, transfers, req, client_version() <= 1); EVENT_PROGRESS(0.); this->set_msg_addr<messages::monero::MoneroKeyImageExportInitRequest>(req.get()); @@ -386,7 +386,7 @@ namespace trezor { void device_trezor::live_refresh_start_unsafe() { - device_state_reset_unsafe(); + device_state_initialize_unsafe(); require_initialized(); auto req = std::make_shared<messages::monero::MoneroLiveRefreshStartRequest>(); @@ -492,7 +492,7 @@ namespace trezor { TREZOR_AUTO_LOCK_CMD(); require_connected(); - device_state_reset_unsafe(); + device_state_initialize_unsafe(); require_initialized(); transaction_versions_check(unsigned_tx, aux_data); @@ -514,7 +514,7 @@ namespace trezor { auto & cpend = signed_tx.ptx.back(); cpend.tx = cdata.tx; cpend.dust = 0; - cpend.fee = 0; + cpend.fee = cpend.tx.rct_signatures.txnFee; cpend.dust_added_to_fee = false; cpend.change_dts = cdata.tx_data.change_dts; cpend.selected_transfers = cdata.tx_data.selected_transfers; @@ -524,6 +524,7 @@ namespace trezor { // Transaction check try { + MDEBUG("signed transaction: " << cryptonote::get_transaction_hash(cpend.tx) << ENDL << cryptonote::obj_to_json_str(cpend.tx) << ENDL); transaction_check(cdata, aux_data); } catch(const std::exception &e){ throw exc::ProtocolException(std::string("Transaction verification failed: ") + e.what()); @@ -582,7 +583,7 @@ namespace trezor { require_connected(); if (idx > 0) - device_state_reset_unsafe(); + device_state_initialize_unsafe(); require_initialized(); EVENT_PROGRESS(0, 1, 1); @@ -670,28 +671,44 @@ namespace trezor { #undef EVENT_PROGRESS } - void device_trezor::transaction_versions_check(const ::tools::wallet2::unsigned_tx_set & unsigned_tx, hw::tx_aux_data & aux_data) + unsigned device_trezor::client_version() { auto trezor_version = get_version(); - unsigned client_version = 1; // default client version for tx - if (trezor_version <= pack_version(2, 0, 10)){ - client_version = 0; + throw exc::TrezorException("Trezor firmware 2.0.10 and lower are not supported. Please update."); + } + + unsigned client_version = 1; + if (trezor_version >= pack_version(2, 3, 1)){ + client_version = 3; + } + +#ifdef WITH_TREZOR_DEBUGGING + // Override client version for tests + const char *env_trezor_client_version = nullptr; + if ((env_trezor_client_version = getenv("TREZOR_CLIENT_VERSION")) != nullptr){ + auto succ = epee::string_tools::get_xtype_from_string(client_version, env_trezor_client_version); + if (succ){ + MINFO("Trezor client version overriden by TREZOR_CLIENT_VERSION to: " << client_version); + } } +#endif + return client_version; + } + + void device_trezor::transaction_versions_check(const ::tools::wallet2::unsigned_tx_set & unsigned_tx, hw::tx_aux_data & aux_data) + { + unsigned cversion = client_version(); if (aux_data.client_version){ auto wanted_client_version = aux_data.client_version.get(); - if (wanted_client_version > client_version){ - throw exc::TrezorException("Trezor firmware 2.0.10 and lower does not support current transaction sign protocol. Please update."); + if (wanted_client_version > cversion){ + throw exc::TrezorException("Trezor has too old firmware version. Please update."); } else { - client_version = wanted_client_version; + cversion = wanted_client_version; } } - aux_data.client_version = client_version; - - if (client_version == 0 && aux_data.bp_version && aux_data.bp_version.get() != 1){ - throw exc::TrezorException("Trezor firmware 2.0.10 and lower does not support current transaction sign protocol (BPv2+). Please update."); - } + aux_data.client_version = cversion; } void device_trezor::transaction_pre_check(std::shared_ptr<messages::monero::MoneroTransactionInitRequest> init_msg) diff --git a/src/device_trezor/device_trezor.hpp b/src/device_trezor/device_trezor.hpp index a26a42788..d91d1de3f 100644 --- a/src/device_trezor/device_trezor.hpp +++ b/src/device_trezor/device_trezor.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // @@ -67,10 +67,11 @@ namespace trezor { bool m_live_refresh_enabled; size_t m_num_transations_to_sign; + unsigned client_version(); void transaction_versions_check(const ::tools::wallet2::unsigned_tx_set & unsigned_tx, hw::tx_aux_data & aux_data); void transaction_pre_check(std::shared_ptr<messages::monero::MoneroTransactionInitRequest> init_msg); void transaction_check(const protocol::tx::TData & tdata, const hw::tx_aux_data & aux_data); - void device_state_reset_unsafe() override; + void device_state_initialize_unsafe() override; void live_refresh_start_unsafe(); void live_refresh_finish_unsafe(); void live_refresh_thread_main(); diff --git a/src/device_trezor/device_trezor_base.cpp b/src/device_trezor/device_trezor_base.cpp index b7adf433d..f59be1573 100644 --- a/src/device_trezor/device_trezor_base.cpp +++ b/src/device_trezor/device_trezor_base.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // @@ -28,6 +28,7 @@ // #include "device_trezor_base.hpp" +#include "memwipe.h" #include <boost/algorithm/string/classification.hpp> #include <boost/algorithm/string/split.hpp> #include <boost/regex.hpp> @@ -151,7 +152,7 @@ namespace trezor { bool device_trezor_base::disconnect() { TREZOR_AUTO_LOCK_DEVICE(); - m_device_state.clear(); + m_device_session_id.clear(); m_features.reset(); if (m_transport){ @@ -292,8 +293,8 @@ namespace trezor { case messages::MessageType_PassphraseRequest: on_passphrase_request(input, dynamic_cast<const messages::common::PassphraseRequest*>(input.m_msg.get())); return true; - case messages::MessageType_PassphraseStateRequest: - on_passphrase_state_request(input, dynamic_cast<const messages::common::PassphraseStateRequest*>(input.m_msg.get())); + case messages::MessageType_Deprecated_PassphraseStateRequest: + on_passphrase_state_request(input, dynamic_cast<const messages::common::Deprecated_PassphraseStateRequest*>(input.m_msg.get())); return true; case messages::MessageType_PinMatrixRequest: on_pin_request(input, dynamic_cast<const messages::common::PinMatrixRequest*>(input.m_msg.get())); @@ -361,23 +362,34 @@ namespace trezor { return false; } - void device_trezor_base::device_state_reset_unsafe() + void device_trezor_base::device_state_initialize_unsafe() { require_connected(); + std::string tmp_session_id; auto initMsg = std::make_shared<messages::management::Initialize>(); + const auto data_cleaner = epee::misc_utils::create_scope_leave_handler([&]() { + memwipe(&tmp_session_id[0], tmp_session_id.size()); + }); - if(!m_device_state.empty()) { - initMsg->set_allocated_state(&m_device_state); + if(!m_device_session_id.empty()) { + tmp_session_id.assign(m_device_session_id.data(), m_device_session_id.size()); + initMsg->set_allocated_session_id(&tmp_session_id); } m_features = this->client_exchange<messages::management::Features>(initMsg); - initMsg->release_state(); + if (m_features->has_session_id()){ + m_device_session_id = m_features->session_id(); + } else { + m_device_session_id.clear(); + } + + initMsg->release_session_id(); } void device_trezor_base::device_state_reset() { TREZOR_AUTO_LOCK_CMD(); - device_state_reset_unsafe(); + device_state_initialize_unsafe(); } #ifdef WITH_TREZOR_DEBUGGING @@ -441,48 +453,89 @@ namespace trezor { pin = m_pin; } - // TODO: remove PIN from memory + std::string pin_field; messages::common::PinMatrixAck m; if (pin) { - m.set_pin(pin.get().data(), pin.get().size()); + pin_field.assign(pin->data(), pin->size()); + m.set_allocated_pin(&pin_field); } + + const auto data_cleaner = epee::misc_utils::create_scope_leave_handler([&]() { + m.release_pin(); + if (!pin_field.empty()){ + memwipe(&pin_field[0], pin_field.size()); + } + }); + resp = call_raw(&m); } void device_trezor_base::on_passphrase_request(GenericMessage & resp, const messages::common::PassphraseRequest * msg) { CHECK_AND_ASSERT_THROW_MES(msg, "Empty message"); - MDEBUG("on_passhprase_request, on device: " << msg->on_device()); - boost::optional<epee::wipeable_string> passphrase; - TREZOR_CALLBACK_GET(passphrase, on_passphrase_request, msg->on_device()); + MDEBUG("on_passhprase_request"); - if (!passphrase && m_passphrase){ - passphrase = m_passphrase; + // Backward compatibility, migration clause. + if (msg->has__on_device() && msg->_on_device()){ + messages::common::PassphraseAck m; + resp = call_raw(&m); + return; } - m_passphrase = boost::none; + bool on_device = true; + if (msg->has__on_device() && !msg->_on_device()){ + on_device = false; // do not enter on device, old devices. + } - messages::common::PassphraseAck m; - if (!msg->on_device() && passphrase){ - // TODO: remove passphrase from memory - m.set_passphrase(passphrase.get().data(), passphrase.get().size()); + if (on_device && m_features && m_features->capabilities_size() > 0){ + on_device = false; + for (auto it = m_features->capabilities().begin(); it != m_features->capabilities().end(); it++) { + if (*it == messages::management::Features::Capability_PassphraseEntry){ + on_device = true; + } + } } - if (!m_device_state.empty()){ - m.set_allocated_state(&m_device_state); + boost::optional<epee::wipeable_string> passphrase; + TREZOR_CALLBACK_GET(passphrase, on_passphrase_request, on_device); + + std::string passphrase_field; + messages::common::PassphraseAck m; + m.set_on_device(on_device); + if (!on_device) { + if (!passphrase && m_passphrase) { + passphrase = m_passphrase; + } + + if (m_passphrase) { + m_passphrase = boost::none; + } + + if (passphrase) { + passphrase_field.assign(passphrase->data(), passphrase->size()); + m.set_allocated_passphrase(&passphrase_field); + } } + const auto data_cleaner = epee::misc_utils::create_scope_leave_handler([&]() { + m.release_passphrase(); + if (!passphrase_field.empty()){ + memwipe(&passphrase_field[0], passphrase_field.size()); + } + }); + resp = call_raw(&m); - m.release_state(); } - void device_trezor_base::on_passphrase_state_request(GenericMessage & resp, const messages::common::PassphraseStateRequest * msg) + void device_trezor_base::on_passphrase_state_request(GenericMessage & resp, const messages::common::Deprecated_PassphraseStateRequest * msg) { MDEBUG("on_passhprase_state_request"); CHECK_AND_ASSERT_THROW_MES(msg, "Empty message"); - m_device_state = msg->state(); - messages::common::PassphraseStateAck m; + if (msg->has_state()) { + m_device_session_id = msg->state(); + } + messages::common::Deprecated_PassphraseStateAck m; resp = call_raw(&m); } @@ -510,7 +563,7 @@ namespace trezor { } auto msg = std::make_shared<messages::management::LoadDevice>(); - msg->set_mnemonic(mnemonic); + msg->add_mnemonics(mnemonic); msg->set_pin(pin); msg->set_passphrase_protection(passphrase_protection); msg->set_label(label); @@ -535,7 +588,8 @@ namespace trezor { return boost::none; } - boost::optional<epee::wipeable_string> trezor_debug_callback::on_passphrase_request(bool on_device) { + boost::optional<epee::wipeable_string> trezor_debug_callback::on_passphrase_request(bool & on_device) { + on_device = true; return boost::none; } diff --git a/src/device_trezor/device_trezor_base.hpp b/src/device_trezor/device_trezor_base.hpp index c106d2099..4db8f0c8e 100644 --- a/src/device_trezor/device_trezor_base.hpp +++ b/src/device_trezor/device_trezor_base.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // @@ -70,7 +70,7 @@ namespace trezor { void on_button_request(uint64_t code=0) override; boost::optional<epee::wipeable_string> on_pin_request() override; - boost::optional<epee::wipeable_string> on_passphrase_request(bool on_device) override; + boost::optional<epee::wipeable_string> on_passphrase_request(bool & on_device) override; void on_passphrase_state_request(const std::string &state); void on_disconnect(); protected: @@ -94,7 +94,7 @@ namespace trezor { std::string m_full_name; std::vector<unsigned int> m_wallet_deriv_path; - std::string m_device_state; // returned after passphrase entry, session + epee::wipeable_string m_device_session_id; // returned after passphrase entry, session std::shared_ptr<messages::management::Features> m_features; // features from the last device reset boost::optional<epee::wipeable_string> m_pin; boost::optional<epee::wipeable_string> m_passphrase; @@ -117,7 +117,7 @@ namespace trezor { void require_initialized() const; void call_ping_unsafe(); void test_ping(); - virtual void device_state_reset_unsafe(); + virtual void device_state_initialize_unsafe(); void ensure_derivation_path() noexcept; // Communication methods @@ -315,7 +315,7 @@ namespace trezor { void on_button_pressed(); void on_pin_request(GenericMessage & resp, const messages::common::PinMatrixRequest * msg); void on_passphrase_request(GenericMessage & resp, const messages::common::PassphraseRequest * msg); - void on_passphrase_state_request(GenericMessage & resp, const messages::common::PassphraseStateRequest * msg); + void on_passphrase_state_request(GenericMessage & resp, const messages::common::Deprecated_PassphraseStateRequest * msg); #ifdef WITH_TREZOR_DEBUGGING void set_debug(bool debug){ diff --git a/src/device_trezor/trezor.hpp b/src/device_trezor/trezor.hpp index 396a27534..b3f85f6a8 100644 --- a/src/device_trezor/trezor.hpp +++ b/src/device_trezor/trezor.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device_trezor/trezor/debug_link.cpp b/src/device_trezor/trezor/debug_link.cpp index c7ee59afe..102d1f966 100644 --- a/src/device_trezor/trezor/debug_link.cpp +++ b/src/device_trezor/trezor/debug_link.cpp @@ -71,9 +71,9 @@ namespace trezor{ call(decision, boost::none, true); } - void DebugLink::input_swipe(bool swipe){ + void DebugLink::input_swipe(messages::debug::DebugLinkDecision_DebugSwipeDirection direction){ messages::debug::DebugLinkDecision decision; - decision.set_up_down(swipe); + decision.set_swipe(direction); call(decision, boost::none, true); } diff --git a/src/device_trezor/trezor/debug_link.hpp b/src/device_trezor/trezor/debug_link.hpp index adf5f1d8f..a5f05ea94 100644 --- a/src/device_trezor/trezor/debug_link.hpp +++ b/src/device_trezor/trezor/debug_link.hpp @@ -49,7 +49,7 @@ namespace trezor { std::shared_ptr<messages::debug::DebugLinkState> state(); void input_word(const std::string & word); void input_button(bool button); - void input_swipe(bool swipe); + void input_swipe(messages::debug::DebugLinkDecision_DebugSwipeDirection direction); void press_yes() { input_button(true); } void press_no() { input_button(false); } void stop(); diff --git a/src/device_trezor/trezor/exceptions.hpp b/src/device_trezor/trezor/exceptions.hpp index 41e8c2875..9d63329e4 100644 --- a/src/device_trezor/trezor/exceptions.hpp +++ b/src/device_trezor/trezor/exceptions.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device_trezor/trezor/messages_map.cpp b/src/device_trezor/trezor/messages_map.cpp index 6956b369a..02869f760 100644 --- a/src/device_trezor/trezor/messages_map.cpp +++ b/src/device_trezor/trezor/messages_map.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device_trezor/trezor/messages_map.hpp b/src/device_trezor/trezor/messages_map.hpp index 28fe9f4c2..a903eaf92 100644 --- a/src/device_trezor/trezor/messages_map.hpp +++ b/src/device_trezor/trezor/messages_map.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device_trezor/trezor/protocol.cpp b/src/device_trezor/trezor/protocol.cpp index 61e51be14..25c3d816d 100644 --- a/src/device_trezor/trezor/protocol.cpp +++ b/src/device_trezor/trezor/protocol.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // @@ -145,7 +145,8 @@ namespace ki { bool key_image_data(wallet_shim * wallet, const std::vector<tools::wallet2::transfer_details> & transfers, - std::vector<MoneroTransferDetails> & res) + std::vector<MoneroTransferDetails> & res, + bool need_all_additionals) { for(auto & td : transfers){ ::crypto::public_key tx_pub_key = wallet->get_tx_pub_key_from_received_outs(td); @@ -157,8 +158,14 @@ namespace ki { cres.set_out_key(key_to_string(boost::get<cryptonote::txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key)); cres.set_tx_pub_key(key_to_string(tx_pub_key)); cres.set_internal_output_index(td.m_internal_output_index); - for(auto & aux : additional_tx_pub_keys){ - cres.add_additional_tx_pub_keys(key_to_string(aux)); + cres.set_sub_addr_major(td.m_subaddr_index.major); + cres.set_sub_addr_minor(td.m_subaddr_index.minor); + if (need_all_additionals) { + for (auto &aux : additional_tx_pub_keys) { + cres.add_additional_tx_pub_keys(key_to_string(aux)); + } + } else if (!additional_tx_pub_keys.empty() && additional_tx_pub_keys.size() > td.m_internal_output_index) { + cres.add_additional_tx_pub_keys(key_to_string(additional_tx_pub_keys[td.m_internal_output_index])); } } @@ -188,7 +195,8 @@ namespace ki { void generate_commitment(std::vector<MoneroTransferDetails> & mtds, const std::vector<tools::wallet2::transfer_details> & transfers, - std::shared_ptr<messages::monero::MoneroKeyImageExportInitRequest> & req) + std::shared_ptr<messages::monero::MoneroKeyImageExportInitRequest> & req, + bool need_subaddr_indices) { req = std::make_shared<messages::monero::MoneroKeyImageExportInitRequest>(); @@ -213,11 +221,13 @@ namespace ki { st.insert(cur.m_subaddr_index.minor); } - for (auto& x: sub_indices){ - auto subs = req->add_subs(); - subs->set_account(x.first); - for(auto minor : x.second){ - subs->add_minor_indices(minor); + if (need_subaddr_indices) { + for (auto &x: sub_indices) { + auto subs = req->add_subs(); + subs->set_account(x.first); + for (auto minor : x.second) { + subs->add_minor_indices(minor); + } } } } @@ -283,26 +293,6 @@ namespace tx { translate_address(dst->mutable_addr(), &(src->addr)); } - void translate_src_entry(MoneroTransactionSourceEntry * dst, const cryptonote::tx_source_entry * src){ - for(auto & cur : src->outputs){ - auto out = dst->add_outputs(); - out->set_idx(cur.first); - translate_rct_key(out->mutable_key(), &(cur.second)); - } - - dst->set_real_output(src->real_output); - dst->set_real_out_tx_key(key_to_string(src->real_out_tx_key)); - for(auto & cur : src->real_out_additional_tx_keys){ - dst->add_real_out_additional_tx_keys(key_to_string(cur)); - } - - dst->set_real_output_in_tx_index(src->real_output_in_tx_index); - dst->set_amount(src->amount); - dst->set_rct(src->rct); - dst->set_mask(key_to_string(src->mask)); - translate_klrki(dst->mutable_multisig_klrki(), &(src->multisig_kLRki)); - } - void translate_klrki(MoneroMultisigKLRki * dst, const rct::multisig_kLRki * src){ dst->set_k(key_to_string(src->k)); dst->set_l(key_to_string(src->L)); @@ -369,6 +359,31 @@ namespace tx { return res; } + std::string compute_sealing_key(const std::string & master_key, size_t idx, bool is_iv) + { + // master-key-32B || domain-sep-12B || index-4B + uint8_t hash[32] = {0}; + KECCAK_CTX ctx; + std::string sep = is_iv ? "sig-iv" : "sig-key"; + std::string idx_data = tools::get_varint_data(idx); + if (idx_data.size() > 4){ + throw std::invalid_argument("index is too big"); + } + + keccak_init(&ctx); + keccak_update(&ctx, (const uint8_t *) master_key.data(), master_key.size()); + keccak_update(&ctx, (const uint8_t *) sep.data(), sep.size()); + keccak_update(&ctx, hash, 12 - sep.size()); + keccak_update(&ctx, (const uint8_t *) idx_data.data(), idx_data.size()); + if (idx_data.size() < 4) { + keccak_update(&ctx, hash, 4 - idx_data.size()); + } + + keccak_finish(&ctx, hash); + keccak(hash, sizeof(hash), hash, sizeof(hash)); + return std::string((const char*) hash, 32); + } + TData::TData() { rsig_type = 0; bp_version = 0; @@ -383,7 +398,7 @@ namespace tx { m_unsigned_tx = unsigned_tx; m_aux_data = aux_data; m_tx_idx = tx_idx; - m_ct.tx_data = cur_tx(); + m_ct.tx_data = cur_src_tx(); m_multisig = false; m_client_version = 1; } @@ -451,6 +466,41 @@ namespace tx { } } + void Signer::set_tx_input(MoneroTransactionSourceEntry * dst, size_t idx, bool need_ring_keys, bool need_ring_indices){ + const cryptonote::tx_source_entry & src = cur_tx().sources[idx]; + const tools::wallet2::transfer_details & transfer = get_source_transfer(idx); + + dst->set_real_output(src.real_output); + for(size_t i = 0; i < src.outputs.size(); ++i){ + auto & cur = src.outputs[i]; + auto out = dst->add_outputs(); + + if (i == src.real_output || need_ring_indices || client_version() <= 1) { + out->set_idx(cur.first); + } + if (i == src.real_output || need_ring_keys || client_version() <= 1) { + translate_rct_key(out->mutable_key(), &(cur.second)); + } + } + + dst->set_real_out_tx_key(key_to_string(src.real_out_tx_key)); + dst->set_real_output_in_tx_index(src.real_output_in_tx_index); + + if (client_version() <= 1) { + for (auto &cur : src.real_out_additional_tx_keys) { + dst->add_real_out_additional_tx_keys(key_to_string(cur)); + } + } else if (!src.real_out_additional_tx_keys.empty()) { + dst->add_real_out_additional_tx_keys(key_to_string(src.real_out_additional_tx_keys.at(src.real_output_in_tx_index))); + } + + dst->set_amount(src.amount); + dst->set_rct(src.rct); + dst->set_mask(key_to_string(src.mask)); + translate_klrki(dst->mutable_multisig_klrki(), &(src.multisig_kLRki)); + dst->set_subaddr_minor(transfer.m_subaddr_index.minor); + } + void Signer::compute_integrated_indices(TsxData * tsx_data){ if (m_aux_data == nullptr || m_aux_data->tx_recipients.empty()){ return; @@ -492,6 +542,7 @@ namespace tx { // extract payment ID from construction data auto & tsx_data = m_ct.tsx_data; auto & tx = cur_tx(); + const size_t input_size = tx.sources.size(); m_ct.tx.version = 2; m_ct.tx.unlock_time = tx.unlock_time; @@ -500,12 +551,20 @@ namespace tx { tsx_data.set_version(1); tsx_data.set_client_version(client_version()); tsx_data.set_unlock_time(tx.unlock_time); - tsx_data.set_num_inputs(static_cast<google::protobuf::uint32>(tx.sources.size())); + tsx_data.set_num_inputs(static_cast<google::protobuf::uint32>(input_size)); tsx_data.set_mixin(static_cast<google::protobuf::uint32>(tx.sources[0].outputs.size() - 1)); tsx_data.set_account(tx.subaddr_account); tsx_data.set_monero_version(std::string(MONERO_VERSION) + "|" + MONERO_VERSION_TAG); tsx_data.set_hard_fork(m_aux_data->hard_fork ? m_aux_data->hard_fork.get() : 0); - assign_to_repeatable(tsx_data.mutable_minor_indices(), tx.subaddr_indices.begin(), tx.subaddr_indices.end()); + + if (client_version() <= 1){ + assign_to_repeatable(tsx_data.mutable_minor_indices(), tx.subaddr_indices.begin(), tx.subaddr_indices.end()); + } + + // TODO: use HF_VERSION_CLSAG after CLSAG is merged + if (tsx_data.hard_fork() >= 13){ + throw exc::ProtocolException("CLSAG is not yet implemented"); + } // Rsig decision auto rsig_data = tsx_data.mutable_rsig_data(); @@ -525,6 +584,11 @@ namespace tx { translate_dst_entry(dst, &cur); } + m_ct.source_permutation.clear(); + for (size_t n = 0; n < input_size; ++n){ + m_ct.source_permutation.push_back(n); + } + compute_integrated_indices(&tsx_data); int64_t fee = 0; @@ -559,7 +623,7 @@ namespace tx { CHECK_AND_ASSERT_THROW_MES(idx < cur_tx().sources.size(), "Invalid source index"); m_ct.cur_input_idx = idx; auto res = std::make_shared<messages::monero::MoneroTransactionSetInputRequest>(); - translate_src_entry(res->mutable_src_entr(), &(cur_tx().sources[idx])); + set_tx_input(res->mutable_src_entr(), idx, false, true); return res; } @@ -582,11 +646,6 @@ namespace tx { void Signer::sort_ki(){ const size_t input_size = cur_tx().sources.size(); - m_ct.source_permutation.clear(); - for (size_t n = 0; n < input_size; ++n){ - m_ct.source_permutation.push_back(n); - } - CHECK_AND_ASSERT_THROW_MES(m_ct.tx.vin.size() == input_size, "Invalid vector size"); std::sort(m_ct.source_permutation.begin(), m_ct.source_permutation.end(), [&](const size_t i0, const size_t i1) { const cryptonote::txin_to_key &tk0 = boost::get<cryptonote::txin_to_key>(m_ct.tx.vin[i0]); @@ -614,6 +673,9 @@ namespace tx { std::shared_ptr<messages::monero::MoneroTransactionInputsPermutationRequest> Signer::step_permutation(){ sort_ki(); + if (client_version() >= 2){ + return nullptr; + } auto res = std::make_shared<messages::monero::MoneroTransactionInputsPermutationRequest>(); assign_to_repeatable(res->mutable_perm(), m_ct.source_permutation.begin(), m_ct.source_permutation.end()); @@ -634,17 +696,10 @@ namespace tx { auto tx = m_ct.tx_data; auto res = std::make_shared<messages::monero::MoneroTransactionInputViniRequest>(); auto & vini = m_ct.tx.vin[idx]; - translate_src_entry(res->mutable_src_entr(), &(tx.sources[idx])); + set_tx_input(res->mutable_src_entr(), idx, false, false); res->set_vini(cryptonote::t_serializable_object_to_blob(vini)); res->set_vini_hmac(m_ct.tx_in_hmacs[idx]); - - if (client_version() == 0) { - CHECK_AND_ASSERT_THROW_MES(idx < m_ct.pseudo_outs.size(), "Invalid transaction index"); - CHECK_AND_ASSERT_THROW_MES(idx < m_ct.pseudo_outs_hmac.size(), "Invalid transaction index"); - res->set_pseudo_out(m_ct.pseudo_outs[idx]); - res->set_pseudo_out_hmac(m_ct.pseudo_outs_hmac[idx]); - } - + res->set_orig_idx(m_ct.source_permutation[idx]); return res; } @@ -657,31 +712,6 @@ namespace tx { } void Signer::step_all_inputs_set_ack(std::shared_ptr<const messages::monero::MoneroTransactionAllInputsSetAck> ack){ - if (client_version() > 0 || !is_offloading()){ - return; - } - - // If offloading, expect rsig configuration. - if (!ack->has_rsig_data()){ - throw exc::ProtocolException("Rsig offloading requires rsig param"); - } - - auto & rsig_data = ack->rsig_data(); - if (!rsig_data.has_mask()){ - throw exc::ProtocolException("Gamma masks not present in offloaded version"); - } - - auto & mask = rsig_data.mask(); - if (mask.size() != 32 * num_outputs()){ - throw exc::ProtocolException("Invalid number of gamma masks"); - } - - m_ct.rsig_gamma.reserve(num_outputs()); - for(size_t c=0; c < num_outputs(); ++c){ - rct::key cmask{}; - memcpy(cmask.bytes, mask.data() + c * 32, 32); - m_ct.rsig_gamma.emplace_back(cmask); - } } std::shared_ptr<messages::monero::MoneroTransactionSetOutputRequest> Signer::step_set_output(size_t idx){ @@ -696,15 +726,6 @@ namespace tx { auto & cur_dst = m_ct.tx_data.splitted_dsts[idx]; translate_dst_entry(res->mutable_dst_entr(), &cur_dst); res->set_dst_entr_hmac(m_ct.tx_out_entr_hmacs[idx]); - - // Range sig offloading to the host - // ClientV0 sends offloaded BP with the last message in the batch. - // ClientV1 needs additional message after the last message in the batch as BP uses deterministic masks. - if (client_version() == 0 && is_offloading() && should_compute_bp_now()) { - auto rsig_data = res->mutable_rsig_data(); - compute_bproof(*rsig_data); - } - return res; } @@ -814,7 +835,7 @@ namespace tx { } std::shared_ptr<messages::monero::MoneroTransactionSetOutputRequest> Signer::step_rsig(size_t idx){ - if (client_version() == 0 || !is_offloading() || !should_compute_bp_now()){ + if (!is_offloading() || !should_compute_bp_now()){ return nullptr; } @@ -917,11 +938,12 @@ namespace tx { CHECK_AND_ASSERT_THROW_MES(idx < m_ct.spend_encs.size(), "Invalid transaction index"); auto res = std::make_shared<messages::monero::MoneroTransactionSignInputRequest>(); - translate_src_entry(res->mutable_src_entr(), &(m_ct.tx_data.sources[idx])); + set_tx_input(res->mutable_src_entr(), idx, true, true); res->set_vini(cryptonote::t_serializable_object_to_blob(m_ct.tx.vin[idx])); res->set_vini_hmac(m_ct.tx_in_hmacs[idx]); res->set_pseudo_out_alpha(m_ct.alphas[idx]); res->set_spend_key(m_ct.spend_encs[idx]); + res->set_orig_idx(m_ct.source_permutation[idx]); CHECK_AND_ASSERT_THROW_MES(idx < m_ct.pseudo_outs.size(), "Invalid transaction index"); CHECK_AND_ASSERT_THROW_MES(idx < m_ct.pseudo_outs_hmac.size(), "Invalid transaction index"); @@ -931,10 +953,7 @@ namespace tx { } void Signer::step_sign_input_ack(std::shared_ptr<const messages::monero::MoneroTransactionSignInputAck> ack){ - rct::mgSig mg; - if (!cn_deserialize(ack->signature(), mg)){ - throw exc::ProtocolException("Cannot deserialize mg[i]"); - } + m_ct.signatures.push_back(ack->signature()); // Sync updated pseudo_outputs, client_version>=1, HF10+ if (client_version() >= 1 && ack->has_pseudo_out()){ @@ -948,12 +967,9 @@ namespace tx { string_to_key(m_ct.rv->pseudoOuts[m_ct.cur_input_idx], ack->pseudo_out()); } } - - m_ct.rv->p.MGs.push_back(mg); } std::shared_ptr<messages::monero::MoneroTransactionFinalRequest> Signer::step_final(){ - m_ct.tx.rct_signatures = *(m_ct.rv); return std::make_shared<messages::monero::MoneroTransactionFinalRequest>(); } @@ -976,6 +992,42 @@ namespace tx { m_ct.enc_salt1 = ack->salt(); m_ct.enc_salt2 = ack->rand_mult(); m_ct.enc_keys = ack->tx_enc_keys(); + + // Opening the sealed signatures + if (client_version() >= 3){ + if(!ack->has_opening_key()){ + throw exc::ProtocolException("Client version 3+ requires sealed signatures"); + } + + for(size_t i = 0; i < m_ct.signatures.size(); ++i){ + CHECK_AND_ASSERT_THROW_MES(m_ct.signatures[i].size() > crypto::chacha::TAG_SIZE, "Invalid signature size"); + std::string nonce = compute_sealing_key(ack->opening_key(), i, true); + std::string key = compute_sealing_key(ack->opening_key(), i, false); + size_t plen = m_ct.signatures[i].size() - crypto::chacha::TAG_SIZE; + std::unique_ptr<uint8_t[]> plaintext(new uint8_t[plen]); + uint8_t * buff = plaintext.get(); + + protocol::crypto::chacha::decrypt( + m_ct.signatures[i].data(), + m_ct.signatures[i].size(), + reinterpret_cast<const uint8_t *>(key.data()), + reinterpret_cast<const uint8_t *>(nonce.data()), + reinterpret_cast<char *>(buff), &plen); + m_ct.signatures[i].assign(reinterpret_cast<const char *>(buff), plen); + } + } + + // CLSAG support comes here once it is merged to the Monero + m_ct.rv->p.MGs.reserve(m_ct.signatures.size()); + for(size_t i = 0; i < m_ct.signatures.size(); ++i) { + rct::mgSig mg; + if (!cn_deserialize(m_ct.signatures[i], mg)) { + throw exc::ProtocolException("Cannot deserialize mg[i]"); + } + m_ct.rv->p.MGs.push_back(mg); + } + + m_ct.tx.rct_signatures = *(m_ct.rv); } std::string Signer::store_tx_aux_info(){ diff --git a/src/device_trezor/trezor/protocol.hpp b/src/device_trezor/trezor/protocol.hpp index f58bf1039..35e7d9605 100644 --- a/src/device_trezor/trezor/protocol.hpp +++ b/src/device_trezor/trezor/protocol.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // @@ -118,7 +118,8 @@ namespace ki { */ bool key_image_data(wallet_shim * wallet, const std::vector<tools::wallet2::transfer_details> & transfers, - std::vector<MoneroTransferDetails> & res); + std::vector<MoneroTransferDetails> & res, + bool need_all_additionals=false); /** * Computes a hash over MoneroTransferDetails. Commitment used in the KI sync. @@ -130,7 +131,8 @@ namespace ki { */ void generate_commitment(std::vector<MoneroTransferDetails> & mtds, const std::vector<tools::wallet2::transfer_details> & transfers, - std::shared_ptr<messages::monero::MoneroKeyImageExportInitRequest> & req); + std::shared_ptr<messages::monero::MoneroKeyImageExportInitRequest> & req, + bool need_subaddr_indices=false); /** * Processes Live refresh step response, parses KI, checks the signature @@ -158,13 +160,13 @@ namespace tx { void translate_address(MoneroAccountPublicAddress * dst, const cryptonote::account_public_address * src); void translate_dst_entry(MoneroTransactionDestinationEntry * dst, const cryptonote::tx_destination_entry * src); - void translate_src_entry(MoneroTransactionSourceEntry * dst, const cryptonote::tx_source_entry * src); void translate_klrki(MoneroMultisigKLRki * dst, const rct::multisig_kLRki * src); void translate_rct_key(MoneroRctKey * dst, const rct::ctkey * src); std::string hash_addr(const MoneroAccountPublicAddress * addr, boost::optional<uint64_t> amount = boost::none, boost::optional<bool> is_subaddr = boost::none); std::string hash_addr(const std::string & spend_key, const std::string & view_key, boost::optional<uint64_t> amount = boost::none, boost::optional<bool> is_subaddr = boost::none); std::string hash_addr(const ::crypto::public_key * spend_key, const ::crypto::public_key * view_key, boost::optional<uint64_t> amount = boost::none, boost::optional<bool> is_subaddr = boost::none); ::crypto::secret_key compute_enc_key(const ::crypto::secret_key & private_view_key, const std::string & aux, const std::string & salt); + std::string compute_sealing_key(const std::string & master_key, size_t idx, bool is_iv=false); typedef boost::variant<rct::rangeSig, rct::Bulletproof> rsig_v; @@ -198,6 +200,7 @@ namespace tx { std::vector<std::string> pseudo_outs_hmac; std::vector<std::string> couts; std::vector<std::string> couts_dec; + std::vector<std::string> signatures; std::vector<rct::key> rsig_gamma; std::string tx_prefix_hash; std::string enc_salt1; @@ -221,16 +224,33 @@ namespace tx { unsigned m_client_version; bool m_multisig; - const tx_construction_data & cur_tx(){ + const tx_construction_data & cur_src_tx() const { CHECK_AND_ASSERT_THROW_MES(m_tx_idx < m_unsigned_tx->txes.size(), "Invalid transaction index"); return m_unsigned_tx->txes[m_tx_idx]; } + const tx_construction_data & cur_tx() const { + return m_ct.tx_data; + } + + const tools::wallet2::transfer_details & get_transfer(size_t idx) const { + CHECK_AND_ASSERT_THROW_MES(idx < m_unsigned_tx->transfers.second.size() + m_unsigned_tx->transfers.first && idx >= m_unsigned_tx->transfers.first, "Invalid transfer index"); + return m_unsigned_tx->transfers.second[idx - m_unsigned_tx->transfers.first]; + } + + const tools::wallet2::transfer_details & get_source_transfer(size_t idx) const { + const auto & sel_transfers = cur_tx().selected_transfers; + CHECK_AND_ASSERT_THROW_MES(idx < m_ct.source_permutation.size(), "Invalid source index - permutation"); + CHECK_AND_ASSERT_THROW_MES(m_ct.source_permutation[idx] < sel_transfers.size(), "Invalid source index"); + return get_transfer(sel_transfers.at(m_ct.source_permutation[idx])); + } + void extract_payment_id(); void compute_integrated_indices(TsxData * tsx_data); bool should_compute_bp_now() const; void compute_bproof(messages::monero::MoneroTransactionRsigData & rsig_data); void process_bproof(rct::Bulletproof & bproof); + void set_tx_input(MoneroTransactionSourceEntry * dst, size_t idx, bool need_ring_keys=false, bool need_ring_indices=false); public: Signer(wallet_shim * wallet2, const unsigned_tx_set * unsigned_tx, size_t tx_idx = 0, hw::tx_aux_data * aux_data = nullptr); diff --git a/src/device_trezor/trezor/transport.cpp b/src/device_trezor/trezor/transport.cpp index 59b281f13..a56090de0 100644 --- a/src/device_trezor/trezor/transport.cpp +++ b/src/device_trezor/trezor/transport.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // @@ -32,6 +32,7 @@ #endif #include <algorithm> +#include <functional> #include <boost/endian/conversion.hpp> #include <boost/asio/io_service.hpp> #include <boost/asio/ip/udp.hpp> @@ -56,6 +57,11 @@ namespace trezor{ return true; } + bool t_serialize(const epee::wipeable_string & in, std::string & out){ + out.assign(in.data(), in.size()); + return true; + } + bool t_serialize(const json_val & in, std::string & out){ rapidjson::StringBuffer sb; rapidjson::Writer<rapidjson::StringBuffer> writer(sb); @@ -75,6 +81,11 @@ namespace trezor{ return true; } + bool t_deserialize(std::string & in, epee::wipeable_string & out){ + out = epee::wipeable_string(in); + return true; + } + bool t_deserialize(const std::string & in, json & out){ if (out.Parse(in.c_str()).HasParseError()) { throw exc::CommunicationException("JSON parse error"); @@ -192,61 +203,69 @@ namespace trezor{ const auto msg_size = message_size(req); const auto buff_size = serialize_message_buffer_size(msg_size) + 2; - std::unique_ptr<uint8_t[]> req_buff(new uint8_t[buff_size]); - uint8_t * req_buff_raw = req_buff.get(); + epee::wipeable_string req_buff; + epee::wipeable_string chunk_buff; + + req_buff.resize(buff_size); + chunk_buff.resize(REPLEN); + + uint8_t * req_buff_raw = reinterpret_cast<uint8_t *>(req_buff.data()); + uint8_t * chunk_buff_raw = reinterpret_cast<uint8_t *>(chunk_buff.data()); + req_buff_raw[0] = '#'; req_buff_raw[1] = '#'; serialize_message(req, msg_size, req_buff_raw + 2, buff_size - 2); size_t offset = 0; - uint8_t chunk_buff[REPLEN]; // Chunk by chunk upload while(offset < buff_size){ auto to_copy = std::min((size_t)(buff_size - offset), (size_t)(REPLEN - 1)); - chunk_buff[0] = '?'; - memcpy(chunk_buff + 1, req_buff_raw + offset, to_copy); + chunk_buff_raw[0] = '?'; + memcpy(chunk_buff_raw + 1, req_buff_raw + offset, to_copy); // Pad with zeros if (to_copy < REPLEN - 1){ - memset(chunk_buff + 1 + to_copy, 0, REPLEN - 1 - to_copy); + memset(chunk_buff_raw + 1 + to_copy, 0, REPLEN - 1 - to_copy); } - transport.write_chunk(chunk_buff, REPLEN); + transport.write_chunk(chunk_buff_raw, REPLEN); offset += REPLEN - 1; } } void ProtocolV1::read(Transport & transport, std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type){ - char chunk[REPLEN]; + epee::wipeable_string chunk_buff; + chunk_buff.resize(REPLEN); + char * chunk_buff_raw = chunk_buff.data(); // Initial chunk read - size_t nread = transport.read_chunk(chunk, REPLEN); + size_t nread = transport.read_chunk(chunk_buff_raw, REPLEN); if (nread != REPLEN){ throw exc::CommunicationException("Read chunk has invalid size"); } - if (strncmp(chunk, "?##", 3) != 0){ + if (memcmp(chunk_buff_raw, "?##", 3) != 0){ throw exc::CommunicationException("Malformed chunk"); } uint16_t tag; uint32_t len; nread -= 3 + 6; - deserialize_message_header(chunk + 3, tag, len); + deserialize_message_header(chunk_buff_raw + 3, tag, len); - std::string data_acc(chunk + 3 + 6, nread); + epee::wipeable_string data_acc(chunk_buff_raw + 3 + 6, nread); data_acc.reserve(len); while(nread < len){ - const size_t cur = transport.read_chunk(chunk, REPLEN); - if (chunk[0] != '?'){ + const size_t cur = transport.read_chunk(chunk_buff_raw, REPLEN); + if (chunk_buff_raw[0] != '?'){ throw exc::CommunicationException("Chunk malformed"); } - data_acc.append(chunk + 1, cur - 1); + data_acc.append(chunk_buff_raw + 1, cur - 1); nread += cur - 1; } @@ -259,7 +278,7 @@ namespace trezor{ } std::shared_ptr<google::protobuf::Message> msg_wrap(MessageMapper::get_message(tag)); - if (!msg_wrap->ParseFromArray(data_acc.c_str(), len)){ + if (!msg_wrap->ParseFromArray(data_acc.data(), len)){ throw exc::CommunicationException("Message could not be parsed"); } @@ -426,15 +445,16 @@ namespace trezor{ const auto msg_size = message_size(req); const auto buff_size = serialize_message_buffer_size(msg_size); + epee::wipeable_string req_buff; + req_buff.resize(buff_size); - std::unique_ptr<uint8_t[]> req_buff(new uint8_t[buff_size]); - uint8_t * req_buff_raw = req_buff.get(); + uint8_t * req_buff_raw = reinterpret_cast<uint8_t *>(req_buff.data()); serialize_message(req, msg_size, req_buff_raw, buff_size); std::string uri = "/call/" + m_session.get(); - std::string req_hex = epee::to_hex::string(epee::span<const std::uint8_t>(req_buff_raw, buff_size)); - std::string res_hex; + epee::wipeable_string res_hex; + epee::wipeable_string req_hex = epee::to_hex::wipeable_string(epee::span<const std::uint8_t>(req_buff_raw, buff_size)); bool req_status = invoke_bridge_http(uri, req_hex, res_hex, m_http_client); if (!req_status){ @@ -449,15 +469,15 @@ namespace trezor{ throw exc::CommunicationException("Could not read, no response stored"); } - std::string bin_data; - if (!epee::string_tools::parse_hexstr_to_binbuff(m_response.get(), bin_data)){ + boost::optional<epee::wipeable_string> bin_data = m_response->parse_hexstr(); + if (!bin_data){ throw exc::CommunicationException("Response is not well hexcoded"); } uint16_t msg_tag; uint32_t msg_len; - deserialize_message_header(bin_data.c_str(), msg_tag, msg_len); - if (bin_data.size() != msg_len + 6){ + deserialize_message_header(bin_data->data(), msg_tag, msg_len); + if (bin_data->size() != msg_len + 6){ throw exc::CommunicationException("Response is not well hexcoded"); } @@ -466,7 +486,7 @@ namespace trezor{ } std::shared_ptr<google::protobuf::Message> msg_wrap(MessageMapper::get_message(msg_tag)); - if (!msg_wrap->ParseFromArray(bin_data.c_str() + 6, msg_len)){ + if (!msg_wrap->ParseFromArray(bin_data->data() + 6, msg_len)){ throw exc::EncodingException("Response is not well hexcoded"); } msg = msg_wrap; @@ -692,7 +712,7 @@ namespace trezor{ // Start the asynchronous operation itself. The handle_receive function // used as a callback will update the ec and length variables. m_socket->async_receive_from(boost::asio::buffer(buffer), m_endpoint, - boost::bind(&UdpTransport::handle_receive, _1, _2, &ec, &length)); + std::bind(&UdpTransport::handle_receive, std::placeholders::_1, std::placeholders::_2, &ec, &length)); // Block until the asynchronous operation has completed. do { diff --git a/src/device_trezor/trezor/transport.hpp b/src/device_trezor/trezor/transport.hpp index affd91553..ff843f06b 100644 --- a/src/device_trezor/trezor/transport.hpp +++ b/src/device_trezor/trezor/transport.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // @@ -66,10 +66,12 @@ namespace trezor { // Base HTTP comm serialization. bool t_serialize(const std::string & in, std::string & out); + bool t_serialize(const epee::wipeable_string & in, std::string & out); bool t_serialize(const json_val & in, std::string & out); std::string t_serialize(const json_val & in); bool t_deserialize(const std::string & in, std::string & out); + bool t_deserialize(std::string & in, epee::wipeable_string & out); bool t_deserialize(const std::string & in, json & out); // Flexible json serialization. HTTP client tailored for bridge API @@ -84,6 +86,13 @@ namespace trezor { additional_params.push_back(std::make_pair("Content-Type","application/json; charset=utf-8")); const http::http_response_info* pri = nullptr; + const auto data_cleaner = epee::misc_utils::create_scope_leave_handler([&]() { + if (!req_param.empty()) { + memwipe(&req_param[0], req_param.size()); + } + transport.wipe_response(); + }); + if(!transport.invoke(uri, method, req_param, timeout, &pri, std::move(additional_params))) { MERROR("Failed to invoke http request to " << uri); @@ -103,7 +112,7 @@ namespace trezor { return false; } - return t_deserialize(pri->m_body, result_struct); + return t_deserialize(const_cast<http::http_response_info*>(pri)->m_body, result_struct); } // Forward decl @@ -186,7 +195,7 @@ namespace trezor { std::string m_bridge_host; boost::optional<std::string> m_device_path; boost::optional<std::string> m_session; - boost::optional<std::string> m_response; + boost::optional<epee::wipeable_string> m_response; boost::optional<json> m_device_info; }; diff --git a/src/device_trezor/trezor/trezor_defs.hpp b/src/device_trezor/trezor/trezor_defs.hpp index f0697cdb5..9882d6d4b 100644 --- a/src/device_trezor/trezor/trezor_defs.hpp +++ b/src/device_trezor/trezor/trezor_defs.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/gen_multisig/CMakeLists.txt b/src/gen_multisig/CMakeLists.txt index 3a5e29273..6d8bdfb5c 100644 --- a/src/gen_multisig/CMakeLists.txt +++ b/src/gen_multisig/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019, The Monero Project +# Copyright (c) 2017-2020, The Monero Project # # All rights reserved. # diff --git a/src/gen_multisig/gen_multisig.cpp b/src/gen_multisig/gen_multisig.cpp index 06019c50e..15f7e5c0a 100644 --- a/src/gen_multisig/gen_multisig.cpp +++ b/src/gen_multisig/gen_multisig.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/gen_ssl_cert/CMakeLists.txt b/src/gen_ssl_cert/CMakeLists.txt index 471df021b..6203feb21 100644 --- a/src/gen_ssl_cert/CMakeLists.txt +++ b/src/gen_ssl_cert/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019, The Monero Project +# Copyright (c) 2017-2020, The Monero Project # # All rights reserved. # diff --git a/src/gen_ssl_cert/gen_ssl_cert.cpp b/src/gen_ssl_cert/gen_ssl_cert.cpp index 7a9b01700..1a048e9e8 100644 --- a/src/gen_ssl_cert/gen_ssl_cert.cpp +++ b/src/gen_ssl_cert/gen_ssl_cert.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/hardforks/CMakeLists.txt b/src/hardforks/CMakeLists.txt index bd2d14ceb..46f51e09d 100644 --- a/src/hardforks/CMakeLists.txt +++ b/src/hardforks/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/hardforks/hardforks.cpp b/src/hardforks/hardforks.cpp index 7ad09dbef..f4de0ddcf 100644 --- a/src/hardforks/hardforks.cpp +++ b/src/hardforks/hardforks.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/hardforks/hardforks.h b/src/hardforks/hardforks.h index e7bceca42..039f18176 100644 --- a/src/hardforks/hardforks.h +++ b/src/hardforks/hardforks.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/mnemonics/CMakeLists.txt b/src/mnemonics/CMakeLists.txt index acb9a4338..ed53a41f6 100644 --- a/src/mnemonics/CMakeLists.txt +++ b/src/mnemonics/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/mnemonics/chinese_simplified.h b/src/mnemonics/chinese_simplified.h index ff035dde0..7a211bc34 100644 --- a/src/mnemonics/chinese_simplified.h +++ b/src/mnemonics/chinese_simplified.h @@ -21,7 +21,7 @@ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-// Code surrounding the word list is Copyright (c) 2014-2019, The Monero Project
+// Code surrounding the word list is Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/dutch.h b/src/mnemonics/dutch.h index 14ba56a7e..29c346d42 100644 --- a/src/mnemonics/dutch.h +++ b/src/mnemonics/dutch.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project
+// Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/electrum-words.cpp b/src/mnemonics/electrum-words.cpp index 2dd40cc9a..bda18d950 100644 --- a/src/mnemonics/electrum-words.cpp +++ b/src/mnemonics/electrum-words.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/mnemonics/electrum-words.h b/src/mnemonics/electrum-words.h index 9aa727e45..8d4c5be66 100644 --- a/src/mnemonics/electrum-words.h +++ b/src/mnemonics/electrum-words.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/mnemonics/english.h b/src/mnemonics/english.h index 677b70052..b7eb3d643 100644 --- a/src/mnemonics/english.h +++ b/src/mnemonics/english.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project
+// Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/english_old.h b/src/mnemonics/english_old.h index 179fc45ec..f408b2a6c 100644 --- a/src/mnemonics/english_old.h +++ b/src/mnemonics/english_old.h @@ -1,6 +1,6 @@ // Word list originally created as part of the Electrum project, Copyright (C) 2014 Thomas Voegtlin
//
-// Copyright (c) 2014-2019, The Monero Project
+// Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/esperanto.h b/src/mnemonics/esperanto.h index ef63a2235..4897fe0b3 100644 --- a/src/mnemonics/esperanto.h +++ b/src/mnemonics/esperanto.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project
+// Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/french.h b/src/mnemonics/french.h index 85925aa53..d5a7b6cb6 100644 --- a/src/mnemonics/french.h +++ b/src/mnemonics/french.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/mnemonics/german.h b/src/mnemonics/german.h index 40116970c..c38a1fb83 100644 --- a/src/mnemonics/german.h +++ b/src/mnemonics/german.h @@ -1,6 +1,6 @@ // Word list created by Monero contributor Shrikez
//
-// Copyright (c) 2014-2019, The Monero Project
+// Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/italian.h b/src/mnemonics/italian.h index 204fdcb78..f202e23e2 100644 --- a/src/mnemonics/italian.h +++ b/src/mnemonics/italian.h @@ -1,6 +1,6 @@ // Word list created by Monero contributor Shrikez
//
-// Copyright (c) 2014-2019, The Monero Project
+// Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/japanese.h b/src/mnemonics/japanese.h index 6213c4976..899d78f48 100644 --- a/src/mnemonics/japanese.h +++ b/src/mnemonics/japanese.h @@ -21,7 +21,7 @@ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-// Code surrounding the word list is Copyright (c) 2014-2019, The Monero Project
+// Code surrounding the word list is Copyright (c) 2014-2020, The Monero Project
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
diff --git a/src/mnemonics/language_base.h b/src/mnemonics/language_base.h index 7d2599e9a..bf8793aa2 100644 --- a/src/mnemonics/language_base.h +++ b/src/mnemonics/language_base.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project
+// Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
@@ -41,6 +41,7 @@ #include <boost/algorithm/string.hpp>
#include "misc_log_ex.h"
#include "fnv1.h"
+#include "common/utf8.h"
/*!
* \namespace Language
@@ -73,78 +74,11 @@ namespace Language return prefix;
}
- template<typename T>
- inline T utf8canonical(const T &s)
- {
- T sc = "";
- size_t avail = s.size();
- const char *ptr = s.data();
- wint_t cp = 0;
- int bytes = 1;
- char wbuf[8], *wptr;
- while (avail--)
- {
- if ((*ptr & 0x80) == 0)
- {
- cp = *ptr++;
- bytes = 1;
- }
- else if ((*ptr & 0xe0) == 0xc0)
- {
- if (avail < 1)
- throw std::runtime_error("Invalid UTF-8");
- cp = (*ptr++ & 0x1f) << 6;
- cp |= *ptr++ & 0x3f;
- --avail;
- bytes = 2;
- }
- else if ((*ptr & 0xf0) == 0xe0)
- {
- if (avail < 2)
- throw std::runtime_error("Invalid UTF-8");
- cp = (*ptr++ & 0xf) << 12;
- cp |= (*ptr++ & 0x3f) << 6;
- cp |= *ptr++ & 0x3f;
- avail -= 2;
- bytes = 3;
- }
- else if ((*ptr & 0xf8) == 0xf0)
- {
- if (avail < 3)
- throw std::runtime_error("Invalid UTF-8");
- cp = (*ptr++ & 0x7) << 18;
- cp |= (*ptr++ & 0x3f) << 12;
- cp |= (*ptr++ & 0x3f) << 6;
- cp |= *ptr++ & 0x3f;
- avail -= 3;
- bytes = 4;
- }
- else
- throw std::runtime_error("Invalid UTF-8");
-
- cp = std::towlower(cp);
- wptr = wbuf;
- switch (bytes)
- {
- case 1: *wptr++ = cp; break;
- case 2: *wptr++ = 0xc0 | (cp >> 6); *wptr++ = 0x80 | (cp & 0x3f); break;
- case 3: *wptr++ = 0xe0 | (cp >> 12); *wptr++ = 0x80 | ((cp >> 6) & 0x3f); *wptr++ = 0x80 | (cp & 0x3f); break;
- case 4: *wptr++ = 0xf0 | (cp >> 18); *wptr++ = 0x80 | ((cp >> 12) & 0x3f); *wptr++ = 0x80 | ((cp >> 6) & 0x3f); *wptr++ = 0x80 | (cp & 0x3f); break;
- default: throw std::runtime_error("Invalid UTF-8");
- }
- *wptr = 0;
- sc += T(wbuf, bytes);
- cp = 0;
- bytes = 1;
- }
- return sc;
- }
-
struct WordHash
{
std::size_t operator()(const epee::wipeable_string &s) const
{
- const epee::wipeable_string sc = utf8canonical(s);
+ const epee::wipeable_string sc = tools::utf8canonical(s, [](wint_t c) -> wint_t { return std::towlower(c); });
return epee::fnv::FNV1a(sc.data(), sc.size());
}
};
@@ -153,8 +87,8 @@ namespace Language {
bool operator()(const epee::wipeable_string &s0, const epee::wipeable_string &s1) const
{
- const epee::wipeable_string s0c = utf8canonical(s0);
- const epee::wipeable_string s1c = utf8canonical(s1);
+ const epee::wipeable_string s0c = tools::utf8canonical(s0, [](wint_t c) -> wint_t { return std::towlower(c); });
+ const epee::wipeable_string s1c = tools::utf8canonical(s1, [](wint_t c) -> wint_t { return std::towlower(c); });
return s0c == s1c;
}
};
diff --git a/src/mnemonics/lojban.h b/src/mnemonics/lojban.h index b6baf46c4..95d8b4633 100644 --- a/src/mnemonics/lojban.h +++ b/src/mnemonics/lojban.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project
+// Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/portuguese.h b/src/mnemonics/portuguese.h index dbc51fb2a..090278f27 100644 --- a/src/mnemonics/portuguese.h +++ b/src/mnemonics/portuguese.h @@ -21,7 +21,7 @@ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-// Code surrounding the word list is Copyright (c) 2014-2019, The Monero Project
+// Code surrounding the word list is Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/russian.h b/src/mnemonics/russian.h index d36942a9b..ca2629c1a 100644 --- a/src/mnemonics/russian.h +++ b/src/mnemonics/russian.h @@ -1,6 +1,6 @@ // Word list created by Monero contributor sammy007
//
-// Copyright (c) 2014-2019, The Monero Project
+// Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/singleton.h b/src/mnemonics/singleton.h index ffe3fe4de..10d220b65 100644 --- a/src/mnemonics/singleton.h +++ b/src/mnemonics/singleton.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project
+// Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/spanish.h b/src/mnemonics/spanish.h index a05485775..f3adaaf42 100644 --- a/src/mnemonics/spanish.h +++ b/src/mnemonics/spanish.h @@ -21,7 +21,7 @@ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-// Code surrounding the word list is Copyright (c) 2014-2019, The Monero Project
+// Code surrounding the word list is Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
diff --git a/src/multisig/CMakeLists.txt b/src/multisig/CMakeLists.txt index 4631b75ef..eaa2c6f71 100644 --- a/src/multisig/CMakeLists.txt +++ b/src/multisig/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019, The Monero Project +# Copyright (c) 2017-2020, The Monero Project # # All rights reserved. # diff --git a/src/multisig/multisig.cpp b/src/multisig/multisig.cpp index 999894db0..272de73b2 100644 --- a/src/multisig/multisig.cpp +++ b/src/multisig/multisig.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // @@ -82,6 +82,7 @@ namespace cryptonote { rct::key sk = rct::scalarmultKey(rct::pk2rct(k), rct::sk2rct(blinded_skey)); crypto::secret_key msk = get_multisig_blinded_secret_key(rct::rct2sk(sk)); + memwipe(&sk, sizeof(sk)); multisig_keys.push_back(msk); sc_add(spend_skey.bytes, spend_skey.bytes, (const unsigned char*)msk.data); } @@ -126,10 +127,10 @@ namespace cryptonote //----------------------------------------------------------------- crypto::secret_key generate_multisig_view_secret_key(const crypto::secret_key &skey, const std::vector<crypto::secret_key> &skeys) { - rct::key view_skey = rct::sk2rct(get_multisig_blinded_secret_key(skey)); + crypto::secret_key view_skey = get_multisig_blinded_secret_key(skey); for (const auto &k: skeys) - sc_add(view_skey.bytes, view_skey.bytes, rct::sk2rct(k).bytes); - return rct::rct2sk(view_skey); + sc_add((unsigned char*)&view_skey, rct::sk2rct(view_skey).bytes, rct::sk2rct(k).bytes); + return view_skey; } //----------------------------------------------------------------- crypto::public_key generate_multisig_M_N_spend_public_key(const std::vector<crypto::public_key> &pkeys) diff --git a/src/multisig/multisig.h b/src/multisig/multisig.h index bc6edbb05..eab32187c 100644 --- a/src/multisig/multisig.h +++ b/src/multisig/multisig.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/net/dandelionpp.cpp b/src/net/dandelionpp.cpp index 4d2f75428..e0d2821e5 100644 --- a/src/net/dandelionpp.cpp +++ b/src/net/dandelionpp.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/net/dandelionpp.h b/src/net/dandelionpp.h index 75b63bc0c..345f868af 100644 --- a/src/net/dandelionpp.h +++ b/src/net/dandelionpp.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/net/i2p_address.cpp b/src/net/i2p_address.cpp index f4cc75fee..6c03b3808 100644 --- a/src/net/i2p_address.cpp +++ b/src/net/i2p_address.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/net/i2p_address.h b/src/net/i2p_address.h index 28a1118ba..5a694419d 100644 --- a/src/net/i2p_address.h +++ b/src/net/i2p_address.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/net/socks.cpp b/src/net/socks.cpp index 5a27e16f4..3463f452c 100644 --- a/src/net/socks.cpp +++ b/src/net/socks.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2019, The Monero Project +// Copyright (c) 2018-2020, The Monero Project // // All rights reserved. // diff --git a/src/net/socks.h b/src/net/socks.h index 4d1d34e9e..739c972ab 100644 --- a/src/net/socks.h +++ b/src/net/socks.h @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2019, The Monero Project +// Copyright (c) 2018-2020, The Monero Project // // All rights reserved. // diff --git a/src/net/socks_connect.cpp b/src/net/socks_connect.cpp index a5557f6f8..9db9d4483 100644 --- a/src/net/socks_connect.cpp +++ b/src/net/socks_connect.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/net/socks_connect.h b/src/net/socks_connect.h index 44b0fa2b3..45bb10efe 100644 --- a/src/net/socks_connect.h +++ b/src/net/socks_connect.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/net/zmq.cpp b/src/net/zmq.cpp index d02a22983..15560ca7e 100644 --- a/src/net/zmq.cpp +++ b/src/net/zmq.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // @@ -33,6 +33,8 @@ #include <limits> #include <utility> +#include "byte_slice.h" + namespace net { namespace zmq @@ -156,20 +158,6 @@ namespace zmq return unsigned(max_out) < added ? max_out : int(added); } }; - - template<typename F, typename... T> - expect<void> retry_op(F op, T&&... args) noexcept(noexcept(op(args...))) - { - for (;;) - { - if (0 <= op(args...)) - return success(); - - const int error = zmq_errno(); - if (error != EINTR) - return make_error_code(error); - } - } } // anonymous expect<std::string> receive(void* const socket, const int flags) @@ -183,6 +171,22 @@ namespace zmq { return retry_op(zmq_send, socket, payload.data(), payload.size(), flags); } + + expect<void> send(epee::byte_slice&& payload, void* socket, int flags) noexcept + { + void* const data = const_cast<std::uint8_t*>(payload.data()); + const std::size_t size = payload.size(); + auto buffer = payload.take_buffer(); // clears `payload` from callee + + zmq_msg_t msg{}; + MONERO_ZMQ_CHECK(zmq_msg_init_data(std::addressof(msg), data, size, epee::release_byte_slice::call, buffer.get())); + buffer.release(); // zmq will now decrement byte_slice ref-count + + expect<void> sent = retry_op(zmq_msg_send, std::addressof(msg), socket, flags); + if (!sent) // beware if removing `noexcept` from this function - possible leak here + zmq_msg_close(std::addressof(msg)); + return sent; + } } // zmq } // net diff --git a/src/net/zmq.h b/src/net/zmq.h index c6a7fd743..fa4ef2fc9 100644 --- a/src/net/zmq.h +++ b/src/net/zmq.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // @@ -26,6 +26,8 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#pragma once + #include <memory> #include <string> #include <system_error> @@ -53,6 +55,11 @@ #define MONERO_ZMQ_THROW(msg) \ MONERO_THROW( ::net::zmq::get_error_code(), msg ) +namespace epee +{ + class byte_slice; +} + namespace net { namespace zmq @@ -100,6 +107,26 @@ namespace zmq //! Unique ZMQ socket handle, calls `zmq_close` on destruction. using socket = std::unique_ptr<void, close>; + /*! Retry a ZMQ function on `EINTR` errors. `F` must return an int with + values less than 0 on error. + + \param op The ZMQ function to execute + retry + \param args Forwarded to `op`. Must be resuable in case of retry. + \return All errors except for `EINTR`. */ + template<typename F, typename... T> + expect<void> retry_op(F op, T&&... args) noexcept(noexcept(op(args...))) + { + for (;;) + { + if (0 <= op(args...)) + return success(); + + const int error = zmq_errno(); + if (error != EINTR) + return make_error_code(error); + } + } + /*! Read all parts of the next message on `socket`. Blocks until the entire next message (all parts) are read, or until `zmq_term` is called on the `zmq_context` associated with `socket`. If the context is terminated, @@ -132,5 +159,24 @@ namespace zmq \param flags See `zmq_send` for possible flags. \return `success()` if sent, otherwise ZMQ error. */ expect<void> send(epee::span<const std::uint8_t> payload, void* socket, int flags = 0) noexcept; + + /*! Sends `payload` on `socket`. Blocks until the entire message is queued + for sending, or until `zmq_term` is called on the `zmq_context` + associated with `socket`. If the context is terminated, + `make_error_code(ETERM)` is returned. + + \note This will automatically retry on `EINTR`, so exiting on + interrupts requires context termination. + \note If non-blocking behavior is requested on `socket` or by `flags`, + then `net::zmq::make_error_code(EAGAIN)` will be returned if this + would block. + + \param payload sent as one message on `socket`. + \param socket Handle created with `zmq_socket`. + \param flags See `zmq_msg_send` for possible flags. + + \post `payload.emtpy()` - ownership is transferred to zmq. + \return `success()` if sent, otherwise ZMQ error. */ + expect<void> send(epee::byte_slice&& payload, void* socket, int flags = 0) noexcept; } // zmq } // net diff --git a/src/p2p/CMakeLists.txt b/src/p2p/CMakeLists.txt index 3aecc3cf9..cfd8273b4 100644 --- a/src/p2p/CMakeLists.txt +++ b/src/p2p/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp index 58c5706de..1c5dca0d7 100644 --- a/src/p2p/net_node.cpp +++ b/src/p2p/net_node.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 5337106dd..e81371783 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -384,6 +384,7 @@ namespace nodetool bool is_addr_recently_failed(const epee::net_utils::network_address& addr); bool is_priority_node(const epee::net_utils::network_address& na); std::set<std::string> get_seed_nodes(cryptonote::network_type nettype) const; + std::set<std::string> get_seed_nodes(); bool connect_to_seed(); template <class Container> @@ -467,7 +468,9 @@ namespace nodetool std::list<epee::net_utils::network_address> m_priority_peers; std::vector<epee::net_utils::network_address> m_exclusive_peers; std::vector<epee::net_utils::network_address> m_seed_nodes; - bool m_fallback_seed_nodes_added; + bool m_seed_nodes_initialized = false; + boost::shared_mutex m_seed_nodes_lock; + std::atomic_flag m_fallback_seed_nodes_added; std::vector<nodetool::peerlist_entry> m_command_line_peers; uint64_t m_peer_livetime; //keep connections to initiate some interactions diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 9ff2627d0..175741146 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -31,7 +31,7 @@ // IP blocking adapted from Boolberry #include <algorithm> -#include <boost/bind.hpp> +#include <boost/bind/bind.hpp> #include <boost/date_time/posix_time/posix_time.hpp> #include <boost/filesystem/operations.hpp> #include <boost/optional/optional.hpp> @@ -435,6 +435,8 @@ namespace nodetool if (command_line::has_arg(vm, arg_p2p_seed_node)) { + boost::unique_lock<boost::shared_mutex> lock(m_seed_nodes_lock); + if (!parse_peers_and_add_to_container(vm, arg_p2p_seed_node, m_seed_nodes)) return false; } @@ -602,16 +604,12 @@ namespace nodetool if (nettype == cryptonote::TESTNET) { full_addrs.insert("212.83.175.67:28080"); - full_addrs.insert("5.9.100.248:28080"); - full_addrs.insert("163.172.182.165:28080"); - full_addrs.insert("195.154.123.123:28080"); full_addrs.insert("212.83.172.165:28080"); full_addrs.insert("192.110.160.146:28080"); } else if (nettype == cryptonote::STAGENET) { full_addrs.insert("162.210.173.150:38080"); - full_addrs.insert("162.210.173.151:38080"); full_addrs.insert("192.110.160.146:38080"); } else if (nettype == cryptonote::FAKECHAIN) @@ -619,18 +617,124 @@ namespace nodetool } else { - full_addrs.insert("107.152.130.98:18080"); full_addrs.insert("212.83.175.67:18080"); - full_addrs.insert("5.9.100.248:18080"); - full_addrs.insert("163.172.182.165:18080"); - full_addrs.insert("161.67.132.39:18080"); - full_addrs.insert("198.74.231.92:18080"); - full_addrs.insert("195.154.123.123:18080"); full_addrs.insert("212.83.172.165:18080"); full_addrs.insert("192.110.160.146:18080"); full_addrs.insert("88.198.163.90:18080"); full_addrs.insert("95.217.25.101:18080"); + full_addrs.insert("209.250.243.248:18080"); + full_addrs.insert("104.238.221.81:18080"); + full_addrs.insert("66.85.74.134:18080"); + } + return full_addrs; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + std::set<std::string> node_server<t_payload_net_handler>::get_seed_nodes() + { + if (!m_exclusive_peers.empty() || m_offline) + { + return {}; + } + if (m_nettype == cryptonote::TESTNET) + { + return get_seed_nodes(cryptonote::TESTNET); + } + if (m_nettype == cryptonote::STAGENET) + { + return get_seed_nodes(cryptonote::STAGENET); + } + + std::set<std::string> full_addrs; + + // for each hostname in the seed nodes list, attempt to DNS resolve and + // add the result addresses as seed nodes + // TODO: at some point add IPv6 support, but that won't be relevant + // for some time yet. + + std::vector<std::vector<std::string>> dns_results; + dns_results.resize(m_seed_nodes_list.size()); + + // some libc implementation provide only a very small stack + // for threads, e.g. musl only gives +- 80kb, which is not + // enough to do a resolve with unbound. we request a stack + // of 1 mb, which should be plenty + boost::thread::attributes thread_attributes; + thread_attributes.set_stack_size(1024*1024); + + std::list<boost::thread> dns_threads; + uint64_t result_index = 0; + for (const std::string& addr_str : m_seed_nodes_list) + { + boost::thread th = boost::thread(thread_attributes, [=, &dns_results, &addr_str] + { + MDEBUG("dns_threads[" << result_index << "] created for: " << addr_str); + // TODO: care about dnssec avail/valid + bool avail, valid; + std::vector<std::string> addr_list; + + try + { + addr_list = tools::DNSResolver::instance().get_ipv4(addr_str, avail, valid); + MDEBUG("dns_threads[" << result_index << "] DNS resolve done"); + boost::this_thread::interruption_point(); + } + catch(const boost::thread_interrupted&) + { + // thread interruption request + // even if we now have results, finish thread without setting + // result variables, which are now out of scope in main thread + MWARNING("dns_threads[" << result_index << "] interrupted"); + return; + } + + MINFO("dns_threads[" << result_index << "] addr_str: " << addr_str << " number of results: " << addr_list.size()); + dns_results[result_index] = addr_list; + }); + + dns_threads.push_back(std::move(th)); + ++result_index; + } + + MDEBUG("dns_threads created, now waiting for completion or timeout of " << CRYPTONOTE_DNS_TIMEOUT_MS << "ms"); + boost::chrono::system_clock::time_point deadline = boost::chrono::system_clock::now() + boost::chrono::milliseconds(CRYPTONOTE_DNS_TIMEOUT_MS); + uint64_t i = 0; + for (boost::thread& th : dns_threads) + { + if (! th.try_join_until(deadline)) + { + MWARNING("dns_threads[" << i << "] timed out, sending interrupt"); + th.interrupt(); + } + ++i; + } + + i = 0; + for (const auto& result : dns_results) + { + MDEBUG("DNS lookup for " << m_seed_nodes_list[i] << ": " << result.size() << " results"); + // if no results for node, thread's lookup likely timed out + if (result.size()) + { + for (const auto& addr_string : result) + full_addrs.insert(addr_string + ":" + std::to_string(cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT)); + } + ++i; + } + + // append the fallback nodes if we have too few seed nodes to start with + if (full_addrs.size() < MIN_WANTED_SEED_NODES) + { + if (full_addrs.empty()) + MINFO("DNS seed node lookup either timed out or failed, falling back to defaults"); + else + MINFO("Not enough DNS seed nodes found, using fallback defaults too"); + + for (const auto &peer: get_seed_nodes(cryptonote::MAINNET)) + full_addrs.insert(peer); + m_fallback_seed_nodes_added.test_and_set(); } + return full_addrs; } //----------------------------------------------------------------------------------- @@ -648,123 +752,21 @@ namespace nodetool template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::init(const boost::program_options::variables_map& vm) { - std::set<std::string> full_addrs; - bool res = handle_command_line(vm); CHECK_AND_ASSERT_MES(res, false, "Failed to handle command line"); - m_fallback_seed_nodes_added = false; if (m_nettype == cryptonote::TESTNET) { memcpy(&m_network_id, &::config::testnet::NETWORK_ID, 16); - full_addrs = get_seed_nodes(cryptonote::TESTNET); } else if (m_nettype == cryptonote::STAGENET) { memcpy(&m_network_id, &::config::stagenet::NETWORK_ID, 16); - full_addrs = get_seed_nodes(cryptonote::STAGENET); } else { memcpy(&m_network_id, &::config::NETWORK_ID, 16); - if (m_exclusive_peers.empty() && !m_offline) - { - // for each hostname in the seed nodes list, attempt to DNS resolve and - // add the result addresses as seed nodes - // TODO: at some point add IPv6 support, but that won't be relevant - // for some time yet. - - std::vector<std::vector<std::string>> dns_results; - dns_results.resize(m_seed_nodes_list.size()); - - // some libc implementation provide only a very small stack - // for threads, e.g. musl only gives +- 80kb, which is not - // enough to do a resolve with unbound. we request a stack - // of 1 mb, which should be plenty - boost::thread::attributes thread_attributes; - thread_attributes.set_stack_size(1024*1024); - - std::list<boost::thread> dns_threads; - uint64_t result_index = 0; - for (const std::string& addr_str : m_seed_nodes_list) - { - boost::thread th = boost::thread(thread_attributes, [=, &dns_results, &addr_str] - { - MDEBUG("dns_threads[" << result_index << "] created for: " << addr_str); - // TODO: care about dnssec avail/valid - bool avail, valid; - std::vector<std::string> addr_list; - - try - { - addr_list = tools::DNSResolver::instance().get_ipv4(addr_str, avail, valid); - MDEBUG("dns_threads[" << result_index << "] DNS resolve done"); - boost::this_thread::interruption_point(); - } - catch(const boost::thread_interrupted&) - { - // thread interruption request - // even if we now have results, finish thread without setting - // result variables, which are now out of scope in main thread - MWARNING("dns_threads[" << result_index << "] interrupted"); - return; - } - - MINFO("dns_threads[" << result_index << "] addr_str: " << addr_str << " number of results: " << addr_list.size()); - dns_results[result_index] = addr_list; - }); - - dns_threads.push_back(std::move(th)); - ++result_index; - } - - MDEBUG("dns_threads created, now waiting for completion or timeout of " << CRYPTONOTE_DNS_TIMEOUT_MS << "ms"); - boost::chrono::system_clock::time_point deadline = boost::chrono::system_clock::now() + boost::chrono::milliseconds(CRYPTONOTE_DNS_TIMEOUT_MS); - uint64_t i = 0; - for (boost::thread& th : dns_threads) - { - if (! th.try_join_until(deadline)) - { - MWARNING("dns_threads[" << i << "] timed out, sending interrupt"); - th.interrupt(); - } - ++i; - } - - i = 0; - for (const auto& result : dns_results) - { - MDEBUG("DNS lookup for " << m_seed_nodes_list[i] << ": " << result.size() << " results"); - // if no results for node, thread's lookup likely timed out - if (result.size()) - { - for (const auto& addr_string : result) - full_addrs.insert(addr_string + ":" + std::to_string(cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT)); - } - ++i; - } - - // append the fallback nodes if we have too few seed nodes to start with - if (full_addrs.size() < MIN_WANTED_SEED_NODES) - { - if (full_addrs.empty()) - MINFO("DNS seed node lookup either timed out or failed, falling back to defaults"); - else - MINFO("Not enough DNS seed nodes found, using fallback defaults too"); - - for (const auto &peer: get_seed_nodes(cryptonote::MAINNET)) - full_addrs.insert(peer); - m_fallback_seed_nodes_added = true; - } - } - } - - for (const auto& full_addr : full_addrs) - { - MDEBUG("Seed node: " << full_addr); - append_net_address(m_seed_nodes, full_addr, cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT); } - MDEBUG("Number of seed nodes: " << m_seed_nodes.size()); m_config_folder = command_line::get_arg(vm, cryptonote::arg_data_dir); network_zone& public_zone = m_network_zones.at(epee::net_utils::zone::public_); @@ -1541,6 +1543,20 @@ namespace nodetool template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::connect_to_seed() { + boost::upgrade_lock<boost::shared_mutex> seed_nodes_upgrade_lock(m_seed_nodes_lock); + + if (!m_seed_nodes_initialized) + { + boost::upgrade_to_unique_lock<boost::shared_mutex> seed_nodes_lock(seed_nodes_upgrade_lock); + m_seed_nodes_initialized = true; + for (const auto& full_addr : get_seed_nodes()) + { + MDEBUG("Seed node: " << full_addr); + append_net_address(m_seed_nodes, full_addr, cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT); + } + MDEBUG("Number of seed nodes: " << m_seed_nodes.size()); + } + if (m_seed_nodes.empty() || m_offline || !m_exclusive_peers.empty()) return true; @@ -1561,16 +1577,19 @@ namespace nodetool break; if(++try_count > m_seed_nodes.size()) { - if (!m_fallback_seed_nodes_added) + if (!m_fallback_seed_nodes_added.test_and_set()) { MWARNING("Failed to connect to any of seed peers, trying fallback seeds"); current_index = m_seed_nodes.size() - 1; - for (const auto &peer: get_seed_nodes(m_nettype)) { - MDEBUG("Fallback seed node: " << peer); - append_net_address(m_seed_nodes, peer, cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT); + boost::upgrade_to_unique_lock<boost::shared_mutex> seed_nodes_lock(seed_nodes_upgrade_lock); + + for (const auto &peer: get_seed_nodes(m_nettype)) + { + MDEBUG("Fallback seed node: " << peer); + append_net_address(m_seed_nodes, peer, cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT); + } } - m_fallback_seed_nodes_added = true; if (current_index == m_seed_nodes.size() - 1) { MWARNING("No fallback seeds, continuing without seeds"); @@ -1604,10 +1623,9 @@ namespace nodetool // Only have seeds in the public zone right now. size_t start_conn_count = get_public_outgoing_connections_count(); - if(!get_public_white_peers_count() && m_seed_nodes.size()) + if(!get_public_white_peers_count() && !connect_to_seed()) { - if (!connect_to_seed()) - return false; + return false; } if (!connect_to_peerlist(m_priority_peers)) return false; diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h index 6a6100e0c..393d38e0a 100644 --- a/src/p2p/net_node_common.h +++ b/src/p2p/net_node_common.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h index 300181bbb..992462d0b 100644 --- a/src/p2p/net_peerlist.h +++ b/src/p2p/net_peerlist.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/p2p/net_peerlist_boost_serialization.h b/src/p2p/net_peerlist_boost_serialization.h index bd5063510..37a85d526 100644 --- a/src/p2p/net_peerlist_boost_serialization.h +++ b/src/p2p/net_peerlist_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h index 609661871..bbc165cfa 100644 --- a/src/p2p/p2p_protocol_defs.h +++ b/src/p2p/p2p_protocol_defs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/p2p/stdafx.h b/src/p2p/stdafx.h index 12a371702..a689b1c15 100644 --- a/src/p2p/stdafx.h +++ b/src/p2p/stdafx.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/platform/mingw/alloca.h b/src/platform/mingw/alloca.h index e4722af2a..b514051aa 100644 --- a/src/platform/mingw/alloca.h +++ b/src/platform/mingw/alloca.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/platform/msc/alloca.h b/src/platform/msc/alloca.h index fcf1731b0..a9ca8cc84 100644 --- a/src/platform/msc/alloca.h +++ b/src/platform/msc/alloca.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/platform/msc/inline_c.h b/src/platform/msc/inline_c.h index 23c11bd06..2a4952cff 100644 --- a/src/platform/msc/inline_c.h +++ b/src/platform/msc/inline_c.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/platform/msc/stdbool.h b/src/platform/msc/stdbool.h index 4d24995bd..445b625b4 100644 --- a/src/platform/msc/stdbool.h +++ b/src/platform/msc/stdbool.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/platform/msc/sys/param.h b/src/platform/msc/sys/param.h index e3cfb7e86..e5d208003 100644 --- a/src/platform/msc/sys/param.h +++ b/src/platform/msc/sys/param.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/ringct/CMakeLists.txt b/src/ringct/CMakeLists.txt index 0192aa931..40b2dfd55 100644 --- a/src/ringct/CMakeLists.txt +++ b/src/ringct/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2016-2019, The Monero Project +# Copyright (c) 2016-2020, The Monero Project # # All rights reserved. # diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index 2ff88c6e7..359dfa879 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // @@ -905,7 +905,7 @@ bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs) rct::key m_y0 = rct::zero(), y1 = rct::zero(); int proof_data_index = 0; rct::keyV w_cache; - rct::keyV proof8_V, proof8_L, proof8_R; + std::vector<ge_p3> proof8_V, proof8_L, proof8_R; for (const Bulletproof *p: proofs) { const Bulletproof &proof = *p; @@ -918,13 +918,17 @@ bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs) const rct::key weight_z = rct::skGen(); // pre-multiply some points by 8 - proof8_V.resize(proof.V.size()); for (size_t i = 0; i < proof.V.size(); ++i) proof8_V[i] = rct::scalarmult8(proof.V[i]); - proof8_L.resize(proof.L.size()); for (size_t i = 0; i < proof.L.size(); ++i) proof8_L[i] = rct::scalarmult8(proof.L[i]); - proof8_R.resize(proof.R.size()); for (size_t i = 0; i < proof.R.size(); ++i) proof8_R[i] = rct::scalarmult8(proof.R[i]); - rct::key proof8_T1 = rct::scalarmult8(proof.T1); - rct::key proof8_T2 = rct::scalarmult8(proof.T2); - rct::key proof8_S = rct::scalarmult8(proof.S); - rct::key proof8_A = rct::scalarmult8(proof.A); + proof8_V.resize(proof.V.size()); for (size_t i = 0; i < proof.V.size(); ++i) rct::scalarmult8(proof8_V[i], proof.V[i]); + proof8_L.resize(proof.L.size()); for (size_t i = 0; i < proof.L.size(); ++i) rct::scalarmult8(proof8_L[i], proof.L[i]); + proof8_R.resize(proof.R.size()); for (size_t i = 0; i < proof.R.size(); ++i) rct::scalarmult8(proof8_R[i], proof.R[i]); + ge_p3 proof8_T1; + ge_p3 proof8_T2; + ge_p3 proof8_S; + ge_p3 proof8_A; + rct::scalarmult8(proof8_T1, proof.T1); + rct::scalarmult8(proof8_T2, proof.T2); + rct::scalarmult8(proof8_S, proof.S); + rct::scalarmult8(proof8_A, proof.A); PERF_TIMER_START_BP(VERIFY_line_61); sc_mulsub(m_y0.bytes, proof.taux.bytes, weight_y.bytes, m_y0.bytes); diff --git a/src/ringct/bulletproofs.h b/src/ringct/bulletproofs.h index 21d494834..d8a9fa4ff 100644 --- a/src/ringct/bulletproofs.h +++ b/src/ringct/bulletproofs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/ringct/rctCryptoOps.c b/src/ringct/rctCryptoOps.c index fbbf6f9bd..506f85c16 100644 --- a/src/ringct/rctCryptoOps.c +++ b/src/ringct/rctCryptoOps.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/ringct/rctCryptoOps.h b/src/ringct/rctCryptoOps.h index 2a25d13a7..dabb2606a 100644 --- a/src/ringct/rctCryptoOps.h +++ b/src/ringct/rctCryptoOps.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp index 6e4d063df..b2dd32ada 100644 --- a/src/ringct/rctOps.cpp +++ b/src/ringct/rctOps.cpp @@ -408,6 +408,18 @@ namespace rct { return res; } + //Computes 8P without byte conversion + void scalarmult8(ge_p3 &res, const key &P) + { + ge_p3 p3; + CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&p3, P.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); + ge_p2 p2; + ge_p3_to_p2(&p2, &p3); + ge_p1p1 p1; + ge_mul8(&p1, &p2); + ge_p1p1_to_p3(&res, &p1); + } + //Computes lA where l is the curve order bool isInMainSubgroup(const key & A) { ge_p3 p3; diff --git a/src/ringct/rctOps.h b/src/ringct/rctOps.h index c24d48e9a..74e0ad833 100644 --- a/src/ringct/rctOps.h +++ b/src/ringct/rctOps.h @@ -124,6 +124,7 @@ namespace rct { key scalarmultH(const key & a); // multiplies a point by 8 key scalarmult8(const key & P); + void scalarmult8(ge_p3 &res, const key & P); // checks a is in the main subgroup (ie, not a small one) bool isInMainSubgroup(const key & a); diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index a7b265d63..2e3e7007e 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -29,6 +29,7 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "misc_log_ex.h" +#include "misc_language.h" #include "common/perf_timer.h" #include "common/threadpool.h" #include "common/util.h" @@ -108,6 +109,7 @@ namespace rct { //Borromean (c.f. gmax/andytoshi's paper) boroSig genBorromean(const key64 x, const key64 P1, const key64 P2, const bits indices) { key64 L[2], alpha; + auto wiper = epee::misc_utils::create_scope_leave_handler([&](){memwipe(alpha, sizeof(alpha));}); key c; int naught = 0, prime = 0, ii = 0, jj=0; boroSig bb; @@ -190,6 +192,7 @@ namespace rct { vector<geDsmp> Ip(dsRows); rv.II = keyV(dsRows); keyV alpha(rows); + auto wiper = epee::misc_utils::create_scope_leave_handler([&](){memwipe(alpha.data(), alpha.size() * sizeof(alpha[0]));}); keyV aG(rows); rv.ss = keyM(cols, aG); keyV aHP(dsRows); @@ -548,7 +551,7 @@ namespace rct { subKeys(M[i][1], pubs[i].mask, Cout); } mgSig result = MLSAG_Gen(message, M, sk, kLRki, mscout, index, rows, hwdev); - memwipe(&sk[0], sizeof(key)); + memwipe(sk.data(), sk.size() * sizeof(key)); return result; } diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index bf4b7b4aa..9b7f26a02 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -48,6 +48,7 @@ extern "C" { #include "hex.h" #include "span.h" +#include "memwipe.h" #include "serialization/vector.h" #include "serialization/debug_archive.h" #include "serialization/binary_archive.h" @@ -106,6 +107,8 @@ namespace rct { key L; key R; key ki; + + ~multisig_kLRki() { memwipe(&k, sizeof(k)); } }; struct multisig_out { diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt index fe5e5a85b..19298c969 100644 --- a/src/rpc/CMakeLists.txt +++ b/src/rpc/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # @@ -45,8 +45,11 @@ set(daemon_messages_sources message.cpp daemon_messages.cpp) +set(rpc_pub_sources zmq_pub.cpp) + set(daemon_rpc_server_sources daemon_handler.cpp + zmq_pub.cpp zmq_server.cpp) @@ -59,8 +62,9 @@ set(rpc_headers rpc_version_str.h rpc_handler.h) -set(daemon_rpc_server_headers) +set(rpc_pub_headers zmq_pub.h) +set(daemon_rpc_server_headers) set(rpc_daemon_private_headers bootstrap_daemon.h @@ -83,6 +87,8 @@ set(daemon_rpc_server_private_headers monero_private_headers(rpc ${rpc_private_headers}) +set(rpc_pub_private_headers) + monero_private_headers(daemon_rpc_server ${daemon_rpc_server_private_headers}) @@ -97,6 +103,11 @@ monero_add_library(rpc ${rpc_headers} ${rpc_private_headers}) +monero_add_library(rpc_pub + ${rpc_pub_sources} + ${rpc_pub_headers} + ${rpc_pub_private_headers}) + monero_add_library(daemon_messages ${daemon_messages_sources} ${daemon_messages_headers} @@ -131,6 +142,14 @@ target_link_libraries(rpc PRIVATE ${EXTRA_LIBRARIES}) +target_link_libraries(rpc_pub + PUBLIC + epee + net + cryptonote_basic + serialization + ${Boost_THREAD_LIBRARY}) + target_link_libraries(daemon_messages LINK_PRIVATE cryptonote_core @@ -142,6 +161,7 @@ target_link_libraries(daemon_messages target_link_libraries(daemon_rpc_server LINK_PRIVATE rpc + rpc_pub cryptonote_core cryptonote_protocol version diff --git a/src/rpc/bootstrap_node_selector.h b/src/rpc/bootstrap_node_selector.h index fc993719b..47722a008 100644 --- a/src/rpc/bootstrap_node_selector.h +++ b/src/rpc/bootstrap_node_selector.h @@ -54,6 +54,8 @@ namespace bootstrap_node struct selector { + virtual ~selector() = default; + virtual void handle_result(const std::string &address, bool success) = 0; virtual boost::optional<node_info> next_node() = 0; }; diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 9b0eeb1f1..bc561abc4 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -68,6 +68,11 @@ using namespace epee; #define DEFAULT_PAYMENT_DIFFICULTY 1000 #define DEFAULT_PAYMENT_CREDITS_PER_HASH 100 +#define RESTRICTED_BLOCK_HEADER_RANGE 1000 +#define RESTRICTED_TRANSACTIONS_COUNT 100 +#define RESTRICTED_SPENT_KEY_IMAGES_COUNT 5000 +#define RESTRICTED_BLOCK_COUNT 1000 + #define RPC_TRACKER(rpc) \ PERF_TIMER(rpc); \ RPCTracker tracker(#rpc, PERF_TIMER_NAME(rpc)) @@ -224,7 +229,7 @@ namespace cryptonote } else if (address == "auto") { - auto get_nodes = [this, credits_per_hash_threshold]() { + auto get_nodes = [this]() { return get_public_nodes(credits_per_hash_threshold); }; m_bootstrap_daemon.reset(new bootstrap_daemon(std::move(get_nodes), rpc_payment_enabled)); @@ -265,25 +270,25 @@ namespace cryptonote { if (!m_restricted && nettype() != FAKECHAIN) { - MERROR("RPC payment enabled, but server is not restricted, anyone can adjust their balance to bypass payment"); + MFATAL("RPC payment enabled, but server is not restricted, anyone can adjust their balance to bypass payment"); return false; } cryptonote::address_parse_info info; if (!get_account_address_from_str(info, nettype(), address)) { - MERROR("Invalid payment address: " << address); + MFATAL("Invalid payment address: " << address); return false; } if (info.is_subaddress) { - MERROR("Payment address may not be a subaddress: " << address); + MFATAL("Payment address may not be a subaddress: " << address); return false; } uint64_t diff = command_line::get_arg(vm, arg_rpc_payment_difficulty); uint64_t credits = command_line::get_arg(vm, arg_rpc_payment_credits); if (diff == 0 || credits == 0) { - MERROR("Payments difficulty and/or payments credits are 0, but a payment address was given"); + MFATAL("Payments difficulty and/or payments credits are 0, but a payment address was given"); return false; } m_rpc_payment_allow_free_loopback = command_line::get_arg(vm, arg_rpc_payment_allow_free_loopback); @@ -303,7 +308,7 @@ namespace cryptonote if (!set_bootstrap_daemon(command_line::get_arg(vm, arg_bootstrap_daemon_address), command_line::get_arg(vm, arg_bootstrap_daemon_login))) { - MERROR("Failed to parse bootstrap daemon address"); + MFATAL("Failed to parse bootstrap daemon address"); return false; } @@ -639,6 +644,13 @@ namespace cryptonote if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCKS_BY_HEIGHT>(invoke_http_mode::BIN, "/getblocks_by_height.bin", req, res, r)) return r; + const bool restricted = m_restricted && ctx; + if (restricted && req.heights.size() > RESTRICTED_BLOCK_COUNT) + { + res.status = "Too many blocks requested in restricted mode"; + return true; + } + res.status = "Failed"; res.blocks.clear(); res.blocks.reserve(req.heights.size()); @@ -793,11 +805,17 @@ namespace cryptonote if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_TRANSACTIONS>(invoke_http_mode::JON, "/gettransactions", req, res, ok)) return ok; - CHECK_PAYMENT_MIN1(req, res, req.txs_hashes.size() * COST_PER_TX, false); - const bool restricted = m_restricted && ctx; const bool request_has_rpc_origin = ctx != NULL; + if (restricted && req.txs_hashes.size() > RESTRICTED_TRANSACTIONS_COUNT) + { + res.status = "Too many transactions requested in restricted mode"; + return true; + } + + CHECK_PAYMENT_MIN1(req, res, req.txs_hashes.size() * COST_PER_TX, false); + std::vector<crypto::hash> vh; for(const auto& tx_hex_str: req.txs_hashes) { @@ -1027,11 +1045,17 @@ namespace cryptonote if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_IS_KEY_IMAGE_SPENT>(invoke_http_mode::JON, "/is_key_image_spent", req, res, ok)) return ok; - CHECK_PAYMENT_MIN1(req, res, req.key_images.size() * COST_PER_KEY_IMAGE, false); - const bool restricted = m_restricted && ctx; const bool request_has_rpc_origin = ctx != NULL; + if (restricted && req.key_images.size() > RESTRICTED_SPENT_KEY_IMAGES_COUNT) + { + res.status = "Too many key images queried in restricted mode"; + return true; + } + + CHECK_PAYMENT_MIN1(req, res, req.key_images.size() * COST_PER_KEY_IMAGE, false); + std::vector<crypto::key_image> key_images; for(const auto& ki_hex_str: req.key_images) { @@ -1990,7 +2014,7 @@ namespace cryptonote r = false; } res.untrusted = true; - return true; + return r; } //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_last_block_header(const COMMAND_RPC_GET_LAST_BLOCK_HEADER::request& req, COMMAND_RPC_GET_LAST_BLOCK_HEADER::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx) @@ -2034,6 +2058,14 @@ namespace cryptonote CHECK_PAYMENT_MIN1(req, res, COST_PER_BLOCK_HEADER, false); + const bool restricted = m_restricted && ctx; + if (restricted && req.hashes.size() > RESTRICTED_BLOCK_COUNT) + { + error_resp.code = CORE_RPC_ERROR_CODE_RESTRICTED; + error_resp.message = "Too many block headers requested in restricted mode"; + return false; + } + auto get = [this](const std::string &hash, bool fill_pow_hash, block_header_response &block_header, bool restricted, epee::json_rpc::error& error_resp) -> bool { crypto::hash block_hash; bool hash_parsed = parse_hash256(hash, block_hash); @@ -2069,7 +2101,6 @@ namespace cryptonote return true; }; - const bool restricted = m_restricted && ctx; if (!req.hash.empty()) { if (!get(req.hash, req.fill_pow_hash, res.block_header, restricted, error_resp)) @@ -2101,6 +2132,14 @@ namespace cryptonote error_resp.message = "Invalid start/end heights."; return false; } + const bool restricted = m_restricted && ctx; + if (restricted && req.end_height - req.start_height > RESTRICTED_BLOCK_HEADER_RANGE) + { + error_resp.code = CORE_RPC_ERROR_CODE_RESTRICTED; + error_resp.message = "Too many block headers requested."; + return false; + } + CHECK_PAYMENT_MIN1(req, res, (req.end_height - req.start_height + 1) * COST_PER_BLOCK_HEADER, false); for (uint64_t h = req.start_height; h <= req.end_height; ++h) { @@ -2127,7 +2166,6 @@ namespace cryptonote return false; } res.headers.push_back(block_header_response()); - const bool restricted = m_restricted && ctx; bool response_filled = fill_block_header_response(blk, false, block_height, block_hash, res.headers.back(), req.fill_pow_hash && !restricted); if (!response_filled) { @@ -2778,7 +2816,7 @@ namespace cryptonote crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data()); cryptonote::blobdata txblob; - if (!m_core.get_pool_transaction(txid, txblob, relay_category::legacy)) + if (m_core.get_pool_transaction(txid, txblob, relay_category::legacy)) { NOTIFY_NEW_TRANSACTIONS::request r; r.txs.push_back(std::move(txblob)); diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 3c404bbd8..dcf6b4e4b 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index a3c187c24..09cd67d7d 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/rpc/core_rpc_server_error_codes.h b/src/rpc/core_rpc_server_error_codes.h index 2fd42f43f..1458049ab 100644 --- a/src/rpc/core_rpc_server_error_codes.h +++ b/src/rpc/core_rpc_server_error_codes.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -48,6 +48,7 @@ #define CORE_RPC_ERROR_CODE_PAYMENT_TOO_LOW -16 #define CORE_RPC_ERROR_CODE_DUPLICATE_PAYMENT -17 #define CORE_RPC_ERROR_CODE_STALE_PAYMENT -18 +#define CORE_RPC_ERROR_CODE_RESTRICTED -19 static inline const char *get_rpc_server_error_message(int64_t code) { @@ -70,6 +71,7 @@ static inline const char *get_rpc_server_error_message(int64_t code) case CORE_RPC_ERROR_CODE_PAYMENT_TOO_LOW: return "Payment too low"; case CORE_RPC_ERROR_CODE_DUPLICATE_PAYMENT: return "Duplicate payment"; case CORE_RPC_ERROR_CODE_STALE_PAYMENT: return "Stale payment"; + case CORE_RPC_ERROR_CODE_RESTRICTED: return "Parameters beyond restricted allowance"; default: MERROR("Unknown error: " << code); return "Unknown error"; } } diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp index d05854e34..ab28319cb 100644 --- a/src/rpc/daemon_handler.cpp +++ b/src/rpc/daemon_handler.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // @@ -33,6 +33,7 @@ #include <stdexcept> #include <boost/uuid/nil_generator.hpp> +#include <boost/utility/string_ref.hpp> // likely included by daemon_handler.h's includes, // but including here for clarity #include "cryptonote_core/cryptonote_core.h" @@ -48,7 +49,7 @@ namespace rpc { namespace { - using handler_function = std::string(DaemonHandler& handler, const rapidjson::Value& id, const rapidjson::Value& msg); + using handler_function = epee::byte_slice(DaemonHandler& handler, const rapidjson::Value& id, const rapidjson::Value& msg); struct handler_map { const char* method_name; @@ -66,7 +67,7 @@ namespace rpc } template<typename Message> - std::string handle_message(DaemonHandler& handler, const rapidjson::Value& id, const rapidjson::Value& parameters) + epee::byte_slice handle_message(DaemonHandler& handler, const rapidjson::Value& id, const rapidjson::Value& parameters) { typename Message::Request request{}; request.fromJson(parameters); @@ -100,7 +101,8 @@ namespace rpc {u8"key_images_spent", handle_message<KeyImagesSpent>}, {u8"mining_status", handle_message<MiningStatus>}, {u8"save_bc", handle_message<SaveBC>}, - {u8"send_raw_tx", handle_message<SendRawTxHex>}, + {u8"send_raw_tx", handle_message<SendRawTx>}, + {u8"send_raw_tx_hex", handle_message<SendRawTxHex>}, {u8"set_log_level", handle_message<SetLogLevel>}, {u8"start_mining", handle_message<StartMining>}, {u8"stop_mining", handle_message<StopMining>} @@ -903,7 +905,7 @@ namespace rpc return true; } - std::string DaemonHandler::handle(const std::string& request) + epee::byte_slice DaemonHandler::handle(const std::string& request) { MDEBUG("Handling RPC request: " << request); @@ -916,8 +918,11 @@ namespace rpc if (matched_handler == std::end(handlers) || matched_handler->method_name != request_type) return BAD_REQUEST(request_type, req_full.getID()); - std::string response = matched_handler->call(*this, req_full.getID(), req_full.getMessage()); - MDEBUG("Returning RPC response: " << response); + epee::byte_slice response = matched_handler->call(*this, req_full.getID(), req_full.getMessage()); + + const boost::string_ref response_view{reinterpret_cast<const char*>(response.data()), response.size()}; + MDEBUG("Returning RPC response: " << response_view); + return response; } catch (const std::exception& e) diff --git a/src/rpc/daemon_handler.h b/src/rpc/daemon_handler.h index 61eac17f0..aa3470c25 100644 --- a/src/rpc/daemon_handler.h +++ b/src/rpc/daemon_handler.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // @@ -28,6 +28,7 @@ #pragma once +#include "byte_slice.h" #include "daemon_messages.h" #include "daemon_rpc_version.h" #include "rpc_handler.h" @@ -132,7 +133,7 @@ class DaemonHandler : public RpcHandler void handle(const GetOutputDistribution::Request& req, GetOutputDistribution::Response& res); - std::string handle(const std::string& request); + epee::byte_slice handle(const std::string& request) override final; private: diff --git a/src/rpc/daemon_messages.cpp b/src/rpc/daemon_messages.cpp index 5c179408e..887486f77 100644 --- a/src/rpc/daemon_messages.cpp +++ b/src/rpc/daemon_messages.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019, The Monero Project +// Copyright (c) 2016-2020, The Monero Project // // All rights reserved. // @@ -34,14 +34,14 @@ namespace cryptonote namespace rpc { -void GetHeight::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetHeight::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const {} void GetHeight::Request::fromJson(const rapidjson::Value& val) { } -void GetHeight::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetHeight::Response::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, height, height); } @@ -52,7 +52,7 @@ void GetHeight::Response::fromJson(const rapidjson::Value& val) } -void GetBlocksFast::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetBlocksFast::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, block_ids, block_ids); INSERT_INTO_JSON_OBJECT(dest, start_height, start_height); @@ -66,7 +66,7 @@ void GetBlocksFast::Request::fromJson(const rapidjson::Value& val) GET_FROM_JSON_OBJECT(val, prune, prune); } -void GetBlocksFast::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetBlocksFast::Response::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, blocks, blocks); INSERT_INTO_JSON_OBJECT(dest, start_height, start_height); @@ -83,7 +83,7 @@ void GetBlocksFast::Response::fromJson(const rapidjson::Value& val) } -void GetHashesFast::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetHashesFast::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, known_hashes, known_hashes); INSERT_INTO_JSON_OBJECT(dest, start_height, start_height); @@ -95,7 +95,7 @@ void GetHashesFast::Request::fromJson(const rapidjson::Value& val) GET_FROM_JSON_OBJECT(val, start_height, start_height); } -void GetHashesFast::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetHashesFast::Response::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, hashes, hashes); INSERT_INTO_JSON_OBJECT(dest, start_height, start_height); @@ -110,7 +110,7 @@ void GetHashesFast::Response::fromJson(const rapidjson::Value& val) } -void GetTransactions::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetTransactions::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, tx_hashes, tx_hashes); } @@ -120,7 +120,7 @@ void GetTransactions::Request::fromJson(const rapidjson::Value& val) GET_FROM_JSON_OBJECT(val, tx_hashes, tx_hashes); } -void GetTransactions::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetTransactions::Response::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, txs, txs); INSERT_INTO_JSON_OBJECT(dest, missed_hashes, missed_hashes); @@ -133,7 +133,7 @@ void GetTransactions::Response::fromJson(const rapidjson::Value& val) } -void KeyImagesSpent::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void KeyImagesSpent::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, key_images, key_images); } @@ -143,7 +143,7 @@ void KeyImagesSpent::Request::fromJson(const rapidjson::Value& val) GET_FROM_JSON_OBJECT(val, key_images, key_images); } -void KeyImagesSpent::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void KeyImagesSpent::Response::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, spent_status, spent_status); } @@ -154,7 +154,7 @@ void KeyImagesSpent::Response::fromJson(const rapidjson::Value& val) } -void GetTxGlobalOutputIndices::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetTxGlobalOutputIndices::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, tx_hash, tx_hash); } @@ -164,7 +164,7 @@ void GetTxGlobalOutputIndices::Request::fromJson(const rapidjson::Value& val) GET_FROM_JSON_OBJECT(val, tx_hash, tx_hash); } -void GetTxGlobalOutputIndices::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetTxGlobalOutputIndices::Response::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, output_indices, output_indices); } @@ -174,7 +174,7 @@ void GetTxGlobalOutputIndices::Response::fromJson(const rapidjson::Value& val) GET_FROM_JSON_OBJECT(val, output_indices, output_indices); } -void SendRawTx::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void SendRawTx::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, tx, tx); INSERT_INTO_JSON_OBJECT(dest, relay, relay); @@ -186,7 +186,7 @@ void SendRawTx::Request::fromJson(const rapidjson::Value& val) GET_FROM_JSON_OBJECT(val, relay, relay); } -void SendRawTx::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void SendRawTx::Response::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, relayed, relayed); } @@ -197,7 +197,7 @@ void SendRawTx::Response::fromJson(const rapidjson::Value& val) GET_FROM_JSON_OBJECT(val, relayed, relayed); } -void SendRawTxHex::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void SendRawTxHex::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, tx_as_hex, tx_as_hex); INSERT_INTO_JSON_OBJECT(dest, relay, relay); @@ -209,7 +209,7 @@ void SendRawTxHex::Request::fromJson(const rapidjson::Value& val) GET_FROM_JSON_OBJECT(val, relay, relay); } -void StartMining::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void StartMining::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, miner_address, miner_address); INSERT_INTO_JSON_OBJECT(dest, threads_count, threads_count); @@ -225,7 +225,7 @@ void StartMining::Request::fromJson(const rapidjson::Value& val) GET_FROM_JSON_OBJECT(val, ignore_battery, ignore_battery); } -void StartMining::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void StartMining::Response::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const {} void StartMining::Response::fromJson(const rapidjson::Value& val) @@ -233,14 +233,14 @@ void StartMining::Response::fromJson(const rapidjson::Value& val) } -void StopMining::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void StopMining::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const {} void StopMining::Request::fromJson(const rapidjson::Value& val) { } -void StopMining::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void StopMining::Response::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const {} void StopMining::Response::fromJson(const rapidjson::Value& val) @@ -248,14 +248,14 @@ void StopMining::Response::fromJson(const rapidjson::Value& val) } -void MiningStatus::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void MiningStatus::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const {} void MiningStatus::Request::fromJson(const rapidjson::Value& val) { } -void MiningStatus::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void MiningStatus::Response::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, active, active); INSERT_INTO_JSON_OBJECT(dest, speed, speed); @@ -274,14 +274,14 @@ void MiningStatus::Response::fromJson(const rapidjson::Value& val) } -void GetInfo::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetInfo::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const {} void GetInfo::Request::fromJson(const rapidjson::Value& val) { } -void GetInfo::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetInfo::Response::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, info, info); } @@ -292,14 +292,14 @@ void GetInfo::Response::fromJson(const rapidjson::Value& val) } -void SaveBC::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void SaveBC::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const {} void SaveBC::Request::fromJson(const rapidjson::Value& val) { } -void SaveBC::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void SaveBC::Response::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const {} void SaveBC::Response::fromJson(const rapidjson::Value& val) @@ -307,7 +307,7 @@ void SaveBC::Response::fromJson(const rapidjson::Value& val) } -void GetBlockHash::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetBlockHash::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, height, height); } @@ -317,7 +317,7 @@ void GetBlockHash::Request::fromJson(const rapidjson::Value& val) GET_FROM_JSON_OBJECT(val, height, height); } -void GetBlockHash::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetBlockHash::Response::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, hash, hash); } @@ -328,14 +328,14 @@ void GetBlockHash::Response::fromJson(const rapidjson::Value& val) } -void GetLastBlockHeader::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetLastBlockHeader::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const {} void GetLastBlockHeader::Request::fromJson(const rapidjson::Value& val) { } -void GetLastBlockHeader::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetLastBlockHeader::Response::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, header, header); } @@ -346,7 +346,7 @@ void GetLastBlockHeader::Response::fromJson(const rapidjson::Value& val) } -void GetBlockHeaderByHash::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetBlockHeaderByHash::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, hash, hash); } @@ -356,7 +356,7 @@ void GetBlockHeaderByHash::Request::fromJson(const rapidjson::Value& val) GET_FROM_JSON_OBJECT(val, hash, hash); } -void GetBlockHeaderByHash::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetBlockHeaderByHash::Response::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, header, header); } @@ -367,7 +367,7 @@ void GetBlockHeaderByHash::Response::fromJson(const rapidjson::Value& val) } -void GetBlockHeaderByHeight::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetBlockHeaderByHeight::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, height, height); } @@ -377,7 +377,7 @@ void GetBlockHeaderByHeight::Request::fromJson(const rapidjson::Value& val) GET_FROM_JSON_OBJECT(val, height, height); } -void GetBlockHeaderByHeight::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetBlockHeaderByHeight::Response::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, header, header); } @@ -388,7 +388,7 @@ void GetBlockHeaderByHeight::Response::fromJson(const rapidjson::Value& val) } -void GetBlockHeadersByHeight::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetBlockHeadersByHeight::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, heights, heights); } @@ -398,7 +398,7 @@ void GetBlockHeadersByHeight::Request::fromJson(const rapidjson::Value& val) GET_FROM_JSON_OBJECT(val, heights, heights); } -void GetBlockHeadersByHeight::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetBlockHeadersByHeight::Response::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, headers, headers); } @@ -409,14 +409,14 @@ void GetBlockHeadersByHeight::Response::fromJson(const rapidjson::Value& val) } -void GetPeerList::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetPeerList::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const {} void GetPeerList::Request::fromJson(const rapidjson::Value& val) { } -void GetPeerList::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetPeerList::Response::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, white_list, white_list); INSERT_INTO_JSON_OBJECT(dest, gray_list, gray_list); @@ -429,7 +429,7 @@ void GetPeerList::Response::fromJson(const rapidjson::Value& val) } -void SetLogLevel::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void SetLogLevel::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, level, level); } @@ -439,7 +439,7 @@ void SetLogLevel::Request::fromJson(const rapidjson::Value& val) GET_FROM_JSON_OBJECT(val, level, level); } -void SetLogLevel::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void SetLogLevel::Response::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const {} void SetLogLevel::Response::fromJson(const rapidjson::Value& val) @@ -447,14 +447,14 @@ void SetLogLevel::Response::fromJson(const rapidjson::Value& val) } -void GetTransactionPool::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetTransactionPool::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const {} void GetTransactionPool::Request::fromJson(const rapidjson::Value& val) { } -void GetTransactionPool::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetTransactionPool::Response::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, transactions, transactions); INSERT_INTO_JSON_OBJECT(dest, key_images, key_images); @@ -467,7 +467,7 @@ void GetTransactionPool::Response::fromJson(const rapidjson::Value& val) } -void HardForkInfo::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void HardForkInfo::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, version, version); } @@ -477,7 +477,7 @@ void HardForkInfo::Request::fromJson(const rapidjson::Value& val) GET_FROM_JSON_OBJECT(val, version, version); } -void HardForkInfo::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void HardForkInfo::Response::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, info, info); } @@ -488,7 +488,7 @@ void HardForkInfo::Response::fromJson(const rapidjson::Value& val) } -void GetOutputHistogram::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetOutputHistogram::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, amounts, amounts); INSERT_INTO_JSON_OBJECT(dest, min_count, min_count); @@ -506,7 +506,7 @@ void GetOutputHistogram::Request::fromJson(const rapidjson::Value& val) GET_FROM_JSON_OBJECT(val, recent_cutoff, recent_cutoff); } -void GetOutputHistogram::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetOutputHistogram::Response::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, histogram, histogram); } @@ -517,7 +517,7 @@ void GetOutputHistogram::Response::fromJson(const rapidjson::Value& val) } -void GetOutputKeys::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetOutputKeys::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, outputs, outputs); } @@ -527,7 +527,7 @@ void GetOutputKeys::Request::fromJson(const rapidjson::Value& val) GET_FROM_JSON_OBJECT(val, outputs, outputs); } -void GetOutputKeys::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetOutputKeys::Response::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, keys, keys); } @@ -538,14 +538,14 @@ void GetOutputKeys::Response::fromJson(const rapidjson::Value& val) } -void GetRPCVersion::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetRPCVersion::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const {} void GetRPCVersion::Request::fromJson(const rapidjson::Value& val) { } -void GetRPCVersion::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetRPCVersion::Response::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, version, version); } @@ -555,7 +555,7 @@ void GetRPCVersion::Response::fromJson(const rapidjson::Value& val) GET_FROM_JSON_OBJECT(val, version, version); } -void GetFeeEstimate::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetFeeEstimate::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, num_grace_blocks, num_grace_blocks); } @@ -565,7 +565,7 @@ void GetFeeEstimate::Request::fromJson(const rapidjson::Value& val) GET_FROM_JSON_OBJECT(val, num_grace_blocks, num_grace_blocks); } -void GetFeeEstimate::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetFeeEstimate::Response::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, estimated_base_fee, estimated_base_fee); INSERT_INTO_JSON_OBJECT(dest, fee_mask, fee_mask); @@ -581,7 +581,7 @@ void GetFeeEstimate::Response::fromJson(const rapidjson::Value& val) GET_FROM_JSON_OBJECT(val, hard_fork_version, hard_fork_version); } -void GetOutputDistribution::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetOutputDistribution::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, amounts, amounts); INSERT_INTO_JSON_OBJECT(dest, from_height, from_height); @@ -597,7 +597,7 @@ void GetOutputDistribution::Request::fromJson(const rapidjson::Value& val) GET_FROM_JSON_OBJECT(val, cumulative, cumulative); } -void GetOutputDistribution::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void GetOutputDistribution::Response::doToJson(rapidjson::Writer<epee::byte_stream>& dest) const { INSERT_INTO_JSON_OBJECT(dest, status, status); INSERT_INTO_JSON_OBJECT(dest, distributions, distributions); diff --git a/src/rpc/daemon_messages.h b/src/rpc/daemon_messages.h index bb5059cdc..dea37c9a6 100644 --- a/src/rpc/daemon_messages.h +++ b/src/rpc/daemon_messages.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019, The Monero Project +// Copyright (c) 2016-2020, The Monero Project // // All rights reserved. // @@ -28,11 +28,11 @@ #pragma once -#include <rapidjson/stringbuffer.h> #include <rapidjson/writer.h> #include <unordered_map> #include <vector> +#include "byte_stream.h" #include "message.h" #include "cryptonote_protocol/cryptonote_protocol_defs.h" #include "rpc/message_data_structs.h" @@ -50,7 +50,7 @@ class classname \ public: \ Request() { } \ ~Request() { } \ - void doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const override final; \ + void doToJson(rapidjson::Writer<epee::byte_stream>& dest) const override final; \ void fromJson(const rapidjson::Value& val) override final; #define BEGIN_RPC_MESSAGE_RESPONSE \ @@ -59,7 +59,7 @@ class classname \ public: \ Response() { } \ ~Response() { } \ - void doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const override final; \ + void doToJson(rapidjson::Writer<epee::byte_stream>& dest) const override final; \ void fromJson(const rapidjson::Value& val) override final; #define END_RPC_MESSAGE_REQUEST }; diff --git a/src/rpc/daemon_rpc_version.h b/src/rpc/daemon_rpc_version.h index b3eb9699b..d178a82bc 100644 --- a/src/rpc/daemon_rpc_version.h +++ b/src/rpc/daemon_rpc_version.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019, The Monero Project +// Copyright (c) 2016-2020, The Monero Project // // All rights reserved. // diff --git a/src/rpc/fwd.h b/src/rpc/fwd.h new file mode 100644 index 000000000..72537f5a5 --- /dev/null +++ b/src/rpc/fwd.h @@ -0,0 +1,37 @@ +// Copyright (c) 2019-2020, 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 + +namespace cryptonote +{ + namespace listener + { + class zmq_pub; + } +} diff --git a/src/rpc/instanciations.cpp b/src/rpc/instanciations.cpp index 94fdb5f18..ac992d4ad 100644 --- a/src/rpc/instanciations.cpp +++ b/src/rpc/instanciations.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/rpc/message.cpp b/src/rpc/message.cpp index 5b6a1c05b..e4f17cef8 100644 --- a/src/rpc/message.cpp +++ b/src/rpc/message.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019, The Monero Project +// Copyright (c) 2016-2020, The Monero Project // // All rights reserved. // @@ -62,7 +62,7 @@ const rapidjson::Value& get_method_field(const rapidjson::Value& src) } } -void Message::toJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +void Message::toJson(rapidjson::Writer<epee::byte_stream>& dest) const { dest.StartObject(); INSERT_INTO_JSON_OBJECT(dest, status, status); @@ -149,11 +149,11 @@ cryptonote::rpc::error FullMessage::getError() return err; } -std::string FullMessage::getRequest(const std::string& request, const Message& message, const unsigned id) +epee::byte_slice FullMessage::getRequest(const std::string& request, const Message& message, const unsigned id) { - rapidjson::StringBuffer buffer; + epee::byte_stream buffer; { - rapidjson::Writer<rapidjson::StringBuffer> dest{buffer}; + rapidjson::Writer<epee::byte_stream> dest{buffer}; dest.StartObject(); INSERT_INTO_JSON_OBJECT(dest, jsonrpc, (boost::string_ref{"2.0", 3})); @@ -172,15 +172,15 @@ std::string FullMessage::getRequest(const std::string& request, const Message& m if (!dest.IsComplete()) throw std::logic_error{"Invalid JSON tree generated"}; } - return std::string{buffer.GetString(), buffer.GetSize()}; + return epee::byte_slice{std::move(buffer)}; } -std::string FullMessage::getResponse(const Message& message, const rapidjson::Value& id) +epee::byte_slice FullMessage::getResponse(const Message& message, const rapidjson::Value& id) { - rapidjson::StringBuffer buffer; + epee::byte_stream buffer; { - rapidjson::Writer<rapidjson::StringBuffer> dest{buffer}; + rapidjson::Writer<epee::byte_stream> dest{buffer}; dest.StartObject(); INSERT_INTO_JSON_OBJECT(dest, jsonrpc, (boost::string_ref{"2.0", 3})); @@ -207,17 +207,17 @@ std::string FullMessage::getResponse(const Message& message, const rapidjson::Va if (!dest.IsComplete()) throw std::logic_error{"Invalid JSON tree generated"}; } - return std::string{buffer.GetString(), buffer.GetSize()}; + return epee::byte_slice{std::move(buffer)}; } // convenience functions for bad input -std::string BAD_REQUEST(const std::string& request) +epee::byte_slice BAD_REQUEST(const std::string& request) { rapidjson::Value invalid; return BAD_REQUEST(request, invalid); } -std::string BAD_REQUEST(const std::string& request, const rapidjson::Value& id) +epee::byte_slice BAD_REQUEST(const std::string& request, const rapidjson::Value& id) { Message fail; fail.status = Message::STATUS_BAD_REQUEST; @@ -225,7 +225,7 @@ std::string BAD_REQUEST(const std::string& request, const rapidjson::Value& id) return FullMessage::getResponse(fail, id); } -std::string BAD_JSON(const std::string& error_details) +epee::byte_slice BAD_JSON(const std::string& error_details) { rapidjson::Value invalid; Message fail; diff --git a/src/rpc/message.h b/src/rpc/message.h index 4cbc84fe4..b858a5913 100644 --- a/src/rpc/message.h +++ b/src/rpc/message.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019, The Monero Project +// Copyright (c) 2016-2020, The Monero Project // // All rights reserved. // @@ -29,10 +29,11 @@ #pragma once #include <rapidjson/document.h> -#include <rapidjson/stringbuffer.h> #include <rapidjson/writer.h> #include <string> +#include "byte_slice.h" +#include "byte_stream.h" #include "rpc/message_data_structs.h" namespace cryptonote @@ -43,7 +44,7 @@ namespace rpc class Message { - virtual void doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const + virtual void doToJson(rapidjson::Writer<epee::byte_stream>& dest) const {} public: @@ -57,7 +58,7 @@ namespace rpc virtual ~Message() { } - void toJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const; + void toJson(rapidjson::Writer<epee::byte_stream>& dest) const; virtual void fromJson(const rapidjson::Value& val); @@ -85,8 +86,8 @@ namespace rpc cryptonote::rpc::error getError(); - static std::string getRequest(const std::string& request, const Message& message, unsigned id); - static std::string getResponse(const Message& message, const rapidjson::Value& id); + static epee::byte_slice getRequest(const std::string& request, const Message& message, unsigned id); + static epee::byte_slice getResponse(const Message& message, const rapidjson::Value& id); private: FullMessage() = default; @@ -99,10 +100,10 @@ namespace rpc // convenience functions for bad input - std::string BAD_REQUEST(const std::string& request); - std::string BAD_REQUEST(const std::string& request, const rapidjson::Value& id); + epee::byte_slice BAD_REQUEST(const std::string& request); + epee::byte_slice BAD_REQUEST(const std::string& request, const rapidjson::Value& id); - std::string BAD_JSON(const std::string& error_details); + epee::byte_slice BAD_JSON(const std::string& error_details); } // namespace rpc diff --git a/src/rpc/message_data_structs.h b/src/rpc/message_data_structs.h index e64f5f163..085148d8a 100644 --- a/src/rpc/message_data_structs.h +++ b/src/rpc/message_data_structs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019, The Monero Project +// Copyright (c) 2016-2020, The Monero Project // // All rights reserved. // diff --git a/src/rpc/rpc_args.cpp b/src/rpc/rpc_args.cpp index 9153e76ea..8601bd0b4 100644 --- a/src/rpc/rpc_args.cpp +++ b/src/rpc/rpc_args.cpp @@ -30,7 +30,7 @@ #include <boost/algorithm/string.hpp> #include <boost/asio/ip/address.hpp> -#include <boost/bind.hpp> +#include <functional> #include "common/command_line.h" #include "common/i18n.h" #include "hex.h" @@ -221,7 +221,7 @@ namespace cryptonote std::vector<std::string> access_control_origins; boost::split(access_control_origins, access_control_origins_input, boost::is_any_of(",")); - std::for_each(access_control_origins.begin(), access_control_origins.end(), boost::bind(&boost::trim<std::string>, _1, std::locale::classic())); + std::for_each(access_control_origins.begin(), access_control_origins.end(), std::bind(&boost::trim<std::string>, std::placeholders::_1, std::locale::classic())); config.access_control_origins = std::move(access_control_origins); } diff --git a/src/rpc/rpc_args.h b/src/rpc/rpc_args.h index ac6eb2744..4cf4279b2 100644 --- a/src/rpc/rpc_args.h +++ b/src/rpc/rpc_args.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/rpc/rpc_handler.h b/src/rpc/rpc_handler.h index b81983d28..97dd0598b 100644 --- a/src/rpc/rpc_handler.h +++ b/src/rpc/rpc_handler.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019, The Monero Project +// Copyright (c) 2016-2020, The Monero Project // // All rights reserved. // @@ -32,6 +32,7 @@ #include <cstdint> #include <string> #include <vector> +#include "byte_slice.h" #include "crypto/hash.h" namespace cryptonote @@ -54,7 +55,7 @@ class RpcHandler RpcHandler() { } virtual ~RpcHandler() { } - virtual std::string handle(const std::string& request) = 0; + virtual epee::byte_slice handle(const std::string& request) = 0; static boost::optional<output_distribution_data> get_output_distribution(const std::function<bool(uint64_t, uint64_t, uint64_t, uint64_t&, std::vector<uint64_t>&, uint64_t&)> &f, uint64_t amount, uint64_t from_height, uint64_t to_height, const std::function<crypto::hash(uint64_t)> &get_hash, bool cumulative, uint64_t blockchain_height); diff --git a/src/rpc/rpc_payment.cpp b/src/rpc/rpc_payment.cpp index 2b9c19f57..edc8f0dda 100644 --- a/src/rpc/rpc_payment.cpp +++ b/src/rpc/rpc_payment.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2019, The Monero Project +// Copyright (c) 2018-2020, The Monero Project // // All rights reserved. // @@ -92,6 +92,7 @@ namespace cryptonote uint64_t rpc_payment::balance(const crypto::public_key &client, int64_t delta) { + boost::lock_guard<boost::mutex> lock(mutex); client_info &info = m_client_info[client]; // creates if not found uint64_t credits = info.credits; if (delta > 0 && credits > std::numeric_limits<uint64_t>::max() - delta) @@ -107,6 +108,7 @@ namespace cryptonote bool rpc_payment::pay(const crypto::public_key &client, uint64_t ts, uint64_t payment, const std::string &rpc, bool same_ts, uint64_t &credits) { + boost::lock_guard<boost::mutex> lock(mutex); client_info &info = m_client_info[client]; // creates if not found if (ts < info.last_request_timestamp || (ts == info.last_request_timestamp && !same_ts)) { @@ -130,6 +132,7 @@ namespace cryptonote bool rpc_payment::get_info(const crypto::public_key &client, const std::function<bool(const cryptonote::blobdata&, cryptonote::block&, uint64_t &seed_height, crypto::hash &seed_hash)> &get_block_template, cryptonote::blobdata &hashing_blob, uint64_t &seed_height, crypto::hash &seed_hash, const crypto::hash &top, uint64_t &diff, uint64_t &credits_per_hash_found, uint64_t &credits, uint32_t &cookie) { + boost::lock_guard<boost::mutex> lock(mutex); client_info &info = m_client_info[client]; // creates if not found const uint64_t now = time(NULL); bool need_template = top != info.top || now >= info.block_template_update_time + STALE_THRESHOLD; @@ -180,6 +183,7 @@ namespace cryptonote bool rpc_payment::submit_nonce(const crypto::public_key &client, uint32_t nonce, const crypto::hash &top, int64_t &error_code, std::string &error_message, uint64_t &credits, crypto::hash &hash, cryptonote::block &block, uint32_t cookie, bool &stale) { + boost::lock_guard<boost::mutex> lock(mutex); client_info &info = m_client_info[client]; // creates if not found if (cookie != info.cookie && cookie != info.cookie - 1) { @@ -272,6 +276,7 @@ namespace cryptonote bool rpc_payment::foreach(const std::function<bool(const crypto::public_key &client, const client_info &info)> &f) const { + boost::lock_guard<boost::mutex> lock(mutex); for (std::unordered_map<crypto::public_key, client_info>::const_iterator i = m_client_info.begin(); i != m_client_info.end(); ++i) { if (!f(i->first, i->second)) @@ -283,8 +288,9 @@ namespace cryptonote bool rpc_payment::load(std::string directory) { TRY_ENTRY(); + boost::lock_guard<boost::mutex> lock(mutex); m_directory = std::move(directory); - std::string state_file_path = directory + "/" + RPC_PAYMENTS_DATA_FILENAME; + std::string state_file_path = m_directory + "/" + RPC_PAYMENTS_DATA_FILENAME; MINFO("loading rpc payments data from " << state_file_path); std::ifstream data; data.open(state_file_path, std::ios_base::binary | std::ios_base::in); @@ -313,6 +319,7 @@ namespace cryptonote bool rpc_payment::store(const std::string &directory_) const { TRY_ENTRY(); + boost::lock_guard<boost::mutex> lock(mutex); const std::string &directory = directory_.empty() ? m_directory : directory_; MDEBUG("storing rpc payments data to " << directory); if (!tools::create_directories_if_necessary(directory)) @@ -345,6 +352,7 @@ namespace cryptonote unsigned int rpc_payment::flush_by_age(time_t seconds) { + boost::lock_guard<boost::mutex> lock(mutex); unsigned int count = 0; const time_t now = time(NULL); time_t seconds0 = seconds; @@ -358,7 +366,7 @@ namespace cryptonote for (std::unordered_map<crypto::public_key, client_info>::iterator i = m_client_info.begin(); i != m_client_info.end(); ) { std::unordered_map<crypto::public_key, client_info>::iterator j = i++; - const time_t t = std::max(j->second.last_request_timestamp, j->second.update_time); + const time_t t = std::max(j->second.last_request_timestamp / 1000000, j->second.update_time); const bool erase = t < ((j->second.credits == 0) ? threshold0 : threshold); if (erase) { @@ -372,6 +380,7 @@ namespace cryptonote uint64_t rpc_payment::get_hashes(unsigned int seconds) const { + boost::lock_guard<boost::mutex> lock(mutex); const uint64_t now = time(NULL); uint64_t hashes = 0; for (std::map<uint64_t, uint64_t>::const_reverse_iterator i = m_hashrate.crbegin(); i != m_hashrate.crend(); ++i) @@ -385,6 +394,7 @@ namespace cryptonote void rpc_payment::prune_hashrate(unsigned int seconds) { + boost::lock_guard<boost::mutex> lock(mutex); const uint64_t now = time(NULL); std::map<uint64_t, uint64_t>::iterator i; for (i = m_hashrate.begin(); i != m_hashrate.end(); ++i) diff --git a/src/rpc/rpc_payment.h b/src/rpc/rpc_payment.h index f6832fd34..dcd43f8d5 100644 --- a/src/rpc/rpc_payment.h +++ b/src/rpc/rpc_payment.h @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2019, The Monero Project +// Copyright (c) 2018-2020, The Monero Project // // All rights reserved. // @@ -31,6 +31,7 @@ #include <string> #include <unordered_set> #include <unordered_map> +#include <boost/thread/mutex.hpp> #include <boost/serialization/version.hpp> #include "cryptonote_basic/blobdatatype.h" #include "cryptonote_basic/cryptonote_basic.h" @@ -139,6 +140,7 @@ namespace cryptonote uint64_t m_nonces_stale; uint64_t m_nonces_bad; uint64_t m_nonces_dupe; + mutable boost::mutex mutex; }; } diff --git a/src/rpc/rpc_payment_costs.h b/src/rpc/rpc_payment_costs.h index 3b27bf286..9bc0f8a2b 100644 --- a/src/rpc/rpc_payment_costs.h +++ b/src/rpc/rpc_payment_costs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/rpc/rpc_payment_signature.cpp b/src/rpc/rpc_payment_signature.cpp index 559f3a1e9..a5dc21717 100644 --- a/src/rpc/rpc_payment_signature.cpp +++ b/src/rpc/rpc_payment_signature.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2019, The Monero Project +// Copyright (c) 2018-2020, The Monero Project // // All rights reserved. // diff --git a/src/rpc/rpc_payment_signature.h b/src/rpc/rpc_payment_signature.h index 4a2fe2ea3..bf7ee061f 100644 --- a/src/rpc/rpc_payment_signature.h +++ b/src/rpc/rpc_payment_signature.h @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2019, The Monero Project +// Copyright (c) 2018-2020, The Monero Project // // All rights reserved. // diff --git a/src/rpc/rpc_version_str.cpp b/src/rpc/rpc_version_str.cpp index c60cf4891..b8b231c16 100644 --- a/src/rpc/rpc_version_str.cpp +++ b/src/rpc/rpc_version_str.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/rpc/rpc_version_str.h b/src/rpc/rpc_version_str.h index 930c807d2..b0cb47c5f 100644 --- a/src/rpc/rpc_version_str.h +++ b/src/rpc/rpc_version_str.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/rpc/zmq_pub.cpp b/src/rpc/zmq_pub.cpp new file mode 100644 index 000000000..0dffffac6 --- /dev/null +++ b/src/rpc/zmq_pub.cpp @@ -0,0 +1,478 @@ +// Copyright (c) 2020, 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 "zmq_pub.h" + +#include <algorithm> +#include <boost/range/adaptor/filtered.hpp> +#include <boost/range/adaptor/transformed.hpp> +#include <boost/thread/locks.hpp> +#include <cassert> +#include <cstdint> +#include <cstring> +#include <rapidjson/document.h> +#include <rapidjson/stringbuffer.h> +#include <rapidjson/writer.h> +#include <stdexcept> +#include <string> +#include <utility> + +#include "common/expect.h" +#include "crypto/crypto.h" +#include "cryptonote_basic/cryptonote_format_utils.h" +#include "cryptonote_basic/events.h" +#include "misc_log_ex.h" +#include "serialization/json_object.h" + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "net.zmq" + +namespace +{ + constexpr const char txpool_signal[] = "tx_signal"; + + using chain_writer = void(epee::byte_stream&, std::uint64_t, epee::span<const cryptonote::block>); + using txpool_writer = void(epee::byte_stream&, epee::span<const cryptonote::txpool_event>); + + template<typename F> + struct context + { + char const* const name; + F* generate_pub; + }; + + template<typename T> + bool operator<(const context<T>& lhs, const context<T>& rhs) noexcept + { + return std::strcmp(lhs.name, rhs.name) < 0; + } + + template<typename T> + bool operator<(const context<T>& lhs, const boost::string_ref rhs) noexcept + { + return lhs.name < rhs; + } + + struct is_valid + { + bool operator()(const cryptonote::txpool_event& event) const noexcept + { + return event.res; + } + }; + + template<typename T, std::size_t N> + void verify_sorted(const std::array<context<T>, N>& elems, const char* name) + { + auto unsorted = std::is_sorted_until(elems.begin(), elems.end()); + if (unsorted != elems.end()) + throw std::logic_error{name + std::string{" array is not properly sorted, see: "} + unsorted->name}; + } + + void write_header(epee::byte_stream& buf, const boost::string_ref name) + { + buf.write(name.data(), name.size()); + buf.put(':'); + } + + //! \return `name:...` where `...` is JSON and `name` is directly copied (no quotes - not JSON). + template<typename T> + void json_pub(epee::byte_stream& buf, const T value) + { + rapidjson::Writer<epee::byte_stream> dest{buf}; + using cryptonote::json::toJsonValue; + toJsonValue(dest, value); + } + + //! Object for "minimal" block serialization + struct minimal_chain + { + const std::uint64_t height; + const epee::span<const cryptonote::block> blocks; + }; + + //! Object for "minimal" tx serialization + struct minimal_txpool + { + const cryptonote::transaction& tx; + }; + + void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const minimal_chain self) + { + namespace adapt = boost::adaptors; + + const auto to_block_id = [](const cryptonote::block& bl) + { + crypto::hash id; + if (!get_block_hash(bl, id)) + MERROR("ZMQ/Pub failure: get_block_hash"); + return id; + }; + + assert(!self.blocks.empty()); // checked in zmq_pub::send_chain_main + + dest.StartObject(); + INSERT_INTO_JSON_OBJECT(dest, first_height, self.height); + INSERT_INTO_JSON_OBJECT(dest, first_prev_id, self.blocks[0].prev_id); + INSERT_INTO_JSON_OBJECT(dest, ids, (self.blocks | adapt::transformed(to_block_id))); + dest.EndObject(); + } + + void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const minimal_txpool self) + { + crypto::hash id{}; + std::size_t blob_size = 0; + if (!get_transaction_hash(self.tx, id, blob_size)) + { + MERROR("ZMQ/Pub failure: get_transaction_hash"); + return; + } + + dest.StartObject(); + INSERT_INTO_JSON_OBJECT(dest, id, id); + INSERT_INTO_JSON_OBJECT(dest, blob_size, blob_size); + dest.EndObject(); + } + + void json_full_chain(epee::byte_stream& buf, const std::uint64_t height, const epee::span<const cryptonote::block> blocks) + { + json_pub(buf, blocks); + } + + void json_minimal_chain(epee::byte_stream& buf, const std::uint64_t height, const epee::span<const cryptonote::block> blocks) + { + json_pub(buf, minimal_chain{height, blocks}); + } + + // boost::adaptors are in place "views" - no copy/move takes place + // moving transactions (via sort, etc.), is expensive! + + void json_full_txpool(epee::byte_stream& buf, epee::span<const cryptonote::txpool_event> txes) + { + namespace adapt = boost::adaptors; + const auto to_full_tx = [](const cryptonote::txpool_event& event) + { + return event.tx; + }; + json_pub(buf, (txes | adapt::filtered(is_valid{}) | adapt::transformed(to_full_tx))); + } + + void json_minimal_txpool(epee::byte_stream& buf, epee::span<const cryptonote::txpool_event> txes) + { + namespace adapt = boost::adaptors; + const auto to_minimal_tx = [](const cryptonote::txpool_event& event) + { + return minimal_txpool{event.tx}; + }; + json_pub(buf, (txes | adapt::filtered(is_valid{}) | adapt::transformed(to_minimal_tx))); + } + + constexpr const std::array<context<chain_writer>, 2> chain_contexts = + {{ + {u8"json-full-chain_main", json_full_chain}, + {u8"json-minimal-chain_main", json_minimal_chain} + }}; + + constexpr const std::array<context<txpool_writer>, 2> txpool_contexts = + {{ + {u8"json-full-txpool_add", json_full_txpool}, + {u8"json-minimal-txpool_add", json_minimal_txpool} + }}; + + template<typename T, std::size_t N> + epee::span<const context<T>> get_range(const std::array<context<T>, N>& contexts, const boost::string_ref value) + { + const auto not_prefix = [](const boost::string_ref lhs, const context<T>& rhs) + { + return !(boost::string_ref{rhs.name}.starts_with(lhs)); + }; + + const auto lower = std::lower_bound(contexts.begin(), contexts.end(), value); + const auto upper = std::upper_bound(lower, contexts.end(), value, not_prefix); + return {lower, std::size_t(upper - lower)}; + } + + template<std::size_t N, typename T> + void add_subscriptions(std::array<std::size_t, N>& subs, const epee::span<const context<T>> range, context<T> const* const first) + { + assert(range.size() <= N); + assert(range.begin() - first <= N - range.size()); + + for (const auto& ctx : range) + { + const std::size_t i = std::addressof(ctx) - first; + subs[i] = std::min(std::numeric_limits<std::size_t>::max() - 1, subs[i]) + 1; + } + } + + template<std::size_t N, typename T> + void remove_subscriptions(std::array<std::size_t, N>& subs, const epee::span<const context<T>> range, context<T> const* const first) + { + assert(range.size() <= N); + assert(range.begin() - first <= N - range.size()); + + for (const auto& ctx : range) + { + const std::size_t i = std::addressof(ctx) - first; + subs[i] = std::max(std::size_t(1), subs[i]) - 1; + } + } + + template<std::size_t N, typename T, typename... U> + std::array<epee::byte_slice, N> make_pubs(const std::array<std::size_t, N>& subs, const std::array<context<T>, N>& contexts, U&&... args) + { + epee::byte_stream buf{}; + + std::size_t last_offset = 0; + std::array<std::size_t, N> offsets{{}}; + for (std::size_t i = 0; i < N; ++i) + { + if (subs[i]) + { + write_header(buf, contexts[i].name); + contexts[i].generate_pub(buf, std::forward<U>(args)...); + offsets[i] = buf.size() - last_offset; + last_offset = buf.size(); + } + } + + epee::byte_slice bytes{std::move(buf)}; + std::array<epee::byte_slice, N> out; + for (std::size_t i = 0; i < N; ++i) + out[i] = bytes.take_slice(offsets[i]); + + return out; + } + + template<std::size_t N> + std::size_t send_messages(void* const socket, std::array<epee::byte_slice, N>& messages) + { + std::size_t count = 0; + for (epee::byte_slice& message : messages) + { + if (!message.empty()) + { + const expect<void> sent = net::zmq::send(std::move(message), socket, ZMQ_DONTWAIT); + if (!sent) + MERROR("Failed to send ZMQ/Pub message: " << sent.error().message()); + else + ++count; + } + } + return count; + } + + expect<bool> relay_block_pub(void* const relay, void* const pub) noexcept + { + zmq_msg_t msg; + zmq_msg_init(std::addressof(msg)); + MONERO_CHECK(net::zmq::retry_op(zmq_msg_recv, std::addressof(msg), relay, ZMQ_DONTWAIT)); + + const boost::string_ref payload{ + reinterpret_cast<const char*>(zmq_msg_data(std::addressof(msg))), + zmq_msg_size(std::addressof(msg)) + }; + + if (payload == txpool_signal) + { + zmq_msg_close(std::addressof(msg)); + return false; + } + + // forward block messages (serialized on P2P thread for now) + const expect<void> sent = net::zmq::retry_op(zmq_msg_send, std::addressof(msg), pub, ZMQ_DONTWAIT); + if (!sent) + { + zmq_msg_close(std::addressof(msg)); + return sent.error(); + } + return true; + } +} // anonymous + +namespace cryptonote { namespace listener +{ + +zmq_pub::zmq_pub(void* context) + : relay_(), + chain_subs_{{0}}, + txpool_subs_{{0}}, + sync_() +{ + if (!context) + throw std::logic_error{"ZMQ context cannot be NULL"}; + + verify_sorted(chain_contexts, "chain_contexts"); + verify_sorted(txpool_contexts, "txpool_contexts"); + + relay_.reset(zmq_socket(context, ZMQ_PAIR)); + if (!relay_) + MONERO_ZMQ_THROW("Failed to create relay socket"); + if (zmq_connect(relay_.get(), relay_endpoint()) != 0) + MONERO_ZMQ_THROW("Failed to connect relay socket"); +} + +zmq_pub::~zmq_pub() +{} + +bool zmq_pub::sub_request(boost::string_ref message) +{ + if (!message.empty()) + { + const char tag = message[0]; + message.remove_prefix(1); + + const auto chain_range = get_range(chain_contexts, message); + const auto txpool_range = get_range(txpool_contexts, message); + + if (!chain_range.empty() || !txpool_range.empty()) + { + MDEBUG("Client " << (tag ? "subscribed" : "unsubscribed") << " to " << + chain_range.size() << " chain topic(s) and " << txpool_range.size() << " txpool topic(s)"); + + const boost::lock_guard<boost::mutex> lock{sync_}; + switch (tag) + { + case 0: + remove_subscriptions(chain_subs_, chain_range, chain_contexts.begin()); + remove_subscriptions(txpool_subs_, txpool_range, txpool_contexts.begin()); + return true; + case 1: + add_subscriptions(chain_subs_, chain_range, chain_contexts.begin()); + add_subscriptions(txpool_subs_, txpool_range, txpool_contexts.begin()); + return true; + default: + break; + } + } + } + MERROR("Invalid ZMQ/Sub message"); + return false; +} + +bool zmq_pub::relay_to_pub(void* const relay, void* const pub) +{ + const expect<bool> relayed = relay_block_pub(relay, pub); + if (!relayed) + { + MERROR("Error relaying ZMQ/Pub: " << relayed.error().message()); + return false; + } + + if (!*relayed) + { + std::array<std::size_t, 2> subs; + std::vector<cryptonote::txpool_event> events; + { + const boost::lock_guard<boost::mutex> lock{sync_}; + if (txes_.empty()) + return false; + + subs = txpool_subs_; + events = std::move(txes_.front()); + txes_.pop_front(); + } + auto messages = make_pubs(subs, txpool_contexts, epee::to_span(events)); + send_messages(pub, messages); + MDEBUG("Sent txpool ZMQ/Pub"); + } + else + MDEBUG("Sent chain_main ZMQ/Pub"); + + return true; +} + +std::size_t zmq_pub::send_chain_main(const std::uint64_t height, const epee::span<const cryptonote::block> blocks) +{ + if (blocks.empty()) + return 0; + + /* Block format only sends one block at a time - multiple block notifications + are less common and only occur on rollbacks. */ + + boost::unique_lock<boost::mutex> guard{sync_}; + + const auto subs_copy = chain_subs_; + guard.unlock(); + + for (const std::size_t sub : subs_copy) + { + if (sub) + { + /* cryptonote_core/blockchain.cpp cannot "give" us the block like core + does for txpool events. Since copying the block is expensive anyway, + serialization is done right here on the p2p thread (for now). */ + + auto messages = make_pubs(subs_copy, chain_contexts, height, blocks); + guard.lock(); + return send_messages(relay_.get(), messages); + } + } + return 0; +} + +std::size_t zmq_pub::send_txpool_add(std::vector<txpool_event> txes) +{ + if (txes.empty()) + return 0; + + const boost::lock_guard<boost::mutex> lock{sync_}; + for (const std::size_t sub : txpool_subs_) + { + if (sub) + { + const expect<void> sent = net::zmq::retry_op(zmq_send_const, relay_.get(), txpool_signal, sizeof(txpool_signal) - 1, ZMQ_DONTWAIT); + if (sent) + txes_.emplace_back(std::move(txes)); + else + MERROR("ZMQ/Pub failure, relay queue error: " << sent.error().message()); + return bool(sent); + } + } + return 0; +} + +void zmq_pub::chain_main::operator()(const std::uint64_t height, epee::span<const cryptonote::block> blocks) const +{ + const std::shared_ptr<zmq_pub> self = self_.lock(); + if (self) + self->send_chain_main(height, blocks); + else + MERROR("Unable to send ZMQ/Pub - ZMQ server destroyed"); +} + +void zmq_pub::txpool_add::operator()(std::vector<cryptonote::txpool_event> txes) const +{ + const std::shared_ptr<zmq_pub> self = self_.lock(); + if (self) + self->send_txpool_add(std::move(txes)); + else + MERROR("Unable to send ZMQ/Pub - ZMQ server destroyed"); +} + +}} diff --git a/src/rpc/zmq_pub.h b/src/rpc/zmq_pub.h new file mode 100644 index 000000000..02e6b8103 --- /dev/null +++ b/src/rpc/zmq_pub.h @@ -0,0 +1,110 @@ +// Copyright (c) 2020, 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 <array> +#include <boost/thread/mutex.hpp> +#include <boost/utility/string_ref.hpp> +#include <cstdint> +#include <deque> +#include <memory> +#include <vector> + +#include "cryptonote_basic/fwd.h" +#include "net/zmq.h" +#include "span.h" + +namespace cryptonote { namespace listener +{ +/*! \brief Sends ZMQ PUB messages on cryptonote events + + Clients must ensure that all transaction(s) are notified before any blocks + they are contained in, and must ensure that each block is notified in chain + order. An external lock **must** be held by clients during the entire + txpool check and notification sequence and (a possibly second) lock is held + during the entire block check and notification sequence. Otherwise, events + could be sent in a different order than processed. */ +class zmq_pub +{ + /* Each socket has its own internal queue. So we can only use one socket, else + the messages being published are not guaranteed to be in the same order + pushed. */ + + net::zmq::socket relay_; + std::deque<std::vector<txpool_event>> txes_; + std::array<std::size_t, 2> chain_subs_; + std::array<std::size_t, 2> txpool_subs_; + boost::mutex sync_; //!< Synchronizes counts in `*_subs_` arrays. + + public: + //! \return Name of ZMQ_PAIR endpoint for pub notifications + static constexpr const char* relay_endpoint() noexcept { return "inproc://pub_relay"; } + + explicit zmq_pub(void* context); + + zmq_pub(const zmq_pub&) = delete; + zmq_pub(zmq_pub&&) = delete; + + ~zmq_pub(); + + zmq_pub& operator=(const zmq_pub&) = delete; + zmq_pub& operator=(zmq_pub&&) = delete; + + //! Process a client subscription request (from XPUB sockets). Thread-safe. + bool sub_request(const boost::string_ref message); + + /*! Forward ZMQ messages sent to `relay` via `send_chain_main` or + `send_txpool_add` to `pub`. Used by `ZmqServer`. */ + bool relay_to_pub(void* relay, void* pub); + + /*! Send a `ZMQ_PUB` notification for a change to the main chain. + Thread-safe. + \return Number of ZMQ messages sent to relay. */ + std::size_t send_chain_main(std::uint64_t height, epee::span<const cryptonote::block> blocks); + + /*! Send a `ZMQ_PUB` notification for new tx(es) being added to the local + pool. Thread-safe. + \return Number of ZMQ messages sent to relay. */ + std::size_t send_txpool_add(std::vector<cryptonote::txpool_event> txes); + + //! Callable for `send_chain_main` with weak ownership to `zmq_pub` object. + struct chain_main + { + std::weak_ptr<zmq_pub> self_; + void operator()(std::uint64_t height, epee::span<const cryptonote::block> blocks) const; + }; + + //! Callable for `send_txpool_add` with weak ownership to `zmq_pub` object. + struct txpool_add + { + std::weak_ptr<zmq_pub> self_; + void operator()(std::vector<cryptonote::txpool_event> txes) const; + }; + }; +}} diff --git a/src/rpc/zmq_server.cpp b/src/rpc/zmq_server.cpp index 1ee55673e..6105b7f3a 100644 --- a/src/rpc/zmq_server.cpp +++ b/src/rpc/zmq_server.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019, The Monero Project +// Copyright (c) 2016-2020, The Monero Project // // All rights reserved. // @@ -29,9 +29,17 @@ #include "zmq_server.h" #include <chrono> -#include <cstdint> +#include <cstring> +#include <utility> +#include <stdexcept> #include <system_error> +#include "byte_slice.h" +#include "rpc/zmq_pub.h" + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "net.zmq" + namespace cryptonote { @@ -40,14 +48,57 @@ namespace constexpr const int num_zmq_threads = 1; constexpr const std::int64_t max_message_size = 10 * 1024 * 1024; // 10 MiB constexpr const std::chrono::seconds linger_timeout{2}; // wait period for pending out messages -} + + net::zmq::socket init_socket(void* context, int type, epee::span<const std::string> addresses) + { + if (context == nullptr) + throw std::logic_error{"NULL context provided"}; + + net::zmq::socket out{}; + out.reset(zmq_socket(context, type)); + if (!out) + { + MONERO_LOG_ZMQ_ERROR("Failed to create ZMQ socket"); + return nullptr; + } + + if (zmq_setsockopt(out.get(), ZMQ_MAXMSGSIZE, std::addressof(max_message_size), sizeof(max_message_size)) != 0) + { + MONERO_LOG_ZMQ_ERROR("Failed to set maximum incoming message size"); + return nullptr; + } + + static constexpr const int linger_value = std::chrono::milliseconds{linger_timeout}.count(); + if (zmq_setsockopt(out.get(), ZMQ_LINGER, std::addressof(linger_value), sizeof(linger_value)) != 0) + { + MONERO_LOG_ZMQ_ERROR("Failed to set linger timeout"); + return nullptr; + } + + for (const std::string& address : addresses) + { + if (zmq_bind(out.get(), address.c_str()) < 0) + { + MONERO_LOG_ZMQ_ERROR("ZMQ bind failed"); + return nullptr; + } + MINFO("ZMQ now listening at " << address); + } + + return out; + } +} // anonymous namespace rpc { ZmqServer::ZmqServer(RpcHandler& h) : handler(h), - context(zmq_init(num_zmq_threads)) + context(zmq_init(num_zmq_threads)), + rep_socket(nullptr), + pub_socket(nullptr), + relay_socket(nullptr), + shared_state(nullptr) { if (!context) MONERO_ZMQ_THROW("Unable to create ZMQ context"); @@ -62,21 +113,59 @@ void ZmqServer::serve() try { // socket must close before `zmq_term` will exit. - const net::zmq::socket socket = std::move(rep_socket); - if (!socket) + const net::zmq::socket rep = std::move(rep_socket); + const net::zmq::socket pub = std::move(pub_socket); + const net::zmq::socket relay = std::move(relay_socket); + const std::shared_ptr<listener::zmq_pub> state = std::move(shared_state); + + const unsigned init_count = unsigned(bool(pub)) + bool(relay) + bool(state); + if (!rep || (init_count && init_count != 3)) { - MERROR("ZMQ RPC server reply socket is null"); + MERROR("ZMQ RPC server socket is null"); return; } + MINFO("ZMQ Server started"); + + const int read_flags = pub ? ZMQ_DONTWAIT : 0; + std::array<zmq_pollitem_t, 3> sockets = + {{ + {relay.get(), 0, ZMQ_POLLIN, 0}, + {pub.get(), 0, ZMQ_POLLIN, 0}, + {rep.get(), 0, ZMQ_POLLIN, 0} + }}; + + /* This uses XPUB to watch for subscribers, to reduce CPU cycles for + serialization when the data will be dropped. This is important for block + serialization, which is done on the p2p threads currently (see + zmq_pub.cpp). + + XPUB sockets are not thread-safe, so the p2p thread cannot write into + the socket while we read here for subscribers. A ZMQ_PAIR socket is + used for inproc notification. No data is every copied to kernel, it is + all userspace messaging. */ + while (1) { - const std::string message = MONERO_UNWRAP(net::zmq::receive(socket.get())); - MDEBUG("Received RPC request: \"" << message << "\""); - const std::string& response = handler.handle(message); + if (pub) + MONERO_UNWRAP(net::zmq::retry_op(zmq_poll, sockets.data(), sockets.size(), -1)); - MONERO_UNWRAP(net::zmq::send(epee::strspan<std::uint8_t>(response), socket.get())); - MDEBUG("Sent RPC reply: \"" << response << "\""); + if (sockets[0].revents) + state->relay_to_pub(relay.get(), pub.get()); + + if (sockets[1].revents) + state->sub_request(MONERO_UNWRAP(net::zmq::receive(pub.get(), ZMQ_DONTWAIT))); + + if (!pub || sockets[2].revents) + { + const std::string message = MONERO_UNWRAP(net::zmq::receive(rep.get(), read_flags)); + MDEBUG("Received RPC request: \"" << message << "\""); + epee::byte_slice response = handler.handle(message); + + const boost::string_ref response_view{reinterpret_cast<const char*>(response.data()), response.size()}; + MDEBUG("Sending RPC reply: \"" << response_view << "\""); + MONERO_UNWRAP(net::zmq::send(std::move(response), rep.get())); + } } } catch (const std::system_error& e) @@ -94,38 +183,12 @@ void ZmqServer::serve() } } -bool ZmqServer::addIPCSocket(const boost::string_ref address, const boost::string_ref port) -{ - MERROR("ZmqServer::addIPCSocket not yet implemented!"); - return false; -} - -bool ZmqServer::addTCPSocket(boost::string_ref address, boost::string_ref port) +void* ZmqServer::init_rpc(boost::string_ref address, boost::string_ref port) { if (!context) { MERROR("ZMQ RPC Server already shutdown"); - return false; - } - - rep_socket.reset(zmq_socket(context.get(), ZMQ_REP)); - if (!rep_socket) - { - MONERO_LOG_ZMQ_ERROR("ZMQ RPC Server socket create failed"); - return false; - } - - if (zmq_setsockopt(rep_socket.get(), ZMQ_MAXMSGSIZE, std::addressof(max_message_size), sizeof(max_message_size)) != 0) - { - MONERO_LOG_ZMQ_ERROR("Failed to set maximum incoming message size"); - return false; - } - - static constexpr const int linger_value = std::chrono::milliseconds{linger_timeout}.count(); - if (zmq_setsockopt(rep_socket.get(), ZMQ_LINGER, std::addressof(linger_value), sizeof(linger_value)) != 0) - { - MONERO_LOG_ZMQ_ERROR("Failed to set linger timeout"); - return false; + return nullptr; } if (address.empty()) @@ -138,12 +201,34 @@ bool ZmqServer::addTCPSocket(boost::string_ref address, boost::string_ref port) bind_address += ":"; bind_address.append(port.data(), port.size()); - if (zmq_bind(rep_socket.get(), bind_address.c_str()) < 0) + rep_socket = init_socket(context.get(), ZMQ_REP, {std::addressof(bind_address), 1}); + return bool(rep_socket) ? context.get() : nullptr; +} + +std::shared_ptr<listener::zmq_pub> ZmqServer::init_pub(epee::span<const std::string> addresses) +{ + try + { + shared_state = std::make_shared<listener::zmq_pub>(context.get()); + pub_socket = init_socket(context.get(), ZMQ_XPUB, addresses); + if (!pub_socket) + throw std::runtime_error{"Unable to initialize ZMQ_XPUB socket"}; + + const std::string relay_address[] = {listener::zmq_pub::relay_endpoint()}; + relay_socket = init_socket(context.get(), ZMQ_PAIR, relay_address); + if (!relay_socket) + throw std::runtime_error{"Unable to initialize ZMQ_PAIR relay"}; + } + catch (const std::runtime_error& e) { - MONERO_LOG_ZMQ_ERROR("ZMQ RPC Server bind failed"); - return false; + shared_state = nullptr; + pub_socket = nullptr; + relay_socket = nullptr; + MERROR("Failed to create ZMQ/Pub listener: " << e.what()); + return nullptr; } - return true; + + return shared_state; } void ZmqServer::run() @@ -160,7 +245,6 @@ void ZmqServer::stop() run_thread.join(); } - } // namespace cryptonote } // namespace rpc diff --git a/src/rpc/zmq_server.h b/src/rpc/zmq_server.h index ce7892dab..ddf44b411 100644 --- a/src/rpc/zmq_server.h +++ b/src/rpc/zmq_server.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019, The Monero Project +// Copyright (c) 2016-2020, The Monero Project // // All rights reserved. // @@ -30,10 +30,16 @@ #include <boost/thread/thread.hpp> #include <boost/utility/string_ref.hpp> +#include <cstdint> +#include <memory> +#include <string> #include "common/command_line.h" +#include "cryptonote_basic/fwd.h" #include "net/zmq.h" -#include "rpc_handler.h" +#include "rpc/fwd.h" +#include "rpc/rpc_handler.h" +#include "span.h" namespace cryptonote { @@ -41,7 +47,7 @@ namespace cryptonote namespace rpc { -class ZmqServer +class ZmqServer final { public: @@ -49,12 +55,13 @@ class ZmqServer ~ZmqServer(); - static void init_options(boost::program_options::options_description& desc); - void serve(); - bool addIPCSocket(boost::string_ref address, boost::string_ref port); - bool addTCPSocket(boost::string_ref address, boost::string_ref port); + //! \return ZMQ context on success, `nullptr` on failure + void* init_rpc(boost::string_ref address, boost::string_ref port); + + //! \return `nullptr` on errors. + std::shared_ptr<listener::zmq_pub> init_pub(epee::span<const std::string> addresses); void run(); void stop(); @@ -67,9 +74,11 @@ class ZmqServer boost::thread run_thread; net::zmq::socket rep_socket; + net::zmq::socket pub_socket; + net::zmq::socket relay_socket; + std::shared_ptr<listener::zmq_pub> shared_state; }; - } // namespace cryptonote } // namespace rpc diff --git a/src/serialization/CMakeLists.txt b/src/serialization/CMakeLists.txt index 0b231f156..a2e7c353e 100644 --- a/src/serialization/CMakeLists.txt +++ b/src/serialization/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2016-2019, The Monero Project +# Copyright (c) 2016-2020, The Monero Project # # All rights reserved. # diff --git a/src/serialization/binary_archive.h b/src/serialization/binary_archive.h index 9f60cf311..80104c8f7 100644 --- a/src/serialization/binary_archive.h +++ b/src/serialization/binary_archive.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/serialization/binary_utils.h b/src/serialization/binary_utils.h index 790661e49..8635585af 100644 --- a/src/serialization/binary_utils.h +++ b/src/serialization/binary_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/serialization/container.h b/src/serialization/container.h index dfe0f9691..4bf47ecfa 100644 --- a/src/serialization/container.h +++ b/src/serialization/container.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/serialization/crypto.h b/src/serialization/crypto.h index d2537146e..42bd06e92 100644 --- a/src/serialization/crypto.h +++ b/src/serialization/crypto.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/serialization/debug_archive.h b/src/serialization/debug_archive.h index 093ca41eb..b04d6ae19 100644 --- a/src/serialization/debug_archive.h +++ b/src/serialization/debug_archive.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/serialization/deque.h b/src/serialization/deque.h index 97f16d0a5..b029ed430 100644 --- a/src/serialization/deque.h +++ b/src/serialization/deque.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/serialization/difficulty_type.h b/src/serialization/difficulty_type.h index c551095f9..56c0312e7 100644 --- a/src/serialization/difficulty_type.h +++ b/src/serialization/difficulty_type.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/serialization/json_archive.h b/src/serialization/json_archive.h index c9f4e175e..50dd5bbd0 100644 --- a/src/serialization/json_archive.h +++ b/src/serialization/json_archive.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp index f20fd181a..5c042aa7b 100644 --- a/src/serialization/json_object.cpp +++ b/src/serialization/json_object.cpp @@ -120,18 +120,18 @@ void read_hex(const rapidjson::Value& val, epee::span<std::uint8_t> dest) throw WRONG_TYPE("string"); } - if (!epee::from_hex::to_buffer(dest, {val.GetString(), val.Size()})) + if (!epee::from_hex::to_buffer(dest, {val.GetString(), val.GetStringLength()})) { throw BAD_INPUT(); } } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rapidjson::Value& src) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rapidjson::Value& src) { src.Accept(dest); } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const boost::string_ref i) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const boost::string_ref i) { dest.String(i.data(), i.size()); } @@ -146,7 +146,7 @@ void fromJsonValue(const rapidjson::Value& val, std::string& str) str = val.GetString(); } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, bool i) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, bool i) { dest.Bool(i); } @@ -185,7 +185,7 @@ void fromJsonValue(const rapidjson::Value& val, short& i) to_int(val, i); } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const unsigned int i) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const unsigned int i) { dest.Uint(i); } @@ -195,7 +195,7 @@ void fromJsonValue(const rapidjson::Value& val, unsigned int& i) to_uint(val, i); } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const int i) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const int i) { dest.Int(i); } @@ -205,7 +205,7 @@ void fromJsonValue(const rapidjson::Value& val, int& i) to_int(val, i); } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const unsigned long long i) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const unsigned long long i) { static_assert(!precision_loss<unsigned long long, std::uint64_t>(), "bad uint64 conversion"); dest.Uint64(i); @@ -216,7 +216,7 @@ void fromJsonValue(const rapidjson::Value& val, unsigned long long& i) to_uint64(val, i); } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const long long i) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const long long i) { static_assert(!precision_loss<long long, std::int64_t>(), "bad int64 conversion"); dest.Int64(i); @@ -237,7 +237,7 @@ void fromJsonValue(const rapidjson::Value& val, long& i) to_int64(val, i); } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::transaction& tx) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::transaction& tx) { dest.StartObject(); @@ -269,7 +269,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::transaction& tx) GET_FROM_JSON_OBJECT(val, tx.rct_signatures, ringct); } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::block& b) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::block& b) { dest.StartObject(); @@ -301,14 +301,14 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::block& b) GET_FROM_JSON_OBJECT(val, b.tx_hashes, tx_hashes); } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txin_v& txin) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txin_v& txin) { dest.StartObject(); struct add_input { using result_type = void; - rapidjson::Writer<rapidjson::StringBuffer>& dest; + rapidjson::Writer<epee::byte_stream>& dest; void operator()(cryptonote::txin_to_key const& input) const { @@ -373,7 +373,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_v& txin) } } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txin_gen& txin) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txin_gen& txin) { dest.StartObject(); @@ -392,7 +392,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_gen& txin) GET_FROM_JSON_OBJECT(val, txin.height, height); } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txin_to_script& txin) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txin_to_script& txin) { dest.StartObject(); @@ -417,7 +417,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_script& txin } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txin_to_scripthash& txin) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txin_to_scripthash& txin) { dest.StartObject(); @@ -443,7 +443,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_scripthash& GET_FROM_JSON_OBJECT(val, txin.sigset, sigset); } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txin_to_key& txin) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txin_to_key& txin) { dest.StartObject(); @@ -467,7 +467,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_key& txin) } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txout_to_script& txout) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txout_to_script& txout) { dest.StartObject(); @@ -489,7 +489,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_script& txo } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txout_to_scripthash& txout) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txout_to_scripthash& txout) { dest.StartObject(); @@ -509,7 +509,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_scripthash& } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txout_to_key& txout) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txout_to_key& txout) { dest.StartObject(); @@ -528,7 +528,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_key& txout) GET_FROM_JSON_OBJECT(val, txout.key, key); } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::tx_out& txout) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::tx_out& txout) { dest.StartObject(); INSERT_INTO_JSON_OBJECT(dest, amount, txout.amount); @@ -537,7 +537,7 @@ void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const crypton { using result_type = void; - rapidjson::Writer<rapidjson::StringBuffer>& dest; + rapidjson::Writer<epee::byte_stream>& dest; void operator()(cryptonote::txout_to_key const& output) const { @@ -596,7 +596,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_out& txout) } } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::connection_info& info) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::connection_info& info) { dest.StartObject(); @@ -668,7 +668,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::connection_info& inf GET_FROM_JSON_OBJECT(val, info.current_upload, current_upload); } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::tx_blob_entry& tx) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::tx_blob_entry& tx) { dest.StartObject(); @@ -689,7 +689,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_blob_entry& tx) GET_FROM_JSON_OBJECT(val, tx.prunable_hash, prunable_hash); } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::block_complete_entry& blk) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::block_complete_entry& blk) { dest.StartObject(); @@ -711,7 +711,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::block_complete_entry GET_FROM_JSON_OBJECT(val, blk.txs, transactions); } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::block_with_transactions& blk) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::block_with_transactions& blk) { dest.StartObject(); @@ -733,7 +733,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::block_with_tran GET_FROM_JSON_OBJECT(val, blk.transactions, transactions); } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::transaction_info& tx_info) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::transaction_info& tx_info) { dest.StartObject(); @@ -757,7 +757,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::transaction_inf GET_FROM_JSON_OBJECT(val, tx_info.transaction, transaction); } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::output_key_and_amount_index& out) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::output_key_and_amount_index& out) { dest.StartObject(); @@ -779,7 +779,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_key_and_ GET_FROM_JSON_OBJECT(val, out.key, key); } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::amount_with_random_outputs& out) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::amount_with_random_outputs& out) { dest.StartObject(); @@ -801,7 +801,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::amount_with_ran GET_FROM_JSON_OBJECT(val, out.outputs, outputs); } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::peer& peer) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::peer& peer) { dest.StartObject(); @@ -833,7 +833,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::peer& peer) GET_FROM_JSON_OBJECT(val, peer.pruning_seed, pruning_seed); } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::tx_in_pool& tx) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::tx_in_pool& tx) { dest.StartObject(); @@ -880,7 +880,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::tx_in_pool& tx) GET_FROM_JSON_OBJECT(val, tx.double_spend_seen, double_spend_seen); } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::hard_fork_info& info) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::hard_fork_info& info) { dest.StartObject(); @@ -914,7 +914,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::hard_fork_info& GET_FROM_JSON_OBJECT(val, info.earliest_height, earliest_height); } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::output_amount_count& out) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::output_amount_count& out) { dest.StartObject(); @@ -940,7 +940,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_amount_c GET_FROM_JSON_OBJECT(val, out.recent_count, recent_count); } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::output_amount_and_index& out) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::output_amount_and_index& out) { dest.StartObject(); @@ -962,7 +962,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_amount_a GET_FROM_JSON_OBJECT(val, out.index, index); } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::output_key_mask_unlocked& out) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::output_key_mask_unlocked& out) { dest.StartObject(); @@ -985,7 +985,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_key_mask GET_FROM_JSON_OBJECT(val, out.unlocked, unlocked); } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::error& err) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::error& err) { dest.StartObject(); @@ -1008,7 +1008,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::error& error) GET_FROM_JSON_OBJECT(val, error.message, message); } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::BlockHeaderResponse& response) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::BlockHeaderResponse& response) { dest.StartObject(); @@ -1045,7 +1045,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::BlockHeaderResp GET_FROM_JSON_OBJECT(val, response.reward, reward); } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::rctSig& sig) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::rctSig& sig) { using boost::adaptors::transform; @@ -1115,7 +1115,7 @@ void fromJsonValue(const rapidjson::Value& val, rct::rctSig& sig) } } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::ecdhTuple& tuple) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::ecdhTuple& tuple) { dest.StartObject(); INSERT_INTO_JSON_OBJECT(dest, mask, tuple.mask); @@ -1134,7 +1134,7 @@ void fromJsonValue(const rapidjson::Value& val, rct::ecdhTuple& tuple) GET_FROM_JSON_OBJECT(val, tuple.amount, amount); } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::rangeSig& sig) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::rangeSig& sig) { dest.StartObject(); @@ -1171,7 +1171,7 @@ void fromJsonValue(const rapidjson::Value& val, rct::rangeSig& sig) } } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::Bulletproof& p) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::Bulletproof& p) { dest.StartObject(); @@ -1212,7 +1212,7 @@ void fromJsonValue(const rapidjson::Value& val, rct::Bulletproof& p) GET_FROM_JSON_OBJECT(val, p.t, t); } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::boroSig& sig) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::boroSig& sig) { dest.StartObject(); @@ -1257,7 +1257,7 @@ void fromJsonValue(const rapidjson::Value& val, rct::boroSig& sig) GET_FROM_JSON_OBJECT(val, sig.ee, ee); } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::mgSig& sig) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::mgSig& sig) { dest.StartObject(); @@ -1278,7 +1278,7 @@ void fromJsonValue(const rapidjson::Value& val, rct::mgSig& sig) GET_FROM_JSON_OBJECT(val, sig.cc, cc); } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::DaemonInfo& info) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::DaemonInfo& info) { dest.StartObject(); @@ -1339,7 +1339,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& inf GET_FROM_JSON_OBJECT(val, info.start_time, start_time); } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::output_distribution& dist) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::output_distribution& dist) { dest.StartObject(); diff --git a/src/serialization/json_object.h b/src/serialization/json_object.h index 664b539b5..e016bef41 100644 --- a/src/serialization/json_object.h +++ b/src/serialization/json_object.h @@ -31,9 +31,9 @@ #include <boost/utility/string_ref.hpp> #include <cstring> #include <rapidjson/document.h> -#include <rapidjson/stringbuffer.h> #include <rapidjson/writer.h> +#include "byte_stream.h" #include "cryptonote_basic/cryptonote_basic.h" #include "rpc/message_data_structs.h" #include "cryptonote_protocol/cryptonote_protocol_defs.h" @@ -123,7 +123,7 @@ void read_hex(const rapidjson::Value& val, epee::span<std::uint8_t> dest); // POD to json key template <class Type> -inline typename std::enable_if<is_to_hex<Type>()>::type toJsonKey(rapidjson::Writer<rapidjson::StringBuffer>& dest, const Type& pod) +inline typename std::enable_if<is_to_hex<Type>()>::type toJsonKey(rapidjson::Writer<epee::byte_stream>& dest, const Type& pod) { const auto hex = epee::to_hex::array(pod); dest.Key(hex.data(), hex.size()); @@ -131,7 +131,7 @@ inline typename std::enable_if<is_to_hex<Type>()>::type toJsonKey(rapidjson::Wri // POD to json value template <class Type> -inline typename std::enable_if<is_to_hex<Type>()>::type toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const Type& pod) +inline typename std::enable_if<is_to_hex<Type>()>::type toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const Type& pod) { const auto hex = epee::to_hex::array(pod); dest.String(hex.data(), hex.size()); @@ -144,16 +144,16 @@ inline typename std::enable_if<is_to_hex<Type>()>::type fromJsonValue(const rapi json::read_hex(val, epee::as_mut_byte_span(t)); } -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rapidjson::Value& src); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rapidjson::Value& src); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, boost::string_ref i); -inline void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const std::string& i) +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, boost::string_ref i); +inline void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const std::string& i) { toJsonValue(dest, boost::string_ref{i}); } void fromJsonValue(const rapidjson::Value& val, std::string& str); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, bool i); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, bool i); void fromJsonValue(const rapidjson::Value& val, bool& b); // integers overloads for toJsonValue are not needed for standard promotions @@ -168,144 +168,144 @@ void fromJsonValue(const rapidjson::Value& val, unsigned short& i); void fromJsonValue(const rapidjson::Value& val, short& i); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const unsigned i); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const unsigned i); void fromJsonValue(const rapidjson::Value& val, unsigned& i); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const int); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const int); void fromJsonValue(const rapidjson::Value& val, int& i); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const unsigned long long i); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const unsigned long long i); void fromJsonValue(const rapidjson::Value& val, unsigned long long& i); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const long long i); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const long long i); void fromJsonValue(const rapidjson::Value& val, long long& i); -inline void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const unsigned long i) { +inline void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const unsigned long i) { toJsonValue(dest, static_cast<unsigned long long>(i)); } void fromJsonValue(const rapidjson::Value& val, unsigned long& i); -inline void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const long i) { +inline void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const long i) { toJsonValue(dest, static_cast<long long>(i)); } void fromJsonValue(const rapidjson::Value& val, long& i); // end integers -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::transaction& tx); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::transaction& tx); void fromJsonValue(const rapidjson::Value& val, cryptonote::transaction& tx); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::block& b); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::block& b); void fromJsonValue(const rapidjson::Value& val, cryptonote::block& b); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txin_v& txin); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txin_v& txin); void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_v& txin); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txin_gen& txin); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txin_gen& txin); void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_gen& txin); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txin_to_script& txin); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txin_to_script& txin); void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_script& txin); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txin_to_scripthash& txin); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txin_to_scripthash& txin); void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_scripthash& txin); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txin_to_key& txin); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txin_to_key& txin); void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_key& txin); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txout_target_v& txout); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txout_target_v& txout); void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_target_v& txout); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txout_to_script& txout); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txout_to_script& txout); void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_script& txout); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txout_to_scripthash& txout); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txout_to_scripthash& txout); void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_scripthash& txout); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txout_to_key& txout); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txout_to_key& txout); void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_key& txout); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::tx_out& txout); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::tx_out& txout); void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_out& txout); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::connection_info& info); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::connection_info& info); void fromJsonValue(const rapidjson::Value& val, cryptonote::connection_info& info); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::tx_blob_entry& tx); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::tx_blob_entry& tx); void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_blob_entry& tx); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::block_complete_entry& blk); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::block_complete_entry& blk); void fromJsonValue(const rapidjson::Value& val, cryptonote::block_complete_entry& blk); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::block_with_transactions& blk); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::block_with_transactions& blk); void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::block_with_transactions& blk); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::transaction_info& tx_info); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::transaction_info& tx_info); void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::transaction_info& tx_info); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::output_key_and_amount_index& out); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::output_key_and_amount_index& out); void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_key_and_amount_index& out); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::amount_with_random_outputs& out); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::amount_with_random_outputs& out); void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::amount_with_random_outputs& out); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::peer& peer); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::peer& peer); void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::peer& peer); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::tx_in_pool& tx); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::tx_in_pool& tx); void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::tx_in_pool& tx); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::hard_fork_info& info); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::hard_fork_info& info); void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::hard_fork_info& info); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::output_amount_count& out); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::output_amount_count& out); void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_amount_count& out); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::output_amount_and_index& out); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::output_amount_and_index& out); void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_amount_and_index& out); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::output_key_mask_unlocked& out); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::output_key_mask_unlocked& out); void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_key_mask_unlocked& out); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::error& err); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::error& err); void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::error& error); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::BlockHeaderResponse& response); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::BlockHeaderResponse& response); void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::BlockHeaderResponse& response); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::rctSig& i); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::rctSig& i); void fromJsonValue(const rapidjson::Value& val, rct::rctSig& sig); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::ecdhTuple& tuple); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::ecdhTuple& tuple); void fromJsonValue(const rapidjson::Value& val, rct::ecdhTuple& tuple); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::rangeSig& sig); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::rangeSig& sig); void fromJsonValue(const rapidjson::Value& val, rct::rangeSig& sig); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::Bulletproof& p); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::Bulletproof& p); void fromJsonValue(const rapidjson::Value& val, rct::Bulletproof& p); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::boroSig& sig); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::boroSig& sig); void fromJsonValue(const rapidjson::Value& val, rct::boroSig& sig); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::mgSig& sig); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::mgSig& sig); void fromJsonValue(const rapidjson::Value& val, rct::mgSig& sig); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::DaemonInfo& info); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::DaemonInfo& info); void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& info); -void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::output_distribution& dist); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::output_distribution& dist); void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_distribution& dist); template <typename Map> -typename std::enable_if<sfinae::is_map_like<Map>::value, void>::type toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const Map& map); +typename std::enable_if<sfinae::is_map_like<Map>::value, void>::type toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const Map& map); template <typename Map> typename std::enable_if<sfinae::is_map_like<Map>::value, void>::type fromJsonValue(const rapidjson::Value& val, Map& map); template <typename Vec> -typename std::enable_if<sfinae::is_vector_like<Vec>::value, void>::type toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const Vec &vec); +typename std::enable_if<sfinae::is_vector_like<Vec>::value, void>::type toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const Vec &vec); template <typename Vec> typename std::enable_if<sfinae::is_vector_like<Vec>::value, void>::type fromJsonValue(const rapidjson::Value& val, Vec& vec); @@ -315,7 +315,7 @@ typename std::enable_if<sfinae::is_vector_like<Vec>::value, void>::type fromJson // unfortunately because of how templates work they have to be here. template <typename Map> -inline typename std::enable_if<sfinae::is_map_like<Map>::value, void>::type toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const Map& map) +inline typename std::enable_if<sfinae::is_map_like<Map>::value, void>::type toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const Map& map) { using key_type = typename Map::key_type; static_assert(std::is_same<std::string, key_type>() || is_to_hex<key_type>(), "invalid map key type"); @@ -351,12 +351,12 @@ inline typename std::enable_if<sfinae::is_map_like<Map>::value, void>::type from } template <typename Vec> -inline typename std::enable_if<sfinae::is_vector_like<Vec>::value, void>::type toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const Vec &vec) +inline typename std::enable_if<sfinae::is_vector_like<Vec>::value, void>::type toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const Vec &vec) { dest.StartArray(); for (const auto& t : vec) toJsonValue(dest, t); - dest.EndArray(vec.size()); + dest.EndArray(); } template <typename Vec> diff --git a/src/serialization/json_utils.h b/src/serialization/json_utils.h index 05122f3bd..8115877dc 100644 --- a/src/serialization/json_utils.h +++ b/src/serialization/json_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/serialization/list.h b/src/serialization/list.h index b7b65e184..139269b40 100644 --- a/src/serialization/list.h +++ b/src/serialization/list.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/serialization/pair.h b/src/serialization/pair.h index aed0c4699..18280d837 100644 --- a/src/serialization/pair.h +++ b/src/serialization/pair.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h index 553e9951f..d0f9ee0f7 100644 --- a/src/serialization/serialization.h +++ b/src/serialization/serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/serialization/set.h b/src/serialization/set.h index ae602a2f1..157c002f7 100644 --- a/src/serialization/set.h +++ b/src/serialization/set.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/serialization/string.h b/src/serialization/string.h index fd8450f7b..f5f69833a 100644 --- a/src/serialization/string.h +++ b/src/serialization/string.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/serialization/unordered_set.h b/src/serialization/unordered_set.h index 6dd01fd93..24aa09437 100644 --- a/src/serialization/unordered_set.h +++ b/src/serialization/unordered_set.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/serialization/variant.h b/src/serialization/variant.h index 5a179ca87..b08cf6bf0 100644 --- a/src/serialization/variant.h +++ b/src/serialization/variant.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/serialization/vector.h b/src/serialization/vector.h index ad96582a5..1fca1d485 100644 --- a/src/serialization/vector.h +++ b/src/serialization/vector.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/simplewallet/CMakeLists.txt b/src/simplewallet/CMakeLists.txt index 030867dc0..a0820c8eb 100644 --- a/src/simplewallet/CMakeLists.txt +++ b/src/simplewallet/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # @@ -49,6 +49,7 @@ target_link_libraries(simplewallet common mnemonics ${EPEE_READLINE} + qrcodegen version ${Boost_CHRONO_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index d45ef3d7c..d36e5009b 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -33,6 +33,11 @@ * * \brief Source file that defines simple_wallet class. */ + +// use boost bind placeholders for now +#define BOOST_BIND_GLOBAL_PLACEHOLDERS 1 +#include <boost/bind.hpp> + #include <locale.h> #include <thread> #include <iostream> @@ -69,10 +74,12 @@ #include "version.h" #include <stdexcept> #include "wallet/message_store.h" +#include "QrCode.hpp" #ifdef WIN32 #include <boost/locale.hpp> #include <boost/filesystem.hpp> +#include <fcntl.h> #endif #ifdef HAVE_READLINE @@ -143,6 +150,7 @@ enum TransferType { }; static std::string get_human_readable_timespan(std::chrono::seconds seconds); +static std::string get_human_readable_timespan(uint64_t seconds); namespace { @@ -182,6 +190,7 @@ namespace const char* USAGE_LOCKED_TRANSFER("locked_transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <addr> <amount>) <lockblocks> [<payment_id (obsolete)>]"); const char* USAGE_LOCKED_SWEEP_ALL("locked_sweep_all [index=<N1>[,<N2>,...] | index=all] [<priority>] [<ring_size>] <address> <lockblocks> [<payment_id (obsolete)>]"); const char* USAGE_SWEEP_ALL("sweep_all [index=<N1>[,<N2>,...] | index=all] [<priority>] [<ring_size>] [outputs=<N>] <address> [<payment_id (obsolete)>]"); + const char* USAGE_SWEEP_ACCOUNT("sweep_account <account> [index=<N1>[,<N2>,...] | index=all] [<priority>] [<ring_size>] [outputs=<N>] <address> [<payment_id (obsolete)>]"); const char* USAGE_SWEEP_BELOW("sweep_below <amount_threshold> [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id (obsolete)>]"); const char* USAGE_SWEEP_SINGLE("sweep_single [<priority>] [<ring_size>] [outputs=<N>] <key_image> <address> [<payment_id (obsolete)>]"); const char* USAGE_DONATE("donate [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <amount> [<payment_id (obsolete)>]"); @@ -207,7 +216,7 @@ namespace const char* USAGE_CHECK_SPEND_PROOF("check_spend_proof <txid> <signature_file> [<message>]"); const char* USAGE_GET_RESERVE_PROOF("get_reserve_proof (all|<amount>) [<message>]"); const char* USAGE_CHECK_RESERVE_PROOF("check_reserve_proof <address> <signature_file> [<message>]"); - const char* USAGE_SHOW_TRANSFERS("show_transfers [in|out|pending|failed|pool|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]]"); + const char* USAGE_SHOW_TRANSFERS("show_transfers [in|out|all|pending|failed|pool|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]]"); const char* USAGE_UNSPENT_OUTPUTS("unspent_outputs [index=<N1>[,<N2>,...]] [<min_amount> [<max_amount>]]"); const char* USAGE_RESCAN_BC("rescan_bc [hard|soft|keep_ki] [start_height=0]"); const char* USAGE_SET_TX_NOTE("set_tx_note <txid> [free text note]"); @@ -248,6 +257,7 @@ namespace const char* USAGE_MMS_SET("mms set <option_name> [<option_value>]"); const char* USAGE_MMS_SEND_SIGNER_CONFIG("mms send_signer_config"); const char* USAGE_MMS_START_AUTO_CONFIG("mms start_auto_config [<label> <label> ...]"); + const char* USAGE_MMS_CONFIG_CHECKSUM("mms config_checksum"); const char* USAGE_MMS_STOP_AUTO_CONFIG("mms stop_auto_config"); const char* USAGE_MMS_AUTO_CONFIG("mms auto_config <auto_config_token>"); const char* USAGE_PRINT_RING("print_ring <key_image> | <txid>"); @@ -267,6 +277,7 @@ namespace const char* USAGE_RPC_PAYMENT_INFO("rpc_payment_info"); const char* USAGE_START_MINING_FOR_RPC("start_mining_for_rpc"); const char* USAGE_STOP_MINING_FOR_RPC("stop_mining_for_rpc"); + const char* USAGE_SHOW_QR_CODE("show_qr_code [<subaddress_index>]"); const char* USAGE_VERSION("version"); const char* USAGE_HELP_ADVANCED("help_advanced [<command>]"); const char* USAGE_HELP("help"); @@ -2386,6 +2397,62 @@ bool simple_wallet::stop_mining_for_rpc(const std::vector<std::string> &args) return true; } +bool simple_wallet::show_qr_code(const std::vector<std::string> &args) +{ + uint32_t subaddress_index = 0; + if (args.size() >= 1) + { + if (!string_tools::get_xtype_from_string(subaddress_index, args[0])) + { + fail_msg_writer() << tr("invalid index: must be an unsigned integer"); + return true; + } + if (subaddress_index >= m_wallet->get_num_subaddresses(m_current_subaddress_account)) + { + fail_msg_writer() << tr("<subaddress_index> is out of bounds"); + return true; + } + } + +#ifdef _WIN32 +#define PRINT_UTF8(pre, x) std::wcout << pre ## x +#define WTEXTON() _setmode(_fileno(stdout), _O_WTEXT) +#define WTEXTOFF() _setmode(_fileno(stdout), _O_TEXT) +#else +#define PRINT_UTF8(pre, x) std::cout << x +#define WTEXTON() +#define WTEXTOFF() +#endif + + WTEXTON(); + try + { + const std::string address = "monero:" + m_wallet->get_subaddress_as_str({m_current_subaddress_account, subaddress_index}); + const qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(address.c_str(), qrcodegen::QrCode::Ecc::LOW); + for (int y = -2; y < qr.getSize() + 2; y+=2) + { + for (int x = -2; x < qr.getSize() + 2; x++) + { + if (qr.getModule(x, y) && qr.getModule(x, y + 1)) + PRINT_UTF8(L, "\u2588"); + else if (qr.getModule(x, y) && !qr.getModule(x, y + 1)) + PRINT_UTF8(L, "\u2580"); + else if (!qr.getModule(x, y) && qr.getModule(x, y + 1)) + PRINT_UTF8(L, "\u2584"); + else + PRINT_UTF8(L, " "); + } + PRINT_UTF8(, std::endl); + } + } + catch (const std::length_error&) + { + fail_msg_writer() << tr("Failed to generate QR code, input too large"); + } + WTEXTOFF(); + return true; +} + bool simple_wallet::set_always_confirm_transfers(const std::vector<std::string> &args/* = std::vector<std::string>()*/) { const auto pwd_container = get_and_verify_password(); @@ -3142,9 +3209,12 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("sweep_unmixable", boost::bind(&simple_wallet::on_command, this, &simple_wallet::sweep_unmixable, _1), tr("Send all unmixable outputs to yourself with ring_size 1")); - m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1), + m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::on_command, this, &simple_wallet::sweep_all, _1), tr(USAGE_SWEEP_ALL), tr("Send all unlocked balance to an address. If the parameter \"index=<N1>[,<N2>,...]\" or \"index=all\" is specified, the wallet sweeps outputs received by those or all address indices, respectively. If omitted, the wallet randomly chooses an address index to be used. If the parameter \"outputs=<N>\" is specified and N > 0, wallet splits the transaction into N even outputs.")); + m_cmd_binder.set_handler("sweep_account", boost::bind(&simple_wallet::on_command, this, &simple_wallet::sweep_account, _1), + tr(USAGE_SWEEP_ACCOUNT), + tr("Send all unlocked balance from a given account to an address. If the parameter \"index=<N1>[,<N2>,...]\" or \"index=all\" is specified, the wallet sweeps outputs received by those or all address indices, respectively. If omitted, the wallet randomly chooses an address index to be used. If the parameter \"outputs=<N>\" is specified and N > 0, wallet splits the transaction into N even outputs.")); m_cmd_binder.set_handler("sweep_below", boost::bind(&simple_wallet::on_command, this, &simple_wallet::sweep_below, _1), tr(USAGE_SWEEP_BELOW), @@ -3206,7 +3276,7 @@ simple_wallet::simple_wallet() boost::bind(&simple_wallet::on_command, this, &simple_wallet::seed, _1), tr("Display the Electrum-style mnemonic seed")); m_cmd_binder.set_handler("restore_height", - boost::bind(&simple_wallet::restore_height, this, _1), + boost::bind(&simple_wallet::on_command, this, &simple_wallet::restore_height, _1), tr("Display the restore height")); m_cmd_binder.set_handler("set", boost::bind(&simple_wallet::on_command, this, &simple_wallet::set_variable, _1), @@ -3338,7 +3408,7 @@ simple_wallet::simple_wallet() "** Set of address indices used as inputs in this transfer.")); m_cmd_binder.set_handler("export_transfers", boost::bind(&simple_wallet::on_command, this, &simple_wallet::export_transfers, _1), - tr("export_transfers [in|out|all|pending|failed|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]] [output=<filepath>]"), + tr("export_transfers [in|out|all|pending|failed|pool|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]] [output=<filepath>]"), tr("Export to CSV the incoming/outgoing transfers within an optional height range.")); m_cmd_binder.set_handler("unspent_outputs", boost::bind(&simple_wallet::on_command, this, &simple_wallet::unspent_outputs, _1), @@ -3455,7 +3525,7 @@ simple_wallet::simple_wallet() tr("Interface with the MMS (Multisig Messaging System)\n" "<subcommand> is one of:\n" " init, info, signer, list, next, sync, transfer, delete, send, receive, export, note, show, set, help\n" - " send_signer_config, start_auto_config, stop_auto_config, auto_config\n" + " send_signer_config, start_auto_config, stop_auto_config, auto_config, config_checksum\n" "Get help about a subcommand with: help_advanced mms <subcommand>")); m_cmd_binder.set_handler("mms init", boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1), @@ -3517,13 +3587,17 @@ simple_wallet::simple_wallet() "auto-send <1|0>\n " " Whether to automatically send newly generated messages right away.\n ")); m_cmd_binder.set_handler("mms send_signer_config", - boost::bind(&simple_wallet::mms, this, _1), + boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1), tr(USAGE_MMS_SEND_SIGNER_CONFIG), tr("Send completed signer config to all other authorized signers")); m_cmd_binder.set_handler("mms start_auto_config", boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1), tr(USAGE_MMS_START_AUTO_CONFIG), tr("Start auto-config at the auto-config manager's wallet by issuing auto-config tokens and optionally set others' labels")); + m_cmd_binder.set_handler("mms config_checksum", + boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1), + tr(USAGE_MMS_CONFIG_CHECKSUM), + tr("Get a checksum that allows signers to easily check for identical MMS configuration")); m_cmd_binder.set_handler("mms stop_auto_config", boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1), tr(USAGE_MMS_STOP_AUTO_CONFIG), @@ -3583,7 +3657,7 @@ simple_wallet::simple_wallet() tr(USAGE_NET_STATS), tr("Prints simple network stats")); m_cmd_binder.set_handler("public_nodes", - boost::bind(&simple_wallet::public_nodes, this, _1), + boost::bind(&simple_wallet::on_command, this, &simple_wallet::public_nodes, _1), tr(USAGE_PUBLIC_NODES), tr("Lists known public nodes")); m_cmd_binder.set_handler("welcome", @@ -3595,17 +3669,21 @@ simple_wallet::simple_wallet() tr(USAGE_VERSION), tr("Returns version information")); m_cmd_binder.set_handler("rpc_payment_info", - boost::bind(&simple_wallet::rpc_payment_info, this, _1), + boost::bind(&simple_wallet::on_command, this, &simple_wallet::rpc_payment_info, _1), tr(USAGE_RPC_PAYMENT_INFO), tr("Get info about RPC payments to current node")); m_cmd_binder.set_handler("start_mining_for_rpc", - boost::bind(&simple_wallet::start_mining_for_rpc, this, _1), + boost::bind(&simple_wallet::on_command, this, &simple_wallet::start_mining_for_rpc, _1), tr(USAGE_START_MINING_FOR_RPC), tr("Start mining to pay for RPC access")); m_cmd_binder.set_handler("stop_mining_for_rpc", - boost::bind(&simple_wallet::stop_mining_for_rpc, this, _1), + boost::bind(&simple_wallet::on_command, this, &simple_wallet::stop_mining_for_rpc, _1), tr(USAGE_STOP_MINING_FOR_RPC), tr("Stop mining to pay for RPC access")); + m_cmd_binder.set_handler("show_qr_code", + boost::bind(&simple_wallet::on_command, this, &simple_wallet::show_qr_code, _1), + tr(USAGE_SHOW_QR_CODE), + tr("Show address as QR code")); m_cmd_binder.set_handler("help_advanced", boost::bind(&simple_wallet::on_command, this, &simple_wallet::help_advanced, _1), tr(USAGE_HELP_ADVANCED), @@ -5461,7 +5539,7 @@ void simple_wallet::on_new_block(uint64_t height, const cryptonote::block& block m_refresh_progress_reporter.update(height, false); } //---------------------------------------------------------------------------------------------------- -void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, uint64_t unlock_time) +void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, bool is_change, uint64_t unlock_time) { if (m_locked) return; @@ -5472,7 +5550,7 @@ void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid, tr("idx ") << subaddr_index; const uint64_t warn_height = m_wallet->nettype() == TESTNET ? 1000000 : m_wallet->nettype() == STAGENET ? 50000 : 1650000; - if (height >= warn_height) + if (height >= warn_height && !is_change) { std::vector<tx_extra_field> tx_extra_fields; parse_tx_extra(tx.extra, tx_extra_fields); // failure ok @@ -5576,14 +5654,19 @@ boost::optional<epee::wipeable_string> simple_wallet::on_device_pin_request() return pwd_container->password(); } //---------------------------------------------------------------------------------------------------- -boost::optional<epee::wipeable_string> simple_wallet::on_device_passphrase_request(bool on_device) +boost::optional<epee::wipeable_string> simple_wallet::on_device_passphrase_request(bool & on_device) { - if (on_device){ - message_writer(console_color_white, true) << tr("Please enter the device passphrase on the device"); - return boost::none; + if (on_device) { + std::string accepted = input_line(tr( + "Device asks for passphrase. Do you want to enter the passphrase on device (Y) (or on the host (N))?")); + if (std::cin.eof() || command_line::is_yes(accepted)) { + message_writer(console_color_white, true) << tr("Please enter the device passphrase on the device"); + return boost::none; + } } PAUSE_READLINE(); + on_device = false; std::string msg = tr("Enter device passphrase"); auto pwd_container = tools::password_container::prompt(false, msg.c_str()); THROW_WALLET_EXCEPTION_IF(!pwd_container, tools::error::password_entry_failed, tr("Failed to read device passphrase")); @@ -5748,15 +5831,19 @@ bool simple_wallet::show_balance_unlocked(bool detailed) success_msg_writer() << tr("Currently selected account: [") << m_current_subaddress_account << tr("] ") << m_wallet->get_subaddress_label({m_current_subaddress_account, 0}); const std::string tag = m_wallet->get_account_tags().second[m_current_subaddress_account]; success_msg_writer() << tr("Tag: ") << (tag.empty() ? std::string{tr("(No tag assigned)")} : tag); - uint64_t blocks_to_unlock; - uint64_t unlocked_balance = m_wallet->unlocked_balance(m_current_subaddress_account, false, &blocks_to_unlock); + uint64_t blocks_to_unlock, time_to_unlock; + uint64_t unlocked_balance = m_wallet->unlocked_balance(m_current_subaddress_account, false, &blocks_to_unlock, &time_to_unlock); std::string unlock_time_message; - if (blocks_to_unlock > 0) + if (blocks_to_unlock > 0 && time_to_unlock > 0) + unlock_time_message = (boost::format(" (%lu block(s) and %s to unlock)") % blocks_to_unlock % get_human_readable_timespan(time_to_unlock)).str(); + else if (blocks_to_unlock > 0) unlock_time_message = (boost::format(" (%lu block(s) to unlock)") % blocks_to_unlock).str(); + else if (time_to_unlock > 0) + unlock_time_message = (boost::format(" (%s to unlock)") % get_human_readable_timespan(time_to_unlock)).str(); success_msg_writer() << tr("Balance: ") << print_money(m_wallet->balance(m_current_subaddress_account, false)) << ", " << tr("unlocked balance: ") << print_money(unlocked_balance) << unlock_time_message << extra; std::map<uint32_t, uint64_t> balance_per_subaddress = m_wallet->balance_per_subaddress(m_current_subaddress_account, false); - std::map<uint32_t, std::pair<uint64_t, uint64_t>> unlocked_balance_per_subaddress = m_wallet->unlocked_balance_per_subaddress(m_current_subaddress_account, false); + std::map<uint32_t, std::pair<uint64_t, std::pair<uint64_t, uint64_t>>> unlocked_balance_per_subaddress = m_wallet->unlocked_balance_per_subaddress(m_current_subaddress_account, false); if (!detailed || balance_per_subaddress.empty()) return true; success_msg_writer() << tr("Balance per address:"); @@ -6221,6 +6308,7 @@ bool simple_wallet::prompt_if_old(const std::vector<tools::wallet2::pending_tx> } return true; } +//---------------------------------------------------------------------------------------------------- void simple_wallet::check_for_inactivity_lock(bool user) { if (m_locked) @@ -6745,7 +6833,7 @@ bool simple_wallet::locked_transfer(const std::vector<std::string> &args_) //---------------------------------------------------------------------------------------------------- bool simple_wallet::locked_sweep_all(const std::vector<std::string> &args_) { - sweep_main(0, true, args_); + sweep_main(m_current_subaddress_account, 0, true, args_); return true; } //---------------------------------------------------------------------------------------------------- @@ -6856,18 +6944,22 @@ bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_) return true; } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<std::string> &args_) +bool simple_wallet::sweep_main(uint32_t account, uint64_t below, bool locked, const std::vector<std::string> &args_) { - auto print_usage = [below]() + auto print_usage = [this, account, below]() { if (below) { PRINT_USAGE(USAGE_SWEEP_BELOW); } - else + else if (account == m_current_subaddress_account) { PRINT_USAGE(USAGE_SWEEP_ALL); } + else + { + PRINT_USAGE(USAGE_SWEEP_ACCOUNT); + } }; if (args_.size() == 0) { @@ -7041,7 +7133,7 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st try { // figure out what tx will be necessary - auto ptx_vector = m_wallet->create_transactions_all(below, info.address, info.is_subaddress, outputs, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices); + auto ptx_vector = m_wallet->create_transactions_all(below, info.address, info.is_subaddress, outputs, fake_outs_count, unlock_block /* unlock_time */, priority, extra, account, subaddr_indices); if (ptx_vector.empty()) { @@ -7381,7 +7473,27 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_) //---------------------------------------------------------------------------------------------------- bool simple_wallet::sweep_all(const std::vector<std::string> &args_) { - sweep_main(0, false, args_); + sweep_main(m_current_subaddress_account, 0, false, args_); + return true; +} +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::sweep_account(const std::vector<std::string> &args_) +{ + auto local_args = args_; + if (local_args.empty()) + { + PRINT_USAGE(USAGE_SWEEP_ACCOUNT); + return true; + } + uint32_t account = 0; + if (!epee::string_tools::get_xtype_from_string(account, local_args[0])) + { + fail_msg_writer() << tr("Invalid account"); + return true; + } + local_args.erase(local_args.begin()); + + sweep_main(account, 0, false, local_args); return true; } //---------------------------------------------------------------------------------------------------- @@ -7398,7 +7510,7 @@ bool simple_wallet::sweep_below(const std::vector<std::string> &args_) fail_msg_writer() << tr("invalid amount threshold"); return true; } - sweep_main(below, false, std::vector<std::string>(++args_.begin(), args_.end())); + sweep_main(m_current_subaddress_account, below, false, std::vector<std::string>(++args_.begin(), args_.end())); return true; } //---------------------------------------------------------------------------------------------------- @@ -8233,6 +8345,11 @@ static std::string get_human_readable_timespan(std::chrono::seconds seconds) return sw::tr("a long time"); } //---------------------------------------------------------------------------------------------------- +static std::string get_human_readable_timespan(uint64_t seconds) +{ + return get_human_readable_timespan(std::chrono::seconds(seconds)); +} +//---------------------------------------------------------------------------------------------------- // mutates local_args as it parses and consumes arguments bool simple_wallet::get_transfers(std::vector<std::string>& local_args, std::vector<transfer_view>& transfers) { @@ -8495,7 +8612,7 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_) std::vector<std::string> local_args = args_; if(local_args.size() > 4) { - fail_msg_writer() << tr("usage: show_transfers [in|out|all|pending|failed|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]]"); + fail_msg_writer() << tr("usage: show_transfers [in|out|all|pending|failed|pool|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]]"); return true; } @@ -8548,7 +8665,7 @@ bool simple_wallet::export_transfers(const std::vector<std::string>& args_) std::vector<std::string> local_args = args_; if(local_args.size() > 5) { - fail_msg_writer() << tr("usage: export_transfers [in|out|all|pending|failed|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]] [output=<path>]"); + fail_msg_writer() << tr("usage: export_transfers [in|out|all|pending|failed|pool|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]] [output=<path>]"); return true; } @@ -10317,6 +10434,14 @@ bool simple_wallet::user_confirms(const std::string &question) return !std::cin.eof() && command_line::is_yes(answer); } +bool simple_wallet::user_confirms_auto_config() +{ + message_writer(console_color_red, true) << tr("WARNING: Using MMS auto-config mechanisms is not trustless"); + message_writer() << tr("A malicious auto-config manager could send you info about own wallets instead of other signers' info"); + message_writer() << tr("If in doubt do not use auto-config or at least compare configs using the \"mms config_checksum\" command"); + return user_confirms("Accept the risks and continue?"); +} + bool simple_wallet::get_number_from_arg(const std::string &arg, uint32_t &number, const uint32_t lower_bound, const uint32_t upper_bound) { bool valid = false; @@ -10469,7 +10594,7 @@ void simple_wallet::show_message(const mms::message &m) case mms::message_type::additional_key_set: case mms::message_type::note: display_content = true; - ms.get_sanitized_message_text(m, sanitized_text); + sanitized_text = mms::message_store::get_sanitized_text(m.content, 1000); break; default: display_content = false; @@ -10818,6 +10943,11 @@ void simple_wallet::mms_next(const std::vector<std::string> &args) { break; } + if (!user_confirms_auto_config()) + { + message_writer() << tr("You can use the \"mms delete\" command to delete any unwanted message"); + break; + } } ms.process_signer_config(state, m.content); ms.stop_auto_config(); @@ -11144,6 +11274,18 @@ void simple_wallet::mms_start_auto_config(const std::vector<std::string> &args) list_signers(ms.get_all_signers()); } +void simple_wallet::mms_config_checksum(const std::vector<std::string> &args) +{ + if (args.size() != 0) + { + fail_msg_writer() << tr("Usage: mms config_checksum"); + return; + } + mms::message_store& ms = m_wallet->get_message_store(); + LOCK_IDLE_SCOPE(); + message_writer() << ms.get_config_checksum(); +} + void simple_wallet::mms_stop_auto_config(const std::vector<std::string> &args) { if (args.size() != 0) @@ -11174,6 +11316,10 @@ void simple_wallet::mms_auto_config(const std::vector<std::string> &args) fail_msg_writer() << tr("Invalid auto-config token"); return; } + if (!user_confirms_auto_config()) + { + return; + } mms::authorized_signer me = ms.get_signer(0); if (me.auto_config_running) { @@ -11286,6 +11432,10 @@ bool simple_wallet::mms(const std::vector<std::string> &args) { mms_start_auto_config(mms_args); } + else if (sub_command == "config_checksum") + { + mms_config_checksum(mms_args); + } else if (sub_command == "stop_auto_config") { mms_stop_auto_config(mms_args); diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 4ba2793e0..5154ff1ef 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -53,7 +53,7 @@ #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "wallet.simplewallet" // Hardcode Monero's donation address (see #1447) -constexpr const char MONERO_DONATION_ADDR[] = "44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A"; +constexpr const char MONERO_DONATION_ADDR[] = "888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H"; /*! * \namespace cryptonote @@ -170,8 +170,9 @@ namespace cryptonote bool transfer(const std::vector<std::string> &args); bool locked_transfer(const std::vector<std::string> &args); bool locked_sweep_all(const std::vector<std::string> &args); - bool sweep_main(uint64_t below, bool locked, const std::vector<std::string> &args); + bool sweep_main(uint32_t account, uint64_t below, bool locked, const std::vector<std::string> &args); bool sweep_all(const std::vector<std::string> &args); + bool sweep_account(const std::vector<std::string> &args); bool sweep_below(const std::vector<std::string> &args); bool sweep_single(const std::vector<std::string> &args); bool sweep_unmixable(const std::vector<std::string> &args); @@ -257,6 +258,7 @@ namespace cryptonote bool rpc_payment_info(const std::vector<std::string> &args); bool start_mining_for_rpc(const std::vector<std::string> &args); bool stop_mining_for_rpc(const std::vector<std::string> &args); + bool show_qr_code(const std::vector<std::string> &args); bool net_stats(const std::vector<std::string>& args); bool public_nodes(const std::vector<std::string>& args); bool welcome(const std::vector<std::string>& args); @@ -341,14 +343,14 @@ namespace cryptonote //----------------- i_wallet2_callback --------------------- virtual void on_new_block(uint64_t height, const cryptonote::block& block); - virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, uint64_t unlock_time); + virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, bool is_change, uint64_t unlock_time); virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index); virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index); virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx); virtual boost::optional<epee::wipeable_string> on_get_password(const char *reason); virtual void on_device_button_request(uint64_t code); virtual boost::optional<epee::wipeable_string> on_device_pin_request(); - virtual boost::optional<epee::wipeable_string> on_device_passphrase_request(bool on_device); + virtual boost::optional<epee::wipeable_string> on_device_passphrase_request(bool & on_device); //---------------------------------------------------------- friend class refresh_progress_reporter_t; @@ -477,6 +479,7 @@ namespace cryptonote void ask_send_all_ready_messages(); void check_for_messages(); bool user_confirms(const std::string &question); + bool user_confirms_auto_config(); bool get_message_from_arg(const std::string &arg, mms::message &m); bool get_number_from_arg(const std::string &arg, uint32_t &number, const uint32_t lower_bound, const uint32_t upper_bound); @@ -497,6 +500,7 @@ namespace cryptonote void mms_help(const std::vector<std::string> &args); void mms_send_signer_config(const std::vector<std::string> &args); void mms_start_auto_config(const std::vector<std::string> &args); + void mms_config_checksum(const std::vector<std::string> &args); void mms_stop_auto_config(const std::vector<std::string> &args); void mms_auto_config(const std::vector<std::string> &args); }; diff --git a/src/version.cpp.in b/src/version.cpp.in index ccb88f1fe..1e720537a 100644 --- a/src/version.cpp.in +++ b/src/version.cpp.in @@ -1,6 +1,6 @@ #define DEF_MONERO_VERSION_TAG "@VERSIONTAG@" -#define DEF_MONERO_VERSION "0.15.0.0" -#define DEF_MONERO_RELEASE_NAME "Carbon Chamaeleon" +#define DEF_MONERO_VERSION "0.16.0.0" +#define DEF_MONERO_RELEASE_NAME "Nitrogen Nebula" #define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG #define DEF_MONERO_VERSION_IS_RELEASE @VERSION_IS_RELEASE@ diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt index a0a166a93..bf238ae37 100644 --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/wallet/api/CMakeLists.txt b/src/wallet/api/CMakeLists.txt index 3376ec70e..30eb4ce03 100644 --- a/src/wallet/api/CMakeLists.txt +++ b/src/wallet/api/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/wallet/api/address_book.cpp b/src/wallet/api/address_book.cpp index 005ddf7ee..f69a69ca3 100644 --- a/src/wallet/api/address_book.cpp +++ b/src/wallet/api/address_book.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/address_book.h b/src/wallet/api/address_book.h index 92e6eaa17..f287969f3 100644 --- a/src/wallet/api/address_book.h +++ b/src/wallet/api/address_book.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp index 52510164a..24f6d37db 100644 --- a/src/wallet/api/pending_transaction.cpp +++ b/src/wallet/api/pending_transaction.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/pending_transaction.h b/src/wallet/api/pending_transaction.h index 92801d77d..bd0ca80a6 100644 --- a/src/wallet/api/pending_transaction.h +++ b/src/wallet/api/pending_transaction.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/subaddress.cpp b/src/wallet/api/subaddress.cpp index 8a1d34864..227bb343d 100644 --- a/src/wallet/api/subaddress.cpp +++ b/src/wallet/api/subaddress.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/subaddress.h b/src/wallet/api/subaddress.h index 87585ec16..18c9ed59e 100644 --- a/src/wallet/api/subaddress.h +++ b/src/wallet/api/subaddress.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/subaddress_account.cpp b/src/wallet/api/subaddress_account.cpp index eaaddc11f..5e502ed5b 100644 --- a/src/wallet/api/subaddress_account.cpp +++ b/src/wallet/api/subaddress_account.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/subaddress_account.h b/src/wallet/api/subaddress_account.h index 358e446d4..1318d4da5 100644 --- a/src/wallet/api/subaddress_account.h +++ b/src/wallet/api/subaddress_account.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/transaction_history.cpp b/src/wallet/api/transaction_history.cpp index ad7029a3c..bcb300889 100644 --- a/src/wallet/api/transaction_history.cpp +++ b/src/wallet/api/transaction_history.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/transaction_history.h b/src/wallet/api/transaction_history.h index 67fe1989d..8f3805788 100644 --- a/src/wallet/api/transaction_history.h +++ b/src/wallet/api/transaction_history.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/transaction_info.cpp b/src/wallet/api/transaction_info.cpp index 21573c6f6..5ae3a6937 100644 --- a/src/wallet/api/transaction_info.cpp +++ b/src/wallet/api/transaction_info.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/transaction_info.h b/src/wallet/api/transaction_info.h index d5c8f31cf..73bb7689d 100644 --- a/src/wallet/api/transaction_info.h +++ b/src/wallet/api/transaction_info.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/unsigned_transaction.cpp b/src/wallet/api/unsigned_transaction.cpp index c2c04cbc3..fb96674a7 100644 --- a/src/wallet/api/unsigned_transaction.cpp +++ b/src/wallet/api/unsigned_transaction.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/unsigned_transaction.h b/src/wallet/api/unsigned_transaction.h index f1af80fa1..81fc098ff 100644 --- a/src/wallet/api/unsigned_transaction.h +++ b/src/wallet/api/unsigned_transaction.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/utils.cpp b/src/wallet/api/utils.cpp index 24252868a..34debee9d 100644 --- a/src/wallet/api/utils.cpp +++ b/src/wallet/api/utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 4612b0397..66e248427 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -157,7 +157,7 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback } } - virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, uint64_t unlock_time) + virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, bool is_change, uint64_t unlock_time) { std::string tx_hash = epee::string_tools::pod_to_hex(txid); @@ -267,13 +267,15 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback return boost::none; } - virtual boost::optional<epee::wipeable_string> on_device_passphrase_request(bool on_device) + virtual boost::optional<epee::wipeable_string> on_device_passphrase_request(bool & on_device) { if (m_listener) { auto passphrase = m_listener->onDevicePassphraseRequest(on_device); - if (!on_device && passphrase) { + if (passphrase) { return boost::make_optional(epee::wipeable_string((*passphrase).data(), (*passphrase).size())); } + } else { + on_device = true; } return boost::none; } diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 66eeb0e73..43d85b704 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index 6309724a4..e2f96a069 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -400,8 +400,8 @@ struct WalletListener /** * @brief called by device when passphrase entry is needed */ - virtual optional<std::string> onDevicePassphraseRequest(bool on_device) { - if (!on_device) throw std::runtime_error("Not supported"); + virtual optional<std::string> onDevicePassphraseRequest(bool & on_device) { + on_device = true; return optional<std::string>(); } diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp index 8d7541cea..69d17eb02 100644 --- a/src/wallet/api/wallet_manager.cpp +++ b/src/wallet/api/wallet_manager.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h index 0595b8327..ca8e9ada2 100644 --- a/src/wallet/api/wallet_manager.h +++ b/src/wallet/api/wallet_manager.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/message_store.cpp b/src/wallet/message_store.cpp index 1bd462ef5..fb07b42f0 100644 --- a/src/wallet/message_store.cpp +++ b/src/wallet/message_store.cpp @@ -39,6 +39,7 @@ #include "serialization/binary_utils.h" #include "common/base58.h" #include "common/util.h" +#include "common/utf8.h" #include "string_tools.h" @@ -129,18 +130,18 @@ void message_store::set_signer(const multisig_wallet_state &state, authorized_signer &m = m_signers[index]; if (label) { - m.label = label.get(); + m.label = get_sanitized_text(label.get(), 50); } if (transport_address) { - m.transport_address = transport_address.get(); + m.transport_address = get_sanitized_text(transport_address.get(), 200); } if (monero_address) { m.monero_address_known = true; m.monero_address = monero_address.get(); } - // Save to minimize the chance to loose that info (at least while in beta) + // Save to minimize the chance to loose that info save(state); } @@ -202,6 +203,13 @@ void message_store::unpack_signer_config(const multisig_wallet_state &state, con } uint32_t num_signers = (uint32_t)signers.size(); THROW_WALLET_EXCEPTION_IF(num_signers != m_num_authorized_signers, tools::error::wallet_internal_error, "Wrong number of signers in config: " + std::to_string(num_signers)); + for (uint32_t i = 0; i < num_signers; ++i) + { + authorized_signer &m = signers[i]; + m.label = get_sanitized_text(m.label, 50); + m.transport_address = get_sanitized_text(m.transport_address, 200); + m.auto_config_token = get_sanitized_text(m.auto_config_token, 20); + } } void message_store::process_signer_config(const multisig_wallet_state &state, const std::string &signer_config) @@ -242,10 +250,10 @@ void message_store::process_signer_config(const multisig_wallet_state &state, co } } authorized_signer &modify = m_signers[take_index]; - modify.label = m.label; // ALWAYS set label, see comments above + modify.label = get_sanitized_text(m.label, 50); // ALWAYS set label, see comments above if (!modify.me) { - modify.transport_address = m.transport_address; + modify.transport_address = get_sanitized_text(m.transport_address, 200); modify.monero_address_known = m.monero_address_known; if (m.monero_address_known) { @@ -392,6 +400,45 @@ void message_store::process_auto_config_data_message(uint32_t id) signer.auto_config_running = false; } +void add_hash(crypto::hash &sum, const crypto::hash &summand) +{ + for (uint32_t i = 0; i < crypto::HASH_SIZE; ++i) + { + uint32_t x = (uint32_t)sum.data[i]; + uint32_t y = (uint32_t)summand.data[i]; + sum.data[i] = (char)((x + y) % 256); + } +} + +// Calculate a checksum that allows signers to make sure they work with an identical signer config +// by exchanging and comparing checksums out-of-band i.e. not using the MMS; +// Because different signers have a different order of signers in the config work with "adding" +// individual hashes because that operation is commutative +std::string message_store::get_config_checksum() const +{ + crypto::hash sum = crypto::null_hash; + uint32_t num = SWAP32LE(m_num_authorized_signers); + add_hash(sum, crypto::cn_fast_hash(&num, sizeof(num))); + num = SWAP32LE(m_num_required_signers); + add_hash(sum, crypto::cn_fast_hash(&num, sizeof(num))); + for (uint32_t i = 0; i < m_num_authorized_signers; ++i) + { + const authorized_signer &m = m_signers[i]; + add_hash(sum, crypto::cn_fast_hash(m.transport_address.data(), m.transport_address.size())); + if (m.monero_address_known) + { + add_hash(sum, crypto::cn_fast_hash(&m.monero_address.m_spend_public_key, sizeof(m.monero_address.m_spend_public_key))); + add_hash(sum, crypto::cn_fast_hash(&m.monero_address.m_view_public_key, sizeof(m.monero_address.m_view_public_key))); + } + } + std::string checksum_bytes; + checksum_bytes += sum.data[0]; + checksum_bytes += sum.data[1]; + checksum_bytes += sum.data[2]; + checksum_bytes += sum.data[3]; + return epee::string_tools::buff_to_hex_nodelimer(checksum_bytes); +} + void message_store::stop_auto_config() { for (uint32_t i = 0; i < m_num_authorized_signers; ++i) @@ -661,32 +708,38 @@ void message_store::delete_all_messages() m_messages.clear(); } -// Make a message text, which is "attacker controlled data", reasonably safe to display +// Make a text, which is "attacker controlled data", reasonably safe to display // This is mostly geared towards the safe display of notes sent by "mms note" with a "mms show" command -void message_store::get_sanitized_message_text(const message &m, std::string &sanitized_text) const +std::string message_store::get_sanitized_text(const std::string &text, size_t max_length) { - sanitized_text.clear(); - // Restrict the size to fend of DOS-style attacks with heaps of data - size_t length = std::min(m.content.length(), (size_t)1000); + size_t length = std::min(text.length(), max_length); + std::string sanitized_text = text.substr(0, length); - for (size_t i = 0; i < length; ++i) + try { - char c = m.content[i]; - if ((int)c < 32) + sanitized_text = tools::utf8canonical(sanitized_text, [](wint_t c) { - // Strip out any controls, especially ESC for getting rid of potentially dangerous - // ANSI escape sequences that a console window might interpret - c = ' '; - } - else if ((c == '<') || (c == '>')) - { - // Make XML or HTML impossible that e.g. might contain scripts that Qt might execute - // when displayed in the GUI wallet - c = ' '; - } - sanitized_text += c; + if ((c < 0x20) || (c == 0x7f) || (c >= 0x80 && c <= 0x9f)) + { + // Strip out any controls, especially ESC for getting rid of potentially dangerous + // ANSI escape sequences that a console window might interpret + c = '?'; + } + else if ((c == '<') || (c == '>')) + { + // Make XML or HTML impossible that e.g. might contain scripts that Qt might execute + // when displayed in the GUI wallet + c = '?'; + } + return c; + }); + } + catch (const std::exception &e) + { + sanitized_text = "(Illegal UTF-8 string)"; } + return sanitized_text; } void message_store::write_to_file(const multisig_wallet_state &state, const std::string &filename) @@ -724,7 +777,7 @@ void message_store::read_from_file(const multisig_wallet_state &state, const std { // Simply do nothing if the file is not there; allows e.g. easy recovery // from problems with the MMS by deleting the file - MERROR("No message store file found: " << filename); + MINFO("No message store file found: " << filename); return; } diff --git a/src/wallet/message_store.h b/src/wallet/message_store.h index d40daf186..9055fd776 100644 --- a/src/wallet/message_store.h +++ b/src/wallet/message_store.h @@ -242,6 +242,7 @@ namespace mms size_t add_auto_config_data_message(const multisig_wallet_state &state, const std::string &auto_config_token); void process_auto_config_data_message(uint32_t id); + std::string get_config_checksum() const; void stop_auto_config(); // Process data just created by "me" i.e. the own local wallet, e.g. as the result of a "prepare_multisig" command @@ -275,7 +276,7 @@ namespace mms void set_message_processed_or_sent(uint32_t id); void delete_message(uint32_t id); void delete_all_messages(); - void get_sanitized_message_text(const message &m, std::string &sanitized_text) const; + static std::string get_sanitized_text(const std::string &text, size_t max_length); void send_message(const multisig_wallet_state &state, uint32_t id); bool check_for_messages(const multisig_wallet_state &state, std::vector<message> &messages); diff --git a/src/wallet/message_transporter.h b/src/wallet/message_transporter.h index 84a2e9bae..557833f2c 100644 --- a/src/wallet/message_transporter.h +++ b/src/wallet/message_transporter.h @@ -31,7 +31,6 @@ #include "cryptonote_basic/cryptonote_basic.h" #include "cryptonote_basic/cryptonote_boost_serialization.h" #include "cryptonote_basic/account_boost_serialization.h" -#include "cryptonote_basic/cryptonote_basic.h" #include "net/http_server_impl_base.h" #include "net/http_client.h" #include "net/abstract_http_client.h" diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp index 873c2ee51..95b8ce8bb 100644 --- a/src/wallet/node_rpc_proxy.cpp +++ b/src/wallet/node_rpc_proxy.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/node_rpc_proxy.h b/src/wallet/node_rpc_proxy.h index b053659e9..500ba81d4 100644 --- a/src/wallet/node_rpc_proxy.h +++ b/src/wallet/node_rpc_proxy.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 4220f18be..d7ed3e999 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -947,7 +947,7 @@ uint32_t get_subaddress_clamped_sum(uint32_t idx, uint32_t extra) static void setup_shim(hw::wallet_shim * shim, tools::wallet2 * wallet) { - shim->get_tx_pub_key_from_received_outs = boost::bind(&tools::wallet2::get_tx_pub_key_from_received_outs, wallet, _1); + shim->get_tx_pub_key_from_received_outs = std::bind(&tools::wallet2::get_tx_pub_key_from_received_outs, wallet, std::placeholders::_1); } bool get_pruned_tx(const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::entry &entry, cryptonote::transaction &tx, crypto::hash &tx_hash) @@ -1109,10 +1109,12 @@ boost::optional<epee::wipeable_string> wallet_device_callback::on_pin_request() return boost::none; } -boost::optional<epee::wipeable_string> wallet_device_callback::on_passphrase_request(bool on_device) +boost::optional<epee::wipeable_string> wallet_device_callback::on_passphrase_request(bool & on_device) { if (wallet) return wallet->on_device_passphrase_request(on_device); + else + on_device = true; return boost::none; } @@ -1521,6 +1523,18 @@ void wallet2::add_subaddress(uint32_t index_major, const std::string& label) m_subaddress_labels[index_major][index_minor] = label; } //---------------------------------------------------------------------------------------------------- +bool wallet2::should_expand(const cryptonote::subaddress_index &index) const +{ + const uint32_t last_major = m_subaddress_labels.size() - 1 > (std::numeric_limits<uint32_t>::max() - m_subaddress_lookahead_major) ? std::numeric_limits<uint32_t>::max() : (m_subaddress_labels.size() + m_subaddress_lookahead_major - 1); + if (index.major > last_major) + return false; + const size_t nsub = index.major < m_subaddress_labels.size() ? m_subaddress_labels[index.major].size() : 0; + const uint32_t last_minor = nsub - 1 > (std::numeric_limits<uint32_t>::max() - m_subaddress_lookahead_minor) ? std::numeric_limits<uint32_t>::max() : (nsub + m_subaddress_lookahead_minor - 1); + if (index.minor > last_minor) + return false; + return true; +} +//---------------------------------------------------------------------------------------------------- void wallet2::expand_subaddresses(const cryptonote::subaddress_index& index) { hw::device &hwdev = m_account.get_device(); @@ -1853,6 +1867,20 @@ void wallet2::cache_tx_data(const cryptonote::transaction& tx, const crypto::has } } //---------------------------------------------------------------------------------------------------- +bool wallet2::spends_one_of_ours(const cryptonote::transaction &tx) const +{ + for (const auto &in: tx.vin) + { + if (in.type() != typeid(cryptonote::txin_to_key)) + continue; + const cryptonote::txin_to_key &in_to_key = boost::get<cryptonote::txin_to_key>(in); + auto it = m_key_images.find(in_to_key.k_image); + if (it != m_key_images.end()) + return true; + } + return false; +} +//---------------------------------------------------------------------------------------------------- void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint8_t block_version, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen, const tx_cache_data &tx_cache_data, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache) { PERF_TIMER(process_new_transaction); @@ -2106,7 +2134,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote td.m_amount = amount; td.m_pk_index = pk_index - 1; td.m_subaddr_index = tx_scan_info[o].received->index; - if (tx_scan_info[o].received->index.major < m_subaddress_labels.size() && tx_scan_info[o].received->index.minor < m_subaddress_labels[tx_scan_info[o].received->index.major].size()) + if (should_expand(tx_scan_info[o].received->index)) expand_subaddresses(tx_scan_info[o].received->index); if (tx.vout[o].amount == 0) { @@ -2139,7 +2167,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote } LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid); if (0 != m_callback) - m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index, td.m_tx.unlock_time); + m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index, spends_one_of_ours(tx), td.m_tx.unlock_time); } total_received_1 += amount; notify = true; @@ -2185,7 +2213,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote td.m_amount = amount; td.m_pk_index = pk_index - 1; td.m_subaddr_index = tx_scan_info[o].received->index; - if (tx_scan_info[o].received->index.major < m_subaddress_labels.size() && tx_scan_info[o].received->index.minor < m_subaddress_labels[tx_scan_info[o].received->index.major].size()) + if (should_expand(tx_scan_info[o].received->index)) expand_subaddresses(tx_scan_info[o].received->index); if (tx.vout[o].amount == 0) { @@ -2216,7 +2244,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid); if (0 != m_callback) - m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index, td.m_tx.unlock_time); + m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index, spends_one_of_ours(tx), td.m_tx.unlock_time); } total_received_1 += extra_amount; notify = true; @@ -3976,13 +4004,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ // Load keys from buffer boost::optional<crypto::chacha_key> keys_to_encrypt; - try { - r = wallet2::load_keys_buf(keys_file_buf, password, keys_to_encrypt); - } catch (const std::exception& e) { - std::size_t found = string(e.what()).find("failed to deserialize keys buffer"); - THROW_WALLET_EXCEPTION_IF(found != std::string::npos, error::wallet_internal_error, "internal error: failed to deserialize \"" + keys_file_name + '\"'); - throw e; - } + r = wallet2::load_keys_buf(keys_file_buf, password, keys_to_encrypt); // Rewrite with encrypted keys if unencrypted, ignore errors if (r && keys_to_encrypt != boost::none) @@ -4846,6 +4868,7 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password, std::vector<crypto::secret_key> multisig_keys; rct::key spend_pkey = rct::identity(); rct::key spend_skey; + auto wiper = epee::misc_utils::create_scope_leave_handler([&](){memwipe(&spend_skey, sizeof(spend_skey));}); std::vector<crypto::public_key> multisig_signers; // decrypt keys @@ -5491,13 +5514,12 @@ bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout) cryptonote::COMMAND_RPC_GET_VERSION::request req_t = AUTO_VAL_INIT(req_t); cryptonote::COMMAND_RPC_GET_VERSION::response resp_t = AUTO_VAL_INIT(resp_t); bool r = invoke_http_json_rpc("/json_rpc", "get_version", req_t, resp_t); - if(!r) { + if(!r || resp_t.status != CORE_RPC_STATUS_OK) { if(version) *version = 0; return false; } - if (resp_t.status == CORE_RPC_STATUS_OK) - m_rpc_version = resp_t.version; + m_rpc_version = resp_t.version; } if (version) *version = m_rpc_version; @@ -5910,18 +5932,22 @@ uint64_t wallet2::balance(uint32_t index_major, bool strict) const return amount; } //---------------------------------------------------------------------------------------------------- -uint64_t wallet2::unlocked_balance(uint32_t index_major, bool strict, uint64_t *blocks_to_unlock) const +uint64_t wallet2::unlocked_balance(uint32_t index_major, bool strict, uint64_t *blocks_to_unlock, uint64_t *time_to_unlock) const { uint64_t amount = 0; if (blocks_to_unlock) *blocks_to_unlock = 0; + if (time_to_unlock) + *time_to_unlock = 0; if(m_light_wallet) return m_light_wallet_balance; for (const auto& i : unlocked_balance_per_subaddress(index_major, strict)) { amount += i.second.first; - if (blocks_to_unlock && i.second.second > *blocks_to_unlock) - *blocks_to_unlock = i.second.second; + if (blocks_to_unlock && i.second.second.first > *blocks_to_unlock) + *blocks_to_unlock = i.second.second.first; + if (time_to_unlock && i.second.second.second > *time_to_unlock) + *time_to_unlock = i.second.second.second; } return amount; } @@ -5958,35 +5984,40 @@ std::map<uint32_t, uint64_t> wallet2::balance_per_subaddress(uint32_t index_majo return amount_per_subaddr; } //---------------------------------------------------------------------------------------------------- -std::map<uint32_t, std::pair<uint64_t, uint64_t>> wallet2::unlocked_balance_per_subaddress(uint32_t index_major, bool strict) const +std::map<uint32_t, std::pair<uint64_t, std::pair<uint64_t, uint64_t>>> wallet2::unlocked_balance_per_subaddress(uint32_t index_major, bool strict) const { - std::map<uint32_t, std::pair<uint64_t, uint64_t>> amount_per_subaddr; + std::map<uint32_t, std::pair<uint64_t, std::pair<uint64_t, uint64_t>>> amount_per_subaddr; const uint64_t blockchain_height = get_blockchain_current_height(); + const uint64_t now = time(NULL); for(const transfer_details& td: m_transfers) { if(td.m_subaddr_index.major == index_major && !is_spent(td, strict) && !td.m_frozen) { - uint64_t amount = 0, blocks_to_unlock = 0; + uint64_t amount = 0, blocks_to_unlock = 0, time_to_unlock = 0; if (is_transfer_unlocked(td)) { amount = td.amount(); blocks_to_unlock = 0; + time_to_unlock = 0; } else { uint64_t unlock_height = td.m_block_height + std::max<uint64_t>(CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE, CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS); if (td.m_tx.unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER && td.m_tx.unlock_time > unlock_height) unlock_height = td.m_tx.unlock_time; + uint64_t unlock_time = td.m_tx.unlock_time >= CRYPTONOTE_MAX_BLOCK_NUMBER ? td.m_tx.unlock_time : 0; blocks_to_unlock = unlock_height > blockchain_height ? unlock_height - blockchain_height : 0; + time_to_unlock = unlock_time > now ? unlock_time - now : 0; amount = 0; } auto found = amount_per_subaddr.find(td.m_subaddr_index.minor); if (found == amount_per_subaddr.end()) - amount_per_subaddr[td.m_subaddr_index.minor] = std::make_pair(amount, blocks_to_unlock); + amount_per_subaddr[td.m_subaddr_index.minor] = std::make_pair(amount, std::make_pair(blocks_to_unlock, time_to_unlock)); else { found->second.first += amount; - found->second.second = std::max(found->second.second, blocks_to_unlock); + found->second.second.first = std::max(found->second.second.first, blocks_to_unlock); + found->second.second.second = std::max(found->second.second.second, time_to_unlock); } } } @@ -6001,17 +6032,21 @@ uint64_t wallet2::balance_all(bool strict) const return r; } //---------------------------------------------------------------------------------------------------- -uint64_t wallet2::unlocked_balance_all(bool strict, uint64_t *blocks_to_unlock) const +uint64_t wallet2::unlocked_balance_all(bool strict, uint64_t *blocks_to_unlock, uint64_t *time_to_unlock) const { uint64_t r = 0; if (blocks_to_unlock) *blocks_to_unlock = 0; + if (time_to_unlock) + *time_to_unlock = 0; for (uint32_t index_major = 0; index_major < get_num_subaddress_accounts(); ++index_major) { - uint64_t local_blocks_to_unlock; - r += unlocked_balance(index_major, strict, blocks_to_unlock ? &local_blocks_to_unlock : NULL); + uint64_t local_blocks_to_unlock, local_time_to_unlock; + r += unlocked_balance(index_major, strict, blocks_to_unlock ? &local_blocks_to_unlock : NULL, time_to_unlock ? &local_time_to_unlock : NULL); if (blocks_to_unlock) *blocks_to_unlock = std::max(*blocks_to_unlock, local_blocks_to_unlock); + if (time_to_unlock) + *time_to_unlock = std::max(*time_to_unlock, local_time_to_unlock); } return r; } @@ -6490,7 +6525,7 @@ void wallet2::commit_tx(pending_tx& ptx) // tx generated, get rid of used k values for (size_t idx: ptx.selected_transfers) - m_transfers[idx].m_multisig_k.clear(); + memwipe(m_transfers[idx].m_multisig_k.data(), m_transfers[idx].m_multisig_k.size() * sizeof(m_transfers[idx].m_multisig_k[0])); //fee includes dust if dust policy specified it. LOG_PRINT_L1("Transaction successfully sent. <" << txid << ">" << ENDL @@ -6932,13 +6967,13 @@ std::string wallet2::save_multisig_tx(multisig_tx_set txs) // txes generated, get rid of used k values for (size_t n = 0; n < txs.m_ptx.size(); ++n) for (size_t idx: txs.m_ptx[n].construction_data.selected_transfers) - m_transfers[idx].m_multisig_k.clear(); + memwipe(m_transfers[idx].m_multisig_k.data(), m_transfers[idx].m_multisig_k.size() * sizeof(m_transfers[idx].m_multisig_k[0])); // zero out some data we don't want to share for (auto &ptx: txs.m_ptx) { for (auto &e: ptx.construction_data.sources) - e.multisig_kLRki.k = rct::zero(); + memwipe(&e.multisig_kLRki.k, sizeof(e.multisig_kLRki.k)); } for (auto &ptx: txs.m_ptx) @@ -7146,10 +7181,12 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto ptx.tx.rct_signatures = sig.sigs; rct::keyV k; + rct::key skey = rct::zero(); + auto wiper = epee::misc_utils::create_scope_leave_handler([&](){ memwipe(k.data(), k.size() * sizeof(k[0])); memwipe(&skey, sizeof(skey)); }); + for (size_t idx: sd.selected_transfers) k.push_back(get_multisig_k(idx, sig.used_L)); - rct::key skey = rct::zero(); for (const auto &msk: get_account().get_multisig_keys()) { crypto::public_key pmsk = get_multisig_signing_public_key(msk); @@ -7197,7 +7234,7 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto // txes generated, get rid of used k values for (size_t n = 0; n < exported_txs.m_ptx.size(); ++n) for (size_t idx: exported_txs.m_ptx[n].construction_data.selected_transfers) - m_transfers[idx].m_multisig_k.clear(); + memwipe(m_transfers[idx].m_multisig_k.data(), m_transfers[idx].m_multisig_k.size() * sizeof(m_transfers[idx].m_multisig_k[0])); exported_txs.m_signers.insert(get_multisig_signer_public_key()); @@ -8995,7 +9032,7 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui MDEBUG("Ignoring output " << j << " of amount " << print_money(td2.amount()) << " which is outside prescribed range [" << print_money(m_ignore_outputs_below) << ", " << print_money(m_ignore_outputs_above) << "]"); continue; } - if (!is_spent(td2, false) && !td2.m_frozen && !td.m_key_image_partial && td2.is_rct() && td.amount() + td2.amount() >= needed_money && is_transfer_unlocked(td2) && td2.m_subaddr_index == td.m_subaddr_index) + if (!is_spent(td2, false) && !td2.m_frozen && !td2.m_key_image_partial && td2.is_rct() && td.amount() + td2.amount() >= needed_money && is_transfer_unlocked(td2) && td2.m_subaddr_index == td.m_subaddr_index) { // update our picks if those outputs are less related than any we // already found. If the same, don't update, and oldest suitable outputs @@ -9650,7 +9687,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp // throw if attempting a transaction with no money THROW_WALLET_EXCEPTION_IF(needed_money == 0, error::zero_destination); - std::map<uint32_t, std::pair<uint64_t, uint64_t>> unlocked_balance_per_subaddr = unlocked_balance_per_subaddress(subaddr_account, false); + std::map<uint32_t, std::pair<uint64_t, std::pair<uint64_t, uint64_t>>> unlocked_balance_per_subaddr = unlocked_balance_per_subaddress(subaddr_account, false); std::map<uint32_t, uint64_t> balance_per_subaddr = balance_per_subaddress(subaddr_account, false); if (subaddr_indices.empty()) // "index=<N1>[,<N2>,...]" wasn't specified -> use all the indices with non-zero unlocked balance @@ -12755,7 +12792,7 @@ process: const crypto::public_key& out_key = boost::get<cryptonote::txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key; bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, out_key, tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, in_ephemeral, td.m_key_image, m_account.get_device()); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image"); - if (td.m_subaddr_index.major < m_subaddress_labels.size() && td.m_subaddr_index.minor < m_subaddress_labels[td.m_subaddr_index.major].size()) + if (should_expand(td.m_subaddr_index)) expand_subaddresses(td.m_subaddr_index); td.m_key_image_known = true; td.m_key_image_request = true; @@ -12948,7 +12985,7 @@ cryptonote::blobdata wallet2::export_multisig() { transfer_details &td = m_transfers[n]; crypto::key_image ki; - td.m_multisig_k.clear(); + memwipe(td.m_multisig_k.data(), td.m_multisig_k.size() * sizeof(td.m_multisig_k[0])); info[n].m_LR.clear(); info[n].m_partial_key_images.clear(); @@ -13057,6 +13094,7 @@ size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs) CHECK_AND_ASSERT_THROW_MES(info.size() + 1 <= m_multisig_signers.size() && info.size() + 1 >= m_multisig_threshold, "Wrong number of multisig sources"); std::vector<std::vector<rct::key>> k; + auto wiper = epee::misc_utils::create_scope_leave_handler([&](){for (auto &v: k) memwipe(v.data(), v.size() * sizeof(v[0]));}); k.reserve(m_transfers.size()); for (const auto &td: m_transfers) k.push_back(td.m_multisig_k); @@ -13618,10 +13656,12 @@ boost::optional<epee::wipeable_string> wallet2::on_device_pin_request() return boost::none; } //---------------------------------------------------------------------------------------------------- -boost::optional<epee::wipeable_string> wallet2::on_device_passphrase_request(bool on_device) +boost::optional<epee::wipeable_string> wallet2::on_device_passphrase_request(bool & on_device) { if (nullptr != m_callback) return m_callback->on_device_passphrase_request(on_device); + else + on_device = true; return boost::none; } //---------------------------------------------------------------------------------------------------- diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 1c3c00152..712f91613 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -131,7 +131,7 @@ private: public: // Full wallet callbacks virtual void on_new_block(uint64_t height, const cryptonote::block& block) {} - virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, uint64_t unlock_time) {} + virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, bool is_change, uint64_t unlock_time) {} virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index) {} virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index) {} virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx) {} @@ -145,7 +145,7 @@ private: virtual void on_device_button_request(uint64_t code) {} virtual void on_device_button_pressed() {} virtual boost::optional<epee::wipeable_string> on_device_pin_request() { return boost::none; } - virtual boost::optional<epee::wipeable_string> on_device_passphrase_request(bool on_device) { return boost::none; } + virtual boost::optional<epee::wipeable_string> on_device_passphrase_request(bool & on_device) { on_device = true; return boost::none; } virtual void on_device_progress(const hw::device_progress& event) {}; // Common callbacks virtual void on_pool_tx_removed(const crypto::hash &txid) {} @@ -159,7 +159,7 @@ private: void on_button_request(uint64_t code=0) override; void on_button_pressed() override; boost::optional<epee::wipeable_string> on_pin_request() override; - boost::optional<epee::wipeable_string> on_passphrase_request(bool on_device) override; + boost::optional<epee::wipeable_string> on_passphrase_request(bool & on_device) override; void on_progress(const hw::device_progress& event) override; private: wallet2 * wallet; @@ -835,13 +835,13 @@ private: // locked & unlocked balance of given or current subaddress account uint64_t balance(uint32_t subaddr_index_major, bool strict) const; - uint64_t unlocked_balance(uint32_t subaddr_index_major, bool strict, uint64_t *blocks_to_unlock = NULL) const; + uint64_t unlocked_balance(uint32_t subaddr_index_major, bool strict, uint64_t *blocks_to_unlock = NULL, uint64_t *time_to_unlock = NULL) const; // locked & unlocked balance per subaddress of given or current subaddress account std::map<uint32_t, uint64_t> balance_per_subaddress(uint32_t subaddr_index_major, bool strict) const; - std::map<uint32_t, std::pair<uint64_t, uint64_t>> unlocked_balance_per_subaddress(uint32_t subaddr_index_major, bool strict) const; + std::map<uint32_t, std::pair<uint64_t, std::pair<uint64_t, uint64_t>>> unlocked_balance_per_subaddress(uint32_t subaddr_index_major, bool strict) const; // all locked & unlocked balances of all subaddress accounts uint64_t balance_all(bool strict) const; - uint64_t unlocked_balance_all(bool strict, uint64_t *blocks_to_unlock = NULL) const; + uint64_t unlocked_balance_all(bool strict, uint64_t *blocks_to_unlock = NULL, uint64_t *time_to_unlock = NULL) const; template<typename T> void transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count, std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, @@ -1507,7 +1507,7 @@ private: void on_device_button_request(uint64_t code); void on_device_button_pressed(); boost::optional<epee::wipeable_string> on_device_pin_request(); - boost::optional<epee::wipeable_string> on_device_passphrase_request(bool on_device); + boost::optional<epee::wipeable_string> on_device_passphrase_request(bool & on_device); void on_device_progress(const hw::device_progress& event); std::string get_rpc_status(const std::string &s) const; @@ -1516,6 +1516,9 @@ private: std::string get_client_signature() const; void check_rpc_cost(const char *call, uint64_t post_call_credits, uint64_t pre_credits, double expected_cost); + bool should_expand(const cryptonote::subaddress_index &index) const; + bool spends_one_of_ours(const cryptonote::transaction &tx) const; + cryptonote::account_base m_account; boost::optional<epee::net_utils::http::login> m_daemon_login; std::string m_daemon_address; diff --git a/src/wallet/wallet_args.cpp b/src/wallet/wallet_args.cpp index 350f016c7..55058bf4e 100644 --- a/src/wallet/wallet_args.cpp +++ b/src/wallet/wallet_args.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/wallet_args.h b/src/wallet/wallet_args.h index 59529662f..4af1b58fe 100644 --- a/src/wallet/wallet_args.h +++ b/src/wallet/wallet_args.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index 3e94e604a..e889ed7d1 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/wallet_rpc_helpers.h b/src/wallet/wallet_rpc_helpers.h index 4291a112d..35714db03 100644 --- a/src/wallet/wallet_rpc_helpers.h +++ b/src/wallet/wallet_rpc_helpers.h @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2019, The Monero Project +// Copyright (c) 2018-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/wallet_rpc_payments.cpp b/src/wallet/wallet_rpc_payments.cpp index 4f5364269..6527b1384 100644 --- a/src/wallet/wallet_rpc_payments.cpp +++ b/src/wallet/wallet_rpc_payments.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2019, The Monero Project +// Copyright (c) 2018-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index db2e2344b..fcaaf7616 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -428,10 +428,10 @@ namespace tools try { res.balance = req.all_accounts ? m_wallet->balance_all(req.strict) : m_wallet->balance(req.account_index, req.strict); - res.unlocked_balance = req.all_accounts ? m_wallet->unlocked_balance_all(req.strict, &res.blocks_to_unlock) : m_wallet->unlocked_balance(req.account_index, req.strict, &res.blocks_to_unlock); + res.unlocked_balance = req.all_accounts ? m_wallet->unlocked_balance_all(req.strict, &res.blocks_to_unlock, &res.time_to_unlock) : m_wallet->unlocked_balance(req.account_index, req.strict, &res.blocks_to_unlock, &res.time_to_unlock); res.multisig_import_needed = m_wallet->multisig() && m_wallet->has_multisig_partial_key_images(); std::map<uint32_t, std::map<uint32_t, uint64_t>> balance_per_subaddress_per_account; - std::map<uint32_t, std::map<uint32_t, std::pair<uint64_t, uint64_t>>> unlocked_balance_per_subaddress_per_account; + std::map<uint32_t, std::map<uint32_t, std::pair<uint64_t, std::pair<uint64_t, uint64_t>>>> unlocked_balance_per_subaddress_per_account; if (req.all_accounts) { for (uint32_t account_index = 0; account_index < m_wallet->get_num_subaddress_accounts(); ++account_index) @@ -451,7 +451,7 @@ namespace tools { uint32_t account_index = p.first; std::map<uint32_t, uint64_t> balance_per_subaddress = p.second; - std::map<uint32_t, std::pair<uint64_t, uint64_t>> unlocked_balance_per_subaddress = unlocked_balance_per_subaddress_per_account[account_index]; + std::map<uint32_t, std::pair<uint64_t, std::pair<uint64_t, uint64_t>>> unlocked_balance_per_subaddress = unlocked_balance_per_subaddress_per_account[account_index]; std::set<uint32_t> address_indices; if (!req.all_accounts && !req.address_indices.empty()) { @@ -471,7 +471,8 @@ namespace tools info.address = m_wallet->get_subaddress_as_str(index); info.balance = balance_per_subaddress[i]; info.unlocked_balance = unlocked_balance_per_subaddress[i].first; - info.blocks_to_unlock = unlocked_balance_per_subaddress[i].second; + info.blocks_to_unlock = unlocked_balance_per_subaddress[i].second.first; + info.time_to_unlock = unlocked_balance_per_subaddress[i].second.second; info.label = m_wallet->get_subaddress_label(index); info.num_unspent_outputs = std::count_if(transfers.begin(), transfers.end(), [&](const tools::wallet2::transfer_details& td) { return !td.m_spent && td.m_subaddr_index == index; }); res.per_subaddress.emplace_back(std::move(info)); diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 89bf3a924..6e39eca1e 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index a212b79e6..ae861d177 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -47,7 +47,7 @@ // advance which version they will stop working with // Don't go over 32767 for any of these #define WALLET_RPC_VERSION_MAJOR 1 -#define WALLET_RPC_VERSION_MINOR 17 +#define WALLET_RPC_VERSION_MINOR 18 #define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR) namespace tools @@ -84,6 +84,7 @@ namespace wallet_rpc std::string label; uint64_t num_unspent_outputs; uint64_t blocks_to_unlock; + uint64_t time_to_unlock; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(account_index) @@ -94,6 +95,7 @@ namespace wallet_rpc KV_SERIALIZE(label) KV_SERIALIZE(num_unspent_outputs) KV_SERIALIZE(blocks_to_unlock) + KV_SERIALIZE(time_to_unlock) END_KV_SERIALIZE_MAP() }; @@ -104,6 +106,7 @@ namespace wallet_rpc bool multisig_import_needed; std::vector<per_subaddress_info> per_subaddress; uint64_t blocks_to_unlock; + uint64_t time_to_unlock; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(balance) @@ -111,6 +114,7 @@ namespace wallet_rpc KV_SERIALIZE(multisig_import_needed) KV_SERIALIZE(per_subaddress) KV_SERIALIZE(blocks_to_unlock) + KV_SERIALIZE(time_to_unlock) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h index 9434fbc3e..9b455af6a 100644 --- a/src/wallet/wallet_rpc_server_error_codes.h +++ b/src/wallet/wallet_rpc_server_error_codes.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // |