diff options
-rw-r--r-- | README.md | 10 | ||||
-rw-r--r-- | contrib/epee/include/net/net_utils_base.h | 3 | ||||
-rw-r--r-- | contrib/epee/src/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/blocks/checkpoints.dat | bin | 290628 -> 303748 bytes | |||
-rw-r--r-- | src/checkpoints/checkpoints.cpp | 1 | ||||
-rw-r--r-- | src/cryptonote_core/blockchain.cpp | 2 | ||||
-rw-r--r-- | src/p2p/net_node.inl | 21 | ||||
-rw-r--r-- | src/p2p/net_peerlist.cpp | 12 | ||||
-rw-r--r-- | src/p2p/net_peerlist.h | 26 | ||||
-rw-r--r-- | src/serialization/json_object.cpp | 18 | ||||
-rw-r--r-- | src/version.cpp.in | 2 | ||||
-rw-r--r-- | src/wallet/api/wallet.cpp | 5 | ||||
-rw-r--r-- | src/wallet/api/wallet.h | 1 | ||||
-rw-r--r-- | src/wallet/api/wallet2_api.h | 1 | ||||
-rw-r--r-- | src/wallet/wallet2.cpp | 51 | ||||
-rw-r--r-- | src/wallet/wallet_rpc_server.cpp | 109 | ||||
-rw-r--r-- | src/wallet/wallet_rpc_server.h | 10 | ||||
-rw-r--r-- | src/wallet/wallet_rpc_server_commands_defs.h | 84 | ||||
-rw-r--r-- | tests/unit_tests/epee_boosted_tcp_server.cpp | 6 | ||||
-rw-r--r-- | tests/unit_tests/epee_utils.cpp | 1 | ||||
-rw-r--r-- | tests/unit_tests/node_server.cpp | 10 |
21 files changed, 324 insertions, 51 deletions
@@ -134,8 +134,8 @@ Dates are provided in the format YYYY-MM-DD. | 1788000 | 2019-03-09 | v10 | v0.14.0.0 | v0.14.1.2 | New PoW based on Cryptonight-R, new block weight algorithm, slightly more efficient RingCT format | 1788720 | 2019-03-10 | v11 | v0.14.0.0 | v0.14.1.2 | forbid old RingCT transaction format | 1978433 | 2019-11-30* | v12 | v0.15.0.0 | v0.16.0.0 | New PoW based on RandomX, only allow >= 2 outputs, change to the block median used to calculate penalty, v1 coinbases are forbidden, rct sigs in coinbase forbidden, 10 block lock time for incoming outputs -| 2210000 | 2020-10-17 | v13 | v0.17.0.0 | v0.17.2.0 | New CLSAG transaction format -| 2210720 | 2020-10-18 | v14 | v0.17.1.1 | v0.17.2.0 | forbid old MLSAG transaction format +| 2210000 | 2020-10-17 | v13 | v0.17.0.0 | v0.17.2.3 | New CLSAG transaction format +| 2210720 | 2020-10-18 | v14 | v0.17.1.1 | v0.17.2.3 | forbid old MLSAG transaction format | XXXXXXX | XXX-XX-XX | XXX | vX.XX.X.X | vX.XX.X.X | XXX | X's indicate that these details have not been determined as of commit date. @@ -295,7 +295,7 @@ Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch ( ```bash git clone https://github.com/monero-project/monero.git cd monero - git checkout tags/v0.17.2.0 + git checkout tags/v0.17.2.3 ``` * Build: @@ -412,10 +412,10 @@ application. cd monero ``` -* If you would like a specific [version/tag](https://github.com/monero-project/monero/tags), do a git checkout for that version. eg. 'v0.17.2.0'. If you don't care about the version and just want binaries from master, skip this step: +* If you would like a specific [version/tag](https://github.com/monero-project/monero/tags), do a git checkout for that version. eg. 'v0.17.2.3'. If you don't care about the version and just want binaries from master, skip this step: ```bash - git checkout v0.17.2.0 + git checkout v0.17.2.3 ``` * If you are on a 64-bit system, run: diff --git a/contrib/epee/include/net/net_utils_base.h b/contrib/epee/include/net/net_utils_base.h index d86c62c17..722206ee1 100644 --- a/contrib/epee/include/net/net_utils_base.h +++ b/contrib/epee/include/net/net_utils_base.h @@ -236,6 +236,7 @@ namespace net_utils virtual address_type get_type_id() const = 0; virtual zone get_zone() const = 0; virtual bool is_blockable() const = 0; + virtual std::uint16_t port() const = 0; }; template<typename T> @@ -266,6 +267,7 @@ namespace net_utils virtual address_type get_type_id() const override { return value.get_type_id(); } virtual zone get_zone() const override { return value.get_zone(); } virtual bool is_blockable() const override { return value.is_blockable(); } + virtual std::uint16_t port() const override { return value.port(); } }; std::shared_ptr<interface> self; @@ -312,6 +314,7 @@ namespace net_utils address_type get_type_id() const { return self ? self->get_type_id() : address_type::invalid; } zone get_zone() const { return self ? self->get_zone() : zone::invalid; } bool is_blockable() const { return self ? self->is_blockable() : false; } + std::uint16_t port() const { return self ? self->port() : 0; } template<typename Type> const Type &as() const { return as_mutable<const Type>(); } BEGIN_KV_SERIALIZE_MAP() diff --git a/contrib/epee/src/CMakeLists.txt b/contrib/epee/src/CMakeLists.txt index 5640f5bb0..bdb3498c0 100644 --- a/contrib/epee/src/CMakeLists.txt +++ b/contrib/epee/src/CMakeLists.txt @@ -61,6 +61,7 @@ target_link_libraries(epee ${Boost_FILESYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${Boost_REGEX_LIBRARY} + ${Boost_SYSTEM_LIBRARY} ${OPENSSL_LIBRARIES} PRIVATE ${EXTRA_LIBRARIES}) @@ -69,6 +70,7 @@ if (USE_READLINE AND (GNU_READLINE_FOUND OR (DEPENDS AND NOT MINGW))) target_link_libraries(epee_readline PUBLIC easylogging + ${Boost_SYSTEM_LIBRARY} PRIVATE ${GNU_READLINE_LIBRARY}) endif() diff --git a/src/blocks/checkpoints.dat b/src/blocks/checkpoints.dat Binary files differindex 10eb6c45d..675e90dd7 100644 --- a/src/blocks/checkpoints.dat +++ b/src/blocks/checkpoints.dat diff --git a/src/checkpoints/checkpoints.cpp b/src/checkpoints/checkpoints.cpp index 2d48eaa1e..30bf5980d 100644 --- a/src/checkpoints/checkpoints.cpp +++ b/src/checkpoints/checkpoints.cpp @@ -247,6 +247,7 @@ namespace cryptonote ADD_CHECKPOINT2(2257500, "99643c32f27b157c6952a67af7dbe07ca819e71df386fa9379a344686d2950cf", "0x11c2f448d4f8830"); ADD_CHECKPOINT2(2265000, "727a6228a71f5b35c899553ee19d60bfc10c126a009ffd633afb30666e8edbe6", "0x121a33e656ecad4"); ADD_CHECKPOINT2(2325000, "f61261994b368700f0cbbfb4477433fa36a3c7537908ab2d1a06ac2987cc8b01", "0x154bceeffaff847"); + ADD_CHECKPOINT2(2430000, "5f7f4273eb6be024df3c4311a8d28482220a253190efa1719f35b7782587740c", "0x1c3f14ba9b0bebc"); return true; } diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 247b0f4c9..06e7cd87b 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -5390,7 +5390,7 @@ void Blockchain::cancel() } #if defined(PER_BLOCK_CHECKPOINT) -static const char expected_block_hashes_hash[] = "b198c2514bed71ca0a43cba6590090068daf6d207b351d8a4c7d0f0f791c095f"; +static const char expected_block_hashes_hash[] = "ff99bc76e59e0d6268e2d8ed54a2206d828de643fcb13be8d296611fc051a540"; void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints) { if (get_checkpoints == nullptr || !m_fast_sync) diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index eb920d6ce..b8cf2d124 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -269,9 +269,17 @@ namespace nodetool peerlist_entry pe{}; pe.adr = addr; - zone.second.m_peerlist.remove_from_peer_white(pe); - zone.second.m_peerlist.remove_from_peer_gray(pe); - zone.second.m_peerlist.remove_from_peer_anchor(addr); + if (addr.port() == 0) + { + zone.second.m_peerlist.evict_host_from_peerlist(true, pe); + zone.second.m_peerlist.evict_host_from_peerlist(false, pe); + } + else + { + zone.second.m_peerlist.remove_from_peer_white(pe); + zone.second.m_peerlist.remove_from_peer_gray(pe); + zone.second.m_peerlist.remove_from_peer_anchor(addr); + } for (const auto &c: conns) zone.second.m_net_server.get_config_object().close(c); @@ -331,6 +339,13 @@ namespace nodetool for (const auto &c: conns) zone.second.m_net_server.get_config_object().close(c); + for (int i = 0; i < 2; ++i) + zone.second.m_peerlist.filter(i == 0, [&subnet](const peerlist_entry &pe){ + if (pe.adr.get_type_id() != epee::net_utils::ipv4_network_address::get_type_id()) + return false; + return subnet.matches(pe.adr.as<const epee::net_utils::ipv4_network_address>()); + }); + conns.clear(); } diff --git a/src/p2p/net_peerlist.cpp b/src/p2p/net_peerlist.cpp index 42ab9727d..50dc6da77 100644 --- a/src/p2p/net_peerlist.cpp +++ b/src/p2p/net_peerlist.cpp @@ -289,17 +289,9 @@ namespace nodetool copy_peers(peers.anchor, m_peers_anchor.get<by_addr>()); } - void peerlist_manager::evict_host_from_white_peerlist(const peerlist_entry& pr) + void peerlist_manager::evict_host_from_peerlist(bool use_white, const peerlist_entry& pr) { - peers_indexed::index<by_time>::type& sorted_index=m_peers_white.get<by_time>(); - auto i = sorted_index.begin(); - while (i != sorted_index.end()) - { - if (i->adr.is_same_host(pr.adr)) - i = sorted_index.erase(i); - else - ++i; - } + filter(use_white, [&pr](const peerlist_entry& pe){ return pe.adr.is_same_host(pr.adr); }); } } diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h index c794b0f3b..61cc56935 100644 --- a/src/p2p/net_peerlist.h +++ b/src/p2p/net_peerlist.h @@ -109,7 +109,7 @@ namespace nodetool bool get_white_peer_by_index(peerlist_entry& p, size_t i); 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); + void evict_host_from_peerlist(bool white, const peerlist_entry& pr); bool append_with_peer_white(const peerlist_entry& pr); bool append_with_peer_gray(const peerlist_entry& pr); bool append_with_peer_anchor(const anchor_peerlist_entry& ple); @@ -120,6 +120,7 @@ namespace nodetool bool get_and_empty_anchor_peerlist(std::vector<anchor_peerlist_entry>& apl); bool remove_from_peer_anchor(const epee::net_utils::network_address& addr); bool remove_from_peer_white(const peerlist_entry& pe); + template<typename F> size_t filter(bool white, const F &f); // f returns true: drop, false: keep private: struct by_time{}; @@ -346,7 +347,7 @@ namespace nodetool if(by_addr_it_wt == m_peers_white.get<by_addr>().end()) { //put new record into white list - evict_host_from_white_peerlist(ple); + evict_host_from_peerlist(true, ple); m_peers_white.insert(ple); trim_white_peerlist(); }else @@ -519,5 +520,26 @@ namespace nodetool CATCH_ENTRY_L0("peerlist_manager::remove_from_peer_anchor()", false); } //-------------------------------------------------------------------------------------------------- + template<typename F> size_t peerlist_manager::filter(bool white, const F &f) + { + size_t filtered = 0; + TRY_ENTRY(); + CRITICAL_REGION_LOCAL(m_peerlist_lock); + peers_indexed::index<by_addr>::type& sorted_index = white ? m_peers_gray.get<by_addr>() : m_peers_white.get<by_addr>(); + auto i = sorted_index.begin(); + while (i != sorted_index.end()) + { + if (f(*i)) + { + i = sorted_index.erase(i); + ++filtered; + } + else + ++i; + } + CATCH_ENTRY_L0("peerlist_manager::filter()", filtered); + return filtered; + } + //-------------------------------------------------------------------------------------------------- } diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp index 67f042c2e..28e207ff2 100644 --- a/src/serialization/json_object.cpp +++ b/src/serialization/json_object.cpp @@ -1091,9 +1091,12 @@ void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::rctSig& }; INSERT_INTO_JSON_OBJECT(dest, type, sig.type); - INSERT_INTO_JSON_OBJECT(dest, encrypted, sig.ecdhInfo); - INSERT_INTO_JSON_OBJECT(dest, commitments, transform(sig.outPk, just_mask)); - INSERT_INTO_JSON_OBJECT(dest, fee, sig.txnFee); + if (sig.type != rct::RCTTypeNull) + { + INSERT_INTO_JSON_OBJECT(dest, encrypted, sig.ecdhInfo); + INSERT_INTO_JSON_OBJECT(dest, commitments, transform(sig.outPk, just_mask)); + INSERT_INTO_JSON_OBJECT(dest, fee, sig.txnFee); + } // prunable if (!sig.p.bulletproofs.empty() || !sig.p.rangeSigs.empty() || !sig.p.MGs.empty() || !sig.get_pseudo_outs().empty()) @@ -1122,9 +1125,12 @@ void fromJsonValue(const rapidjson::Value& val, rct::rctSig& sig) } GET_FROM_JSON_OBJECT(val, sig.type, type); - GET_FROM_JSON_OBJECT(val, sig.ecdhInfo, encrypted); - GET_FROM_JSON_OBJECT(val, sig.outPk, commitments); - GET_FROM_JSON_OBJECT(val, sig.txnFee, fee); + if (sig.type != rct::RCTTypeNull) + { + GET_FROM_JSON_OBJECT(val, sig.ecdhInfo, encrypted); + GET_FROM_JSON_OBJECT(val, sig.outPk, commitments); + GET_FROM_JSON_OBJECT(val, sig.txnFee, fee); + } // prunable const auto prunable = val.FindMember("prunable"); diff --git a/src/version.cpp.in b/src/version.cpp.in index 013c1adac..214b0a39d 100644 --- a/src/version.cpp.in +++ b/src/version.cpp.in @@ -1,5 +1,5 @@ #define DEF_MONERO_VERSION_TAG "@VERSIONTAG@" -#define DEF_MONERO_VERSION "0.17.2.0" +#define DEF_MONERO_VERSION "0.17.2.3" #define DEF_MONERO_RELEASE_NAME "Oxygen Orion" #define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG #define DEF_MONERO_VERSION_IS_RELEASE @VERSION_IS_RELEASE@ diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 94edba213..f019a38a8 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -2355,6 +2355,11 @@ bool WalletImpl::parse_uri(const std::string &uri, std::string &address, std::st return m_wallet->parse_uri(uri, address, payment_id, amount, tx_description, recipient_name, unknown_parameters, error); } +std::string WalletImpl::make_uri(const std::string &address, const std::string &payment_id, uint64_t amount, const std::string &tx_description, const std::string &recipient_name, std::string &error) const +{ + return m_wallet->make_uri(address, payment_id, amount, tx_description, recipient_name, error); +} + std::string WalletImpl::getDefaultDataDir() const { return tools::get_default_data_dir(); diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index b53548951..5b78e496e 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -204,6 +204,7 @@ public: virtual void startRefresh() override; virtual void pauseRefresh() override; virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error) override; + virtual std::string make_uri(const std::string &address, const std::string &payment_id, uint64_t amount, const std::string &tx_description, const std::string &recipient_name, std::string &error) const override; virtual std::string getDefaultDataDir() const override; virtual bool lightWalletLogin(bool &isNewWallet) const override; virtual bool lightWalletImportWalletRequest(std::string &payment_id, uint64_t &fee, bool &new_request, bool &request_fulfilled, std::string &payment_address, std::string &status) override; diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index 35516b35d..0b8be83d9 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -1020,6 +1020,7 @@ struct Wallet virtual bool verifyMessageWithPublicKey(const std::string &message, const std::string &publicKey, const std::string &signature) const = 0; virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error) = 0; + virtual std::string make_uri(const std::string &address, const std::string &payment_id, uint64_t amount, const std::string &tx_description, const std::string &recipient_name, std::string &error) const = 0; virtual std::string getDefaultDataDir() const = 0; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index d5befdaaf..45f991b81 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -142,6 +142,9 @@ using namespace cryptonote; #define IGNORE_LONG_PAYMENT_ID_FROM_BLOCK_VERSION 12 +#define DEFAULT_UNLOCK_TIME (CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE * DIFFICULTY_TARGET_V2) +#define RECENT_SPEND_WINDOW (50 * DIFFICULTY_TARGET_V2) + static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1"; static const std::string MULTISIG_EXTRA_INFO_MAGIC = "MultisigxV1"; @@ -1022,7 +1025,13 @@ gamma_picker::gamma_picker(const std::vector<uint64_t> &rct_offsets, double shap end = rct_offsets.data() + rct_offsets.size() - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE; num_rct_outputs = *(end - 1); THROW_WALLET_EXCEPTION_IF(num_rct_outputs == 0, error::wallet_internal_error, "No rct outputs"); + THROW_WALLET_EXCEPTION_IF(outputs_to_consider == 0, error::wallet_internal_error, "No rct outputs to consider"); average_output_time = DIFFICULTY_TARGET_V2 * blocks_to_consider / outputs_to_consider; // this assumes constant target over the whole rct range + if (average_output_time == 0) { + // TODO: apply this to all cases; do so alongside a hard fork, where all clients will update at the same time, preventing anonymity puddle formation + average_output_time = DIFFICULTY_TARGET_V2 * blocks_to_consider / static_cast<double>(outputs_to_consider); + } + THROW_WALLET_EXCEPTION_IF(average_output_time == 0, error::wallet_internal_error, "Average seconds per output cannot be 0."); }; gamma_picker::gamma_picker(const std::vector<uint64_t> &rct_offsets): gamma_picker(rct_offsets, GAMMA_SHAPE, GAMMA_SCALE) {} @@ -1031,6 +1040,34 @@ uint64_t gamma_picker::pick() { double x = gamma(engine); x = exp(x); + + if (x > DEFAULT_UNLOCK_TIME) + { + // We are trying to select an output from the chain that appeared 'x' seconds before the + // current chain tip, where 'x' is selected from the gamma distribution recommended in Miller et al. + // (https://arxiv.org/pdf/1704.04299/). + // Our method is to get the average time delta between outputs in the recent past, estimate the number of + // outputs 'n' that would have appeared between 'chain_tip - x' and 'chain_tip', select the real output at + // 'current_num_outputs - n', then randomly select an output from the block where that output appears. + // Source code to paper: https://github.com/maltemoeser/moneropaper + // + // Due to the 'default spendable age' mechanic in Monero, 'current_num_outputs' only contains + // currently *unlocked* outputs, which means the earliest output that can be selected is not at the chain tip! + // Therefore, we must offset 'x' so it matches up with the timing of the outputs being considered. We do + // this by saying if 'x` equals the expected age of the first unlocked output (compared to the current + // chain tip - i.e. DEFAULT_UNLOCK_TIME), then select the first unlocked output. + x -= DEFAULT_UNLOCK_TIME; + } + else + { + // If the spent time suggested by the gamma is less than the unlock time, that means the gamma is suggesting an output + // that is no longer feasible to be spent (possible since the gamma was constructed when consensus rules did not enforce the + // lock time). The assumption made in this code is that an output expected spent quicker than the unlock time would likely + // be spent within RECENT_SPEND_WINDOW after allowed. So it returns an output that falls between 0 and the RECENT_SPEND_WINDOW. + // The RECENT_SPEND_WINDOW was determined with empirical analysis of observed data. + x = crypto::rand_idx(static_cast<uint64_t>(RECENT_SPEND_WINDOW)); + } + uint64_t output_index = x / average_output_time; if (output_index >= num_rct_outputs) return std::numeric_limits<uint64_t>::max(); // bad pick @@ -1215,6 +1252,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std wallet2::~wallet2() { + deinit(); } bool wallet2::has_testnet_option(const boost::program_options::variables_map& vm) @@ -3691,9 +3729,11 @@ void wallet2::detach_blockchain(uint64_t height, std::map<std::pair<uint64_t, ui //---------------------------------------------------------------------------------------------------- bool wallet2::deinit() { - m_is_initialized=false; - unlock_keys_file(); - m_account.deinit(); + if(m_is_initialized) { + m_is_initialized = false; + unlock_keys_file(); + m_account.deinit(); + } return true; } //---------------------------------------------------------------------------------------------------- @@ -4363,7 +4403,7 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st account_public_address device_account_public_address; THROW_WALLET_EXCEPTION_IF(!hwdev.get_public_address(device_account_public_address), error::wallet_internal_error, "Cannot get a device address"); - THROW_WALLET_EXCEPTION_IF(device_account_public_address != m_account.get_keys().m_account_address, error::wallet_internal_error, "Device wallet does not match wallet address. " + THROW_WALLET_EXCEPTION_IF(device_account_public_address != m_account.get_keys().m_account_address, error::wallet_internal_error, "Device wallet does not match wallet address. If the device uses the passphrase feature, please check whether the passphrase was entered correctly (it may have been misspelled - different passphrases generate different wallets, passphrase is case-sensitive). " "Device address: " + cryptonote::get_account_address_as_str(m_nettype, false, device_account_public_address) + ", wallet address: " + m_account.get_public_address_str(m_nettype)); LOG_PRINT_L0("Device inited..."); @@ -8582,7 +8622,8 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> if (req.outputs[i].index == td.m_global_output_index) if (daemon_resp.outs[i].key == boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key) if (daemon_resp.outs[i].mask == mask) - real_out_found = true; + if (daemon_resp.outs[i].unlocked) + real_out_found = true; } THROW_WALLET_EXCEPTION_IF(!real_out_found, error::wallet_internal_error, "Daemon response did not include the requested real output"); diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index db578ca63..f324f8c72 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; @@ -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) { @@ -1390,7 +1485,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) { @@ -1448,7 +1543,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) { @@ -1523,7 +1618,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) { diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 6e39eca1e..c3ff3c018 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) @@ -173,6 +176,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); @@ -255,10 +261,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 81f83fb18..302ac7dc4 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 20 +#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; diff --git a/tests/unit_tests/epee_boosted_tcp_server.cpp b/tests/unit_tests/epee_boosted_tcp_server.cpp index b8481bf0b..b1ed9baf7 100644 --- a/tests/unit_tests/epee_boosted_tcp_server.cpp +++ b/tests/unit_tests/epee_boosted_tcp_server.cpp @@ -111,8 +111,7 @@ TEST(boosted_tcp_server, worker_threads_are_exception_resistant) { boost::unique_lock<boost::mutex> lock(mtx); - ASSERT_NE(boost::cv_status::timeout, cond.wait_for(lock, boost::chrono::seconds(5))); - ASSERT_EQ(4, counter); + ASSERT_TRUE(cond.wait_for(lock, boost::chrono::seconds(5), [&counter]{ return counter == 4; })); } // Check if threads are alive @@ -125,8 +124,7 @@ TEST(boosted_tcp_server, worker_threads_are_exception_resistant) { boost::unique_lock<boost::mutex> lock(mtx); - ASSERT_NE(boost::cv_status::timeout, cond.wait_for(lock, boost::chrono::seconds(5))); - ASSERT_EQ(4, counter); + ASSERT_TRUE(cond.wait_for(lock, boost::chrono::seconds(5), [&counter]{ return counter == 4; })); } srv.send_stop_signal(); diff --git a/tests/unit_tests/epee_utils.cpp b/tests/unit_tests/epee_utils.cpp index 8c89968b9..7ba9c26fa 100644 --- a/tests/unit_tests/epee_utils.cpp +++ b/tests/unit_tests/epee_utils.cpp @@ -1503,6 +1503,7 @@ TEST(NetUtils, NetworkAddress) constexpr static epee::net_utils::address_type get_type_id() noexcept { return epee::net_utils::address_type(-1); } constexpr static epee::net_utils::zone get_zone() noexcept { return epee::net_utils::zone::invalid; } constexpr static bool is_blockable() noexcept { return false; } + constexpr static uint16_t port() { return 0; } }; const epee::net_utils::network_address empty; diff --git a/tests/unit_tests/node_server.cpp b/tests/unit_tests/node_server.cpp index 8fc086a38..f91e2e78b 100644 --- a/tests/unit_tests/node_server.cpp +++ b/tests/unit_tests/node_server.cpp @@ -803,9 +803,11 @@ TEST(cryptonote_protocol_handler, race_condition) workers_t workers; } check; check.work = std::make_shared<work_t>(check.io_context); - check.workers.emplace_back([&check]{ - check.io_context.run(); - }); + while (check.workers.size() < 2) { + check.workers.emplace_back([&check]{ + check.io_context.run(); + }); + } while (daemon.main.conn.size() < 1) { daemon.main.conn.emplace_back(new connection_t(check.io_context, daemon.main.shared_state, {}, {})); daemon.alt.conn.emplace_back(new connection_t(io_context, daemon.alt.shared_state, {}, {})); @@ -864,7 +866,7 @@ TEST(cryptonote_protocol_handler, race_condition) } } while (daemon.main.conn.size() < 2) { - daemon.main.conn.emplace_back(new connection_t(io_context, daemon.main.shared_state, {}, {})); + daemon.main.conn.emplace_back(new connection_t(check.io_context, daemon.main.shared_state, {}, {})); daemon.alt.conn.emplace_back(new connection_t(io_context, daemon.alt.shared_state, {}, {})); create_conn_pair(daemon.main.conn.back(), daemon.alt.conn.back()); conduct_handshake(daemon.alt.net_node, daemon.alt.conn.back()); |