Merge commit '7addabc' into LokiMergeUpstreamUntil_20180911_e6d36c1

This commit is contained in:
doy-lee 2018-10-10 10:30:31 +11:00
commit 2d3adf003b
45 changed files with 3305 additions and 1437 deletions

View File

@ -503,6 +503,17 @@ else()
set(ARCH_FLAG "-march=armv8")
else()
set(ARCH_FLAG "-march=${ARCH}")
if(ARCH STREQUAL "native")
check_c_compiler_flag(-march=native CC_SUPPORTS_MARCH_NATIVE)
if (NOT CC_SUPPORTS_MARCH_NATIVE)
check_c_compiler_flag(-mtune=native CC_SUPPORTS_MTUNE_NATIVE)
if (CC_SUPPORTS_MTUNE_NATIVE)
set(ARCH_FLAG "-mtune=${ARCH}")
else()
set(ARCH_FLAG "")
endif()
endif()
endif()
endif()
set(WARNINGS "-Wall -Wextra -Wpointer-arith -Wundef -Wvla -Wwrite-strings -Wno-error=extra -Wno-error=deprecated-declarations -Wno-unused-parameter -Wno-unused-variable -Wno-error=unused-variable -Wno-error=undef -Wno-error=uninitialized")
if(NOT MINGW)
@ -575,6 +586,17 @@ else()
add_cxx_flag_if_supported(-fstack-protector-strong CXX_SECURITY_FLAGS)
endif()
# New in GCC 8.2
if (NOT WIN32)
add_c_flag_if_supported(-fcf-protection=full C_SECURITY_FLAGS)
add_cxx_flag_if_supported(-fcf-protection=full CXX_SECURITY_FLAGS)
add_c_flag_if_supported(-fstack-clash-protection C_SECURITY_FLAGS)
add_cxx_flag_if_supported(-fstack-clash-protection CXX_SECURITY_FLAGS)
endif()
add_c_flag_if_supported(-mmitigate-rop C_SECURITY_FLAGS)
add_cxx_flag_if_supported(-mmitigate-rop CXX_SECURITY_FLAGS)
# linker
if (NOT WIN32)
# Windows binaries die on startup with PIE

117
Makefile
View File

@ -28,112 +28,129 @@
ANDROID_STANDALONE_TOOLCHAIN_PATH ?= /usr/local/toolchain
subbuilddir:=$(shell echo `uname | sed -e 's|[:/\\ \(\)]|_|g'`/`git branch | grep '\* ' | cut -f2- -d' '| sed -e 's|[:/\\ \(\)]|_|g'`)
ifeq ($(USE_SINGLE_BUILDDIR),)
builddir := build/"$(subbuilddir)"
topdir := ../../../..
deldirs := $(builddir)
else
builddir := build
topdir := ../..
deldirs := $(builddir)/debug $(builddir)/release $(builddir)/fuzz
endif
all: release-all
cmake-debug:
mkdir -p build/debug
cd build/debug && cmake -D CMAKE_BUILD_TYPE=Debug ../..
mkdir -p $(builddir)/debug
cd $(builddir)/debug && cmake -D CMAKE_BUILD_TYPE=Debug $(topdir)
debug: cmake-debug
cd build/debug && $(MAKE)
cd $(builddir)/debug && $(MAKE)
# Temporarily disable some tests:
# * libwallet_api_tests fail (Issue #895)
debug-test:
mkdir -p build/debug
cd build/debug && cmake -D BUILD_TESTS=ON -D CMAKE_BUILD_TYPE=Debug ../.. && $(MAKE) && $(MAKE) ARGS="-E libwallet_api_tests" test
mkdir -p $(builddir)/debug
cd $(builddir)/debug && cmake -D BUILD_TESTS=ON -D CMAKE_BUILD_TYPE=Debug $(topdir) && $(MAKE) && $(MAKE) ARGS="-E libwallet_api_tests" test
debug-all:
mkdir -p build/debug
cd build/debug && cmake -D BUILD_TESTS=ON -D BUILD_SHARED_LIBS=OFF -D CMAKE_BUILD_TYPE=Debug ../.. && $(MAKE)
mkdir -p $(builddir)/debug
cd $(builddir)/debug && cmake -D BUILD_TESTS=ON -D BUILD_SHARED_LIBS=OFF -D CMAKE_BUILD_TYPE=Debug $(topdir) && $(MAKE)
debug-static-all:
mkdir -p build/debug
cd build/debug && cmake -D BUILD_TESTS=ON -D STATIC=ON -D CMAKE_BUILD_TYPE=Debug ../.. && $(MAKE)
mkdir -p $(builddir)/debug
cd $(builddir)/debug && cmake -D BUILD_TESTS=ON -D STATIC=ON -D CMAKE_BUILD_TYPE=Debug $(topdir) && $(MAKE)
debug-static-win64:
mkdir -p build/debug
cd build/debug && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Debug -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=../../cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys64 ../.. && $(MAKE)
mkdir -p $(builddir)/debug
cd $(builddir)/debug && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Debug -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys64 $(topdir) && $(MAKE)
debug-static-win32:
mkdir -p build/debug
cd build/debug && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="i686" -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=Debug -D BUILD_TAG="win-x32" -D CMAKE_TOOLCHAIN_FILE=../../cmake/32-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys32 ../.. && $(MAKE)
mkdir -p $(builddir)/debug
cd $(builddir)/debug && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="i686" -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=Debug -D BUILD_TAG="win-x32" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/32-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys32 $(topdir) && $(MAKE)
cmake-release:
mkdir -p build/release
cd build/release && cmake -D CMAKE_BUILD_TYPE=Release ../..
mkdir -p $(builddir)/release
cd $(builddir)/release && cmake -D CMAKE_BUILD_TYPE=Release $(topdir)
release: cmake-release
cd build/release && $(MAKE)
cd $(builddir)/release && $(MAKE)
release-test:
mkdir -p build/release
cd build/release && cmake -D BUILD_TESTS=ON -D CMAKE_BUILD_TYPE=release ../.. && $(MAKE) && $(MAKE) test
mkdir -p $(builddir)/release
cd $(builddir)/release && cmake -D BUILD_TESTS=ON -D CMAKE_BUILD_TYPE=release $(topdir) && $(MAKE) && $(MAKE) test
release-all:
mkdir -p build/release
cd build/release && cmake -D BUILD_TESTS=ON -D CMAKE_BUILD_TYPE=release ../.. && $(MAKE)
mkdir -p $(builddir)/release
cd $(builddir)/release && cmake -D BUILD_TESTS=ON -D CMAKE_BUILD_TYPE=release $(topdir) && $(MAKE)
release-static:
mkdir -p build/release
cd build/release && cmake -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release ../.. && $(MAKE)
mkdir -p $(builddir)/release
cd $(builddir)/release && cmake -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release $(topdir) && $(MAKE)
coverage:
mkdir -p build/debug
cd build/debug && cmake -D BUILD_TESTS=ON -D CMAKE_BUILD_TYPE=Debug -D COVERAGE=ON ../.. && $(MAKE) && $(MAKE) test
mkdir -p $(builddir)/debug
cd $(builddir)/debug && cmake -D BUILD_TESTS=ON -D CMAKE_BUILD_TYPE=Debug -D COVERAGE=ON $(topdir) && $(MAKE) && $(MAKE) test
# Targets for specific prebuilt builds which will be advertised for updates by their build tag
release-static-linux-armv6:
mkdir -p build/release
cd build/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv6zk" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="linux-armv6" ../.. && $(MAKE)
mkdir -p $(builddir)/release
cd $(builddir)/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv6zk" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="linux-armv6" $(topdir) && $(MAKE)
release-static-linux-armv7:
mkdir -p build/release
cd build/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv7-a" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="linux-armv7" ../.. && $(MAKE)
mkdir -p $(builddir)/release
cd $(builddir)/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv7-a" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="linux-armv7" $(topdir) && $(MAKE)
release-static-android:
mkdir -p build/release/translations
cd build/release/translations && cmake ../../../translations && $(MAKE)
cd build/release && CC=arm-linux-androideabi-clang CXX=arm-linux-androideabi-clang++ cmake -D BUILD_TESTS=OFF -D ARCH="armv7-a" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D INSTALL_VENDORED_LIBUNBOUND=ON -D BUILD_TAG="android" -D CMAKE_SYSTEM_NAME="Android" -D CMAKE_ANDROID_STANDALONE_TOOLCHAIN="${ANDROID_STANDALONE_TOOLCHAIN_PATH}" -D CMAKE_ANDROID_ARM_MODE=ON -D CMAKE_ANDROID_ARCH_ABI="armeabi-v7a" ../.. && $(MAKE)
mkdir -p $(builddir)/release/translations
cd $(builddir)/release/translations && cmake ../../../translations && $(MAKE)
cd $(builddir)/release && CC=arm-linux-androideabi-clang CXX=arm-linux-androideabi-clang++ cmake -D BUILD_TESTS=OFF -D ARCH="armv7-a" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D INSTALL_VENDORED_LIBUNBOUND=ON -D BUILD_TAG="android" -D CMAKE_SYSTEM_NAME="Android" -D CMAKE_ANDROID_STANDALONE_TOOLCHAIN="${ANDROID_STANDALONE_TOOLCHAIN_PATH}" -D CMAKE_ANDROID_ARM_MODE=ON -D CMAKE_ANDROID_ARCH_ABI="armeabi-v7a" ../.. && $(MAKE)
release-static-linux-armv8:
mkdir -p build/release
cd build/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv8-a" -D STATIC=ON -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="linux-armv8" ../.. && $(MAKE)
mkdir -p $(builddir)/release
cd $(builddir)/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv8-a" -D STATIC=ON -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="linux-armv8" $(topdir) && $(MAKE)
release-static-linux-x86_64:
mkdir -p build/release
cd build/release && cmake -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="linux-x64" ../.. && $(MAKE)
mkdir -p $(builddir)/release
cd $(builddir)/release && cmake -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="linux-x64" $(topdir) && $(MAKE)
release-static-freebsd-x86_64:
mkdir -p build/release
cd build/release && cmake -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="freebsd-x64" ../.. && $(MAKE)
mkdir -p $(builddir)/release
cd $(builddir)/release && cmake -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="freebsd-x64" $(topdir) && $(MAKE)
release-static-mac-x86_64:
mkdir -p build/release
cd build/release && cmake -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="mac-x64" ../.. && $(MAKE)
mkdir -p $(builddir)/release
cd $(builddir)/release && cmake -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="mac-x64" $(topdir) && $(MAKE)
release-static-linux-i686:
mkdir -p build/release
cd build/release && cmake -D STATIC=ON -D ARCH="i686" -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="linux-x86" ../.. && $(MAKE)
mkdir -p $(builddir)/release
cd $(builddir)/release && cmake -D STATIC=ON -D ARCH="i686" -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="linux-x86" $(topdir) && $(MAKE)
release-static-win64:
mkdir -p build/release
cd build/release && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=../../cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys64 ../.. && $(MAKE)
mkdir -p $(builddir)/release
cd $(builddir)/release && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys64 $(topdir) && $(MAKE)
release-static-win32:
mkdir -p build/release
cd build/release && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="i686" -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=Release -D BUILD_TAG="win-x32" -D CMAKE_TOOLCHAIN_FILE=../../cmake/32-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys32 ../.. && $(MAKE)
mkdir -p $(builddir)/release
cd $(builddir)/release && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="i686" -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=Release -D BUILD_TAG="win-x32" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/32-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys32 $(topdir) && $(MAKE)
fuzz:
mkdir -p build/fuzz
cd build/fuzz && cmake -D STATIC=ON -D SANITIZE=ON -D BUILD_TESTS=ON -D USE_LTO=OFF -D CMAKE_C_COMPILER=afl-gcc -D CMAKE_CXX_COMPILER=afl-g++ -D ARCH="x86-64" -D CMAKE_BUILD_TYPE=fuzz -D BUILD_TAG="linux-x64" ../.. && $(MAKE)
mkdir -p $(builddir)/fuzz
cd $(builddir)/fuzz && cmake -D STATIC=ON -D SANITIZE=ON -D BUILD_TESTS=ON -D USE_LTO=OFF -D CMAKE_C_COMPILER=afl-gcc -D CMAKE_CXX_COMPILER=afl-g++ -D ARCH="x86-64" -D CMAKE_BUILD_TYPE=fuzz -D BUILD_TAG="linux-x64" $(topdir) && $(MAKE)
clean:
@echo "WARNING: Back-up your wallet if it exists within ./build!" ; \
read -r -p "This will destroy the build directory, continue (y/N)?: " CONTINUE; \
@echo "WARNING: Back-up your wallet if it exists within ./"$(deldirs)"!" ; \
read -r -p "This will destroy the build directory, continue (y/N)?: " CONTINUE; \
[ $$CONTINUE = "y" ] || [ $$CONTINUE = "Y" ] || (echo "Exiting."; exit 1;)
rm -rf build
rm -rf $(deldirs)
clean-all:
@echo "WARNING: Back-up your wallet if it exists within ./build!" ; \
read -r -p "This will destroy all build directories, continue (y/N)?: " CONTINUE; \
[ $$CONTINUE = "y" ] || [ $$CONTINUE = "Y" ] || (echo "Exiting."; exit 1;)
rm -rf ./build
tags:
ctags -R --sort=1 --c++-kinds=+p --fields=+iaS --extra=+q --language-force=C++ src contrib tests/gtest tests

View File

@ -26,7 +26,9 @@
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
set (CMAKE_SYSTEM_NAME Windows)
if (NOT CMAKE_HOST_WIN32)
set (CMAKE_SYSTEM_NAME Windows)
endif()
set (GCC_PREFIX i686-w64-mingw32)
set (CMAKE_C_COMPILER ${GCC_PREFIX}-gcc)

View File

@ -26,7 +26,9 @@
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
set (CMAKE_SYSTEM_NAME Windows)
if (NOT CMAKE_HOST_WIN32)
set (CMAKE_SYSTEM_NAME Windows)
endif()
set (GCC_PREFIX x86_64-w64-mingw32)
set (CMAKE_C_COMPILER ${GCC_PREFIX}-gcc)

View File

