aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--contrib/epee/include/net/http_client.h42
-rw-r--r--contrib/epee/include/serialization/wire.h51
-rw-r--r--contrib/epee/include/serialization/wire/adapted/list.h41
-rw-r--r--contrib/epee/include/serialization/wire/adapted/vector.h61
-rw-r--r--contrib/epee/include/serialization/wire/error.h137
-rw-r--r--contrib/epee/include/serialization/wire/field.h141
-rw-r--r--contrib/epee/include/serialization/wire/fwd.h103
-rw-r--r--contrib/epee/include/serialization/wire/traits.h195
-rw-r--r--contrib/epee/include/serialization/wire/wrapper/defaulted.h77
-rw-r--r--contrib/epee/include/serialization/wire/write.h287
-rw-r--r--contrib/epee/include/span.h10
-rw-r--r--src/blockchain_db/blockchain_db.h2
-rw-r--r--src/blockchain_utilities/blockchain_ancestry.cpp11
-rw-r--r--src/blockchain_utilities/blockchain_depth.cpp17
-rw-r--r--src/blockchain_utilities/blockchain_export.cpp19
-rw-r--r--src/blockchain_utilities/blockchain_prune.cpp25
-rw-r--r--src/blockchain_utilities/blockchain_prune_known_spent_data.cpp12
-rw-r--r--src/blockchain_utilities/blockchain_stats.cpp11
-rw-r--r--src/blockchain_utilities/blockchain_usage.cpp14
-rw-r--r--src/cryptonote_core/blockchain.h16
-rw-r--r--src/cryptonote_core/blockchain_and_pool.h58
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp8
-rw-r--r--src/cryptonote_core/cryptonote_core.h8
-rw-r--r--src/cryptonote_core/tx_pool.h17
-rw-r--r--src/net/tor_address.cpp5
-rw-r--r--src/net/tor_address.h2
-rw-r--r--src/rpc/core_rpc_server.cpp4
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h5
-rw-r--r--src/simplewallet/simplewallet.cpp4
-rw-r--r--src/wallet/wallet2.cpp53
-rw-r--r--src/wallet/wallet2.h2
-rw-r--r--src/wallet/wallet_rpc_server.cpp33
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h2
-rw-r--r--tests/block_weight/block_weight.cpp9
-rw-r--r--tests/core_tests/chaingen.cpp17
-rwxr-xr-xtests/functional_tests/mining.py10
-rwxr-xr-xtests/functional_tests/multisig.py155
-rw-r--r--tests/unit_tests/long_term_block_weight.cpp11
-rw-r--r--tests/unit_tests/net.cpp103
-rw-r--r--tests/unit_tests/output_distribution.cpp12
-rw-r--r--tests/unit_tests/scaling_2021.cpp7
-rw-r--r--utils/python-rpc/framework/wallet.py3
42 files changed, 1508 insertions, 292 deletions
diff --git a/contrib/epee/include/net/http_client.h b/contrib/epee/include/net/http_client.h
index 8cee399cb..ecbceb566 100644
--- a/contrib/epee/include/net/http_client.h
+++ b/contrib/epee/include/net/http_client.h
@@ -199,8 +199,18 @@ namespace net_utils
}
}
+ // This magic var determines the maximum length for when copying the body message in
+ // memory is faster/more preferable than the round-trip time for one packet
+ constexpr size_t BODY_NO_COPY_CUTOFF = 128 * 1024; // ~262 KB or ~175 packets
+
+ // Maximum expected total headers bytes
+ constexpr size_t HEADER_RESERVE_SIZE = 2048;
+
+ const bool do_copy_body = body.size() <= BODY_NO_COPY_CUTOFF;
+ const size_t req_buff_cap = HEADER_RESERVE_SIZE + (do_copy_body ? body.size() : 0);
+
std::string req_buff{};
- req_buff.reserve(2048);
+ req_buff.reserve(req_buff_cap);
req_buff.append(method.data(), method.size()).append(" ").append(uri.data(), uri.size()).append(" HTTP/1.1\r\n");
add_field(req_buff, "Host", m_host_buff);
add_field(req_buff, "Content-Length", std::to_string(body.size()));
@@ -209,9 +219,7 @@ namespace net_utils
for(const auto& field : additional_params)
add_field(req_buff, field);
- for (unsigned sends = 0; sends < 2; ++sends)
{
- const std::size_t initial_size = req_buff.size();
const auto auth = m_auth.get_auth_field(method, uri);
if (auth)
add_field(req_buff, *auth);
@@ -219,11 +227,21 @@ namespace net_utils
req_buff += "\r\n";
//--
- bool res = m_net_client.send(req_buff, timeout);
- CHECK_AND_ASSERT_MES(res, false, "HTTP_CLIENT: Failed to SEND");
- if(body.size())
+ if (do_copy_body) // small body
+ {
+ // Copy headers + body together and potentially send one fewer packet
+ req_buff.append(body.data(), body.size());
+ const bool res = m_net_client.send(req_buff, timeout);
+ CHECK_AND_ASSERT_MES(res, false, "HTTP_CLIENT: Failed to SEND");
+ }
+ else // large body
+ {
+ // Send headers and body seperately to avoid copying heavy body message
+ bool res = m_net_client.send(req_buff, timeout);
+ CHECK_AND_ASSERT_MES(res, false, "HTTP_CLIENT: Failed to SEND");
res = m_net_client.send(body, timeout);
- CHECK_AND_ASSERT_MES(res, false, "HTTP_CLIENT: Failed to SEND");
+ CHECK_AND_ASSERT_MES(res, false, "HTTP_CLIENT: Failed to SEND");
+ }
m_response_info.clear();
m_state = reciev_machine_state_header;
@@ -236,19 +254,11 @@ namespace net_utils
return true;
}
- switch (m_auth.handle_401(m_response_info))
+ if (m_auth.handle_401(m_response_info) == http_client_auth::kParseFailure)
{
- case http_client_auth::kSuccess:
- break;
- case http_client_auth::kBadPassword:
- sends = 2;
- break;
- default:
- case http_client_auth::kParseFailure:
LOG_ERROR("Bad server response for authentication");
return false;
}
- req_buff.resize(initial_size); // rollback for new auth generation
}
LOG_ERROR("Client has incorrect username/password for server requiring authentication");
return false;
diff --git a/contrib/epee/include/serialization/wire.h b/contrib/epee/include/serialization/wire.h
new file mode 100644
index 000000000..4dbb0b2f4
--- /dev/null
+++ b/contrib/epee/include/serialization/wire.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2021-2023, The Monero Project
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include "serialization/wire/fwd.h"
+#include "serialization/wire/read.h"
+#include "serialization/wire/write.h"
+
+//! Define functions that list fields in `type` (using virtual interface)
+#define WIRE_DEFINE_OBJECT(type, map) \
+ void read_bytes(::wire::reader& source, type& dest) \
+ { map(source, dest); } \
+ \
+ void write_bytes(::wire::writer& dest, const type& source) \
+ { map(dest, source); }
+
+//! Define `from_bytes` and `to_bytes` for `this`.
+#define WIRE_DEFINE_CONVERSIONS() \
+ template<typename R, typename T> \
+ std::error_code from_bytes(T&& source) \
+ { return ::wire_read::from_bytes<R>(std::forward<T>(source), *this); } \
+ \
+ template<typename W, typename T> \
+ std::error_code to_bytes(T& dest) const \
+ { return ::wire_write::to_bytes<W>(dest, *this); }
+
diff --git a/contrib/epee/include/serialization/wire/adapted/list.h b/contrib/epee/include/serialization/wire/adapted/list.h
new file mode 100644
index 000000000..8884193ad
--- /dev/null
+++ b/contrib/epee/include/serialization/wire/adapted/list.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2023, The Monero Project
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include <list>
+#include <type_traits>
+
+#include "serialization/wire/traits.h"
+
+namespace wire
+{
+ template<typename T, typename A>
+ struct is_array<std::list<T, A>>
+ : std::true_type
+ {};
+}
diff --git a/contrib/epee/include/serialization/wire/adapted/vector.h b/contrib/epee/include/serialization/wire/adapted/vector.h
new file mode 100644
index 000000000..1dd4b0ded
--- /dev/null
+++ b/contrib/epee/include/serialization/wire/adapted/vector.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2022-2023-2023, The Monero Project
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include <cstdint>
+#include <cstring>
+#include <type_traits>
+#include <vector>
+
+#include "byte_slice.h"
+#include "serialization/wire/traits.h"
+
+namespace wire
+{
+ template<typename T, typename A>
+ struct is_array<std::vector<T, A>>
+ : std::true_type
+ {};
+ template<typename A>
+ struct is_array<std::vector<std::uint8_t, A>>
+ : std::false_type
+ {};
+
+ template<typename R, typename A>
+ inline void read_bytes(R& source, std::vector<std::uint8_t, A>& dest)
+ {
+ const epee::byte_slice bytes = source.binary();
+ dest.resize(bytes.size());
+ std::memcpy(dest.data(), bytes.data(), bytes.size());
+ }
+ template<typename W, typename A>
+ inline void write_bytes(W& dest, const std::vector<std::uint8_t, A>& source)
+ {
+ dest.binary(epee::to_span(source));
+ }
+}
diff --git a/contrib/epee/include/serialization/wire/error.h b/contrib/epee/include/serialization/wire/error.h
new file mode 100644
index 000000000..b4920e53d
--- /dev/null
+++ b/contrib/epee/include/serialization/wire/error.h
@@ -0,0 +1,137 @@
+// Copyright (c) 2022-2023, The Monero Project
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include <exception>
+#include <system_error>
+#include <type_traits>
+
+#include "misc_log_ex.h"
+
+//! Print default `code` message followed by optional message to debug log then throw `code`.
+#define WIRE_DLOG_THROW_(code, ...) \
+ do \
+ { \
+ MDEBUG( get_string(code) __VA_ARGS__ ); \
+ throw ::wire::exception_t<decltype(code)>{code}; \
+ } \
+ while (0)
+
+//! Print default `code` message followed by `msg` to debug log then throw `code`.
+#define WIRE_DLOG_THROW(code, msg) \
+ WIRE_DLOG_THROW_(code, << ": " << msg)
+
+namespace wire
+{
+ namespace error
+ {
+ enum class schema : int
+ {
+ none = 0, //!< Must be zero for `expect<..>`
+ array, //!< Expected an array value
+ array_max_element,//!< Exceeded max array count
+ array_min_size, //!< Below min element wire size
+ binary, //!< Expected a binary value of variable length
+ boolean, //!< Expected a boolean value
+ enumeration, //!< Expected a value from a specific set
+ fixed_binary, //!< Expected a binary value of fixed length
+ integer, //!< Expected an integer value
+ invalid_key, //!< Key for object is invalid
+ larger_integer, //!< Expected a larger integer value
+ maximum_depth, //!< Hit maximum number of object+array tracking
+ missing_key, //!< Missing required key for object
+ number, //!< Expected a number (integer or float) value
+ object, //!< Expected object value
+ smaller_integer, //!< Expected a smaller integer value
+ string, //!< Expected string value
+ };
+
+ //! \return Error message string.
+ const char* get_string(schema value) noexcept;
+
+ //! \return Category for `schema_error`.
+ const std::error_category& schema_category() noexcept;
+
+ //! \return Error code with `value` and `schema_category()`.
+ inline std::error_code make_error_code(const schema value) noexcept
+ {
+ return std::error_code{int(value), schema_category()};
+ }
+ } // error
+
+ //! `std::exception` doesn't require dynamic memory like `std::runtime_error`
+ struct exception : std::exception
+ {
+ exception() noexcept
+ : std::exception()
+ {}
+
+ exception(const exception&) = default;
+ exception& operator=(const exception&) = default;
+ virtual ~exception() noexcept
+ {}
+
+ virtual std::error_code code() const noexcept = 0;
+ };
+
+ template<typename T>
+ class exception_t final : public wire::exception
+ {
+ static_assert(std::is_enum<T>(), "only enumerated types allowed");
+ T value;
+
+ public:
+ exception_t(T value) noexcept
+ : value(value)
+ {}
+
+ exception_t(const exception_t&) = default;
+ ~exception_t() = default;
+ exception_t& operator=(const exception_t&) = default;
+
+ const char* what() const noexcept override final
+ {
+ static_assert(noexcept(noexcept(get_string(value))), "get_string function must be noexcept");
+ return get_string(value);
+ }
+
+ std::error_code code() const noexcept override final
+ {
+ static_assert(noexcept(noexcept(make_error_code(value))), "make_error_code funcion must be noexcept");
+ return make_error_code(value);
+ }
+ };
+} // wire
+
+namespace std
+{
+ template<>
+ struct is_error_code_enum<wire::error::schema>
+ : true_type
+ {};
+}
diff --git a/contrib/epee/include/serialization/wire/field.h b/contrib/epee/include/serialization/wire/field.h
new file mode 100644
index 000000000..402fa9ad4
--- /dev/null
+++ b/contrib/epee/include/serialization/wire/field.h
@@ -0,0 +1,141 @@
+// Copyright (c) 2021-2023, The Monero Project
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include <functional>
+#include <utility>
+
+#include "serialization/wire/traits.h"
+
+//! A required field has the same key name and C/C++ name
+#define WIRE_FIELD(name) \
+ ::wire::field( #name , std::ref( self . name ))
+
+//! A required field has the same key name and C/C++ name AND is cheap to copy (faster output).
+#define WIRE_FIELD_COPY(name) \
+ ::wire::field( #name , self . name )
+
+//! The optional field has the same key name and C/C++ name
+#define WIRE_OPTIONAL_FIELD(name) \
+ ::wire::optional_field( #name , std::ref( self . name ))
+
+namespace wire
+{
+ /*! Links `name` to a `value` for object serialization.
+
+ `value_type` is `T` with optional `std::reference_wrapper` removed.
+ `value_type` needs a `read_bytes` function when parsing with a
+ `wire::reader` - see `read.h` for more info. `value_type` needs a
+ `write_bytes` function when writing with a `wire::writer` - see `write.h`
+ for more info.
+
+ Any `value_type` where `is_optional_on_empty<value_type> == true`, will
+ automatically be converted to an optional field iff `value_type` has an
+ `empty()` method that returns `true`. The old output engine omitted fields
+ when an array was empty, and the standard input macro would ignore the
+ `false` return for the missing field. For compability reasons, the
+ input/output engine here matches that behavior. See `wrapper/array.h` to
+ enforce a required field even when the array is empty or specialize the
+ `is_optional_on_empty` trait. Only new fields should use this behavior.
+
+ Additional concept requirements for `value_type` when `Required == false`:
+ * must have an `operator*()` function.
+ * must have a conversion to bool function that returns true when
+ `operator*()` is safe to call (and implicitly when the associated field
+ should be written as opposed to skipped/omitted).
+ Additional concept requirements for `value_type` when `Required == false`
+ when reading:
+ * must have an `emplace()` method that ensures `operator*()` is safe to call.
+ * must have a `reset()` method to indicate a field was skipped/omitted.
+
+ If a standard type needs custom serialization, one "trick":
+ ```
+ struct custom_tag{};
+ void read_bytes(wire::reader&, boost::fusion::pair<custom_tag, std::string&>)
+ { ... }
+ void write_bytes(wire::writer&, boost::fusion::pair<custom_tag, const std::string&>)
+ { ... }
+
+ template<typename F, typename T>
+ void object_map(F& format, T& self)
+ {
+ wire::object(format,
+ wire::field("foo", boost::fusion::make_pair<custom_tag>(std::ref(self.foo)))
+ );
+ }
+ ```
+
+ Basically each input/output format needs a unique type so that the compiler
+ knows how to "dispatch" the read/write calls. */
+ template<typename T, bool Required>
+ struct field_
+ {
+ using value_type = unwrap_reference_t<T>;
+
+ //! \return True if field is forced optional when `get_value().empty()`.
+ static constexpr bool optional_on_empty() noexcept
+ { return is_optional_on_empty<value_type>::value; }
+
+ static constexpr bool is_required() noexcept { return Required && !optional_on_empty(); }
+ static constexpr std::size_t count() noexcept { return 1; }
+
+ const char* name;
+ T value;
+
+ constexpr const value_type& get_value() const noexcept { return value; }
+ value_type& get_value() noexcept { return value; }
+ };
+
+ //! Links `name` to `value`. Use `std::ref` if de-serializing.
+ template<typename T>
+ constexpr inline field_<T, true> field(const char* name, T value)
+ {
+ return {name, std::move(value)};
+ }
+
+ //! Links `name` to optional `value`. Use `std::ref` if de-serializing.
+ template<typename T>
+ constexpr inline field_<T, false> optional_field(const char* name, T value)
+ {
+ return {name, std::move(value)};
+ }
+
+
+ template<typename T>
+ inline constexpr bool available(const field_<T, true>& elem)
+ {
+ /* The old output engine always skipped fields when it was an empty array,
+ this follows that behavior. See comments for `field_`. */
+ return elem.is_required() || (elem.optional_on_empty() && !wire::empty(elem.get_value()));
+ }
+ template<typename T>
+ inline constexpr bool available(const field_<T, false>& elem)
+ {
+ return bool(elem.get_value());
+ }
+} // wire
diff --git a/contrib/epee/include/serialization/wire/fwd.h b/contrib/epee/include/serialization/wire/fwd.h
new file mode 100644
index 000000000..f9e79dd1a
--- /dev/null
+++ b/contrib/epee/include/serialization/wire/fwd.h
@@ -0,0 +1,103 @@
+// Copyright (c) 2021-2023, The Monero Project
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include <type_traits>
+
+//! Declare an enum to be serialized as an integer
+#define WIRE_AS_INTEGER(type_) \
+ static_assert(std::is_enum<type_>(), "AS_INTEGER only enum types"); \
+ template<typename R> \
+ inline void read_bytes(R& source, type_& dest) \
+ { \
+ std::underlying_type<type_>::type temp{}; \
+ read_bytes(source, temp); \
+ dest = type_(temp); \
+ } \
+ template<typename W> \
+ inline void write_bytes(W& dest, const type_ source) \
+ { write_bytes(dest, std::underlying_type<type_>::type(source)); }
+
+//! Declare functions that list fields in `type` (using virtual interface)
+#define WIRE_DECLARE_OBJECT(type) \
+ void read_bytes(::wire::reader&, type&); \
+ void write_bytes(::wire::writer&, const type&)
+
+//! Cast readers to `rtype` and writers to `wtype` before code expansion
+#define WIRE_BEGIN_MAP_BASE(rtype, wtype) \
+ template<typename R> \
+ void read_bytes(R& source) \
+ { wire_map(std::true_type{}, static_cast<rtype&>(source), *this); } \
+ \
+ template<typename W> \
+ void write_bytes(W& dest) const \
+ { wire_map(std::false_type{}, static_cast<wtype&>(dest), *this); } \
+ \
+ template<typename B, typename F, typename T> \
+ static void wire_map(const B is_read, F& format, T& self) \
+ { ::wire::object_fwd(is_read, format
+
+/*! Define `read_bytes`, and `write_bytes` for `this` that forward the
+ derived format types for max performance. */
+#define WIRE_BEGIN_MAP() \
+ WIRE_BEGIN_MAP_BASE(R, W)
+
+/*! Define `read_bytes`, and `write_bytes` for `this` that forward base format
+ types to reduce code expansion and executable size. */
+#define WIRE_BEGIN_MAP_ASM_SIZE() \
+ WIRE_BEGIN_MAP_BASE(::wire::reader, ::wire::writer)
+
+//! End object map; omit last `,`
+#define WIRE_END_MAP() );}
+
+namespace wire
+{
+ struct basic_value;
+ class reader;
+ struct writer;
+
+ // defined in `wire/read.h`
+ template<typename R, typename... T>
+ void object_fwd(std::true_type is_read, R& source, T&&... fields);
+
+ // defined in `wire/write.h`
+ template<typename W, typename... T>
+ void object_fwd(std::false_type is_read, W& dest, T... fields);
+}
+namespace wire_read
+{
+ // defined in `wire/read.h`
+ template<typename R, typename T>
+ void bytes(R& source, T&& dest);
+}
+namespace wire_write
+{
+ // defined in `wire/write.h`
+ template<typename W, typename T>
+ void bytes(W& dest, const T& source);
+}
diff --git a/contrib/epee/include/serialization/wire/traits.h b/contrib/epee/include/serialization/wire/traits.h
new file mode 100644
index 000000000..284506a29
--- /dev/null
+++ b/contrib/epee/include/serialization/wire/traits.h
@@ -0,0 +1,195 @@
+// Copyright (c) 2021, The Monero Project
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include <cstdint>
+#include <type_traits>
+
+#define WIRE_DECLARE_BLOB_NS(type) \
+ template<> \
+ struct is_blob<type> \
+ : std::true_type \
+ {}
+
+#define WIRE_DECLARE_BLOB(type) \
+ namespace wire { WIRE_DECLARE_BLOB_NS(type); }
+
+#define WIRE_DECLARE_OPTIONAL_ROOT(type) \
+ template<> \
+ struct is_optional_root<type> \
+ : std::true_type \
+ {}
+
+namespace wire
+{
+ template<typename T>
+ struct unwrap_reference
+ {
+ using type = std::remove_cv_t<std::remove_reference_t<T>>;
+ };
+
+ template<typename T>
+ struct unwrap_reference<std::reference_wrapper<T>>
+ : std::remove_cv<T>
+ {};
+
+ template<typename T>
+ using unwrap_reference_t = typename unwrap_reference<T>::type;
+
+
+ /*! Mark `T` as an array for writing, and reading when
+ `default_min_element_size<T::value_type>::value != 0`. See `array_` in
+ `wrapper/array.h`. */
+ template<typename T>
+ struct is_array : std::false_type
+ {};
+
+ /*! Mark `T` as fixed binary data for reading+writing. Concept requirements
+ for reading:
+ * `T` must be compatible with `epee::as_mut_byte_span` (`std::is_pod<T>`
+ and no padding).
+ Concept requirements for writing:
+ * `T` must be compatible with `epee::as_byte_span` (std::is_pod<T>` and
+ no padding). */
+ template<typename T>
+ struct is_blob : std::false_type
+ {};
+
+ /*! Forces field to be optional when empty. Concept requirements for `T` when
+ `is_optional_on_empty<T>::value == true`:
+ * must have an `empty()` method that toggles whether the associated
+ `wire::field_<...>` is omitted by the `wire::writer`.
+ * must have a `clear()` method where `empty() == true` upon completion,
+ used by the `wire::reader` when the `wire::field_<...>` is omitted. */
+ template<typename T>
+ struct is_optional_on_empty
+ : is_array<T> // all array types in old output engine were optional when empty
+ {};
+
+ //! When `T` is being read as root object, allow an empty read buffer.
+ template<typename T>
+ struct is_optional_root
+ : std::is_empty<T>
+ {};
+
+ //! A constraint for `wire_read::array` where a max of `N` elements can be read.
+ template<std::size_t N>
+ struct max_element_count
+ : std::integral_constant<std::size_t, N>
+ {
+ // The threshold is low - min_element_size is a better constraint metric
+ static constexpr std::size_t max_bytes() noexcept { return 512 * 1024; } // 512 KiB
+
+ //! \return True if `N` C++ objects of type `T` are below `max_bytes()` threshold.
+ template<typename T>
+ static constexpr bool check() noexcept
+ {
+ return N <= (max_bytes() / sizeof(T));
+ }
+ };
+
+ //! A constraint for `wire_read::array` where each element must use at least `N` bytes on the wire.
+ template<std::size_t N>
+ struct min_element_size
+ : std::integral_constant<std::size_t, N>
+ {
+ static constexpr std::size_t max_ratio() noexcept { return 4; }
+
+ //! \return True if C++ object of type `T` with minimum wire size `N` is below `max_ratio()`.
+ template<typename T>
+ static constexpr bool check() noexcept
+ {
+ return N != 0 ? ((sizeof(T) / N) <= max_ratio()) : false;
+ }
+ };
+
+ /*! Trait used in `wire/read.h` for default `min_element_size` behavior based
+ on an array of `T` objects and `R` reader type. This trait can be used
+ instead of the `wire::array(...)` (and associated macros) functionality, as
+ it sets a global value. The last argument is for `enable_if`. */
+ template<typename R, typename T, typename = void>
+ struct default_min_element_size
+ : std::integral_constant<std::size_t, 0>
+ {};
+
+ //! If `T` is a blob, a safe default for all formats is the size of the blob
+ template<typename R, typename T>
+ struct default_min_element_size<R, T, std::enable_if_t<is_blob<T>::value>>
+ : std::integral_constant<std::size_t, sizeof(T)>
+ {};
+
+ // example usage : `wire::sum(std::size_t(wire::available(fields))...)`
+
+ inline constexpr int sum() noexcept
+ {
+ return 0;
+ }
+ template<typename T, typename... U>
+ inline constexpr T sum(const T head, const U... tail) noexcept
+ {
+ return head + sum(tail...);
+ }
+
+ template<typename... T>
+ using min_element_sizeof = min_element_size<sum(sizeof(T)...)>;
+
+ //! If container has no `reserve(0)` function, this function is used
+ template<typename... T>
+ inline void reserve(const T&...) noexcept
+ {}
+
+ //! Container has `reserve(std::size_t)` function, use it
+ template<typename T>
+ inline auto reserve(T& container, const std::size_t count) -> decltype(container.reserve(count))
+ { return container.reserve(count); }
+
+ //! If `T` has no `empty()` function, this function is used
+ template<typename... T>
+ inline constexpr bool empty(const T&...) noexcept
+ {
+ static_assert(sum(is_optional_on_empty<T>::value...) == 0, "type needs empty method");
+ return false;
+ }
+
+ //! `T` has `empty()` function, use it
+ template<typename T>
+ inline auto empty(const T& container) -> decltype(container.empty())
+ { return container.empty(); }
+
+ //! If `T` has no `clear()` function, this function is used
+ template<typename... T>
+ inline void clear(const T&...) noexcept
+ {
+ static_assert(sum(is_optional_on_empty<T>::value...) == 0, "type needs clear method");
+ }
+
+ //! `T` has `clear()` function, use it
+ template<typename T>
+ inline auto clear(T& container) -> decltype(container.clear())
+ { return container.clear(); }
+} // wire
diff --git a/contrib/epee/include/serialization/wire/wrapper/defaulted.h b/contrib/epee/include/serialization/wire/wrapper/defaulted.h
new file mode 100644
index 000000000..f9a411c9e
--- /dev/null
+++ b/contrib/epee/include/serialization/wire/wrapper/defaulted.h
@@ -0,0 +1,77 @@
+// Copyright (c) 2021-2023, The Monero Project
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include <functional>
+#include <utility>
+
+#include "serialization/wire/field.h"
+#include "serialization/wire/traits.h"
+
+//! An optional field that is omitted when a default value is used
+#define WIRE_FIELD_DEFAULTED(name, default_) \
+ ::wire::optional_field( #name , ::wire::defaulted(std::ref( self . name ), default_ ))
+
+namespace wire
+{
+ /*! A wrapper that tells `wire::writer`s to skip field generation when default
+ value, and tells `wire::reader`s to use default value when field not present. */
+ template<typename T, typename U>
+ struct defaulted_
+ {
+ using value_type = unwrap_reference_t<T>;
+
+ T value;
+ U default_;
+
+ constexpr const value_type& get_value() const noexcept { return value; }
+ value_type& get_value() noexcept { return value; }
+
+ // concept requirements for optional fields
+
+ constexpr explicit operator bool() const { return get_value() != default_; }
+ value_type& emplace() noexcept { return get_value(); }
+
+ constexpr const value_type& operator*() const noexcept { return get_value(); }
+ value_type& operator*() noexcept { return get_value(); }
+
+ void reset() { get_value() = default_; }
+ };
+
+ //! Links `value` with `default_`.
+ template<typename T, typename U>
+ inline constexpr defaulted_<T, U> defaulted(T value, U default_)
+ {
+ return {std::move(value), std::move(default_)};
+ }
+
+ /* read/write functions not needed since `defaulted_` meets the concept
+ requirements for an optional type (optional fields are handled
+ directly by the generic read/write code because the field name is omitted
+ entirely when the value is "empty"). */
+} // wire
diff --git a/contrib/epee/include/serialization/wire/write.h b/contrib/epee/include/serialization/wire/write.h
new file mode 100644
index 000000000..c18f7dbcc
--- /dev/null
+++ b/contrib/epee/include/serialization/wire/write.h
@@ -0,0 +1,287 @@
+// Copyright (c) 2023, The Monero Project
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include <boost/utility/string_ref.hpp>
+#include <boost/range/size.hpp>
+#include <cstdint>
+#include <system_error>
+#include <type_traits>
+
+#include "byte_slice.h"
+#include "serialization/wire/error.h"
+#include "serialization/wire/field.h"
+#include "serialization/wire/traits.h"
+#include "span.h"
+
+/*
+ Custom types (e.g type `type` in namespace `ns`) can define an output function by:
+ * `namespace wire { template<> struct is_array<ns::type> : std::true_type {}; }`
+ * `namespace wire { template<> struct is_blob<ns::type> : std::true_type {}; }`
+ * `namespace wire { void write_bytes(writer&, const ns::type&); }`
+ * `namespace ns { void write_bytes(wire::writer&, const type&); }`
+
+ See `wrappers.h` for `is_array` requirements, and `traits.h` for `is_blob`
+ requirements. `write_bytes` function can also specify derived type for faster
+ output (i.e. `namespace ns { void write_bytes(wire::epee_writer&, type&); }`).
+ Using the derived type allows the compiler to de-virtualize and allows for
+ custom functions not defined by base interface. Using base interface allows
+ for multiple formats with minimal instruction count. */
+
+namespace wire
+{
+ //! Interface for converting C/C++ objects to "wire" (byte) formats.
+ struct writer
+ {
+ writer() = default;
+
+ virtual ~writer() noexcept;
+
+ //! By default, insist on retrieving array size before writing array
+ static constexpr std::true_type need_array_size() noexcept { return{}; }
+
+ virtual void boolean(bool) = 0;
+
+ virtual void integer(std::intmax_t) = 0;
+ virtual void unsigned_integer(std::uintmax_t) = 0;
+
+ virtual void real(double) = 0;
+
+ virtual void string(boost::string_ref) = 0;
+ virtual void binary(epee::span<const std::uint8_t>) = 0;
+
+ virtual void start_array(std::size_t) = 0;
+ virtual void end_array() = 0;
+
+ virtual void start_object(std::size_t) = 0;
+ virtual void key(boost::string_ref) = 0;
+ virtual void binary_key(epee::span<const std::uint8_t>) = 0;
+ virtual void end_object() = 0;
+
+ protected:
+ writer(const writer&) = default;
+ writer(writer&&) = default;
+ writer& operator=(const writer&) = default;
+ writer& operator=(writer&&) = default;
+ };
+
+ template<typename W>
+ inline void write_arithmetic(W& dest, const bool source)
+ { dest.boolean(source); }
+
+ template<typename W>
+ inline void write_arithmetic(W& dest, const int source)
+ { dest.integer(source); }
+
+ template<typename W>
+ inline void write_arithmetic(W& dest, const long source)
+ { dest.integer(std::intmax_t(source)); }
+
+ template<typename W>
+ inline void write_arithmetic(W& dest, const long long source)
+ { dest.integer(std::intmax_t(source)); }
+
+ template<typename W>
+ inline void write_arithmetic(W& dest, const unsigned source)
+ { dest.unsigned_integer(source); }
+
+ template<typename W>
+ inline void write_arithmetic(W& dest, const unsigned long source)
+ { dest.unsigned_integer(std::uintmax_t(source)); }
+
+ template<typename W>
+ inline void write_arithmetic(W& dest, const unsigned long long source)
+ { dest.unsigned_integer(std::uintmax_t(source));}
+
+ template<typename W>
+ inline void write_arithmetic(W& dest, const double source)
+ { dest.real(source); }
+
+ // Template both arguments to allow derived writer specializations
+ template<typename W, typename T>
+ inline std::enable_if_t<std::is_arithmetic<T>::value> write_bytes(W& dest, const T source)
+ { write_arithmetic(dest, source); }
+
+ template<typename W>
+ inline void write_bytes(W& dest, const boost::string_ref source)
+ { dest.string(source); }
+
+ template<typename W, typename T>
+ inline std::enable_if_t<is_blob<T>::value> write_bytes(W& dest, const T& source)
+ { dest.binary(epee::as_byte_span(source)); }
+
+ template<typename W>
+ inline void write_bytes(W& dest, const epee::span<const std::uint8_t> source)
+ { dest.binary(source); }
+
+ template<typename W>
+ inline void write_bytes(W& dest, const epee::byte_slice& source)
+ { write_bytes(dest, epee::to_span(source)); }
+
+ //! Use `write_bytes(...)` method if available for `T`.
+ template<typename W, typename T>
+ inline auto write_bytes(W& dest, const T& source) -> decltype(source.write_bytes(dest))
+ { return source.write_bytes(dest); }
+}
+
+namespace wire_write
+{
+ /*! Don't add a function called `write_bytes` to this namespace, it will
+ prevent ADL lookup. ADL lookup delays the function searching until the
+ template is used instead of when its defined. This allows the unqualified
+ calls to `write_bytes` in this namespace to "find" user functions that are
+ declared after these functions. */
+
+ template<typename W, typename T>
+ inline void bytes(W& dest, const T& source)
+ {
+ write_bytes(dest, source); // ADL (searches every associated namespace)
+ }
+
+ //! Use writer `W` to convert `source` into bytes appended to `dest`.
+ template<typename W, typename T, typename U>
+ inline std::error_code to_bytes(T& dest, const U& source)
+ {
+ try
+ {
+ W out{std::move(dest)};
+ bytes(out, source);
+ dest = out.take_buffer();
+ }
+ catch (const wire::exception& e)
+ {
+ dest.clear();
+ return e.code();
+ }
+ catch (...)
+ {
+ dest.clear();
+ throw;
+ }
+ return {};
+ }
+
+ template<typename T>
+ inline std::size_t array_size(std::true_type, const T& source)
+ { return boost::size(source); }
+
+ template<typename T>
+ inline constexpr std::size_t array_size(std::false_type, const T&) noexcept
+ { return 0; }
+
+ template<typename W, typename T>
+ inline void array(W& dest, const T& source)
+ {
+ using value_type = typename T::value_type;
+ static_assert(!std::is_same<value_type, char>::value, "write array of chars as string");
+ static_assert(!std::is_same<value_type, std::int8_t>::value, "write array of signed chars as binary");
+ static_assert(!std::is_same<value_type, std::uint8_t>::value, "write array of unsigned chars as binary");
+
+ dest.start_array(array_size(dest.need_array_size(), source));
+ for (const auto& elem : source)
+ bytes(dest, elem);
+ dest.end_array();
+ }
+
+ template<typename W, typename T>
+ inline bool field(W& dest, const wire::field_<T, true>& field)
+ {
+ // Arrays always optional, see `wire/field.h`
+ if (wire::available(field))
+ {
+ dest.key(field.name);
+ bytes(dest, field.get_value());
+ }
+ return true;
+ }
+
+ template<typename W, typename T>
+ inline bool field(W& dest, const wire::field_<T, false>& field)
+ {
+ if (wire::available(field))
+ {
+ dest.key(field.name);
+ bytes(dest, *field.get_value());
+ }
+ return true;
+ }
+
+ template<typename W, typename T>
+ inline std::enable_if_t<std::is_pod<T>::value> dynamic_object_key(W& dest, const T& source)
+ {
+ dest.binary_key(epee::as_byte_span(source));
+ }
+
+ template<typename W>
+ inline void dynamic_object_key(W& dest, const boost::string_ref source)
+ {
+ dest.key(source);
+ }
+
+ template<typename W, typename T>
+ inline void dynamic_object(W& dest, const T& source)
+ {
+ dest.start_object(source.size());
+ for (const auto& elem : source)
+ {
+ dynamic_object_key(dest, elem.first);
+ bytes(dest, elem.second);
+ }
+ dest.end_object();
+ }
+
+ template<typename W, typename... T>
+ inline void object(W& dest, T&&... fields)
+ {
+ dest.start_object(wire::sum(std::size_t(wire::available(fields))...));
+ const bool dummy[] = {field(dest, std::forward<T>(fields))...};
+ dest.end_object();
+ (void)dummy; // expand into array to get 0,1,2,etc order
+ }
+} // wire_write
+
+namespace wire
+{
+ template<typename W, typename T>
+ inline std::enable_if_t<is_array<T>::value> write_bytes(W& dest, const T& source)
+ {
+ wire_write::array(dest, source);
+ }
+
+ template<typename W, typename... T>
+ inline std::enable_if_t<std::is_base_of<writer, W>::value> object(W& dest, T... fields)
+ {
+ wire_write::object(dest, std::move(fields)...);
+ }
+
+ template<typename W, typename... T>
+ inline void object_fwd(const std::false_type /* is_read */, W& dest, T... fields)
+ {
+ wire::object(dest, std::move(fields)...);
+ }
+}
diff --git a/contrib/epee/include/span.h b/contrib/epee/include/span.h
index 82936a777..bbd5d690c 100644
--- a/contrib/epee/include/span.h
+++ b/contrib/epee/include/span.h
@@ -147,6 +147,16 @@ namespace epee
return {reinterpret_cast<const std::uint8_t*>(src.data()), src.size_bytes()};
}
+ //! \return `span<std::uint8_t>` from a STL compatible `src`.
+ template<typename T>
+ constexpr span<std::uint8_t> to_mut_byte_span(T& src)
+ {
+ using value_type = typename T::value_type;
+ static_assert(!std::is_empty<value_type>(), "empty value types will not work -> sizeof == 1");
+ static_assert(!has_padding<value_type>(), "source value type may have padding");
+ return {reinterpret_cast<std::uint8_t*>(src.data()), src.size() * sizeof(value_type)};
+ }
+
//! \return `span<const std::uint8_t>` which represents the bytes at `&src`.
template<typename T>
span<const std::uint8_t> as_byte_span(const T& src) noexcept
diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h
index 835d6dcad..b38ec9e05 100644
--- a/src/blockchain_db/blockchain_db.h
+++ b/src/blockchain_db/blockchain_db.h
@@ -1053,7 +1053,7 @@ public:
* @brief fetch a block's already generated coins
*
* The subclass should return the total coins generated as of the block
- * with the given height.
+ * with the given height, capped to a maximum value of MONEY_SUPPLY.
*
* If the block does not exist, the subclass should throw BLOCK_DNE
*
diff --git a/src/blockchain_utilities/blockchain_ancestry.cpp b/src/blockchain_utilities/blockchain_ancestry.cpp
index 66dd7813b..36c17357a 100644
--- a/src/blockchain_utilities/blockchain_ancestry.cpp
+++ b/src/blockchain_utilities/blockchain_ancestry.cpp
@@ -37,10 +37,7 @@
#include "common/command_line.h"
#include "common/varint.h"
#include "cryptonote_basic/cryptonote_boost_serialization.h"
-#include "cryptonote_core/tx_pool.h"
#include "cryptonote_core/cryptonote_core.h"
-#include "cryptonote_core/blockchain.h"
-#include "blockchain_db/blockchain_db.h"
#include "version.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
@@ -449,9 +446,7 @@ int main(int argc, char* argv[])
// because unlike blockchain_storage constructor, which takes a pointer to
// tx_memory_pool, Blockchain's constructor takes tx_memory_pool object.
LOG_PRINT_L0("Initializing source blockchain (BlockchainDB)");
- std::unique_ptr<Blockchain> core_storage;
- tx_memory_pool m_mempool(*core_storage);
- core_storage.reset(new Blockchain(m_mempool));
+ std::unique_ptr<BlockchainAndPool> core_storage = std::make_unique<BlockchainAndPool>();
BlockchainDB *db = new_db();
if (db == NULL)
{
@@ -472,7 +467,7 @@ int main(int argc, char* argv[])
LOG_PRINT_L0("Error opening database: " << e.what());
return 1;
}
- r = core_storage->init(db, net_type);
+ r = core_storage->blockchain.init(db, net_type);
CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize source blockchain storage");
LOG_PRINT_L0("Source blockchain storage initialized OK");
@@ -716,7 +711,7 @@ int main(int argc, char* argv[])
}
done:
- core_storage->deinit();
+ core_storage->blockchain.deinit();
if (opt_show_cache_stats)
MINFO("cache: txes " << std::to_string(cached_txes*100./total_txes)
diff --git a/src/blockchain_utilities/blockchain_depth.cpp b/src/blockchain_utilities/blockchain_depth.cpp
index 6a06e0a96..f49211233 100644
--- a/src/blockchain_utilities/blockchain_depth.cpp
+++ b/src/blockchain_utilities/blockchain_depth.cpp
@@ -31,10 +31,7 @@
#include <boost/algorithm/string.hpp>
#include "common/command_line.h"
#include "common/varint.h"
-#include "cryptonote_core/tx_pool.h"
#include "cryptonote_core/cryptonote_core.h"
-#include "cryptonote_core/blockchain.h"
-#include "blockchain_db/blockchain_db.h"
#include "version.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
@@ -129,16 +126,8 @@ int main(int argc, char* argv[])
// Use Blockchain instead of lower-level BlockchainDB for two reasons:
// 1. Blockchain has the init() method for easy setup
// 2. exporter needs to use get_current_blockchain_height(), get_block_id_by_height(), get_block_by_hash()
- //
- // cannot match blockchain_storage setup above with just one line,
- // e.g.
- // Blockchain* core_storage = new Blockchain(NULL);
- // because unlike blockchain_storage constructor, which takes a pointer to
- // tx_memory_pool, Blockchain's constructor takes tx_memory_pool object.
LOG_PRINT_L0("Initializing source blockchain (BlockchainDB)");
- std::unique_ptr<Blockchain> core_storage;
- tx_memory_pool m_mempool(*core_storage);
- core_storage.reset(new Blockchain(m_mempool));
+ std::unique_ptr<BlockchainAndPool> core_storage = std::make_unique<BlockchainAndPool>();
BlockchainDB *db = new_db();
if (db == NULL)
{
@@ -159,7 +148,7 @@ int main(int argc, char* argv[])
LOG_PRINT_L0("Error opening database: " << e.what());
return 1;
}
- r = core_storage->init(db, net_type);
+ r = core_storage->blockchain.init(db, net_type);
CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize source blockchain storage");
LOG_PRINT_L0("Source blockchain storage initialized OK");
@@ -327,7 +316,7 @@ done:
LOG_PRINT_L0("Average min depth for " << start_txids.size() << " transaction(s): " << cumulative_depth/(float)depths.size());
LOG_PRINT_L0("Median min depth for " << start_txids.size() << " transaction(s): " << epee::misc_utils::median(depths));
- core_storage->deinit();
+ core_storage->blockchain.deinit();
return 0;
CATCH_ENTRY("Depth query error", 1);
diff --git a/src/blockchain_utilities/blockchain_export.cpp b/src/blockchain_utilities/blockchain_export.cpp
index 3d7b3f61a..1f8370034 100644
--- a/src/blockchain_utilities/blockchain_export.cpp
+++ b/src/blockchain_utilities/blockchain_export.cpp
@@ -29,7 +29,6 @@
#include "bootstrap_file.h"
#include "blocksdat_file.h"
#include "common/command_line.h"
-#include "cryptonote_core/tx_pool.h"
#include "cryptonote_core/cryptonote_core.h"
#include "blockchain_db/blockchain_db.h"
#include "version.h"
@@ -129,16 +128,8 @@ int main(int argc, char* argv[])
// Use Blockchain instead of lower-level BlockchainDB for two reasons:
// 1. Blockchain has the init() method for easy setup
// 2. exporter needs to use get_current_blockchain_height(), get_block_id_by_height(), get_block_by_hash()
- //
- // cannot match blockchain_storage setup above with just one line,
- // e.g.
- // Blockchain* core_storage = new Blockchain(NULL);
- // because unlike blockchain_storage constructor, which takes a pointer to
- // tx_memory_pool, Blockchain's constructor takes tx_memory_pool object.
LOG_PRINT_L0("Initializing source blockchain (BlockchainDB)");
- Blockchain* core_storage = NULL;
- tx_memory_pool m_mempool(*core_storage);
- core_storage = new Blockchain(m_mempool);
+ std::unique_ptr<BlockchainAndPool> core_storage = std::make_unique<BlockchainAndPool>();
BlockchainDB* db = new_db();
if (db == NULL)
@@ -162,9 +153,9 @@ int main(int argc, char* argv[])
LOG_PRINT_L0("Error opening database: " << e.what());
return 1;
}
- r = core_storage->init(db, opt_testnet ? cryptonote::TESTNET : opt_stagenet ? cryptonote::STAGENET : cryptonote::MAINNET);
+ r = core_storage->blockchain.init(db, opt_testnet ? cryptonote::TESTNET : opt_stagenet ? cryptonote::STAGENET : cryptonote::MAINNET);
- if (core_storage->get_blockchain_pruning_seed() && !opt_blocks_dat)
+ if (core_storage->blockchain.get_blockchain_pruning_seed() && !opt_blocks_dat)
{
LOG_PRINT_L0("Blockchain is pruned, cannot export");
return 1;
@@ -177,12 +168,12 @@ int main(int argc, char* argv[])
if (opt_blocks_dat)
{
BlocksdatFile blocksdat;
- r = blocksdat.store_blockchain_raw(core_storage, NULL, output_file_path, block_stop);
+ r = blocksdat.store_blockchain_raw(&core_storage->blockchain, NULL, output_file_path, block_stop);
}
else
{
BootstrapFile bootstrap;
- r = bootstrap.store_blockchain_raw(core_storage, NULL, output_file_path, block_start, block_stop);
+ r = bootstrap.store_blockchain_raw(&core_storage->blockchain, NULL, output_file_path, block_start, block_stop);
}
CHECK_AND_ASSERT_MES(r, 1, "Failed to export blockchain raw data");
LOG_PRINT_L0("Blockchain raw data exported OK");
diff --git a/src/blockchain_utilities/blockchain_prune.cpp b/src/blockchain_utilities/blockchain_prune.cpp
index 1e4b48b73..1a9618617 100644
--- a/src/blockchain_utilities/blockchain_prune.cpp
+++ b/src/blockchain_utilities/blockchain_prune.cpp
@@ -35,8 +35,6 @@
#include "common/command_line.h"
#include "common/pruning.h"
#include "cryptonote_core/cryptonote_core.h"
-#include "cryptonote_core/blockchain.h"
-#include "blockchain_db/blockchain_db.h"
#include "blockchain_db/lmdb/db_lmdb.h"
#include "version.h"
@@ -562,22 +560,15 @@ int main(int argc, char* argv[])
// Use Blockchain instead of lower-level BlockchainDB for two reasons:
// 1. Blockchain has the init() method for easy setup
// 2. exporter needs to use get_current_blockchain_height(), get_block_id_by_height(), get_block_by_hash()
- //
- // cannot match blockchain_storage setup above with just one line,
- // e.g.
- // Blockchain* core_storage = new Blockchain(NULL);
- // because unlike blockchain_storage constructor, which takes a pointer to
- // tx_memory_pool, Blockchain's constructor takes tx_memory_pool object.
MINFO("Initializing source blockchain (BlockchainDB)");
- std::array<std::unique_ptr<Blockchain>, 2> core_storage;
- Blockchain *blockchain = NULL;
- tx_memory_pool m_mempool(*blockchain);
+ std::array<std::unique_ptr<BlockchainAndPool>, 2> core_storage{
+ std::make_unique<BlockchainAndPool>(),
+ std::make_unique<BlockchainAndPool>()};
+
boost::filesystem::path paths[2];
bool already_pruned = false;
for (size_t n = 0; n < core_storage.size(); ++n)
{
- core_storage[n].reset(new Blockchain(m_mempool));
-
BlockchainDB* db = new_db();
if (db == NULL)
{
@@ -622,12 +613,12 @@ int main(int argc, char* argv[])
MERROR("Error opening database: " << e.what());
return 1;
}
- r = core_storage[n]->init(db, net_type);
+ r = core_storage[n]->blockchain.init(db, net_type);
std::string source_dest = n == 0 ? "source" : "pruned";
CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize " << source_dest << " blockchain storage");
MINFO(source_dest << " blockchain storage initialized OK");
- if (n == 0 && core_storage[0]->get_blockchain_pruning_seed())
+ if (n == 0 && core_storage[0]->blockchain.get_blockchain_pruning_seed())
{
if (!opt_copy_pruned_database)
{
@@ -637,9 +628,9 @@ int main(int argc, char* argv[])
already_pruned = true;
}
}
- core_storage[0]->deinit();
+ core_storage[0]->blockchain.deinit();
core_storage[0].reset(NULL);
- core_storage[1]->deinit();
+ core_storage[1]->blockchain.deinit();
core_storage[1].reset(NULL);
MINFO("Pruning...");
diff --git a/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp b/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp
index 4da9c15c1..4a459dc66 100644
--- a/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp
+++ b/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp
@@ -30,10 +30,7 @@
#include <boost/filesystem.hpp>
#include "common/command_line.h"
#include "serialization/crypto.h"
-#include "cryptonote_core/tx_pool.h"
#include "cryptonote_core/cryptonote_core.h"
-#include "cryptonote_core/blockchain.h"
-#include "blockchain_db/blockchain_db.h"
#include "version.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
@@ -160,9 +157,8 @@ int main(int argc, char* argv[])
const std::string input = command_line::get_arg(vm, arg_input);
LOG_PRINT_L0("Initializing source blockchain (BlockchainDB)");
- std::unique_ptr<Blockchain> core_storage;
- tx_memory_pool m_mempool(*core_storage);
- core_storage.reset(new Blockchain(m_mempool));
+ std::unique_ptr<BlockchainAndPool> core_storage = std::make_unique<BlockchainAndPool>();
+
BlockchainDB *db = new_db();
if (db == NULL)
{
@@ -182,7 +178,7 @@ int main(int argc, char* argv[])
LOG_PRINT_L0("Error opening database: " << e.what());
return 1;
}
- r = core_storage->init(db, net_type);
+ r = core_storage->blockchain.init(db, net_type);
CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize source blockchain storage");
LOG_PRINT_L0("Source blockchain storage initialized OK");
@@ -280,7 +276,7 @@ int main(int argc, char* argv[])
MINFO("Prunable outputs: " << num_prunable_outputs);
LOG_PRINT_L0("Blockchain known spent data pruned OK");
- core_storage->deinit();
+ core_storage->blockchain.deinit();
return 0;
CATCH_ENTRY("Error", 1);
diff --git a/src/blockchain_utilities/blockchain_stats.cpp b/src/blockchain_utilities/blockchain_stats.cpp
index 5e4245ebd..f65054fc5 100644
--- a/src/blockchain_utilities/blockchain_stats.cpp
+++ b/src/blockchain_utilities/blockchain_stats.cpp
@@ -31,9 +31,7 @@
#include "common/command_line.h"
#include "common/varint.h"
#include "cryptonote_basic/cryptonote_boost_serialization.h"
-#include "cryptonote_core/tx_pool.h"
#include "cryptonote_core/cryptonote_core.h"
-#include "cryptonote_core/blockchain.h"
#include "blockchain_db/blockchain_db.h"
#include "version.h"
@@ -203,9 +201,8 @@ int main(int argc, char* argv[])
do_diff = command_line::get_arg(vm, arg_diff);
LOG_PRINT_L0("Initializing source blockchain (BlockchainDB)");
- std::unique_ptr<Blockchain> core_storage;
- tx_memory_pool m_mempool(*core_storage);
- core_storage.reset(new Blockchain(m_mempool));
+ std::unique_ptr<BlockchainAndPool> core_storage = std::make_unique<BlockchainAndPool>();
+
BlockchainDB *db = new_db();
if (db == NULL)
{
@@ -225,7 +222,7 @@ int main(int argc, char* argv[])
LOG_PRINT_L0("Error opening database: " << e.what());
return 1;
}
- r = core_storage->init(db, net_type);
+ r = core_storage->blockchain.init(db, net_type);
CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize source blockchain storage");
LOG_PRINT_L0("Source blockchain storage initialized OK");
@@ -381,7 +378,7 @@ plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, ''
if (currblks)
doprint();
- core_storage->deinit();
+ core_storage->blockchain.deinit();
return 0;
CATCH_ENTRY("Stats reporting error", 1);
diff --git a/src/blockchain_utilities/blockchain_usage.cpp b/src/blockchain_utilities/blockchain_usage.cpp
index a5228eb92..0b9686765 100644
--- a/src/blockchain_utilities/blockchain_usage.cpp
+++ b/src/blockchain_utilities/blockchain_usage.cpp
@@ -31,10 +31,7 @@
#include <boost/filesystem/path.hpp>
#include "common/command_line.h"
#include "common/varint.h"
-#include "cryptonote_core/tx_pool.h"
#include "cryptonote_core/cryptonote_core.h"
-#include "cryptonote_core/blockchain.h"
-#include "blockchain_db/blockchain_db.h"
#include "version.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
@@ -151,9 +148,8 @@ int main(int argc, char* argv[])
// tx_memory_pool, Blockchain's constructor takes tx_memory_pool object.
LOG_PRINT_L0("Initializing source blockchain (BlockchainDB)");
const std::string input = command_line::get_arg(vm, arg_input);
- std::unique_ptr<Blockchain> core_storage;
- tx_memory_pool m_mempool(*core_storage);
- core_storage.reset(new Blockchain(m_mempool));
+ std::unique_ptr<BlockchainAndPool> core_storage = std::make_unique<BlockchainAndPool>();
+
BlockchainDB* db = new_db();
if (db == NULL)
{
@@ -174,7 +170,7 @@ int main(int argc, char* argv[])
LOG_PRINT_L0("Error opening database: " << e.what());
return 1;
}
- r = core_storage->init(db, net_type);
+ r = core_storage->blockchain.init(db, net_type);
CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize source blockchain storage");
LOG_PRINT_L0("Source blockchain storage initialized OK");
@@ -185,10 +181,10 @@ int main(int argc, char* argv[])
std::unordered_map<uint64_t,uint64_t> indices;
LOG_PRINT_L0("Reading blockchain from " << input);
- core_storage->for_all_transactions([&](const crypto::hash &hash, const cryptonote::transaction &tx)->bool
+ core_storage->blockchain.for_all_transactions([&](const crypto::hash &hash, const cryptonote::transaction &tx)->bool
{
const bool coinbase = tx.vin.size() == 1 && tx.vin[0].type() == typeid(txin_gen);
- const uint64_t height = core_storage->get_db().get_tx_block_height(hash);
+ const uint64_t height = core_storage->blockchain.get_db().get_tx_block_height(hash);
// create new outputs
for (const auto &out: tx.vout)
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index a45d3ec60..3ad051fc3 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -113,13 +113,6 @@ namespace cryptonote
};
/**
- * @brief Blockchain constructor
- *
- * @param tx_pool a reference to the transaction pool to be kept by the Blockchain
- */
- Blockchain(tx_memory_pool& tx_pool);
-
- /**
* @brief Blockchain destructor
*/
~Blockchain();
@@ -1236,6 +1229,13 @@ namespace cryptonote
mutable rct_ver_cache_t m_rct_ver_cache;
/**
+ * @brief Blockchain constructor
+ *
+ * @param tx_pool a reference to the transaction pool to be kept by the Blockchain
+ */
+ Blockchain(tx_memory_pool& tx_pool);
+
+ /**
* @brief collects the keys for all outputs being "spent" as an input
*
* This function makes sure that each "input" in an input (mixins) exists
@@ -1608,5 +1608,7 @@ namespace cryptonote
* @param already_generated_coins total coins mined by the network so far
*/
void send_miner_notifications(uint64_t height, const crypto::hash &seed_hash, const crypto::hash &prev_id, uint64_t already_generated_coins);
+
+ friend class BlockchainAndPool;
};
} // namespace cryptonote
diff --git a/src/cryptonote_core/blockchain_and_pool.h b/src/cryptonote_core/blockchain_and_pool.h
new file mode 100644
index 000000000..c0f607f64
--- /dev/null
+++ b/src/cryptonote_core/blockchain_and_pool.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2023, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list
+// of conditions and the following disclaimer in the documentation and/or
+// other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors
+// may be
+// used to endorse or promote products derived from this software without
+// specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include <memory>
+
+#include "blockchain.h"
+#include "tx_pool.h"
+
+namespace cryptonote
+{
+/**
+ * @brief Container for safely constructing Blockchain and tx_memory_pool classes
+ *
+ * The reason for this class existing is that the constructors for both Blockchain and
+ * tx_memory_pool take a reference for tx_memory_pool and Blockchain, respectively. Because of this
+ * circular reference, it is annoying/unsafe to construct these normally. This class guarantees that
+ * we don't make any silly mistakes with pointers / dangling references.
+ */
+struct BlockchainAndPool
+{
+ Blockchain blockchain;
+ tx_memory_pool tx_pool;
+
+ BlockchainAndPool(): blockchain(tx_pool), tx_pool(blockchain) {}
+};
+}
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 7b0c9e495..a5a59c892 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -221,8 +221,9 @@ namespace cryptonote
//-----------------------------------------------------------------------------------------------
core::core(i_cryptonote_protocol* pprotocol):
- m_mempool(m_blockchain_storage),
- m_blockchain_storage(m_mempool),
+ m_bap(),
+ m_mempool(m_bap.tx_pool),
+ m_blockchain_storage(m_bap.blockchain),
m_miner(this, [this](const cryptonote::block &b, uint64_t height, const crypto::hash *seed_hash, unsigned int threads, crypto::hash &hash) {
return cryptonote::get_block_longhash(&m_blockchain_storage, b, hash, height, seed_hash, threads);
}),
@@ -1558,7 +1559,8 @@ namespace cryptonote
return false;
}
m_blockchain_storage.add_new_block(b, bvc);
- cleanup_handle_incoming_blocks(true);
+ const bool force_sync = m_nettype != FAKECHAIN;
+ cleanup_handle_incoming_blocks(force_sync);
//anyway - update miner template
update_miner_block_template();
m_miner.resume();
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index e0655dfa2..8108dfae0 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -42,8 +42,7 @@
#include "cryptonote_protocol/enums.h"
#include "common/download.h"
#include "common/command_line.h"
-#include "tx_pool.h"
-#include "blockchain.h"
+#include "blockchain_and_pool.h"
#include "cryptonote_basic/miner.h"
#include "cryptonote_basic/connection_context.h"
#include "warnings.h"
@@ -1098,8 +1097,9 @@ namespace cryptonote
uint64_t m_test_drop_download_height = 0; //!< height under which to drop incoming blocks, if doing so
- tx_memory_pool m_mempool; //!< transaction pool instance
- Blockchain m_blockchain_storage; //!< Blockchain instance
+ BlockchainAndPool m_bap; //! Contains owned instances of Blockchain and tx_memory_pool
+ tx_memory_pool& m_mempool; //!< ref to transaction pool instance in m_bap
+ Blockchain& m_blockchain_storage; //!< ref to Blockchain instance in m_bap
i_cryptonote_protocol* m_pprotocol; //!< cryptonote protocol instance
diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h
index 6fe2eea59..47268efb6 100644
--- a/src/cryptonote_core/tx_pool.h
+++ b/src/cryptonote_core/tx_pool.h
@@ -98,14 +98,6 @@ namespace cryptonote
{
public:
/**
- * @brief Constructor
- *
- * @param bchs a Blockchain class instance, for getting chain info
- */
- tx_memory_pool(Blockchain& bchs);
-
-
- /**
* @copydoc add_tx(transaction&, tx_verification_context&, bool, bool, uint8_t)
*
* @param id the transaction's hash
@@ -489,6 +481,13 @@ namespace cryptonote
private:
/**
+ * @brief Constructor
+ *
+ * @param bchs a Blockchain class instance, for getting chain info
+ */
+ tx_memory_pool(Blockchain& bchs);
+
+ /**
* @brief insert key images into m_spent_key_images
*
* @return true on success, false on error
@@ -676,6 +675,8 @@ private:
//! Next timestamp that a DB check for relayable txes is allowed
std::atomic<time_t> m_next_check;
+
+ friend class BlockchainAndPool;
};
}
diff --git a/src/net/tor_address.cpp b/src/net/tor_address.cpp
index 53b73a839..ad8b399c8 100644
--- a/src/net/tor_address.cpp
+++ b/src/net/tor_address.cpp
@@ -48,7 +48,6 @@ namespace net
constexpr const char tld[] = u8".onion";
constexpr const char unknown_host[] = "<unknown tor host>";
- constexpr const unsigned v2_length = 16;
constexpr const unsigned v3_length = 56;
constexpr const char base32_alphabet[] =
@@ -62,7 +61,7 @@ namespace net
host.remove_suffix(sizeof(tld) - 1);
//! \TODO v3 has checksum, base32 decoding is required to verify it
- if (host.size() != v2_length && host.size() != v3_length)
+ if (host.size() != v3_length)
return {net::error::invalid_tor_address};
if (host.find_first_not_of(base32_alphabet) != boost::string_ref::npos)
return {net::error::invalid_tor_address};
@@ -118,7 +117,6 @@ namespace net
if (!port.empty() && !epee::string_tools::get_xtype_from_string(porti, std::string{port}))
return {net::error::invalid_port};
- static_assert(v2_length <= v3_length, "bad internal host size");
static_assert(v3_length + sizeof(tld) == sizeof(tor_address::host_), "bad internal host size");
return tor_address{host, porti};
}
@@ -180,7 +178,6 @@ namespace net
bool tor_address::is_same_host(const tor_address& rhs) const noexcept
{
- //! \TODO v2 and v3 should be comparable - requires base32
return std::strcmp(host_str(), rhs.host_str()) == 0;
}
diff --git a/src/net/tor_address.h b/src/net/tor_address.h
index 3dd320b5d..d04bf5145 100644
--- a/src/net/tor_address.h
+++ b/src/net/tor_address.h
@@ -71,7 +71,7 @@ namespace net
static tor_address unknown() noexcept { return tor_address{}; }
/*!
- Parse `address` in onion v2 or v3 format with (i.e. x.onion:80)
+ Parse `address` in onion v3 format with (i.e. x.onion:80)
with `default_port` being used iff port is not specified in
`address`.
*/
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index 0adf0b65e..03e9ec494 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -2216,7 +2216,8 @@ namespace cryptonote
// Fixing of high orphan issue for most pools
// Thanks Boolberry!
block b;
- if(!parse_and_validate_block_from_blob(blockblob, b))
+ crypto::hash blk_id;
+ if(!parse_and_validate_block_from_blob(blockblob, b, blk_id))
{
error_resp.code = CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB;
error_resp.message = "Wrong block blob";
@@ -2239,6 +2240,7 @@ namespace cryptonote
error_resp.message = "Block not accepted";
return false;
}
+ res.block_id = epee::string_tools::pod_to_hex(blk_id);
res.status = CORE_RPC_STATUS_OK;
return true;
}
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index 7f1581d0c..819d77c1f 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 12
+#define CORE_RPC_VERSION_MINOR 13
#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)
@@ -1115,8 +1115,11 @@ namespace cryptonote
struct response_t: public rpc_response_base
{
+ std::string block_id;
+
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_PARENT(rpc_response_base)
+ KV_SERIALIZE(block_id)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index e41a66d24..6e5d0b1ec 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -3186,6 +3186,8 @@ simple_wallet::simple_wallet()
" decrypt: same as action, but keeps the spend key encrypted in memory when not needed\n "
"unit <monero|millinero|micronero|nanonero|piconero>\n "
" Set the default monero (sub-)unit.\n "
+ "max-reorg-depth <unsigned int>\n "
+ " Set the maximum amount of blocks to accept in a reorg.\n "
"min-outputs-count [n]\n "
" Try to keep at least that many outputs of value at least min-outputs-value.\n "
"min-outputs-value [n]\n "
@@ -3224,6 +3226,8 @@ simple_wallet::simple_wallet()
" Device name for hardware wallet.\n "
"export-format <\"binary\"|\"ascii\">\n "
" Save all exported files as binary (cannot be copied and pasted) or ascii (can be).\n "
+ "load-deprecated-formats <1|0>\n "
+ " Whether to enable importing data in deprecated formats.\n "
"show-wallet-name-when-locked <1|0>\n "
" Set this if you would like to display the wallet name when locked.\n "
"enable-multisig-experimental <1|0>\n "
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index f5235dbaf..ac657cf49 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -986,6 +986,21 @@ bool get_pruned_tx(const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::entry &entry,
return false;
}
+// Given M (threshold) and N (total), calculate the number of private multisig keys each
+// signer should have. This value is equal to (N - 1) choose (N - M)
+// Prereq: M >= 1 && N >= M && N <= 16
+uint64_t num_priv_multisig_keys_post_setup(uint64_t threshold, uint64_t total)
+{
+ THROW_WALLET_EXCEPTION_IF(threshold < 1 || total < threshold || threshold > 16,
+ tools::error::wallet_internal_error, "Invalid arguments to num_priv_multisig_keys_post_setup");
+
+ uint64_t n_multisig_keys = 1;
+ for (uint64_t i = 2; i <= total - 1; ++i) n_multisig_keys *= i; // multiply by (N - 1)!
+ for (uint64_t i = 2; i <= total - threshold; ++i) n_multisig_keys /= i; // divide by (N - M)!
+ for (uint64_t i = 2; i <= threshold - 1; ++i) n_multisig_keys /= i; // divide by ((N - 1) - (N - M))!
+ return n_multisig_keys;
+}
+
//-----------------------------------------------------------------
} //namespace
@@ -1394,7 +1409,7 @@ bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeab
return true;
}
//----------------------------------------------------------------------------------------------------
-bool wallet2::get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase, bool raw) const
+bool wallet2::get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase) const
{
bool ready;
uint32_t threshold, total;
@@ -1408,15 +1423,14 @@ bool wallet2::get_multisig_seed(epee::wipeable_string& seed, const epee::wipeabl
std::cout << "This multisig wallet is not yet finalized" << std::endl;
return false;
}
- if (!raw && seed_language.empty())
- {
- std::cout << "seed_language not set" << std::endl;
- return false;
- }
+
+ const uint64_t num_expected_ms_keys = num_priv_multisig_keys_post_setup(threshold, total);
crypto::secret_key skey;
crypto::public_key pkey;
const account_keys &keys = get_account().get_keys();
+ THROW_WALLET_EXCEPTION_IF(num_expected_ms_keys != keys.m_multisig_keys.size(),
+ error::wallet_internal_error, "Unexpected number of private multisig keys")
epee::wipeable_string data;
data.append((const char*)&threshold, sizeof(uint32_t));
data.append((const char*)&total, sizeof(uint32_t));
@@ -1441,18 +1455,7 @@ bool wallet2::get_multisig_seed(epee::wipeable_string& seed, const epee::wipeabl
data = encrypt(data, key, true);
}
- if (raw)
- {
- seed = epee::to_hex::wipeable_string({(const unsigned char*)data.data(), data.size()});
- }
- else
- {
- if (!crypto::ElectrumWords::bytes_to_words(data.data(), data.size(), seed, seed_language))
- {
- std::cout << "Failed to encode seed";
- return false;
- }
- }
+ seed = epee::to_hex::wipeable_string({(const unsigned char*)data.data(), data.size()});
return true;
}
@@ -5212,9 +5215,11 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
offset += sizeof(uint32_t);
uint32_t total = *(uint32_t*)(multisig_data.data() + offset);
offset += sizeof(uint32_t);
- THROW_WALLET_EXCEPTION_IF(threshold < 2, error::invalid_multisig_seed);
- THROW_WALLET_EXCEPTION_IF(total != threshold && total != threshold + 1, error::invalid_multisig_seed);
- const size_t n_multisig_keys = total == threshold ? 1 : threshold;
+
+ THROW_WALLET_EXCEPTION_IF(threshold < 1, error::invalid_multisig_seed);
+ THROW_WALLET_EXCEPTION_IF(total < threshold, error::invalid_multisig_seed);
+ THROW_WALLET_EXCEPTION_IF(threshold > 16, error::invalid_multisig_seed); // doing N choose (N - M + 1) might overflow
+ const uint64_t n_multisig_keys = num_priv_multisig_keys_post_setup(threshold, total);
THROW_WALLET_EXCEPTION_IF(multisig_data.size() != 8 + 32 * (4 + n_multisig_keys + total), error::invalid_multisig_seed);
std::vector<crypto::secret_key> multisig_keys;
@@ -10217,7 +10222,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
else
{
LOG_PRINT_L2("We made a tx, adjusting fee and saving it, we need " << print_money(needed_fee) << " and we have " << print_money(test_ptx.fee));
- while (needed_fee > test_ptx.fee) {
+ do {
if (use_rct)
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, unlock_time, needed_fee, extra,
test_tx, test_ptx, rct_config, use_view_tags);
@@ -10228,7 +10233,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_quantization_mask);
LOG_PRINT_L2("Made an attempt at a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change");
- }
+ } while (needed_fee > test_ptx.fee);
LOG_PRINT_L2("Made a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change");
@@ -10624,7 +10629,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
THROW_WALLET_EXCEPTION_IF(needed_fee > available_for_fee, error::wallet_internal_error, "Transaction cannot pay for itself");
do {
- LOG_PRINT_L2("We made a tx, adjusting fee and saving it");
+ LOG_PRINT_L2("We made a tx, adjusting fee and saving it, we need " << print_money(needed_fee) << " and we have " << print_money(test_ptx.fee));
// distribute total transferred amount between outputs
uint64_t amount_transferred = available_for_fee - needed_fee;
uint64_t dt_amount = amount_transferred / outputs;
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 9c310f692..684bbd1ec 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -1044,7 +1044,7 @@ private:
bool multisig(bool *ready = NULL, uint32_t *threshold = NULL, uint32_t *total = NULL) const;
bool has_multisig_partial_key_images() const;
bool has_unknown_key_images() const;
- bool get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase = std::string(), bool raw = true) const;
+ bool get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase = std::string()) const;
bool key_on_device() const { return get_device_type() != hw::device::device_type::SOFTWARE; }
hw::device::device_type get_device_type() const { return m_key_device_type; }
bool reconnect_device();
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 7c46d9887..0cc42ae3f 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -3811,7 +3811,7 @@ namespace tools
std::string old_language;
// check the given seed
- {
+ if (!req.enable_multisig_experimental) {
if (!crypto::ElectrumWords::words_to_bytes(req.seed, recovery_key, old_language))
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
@@ -3834,6 +3834,13 @@ namespace tools
// process seed_offset if given
{
+ if (req.enable_multisig_experimental && !req.seed_offset.empty())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "Multisig seeds are not compatible with seed offsets";
+ return false;
+ }
+
if (!req.seed_offset.empty())
{
recovery_key = cryptonote::decrypt_key(recovery_key, req.seed_offset);
@@ -3897,7 +3904,27 @@ namespace tools
crypto::secret_key recovery_val;
try
{
- recovery_val = wal->generate(wallet_file, std::move(rc.second).password(), recovery_key, true, false, false);
+ if (req.enable_multisig_experimental)
+ {
+ // Parse multisig seed into raw multisig data
+ epee::wipeable_string multisig_data;
+ multisig_data.resize(req.seed.size() / 2);
+ if (!epee::from_hex::to_buffer(epee::to_mut_byte_span(multisig_data), req.seed))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "Multisig seed not represented as hexadecimal string";
+ return false;
+ }
+
+ // Generate multisig wallet
+ wal->generate(wallet_file, std::move(rc.second).password(), multisig_data, false);
+ wal->enable_multisig(true);
+ }
+ else
+ {
+ // Generate normal wallet
+ recovery_val = wal->generate(wallet_file, std::move(rc.second).password(), recovery_key, true, false, false);
+ }
MINFO("Wallet has been restored.\n");
}
catch (const std::exception &e)
@@ -3908,7 +3935,7 @@ namespace tools
// // Convert the secret key back to seed
epee::wipeable_string electrum_words;
- if (!crypto::ElectrumWords::bytes_to_words(recovery_val, electrum_words, mnemonic_language))
+ if (!req.enable_multisig_experimental && !crypto::ElectrumWords::bytes_to_words(recovery_val, electrum_words, mnemonic_language))
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "Failed to encode seed";
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index 002db4289..c00271030 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -2262,6 +2262,7 @@ namespace wallet_rpc
std::string password;
std::string language;
bool autosave_current;
+ bool enable_multisig_experimental;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_OPT(restore_height, (uint64_t)0)
@@ -2271,6 +2272,7 @@ namespace wallet_rpc
KV_SERIALIZE(password)
KV_SERIALIZE(language)
KV_SERIALIZE_OPT(autosave_current, true)
+ KV_SERIALIZE_OPT(enable_multisig_experimental, false)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
diff --git a/tests/block_weight/block_weight.cpp b/tests/block_weight/block_weight.cpp
index 7cd0d572b..4b00fc63f 100644
--- a/tests/block_weight/block_weight.cpp
+++ b/tests/block_weight/block_weight.cpp
@@ -30,8 +30,6 @@
#include <stdio.h>
#include <math.h>
-#include "cryptonote_core/blockchain.h"
-#include "cryptonote_core/tx_pool.h"
#include "cryptonote_core/cryptonote_core.h"
#include "blockchain_db/testdb.h"
@@ -110,9 +108,6 @@ private:
}
#define PREFIX_WINDOW(hf_version,window) \
- std::unique_ptr<cryptonote::Blockchain> bc; \
- cryptonote::tx_memory_pool txpool(*bc); \
- bc.reset(new cryptonote::Blockchain(txpool)); \
struct get_test_options { \
const std::pair<uint8_t, uint64_t> hard_forks[3]; \
const cryptonote::test_options test_options = { \
@@ -121,7 +116,9 @@ private:
}; \
get_test_options(): hard_forks{std::make_pair(1, (uint64_t)0), std::make_pair((uint8_t)hf_version, (uint64_t)LONG_TERM_BLOCK_WEIGHT_WINDOW), std::make_pair((uint8_t)0, (uint64_t)0)} {} \
} opts; \
- cryptonote::Blockchain *blockchain = bc.get(); \
+ cryptonote::BlockchainAndPool bap; \
+ cryptonote::Blockchain *blockchain = &bap.blockchain; \
+ cryptonote::Blockchain *bc = blockchain; \
bool r = blockchain->init(new TestDB(), cryptonote::FAKECHAIN, true, &opts.test_options, 0, NULL); \
if (!r) \
{ \
diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp
index 9f6fe5387..b66aa7924 100644
--- a/tests/core_tests/chaingen.cpp
+++ b/tests/core_tests/chaingen.cpp
@@ -143,9 +143,8 @@ namespace
}
-static std::unique_ptr<cryptonote::Blockchain> init_blockchain(const std::vector<test_event_entry> & events, cryptonote::network_type nettype)
+static std::unique_ptr<cryptonote::BlockchainAndPool> init_blockchain(const std::vector<test_event_entry> & events, cryptonote::network_type nettype)
{
- std::unique_ptr<cryptonote::Blockchain> bc;
v_hardforks_t hardforks;
cryptonote::test_options test_options_tmp{nullptr, 0};
const cryptonote::test_options * test_options = &test_options_tmp;
@@ -159,10 +158,8 @@ static std::unique_ptr<cryptonote::Blockchain> init_blockchain(const std::vector
test_options_tmp.hard_forks = hardforks.data();
test_options = &test_options_tmp;
- cryptonote::tx_memory_pool txpool(*bc);
- bc.reset(new cryptonote::Blockchain(txpool));
+ std::unique_ptr<cryptonote::BlockchainAndPool> bap(new BlockchainAndPool());
- cryptonote::Blockchain *blockchain = bc.get();
auto bdb = new TestDB();
BOOST_FOREACH(const test_event_entry &ev, events)
@@ -177,9 +174,9 @@ static std::unique_ptr<cryptonote::Blockchain> init_blockchain(const std::vector
bdb->add_block(*blk, 1, 1, 1, 0, 0, blk_hash);
}
- bool r = blockchain->init(bdb, nettype, true, test_options, 2, nullptr);
+ bool r = bap->blockchain.init(bdb, nettype, true, test_options, 2, nullptr);
CHECK_AND_ASSERT_THROW_MES(r, "could not init blockchain from events");
- return bc;
+ return bap;
}
void test_generator::get_block_chain(std::vector<block_info>& blockchain, const crypto::hash& head, size_t n) const
@@ -393,7 +390,7 @@ bool test_generator::construct_block_manually_tx(cryptonote::block& blk, const c
void test_generator::fill_nonce(cryptonote::block& blk, const difficulty_type& diffic, uint64_t height)
{
const cryptonote::Blockchain *blockchain = nullptr;
- std::unique_ptr<cryptonote::Blockchain> bc;
+ std::unique_ptr<cryptonote::BlockchainAndPool> bap;
if (blk.major_version >= RX_BLOCK_VERSION && diffic > 1)
{
@@ -403,8 +400,8 @@ void test_generator::fill_nonce(cryptonote::block& blk, const difficulty_type& d
}
else
{
- bc = init_blockchain(*m_events, m_nettype);
- blockchain = bc.get();
+ bap = init_blockchain(*m_events, m_nettype);
+ blockchain = &bap->blockchain;
}
}
diff --git a/tests/functional_tests/mining.py b/tests/functional_tests/mining.py
index e98037811..242c58dbe 100755
--- a/tests/functional_tests/mining.py
+++ b/tests/functional_tests/mining.py
@@ -36,6 +36,7 @@ import math
import monotonic
import util_resources
import multiprocessing
+import string
"""Test daemon mining RPC calls
@@ -52,6 +53,11 @@ Control the behavior with these environment variables:
from framework.daemon import Daemon
from framework.wallet import Wallet
+def assert_non_null_hash(s):
+ assert len(s) == 64 # correct length
+ assert all((c in string.hexdigits for c in s)) # is parseable as hex
+ assert s != ('0' * 64) # isn't null hash
+
class MiningTest():
def run_test(self):
self.reset()
@@ -250,6 +256,8 @@ class MiningTest():
block_hash = hashes[i]
assert len(block_hash) == 64
res = daemon.submitblock(blocks[i])
+ submitted_block_id = res.block_id
+ assert_non_null_hash(submitted_block_id)
res = daemon.get_height()
assert res.height == height + i + 1
assert res.hash == block_hash
@@ -346,6 +354,8 @@ class MiningTest():
t0 = time.time()
for h in range(len(block_hashes)):
res = daemon.submitblock(blocks[h])
+ submitted_block_id = res.block_id
+ assert_non_null_hash(submitted_block_id)
t0 = time.time() - t0
res = daemon.get_info()
assert height == res.height
diff --git a/tests/functional_tests/multisig.py b/tests/functional_tests/multisig.py
index 0ca438857..7d3513070 100755
--- a/tests/functional_tests/multisig.py
+++ b/tests/functional_tests/multisig.py
@@ -29,6 +29,7 @@
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from __future__ import print_function
+import random
"""Test multisig transfers
"""
@@ -36,42 +37,74 @@ from __future__ import print_function
from framework.daemon import Daemon
from framework.wallet import Wallet
+MULTISIG_PUB_ADDRS = [
+ '45J58b7PmKJFSiNPFFrTdtfMcFGnruP7V4CMuRpX7NsH4j3jGHKAjo3YJP2RePX6HMaSkbvTbrWUFhDNcNcHgtNmQ3gr7sG', # 2/2
+ '44G2TQNfsiURKkvxp7gbgaJY8WynZvANnhmyMAwv6WeEbAvyAWMfKXRhh3uBXT2UAKhAsUJ7Fg5zjjF2U1iGciFk5duN94i', # 2/3
+ '41mro238grj56GnrWkakAKTkBy2yDcXYsUZ2iXCM9pe5Ueajd2RRc6Fhh3uBXT2UAKhAsUJ7Fg5zjjF2U1iGciFk5ief4ZP', # 3/3
+ '44vZSprQKJQRFe6t1VHgU4ESvq2dv7TjBLVGE7QscKxMdFSiyyPCEV64NnKUQssFPyWxc2meyt7j63F2S2qtCTRL6dakeff', # 3/4
+ '47puypSwsV1gvUDratmX4y58fSwikXVehEiBhVLxJA1gRCxHyrRgTDr4NnKUQssFPyWxc2meyt7j63F2S2qtCTRL6aRPj5U', # 2/4
+ '4A8RnBQixry4VXkqeWhmg8L7vWJVDJj4FN9PV4E7Mgad5ZZ6LKQdn8dYJP2RePX6HMaSkbvTbrWUFhDNcNcHgtNmQ4S8RSB' # 1/2
+]
+
class MultisigTest():
def run_test(self):
self.reset()
- self.mine('45J58b7PmKJFSiNPFFrTdtfMcFGnruP7V4CMuRpX7NsH4j3jGHKAjo3YJP2RePX6HMaSkbvTbrWUFhDNcNcHgtNmQ3gr7sG', 5)
- self.mine('44G2TQNfsiURKkvxp7gbgaJY8WynZvANnhmyMAwv6WeEbAvyAWMfKXRhh3uBXT2UAKhAsUJ7Fg5zjjF2U1iGciFk5duN94i', 5)
- self.mine('41mro238grj56GnrWkakAKTkBy2yDcXYsUZ2iXCM9pe5Ueajd2RRc6Fhh3uBXT2UAKhAsUJ7Fg5zjjF2U1iGciFk5ief4ZP', 5)
- self.mine('44vZSprQKJQRFe6t1VHgU4ESvq2dv7TjBLVGE7QscKxMdFSiyyPCEV64NnKUQssFPyWxc2meyt7j63F2S2qtCTRL6dakeff', 5)
- self.mine('47puypSwsV1gvUDratmX4y58fSwikXVehEiBhVLxJA1gRCxHyrRgTDr4NnKUQssFPyWxc2meyt7j63F2S2qtCTRL6aRPj5U', 5)
+ for pub_addr in MULTISIG_PUB_ADDRS:
+ self.mine(pub_addr, 4)
self.mine('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 80)
self.test_states()
+ self.fund_addrs_with_normal_wallet(MULTISIG_PUB_ADDRS)
+
self.create_multisig_wallets(2, 2, '45J58b7PmKJFSiNPFFrTdtfMcFGnruP7V4CMuRpX7NsH4j3jGHKAjo3YJP2RePX6HMaSkbvTbrWUFhDNcNcHgtNmQ3gr7sG')
self.import_multisig_info([1, 0], 5)
txid = self.transfer([1, 0])
self.import_multisig_info([0, 1], 6)
self.check_transaction(txid)
+ self.remake_some_multisig_wallets_by_multsig_seed(2)
+ self.import_multisig_info([0, 1], 6) # six outputs, same as before
+ txid = self.transfer([0, 1])
+ self.import_multisig_info([0, 1], 7) # seven outputs b/c we're dest plus change
+ self.check_transaction(txid)
+
self.create_multisig_wallets(2, 3, '44G2TQNfsiURKkvxp7gbgaJY8WynZvANnhmyMAwv6WeEbAvyAWMfKXRhh3uBXT2UAKhAsUJ7Fg5zjjF2U1iGciFk5duN94i')
self.import_multisig_info([0, 2], 5)
txid = self.transfer([0, 2])
self.import_multisig_info([0, 1, 2], 6)
self.check_transaction(txid)
+ self.remake_some_multisig_wallets_by_multsig_seed(2)
+ self.import_multisig_info([0, 2], 6) # six outputs, same as before
+ txid = self.transfer([0, 2])
+ self.import_multisig_info([0, 1, 2], 7) # seven outputs b/c we're dest plus change
+ self.check_transaction(txid)
+
self.create_multisig_wallets(3, 3, '41mro238grj56GnrWkakAKTkBy2yDcXYsUZ2iXCM9pe5Ueajd2RRc6Fhh3uBXT2UAKhAsUJ7Fg5zjjF2U1iGciFk5ief4ZP')
self.import_multisig_info([2, 0, 1], 5)
txid = self.transfer([2, 1, 0])
self.import_multisig_info([0, 2, 1], 6)
self.check_transaction(txid)
+ self.remake_some_multisig_wallets_by_multsig_seed(3)
+ self.import_multisig_info([2, 0, 1], 6) # six outputs, same as before
+ txid = self.transfer([2, 1, 0])
+ self.import_multisig_info([0, 2, 1], 7) # seven outputs b/c we're dest plus change
+ self.check_transaction(txid)
+
self.create_multisig_wallets(3, 4, '44vZSprQKJQRFe6t1VHgU4ESvq2dv7TjBLVGE7QscKxMdFSiyyPCEV64NnKUQssFPyWxc2meyt7j63F2S2qtCTRL6dakeff')
self.import_multisig_info([0, 2, 3], 5)
txid = self.transfer([0, 2, 3])
self.import_multisig_info([0, 1, 2, 3], 6)
self.check_transaction(txid)
+ self.remake_some_multisig_wallets_by_multsig_seed(3)
+ self.import_multisig_info([0, 2, 3], 6) # six outputs, same as before
+ txid = self.transfer([0, 2, 3])
+ self.import_multisig_info([0, 1, 2, 3], 7) # seven outputs b/c we're dest plus change
+ self.check_transaction(txid)
+
self.create_multisig_wallets(2, 4, '47puypSwsV1gvUDratmX4y58fSwikXVehEiBhVLxJA1gRCxHyrRgTDr4NnKUQssFPyWxc2meyt7j63F2S2qtCTRL6aRPj5U')
self.import_multisig_info([1, 2], 5)
txid = self.transfer([1, 2])
@@ -81,6 +114,24 @@ class MultisigTest():
self.import_multisig_info([0, 1, 2, 3], 7)
self.check_transaction(txid)
+ self.remake_some_multisig_wallets_by_multsig_seed(2)
+ self.import_multisig_info([0, 1, 2, 3], 6) # six outputs, same as before
+ txid = self.transfer([2, 3])
+ self.import_multisig_info([0, 1, 2, 3], 7) # seven outputs b/c we're dest plus change
+ self.check_transaction(txid)
+
+ self.create_multisig_wallets(1, 2, '4A8RnBQixry4VXkqeWhmg8L7vWJVDJj4FN9PV4E7Mgad5ZZ6LKQdn8dYJP2RePX6HMaSkbvTbrWUFhDNcNcHgtNmQ4S8RSB')
+ self.import_multisig_info([0, 1], 5)
+ txid = self.transfer([0])
+ self.import_multisig_info([0, 1], 6)
+ self.check_transaction(txid)
+
+ self.remake_some_multisig_wallets_by_multsig_seed(1)
+ self.import_multisig_info([0, 1], 6) # six outputs, same as before
+ txid = self.transfer([1])
+ self.import_multisig_info([0, 1], 7) # seven outputs b/c we're dest plus change
+ self.check_transaction(txid)
+
def reset(self):
print('Resetting blockchain')
daemon = Daemon()
@@ -93,6 +144,11 @@ class MultisigTest():
daemon = Daemon()
daemon.generateblocks(address, blocks)
+ # This method sets up N_total wallets with a threshold of M_threshold doing the following steps:
+ # * restore_deterministic_wallet(w/ hardcoded seeds)
+ # * prepare_multisig(enable_multisig_experimental = True)
+ # * make_multisig()
+ # * exchange_multisig_keys()
def create_multisig_wallets(self, M_threshold, N_total, expected_address):
print('Creating ' + str(M_threshold) + '/' + str(N_total) + ' multisig wallet')
seeds = [
@@ -103,6 +159,8 @@ class MultisigTest():
]
assert M_threshold <= N_total
assert N_total <= len(seeds)
+
+ # restore_deterministic_wallet() & prepare_multisig()
self.wallet = [None] * N_total
info = []
for i in range(N_total):
@@ -114,10 +172,12 @@ class MultisigTest():
assert len(res.multisig_info) > 0
info.append(res.multisig_info)
+ # Assert that all wallets are multisig
for i in range(N_total):
res = self.wallet[i].is_multisig()
assert res.multisig == False
+ # make_multisig() with each other's info
addresses = []
next_stage = []
for i in range(N_total):
@@ -125,6 +185,7 @@ class MultisigTest():
addresses.append(res.address)
next_stage.append(res.multisig_info)
+ # Assert multisig paramaters M/N for each wallet
for i in range(N_total):
res = self.wallet[i].is_multisig()
assert res.multisig == True
@@ -132,13 +193,15 @@ class MultisigTest():
assert res.threshold == M_threshold
assert res.total == N_total
- while True:
+ # exchange_multisig_keys()
+ num_exchange_multisig_keys_stages = 0
+ while True: # while not all wallets are ready
n_ready = 0
for i in range(N_total):
res = self.wallet[i].is_multisig()
if res.ready == True:
n_ready += 1
- assert n_ready == 0 or n_ready == N_total
+ assert n_ready == 0 or n_ready == N_total # No partial readiness
if n_ready == N_total:
break
info = next_stage
@@ -148,10 +211,17 @@ class MultisigTest():
res = self.wallet[i].exchange_multisig_keys(info)
next_stage.append(res.multisig_info)
addresses.append(res.address)
+ num_exchange_multisig_keys_stages += 1
+
+ # We should only need N - M + 1 key exchange rounds
+ assert num_exchange_multisig_keys_stages == N_total - M_threshold + 1
+
+ # Assert that the all wallets have expected public address
for i in range(N_total):
- assert addresses[i] == expected_address
+ assert addresses[i] == expected_address, addresses[i]
self.wallet_address = expected_address
+ # Assert multisig paramaters M/N and "ready" for each wallet
for i in range(N_total):
res = self.wallet[i].is_multisig()
assert res.multisig == True
@@ -159,6 +229,73 @@ class MultisigTest():
assert res.threshold == M_threshold
assert res.total == N_total
+ # We want to test if multisig wallets can receive normal transfers as well and mining transfers
+ def fund_addrs_with_normal_wallet(self, addrs):
+ print("Funding multisig wallets with normal wallet-to-wallet transfers")
+
+ # Generate normal deterministic wallet
+ normal_seed = 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted'
+ assert not hasattr(self, 'wallet') or not self.wallet
+ self.wallet = [Wallet(idx = 0)]
+ res = self.wallet[0].restore_deterministic_wallet(seed = normal_seed)
+ assert res.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
+
+ self.wallet[0].refresh()
+
+ # Check that we own enough spendable enotes
+ res = self.wallet[0].incoming_transfers(transfer_type = 'available')
+ assert 'transfers' in res
+ num_outs_spendable = 0
+ min_out_amount = None
+ for t in res.transfers:
+ if not t.spent:
+ num_outs_spendable += 1
+ min_out_amount = min(min_out_amount, t.amount) if min_out_amount is not None else t.amount
+ assert num_outs_spendable >= 2 * len(addrs)
+
+ # Transfer to addrs and mine to confirm tx
+ dsts = [{'address': addr, 'amount': int(min_out_amount * 0.95)} for addr in addrs]
+ res = self.wallet[0].transfer(dsts, get_tx_metadata = True)
+ tx_hex = res.tx_metadata
+ res = self.wallet[0].relay_tx(tx_hex)
+ self.mine('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 10)
+
+ def remake_some_multisig_wallets_by_multsig_seed(self, threshold):
+ N = len(self.wallet)
+ signers_to_remake = set()
+ num_signers_to_remake = random.randint(1, N) # Do at least one
+ while len(signers_to_remake) < num_signers_to_remake:
+ signers_to_remake.add(random.randint(0, N - 1))
+
+ for i in signers_to_remake:
+ print("Remaking {}/{} multsig wallet from multisig seed: #{}".format(threshold, N, i+1))
+
+ otherwise_unused_seed = \
+ 'factual wiggle awakened maul sash biscuit pause reinvest fonts sleepless knowledge tossed jewels request gusts dagger gumball onward dotted amended powder cynical strained topic request'
+
+ # Get information about wallet, will compare against later
+ old_viewkey = self.wallet[i].query_key('view_key').key
+ old_spendkey = self.wallet[i].query_key('spend_key').key
+ old_multisig_seed = self.wallet[i].query_key('mnemonic').key
+
+ # Close old wallet and restore w/ random seed so we know that restoring actually did something
+ self.wallet[i].close_wallet()
+ self.wallet[i].restore_deterministic_wallet(seed=otherwise_unused_seed)
+ mid_viewkey = self.wallet[i].query_key('view_key').key
+ assert mid_viewkey != old_viewkey
+
+ # Now restore w/ old multisig seed and check against original
+ self.wallet[i].close_wallet()
+ self.wallet[i].restore_deterministic_wallet(seed=old_multisig_seed, enable_multisig_experimental=True)
+ new_viewkey = self.wallet[i].query_key('view_key').key
+ new_spendkey = self.wallet[i].query_key('spend_key').key
+ new_multisig_seed = self.wallet[i].query_key('mnemonic').key
+ assert new_viewkey == old_viewkey
+ assert new_spendkey == old_spendkey
+ assert new_multisig_seed == old_multisig_seed
+
+ self.wallet[i].refresh()
+
def test_states(self):
print('Testing multisig states')
seeds = [
@@ -251,7 +388,7 @@ class MultisigTest():
assert res.n_outputs == expected_outputs
def transfer(self, signers):
- assert len(signers) >= 2
+ assert len(signers) >= 1
daemon = Daemon()
diff --git a/tests/unit_tests/long_term_block_weight.cpp b/tests/unit_tests/long_term_block_weight.cpp
index b9ce2b247..e52bd1955 100644
--- a/tests/unit_tests/long_term_block_weight.cpp
+++ b/tests/unit_tests/long_term_block_weight.cpp
@@ -106,16 +106,9 @@ static uint32_t lcg()
}
-struct BlockchainAndPool
-{
- cryptonote::tx_memory_pool txpool;
- cryptonote::Blockchain bc;
- BlockchainAndPool(): txpool(bc), bc(txpool) {}
-};
-
#define PREFIX_WINDOW(hf_version,window) \
- BlockchainAndPool bap; \
- cryptonote::Blockchain *bc = &bap.bc; \
+ cryptonote::BlockchainAndPool bap; \
+ cryptonote::Blockchain *bc = &bap.blockchain; \
struct get_test_options { \
const std::pair<uint8_t, uint64_t> hard_forks[3]; \
const cryptonote::test_options test_options = { \
diff --git a/tests/unit_tests/net.cpp b/tests/unit_tests/net.cpp
index 03072b283..b9555b213 100644
--- a/tests/unit_tests/net.cpp
+++ b/tests/unit_tests/net.cpp
@@ -74,6 +74,8 @@ namespace
"xmrto2bturnore26.onion";
static constexpr const char v3_onion[] =
"vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd.onion";
+ static constexpr const char v3_onion_2[] =
+ "zpv4fa3szgel7vf6jdjeugizdclq2vzkelscs2bhbgnlldzzggcen3ad.onion";
}
TEST(tor_address, constants)
@@ -94,12 +96,10 @@ TEST(tor_address, invalid)
EXPECT_TRUE(net::tor_address::make(":").has_error());
EXPECT_TRUE(net::tor_address::make(".onion").has_error());
EXPECT_TRUE(net::tor_address::make(".onion:").has_error());
- EXPECT_TRUE(net::tor_address::make(v2_onion + 1).has_error());
EXPECT_TRUE(net::tor_address::make(v3_onion + 1).has_error());
- EXPECT_TRUE(net::tor_address::make(boost::string_ref{v2_onion, sizeof(v2_onion) - 2}).has_error());
EXPECT_TRUE(net::tor_address::make(boost::string_ref{v3_onion, sizeof(v3_onion) - 2}).has_error());
- EXPECT_TRUE(net::tor_address::make(std::string{v2_onion} + ":-").has_error());
- EXPECT_TRUE(net::tor_address::make(std::string{v2_onion} + ":900a").has_error());
+ EXPECT_TRUE(net::tor_address::make(std::string{v3_onion} + ":-").has_error());
+ EXPECT_TRUE(net::tor_address::make(std::string{v3_onion} + ":900a").has_error());
EXPECT_TRUE(net::tor_address::make(std::string{v3_onion} + ":65536").has_error());
EXPECT_TRUE(net::tor_address::make(std::string{v3_onion} + ":-1").has_error());
@@ -163,11 +163,11 @@ TEST(tor_address, valid)
EXPECT_FALSE(address2.less(*address1));
EXPECT_FALSE(address1->less(address2));
- address2 = MONERO_UNWRAP(net::tor_address::make(std::string{v2_onion} + ":6545"));
+ address2 = MONERO_UNWRAP(net::tor_address::make(std::string{v3_onion_2} + ":6545"));
EXPECT_EQ(6545, address2.port());
- EXPECT_STREQ(v2_onion, address2.host_str());
- EXPECT_EQ(std::string{v2_onion} + ":6545", address2.str().c_str());
+ EXPECT_STREQ(v3_onion_2, address2.host_str());
+ EXPECT_EQ(std::string{v3_onion_2} + ":6545", address2.str().c_str());
EXPECT_TRUE(address2.is_blockable());
EXPECT_FALSE(address2.equal(*address1));
EXPECT_FALSE(address1->equal(address2));
@@ -244,57 +244,6 @@ namespace
};
}
-TEST(tor_address, epee_serializev_v2)
-{
- epee::byte_slice buffer{};
- {
- test_command_tor command{MONERO_UNWRAP(net::tor_address::make(v2_onion, 10))};
- EXPECT_FALSE(command.tor.is_unknown());
- EXPECT_NE(net::tor_address{}, command.tor);
- EXPECT_STREQ(v2_onion, command.tor.host_str());
- EXPECT_EQ(10u, command.tor.port());
-
- epee::serialization::portable_storage stg{};
- EXPECT_TRUE(command.store(stg));
- EXPECT_TRUE(stg.store_to_binary(buffer));
- }
-
- test_command_tor command{};
- {
- EXPECT_TRUE(command.tor.is_unknown());
- EXPECT_EQ(net::tor_address{}, command.tor);
- EXPECT_STREQ(net::tor_address::unknown_str(), command.tor.host_str());
- EXPECT_EQ(0u, command.tor.port());
-
- epee::serialization::portable_storage stg{};
- EXPECT_TRUE(stg.load_from_binary(epee::to_span(buffer)));
- EXPECT_TRUE(command.load(stg));
- }
- EXPECT_FALSE(command.tor.is_unknown());
- EXPECT_NE(net::tor_address{}, command.tor);
- EXPECT_STREQ(v2_onion, command.tor.host_str());
- EXPECT_EQ(10u, command.tor.port());
-
- // make sure that exceeding max buffer doesn't destroy tor_address::_load
- {
- epee::serialization::portable_storage stg{};
- stg.load_from_binary(epee::to_span(buffer));
-
- std::string host{};
- ASSERT_TRUE(stg.get_value("host", host, stg.open_section("tor", nullptr, false)));
- EXPECT_EQ(std::strlen(v2_onion), host.size());
-
- host.push_back('k');
- EXPECT_TRUE(stg.set_value("host", std::move(host), stg.open_section("tor", nullptr, false)));
- EXPECT_TRUE(command.load(stg)); // poor error reporting from `KV_SERIALIZE`
- }
-
- EXPECT_TRUE(command.tor.is_unknown());
- EXPECT_EQ(net::tor_address{}, command.tor);
- EXPECT_STREQ(net::tor_address::unknown_str(), command.tor.host_str());
- EXPECT_EQ(0u, command.tor.port());
-}
-
TEST(tor_address, epee_serializev_v3)
{
epee::byte_slice buffer{};
@@ -397,41 +346,6 @@ TEST(tor_address, epee_serialize_unknown)
EXPECT_EQ(0u, command.tor.port());
}
-TEST(tor_address, boost_serialize_v2)
-{
- std::string buffer{};
- {
- const net::tor_address tor = MONERO_UNWRAP(net::tor_address::make(v2_onion, 10));
- EXPECT_FALSE(tor.is_unknown());
- EXPECT_NE(net::tor_address{}, tor);
- EXPECT_STREQ(v2_onion, tor.host_str());
- EXPECT_EQ(10u, tor.port());
-
- std::ostringstream stream{};
- {
- boost::archive::portable_binary_oarchive archive{stream};
- archive << tor;
- }
- buffer = stream.str();
- }
-
- net::tor_address tor{};
- {
- EXPECT_TRUE(tor.is_unknown());
- EXPECT_EQ(net::tor_address{}, tor);
- EXPECT_STREQ(net::tor_address::unknown_str(), tor.host_str());
- EXPECT_EQ(0u, tor.port());
-
- std::istringstream stream{buffer};
- boost::archive::portable_binary_iarchive archive{stream};
- archive >> tor;
- }
- EXPECT_FALSE(tor.is_unknown());
- EXPECT_NE(net::tor_address{}, tor);
- EXPECT_STREQ(v2_onion, tor.host_str());
- EXPECT_EQ(10u, tor.port());
-}
-
TEST(tor_address, boost_serialize_v3)
{
std::string buffer{};
@@ -511,6 +425,9 @@ TEST(get_network_address, onion)
address = net::get_network_address(".onion", 0);
EXPECT_EQ(net::error::invalid_tor_address, address);
+ address = net::get_network_address(v2_onion, 1000);
+ EXPECT_EQ(net::error::invalid_tor_address, address);
+
address = net::get_network_address(v3_onion, 1000);
ASSERT_TRUE(bool(address));
EXPECT_EQ(epee::net_utils::address_type::tor, address->get_type_id());
diff --git a/tests/unit_tests/output_distribution.cpp b/tests/unit_tests/output_distribution.cpp
index ab474a7d8..038c19874 100644
--- a/tests/unit_tests/output_distribution.cpp
+++ b/tests/unit_tests/output_distribution.cpp
@@ -30,10 +30,7 @@
#include "gtest/gtest.h"
#include "misc_log_ex.h"
#include "rpc/rpc_handler.h"
-#include "blockchain_db/blockchain_db.h"
#include "cryptonote_core/cryptonote_core.h"
-#include "cryptonote_core/tx_pool.h"
-#include "cryptonote_core/blockchain.h"
#include "blockchain_db/testdb.h"
static const uint64_t test_distribution[32] = {
@@ -77,9 +74,6 @@ public:
bool get_output_distribution(uint64_t amount, uint64_t from, uint64_t to, uint64_t &start_height, std::vector<uint64_t> &distribution, uint64_t &base)
{
- std::unique_ptr<cryptonote::Blockchain> bc;
- cryptonote::tx_memory_pool txpool(*bc);
- bc.reset(new cryptonote::Blockchain(txpool));
struct get_test_options {
const std::pair<uint8_t, uint64_t> hard_forks[2];
const cryptonote::test_options test_options = {
@@ -87,9 +81,9 @@ bool get_output_distribution(uint64_t amount, uint64_t from, uint64_t to, uint64
};
get_test_options():hard_forks{std::make_pair((uint8_t)1, (uint64_t)0), std::make_pair((uint8_t)0, (uint64_t)0)}{}
} opts;
- cryptonote::Blockchain *blockchain = bc.get();
- bool r = blockchain->init(new TestDB(test_distribution_size), cryptonote::FAKECHAIN, true, &opts.test_options, 0, NULL);
- return r && bc->get_output_distribution(amount, from, to, start_height, distribution, base);
+ cryptonote::BlockchainAndPool bap;
+ bool r = bap.blockchain.init(new TestDB(test_distribution_size), cryptonote::FAKECHAIN, true, &opts.test_options, 0, NULL);
+ return r && bap.blockchain.get_output_distribution(amount, from, to, start_height, distribution, base);
}
crypto::hash get_block_hash(uint64_t height)
diff --git a/tests/unit_tests/scaling_2021.cpp b/tests/unit_tests/scaling_2021.cpp
index 024a4b4fd..d90f0f9e6 100644
--- a/tests/unit_tests/scaling_2021.cpp
+++ b/tests/unit_tests/scaling_2021.cpp
@@ -50,9 +50,6 @@ public:
}
#define PREFIX_WINDOW(hf_version,window) \
- std::unique_ptr<cryptonote::Blockchain> bc; \
- cryptonote::tx_memory_pool txpool(*bc); \
- bc.reset(new cryptonote::Blockchain(txpool)); \
struct get_test_options { \
const std::pair<uint8_t, uint64_t> hard_forks[3]; \
const cryptonote::test_options test_options = { \
@@ -61,7 +58,9 @@ public:
}; \
get_test_options(): hard_forks{std::make_pair(1, (uint64_t)0), std::make_pair((uint8_t)hf_version, (uint64_t)1), std::make_pair((uint8_t)0, (uint64_t)0)} {} \
} opts; \
- cryptonote::Blockchain *blockchain = bc.get(); \
+ cryptonote::BlockchainAndPool bap; \
+ cryptonote::Blockchain *blockchain = &bap.blockchain; \
+ cryptonote::Blockchain *bc = blockchain; \
bool r = blockchain->init(new TestDB(), cryptonote::FAKECHAIN, true, &opts.test_options, 0, NULL); \
ASSERT_TRUE(r)
diff --git a/utils/python-rpc/framework/wallet.py b/utils/python-rpc/framework/wallet.py
index d78cb732f..1c42f7f04 100644
--- a/utils/python-rpc/framework/wallet.py
+++ b/utils/python-rpc/framework/wallet.py
@@ -297,7 +297,7 @@ class Wallet(object):
}
return self.rpc.send_json_rpc_request(query_key)
- def restore_deterministic_wallet(self, seed = '', seed_offset = '', filename = '', restore_height = 0, password = '', language = '', autosave_current = True):
+ def restore_deterministic_wallet(self, seed = '', seed_offset = '', filename = '', restore_height = 0, password = '', language = '', autosave_current = True, enable_multisig_experimental = False):
restore_deterministic_wallet = {
'method': 'restore_deterministic_wallet',
'params' : {
@@ -308,6 +308,7 @@ class Wallet(object):
'password': password,
'language': language,
'autosave_current': autosave_current,
+ 'enable_multisig_experimental': enable_multisig_experimental
},
'jsonrpc': '2.0',
'id': '0'