mirror of
https://github.com/wownero/lwsf-wow.git
synced 2026-10-17 07:21:28 +02:00
Initial base set of code
This is far from complete, but has the basic structure and concepts implemented. The remainder is just fill in the blanks, and testing.
This commit is contained in:
committed by
Lee *!* Clagett
parent
354c7b7e6b
commit
baaa19f2f4
+241
@@ -0,0 +1,241 @@
|
||||
# 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.
|
||||
|
||||
cmake_minimum_required(VERSION 3.1.0)
|
||||
project(lws-frontend)
|
||||
|
||||
enable_language(CXX)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
option(BUILD_TESTS "Build Tests" OFF)
|
||||
|
||||
set(MONERO_LIBRARIES
|
||||
daemon_messages
|
||||
serialization
|
||||
lmdb_lib
|
||||
net
|
||||
cryptonote_core
|
||||
cryptonote_basic
|
||||
cryptonote_format_utils_basic
|
||||
ringct
|
||||
ringct_basic
|
||||
multisig
|
||||
blockchain_db
|
||||
common
|
||||
lmdb
|
||||
device
|
||||
cncrypto
|
||||
randomx
|
||||
epee
|
||||
easylogging
|
||||
version
|
||||
unbound
|
||||
)
|
||||
|
||||
set(MONERO_OPTIONAL unbound)
|
||||
|
||||
set(MONERO_SEARCH_PATHS
|
||||
"/contrib/epee/src"
|
||||
"/external/db_drivers/liblmdb"
|
||||
"/external/easylogging++"
|
||||
"/src"
|
||||
"/src/crypto"
|
||||
"/src/cryptonote_basic"
|
||||
"/src/lmdb"
|
||||
"/src/ringct"
|
||||
"/src/rpc"
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# Pull some information from monero build
|
||||
#
|
||||
|
||||
# Needed due to "bug" in monero CMake - the `project` function is used twice!
|
||||
if (NOT MONERO_SOURCE_DIR)
|
||||
message(FATAL_ERROR "The argument -DMONERO_SOURCE_DIR must specify a location of a monero source tree")
|
||||
endif()
|
||||
|
||||
if (NOT MONERO_BUILD_DIR)
|
||||
message(FATAL_ERROR "The argument -DMONERO_BUILD_DIR must specify a location of an existing monero build")
|
||||
endif()
|
||||
|
||||
if(STATIC)
|
||||
if(MSVC)
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES .lib .dll.a .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||
else()
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
load_cache(${MONERO_BUILD_DIR} READ_WITH_PREFIX monero_
|
||||
Boost_THREAD_LIBRARY_RELEASE
|
||||
CMAKE_CXX_COMPILER
|
||||
EXTRA_LIBRARIES
|
||||
LIBUDEV_LIBRARY
|
||||
usb_LIBRARY
|
||||
HIDAPI_INCLUDE_DIR
|
||||
HIDAPI_LIBRARY
|
||||
LMDB_INCLUDE
|
||||
monero_SOURCE_DIR
|
||||
OPENSSL_INCLUDE_DIR
|
||||
OPENSSL_CRYPTO_LIBRARY
|
||||
OPENSSL_SSL_LIBRARY
|
||||
sodium_LIBRARY_RELEASE
|
||||
UNBOUND_LIBRARIES
|
||||
ZMQ_INCLUDE_PATH
|
||||
ZMQ_LIB
|
||||
PGM_LIBRARY
|
||||
NORM_LIBRARY
|
||||
GSSAPI_LIBRARY
|
||||
PROTOLIB_LIBRARY
|
||||
)
|
||||
|
||||
if (NOT (monero_monero_SOURCE_DIR MATCHES "${MONERO_SOURCE_DIR}(/src/cryptonote_protocol)"))
|
||||
message(FATAL_ERROR "Invalid Monero source dir - does not appear to match source used for build directory")
|
||||
endif()
|
||||
|
||||
if (NOT (CMAKE_CXX_COMPILER STREQUAL monero_CMAKE_CXX_COMPILER))
|
||||
message(FATAL_ERROR "Compiler for monero build differs from this project")
|
||||
endif()
|
||||
|
||||
if ("${monero_UNBOUND_LIBRARIES}" STREQUAL "UNBOUND_LIBRARIES-NOTFOUND")
|
||||
unset(monero_UNBOUND_LIBRARIES)
|
||||
endif()
|
||||
|
||||
if ("${monero_HIDAPI_LIBRARY}" STREQUAL "HIDAPI_LIBRARY-NOTFOUND")
|
||||
unset(monero_HIDAPI_INCLUDE_DIR)
|
||||
unset(monero_HIDAPI_LIBRARY)
|
||||
endif()
|
||||
|
||||
#
|
||||
# Dependencies specific to monero-lws
|
||||
#
|
||||
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
# boost
|
||||
set(Boost_NO_BOOST_CMAKE ON)
|
||||
if(STATIC)
|
||||
set(Boost_USE_STATIC_LIBS ON)
|
||||
set(Boost_USE_STATIC_RUNTIME ON)
|
||||
endif()
|
||||
find_package(Boost 1.58 QUIET REQUIRED COMPONENTS chrono filesystem program_options regex serialization system thread)
|
||||
|
||||
if (NOT (Boost_THREAD_LIBRARY STREQUAL monero_Boost_THREAD_LIBRARY_RELEASE))
|
||||
message(STATUS "Found Boost_THREAD_LIBRARY: ${Boost_THREAD_LIBRARY}")
|
||||
message(STATUS "Found monero_Boost_THREAD_LIBRARY_RELEASE: ${monero_Boost_THREAD_LIBRARY_RELEASE}")
|
||||
message(FATAL_ERROR "Boost libraries for monero build differs from this project")
|
||||
endif()
|
||||
|
||||
if(monero_PGM_LIBRARY)
|
||||
set(ZMQ_LIB "${ZMQ_LIB};${monero_PGM_LIBRARY}")
|
||||
endif()
|
||||
if(monero_NORM_LIBRARY)
|
||||
set(ZMQ_LIB "${ZMQ_LIB};${monero_NORM_LIBRARY}")
|
||||
endif()
|
||||
if(monero_GSSAPI_LIBRARY)
|
||||
set(ZMQ_LIB "${ZMQ_LIB};${monero_GSSAPI_LIBRARY}")
|
||||
endif()
|
||||
if(monero_PROTOLIB_LIBRARY)
|
||||
set(ZMQ_LIB "${ZMQ_LIB};${monero_PROTOLIB_LIBRARY}")
|
||||
endif()
|
||||
if(monero_sodium_LIBRARY_RELEASE)
|
||||
set(ZMQ_LIB "${ZMQ_LIB};${monero_sodium_LIBRARY_RELEASE}")
|
||||
endif()
|
||||
|
||||
if(STATIC AND NOT IOS)
|
||||
if(UNIX)
|
||||
set(monero_OPENSSL_LIBRARIES "${monero_OPENSSL_LIBRARIES};${CMAKE_DL_LIBS};${CMAKE_THREAD_LIBS_INIT}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
foreach (LIB ${MONERO_LIBRARIES})
|
||||
find_library(LIB_PATH NAMES "${LIB}" PATHS ${MONERO_BUILD_DIR} PATH_SUFFIXES "/src/${LIB}" "external/${LIB}" ${MONERO_SEARCH_PATHS} NO_DEFAULT_PATH)
|
||||
|
||||
list(FIND MONERO_OPTIONAL "${LIB}" LIB_OPTIONAL)
|
||||
if (NOT LIB_PATH)
|
||||
if (LIB_OPTIONAL EQUAL -1)
|
||||
message(FATAL_ERROR "Unable to find required Monero library ${LIB}")
|
||||
endif()
|
||||
else ()
|
||||
set(LIB_NAME "monero::${LIB}")
|
||||
add_library(${LIB_NAME} STATIC IMPORTED)
|
||||
set_target_properties(${LIB_NAME} PROPERTIES IMPORTED_LOCATION ${LIB_PATH})
|
||||
list(APPEND IMPORTED_MONERO_LIBRARIES "${LIB_NAME}")
|
||||
endif()
|
||||
unset(LIB_PATH CACHE)
|
||||
endforeach()
|
||||
|
||||
if(APPLE)
|
||||
find_library(IOKIT_LIBRARY IOKit)
|
||||
list(APPEND IMPORTED_MONERO_LIBRARIES ${IOKIT_LIBRARY})
|
||||
endif()
|
||||
|
||||
add_library(monero::libraries INTERFACE IMPORTED)
|
||||
set_property(TARGET monero::libraries PROPERTY
|
||||
INTERFACE_INCLUDE_DIRECTORIES
|
||||
${Boost_INCLUDE_DIR}
|
||||
${monero_HIDAPI_INCLUDE_DIRS}
|
||||
${monero_OPENSSL_INCLUDE_DIR}
|
||||
"${MONERO_BUILD_DIR}/generated_include"
|
||||
"${MONERO_SOURCE_DIR}/contrib/epee/include"
|
||||
"${MONERO_SOURCE_DIR}/external/easylogging++"
|
||||
"${MONERO_SOURCE_DIR}/external/rapidjson/include"
|
||||
"${MONERO_SOURCE_DIR}/external/supercop/include"
|
||||
"${MONERO_SOURCE_DIR}/src"
|
||||
)
|
||||
set_property(TARGET monero::libraries PROPERTY
|
||||
INTERFACE_LINK_LIBRARIES
|
||||
${IMPORTED_MONERO_LIBRARIES}
|
||||
${Boost_CHRONO_LIBRARY}
|
||||
${Boost_FILESYSTEM_LIBRARY}
|
||||
${Boost_PROGRAM_OPTIONS_LIBRARY}
|
||||
${Boost_REGEX_LIBRARY}
|
||||
${Boost_SERIALIZATION_LIBRARY}
|
||||
${Boost_SYSTEM_LIBRARY}
|
||||
${Boost_THREAD_LIBRARY}
|
||||
${monero_HIDAPI_LIBRARY}
|
||||
${monero_usb_LIBRARY}
|
||||
${monero_LIBUDEV_LIBRARY}
|
||||
${monero_OPENSSL_SSL_LIBRARY}
|
||||
${monero_OPENSSL_CRYPTO_LIBRARY}
|
||||
${monero_sodium_LIBRARY_RELEASE}
|
||||
${monero_UNBOUND_LIBRARIES}
|
||||
${CMAKE_DL_LIBS}
|
||||
)
|
||||
|
||||
#
|
||||
# Build monero-lws code
|
||||
#
|
||||
|
||||
include_directories(include)
|
||||
add_subdirectory(src)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,42 @@
|
||||
# Copyright (c) 2024, 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_directories(.)
|
||||
|
||||
# For both the server and admin utility.
|
||||
set(monero-lwsf_sources backend.cpp rpc.cpp transaction_history.cpp transaction_info.cpp wallet.cpp wallet_manager.cpp)
|
||||
set(monero-lwsf_headers backend.h lwsf_config.h rpc.h transaction_history.h transaction_info.h wallet.h)
|
||||
|
||||
add_executable(monero-lwsf dummy_main.cpp ${monero-lwsf_sources} ${monero-lwsf_headers})
|
||||
target_link_libraries(monero-lwsf
|
||||
PRIVATE
|
||||
monero::libraries
|
||||
${Boost_PROGRAM_OPTIONS_LIBRARY}
|
||||
${Boost_FILESYSTEM_LIBRARY}
|
||||
)
|
||||
|
||||
+340
@@ -0,0 +1,340 @@
|
||||
// Copyright (c) 2024, 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 "backend.h"
|
||||
|
||||
#include <boost/thread/lock_guard.hpp>
|
||||
#include "cryptonote_basic/cryptonote_basic_impl.h" // monero/src
|
||||
#include "lwsf_config.h"
|
||||
#include "wire.h"
|
||||
#include "wire/adapted/crypto.h"
|
||||
#include "wire/msgpack.h"
|
||||
#include "wire/wrapper/trusted_array.h"
|
||||
|
||||
namespace lwsf
|
||||
{
|
||||
WIRE_AS_INTEGER(TransactionInfo::Direction);
|
||||
WIRE_AS_INTEGER(NetworkType);
|
||||
}
|
||||
namespace lwsf { namespace internal { namespace backend
|
||||
{
|
||||
namespace
|
||||
{
|
||||
cryptonote::network_type convert_net_type(const NetworkType in)
|
||||
{
|
||||
switch(in)
|
||||
{
|
||||
case NetworkType::MAINNET:
|
||||
return cryptonote::network_type::MAINNET;
|
||||
case NetworkType::TESTNET:
|
||||
return cryptonote::network_type::TESTNET;
|
||||
case NetworkType::STAGENET:
|
||||
return cryptonote::network_type::STAGENET;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return cryptonote::network_type::UNDEFINED;
|
||||
}
|
||||
|
||||
template<typename F, typename T>
|
||||
void map_transfer_out(F& format, T& self)
|
||||
{
|
||||
wire::object(format,
|
||||
WIRE_FIELD(address),
|
||||
WIRE_FIELD(amount),
|
||||
WIRE_FIELD(sender),
|
||||
WIRE_OPTIONAL_FIELD(secret),
|
||||
WIRE_FIELD(tx_pub)
|
||||
);
|
||||
}
|
||||
|
||||
template<typename F, typename T>
|
||||
void map_transfer_in(F& format, T& self)
|
||||
{
|
||||
wire::object(format,
|
||||
WIRE_FIELD(global_index),
|
||||
WIRE_FIELD(amount),
|
||||
WIRE_FIELD(recipient),
|
||||
WIRE_FIELD(index),
|
||||
WIRE_FIELD(output_pub),
|
||||
WIRE_FIELD(rct_mask)
|
||||
);
|
||||
}
|
||||
}
|
||||
static void read_bytes(wire::reader& source, std::pair<crypto::public_key, transfer_in>& dest)
|
||||
{
|
||||
read_bytes(source, dest.second);
|
||||
dest.first = dest.second.output_pub;
|
||||
}
|
||||
static void write_bytes(wire::writer& dest, const std::pair<const crypto::public_key, transfer_in>& source)
|
||||
{
|
||||
write_bytes(dest, source.second);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
template<typename F, typename T>
|
||||
void map_transaction(F& format, T& self)
|
||||
{
|
||||
//! TODO complete payment_id storage
|
||||
wire::object(format,
|
||||
wire::field("spends", wire::trusted_array(std::ref(self.spends))),
|
||||
wire::field("receives", wire::trusted_array(std::ref(self.receives))),
|
||||
WIRE_FIELD(description),
|
||||
WIRE_FIELD(label),
|
||||
WIRE_OPTIONAL_FIELD(timestamp),
|
||||
WIRE_FIELD(amount),
|
||||
WIRE_OPTIONAL_FIELD(height),
|
||||
WIRE_FIELD(unlock_time),
|
||||
WIRE_FIELD(direction),
|
||||
WIRE_OPTIONAL_FIELD(tx_secret),
|
||||
WIRE_FIELD(id),
|
||||
WIRE_FIELD(coinbase)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static void read_bytes(wire::reader& source, std::pair<crypto::hash, std::shared_ptr<transaction>>& dest)
|
||||
{
|
||||
if (!dest.second)
|
||||
dest.second = std::make_shared<transaction>(transaction{});
|
||||
read_bytes(source, *dest.second);
|
||||
dest.first = dest.second->id;
|
||||
}
|
||||
static void write_bytes(wire::writer& dest, const std::pair<const crypto::hash, std::shared_ptr<transaction>>& source)
|
||||
{
|
||||
if (!source.second)
|
||||
WIRE_DLOG_THROW(wire::error::schema::object, "Unexpected nullptr");
|
||||
write_bytes(dest, *source.second);
|
||||
}
|
||||
namespace {
|
||||
template<typename F, typename T>
|
||||
void map_keypair(F& format, T& self)
|
||||
{
|
||||
wire::object(format, WIRE_FIELD(sec), WIRE_FIELD(pub));
|
||||
}
|
||||
|
||||
template<typename F, typename T>
|
||||
void map_account(F& format, T& self)
|
||||
{
|
||||
wire::object(format,
|
||||
WIRE_FIELD(restore_height),
|
||||
wire::field("txes", wire::trusted_array(std::ref(self.txes))),
|
||||
WIRE_FIELD(type),
|
||||
WIRE_FIELD(view),
|
||||
WIRE_FIELD(spend)
|
||||
);
|
||||
}
|
||||
|
||||
void new_tx(const account& self, transaction& out, const rpc::transaction& source)
|
||||
{
|
||||
out.id = source.tx_hash;
|
||||
out.timestamp = source.timestamp;
|
||||
out.fee = std::uint64_t(source.fee.value_or(rpc::uint64_string(0)));
|
||||
out.height = source.height;
|
||||
out.unlock_time = source.unlock_time;
|
||||
out.payment_id = source.payment_id;
|
||||
out.coinbase = source.is_coinbase;
|
||||
|
||||
std::uint64_t total_spent = 0;
|
||||
for (const auto& spend : source.spent_outputs)
|
||||
{
|
||||
crypto::key_derivation derivation{};
|
||||
if (!crypto::generate_key_derivation(spend.tx_pub_key, self.view.sec, derivation))
|
||||
continue;
|
||||
crypto::public_key output_pub{};
|
||||
if (!crypto::derive_public_key(derivation, spend.out_index, self.spend.pub, output_pub))
|
||||
continue;
|
||||
crypto::secret_key output_secret{};
|
||||
crypto::derive_secret_key(derivation, spend.out_index, self.spend.sec, output_secret);
|
||||
|
||||
crypto::key_image image{};
|
||||
crypto::generate_key_image(output_pub, output_secret, image);
|
||||
if (image == spend.key_image)
|
||||
{
|
||||
out.spends.emplace_back(std::uint64_t(spend.amount), spend.tx_pub_key);
|
||||
total_spent += std::uint64_t(spend.amount);
|
||||
}
|
||||
}
|
||||
|
||||
if (rpc::uint64_string(total_spent) < source.total_received)
|
||||
{
|
||||
out.direction = TransactionInfo::Direction::In;
|
||||
out.amount = std::uint64_t(source.total_received) - total_spent;
|
||||
}
|
||||
else
|
||||
{
|
||||
out.direction = TransactionInfo::Direction::Out;
|
||||
out.amount = total_spent - std::uint64_t(source.total_received);
|
||||
}
|
||||
}
|
||||
|
||||
void merge_output(std::shared_ptr<transaction>& out, const rpc::output& output)
|
||||
{
|
||||
if (!out)
|
||||
throw std::logic_error{"nullptr transaction in merge_output"};
|
||||
|
||||
out->prefix = output.tx_prefix_hash;
|
||||
const auto receive = out->receives.lower_bound(output.public_key);
|
||||
if (receive == out->receives.end() || receive->first != output.public_key)
|
||||
{
|
||||
out->receives.emplace_hint(receive, output.public_key, transfer_in{output});
|
||||
}
|
||||
}
|
||||
|
||||
bool merge_response(wallet& self, const rpc::get_address_txs& source)
|
||||
{
|
||||
bool updated_wallet = false;
|
||||
self.scan_height = source.scanned_block_height;
|
||||
self.blockchain_height = source.blockchain_height;
|
||||
self.primary.restore_height = source.start_height;
|
||||
|
||||
self.primary.txes.reserve(source.transactions.size());
|
||||
for (const auto& tx : source.transactions)
|
||||
{
|
||||
auto iter = self.primary.txes.lower_bound(tx.tx_hash);
|
||||
if (iter == self.primary.txes.end() || iter->first != tx.tx_hash)
|
||||
{
|
||||
updated_wallet = true;
|
||||
new_tx(self.primary, *self.primary.txes.emplace_hint(iter, tx.tx_hash, std::make_shared<transaction>(transaction{}))->second, tx);
|
||||
}
|
||||
}
|
||||
return updated_wallet;
|
||||
}
|
||||
|
||||
void merge_response(account& self, const rpc::get_unspent_outs& source)
|
||||
{
|
||||
for (const auto& output : source.outputs)
|
||||
{
|
||||
auto iter = self.txes.find(output.tx_hash);
|
||||
if (iter != self.txes.end())
|
||||
merge_output(iter->second, output);
|
||||
}
|
||||
}
|
||||
} // anonymous
|
||||
|
||||
WIRE_DEFINE_OBJECT(transfer_out, map_transfer_out);
|
||||
WIRE_DEFINE_OBJECT(transfer_in, map_transfer_in);
|
||||
WIRE_DEFINE_OBJECT(transaction, map_transaction);
|
||||
WIRE_DEFINE_OBJECT(keypair, map_keypair);
|
||||
void read_bytes(wire::reader& source, account& dest)
|
||||
{
|
||||
map_account(source, dest);
|
||||
dest.address = cryptonote::get_account_address_as_str(
|
||||
convert_net_type(dest.type), false, cryptonote::account_public_address{dest.spend.pub, dest.view.pub}
|
||||
);
|
||||
}
|
||||
void write_bytes(wire::writer& dest, const account& source)
|
||||
{ map_account(dest, source); }
|
||||
|
||||
transfer_in::transfer_in()
|
||||
: global_index(0),
|
||||
amount(0),
|
||||
recipient(),
|
||||
index(0),
|
||||
output_pub{},
|
||||
rct_mask{}
|
||||
{}
|
||||
|
||||
transfer_in::transfer_in(const rpc::output& source)
|
||||
: global_index(std::uint64_t(source.global_index)),
|
||||
amount(std::uint64_t(source.amount)),
|
||||
recipient(source.recipient.value_or(rpc::address_meta{})),
|
||||
index(source.index),
|
||||
output_pub(source.public_key),
|
||||
rct_mask(source.rct.mask)
|
||||
{}
|
||||
|
||||
wallet::wallet(const bool generated_locally)
|
||||
: client(),
|
||||
primary{},
|
||||
status(),
|
||||
last_sync(0),
|
||||
scan_height(0),
|
||||
blockchain_height(0),
|
||||
sync(),
|
||||
generated_locally(generated_locally)
|
||||
{}
|
||||
|
||||
expect<epee::byte_slice> wallet::to_bytes() const
|
||||
{
|
||||
epee::byte_stream dest{};
|
||||
dest.reserve(config::initial_buffer_size);
|
||||
const boost::lock_guard<boost::mutex> lock{sync};
|
||||
const std::error_code error = wire::msgpack::to_bytes(dest, primary);
|
||||
if (error)
|
||||
return error;
|
||||
return epee::byte_slice{std::move(dest)};
|
||||
}
|
||||
|
||||
expect<void> wallet::from_bytes(epee::byte_slice source)
|
||||
{
|
||||
const boost::lock_guard<boost::mutex> lock{sync};
|
||||
const std::error_code error = wire::msgpack::from_bytes(std::move(source), primary);
|
||||
if (error)
|
||||
return error;
|
||||
return success();
|
||||
}
|
||||
|
||||
void wallet::refresh(const bool mandatory)
|
||||
{
|
||||
if (!mandatory)
|
||||
{
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
const std::chrono::steady_clock::time_point start{
|
||||
std::chrono::steady_clock::time_point::duration{last_sync.load()}
|
||||
};
|
||||
if (now - start < config::refresh_interval)
|
||||
return;
|
||||
}
|
||||
|
||||
expect<rpc::get_unspent_outs> outs_response{common_error::kInvalidArgument};
|
||||
const rpc::login login{primary.address, primary.view.sec};
|
||||
const auto txs_response = rpc::invoke<rpc::get_address_txs>(client, login);
|
||||
if (txs_response)
|
||||
outs_response = rpc::invoke<rpc::get_unspent_outs>(client, login);
|
||||
|
||||
const boost::lock_guard<boost::mutex> lock{sync};
|
||||
if (!txs_response)
|
||||
{
|
||||
status = txs_response.error();
|
||||
return;
|
||||
}
|
||||
if (!outs_response)
|
||||
{
|
||||
status = outs_response.error();
|
||||
return;
|
||||
}
|
||||
|
||||
status = std::error_code{};
|
||||
merge_response(*this, *txs_response);
|
||||
merge_response(primary, *outs_response);
|
||||
last_sync = std::chrono::steady_clock::now().time_since_epoch().count();
|
||||
}
|
||||
}}} // lwsf // internal // backend
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
// Copyright (c) 2024, 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 <atomic>
|
||||
#include <boost/container/flat_map.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "common/expect.h" // moneor/src
|
||||
#include "crypto/crypto.h" // monero/src
|
||||
#include "crypto/hash.h" // monero/src
|
||||
#include "lws_frontend.h"
|
||||
#include "net/http_client.h" // monero/contrib/epee/include
|
||||
#include "ringct/rctTypes.h" // monero/src
|
||||
#include "rpc.h"
|
||||
#include "wire/fwd.h"
|
||||
|
||||
namespace lwsf { namespace internal { namespace backend
|
||||
{
|
||||
struct memory
|
||||
{
|
||||
template<typename T>
|
||||
bool operator()(const T& lhs, const T& rhs) const noexcept
|
||||
{
|
||||
static_assert(std::is_pod<T>::value);
|
||||
return std::memcmp(std::addressof(lhs), std::addressof(rhs), sizeof(T)) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct transfer_out
|
||||
{
|
||||
std::string address;
|
||||
std::uint64_t amount;
|
||||
rpc::address_meta sender;
|
||||
std::optional<crypto::secret_key> secret;
|
||||
crypto::public_key tx_pub;
|
||||
|
||||
transfer_out()
|
||||
: address(), amount(0), sender(), secret(), tx_pub{}
|
||||
{}
|
||||
|
||||
explicit transfer_out(const std::uint64_t amount, const crypto::public_key& tx_pub)
|
||||
: address(), amount(amount), sender(), secret(), tx_pub(tx_pub)
|
||||
{}
|
||||
};
|
||||
WIRE_DECLARE_OBJECT(transfer_out);
|
||||
|
||||
struct transfer_in
|
||||
{
|
||||
std::uint64_t global_index;
|
||||
std::uint64_t amount;
|
||||
rpc::address_meta recipient;
|
||||
std::uint16_t index;
|
||||
crypto::public_key output_pub;
|
||||
rct::key rct_mask;
|
||||
|
||||
transfer_in();
|
||||
explicit transfer_in(const rpc::output& source);
|
||||
};
|
||||
WIRE_DECLARE_OBJECT(transfer_in);
|
||||
|
||||
struct transaction
|
||||
{
|
||||
transaction() = delete;
|
||||
std::vector<transfer_out> spends;
|
||||
std::map<crypto::public_key, transfer_in, memory> receives;
|
||||
std::string description;
|
||||
std::string label;
|
||||
std::optional<std::time_t> timestamp;
|
||||
std::uint64_t amount;
|
||||
std::uint64_t fee;
|
||||
std::optional<std::uint64_t> height;
|
||||
std::uint64_t unlock_time;
|
||||
TransactionInfo::Direction direction;
|
||||
std::variant<std::nullptr_t, crypto::hash8, crypto::hash> payment_id;
|
||||
std::optional<crypto::secret_key> tx_secret;
|
||||
crypto::public_key pub;
|
||||
crypto::hash id;
|
||||
crypto::hash prefix;
|
||||
bool coinbase;
|
||||
};
|
||||
WIRE_DECLARE_OBJECT(transaction);
|
||||
|
||||
struct keypair
|
||||
{
|
||||
keypair() = delete;
|
||||
crypto::secret_key sec;
|
||||
crypto::public_key pub;
|
||||
};
|
||||
WIRE_DECLARE_OBJECT(keypair);
|
||||
|
||||
struct account
|
||||
{
|
||||
account() = delete;
|
||||
std::string address; //!> not serialized, recovered on read_bytes
|
||||
boost::container::flat_map<crypto::hash, std::shared_ptr<transaction>, memory> txes;
|
||||
std::uint64_t restore_height;
|
||||
NetworkType type;
|
||||
keypair view;
|
||||
keypair spend;
|
||||
};
|
||||
WIRE_DECLARE_OBJECT(account);
|
||||
|
||||
struct wallet
|
||||
{
|
||||
rpc::http_client client;
|
||||
account primary;
|
||||
std::error_code status;
|
||||
std::atomic<std::chrono::steady_clock::time_point::rep> last_sync;
|
||||
std::uint64_t scan_height;
|
||||
std::uint64_t blockchain_height;
|
||||
mutable boost::mutex sync;
|
||||
const bool generated_locally;
|
||||
|
||||
explicit wallet(bool generated_locally);
|
||||
|
||||
expect<epee::byte_slice> to_bytes() const;
|
||||
expect<void> from_bytes(epee::byte_slice source);
|
||||
|
||||
bool login() const;
|
||||
void refresh(bool mandatory = false);
|
||||
};
|
||||
}}} // lwsf // internal // backend
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
int main()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2024, 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
|
||||
|
||||
namespace lwsf { namespace config
|
||||
{
|
||||
constexpr const std::chrono::seconds connect_timeout{2};
|
||||
constexpr const std::chrono::seconds daemon_status_cache{10};
|
||||
constexpr const std::size_t initial_buffer_size = 1024 * 64; // 64 KiB
|
||||
constexpr const std::chrono::seconds refresh_interval{30};
|
||||
constexpr const std::chrono::seconds rpc_timeout{5};
|
||||
constexpr const std::size_t max_txes_in_rpc = 2048;
|
||||
}} // lwsf // config
|
||||
+237
@@ -0,0 +1,237 @@
|
||||
// Copyright (c) 2024, 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 "rpc.h"
|
||||
|
||||
#include <boost/spirit/include/qi_char.hpp>
|
||||
#include <boost/spirit/include/qi_uint.hpp>
|
||||
#include <boost/spirit/include/qi_parse_attr.hpp>
|
||||
#include <boost/spirit/include/qi_sequence.hpp>
|
||||
#include <ctime>
|
||||
#include "lwsf_config.h"
|
||||
#include "net/http_client.h" // monero/contrib/epee/include
|
||||
#include "wire.h"
|
||||
#include "wire/adapted/crypto.h"
|
||||
#include "wire/field.h"
|
||||
#include "wire/json.h"
|
||||
#include "wire/traits.h"
|
||||
#include "wire/wrapper/array.h"
|
||||
#include "wire/wrappers_impl.h"
|
||||
|
||||
namespace lwsf { namespace internal { namespace rpc
|
||||
{
|
||||
//! \return Error message string.
|
||||
const char* get_string(error value) noexcept
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
default:
|
||||
break;
|
||||
|
||||
case error::none:
|
||||
return "No rpc errors";
|
||||
case error::invoke_failure:
|
||||
return "HTTP invoke failed";
|
||||
case error::not_connected:
|
||||
return "No connection to HTTP server";
|
||||
case error::wrong_response_code:
|
||||
return "Expected HTTP 200 OK";
|
||||
}
|
||||
return "Unknown rpc error";
|
||||
}
|
||||
|
||||
//! \return Category for `error`.
|
||||
const std::error_category& error_category() noexcept
|
||||
{
|
||||
struct category final : std::error_category
|
||||
{
|
||||
virtual const char* name() const noexcept override final
|
||||
{
|
||||
return "lwsf::internal::rpc::error_category()";
|
||||
}
|
||||
|
||||
virtual std::string message(int value) const override final
|
||||
{
|
||||
return get_string(error(value));
|
||||
}
|
||||
};
|
||||
static const category instance{};
|
||||
return instance;
|
||||
}
|
||||
|
||||
expect<std::string> invoke_payload(http_client& client, const boost::string_ref endpoint, const epee::byte_slice payload)
|
||||
{
|
||||
static const epee::net_utils::http::fields_list headers{
|
||||
{"Content-Type", "application/json; charset=utf-8"}
|
||||
};
|
||||
|
||||
const epee::net_utils::http::http_response_info* response = nullptr;
|
||||
if (!client.invoke(endpoint, "POST", {reinterpret_cast<const char*>(payload.data()), payload.size()}, config::rpc_timeout, std::addressof(response), headers))
|
||||
return {error::invoke_failure};
|
||||
if (!response)
|
||||
return {error::invoke_failure};
|
||||
if(response->m_response_code != 200)
|
||||
return {error::wrong_response_code};
|
||||
|
||||
return response->m_body;
|
||||
}
|
||||
|
||||
|
||||
void write_bytes(wire::json_writer& dest, const login& self)
|
||||
{
|
||||
wire::object(dest, WIRE_FIELD(address), WIRE_FIELD(view_key));
|
||||
}
|
||||
|
||||
void write_bytes(wire::json_writer& dest, const login_request& self)
|
||||
{
|
||||
wire::object(dest,
|
||||
WIRE_FIELD(address),
|
||||
WIRE_FIELD(view_key),
|
||||
WIRE_FIELD(create_account),
|
||||
WIRE_FIELD(generated_locally)
|
||||
);
|
||||
}
|
||||
|
||||
void read_bytes(wire::json_reader& source, uint64_string& dest)
|
||||
{
|
||||
dest = uint64_string(wire::integer::cast_unsigned<std::uint64_t>(source.safe_unsigned_integer()));
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
template<typename F, typename T>
|
||||
void map_address_meta(F& format, T& self)
|
||||
{
|
||||
wire::object(format, WIRE_FIELD(maj_i), WIRE_FIELD(min_i));
|
||||
}
|
||||
}
|
||||
|
||||
WIRE_DEFINE_OBJECT(address_meta, map_address_meta);
|
||||
|
||||
void read_bytes(wire::json_reader& source, transaction_spend& self)
|
||||
{
|
||||
wire::object(source,
|
||||
WIRE_FIELD(amount),
|
||||
WIRE_OPTIONAL_FIELD(sender),
|
||||
WIRE_FIELD(out_index),
|
||||
WIRE_FIELD(key_image),
|
||||
WIRE_FIELD(tx_pub_key)
|
||||
);
|
||||
}
|
||||
|
||||
void read_bytes(wire::json_reader& source, transaction& self)
|
||||
{
|
||||
using min_spent = wire::min_element_sizeof<crypto::key_image>;
|
||||
|
||||
std::optional<std::string> timestamp;
|
||||
std::optional<std::vector<std::uint8_t>> payment_id;
|
||||
wire::object(source,
|
||||
WIRE_FIELD_ARRAY(spent_outputs, min_spent),
|
||||
wire::optional_field("payment_id", std::ref(payment_id)),
|
||||
wire::optional_field("timestamp", std::ref(timestamp)),
|
||||
WIRE_OPTIONAL_FIELD(fee),
|
||||
WIRE_FIELD(total_received),
|
||||
WIRE_FIELD(unlock_time),
|
||||
WIRE_OPTIONAL_FIELD(height),
|
||||
WIRE_FIELD(tx_hash),
|
||||
WIRE_FIELD(is_coinbase)
|
||||
);
|
||||
|
||||
|
||||
if (payment_id && !payment_id->empty())
|
||||
{
|
||||
if (payment_id->size() == sizeof(crypto::hash8))
|
||||
{
|
||||
self.payment_id = crypto::hash8{};
|
||||
std::memcpy(std::addressof(std::get<crypto::hash8>(self.payment_id)), payment_id->data(), sizeof(crypto::hash8));
|
||||
}
|
||||
else if (payment_id->size() == sizeof(crypto::hash))
|
||||
{
|
||||
self.payment_id = crypto::hash{};
|
||||
std::memcpy(std::addressof(std::get<crypto::hash>(self.payment_id)), payment_id->data(), sizeof(crypto::hash));
|
||||
}
|
||||
else
|
||||
WIRE_DLOG_THROW(wire::error::schema::fixed_binary, "Invalid payment_id size");
|
||||
}
|
||||
else
|
||||
self.payment_id = nullptr;
|
||||
|
||||
self.timestamp.reset();
|
||||
|
||||
if (timestamp)
|
||||
{
|
||||
// %Y-%m-%dT%H:%M:%SZ
|
||||
namespace qi = boost::spirit::qi;
|
||||
std::tm fields{};
|
||||
if (!qi::parse(timestamp->begin(), timestamp->end(), qi::ushort_ >> '-' >> qi::ushort_ >> '-' >> qi::ushort_ >> 'T' >> qi::ushort_ >> ':' >> qi::ushort_ >> qi::ushort_ >> 'Z', fields.tm_year, fields.tm_mon, fields.tm_mday, fields.tm_hour, fields.tm_min, fields.tm_sec))
|
||||
WIRE_DLOG_THROW(wire::error::schema::string, "Timestamp string invalid format");
|
||||
if ((self.timestamp = std::mktime(std::addressof(fields))) == -1)
|
||||
WIRE_DLOG_THROW(wire::error::schema::string, "Invalid timestamp value");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void read_bytes(wire::json_reader& source, get_address_txs& self)
|
||||
{
|
||||
using max_transactions = wire::max_element_count<config::max_txes_in_rpc>;
|
||||
wire::object(source,
|
||||
WIRE_FIELD_ARRAY(transactions, max_transactions),
|
||||
WIRE_FIELD(scanned_block_height),
|
||||
WIRE_FIELD(start_height),
|
||||
WIRE_FIELD(blockchain_height)
|
||||
);
|
||||
}
|
||||
|
||||
void read_bytes(wire::json_reader& source, output& self)
|
||||
{
|
||||
wire::object(source,
|
||||
WIRE_FIELD(amount),
|
||||
WIRE_FIELD(index),
|
||||
WIRE_FIELD(global_index),
|
||||
WIRE_OPTIONAL_FIELD(recipient),
|
||||
WIRE_FIELD(rct),
|
||||
WIRE_FIELD(tx_hash),
|
||||
WIRE_FIELD(tx_prefix_hash),
|
||||
WIRE_FIELD(public_key),
|
||||
WIRE_FIELD(tx_pub_key)
|
||||
);
|
||||
}
|
||||
|
||||
void read_bytes(wire::json_reader& source, get_unspent_outs& self)
|
||||
{
|
||||
using min_output_size = wire::min_element_sizeof<
|
||||
ringct, crypto::hash, crypto::hash, crypto::public_key, crypto::public_key
|
||||
>;
|
||||
wire::object(source,
|
||||
WIRE_FIELD_ARRAY(outputs, min_output_size),
|
||||
WIRE_FIELD(per_byte_fee),
|
||||
WIRE_FIELD(fee_mask)
|
||||
);
|
||||
}
|
||||
}}} // lwsf // internal // rpc
|
||||
|
||||
@@ -0,0 +1,267 @@
|
||||
// Copyright (c) 2024, 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/variant.hpp>
|
||||
#include <ctime>
|
||||
#include <system_error>
|
||||
#include "byte_slice.h" // monero/contrib/epee/include
|
||||
#include "byte_stream.h" // moneor/contrib/epee/include
|
||||
#include "common/expect.h" // monero/src
|
||||
#include "crypto/crypto.h" // monero/src
|
||||
#include "ringct/rctTypes.h" // monero/src
|
||||
#include "wire/fwd.h"
|
||||
#include "wire/json.h"
|
||||
|
||||
// Contains code for interact
|
||||
|
||||
namespace epee { namespace net_utils
|
||||
{
|
||||
class blocked_mode_client;
|
||||
namespace http { template<typename> class http_simple_client_template; }
|
||||
}}
|
||||
|
||||
namespace lwsf { namespace internal { namespace rpc
|
||||
{
|
||||
using http_client = epee::net_utils::http::http_simple_client_template<
|
||||
epee::net_utils::blocked_mode_client
|
||||
>;
|
||||
|
||||
enum class error : int
|
||||
{
|
||||
none = 0, invoke_failure, not_connected, wrong_response_code
|
||||
};
|
||||
|
||||
const char* get_string(error value) noexcept;
|
||||
const std::error_category& error_category() noexcept;
|
||||
inline std::error_code make_error_code(const error value) noexcept
|
||||
{
|
||||
return std::error_code{int(value), error_category()};
|
||||
}
|
||||
|
||||
|
||||
//! Send `payload` to `client` at uri `endpoint`, and \return payload response
|
||||
expect<std::string> invoke_payload(http_client& client, boost::string_ref endpoint, epee::byte_slice payload);
|
||||
|
||||
template<typename F, typename G>
|
||||
expect<F> invoke(http_client& client, const G& in)
|
||||
{
|
||||
epee::byte_stream sink{};
|
||||
std::error_code error = wire::json::to_bytes(sink, in);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
expect<std::string> result = invoke_payload(client, F::endpoint(), epee::byte_slice{std::move(sink)});
|
||||
if (!result)
|
||||
return result.error();
|
||||
|
||||
F out{};
|
||||
error = wire::json::from_bytes(std::move(*result), out);
|
||||
if (error)
|
||||
return out;
|
||||
return out;
|
||||
}
|
||||
|
||||
struct empty
|
||||
{
|
||||
empty() = delete;
|
||||
};
|
||||
inline void write_bytes(const wire::json_writer&, const empty&)
|
||||
{}
|
||||
|
||||
struct login
|
||||
{
|
||||
login() = delete;
|
||||
|
||||
std::string address;
|
||||
crypto::secret_key view_key;
|
||||
};
|
||||
void write_bytes(wire::json_writer&, const login&);
|
||||
|
||||
|
||||
struct login_request
|
||||
{
|
||||
login_request() = delete;
|
||||
|
||||
std::string address;
|
||||
crypto::secret_key view_key;
|
||||
bool create_account;
|
||||
bool generated_locally;
|
||||
};
|
||||
void write_bytes(wire::json_writer&, const login_request&);
|
||||
|
||||
struct login_response
|
||||
{
|
||||
login_response() = delete;
|
||||
static constexpr const char* endpoint() noexcept { return "/login"; }
|
||||
|
||||
std::optional<bool> generated_locally;
|
||||
};
|
||||
inline void read_bytes(const wire::json_reader&, const login_response&)
|
||||
{}
|
||||
|
||||
struct daemon_status
|
||||
{
|
||||
daemon_status() = delete;
|
||||
static constexpr const char* endpoint() noexcept { return "/daemon_status"; }
|
||||
std::uint64_t outgoing_connections_count;
|
||||
std::uint64_t incoming_connections_count;
|
||||
std::uint64_t height;
|
||||
std::uint64_t target_height;
|
||||
};
|
||||
void read_bytes(wire::json_reader&, daemon_status&);
|
||||
|
||||
|
||||
enum class uint64_string : std::uint64_t {};
|
||||
void read_bytes(wire::json_reader&, uint64_string&);
|
||||
|
||||
struct address_meta
|
||||
{
|
||||
std::uint32_t maj_i;
|
||||
std::uint32_t min_i;
|
||||
|
||||
address_meta() noexcept
|
||||
: maj_i(0), min_i(0)
|
||||
{}
|
||||
};
|
||||
WIRE_DECLARE_OBJECT(address_meta);
|
||||
|
||||
struct transaction_spend
|
||||
{
|
||||
uint64_string amount;
|
||||
std::optional<address_meta> sender;
|
||||
std::uint16_t out_index;
|
||||
crypto::key_image key_image;
|
||||
crypto::public_key tx_pub_key;
|
||||
|
||||
transaction_spend() noexcept
|
||||
: amount(uint64_string(0)),
|
||||
sender(),
|
||||
out_index(0),
|
||||
key_image{},
|
||||
tx_pub_key{}
|
||||
{}
|
||||
};
|
||||
void read_bytes(wire::json_reader&, transaction_spend&);
|
||||
|
||||
struct transaction
|
||||
{
|
||||
std::vector<transaction_spend> spent_outputs;
|
||||
std::variant<std::nullptr_t, crypto::hash8, crypto::hash> payment_id;
|
||||
std::optional<std::time_t> timestamp;
|
||||
std::optional<uint64_string> fee;
|
||||
uint64_string total_received;
|
||||
std::uint64_t unlock_time;
|
||||
std::optional<std::uint64_t> height;
|
||||
crypto::hash tx_hash;
|
||||
bool is_coinbase;
|
||||
|
||||
transaction()
|
||||
: spent_outputs(),
|
||||
payment_id(),
|
||||
timestamp(),
|
||||
fee(),
|
||||
total_received(uint64_string(0)),
|
||||
unlock_time(0),
|
||||
height(),
|
||||
tx_hash{},
|
||||
is_coinbase(false)
|
||||
{}
|
||||
};
|
||||
void read_bytes(wire::json_reader&, transaction&);
|
||||
|
||||
struct get_address_txs
|
||||
{
|
||||
get_address_txs() = delete;
|
||||
static constexpr const char* endpoint() noexcept { return "/get_address_txs"; }
|
||||
|
||||
std::vector<transaction> transactions;
|
||||
std::uint64_t scanned_block_height;
|
||||
std::uint64_t start_height;
|
||||
std::uint64_t blockchain_height;
|
||||
};
|
||||
void read_bytes(wire::json_reader&, get_address_txs&);
|
||||
|
||||
|
||||
struct ringct
|
||||
{
|
||||
ringct() = delete;
|
||||
rct::key commitment;
|
||||
rct::key mask;
|
||||
rct::key amount;
|
||||
};
|
||||
static_assert(sizeof(ringct) == 96);
|
||||
|
||||
struct output
|
||||
{
|
||||
uint64_string amount;
|
||||
uint64_string global_index;
|
||||
std::optional<address_meta> recipient;
|
||||
std::uint16_t index;
|
||||
ringct rct;
|
||||
crypto::hash tx_hash;
|
||||
crypto::hash tx_prefix_hash;
|
||||
crypto::public_key public_key;
|
||||
crypto::public_key tx_pub_key;
|
||||
|
||||
output() noexcept
|
||||
: amount(uint64_string(0)),
|
||||
global_index(uint64_string(0)),
|
||||
recipient(),
|
||||
index(0),
|
||||
rct{},
|
||||
tx_hash{},
|
||||
tx_prefix_hash{},
|
||||
public_key{},
|
||||
tx_pub_key{}
|
||||
{}
|
||||
};
|
||||
void read_bytes(wire::json_reader&, output&);
|
||||
|
||||
struct get_unspent_outs
|
||||
{
|
||||
get_unspent_outs() = delete;
|
||||
static constexpr const char* endpoint() noexcept { return "/get_unspent_outs"; }
|
||||
|
||||
std::vector<output> outputs;
|
||||
std::uint64_t per_byte_fee;
|
||||
std::uint64_t fee_mask;
|
||||
};
|
||||
void read_bytes(wire::json_reader&, get_unspent_outs&);
|
||||
}}} // lwsf // internal // rpc
|
||||
|
||||
WIRE_DECLARE_BLOB_NS(lwsf::internal::rpc::ringct);
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<>
|
||||
struct is_error_code_enum<lwsf::internal::rpc::error>
|
||||
: true_type
|
||||
{};
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
// Copyright (c) 2024, 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 "transaction_history.h"
|
||||
|
||||
#include <boost/thread/lock_guard.hpp>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "transaction_info.h"
|
||||
|
||||
#include "string_tools.h" // monero/contrib/epee/include
|
||||
|
||||
namespace lwsf { namespace internal
|
||||
{
|
||||
transaction_history::transaction_history(std::shared_ptr<backend::wallet> data)
|
||||
: data_(std::move(data))
|
||||
{
|
||||
if (!data_)
|
||||
throw std::logic_error{"transaction_history was given nullptr"};
|
||||
}
|
||||
|
||||
transaction_history::~transaction_history()
|
||||
{}
|
||||
|
||||
int transaction_history::count() const
|
||||
{
|
||||
const boost::lock_guard<boost::mutex> lock{data_->sync};
|
||||
if (std::numeric_limits<int>::max() < data_->primary.txes.size())
|
||||
throw std::runtime_error{"Exceeded max int size in transaction_history::count"};
|
||||
return data_->primary.txes.size();
|
||||
}
|
||||
|
||||
std::shared_ptr<TransactionInfo> transaction_history::transaction(int index) const
|
||||
{
|
||||
const boost::lock_guard<boost::mutex> lock{data_->sync};
|
||||
if (std::numeric_limits<int>::max() < data_->primary.txes.size())
|
||||
throw std::runtime_error{"Exceeded max int size in transaction_history:transaction"};
|
||||
if (data_->primary.txes.size() < index || index < 0)
|
||||
throw std::runtime_error{"index provided to transaction invalid"};
|
||||
return std::make_shared<transaction_info>(data_, data_->primary.txes.nth(index)->second);
|
||||
}
|
||||
|
||||
std::shared_ptr<TransactionInfo> transaction_history::transaction(const std::string &id) const
|
||||
{
|
||||
crypto::hash binary_id{};
|
||||
if (!epee::string_tools::hex_to_pod(id, binary_id))
|
||||
throw std::runtime_error{"transaction_history given invalid hex id"};
|
||||
|
||||
const boost::lock_guard<boost::mutex> lock{data_->sync};
|
||||
const auto iter = data_->primary.txes.find(binary_id);
|
||||
if (iter != data_->primary.txes.end())
|
||||
return std::make_shared<transaction_info>(data_, iter->second);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<TransactionInfo>> transaction_history::getAll() const
|
||||
{
|
||||
std::vector<std::shared_ptr<TransactionInfo>> out;
|
||||
const boost::lock_guard<boost::mutex> lock{data_->sync};
|
||||
out.reserve(data_->primary.txes.size());
|
||||
for (const auto& tx : data_->primary.txes)
|
||||
out.push_back(std::make_shared<transaction_info>(data_, tx.second));
|
||||
return out;
|
||||
}
|
||||
|
||||
void transaction_history::refresh()
|
||||
{
|
||||
data_->refresh(true);
|
||||
}
|
||||
|
||||
void transaction_history::setTxNote(const std::string &txid, const std::string ¬e)
|
||||
{
|
||||
crypto::hash binary_id{};
|
||||
if (!epee::string_tools::hex_to_pod(txid, binary_id))
|
||||
throw std::runtime_error{"transaction_history given invalid hex id"};
|
||||
|
||||
const boost::lock_guard<boost::mutex> lock{data_->sync};
|
||||
auto iter = data_->primary.txes.find(binary_id);
|
||||
if (iter == data_->primary.txes.end())
|
||||
throw std::runtime_error{"transaction_history setTxNote given invalid id"};
|
||||
|
||||
iter->second->description = note;
|
||||
}
|
||||
}} // lwsf // internal
|
||||
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2024, 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 "backend.h"
|
||||
#include "lws_frontend.h"
|
||||
|
||||
namespace lwsf { namespace internal
|
||||
{
|
||||
class transaction_history : public lwsf::TransactionHistory
|
||||
{
|
||||
const std::shared_ptr<backend::wallet> data_;
|
||||
|
||||
public:
|
||||
explicit transaction_history(std::shared_ptr<backend::wallet> data);
|
||||
|
||||
transaction_history(const transaction_history&) = delete;
|
||||
transaction_history(transaction_history&&) = delete;
|
||||
virtual ~transaction_history() override;
|
||||
transaction_history& operator=(const transaction_history&) = delete;
|
||||
transaction_history& operator=(transaction_history&&) = delete;
|
||||
|
||||
virtual int count() const override;
|
||||
virtual std::shared_ptr<TransactionInfo> transaction(int index) const override;
|
||||
virtual std::shared_ptr<TransactionInfo> transaction(const std::string &id) const override;
|
||||
virtual std::vector<std::shared_ptr<TransactionInfo>> getAll() const override;
|
||||
virtual void refresh() override;
|
||||
virtual void setTxNote(const std::string &txid, const std::string ¬e) override;
|
||||
};
|
||||
}} // lwsf // internal
|
||||
@@ -0,0 +1,79 @@
|
||||
// Copyright (c) 2024, 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 "transaction_info.h"
|
||||
|
||||
#include "hex.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
struct payment_id_to_hex
|
||||
{
|
||||
template<typename T>
|
||||
std::string operator()(const T& source) const
|
||||
{
|
||||
return epee::to_hex::string(epee::as_byte_span(source));
|
||||
}
|
||||
|
||||
std::string operator()(const std::nullptr_t) const { return {}; }
|
||||
};
|
||||
}
|
||||
|
||||
namespace lwsf { namespace internal
|
||||
{
|
||||
transaction_info::transaction_info(std::shared_ptr<backend::wallet> wallet, std::shared_ptr<backend::transaction> data)
|
||||
: wallet_(std::move(wallet)), data_(std::move(data)), transfers_()
|
||||
{
|
||||
if (!wallet_ || !data_)
|
||||
throw std::logic_error{"internal::transaction_info cannot be given nullptr"};
|
||||
|
||||
transfers_.reserve(data_->spends.size());
|
||||
for (const auto& spend : data_->spends)
|
||||
transfers_.emplace_back(spend.amount, spend.address);
|
||||
}
|
||||
|
||||
transaction_info::~transaction_info()
|
||||
{}
|
||||
|
||||
uint64_t transaction_info::confirmations() const
|
||||
{
|
||||
const auto block_height = blockHeight();
|
||||
const boost::lock_guard<boost::mutex> lock{wallet_->sync};
|
||||
return std::max(wallet_->blockchain_height, block_height) - block_height;
|
||||
}
|
||||
|
||||
std::string transaction_info::hash() const
|
||||
{
|
||||
return epee::to_hex::string(epee::as_byte_span(data_->id));
|
||||
}
|
||||
|
||||
std::string transaction_info::paymentId() const
|
||||
{
|
||||
return std::visit(payment_id_to_hex{}, data_->payment_id);
|
||||
}
|
||||
}} // lwsf // internal
|
||||
@@ -0,0 +1,75 @@
|
||||
// Copyright (c) 2024, 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 "lws_frontend.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "backend.h"
|
||||
|
||||
namespace lwsf { namespace internal
|
||||
{
|
||||
//! \todo isFailed, subaddressIndex, and subaddrAccount
|
||||
class transaction_info final : public lwsf::TransactionInfo
|
||||
{
|
||||
const std::shared_ptr<backend::wallet> wallet_;
|
||||
const std::shared_ptr<backend::transaction> data_;
|
||||
std::vector<Transfer> transfers_;
|
||||
|
||||
public:
|
||||
explicit transaction_info(std::shared_ptr<backend::wallet> wallet, std::shared_ptr<backend::transaction> data);
|
||||
|
||||
transaction_info(const transaction_info&) = delete;
|
||||
transaction_info(transaction_info&&) = delete;
|
||||
virtual ~transaction_info() override;
|
||||
transaction_info& operator=(const transaction_info&) = delete;
|
||||
transaction_info& operator=(transaction_info&&) = delete;
|
||||
|
||||
virtual Direction direction() const override { return data_->direction; }
|
||||
virtual bool isPending() const override { return confirmations() == 0; }
|
||||
virtual bool isFailed() const override { return false; }
|
||||
virtual bool isCoinbase() const override { return data_->coinbase; }
|
||||
virtual uint64_t amount() const override { return data_->amount; }
|
||||
virtual uint64_t fee() const override { return data_->fee; }
|
||||
virtual uint64_t blockHeight() const override { return data_->height.value_or(0); }
|
||||
virtual std::string description() const override { return data_->description; }
|
||||
virtual std::set<uint32_t> subaddrIndex() const override { return {}; };
|
||||
virtual uint32_t subaddrAccount() const override { return 0; }
|
||||
virtual std::string label() const override { return data_->label; }
|
||||
virtual uint64_t confirmations() const override;
|
||||
virtual uint64_t unlockTime() const override { return data_->unlock_time; }
|
||||
virtual std::string hash() const override;
|
||||
virtual std::time_t timestamp() const override { return data_->timestamp.value_or(std::time_t(0)); }
|
||||
virtual std::string paymentId() const override;
|
||||
virtual const std::vector<Transfer>& transfers() const override { return transfers_; }
|
||||
};
|
||||
}} // lwsf // internal
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
// Copyright (c) 2014-2024, 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 "wallet.h"
|
||||
|
||||
#include <fstream>
|
||||
#include "backend.h"
|
||||
#include "hex.h" // monero/contrib/epee/include
|
||||
#include "lwsf_config.h"
|
||||
#include "net/parse.h" // monero/src
|
||||
#include "net/socks_connect.h" // monero/src
|
||||
#include "transaction_history.h"
|
||||
|
||||
namespace lwsf { namespace internal
|
||||
{
|
||||
namespace
|
||||
{
|
||||
struct null_connector
|
||||
{
|
||||
boost::unique_future<boost::asio::ip::tcp::socket>
|
||||
operator()(const std::string&, const std::string&, boost::asio::steady_timer&) const
|
||||
{
|
||||
throw std::runtime_error{"Unable to connect"};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
wallet::wallet(std::string filename, std::shared_ptr<backend::wallet> data)
|
||||
: data_(std::move(data)), filename_(std::move(filename)), thread_()
|
||||
{
|
||||
if (!data_)
|
||||
throw std::logic_error{"Unepected nullptr in internal::wallet"};
|
||||
}
|
||||
|
||||
wallet::~wallet()
|
||||
{}
|
||||
|
||||
void wallet::statusWithErrorString(int& status, std::string& errorString) const
|
||||
{
|
||||
const boost::unique_lock<boost::mutex> lock{data_->sync};
|
||||
status = data_->status.value();
|
||||
if (status)
|
||||
errorString = data_->status.message();
|
||||
else
|
||||
errorString.clear();
|
||||
}
|
||||
|
||||
NetworkType wallet::nettype() const { data_->primary.type; }
|
||||
|
||||
std::string wallet::secretViewKey() const
|
||||
{
|
||||
return epee::to_hex::string(epee::as_byte_span(unwrap(unwrap(data_->primary.view.sec))));
|
||||
}
|
||||
|
||||
std::string wallet::publicViewKey() const
|
||||
{
|
||||
return epee::to_hex::string(epee::as_byte_span(data_->primary.view.pub));
|
||||
}
|
||||
|
||||
std::string wallet::secretSpendKey() const
|
||||
{
|
||||
return epee::to_hex::string(epee::as_byte_span(unwrap(unwrap(data_->primary.spend.sec))));
|
||||
}
|
||||
|
||||
std::string wallet::publicSpendKey() const
|
||||
{
|
||||
return epee::to_hex::string(epee::as_byte_span(data_->primary.spend.pub));
|
||||
}
|
||||
|
||||
bool wallet::store(const std::string& path)
|
||||
{
|
||||
expect<epee::byte_slice> bytes = data_->to_bytes();
|
||||
if (!bytes)
|
||||
return false;
|
||||
|
||||
//! \TODO Write, fsync, then atomic swap filenames
|
||||
|
||||
std::ofstream file{path, std::ios::out | std::ios::binary};
|
||||
file.write(reinterpret_cast<const char*>(bytes->data()), bytes->size());
|
||||
file.flush();
|
||||
return file.good();
|
||||
}
|
||||
|
||||
bool wallet::init(const std::string &daemon_address, uint64_t, const std::string &daemon_username, const std::string &daemon_password, bool use_ssl, bool light_wallet, const std::string &proxy_address)
|
||||
{
|
||||
if (!light_wallet)
|
||||
throw std::runtime_error{"Only light_wallets are supported with this instance"};
|
||||
|
||||
const auto split = daemon_address.rfind(':');
|
||||
if (!setProxy(proxy_address))
|
||||
return false; // set_status already set
|
||||
|
||||
std::string host = daemon_address.substr(0, split);
|
||||
std::string port = use_ssl ? "443" : "80";
|
||||
if (split != std::string::npos)
|
||||
port = daemon_address.substr(split + 1);
|
||||
|
||||
epee::net_utils::http::login login{daemon_username, daemon_password};
|
||||
const epee::net_utils::ssl_options_t options{
|
||||
use_ssl ? epee::net_utils::ssl_support_t::e_ssl_support_disabled : epee::net_utils::ssl_support_t::e_ssl_support_enabled
|
||||
};
|
||||
|
||||
data_->client.set_server(std::move(host), std::move(port), std::move(login), std::move(options));
|
||||
|
||||
//! \TODO Login!
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
uint64_t wallet::getRefreshFromBlockHeight() const
|
||||
{
|
||||
const boost::lock_guard<boost::mutex> lock{data_->sync};
|
||||
return data_->primary.restore_height;
|
||||
}
|
||||
|
||||
bool wallet::connectToDaemon()
|
||||
{
|
||||
if (data_->client.is_connected())
|
||||
return true;
|
||||
return data_->client.connect(config::connect_timeout);
|
||||
}
|
||||
|
||||
Wallet::ConnectionStatus wallet::connected() const
|
||||
{
|
||||
return data_->client.is_connected() ?
|
||||
ConnectionStatus::Connected : ConnectionStatus::Disconnected;
|
||||
}
|
||||
|
||||
bool wallet::setProxy(const std::string &address)
|
||||
{
|
||||
auto endpoint = net::get_tcp_endpoint(address);
|
||||
if (!endpoint)
|
||||
{
|
||||
data_->client.set_connector(null_connector{});
|
||||
const boost::lock_guard<boost::mutex> lock{data_->sync};
|
||||
data_->status = endpoint.error();
|
||||
return false;
|
||||
}
|
||||
data_->client.set_connector(net::socks::connector{std::move(*endpoint)});
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t wallet::blockChainHeight() const
|
||||
{
|
||||
const boost::unique_lock<boost::mutex> lock{data_->sync};
|
||||
return data_->blockchain_height;
|
||||
}
|
||||
|
||||
uint64_t wallet::daemonBlockChainHeight() const
|
||||
{ return blockChainHeight(); }
|
||||
|
||||
uint64_t wallet::daemonBlockChainTargetHeight() const
|
||||
{ return blockChainHeight(); }
|
||||
|
||||
|
||||
std::shared_ptr<TransactionHistory> wallet::history()
|
||||
{
|
||||
return std::make_shared<transaction_history>(data_);
|
||||
}
|
||||
}} // lwsf // internal
|
||||
+705
@@ -0,0 +1,705 @@
|
||||
// Copyright (c) 2024, 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 "lws_frontend.h"
|
||||
|
||||
#include <boost/thread/thread.hpp>
|
||||
#include <string>
|
||||
#include "cryptonote_basic/account.h" // monero/src
|
||||
#include "net/http_client.h" // monero/contrib/epee/include
|
||||
|
||||
namespace lwsf
|
||||
{
|
||||
namespace internal
|
||||
{
|
||||
namespace backend { struct wallet; }
|
||||
|
||||
//! \TODO Mark final when completely implemented
|
||||
class wallet : public lwsf::Wallet
|
||||
{
|
||||
const std::shared_ptr<backend::wallet> data_;
|
||||
std::string filename_;
|
||||
boost::thread thread_;
|
||||
|
||||
public:
|
||||
explicit wallet(std::string filename, std::shared_ptr<backend::wallet> data);
|
||||
|
||||
wallet(const wallet&) = delete;
|
||||
wallet(wallet&&) = delete;
|
||||
virtual ~wallet() override;
|
||||
wallet& operator=(const wallet&) = delete;
|
||||
wallet& operator=(wallet&&) = delete;
|
||||
|
||||
virtual std::string seed(const std::string& seed_offset = "") const = 0;
|
||||
|
||||
virtual std::string getSeedLanguage() const = 0;
|
||||
virtual void setSeedLanguage(const std::string &arg) = 0;
|
||||
|
||||
virtual void statusWithErrorString(int& status, std::string& errorString) const override;
|
||||
|
||||
virtual bool setPassword(const std::string &password) = 0;
|
||||
virtual const std::string& getPassword() const = 0;
|
||||
|
||||
virtual bool setDevicePin(const std::string &) override { return false; };
|
||||
virtual bool setDevicePassphrase(const std::string &) override { return false; };
|
||||
virtual std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const override;
|
||||
std::string mainAddress() const { return address(0, 0); }
|
||||
virtual std::string path() const = 0;
|
||||
virtual NetworkType nettype() const override;
|
||||
bool mainnet() const { return nettype() == MAINNET; }
|
||||
bool testnet() const { return nettype() == TESTNET; }
|
||||
bool stagenet() const { return nettype() == STAGENET; }
|
||||
//! returns current hard fork info
|
||||
virtual void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const = 0;
|
||||
//! check if hard fork rules should be used
|
||||
virtual bool useForkRules(uint8_t version, int64_t early_blocks) const = 0;
|
||||
/*!
|
||||
* \brief integratedAddress - returns integrated address for current wallet address and given payment_id.
|
||||
* if passed "payment_id" param is an empty string or not-valid payment id string
|
||||
* (16 characters hexadecimal string) - random payment_id will be generated
|
||||
*
|
||||
* \param payment_id - 16 characters hexadecimal string or empty string if new random payment id needs to be
|
||||
* generated
|
||||
* \return - 106 characters string representing integrated address
|
||||
*/
|
||||
virtual std::string integratedAddress(const std::string &payment_id) const = 0;
|
||||
|
||||
/*!
|
||||
* \brief secretViewKey - returns secret view key
|
||||
* \return - secret view key
|
||||
*/
|
||||
virtual std::string secretViewKey() const override;
|
||||
|
||||
/*!
|
||||
* \brief publicViewKey - returns public view key
|
||||
* \return - public view key
|
||||
*/
|
||||
virtual std::string publicViewKey() const override;
|
||||
|
||||
/*!
|
||||
* \brief secretSpendKey - returns secret spend key
|
||||
* \return - secret spend key
|
||||
*/
|
||||
virtual std::string secretSpendKey() const override;
|
||||
|
||||
/*!
|
||||
* \brief publicSpendKey - returns public spend key
|
||||
* \return - public spend key
|
||||
*/
|
||||
virtual std::string publicSpendKey() const override;
|
||||
|
||||
/*!
|
||||
* \brief publicMultisigSignerKey - returns public signer key
|
||||
* \return - public multisignature signer key or empty string if wallet is not multisig
|
||||
*/
|
||||
virtual std::string publicMultisigSignerKey() const override
|
||||
{ throw std::logic_error{"lwsf does not support multisig"}; }
|
||||
|
||||
/*!
|
||||
* \brief stop - interrupts wallet refresh() loop once (doesn't stop background refresh thread)
|
||||
*/
|
||||
virtual void stop() = 0;
|
||||
|
||||
/*!
|
||||
* \brief store - stores wallet to file.
|
||||
* \param path - main filename to store wallet to. additionally stores address file and keys file.
|
||||
* to store to the same file - just pass empty string;
|
||||
* \return
|
||||
*/
|
||||
virtual bool store(const std::string &path) override;
|
||||
/*!
|
||||
* \brief filename - returns wallet filename
|
||||
* \return
|
||||
*/
|
||||
virtual std::string filename() const override { return filename_; }
|
||||
/*!
|
||||
* \brief keysFilename - returns keys filename. usually this formed as "wallet_filename".keys
|
||||
* \return
|
||||
*/
|
||||
virtual std::string keysFilename() const = 0;
|
||||
/*!
|
||||
* \brief init - initializes wallet with daemon connection params.
|
||||
* if daemon_address is local address, "trusted daemon" will be set to true forcibly
|
||||
* startRefresh() should be called when wallet is initialized.
|
||||
*
|
||||
* \param daemon_address - daemon address in "hostname:port" format
|
||||
* \param daemon_username
|
||||
* \param daemon_password
|
||||
* \param proxy_address - set proxy address, empty string to disable
|
||||
* \return - true on success
|
||||
*/
|
||||
virtual bool init(const std::string &daemon_address, uint64_t, const std::string &daemon_username = "", const std::string &daemon_password = "", bool use_ssl = false, bool lightWallet = false, const std::string &proxy_address = "") override;
|
||||
|
||||
/*!
|
||||
* \brief createWatchOnly - Creates a watch only wallet
|
||||
* \param path - where to store the wallet
|
||||
* \param password
|
||||
* \param language
|
||||
* \return - true if created successfully
|
||||
*/
|
||||
virtual bool createWatchOnly(const std::string &path, const std::string &password, const std::string &language) const = 0;
|
||||
|
||||
/*!
|
||||
* \brief setRefreshFromBlockHeight - start refresh from block height on recover
|
||||
*
|
||||
* \param refresh_from_block_height - blockchain start height
|
||||
*/
|
||||
virtual void setRefreshFromBlockHeight(uint64_t refresh_from_block_height) = 0;
|
||||
|
||||
/*!
|
||||
* \brief getRestoreHeight - get wallet creation height
|
||||
*
|
||||
*/
|
||||
virtual uint64_t getRefreshFromBlockHeight() const override;
|
||||
|
||||
/*!
|
||||
* \brief setRecoveringFromSeed - set state recover form seed
|
||||
*
|
||||
* \param recoveringFromSeed - true/false
|
||||
*/
|
||||
virtual void setRecoveringFromSeed(bool recoveringFromSeed) = 0;
|
||||
|
||||
/*!
|
||||
* \brief setRecoveringFromDevice - set state to recovering from device
|
||||
*
|
||||
* \param recoveringFromDevice - true/false
|
||||
*/
|
||||
virtual void setRecoveringFromDevice(bool recoveringFromDevice) = 0;
|
||||
|
||||
/*!
|
||||
* \brief setSubaddressLookahead - set size of subaddress lookahead
|
||||
*
|
||||
* \param major - size fot the major index
|
||||
* \param minor - size fot the minor index
|
||||
*/
|
||||
virtual void setSubaddressLookahead(uint32_t major, uint32_t minor) = 0;
|
||||
|
||||
/**
|
||||
* @brief connectToDaemon - connects to the daemon. TODO: check if it can be removed
|
||||
* @return
|
||||
*/
|
||||
virtual bool connectToDaemon() override;
|
||||
|
||||
/**
|
||||
* @brief connected - checks if the wallet connected to the daemon
|
||||
* @return - true if connected
|
||||
*/
|
||||
virtual ConnectionStatus connected() const override;
|
||||
virtual void setTrustedDaemon(bool arg) = 0;
|
||||
virtual bool trustedDaemon() const = 0;
|
||||
virtual bool setProxy(const std::string &address) override;
|
||||
virtual uint64_t balance(uint32_t accountIndex = 0) const = 0;
|
||||
uint64_t balanceAll() const {
|
||||
uint64_t result = 0;
|
||||
for (uint32_t i = 0; i < numSubaddressAccounts(); ++i)
|
||||
result += balance(i);
|
||||
return result;
|
||||
}
|
||||
virtual uint64_t unlockedBalance(uint32_t accountIndex = 0) const = 0;
|
||||
uint64_t unlockedBalanceAll() const {
|
||||
uint64_t result = 0;
|
||||
for (uint32_t i = 0; i < numSubaddressAccounts(); ++i)
|
||||
result += unlockedBalance(i);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief watchOnly - checks if wallet is watch only
|
||||
* @return - true if watch only
|
||||
*/
|
||||
virtual bool watchOnly() const = 0;
|
||||
|
||||
/**
|
||||
* @brief isDeterministic - checks if wallet keys are deterministic
|
||||
* @return - true if deterministic
|
||||
*/
|
||||
virtual bool isDeterministic() const = 0;
|
||||
|
||||
/**
|
||||
* @brief blockChainHeight - returns current blockchain height
|
||||
* @return
|
||||
*/
|
||||
virtual uint64_t blockChainHeight() const override;
|
||||
|
||||
/**
|
||||
* @brief approximateBlockChainHeight - returns approximate blockchain height calculated from date/time
|
||||
* @return
|
||||
*/
|
||||
virtual uint64_t approximateBlockChainHeight() const = 0;
|
||||
|
||||
/**
|
||||
* @brief estimateBlockChainHeight - returns estimate blockchain height. More accurate than approximateBlockChainHeight,
|
||||
* uses daemon height and falls back to calculation from date/time
|
||||
* @return
|
||||
**/
|
||||
virtual uint64_t estimateBlockChainHeight() const = 0;
|
||||
/**
|
||||
* @brief daemonBlockChainHeight - returns daemon blockchain height
|
||||
* @return 0 - in case error communicating with the daemon.
|
||||
* status() will return Status_Error and errorString() will return verbose error description
|
||||
*/
|
||||
virtual uint64_t daemonBlockChainHeight() const override;
|
||||
|
||||
/**
|
||||
* @brief daemonBlockChainTargetHeight - returns daemon blockchain target height
|
||||
* @return 0 - in case error communicating with the daemon.
|
||||
* status() will return Status_Error and errorString() will return verbose error description
|
||||
*/
|
||||
virtual uint64_t daemonBlockChainTargetHeight() const override;
|
||||
|
||||
/**
|
||||
* @brief synchronized - checks if wallet was ever synchronized
|
||||
* @return
|
||||
*/
|
||||
virtual bool synchronized() const = 0;
|
||||
|
||||
static std::string displayAmount(uint64_t amount);
|
||||
static uint64_t amountFromString(const std::string &amount);
|
||||
static uint64_t amountFromDouble(double amount);
|
||||
static std::string genPaymentId();
|
||||
static bool paymentIdValid(const std::string &paiment_id);
|
||||
static bool addressValid(const std::string &str, NetworkType nettype);
|
||||
static bool addressValid(const std::string &str, bool testnet) // deprecated
|
||||
{
|
||||
return addressValid(str, testnet ? TESTNET : MAINNET);
|
||||
}
|
||||
static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, NetworkType nettype, std::string &error);
|
||||
static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, bool testnet, std::string &error) // deprecated
|
||||
{
|
||||
return keyValid(secret_key_string, address_string, isViewKey, testnet ? TESTNET : MAINNET, error);
|
||||
}
|
||||
static std::string paymentIdFromAddress(const std::string &str, NetworkType nettype);
|
||||
static std::string paymentIdFromAddress(const std::string &str, bool testnet) // deprecated
|
||||
{
|
||||
return paymentIdFromAddress(str, testnet ? TESTNET : MAINNET);
|
||||
}
|
||||
static uint64_t maximumAllowedAmount();
|
||||
// Easylogger wrapper
|
||||
static void init(const char *argv0, const char *default_log_base_name) { init(argv0, default_log_base_name, "", true); }
|
||||
static void init(const char *argv0, const char *default_log_base_name, const std::string &log_path, bool console);
|
||||
static void debug(const std::string &category, const std::string &str);
|
||||
static void info(const std::string &category, const std::string &str);
|
||||
static void warning(const std::string &category, const std::string &str);
|
||||
static void error(const std::string &category, const std::string &str);
|
||||
|
||||
/**
|
||||
* @brief StartRefresh - Start/resume refresh thread (refresh every 10 seconds)
|
||||
*/
|
||||
virtual void startRefresh() = 0;
|
||||
/**
|
||||
* @brief pauseRefresh - pause refresh thread
|
||||
*/
|
||||
virtual void pauseRefresh() = 0;
|
||||
|
||||
/**
|
||||
* @brief refresh - refreshes the wallet, updating transactions from daemon
|
||||
* @return - true if refreshed successfully;
|
||||
*/
|
||||
virtual bool refresh() = 0;
|
||||
|
||||
/**
|
||||
* @brief refreshAsync - refreshes wallet asynchronously.
|
||||
*/
|
||||
virtual void refreshAsync() = 0;
|
||||
|
||||
/**
|
||||
* @brief rescanBlockchain - rescans the wallet, updating transactions from daemon
|
||||
* @return - true if refreshed successfully;
|
||||
*/
|
||||
virtual bool rescanBlockchain() = 0;
|
||||
|
||||
/**
|
||||
* @brief rescanBlockchainAsync - rescans wallet asynchronously, starting from genesys
|
||||
*/
|
||||
virtual void rescanBlockchainAsync() = 0;
|
||||
|
||||
/**
|
||||
* @brief setAutoRefreshInterval - setup interval for automatic refresh.
|
||||
* @param seconds - interval in millis. if zero or less than zero - automatic refresh disabled;
|
||||
*/
|
||||
virtual void setAutoRefreshInterval(int millis) = 0;
|
||||
|
||||
/**
|
||||
* @brief autoRefreshInterval - returns automatic refresh interval in millis
|
||||
* @return
|
||||
*/
|
||||
virtual int autoRefreshInterval() const = 0;
|
||||
|
||||
/**
|
||||
* @brief addSubaddressAccount - appends a new subaddress account at the end of the last major index of existing subaddress accounts
|
||||
* @param label - the label for the new account (which is the as the label of the primary address (accountIndex,0))
|
||||
*/
|
||||
virtual void addSubaddressAccount(const std::string& label) = 0;
|
||||
/**
|
||||
* @brief numSubaddressAccounts - returns the number of existing subaddress accounts
|
||||
*/
|
||||
virtual size_t numSubaddressAccounts() const = 0;
|
||||
/**
|
||||
* @brief numSubaddresses - returns the number of existing subaddresses associated with the specified subaddress account
|
||||
* @param accountIndex - the major index specifying the subaddress account
|
||||
*/
|
||||
virtual size_t numSubaddresses(uint32_t accountIndex) const = 0;
|
||||
/**
|
||||
* @brief addSubaddress - appends a new subaddress at the end of the last minor index of the specified subaddress account
|
||||
* @param accountIndex - the major index specifying the subaddress account
|
||||
* @param label - the label for the new subaddress
|
||||
*/
|
||||
virtual void addSubaddress(uint32_t accountIndex, const std::string& label) = 0;
|
||||
/**
|
||||
* @brief getSubaddressLabel - gets the label of the specified subaddress
|
||||
* @param accountIndex - the major index specifying the subaddress account
|
||||
* @param addressIndex - the minor index specifying the subaddress
|
||||
*/
|
||||
virtual std::string getSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex) const = 0;
|
||||
/**
|
||||
* @brief setSubaddressLabel - sets the label of the specified subaddress
|
||||
* @param accountIndex - the major index specifying the subaddress account
|
||||
* @param addressIndex - the minor index specifying the subaddress
|
||||
* @param label - the new label for the specified subaddress
|
||||
*/
|
||||
virtual void setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label) = 0;
|
||||
|
||||
/**
|
||||
* @brief multisig - returns current state of multisig wallet creation process
|
||||
* @return MultisigState struct
|
||||
*/
|
||||
virtual MultisigState multisig() const = 0;
|
||||
/**
|
||||
* @brief getMultisigInfo
|
||||
* @return serialized and signed multisig info string
|
||||
*/
|
||||
virtual std::string getMultisigInfo() const = 0;
|
||||
/**
|
||||
* @brief makeMultisig - switches wallet in multisig state. The one and only creation phase for N / N wallets
|
||||
* @param info - vector of multisig infos from other participants obtained with getMulitisInfo call
|
||||
* @param threshold - number of required signers to make valid transaction. Must be <= number of participants
|
||||
* @return in case of N / N wallets returns empty string since no more key exchanges needed. For N - 1 / N wallets returns base58 encoded extra multisig info
|
||||
*/
|
||||
virtual std::string makeMultisig(const std::vector<std::string>& info, uint32_t threshold) = 0;
|
||||
/**
|
||||
* @brief exchange_multisig_keys - provides additional key exchange round for arbitrary multisig schemes (like N-1/N, M/N)
|
||||
* @param info - base58 encoded key derivations returned by makeMultisig or exchangeMultisigKeys function call
|
||||
* @param force_update_use_with_caution - force multisig account to update even if not all signers contribute round messages
|
||||
* @return new info string if more rounds required or an empty string if wallet creation is done
|
||||
*/
|
||||
virtual std::string exchangeMultisigKeys(const std::vector<std::string> &info, const bool force_update_use_with_caution) = 0;
|
||||
/**
|
||||
* @brief exportMultisigImages - exports transfers' key images
|
||||
* @param images - output paramter for hex encoded array of images
|
||||
* @return true if success
|
||||
*/
|
||||
virtual bool exportMultisigImages(std::string& images) = 0;
|
||||
/**
|
||||
* @brief importMultisigImages - imports other participants' multisig images
|
||||
* @param images - array of hex encoded arrays of images obtained with exportMultisigImages
|
||||
* @return number of imported images
|
||||
*/
|
||||
virtual size_t importMultisigImages(const std::vector<std::string>& images) = 0;
|
||||
/**
|
||||
* @brief hasMultisigPartialKeyImages - checks if wallet needs to import multisig key images from other participants
|
||||
* @return true if there are partial key images
|
||||
*/
|
||||
virtual bool hasMultisigPartialKeyImages() const = 0;
|
||||
|
||||
/**
|
||||
* @brief restoreMultisigTransaction creates PendingTransaction from signData
|
||||
* @param signData encrypted unsigned transaction. Obtained with PendingTransaction::multisigSignData
|
||||
* @return PendingTransaction
|
||||
*/
|
||||
virtual std::unique_ptr<PendingTransaction> restoreMultisigTransaction(const std::string& signData) = 0;
|
||||
|
||||
/*!
|
||||
* \brief createTransactionMultDest creates transaction with multiple destinations. if dst_addr is an integrated address, payment_id is ignored
|
||||
* \param dst_addr vector of destination address as string
|
||||
* \param payment_id optional payment_id, can be empty string
|
||||
* \param amount vector of amounts
|
||||
* \param mixin_count mixin count. if 0 passed, wallet will use default value
|
||||
* \param subaddr_account subaddress account from which the input funds are taken
|
||||
* \param subaddr_indices set of subaddress indices to use for transfer or sweeping. if set empty, all are chosen when sweeping, and one or more are automatically chosen when transferring. after execution, returns the set of actually used indices
|
||||
* \param priority
|
||||
* \return PendingTransaction object. caller is responsible to check PendingTransaction::status()
|
||||
* after object returned
|
||||
*/
|
||||
|
||||
virtual std::unique_ptr<PendingTransaction> createTransactionMultDest(const std::vector<std::string> &dst_addr, const std::string &payment_id,
|
||||
optional<std::vector<uint64_t>> amount, uint32_t mixin_count,
|
||||
PendingTransaction::Priority = PendingTransaction::Priority::Low,
|
||||
uint32_t subaddr_account = 0,
|
||||
std::set<uint32_t> subaddr_indices = {}) = 0;
|
||||
|
||||
/*!
|
||||
* \brief createTransaction creates transaction. if dst_addr is an integrated address, payment_id is ignored
|
||||
* \param dst_addr destination address as string
|
||||
* \param payment_id optional payment_id, can be empty string
|
||||
* \param amount amount
|
||||
* \param mixin_count mixin count. if 0 passed, wallet will use default value
|
||||
* \param subaddr_account subaddress account from which the input funds are taken
|
||||
* \param subaddr_indices set of subaddress indices to use for transfer or sweeping. if set empty, all are chosen when sweeping, and one or more are automatically chosen when transferring. after execution, returns the set of actually used indices
|
||||
* \param priority
|
||||
* \return PendingTransaction object. caller is responsible to check PendingTransaction::status()
|
||||
* after object returned
|
||||
*/
|
||||
|
||||
virtual std::unique_ptr<PendingTransaction> createTransaction(const std::string &dst_addr, const std::string &payment_id,
|
||||
std::optional<uint64_t> amount, uint32_t mixin_count,
|
||||
PendingTransaction::Priority = PendingTransaction::Priority::Low,
|
||||
uint32_t subaddr_account = 0,
|
||||
std::set<uint32_t> subaddr_indices = {}) = 0;
|
||||
|
||||
/*!
|
||||
* \brief createSweepUnmixableTransaction creates transaction with unmixable outputs.
|
||||
* \return PendingTransaction object. caller is responsible to check PendingTransaction::status()
|
||||
* after object returned
|
||||
*/
|
||||
|
||||
virtual std::unique_ptr<PendingTransaction> createSweepUnmixableTransaction() = 0;
|
||||
|
||||
/*!
|
||||
* \brief loadUnsignedTx - creates transaction from unsigned tx file
|
||||
* \return - UnsignedTransaction object. caller is responsible to check UnsignedTransaction::status()
|
||||
* after object returned
|
||||
*/
|
||||
virtual std::unique_ptr<UnsignedTransaction> loadUnsignedTx(const std::string &unsigned_filename) = 0;
|
||||
|
||||
/*!
|
||||
* \brief submitTransaction - submits transaction in signed tx file
|
||||
* \return - true on success
|
||||
*/
|
||||
virtual bool submitTransaction(const std::string &fileName) = 0;
|
||||
|
||||
|
||||
/*!
|
||||
* \brief disposeTransaction - destroys transaction object
|
||||
* \param t - pointer to the "PendingTransaction" object. Pointer is not valid after function returned;
|
||||
*/
|
||||
virtual void disposeTransaction(PendingTransaction * t) = 0;
|
||||
|
||||
/*!
|
||||
* \brief Estimates transaction fee.
|
||||
* \param destinations Vector consisting of <address, amount> pairs.
|
||||
* \return Estimated fee.
|
||||
*/
|
||||
virtual uint64_t estimateTransactionFee(const std::vector<std::pair<std::string, uint64_t>> &destinations,
|
||||
PendingTransaction::Priority priority) const = 0;
|
||||
|
||||
/*!
|
||||
* \brief exportKeyImages - exports key images to file
|
||||
* \param filename
|
||||
* \param all - export all key images or only those that have not yet been exported
|
||||
* \return - true on success
|
||||
*/
|
||||
virtual bool exportKeyImages(const std::string &filename, bool all = false) = 0;
|
||||
|
||||
/*!
|
||||
* \brief importKeyImages - imports key images from file
|
||||
* \param filename
|
||||
* \return - true on success
|
||||
*/
|
||||
virtual bool importKeyImages(const std::string &filename) = 0;
|
||||
|
||||
/*!
|
||||
* \brief importOutputs - exports outputs to file
|
||||
* \param filename
|
||||
* \return - true on success
|
||||
*/
|
||||
virtual bool exportOutputs(const std::string &filename, bool all = false) = 0;
|
||||
|
||||
/*!
|
||||
* \brief importOutputs - imports outputs from file
|
||||
* \param filename
|
||||
* \return - true on success
|
||||
*/
|
||||
virtual bool importOutputs(const std::string &filename) = 0;
|
||||
|
||||
/*!
|
||||
* \brief scanTransactions - scan a list of transaction ids, this operation may reveal the txids to the remote node and affect your privacy
|
||||
* \param txids - list of transaction ids
|
||||
* \return - true on success
|
||||
*/
|
||||
virtual bool scanTransactions(const std::vector<std::string> &txids) = 0;
|
||||
|
||||
virtual std::shared_ptr<TransactionHistory> history() override;
|
||||
virtual std::shared_ptr<AddressBook> addressBook() = 0;
|
||||
virtual std::shared_ptr<Subaddress> subaddress() = 0;
|
||||
virtual std::shared_ptr<SubaddressAccount> subaddressAccount() = 0;
|
||||
virtual void setListener(WalletListener *) = 0;
|
||||
/*!
|
||||
* \brief defaultMixin - returns number of mixins used in transactions
|
||||
* \return
|
||||
*/
|
||||
virtual uint32_t defaultMixin() const = 0;
|
||||
/*!
|
||||
* \brief setDefaultMixin - setum number of mixins to be used for new transactions
|
||||
* \param arg
|
||||
*/
|
||||
virtual void setDefaultMixin(uint32_t arg) = 0;
|
||||
|
||||
/*!
|
||||
* \brief setCacheAttribute - attach an arbitrary string to a wallet cache attribute
|
||||
* \param key - the key
|
||||
* \param val - the value
|
||||
* \return true if successful, false otherwise
|
||||
*/
|
||||
virtual bool setCacheAttribute(const std::string &key, const std::string &val) = 0;
|
||||
/*!
|
||||
* \brief getCacheAttribute - return an arbitrary string attached to a wallet cache attribute
|
||||
* \param key - the key
|
||||
* \return the attached string, or empty string if there is none
|
||||
*/
|
||||
virtual std::string getCacheAttribute(const std::string &key) const = 0;
|
||||
/*!
|
||||
* \brief setUserNote - attach an arbitrary string note to a txid
|
||||
* \param txid - the transaction id to attach the note to
|
||||
* \param note - the note
|
||||
* \return true if successful, false otherwise
|
||||
*/
|
||||
virtual bool setUserNote(const std::string &txid, const std::string ¬e) = 0;
|
||||
/*!
|
||||
* \brief getUserNote - return an arbitrary string note attached to a txid
|
||||
* \param txid - the transaction id to attach the note to
|
||||
* \return the attached note, or empty string if there is none
|
||||
*/
|
||||
virtual std::string getUserNote(const std::string &txid) const = 0;
|
||||
virtual std::string getTxKey(const std::string &txid) const = 0;
|
||||
virtual bool checkTxKey(const std::string &txid, std::string tx_key, const std::string &address, uint64_t &received, bool &in_pool, uint64_t &confirmations) = 0;
|
||||
virtual std::string getTxProof(const std::string &txid, const std::string &address, const std::string &message) const = 0;
|
||||
virtual bool checkTxProof(const std::string &txid, const std::string &address, const std::string &message, const std::string &signature, bool &good, uint64_t &received, bool &in_pool, uint64_t &confirmations) = 0;
|
||||
virtual std::string getSpendProof(const std::string &txid, const std::string &message) const = 0;
|
||||
virtual bool checkSpendProof(const std::string &txid, const std::string &message, const std::string &signature, bool &good) const = 0;
|
||||
/*!
|
||||
* \brief getReserveProof - Generates a proof that proves the reserve of unspent funds
|
||||
* Parameters `account_index` and `amount` are ignored when `all` is true
|
||||
*/
|
||||
virtual std::string getReserveProof(bool all, uint32_t account_index, uint64_t amount, const std::string &message) const = 0;
|
||||
virtual bool checkReserveProof(const std::string &address, const std::string &message, const std::string &signature, bool &good, uint64_t &total, uint64_t &spent) const = 0;
|
||||
|
||||
/*
|
||||
* \brief signMessage - sign a message with the spend private key
|
||||
* \param message - the message to sign (arbitrary byte data)
|
||||
* \return the signature
|
||||
*/
|
||||
virtual std::string signMessage(const std::string &message, const std::string &address = "") = 0;
|
||||
/*!
|
||||
* \brief verifySignedMessage - verify a signature matches a given message
|
||||
* \param message - the message (arbitrary byte data)
|
||||
* \param address - the address the signature claims to be made with
|
||||
* \param signature - the signature
|
||||
* \return true if the signature verified, false otherwise
|
||||
*/
|
||||
virtual bool verifySignedMessage(const std::string &message, const std::string &addres, const std::string &signature) const = 0;
|
||||
|
||||
/*!
|
||||
* \brief signMultisigParticipant signs given message with the multisig public signer key
|
||||
* \param message message to sign
|
||||
* \return signature in case of success. Sets status to Error and return empty string in case of error
|
||||
*/
|
||||
virtual std::string signMultisigParticipant(const std::string &message) const = 0;
|
||||
/*!
|
||||
* \brief verifyMessageWithPublicKey verifies that message was signed with the given public key
|
||||
* \param message message
|
||||
* \param publicKey hex encoded public key
|
||||
* \param signature signature of the message
|
||||
* \return true if the signature is correct. false and sets error state in case of error
|
||||
*/
|
||||
virtual bool verifyMessageWithPublicKey(const std::string &message, const std::string &publicKey, const std::string &signature) const = 0;
|
||||
|
||||
virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error) = 0;
|
||||
virtual std::string make_uri(const std::string &address, const std::string &payment_id, uint64_t amount, const std::string &tx_description, const std::string &recipient_name, std::string &error) const = 0;
|
||||
|
||||
virtual std::string getDefaultDataDir() const = 0;
|
||||
|
||||
/*
|
||||
* \brief rescanSpent - Rescan spent outputs - Can only be used with trusted daemon
|
||||
* \return true on success
|
||||
*/
|
||||
virtual bool rescanSpent() = 0;
|
||||
|
||||
/*
|
||||
* \brief setOffline - toggle set offline on/off
|
||||
* \param offline - true/false
|
||||
*/
|
||||
virtual void setOffline(bool offline) = 0;
|
||||
virtual bool isOffline() const = 0;
|
||||
|
||||
//! blackballs a set of outputs
|
||||
virtual bool blackballOutputs(const std::vector<std::string> &outputs, bool add) = 0;
|
||||
|
||||
//! blackballs an output
|
||||
virtual bool blackballOutput(const std::string &amount, const std::string &offset) = 0;
|
||||
|
||||
//! unblackballs an output
|
||||
virtual bool unblackballOutput(const std::string &amount, const std::string &offset) = 0;
|
||||
|
||||
//! gets the ring used for a key image, if any
|
||||
virtual bool getRing(const std::string &key_image, std::vector<uint64_t> &ring) const = 0;
|
||||
|
||||
//! gets the rings used for a txid, if any
|
||||
virtual bool getRings(const std::string &txid, std::vector<std::pair<std::string, std::vector<uint64_t>>> &rings) const = 0;
|
||||
|
||||
//! sets the ring used for a key image
|
||||
virtual bool setRing(const std::string &key_image, const std::vector<uint64_t> &ring, bool relative) = 0;
|
||||
|
||||
//! sets whether pre-fork outs are to be segregated
|
||||
virtual void segregatePreForkOutputs(bool segregate) = 0;
|
||||
|
||||
//! sets the height where segregation should occur
|
||||
virtual void segregationHeight(uint64_t height) = 0;
|
||||
|
||||
//! secondary key reuse mitigation
|
||||
virtual void keyReuseMitigation2(bool mitigation) = 0;
|
||||
|
||||
//! locks/unlocks the keys file; returns true on success
|
||||
virtual bool lockKeysFile() = 0;
|
||||
virtual bool unlockKeysFile() = 0;
|
||||
//! returns true if the keys file is locked
|
||||
virtual bool isKeysFileLocked() = 0;
|
||||
|
||||
/*!
|
||||
* \brief Queries backing device for wallet keys
|
||||
* \return Device they are on
|
||||
*/
|
||||
virtual Device getDeviceType() const = 0;
|
||||
|
||||
//! cold-device protocol key image sync
|
||||
virtual uint64_t coldKeyImageSync(uint64_t &spent, uint64_t &unspent) = 0;
|
||||
|
||||
//! shows address on device display
|
||||
virtual void deviceShowAddress(uint32_t accountIndex, uint32_t addressIndex, const std::string &paymentId) = 0;
|
||||
|
||||
//! attempt to reconnect to hardware device
|
||||
virtual bool reconnectDevice() = 0;
|
||||
|
||||
//! get bytes received
|
||||
virtual uint64_t getBytesReceived() = 0;
|
||||
|
||||
//! get bytes sent
|
||||
virtual uint64_t getBytesSent() = 0;
|
||||
};
|
||||
}} // lwsf // internal
|
||||
@@ -0,0 +1,365 @@
|
||||
// Copyright (c) 2024, 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 "lws_frontend.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include "backend.h"
|
||||
#include "lwsf_config.h"
|
||||
#include "net/http_client.h" // monero/contrib/epee/include
|
||||
#include "net/parse.h" // monero/src
|
||||
#include "net/socks_connect.h" // monero/src
|
||||
#include "rpc.h"
|
||||
#include "wallet.h"
|
||||
|
||||
namespace lwsf
|
||||
{
|
||||
namespace internal
|
||||
{
|
||||
//! \TODO Mark final when completely implemented
|
||||
class wallet_manager : public lwsf::WalletManager
|
||||
{
|
||||
rpc::http_client client_;
|
||||
std::string error_;
|
||||
rpc::daemon_status cached_;
|
||||
std::chrono::steady_clock::time_point cached_last_;
|
||||
|
||||
rpc::daemon_status get_daemon_status()
|
||||
{
|
||||
if (std::chrono::steady_clock::now() - cached_last_ > config::daemon_status_cache)
|
||||
{
|
||||
const auto resp = rpc::invoke<rpc::daemon_status>(client_, rpc::empty{});
|
||||
if (!resp)
|
||||
{
|
||||
error_ = resp.error().message();
|
||||
cached_ = rpc::daemon_status{};
|
||||
}
|
||||
else
|
||||
{
|
||||
error_.clear();
|
||||
cached_ = std::move(*resp);
|
||||
}
|
||||
}
|
||||
return cached_;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
wallet_manager()
|
||||
: client_(), error_(), cached_{}, cached_last_(std::chrono::seconds{0})
|
||||
{}
|
||||
|
||||
virtual ~wallet_manager() override
|
||||
{}
|
||||
|
||||
/*!
|
||||
* \brief Creates new wallet
|
||||
* \param path Name of wallet file
|
||||
* \param password Password of wallet file
|
||||
* \param language Language to be used to generate electrum seed mnemonic
|
||||
* \param nettype Network type
|
||||
* \param kdf_rounds Number of rounds for key derivation function
|
||||
* \return Wallet instance (Wallet::status() needs to be called to check if created successfully)
|
||||
*/
|
||||
std::unique_ptr<Wallet> createWallet(const std::string &path, const std::string &password, const std::string &language, NetworkType nettype, uint64_t kdf_rounds) override
|
||||
{
|
||||
//! \TODO Complete
|
||||
return std::make_unique<wallet>(path, std::make_shared<backend::wallet>(true));
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Opens existing wallet
|
||||
* \param path Name of wallet file
|
||||
* \param password Password of wallet file
|
||||
* \param nettype Network type
|
||||
* \param kdf_rounds Number of rounds for key derivation function
|
||||
* \param listener Wallet listener to set to the wallet after creation
|
||||
* \return Wallet instance (Wallet::status() needs to be called to check if opened successfully)
|
||||
*/
|
||||
|
||||
std::unique_ptr<Wallet> openWallet(const std::string &path, const std::string &password, NetworkType nettype, uint64_t kdf_rounds, std::shared_ptr<WalletListener> listener) override
|
||||
{
|
||||
//! \TODO Complete
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief recovers existing wallet using mnemonic (electrum seed)
|
||||
* \param path Name of wallet file to be created
|
||||
* \param password Password of wallet file
|
||||
* \param mnemonic mnemonic (25 words electrum seed)
|
||||
* \param nettype Network type
|
||||
* \param restoreHeight restore from start height
|
||||
* \param kdf_rounds Number of rounds for key derivation function
|
||||
* \param seed_offset Seed offset passphrase (optional)
|
||||
* \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully)
|
||||
*/
|
||||
std::unique_ptr<Wallet> recoveryWallet(const std::string &path, const std::string &password, const std::string &mnemonic,
|
||||
NetworkType nettype = MAINNET, uint64_t restoreHeight = 0, uint64_t kdf_rounds = 1,
|
||||
const std::string &seed_offset = {}) override final
|
||||
{
|
||||
//! \TODO Complete
|
||||
}
|
||||
|
||||
/*!
|
||||
* \deprecated this method creates a wallet WITHOUT a passphrase, use the alternate recoverWallet() method
|
||||
* \brief recovers existing wallet using mnemonic (electrum seed)
|
||||
* \param path Name of wallet file to be created
|
||||
* \param mnemonic mnemonic (25 words electrum seed)
|
||||
* \param nettype Network type
|
||||
* \param restoreHeight restore from start height
|
||||
* \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully)
|
||||
*/
|
||||
std::unique_ptr<Wallet> recoveryWallet(const std::string &path, const std::string &mnemonic, NetworkType nettype, uint64_t restoreHeight = 0) override
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief recovers existing wallet using keys. Creates a view only wallet if spend key is omitted
|
||||
* \param path Name of wallet file to be created
|
||||
* \param password Password of wallet file
|
||||
* \param language language
|
||||
* \param nettype Network type
|
||||
* \param restoreHeight restore from start height
|
||||
* \param addressString public address
|
||||
* \param viewKeyString view key
|
||||
* \param spendKeyString spend key (optional)
|
||||
* \param kdf_rounds Number of rounds for key derivation function
|
||||
* \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully)
|
||||
*/
|
||||
std::unique_ptr<Wallet> createWalletFromKeys(const std::string &path,
|
||||
const std::string &password,
|
||||
const std::string &language,
|
||||
NetworkType nettype,
|
||||
uint64_t restoreHeight,
|
||||
const std::string &addressString,
|
||||
const std::string &viewKeyString,
|
||||
const std::string &spendKeyString,
|
||||
uint64_t kdf_rounds) override
|
||||
{
|
||||
//! \TODO Complete
|
||||
}
|
||||
|
||||
/*!
|
||||
* \deprecated this method creates a wallet WITHOUT a passphrase, use createWalletFromKeys(..., password, ...) instead
|
||||
* \brief recovers existing wallet using keys. Creates a view only wallet if spend key is omitted
|
||||
* \param path Name of wallet file to be created
|
||||
* \param language language
|
||||
* \param nettype Network type
|
||||
* \param restoreHeight restore from start height
|
||||
* \param addressString public address
|
||||
* \param viewKeyString view key
|
||||
* \param spendKeyString spend key (optional)
|
||||
* \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully)
|
||||
*/
|
||||
std::unique_ptr<Wallet> createWalletFromKeys(const std::string &path,
|
||||
const std::string &language,
|
||||
NetworkType nettype,
|
||||
uint64_t restoreHeight,
|
||||
const std::string &addressString,
|
||||
const std::string &viewKeyString,
|
||||
const std::string &spendKeyString) override
|
||||
{
|
||||
//! \TODO Complete
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief creates wallet using hardware device.
|
||||
* \param path Name of wallet file to be created
|
||||
* \param password Password of wallet file
|
||||
* \param nettype Network type
|
||||
* \param deviceName Device name
|
||||
* \param restoreHeight restore from start height (0 sets to current height)
|
||||
* \param subaddressLookahead Size of subaddress lookahead (empty sets to some default low value)
|
||||
* \param kdf_rounds Number of rounds for key derivation function
|
||||
* \param listener Wallet listener to set to the wallet after creation
|
||||
* \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully)
|
||||
*/
|
||||
std::unique_ptr<Wallet> createWalletFromDevice(const std::string &path,
|
||||
const std::string &password,
|
||||
NetworkType nettype,
|
||||
const std::string &deviceName,
|
||||
uint64_t restoreHeight,
|
||||
const std::string &subaddressLookahead,
|
||||
uint64_t kdf_rounds,
|
||||
WalletListener * listener) override
|
||||
{
|
||||
//! \TODO Complete
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Closes wallet. In case operation succeeded, wallet object deleted. in case operation failed, wallet object not deleted
|
||||
* \param wallet previously opened / created wallet instance
|
||||
* \return None
|
||||
*/
|
||||
bool closeWallet(std::unique_ptr<Wallet> wallet, bool store) override
|
||||
{
|
||||
if (wallet && store)
|
||||
wallet->store(wallet->filename());
|
||||
}
|
||||
|
||||
/*
|
||||
* ! checks if wallet with the given name already exists
|
||||
*/
|
||||
|
||||
/*!
|
||||
* @brief TODO: delme walletExists - check if the given filename is the wallet
|
||||
* @param path - filename
|
||||
* @return - true if wallet exists
|
||||
*/
|
||||
bool walletExists(const std::string &path) override
|
||||
{
|
||||
//! \TODO Complete
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief verifyWalletPassword - check if the given filename is the wallet
|
||||
* @param keys_file_name - location of keys file
|
||||
* @param password - password to verify
|
||||
* @param no_spend_key - verify only view keys?
|
||||
* @param kdf_rounds - number of rounds for key derivation function
|
||||
* @return - true if password is correct
|
||||
*
|
||||
* @note
|
||||
* This function will fail when the wallet keys file is opened because the wallet program locks the keys file.
|
||||
* In this case, Wallet::unlockKeysFile() and Wallet::lockKeysFile() need to be called before and after the call to this function, respectively.
|
||||
*/
|
||||
bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key, uint64_t kdf_rounds) const override
|
||||
{
|
||||
//! \TODO Complete
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief determine the key storage for the specified wallet file
|
||||
* \param device_type (OUT) wallet backend as enumerated in Wallet::Device
|
||||
* \param keys_file_name Keys file to verify password for
|
||||
* \param password Password to verify
|
||||
* \return true if password correct, else false
|
||||
*
|
||||
* for verification only - determines key storage hardware
|
||||
*
|
||||
*/
|
||||
bool queryWalletDevice(Wallet::Device& device_type, const std::string &keys_file_name, const std::string &password, uint64_t kdf_rounds) const override
|
||||
{
|
||||
//! \TODO Complete
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief findWallets - searches for the wallet files by given path name recursively
|
||||
* \param path - starting point to search
|
||||
* \return - list of strings with found wallets (absolute paths);
|
||||
*/
|
||||
std::vector<std::string> findWallets(const std::string &path) override
|
||||
{
|
||||
//! \TODO Complete
|
||||
}
|
||||
|
||||
std::string errorString() const override { return error_; }
|
||||
|
||||
//! set the daemon address (hostname and port)
|
||||
void setDaemonAddress(const std::string &address) override
|
||||
{
|
||||
//! \TODO Complete
|
||||
}
|
||||
|
||||
//! returns whether the daemon can be reached, and its version number
|
||||
bool connected(uint32_t *version = NULL) override
|
||||
{
|
||||
version = 0;
|
||||
return client_.is_connected();
|
||||
}
|
||||
|
||||
uint64_t blockchainHeight() override
|
||||
{
|
||||
return get_daemon_status().height;
|
||||
}
|
||||
|
||||
uint64_t blockchainTargetHeight() override
|
||||
{
|
||||
return get_daemon_status().target_height;
|
||||
}
|
||||
|
||||
//! \TODO Complete
|
||||
uint64_t networkDifficulty() override { return 0; }
|
||||
|
||||
//! \TODO Complete
|
||||
double miningHashRate() override { return 0.f; }
|
||||
|
||||
//! \TODO Complete
|
||||
uint64_t blockTarget() override { return 0; }
|
||||
|
||||
bool isMining() override { return false; }
|
||||
bool startMining(const std::string&, uint32_t, bool, bool) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool stopMining() override { return true; }
|
||||
|
||||
std::string resolveOpenAlias(const std::string &address, bool &dnssec_valid) const override
|
||||
{
|
||||
//! \TODO Complete
|
||||
}
|
||||
|
||||
//! checks for an update and returns version, hash and url
|
||||
static std::tuple<bool, std::string, std::string, std::string, std::string> checkUpdates(
|
||||
const std::string &software,
|
||||
std::string subdir,
|
||||
const char *buildtag = nullptr,
|
||||
const char *current_version = nullptr);
|
||||
|
||||
bool setProxy(const std::string &address) override
|
||||
{
|
||||
auto endpoint = net::get_tcp_endpoint(address);
|
||||
if (!endpoint)
|
||||
{
|
||||
error_ = endpoint.error().message();
|
||||
return false;
|
||||
}
|
||||
client_.set_connector(net::socks::connector{std::move(*endpoint)});
|
||||
return true;
|
||||
}
|
||||
};
|
||||
} // internal
|
||||
|
||||
std::unique_ptr<WalletManager> WalletManagerFactory::getWalletManager()
|
||||
{
|
||||
return std::make_unique<internal::wallet_manager>();
|
||||
}
|
||||
|
||||
void WalletManagerFactory::setLogLevel(int level)
|
||||
{
|
||||
//! \TODO Complete
|
||||
}
|
||||
|
||||
void WalletManagerFactory::setLogCategories(const std::string &categories)
|
||||
{
|
||||
//! \TODO Complete
|
||||
}
|
||||
} // lwsf
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
// 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 <algorithm>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
#include "common/expect.h" // monero/src
|
||||
#include "wire/error.h"
|
||||
#include "wire/read.h"
|
||||
#include "wire/write.h"
|
||||
|
||||
#define WIRE_DEFINE_ENUM(type_, map) \
|
||||
static_assert(std::is_enum<type_>::value, "get_string will fail"); \
|
||||
static_assert(!std::is_signed<type_>::value, "write_bytes will fail"); \
|
||||
const char* get_string(const type_ source) noexcept \
|
||||
{ \
|
||||
using native_type = std::underlying_type<type_>::type; \
|
||||
const native_type value = native_type(source); \
|
||||
if (value < std::end(map) - std::begin(map)) \
|
||||
return map[value]; \
|
||||
return "invalid enum value"; \
|
||||
} \
|
||||
expect<type_> type_ ## _from_string(const boost::string_ref source) noexcept \
|
||||
{ \
|
||||
if (const auto elem = std::find(std::begin(map), std::end(map), source)) \
|
||||
{ \
|
||||
if (elem != std::end(map)) \
|
||||
return type_(elem - std::begin(map)); \
|
||||
} \
|
||||
return {::wire::error::schema::enumeration}; \
|
||||
} \
|
||||
void read_bytes(::wire::reader& source, type_& dest) \
|
||||
{ \
|
||||
const auto val = type_ ## _from_string(source.string()); \
|
||||
if (!val) \
|
||||
WIRE_DLOG_THROW(::wire::error::schema::enumeration, #type_); \
|
||||
dest = *val; \
|
||||
} \
|
||||
void write_bytes(::wire::writer& dest, const type_ source) \
|
||||
{ \
|
||||
dest.string(get_string(source)); \
|
||||
}
|
||||
|
||||
#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); \
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
# Copyright (c) 2020-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.
|
||||
|
||||
set(monero-lws-wire_sources error.cpp read.cpp write.cpp)
|
||||
set(monero-lws-wire_headers error.h field.h filters.h fwd.h json.h read.h traits.h vector.h write.h)
|
||||
|
||||
add_library(monero-lws-wire ${monero-lws-wire_sources} ${monero-lws-wire_headers})
|
||||
target_include_directories(monero-lws-wire PUBLIC "${LMDB_INCLUDE}")
|
||||
target_link_libraries(monero-lws-wire PRIVATE monero::libraries)
|
||||
|
||||
add_subdirectory(json)
|
||||
add_subdirectory(msgpack)
|
||||
add_subdirectory(wrapper)
|
||||
@@ -0,0 +1,96 @@
|
||||
// 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 <cstdint>
|
||||
#include "span.h"
|
||||
#include "wire/error.h"
|
||||
#include "wire/read.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
// enable writing of std::array
|
||||
template<typename T, std::size_t N>
|
||||
struct is_array<std::array<T, N>>
|
||||
: std::true_type
|
||||
{};
|
||||
|
||||
// `std::array`s of `char` and `uint8_t` are not arrays
|
||||
template<std::size_t N>
|
||||
struct is_array<std::array<char, N>>
|
||||
: std::false_type
|
||||
{};
|
||||
template<std::size_t N>
|
||||
struct is_array<std::array<std::uint8_t, N>>
|
||||
: std::false_type
|
||||
{};
|
||||
|
||||
template<typename R, std::size_t N>
|
||||
inline void read_bytes(R& source, std::array<char, N>& dest)
|
||||
{
|
||||
source.binary(epee::to_mut_span(dest));
|
||||
}
|
||||
template<typename R, std::size_t N>
|
||||
inline void read_bytes(R& source, std::array<std::uint8_t, N>& dest)
|
||||
{
|
||||
source.binary(epee::to_mut_span(dest));
|
||||
}
|
||||
|
||||
template<typename W, std::size_t N>
|
||||
inline void write_bytes(W& dest, const std::array<char, N>& source)
|
||||
{
|
||||
source.binary(epee::to_span(source));
|
||||
}
|
||||
template<typename W, std::size_t N>
|
||||
inline void write_bytes(W& dest, const std::array<std::uint8_t, N>& source)
|
||||
{
|
||||
source.binary(epee::to_span(source));
|
||||
}
|
||||
|
||||
// Read a fixed sized array
|
||||
template<typename R, typename T, std::size_t N>
|
||||
inline void read_bytes(R& source, std::array<T, N>& dest)
|
||||
{
|
||||
std::size_t count = source.start_array(0);
|
||||
const bool json = (count == 0);
|
||||
if (!json && count != dest.size())
|
||||
WIRE_DLOG_THROW(wire::error::schema::array, "Expected array of size " << dest.size());
|
||||
|
||||
for (auto& elem : dest)
|
||||
{
|
||||
if (json && source.is_array_end(count))
|
||||
WIRE_DLOG_THROW(wire::error::schema::array, "Expected array of size " << dest.size());
|
||||
wire_read::bytes(source, elem);
|
||||
--count;
|
||||
}
|
||||
if (!source.is_array_end(count))
|
||||
WIRE_DLOG_THROW(wire::error::schema::array, "Expected array of size " << dest.size());
|
||||
source.end_array();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
// 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 <type_traits>
|
||||
|
||||
#include "crypto/crypto.h" // monero/src
|
||||
#include "span.h" // monero/contrib/include
|
||||
#include "ringct/rctTypes.h" // monero/src
|
||||
#include "wire/traits.h"
|
||||
|
||||
namespace crypto
|
||||
{
|
||||
template<typename R>
|
||||
void read_bytes(R& source, crypto::secret_key& self)
|
||||
{
|
||||
source.binary(epee::as_mut_byte_span(unwrap(unwrap(self))));
|
||||
}
|
||||
|
||||
template<typename W>
|
||||
void write_bytes(W& dest, const crypto::secret_key& self)
|
||||
{
|
||||
dest.binary(epee::as_byte_span(unwrap(unwrap(self))));
|
||||
}
|
||||
}
|
||||
|
||||
namespace wire
|
||||
{
|
||||
WIRE_DECLARE_BLOB(crypto::ec_scalar);
|
||||
WIRE_DECLARE_BLOB(crypto::hash);
|
||||
WIRE_DECLARE_BLOB(crypto::hash8);
|
||||
WIRE_DECLARE_BLOB(crypto::key_derivation);
|
||||
WIRE_DECLARE_BLOB(crypto::key_image);
|
||||
WIRE_DECLARE_BLOB(crypto::public_key);
|
||||
WIRE_DECLARE_BLOB(crypto::signature);
|
||||
WIRE_DECLARE_BLOB(crypto::view_tag);
|
||||
WIRE_DECLARE_BLOB(rct::key);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright (c) 2024, 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 "wire/field.h"
|
||||
#include "wire/read.h"
|
||||
#include "wire/write.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
template<typename F, typename T>
|
||||
void map_pair(F& format, T& self)
|
||||
{
|
||||
wire::object(format, WIRE_FIELD_ID(0, first), WIRE_FIELD_ID(1, second));
|
||||
}
|
||||
|
||||
template<typename R, typename T, typename U>
|
||||
void read_bytes(R& source, std::pair<T, U>& dest)
|
||||
{ map_pair(source, dest); }
|
||||
|
||||
template<typename W, typename T, typename U>
|
||||
void write_bytes(W& dest, const std::pair<T, U>& source)
|
||||
{ map_pair(dest, source); }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
// 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 "span.h" // monero/contrib/epee/include
|
||||
#include "wire/traits.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
//! Enable span types for array output
|
||||
template<typename T>
|
||||
struct is_array<epee::span<T>>
|
||||
: std::true_type
|
||||
{};
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
// 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 "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,137 @@
|
||||
// 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 <exception>
|
||||
#include <system_error>
|
||||
#include <type_traits>
|
||||
|
||||
#include "misc_log_ex.h" // monero/contrib/epee/include
|
||||
|
||||
//! 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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<>
|
||||
struct is_error_code_enum<wire::error::schema>
|
||||
: true_type
|
||||
{};
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
// Copyright (c) 2020-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 "wire/filters.h"
|
||||
#include "wire/traits.h"
|
||||
|
||||
//! A required field with the same key name and C/C++ name
|
||||
#define WIRE_FIELD_ID(id, name) \
|
||||
::wire::field< id >( #name , std::ref( self . name ))
|
||||
|
||||
//! A required field has the same key name and C/C++ name
|
||||
#define WIRE_FIELD(name) \
|
||||
WIRE_FIELD_ID(0, 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_ID(id, name) \
|
||||
::wire::optional_field< id >( #name , std::ref( self . name ))
|
||||
|
||||
//! The optional field has the same key name and C/C++ name
|
||||
#define WIRE_OPTIONAL_FIELD(name) \
|
||||
WIRE_OPTIONAL_FIELD_ID(0, name)
|
||||
|
||||
namespace wire
|
||||
{
|
||||
/*! Links `name` to a `value` and index `I` 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 parsing 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, unsigned I = 0>
|
||||
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; }
|
||||
static constexpr unsigned id() noexcept { return I; }
|
||||
|
||||
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<unsigned I = 0, typename T = void>
|
||||
constexpr inline field_<T, true, I> field(const char* name, T value)
|
||||
{
|
||||
return {name, std::move(value)};
|
||||
}
|
||||
|
||||
//! Links `name` to `value`. Use `std::ref` if de-serializing.
|
||||
template<unsigned I = 0, typename T = void>
|
||||
constexpr inline field_<T, false, I> optional_field(const char* name, T value)
|
||||
{
|
||||
return {name, std::move(value)};
|
||||
}
|
||||
|
||||
|
||||
//! Indicates a field value should be written as an array
|
||||
template<typename T, typename F>
|
||||
struct as_array_
|
||||
{
|
||||
using value_type = typename unwrap_reference<T>::type;
|
||||
|
||||
T value;
|
||||
F filter; //!< Each element in `value` given to this callable before `write_bytes`.
|
||||
|
||||
//! \return `value` with `std::reference_wrapper` removed.
|
||||
constexpr const value_type& get_value() const noexcept
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
//! \return `value` with `std::reference_wrapper` removed.
|
||||
value_type& get_value() noexcept
|
||||
{
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
//! Callable that can filter `as_object` values or be used immediately.
|
||||
template<typename Default>
|
||||
struct as_array_filter
|
||||
{
|
||||
Default default_filter;
|
||||
|
||||
template<typename T>
|
||||
constexpr as_array_<T, Default> operator()(T value) const
|
||||
{
|
||||
return {std::move(value), default_filter};
|
||||
}
|
||||
|
||||
template<typename T, typename F>
|
||||
constexpr as_array_<T, F> operator()(T value, F filter) const
|
||||
{
|
||||
return {std::move(value), std::move(filter)};
|
||||
}
|
||||
};
|
||||
//! Usage: `wire::field("foo", wire::as_array(self.foo, to_string{})`. Consider `std::ref`.
|
||||
constexpr as_array_filter<identity_> as_array{};
|
||||
|
||||
|
||||
//! Indicates a field value should be written as an object
|
||||
template<typename T, typename F, typename G>
|
||||
struct as_object_
|
||||
{
|
||||
using map_type = typename unwrap_reference<T>::type;
|
||||
|
||||
T map;
|
||||
F key_filter; //!< Each key (`.first`) in `map` given to this callable before writing field key.
|
||||
G value_filter; //!< Each value (`.second`) in `map` given to this callable before `write_bytes`.
|
||||
|
||||
//! \return `map` with `std::reference_wrapper` removed.
|
||||
constexpr const map_type& get_map() const noexcept
|
||||
{
|
||||
return map;
|
||||
}
|
||||
|
||||
//! \return `map` with `std::reference_wrapper` removed.
|
||||
map_type& get_map() noexcept
|
||||
{
|
||||
return map;
|
||||
}
|
||||
};
|
||||
|
||||
//! Usage: `wire::field("foo", wire::as_object(self.foo, to_string{}, wire::as_array))`. Consider `std::ref`.
|
||||
template<typename T, typename F = identity_, typename G = identity_>
|
||||
inline constexpr as_object_<T, F, G> as_object(T map, F key_filter = F{}, G value_filter = G{})
|
||||
{
|
||||
return {std::move(map), std::move(key_filter), std::move(value_filter)};
|
||||
}
|
||||
|
||||
|
||||
template<typename T, unsigned I>
|
||||
inline constexpr bool available(const field_<T, true, I>& elem) noexcept
|
||||
{
|
||||
/* 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, unsigned I>
|
||||
inline bool available(const field_<T, false, I>& elem)
|
||||
{
|
||||
return bool(elem.get_value());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
// 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 <cassert>
|
||||
#include <type_traits>
|
||||
|
||||
// These functions are to be used with `wire::as_object(...)` key filtering
|
||||
|
||||
namespace wire
|
||||
{
|
||||
//! Callable that returns the value unchanged; default filter for `as_array` and `as_object`.
|
||||
struct identity_
|
||||
{
|
||||
template<typename T>
|
||||
const T& operator()(const T& value) const noexcept
|
||||
{
|
||||
return value;
|
||||
}
|
||||
};
|
||||
constexpr const identity_ identity{};
|
||||
|
||||
//! Callable that forwards enum to get_string.
|
||||
struct enum_as_string_
|
||||
{
|
||||
template<typename T>
|
||||
auto operator()(const T value) const noexcept(noexcept(get_string(value))) -> decltype(get_string(value))
|
||||
{
|
||||
return get_string(value);
|
||||
}
|
||||
};
|
||||
constexpr const enum_as_string_ enum_as_string{};
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
// 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 <boost/utility/string_ref.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
#include "common/expect.h" // monero/src
|
||||
|
||||
//! 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 an enum to be serialized as a string (json) or integer (msgpack)
|
||||
#define WIRE_DECLARE_ENUM(type) \
|
||||
const char* get_string(type) noexcept; \
|
||||
expect<type> type ## _from_string(const boost::string_ref) noexcept; \
|
||||
void read_bytes(::wire::reader&, type&); \
|
||||
void write_bytes(::wire::writer&, type)
|
||||
|
||||
//! Declare a class/struct serialization for all available formats
|
||||
#define WIRE_DECLARE_OBJECT(type) \
|
||||
void read_bytes(::wire::reader&, type&); \
|
||||
void write_bytes(::wire::writer&, const type&)
|
||||
|
||||
namespace wire
|
||||
{
|
||||
class reader;
|
||||
struct writer;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
// 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 "wire/json/base.h"
|
||||
#include "wire/json/error.h"
|
||||
#include "wire/json/read.h"
|
||||
#include "wire/json/write.h"
|
||||
|
||||
#define WIRE_JSON_DEFINE_ENUM(type, map) \
|
||||
void read_bytes(::wire::json_reader& source, type& dest) \
|
||||
{ \
|
||||
dest = type(source.enumeration(map)); \
|
||||
} \
|
||||
void write_bytes(::wire::json_writer& dest, const type source) \
|
||||
{ \
|
||||
dest.enumeration(std::size_t(source), map); \
|
||||
}
|
||||
|
||||
#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); \
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# 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.
|
||||
|
||||
set(monero-lws_wire-json_sources error.cpp read.cpp write.cpp)
|
||||
set(monero-lws_wire-json_headers base.h error.h fwd.h read.h write.h)
|
||||
|
||||
add_library(monero-lws-wire-json ${monero-lws_wire-json_sources} ${monero-lws-wire-json_headers})
|
||||
target_link_libraries(monero-lws-wire-json monero::libraries monero-lws-wire)
|
||||
@@ -0,0 +1,58 @@
|
||||
// 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 <string>
|
||||
#include <system_error>
|
||||
|
||||
#include "byte_stream.h"
|
||||
#include "wire/json/fwd.h"
|
||||
#include "wire/read.h"
|
||||
#include "wire/write.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
struct json
|
||||
{
|
||||
using input_type = json_reader;
|
||||
using output_type = json_slice_writer;
|
||||
|
||||
template<typename T>
|
||||
static std::error_code from_bytes(std::string&& source, T& dest)
|
||||
{
|
||||
return wire_read::from_bytes<input_type>(std::move(source), dest);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
static std::error_code to_bytes(T& dest, const U& source)
|
||||
{
|
||||
return wire_write::to_bytes<output_type>(dest, source);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
// 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 "error.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
namespace error
|
||||
{
|
||||
const char* get_string(const rapidjson_e value) noexcept
|
||||
{
|
||||
switch (rapidjson::ParseErrorCode(value))
|
||||
{
|
||||
default:
|
||||
break;
|
||||
|
||||
case rapidjson::kParseErrorNone:
|
||||
return "No JSON parsing errors";
|
||||
|
||||
// from rapidjson
|
||||
case rapidjson::kParseErrorDocumentEmpty:
|
||||
return "JSON parser expected non-empty document";
|
||||
case rapidjson::kParseErrorDocumentRootNotSingular:
|
||||
return "JSON parser expected one value at root level";
|
||||
|
||||
case rapidjson::kParseErrorValueInvalid:
|
||||
return "JSON parser found invalid value";
|
||||
|
||||
case rapidjson::kParseErrorObjectMissName:
|
||||
return "JSON parser expected name for object field";
|
||||
case rapidjson::kParseErrorObjectMissColon:
|
||||
return "JSON parser expected ':' between name and value";
|
||||
case rapidjson::kParseErrorObjectMissCommaOrCurlyBracket:
|
||||
return "JSON parser expected ',' or '}'";
|
||||
|
||||
case rapidjson::kParseErrorArrayMissCommaOrSquareBracket:
|
||||
return "JSON parser expected ',' or ']'";
|
||||
|
||||
case rapidjson::kParseErrorStringUnicodeEscapeInvalidHex:
|
||||
return "JSON parser found invalid unicode escape";
|
||||
case rapidjson::kParseErrorStringUnicodeSurrogateInvalid:
|
||||
return "JSON parser found invalid unicode surrogate value";
|
||||
case rapidjson::kParseErrorStringEscapeInvalid:
|
||||
return "JSON parser found invalid escape sequence in string value";
|
||||
case rapidjson::kParseErrorStringMissQuotationMark:
|
||||
return "JSON parser expected '\"'";
|
||||
case rapidjson::kParseErrorStringInvalidEncoding:
|
||||
return "JSON parser found invalid encoding";
|
||||
|
||||
case rapidjson::kParseErrorNumberTooBig:
|
||||
return "JSON parser found number value larger than double float precision";
|
||||
case rapidjson::kParseErrorNumberMissFraction:
|
||||
return "JSON parser found number missing fractional component";
|
||||
case rapidjson::kParseErrorNumberMissExponent:
|
||||
return "JSON parser found number missing exponent";
|
||||
|
||||
case rapidjson::kParseErrorTermination:
|
||||
return "JSON parser was stopped";
|
||||
case rapidjson::kParseErrorUnspecificSyntaxError:
|
||||
return "JSON parser found syntax error";
|
||||
}
|
||||
|
||||
return "Unknown JSON parser error";
|
||||
}
|
||||
|
||||
const std::error_category& rapidjson_category() noexcept
|
||||
{
|
||||
struct category final : std::error_category
|
||||
{
|
||||
virtual const char* name() const noexcept override final
|
||||
{
|
||||
return "wire::error::rapidjson_category()";
|
||||
}
|
||||
|
||||
virtual std::string message(int value) const override final
|
||||
{
|
||||
return get_string(rapidjson_e(value));
|
||||
}
|
||||
};
|
||||
static const category instance{};
|
||||
return instance;
|
||||
}
|
||||
} // error
|
||||
} // wire
|
||||
@@ -0,0 +1,53 @@
|
||||
// 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 <rapidjson/error/error.h>
|
||||
#include <system_error>
|
||||
|
||||
namespace wire
|
||||
{
|
||||
namespace error
|
||||
{
|
||||
//! Type wrapper to "grab" rapidjson errors
|
||||
enum class rapidjson_e : int {};
|
||||
|
||||
//! \return Static string describing error `value`.
|
||||
const char* get_string(rapidjson_e value) noexcept;
|
||||
|
||||
//! \return Category for rapidjson generated errors.
|
||||
const std::error_category& rapidjson_category() noexcept;
|
||||
|
||||
//! \return Error code with `value` and `rapidjson_category()`.
|
||||
inline std::error_code make_error_code(rapidjson_e value) noexcept
|
||||
{
|
||||
return std::error_code{int(value), rapidjson_category()};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
// 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
|
||||
|
||||
#define WIRE_JSON_DECLARE_ENUM(type) \
|
||||
const char* get_string(type) noexcept; \
|
||||
void read_bytes(::wire::json_reader&, type&); \
|
||||
void write_bytes(:wire::json_writer&, type)
|
||||
|
||||
#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;
|
||||
class json_slice_writer;
|
||||
class json_writer;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,411 @@
|
||||
// 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 "read.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <rapidjson/memorystream.h>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "common/expect.h" // monero/src
|
||||
#include "hex.h" // monero/contrib/epee/include
|
||||
#include "wire/error.h"
|
||||
#include "wire/json/error.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
//! Maximum number of bytes to display "near" JSON error.
|
||||
constexpr const std::size_t snippet_size = 30;
|
||||
|
||||
struct json_default_reject : rapidjson::BaseReaderHandler<rapidjson::UTF8<>, json_default_reject>
|
||||
{
|
||||
bool Default() const noexcept { return false; }
|
||||
};
|
||||
|
||||
//! \throw std::system_error by converting `code` into a std::error_code
|
||||
[[noreturn]] void throw_json_error(const epee::span<const std::uint8_t> source, const rapidjson::Reader& reader, const wire::error::schema expected)
|
||||
{
|
||||
const std::size_t offset = std::min(source.size(), reader.GetErrorOffset());
|
||||
const std::size_t start = offset;//std::max(snippet_size / 2, offset) - (snippet_size / 2);
|
||||
const std::size_t end = start + std::min(snippet_size, source.size() - start);
|
||||
|
||||
const boost::string_ref text{reinterpret_cast<const char*>(source.data()) + start, end - start};
|
||||
const rapidjson::ParseErrorCode parse_error = reader.GetParseErrorCode();
|
||||
switch (parse_error)
|
||||
{
|
||||
default:
|
||||
WIRE_DLOG_THROW(wire::error::rapidjson_e(parse_error), "near \"" << text << '"');
|
||||
case rapidjson::kParseErrorNone:
|
||||
case rapidjson::kParseErrorTermination: // the handler returned false
|
||||
break;
|
||||
}
|
||||
WIRE_DLOG_THROW(expected, "near '" << text << '\'');
|
||||
}
|
||||
}
|
||||
|
||||
namespace wire
|
||||
{
|
||||
struct json_reader::rapidjson_sax
|
||||
{
|
||||
struct string_contents
|
||||
{
|
||||
const char* ptr;
|
||||
std::size_t length;
|
||||
};
|
||||
|
||||
union
|
||||
{
|
||||
bool boolean;
|
||||
std::intmax_t integer;
|
||||
std::uintmax_t unsigned_integer;
|
||||
double number;
|
||||
string_contents string;
|
||||
} value;
|
||||
|
||||
error::schema expected_;
|
||||
bool negative;
|
||||
|
||||
explicit rapidjson_sax(error::schema expected) noexcept
|
||||
: expected_(expected), negative(false)
|
||||
{}
|
||||
|
||||
bool Null() const noexcept
|
||||
{
|
||||
return expected_ == error::schema::none;
|
||||
}
|
||||
|
||||
bool Bool(bool i) noexcept
|
||||
{
|
||||
value.boolean = i;
|
||||
return expected_ == error::schema::boolean || expected_ == error::schema::none;
|
||||
}
|
||||
|
||||
bool Int(int i) noexcept
|
||||
{
|
||||
return Int64(i);
|
||||
}
|
||||
bool Uint(unsigned i) noexcept
|
||||
{
|
||||
return Uint64(i);
|
||||
}
|
||||
bool Int64(std::int64_t i) noexcept
|
||||
{
|
||||
negative = true;
|
||||
switch(expected_)
|
||||
{
|
||||
default:
|
||||
return false;
|
||||
case error::schema::integer:
|
||||
value.integer = i;
|
||||
break;
|
||||
case error::schema::number:
|
||||
value.number = i;
|
||||
break;
|
||||
case error::schema::none:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool Uint64(std::uint64_t i) noexcept
|
||||
{
|
||||
switch (expected_)
|
||||
{
|
||||
default:
|
||||
return false;
|
||||
case error::schema::integer:
|
||||
value.unsigned_integer = i;
|
||||
break;
|
||||
case error::schema::number:
|
||||
value.number = i;
|
||||
break;
|
||||
case error::schema::none:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Double(double i) noexcept
|
||||
{
|
||||
value.number = i;
|
||||
return expected_ == error::schema::number || expected_ == error::schema::none;
|
||||
}
|
||||
|
||||
bool RawNumber(const char*, std::size_t, bool) const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool String(const char* str, std::size_t length, bool) noexcept
|
||||
{
|
||||
value.string = {str, length};
|
||||
return expected_ == error::schema::string || expected_ == error::schema::none;
|
||||
}
|
||||
bool Key(const char* str, std::size_t length, bool)
|
||||
{
|
||||
return String(str, length, true);
|
||||
}
|
||||
|
||||
bool StartArray() const noexcept { return expected_ == error::schema::none; }
|
||||
bool EndArray(std::size_t) const noexcept { return expected_ == error::schema::none; }
|
||||
bool StartObject() const noexcept { return expected_ == error::schema::none; }
|
||||
bool EndObject(std::size_t) const noexcept { return expected_ == error::schema::none; }
|
||||
};
|
||||
|
||||
void json_reader::read_next_value(rapidjson_sax& handler)
|
||||
{
|
||||
rapidjson::MemoryStream stream{reinterpret_cast<const char*>(remaining_.data()), remaining_.size()};
|
||||
rapidjson::EncodedInputStream<rapidjson::UTF8<>, rapidjson::MemoryStream> istream{stream};
|
||||
if (!reader_.Parse<rapidjson::kParseStopWhenDoneFlag>(istream, handler))
|
||||
throw_json_error(remaining_, reader_, handler.expected_);
|
||||
remaining_.remove_prefix(istream.Tell());
|
||||
}
|
||||
|
||||
char json_reader::get_next_token()
|
||||
{
|
||||
rapidjson::MemoryStream stream{reinterpret_cast<const char*>(remaining_.data()), remaining_.size()};
|
||||
rapidjson::EncodedInputStream<rapidjson::UTF8<>, rapidjson::MemoryStream> istream{stream};
|
||||
rapidjson::SkipWhitespace(istream);
|
||||
remaining_.remove_prefix(istream.Tell());
|
||||
return stream.Peek();
|
||||
}
|
||||
|
||||
boost::string_ref json_reader::get_next_string()
|
||||
{
|
||||
if (get_next_token() != '"')
|
||||
WIRE_DLOG_THROW_(error::schema::string);
|
||||
remaining_.remove_prefix(1);
|
||||
|
||||
void const* const end = std::memchr(remaining_.data(), '"', remaining_.size());
|
||||
if (!end)
|
||||
WIRE_DLOG_THROW_(error::rapidjson_e(rapidjson::kParseErrorStringMissQuotationMark));
|
||||
|
||||
std::uint8_t const* const begin = remaining_.data();
|
||||
const std::size_t length = remaining_.remove_prefix(static_cast<const std::uint8_t*>(end) - remaining_.data() + 1);
|
||||
return {reinterpret_cast<const char*>(begin), length - 1};
|
||||
}
|
||||
|
||||
void json_reader::skip_value()
|
||||
{
|
||||
rapidjson_sax accept_all{error::schema::none};
|
||||
read_next_value(accept_all);
|
||||
}
|
||||
|
||||
json_reader::json_reader(std::string&& source)
|
||||
: reader(nullptr),
|
||||
source_(std::move(source)),
|
||||
reader_()
|
||||
{
|
||||
remaining_ = {reinterpret_cast<const std::uint8_t*>(source_.data()), source_.size()};
|
||||
}
|
||||
|
||||
void json_reader::check_complete() const
|
||||
{
|
||||
if (depth())
|
||||
WIRE_DLOG_THROW(error::rapidjson_e(rapidjson::kParseErrorUnspecificSyntaxError), "Unexpected end");
|
||||
}
|
||||
|
||||
bool json_reader::boolean()
|
||||
{
|
||||
rapidjson_sax json_bool{error::schema::boolean};
|
||||
read_next_value(json_bool);
|
||||
return json_bool.value.boolean;
|
||||
}
|
||||
|
||||
using imax_limits = std::numeric_limits<std::intmax_t>;
|
||||
static_assert(0 <= imax_limits::max(), "expected 0 <= intmax_t::max");
|
||||
static_assert(
|
||||
imax_limits::max() <= std::numeric_limits<std::uintmax_t>::max(),
|
||||
"expected intmax_t::max <= uintmax_t::max"
|
||||
);
|
||||
|
||||
std::intmax_t json_reader::integer()
|
||||
{
|
||||
rapidjson_sax json_int{error::schema::integer};
|
||||
read_next_value(json_int);
|
||||
if (json_int.negative)
|
||||
return json_int.value.integer;
|
||||
if (static_cast<std::uintmax_t>(imax_limits::max()) < json_int.value.unsigned_integer)
|
||||
WIRE_DLOG_THROW_(error::schema::smaller_integer);
|
||||
return static_cast<std::intmax_t>(json_int.value.unsigned_integer);
|
||||
}
|
||||
|
||||
std::uintmax_t json_reader::unsigned_integer()
|
||||
{
|
||||
rapidjson_sax json_uint{error::schema::integer};
|
||||
read_next_value(json_uint);
|
||||
if (!json_uint.negative)
|
||||
return json_uint.value.unsigned_integer;
|
||||
if (json_uint.value.integer < 0)
|
||||
WIRE_DLOG_THROW_(error::schema::larger_integer);
|
||||
return static_cast<std::uintmax_t>(json_uint.value.integer);
|
||||
}
|
||||
/*
|
||||
const std::vector<std::uintmax_t>& json_reader::unsigned_integer_array()
|
||||
{
|
||||
read_next_unsigned_array(
|
||||
}*/
|
||||
|
||||
std::uintmax_t json_reader::safe_unsigned_integer()
|
||||
{
|
||||
if (get_next_token() != '"')
|
||||
WIRE_DLOG_THROW_(error::schema::string);
|
||||
remaining_.remove_prefix(1);
|
||||
|
||||
const std::uintmax_t out = unsigned_integer();
|
||||
|
||||
if (get_next_token() != '"')
|
||||
WIRE_DLOG_THROW_(error::rapidjson_e(rapidjson::kParseErrorStringMissQuotationMark));
|
||||
remaining_.remove_prefix(1);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
double json_reader::real()
|
||||
{
|
||||
rapidjson_sax json_number{error::schema::number};
|
||||
read_next_value(json_number);
|
||||
return json_number.value.number;
|
||||
}
|
||||
|
||||
std::string json_reader::string()
|
||||
{
|
||||
rapidjson_sax json_string{error::schema::string};
|
||||
read_next_value(json_string);
|
||||
return std::string{json_string.value.string.ptr, json_string.value.string.length};
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> json_reader::binary()
|
||||
{
|
||||
const boost::string_ref value = get_next_string();
|
||||
|
||||
std::vector<std::uint8_t> out;
|
||||
out.resize(value.size() / 2);
|
||||
|
||||
if (!epee::from_hex::to_buffer(epee::to_mut_span(out), value))
|
||||
WIRE_DLOG_THROW_(error::schema::binary);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void json_reader::binary(epee::span<std::uint8_t> dest)
|
||||
{
|
||||
const boost::string_ref value = get_next_string();
|
||||
if (!epee::from_hex::to_buffer(dest, value))
|
||||
WIRE_DLOG_THROW(error::schema::fixed_binary, "of size" << dest.size() * 2 << " but got " << value.size());
|
||||
}
|
||||
|
||||
std::size_t json_reader::start_array(std::size_t)
|
||||
{
|
||||
if (get_next_token() != '[')
|
||||
WIRE_DLOG_THROW_(error::schema::array);
|
||||
remaining_.remove_prefix(1);
|
||||
increment_depth();
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool json_reader::is_array_end(const std::size_t count)
|
||||
{
|
||||
const char next = get_next_token();
|
||||
if (next == 0)
|
||||
WIRE_DLOG_THROW_(error::rapidjson_e(rapidjson::kParseErrorArrayMissCommaOrSquareBracket));
|
||||
if (next == ']')
|
||||
{
|
||||
remaining_.remove_prefix(1);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (count)
|
||||
{
|
||||
if (next != ',')
|
||||
WIRE_DLOG_THROW_(error::rapidjson_e(rapidjson::kParseErrorArrayMissCommaOrSquareBracket));
|
||||
remaining_.remove_prefix(1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::size_t json_reader::start_object()
|
||||
{
|
||||
if (get_next_token() != '{')
|
||||
WIRE_DLOG_THROW_(error::schema::object);
|
||||
remaining_.remove_prefix(1);
|
||||
increment_depth();
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool json_reader::key(const epee::span<const key_map> map, std::size_t& state, std::size_t& index)
|
||||
{
|
||||
rapidjson_sax json_key{error::schema::string};
|
||||
const auto process_key = [map] (const rapidjson_sax::string_contents value)
|
||||
{
|
||||
const boost::string_ref key{value.ptr, value.length};
|
||||
for (std::size_t i = 0; i < map.size(); ++i)
|
||||
{
|
||||
if (map[i].name == key)
|
||||
return i;
|
||||
}
|
||||
return map.size();
|
||||
};
|
||||
|
||||
index = map.size();
|
||||
for (;;)
|
||||
{
|
||||
// check for object or text end
|
||||
const char next = get_next_token();
|
||||
if (next == 0)
|
||||
WIRE_DLOG_THROW_(error::rapidjson_e(rapidjson::kParseErrorObjectMissCommaOrCurlyBracket));
|
||||
if (next == '}')
|
||||
{
|
||||
remaining_.remove_prefix(1);
|
||||
return false;
|
||||
}
|
||||
|
||||
// parse next field token
|
||||
if (state)
|
||||
{
|
||||
if (next != ',')
|
||||
WIRE_DLOG_THROW_(error::rapidjson_e(rapidjson::kParseErrorObjectMissCommaOrCurlyBracket));
|
||||
remaining_.remove_prefix(1);
|
||||
}
|
||||
++state;
|
||||
|
||||
// parse key
|
||||
read_next_value(json_key);
|
||||
index = process_key(json_key.value.string);
|
||||
if (get_next_token() != ':')
|
||||
WIRE_DLOG_THROW_(error::rapidjson_e(rapidjson::kParseErrorObjectMissColon));
|
||||
remaining_.remove_prefix(1);
|
||||
|
||||
// parse value
|
||||
if (index != map.size())
|
||||
break;
|
||||
skip_value();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
// 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 <boost/utility/string_ref.hpp>
|
||||
#include <cstddef>
|
||||
#include <rapidjson/reader.h>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "wire/field.h"
|
||||
#include "wire/json/base.h"
|
||||
#include "wire/read.h"
|
||||
#include "wire/traits.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
//! Reads JSON tokens one-at-a-time for DOMless parsing
|
||||
class json_reader : public reader
|
||||
{
|
||||
struct rapidjson_sax;
|
||||
|
||||
std::string source_;
|
||||
rapidjson::Reader reader_;
|
||||
|
||||
void read_next_value(rapidjson_sax& handler);
|
||||
char get_next_token();
|
||||
boost::string_ref get_next_string();
|
||||
|
||||
//! Skips next value. \throw wire::exception if invalid JSON syntax.
|
||||
void skip_value();
|
||||
|
||||
public:
|
||||
explicit json_reader(std::string&& source);
|
||||
|
||||
//! \throw wire::exception if JSON parsing is incomplete.
|
||||
void check_complete() const override final;
|
||||
|
||||
//! \throw wire::exception if next token not a boolean.
|
||||
bool boolean() override final;
|
||||
|
||||
//! \throw wire::expception if next token not an integer.
|
||||
std::intmax_t integer() override final;
|
||||
|
||||
//! \throw wire::exception if next token not an unsigned integer.
|
||||
std::uintmax_t unsigned_integer() override final;
|
||||
|
||||
//! \throw wire::exception if next token is not an integer encoded as string
|
||||
std::uintmax_t safe_unsigned_integer();
|
||||
|
||||
//! \throw wire::exception if next token not a valid real number
|
||||
double real() override final;
|
||||
|
||||
//! \throw wire::exception if next token not a string
|
||||
std::string string() override final;
|
||||
|
||||
//! \throw wire::exception if next token cannot be read as hex
|
||||
std::vector<std::uint8_t> binary() override final;
|
||||
|
||||
//! \throw wire::exception if next token cannot be read as hex into `dest`.
|
||||
void binary(epee::span<std::uint8_t> dest) override final;
|
||||
|
||||
|
||||
//! \throw wire::exception if next token not `[`.
|
||||
std::size_t start_array(std::size_t) override final;
|
||||
|
||||
//! Skips whitespace to next token. \return True if next token is eof or ']'.
|
||||
bool is_array_end(std::size_t count) override final;
|
||||
|
||||
|
||||
//! \throw wire::exception if next token not `{`.
|
||||
std::size_t start_object() override final;
|
||||
|
||||
/*! \throw wire::exception if next token not key or `}`.
|
||||
\param[out] index of key match within `map`.
|
||||
\return True if another value to read. */
|
||||
bool key(epee::span<const key_map> map, std::size_t&, std::size_t& index) override final;
|
||||
};
|
||||
} // wire
|
||||
@@ -0,0 +1,169 @@
|
||||
// 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 "write.h"
|
||||
|
||||
#include <ostream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "hex.h" // monero/contrib/epee/include
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr const unsigned flush_threshold = 100;
|
||||
constexpr const unsigned max_buffer = 4096;
|
||||
}
|
||||
|
||||
namespace wire
|
||||
{
|
||||
void json_writer::do_flush(epee::span<const std::uint8_t>)
|
||||
{}
|
||||
|
||||
void json_writer::check_flush()
|
||||
{
|
||||
if (needs_flush_ && (max_buffer < bytes_.size() || bytes_.available() < flush_threshold))
|
||||
flush();
|
||||
}
|
||||
|
||||
void json_writer::check_complete()
|
||||
{
|
||||
if (!formatter_.IsComplete())
|
||||
throw std::logic_error{"json_writer::take_json() failed with incomplete JSON tree"};
|
||||
}
|
||||
epee::byte_stream json_writer::take_json()
|
||||
{
|
||||
check_complete();
|
||||
epee::byte_stream out{std::move(bytes_)};
|
||||
bytes_.clear();
|
||||
formatter_.Reset(bytes_);
|
||||
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 int source)
|
||||
{
|
||||
formatter_.Int(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 unsigned source)
|
||||
{
|
||||
formatter_.Uint(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 boost::string_ref source)
|
||||
{
|
||||
formatter_.String(source.data(), source.size());
|
||||
check_flush();
|
||||
}
|
||||
void json_writer::binary(epee::span<const std::uint8_t> source)
|
||||
{/* TODO update monero project
|
||||
std::array<char, 256> buffer;
|
||||
if (source.size() <= buffer.size() / 2)
|
||||
{
|
||||
if (!epee::to_hex::buffer({buffer.data(), source.size() * 2}, source))
|
||||
throw std::logic_error{"Invalid buffer size for binary->hex conversion"};
|
||||
string({buffer.data(), source.size() * 2});
|
||||
}
|
||||
else
|
||||
{*/
|
||||
const auto hex = epee::to_hex::string(source);
|
||||
string(hex);
|
||||
//}
|
||||
}
|
||||
|
||||
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 boost::string_ref str)
|
||||
{
|
||||
formatter_.Key(str.data(), str.size());
|
||||
check_flush();
|
||||
}
|
||||
void json_writer::key(const std::uintmax_t id)
|
||||
{
|
||||
auto str = json_writer::to_string(id);
|
||||
key(str.data());
|
||||
}
|
||||
void json_writer::key(unsigned, const boost::string_ref str)
|
||||
{
|
||||
key(str);
|
||||
}
|
||||
void json_writer::end_object()
|
||||
{
|
||||
formatter_.EndObject();
|
||||
}
|
||||
|
||||
void json_stream_writer::do_flush(epee::span<const std::uint8_t> bytes)
|
||||
{
|
||||
dest.write(reinterpret_cast<const char*>(bytes.data()), bytes.size());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
// 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 <boost/utility/string_ref.hpp>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <rapidjson/writer.h>
|
||||
|
||||
#include "byte_stream.h" // monero/contrib/epee/include
|
||||
#include "span.h" // monero/contrib/epee/include
|
||||
#include "wire/field.h"
|
||||
#include "wire/filters.h"
|
||||
#include "wire/json/base.h"
|
||||
#include "wire/traits.h"
|
||||
#include "wire/write.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
constexpr const std::size_t uint_to_string_size =
|
||||
std::numeric_limits<std::uintmax_t>::digits10 + 2;
|
||||
|
||||
//! Writes JSON tokens one-at-a-time for DOMless output.
|
||||
class json_writer : public writer
|
||||
{
|
||||
epee::byte_stream bytes_;
|
||||
rapidjson::Writer<epee::byte_stream> formatter_;
|
||||
bool needs_flush_;
|
||||
|
||||
//! \return True if buffer needs to be cleared
|
||||
virtual void do_flush(epee::span<const uint8_t>);
|
||||
|
||||
//! Flush written bytes to `do_flush(...)` if configured
|
||||
void check_flush();
|
||||
|
||||
protected:
|
||||
json_writer(epee::byte_stream&& out, bool needs_flush)
|
||||
: writer(), bytes_(std::move(out)), formatter_(bytes_), needs_flush_(needs_flush)
|
||||
{}
|
||||
|
||||
//! \throw std::logic_error if incomplete JSON tree
|
||||
void check_complete();
|
||||
|
||||
//! \throw std::logic_error if incomplete JSON tree. \return JSON bytes
|
||||
epee::byte_stream take_json();
|
||||
|
||||
//! Flush bytes in local buffer to `do_flush(...)`
|
||||
void flush()
|
||||
{
|
||||
do_flush({bytes_.data(), bytes_.size()});
|
||||
bytes_ = epee::byte_stream{}; // TODO create .clear() method in monero project
|
||||
}
|
||||
|
||||
public:
|
||||
//! JSON does not need array sizes.
|
||||
static constexpr std::false_type need_array_size() noexcept { return{}; }
|
||||
|
||||
json_writer(const json_writer&) = delete;
|
||||
virtual ~json_writer() noexcept;
|
||||
json_writer& operator=(const json_writer&) = delete;
|
||||
|
||||
//! \return Null-terminated buffer containing uint as decimal ascii
|
||||
static std::array<char, uint_to_string_size> to_string(std::uintmax_t) noexcept;
|
||||
|
||||
void boolean(bool) override final;
|
||||
|
||||
void integer(int) override final;
|
||||
void integer(std::intmax_t) override final;
|
||||
|
||||
void unsigned_integer(unsigned) override final;
|
||||
void unsigned_integer(std::uintmax_t) override final;
|
||||
|
||||
void real(double) override final;
|
||||
|
||||
void string(boost::string_ref) override final;
|
||||
void binary(epee::span<const std::uint8_t> source) 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::uintmax_t) override final;
|
||||
void key(boost::string_ref) override final;
|
||||
void key(unsigned, boost::string_ref) override final;
|
||||
void end_object() override final;
|
||||
};
|
||||
|
||||
//! Buffers entire JSON message in memory
|
||||
struct json_slice_writer final : json_writer
|
||||
{
|
||||
using sink = epee::byte_stream;
|
||||
|
||||
explicit json_slice_writer(sink&& out)
|
||||
: json_writer(std::move(out), false)
|
||||
{}
|
||||
|
||||
explicit json_slice_writer()
|
||||
: json_writer(epee::byte_stream{}, false)
|
||||
{}
|
||||
|
||||
//! \throw std::logic_error if incomplete JSON tree \return JSON bytes
|
||||
epee::byte_stream take_sink()
|
||||
{
|
||||
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 std::uint8_t>) override final;
|
||||
public:
|
||||
explicit json_stream_writer(std::ostream& dest)
|
||||
: json_writer(epee::byte_stream{}, true), dest(dest)
|
||||
{}
|
||||
|
||||
//! Flush remaining bytes to stream \throw std::logic_error if incomplete JSON tree
|
||||
void finish()
|
||||
{
|
||||
check_complete();
|
||||
flush();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// 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 "wire/msgpack/base.h"
|
||||
#include "wire/msgpack/error.h"
|
||||
#include "wire/msgpack/read.h"
|
||||
#include "wire/msgpack/write.h"
|
||||
|
||||
#define WIRE_MSGPACK_DEFINE_ENUM(type, map) \
|
||||
void read_bytes(::wire::msgpack_reader& source, type& dest) \
|
||||
{ \
|
||||
dest = type(source.enumeration(map)); \
|
||||
} \
|
||||
void write_bytes(::wire::msgpack_writer& dest, const type source) \
|
||||
{ \
|
||||
dest.enumeration(std::size_t(source), map); \
|
||||
}
|
||||
|
||||
#define WIRE_MSGPACK_DEFINE_OBJECT(type, map) \
|
||||
void read_bytes(::wire::msgpack_reader& source, type& dest) \
|
||||
{ \
|
||||
map(source, dest); \
|
||||
} \
|
||||
void write_bytes(::wire::msgpack_writer& dest, const type& source) \
|
||||
{ \
|
||||
map(dest, source); \
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# 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.
|
||||
|
||||
set(monero-lws_wire-msgpack_sources error.cpp read.cpp write.cpp)
|
||||
set(monero-lws_wire-msgpack_headers base.h error.h fwd.h read.h write.h)
|
||||
|
||||
add_library(monero-lws-wire-msgpack ${monero-lws_wire-msgpack_sources} ${monero-lws-wire-msgpack_headers})
|
||||
target_link_libraries(monero-lws-wire-msgpack monero::libraries monero-lws-wire)
|
||||
@@ -0,0 +1,181 @@
|
||||
// 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 <cstdint>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <limits>
|
||||
#include <tuple>
|
||||
|
||||
#include "byte_slice.h"
|
||||
#include "common/expect.h"
|
||||
#include "wire/msgpack/fwd.h"
|
||||
#include "wire/read.h"
|
||||
#include "wire/write.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
struct msgpack
|
||||
{
|
||||
using input_type = msgpack_reader;
|
||||
using output_type = msgpack_slice_writer;
|
||||
|
||||
//! Tags that do not require bitmask to identify
|
||||
enum class tag : std::uint8_t
|
||||
{
|
||||
nil = 0xc0,
|
||||
unused,
|
||||
False,
|
||||
True,
|
||||
binary8,
|
||||
binary16,
|
||||
binary32,
|
||||
extension8,
|
||||
extension16,
|
||||
extension32,
|
||||
float32,
|
||||
float64,
|
||||
uint8,
|
||||
uint16,
|
||||
uint32,
|
||||
uint64,
|
||||
int8,
|
||||
int16,
|
||||
int32,
|
||||
int64,
|
||||
fixed_extension1,
|
||||
fixed_extension2,
|
||||
fixed_extension4,
|
||||
fixed_extension8,
|
||||
fixed_extension16,
|
||||
string8,
|
||||
string16,
|
||||
string32,
|
||||
array16,
|
||||
array32,
|
||||
object16,
|
||||
object32
|
||||
};
|
||||
|
||||
//! Link a fixed tag `T` to its corresponding mask `M` and max value `N`
|
||||
template<std::uint8_t T, std::uint8_t M, std::uint8_t N>
|
||||
struct fixed_tag
|
||||
{
|
||||
static constexpr std::uint8_t tag() noexcept { return T; }
|
||||
static constexpr std::uint8_t mask() noexcept { return M; }
|
||||
static constexpr std::uint8_t max() noexcept { return N; }
|
||||
|
||||
//! \return True if `value` is fixed tag `T`
|
||||
static constexpr bool matches(const std::uint8_t value) noexcept
|
||||
{ return (value & mask()) == tag(); }
|
||||
|
||||
//! \return True if `value` is fixed tag `T`
|
||||
static constexpr bool matches(const msgpack::tag value) noexcept
|
||||
{ return matches(std::uint8_t(value)); }
|
||||
|
||||
//! \return Value encoded in fixed tag
|
||||
static constexpr std::uint8_t extract(const std::uint8_t value) noexcept
|
||||
{ return value & ~mask(); }
|
||||
|
||||
//! \return Value encoded in fixed tag
|
||||
static constexpr std::uint8_t extract(const msgpack::tag value) noexcept
|
||||
{ return extract(std::uint8_t(value)); }
|
||||
};
|
||||
|
||||
// Tags requiring bitmask to identify
|
||||
using ftag_unsigned = fixed_tag<0x00, 0x80, 0x7f>;
|
||||
using ftag_signed = fixed_tag<0xe0, 0xe0, 0>;
|
||||
using ftag_string = fixed_tag<0xa0, 0xe0, 31>;
|
||||
using ftag_array = fixed_tag<0x90, 0xf0, 15>;
|
||||
using ftag_object = fixed_tag<0x80, 0xf0, 15>;
|
||||
|
||||
//! Link a msgpack tag to a C++ numeric
|
||||
template<typename T, tag V>
|
||||
struct type
|
||||
{
|
||||
static constexpr bool is_signed() noexcept { return std::numeric_limits<T>::is_signed; }
|
||||
static constexpr T min() noexcept { return std::numeric_limits<T>::min(); }
|
||||
static constexpr T max() noexcept { return std::numeric_limits<T>::max(); }
|
||||
static constexpr tag Tag() noexcept { return V; }
|
||||
};
|
||||
|
||||
using int8 = type<std::int8_t, tag::int8>;
|
||||
using int16 = type<std::int16_t, tag::int16>;
|
||||
using int32 = type<std::int32_t, tag::int32>;
|
||||
using int64 = type<std::int64_t, tag::int64>;
|
||||
using signed_types = std::tuple<int8, int16, int32, int64>;
|
||||
|
||||
using uint8 = type<std::uint8_t, tag::uint8>;
|
||||
using uint16 = type<std::uint16_t, tag::uint16>;
|
||||
using uint32 = type<std::uint32_t, tag::uint32>;
|
||||
using uint64 = type<std::uint64_t, tag::uint64>;
|
||||
using unsigned_types = std::tuple<uint8, uint16, uint32, uint64>;
|
||||
|
||||
using integer_types = std::tuple<
|
||||
msgpack::uint8, msgpack::int8, msgpack::uint16, msgpack::int16,
|
||||
msgpack::uint32, msgpack::int32, msgpack::uint64, msgpack::int64
|
||||
>;
|
||||
|
||||
using string8 = type<std::uint8_t, tag::string8>;
|
||||
using string16 = type<std::uint16_t, tag::string16>;
|
||||
using string32 = type<std::uint32_t, tag::string32>;
|
||||
using string_types = std::tuple<string8, string16, string32>;
|
||||
|
||||
using binary8 = type<std::uint8_t, tag::binary8>;
|
||||
using binary16 = type<std::uint16_t, tag::binary16>;
|
||||
using binary32 = type<std::uint32_t, tag::binary32>;
|
||||
using binary_types = std::tuple<binary8, binary16, binary32>;
|
||||
|
||||
using extension8 = type<std::uint8_t, tag::extension8>;
|
||||
using extension16 = type<std::uint16_t, tag::extension16>;
|
||||
using extension32 = type<std::uint32_t, tag::extension32>;
|
||||
using extension_types = std::tuple<extension8, extension16, extension32>;
|
||||
|
||||
using array16 = type<std::uint16_t, tag::array16>;
|
||||
using array32 = type<std::uint32_t, tag::array32>;
|
||||
using array_types = std::tuple<array16, array32>;
|
||||
|
||||
using object16 = type<std::uint16_t, tag::object16>;
|
||||
using object32 = type<std::uint32_t, tag::object32>;
|
||||
using object_types = std::tuple<object16, object32>;
|
||||
|
||||
template<typename T>
|
||||
static std::error_code from_bytes(epee::byte_slice&& source, T& dest)
|
||||
{
|
||||
return wire_read::from_bytes<input_type>(std::move(source), dest);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
static std::error_code to_bytes(T& dest, const U& source)
|
||||
{
|
||||
return wire_write::to_bytes<output_type>(dest, source);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
// 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.
|
||||
|
||||
#include "error.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
namespace error
|
||||
{
|
||||
const char* get_string(const msgpack value) noexcept
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
default:
|
||||
break;
|
||||
case msgpack::incomplete:
|
||||
return "Incomplete msgpack tree structure";
|
||||
case msgpack::integer_encoding:
|
||||
return "Unable to encode integer in msgpack";
|
||||
case msgpack::invalid:
|
||||
return "Invalid msgpack encoding";
|
||||
case msgpack::max_tree_size:
|
||||
return "Exceeded tag tracking amount";
|
||||
case msgpack::not_enough_bytes:
|
||||
return "Expected more bytes in the msgpack stream";
|
||||
case msgpack::underflow_tree:
|
||||
return "Expected more tags";
|
||||
}
|
||||
|
||||
return "Unknown msgpack error";
|
||||
}
|
||||
|
||||
const std::error_category& msgpack_category() noexcept
|
||||
{
|
||||
struct category final : std::error_category
|
||||
{
|
||||
virtual const char* name() const noexcept override final
|
||||
{
|
||||
return "wire::error::msgpack_category()";
|
||||
}
|
||||
|
||||
virtual std::string message(int value) const override final
|
||||
{
|
||||
return get_string(msgpack(value));
|
||||
}
|
||||
};
|
||||
static const category instance{};
|
||||
return instance;
|
||||
}
|
||||
} // error
|
||||
} // wire
|
||||
@@ -0,0 +1,61 @@
|
||||
// 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 <system_error>
|
||||
|
||||
namespace wire
|
||||
{
|
||||
namespace error
|
||||
{
|
||||
//! Type wrapper to "grab" rapidjson errors
|
||||
enum class msgpack : int
|
||||
{
|
||||
success = 0, // required for `expected<T>`
|
||||
incomplete,
|
||||
integer_encoding,
|
||||
invalid,
|
||||
max_tree_size,
|
||||
not_enough_bytes,
|
||||
underflow_tree
|
||||
};
|
||||
|
||||
//! \return Static string describing error `value`.
|
||||
const char* get_string(msgpack value) noexcept;
|
||||
|
||||
//! \return Category for msgpack generated errors.
|
||||
const std::error_category& msgpack_category() noexcept;
|
||||
|
||||
//! \return Error code with `value` and `rapidjson_category()`.
|
||||
inline std::error_code make_error_code(msgpack value) noexcept
|
||||
{
|
||||
return std::error_code{int(value), msgpack_category()};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
// 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
|
||||
|
||||
#define WIRE_MSGPACK_DECLARE_ENUM(type) \
|
||||
const char* get_string(type) noexcept; \
|
||||
void read_bytes(::wire::msgpack_reader&, type&); \
|
||||
void write_bytes(:wire::msgpack_writer&, type)
|
||||
|
||||
#define WIRE_MSGPACK_DECLARE_OBJECT(type) \
|
||||
void read_bytes(::wire::msgpack_reader&, type&); \
|
||||
void write_bytes(::wire::msgpack_writer&, const type&)
|
||||
|
||||
namespace wire
|
||||
{
|
||||
struct msgpack;
|
||||
class msgpack_reader;
|
||||
class msgpack_slice_writer;
|
||||
class msgpack_writer;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,515 @@
|
||||
// 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.
|
||||
|
||||
#include "read.h"
|
||||
|
||||
#include <boost/endian/buffers.hpp>
|
||||
#include <boost/fusion/include/any.hpp>
|
||||
#include <boost/fusion/include/std_tuple.hpp>
|
||||
#include <boost/numeric/conversion/cast.hpp>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
#include "wire/error.h"
|
||||
#include "wire/msgpack/error.h"
|
||||
|
||||
// Expands to every possible fixed string tag value
|
||||
#define MLWS_FIXED_STRING_TAGS() \
|
||||
case wire::msgpack::tag(0xa0): case wire::msgpack::tag(0xa1): \
|
||||
case wire::msgpack::tag(0xa2): case wire::msgpack::tag(0xa3): \
|
||||
case wire::msgpack::tag(0xa4): case wire::msgpack::tag(0xa5): \
|
||||
case wire::msgpack::tag(0xa6): case wire::msgpack::tag(0xa7): \
|
||||
case wire::msgpack::tag(0xa8): case wire::msgpack::tag(0xa9): \
|
||||
case wire::msgpack::tag(0xaa): case wire::msgpack::tag(0xab): \
|
||||
case wire::msgpack::tag(0xac): case wire::msgpack::tag(0xad): \
|
||||
case wire::msgpack::tag(0xae): case wire::msgpack::tag(0xaf): \
|
||||
case wire::msgpack::tag(0xb0): case wire::msgpack::tag(0xb1): \
|
||||
case wire::msgpack::tag(0xb2): case wire::msgpack::tag(0xb3): \
|
||||
case wire::msgpack::tag(0xb4): case wire::msgpack::tag(0xb5): \
|
||||
case wire::msgpack::tag(0xb6): case wire::msgpack::tag(0xb7): \
|
||||
case wire::msgpack::tag(0xb8): case wire::msgpack::tag(0xb9): \
|
||||
case wire::msgpack::tag(0xba): case wire::msgpack::tag(0xbb): \
|
||||
case wire::msgpack::tag(0xbc): case wire::msgpack::tag(0xbd): \
|
||||
case wire::msgpack::tag(0xbe): case wire::msgpack::tag(0xbf):
|
||||
|
||||
namespace
|
||||
{
|
||||
template<typename T>
|
||||
using limits = std::numeric_limits<T>;
|
||||
|
||||
//! \return True iif `value` matches a tag in `T` tuple.
|
||||
template<typename T>
|
||||
bool matches(const wire::msgpack::tag tag)
|
||||
{
|
||||
const auto matched_type = [tag] (const auto type)
|
||||
{
|
||||
return type.Tag() == tag;
|
||||
};
|
||||
// NOTE: This is slower than a switch but more flexible/reusable
|
||||
return boost::fusion::any(T{}, matched_type);
|
||||
}
|
||||
|
||||
//! \return Integer `T` encoded as big endian in `source`.
|
||||
template<typename T>
|
||||
T read_endian(epee::span<const std::uint8_t>& source)
|
||||
{
|
||||
static_assert(std::is_integral<T>::value, "must be integral type");
|
||||
static constexpr const std::size_t bits = 8 * sizeof(T);
|
||||
using buffer_type =
|
||||
boost::endian::endian_buffer<boost::endian::order::big, T, bits>;
|
||||
|
||||
buffer_type buffer;
|
||||
static_assert(sizeof(buffer) == sizeof(T), "unexpected buffer size");
|
||||
if (source.size() < sizeof(buffer))
|
||||
WIRE_DLOG_THROW_(wire::error::msgpack::not_enough_bytes);
|
||||
std::memcpy(std::addressof(buffer), source.data(), sizeof(buffer));
|
||||
source.remove_prefix(sizeof(buffer));
|
||||
return buffer.value();
|
||||
}
|
||||
|
||||
//! \return Integer `T` encoded as big endian in `source`.
|
||||
template<typename T, wire::msgpack::tag U>
|
||||
T read_endian(epee::span<const std::uint8_t>& source, const wire::msgpack::type<T, U>)
|
||||
{ return read_endian<T>(source); }
|
||||
|
||||
//! \return Integer `T` whose encoding is specified by tag `next`
|
||||
template<typename T>
|
||||
T read_integer(epee::span<const std::uint8_t>& source, const wire::msgpack::tag next)
|
||||
{
|
||||
try
|
||||
{
|
||||
// msgpack::integer_types
|
||||
switch (next)
|
||||
{
|
||||
default:
|
||||
break;
|
||||
case wire::msgpack::tag::int8:
|
||||
return boost::numeric_cast<T>(read_endian<std::int8_t>(source));
|
||||
case wire::msgpack::tag::uint8:
|
||||
return boost::numeric_cast<T>(read_endian<std::uint8_t>(source));
|
||||
case wire::msgpack::tag::int16:
|
||||
return boost::numeric_cast<T>(read_endian<std::int16_t>(source));
|
||||
case wire::msgpack::tag::uint16:
|
||||
return boost::numeric_cast<T>(read_endian<std::uint16_t>(source));
|
||||
case wire::msgpack::tag::int32:
|
||||
return boost::numeric_cast<T>(read_endian<std::int32_t>(source));
|
||||
case wire::msgpack::tag::uint32:
|
||||
return boost::numeric_cast<T>(read_endian<std::uint32_t>(source));
|
||||
case wire::msgpack::tag::int64:
|
||||
return boost::numeric_cast<T>(read_endian<std::int64_t>(source));
|
||||
case wire::msgpack::tag::uint64:
|
||||
return boost::numeric_cast<T>(read_endian<std::uint64_t>(source));
|
||||
}
|
||||
}
|
||||
catch (const boost::numeric::positive_overflow&)
|
||||
{ WIRE_DLOG_THROW_(wire::error::schema::smaller_integer); }
|
||||
catch (const boost::numeric::negative_overflow&)
|
||||
{ WIRE_DLOG_THROW_(wire::error::schema::larger_integer); }
|
||||
|
||||
WIRE_DLOG_THROW_(wire::error::schema::integer);
|
||||
}
|
||||
|
||||
epee::span<const std::uint8_t> read_raw(epee::span<const std::uint8_t>& source, const std::size_t bytes)
|
||||
{
|
||||
if (source.size() < bytes)
|
||||
WIRE_DLOG_THROW_(wire::error::msgpack::not_enough_bytes);
|
||||
const std::size_t actual = source.remove_prefix(bytes);
|
||||
return {source.data() - actual, actual};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
epee::span<const std::uint8_t> read_raw(epee::span<const std::uint8_t>& source)
|
||||
{
|
||||
return read_raw(source, wire::integer::cast_unsigned<std::size_t>(read_endian<T>(source)));
|
||||
}
|
||||
|
||||
epee::span<const std::uint8_t> read_string(epee::span<const std::uint8_t>& source, const wire::msgpack::tag next)
|
||||
{
|
||||
switch (next)
|
||||
{
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wswitch"
|
||||
MLWS_FIXED_STRING_TAGS()
|
||||
return read_raw(source, wire::msgpack::ftag_string::extract(next));
|
||||
#pragma GCC diagnostic pop
|
||||
case wire::msgpack::tag::string8:
|
||||
return read_raw<std::uint8_t>(source);
|
||||
case wire::msgpack::tag::string16:
|
||||
return read_raw<std::uint16_t>(source);
|
||||
case wire::msgpack::tag::string32:
|
||||
return read_raw<std::uint32_t>(source);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
WIRE_DLOG_THROW_(wire::error::schema::string);
|
||||
}
|
||||
|
||||
//! \return Binary blob encoded message
|
||||
epee::span<const std::uint8_t> read_binary(epee::span<const std::uint8_t>& source, const wire::msgpack::tag next)
|
||||
{
|
||||
switch (next)
|
||||
{
|
||||
case wire::msgpack::tag::binary8:
|
||||
return read_raw<std::uint8_t>(source);
|
||||
case wire::msgpack::tag::binary16:
|
||||
return read_raw<std::uint16_t>(source);
|
||||
case wire::msgpack::tag::binary32:
|
||||
return read_raw<std::uint32_t>(source);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
WIRE_DLOG_THROW_(wire::error::schema::string);
|
||||
}
|
||||
}
|
||||
|
||||
namespace wire
|
||||
{
|
||||
void msgpack_reader::throw_wire_exception()
|
||||
{
|
||||
WIRE_DLOG_THROW_(error::msgpack::underflow_tree);
|
||||
}
|
||||
|
||||
void msgpack_reader::skip_value()
|
||||
{
|
||||
assert(tags_remaining_);
|
||||
if (limits<std::size_t>::max() == tags_remaining_)
|
||||
WIRE_DLOG_THROW_(error::msgpack::max_tree_size);
|
||||
|
||||
const std::size_t initial = tags_remaining_;
|
||||
do
|
||||
{
|
||||
const std::size_t size = remaining_.size();
|
||||
const msgpack::tag next = peek_tag();
|
||||
switch (next)
|
||||
{
|
||||
default:
|
||||
break;
|
||||
case msgpack::tag::nil:
|
||||
case msgpack::tag::unused:
|
||||
case msgpack::tag::False:
|
||||
case msgpack::tag::True:
|
||||
remaining_.remove_prefix(1);
|
||||
break;
|
||||
case msgpack::tag::binary8:
|
||||
case msgpack::tag::binary16:
|
||||
case msgpack::tag::binary32:
|
||||
remaining_.remove_prefix(1);
|
||||
read_binary(remaining_, next);
|
||||
break;
|
||||
case msgpack::tag::extension8:
|
||||
remaining_.remove_prefix(1);
|
||||
read_raw<std::uint8_t>(remaining_);
|
||||
remaining_.remove_prefix(1);
|
||||
break;
|
||||
case msgpack::tag::extension16:
|
||||
remaining_.remove_prefix(1);
|
||||
read_raw<std::uint16_t>(remaining_);
|
||||
remaining_.remove_prefix(1);
|
||||
break;
|
||||
case msgpack::tag::extension32:
|
||||
remaining_.remove_prefix(1);
|
||||
read_raw<std::uint32_t>(remaining_);
|
||||
remaining_.remove_prefix(1);
|
||||
break;
|
||||
case msgpack::tag::int8:
|
||||
case msgpack::tag::uint8:
|
||||
remaining_.remove_prefix(2);
|
||||
break;
|
||||
case msgpack::tag::int16:
|
||||
case msgpack::tag::uint16:
|
||||
case msgpack::tag::fixed_extension1:
|
||||
remaining_.remove_prefix(3);
|
||||
break;
|
||||
case msgpack::tag::int32:
|
||||
case msgpack::tag::uint32:
|
||||
case msgpack::tag::float32:
|
||||
remaining_.remove_prefix(5);
|
||||
break;
|
||||
case msgpack::tag::int64:
|
||||
case msgpack::tag::uint64:
|
||||
case msgpack::tag::float64:
|
||||
remaining_.remove_prefix(9);
|
||||
break;
|
||||
case msgpack::tag::fixed_extension2:
|
||||
remaining_.remove_prefix(4);
|
||||
break;
|
||||
case msgpack::tag::fixed_extension4:
|
||||
remaining_.remove_prefix(6);
|
||||
break;
|
||||
case msgpack::tag::fixed_extension8:
|
||||
remaining_.remove_prefix(10);
|
||||
break;
|
||||
case msgpack::tag::fixed_extension16:
|
||||
remaining_.remove_prefix(18);
|
||||
break;
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wswitch"
|
||||
MLWS_FIXED_STRING_TAGS()
|
||||
case msgpack::tag::string8:
|
||||
case msgpack::tag::string16:
|
||||
case msgpack::tag::string32:
|
||||
remaining_.remove_prefix(1);
|
||||
read_string(remaining_, next);
|
||||
break;
|
||||
case msgpack::tag(0x90): case msgpack::tag(0x91): case msgpack::tag(0x92):
|
||||
case msgpack::tag(0x93): case msgpack::tag(0x94): case msgpack::tag(0x95):
|
||||
case msgpack::tag(0x96): case msgpack::tag(0x97): case msgpack::tag(0x98):
|
||||
case msgpack::tag(0x99): case msgpack::tag(0x9a): case msgpack::tag(0x9b):
|
||||
case msgpack::tag(0x9c): case msgpack::tag(0x9d): case msgpack::tag(0x9e):
|
||||
case msgpack::tag(0x9f):
|
||||
case msgpack::tag::array16:
|
||||
case msgpack::tag::array32:
|
||||
start_array(0);
|
||||
break;
|
||||
case msgpack::tag(0x80): case msgpack::tag(0x81): case msgpack::tag(0x82):
|
||||
case msgpack::tag(0x83): case msgpack::tag(0x84): case msgpack::tag(0x85):
|
||||
case msgpack::tag(0x86): case msgpack::tag(0x87): case msgpack::tag(0x88):
|
||||
case msgpack::tag(0x89): case msgpack::tag(0x8a): case msgpack::tag(0x8b):
|
||||
case msgpack::tag(0x8c): case msgpack::tag(0x8d): case msgpack::tag(0x8e):
|
||||
case msgpack::tag(0x8f):
|
||||
case msgpack::tag::object16:
|
||||
case msgpack::tag::object32:
|
||||
start_object();
|
||||
break;
|
||||
#pragma GCC diagnostic pop
|
||||
};
|
||||
|
||||
if (size == remaining_.size())
|
||||
{
|
||||
if (!msgpack::ftag_unsigned::matches(next) && !msgpack::ftag_signed::matches(next))
|
||||
WIRE_DLOG_THROW_(error::msgpack::invalid);
|
||||
remaining_.remove_prefix(1);
|
||||
}
|
||||
update_tags_remaining();
|
||||
} while (initial <= tags_remaining_);
|
||||
}
|
||||
|
||||
msgpack::tag msgpack_reader::peek_tag()
|
||||
{
|
||||
if (remaining_.empty())
|
||||
WIRE_DLOG_THROW_(error::msgpack::not_enough_bytes);
|
||||
return msgpack::tag(*remaining_.data());
|
||||
}
|
||||
|
||||
msgpack::tag msgpack_reader::get_tag()
|
||||
{
|
||||
const msgpack::tag next = peek_tag();
|
||||
remaining_.remove_prefix(1);
|
||||
return next;
|
||||
}
|
||||
|
||||
std::intmax_t msgpack_reader::do_integer(const msgpack::tag next)
|
||||
{
|
||||
if (msgpack::ftag_signed::matches(next))
|
||||
return *reinterpret_cast<const std::int8_t*>(std::addressof(next)); // special case
|
||||
return read_integer<std::intmax_t>(remaining_, next);
|
||||
}
|
||||
|
||||
std::uintmax_t msgpack_reader::do_unsigned_integer(const msgpack::tag next)
|
||||
{
|
||||
return read_integer<std::uintmax_t>(remaining_, next);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
std::size_t msgpack_reader::read_count(const error::schema expected)
|
||||
{
|
||||
const msgpack::tag next = get_tag();
|
||||
if (T::matches(next))
|
||||
return T::extract(next);
|
||||
|
||||
std::size_t out = 0;
|
||||
const auto matched_type = [this, &out, next](const auto type)
|
||||
{
|
||||
if (type.Tag() == next)
|
||||
{
|
||||
out = integer::cast_unsigned<std::size_t>(read_endian(remaining_, type));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if (!boost::fusion::any(U{}, matched_type))
|
||||
WIRE_DLOG_THROW_(expected);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void msgpack_reader::check_complete() const
|
||||
{
|
||||
if (tags_remaining_)
|
||||
WIRE_DLOG_THROW_(error::msgpack::incomplete);
|
||||
}
|
||||
|
||||
bool msgpack_reader::boolean()
|
||||
{
|
||||
update_tags_remaining();
|
||||
switch (get_tag())
|
||||
{
|
||||
case msgpack::tag::True:
|
||||
return true;
|
||||
case msgpack::tag::False:
|
||||
return false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
WIRE_DLOG_THROW_(error::schema::boolean);
|
||||
}
|
||||
|
||||
double msgpack_reader::real()
|
||||
{
|
||||
update_tags_remaining();
|
||||
|
||||
const auto read_float = [this](auto value)
|
||||
{
|
||||
if (remaining_.size() < sizeof(value))
|
||||
WIRE_DLOG_THROW_(error::msgpack::not_enough_bytes);
|
||||
std::memcpy(std::addressof(value), remaining_.data(), sizeof(value));
|
||||
remaining_.remove_prefix(sizeof(value));
|
||||
return value;
|
||||
};
|
||||
|
||||
switch (get_tag())
|
||||
{
|
||||
case msgpack::tag::float32:
|
||||
return read_float(float(0));
|
||||
case msgpack::tag::float64:
|
||||
return read_float(double(0));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
WIRE_DLOG_THROW_(error::schema::number);
|
||||
}
|
||||
|
||||
std::string msgpack_reader::string()
|
||||
{
|
||||
update_tags_remaining();
|
||||
const epee::span<const std::uint8_t> bytes = read_string(remaining_, get_tag());
|
||||
return std::string{reinterpret_cast<const char*>(bytes.data()), bytes.size()};
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> msgpack_reader::binary()
|
||||
{
|
||||
update_tags_remaining();
|
||||
const epee::span<const std::uint8_t> bytes = read_binary(remaining_, get_tag());
|
||||
return std::vector<std::uint8_t>{bytes.begin(), bytes.end()};
|
||||
}
|
||||
|
||||
void msgpack_reader::binary(epee::span<std::uint8_t> dest)
|
||||
{
|
||||
update_tags_remaining();
|
||||
const epee::span<const std::uint8_t> bytes = read_binary(remaining_, get_tag());
|
||||
if (dest.size() != bytes.size())
|
||||
WIRE_DLOG_THROW(error::schema::fixed_binary, "of size " << dest.size() << " but got " << bytes.size());
|
||||
std::memcpy(dest.data(), bytes.data(), dest.size());
|
||||
}
|
||||
|
||||
std::size_t msgpack_reader::start_array(const std::size_t min_element_size)
|
||||
{
|
||||
const std::size_t upcoming =
|
||||
read_count<msgpack::ftag_array, msgpack::array_types>(error::schema::array);
|
||||
if (limits<std::size_t>::max() - tags_remaining_ < upcoming)
|
||||
WIRE_DLOG_THROW_(error::msgpack::max_tree_size);
|
||||
if (min_element_size && (remaining_.size() / min_element_size) < upcoming)
|
||||
WIRE_DLOG_THROW(error::schema::array, upcoming << " array elements of at least " << min_element_size << " bytes each exceeds " << remaining_.size() << " remaining bytes");
|
||||
|
||||
tags_remaining_ += upcoming;
|
||||
increment_depth();
|
||||
return upcoming;
|
||||
}
|
||||
|
||||
bool msgpack_reader::is_array_end(const std::size_t count)
|
||||
{
|
||||
if (count)
|
||||
return false;
|
||||
update_tags_remaining();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::size_t msgpack_reader::start_object()
|
||||
{
|
||||
const std::size_t upcoming =
|
||||
read_count<msgpack::ftag_object, msgpack::object_types>(error::schema::object);
|
||||
if (limits<std::size_t>::max() / 2 < upcoming)
|
||||
WIRE_DLOG_THROW_(error::msgpack::max_tree_size);
|
||||
if (limits<std::size_t>::max() - tags_remaining_ < upcoming * 2)
|
||||
WIRE_DLOG_THROW_(error::msgpack::max_tree_size);
|
||||
tags_remaining_ += upcoming * 2;
|
||||
increment_depth();
|
||||
return upcoming;
|
||||
}
|
||||
|
||||
bool msgpack_reader::key(const epee::span<const key_map> map, std::size_t& state, std::size_t& index)
|
||||
{
|
||||
index = map.size();
|
||||
for ( ;state; --state)
|
||||
{
|
||||
update_tags_remaining(); // for key
|
||||
const msgpack::tag next = get_tag();
|
||||
const bool single = msgpack::ftag_unsigned::matches(next);
|
||||
if (single || matches<msgpack::unsigned_types>(next))
|
||||
{
|
||||
unsigned key = std::uint8_t(next);
|
||||
if (!single)
|
||||
key = read_integer<unsigned>(remaining_, next);
|
||||
for (const key_map& elem : map)
|
||||
{
|
||||
if (elem.id == key)
|
||||
{
|
||||
index = std::addressof(elem) - map.begin();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (msgpack::ftag_string::matches(next) || matches<msgpack::string_types>(next))
|
||||
{
|
||||
const epee::span<const std::uint8_t> key = read_string(remaining_, next);
|
||||
for (const key_map& elem : map)
|
||||
{
|
||||
const boost::string_ref elem_{elem.name};
|
||||
if (key.size() == elem_.size() && std::memcmp(key.data(), elem_.data(), key.size()) == 0)
|
||||
{
|
||||
index = std::addressof(elem) - map.begin();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
WIRE_DLOG_THROW(error::schema::invalid_key, "Invalid key type");
|
||||
|
||||
if (index < map.size())
|
||||
{
|
||||
--state;
|
||||
return true;
|
||||
}
|
||||
skip_value();
|
||||
} // until state == 0
|
||||
update_tags_remaining(); // for end of object
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
// 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 <cstddef>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "wire/field.h"
|
||||
#include "wire/msgpack/base.h"
|
||||
#include "wire/read.h"
|
||||
#include "wire/traits.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
//! Reads MSGPACK tokens one-at-a-time for DOMless parsing
|
||||
class msgpack_reader : public reader
|
||||
{
|
||||
epee::byte_slice source_;
|
||||
std::size_t tags_remaining_; //!< Expected number of elements remaining
|
||||
|
||||
//! \throw wire::exception with `error::msgpack::underflow_tree`
|
||||
[[noreturn]] void throw_wire_exception();
|
||||
//! Decrement tags_remaining_ if not zero, \throw std::logic_error when `tags_remaining_ == 0`.
|
||||
void update_tags_remaining()
|
||||
{
|
||||
if (tags_remaining_)
|
||||
--tags_remaining_;
|
||||
else
|
||||
throw_wire_exception();
|
||||
}
|
||||
|
||||
//! Skips next value. \throw wire::exception if invalid JSON syntax.
|
||||
void skip_value();
|
||||
|
||||
//! \return Next tag but leave `remaining_` untouched.
|
||||
msgpack::tag peek_tag();
|
||||
//! \return Next tag and remove first byte from `remaining_`.
|
||||
msgpack::tag get_tag();
|
||||
|
||||
//! \return Integer from `remaining_` where positive fixed tag has been checked.
|
||||
std::intmax_t do_integer(msgpack::tag);
|
||||
//! \return Integer from `remaining_` where fixed tag has been checked.
|
||||
std::uintmax_t do_unsigned_integer(msgpack::tag);
|
||||
|
||||
//! \return Number of items determined by `T` fixed tag and `U` tuple of tags.
|
||||
template<typename T, typename U>
|
||||
std::size_t read_count(error::schema);
|
||||
|
||||
public:
|
||||
explicit msgpack_reader(epee::byte_slice&& source)
|
||||
: reader(nullptr), source_(std::move(source)), tags_remaining_(1)
|
||||
{
|
||||
remaining_ = {source_.data(), source_.size()};
|
||||
}
|
||||
|
||||
//! \throw wire::exception if JSON parsing is incomplete.
|
||||
void check_complete() const override final;
|
||||
|
||||
//! \throw wire::exception if next token not a boolean.
|
||||
bool boolean() override final;
|
||||
|
||||
//! \throw wire::expception if next token not an integer.
|
||||
std::intmax_t integer() override final
|
||||
{
|
||||
update_tags_remaining();
|
||||
const msgpack::tag next = get_tag();
|
||||
if (std::uint8_t(next) <= msgpack::ftag_unsigned::max())
|
||||
return std::uint8_t(next);
|
||||
return do_integer(next);
|
||||
}
|
||||
|
||||
//! \throw wire::exception if next token not an unsigned integer.
|
||||
std::uintmax_t unsigned_integer() override final
|
||||
{
|
||||
update_tags_remaining();
|
||||
const msgpack::tag next = get_tag();
|
||||
if (std::uint8_t(next) <= msgpack::ftag_unsigned::max())
|
||||
return std::uint8_t(next);
|
||||
return do_unsigned_integer(next);
|
||||
}
|
||||
|
||||
//! \throw wire::exception if next token not a valid real number
|
||||
double real() override final;
|
||||
|
||||
//! \throw wire::exception if next token not a string
|
||||
std::string string() override final;
|
||||
|
||||
//! \throw wire::exception if next token cannot be read as hex
|
||||
std::vector<std::uint8_t> binary() override final;
|
||||
|
||||
//! \throw wire::exception if next token cannot be read as hex into `dest`.
|
||||
void binary(epee::span<std::uint8_t> dest) override final;
|
||||
|
||||
|
||||
//! \throw wire::exception if next token not `[`.
|
||||
std::size_t start_array(std::size_t min_element_size) override final;
|
||||
|
||||
//! \return true when `count == 0`.
|
||||
bool is_array_end(const std::size_t count) override final;
|
||||
|
||||
|
||||
//! \throw wire::exception if next token not `{`.
|
||||
std::size_t start_object() override final;
|
||||
|
||||
/*! \throw wire::exception if next token not key or `}`.
|
||||
\param[out] index of key match within `map`.
|
||||
\return True if another value to read. */
|
||||
bool key(epee::span<const key_map> map, std::size_t&, std::size_t& index) override final;
|
||||
};
|
||||
} // wire
|
||||
@@ -0,0 +1,212 @@
|
||||
// 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.
|
||||
|
||||
#include "write.h"
|
||||
|
||||
#include <boost/endian/buffers.hpp>
|
||||
#include <boost/fusion/include/any.hpp>
|
||||
#include <boost/fusion/include/std_tuple.hpp>
|
||||
#include <cassert>
|
||||
#include <limits>
|
||||
#include <ostream>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
#include "wire/error.h"
|
||||
#include "wire/msgpack/error.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
template<typename T>
|
||||
using limits = std::numeric_limits<T>;
|
||||
|
||||
constexpr const unsigned flush_threshold = 100;
|
||||
constexpr const unsigned max_buffer = 4096;
|
||||
|
||||
void write_tag(epee::byte_stream& bytes, const wire::msgpack::tag value)
|
||||
{
|
||||
bytes.put(std::uint8_t(value));
|
||||
}
|
||||
|
||||
template<typename T, typename U, wire::msgpack::tag tag>
|
||||
void write_endian(epee::byte_stream& bytes, const T value, const wire::msgpack::type<U, tag> type)
|
||||
{
|
||||
static_assert(std::is_integral<T>::value, "input not integral");
|
||||
static_assert(std::is_integral<U>::value, "output not integral");
|
||||
|
||||
using in_limits = std::numeric_limits<T>;
|
||||
using out_limits = std::numeric_limits<U>;
|
||||
static_assert(in_limits::is_signed == out_limits::is_signed, "signs must match");
|
||||
|
||||
assert(type.min() <= value);
|
||||
assert(value <= type.max());
|
||||
|
||||
static constexpr const std::size_t bits = 8 * sizeof(U);
|
||||
using buffer_type =
|
||||
boost::endian::endian_buffer<boost::endian::order::big, U, bits>;
|
||||
|
||||
buffer_type buffer(value);
|
||||
write_tag(bytes, type.Tag());
|
||||
bytes.write(buffer.data(), sizeof(buffer));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void write_count(epee::byte_stream& bytes, const std::uintmax_t items)
|
||||
{
|
||||
const auto match_size = [&bytes, items] (const auto type) -> bool
|
||||
{
|
||||
if (type.max() < items)
|
||||
return false;
|
||||
write_endian(bytes, items, type);
|
||||
return true;
|
||||
};
|
||||
if (!boost::fusion::any(T{}, match_size))
|
||||
WIRE_DLOG_THROW_(wire::error::msgpack::integer_encoding);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
void write_count(epee::byte_stream& bytes, const std::uintmax_t items)
|
||||
{
|
||||
if (items <= T::max())
|
||||
bytes.put(T::tag() | std::uint8_t(items));
|
||||
else
|
||||
write_count<U>(bytes, items);
|
||||
}
|
||||
}
|
||||
|
||||
namespace wire
|
||||
{
|
||||
void msgpack_writer::do_flush(epee::span<const std::uint8_t>)
|
||||
{}
|
||||
|
||||
void msgpack_writer::check_flush()
|
||||
{
|
||||
if (needs_flush_ && (max_buffer < bytes_.size() || bytes_.available() < flush_threshold))
|
||||
flush();
|
||||
}
|
||||
|
||||
void msgpack_writer::do_integer(const std::intmax_t value)
|
||||
{
|
||||
assert(value < 0); // constraint checked in header
|
||||
if (0xe0 < value) // 0xe0 needs to be type `int` to work
|
||||
{
|
||||
bytes_.put(std::uint8_t(value));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto match_size = [this, value] (const auto type) -> bool
|
||||
{
|
||||
if (value < type.min())
|
||||
return false;
|
||||
write_endian(bytes_, value, type);
|
||||
return true;
|
||||
};
|
||||
if (!boost::fusion::any(wire::msgpack::signed_types{}, match_size))
|
||||
WIRE_DLOG_THROW_(wire::error::msgpack::integer_encoding);
|
||||
}
|
||||
|
||||
void msgpack_writer::do_unsigned_integer(const std::uintmax_t value)
|
||||
{
|
||||
const auto match_size = [this, value] (const auto type) -> bool
|
||||
{
|
||||
if (type.max() < value)
|
||||
return false;
|
||||
write_endian(bytes_, value, type);
|
||||
return true;
|
||||
};
|
||||
if (!boost::fusion::any(wire::msgpack::unsigned_types{}, match_size))
|
||||
WIRE_DLOG_THROW_(wire::error::msgpack::integer_encoding);
|
||||
}
|
||||
|
||||
void msgpack_writer::check_complete()
|
||||
{
|
||||
if (expected_)
|
||||
throw std::logic_error{"msgpack_writer::take_msgpack() failed with incomplete tree"};
|
||||
}
|
||||
epee::byte_stream msgpack_writer::take_msgpack()
|
||||
{
|
||||
check_complete();
|
||||
epee::byte_stream out{std::move(bytes_)};
|
||||
bytes_.clear();
|
||||
return out;
|
||||
}
|
||||
|
||||
msgpack_writer::~msgpack_writer() noexcept
|
||||
{}
|
||||
|
||||
void msgpack_writer::real(const double source)
|
||||
{
|
||||
write_tag(bytes_, msgpack::tag::float64);
|
||||
bytes_.write(reinterpret_cast<const char*>(std::addressof(source)), sizeof(source));
|
||||
--expected_;
|
||||
}
|
||||
|
||||
void msgpack_writer::string(const boost::string_ref source)
|
||||
{
|
||||
write_count<msgpack::ftag_string, msgpack::string_types>(bytes_, source.size());
|
||||
bytes_.write(source.data(), source.size());
|
||||
--expected_;
|
||||
}
|
||||
void msgpack_writer::binary(epee::span<const std::uint8_t> source)
|
||||
{
|
||||
write_count<msgpack::binary_types>(bytes_, source.size());
|
||||
bytes_.write(source);
|
||||
--expected_;
|
||||
}
|
||||
|
||||
void msgpack_writer::start_array(const std::size_t items)
|
||||
{
|
||||
write_count<msgpack::ftag_array, msgpack::array_types>(bytes_, items);
|
||||
expected_ += items;
|
||||
}
|
||||
|
||||
void msgpack_writer::start_object(const std::size_t items)
|
||||
{
|
||||
write_count<msgpack::ftag_object, msgpack::object_types>(bytes_, items);
|
||||
expected_ += items * 2;
|
||||
}
|
||||
void msgpack_writer::key(const boost::string_ref str)
|
||||
{
|
||||
string(str);
|
||||
}
|
||||
void msgpack_writer::key(const std::uintmax_t id)
|
||||
{
|
||||
unsigned_integer(id);
|
||||
}
|
||||
void msgpack_writer::key(const unsigned id, boost::string_ref str)
|
||||
{
|
||||
if (integer_keys_)
|
||||
key(id);
|
||||
else
|
||||
key(str);
|
||||
}
|
||||
|
||||
void msgpack_stream_writer::do_flush(epee::span<const std::uint8_t> bytes)
|
||||
{
|
||||
dest.write(reinterpret_cast<const char*>(bytes.data()), bytes.size());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
// 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 <array>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <cstdint>
|
||||
#include <iosfwd>
|
||||
#include <limits>
|
||||
|
||||
#include "byte_stream.h" // monero/contrib/epee/include
|
||||
#include "span.h" // monero/contrib/epee/include
|
||||
#include "wire/field.h"
|
||||
#include "wire/filters.h"
|
||||
#include "wire/msgpack/base.h"
|
||||
#include "wire/traits.h"
|
||||
#include "wire/write.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
//! Writes MSGPACK tokens one-at-a-time for DOMless output.
|
||||
class msgpack_writer : public writer
|
||||
{
|
||||
epee::byte_stream bytes_;
|
||||
std::size_t expected_; //! Tracks number of expected elements remaining
|
||||
const bool integer_keys_;
|
||||
bool needs_flush_;
|
||||
|
||||
//! Provided data currently in `bytes_`.
|
||||
virtual void do_flush(epee::span<const uint8_t>);
|
||||
|
||||
//! Flush written bytes to `do_flush(...)` if configured
|
||||
void check_flush();
|
||||
|
||||
void do_integer(std::intmax_t);
|
||||
void do_unsigned_integer(std::uintmax_t);
|
||||
|
||||
template<typename T>
|
||||
void integer_t(const T value)
|
||||
{
|
||||
if (0 <= value)
|
||||
{
|
||||
if (value <= msgpack::ftag_unsigned::max())
|
||||
bytes_.put(std::uint8_t(value));
|
||||
else // if multibyte
|
||||
do_unsigned_integer(std::uintmax_t(value));
|
||||
}
|
||||
else // if negative
|
||||
do_integer(value);
|
||||
--expected_;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void unsigned_integer_t(const T value)
|
||||
{
|
||||
if (value <= msgpack::ftag_unsigned::max())
|
||||
bytes_.put(std::uint8_t(value));
|
||||
else // if multibyte
|
||||
do_unsigned_integer(value);
|
||||
--expected_;
|
||||
}
|
||||
|
||||
protected:
|
||||
msgpack_writer(epee::byte_stream&& sink, bool integer_keys, bool needs_flush)
|
||||
: writer(), bytes_(std::move(sink)), expected_(1), integer_keys_(integer_keys), needs_flush_(needs_flush)
|
||||
{}
|
||||
|
||||
//! \throw std::logic_error if tree was not completed
|
||||
void check_complete();
|
||||
|
||||
//! \throw std::logic_error if incomplete msgpack tree. \return msgpack bytes
|
||||
epee::byte_stream take_msgpack();
|
||||
|
||||
//! Flush bytes in local buffer to `do_flush(...)`
|
||||
void flush()
|
||||
{
|
||||
do_flush({bytes_.data(), bytes_.size()});
|
||||
bytes_.clear();
|
||||
}
|
||||
|
||||
public:
|
||||
msgpack_writer(const msgpack_writer&) = delete;
|
||||
virtual ~msgpack_writer() noexcept;
|
||||
msgpack_writer& operator=(const msgpack_writer&) = delete;
|
||||
|
||||
void boolean(const bool value) override final
|
||||
{
|
||||
if (value)
|
||||
bytes_.put(std::uint8_t(msgpack::tag::True));
|
||||
else
|
||||
bytes_.put(std::uint8_t(msgpack::tag::False));
|
||||
--expected_;
|
||||
}
|
||||
|
||||
void integer(const int value) override final
|
||||
{ integer_t(value); }
|
||||
|
||||
void integer(const std::intmax_t value) override final
|
||||
{ integer_t(value); }
|
||||
|
||||
void unsigned_integer(const unsigned value) override final
|
||||
{ unsigned_integer_t(value); }
|
||||
|
||||
void unsigned_integer(const std::uintmax_t value) override final
|
||||
{ unsigned_integer_t(value); }
|
||||
|
||||
void real(double) override final;
|
||||
|
||||
//! \throw wire::exception if `source.size()` exceeds 2^32-1
|
||||
void string(boost::string_ref source) override final;
|
||||
//! \throw wire::exception if `source.size()` exceeds 2^32-1
|
||||
void binary(epee::span<const std::uint8_t> source) override final;
|
||||
|
||||
//! \throw wire::exception if `items` exceeds 2^32-1.
|
||||
void start_array(std::size_t items) override final;
|
||||
void end_array() override final { --expected_; }
|
||||
|
||||
//! \throw wire::exception if `items` exceeds 2^32-1
|
||||
void start_object(std::size_t items) override final;
|
||||
void key(std::uintmax_t) override final;
|
||||
void key(boost::string_ref) override final;
|
||||
void key(unsigned, boost::string_ref) override final;
|
||||
void end_object() override final { --expected_; }
|
||||
};
|
||||
|
||||
//! Buffers entire JSON message in memory
|
||||
struct msgpack_slice_writer final : msgpack_writer
|
||||
{
|
||||
explicit msgpack_slice_writer(epee::byte_stream&& sink, bool integer_keys = false)
|
||||
: msgpack_writer(std::move(sink), integer_keys, false)
|
||||
{}
|
||||
|
||||
explicit msgpack_slice_writer(bool integer_keys = false)
|
||||
: msgpack_slice_writer(epee::byte_stream{}, integer_keys)
|
||||
{}
|
||||
|
||||
//! \throw std::logic_error if incomplete JSON tree \return JSON bytes
|
||||
epee::byte_stream take_sink()
|
||||
{
|
||||
return msgpack_writer::take_msgpack();
|
||||
}
|
||||
};
|
||||
|
||||
//! Periodically flushes MsgPack data to `std::ostream`
|
||||
class msgpack_stream_writer final : public msgpack_writer
|
||||
{
|
||||
std::ostream& dest;
|
||||
|
||||
virtual void do_flush(epee::span<const std::uint8_t>) override final;
|
||||
public:
|
||||
explicit msgpack_stream_writer(std::ostream& dest, bool integer_keys = false)
|
||||
: msgpack_writer(epee::byte_stream{}, integer_keys, true), dest(dest)
|
||||
{}
|
||||
|
||||
//! Flush remaining bytes to stream \throw std::logic_error if incomplete JSON tree
|
||||
void finish()
|
||||
{
|
||||
check_complete();
|
||||
flush();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
// 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 "wire/read.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
void wire::reader::increment_depth()
|
||||
{
|
||||
if (++depth_ == max_read_depth())
|
||||
WIRE_DLOG_THROW_(error::schema::maximum_depth);
|
||||
}
|
||||
|
||||
void wire::reader::decrement_depth()
|
||||
{
|
||||
if (!depth_)
|
||||
throw std::logic_error{"reader::decrement_depth() already at zero"};
|
||||
--depth_;
|
||||
}
|
||||
|
||||
[[noreturn]] void wire::integer::throw_exception(std::intmax_t source, std::intmax_t min, std::intmax_t max)
|
||||
{
|
||||
static_assert(
|
||||
std::numeric_limits<std::intmax_t>::max() <= std::numeric_limits<std::uintmax_t>::max(),
|
||||
"expected intmax_t::max <= uintmax_t::max"
|
||||
);
|
||||
if (source < 0)
|
||||
WIRE_DLOG_THROW(error::schema::larger_integer, source << " given when " << min << " is minimum permitted");
|
||||
else
|
||||
throw_exception(std::uintmax_t(source), std::uintmax_t(max));
|
||||
}
|
||||
[[noreturn]] void wire::integer::throw_exception(std::uintmax_t source, std::uintmax_t max)
|
||||
{
|
||||
WIRE_DLOG_THROW(error::schema::smaller_integer, source << " given when " << max << "is maximum permitted");
|
||||
}
|
||||
|
||||
[[noreturn]] void wire_read::throw_exception(const wire::error::schema code, const char* display, epee::span<char const* const> names)
|
||||
{
|
||||
const char* name = nullptr;
|
||||
for (const char* elem : names)
|
||||
{
|
||||
if (elem != nullptr)
|
||||
{
|
||||
name = elem;
|
||||
break;
|
||||
}
|
||||
}
|
||||
WIRE_DLOG_THROW(code, display << (name ? name : ""));
|
||||
}
|
||||
|
||||
|
||||
+505
@@ -0,0 +1,505 @@
|
||||
// Copyright (c) 2020-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 <limits>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "common/expect.h" // monero/src
|
||||
#include "span.h" // monero/contrib/epee/include
|
||||
#include "wire/error.h"
|
||||
#include "wire/field.h"
|
||||
#include "wire/traits.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
//! Interface for converting "wire" (byte) formats to C/C++ objects without a DOM.
|
||||
class reader
|
||||
{
|
||||
std::size_t depth_; //!< Tracks number of recursive objects and arrays
|
||||
|
||||
protected:
|
||||
epee::span<const std::uint8_t> remaining_; //!< Derived class tracks unprocessed bytes here
|
||||
|
||||
reader(const epee::span<const std::uint8_t> remaining) noexcept
|
||||
: depth_(0), remaining_(remaining)
|
||||
{}
|
||||
|
||||
reader(const reader&) = default;
|
||||
reader(reader&&) = default;
|
||||
reader& operator=(const reader&) = default;
|
||||
reader& operator=(reader&&) = default;
|
||||
|
||||
//! \throw wire::exception if max depth is reached
|
||||
void increment_depth();
|
||||
|
||||
//! \throw std::logic_error if already `depth() == 0`.
|
||||
void decrement_depth();
|
||||
|
||||
public:
|
||||
struct key_map
|
||||
{
|
||||
const char* name;
|
||||
unsigned id; //<! For integer key formats;
|
||||
};
|
||||
|
||||
//! \return Maximum read depth for both objects and arrays before erroring
|
||||
static constexpr std::size_t max_read_depth() noexcept { return 100; }
|
||||
|
||||
//! \return Assume delimited arrays in generic interface (some optimizations disabled)
|
||||
static constexpr std::true_type delimited_arrays() noexcept { return {}; }
|
||||
|
||||
virtual ~reader() noexcept
|
||||
{}
|
||||
|
||||
//! \return Number of recursive objects and arrays
|
||||
std::size_t depth() const noexcept { return depth_; }
|
||||
|
||||
//! \return Unprocessed bytes
|
||||
epee::span<const std::uint8_t> remaining() const noexcept { return remaining_; }
|
||||
|
||||
//! \throw wire::exception if parsing is incomplete.
|
||||
virtual void check_complete() const = 0;
|
||||
|
||||
//! \throw wire::exception if next value not a boolean.
|
||||
virtual bool boolean() = 0;
|
||||
|
||||
//! \throw wire::exception if next value not an integer.
|
||||
virtual std::intmax_t integer() = 0;
|
||||
|
||||
//! \throw wire::exception if next value not an unsigned integer.
|
||||
virtual std::uintmax_t unsigned_integer() = 0;
|
||||
|
||||
//! \throw wire::exception if next value not number
|
||||
virtual double real() = 0;
|
||||
|
||||
//! throw wire::exception if next value not string
|
||||
virtual std::string string() = 0;
|
||||
|
||||
// ! \throw wire::exception if next value cannot be read as binary
|
||||
virtual std::vector<std::uint8_t> binary() = 0;
|
||||
|
||||
//! \throw wire::exception if next value cannot be read as binary into `dest`.
|
||||
virtual void binary(epee::span<std::uint8_t> dest) = 0;
|
||||
|
||||
/* \param min_element_size of each array element in any format - if known.
|
||||
Derived types with explicit element count should verify available
|
||||
space, and throw a `wire::exception` on issues.
|
||||
\throw wire::exception if next value not array
|
||||
\throw wire::exception if not enough bytes for all array elements
|
||||
(with epee/msgpack which has specified number of elements).
|
||||
\return Number of values to read before calling `is_array_end()` */
|
||||
virtual std::size_t start_array(std::size_t min_element_size) = 0;
|
||||
|
||||
//! \return True if there is another element to read.
|
||||
virtual bool is_array_end(std::size_t count) = 0;
|
||||
|
||||
//! \throw wire::exception if array end delimiter not present.
|
||||
void end_array() { decrement_depth(); }
|
||||
|
||||
|
||||
//! \throw wire::exception if not object begin. \return State to be given to `key(...)` function.
|
||||
virtual std::size_t start_object() = 0;
|
||||
|
||||
/*! Read a key of an object field and match against a known list of keys.
|
||||
Skips or throws exceptions on unknown fields depending on implementation
|
||||
settings.
|
||||
|
||||
\param map of known keys (strings and integer) that are valid.
|
||||
\param[in,out] state returned by `start_object()` or `key(...)` whichever
|
||||
was last.
|
||||
\param[out] index of match found in `map`.
|
||||
|
||||
\throw wire::exception if next value not a key.
|
||||
\throw wire::exception if next key not found in `map` and skipping
|
||||
fields disabled.
|
||||
|
||||
\return True if this function found a field in `map` to process.
|
||||
*/
|
||||
virtual bool key(epee::span<const key_map> map, std::size_t& state, std::size_t& index) = 0;
|
||||
|
||||
void end_object() { decrement_depth(); }
|
||||
};
|
||||
|
||||
template<typename R>
|
||||
inline void read_bytes(R& source, bool& dest)
|
||||
{ dest = source.boolean(); }
|
||||
|
||||
template<typename R>
|
||||
inline void read_bytes(R& source, double& dest)
|
||||
{ dest = source.real(); }
|
||||
|
||||
template<typename R>
|
||||
inline void read_bytes(R& source, std::string& dest)
|
||||
{ dest = source.string(); }
|
||||
|
||||
template<typename R>
|
||||
inline void read_bytes(R& source, std::vector<std::uint8_t>& dest)
|
||||
{ dest = source.binary(); }
|
||||
|
||||
template<typename R, typename T>
|
||||
inline std::enable_if_t<is_blob<T>::value> read_bytes(R& source, T& dest)
|
||||
{ source.binary(epee::as_mut_byte_span(dest)); }
|
||||
|
||||
//! Use `read_bytes(...)` method if available for `T`.
|
||||
template<typename R, typename T>
|
||||
inline auto read_bytes(R& source, T& dest) -> decltype(dest.read_bytes(source))
|
||||
{ return dest.read_bytes(source); }
|
||||
|
||||
namespace integer
|
||||
{
|
||||
[[noreturn]] void throw_exception(std::intmax_t value, std::intmax_t min, std::intmax_t max);
|
||||
[[noreturn]] void throw_exception(std::uintmax_t value, std::uintmax_t max);
|
||||
|
||||
template<typename T, typename U>
|
||||
inline T cast_signed(const U source)
|
||||
{
|
||||
using limit = std::numeric_limits<T>;
|
||||
static_assert(
|
||||
std::is_signed<T>::value && std::is_integral<T>::value,
|
||||
"target must be signed integer type"
|
||||
);
|
||||
static_assert(
|
||||
std::is_signed<U>::value && std::is_integral<U>::value,
|
||||
"source must be signed integer type"
|
||||
);
|
||||
if (source < limit::min() || limit::max() < source)
|
||||
throw_exception(source, limit::min(), limit::max());
|
||||
return static_cast<T>(source);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
inline T cast_unsigned(const U source)
|
||||
{
|
||||
using limit = std::numeric_limits<T>;
|
||||
static_assert(
|
||||
std::is_unsigned<T>::value && std::is_integral<T>::value,
|
||||
"target must be unsigned integer type"
|
||||
);
|
||||
static_assert(
|
||||
std::is_unsigned<U>::value && std::is_integral<U>::value,
|
||||
"source must be unsigned integer type"
|
||||
);
|
||||
if (limit::max() < source)
|
||||
throw_exception(source, limit::max());
|
||||
return static_cast<T>(source);
|
||||
}
|
||||
}
|
||||
|
||||
//! read all current and future signed integer types
|
||||
template<typename R, typename T>
|
||||
inline std::enable_if_t<std::is_signed<T>::value && std::is_integral<T>::value>
|
||||
read_bytes(R& source, T& dest)
|
||||
{
|
||||
dest = integer::cast_signed<T>(source.integer());
|
||||
}
|
||||
|
||||
//! read all current and future unsigned integer types
|
||||
template<typename R, typename T>
|
||||
inline std::enable_if_t<std::is_unsigned<T>::value && std::is_integral<T>::value>
|
||||
read_bytes(R& source, T& dest)
|
||||
{
|
||||
dest = integer::cast_unsigned<T>(source.unsigned_integer());
|
||||
}
|
||||
} // wire
|
||||
|
||||
namespace wire_read
|
||||
{
|
||||
/*! Don't add a function called `read_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
|
||||
`read_bytes` in this namespace to "find" user functions that are declared
|
||||
after these functions (the technique behind `boost::serialization`). */
|
||||
|
||||
[[noreturn]] void throw_exception(wire::error::schema code, const char* display, epee::span<char const* const> name_list);
|
||||
|
||||
template<typename R, typename T>
|
||||
inline void bytes(R& source, T&& dest)
|
||||
{
|
||||
read_bytes(source, dest); // ADL (searches every associated namespace)
|
||||
}
|
||||
|
||||
//! \return `T` converted from `source` or error.
|
||||
template<typename R, typename T, typename U>
|
||||
inline std::error_code from_bytes(T&& source, U& dest)
|
||||
{
|
||||
try
|
||||
{
|
||||
R in{std::forward<T>(source)};
|
||||
bytes(in, dest);
|
||||
in.check_complete();
|
||||
}
|
||||
catch (const wire::exception& e)
|
||||
{
|
||||
return e.code();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
// Trap objects that do not have standard insertion functions
|
||||
template<typename R, typename... T>
|
||||
void array_insert(const R&, const T&...) noexcept
|
||||
{
|
||||
static_assert(std::is_same<R, void>::value, "type T does not have a valid insertion function");
|
||||
}
|
||||
|
||||
// Insert to sorted containers
|
||||
template<typename R, typename T, typename V = std::pair<typename T::key_type, typename T::mapped_type>>
|
||||
inline auto array_insert(R& source, T& dest) -> decltype(dest.emplace_hint(dest.end(), std::declval<V>()), bool(true))
|
||||
{
|
||||
V val{};
|
||||
wire_read::bytes(source, val);
|
||||
dest.emplace_hint(dest.end(), std::move(val));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Insert into unsorted containers
|
||||
template<typename R, typename T, typename V = typename T::value_type>
|
||||
inline auto array_insert(R& source, T& dest) -> decltype(dest.emplace_back(), dest.back(), bool(true))
|
||||
{
|
||||
// more efficient to process the object in-place in many cases
|
||||
dest.emplace_back();
|
||||
wire_read::bytes(source, dest.back());
|
||||
return true;
|
||||
}
|
||||
|
||||
// no compile-time checks for the array constraints
|
||||
template<typename R, typename T>
|
||||
inline void array_unchecked(R& source, T& dest, const std::size_t min_element_size, const std::size_t max_element_count)
|
||||
{
|
||||
using value_type = typename T::value_type;
|
||||
static_assert(!std::is_same<value_type, char>::value, "read array of chars as string");
|
||||
static_assert(!std::is_same<value_type, std::int8_t>::value, "read array of signed chars as binary");
|
||||
static_assert(!std::is_same<value_type, std::uint8_t>::value, "read array of unsigned chars as binary");
|
||||
|
||||
std::size_t count = source.start_array(min_element_size);
|
||||
|
||||
// quick check for epee/msgpack formats
|
||||
if (max_element_count < count)
|
||||
throw_exception(wire::error::schema::array_max_element, "", nullptr);
|
||||
|
||||
// also checked by derived formats when count is known
|
||||
if (min_element_size && (source.remaining().size() / min_element_size) < count)
|
||||
throw_exception(wire::error::schema::array_min_size, "", nullptr);
|
||||
|
||||
dest.clear();
|
||||
wire::reserve(dest, count);
|
||||
|
||||
bool more = count;
|
||||
const std::size_t start_bytes = source.remaining().size();
|
||||
while (more || !source.is_array_end(count))
|
||||
{
|
||||
// check for json/cbor formats
|
||||
if (source.delimited_arrays() && max_element_count <= dest.size())
|
||||
throw_exception(wire::error::schema::array_max_element, "", nullptr);
|
||||
|
||||
wire_read::array_insert(source, dest);
|
||||
--count;
|
||||
more &= bool(count);
|
||||
|
||||
if (((start_bytes - source.remaining().size()) / dest.size()) < min_element_size)
|
||||
throw_exception(wire::error::schema::array_min_size, "", nullptr);
|
||||
}
|
||||
|
||||
source.end_array();
|
||||
}
|
||||
|
||||
template<typename R, typename T, std::size_t M, std::size_t N = std::numeric_limits<std::size_t>::max()>
|
||||
inline void array(R& source, T& dest, wire::min_element_size<M> min_element_size, wire::max_element_count<N> max_element_count = {})
|
||||
{
|
||||
using value_type = typename T::value_type;
|
||||
static_assert(
|
||||
min_element_size.template check<value_type>() || max_element_count.template check<value_type>(),
|
||||
"array unpacking memory issues"
|
||||
);
|
||||
// each set of template args generates unique ASM, merge them down
|
||||
array_unchecked(source, dest, min_element_size, max_element_count);
|
||||
}
|
||||
|
||||
template<typename T, unsigned I>
|
||||
inline void reset_field(wire::field_<T, true, I>& dest)
|
||||
{
|
||||
// array fields are always optional, see `wire/field.h`
|
||||
if (dest.optional_on_empty())
|
||||
wire::clear(dest.get_value());
|
||||
}
|
||||
|
||||
template<typename T, unsigned I>
|
||||
inline void reset_field(wire::field_<T, false, I>& dest)
|
||||
{
|
||||
dest.get_value().reset();
|
||||
}
|
||||
|
||||
template<typename R, typename T, unsigned I>
|
||||
inline void unpack_field(std::size_t, R& source, wire::field_<T, true, I>& dest)
|
||||
{
|
||||
bytes(source, dest.get_value());
|
||||
}
|
||||
|
||||
template<typename R, typename T, unsigned I>
|
||||
inline void unpack_field(std::size_t, R& source, wire::field_<T, false, I>& dest)
|
||||
{
|
||||
if (!bool(dest.get_value()))
|
||||
dest.get_value().emplace();
|
||||
bytes(source, *dest.get_value());
|
||||
}
|
||||
|
||||
//! Tracks read status of every object field instance.
|
||||
template<typename T>
|
||||
class tracker
|
||||
{
|
||||
T field_;
|
||||
std::size_t our_index_;
|
||||
bool read_;
|
||||
|
||||
public:
|
||||
static constexpr bool is_required() noexcept { return T::is_required(); }
|
||||
static constexpr std::size_t count() noexcept { return T::count(); }
|
||||
|
||||
explicit tracker(T field)
|
||||
: field_(std::move(field)), our_index_(0), read_(false)
|
||||
{}
|
||||
|
||||
//! \return Field name if required and not read, otherwise `nullptr`.
|
||||
const char* name_if_missing() const noexcept
|
||||
{
|
||||
return (is_required() && !read_) ? field_.name : nullptr;
|
||||
}
|
||||
|
||||
|
||||
//! Set all entries in `map` related to this field (expand variant types!).
|
||||
template<std::size_t N>
|
||||
std::size_t set_mapping(std::size_t index, wire::reader::key_map (&map)[N])
|
||||
{
|
||||
our_index_ = index;
|
||||
map[index].id = field_.id();
|
||||
map[index].name = field_.name;
|
||||
return index + count();
|
||||
}
|
||||
|
||||
//! Try to read next value if `index` matches `this`. \return 0 if no match, 1 if optional field read, and 2 if required field read
|
||||
template<typename R>
|
||||
std::size_t try_read(R& source, const std::size_t index)
|
||||
{
|
||||
if (index < our_index_ || our_index_ + count() <= index)
|
||||
return 0;
|
||||
if (read_)
|
||||
throw_exception(wire::error::schema::invalid_key, "duplicate", {std::addressof(field_.name), 1});
|
||||
|
||||
unpack_field(index - our_index_, source, field_);
|
||||
read_ = true;
|
||||
return 1 + is_required();
|
||||
}
|
||||
|
||||
//! Reset optional fields that were skipped
|
||||
bool reset_omitted()
|
||||
{
|
||||
if (!is_required() && !read_)
|
||||
reset_field(field_);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// `expand_tracker_map` writes all `tracker` types to a table
|
||||
|
||||
template<std::size_t N>
|
||||
inline constexpr std::size_t expand_tracker_map(std::size_t index, const wire::reader::key_map (&)[N])
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
template<std::size_t N, typename T, typename... U>
|
||||
inline void expand_tracker_map(std::size_t index, wire::reader::key_map (&map)[N], tracker<T>& head, tracker<U>&... tail)
|
||||
{
|
||||
expand_tracker_map(head.set_mapping(index, map), map, tail...);
|
||||
}
|
||||
|
||||
template<typename R, typename... T>
|
||||
inline void object(R& source, tracker<T>... fields)
|
||||
{
|
||||
static constexpr const std::size_t total_subfields = wire::sum(fields.count()...);
|
||||
static_assert(total_subfields < 100, "algorithm uses too much stack space and linear searching");
|
||||
|
||||
std::size_t state = source.start_object();
|
||||
std::size_t required = wire::sum(std::size_t(fields.is_required())...);
|
||||
|
||||
wire::reader::key_map map[total_subfields] = {};
|
||||
expand_tracker_map(0, map, fields...);
|
||||
|
||||
std::size_t next = 0;
|
||||
while (source.key(map, state, next))
|
||||
{
|
||||
switch (wire::sum(fields.try_read(source, next)...))
|
||||
{
|
||||
default:
|
||||
case 0:
|
||||
throw_exception(wire::error::schema::invalid_key, "bad map setup", nullptr);
|
||||
break;
|
||||
case 2:
|
||||
--required; /* fallthrough */
|
||||
case 1:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (required)
|
||||
{
|
||||
const char* missing[] = {fields.name_if_missing()...};
|
||||
throw_exception(wire::error::schema::missing_key, "", missing);
|
||||
}
|
||||
|
||||
wire::sum(fields.reset_omitted()...);
|
||||
source.end_object();
|
||||
}
|
||||
} // wire_read
|
||||
|
||||
namespace wire
|
||||
{
|
||||
template<typename R, typename T>
|
||||
inline std::enable_if_t<is_array<T>::value> read_bytes(R& source, T& dest)
|
||||
{
|
||||
static constexpr const std::size_t wire_size =
|
||||
default_min_element_size<R, typename T::value_type>::value;
|
||||
static_assert(
|
||||
wire_size != 0,
|
||||
"no sane default array constraints for the reader / value_type pair"
|
||||
);
|
||||
|
||||
wire_read::array(source, dest, min_element_size<wire_size>{});
|
||||
}
|
||||
|
||||
template<typename R, typename... T>
|
||||
inline std::enable_if_t<std::is_base_of<reader, R>::value> object(R& source, T... fields)
|
||||
{
|
||||
wire_read::object(source, wire_read::tracker<T>{std::move(fields)}...);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
// 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 <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#define WIRE_DECLARE_BLOB(type) \
|
||||
template<> \
|
||||
struct is_blob<type> \
|
||||
: std::true_type \
|
||||
{}
|
||||
|
||||
#define WIRE_DECLARE_BLOB_NS(type) \
|
||||
namespace wire { WIRE_DECLARE_BLOB(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
|
||||
{};
|
||||
|
||||
//! 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(); }
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// 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 <boost/uuid/uuid.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace wire
|
||||
{
|
||||
template<>
|
||||
struct is_blob<boost::uuids::uuid>
|
||||
: std::true_type
|
||||
{};
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// 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 <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "wire/traits.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
template<typename T>
|
||||
struct is_array<std::vector<T>>
|
||||
: std::true_type
|
||||
{};
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
# 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.
|
||||
|
||||
set(monero-lws-wire_sources variant.cpp)
|
||||
set(monero-lws-wire_headers variant.h)
|
||||
|
||||
add_library(monero-lws-wire-wrapper ${monero-lws-wire_sources} ${monero-lws-wire_headers})
|
||||
target_include_directories(monero-lws-wire-wrapper PUBLIC "${LMDB_INCLUDE}")
|
||||
target_link_libraries(monero-lws-wire-wrapper PRIVATE monero::libraries)
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
// 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 <utility>
|
||||
|
||||
//#include "wire/field.h"
|
||||
#include "wire/traits.h"
|
||||
|
||||
/*! An array field with read constraint. See `array_` for more info. All (empty)
|
||||
arrays were "optional" (omitted) historically in epee, so this matches prior
|
||||
behavior. */
|
||||
#define WIRE_FIELD_ARRAY(name, read_constraint) \
|
||||
::wire::optional_field( #name , ::wire::array< read_constraint >(std::ref( self . name )))
|
||||
|
||||
namespace wire
|
||||
{
|
||||
/*! A wrapper that ensures `T` is written as an array, with `C` constraints
|
||||
when reading (`max_element_count` or `min_element_size`). `C` can be `void`
|
||||
if write-only.
|
||||
This wrapper meets the requirements for an optional field; `wire::field`
|
||||
and `wire::optional_field` determine whether an empty array must be
|
||||
encoded on the wire. Historically, empty arrays were always omitted on
|
||||
the wire (a defacto optional field).
|
||||
The `is_array` trait can also be used, but is default treated as an optional
|
||||
field. The trait `is_optional_on_empty` traits can be specialized to disable
|
||||
the optional on empty behavior. See `wire/traits.h` for more ifnormation
|
||||
on the `is_optional_on_empty` trait.
|
||||
`container_type` is `T` with optional `std::reference_wrapper` removed.
|
||||
`container_type` concept requirements:
|
||||
* `typedef` `value_type` that specifies inner type.
|
||||
* must have `size()` method that returns number of elements.
|
||||
Additional concept requirements for `container_type` when reading:
|
||||
* must have `clear()` method that removes all elements (`size() == 0`).
|
||||
* must have either: (1) `end()` and `emplace_hint(iterator, value_type&&)`
|
||||
or (2) `emplace_back()` and `back()`:
|
||||
* `end()` method that returns one-past the last element.
|
||||
* `emplace_hint(iterator, value_type&&)` method that move constructs a new
|
||||
element.
|
||||
* `emplace_back()` method that default initializes new element
|
||||
* `back()` method that retrieves last element by reference.
|
||||
Additional concept requirements for `container_type` when writing:
|
||||
* must work with foreach loop (`std::begin` and `std::end`).
|
||||
* must work with `boost::size` (from the `boost::range` library). */
|
||||
template<typename T, typename C = void>
|
||||
struct array_
|
||||
{
|
||||
using constraint = C;
|
||||
using container_type = unwrap_reference_t<T>;
|
||||
using value_type = typename container_type::value_type;
|
||||
|
||||
// See nested `array_` overload below
|
||||
using inner_array = std::reference_wrapper<value_type>;
|
||||
using inner_array_const = std::reference_wrapper<const value_type>;
|
||||
|
||||
T container;
|
||||
|
||||
constexpr const container_type& get_container() const noexcept { return container; }
|
||||
container_type& get_container() noexcept { return container; }
|
||||
|
||||
//! Read directly into the non-nested array
|
||||
container_type& get_read_object() noexcept { return get_container(); }
|
||||
|
||||
|
||||
// concept requirements for optional fields
|
||||
|
||||
explicit operator bool() const noexcept { return !get_container().empty(); }
|
||||
array_& emplace() noexcept { return *this; }
|
||||
|
||||
array_& operator*() noexcept { return *this; }
|
||||
const array_& operator*() const noexcept { return *this; }
|
||||
|
||||
void reset() { get_container().clear(); }
|
||||
};
|
||||
|
||||
//! Nested array case
|
||||
template<typename T, typename C, typename D>
|
||||
struct array_<array_<T, C>, D>
|
||||
{
|
||||
// compute `container_type` and `value_type` recursively
|
||||
using constraint = D;
|
||||
using container_type = typename array_<T, C>::container_type;
|
||||
using value_type = typename container_type::value_type;
|
||||
|
||||
// Re-compute `array_` for inner values
|
||||
using inner_array = array_<typename array_<T, C>::inner_array, C>;
|
||||
using inner_array_const = array_<typename array_<T, C>::inner_array_const, C>;
|
||||
|
||||
array_<T, C> nested;
|
||||
|
||||
const container_type& get_container() const noexcept { return nested.get_container(); }
|
||||
container_type& get_container() noexcept { return nested.get_container(); }
|
||||
|
||||
//! Read through this proxy to track nested array
|
||||
array_& get_read_object() noexcept { return *this; }
|
||||
|
||||
|
||||
// concept requirements for optional fields
|
||||
|
||||
explicit operator bool() const noexcept { return !empty(); }
|
||||
array_& emplace() noexcept { return *this; }
|
||||
|
||||
array_& operator*() noexcept { return *this; }
|
||||
const array_& operator*() const noexcept { return *this; }
|
||||
|
||||
void reset() { clear(); }
|
||||
|
||||
|
||||
/* For reading nested arrays. writing nested arrays is handled in
|
||||
`wrappers_impl.h` with range transform. */
|
||||
|
||||
void clear() { get_container().clear(); }
|
||||
bool empty() const noexcept { return get_container().empty(); }
|
||||
std::size_t size() const noexcept { return get_container().size(); }
|
||||
|
||||
void emplace_back() { get_container().emplace_back(); }
|
||||
|
||||
//! \return A proxy object for tracking inner-array constraints
|
||||
inner_array back() noexcept { return {std::ref(get_container().back())}; }
|
||||
};
|
||||
|
||||
//! Treat `value` as an array when reading/writing, and constrain reading with `C`.
|
||||
template<typename C = void, typename T = void>
|
||||
inline constexpr array_<T, C> array(T value)
|
||||
{
|
||||
return {std::move(value)};
|
||||
}
|
||||
|
||||
/* Do not register with `is_optional_on_empty` trait, this allows selection
|
||||
on whether an array is mandatory on wire. */
|
||||
|
||||
} // wire
|
||||
@@ -0,0 +1,78 @@
|
||||
// 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 "wire/field.h"
|
||||
#include "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 = typename unwrap_reference<T>::type;
|
||||
|
||||
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
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
// Copyright (c) 2024, 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 <cstddef>
|
||||
#include <limits>
|
||||
#include "wire/traits.h"
|
||||
#include "wire/read.h"
|
||||
#include "wire/write.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
//! \brief Wrapper that removes read constraints
|
||||
template<typename T>
|
||||
struct trusted_array_
|
||||
{
|
||||
using container_type = wire::unwrap_reference_t<T>;
|
||||
T container;
|
||||
|
||||
const container_type& get_container() const noexcept { return container; }
|
||||
container_type& get_container() noexcept { return container; }
|
||||
|
||||
// concept requirements for optional fields
|
||||
|
||||
explicit operator bool() const noexcept { return !get_container().empty(); }
|
||||
trusted_array_& emplace() noexcept { return *this; }
|
||||
|
||||
trusted_array_& operator*() noexcept { return *this; }
|
||||
const trusted_array_& operator*() const noexcept { return *this; }
|
||||
|
||||
void reset() { get_container().clear(); }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
trusted_array_<T> trusted_array(T value)
|
||||
{
|
||||
return {std::move(value)};
|
||||
}
|
||||
|
||||
template<typename R, typename T>
|
||||
void read_bytes(R& source, trusted_array_<T> dest)
|
||||
{
|
||||
wire_read::array_unchecked(source, dest.get_container(), 0, std::numeric_limits<std::size_t>::max());
|
||||
}
|
||||
|
||||
template<typename W, typename T>
|
||||
void write_bytes(W& dest, const trusted_array_<T> source)
|
||||
{
|
||||
wire_write::array(dest, source.get_container());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// 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.
|
||||
|
||||
#include "wire/wrapper/variant.h"
|
||||
|
||||
#include <boost/core/demangle.hpp>
|
||||
#include "wire/error.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
[[noreturn]] void throw_variant_exception(wire::error::schema type, const char* variant_name)
|
||||
{
|
||||
WIRE_DLOG_THROW(type, "error with variant type: " << boost::core::demangle(variant_name));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
// 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 <boost/variant/get.hpp>
|
||||
#include <boost/variant/variant_fwd.hpp>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
#include "wire/error.h"
|
||||
#include "wire/fwd.h"
|
||||
#include "wire/field.h"
|
||||
#include "wire/read.h"
|
||||
#include "wire/write.h"
|
||||
|
||||
#define WIRE_OPTION(name, type, cpp_name) \
|
||||
wire::optional_field(name, wire::option<type>(std::ref(cpp_name)))
|
||||
|
||||
namespace wire
|
||||
{
|
||||
[[noreturn]] void throw_variant_exception(error::schema type, const char* variant_name);
|
||||
|
||||
/*! Wrapper for any C++ variant type that tracks if a `read_bytes` call has
|
||||
completed on wrapped `value`. This wrapper is not needed if the variant is
|
||||
being used for writes only - see `wire::option_` below for more information.
|
||||
|
||||
`variant_type` is `T` with optional `std::reference_wrapper` removed. See
|
||||
`option_` for concept requirements of `variant_type`.
|
||||
|
||||
Example usage:
|
||||
```
|
||||
template<typename F, typename T>
|
||||
void type_map(F& format, T& self)
|
||||
{
|
||||
auto variant = wire::variant(std::ref(self.field3));
|
||||
wire::object(format,
|
||||
...
|
||||
WIRE_OPTION("type1", type1, variant),
|
||||
WIRE_OPTION("type2", type2, variant)
|
||||
);
|
||||
}
|
||||
``` */
|
||||
template<typename T>
|
||||
struct variant_
|
||||
{
|
||||
using variant_type = unwrap_reference_t<T>;
|
||||
|
||||
//! \throw wire::exception with `type` and mangled C++ name of `variant_type`.
|
||||
[[noreturn]] static void throw_exception(const error::schema type)
|
||||
{ throw_variant_exception(type, typeid(variant_type).name()); }
|
||||
|
||||
constexpr variant_(T&& value)
|
||||
: value(std::move(value)), read(false)
|
||||
{}
|
||||
|
||||
T value;
|
||||
bool read;
|
||||
|
||||
constexpr const variant_type& get_variant() const noexcept { return value; }
|
||||
variant_type& get_variant() noexcept { return value; }
|
||||
|
||||
//! Makes `variant_` compatible with `emplace()` in `option_`.
|
||||
template<typename U>
|
||||
variant_& operator=(U&& rhs)
|
||||
{
|
||||
get_variant() = std::forward<U>(rhs);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline constexpr variant_<T> variant(T value)
|
||||
{ return {std::move(value)}; }
|
||||
|
||||
namespace adapt
|
||||
{
|
||||
template<typename T>
|
||||
inline void throw_if_not_read(const T&)
|
||||
{ throw_variant_exception(error::schema::missing_key, typeid(T).name()); }
|
||||
|
||||
template<typename T>
|
||||
inline void throw_if_not_read(const variant_<T>& value)
|
||||
{
|
||||
if (!value.read)
|
||||
value.throw_exception(error::schema::missing_key);
|
||||
}
|
||||
|
||||
|
||||
// other variant overloads can be added here as needed
|
||||
|
||||
template<typename U, typename... T>
|
||||
inline const U* get_if(const boost::variant<T...>& value)
|
||||
{ return boost::get<U>(std::addressof(value)); }
|
||||
|
||||
template<typename U, typename T>
|
||||
inline const U* get_if(const variant_<T>& value)
|
||||
{ return adapt::get_if<U>(value.get_variant()); }
|
||||
}
|
||||
|
||||
/*! Wrapper that makes a variant compatible with `wire::optional_field`.
|
||||
Currently `wire::variant_` and `boost::variant` are valid variant types
|
||||
for writing, and only `wire::variant_` is valid for reading.
|
||||
|
||||
`variant_type` is `T` with optional `std::reference_wrapper` removed.
|
||||
`variant_type` concept requirements:
|
||||
* must have two overloads for `get<U>` function in `adapt` namespace - one
|
||||
`const` and one non-`const` that returns `const U&` and `U&` respectively
|
||||
iff `variant_type` is storing type `U`. Otherwise, the function should
|
||||
throw an exception.
|
||||
* must have overload for `get_if<U>` function in `adapt` namespace that
|
||||
returns `const U*` when `variant_type` is storing type `U`. Otherwise, the
|
||||
function should return `nullptr`.
|
||||
* must have a member function `operator=(U&&)` that changes the stored type
|
||||
to `U` (`get<U>` and `get_if<U>` will return `U` after `operator=`
|
||||
completion).
|
||||
|
||||
The `wire::variant(std::ref(self.field3))` step in the example above can be
|
||||
omitted if only writing is needed. The `boost::variant` value should be
|
||||
given directly to `wire::option<U>(...)` or `WIRE_OPTION` macro - only one
|
||||
type is active so `wire::optional_field` will omit all other types/fields. */
|
||||
template<typename T, typename U>
|
||||
struct option_
|
||||
{
|
||||
using variant_type = unwrap_reference_t<T>;
|
||||
using option_type = U;
|
||||
|
||||
T value;
|
||||
|
||||
constexpr const variant_type& get_variant() const noexcept { return value; }
|
||||
variant_type& get_variant() noexcept { return value; }
|
||||
|
||||
//! \return `true` iff `U` is active type in variant.
|
||||
bool is_active() const { return adapt::get_if<U>(get_variant()) != nullptr; }
|
||||
|
||||
|
||||
// concept requirements for optional fields
|
||||
|
||||
explicit operator bool() const { return is_active(); }
|
||||
void emplace() { get_variant() = U{}; }
|
||||
|
||||
const option_& operator*() const { return *this; }
|
||||
option_& operator*() { return *this; }
|
||||
|
||||
//! \throw wire::exception iff no variant type was read.
|
||||
void reset() { adapt::throw_if_not_read(get_variant()); }
|
||||
};
|
||||
|
||||
template<typename U, typename T>
|
||||
inline constexpr option_<T, U> option(T value)
|
||||
{ return {std::move(value)}; }
|
||||
|
||||
namespace adapt
|
||||
{
|
||||
// other variant overloads can be added here as needed
|
||||
|
||||
template<typename U, typename... T>
|
||||
inline U& get(boost::variant<T...>& value)
|
||||
{ return boost::get<U>(value); }
|
||||
|
||||
template<typename U, typename... T>
|
||||
inline const U& get(const boost::variant<T...>& value)
|
||||
{ return boost::get<U>(value); }
|
||||
|
||||
template<typename U, typename T>
|
||||
inline const U& get(const variant_<T>& value)
|
||||
{ return adapt::get<U>(value.get_variant()); }
|
||||
}
|
||||
|
||||
//! \throw wire::exception if `dest.get_variant()` has already been used in `read_bytes`.
|
||||
template<typename R, typename T, typename U>
|
||||
inline void read_bytes(R& source, option_<std::reference_wrapper<variant_<T>>, U> dest)
|
||||
{
|
||||
if (dest.get_variant().read)
|
||||
dest.get_variant().throw_exception(error::schema::invalid_key);
|
||||
wire_read::bytes(source, adapt::get<U>(dest.get_variant().get_variant()));
|
||||
dest.get_variant().read = true;
|
||||
}
|
||||
|
||||
template<typename W, typename T, typename U>
|
||||
inline void write_bytes(W& dest, const option_<T, U>& source)
|
||||
{ wire_write::bytes(dest, adapt::get<U>(source.get_variant())); }
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
// 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 <boost/range/adaptor/transformed.hpp>
|
||||
#include "wire/error.h"
|
||||
#include "wire/read.h"
|
||||
#include "wire/write.h"
|
||||
#include "wire/wrapper/array.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
//
|
||||
// free functions for `array_` wrapper
|
||||
//
|
||||
|
||||
template<typename R, typename T, typename C>
|
||||
inline void read_bytes(R& source, array_<T, C> wrapper)
|
||||
{
|
||||
// see constraints directly above `array_` definition
|
||||
static_assert(std::is_same<R, void>::value, "array_ must have a read constraint for memory purposes");
|
||||
}
|
||||
|
||||
template<typename R, typename T, std::size_t N>
|
||||
inline void read_bytes(R& source, array_<T, max_element_count<N>>& wrapper)
|
||||
{
|
||||
using array_type = array_<T, max_element_count<N>>;
|
||||
using value_type = typename array_type::value_type;
|
||||
using constraint = typename array_type::constraint;
|
||||
static_assert(constraint::template check<value_type>(), "max reserve bytes exceeded for element");
|
||||
wire_read::array(source, wrapper.get_read_object(), min_element_size<0>{}, constraint{});
|
||||
}
|
||||
template<typename R, typename T, std::size_t N>
|
||||
inline void read_bytes(R& source, array_<T, min_element_size<N>>& wrapper)
|
||||
{
|
||||
using array_type = array_<T, min_element_size<N>>;
|
||||
using value_type = typename array_type::value_type;
|
||||
using constraint = typename array_type::constraint;
|
||||
static_assert(constraint::template check<value_type>(), "max compression ratio exceeded for element");
|
||||
wire_read::array(source, wrapper.get_read_object(), constraint{});
|
||||
}
|
||||
|
||||
template<typename W, typename T, typename C>
|
||||
inline void write_bytes(W& dest, const array_<T, C>& wrapper)
|
||||
{
|
||||
wire_write::array(dest, wrapper.get_container());
|
||||
}
|
||||
template<typename W, typename T, typename C, typename D>
|
||||
inline void write_bytes(W& dest, const array_<array_<T, C>, D>& wrapper)
|
||||
{
|
||||
using inner_type = typename array_<array_<T, C>, D>::inner_array_const;
|
||||
const auto wrap = [](const auto& val) -> inner_type { return {std::ref(val)}; };
|
||||
wire_write::array(dest, boost::adaptors::transform(wrapper.get_container(), wrap));
|
||||
}
|
||||
} // wire
|
||||
@@ -0,0 +1,31 @@
|
||||
// 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 "write.h"
|
||||
|
||||
wire::writer::~writer() noexcept
|
||||
{}
|
||||
@@ -0,0 +1,299 @@
|
||||
// Copyright (c) 2020-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 <array>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <boost/range/size.hpp>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include <system_error>
|
||||
|
||||
#include "byte_slice.h" // monero/contrib/epee/include
|
||||
#include "byte_stream.h"// monero/contrib/epee/include
|
||||
#include "span.h" // monero/contrib/epee/include
|
||||
#include "wire/error.h"
|
||||
#include "wire/field.h"
|
||||
#include "wire/filters.h"
|
||||
#include "wire/traits.h"
|
||||
|
||||
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(int) = 0;
|
||||
virtual void integer(std::intmax_t) = 0;
|
||||
|
||||
virtual void unsigned_integer(unsigned) = 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> bytes) = 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(std::uintmax_t) = 0;
|
||||
virtual void key(boost::string_ref) = 0;
|
||||
virtual void key(unsigned, boost::string_ref) = 0; //!< Implementation should output fastest key
|
||||
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_bytes(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, 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 double source)
|
||||
{ dest.real(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)
|
||||
}
|
||||
|
||||
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_sink();
|
||||
}
|
||||
catch (const wire::exception& e)
|
||||
{
|
||||
dest.clear();
|
||||
return e.code();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
dest.clear();
|
||||
throw;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename W, typename T>
|
||||
inline std::error_code to_bytes(epee::byte_slice& dest, const T& source)
|
||||
{
|
||||
epee::byte_stream sink{};
|
||||
const std::error_code error = wire_write::to_bytes<W>(sink, source);
|
||||
if (error)
|
||||
{
|
||||
dest = nullptr;
|
||||
return error;
|
||||
}
|
||||
dest = epee::byte_slice{std::move(sink)};
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline std::size_t array_size_(std::true_type, const T& source)
|
||||
{
|
||||
static_assert(
|
||||
!std::is_same<typename std::iterator_traits<typename T::const_iterator>::iterator_category, std::input_iterator_tag>{},
|
||||
"Input iterators must use json (or similar) derived classes directly"
|
||||
);
|
||||
return boost::size(source);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline constexpr std::size_t array_size_(std::false_type, const T&) noexcept
|
||||
{ return 0; }
|
||||
|
||||
template<typename W, typename T>
|
||||
inline constexpr std::size_t array_size(const W& dest, const T& source) noexcept
|
||||
{ return array_size_(dest.need_array_size(), source); }
|
||||
|
||||
template<typename W, typename T, typename F = wire::identity_>
|
||||
inline void array(W& dest, const T& source, F f = {})
|
||||
{
|
||||
using value_type = typename T::value_type;
|
||||
static_assert(!std::is_same<value_type, char>::value, "write array of 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, source));
|
||||
for (const auto& elem : source)
|
||||
bytes(dest, f(elem));
|
||||
dest.end_array();
|
||||
}
|
||||
|
||||
template<typename W, typename T, unsigned I>
|
||||
inline bool field(W& dest, const wire::field_<T, true, I> elem)
|
||||
{
|
||||
// Arrays always optional, see `wire::field.h`
|
||||
if (wire::available(elem))
|
||||
{
|
||||
dest.key(I, elem.name);
|
||||
bytes(dest, elem.get_value());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename W, typename T, unsigned I>
|
||||
inline bool field(W& dest, const wire::field_<T, false, I> elem)
|
||||
{
|
||||
if (wire::available(elem))
|
||||
{
|
||||
dest.key(I, elem.name);
|
||||
bytes(dest, *elem.get_value());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
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::move(fields))...};
|
||||
dest.end_object();
|
||||
}
|
||||
|
||||
template<typename W, typename T, typename F, typename G>
|
||||
inline void dynamic_object(W& dest, const T& values, F key_filter, G value_filter)
|
||||
{
|
||||
dest.start_object(array_size(dest, values));
|
||||
for (const auto& elem : values)
|
||||
{
|
||||
dest.key(key_filter(elem.first));
|
||||
bytes(dest, value_filter(elem.second));
|
||||
}
|
||||
dest.end_object();
|
||||
}
|
||||
} // wire_write
|
||||
|
||||
namespace wire
|
||||
{
|
||||
template<typename W, typename T, typename F>
|
||||
inline void write_bytes(W& dest, const as_array_<T, F> source)
|
||||
{
|
||||
wire_write::array(dest, source.get_value(), std::move(source.filter));
|
||||
}
|
||||
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, typename F = identity_, typename G = identity_>
|
||||
inline std::enable_if_t<std::is_base_of<writer, W>::value>
|
||||
dynamic_object(W& dest, const T& source, F key_filter = F{}, G value_filter = G{})
|
||||
{
|
||||
wire_write::dynamic_object(dest, source, std::move(key_filter), std::move(value_filter));
|
||||
}
|
||||
template<typename W, typename T, typename F, typename G>
|
||||
inline void write_bytes(W& dest, as_object_<T, F, G> source)
|
||||
{
|
||||
wire::dynamic_object(dest, source.get_map(), std::move(source.key_filter), std::move(source.value_filter));
|
||||
}
|
||||
|
||||
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)...);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user