more updates - removing common/util.cpp cause aint nobody got time for that, and modified threadpool.cpp to encapsulate expansion to tools namespace of max_concurrency accessor
parent
0e5a7ceec1
commit
34fa840ba4
@ -0,0 +1,70 @@
|
||||
// Copyright (c) 2017-2018, 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.
|
||||
//
|
||||
// Most of this file is originally copyright (c) 2017 Raymond Chen, Microsoft
|
||||
// This algorithm is adapted from Raymond Chen's code:
|
||||
// https://blogs.msdn.microsoft.com/oldnewthing/20170109-00/?p=95145
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include "misc_log_ex.h"
|
||||
|
||||
namespace tools
|
||||
{
|
||||
|
||||
template<typename F>
|
||||
void apply_permutation(std::vector<size_t> permutation, const F &swap)
|
||||
{
|
||||
//sanity check
|
||||
for (size_t n = 0; n < permutation.size(); ++n)
|
||||
CHECK_AND_ASSERT_THROW_MES(std::find(permutation.begin(), permutation.end(), n) != permutation.end(), "Bad permutation");
|
||||
|
||||
for (size_t i = 0; i < permutation.size(); ++i)
|
||||
{
|
||||
size_t current = i;
|
||||
while (i != permutation[current])
|
||||
{
|
||||
size_t next = permutation[current];
|
||||
swap(current, next);
|
||||
permutation[current] = current;
|
||||
current = next;
|
||||
}
|
||||
permutation[current] = current;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void apply_permutation(const std::vector<size_t> &permutation, std::vector<T> &v)
|
||||
{
|
||||
CHECK_AND_ASSERT_THROW_MES(permutation.size() == v.size(), "Mismatched vector sizes");
|
||||
apply_permutation(permutation, [&v](size_t i0, size_t i1){ std::swap(v[i0], v[i1]); });
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,182 @@
|
||||
// Copyright (c) 2017-2018, 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 "misc_log_ex.h"
|
||||
#include "common/threadpool.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "cryptonote_config.h"
|
||||
#include "common/util.h"
|
||||
|
||||
static __thread int depth = 0;
|
||||
static __thread bool is_leaf = false;
|
||||
|
||||
namespace tools
|
||||
{
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
boost::mutex max_concurrency_lock;
|
||||
unsigned max_concurrency = boost::thread::hardware_concurrency();
|
||||
}
|
||||
|
||||
void set_max_concurrency(unsigned n)
|
||||
{
|
||||
if (n < 1)
|
||||
n = boost::thread::hardware_concurrency();
|
||||
unsigned hwc = boost::thread::hardware_concurrency();
|
||||
if (n > hwc)
|
||||
n = hwc;
|
||||
boost::lock_guard<boost::mutex> lock(max_concurrency_lock);
|
||||
max_concurrency = n;
|
||||
}
|
||||
|
||||
unsigned get_max_concurrency()
|
||||
{
|
||||
boost::lock_guard<boost::mutex> lock(max_concurrency_lock);
|
||||
return max_concurrency;
|
||||
}
|
||||
|
||||
|
||||
threadpool::threadpool(unsigned int max_threads) : running(true), active(0) {
|
||||
boost::thread::attributes attrs;
|
||||
attrs.set_stack_size(THREAD_STACK_SIZE);
|
||||
max = max_threads ? max_threads : tools::get_max_concurrency();
|
||||
size_t i = max ? max - 1 : 0;
|
||||
while(i--) {
|
||||
threads.push_back(boost::thread(attrs, boost::bind(&threadpool::run, this, false)));
|
||||
}
|
||||
}
|
||||
|
||||
threadpool::~threadpool() {
|
||||
{
|
||||
const boost::unique_lock<boost::mutex> lock(mutex);
|
||||
running = false;
|
||||
has_work.notify_all();
|
||||
}
|
||||
for (size_t i = 0; i<threads.size(); i++) {
|
||||
threads[i].join();
|
||||
}
|
||||
}
|
||||
|
||||
void threadpool::submit(waiter *obj, std::function<void()> f, bool leaf) {
|
||||
CHECK_AND_ASSERT_THROW_MES(!is_leaf, "A leaf routine is using a thread pool");
|
||||
boost::unique_lock<boost::mutex> lock(mutex);
|
||||
if (!leaf && ((active == max && !queue.empty()) || depth > 0)) {
|
||||
// if all available threads are already running
|
||||
// and there's work waiting, just run in current thread
|
||||
lock.unlock();
|
||||
++depth;
|
||||
is_leaf = leaf;
|
||||
f();
|
||||
--depth;
|
||||
is_leaf = false;
|
||||
} else {
|
||||
if (obj)
|
||||
obj->inc();
|
||||
if (leaf)
|
||||
queue.push_front({obj, f, leaf});
|
||||
else
|
||||
queue.push_back({obj, f, leaf});
|
||||
has_work.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int threadpool::get_max_concurrency() const {
|
||||
return max;
|
||||
}
|
||||
|
||||
threadpool::waiter::~waiter()
|
||||
{
|
||||
{
|
||||
boost::unique_lock<boost::mutex> lock(mt);
|
||||
if (num)
|
||||
MERROR("wait should have been called before waiter dtor - waiting now");
|
||||
}
|
||||
try
|
||||
{
|
||||
wait(NULL);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
/* ignored */
|
||||
}
|
||||
}
|
||||
|
||||
void threadpool::waiter::wait(threadpool *tpool) {
|
||||
if (tpool)
|
||||
tpool->run(true);
|
||||
boost::unique_lock<boost::mutex> lock(mt);
|
||||
while(num)
|
||||
cv.wait(lock);
|
||||
}
|
||||
|
||||
void threadpool::waiter::inc() {
|
||||
const boost::unique_lock<boost::mutex> lock(mt);
|
||||
num++;
|
||||
}
|
||||
|
||||
void threadpool::waiter::dec() {
|
||||
const boost::unique_lock<boost::mutex> lock(mt);
|
||||
num--;
|
||||
if (!num)
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void threadpool::run(bool flush) {
|
||||
boost::unique_lock<boost::mutex> lock(mutex);
|
||||
while (running) {
|
||||
entry e;
|
||||
while(queue.empty() && running)
|
||||
{
|
||||
if (flush)
|
||||
return;
|
||||
has_work.wait(lock);
|
||||
}
|
||||
if (!running) break;
|
||||
|
||||
active++;
|
||||
e = queue.front();
|
||||
queue.pop_front();
|
||||
lock.unlock();
|
||||
++depth;
|
||||
is_leaf = e.leaf;
|
||||
e.f();
|
||||
--depth;
|
||||
is_leaf = false;
|
||||
|
||||
if (e.wo)
|
||||
e.wo->dec();
|
||||
lock.lock();
|
||||
active--;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
// Copyright (c) 2017-2018, 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/thread/condition_variable.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <boost/thread/thread.hpp>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace tools
|
||||
{
|
||||
//! A global thread pool
|
||||
class threadpool
|
||||
{
|
||||
public:
|
||||
static threadpool& getInstance() {
|
||||
static threadpool instance;
|
||||
return instance;
|
||||
}
|
||||
static threadpool *getNewForUnitTests(unsigned max_threads = 0) {
|
||||
return new threadpool(max_threads);
|
||||
}
|
||||
|
||||
// The waiter lets the caller know when all of its
|
||||
// tasks are completed.
|
||||
class waiter {
|
||||
boost::mutex mt;
|
||||
boost::condition_variable cv;
|
||||
int num;
|
||||
public:
|
||||
void inc();
|
||||
void dec();
|
||||
void wait(threadpool *tpool); //! Wait for a set of tasks to finish.
|
||||
waiter() : num(0){}
|
||||
~waiter();
|
||||
};
|
||||
|
||||
// Submit a task to the pool. The waiter pointer may be
|
||||
// NULL if the caller doesn't care to wait for the
|
||||
// task to finish.
|
||||
void submit(waiter *waiter, std::function<void()> f, bool leaf = false);
|
||||
|
||||
unsigned int get_max_concurrency() const;
|
||||
|
||||
~threadpool();
|
||||
|
||||
private:
|
||||
threadpool(unsigned int max_threads = 0);
|
||||
typedef struct entry {
|
||||
waiter *wo;
|
||||
std::function<void()> f;
|
||||
bool leaf;
|
||||
} entry;
|
||||
std::deque<entry> queue;
|
||||
boost::condition_variable has_work;
|
||||
boost::mutex mutex;
|
||||
std::vector<boost::thread> threads;
|
||||
unsigned int active;
|
||||
unsigned int max;
|
||||
bool running;
|
||||
void run(bool flush = false);
|
||||
};
|
||||
|
||||
}
|
@ -1,922 +0,0 @@
|
||||
// Copyright (c) 2014-2018, 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.
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#ifdef __GLIBC__
|
||||
#include <gnu/libc-version.h>
|
||||
#endif
|
||||
|
||||
#ifdef __GLIBC__
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <ustat.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <string>
|
||||
#endif
|
||||
|
||||
#include "unbound.h"
|
||||
|
||||
#include "include_base_utils.h"
|
||||
#include "file_io_utils.h"
|
||||
#include "wipeable_string.h"
|
||||
using namespace epee;
|
||||
|
||||
#include "crypto/crypto.h"
|
||||
#include "util.h"
|
||||
#include "stack_trace.h"
|
||||
#include "memwipe.h"
|
||||
#include "cryptonote_config.h"
|
||||
#include "net/http_client.h" // epee::net_utils::...
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#include <shlobj.h>
|
||||
#include <strsafe.h>
|
||||
#else
|
||||
#include <sys/file.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
namespace tools
|
||||
{
|
||||
std::function<void(int)> signal_handler::m_handler;
|
||||
|
||||
private_file::private_file() noexcept : m_handle(), m_filename() {}
|
||||
|
||||
private_file::private_file(std::FILE* handle, std::string&& filename) noexcept
|
||||
: m_handle(handle), m_filename(std::move(filename)) {}
|
||||
|
||||
private_file private_file::create(std::string name)
|
||||
{
|
||||
#ifdef WIN32
|
||||
struct close_handle
|
||||
{
|
||||
void operator()(HANDLE handle) const noexcept
|
||||
{
|
||||
CloseHandle(handle);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<void, close_handle> process = nullptr;
|
||||
{
|
||||
HANDLE temp{};
|
||||
const bool fail = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, std::addressof(temp)) == 0;
|
||||
process.reset(temp);
|
||||
if (fail)
|
||||
return {};
|
||||
}
|
||||
|
||||
DWORD sid_size = 0;
|
||||
GetTokenInformation(process.get(), TokenOwner, nullptr, 0, std::addressof(sid_size));
|
||||
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
||||
return {};
|
||||
|
||||
std::unique_ptr<char[]> sid{new char[sid_size]};
|
||||
if (!GetTokenInformation(process.get(), TokenOwner, sid.get(), sid_size, std::addressof(sid_size)))
|
||||
return {};
|
||||
|
||||
const PSID psid = reinterpret_cast<const PTOKEN_OWNER>(sid.get())->Owner;
|
||||
const DWORD daclSize =
|
||||
sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(psid) - sizeof(DWORD);
|
||||
|
||||
const std::unique_ptr<char[]> dacl{new char[daclSize]};
|
||||
if (!InitializeAcl(reinterpret_cast<PACL>(dacl.get()), daclSize, ACL_REVISION))
|
||||
return {};
|
||||
|
||||
if (!AddAccessAllowedAce(reinterpret_cast<PACL>(dacl.get()), ACL_REVISION, (READ_CONTROL | FILE_GENERIC_READ | DELETE), psid))
|
||||
return {};
|
||||
|
||||
SECURITY_DESCRIPTOR descriptor{};
|
||||
if (!InitializeSecurityDescriptor(std::addressof(descriptor), SECURITY_DESCRIPTOR_REVISION))
|
||||
return {};
|
||||
|
||||
if (!SetSecurityDescriptorDacl(std::addressof(descriptor), true, reinterpret_cast<PACL>(dacl.get()), false))
|
||||
return {};
|
||||
|
||||
SECURITY_ATTRIBUTES attributes{sizeof(SECURITY_ATTRIBUTES), std::addressof(descriptor), false};
|
||||
std::unique_ptr<void, close_handle> file{
|
||||
CreateFile(
|
||||
name.c_str(),
|
||||
GENERIC_WRITE, FILE_SHARE_READ,
|
||||
std::addressof(attributes),
|
||||
CREATE_NEW, (FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE),
|
||||
nullptr
|
||||
)
|
||||
};
|
||||
if (file)
|
||||
{
|
||||
const int fd = _open_osfhandle(reinterpret_cast<intptr_t>(file.get()), 0);
|
||||
if (0 <= fd)
|
||||
{
|
||||
file.release();
|
||||
std::FILE* real_file = _fdopen(fd, "w");
|
||||
if (!real_file)
|
||||
{
|
||||
_close(fd);
|
||||
}
|
||||
return {real_file, std::move(name)};
|
||||
}
|
||||
}
|
||||
#else
|
||||
const int fdr = open(name.c_str(), (O_RDONLY | O_CREAT), S_IRUSR);
|
||||
if (0 <= fdr)
|
||||
{
|
||||
struct stat rstats = {};
|
||||
if (fstat(fdr, std::addressof(rstats)) != 0)
|
||||
{
|
||||
close(fdr);
|
||||
return {};
|
||||
}
|
||||
fchmod(fdr, (S_IRUSR | S_IWUSR));
|
||||
const int fdw = open(name.c_str(), O_RDWR);
|
||||
fchmod(fdr, rstats.st_mode);
|
||||
close(fdr);
|
||||
|
||||
if (0 <= fdw)
|
||||
{
|
||||
struct stat wstats = {};
|
||||
if (fstat(fdw, std::addressof(wstats)) == 0 &&
|
||||
rstats.st_dev == wstats.st_dev && rstats.st_ino == wstats.st_ino &&
|
||||
flock(fdw, (LOCK_EX | LOCK_NB)) == 0 && ftruncate(fdw, 0) == 0)
|
||||
{
|
||||
std::FILE* file = fdopen(fdw, "w");
|
||||
if (file) return {file, std::move(name)};
|
||||
}
|
||||
close(fdw);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
|
||||
private_file::~private_file() noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
boost::system::error_code ec{};
|
||||
boost::filesystem::remove(filename(), ec);
|
||||
}
|
||||
catch (...) {}
|
||||
}
|
||||
|
||||
file_locker::file_locker(const std::string &filename)
|
||||
{
|
||||
#ifdef WIN32
|
||||
m_fd = INVALID_HANDLE_VALUE;
|
||||
std::wstring filename_wide;
|
||||
try
|
||||
{
|
||||
filename_wide = string_tools::utf8_to_utf16(filename);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
MERROR("Failed to convert path \"" << filename << "\" to UTF-16: " << e.what());
|
||||
return;
|
||||
}
|
||||
m_fd = CreateFileW(filename_wide.c_str(), GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (m_fd != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
OVERLAPPED ov;
|
||||
memset(&ov, 0, sizeof(ov));
|
||||
if (!LockFileEx(m_fd, LOCKFILE_FAIL_IMMEDIATELY | LOCKFILE_EXCLUSIVE_LOCK, 0, 1, 0, &ov))
|
||||
{
|
||||
MERROR("Failed to lock " << filename << ": " << std::error_code(GetLastError(), std::system_category()));
|
||||
CloseHandle(m_fd);
|
||||
m_fd = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MERROR("Failed to open " << filename << ": " << std::error_code(GetLastError(), std::system_category()));
|
||||
}
|
||||
#else
|
||||
m_fd = open(filename.c_str(), O_RDONLY | O_CREAT, 0666);
|
||||
if (m_fd != -1)
|
||||
{
|
||||
if (flock(m_fd, LOCK_EX | LOCK_NB) == -1)
|
||||
{
|
||||
MERROR("Failed to lock " << filename << ": " << std::strerror(errno));
|
||||
close(m_fd);
|
||||
m_fd = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MERROR("Failed to open " << filename << ": " << std::strerror(errno));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
file_locker::~file_locker()
|
||||
{
|
||||
if (locked())
|
||||
{
|
||||
#ifdef WIN32
|
||||
CloseHandle(m_fd);
|
||||
#else
|
||||
close(m_fd);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
bool file_locker::locked() const
|
||||
{
|
||||
#ifdef WIN32
|
||||
return m_fd != INVALID_HANDLE_VALUE;
|
||||
#else
|
||||
return m_fd != -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
std::string get_windows_version_display_string()
|
||||
{
|
||||
typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO);
|
||||
typedef BOOL (WINAPI *PGPI)(DWORD, DWORD, DWORD, DWORD, PDWORD);
|
||||
#define BUFSIZE 10000
|
||||
|
||||
char pszOS[BUFSIZE] = {0};
|
||||
OSVERSIONINFOEX osvi;
|
||||
SYSTEM_INFO si;
|
||||
PGNSI pGNSI;
|
||||
PGPI pGPI;
|
||||
BOOL bOsVersionInfoEx;
|
||||
DWORD dwType;
|
||||
|
||||
ZeroMemory(&si, sizeof(SYSTEM_INFO));
|
||||
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
|
||||
|
||||
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
||||
bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO*) &osvi);
|
||||
|
||||
if(!bOsVersionInfoEx) return pszOS;
|
||||
|
||||
// Call GetNativeSystemInfo if supported or GetSystemInfo otherwise.
|
||||
|
||||
pGNSI = (PGNSI) GetProcAddress(
|
||||
GetModuleHandle(TEXT("kernel32.dll")),
|
||||
"GetNativeSystemInfo");
|
||||
if(NULL != pGNSI)
|
||||
pGNSI(&si);
|
||||
else GetSystemInfo(&si);
|
||||
|
||||
if ( VER_PLATFORM_WIN32_NT==osvi.dwPlatformId &&
|
||||
osvi.dwMajorVersion > 4 )
|
||||
{
|
||||
StringCchCopy(pszOS, BUFSIZE, TEXT("Microsoft "));
|
||||
|
||||
// Test for the specific product.
|
||||
|
||||
if ( osvi.dwMajorVersion == 6 )
|
||||
{
|
||||
if( osvi.dwMinorVersion == 0 )
|
||||
{
|
||||
if( osvi.wProductType == VER_NT_WORKSTATION )
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT("Windows Vista "));
|
||||
else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2008 " ));
|
||||
}
|
||||
|
||||
if ( osvi.dwMinorVersion == 1 )
|
||||
{
|
||||
if( osvi.wProductType == VER_NT_WORKSTATION )
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT("Windows 7 "));
|
||||
else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2008 R2 " ));
|
||||
}
|
||||
|
||||
pGPI = (PGPI) GetProcAddress(
|
||||
GetModuleHandle(TEXT("kernel32.dll")),
|
||||
"GetProductInfo");
|
||||
|
||||
pGPI( osvi.dwMajorVersion, osvi.dwMinorVersion, 0, 0, &dwType);
|
||||
|
||||
switch( dwType )
|
||||
{
|
||||
case PRODUCT_ULTIMATE:
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT("Ultimate Edition" ));
|
||||
break;
|
||||
case PRODUCT_PROFESSIONAL:
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT("Professional" ));
|
||||
break;
|
||||
case PRODUCT_HOME_PREMIUM:
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT("Home Premium Edition" ));
|
||||
break;
|
||||
case PRODUCT_HOME_BASIC:
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT("Home Basic Edition" ));
|
||||
break;
|
||||
case PRODUCT_ENTERPRISE:
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT("Enterprise Edition" ));
|
||||
break;
|
||||
case PRODUCT_BUSINESS:
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT("Business Edition" ));
|
||||
break;
|
||||
case PRODUCT_STARTER:
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT("Starter Edition" ));
|
||||
break;
|
||||
case PRODUCT_CLUSTER_SERVER:
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT("Cluster Server Edition" ));
|
||||
break;
|
||||
case PRODUCT_DATACENTER_SERVER:
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT("Datacenter Edition" ));
|
||||
break;
|
||||
case PRODUCT_DATACENTER_SERVER_CORE:
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT("Datacenter Edition (core installation)" ));
|
||||
break;
|
||||
case PRODUCT_ENTERPRISE_SERVER:
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT("Enterprise Edition" ));
|
||||
break;
|
||||
case PRODUCT_ENTERPRISE_SERVER_CORE:
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT("Enterprise Edition (core installation)" ));
|
||||
break;
|
||||
case PRODUCT_ENTERPRISE_SERVER_IA64:
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT("Enterprise Edition for Itanium-based Systems" ));
|
||||
break;
|
||||
case PRODUCT_SMALLBUSINESS_SERVER:
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT("Small Business Server" ));
|
||||
break;
|
||||
case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM:
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT("Small Business Server Premium Edition" ));
|
||||
break;
|
||||
case PRODUCT_STANDARD_SERVER:
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT("Standard Edition" ));
|
||||
break;
|
||||
case PRODUCT_STANDARD_SERVER_CORE:
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT("Standard Edition (core installation)" ));
|
||||
break;
|
||||
case PRODUCT_WEB_SERVER:
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT("Web Server Edition" ));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2 )
|
||||
{
|
||||
if( GetSystemMetrics(SM_SERVERR2) )
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT( "Windows Server 2003 R2, "));
|
||||
else if ( osvi.wSuiteMask & VER_SUITE_STORAGE_SERVER )
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT( "Windows Storage Server 2003"));
|
||||
else if ( osvi.wSuiteMask & VER_SUITE_WH_SERVER )
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT( "Windows Home Server"));
|
||||
else if( osvi.wProductType == VER_NT_WORKSTATION &&
|
||||
si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64)
|
||||
{
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT( "Windows XP Professional x64 Edition"));
|
||||
}
|
||||
else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2003, "));
|
||||
|
||||
// Test for the server type.
|
||||
if ( osvi.wProductType != VER_NT_WORKSTATION )
|
||||
{
|
||||
if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_IA64 )
|
||||
{
|
||||
if( osvi.wSuiteMask & VER_SUITE_DATACENTER )
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT( "Datacenter Edition for Itanium-based Systems" ));
|
||||
else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT( "Enterprise Edition for Itanium-based Systems" ));
|
||||
}
|
||||
|
||||
else if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64 )
|
||||
{
|
||||
if( osvi.wSuiteMask & VER_SUITE_DATACENTER )
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT( "Datacenter x64 Edition" ));
|
||||
else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT( "Enterprise x64 Edition" ));
|
||||
else StringCchCat(pszOS, BUFSIZE, TEXT( "Standard x64 Edition" ));
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if ( osvi.wSuiteMask & VER_SUITE_COMPUTE_SERVER )
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT( "Compute Cluster Edition" ));
|
||||
else if( osvi.wSuiteMask & VER_SUITE_DATACENTER )
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT( "Datacenter Edition" ));
|
||||
else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT( "Enterprise Edition" ));
|
||||
else if ( osvi.wSuiteMask & VER_SUITE_BLADE )
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT( "Web Edition" ));
|
||||
else StringCchCat(pszOS, BUFSIZE, TEXT( "Standard Edition" ));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1 )
|
||||
{
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT("Windows XP "));
|
||||
if( osvi.wSuiteMask & VER_SUITE_PERSONAL )
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT( "Home Edition" ));
|
||||
else StringCchCat(pszOS, BUFSIZE, TEXT( "Professional" ));
|
||||
}
|
||||
|
||||
if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0 )
|
||||
{
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT("Windows 2000 "));
|
||||
|
||||
if ( osvi.wProductType == VER_NT_WORKSTATION )
|
||||
{
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT( "Professional" ));
|
||||
}
|
||||
else
|
||||
{
|
||||
if( osvi.wSuiteMask & VER_SUITE_DATACENTER )
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT( "Datacenter Server" ));
|
||||
else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT( "Advanced Server" ));
|
||||
else StringCchCat(pszOS, BUFSIZE, TEXT( "Server" ));
|
||||
}
|
||||
}
|
||||
|
||||
// Include service pack (if any) and build number.
|
||||
|
||||
if( strlen(osvi.szCSDVersion) > 0 )
|
||||
{
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT(" ") );
|
||||
StringCchCat(pszOS, BUFSIZE, osvi.szCSDVersion);
|
||||
}
|
||||
|
||||
TCHAR buf[80];
|
||||
|
||||
StringCchPrintf( buf, 80, TEXT(" (build %d)"), osvi.dwBuildNumber);
|
||||
StringCchCat(pszOS, BUFSIZE, buf);
|
||||
|
||||
if ( osvi.dwMajorVersion >= 6 )
|
||||
{
|
||||
if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64 )
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT( ", 64-bit" ));
|
||||
else if (si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_INTEL )
|
||||
StringCchCat(pszOS, BUFSIZE, TEXT(", 32-bit"));
|
||||
}
|
||||
|
||||
return pszOS;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf( "This sample does not support this version of Windows.\n");
|
||||
return pszOS;
|
||||
}
|
||||
}
|
||||
#else
|
||||
std::string get_nix_version_display_string()
|
||||
{
|
||||
struct utsname un;
|
||||
|
||||
if(uname(&un) < 0)
|
||||
return std::string("*nix: failed to get os version");
|
||||
return std::string() + un.sysname + " " + un.version + " " + un.release;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
std::string get_os_version_string()
|
||||
{
|
||||
#ifdef WIN32
|
||||
return get_windows_version_display_string();
|
||||
#else
|
||||
return get_nix_version_display_string();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
std::string get_special_folder_path(int nfolder, bool iscreate)
|
||||
{
|
||||
WCHAR psz_path[MAX_PATH] = L"";
|
||||
|
||||
if (SHGetSpecialFolderPathW(NULL, psz_path, nfolder, iscreate))
|
||||
{
|
||||
try
|
||||
{
|
||||
return string_tools::utf16_to_utf8(psz_path);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
MERROR("utf16_to_utf8 failed: " << e.what());
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
LOG_ERROR("SHGetSpecialFolderPathW() failed, could not obtain requested path.");
|
||||
return "";
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string get_default_data_dir()
|
||||
{
|
||||
/* Please for the love of god refactor the ifdefs out of this */
|
||||
|
||||
// namespace fs = boost::filesystem;
|
||||
// Windows < Vista: C:\Documents and Settings\Username\Application Data\CRYPTONOTE_NAME
|
||||
// Windows >= Vista: C:\Users\Username\AppData\Roaming\CRYPTONOTE_NAME
|
||||
// Unix & Mac: ~/.CRYPTONOTE_NAME
|
||||
std::string config_folder;
|
||||
|
||||
#ifdef WIN32
|
||||
config_folder = get_special_folder_path(CSIDL_COMMON_APPDATA, true) + "\\" + CRYPTONOTE_NAME;
|
||||
#else
|
||||
std::string pathRet;
|
||||
char* pszHome = getenv("HOME");
|
||||
if (pszHome == NULL || strlen(pszHome) == 0)
|
||||
pathRet = "/";
|
||||
else
|
||||
pathRet = pszHome;
|
||||
config_folder = (pathRet + "/." + CRYPTONOTE_NAME);
|
||||
#endif
|
||||
|
||||
return config_folder;
|
||||
}
|
||||
|
||||
bool create_directories_if_necessary(const std::string& path)
|
||||
{
|
||||
namespace fs = boost::filesystem;
|
||||
boost::system::error_code ec;
|
||||
fs::path fs_path(path);
|
||||
if (fs::is_directory(fs_path, ec))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool res = fs::create_directories(fs_path, ec);
|
||||
if (res)
|
||||
{
|
||||
LOG_PRINT_L2("Created directory: " << path);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_PRINT_L2("Can't create directory: " << path << ", err: "<< ec.message());
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::error_code replace_file(const std::string& replacement_name, const std::string& replaced_name)
|
||||
{
|
||||
int code;
|
||||
#if defined(WIN32)
|
||||
// Maximizing chances for success
|
||||
std::wstring wide_replacement_name;
|
||||
try { wide_replacement_name = string_tools::utf8_to_utf16(replacement_name); }
|
||||
catch (...) { return std::error_code(GetLastError(), std::system_category()); }
|
||||
std::wstring wide_replaced_name;
|
||||
try { wide_replaced_name = string_tools::utf8_to_utf16(replaced_name); }
|
||||
catch (...) { return std::error_code(GetLastError(), std::system_category()); }
|
||||
|
||||
DWORD attributes = ::GetFileAttributesW(wide_replaced_name.c_str());
|
||||
if (INVALID_FILE_ATTRIBUTES != attributes)
|
||||
{
|
||||
::SetFileAttributesW(wide_replaced_name.c_str(), attributes & (~FILE_ATTRIBUTE_READONLY));
|
||||
}
|
||||
|
||||
bool ok = 0 != ::MoveFileExW(wide_replacement_name.c_str(), wide_replaced_name.c_str(), MOVEFILE_REPLACE_EXISTING);
|
||||
code = ok ? 0 : static_cast<int>(::GetLastError());
|
||||
#else
|
||||
bool ok = 0 == std::rename(replacement_name.c_str(), replaced_name.c_str());
|
||||
code = ok ? 0 : errno;
|
||||
#endif
|
||||
return std::error_code(code, std::system_category());
|
||||
}
|
||||
|
||||
static bool unbound_built_with_threads()
|
||||
{
|
||||
ub_ctx *ctx = ub_ctx_create();
|
||||
if (!ctx) return false; // cheat a bit, should not happen unless OOM
|
||||
char *monero = strdup("monero"), *unbound = strdup("unbound");
|
||||
ub_ctx_zone_add(ctx, monero, unbound); // this calls ub_ctx_finalize first, then errors out with UB_SYNTAX
|
||||
free(unbound);
|
||||
free(monero);
|
||||
// if no threads, bails out early with UB_NOERROR, otherwise fails with UB_AFTERFINAL id already finalized
|
||||
bool with_threads = ub_ctx_async(ctx, 1) != 0; // UB_AFTERFINAL is not defined in public headers, check any error
|
||||
ub_ctx_delete(ctx);
|
||||
MINFO("libunbound was built " << (with_threads ? "with" : "without") << " threads");
|
||||
return with_threads;
|
||||
}
|
||||
|
||||
bool sanitize_locale()
|
||||
{
|
||||
// boost::filesystem throws for "invalid" locales, such as en_US.UTF-8, or kjsdkfs,
|
||||
// so reset it here before any calls to it
|
||||
try
|
||||
{
|
||||
boost::filesystem::path p {std::string("test")};
|
||||
p /= std::string("test");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
#if defined(__MINGW32__) || defined(__MINGW__)
|
||||
putenv("LC_ALL=C");
|
||||
putenv("LANG=C");
|
||||
#else
|
||||
setenv("LC_ALL", "C", 1);
|
||||
setenv("LANG", "C", 1);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef STACK_TRACE
|
||||
#ifdef _WIN32
|
||||
// https://stackoverflow.com/questions/1992816/how-to-handle-seg-faults-under-windows
|
||||
static LONG WINAPI windows_crash_handler(PEXCEPTION_POINTERS pExceptionInfo)
|
||||
{
|
||||
tools::log_stack_trace("crashing");
|
||||
exit(1);
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
static void setup_crash_dump()
|
||||
{
|
||||
SetUnhandledExceptionFilter(windows_crash_handler);
|
||||
}
|
||||
#else
|
||||
static void posix_crash_handler(int signal)
|
||||
{
|
||||
tools::log_stack_trace(("crashing with fatal signal " + std::to_string(signal)).c_str());
|
||||
#ifdef NDEBUG
|
||||
_exit(1);
|
||||
#else
|
||||
abort();
|
||||
#endif
|
||||
}
|
||||
static void setup_crash_dump()
|
||||
{
|
||||
signal(SIGSEGV, posix_crash_handler);
|
||||
signal(SIGBUS, posix_crash_handler);
|
||||
signal(SIGILL, posix_crash_handler);
|
||||
signal(SIGFPE, posix_crash_handler);
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
static void setup_crash_dump() {}
|
||||
#endif
|
||||
|
||||
bool on_startup()
|
||||
{
|
||||
mlog_configure("", true);
|
||||
|
||||
setup_crash_dump();
|
||||
|
||||
sanitize_locale();
|
||||
|
||||
#ifdef __GLIBC__
|
||||
const char *ver = gnu_get_libc_version();
|
||||
if (!strcmp(ver, "2.25"))
|
||||
MCLOG_RED(el::Level::Warning, "global", "Running with glibc " << ver << ", hangs may occur - change glibc version if possible");
|
||||
#endif
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000 || defined(LIBRESSL_VERSION_TEXT)
|
||||
SSL_library_init();
|
||||
#else
|
||||
OPENSSL_init_ssl(0, NULL);
|
||||
#endif
|
||||
|
||||
if (!unbound_built_with_threads())
|
||||
MCLOG_RED(el::Level::Warning, "global", "libunbound was not built with threads enabled - crashes may occur");
|
||||
|
||||
return true;
|
||||
}
|
||||
void set_strict_default_file_permissions(bool strict)
|
||||
{
|
||||
#if defined(__MINGW32__) || defined(__MINGW__)
|
||||
// no clue about the odd one out
|
||||
#else
|
||||
mode_t mode = strict ? 077 : 0;
|
||||
umask(mode);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool is_hdd(const char *path)
|
||||
{
|
||||
#ifdef __GLIBC__
|
||||
std::string device = "";
|
||||
struct stat st, dst;
|
||||
if (stat(path, &st) < 0)
|
||||
return 0;
|
||||
|
||||
DIR *dir = opendir("/dev/block");
|
||||
if (!dir)
|
||||
return 0;
|
||||
struct dirent *de;
|
||||
while ((de = readdir(dir)))
|
||||
{
|
||||
if (strcmp(de->d_name, ".") && strcmp(de->d_name, ".."))
|
||||
{
|
||||
std::string dev_path = std::string("/dev/block/") + de->d_name;
|
||||
char resolved[PATH_MAX];
|
||||
if (realpath(dev_path.c_str(), resolved) && !strncmp(resolved, "/dev/", 5))
|
||||
{
|
||||
if (stat(resolved, &dst) == 0)
|
||||
{
|
||||
if (dst.st_rdev == st.st_dev)
|
||||
{
|
||||
// take out trailing digits (eg, sda1 -> sda)
|
||||
char *ptr = resolved;
|
||||
while (*ptr)
|
||||
++ptr;
|
||||
while (ptr > resolved && isdigit(*--ptr))
|
||||
*ptr = 0;
|
||||
device = resolved + 5;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
if (device.empty())
|
||||
return 0;
|
||||
|
||||
std::string sys_path = "/sys/block/" + device + "/queue/rotational";
|
||||
FILE *f = fopen(sys_path.c_str(), "r");
|
||||
if (!f)
|
||||
return false;
|
||||
char s[8];
|
||||
char *ptr = fgets(s, sizeof(s), f);
|
||||
fclose(f);
|
||||
if (!ptr)
|
||||
return 0;
|
||||
s[sizeof(s) - 1] = 0;
|
||||
int n = atoi(s); // returns 0 on parse error
|
||||
return n == 1;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
boost::mutex max_concurrency_lock;
|
||||
unsigned max_concurrency = boost::thread::hardware_concurrency();
|
||||
}
|
||||
|
||||
void set_max_concurrency(unsigned n)
|
||||
{
|
||||
if (n < 1)
|
||||
n = boost::thread::hardware_concurrency();
|
||||
unsigned hwc = boost::thread::hardware_concurrency();
|
||||
if (n > hwc)
|
||||
n = hwc;
|
||||
boost::lock_guard<boost::mutex> lock(max_concurrency_lock);
|
||||
max_concurrency = n;
|
||||
}
|
||||
|
||||
unsigned get_max_concurrency()
|
||||
{
|
||||
boost::lock_guard<boost::mutex> lock(max_concurrency_lock);
|
||||
return max_concurrency;
|
||||
}
|
||||
|
||||
bool is_local_address(const std::string &address)
|
||||
{
|
||||
// always assume Tor/I2P addresses to be untrusted by default
|
||||
if (boost::ends_with(address, ".onion") || boost::ends_with(address, ".i2p"))
|
||||
{
|
||||
MDEBUG("Address '" << address << "' is Tor/I2P, non local");
|
||||
return false;
|
||||
}
|
||||
|
||||
// extract host
|
||||
epee::net_utils::http::url_content u_c;
|
||||
if (!epee::net_utils::parse_url(address, u_c))
|
||||
{
|
||||
MWARNING("Failed to determine whether address '" << address << "' is local, assuming not");
|
||||
return false;
|
||||
}
|
||||
if (u_c.host.empty())
|
||||
{
|
||||
MWARNING("Failed to determine whether address '" << address << "' is local, assuming not");
|
||||
return false;
|
||||
}
|
||||
|
||||
// resolve to IP
|
||||
boost::asio::io_service io_service;
|
||||
boost::asio::ip::tcp::resolver resolver(io_service);
|
||||
boost::asio::ip::tcp::resolver::query query(u_c.host, "");
|
||||
boost::asio::ip::tcp::resolver::iterator i = resolver.resolve(query);
|
||||
while (i != boost::asio::ip::tcp::resolver::iterator())
|
||||
{
|
||||
const boost::asio::ip::tcp::endpoint &ep = *i;
|
||||
if (ep.address().is_loopback())
|
||||
{
|
||||
MDEBUG("Address '" << address << "' is local");
|
||||
return true;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
MDEBUG("Address '" << address << "' is not local");
|
||||
return false;
|
||||
}
|
||||
int vercmp(const char *v0, const char *v1)
|
||||
{
|
||||
std::vector<std::string> f0, f1;
|
||||
boost::split(f0, v0, boost::is_any_of(".-"));
|
||||
boost::split(f1, v1, boost::is_any_of(".-"));
|
||||
for (size_t i = 0; i < std::max(f0.size(), f1.size()); ++i) {
|
||||
if (i >= f0.size())
|
||||
return -1;
|
||||
if (i >= f1.size())
|
||||
return 1;
|
||||
int f0i = atoi(f0[i].c_str()), f1i = atoi(f1[i].c_str());
|
||||
int n = f0i - f1i;
|
||||
if (n)
|
||||
return n;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool sha256sum(const uint8_t *data, size_t len, crypto::hash &hash)
|
||||
{
|
||||
SHA256_CTX ctx;
|
||||
if (!SHA256_Init(&ctx))
|
||||
return false;
|
||||
if (!SHA256_Update(&ctx, data, len))
|
||||
return false;
|
||||
if (!SHA256_Final((unsigned char*)hash.data, &ctx))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sha256sum(const std::string &filename, crypto::hash &hash)
|
||||
{
|
||||
if (!epee::file_io_utils::is_file_exist(filename))
|
||||
return false;
|
||||
std::ifstream f;
|
||||
f.exceptions(std::ifstream::failbit | std::ifstream::badbit);
|
||||
f.open(filename, std::ios_base::binary | std::ios_base::in | std::ios::ate);
|
||||
if (!f)
|
||||
return false;
|
||||
std::ifstream::pos_type file_size = f.tellg();
|
||||
SHA256_CTX ctx;
|
||||
if (!SHA256_Init(&ctx))
|
||||
return false;
|
||||
size_t size_left = file_size;
|
||||
f.seekg(0, std::ios::beg);
|
||||
while (size_left)
|
||||
{
|
||||
char buf[4096];
|
||||
std::ifstream::pos_type read_size = size_left > sizeof(buf) ? sizeof(buf) : size_left;
|
||||
f.read(buf, read_size);
|
||||
if (!f || !f.good())
|
||||
return false;
|
||||
if (!SHA256_Update(&ctx, buf, read_size))
|
||||
return false;
|
||||
size_left -= read_size;
|
||||
}
|
||||
f.close();
|
||||
if (!SHA256_Final((unsigned char*)hash.data, &ctx))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
boost::optional<std::pair<uint32_t, uint32_t>> parse_subaddress_lookahead(const std::string& str)
|
||||
{
|
||||
auto pos = str.find(":");
|
||||
bool r = pos != std::string::npos;
|
||||
uint32_t major;
|
||||
r = r && epee::string_tools::get_xtype_from_string(major, str.substr(0, pos));
|
||||
uint32_t minor;
|
||||
r = r && epee::string_tools::get_xtype_from_string(minor, str.substr(pos + 1));
|
||||
if (r)
|
||||
{
|
||||
return std::make_pair(major, minor);
|
||||
}
|
||||
else
|
||||
{
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,660 @@
|
||||
// Copyright (c) 2014-2018, 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.
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
#include <unordered_set>
|
||||
#include <random>
|
||||
#include "include_base_utils.h"
|
||||
#include "string_tools.h"
|
||||
using namespace epee;
|
||||
|
||||
#include "common/apply_permutation.h"
|
||||
#include "cryptonote_tx_utils.h"
|
||||
#include "cryptonote_config.h"
|
||||
//#include "cryptonote_basic/miner.h"
|
||||
#include "crypto/crypto.h"
|
||||
#include "crypto/hash.h"
|
||||
#include "ringct/rctSigs.h"
|
||||
#include "multisig/multisig.h"
|
||||
|
||||
using namespace crypto;
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
//---------------------------------------------------------------
|
||||
void classify_addresses(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::account_public_address>& change_addr, size_t &num_stdaddresses, size_t &num_subaddresses, account_public_address &single_dest_subaddress)
|
||||
{
|
||||
num_stdaddresses = 0;
|
||||
num_subaddresses = 0;
|
||||
std::unordered_set<cryptonote::account_public_address> unique_dst_addresses;
|
||||
for(const tx_destination_entry& dst_entr: destinations)
|
||||
{
|
||||
if (change_addr && dst_entr.addr == change_addr)
|
||||
continue;
|
||||
if (unique_dst_addresses.count(dst_entr.addr) == 0)
|
||||
{
|
||||
unique_dst_addresses.insert(dst_entr.addr);
|
||||
if (dst_entr.is_subaddress)
|
||||
{
|
||||
++num_subaddresses;
|
||||
single_dest_subaddress = dst_entr.addr;
|
||||
}
|
||||
else
|
||||
{
|
||||
++num_stdaddresses;
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG_PRINT_L2("destinations include " << num_stdaddresses << " standard addresses and " << num_subaddresses << " subaddresses");
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
// bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce, size_t max_outs, uint8_t hard_fork_version) {
|
||||
// tx.vin.clear();
|
||||
// tx.vout.clear();
|
||||
// tx.extra.clear();
|
||||
//
|
||||
// keypair txkey = keypair::generate(hw::get_device("default"));
|
||||
// add_tx_pub_key_to_extra(tx, txkey.pub);
|
||||
// if(!extra_nonce.empty())
|
||||
// if(!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce))
|
||||
// return false;
|
||||
//
|
||||
// txin_gen in;
|
||||
// in.height = height;
|
||||
//
|
||||
// uint64_t block_reward;
|
||||
// if(!get_block_reward(median_size, current_block_size, already_generated_coins, block_reward, hard_fork_version))
|
||||
// {
|
||||
// LOG_PRINT_L0("Block is too big");
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
//#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
|
||||
// LOG_PRINT_L1("Creating block template: reward " << block_reward <<
|
||||
// ", fee " << fee);
|
||||
//#endif
|
||||
// block_reward += fee;
|
||||
//
|
||||
// // from hard fork 2, we cut out the low significant digits. This makes the tx smaller, and
|
||||
// // keeps the paid amount almost the same. The unpaid remainder gets pushed back to the
|
||||
// // emission schedule
|
||||
// // from hard fork 4, we use a single "dusty" output. This makes the tx even smaller,
|
||||
// // and avoids the quantization. These outputs will be added as rct outputs with identity
|
||||
// // masks, to they can be used as rct inputs.
|
||||
// if (hard_fork_version >= 2 && hard_fork_version < 4) {
|
||||
// block_reward = block_reward - block_reward % ::config::BASE_REWARD_CLAMP_THRESHOLD;
|
||||
// }
|
||||
//
|
||||
// std::vector<uint64_t> out_amounts;
|
||||
// decompose_amount_into_digits(block_reward, hard_fork_version >= 2 ? 0 : ::config::DEFAULT_DUST_THRESHOLD,
|
||||
// [&out_amounts](uint64_t a_chunk) { out_amounts.push_back(a_chunk); },
|
||||
// [&out_amounts](uint64_t a_dust) { out_amounts.push_back(a_dust); });
|
||||
//
|
||||
// CHECK_AND_ASSERT_MES(1 <= max_outs, false, "max_out must be non-zero");
|
||||
// if (height == 0 || hard_fork_version >= 4)
|
||||
// {
|
||||
// // the genesis block was not decomposed, for unknown reasons
|
||||
// while (max_outs < out_amounts.size())
|
||||
// {
|
||||
// //out_amounts[out_amounts.size() - 2] += out_amounts.back();
|
||||
// //out_amounts.resize(out_amounts.size() - 1);
|
||||
// out_amounts[1] += out_amounts[0];
|
||||
// for (size_t n = 1; n < out_amounts.size(); ++n)
|
||||
// out_amounts[n - 1] = out_amounts[n];
|
||||
// out_amounts.resize(out_amounts.size() - 1);
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// CHECK_AND_ASSERT_MES(max_outs >= out_amounts.size(), false, "max_out exceeded");
|
||||
// }
|
||||
//
|
||||
// uint64_t summary_amounts = 0;
|
||||
// for (size_t no = 0; no < out_amounts.size(); no++)
|
||||
// {
|
||||
// crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);;
|
||||
// crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key);
|
||||
// bool r = crypto::generate_key_derivation(miner_address.m_view_public_key, txkey.sec, derivation);
|
||||
// CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to generate_key_derivation(" << miner_address.m_view_public_key << ", " << txkey.sec << ")");
|
||||
//
|
||||
// r = crypto::derive_public_key(derivation, no, miner_address.m_spend_public_key, out_eph_public_key);
|
||||
// CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to derive_public_key(" << derivation << ", " << no << ", "<< miner_address.m_spend_public_key << ")");
|
||||
//
|
||||
// txout_to_key tk;
|
||||
// tk.key = out_eph_public_key;
|
||||
//
|
||||
// tx_out out;
|
||||
// summary_amounts += out.amount = out_amounts[no];
|
||||
// out.target = tk;
|
||||
// tx.vout.push_back(out);
|
||||
// }
|
||||
//
|
||||
// CHECK_AND_ASSERT_MES(summary_amounts == block_reward, false, "Failed to construct miner tx, summary_amounts = " << summary_amounts << " not equal block_reward = " << block_reward);
|
||||
//
|
||||
// if (hard_fork_version >= 4)
|
||||
// tx.version = 2;
|
||||
// else
|
||||
// tx.version = 1;
|
||||
//
|
||||
// //lock
|
||||
// tx.unlock_time = height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW;
|
||||
// tx.vin.push_back(in);
|
||||
//
|
||||
// tx.invalidate_hashes();
|
||||
//
|
||||
// //LOG_PRINT("MINER_TX generated ok, block_reward=" << print_money(block_reward) << "(" << print_money(block_reward - fee) << "+" << print_money(fee)
|
||||
// // << "), current_block_size=" << current_block_size << ", already_generated_coins=" << already_generated_coins << ", tx_id=" << get_transaction_hash(tx), LOG_LEVEL_2);
|
||||
// return true;
|
||||
// }
|
||||
//---------------------------------------------------------------
|
||||
crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::account_public_address>& change_addr)
|
||||
{
|
||||
account_public_address addr = {null_pkey, null_pkey};
|
||||
size_t count = 0;
|
||||
for (const auto &i : destinations)
|
||||
{
|
||||
if (i.amount == 0)
|
||||
continue;
|
||||
if (change_addr && i.addr == *change_addr)
|
||||
continue;
|
||||
if (i.addr == addr)
|
||||
continue;
|
||||
if (count > 0)
|
||||
return null_pkey;
|
||||
addr = i.addr;
|
||||
++count;
|
||||
}
|
||||
if (count == 0 && change_addr)
|
||||
return change_addr->m_view_public_key;
|
||||
return addr.m_view_public_key;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct, bool bulletproof, rct::multisig_out *msout, bool shuffle_outs)
|
||||
{
|
||||
hw::device &hwdev = sender_account_keys.get_device();
|
||||
|
||||
if (sources.empty())
|
||||
{
|
||||
LOG_ERROR("Empty sources");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<rct::key> amount_keys;
|
||||
tx.set_null();
|
||||
amount_keys.clear();
|
||||
if (msout)
|
||||
{
|
||||
msout->c.clear();
|
||||
}
|
||||
|
||||
tx.version = rct ? 2 : 1;
|
||||
tx.unlock_time = unlock_time;
|
||||
|
||||
tx.extra = extra;
|
||||
crypto::public_key txkey_pub;
|
||||
|
||||
// if we have a stealth payment id, find it and encrypt it with the tx key now
|
||||
std::vector<tx_extra_field> tx_extra_fields;
|
||||
if (parse_tx_extra(tx.extra, tx_extra_fields))
|
||||
{
|
||||
tx_extra_nonce extra_nonce;
|
||||
if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
|
||||
{
|
||||
crypto::hash8 payment_id = null_hash8;
|
||||
if (get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
|
||||
{
|
||||
LOG_PRINT_L2("Encrypting payment id " << payment_id);
|
||||
crypto::public_key view_key_pub = get_destination_view_key_pub(destinations, change_addr);
|
||||
if (view_key_pub == null_pkey)
|
||||
{
|
||||
LOG_ERROR("Destinations have to have exactly one output to support encrypted payment ids");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!hwdev.encrypt_payment_id(payment_id, view_key_pub, tx_key))
|
||||
{
|
||||
LOG_ERROR("Failed to encrypt payment id");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string extra_nonce;
|
||||
set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
|
||||
remove_field_from_tx_extra(tx.extra, typeid(tx_extra_nonce));
|
||||
if (!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce))
|
||||
{
|
||||
LOG_ERROR("Failed to add encrypted payment id to tx extra");
|
||||
return false;
|
||||
}
|
||||
LOG_PRINT_L1("Encrypted payment ID: " << payment_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("Failed to parse tx extra");
|
||||
return false;
|
||||
}
|
||||
|
||||
struct input_generation_context_data
|
||||
{
|
||||
keypair in_ephemeral;
|
||||
};
|
||||
std::vector<input_generation_context_data> in_contexts;
|
||||
|
||||
uint64_t summary_inputs_money = 0;
|
||||
//fill inputs
|
||||
int idx = -1;
|
||||
for(const tx_source_entry& src_entr: sources)
|
||||
{
|
||||
++idx;
|
||||
if(src_entr.real_output >= src_entr.outputs.size())
|
||||
{
|
||||
LOG_ERROR("real_output index (" << src_entr.real_output << ")bigger than output_keys.size()=" << src_entr.outputs.size());
|
||||
return false;
|
||||
}
|
||||
summary_inputs_money += src_entr.amount;
|
||||
|
||||
//key_derivation recv_derivation;
|
||||
in_contexts.push_back(input_generation_context_data());
|
||||
keypair& in_ephemeral = in_contexts.back().in_ephemeral;
|
||||
crypto::key_image img;
|
||||
const auto& out_key = reinterpret_cast<const crypto::public_key&>(src_entr.outputs[src_entr.real_output].second.dest);
|
||||
if(!generate_key_image_helper(sender_account_keys, subaddresses, out_key, src_entr.real_out_tx_key, src_entr.real_out_additional_tx_keys, src_entr.real_output_in_tx_index, in_ephemeral,img, hwdev))
|
||||
{
|
||||
LOG_ERROR("Key image generation failed!");
|
||||
return false;
|
||||
}
|
||||
|
||||
//check that derivated key is equal with real output key (if non multisig)
|
||||
if(!msout && !(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].second.dest) )
|
||||
{
|
||||
LOG_ERROR("derived public key mismatch with output public key at index " << idx << ", real out " << src_entr.real_output << "! "<< ENDL << "derived_key:"
|
||||
<< string_tools::pod_to_hex(in_ephemeral.pub) << ENDL << "real output_public_key:"
|
||||
<< string_tools::pod_to_hex(src_entr.outputs[src_entr.real_output].second.dest) );
|
||||
LOG_ERROR("amount " << src_entr.amount << ", rct " << src_entr.rct);
|
||||
LOG_ERROR("tx pubkey " << src_entr.real_out_tx_key << ", real_output_in_tx_index " << src_entr.real_output_in_tx_index);
|
||||
return false;
|
||||
}
|
||||
|
||||
//put key image into tx input
|
||||
txin_to_key input_to_key;
|
||||
input_to_key.amount = src_entr.amount;
|
||||
input_to_key.k_image = msout ? rct::rct2ki(src_entr.multisig_kLRki.ki) : img;
|
||||
|
||||
//fill outputs array and use relative offsets
|
||||
for(const tx_source_entry::output_entry& out_entry: src_entr.outputs)
|
||||
input_to_key.key_offsets.push_back(out_entry.first);
|
||||
|
||||
input_to_key.key_offsets = absolute_output_offsets_to_relative(input_to_key.key_offsets);
|
||||
tx.vin.push_back(input_to_key);
|
||||
}
|
||||
|
||||
if (shuffle_outs)
|
||||
{
|
||||
std::shuffle(destinations.begin(), destinations.end(), std::default_random_engine(crypto::rand<unsigned int>()));
|
||||
}
|
||||
|
||||
// sort ins by their key image
|
||||
std::vector<size_t> ins_order(sources.size());
|
||||
for (size_t n = 0; n < sources.size(); ++n)
|
||||
ins_order[n] = n;
|
||||
std::sort(ins_order.begin(), ins_order.end(), [&](const size_t i0, const size_t i1) {
|
||||
const txin_to_key &tk0 = boost::get<txin_to_key>(tx.vin[i0]);
|
||||
const txin_to_key &tk1 = boost::get<txin_to_key>(tx.vin[i1]);
|
||||
return memcmp(&tk0.k_image, &tk1.k_image, sizeof(tk0.k_image)) > 0;
|
||||
});
|
||||
tools::apply_permutation(ins_order, [&] (size_t i0, size_t i1) {
|
||||
std::swap(tx.vin[i0], tx.vin[i1]);
|
||||
std::swap(in_contexts[i0], in_contexts[i1]);
|
||||
std::swap(sources[i0], sources[i1]);
|
||||
});
|
||||
|
||||
// figure out if we need to make additional tx pubkeys
|
||||
size_t num_stdaddresses = 0;
|
||||
size_t num_subaddresses = 0;
|
||||
account_public_address single_dest_subaddress;
|
||||
classify_addresses(destinations, change_addr, num_stdaddresses, num_subaddresses, single_dest_subaddress);
|
||||
|
||||
// if this is a single-destination transfer to a subaddress, we set the tx pubkey to R=s*D
|
||||
if (num_stdaddresses == 0 && num_subaddresses == 1)
|
||||
{
|
||||
txkey_pub = rct::rct2pk(hwdev.scalarmultKey(rct::pk2rct(single_dest_subaddress.m_spend_public_key), rct::sk2rct(tx_key)));
|
||||
}
|
||||
else
|
||||
{
|
||||
txkey_pub = rct::rct2pk(hwdev.scalarmultBase(rct::sk2rct(tx_key)));
|
||||
}
|
||||
remove_field_from_tx_extra(tx.extra, typeid(tx_extra_pub_key));
|
||||
add_tx_pub_key_to_extra(tx, txkey_pub);
|
||||
|
||||
std::vector<crypto::public_key> additional_tx_public_keys;
|
||||
|
||||
// we don't need to include additional tx keys if:
|
||||
// - all the destinations are standard addresses
|
||||
// - there's only one destination which is a subaddress
|
||||
bool need_additional_txkeys = num_subaddresses > 0 && (num_stdaddresses > 0 || num_subaddresses > 1);
|
||||
if (need_additional_txkeys)
|
||||
CHECK_AND_ASSERT_MES(destinations.size() == additional_tx_keys.size(), false, "Wrong amount of additional tx keys");
|
||||
|
||||
uint64_t summary_outs_money = 0;
|
||||
//fill outputs
|
||||
size_t output_index = 0;
|
||||
for(const tx_destination_entry& dst_entr: destinations)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(dst_entr.amount > 0 || tx.version > 1, false, "Destination with wrong amount: " << dst_entr.amount);
|
||||
crypto::key_derivation derivation;
|
||||
crypto::public_key out_eph_public_key;
|
||||
|
||||
// make additional tx pubkey if necessary
|
||||
keypair additional_txkey;
|
||||
if (need_additional_txkeys)
|
||||
{
|
||||
additional_txkey.sec = additional_tx_keys[output_index];
|
||||
if (dst_entr.is_subaddress)
|
||||
additional_txkey.pub = rct::rct2pk(hwdev.scalarmultKey(rct::pk2rct(dst_entr.addr.m_spend_public_key), rct::sk2rct(additional_txkey.sec)));
|
||||
else
|
||||
additional_txkey.pub = rct::rct2pk(hwdev.scalarmultBase(rct::sk2rct(additional_txkey.sec)));
|
||||
}
|
||||
|
||||
bool r;
|
||||
if (change_addr && dst_entr.addr == *change_addr)
|
||||
{
|
||||
// sending change to yourself; derivation = a*R
|
||||
r = hwdev.generate_key_derivation(txkey_pub, sender_account_keys.m_view_secret_key, derivation);
|
||||
CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << txkey_pub << ", " << sender_account_keys.m_view_secret_key << ")");
|
||||
}
|
||||
else
|
||||
{
|
||||
// sending to the recipient; derivation = r*A (or s*C in the subaddress scheme)
|
||||
r = hwdev.generate_key_derivation(dst_entr.addr.m_view_public_key, dst_entr.is_subaddress && need_additional_txkeys ? additional_txkey.sec : tx_key, derivation);
|
||||
CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << dst_entr.addr.m_view_public_key << ", " << (dst_entr.is_subaddress && need_additional_txkeys ? additional_txkey.sec : tx_key) << ")");
|
||||
}
|
||||
|
||||
if (need_additional_txkeys)
|
||||
{
|
||||
additional_tx_public_keys.push_back(additional_txkey.pub);
|
||||
}
|
||||
|
||||
if (tx.version > 1)
|
||||
{
|
||||
crypto::secret_key scalar1;
|
||||
hwdev.derivation_to_scalar(derivation, output_index, scalar1);
|
||||
amount_keys.push_back(rct::sk2rct(scalar1));
|
||||
}
|
||||
r = hwdev.derive_public_key(derivation, output_index, dst_entr.addr.m_spend_public_key, out_eph_public_key);
|
||||
CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to derive_public_key(" << derivation << ", " << output_index << ", "<< dst_entr.addr.m_spend_public_key << ")");
|
||||
|
||||
hwdev.add_output_key_mapping(dst_entr.addr.m_view_public_key, dst_entr.addr.m_spend_public_key, dst_entr.is_subaddress, output_index, amount_keys.back(), out_eph_public_key);
|
||||
|
||||
tx_out out;
|
||||
out.amount = dst_entr.amount;
|
||||
txout_to_key tk;
|
||||
tk.key = out_eph_public_key;
|
||||
out.target = tk;
|
||||
tx.vout.push_back(out);
|
||||
output_index++;
|
||||
summary_outs_money += dst_entr.amount;
|
||||
}
|
||||
CHECK_AND_ASSERT_MES(additional_tx_public_keys.size() == additional_tx_keys.size(), false, "Internal error creating additional public keys");
|
||||
|
||||
remove_field_from_tx_extra(tx.extra, typeid(tx_extra_additional_pub_keys));
|
||||
|
||||
LOG_PRINT_L2("tx pubkey: " << txkey_pub);
|
||||
if (need_additional_txkeys)
|
||||
{
|
||||
LOG_PRINT_L2("additional tx pubkeys: ");
|
||||
for (size_t i = 0; i < additional_tx_public_keys.size(); ++i)
|
||||
LOG_PRINT_L2(additional_tx_public_keys[i]);
|
||||
add_additional_tx_pub_keys_to_extra(tx.extra, additional_tx_public_keys);
|
||||
}
|
||||
|
||||
//check money
|
||||
if(summary_outs_money > summary_inputs_money )
|
||||
{
|
||||
LOG_ERROR("Transaction inputs money ("<< summary_inputs_money << ") less than outputs money (" << summary_outs_money << ")");
|
||||
return false;
|
||||
}
|
||||
|
||||
// check for watch only wallet
|
||||
bool zero_secret_key = true;
|
||||
for (size_t i = 0; i < sizeof(sender_account_keys.m_spend_secret_key); ++i)
|
||||
zero_secret_key &= (sender_account_keys.m_spend_secret_key.data[i] == 0);
|
||||
if (zero_secret_key)
|
||||
{
|
||||
MDEBUG("Null secret key, skipping signatures");
|
||||
}
|
||||
|
||||
if (tx.version == 1)
|
||||
{
|
||||
//generate ring signatures
|
||||
crypto::hash tx_prefix_hash;
|
||||
get_transaction_prefix_hash(tx, tx_prefix_hash);
|
||||
|
||||
std::stringstream ss_ring_s;
|
||||
size_t i = 0;
|
||||
for(const tx_source_entry& src_entr: sources)
|
||||
{
|
||||
ss_ring_s << "pub_keys:" << ENDL;
|
||||
std::vector<const crypto::public_key*> keys_ptrs;
|
||||
std::vector<crypto::public_key> keys(src_entr.outputs.size());
|
||||
size_t ii = 0;
|
||||
for(const tx_source_entry::output_entry& o: src_entr.outputs)
|
||||
{
|
||||
keys[ii] = rct2pk(o.second.dest);
|
||||
keys_ptrs.push_back(&keys[ii]);
|
||||
ss_ring_s << o.second.dest << ENDL;
|
||||
++ii;
|
||||
}
|
||||
|
||||
tx.signatures.push_back(std::vector<crypto::signature>());
|
||||
std::vector<crypto::signature>& sigs = tx.signatures.back();
|
||||
sigs.resize(src_entr.outputs.size());
|
||||
if (!zero_secret_key)
|
||||
crypto::generate_ring_signature(tx_prefix_hash, boost::get<txin_to_key>(tx.vin[i]).k_image, keys_ptrs, in_contexts[i].in_ephemeral.sec, src_entr.real_output, sigs.data());
|
||||
ss_ring_s << "signatures:" << ENDL;
|
||||
std::for_each(sigs.begin(), sigs.end(), [&](const crypto::signature& s){ss_ring_s << s << ENDL;});
|
||||
ss_ring_s << "prefix_hash:" << tx_prefix_hash << ENDL << "in_ephemeral_key: " << in_contexts[i].in_ephemeral.sec << ENDL << "real_output: " << src_entr.real_output << ENDL;
|
||||
i++;
|
||||
}
|
||||
|
||||
MCINFO("construct_tx", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL << ss_ring_s.str());
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t n_total_outs = sources[0].outputs.size(); // only for non-simple rct
|
||||
|
||||
// the non-simple version is slightly smaller, but assumes all real inputs
|
||||
// are on the same index, so can only be used if there just one ring.
|
||||
bool use_simple_rct = sources.size() > 1;
|
||||
|
||||
if (!use_simple_rct)
|
||||
{
|
||||
// non simple ringct requires all real inputs to be at the same index for all inputs
|
||||
for(const tx_source_entry& src_entr: sources)
|
||||
{
|
||||
if(src_entr.real_output != sources.begin()->real_output)
|
||||
{
|
||||
LOG_ERROR("All inputs must have the same index for non-simple ringct");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// enforce same mixin for all outputs
|
||||
for (size_t i = 1; i < sources.size(); ++i) {
|
||||
if (n_total_outs != sources[i].outputs.size()) {
|
||||
LOG_ERROR("Non-simple ringct transaction has varying ring size");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t amount_in = 0, amount_out = 0;
|
||||
rct::ctkeyV inSk;
|
||||
// mixRing indexing is done the other way round for simple
|
||||
rct::ctkeyM mixRing(use_simple_rct ? sources.size() : n_total_outs);
|
||||
rct::keyV destinations;
|
||||
std::vector<uint64_t> inamounts, outamounts;
|
||||
std::vector<unsigned int> index;
|
||||
std::vector<rct::multisig_kLRki> kLRki;
|
||||
for (size_t i = 0; i < sources.size(); ++i)
|
||||
{
|
||||
rct::ctkey ctkey;
|
||||
amount_in += sources[i].amount;
|
||||
inamounts.push_back(sources[i].amount);
|
||||
index.push_back(sources[i].real_output);
|
||||
// inSk: (secret key, mask)
|
||||
ctkey.dest = rct::sk2rct(in_contexts[i].in_ephemeral.sec);
|
||||
ctkey.mask = sources[i].mask;
|
||||
inSk.push_back(ctkey);
|
||||
// inPk: (public key, commitment)
|
||||
// will be done when filling in mixRing
|
||||
if (msout)
|
||||
{
|
||||
kLRki.push_back(sources[i].multisig_kLRki);
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < tx.vout.size(); ++i)
|
||||
{
|
||||
destinations.push_back(rct::pk2rct(boost::get<txout_to_key>(tx.vout[i].target).key));
|
||||
outamounts.push_back(tx.vout[i].amount);
|
||||
amount_out += tx.vout[i].amount;
|
||||
}
|
||||
|
||||
if (use_simple_rct)
|
||||
{
|
||||
// mixRing indexing is done the other way round for simple
|
||||
for (size_t i = 0; i < sources.size(); ++i)
|
||||
{
|
||||
mixRing[i].resize(sources[i].outputs.size());
|
||||
for (size_t n = 0; n < sources[i].outputs.size(); ++n)
|
||||
{
|
||||
mixRing[i][n] = sources[i].outputs[n].second;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < n_total_outs; ++i) // same index assumption
|
||||
{
|
||||
mixRing[i].resize(sources.size());
|
||||
for (size_t n = 0; n < sources.size(); ++n)
|
||||
{
|
||||
mixRing[i][n] = sources[n].outputs[i].second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fee
|
||||
if (!use_simple_rct && amount_in > amount_out)
|
||||
outamounts.push_back(amount_in - amount_out);
|
||||
|
||||
// zero out all amounts to mask rct outputs, real amounts are now encrypted
|
||||
for (size_t i = 0; i < tx.vin.size(); ++i)
|
||||
{
|
||||
if (sources[i].rct)
|
||||
boost::get<txin_to_key>(tx.vin[i]).amount = 0;
|
||||
}
|
||||
for (size_t i = 0; i < tx.vout.size(); ++i)
|
||||
tx.vout[i].amount = 0;
|
||||
|
||||
crypto::hash tx_prefix_hash;
|
||||
get_transaction_prefix_hash(tx, tx_prefix_hash);
|
||||
rct::ctkeyV outSk;
|
||||
if (use_simple_rct)
|
||||
tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, msout ? &kLRki : NULL, msout, index, outSk, bulletproof, hwdev);
|
||||
else
|
||||
tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, amount_keys, msout ? &kLRki[0] : NULL, msout, sources[0].real_output, outSk, bulletproof, hwdev); // same index assumption
|
||||
|
||||
CHECK_AND_ASSERT_MES(tx.vout.size() == outSk.size(), false, "outSk size does not match vout");
|
||||
|
||||
MCINFO("construct_tx", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL);
|
||||
}
|
||||
|
||||
tx.invalidate_hashes();
|
||||
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct, bool bulletproof, rct::multisig_out *msout)
|
||||
{
|
||||
hw::device &hwdev = sender_account_keys.get_device();
|
||||
hwdev.open_tx(tx_key);
|
||||
|
||||
// figure out if we need to make additional tx pubkeys
|
||||
size_t num_stdaddresses = 0;
|
||||
size_t num_subaddresses = 0;
|
||||
account_public_address single_dest_subaddress;
|
||||
classify_addresses(destinations, change_addr, num_stdaddresses, num_subaddresses, single_dest_subaddress);
|
||||
bool need_additional_txkeys = num_subaddresses > 0 && (num_stdaddresses > 0 || num_subaddresses > 1);
|
||||
if (need_additional_txkeys)
|
||||
{
|
||||
additional_tx_keys.clear();
|
||||
for (const auto &d: destinations)
|
||||
additional_tx_keys.push_back(keypair::generate(sender_account_keys.get_device()).sec);
|
||||
}
|
||||
|
||||
bool r = construct_tx_with_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct, bulletproof, msout);
|
||||
hwdev.close_tx();
|
||||
return r;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time)
|
||||
{
|
||||
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
|
||||
subaddresses[sender_account_keys.m_account_address.m_spend_public_key] = {0,0};
|
||||
crypto::secret_key tx_key;
|
||||
std::vector<crypto::secret_key> additional_tx_keys;
|
||||
std::vector<tx_destination_entry> destinations_copy = destinations;
|
||||
return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations_copy, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, false, NULL);
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
// bool generate_genesis_block(
|
||||
// block& bl
|
||||
// , std::string const & genesis_tx
|
||||
// , uint32_t nonce
|
||||
// )
|
||||
// {
|
||||
// //genesis block
|
||||
// bl = boost::value_initialized<block>();
|
||||
//
|
||||
// blobdata tx_bl;
|
||||
// bool r = string_tools::parse_hexstr_to_binbuff(genesis_tx, tx_bl);
|
||||
// CHECK_AND_ASSERT_MES(r, false, "failed to parse coinbase tx from hard coded blob");
|
||||
// r = parse_and_validate_tx_from_blob(tx_bl, bl.miner_tx);
|
||||
// CHECK_AND_ASSERT_MES(r, false, "failed to parse coinbase tx from hard coded blob");
|
||||
// bl.major_version = CURRENT_BLOCK_MAJOR_VERSION;
|
||||
// bl.minor_version = CURRENT_BLOCK_MINOR_VERSION;
|
||||
// bl.timestamp = 0;
|
||||
// bl.nonce = nonce;
|
||||
// miner::find_nonce_for_given_block(bl, 1, 0);
|
||||
// bl.invalidate_hashes();
|
||||
// return true;
|
||||
// }
|
||||
//---------------------------------------------------------------
|
||||
}
|
@ -0,0 +1,137 @@
|
||||
// Copyright (c) 2014-2018, 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.
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
#pragma once
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||
#include <boost/serialization/vector.hpp>
|
||||
#include <boost/serialization/utility.hpp>
|
||||
#include "ringct/rctOps.h"
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
//---------------------------------------------------------------
|
||||
// bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce = blobdata(), size_t max_outs = 999, uint8_t hard_fork_version = 1);
|
||||
|
||||
struct tx_source_entry
|
||||
{
|
||||
typedef std::pair<uint64_t, rct::ctkey> output_entry;
|
||||
|
||||
std::vector<output_entry> outputs; //index + key + optional ringct commitment
|
||||
size_t real_output; //index in outputs vector of real output_entry
|
||||
crypto::public_key real_out_tx_key; //incoming real tx public key
|
||||
std::vector<crypto::public_key> real_out_additional_tx_keys; //incoming real tx additional public keys
|
||||
size_t real_output_in_tx_index; //index in transaction outputs vector
|
||||
uint64_t amount; //money
|
||||
bool rct; //true if the output is rct
|
||||
rct::key mask; //ringct amount mask
|
||||
rct::multisig_kLRki multisig_kLRki; //multisig info
|
||||
|
||||
void push_output(uint64_t idx, const crypto::public_key &k, uint64_t amount) { outputs.push_back(std::make_pair(idx, rct::ctkey({rct::pk2rct(k), rct::zeroCommit(amount)}))); }
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELD(outputs)
|
||||
FIELD(real_output)
|
||||
FIELD(real_out_tx_key)
|
||||
FIELD(real_out_additional_tx_keys)
|
||||
FIELD(real_output_in_tx_index)
|
||||
FIELD(amount)
|
||||
FIELD(rct)
|
||||
FIELD(mask)
|
||||
FIELD(multisig_kLRki)
|
||||
|
||||
if (real_output >= outputs.size())
|
||||
return false;
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
struct tx_destination_entry
|
||||
{
|
||||
uint64_t amount; //money
|
||||
account_public_address addr; //destination address
|
||||
bool is_subaddress;
|
||||
|
||||
tx_destination_entry() : amount(0), addr(AUTO_VAL_INIT(addr)), is_subaddress(false) { }
|
||||
tx_destination_entry(uint64_t a, const account_public_address &ad, bool is_subaddress) : amount(a), addr(ad), is_subaddress(is_subaddress) { }
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
VARINT_FIELD(amount)
|
||||
FIELD(addr)
|
||||
FIELD(is_subaddress)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------
|
||||
crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::account_public_address>& change_addr);
|
||||
bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry> &sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time);
|
||||
bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, bool bulletproof = false, rct::multisig_out *msout = NULL, bool shuffle_outs = true);
|
||||
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, bool bulletproof = false, rct::multisig_out *msout = NULL);
|
||||
|
||||
// bool generate_genesis_block(
|
||||
// block& bl
|
||||
// , std::string const & genesis_tx
|
||||
// , uint32_t nonce
|
||||
// );
|
||||
|
||||
}
|
||||
|
||||
BOOST_CLASS_VERSION(cryptonote::tx_source_entry, 1)
|
||||
BOOST_CLASS_VERSION(cryptonote::tx_destination_entry, 1)
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace serialization
|
||||
{
|
||||
template <class Archive>
|
||||
inline void serialize(Archive &a, cryptonote::tx_source_entry &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
a & x.outputs;
|
||||
a & x.real_output;
|
||||
a & x.real_out_tx_key;
|
||||
a & x.real_output_in_tx_index;
|
||||
a & x.amount;
|
||||
a & x.rct;
|
||||
a & x.mask;
|
||||
if (ver < 1)
|
||||
return;
|
||||
a & x.multisig_kLRki;
|
||||
a & x.real_out_additional_tx_keys;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
inline void serialize(Archive& a, cryptonote::tx_destination_entry& x, const boost::serialization::version_type ver)
|
||||
{
|
||||
a & x.amount;
|
||||
a & x.addr;
|
||||
if (ver < 1)
|
||||
return;
|
||||
a & x.is_subaddress;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,141 @@
|
||||
// Copyright (c) 2017-2018, 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 <unordered_set>
|
||||
#include "include_base_utils.h"
|
||||
#include "crypto/crypto.h"
|
||||
#include "ringct/rctOps.h"
|
||||
#include "cryptonote_basic/account.h"
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||
#include "multisig.h"
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "multisig"
|
||||
|
||||
using namespace std;
|
||||
|
||||
static const rct::key multisig_salt = { {'M', 'u', 'l', 't' , 'i', 's', 'i', 'g', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
//-----------------------------------------------------------------
|
||||
crypto::secret_key get_multisig_blinded_secret_key(const crypto::secret_key &key)
|
||||
{
|
||||
rct::keyV data;
|
||||
data.push_back(rct::sk2rct(key));
|
||||
data.push_back(multisig_salt);
|
||||
return rct::rct2sk(rct::hash_to_scalar(data));
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
void generate_multisig_N_N(const account_keys &keys, const std::vector<crypto::public_key> &spend_keys, std::vector<crypto::secret_key> &multisig_keys, rct::key &spend_skey, rct::key &spend_pkey)
|
||||
{
|
||||
// the multisig spend public key is the sum of all spend public keys
|
||||
multisig_keys.clear();
|
||||
const crypto::secret_key spend_secret_key = get_multisig_blinded_secret_key(keys.m_spend_secret_key);
|
||||
CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(spend_secret_key, (crypto::public_key&)spend_pkey), "Failed to derive public key");
|
||||
for (const auto &k: spend_keys)
|
||||
rct::addKeys(spend_pkey, spend_pkey, rct::pk2rct(k));
|
||||
multisig_keys.push_back(spend_secret_key);
|
||||
spend_skey = rct::sk2rct(spend_secret_key);
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
void generate_multisig_N1_N(const account_keys &keys, const std::vector<crypto::public_key> &spend_keys, std::vector<crypto::secret_key> &multisig_keys, rct::key &spend_skey, rct::key &spend_pkey)
|
||||
{
|
||||
multisig_keys.clear();
|
||||
spend_pkey = rct::identity();
|
||||
spend_skey = rct::zero();
|
||||
|
||||
// create all our composite private keys
|
||||
crypto::secret_key blinded_skey = get_multisig_blinded_secret_key(keys.m_spend_secret_key);
|
||||
for (const auto &k: spend_keys)
|
||||
{
|
||||
rct::key sk = rct::scalarmultKey(rct::pk2rct(k), rct::sk2rct(blinded_skey));
|
||||
crypto::secret_key msk = get_multisig_blinded_secret_key(rct::rct2sk(sk));
|
||||
multisig_keys.push_back(msk);
|
||||
sc_add(spend_skey.bytes, spend_skey.bytes, (const unsigned char*)msk.data);
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
crypto::secret_key generate_multisig_view_secret_key(const crypto::secret_key &skey, const std::vector<crypto::secret_key> &skeys)
|
||||
{
|
||||
rct::key view_skey = rct::sk2rct(get_multisig_blinded_secret_key(skey));
|
||||
for (const auto &k: skeys)
|
||||
sc_add(view_skey.bytes, view_skey.bytes, rct::sk2rct(k).bytes);
|
||||
return rct::rct2sk(view_skey);
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
crypto::public_key generate_multisig_N1_N_spend_public_key(const std::vector<crypto::public_key> &pkeys)
|
||||
{
|
||||
rct::key spend_public_key = rct::identity();
|
||||
for (const auto &pk: pkeys)
|
||||
{
|
||||
rct::addKeys(spend_public_key, spend_public_key, rct::pk2rct(pk));
|
||||
}
|
||||
return rct::rct2pk(spend_public_key);
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
bool generate_multisig_key_image(const account_keys &keys, size_t multisig_key_index, const crypto::public_key& out_key, crypto::key_image& ki)
|
||||
{
|
||||
if (multisig_key_index >= keys.m_multisig_keys.size())
|
||||
return false;
|
||||
crypto::generate_key_image(out_key, keys.m_multisig_keys[multisig_key_index], ki);
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
void generate_multisig_LR(const crypto::public_key pkey, const crypto::secret_key &k, crypto::public_key &L, crypto::public_key &R)
|
||||
{
|
||||
rct::scalarmultBase((rct::key&)L, rct::sk2rct(k));
|
||||
crypto::generate_key_image(pkey, k, (crypto::key_image&)R);
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
bool generate_multisig_composite_key_image(const account_keys &keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, const crypto::public_key& out_key, const crypto::public_key &tx_public_key, const std::vector<crypto::public_key>& additional_tx_public_keys, size_t real_output_index, const std::vector<crypto::key_image> &pkis, crypto::key_image &ki)
|
||||
{
|
||||
cryptonote::keypair in_ephemeral;
|
||||
if (!cryptonote::generate_key_image_helper(keys, subaddresses, out_key, tx_public_key, additional_tx_public_keys, real_output_index, in_ephemeral, ki, keys.get_device()))
|
||||
return false;
|
||||
std::unordered_set<crypto::key_image> used;
|
||||
for (size_t m = 0; m < keys.m_multisig_keys.size(); ++m)
|
||||
{
|
||||
crypto::key_image pki;
|
||||
bool r = cryptonote::generate_multisig_key_image(keys, m, out_key, pki);
|
||||
if (!r)
|
||||
return false;
|
||||
used.insert(pki);
|
||||
}
|
||||
for (const auto &pki: pkis)
|
||||
{
|
||||
if (used.find(pki) == used.end())
|
||||
{
|
||||
used.insert(pki);
|
||||
rct::addKeys((rct::key&)ki, rct::ki2rct(ki), rct::ki2rct(pki));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
// Copyright (c) 2017-2018, 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 <vector>
|
||||
#include <unordered_map>
|
||||
#include "crypto/crypto.h"
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||
#include "ringct/rctTypes.h"
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
struct account_keys;
|
||||
|
||||
crypto::secret_key get_multisig_blinded_secret_key(const crypto::secret_key &key);
|
||||
void generate_multisig_N_N(const account_keys &keys, const std::vector<crypto::public_key> &spend_keys, std::vector<crypto::secret_key> &multisig_keys, rct::key &spend_skey, rct::key &spend_pkey);
|
||||
void generate_multisig_N1_N(const account_keys &keys, const std::vector<crypto::public_key> &spend_keys, std::vector<crypto::secret_key> &multisig_keys, rct::key &spend_skey, rct::key &spend_pkey);
|
||||
crypto::secret_key generate_multisig_view_secret_key(const crypto::secret_key &skey, const std::vector<crypto::secret_key> &skeys);
|
||||
crypto::public_key generate_multisig_N1_N_spend_public_key(const std::vector<crypto::public_key> &pkeys);
|
||||
bool generate_multisig_key_image(const account_keys &keys, size_t multisig_key_index, const crypto::public_key& out_key, crypto::key_image& ki);
|
||||
void generate_multisig_LR(const crypto::public_key pkey, const crypto::secret_key &k, crypto::public_key &L, crypto::public_key &R);
|
||||
bool generate_multisig_composite_key_image(const account_keys &keys, const std::unordered_map<crypto::public_key, cryptonote::subaddress_index>& subaddresses, const crypto::public_key& out_key, const crypto::public_key &tx_public_key, const std::vector<crypto::public_key>& additional_tx_public_keys, size_t real_output_index, const std::vector<crypto::key_image> &pkis, crypto::key_image &ki);
|
||||
}
|
Loading…
Reference in new issue