@ -1109,8 +1109,16 @@ POP_WARNINGS
sock_.open(remote_endpoint.protocol());
if(bind_ip != "0.0.0.0" && bind_ip != "0" && bind_ip != "" )
{
boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(adr.c_str()), 0);
sock_.bind(local_endpoint);
boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(bind_ip.c_str()), 0);
boost::system::error_code ec;
sock_.bind(local_endpoint, ec);
if (ec)
{
MERROR("Error binding to " << bind_ip << ": " << ec.message());
if (sock_.is_open())
sock_.close();
return false;
}
}
/*
@ -1215,8 +1223,16 @@ POP_WARNINGS
sock_.open(remote_endpoint.protocol());
if(bind_ip != "0.0.0.0" && bind_ip != "0" && bind_ip != "" )
{
boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(adr.c_str()), 0);
sock_.bind(local_endpoint);
boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(bind_ip.c_str()), 0);
boost::system::error_code ec;
sock_.bind(local_endpoint, ec);
if (ec)
{
MERROR("Error binding to " << bind_ip << ": " << ec.message());
if (sock_.is_open())
sock_.close();
return false;
}
}
boost::shared_ptr<boost::asio::deadline_timer> sh_deadline(new boost::asio::deadline_timer(io_service_));

View File

@ -47,6 +47,9 @@ if(MSVC)
elseif(NOT MSVC)
set_property(TARGET libminiupnpc-static APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-undef -Wno-unused-result -Wno-unused-value")
endif()
if(CMAKE_SYSTEM_NAME MATCHES "NetBSD")
set_property(TARGET libminiupnpc-static APPEND_STRING PROPERTY COMPILE_FLAGS " -D_NETBSD_SOURCE")
endif()
set(UPNP_LIBRARIES "libminiupnpc-static" PARENT_SCOPE)

View File

@ -32,3 +32,6 @@ message(STATUS "Using ${ARCH_WIDTH}-bit LMDB from source tree")
add_subdirectory(liblmdb)
set(LMDB_INCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/liblmdb" CACHE STRING "LMDB Include path")
set(LMDB_LIBRARY "lmdb" CACHE STRING "LMDB Library name")
if(CMAKE_SYSTEM_NAME MATCHES "NetBSD")
set_property(TARGET lmdb APPEND_STRING PROPERTY COMPILE_FLAGS " -D_SEM_SEMUN_UNDEFINED")
endif()

View File

@ -104,6 +104,11 @@
#else
# define ELPP_OS_OPENBSD 0
#endif
#if (defined(__NetBSD__))
# define ELPP_OS_NETBSD 1
#else
# define ELPP_OS_NETBSD 0
#endif
#if (defined(__sun))
# define ELPP_OS_SOLARIS 1
#else
@ -115,7 +120,7 @@
# define ELPP_OS_DRAGONFLY 0
#endif
// Unix
#if ((ELPP_OS_LINUX || ELPP_OS_MAC || ELPP_OS_FREEBSD || ELPP_OS_SOLARIS || ELPP_OS_DRAGONFLY || ELPP_OS_OPENBSD) && (!ELPP_OS_WINDOWS))
#if ((ELPP_OS_LINUX || ELPP_OS_MAC || ELPP_OS_FREEBSD || ELPP_OS_SOLARIS || ELPP_OS_DRAGONFLY || ELPP_OS_OPENBSD || ELPP_OS_NETBSD ) && (!ELPP_OS_WINDOWS))
# define ELPP_OS_UNIX 1
#else
# define ELPP_OS_UNIX 0
@ -200,7 +205,7 @@ ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStre
# define ELPP_INTERNAL_INFO(lvl, msg)
#endif // (defined(ELPP_DEBUG_INFO))
#if (defined(ELPP_FEATURE_ALL)) || (defined(ELPP_FEATURE_CRASH_LOG))
# if (ELPP_COMPILER_GCC && !ELPP_MINGW && !ELPP_OS_OPENBSD)
# if (ELPP_COMPILER_GCC && !ELPP_MINGW && !ELPP_OS_OPENBSD && !ELPP_OS_NETBSD)
# define ELPP_STACKTRACE 1
# else
# define ELPP_STACKTRACE 0

View File

@ -92,6 +92,26 @@ loki_private_headers(blockchain_usage
set(blockchain_ancestry_sources
blockchain_ancestry.cpp
)
set(blockchain_ancestry_private_headers)
loki_private_headers(blockchain_ancestry
${blockchain_ancestry_private_headers})
set(blockchain_depth_sources
blockchain_depth.cpp
)
set(blockchain_depth_private_headers)
loki_private_headers(blockchain_depth
${blockchain_depth_private_headers})
loki_add_executable(blockchain_import
${blockchain_import_sources}
${blockchain_import_private_headers}
@ -184,3 +204,45 @@ set_property(TARGET blockchain_usage
OUTPUT_NAME "loki-blockchain-usage")
install(TARGETS blockchain_usage DESTINATION bin)
loki_add_executable(blockchain_ancestry
${blockchain_ancestry_sources}
${blockchain_ancestry_private_headers})
target_link_libraries(blockchain_ancestry
PRIVATE
cryptonote_core
blockchain_db
version
epee
${Boost_FILESYSTEM_LIBRARY}
${Boost_SYSTEM_LIBRARY}
${Boost_THREAD_LIBRARY}
${CMAKE_THREAD_LIBS_INIT}
${EXTRA_LIBRARIES})
set_property(TARGET blockchain_ancestry
PROPERTY
OUTPUT_NAME "monero-blockchain-ancestry")
install(TARGETS blockchain_ancestry DESTINATION bin)
loki_add_executable(blockchain_depth
${blockchain_depth_sources}
${blockchain_depth_private_headers})
target_link_libraries(blockchain_depth
PRIVATE
cryptonote_core
blockchain_db
version
epee
${Boost_FILESYSTEM_LIBRARY}
${Boost_SYSTEM_LIBRARY}
${Boost_THREAD_LIBRARY}
${CMAKE_THREAD_LIBS_INIT}
${EXTRA_LIBRARIES})
set_property(TARGET blockchain_depth
PROPERTY
OUTPUT_NAME "monero-blockchain-depth")
install(TARGETS blockchain_depth DESTINATION bin)

View File

@ -0,0 +1,773 @@
// 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.
#include <unordered_map>
#include <unordered_set>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/archive/portable_binary_iarchive.hpp>
#include <boost/archive/portable_binary_oarchive.hpp>
#include "common/unordered_containers_boost_serialization.h"
#include "common/command_line.h"
#include "common/varint.h"
#include "cryptonote_basic/cryptonote_boost_serialization.h"
#include "cryptonote_core/tx_pool.h"
#include "cryptonote_core/cryptonote_core.h"
#include "cryptonote_core/blockchain.h"
#include "blockchain_db/blockchain_db.h"
#include "blockchain_db/db_types.h"
#include "version.h"
#undef LOKI_DEFAULT_LOG_CATEGORY
#define LOKI_DEFAULT_LOG_CATEGORY "bcutil"
namespace po = boost::program_options;
using namespace epee;
using namespace cryptonote;
static bool stop_requested = false;
struct ancestor
{
uint64_t amount;
uint64_t offset;
bool operator==(const ancestor &other) const { return amount == other.amount && offset == other.offset; }
template <typename t_archive> void serialize(t_archive &a, const unsigned int ver)
{
a & amount;
a & offset;
}
};
BOOST_CLASS_VERSION(ancestor, 0)
namespace std
{
template<> struct hash<ancestor>
{
size_t operator()(const ancestor &a) const
{
return a.amount ^ a.offset; // not that bad, since amount almost always have a high bit set, and offset doesn't
}
};
}
struct tx_data_t
{
std::vector<std::pair<uint64_t, std::vector<uint64_t>>> vin;
std::vector<crypto::public_key> vout;
bool coinbase;
tx_data_t(): coinbase(false) {}
tx_data_t(const cryptonote::transaction &tx)
{
coinbase = tx.vin.size() == 1 && tx.vin[0].type() == typeid(cryptonote::txin_gen);
if (!coinbase)
{
vin.reserve(tx.vin.size());
for (size_t ring = 0; ring < tx.vin.size(); ++ring)
{
if (tx.vin[ring].type() == typeid(cryptonote::txin_to_key))
{
const cryptonote::txin_to_key &txin = boost::get<cryptonote::txin_to_key>(tx.vin[ring]);
vin.push_back(std::make_pair(txin.amount, cryptonote::relative_output_offsets_to_absolute(txin.key_offsets)));
}
else
{
LOG_PRINT_L0("Bad vin type in txid " << get_transaction_hash(tx));
throw std::runtime_error("Bad vin type");
}
}
}
vout.reserve(tx.vout.size());
for (size_t out = 0; out < tx.vout.size(); ++out)
{
if (tx.vout[out].target.type() == typeid(cryptonote::txout_to_key))
{
const auto &txout = boost::get<cryptonote::txout_to_key>(tx.vout[out].target);
vout.push_back(txout.key);
}
else
{
LOG_PRINT_L0("Bad vout type in txid " << get_transaction_hash(tx));
throw std::runtime_error("Bad vout type");
}
}
}
template <typename t_archive> void serialize(t_archive &a, const unsigned int ver)
{
a & coinbase;
a & vin;
a & vout;
}
};
struct ancestry_state_t
{
uint64_t height;
std::unordered_map<crypto::hash, std::unordered_set<ancestor>> ancestry;
std::unordered_map<ancestor, crypto::hash> output_cache;
std::unordered_map<crypto::hash, ::tx_data_t> tx_cache;
std::vector<cryptonote::block> block_cache;
template <typename t_archive> void serialize(t_archive &a, const unsigned int ver)
{
a & height;
a & ancestry;
a & output_cache;
if (ver < 1)
{
std::unordered_map<crypto::hash, cryptonote::transaction> old_tx_cache;
a & old_tx_cache;
for (const auto i: old_tx_cache)
tx_cache.insert(std::make_pair(i.first, ::tx_data_t(i.second)));
}
else
{
a & tx_cache;
}
if (ver < 2)
{
std::unordered_map<uint64_t, cryptonote::block> old_block_cache;
a & old_block_cache;
block_cache.resize(old_block_cache.size());
for (const auto i: old_block_cache)
block_cache[i.first] = i.second;
}
else
{
a & block_cache;
}
}
};
BOOST_CLASS_VERSION(ancestry_state_t, 2)
static void add_ancestor(std::unordered_map<ancestor, unsigned int> &ancestry, uint64_t amount, uint64_t offset)
{
std::pair<std::unordered_map<ancestor, unsigned int>::iterator, bool> p = ancestry.insert(std::make_pair(ancestor{amount, offset}, 1));
if (!p.second)
{
++p.first->second;
}
}
static size_t get_full_ancestry(const std::unordered_map<ancestor, unsigned int> &ancestry)
{
size_t count = 0;
for (const auto &i: ancestry)
count += i.second;
return count;
}
static size_t get_deduplicated_ancestry(const std::unordered_map<ancestor, unsigned int> &ancestry)
{
return ancestry.size();
}
static void add_ancestry(std::unordered_map<crypto::hash, std::unordered_set<ancestor>> &ancestry, const crypto::hash &txid, const std::unordered_set<ancestor> &ancestors)
{
std::pair<std::unordered_map<crypto::hash, std::unordered_set<ancestor>>::iterator, bool> p = ancestry.insert(std::make_pair(txid, ancestors));
if (!p.second)
{
for (const auto &e: ancestors)
p.first->second.insert(e);
}
}
static void add_ancestry(std::unordered_map<crypto::hash, std::unordered_set<ancestor>> &ancestry, const crypto::hash &txid, const ancestor &new_ancestor)
{
std::pair<std::unordered_map<crypto::hash, std::unordered_set<ancestor>>::iterator, bool> p = ancestry.insert(std::make_pair(txid, std::unordered_set<ancestor>()));
p.first->second.insert(new_ancestor);
}
static std::unordered_set<ancestor> get_ancestry(const std::unordered_map<crypto::hash, std::unordered_set<ancestor>> &ancestry, const crypto::hash &txid)
{
std::unordered_map<crypto::hash, std::unordered_set<ancestor>>::const_iterator i = ancestry.find(txid);
if (i == ancestry.end())
{
//MERROR("txid ancestry not found: " << txid);
//throw std::runtime_error("txid ancestry not found");
return std::unordered_set<ancestor>();
}
return i->second;
}
int main(int argc, char* argv[])
{
TRY_ENTRY();
epee::string_tools::set_module_name_and_folder(argv[0]);
std::string default_db_type = "lmdb";
std::string available_dbs = cryptonote::blockchain_db_types(", ");
available_dbs = "available: " + available_dbs;
uint32_t log_level = 0;
tools::on_startup();
boost::filesystem::path output_file_path;
po::options_description desc_cmd_only("Command line options");
po::options_description desc_cmd_sett("Command line options and settings options");
const command_line::arg_descriptor<std::string> arg_log_level = {"log-level", "0-4 or categories", ""};
const command_line::arg_descriptor<std::string> arg_database = {
"database", available_dbs.c_str(), default_db_type
};
const command_line::arg_descriptor<std::string> arg_txid = {"txid", "Get ancestry for this txid", ""};
const command_line::arg_descriptor<uint64_t> arg_height = {"height", "Get ancestry for all txes at this height", 0};
const command_line::arg_descriptor<bool> arg_all = {"all", "Include the whole chain", false};
const command_line::arg_descriptor<bool> arg_cache_outputs = {"cache-outputs", "Cache outputs (memory hungry)", false};
const command_line::arg_descriptor<bool> arg_cache_txes = {"cache-txes", "Cache txes (memory hungry)", false};
const command_line::arg_descriptor<bool> arg_cache_blocks = {"cache-blocks", "Cache blocks (memory hungry)", false};
const command_line::arg_descriptor<bool> arg_include_coinbase = {"include-coinbase", "Including coinbase tx", false};
const command_line::arg_descriptor<bool> arg_show_cache_stats = {"show-cache-stats", "Show cache statistics", false};
command_line::add_arg(desc_cmd_sett, cryptonote::arg_data_dir);
command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on);
command_line::add_arg(desc_cmd_sett, cryptonote::arg_stagenet_on);
command_line::add_arg(desc_cmd_sett, arg_log_level);
command_line::add_arg(desc_cmd_sett, arg_database);
command_line::add_arg(desc_cmd_sett, arg_txid);
command_line::add_arg(desc_cmd_sett, arg_height);
command_line::add_arg(desc_cmd_sett, arg_all);
command_line::add_arg(desc_cmd_sett, arg_cache_outputs);
command_line::add_arg(desc_cmd_sett, arg_cache_txes);
command_line::add_arg(desc_cmd_sett, arg_cache_blocks);
command_line::add_arg(desc_cmd_sett, arg_include_coinbase);
command_line::add_arg(desc_cmd_sett, arg_show_cache_stats);
command_line::add_arg(desc_cmd_only, command_line::arg_help);
po::options_description desc_options("Allowed options");
desc_options.add(desc_cmd_only).add(desc_cmd_sett);
po::variables_map vm;
bool r = command_line::handle_error_helper(desc_options, [&]()
{
auto parser = po::command_line_parser(argc, argv).options(desc_options);
po::store(parser.run(), vm);
po::notify(vm);
return true;
});
if (! r)
return 1;
if (command_line::get_arg(vm, command_line::arg_help))
{
std::cout << "Loki '" << LOKI_RELEASE_NAME << "' (v" << LOKI_VERSION_FULL << ")" << ENDL << ENDL;
std::cout << desc_options << std::endl;
return 1;
}
mlog_configure(mlog_get_default_log_path("loki-blockchain-ancestry.log"), true);
if (!command_line::is_arg_defaulted(vm, arg_log_level))
mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str());
else
mlog_set_log(std::string(std::to_string(log_level) + ",bcutil:INFO").c_str());
LOG_PRINT_L0("Starting...");
std::string opt_data_dir = command_line::get_arg(vm, cryptonote::arg_data_dir);
bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
bool opt_stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on);
network_type net_type = opt_testnet ? TESTNET : opt_stagenet ? STAGENET : MAINNET;
std::string opt_txid_string = command_line::get_arg(vm, arg_txid);
uint64_t opt_height = command_line::get_arg(vm, arg_height);
bool opt_all = command_line::get_arg(vm, arg_all);
bool opt_cache_outputs = command_line::get_arg(vm, arg_cache_outputs);
bool opt_cache_txes = command_line::get_arg(vm, arg_cache_txes);
bool opt_cache_blocks = command_line::get_arg(vm, arg_cache_blocks);
bool opt_include_coinbase = command_line::get_arg(vm, arg_include_coinbase);
bool opt_show_cache_stats = command_line::get_arg(vm, arg_show_cache_stats);
if ((!opt_txid_string.empty()) + !!opt_height + !!opt_all > 1)
{
std::cerr << "Only one of --txid, --height and --all can be given" << std::endl;
return 1;
}
crypto::hash opt_txid = crypto::null_hash;
if (!opt_txid_string.empty())
{
if (!epee::string_tools::hex_to_pod(opt_txid_string, opt_txid))
{
std::cerr << "Invalid txid" << std::endl;
return 1;
}
}
std::string db_type = command_line::get_arg(vm, arg_database);
if (!cryptonote::blockchain_valid_db_type(db_type))
{
std::cerr << "Invalid database type: " << db_type << std::endl;
return 1;
}
LOG_PRINT_L0("Initializing source blockchain (BlockchainDB)");
// This is done this way because of the circular constructors.
struct BlockchainObjects
{
Blockchain m_blockchain;
tx_memory_pool m_mempool;
service_nodes::service_node_list m_service_node_list;
loki::deregister_vote_pool m_deregister_vote_pool;
BlockchainObjects() :
m_blockchain(m_mempool, m_service_node_list, m_deregister_vote_pool),
m_service_node_list(m_blockchain),
m_mempool(m_blockchain) { }
};
BlockchainObjects *blockchain_objects = new BlockchainObjects();
Blockchain *core_storage = &blockchain_objects->m_blockchain;
BlockchainDB *db = new_db(db_type);
if (db == NULL)
{
LOG_ERROR("Attempted to use non-existent database type: " << db_type);
throw std::runtime_error("Attempting to use non-existent database type");
}
LOG_PRINT_L0("database: " << db_type);
const std::string filename = (boost::filesystem::path(opt_data_dir) / db->get_db_name()).string();
LOG_PRINT_L0("Loading blockchain from folder " << filename << " ...");
try
{
db->open(filename, DBF_RDONLY);
}
catch (const std::exception& e)
{
LOG_PRINT_L0("Error opening database: " << e.what());
return 1;
}
r = core_storage->init(db, net_type);
CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize source blockchain storage");
LOG_PRINT_L0("Source blockchain storage initialized OK");
std::vector<crypto::hash> start_txids;
// forward method
if (opt_all)
{
uint64_t cached_txes = 0, cached_blocks = 0, cached_outputs = 0, total_txes = 0, total_blocks = 0, total_outputs = 0;
ancestry_state_t state;
const std::string state_file_path = (boost::filesystem::path(opt_data_dir) / "ancestry-state.bin").string();
LOG_PRINT_L0("Loading state data from " << state_file_path);
std::ifstream state_data_in;
state_data_in.open(state_file_path, std::ios_base::binary | std::ios_base::in);
if (!state_data_in.fail())
{
try
{
boost::archive::portable_binary_iarchive a(state_data_in);
a >> state;
}
catch (const std::exception &e)
{
MERROR("Failed to load state data from " << state_file_path << ", restarting from scratch");
state = ancestry_state_t();
}
state_data_in.close();
}
tools::signal_handler::install([](int type) {
stop_requested = true;
});
MINFO("Starting from height " << state.height);
const uint64_t db_height = db->height();
state.block_cache.reserve(db_height);
for (uint64_t h = state.height; h < db_height; ++h)
{
size_t block_ancestry_size = 0;
const crypto::hash block_hash = db->get_block_hash_from_height(h);
const cryptonote::blobdata bd = db->get_block_blob(block_hash);
++total_blocks;
cryptonote::block b;
if (!cryptonote::parse_and_validate_block_from_blob(bd, b))
{
LOG_PRINT_L0("Bad block from db");
return 1;
}
if (opt_cache_blocks)
{
state.block_cache.resize(h + 1);
state.block_cache[h] = b;
}
std::vector<crypto::hash> txids;
txids.reserve(1 + b.tx_hashes.size());
if (opt_include_coinbase)
txids.push_back(cryptonote::get_transaction_hash(b.miner_tx));
for (const auto &h: b.tx_hashes)
txids.push_back(h);
for (const crypto::hash &txid: txids)
{
printf("%lu/%lu \r", (unsigned long)h, (unsigned long)db_height);
fflush(stdout);
::tx_data_t tx_data;
std::unordered_map<crypto::hash, ::tx_data_t>::const_iterator i = state.tx_cache.find(txid);
++total_txes;
if (i != state.tx_cache.end())
{
++cached_txes;
tx_data = i->second;
}
else
{
cryptonote::blobdata bd;
if (!db->get_pruned_tx_blob(txid, bd))
{
LOG_PRINT_L0("Failed to get txid " << txid << " from db");
return 1;
}
cryptonote::transaction tx;
if (!cryptonote::parse_and_validate_tx_base_from_blob(bd, tx))
{
LOG_PRINT_L0("Bad tx: " << txid);
return 1;
}
tx_data = ::tx_data_t(tx);
if (opt_cache_txes)
state.tx_cache.insert(std::make_pair(txid, tx_data));
}
if (tx_data.coinbase)
{
add_ancestry(state.ancestry, txid, std::unordered_set<ancestor>());
}
else
{
for (size_t ring = 0; ring < tx_data.vin.size(); ++ring)
{
if (1)
{
const uint64_t amount = tx_data.vin[ring].first;
const std::vector<uint64_t> &absolute_offsets = tx_data.vin[ring].second;
for (uint64_t offset: absolute_offsets)
{
const output_data_t od = db->get_output_key(amount, offset);
add_ancestry(state.ancestry, txid, ancestor{amount, offset});
cryptonote::block b;
++total_blocks;
if (state.block_cache.size() > od.height && !state.block_cache[od.height].miner_tx.vin.empty())
{
++cached_blocks;
b = state.block_cache[od.height];
}
else
{
const crypto::hash block_hash = db->get_block_hash_from_height(od.height);
cryptonote::blobdata bd = db->get_block_blob(block_hash);
if (!cryptonote::parse_and_validate_block_from_blob(bd, b))
{
LOG_PRINT_L0("Bad block from db");
return 1;
}
if (opt_cache_blocks)
{
state.block_cache.resize(od.height + 1);
state.block_cache[od.height] = b;
}
}
// find the tx which created this output
bool found = false;
std::unordered_map<ancestor, crypto::hash>::const_iterator i = state.output_cache.find({amount, offset});
++total_outputs;
if (i != state.output_cache.end())
{
++cached_outputs;
add_ancestry(state.ancestry, txid, get_ancestry(state.ancestry, i->second));
found = true;
}
else for (size_t out = 0; out < b.miner_tx.vout.size(); ++out)
{
if (b.miner_tx.vout[out].target.type() == typeid(cryptonote::txout_to_key))
{
const auto &txout = boost::get<cryptonote::txout_to_key>(b.miner_tx.vout[out].target);
if (txout.key == od.pubkey)
{
found = true;
add_ancestry(state.ancestry, txid, get_ancestry(state.ancestry, cryptonote::get_transaction_hash(b.miner_tx)));
if (opt_cache_outputs)
state.output_cache.insert(std::make_pair(ancestor{amount, offset}, cryptonote::get_transaction_hash(b.miner_tx)));
break;
}
}
else
{
LOG_PRINT_L0("Bad vout type in txid " << cryptonote::get_transaction_hash(b.miner_tx));
return 1;
}
}
for (const crypto::hash &block_txid: b.tx_hashes)
{
if (found)
break;
::tx_data_t tx_data2;
std::unordered_map<crypto::hash, ::tx_data_t>::const_iterator i = state.tx_cache.find(block_txid);
++total_txes;
if (i != state.tx_cache.end())
{
++cached_txes;
tx_data2 = i->second;
}
else
{
cryptonote::blobdata bd;
if (!db->get_pruned_tx_blob(block_txid, bd))
{
LOG_PRINT_L0("Failed to get txid " << block_txid << " from db");
return 1;
}
cryptonote::transaction tx;
if (!cryptonote::parse_and_validate_tx_base_from_blob(bd, tx))
{
LOG_PRINT_L0("Bad tx: " << block_txid);
return 1;
}
tx_data2 = ::tx_data_t(tx);
if (opt_cache_txes)
state.tx_cache.insert(std::make_pair(block_txid, tx_data2));
}
for (size_t out = 0; out < tx_data2.vout.size(); ++out)
{
if (tx_data2.vout[out] == od.pubkey)
{
found = true;
add_ancestry(state.ancestry, txid, get_ancestry(state.ancestry, block_txid));
if (opt_cache_outputs)
state.output_cache.insert(std::make_pair(ancestor{amount, offset}, block_txid));
break;
}
}
}
if (!found)
{
LOG_PRINT_L0("Output originating transaction not found");
return 1;
}
}
}
}
}
const size_t ancestry_size = get_ancestry(state.ancestry, txid).size();
block_ancestry_size += ancestry_size;
MINFO(txid << ": " << ancestry_size);
}
if (!txids.empty())
{
std::string stats_msg;
if (opt_show_cache_stats)
stats_msg = std::string(", cache: txes ") + std::to_string(cached_txes*100./total_txes)
+ ", blocks " + std::to_string(cached_blocks*100./total_blocks) + ", outputs "
+ std::to_string(cached_outputs*100./total_outputs);
MINFO("Height " << h << ": " << (block_ancestry_size / txids.size()) << " average over " << txids.size() << stats_msg);
}
state.height = h;
if (stop_requested)
break;
}
LOG_PRINT_L0("Saving state data to " << state_file_path);
std::ofstream state_data_out;
state_data_out.open(state_file_path, std::ios_base::binary | std::ios_base::out | std::ios::trunc);
if (!state_data_out.fail())
{
try
{
boost::archive::portable_binary_oarchive a(state_data_out);
a << state;
}
catch (const std::exception &e)
{
MERROR("Failed to save state data to " << state_file_path);
}
state_data_out.close();
}
goto done;
}
if (!opt_txid_string.empty())
{
start_txids.push_back(opt_txid);
}
else
{
const crypto::hash block_hash = db->get_block_hash_from_height(opt_height);
const cryptonote::blobdata bd = db->get_block_blob(block_hash);
cryptonote::block b;
if (!cryptonote::parse_and_validate_block_from_blob(bd, b))
{
LOG_PRINT_L0("Bad block from db");
return 1;
}
for (const crypto::hash &txid: b.tx_hashes)
start_txids.push_back(txid);
}
if (start_txids.empty())
{
LOG_PRINT_L0("No transaction(s) to check");
return 1;
}
for (const crypto::hash &start_txid: start_txids)
{
LOG_PRINT_L0("Checking ancestry for txid " << start_txid);
std::unordered_map<ancestor, unsigned int> ancestry;
std::list<crypto::hash> txids;
txids.push_back(start_txid);
while (!txids.empty())
{
const crypto::hash txid = txids.front();
txids.pop_front();
cryptonote::blobdata bd;
if (!db->get_pruned_tx_blob(txid, bd))
{
LOG_PRINT_L0("Failed to get txid " << txid << " from db");
return 1;
}
cryptonote::transaction tx;
if (!cryptonote::parse_and_validate_tx_base_from_blob(bd, tx))
{
LOG_PRINT_L0("Bad tx: " << txid);
return 1;
}
const bool coinbase = tx.vin.size() == 1 && tx.vin[0].type() == typeid(cryptonote::txin_gen);
if (coinbase)
continue;
for (size_t ring = 0; ring < tx.vin.size(); ++ring)
{
if (tx.vin[ring].type() == typeid(cryptonote::txin_to_key))
{
const cryptonote::txin_to_key &txin = boost::get<cryptonote::txin_to_key>(tx.vin[ring]);
const uint64_t amount = txin.amount;
auto absolute_offsets = cryptonote::relative_output_offsets_to_absolute(txin.key_offsets);
for (uint64_t offset: absolute_offsets)
{
add_ancestor(ancestry, amount, offset);
const output_data_t od = db->get_output_key(amount, offset);
const crypto::hash block_hash = db->get_block_hash_from_height(od.height);
bd = db->get_block_blob(block_hash);
cryptonote::block b;
if (!cryptonote::parse_and_validate_block_from_blob(bd, b))
{
LOG_PRINT_L0("Bad block from db");
return 1;
}
// find the tx which created this output
bool found = false;
for (size_t out = 0; out < b.miner_tx.vout.size(); ++out)
{
if (b.miner_tx.vout[out].target.type() == typeid(cryptonote::txout_to_key))
{
const auto &txout = boost::get<cryptonote::txout_to_key>(b.miner_tx.vout[out].target);
if (txout.key == od.pubkey)
{
found = true;
txids.push_back(cryptonote::get_transaction_hash(b.miner_tx));
MDEBUG("adding txid: " << cryptonote::get_transaction_hash(b.miner_tx));
break;
}
}
else
{
LOG_PRINT_L0("Bad vout type in txid " << cryptonote::get_transaction_hash(b.miner_tx));
return 1;
}
}
for (const crypto::hash &block_txid: b.tx_hashes)
{
if (found)
break;
if (!db->get_pruned_tx_blob(block_txid, bd))
{
LOG_PRINT_L0("Failed to get txid " << block_txid << " from db");
return 1;
}
cryptonote::transaction tx2;
if (!cryptonote::parse_and_validate_tx_base_from_blob(bd, tx2))
{
LOG_PRINT_L0("Bad tx: " << block_txid);
return 1;
}
for (size_t out = 0; out < tx2.vout.size(); ++out)
{
if (tx2.vout[out].target.type() == typeid(cryptonote::txout_to_key))
{
const auto &txout = boost::get<cryptonote::txout_to_key>(tx2.vout[out].target);
if (txout.key == od.pubkey)
{
found = true;
txids.push_back(block_txid);
MDEBUG("adding txid: " << block_txid);
break;
}
}
else
{
LOG_PRINT_L0("Bad vout type in txid " << block_txid);
return 1;
}
}
}
if (!found)
{
LOG_PRINT_L0("Output originating transaction not found");
return 1;
}
}
}
else
{
LOG_PRINT_L0("Bad vin type in txid " << txid);
return 1;
}
}
}
MINFO("Ancestry for " << start_txid << ": " << get_deduplicated_ancestry(ancestry) << " / " << get_full_ancestry(ancestry));
for (const auto &i: ancestry)
{
MINFO(cryptonote::print_money(i.first.amount) << "/" << i.first.offset << ": " << i.second);
}
}
done:
core_storage->deinit();
return 0;
CATCH_ENTRY("Depth query error", 1);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,353 @@
// 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.
#include <boost/range/adaptor/transformed.hpp>
#include <boost/algorithm/string.hpp>
#include "common/command_line.h"
#include "common/varint.h"
#include "cryptonote_core/tx_pool.h"
#include "cryptonote_core/cryptonote_core.h"
#include "cryptonote_core/blockchain.h"
#include "blockchain_db/blockchain_db.h"
#include "blockchain_db/db_types.h"
#include "version.h"
#undef LOKI_DEFAULT_LOG_CATEGORY
#define LOKI_DEFAULT_LOG_CATEGORY "bcutil"
namespace po = boost::program_options;
using namespace epee;
using namespace cryptonote;
int main(int argc, char* argv[])
{
TRY_ENTRY();
epee::string_tools::set_module_name_and_folder(argv[0]);
std::string default_db_type = "lmdb";
std::string available_dbs = cryptonote::blockchain_db_types(", ");
available_dbs = "available: " + available_dbs;
uint32_t log_level = 0;
tools::on_startup();
boost::filesystem::path output_file_path;
po::options_description desc_cmd_only("Command line options");
po::options_description desc_cmd_sett("Command line options and settings options");
const command_line::arg_descriptor<std::string> arg_log_level = {"log-level", "0-4 or categories", ""};
const command_line::arg_descriptor<std::string> arg_database = {
"database", available_dbs.c_str(), default_db_type
};
const command_line::arg_descriptor<std::string> arg_txid = {"txid", "Get min depth for this txid", ""};
const command_line::arg_descriptor<uint64_t> arg_height = {"height", "Get min depth for all txes at this height", 0};
const command_line::arg_descriptor<bool> arg_include_coinbase = {"include-coinbase", "Include coinbase in the average", false};
command_line::add_arg(desc_cmd_sett, cryptonote::arg_data_dir);
command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on);
command_line::add_arg(desc_cmd_sett, cryptonote::arg_stagenet_on);
command_line::add_arg(desc_cmd_sett, arg_log_level);
command_line::add_arg(desc_cmd_sett, arg_database);
command_line::add_arg(desc_cmd_sett, arg_txid);
command_line::add_arg(desc_cmd_sett, arg_height);
command_line::add_arg(desc_cmd_sett, arg_include_coinbase);
command_line::add_arg(desc_cmd_only, command_line::arg_help);
po::options_description desc_options("Allowed options");
desc_options.add(desc_cmd_only).add(desc_cmd_sett);
po::variables_map vm;
bool r = command_line::handle_error_helper(desc_options, [&]()
{
auto parser = po::command_line_parser(argc, argv).options(desc_options);
po::store(parser.run(), vm);
po::notify(vm);
return true;
});
if (! r)
return 1;
if (command_line::get_arg(vm, command_line::arg_help))
{
std::cout << "Loki '" << LOKI_RELEASE_NAME << "' (v" << LOKI_VERSION_FULL << ")" << ENDL << ENDL;
std::cout << desc_options << std::endl;
return 1;
}
mlog_configure(mlog_get_default_log_path("loki-blockchain-depth.log"), true);
if (!command_line::is_arg_defaulted(vm, arg_log_level))
mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str());
else
mlog_set_log(std::string(std::to_string(log_level) + ",bcutil:INFO").c_str());
LOG_PRINT_L0("Starting...");
std::string opt_data_dir = command_line::get_arg(vm, cryptonote::arg_data_dir);
bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
bool opt_stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on);
network_type net_type = opt_testnet ? TESTNET : opt_stagenet ? STAGENET : MAINNET;
std::string opt_txid_string = command_line::get_arg(vm, arg_txid);
uint64_t opt_height = command_line::get_arg(vm, arg_height);
bool opt_include_coinbase = command_line::get_arg(vm, arg_include_coinbase);
if (!opt_txid_string.empty() && opt_height)
{
std::cerr << "txid and height cannot be given at the same time" << std::endl;
return 1;
}
crypto::hash opt_txid = crypto::null_hash;
if (!opt_txid_string.empty())
{
if (!epee::string_tools::hex_to_pod(opt_txid_string, opt_txid))
{
std::cerr << "Invalid txid" << std::endl;
return 1;
}
}
std::string db_type = command_line::get_arg(vm, arg_database);
if (!cryptonote::blockchain_valid_db_type(db_type))
{
std::cerr << "Invalid database type: " << db_type << std::endl;
return 1;
}
LOG_PRINT_L0("Initializing source blockchain (BlockchainDB)");
// This is done this way because of the circular constructors.
struct BlockchainObjects
{
Blockchain m_blockchain;
tx_memory_pool m_mempool;
service_nodes::service_node_list m_service_node_list;
loki::deregister_vote_pool m_deregister_vote_pool;
BlockchainObjects() :
m_blockchain(m_mempool, m_service_node_list, m_deregister_vote_pool),
m_service_node_list(m_blockchain),
m_mempool(m_blockchain) { }
};
BlockchainObjects *blockchain_objects = new BlockchainObjects();
Blockchain *core_storage = &blockchain_objects->m_blockchain;
BlockchainDB *db = new_db(db_type);
if (db == NULL)
{
LOG_ERROR("Attempted to use non-existent database type: " << db_type);
throw std::runtime_error("Attempting to use non-existent database type");
}
LOG_PRINT_L0("database: " << db_type);
const std::string filename = (boost::filesystem::path(opt_data_dir) / db->get_db_name()).string();
LOG_PRINT_L0("Loading blockchain from folder " << filename << " ...");
try
{
db->open(filename, DBF_RDONLY);
}
catch (const std::exception& e)
{
LOG_PRINT_L0("Error opening database: " << e.what());
return 1;
}
r = core_storage->init(db, net_type);
CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize source blockchain storage");
LOG_PRINT_L0("Source blockchain storage initialized OK");
std::vector<crypto::hash> start_txids;
if (!opt_txid_string.empty())
{
start_txids.push_back(opt_txid);
}
else
{
const crypto::hash block_hash = db->get_block_hash_from_height(opt_height);
const cryptonote::blobdata bd = db->get_block_blob(block_hash);
cryptonote::block b;
if (!cryptonote::parse_and_validate_block_from_blob(bd, b))
{
LOG_PRINT_L0("Bad block from db");
return 1;
}
for (const crypto::hash &txid: b.tx_hashes)
start_txids.push_back(txid);
if (opt_include_coinbase)
start_txids.push_back(cryptonote::get_transaction_hash(b.miner_tx));
}
if (start_txids.empty())
{
LOG_PRINT_L0("No transaction(s) to check");
return 1;
}
std::vector<uint64_t> depths;
for (const crypto::hash &start_txid: start_txids)
{
uint64_t depth = 0;
bool coinbase = false;
LOG_PRINT_L0("Checking depth for txid " << start_txid);
std::vector<crypto::hash> txids(1, start_txid);
while (!coinbase)
{
LOG_PRINT_L0("Considering "<< txids.size() << " transaction(s) at depth " << depth);
std::vector<crypto::hash> new_txids;
for (const crypto::hash &txid: txids)
{
cryptonote::blobdata bd;
if (!db->get_pruned_tx_blob(txid, bd))
{
LOG_PRINT_L0("Failed to get txid " << txid << " from db");
return 1;
}
cryptonote::transaction tx;
if (!cryptonote::parse_and_validate_tx_base_from_blob(bd, tx))
{
LOG_PRINT_L0("Bad tx: " << txid);
return 1;
}
for (size_t ring = 0; ring < tx.vin.size(); ++ring)
{
if (tx.vin[ring].type() == typeid(cryptonote::txin_gen))
{
MDEBUG(txid << " is a coinbase transaction");
coinbase = true;
goto done;
}
if (tx.vin[ring].type() == typeid(cryptonote::txin_to_key))
{
const cryptonote::txin_to_key &txin = boost::get<cryptonote::txin_to_key>(tx.vin[ring]);
const uint64_t amount = txin.amount;
auto absolute_offsets = cryptonote::relative_output_offsets_to_absolute(txin.key_offsets);
for (uint64_t offset: absolute_offsets)
{
const output_data_t od = db->get_output_key(amount, offset);
const crypto::hash block_hash = db->get_block_hash_from_height(od.height);
bd = db->get_block_blob(block_hash);
cryptonote::block b;
if (!cryptonote::parse_and_validate_block_from_blob(bd, b))
{
LOG_PRINT_L0("Bad block from db");
return 1;
}
// find the tx which created this output
bool found = false;
for (size_t out = 0; out < b.miner_tx.vout.size(); ++out)
{
if (b.miner_tx.vout[out].target.type() == typeid(cryptonote::txout_to_key))
{
const auto &txout = boost::get<cryptonote::txout_to_key>(b.miner_tx.vout[out].target);
if (txout.key == od.pubkey)
{
found = true;
new_txids.push_back(cryptonote::get_transaction_hash(b.miner_tx));
MDEBUG("adding txid: " << cryptonote::get_transaction_hash(b.miner_tx));
break;
}
}
else
{
LOG_PRINT_L0("Bad vout type in txid " << cryptonote::get_transaction_hash(b.miner_tx));
return 1;
}
}
for (const crypto::hash &block_txid: b.tx_hashes)
{
if (found)
break;
if (!db->get_pruned_tx_blob(block_txid, bd))
{
LOG_PRINT_L0("Failed to get txid " << block_txid << " from db");
return 1;
}
cryptonote::transaction tx2;
if (!cryptonote::parse_and_validate_tx_base_from_blob(bd, tx2))
{
LOG_PRINT_L0("Bad tx: " << block_txid);
return 1;
}
for (size_t out = 0; out < tx2.vout.size(); ++out)
{
if (tx2.vout[out].target.type() == typeid(cryptonote::txout_to_key))
{
const auto &txout = boost::get<cryptonote::txout_to_key>(tx2.vout[out].target);
if (txout.key == od.pubkey)
{
found = true;
new_txids.push_back(block_txid);
MDEBUG("adding txid: " << block_txid);
break;
}
}
else
{
LOG_PRINT_L0("Bad vout type in txid " << block_txid);
return 1;
}
}
}
if (!found)
{
LOG_PRINT_L0("Output originating transaction not found");
return 1;
}
}
}
else
{
LOG_PRINT_L0("Bad vin type in txid " << txid);
return 1;
}
}
}
if (!coinbase)
{
std::swap(txids, new_txids);
++depth;
}
}
done:
LOG_PRINT_L0("Min depth for txid " << start_txid << ": " << depth);
depths.push_back(depth);
}
uint64_t cumulative_depth = 0;
for (uint64_t depth: depths)
cumulative_depth += depth;
LOG_PRINT_L0("Average min depth for " << start_txids.size() << " transaction(s): " << cumulative_depth/(float)depths.size());
LOG_PRINT_L0("Median min depth for " << start_txids.size() << " transaction(s): " << epee::misc_utils::median(depths));
core_storage->deinit();
return 0;
CATCH_ENTRY("Depth query error", 1);
}

View File

@ -98,11 +98,16 @@ get_builtin_cert(void)
*/
/** return the built in root DS trust anchor */
static const char*
static const char* const*
get_builtin_ds(void)
{
return
". IN DS 19036 8 2 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5\n";
static const char * const ds[] =
{
". IN DS 19036 8 2 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5\n",
". IN DS 20326 8 2 E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D\n",
NULL
};
return ds;
}
/************************************************************
@ -241,7 +246,12 @@ DNSResolver::DNSResolver() : m_data(new DNSResolverData())
ub_ctx_hosts(m_data->m_ub_context, NULL);
}
ub_ctx_add_ta(m_data->m_ub_context, string_copy(::get_builtin_ds()));
const char * const *ds = ::get_builtin_ds();
while (*ds)
{
MINFO("adding trust anchor: " << *ds);
ub_ctx_add_ta(m_data->m_ub_context, string_copy(*ds++));
}
}
DNSResolver::~DNSResolver()

View File

@ -33,14 +33,15 @@
#include <stdlib.h>
#include <stdio.h>
// OS X, FreeBSD, and OpenBSD don't need malloc.h
// OS X, FreeBSD, OpenBSD and NetBSD don't need malloc.h
#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) \
&& !defined(__DragonFly__)
&& !defined(__DragonFly__) && !defined(__NetBSD__)
#include <malloc.h>
#endif
// ANDROID, FreeBSD, and OpenBSD also don't need timeb.h
#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__ANDROID__)
// ANDROID, FreeBSD, OpenBSD and NetBSD also don't need timeb.h
#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__ANDROID__) \
&& !defined(__NetBSD__)
#include <sys/timeb.h>
#else
#include <sys/time.h>
@ -473,7 +474,7 @@ OAES_RET oaes_sprintf(
#ifdef OAES_HAVE_ISAAC
static void oaes_get_seed( char buf[RANDSIZ + 1] )
{
#if !defined(__FreeBSD__) && !defined(__OpenBSD__)
#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__)
struct timeb timer;
struct tm *gmTimer;
char * _test = NULL;
@ -505,7 +506,7 @@ static void oaes_get_seed( char buf[RANDSIZ + 1] )
#else
static uint32_t oaes_get_seed(void)
{
#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__ANDROID__)
#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__ANDROID__) && !defined(__NetBSD__)
struct timeb timer;
struct tm *gmTimer;
char * _test = NULL;

View File

@ -38,6 +38,7 @@
#include "common/int-util.h"
#include "hash-ops.h"
#include "oaes_lib.h"
#include "variant2_int_sqrt.h"
#define MEMORY (1 << 21) // 2MB scratchpad
#define ITER (1 << 20)
@ -50,7 +51,7 @@ extern int aesb_single_round(const uint8_t *in, uint8_t*out, const uint8_t *expa
extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *expandedKey);
#define VARIANT1_1(p) \
do if (variant > 0) \
do if (variant == 1) \
{ \
const uint8_t tmp = ((const uint8_t*)(p))[11]; \
static const uint32_t table = 0x75310; \
@ -59,7 +60,7 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp
} while(0)
#define VARIANT1_2(p) \
do if (variant > 0) \
do if (variant == 1) \
{ \
xor64(p, tweak1_2); \
} while(0)
@ -67,7 +68,7 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp
#define VARIANT1_CHECK() \
do if (length < 43) \
{ \
fprintf(stderr, "Cryptonight variants need at least 43 bytes of data"); \
fprintf(stderr, "Cryptonight variant 1 needs at least 43 bytes of data"); \
_exit(1); \
} while(0)
@ -75,7 +76,7 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp
#define VARIANT1_PORTABLE_INIT() \
uint8_t tweak1_2[8]; \
do if (variant > 0) \
do if (variant == 1) \
{ \
VARIANT1_CHECK(); \
memcpy(&tweak1_2, &state.hs.b[192], sizeof(tweak1_2)); \
@ -83,11 +84,119 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp
} while(0)
#define VARIANT1_INIT64() \
if (variant > 0) \
if (variant == 1) \
{ \
VARIANT1_CHECK(); \
} \
const uint64_t tweak1_2 = variant > 0 ? (state.hs.w[24] ^ (*((const uint64_t*)NONCE_POINTER))) : 0
const uint64_t tweak1_2 = (variant == 1) ? (state.hs.w[24] ^ (*((const uint64_t*)NONCE_POINTER))) : 0
#define VARIANT2_INIT64() \
uint64_t division_result = 0; \
uint64_t sqrt_result = 0; \
do if (variant >= 2) \
{ \
U64(b)[2] = state.hs.w[8] ^ state.hs.w[10]; \
U64(b)[3] = state.hs.w[9] ^ state.hs.w[11]; \
division_result = state.hs.w[12]; \
sqrt_result = state.hs.w[13]; \
} while (0)
#define VARIANT2_PORTABLE_INIT() \
uint64_t division_result = 0; \
uint64_t sqrt_result = 0; \
do if (variant >= 2) \
{ \
memcpy(b + AES_BLOCK_SIZE, state.hs.b + 64, AES_BLOCK_SIZE); \
xor64(b + AES_BLOCK_SIZE, state.hs.b + 80); \
xor64(b + AES_BLOCK_SIZE + 8, state.hs.b + 88); \
division_result = state.hs.w[12]; \
sqrt_result = state.hs.w[13]; \
} while (0)
#define VARIANT2_SHUFFLE_ADD_SSE2(base_ptr, offset) \
do if (variant >= 2) \
{ \
const __m128i chunk1 = _mm_load_si128((__m128i *)((base_ptr) + ((offset) ^ 0x10))); \
const __m128i chunk2 = _mm_load_si128((__m128i *)((base_ptr) + ((offset) ^ 0x20))); \
const __m128i chunk3 = _mm_load_si128((__m128i *)((base_ptr) + ((offset) ^ 0x30))); \
_mm_store_si128((__m128i *)((base_ptr) + ((offset) ^ 0x10)), _mm_add_epi64(chunk3, _b1)); \
_mm_store_si128((__m128i *)((base_ptr) + ((offset) ^ 0x20)), _mm_add_epi64(chunk1, _b)); \
_mm_store_si128((__m128i *)((base_ptr) + ((offset) ^ 0x30)), _mm_add_epi64(chunk2, _a)); \
} while (0)
#define VARIANT2_SHUFFLE_ADD_NEON(base_ptr, offset) \
do if (variant >= 2) \
{ \
const uint64x2_t chunk1 = vld1q_u64(U64((base_ptr) + ((offset) ^ 0x10))); \
const uint64x2_t chunk2 = vld1q_u64(U64((base_ptr) + ((offset) ^ 0x20))); \
const uint64x2_t chunk3 = vld1q_u64(U64((base_ptr) + ((offset) ^ 0x30))); \
vst1q_u64(U64((base_ptr) + ((offset) ^ 0x10)), vaddq_u64(chunk3, vreinterpretq_u64_u8(_b1))); \
vst1q_u64(U64((base_ptr) + ((offset) ^ 0x20)), vaddq_u64(chunk1, vreinterpretq_u64_u8(_b))); \
vst1q_u64(U64((base_ptr) + ((offset) ^ 0x30)), vaddq_u64(chunk2, vreinterpretq_u64_u8(_a))); \
} while (0)
#define VARIANT2_PORTABLE_SHUFFLE_ADD(base_ptr, offset) \
do if (variant >= 2) \
{ \
uint64_t* chunk1 = U64((base_ptr) + ((offset) ^ 0x10)); \
uint64_t* chunk2 = U64((base_ptr) + ((offset) ^ 0x20)); \
uint64_t* chunk3 = U64((base_ptr) + ((offset) ^ 0x30)); \
\
const uint64_t chunk1_old[2] = { chunk1[0], chunk1[1] }; \
\
uint64_t b1[2]; \
memcpy(b1, b + 16, 16); \
chunk1[0] = chunk3[0] + b1[0]; \
chunk1[1] = chunk3[1] + b1[1]; \
\
uint64_t a0[2]; \
memcpy(a0, a, 16); \
chunk3[0] = chunk2[0] + a0[0]; \
chunk3[1] = chunk2[1] + a0[1]; \
\
uint64_t b0[2]; \
memcpy(b0, b, 16); \
chunk2[0] = chunk1_old[0] + b0[0]; \
chunk2[1] = chunk1_old[1] + b0[1]; \
} while (0)
#define VARIANT2_INTEGER_MATH_DIVISION_STEP(b, ptr) \
((uint64_t*)(b))[0] ^= division_result ^ (sqrt_result << 32); \
{ \
const uint64_t dividend = ((uint64_t*)(ptr))[1]; \
const uint32_t divisor = (((uint64_t*)(ptr))[0] + (uint32_t)(sqrt_result << 1)) | 0x80000001UL; \
division_result = ((uint32_t)(dividend / divisor)) + \
(((uint64_t)(dividend % divisor)) << 32); \
} \
const uint64_t sqrt_input = ((uint64_t*)(ptr))[0] + division_result
#define VARIANT2_INTEGER_MATH_SSE2(b, ptr) \
do if (variant >= 2) \
{ \
VARIANT2_INTEGER_MATH_DIVISION_STEP(b, ptr); \
VARIANT2_INTEGER_MATH_SQRT_STEP_SSE2(); \
VARIANT2_INTEGER_MATH_SQRT_FIXUP(sqrt_result); \
} while(0)
#if defined DBL_MANT_DIG && (DBL_MANT_DIG >= 50)
// double precision floating point type has enough bits of precision on current platform
#define VARIANT2_PORTABLE_INTEGER_MATH(b, ptr) \
do if (variant >= 2) \
{ \
VARIANT2_INTEGER_MATH_DIVISION_STEP(b, ptr); \
VARIANT2_INTEGER_MATH_SQRT_STEP_FP64(); \
VARIANT2_INTEGER_MATH_SQRT_FIXUP(sqrt_result); \
} while (0)
#else
// double precision floating point type is not good enough on current platform
// fall back to the reference code (integer only)
#define VARIANT2_PORTABLE_INTEGER_MATH(b, ptr) \
do if (variant >= 2) \
{ \
VARIANT2_INTEGER_MATH_DIVISION_STEP(b, ptr); \
VARIANT2_INTEGER_MATH_SQRT_STEP_REF(); \
} while (0)
#endif
#if !defined NO_AES && (defined(__x86_64__) || (defined(_MSC_VER) && defined(_WIN64)))
// Optimised code below, uses x86-specific intrinsics, SSE2, AES-NI
@ -164,19 +273,22 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp
* This code is based upon an optimized implementation by dga.
*/
#define post_aes() \
VARIANT2_SHUFFLE_ADD_SSE2(hp_state, j); \
_mm_store_si128(R128(c), _c); \
_b = _mm_xor_si128(_b, _c); \
_mm_store_si128(R128(&hp_state[j]), _b); \
_mm_store_si128(R128(&hp_state[j]), _mm_xor_si128(_b, _c)); \
VARIANT1_1(&hp_state[j]); \
j = state_index(c); \
p = U64(&hp_state[j]); \
b[0] = p[0]; b[1] = p[1]; \
VARIANT2_INTEGER_MATH_SSE2(b, c); \
__mul(); \
VARIANT2_SHUFFLE_ADD_SSE2(hp_state, j); \
a[0] += hi; a[1] += lo; \
p = U64(&hp_state[j]); \
p[0] = a[0]; p[1] = a[1]; \
a[0] ^= b[0]; a[1] ^= b[1]; \
VARIANT1_2(p + 1); \
_b1 = _b; \
_b = _c; \
#if defined(_MSC_VER)
@ -492,7 +604,7 @@ void slow_hash_allocate_state(void)
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#else
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \
defined(__DragonFly__)
defined(__DragonFly__) || defined(__NetBSD__)
hp_state = mmap(0, MEMORY, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON, 0, 0);
#else
@ -570,10 +682,10 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int
uint8_t text[INIT_SIZE_BYTE];
RDATA_ALIGN16 uint64_t a[2];
RDATA_ALIGN16 uint64_t b[2];
RDATA_ALIGN16 uint64_t b[4];
RDATA_ALIGN16 uint64_t c[2];
union cn_slow_hash_state state;
__m128i _a, _b, _c;
__m128i _a, _b, _b1, _c;
uint64_t hi, lo;
size_t i, j;
@ -599,6 +711,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int
memcpy(text, state.init, INIT_SIZE_BYTE);
VARIANT1_INIT64();
VARIANT2_INIT64();
/* CryptoNight Step 2: Iteratively encrypt the results from Keccak to fill
* the 2MB large random access buffer.
@ -637,6 +750,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int
*/
_b = _mm_load_si128(R128(b));
_b1 = _mm_load_si128(R128(b) + 1);
// Two independent versions, one with AES, one without, to ensure that
// the useAes test is only performed once, not every iteration.
if(useAes)
@ -761,19 +875,22 @@ union cn_slow_hash_state
_a = vld1q_u8((const uint8_t *)a); \
#define post_aes() \
VARIANT2_SHUFFLE_ADD_NEON(hp_state, j); \
vst1q_u8((uint8_t *)c, _c); \
_b = veorq_u8(_b, _c); \
vst1q_u8(&hp_state[j], _b); \
vst1q_u8(&hp_state[j], veorq_u8(_b, _c)); \
VARIANT1_1(&hp_state[j]); \
j = state_index(c); \
p = U64(&hp_state[j]); \
b[0] = p[0]; b[1] = p[1]; \
VARIANT2_PORTABLE_INTEGER_MATH(b, c); \
__mul(); \
VARIANT2_SHUFFLE_ADD_NEON(hp_state, j); \
a[0] += hi; a[1] += lo; \
p = U64(&hp_state[j]); \
p[0] = a[0]; p[1] = a[1]; \
a[0] ^= b[0]; a[1] ^= b[1]; \
VARIANT1_2(p + 1); \
_b1 = _b; \
_b = _c; \
@ -912,10 +1029,10 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int
uint8_t text[INIT_SIZE_BYTE];
RDATA_ALIGN16 uint64_t a[2];
RDATA_ALIGN16 uint64_t b[2];
RDATA_ALIGN16 uint64_t b[4];
RDATA_ALIGN16 uint64_t c[2];
union cn_slow_hash_state state;
uint8x16_t _a, _b, _c, zero = {0};
uint8x16_t _a, _b, _b1, _c, zero = {0};
uint64_t hi, lo;
size_t i, j;
@ -936,6 +1053,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int
memcpy(text, state.init, INIT_SIZE_BYTE);
VARIANT1_INIT64();
VARIANT2_INIT64();
/* CryptoNight Step 2: Iteratively encrypt the results from Keccak to fill
* the 2MB large random access buffer.
@ -959,7 +1077,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int
*/
_b = vld1q_u8((const uint8_t *)b);
_b1 = vld1q_u8(((const uint8_t *)b) + AES_BLOCK_SIZE);
for(i = 0; i < ITER / 2; i++)
{
@ -1075,6 +1193,11 @@ __asm__ __volatile__(
#endif /* !aarch64 */
#endif // NO_OPTIMIZED_MULTIPLY_ON_ARM
STATIC INLINE void copy_block(uint8_t* dst, const uint8_t* src)
{
memcpy(dst, src, AES_BLOCK_SIZE);
}
STATIC INLINE void sum_half_blocks(uint8_t* a, const uint8_t* b)
{
uint64_t a0, a1, b0, b1;
@ -1109,7 +1232,9 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int
{
uint8_t text[INIT_SIZE_BYTE];
uint8_t a[AES_BLOCK_SIZE];
uint8_t b[AES_BLOCK_SIZE];
uint8_t b[AES_BLOCK_SIZE * 2];
uint8_t c[AES_BLOCK_SIZE];
uint8_t c1[AES_BLOCK_SIZE];
uint8_t d[AES_BLOCK_SIZE];
uint8_t aes_key[AES_KEY_SIZE];
RDATA_ALIGN16 uint8_t expandedKey[256];
@ -1138,11 +1263,12 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int
}
memcpy(text, state.init, INIT_SIZE_BYTE);
VARIANT1_INIT64();
aes_ctx = (oaes_ctx *) oaes_alloc();
oaes_key_import_data(aes_ctx, state.hs.b, AES_KEY_SIZE);
VARIANT1_INIT64();
VARIANT2_INIT64();
// use aligned data
memcpy(expandedKey, aes_ctx->key->exp_data, aes_ctx->key->exp_data_len);
for(i = 0; i < MEMORY / INIT_SIZE_BYTE; i++)
@ -1163,23 +1289,33 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int
#define state_index(x) ((*(uint32_t *) x) & MASK)
// Iteration 1
p = &long_state[state_index(a)];
j = state_index(a);
p = &long_state[j];
aesb_single_round(p, p, a);
copy_block(c1, p);
xor_blocks(b, p);
swap_blocks(b, p);
swap_blocks(a, b);
VARIANT2_PORTABLE_SHUFFLE_ADD(long_state, j);
xor_blocks(p, b);
VARIANT1_1(p);
// Iteration 2
p = &long_state[state_index(a)];
j = state_index(c1);
p = &long_state[j];
copy_block(c, p);
mul(a, p, d);
sum_half_blocks(b, d);
swap_blocks(b, p);
xor_blocks(b, p);
swap_blocks(a, b);
VARIANT1_2(U64(p) + 1);
VARIANT2_PORTABLE_INTEGER_MATH(c, c1);
mul(c1, c, d);
VARIANT2_PORTABLE_SHUFFLE_ADD(long_state, j);
sum_half_blocks(a, d);
swap_blocks(a, c);
xor_blocks(a, c);
VARIANT1_2(U64(c) + 1);
copy_block(p, c);
if (variant >= 2) {
copy_block(b + AES_BLOCK_SIZE, b);
}
copy_block(b, c1);
}
memcpy(text, state.init, INIT_SIZE_BYTE);
@ -1298,8 +1434,9 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int
union cn_slow_hash_state state;
uint8_t text[INIT_SIZE_BYTE];
uint8_t a[AES_BLOCK_SIZE];
uint8_t b[AES_BLOCK_SIZE];
uint8_t c[AES_BLOCK_SIZE];
uint8_t b[AES_BLOCK_SIZE * 2];
uint8_t c1[AES_BLOCK_SIZE];
uint8_t c2[AES_BLOCK_SIZE];
uint8_t d[AES_BLOCK_SIZE];
size_t i, j;
uint8_t aes_key[AES_KEY_SIZE];
@ -1315,6 +1452,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int
aes_ctx = (oaes_ctx *) oaes_alloc();
VARIANT1_PORTABLE_INIT();
VARIANT2_PORTABLE_INIT();
oaes_key_import_data(aes_ctx, aes_key, AES_KEY_SIZE);
for (i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) {
@ -1324,9 +1462,9 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int
memcpy(&long_state[i * INIT_SIZE_BYTE], text, INIT_SIZE_BYTE);
}
for (i = 0; i < 16; i++) {
a[i] = state.k[ i] ^ state.k[32 + i];
b[i] = state.k[16 + i] ^ state.k[48 + i];
for (i = 0; i < AES_BLOCK_SIZE; i++) {
a[i] = state.k[ i] ^ state.k[AES_BLOCK_SIZE * 2 + i];
b[i] = state.k[AES_BLOCK_SIZE + i] ^ state.k[AES_BLOCK_SIZE * 3 + i];
}
for (i = 0; i < ITER / 2; i++) {
@ -1335,26 +1473,32 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int
* next address <-+
*/
/* Iteration 1 */
j = e2i(a, MEMORY / AES_BLOCK_SIZE);
copy_block(c, &long_state[j * AES_BLOCK_SIZE]);
aesb_single_round(c, c, a);
xor_blocks(b, c);
swap_blocks(b, c);
copy_block(&long_state[j * AES_BLOCK_SIZE], c);
assert(j == e2i(a, MEMORY / AES_BLOCK_SIZE));
swap_blocks(a, b);
VARIANT1_1(&long_state[j * AES_BLOCK_SIZE]);
j = e2i(a, MEMORY / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
copy_block(c1, &long_state[j]);
aesb_single_round(c1, c1, a);
VARIANT2_PORTABLE_SHUFFLE_ADD(long_state, j);
copy_block(&long_state[j], c1);
xor_blocks(&long_state[j], b);
assert(j == e2i(a, MEMORY / AES_BLOCK_SIZE) * AES_BLOCK_SIZE);
VARIANT1_1(&long_state[j]);
/* Iteration 2 */
j = e2i(a, MEMORY / AES_BLOCK_SIZE);
copy_block(c, &long_state[j * AES_BLOCK_SIZE]);
mul(a, c, d);
sum_half_blocks(b, d);
swap_blocks(b, c);
xor_blocks(b, c);
VARIANT1_2(c + 8);
copy_block(&long_state[j * AES_BLOCK_SIZE], c);
assert(j == e2i(a, MEMORY / AES_BLOCK_SIZE));
swap_blocks(a, b);
j = e2i(c1, MEMORY / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
copy_block(c2, &long_state[j]);
VARIANT2_PORTABLE_INTEGER_MATH(c2, c1);
mul(c1, c2, d);
VARIANT2_PORTABLE_SHUFFLE_ADD(long_state, j);
swap_blocks(a, c1);
sum_half_blocks(c1, d);
swap_blocks(c1, c2);
xor_blocks(c1, c2);
VARIANT1_2(c2 + 8);
copy_block(&long_state[j], c2);
assert(j == e2i(a, MEMORY / AES_BLOCK_SIZE) * AES_BLOCK_SIZE);
if (variant >= 2) {
copy_block(b + AES_BLOCK_SIZE, b);
}
copy_block(b, a);
copy_block(a, c1);
}
memcpy(text, state.init, INIT_SIZE_BYTE);

View File

@ -36,7 +36,8 @@
#ifdef _MSC_VER
#include <malloc.h>
#elif !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__DragonFly__)
#elif !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__DragonFly__) \
&& !defined(__NetBSD__)
#include <alloca.h>
#else
#include <stdlib.h>

View File

@ -0,0 +1,163 @@
#ifndef VARIANT2_INT_SQRT_H
#define VARIANT2_INT_SQRT_H
#include <math.h>
#include <float.h>
#define VARIANT2_INTEGER_MATH_SQRT_STEP_SSE2() \
do { \
const __m128i exp_double_bias = _mm_set_epi64x(0, 1023ULL << 52); \
__m128d x = _mm_castsi128_pd(_mm_add_epi64(_mm_cvtsi64_si128(sqrt_input >> 12), exp_double_bias)); \
x = _mm_sqrt_sd(_mm_setzero_pd(), x); \
sqrt_result = (uint64_t)(_mm_cvtsi128_si64(_mm_sub_epi64(_mm_castpd_si128(x), exp_double_bias))) >> 19; \
} while(0)
#define VARIANT2_INTEGER_MATH_SQRT_STEP_FP64() \
do { \
sqrt_result = sqrt(sqrt_input + 18446744073709551616.0) * 2.0 - 8589934592.0; \
} while(0)
#define VARIANT2_INTEGER_MATH_SQRT_STEP_REF() \
sqrt_result = integer_square_root_v2(sqrt_input)
// Reference implementation of the integer square root for Cryptonight variant 2
// Computes integer part of "sqrt(2^64 + n) * 2 - 2^33"
//
// In other words, given 64-bit unsigned integer n:
// 1) Write it as x = 1.NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN000... in binary (1 <= x < 2, all 64 bits of n are used)
// 2) Calculate sqrt(x) = 1.0RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR... (1 <= sqrt(x) < sqrt(2), so it will always start with "1.0" in binary)
// 3) Take 32 bits that come after "1.0" and return them as a 32-bit unsigned integer, discard all remaining bits
//
// Some sample inputs and outputs:
//
// Input | Output | Exact value of "sqrt(2^64 + n) * 2 - 2^33"
// -----------------|------------|-------------------------------------------
// 0 | 0 | 0
// 2^32 | 0 | 0.99999999994179233909330885695244...
// 2^32 + 1 | 1 | 1.0000000001746229827200734316305...
// 2^50 | 262140 | 262140.00012206565608606978175873...
// 2^55 + 20963331 | 8384515 | 8384515.9999999997673963974959744...
// 2^55 + 20963332 | 8384516 | 8384516
// 2^62 + 26599786 | 1013904242 | 1013904242.9999999999479374853545...
// 2^62 + 26599787 | 1013904243 | 1013904243.0000000001561875439364...
// 2^64 - 1 | 3558067407 | 3558067407.9041987696409179931096...
// The reference implementation as it is now uses only unsigned int64 arithmetic, so it can't have undefined behavior
// It was tested once for all edge cases and confirmed correct
static inline uint32_t integer_square_root_v2(uint64_t n)
{
uint64_t r = 1ULL << 63;
for (uint64_t bit = 1ULL << 60; bit; bit >>= 2)
{
const bool b = (n < r + bit);
const uint64_t n_next = n - (r + bit);
const uint64_t r_next = r + bit * 2;
n = b ? n : n_next;
r = b ? r : r_next;
r >>= 1;
}
return r * 2 + ((n > r) ? 1 : 0);
}
/*
VARIANT2_INTEGER_MATH_SQRT_FIXUP checks that "r" is an integer part of "sqrt(2^64 + sqrt_input) * 2 - 2^33" and adds or subtracts 1 if needed
It's hard to understand how it works, so here is a full calculation of formulas used in VARIANT2_INTEGER_MATH_SQRT_FIXUP
The following inequalities must hold for r if it's an integer part of "sqrt(2^64 + sqrt_input) * 2 - 2^33":
1) r <= sqrt(2^64 + sqrt_input) * 2 - 2^33
2) r + 1 > sqrt(2^64 + sqrt_input) * 2 - 2^33
We need to check them using only unsigned integer arithmetic to avoid rounding errors and undefined behavior
First inequality: r <= sqrt(2^64 + sqrt_input) * 2 - 2^33
-----------------------------------------------------------------------------------
r <= sqrt(2^64 + sqrt_input) * 2 - 2^33
r + 2^33 <= sqrt(2^64 + sqrt_input) * 2
r/2 + 2^32 <= sqrt(2^64 + sqrt_input)
(r/2 + 2^32)^2 <= 2^64 + sqrt_input
Rewrite r as r = s * 2 + b (s = trunc(r/2), b is 0 or 1)
((s*2+b)/2 + 2^32)^2 <= 2^64 + sqrt_input
(s*2+b)^2/4 + 2*2^32*(s*2+b)/2 + 2^64 <= 2^64 + sqrt_input
(s*2+b)^2/4 + 2*2^32*(s*2+b)/2 <= sqrt_input
(s*2+b)^2/4 + 2^32*r <= sqrt_input
(s^2*4+2*s*2*b+b^2)/4 + 2^32*r <= sqrt_input
s^2+s*b+b^2/4 + 2^32*r <= sqrt_input
s*(s+b) + b^2/4 + 2^32*r <= sqrt_input
Let r2 = s*(s+b) + r*2^32
r2 + b^2/4 <= sqrt_input
If this inequality doesn't hold, then we must decrement r: IF "r2 + b^2/4 > sqrt_input" THEN r = r - 1
b can be 0 or 1
If b is 0 then we need to compare "r2 > sqrt_input"
If b is 1 then b^2/4 = 0.25, so we need to compare "r2 + 0.25 > sqrt_input"
Since both r2 and sqrt_input are integers, we can safely replace it with "r2 + 1 > sqrt_input"
-----------------------------------------------------------------------------------
Both cases can be merged to a single expression "r2 + b > sqrt_input"
-----------------------------------------------------------------------------------
There will be no overflow when calculating "r2 + b", so it's safe to compare with sqrt_input:
r2 + b = s*(s+b) + r*2^32 + b
The largest value s, b and r can have is s = 1779033703, b = 1, r = 3558067407 when sqrt_input = 2^64 - 1
r2 + b <= 1779033703*1779033704 + 3558067407*2^32 + 1 = 18446744068217447385 < 2^64
Second inequality: r + 1 > sqrt(2^64 + sqrt_input) * 2 - 2^33
-----------------------------------------------------------------------------------
r + 1 > sqrt(2^64 + sqrt_input) * 2 - 2^33
r + 1 + 2^33 > sqrt(2^64 + sqrt_input) * 2
((r+1)/2 + 2^32)^2 > 2^64 + sqrt_input
Rewrite r as r = s * 2 + b (s = trunc(r/2), b is 0 or 1)
((s*2+b+1)/2 + 2^32)^2 > 2^64 + sqrt_input
(s*2+b+1)^2/4 + 2*(s*2+b+1)/2*2^32 + 2^64 > 2^64 + sqrt_input
(s*2+b+1)^2/4 + (s*2+b+1)*2^32 > sqrt_input
(s*2+b+1)^2/4 + (r+1)*2^32 > sqrt_input
(s*2+(b+1))^2/4 + r*2^32 + 2^32 > sqrt_input
(s^2*4+2*s*2*(b+1)+(b+1)^2)/4 + r*2^32 + 2^32 > sqrt_input
s^2+s*(b+1)+(b+1)^2/4 + r*2^32 + 2^32 > sqrt_input
s*(s+b) + s + (b+1)^2/4 + r*2^32 + 2^32 > sqrt_input
Let r2 = s*(s+b) + r*2^32
r2 + s + (b+1)^2/4 + 2^32 > sqrt_input
r2 + 2^32 + (b+1)^2/4 > sqrt_input - s
If this inequality doesn't hold, then we must decrement r: IF "r2 + 2^32 + (b+1)^2/4 <= sqrt_input - s" THEN r = r - 1
b can be 0 or 1
If b is 0 then we need to compare "r2 + 2^32 + 1/4 <= sqrt_input - s" which is equal to "r2 + 2^32 < sqrt_input - s" because all numbers here are integers
If b is 1 then (b+1)^2/4 = 1, so we need to compare "r2 + 2^32 + 1 <= sqrt_input - s" which is also equal to "r2 + 2^32 < sqrt_input - s"
-----------------------------------------------------------------------------------
Both cases can be merged to a single expression "r2 + 2^32 < sqrt_input - s"
-----------------------------------------------------------------------------------
There will be no overflow when calculating "r2 + 2^32":
r2 + 2^32 = s*(s+b) + r*2^32 + 2^32 = s*(s+b) + (r+1)*2^32
The largest value s, b and r can have is s = 1779033703, b = 1, r = 3558067407 when sqrt_input = 2^64 - 1
r2 + b <= 1779033703*1779033704 + 3558067408*2^32 = 18446744072512414680 < 2^64
There will be no integer overflow when calculating "sqrt_input - s", i.e. "sqrt_input >= s" at all times:
s = trunc(r/2) = trunc(sqrt(2^64 + sqrt_input) - 2^32) < sqrt(2^64 + sqrt_input) - 2^32 + 1
sqrt_input > sqrt(2^64 + sqrt_input) - 2^32 + 1
sqrt_input + 2^32 - 1 > sqrt(2^64 + sqrt_input)
(sqrt_input + 2^32 - 1)^2 > sqrt_input + 2^64
sqrt_input^2 + 2*sqrt_input*(2^32 - 1) + (2^32-1)^2 > sqrt_input + 2^64
sqrt_input^2 + sqrt_input*(2^33 - 2) + (2^32-1)^2 > sqrt_input + 2^64
sqrt_input^2 + sqrt_input*(2^33 - 3) + (2^32-1)^2 > 2^64
sqrt_input^2 + sqrt_input*(2^33 - 3) + 2^64-2^33+1 > 2^64
sqrt_input^2 + sqrt_input*(2^33 - 3) - 2^33 + 1 > 0
This inequality is true if sqrt_input > 1 and it's easy to check that s = 0 if sqrt_input is 0 or 1, so there will be no integer overflow
*/
#define VARIANT2_INTEGER_MATH_SQRT_FIXUP(r) \
do { \
const uint64_t s = r >> 1; \
const uint64_t b = r & 1; \
const uint64_t r2 = (uint64_t)(s) * (s + b) + (r << 32); \
r += ((r2 + b > sqrt_input) ? -1 : 0) + ((r2 + (1ULL << 32) < sqrt_input - s) ? 1 : 0); \
} while(0)
#endif

View File

@ -223,15 +223,25 @@ namespace cryptonote
{
crypto::key_derivation recv_derivation = AUTO_VAL_INIT(recv_derivation);
bool r = hwdev.generate_key_derivation(tx_public_key, ack.m_view_secret_key, recv_derivation);
CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to generate_key_derivation(" << tx_public_key << ", " << ack.m_view_secret_key << ")");
if (!r)
{
MWARNING("key image helper: failed to generate_key_derivation(" << tx_public_key << ", " << ack.m_view_secret_key << ")");
memcpy(&recv_derivation, rct::identity().bytes, sizeof(recv_derivation));
}
std::vector<crypto::key_derivation> additional_recv_derivations;
for (size_t i = 0; i < additional_tx_public_keys.size(); ++i)
{
crypto::key_derivation additional_recv_derivation = AUTO_VAL_INIT(additional_recv_derivation);
r = hwdev.generate_key_derivation(additional_tx_public_keys[i], ack.m_view_secret_key, additional_recv_derivation);
CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to generate_key_derivation(" << additional_tx_public_keys[i] << ", " << ack.m_view_secret_key << ")");
additional_recv_derivations.push_back(additional_recv_derivation);
if (!r)
{
MWARNING("key image helper: failed to generate_key_derivation(" << additional_tx_public_keys[i] << ", " << ack.m_view_secret_key << ")");
}
else
{
additional_recv_derivations.push_back(additional_recv_derivation);
}
}
boost::optional<subaddress_receive_info> subaddr_recv_info = is_out_to_acc_precomp(subaddresses, out_key, recv_derivation, additional_recv_derivations, real_output_index,hwdev);

View File

@ -1764,17 +1764,6 @@ size_t Blockchain::get_alternative_blocks_count() const
//------------------------------------------------------------------
// This function adds the output specified by <amount, i> to the result_outs container
// unlocked and other such checks should be done by here.
void Blockchain::add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& oen = *result_outs.outs.insert(result_outs.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry());
oen.global_amount_index = i;
output_data_t data = m_db->get_output_key(amount, i);
oen.out_key = data.pubkey;
}
uint64_t Blockchain::get_num_mature_outputs(uint64_t amount) const
{
uint64_t num_outs = m_db->get_num_outputs(amount);
@ -1792,231 +1781,12 @@ uint64_t Blockchain::get_num_mature_outputs(uint64_t amount) const
return num_outs;
}
std::vector<uint64_t> Blockchain::get_random_outputs(uint64_t amount, uint64_t count) const
{
uint64_t num_outs = get_num_mature_outputs(amount);
std::vector<uint64_t> indices;
std::unordered_set<uint64_t> seen_indices;
// if there aren't enough outputs to mix with (or just enough),
// use all of them. Eventually this should become impossible.
if (num_outs <= count)
{
for (uint64_t i = 0; i < num_outs; i++)
{
// if tx is unlocked, add output to indices
if (is_output_spendtime_unlocked(m_db->get_output_unlock_time(amount, i)))
{
indices.push_back(i);
}
}
}
else
{
// while we still need more mixins
while (indices.size() < count)
{
// if we've gone through every possible output, we've gotten all we can
if (seen_indices.size() == num_outs)
{
break;
}
// get a random output index from the DB. If we've already seen it,
// return to the top of the loop and try again, otherwise add it to the
// list of output indices we've seen.
// triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53);
double frac = std::sqrt((double)r / ((uint64_t)1 << 53));
uint64_t i = (uint64_t)(frac*num_outs);
// just in case rounding up to 1 occurs after sqrt
if (i == num_outs)
--i;
if (seen_indices.count(i))
{
continue;
}
seen_indices.emplace(i);
// if the output's transaction is unlocked, add the output's index to
// our list.
if (is_output_spendtime_unlocked(m_db->get_output_unlock_time(amount, i)))
{
indices.push_back(i);
}
}
}
return indices;
}
crypto::public_key Blockchain::get_output_key(uint64_t amount, uint64_t global_index) const
{
output_data_t data = m_db->get_output_key(amount, global_index);
return data.pubkey;
}
//------------------------------------------------------------------
// This function takes an RPC request for mixins and creates an RPC response
// with the requested mixins.
// TODO: figure out why this returns boolean / if we should be returning false
// in some cases
bool Blockchain::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
// for each amount that we need to get mixins for, get <n> random outputs
// from BlockchainDB where <n> is req.outs_count (number of mixins).
for (uint64_t amount : req.amounts)
{
// create outs_for_amount struct and populate amount field
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs = *res.outs.insert(res.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount());
result_outs.amount = amount;
std::vector<uint64_t> indices = get_random_outputs(amount, req.outs_count);
for (auto i : indices)
{
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& oe = *result_outs.outs.insert(result_outs.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry());
oe.global_amount_index = i;
oe.out_key = get_output_key(amount, i);
}
}
return true;
}
//------------------------------------------------------------------
// This function adds the ringct output at index i to the list
// unlocked and other such checks should be done by here.
void Blockchain::add_out_to_get_rct_random_outs(std::list<COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry>& outs, uint64_t amount, size_t i) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry& oen = *outs.insert(outs.end(), COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry());
oen.amount = amount;
oen.global_amount_index = i;
output_data_t data = m_db->get_output_key(amount, i);
oen.out_key = data.pubkey;
oen.commitment = data.commitment;
}
//------------------------------------------------------------------
// This function takes an RPC request for mixins and creates an RPC response
// with the requested mixins.
// TODO: figure out why this returns boolean / if we should be returning false
// in some cases
bool Blockchain::get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
// for each amount that we need to get mixins for, get <n> random outputs
// from BlockchainDB where <n> is req.outs_count (number of mixins).
auto num_outs = m_db->get_num_outputs(0);
// ensure we don't include outputs that aren't yet eligible to be used
// outpouts are sorted by height
while (num_outs > 0)
{
const tx_out_index toi = m_db->get_output_tx_and_index(0, num_outs - 1);
const uint64_t height = m_db->get_tx_block_height(toi.first);
if (height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE <= m_db->height())
break;
--num_outs;
}
std::unordered_set<uint64_t> seen_indices;
// if there aren't enough outputs to mix with (or just enough),
// use all of them. Eventually this should become impossible.
if (num_outs <= req.outs_count)
{
for (uint64_t i = 0; i < num_outs; i++)
{
// if tx is unlocked, add output to result_outs
if (is_output_spendtime_unlocked(m_db->get_output_unlock_time(0, i)))
{
add_out_to_get_rct_random_outs(res.outs, 0, i);
}
}
}
else
{
// while we still need more mixins
while (res.outs.size() < req.outs_count)
{
// if we've gone through every possible output, we've gotten all we can
if (seen_indices.size() == num_outs)
{
break;
}
// get a random output index from the DB. If we've already seen it,
// return to the top of the loop and try again, otherwise add it to the
// list of output indices we've seen.
// triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53);
double frac = std::sqrt((double)r / ((uint64_t)1 << 53));
uint64_t i = (uint64_t)(frac*num_outs);
// just in case rounding up to 1 occurs after sqrt
if (i == num_outs)
--i;
if (seen_indices.count(i))
{
continue;
}
seen_indices.emplace(i);
// if the output's transaction is unlocked, add the output's index to
// our list.
if (is_output_spendtime_unlocked(m_db->get_output_unlock_time(0, i)))
{
add_out_to_get_rct_random_outs(res.outs, 0, i);
}
}
}
if (res.outs.size() < req.outs_count)
return false;
#if 0
// if we do not have enough RCT inputs, we can pick from the non RCT ones
// which will have a zero mask
if (res.outs.size() < req.outs_count)
{
LOG_PRINT_L0("Out of RCT inputs (" << res.outs.size() << "/" << req.outs_count << "), using regular ones");
// TODO: arbitrary selection, needs better
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request req2 = AUTO_VAL_INIT(req2);
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response res2 = AUTO_VAL_INIT(res2);
req2.outs_count = req.outs_count - res.outs.size();
static const uint64_t amounts[] = {1, 10, 20, 50, 100, 200, 500, 1000, 10000};
for (uint64_t a: amounts)
req2.amounts.push_back(a);
if (!get_random_outs_for_amounts(req2, res2))
return false;
// pick random ones from there
while (res.outs.size() < req.outs_count)
{
int list_idx = rand() % (sizeof(amounts)/sizeof(amounts[0]));
if (!res2.outs[list_idx].outs.empty())
{
const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry oe = res2.outs[list_idx].outs.back();
res2.outs[list_idx].outs.pop_back();
add_out_to_get_rct_random_outs(res.outs, res2.outs[list_idx].amount, oe.global_amount_index);
}
}
}
#endif
return true;
}
//------------------------------------------------------------------
bool Blockchain::get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res) const
{

View File

@ -480,16 +480,6 @@ namespace cryptonote
*/
uint64_t get_num_mature_outputs(uint64_t amount) const;
/**
* @brief get random outputs (indices) for an amount
*
* @param amount the amount
* @param count the number of random outputs to choose
*
* @return the outputs' amount-global indices
*/
std::vector<uint64_t> get_random_outputs(uint64_t amount, uint64_t count) const;
/**
* @brief get the public key for an output
*
@ -500,22 +490,6 @@ namespace cryptonote
*/
crypto::public_key get_output_key(uint64_t amount, uint64_t global_index) const;
/**
* @brief gets random outputs to mix with
*
* This function takes an RPC request for outputs to mix with
* and creates an RPC response with the resultant output indices.
*
* Outputs to mix with are randomly selected from the utxo set
* for each output amount in the request.
*
* @param req the output amounts and number of mixins to select
* @param res return-by-reference the resultant output indices
*
* @return true
*/
bool get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) const;
/**
* @brief gets specific outputs to mix with
*
@ -542,23 +516,6 @@ namespace cryptonote
*/
void get_output_key_mask_unlocked(const uint64_t& amount, const uint64_t& index, crypto::public_key& key, rct::key& mask, bool& unlocked) const;
/**
* @brief gets random ringct outputs to mix with
*
* This function takes an RPC request for outputs to mix with
* and creates an RPC response with the resultant output indices
* and the matching keys.
*
* Outputs to mix with are randomly selected from the utxo set
* for each output amount in the request.
*
* @param req the output amounts and number of mixins to select
* @param res return-by-reference the resultant output indices
*
* @return true
*/
bool get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const;
/**
* @brief gets per block distribution of outputs of a given amount
*
@ -1327,25 +1284,7 @@ namespace cryptonote
void get_last_n_blocks_weights(std::vector<size_t>& weights, size_t count) const;
/**
* @brief adds the given output to the requested set of random outputs
*
* @param result_outs return-by-reference the set the output is to be added to
* @param amount the output amount
* @param i the output index (indexed to amount)
*/
void add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) const;
/**
* @brief adds the given output to the requested set of random ringct outputs
*
* @param outs return-by-reference the set the output is to be added to
* @param amount the output amount (0 for rct inputs)
* @param i the rct output index
*/
void add_out_to_get_rct_random_outs(std::list<COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry>& outs, uint64_t amount, size_t i) const;
/**
* @brief checks if an output is unlocked (spendable)
* @brief checks if a transaction is unlocked (its outputs spendable)
*
* This function checks to see if an output is unlocked.
* unlock_time is either a block index or a unix time.

View File

@ -1294,21 +1294,11 @@ namespace cryptonote
return m_blockchain_storage.find_blockchain_supplement(req_start_block, qblock_ids, blocks, total_height, start_height, pruned, get_miner_tx_hash, max_count);
}
//-----------------------------------------------------------------------------------------------
bool core::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) const
{
return m_blockchain_storage.get_random_outs_for_amounts(req, res);
}
//-----------------------------------------------------------------------------------------------
bool core::get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res) const
{
return m_blockchain_storage.get_outs(req, res);
}
//-----------------------------------------------------------------------------------------------
bool core::get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const
{
return m_blockchain_storage.get_random_rct_outs(req, res);
}
//-----------------------------------------------------------------------------------------------
bool core::get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t to_height, uint64_t &start_height, std::vector<uint64_t> &distribution, uint64_t &base) const
{
return m_blockchain_storage.get_output_distribution(amount, from_height, to_height, start_height, distribution, base);

View File

@ -566,13 +566,6 @@ namespace cryptonote
*/
difficulty_type get_block_cumulative_difficulty(uint64_t height) const;
/**
* @copydoc Blockchain::get_random_outs_for_amounts
*
* @note see Blockchain::get_random_outs_for_amounts
*/
bool get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) const;
/**
* @copydoc Blockchain::get_outs
*
@ -580,14 +573,6 @@ namespace cryptonote
*/
bool get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res) const;
/**
*
* @copydoc Blockchain::get_random_rct_outs
*
* @note see Blockchain::get_random_rct_outs
*/
bool get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const;
/**
* @copydoc Blockchain::get_output_distribution
*

View File

@ -225,7 +225,7 @@ namespace cryptonote
}
size_t tx_weight_limit = get_transaction_weight_limit(version);
if (!kept_by_block && tx_weight > tx_weight_limit)
if ((!kept_by_block || version >= HF_VERSION_PER_BYTE_FEE) && tx_weight > tx_weight_limit)
{
LOG_PRINT_L1("transaction is too heavy: " << tx_weight << " bytes, maximum weight: " << tx_weight_limit);
tvc.m_verifivation_failed = true;

View File

@ -44,6 +44,15 @@ using namespace epee;
using namespace cryptonote;
static std::string extra_nonce_to_string(const cryptonote::tx_extra_nonce &extra_nonce)
{
if (extra_nonce.nonce.size() == 9 && extra_nonce.nonce[0] == TX_EXTRA_NONCE_ENCRYPTED_PAYMENT_ID)
return "encrypted payment ID: " + epee::string_tools::buff_to_hex_nodelimer(extra_nonce.nonce.substr(1));
if (extra_nonce.nonce.size() == 33 && extra_nonce.nonce[0] == TX_EXTRA_NONCE_PAYMENT_ID)
return "plaintext payment ID: " + epee::string_tools::buff_to_hex_nodelimer(extra_nonce.nonce.substr(1));
return epee::string_tools::buff_to_hex_nodelimer(extra_nonce.nonce);
}
static void print_extra_fields(const std::vector<cryptonote::tx_extra_field> &fields)
{
std::cout << "tx_extra has " << fields.size() << " field(s)" << std::endl;
@ -52,7 +61,7 @@ static void print_extra_fields(const std::vector<cryptonote::tx_extra_field> &fi
std::cout << "field " << n << ": ";
if (typeid(cryptonote::tx_extra_padding) == fields[n].type()) std::cout << "extra padding: " << boost::get<cryptonote::tx_extra_padding>(fields[n]).size << " bytes";
else if (typeid(cryptonote::tx_extra_pub_key) == fields[n].type()) std::cout << "extra pub key: " << boost::get<cryptonote::tx_extra_pub_key>(fields[n]).pub_key;
else if (typeid(cryptonote::tx_extra_nonce) == fields[n].type()) std::cout << "extra nonce: " << epee::string_tools::buff_to_hex_nodelimer(boost::get<cryptonote::tx_extra_nonce>(fields[n]).nonce);
else if (typeid(cryptonote::tx_extra_nonce) == fields[n].type()) std::cout << "extra nonce: " << extra_nonce_to_string(boost::get<cryptonote::tx_extra_nonce>(fields[n]));
else if (typeid(cryptonote::tx_extra_merge_mining_tag) == fields[n].type()) std::cout << "extra merge mining tag: depth " << boost::get<cryptonote::tx_extra_merge_mining_tag>(fields[n]).depth << ", merkle root " << boost::get<cryptonote::tx_extra_merge_mining_tag>(fields[n]).merkle_root;
else if (typeid(cryptonote::tx_extra_additional_pub_keys) == fields[n].type()) std::cout << "additional tx pubkeys: " << boost::join(boost::get<cryptonote::tx_extra_additional_pub_keys>(fields[n]).data | boost::adaptors::transformed([](const crypto::public_key &key){ return epee::string_tools::pod_to_hex(key); }), ", " );
else if (typeid(cryptonote::tx_extra_mysterious_minergate) == fields[n].type()) std::cout << "extra minergate custom: " << epee::string_tools::buff_to_hex_nodelimer(boost::get<cryptonote::tx_extra_mysterious_minergate>(fields[n]).data);
@ -169,9 +178,14 @@ int main(int argc, char* argv[])
std::cout << "Parsed block:" << std::endl;
std::cout << cryptonote::obj_to_json_str(block) << std::endl;
}
else if (cryptonote::parse_and_validate_tx_from_blob(blob, tx))
else if (cryptonote::parse_and_validate_tx_from_blob(blob, tx) || cryptonote::parse_and_validate_tx_base_from_blob(blob, tx))
{
std::cout << "Parsed transaction:" << std::endl;
/*
if (tx.pruned)
std::cout << "Parsed pruned transaction:" << std::endl;
else
*/
std::cout << "Parsed transaction:" << std::endl;
std::cout << cryptonote::obj_to_json_str(tx) << std::endl;
bool parsed = cryptonote::parse_tx_extra(tx.extra, fields);

