diff --git a/CMakeLists.txt b/CMakeLists.txt index 60130ab2..56dc97d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -125,10 +125,10 @@ if(BUILD_WITH_TOOLS) add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/src") endif() -if(BUILD_WITH_TESTS) +#[[if(BUILD_WITH_TESTS) enable_testing() add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/tests") -endif() +endif()]] if(BUILD_WITH_PACKAGE) include(project/CPackConfig) diff --git a/cmake/find/FindBoost.cmake b/cmake/find/FindBoost.cmake index 03ff7c0f..45e76192 100644 --- a/cmake/find/FindBoost.cmake +++ b/cmake/find/FindBoost.cmake @@ -898,7 +898,7 @@ function(_Boost_COMPONENT_DEPENDENCIES component _ret) set(_Boost_WAVE_DEPENDENCIES filesystem serialization thread chrono date_time atomic) set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) endif() - if(NOT Boost_VERSION VERSION_LESS 107100) + if(NOT Boost_VERSION VERSION_LESS 107300) message(WARNING "New Boost version may have incorrect or missing dependencies and imported targets") endif() endif() diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 6a17f580..753fce07 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -177,7 +177,8 @@ set(QwertycoinFramework_Common_SOURCES "${CMAKE_CURRENT_LIST_DIR}/Common/int-util.h" "${CMAKE_CURRENT_LIST_DIR}/Common/pod-class.h" "${CMAKE_CURRENT_LIST_DIR}/Common/static_assert.h" -) + "${CMAKE_CURRENT_LIST_DIR}/Common/HardwareInfo.cpp" + "${CMAKE_CURRENT_LIST_DIR}/Common/HardwareInfo.h") set(QwertycoinFramework_Common_LIBS Threads::Threads diff --git a/lib/Common/HardwareInfo.cpp b/lib/Common/HardwareInfo.cpp new file mode 100644 index 00000000..19166296 --- /dev/null +++ b/lib/Common/HardwareInfo.cpp @@ -0,0 +1,143 @@ +// +// Created by exploshot on 12.10.20. +// + +#ifndef _WIN32 + +#include +#include +#include +#include +#include +#include +#include + +#endif + +#ifdef _WIN32 + +#define WIN32_LEAN_AND_MEAN +#include +#include + +#endif + +#if __APPLE__ +#include +#include +#endif + +#include + +#include + +using namespace Tools::CPU; + +#ifdef _WIN32 + +static std::vector cpuinfoBuffer() +{ + std::vector buffer; + + DWORD byte_count = 0; + GetLogicalProcessorInformation(nullptr, &byte_count); + buffer.resize(byte_count / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION)); + GetLogicalProcessorInformation(buffer.data(), &byte_count); + + return buffer; +} + +#endif + +std::string Tools::CPU::architecture() noexcept +{ + #ifdef _WIN32 + SYSTEM_INFO sysInfo; + GetSystemInfo(&sysInfo); + + switch (sysInfo.wProcessorArchitecture) + { + case PROCESSOR_ARCHITECTURE_AMD64: + return "x64"; + case PROCESSOR_ARCHITECTURE_ARM: + return "ARM"; + case PROCESSOR_ARCHITECTURE_IA64: + return "Itanium"; + case PROCESSOR_ARCHITECTURE_INTEL: + return "x86"; + default: + return "unknown"; + } + #endif + + #ifndef _WIN32 + utsname buf; + if (uname(&buf) == -1) { + return "unknown"; + } + + if (!strcmp(buf.machine, "x86_64")) + return "x64"; + else if (strstr(buf.machine, "arm") == buf.machine) + return "ARM"; + else if (!strcmp(buf.machine, "ia64") || !strcmp(buf.machine, "IA64")) + return "Itanium"; + else if (!strcmp(buf.machine, "i686")) + return "x86"; + else + return "unknown"; + #endif +} + +Tools::CPU::Quantities Tools::CPU::quantities() +{ + Quantities ret{}; + + #ifdef _WIN32 + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + ret.logical = sysinfo.dwNumberOfProcessors; + ret.physical = ret.logical / 2; + #elif __APPLE__ + int nm[2]; + size_t len = 4; + uint32_t count; + + nm[0] = CTL_HW; nm[1] = HW_AVAILCPU; + sysctl(nm, 2, &count, &len, NULL, 0); + + if(count < 1) { + nm[1] = HW_NCPU; + sysctl(nm, 2, &count, &len, NULL, 0); + if(count < 1) { count = 1; } + } + ret.logical = count; + ret.physical = ret.logical / 2; + #else + ret.logical = sysconf(_SC_NPROCESSORS_ONLN); + + std::ifstream cpuinfo("/proc/cpuinfo"); + + if (!cpuinfo.is_open() || !cpuinfo) { + return ret; + } + + std::vector packageIds; + for (std::string line; std::getline(cpuinfo, line);) { + if (line.find("physical id") == 0) { + const auto physicalId = std::strtoul(line.c_str() + line.find_first_of("1234567890"), nullptr, 10); + if (std::find(packageIds.begin(), packageIds.end(), physicalId) == packageIds.end()) { + packageIds.emplace_back(physicalId); + } + } + } + + ret.packages = packageIds.size(); + ret.physical = ret.logical / ret.packages; + #endif + return ret; +} + + + + diff --git a/lib/Common/HardwareInfo.h b/lib/Common/HardwareInfo.h new file mode 100644 index 00000000..28d7a2e3 --- /dev/null +++ b/lib/Common/HardwareInfo.h @@ -0,0 +1,409 @@ +// +// Created by exploshot on 12.10.20. +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if (defined(__APPLE__)) +# define SYS_MEMINFO_MAC + +# include +# include +# include +# include +# include + +#elif (defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(_WIN64)) +# define SYS_MEMINFO_WIN + +# include +# include + +# undef min +# undef max +#else +# define SYS_MEMINFO_NIX + +# include +# include +# include + +#endif + +#if defined _MSC_VER +#if defined HARDWARE_INFO_DLL +#if defined HARDWARE_INFO_BUILDING +#define HARDWARE_INFO_LINKAGE __declspec(dllexport) +#define HARDWARE_INFO_LINKAGE_INTERNAL +#else +#define HARDWARE_INFO_LINKAGE __declspec(dllimport) +#define HARDWARE_INFO_LINKAGE_INTERNAL +#endif +#else +#define HARDWARE_INFO_LINKAGE +#define HARDWARE_INFO_LINKAGE_INTERNAL +#endif // Building a DLL vs. Static Library +#else +#define HARDWARE_INFO_LINKAGE __attribute__((visibility("default"))) +#define HARDWARE_INFO_LINKAGE_INTERNAL __attribute__((visibility("hidden"))) +#endif + +namespace Tools { + namespace CPU { + enum class Architecture + { + x64, + arm, + itanium, + x86, + unknown, + }; + + enum class ByteOrder + { + little, + big, + }; + + enum class InstructionSet + { + s3d_now, + s3d_now_extended, + mmx, + mmx_extended, + sse, + sse2, + sse3, + ssse3, + sse4a, + sse41, + sse42, + aes, + + avx, + avx2, + + avx_512, + avx_512_f, + avx_512_cd, + avx_512_pf, + avx_512_er, + avx_512_vl, + avx_512_bw, + avx_512_bq, + avx_512_dq, + avx_512_ifma, + avx_512_vbmi, + + hle, + + bmi1, + bmi2, + adx, + mpx, + sha, + prefetch_wt1, + + fma3, + fma4, + + xop, + + rd_rand, + + x64, + x87_fpu, + }; + + enum class CacheType + { + unified, + instruction, + data, + trace, + }; + + struct Quantities + { + // Hyperthreads. + uint32_t logical; + // Physical "cores". + uint32_t physical; + // Physical CPU units/packages/sockets. + uint32_t packages; + }; + + struct Cache + { + std::size_t size; + std::size_t line_size; + std::uint8_t associativity; + CacheType type; + }; + + // Returns the quantity of CPU at various gradation. + HARDWARE_INFO_LINKAGE Quantities quantities(); + + // Get CPU cache properties + HARDWARE_INFO_LINKAGE Cache cache(unsigned int level); + + // Returns the architecture of the current CPU. + HARDWARE_INFO_LINKAGE std::string architecture() noexcept; + + // Returns the current frequency of the current CPU in Hz. + HARDWARE_INFO_LINKAGE uint64_t frequency() noexcept; + + // Returns the current byte order of the current CPU. + HARDWARE_INFO_LINKAGE ByteOrder byteOrder() noexcept; + + // Returns the CPU's vendor. + HARDWARE_INFO_LINKAGE std::string vendor(); + + // Returns the CPU's vendor according to the CPUID instruction + HARDWARE_INFO_LINKAGE std::string vendorId(); + + // Returns the CPU's model name. + HARDWARE_INFO_LINKAGE std::string modelName(); + + // Returns whether an instruction set is supported by the current CPU. + // `noexcept` on Windows + HARDWARE_INFO_LINKAGE bool instructionSetSupported(InstructionSet set); + + // Retrieve all of the instruction sets this hardware supports + HARDWARE_INFO_LINKAGE std::vector supportedInstructionSets(); + } + + namespace Memory { + class MemInfo + { + public: + inline static double usedVirtMem(); + + inline static double usedPhysMem(); + + inline static double usedVirtMemMax(); + + inline static double usedPhysMemMax(); + + inline static double usedSysMem(); + + inline static double freeSysMem(); + + inline static double sysMem(); + + private: + inline static int parseLine(char *line) + { + int i = strlen(line); + while (*line < '0' || *line > '9') line++; + line[i - 3] = '\0'; + i = atoi(line); + return i; + } + }; + + double MemInfo::usedVirtMem() + { + #ifdef SYS_MEMINFO_NIX + FILE *file = fopen("/proc/self/status", "r"); + int result = -1; + char line[128]; + while (fgets(line, 128, file) != NULL) { + if (strncmp(line, "VmSize:", 7) == 0) { + result = parseLine(line); + break; + } + } + fclose(file); + return static_cast(result); + #elif defined(SYS_MEMINFO_WIN) + PROCESS_MEMORY_COUNTERS_EX pmc; + GetProcessMemoryInfo(GetCurrentProcess(),(PROCESS_MEMORY_COUNTERS*) &pmc, sizeof(pmc)); + return static_cast(pmc.WorkingSetSize)/1024.0; + #elif defined(SYS_MEMINFO_MAC) + struct mach_task_basic_info info; + mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT; + if ( task_info( mach_task_self( ), MACH_TASK_BASIC_INFO,(task_info_t)&info, &infoCount ) != KERN_SUCCESS ){ + return std::numeric_limits::quiet_NaN(); + } else { + return static_cast(info.virtual_size)/1024.0; + } + #else + return std::numeric_limits::quiet_NaN(); + #endif + } + + double MemInfo::usedPhysMem() + { + #ifdef SYS_MEMINFO_NIX + FILE *file = fopen("/proc/self/status", "r"); + int result = -1; + char line[128]; + while (fgets(line, 128, file) != NULL) { + if (strncmp(line, "VmRSS:", 6) == 0) { + result = parseLine(line); + break; + } + } + fclose(file); + return static_cast(result); + #elif defined(SYS_MEMINFO_WIN) + PROCESS_MEMORY_COUNTERS_EX pmc; + GetProcessMemoryInfo(GetCurrentProcess(),(PROCESS_MEMORY_COUNTERS*) &pmc, sizeof(pmc)); + return static_cast(pmc.WorkingSetSize)/1024.0; + #elif defined(SYS_MEMINFO_MAC) + struct mach_task_basic_info info; + mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT; + if ( task_info( mach_task_self( ), MACH_TASK_BASIC_INFO,(task_info_t)&info, &infoCount ) != KERN_SUCCESS ){ + return std::numeric_limits::quiet_NaN(); + } else { + return static_cast(info.resident_size)/1024.0; + } + #else + return std::numeric_limits::quiet_NaN(); + #endif + } + + double MemInfo::usedVirtMemMax() + { + #ifdef SYS_MEMINFO_NIX + FILE *file = fopen("/proc/self/status", "r"); + int result = -1; + char line[128]; + while (fgets(line, 128, file) != NULL) { + if (strncmp(line, "VmPeak:", 7) == 0) { + result = parseLine(line); + break; + } + } + fclose(file); + return static_cast(result); + #elif defined(SYS_MEMINFO_WIN) + PROCESS_MEMORY_COUNTERS_EX pmc; + GetProcessMemoryInfo(GetCurrentProcess(),(PROCESS_MEMORY_COUNTERS*) &pmc, sizeof(pmc)); + return static_cast(pmc.PeakPagefileUsage)/1024.0; + #elif defined(SYS_MEMINFO_MAC) + return std::numeric_limits::quiet_NaN(); + #else + return std::numeric_limits::quiet_NaN(); + #endif + } + + double MemInfo::usedPhysMemMax() + { + #ifdef SYS_MEMINFO_NIX + FILE *file = fopen("/proc/self/status", "r"); + int result = -1; + char line[128]; + while (fgets(line, 128, file) != NULL) { + if (strncmp(line, "VmHWM:", 6) == 0) { + result = parseLine(line); + break; + } + } + fclose(file); + return static_cast(result); + #elif defined(SYS_MEMINFO_WIN) + PROCESS_MEMORY_COUNTERS_EX pmc; + GetProcessMemoryInfo(GetCurrentProcess(),(PROCESS_MEMORY_COUNTERS*) &pmc, sizeof(pmc)); + return static_cast(pmc.PeakWorkingSetSize)/1024.0; + #elif defined(SYS_MEMINFO_MAC) + struct rusage rusage; + getrusage( RUSAGE_SELF, &rusage ); + return static_cast(rusage.ru_maxrss)/1024; + #else + return std::numeric_limits::quiet_NaN(); + #endif + } + + double MemInfo::usedSysMem() + { + #ifdef SYS_MEMINFO_NIX + struct sysinfo memInfo; + sysinfo(&memInfo); + return static_cast(memInfo.totalram - memInfo.freeram) * memInfo.mem_unit / 1024.0; + #elif defined(SYS_MEMINFO_WIN) + MEMORYSTATUSEX memInfo; + memInfo.dwLength = sizeof(MEMORYSTATUSEX); + GlobalMemoryStatusEx(&memInfo); + return static_cast(memInfo.ullTotalPhys-memInfo.ullAvailPhys)/1024.0; + #elif defined(SYS_MEMINFO_MAC) + return std::numeric_limits::quiet_NaN(); + #else + return std::numeric_limits::quiet_NaN(); + #endif + } + + double MemInfo::freeSysMem() + { + #ifdef SYS_MEMINFO_NIX + struct sysinfo memInfo; + sysinfo(&memInfo); + return static_cast(memInfo.freeram) * memInfo.mem_unit / 1024.0; + #elif defined(SYS_MEMINFO_WIN) + MEMORYSTATUSEX memInfo; + memInfo.dwLength = sizeof(MEMORYSTATUSEX); + GlobalMemoryStatusEx(&memInfo); + return static_cast(memInfo.ullAvailPhys)/1024.0; + #elif defined(SYS_MEMINFO_MAC) + return std::numeric_limits::quiet_NaN(); + #else + return std::numeric_limits::quiet_NaN(); + #endif + } + + double MemInfo::sysMem() + { + #ifdef SYS_MEMINFO_NIX + struct sysinfo memInfo; + sysinfo(&memInfo); + return static_cast(memInfo.totalram) * memInfo.mem_unit / 1024.0; + #elif defined(SYS_MEMINFO_WIN) + MEMORYSTATUSEX memInfo; + memInfo.dwLength = sizeof(MEMORYSTATUSEX); + GlobalMemoryStatusEx(&memInfo); + return static_cast(memInfo.ullTotalPhys)/1024.0; + #elif defined(SYS_MEMINFO_MAC) + return std::numeric_limits::quiet_NaN(); + #else + return std::numeric_limits::quiet_NaN(); + #endif + } + } + + namespace Storage { + class SpaceInfo + { + public: + inline static uintmax_t freeSpace (boost::filesystem::path path); + inline static uintmax_t availableSpace (boost::filesystem::path path); + inline static uintmax_t capacitySpace (boost::filesystem::path path); + }; + + uintmax_t SpaceInfo::freeSpace(boost::filesystem::path path) + { + return boost::filesystem::space(path).free; + } + + uintmax_t SpaceInfo::availableSpace(boost::filesystem::path path) + { + return boost::filesystem::space(path).available; + } + + uintmax_t SpaceInfo::capacitySpace(boost::filesystem::path path) + { + return boost::filesystem::space(path).capacity; + } + } +} diff --git a/lib/Common/Math.h b/lib/Common/Math.h index 7ee93774..f6e518eb 100644 --- a/lib/Common/Math.h +++ b/lib/Common/Math.h @@ -1,4 +1,5 @@ // Copyright (c) 2012-2017, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2020, The Karbowanec developers // Copyright (c) 2018-2021, The Qwertycoin Group. // // This file is part of Qwertycoin. @@ -21,6 +22,10 @@ #include #include #include +#include +#include + +using namespace std; namespace Common { @@ -69,4 +74,105 @@ double medianValue(std::vector &v) } } +template +void integer_cast_throw(const Source &arg) +{ + throw std::out_of_range("Cannot convert value " + Common::toString(arg) + " to integer in range [" + + Common::toString(std::numeric_limits::min()) + ".." + + Common::toString(std::numeric_limits::max()) + "]"); +} + +template +inline Target integer_cast_impl(const Source &arg, std::true_type, std::true_type) +{ + // both unsigned + if (arg > std::numeric_limits::max()) + integer_cast_throw(arg); + return static_cast(arg); +} + +template +inline Target integer_cast_impl(const Source &arg, std::false_type, std::false_type) +{ + // both signed + if (arg > std::numeric_limits::max()) + integer_cast_throw(arg); + if (arg < std::numeric_limits::min()) + integer_cast_throw(arg); + return static_cast(arg); +} + +template +inline Target integer_cast_impl(const Source &arg, std::true_type, std::false_type) +{ + // signed to unsigned + typedef typename std::make_unsigned::type USource; + if (arg < 0) + integer_cast_throw(arg); + if (static_cast(arg) > std::numeric_limits::max()) + integer_cast_throw(arg); + return static_cast(arg); +} + +template +inline Target integer_cast_impl(const Source &arg, std::false_type, std::true_type) +{ + // unsigned to signed + typedef typename std::make_unsigned::type UTarget; + if (arg > static_cast(std::numeric_limits::max())) + integer_cast_throw(arg); + return static_cast(arg); +} + +template // source integral +inline Target integer_cast_is_integral(const Source &arg, std::true_type) +{ + return integer_cast_impl(arg, std::is_unsigned{}, std::is_unsigned{}); +} + +inline bool has_sign(const std::string &arg) +{ + size_t pos = 0; + while (pos != arg.size() && isspace(arg[pos])) + pos += 1; + return pos != arg.size() && arg[pos] == '-'; +} + +inline bool has_tail(const std::string &arg, size_t &pos) +{ + while (pos != arg.size() && isspace(arg[pos])) + pos += 1; + return pos != arg.size(); +} + +template // source not integral (string convertible) +inline Target integer_cast_is_integral(const Source &arg, std::false_type) +{ + const std::string &sarg = arg; // creates tmp object if neccessary + if (std::is_unsigned::value) + { // Crazy stupid C++ standard :( + if (has_sign(sarg)) + throw std::out_of_range("Cannot convert string '" + sarg + "' to integer, must be >= 0"); + size_t pos = 0; + auto val = std::stoull(sarg, &pos); + if (has_tail(sarg, pos)) + throw std::out_of_range("Cannot convert string '" + sarg + "' to integer, excess characters not allowed"); + return integer_cast_is_integral(val, std::true_type{}); + } + size_t pos = 0; + auto val = std::stoll(sarg, &pos); + if (has_tail(sarg, pos)) + throw std::out_of_range("Cannot convert string '" + sarg + "' to integer, excess characters '" + sarg.substr(pos) + "' not allowed"); + return integer_cast_is_integral(val, std::true_type{}); +} + +template +inline Target integer_cast(const Source &arg) +{ + static_assert( + std::is_integral::value, + "Target type must be integral, source either integral or string convertible"); + return integer_cast_is_integral(arg, std::is_integral{}); +} + } // namespace Common diff --git a/lib/Common/StringTools.cpp b/lib/Common/StringTools.cpp index 505cf867..42de76a9 100644 --- a/lib/Common/StringTools.cpp +++ b/lib/Common/StringTools.cpp @@ -432,4 +432,18 @@ std::string timeIntervalToString(uint64_t intervalInSeconds) return ss.str(); } +bool starts_with(const std::string &str1, const std::string &str2) +{ + if (str1.length() < str2.length()) + return false; + return str1.compare(0, str2.length(), str2) == 0; +} + +bool ends_with(const std::string &str1, const std::string &str2) +{ + if (str1.length() < str2.length()) + return false; + return str1.compare(str1.length() - str2.length(), str2.length(), str2) == 0; +} + } // namespace Common diff --git a/lib/Common/StringTools.h b/lib/Common/StringTools.h index fc1b815a..2983ac28 100644 --- a/lib/Common/StringTools.h +++ b/lib/Common/StringTools.h @@ -77,6 +77,38 @@ std::string podToHex(const T &s) return toHex(&s, sizeof(s)); } +bool starts_with(const std::string &str1, const std::string &str2); +bool ends_with(const std::string &str1, const std::string &str2); + +inline bool split_string_helper( + const std::string &str, + size_t pos, + const std::string &, std::string &head) +{ + head = str.substr(pos); + return true; +} + +template +inline bool split_string_helper( + const std::string &str, + size_t pos, + const std::string &separator, + std::string &head, + Parts &... parts) +{ + size_t pos2 = str.find(separator, pos); + if (pos2 == std::string::npos) + return false; + head = str.substr(pos, pos2 - pos); + return split_string_helper(str, pos2 + 1, separator, parts...); +} + +template +inline bool split_string(const std::string &str, const std::string &separator, Parts &... parts) { +return split_string_helper(str, 0, separator, parts...); +} + std::string extract(std::string &text, char delimiter); std::string extract(const std::string &text, char delimiter, size_t &offset); diff --git a/lib/CryptoNoteCore/Blockchain.cpp b/lib/CryptoNoteCore/Blockchain.cpp index 6fc930d8..d594efa5 100644 --- a/lib/CryptoNoteCore/Blockchain.cpp +++ b/lib/CryptoNoteCore/Blockchain.cpp @@ -1923,6 +1923,35 @@ bool Blockchain::handleGetObjects( return true; } +bool Blockchain::getTransactionsWithOutputGlobalIndexes(const std::vector &txsIds, + std::list &missedTxs, + std::vector>> &txs) +{ + std::lock_guard lk(m_blockchain_lock); + + for (const auto &txId : txsIds) { + auto it = m_transactionMap.find(txId); + if (it == m_transactionMap.end()) { + missedTxs.push_back(txId); + } else { + const TransactionEntry &tx = transactionByIndex(it->second); + if (!(tx.m_global_output_indexes.size())) { + logger(ERROR, BRIGHT_RED) + << "internal error: global indexes for transaction " + << txId + << " is empty"; + + return false; + } + + txs.push_back(std::make_pair(tx.tx, tx.m_global_output_indexes)); + } + } + + return true; +} + bool Blockchain::getAlternativeBlocks(std::list& blocks) { std::lock_guard lk(m_blockchain_lock); diff --git a/lib/CryptoNoteCore/Blockchain.h b/lib/CryptoNoteCore/Blockchain.h index 553a02cc..bfef5dd4 100644 --- a/lib/CryptoNoteCore/Blockchain.h +++ b/lib/CryptoNoteCore/Blockchain.h @@ -81,6 +81,10 @@ class Blockchain : public CryptoNote::ITransactionValidator void setCheckpoints(Checkpoints &&chk_pts) { m_checkpoints = chk_pts; } bool getBlocks(uint32_t start_offset, uint32_t count, std::list &blocks, std::list &txs); bool getBlocks(uint32_t start_offset, uint32_t count, std::list &blocks); + bool getTransactionsWithOutputGlobalIndexes(const std::vector &txsIds, + std::list &missedTxs, + std::vector>> &txs); bool getAlternativeBlocks(std::list &blocks); uint32_t getAlternativeBlocksCount(); Crypto::Hash getBlockIdByHeight(uint32_t height); diff --git a/lib/CryptoNoteCore/Core.cpp b/lib/CryptoNoteCore/Core.cpp index bd618fdd..fe862068 100644 --- a/lib/CryptoNoteCore/Core.cpp +++ b/lib/CryptoNoteCore/Core.cpp @@ -165,6 +165,13 @@ void core::getTransactions( m_blockchain.getTransactions(txs_ids, txs, missed_txs, checkTxPool); } +bool core::getTransactionsWithOutputGlobalIndexes(const std::vector &txsIds, + std::list &missedTxs, + std::vector>> &txs) +{ + return m_blockchain.getTransactionsWithOutputGlobalIndexes(txsIds, missedTxs, txs); +} + bool core::get_alternative_blocks(std::list &blocks) { return m_blockchain.getAlternativeBlocks(blocks); diff --git a/lib/CryptoNoteCore/Core.h b/lib/CryptoNoteCore/Core.h index f9bba5dd..61819874 100644 --- a/lib/CryptoNoteCore/Core.h +++ b/lib/CryptoNoteCore/Core.h @@ -233,6 +233,10 @@ class core : public ICore, std::list &txs, std::list &missed_txs, bool checkTxPool = false) override; + bool getTransactionsWithOutputGlobalIndexes(const std::vector &txsIds, + std::list &missedTxs, + std::vector>> &txs) override; bool getBlockByHash(const Crypto::Hash &h, Block &blk) override; bool getBlockHeight(const Crypto::Hash &blockId, uint32_t &blockHeight) override; @@ -264,6 +268,8 @@ class core : public ICore, Blockchain &get_blockchain_storage() { return m_blockchain; } + std::string getConfigFolder() { return m_config_folder; } + // debug functions void print_blockchain(uint32_t start_index, uint32_t end_index); void print_blockchain_index(); @@ -349,7 +355,6 @@ class core : public ICore, size_t median(std::vector &v); -private: const Currency &m_currency; Logging::LoggerRef logger; CryptoNote::RealTimeProvider m_timeProvider; diff --git a/lib/CryptoNoteCore/ICore.h b/lib/CryptoNoteCore/ICore.h index 3070faeb..4300ece5 100644 --- a/lib/CryptoNoteCore/ICore.h +++ b/lib/CryptoNoteCore/ICore.h @@ -144,6 +144,10 @@ class ICore std::list &txs, std::list &missed_txs, bool checkTxPool = false) = 0; + virtual bool getTransactionsWithOutputGlobalIndexes(const std::vector &txsIds, + std::list &missedTxs, + std::vector>> &txs) = 0; virtual bool getBackwardBlocksSizes( uint32_t fromHeight, std::vector &sizes, diff --git a/lib/Global/version.h.in b/lib/Global/version.h.in index 3629c7fd..07cfb710 100644 --- a/lib/Global/version.h.in +++ b/lib/Global/version.h.in @@ -6,7 +6,7 @@ #define PROJECT_COPYRIGHT "@PROJECT_COPYRIGHT@" #define APP_VER_MAJOR 6 #define APP_VER_MINOR 0 -#define APP_VER_REV 3 +#define APP_VER_REV 4 #define APP_VER_BUILD 6075 #define BUILD_COMMIT_ID "@PROJECT_GIT_COMMIT_ID@" diff --git a/lib/P2p/NetNode.cpp b/lib/P2p/NetNode.cpp index b56e0cc4..76cdd6d8 100644 --- a/lib/P2p/NetNode.cpp +++ b/lib/P2p/NetNode.cpp @@ -498,6 +498,9 @@ bool NodeServer::is_remote_host_allowed(const uint32_t address_ip) bool NodeServer::ban_host(const uint32_t address_ip, time_t seconds) { std::unique_lock lock(mutex); + + logger(WARNING, BRIGHT_YELLOW) << "Banning Host " << Common::ipAddressToString(address_ip); + return block_host(address_ip, seconds); } @@ -793,6 +796,7 @@ bool NodeServer::handshake(CryptoNote::LevinProtocol &proto, << "COMMAND_HANDSHAKE Failed, peer is wrong version! (" << std::to_string(rsp.node_data.version) << "), closing connection."; + ban_host(context.m_remote_ip); return false; } else if ((rsp.node_data.version - CryptoNote::P2P_CURRENT_VERSION) >= CryptoNote::P2P_UPGRADE_WINDOW) { logger(Logging::WARNING) @@ -1508,6 +1512,7 @@ int NodeServer::handle_handshake(int command, << "UNSUPPORTED NETWORK AGENT VERSION CONNECTED! version=" << std::to_string(arg.node_data.version); context.m_state = CryptoNoteConnectionContext::state_shutdown; + ban_host(context.m_remote_ip); return 1; } else if (arg.node_data.version > CryptoNote::P2P_CURRENT_VERSION) { logger(Logging::WARNING) diff --git a/lib/Rpc/CoreRpcServerCommandsDefinitions.h b/lib/Rpc/CoreRpcServerCommandsDefinitions.h index fe37cc84..382b5d4d 100644 --- a/lib/Rpc/CoreRpcServerCommandsDefinitions.h +++ b/lib/Rpc/CoreRpcServerCommandsDefinitions.h @@ -33,1474 +33,1631 @@ namespace CryptoNote { #define CORE_RPC_STATUS_OK "OK" #define CORE_RPC_STATUS_BUSY "BUSY" -struct EMPTY_STRUCT -{ - void serialize(ISerializer &s) - { - } -}; - -struct STATUS_STRUCT -{ - void serialize(ISerializer &s) - { - KV_MEMBER(status) - } - - std::string status; -}; - -struct COMMAND_RPC_GET_HEIGHT -{ - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(height) - KV_MEMBER(status) - } - - uint64_t height; - std::string status; - }; - - typedef EMPTY_STRUCT request; -}; - -struct COMMAND_RPC_GET_BLOCKS_FAST -{ - struct request - { - void serialize(ISerializer &s) - { - serializeAsBinary(block_ids, "block_ids", s); - } - - /*! - First 10 blocks id goes sequential, next goes in pow(2,n) offset, - like 2, 4, 8, 16, 32, 64 and so on, and the last one is always - genesis block - */ - std::vector block_ids; - }; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(blocks) - KV_MEMBER(start_height) - KV_MEMBER(current_height) - KV_MEMBER(status) - } - - std::vector blocks; - uint64_t start_height; - uint64_t current_height; - std::string status; - }; -}; - -struct COMMAND_RPC_GET_TRANSACTIONS -{ - struct request - { - void serialize(ISerializer &s) - { - KV_MEMBER(txs_hashes) - } - - std::vector txs_hashes; - }; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(txs_as_hex) - KV_MEMBER(missed_tx) - KV_MEMBER(status) - } - - std::vector txs_as_hex; // transactions blobs as hex - std::vector missed_tx; // not found transactions - std::string status; - }; -}; - -struct COMMAND_RPC_GET_TRANSACTIONS_BY_HEIGHTS -{ - struct request - { - void serialize(ISerializer &s) - { - KV_MEMBER(startBlock) - KV_MEMBER(additor) - KV_MEMBER(sigCut) - }; - - uint32_t startBlock; - uint32_t additor = 100; - bool sigCut; - }; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(status) - KV_MEMBER(transactions) - } - - std::vector transactions; - std::string status; - }; -}; - -struct COMMAND_RPC_GET_POOL_CHANGES -{ - struct request - { - void serialize(ISerializer &s) - { - KV_MEMBER(tailBlockId) - serializeAsBinary(knownTxsIds, "knownTxsIds", s); - } - - Crypto::Hash tailBlockId; - std::vector knownTxsIds; - }; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(isTailBlockActual) - KV_MEMBER(addedTxs) - serializeAsBinary(deletedTxsIds, "deletedTxsIds", s); - KV_MEMBER(status) - } - - bool isTailBlockActual; - std::vector addedTxs; // added transactions blobs - std::vector deletedTxsIds; // IDs of not found transactions - std::string status; - }; -}; - -struct COMMAND_RPC_GET_POOL_CHANGES_LITE -{ - struct request - { - void serialize(ISerializer &s) - { - KV_MEMBER(tailBlockId) - serializeAsBinary(knownTxsIds, "knownTxsIds", s); - } - - Crypto::Hash tailBlockId; - std::vector knownTxsIds; - }; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(isTailBlockActual) - KV_MEMBER(addedTxs) - serializeAsBinary(deletedTxsIds, "deletedTxsIds", s); - KV_MEMBER(status) - } - - bool isTailBlockActual; - std::vector addedTxs; // added transactions blobs - std::vector deletedTxsIds; // IDs of not found transactions - std::string status; - }; -}; - -struct COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES -{ - struct request - { - void serialize(ISerializer &s) - { - KV_MEMBER(txid) - } - - Crypto::Hash txid; - }; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(o_indexes) - KV_MEMBER(status) - } - - std::vector o_indexes; - std::string status; - }; -}; - -struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request -{ - void serialize(ISerializer &s) - { - KV_MEMBER(amounts) - KV_MEMBER(outs_count) - } - - std::vector amounts; - uint64_t outs_count; -}; + struct EMPTY_STRUCT + { + void serialize(ISerializer &s) {} + }; + + struct STATUS_STRUCT + { + void serialize(ISerializer &s) + { + KV_MEMBER(status) + } + + std::string status; + }; + + struct COMMAND_RPC_GET_HEIGHT + { + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(height) + KV_MEMBER(status) + } + + uint64_t height; + std::string status; + }; + + typedef EMPTY_STRUCT request; + }; + + struct COMMAND_RPC_GET_BLOCKS_FAST + { + struct request + { + void serialize(ISerializer &s) { serializeAsBinary(block_ids, "block_ids", s); } + + /*! + First 10 blocks id goes sequential, next goes in pow(2,n) offset, + like 2, 4, 8, 16, 32, 64 and so on, and the last one is always + genesis block + */ + std::vector block_ids; + }; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(blocks) + KV_MEMBER(start_height) + KV_MEMBER(current_height) + KV_MEMBER(status) + } + + std::vector blocks; + uint64_t start_height; + uint64_t current_height; + std::string status; + }; + }; + + struct COMMAND_RPC_GET_TRANSACTIONS + { + struct request + { + void serialize(ISerializer &s) { KV_MEMBER(txs_hashes) } + + std::vector txs_hashes; + }; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(txs_as_hex) + KV_MEMBER(missed_tx) + KV_MEMBER(status) + } + + std::vector txs_as_hex; // transactions blobs as hex + std::vector missed_tx; // not found transactions + std::string status; + }; + }; + + struct TxWithOutputGlobalIndices + { + void serialize(ISerializer &s) + { + KV_MEMBER(transaction) + KV_MEMBER(hash) + KV_MEMBER(block_hash) + KV_MEMBER(height) + KV_MEMBER(fee) + KV_MEMBER(timestamp) + KV_MEMBER(output_indexes) + } + + TransactionPrefix transaction; + Crypto::Hash hash; + Crypto::Hash block_hash; + uint32_t height; + uint64_t fee; + uint64_t timestamp; + std::vector output_indexes; + }; + + struct COMMAND_RPC_GET_TRANSACTIONS_BY_HEIGHTS + { + struct request + { + void serialize(ISerializer &s) + { + KV_MEMBER(heights) + KV_MEMBER(as_json) + KV_MEMBER(include_miner_txs) + KV_MEMBER(range) + } + + std::vector heights; + bool include_miner_txs; + bool range; + bool as_json; + }; + + struct entry + { + void serialize(ISerializer &s) + { + KV_MEMBER(tx_hash) + KV_MEMBER(as_json) + KV_MEMBER(fee) + KV_MEMBER(block_height) + KV_MEMBER(block_timestamp) + KV_MEMBER(output_indices) + } + + std::string tx_hash; + Transaction as_json; + uint64_t fee; + uint64_t block_height; + uint64_t block_timestamp; + std::vector output_indices; + }; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(missed_tx) + KV_MEMBER(txs) + KV_MEMBER(status) + } + + // in both old and new + std::list missed_tx; // not found transactions + + // new style + std::vector txs; + std::string status; + }; + }; + + struct COMMAND_RPC_GET_RAW_TRANSACTIONS_BY_HEIGHTS + { + struct request + { + void serialize(ISerializer &s) + { + KV_MEMBER(heights) + KV_MEMBER(includeMinerTxs) + KV_MEMBER(range) + } + + std::vector heights; + bool includeMinerTxs = true; + bool range = false; + }; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(transactions) + KV_MEMBER(missedTxs) + KV_MEMBER(status) + } + + std::vector transactions; + std::list missedTxs; + std::string status; + }; + }; + + struct COMMAND_RPC_GET_RAW_TRANSACTIONS_FROM_POOL + { + typedef EMPTY_STRUCT request; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(transactions) + KV_MEMBER(status) + } + + std::vector transactions; + std::string status; + }; + }; + + struct COMMAND_RPC_GET_POOL_CHANGES + { + struct request + { + void serialize(ISerializer &s) + { + KV_MEMBER(tailBlockId) + serializeAsBinary(knownTxsIds, "knownTxsIds", s); + } + + Crypto::Hash tailBlockId; + std::vector knownTxsIds; + }; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(isTailBlockActual) + KV_MEMBER(addedTxs) + serializeAsBinary(deletedTxsIds, "deletedTxsIds", s); + KV_MEMBER(status) + } + + bool isTailBlockActual; + std::vector addedTxs; // added transactions blobs + std::vector deletedTxsIds; // IDs of not found transactions + std::string status; + }; + }; + + struct COMMAND_RPC_GET_POOL_CHANGES_LITE + { + struct request + { + void serialize(ISerializer &s) + { + KV_MEMBER(tailBlockId) + serializeAsBinary(knownTxsIds, "knownTxsIds", s); + } + + Crypto::Hash tailBlockId; + std::vector knownTxsIds; + }; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(isTailBlockActual) + KV_MEMBER(addedTxs) + serializeAsBinary(deletedTxsIds, "deletedTxsIds", s); + KV_MEMBER(status) + } + + bool isTailBlockActual; + std::vector addedTxs; // added transactions blobs + std::vector deletedTxsIds; // IDs of not found transactions + std::string status; + }; + }; + + struct COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES + { + struct request + { + void serialize(ISerializer &s) { KV_MEMBER(txid) } + + Crypto::Hash txid; + }; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(o_indexes) + KV_MEMBER(status) + } + + std::vector o_indexes; + std::string status; + }; + }; + + struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request + { + void serialize(ISerializer &s) + { + KV_MEMBER(amounts) + KV_MEMBER(outs_count) + } + + std::vector amounts; + uint64_t outs_count; + }; #pragma pack(push, 1) -struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_out_entry -{ - uint64_t global_amount_index; - Crypto::PublicKey out_key; -}; + struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_out_entry + { + uint64_t global_amount_index; + Crypto::PublicKey out_key; + }; #pragma pack(pop) -struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_outs_for_amount -{ - void serialize(ISerializer &s) - { - KV_MEMBER(amount) - serializeAsBinary(outs, "outs", s); - } - - uint64_t amount; - std::vector outs; -}; - -struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response -{ - void serialize(ISerializer &s) - { - KV_MEMBER(outs) - KV_MEMBER(status) - } - - std::vector outs; - std::string status; -}; - -struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS -{ - typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request request; - typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response response; - typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_out_entry out_entry; - typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_outs_for_amount outs_for_amount; -}; - -struct COMMAND_RPC_SEND_RAW_TX -{ - struct request - { - request() = default; - explicit request(const Transaction &); - - void serialize(ISerializer &s) - { - KV_MEMBER(tx_as_hex) - } - - std::string tx_as_hex; - }; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(status) - } - - std::string status; - }; -}; - -struct COMMAND_RPC_START_MINING -{ - struct request - { - void serialize(ISerializer &s) - { - KV_MEMBER(miner_address) - KV_MEMBER(threads_count) - } - - std::string miner_address; - uint64_t threads_count; - }; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(status) - } - - std::string status; - }; -}; - -struct COMMAND_RPC_GET_INFO -{ - typedef EMPTY_STRUCT request; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(status) - KV_MEMBER(version) - KV_MEMBER(height) - KV_MEMBER(top_block_hash) - KV_MEMBER(difficulty) - KV_MEMBER(min_tx_fee) - KV_MEMBER(readable_tx_fee) - KV_MEMBER(tx_count) - KV_MEMBER(tx_pool_size) - KV_MEMBER(alt_blocks_count) - KV_MEMBER(outgoing_connections_count) - KV_MEMBER(incoming_connections_count) - KV_MEMBER(rpc_connections_count) - KV_MEMBER(white_peerlist_size) - KV_MEMBER(grey_peerlist_size) - KV_MEMBER(last_known_block_index) - KV_MEMBER(start_time) - KV_MEMBER(fee_address) - KV_MEMBER(block_major_version) - KV_MEMBER(already_generated_coins) - KV_MEMBER(contact) - KV_MEMBER(block_minor_version) - KV_MEMBER(last_block_reward) - KV_MEMBER(last_block_timestamp) - KV_MEMBER(last_block_difficulty) - } - - std::string status; - std::string version; - uint64_t height; - std::string top_block_hash; - uint64_t difficulty; - uint64_t min_tx_fee; - std::string readable_tx_fee; - uint64_t tx_count; - uint64_t tx_pool_size; - uint64_t alt_blocks_count; - uint64_t outgoing_connections_count; - uint64_t incoming_connections_count; - uint64_t rpc_connections_count; - uint64_t white_peerlist_size; - uint64_t grey_peerlist_size; - uint32_t last_known_block_index; - uint64_t start_time; - std::string fee_address; - uint8_t block_major_version; - std::string already_generated_coins; - std::string contact; - uint8_t block_minor_version; - uint64_t last_block_reward; - uint64_t last_block_timestamp; - uint64_t last_block_difficulty; - }; -}; - -struct COMMAND_RPC_STOP_MINING -{ - typedef EMPTY_STRUCT request; - typedef STATUS_STRUCT response; -}; - -struct COMMAND_RPC_STOP_DAEMON -{ - typedef EMPTY_STRUCT request; - typedef STATUS_STRUCT response; -}; - -struct COMMAND_RPC_GET_PEER_LIST -{ - typedef EMPTY_STRUCT request; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(peers) - KV_MEMBER(status) - } - - std::vector peers; - std::string status; - }; -}; - -struct COMMAND_RPC_GET_FEE_ADDRESS -{ - typedef EMPTY_STRUCT request; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(fee_address) - KV_MEMBER(status) - } - - std::string fee_address; - std::string status; - }; -}; - -struct COMMAND_RPC_GETBLOCKCOUNT -{ - typedef std::vector request; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(count) - KV_MEMBER(status) - } - - uint64_t count; - std::string status; - }; -}; - -struct COMMAND_RPC_GETBLOCKHASH -{ - typedef std::vector request; - typedef std::string response; -}; - -struct COMMAND_RPC_GETBLOCKTEMPLATE -{ - struct request - { - void serialize(ISerializer &s) - { - KV_MEMBER(reserve_size) - KV_MEMBER(wallet_address) - } - - uint64_t reserve_size; // max 255 bytes - std::string wallet_address; - }; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(difficulty) - KV_MEMBER(height) - KV_MEMBER(reserved_offset) - KV_MEMBER(blocktemplate_blob) - KV_MEMBER(blockhashing_blob) - KV_MEMBER(status) - } - - uint64_t difficulty; - uint32_t height; - uint64_t reserved_offset; - std::string blocktemplate_blob; - std::string blockhashing_blob; - std::string status; - }; -}; - -struct COMMAND_RPC_GET_CURRENCY_ID -{ - typedef EMPTY_STRUCT request; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(currency_id_blob) - } - - std::string currency_id_blob; - }; -}; - -struct COMMAND_RPC_SUBMITBLOCK -{ - typedef std::vector request; - typedef STATUS_STRUCT response; -}; - -struct block_header_response -{ - void serialize(ISerializer &s) - { - KV_MEMBER(major_version) - KV_MEMBER(minor_version) - KV_MEMBER(timestamp) - KV_MEMBER(prev_hash) - KV_MEMBER(nonce) - KV_MEMBER(orphan_status) - KV_MEMBER(height) - KV_MEMBER(depth) - KV_MEMBER(hash) - KV_MEMBER(difficulty) - KV_MEMBER(reward) - } - - uint8_t major_version; - uint8_t minor_version; - uint64_t timestamp; - std::string prev_hash; - uint32_t nonce; - bool orphan_status; - uint64_t height; - uint64_t depth; - std::string hash; - difficulty_type difficulty; - uint64_t reward; -}; - -struct BLOCK_HEADER_RESPONSE -{ - void serialize(ISerializer &s) - { - KV_MEMBER(block_header) - KV_MEMBER(status) - } - - std::string status; - block_header_response block_header; -}; - -struct f_transaction_short_response -{ - void serialize(ISerializer &s) - { - KV_MEMBER(hash) - KV_MEMBER(fee) - KV_MEMBER(amount_out) - KV_MEMBER(size) - } - - std::string hash; - uint64_t fee; - uint64_t amount_out; - uint64_t size; -}; - -struct f_transaction_details_extra_response -{ - void serialize(ISerializer &s) - { - KV_MEMBER(padding) - KV_MEMBER(publicKey) - KV_MEMBER(nonce) - KV_MEMBER(raw) - } - - std::vector padding; - Crypto::PublicKey publicKey; - std::vector nonce; - std::vector raw; -}; - -struct f_transaction_details_response -{ - void serialize(ISerializer &s) - { - KV_MEMBER(hash) - KV_MEMBER(size) - KV_MEMBER(paymentId) - KV_MEMBER(mixin) - KV_MEMBER(fee) - KV_MEMBER(amount_out) - KV_MEMBER(confirmations) - KV_MEMBER(extra) - } - - std::string hash; - size_t size; - std::string paymentId; - uint64_t mixin; - uint64_t fee; - uint64_t amount_out; - uint32_t confirmations = 0; - f_transaction_details_extra_response extra; -}; - -struct f_mempool_transaction_response -{ - void serialize(ISerializer &s) - { - KV_MEMBER(hash) - KV_MEMBER(fee) - KV_MEMBER(amount_out) - KV_MEMBER(size) - KV_MEMBER(receiveTime) - KV_MEMBER(keptByBlock) - KV_MEMBER(max_used_block_height) - KV_MEMBER(max_used_block_id) - KV_MEMBER(last_failed_height) - KV_MEMBER(last_failed_id) - } - - std::string hash; - uint64_t fee; - uint64_t amount_out; - uint64_t size; - uint64_t receiveTime; - bool keptByBlock; - uint32_t max_used_block_height; - std::string max_used_block_id; - uint32_t last_failed_height; - std::string last_failed_id; -}; - -struct f_block_short_response -{ - void serialize(ISerializer &s) - { - KV_MEMBER(timestamp) - KV_MEMBER(height) - KV_MEMBER(hash) - KV_MEMBER(cumul_size) - KV_MEMBER(tx_count) - KV_MEMBER(reward) - KV_MEMBER(difficulty) - KV_MEMBER(min_tx_fee) - } - - uint64_t timestamp; - uint32_t height; - std::string hash; - uint64_t tx_count; - uint64_t reward; - uint64_t cumul_size; - difficulty_type difficulty; - uint64_t min_tx_fee; -}; - -struct f_block_details_response -{ - void serialize(ISerializer &s) - { - KV_MEMBER(major_version) - KV_MEMBER(minor_version) - KV_MEMBER(timestamp) - KV_MEMBER(prev_hash) - KV_MEMBER(nonce) - KV_MEMBER(orphan_status) - KV_MEMBER(height) - KV_MEMBER(depth) - KV_MEMBER(hash) - KV_MEMBER(difficulty) - KV_MEMBER(cumulativeDifficulty) - KV_MEMBER(reward) - KV_MEMBER(blockSize) - KV_MEMBER(sizeMedian) - KV_MEMBER(effectiveSizeMedian) - KV_MEMBER(transactionsCumulativeSize) - KV_MEMBER(alreadyGeneratedCoins) - KV_MEMBER(alreadyGeneratedTransactions) - KV_MEMBER(baseReward) - KV_MEMBER(penalty) - KV_MEMBER(totalFeeAmount) - KV_MEMBER(transactions) - } - - uint8_t major_version; - uint8_t minor_version; - uint64_t timestamp; - std::string prev_hash; - uint32_t nonce; - bool orphan_status; - uint64_t height; - uint64_t depth; - std::string hash; - difficulty_type difficulty; - difficulty_type cumulativeDifficulty; - uint64_t reward; - uint64_t blockSize; - size_t sizeMedian; - uint64_t effectiveSizeMedian; - uint64_t transactionsCumulativeSize; - std::string alreadyGeneratedCoins; - uint64_t alreadyGeneratedTransactions; - uint64_t baseReward; - double penalty; - uint64_t totalFeeAmount; - std::vector transactions; -}; - -struct COMMAND_RPC_GET_LAST_BLOCK_HEADER -{ - typedef EMPTY_STRUCT request; - typedef BLOCK_HEADER_RESPONSE response; -}; - -struct COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH -{ - typedef BLOCK_HEADER_RESPONSE response; - - struct request - { - void serialize(ISerializer &s) - { - KV_MEMBER(hash) - } - - std::string hash; - }; -}; - -struct COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT -{ - typedef BLOCK_HEADER_RESPONSE response; - - struct request - { - void serialize(ISerializer &s) - { - KV_MEMBER(height) - } - - uint64_t height; - }; -}; - -struct F_COMMAND_RPC_GET_BLOCKS_LIST -{ - struct request - { - void serialize(ISerializer &s) - { - KV_MEMBER(height) - } - - uint64_t height; - }; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(blocks) - KV_MEMBER(status) - } - - std::vector blocks; // transactions blobs as hex - std::string status; - }; -}; - -struct F_COMMAND_RPC_GET_BLOCK_DETAILS -{ - struct request - { - void serialize(ISerializer &s) - { - KV_MEMBER(hash) - } - - std::string hash; - }; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(block) - KV_MEMBER(status) - } - - f_block_details_response block; - std::string status; - }; -}; - -struct K_COMMAND_RPC_GET_TRANSACTIONS_BY_PAYMENT_ID -{ - struct request - { - void serialize(ISerializer &s) - { - KV_MEMBER(payment_id) - } - - std::string payment_id; - }; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(transactions) - KV_MEMBER(status) - } - - std::vector transactions; - std::string status; - }; -}; - -struct F_COMMAND_RPC_GET_TRANSACTION_DETAILS -{ - struct request - { - void serialize(ISerializer &s) - { - KV_MEMBER(hash) - } - - std::string hash; - }; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(tx) - KV_MEMBER(txDetails) - KV_MEMBER(block) - KV_MEMBER(status) - } - - Transaction tx; - f_transaction_details_response txDetails; - f_block_short_response block; - std::string status; - }; -}; - -struct F_COMMAND_RPC_GET_POOL -{ - typedef EMPTY_STRUCT request; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(transactions) - KV_MEMBER(status) - } - - std::vector transactions; - std::string status; - }; -}; - -struct COMMAND_RPC_GET_MEMPOOL -{ - typedef EMPTY_STRUCT request; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(mempool) - KV_MEMBER(status) - } - - std::vector mempool; - std::string status; - }; -}; - -struct COMMAND_RPC_QUERY_BLOCKS -{ - struct request - { - void serialize(ISerializer &s) - { - serializeAsBinary(block_ids, "block_ids", s); - KV_MEMBER(timestamp) - } - - /*! - First 10 blocks id goes sequential, next goes in pow(2,n) offset, - like 2, 4, 8, 16, 32, 64 and so on, and the last one is always - genesis block - */ - std::vector block_ids; - uint64_t timestamp; - }; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(status) - KV_MEMBER(start_height) - KV_MEMBER(current_height) - KV_MEMBER(full_offset) - KV_MEMBER(items) - } - - std::string status; - uint64_t start_height; - uint64_t current_height; - uint64_t full_offset; - std::vector items; - }; -}; - -struct COMMAND_RPC_QUERY_BLOCKS_LITE -{ - struct request - { - void serialize(ISerializer &s) - { - serializeAsBinary(blockIds, "block_ids", s); - KV_MEMBER(timestamp) - } - - std::vector blockIds; - uint64_t timestamp; - }; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(status) - KV_MEMBER(startHeight) - KV_MEMBER(currentHeight) - KV_MEMBER(fullOffset) - KV_MEMBER(items) - } - - std::string status; - uint64_t startHeight; - uint64_t currentHeight; - uint64_t fullOffset; - std::vector items; - }; -}; - -struct COMMAND_RPC_QUERY_BLOCKS_DETAILED -{ - struct request - { - void serialize(ISerializer &s) - { - KV_MEMBER(blockIds) - KV_MEMBER(timestamp) - } - - std::vector blockIds; - uint64_t timestamp; - }; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(status) - KV_MEMBER(startHeight) - KV_MEMBER(currentHeight) - KV_MEMBER(fullOffset) - KV_MEMBER(blocks) - } - - std::string status; - uint64_t startHeight; - uint64_t currentHeight; - uint64_t fullOffset; - std::vector blocks; - }; -}; - -struct COMMAND_RPC_GEN_PAYMENT_ID -{ - typedef EMPTY_STRUCT request; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(payment_id) - } - - std::string payment_id; - }; -}; - -struct K_COMMAND_RPC_CHECK_TX_KEY -{ - struct request - { - void serialize(ISerializer &s) - { - KV_MEMBER(txid) - KV_MEMBER(txkey) - KV_MEMBER(address) - } - - std::string txid; - std::string txkey; - std::string address; - }; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(amount) - KV_MEMBER(outputs) - KV_MEMBER(status) - } - - uint64_t amount; - std::vector outputs; - std::string status; - }; -}; - -struct K_COMMAND_RPC_CHECK_TX_WITH_PRIVATE_VIEW_KEY -{ - struct request - { - void serialize(ISerializer &s) - { - KV_MEMBER(txid) - KV_MEMBER(view_key) - KV_MEMBER(address) - } - - std::string txid; - std::string view_key; - std::string address; - }; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(amount) - KV_MEMBER(outputs) - KV_MEMBER(confirmations) - KV_MEMBER(status) - } - - uint64_t amount; - std::vector outputs; - uint32_t confirmations = 0; - std::string status; - }; -}; - -struct COMMAND_RPC_VALIDATE_ADDRESS -{ - struct request - { - void serialize(ISerializer &s) - { - KV_MEMBER(address) - } - - std::string address; - }; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(isvalid) - KV_MEMBER(address) - KV_MEMBER(spendPublicKey) - KV_MEMBER(viewPublicKey) - KV_MEMBER(status) - } - - bool isvalid; - std::string address; - std::string spendPublicKey; - std::string viewPublicKey; - std::string status; - }; -}; - -struct COMMAND_RPC_VERIFY_MESSAGE -{ - struct request - { - void serialize(ISerializer &s) - { - KV_MEMBER(message) - KV_MEMBER(address) - KV_MEMBER(signature) - } - - std::string message; - std::string address; - std::string signature; - }; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(sig_valid) - KV_MEMBER(status) - } - - bool sig_valid; - std::string status; - }; -}; - -struct COMMAND_RPC_GET_BLOCKS_DETAILS_BY_HEIGHTS -{ - struct request - { - void serialize(ISerializer &s) - { - KV_MEMBER(blockHeights) - } - - std::vector blockHeights; - }; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(status) - KV_MEMBER(blocks) - } - - std::vector blocks; - std::string status; - }; -}; - -struct COMMAND_RPC_GET_BLOCKS_DETAILS_BY_HASHES -{ - struct request - { - void serialize(ISerializer &s) - { - KV_MEMBER(blockHashes) - } - - std::vector blockHashes; - }; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(status) - KV_MEMBER(blocks) - } - - std::vector blocks; - std::string status; - }; -}; - -struct COMMAND_RPC_GET_BLOCK_DETAILS_BY_HEIGHT -{ - struct request - { - void serialize(ISerializer &s) - { - KV_MEMBER(blockHeight) - } - - uint32_t blockHeight; - }; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(status) - KV_MEMBER(block) - } - - BlockDetails2 block; - std::string status; - }; -}; - -struct COMMAND_RPC_GET_BLOCKS_HASHES_BY_TIMESTAMPS -{ - struct request - { - void serialize(ISerializer &s) - { - KV_MEMBER(timestampBegin) - KV_MEMBER(timestampEnd) - KV_MEMBER(limit) - } - - uint64_t timestampBegin; - uint64_t timestampEnd; - uint32_t limit; - }; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(status) - KV_MEMBER(count) - KV_MEMBER(blockHashes) - } - - std::vector blockHashes; - uint32_t count; - std::string status; - }; -}; - -struct COMMAND_RPC_GET_TRANSACTION_HASHES_BY_PAYMENT_ID -{ - struct request - { - void serialize(ISerializer &s) - { - KV_MEMBER(paymentId) - } - - Crypto::Hash paymentId; - }; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(status) - KV_MEMBER(transactionHashes) - } - - std::vector transactionHashes; - std::string status; - }; -}; - -struct COMMAND_RPC_GET_TRANSACTIONS_DETAILS_BY_HASHES -{ - struct request - { - void serialize(ISerializer &s) - { - KV_MEMBER(transactionHashes) - } - - std::vector transactionHashes; - }; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(status) - KV_MEMBER(transactions) - } - - std::vector transactions; - std::string status; - }; -}; - -struct COMMAND_RPC_GET_TRANSACTION_DETAILS_BY_HASH -{ - struct request - { - void serialize(ISerializer &s) - { - KV_MEMBER(hash) - } - - Crypto::Hash hash; - }; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(status) - KV_MEMBER(transaction) - } - - TransactionDetails2 transaction; - std::string status; - }; -}; - -struct difficulty_statistics -{ - void serialize(ISerializer &s) - { - KV_MEMBER(block_num) - KV_MEMBER(avg_solve_time) - KV_MEMBER(stddev_solve_time) - KV_MEMBER(outliers_num) - KV_MEMBER(avg_diff) - KV_MEMBER(min_diff) - KV_MEMBER(max_diff) - } - - uint32_t block_num; - uint64_t avg_solve_time; - uint64_t stddev_solve_time; - uint32_t outliers_num; - difficulty_type avg_diff; - difficulty_type min_diff; - difficulty_type max_diff; -}; - -struct COMMAND_RPC_GET_DIFFICULTY_STAT -{ - struct request - { - void serialize(ISerializer &s) - { - KV_MEMBER(height) - } - - uint32_t height; - - }; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(status) - KV_MEMBER(hour) - KV_MEMBER(day) - KV_MEMBER(week) - KV_MEMBER(month) - KV_MEMBER(halfyear) - KV_MEMBER(year) - KV_MEMBER(blocks30) - KV_MEMBER(blocks720) - KV_MEMBER(blocks5040) - KV_MEMBER(blocks21900) - KV_MEMBER(blocks131400) - KV_MEMBER(blocks262800) - } - - std::string status; - difficulty_statistics hour; - difficulty_statistics day; - difficulty_statistics week; - difficulty_statistics month; - difficulty_statistics halfyear; - difficulty_statistics year; - difficulty_statistics blocks30; - difficulty_statistics blocks720; - difficulty_statistics blocks5040; - difficulty_statistics blocks21900; - difficulty_statistics blocks131400; - difficulty_statistics blocks262800; - }; -}; - -struct reserve_proof_entry -{ - void serialize(ISerializer &s) - { - KV_MEMBER(txid) - KV_MEMBER(index_in_tx) - KV_MEMBER(shared_secret) - KV_MEMBER(key_image) - KV_MEMBER(shared_secret_sig) - KV_MEMBER(key_image_sig) - } - - Crypto::Hash txid; - uint64_t index_in_tx; - Crypto::PublicKey shared_secret; - Crypto::KeyImage key_image; - Crypto::Signature shared_secret_sig; - Crypto::Signature key_image_sig; -}; - -struct reserve_proof -{ - void serialize(ISerializer &s) - { - KV_MEMBER(proofs) - KV_MEMBER(signature) - } - - std::vector proofs; - Crypto::Signature signature; -}; - -struct K_COMMAND_RPC_CHECK_TX_PROOF -{ - struct request - { - void serialize(ISerializer &s) - { - KV_MEMBER(tx_id) - KV_MEMBER(dest_address) - KV_MEMBER(signature) - } - - std::string tx_id; - std::string dest_address; - std::string signature; - }; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(signature_valid) - KV_MEMBER(received_amount) - KV_MEMBER(outputs) - KV_MEMBER(confirmations) - KV_MEMBER(status) - } - - bool signature_valid; - uint64_t received_amount; - std::vector outputs; - uint32_t confirmations = 0; - std::string status; - }; -}; - -struct K_COMMAND_RPC_CHECK_RESERVE_PROOF -{ - struct request - { - void serialize(ISerializer &s) - { - KV_MEMBER(address) - KV_MEMBER(message) - KV_MEMBER(signature) - } - - std::string address; - std::string message; - std::string signature; - }; - - struct response - { - void serialize(ISerializer &s) - { - KV_MEMBER(good) - KV_MEMBER(total) - KV_MEMBER(spent) - } - - bool good; - uint64_t total; - uint64_t spent; - }; -}; + struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_outs_for_amount + { + void serialize(ISerializer &s) + { + KV_MEMBER(amount) + serializeAsBinary(outs, "outs", s); + } + + uint64_t amount; + std::vector outs; + }; + + struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response + { + void serialize(ISerializer &s) + { + KV_MEMBER(outs) + KV_MEMBER(status) + } + + std::vector outs; + std::string status; + }; + + struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS + { + typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request request; + typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response response; + typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_out_entry out_entry; + typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_outs_for_amount outs_for_amount; + }; + + struct COMMAND_RPC_SEND_RAW_TX + { + struct request + { + request() = default; + + explicit request(const Transaction &); + + void serialize(ISerializer &s) { KV_MEMBER(tx_as_hex) } + + std::string tx_as_hex; + }; + + struct response + { + void serialize(ISerializer &s) { KV_MEMBER(status) } + + std::string status; + }; + }; + + struct COMMAND_RPC_START_MINING + { + struct request + { + void serialize(ISerializer &s) + { + KV_MEMBER(miner_address) + KV_MEMBER(threads_count) + } + + std::string miner_address; + uint64_t threads_count; + }; + + struct response + { + void serialize(ISerializer &s) { KV_MEMBER(status) } + + std::string status; + }; + }; + + struct COMMAND_HTTP + { + typedef EMPTY_STRUCT request; + + typedef std::string response; + }; + + struct COMMAND_RPC_GET_INFO + { + typedef EMPTY_STRUCT request; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(status) + KV_MEMBER(version) + KV_MEMBER(height) + KV_MEMBER( + top_block_hash) + KV_MEMBER(difficulty) + KV_MEMBER(min_tx_fee) + KV_MEMBER(readable_tx_fee) + KV_MEMBER(tx_count) + KV_MEMBER( + tx_pool_size) + KV_MEMBER(alt_blocks_count) + KV_MEMBER(outgoing_connections_count) + KV_MEMBER( + incoming_connections_count) + KV_MEMBER(rpc_connections_count) + KV_MEMBER(white_peerlist_size) + KV_MEMBER( + grey_peerlist_size) + KV_MEMBER(last_known_block_index) + KV_MEMBER(start_time) + KV_MEMBER(fee_address) + KV_MEMBER( + block_major_version) + KV_MEMBER(already_generated_coins) + KV_MEMBER( + contact) + KV_MEMBER(block_minor_version) + KV_MEMBER(last_block_reward) + KV_MEMBER( + last_block_timestamp) + KV_MEMBER(last_block_difficulty) + } + + std::string status; + std::string version; + uint64_t height; + std::string top_block_hash; + uint64_t difficulty; + uint64_t min_tx_fee; + std::string readable_tx_fee; + uint64_t tx_count; + uint64_t tx_pool_size; + uint64_t alt_blocks_count; + uint64_t outgoing_connections_count; + uint64_t incoming_connections_count; + uint64_t rpc_connections_count; + uint64_t white_peerlist_size; + uint64_t grey_peerlist_size; + uint32_t last_known_block_index; + uint64_t start_time; + std::string fee_address; + uint8_t block_major_version; + std::string already_generated_coins; + std::string contact; + uint8_t block_minor_version; + uint64_t last_block_reward; + uint64_t last_block_timestamp; + uint64_t last_block_difficulty; + }; + }; + + struct COMMAND_RPC_GET_VERSION + { + typedef EMPTY_STRUCT request; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(version) + } + + std::string version; + }; + }; + + struct COMMAND_RPC_GET_HARDWARE_INFO + { + typedef EMPTY_STRUCT request; + + struct CPUEntry + { + void serialize (ISerializer &s) + { + KV_MEMBER(coreCount) + KV_MEMBER(threadCount) + KV_MEMBER(architecture) + } + + uint8_t coreCount; + uint8_t threadCount; + std::string architecture; + }; + + struct RAMEntry + { + void serialize (ISerializer &s) + { + KV_MEMBER(ramTotal) + KV_MEMBER(ramUsedTotal) + KV_MEMBER(ramAvailable) + KV_MEMBER(ramUsageVirt) + KV_MEMBER(ramUsagePhys) + KV_MEMBER(ramUsageVirtMax) + KV_MEMBER(ramUsagePhysMax) + } + + uint32_t ramTotal; + uint32_t ramUsedTotal; + uint32_t ramAvailable; + uint32_t ramUsageVirt; + uint32_t ramUsagePhys; + uint32_t ramUsageVirtMax; + uint32_t ramUsagePhysMax; + }; + + struct SpaceEntry + { + void serialize (ISerializer &s) + { + KV_MEMBER(freeSpace) + KV_MEMBER(availableSpace) + KV_MEMBER(capacitySpace) + } + + std::string freeSpace; + std::string availableSpace; + std::string capacitySpace; + }; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(status) + KV_MEMBER(uptime) + KV_MEMBER(cpuInfo) + KV_MEMBER(ramInfo) + KV_MEMBER(spaceInfo) + } + + CPUEntry cpuInfo; + RAMEntry ramInfo; + SpaceEntry spaceInfo; + std::string uptime; + std::string status; + + }; + }; + + struct COMMAND_RPC_STOP_MINING + { + typedef EMPTY_STRUCT request; + typedef STATUS_STRUCT response; + }; + + struct COMMAND_RPC_STOP_DAEMON + { + typedef EMPTY_STRUCT request; + typedef STATUS_STRUCT response; + }; + + struct COMMAND_RPC_GET_PEER_LIST + { + typedef EMPTY_STRUCT request; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(peers) + KV_MEMBER(status) + } + + std::vector peers; + std::string status; + }; + }; + + struct COMMAND_RPC_GET_FEE_ADDRESS + { + typedef EMPTY_STRUCT request; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(fee_address) + KV_MEMBER(status) + } + + std::string fee_address; + std::string status; + }; + }; + + struct COMMAND_RPC_GET_BLOCK_COUNT + { + typedef std::vector request; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(count) + KV_MEMBER(status) + } + + uint64_t count; + std::string status; + }; + }; + + struct COMMAND_RPC_GET_BLOCK_HASH + { + typedef std::vector request; + typedef std::string response; + }; + + struct COMMAND_RPC_GET_BLOCK_TEMPLATE + { + struct request + { + void serialize(ISerializer &s) + { + KV_MEMBER(reserve_size) + KV_MEMBER(wallet_address) + } + + uint64_t reserve_size; // max 255 bytes + std::string wallet_address; + }; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(difficulty) + KV_MEMBER(height) + KV_MEMBER(reserved_offset) + KV_MEMBER(blocktemplate_blob) + KV_MEMBER(blockhashing_blob) + KV_MEMBER(status) + } + + uint64_t difficulty; + uint32_t height; + uint64_t reserved_offset; + std::string blocktemplate_blob; + std::string blockhashing_blob; + std::string status; + }; + }; + + struct COMMAND_RPC_GET_CURRENCY_ID + { + typedef EMPTY_STRUCT request; + + struct response + { + void serialize(ISerializer &s) { KV_MEMBER(currency_id_blob) } + + std::string currency_id_blob; + }; + }; + + struct COMMAND_RPC_SUBMIT_BLOCK + { + typedef std::vector request; + typedef STATUS_STRUCT response; + }; + + struct BLOCK_HEADER_RESPONSE_ENTRY + { + void serialize(ISerializer &s) + { + KV_MEMBER(major_version) + KV_MEMBER(minor_version) + KV_MEMBER(timestamp) + KV_MEMBER(prev_hash) + KV_MEMBER(nonce) + KV_MEMBER(orphan_status) + KV_MEMBER(height) + KV_MEMBER(depth) + KV_MEMBER(hash) + KV_MEMBER(difficulty) + KV_MEMBER(reward) + } + + uint8_t major_version; + uint8_t minor_version; + uint64_t timestamp; + std::string prev_hash; + uint32_t nonce; + bool orphan_status; + uint64_t height; + uint64_t depth; + std::string hash; + difficulty_type difficulty; + uint64_t reward; + }; + + struct BLOCK_HEADER_RESPONSE + { + void serialize(ISerializer &s) + { + KV_MEMBER(block_header) + KV_MEMBER(status) + } + + std::string status; + BLOCK_HEADER_RESPONSE_ENTRY block_header; + }; + + struct TRANSACTION_SHORT_RESPONSE + { + void serialize(ISerializer &s) + { + KV_MEMBER(hash) + KV_MEMBER(fee) + KV_MEMBER(amount_out) + KV_MEMBER(size) + } + + std::string hash; + uint64_t fee; + uint64_t amount_out; + uint64_t size; + }; + + struct TRANSACTION_DETAILS_EXTRA_RESPONSE + { + void serialize(ISerializer &s) + { + KV_MEMBER(padding) + KV_MEMBER(publicKey) + KV_MEMBER(nonce) + KV_MEMBER(raw) + } + + std::vector padding; + Crypto::PublicKey publicKey; + std::vector nonce; + std::vector raw; + }; + + struct TRANSACTION_DETAILS_RESPONSE + { + void serialize(ISerializer &s) + { + KV_MEMBER(hash) + KV_MEMBER(size) + KV_MEMBER(paymentId) + KV_MEMBER(mixin) + KV_MEMBER(fee) + KV_MEMBER(amount_out) + KV_MEMBER(confirmations) + KV_MEMBER(extra) + } + + std::string hash; + size_t size; + std::string paymentId; + uint64_t mixin; + uint64_t fee; + uint64_t amount_out; + uint32_t confirmations = 0; + TRANSACTION_DETAILS_EXTRA_RESPONSE extra; + }; + + struct MEMPOOL_TRANSACTION_RESPONSE + { + void serialize(ISerializer &s) + { + KV_MEMBER(hash) + KV_MEMBER(fee) + KV_MEMBER(amount_out) + KV_MEMBER(size) + KV_MEMBER(receiveTime) + KV_MEMBER(keptByBlock) + KV_MEMBER(max_used_block_height) + KV_MEMBER(max_used_block_id) + KV_MEMBER(last_failed_height) + KV_MEMBER(last_failed_id) + KV_MEMBER(tx_json) + } + + std::string hash; + uint64_t fee; + uint64_t amount_out; + uint64_t size; + uint64_t receiveTime; + bool keptByBlock; + uint32_t max_used_block_height; + std::string max_used_block_id; + uint32_t last_failed_height; + std::string last_failed_id; + Transaction tx_json; + }; + + struct BLOCK_SHORT_RESPONSE + { + void serialize(ISerializer &s) + { + KV_MEMBER(timestamp) + KV_MEMBER(height) + KV_MEMBER(hash) + KV_MEMBER(cumul_size) + KV_MEMBER(tx_count) + KV_MEMBER(reward) + KV_MEMBER(difficulty) + KV_MEMBER(min_tx_fee) + } + + uint64_t timestamp; + uint32_t height; + std::string hash; + uint64_t tx_count; + uint64_t reward; + uint64_t cumul_size; + difficulty_type difficulty; + uint64_t min_tx_fee; + }; + + struct BLOCK_DETAILS_RESPONSE + { + void serialize(ISerializer &s) + { + KV_MEMBER(major_version) + KV_MEMBER(minor_version) + KV_MEMBER(timestamp) + KV_MEMBER(prev_hash) + KV_MEMBER(nonce) + KV_MEMBER(orphan_status) + KV_MEMBER(height) + KV_MEMBER( + depth) + KV_MEMBER(hash) + KV_MEMBER(difficulty) + KV_MEMBER(cumulativeDifficulty) + KV_MEMBER(reward) + KV_MEMBER(blockSize) + KV_MEMBER(sizeMedian) + KV_MEMBER(effectiveSizeMedian) + KV_MEMBER(transactionsCumulativeSize) + KV_MEMBER(alreadyGeneratedCoins) + KV_MEMBER(alreadyGeneratedTransactions) + KV_MEMBER(baseReward) + KV_MEMBER(penalty) + KV_MEMBER(totalFeeAmount) + KV_MEMBER(transactions) + } + + uint8_t major_version; + uint8_t minor_version; + uint64_t timestamp; + std::string prev_hash; + uint32_t nonce; + bool orphan_status; + uint64_t height; + uint64_t depth; + std::string hash; + difficulty_type difficulty; + difficulty_type cumulativeDifficulty; + uint64_t reward; + uint64_t blockSize; + size_t sizeMedian; + uint64_t effectiveSizeMedian; + uint64_t transactionsCumulativeSize; + std::string alreadyGeneratedCoins; + uint64_t alreadyGeneratedTransactions; + uint64_t baseReward; + double penalty; + uint64_t totalFeeAmount; + std::vector transactions; + }; + + struct COMMAND_RPC_GET_LAST_BLOCK_HEADER + { + typedef EMPTY_STRUCT request; + typedef BLOCK_HEADER_RESPONSE response; + }; + + struct COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH + { + typedef BLOCK_HEADER_RESPONSE response; + + struct request + { + void serialize(ISerializer &s) { KV_MEMBER(hash) } + + std::string hash; + }; + }; + + struct COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT + { + typedef BLOCK_HEADER_RESPONSE response; + + struct request + { + void serialize(ISerializer &s) { KV_MEMBER(height) } + + uint64_t height; + }; + }; + + struct COMMAND_RPC_GET_BLOCK_DETAILS_BY_HASH + { + struct request + { + std::string hash; + + void serialize(ISerializer &s) { KV_MEMBER(hash) } + }; + + struct response + { + BlockDetails2 block; + std::string status; + + void serialize(ISerializer &s) + { + KV_MEMBER(status) + KV_MEMBER(block) + } + }; + }; + + struct COMMAND_RPC_GET_BLOCKS_LIST + { + struct request + { + void serialize(ISerializer &s) { KV_MEMBER(height) } + + uint64_t height; + }; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(blocks) + KV_MEMBER(status) + } + + std::vector blocks; // transactions blobs as hex + std::string status; + }; + }; + + struct COMMAND_RPC_GET_BLOCK_DETAILS + { + struct request + { + void serialize(ISerializer &s) { KV_MEMBER(hash) } + + std::string hash; + }; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(block) + KV_MEMBER(status) + } + + BLOCK_DETAILS_RESPONSE block; + std::string status; + }; + }; + + struct COMMAND_RPC_GET_TRANSACTIONS_BY_PAYMENT_ID + { + struct request + { + void serialize(ISerializer &s) { KV_MEMBER(payment_id) } + + std::string payment_id; + }; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(transactions) + KV_MEMBER(status) + } + + std::vector transactions; + std::string status; + }; + }; + + struct COMMAND_RPC_GET_TRANSACTION_DETAILS + { + struct request + { + void serialize(ISerializer &s) { KV_MEMBER(hash) } + + std::string hash; + }; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(tx) + KV_MEMBER(txDetails) + KV_MEMBER(block) + KV_MEMBER(status) + } + + Transaction tx; + TRANSACTION_DETAILS_RESPONSE txDetails; + BLOCK_SHORT_RESPONSE block; + std::string status; + }; + }; + + struct COMMAND_RPC_GET_POOL + { + typedef EMPTY_STRUCT request; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(transactions) + KV_MEMBER(status) + } + + std::vector transactions; + std::string status; + }; + }; + + struct COMMAND_RPC_GET_MEMPOOL + { + typedef EMPTY_STRUCT request; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(transactions) + KV_MEMBER(status) + } + + std::vector transactions; + std::string status; + }; + }; + + struct COMMAND_RPC_QUERY_BLOCKS + { + struct request + { + void serialize(ISerializer &s) + { + serializeAsBinary(block_ids, "block_ids", s); + KV_MEMBER(timestamp) + } + + /*! + First 10 blocks id goes sequential, next goes in pow(2,n) offset, + like 2, 4, 8, 16, 32, 64 and so on, and the last one is always + genesis block + */ + std::vector block_ids; + uint64_t timestamp; + }; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(status) + KV_MEMBER(start_height) + KV_MEMBER(current_height) + KV_MEMBER(full_offset) + KV_MEMBER(items) + } + + std::string status; + uint64_t start_height; + uint64_t current_height; + uint64_t full_offset; + std::vector items; + }; + }; + + struct COMMAND_RPC_QUERY_BLOCKS_LITE + { + struct request + { + void serialize(ISerializer &s) + { + serializeAsBinary(blockIds, "block_ids", s); + KV_MEMBER(timestamp) + } + + std::vector blockIds; + uint64_t timestamp; + }; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(status) + KV_MEMBER(startHeight) + KV_MEMBER(currentHeight) + KV_MEMBER(fullOffset) + KV_MEMBER(items) + } + + std::string status; + uint64_t startHeight; + uint64_t currentHeight; + uint64_t fullOffset; + std::vector items; + }; + }; + + struct COMMAND_RPC_QUERY_BLOCKS_DETAILED + { + struct request + { + void serialize(ISerializer &s) + { + KV_MEMBER(blockIds) + KV_MEMBER(timestamp) + } + + std::vector blockIds; + uint64_t timestamp; + }; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(status) + KV_MEMBER(startHeight) + KV_MEMBER(currentHeight) + KV_MEMBER(fullOffset) + KV_MEMBER(blocks) + } + + std::string status; + uint64_t startHeight; + uint64_t currentHeight; + uint64_t fullOffset; + std::vector blocks; + }; + }; + + struct COMMAND_RPC_GEN_PAYMENT_ID + { + typedef EMPTY_STRUCT request; + + struct response + { + void serialize(ISerializer &s) { KV_MEMBER(payment_id) } + + std::string payment_id; + }; + }; + + struct COMMAND_RPC_CHECK_TX_KEY + { + struct request + { + void serialize(ISerializer &s) + { + KV_MEMBER(txid) + KV_MEMBER(txkey) + KV_MEMBER(address) + } + + std::string txid; + std::string txkey; + std::string address; + }; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(amount) + KV_MEMBER(outputs) + KV_MEMBER(status) + } + + uint64_t amount; + std::vector outputs; + std::string status; + }; + }; + + struct COMMAND_RPC_CHECK_TX_WITH_PRIVATE_VIEW_KEY + { + struct request + { + void serialize(ISerializer &s) + { + KV_MEMBER(txid) + KV_MEMBER(view_key) + KV_MEMBER(address) + } + + std::string txid; + std::string view_key; + std::string address; + }; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(amount) + KV_MEMBER(outputs) + KV_MEMBER(confirmations) + KV_MEMBER(status) + } + + uint64_t amount; + std::vector outputs; + uint32_t confirmations = 0; + std::string status; + }; + }; + + struct COMMAND_RPC_VALIDATE_ADDRESS + { + struct request + { + void serialize(ISerializer &s) { KV_MEMBER(address) } + + std::string address; + }; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(isvalid) + KV_MEMBER(address) + KV_MEMBER(spendPublicKey) + KV_MEMBER(viewPublicKey) + KV_MEMBER(status) + } + + bool isvalid; + std::string address; + std::string spendPublicKey; + std::string viewPublicKey; + std::string status; + }; + }; + + struct COMMAND_RPC_VERIFY_MESSAGE + { + struct request + { + void serialize(ISerializer &s) + { + KV_MEMBER(message) + KV_MEMBER(address) + KV_MEMBER(signature) + } + + std::string message; + std::string address; + std::string signature; + }; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(sig_valid) + KV_MEMBER(status) + } + + bool sig_valid; + std::string status; + }; + }; + + struct COMMAND_RPC_GET_BLOCKS_DETAILS_BY_HEIGHTS + { + struct request + { + void serialize(ISerializer &s) { KV_MEMBER(blockHeights) } + + std::vector blockHeights; + }; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(status) + KV_MEMBER(blocks) + } + + std::vector blocks; + std::string status; + }; + }; + + struct COMMAND_RPC_GET_BLOCKS_DETAILS_BY_HASHES + { + struct request + { + void serialize(ISerializer &s) { KV_MEMBER(blockHashes) } + + std::vector blockHashes; + }; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(status) + KV_MEMBER(blocks) + } + + std::vector blocks; + std::string status; + }; + }; + + struct COMMAND_RPC_GET_BLOCK_DETAILS_BY_HEIGHT + { + struct request + { + void serialize(ISerializer &s) { KV_MEMBER(blockHeight) } + + uint32_t blockHeight; + }; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(status) + KV_MEMBER(block) + } + + BlockDetails2 block; + std::string status; + }; + }; + + struct COMMAND_RPC_GET_BLOCKS_HASHES_BY_TIMESTAMPS + { + struct request + { + void serialize(ISerializer &s) + { + KV_MEMBER(timestampBegin) + KV_MEMBER(timestampEnd) + KV_MEMBER(limit) + } + + uint64_t timestampBegin; + uint64_t timestampEnd; + uint32_t limit; + }; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(status) + KV_MEMBER(count) + KV_MEMBER(blockHashes) + } + + std::vector blockHashes; + uint32_t count; + std::string status; + }; + }; + + struct COMMAND_RPC_GET_TRANSACTION_HASHES_BY_PAYMENT_ID + { + struct request + { + std::string paymentId; + + void serialize(ISerializer &s) { KV_MEMBER(paymentId) } + }; + + struct response + { + std::vector transactionHashes; + std::string status; + + void serialize(ISerializer &s) + { + KV_MEMBER(status) + KV_MEMBER(transactionHashes); + } + }; + }; + + struct COMMAND_RPC_GET_TRANSACTIONS_DETAILS_BY_HASHES + { + struct request + { + void serialize(ISerializer &s) { KV_MEMBER(transactionHashes) } + + std::vector transactionHashes; + }; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(status) + KV_MEMBER(transactions) + } + + std::vector transactions; + std::string status; + }; + }; + + struct COMMAND_RPC_GET_TRANSACTION_DETAILS_BY_HASH + { + struct request + { + std::string hash; + + void serialize(ISerializer &s) { KV_MEMBER(hash); } + }; + + struct response + { + TransactionDetails2 transaction; + std::string status; + + void serialize(ISerializer &s) + { + KV_MEMBER(status) + KV_MEMBER(transaction) + } + }; + }; + + struct DIFFICULTY_STATISTICS + { + void serialize(ISerializer &s) + { + KV_MEMBER(block_num) + KV_MEMBER(avg_solve_time) + KV_MEMBER(stddev_solve_time) + KV_MEMBER(outliers_num) + KV_MEMBER(avg_diff) + KV_MEMBER(min_diff) + KV_MEMBER(max_diff) + } + + uint32_t block_num; + uint64_t avg_solve_time; + uint64_t stddev_solve_time; + uint32_t outliers_num; + difficulty_type avg_diff; + difficulty_type min_diff; + difficulty_type max_diff; + }; + + struct COMMAND_RPC_GET_DIFFICULTY_STAT + { + struct request + { + void serialize(ISerializer &s) { KV_MEMBER(height) } + + uint32_t height; + }; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(status) + KV_MEMBER(hour) + KV_MEMBER(day) + KV_MEMBER(week) + KV_MEMBER(month) + KV_MEMBER(halfyear) + KV_MEMBER(year) + KV_MEMBER(blocks30) + KV_MEMBER(blocks720) + KV_MEMBER(blocks5040) + KV_MEMBER(blocks21900) + KV_MEMBER(blocks131400) + KV_MEMBER(blocks262800) + } + + std::string status; + DIFFICULTY_STATISTICS hour; + DIFFICULTY_STATISTICS day; + DIFFICULTY_STATISTICS week; + DIFFICULTY_STATISTICS month; + DIFFICULTY_STATISTICS halfyear; + DIFFICULTY_STATISTICS year; + DIFFICULTY_STATISTICS blocks30; + DIFFICULTY_STATISTICS blocks720; + DIFFICULTY_STATISTICS blocks5040; + DIFFICULTY_STATISTICS blocks21900; + DIFFICULTY_STATISTICS blocks131400; + DIFFICULTY_STATISTICS blocks262800; + }; + }; + + struct RESERVE_PROOF_ENTRY + { + void serialize(ISerializer &s) + { + KV_MEMBER(txid) + KV_MEMBER(index_in_tx) + KV_MEMBER(shared_secret) + KV_MEMBER(key_image) + KV_MEMBER(shared_secret_sig) + KV_MEMBER(key_image_sig) + } + + Crypto::Hash txid; + uint64_t index_in_tx; + Crypto::PublicKey shared_secret; + Crypto::KeyImage key_image; + Crypto::Signature shared_secret_sig; + Crypto::Signature key_image_sig; + }; + + struct RESERVE_PROOF + { + void serialize(ISerializer &s) + { + KV_MEMBER(proofs) + KV_MEMBER(signature) + } + + std::vector proofs; + Crypto::Signature signature; + }; + + struct COMMAND_RPC_CHECK_TX_PROOF + { + struct request + { + void serialize(ISerializer &s) + { + KV_MEMBER(tx_id) + KV_MEMBER(dest_address) + KV_MEMBER(signature) + } + + std::string tx_id; + std::string dest_address; + std::string signature; + }; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(signature_valid) + KV_MEMBER(received_amount) + KV_MEMBER(outputs) + KV_MEMBER(confirmations) + KV_MEMBER(status) + } + + bool signature_valid; + uint64_t received_amount; + std::vector outputs; + uint32_t confirmations = 0; + std::string status; + }; + }; + + struct COMMAND_RPC_CHECK_RESERVE_PROOF + { + struct request + { + void serialize(ISerializer &s) + { + KV_MEMBER(address) + KV_MEMBER(message) + KV_MEMBER(signature) + } + + std::string address; + std::string message; + std::string signature; + }; + + struct response + { + void serialize(ISerializer &s) + { + KV_MEMBER(good) + KV_MEMBER(total) + KV_MEMBER(spent) + } + + bool good; + uint64_t total; + uint64_t spent; + }; + }; } // namespace CryptoNote diff --git a/lib/Rpc/HttpServer.cpp b/lib/Rpc/HttpServer.cpp index 4b177428..9a559d1d 100644 --- a/lib/Rpc/HttpServer.cpp +++ b/lib/Rpc/HttpServer.cpp @@ -113,7 +113,6 @@ void HttpServer::acceptLoop() HttpRequest req; HttpResponse resp; resp.addHeader("Access-Control-Allow-Origin", "*"); - resp.addHeader("content-type", "application/json"); parser.receiveRequest(stream, req); if (authenticate(req)) { diff --git a/lib/Rpc/RpcServer.cpp b/lib/Rpc/RpcServer.cpp index 83d31b41..619776a1 100644 --- a/lib/Rpc/RpcServer.cpp +++ b/lib/Rpc/RpcServer.cpp @@ -19,11 +19,14 @@ // You should have received a copy of the GNU Lesser General Public License // along with Qwertycoin. If not, see . +#include #include #include #include #include #include +#include +#include #include #include #include @@ -40,2789 +43,3152 @@ #undef ERROR // TODO: WTF!? +const uint32_t MAX_NUMBER_OF_BLOCKS_PER_STATS_REQUEST = 10000; +const uint64_t BLOCK_LIST_MAX_COUNT = 1000; + using namespace Crypto; using namespace Common; using namespace Logging; -static const Crypto::SecretKey I = {{ - 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 -}}; +static const Crypto::SecretKey I = {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; namespace CryptoNote { -namespace { - -template -RpcServer::HandlerFunction binMethod( - bool (RpcServer::*handler)(typename Command::request const &, typename Command::response &)) -{ - return [handler](RpcServer *obj, const HttpRequest &request, HttpResponse &response) { - boost::value_initialized req; - boost::value_initialized res; - - if (!loadFromBinaryKeyValue(static_cast(req), - request.getBody())) { - return false; - } - - bool result = (obj->*handler)(req, res); - response.setBody(storeToBinaryKeyValue(res.data())); - - return result; - }; -} - -template -RpcServer::HandlerFunction jsonMethod( - bool (RpcServer::*handler)(typename Command::request const &, typename Command::response &)) -{ - return [handler](RpcServer *obj, const HttpRequest &request, HttpResponse &response) { - boost::value_initialized req; - boost::value_initialized res; - - if (!loadFromJson(static_cast(req), request.getBody())) { - return false; - } - - bool result = (obj->*handler)(req, res); - std::string cors_domain = obj->getCorsDomain(); - if (!cors_domain.empty()) { - response.addHeader("Access-Control-Allow-Origin", cors_domain); - response.addHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); - response.addHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS"); - } - response.addHeader("Content-Type", "application/json"); - response.setBody(storeToJson(res.data())); - - return result; - }; -} - -} // namespace - -std::unordered_map< - std::string, - RpcServer::RpcHandler -> RpcServer::s_handlers = { - // binary handlers - { - "/getblocks.bin", - { binMethod(&RpcServer::on_get_blocks), false } - },{ - "/queryblocks.bin", - { binMethod(&RpcServer::on_query_blocks), false } - },{ - "/queryblockslite.bin", - { binMethod(&RpcServer::on_query_blocks_lite), false } - }, - - { - "/get_o_indexes.bin", - { binMethod(&RpcServer::on_get_indexes), false } - },{ - "/getrandom_outs.bin", - { - binMethod(&RpcServer::on_get_random_outs), - false - } - },{ - "/get_pool_changes.bin", - { binMethod(&RpcServer::onGetPoolChanges), false } - },{ - "/get_pool_changes_lite.bin", - { binMethod(&RpcServer::onGetPoolChangesLite), false } - }, - - // json handlers - { - "/getinfo", - { jsonMethod(&RpcServer::on_get_info), true } - },{ - "/getheight", - { jsonMethod(&RpcServer::on_get_height), true } - },{ - "/gettransactions", - { jsonMethod(&RpcServer::on_get_transactions), false } - },{ - "/sendrawtransaction", - { jsonMethod(&RpcServer::on_send_raw_tx), false } - },{ - "/feeaddress", - { jsonMethod(&RpcServer::on_get_fee_address), true } - },{ - "/peers", - { jsonMethod(&RpcServer::on_get_peer_list), true } - },{ - "/paymentid", - { jsonMethod(&RpcServer::on_get_payment_id), true } - }, - - { - "/getpeers", - { jsonMethod(&RpcServer::on_get_peer_list), true } - },{ - "/getblocks", - { jsonMethod(&RpcServer::on_get_blocks), false } - }, - - { - "/queryblocks", - { jsonMethod(&RpcServer::on_query_blocks), false } - },{ - "/queryblockslite", - { jsonMethod(&RpcServer::on_query_blocks_lite), false } - },{ - "/queryblocksdetailed", - { - jsonMethod(&RpcServer::on_query_blocks_detailed), - false - } - }, - - { - "/get_o_indexes", - { jsonMethod(&RpcServer::on_get_indexes), false } - },{ - "/getrandom_outs", - { - jsonMethod(&RpcServer::on_get_random_outs), - false - } - },{ - "/get_pool_changes", - { jsonMethod(&RpcServer::onGetPoolChanges), false } - },{ - "/get_pool_changes_lite", - { jsonMethod(&RpcServer::onGetPoolChangesLite), false } - },{ - "/get_block_details_by_height", - { - jsonMethod< - COMMAND_RPC_GET_BLOCK_DETAILS_BY_HEIGHT - >(&RpcServer::onGetBlockDetailsByHeight), - false - } - },{ - "/get_blocks_details_by_heights", - { - jsonMethod< - COMMAND_RPC_GET_BLOCKS_DETAILS_BY_HEIGHTS - >(&RpcServer::onGetBlocksDetailsByHeights), - false - } - },{ - "/get_blocks_details_by_hashes", - { - jsonMethod< - COMMAND_RPC_GET_BLOCKS_DETAILS_BY_HASHES - >(&RpcServer::onGetBlocksDetailsByHashes), - false - } - },{ - "/get_blocks_hashes_by_timestamps", - { - jsonMethod< - COMMAND_RPC_GET_BLOCKS_HASHES_BY_TIMESTAMPS - >(&RpcServer::onGetBlocksHashesByTimestamps), - false - } - },{ - "/get_transaction_details_by_hashes", - { - jsonMethod< - COMMAND_RPC_GET_TRANSACTIONS_DETAILS_BY_HASHES - >(&RpcServer::onGetTransactionsDetailsByHashes), - false - } - },{ - "/get_transaction_details_by_heights", - { - jsonMethod< - COMMAND_RPC_GET_TRANSACTIONS_BY_HEIGHTS - >( - &RpcServer::onGetTransactionsByHeights), - false - } - },{ - "/get_transaction_hashes_by_payment_id", - { - jsonMethod< - COMMAND_RPC_GET_TRANSACTION_HASHES_BY_PAYMENT_ID - >(&RpcServer::onGetTransactionHashesByPaymentId), - false - } - }, - - // disabled in restricted rpc mode - { - "/start_mining", - { jsonMethod(&RpcServer::on_start_mining), false } - },{ - "/stop_mining", - { jsonMethod(&RpcServer::on_stop_mining), false } - },{ - "/stop_daemon", - { jsonMethod(&RpcServer::on_stop_daemon), true } - },{ - "/get_difficulty_stat", - { jsonMethod(&RpcServer::on_get_difficulty_stat), false } - }, - - // json rpc - { - "/json_rpc", - { - std::bind(&RpcServer::processJsonRpcRequest, - std::placeholders::_1, - std::placeholders::_2, - std::placeholders::_3), - true - } - } -}; - -RpcServer::RpcServer(System::Dispatcher &dispatcher, - Logging::ILogger &log, - core &c, - NodeServer &p2p, - const ICryptoNoteProtocolQuery &protocolQuery) - : HttpServer(dispatcher, log), - logger(log, "RpcServer"), - m_core(c), - m_p2p(p2p), - m_protocolQuery(protocolQuery) -{ -} - -void RpcServer::processRequest(const HttpRequest &request, HttpResponse &response) -{ - logger(TRACE) << "RPC request came: \n" << request << std::endl; - - auto url = request.getUrl(); - - auto it = s_handlers.find(url); - if (it == s_handlers.end()) { - response.setStatus(HttpResponse::STATUS_404); - return; - } - - if (!it->second.allowBusyCore && !isCoreReady()) { - response.setStatus(HttpResponse::STATUS_500); - response.setBody("Core is busy"); - return; - } - - it->second.handler(this, request, response); -} - -bool RpcServer::processJsonRpcRequest(const HttpRequest &request, HttpResponse &response) -{ - using namespace JsonRpc; - - response.addHeader("Content-Type", "application/json"); - if (!m_cors_domain.empty()) { - response.addHeader("Access-Control-Allow-Origin", m_cors_domain); - response.addHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); - response.addHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS"); - } - - JsonRpcRequest jsonRequest; - JsonRpcResponse jsonResponse; - - try { - logger(TRACE) << "JSON-RPC request: " << request.getBody(); - - jsonRequest.parseRequest(request.getBody()); - jsonResponse.setId(jsonRequest.getId()); // copy id - - static std::unordered_map< - std::string, - RpcServer::RpcHandler - > jsonRpcHandlers = { - { - "getblockcount", - { makeMemberMethod(&RpcServer::on_getblockcount), true } - },{ - "on_getblockhash", - { makeMemberMethod(&RpcServer::on_getblockhash), false } - },{ - "getblocktemplate", - { makeMemberMethod(&RpcServer::on_getblocktemplate), false } - },{ - "getcurrencyid", - { makeMemberMethod(&RpcServer::on_get_currency_id), true } - },{ - "submitblock", - { makeMemberMethod(&RpcServer::on_submitblock), false } - },{ - "getlastblockheader", - { makeMemberMethod(&RpcServer::on_get_last_block_header), false } - },{ - "getblockheaderbyhash", - { makeMemberMethod(&RpcServer::on_get_block_header_by_hash), false } - },{ - "getblockheaderbyheight", - { makeMemberMethod(&RpcServer::on_get_block_header_by_height), false } - },{ - "f_blocks_list_json", - { makeMemberMethod(&RpcServer::f_on_blocks_list_json), false } - },{ - "f_block_json", - { makeMemberMethod(&RpcServer::f_on_block_json), false } - },{ - "f_transaction_json", - { makeMemberMethod(&RpcServer::f_on_transaction_json), false } - },{ - "f_on_transactions_pool_json", - { makeMemberMethod(&RpcServer::f_on_transactions_pool_json), false } - },{ - "f_pool_json", - { makeMemberMethod(&RpcServer::f_on_pool_json), false } - },{ - "f_mempool_json", - { makeMemberMethod(&RpcServer::f_on_mempool_json), false } - },{ - "k_transactions_by_payment_id", - { makeMemberMethod(&RpcServer::k_on_transactions_by_payment_id), false } - },{ - "get_transaction_hashes_by_payment_id", - { makeMemberMethod(&RpcServer::onGetTransactionHashesByPaymentId), false } - },{ - "get_transaction_details_by_hashes", - { makeMemberMethod(&RpcServer::onGetTransactionsDetailsByHashes), false } - },{ - "get_transaction_details_by_heights", - {makeMemberMethod(&RpcServer::onGetTransactionsByHeights), false} - },{ - "k_transaction_details_by_hash", - { makeMemberMethod(&RpcServer::onGetTransactionDetailsByHash), false } - },{ - "get_blocks_details_by_heights", - { makeMemberMethod(&RpcServer::onGetBlocksDetailsByHeights), false } - },{ - "get_block_details_by_height", - { makeMemberMethod(&RpcServer::onGetBlockDetailsByHeight), false } - },{ - "get_blocks_details_by_hashes", - { makeMemberMethod(&RpcServer::onGetBlocksDetailsByHashes), false } - },{ - "get_blocks_hashes_by_timestamps", - { makeMemberMethod(&RpcServer::onGetBlocksHashesByTimestamps), false } - },{ - "check_tx_key", - { makeMemberMethod(&RpcServer::k_on_check_tx_key), false } - },{ - "check_tx_with_view_key", - { makeMemberMethod(&RpcServer::k_on_check_tx_with_view_key), false } - },{ - "check_tx_proof", - { makeMemberMethod(&RpcServer::k_on_check_tx_proof), false } - },{ - "check_reserve_proof", - { makeMemberMethod(&RpcServer::k_on_check_reserve_proof), false } - },{ - "validateaddress", - { makeMemberMethod(&RpcServer::on_validate_address), false } - },{ - "verifymessage", - { makeMemberMethod(&RpcServer::on_verify_message), false } - } - }; - - auto it = jsonRpcHandlers.find(jsonRequest.getMethod()); - if (it == jsonRpcHandlers.end()) { - throw JsonRpcError(JsonRpc::errMethodNotFound); - } - if (!it->second.allowBusyCore && !isCoreReady()) { - throw JsonRpcError(CORE_RPC_ERROR_CODE_CORE_BUSY, "Core is busy"); - } - it->second.handler(this, jsonRequest, jsonResponse); - } catch (const JsonRpcError &err) { - jsonResponse.setError(err); - } catch (const std::exception &e) { - jsonResponse.setError(JsonRpcError(JsonRpc::errInternalError, e.what())); - } - - response.setBody(jsonResponse.getBody()); - logger(TRACE) << "JSON-RPC response: " << jsonResponse.getBody(); - - return true; -} - -bool RpcServer::restrictRPC(const bool is_restricted) -{ - m_restricted_rpc = is_restricted; - - return true; -} - -bool RpcServer::enableCors(const std::string domain) -{ - m_cors_domain = domain; - - return true; -} - -std::string RpcServer::getCorsDomain() { - return m_cors_domain; -} - -bool RpcServer::setFeeAddress( - const std::string &fee_address, - const AccountPublicAddress &fee_acc) -{ - m_fee_address = fee_address; - m_fee_acc = fee_acc; - - return true; -} - -bool RpcServer::setViewKey(const std::string &view_key) -{ - Crypto::Hash private_view_key_hash; - size_t size; - if (!Common::fromHex(view_key, &private_view_key_hash, sizeof(private_view_key_hash), size) - || size != sizeof(private_view_key_hash)) { - logger(INFO) << "Could not parse private view key"; - return false; - } - - m_view_key = *(struct Crypto::SecretKey *)&private_view_key_hash; - - return true; -} - -bool RpcServer::setContactInfo(const std::string &contact) -{ - m_contact_info = contact; - - return true; -} - -bool RpcServer::isCoreReady() -{ - return m_core.currency().isTestnet() || m_p2p.get_payload_object().isSynchronized(); -} - -bool RpcServer::masternode_check_incoming_tx(const BinaryArray &tx_blob) -{ - Crypto::Hash tx_hash = NULL_HASH; - Crypto::Hash tx_prefixt_hash = NULL_HASH; - Transaction tx; - if (!parseAndValidateTransactionFromBinaryArray(tx_blob, tx, tx_hash, tx_prefixt_hash)) { - logger(INFO) << "Could not parse tx from blob"; - return false; - } - - // always relay fusion transactions - uint64_t inputs_amount = 0; - get_inputs_money_amount(tx, inputs_amount); - uint64_t outputs_amount = get_outs_money_amount(tx); - - const uint64_t fee = inputs_amount - outputs_amount; - bool isFusionTransaction = m_core.currency().isFusionTransaction( - tx, - tx_blob.size(), - m_core.get_current_blockchain_height() - 1); - if (fee == 0 && isFusionTransaction) { - logger(DEBUGGING) << "Masternode received fusion transaction, relaying with no fee check"; - return true; - } - - CryptoNote::TransactionPrefix transaction = *static_cast(&tx); - - std::vector out; - uint64_t amount; - - if (!CryptoNote::findOutputsToAccount(transaction, m_fee_acc, m_view_key, out, amount)) { - logger(INFO) << "Could not find outputs to masternode fee address"; - return false; - } - - if (amount != 0) { - logger(INFO) - << "Masternode received relayed transaction fee: " - << m_core.currency().formatAmount(amount) << " QWC"; - return true; - } - - return false; -} - -bool RpcServer::on_get_blocks( - const COMMAND_RPC_GET_BLOCKS_FAST::request &req, - COMMAND_RPC_GET_BLOCKS_FAST::response &res) -{ - // TODO: code duplication see InProcessNode::doGetNewBlocks() - if (req.block_ids.empty()) { - res.status = "Failed"; - return false; - } - - if (req.block_ids.back() != m_core.getBlockIdByHeight(0)) { - res.status = "Failed"; - return false; - } - - uint32_t totalBlockCount; - uint32_t startBlockIndex; - std::vector supplement = m_core.findBlockchainSupplement( - req.block_ids, - COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT, - totalBlockCount, - startBlockIndex); - - res.current_height = totalBlockCount; - res.start_height = startBlockIndex; - - for (const auto &blockId : supplement) { - assert(m_core.have_block(blockId)); - auto completeBlock = m_core.getBlock(blockId); - assert(completeBlock != nullptr); - - res.blocks.resize(res.blocks.size() + 1); - res.blocks.back().block = asString(toBinaryArray(completeBlock->getBlock())); - - res.blocks.back().txs.reserve(completeBlock->getTransactionCount()); - for (size_t i = 0; i < completeBlock->getTransactionCount(); ++i) { - res.blocks.back().txs.push_back( - asString(toBinaryArray(completeBlock->getTransaction(i))) - ); - } - } - - res.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::on_query_blocks( - const COMMAND_RPC_QUERY_BLOCKS::request &req, - COMMAND_RPC_QUERY_BLOCKS::response &res) -{ - uint32_t startHeight; - uint32_t currentHeight; - uint32_t fullOffset; - - if (!m_core.queryBlocks(req.block_ids, - req.timestamp, - startHeight, - currentHeight, - fullOffset, - res.items)) { - res.status = "Failed to perform query"; - return false; - } - - res.start_height = startHeight; - res.current_height = currentHeight; - res.full_offset = fullOffset; - - res.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::on_query_blocks_lite( - const COMMAND_RPC_QUERY_BLOCKS_LITE::request &req, - COMMAND_RPC_QUERY_BLOCKS_LITE::response &res) -{ - uint32_t startHeight; - uint32_t currentHeight; - uint32_t fullOffset; - if (!m_core.queryBlocksLite(req.blockIds, - req.timestamp, - startHeight, - currentHeight, - fullOffset, - res.items)) { - res.status = "Failed to perform query"; - return false; - } - - res.startHeight = startHeight; - res.currentHeight = currentHeight; - res.fullOffset = fullOffset; - - res.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::on_query_blocks_detailed( - const COMMAND_RPC_QUERY_BLOCKS_DETAILED::request &req, - COMMAND_RPC_QUERY_BLOCKS_DETAILED::response &res) -{ - uint32_t startIndex; - uint32_t currentIndex; - uint32_t fullOffset; - - if (!m_core.queryBlocksDetailed(req.blockIds, - req.timestamp, - startIndex, - currentIndex, - fullOffset, - res.blocks)) { - res.status = "Failed to perform query"; - return false; - } - - res.startHeight = startIndex; - res.currentHeight = currentIndex; - res.fullOffset = fullOffset; - - res.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::on_get_indexes( - const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request &req, - COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response &res) -{ - std::vector outputIndexes; - if (!m_core.get_tx_outputs_gindexs(req.txid, outputIndexes)) { - res.status = "Failed"; - return true; - } - - res.o_indexes.assign(outputIndexes.begin(), outputIndexes.end()); - logger(TRACE) << "COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES: [" << res.o_indexes.size() << "]"; - - res.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::on_get_random_outs( - const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request &req, - COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response &res) -{ - res.status = "Failed"; - 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 << "]:"; - - assert(ofa.outs.size() && "internal error: ofa.outs.size() is empty"); - - std::for_each(ofa.outs.begin(), ofa.outs.end(), [&](out_entry &oe) { - ss << oe.global_amount_index << " "; - }); - - ss << ENDL; - }); - std::string s = ss.str(); - logger(TRACE) << "COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS: " << ENDL << s; - - res.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::onGetPoolChanges( - const COMMAND_RPC_GET_POOL_CHANGES::request &req, - COMMAND_RPC_GET_POOL_CHANGES::response &rsp) -{ - rsp.status = CORE_RPC_STATUS_OK; - std::vector addedTransactions; - rsp.isTailBlockActual = m_core.getPoolChanges(req.tailBlockId, req.knownTxsIds, addedTransactions, rsp.deletedTxsIds); - for (auto &tx : addedTransactions) { - BinaryArray txBlob; - if (!toBinaryArray(tx, txBlob)) { - rsp.status = "Internal error"; - break; - } - - rsp.addedTxs.emplace_back(std::move(txBlob)); - } - return true; -} - -bool RpcServer::onGetPoolChangesLite( - const COMMAND_RPC_GET_POOL_CHANGES_LITE::request &req, - COMMAND_RPC_GET_POOL_CHANGES_LITE::response &rsp) -{ - rsp.status = CORE_RPC_STATUS_OK; - rsp.isTailBlockActual = m_core.getPoolChangesLite(req.tailBlockId, - req.knownTxsIds, - rsp.addedTxs, - rsp.deletedTxsIds); - - return true; -} - -bool RpcServer::onGetBlocksDetailsByHeights( - const COMMAND_RPC_GET_BLOCKS_DETAILS_BY_HEIGHTS::request &req, - COMMAND_RPC_GET_BLOCKS_DETAILS_BY_HEIGHTS::response &rsp) -{ - try { - std::vector blockDetails; - for (const uint32_t &height : req.blockHeights) { - if (m_core.get_current_blockchain_height() <= height) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT, - std::string("To big height: ") - + std::to_string(height) - + ", current blockchain height = " - + std::to_string(m_core.get_current_blockchain_height() - 1) - }; - } - Hash block_hash = m_core.getBlockIdByHeight(height); - Block blk; - if (!m_core.getBlockByHash(block_hash, blk)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Internal error: can't get block by height " + std::to_string(height) + '.' - }; - } - BlockDetails2 detail; - if (!m_core.fillBlockDetails(blk, detail)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Internal error: can't fill block details." - }; - } - blockDetails.push_back(detail); - } - rsp.blocks = std::move(blockDetails); - } catch (std::system_error &e) { - rsp.status = e.what(); - return false; - } catch (std::exception &e) { - rsp.status = "Error: " + std::string(e.what()); - return false; - } - - rsp.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::onGetBlocksDetailsByHashes( - const COMMAND_RPC_GET_BLOCKS_DETAILS_BY_HASHES::request &req, - COMMAND_RPC_GET_BLOCKS_DETAILS_BY_HASHES::response &rsp) -{ - try { - std::vector blockDetails; - for (const Crypto::Hash &hash : req.blockHashes) { - Block blk; - if (!m_core.getBlockByHash(hash, blk)) { - //throw JsonRpc::JsonRpcError{ - // CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - // "Internal error: can't get block by hash " + Common::PodToHex(hash) + '.' - //}; - } - BlockDetails2 detail; - if (!m_core.fillBlockDetails(blk, detail)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Internal error: can't fill block details." - }; - } - blockDetails.push_back(detail); - } - rsp.blocks = std::move(blockDetails); - } catch (std::system_error &e) { - rsp.status = e.what(); - return false; - } catch (std::exception &e) { - rsp.status = "Error: " + std::string(e.what()); - return false; - } - - rsp.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::onGetBlockDetailsByHeight( - const COMMAND_RPC_GET_BLOCK_DETAILS_BY_HEIGHT::request &req, - COMMAND_RPC_GET_BLOCK_DETAILS_BY_HEIGHT::response &rsp) -{ - try { - BlockDetails2 blockDetails; - if (m_core.get_current_blockchain_height() <= req.blockHeight) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT, - std::string("To big height: ") - + std::to_string(req.blockHeight) - + ", current blockchain height = " - + std::to_string(m_core.get_current_blockchain_height() - 1) }; - } - Hash block_hash = m_core.getBlockIdByHeight(req.blockHeight); - Block blk; - if (!m_core.getBlockByHash(block_hash, blk)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Internal error: can't get block by height " + std::to_string(req.blockHeight) + '.' - }; - } - if (!m_core.fillBlockDetails(blk, blockDetails)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Internal error: can't fill block details." - }; - } - rsp.block = blockDetails; - } catch (std::system_error &e) { - rsp.status = e.what(); - return false; - } catch (std::exception &e) { - rsp.status = "Error: " + std::string(e.what()); - return false; - } - - rsp.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::onGetBlocksHashesByTimestamps( - const COMMAND_RPC_GET_BLOCKS_HASHES_BY_TIMESTAMPS::request &req, - COMMAND_RPC_GET_BLOCKS_HASHES_BY_TIMESTAMPS::response &rsp) -{ - try { - uint32_t count; - std::vector blockHashes; - if (!m_core.get_blockchain_storage().getBlockIdsByTimestamp(req.timestampBegin, - req.timestampEnd, - req.limit, - blockHashes, - count)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Internal error: can't get blocks within timestamps " - + std::to_string(req.timestampBegin) - + " - " - + std::to_string(req.timestampEnd) - + "." - }; - } - rsp.blockHashes = std::move(blockHashes); - rsp.count = count; - } catch (std::system_error &e) { - rsp.status = e.what(); - return false; - } catch (std::exception &e) { - rsp.status = "Error: " + std::string(e.what()); - return false; - } - - rsp.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::onGetTransactionsDetailsByHashes( - const COMMAND_RPC_GET_TRANSACTIONS_DETAILS_BY_HASHES::request &req, - COMMAND_RPC_GET_TRANSACTIONS_DETAILS_BY_HASHES::response &rsp) -{ - try { - std::vector transactionsDetails; - transactionsDetails.reserve(req.transactionHashes.size()); - - std::list missed_txs; - std::list txs; - m_core.getTransactions(req.transactionHashes, txs, missed_txs, true); - - if (!txs.empty()) { - for (const Transaction &tx: txs) { - TransactionDetails2 txDetails; - if (!m_core.fillTransactionDetails(tx, txDetails)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Internal error: can't fill transaction details." - }; - } - transactionsDetails.push_back(txDetails); - } - - rsp.transactions = std::move(transactionsDetails); - rsp.status = CORE_RPC_STATUS_OK; - } - if (txs.empty() || !missed_txs.empty()) { - std::ostringstream ss; - std::string separator; - for (auto h : missed_txs) { - ss << separator << Common::podToHex(h); - separator = ","; - } - rsp.status = "transaction(s) not found: " + ss.str() + "."; - } - } catch (std::system_error &e) { - rsp.status = e.what(); - return false; - } catch (std::exception &e) { - rsp.status = "Error: " + std::string(e.what()); - return false; - } - - return true; -} - -bool RpcServer::onGetTransactionDetailsByHash( - const COMMAND_RPC_GET_TRANSACTION_DETAILS_BY_HASH::request &req, - COMMAND_RPC_GET_TRANSACTION_DETAILS_BY_HASH::response &rsp) -{ - std::list missed_txs; - std::list txs; - std::vector hashes; - hashes.push_back(req.hash); - m_core.getTransactions(hashes, txs, missed_txs, true); - - if (txs.empty() || !missed_txs.empty()) { - std::string hash_str = Common::podToHex(missed_txs.back()); - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_WRONG_PARAM, - "transaction wasn't found. Hash = " + hash_str + '.' - }; - } - - TransactionDetails2 transactionsDetails; - if (!m_core.fillTransactionDetails(txs.back(), transactionsDetails)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Internal error: can't fill transaction details." - }; - } - - rsp.transaction = std::move(transactionsDetails); - - rsp.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::onGetTransactionHashesByPaymentId( - const COMMAND_RPC_GET_TRANSACTION_HASHES_BY_PAYMENT_ID::request &req, - COMMAND_RPC_GET_TRANSACTION_HASHES_BY_PAYMENT_ID::response &rsp) -{ - try { - rsp.transactionHashes = m_core.getTransactionHashesByPaymentId(req.paymentId); - } catch (std::system_error &e) { - rsp.status = e.what(); - return false; - } catch (std::exception &e) { - rsp.status = "Error: " + std::string(e.what()); - return false; - } - - rsp.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::on_get_info( - const COMMAND_RPC_GET_INFO::request &req, - COMMAND_RPC_GET_INFO::response &res) -{ - res.height = m_core.get_current_blockchain_height(); - res.last_known_block_index = std::max(static_cast(1), - m_protocolQuery.getObservedHeight()) - 1; - if (res.height == res.last_known_block_index) { - // node is synced - res.difficulty = m_core.getNextBlockDifficulty(time(nullptr)); - } else { - // node is not synced yet - res.difficulty = m_core.getNextBlockDifficulty(0); - } - res.tx_count = m_core.get_blockchain_total_transactions() - res.height; // without coinbase - res.tx_pool_size = m_core.get_pool_transactions_count(); - res.alt_blocks_count = m_core.get_alternative_blocks_count(); - uint64_t total_conn = m_p2p.get_connections_count(); - res.outgoing_connections_count = m_p2p.get_outgoing_connections_count(); - res.incoming_connections_count = total_conn - res.outgoing_connections_count; - res.rpc_connections_count = get_connections_count(); - res.white_peerlist_size = m_p2p.getPeerlistManager().get_white_peers_count(); - res.grey_peerlist_size = m_p2p.getPeerlistManager().get_gray_peers_count(); - Crypto::Hash last_block_hash = m_core.getBlockIdByHeight( - m_core.get_current_blockchain_height() - 1 - ); - res.top_block_hash = Common::podToHex(last_block_hash); - res.version = PROJECT_VERSION_LONG; - res.fee_address = m_fee_address.empty() ? std::string() : m_fee_address; - res.contact = m_contact_info.empty() ? std::string() : m_contact_info; - res.min_tx_fee = m_core.getMinimalFee(); - res.readable_tx_fee = m_core.currency().formatAmount(m_core.getMinimalFee()); - res.start_time = (uint64_t)m_core.getStartTime(); - // NOTE: that large uint64_t number is unsafe in JavaScript environment - // and therefore as a JSON value so we display it as a formatted string - res.already_generated_coins = m_core.currency().formatAmount(m_core.getTotalGeneratedAmount()); - - Block blk; - if (!m_core.getBlockByHash(last_block_hash, blk)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Internal error: can't get last block by hash." - }; - } - - if (blk.baseTransaction.inputs.front().type() != typeid(BaseInput)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Internal error: coinbase transaction in the block has the wrong type" - }; - } - - block_header_response block_header; - uint32_t lastBlockHeight = boost::get(blk.baseTransaction.inputs.front()).blockIndex; - - Crypto::Hash tmp_hash = m_core.getBlockIdByHeight(lastBlockHeight); - bool is_orphaned = last_block_hash != tmp_hash; - fill_block_header_response(blk, is_orphaned, lastBlockHeight, last_block_hash, block_header); - - res.block_major_version = block_header.major_version; - res.block_minor_version = block_header.minor_version; - res.last_block_timestamp = block_header.timestamp; - res.last_block_reward = block_header.reward; - m_core.getBlockDifficulty(static_cast(lastBlockHeight), res.last_block_difficulty); - - res.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::on_get_height( - const COMMAND_RPC_GET_HEIGHT::request &req, - COMMAND_RPC_GET_HEIGHT::response &res) -{ - res.height = m_core.get_current_blockchain_height(); - res.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::on_get_transactions( - const COMMAND_RPC_GET_TRANSACTIONS::request &req, - COMMAND_RPC_GET_TRANSACTIONS::response &res) -{ - std::vector vh; - for (const auto &tx_hex_str : req.txs_hashes) { - BinaryArray b; - if (!fromHex(tx_hex_str, b)) { - res.status = "Failed to parse hex representation of transaction hash"; - return true; - } - if (b.size() != sizeof(Hash)) { - res.status = "Failed, size of data mismatch"; - } - vh.push_back(*reinterpret_cast(b.data())); - } - std::list missed_txs; - std::list txs; - m_core.getTransactions(vh, txs, missed_txs); - - for (auto &tx : txs) { - res.txs_as_hex.push_back(toHex(toBinaryArray(tx))); - } - - for (const auto &miss_tx : missed_txs) { - res.missed_tx.push_back(Common::podToHex(miss_tx)); - } - - res.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::onGetTransactionsByHeights( - const COMMAND_RPC_GET_TRANSACTIONS_BY_HEIGHTS::request &req, - COMMAND_RPC_GET_TRANSACTIONS_BY_HEIGHTS::response &res) -{ - try { - std::vector vh; - - uint32_t upperBorder = std::min(req.startBlock + req.additor, - m_core.get_current_blockchain_height()); - for (size_t i = req.startBlock; i <= upperBorder; i++) { - Block blk; - uint32_t h = static_cast(i); - Crypto::Hash blockHash = m_core.getBlockIdByHeight(i); - - if (!m_core.getBlockByHash(blockHash, blk)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Internal error: can't get block by hash. Hash = " + podToHex(blockHash) + '.' - }; - } - - if (blk.baseTransaction.inputs.front().type() != typeid(BaseInput)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Internal error: coinbase transaction in the block has the wrong type" - }; - } - - for (auto tx : blk.transactionHashes) { - vh.push_back(tx); - } - } - - std::vector transactionDetails; - transactionDetails.reserve(vh.size()); - - std::list missedTxs; - std::list txs; - - m_core.getTransactions(vh, txs, missedTxs); - - if (!txs.empty()) { - for (const Transaction &tx : txs) { - TransactionDetails2 txDetails; - if (!m_core.fillTransactionDetails(tx, txDetails)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Internal error: can't fill transaction Details." - }; - } - if (req.sigCut) { - txDetails.signatures.clear(); - } - transactionDetails.push_back(txDetails); - } - - res.transactions = std::move(transactionDetails); - res.status = CORE_RPC_STATUS_OK; - } - if (txs.empty() || !missedTxs.empty()) { - std::ostringstream oss; - std::string seperator; - for (auto h : missedTxs) { - oss << seperator << Common::podToHex(h); - seperator = ","; - } - res.status = "transaction(s) not found: " + oss.str() + "."; - } - } catch (std::system_error &e) { - res.status = e.what(); - return false; - } catch (std::exception &e) { - res.status = "Error: " + std::string(e.what()); - return false; - } - - return true; -} - -bool RpcServer::on_send_raw_tx( - const COMMAND_RPC_SEND_RAW_TX::request &req, - COMMAND_RPC_SEND_RAW_TX::response &res) -{ - BinaryArray tx_blob; - if (!fromHex(req.tx_as_hex, tx_blob)) { - logger(INFO) << "[on_send_raw_tx]: Failed to parse tx from hexbuff: " << req.tx_as_hex; - res.status = "Failed"; - return true; - } - - Crypto::Hash transactionHash = Crypto::cn_fast_hash(tx_blob.data(), tx_blob.size()); - logger(DEBUGGING) << "transaction " << transactionHash << " came in on_send_raw_tx"; - - tx_verification_context tvc = boost::value_initialized(); - if (!m_core.handle_incoming_tx(tx_blob, tvc, false, false)) { - logger(INFO) << "[on_send_raw_tx]: Failed to process tx"; - res.status = "Failed"; - return true; - } - - if (tvc.m_verification_failed) { - logger(INFO) << "[on_send_raw_tx]: tx verification failed"; - res.status = "Failed"; - return true; - } - - if (!tvc.m_should_be_relayed) { - logger(INFO) << "[on_send_raw_tx]: tx accepted, but not relayed"; - res.status = "Not relayed"; - return true; - } - - if (!m_fee_address.empty() && m_view_key != NULL_SECRET_KEY) { - if (!masternode_check_incoming_tx(tx_blob)) { - logger(INFO) << "Transaction not relayed due to lack of masternode fee"; - res.status = "Not relayed due to lack of node fee"; - return true; - } - } - - NOTIFY_NEW_TRANSACTIONS::request r; - r.txs.push_back(asString(tx_blob)); - m_core.get_protocol()->relay_transactions(r); - - // TODO: - // make sure that tx has reached other nodes here, - // probably wait to receive reflections from other nodes - res.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::on_start_mining( - const COMMAND_RPC_START_MINING::request &req, - COMMAND_RPC_START_MINING::response &res) -{ - if (m_restricted_rpc) { - res.status = "Failed, restricted handle"; - return false; - } - - AccountPublicAddress adr; - if (!m_core.currency().parseAccountAddressString(req.miner_address, adr)) { - res.status = "Failed, wrong address"; - return true; - } - - if (!m_core.get_miner().start(adr, static_cast(req.threads_count))) { - res.status = "Failed, mining not started"; - return true; - } - - res.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::on_stop_mining( - const COMMAND_RPC_STOP_MINING::request &req, - COMMAND_RPC_STOP_MINING::response &res) -{ - if (m_restricted_rpc) { - res.status = "Failed, restricted handle"; - return false; - } - - if (!m_core.get_miner().stop()) { - res.status = "Failed, mining not stopped"; - return true; - } - - res.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::on_stop_daemon( - const COMMAND_RPC_STOP_DAEMON::request &req, - COMMAND_RPC_STOP_DAEMON::response &res) -{ - if (m_restricted_rpc) { - res.status = "Failed, restricted handle"; - return false; - } - if (m_core.currency().isTestnet()) { - m_p2p.sendStopSignal(); - res.status = CORE_RPC_STATUS_OK; - } else { - res.status = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; - return false; - } - return true; -} - -bool RpcServer::on_get_fee_address( - const COMMAND_RPC_GET_FEE_ADDRESS::request &req, - COMMAND_RPC_GET_FEE_ADDRESS::response &res) -{ - if (m_fee_address.empty()) { - res.status = CORE_RPC_STATUS_OK; - return false; - } - - res.fee_address = m_fee_address; - res.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::on_get_peer_list( - const COMMAND_RPC_GET_PEER_LIST::request &req, - COMMAND_RPC_GET_PEER_LIST::response &res) -{ - std::list pl_wite; - std::list pl_gray; - m_p2p.getPeerlistManager().get_peerlist_full(pl_gray, pl_wite); - for (const auto &pe : pl_wite) { - std::stringstream ss; - ss << pe.adr; - res.peers.push_back(ss.str()); - } - - res.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::on_get_payment_id( - const COMMAND_RPC_GEN_PAYMENT_ID::request &req, - COMMAND_RPC_GEN_PAYMENT_ID::response &res) -{ - std::string pid; - try { - pid = Common::podToHex(Crypto::rand()); - } catch (const std::exception &e) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Internal error: can't generate Payment ID" - }; - } - - res.payment_id = pid; - - return true; -} - -bool RpcServer::f_on_blocks_list_json( - const F_COMMAND_RPC_GET_BLOCKS_LIST::request &req, - F_COMMAND_RPC_GET_BLOCKS_LIST::response &res) -{ - if (m_core.get_current_blockchain_height() <= req.height) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT, - std::string("To big height: ") - + std::to_string(req.height) - + ", current blockchain height = " - + std::to_string(m_core.get_current_blockchain_height()) - }; - } - - uint32_t print_blocks_count = 20; - uint32_t last_height = req.height - print_blocks_count; - if (req.height <= print_blocks_count) { - last_height = 0; - } - - for (uint32_t i = req.height; i >= last_height; i--) { - Hash block_hash = m_core.getBlockIdByHeight(static_cast(i)); - Block blk; - if (!m_core.getBlockByHash(block_hash, blk)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Internal error: can't get block by height. Height = " + std::to_string(i) + '.' - }; - } - - size_t tx_cumulative_block_size; - m_core.getBlockSize(block_hash, tx_cumulative_block_size); - size_t blockBlobSize = getObjectBinarySize(blk); - size_t minerTxBlobSize = getObjectBinarySize(blk.baseTransaction); - difficulty_type blockDiff; - m_core.getBlockDifficulty(static_cast(i), blockDiff); - - f_block_short_response block_short; - block_header_response blockHeaderResponse; - - Crypto::Hash tmp_hash = m_core.getBlockIdByHeight(i); - bool is_orphaned = block_hash != tmp_hash; - - fill_block_header_response(blk, is_orphaned, i, block_hash, blockHeaderResponse); - - block_short.timestamp = blk.timestamp; - block_short.height = i; - block_short.hash = Common::podToHex(block_hash); - block_short.cumul_size = blockBlobSize + tx_cumulative_block_size - minerTxBlobSize; - block_short.tx_count = blk.transactionHashes.size() + 1; - block_short.reward = blockHeaderResponse.reward; - block_short.difficulty = blockDiff; - block_short.min_tx_fee = m_core.getMinimalFeeForHeight(i); - - res.blocks.push_back(block_short); - - if (i == 0) { - break; - } - } - - res.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::f_on_block_json( - const F_COMMAND_RPC_GET_BLOCK_DETAILS::request &req, - F_COMMAND_RPC_GET_BLOCK_DETAILS::response &res) -{ - Hash hash; - - try { - auto height = boost::lexical_cast(req.hash); - hash = m_core.getBlockIdByHeight(height); - } catch (boost::bad_lexical_cast &) { - if (!parse_hash256(req.hash, hash)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_WRONG_PARAM, - "Failed to parse hex representation of block hash. Hex = " + req.hash + '.' - }; - } - } - - Block blk; - if (!m_core.getBlockByHash(hash, blk)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Internal error: can't get block by hash. Hash = " + req.hash + '.' - }; - } - - if (blk.baseTransaction.inputs.front().type() != typeid(BaseInput)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Internal error: coinbase transaction in the block has the wrong type" - }; - } - - block_header_response block_header; - res.block.height = boost::get(blk.baseTransaction.inputs.front()).blockIndex; - - Crypto::Hash tmp_hash = m_core.getBlockIdByHeight(res.block.height); - bool is_orphaned = hash != tmp_hash; - fill_block_header_response(blk, is_orphaned, res.block.height, hash, block_header); - - res.block.major_version = block_header.major_version; - res.block.minor_version = block_header.minor_version; - res.block.timestamp = block_header.timestamp; - res.block.prev_hash = block_header.prev_hash; - res.block.nonce = block_header.nonce; - res.block.hash = block_header.hash; - res.block.depth = block_header.depth; - res.block.orphan_status = block_header.orphan_status; - m_core.getBlockDifficulty(static_cast(res.block.height), res.block.difficulty); - m_core.getBlockCumulativeDifficulty(static_cast(res.block.height), - res.block.cumulativeDifficulty); - - res.block.reward = block_header.reward; - - std::vector blocksSizes; - if (!m_core.getBackwardBlocksSizes(res.block.height, - blocksSizes, - parameters::CRYPTONOTE_REWARD_BLOCKS_WINDOW)) { - return false; - } - res.block.sizeMedian = Common::medianValue(blocksSizes); - - size_t blockSize = 0; - if (!m_core.getBlockSize(hash, blockSize)) { - return false; - } - res.block.transactionsCumulativeSize = blockSize; - - size_t blokBlobSize = getObjectBinarySize(blk); - size_t minerTxBlobSize = getObjectBinarySize(blk.baseTransaction); - res.block.blockSize = blokBlobSize + res.block.transactionsCumulativeSize - minerTxBlobSize; - - uint64_t alreadyGeneratedCoins; - if (!m_core.getAlreadyGeneratedCoins(hash, alreadyGeneratedCoins)) { - return false; - } - res.block.alreadyGeneratedCoins = std::to_string(alreadyGeneratedCoins); - - if (!m_core.getGeneratedTransactionsNumber(res.block.height, - res.block.alreadyGeneratedTransactions)) { - return false; - } - - uint64_t prevBlockGeneratedCoins = 0; - uint32_t previousBlockHeight = 0; - uint64_t blockTarget = CryptoNote::parameters::DIFFICULTY_TARGET; - - if (res.block.height > 0) { - if (!m_core.getAlreadyGeneratedCoins(blk.previousBlockHash, prevBlockGeneratedCoins)) { - return false; - } - } - - if (res.block.height >= CryptoNote::parameters::UPGRADE_HEIGHT_V6) { - m_core.getBlockHeight(blk.previousBlockHash, previousBlockHeight); - blockTarget = blk.timestamp - m_core.getBlockTimestamp(previousBlockHeight); - } - - uint64_t maxReward = 0; - uint64_t currentReward = 0; - int64_t emissionChange = 0; - size_t blockGrantedFullRewardZone = CryptoNote::parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2; - res.block.effectiveSizeMedian = std::max(res.block.sizeMedian, blockGrantedFullRewardZone); - - if (!m_core.getBlockReward(res.block.major_version, - res.block.sizeMedian, - 0, - prevBlockGeneratedCoins, - 0, - maxReward, - emissionChange, - res.block.height, - blockTarget)) { - return false; - } - if (!m_core.getBlockReward(res.block.major_version, - res.block.sizeMedian, - res.block.transactionsCumulativeSize, - prevBlockGeneratedCoins, - 0, - currentReward, - emissionChange, - res.block.height, - blockTarget)) { - return false; - } - - res.block.baseReward = maxReward; - if (maxReward == 0 && currentReward == 0) { - res.block.penalty = static_cast(0); - } else { - if (maxReward < currentReward) { - return false; - } - res.block.penalty = - static_cast(maxReward - currentReward) / static_cast(maxReward); - } - - // Base transaction adding - f_transaction_short_response transaction_short; - transaction_short.hash = Common::podToHex(getObjectHash(blk.baseTransaction)); - transaction_short.fee = 0; - transaction_short.amount_out = get_outs_money_amount(blk.baseTransaction); - transaction_short.size = getObjectBinarySize(blk.baseTransaction); - res.block.transactions.push_back(transaction_short); - - std::list missed_txs; - std::list txs; - m_core.getTransactions(blk.transactionHashes, txs, missed_txs); - - res.block.totalFeeAmount = 0; - - for (const Transaction &tx : txs) { - f_transaction_short_response tr_short; - uint64_t amount_in = 0; - get_inputs_money_amount(tx, amount_in); - uint64_t amount_out = get_outs_money_amount(tx); - - tr_short.hash = Common::podToHex(getObjectHash(tx)); - tr_short.fee = amount_in - amount_out; - tr_short.amount_out = amount_out; - tr_short.size = getObjectBinarySize(tx); - res.block.transactions.push_back(tr_short); - - res.block.totalFeeAmount += tr_short.fee; - } - - res.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::f_on_transaction_json( - const F_COMMAND_RPC_GET_TRANSACTION_DETAILS::request &req, - F_COMMAND_RPC_GET_TRANSACTION_DETAILS::response &res) -{ - Hash hash; - - if (!parse_hash256(req.hash, hash)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_WRONG_PARAM, - "Failed to parse hex representation of transaction hash. Hex = " + req.hash + '.' - }; - } - - std::vector tx_ids; - tx_ids.push_back(hash); - - std::list missed_txs; - std::list txs; - m_core.getTransactions(tx_ids, txs, missed_txs, true); - - if (1 == txs.size()) { - res.tx = txs.front(); - } else { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_WRONG_PARAM, - "transaction wasn't found. Hash = " + req.hash + '.' - }; - } - - Crypto::Hash blockHash; - uint32_t blockHeight; - if (m_core.getBlockContainingTx(hash, blockHash, blockHeight)) { - Block blk; - if (m_core.getBlockByHash(blockHash, blk)) { - size_t tx_cumulative_block_size; - m_core.getBlockSize(blockHash, tx_cumulative_block_size); - size_t blokBlobSize = getObjectBinarySize(blk); - size_t minerTxBlobSize = getObjectBinarySize(blk.baseTransaction); - f_block_short_response block_short; - - block_short.timestamp = blk.timestamp; - block_short.height = blockHeight; - block_short.hash = Common::podToHex(blockHash); - block_short.cumul_size = blokBlobSize + tx_cumulative_block_size - minerTxBlobSize; - block_short.tx_count = blk.transactionHashes.size() + 1; - res.block = block_short; - res.txDetails.confirmations = m_protocolQuery.getObservedHeight() - blockHeight; - } - } - - uint64_t amount_in = 0; - get_inputs_money_amount(res.tx, amount_in); - uint64_t amount_out = get_outs_money_amount(res.tx); - - res.txDetails.hash = Common::podToHex(getObjectHash(res.tx)); - res.txDetails.fee = amount_in - amount_out; - if (amount_in == 0) { - res.txDetails.fee = 0; - } - res.txDetails.amount_out = amount_out; - res.txDetails.size = getObjectBinarySize(res.tx); - - uint64_t mixin; - if (!f_getMixin(res.tx, mixin)) { - return false; - } - res.txDetails.mixin = mixin; - - Crypto::Hash paymentId; - if (CryptoNote::getPaymentIdFromTxExtra(res.tx.extra, paymentId)) { - res.txDetails.paymentId = Common::podToHex(paymentId); - } else { - res.txDetails.paymentId = ""; - } - - res.txDetails.extra.raw = res.tx.extra; - - std::vector txExtraFields; - parseTransactionExtra(res.tx.extra, txExtraFields); - for (const CryptoNote::TransactionExtraField &field : txExtraFields) { - if (typeid(CryptoNote::TransactionExtraPadding) == field.type()) { - res.txDetails.extra.padding.push_back(std::move( - boost::get(field).size - )); - } else if (typeid(CryptoNote::TransactionExtraPublicKey) == field.type()) { - res.txDetails.extra.publicKey = getTransactionPublicKeyFromExtra(res.tx.extra); - } else if (typeid(CryptoNote::TransactionExtraNonce) == field.type()) { - res.txDetails.extra.nonce.push_back( - Common::toHex( - boost::get(field).nonce.data(), - boost::get(field).nonce.size() - ) - ); - } - } - - res.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::f_on_pool_json( - const F_COMMAND_RPC_GET_POOL::request &req, - F_COMMAND_RPC_GET_POOL::response &res) -{ - auto pool = m_core.getPoolTransactions(); - for (const Transaction tx : pool) { - f_transaction_short_response transaction_short; - - uint64_t amount_in = getInputAmount(tx); - uint64_t amount_out = getOutputAmount(tx); - bool amount_in_less = (amount_in < amount_out + parameters::MINIMUM_FEE); - - transaction_short.hash = Common::podToHex(getObjectHash(tx)); - transaction_short.fee = amount_in_less ? parameters::MINIMUM_FEE : amount_in - amount_out; - transaction_short.amount_out = amount_out; - transaction_short.size = getObjectBinarySize(tx); - res.transactions.push_back(transaction_short); - } - - res.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::f_on_mempool_json( - const COMMAND_RPC_GET_MEMPOOL::request &req, - COMMAND_RPC_GET_MEMPOOL::response &res) -{ - auto pool = m_core.getMemoryPool(); - for (const CryptoNote::tx_memory_pool::TransactionDetails txd : pool) { - f_mempool_transaction_response mempool_transaction; - uint64_t amount_out = getOutputAmount(txd.tx); - - mempool_transaction.hash = Common::podToHex(txd.id); - mempool_transaction.fee = txd.fee; - mempool_transaction.amount_out = amount_out; - mempool_transaction.size = txd.blobSize; - mempool_transaction.receiveTime = txd.receiveTime; - mempool_transaction.keptByBlock = txd.keptByBlock; - mempool_transaction.max_used_block_height = txd.maxUsedBlock.height; - mempool_transaction.max_used_block_id = Common::podToHex(txd.maxUsedBlock.id); - mempool_transaction.last_failed_height = txd.lastFailedBlock.height; - mempool_transaction.last_failed_id = Common::podToHex(txd.lastFailedBlock.id); - res.mempool.push_back(mempool_transaction); - } - - res.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::f_getMixin(const Transaction &transaction, uint64_t &mixin) -{ - mixin = 0; - - for (const TransactionInput &txin : transaction.inputs) { - if (txin.type() != typeid(KeyInput)) { - continue; - } - uint64_t currentMixin = boost::get(txin).outputIndexes.size(); - if (currentMixin > mixin) { - mixin = currentMixin; - } - } - - return true; -} - -bool RpcServer::k_on_transactions_by_payment_id( - const K_COMMAND_RPC_GET_TRANSACTIONS_BY_PAYMENT_ID::request &req, - K_COMMAND_RPC_GET_TRANSACTIONS_BY_PAYMENT_ID::response &res) -{ - if (req.payment_id.empty()) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_WRONG_PARAM, - "Wrong parameters, expected payment_id" - }; - } - logger(Logging::DEBUGGING, Logging::WHITE) - << "RPC request came: Search by Payment ID: " - << req.payment_id; - - Crypto::Hash paymentId; - std::vector transactions; - - if (!parse_hash256(req.payment_id, paymentId)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_WRONG_PARAM, - "Failed to parse Payment ID: " + req.payment_id + '.' - }; - } - - if (!m_core.getTransactionsByPaymentId(paymentId, transactions)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Internal error: can't get transactions by Payment ID: " + req.payment_id + '.' - }; - } - - for (const Transaction &tx : transactions) { - f_transaction_short_response transaction_short; - uint64_t amount_in = 0; - get_inputs_money_amount(tx, amount_in); - uint64_t amount_out = get_outs_money_amount(tx); - - transaction_short.hash = Common::podToHex(getObjectHash(tx)); - transaction_short.fee = amount_in - amount_out; - transaction_short.amount_out = amount_out; - transaction_short.size = getObjectBinarySize(tx); - res.transactions.push_back(transaction_short); - } - - res.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::f_on_transactions_pool_json( - const F_COMMAND_RPC_GET_POOL::request &req, - F_COMMAND_RPC_GET_POOL::response &res) -{ - auto pool = m_core.getPoolTransactions(); - for (const Transaction &tx : pool) { - f_transaction_short_response transaction_short; - uint64_t amount_in = getInputAmount(tx); - uint64_t amount_out = getOutputAmount(tx); - - transaction_short.hash = Common::podToHex(getObjectHash(tx)); - transaction_short.fee = amount_in - amount_out; - transaction_short.amount_out = amount_out; - transaction_short.size = getObjectBinarySize(tx); - res.transactions.push_back(transaction_short); - } - - res.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::on_getblockcount( - const COMMAND_RPC_GETBLOCKCOUNT::request &req, - COMMAND_RPC_GETBLOCKCOUNT::response &res) -{ - res.count = m_core.get_current_blockchain_height(); - res.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::on_getblockhash( - const COMMAND_RPC_GETBLOCKHASH::request &req, - COMMAND_RPC_GETBLOCKHASH::response &res) -{ - if (req.size() != 1) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_WRONG_PARAM, - "Wrong parameters, expected height" - }; - } - - uint32_t h = static_cast(req[0]); - Crypto::Hash blockId = m_core.getBlockIdByHeight(h); - if (blockId == NULL_HASH) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT, - std::string("To big height: ") - + std::to_string(h) - + ", current blockchain height = " - + std::to_string(m_core.get_current_blockchain_height()) - }; - } - - res = Common::podToHex(blockId); - - return true; -} - -namespace { - -uint64_t slow_memmem(void *start_buff, size_t buflen, void *pat, size_t patlen) -{ - void* buf = start_buff; - void* end = (char*)buf + buflen - patlen; - while ((buf = memchr(buf, ((char*)pat)[0], buflen))) { - if (buf>end) { - return 0; - } - if (memcmp(buf, pat, patlen) == 0) { - return (char*)buf - (char*)start_buff; - } - buf = (char*)buf + 1; - } - return 0; -} - -} // namespace - -bool RpcServer::on_getblocktemplate( - const COMMAND_RPC_GETBLOCKTEMPLATE::request &req, - COMMAND_RPC_GETBLOCKTEMPLATE::response &res) -{ - if (req.reserve_size > TX_EXTRA_NONCE_MAX_COUNT) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_TOO_BIG_RESERVE_SIZE, - "To big reserved size, maximum 255" - }; - } - - AccountPublicAddress acc = boost::value_initialized(); - - if (!req.wallet_address.size() - || !m_core.currency().parseAccountAddressString(req.wallet_address, acc)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_WRONG_WALLET_ADDRESS, - "Failed to parse wallet address" - }; - } - - Block b = boost::value_initialized(); - CryptoNote::BinaryArray blob_reserve; - blob_reserve.resize(req.reserve_size, 0); - if (!m_core.get_block_template(b, acc, res.difficulty, res.height, blob_reserve)) { - logger(ERROR) << "Failed to create block template"; - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Internal error: failed to create block template" - }; - } - - BinaryArray block_blob = toBinaryArray(b); - PublicKey tx_pub_key = CryptoNote::getTransactionPublicKeyFromExtra(b.baseTransaction.extra); - if (tx_pub_key == NULL_PUBLIC_KEY) { - logger(ERROR) << "Failed to find tx pub key in coinbase extra"; - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Internal error: failed to find tx pub key in coinbase extra" - }; - } - - if (0 < req.reserve_size) { - res.reserved_offset = slow_memmem((void*)block_blob.data(), - block_blob.size(), - &tx_pub_key, - sizeof(tx_pub_key)); - if (!res.reserved_offset) { - logger(ERROR) << "Failed to find tx pub key in blockblob"; - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Internal error: failed to create block template" - }; - } - // 3 bytes: - // - tag for TX_EXTRA_TAG_PUBKEY(1 byte) - // - tag for TX_EXTRA_NONCE(1 byte) - // - counter in TX_EXTRA_NONCE(1 byte) - res.reserved_offset += sizeof(tx_pub_key) + 3; - if (res.reserved_offset + req.reserve_size > block_blob.size()) { - logger(ERROR) << "Failed to calculate offset for reserved bytes"; - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Internal error: failed to create block template" - }; - } - } else { - res.reserved_offset = 0; - } - - BinaryArray hashing_blob; - if (!get_block_hashing_blob(b, hashing_blob)) { - logger(ERROR) << "Failed to get blockhashing_blob"; - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Internal error: failed to get blockhashing_blob" - }; - } - - res.blocktemplate_blob = toHex(block_blob); - res.blockhashing_blob = toHex(hashing_blob); - res.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::on_get_currency_id( - const COMMAND_RPC_GET_CURRENCY_ID::request &/*req*/, - COMMAND_RPC_GET_CURRENCY_ID::response &res) -{ - Hash currencyId = m_core.currency().genesisBlockHash(); - res.currency_id_blob = Common::podToHex(currencyId); - - return true; -} - -bool RpcServer::on_submitblock( - const COMMAND_RPC_SUBMITBLOCK::request &req, - COMMAND_RPC_SUBMITBLOCK::response &res) -{ - if (req.size() != 1) { - throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_WRONG_PARAM, "Wrong param" }; - } - - BinaryArray blockblob; - if (!fromHex(req[0], blockblob)) { - throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB, "Wrong block blob" }; - } - - block_verification_context bvc = boost::value_initialized(); - - m_core.handle_incoming_block_blob(blockblob, bvc, true, true); - - if (!bvc.m_added_to_main_chain) { - throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED, "Block not accepted" }; - } - - res.status = CORE_RPC_STATUS_OK; - - return true; -} - -namespace { - -uint64_t get_block_reward(const Block &blk) -{ - uint64_t reward = 0; - for (const TransactionOutput &out : blk.baseTransaction.outputs) { - reward += out.amount; - } - - return reward; -} - -} // namespace - -void RpcServer::fill_block_header_response( - const Block &blk, - bool orphan_status, - uint64_t height, - const Hash &hash, - block_header_response &responce) -{ - responce.major_version = blk.majorVersion; - responce.minor_version = blk.minorVersion; - responce.timestamp = blk.timestamp; - responce.prev_hash = Common::podToHex(blk.previousBlockHash); - responce.nonce = blk.nonce; - responce.orphan_status = orphan_status; - responce.height = height; - responce.depth = m_core.get_current_blockchain_height() - height - 1; - responce.hash = Common::podToHex(hash); - m_core.getBlockDifficulty(static_cast(height), responce.difficulty); - responce.reward = get_block_reward(blk); -} - -bool RpcServer::on_get_last_block_header( - const COMMAND_RPC_GET_LAST_BLOCK_HEADER::request &req, - COMMAND_RPC_GET_LAST_BLOCK_HEADER::response &res) -{ - uint32_t last_block_height; - Hash last_block_hash; - - m_core.get_blockchain_top(last_block_height, last_block_hash); - - Block last_block; - if (!m_core.getBlockByHash(last_block_hash, last_block)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Internal error: can't get last block hash." - }; - } - - Crypto::Hash tmp_hash = m_core.getBlockIdByHeight(last_block_height); - bool is_orphaned = last_block_hash != tmp_hash; - fill_block_header_response(last_block, is_orphaned, last_block_height, last_block_hash, res.block_header); - res.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::on_get_block_header_by_hash( - const COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::request &req, - COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::response &res) -{ - Hash block_hash; - - if (!parse_hash256(req.hash, block_hash)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_WRONG_PARAM, - "Failed to parse hex representation of block hash. Hex = " + req.hash + '.' - }; - } - - Block blk; - if (!m_core.getBlockByHash(block_hash, blk)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Internal error: can't get block by hash. Hash = " + req.hash + '.' - }; - } - - if (blk.baseTransaction.inputs.front().type() != typeid(BaseInput)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Internal error: coinbase transaction in the block has the wrong type" - }; - } - - uint64_t block_height = boost::get(blk.baseTransaction.inputs.front()).blockIndex; - Crypto::Hash tmp_hash = m_core.getBlockIdByHeight(block_height); - bool is_orphaned = block_hash != tmp_hash; - fill_block_header_response(blk, is_orphaned, block_height, block_hash, res.block_header); - res.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::on_get_block_header_by_height( - const COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::request &req, - COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response &res) -{ - if (m_core.get_current_blockchain_height() <= req.height) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT, - std::string("To big height: ") - + std::to_string(req.height) - + ", current blockchain height = " - + std::to_string(m_core.get_current_blockchain_height()) - }; - } - - Hash block_hash = m_core.getBlockIdByHeight(static_cast(req.height)); - Block blk; - if (!m_core.getBlockByHash(block_hash, blk)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Internal error: can't get block by height. Height = " + std::to_string(req.height) + '.' - }; - } - - Crypto::Hash tmp_hash = m_core.getBlockIdByHeight(req.height); - bool is_orphaned = block_hash != tmp_hash; - fill_block_header_response(blk, is_orphaned, req.height, block_hash, res.block_header); - res.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::k_on_check_tx_key( - const K_COMMAND_RPC_CHECK_TX_KEY::request &req, - K_COMMAND_RPC_CHECK_TX_KEY::response &res) -{ - // parse txid - Crypto::Hash txid; - if (!parse_hash256(req.txid, txid)) { - throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_WRONG_PARAM, "Failed to parse txid" }; - } - - // parse address - CryptoNote::AccountPublicAddress address; - if (!m_core.currency().parseAccountAddressString(req.address, address)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_WRONG_PARAM, - "Failed to parse address " + req.address + '.' - }; - } - - // parse txkey - Crypto::Hash tx_key_hash; - size_t size; - if (!Common::fromHex(req.txkey, &tx_key_hash, sizeof(tx_key_hash), size) - || size != sizeof(tx_key_hash)) { - throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_WRONG_PARAM, "Failed to parse txkey" }; - } - Crypto::SecretKey tx_key = *(struct Crypto::SecretKey *) &tx_key_hash; - - // fetch tx - Transaction tx; - std::vector tx_ids; - tx_ids.push_back(txid); - std::list missed_txs; - std::list txs; - m_core.getTransactions(tx_ids, txs, missed_txs, true); - - if (1 == txs.size()) { - tx = txs.front(); - } else { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_WRONG_PARAM, - "Couldn't find transaction with hash: " + req.txid + '.' - }; - } - CryptoNote::TransactionPrefix transaction = *static_cast(&tx); - - // obtain key derivation - Crypto::KeyDerivation derivation; - if (!Crypto::generate_key_derivation(address.viewPublicKey, tx_key, derivation)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_WRONG_PARAM, - "Failed to generate key derivation from supplied parameters" - }; - } - - // look for outputs - uint64_t received(0); - size_t keyIndex(0); - std::vector outputs; - try { - for (const TransactionOutput &o : transaction.outputs) { - if (o.target.type() == typeid(KeyOutput)) { - const KeyOutput out_key = boost::get(o.target); - Crypto::PublicKey pubkey; - derive_public_key(derivation, keyIndex, address.spendPublicKey, pubkey); - if (pubkey == out_key.key) { - received += o.amount; - outputs.push_back(o); - } - } - ++keyIndex; - } - } catch (...) { - throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Unknown error" }; - } - - res.amount = received; - res.outputs = outputs; - res.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::k_on_check_tx_with_view_key( - const K_COMMAND_RPC_CHECK_TX_WITH_PRIVATE_VIEW_KEY::request &req, - K_COMMAND_RPC_CHECK_TX_WITH_PRIVATE_VIEW_KEY::response &res) -{ - // parse txid - Crypto::Hash txid; - if (!parse_hash256(req.txid, txid)) { - throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_WRONG_PARAM, "Failed to parse txid" }; - } - - // parse address - CryptoNote::AccountPublicAddress address; - if (!m_core.currency().parseAccountAddressString(req.address, address)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_WRONG_PARAM, - "Failed to parse address " + req.address + '.' - }; - } - - // parse view key - Crypto::Hash view_key_hash; - size_t size; - if (!Common::fromHex(req.view_key, &view_key_hash, sizeof(view_key_hash), size) - || size != sizeof(view_key_hash)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_WRONG_PARAM, - "Failed to parse private view key" - }; - } - Crypto::SecretKey viewKey = *(struct Crypto::SecretKey *) &view_key_hash; - - // fetch tx - Transaction tx; - std::vector tx_ids; - tx_ids.push_back(txid); - std::list missed_txs; - std::list txs; - m_core.getTransactions(tx_ids, txs, missed_txs, true); - - if (1 == txs.size()) { - tx = txs.front(); - } else { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_WRONG_PARAM, - "Couldn't find transaction with hash: " + req.txid + '.' - }; - } - CryptoNote::TransactionPrefix transaction = *static_cast(&tx); - - // get tx pub key - Crypto::PublicKey txPubKey = getTransactionPublicKeyFromExtra(transaction.extra); - - // obtain key derivation - Crypto::KeyDerivation derivation; - if (!Crypto::generate_key_derivation(txPubKey, viewKey, derivation)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_WRONG_PARAM, - "Failed to generate key derivation from supplied parameters" - }; - } - - // look for outputs - uint64_t received(0); - size_t keyIndex(0); - std::vector outputs; - try { - for (const TransactionOutput &o : transaction.outputs) { - if (o.target.type() == typeid(KeyOutput)) { - const KeyOutput out_key = boost::get(o.target); - Crypto::PublicKey pubkey; - derive_public_key(derivation, keyIndex, address.spendPublicKey, pubkey); - if (pubkey == out_key.key) { - received += o.amount; - outputs.push_back(o); - } - } - ++keyIndex; - } - } catch (...) { - throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Unknown error" }; - } - res.amount = received; - res.outputs = outputs; - - Crypto::Hash blockHash; - uint32_t blockHeight; - if (m_core.getBlockContainingTx(txid, blockHash, blockHeight)) { - res.confirmations = m_protocolQuery.getObservedHeight() - blockHeight; - } - - res.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::k_on_check_tx_proof( - const K_COMMAND_RPC_CHECK_TX_PROOF::request &req, - K_COMMAND_RPC_CHECK_TX_PROOF::response &res) -{ - // parse txid - Crypto::Hash txid; - if (!parse_hash256(req.tx_id, txid)) { - throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_WRONG_PARAM, "Failed to parse txid" }; - } - - // parse address - CryptoNote::AccountPublicAddress address; - if (!m_core.currency().parseAccountAddressString(req.dest_address, address)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_WRONG_PARAM, - "Failed to parse address " + req.dest_address + '.' - }; - } - - // parse pubkey r*A & signature - const size_t header_len = strlen("ProofV1"); - if (req.signature.size() < header_len || req.signature.substr(0, header_len) != "ProofV1") { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_WRONG_PARAM, - "Signature header check error" - }; - } - Crypto::PublicKey rA; - Crypto::Signature sig; - const size_t rA_len = Tools::Base58::encode(std::string((const char *)&rA, - sizeof(Crypto::PublicKey))).size(); - const size_t sig_len = Tools::Base58::encode(std::string((const char *)&sig, - sizeof(Crypto::Signature))).size(); - std::string rA_decoded; - std::string sig_decoded; - if (!Tools::Base58::decode(req.signature.substr(header_len, rA_len), rA_decoded)) { - throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_WRONG_PARAM, "Signature decoding error" }; - } - if (!Tools::Base58::decode(req.signature.substr(header_len + rA_len, sig_len), sig_decoded)) { - throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_WRONG_PARAM, "Signature decoding error" }; - } - if (sizeof(Crypto::PublicKey) != rA_decoded.size() - || sizeof(Crypto::Signature) != sig_decoded.size()) { - throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_WRONG_PARAM, "Signature decoding error" }; - } - memcpy(&rA, rA_decoded.data(), sizeof(Crypto::PublicKey)); - memcpy(&sig, sig_decoded.data(), sizeof(Crypto::Signature)); - - // fetch tx pubkey - Transaction tx; - - std::vector out; - std::vector tx_ids; - tx_ids.push_back(txid); - std::list missed_txs; - std::list txs; - m_core.getTransactions(tx_ids, txs, missed_txs, true); - - if (1 == txs.size()) { - tx = txs.front(); - } else { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_WRONG_PARAM, - "transaction wasn't found. Hash = " + req.tx_id + '.' - }; - } - CryptoNote::TransactionPrefix transaction = *static_cast(&tx); - - Crypto::PublicKey R = getTransactionPublicKeyFromExtra(transaction.extra); - if (R == NULL_PUBLIC_KEY) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Tx pubkey was not found" - }; - } - - // check signature - bool r = Crypto::check_tx_proof(txid, R, address.viewPublicKey, rA, sig); - res.signature_valid = r; - - if (r) { - // obtain key derivation by multiplying scalar 1 to the pubkey r*A included in the signature - Crypto::KeyDerivation derivation; - if (!Crypto::generate_key_derivation(rA, I, derivation)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Failed to generate key derivation" - }; - } - - // look for outputs - uint64_t received(0); - size_t keyIndex(0); - std::vector outputs; - try { - for (const TransactionOutput &o : transaction.outputs) { - if (o.target.type() == typeid(KeyOutput)) { - const KeyOutput out_key = boost::get(o.target); - Crypto::PublicKey pubkey; - derive_public_key(derivation, keyIndex, address.spendPublicKey, pubkey); - if (pubkey == out_key.key) { - received += o.amount; - outputs.push_back(o); - } - } - ++keyIndex; - } - } catch (...) { - throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Unknown error" }; - } - res.received_amount = received; - res.outputs = outputs; - - Crypto::Hash blockHash; - uint32_t blockHeight; - if (m_core.getBlockContainingTx(txid, blockHash, blockHeight)) { - res.confirmations = m_protocolQuery.getObservedHeight() - blockHeight; - } - } else { - res.received_amount = 0; - } - - res.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::k_on_check_reserve_proof( - const K_COMMAND_RPC_CHECK_RESERVE_PROOF::request &req, - K_COMMAND_RPC_CHECK_RESERVE_PROOF::response &res) -{ - // parse address - CryptoNote::AccountPublicAddress address; - if (!m_core.currency().parseAccountAddressString(req.address, address)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_WRONG_PARAM, - "Failed to parse address " + req.address + '.' - }; - } - - // parse sugnature - static constexpr char header[] = "ReserveProofV1"; - const size_t header_len = strlen(header); - if (req.signature.size() < header_len || req.signature.substr(0, header_len) != header) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Signature header check error" - }; - } - - std::string sig_decoded; - if (!Tools::Base58::decode(req.signature.substr(header_len), sig_decoded)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Signature decoding error" - }; - } - - BinaryArray ba; - if (!Common::fromHex(sig_decoded, ba)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Proof decoding error" - }; - } - - reserve_proof proof_decoded; - if (!fromBinaryArray(proof_decoded, ba)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "BinaryArray decoding error" - }; - } - - std::vector& proofs = proof_decoded.proofs; - - // compute signature prefix hash - std::string prefix_data = req.message; - prefix_data.append((const char *)&address, sizeof(CryptoNote::AccountPublicAddress)); - for (size_t i = 0; i < proofs.size(); ++i) { - prefix_data.append((const char*)&proofs[i].key_image, sizeof(Crypto::PublicKey)); - } - Crypto::Hash prefix_hash; - Crypto::cn_fast_hash(prefix_data.data(), prefix_data.size(), prefix_hash); - - // fetch txes - std::vector transactionHashes; - for (size_t i = 0; i < proofs.size(); ++i) { - transactionHashes.push_back(proofs[i].txid); - } - std::list missed_txs; - std::list txs; - m_core.getTransactions(transactionHashes, txs, missed_txs); - std::vector transactions; - std::copy(txs.begin(), txs.end(), std::inserter(transactions, transactions.end())); - - // check spent status - res.total = 0; - res.spent = 0; - for (size_t i = 0; i < proofs.size(); ++i) { - const reserve_proof_entry& proof = proofs[i]; - - CryptoNote::TransactionPrefix tx = *static_cast(&transactions[i]); - - if (proof.index_in_tx >= tx.outputs.size()) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "index_in_tx is out of bound" - }; - } - - const KeyOutput out_key = boost::get(tx.outputs[proof.index_in_tx].target); - - // get tx pub key - Crypto::PublicKey txPubKey = getTransactionPublicKeyFromExtra(tx.extra); - - // check singature for shared secret - if (!Crypto::check_tx_proof(prefix_hash, - address.viewPublicKey, - txPubKey, - proof.shared_secret, - proof.shared_secret_sig)) { - //throw JsonRpc::JsonRpcError{ - // CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - // "Failed to check singature for shared secret" - //}; - res.good = false; - return true; - } - - // check signature for key image - const std::vector& pubs = { &out_key.key }; - if (!Crypto::check_ring_signature(prefix_hash, - proof.key_image, - &pubs[0], - 1, - &proof.key_image_sig)) { - //throw JsonRpc::JsonRpcError{ - // CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - // "Failed to check signature for key image" - //}; - res.good = false; - return true; - } - - // check if the address really received the fund - Crypto::KeyDerivation derivation; - if (!Crypto::generate_key_derivation(proof.shared_secret, I, derivation)) { - throw JsonRpc::JsonRpcError{ - CORE_RPC_ERROR_CODE_INTERNAL_ERROR, - "Failed to generate key derivation" - }; - } - - try { - Crypto::PublicKey pubkey; - derive_public_key(derivation, proof.index_in_tx, address.spendPublicKey, pubkey); - if (pubkey == out_key.key) { - uint64_t amount = tx.outputs[proof.index_in_tx].amount; - res.total += amount; - - if (m_core.is_key_image_spent(proof.key_image)) { - res.spent += amount; - } - } - } catch (...) { - throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Unknown error" }; - } - } - - // check signature for address spend keys - Crypto::Signature sig = proof_decoded.signature; - if (!Crypto::check_signature(prefix_hash, address.spendPublicKey, sig)) { - res.good = false; - return true; - } - - return true; -} - -bool RpcServer::on_validate_address( - const COMMAND_RPC_VALIDATE_ADDRESS::request &req, - COMMAND_RPC_VALIDATE_ADDRESS::response &res) -{ - AccountPublicAddress acc = boost::value_initialized(); - bool r = m_core.currency().parseAccountAddressString(req.address, acc); - res.isvalid = r; - if (r) { - res.address = m_core.currency().accountAddressAsString(acc); - res.spendPublicKey = Common::podToHex(acc.spendPublicKey); - res.viewPublicKey = Common::podToHex(acc.viewPublicKey); - } - res.status = CORE_RPC_STATUS_OK; - return true; -} - -bool RpcServer::on_verify_message( - const COMMAND_RPC_VERIFY_MESSAGE::request &req, - COMMAND_RPC_VERIFY_MESSAGE::response &res) -{ - Crypto::Hash hash; - Crypto::cn_fast_hash(req.message.data(), req.message.size(), hash); - - AccountPublicAddress acc = boost::value_initialized(); - if (!m_core.currency().parseAccountAddressString(req.address, acc)) { - throw JsonRpc::JsonRpcError(CORE_RPC_ERROR_CODE_WRONG_PARAM, - std::string("Failed to parse address")); - } - - const size_t header_len = strlen("SigV1"); - if (req.signature.size() < header_len || req.signature.substr(0, header_len) != "SigV1") { - throw JsonRpc::JsonRpcError(CORE_RPC_ERROR_CODE_WRONG_PARAM, - std::string("Signature header check error")); - } - - std::string decoded; - Crypto::Signature s; - if (!Tools::Base58::decode(req.signature.substr(header_len), decoded) - || sizeof(s) != decoded.size()) { - throw JsonRpc::JsonRpcError(CORE_RPC_ERROR_CODE_WRONG_PARAM, - std::string("Signature decoding error")); - return false; - } - - memcpy(&s, decoded.data(), sizeof(s)); - - res.sig_valid = Crypto::check_signature(hash, acc.spendPublicKey, s); - res.status = CORE_RPC_STATUS_OK; - - return true; -} - -bool RpcServer::on_get_difficulty_stat(const COMMAND_RPC_GET_DIFFICULTY_STAT::request &req, COMMAND_RPC_GET_DIFFICULTY_STAT::response &res) -{ - try { - if(!m_core.get_difficulty_stat(req.height, - IMinerHandler::stat_period::hour, - res.hour.block_num, - res.hour.avg_solve_time, - res.hour.stddev_solve_time, - res.hour.outliers_num, - res.hour.avg_diff, - res.hour.min_diff, - res.hour.max_diff)) - throw std::runtime_error("Failed to get hour difficulty statistics"); - if(!m_core.get_difficulty_stat(req.height, - IMinerHandler::stat_period::day, - res.day.block_num, - res.day.avg_solve_time, - res.day.stddev_solve_time, - res.day.outliers_num, - res.day.avg_diff, - res.day.min_diff, - res.day.max_diff)) - throw std::runtime_error("Failed to get day difficulty statistics"); - if(!m_core.get_difficulty_stat(req.height, - IMinerHandler::stat_period::week, - res.week.block_num, - res.week.avg_solve_time, - res.week.stddev_solve_time, - res.week.outliers_num, - res.week.avg_diff, - res.week.min_diff, - res.week.max_diff)) - throw std::runtime_error("Failed to get week difficulty statistics"); - if(!m_core.get_difficulty_stat(req.height, - IMinerHandler::stat_period::month, - res.month.block_num, - res.month.avg_solve_time, - res.month.stddev_solve_time, - res.month.outliers_num, - res.month.avg_diff, - res.month.min_diff, - res.month.max_diff)) - throw std::runtime_error("Failed to get month difficulty statistics"); - if(!m_core.get_difficulty_stat(req.height, - IMinerHandler::stat_period::halfyear, - res.halfyear.block_num, - res.halfyear.avg_solve_time, - res.halfyear.stddev_solve_time, - res.halfyear.outliers_num, - res.halfyear.avg_diff, - res.halfyear.min_diff, - res.halfyear.max_diff)) - throw std::runtime_error("Failed to get halfyear difficulty statistics"); - if(!m_core.get_difficulty_stat(req.height, - IMinerHandler::stat_period::year, - res.year.block_num, - res.year.avg_solve_time, - res.year.stddev_solve_time, - res.year.outliers_num, - res.year.avg_diff, - res.year.min_diff, - res.year.max_diff)) - throw std::runtime_error("Failed to get year difficulty statistics"); - - res.blocks30.block_num = 30; - if(!m_core.get_difficulty_stat(req.height, - IMinerHandler::stat_period::by_block_number, - res.blocks30.block_num, - res.blocks30.avg_solve_time, - res.blocks30.stddev_solve_time, - res.blocks30.outliers_num, - res.blocks30.avg_diff, - res.blocks30.min_diff, - res.blocks30.max_diff)) - throw std::runtime_error("Failed to get difficulty statistics for 30 blocks"); - res.blocks720.block_num = 720; - if(!m_core.get_difficulty_stat(req.height, - IMinerHandler::stat_period::by_block_number, - res.blocks720.block_num, - res.blocks720.avg_solve_time, - res.blocks720.stddev_solve_time, - res.blocks720.outliers_num, - res.blocks720.avg_diff, - res.blocks720.min_diff, - res.blocks720.max_diff)) - throw std::runtime_error("Failed to get difficulty statistics for 720 blocks"); - res.blocks5040.block_num = 5040; - if(!m_core.get_difficulty_stat(req.height, - IMinerHandler::stat_period::by_block_number, - res.blocks5040.block_num, - res.blocks5040.avg_solve_time, - res.blocks5040.stddev_solve_time, - res.blocks5040.outliers_num, - res.blocks5040.avg_diff, - res.blocks5040.min_diff, - res.blocks5040.max_diff)) - throw std::runtime_error("Failed to get difficulty statistics for 5040 blocks"); - res.blocks21900.block_num = 21900; - if(!m_core.get_difficulty_stat(req.height, - IMinerHandler::stat_period::by_block_number, - res.blocks21900.block_num, - res.blocks21900.avg_solve_time, - res.blocks21900.stddev_solve_time, - res.blocks21900.outliers_num, - res.blocks21900.avg_diff, - res.blocks21900.min_diff, - res.blocks21900.max_diff)) - throw std::runtime_error("Failed to get difficulty statistics for 21900 blocks"); - res.blocks131400.block_num = 131400; - if(!m_core.get_difficulty_stat(req.height, - IMinerHandler::stat_period::by_block_number, - res.blocks131400.block_num, - res.blocks131400.avg_solve_time, - res.blocks131400.stddev_solve_time, - res.blocks131400.outliers_num, - res.blocks131400.avg_diff, - res.blocks131400.min_diff, - res.blocks131400.max_diff)) - throw std::runtime_error("Failed to get difficulty statistics for 131400 blocks"); - res.blocks262800.block_num = 262800; - if(!m_core.get_difficulty_stat(req.height, - IMinerHandler::stat_period::by_block_number, - res.blocks262800.block_num, - res.blocks262800.avg_solve_time, - res.blocks262800.stddev_solve_time, - res.blocks262800.outliers_num, - res.blocks262800.avg_diff, - res.blocks262800.min_diff, - res.blocks262800.max_diff)) - throw std::runtime_error("Failed to get difficulty statistics for 262800 blocks"); - } catch (std::system_error &e) { - res.status = e.what(); - return false; - } catch (std::exception &e) { - res.status = "Error: " + std::string(e.what()); - return false; - } - - res.status = CORE_RPC_STATUS_OK; - - return true; -} + namespace { + + template + RpcServer::HandlerFunction binMethod(bool (RpcServer::*handler)(typename Command::request const &, + typename Command::response &)) + { + return [handler](RpcServer *obj, const HttpRequest &request, HttpResponse &response) { + boost::value_initialized req; + boost::value_initialized res; + + if (!loadFromBinaryKeyValue(static_cast(req), + request.getBody())) { + return false; + } + + bool result = (obj->*handler)(req, res); + response.setBody(storeToBinaryKeyValue(res.data())); + + return result; + }; + } + + template + RpcServer::HandlerFunction jsonMethod(bool (RpcServer::*handler)(typename Command::request const &, + typename Command::response &)) + { + return [handler](RpcServer *obj, const HttpRequest &request, HttpResponse &response) { + boost::value_initialized req; + boost::value_initialized res; + + if (!loadFromJson(static_cast(req), request.getBody())) { + return false; + } + + bool result = (obj->*handler)(req, res); + std::string cors_domain = obj->getCorsDomain(); + if (!cors_domain.empty()) { + response.addHeader("Access-Control-Allow-Origin", cors_domain); + response.addHeader("Access-Control-Allow-Headers", + "Origin, X-Requested-With, Content-Type, Accept"); + response.addHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS"); + } + response.addHeader("Content-Type", "application/json"); + response.setBody(storeToJson(res.data())); + + return result; + }; + } + + template + RpcServer::HandlerFunction httpMethod(bool (RpcServer::*handler)(typename Command::request const &, + typename Command::response &)) + { + return [handler](RpcServer *obj, const HttpRequest &request, HttpResponse &response) { + boost::value_initialized req; + boost::value_initialized res; + + if (!loadFromJson(static_cast(req), request.getBody())) { + return false; + } + + bool result = (obj->*handler)(req, res); + + std::string cors_domain = obj->getCorsDomain(); + if (!cors_domain.empty()) { + response.addHeader("Access-Control-Allow-Origin", cors_domain); + response.addHeader("Access-Control-Allow-Headers", + "Origin, X-Requested-With, Content-Type, Accept"); + response.addHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS"); + } + response.addHeader("Content-Type", "text/html; charset=UTF-8"); + response.addHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + response.addHeader("Expires", "0"); + response.setStatus(HttpResponse::HTTP_STATUS::STATUS_200); + + response.setBody(res); + + return result; + }; + } + + } // namespace + + std::unordered_map> + RpcServer::s_handlers = { + // binary handlers + {"/getblocks.bin", + {binMethod( + &RpcServer::onGetBlocks), false}}, + {"/queryblocks.bin", + {binMethod( + &RpcServer::onQueryBlocks), false}}, + {"/queryblockslite.bin", + {binMethod( + &RpcServer::onQueryBlocksLite), + false}}, + + {"/get_o_indexes.bin", + {binMethod( + &RpcServer::onGetIndexes), + false}}, + {"/getrandom_outs.bin", + {binMethod( + &RpcServer::onGetRandomOuts), + false}}, + {"/get_pool_changes.bin", + {binMethod( + &RpcServer::onGetPoolChanges), false}}, + {"/get_pool_changes_lite.bin", + {binMethod( + &RpcServer::onGetPoolChangesLite), + false}}, + + // http handlers + {"/", {httpMethod( + &RpcServer::onGetIndex), true}}, + {"/supply", {httpMethod( + &RpcServer::onGetSupply), false}}, + {"/paymentid", {httpMethod( + &RpcServer::onGetPaymentId), false}}, + + // json handlers + {"/getinfo", {jsonMethod( + &RpcServer::onGetInfo), true}}, + {"/getversion", {jsonMethod( + &RpcServer::onGetVersion), true}}, + {"/gethardwareinfo", {jsonMethod( + &RpcServer::onGetHardwareInfo), true}}, + {"/getheight", + {jsonMethod( + &RpcServer::onGetHeight), true}}, + {"/gettransactions", + {jsonMethod( + &RpcServer::onGetTransactions), + false}}, + {"/sendrawtransaction", + {jsonMethod( + &RpcServer::onSendRawTx), false}}, + {"/feeaddress", + {jsonMethod( + &RpcServer::onGetFeeAddress), true}}, + {"/peers", + {jsonMethod( + &RpcServer::onGetPeerList), true}}, + {"/get_mempool", + {jsonMethod(&RpcServer::onTransactionsPoolJson), + false}}, + {"/get_mempool_detailed", + {jsonMethod( + &RpcServer::onMempoolJson), false}}, + {"/getpeers", + {jsonMethod( + &RpcServer::onGetPeerList), true}}, + {"/getblocks", + {jsonMethod( + &RpcServer::onGetBlocks), false}}, + + {"/queryblocks", + {jsonMethod( + &RpcServer::onQueryBlocks), false}}, + {"/queryblockslite", + {jsonMethod( + &RpcServer::onQueryBlocksLite), + false}}, + {"/queryblocksdetailed", + {jsonMethod( + &RpcServer::onQueryBlocksDetailed), + false}}, + + {"/get_o_indexes", + {jsonMethod( + &RpcServer::onGetIndexes), + false}}, + {"/getrandom_outs", + {jsonMethod( + &RpcServer::onGetRandomOuts), + false}}, + {"/get_pool_changes", + {jsonMethod( + &RpcServer::onGetPoolChanges), false}}, + {"/get_pool_changes_lite", + {jsonMethod( + &RpcServer::onGetPoolChangesLite), + false}}, + {"/get_block_details_by_height", + {jsonMethod( + &RpcServer::onGetBlockDetailsByHeight), + false}}, + {"/get_block_details_by_hash", + {jsonMethod( + &RpcServer::onGetBlockDetailsByHash), + true}}, + {"/get_blocks_details_by_heights", + {jsonMethod( + &RpcServer::onGetBlocksDetailsByHeights), + false}}, + {"/get_blocks_details_by_hashes", + {jsonMethod( + &RpcServer::onGetBlocksDetailsByHashes), + false}}, + {"/get_blocks_hashes_by_timestamps", + {jsonMethod( + &RpcServer::onGetBlocksHashesByTimestamps), + false}}, + {"/get_transaction_details_by_hashes", + {jsonMethod( + &RpcServer::onGetTransactionsDetailsByHashes), + false}}, + {"/get_transaction_details_by_hash", + {jsonMethod( + &RpcServer::onGetTransactionDetailsByHash), + true}}, + {"/get_transactions_by_heights", + {jsonMethod( + &RpcServer::onGetTransactionsByHeights), + false}}, + {"/get_raw_transactions_by_heights", {jsonMethod( + &RpcServer::onGetRawTransactionsByHeights), false}}, + {"/get_raw_transactions_from_pool", {jsonMethod( + &RpcServer::onGetRawTransactionPool), false}}, + {"/get_transaction_hashes_by_payment_id", + {jsonMethod( + &RpcServer::onGetTransactionHashesByPaymentId), + false}}, + + // disabled in restricted rpc mode + {"/start_mining", + {jsonMethod( + &RpcServer::onStartMining), false}}, + {"/stop_mining", + {jsonMethod( + &RpcServer::onStopMining), false}}, + {"/stop_daemon", + {jsonMethod( + &RpcServer::onStopDaemon), true}}, + {"/get_difficulty_stat", + {jsonMethod( + &RpcServer::onGetDifficultyStat), + false}}, + + // json rpc + {"/json_rpc", + {std::bind(&RpcServer::processJsonRpcRequest, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3), + true}} + }; + + RpcServer::RpcServer(System::Dispatcher &dispatcher, Logging::ILogger &log, core &c, + NodeServer &p2p, const ICryptoNoteProtocolQuery &protocolQuery) + : HttpServer(dispatcher, log), + logger(log, "RpcServer"), + m_core(c), + m_p2p(p2p), + m_protocolQuery(protocolQuery) + { + } + + void RpcServer::processRequest(const HttpRequest &request, HttpResponse &response) + { + logger(TRACE) << "RPC request came: \n" << request << std::endl; + + /* + * Greetings to Aivve from the Karbowanec project for this idea. + */ + try { + auto url = request.getUrl(); + + auto it = s_handlers.find(url); + if (it == s_handlers.end()) { + if (Common::starts_with(url, "/api/")) { + // blocks + std::string block_height_method = "/api/block/height/"; + std::string block_hash_method = "/api/block/hash/"; + // transactions + std::string tx_height_method = "/api/transaction/height/"; + std::string tx_hash_method = "/api/transaction/hash/"; + std::string tx_mempool_method = "/api/mempool"; + std::string tx_mempool_detailed_method = "/api/mempool/detailed"; + std::string payment_id_method = "/api/payment_id/"; + // tools + std::string version_method = "/api/tools/version"; + std::string hardware_info_method = "/api/tools/hardwareinfo"; + + if (Common::starts_with(url, block_height_method)) { + std::string height_str = url.substr(block_height_method.size()); + uint32_t height = Common::integer_cast(height_str); + auto it = s_handlers.find("/get_block_details_by_height"); + if (!it->second.allowBusyCore && !isCoreReady()) { + response.setStatus(HttpResponse::STATUS_500); + response.setBody("Core is busy"); + return; + } + COMMAND_RPC_GET_BLOCK_DETAILS_BY_HEIGHT::request req; + req.blockHeight = height; + COMMAND_RPC_GET_BLOCK_DETAILS_BY_HEIGHT::response rsp; + bool r = onGetBlockDetailsByHeight(req, rsp); + if (r) { + response.addHeader("Content-Type", "application/json"); + response.setStatus(HttpResponse::HTTP_STATUS::STATUS_200); + response.setBody(storeToJson(rsp)); + } + else { + response.setStatus(HttpResponse::STATUS_500); + response.setBody("Internal error"); + } + return; + } + else if (Common::starts_with(url, block_hash_method)) { + std::string hash_str = url.substr(block_hash_method.size()); + + auto it = s_handlers.find("/get_block_details_by_hash"); + if (!it->second.allowBusyCore && !isCoreReady()) { + response.setStatus(HttpResponse::STATUS_500); + response.setBody("Core is busy"); + return; + } + COMMAND_RPC_GET_BLOCK_DETAILS_BY_HASH::request req; + req.hash = hash_str; + COMMAND_RPC_GET_BLOCK_DETAILS_BY_HASH::response rsp; + bool r = onGetBlockDetailsByHash(req, rsp); + if (r) { + response.addHeader("Content-Type", "application/json"); + response.setStatus(HttpResponse::HTTP_STATUS::STATUS_200); + response.setBody(storeToJson(rsp)); + } + else { + response.setStatus(HttpResponse::STATUS_500); + response.setBody("Internal error"); + } + return; + } + else if (Common::starts_with(url, tx_height_method)) { + std::string hash_str = url.substr(tx_height_method.size()); + uint32_t tempInt = std::stoi(hash_str); + auto it = s_handlers.find("/get_transactions_by_heights"); + if (!it->second.allowBusyCore && !isCoreReady()) { + response.setStatus(HttpResponse::STATUS_500); + response.setBody("Core is busy"); + return; + } + + Block blk; + std::vector vh; + uint32_t upperBorder = std::min(tempInt, m_core.get_current_blockchain_height()); + Crypto::Hash blockHash = m_core.getBlockIdByHeight(upperBorder); + + if (!m_core.getBlockByHash(blockHash, blk)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: can't get block by hash. Hash = " + + podToHex(blockHash) + '.'}; + } + + if (blk.baseTransaction.inputs.front().type() != typeid(BaseInput)) { + throw JsonRpc::JsonRpcError{ + CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: coinbase transaction in the block has the wrong type" + }; + } + + for (Crypto::Hash &bTxs : blk.transactionHashes) { + vh.push_back(bTxs); + } + + vh.push_back(getObjectHash(blk.baseTransaction)); + + std::list missedTxs; + std::list txs; + + m_core.getTransactions(vh, txs, missedTxs, true); + + logger(DEBUGGING) << "Found " << txs.size() << "/" << vh.size() + << " transactions on the blockchain."; + + TransactionDetails2 transactionsDetails; + + bool r = m_core.fillTransactionDetails(txs.back(), transactionsDetails); + if (r) { + response.addHeader("Content-Type", "application/json"); + response.setStatus(HttpResponse::HTTP_STATUS::STATUS_200); + response.setBody(storeToJson(transactionsDetails)); + } + else { + response.setStatus(HttpResponse::STATUS_500); + response.setBody("Internal error"); + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: can't fill transaction details."}; + } + + return; + } + else if (Common::starts_with(url, tx_hash_method)) { + std::string hash_str = url.substr(tx_hash_method.size()); + auto it = s_handlers.find("/get_transaction_details_by_hash"); + if (!it->second.allowBusyCore && !isCoreReady()) { + response.setStatus(HttpResponse::STATUS_500); + response.setBody("Core is busy"); + return; + } + COMMAND_RPC_GET_TRANSACTION_DETAILS_BY_HASH::request req; + req.hash = hash_str; + COMMAND_RPC_GET_TRANSACTION_DETAILS_BY_HASH::response rsp; + bool r = onGetTransactionDetailsByHash(req, rsp); + if (r) { + response.addHeader("Content-Type", "application/json"); + response.setStatus(HttpResponse::HTTP_STATUS::STATUS_200); + response.setBody(storeToJson(rsp)); + } + else { + response.setStatus(HttpResponse::STATUS_500); + response.setBody("Internal error"); + } + return; + + } + else if (Common::starts_with(url, tx_mempool_method)) { + auto it = s_handlers.find("/get_mempool"); + if (!it->second.allowBusyCore && !isCoreReady()) { + response.setStatus(HttpResponse::STATUS_500); + response.setBody("Core is busy"); + return; + } + COMMAND_RPC_GET_POOL::request req; + COMMAND_RPC_GET_POOL::response rsp; + bool r = onTransactionsPoolJson(req, rsp); + if (r) { + response.addHeader("Content-Type", "application/json"); + response.setStatus(HttpResponse::HTTP_STATUS::STATUS_200); + response.setBody(storeToJson(rsp)); + } + else { + response.setStatus(HttpResponse::STATUS_500); + response.setBody("Internal error"); + } + + return; + } + else if (Common::starts_with(url, tx_mempool_detailed_method)) { + auto it = s_handlers.find("/get_mempool_detailed"); + if (!it->second.allowBusyCore && !isCoreReady()) { + response.setStatus(HttpResponse::STATUS_500); + response.setBody("Core is busy"); + return; + } + COMMAND_RPC_GET_MEMPOOL::request req; + COMMAND_RPC_GET_MEMPOOL::response rsp; + bool r = onMempoolJson(req, rsp); + if (r) { + response.addHeader("Content-Type", "application/json"); + response.setStatus(HttpResponse::HTTP_STATUS::STATUS_200); + response.setBody(storeToJson(rsp)); + } + else { + response.setStatus(HttpResponse::STATUS_500); + response.setBody("Internal error"); + } + + return; + } + else if (Common::starts_with(url, payment_id_method)) { + std::string pid_str = url.substr(payment_id_method.size()); + + auto it = s_handlers.find("/get_transaction_hashes_by_payment_id"); + if (!it->second.allowBusyCore && !isCoreReady()) { + response.setStatus(HttpResponse::STATUS_500); + response.setBody("Core is busy"); + return; + } + COMMAND_RPC_GET_TRANSACTION_HASHES_BY_PAYMENT_ID::request req; + req.paymentId = pid_str; + COMMAND_RPC_GET_TRANSACTION_HASHES_BY_PAYMENT_ID::response rsp; + bool r = onGetTransactionHashesByPaymentId(req, rsp); + if (r) { + response.addHeader("Content-Type", "application/json"); + response.setStatus(HttpResponse::HTTP_STATUS::STATUS_200); + response.setBody(storeToJson(rsp)); + } + else { + response.setStatus(HttpResponse::STATUS_500); + response.setBody("Internal error"); + } + return; + } + else if (Common::starts_with(url, version_method)) { + auto it = s_handlers.find("/getversion"); + if (!it->second.allowBusyCore && !isCoreReady()) { + response.setStatus(HttpResponse::STATUS_500); + response.setBody("Core is busy"); + return; + } + COMMAND_RPC_GET_VERSION::request req; + COMMAND_RPC_GET_VERSION::response rsp; + bool r = onGetVersion(req, rsp); + if (r) { + response.addHeader("Content-Type", "application/json"); + response.setStatus(HttpResponse::HTTP_STATUS::STATUS_200); + response.setBody(storeToJson(rsp)); + } + else { + response.setStatus(HttpResponse::STATUS_500); + response.setBody("Internal error"); + } + return; + } + else if (Common::starts_with(url, hardware_info_method)) { + auto it = s_handlers.find("/gethardwareinfo"); + if (!it->second.allowBusyCore && !isCoreReady()) { + response.setStatus(HttpResponse::STATUS_500); + response.setBody("Core is busy"); + return; + } + COMMAND_RPC_GET_HARDWARE_INFO::request req; + COMMAND_RPC_GET_HARDWARE_INFO::response rsp; + bool r = onGetHardwareInfo(req, rsp); + if (r) { + response.addHeader("Content-Type", "application/json"); + response.setStatus(HttpResponse::HTTP_STATUS::STATUS_200); + response.setBody(storeToJson(rsp)); + } + else { + response.setStatus(HttpResponse::STATUS_500); + response.setBody("Internal error"); + } + return; + } + response.setStatus(HttpResponse::STATUS_404); + return; + } + else { + response.setStatus(HttpResponse::STATUS_404); + return; + } + } + + if (!it->second.allowBusyCore && !isCoreReady()) { + response.setStatus(HttpResponse::STATUS_500); + response.setBody("Core is busy"); + return; + } + + it->second.handler(this, request, response); + + } catch (const JsonRpc::JsonRpcError &err) { + response.addHeader("Content-Type", "application/json"); + response.setStatus(HttpResponse::STATUS_500); + response.setBody(storeToJsonValue(err).toString()); + } catch (const std::exception &e) { + response.setStatus(HttpResponse::STATUS_500); + response.setBody(e.what()); + } + } + + bool RpcServer::processJsonRpcRequest(const HttpRequest &request, HttpResponse &response) + { + using namespace JsonRpc; + + response.addHeader("Content-Type", "application/json"); + if (!m_cors_domain.empty()) { + response.addHeader("Access-Control-Allow-Origin", m_cors_domain); + response.addHeader("Access-Control-Allow-Headers", + "Origin, X-Requested-With, Content-Type, Accept"); + response.addHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS"); + } + + JsonRpcRequest jsonRequest; + JsonRpcResponse jsonResponse; + + try { + logger(TRACE) << "JSON-RPC request: " << request.getBody(); + + jsonRequest.parseRequest(request.getBody()); + jsonResponse.setId(jsonRequest.getId()); // copy id + + static std::unordered_map> + jsonRpcHandlers = { + {"getblockcount", {makeMemberMethod(&RpcServer::onGetBlockCount), true}}, + {"on_getblockhash", {makeMemberMethod(&RpcServer::onGetBlockHash), false}}, + {"getblocktemplate", + {makeMemberMethod(&RpcServer::onGetBlockTemplate), false}}, + {"getcurrencyid", {makeMemberMethod(&RpcServer::onGetCurrencyId), true}}, + {"submitblock", {makeMemberMethod(&RpcServer::onSubmitBlock), false}}, + {"getlastblockheader", + {makeMemberMethod(&RpcServer::onGetLastBlockHeader), false}}, + {"getblockheaderbyhash", + {makeMemberMethod(&RpcServer::onGetBlockHeaderByHash), false}}, + {"getblockheaderbyheight", + {makeMemberMethod(&RpcServer::onGetBlockHeaderByHeight), false}}, + {"getblockbyhash", + {makeMemberMethod(&RpcServer::onGetBlockDetailsByHash), true}}, + {"f_blocks_list_json", + {makeMemberMethod(&RpcServer::onBlocksListJson), false}}, + {"f_block_json", {makeMemberMethod(&RpcServer::onBlockJson), false}}, + {"f_transaction_json", + {makeMemberMethod(&RpcServer::onTransactionJson), false}}, + {"get_mempool", + {makeMemberMethod(&RpcServer::onTransactionsPoolJson), false}}, + {"get_mempool_detailed", + {makeMemberMethod(&RpcServer::onMempoolJson), false}}, + {"f_pool_json", {makeMemberMethod(&RpcServer::onPoolJson), false}}, + {"transactions_by_payment_id", + {makeMemberMethod(&RpcServer::onTransactionsByPaymentId), false}}, + {"get_transaction_hashes_by_payment_id", + {makeMemberMethod(&RpcServer::onGetTransactionHashesByPaymentId), false}}, + {"get_transaction_details_by_hashes", + {makeMemberMethod(&RpcServer::onGetTransactionsDetailsByHashes), false}}, + {"get_transactions_by_heights", + {makeMemberMethod(&RpcServer::onGetTransactionsByHeights), false}}, + {"gettransaction", + {makeMemberMethod(&RpcServer::onGetTransactionDetailsByHash), false}}, + {"get_blocks_details_by_heights", + {makeMemberMethod(&RpcServer::onGetBlocksDetailsByHeights), false}}, + {"get_block_details_by_height", + {makeMemberMethod(&RpcServer::onGetBlockDetailsByHeight), false}}, + {"get_blocks_details_by_hashes", + {makeMemberMethod(&RpcServer::onGetBlocksDetailsByHashes), false}}, + {"get_blocks_hashes_by_timestamps", + {makeMemberMethod(&RpcServer::onGetBlocksHashesByTimestamps), false}}, + {"check_tx_key", {makeMemberMethod(&RpcServer::onCheckTxKey), false}}, + {"check_tx_with_view_key", + {makeMemberMethod(&RpcServer::onCheckTxWithViewKey), false}}, + {"check_tx_proof", + {makeMemberMethod(&RpcServer::onCheckTxProof), false}}, + {"check_reserve_proof", + {makeMemberMethod(&RpcServer::onCheckReserveProof), false}}, + {"validateaddress", + {makeMemberMethod(&RpcServer::onValidateAddress), false}}, + {"verifymessage", {makeMemberMethod(&RpcServer::onVerifyMessage), false}} + }; + + auto it = jsonRpcHandlers.find(jsonRequest.getMethod()); + if (it == jsonRpcHandlers.end()) { + throw JsonRpcError(JsonRpc::errMethodNotFound); + } + if (!it->second.allowBusyCore && !isCoreReady()) { + throw JsonRpcError(CORE_RPC_ERROR_CODE_CORE_BUSY, "Core is busy"); + } + it->second.handler(this, jsonRequest, jsonResponse); + } catch (const JsonRpcError &err) { + jsonResponse.setError(err); + } catch (const std::exception &e) { + jsonResponse.setError(JsonRpcError(JsonRpc::errInternalError, e.what())); + } + + response.setBody(jsonResponse.getBody()); + logger(TRACE) << "JSON-RPC response: " << jsonResponse.getBody(); + + return true; + } + + bool RpcServer::restrictRPC(const bool is_restricted) + { + m_restricted_rpc = is_restricted; + + return true; + } + + bool RpcServer::enableCors(const std::string domain) + { + m_cors_domain = domain; + + return true; + } + + std::string RpcServer::getCorsDomain() + { + return m_cors_domain; + } + + bool RpcServer::setFeeAddress(const std::string &fee_address, const AccountPublicAddress &fee_acc) + { + m_fee_address = fee_address; + m_fee_acc = fee_acc; + + return true; + } + + bool RpcServer::setViewKey(const std::string &view_key) + { + Crypto::Hash private_view_key_hash; + size_t size; + if (!Common::fromHex(view_key, &private_view_key_hash, sizeof(private_view_key_hash), size) + || size != sizeof(private_view_key_hash)) { + logger(INFO) << "Could not parse private view key"; + return false; + } + + m_view_key = *(struct Crypto::SecretKey *) &private_view_key_hash; + + return true; + } + + bool RpcServer::setContactInfo(const std::string &contact) + { + m_contact_info = contact; + + return true; + } + + bool RpcServer::isCoreReady() + { + return m_core.currency().isTestnet() || m_p2p.get_payload_object().isSynchronized(); + } + + bool RpcServer::masternodeCheckIncomingTx(const BinaryArray &tx_blob) + { + Crypto::Hash tx_hash = NULL_HASH; + Crypto::Hash tx_prefixt_hash = NULL_HASH; + Transaction tx; + if (!parseAndValidateTransactionFromBinaryArray(tx_blob, tx, tx_hash, tx_prefixt_hash)) { + logger(INFO) << "Could not parse tx from blob"; + return false; + } + + // always relay fusion transactions + uint64_t inputs_amount = 0; + get_inputs_money_amount(tx, inputs_amount); + uint64_t outputs_amount = get_outs_money_amount(tx); + + const uint64_t fee = inputs_amount - outputs_amount; + bool isFusionTransaction = m_core.currency().isFusionTransaction( + tx, tx_blob.size(), m_core.get_current_blockchain_height() - 1); + if (fee == 0 && isFusionTransaction) { + logger(DEBUGGING) << "Masternode received fusion transaction, relaying with no fee check"; + return true; + } + + CryptoNote::TransactionPrefix transaction = *static_cast(&tx); + + std::vector out; + uint64_t amount; + + if (!CryptoNote::findOutputsToAccount(transaction, m_fee_acc, m_view_key, out, amount)) { + logger(INFO) << "Could not find outputs to masternode fee address"; + return false; + } + + if (amount != 0) { + logger(INFO) << "Masternode received relayed transaction fee: " + << m_core.currency().formatAmount(amount) << " QWC"; + return true; + } + + return false; + } + + bool RpcServer::onGetBlocks(const COMMAND_RPC_GET_BLOCKS_FAST::request &req, + COMMAND_RPC_GET_BLOCKS_FAST::response &res) + { + // TODO: code duplication see InProcessNode::doGetNewBlocks() + if (req.block_ids.empty()) { + res.status = "Failed"; + return false; + } + + if (req.block_ids.back() != m_core.getBlockIdByHeight(0)) { + res.status = "Failed"; + return false; + } + + uint32_t totalBlockCount; + uint32_t startBlockIndex; + std::vector supplement = m_core.findBlockchainSupplement( + req.block_ids, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT, totalBlockCount, startBlockIndex); + + res.current_height = totalBlockCount; + res.start_height = startBlockIndex; + + for (const auto &blockId : supplement) { + assert(m_core.have_block(blockId)); + auto completeBlock = m_core.getBlock(blockId); + assert(completeBlock != nullptr); + + res.blocks.resize(res.blocks.size() + 1); + res.blocks.back().block = asString(toBinaryArray(completeBlock->getBlock())); + + res.blocks.back().txs.reserve(completeBlock->getTransactionCount()); + for (size_t i = 0; i < completeBlock->getTransactionCount(); ++i) { + res.blocks.back().txs.push_back( + asString(toBinaryArray(completeBlock->getTransaction(i)))); + } + } + + res.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onQueryBlocks(const COMMAND_RPC_QUERY_BLOCKS::request &req, + COMMAND_RPC_QUERY_BLOCKS::response &res) + { + uint32_t startHeight; + uint32_t currentHeight; + uint32_t fullOffset; + + if (!m_core.queryBlocks(req.block_ids, req.timestamp, startHeight, currentHeight, fullOffset, + res.items)) { + res.status = "Failed to perform query"; + return false; + } + + res.start_height = startHeight; + res.current_height = currentHeight; + res.full_offset = fullOffset; + + res.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onQueryBlocksLite(const COMMAND_RPC_QUERY_BLOCKS_LITE::request &req, + COMMAND_RPC_QUERY_BLOCKS_LITE::response &res) + { + uint32_t startHeight; + uint32_t currentHeight; + uint32_t fullOffset; + if (!m_core.queryBlocksLite(req.blockIds, req.timestamp, startHeight, currentHeight, fullOffset, + res.items)) { + res.status = "Failed to perform query"; + return false; + } + + res.startHeight = startHeight; + res.currentHeight = currentHeight; + res.fullOffset = fullOffset; + + res.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onQueryBlocksDetailed(const COMMAND_RPC_QUERY_BLOCKS_DETAILED::request &req, + COMMAND_RPC_QUERY_BLOCKS_DETAILED::response &res) + { + uint32_t startIndex; + uint32_t currentIndex; + uint32_t fullOffset; + + if (!m_core.queryBlocksDetailed(req.blockIds, req.timestamp, startIndex, currentIndex, + fullOffset, res.blocks)) { + res.status = "Failed to perform query"; + return false; + } + + res.startHeight = startIndex; + res.currentHeight = currentIndex; + res.fullOffset = fullOffset; + + res.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onGetIndexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request &req, + COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response &res) + { + std::vector outputIndexes; + if (!m_core.get_tx_outputs_gindexs(req.txid, outputIndexes)) { + res.status = "Failed"; + return true; + } + + res.o_indexes.assign(outputIndexes.begin(), outputIndexes.end()); + logger(TRACE) << "COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES: [" << res.o_indexes.size() << "]"; + + res.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onGetRandomOuts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request &req, + COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response &res) + { + res.status = "Failed"; + 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 << "]:"; + + assert(ofa.outs.size() && "internal error: ofa.outs.size() is empty"); + + std::for_each(ofa.outs.begin(), ofa.outs.end(), + [&](out_entry &oe) { ss << oe.global_amount_index << " "; }); + + ss << ENDL; + }); + std::string s = ss.str(); + logger(TRACE) << "COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS: " << ENDL << s; + + res.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onGetPoolChanges(const COMMAND_RPC_GET_POOL_CHANGES::request &req, + COMMAND_RPC_GET_POOL_CHANGES::response &rsp) + { + rsp.status = CORE_RPC_STATUS_OK; + std::vector addedTransactions; + rsp.isTailBlockActual = m_core.getPoolChanges(req.tailBlockId, req.knownTxsIds, + addedTransactions, rsp.deletedTxsIds); + for (auto &tx : addedTransactions) { + BinaryArray txBlob; + if (!toBinaryArray(tx, txBlob)) { + rsp.status = "Internal error"; + break; + } + + rsp.addedTxs.emplace_back(std::move(txBlob)); + } + return true; + } + + bool RpcServer::onGetPoolChangesLite(const COMMAND_RPC_GET_POOL_CHANGES_LITE::request &req, + COMMAND_RPC_GET_POOL_CHANGES_LITE::response &rsp) + { + rsp.status = CORE_RPC_STATUS_OK; + rsp.isTailBlockActual = m_core.getPoolChangesLite(req.tailBlockId, req.knownTxsIds, + rsp.addedTxs, rsp.deletedTxsIds); + + return true; + } + + bool RpcServer::onGetBlocksDetailsByHeights( + const COMMAND_RPC_GET_BLOCKS_DETAILS_BY_HEIGHTS::request &req, + COMMAND_RPC_GET_BLOCKS_DETAILS_BY_HEIGHTS::response &rsp) + { + try { + std::vector blockDetails; + for (const uint32_t &height : req.blockHeights) { + if (m_core.get_current_blockchain_height() <= height) { + throw JsonRpc::JsonRpcError{ + CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT, + std::string("To big height: ") + std::to_string(height) + + ", current blockchain height = " + + std::to_string(m_core.get_current_blockchain_height() - 1) + }; + } + Hash block_hash = m_core.getBlockIdByHeight(height); + Block blk; + if (!m_core.getBlockByHash(block_hash, blk)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: can't get block by height " + + std::to_string(height) + '.'}; + } + BlockDetails2 detail; + if (!m_core.fillBlockDetails(blk, detail)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: can't fill block details."}; + } + blockDetails.push_back(detail); + } + rsp.blocks = std::move(blockDetails); + } catch (std::system_error &e) { + rsp.status = e.what(); + return false; + } catch (std::exception &e) { + rsp.status = "Error: " + std::string(e.what()); + return false; + } + + rsp.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onGetBlocksDetailsByHashes( + const COMMAND_RPC_GET_BLOCKS_DETAILS_BY_HASHES::request &req, + COMMAND_RPC_GET_BLOCKS_DETAILS_BY_HASHES::response &rsp) + { + try { + std::vector blockDetails; + for (const Crypto::Hash &hash : req.blockHashes) { + Block blk; + if (!m_core.getBlockByHash(hash, blk)) { + // throw JsonRpc::JsonRpcError{ + // CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + // "Internal error: can't get block by hash " + Common::PodToHex(hash) + '.' + //}; + } + BlockDetails2 detail; + if (!m_core.fillBlockDetails(blk, detail)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: can't fill block details."}; + } + blockDetails.push_back(detail); + } + rsp.blocks = std::move(blockDetails); + } catch (std::system_error &e) { + rsp.status = e.what(); + return false; + } catch (std::exception &e) { + rsp.status = "Error: " + std::string(e.what()); + return false; + } + + rsp.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onGetBlockDetailsByHeight( + const COMMAND_RPC_GET_BLOCK_DETAILS_BY_HEIGHT::request &req, + COMMAND_RPC_GET_BLOCK_DETAILS_BY_HEIGHT::response &rsp) + { + try { + BlockDetails2 blockDetails; + if (m_core.get_current_blockchain_height() <= req.blockHeight) { + throw JsonRpc::JsonRpcError{ + CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT, + std::string("To big height: ") + std::to_string(req.blockHeight) + + ", current blockchain height = " + + std::to_string(m_core.get_current_blockchain_height() - 1) + }; + } + Hash block_hash = m_core.getBlockIdByHeight(req.blockHeight); + Block blk; + if (!m_core.getBlockByHash(block_hash, blk)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: can't get block by height " + + std::to_string(req.blockHeight) + '.'}; + } + if (!m_core.fillBlockDetails(blk, blockDetails)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: can't fill block details."}; + } + rsp.block = blockDetails; + } catch (std::system_error &e) { + rsp.status = e.what(); + return false; + } catch (std::exception &e) { + rsp.status = "Error: " + std::string(e.what()); + return false; + } + + rsp.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onGetBlockDetailsByHash(const COMMAND_RPC_GET_BLOCK_DETAILS_BY_HASH::request &req, + COMMAND_RPC_GET_BLOCK_DETAILS_BY_HASH::response &rsp) + { + try { + BlockDetails2 blockDetails; + Crypto::Hash block_hash; + + if (!parse_hash256(req.hash, block_hash)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_WRONG_PARAM, + "Failed to parse hex representation of block hash. Hex = " + + req.hash + '.'}; + } + Block blk; + if (!m_core.getBlockByHash(block_hash, blk)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: can't get block by hash. Hash = " + + req.hash + '.'}; + } + if (!m_core.fillBlockDetails(blk, blockDetails)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: can't fill block details."}; + } + rsp.block = blockDetails; + } catch (std::system_error &e) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, e.what()}; + return false; + } catch (std::exception &e) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Error: " + std::string(e.what())}; + return false; + } + rsp.status = CORE_RPC_STATUS_OK; + return true; + } + + bool RpcServer::onGetBlocksHashesByTimestamps( + const COMMAND_RPC_GET_BLOCKS_HASHES_BY_TIMESTAMPS::request &req, + COMMAND_RPC_GET_BLOCKS_HASHES_BY_TIMESTAMPS::response &rsp) + { + try { + uint32_t count; + std::vector blockHashes; + if (!m_core.get_blockchain_storage().getBlockIdsByTimestamp( + req.timestampBegin, req.timestampEnd, req.limit, blockHashes, count)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: can't get blocks within timestamps " + + std::to_string(req.timestampBegin) + " - " + + std::to_string(req.timestampEnd) + "."}; + } + rsp.blockHashes = std::move(blockHashes); + rsp.count = count; + } catch (std::system_error &e) { + rsp.status = e.what(); + return false; + } catch (std::exception &e) { + rsp.status = "Error: " + std::string(e.what()); + return false; + } + + rsp.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onGetTransactionsDetailsByHashes( + const COMMAND_RPC_GET_TRANSACTIONS_DETAILS_BY_HASHES::request &req, + COMMAND_RPC_GET_TRANSACTIONS_DETAILS_BY_HASHES::response &rsp) + { + try { + std::vector transactionsDetails; + transactionsDetails.reserve(req.transactionHashes.size()); + + std::list missed_txs; + std::list txs; + m_core.getTransactions(req.transactionHashes, txs, missed_txs, true); + + if (!txs.empty()) { + for (const Transaction &tx : txs) { + TransactionDetails2 txDetails; + if (!m_core.fillTransactionDetails(tx, txDetails)) { + throw JsonRpc::JsonRpcError{ + CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: can't fill transaction details." + }; + } + transactionsDetails.push_back(txDetails); + } + + rsp.transactions = std::move(transactionsDetails); + rsp.status = CORE_RPC_STATUS_OK; + } + if (txs.empty() || !missed_txs.empty()) { + std::ostringstream ss; + std::string separator; + for (auto h : missed_txs) { + ss << separator << Common::podToHex(h); + separator = ","; + } + rsp.status = "transaction(s) not found: " + ss.str() + "."; + } + } catch (std::system_error &e) { + rsp.status = e.what(); + return false; + } catch (std::exception &e) { + rsp.status = "Error: " + std::string(e.what()); + return false; + } + + return true; + } + + bool RpcServer::onGetTransactionDetailsByHash( + const COMMAND_RPC_GET_TRANSACTION_DETAILS_BY_HASH::request &req, + COMMAND_RPC_GET_TRANSACTION_DETAILS_BY_HASH::response &rsp) + { + try { + std::list missed_txs; + std::list txs; + std::vector hashes; + Crypto::Hash tx_hash; + if (!parse_hash256(req.hash, tx_hash)) { + throw JsonRpc::JsonRpcError{ + CORE_RPC_ERROR_CODE_WRONG_PARAM, + "Failed to parse hex representation of transaction hash. Hex = " + req.hash + '.' + }; + } + hashes.push_back(tx_hash); + m_core.getTransactions(hashes, txs, missed_txs, true); + + if (txs.empty() || !missed_txs.empty()) { + std::string hash_str = Common::podToHex(missed_txs.back()); + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_WRONG_PARAM, + "transaction wasn't found. Hash = " + hash_str + '.'}; + } + + TransactionDetails2 transactionsDetails; + if (!m_core.fillTransactionDetails(txs.back(), transactionsDetails)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: can't fill transaction details."}; + } + + rsp.transaction = std::move(transactionsDetails); + } catch (std::system_error &e) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, e.what()}; + return false; + } catch (std::exception &e) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Error: " + std::string(e.what())}; + return false; + } + rsp.status = CORE_RPC_STATUS_OK; + return true; + } + + bool RpcServer::onGetTransactionHashesByPaymentId( + const COMMAND_RPC_GET_TRANSACTION_HASHES_BY_PAYMENT_ID::request &req, + COMMAND_RPC_GET_TRANSACTION_HASHES_BY_PAYMENT_ID::response &rsp) + { + Crypto::Hash pid_hash; + if (!parse_hash256(req.paymentId, pid_hash)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_WRONG_PARAM, + "Failed to parse hex representation of payment id. Hex = " + + req.paymentId + '.'}; + } + try { + rsp.transactionHashes = m_core.getTransactionHashesByPaymentId(pid_hash); + } catch (std::system_error &e) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, e.what()}; + return false; + } catch (std::exception &e) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Error: " + std::string(e.what())}; + return false; + } + rsp.status = CORE_RPC_STATUS_OK; + return true; + } +// +// HTTP handlers +// + + bool RpcServer::onGetIndex(const COMMAND_HTTP::request &req, COMMAND_HTTP::response &res) + { + const std::string index_start = + R"(

)" + "

