forked from such-gitea/wownero-lws
Initial working base separated from top-level monero project
This commit is contained in:
33
src/util/CMakeLists.txt
Normal file
33
src/util/CMakeLists.txt
Normal file
@@ -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-util_sources gamma_picker.cpp random_outputs.cpp transactions.cpp)
|
||||
set(monero-lws-util_headers fwd.h gamma_picker.h http_server.h random_outputs.h transactions.h)
|
||||
|
||||
add_library(monero-lws-util ${monero-lws-util_sources} ${monero-lws-util_headers})
|
||||
target_link_libraries(monero-lws-util monero::libraries)
|
||||
35
src/util/fwd.h
Normal file
35
src/util/fwd.h
Normal file
@@ -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.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace lws
|
||||
{
|
||||
class gamma_picker;
|
||||
struct random_output;
|
||||
struct random_ring;
|
||||
}
|
||||
112
src/util/gamma_picker.cpp
Normal file
112
src/util/gamma_picker.cpp
Normal file
@@ -0,0 +1,112 @@
|
||||
// Copyright (c) 2019-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 "gamma_picker.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "crypto/crypto.h"
|
||||
#include "cryptonote_config.h"
|
||||
|
||||
namespace lws
|
||||
{
|
||||
namespace
|
||||
{
|
||||
constexpr const double gamma_shape = 19.28;
|
||||
constexpr const double gamma_scale = 1 / double(1.61);
|
||||
constexpr const std::size_t blocks_in_a_year = (86400 * 365) / DIFFICULTY_TARGET_V2;
|
||||
}
|
||||
|
||||
gamma_picker::gamma_picker(std::vector<uint64_t> rct_offsets)
|
||||
: gamma_picker(std::move(rct_offsets), gamma_shape, gamma_scale)
|
||||
{}
|
||||
|
||||
gamma_picker::gamma_picker(std::vector<std::uint64_t> offsets_in, double shape, double scale)
|
||||
: rct_offsets(std::move(offsets_in)),
|
||||
gamma(shape, scale),
|
||||
outputs_per_second(0)
|
||||
{
|
||||
if (!rct_offsets.empty())
|
||||
{
|
||||
const std::size_t blocks_to_consider = std::min(rct_offsets.size(), blocks_in_a_year);
|
||||
const std::uint64_t initial = blocks_to_consider < rct_offsets.size() ?
|
||||
rct_offsets[rct_offsets.size() - blocks_to_consider - 1] : 0;
|
||||
const std::size_t outputs_to_consider = rct_offsets.back() - initial;
|
||||
|
||||
static_assert(0 < DIFFICULTY_TARGET_V2, "block target time cannot be zero");
|
||||
// this assumes constant target over the whole rct range
|
||||
outputs_per_second = outputs_to_consider / double(DIFFICULTY_TARGET_V2 * blocks_to_consider);
|
||||
}
|
||||
}
|
||||
|
||||
bool gamma_picker::is_valid() const noexcept
|
||||
{
|
||||
return CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE < rct_offsets.size();
|
||||
}
|
||||
|
||||
std::uint64_t gamma_picker::spendable_upper_bound() const noexcept
|
||||
{
|
||||
if (!is_valid())
|
||||
return 0;
|
||||
return *(rct_offsets.end() - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE - 1);
|
||||
}
|
||||
|
||||
std::uint64_t gamma_picker::operator()()
|
||||
{
|
||||
if (!is_valid())
|
||||
throw std::logic_error{"Cannot select random output - blockchain height too small"};
|
||||
|
||||
static_assert(std::is_empty<crypto::random_device>(), "random_device is no longer cheap to construct");
|
||||
static constexpr const crypto::random_device engine{};
|
||||
const auto end = offsets().end() - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE;
|
||||
|
||||
for (unsigned tries = 0; tries < 100; ++tries)
|
||||
{
|
||||
std::uint64_t output_index = std::exp(gamma(engine)) * outputs_per_second;
|
||||
if (offsets().back() <= output_index)
|
||||
continue; // gamma selected older than blockchain height (rare)
|
||||
|
||||
output_index = offsets().back() - 1 - output_index;
|
||||
const auto selection = std::lower_bound(offsets().begin(), end, output_index);
|
||||
if (selection == end)
|
||||
continue; // gamma selected within locked/non-spendable range (rare)
|
||||
|
||||
const std::uint64_t first_rct = offsets().begin() == selection ? 0 : *(selection - 1);
|
||||
const std::uint64_t n_rct = *selection - first_rct;
|
||||
if (n_rct != 0)
|
||||
return first_rct + crypto::rand_idx(n_rct);
|
||||
// block had zero outputs (miner didn't collect XMR?)
|
||||
}
|
||||
throw std::runtime_error{"Unable to select random output in spendable range using gamma distribution after 1,024 attempts"};
|
||||
}
|
||||
|
||||
std::vector<std::uint64_t> gamma_picker::take_offsets()
|
||||
{
|
||||
return std::vector<std::uint64_t>{std::move(rct_offsets)};
|
||||
}
|
||||
} // lws
|
||||
88
src/util/gamma_picker.h
Normal file
88
src/util/gamma_picker.h
Normal file
@@ -0,0 +1,88 @@
|
||||
// Copyright (c) 2019-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 <cstdint>
|
||||
#include <random>
|
||||
#include <vector>
|
||||
|
||||
namespace lws
|
||||
{
|
||||
//! Select outputs using a gamma distribution with Sarang's output-lineup method
|
||||
class gamma_picker
|
||||
{
|
||||
std::vector<uint64_t> rct_offsets;
|
||||
std::gamma_distribution<double> gamma;
|
||||
double outputs_per_second;
|
||||
|
||||
gamma_picker(const gamma_picker&) = default; // force explicit usage of `clone()` to copy.
|
||||
public:
|
||||
//! \post `!is_valid()` since the chain of offsets is empty.
|
||||
gamma_picker()
|
||||
: gamma_picker(std::vector<std::uint64_t>{})
|
||||
{}
|
||||
|
||||
//! Use default (recommended) gamma parameters with `rct_offsets`.
|
||||
explicit gamma_picker(std::vector<std::uint64_t> rct_offsets);
|
||||
explicit gamma_picker(std::vector<std::uint64_t> rct_offsets, double shape, double scale);
|
||||
|
||||
//! \post Source of move `!is_valid()`.
|
||||
gamma_picker(gamma_picker&&) = default;
|
||||
|
||||
//! \post Source of move `!is_valid()`.
|
||||
gamma_picker& operator=(gamma_picker&&) = default;
|
||||
|
||||
//! \return A copy of `this`.
|
||||
gamma_picker clone() const { return gamma_picker{*this}; }
|
||||
|
||||
//! \return `is_valid()`.
|
||||
explicit operator bool() const noexcept { return is_valid(); }
|
||||
|
||||
//! \return True if `operator()()` can pick an output using `offsets()`.
|
||||
bool is_valid() const noexcept;
|
||||
|
||||
//! \return An upper-bound on the number of unlocked/spendable outputs based on block age.
|
||||
std::uint64_t spendable_upper_bound() const noexcept;
|
||||
|
||||
/*!
|
||||
Select a random output index for use in a ring. Outputs in the unspendable
|
||||
range (too new) and older than the chain (too old) are filtered out by
|
||||
retrying the gamma distribution.
|
||||
|
||||
\throw std::logic_error if `!is_valid()` - considered unrecoverable.
|
||||
\throw std::runtiime_error if no output within spendable range was selected
|
||||
after 100 attempts.
|
||||
\return Selected output using gamma distribution.
|
||||
*/
|
||||
std::uint64_t operator()();
|
||||
|
||||
//! \return Current ringct distribution used for `operator()()` output selection.
|
||||
const std::vector<std::uint64_t>& offsets() const noexcept { return rct_offsets; }
|
||||
|
||||
//! \return Ownership of `offsets()` by move. \post `!is_valid()`
|
||||
std::vector<std::uint64_t> take_offsets();
|
||||
};
|
||||
} // lws
|
||||
124
src/util/http_server.h
Normal file
124
src/util/http_server.h
Normal file
@@ -0,0 +1,124 @@
|
||||
// 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 <boost/bind/bind.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
#include <boost/optional/optional.hpp>
|
||||
|
||||
#include "misc_log_ex.h"
|
||||
#include "net/abstract_tcp_server2.h" // monero/contrib/epee/include
|
||||
#include "net/http_protocol_handler.h" // monero/contrib/epee/include
|
||||
#include "net/http_server_handlers_map2.h" // monero/contrib/epee/include
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "net.http"
|
||||
|
||||
|
||||
namespace lws
|
||||
{
|
||||
template<class t_child_class, class t_connection_context = epee::net_utils::connection_context_base>
|
||||
class http_server_impl_base: public epee::net_utils::http::i_http_server_handler<t_connection_context>
|
||||
{
|
||||
|
||||
public:
|
||||
http_server_impl_base()
|
||||
: m_net_server(epee::net_utils::e_connection_type_RPC)
|
||||
{}
|
||||
|
||||
explicit http_server_impl_base(boost::asio::io_service& external_io_service)
|
||||
: m_net_server(external_io_service, epee::net_utils::e_connection_type_RPC)
|
||||
{}
|
||||
|
||||
bool init(const std::string& bind_port, const std::string& bind_ip,
|
||||
std::vector<std::string> access_control_origins, epee::net_utils::ssl_options_t ssl_options)
|
||||
{
|
||||
|
||||
//set self as callback handler
|
||||
m_net_server.get_config_object().m_phandler = static_cast<t_child_class*>(this);
|
||||
|
||||
//here set folder for hosting reqests
|
||||
m_net_server.get_config_object().m_folder = "";
|
||||
|
||||
//set access control allow origins if configured
|
||||
std::sort(access_control_origins.begin(), access_control_origins.end());
|
||||
m_net_server.get_config_object().m_access_control_origins = std::move(access_control_origins);
|
||||
|
||||
|
||||
MGINFO("Binding on " << bind_ip << " (IPv4):" << bind_port);
|
||||
bool res = m_net_server.init_server(bind_port, bind_ip, bind_port, std::string{}, false, true, std::move(ssl_options));
|
||||
if(!res)
|
||||
{
|
||||
LOG_ERROR("Failed to bind server");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool run(size_t threads_count, bool wait = true)
|
||||
{
|
||||
//go to loop
|
||||
MINFO("Run net_service loop( " << threads_count << " threads)...");
|
||||
if(!m_net_server.run_server(threads_count, wait))
|
||||
{
|
||||
LOG_ERROR("Failed to run net tcp server!");
|
||||
}
|
||||
|
||||
if(wait)
|
||||
MINFO("net_service loop stopped.");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool deinit()
|
||||
{
|
||||
return m_net_server.deinit_server();
|
||||
}
|
||||
|
||||
bool timed_wait_server_stop(uint64_t ms)
|
||||
{
|
||||
return m_net_server.timed_wait_server_stop(ms);
|
||||
}
|
||||
|
||||
bool send_stop_signal()
|
||||
{
|
||||
m_net_server.send_stop_signal();
|
||||
return true;
|
||||
}
|
||||
|
||||
int get_binded_port()
|
||||
{
|
||||
return m_net_server.get_binded_port();
|
||||
}
|
||||
|
||||
long get_connections_count() const
|
||||
{
|
||||
return m_net_server.get_connections_count();
|
||||
}
|
||||
|
||||
protected:
|
||||
epee::net_utils::boosted_tcp_server<epee::net_utils::http::http_custom_handler<t_connection_context> > m_net_server;
|
||||
};
|
||||
} // lws
|
||||
309
src/util/random_outputs.cpp
Normal file
309
src/util/random_outputs.cpp
Normal file
@@ -0,0 +1,309 @@
|
||||
// Copyright (c) 2018-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 "random_outputs.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/range/combine.hpp>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <random>
|
||||
#include <utility>
|
||||
|
||||
#include "cryptonote_config.h" // monero/src
|
||||
#include "error.h"
|
||||
#include "util/gamma_picker.h"
|
||||
|
||||
namespace lws
|
||||
{
|
||||
namespace
|
||||
{
|
||||
struct by_amount
|
||||
{
|
||||
template<typename T>
|
||||
bool operator()(T const& left, T const& right) const noexcept
|
||||
{
|
||||
return left.amount < right.amount;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool operator()(std::uint64_t left, T const& right) const noexcept
|
||||
{
|
||||
return left < right.amount;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool operator()(T const& left, std::uint64_t right) const noexcept
|
||||
{
|
||||
return left.amount < right;
|
||||
}
|
||||
};
|
||||
|
||||
struct by_index
|
||||
{
|
||||
template<typename T, typename U>
|
||||
bool operator()(T const& left, U const& right) const noexcept
|
||||
{
|
||||
return left.index < right.index;
|
||||
}
|
||||
};
|
||||
|
||||
struct same_index
|
||||
{
|
||||
bool operator()(lws::output_ref const& left, lws::output_ref const& right) const noexcept
|
||||
{
|
||||
return left.index == right.index;
|
||||
}
|
||||
};
|
||||
|
||||
expect<void> pick_all(epee::span<lws::output_ref> out, const std::uint64_t amount) noexcept
|
||||
{
|
||||
static_assert(
|
||||
std::numeric_limits<std::size_t>::max() <= std::numeric_limits<std::uint64_t>::max(),
|
||||
"size_t is really large"
|
||||
);
|
||||
|
||||
std::size_t index = 0;
|
||||
for (auto& entry : out)
|
||||
{
|
||||
entry.amount = amount;
|
||||
entry.index = index++;
|
||||
}
|
||||
return success();
|
||||
}
|
||||
|
||||
expect<void> triangular_pick(epee::span<lws::output_ref> out, lws::histogram const& hist)
|
||||
{
|
||||
MONERO_PRECOND(hist.unlocked_count <= hist.total_count);
|
||||
|
||||
if (hist.unlocked_count < out.size())
|
||||
return {lws::error::not_enough_mixin};
|
||||
|
||||
if (hist.unlocked_count == out.size())
|
||||
return pick_all(out, hist.amount);
|
||||
|
||||
/* This does not match the wallet2 selection code - recents are not
|
||||
considered. There should be no new recents for this selection
|
||||
algorithm because it is only used for non-ringct outputs. */
|
||||
|
||||
static constexpr const std::uint64_t max = std::uint64_t(1) << 53;
|
||||
for (auto& entry : out)
|
||||
{
|
||||
entry.amount = hist.amount;
|
||||
|
||||
/* \TODO Update REST API to send real outputs so selection
|
||||
algorithm can use fork information (like wallet2). */
|
||||
|
||||
do
|
||||
{
|
||||
const std::uint64_t r = crypto::rand<std::uint64_t>() % max;
|
||||
const double frac = std::sqrt(double(r) / double(max));
|
||||
entry.index = std::uint64_t(frac * double(hist.total_count));
|
||||
} while (hist.unlocked_count < entry.index);
|
||||
}
|
||||
|
||||
return success();
|
||||
}
|
||||
|
||||
expect<void> gamma_pick(epee::span<lws::output_ref> out, gamma_picker& pick_rct)
|
||||
{
|
||||
if (!pick_rct)
|
||||
return {lws::error::not_enough_mixin};
|
||||
|
||||
const std::uint64_t spendable = pick_rct.spendable_upper_bound();
|
||||
if (spendable < out.size())
|
||||
return {lws::error::not_enough_mixin};
|
||||
if (spendable == out.size())
|
||||
return pick_all(out, 0);
|
||||
|
||||
for (auto& entry : out)
|
||||
{
|
||||
entry.amount = 0;
|
||||
entry.index = pick_rct();
|
||||
|
||||
/* \TODO Update REST API to send real outputs so selection
|
||||
algorithm can use fork information (like wallet2). */
|
||||
}
|
||||
return success();
|
||||
}
|
||||
}
|
||||
|
||||
expect<std::vector<random_ring>> pick_random_outputs(
|
||||
const std::uint32_t mixin,
|
||||
const epee::span<const std::uint64_t> amounts,
|
||||
gamma_picker& pick_rct,
|
||||
epee::span<histogram> histograms,
|
||||
const std::function<key_fetcher> fetch
|
||||
) {
|
||||
if (mixin == 0 || amounts.empty())
|
||||
return std::vector<random_ring>{amounts.size()};
|
||||
|
||||
const std::size_t sizet_max = std::numeric_limits<std::size_t>::max();
|
||||
MONERO_PRECOND(bool(fetch));
|
||||
MONERO_PRECOND(mixin <= (sizet_max / amounts.size()));
|
||||
|
||||
std::vector<output_ref> proposed{};
|
||||
std::vector<random_ring> rings{};
|
||||
rings.resize(amounts.size());
|
||||
|
||||
for (auto ring : boost::combine(amounts, rings))
|
||||
boost::get<1>(ring).amount = boost::get<0>(ring);
|
||||
|
||||
std::sort(histograms.begin(), histograms.end(), by_amount{});
|
||||
for (unsigned tries = 0; tries < 64; ++tries)
|
||||
{
|
||||
proposed.clear();
|
||||
proposed.reserve(rings.size() * mixin);
|
||||
|
||||
// select indexes foreach ring below mixin count
|
||||
for (auto ring = rings.begin(); ring != rings.end(); /* handled below */)
|
||||
{
|
||||
const std::size_t count = proposed.size();
|
||||
if (ring->ring.size() < mixin)
|
||||
{
|
||||
const std::size_t diff = mixin - ring->ring.size();
|
||||
proposed.resize(proposed.size() + diff);
|
||||
{
|
||||
const epee::span<output_ref> latest{proposed.data() + count, diff};
|
||||
|
||||
expect<void> picked{};
|
||||
const std::uint64_t amount = ring->amount;
|
||||
if (amount == 0)
|
||||
picked = gamma_pick(latest, pick_rct);
|
||||
else
|
||||
{
|
||||
const auto match =
|
||||
std::lower_bound(histograms.begin(), histograms.end(), amount, by_amount{});
|
||||
MONERO_PRECOND(match != histograms.end() && match->amount == amount);
|
||||
picked = triangular_pick(latest, *match);
|
||||
}
|
||||
|
||||
if (!picked)
|
||||
{
|
||||
if (picked == lws::error::not_enough_mixin)
|
||||
{
|
||||
proposed.resize(proposed.size() - diff);
|
||||
ring = rings.erase(ring);
|
||||
continue;
|
||||
}
|
||||
return picked.error();
|
||||
}
|
||||
|
||||
// drop dupes in latest selection
|
||||
std::sort(latest.begin(), latest.end(), by_index{});
|
||||
const auto last = std::unique(latest.begin(), latest.end(), same_index{});
|
||||
proposed.resize(last - proposed.data());
|
||||
}
|
||||
|
||||
ring->ring.reserve(mixin);
|
||||
epee::span<random_output> current = epee::to_mut_span(ring->ring);
|
||||
std::sort(current.begin(), current.end(), by_index{});
|
||||
|
||||
// See if new list has duplicates with existing ring
|
||||
for (auto ref = proposed.begin() + count; ref < proposed.end(); /* see branches */ )
|
||||
{
|
||||
// must update after push_back call
|
||||
current = {ring->ring.data(), current.size()};
|
||||
|
||||
const auto match =
|
||||
std::lower_bound(current.begin(), current.end(), *ref, by_index{});
|
||||
if (match == current.end() || match->index != ref->index)
|
||||
{
|
||||
ring->ring.push_back(random_output{{}, ref->index});
|
||||
ring->ring.back().keys.unlocked = false; // for tracking below
|
||||
++ref;
|
||||
}
|
||||
else // dupe
|
||||
ref = proposed.erase(ref);
|
||||
}
|
||||
}
|
||||
|
||||
++ring;
|
||||
}
|
||||
|
||||
// all amounts lack enough mixin
|
||||
if (rings.empty())
|
||||
return rings;
|
||||
|
||||
/* \TODO For maximum privacy, the real outputs need to be fetched
|
||||
below. This requires an update of the REST API. */
|
||||
|
||||
// fetch all new keys in one shot
|
||||
const std::size_t expected = proposed.size();
|
||||
auto result = fetch(std::move(proposed));
|
||||
if (!result)
|
||||
return result.error();
|
||||
|
||||
if (expected != result->size())
|
||||
return {lws::error::bad_daemon_response};
|
||||
|
||||
bool done = true;
|
||||
std::size_t offset = 0;
|
||||
for (auto& ring : rings)
|
||||
{
|
||||
// this should never fail, else the logic in here is bad
|
||||
assert(ring.ring.size() <= ring.ring.size());
|
||||
|
||||
// if we dropped a selection due to dupe, must try again
|
||||
done = (done && mixin <= ring.ring.size());
|
||||
|
||||
for (auto entry = ring.ring.begin(); entry < ring.ring.end(); /* see branches */)
|
||||
{
|
||||
// check only new keys
|
||||
if (entry->keys.unlocked)
|
||||
++entry;
|
||||
else
|
||||
{
|
||||
if (result->size() <= offset)
|
||||
return {lws::error::bad_daemon_response};
|
||||
|
||||
output_keys const& keys = result->at(offset);
|
||||
++offset;
|
||||
|
||||
if (keys.unlocked)
|
||||
{
|
||||
entry->keys = keys;
|
||||
++entry;
|
||||
}
|
||||
else
|
||||
{
|
||||
done = false;
|
||||
entry = ring.ring.erase(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(offset == result->size());
|
||||
|
||||
if (done)
|
||||
return {std::move(rings)};
|
||||
}
|
||||
|
||||
return {lws::error::not_enough_mixin};
|
||||
}
|
||||
}
|
||||
|
||||
92
src/util/random_outputs.h
Normal file
92
src/util/random_outputs.h
Normal file
@@ -0,0 +1,92 @@
|
||||
// Copyright (c) 2018-2019, 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 <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "common/expect.h" // monero/src
|
||||
#include "rpc/message_data_structs.h" // monero/src
|
||||
#include "span.h" // monero/src
|
||||
#include "util/fwd.h"
|
||||
|
||||
namespace lws
|
||||
{
|
||||
using histogram = cryptonote::rpc::output_amount_count;
|
||||
using output_ref = cryptonote::rpc::output_amount_and_index;
|
||||
using output_keys = cryptonote::rpc::output_key_mask_unlocked;
|
||||
|
||||
struct random_output
|
||||
{
|
||||
output_keys keys;
|
||||
std::uint64_t index;
|
||||
};
|
||||
|
||||
struct random_ring
|
||||
{
|
||||
std::vector<random_output> ring;
|
||||
std::uint64_t amount;
|
||||
};
|
||||
|
||||
using key_fetcher = expect<std::vector<output_keys>>(std::vector<output_ref>);
|
||||
|
||||
/*!
|
||||
Selects random outputs for use in a ring signature. `amounts` of `0`
|
||||
use a gamma distribution algorithm and all other amounts use a
|
||||
triangular distribution.
|
||||
|
||||
\param mixin The number of dummy outputs per ring.
|
||||
\param amounts The amounts that need dummy outputs to be selected.
|
||||
\param pick_rct Ring-ct distribution from the daemon
|
||||
\param histograms A histogram from the daemon foreach non-zero value
|
||||
in `amounts`.
|
||||
\param fetch A function that can retrieve the keys for the randomly
|
||||
selected outputs.
|
||||
|
||||
\note `histograms` is modified - the list is sorted by amount.
|
||||
|
||||
\note This currenty leaks the real outputs to `fetch`, because the
|
||||
real output is not provided alongside the dummy outputs. This is a
|
||||
limitation of the current openmonero/mymonero API. When this is
|
||||
resolved, this function can possibly be moved outside of the `lws`
|
||||
namespace for use by simple wallet.
|
||||
|
||||
\return Randomly selected outputs in rings of size `mixin`, one for
|
||||
each element in `amounts`. Amounts with less than `mixin` available
|
||||
are not returned. All outputs are unlocked.
|
||||
*/
|
||||
expect<std::vector<random_ring>> pick_random_outputs(
|
||||
std::uint32_t mixin,
|
||||
epee::span<const std::uint64_t> amounts,
|
||||
gamma_picker& pick_rct,
|
||||
epee::span<histogram> histograms,
|
||||
std::function<key_fetcher> fetch
|
||||
);
|
||||
}
|
||||
|
||||
61
src/util/transactions.cpp
Normal file
61
src/util/transactions.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
// 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 "transactions.h"
|
||||
|
||||
#include "cryptonote_config.h"
|
||||
#include "crypto/crypto.h"
|
||||
#include "crypto/hash.h"
|
||||
#include "ringct/rctOps.h"
|
||||
|
||||
void lws::decrypt_payment_id(crypto::hash8& out, const crypto::key_derivation& key)
|
||||
{
|
||||
crypto::hash hash;
|
||||
char data[33]; /* A hash, and an extra byte */
|
||||
|
||||
memcpy(data, &key, 32);
|
||||
data[32] = config::HASH_KEY_ENCRYPTED_PAYMENT_ID;
|
||||
cn_fast_hash(data, 33, hash);
|
||||
|
||||
for (size_t b = 0; b < 8; ++b)
|
||||
out.data[b] ^= hash.data[b];
|
||||
}
|
||||
|
||||
boost::optional<std::pair<std::uint64_t, rct::key>> lws::decode_amount(const rct::key& commitment, const rct::ecdhTuple& info, const crypto::key_derivation& sk, std::size_t index, const bool bulletproof2)
|
||||
{
|
||||
crypto::secret_key scalar{};
|
||||
crypto::derivation_to_scalar(sk, index, scalar);
|
||||
|
||||
rct::ecdhTuple copy{info};
|
||||
rct::ecdhDecode(copy, rct::sk2rct(scalar), bulletproof2);
|
||||
|
||||
rct::key Ctmp;
|
||||
rct::addKeys2(Ctmp, copy.mask, copy.amount, rct::H);
|
||||
if (rct::equalKeys(commitment, Ctmp))
|
||||
return {{rct::h2d(copy.amount), copy.mask}};
|
||||
return boost::none;
|
||||
}
|
||||
45
src/util/transactions.h
Normal file
45
src/util/transactions.h
Normal file
@@ -0,0 +1,45 @@
|
||||
// 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 <boost/optional/optional.hpp>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
#include "common/pod-class.h"
|
||||
#include "ringct/rctTypes.h"
|
||||
|
||||
namespace crypto
|
||||
{
|
||||
POD_CLASS hash8;
|
||||
POD_CLASS key_derivation;
|
||||
}
|
||||
|
||||
namespace lws
|
||||
{
|
||||
void decrypt_payment_id(crypto::hash8& out, const crypto::key_derivation& key);
|
||||
boost::optional<std::pair<std::uint64_t, rct::key>> decode_amount(const rct::key& commitment, const rct::ecdhTuple& info, const crypto::key_derivation& sk, std::size_t index, const bool bulletproof2);
|
||||
}
|
||||
Reference in New Issue
Block a user