View File

@ -1355,7 +1355,7 @@ namespace hw {
this->exchange();
//pseudoOuts
if ((type == rct::RCTTypeSimple) || (type == rct::RCTTypeBulletproof)) {
if (type == rct::RCTTypeSimple) {
for ( i = 0; i < inputs_size; i++) {
offset = set_command_header(INS_VALIDATE, 0x01, i+2);
//options

View File

@ -355,49 +355,6 @@ namespace cryptonote
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res)
{
PERF_TIMER(on_get_random_outs);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS>(invoke_http_mode::BIN, "/getrandom_outs.bin", req, res, r))
return r;
res.status = "Failed";
if (m_restricted)
{
if (req.amounts.size() > 100 || req.outs_count > MAX_RESTRICTED_FAKE_OUTS_COUNT)
{
res.status = "Too many outs requested";
return true;
}
}
if(!m_core.get_random_outs_for_amounts(req, res))
{
return true;
}
res.status = CORE_RPC_STATUS_OK;
std::stringstream ss;
typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount outs_for_amount;
typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry out_entry;
std::for_each(res.outs.begin(), res.outs.end(), [&](outs_for_amount& ofa)
{
ss << "[" << ofa.amount << "]:";
CHECK_AND_ASSERT_MES(ofa.outs.size(), ;, "internal error: ofa.outs.size() is empty for amount " << ofa.amount);
std::for_each(ofa.outs.begin(), ofa.outs.end(), [&](out_entry& oe)
{
ss << oe.global_amount_index << " ";
});
ss << ENDL;
});
std::string s = ss.str();
LOG_PRINT_L2("COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS: " << ENDL << s);
res.status = CORE_RPC_STATUS_OK;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_outs_bin(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res)
{
PERF_TIMER(on_get_outs_bin);
@ -467,34 +424,6 @@ namespace cryptonote
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res)
{
PERF_TIMER(on_get_random_rct_outs);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS>(invoke_http_mode::BIN, "/getrandom_rctouts.bin", req, res, r))
return r;
res.status = "Failed";
if(!m_core.get_random_rct_outs(req, res))
{
return true;
}
res.status = CORE_RPC_STATUS_OK;
std::stringstream ss;
typedef COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry out_entry;
CHECK_AND_ASSERT_MES(res.outs.size(), true, "internal error: res.outs.size() is empty");
std::for_each(res.outs.begin(), res.outs.end(), [&](out_entry& oe)
{
ss << oe.global_amount_index << " ";
});
ss << ENDL;
std::string s = ss.str();
LOG_PRINT_L2("COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS: " << ENDL << s);
res.status = CORE_RPC_STATUS_OK;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res)
{
PERF_TIMER(on_get_indexes);

View File

@ -87,11 +87,7 @@ namespace cryptonote
MAP_URI_AUTO_BIN2("/get_hashes.bin", on_get_hashes, COMMAND_RPC_GET_HASHES_FAST)
MAP_URI_AUTO_BIN2("/gethashes.bin", on_get_hashes, COMMAND_RPC_GET_HASHES_FAST)
MAP_URI_AUTO_BIN2("/get_o_indexes.bin", on_get_indexes, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES)
MAP_URI_AUTO_BIN2("/get_random_outs.bin", on_get_random_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS)
MAP_URI_AUTO_BIN2("/getrandom_outs.bin", on_get_random_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS)
MAP_URI_AUTO_BIN2("/get_outs.bin", on_get_outs_bin, COMMAND_RPC_GET_OUTPUTS_BIN)
MAP_URI_AUTO_BIN2("/get_random_rctouts.bin", on_get_random_rct_outs, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS)
MAP_URI_AUTO_BIN2("/getrandom_rctouts.bin", on_get_random_rct_outs, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS)
MAP_URI_AUTO_JON2("/get_transactions", on_get_transactions, COMMAND_RPC_GET_TRANSACTIONS)
MAP_URI_AUTO_JON2("/gettransactions", on_get_transactions, COMMAND_RPC_GET_TRANSACTIONS)
MAP_URI_AUTO_JON2("/get_alt_blocks_hashes", on_get_alt_blocks_hashes, COMMAND_RPC_GET_ALT_BLOCKS_HASHES)
@ -176,10 +172,8 @@ namespace cryptonote
bool on_start_mining(const COMMAND_RPC_START_MINING::request& req, COMMAND_RPC_START_MINING::response& res);
bool on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMMAND_RPC_STOP_MINING::response& res);
bool on_mining_status(const COMMAND_RPC_MINING_STATUS::request& req, COMMAND_RPC_MINING_STATUS::response& res);
bool on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res);
bool on_get_outs_bin(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res);
bool on_get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res);
bool on_get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res);
bool on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res);
bool on_save_bc(const COMMAND_RPC_SAVE_BC::request& req, COMMAND_RPC_SAVE_BC::response& res);
bool on_get_peer_list(const COMMAND_RPC_GET_PEER_LIST::request& req, COMMAND_RPC_GET_PEER_LIST::response& res);

