mirror of
https://github.com/monero-project/monero.git
synced 2026-06-12 19:11:36 -07:00
Add wire::json_writer, and use in some of ZMQ-PUB functions.
This commit is contained in:
committed by
Lee Clagett
parent
fb5b06bd3b
commit
108244cdf5
@@ -27,24 +27,17 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "serialization/wire/error.h"
|
||||
#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); }
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
// 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 <string>
|
||||
#include "serialization/wire/json/base.h"
|
||||
#include "serialization/wire/json/fwd.h"
|
||||
#include "serialization/wire/json/write.h"
|
||||
#include "serialization/wire/write.h"
|
||||
#include "span.h"
|
||||
|
||||
//! Define functions that list fields in `type` (calls de-virtualized)
|
||||
#define WIRE_JSON_DEFINE_OBJECT(type, map) \
|
||||
void read_bytes(::wire::json_reader& source, type& dest) \
|
||||
{ map(source, dest); } \
|
||||
\
|
||||
void write_bytes(::wire::json_writer& dest, const type& source) \
|
||||
{ map(dest, source); }
|
||||
|
||||
//! Define functions that convert `type` to/from json bytes
|
||||
#define WIRE_JSON_DEFINE_CONVERSION(type) \
|
||||
std::error_code convert_from_json(const ::epee::span<const char> source, type& dest) \
|
||||
{ return ::wire_read::from_bytes<::wire::json_reader>(source, dest); } \
|
||||
\
|
||||
std::error_code convert_to_json(std::string& dest, const type& source) \
|
||||
{ return ::wire_write::to_bytes<::wire::json_string_writer>(dest, source); }
|
||||
|
||||
//! Define functions that convert `type::request` and `type::response` to/from json bytes
|
||||
#define WIRE_JSON_DEFINE_COMMAND(type) \
|
||||
WIRE_JSON_DEFINE_CONVERSION(type::request) \
|
||||
WIRE_JSON_DEFINE_CONVERSION(type::response)
|
||||
@@ -0,0 +1,93 @@
|
||||
// Copyright (c) 2022, 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 <string>
|
||||
#include <system_error>
|
||||
#include "byte_stream.h"
|
||||
#include "serialization/wire/json/fwd.h"
|
||||
#include "span.h"
|
||||
|
||||
//! Declare functions that convert `type` to/from json bytes
|
||||
#define WIRE_JSON_DECLARE_CONVERSION(type) \
|
||||
std::error_code convert_from_json(::epee::span<const char>, type&); \
|
||||
std::error_code convert_to_json(std::string&, const type&) \
|
||||
|
||||
//! Declare functions that convert `type::request` and `type::response` to/from json bytes
|
||||
#define WIRE_JSON_DECLARE_COMMAND(type) \
|
||||
WIRE_JSON_DECLARE_CONVERSION(type::request); \
|
||||
WIRE_JSON_DECLARE_CONVERSION(type::response)
|
||||
|
||||
namespace wire
|
||||
{
|
||||
struct json
|
||||
{
|
||||
using input_type = json_reader;
|
||||
using output_type = json_string_writer;
|
||||
|
||||
//! Only enabled if `T` has templated method `from_bytes`.
|
||||
template<typename T>
|
||||
static auto from_bytes(const epee::span<const char> source, T& dest) -> decltype(dest.template from_bytes<input_type>(source))
|
||||
{
|
||||
return dest.template from_bytes<input_type>(source);
|
||||
}
|
||||
|
||||
//! Only enabled if `T` has templated method `to_bytes`.
|
||||
template<typename T>
|
||||
static auto to_bytes(std::string& dest, const T& source) -> decltype(source.template to_bytes<output_type>(dest))
|
||||
{
|
||||
return source.template to_bytes<output_type>(dest);
|
||||
}
|
||||
|
||||
|
||||
// Parameters packs have lower precedence; above functions are preferred.
|
||||
|
||||
template<typename... T>
|
||||
static std::error_code from_bytes(const epee::span<const char> source, T&... dest)
|
||||
{
|
||||
return convert_from_json(source, dest...); // ADL (searches every associated namespace)
|
||||
}
|
||||
|
||||
template<typename... T>
|
||||
static std::error_code to_bytes(std::string& dest, const T&... source)
|
||||
{
|
||||
return convert_to_json(dest, source...); // ADL (searches every associated namespace)
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static std::error_code to_bytes(epee::byte_stream& dest, const T& source)
|
||||
{
|
||||
std::string buf{};
|
||||
const std::error_code error = to_bytes(buf, source);
|
||||
if (!error)
|
||||
dest.write(epee::to_span(buf));
|
||||
return error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright (c) 2022, 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
|
||||
|
||||
//! Declare function that list fields `type` (calls de-virtualized)
|
||||
#define WIRE_JSON_DECLARE_OBJECT(type) \
|
||||
void read_bytes(::wire::json_reader&, type&); \
|
||||
void write_bytes(::wire::json_writer&, const type&)
|
||||
|
||||
namespace wire
|
||||
{
|
||||
struct json;
|
||||
class json_reader;
|
||||
struct json_string_writer;
|
||||
class json_writer;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
// Copyright (c) 2020, The Monero Project
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <rapidjson/writer.h>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "serialization/wire/write.h"
|
||||
#include "span.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
constexpr const std::size_t uint_to_string_size =
|
||||
std::numeric_limits<std::uintmax_t>::digits10 + 2;
|
||||
|
||||
struct string_stream
|
||||
{
|
||||
using Ch = char;
|
||||
std::string buffer_;
|
||||
|
||||
void Put(const char c) { buffer_.push_back(c); }
|
||||
static void Flush() noexcept {}
|
||||
};
|
||||
|
||||
//! Compatability/optimization for rapidjson.
|
||||
inline void PutReserve(string_stream& dest, const std::size_t length)
|
||||
{
|
||||
dest.buffer_.reserve(length);
|
||||
}
|
||||
|
||||
//! Compability/optimization for rapidjson.
|
||||
inline void PutN(string_stream& dest, const std::uint8_t ch, const std::size_t count)
|
||||
{
|
||||
dest.buffer_.append(count, ch);
|
||||
}
|
||||
|
||||
//! Writes JSON tokens one-at-a-time for DOMless output.
|
||||
class json_writer : public writer
|
||||
{
|
||||
string_stream stream_;
|
||||
rapidjson::Writer<string_stream> formatter_;
|
||||
bool needs_flush_;
|
||||
|
||||
virtual void do_flush(epee::span<const char>);
|
||||
|
||||
//! Flush written bytes to `do_flush(...)` if configured
|
||||
void check_flush();
|
||||
|
||||
protected:
|
||||
json_writer(bool needs_flush)
|
||||
: writer(), stream_{}, formatter_(stream_), needs_flush_(needs_flush)
|
||||
{}
|
||||
|
||||
json_writer(std::string&& bytes)
|
||||
: writer(), stream_{std::move(bytes)}, formatter_(stream_), needs_flush_(false)
|
||||
{}
|
||||
|
||||
//! \throw std::logic_error if incomplete JSON tree. \return JSON bytes
|
||||
std::string take_json();
|
||||
|
||||
//! Flush bytes in local buffer to `do_flush(...)`
|
||||
void flush()
|
||||
{
|
||||
do_flush(epee::to_span(stream_.buffer_));
|
||||
stream_.buffer_.clear();
|
||||
}
|
||||
|
||||
public:
|
||||
json_writer(const json_writer&) = delete;
|
||||
virtual ~json_writer() noexcept;
|
||||
json_writer& operator=(const json_writer&) = delete;
|
||||
|
||||
//! JSON does not have length field for arrays
|
||||
static constexpr std::false_type need_array_size() noexcept { return {}; }
|
||||
|
||||
//! \return Null-terminated buffer containing uint as decimal ascii
|
||||
static std::array<char, uint_to_string_size> to_string(std::uintmax_t) noexcept;
|
||||
|
||||
//! \throw std::logic_error if incomplete JSON tree
|
||||
void check_complete();
|
||||
|
||||
void boolean(bool) override final;
|
||||
|
||||
void integer(std::intmax_t) override final;
|
||||
void unsigned_integer(std::uintmax_t) override final;
|
||||
|
||||
void real(double) override final;
|
||||
|
||||
void string(std::string_view) override final;
|
||||
void binary(epee::span<const std::uint8_t>) override final;
|
||||
|
||||
void start_array(std::size_t) override final;
|
||||
void end_array() override final;
|
||||
|
||||
void start_object(std::size_t) override final;
|
||||
void key(std::string_view) override final;
|
||||
void binary_key(epee::span<const std::uint8_t>) override final;
|
||||
void end_object() override final;
|
||||
};
|
||||
|
||||
//! Buffers entire JSON message in memory
|
||||
struct json_string_writer final : json_writer
|
||||
{
|
||||
//! Write json to an empty buffer.
|
||||
explicit json_string_writer()
|
||||
: json_string_writer(std::string{})
|
||||
{}
|
||||
|
||||
//! Append json to existing `buffer`.
|
||||
explicit json_string_writer(std::string&& buffer)
|
||||
: json_writer(std::move(buffer))
|
||||
{}
|
||||
|
||||
//! \throw std::logic_error if incomplete JSON tree \return JSON bytes
|
||||
std::string take_buffer()
|
||||
{
|
||||
return json_writer::take_json();
|
||||
}
|
||||
};
|
||||
|
||||
//! Periodically flushes JSON data to `std::ostream`
|
||||
class json_stream_writer final : public json_writer
|
||||
{
|
||||
std::ostream& dest;
|
||||
|
||||
virtual void do_flush(epee::span<const char>) override final;
|
||||
public:
|
||||
explicit json_stream_writer(std::ostream& dest)
|
||||
: json_writer(true), dest(dest)
|
||||
{}
|
||||
|
||||
//! Flush remaining bytes to stream \throw std::logic_error if incomplete JSON tree
|
||||
void finish()
|
||||
{
|
||||
check_complete();
|
||||
flush();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// Copyright (c) 2025, 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>
|
||||
#include <utility>
|
||||
|
||||
#include "serialization/wire/field.h"
|
||||
#include "serialization/wire/traits.h"
|
||||
#include "serialization/wire.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
//! A wrapper that tells `wire::writer`s to write the inner type as an array.
|
||||
template<typename U>
|
||||
struct range_
|
||||
{
|
||||
WIRE_DEFINE_CONVERSIONS()
|
||||
|
||||
using value_type = unwrap_reference_t<U>;
|
||||
|
||||
U value;
|
||||
|
||||
constexpr const value_type& get_value() const noexcept { return value; }
|
||||
value_type& get_value() noexcept { return value; }
|
||||
|
||||
// concept requirements for `is_optional_on_empty`
|
||||
bool empty() const { return get_value().empty(); }
|
||||
};
|
||||
|
||||
template<typename F, typename T>
|
||||
void write_bytes(F& format, const range_<T> self)
|
||||
{
|
||||
wire_write::array(format, self.get_value());
|
||||
}
|
||||
|
||||
//! Links `value` with `range_`.
|
||||
template<typename T>
|
||||
inline constexpr range_<T> range(T value)
|
||||
{
|
||||
return {std::move(value)};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct is_optional_on_empty<range_<T>>
|
||||
: std::true_type
|
||||
{};
|
||||
} // wire
|
||||
@@ -27,10 +27,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <boost/range/size.hpp>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <string_view>
|
||||
#include <system_error>
|
||||
#include <type_traits>
|
||||
|
||||
@@ -73,14 +73,14 @@ namespace wire
|
||||
|
||||
virtual void real(double) = 0;
|
||||
|
||||
virtual void string(boost::string_ref) = 0;
|
||||
virtual void string(std::string_view) = 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 key(std::string_view) = 0;
|
||||
virtual void binary_key(epee::span<const std::uint8_t>) = 0;
|
||||
virtual void end_object() = 0;
|
||||
|
||||
@@ -129,7 +129,7 @@ namespace wire
|
||||
{ write_arithmetic(dest, source); }
|
||||
|
||||
template<typename W>
|
||||
inline void write_bytes(W& dest, const boost::string_ref source)
|
||||
inline void write_bytes(W& dest, const std::string_view source)
|
||||
{ dest.string(source); }
|
||||
|
||||
template<typename W, typename T>
|
||||
@@ -245,7 +245,7 @@ namespace wire_write
|
||||
}
|
||||
|
||||
template<typename W>
|
||||
inline void dynamic_object_key(W& dest, const boost::string_ref source)
|
||||
inline void dynamic_object_key(W& dest, const std::string_view source)
|
||||
{
|
||||
dest.key(source);
|
||||
}
|
||||
|
||||
@@ -87,3 +87,4 @@ target_include_directories(epee
|
||||
"${EPEE_INCLUDE_DIR_BASE}"
|
||||
"${OPENSSL_INCLUDE_DIR}")
|
||||
|
||||
add_subdirectory(wire)
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
# Copyright (c) 2025, 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.
|
||||
|
||||
set(wire_sources error.cpp write.cpp)
|
||||
|
||||
add_library(wire ${wire_sources})
|
||||
target_link_libraries(wire epee)
|
||||
|
||||
add_subdirectory(json)
|
||||
@@ -0,0 +1,97 @@
|
||||
// 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.
|
||||
|
||||
#include "serialization/wire/error.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
namespace error
|
||||
{
|
||||
const char* get_string(const schema value) noexcept
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
default:
|
||||
break;
|
||||
|
||||
case schema::none:
|
||||
return "No schema errors";
|
||||
case schema::array:
|
||||
return "Schema expected array";
|
||||
case schema::array_max_element:
|
||||
return "Schema expected array size to be smaller";
|
||||
case schema::array_min_size:
|
||||
return "Schema expected minimum wire size per array element to be larger";
|
||||
case schema::binary:
|
||||
return "Schema expected binary value of variable size";
|
||||
case schema::boolean:
|
||||
return "Schema expected boolean value";
|
||||
case schema::enumeration:
|
||||
return "Schema expected a specific of enumeration value(s)";
|
||||
case schema::fixed_binary:
|
||||
return "Schema expected binary of fixed size";
|
||||
case schema::integer:
|
||||
return "Schema expected integer value";
|
||||
case schema::invalid_key:
|
||||
return "Schema does not allow object field key";
|
||||
case schema::larger_integer:
|
||||
return "Schema expected a larger integer value";
|
||||
case schema::maximum_depth:
|
||||
return "Schema hit maximum array+object depth tracking";
|
||||
case schema::missing_key:
|
||||
return "Schema missing required field key";
|
||||
case schema::number:
|
||||
return "Schema expected number (integer or float) value";
|
||||
case schema::object:
|
||||
return "Schema expected object";
|
||||
case schema::smaller_integer:
|
||||
return "Schema expected a smaller integer value";
|
||||
case schema::string:
|
||||
return "Schema expected string";
|
||||
}
|
||||
return "Unknown schema error";
|
||||
}
|
||||
|
||||
const std::error_category& schema_category() noexcept
|
||||
{
|
||||
struct category final : std::error_category
|
||||
{
|
||||
virtual const char* name() const noexcept override final
|
||||
{
|
||||
return "wire::error::schema_category()";
|
||||
}
|
||||
|
||||
virtual std::string message(int value) const override final
|
||||
{
|
||||
return get_string(schema(value));
|
||||
}
|
||||
};
|
||||
static const category instance{};
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
# Copyright (c) 2025, 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.
|
||||
|
||||
set(wire-json_sources write.cpp)
|
||||
|
||||
add_library(wire-json ${wire-json_sources})
|
||||
target_link_libraries(wire-json wire)
|
||||
@@ -0,0 +1,154 @@
|
||||
// Copyright (c) 2020, The Monero Project
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "serialization/wire/json/write.h"
|
||||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "hex.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr const unsigned flush_threshold = 100;
|
||||
constexpr const unsigned max_buffer = 4096;
|
||||
|
||||
boost::container::small_vector<char, 256> json_hex(const epee::span<const std::uint8_t> source)
|
||||
{
|
||||
boost::container::small_vector<char, 256> buffer;
|
||||
buffer.resize(source.size() * 2);
|
||||
if (!epee::to_hex::buffer(epee::to_mut_span(buffer), source))
|
||||
throw std::logic_error{"Invalid buffer size for binary->hex conversion in json_writer"};
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
namespace wire
|
||||
{
|
||||
void json_writer::do_flush(epee::span<const char>)
|
||||
{}
|
||||
|
||||
void json_writer::check_flush()
|
||||
{
|
||||
if (needs_flush_ && (max_buffer < stream_.buffer_.size() || stream_.buffer_.capacity() - stream_.buffer_.size() < flush_threshold))
|
||||
flush();
|
||||
}
|
||||
|
||||
void json_writer::check_complete()
|
||||
{
|
||||
if (!formatter_.IsComplete())
|
||||
throw std::logic_error{"json_writer::take_json() failed with incomplete JSON tree"};
|
||||
}
|
||||
std::string json_writer::take_json()
|
||||
{
|
||||
check_complete();
|
||||
std::string out{std::move(stream_.buffer_)};
|
||||
stream_.buffer_.clear();
|
||||
formatter_.Reset(stream_);
|
||||
return out;
|
||||
}
|
||||
|
||||
json_writer::~json_writer() noexcept
|
||||
{}
|
||||
|
||||
std::array<char, uint_to_string_size> json_writer::to_string(const std::uintmax_t value) noexcept
|
||||
{
|
||||
static_assert(std::numeric_limits<std::uintmax_t>::max() <= std::numeric_limits<std::uint64_t>::max(), "bad uint conversion");
|
||||
std::array<char, uint_to_string_size> buf{{}};
|
||||
rapidjson::internal::u64toa(std::uint64_t(value), buf.data());
|
||||
return buf;
|
||||
}
|
||||
|
||||
void json_writer::boolean(const bool source)
|
||||
{
|
||||
formatter_.Bool(source);
|
||||
check_flush();
|
||||
}
|
||||
|
||||
void json_writer::integer(const std::intmax_t source)
|
||||
{
|
||||
static_assert(std::numeric_limits<std::int64_t>::min() <= std::numeric_limits<std::intmax_t>::min(), "too small");
|
||||
static_assert(std::numeric_limits<std::intmax_t>::max() <= std::numeric_limits<std::int64_t>::max(), "too large");
|
||||
formatter_.Int64(source);
|
||||
check_flush();
|
||||
}
|
||||
void json_writer::unsigned_integer(const std::uintmax_t source)
|
||||
{
|
||||
static_assert(std::numeric_limits<std::uintmax_t>::max() <= std::numeric_limits<std::uint64_t>::max(), "too large");
|
||||
formatter_.Uint64(source);
|
||||
check_flush();
|
||||
}
|
||||
void json_writer::real(const double source)
|
||||
{
|
||||
formatter_.Double(source);
|
||||
check_flush();
|
||||
}
|
||||
|
||||
void json_writer::string(const std::string_view source)
|
||||
{
|
||||
formatter_.String(source.data(), source.size());
|
||||
check_flush();
|
||||
}
|
||||
void json_writer::binary(const epee::span<const std::uint8_t> source)
|
||||
{
|
||||
const auto buffer = json_hex(source);
|
||||
string({buffer.data(), buffer.size()});
|
||||
}
|
||||
|
||||
void json_writer::start_array(std::size_t)
|
||||
{
|
||||
formatter_.StartArray();
|
||||
}
|
||||
void json_writer::end_array()
|
||||
{
|
||||
formatter_.EndArray();
|
||||
}
|
||||
|
||||
void json_writer::start_object(std::size_t)
|
||||
{
|
||||
formatter_.StartObject();
|
||||
}
|
||||
void json_writer::key(const std::string_view str)
|
||||
{
|
||||
formatter_.Key(str.data(), str.size());
|
||||
check_flush();
|
||||
}
|
||||
void json_writer::binary_key(const epee::span<const std::uint8_t> source)
|
||||
{
|
||||
const auto buffer = json_hex(source);
|
||||
key({buffer.data(), buffer.size()});
|
||||
}
|
||||
void json_writer::end_object()
|
||||
{
|
||||
formatter_.EndObject();
|
||||
}
|
||||
|
||||
void json_stream_writer::do_flush(const epee::span<const char> bytes)
|
||||
{
|
||||
dest.write(bytes.data(), bytes.size());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) 2025, The Monero Project
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "serialization/wire/write.h"
|
||||
|
||||
wire::writer::~writer() noexcept
|
||||
{}
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "generic-ops.h"
|
||||
#include "hex.h"
|
||||
#include "span.h"
|
||||
#include "serialization/wire/traits.h"
|
||||
|
||||
namespace crypto {
|
||||
|
||||
@@ -108,3 +109,9 @@ namespace crypto {
|
||||
|
||||
CRYPTO_MAKE_HASHABLE(hash)
|
||||
CRYPTO_MAKE_COMPARABLE(hash8)
|
||||
|
||||
namespace wire
|
||||
{
|
||||
WIRE_DECLARE_BLOB_NS(crypto::hash);
|
||||
WIRE_DECLARE_BLOB_NS(crypto::hash8);
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ using namespace epee;
|
||||
#include "crypto/crypto.h"
|
||||
#include "crypto/hash.h"
|
||||
#include "ringct/rctSigs.h"
|
||||
#include "serialization/wire.h"
|
||||
|
||||
using namespace crypto;
|
||||
|
||||
@@ -217,6 +218,16 @@ namespace cryptonote
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
namespace
|
||||
{
|
||||
template<typename F, typename T>
|
||||
void map_backlog_entry(F& format, T& self)
|
||||
{
|
||||
wire::object(format, WIRE_FIELD(id), WIRE_FIELD(weight), WIRE_FIELD(fee));
|
||||
}
|
||||
}
|
||||
WIRE_DEFINE_OBJECT(tx_block_template_backlog_entry, map_backlog_entry);
|
||||
//---------------------------------------------------------------
|
||||
crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::account_public_address>& change_addr)
|
||||
{
|
||||
account_public_address addr = {null_pkey, null_pkey};
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||
#include "ringct/rctOps.h"
|
||||
#include "serialization/wire/fwd.h"
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
@@ -114,6 +115,7 @@ namespace cryptonote
|
||||
uint64_t weight;
|
||||
uint64_t fee;
|
||||
};
|
||||
WIRE_DECLARE_OBJECT(tx_block_template_backlog_entry);
|
||||
|
||||
//---------------------------------------------------------------
|
||||
crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::account_public_address>& change_addr);
|
||||
|
||||
@@ -147,7 +147,9 @@ target_link_libraries(rpc_pub
|
||||
epee
|
||||
net
|
||||
cryptonote_basic
|
||||
cryptonote_core
|
||||
serialization
|
||||
wire-json
|
||||
${Boost_THREAD_LIBRARY})
|
||||
|
||||
target_link_libraries(daemon_messages
|
||||
@@ -167,6 +169,7 @@ target_link_libraries(daemon_rpc_server
|
||||
version
|
||||
daemon_messages
|
||||
serialization
|
||||
wire-json
|
||||
${Boost_REGEX_LIBRARY}
|
||||
${Boost_SYSTEM_LIBRARY}
|
||||
${Boost_THREAD_LIBRARY}
|
||||
|
||||
+128
-62
@@ -51,6 +51,10 @@
|
||||
#include "serialization/json_object.h"
|
||||
#include "ringct/rctTypes.h"
|
||||
#include "cryptonote_core/cryptonote_tx_utils.h"
|
||||
#include "serialization/wire.h"
|
||||
#include "serialization/wire/adapted/vector.h"
|
||||
#include "serialization/wire/json.h"
|
||||
#include "serialization/wire/wrapper/range.h"
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "net.zmq"
|
||||
@@ -59,9 +63,58 @@ namespace
|
||||
{
|
||||
constexpr const char txpool_signal[] = "tx_signal";
|
||||
|
||||
using chain_writer = void(epee::byte_stream&, std::uint64_t, epee::span<const cryptonote::block>);
|
||||
using miner_writer = void(epee::byte_stream&, uint8_t, uint64_t, const crypto::hash&, const crypto::hash&, cryptonote::difficulty_type, uint64_t, uint64_t, const std::vector<cryptonote::tx_block_template_backlog_entry>&);
|
||||
using txpool_writer = void(epee::byte_stream&, epee::span<const cryptonote::txpool_event>);
|
||||
using chain_writer = std::error_code(epee::byte_stream&, std::uint64_t, epee::span<const cryptonote::block>);
|
||||
using miner_writer = std::error_code(epee::byte_stream&, uint8_t, uint64_t, const crypto::hash&, const crypto::hash&, cryptonote::difficulty_type, uint64_t, uint64_t, const std::vector<cryptonote::tx_block_template_backlog_entry>&);
|
||||
using txpool_writer = std::error_code(epee::byte_stream&, epee::span<const cryptonote::txpool_event>);
|
||||
|
||||
namespace error
|
||||
{
|
||||
enum class code : int
|
||||
{
|
||||
none = 0, //!< Must be zero for `expect<..>`
|
||||
bad_block_hash //!< Unable to compute block hash
|
||||
};
|
||||
|
||||
//! \return Error message string.
|
||||
const char* get_string(code value) noexcept
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case code::none:
|
||||
return "no error";
|
||||
case code::bad_block_hash:
|
||||
return "Unable to hash block";
|
||||
default:
|
||||
break;
|
||||
};
|
||||
return "invalid zmq/pub error code";
|
||||
}
|
||||
|
||||
//! \return Category for `code`.
|
||||
const std::error_category& category() noexcept
|
||||
{
|
||||
struct category final : std::error_category
|
||||
{
|
||||
virtual const char* name() const noexcept override final
|
||||
{
|
||||
return "zmq_pub::error_category()";
|
||||
}
|
||||
|
||||
virtual std::string message(int value) const override final
|
||||
{
|
||||
return get_string(code(value));
|
||||
}
|
||||
};
|
||||
static const category instance{};
|
||||
return instance;
|
||||
}
|
||||
|
||||
//! \return Error code with `value` and `schema_category()`.
|
||||
inline std::error_code make_error_code(const code value) noexcept
|
||||
{
|
||||
return std::error_code{int(value), category()};
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
struct context
|
||||
@@ -106,23 +159,48 @@ namespace
|
||||
|
||||
//! \return `name:...` where `...` is JSON and `name` is directly copied (no quotes - not JSON).
|
||||
template<typename T>
|
||||
void json_pub(epee::byte_stream& buf, const T value)
|
||||
std::error_code json_pub(epee::byte_stream& buf, const T value)
|
||||
{
|
||||
rapidjson::Writer<epee::byte_stream> dest{buf};
|
||||
using cryptonote::json::toJsonValue;
|
||||
toJsonValue(dest, value);
|
||||
return {};
|
||||
}
|
||||
|
||||
//! Object for "minimal" block serialization
|
||||
struct minimal_chain
|
||||
{
|
||||
WIRE_DEFINE_CONVERSIONS()
|
||||
|
||||
const std::uint64_t height;
|
||||
const epee::span<const cryptonote::block> blocks;
|
||||
};
|
||||
void write_bytes(wire::writer& dest, const minimal_chain& self)
|
||||
{
|
||||
namespace adapt = boost::adaptors;
|
||||
|
||||
const auto to_block_id = [](const cryptonote::block& bl)
|
||||
{
|
||||
crypto::hash id;
|
||||
if (!get_block_hash(bl, id))
|
||||
WIRE_DLOG_THROW(error::code::bad_block_hash, "ZMQ/Pub failure");
|
||||
return id;
|
||||
};
|
||||
|
||||
assert(!self.blocks.empty()); // checked in zmq_pub::send_chain_main
|
||||
|
||||
wire::object(dest,
|
||||
wire::field("first_height", self.height),
|
||||
wire::field("first_prev_id", std::cref(self.blocks[0].prev_id)),
|
||||
wire::field("ids", wire::range(self.blocks | adapt::transformed(to_block_id)))
|
||||
);
|
||||
}
|
||||
|
||||
//! Object for miner data serialization
|
||||
struct miner_data
|
||||
{
|
||||
WIRE_DEFINE_CONVERSIONS()
|
||||
|
||||
uint8_t major_version;
|
||||
uint64_t height;
|
||||
const crypto::hash& prev_id;
|
||||
@@ -132,115 +210,98 @@ namespace
|
||||
uint64_t already_generated_coins;
|
||||
const std::vector<cryptonote::tx_block_template_backlog_entry>& tx_backlog;
|
||||
};
|
||||
void write_bytes(wire::writer& dest, const miner_data& self)
|
||||
{
|
||||
wire::object(dest,
|
||||
WIRE_FIELD_COPY(major_version),
|
||||
WIRE_FIELD_COPY(height),
|
||||
WIRE_FIELD(prev_id),
|
||||
WIRE_FIELD(seed_hash),
|
||||
wire::field("difficulty", cryptonote::hex(self.diff)),
|
||||
WIRE_FIELD_COPY(median_weight),
|
||||
WIRE_FIELD_COPY(already_generated_coins),
|
||||
WIRE_FIELD(tx_backlog)
|
||||
);
|
||||
}
|
||||
|
||||
//! Object for "minimal" tx serialization
|
||||
struct minimal_txpool
|
||||
{
|
||||
WIRE_DEFINE_CONVERSIONS()
|
||||
|
||||
const cryptonote::transaction& tx;
|
||||
crypto::hash hash;
|
||||
uint64_t blob_size;
|
||||
uint64_t weight;
|
||||
uint64_t fee;
|
||||
};
|
||||
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const minimal_chain& self)
|
||||
void write_bytes(wire::writer& dest, const minimal_txpool& self)
|
||||
{
|
||||
namespace adapt = boost::adaptors;
|
||||
|
||||
const auto to_block_id = [](const cryptonote::block& bl)
|
||||
{
|
||||
crypto::hash id;
|
||||
if (!get_block_hash(bl, id))
|
||||
MERROR("ZMQ/Pub failure: get_block_hash");
|
||||
return id;
|
||||
};
|
||||
|
||||
assert(!self.blocks.empty()); // checked in zmq_pub::send_chain_main
|
||||
|
||||
dest.StartObject();
|
||||
INSERT_INTO_JSON_OBJECT(dest, first_height, self.height);
|
||||
INSERT_INTO_JSON_OBJECT(dest, first_prev_id, self.blocks[0].prev_id);
|
||||
INSERT_INTO_JSON_OBJECT(dest, ids, (self.blocks | adapt::transformed(to_block_id)));
|
||||
dest.EndObject();
|
||||
wire::object(dest,
|
||||
wire::field("id", std::cref(self.hash)),
|
||||
WIRE_FIELD_COPY(blob_size),
|
||||
WIRE_FIELD_COPY(weight),
|
||||
WIRE_FIELD_COPY(fee)
|
||||
);
|
||||
}
|
||||
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const miner_data& self)
|
||||
std::error_code json_full_chain(epee::byte_stream& buf, const std::uint64_t height, const epee::span<const cryptonote::block> blocks)
|
||||
{
|
||||
dest.StartObject();
|
||||
INSERT_INTO_JSON_OBJECT(dest, major_version, self.major_version);
|
||||
INSERT_INTO_JSON_OBJECT(dest, height, self.height);
|
||||
INSERT_INTO_JSON_OBJECT(dest, prev_id, self.prev_id);
|
||||
INSERT_INTO_JSON_OBJECT(dest, seed_hash, self.seed_hash);
|
||||
INSERT_INTO_JSON_OBJECT(dest, difficulty, cryptonote::hex(self.diff));
|
||||
INSERT_INTO_JSON_OBJECT(dest, median_weight, self.median_weight);
|
||||
INSERT_INTO_JSON_OBJECT(dest, already_generated_coins, self.already_generated_coins);
|
||||
INSERT_INTO_JSON_OBJECT(dest, tx_backlog, self.tx_backlog);
|
||||
dest.EndObject();
|
||||
return json_pub(buf, blocks);
|
||||
}
|
||||
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const minimal_txpool& self)
|
||||
template<typename F>
|
||||
std::error_code minimal_chain_format(epee::byte_stream& buf, const std::uint64_t height, const epee::span<const cryptonote::block> blocks)
|
||||
{
|
||||
dest.StartObject();
|
||||
INSERT_INTO_JSON_OBJECT(dest, id, self.hash);
|
||||
INSERT_INTO_JSON_OBJECT(dest, blob_size, self.blob_size);
|
||||
INSERT_INTO_JSON_OBJECT(dest, weight, self.weight);
|
||||
INSERT_INTO_JSON_OBJECT(dest, fee, self.fee);
|
||||
dest.EndObject();
|
||||
return F::to_bytes(buf, minimal_chain{height, blocks});
|
||||
}
|
||||
|
||||
void json_full_chain(epee::byte_stream& buf, const std::uint64_t height, const epee::span<const cryptonote::block> blocks)
|
||||
template<typename F>
|
||||
std::error_code miner_data_format(epee::byte_stream& buf, uint8_t major_version, uint64_t height, const crypto::hash& prev_id, const crypto::hash& seed_hash, cryptonote::difficulty_type diff, uint64_t median_weight, uint64_t already_generated_coins, const std::vector<cryptonote::tx_block_template_backlog_entry>& tx_backlog)
|
||||
{
|
||||
json_pub(buf, blocks);
|
||||
}
|
||||
|
||||
void json_minimal_chain(epee::byte_stream& buf, const std::uint64_t height, const epee::span<const cryptonote::block> blocks)
|
||||
{
|
||||
json_pub(buf, minimal_chain{height, blocks});
|
||||
}
|
||||
|
||||
void json_miner_data(epee::byte_stream& buf, uint8_t major_version, uint64_t height, const crypto::hash& prev_id, const crypto::hash& seed_hash, cryptonote::difficulty_type diff, uint64_t median_weight, uint64_t already_generated_coins, const std::vector<cryptonote::tx_block_template_backlog_entry>& tx_backlog)
|
||||
{
|
||||
json_pub(buf, miner_data{major_version, height, prev_id, seed_hash, diff, median_weight, already_generated_coins, tx_backlog});
|
||||
return F::to_bytes(buf, miner_data{major_version, height, prev_id, seed_hash, diff, median_weight, already_generated_coins, tx_backlog});
|
||||
}
|
||||
|
||||
// boost::adaptors are in place "views" - no copy/move takes place
|
||||
// moving transactions (via sort, etc.), is expensive!
|
||||
|
||||
void json_full_txpool(epee::byte_stream& buf, epee::span<const cryptonote::txpool_event> txes)
|
||||
std::error_code json_full_txpool(epee::byte_stream& buf, epee::span<const cryptonote::txpool_event> txes)
|
||||
{
|
||||
namespace adapt = boost::adaptors;
|
||||
const auto to_full_tx = [](const cryptonote::txpool_event& event)
|
||||
{
|
||||
return event.tx;
|
||||
};
|
||||
json_pub(buf, (txes | adapt::filtered(is_valid{}) | adapt::transformed(to_full_tx)));
|
||||
return json_pub(buf, (txes | adapt::filtered(is_valid{}) | adapt::transformed(to_full_tx)));
|
||||
}
|
||||
|
||||
void json_minimal_txpool(epee::byte_stream& buf, epee::span<const cryptonote::txpool_event> txes)
|
||||
template<typename F>
|
||||
std::error_code minimal_txpool_format(epee::byte_stream& buf, epee::span<const cryptonote::txpool_event> txes)
|
||||
{
|
||||
namespace adapt = boost::adaptors;
|
||||
const auto to_minimal_tx = [](const cryptonote::txpool_event& event)
|
||||
{
|
||||
return minimal_txpool{event.tx, event.hash, event.blob_size, event.weight, cryptonote::get_tx_fee(event.tx)};
|
||||
};
|
||||
json_pub(buf, (txes | adapt::filtered(is_valid{}) | adapt::transformed(to_minimal_tx)));
|
||||
|
||||
return F::to_bytes(buf, wire::range(txes | adapt::filtered(is_valid{}) | adapt::transformed(to_minimal_tx)));
|
||||
}
|
||||
|
||||
constexpr const std::array<context<chain_writer>, 2> chain_contexts =
|
||||
{{
|
||||
{u8"json-full-chain_main", json_full_chain},
|
||||
{u8"json-minimal-chain_main", json_minimal_chain}
|
||||
{u8"json-minimal-chain_main", minimal_chain_format<wire::json>}
|
||||
}};
|
||||
|
||||
constexpr const std::array<context<miner_writer>, 1> miner_contexts =
|
||||
{{
|
||||
{u8"json-full-miner_data", json_miner_data},
|
||||
{u8"json-full-miner_data", miner_data_format<wire::json>}
|
||||
}};
|
||||
|
||||
constexpr const std::array<context<txpool_writer>, 2> txpool_contexts =
|
||||
{{
|
||||
{u8"json-full-txpool_add", json_full_txpool},
|
||||
{u8"json-minimal-txpool_add", json_minimal_txpool}
|
||||
{u8"json-minimal-txpool_add", minimal_txpool_format<wire::json>}
|
||||
}};
|
||||
|
||||
template<typename T, std::size_t N>
|
||||
@@ -288,20 +349,25 @@ namespace
|
||||
epee::byte_stream buf{};
|
||||
|
||||
std::size_t last_offset = 0;
|
||||
std::array<epee::byte_slice, N> out;
|
||||
std::array<std::size_t, N> offsets{{}};
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
{
|
||||
if (subs[i])
|
||||
{
|
||||
write_header(buf, contexts[i].name);
|
||||
contexts[i].generate_pub(buf, std::forward<U>(args)...);
|
||||
const std::error_code error = contexts[i].generate_pub(buf, std::forward<U>(args)...);
|
||||
if (error)
|
||||
{
|
||||
MERROR("Failed to serialize " << contexts[i].name << ": " << error.message());
|
||||
return out;
|
||||
}
|
||||
offsets[i] = buf.size() - last_offset;
|
||||
last_offset = buf.size();
|
||||
}
|
||||
}
|
||||
|
||||
epee::byte_slice bytes{std::move(buf)};
|
||||
std::array<epee::byte_slice, N> out;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
out[i] = bytes.take_slice(offsets[i]);
|
||||
|
||||
|
||||
+2
-2
@@ -82,8 +82,8 @@ class zmq_pub
|
||||
//! Process a client subscription request (from XPUB sockets). Thread-safe.
|
||||
bool sub_request(const boost::string_ref message);
|
||||
|
||||
/*! Forward ZMQ messages sent to `relay` via `send_chain_main` or
|
||||
`send_txpool_add` to `pub`. Used by `ZmqServer`. */
|
||||
/*! Forward ZMQ messages sent to `relay` via `send_chain_main`,
|
||||
`send_txpool_add`, or `send_miner_data` to `pub`. Used by `ZmqServer`. */
|
||||
bool relay_to_pub(void* relay, void* pub);
|
||||
|
||||
/*! Send a `ZMQ_PUB` notification for a change to the main chain.
|
||||
|
||||
@@ -1532,27 +1532,6 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_distribu
|
||||
GET_FROM_JSON_OBJECT(val, dist.data.base, base);
|
||||
}
|
||||
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::tx_block_template_backlog_entry& entry)
|
||||
{
|
||||
dest.StartObject();
|
||||
INSERT_INTO_JSON_OBJECT(dest, id, entry.id);
|
||||
INSERT_INTO_JSON_OBJECT(dest, weight, entry.weight);
|
||||
INSERT_INTO_JSON_OBJECT(dest, fee, entry.fee);
|
||||
dest.EndObject();
|
||||
}
|
||||
|
||||
void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_block_template_backlog_entry& entry)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, entry.id, id);
|
||||
GET_FROM_JSON_OBJECT(val, entry.weight, weight);
|
||||
GET_FROM_JSON_OBJECT(val, entry.fee, fee);
|
||||
}
|
||||
|
||||
} // namespace json
|
||||
|
||||
} // namespace cryptonote
|
||||
|
||||
@@ -311,9 +311,6 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& inf
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::output_distribution& dist);
|
||||
void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_distribution& dist);
|
||||
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::tx_block_template_backlog_entry& entry);
|
||||
void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_block_template_backlog_entry& entry);
|
||||
|
||||
template <typename Map>
|
||||
typename std::enable_if<sfinae::is_map_like<Map>::value, void>::type toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const Map& map);
|
||||
|
||||
|
||||
@@ -30,11 +30,14 @@
|
||||
#include <boost/preprocessor/stringize.hpp>
|
||||
#include <gtest/gtest.h>
|
||||
#include <rapidjson/document.h>
|
||||
#include <rapidjson/ostreamwrapper.h>
|
||||
#include <rapidjson/prettywriter.h>
|
||||
|
||||
#include "cryptonote_basic/account.h"
|
||||
#include "cryptonote_basic/cryptonote_basic.h"
|
||||
#include "cryptonote_basic/events.h"
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||
#include "cryptonote_core/cryptonote_tx_utils.h"
|
||||
#include "json_serialization.h"
|
||||
#include "net/zmq.h"
|
||||
#include "rpc/message.h"
|
||||
@@ -47,6 +50,17 @@
|
||||
if (!(__VA_ARGS__)) \
|
||||
return testing::AssertionFailure() << BOOST_PP_STRINGIZE(__VA_ARGS__)
|
||||
|
||||
namespace rapidjson
|
||||
{
|
||||
std::ostream& operator<<(std::ostream& out, const Document& src)
|
||||
{
|
||||
OStreamWrapper buffer{out};
|
||||
PrettyWriter<OStreamWrapper> writer{buffer};
|
||||
src.Accept(writer);
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ZmqFullMessage, InvalidRequest)
|
||||
{
|
||||
EXPECT_THROW(
|
||||
@@ -138,6 +152,16 @@ namespace
|
||||
return out;
|
||||
}
|
||||
|
||||
testing::AssertionResult compare_json(const std::string expected_json, const rapidjson::Document& published)
|
||||
{
|
||||
rapidjson::Document expected;
|
||||
expected.Parse(expected_json.c_str());
|
||||
MASSERT(!expected.HasParseError());
|
||||
if (expected != published)
|
||||
return testing::AssertionFailure() << expected << " != " << published;
|
||||
return testing::AssertionSuccess();
|
||||
}
|
||||
|
||||
testing::AssertionResult compare_full_txpool(epee::span<const cryptonote::txpool_event> events, const published_json& pub)
|
||||
{
|
||||
MASSERT(pub.first == "json-full-txpool_add");
|
||||
@@ -193,6 +217,12 @@ namespace
|
||||
return testing::AssertionSuccess();
|
||||
}
|
||||
|
||||
testing::AssertionResult compare_miner_data(const std::string expected, const published_json& pub)
|
||||
{
|
||||
MASSERT(pub.first == "json-full-miner_data");
|
||||
return compare_json(expected, pub.second);
|
||||
}
|
||||
|
||||
testing::AssertionResult compare_full_block(const epee::span<const cryptonote::block> expected, const published_json& pub)
|
||||
{
|
||||
MASSERT(pub.first == "json-full-chain_main");
|
||||
@@ -513,6 +543,66 @@ TEST_F(zmq_pub, JsonFullChain)
|
||||
EXPECT_TRUE(compare_full_block(epee::to_span(blocks), pubs.front()));
|
||||
}
|
||||
|
||||
TEST_F(zmq_pub, JsonFullMinerData)
|
||||
{
|
||||
/* uint8_t major_version;
|
||||
uint64_t height;
|
||||
const crypto::hash& prev_id;
|
||||
const crypto::hash& seed_hash;
|
||||
cryptonote::difficulty_type diff;
|
||||
uint64_t median_weight;
|
||||
uint64_t already_generated_coins;
|
||||
const std::vector<cryptonote::tx_block_template_backlog_entry>& tx_backlog ; */
|
||||
|
||||
static constexpr const char topic[] = "\1json-full-miner_data";
|
||||
ASSERT_TRUE(sub_request(topic));
|
||||
|
||||
//std::size_t send_miner_data(uint8_t major_version, uint64_t height, const crypto::hash& prev_id, const crypto::hash& seed_hash, difficulty_type diff, uint64_t median_weight, uint64_t already_generated_coins, const std::vector<tx_block_template_backlog_entry>& tx_backlog);
|
||||
|
||||
const auto hash = crypto::rand<crypto::hash>();
|
||||
const auto seed = crypto::rand<crypto::hash>();
|
||||
const cryptonote::difficulty_type difficulty = 500;
|
||||
const std::vector<cryptonote::tx_block_template_backlog_entry> txs = {
|
||||
{crypto::rand<crypto::hash>(), 7545, 455},
|
||||
{crypto::rand<crypto::hash>(), 755, 34545}
|
||||
};
|
||||
const std::string expected =
|
||||
R"({
|
||||
"major_version": 100,
|
||||
"height": 200,
|
||||
"prev_id": ")" + epee::to_hex::string(epee::as_byte_span(hash)) + R"(",
|
||||
"seed_hash": ")" + epee::to_hex::string(epee::as_byte_span(seed)) + R"(",
|
||||
"difficulty": ")" + cryptonote::hex(difficulty) + R"(",
|
||||
"median_weight": 400,
|
||||
"already_generated_coins": 10000,
|
||||
"tx_backlog": [
|
||||
{
|
||||
"id": ")" + epee::to_hex::string(epee::as_byte_span(txs.at(0).id)) + R"(",
|
||||
"weight": 7545,
|
||||
"fee": 455
|
||||
},{
|
||||
"id": ")" + epee::to_hex::string(epee::as_byte_span(txs.at(1).id)) + R"(",
|
||||
"weight": 755,
|
||||
"fee": 34545
|
||||
}
|
||||
]
|
||||
})";
|
||||
EXPECT_EQ(1u, pub->send_miner_data(100, 200, hash, seed, difficulty, 400, 10000, txs));
|
||||
EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get()));
|
||||
|
||||
auto pubs = get_published(dummy_client.get());
|
||||
ASSERT_EQ(1u, pubs.size());
|
||||
EXPECT_TRUE(compare_miner_data(expected, pubs.front()));
|
||||
|
||||
EXPECT_NO_THROW(cryptonote::listener::zmq_pub::miner_data{pub}(100, 200, hash, seed, difficulty, 400, 10000, txs));
|
||||
EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get()));
|
||||
|
||||
pubs = get_published(dummy_client.get());
|
||||
ASSERT_EQ(1u, pubs.size());
|
||||
EXPECT_TRUE(compare_miner_data(expected, pubs.front()));
|
||||
}
|
||||
|
||||
|
||||
TEST_F(zmq_pub, JsonMinimalChain)
|
||||
{
|
||||
static constexpr const char topic[] = "\1json-minimal-chain_main";
|
||||
|
||||
Reference in New Issue
Block a user