diff options
Diffstat (limited to 'src')
93 files changed, 1219 insertions, 623 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9904c5de7..aaaae3a09 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -77,30 +77,7 @@ function (monero_add_executable name) enable_stack_trace("${name}") monero_set_target_no_relink("${name}") -endfunction () - -function (monero_add_library name) - monero_add_library_with_deps(NAME "${name}" SOURCES ${ARGN}) -endfunction() - -function (monero_add_library_with_deps) - cmake_parse_arguments(MONERO_ADD_LIBRARY "" "NAME" "DEPENDS;SOURCES" ${ARGN}) - source_group("${MONERO_ADD_LIBRARY_NAME}" FILES ${MONERO_ADD_LIBRARY_SOURCES}) - - # Define a ("virtual") object library and an actual library that links those - # objects together. The virtual libraries can be arbitrarily combined to link - # any subset of objects into one library archive. This is used for releasing - # libwallet, which combines multiple components. - set(objlib obj_${MONERO_ADD_LIBRARY_NAME}) - add_library(${objlib} OBJECT ${MONERO_ADD_LIBRARY_SOURCES}) - add_library("${MONERO_ADD_LIBRARY_NAME}" $<TARGET_OBJECTS:${objlib}>) - monero_set_target_no_relink("${MONERO_ADD_LIBRARY_NAME}") - if (MONERO_ADD_LIBRARY_DEPENDS) - add_dependencies(${objlib} ${MONERO_ADD_LIBRARY_DEPENDS}) - endif() - set_property(TARGET "${MONERO_ADD_LIBRARY_NAME}" PROPERTY FOLDER "libs") - target_compile_definitions(${objlib} - PRIVATE $<TARGET_PROPERTY:${MONERO_ADD_LIBRARY_NAME},INTERFACE_COMPILE_DEFINITIONS>) + monero_set_target_strip ("${name}") endfunction () include(Version) diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp index 5e12fa8ec..a84a4148d 100644 --- a/src/blockchain_db/blockchain_db.cpp +++ b/src/blockchain_db/blockchain_db.cpp @@ -216,15 +216,8 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const std::pair } else { - LOG_PRINT_L1("Unsupported input type, removing key images and aborting transaction addition"); - for (const txin_v& tx_input : tx.vin) - { - if (tx_input.type() == typeid(txin_to_key)) - { - remove_spent_key(boost::get<txin_to_key>(tx_input).k_image); - } - } - return; + LOG_PRINT_L1("Unsupported input type, aborting transaction addition"); + throw std::runtime_error("Unexpected input type, aborting"); } } diff --git a/src/blockchain_utilities/blockchain_ancestry.cpp b/src/blockchain_utilities/blockchain_ancestry.cpp index b1b238427..99a84606d 100644 --- a/src/blockchain_utilities/blockchain_ancestry.cpp +++ b/src/blockchain_utilities/blockchain_ancestry.cpp @@ -32,6 +32,7 @@ #include <boost/algorithm/string.hpp> #include <boost/archive/portable_binary_iarchive.hpp> #include <boost/archive/portable_binary_oarchive.hpp> +#include <boost/filesystem/path.hpp> #include "common/unordered_containers_boost_serialization.h" #include "common/command_line.h" #include "common/varint.h" diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index e439895bf..d53251fd3 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -28,6 +28,7 @@ #include <boost/range/adaptor/transformed.hpp> #include <boost/algorithm/string.hpp> +#include <boost/filesystem.hpp> #include "common/unordered_containers_boost_serialization.h" #include "common/command_line.h" #include "common/varint.h" @@ -387,9 +388,7 @@ static bool for_all_transactions(const std::string &filename, uint64_t &start_id cryptonote::transaction_prefix tx; blobdata bd; bd.assign(reinterpret_cast<char*>(v.mv_data), v.mv_size); - std::stringstream ss; - ss << bd; - binary_archive<false> ba(ss); + binary_archive<false> ba{epee::strspan<std::uint8_t>(bd)}; bool r = do_serialize(ba, tx); CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob"); diff --git a/src/blockchain_utilities/blockchain_depth.cpp b/src/blockchain_utilities/blockchain_depth.cpp index 6199586bf..8c3c3a009 100644 --- a/src/blockchain_utilities/blockchain_depth.cpp +++ b/src/blockchain_utilities/blockchain_depth.cpp @@ -27,6 +27,7 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <boost/range/adaptor/transformed.hpp> +#include <boost/filesystem/path.hpp> #include <boost/algorithm/string.hpp> #include "common/command_line.h" #include "common/varint.h" diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp index df2662444..8d81ef54d 100644 --- a/src/blockchain_utilities/blockchain_import.cpp +++ b/src/blockchain_utilities/blockchain_import.cpp @@ -146,7 +146,7 @@ int check_flush(cryptonote::core &core, std::vector<block_complete_entry> &block if (!parse_and_validate_block_from_blob(b.block, block)) { MERROR("Failed to parse block: " - << epee::string_tools::pod_to_hex(get_blob_hash(b.block))); + << epee::string_tools::buff_to_hex_nodelimer(b.block)); core.cleanup_handle_incoming_blocks(); return 1; } @@ -177,8 +177,11 @@ int check_flush(cryptonote::core &core, std::vector<block_complete_entry> &block core.handle_incoming_tx(tx_blob, tvc, relay_method::block, true); if(tvc.m_verifivation_failed) { - MERROR("transaction verification failed, tx_id = " - << epee::string_tools::pod_to_hex(get_blob_hash(tx_blob.blob))); + cryptonote::transaction transaction; + if (cryptonote::parse_and_validate_tx_from_blob(tx_blob.blob, transaction)) + MERROR("Transaction verification failed, tx_id = " << cryptonote::get_transaction_hash(transaction)); + else + MERROR("Transaction verification failed, transaction is unparsable"); core.cleanup_handle_incoming_blocks(); return 1; } @@ -192,8 +195,11 @@ int check_flush(cryptonote::core &core, std::vector<block_complete_entry> &block if(bvc.m_verifivation_failed) { - MERROR("Block verification failed, id = " - << epee::string_tools::pod_to_hex(get_blob_hash(block_entry.block))); + cryptonote::block block; + if (cryptonote::parse_and_validate_block_from_blob(block_entry.block, block)) + MERROR("Block verification failed, id = " << cryptonote::get_block_hash(block)); + else + MERROR("Block verification failed, block is unparsable"); core.cleanup_handle_incoming_blocks(); return 1; } diff --git a/src/blockchain_utilities/blockchain_prune.cpp b/src/blockchain_utilities/blockchain_prune.cpp index 9a9d58c46..b1c599f3a 100644 --- a/src/blockchain_utilities/blockchain_prune.cpp +++ b/src/blockchain_utilities/blockchain_prune.cpp @@ -29,6 +29,8 @@ #include <array> #include <lmdb.h> #include <boost/algorithm/string.hpp> +#include <boost/system/error_code.hpp> +#include <boost/filesystem.hpp> #include "common/command_line.h" #include "common/pruning.h" #include "cryptonote_core/cryptonote_core.h" diff --git a/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp b/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp index 1a54778a7..78a662134 100644 --- a/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp +++ b/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp @@ -27,6 +27,7 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <boost/algorithm/string.hpp> +#include <boost/filesystem.hpp> #include "common/command_line.h" #include "serialization/crypto.h" #include "cryptonote_core/tx_pool.h" diff --git a/src/blockchain_utilities/blockchain_stats.cpp b/src/blockchain_utilities/blockchain_stats.cpp index 1f728b4e5..5f5ca6abf 100644 --- a/src/blockchain_utilities/blockchain_stats.cpp +++ b/src/blockchain_utilities/blockchain_stats.cpp @@ -27,6 +27,7 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <boost/algorithm/string.hpp> +#include <boost/filesystem.hpp> #include "common/command_line.h" #include "common/varint.h" #include "cryptonote_basic/cryptonote_boost_serialization.h" diff --git a/src/blockchain_utilities/blockchain_usage.cpp b/src/blockchain_utilities/blockchain_usage.cpp index 095e6c503..8356ef420 100644 --- a/src/blockchain_utilities/blockchain_usage.cpp +++ b/src/blockchain_utilities/blockchain_usage.cpp @@ -28,6 +28,7 @@ #include <boost/range/adaptor/transformed.hpp> #include <boost/algorithm/string.hpp> +#include <boost/filesystem/path.hpp> #include "common/command_line.h" #include "common/varint.h" #include "cryptonote_core/tx_pool.h" diff --git a/src/checkpoints/checkpoints.cpp b/src/checkpoints/checkpoints.cpp index c88a630cc..6b48d8723 100644 --- a/src/checkpoints/checkpoints.cpp +++ b/src/checkpoints/checkpoints.cpp @@ -34,6 +34,8 @@ #include "string_tools.h" #include "storages/portable_storage_template_helper.h" // epee json include #include "serialization/keyvalue_serialization.h" +#include <boost/system/error_code.hpp> +#include <boost/filesystem.hpp> #include <functional> #include <vector> diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp index c31d1dd96..f0b617798 100644 --- a/src/common/dns_utils.cpp +++ b/src/common/dns_utils.cpp @@ -37,6 +37,7 @@ #include <boost/thread/mutex.hpp> #include <boost/algorithm/string/join.hpp> #include <boost/optional.hpp> +#include <boost/utility/string_ref.hpp> using namespace epee; #undef MONERO_DEFAULT_LOG_CATEGORY @@ -124,6 +125,7 @@ static const char *get_record_name(int record_type) case DNS_TYPE_A: return "A"; case DNS_TYPE_TXT: return "TXT"; case DNS_TYPE_AAAA: return "AAAA"; + case DNS_TYPE_TLSA: return "TLSA"; default: return "unknown"; } } @@ -186,6 +188,13 @@ boost::optional<std::string> txt_to_string(const char* src, size_t len) return std::string(src+1, len-1); } +boost::optional<std::string> tlsa_to_string(const char* src, size_t len) +{ + if (len < 4) + return boost::none; + return std::string(src, len); +} + // custom smart pointer. // TODO: see if std::auto_ptr and the like support custom destructors template<typename type, void (*freefunc)(type*)> @@ -326,11 +335,15 @@ std::vector<std::string> DNSResolver::get_record(const std::string& url, int rec // destructor takes care of cleanup ub_result_ptr result; + MDEBUG("Performing DNSSEC " << get_record_name(record_type) << " record query for " << url); + // call DNS resolver, blocking. if return value not zero, something went wrong if (!ub_resolve(m_data->m_ub_context, string_copy(url.c_str()), record_type, DNS_CLASS_IN, &result)) { dnssec_available = (result->secure || result->bogus); dnssec_valid = result->secure && !result->bogus; + if (dnssec_available && !dnssec_valid) + MWARNING("Invalid DNSSEC " << get_record_name(record_type) << " record signature for " << url << ": " << result->why_bogus); if (result->havedata) { for (size_t i=0; result->data[i] != NULL; i++) @@ -338,8 +351,9 @@ std::vector<std::string> DNSResolver::get_record(const std::string& url, int rec boost::optional<std::string> res = (*reader)(result->data[i], result->len[i]); if (res) { - MINFO("Found \"" << *res << "\" in " << get_record_name(record_type) << " record for " << url); - addresses.push_back(*res); + // do not dump dns record directly from dns into log + MINFO("Found " << get_record_name(record_type) << " record for " << url); + addresses.push_back(std::move(*res)); } } } @@ -363,6 +377,17 @@ std::vector<std::string> DNSResolver::get_txt_record(const std::string& url, boo return get_record(url, DNS_TYPE_TXT, txt_to_string, dnssec_available, dnssec_valid); } +std::vector<std::string> DNSResolver::get_tlsa_tcp_record(const boost::string_ref url, const boost::string_ref port, bool& dnssec_available, bool& dnssec_valid) +{ + std::string service_addr; + service_addr.reserve(url.size() + port.size() + 7); + service_addr.push_back('_'); + service_addr.append(port.data(), port.size()); + service_addr.append("._tcp."); + service_addr.append(url.data(), url.size()); + return get_record(service_addr, DNS_TYPE_TLSA, tlsa_to_string, dnssec_available, dnssec_valid); +} + std::string DNSResolver::get_dns_format_from_oa_address(const std::string& oa_addr) { std::string addr(oa_addr); diff --git a/src/common/dns_utils.h b/src/common/dns_utils.h index 30c4cced2..99e91bc54 100644 --- a/src/common/dns_utils.h +++ b/src/common/dns_utils.h @@ -31,15 +31,17 @@ #include <string> #include <functional> #include <boost/optional/optional_fwd.hpp> +#include <boost/utility/string_ref_fwd.hpp> namespace tools { // RFC defines for record types and classes for DNS, gleaned from ldns source -const static int DNS_CLASS_IN = 1; -const static int DNS_TYPE_A = 1; -const static int DNS_TYPE_TXT = 16; -const static int DNS_TYPE_AAAA = 8; +constexpr const int DNS_CLASS_IN = 1; +constexpr const int DNS_TYPE_A = 1; +constexpr const int DNS_TYPE_TXT = 16; +constexpr const int DNS_TYPE_AAAA = 8; +constexpr const int DNS_TYPE_TLSA = 52; struct DNSResolverData; @@ -106,6 +108,17 @@ public: std::vector<std::string> get_txt_record(const std::string& url, bool& dnssec_available, bool& dnssec_valid); /** + * @brief gets all TLSA TCP records from a DNS query for the supplied URL; + * if no TLSA record present returns an empty vector. + * + * @param url A string containing a URL to query for + * @param port The service port number (as string) to query + * + * @return A vector of strings containing all TLSA records; or an empty vector + */ + std::vector<std::string> get_tlsa_tcp_record(boost::string_ref url, boost::string_ref port, bool& dnssec_available, bool& dnssec_valid); + + /** * @brief Gets a DNS address from OpenAlias format * * If the address looks good, but contains one @ symbol, replace that with a . diff --git a/src/common/i18n.cpp b/src/common/i18n.cpp index ebc367b3a..051220ee1 100644 --- a/src/common/i18n.cpp +++ b/src/common/i18n.cpp @@ -35,6 +35,10 @@ #include "common/i18n.h" #include "translation_files.h" +#include <boost/system/error_code.hpp> +#include <boost/filesystem.hpp> +#include <algorithm> + #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "i18n" diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt index 3b33fe90a..3f0f7d34b 100644 --- a/src/crypto/CMakeLists.txt +++ b/src/crypto/CMakeLists.txt @@ -116,6 +116,7 @@ endif() # cheat because cmake and ccache hate each other set_property(SOURCE CryptonightR_template.S PROPERTY LANGUAGE C) +set_property(SOURCE CryptonightR_template.S PROPERTY XCODE_EXPLICIT_FILE_TYPE sourcecode.asm) # Must be done last, because it references libraries in this directory add_subdirectory(wallet) diff --git a/src/crypto/rx-slow-hash.c b/src/crypto/rx-slow-hash.c index fa35a32e2..801987e37 100644 --- a/src/crypto/rx-slow-hash.c +++ b/src/crypto/rx-slow-hash.c @@ -264,12 +264,14 @@ void rx_slow_hash(const uint64_t mainheight, const uint64_t seedheight, const ch cache = rx_sp->rs_cache; if (cache == NULL) { - if (cache == NULL) { + if (!(disabled_flags() & RANDOMX_FLAG_LARGE_PAGES)) { cache = randomx_alloc_cache(flags | RANDOMX_FLAG_LARGE_PAGES); if (cache == NULL) { mdebug(RX_LOGCAT, "Couldn't use largePages for RandomX cache"); - cache = randomx_alloc_cache(flags); } + } + if (cache == NULL) { + cache = randomx_alloc_cache(flags); if (cache == NULL) local_abort("Couldn't allocate RandomX cache"); } @@ -291,11 +293,14 @@ void rx_slow_hash(const uint64_t mainheight, const uint64_t seedheight, const ch CTHR_MUTEX_LOCK(rx_dataset_mutex); if (!rx_dataset_nomem) { if (rx_dataset == NULL) { - rx_dataset = randomx_alloc_dataset(RANDOMX_FLAG_LARGE_PAGES); - if (rx_dataset == NULL) { - mdebug(RX_LOGCAT, "Couldn't use largePages for RandomX dataset"); - rx_dataset = randomx_alloc_dataset(RANDOMX_FLAG_DEFAULT); + if (!(disabled_flags() & RANDOMX_FLAG_LARGE_PAGES)) { + rx_dataset = randomx_alloc_dataset(RANDOMX_FLAG_LARGE_PAGES); + if (rx_dataset == NULL) { + mdebug(RX_LOGCAT, "Couldn't use largePages for RandomX dataset"); + } } + if (rx_dataset == NULL) + rx_dataset = randomx_alloc_dataset(RANDOMX_FLAG_DEFAULT); if (rx_dataset != NULL) rx_initdata(rx_sp->rs_cache, miners, seedheight); } @@ -311,11 +316,14 @@ void rx_slow_hash(const uint64_t mainheight, const uint64_t seedheight, const ch } CTHR_MUTEX_UNLOCK(rx_dataset_mutex); } - rx_vm = randomx_create_vm(flags | RANDOMX_FLAG_LARGE_PAGES, rx_sp->rs_cache, rx_dataset); - if(rx_vm == NULL) { //large pages failed - mdebug(RX_LOGCAT, "Couldn't use largePages for RandomX VM"); - rx_vm = randomx_create_vm(flags, rx_sp->rs_cache, rx_dataset); + if (!(disabled_flags() & RANDOMX_FLAG_LARGE_PAGES)) { + rx_vm = randomx_create_vm(flags | RANDOMX_FLAG_LARGE_PAGES, rx_sp->rs_cache, rx_dataset); + if(rx_vm == NULL) { //large pages failed + mdebug(RX_LOGCAT, "Couldn't use largePages for RandomX VM"); + } } + if (rx_vm == NULL) + rx_vm = randomx_create_vm(flags, rx_sp->rs_cache, rx_dataset); if(rx_vm == NULL) {//fallback if everything fails flags = RANDOMX_FLAG_DEFAULT | (miners ? RANDOMX_FLAG_FULL_MEM : 0); rx_vm = randomx_create_vm(flags, rx_sp->rs_cache, rx_dataset); diff --git a/src/cryptonote_basic/connection_context.h b/src/cryptonote_basic/connection_context.h index a7d688300..ee26a0c07 100644 --- a/src/cryptonote_basic/connection_context.h +++ b/src/cryptonote_basic/connection_context.h @@ -77,6 +77,8 @@ namespace cryptonote int m_expect_response; uint64_t m_expect_height; size_t m_num_requested; + epee::copyable_atomic m_new_stripe_notification{0}; + epee::copyable_atomic m_idle_peer_notification{0}; }; inline std::string get_protocol_state_string(cryptonote_connection_context::state s) diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index c70ae1df1..6394a7071 100644 --- a/src/cryptonote_basic/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -152,10 +152,6 @@ namespace cryptonote }; - template<typename T> static inline unsigned int getpos(T &ar) { return 0; } - template<> inline unsigned int getpos(binary_archive<true> &ar) { return ar.stream().tellp(); } - template<> inline unsigned int getpos(binary_archive<false> &ar) { return ar.stream().tellg(); } - class transaction_prefix { @@ -236,17 +232,17 @@ namespace cryptonote set_blob_size_valid(false); } - const unsigned int start_pos = getpos(ar); + const auto start_pos = ar.getpos(); FIELDS(*static_cast<transaction_prefix *>(this)) if (std::is_same<Archive<W>, binary_archive<W>>()) - prefix_size = getpos(ar) - start_pos; + prefix_size = ar.getpos() - start_pos; if (version == 1) { if (std::is_same<Archive<W>, binary_archive<W>>()) - unprunable_size = getpos(ar) - start_pos; + unprunable_size = ar.getpos() - start_pos; ar.tag("signatures"); ar.begin_array(); @@ -284,11 +280,11 @@ namespace cryptonote { ar.begin_object(); bool r = rct_signatures.serialize_rctsig_base(ar, vin.size(), vout.size()); - if (!r || !ar.stream().good()) return false; + if (!r || !ar.good()) return false; ar.end_object(); if (std::is_same<Archive<W>, binary_archive<W>>()) - unprunable_size = getpos(ar) - start_pos; + unprunable_size = ar.getpos() - start_pos; if (!pruned && rct_signatures.type != rct::RCTTypeNull) { @@ -296,7 +292,7 @@ namespace cryptonote ar.begin_object(); r = rct_signatures.p.serialize_rctsig_prunable(ar, rct_signatures.type, vin.size(), vout.size(), vin.size() > 0 && vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(vin[0]).key_offsets.size() - 1 : 0); - if (!r || !ar.stream().good()) return false; + if (!r || !ar.good()) return false; ar.end_object(); } } @@ -320,13 +316,13 @@ namespace cryptonote { ar.begin_object(); bool r = rct_signatures.serialize_rctsig_base(ar, vin.size(), vout.size()); - if (!r || !ar.stream().good()) return false; + if (!r || !ar.good()) return false; ar.end_object(); } } if (!typename Archive<W>::is_saving()) pruned = true; - return ar.stream().good(); + return ar.good(); } private: diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 3e4532d4e..5cd40ce79 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -32,6 +32,7 @@ #include <boost/algorithm/string.hpp> #include "wipeable_string.h" #include "string_tools.h" +#include "string_tools_lexical.h" #include "serialization/string.h" #include "cryptonote_format_utils.h" #include "cryptonote_config.h" @@ -210,9 +211,7 @@ namespace cryptonote //--------------------------------------------------------------- bool parse_and_validate_tx_from_blob(const blobdata_ref& tx_blob, transaction& tx) { - std::stringstream ss; - ss << tx_blob; - binary_archive<false> ba(ss); + binary_archive<false> ba{epee::strspan<std::uint8_t>(tx_blob)}; bool r = ::serialization::serialize(ba, tx); CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob"); CHECK_AND_ASSERT_MES(expand_transaction_1(tx, false), false, "Failed to expand transaction data"); @@ -223,9 +222,7 @@ namespace cryptonote //--------------------------------------------------------------- bool parse_and_validate_tx_base_from_blob(const blobdata_ref& tx_blob, transaction& tx) { - std::stringstream ss; - ss << tx_blob; - binary_archive<false> ba(ss); + binary_archive<false> ba{epee::strspan<std::uint8_t>(tx_blob)}; bool r = tx.serialize_base(ba); CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob"); CHECK_AND_ASSERT_MES(expand_transaction_1(tx, true), false, "Failed to expand transaction data"); @@ -235,9 +232,7 @@ namespace cryptonote //--------------------------------------------------------------- bool parse_and_validate_tx_prefix_from_blob(const blobdata_ref& tx_blob, transaction_prefix& tx) { - std::stringstream ss; - ss << tx_blob; - binary_archive<false> ba(ss); + binary_archive<false> ba{epee::strspan<std::uint8_t>(tx_blob)}; bool r = ::serialization::serialize_noeof(ba, tx); CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction prefix from blob"); return true; @@ -245,9 +240,7 @@ namespace cryptonote //--------------------------------------------------------------- bool parse_and_validate_tx_from_blob(const blobdata_ref& tx_blob, transaction& tx, crypto::hash& tx_hash) { - std::stringstream ss; - ss << tx_blob; - binary_archive<false> ba(ss); + binary_archive<false> ba{epee::strspan<std::uint8_t>(tx_blob)}; bool r = ::serialization::serialize(ba, tx); CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob"); CHECK_AND_ASSERT_MES(expand_transaction_1(tx, false), false, "Failed to expand transaction data"); @@ -531,22 +524,15 @@ namespace cryptonote if(tx_extra.empty()) return true; - std::string extra_str(reinterpret_cast<const char*>(tx_extra.data()), tx_extra.size()); - std::istringstream iss(extra_str); - binary_archive<false> ar(iss); + binary_archive<false> ar{epee::to_span(tx_extra)}; - bool eof = false; - while (!eof) + do { tx_extra_field field; bool r = ::do_serialize(ar, field); CHECK_AND_NO_ASSERT_MES_L1(r, false, "failed to deserialize extra field. extra = " << string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast<const char*>(tx_extra.data()), tx_extra.size()))); tx_extra_fields.push_back(field); - - std::ios_base::iostate state = iss.rdstate(); - eof = (EOF == iss.peek()); - iss.clear(state); - } + } while (!ar.eof()); CHECK_AND_NO_ASSERT_MES_L1(::serialization::check_stream_state(ar), false, "failed to deserialize extra field. extra = " << string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast<const char*>(tx_extra.data()), tx_extra.size()))); return true; @@ -577,13 +563,10 @@ namespace cryptonote return true; } - std::string extra_str(reinterpret_cast<const char*>(tx_extra.data()), tx_extra.size()); - std::istringstream iss(extra_str); - binary_archive<false> ar(iss); + binary_archive<false> ar{epee::to_span(tx_extra)}; - bool eof = false; size_t processed = 0; - while (!eof) + do { tx_extra_field field; bool r = ::do_serialize(ar, field); @@ -595,12 +578,8 @@ namespace cryptonote break; } tx_extra_fields.push_back(field); - processed = iss.tellg(); - - std::ios_base::iostate state = iss.rdstate(); - eof = (EOF == iss.peek()); - iss.clear(state); - } + processed = ar.getpos(); + } while (!ar.eof()); if (!::serialization::check_stream_state(ar)) { MWARNING("failed to deserialize extra field. extra = " << string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast<const char*>(tx_extra.data()), tx_extra.size()))); @@ -751,24 +730,18 @@ namespace cryptonote if (tx_extra.empty()) return true; std::string extra_str(reinterpret_cast<const char*>(tx_extra.data()), tx_extra.size()); - std::istringstream iss(extra_str); - binary_archive<false> ar(iss); + binary_archive<false> ar{epee::strspan<std::uint8_t>(extra_str)}; std::ostringstream oss; binary_archive<true> newar(oss); - bool eof = false; - while (!eof) + do { tx_extra_field field; bool r = ::do_serialize(ar, field); CHECK_AND_NO_ASSERT_MES_L1(r, false, "failed to deserialize extra field. extra = " << string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast<const char*>(tx_extra.data()), tx_extra.size()))); if (field.type() != type) ::do_serialize(newar, field); - - std::ios_base::iostate state = iss.rdstate(); - eof = (EOF == iss.peek()); - iss.clear(state); - } + } while (!ar.eof()); CHECK_AND_NO_ASSERT_MES_L1(::serialization::check_stream_state(ar), false, "failed to deserialize extra field. extra = " << string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast<const char*>(tx_extra.data()), tx_extra.size()))); tx_extra.clear(); std::string s = oss.str(); @@ -1356,9 +1329,7 @@ namespace cryptonote //--------------------------------------------------------------- bool parse_and_validate_block_from_blob(const blobdata_ref& b_blob, block& b, crypto::hash *block_hash) { - std::stringstream ss; - ss << b_blob; - binary_archive<false> ba(ss); + binary_archive<false> ba{epee::strspan<std::uint8_t>(b_blob)}; bool r = ::serialization::serialize(ba, b); CHECK_AND_ASSERT_MES(r, false, "Failed to parse block from blob"); b.invalidate_hashes(); diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h index b311bd2b2..3fe4c44e7 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.h +++ b/src/cryptonote_basic/cryptonote_format_utils.h @@ -148,9 +148,7 @@ namespace cryptonote template<class t_object> bool t_serializable_object_from_blob(t_object& to, const blobdata& b_blob) { - std::stringstream ss; - ss << b_blob; - binary_archive<false> ba(ss); + binary_archive<false> ba{epee::strspan<std::uint8_t>(b_blob)}; bool r = ::serialization::serialize(ba, to); return r; } diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp index 29f6dce5a..ae514aac6 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -44,6 +44,7 @@ #include "string_tools.h" #include "storages/portable_storage_template_helper.h" #include "boost/logic/tribool.hpp" +#include <boost/filesystem.hpp> #ifdef __APPLE__ #include <sys/times.h> diff --git a/src/cryptonote_basic/tx_extra.h b/src/cryptonote_basic/tx_extra.h index 50f2e1438..76efc22d3 100644 --- a/src/cryptonote_basic/tx_extra.h +++ b/src/cryptonote_basic/tx_extra.h @@ -57,11 +57,7 @@ namespace cryptonote // size - 1 - because of variant tag for (size = 1; size <= TX_EXTRA_PADDING_MAX_COUNT; ++size) { - std::ios_base::iostate state = ar.stream().rdstate(); - bool eof = EOF == ar.stream().peek(); - ar.stream().clear(state); - - if (eof) + if (ar.eof()) break; uint8_t zero; @@ -139,8 +135,7 @@ namespace cryptonote if(!::do_serialize(ar, field)) return false; - std::istringstream iss(field); - binary_archive<false> iar(iss); + binary_archive<false> iar{epee::strspan<std::uint8_t>(field)}; serialize_helper helper(*this); return ::serialization::serialize(iar, helper); } diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index cdad39a2c..2cd04d4cf 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -57,6 +57,7 @@ #include "common/notify.h" #include "common/varint.h" #include "common/pruning.h" +#include "time_helper.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "blockchain" @@ -565,6 +566,8 @@ void Blockchain::pop_blocks(uint64_t nblocks) return; } + CHECK_AND_ASSERT_THROW_MES(update_next_cumulative_weight_limit(), "Error updating next cumulative weight limit"); + if (stop_batch) m_db->batch_stop(); } @@ -642,7 +645,6 @@ block Blockchain::pop_block_from_blockchain() m_scan_table.clear(); m_blocks_txs_check.clear(); - CHECK_AND_ASSERT_THROW_MES(update_next_cumulative_weight_limit(), "Error updating next cumulative weight limit"); uint64_t top_block_height; crypto::hash top_block_hash = get_tail_id(top_block_height); m_tx_pool.on_blockchain_dec(top_block_height, top_block_hash); @@ -1109,6 +1111,7 @@ bool Blockchain::rollback_blockchain_switching(std::list<block>& original_chain, { pop_block_from_blockchain(); } + CHECK_AND_ASSERT_THROW_MES(update_next_cumulative_weight_limit(), "Error updating next cumulative weight limit"); // make sure the hard fork object updates its current version m_hardfork->reorganize_from_chain_height(rollback_height); @@ -1159,6 +1162,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<block_extended_info> block b = pop_block_from_blockchain(); disconnected_chain.push_front(b); } + CHECK_AND_ASSERT_THROW_MES(update_next_cumulative_weight_limit(), "Error updating next cumulative weight limit"); auto split_height = m_db->height(); @@ -2608,7 +2612,7 @@ bool Blockchain::get_split_transactions_blobs(const t_ids_container& txs_ids, t_ } //------------------------------------------------------------------ template<class t_ids_container, class t_tx_container, class t_missed_container> -bool Blockchain::get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) const +bool Blockchain::get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs, bool pruned) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -2619,10 +2623,12 @@ bool Blockchain::get_transactions(const t_ids_container& txs_ids, t_tx_container try { cryptonote::blobdata tx; - if (m_db->get_tx_blob(tx_hash, tx)) + bool res = pruned ? m_db->get_pruned_tx_blob(tx_hash, tx) : m_db->get_tx_blob(tx_hash, tx); + if (res) { txs.push_back(transaction()); - if (!parse_and_validate_tx_from_blob(tx, txs.back())) + res = pruned ? parse_and_validate_tx_base_from_blob(tx, txs.back()) : parse_and_validate_tx_from_blob(tx, txs.back()); + if (!res) { LOG_ERROR("Invalid transaction"); return false; @@ -5149,7 +5155,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete if (m_cancel) return false; - for (const auto &tx_blob : entry.txs) + for (size_t i = 0; i < entry.txs.size(); ++i) { if (tx_index >= txes.size()) SCAN_TABLE_QUIT("tx_index is out of sync"); @@ -5526,6 +5532,6 @@ void Blockchain::cache_block_template(const block &b, const cryptonote::account_ } namespace cryptonote { -template bool Blockchain::get_transactions(const std::vector<crypto::hash>&, std::vector<transaction>&, std::vector<crypto::hash>&) const; +template bool Blockchain::get_transactions(const std::vector<crypto::hash>&, std::vector<transaction>&, std::vector<crypto::hash>&, bool) const; template bool Blockchain::get_split_transactions_blobs(const std::vector<crypto::hash>&, std::vector<std::tuple<crypto::hash, cryptonote::blobdata, crypto::hash, cryptonote::blobdata>>&, std::vector<crypto::hash>&) const; } diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 5291f1338..a0e7967de 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -721,7 +721,7 @@ namespace cryptonote template<class t_ids_container, class t_tx_container, class t_missed_container> bool get_split_transactions_blobs(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) const; template<class t_ids_container, class t_tx_container, class t_missed_container> - bool get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) const; + bool get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs, bool pruned = false) const; //debug functions diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 57104fd59..1da14221a 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -57,6 +57,8 @@ using namespace epee; #include "hardforks/hardforks.h" #include "version.h" +#include <boost/filesystem.hpp> + #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "cn" @@ -427,9 +429,9 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- - bool core::get_transactions(const std::vector<crypto::hash>& txs_ids, std::vector<cryptonote::blobdata>& txs, std::vector<crypto::hash>& missed_txs) const + bool core::get_transactions(const std::vector<crypto::hash>& txs_ids, std::vector<cryptonote::blobdata>& txs, std::vector<crypto::hash>& missed_txs, bool pruned) const { - return m_blockchain_storage.get_transactions_blobs(txs_ids, txs, missed_txs); + return m_blockchain_storage.get_transactions_blobs(txs_ids, txs, missed_txs, pruned); } //----------------------------------------------------------------------------------------------- bool core::get_split_transactions_blobs(const std::vector<crypto::hash>& txs_ids, std::vector<std::tuple<crypto::hash, cryptonote::blobdata, crypto::hash, cryptonote::blobdata>>& txs, std::vector<crypto::hash>& missed_txs) const @@ -443,9 +445,9 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- - bool core::get_transactions(const std::vector<crypto::hash>& txs_ids, std::vector<transaction>& txs, std::vector<crypto::hash>& missed_txs) const + bool core::get_transactions(const std::vector<crypto::hash>& txs_ids, std::vector<transaction>& txs, std::vector<crypto::hash>& missed_txs, bool pruned) const { - return m_blockchain_storage.get_transactions(txs_ids, txs, missed_txs); + return m_blockchain_storage.get_transactions(txs_ids, txs, missed_txs, pruned); } //----------------------------------------------------------------------------------------------- bool core::get_alternative_blocks(std::vector<block>& blocks) const @@ -1241,7 +1243,7 @@ namespace cryptonote std::vector<transaction> txs; std::vector<crypto::hash> missed_txs; uint64_t coinbase_amount = get_outs_money_amount(b.miner_tx); - this->get_transactions(b.tx_hashes, txs, missed_txs); + this->get_transactions(b.tx_hashes, txs, missed_txs, true); uint64_t tx_fee_amount = 0; for(const auto& tx: txs) { diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 8891540a9..8478049f9 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -385,7 +385,7 @@ namespace cryptonote * * @note see Blockchain::get_transactions */ - bool get_transactions(const std::vector<crypto::hash>& txs_ids, std::vector<cryptonote::blobdata>& txs, std::vector<crypto::hash>& missed_txs) const; + bool get_transactions(const std::vector<crypto::hash>& txs_ids, std::vector<cryptonote::blobdata>& txs, std::vector<crypto::hash>& missed_txs, bool pruned = false) const; /** * @copydoc Blockchain::get_transactions @@ -399,7 +399,7 @@ namespace cryptonote * * @note see Blockchain::get_transactions */ - bool get_transactions(const std::vector<crypto::hash>& txs_ids, std::vector<transaction>& txs, std::vector<crypto::hash>& missed_txs) const; + bool get_transactions(const std::vector<crypto::hash>& txs_ids, std::vector<transaction>& txs, std::vector<crypto::hash>& missed_txs, bool pruned = false) const; /** * @copydoc Blockchain::get_block_by_hash diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 7400c4328..f41c63a4b 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -622,8 +622,10 @@ namespace cryptonote if (need_additional_txkeys) { additional_tx_keys.clear(); - for (const auto &d: destinations) + for (size_t i = 0; i < destinations.size(); ++i) + { additional_tx_keys.push_back(keypair::generate(sender_account_keys.get_device()).sec); + } } bool r = construct_tx_with_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct, rct_config, msout); diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h index dbdf409b5..73cdd31cd 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.h +++ b/src/cryptonote_core/cryptonote_tx_utils.h @@ -44,10 +44,10 @@ namespace cryptonote typedef std::pair<uint64_t, rct::ctkey> output_entry; std::vector<output_entry> outputs; //index + key + optional ringct commitment - size_t real_output; //index in outputs vector of real output_entry + uint64_t real_output; //index in outputs vector of real output_entry crypto::public_key real_out_tx_key; //incoming real tx public key std::vector<crypto::public_key> real_out_additional_tx_keys; //incoming real tx additional public keys - size_t real_output_in_tx_index; //index in transaction outputs vector + uint64_t real_output_in_tx_index; //index in transaction outputs vector uint64_t amount; //money bool rct; //true if the output is rct rct::key mask; //ringct amount mask diff --git a/src/cryptonote_protocol/CMakeLists.txt b/src/cryptonote_protocol/CMakeLists.txt index d28b44bb7..85c25546f 100644 --- a/src/cryptonote_protocol/CMakeLists.txt +++ b/src/cryptonote_protocol/CMakeLists.txt @@ -26,7 +26,7 @@ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -cmake_minimum_required (VERSION 2.6) +cmake_minimum_required (VERSION 3.5) project (monero CXX) file(GLOB CRYPTONOTE_PROTOCOL *) diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index 28530f3e7..80dd2bc39 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -46,6 +46,8 @@ #include "block_queue.h" #include "common/perf_timer.h" #include "cryptonote_basic/connection_context.h" +#include "net/levin_base.h" +#include "p2p/net_node_common.h" #include <boost/circular_buffer.hpp> PUSH_WARNINGS @@ -195,10 +197,11 @@ namespace cryptonote bool post_notify(typename t_parameter::request& arg, cryptonote_connection_context& context) { LOG_PRINT_L2("[" << epee::net_utils::print_connection_context_short(context) << "] post " << typeid(t_parameter).name() << " -->"); - epee::byte_slice blob; - epee::serialization::store_t_to_binary(arg, blob, 256 * 1024); // optimize for block responses + + epee::levin::message_writer out{256 * 1024}; // optimize for block responses + epee::serialization::store_t_to_binary(arg, out.buffer); //handler_response_blocks_now(blob.size()); // XXX - return m_p2p->invoke_notify_to_peer(t_parameter::ID, epee::to_span(blob), context); + return m_p2p->invoke_notify_to_peer(t_parameter::ID, std::move(out), context); } }; diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index c798dbcdb..685968c08 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -137,6 +137,41 @@ namespace cryptonote CHECK_AND_ASSERT_MES_CC( context.m_callback_request_count > 0, false, "false callback fired, but context.m_callback_request_count=" << context.m_callback_request_count); --context.m_callback_request_count; + uint32_t notified = true; + if (context.m_idle_peer_notification.compare_exchange_strong(notified, not notified)) + { + if (context.m_state == cryptonote_connection_context::state_synchronizing && context.m_last_request_time != boost::date_time::not_a_date_time) + { + const boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time(); + const boost::posix_time::time_duration dt = now - context.m_last_request_time; + const auto ms = dt.total_microseconds(); + if (ms > IDLE_PEER_KICK_TIME || (context.m_expect_response && ms > NON_RESPONSIVE_PEER_KICK_TIME)) + { + if (context.m_score-- >= 0) + { + MINFO(context << " kicking idle peer, last update " << (dt.total_microseconds() / 1.e6) << " seconds ago, expecting " << (int)context.m_expect_response); + context.m_last_request_time = boost::date_time::not_a_date_time; + context.m_expect_response = 0; + context.m_expect_height = 0; + context.m_state = cryptonote_connection_context::state_standby; // we'll go back to adding, then (if we can't), download + } + else + { + MINFO(context << "dropping idle peer with negative score"); + drop_connection_with_score(context, context.m_expect_response == 0 ? 1 : 5, false); + return false; + } + } + } + } + + notified = true; + if (context.m_new_stripe_notification.compare_exchange_strong(notified, not notified)) + { + if (context.m_state == cryptonote_connection_context::state_normal) + context.m_state = cryptonote_connection_context::state_synchronizing; + } + if(context.m_state == cryptonote_connection_context::state_synchronizing && context.m_last_request_time == boost::posix_time::not_a_date_time) { NOTIFY_REQUEST_CHAIN::request r = {}; @@ -1687,7 +1722,7 @@ skip: const uint32_t peer_stripe = tools::get_pruning_stripe(context.m_pruning_seed); if (stripe && peer_stripe && peer_stripe != stripe) return true; - context.m_state = cryptonote_connection_context::state_synchronizing; + context.m_new_stripe_notification = true; LOG_PRINT_CCONTEXT_L2("requesting callback"); ++context.m_callback_request_count; m_p2p->request_callback(context); @@ -1710,7 +1745,6 @@ skip: bool t_cryptonote_protocol_handler<t_core>::kick_idle_peers() { MTRACE("Checking for idle peers..."); - std::vector<std::pair<boost::uuids::uuid, unsigned>> idle_peers; m_p2p->for_each_connection([&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags)->bool { if (context.m_state == cryptonote_connection_context::state_synchronizing && context.m_last_request_time != boost::date_time::not_a_date_time) @@ -1720,36 +1754,16 @@ skip: const auto ms = dt.total_microseconds(); if (ms > IDLE_PEER_KICK_TIME || (context.m_expect_response && ms > NON_RESPONSIVE_PEER_KICK_TIME)) { - if (context.m_score-- >= 0) - { - MINFO(context << " kicking idle peer, last update " << (dt.total_microseconds() / 1.e6) << " seconds ago, expecting " << (int)context.m_expect_response); - LOG_PRINT_CCONTEXT_L2("requesting callback"); - context.m_last_request_time = boost::date_time::not_a_date_time; - context.m_expect_response = 0; - context.m_expect_height = 0; - context.m_state = cryptonote_connection_context::state_standby; // we'll go back to adding, then (if we can't), download - ++context.m_callback_request_count; - m_p2p->request_callback(context); - } - else - { - idle_peers.push_back(std::make_pair(context.m_connection_id, context.m_expect_response == 0 ? 1 : 5)); - } + context.m_idle_peer_notification = true; + LOG_PRINT_CCONTEXT_L2("requesting callback"); + ++context.m_callback_request_count; + m_p2p->request_callback(context); + MLOG_PEER_STATE("requesting callback"); } } return true; }); - for (const auto &e: idle_peers) - { - const auto &uuid = e.first; - m_p2p->for_connection(uuid, [&](cryptonote_connection_context& ctx, nodetool::peerid_type peer_id, uint32_t f)->bool{ - MINFO(ctx << "dropping idle peer with negative score"); - drop_connection_with_score(ctx, e.second, false); - return true; - }); - } - return true; } //------------------------------------------------------------------------------------------------------------------------ @@ -2296,7 +2310,7 @@ skip: const uint32_t peer_stripe = tools::get_pruning_stripe(context.m_pruning_seed); const uint32_t first_stripe = tools::get_pruning_stripe(span.first, context.m_remote_blockchain_height, CRYPTONOTE_PRUNING_LOG_STRIPES); const uint32_t last_stripe = tools::get_pruning_stripe(span.first + span.second - 1, context.m_remote_blockchain_height, CRYPTONOTE_PRUNING_LOG_STRIPES); - if ((((first_stripe && peer_stripe != first_stripe) || (last_stripe && peer_stripe != last_stripe)) && !m_sync_pruned_blocks) || (m_sync_pruned_blocks && req.prune)) + if (((first_stripe && peer_stripe != first_stripe) || (last_stripe && peer_stripe != last_stripe)) && !m_sync_pruned_blocks) { MDEBUG(context << "We need full data, but the peer does not have it, dropping peer"); return false; @@ -2699,15 +2713,15 @@ skip: // send fluffy ones first, we want to encourage people to run that if (!fluffyConnections.empty()) { - epee::byte_slice fluffyBlob; - epee::serialization::store_t_to_binary(fluffy_arg, fluffyBlob, 32 * 1024); - m_p2p->relay_notify_to_list(NOTIFY_NEW_FLUFFY_BLOCK::ID, epee::to_span(fluffyBlob), std::move(fluffyConnections)); + epee::levin::message_writer fluffyBlob{32 * 1024}; + epee::serialization::store_t_to_binary(fluffy_arg, fluffyBlob.buffer); + m_p2p->relay_notify_to_list(NOTIFY_NEW_FLUFFY_BLOCK::ID, std::move(fluffyBlob), std::move(fluffyConnections)); } if (!fullConnections.empty()) { - epee::byte_slice fullBlob; - epee::serialization::store_t_to_binary(arg, fullBlob, 128 * 1024); - m_p2p->relay_notify_to_list(NOTIFY_NEW_BLOCK::ID, epee::to_span(fullBlob), std::move(fullConnections)); + epee::levin::message_writer fullBlob{128 * 1024}; + epee::serialization::store_t_to_binary(arg, fullBlob.buffer); + m_p2p->relay_notify_to_list(NOTIFY_NEW_BLOCK::ID, std::move(fullBlob), std::move(fullConnections)); } return true; @@ -2840,15 +2854,12 @@ skip: epee::string_tools::to_string_hex(context.m_pruning_seed) << "), score " << score << ", flush_all_spans " << flush_all_spans); - m_block_queue.flush_spans(context.m_connection_id, flush_all_spans); + if (score > 0) + m_p2p->add_host_fail(context.m_remote_address, score); - // copy since dropping the connection will invalidate the context, and thus the address - const auto remote_address = context.m_remote_address; + m_block_queue.flush_spans(context.m_connection_id, flush_all_spans); m_p2p->drop_connection(context); - - if (score > 0) - m_p2p->add_host_fail(remote_address, score); } //------------------------------------------------------------------------------------------------------------------------ template<class t_core> diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h index 79c2edf1d..57b1d049c 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h @@ -30,8 +30,8 @@ #pragma once -#include "p2p/net_node_common.h" #include "cryptonote_protocol/cryptonote_protocol_defs.h" +#include "cryptonote_protocol/enums.h" #include "cryptonote_basic/connection_context.h" namespace cryptonote { diff --git a/src/cryptonote_protocol/levin_notify.cpp b/src/cryptonote_protocol/levin_notify.cpp index 1e9f3e399..0b065c3c3 100644 --- a/src/cryptonote_protocol/levin_notify.cpp +++ b/src/cryptonote_protocol/levin_notify.cpp @@ -159,7 +159,7 @@ namespace levin return get_out_connections(p2p, get_blockchain_height(p2p, core)); } - epee::byte_slice make_tx_payload(std::vector<blobdata>&& txs, const bool pad, const bool fluff) + epee::levin::message_writer make_tx_message(std::vector<blobdata>&& txs, const bool pad, const bool fluff) { NOTIFY_NEW_TRANSACTIONS::request request{}; request.txs = std::move(txs); @@ -193,21 +193,17 @@ namespace levin // if the size of _ moved enough, we might lose byte in size encoding, we don't care } - epee::byte_slice fullBlob; - if (!epee::serialization::store_t_to_binary(request, fullBlob)) + epee::levin::message_writer out; + if (!epee::serialization::store_t_to_binary(request, out.buffer)) throw std::runtime_error{"Failed to serialize to epee binary format"}; - return fullBlob; + return out; } bool make_payload_send_txs(connections& p2p, std::vector<blobdata>&& txs, const boost::uuids::uuid& destination, const bool pad, const bool fluff) { - const epee::byte_slice blob = make_tx_payload(std::move(txs), pad, fluff); - p2p.for_connection(destination, [&blob](detail::p2p_context& context) { - on_levin_traffic(context, true, true, false, blob.size(), NOTIFY_NEW_TRANSACTIONS::ID); - return true; - }); - return p2p.notify(NOTIFY_NEW_TRANSACTIONS::ID, epee::to_span(blob), destination); + epee::byte_slice blob = make_tx_message(std::move(txs), pad, fluff).finalize_notify(NOTIFY_NEW_TRANSACTIONS::ID); + return p2p.send(std::move(blob), destination); } /* The current design uses `asio::strand`s. The documentation isn't as clear @@ -653,10 +649,6 @@ namespace levin else message = zone_->noise.clone(); - zone_->p2p->for_connection(channel.connection, [&](detail::p2p_context& context) { - on_levin_traffic(context, true, true, false, message.size(), "noise"); - return true; - }); if (zone_->p2p->send(std::move(message), channel.connection)) { if (!channel.queue.empty() && channel.active.empty()) @@ -816,9 +808,8 @@ namespace levin // Padding is not useful when using noise mode. Send as stem so receiver // forwards in Dandelion++ mode. - const epee::byte_slice payload = make_tx_payload(std::move(txs), false, false); epee::byte_slice message = epee::levin::make_fragmented_notify( - zone_->noise, NOTIFY_NEW_TRANSACTIONS::ID, epee::to_span(payload) + zone_->noise.size(), NOTIFY_NEW_TRANSACTIONS::ID, make_tx_message(std::move(txs), false, false) ); if (CRYPTONOTE_MAX_FRAGMENTS * zone_->noise.size() < message.size()) { diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index 5a7560874..14233bf29 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -30,6 +30,8 @@ #include "common/command_line.h" #include "net/parse.h" #include "daemon/command_parser_executor.h" +#include <boost/filesystem.hpp> +#include <boost/algorithm/string/predicate.hpp> #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "daemon" @@ -986,17 +988,67 @@ bool t_command_parser_executor::check_blockchain_pruning(const std::vector<std:: bool t_command_parser_executor::set_bootstrap_daemon(const std::vector<std::string>& args) { - const size_t args_count = args.size(); - if (args_count < 1 || args_count > 3) + struct parsed_t + { + std::string address; + std::string user; + std::string password; + std::string proxy; + }; + + boost::optional<parsed_t> parsed = [&args]() -> boost::optional<parsed_t> { + const size_t args_count = args.size(); + if (args_count == 0) + { + return {}; + } + if (args[0] == "auto") + { + if (args_count == 1) + { + return {{args[0], "", "", ""}}; + } + if (args_count == 2) + { + return {{args[0], "", "", args[1]}}; + } + } + else if (args[0] == "none") + { + if (args_count == 1) + { + return {{"", "", "", ""}}; + } + } + else + { + if (args_count == 1) + { + return {{args[0], "", "", ""}}; + } + if (args_count == 2) + { + return {{args[0], "", "", args[1]}}; + } + if (args_count == 3) + { + return {{args[0], args[1], args[2], ""}}; + } + if (args_count == 4) + { + return {{args[0], args[1], args[2], args[3]}}; + } + } + return {}; + }(); + + if (!parsed) { std::cout << "Invalid syntax: Wrong number of parameters. For more details, use the help command." << std::endl; return true; } - return m_executor.set_bootstrap_daemon( - args[0] != "none" ? args[0] : std::string(), - args_count > 1 ? args[1] : std::string(), - args_count > 2 ? args[2] : std::string()); + return m_executor.set_bootstrap_daemon(parsed->address, parsed->user, parsed->password, parsed->proxy); } bool t_command_parser_executor::flush_cache(const std::vector<std::string>& args) diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp index 4768bb842..63f44c4cd 100644 --- a/src/daemon/command_server.cpp +++ b/src/daemon/command_server.cpp @@ -326,7 +326,7 @@ t_command_server::t_command_server( m_command_lookup.set_handler( "set_bootstrap_daemon" , std::bind(&t_command_parser_executor::set_bootstrap_daemon, &m_parser, p::_1) - , "set_bootstrap_daemon (auto | none | host[:port] [username] [password])" + , "set_bootstrap_daemon (auto | none | host[:port] [username] [password]) [proxy_ip:proxy_port]" , "URL of a 'bootstrap' remote daemon that the connected wallets can use while this daemon is still not fully synced.\n" "Use 'auto' to enable automatic public nodes discovering and bootstrap daemon switching" ); diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 8194fe642..16e6a304c 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -2405,7 +2405,8 @@ bool t_rpc_command_executor::check_blockchain_pruning() bool t_rpc_command_executor::set_bootstrap_daemon( const std::string &address, const std::string &username, - const std::string &password) + const std::string &password, + const std::string &proxy) { cryptonote::COMMAND_RPC_SET_BOOTSTRAP_DAEMON::request req; cryptonote::COMMAND_RPC_SET_BOOTSTRAP_DAEMON::response res; @@ -2414,6 +2415,7 @@ bool t_rpc_command_executor::set_bootstrap_daemon( req.address = address; req.username = username; req.password = password; + req.proxy = proxy; if (m_is_rpc) { diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h index 6fb5d6903..118f04731 100644 --- a/src/daemon/rpc_command_executor.h +++ b/src/daemon/rpc_command_executor.h @@ -168,7 +168,8 @@ public: bool set_bootstrap_daemon( const std::string &address, const std::string &username, - const std::string &password); + const std::string &password, + const std::string &proxy); bool rpc_payments(); diff --git a/src/device_trezor/device_trezor.cpp b/src/device_trezor/device_trezor.cpp index 7c3e8785d..c2070b0d1 100644 --- a/src/device_trezor/device_trezor.cpp +++ b/src/device_trezor/device_trezor.cpp @@ -28,6 +28,8 @@ // #include "device_trezor.hpp" +#include <boost/filesystem.hpp> +#include <boost/algorithm/string/predicate.hpp> namespace hw { namespace trezor { diff --git a/src/device_trezor/device_trezor_base.cpp b/src/device_trezor/device_trezor_base.cpp index 70dc7f539..5f21ecd49 100644 --- a/src/device_trezor/device_trezor_base.cpp +++ b/src/device_trezor/device_trezor_base.cpp @@ -31,6 +31,7 @@ #include "memwipe.h" #include <boost/algorithm/string/classification.hpp> #include <boost/algorithm/string/split.hpp> +#include <boost/algorithm/string/predicate.hpp> #include <boost/regex.hpp> namespace hw { diff --git a/src/device_trezor/trezor/protocol.hpp b/src/device_trezor/trezor/protocol.hpp index fa824ec3b..0fdd36a51 100644 --- a/src/device_trezor/trezor/protocol.hpp +++ b/src/device_trezor/trezor/protocol.hpp @@ -66,9 +66,7 @@ namespace protocol{ template<typename T> bool cn_deserialize(const void * buff, size_t len, T & dst){ - std::stringstream ss; - ss.write(static_cast<const char *>(buff), len); //ss << tx_blob; - binary_archive<false> ba(ss); + binary_archive<false> ba{{reinterpret_cast<const std::uint8_t*>(buff), len}}; bool r = ::serialization::serialize(ba, dst); return r; } diff --git a/src/device_trezor/trezor/transport.cpp b/src/device_trezor/trezor/transport.cpp index a56090de0..194176413 100644 --- a/src/device_trezor/trezor/transport.cpp +++ b/src/device_trezor/trezor/transport.cpp @@ -38,6 +38,7 @@ #include <boost/asio/ip/udp.hpp> #include <boost/date_time/posix_time/posix_time_types.hpp> #include <boost/format.hpp> +#include <boost/algorithm/string/predicate.hpp> #include "common/apply_permutation.h" #include "transport.hpp" #include "messages/messages-common.pb.h" @@ -156,7 +157,7 @@ namespace trezor{ #define PROTO_HEADER_SIZE 6 static size_t message_size(const google::protobuf::Message &req){ - return static_cast<size_t>(req.ByteSize()); + return req.ByteSizeLong(); } static size_t serialize_message_buffer_size(size_t msg_size) { diff --git a/src/mnemonics/electrum-words.cpp b/src/mnemonics/electrum-words.cpp index b6bc22a3d..8c79a53ca 100644 --- a/src/mnemonics/electrum-words.cpp +++ b/src/mnemonics/electrum-words.cpp @@ -491,6 +491,14 @@ namespace crypto return "<language not found>"; } + bool is_valid_language(const std::string &language) + { + const std::vector<const Language::Base*> language_instances = get_language_list(); + for (std::vector<const Language::Base*>::const_iterator it = language_instances.begin(); it != language_instances.end(); it++) + if ((*it)->get_english_language_name() == language || (*it)->get_language_name() == language) + return true; + return false; + } } } diff --git a/src/mnemonics/electrum-words.h b/src/mnemonics/electrum-words.h index 8d4c5be66..eb0c99e0b 100644 --- a/src/mnemonics/electrum-words.h +++ b/src/mnemonics/electrum-words.h @@ -124,6 +124,8 @@ namespace crypto * \return the name of the language in English */ std::string get_english_name_for(const std::string &name); + + bool is_valid_language(const std::string &language); } } diff --git a/src/net/CMakeLists.txt b/src/net/CMakeLists.txt index afcd42ef7..e93e27bcd 100644 --- a/src/net/CMakeLists.txt +++ b/src/net/CMakeLists.txt @@ -26,10 +26,10 @@ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -set(net_sources dandelionpp.cpp error.cpp http.cpp i2p_address.cpp parse.cpp socks.cpp - socks_connect.cpp tor_address.cpp zmq.cpp) -set(net_headers dandelionpp.h error.h http.cpp i2p_address.h parse.h socks.h socks_connect.h - tor_address.h zmq.h) +set(net_sources dandelionpp.cpp error.cpp http.cpp i2p_address.cpp parse.cpp resolve.cpp + socks.cpp socks_connect.cpp tor_address.cpp zmq.cpp) +set(net_headers dandelionpp.h error.h http.cpp i2p_address.h parse.h socks.h resolve.h + socks_connect.h tor_address.h zmq.h) monero_add_library(net ${net_sources} ${net_headers}) target_link_libraries(net common epee ${ZMQ_LIB} ${Boost_ASIO_LIBRARY}) diff --git a/src/net/error.cpp b/src/net/error.cpp index 037f44d52..d2e713bc5 100644 --- a/src/net/error.cpp +++ b/src/net/error.cpp @@ -47,12 +47,18 @@ namespace { switch (net::error(value)) { + case net::error::bogus_dnssec: + return "Invalid response signature from DNSSEC enabled domain"; + case net::error::dns_query_failure: + return "Failed to retrieve desired DNS record"; case net::error::expected_tld: return "Expected top-level domain"; case net::error::invalid_host: return "Host value is not valid"; case net::error::invalid_i2p_address: return "Invalid I2P address"; + case net::error::invalid_mask: + return "CIDR netmask outside of 0-32 range"; case net::error::invalid_port: return "Invalid port value (expected 0-65535)"; case net::error::invalid_tor_address: @@ -71,6 +77,7 @@ namespace switch (net::error(value)) { case net::error::invalid_port: + case net::error::invalid_mask: return std::errc::result_out_of_range; case net::error::expected_tld: case net::error::invalid_tor_address: diff --git a/src/net/error.h b/src/net/error.h index 7c852dd20..746eb0ecb 100644 --- a/src/net/error.h +++ b/src/net/error.h @@ -37,13 +37,16 @@ namespace net enum class error : int { // 0 reserved for success (as per expect<T>) - expected_tld = 1, //!< Expected a tld + bogus_dnssec = 1, //!< Invalid response signature from DNSSEC enabled domain + dns_query_failure, //!< Failed to retrieve desired DNS record + expected_tld, //!< Expected a tld invalid_host, //!< Hostname is not valid invalid_i2p_address, + invalid_mask, //!< Outside of 0-32 range invalid_port, //!< Outside of 0-65535 range invalid_tor_address,//!< Invalid base32 or length unsupported_address,//!< Type not supported by `get_network_address` - invalid_mask, //!< Outside of 0-32 range + }; //! \return `std::error_category` for `net` namespace. diff --git a/src/net/i2p_address.cpp b/src/net/i2p_address.cpp index 6c03b3808..ada4eb0d3 100644 --- a/src/net/i2p_address.cpp +++ b/src/net/i2p_address.cpp @@ -38,7 +38,7 @@ #include "net/error.h" #include "serialization/keyvalue_serialization.h" #include "storages/portable_storage.h" -#include "string_tools.h" +#include "string_tools_lexical.h" namespace net { diff --git a/src/net/parse.cpp b/src/net/parse.cpp index 8a98e941a..298576ba4 100644 --- a/src/net/parse.cpp +++ b/src/net/parse.cpp @@ -31,6 +31,7 @@ #include "net/tor_address.h" #include "net/i2p_address.h" #include "string_tools.h" +#include "string_tools_lexical.h" namespace net { diff --git a/src/net/resolve.cpp b/src/net/resolve.cpp new file mode 100644 index 000000000..1b43cf6c4 --- /dev/null +++ b/src/net/resolve.cpp @@ -0,0 +1,71 @@ +// 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 "net/resolve.h" + +#include <boost/utility/string_ref.hpp> +#include "common/dns_utils.h" +#include "common/expect.h" +#include "net/error.h" + +namespace net +{ +namespace dnssec +{ + expect<service_response> resolve_hostname(const std::string& addr, const std::string& tlsa_port) + { + // use basic (blocking) unbound for now, possibly refactor later + tools::DNSResolver& resolver = tools::DNSResolver::instance(); + + bool dnssec_available = false; + bool dnssec_valid = false; + std::vector<std::string> ip_records = resolver.get_ipv4(addr, dnssec_available, dnssec_valid); + + if (dnssec_available && !dnssec_valid) + return {net::error::bogus_dnssec}; + + if (ip_records.empty()) + { + ip_records = resolver.get_ipv6(addr, dnssec_available, dnssec_valid); + if (dnssec_available && !dnssec_valid) + return {net::error::bogus_dnssec}; + if (ip_records.empty()) + return {net::error::dns_query_failure}; + } + + std::vector<std::string> tlsa{}; + if (dnssec_available && !tlsa_port.empty()) + { + tlsa = resolver.get_tlsa_tcp_record(addr, tlsa_port, dnssec_available, dnssec_valid); + if (!dnssec_valid) + return {net::error::bogus_dnssec}; + } + return {{std::move(ip_records), std::move(tlsa)}}; + } +} // dnssec +} // net diff --git a/src/net/resolve.h b/src/net/resolve.h new file mode 100644 index 000000000..46bd8e617 --- /dev/null +++ b/src/net/resolve.h @@ -0,0 +1,47 @@ +// 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 <string> +#include <vector> + +template<typename> class expect; + +namespace net +{ +namespace dnssec +{ + struct service_response + { + std::vector<std::string> ip; //!< IPv4/6 records in dotted or semicolon notation + std::vector<std::string> tlsa; //!< DANE/TLSA records + }; + + //! \return IP + (optionally) DANE/TLSA records, failing if DNSSEC signature is "bogus" + expect<service_response> resolve_hostname(const std::string& addr, const std::string& tlsa_port = {}); +} // dnssec +} // net diff --git a/src/net/socks_connect.cpp b/src/net/socks_connect.cpp index 9db9d4483..c797a24d4 100644 --- a/src/net/socks_connect.cpp +++ b/src/net/socks_connect.cpp @@ -38,6 +38,7 @@ #include "net/net_utils_base.h" #include "net/socks.h" #include "string_tools.h" +#include "string_tools_lexical.h" namespace net { diff --git a/src/net/tor_address.cpp b/src/net/tor_address.cpp index 4414861e7..a04dcb042 100644 --- a/src/net/tor_address.cpp +++ b/src/net/tor_address.cpp @@ -38,7 +38,7 @@ #include "net/error.h" #include "serialization/keyvalue_serialization.h" #include "storages/portable_storage.h" -#include "string_tools.h" +#include "string_tools_lexical.h" namespace net { diff --git a/src/p2p/CMakeLists.txt b/src/p2p/CMakeLists.txt index cfd8273b4..0cd38f253 100644 --- a/src/p2p/CMakeLists.txt +++ b/src/p2p/CMakeLists.txt @@ -26,7 +26,7 @@ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -cmake_minimum_required (VERSION 2.6) +cmake_minimum_required (VERSION 3.5) project (monero CXX) file(GLOB P2P *) diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index db931122e..f2888674b 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -344,10 +344,9 @@ namespace nodetool virtual void on_connection_close(p2p_connection_context& context); virtual void callback(p2p_connection_context& context); //----------------- i_p2p_endpoint ------------------------------------------------------------- - virtual bool relay_notify_to_list(int command, const epee::span<const uint8_t> data_buff, std::vector<std::pair<epee::net_utils::zone, boost::uuids::uuid>> connections); + virtual bool relay_notify_to_list(int command, epee::levin::message_writer message, std::vector<std::pair<epee::net_utils::zone, boost::uuids::uuid>> connections) final; virtual epee::net_utils::zone send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::relay_method tx_relay); - virtual bool invoke_command_to_peer(int command, const epee::span<const uint8_t> req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context); - virtual bool invoke_notify_to_peer(int command, const epee::span<const uint8_t> req_buff, const epee::net_utils::connection_context_base& context); + virtual bool invoke_notify_to_peer(int command, epee::levin::message_writer message, const epee::net_utils::connection_context_base& context) final; virtual bool drop_connection(const epee::net_utils::connection_context_base& context); virtual void request_callback(const epee::net_utils::connection_context_base& context); virtual void for_each_connection(std::function<bool(typename t_payload_net_handler::connection_context&, peerid_type, uint32_t)> f); diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 07b45b9bd..a0b8438b2 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -682,11 +682,13 @@ namespace nodetool full_addrs.insert("212.83.175.67:28080"); full_addrs.insert("212.83.172.165:28080"); full_addrs.insert("192.110.160.146:28080"); + full_addrs.insert("88.99.173.38:28080"); } else if (m_nettype == cryptonote::STAGENET) { full_addrs.insert("162.210.173.150:38080"); full_addrs.insert("192.110.160.146:38080"); + full_addrs.insert("88.99.173.38:38080"); } else if (m_nettype == cryptonote::FAKECHAIN) { @@ -701,6 +703,7 @@ namespace nodetool full_addrs.insert("209.250.243.248:18080"); full_addrs.insert("104.238.221.81:18080"); full_addrs.insert("66.85.74.134:18080"); + full_addrs.insert("88.99.173.38:18080"); } return full_addrs; } @@ -1203,9 +1206,8 @@ namespace nodetool if(!handle_remote_peerlist(rsp.local_peerlist_new, context)) { LOG_WARNING_CC(context, "COMMAND_TIMED_SYNC: failed to handle_remote_peerlist(...), closing connection."); - const auto remote_address = context.m_remote_address; m_network_zones.at(context.m_remote_address.get_zone()).m_net_server.get_config_object().close(context.m_connection_id ); - add_host_fail(remote_address); + add_host_fail(context.m_remote_address); } if(!context.m_is_income) m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.set_peer_just_seen(context.peer_id, context.m_remote_address, context.m_pruning_seed, context.m_rpc_port, context.m_rpc_credits_per_hash); @@ -1230,8 +1232,8 @@ namespace nodetool if(!max_index) return 0; - size_t x = crypto::rand<size_t>()%(max_index+1); - size_t res = (x*x*x)/(max_index*max_index); //parabola \/ + size_t x = crypto::rand<size_t>()%(16*max_index+1); + size_t res = (x*x*x)/(max_index*max_index*16*16*16); //parabola \/ MDEBUG("Random connection index=" << res << "(x="<< x << ", max_index=" << max_index << ")"); return res; } @@ -1369,7 +1371,7 @@ namespace nodetool if(just_take_peerlist) { zone.m_net_server.get_config_object().close(con->m_connection_id); - MDEBUG(na.str() << "CONNECTION HANDSHAKED OK AND CLOSED."); + LOG_DEBUG_CC(*con, "CONNECTION HANDSHAKED OK AND CLOSED."); return true; } @@ -1431,7 +1433,7 @@ namespace nodetool zone.m_net_server.get_config_object().close(con->m_connection_id); - MDEBUG(na.str() << "CONNECTION HANDSHAKED OK AND CLOSED."); + LOG_DEBUG_CC(*con, "CONNECTION HANDSHAKED OK AND CLOSED."); return true; } @@ -2003,15 +2005,22 @@ namespace nodetool { if (ip.empty()) continue; + auto subnet = net::get_ipv4_subnet_address(ip); + if (subnet) + { + block_subnet(*subnet, DNS_BLOCKLIST_LIFETIME); + ++good; + continue; + } const expect<epee::net_utils::network_address> parsed_addr = net::get_network_address(ip, 0); - if (!parsed_addr) + if (parsed_addr) { - MWARNING("Invalid IP address from DNS blocklist: " << ip << " - " << parsed_addr.error()); - ++bad; + block_host(*parsed_addr, DNS_BLOCKLIST_LIFETIME, true); + ++good; continue; } - block_host(*parsed_addr, DNS_BLOCKLIST_LIFETIME, true); - ++good; + MWARNING("Invalid IP address or subnet from DNS blocklist: " << ip << " - " << parsed_addr.error()); + ++bad; } } if (good > 0) @@ -2169,8 +2178,9 @@ namespace nodetool } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> - bool node_server<t_payload_net_handler>::relay_notify_to_list(int command, const epee::span<const uint8_t> data_buff, std::vector<std::pair<epee::net_utils::zone, boost::uuids::uuid>> connections) + bool node_server<t_payload_net_handler>::relay_notify_to_list(int command, epee::levin::message_writer data_buff, std::vector<std::pair<epee::net_utils::zone, boost::uuids::uuid>> connections) { + epee::byte_slice message = data_buff.finalize_notify(command); std::sort(connections.begin(), connections.end()); auto zone = m_network_zones.begin(); for(const auto& c_id: connections) @@ -2188,7 +2198,7 @@ namespace nodetool ++zone; } if (zone->first == c_id.first) - zone->second.m_net_server.get_config_object().notify(command, data_buff, c_id.second); + zone->second.m_net_server.get_config_object().send(message.clone(), c_id.second); } return true; } @@ -2255,24 +2265,13 @@ namespace nodetool } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> - bool node_server<t_payload_net_handler>::invoke_notify_to_peer(int command, const epee::span<const uint8_t> req_buff, const epee::net_utils::connection_context_base& context) + bool node_server<t_payload_net_handler>::invoke_notify_to_peer(const int command, epee::levin::message_writer message, const epee::net_utils::connection_context_base& context) { if(is_filtered_command(context.m_remote_address, command)) return false; network_zone& zone = m_network_zones.at(context.m_remote_address.get_zone()); - int res = zone.m_net_server.get_config_object().notify(command, req_buff, context.m_connection_id); - return res > 0; - } - //----------------------------------------------------------------------------------- - template<class t_payload_net_handler> - bool node_server<t_payload_net_handler>::invoke_command_to_peer(int command, const epee::span<const uint8_t> req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context) - { - if(is_filtered_command(context.m_remote_address, command)) - return false; - - network_zone& zone = m_network_zones.at(context.m_remote_address.get_zone()); - int res = zone.m_net_server.get_config_object().invoke(command, req_buff, resp_buff, context.m_connection_id); + int res = zone.m_net_server.get_config_object().send(message.finalize_notify(command), context.m_connection_id); return res > 0; } //----------------------------------------------------------------------------------- @@ -2458,14 +2457,12 @@ namespace nodetool template<class t_payload_net_handler> int node_server<t_payload_net_handler>::handle_handshake(int command, typename COMMAND_HANDSHAKE::request& arg, typename COMMAND_HANDSHAKE::response& rsp, p2p_connection_context& context) { - // copy since dropping the connection will invalidate the context, and thus the address - const auto remote_address = context.m_remote_address; - if(arg.node_data.network_id != m_network_id) { + LOG_INFO_CC(context, "WRONG NETWORK AGENT CONNECTED! id=" << arg.node_data.network_id); drop_connection(context); - add_host_fail(remote_address); + add_host_fail(context.m_remote_address); return 1; } @@ -2473,7 +2470,7 @@ namespace nodetool { LOG_WARNING_CC(context, "COMMAND_HANDSHAKE came not from incoming connection"); drop_connection(context); - add_host_fail(remote_address); + add_host_fail(context.m_remote_address); return 1; } diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h index 0da758ad4..92b7596ae 100644 --- a/src/p2p/net_node_common.h +++ b/src/p2p/net_node_common.h @@ -40,6 +40,8 @@ #include "net/net_utils_base.h" #include "p2p_protocol_defs.h" +namespace epee { namespace levin { class message_writer; } } + namespace nodetool { @@ -49,10 +51,9 @@ namespace nodetool template<class t_connection_context> struct i_p2p_endpoint { - virtual bool relay_notify_to_list(int command, const epee::span<const uint8_t> data_buff, std::vector<std::pair<epee::net_utils::zone, boost::uuids::uuid>> connections)=0; + virtual bool relay_notify_to_list(int command, epee::levin::message_writer message, std::vector<std::pair<epee::net_utils::zone, boost::uuids::uuid>> connections)=0; virtual epee::net_utils::zone send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::relay_method tx_relay)=0; - virtual bool invoke_command_to_peer(int command, const epee::span<const uint8_t> req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context)=0; - virtual bool invoke_notify_to_peer(int command, const epee::span<const uint8_t> req_buff, const epee::net_utils::connection_context_base& context)=0; + virtual bool invoke_notify_to_peer(int command, epee::levin::message_writer message, const epee::net_utils::connection_context_base& context)=0; virtual bool drop_connection(const epee::net_utils::connection_context_base& context)=0; virtual void request_callback(const epee::net_utils::connection_context_base& context)=0; virtual uint64_t get_public_connections_count()=0; @@ -71,7 +72,7 @@ namespace nodetool template<class t_connection_context> struct p2p_endpoint_stub: public i_p2p_endpoint<t_connection_context> { - virtual bool relay_notify_to_list(int command, const epee::span<const uint8_t> data_buff, std::vector<std::pair<epee::net_utils::zone, boost::uuids::uuid>> connections) + virtual bool relay_notify_to_list(int command, epee::levin::message_writer message, std::vector<std::pair<epee::net_utils::zone, boost::uuids::uuid>> connections) { return false; } @@ -79,11 +80,7 @@ namespace nodetool { return epee::net_utils::zone::invalid; } - virtual bool invoke_command_to_peer(int command, const epee::span<const uint8_t> req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context) - { - return false; - } - virtual bool invoke_notify_to_peer(int command, const epee::span<const uint8_t> req_buff, const epee::net_utils::connection_context_base& context) + virtual bool invoke_notify_to_peer(int command, epee::levin::message_writer message, const epee::net_utils::connection_context_base& context) { return true; } diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h index c794b0f3b..d8de6abe9 100644 --- a/src/p2p/net_peerlist.h +++ b/src/p2p/net_peerlist.h @@ -110,7 +110,7 @@ namespace nodetool bool get_gray_peer_by_index(peerlist_entry& p, size_t i); template<typename F> bool foreach(bool white, const F &f); void evict_host_from_white_peerlist(const peerlist_entry& pr); - bool append_with_peer_white(const peerlist_entry& pr); + bool append_with_peer_white(const peerlist_entry& pr, bool trust_last_seen = false); bool append_with_peer_gray(const peerlist_entry& pr); bool append_with_peer_anchor(const anchor_peerlist_entry& ple); bool set_peer_just_seen(peerid_type peer, const epee::net_utils::network_address& addr, uint32_t pruning_seed, uint16_t rpc_port, uint32_t rpc_credits_per_hash); @@ -329,12 +329,12 @@ namespace nodetool ple.pruning_seed = pruning_seed; ple.rpc_port = rpc_port; ple.rpc_credits_per_hash = rpc_credits_per_hash; - return append_with_peer_white(ple); + return append_with_peer_white(ple, true); CATCH_ENTRY_L0("peerlist_manager::set_peer_just_seen()", false); } //-------------------------------------------------------------------------------------------------- inline - bool peerlist_manager::append_with_peer_white(const peerlist_entry& ple) + bool peerlist_manager::append_with_peer_white(const peerlist_entry& ple, bool trust_last_seen) { TRY_ENTRY(); if(!is_host_allowed(ple.adr)) @@ -357,7 +357,8 @@ namespace nodetool new_ple.pruning_seed = by_addr_it_wt->pruning_seed; if (by_addr_it_wt->rpc_port && ple.rpc_port == 0) // guard against older nodes not passing RPC port around new_ple.rpc_port = by_addr_it_wt->rpc_port; - new_ple.last_seen = by_addr_it_wt->last_seen; // do not overwrite the last seen timestamp, incoming peer list are untrusted + if (!trust_last_seen) + new_ple.last_seen = by_addr_it_wt->last_seen; // do not overwrite the last seen timestamp, incoming peer lists are untrusted m_peers_white.replace(by_addr_it_wt, new_ple); } //remove from gray list, if need diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index 00b72123a..278ff4164 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -284,7 +284,7 @@ namespace rct { { FIELD(type) if (type == RCTTypeNull) - return ar.stream().good(); + return ar.good(); if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2 && type != RCTTypeCLSAG) return false; VARINT_FIELD(txnFee) @@ -344,7 +344,7 @@ namespace rct { ar.delimit_array(); } ar.end_array(); - return ar.stream().good(); + return ar.good(); } BEGIN_SERIALIZE_OBJECT() @@ -375,7 +375,7 @@ namespace rct { if (mixin >= 0xffffffff) return false; if (type == RCTTypeNull) - return ar.stream().good(); + return ar.good(); if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2 && type != RCTTypeCLSAG) return false; if (type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG) @@ -522,7 +522,7 @@ namespace rct { } ar.end_array(); } - return ar.stream().good(); + return ar.good(); } BEGIN_SERIALIZE_OBJECT() diff --git a/src/rpc/bootstrap_daemon.cpp b/src/rpc/bootstrap_daemon.cpp index 2fdd28406..ffea906d5 100644 --- a/src/rpc/bootstrap_daemon.cpp +++ b/src/rpc/bootstrap_daemon.cpp @@ -7,6 +7,7 @@ #include "crypto/crypto.h" #include "cryptonote_core/cryptonote_core.h" #include "misc_log_ex.h" +#include "net/parse.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "daemon.rpc.bootstrap_daemon" @@ -16,19 +17,23 @@ namespace cryptonote bootstrap_daemon::bootstrap_daemon( std::function<std::map<std::string, bool>()> get_public_nodes, - bool rpc_payment_enabled) + bool rpc_payment_enabled, + const std::string &proxy) : m_selector(new bootstrap_node::selector_auto(std::move(get_public_nodes))) , m_rpc_payment_enabled(rpc_payment_enabled) { + set_proxy(proxy); } bootstrap_daemon::bootstrap_daemon( const std::string &address, boost::optional<epee::net_utils::http::login> credentials, - bool rpc_payment_enabled) + bool rpc_payment_enabled, + const std::string &proxy) : m_selector(nullptr) , m_rpc_payment_enabled(rpc_payment_enabled) { + set_proxy(proxy); if (!set_server(address, std::move(credentials))) { throw std::runtime_error("invalid bootstrap daemon address or credentials"); @@ -78,6 +83,18 @@ namespace cryptonote return success; } + void bootstrap_daemon::set_proxy(const std::string &address) + { + if (!address.empty() && !net::get_tcp_endpoint(address)) + { + throw std::runtime_error("invalid proxy address format"); + } + if (!m_http_client.set_proxy(address)) + { + throw std::runtime_error("failed to set proxy address"); + } + } + bool bootstrap_daemon::set_server(const std::string &address, const boost::optional<epee::net_utils::http::login> &credentials /* = boost::none */) { if (!m_http_client.set_server(address, credentials)) diff --git a/src/rpc/bootstrap_daemon.h b/src/rpc/bootstrap_daemon.h index d54042b11..1e4477123 100644 --- a/src/rpc/bootstrap_daemon.h +++ b/src/rpc/bootstrap_daemon.h @@ -8,7 +8,7 @@ #include <boost/thread/mutex.hpp> #include <boost/utility/string_ref.hpp> -#include "net/http_client.h" +#include "net/http.h" #include "storages/http_abstract_invoke.h" #include "bootstrap_node_selector.h" @@ -21,11 +21,13 @@ namespace cryptonote public: bootstrap_daemon( std::function<std::map<std::string, bool>()> get_public_nodes, - bool rpc_payment_enabled); + bool rpc_payment_enabled, + const std::string &proxy); bootstrap_daemon( const std::string &address, boost::optional<epee::net_utils::http::login> credentials, - bool rpc_payment_enabled); + bool rpc_payment_enabled, + const std::string &proxy); std::string address() const noexcept; boost::optional<std::pair<uint64_t, uint64_t>> get_height(); @@ -72,12 +74,14 @@ namespace cryptonote return handle_result(result, result_struct.status); } + void set_proxy(const std::string &address); + private: bool set_server(const std::string &address, const boost::optional<epee::net_utils::http::login> &credentials = boost::none); bool switch_server_if_needed(); private: - epee::net_utils::http::http_simple_client m_http_client; + net::http::client m_http_client; const bool m_rpc_payment_enabled; const std::unique_ptr<bootstrap_node::selector> m_selector; boost::mutex m_selector_mutex; diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 0c6e2ec8f..8d8a68efb 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -30,6 +30,7 @@ #include <boost/preprocessor/stringize.hpp> #include <boost/uuid/nil_generator.hpp> +#include <boost/filesystem.hpp> #include "include_base_utils.h" #include "string_tools.h" using namespace epee; @@ -155,6 +156,7 @@ namespace cryptonote command_line::add_arg(desc, arg_restricted_rpc); command_line::add_arg(desc, arg_bootstrap_daemon_address); command_line::add_arg(desc, arg_bootstrap_daemon_login); + command_line::add_arg(desc, arg_bootstrap_daemon_proxy); cryptonote::rpc_args::init_options(desc, true); command_line::add_arg(desc, arg_rpc_payment_address); command_line::add_arg(desc, arg_rpc_payment_difficulty); @@ -173,7 +175,10 @@ namespace cryptonote , m_rpc_payment_allow_free_loopback(false) {} //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::set_bootstrap_daemon(const std::string &address, const std::string &username_password) + bool core_rpc_server::set_bootstrap_daemon( + const std::string &address, + const std::string &username_password, + const std::string &proxy) { boost::optional<epee::net_utils::http::login> credentials; const auto loc = username_password.find(':'); @@ -181,7 +186,7 @@ namespace cryptonote { credentials = epee::net_utils::http::login(username_password.substr(0, loc), username_password.substr(loc + 1)); } - return set_bootstrap_daemon(address, credentials); + return set_bootstrap_daemon(address, credentials, proxy); } //------------------------------------------------------------------------------------------------------------------------------ std::map<std::string, bool> core_rpc_server::get_public_nodes(uint32_t credits_per_hash_threshold/* = 0*/) @@ -218,7 +223,10 @@ namespace cryptonote return result; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::set_bootstrap_daemon(const std::string &address, const boost::optional<epee::net_utils::http::login> &credentials) + bool core_rpc_server::set_bootstrap_daemon( + const std::string &address, + const boost::optional<epee::net_utils::http::login> &credentials, + const std::string &proxy) { boost::unique_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex); @@ -234,11 +242,11 @@ namespace cryptonote 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)); + m_bootstrap_daemon.reset(new bootstrap_daemon(std::move(get_nodes), rpc_payment_enabled, proxy)); } else { - m_bootstrap_daemon.reset(new bootstrap_daemon(address, credentials, rpc_payment_enabled)); + m_bootstrap_daemon.reset(new bootstrap_daemon(address, credentials, rpc_payment_enabled, proxy)); } m_should_use_bootstrap_daemon = m_bootstrap_daemon.get() != nullptr; @@ -320,8 +328,10 @@ namespace cryptonote MWARNING("The RPC server is accessible from the outside, but no RPC payment was setup. RPC access will be free for all."); } - if (!set_bootstrap_daemon(command_line::get_arg(vm, arg_bootstrap_daemon_address), - command_line::get_arg(vm, arg_bootstrap_daemon_login))) + if (!set_bootstrap_daemon( + command_line::get_arg(vm, arg_bootstrap_daemon_address), + command_line::get_arg(vm, arg_bootstrap_daemon_login), + command_line::get_arg(vm, arg_bootstrap_daemon_proxy))) { MFATAL("Failed to parse bootstrap daemon address"); return false; @@ -595,7 +605,7 @@ namespace cryptonote if (max_blocks == 0) { res.status = CORE_RPC_STATUS_PAYMENT_REQUIRED; - return false; + return true; } } @@ -604,7 +614,7 @@ namespace cryptonote { res.status = "Failed"; add_host_fail(ctx); - return false; + return true; } CHECK_PAYMENT_SAME_TS(req, res, bs.size() * COST_PER_BLOCK); @@ -640,12 +650,12 @@ namespace cryptonote if (!r) { res.status = "Failed"; - return false; + return true; } if (indices.size() != n_txes_to_lookup || res.output_indices.back().indices.size() != (req.no_miner_tx ? 1 : 0)) { res.status = "Failed"; - return false; + return true; } for (size_t i = 0; i < indices.size(); ++i) res.output_indices.back().indices.push_back({std::move(indices[i])}); @@ -668,7 +678,7 @@ namespace cryptonote if(!m_core.get_alternative_blocks(blks)) { res.status = "Failed"; - return false; + return true; } res.blks_hashes.reserve(blks.size()); @@ -739,7 +749,7 @@ namespace cryptonote { res.status = "Failed"; add_host_fail(ctx); - return false; + return true; } CHECK_PAYMENT_SAME_TS(req, res, res.m_block_ids.size() * COST_PER_BLOCK_HASH); @@ -1034,6 +1044,7 @@ namespace cryptonote if (e.in_pool) { e.block_height = e.block_timestamp = std::numeric_limits<uint64_t>::max(); + e.confirmations = 0; auto it = per_tx_pool_tx_info.find(tx_hash); if (it != per_tx_pool_tx_info.end()) { @@ -1052,6 +1063,7 @@ namespace cryptonote else { e.block_height = m_core.get_blockchain_storage().get_db().get_tx_block_height(tx_hash); + e.confirmations = m_core.get_current_blockchain_height() - e.block_height; e.block_timestamp = m_core.get_blockchain_storage().get_db().get_block_timestamp(e.block_height); e.received_timestamp = 0; e.double_spend_seen = false; @@ -1070,7 +1082,7 @@ namespace cryptonote if (!r) { res.status = "Failed"; - return false; + return true; } } } @@ -1446,7 +1458,8 @@ namespace cryptonote res.status = peer_list_res.status; if (!success) { - return false; + res.status = "Failed to get peer list"; + return true; } if (res.status != CORE_RPC_STATUS_OK) { @@ -1617,15 +1630,15 @@ namespace cryptonote { credentials = epee::net_utils::http::login(req.username, req.password); } - - if (set_bootstrap_daemon(req.address, credentials)) + + if (set_bootstrap_daemon(req.address, credentials, req.proxy)) { res.status = CORE_RPC_STATUS_OK; } else { res.status = "Failed to set bootstrap daemon"; - } + } return true; } @@ -1648,7 +1661,7 @@ namespace cryptonote if (m_should_use_bootstrap_daemon) { res.status = "This command is unsupported for bootstrap daemon"; - return false; + return true; } } res.count = m_core.get_current_blockchain_height(); @@ -1664,7 +1677,7 @@ namespace cryptonote if (m_should_use_bootstrap_daemon) { res = "This command is unsupported for bootstrap daemon"; - return false; + return true; } } if(req.size() != 1) @@ -1974,7 +1987,8 @@ namespace cryptonote boost::shared_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex); if (m_should_use_bootstrap_daemon) { - res.status = "This command is unsupported for bootstrap daemon"; + error_resp.code = CORE_RPC_ERROR_CODE_UNSUPPORTED_BOOTSTRAP; + error_resp.message = "This command is unsupported for bootstrap daemon"; return false; } } @@ -2666,7 +2680,7 @@ namespace cryptonote if (!m_core.get_blockchain_storage().flush_txes_from_pool(txids)) { res.status = "Failed to remove one or more tx(es)"; - return false; + return true; } if (failed) @@ -2675,7 +2689,7 @@ namespace cryptonote res.status = "Failed to parse txid"; else res.status = "Failed to parse some of the txids"; - return false; + return true; } res.status = CORE_RPC_STATUS_OK; @@ -2834,7 +2848,7 @@ namespace cryptonote if (req.limit_down != -1) { res.status = CORE_RPC_ERROR_CODE_WRONG_PARAM; - return false; + return true; } epee::net_utils::connection_basic::set_rate_down_limit(nodetool::default_limit_down); } @@ -2848,7 +2862,7 @@ namespace cryptonote if (req.limit_up != -1) { res.status = CORE_RPC_ERROR_CODE_WRONG_PARAM; - return false; + return true; } epee::net_utils::connection_basic::set_rate_up_limit(nodetool::default_limit_up); } @@ -2952,17 +2966,17 @@ namespace cryptonote if (!tools::download(path.string(), res.auto_uri)) { MERROR("Failed to download " << res.auto_uri); - return false; + return true; } if (!tools::sha256sum(path.string(), file_hash)) { MERROR("Failed to hash " << path); - return false; + return true; } if (hash != epee::string_tools::pod_to_hex(file_hash)) { MERROR("Download from " << res.auto_uri << " does not match the expected hash"); - return false; + return true; } MINFO("New version downloaded to " << path); } @@ -3037,6 +3051,8 @@ namespace cryptonote if (failed) { + error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; + error_resp.message = res.status; return false; } @@ -3148,7 +3164,7 @@ namespace cryptonote if (!req.binary) { res.status = "Binary only call"; - return false; + return true; } try { @@ -3160,7 +3176,7 @@ namespace cryptonote if (!data) { res.status = "Failed to get output distribution"; - return false; + return true; } res.distributions.push_back({std::move(*data), amount, "", req.binary, req.compress}); @@ -3169,7 +3185,7 @@ namespace cryptonote catch (const std::exception &e) { res.status = "Failed to get output distribution"; - return false; + return true; } res.status = CORE_RPC_STATUS_OK; @@ -3403,7 +3419,8 @@ namespace cryptonote if (!m_rpc_payment) { - res.status = "Payments not enabled"; + error_resp.code = CORE_RPC_ERROR_CODE_PAYMENTS_NOT_ENABLED; + error_resp.message = "Payments not enabled"; return false; } @@ -3431,7 +3448,8 @@ namespace cryptonote if (!m_rpc_payment) { - res.status = "Payments not enabled"; + error_resp.code = CORE_RPC_ERROR_CODE_PAYMENTS_NOT_ENABLED; + error_resp.message = "Payments not enabled"; return false; } @@ -3488,6 +3506,12 @@ namespace cryptonote , "" }; + const command_line::arg_descriptor<std::string> core_rpc_server::arg_bootstrap_daemon_proxy = { + "bootstrap-daemon-proxy" + , "<ip>:<port> socks proxy to use for bootstrap daemon connections" + , "" + }; + const command_line::arg_descriptor<std::string> core_rpc_server::arg_rpc_payment_address = { "rpc-payment-address" , "Restrict RPC to clients sending micropayment to this address" diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 6736a6b7f..b21e43ab0 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -72,6 +72,7 @@ namespace cryptonote static const command_line::arg_descriptor<bool> arg_rpc_ssl_allow_any_cert; static const command_line::arg_descriptor<std::string> arg_bootstrap_daemon_address; static const command_line::arg_descriptor<std::string> arg_bootstrap_daemon_login; + static const command_line::arg_descriptor<std::string> arg_bootstrap_daemon_proxy; static const command_line::arg_descriptor<std::string> arg_rpc_payment_address; static const command_line::arg_descriptor<uint64_t> arg_rpc_payment_difficulty; static const command_line::arg_descriptor<uint64_t> arg_rpc_payment_credits; @@ -270,8 +271,14 @@ private: uint64_t get_block_reward(const block& blk); bool fill_block_header_response(const block& blk, bool orphan_status, uint64_t height, const crypto::hash& hash, block_header_response& response, bool fill_pow_hash); std::map<std::string, bool> get_public_nodes(uint32_t credits_per_hash_threshold = 0); - bool set_bootstrap_daemon(const std::string &address, const std::string &username_password); - bool set_bootstrap_daemon(const std::string &address, const boost::optional<epee::net_utils::http::login> &credentials); + bool set_bootstrap_daemon( + const std::string &address, + const std::string &username_password, + const std::string &proxy); + bool set_bootstrap_daemon( + const std::string &address, + const boost::optional<epee::net_utils::http::login> &credentials, + const std::string &proxy); enum invoke_http_mode { JON, BIN, JON_RPC }; template <typename COMMAND_TYPE> bool use_bootstrap_daemon_if_necessary(const invoke_http_mode &mode, const std::string &command_name, const typename COMMAND_TYPE::request& req, typename COMMAND_TYPE::response& res, bool &r); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index e7bcb5570..ff8c98b98 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -88,7 +88,7 @@ namespace cryptonote // advance which version they will stop working with // Don't go over 32767 for any of these #define CORE_RPC_VERSION_MAJOR 3 -#define CORE_RPC_VERSION_MINOR 6 +#define CORE_RPC_VERSION_MINOR 7 #define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR) @@ -350,6 +350,7 @@ namespace cryptonote bool in_pool; bool double_spend_seen; uint64_t block_height; + uint64_t confirmations; uint64_t block_timestamp; uint64_t received_timestamp; std::vector<uint64_t> output_indices; @@ -367,6 +368,7 @@ namespace cryptonote if (!this_ref.in_pool) { KV_SERIALIZE(block_height) + KV_SERIALIZE(confirmations) KV_SERIALIZE(block_timestamp) KV_SERIALIZE(output_indices) } @@ -1659,11 +1661,13 @@ namespace cryptonote std::string address; std::string username; std::string password; + std::string proxy; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(address) KV_SERIALIZE(username) KV_SERIALIZE(password) + KV_SERIALIZE(proxy) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; diff --git a/src/rpc/core_rpc_server_error_codes.h b/src/rpc/core_rpc_server_error_codes.h index 1458049ab..232df0373 100644 --- a/src/rpc/core_rpc_server_error_codes.h +++ b/src/rpc/core_rpc_server_error_codes.h @@ -49,6 +49,8 @@ #define CORE_RPC_ERROR_CODE_DUPLICATE_PAYMENT -17 #define CORE_RPC_ERROR_CODE_STALE_PAYMENT -18 #define CORE_RPC_ERROR_CODE_RESTRICTED -19 +#define CORE_RPC_ERROR_CODE_UNSUPPORTED_BOOTSTRAP -20 +#define CORE_RPC_ERROR_CODE_PAYMENTS_NOT_ENABLED -21 static inline const char *get_rpc_server_error_message(int64_t code) { @@ -72,6 +74,8 @@ static inline const char *get_rpc_server_error_message(int64_t code) 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"; + case CORE_RPC_ERROR_CODE_UNSUPPORTED_BOOTSTRAP: return "Command is unsupported in bootstrap mode"; + case CORE_RPC_ERROR_CODE_PAYMENTS_NOT_ENABLED: return "Payments not enabled"; default: MERROR("Unknown error: " << code); return "Unknown error"; } } diff --git a/src/rpc/rpc_payment.cpp b/src/rpc/rpc_payment.cpp index 176f11fa3..f0c513911 100644 --- a/src/rpc/rpc_payment.cpp +++ b/src/rpc/rpc_payment.cpp @@ -27,6 +27,7 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <boost/archive/portable_binary_iarchive.hpp> +#include <boost/filesystem.hpp> #include "cryptonote_config.h" #include "include_base_utils.h" #include "string_tools.h" @@ -292,12 +293,13 @@ namespace cryptonote 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); + std::string bytes(std::istream_iterator<char>{data}, std::istream_iterator<char>{}); if (!data.fail()) { bool loaded = false; try { - binary_archive<false> ar(data); + binary_archive<false> ar{epee::strspan<std::uint8_t>(bytes)}; if (::serialization::serialize(ar, *this)) if (::serialization::check_stream_state(ar)) loaded = true; @@ -305,6 +307,8 @@ namespace cryptonote catch (...) {} if (!loaded) { + bytes.clear(); + bytes.shrink_to_fit(); try { boost::archive::portable_binary_iarchive a(data); diff --git a/src/serialization/binary_archive.h b/src/serialization/binary_archive.h index 80104c8f7..acda70039 100644 --- a/src/serialization/binary_archive.h +++ b/src/serialization/binary_archive.h @@ -36,9 +36,11 @@ #include <cassert> #include <iostream> #include <iterator> +#include <boost/endian/conversion.hpp> #include <boost/type_traits/make_unsigned.hpp> #include "common/varint.h" +#include "span.h" #include "warnings.h" /* I have no clue what these lines means */ @@ -55,16 +57,15 @@ DISABLE_VS_WARNINGS(4244) * purpse is to define the functions used for the binary_archive. Its * a header, basically. I think it was declared simply to save typing... */ -template <class Stream, bool IsSaving> +template <bool IsSaving> struct binary_archive_base { - typedef Stream stream_type; - typedef binary_archive_base<Stream, IsSaving> base_type; + typedef binary_archive_base<IsSaving> base_type; typedef boost::mpl::bool_<IsSaving> is_saving; typedef uint8_t variant_tag_type; - explicit binary_archive_base(stream_type &s) : stream_(s) { } + explicit binary_archive_base() { } /* definition of standard API functions */ void tag(const char *) { } @@ -72,12 +73,6 @@ struct binary_archive_base void end_object() { } void begin_variant() { } void end_variant() { } - /* I just want to leave a comment saying how this line really shows - flaws in the ownership model of many OOP languages, that is all. */ - stream_type &stream() { return stream_; } - -protected: - stream_type &stream_; }; /* \struct binary_archive @@ -95,15 +90,18 @@ struct binary_archive; template <> -struct binary_archive<false> : public binary_archive_base<std::istream, false> +struct binary_archive<false> : public binary_archive_base<false> { + explicit binary_archive(epee::span<const std::uint8_t> s) + : base_type(), bytes_(s), begin_(s.begin()), good_(true), varint_bug_backward_compatibility_(false) + {} - explicit binary_archive(stream_type &s) : base_type(s) { - stream_type::pos_type pos = stream_.tellg(); - stream_.seekg(0, std::ios_base::end); - eof_pos_ = stream_.tellg(); - stream_.seekg(pos); - } + bool good() const noexcept { return good_; } + void set_fail() noexcept { good_ = false; } + + //! If implementing as `std::istream`, reset stream error state after `peek()` call. + bool eof() const noexcept { return bytes_.empty(); } + std::size_t getpos() const noexcept { return bytes_.begin() - begin_; } template <class T> void serialize_int(T &v) @@ -116,24 +114,24 @@ struct binary_archive<false> : public binary_archive_base<std::istream, false> * \brief serializes an unsigned integer */ template <class T> - void serialize_uint(T &v, size_t width = sizeof(T)) + void serialize_uint(T &v) { - T ret = 0; - unsigned shift = 0; - for (size_t i = 0; i < width; i++) { - //std::cerr << "tell: " << stream_.tellg() << " value: " << ret << std::endl; - char c; - stream_.get(c); - T b = (unsigned char)c; - ret += (b << shift); // can this be changed to OR, i think it can. - shift += 8; + const std::size_t actual = bytes_.remove_prefix(sizeof(T)); + good_ &= (actual == sizeof(T)); + if (actual == sizeof(T)) + { + std::memcpy(std::addressof(v), bytes_.data() - sizeof(T), sizeof(T)); + boost::endian::little_to_native_inplace(v); // epee isn't templated } - v = ret; + else + v = 0; // ensures initialization } void serialize_blob(void *buf, size_t len, const char *delimiter="") { - stream_.read((char *)buf, len); + const std::size_t actual = bytes_.remove_prefix(len); + good_ &= (len == actual); + std::memcpy(buf, bytes_.data() - actual, actual); } template <class T> @@ -145,9 +143,11 @@ struct binary_archive<false> : public binary_archive_base<std::istream, false> template <class T> void serialize_uvarint(T &v) { - typedef std::istreambuf_iterator<char> it; - if (tools::read_varint(it(stream_), it(), v) < 0) - stream_.setstate(std::ios_base::failbit); + auto current = bytes_.cbegin(); + auto end = bytes_.cend(); + good_ &= (0 <= tools::read_varint(current, end, v)); + current = std::min(current, bytes_.cend()); + bytes_ = {current, std::size_t(bytes_.cend() - current)}; } void begin_array(size_t &s) @@ -166,21 +166,26 @@ struct binary_archive<false> : public binary_archive_base<std::istream, false> serialize_int(t); } - size_t remaining_bytes() { - if (!stream_.good()) - return 0; - //std::cerr << "tell: " << stream_.tellg() << std::endl; - assert(stream_.tellg() <= eof_pos_); - return eof_pos_ - stream_.tellg(); - } + size_t remaining_bytes() const noexcept { return good() ? bytes_.size() : 0; } + void enable_varint_bug_backward_compatibility() { varint_bug_backward_compatibility_ = true; } + bool varint_bug_backward_compatibility_enabled() const { return varint_bug_backward_compatibility_; } protected: - std::streamoff eof_pos_; + epee::span<const std::uint8_t> bytes_; + std::uint8_t const* const begin_; + bool good_; + bool varint_bug_backward_compatibility_; }; template <> -struct binary_archive<true> : public binary_archive_base<std::ostream, true> +struct binary_archive<true> : public binary_archive_base<true> { - explicit binary_archive(stream_type &s) : base_type(s) { } + typedef std::ostream stream_type; + explicit binary_archive(stream_type &s) : base_type(), stream_(s) { } + + bool good() const { return stream_.good(); } + void set_fail() { stream_.setstate(std::ios::failbit); } + + std::streampos getpos() const { return stream_.tellp(); } template <class T> void serialize_int(T v) @@ -227,6 +232,10 @@ struct binary_archive<true> : public binary_archive_base<std::ostream, true> void write_variant_tag(variant_tag_type t) { serialize_int(t); } + + bool varint_bug_backward_compatibility_enabled() const { return false; } +protected: + stream_type& stream_; }; POP_WARNINGS diff --git a/src/serialization/binary_utils.h b/src/serialization/binary_utils.h index 8635585af..8444f220e 100644 --- a/src/serialization/binary_utils.h +++ b/src/serialization/binary_utils.h @@ -39,8 +39,7 @@ namespace serialization { template <class T> bool parse_binary(const std::string &blob, T &v) { - std::istringstream istr(blob); - binary_archive<false> iar(istr); + binary_archive<false> iar{epee::strspan<std::uint8_t>(blob)}; return ::serialization::serialize(iar, v); } diff --git a/src/serialization/container.h b/src/serialization/container.h index d5e75bb4f..77681ec93 100644 --- a/src/serialization/container.h +++ b/src/serialization/container.h @@ -32,22 +32,27 @@ namespace serialization { namespace detail { - template <typename Archive, class T> - bool serialize_container_element(Archive& ar, T& e) + template<typename T> + inline constexpr bool use_container_varint() noexcept { - return ::do_serialize(ar, e); + return std::is_integral<T>::value && std::is_unsigned<T>::value && sizeof(T) > 1; } - template <typename Archive> - bool serialize_container_element(Archive& ar, uint32_t& e) + template <typename Archive, class T> + typename std::enable_if<!use_container_varint<T>(), bool>::type + serialize_container_element(Archive& ar, T& e) { - ar.serialize_varint(e); - return true; + return ::do_serialize(ar, e); } - template <typename Archive> - bool serialize_container_element(Archive& ar, uint64_t& e) + template<typename Archive, typename T> + typename std::enable_if<use_container_varint<T>(), bool>::type + serialize_container_element(Archive& ar, T& e) { + static constexpr const bool previously_varint = std::is_same<uint64_t, T>() || std::is_same<uint32_t, T>(); + + if (!previously_varint && ar.varint_bug_backward_compatibility_enabled() && !typename Archive::is_saving()) + return ::do_serialize(ar, e); ar.serialize_varint(e); return true; } @@ -62,13 +67,13 @@ bool do_serialize_container(Archive<false> &ar, C &v) { size_t cnt; ar.begin_array(cnt); - if (!ar.stream().good()) + if (!ar.good()) return false; v.clear(); // very basic sanity check if (ar.remaining_bytes() < cnt) { - ar.stream().setstate(std::ios::failbit); + ar.set_fail(); return false; } @@ -81,7 +86,7 @@ bool do_serialize_container(Archive<false> &ar, C &v) if (!::serialization::detail::serialize_container_element(ar, e)) return false; ::serialization::detail::do_add(v, std::move(e)); - if (!ar.stream().good()) + if (!ar.good()) return false; } ar.end_array(); @@ -95,13 +100,13 @@ bool do_serialize_container(Archive<true> &ar, C &v) ar.begin_array(cnt); for (auto i = v.begin(); i != v.end(); ++i) { - if (!ar.stream().good()) + if (!ar.good()) return false; if (i != v.begin()) ar.delimit_array(); if(!::serialization::detail::serialize_container_element(ar, (typename C::value_type&)*i)) return false; - if (!ar.stream().good()) + if (!ar.good()) return false; } ar.end_array(); diff --git a/src/serialization/crypto.h b/src/serialization/crypto.h index 42bd06e92..d635a9ee9 100644 --- a/src/serialization/crypto.h +++ b/src/serialization/crypto.h @@ -47,7 +47,7 @@ bool do_serialize(Archive<false> &ar, std::vector<crypto::signature> &v) // very basic sanity check if (ar.remaining_bytes() < cnt*sizeof(crypto::signature)) { - ar.stream().setstate(std::ios::failbit); + ar.set_fail(); return false; } @@ -55,7 +55,7 @@ bool do_serialize(Archive<false> &ar, std::vector<crypto::signature> &v) for (size_t i = 0; i < cnt; i++) { v.resize(i+1); ar.serialize_blob(&(v[i]), sizeof(crypto::signature), ""); - if (!ar.stream().good()) + if (!ar.good()) return false; } return true; @@ -70,7 +70,7 @@ bool do_serialize(Archive<true> &ar, std::vector<crypto::signature> &v) size_t cnt = v.size(); for (size_t i = 0; i < cnt; i++) { ar.serialize_blob(&(v[i]), sizeof(crypto::signature), ""); - if (!ar.stream().good()) + if (!ar.good()) return false; } ar.end_string(); diff --git a/src/serialization/debug_archive.h b/src/serialization/debug_archive.h index b04d6ae19..e8ccb9a58 100644 --- a/src/serialization/debug_archive.h +++ b/src/serialization/debug_archive.h @@ -38,6 +38,7 @@ struct debug_archive : public json_archive<W> { typedef typename json_archive<W>::stream_type stream_type; debug_archive(stream_type &s) : json_archive<W>(s) { } + stream_type& stream() { return this->stream_; } }; template <class T> diff --git a/src/serialization/difficulty_type.h b/src/serialization/difficulty_type.h index 56c0312e7..75d1fd13f 100644 --- a/src/serialization/difficulty_type.h +++ b/src/serialization/difficulty_type.h @@ -38,10 +38,10 @@ inline bool do_serialize(Archive<false>& ar, cryptonote::difficulty_type &diff) { uint64_t hi, lo; ar.serialize_varint(hi); - if (!ar.stream().good()) + if (!ar.good()) return false; ar.serialize_varint(lo); - if (!ar.stream().good()) + if (!ar.good()) return false; diff = hi; diff <<= 64; @@ -52,13 +52,13 @@ inline bool do_serialize(Archive<false>& ar, cryptonote::difficulty_type &diff) template <template <bool> class Archive> inline bool do_serialize(Archive<true>& ar, cryptonote::difficulty_type &diff) { - if (!ar.stream().good()) + if (!ar.good()) return false; const uint64_t hi = ((diff >> 64) & 0xffffffffffffffff).convert_to<uint64_t>(); const uint64_t lo = (diff & 0xffffffffffffffff).convert_to<uint64_t>(); ar.serialize_varint(hi); ar.serialize_varint(lo); - if (!ar.stream().good()) + if (!ar.good()) return false; return true; } diff --git a/src/serialization/json_archive.h b/src/serialization/json_archive.h index 50dd5bbd0..bab6dfa94 100644 --- a/src/serialization/json_archive.h +++ b/src/serialization/json_archive.h @@ -58,6 +58,10 @@ struct json_archive_base json_archive_base(stream_type &s, bool indent = false) : stream_(s), indent_(indent), object_begin(false), depth_(0) { } + bool good() const { return stream_.good(); } + void set_fail() { stream_.setstate(std::ios::failbit); } + void clear_fail() { stream_.clear(); } + void tag(const char *tag) { if (!object_begin) stream_ << ", "; @@ -82,7 +86,8 @@ struct json_archive_base void begin_variant() { begin_object(); } void end_variant() { end_object(); } - Stream &stream() { return stream_; } + + bool varint_bug_backward_compatibility_enabled() const { return false; } protected: void make_indent() @@ -115,6 +120,8 @@ struct json_archive<true> : public json_archive_base<std::ostream, true> { json_archive(stream_type &s, bool indent = false) : base_type(s, indent), inner_array_size_(0) { } + std::streampos getpos() const { return stream_.tellp(); } + template<typename T> static auto promote_to_printable_integer_type(T v) -> decltype(+v) { diff --git a/src/serialization/json_object.h b/src/serialization/json_object.h index de14c8911..35ea990b3 100644 --- a/src/serialization/json_object.h +++ b/src/serialization/json_object.h @@ -365,7 +365,7 @@ inline typename std::enable_if<sfinae::is_vector_like<Vec>::value, void>::type t static_assert(!std::is_same<value_type, unsigned char>::value, "encoding an array of unsigned char is faster as hex"); dest.StartArray(); - for (const auto& t : vec) + for (auto t : vec) toJsonValue(dest, t); dest.EndArray(); } diff --git a/src/serialization/pair.h b/src/serialization/pair.h index 18280d837..2d9a89242 100644 --- a/src/serialization/pair.h +++ b/src/serialization/pair.h @@ -30,21 +30,34 @@ #pragma once #include <memory> +#include <boost/type_traits/make_unsigned.hpp> #include "serialization.h" namespace serialization { namespace detail { + template<typename T> + inline constexpr bool use_pair_varint() noexcept + { + return std::is_integral<T>::value && std::is_unsigned<T>::value && sizeof(T) > 1; + } + template <typename Archive, class T> - bool serialize_pair_element(Archive& ar, T& e) + typename std::enable_if<!use_pair_varint<T>(), bool>::type + serialize_pair_element(Archive& ar, T& e) { return ::do_serialize(ar, e); } - template <typename Archive> - bool serialize_pair_element(Archive& ar, uint64_t& e) + template<typename Archive, typename T> + typename std::enable_if<use_pair_varint<T>(), bool>::type + serialize_pair_element(Archive& ar, T& e) { + static constexpr const bool previously_varint = std::is_same<uint64_t, T>(); + + if (!previously_varint && ar.varint_bug_backward_compatibility_enabled() && !typename Archive::is_saving()) + return ::do_serialize(ar, e); ar.serialize_varint(e); return true; } @@ -56,19 +69,19 @@ inline bool do_serialize(Archive<false>& ar, std::pair<F,S>& p) { size_t cnt; ar.begin_array(cnt); - if (!ar.stream().good()) + if (!ar.good()) return false; if (cnt != 2) return false; if (!::serialization::detail::serialize_pair_element(ar, p.first)) return false; - if (!ar.stream().good()) + if (!ar.good()) return false; ar.delimit_array(); if (!::serialization::detail::serialize_pair_element(ar, p.second)) return false; - if (!ar.stream().good()) + if (!ar.good()) return false; ar.end_array(); @@ -79,16 +92,16 @@ template <template <bool> class Archive, class F, class S> inline bool do_serialize(Archive<true>& ar, std::pair<F,S>& p) { ar.begin_array(2); - if (!ar.stream().good()) + if (!ar.good()) return false; if(!::serialization::detail::serialize_pair_element(ar, p.first)) return false; - if (!ar.stream().good()) + if (!ar.good()) return false; ar.delimit_array(); if(!::serialization::detail::serialize_pair_element(ar, p.second)) return false; - if (!ar.stream().good()) + if (!ar.good()) return false; ar.end_array(); return true; diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h index 3ebeed171..4acadeab3 100644 --- a/src/serialization/serialization.h +++ b/src/serialization/serialization.h @@ -213,7 +213,7 @@ inline bool do_serialize(Archive &ar, bool &v) * \brief self-explanatory */ #define END_SERIALIZE() \ - return ar.stream().good(); \ + return ar.good(); \ } /*! \macro VALUE(f) @@ -223,7 +223,7 @@ inline bool do_serialize(Archive &ar, bool &v) do { \ ar.tag(#f); \ bool r = ::do_serialize(ar, f); \ - if (!r || !ar.stream().good()) return false; \ + if (!r || !ar.good()) return false; \ } while(0); /*! \macro FIELD_N(t,f) @@ -234,7 +234,7 @@ inline bool do_serialize(Archive &ar, bool &v) do { \ ar.tag(t); \ bool r = ::do_serialize(ar, f); \ - if (!r || !ar.stream().good()) return false; \ + if (!r || !ar.good()) return false; \ } while(0); /*! \macro FIELD(f) @@ -245,7 +245,7 @@ inline bool do_serialize(Archive &ar, bool &v) do { \ ar.tag(#f); \ bool r = ::do_serialize(ar, f); \ - if (!r || !ar.stream().good()) return false; \ + if (!r || !ar.good()) return false; \ } while(0); /*! \macro FIELDS(f) @@ -255,7 +255,7 @@ inline bool do_serialize(Archive &ar, bool &v) #define FIELDS(f) \ do { \ bool r = ::do_serialize(ar, f); \ - if (!r || !ar.stream().good()) return false; \ + if (!r || !ar.good()) return false; \ } while(0); /*! \macro VARINT_FIELD(f) @@ -265,7 +265,7 @@ inline bool do_serialize(Archive &ar, bool &v) do { \ ar.tag(#f); \ ar.serialize_varint(f); \ - if (!ar.stream().good()) return false; \ + if (!ar.good()) return false; \ } while(0); /*! \macro VARINT_FIELD_N(t, f) @@ -276,7 +276,7 @@ inline bool do_serialize(Archive &ar, bool &v) do { \ ar.tag(t); \ ar.serialize_varint(f); \ - if (!ar.stream().good()) return false; \ + if (!ar.good()) return false; \ } while(0); /*! \macro MAGIC_FIELD(m) @@ -286,7 +286,7 @@ inline bool do_serialize(Archive &ar, bool &v) do { \ ar.tag("magic"); \ ar.serialize_blob((void*)magic.data(), magic.size()); \ - if (!ar.stream().good()) return false; \ + if (!ar.good()) return false; \ if (magic != m) return false; \ } while(0); @@ -297,7 +297,7 @@ inline bool do_serialize(Archive &ar, bool &v) do { \ ar.tag("version"); \ ar.serialize_varint(version); \ - if (!ar.stream().good()) return false; \ + if (!ar.good()) return false; \ } while(0); @@ -339,10 +339,10 @@ namespace serialization { * * \brief self explanatory */ - template<class Stream> - bool do_check_stream_state(Stream& s, boost::mpl::bool_<true>, bool noeof) + template<class Archive> + bool do_check_stream_state(Archive& ar, boost::mpl::bool_<true>, bool noeof) { - return s.good(); + return ar.good(); } /*! \fn do_check_stream_state * @@ -350,15 +350,13 @@ namespace serialization { * * \detailed Also checks to make sure that the stream is not at EOF */ - template<class Stream> - bool do_check_stream_state(Stream& s, boost::mpl::bool_<false>, bool noeof) + template<class Archive> + bool do_check_stream_state(Archive& ar, boost::mpl::bool_<false>, bool noeof) { bool result = false; - if (s.good()) + if (ar.good()) { - std::ios_base::iostate state = s.rdstate(); - result = noeof || EOF == s.peek(); - s.clear(state); + result = noeof || ar.eof(); } return result; } @@ -371,7 +369,7 @@ namespace serialization { template<class Archive> bool check_stream_state(Archive& ar, bool noeof = false) { - return detail::do_check_stream_state(ar.stream(), typename Archive::is_saving(), noeof); + return detail::do_check_stream_state(ar, typename Archive::is_saving(), noeof); } /*! \fn serialize diff --git a/src/serialization/string.h b/src/serialization/string.h index f5f69833a..f1f8f4ab0 100644 --- a/src/serialization/string.h +++ b/src/serialization/string.h @@ -39,7 +39,7 @@ inline bool do_serialize(Archive<false>& ar, std::string& str) ar.serialize_varint(size); if (ar.remaining_bytes() < size) { - ar.stream().setstate(std::ios::failbit); + ar.set_fail(); return false; } diff --git a/src/serialization/variant.h b/src/serialization/variant.h index b08cf6bf0..6debb63d1 100644 --- a/src/serialization/variant.h +++ b/src/serialization/variant.h @@ -74,7 +74,7 @@ struct variant_reader current_type x; if(!::do_serialize(ar, x)) { - ar.stream().setstate(std::ios::failbit); + ar.set_fail(); return false; } v = x; @@ -95,7 +95,7 @@ struct variant_reader<Archive, Variant, TBegin, TBegin> static inline bool read(Archive &ar, Variant &v, variant_tag_type t) { - ar.stream().setstate(std::ios::failbit); + ar.set_fail(); return false; } }; @@ -116,7 +116,7 @@ struct serializer<Archive<false>, boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)>> typename boost::mpl::begin<types>::type, typename boost::mpl::end<types>::type>::read(ar, v, t)) { - ar.stream().setstate(std::ios::failbit); + ar.set_fail(); return false; } ar.end_variant(); @@ -143,7 +143,7 @@ struct serializer<Archive<true>, boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)>> ar.write_variant_tag(variant_serialization_traits<Archive<true>, T>::get_tag()); if(!::do_serialize(ar, rv)) { - ar.stream().setstate(std::ios::failbit); + ar.set_fail(); return false; } ar.end_variant(); diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 2a3c33f48..e859a4693 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -50,6 +50,7 @@ #include <boost/format.hpp> #include <boost/regex.hpp> #include <boost/range/adaptor/transformed.hpp> +#include <boost/filesystem.hpp> #include "include_base_utils.h" #include "console_handler.h" #include "common/i18n.h" @@ -276,7 +277,7 @@ namespace const char* USAGE_PUBLIC_NODES("public_nodes"); const char* USAGE_WELCOME("welcome"); const char* USAGE_RPC_PAYMENT_INFO("rpc_payment_info"); - const char* USAGE_START_MINING_FOR_RPC("start_mining_for_rpc"); + const char* USAGE_START_MINING_FOR_RPC("start_mining_for_rpc [<number_of_threads>]"); 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"); @@ -607,9 +608,14 @@ void simple_wallet::handle_transfer_exception(const std::exception_ptr &e, bool fail_msg_writer() << e.what(); warn_of_possible_attack = false; } + catch (const tools::error::zero_amount&) + { + fail_msg_writer() << sw::tr("destination amount is zero"); + warn_of_possible_attack = false; + } catch (const tools::error::zero_destination&) { - fail_msg_writer() << sw::tr("one of destinations is zero"); + fail_msg_writer() << sw::tr("transaction has no destination"); warn_of_possible_attack = false; } catch (const tools::error::tx_too_big& e) @@ -2361,6 +2367,24 @@ bool simple_wallet::start_mining_for_rpc(const std::vector<std::string> &args) if (!try_connect_to_daemon()) return true; + bool ok = true; + if(args.size() >= 1) + { + uint16_t num = 0; + ok = string_tools::get_xtype_from_string(num, args[0]); + m_rpc_payment_threads = num; + } + else + { + m_rpc_payment_threads = 0; + } + + if (!ok) + { + PRINT_USAGE(USAGE_START_MINING_FOR_RPC); + return true; + } + LOCK_IDLE_SCOPE(); bool payment_required; @@ -5871,7 +5895,7 @@ bool simple_wallet::refresh_main(uint64_t start_height, enum ResetType reset, bo if (reset != ResetNone) { if (reset == ResetSoftKeepKI) - height_pre = m_wallet->hash_m_transfers(-1, transfer_hash_pre); + height_pre = m_wallet->hash_m_transfers(boost::none, transfer_hash_pre); m_wallet->rescan_blockchain(reset == ResetHard, false, reset == ResetSoftKeepKI); } @@ -9327,7 +9351,7 @@ bool simple_wallet::check_rpc_payment() fail_msg_writer() << tr("Error mining to daemon: ") << error; m_cmd_binder.print_prompt(); }; - bool ret = m_wallet->search_for_rpc_payment(target, startfunc, contfunc, foundfunc, errorfunc); + bool ret = m_wallet->search_for_rpc_payment(target, m_rpc_payment_threads, startfunc, contfunc, foundfunc, errorfunc); if (!ret) { fail_msg_writer() << tr("Failed to start mining for RPC payment"); diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index af654e0dd..8780bee1d 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -464,6 +464,7 @@ namespace cryptonote std::atomic<bool> m_need_payment; boost::posix_time::ptime m_last_rpc_payment_mining_time; bool m_rpc_payment_mining_requested; + uint32_t m_rpc_payment_threads = 0; bool m_daemon_rpc_payment_message_displayed; float m_rpc_payment_hash_rate; std::atomic<bool> m_suspend_rpc_payment_mining; diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt index bf238ae37..2dd64a38f 100644 --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt @@ -40,18 +40,7 @@ set(wallet_sources wallet_rpc_payments.cpp ) -set(wallet_private_headers - wallet2.h - wallet_args.h - wallet_errors.h - wallet_rpc_server.h - wallet_rpc_server_commands_defs.h - wallet_rpc_server_error_codes.h - ringdb.h - node_rpc_proxy.h - message_store.h - message_transporter.h - wallet_rpc_helpers.h) +monero_find_all_headers(wallet_private_headers "${CMAKE_CURRENT_SOURCE_DIR}") monero_private_headers(wallet ${wallet_private_headers}) @@ -115,45 +104,4 @@ if(NOT IOS) install(TARGETS wallet_rpc_server DESTINATION bin) endif() -# build and install libwallet_merged only if we building for GUI -if (BUILD_GUI_DEPS) - set(libs_to_merge - wallet_api - wallet - rpc_base - multisig - blockchain_db - cryptonote_core - cryptonote_basic - mnemonics - common - cncrypto - device - hardforks - ringct - ringct_basic - checkpoints - version - net - device_trezor) - - foreach(lib ${libs_to_merge}) - list(APPEND objlibs $<TARGET_OBJECTS:obj_${lib}>) # matches naming convention in src/CMakeLists.txt - endforeach() - add_library(wallet_merged STATIC ${objlibs}) - if(IOS) - set(lib_folder lib-${ARCH}) - else() - set(lib_folder lib) - endif() - install(TARGETS wallet_merged - ARCHIVE DESTINATION ${lib_folder}) - - install(FILES ${TREZOR_DEP_LIBS} - DESTINATION ${lib_folder}) - file(WRITE "trezor_link_flags.txt" ${TREZOR_DEP_LINKER}) - install(FILES "trezor_link_flags.txt" - DESTINATION ${lib_folder}) -endif() - add_subdirectory(api) diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp index 24f6d37db..b28ffd64c 100644 --- a/src/wallet/api/pending_transaction.cpp +++ b/src/wallet/api/pending_transaction.cpp @@ -40,6 +40,7 @@ #include <vector> #include <sstream> #include <boost/format.hpp> +#include <boost/filesystem.hpp> using namespace std; diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 4f923ce54..db3049f9e 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -45,10 +45,8 @@ #include <sstream> #include <unordered_map> -#ifdef WIN32 #include <boost/locale.hpp> #include <boost/filesystem.hpp> -#endif using namespace std; using namespace cryptonote; @@ -791,11 +789,11 @@ bool WalletImpl::close(bool store) return result; } -std::string WalletImpl::seed() const +std::string WalletImpl::seed(const std::string& seed_offset) const { epee::wipeable_string seed; if (m_wallet) - m_wallet->get_seed(seed); + m_wallet->get_seed(seed, seed_offset); return std::string(seed.data(), seed.size()); // TODO } @@ -1216,6 +1214,68 @@ bool WalletImpl::importKeyImages(const string &filename) return true; } +bool WalletImpl::exportOutputs(const string &filename, bool all) +{ + if (m_wallet->key_on_device()) + { + setStatusError(string(tr("Not supported on HW wallets.")) + filename); + return false; + } + + try + { + std::string data = m_wallet->export_outputs_to_str(all); + bool r = m_wallet->save_to_file(filename, data); + if (!r) + { + LOG_ERROR("Failed to save file " << filename); + setStatusError(string(tr("Failed to save file: ")) + filename); + return false; + } + } + catch (const std::exception &e) + { + LOG_ERROR("Error exporting outputs: " << e.what()); + setStatusError(string(tr("Error exporting outputs: ")) + e.what()); + return false; + } + + LOG_PRINT_L2("Outputs exported to " << filename); + return true; +} + +bool WalletImpl::importOutputs(const string &filename) +{ + if (m_wallet->key_on_device()) + { + setStatusError(string(tr("Not supported on HW wallets.")) + filename); + return false; + } + + std::string data; + bool r = m_wallet->load_from_file(filename, data); + if (!r) + { + LOG_ERROR("Failed to read file: " << filename); + setStatusError(string(tr("Failed to read file: ")) + filename); + return false; + } + + try + { + size_t n_outputs = m_wallet->import_outputs_from_str(data); + LOG_PRINT_L2(std::to_string(n_outputs) << " outputs imported"); + } + catch (const std::exception &e) + { + LOG_ERROR("Failed to import outputs: " << e.what()); + setStatusError(string(tr("Failed to import outputs: ")) + e.what()); + return false; + } + + return true; +} + void WalletImpl::addSubaddressAccount(const std::string& label) { m_wallet->add_subaddress_account(label); @@ -1567,8 +1627,10 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector<stri setStatusError(writer.str()); } catch (const tools::error::tx_sum_overflow& e) { setStatusError(e.what()); + } catch (const tools::error::zero_amount&) { + setStatusError(tr("destination amount is zero")); } catch (const tools::error::zero_destination&) { - setStatusError(tr("one of destinations is zero")); + setStatusError(tr("transaction has no destination")); } catch (const tools::error::tx_too_big& e) { setStatusError(tr("failed to find a suitable way to split transactions")); } catch (const tools::error::transfer_error& e) { @@ -2104,6 +2166,11 @@ bool WalletImpl::watchOnly() const return m_wallet->watch_only(); } +bool WalletImpl::isDeterministic() const +{ + return m_wallet->is_deterministic(); +} + void WalletImpl::clearStatus() const { boost::lock_guard<boost::mutex> l(m_statusMutex); @@ -2305,6 +2372,10 @@ bool WalletImpl::rescanSpent() return true; } +void WalletImpl::setOffline(bool offline) +{ + m_wallet->set_offline(offline); +} void WalletImpl::hardForkInfo(uint8_t &version, uint64_t &earliest_height) const { diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index e501d3943..ce2d7d7e4 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -81,7 +81,7 @@ public: const std::string &device_name); Device getDeviceType() const override; bool close(bool store = true); - std::string seed() const override; + std::string seed(const std::string& seed_offset = "") const override; std::string getSeedLanguage() const override; void setSeedLanguage(const std::string &arg) override; // void setListener(Listener *) {} @@ -129,6 +129,7 @@ public: void setRecoveringFromDevice(bool recoveringFromDevice) override; void setSubaddressLookahead(uint32_t major, uint32_t minor) override; bool watchOnly() const override; + bool isDeterministic() const override; bool rescanSpent() override; NetworkType nettype() const override {return static_cast<NetworkType>(m_wallet->nettype());} void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const override; @@ -166,6 +167,8 @@ public: virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) override; bool exportKeyImages(const std::string &filename, bool all = false) override; bool importKeyImages(const std::string &filename) override; + bool exportOutputs(const std::string &filename, bool all = false) override; + bool importOutputs(const std::string &filename) override; virtual void disposeTransaction(PendingTransaction * t) override; virtual uint64_t estimateTransactionFee(const std::vector<std::pair<std::string, uint64_t>> &destinations, @@ -181,6 +184,8 @@ public: virtual bool setCacheAttribute(const std::string &key, const std::string &val) override; virtual std::string getCacheAttribute(const std::string &key) const override; + virtual void setOffline(bool offline) override; + virtual bool setUserNote(const std::string &txid, const std::string ¬e) override; virtual std::string getUserNote(const std::string &txid) const override; virtual std::string getTxKey(const std::string &txid) const override; diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index b40b6763f..e34332734 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -446,7 +446,7 @@ struct Wallet }; virtual ~Wallet() = 0; - virtual std::string seed() const = 0; + virtual std::string seed(const std::string& seed_offset = "") const = 0; virtual std::string getSeedLanguage() const = 0; virtual void setSeedLanguage(const std::string &arg) = 0; //! returns wallet status (Status_Ok | Status_Error) @@ -627,6 +627,12 @@ struct Wallet virtual bool watchOnly() const = 0; /** + * @brief isDeterministic - checks if wallet keys are deterministic + * @return - true if deterministic + */ + virtual bool isDeterministic() const = 0; + + /** * @brief blockChainHeight - returns current blockchain height * @return */ @@ -913,6 +919,19 @@ struct Wallet */ virtual bool importKeyImages(const std::string &filename) = 0; + /*! + * \brief importOutputs - exports outputs to file + * \param filename + * \return - true on success + */ + virtual bool exportOutputs(const std::string &filename, bool all = false) = 0; + + /*! + * \brief importOutputs - imports outputs from file + * \param filename + * \return - true on success + */ + virtual bool importOutputs(const std::string &filename) = 0; virtual TransactionHistory * history() = 0; virtual AddressBook * addressBook() = 0; @@ -1008,6 +1027,12 @@ struct Wallet * \return true on success */ virtual bool rescanSpent() = 0; + + /* + * \brief setOffline - toggle set offline on/off + * \param offline - true/false + */ + virtual void setOffline(bool offline) = 0; //! blackballs a set of outputs virtual bool blackballOutputs(const std::vector<std::string> &outputs, bool add) = 0; diff --git a/src/wallet/message_store.cpp b/src/wallet/message_store.cpp index 87cb75fbf..34b4f440b 100644 --- a/src/wallet/message_store.cpp +++ b/src/wallet/message_store.cpp @@ -30,6 +30,8 @@ #include <boost/archive/portable_binary_iarchive.hpp> #include <boost/format.hpp> #include <boost/algorithm/string.hpp> +#include <boost/system/error_code.hpp> +#include <boost/filesystem.hpp> #include <fstream> #include <sstream> #include "file_io_utils.h" @@ -191,9 +193,7 @@ void message_store::unpack_signer_config(const multisig_wallet_state &state, con { try { - std::stringstream iss; - iss << signer_config; - binary_archive<false> ar(iss); + binary_archive<false> ar{epee::strspan<std::uint8_t>(signer_config)}; THROW_WALLET_EXCEPTION_IF(!::serialization::serialize(ar, signers), tools::error::wallet_internal_error, "Failed to serialize signer config"); } catch (...) @@ -381,9 +381,7 @@ void message_store::process_auto_config_data_message(uint32_t id) auto_config_data data; try { - std::stringstream iss; - iss << m.content; - binary_archive<false> ar(iss); + binary_archive<false> ar{epee::strspan<std::uint8_t>(m.content)}; THROW_WALLET_EXCEPTION_IF(!::serialization::serialize(ar, data), tools::error::wallet_internal_error, "Failed to serialize auto config data"); } catch (...) @@ -788,9 +786,7 @@ void message_store::read_from_file(const multisig_wallet_state &state, const std file_data read_file_data; try { - std::stringstream iss; - iss << buf; - binary_archive<false> ar(iss); + binary_archive<false> ar{epee::strspan<std::uint8_t>(buf)}; if (::serialization::serialize(ar, read_file_data)) if (::serialization::check_stream_state(ar)) loaded = true; @@ -827,9 +823,7 @@ void message_store::read_from_file(const multisig_wallet_state &state, const std loaded = false; try { - std::stringstream iss; - iss << decrypted_data; - binary_archive<false> ar(iss); + binary_archive<false> ar{epee::strspan<std::uint8_t>(decrypted_data)}; if (::serialization::serialize(ar, *this)) if (::serialization::check_stream_state(ar)) loaded = true; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index e298eca53..05fe0a1ad 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -37,6 +37,7 @@ #include <boost/algorithm/string/trim.hpp> #include <boost/algorithm/string/split.hpp> #include <boost/algorithm/string/join.hpp> +#include <boost/algorithm/string/predicate.hpp> #include <boost/asio/ip/address.hpp> #include <boost/range/adaptor/transformed.hpp> #include <boost/preprocessor/stringize.hpp> @@ -5700,12 +5701,18 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass try { - std::stringstream iss; - iss << cache_data; - binary_archive<false> ar(iss); + binary_archive<false> ar{epee::strspan<std::uint8_t>(cache_data)}; if (::serialization::serialize(ar, *this)) if (::serialization::check_stream_state(ar)) loaded = true; + if (!loaded) + { + binary_archive<false> ar{epee::strspan<std::uint8_t>(cache_data)}; + ar.enable_varint_bug_backward_compatibility(); + if (::serialization::serialize(ar, *this)) + if (::serialization::check_stream_state(ar)) + loaded = true; + } } catch(...) { } @@ -6025,7 +6032,7 @@ uint64_t wallet2::balance(uint32_t index_major, bool strict) const { uint64_t amount = 0; if(m_light_wallet) - return m_light_wallet_unlocked_balance; + return m_light_wallet_balance; for (const auto& i : balance_per_subaddress(index_major, strict)) amount += i.second; return amount; @@ -6039,7 +6046,7 @@ uint64_t wallet2::unlocked_balance(uint32_t index_major, bool strict, uint64_t * if (time_to_unlock) *time_to_unlock = 0; if(m_light_wallet) - return m_light_wallet_balance; + return m_light_wallet_unlocked_balance; for (const auto& i : unlocked_balance_per_subaddress(index_major, strict)) { amount += i.second.first; @@ -6775,8 +6782,7 @@ bool wallet2::parse_unsigned_tx_from_str(const std::string &unsigned_tx_st, unsi catch(const std::exception &e) { LOG_PRINT_L0("Failed to decrypt unsigned tx: " << e.what()); return false; } try { - std::istringstream iss(s); - binary_archive<false> ar(iss); + binary_archive<false> ar{epee::strspan<std::uint8_t>(s)}; if (!::serialization::serialize(ar, exported_txs)) { LOG_PRINT_L0("Failed to parse data from unsigned tx"); @@ -7090,8 +7096,7 @@ bool wallet2::parse_tx_from_str(const std::string &signed_tx_st, std::vector<too catch (const std::exception &e) { LOG_PRINT_L0("Failed to decrypt signed transaction: " << e.what()); return false; } try { - std::istringstream iss(s); - binary_archive<false> ar(iss); + binary_archive<false> ar{epee::strspan<std::uint8_t>(s)}; if (!::serialization::serialize(ar, signed_txs)) { LOG_PRINT_L0("Failed to deserialize signed transaction"); @@ -7226,8 +7231,7 @@ bool wallet2::parse_multisig_tx_from_str(std::string multisig_tx_st, multisig_tx bool loaded = false; try { - std::istringstream iss(multisig_tx_st); - binary_archive<false> ar(iss); + binary_archive<false> ar{epee::strspan<std::uint8_t>(multisig_tx_st)}; if (::serialization::serialize(ar, exported_txs)) if (::serialization::check_stream_state(ar)) loaded = true; @@ -8750,7 +8754,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent // throw if total amount overflows uint64_t for(auto& dt: dsts) { - THROW_WALLET_EXCEPTION_IF(0 == dt.amount, error::zero_destination); + THROW_WALLET_EXCEPTION_IF(0 == dt.amount, error::zero_amount); needed_money += dt.amount; LOG_PRINT_L2("transfer: adding " << print_money(dt.amount) << ", for a total of " << print_money (needed_money)); THROW_WALLET_EXCEPTION_IF(needed_money < dt.amount, error::tx_sum_overflow, dsts, fee, m_nettype); @@ -8909,7 +8913,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry // throw if total amount overflows uint64_t for(auto& dt: dsts) { - THROW_WALLET_EXCEPTION_IF(0 == dt.amount, error::zero_destination); + THROW_WALLET_EXCEPTION_IF(0 == dt.amount, error::zero_amount); needed_money += dt.amount; LOG_PRINT_L2("transfer: adding " << print_money(dt.amount) << ", for a total of " << print_money (needed_money)); THROW_WALLET_EXCEPTION_IF(needed_money < dt.amount, error::tx_sum_overflow, dsts, fee, m_nettype); @@ -9882,14 +9886,14 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp needed_money = 0; for(auto& dt: dsts) { - THROW_WALLET_EXCEPTION_IF(0 == dt.amount, error::zero_destination); + THROW_WALLET_EXCEPTION_IF(0 == dt.amount, error::zero_amount); needed_money += dt.amount; LOG_PRINT_L2("transfer: adding " << print_money(dt.amount) << ", for a total of " << print_money (needed_money)); THROW_WALLET_EXCEPTION_IF(needed_money < dt.amount, error::tx_sum_overflow, dsts, 0, m_nettype); } // throw if attempting a transaction with no money - THROW_WALLET_EXCEPTION_IF(needed_money == 0, error::zero_destination); + THROW_WALLET_EXCEPTION_IF(needed_money == 0, error::zero_amount); 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); @@ -11505,6 +11509,9 @@ void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypt void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations) { + uint32_t rpc_version; + THROW_WALLET_EXCEPTION_IF(!check_connection(&rpc_version), error::wallet_internal_error, "Failed to connect to daemon: " + get_daemon_address()); + COMMAND_RPC_GET_TRANSACTIONS::request req; COMMAND_RPC_GET_TRANSACTIONS::response res; req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid)); @@ -11550,10 +11557,17 @@ void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_de confirmations = 0; if (!in_pool) { - std::string err; - uint64_t bc_height = get_daemon_blockchain_height(err); - if (err.empty()) - confirmations = bc_height - res.txs.front().block_height; + if (rpc_version < MAKE_CORE_RPC_VERSION(3, 7)) + { + std::string err; + uint64_t bc_height = get_daemon_blockchain_height(err); + if (err.empty() && bc_height > res.txs.front().block_height) + confirmations = bc_height - res.txs.front().block_height; + } + else + { + confirmations = res.txs.front().confirmations; + } } } @@ -12016,8 +12030,7 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr serializable_unordered_map<crypto::public_key, crypto::signature> subaddr_spendkeys; try { - std::istringstream iss(sig_decoded); - binary_archive<false> ar(iss); + binary_archive<false> ar{epee::strspan<std::uint8_t>(sig_decoded)}; if (::serialization::serialize_noeof(ar, proofs)) if (::serialization::serialize_noeof(ar, subaddr_spendkeys)) if (::serialization::check_stream_state(ar)) @@ -12217,7 +12230,7 @@ uint64_t wallet2::get_approximate_blockchain_height() const // Calculated blockchain height uint64_t approx_blockchain_height = fork_block + (time(NULL) - fork_time)/seconds_per_block; // testnet got some huge rollbacks, so the estimation is way off - static const uint64_t approximate_testnet_rolled_back_blocks = 303967; + static const uint64_t approximate_testnet_rolled_back_blocks = 342100; if (m_nettype == TESTNET && approx_blockchain_height > approximate_testnet_rolled_back_blocks) approx_blockchain_height -= approximate_testnet_rolled_back_blocks; LOG_PRINT_L2("Calculated blockchain height: " << approx_blockchain_height); @@ -12528,7 +12541,7 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle bool wallet2::export_key_images(const std::string &filename, bool all) const { PERF_TIMER(export_key_images); - std::pair<size_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> ski = export_key_images(all); + std::pair<uint64_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> ski = export_key_images(all); std::string magic(KEY_IMAGE_EXPORT_FILE_MAGIC, strlen(KEY_IMAGE_EXPORT_FILE_MAGIC)); const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; const uint32_t offset = ski.first; @@ -12555,7 +12568,7 @@ bool wallet2::export_key_images(const std::string &filename, bool all) const } //---------------------------------------------------------------------------------------------------- -std::pair<size_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> wallet2::export_key_images(bool all) const +std::pair<uint64_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> wallet2::export_key_images(bool all) const { PERF_TIMER(export_key_images_raw); std::vector<std::pair<crypto::key_image, crypto::signature>> ski; @@ -13052,7 +13065,7 @@ void wallet2::import_blockchain(const std::tuple<size_t, crypto::hash, std::vect m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx); } //---------------------------------------------------------------------------------------------------- -std::pair<size_t, std::vector<tools::wallet2::transfer_details>> wallet2::export_outputs(bool all) const +std::pair<uint64_t, std::vector<tools::wallet2::transfer_details>> wallet2::export_outputs(bool all) const { PERF_TIMER(export_outputs); std::vector<tools::wallet2::transfer_details> outs; @@ -13092,7 +13105,7 @@ std::string wallet2::export_outputs_to_str(bool all) const return magic + ciphertext; } //---------------------------------------------------------------------------------------------------- -size_t wallet2::import_outputs(const std::pair<size_t, std::vector<tools::wallet2::transfer_details>> &outputs) +size_t wallet2::import_outputs(const std::pair<uint64_t, std::vector<tools::wallet2::transfer_details>> &outputs) { PERF_TIMER(import_outputs); @@ -13198,12 +13211,10 @@ size_t wallet2::import_outputs_from_str(const std::string &outputs_st) try { std::string body(data, headerlen); - std::pair<size_t, std::vector<tools::wallet2::transfer_details>> outputs; + std::pair<uint64_t, std::vector<tools::wallet2::transfer_details>> outputs; try { - std::stringstream iss; - iss << body; - binary_archive<false> ar(iss); + binary_archive<false> ar{epee::strspan<std::uint8_t>(body)}; if (::serialization::serialize(ar, outputs)) if (::serialization::check_stream_state(ar)) loaded = true; @@ -13455,8 +13466,7 @@ size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs) bool loaded = false; try { - std::istringstream iss(body); - binary_archive<false> ar(iss); + binary_archive<false> ar{epee::strspan<std::uint8_t>(body)}; if (::serialization::serialize(ar, i)) if (::serialization::check_stream_state(ar)) loaded = true; @@ -14178,15 +14188,15 @@ void wallet2::hash_m_transfer(const transfer_details & transfer, crypto::hash &h KECCAK_CTX state; keccak_init(&state); keccak_update(&state, (const uint8_t *) transfer.m_txid.data, sizeof(transfer.m_txid.data)); - keccak_update(&state, (const uint8_t *) transfer.m_internal_output_index, sizeof(transfer.m_internal_output_index)); - keccak_update(&state, (const uint8_t *) transfer.m_global_output_index, sizeof(transfer.m_global_output_index)); - keccak_update(&state, (const uint8_t *) transfer.m_amount, sizeof(transfer.m_amount)); + keccak_update(&state, (const uint8_t *) &transfer.m_internal_output_index, sizeof(transfer.m_internal_output_index)); + keccak_update(&state, (const uint8_t *) &transfer.m_global_output_index, sizeof(transfer.m_global_output_index)); + keccak_update(&state, (const uint8_t *) &transfer.m_amount, sizeof(transfer.m_amount)); keccak_finish(&state, (uint8_t *) hash.data); } //---------------------------------------------------------------------------------------------------- -uint64_t wallet2::hash_m_transfers(int64_t transfer_height, crypto::hash &hash) const +uint64_t wallet2::hash_m_transfers(boost::optional<uint64_t> transfer_height, crypto::hash &hash) const { - CHECK_AND_ASSERT_THROW_MES(transfer_height > (int64_t)m_transfers.size(), "Hash height is greater than number of transfers"); + CHECK_AND_ASSERT_THROW_MES(!transfer_height || *transfer_height <= m_transfers.size(), "Hash height is greater than number of transfers"); KECCAK_CTX state; crypto::hash tmp_hash{}; @@ -14194,12 +14204,12 @@ uint64_t wallet2::hash_m_transfers(int64_t transfer_height, crypto::hash &hash) keccak_init(&state); for(const transfer_details & transfer : m_transfers){ - if (transfer_height >= 0 && current_height >= (uint64_t)transfer_height){ + if (transfer_height && current_height >= *transfer_height){ break; } hash_m_transfer(transfer, tmp_hash); - keccak_update(&state, (const uint8_t *) transfer.m_block_height, sizeof(transfer.m_block_height)); + keccak_update(&state, (const uint8_t *) &transfer.m_block_height, sizeof(transfer.m_block_height)); keccak_update(&state, (const uint8_t *) tmp_hash.data, sizeof(tmp_hash.data)); current_height += 1; } @@ -14211,23 +14221,28 @@ uint64_t wallet2::hash_m_transfers(int64_t transfer_height, crypto::hash &hash) void wallet2::finish_rescan_bc_keep_key_images(uint64_t transfer_height, const crypto::hash &hash) { // Compute hash of m_transfers, if differs there had to be BC reorg. - crypto::hash new_transfers_hash{}; - hash_m_transfers((int64_t) transfer_height, new_transfers_hash); + if (transfer_height <= m_transfers.size()) { + crypto::hash new_transfers_hash{}; + hash_m_transfers(transfer_height, new_transfers_hash); - if (new_transfers_hash != hash) - { - // Soft-Reset to avoid inconsistency in case of BC reorg. - clear_soft(false); // keep_key_images works only with soft reset. - THROW_WALLET_EXCEPTION_IF(true, error::wallet_internal_error, "Transfers changed during rescan, soft or hard rescan is needed"); - } + if (new_transfers_hash == hash) { + // Restore key images in m_transfers from m_key_images + for(auto it = m_key_images.begin(); it != m_key_images.end(); it++) + { + THROW_WALLET_EXCEPTION_IF(it->second >= m_transfers.size(), + error::wallet_internal_error, + "Key images cache contains illegal transfer offset"); + m_transfers[it->second].m_key_image = it->first; + m_transfers[it->second].m_key_image_known = true; + } - // Restore key images in m_transfers from m_key_images - for(auto it = m_key_images.begin(); it != m_key_images.end(); it++) - { - THROW_WALLET_EXCEPTION_IF(it->second >= m_transfers.size(), error::wallet_internal_error, "Key images cache contains illegal transfer offset"); - m_transfers[it->second].m_key_image = it->first; - m_transfers[it->second].m_key_image_known = true; + return; + } } + + // Soft-Reset to avoid inconsistency in case of BC reorg. + clear_soft(false); // keep_key_images works only with soft reset. + THROW_WALLET_EXCEPTION_IF(true, error::wallet_internal_error, "Transfers changed during rescan, soft or hard rescan is needed"); } //---------------------------------------------------------------------------------------------------- uint64_t wallet2::get_bytes_sent() const diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index e5a5136a4..facf9878d 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -327,7 +327,7 @@ private: uint64_t m_block_height; cryptonote::transaction_prefix m_tx; crypto::hash m_txid; - size_t m_internal_output_index; + uint64_t m_internal_output_index; uint64_t m_global_output_index; bool m_spent; bool m_frozen; @@ -338,7 +338,7 @@ private: bool m_rct; bool m_key_image_known; bool m_key_image_request; // view wallets: we want to request it; cold wallets: it was requested - size_t m_pk_index; + uint64_t m_pk_index; cryptonote::subaddress_index m_subaddr_index; bool m_key_image_partial; std::vector<rct::key> m_multisig_k; @@ -1372,9 +1372,9 @@ private: bool verify_with_public_key(const std::string &data, const crypto::public_key &public_key, const std::string &signature) const; // Import/Export wallet data - std::pair<size_t, std::vector<tools::wallet2::transfer_details>> export_outputs(bool all = false) const; + std::pair<uint64_t, std::vector<tools::wallet2::transfer_details>> export_outputs(bool all = false) const; std::string export_outputs_to_str(bool all = false) const; - size_t import_outputs(const std::pair<size_t, std::vector<tools::wallet2::transfer_details>> &outputs); + size_t import_outputs(const std::pair<uint64_t, std::vector<tools::wallet2::transfer_details>> &outputs); size_t import_outputs_from_str(const std::string &outputs_st); payment_container export_payments() const; void import_payments(const payment_container &payments); @@ -1382,7 +1382,7 @@ private: std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> export_blockchain() const; void import_blockchain(const std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> &bc); bool export_key_images(const std::string &filename, bool all = false) const; - std::pair<size_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> export_key_images(bool all = false) const; + std::pair<uint64_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> export_key_images(bool all = false) const; uint64_t import_key_images(const std::vector<std::pair<crypto::key_image, crypto::signature>> &signed_key_images, size_t offset, uint64_t &spent, uint64_t &unspent, bool check_spent = true); uint64_t import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent); bool import_key_images(std::vector<crypto::key_image> key_images, size_t offset=0, boost::optional<std::unordered_set<size_t>> selected_transfers=boost::none); @@ -1428,7 +1428,7 @@ private: bool get_rpc_payment_info(bool mining, bool &payment_required, uint64_t &credits, uint64_t &diff, uint64_t &credits_per_hash_found, cryptonote::blobdata &hashing_blob, uint64_t &height, uint64_t &seed_height, crypto::hash &seed_hash, crypto::hash &next_seed_hash, uint32_t &cookie); bool daemon_requires_payment(); bool make_rpc_payment(uint32_t nonce, uint32_t cookie, uint64_t &credits, uint64_t &balance); - bool search_for_rpc_payment(uint64_t credits_target, const std::function<bool(uint64_t, uint64_t)> &startfunc, const std::function<bool(unsigned)> &contfunc, const std::function<bool(uint64_t)> &foundfunc = NULL, const std::function<void(const std::string&)> &errorfunc = NULL); + bool search_for_rpc_payment(uint64_t credits_target, uint32_t n_threads, const std::function<bool(uint64_t, uint64_t)> &startfunc, const std::function<bool(unsigned)> &contfunc, const std::function<bool(uint64_t)> &foundfunc = NULL, const std::function<void(const std::string&)> &errorfunc = NULL); template<typename T> void handle_payment_changes(const T &res, std::true_type) { if (res.status == CORE_RPC_STATUS_OK || res.status == CORE_RPC_STATUS_PAYMENT_REQUIRED) m_rpc_payment_state.credits = res.credits; @@ -1547,7 +1547,7 @@ private: bool is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t block_height); void hash_m_transfer(const transfer_details & transfer, crypto::hash &hash) const; - uint64_t hash_m_transfers(int64_t transfer_height, crypto::hash &hash) const; + uint64_t hash_m_transfers(boost::optional<uint64_t> transfer_height, crypto::hash &hash) const; void finish_rescan_bc_keep_key_images(uint64_t transfer_height, const crypto::hash &hash); void enable_dns(bool enable) { m_use_dns = enable; } void set_offline(bool offline = true); diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index 4a89ed81a..011780f43 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -83,6 +83,7 @@ namespace tools // tx_rejected // tx_sum_overflow // tx_too_big + // zero_amount // zero_destination // wallet_rpc_error * // daemon_busy @@ -750,10 +751,18 @@ namespace tools uint64_t m_tx_weight_limit; }; //---------------------------------------------------------------------------------------------------- + struct zero_amount: public transfer_error + { + explicit zero_amount(std::string&& loc) + : transfer_error(std::move(loc), "destination amount is zero") + { + } + }; + //---------------------------------------------------------------------------------------------------- struct zero_destination : public transfer_error { explicit zero_destination(std::string&& loc) - : transfer_error(std::move(loc), "destination amount is zero") + : transfer_error(std::move(loc), "transaction has no destination") { } }; diff --git a/src/wallet/wallet_rpc_payments.cpp b/src/wallet/wallet_rpc_payments.cpp index 6527b1384..bf278f695 100644 --- a/src/wallet/wallet_rpc_payments.cpp +++ b/src/wallet/wallet_rpc_payments.cpp @@ -42,6 +42,7 @@ #include "cryptonote_basic/blobdatatype.h" #include "common/i18n.h" #include "common/util.h" +#include "common/threadpool.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "wallet.wallet2.rpc_payments" @@ -101,7 +102,7 @@ bool wallet2::make_rpc_payment(uint32_t nonce, uint32_t cookie, uint64_t &credit return true; } //---------------------------------------------------------------------------------------------------- -bool wallet2::search_for_rpc_payment(uint64_t credits_target, const std::function<bool(uint64_t, uint64_t)> &startfunc, const std::function<bool(unsigned)> &contfunc, const std::function<bool(uint64_t)> &foundfunc, const std::function<void(const std::string&)> &errorfunc) +bool wallet2::search_for_rpc_payment(uint64_t credits_target, uint32_t n_threads, const std::function<bool(uint64_t, uint64_t)> &startfunc, const std::function<bool(unsigned)> &contfunc, const std::function<bool(uint64_t)> &foundfunc, const std::function<void(const std::string&)> &errorfunc) { bool need_payment = false; bool payment_required; @@ -139,49 +140,65 @@ bool wallet2::search_for_rpc_payment(uint64_t credits_target, const std::functio continue; } - crypto::hash hash; - const uint32_t local_nonce = nonce++; // wrapping's OK - *(uint32_t*)(hashing_blob.data() + 39) = SWAP32LE(local_nonce); - const uint8_t major_version = hashing_blob[0]; - if (major_version >= RX_BLOCK_VERSION) - { - const int miners = 1; - crypto::rx_slow_hash(height, seed_height, seed_hash.data, hashing_blob.data(), hashing_blob.size(), hash.data, miners, 0); - } - else + if(n_threads == 0) + n_threads = boost::thread::hardware_concurrency(); + + std::vector<crypto::hash> hash(n_threads); + tools::threadpool& tpool = tools::threadpool::getInstance(); + tools::threadpool::waiter waiter(tpool); + + const uint32_t local_nonce = nonce += n_threads; // wrapping's OK + for (size_t i = 0; i < n_threads; i++) { - int cn_variant = hashing_blob[0] >= 7 ? hashing_blob[0] - 6 : 0; - crypto::cn_slow_hash(hashing_blob.data(), hashing_blob.size(), hash, cn_variant, height); + tpool.submit(&waiter, [&, i] { + *(uint32_t*)(hashing_blob.data() + 39) = SWAP32LE(local_nonce-i); + const uint8_t major_version = hashing_blob[0]; + if (major_version >= RX_BLOCK_VERSION) + { + const int miners = 1; + crypto::rx_slow_hash(height, seed_height, seed_hash.data, hashing_blob.data(), hashing_blob.size(), hash[i].data, miners, 0); + } + else + { + int cn_variant = hashing_blob[0] >= 7 ? hashing_blob[0] - 6 : 0; + crypto::cn_slow_hash(hashing_blob.data(), hashing_blob.size(), hash[i], cn_variant, height); + } + }); } - ++n_hashes; - if (cryptonote::check_hash(hash, diff)) + waiter.wait(); + n_hashes += n_threads; + + for(size_t i=0; i < n_threads; i++) { - uint64_t credits, balance; - try + if (cryptonote::check_hash(hash[i], diff)) { - make_rpc_payment(local_nonce, cookie, credits, balance); - if (credits != credits_per_hash_found) + uint64_t credits, balance; + try { - MERROR("Found nonce, but daemon did not credit us with the expected amount"); + make_rpc_payment(local_nonce-i, cookie, credits, balance); + if (credits != credits_per_hash_found) + { + MERROR("Found nonce, but daemon did not credit us with the expected amount"); + if (errorfunc) + errorfunc("Found nonce, but daemon did not credit us with the expected amount"); + return false; + } + MDEBUG("Found nonce " << local_nonce-i << " at diff " << diff << ", gets us " << credits_per_hash_found << ", now " << balance << " credits"); + if (!foundfunc(credits)) + break; + } + catch (const tools::error::wallet_coded_rpc_error &e) + { + MWARNING("Found a local_nonce at diff " << diff << ", but failed to send it to the daemon"); if (errorfunc) - errorfunc("Found nonce, but daemon did not credit us with the expected amount"); - return false; + errorfunc("Found nonce, but daemon errored out with error " + std::to_string(e.code()) + ": " + e.status() + ", continuing"); + } + catch (const std::exception &e) + { + MWARNING("Found a local_nonce at diff " << diff << ", but failed to send it to the daemon"); + if (errorfunc) + errorfunc("Found nonce, but daemon errored out with: '" + std::string(e.what()) + "', continuing"); } - MDEBUG("Found nonce " << local_nonce << " at diff " << diff << ", gets us " << credits_per_hash_found << ", now " << balance << " credits"); - if (!foundfunc(credits)) - break; - } - catch (const tools::error::wallet_coded_rpc_error &e) - { - MWARNING("Found a local_nonce at diff " << diff << ", but failed to send it to the daemon"); - if (errorfunc) - errorfunc("Found nonce, but daemon errored out with error " + std::to_string(e.code()) + ": " + e.status() + ", continuing"); - } - catch (const std::exception &e) - { - MWARNING("Found a local_nonce at diff " << diff << ", but failed to send it to the daemon"); - if (errorfunc) - errorfunc("Found nonce, but daemon errored out with: '" + std::string(e.what()) + "', continuing"); } } } diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index b09e24d31..b72817ba0 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -762,6 +762,90 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_freeze(const wallet_rpc::COMMAND_RPC_FREEZE::request& req, wallet_rpc::COMMAND_RPC_FREEZE::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); + try + { + if (req.key_image.empty()) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = std::string("Must specify key image to freeze"); + return false; + } + crypto::key_image ki; + if (!epee::string_tools::hex_to_pod(req.key_image, ki)) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE; + er.message = "failed to parse key image"; + return false; + } + m_wallet->freeze(ki); + } + catch (const std::exception& e) + { + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); + return false; + } + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_thaw(const wallet_rpc::COMMAND_RPC_THAW::request& req, wallet_rpc::COMMAND_RPC_THAW::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); + try + { + if (req.key_image.empty()) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = std::string("Must specify key image to thaw"); + return false; + } + crypto::key_image ki; + if (!epee::string_tools::hex_to_pod(req.key_image, ki)) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE; + er.message = "failed to parse key image"; + return false; + } + m_wallet->thaw(ki); + } + catch (const std::exception& e) + { + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); + return false; + } + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_frozen(const wallet_rpc::COMMAND_RPC_FROZEN::request& req, wallet_rpc::COMMAND_RPC_FROZEN::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); + try + { + if (req.key_image.empty()) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = std::string("Must specify key image to check if frozen"); + return false; + } + crypto::key_image ki; + if (!epee::string_tools::hex_to_pod(req.key_image, ki)) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE; + er.message = "failed to parse key image"; + return false; + } + res.frozen = m_wallet->frozen(ki); + } + catch (const std::exception& e) + { + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); + return false; + } + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::validate_transfer(const std::list<wallet_rpc::transfer_destination>& destinations, const std::string& payment_id, std::vector<cryptonote::tx_destination_entry>& dsts, std::vector<uint8_t>& extra, bool at_least_one_destination, epee::json_rpc::error& er) { crypto::hash8 integrated_payment_id = crypto::null_hash8; @@ -822,7 +906,7 @@ namespace tools if (at_least_one_destination && dsts.empty()) { er.code = WALLET_RPC_ERROR_CODE_ZERO_DESTINATION; - er.message = "No destinations for this transfer"; + er.message = "Transaction has no destination"; return false; } @@ -877,10 +961,10 @@ namespace tools return amount; } //------------------------------------------------------------------------------------------------------------------------------ - template<typename Ts, typename Tu> + template<typename Ts, typename Tu, typename Tk> bool wallet_rpc_server::fill_response(std::vector<tools::wallet2::pending_tx> &ptx_vector, bool get_tx_key, Ts& tx_key, Tu &amount, Tu &fee, Tu &weight, std::string &multisig_txset, std::string &unsigned_txset, bool do_not_relay, - Ts &tx_hash, bool get_tx_hex, Ts &tx_blob, bool get_tx_metadata, Ts &tx_metadata, epee::json_rpc::error &er) + Ts &tx_hash, bool get_tx_hex, Ts &tx_blob, bool get_tx_metadata, Ts &tx_metadata, Tk &spent_key_images, epee::json_rpc::error &er) { for (const auto & ptx : ptx_vector) { @@ -895,6 +979,17 @@ namespace tools fill(amount, total_amount(ptx)); fill(fee, ptx.fee); fill(weight, cryptonote::get_transaction_weight(ptx.tx)); + + // add spent key images + tools::wallet_rpc::key_image_list key_image_list; + bool all_are_txin_to_key = std::all_of(ptx.tx.vin.begin(), ptx.tx.vin.end(), [&](const cryptonote::txin_v& s_e) -> bool + { + CHECKED_GET_SPECIFIC_VARIANT(s_e, const cryptonote::txin_to_key, in, false); + key_image_list.key_images.push_back(epee::string_tools::pod_to_hex(in.k_image)); + return true; + }); + THROW_WALLET_EXCEPTION_IF(!all_are_txin_to_key, error::unexpected_txin_type, ptx.tx); + fill(spent_key_images, key_image_list); } if (m_wallet->multisig()) @@ -981,7 +1076,7 @@ namespace tools } return fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.fee, res.weight, res.multisig_txset, res.unsigned_txset, req.do_not_relay, - res.tx_hash, req.get_tx_hex, res.tx_blob, req.get_tx_metadata, res.tx_metadata, er); + res.tx_hash, req.get_tx_hex, res.tx_blob, req.get_tx_metadata, res.tx_metadata, res.spent_key_images, er); } catch (const std::exception& e) { @@ -1027,7 +1122,7 @@ namespace tools } return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.weight_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay, - res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er); + res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, res.spent_key_images_list, er); } catch (const std::exception& e) { @@ -1389,7 +1484,7 @@ namespace tools std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_unmixable_sweep_transactions(); return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.weight_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay, - res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er); + res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, res.spent_key_images_list, er); } catch (const std::exception& e) { @@ -1447,7 +1542,7 @@ namespace tools std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, req.outputs, mixin, req.unlock_time, priority, extra, req.account_index, subaddr_indices); return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.weight_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay, - res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er); + res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, res.spent_key_images_list, er); } catch (const std::exception& e) { @@ -1522,7 +1617,7 @@ namespace tools } return fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.fee, res.weight, res.multisig_txset, res.unsigned_txset, req.do_not_relay, - res.tx_hash, req.get_tx_hex, res.tx_blob, req.get_tx_metadata, res.tx_metadata, er); + res.tx_hash, req.get_tx_hex, res.tx_blob, req.get_tx_metadata, res.tx_metadata, res.spent_key_images, er); } catch (const std::exception& e) { @@ -1555,8 +1650,7 @@ namespace tools try { - std::istringstream iss(blob); - binary_archive<false> ar(iss); + binary_archive<false> ar{epee::strspan<std::uint8_t>(blob)}; if (::serialization::serialize(ar, ptx)) loaded = true; } @@ -2702,7 +2796,7 @@ namespace tools if (!m_wallet) return not_open(er); try { - std::pair<size_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> ski = m_wallet->export_key_images(req.all); + std::pair<uint64_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> ski = m_wallet->export_key_images(req.all); res.offset = ski.first; res.signed_key_images.resize(ski.second.size()); for (size_t n = 0; n < ski.second.size(); ++n) @@ -3154,17 +3248,7 @@ namespace tools } std::string wallet_file = req.filename.empty() ? "" : (m_wallet_dir + "/" + req.filename); { - std::vector<std::string> languages; - crypto::ElectrumWords::get_language_list(languages, false); - std::vector<std::string>::iterator it; - - it = std::find(languages.begin(), languages.end(), req.language); - if (it == languages.end()) - { - crypto::ElectrumWords::get_language_list(languages, true); - it = std::find(languages.begin(), languages.end(), req.language); - } - if (it == languages.end()) + if (!crypto::ElectrumWords::is_valid_language(req.language)) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = "Unknown language: " + req.language; @@ -3369,6 +3453,11 @@ namespace tools er.code = WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY; er.message = e.what(); } + catch (const tools::error::zero_amount& e) + { + er.code = WALLET_RPC_ERROR_CODE_ZERO_AMOUNT; + er.message = e.what(); + } catch (const tools::error::zero_destination& e) { er.code = WALLET_RPC_ERROR_CODE_ZERO_DESTINATION; @@ -3582,6 +3671,17 @@ namespace tools return false; } + if (!req.language.empty()) + { + if (!crypto::ElectrumWords::is_valid_language(req.language)) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "The specified seed language is invalid."; + return false; + } + wal->set_seed_language(req.language); + } + // set blockheight if given try { @@ -3725,12 +3825,7 @@ namespace tools er.message = "Wallet was using the old seed language. You need to specify a new seed language."; return false; } - std::vector<std::string> language_list; - std::vector<std::string> language_list_en; - crypto::ElectrumWords::get_language_list(language_list); - crypto::ElectrumWords::get_language_list(language_list_en, true); - if (std::find(language_list.begin(), language_list.end(), req.language) == language_list.end() && - std::find(language_list_en.begin(), language_list_en.end(), req.language) == language_list_en.end()) + if (!crypto::ElectrumWords::is_valid_language(req.language)) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = "Wallet was using the old seed language, and the specified new seed language is invalid."; diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 9f9e3c134..7169c9136 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -84,6 +84,9 @@ namespace tools MAP_JON_RPC_WE("set_account_tag_description", on_set_account_tag_description, wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION) MAP_JON_RPC_WE("get_height", on_getheight, wallet_rpc::COMMAND_RPC_GET_HEIGHT) MAP_JON_RPC_WE("getheight", on_getheight, wallet_rpc::COMMAND_RPC_GET_HEIGHT) + MAP_JON_RPC_WE("freeze", on_freeze, wallet_rpc::COMMAND_RPC_FREEZE) + MAP_JON_RPC_WE("thaw", on_thaw, wallet_rpc::COMMAND_RPC_THAW) + MAP_JON_RPC_WE("frozen", on_frozen, wallet_rpc::COMMAND_RPC_FROZEN) MAP_JON_RPC_WE("transfer", on_transfer, wallet_rpc::COMMAND_RPC_TRANSFER) MAP_JON_RPC_WE("transfer_split", on_transfer_split, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT) MAP_JON_RPC_WE("sign_transfer", on_sign_transfer, wallet_rpc::COMMAND_RPC_SIGN_TRANSFER) @@ -174,6 +177,9 @@ namespace tools bool on_untag_accounts(const wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_set_account_tag_description(const wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::request& req, wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_getheight(const wallet_rpc::COMMAND_RPC_GET_HEIGHT::request& req, wallet_rpc::COMMAND_RPC_GET_HEIGHT::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); + bool on_freeze(const wallet_rpc::COMMAND_RPC_FREEZE::request& req, wallet_rpc::COMMAND_RPC_FREEZE::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); + bool on_thaw(const wallet_rpc::COMMAND_RPC_THAW::request& req, wallet_rpc::COMMAND_RPC_THAW::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); + bool on_frozen(const wallet_rpc::COMMAND_RPC_FROZEN::request& req, wallet_rpc::COMMAND_RPC_FROZEN::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_transfer_split(const wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_sign_transfer(const wallet_rpc::COMMAND_RPC_SIGN_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SIGN_TRANSFER::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); @@ -257,10 +263,10 @@ namespace tools bool not_open(epee::json_rpc::error& er); void handle_rpc_exception(const std::exception_ptr& e, epee::json_rpc::error& er, int default_error_code); - template<typename Ts, typename Tu> + template<typename Ts, typename Tu, typename Tk> bool fill_response(std::vector<tools::wallet2::pending_tx> &ptx_vector, bool get_tx_key, Ts& tx_key, Tu &amount, Tu &fee, Tu &weight, std::string &multisig_txset, std::string &unsigned_txset, bool do_not_relay, - Ts &tx_hash, bool get_tx_hex, Ts &tx_blob, bool get_tx_metadata, Ts &tx_metadata, epee::json_rpc::error &er); + Ts &tx_hash, bool get_tx_hex, Ts &tx_blob, bool get_tx_metadata, Ts &tx_metadata, Tk &spent_key_images, epee::json_rpc::error &er); bool validate_transfer(const std::list<wallet_rpc::transfer_destination>& destinations, const std::string& payment_id, std::vector<cryptonote::tx_destination_entry>& dsts, std::vector<uint8_t>& extra, bool at_least_one_destination, epee::json_rpc::error& er); diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index abc7702f7..6640441ed 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -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 21 +#define WALLET_RPC_VERSION_MINOR 22 #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 @@ -456,6 +456,78 @@ namespace wallet_rpc END_KV_SERIALIZE_MAP() }; + struct COMMAND_RPC_FREEZE + { + struct request_t + { + std::string key_image; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(key_image) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<request_t> request; + + struct response_t + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<response_t> response; + }; + + struct COMMAND_RPC_THAW + { + struct request_t + { + std::string key_image; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(key_image) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<request_t> request; + + struct response_t + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<response_t> response; + }; + + struct COMMAND_RPC_FROZEN + { + struct request_t + { + std::string key_image; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(key_image) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<request_t> request; + + struct response_t + { + bool frozen; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(frozen) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<response_t> response; + }; + + struct key_image_list + { + std::list<std::string> key_images; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(key_images) + END_KV_SERIALIZE_MAP() + }; + struct COMMAND_RPC_TRANSFER { struct request_t @@ -499,6 +571,7 @@ namespace wallet_rpc std::string tx_metadata; std::string multisig_txset; std::string unsigned_txset; + key_image_list spent_key_images; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(tx_hash) @@ -510,6 +583,7 @@ namespace wallet_rpc KV_SERIALIZE(tx_metadata) KV_SERIALIZE(multisig_txset) KV_SERIALIZE(unsigned_txset) + KV_SERIALIZE(spent_key_images) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -567,6 +641,7 @@ namespace wallet_rpc std::list<std::string> tx_metadata_list; std::string multisig_txset; std::string unsigned_txset; + std::list<key_image_list> spent_key_images_list; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(tx_hash_list) @@ -578,6 +653,7 @@ namespace wallet_rpc KV_SERIALIZE(tx_metadata_list) KV_SERIALIZE(multisig_txset) KV_SERIALIZE(unsigned_txset) + KV_SERIALIZE(spent_key_images_list) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -742,6 +818,7 @@ namespace wallet_rpc std::list<std::string> tx_metadata_list; std::string multisig_txset; std::string unsigned_txset; + std::list<key_image_list> spent_key_images_list; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(tx_hash_list) @@ -753,6 +830,7 @@ namespace wallet_rpc KV_SERIALIZE(tx_metadata_list) KV_SERIALIZE(multisig_txset) KV_SERIALIZE(unsigned_txset) + KV_SERIALIZE(spent_key_images_list) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -816,6 +894,7 @@ namespace wallet_rpc std::list<std::string> tx_metadata_list; std::string multisig_txset; std::string unsigned_txset; + std::list<key_image_list> spent_key_images_list; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(tx_hash_list) @@ -827,6 +906,7 @@ namespace wallet_rpc KV_SERIALIZE(tx_metadata_list) KV_SERIALIZE(multisig_txset) KV_SERIALIZE(unsigned_txset) + KV_SERIALIZE(spent_key_images_list) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -875,6 +955,7 @@ namespace wallet_rpc std::string tx_metadata; std::string multisig_txset; std::string unsigned_txset; + key_image_list spent_key_images; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(tx_hash) @@ -886,6 +967,7 @@ namespace wallet_rpc KV_SERIALIZE(tx_metadata) KV_SERIALIZE(multisig_txset) KV_SERIALIZE(unsigned_txset) + KV_SERIALIZE(spent_key_images) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -2214,6 +2296,7 @@ namespace wallet_rpc std::string viewkey; std::string password; bool autosave_current; + std::string language; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE_OPT(restore_height, (uint64_t)0) @@ -2223,6 +2306,7 @@ namespace wallet_rpc KV_SERIALIZE(viewkey) KV_SERIALIZE(password) KV_SERIALIZE_OPT(autosave_current, true) + KV_SERIALIZE(language) END_KV_SERIALIZE_MAP() }; diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h index f7c5bb0e1..b991029a9 100644 --- a/src/wallet/wallet_rpc_server_error_codes.h +++ b/src/wallet/wallet_rpc_server_error_codes.h @@ -76,4 +76,5 @@ #define WALLET_RPC_ERROR_CODE_NON_DETERMINISTIC -43 #define WALLET_RPC_ERROR_CODE_INVALID_LOG_LEVEL -44 #define WALLET_RPC_ERROR_CODE_ATTRIBUTE_NOT_FOUND -45 +#define WALLET_RPC_ERROR_CODE_ZERO_AMOUNT -46 #define WALLET_RPC_ERROR_CODE_INVALID_SIGNATURE_TYPE -47 |