View File

@ -683,50 +683,6 @@ namespace cryptonote
};
};
//-----------------------------------------------
struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS
{
struct request
{
std::vector<uint64_t> amounts;
uint64_t outs_count;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amounts)
KV_SERIALIZE(outs_count)
END_KV_SERIALIZE_MAP()
};
#pragma pack (push, 1)
struct out_entry
{
uint64_t global_amount_index;
crypto::public_key out_key;
};
#pragma pack(pop)
struct outs_for_amount
{
uint64_t amount;
std::list<out_entry> outs;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amount)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(outs)
END_KV_SERIALIZE_MAP()
};
struct response
{
std::vector<outs_for_amount> outs;
std::string status;
bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(outs)
KV_SERIALIZE(status)
KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
};
//-----------------------------------------------
struct get_outputs_out
{
uint64_t amount;
@ -821,39 +777,6 @@ namespace cryptonote
END_KV_SERIALIZE_MAP()
};
};
struct COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS
{
struct request
{
uint64_t outs_count;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(outs_count)
END_KV_SERIALIZE_MAP()
};
#pragma pack (push, 1)
struct out_entry
{
uint64_t amount;
uint64_t global_amount_index;
crypto::public_key out_key;
rct::key commitment;
};
#pragma pack(pop)
struct response
{
std::list<out_entry> outs;
std::string status;
bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(outs)
KV_SERIALIZE(status)
KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
};
//-----------------------------------------------
struct COMMAND_RPC_SEND_RAW_TX
{

View File

@ -260,44 +260,6 @@ namespace rpc
}
//TODO: handle "restricted" RPC
void DaemonHandler::handle(const GetRandomOutputsForAmounts::Request& req, GetRandomOutputsForAmounts::Response& res)
{
auto& chain = m_core.get_blockchain_storage();
try
{
for (const uint64_t& amount : req.amounts)
{
std::vector<uint64_t> indices = chain.get_random_outputs(amount, req.count);
outputs_for_amount ofa;
ofa.resize(indices.size());
for (size_t i = 0; i < indices.size(); i++)
{
crypto::public_key key = chain.get_output_key(amount, indices[i]);
ofa[i].amount_index = indices[i];
ofa[i].key = key;
}
amount_with_random_outputs amt;
amt.amount = amount;
amt.outputs = ofa;
res.amounts_with_outputs.push_back(amt);
}
res.status = Message::STATUS_OK;
}
catch (const std::exception& e)
{
res.status = Message::STATUS_FAILED;
res.error_details = e.what();
}
}
void DaemonHandler::handle(const SendRawTx::Request& req, SendRawTx::Response& res)
{
auto tx_blob = cryptonote::tx_to_blob(req.tx);
@ -829,7 +791,6 @@ namespace rpc
REQ_RESP_TYPES_MACRO(request_type, GetTransactions, req_json, resp_message, handle);
REQ_RESP_TYPES_MACRO(request_type, KeyImagesSpent, req_json, resp_message, handle);
REQ_RESP_TYPES_MACRO(request_type, GetTxGlobalOutputIndices, req_json, resp_message, handle);
REQ_RESP_TYPES_MACRO(request_type, GetRandomOutputsForAmounts, req_json, resp_message, handle);
REQ_RESP_TYPES_MACRO(request_type, SendRawTx, req_json, resp_message, handle);
REQ_RESP_TYPES_MACRO(request_type, GetInfo, req_json, resp_message, handle);
REQ_RESP_TYPES_MACRO(request_type, StartMining, req_json, resp_message, handle);

View File

@ -66,8 +66,6 @@ class DaemonHandler : public RpcHandler
void handle(const GetTxGlobalOutputIndices::Request& req, GetTxGlobalOutputIndices::Response& res);
void handle(const GetRandomOutputsForAmounts::Request& req, GetRandomOutputsForAmounts::Response& res);
void handle(const SendRawTx::Request& req, SendRawTx::Response& res);
void handle(const StartMining::Request& req, StartMining::Response& res);

View File

@ -41,7 +41,6 @@ const char* const GetHashesFast::name = "get_hashes_fast";
const char* const GetTransactions::name = "get_transactions";
const char* const KeyImagesSpent::name = "key_images_spent";
const char* const GetTxGlobalOutputIndices::name = "get_tx_global_output_indices";
const char* const GetRandomOutputsForAmounts::name = "get_random_outputs_for_amounts";
const char* const SendRawTx::name = "send_raw_tx";
const char* const StartMining::name = "start_mining";
const char* const StopMining::name = "stop_mining";
@ -273,42 +272,6 @@ void GetTxGlobalOutputIndices::Response::fromJson(rapidjson::Value& val)
GET_FROM_JSON_OBJECT(val, output_indices, output_indices);
}
rapidjson::Value GetRandomOutputsForAmounts::Request::toJson(rapidjson::Document& doc) const
{
auto val = Message::toJson(doc);
auto& al = doc.GetAllocator();
INSERT_INTO_JSON_OBJECT(val, doc, amounts, amounts);
INSERT_INTO_JSON_OBJECT(val, doc, count, count);
return val;
}
void GetRandomOutputsForAmounts::Request::fromJson(rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, amounts, amounts);
GET_FROM_JSON_OBJECT(val, count, count);
}
rapidjson::Value GetRandomOutputsForAmounts::Response::toJson(rapidjson::Document& doc) const
{
auto val = Message::toJson(doc);
auto& al = doc.GetAllocator();
INSERT_INTO_JSON_OBJECT(val, doc, amounts_with_outputs, amounts_with_outputs);
return val;
}
void GetRandomOutputsForAmounts::Response::fromJson(rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, amounts_with_outputs, amounts_with_outputs);
}
rapidjson::Value SendRawTx::Request::toJson(rapidjson::Document& doc) const
{
auto val = Message::toJson(doc);

View File

@ -458,7 +458,7 @@ namespace
LOG_ERROR("RPC error: " << e.to_string());
fail_msg_writer() << tr("RPC error: ") << e.what();
}
catch (const tools::error::get_random_outs_error &e)
catch (const tools::error::get_outs_error &e)
{
fail_msg_writer() << tr("failed to get random outputs to mix: ") << e.what();
}
@ -1640,23 +1640,23 @@ bool simple_wallet::set_ring(const std::vector<std::string> &args)
bool simple_wallet::blackball(const std::vector<std::string> &args)
{
crypto::public_key output;
uint64_t amount = std::numeric_limits<uint64_t>::max(), offset, num_offsets;
if (args.size() == 0)
{
fail_msg_writer() << tr("usage: blackball <output_public_key> | <filename> [add]");
fail_msg_writer() << tr("usage: blackball <amount>/<offset> | <filename> [add]");
return true;
}
try
{
if (epee::string_tools::hex_to_pod(args[0], output))
if (sscanf(args[0].c_str(), "%" PRIu64 "/%" PRIu64, &amount, &offset) == 2)
{
m_wallet->blackball_output(output);
m_wallet->blackball_output(std::make_pair(amount, offset));
}
else if (epee::file_io_utils::is_file_exist(args[0]))
{
std::vector<crypto::public_key> outputs;
char str[65];
std::vector<std::pair<uint64_t, uint64_t>> outputs;
char str[256];
std::unique_ptr<FILE, tools::close_file> f(fopen(args[0].c_str(), "r"));
if (f)
@ -1670,10 +1670,27 @@ bool simple_wallet::blackball(const std::vector<std::string> &args)
str[len - 1] = 0;
if (!str[0])
continue;
outputs.push_back(crypto::public_key());
if (!epee::string_tools::hex_to_pod(str, outputs.back()))
if (sscanf(str, "@%" PRIu64, &amount) == 1)
{
fail_msg_writer() << tr("Invalid public key: ") << str;
continue;
}
if (amount == std::numeric_limits<uint64_t>::max())
{
fail_msg_writer() << tr("First line is not an amount");
return true;
}
if (sscanf(str, "%" PRIu64 "*%" PRIu64, &offset, &num_offsets) == 2 && num_offsets <= std::numeric_limits<uint64_t>::max() - offset)
{
while (num_offsets--)
outputs.push_back(std::make_pair(amount, offset++));
}
else if (sscanf(str, "%" PRIu64, &offset) == 1)
{
outputs.push_back(std::make_pair(amount, offset));
}
else
{
fail_msg_writer() << tr("Invalid output: ") << str;
return true;
}
}
@ -1698,7 +1715,7 @@ bool simple_wallet::blackball(const std::vector<std::string> &args)
}
else
{
fail_msg_writer() << tr("Invalid public key, and file doesn't exist");
fail_msg_writer() << tr("Invalid output key, and file doesn't exist");
return true;
}
}
@ -1712,16 +1729,16 @@ bool simple_wallet::blackball(const std::vector<std::string> &args)
bool simple_wallet::unblackball(const std::vector<std::string> &args)
{
crypto::public_key output;
std::pair<uint64_t, uint64_t> output;
if (args.size() != 1)
{
fail_msg_writer() << tr("usage: unblackball <output_public_key>");
fail_msg_writer() << tr("usage: unblackball <amount>/<offset>");
return true;
}
if (!epee::string_tools::hex_to_pod(args[0], output))
if (sscanf(args[0].c_str(), "%" PRIu64 "/%" PRIu64, &output.first, &output.second) != 2)
{
fail_msg_writer() << tr("Invalid public key");
fail_msg_writer() << tr("Invalid output");
return true;
}
@ -1739,25 +1756,25 @@ bool simple_wallet::unblackball(const std::vector<std::string> &args)
bool simple_wallet::blackballed(const std::vector<std::string> &args)
{
crypto::public_key output;
std::pair<uint64_t, uint64_t> output;
if (args.size() != 1)
{
fail_msg_writer() << tr("usage: blackballed <output_public_key>");
fail_msg_writer() << tr("usage: blackballed <amount>/<offset>");
return true;
}
if (!epee::string_tools::hex_to_pod(args[0], output))
if (sscanf(args[0].c_str(), "%" PRIu64 "/%" PRIu64, &output.first, &output.second) != 2)
{
fail_msg_writer() << tr("Invalid public key");
fail_msg_writer() << tr("Invalid output");
return true;
}
try
{
if (m_wallet->is_output_blackballed(output))
message_writer() << tr("Blackballed: ") << output;
message_writer() << tr("Blackballed: ") << output.first << "/" << output.second;
else
message_writer() << tr("not blackballed: ") << output;
message_writer() << tr("not blackballed: ") << output.first << "/" << output.second;
}
catch (const std::exception &e)
{
@ -2236,15 +2253,15 @@ simple_wallet::simple_wallet()
tr("Show the blockchain height."));
m_cmd_binder.set_handler("transfer_original",
boost::bind(&simple_wallet::transfer, this, _1),
tr("transfer_original [index=<N1>[,<N2>,...]] [<priority>] <address> <amount> [<payment_id>]"),
tr("Transfer <amount> to <address> using an older transaction building algorithm. If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. Multiple payments can be made at once by adding <address_2> <amount_2> etcetera (before the payment ID, if it's included)"));
tr("transfer_original [index=<N1>[,<N2>,...]] [<priority>] (<URI> | <address> <amount>) [<payment_id>]"),
tr("Transfer <amount> to <address> using an older transaction building algorithm. If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. Multiple payments can be made at once by adding URI_2 or <address_2> <amount_2> etcetera (before the payment ID, if it's included)"));
m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer_new, this, _1),
tr("transfer [index=<N1>[,<N2>,...]] [<priority>] <address> <amount> [<payment_id>]"),
tr("Transfer <amount> to <address>. If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. Multiple payments can be made at once by adding <address_2> <amount_2> etcetera (before the payment ID, if it's included)"));
m_cmd_binder.set_handler("locked_transfer",
boost::bind(&simple_wallet::locked_transfer, this, _1),
tr("locked_transfer [index=<N1>[,<N2>,...]] [<priority>] <addr> <amount> <lockblocks> [<payment_id>]"),
tr("Transfer <amount> to <address> and lock it for <lockblocks> (max. 1000000). If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. Multiple payments can be made at once by adding <address_2> <amount_2> etcetera (before the payment ID, if it's included)"));
tr("locked_transfer [index=<N1>[,<N2>,...]] [<priority>] (<URI> | <addr> <amount>) <lockblocks> [<payment_id>]"),
tr("Transfer <amount> to <address> and lock it for <lockblocks> (max. 1000000). If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. Multiple payments can be made at once by adding URI_2 or <address_2> <amount_2> etcetera (before the payment ID, if it's included)"));
m_cmd_binder.set_handler("locked_sweep_all",
boost::bind(&simple_wallet::locked_sweep_all, this, _1),
tr("locked_sweep_all [index=<N1>[,<N2>,...]] [<priority>] <address> <lockblocks> [<payment_id>]"),
@ -2534,15 +2551,15 @@ simple_wallet::simple_wallet()
tr("Save known rings to the shared rings database"));
m_cmd_binder.set_handler("blackball",
boost::bind(&simple_wallet::blackball, this, _1),
tr("blackball <output public key> | <filename> [add]"),
tr("blackball <amount>/<offset> | <filename> [add]"),
tr("Blackball output(s) so they never get selected as fake outputs in a ring"));
m_cmd_binder.set_handler("unblackball",
boost::bind(&simple_wallet::unblackball, this, _1),
tr("unblackball <output public key>"),
tr("unblackball <amount>/<offset>"),
tr("Unblackballs an output so it may get selected as a fake output in a ring"));
m_cmd_binder.set_handler("blackballed",
boost::bind(&simple_wallet::blackballed, this, _1),
tr("blackballed <output public key>"),
tr("blackballed <amount>/<offset>"),
tr("Checks whether an output is blackballed"));
m_cmd_binder.set_handler("version",
boost::bind(&simple_wallet::version, this, _1),
@ -4678,7 +4695,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
priority = m_wallet->adjust_priority(priority);
const size_t min_args = (transfer_type == TransferLocked) ? 3 : 2;
const size_t min_args = (transfer_type == TransferLocked) ? 2 : 1;
if(local_args.size() < min_args)
{
fail_msg_writer() << tr("wrong number of arguments");
@ -4687,39 +4704,38 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
std::vector<uint8_t> extra;
bool payment_id_seen = false;
bool expect_even = (transfer_type == TransferLocked);
if ((expect_even ? 0 : 1) == local_args.size() % 2)
if (!local_args.empty())
{
std::string payment_id_str = local_args.back();
local_args.pop_back();
crypto::hash payment_id;
bool r = tools::wallet2::parse_long_payment_id(payment_id_str, payment_id);
if(r)
bool r = true;
if (tools::wallet2::parse_long_payment_id(payment_id_str, payment_id))
{
std::string extra_nonce;
set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
r = add_extra_nonce_to_tx_extra(extra, extra_nonce);
local_args.pop_back();
payment_id_seen = true;
message_writer() << tr("Unencrypted payment IDs are bad for privacy: ask the recipient to use subaddresses instead");
}
else
{
crypto::hash8 payment_id8;
r = tools::wallet2::parse_short_payment_id(payment_id_str, payment_id8);
if(r)
if (tools::wallet2::parse_short_payment_id(payment_id_str, payment_id8))
{
std::string extra_nonce;
set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id8);
r = add_extra_nonce_to_tx_extra(extra, extra_nonce);
local_args.pop_back();
payment_id_seen = true;
}
}
if(!r)
{
fail_msg_writer() << tr("payment id has invalid format, expected 16 or 64 character hex string: ") << payment_id_str;
fail_msg_writer() << tr("payment id failed to encode");
return true;
}
payment_id_seen = true;
message_writer() << tr("Unencrypted payment IDs are bad for privacy: ask the recipient to use subaddresses instead");
}
uint64_t locked_blocks = 0;
@ -4734,11 +4750,54 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
vector<cryptonote::tx_destination_entry> dsts;
size_t num_subaddresses = 0;
for (size_t i = 0; i < local_args.size(); i += 2)
for (size_t i = 0; i < local_args.size(); )
{
cryptonote::address_parse_info info;
cryptonote::tx_destination_entry de;
if (!cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), local_args[i], oa_prompter))
cryptonote::address_parse_info info;
bool r = true;
// check for a URI
std::string address_uri, payment_id_uri, tx_description, recipient_name, error;
std::vector<std::string> unknown_parameters;
uint64_t amount = 0;
bool has_uri = m_wallet->parse_uri(local_args[i], address_uri, payment_id_uri, amount, tx_description, recipient_name, unknown_parameters, error);
if (has_uri)
{
r = cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), address_uri, oa_prompter);
if (payment_id_uri.size() == 16)
{
if (!tools::wallet2::parse_short_payment_id(payment_id_uri, info.payment_id))
{
fail_msg_writer() << tr("failed to parse short payment ID from URI");
return true;
}
info.has_payment_id = true;
}
de.amount = amount;
++i;
}
else if (i + 1 < local_args.size())
{
r = cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), local_args[i], oa_prompter);
bool ok = cryptonote::parse_amount(de.amount, local_args[i + 1]);
if(!ok || 0 == de.amount)
{
fail_msg_writer() << tr("amount is wrong: ") << local_args[i] << ' ' << local_args[i + 1] <<
", " << tr("expected number from 0 to ") << print_money(std::numeric_limits<uint64_t>::max());
return true;
}
i += 2;
}
else
{
if (boost::starts_with(local_args[i], "monero:"))
fail_msg_writer() << tr("Invalid last argument: ") << local_args.back() << ": " << error;
else
fail_msg_writer() << tr("Invalid last argument: ") << local_args.back();
return true;
}
if (!r)
{
fail_msg_writer() << tr("failed to parse address");
return true;
@ -4747,16 +4806,30 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
de.is_subaddress = info.is_subaddress;
num_subaddresses += info.is_subaddress;
if (info.has_payment_id)
if (info.has_payment_id || !payment_id_uri.empty())
{
if (payment_id_seen)
{
fail_msg_writer() << tr("a single transaction cannot use more than one payment id: ") << local_args[i];
fail_msg_writer() << tr("a single transaction cannot use more than one payment id");
return true;
}
crypto::hash payment_id;
std::string extra_nonce;
set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, info.payment_id);
if (info.has_payment_id)
{
set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, info.payment_id);
}
else if (tools::wallet2::parse_payment_id(payment_id_uri, payment_id))
{
set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
message_writer() << tr("Unencrypted payment IDs are bad for privacy: ask the recipient to use subaddresses instead");
}
else
{
fail_msg_writer() << tr("failed to parse payment id, though it was detected");
return true;
}
bool r = add_extra_nonce_to_tx_extra(extra, extra_nonce);
if(!r)
{
@ -4766,14 +4839,6 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
payment_id_seen = true;
}
bool ok = cryptonote::parse_amount(de.amount, local_args[i + 1]);
if(!ok || 0 == de.amount)
{
fail_msg_writer() << tr("amount is wrong: ") << local_args[i] << ' ' << local_args[i + 1] <<
", " << tr("expected number from 0 to ") << print_money(std::numeric_limits<uint64_t>::max());
return true;
}
dsts.push_back(de);
}
@ -4809,15 +4874,12 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
unlock_block = bc_height + locked_blocks;
ptx_vector = m_wallet->create_transactions_2(dsts, CRYPTONOTE_TX_DEFAULT_MIX, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices);
break;
default:
LOG_ERROR("Unknown transfer method, using default");
/* FALLTHRU */
case TransferNew:
ptx_vector = m_wallet->create_transactions_2(dsts, CRYPTONOTE_TX_DEFAULT_MIX, 0 /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices);
break;
default:
LOG_ERROR("Unknown transfer method, using original");
/* FALLTHRU */
case TransferOriginal:
ptx_vector = m_wallet->create_transactions(dsts, CRYPTONOTE_TX_DEFAULT_MIX, 0 /* unlock_time */, priority, extra);
break;
}
if (ptx_vector.empty())

