Initial working base separated from top-level monero project

This commit is contained in:
Lee Clagett
2020-08-19 18:29:32 -04:00
commit a2ff89bc24
68 changed files with 11543 additions and 0 deletions

33
src/util/CMakeLists.txt Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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);
}