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

pull/2/head
Paul Shapiro 6 years ago
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 {};
}
}
}

@ -41,7 +41,7 @@ using namespace epee;
#include "common/base58.h"
#include "crypto/hash.h"
#include "common/int-util.h"
//#include "common/dns_utils.h"
// #include "common/dns_utils.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "cn"
@ -296,7 +296,7 @@ namespace cryptonote {
return true;
}
//--------------------------------------------------------------------------------
// //--------------------------------------------------------------------------------
// bool get_account_address_from_str_or_url(
// address_parse_info& info
// , network_type nettype

@ -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);
}

@ -32,7 +32,7 @@
#include <openssl/ssl.h>
#include <boost/thread/mutex.hpp>
#include "misc_log_ex.h"
#include "common/perf_timer.h"
// #include "common/perf_timer.h"
extern "C"
{
#include "crypto/crypto-ops.h"
@ -45,7 +45,7 @@ extern "C"
//#define DEBUG_BP
#define PERF_TIMER_START_BP(x) PERF_TIMER_START_UNIT(x, 1000000)
// #define PERF_TIMER_START_BP(x) PERF_TIMER_START_UNIT(x, 1000000)
namespace rct
{
@ -335,7 +335,7 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma)
{
init_exponents();
PERF_TIMER_UNIT(PROVE, 1000000);
// PERF_TIMER_UNIT(PROVE, 1000000);
constexpr size_t logN = 6; // log2(64)
constexpr size_t N = 1<<logN;
@ -343,11 +343,11 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma)
rct::key V;
rct::keyV aL(N), aR(N);
PERF_TIMER_START_BP(PROVE_v);
// PERF_TIMER_START_BP(PROVE_v);
rct::addKeys2(V, gamma, sv, rct::H);
PERF_TIMER_STOP(PROVE_v);
// PERF_TIMER_STOP(PROVE_v);
PERF_TIMER_START_BP(PROVE_aLaR);
// PERF_TIMER_START_BP(PROVE_aLaR);
for (size_t i = N; i-- > 0; )
{
if (sv[i/8] & (((uint64_t)1)<<(i%8)))
@ -360,7 +360,7 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma)
}
sc_sub(aR[i].bytes, aL[i].bytes, rct::identity().bytes);
}
PERF_TIMER_STOP(PROVE_aLaR);
// PERF_TIMER_STOP(PROVE_aLaR);
rct::key hash_cache = rct::hash_to_scalar(V);
@ -380,7 +380,7 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma)
CHECK_AND_ASSERT_THROW_MES(test_aR == v_test, "test_aR failed");
#endif
PERF_TIMER_START_BP(PROVE_step1);
// PERF_TIMER_START_BP(PROVE_step1);
// PAPER LINES 38-39
rct::key alpha = rct::skGen();
rct::key ve = vector_exponent(aL, aR);
@ -433,9 +433,9 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma)
sc_add(test_t0.bytes, test_t0.bytes, k.bytes);
CHECK_AND_ASSERT_THROW_MES(t0 == test_t0, "t0 check failed");
#endif
PERF_TIMER_STOP(PROVE_step1);
// PERF_TIMER_STOP(PROVE_step1);
PERF_TIMER_START_BP(PROVE_step2);
// PERF_TIMER_START_BP(PROVE_step2);
const auto HyNsR = hadamard(yN, sR);
const auto vpIz = vector_scalar(oneN, z);
const auto vp2zsq = vector_scalar(twoN, zsq);
@ -473,9 +473,9 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma)
// PAPER LINES 54-57
rct::keyV l = vector_add(aL_vpIz, vector_scalar(sL, x));
rct::keyV r = vector_add(hadamard(yN, vector_add(aR_vpIz, vector_scalar(sR, x))), vp2zsq);
PERF_TIMER_STOP(PROVE_step2);
// PERF_TIMER_STOP(PROVE_step2);
PERF_TIMER_START_BP(PROVE_step3);
// PERF_TIMER_START_BP(PROVE_step3);
rct::key t = inner_product(l, r);
// DEBUG: Test if the l and r vectors match the polynomial forms
@ -509,9 +509,9 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma)
rct::keyV R(logN);
int round = 0;
rct::keyV w(logN); // this is the challenge x in the inner product protocol
PERF_TIMER_STOP(PROVE_step3);
// PERF_TIMER_STOP(PROVE_step3);
PERF_TIMER_START_BP(PROVE_step4);
// PERF_TIMER_START_BP(PROVE_step4);
// PAPER LINE 13
while (nprime > 1)
{
@ -544,7 +544,7 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma)
++round;
}
PERF_TIMER_STOP(PROVE_step4);
// PERF_TIMER_STOP(PROVE_step4);
// PAPER LINE 58 (with inclusions from PAPER LINE 8 and PAPER LINE 20)
return Bulletproof(V, A, S, T1, T2, taux, mu, L, R, aprime[0], bprime[0], t);
@ -553,7 +553,7 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma)
Bulletproof bulletproof_PROVE(uint64_t v, const rct::key &gamma)
{
// vG + gammaH
PERF_TIMER_START_BP(PROVE_v);
// PERF_TIMER_START_BP(PROVE_v);
rct::key sv = rct::zero();
sv.bytes[0] = v & 255;
sv.bytes[1] = (v >> 8) & 255;
@ -563,7 +563,7 @@ Bulletproof bulletproof_PROVE(uint64_t v, const rct::key &gamma)
sv.bytes[5] = (v >> 40) & 255;
sv.bytes[6] = (v >> 48) & 255;
sv.bytes[7] = (v >> 56) & 255;
PERF_TIMER_STOP(PROVE_v);
// PERF_TIMER_STOP(PROVE_v);
return bulletproof_PROVE(sv, gamma);
}
@ -581,20 +581,20 @@ bool bulletproof_VERIFY(const Bulletproof &proof)
const size_t N = 1 << logN;
// Reconstruct the challenges
PERF_TIMER_START_BP(VERIFY);
PERF_TIMER_START_BP(VERIFY_start);
// PERF_TIMER_START_BP(VERIFY);
// PERF_TIMER_START_BP(VERIFY_start);
rct::key hash_cache = rct::hash_to_scalar(proof.V[0]);
rct::key y = hash_cache_mash(hash_cache, proof.A, proof.S);
rct::key z = hash_cache = rct::hash_to_scalar(y);
rct::key x = hash_cache_mash(hash_cache, z, proof.T1, proof.T2);
PERF_TIMER_STOP(VERIFY_start);
// PERF_TIMER_STOP(VERIFY_start);
PERF_TIMER_START_BP(VERIFY_line_60);
// PERF_TIMER_START_BP(VERIFY_line_60);
// Reconstruct the challenges
rct::key x_ip = hash_cache_mash(hash_cache, x, proof.taux, proof.mu, proof.t);
PERF_TIMER_STOP(VERIFY_line_60);
// PERF_TIMER_STOP(VERIFY_line_60);
PERF_TIMER_START_BP(VERIFY_line_61);
// PERF_TIMER_START_BP(VERIFY_line_61);
// PAPER LINE 61
rct::key L61Left = rct::addKeys(rct::scalarmultBase(proof.taux), rct::scalarmultKey(rct::H, proof.t));
@ -608,9 +608,9 @@ bool bulletproof_VERIFY(const Bulletproof &proof)
rct::key zcu;
sc_mul(zcu.bytes, zsq.bytes, z.bytes);
sc_mulsub(k.bytes, zcu.bytes, ip12.bytes, k.bytes);
PERF_TIMER_STOP(VERIFY_line_61);
// PERF_TIMER_STOP(VERIFY_line_61);
PERF_TIMER_START_BP(VERIFY_line_61rl);
// PERF_TIMER_START_BP(VERIFY_line_61rl);
sc_muladd(tmp.bytes, z.bytes, ip1y.bytes, k.bytes);
rct::key L61Right = rct::scalarmultKey(rct::H, tmp);
@ -625,7 +625,7 @@ bool bulletproof_VERIFY(const Bulletproof &proof)
sc_mul(xsq.bytes, x.bytes, x.bytes);
tmp = rct::scalarmultKey(proof.T2, xsq);
rct::addKeys(L61Right, L61Right, tmp);
PERF_TIMER_STOP(VERIFY_line_61rl);
// PERF_TIMER_STOP(VERIFY_line_61rl);
if (!(L61Right == L61Left))
{
@ -633,16 +633,16 @@ bool bulletproof_VERIFY(const Bulletproof &proof)
return false;
}
PERF_TIMER_START_BP(VERIFY_line_62);
// PERF_TIMER_START_BP(VERIFY_line_62);
// PAPER LINE 62
rct::key P = rct::addKeys(proof.A, rct::scalarmultKey(proof.S, x));
PERF_TIMER_STOP(VERIFY_line_62);
// PERF_TIMER_STOP(VERIFY_line_62);
// Compute the number of rounds for the inner product
const size_t rounds = proof.L.size();
CHECK_AND_ASSERT_MES(rounds > 0, false, "Zero rounds");
PERF_TIMER_START_BP(VERIFY_line_21_22);
// PERF_TIMER_START_BP(VERIFY_line_21_22);
// PAPER LINES 21-22
// The inner product challenges are computed per round
rct::keyV w(rounds);
@ -650,21 +650,21 @@ bool bulletproof_VERIFY(const Bulletproof &proof)
{
w[i] = hash_cache_mash(hash_cache, proof.L[i], proof.R[i]);
}
PERF_TIMER_STOP(VERIFY_line_21_22);
// PERF_TIMER_STOP(VERIFY_line_21_22);
PERF_TIMER_START_BP(VERIFY_line_24_25);
// PERF_TIMER_START_BP(VERIFY_line_24_25);
// Basically PAPER LINES 24-25
// Compute the curvepoints from G[i] and H[i]
rct::key inner_prod = rct::identity();
rct::key yinvpow = rct::identity();
rct::key ypow = rct::identity();
PERF_TIMER_START_BP(VERIFY_line_24_25_invert);
// PERF_TIMER_START_BP(VERIFY_line_24_25_invert);
const rct::key yinv = invert(y);
rct::keyV winv(rounds);
for (size_t i = 0; i < rounds; ++i)
winv[i] = invert(w[i]);
PERF_TIMER_STOP(VERIFY_line_24_25_invert);
// PERF_TIMER_STOP(VERIFY_line_24_25_invert);
for (size_t i = 0; i < N; ++i)
{
@ -706,9 +706,9 @@ bool bulletproof_VERIFY(const Bulletproof &proof)
sc_mul(ypow.bytes, ypow.bytes, y.bytes);
}
}
PERF_TIMER_STOP(VERIFY_line_24_25);
// PERF_TIMER_STOP(VERIFY_line_24_25);
PERF_TIMER_START_BP(VERIFY_line_26);
// PERF_TIMER_START_BP(VERIFY_line_26);
// PAPER LINE 26
rct::key pprime;
sc_sub(tmp.bytes, rct::zero().bytes, proof.mu.bytes);
@ -731,21 +731,21 @@ bool bulletproof_VERIFY(const Bulletproof &proof)
}
sc_mul(tmp.bytes, proof.t.bytes, x_ip.bytes);
rct::addKeys(pprime, pprime, rct::scalarmultKey(rct::H, tmp));
PERF_TIMER_STOP(VERIFY_line_26);
// PERF_TIMER_STOP(VERIFY_line_26);
PERF_TIMER_START_BP(VERIFY_step2_check);
// PERF_TIMER_START_BP(VERIFY_step2_check);
sc_mul(tmp.bytes, proof.a.bytes, proof.b.bytes);
sc_mul(tmp.bytes, tmp.bytes, x_ip.bytes);
tmp = rct::scalarmultKey(rct::H, tmp);
rct::addKeys(tmp, tmp, inner_prod);
PERF_TIMER_STOP(VERIFY_step2_check);
// PERF_TIMER_STOP(VERIFY_step2_check);
if (!(pprime == tmp))
{
MERROR("Verification failure at step 2");
return false;
}
PERF_TIMER_STOP(VERIFY);
// PERF_TIMER_STOP(VERIFY);
return true;
}