View File

@ -1382,8 +1382,8 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
setStatusError(tr("no connection to daemon. Please make sure daemon is running."));
} catch (const tools::error::wallet_rpc_error& e) {
setStatusError(tr("RPC error: ") + e.to_string());
} catch (const tools::error::get_random_outs_error &e) {
setStatusError((boost::format(tr("failed to get random outputs to mix: %s")) % e.what()).str());
} catch (const tools::error::get_outs_error &e) {
setStatusError((boost::format(tr("failed to get outputs to mix: %s")) % e.what()).str());
} catch (const tools::error::not_enough_unlocked_money& e) {
std::ostringstream writer;
@ -1464,8 +1464,8 @@ PendingTransaction *WalletImpl::createSweepUnmixableTransaction()
setStatusError(tr("no connection to daemon. Please make sure daemon is running."));
} catch (const tools::error::wallet_rpc_error& e) {
setStatusError(tr("RPC error: ") + e.to_string());
} catch (const tools::error::get_random_outs_error&) {
setStatusError(tr("failed to get random outputs to mix"));
} catch (const tools::error::get_outs_error&) {
setStatusError(tr("failed to get outputs to mix"));
} catch (const tools::error::not_enough_unlocked_money& e) {
setStatusError("");
std::ostringstream writer;

View File

@ -56,6 +56,13 @@ static int compare_hash32(const MDB_val *a, const MDB_val *b)
return 0;
}
static int compare_uint64(const MDB_val *a, const MDB_val *b)
{
const uint64_t va = *(const uint64_t*) a->mv_data;
const uint64_t vb = *(const uint64_t*) b->mv_data;
return va < vb ? -1 : va > vb;
}
static std::string compress_ring(const std::vector<uint64_t> &ring)
{
std::string s;
@ -147,7 +154,7 @@ static int resize_env(MDB_env *env, const char *db_path, size_t needed)
MDB_stat mst;
int ret;
needed = std::max(needed, (size_t)(2ul * 1024 * 1024)); // at least 2 MB
needed = std::max(needed, (size_t)(100ul * 1024 * 1024)); // at least 100 MB
ret = mdb_env_info(env, &mei);
if (ret)
@ -218,9 +225,9 @@ ringdb::ringdb(std::string filename, const std::string &genesis):
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr)));
mdb_set_compare(txn, dbi_rings, compare_hash32);
dbr = mdb_dbi_open(txn, ("blackballs-" + genesis).c_str(), MDB_CREATE | MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED, &dbi_blackballs);
dbr = mdb_dbi_open(txn, ("blackballs2-" + genesis).c_str(), MDB_CREATE | MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED, &dbi_blackballs);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr)));
mdb_set_dupsort(txn, dbi_blackballs, compare_hash32);
mdb_set_dupsort(txn, dbi_blackballs, compare_uint64);
dbr = mdb_txn_commit(txn);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to commit txn creating/opening database: " + std::string(mdb_strerror(dbr)));
@ -375,7 +382,7 @@ bool ringdb::set_ring(const crypto::chacha_key &chacha_key, const crypto::key_im
return true;
}
bool ringdb::blackball_worker(const crypto::public_key &output, int op)
bool ringdb::blackball_worker(const std::vector<std::pair<uint64_t, uint64_t>> &outputs, int op)
{
MDB_txn *txn;
MDB_cursor *cursor;
@ -383,49 +390,61 @@ bool ringdb::blackball_worker(const crypto::public_key &output, int op)
bool tx_active = false;
bool ret = true;
dbr = resize_env(env, filename.c_str(), 32 * 2); // a pubkey, and some slack
THROW_WALLET_EXCEPTION_IF(outputs.size() > 1 && op == BLACKBALL_QUERY, tools::error::wallet_internal_error, "Blackball query only makes sense for a single output");
dbr = resize_env(env, filename.c_str(), 32 * 2 * outputs.size()); // a pubkey, and some slack
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to set env map size: " + std::string(mdb_strerror(dbr)));
dbr = mdb_txn_begin(env, NULL, 0, &txn);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
epee::misc_utils::auto_scope_leave_caller txn_dtor = epee::misc_utils::create_scope_leave_handler([&](){if (tx_active) mdb_txn_abort(txn);});
tx_active = true;
MDB_val key = zerokeyval;
MDB_val data;
data.mv_data = (void*)&output;
data.mv_size = sizeof(output);
switch (op)
MDB_val key, data;
for (const std::pair<uint64_t, uint64_t> &output: outputs)
{
case BLACKBALL_BLACKBALL:
MDEBUG("Blackballing output " << output);
dbr = mdb_put(txn, dbi_blackballs, &key, &data, MDB_NODUPDATA);
if (dbr == MDB_KEYEXIST)
dbr = 0;
break;
case BLACKBALL_UNBLACKBALL:
MDEBUG("Unblackballing output " << output);
dbr = mdb_del(txn, dbi_blackballs, &key, &data);
if (dbr == MDB_NOTFOUND)
dbr = 0;
break;
case BLACKBALL_QUERY:
dbr = mdb_cursor_open(txn, dbi_blackballs, &cursor);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to create cursor for blackballs table: " + std::string(mdb_strerror(dbr)));
dbr = mdb_cursor_get(cursor, &key, &data, MDB_GET_BOTH);
THROW_WALLET_EXCEPTION_IF(dbr && dbr != MDB_NOTFOUND, tools::error::wallet_internal_error, "Failed to lookup in blackballs table: " + std::string(mdb_strerror(dbr)));
ret = dbr != MDB_NOTFOUND;
if (dbr == MDB_NOTFOUND)
dbr = 0;
mdb_cursor_close(cursor);
break;
case BLACKBALL_CLEAR:
dbr = mdb_drop(txn, dbi_blackballs, 0);
break;
default:
THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, "Invalid blackball op");
key.mv_data = (void*)&output.first;
key.mv_size = sizeof(output.first);
data.mv_data = (void*)&output.second;
data.mv_size = sizeof(output.second);
switch (op)
{
case BLACKBALL_BLACKBALL:
MDEBUG("Blackballing output " << output.first << "/" << output.second);
dbr = mdb_put(txn, dbi_blackballs, &key, &data, MDB_APPENDDUP);
if (dbr == MDB_KEYEXIST)
dbr = 0;
break;
case BLACKBALL_UNBLACKBALL:
MDEBUG("Unblackballing output " << output.first << "/" << output.second);
dbr = mdb_del(txn, dbi_blackballs, &key, &data);
if (dbr == MDB_NOTFOUND)
dbr = 0;
break;
case BLACKBALL_QUERY:
dbr = mdb_cursor_open(txn, dbi_blackballs, &cursor);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to create cursor for blackballs table: " + std::string(mdb_strerror(dbr)));
dbr = mdb_cursor_get(cursor, &key, &data, MDB_GET_BOTH);
THROW_WALLET_EXCEPTION_IF(dbr && dbr != MDB_NOTFOUND, tools::error::wallet_internal_error, "Failed to lookup in blackballs table: " + std::string(mdb_strerror(dbr)));
ret = dbr != MDB_NOTFOUND;
if (dbr == MDB_NOTFOUND)
dbr = 0;
mdb_cursor_close(cursor);
break;
case BLACKBALL_CLEAR:
break;
default:
THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, "Invalid blackball op");
}
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to query blackballs table: " + std::string(mdb_strerror(dbr)));
}
if (op == BLACKBALL_CLEAR)
{
dbr = mdb_drop(txn, dbi_blackballs, 0);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to clear blackballs table: " + std::string(mdb_strerror(dbr)));
}
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to query blackballs table: " + std::string(mdb_strerror(dbr)));
dbr = mdb_txn_commit(txn);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to commit txn blackballing output to database: " + std::string(mdb_strerror(dbr)));
@ -433,24 +452,32 @@ bool ringdb::blackball_worker(const crypto::public_key &output, int op)
return ret;
}
bool ringdb::blackball(const crypto::public_key &output)
bool ringdb::blackball(const std::vector<std::pair<uint64_t, uint64_t>> &outputs)
{
return blackball_worker(output, BLACKBALL_BLACKBALL);
return blackball_worker(outputs, BLACKBALL_BLACKBALL);
}
bool ringdb::unblackball(const crypto::public_key &output)
bool ringdb::blackball(const std::pair<uint64_t, uint64_t> &output)
{
return blackball_worker(output, BLACKBALL_UNBLACKBALL);
std::vector<std::pair<uint64_t, uint64_t>> outputs(1, output);
return blackball_worker(outputs, BLACKBALL_BLACKBALL);
}
bool ringdb::blackballed(const crypto::public_key &output)
bool ringdb::unblackball(const std::pair<uint64_t, uint64_t> &output)
{
return blackball_worker(output, BLACKBALL_QUERY);
std::vector<std::pair<uint64_t, uint64_t>> outputs(1, output);
return blackball_worker(outputs, BLACKBALL_UNBLACKBALL);
}
bool ringdb::blackballed(const std::pair<uint64_t, uint64_t> &output)
{
std::vector<std::pair<uint64_t, uint64_t>> outputs(1, output);
return blackball_worker(outputs, BLACKBALL_QUERY);
}
bool ringdb::clear_blackballs()
{
return blackball_worker(crypto::public_key(), BLACKBALL_CLEAR);
return blackball_worker(std::vector<std::pair<uint64_t, uint64_t>>(), BLACKBALL_CLEAR);
}
}

