diff options
-rw-r--r-- | .github/workflows/depends.yml | 2 | ||||
-rw-r--r-- | contrib/epee/include/serialization/wire/write.h | 9 | ||||
-rw-r--r-- | src/cryptonote_basic/tx_extra.h | 8 | ||||
-rw-r--r-- | src/rpc/zmq_server.cpp | 23 | ||||
-rw-r--r-- | src/serialization/container.h | 4 | ||||
-rw-r--r-- | src/serialization/debug_archive.h | 10 | ||||
-rw-r--r-- | src/serialization/difficulty_type.h | 2 | ||||
-rw-r--r-- | src/serialization/pair.h | 4 | ||||
-rw-r--r-- | src/serialization/serialization.h | 125 | ||||
-rw-r--r-- | src/serialization/tuple.h | 2 | ||||
-rw-r--r-- | src/serialization/variant.h | 45 | ||||
-rw-r--r-- | src/wallet/message_transporter.cpp | 13 | ||||
-rw-r--r-- | src/wallet/wallet2.cpp | 63 | ||||
-rw-r--r-- | tests/unit_tests/serialization.cpp | 39 | ||||
-rw-r--r-- | utils/gpg_keys/tobtoht.asc | 51 |
15 files changed, 220 insertions, 180 deletions
diff --git a/.github/workflows/depends.yml b/.github/workflows/depends.yml index 4827bb51d..c534965e8 100644 --- a/.github/workflows/depends.yml +++ b/.github/workflows/depends.yml @@ -105,7 +105,7 @@ jobs: ${{env.CCACHE_SETTINGS}} make depends target=${{ matrix.toolchain.host }} -j2 - uses: actions/upload-artifact@v3 - if: ${{ matrix.toolchain.host == 'x86_64-w64-mingw32' || matrix.toolchain.host == 'x86_64-apple-darwin11' || matrix.toolchain.host == 'x86_64-unknown-linux-gnu' }} + if: ${{ matrix.toolchain.host == 'x86_64-w64-mingw32' || matrix.toolchain.host == 'x86_64-apple-darwin' || matrix.toolchain.host == 'x86_64-unknown-linux-gnu' }} with: name: ${{ matrix.toolchain.name }} path: | diff --git a/contrib/epee/include/serialization/wire/write.h b/contrib/epee/include/serialization/wire/write.h index c18f7dbcc..c2359918c 100644 --- a/contrib/epee/include/serialization/wire/write.h +++ b/contrib/epee/include/serialization/wire/write.h @@ -30,6 +30,7 @@ #include <boost/utility/string_ref.hpp> #include <boost/range/size.hpp> #include <cstdint> +#include <iterator> #include <system_error> #include <type_traits> @@ -188,7 +189,13 @@ namespace wire_write template<typename T> inline std::size_t array_size(std::true_type, const T& source) - { return boost::size(source); } + { + static_assert( + !std::is_same<typename std::iterator_traits<typename T::const_iterator>::iterator_category, std::input_iterator_tag>{}, + "Input iterators must use json (or similar) derived classes directly" + ); + return boost::size(source); + } template<typename T> inline constexpr std::size_t array_size(std::false_type, const T&) noexcept diff --git a/src/cryptonote_basic/tx_extra.h b/src/cryptonote_basic/tx_extra.h index af6ee5197..a29f126ee 100644 --- a/src/cryptonote_basic/tx_extra.h +++ b/src/cryptonote_basic/tx_extra.h @@ -52,7 +52,7 @@ namespace cryptonote // load template <template <bool> class Archive> - bool do_serialize(Archive<false>& ar) + bool member_do_serialize(Archive<false>& ar) { // size - 1 - because of variant tag for (size = 1; size <= TX_EXTRA_PADDING_MAX_COUNT; ++size) @@ -73,7 +73,7 @@ namespace cryptonote // store template <template <bool> class Archive> - bool do_serialize(Archive<true>& ar) + bool member_do_serialize(Archive<true>& ar) { if(TX_EXTRA_PADDING_MAX_COUNT < size) return false; @@ -129,7 +129,7 @@ namespace cryptonote // load template <template <bool> class Archive> - bool do_serialize(Archive<false>& ar) + bool member_do_serialize(Archive<false>& ar) { std::string field; if(!::do_serialize(ar, field)) @@ -142,7 +142,7 @@ namespace cryptonote // store template <template <bool> class Archive> - bool do_serialize(Archive<true>& ar) + bool member_do_serialize(Archive<true>& ar) { std::ostringstream oss; binary_archive<true> oar(oss); diff --git a/src/rpc/zmq_server.cpp b/src/rpc/zmq_server.cpp index cb8a8bea4..d73ea3bc9 100644 --- a/src/rpc/zmq_server.cpp +++ b/src/rpc/zmq_server.cpp @@ -158,13 +158,22 @@ void ZmqServer::serve() if (!pub || sockets[2].revents) { - std::string message = MONERO_UNWRAP(net::zmq::receive(rep.get(), read_flags)); - MDEBUG("Received RPC request: \"" << message << "\""); - epee::byte_slice response = handler.handle(std::move(message)); - - const boost::string_ref response_view{reinterpret_cast<const char*>(response.data()), response.size()}; - MDEBUG("Sending RPC reply: \"" << response_view << "\""); - MONERO_UNWRAP(net::zmq::send(std::move(response), rep.get())); + expect<std::string> message = net::zmq::receive(rep.get(), read_flags); + if (!message) + { + // EAGAIN can occur when using `zmq_poll`, which doesn't inspect for message validity + if (message != net::zmq::make_error_code(EAGAIN)) + MONERO_THROW(message.error(), "Read failure on ZMQ-RPC"); + } + else // no errors + { + MDEBUG("Received RPC request: \"" << *message << "\""); + epee::byte_slice response = handler.handle(std::move(*message)); + + const boost::string_ref response_view{reinterpret_cast<const char*>(response.data()), response.size()}; + MDEBUG("Sending RPC reply: \"" << response_view << "\""); + MONERO_UNWRAP(net::zmq::send(std::move(response), rep.get())); + } } } } diff --git a/src/serialization/container.h b/src/serialization/container.h index 80e1892f2..393c66399 100644 --- a/src/serialization/container.h +++ b/src/serialization/container.h @@ -42,7 +42,7 @@ namespace serialization typename std::enable_if<!use_container_varint<T>(), bool>::type serialize_container_element(Archive& ar, T& e) { - return ::do_serialize(ar, e); + return do_serialize(ar, e); } template<typename Archive, typename T> @@ -52,7 +52,7 @@ namespace serialization 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); + return do_serialize(ar, e); ar.serialize_varint(e); return true; } diff --git a/src/serialization/debug_archive.h b/src/serialization/debug_archive.h index ef3bb4345..f4df963db 100644 --- a/src/serialization/debug_archive.h +++ b/src/serialization/debug_archive.h @@ -42,14 +42,12 @@ struct debug_archive : public json_archive<W> { }; template <class T> -struct serializer<debug_archive<true>, T> +static inline bool do_serialize(debug_archive<true> &ar, T &v) { - static void serialize(debug_archive<true> &ar, T &v) - { ar.begin_object(); ar.tag(variant_serialization_traits<debug_archive<true>, T>::get_tag()); - serializer<json_archive<true>, T>::serialize(ar, v); + do_serialize(static_cast<json_archive<true>&>(ar), v); ar.end_object(); ar.stream() << std::endl; - } -}; + return true; +} diff --git a/src/serialization/difficulty_type.h b/src/serialization/difficulty_type.h index 37761839b..c071f287b 100644 --- a/src/serialization/difficulty_type.h +++ b/src/serialization/difficulty_type.h @@ -31,8 +31,6 @@ #include "cryptonote_basic/difficulty.h" #include "serialization.h" -template<> struct is_basic_type<cryptonote::difficulty_type> { typedef boost::true_type type; }; - template <template <bool> class Archive> inline bool do_serialize(Archive<false>& ar, cryptonote::difficulty_type &diff) { diff --git a/src/serialization/pair.h b/src/serialization/pair.h index f21041fe8..888033fb9 100644 --- a/src/serialization/pair.h +++ b/src/serialization/pair.h @@ -47,7 +47,7 @@ namespace serialization typename std::enable_if<!use_pair_varint<T>(), bool>::type serialize_pair_element(Archive& ar, T& e) { - return ::do_serialize(ar, e); + return do_serialize(ar, e); } template<typename Archive, typename T> @@ -57,7 +57,7 @@ namespace serialization 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); + return do_serialize(ar, e); ar.serialize_varint(e); return true; } diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h index efe1270a4..2911aecb5 100644 --- a/src/serialization/serialization.h +++ b/src/serialization/serialization.h @@ -57,73 +57,30 @@ template <class T> struct is_blob_type { typedef boost::false_type type; }; -/*! \struct has_free_serializer - * - * \brief a descriptor for dispatching serialize - */ -template <class T> -struct has_free_serializer { typedef boost::true_type type; }; - -/*! \struct is_basic_type - * - * \brief a descriptor for dispatching serialize - */ -template <class T> -struct is_basic_type { typedef boost::false_type type; }; - -template<typename F, typename S> -struct is_basic_type<std::pair<F,S>> { typedef boost::true_type type; }; -template<> -struct is_basic_type<std::string> { typedef boost::true_type type; }; - -/*! \struct serializer +/*! \fn do_serialize(Archive &ar, T &v) * - * \brief ... wouldn't a class be better? + * \brief main function for dispatching serialization for a given pair of archive and value types * - * \detailed The logic behind serializing data. Places the archive - * data into the supplied parameter. This dispatches based on the - * supplied \a T template parameter's traits of is_blob_type or it is - * an integral (as defined by the is_integral trait). Depends on the - * \a Archive parameter to have overloaded the serialize_blob(T v, - * size_t size) and serialize_int(T v) base on which trait it - * applied. When the class has neither types, it falls to the - * overloaded method do_serialize(Archive ar) in T to do the work. + * Types marked true with is_blob_type<T> will be serialized as a blob, integral types will be + * serialized as integers, and types who have a `member_do_serialize` method will be serialized + * using that method. Booleans are serialized like blobs. */ template <class Archive, class T> -struct serializer{ - static bool serialize(Archive &ar, T &v) { - return serialize(ar, v, typename boost::is_integral<T>::type(), typename is_blob_type<T>::type(), typename is_basic_type<T>::type()); - } - template<typename A> - static bool serialize(Archive &ar, T &v, boost::false_type, boost::true_type, A a) { - ar.serialize_blob(&v, sizeof(v)); - return true; - } - template<typename A> - static bool serialize(Archive &ar, T &v, boost::true_type, boost::false_type, A a) { - ar.serialize_int(v); - return true; - } - static bool serialize(Archive &ar, T &v, boost::false_type, boost::false_type, boost::false_type) { - //serialize_custom(ar, v, typename has_free_serializer<T>::type()); - return v.do_serialize(ar); - } - static bool serialize(Archive &ar, T &v, boost::false_type, boost::false_type, boost::true_type) { - //serialize_custom(ar, v, typename has_free_serializer<T>::type()); - return do_serialize(ar, v); - } - static void serialize_custom(Archive &ar, T &v, boost::true_type) { - } -}; - -/*! \fn do_serialize(Archive &ar, T &v) - * - * \brief just calls the serialize function defined for ar and v... - */ +inline std::enable_if_t<is_blob_type<T>::type::value, bool> do_serialize(Archive &ar, T &v) +{ + ar.serialize_blob(&v, sizeof(v)); + return true; +} template <class Archive, class T> -inline bool do_serialize(Archive &ar, T &v) +inline std::enable_if_t<boost::is_integral<T>::value, bool> do_serialize(Archive &ar, T &v) { - return ::serializer<Archive, T>::serialize(ar, v); + ar.serialize_int(v); + return true; +} +template <class Archive, class T> +inline auto do_serialize(Archive &ar, T &v) -> decltype(v.member_do_serialize(ar), true) +{ + return v.member_do_serialize(ar); } template <class Archive> inline bool do_serialize(Archive &ar, bool &v) @@ -144,16 +101,6 @@ inline bool do_serialize(Archive &ar, bool &v) typedef boost::true_type type; \ } -/*! \macro FREE_SERIALIZER - * - * \brief adds the has_free_serializer to the type - */ -#define FREE_SERIALIZER(T) \ - template<> \ - struct has_free_serializer<T> { \ - typedef boost::true_type type; \ - } - /*! \macro VARIANT_TAG * * \brief Adds the tag \tag to the \a Archive of \a Type @@ -174,7 +121,7 @@ inline bool do_serialize(Archive &ar, bool &v) */ #define BEGIN_SERIALIZE() \ template <bool W, template <bool> class Archive> \ - bool do_serialize(Archive<W> &ar) { + bool member_do_serialize(Archive<W> &ar) { /*! \macro BEGIN_SERIALIZE_OBJECT * @@ -183,7 +130,7 @@ inline bool do_serialize(Archive &ar, bool &v) */ #define BEGIN_SERIALIZE_OBJECT() \ template <bool W, template <bool> class Archive> \ - bool do_serialize(Archive<W> &ar) { \ + bool member_do_serialize(Archive<W> &ar) { \ ar.begin_object(); \ bool r = do_serialize_object(ar); \ ar.end_object(); \ @@ -197,11 +144,6 @@ inline bool do_serialize(Archive &ar, bool &v) #define PREPARE_CUSTOM_VECTOR_SERIALIZATION(size, vec) \ ::serialization::detail::prepare_custom_vector_serialization(size, vec, typename Archive<W>::is_saving()) -/*! \macro PREPARE_CUSTOM_DEQUE_SERIALIZATION - */ -#define PREPARE_CUSTOM_DEQUE_SERIALIZATION(size, vec) \ - ::serialization::detail::prepare_custom_deque_serialization(size, vec, typename Archive<W>::is_saving()) - /*! \macro END_SERIALIZE * \brief self-explanatory */ @@ -209,16 +151,6 @@ inline bool do_serialize(Archive &ar, bool &v) return ar.good(); \ } -/*! \macro VALUE(f) - * \brief the same as FIELD(f) - */ -#define VALUE(f) \ - do { \ - ar.tag(#f); \ - bool r = ::do_serialize(ar, f); \ - if (!r || !ar.good()) return false; \ - } while(0); - /*! \macro FIELD_N(t,f) * * \brief serializes a field \a f tagged \a t @@ -226,7 +158,7 @@ inline bool do_serialize(Archive &ar, bool &v) #define FIELD_N(t, f) \ do { \ ar.tag(t); \ - bool r = ::do_serialize(ar, f); \ + bool r = do_serialize(ar, f); \ if (!r || !ar.good()) return false; \ } while(0); @@ -237,7 +169,7 @@ inline bool do_serialize(Archive &ar, bool &v) #define FIELD(f) \ do { \ ar.tag(#f); \ - bool r = ::do_serialize(ar, f); \ + bool r = do_serialize(ar, f); \ if (!r || !ar.good()) return false; \ } while(0); @@ -247,7 +179,7 @@ inline bool do_serialize(Archive &ar, bool &v) */ #define FIELDS(f) \ do { \ - bool r = ::do_serialize(ar, f); \ + bool r = do_serialize(ar, f); \ if (!r || !ar.good()) return false; \ } while(0); @@ -317,17 +249,6 @@ namespace serialization { vec.resize(size); } - template <typename T> - void prepare_custom_deque_serialization(size_t size, std::deque<T>& vec, const boost::mpl::bool_<true>& /*is_saving*/) - { - } - - template <typename T> - void prepare_custom_deque_serialization(size_t size, std::deque<T>& vec, const boost::mpl::bool_<false>& /*is_saving*/) - { - vec.resize(size); - } - /*! \fn do_check_stream_state * * \brief self explanatory diff --git a/src/serialization/tuple.h b/src/serialization/tuple.h index e76ca0b99..d9592bc96 100644 --- a/src/serialization/tuple.h +++ b/src/serialization/tuple.h @@ -39,7 +39,7 @@ namespace serialization template <typename Archive, class T> bool serialize_tuple_element(Archive& ar, T& e) { - return ::do_serialize(ar, e); + return do_serialize(ar, e); } template <typename Archive> diff --git a/src/serialization/variant.h b/src/serialization/variant.h index 77d767a1c..1cf93c8cf 100644 --- a/src/serialization/variant.h +++ b/src/serialization/variant.h @@ -72,7 +72,7 @@ struct variant_reader { if(variant_serialization_traits<Archive, current_type>::get_tag() == t) { current_type x; - if(!::do_serialize(ar, x)) + if(!do_serialize(ar, x)) { ar.set_fail(); return false; @@ -100,19 +100,13 @@ struct variant_reader<Archive, Variant, TBegin, TBegin> } }; - -template <template <bool> class Archive, BOOST_VARIANT_ENUM_PARAMS(typename T)> -struct serializer<Archive<false>, boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)>> -{ - typedef boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> variant_type; - typedef typename Archive<false>::variant_tag_type variant_tag_type; - typedef typename variant_type::types types; - - static bool serialize(Archive<false> &ar, variant_type &v) { - variant_tag_type t; +template <template <bool> class Archive, typename... T> +static bool do_serialize(Archive<false> &ar, boost::variant<T...> &v) { + using types = typename boost::variant<T...>::types; + typename Archive<false>::variant_tag_type t; ar.begin_variant(); ar.read_variant_tag(t); - if(!variant_reader<Archive<false>, variant_type, + if(!variant_reader<Archive<false>, boost::variant<T...>, typename boost::mpl::begin<types>::type, typename boost::mpl::end<types>::type>::read(ar, v, t)) { @@ -121,27 +115,21 @@ struct serializer<Archive<false>, boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)>> } ar.end_variant(); return true; - } -}; +} -template <template <bool> class Archive, BOOST_VARIANT_ENUM_PARAMS(typename T)> -struct serializer<Archive<true>, boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)>> +template <template <bool> class Archive> +struct variant_write_visitor : public boost::static_visitor<bool> { - typedef boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> variant_type; - //typedef typename Archive<true>::variant_tag_type variant_tag_type; - - struct visitor : public boost::static_visitor<bool> - { Archive<true> &ar; - visitor(Archive<true> &a) : ar(a) { } + variant_write_visitor(Archive<true> &a) : ar(a) { } template <class T> bool operator ()(T &rv) const { ar.begin_variant(); ar.write_variant_tag(variant_serialization_traits<Archive<true>, T>::get_tag()); - if(!::do_serialize(ar, rv)) + if(!do_serialize(ar, rv)) { ar.set_fail(); return false; @@ -149,9 +137,10 @@ struct serializer<Archive<true>, boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)>> ar.end_variant(); return true; } - }; - - static bool serialize(Archive<true> &ar, variant_type &v) { - return boost::apply_visitor(visitor(ar), v); - } }; + +template <template <bool> class Archive, typename... T> +static bool do_serialize(Archive<true> &ar, boost::variant<T...> &v) +{ + return boost::apply_visitor(variant_write_visitor<Archive>(ar), v); +} diff --git a/src/wallet/message_transporter.cpp b/src/wallet/message_transporter.cpp index b4f6ce7bd..e6c26cba0 100644 --- a/src/wallet/message_transporter.cpp +++ b/src/wallet/message_transporter.cpp @@ -273,16 +273,29 @@ bool message_transporter::post_request(const std::string &request, std::string & { if ((string_value.find("API Error 0021") == 0) && (request.find("joinChan") != std::string::npos)) { + // "API Error 0021: Unexpected API Failure" // Error that occurs if one tries to join an already joined chan, which can happen // if several auto-config participants share one PyBitmessage instance: As a little // hack simply ignore the error. (A clean solution would be to check for the chan // with 'listAddresses2', but parsing the returned array is much more complicated.) } + else if ((string_value.find("API Error 0024") == 0) && (request.find("joinChan") != std::string::npos)) + { + // "API Error 0024: Chan address is already present." + // Maybe a result of creating the chan in a slightly different way i.e. not with + // 'createChan'; everything works by just ignoring this error + } else if ((string_value.find("API Error 0013") == 0) && (request.find("leaveChan") != std::string::npos)) { + // "API Error 0013: Could not find your fromAddress in the keys.dat file." // Error that occurs if one tries to leave an already left / deleted chan, which can happen // if several auto-config participants share one PyBitmessage instance: Also ignore. } + else if ((string_value.find("API Error 0025") == 0) && (request.find("leaveChan") != std::string::npos)) + { + // "API Error 0025: Specified address is not a chan address. Use deleteAddress API call instead." + // Error does not really make sense, but everything works by just ignoring + } else { THROW_WALLET_EXCEPTION(tools::error::bitmessage_api_error, string_value); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index b5392d5f9..12e4621bf 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -7304,9 +7304,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pendin crypto::key_derivation derivation; std::vector<crypto::key_derivation> additional_derivations; - // compute public keys from out secret keys - crypto::public_key tx_pub_key; - crypto::secret_key_to_public_key(txs[n].tx_key, tx_pub_key); + crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx); std::vector<crypto::public_key> additional_tx_pub_keys; for (const crypto::secret_key &skey: txs[n].additional_tx_keys) { @@ -8685,6 +8683,26 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> COMMAND_RPC_GET_OUTPUTS_BIN::request req = AUTO_VAL_INIT(req); COMMAND_RPC_GET_OUTPUTS_BIN::response daemon_resp = AUTO_VAL_INIT(daemon_resp); + // The secret picking order contains outputs in the order that we selected them. + // + // We will later sort the output request entries in a pre-determined order so that the daemon + // that we're requesting information from doesn't learn any information about the true spend + // for each ring. However, internally, we want to prefer to construct our rings using the + // outputs that we picked first versus outputs picked later. + // + // The reason why is because each consecutive output pick within a ring becomes increasing less + // statistically independent from other picks, since we pick outputs from a finite set + // *without replacement*, due to the protocol not allowing duplicate ring members. This effect + // is exacerbated by the fact that we pick 1.5x + 75 as many outputs as we need per RPC + // request to account for unusable outputs. This effect is small, but non-neglibile and gets + // worse with larger ring sizes. + std::vector<get_outputs_out> secret_picking_order; + + // Convenience/safety lambda to make sure that both output lists req.outputs and secret_picking_order are updated together + // Each ring section of req.outputs gets sorted later after selecting all outputs for that ring + const auto add_output_to_lists = [&req, &secret_picking_order](const get_outputs_out &goo) + { req.outputs.push_back(goo); secret_picking_order.push_back(goo); }; + std::unique_ptr<gamma_picker> gamma; if (has_rct) gamma.reset(new gamma_picker(rct_offsets)); @@ -8819,7 +8837,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> if (out < num_outs) { MINFO("Using it"); - req.outputs.push_back({amount, out}); + add_output_to_lists({amount, out}); ++num_found; seen_indices.emplace(out); if (out == td.m_global_output_index) @@ -8841,12 +8859,12 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> if (num_outs <= requested_outputs_count) { for (uint64_t i = 0; i < num_outs; i++) - req.outputs.push_back({amount, i}); + add_output_to_lists({amount, i}); // duplicate to make up shortfall: this will be caught after the RPC call, // so we can also output the amounts for which we can't reach the required // mixin after checking the actual unlockedness for (uint64_t i = num_outs; i < requested_outputs_count; ++i) - req.outputs.push_back({amount, num_outs - 1}); + add_output_to_lists({amount, num_outs - 1}); } else { @@ -8855,7 +8873,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> { num_found = 1; seen_indices.emplace(td.m_global_output_index); - req.outputs.push_back({amount, td.m_global_output_index}); + add_output_to_lists({amount, td.m_global_output_index}); LOG_PRINT_L1("Selecting real output: " << td.m_global_output_index << " for " << print_money(amount)); } @@ -8963,7 +8981,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> seen_indices.emplace(i); picks[type].insert(i); - req.outputs.push_back({amount, i}); + add_output_to_lists({amount, i}); ++num_found; MDEBUG("picked " << i << ", " << num_found << " now picked"); } @@ -8977,7 +8995,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> // we'll error out later while (num_found < requested_outputs_count) { - req.outputs.push_back({amount, 0}); + add_output_to_lists({amount, 0}); ++num_found; } } @@ -8987,6 +9005,10 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> [](const get_outputs_out &a, const get_outputs_out &b) { return a.index < b.index; }); } + THROW_WALLET_EXCEPTION_IF(req.outputs.size() != secret_picking_order.size(), error::wallet_internal_error, + "bug: we did not update req.outputs/secret_picking_order in tandem"); + + // List all requested outputs to debug log if (ELPP->vRegistry()->allowed(el::Level::Debug, MONERO_DEFAULT_LOG_CATEGORY)) { std::map<uint64_t, std::set<uint64_t>> outs; @@ -9104,18 +9126,21 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> } } - // then pick others in random order till we reach the required number - // since we use an equiprobable pick here, we don't upset the triangular distribution - std::vector<size_t> order; - order.resize(requested_outputs_count); - for (size_t n = 0; n < order.size(); ++n) - order[n] = n; - std::shuffle(order.begin(), order.end(), crypto::random_device{}); - + // While we are still lacking outputs in this result ring, in our secret pick order... LOG_PRINT_L2("Looking for " << (fake_outputs_count+1) << " outputs of size " << print_money(td.is_rct() ? 0 : td.amount())); - for (size_t o = 0; o < requested_outputs_count && outs.back().size() < fake_outputs_count + 1; ++o) + for (size_t ring_pick_idx = base; ring_pick_idx < base + requested_outputs_count && outs.back().size() < fake_outputs_count + 1; ++ring_pick_idx) { - size_t i = base + order[o]; + const get_outputs_out attempted_output = secret_picking_order[ring_pick_idx]; + + // Find the index i of our pick in the request/response arrays + size_t i; + for (i = base; i < base + requested_outputs_count; ++i) + if (req.outputs[i].index == attempted_output.index) + break; + THROW_WALLET_EXCEPTION_IF(i == base + requested_outputs_count, error::wallet_internal_error, + "Could not find index of picked output in requested outputs"); + + // Try adding this output's information to result ring if output isn't invalid LOG_PRINT_L2("Index " << i << "/" << requested_outputs_count << ": idx " << req.outputs[i].index << " (real " << td.m_global_output_index << "), unlocked " << daemon_resp.outs[i].unlocked << ", key " << daemon_resp.outs[i].key); tx_add_fake_output(outs, req.outputs[i].index, daemon_resp.outs[i].key, daemon_resp.outs[i].mask, td.m_global_output_index, daemon_resp.outs[i].unlocked, valid_public_keys_cache); } diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 49f1dc310..ab81acefc 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -59,9 +59,7 @@ struct Struct }; template <class Archive> -struct serializer<Archive, Struct> -{ - static bool serialize(Archive &ar, Struct &s) { +static bool do_serialize(Archive &ar, Struct &s) { ar.begin_object(); ar.tag("a"); ar.serialize_int(s.a); @@ -71,8 +69,7 @@ struct serializer<Archive, Struct> ar.serialize_blob(s.blob, sizeof(s.blob)); ar.end_object(); return true; - } -}; +} struct Struct1 { @@ -122,6 +119,23 @@ bool try_parse(const string &blob) return serialization::parse_binary(blob, s1); } +namespace example_namespace +{ + struct ADLExampleStruct + { + std::string msg; + }; + + template <class Archive> + static bool do_serialize(Archive &ar, ADLExampleStruct &aes) + { + ar.begin_object(); + FIELD_N("custom_fieldname", aes.msg); + ar.end_object(); + return ar.good(); + } +} + TEST(Serialization, BinaryArchiveInts) { uint64_t x = 0xff00000000, x1; @@ -1178,3 +1192,18 @@ TEST(Serialization, difficulty_type) ASSERT_EQ(v_original, v_unserialized); } + +TEST(Serialization, adl_free_function) +{ + std::stringstream ss; + json_archive<true> ar(ss); + + const std::string msg = "Howdy, World!"; + example_namespace::ADLExampleStruct aes{msg}; + + ASSERT_TRUE(serialization::serialize(ar, aes)); + + // VVVVVVVVVVVVVVVVVVVVVVVVVV weird string serialization artifact + const std::string expected = "{\"custom_fieldname\": " + std::to_string(msg.size()) + '"' + epee::string_tools::buff_to_hex_nodelimer(msg) + "\"}"; + EXPECT_EQ(expected, ss.str()); +} diff --git a/utils/gpg_keys/tobtoht.asc b/utils/gpg_keys/tobtoht.asc new file mode 100644 index 000000000..16311b2d8 --- /dev/null +++ b/utils/gpg_keys/tobtoht.asc @@ -0,0 +1,51 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=KI4D +-----END PGP PUBLIC KEY BLOCK----- |