@ -29,7 +29,7 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "misc_log_ex.h"
#include "common/perf_timer.h"
// #include "common/perf_timer.h"
#include "common/threadpool.h"
#include "common/util.h"
#include "rctSigs.h"
@ -347,7 +347,7 @@ namespace rct {
bool verRange(const key & C, const rangeSig & as) {
try
{
PERF_TIMER(verRange);
// PERF_TIMER(verRange);
ge_p3 CiH[64], asCi[64];
int i = 0;
ge_p3 Ctmp_p3 = ge_p3_identity;
@ -534,7 +534,7 @@ namespace rct {
//Ver:
// verifies the above sig is created corretly
bool verRctMG(const mgSig &mg, const ctkeyM & pubs, const ctkeyV & outPk, key txnFeeKey, const key &message) {
PERF_TIMER(verRctMG);
// PERF_TIMER(verRctMG);
//setup vars
size_t cols = pubs.size();
CHECK_AND_ASSERT_MES(cols >= 1, false, "Empty pubs");
@ -575,7 +575,7 @@ namespace rct {
bool verRctMGSimple(const key &message, const mgSig &mg, const ctkeyV & pubs, const key & C) {
try
{
PERF_TIMER(verRctMGSimple);
// PERF_TIMER(verRctMGSimple);
//setup vars
size_t rows = 1;
size_t cols = pubs.size();
@ -832,7 +832,7 @@ namespace rct {
// uses the attached ecdh info to find the amounts represented by each output commitment
// must know the destination private key to find the correct amount, else will return a random number
bool verRct(const rctSig & rv, bool semantics) {
PERF_TIMER(verRct);
// PERF_TIMER(verRct);
CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull || rv.type == RCTTypeFullBulletproof, false, "verRct called on non-full rctSig");
if (semantics)
{
@ -905,7 +905,7 @@ namespace rct {
bool verRctSimple(const rctSig & rv, bool semantics) {
try
{
PERF_TIMER(verRctSimple);
// PERF_TIMER(verRctSimple);
CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeSimpleBulletproof, false, "verRctSimple called on non simple rctSig");
if (semantics)

Loading…
Cancel
Save