View File

@ -49,13 +49,14 @@ namespace tools
bool get_ring(const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, std::vector<uint64_t> &outs);
bool set_ring(const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, const std::vector<uint64_t> &outs, bool relative);
bool blackball(const crypto::public_key &output);
bool unblackball(const crypto::public_key &output);
bool blackballed(const crypto::public_key &output);
bool blackball(const std::pair<uint64_t, uint64_t> &output);
bool blackball(const std::vector<std::pair<uint64_t, uint64_t>> &outputs);
bool unblackball(const std::pair<uint64_t, uint64_t> &output);
bool blackballed(const std::pair<uint64_t, uint64_t> &output);
bool clear_blackballs();
private:
bool blackball_worker(const crypto::public_key &output, int op);
bool blackball_worker(const std::vector<std::pair<uint64_t, uint64_t>> &outputs, int op);
private:
std::string filename;

View File

@ -2156,9 +2156,11 @@ void wallet2::pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks
{
drop_from_short_history(short_chain_history, 3);
THROW_WALLET_EXCEPTION_IF(prev_blocks.size() != prev_parsed_blocks.size(), error::wallet_internal_error, "size mismatch");
// prepend the last 3 blocks, should be enough to guard against a block or two's reorg
std::vector<parsed_block>::const_reverse_iterator i = prev_parsed_blocks.rbegin();
for (size_t n = 0; n < std::min((size_t)3, prev_blocks.size()); ++n)
for (size_t n = 0; n < std::min((size_t)3, prev_parsed_blocks.size()); ++n)
{
short_chain_history.push_front(i->hash);
++i;
@ -2478,6 +2480,7 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height,
while (missing_blocks-- > 0)
m_blockchain.push_back(crypto::null_hash); // maybe a bit suboptimal, but deque won't do huge reallocs like vector
m_blockchain.push_back(m_checkpoints.get_points().at(checkpoint_height));
m_blockchain.trim(checkpoint_height);
short_chain_history.clear();
get_short_chain_history(short_chain_history);
}
@ -2689,10 +2692,6 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
break;
}
// switch to the new blocks from the daemon
blocks_start_height = next_blocks_start_height;
blocks = std::move(next_blocks);
parsed_blocks = std::move(next_parsed_blocks);
first = false;
// handle error from async fetching thread
@ -2700,6 +2699,11 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
{
throw std::runtime_error("proxy exception in refresh thread");
}
// switch to the new blocks from the daemon
blocks_start_height = next_blocks_start_height;
blocks = std::move(next_blocks);
parsed_blocks = std::move(next_parsed_blocks);
}
catch (const tools::error::password_needed&)
{
@ -5061,69 +5065,6 @@ void wallet2::add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t amo
}
}
//----------------------------------------------------------------------------------------------------
void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outs_count, const std::vector<size_t> &unused_transfers_indices,
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx& ptx)
{
transfer(dsts, fake_outs_count, unused_transfers_indices, unlock_time, fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), tx, ptx);
}
//----------------------------------------------------------------------------------------------------
void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outs_count, const std::vector<size_t> &unused_transfers_indices,
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra)
{
cryptonote::transaction tx;
pending_tx ptx;
transfer(dsts, fake_outs_count, unused_transfers_indices, unlock_time, fee, extra, tx, ptx);
}
namespace {
// split_amounts(vector<cryptonote::tx_destination_entry> dsts, size_t num_splits)
//
// split amount for each dst in dsts into num_splits parts
// and make num_splits new vector<crypt...> instances to hold these new amounts
std::vector<std::vector<cryptonote::tx_destination_entry>> split_amounts(
std::vector<cryptonote::tx_destination_entry> dsts, size_t num_splits)
{
std::vector<std::vector<cryptonote::tx_destination_entry>> retVal;
if (num_splits <= 1)
{
retVal.push_back(dsts);
return retVal;
}
// for each split required
for (size_t i=0; i < num_splits; i++)
{
std::vector<cryptonote::tx_destination_entry> new_dsts;
// for each destination
for (size_t j=0; j < dsts.size(); j++)
{
cryptonote::tx_destination_entry de;
uint64_t amount;
amount = dsts[j].amount;
amount = amount / num_splits;
// if last split, add remainder
if (i + 1 == num_splits)
{
amount += dsts[j].amount % num_splits;
}
de.addr = dsts[j].addr;
de.amount = amount;
new_dsts.push_back(de);
}
retVal.push_back(new_dsts);
}
return retVal;
}
} // anonymous namespace
//----------------------------------------------------------------------------------------------------
crypto::hash wallet2::get_payment_id(const pending_tx &ptx) const
{
@ -6012,26 +5953,6 @@ int wallet2::get_fee_algorithm() const
return 0;
}
//------------------------------------------------------------------------------------------------------------------------------
uint64_t wallet2::get_min_ring_size() const
{
if (use_fork_rules(8, 10))
return 11;
if (use_fork_rules(7, 10))
return 7;
if (use_fork_rules(6, 10))
return 5;
if (use_fork_rules(2, 10))
return 3;
return 0;
}
//------------------------------------------------------------------------------------------------------------------------------
uint64_t wallet2::get_max_ring_size() const
{
if (use_fork_rules(8, 10))
return 11;
return 0;
}
//------------------------------------------------------------------------------------------------------------------------------
uint64_t wallet2::adjust_mixin(uint64_t mixin) const
{
if (mixin != 9) {
@ -6117,116 +6038,6 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
return priority;
}
//----------------------------------------------------------------------------------------------------
// separated the call(s) to wallet2::transfer into their own function
//
// this function will make multiple calls to wallet2::transfer if multiple
// transactions will be required
std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra)
{
const std::vector<size_t> unused_transfers_indices = select_available_outputs_from_histogram(fake_outs_count + 1, true, true, true);
const uint64_t base_fee = get_base_fee();
const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE);
const uint64_t fee_quantization_mask = get_fee_quantization_mask();
// failsafe split attempt counter
size_t attempt_count = 0;
for(attempt_count = 1; ;attempt_count++)
{
size_t num_tx = 0.5 + pow(1.7,attempt_count-1);
auto split_values = split_amounts(dsts, num_tx);
// Throw if split_amounts comes back with a vector of size different than it should
if (split_values.size() != num_tx)
{
throw std::runtime_error("Splitting transactions returned a number of potential tx not equal to what was requested");
}
std::vector<pending_tx> ptx_vector;
try
{
// for each new destination vector (i.e. for each new tx)
for (auto & dst_vector : split_values)
{
cryptonote::transaction tx;
pending_tx ptx;
// loop until fee is met without increasing tx size to next KB boundary.
uint64_t needed_fee = estimate_fee(use_per_byte_fee, false, unused_transfers_indices.size(), fake_outs_count, dst_vector.size()+1, extra.size(), false, base_fee, fee_multiplier, fee_quantization_mask);
do
{
transfer(dst_vector, fake_outs_count, unused_transfers_indices, unlock_time, needed_fee, extra, tx, ptx);
auto txBlob = t_serializable_object_to_blob(ptx.tx);
needed_fee = calculate_fee(use_per_byte_fee, ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
} while (ptx.fee < needed_fee);
ptx_vector.push_back(ptx);
// mark transfers to be used as "spent"
for(size_t idx: ptx.selected_transfers)
{
set_spent(idx, 0);
}
}
// if we made it this far, we've selected our transactions. committing them will mark them spent,
// so this is a failsafe in case they don't go through
// unmark pending tx transfers as spent
for (auto & ptx : ptx_vector)
{
// mark transfers to be used as not spent
for(size_t idx2: ptx.selected_transfers)
{
set_unspent(idx2);
}
}
// if we made it this far, we're OK to actually send the transactions
return ptx_vector;
}
// only catch this here, other exceptions need to pass through to the calling function
catch (const tools::error::tx_too_big& e)
{
// unmark pending tx transfers as spent
for (auto & ptx : ptx_vector)
{
// mark transfers to be used as not spent
for(size_t idx2: ptx.selected_transfers)
{
set_unspent(idx2);
}
}
if (attempt_count >= MAX_SPLIT_ATTEMPTS)
{
throw;
}
}
catch (...)
{
// in case of some other exception, make sure any tx in queue are marked unspent again
// unmark pending tx transfers as spent
for (auto & ptx : ptx_vector)
{
// mark transfers to be used as not spent
for(size_t idx2: ptx.selected_transfers)
{
set_unspent(idx2);
}
}
throw;
}
}
}
bool wallet2::set_ring_database(const std::string &filename)
{
m_ring_database = filename;
@ -6399,7 +6210,7 @@ bool wallet2::find_and_save_rings(bool force)
return true;
}
bool wallet2::blackball_output(const crypto::public_key &output)
bool wallet2::blackball_output(const std::pair<uint64_t, uint64_t> &output)
{
if (!m_ringdb)
return false;
@ -6407,7 +6218,7 @@ bool wallet2::blackball_output(const crypto::public_key &output)
catch (const std::exception &e) { return false; }
}
bool wallet2::set_blackballed_outputs(const std::vector<crypto::public_key> &outputs, bool add)
bool wallet2::set_blackballed_outputs(const std::vector<std::pair<uint64_t, uint64_t>> &outputs, bool add)
{
if (!m_ringdb)
return false;
@ -6416,14 +6227,13 @@ bool wallet2::set_blackballed_outputs(const std::vector<crypto::public_key> &out
bool ret = true;
if (!add)
ret &= m_ringdb->clear_blackballs();
for (const auto &output: outputs)
ret &= m_ringdb->blackball(output);
ret &= m_ringdb->blackball(outputs);
return ret;
}
catch (const std::exception &e) { return false; }
}
bool wallet2::unblackball_output(const crypto::public_key &output)
bool wallet2::unblackball_output(const std::pair<uint64_t, uint64_t> &output)
{
if (!m_ringdb)
return false;
@ -6431,7 +6241,7 @@ bool wallet2::unblackball_output(const crypto::public_key &output)
catch (const std::exception &e) { return false; }
}
bool wallet2::is_output_blackballed(const crypto::public_key &output) const
bool wallet2::is_output_blackballed(const std::pair<uint64_t, uint64_t> &output) const
{
if (!m_ringdb)
return false;
@ -6476,8 +6286,8 @@ bool wallet2::tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_out
CHECK_AND_ASSERT_MES(!outs.empty(), false, "internal error: outs is empty");
if (std::find(outs.back().begin(), outs.back().end(), item) != outs.back().end()) // don't add duplicates
return false;
if (is_output_blackballed(output_public_key)) // don't add blackballed outputs
return false;
// if (is_output_blackballed(output_public_key)) // don't add blackballed outputs
// return false;
outs.back().push_back(item);
return true;
}
@ -6974,6 +6784,8 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
if (seen_indices.count(i))
continue;
if (is_output_blackballed(std::make_pair(amount, i))) // don't add blackballed outputs
continue;
seen_indices.emplace(i);
LOG_PRINT_L2("picking " << i << " as " << type);
@ -6996,7 +6808,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_outs.bin");
THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_outs.bin");
THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_random_outs_error, daemon_resp.status);
THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_outs_error, daemon_resp.status);
THROW_WALLET_EXCEPTION_IF(daemon_resp.outs.size() != req.outputs.size(), error::wallet_internal_error,
"daemon returned wrong response for get_outs.bin, wrong amounts count = " +
std::to_string(daemon_resp.outs.size()) + ", expected " + std::to_string(req.outputs.size()));