Qwertycoin Masternode

" + R"( version + )"; + const std::string index_finish = "

"; + const std::time_t uptime = std::time(nullptr) - m_core.getStartTime(); + const std::string uptime_str = std::to_string((unsigned int) floor(uptime / 60.0 / 60.0 / 24.0)) + + "d " + + std::to_string((unsigned int) floor(fmod((uptime / 60.0 / 60.0), 24.0))) + "h " + + std::to_string((unsigned int) floor(fmod((uptime / 60.0), 60.0))) + "m " + + std::to_string((unsigned int) fmod(uptime, 60.0)) + "s"; + uint32_t top_block_index = m_core.get_current_blockchain_height() - 1; + uint32_t top_known_block_index = + std::max(static_cast(1), m_protocolQuery.getObservedHeight()) - 1; + size_t outConn = m_p2p.get_outgoing_connections_count(); + size_t incConn = m_p2p.get_connections_count() - outConn; + Crypto::Hash last_block_hash = m_core.getBlockIdByHeight(top_block_index); + size_t white_peerlist_size = m_p2p.getPeerlistManager().get_white_peers_count(); + size_t grey_peerlist_size = m_p2p.getPeerlistManager().get_gray_peers_count(); + size_t alt_blocks_count = m_core.get_alternative_blocks_count(); + size_t total_tx_count = m_core.get_blockchain_total_transactions() - top_block_index + 1; + + size_t tx_pool_count = m_core.get_pool_transactions_count(); + + const std::string body = index_start + PROJECT_VERSION_LONG + " powered by " + + (m_core.currency().isTestnet() ? "testnet" : "mainnet") + "
    " + "
  • " + + "Synchronization status: " + std::to_string(top_block_index) + "/" + + std::to_string(top_known_block_index) + "
  • " + + "Last block hash: " + Common::podToHex(last_block_hash) + "
  • " + "
  • " + + "Difficulty: " + std::to_string(m_core.getNextBlockDifficulty(0)) + "
  • " + "
  • " + + "Alt. blocks: " + std::to_string(alt_blocks_count) + "
  • " + "
  • " + + "Total transactions in network: " + std::to_string(total_tx_count) + "
  • " + "
  • " + + "Transactions in pool: " + std::to_string(tx_pool_count) + "
  • " + "
  • " + + "Connections:" + "
      " + "
    • " + "RPC: " + std::to_string(get_connections_count()) + + "
    • " + "
    • " + "OUT: " + std::to_string(outConn) + "
    • " + "
    • " + + "INC: " + std::to_string(incConn) + "
    • " + "
    " + "
  • " + "
  • " + + "Peers: " + std::to_string(white_peerlist_size) + " white, " + + std::to_string(grey_peerlist_size) + " grey" + "
  • " + "
  • " + + "Uptime: " + uptime_str + "
  • " + "