View File

@ -726,12 +726,6 @@ namespace tools
uint64_t balance_all() const;
uint64_t unlocked_balance_all() const;
template<typename T>
void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy);
template<typename T>
void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx& ptx);
void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra);
void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx& ptx);
template<typename T>
void transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx);
@ -760,7 +754,6 @@ namespace tools
bool parse_unsigned_tx_from_str(const std::string &unsigned_tx_st, unsigned_tx_set &exported_txs) const;
bool load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func = NULL);
bool parse_tx_from_str(const std::string &signed_tx_st, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set &)> accept_func);
std::vector<pending_tx> create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra);
std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, bool is_staking_tx=false); // pass subaddr_indices by value on purpose
std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, bool is_staking_tx=false);
std::vector<wallet2::pending_tx> create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra);
@ -1099,8 +1092,6 @@ namespace tools
uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm = -1) const;
uint64_t get_base_fee() const;
uint64_t get_fee_quantization_mask() const;
uint64_t get_min_ring_size() const;
uint64_t get_max_ring_size() const;
uint64_t adjust_mixin(uint64_t mixin) const;
uint32_t adjust_priority(uint32_t priority);
@ -1174,10 +1165,10 @@ namespace tools
bool set_ring(const crypto::key_image &key_image, const std::vector<uint64_t> &outs, bool relative);
bool find_and_save_rings(bool force = true);
bool blackball_output(const crypto::public_key &output);
bool set_blackballed_outputs(const std::vector<crypto::public_key> &outputs, bool add = false);
bool unblackball_output(const crypto::public_key &output);
bool is_output_blackballed(const crypto::public_key &output) const;
bool blackball_output(const std::pair<uint64_t, uint64_t> &output);
bool set_blackballed_outputs(const std::vector<std::pair<uint64_t, uint64_t>> &outputs, bool add = false);
bool unblackball_output(const std::pair<uint64_t, uint64_t> &output);
bool is_output_blackballed(const std::pair<uint64_t, uint64_t> &output) const;
bool lock_keys_file();
bool unlock_keys_file();
@ -1832,199 +1823,4 @@ namespace tools
//----------------------------------------------------------------------------------------------------
}
//----------------------------------------------------------------------------------------------------
template<typename T>
void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outs_count, const std::vector<size_t> &unused_transfers_indices,
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy)
{
pending_tx ptx;
cryptonote::transaction tx;
transfer(dsts, fake_outs_count, unused_transfers_indices, unlock_time, fee, extra, destination_split_strategy, dust_policy, tx, ptx);
}
template<typename T>
void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices,
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx)
{
using namespace cryptonote;
// throw if attempting a transaction with no destinations
THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination);
THROW_WALLET_EXCEPTION_IF(m_multisig, error::wallet_internal_error, "Multisig wallets cannot spend non rct outputs");
uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit();
uint64_t needed_money = fee;
// calculate total amount being sent to all destinations
// throw if total amount overflows uint64_t
for(auto& dt: dsts)
{
THROW_WALLET_EXCEPTION_IF(0 == dt.amount, error::zero_destination);
needed_money += dt.amount;
THROW_WALLET_EXCEPTION_IF(needed_money < dt.amount, error::tx_sum_overflow, dsts, fee, m_nettype);
}
// randomly select inputs for transaction
// throw if requested send amount is greater than (unlocked) amount available to send
std::vector<size_t> selected_transfers;
uint64_t found_money = select_transfers(needed_money, unused_transfers_indices, selected_transfers);
THROW_WALLET_EXCEPTION_IF(found_money < needed_money, error::not_enough_unlocked_money, found_money, needed_money - fee, fee);
uint32_t subaddr_account = m_transfers[*selected_transfers.begin()].m_subaddr_index.major;
for (auto i = ++selected_transfers.begin(); i != selected_transfers.end(); ++i)
THROW_WALLET_EXCEPTION_IF(subaddr_account != *i, error::wallet_internal_error, "the tx uses funds from multiple accounts");
typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry out_entry;
typedef cryptonote::tx_source_entry::output_entry tx_output_entry;
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
if(fake_outputs_count)
{
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request req = AUTO_VAL_INIT(req);
req.outs_count = fake_outputs_count + 1;// add one to make possible (if need) to skip real output key
for(size_t idx: selected_transfers)
{
const transfer_container::const_iterator it = m_transfers.begin() + idx;
THROW_WALLET_EXCEPTION_IF(it->m_tx.vout.size() <= it->m_internal_output_index, error::wallet_internal_error,
"m_internal_output_index = " + std::to_string(it->m_internal_output_index) +
" is greater or equal to outputs count = " + std::to_string(it->m_tx.vout.size()));
req.amounts.push_back(it->amount());
}
m_daemon_rpc_mutex.lock();
bool r = epee::net_utils::invoke_http_bin("/getrandom_outs.bin", req, daemon_resp, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getrandom_outs.bin");
THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getrandom_outs.bin");
THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_random_outs_error, daemon_resp.status);
THROW_WALLET_EXCEPTION_IF(daemon_resp.outs.size() != selected_transfers.size(), error::wallet_internal_error,
"daemon returned wrong response for getrandom_outs.bin, wrong amounts count = " +
std::to_string(daemon_resp.outs.size()) + ", expected " + std::to_string(selected_transfers.size()));
std::unordered_map<uint64_t, uint64_t> scanty_outs;
for(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& amount_outs: daemon_resp.outs)
{
if (amount_outs.outs.size() < fake_outputs_count)
{
scanty_outs[amount_outs.amount] = amount_outs.outs.size();
}
}
THROW_WALLET_EXCEPTION_IF(!scanty_outs.empty(), error::not_enough_outs_to_mix, scanty_outs, fake_outputs_count);
}
//prepare inputs
size_t i = 0;
std::vector<cryptonote::tx_source_entry> sources;
for(size_t idx: selected_transfers)
{
sources.resize(sources.size()+1);
cryptonote::tx_source_entry& src = sources.back();
const transfer_details& td = m_transfers[idx];
src.amount = td.amount();
src.rct = false;
//paste mixin transaction
if(daemon_resp.outs.size())
{
daemon_resp.outs[i].outs.sort([](const out_entry& a, const out_entry& b){return a.global_amount_index < b.global_amount_index;});
for(out_entry& daemon_oe: daemon_resp.outs[i].outs)
{
if(td.m_global_output_index == daemon_oe.global_amount_index)
continue;
tx_output_entry oe;
oe.first = daemon_oe.global_amount_index;
oe.second.dest = rct::pk2rct(daemon_oe.out_key);
oe.second.mask = rct::identity();
src.outputs.push_back(oe);
if(src.outputs.size() >= fake_outputs_count)
break;
}
}
//paste real transaction to the random index
auto it_to_insert = std::find_if(src.outputs.begin(), src.outputs.end(), [&](const tx_output_entry& a)
{
return a.first >= td.m_global_output_index;
});
//size_t real_index = src.outputs.size() ? (rand() % src.outputs.size() ):0;
tx_output_entry real_oe;
real_oe.first = td.m_global_output_index;
real_oe.second.dest = rct::pk2rct(boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key);
real_oe.second.mask = rct::identity();
auto interted_it = src.outputs.insert(it_to_insert, real_oe);
src.real_out_tx_key = get_tx_pub_key_from_extra(td.m_tx);
src.real_output = interted_it - src.outputs.begin();
src.real_output_in_tx_index = td.m_internal_output_index;
src.multisig_kLRki = rct::multisig_kLRki({rct::zero(), rct::zero(), rct::zero(), rct::zero()});
detail::print_source_entry(src);
++i;
}
cryptonote::tx_destination_entry change_dts = AUTO_VAL_INIT(change_dts);
if (needed_money < found_money)
{
change_dts.addr = get_subaddress({subaddr_account, 0});
change_dts.amount = found_money - needed_money;
}
std::vector<cryptonote::tx_destination_entry> splitted_dsts, dust_dsts;
uint64_t dust = 0;
destination_split_strategy(dsts, change_dts, dust_policy.dust_threshold, splitted_dsts, dust_dsts);
for(auto& d: dust_dsts) {
THROW_WALLET_EXCEPTION_IF(dust_policy.dust_threshold < d.amount, error::wallet_internal_error, "invalid dust value: dust = " +
std::to_string(d.amount) + ", dust_threshold = " + std::to_string(dust_policy.dust_threshold));
}
for(auto& d: dust_dsts) {
if (!dust_policy.add_to_fee)
splitted_dsts.push_back(cryptonote::tx_destination_entry(d.amount, dust_policy.addr_for_dust, d.is_subaddress));
dust += d.amount;
}
crypto::secret_key tx_key;
std::vector<crypto::secret_key> additional_tx_keys;
rct::multisig_out msout;
bool per_output_unlock = use_fork_rules(9, 10);
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts, extra, tx, unlock_time, tx_key, additional_tx_keys, false, rct::RangeProofBorromean, m_multisig ? &msout : NULL, per_output_unlock);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype);
THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit);
std::string key_images;
bool all_are_txin_to_key = std::all_of(tx.vin.begin(), tx.vin.end(), [&](const txin_v& s_e) -> bool
{
CHECKED_GET_SPECIFIC_VARIANT(s_e, const txin_to_key, in, false);
key_images += boost::to_string(in.k_image) + " ";
return true;
});
THROW_WALLET_EXCEPTION_IF(!all_are_txin_to_key, error::unexpected_txin_type, tx);
bool dust_sent_elsewhere = (dust_policy.addr_for_dust.m_view_public_key != change_dts.addr.m_view_public_key
|| dust_policy.addr_for_dust.m_spend_public_key != change_dts.addr.m_spend_public_key);
if (dust_policy.add_to_fee || dust_sent_elsewhere) change_dts.amount -= dust;
ptx.key_images = key_images;
ptx.fee = (dust_policy.add_to_fee ? fee+dust : fee);
ptx.dust = ((dust_policy.add_to_fee || dust_sent_elsewhere) ? dust : 0);
ptx.dust_added_to_fee = dust_policy.add_to_fee;
ptx.tx = tx;
ptx.change_dts = change_dts;
ptx.selected_transfers = selected_transfers;
ptx.tx_key = tx_key;
ptx.additional_tx_keys = additional_tx_keys;
ptx.dests = dsts;
ptx.construction_data.sources = sources;
ptx.construction_data.change_dts = change_dts;
ptx.construction_data.splitted_dsts = splitted_dsts;
ptx.construction_data.selected_transfers = selected_transfers;
ptx.construction_data.extra = tx.extra;
ptx.construction_data.unlock_time = unlock_time;
ptx.construction_data.use_rct = false;
ptx.construction_data.use_bulletproofs = false;
ptx.construction_data.dests = dsts;
// record which subaddress indices are being used as inputs
ptx.construction_data.subaddr_account = subaddr_account;
ptx.construction_data.subaddr_indices.clear();
for (size_t idx: selected_transfers)
ptx.construction_data.subaddr_indices.insert(m_transfers[idx].m_subaddr_index.minor);
}
}

View File

@ -73,7 +73,7 @@ namespace tools
// get_tx_pool_error
// out_of_hashchain_bounds_error
// transfer_error *
// get_random_outs_general_error
// get_outs_general_error
// not_enough_unlocked_money
// not_enough_money
// tx_not_possible
@ -130,8 +130,8 @@ namespace tools
get_blocks_error_message_index,
get_hashes_error_message_index,
get_out_indices_error_message_index,
get_random_outs_error_message_index,
get_service_nodes_error_message_index
get_service_nodes_error_message_index,
get_outs_error_message_index
};
template<typename Base, int msg_index>
@ -432,7 +432,7 @@ namespace tools
}
};
//----------------------------------------------------------------------------------------------------
typedef failed_rpc_request<transfer_error, get_random_outs_error_message_index> get_random_outs_error;
typedef failed_rpc_request<transfer_error, get_outs_error_message_index> get_outs_error;
//----------------------------------------------------------------------------------------------------
struct not_enough_unlocked_money : public transfer_error
{

View File

@ -767,15 +767,9 @@ namespace wallet_rpc
struct response
{
std::string tx_hash;
std::string tx_key;
uint64_t fee;
std::string tx_blob;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash)
KV_SERIALIZE(tx_key)
KV_SERIALIZE(fee)
KV_SERIALIZE(tx_blob)
END_KV_SERIALIZE_MAP()
};
};

View File

@ -84,10 +84,10 @@ bool do_send_money(tools::wallet2& w1, tools::wallet2& w2, size_t mix_in_factor,
try
{
tools::wallet2::pending_tx ptx;
std::vector<size_t> indices = w1.select_available_outputs([](const tools::wallet2::transfer_details&) { return true; });
w1.transfer(dsts, mix_in_factor, indices, 0, TEST_FEE, std::vector<uint8_t>(), tools::detail::null_split_strategy, tools::tx_dust_policy(TEST_DUST_THRESHOLD), tx, ptx);
w1.commit_tx(ptx);
std::vector<tools::wallet2::pending_tx> ptx;
ptx = w1.create_transactions_2(dsts, mix_in_factor, 0, 0, std::vector<uint8_t>(), 0, {});
for (auto &p: ptx)
w1.commit_tx(p);
return true;
}
catch (const std::exception&)
@ -167,8 +167,8 @@ bool transactions_flow_test(std::string& working_folder,
daemon_req.miner_address = w1.get_account().get_public_address_str(MAINNET);
daemon_req.threads_count = 9;
r = net_utils::invoke_http_json("/start_mining", daemon_req, daemon_rsp, http_client, std::chrono::seconds(10));
CHECK_AND_ASSERT_MES(r, false, "failed to get getrandom_outs");
CHECK_AND_ASSERT_MES(daemon_rsp.status == CORE_RPC_STATUS_OK, false, "failed to getrandom_outs.bin");
CHECK_AND_ASSERT_MES(r, false, "failed to start mining getrandom_outs");
CHECK_AND_ASSERT_MES(daemon_rsp.status == CORE_RPC_STATUS_OK, false, "failed to start mining");
//wait for money, until balance will have enough money
w1.refresh(true, blocks_fetched, received_money, ok);

View File

@ -52,6 +52,10 @@ int run_fuzzer(int argc, const char **argv, Fuzzer &fuzzer)
return 1;
}
#ifdef __AFL_HAVE_MANUAL_CONTROL
__AFL_INIT();
#endif
int ret = fuzzer.init();
if (ret)
return ret;

View File

@ -43,8 +43,14 @@ set_property(TARGET hash-tests
PROPERTY
FOLDER "tests")
# NOTE(loki): We merged in cn_slow_hash_2 from monero, but we're currently on CryptonightHeavy, so only need slow and slow-1
# foreach (hash IN ITEMS fast slow slow-1 slow-2 tree extra-blake extra-groestl extra-jh extra-skein)
foreach (hash IN ITEMS fast slow slow-1 tree extra-blake extra-groestl extra-jh extra-skein)
add_test(
NAME "hash-${hash}"
COMMAND hash-tests "${hash}" "${CMAKE_CURRENT_SOURCE_DIR}/tests-${hash}.txt")
endforeach ()
add_test(
NAME "hash-variant2-int-sqrt"
COMMAND hash-tests "variant2_int_sqrt")

View File

@ -33,9 +33,11 @@
#include <iomanip>
#include <ios>
#include <string>
#include <cfenv>
#include "warnings.h"
#include "crypto/hash.h"
#include "crypto/variant2_int_sqrt.h"
#include "../io.h"
using namespace std;
@ -71,6 +73,9 @@ struct hash_func {
{"extra-jh", hash_extra_jh}, {"extra-skein", hash_extra_skein},
{"slow-1", cn_slow_hash_1}};
int test_variant2_int_sqrt();
int test_variant2_int_sqrt_ref();
int main(int argc, char *argv[]) {
hash_f *f;
hash_func *hf;
@ -80,6 +85,36 @@ int main(int argc, char *argv[]) {
size_t test = 0;
bool error = false;
if (argc != 3) {
if ((argc == 2) && (strcmp(argv[1], "variant2_int_sqrt") == 0)) {
if (test_variant2_int_sqrt_ref() != 0) {
return 1;
}
const int round_modes[3] = { FE_DOWNWARD, FE_TONEAREST, FE_UPWARD };
for (int i = 0; i < 3; ++i) {
std::fesetround(round_modes[i]);
const int result = test_variant2_int_sqrt();
if (result != 0) {
cerr << "FPU round mode was set to ";
switch (round_modes[i]) {
case FE_DOWNWARD:
cerr << "FE_DOWNWARD";
break;
case FE_TONEAREST:
cerr << "FE_TONEAREST";
break;
case FE_UPWARD:
cerr << "FE_UPWARD";
break;
default:
cerr << "unknown";
break;
}
cerr << endl;
return result;
}
}
return 0;
}
cerr << "Wrong number of arguments" << endl;
return 1;
}
@ -129,3 +164,165 @@ int main(int argc, char *argv[]) {
}
return error ? 1 : 0;
}
#if defined(__x86_64__) || (defined(_MSC_VER) && defined(_WIN64))
#include <emmintrin.h>
#if defined(_MSC_VER) || defined(__MINGW32__)
#include <intrin.h>
#else
#include <wmmintrin.h>
#endif
#endif
static inline bool test_variant2_int_sqrt_sse(const uint64_t sqrt_input, const uint64_t correct_result)
{
#if defined(__x86_64__) || (defined(_MSC_VER) && defined(_WIN64))
uint64_t sqrt_result;
VARIANT2_INTEGER_MATH_SQRT_STEP_SSE2();
VARIANT2_INTEGER_MATH_SQRT_FIXUP(sqrt_result);
if (sqrt_result != correct_result) {
cerr << "Integer sqrt (SSE2 version) returned incorrect result for N = " << sqrt_input << endl;
cerr << "Expected result: " << correct_result << endl;
cerr << "Returned result: " << sqrt_result << endl;
return false;
}
#endif
return true;
}
static inline bool test_variant2_int_sqrt_fp64(const uint64_t sqrt_input, const uint64_t correct_result)
{
#if defined DBL_MANT_DIG && (DBL_MANT_DIG >= 50)
uint64_t sqrt_result;
VARIANT2_INTEGER_MATH_SQRT_STEP_FP64();
VARIANT2_INTEGER_MATH_SQRT_FIXUP(sqrt_result);
if (sqrt_result != correct_result) {
cerr << "Integer sqrt (FP64 version) returned incorrect result for N = " << sqrt_input << endl;
cerr << "Expected result: " << correct_result << endl;
cerr << "Returned result: " << sqrt_result << endl;
return false;
}
#endif
return true;
}
static inline bool test_variant2_int_sqrt_ref(const uint64_t sqrt_input, const uint64_t correct_result)
{
uint64_t sqrt_result;
VARIANT2_INTEGER_MATH_SQRT_STEP_REF();
if (sqrt_result != correct_result) {
cerr << "Integer sqrt (reference version) returned incorrect result for N = " << sqrt_input << endl;
cerr << "Expected result: " << correct_result << endl;
cerr << "Returned result: " << sqrt_result << endl;
return false;
}
return true;
}
static inline bool test_variant2_int_sqrt(const uint64_t sqrt_input, const uint64_t correct_result)
{
if (!test_variant2_int_sqrt_sse(sqrt_input, correct_result)) {
return false;
}
if (!test_variant2_int_sqrt_fp64(sqrt_input, correct_result)) {
return false;
}
return true;
}
int test_variant2_int_sqrt()
{
if (!test_variant2_int_sqrt(0, 0)) {
return 1;
}
if (!test_variant2_int_sqrt(1ULL << 63, 1930543745UL)) {
return 1;
}
if (!test_variant2_int_sqrt(uint64_t(-1), 3558067407UL)) {
return 1;
}
for (uint64_t i = 1; i <= 3558067407UL; ++i) {
// "i" is integer part of "sqrt(2^64 + n) * 2 - 2^33"
// n = (i/2 + 2^32)^2 - 2^64
const uint64_t i0 = i >> 1;
uint64_t n1;
if ((i & 1) == 0) {
// n = (i/2 + 2^32)^2 - 2^64
// n = i^2/4 + 2*2^32*i/2 + 2^64 - 2^64
// n = i^2/4 + 2^32*i
// i is even, so i^2 is divisible by 4:
// n = (i^2 >> 2) + (i << 32)
// int_sqrt_v2(i^2/4 + 2^32*i - 1) must be equal to i - 1
// int_sqrt_v2(i^2/4 + 2^32*i) must be equal to i
n1 = i0 * i0 + (i << 32) - 1;
}
else {
// n = (i/2 + 2^32)^2 - 2^64
// n = i^2/4 + 2*2^32*i/2 + 2^64 - 2^64
// n = i^2/4 + 2^32*i
// i is odd, so i = i0*2+1 (i0 = i >> 1)
// n = (i0*2+1)^2/4 + 2^32*i
// n = (i0^2*4+i0*4+1)/4 + 2^32*i
// n = i0^2+i0+1/4 + 2^32*i
// i0^2+i0 + 2^32*i < n < i0^2+i0+1 + 2^32*i
// int_sqrt_v2(i0^2+i0 + 2^32*i) must be equal to i - 1
// int_sqrt_v2(i0^2+i0+1 + 2^32*i) must be equal to i
n1 = i0 * i0 + i0 + (i << 32);
}
if (!test_variant2_int_sqrt(n1, i - 1)) {
return 1;
}
if (!test_variant2_int_sqrt(n1 + 1, i)) {
return 1;
}
}
return 0;
}
int test_variant2_int_sqrt_ref()
{
if (!test_variant2_int_sqrt_ref(0, 0)) {
return 1;
}
if (!test_variant2_int_sqrt_ref(1ULL << 63, 1930543745UL)) {
return 1;
}
if (!test_variant2_int_sqrt_ref(uint64_t(-1), 3558067407UL)) {
return 1;
}
// Reference version is slow, so we test only every 83th edge case
// "i += 83" because 1 + 83 * 42868282 = 3558067407
for (uint64_t i = 1; i <= 3558067407UL; i += 83) {
const uint64_t i0 = i >> 1;
uint64_t n1;
if ((i & 1) == 0) {
n1 = i0 * i0 + (i << 32) - 1;
}
else {
n1 = i0 * i0 + i0 + (i << 32);
}
if (!test_variant2_int_sqrt_ref(n1, i - 1)) {
return 1;
}
if (!test_variant2_int_sqrt_ref(n1 + 1, i)) {
return 1;
}
}
return 0;
}

View File

@ -0,0 +1,10 @@
4cf1ff9ca46eb433b36cd9f70e02b14cc06bfd18ca77fa9ccaafd1fd96c674b0 5468697320697320612074657374205468697320697320612074657374205468697320697320612074657374
7d292e43f4751714ec07dbcb0e4bbffe2a7afb6066420960684ff57d7474c871 4c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e67
335563425256edebf1d92dc342369c2f4770ebb4112ba975659bd8a0f210abd0 656c69742c2073656420646f20656975736d6f642074656d706f7220696e6369646964756e74207574206c61626f7265
47758e86d2f57210366cec36fff26f9464d89efd116fe6ef28b718b5da120801 657420646f6c6f7265206d61676e6120616c697175612e20557420656e696d206164206d696e696d2076656e69616d2c
48787b48d5c68f0c1dd825c32580af741cc0ee314f08133135c1e86d87a24a95 71756973206e6f737472756420657865726369746174696f6e20756c6c616d636f206c61626f726973206e697369
93bdf47495854f7cfaaca1af8c0f39ef4a3024c10eb0dea23726b0e06ef29e84 757420616c697175697020657820656120636f6d6d6f646f20636f6e7365717561742e20447569732061757465
a375a71d0541057ccc96719150dfe10b6e6f486b19cf4a0835e19605413a8417 697275726520646f6c6f7220696e20726570726568656e646572697420696e20766f6c7570746174652076656c6974
163478a76f8f1432533fbdd1284d65c89f37479e54f20841c6ce4eba56c73854 657373652063696c6c756d20646f6c6f726520657520667567696174206e756c6c612070617269617475722e
356b0470c6eea75cad7a108179e232905b23bdaf03c2824c6e619d503ee93677 4578636570746575722073696e74206f6363616563617420637570696461746174206e6f6e2070726f6964656e742c
a47e2b007dc25bb279e197a1b91f67ecebe2ddd8791cd32dd2cb76dd21ed943f 73756e7420696e2063756c706120717569206f666669636961206465736572756e74206d6f6c6c697420616e696d20696420657374206c61626f72756d2e

View File

@ -59,17 +59,17 @@ static crypto::key_image generate_key_image()
return key_image;
}
static crypto::public_key generate_output()
static std::pair<uint64_t, uint64_t> generate_output()
{
return rct::rct2pk(rct::scalarmultBase(rct::skGen()));
return std::make_pair(rand(), rand());
}
static const crypto::chacha_key KEY_1 = generate_chacha_key();
static const crypto::chacha_key KEY_2 = generate_chacha_key();
static const crypto::key_image KEY_IMAGE_1 = generate_key_image();
static const crypto::public_key OUTPUT_1 = generate_output();
static const crypto::public_key OUTPUT_2 = generate_output();
static const std::pair<uint64_t, uint64_t> OUTPUT_1 = generate_output();
static const std::pair<uint64_t, uint64_t> OUTPUT_2 = generate_output();
class RingDB: public tools::ringdb
{