" + index_finish; + + res = body; + + return true; + } + + bool RpcServer::onGetSupply(const COMMAND_HTTP::request &req, COMMAND_HTTP::response &res) + { + std::string already_generated_coins = + m_core.currency().formatAmount(m_core.getTotalGeneratedAmount()); + res = already_generated_coins; + + return true; + } + + bool RpcServer::onGetPaymentId(const COMMAND_HTTP::request &req, COMMAND_HTTP::response &res) + { + res = Common::podToHex(Crypto::rand()); + return true; + } + +// +// JSON handlers +// + + bool RpcServer::onGetInfo(const COMMAND_RPC_GET_INFO::request &req, + COMMAND_RPC_GET_INFO::response &res) + { + res.height = m_core.get_current_blockchain_height(); + res.last_known_block_index = + std::max(static_cast(1), m_protocolQuery.getObservedHeight()) - 1; + if (res.height == res.last_known_block_index) { + // node is synced + res.difficulty = m_core.getNextBlockDifficulty(time(nullptr)); + } + else { + // node is not synced yet + res.difficulty = m_core.getNextBlockDifficulty(0); + } + res.tx_count = m_core.get_blockchain_total_transactions() - res.height; // without coinbase + res.tx_pool_size = m_core.get_pool_transactions_count(); + res.alt_blocks_count = m_core.get_alternative_blocks_count(); + uint64_t total_conn = m_p2p.get_connections_count(); + res.outgoing_connections_count = m_p2p.get_outgoing_connections_count(); + res.incoming_connections_count = total_conn - res.outgoing_connections_count; + res.rpc_connections_count = get_connections_count(); + res.white_peerlist_size = m_p2p.getPeerlistManager().get_white_peers_count(); + res.grey_peerlist_size = m_p2p.getPeerlistManager().get_gray_peers_count(); + Crypto::Hash last_block_hash = + m_core.getBlockIdByHeight(m_core.get_current_blockchain_height() - 1); + res.top_block_hash = Common::podToHex(last_block_hash); + res.version = PROJECT_VERSION_LONG; + res.fee_address = m_fee_address.empty() ? std::string() : m_fee_address; + res.contact = m_contact_info.empty() ? std::string() : m_contact_info; + res.min_tx_fee = m_core.getMinimalFee(); + res.readable_tx_fee = m_core.currency().formatAmount(m_core.getMinimalFee()); + res.start_time = (uint64_t) m_core.getStartTime(); + // NOTE: that large uint64_t number is unsafe in JavaScript environment + // and therefore as a JSON value so we display it as a formatted string + res.already_generated_coins = m_core.currency().formatAmount(m_core.getTotalGeneratedAmount()); + + Block blk; + if (!m_core.getBlockByHash(last_block_hash, blk)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: can't get last block by hash."}; + } + + if (blk.baseTransaction.inputs.front().type() != typeid(BaseInput)) { + throw JsonRpc::JsonRpcError{ + CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: coinbase transaction in the block has the wrong type" + }; + } + + BLOCK_HEADER_RESPONSE_ENTRY block_header; + uint32_t lastBlockHeight = boost::get(blk.baseTransaction.inputs.front()).blockIndex; + + Crypto::Hash tmp_hash = m_core.getBlockIdByHeight(lastBlockHeight); + bool is_orphaned = last_block_hash != tmp_hash; + fillBlockHeaderResponse(blk, is_orphaned, lastBlockHeight, last_block_hash, block_header); + + res.block_major_version = block_header.major_version; + res.block_minor_version = block_header.minor_version; + res.last_block_timestamp = block_header.timestamp; + res.last_block_reward = block_header.reward; + m_core.getBlockDifficulty(static_cast(lastBlockHeight), res.last_block_difficulty); + + res.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onGetVersion(const COMMAND_RPC_GET_VERSION::request &req, + COMMAND_RPC_GET_VERSION::response &res) + { + res.version = PROJECT_VERSION_LONG; + + return true; + } + + bool RpcServer::onGetHardwareInfo(const COMMAND_RPC_GET_HARDWARE_INFO::request &req, + COMMAND_RPC_GET_HARDWARE_INFO::response &res) + { + const std::time_t uptime = std::time(nullptr) - m_core.getStartTime(); + const std::string uptime_str = std::to_string((unsigned int) floor(uptime / 60.0 / 60.0 / 24.0)) + + "d " + + std::to_string((unsigned int) floor(fmod((uptime / 60.0 / 60.0), 24.0))) + "h " + + std::to_string((unsigned int) floor(fmod((uptime / 60.0), 60.0))) + "m " + + std::to_string((unsigned int) fmod(uptime, 60.0)) + "s"; + + // CPU + res.cpuInfo.coreCount = Tools::CPU::quantities().physical; + res.cpuInfo.threadCount = Tools::CPU::quantities().logical; + res.cpuInfo.architecture = Tools::CPU::architecture(); + + // RAM + res.ramInfo.ramTotal = Tools::Memory::MemInfo::sysMem(); + res.ramInfo.ramAvailable = Tools::Memory::MemInfo::freeSysMem(); + res.ramInfo.ramUsageVirt = Tools::Memory::MemInfo::usedVirtMem(); + res.ramInfo.ramUsagePhys = Tools::Memory::MemInfo::usedPhysMem(); + res.ramInfo.ramUsageVirtMax = Tools::Memory::MemInfo::usedVirtMemMax(); + res.ramInfo.ramUsagePhysMax = Tools::Memory::MemInfo::usedPhysMemMax(); + + // Space + boost::filesystem::path configFolderPath(m_core.getConfigFolder()); + res.spaceInfo.freeSpace = std::to_string(Tools::Storage::SpaceInfo::freeSpace(configFolderPath) / (1 << 20)) + " MB"; + res.spaceInfo.availableSpace = std::to_string(Tools::Storage::SpaceInfo::availableSpace(configFolderPath) / (1 << 20)) + " MB"; + res.spaceInfo.capacitySpace = std::to_string(Tools::Storage::SpaceInfo::capacitySpace(configFolderPath) / (1 << 20)) + " MB"; + + // other + res.uptime = uptime_str; + res.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onGetHeight(const COMMAND_RPC_GET_HEIGHT::request &req, + COMMAND_RPC_GET_HEIGHT::response &res) + { + res.height = m_core.get_current_blockchain_height(); + res.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onGetTransactions(const COMMAND_RPC_GET_TRANSACTIONS::request &req, + COMMAND_RPC_GET_TRANSACTIONS::response &res) + { + std::vector vh; + for (const auto &tx_hex_str : req.txs_hashes) { + BinaryArray b; + if (!fromHex(tx_hex_str, b)) { + res.status = "Failed to parse hex representation of transaction hash"; + return true; + } + if (b.size() != sizeof(Hash)) { + res.status = "Failed, size of data mismatch"; + } + vh.push_back(*reinterpret_cast(b.data())); + } + std::list missed_txs; + std::list txs; + m_core.getTransactions(vh, txs, missed_txs); + + for (auto &tx : txs) { + res.txs_as_hex.push_back(toHex(toBinaryArray(tx))); + } + + for (const auto &miss_tx : missed_txs) { + res.missed_tx.push_back(Common::podToHex(miss_tx)); + } + + res.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onGetTransactionsByHeights( + const COMMAND_RPC_GET_TRANSACTIONS_BY_HEIGHTS::request &req, + COMMAND_RPC_GET_TRANSACTIONS_BY_HEIGHTS::response &res) + { + try { + std::vector vh; + + if (req.range) { + if (req.heights.size() != 2) { + res.status = "Range set true but heights size != 2"; + return true; + } + + uint32_t upperBorder = std::min(req.heights[1], m_core.get_current_blockchain_height()); + + for (size_t i = 0; i < (upperBorder - req.heights[0]); i++) { + Block blk; + Crypto::Hash blockHash = m_core.getBlockIdByHeight(req.heights[0] + i); + + if (!m_core.getBlockByHash(blockHash, blk)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: can't get block by hash. Hash = " + + podToHex(blockHash) + '.'}; + } + + if (blk.baseTransaction.inputs.front().type() != typeid(BaseInput)) { + throw JsonRpc::JsonRpcError{ + CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: coinbase transaction in the block has the wrong type" + }; + } + + for (Crypto::Hash &bTxs : blk.transactionHashes) { + vh.push_back(bTxs); + } + + if (req.include_miner_txs) { + vh.push_back(getObjectHash(blk.baseTransaction)); + } + } + } + else { + for (size_t i = 0; i < req.heights.size(); i++) { + Block blk; + Crypto::Hash blockHash = m_core.getBlockIdByHeight(req.heights[i]); + + for (Crypto::Hash &bTxs : blk.transactionHashes) { + vh.push_back(bTxs); + } + + if (req.include_miner_txs) { + vh.push_back(getObjectHash(blk.baseTransaction)); + } + } + } + + std::list missedTxs; + std::list txs; + + m_core.getTransactions(vh, txs, missedTxs, true); + + std::list txHashes; + for (auto &tx : txs) { + txHashes.push_back(Common::podToHex(getObjectHash(tx))); + } + + logger(DEBUGGING) << "Found " << txs.size() << "/" << vh.size() + << " transactions on the blockchain."; + + std::list::const_iterator txHi = txHashes.begin(); + std::vector::const_iterator vHi = vh.begin(); + + for (const Transaction &tx : txs) { + res.txs.push_back(COMMAND_RPC_GET_TRANSACTIONS_BY_HEIGHTS::entry()); + COMMAND_RPC_GET_TRANSACTIONS_BY_HEIGHTS::entry &e = res.txs.back(); + + Crypto::Hash blockHash; + uint32_t blockHeight; + uint64_t fee; + get_tx_fee(tx, fee); + + Crypto::Hash txHash = *vHi++; + e.tx_hash = *txHi++; + + bool r = m_core.getBlockContainingTx(txHash, blockHash, blockHeight); + bool oR = m_core.get_tx_outputs_gindexs(txHash, e.output_indices); + + if (req.as_json) { + e.as_json = tx; + } + + e.block_height = blockHeight; + e.block_timestamp = m_core.getBlockTimestamp(blockHeight); + e.fee = fee; + } + + if (txs.empty() || !missedTxs.empty()) { + std::ostringstream oss; + std::string seperator; + for (auto h : missedTxs) { + oss << seperator << Common::podToHex(h); + seperator = ","; + } + res.status = "transaction(s) not found: " + oss.str() + "."; + } + + res.status = CORE_RPC_STATUS_OK; + return true; + + } catch (std::system_error &e) { + res.status = e.what(); + return false; + } catch (std::exception &e) { + res.status = "Error: " + std::string(e.what()); + return false; + } + + return true; + } + + bool RpcServer::onGetRawTransactionsByHeights(const COMMAND_RPC_GET_RAW_TRANSACTIONS_BY_HEIGHTS::request &req, + COMMAND_RPC_GET_RAW_TRANSACTIONS_BY_HEIGHTS::response &res) + { + try { + if (req.heights.size() > BLOCK_LIST_MAX_COUNT) { + throw JsonRpc::JsonRpcError{ + CORE_RPC_ERROR_CODE_WRONG_PARAM, + std::string("Requested blocks count: ") + + std::to_string(req.heights.size()) + + " exceeded max limit of " + + std::to_string(BLOCK_LIST_MAX_COUNT) + }; + } + + std::vector heights; + + if (req.range) { + if (req.heights.size() != 2) { + throw JsonRpc::JsonRpcError{ + CORE_RPC_ERROR_CODE_WRONG_PARAM, + std::string("The range is set to true but heights size is not equal to 2") + }; + } + + uint32_t upperBound = std::min(req.heights[1], m_core.get_current_blockchain_height()); + for (size_t i = 0; i < (upperBound - req.heights[0]); i++) { + heights.push_back(req.heights[0] + i); + } + } + else { + heights = req.heights; + } + + for (const uint32_t &height : heights) { + if (m_core.get_current_blockchain_height() <= height) { + throw JsonRpc::JsonRpcError{ + CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT, + std::string("To big height: ") + + std::to_string(height) + + ", current blockchain height = " + + std::to_string(m_core.get_current_blockchain_height() - 1) + }; + } + + Crypto::Hash blockHash = m_core.getBlockIdByHeight(height); + Block blk; + std::vector txsIds; + if (!m_core.getBlockByHash(blockHash, blk)) { + throw JsonRpc::JsonRpcError{ + CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: can't get block by height " + + std::to_string(height) + + '.' + }; + } + + if (req.includeMinerTxs) { + txsIds.reserve(blk.transactionHashes.size() + 1); + txsIds.push_back(getObjectHash(blk.baseTransaction)); + } + else { + txsIds.reserve(blk.transactionHashes.size()); + } + + if (!blk.transactionHashes.empty()) { + txsIds.insert(txsIds.end(), blk.transactionHashes.begin(), blk.transactionHashes.end()); + } + + std::vector::const_iterator ti = txsIds.begin(); + + std::vector>> txs; + std::list missed; + + if (!txsIds.empty()) { + if (!m_core.getTransactionsWithOutputGlobalIndexes(txsIds, missed, txs)) { + throw JsonRpc::JsonRpcError{ + CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Error getting transactions with output global indexes" + }; + } + + for (const auto &txi : txs) { + res.transactions.push_back(TxWithOutputGlobalIndices()); + TxWithOutputGlobalIndices &e = res.transactions.back(); + + e.hash = *ti++; + e.block_hash = blockHash; + e.height = height; + e.timestamp = blk.timestamp; + e.transaction = *static_cast(&txi.first); + e.output_indexes = txi.second; + e.fee = is_coinbase(txi.first) ? 0 : getInputAmount(txi.first) - getOutputAmount(txi.first); + } + } + + for (const auto &missTx : missed) { + res.missedTxs.push_back(Common::podToHex(missTx)); + } + } + } catch (std::system_error &e) { + throw JsonRpc::JsonRpcError{ + CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + e.what() + }; + + return false; + } catch (std::exception &e) { + throw JsonRpc::JsonRpcError{ + CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Error: " + std::string(e.what()) + }; + + return false; + } + + res.status = CORE_RPC_STATUS_OK; + return true; + } + + bool RpcServer::onGetRawTransactionPool(const COMMAND_RPC_GET_RAW_TRANSACTIONS_FROM_POOL::request &req, + COMMAND_RPC_GET_RAW_TRANSACTIONS_FROM_POOL::response &res) + { + auto pool = m_core.getMemoryPool(); + + for (const auto &txd : pool) { + res.transactions.push_back(TxWithOutputGlobalIndices()); + TxWithOutputGlobalIndices &e = res.transactions.back(); + + e.hash = txd.id; + e.height = boost::value_initialized(); + e.block_hash = boost::value_initialized(); + e.timestamp = txd.receiveTime; + e.transaction = *static_cast(&txd.tx); + e.fee = txd.fee; + } + + res.status = CORE_RPC_STATUS_OK; + return true; + } + + bool RpcServer::onSendRawTx(const COMMAND_RPC_SEND_RAW_TX::request &req, + COMMAND_RPC_SEND_RAW_TX::response &res) + { + BinaryArray tx_blob; + if (!fromHex(req.tx_as_hex, tx_blob)) { + logger(INFO) << "[on_send_raw_tx]: Failed to parse tx from hexbuff: " << req.tx_as_hex; + res.status = "Failed"; + return true; + } + + Crypto::Hash transactionHash = Crypto::cn_fast_hash(tx_blob.data(), tx_blob.size()); + logger(DEBUGGING) << "transaction " << transactionHash << " came in on_send_raw_tx"; + + tx_verification_context tvc = boost::value_initialized(); + if (!m_core.handle_incoming_tx(tx_blob, tvc, false, false)) { + logger(INFO) << "[on_send_raw_tx]: Failed to process tx"; + res.status = "Failed"; + return true; + } + + if (tvc.m_verification_failed) { + logger(INFO) << "[on_send_raw_tx]: tx verification failed"; + res.status = "Failed"; + return true; + } + + if (!tvc.m_should_be_relayed) { + logger(INFO) << "[on_send_raw_tx]: tx accepted, but not relayed"; + res.status = "Not relayed"; + return true; + } + + if (!m_fee_address.empty() && m_view_key != NULL_SECRET_KEY) { + if (!masternodeCheckIncomingTx(tx_blob)) { + logger(INFO) << "Transaction not relayed due to lack of masternode fee"; + res.status = "Not relayed due to lack of node fee"; + return true; + } + } + + NOTIFY_NEW_TRANSACTIONS::request r; + r.txs.push_back(asString(tx_blob)); + m_core.get_protocol()->relay_transactions(r); + + // TODO: + // make sure that tx has reached other nodes here, + // probably wait to receive reflections from other nodes + res.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onStartMining(const COMMAND_RPC_START_MINING::request &req, + COMMAND_RPC_START_MINING::response &res) + { + if (m_restricted_rpc) { + res.status = "Failed, restricted handle"; + return false; + } + + AccountPublicAddress adr; + if (!m_core.currency().parseAccountAddressString(req.miner_address, adr)) { + res.status = "Failed, wrong address"; + return true; + } + + if (!m_core.get_miner().start(adr, static_cast(req.threads_count))) { + res.status = "Failed, mining not started"; + return true; + } + + res.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onStopMining(const COMMAND_RPC_STOP_MINING::request &req, + COMMAND_RPC_STOP_MINING::response &res) + { + if (m_restricted_rpc) { + res.status = "Failed, restricted handle"; + return false; + } + + if (!m_core.get_miner().stop()) { + res.status = "Failed, mining not stopped"; + return true; + } + + res.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onStopDaemon(const COMMAND_RPC_STOP_DAEMON::request &req, + COMMAND_RPC_STOP_DAEMON::response &res) + { + if (m_restricted_rpc) { + res.status = "Failed, restricted handle"; + return false; + } + if (m_core.currency().isTestnet()) { + m_p2p.sendStopSignal(); + res.status = CORE_RPC_STATUS_OK; + } + else { + res.status = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; + return false; + } + return true; + } + + bool RpcServer::onGetFeeAddress(const COMMAND_RPC_GET_FEE_ADDRESS::request &req, + COMMAND_RPC_GET_FEE_ADDRESS::response &res) + { + if (m_fee_address.empty()) { + res.status = CORE_RPC_STATUS_OK; + return false; + } + + res.fee_address = m_fee_address; + res.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onGetPeerList(const COMMAND_RPC_GET_PEER_LIST::request &req, + COMMAND_RPC_GET_PEER_LIST::response &res) + { + std::list pl_wite; + std::list pl_gray; + m_p2p.getPeerlistManager().get_peerlist_full(pl_gray, pl_wite); + for (const auto &pe : pl_wite) { + std::stringstream ss; + ss << pe.adr; + res.peers.push_back(ss.str()); + } + + res.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onBlocksListJson(const COMMAND_RPC_GET_BLOCKS_LIST::request &req, + COMMAND_RPC_GET_BLOCKS_LIST::response &res) + { + if (m_core.get_current_blockchain_height() <= req.height) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT, + std::string("To big height: ") + std::to_string(req.height) + + ", current blockchain height = " + + std::to_string( + m_core.get_current_blockchain_height())}; + } + + uint32_t print_blocks_count = 20; + uint32_t last_height = req.height - print_blocks_count; + if (req.height <= print_blocks_count) { + last_height = 0; + } + + for (uint32_t i = req.height; i >= last_height; i--) { + Hash block_hash = m_core.getBlockIdByHeight(static_cast(i)); + Block blk; + if (!m_core.getBlockByHash(block_hash, blk)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: can't get block by height. Height = " + + std::to_string(i) + '.'}; + } + + size_t tx_cumulative_block_size; + m_core.getBlockSize(block_hash, tx_cumulative_block_size); + size_t blockBlobSize = getObjectBinarySize(blk); + size_t minerTxBlobSize = getObjectBinarySize(blk.baseTransaction); + difficulty_type blockDiff; + m_core.getBlockDifficulty(static_cast(i), blockDiff); + + BLOCK_SHORT_RESPONSE block_short; + BLOCK_HEADER_RESPONSE_ENTRY blockHeaderResponse; + + Crypto::Hash tmp_hash = m_core.getBlockIdByHeight(i); + bool is_orphaned = block_hash != tmp_hash; + + fillBlockHeaderResponse(blk, is_orphaned, i, block_hash, blockHeaderResponse); + + block_short.timestamp = blk.timestamp; + block_short.height = i; + block_short.hash = Common::podToHex(block_hash); + block_short.cumul_size = blockBlobSize + tx_cumulative_block_size - minerTxBlobSize; + block_short.tx_count = blk.transactionHashes.size() + 1; + block_short.reward = blockHeaderResponse.reward; + block_short.difficulty = blockDiff; + block_short.min_tx_fee = m_core.getMinimalFeeForHeight(i); + + res.blocks.push_back(block_short); + + if (i == 0) { + break; + } + } + + res.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onBlockJson(const COMMAND_RPC_GET_BLOCK_DETAILS::request &req, + COMMAND_RPC_GET_BLOCK_DETAILS::response &res) + { + Hash hash; + + try { + auto height = boost::lexical_cast(req.hash); + hash = m_core.getBlockIdByHeight(height); + } catch (boost::bad_lexical_cast &) { + if (!parse_hash256(req.hash, hash)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_WRONG_PARAM, + "Failed to parse hex representation of block hash. Hex = " + + req.hash + '.'}; + } + } + + Block blk; + if (!m_core.getBlockByHash(hash, blk)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: can't get block by hash. Hash = " + req.hash + + '.'}; + } + + if (blk.baseTransaction.inputs.front().type() != typeid(BaseInput)) { + throw JsonRpc::JsonRpcError{ + CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: coinbase transaction in the block has the wrong type" + }; + } + + BLOCK_HEADER_RESPONSE_ENTRY block_header; + res.block.height = boost::get(blk.baseTransaction.inputs.front()).blockIndex; + + Crypto::Hash tmp_hash = m_core.getBlockIdByHeight(res.block.height); + bool is_orphaned = hash != tmp_hash; + fillBlockHeaderResponse(blk, is_orphaned, res.block.height, hash, block_header); + + res.block.major_version = block_header.major_version; + res.block.minor_version = block_header.minor_version; + res.block.timestamp = block_header.timestamp; + res.block.prev_hash = block_header.prev_hash; + res.block.nonce = block_header.nonce; + res.block.hash = block_header.hash; + res.block.depth = block_header.depth; + res.block.orphan_status = block_header.orphan_status; + m_core.getBlockDifficulty(static_cast(res.block.height), res.block.difficulty); + m_core.getBlockCumulativeDifficulty(static_cast(res.block.height), + res.block.cumulativeDifficulty); + + res.block.reward = block_header.reward; + + std::vector blocksSizes; + if (!m_core.getBackwardBlocksSizes(res.block.height, blocksSizes, + parameters::CRYPTONOTE_REWARD_BLOCKS_WINDOW)) { + return false; + } + res.block.sizeMedian = Common::medianValue(blocksSizes); + + size_t blockSize = 0; + if (!m_core.getBlockSize(hash, blockSize)) { + return false; + } + res.block.transactionsCumulativeSize = blockSize; + + size_t blokBlobSize = getObjectBinarySize(blk); + size_t minerTxBlobSize = getObjectBinarySize(blk.baseTransaction); + res.block.blockSize = blokBlobSize + res.block.transactionsCumulativeSize - minerTxBlobSize; + + uint64_t alreadyGeneratedCoins; + if (!m_core.getAlreadyGeneratedCoins(hash, alreadyGeneratedCoins)) { + return false; + } + res.block.alreadyGeneratedCoins = std::to_string(alreadyGeneratedCoins); + + if (!m_core.getGeneratedTransactionsNumber(res.block.height, + res.block.alreadyGeneratedTransactions)) { + return false; + } + + uint64_t prevBlockGeneratedCoins = 0; + uint32_t previousBlockHeight = 0; + uint64_t blockTarget = CryptoNote::parameters::DIFFICULTY_TARGET; + + if (res.block.height > 0) { + if (!m_core.getAlreadyGeneratedCoins(blk.previousBlockHash, prevBlockGeneratedCoins)) { + return false; + } + } + + if (res.block.height >= CryptoNote::parameters::UPGRADE_HEIGHT_V6) { + m_core.getBlockHeight(blk.previousBlockHash, previousBlockHeight); + blockTarget = blk.timestamp - m_core.getBlockTimestamp(previousBlockHeight); + } + + uint64_t maxReward = 0; + uint64_t currentReward = 0; + int64_t emissionChange = 0; + size_t blockGrantedFullRewardZone = + CryptoNote::parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2; + res.block.effectiveSizeMedian = std::max(res.block.sizeMedian, blockGrantedFullRewardZone); + + if (!m_core.getBlockReward(res.block.major_version, res.block.sizeMedian, 0, + prevBlockGeneratedCoins, 0, maxReward, emissionChange, + res.block.height, blockTarget)) { + return false; + } + if (!m_core.getBlockReward(res.block.major_version, res.block.sizeMedian, + res.block.transactionsCumulativeSize, prevBlockGeneratedCoins, 0, + currentReward, emissionChange, res.block.height, blockTarget)) { + return false; + } + + res.block.baseReward = maxReward; + if (maxReward == 0 && currentReward == 0) { + res.block.penalty = static_cast(0); + } + else { + if (maxReward < currentReward) { + return false; + } + res.block.penalty = + static_cast(maxReward - currentReward) / static_cast(maxReward); + } + + // Base transaction adding + TRANSACTION_SHORT_RESPONSE transaction_short; + transaction_short.hash = Common::podToHex(getObjectHash(blk.baseTransaction)); + transaction_short.fee = 0; + transaction_short.amount_out = get_outs_money_amount(blk.baseTransaction); + transaction_short.size = getObjectBinarySize(blk.baseTransaction); + res.block.transactions.push_back(transaction_short); + + std::list missed_txs; + std::list txs; + m_core.getTransactions(blk.transactionHashes, txs, missed_txs); + + res.block.totalFeeAmount = 0; + + for (const Transaction &tx : txs) { + TRANSACTION_SHORT_RESPONSE tr_short; + uint64_t amount_in = 0; + get_inputs_money_amount(tx, amount_in); + uint64_t amount_out = get_outs_money_amount(tx); + + tr_short.hash = Common::podToHex(getObjectHash(tx)); + tr_short.fee = amount_in - amount_out; + tr_short.amount_out = amount_out; + tr_short.size = getObjectBinarySize(tx); + res.block.transactions.push_back(tr_short); + + res.block.totalFeeAmount += tr_short.fee; + } + + res.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onTransactionJson(const COMMAND_RPC_GET_TRANSACTION_DETAILS::request &req, + COMMAND_RPC_GET_TRANSACTION_DETAILS::response &res) + { + Hash hash; + + if (!parse_hash256(req.hash, hash)) { + throw JsonRpc::JsonRpcError{ + CORE_RPC_ERROR_CODE_WRONG_PARAM, + "Failed to parse hex representation of transaction hash. Hex = " + req.hash + '.' + }; + } + + std::vector tx_ids; + tx_ids.push_back(hash); + + std::list missed_txs; + std::list txs; + m_core.getTransactions(tx_ids, txs, missed_txs, true); + + if (1 == txs.size()) { + res.tx = txs.front(); + } + else { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_WRONG_PARAM, + "transaction wasn't found. Hash = " + req.hash + '.'}; + } + + Crypto::Hash blockHash; + uint32_t blockHeight; + if (m_core.getBlockContainingTx(hash, blockHash, blockHeight)) { + Block blk; + if (m_core.getBlockByHash(blockHash, blk)) { + size_t tx_cumulative_block_size; + m_core.getBlockSize(blockHash, tx_cumulative_block_size); + size_t blokBlobSize = getObjectBinarySize(blk); + size_t minerTxBlobSize = getObjectBinarySize(blk.baseTransaction); + BLOCK_SHORT_RESPONSE block_short; + + block_short.timestamp = blk.timestamp; + block_short.height = blockHeight; + block_short.hash = Common::podToHex(blockHash); + block_short.cumul_size = blokBlobSize + tx_cumulative_block_size - minerTxBlobSize; + block_short.tx_count = blk.transactionHashes.size() + 1; + res.block = block_short; + res.txDetails.confirmations = m_protocolQuery.getObservedHeight() - blockHeight; + } + } + + uint64_t amount_in = 0; + get_inputs_money_amount(res.tx, amount_in); + uint64_t amount_out = get_outs_money_amount(res.tx); + + res.txDetails.hash = Common::podToHex(getObjectHash(res.tx)); + res.txDetails.fee = amount_in - amount_out; + if (amount_in == 0) { + res.txDetails.fee = 0; + } + res.txDetails.amount_out = amount_out; + res.txDetails.size = getObjectBinarySize(res.tx); + + uint64_t mixin; + if (!onGetMixin(res.tx, mixin)) { + return false; + } + res.txDetails.mixin = mixin; + + Crypto::Hash paymentId; + if (CryptoNote::getPaymentIdFromTxExtra(res.tx.extra, paymentId)) { + res.txDetails.paymentId = Common::podToHex(paymentId); + } + else { + res.txDetails.paymentId = ""; + } + + res.txDetails.extra.raw = res.tx.extra; + + std::vector txExtraFields; + parseTransactionExtra(res.tx.extra, txExtraFields); + for (const CryptoNote::TransactionExtraField &field : txExtraFields) { + if (typeid(CryptoNote::TransactionExtraPadding) == field.type()) { + res.txDetails.extra.padding.push_back( + std::move(boost::get(field).size)); + } + else if (typeid(CryptoNote::TransactionExtraPublicKey) == field.type()) { + res.txDetails.extra.publicKey = getTransactionPublicKeyFromExtra(res.tx.extra); + } + else if (typeid(CryptoNote::TransactionExtraNonce) == field.type()) { + res.txDetails.extra.nonce.push_back(Common::toHex( + boost::get(field).nonce.data(), + boost::get(field).nonce.size())); + } + } + + res.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onPoolJson(const COMMAND_RPC_GET_POOL::request &req, + COMMAND_RPC_GET_POOL::response &res) + { + auto pool = m_core.getPoolTransactions(); + for (const Transaction tx : pool) { + TRANSACTION_SHORT_RESPONSE transaction_short; + + uint64_t amount_in = getInputAmount(tx); + uint64_t amount_out = getOutputAmount(tx); + bool amount_in_less = (amount_in < amount_out + parameters::MINIMUM_FEE); + + transaction_short.hash = Common::podToHex(getObjectHash(tx)); + transaction_short.fee = amount_in_less ? parameters::MINIMUM_FEE : amount_in - amount_out; + transaction_short.amount_out = amount_out; + transaction_short.size = getObjectBinarySize(tx); + res.transactions.push_back(transaction_short); + } + + res.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onMempoolJson(const COMMAND_RPC_GET_MEMPOOL::request &req, + COMMAND_RPC_GET_MEMPOOL::response &res) + { + auto pool = m_core.getMemoryPool(); + for (const CryptoNote::tx_memory_pool::TransactionDetails txd : pool) { + MEMPOOL_TRANSACTION_RESPONSE mempool_transaction; + uint64_t amount_out = getOutputAmount(txd.tx); + + mempool_transaction.hash = Common::podToHex(txd.id); + mempool_transaction.fee = txd.fee; + mempool_transaction.amount_out = amount_out; + mempool_transaction.size = txd.blobSize; + mempool_transaction.receiveTime = txd.receiveTime; + mempool_transaction.keptByBlock = txd.keptByBlock; + mempool_transaction.max_used_block_height = txd.maxUsedBlock.height; + mempool_transaction.max_used_block_id = Common::podToHex(txd.maxUsedBlock.id); + mempool_transaction.last_failed_height = txd.lastFailedBlock.height; + mempool_transaction.last_failed_id = Common::podToHex(txd.lastFailedBlock.id); + mempool_transaction.tx_json = txd.tx; + res.transactions.push_back(mempool_transaction); + } + + res.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onGetMixin(const Transaction &transaction, uint64_t &mixin) + { + mixin = 0; + + for (const TransactionInput &txin : transaction.inputs) { + if (txin.type() != typeid(KeyInput)) { + continue; + } + uint64_t currentMixin = boost::get(txin).outputIndexes.size(); + if (currentMixin > mixin) { + mixin = currentMixin; + } + } + + return true; + } + + bool RpcServer::onTransactionsByPaymentId( + const COMMAND_RPC_GET_TRANSACTIONS_BY_PAYMENT_ID::request &req, + COMMAND_RPC_GET_TRANSACTIONS_BY_PAYMENT_ID::response &res) + { + if (req.payment_id.empty()) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_WRONG_PARAM, + "Wrong parameters, expected payment_id"}; + } + logger(Logging::DEBUGGING, Logging::WHITE) + << "RPC request came: Search by Payment ID: " << req.payment_id; + + Crypto::Hash paymentId; + std::vector transactions; + + if (!parse_hash256(req.payment_id, paymentId)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_WRONG_PARAM, + "Failed to parse Payment ID: " + req.payment_id + '.'}; + } + + if (!m_core.getTransactionsByPaymentId(paymentId, transactions)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: can't get transactions by Payment ID: " + + req.payment_id + '.'}; + } + + for (const Transaction &tx : transactions) { + TRANSACTION_SHORT_RESPONSE transaction_short; + uint64_t amount_in = 0; + get_inputs_money_amount(tx, amount_in); + uint64_t amount_out = get_outs_money_amount(tx); + + transaction_short.hash = Common::podToHex(getObjectHash(tx)); + transaction_short.fee = amount_in - amount_out; + transaction_short.amount_out = amount_out; + transaction_short.size = getObjectBinarySize(tx); + res.transactions.push_back(transaction_short); + } + + res.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onTransactionsPoolJson(const COMMAND_RPC_GET_POOL::request &req, + COMMAND_RPC_GET_POOL::response &res) + { + auto pool = m_core.getPoolTransactions(); + for (const Transaction &tx : pool) { + TRANSACTION_SHORT_RESPONSE transaction_short; + uint64_t amount_in = getInputAmount(tx); + uint64_t amount_out = getOutputAmount(tx); + + transaction_short.hash = Common::podToHex(getObjectHash(tx)); + transaction_short.fee = amount_in - amount_out; + transaction_short.amount_out = amount_out; + transaction_short.size = getObjectBinarySize(tx); + res.transactions.push_back(transaction_short); + } + + res.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onGetBlockCount(const COMMAND_RPC_GET_BLOCK_COUNT::request &req, + COMMAND_RPC_GET_BLOCK_COUNT::response &res) + { + res.count = m_core.get_current_blockchain_height(); + res.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onGetBlockHash(const COMMAND_RPC_GET_BLOCK_HASH::request &req, + COMMAND_RPC_GET_BLOCK_HASH::response &res) + { + if (req.size() != 1) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_WRONG_PARAM, + "Wrong parameters, expected height"}; + } + + uint32_t h = static_cast(req[0]); + Crypto::Hash blockId = m_core.getBlockIdByHeight(h); + if (blockId == NULL_HASH) { + throw JsonRpc::JsonRpcError{ + CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT, + std::string("To big height: ") + std::to_string(h) + ", current blockchain height = " + + std::to_string(m_core.get_current_blockchain_height()) + }; + } + + res = Common::podToHex(blockId); + + return true; + } + + namespace { + + uint64_t slowMemMem(void *start_buff, size_t buflen, void *pat, size_t patlen) + { + void *buf = start_buff; + void *end = (char *) buf + buflen - patlen; + while ((buf = memchr(buf, ((char *) pat)[0], buflen))) { + if (buf > end) { + return 0; + } + if (memcmp(buf, pat, patlen) == 0) { + return (char *) buf - (char *) start_buff; + } + buf = (char *) buf + 1; + } + return 0; + } + + } // namespace + + bool RpcServer::onGetBlockTemplate(const COMMAND_RPC_GET_BLOCK_TEMPLATE::request &req, + COMMAND_RPC_GET_BLOCK_TEMPLATE::response &res) + { + if (req.reserve_size > TX_EXTRA_NONCE_MAX_COUNT) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_TOO_BIG_RESERVE_SIZE, + "To big reserved size, maximum 255"}; + } + + AccountPublicAddress acc = boost::value_initialized(); + + if (!req.wallet_address.size() + || !m_core.currency().parseAccountAddressString(req.wallet_address, acc)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_WRONG_WALLET_ADDRESS, + "Failed to parse wallet address"}; + } + + Block b = boost::value_initialized(); + CryptoNote::BinaryArray blob_reserve; + blob_reserve.resize(req.reserve_size, 0); + if (!m_core.get_block_template(b, acc, res.difficulty, res.height, blob_reserve)) { + logger(ERROR) << "Failed to create block template"; + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: failed to create block template"}; + } + + BinaryArray block_blob = toBinaryArray(b); + PublicKey tx_pub_key = CryptoNote::getTransactionPublicKeyFromExtra(b.baseTransaction.extra); + if (tx_pub_key == NULL_PUBLIC_KEY) { + logger(ERROR) << "Failed to find tx pub key in coinbase extra"; + throw JsonRpc::JsonRpcError{ + CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: failed to find tx pub key in coinbase extra" + }; + } + + if (0 < req.reserve_size) { + res.reserved_offset = slowMemMem((void *) block_blob.data(), block_blob.size(), &tx_pub_key, + sizeof(tx_pub_key)); + if (!res.reserved_offset) { + logger(ERROR) << "Failed to find tx pub key in blockblob"; + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: failed to create block template"}; + } + // 3 bytes: + // - tag for TX_EXTRA_TAG_PUBKEY(1 byte) + // - tag for TX_EXTRA_NONCE(1 byte) + // - counter in TX_EXTRA_NONCE(1 byte) + res.reserved_offset += sizeof(tx_pub_key) + 3; + if (res.reserved_offset + req.reserve_size > block_blob.size()) { + logger(ERROR) << "Failed to calculate offset for reserved bytes"; + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: failed to create block template"}; + } + } + else { + res.reserved_offset = 0; + } + + BinaryArray hashing_blob; + if (!get_block_hashing_blob(b, hashing_blob)) { + logger(ERROR) << "Failed to get blockhashing_blob"; + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: failed to get blockhashing_blob"}; + } + + res.blocktemplate_blob = toHex(block_blob); + res.blockhashing_blob = toHex(hashing_blob); + res.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onGetCurrencyId(const COMMAND_RPC_GET_CURRENCY_ID::request & /*req*/, + COMMAND_RPC_GET_CURRENCY_ID::response &res) + { + Hash currencyId = m_core.currency().genesisBlockHash(); + res.currency_id_blob = Common::podToHex(currencyId); + + return true; + } + + bool RpcServer::onSubmitBlock(const COMMAND_RPC_SUBMIT_BLOCK::request &req, + COMMAND_RPC_SUBMIT_BLOCK::response &res) + { + if (req.size() != 1) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_WRONG_PARAM, "Wrong param"}; + } + + BinaryArray blockblob; + if (!fromHex(req[0], blockblob)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB, "Wrong block blob"}; + } + + block_verification_context bvc = boost::value_initialized(); + + m_core.handle_incoming_block_blob(blockblob, bvc, true, true); + + if (!bvc.m_added_to_main_chain) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED, + "Block not accepted"}; + } + + res.status = CORE_RPC_STATUS_OK; + + return true; + } + + namespace { + + uint64_t getBlockReward(const Block &blk) + { + uint64_t reward = 0; + for (const TransactionOutput &out : blk.baseTransaction.outputs) { + reward += out.amount; + } + + return reward; + } + + } // namespace + + void RpcServer::fillBlockHeaderResponse(const Block &blk, bool orphan_status, uint64_t height, + const Hash &hash, BLOCK_HEADER_RESPONSE_ENTRY &responce) + { + responce.major_version = blk.majorVersion; + responce.minor_version = blk.minorVersion; + responce.timestamp = blk.timestamp; + responce.prev_hash = Common::podToHex(blk.previousBlockHash); + responce.nonce = blk.nonce; + responce.orphan_status = orphan_status; + responce.height = height; + responce.depth = m_core.get_current_blockchain_height() - height - 1; + responce.hash = Common::podToHex(hash); + m_core.getBlockDifficulty(static_cast(height), responce.difficulty); + responce.reward = getBlockReward(blk); + } + + bool RpcServer::onGetLastBlockHeader(const COMMAND_RPC_GET_LAST_BLOCK_HEADER::request &req, + COMMAND_RPC_GET_LAST_BLOCK_HEADER::response &res) + { + uint32_t last_block_height; + Hash last_block_hash; + + m_core.get_blockchain_top(last_block_height, last_block_hash); + + Block last_block; + if (!m_core.getBlockByHash(last_block_hash, last_block)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: can't get last block hash."}; + } + + Crypto::Hash tmp_hash = m_core.getBlockIdByHeight(last_block_height); + bool is_orphaned = last_block_hash != tmp_hash; + fillBlockHeaderResponse(last_block, is_orphaned, last_block_height, last_block_hash, + res.block_header); + res.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onGetBlockHeaderByHash( + const COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::request &req, + COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::response &res) + { + Hash block_hash; + + if (!parse_hash256(req.hash, block_hash)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_WRONG_PARAM, + "Failed to parse hex representation of block hash. Hex = " + + req.hash + '.'}; + } + + Block blk; + if (!m_core.getBlockByHash(block_hash, blk)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: can't get block by hash. Hash = " + req.hash + + '.'}; + } + + if (blk.baseTransaction.inputs.front().type() != typeid(BaseInput)) { + throw JsonRpc::JsonRpcError{ + CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: coinbase transaction in the block has the wrong type" + }; + } + + uint64_t block_height = boost::get(blk.baseTransaction.inputs.front()).blockIndex; + Crypto::Hash tmp_hash = m_core.getBlockIdByHeight(block_height); + bool is_orphaned = block_hash != tmp_hash; + fillBlockHeaderResponse(blk, is_orphaned, block_height, block_hash, res.block_header); + res.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onGetBlockHeaderByHeight( + const COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::request &req, + COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response &res) + { + if (m_core.get_current_blockchain_height() <= req.height) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT, + std::string("To big height: ") + std::to_string(req.height) + + ", current blockchain height = " + + std::to_string( + m_core.get_current_blockchain_height())}; + } + + Hash block_hash = m_core.getBlockIdByHeight(static_cast(req.height)); + Block blk; + if (!m_core.getBlockByHash(block_hash, blk)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: can't get block by height. Height = " + + std::to_string(req.height) + '.'}; + } + + Crypto::Hash tmp_hash = m_core.getBlockIdByHeight(req.height); + bool is_orphaned = block_hash != tmp_hash; + fillBlockHeaderResponse(blk, is_orphaned, req.height, block_hash, res.block_header); + res.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onCheckTxKey(const COMMAND_RPC_CHECK_TX_KEY::request &req, + COMMAND_RPC_CHECK_TX_KEY::response &res) + { + // parse txid + Crypto::Hash txid; + if (!parse_hash256(req.txid, txid)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_WRONG_PARAM, "Failed to parse txid"}; + } + + // parse address + CryptoNote::AccountPublicAddress address; + if (!m_core.currency().parseAccountAddressString(req.address, address)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_WRONG_PARAM, + "Failed to parse address " + req.address + '.'}; + } + + // parse txkey + Crypto::Hash tx_key_hash; + size_t size; + if (!Common::fromHex(req.txkey, &tx_key_hash, sizeof(tx_key_hash), size) + || size != sizeof(tx_key_hash)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_WRONG_PARAM, "Failed to parse txkey"}; + } + Crypto::SecretKey tx_key = *(struct Crypto::SecretKey *) &tx_key_hash; + + // fetch tx + Transaction tx; + std::vector tx_ids; + tx_ids.push_back(txid); + std::list missed_txs; + std::list txs; + m_core.getTransactions(tx_ids, txs, missed_txs, true); + + if (1 == txs.size()) { + tx = txs.front(); + } + else { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_WRONG_PARAM, + "Couldn't find transaction with hash: " + req.txid + '.'}; + } + CryptoNote::TransactionPrefix transaction = *static_cast(&tx); + + // obtain key derivation + Crypto::KeyDerivation derivation; + if (!Crypto::generate_key_derivation(address.viewPublicKey, tx_key, derivation)) { + throw JsonRpc::JsonRpcError{ + CORE_RPC_ERROR_CODE_WRONG_PARAM, + "Failed to generate key derivation from supplied parameters" + }; + } + + // look for outputs + uint64_t received(0); + size_t keyIndex(0); + std::vector outputs; + try { + for (const TransactionOutput &o : transaction.outputs) { + if (o.target.type() == typeid(KeyOutput)) { + const KeyOutput out_key = boost::get(o.target); + Crypto::PublicKey pubkey; + derive_public_key(derivation, keyIndex, address.spendPublicKey, pubkey); + if (pubkey == out_key.key) { + received += o.amount; + outputs.push_back(o); + } + } + ++keyIndex; + } + } catch (...) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Unknown error"}; + } + + res.amount = received; + res.outputs = outputs; + res.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onCheckTxWithViewKey( + const COMMAND_RPC_CHECK_TX_WITH_PRIVATE_VIEW_KEY::request &req, + COMMAND_RPC_CHECK_TX_WITH_PRIVATE_VIEW_KEY::response &res) + { + // parse txid + Crypto::Hash txid; + if (!parse_hash256(req.txid, txid)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_WRONG_PARAM, "Failed to parse txid"}; + } + + // parse address + CryptoNote::AccountPublicAddress address; + if (!m_core.currency().parseAccountAddressString(req.address, address)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_WRONG_PARAM, + "Failed to parse address " + req.address + '.'}; + } + + // parse view key + Crypto::Hash view_key_hash; + size_t size; + if (!Common::fromHex(req.view_key, &view_key_hash, sizeof(view_key_hash), size) + || size != sizeof(view_key_hash)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_WRONG_PARAM, + "Failed to parse private view key"}; + } + Crypto::SecretKey viewKey = *(struct Crypto::SecretKey *) &view_key_hash; + + // fetch tx + Transaction tx; + std::vector tx_ids; + tx_ids.push_back(txid); + std::list missed_txs; + std::list txs; + m_core.getTransactions(tx_ids, txs, missed_txs, true); + + if (1 == txs.size()) { + tx = txs.front(); + } + else { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_WRONG_PARAM, + "Couldn't find transaction with hash: " + req.txid + '.'}; + } + CryptoNote::TransactionPrefix transaction = *static_cast(&tx); + + // get tx pub key + Crypto::PublicKey txPubKey = getTransactionPublicKeyFromExtra(transaction.extra); + + // obtain key derivation + Crypto::KeyDerivation derivation; + if (!Crypto::generate_key_derivation(txPubKey, viewKey, derivation)) { + throw JsonRpc::JsonRpcError{ + CORE_RPC_ERROR_CODE_WRONG_PARAM, + "Failed to generate key derivation from supplied parameters" + }; + } + + // look for outputs + uint64_t received(0); + size_t keyIndex(0); + std::vector outputs; + try { + for (const TransactionOutput &o : transaction.outputs) { + if (o.target.type() == typeid(KeyOutput)) { + const KeyOutput out_key = boost::get(o.target); + Crypto::PublicKey pubkey; + derive_public_key(derivation, keyIndex, address.spendPublicKey, pubkey); + if (pubkey == out_key.key) { + received += o.amount; + outputs.push_back(o); + } + } + ++keyIndex; + } + } catch (...) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Unknown error"}; + } + res.amount = received; + res.outputs = outputs; + + Crypto::Hash blockHash; + uint32_t blockHeight; + if (m_core.getBlockContainingTx(txid, blockHash, blockHeight)) { + res.confirmations = m_protocolQuery.getObservedHeight() - blockHeight; + } + + res.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onCheckTxProof(const COMMAND_RPC_CHECK_TX_PROOF::request &req, + COMMAND_RPC_CHECK_TX_PROOF::response &res) + { + // parse txid + Crypto::Hash txid; + if (!parse_hash256(req.tx_id, txid)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_WRONG_PARAM, "Failed to parse txid"}; + } + + // parse address + CryptoNote::AccountPublicAddress address; + if (!m_core.currency().parseAccountAddressString(req.dest_address, address)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_WRONG_PARAM, + "Failed to parse address " + req.dest_address + '.'}; + } + + // parse pubkey r*A & signature + const size_t header_len = strlen("ProofV1"); + if (req.signature.size() < header_len || req.signature.substr(0, header_len) != "ProofV1") { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_WRONG_PARAM, + "Signature header check error"}; + } + Crypto::PublicKey rA; + Crypto::Signature sig; + const size_t rA_len = + Tools::Base58::encode(std::string((const char *) &rA, sizeof(Crypto::PublicKey))).size(); + const size_t sig_len = + Tools::Base58::encode(std::string((const char *) &sig, sizeof(Crypto::Signature))) + .size(); + std::string rA_decoded; + std::string sig_decoded; + if (!Tools::Base58::decode(req.signature.substr(header_len, rA_len), rA_decoded)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_WRONG_PARAM, "Signature decoding error"}; + } + if (!Tools::Base58::decode(req.signature.substr(header_len + rA_len, sig_len), sig_decoded)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_WRONG_PARAM, "Signature decoding error"}; + } + if (sizeof(Crypto::PublicKey) != rA_decoded.size() + || sizeof(Crypto::Signature) != sig_decoded.size()) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_WRONG_PARAM, "Signature decoding error"}; + } + memcpy(&rA, rA_decoded.data(), sizeof(Crypto::PublicKey)); + memcpy(&sig, sig_decoded.data(), sizeof(Crypto::Signature)); + + // fetch tx pubkey + Transaction tx; + + std::vector out; + std::vector tx_ids; + tx_ids.push_back(txid); + std::list missed_txs; + std::list txs; + m_core.getTransactions(tx_ids, txs, missed_txs, true); + + if (1 == txs.size()) { + tx = txs.front(); + } + else { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_WRONG_PARAM, + "transaction wasn't found. Hash = " + req.tx_id + '.'}; + } + CryptoNote::TransactionPrefix transaction = *static_cast(&tx); + + Crypto::PublicKey R = getTransactionPublicKeyFromExtra(transaction.extra); + if (R == NULL_PUBLIC_KEY) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Tx pubkey was not found"}; + } + + // check signature + bool r = Crypto::check_tx_proof(txid, R, address.viewPublicKey, rA, sig); + res.signature_valid = r; + + if (r) { + // obtain key derivation by multiplying scalar 1 to the pubkey r*A included in the signature + Crypto::KeyDerivation derivation; + if (!Crypto::generate_key_derivation(rA, I, derivation)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Failed to generate key derivation"}; + } + + // look for outputs + uint64_t received(0); + size_t keyIndex(0); + std::vector outputs; + try { + for (const TransactionOutput &o : transaction.outputs) { + if (o.target.type() == typeid(KeyOutput)) { + const KeyOutput out_key = boost::get(o.target); + Crypto::PublicKey pubkey; + derive_public_key(derivation, keyIndex, address.spendPublicKey, pubkey); + if (pubkey == out_key.key) { + received += o.amount; + outputs.push_back(o); + } + } + ++keyIndex; + } + } catch (...) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Unknown error"}; + } + res.received_amount = received; + res.outputs = outputs; + + Crypto::Hash blockHash; + uint32_t blockHeight; + if (m_core.getBlockContainingTx(txid, blockHash, blockHeight)) { + res.confirmations = m_protocolQuery.getObservedHeight() - blockHeight; + } + } + else { + res.received_amount = 0; + } + + res.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onCheckReserveProof(const COMMAND_RPC_CHECK_RESERVE_PROOF::request &req, + COMMAND_RPC_CHECK_RESERVE_PROOF::response &res) + { + // parse address + CryptoNote::AccountPublicAddress address; + if (!m_core.currency().parseAccountAddressString(req.address, address)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_WRONG_PARAM, + "Failed to parse address " + req.address + '.'}; + } + + // parse sugnature + static constexpr char header[] = "ReserveProofV1"; + const size_t header_len = strlen(header); + if (req.signature.size() < header_len || req.signature.substr(0, header_len) != header) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Signature header check error"}; + } + + std::string sig_decoded; + if (!Tools::Base58::decode(req.signature.substr(header_len), sig_decoded)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Signature decoding error"}; + } + + BinaryArray ba; + if (!Common::fromHex(sig_decoded, ba)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Proof decoding error"}; + } + + RESERVE_PROOF proof_decoded; + if (!fromBinaryArray(proof_decoded, ba)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "BinaryArray decoding error"}; + } + + std::vector &proofs = proof_decoded.proofs; + + // compute signature prefix hash + std::string prefix_data = req.message; + prefix_data.append((const char *) &address, sizeof(CryptoNote::AccountPublicAddress)); + for (size_t i = 0; i < proofs.size(); ++i) { + prefix_data.append((const char *) &proofs[i].key_image, sizeof(Crypto::PublicKey)); + } + Crypto::Hash prefix_hash; + Crypto::cn_fast_hash(prefix_data.data(), prefix_data.size(), prefix_hash); + + // fetch txes + std::vector transactionHashes; + for (size_t i = 0; i < proofs.size(); ++i) { + transactionHashes.push_back(proofs[i].txid); + } + std::list missed_txs; + std::list txs; + m_core.getTransactions(transactionHashes, txs, missed_txs); + std::vector transactions; + std::copy(txs.begin(), txs.end(), std::inserter(transactions, transactions.end())); + + // check spent status + res.total = 0; + res.spent = 0; + for (size_t i = 0; i < proofs.size(); ++i) { + const RESERVE_PROOF_ENTRY &proof = proofs[i]; + + CryptoNote::TransactionPrefix tx = + *static_cast(&transactions[i]); + + if (proof.index_in_tx >= tx.outputs.size()) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "index_in_tx is out of bound"}; + } + + const KeyOutput out_key = boost::get(tx.outputs[proof.index_in_tx].target); + + // get tx pub key + Crypto::PublicKey txPubKey = getTransactionPublicKeyFromExtra(tx.extra); + + // check singature for shared secret + if (!Crypto::check_tx_proof(prefix_hash, address.viewPublicKey, txPubKey, + proof.shared_secret, proof.shared_secret_sig)) { + // throw JsonRpc::JsonRpcError{ + // CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + // "Failed to check singature for shared secret" + //}; + res.good = false; + return true; + } + + // check signature for key image + const std::vector &pubs = {&out_key.key}; + if (!Crypto::check_ring_signature(prefix_hash, proof.key_image, &pubs[0], 1, + &proof.key_image_sig)) { + // throw JsonRpc::JsonRpcError{ + // CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + // "Failed to check signature for key image" + //}; + res.good = false; + return true; + } + + // check if the address really received the fund + Crypto::KeyDerivation derivation; + if (!Crypto::generate_key_derivation(proof.shared_secret, I, derivation)) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Failed to generate key derivation"}; + } + + try { + Crypto::PublicKey pubkey; + derive_public_key(derivation, proof.index_in_tx, address.spendPublicKey, pubkey); + if (pubkey == out_key.key) { + uint64_t amount = tx.outputs[proof.index_in_tx].amount; + res.total += amount; + + if (m_core.is_key_image_spent(proof.key_image)) { + res.spent += amount; + } + } + } catch (...) { + throw JsonRpc::JsonRpcError{CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Unknown error"}; + } + } + + // check signature for address spend keys + Crypto::Signature sig = proof_decoded.signature; + if (!Crypto::check_signature(prefix_hash, address.spendPublicKey, sig)) { + res.good = false; + return true; + } + + return true; + } + + bool RpcServer::onValidateAddress(const COMMAND_RPC_VALIDATE_ADDRESS::request &req, + COMMAND_RPC_VALIDATE_ADDRESS::response &res) + { + AccountPublicAddress acc = boost::value_initialized(); + bool r = m_core.currency().parseAccountAddressString(req.address, acc); + res.isvalid = r; + if (r) { + res.address = m_core.currency().accountAddressAsString(acc); + res.spendPublicKey = Common::podToHex(acc.spendPublicKey); + res.viewPublicKey = Common::podToHex(acc.viewPublicKey); + } + res.status = CORE_RPC_STATUS_OK; + return true; + } + + bool RpcServer::onVerifyMessage(const COMMAND_RPC_VERIFY_MESSAGE::request &req, + COMMAND_RPC_VERIFY_MESSAGE::response &res) + { + Crypto::Hash hash; + Crypto::cn_fast_hash(req.message.data(), req.message.size(), hash); + + AccountPublicAddress acc = boost::value_initialized(); + if (!m_core.currency().parseAccountAddressString(req.address, acc)) { + throw JsonRpc::JsonRpcError(CORE_RPC_ERROR_CODE_WRONG_PARAM, + std::string("Failed to parse address")); + } + + const size_t header_len = strlen("SigV1"); + if (req.signature.size() < header_len || req.signature.substr(0, header_len) != "SigV1") { + throw JsonRpc::JsonRpcError(CORE_RPC_ERROR_CODE_WRONG_PARAM, + std::string("Signature header check error")); + } + + std::string decoded; + Crypto::Signature s; + if (!Tools::Base58::decode(req.signature.substr(header_len), decoded) + || sizeof(s) != decoded.size()) { + throw JsonRpc::JsonRpcError(CORE_RPC_ERROR_CODE_WRONG_PARAM, + std::string("Signature decoding error")); + return false; + } + + memcpy(&s, decoded.data(), sizeof(s)); + + res.sig_valid = Crypto::check_signature(hash, acc.spendPublicKey, s); + res.status = CORE_RPC_STATUS_OK; + + return true; + } + + bool RpcServer::onGetDifficultyStat(const COMMAND_RPC_GET_DIFFICULTY_STAT::request &req, + COMMAND_RPC_GET_DIFFICULTY_STAT::response &res) + { + try { + if (!m_core.get_difficulty_stat(req.height, IMinerHandler::stat_period::hour, + res.hour.block_num, res.hour.avg_solve_time, + res.hour.stddev_solve_time, res.hour.outliers_num, + res.hour.avg_diff, res.hour.min_diff, res.hour.max_diff)) + throw std::runtime_error("Failed to get hour difficulty statistics"); + if (!m_core.get_difficulty_stat(req.height, IMinerHandler::stat_period::day, + res.day.block_num, res.day.avg_solve_time, + res.day.stddev_solve_time, res.day.outliers_num, + res.day.avg_diff, res.day.min_diff, res.day.max_diff)) + throw std::runtime_error("Failed to get day difficulty statistics"); + if (!m_core.get_difficulty_stat(req.height, IMinerHandler::stat_period::week, + res.week.block_num, res.week.avg_solve_time, + res.week.stddev_solve_time, res.week.outliers_num, + res.week.avg_diff, res.week.min_diff, res.week.max_diff)) + throw std::runtime_error("Failed to get week difficulty statistics"); + if (!m_core.get_difficulty_stat(req.height, IMinerHandler::stat_period::month, + res.month.block_num, res.month.avg_solve_time, + res.month.stddev_solve_time, res.month.outliers_num, + res.month.avg_diff, res.month.min_diff, res.month.max_diff)) + throw std::runtime_error("Failed to get month difficulty statistics"); + if (!m_core.get_difficulty_stat(req.height, IMinerHandler::stat_period::halfyear, + res.halfyear.block_num, res.halfyear.avg_solve_time, + res.halfyear.stddev_solve_time, res.halfyear.outliers_num, + res.halfyear.avg_diff, res.halfyear.min_diff, + res.halfyear.max_diff)) + throw std::runtime_error("Failed to get halfyear difficulty statistics"); + if (!m_core.get_difficulty_stat(req.height, IMinerHandler::stat_period::year, + res.year.block_num, res.year.avg_solve_time, + res.year.stddev_solve_time, res.year.outliers_num, + res.year.avg_diff, res.year.min_diff, res.year.max_diff)) + throw std::runtime_error("Failed to get year difficulty statistics"); + + res.blocks30.block_num = 30; + if (!m_core.get_difficulty_stat(req.height, IMinerHandler::stat_period::by_block_number, + res.blocks30.block_num, res.blocks30.avg_solve_time, + res.blocks30.stddev_solve_time, res.blocks30.outliers_num, + res.blocks30.avg_diff, res.blocks30.min_diff, + res.blocks30.max_diff)) + throw std::runtime_error("Failed to get difficulty statistics for 30 blocks"); + res.blocks720.block_num = 720; + if (!m_core.get_difficulty_stat(req.height, IMinerHandler::stat_period::by_block_number, + res.blocks720.block_num, res.blocks720.avg_solve_time, + res.blocks720.stddev_solve_time, res.blocks720.outliers_num, + res.blocks720.avg_diff, res.blocks720.min_diff, + res.blocks720.max_diff)) + throw std::runtime_error("Failed to get difficulty statistics for 720 blocks"); + res.blocks5040.block_num = 5040; + if (!m_core.get_difficulty_stat(req.height, IMinerHandler::stat_period::by_block_number, + res.blocks5040.block_num, res.blocks5040.avg_solve_time, + res.blocks5040.stddev_solve_time, + res.blocks5040.outliers_num, res.blocks5040.avg_diff, + res.blocks5040.min_diff, res.blocks5040.max_diff)) + throw std::runtime_error("Failed to get difficulty statistics for 5040 blocks"); + res.blocks21900.block_num = 21900; + if (!m_core.get_difficulty_stat(req.height, IMinerHandler::stat_period::by_block_number, + res.blocks21900.block_num, res.blocks21900.avg_solve_time, + res.blocks21900.stddev_solve_time, + res.blocks21900.outliers_num, res.blocks21900.avg_diff, + res.blocks21900.min_diff, res.blocks21900.max_diff)) + throw std::runtime_error("Failed to get difficulty statistics for 21900 blocks"); + res.blocks131400.block_num = 131400; + if (!m_core.get_difficulty_stat(req.height, IMinerHandler::stat_period::by_block_number, + res.blocks131400.block_num, res.blocks131400.avg_solve_time, + res.blocks131400.stddev_solve_time, + res.blocks131400.outliers_num, res.blocks131400.avg_diff, + res.blocks131400.min_diff, res.blocks131400.max_diff)) + throw std::runtime_error("Failed to get difficulty statistics for 131400 blocks"); + res.blocks262800.block_num = 262800; + if (!m_core.get_difficulty_stat(req.height, IMinerHandler::stat_period::by_block_number, + res.blocks262800.block_num, res.blocks262800.avg_solve_time, + res.blocks262800.stddev_solve_time, + res.blocks262800.outliers_num, res.blocks262800.avg_diff, + res.blocks262800.min_diff, res.blocks262800.max_diff)) + throw std::runtime_error("Failed to get difficulty statistics for 262800 blocks"); + } catch (std::system_error &e) { + res.status = e.what(); + return false; + } catch (std::exception &e) { + res.status = "Error: " + std::string(e.what()); + return false; + } + + res.status = CORE_RPC_STATUS_OK; + + return true; + } } // namespace CryptoNote diff --git a/lib/Rpc/RpcServer.h b/lib/Rpc/RpcServer.h index 332c1bd4..ceda1d00 100644 --- a/lib/Rpc/RpcServer.h +++ b/lib/Rpc/RpcServer.h @@ -33,223 +33,298 @@ using namespace Qwertycoin; namespace CryptoNote { -class core; -class NodeServer; -class BlockchainExplorer; -class ICryptoNoteProtocolQuery; - -class RpcServer : public HttpServer -{ - template - struct RpcHandler - { - const Handler handler; - const bool allowBusyCore; - }; - -public: - typedef std::function< - bool(RpcServer *, const HttpRequest &request, HttpResponse &response) - > HandlerFunction; - - //typedef void (RpcServer::*HandlerPtr)(const HttpRequest &request, HttpResponse &response); - - RpcServer( - System::Dispatcher &dispatcher, - Logging::ILogger &log, - core &c, - NodeServer &p2p, - const ICryptoNoteProtocolQuery &protocolQuery); - - bool restrictRPC(const bool is_resctricted); - bool enableCors(const std::string domain); - bool setFeeAddress(const std::string &fee_address, const AccountPublicAddress &fee_acc); - bool setViewKey(const std::string &view_key); - bool setContactInfo(const std::string &contact); - bool masternode_check_incoming_tx(const BinaryArray &tx_blob); - std::string getCorsDomain(); - -private: - void processRequest(const HttpRequest &request, HttpResponse &response) override; - bool processJsonRpcRequest(const HttpRequest &request, HttpResponse &response); - bool isCoreReady(); - - // binary handlers - bool on_get_blocks( - const COMMAND_RPC_GET_BLOCKS_FAST::request &req, - COMMAND_RPC_GET_BLOCKS_FAST::response &res); - - bool on_query_blocks( - const COMMAND_RPC_QUERY_BLOCKS::request &req, - COMMAND_RPC_QUERY_BLOCKS::response &res); - bool on_query_blocks_lite( - const COMMAND_RPC_QUERY_BLOCKS_LITE::request &req, - COMMAND_RPC_QUERY_BLOCKS_LITE::response &res); - bool on_query_blocks_detailed( - const COMMAND_RPC_QUERY_BLOCKS_DETAILED::request &req, - COMMAND_RPC_QUERY_BLOCKS_DETAILED::response &res); - - bool on_get_indexes( - const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request &req, - COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::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 onGetPoolChanges( - const COMMAND_RPC_GET_POOL_CHANGES::request &req, - COMMAND_RPC_GET_POOL_CHANGES::response &rsp); - bool onGetPoolChangesLite( - const COMMAND_RPC_GET_POOL_CHANGES_LITE::request &req, - COMMAND_RPC_GET_POOL_CHANGES_LITE::response &rsp); - - // json handlers - bool on_get_info( - const COMMAND_RPC_GET_INFO::request &req, - COMMAND_RPC_GET_INFO::response &res); - bool on_get_height( - const COMMAND_RPC_GET_HEIGHT::request &req, - COMMAND_RPC_GET_HEIGHT::response &res); - bool on_get_transactions( - const COMMAND_RPC_GET_TRANSACTIONS::request &req, - COMMAND_RPC_GET_TRANSACTIONS::response &res); - bool onGetTransactionsByHeights( - const COMMAND_RPC_GET_TRANSACTIONS_BY_HEIGHTS::request &req, - COMMAND_RPC_GET_TRANSACTIONS_BY_HEIGHTS::response &res); - bool on_send_raw_tx( - const COMMAND_RPC_SEND_RAW_TX::request &req, - COMMAND_RPC_SEND_RAW_TX::response &res); - 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_stop_daemon( - const COMMAND_RPC_STOP_DAEMON::request &req, - COMMAND_RPC_STOP_DAEMON::response &res); - bool on_get_fee_address( - const COMMAND_RPC_GET_FEE_ADDRESS::request &req, - COMMAND_RPC_GET_FEE_ADDRESS::response &res); - bool on_get_peer_list( - const COMMAND_RPC_GET_PEER_LIST::request &req, - COMMAND_RPC_GET_PEER_LIST::response &res); - bool on_get_payment_id( - const COMMAND_RPC_GEN_PAYMENT_ID::request &req, - COMMAND_RPC_GEN_PAYMENT_ID::response &res); - - bool onGetBlocksDetailsByHeights( - const COMMAND_RPC_GET_BLOCKS_DETAILS_BY_HEIGHTS::request &req, - COMMAND_RPC_GET_BLOCKS_DETAILS_BY_HEIGHTS::response &rsp); - bool onGetBlocksDetailsByHashes( - const COMMAND_RPC_GET_BLOCKS_DETAILS_BY_HASHES::request &req, - COMMAND_RPC_GET_BLOCKS_DETAILS_BY_HASHES::response &rsp); - bool onGetBlockDetailsByHeight( - const COMMAND_RPC_GET_BLOCK_DETAILS_BY_HEIGHT::request &req, - COMMAND_RPC_GET_BLOCK_DETAILS_BY_HEIGHT::response &rsp); - bool onGetBlocksHashesByTimestamps( - const COMMAND_RPC_GET_BLOCKS_HASHES_BY_TIMESTAMPS::request &req, - COMMAND_RPC_GET_BLOCKS_HASHES_BY_TIMESTAMPS::response &rsp); - bool onGetTransactionsDetailsByHashes( - const COMMAND_RPC_GET_TRANSACTIONS_DETAILS_BY_HASHES::request &req, - COMMAND_RPC_GET_TRANSACTIONS_DETAILS_BY_HASHES::response &rsp); - bool onGetTransactionDetailsByHash( - const COMMAND_RPC_GET_TRANSACTION_DETAILS_BY_HASH::request &req, - COMMAND_RPC_GET_TRANSACTION_DETAILS_BY_HASH::response &rsp); - bool onGetTransactionHashesByPaymentId( - const COMMAND_RPC_GET_TRANSACTION_HASHES_BY_PAYMENT_ID::request &req, - COMMAND_RPC_GET_TRANSACTION_HASHES_BY_PAYMENT_ID::response &rsp); - - // json rpc - bool on_getblockcount( - const COMMAND_RPC_GETBLOCKCOUNT::request &req, - COMMAND_RPC_GETBLOCKCOUNT::response &res); - bool on_getblockhash( - const COMMAND_RPC_GETBLOCKHASH::request &req, - COMMAND_RPC_GETBLOCKHASH::response &res); - bool on_getblocktemplate( - const COMMAND_RPC_GETBLOCKTEMPLATE::request &req, - COMMAND_RPC_GETBLOCKTEMPLATE::response &res); - bool on_get_currency_id( - const COMMAND_RPC_GET_CURRENCY_ID::request &req, - COMMAND_RPC_GET_CURRENCY_ID::response &res); - bool on_submitblock( - const COMMAND_RPC_SUBMITBLOCK::request &req, - COMMAND_RPC_SUBMITBLOCK::response &res); - bool on_get_last_block_header( - const COMMAND_RPC_GET_LAST_BLOCK_HEADER::request &req, - COMMAND_RPC_GET_LAST_BLOCK_HEADER::response &res); - bool on_get_block_header_by_hash( - const COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::request &req, - COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::response &res); - bool on_get_block_header_by_height( - const COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::request &req, - COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response &res); - - void fill_block_header_response( - const Block &blk, - bool orphan_status, - uint64_t height, - const Crypto::Hash &hash, - block_header_response &responce); - - bool f_on_blocks_list_json( - const F_COMMAND_RPC_GET_BLOCKS_LIST::request &req, - F_COMMAND_RPC_GET_BLOCKS_LIST::response &res); - bool f_on_block_json( - const F_COMMAND_RPC_GET_BLOCK_DETAILS::request &req, - F_COMMAND_RPC_GET_BLOCK_DETAILS::response &res); - bool f_on_transaction_json( - const F_COMMAND_RPC_GET_TRANSACTION_DETAILS::request &req, - F_COMMAND_RPC_GET_TRANSACTION_DETAILS::response &res); - bool f_on_pool_json( - const F_COMMAND_RPC_GET_POOL::request &req, - F_COMMAND_RPC_GET_POOL::response &res); - bool f_on_transactions_pool_json( - const F_COMMAND_RPC_GET_POOL::request &req, - F_COMMAND_RPC_GET_POOL::response &res); - bool f_on_mempool_json( - const COMMAND_RPC_GET_MEMPOOL::request &req, - COMMAND_RPC_GET_MEMPOOL::response &res); - bool k_on_transactions_by_payment_id( - const K_COMMAND_RPC_GET_TRANSACTIONS_BY_PAYMENT_ID::request &req, - K_COMMAND_RPC_GET_TRANSACTIONS_BY_PAYMENT_ID::response &res); - bool k_on_check_tx_key( - const K_COMMAND_RPC_CHECK_TX_KEY::request &req, - K_COMMAND_RPC_CHECK_TX_KEY::response &res); - bool k_on_check_tx_with_view_key( - const K_COMMAND_RPC_CHECK_TX_WITH_PRIVATE_VIEW_KEY::request &req, - K_COMMAND_RPC_CHECK_TX_WITH_PRIVATE_VIEW_KEY::response &res); - bool k_on_check_tx_proof( - const K_COMMAND_RPC_CHECK_TX_PROOF::request &req, - K_COMMAND_RPC_CHECK_TX_PROOF::response &res); - bool k_on_check_reserve_proof( - const K_COMMAND_RPC_CHECK_RESERVE_PROOF::request &req, - K_COMMAND_RPC_CHECK_RESERVE_PROOF::response &res); - bool on_validate_address( - const COMMAND_RPC_VALIDATE_ADDRESS::request &req, - COMMAND_RPC_VALIDATE_ADDRESS::response &res); - bool on_verify_message( - const COMMAND_RPC_VERIFY_MESSAGE::request &req, - COMMAND_RPC_VERIFY_MESSAGE::response &res); - - bool on_get_difficulty_stat( - const COMMAND_RPC_GET_DIFFICULTY_STAT::request &req, - COMMAND_RPC_GET_DIFFICULTY_STAT::response &res); - - bool f_getMixin(const Transaction &transaction, uint64_t &mixin); - -private: - static std::unordered_map> s_handlers; - Logging::LoggerRef logger; - core &m_core; - NodeServer &m_p2p; - const ICryptoNoteProtocolQuery &m_protocolQuery; - bool m_restricted_rpc; - std::string m_cors_domain; - std::string m_fee_address; - std::string m_contact_info; - Crypto::SecretKey m_view_key = NULL_SECRET_KEY; - AccountPublicAddress m_fee_acc; -}; + class core; + + class NodeServer; + + class BlockchainExplorer; + + class ICryptoNoteProtocolQuery; + + class RpcServer : public HttpServer + { + template + struct RpcHandler + { + const Handler handler; + const bool allowBusyCore; + }; + + public: + typedef std::function< + bool(RpcServer *, const HttpRequest &request, HttpResponse &response) + > HandlerFunction; + + RpcServer( + System::Dispatcher &dispatcher, + Logging::ILogger &log, + core &c, + NodeServer &p2p, + const ICryptoNoteProtocolQuery &protocolQuery); + + bool restrictRPC(const bool is_resctricted); + + bool enableCors(const std::string domain); + + bool setFeeAddress(const std::string &fee_address, const AccountPublicAddress &fee_acc); + + bool setViewKey(const std::string &view_key); + + bool setContactInfo(const std::string &contact); + + bool masternodeCheckIncomingTx(const BinaryArray &tx_blob); + + std::string getCorsDomain(); + + private: + void processRequest(const HttpRequest &request, HttpResponse &response) override; + + bool processJsonRpcRequest(const HttpRequest &request, HttpResponse &response); + + bool isCoreReady(); + + // binary handlers + bool onGetBlocks( + const COMMAND_RPC_GET_BLOCKS_FAST::request &req, + COMMAND_RPC_GET_BLOCKS_FAST::response &res); + + bool onQueryBlocks( + const COMMAND_RPC_QUERY_BLOCKS::request &req, + COMMAND_RPC_QUERY_BLOCKS::response &res); + + bool onQueryBlocksLite( + const COMMAND_RPC_QUERY_BLOCKS_LITE::request &req, + COMMAND_RPC_QUERY_BLOCKS_LITE::response &res); + + bool onQueryBlocksDetailed( + const COMMAND_RPC_QUERY_BLOCKS_DETAILED::request &req, + COMMAND_RPC_QUERY_BLOCKS_DETAILED::response &res); + + bool onGetIndexes( + const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request &req, + COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response &res); + + bool onGetRandomOuts( + const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request &req, + COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response &res); + + bool onGetPoolChanges( + const COMMAND_RPC_GET_POOL_CHANGES::request &req, + COMMAND_RPC_GET_POOL_CHANGES::response &rsp); + + bool onGetPoolChangesLite( + const COMMAND_RPC_GET_POOL_CHANGES_LITE::request &req, + COMMAND_RPC_GET_POOL_CHANGES_LITE::response &rsp); + + // http handlers + bool onGetIndex( + const COMMAND_HTTP::request &req, COMMAND_HTTP::response &res); + + bool onGetSupply( + const COMMAND_HTTP::request &req, COMMAND_HTTP::response &res); + + bool onGetPaymentId( + const COMMAND_HTTP::request &req, COMMAND_HTTP::response &res); + + // json handlers + bool onGetInfo( + const COMMAND_RPC_GET_INFO::request &req, + COMMAND_RPC_GET_INFO::response &res); + + bool onGetVersion( + const COMMAND_RPC_GET_VERSION::request &req, + COMMAND_RPC_GET_VERSION::response &res); + + bool onGetHardwareInfo( + const COMMAND_RPC_GET_HARDWARE_INFO::request &req, + COMMAND_RPC_GET_HARDWARE_INFO::response &res); + + bool onGetHeight( + const COMMAND_RPC_GET_HEIGHT::request &req, + COMMAND_RPC_GET_HEIGHT::response &res); + + bool onGetTransactions( + const COMMAND_RPC_GET_TRANSACTIONS::request &req, + COMMAND_RPC_GET_TRANSACTIONS::response &res); + + bool onGetTransactionsByHeights( + const COMMAND_RPC_GET_TRANSACTIONS_BY_HEIGHTS::request &req, + COMMAND_RPC_GET_TRANSACTIONS_BY_HEIGHTS::response &res); + + bool onGetRawTransactionsByHeights( + const COMMAND_RPC_GET_RAW_TRANSACTIONS_BY_HEIGHTS::request &req, + COMMAND_RPC_GET_RAW_TRANSACTIONS_BY_HEIGHTS::response &res); + + bool onGetRawTransactionPool( + const COMMAND_RPC_GET_RAW_TRANSACTIONS_FROM_POOL::request &req, + COMMAND_RPC_GET_RAW_TRANSACTIONS_FROM_POOL::response &res); + + bool onSendRawTx( + const COMMAND_RPC_SEND_RAW_TX::request &req, + COMMAND_RPC_SEND_RAW_TX::response &res); + + bool onStartMining( + const COMMAND_RPC_START_MINING::request &req, + COMMAND_RPC_START_MINING::response &res); + + bool onStopMining( + const COMMAND_RPC_STOP_MINING::request &req, + COMMAND_RPC_STOP_MINING::response &res); + + bool onStopDaemon( + const COMMAND_RPC_STOP_DAEMON::request &req, + COMMAND_RPC_STOP_DAEMON::response &res); + + bool onGetFeeAddress( + const COMMAND_RPC_GET_FEE_ADDRESS::request &req, + COMMAND_RPC_GET_FEE_ADDRESS::response &res); + + bool onGetPeerList( + const COMMAND_RPC_GET_PEER_LIST::request &req, + COMMAND_RPC_GET_PEER_LIST::response &res); + + bool onGetBlocksDetailsByHeights( + const COMMAND_RPC_GET_BLOCKS_DETAILS_BY_HEIGHTS::request &req, + COMMAND_RPC_GET_BLOCKS_DETAILS_BY_HEIGHTS::response &rsp); + + bool onGetBlocksDetailsByHashes( + const COMMAND_RPC_GET_BLOCKS_DETAILS_BY_HASHES::request &req, + COMMAND_RPC_GET_BLOCKS_DETAILS_BY_HASHES::response &rsp); + + bool onGetBlockDetailsByHeight( + const COMMAND_RPC_GET_BLOCK_DETAILS_BY_HEIGHT::request &req, + COMMAND_RPC_GET_BLOCK_DETAILS_BY_HEIGHT::response &rsp); + + bool onGetBlockDetailsByHash( + const COMMAND_RPC_GET_BLOCK_DETAILS_BY_HASH::request &req, + COMMAND_RPC_GET_BLOCK_DETAILS_BY_HASH::response &rsp); + + bool onGetBlocksHashesByTimestamps( + const COMMAND_RPC_GET_BLOCKS_HASHES_BY_TIMESTAMPS::request &req, + COMMAND_RPC_GET_BLOCKS_HASHES_BY_TIMESTAMPS::response &rsp); + + bool onGetTransactionsDetailsByHashes( + const COMMAND_RPC_GET_TRANSACTIONS_DETAILS_BY_HASHES::request &req, + COMMAND_RPC_GET_TRANSACTIONS_DETAILS_BY_HASHES::response &rsp); + + bool onGetTransactionDetailsByHash( + const COMMAND_RPC_GET_TRANSACTION_DETAILS_BY_HASH::request &req, + COMMAND_RPC_GET_TRANSACTION_DETAILS_BY_HASH::response &rsp); + + bool onGetTransactionHashesByPaymentId( + const COMMAND_RPC_GET_TRANSACTION_HASHES_BY_PAYMENT_ID::request &req, + COMMAND_RPC_GET_TRANSACTION_HASHES_BY_PAYMENT_ID::response &rsp); + + // json rpc + bool onGetBlockCount( + const COMMAND_RPC_GET_BLOCK_COUNT::request &req, + COMMAND_RPC_GET_BLOCK_COUNT::response &res); + + bool onGetBlockHash( + const COMMAND_RPC_GET_BLOCK_HASH::request &req, + COMMAND_RPC_GET_BLOCK_HASH::response &res); + + bool onGetBlockTemplate( + const COMMAND_RPC_GET_BLOCK_TEMPLATE::request &req, + COMMAND_RPC_GET_BLOCK_TEMPLATE::response &res); + + bool onGetCurrencyId( + const COMMAND_RPC_GET_CURRENCY_ID::request &, + COMMAND_RPC_GET_CURRENCY_ID::response &res); + + bool onSubmitBlock( + const COMMAND_RPC_SUBMIT_BLOCK::request &req, + COMMAND_RPC_SUBMIT_BLOCK::response &res); + + bool onGetLastBlockHeader( + const COMMAND_RPC_GET_LAST_BLOCK_HEADER::request &req, + COMMAND_RPC_GET_LAST_BLOCK_HEADER::response &res); + + bool onGetBlockHeaderByHash( + const COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::request &req, + COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::response &res); + + bool onGetBlockHeaderByHeight( + const COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::request &req, + COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response &res); + + void fillBlockHeaderResponse( + const Block &blk, + bool orphan_status, + uint64_t height, + const Crypto::Hash &hash, + BLOCK_HEADER_RESPONSE_ENTRY &responseEntry); + + bool onBlocksListJson( + const COMMAND_RPC_GET_BLOCKS_LIST::request &req, + COMMAND_RPC_GET_BLOCKS_LIST::response &res); + + bool onBlockJson( + const COMMAND_RPC_GET_BLOCK_DETAILS::request &req, + COMMAND_RPC_GET_BLOCK_DETAILS::response &res); + + bool onTransactionJson( + const COMMAND_RPC_GET_TRANSACTION_DETAILS::request &req, + COMMAND_RPC_GET_TRANSACTION_DETAILS::response &res); + + bool onPoolJson( + const COMMAND_RPC_GET_POOL::request &req, + COMMAND_RPC_GET_POOL::response &res); + + bool onTransactionsPoolJson( + const COMMAND_RPC_GET_POOL::request &req, + COMMAND_RPC_GET_POOL::response &res); + + bool onMempoolJson( + const COMMAND_RPC_GET_MEMPOOL::request &req, + COMMAND_RPC_GET_MEMPOOL::response &res); + + bool onTransactionsByPaymentId( + const COMMAND_RPC_GET_TRANSACTIONS_BY_PAYMENT_ID::request &req, + COMMAND_RPC_GET_TRANSACTIONS_BY_PAYMENT_ID::response &res); + + bool onCheckTxKey( + const COMMAND_RPC_CHECK_TX_KEY::request &req, + COMMAND_RPC_CHECK_TX_KEY::response &res); + + bool onCheckTxWithViewKey( + const COMMAND_RPC_CHECK_TX_WITH_PRIVATE_VIEW_KEY::request &req, + COMMAND_RPC_CHECK_TX_WITH_PRIVATE_VIEW_KEY::response &res); + + bool onCheckTxProof( + const COMMAND_RPC_CHECK_TX_PROOF::request &req, + COMMAND_RPC_CHECK_TX_PROOF::response &res); + + bool onCheckReserveProof( + const COMMAND_RPC_CHECK_RESERVE_PROOF::request &req, + COMMAND_RPC_CHECK_RESERVE_PROOF::response &res); + + bool onValidateAddress( + const COMMAND_RPC_VALIDATE_ADDRESS::request &req, + COMMAND_RPC_VALIDATE_ADDRESS::response &res); + + bool onVerifyMessage( + const COMMAND_RPC_VERIFY_MESSAGE::request &req, + COMMAND_RPC_VERIFY_MESSAGE::response &res); + + bool onGetDifficultyStat( + const COMMAND_RPC_GET_DIFFICULTY_STAT::request &req, + COMMAND_RPC_GET_DIFFICULTY_STAT::response &res); + + bool onGetMixin(const Transaction &transaction, uint64_t &mixin); + + private: + static std::unordered_map> s_handlers; + Logging::LoggerRef logger; + core &m_core; + NodeServer &m_p2p; + const ICryptoNoteProtocolQuery &m_protocolQuery; + bool m_restricted_rpc; + std::string m_cors_domain; + std::string m_fee_address; + std::string m_contact_info; + Crypto::SecretKey m_view_key = NULL_SECRET_KEY; + AccountPublicAddress m_fee_acc; + }; } // namespace CryptoNote diff --git a/lib/WalletLegacy/WalletLegacy.cpp b/lib/WalletLegacy/WalletLegacy.cpp index 29e04149..d440810e 100644 --- a/lib/WalletLegacy/WalletLegacy.cpp +++ b/lib/WalletLegacy/WalletLegacy.cpp @@ -1367,11 +1367,11 @@ std::string WalletLegacy::getReserveProof(const uint64_t &reserve, const std::st Crypto::cn_fast_hash(prefix_data.data(), prefix_data.size(), prefix_hash); // generate proof entries - std::vector proofs(selected_transfers.size()); + std::vector proofs(selected_transfers.size()); for (size_t i = 0; i < selected_transfers.size(); ++i) { const TransactionOutputInformation &td = selected_transfers[i]; - reserve_proof_entry &proof = proofs[i]; + RESERVE_PROOF_ENTRY &proof = proofs[i]; proof.key_image = kimages[i]; proof.txid = td.transactionHash; proof.index_in_tx = td.outputInTransaction; @@ -1437,7 +1437,7 @@ std::string WalletLegacy::getReserveProof(const uint64_t &reserve, const std::st ); // serialize & encode - reserve_proof p; + RESERVE_PROOF p; p.proofs.assign(proofs.begin(), proofs.end()); memcpy(&p.signature, &signature, sizeof(signature)); diff --git a/src/Miner/MinerManager.cpp b/src/Miner/MinerManager.cpp index f797d7fb..4c3034e2 100644 --- a/src/Miner/MinerManager.cpp +++ b/src/Miner/MinerManager.cpp @@ -262,10 +262,10 @@ bool MinerManager::submitBlock( try { HttpClient client(m_dispatcher, daemonHost, daemonPort); - COMMAND_RPC_SUBMITBLOCK::request request; + COMMAND_RPC_SUBMIT_BLOCK::request request; request.emplace_back(Common::toHex(toBinaryArray(minedBlock))); - COMMAND_RPC_SUBMITBLOCK::response response; + COMMAND_RPC_SUBMIT_BLOCK::response response; System::EventLock lk(m_httpEvent); JsonRpc::invokeJsonRpcCommand(client, "submitblock", request, response); @@ -292,11 +292,11 @@ BlockMiningParameters MinerManager::requestMiningParameters( try { HttpClient client(dispatcher, daemonHost, daemonPort); - COMMAND_RPC_GETBLOCKTEMPLATE::request request; + COMMAND_RPC_GET_BLOCK_TEMPLATE::request request; request.wallet_address = miningAddress; request.reserve_size = 0; - COMMAND_RPC_GETBLOCKTEMPLATE::response response; + COMMAND_RPC_GET_BLOCK_TEMPLATE::response response; System::EventLock lk(m_httpEvent); JsonRpc::invokeJsonRpcCommand(client, "getblocktemplate", request, response);