1
0
Fork 0
forked from lthn/blockchain

Merge remote-tracking branch 'origin/frontend'

This commit is contained in:
wildkif 2019-02-19 16:15:47 +02:00
commit afc2e972d1
87 changed files with 3570 additions and 956 deletions

View file

@ -45,6 +45,10 @@ if (UNIX AND NOT APPLE)
find_package(Threads REQUIRED)
endif()
# TODO(unassigned): expand on types and versions, and then refactor.
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(CLANG TRUE)
endif()
if(MSVC)
add_definitions("/bigobj /Zm1000 /Z7 /MP /W3 /GS- /D_CRT_SECURE_NO_WARNINGS /wd4996 /wd4503 /wd4345 /wd4091 /D_WIN32_WINNT=0x0600 /DWIN32_LEAN_AND_MEAN /DGTEST_HAS_TR1_TUPLE=0 /FIinline_c.h /D__SSE4_1__")
@ -66,7 +70,6 @@ else()
# if(NOT APPLE)
# set(WARNINGS "${WARNINGS} -Werror")
# endif()
if(CMAKE_C_COMPILER_ID STREQUAL "Clang")
set(WARNINGS "${WARNINGS} -Wno-shift-count-overflow -Wno-error=mismatched-tags -Wno-error=null-conversion -Wno-overloaded-shift-op-parentheses -Wno-error=shift-count-overflow -Wno-error=tautological-constant-out-of-range-compare -Wno-error=unused-private-field -Wno-error=unneeded-internal-declaration")
else()
@ -103,13 +106,15 @@ else()
else()
set(STATIC_ASSERT_FLAG "-Dstatic_assert=_Static_assert")
endif()
set(LINUX_LD_GOLD "")
set(LINUX_STATIC_ICU "")
if((NOT APPLE) AND (NOT MSVC))
set(LINUX_LD_GOLD "-fuse-ld=gold")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 -D_GNU_SOURCE ${MINGW_FLAG} ${STATIC_ASSERT_FLAG} ${WARNINGS} ${C_WARNINGS} ${ARCH_FLAG}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive -ftemplate-depth-1024 -std=c++11 -D_GNU_SOURCE ${APPLE_FLAG} ${MINGW_FLAG} ${WARNINGS} ${CXX_WARNINGS} ${ARCH_FLAG}")
if (NOT APPLE AND NOT MSVC)
if (CLANG)
set(LLVM_USE_LINKER "gold")
else()
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=gold")
endif()
endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LINUX_LD_GOLD} -std=c11 -D_GNU_SOURCE ${MINGW_FLAG} ${STATIC_ASSERT_FLAG} ${WARNINGS} ${C_WARNINGS} ${ARCH_FLAG}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LINUX_LD_GOLD} -fpermissive -ftemplate-depth-1024 -std=c++11 -D_GNU_SOURCE ${APPLE_FLAG} ${MINGW_FLAG} ${WARNINGS} ${CXX_WARNINGS} ${ARCH_FLAG}")
if(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND NOT (CMAKE_C_COMPILER_VERSION VERSION_LESS 4.8))
set(DEBUG_FLAGS "-g3 -O0") #set(DEBUG_FLAGS "-g3 -Og")
else()

View file

@ -4,7 +4,8 @@ Building
### Dependencies
| component / version | minimum <br>(not recommended but may work) | recommended | most recent of what we have ever tested |
|--|--|--|--|
| gcc (Linux) | 5.4.0 | 7.2.0 | 7.2.0 |
| gcc (Linux) | 5.4.0 | 7.2.0 | 8.2.1 |
| llvm/clang (Linux) | UNKNOWN | 7.0.1 | 7.0.1 |
| [MSVC](https://visualstudio.microsoft.com/downloads/) (Windows) | 2015 (14.0 update 1) | 2015 (14.0 update 3) | 2017 (15.5.7) |
| [XCode](https://developer.apple.com/downloads/) (macOS) | 7.3.1 | 9.2 | 9.2 |
| [CMake](https://cmake.org/download/) | 2.8.6 | 3.4.1 | 3.11.0 |

View file

@ -2,6 +2,6 @@ add_subdirectory(liblmdb)
if(MSVC)
target_compile_options(lmdb PRIVATE /wd4996 /wd4503 /wd4345 /wd4267 /wd4244 /wd4146 /wd4333 /wd4172)
else()
target_compile_options(lmdb PRIVATE -Wno-discarded-qualifiers -Wno-empty-body -Wno-unused-but-set-variable)
# Warnings as used by LMDB itself (LMDB_0.9.23)
target_compile_options(lmdb PRIVATE -Wall -Wno-unused-parameter -Wbad-function-cast -Wuninitialized)
endif()

View file

@ -35,12 +35,11 @@ namespace eos {
// version of the linked boost archive library
const archive_version_type archive_version(
11
// #if BOOST_VERSION < 103700
// boost::archive::ARCHIVE_VERSION()
// #else
// boost::archive::BOOST_ARCHIVE_VERSION()
// #endif
#if BOOST_VERSION < 103700
boost::archive::ARCHIVE_VERSION()
#else
boost::archive::BOOST_ARCHIVE_VERSION()
#endif
);
/**

View file

@ -3,7 +3,7 @@
* \file portable_iarchive.hpp
* \brief Provides an archive to read from portable binary files.
* \author christian.pfligersdorffer@gmx.at
* \version 5.0
* \version 5.1
*
* This pair of archives brings the advantages of binary streams to the cross
* platform boost::serialization user. While being almost as fast as the native
@ -23,6 +23,9 @@
* chance it will instantly work for your specific setup. If you encounter
* problems or have suggestions please contact the author.
*
* \note Version 5.1 is now compatible with boost up to version 1.59. Thanks to
* ecotax for pointing to the issue with shared_ptr_helper.
*
* \note Version 5.0 is now compatible with boost up to version 1.49 and enables
* serialization of std::wstring by converting it to/from utf8 (thanks to
* Arash Abghari for this suggestion). With that all unit tests from the
@ -89,9 +92,7 @@
#include <boost/archive/basic_binary_iprimitive.hpp>
#include <boost/archive/basic_binary_iarchive.hpp>
#if BOOST_VERSION >= 105600
#include <boost/serialization/shared_ptr_helper.hpp>
#elif BOOST_VERSION >= 103500
#if BOOST_VERSION >= 103500 && BOOST_VERSION < 105600
#include <boost/archive/shared_ptr_helper.hpp>
#endif
@ -115,14 +116,16 @@
#include <boost/math/fpclassify.hpp>
#elif BOOST_VERSION < 104800
#include <boost/spirit/home/support/detail/integer/endian.hpp>
// Boost 1.69 (Spirit.X2/X3) has dropped their own FP routines in favor of boost::math
#elif BOOST_VERSION < 106900
#include <boost/spirit/home/support/detail/math/fpclassify.hpp>
#include <boost/spirit/home/support/detail/endian/endian.hpp>
#else
#include <boost/spirit/home/support/detail/endian/endian.hpp>
#include <boost/spirit/home/support/detail/math/fpclassify.hpp>
#endif
// namespace alias
#if BOOST_VERSION < 103800
#if BOOST_VERSION < 103800 || BOOST_VERSION >= 106900
namespace fp = boost::math;
#else
namespace fp = boost::spirit::math;
@ -135,7 +138,7 @@ namespace endian = boost::detail;
namespace endian = boost::spirit::detail;
#endif
#ifndef BOOST_NO_STD_WSTRING
#if BOOST_VERSION >= 104500 && !defined BOOST_NO_STD_WSTRING
// used for wstring to utf8 conversion
#include <boost/program_options/config.hpp>
#include <boost/program_options/detail/convert.hpp>
@ -190,9 +193,7 @@ namespace eos {
// load_override functions so we chose to stay one level higher
, public boost::archive::basic_binary_iarchive<portable_iarchive>
#if BOOST_VERSION >= 105600
// mix-in helper class for serializing shared_ptr does not exist anymore
#elif BOOST_VERSION >= 103500
#if BOOST_VERSION >= 103500 && BOOST_VERSION < 105600
// mix-in helper class for serializing shared_ptr
, public boost::archive::detail::shared_ptr_helper
#endif
@ -349,7 +350,7 @@ namespace eos {
T temp = size < 0 ? -1 : 0;
load_binary(&temp, abs(size));
// load the value from little endian - is is then converted
// load the value from little endian - it is then converted
// to the target type T and fits it because size <= sizeof(T)
t = endian::load_little_endian<T, sizeof(T)>(&temp);
}

View file

@ -3,7 +3,7 @@
* \file portable_oarchive.hpp
* \brief Provides an archive to create portable binary files.
* \author christian.pfligersdorffer@gmx.at
* \version 5.0
* \version 5.1
*
* This pair of archives brings the advantages of binary streams to the cross
* platform boost::serialization user. While being almost as fast as the native
@ -23,6 +23,9 @@
* chance it will instantly work for your specific setup. If you encounter
* problems or have suggestions please contact the author.
*
* \note Version 5.1 is now compatible with boost up to version 1.59. Thanks to
* ecotax for pointing to the issue with shared_ptr_helper.
*
* \note Version 5.0 is now compatible with boost up to version 1.49 and enables
* serialization of std::wstring by converting it to/from utf8 (thanks to
* Arash Abghari for this suggestion). With that all unit tests from the
@ -91,15 +94,9 @@
#include <boost/utility/enable_if.hpp>
#include <boost/archive/basic_binary_oprimitive.hpp>
#include <boost/archive/basic_binary_oarchive.hpp>
#if BOOST_VERSION >= 105600
#include <boost/serialization/shared_ptr_helper.hpp>
#elif BOOST_VERSION >= 103500
#include <boost/archive/shared_ptr_helper.hpp>
#endif
#if BOOST_VERSION >= 104500
#include <boost/program_options/config.hpp>
#include <boost/program_options/detail/convert.hpp>
#if BOOST_VERSION >= 103500 && BOOST_VERSION < 105600
#include <boost/archive/shared_ptr_helper.hpp>
#endif
// funny polymorphics
@ -122,14 +119,16 @@
#include <boost/math/fpclassify.hpp>
#elif BOOST_VERSION < 104800
#include <boost/spirit/home/support/detail/integer/endian.hpp>
// Boost 1.69 (Spirit.X2/X3) has dropped their own FP routines in favor of boost::math
#elif BOOST_VERSION < 106900
#include <boost/spirit/home/support/detail/math/fpclassify.hpp>
#include <boost/spirit/home/support/detail/endian/endian.hpp>
#else
#include <boost/spirit/home/support/detail/endian/endian.hpp>
#include <boost/spirit/home/support/detail/math/fpclassify.hpp>
#endif
// namespace alias fp_classify
#if BOOST_VERSION < 103800
#if BOOST_VERSION < 103800 || BOOST_VERSION >= 106900
namespace fp = boost::math;
#else
namespace fp = boost::spirit::math;
@ -142,7 +141,7 @@ namespace endian = boost::detail;
namespace endian = boost::spirit::detail;
#endif
#ifndef BOOST_NO_STD_WSTRING
#if BOOST_VERSION >= 104500 && !defined BOOST_NO_STD_WSTRING
// used for wstring to utf8 conversion
#include <boost/program_options/config.hpp>
#include <boost/program_options/detail/convert.hpp>
@ -195,9 +194,7 @@ namespace eos {
// save_override functions so we chose to stay one level higher
, public boost::archive::basic_binary_oarchive<portable_oarchive>
#if BOOST_VERSION >= 105600
// mix-in helper class for serializing shared_ptr does not exist anymore
#elif BOOST_VERSION >= 103500
#if BOOST_VERSION >= 103500 && BOOST_VERSION < 105600
// mix-in helper class for serializing shared_ptr
, public boost::archive::detail::shared_ptr_helper
#endif

View file

@ -893,12 +893,10 @@ namespace log_space
FAST_CRITICAL_REGION_END();
return true;
}
std::string get_thread_prefix()
{
FAST_CRITICAL_REGION_LOCAL(m_critical_sec);
return m_thr_prefix_strings[misc_utils::get_thread_string_id()];
}
std::string get_default_log_file()
@ -1160,7 +1158,6 @@ namespace log_space
}
static bool add_logger( ibase_log_stream* pstream, int log_level_limit = LOG_LEVEL_4 )
{
logger* plogger = get_or_create_instance();
@ -1234,8 +1231,6 @@ POP_WARNINGS
}
#ifdef _MSC_VER
@ -1290,8 +1285,6 @@ POP_WARNINGS
return plogger->get_thread_prefix();
}
static std::string get_prefix_entry()
{
std::stringstream str_prefix;
@ -1587,8 +1580,6 @@ POP_WARNINGS
#endif
#define LOG_PRINT_NO_POSTFIX(mess, level) LOG_PRINT_NO_POSTFIX2(LOG_DEFAULT_TARGET, mess, level)
#define LOG_PRINT_NO_PREFIX(mess, level) LOG_PRINT_NO_PREFIX2(LOG_DEFAULT_TARGET, mess, level)
#define LOG_PRINT_NO_PREFIX_NO_POSTFIX(mess, level) LOG_PRINT_NO_PREFIX_NO_POSTFIX2(LOG_DEFAULT_TARGET, mess, level)
@ -1694,7 +1685,6 @@ POP_WARNINGS
#define CHECK_AND_ASSERT_MES2(expr, message) do{if(!(expr)) {LOG_ERROR(message); };}while(0)
#endif
}
POP_WARNINGS

View file

@ -137,6 +137,7 @@ namespace net_utils
bool m_is_stop_handling;
http::http_request_info m_query_info;
size_t m_len_summary, m_len_remain;
size_t m_precommand_line_chars;
config_type& m_config;
bool m_want_close;
protected:

View file

@ -33,8 +33,9 @@
#include "file_io_utils.h"
#include "net_parse_helpers.h"
#define HTTP_MAX_URI_LEN 9000
#define HTTP_MAX_HEADER_LEN 100000
#define HTTP_MAX_URI_LEN 9000
#define HTTP_MAX_PRE_COMMAND_LINE_CHARS 20
#define HTTP_MAX_HEADER_LEN 100000
PUSH_WARNINGS
DISABLE_GCC_WARNING(maybe-uninitialized)
@ -204,7 +205,8 @@ namespace net_utils
m_len_remain(0),
m_config(config),
m_want_close(false),
m_psnd_hndlr(psnd_hndlr)
m_psnd_hndlr(psnd_hndlr),
m_precommand_line_chars(0)
{
}
@ -217,6 +219,7 @@ namespace net_utils
m_body_transfer_type = http_body_transfer_undefined;
m_query_info.clear();
m_len_summary = 0;
m_precommand_line_chars = 0;
return true;
}
//--------------------------------------------------------------------------------------------
@ -257,11 +260,19 @@ namespace net_utils
if((m_cache[0] == '\r' || m_cache[0] == '\n'))
{
//some times it could be that before query line cold be few line breaks
//so we have to be calm without panic with assers
//so we have to be calm down without panic and asserts
m_cache.erase(0, 1);
//fixed bug with possible '\r\n' chars flood, thanks to @anonimal (https://github.com/anonimal) for pointing this
++m_precommand_line_chars;
if (m_precommand_line_chars > HTTP_MAX_PRE_COMMAND_LINE_CHARS)
{
LOG_ERROR("simple_http_connection_handler::handle_buff_in: Too long URI line");
m_state = http_state_error;
return false;
}
break;
}
if(std::string::npos != m_cache.find('\n', 0))
handle_invoke_query_line();
else
@ -269,7 +280,7 @@ namespace net_utils
m_is_stop_handling = true;
if(m_cache.size() > HTTP_MAX_URI_LEN)
{
LOG_ERROR("simple_http_connection_handler::handle_buff_out: Too long URI line");
LOG_ERROR("simple_http_connection_handler::handle_buff_in: Too long URI line");
m_state = http_state_error;
return false;
}
@ -297,10 +308,10 @@ namespace net_utils
case http_state_connection_close:
return false;
default:
LOG_ERROR("simple_http_connection_handler::handle_char_out: Wrong state: " << m_state);
LOG_ERROR("simple_http_connection_handler::handle_buff_in: Wrong state: " << m_state);
return false;
case http_state_error:
LOG_ERROR("simple_http_connection_handler::handle_char_out: Error state!!!");
LOG_ERROR("simple_http_connection_handler::handle_buff_in: Error state!!!");
return false;
}
@ -334,10 +345,10 @@ namespace net_utils
template<class t_connection_context>
bool simple_http_connection_handler<t_connection_context>::handle_invoke_query_line()
{
LOG_FRAME("simple_http_connection_handler<t_connection_context>::handle_recognize_protocol_out(*)", LOG_LEVEL_3);
LOG_FRAME("simple_http_connection_handler<t_connection_context>::handle_invoke_query_line(*)", LOG_LEVEL_3);
STATIC_REGEXP_EXPR_1(rexp_match_command_line, "^(((OPTIONS)|(GET)|(HEAD)|(POST)|(PUT)|(DELETE)|(TRACE)) (\\S+) HTTP/(\\d+).(\\d+))\r?\n", boost::regex::icase | boost::regex::normal);
// 123 4 5 6 7 8 9 10 11 12
// 123 4 5 6 7 8 9 10 11 12
//size_t match_len = 0;
boost::smatch result;
if(boost::regex_search(m_cache, result, rexp_match_command_line, boost::match_default) && result[0].matched)
@ -682,4 +693,4 @@ namespace net_utils
POP_WARNINGS
//--------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------

View file

@ -701,6 +701,11 @@ namespace epee
#define CRITICAL_REGION_BEGIN1(x) CRITICAL_REGION_BEGIN_VAR(x, critical_region_var1)
#define CRITICAL_REGION_END() }
#define SHARED_CRITICAL_REGION_LOCAL(x) boost::shared_lock< boost::shared_mutex > critical_region_var(x)
#define EXCLUSIVE_CRITICAL_REGION_LOCAL(x) boost::unique_lock< boost::shared_mutex > critical_region_var(x)
#define SHARED_CRITICAL_REGION_BEGIN(x) { SHARED_CRITICAL_REGION_LOCAL(x)
#define EXCLUSIVE_CRITICAL_REGION_BEGIN(x) { EXCLUSIVE_CRITICAL_REGION_LOCAL(x)
}

View file

@ -92,7 +92,7 @@ add_library(common ${COMMON})
add_dependencies(common version ${PCH_LIB_NAME})
ENABLE_SHARED_PCH(COMMON)
if(NOT MSVC AND NOT APPLE)
if(NOT MSVC AND NOT APPLE AND NOT CLANG) # TODO(unassigned): do we really need the clang equivalent?
target_compile_options(common PRIVATE -fno-var-tracking-assignments)
endif()

View file

@ -0,0 +1,36 @@
// Copyright (c) 2018-2019 Zano Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#pragma once
#include <string>
#include <sstream>
#include "include_base_utils.h"
#include "crypto/crypto.h"
#include "crypto/hash.h"
//------
bool parse_hash256(const std::string str_hash, crypto::hash& hash);
template <class T>
std::ostream &print256(std::ostream &o, const T &v) {
return o << "<" << epee::string_tools::pod_to_hex(v) << ">";
}
template <class T>
std::ostream &print16(std::ostream &o, const T &v) {
return o << "<" << epee::string_tools::pod_to_hex(v).substr(0, 5) << "..>";
}
template <class T>
std::string print16(const T &v) {
return std::string("<") + epee::string_tools::pod_to_hex(v).substr(0, 5) + "..>";
}
namespace crypto {
inline std::ostream &operator <<(std::ostream &o, const crypto::public_key &v) { return print256(o, v); }
inline std::ostream &operator <<(std::ostream &o, const crypto::secret_key &v) { return print256(o, v); }
inline std::ostream &operator <<(std::ostream &o, const crypto::key_derivation &v) { return print256(o, v); }
inline std::ostream &operator <<(std::ostream &o, const crypto::key_image &v) { return print256(o, v); }
inline std::ostream &operator <<(std::ostream &o, const crypto::signature &v) { return print256(o, v); }
inline std::ostream &operator <<(std::ostream &o, const crypto::hash &v) { return print256(o, v); }
}

View file

@ -34,11 +34,13 @@
// Copyright (c) 2014-2018 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
#include <cassert>
#include <map>
#include <cstdint>
#include <boost/algorithm/string.hpp>
#include "mnemonic-encoding.h"
#include "include_base_utils.h"
namespace tools
{
@ -3371,5 +3373,18 @@ namespace tools
}
return res;
}
std::string word_by_num(uint32_t n)
{
if (n >= NUMWORDS)
return "";
return wordsArray[n];
}
uint64_t num_by_word(const std::string& w)
{
auto it = wordsMap.find(w);
CHECK_AND_ASSERT_THROW_MES(it!= wordsMap.end(), "unable to find word \"" << w << "\" in mnemonic dictionary");
return it->second;
}
}
}

View file

@ -42,5 +42,7 @@ namespace tools
{
std::vector<unsigned char> text2binary(const std::string& text);
std::string binary2text(const std::vector<unsigned char>& binary);
std::string word_by_num(uint32_t n);
uint64_t num_by_word(const std::string& w);
}
}

View file

@ -5,6 +5,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "include_base_utils.h"
#include "zlib_helper.h"
using namespace epee;
#include "util.h"
@ -526,4 +527,65 @@ std::string get_nix_version_display_string()
#endif
return std::error_code(code, std::system_category());
}
#define REQUEST_LOG_CHUNK_SIZE_MAX (10 * 1024 * 1024)
bool get_log_chunk_gzipped(uint64_t offset, uint64_t size, std::string& output, std::string& error)
{
if (size > REQUEST_LOG_CHUNK_SIZE_MAX)
{
error = std::string("size is exceeding the limit = ") + epee::string_tools::num_to_string_fast(REQUEST_LOG_CHUNK_SIZE_MAX);
return false;
}
std::string log_filename = epee::log_space::log_singletone::get_actual_log_file_path();
if (std::ifstream log{ log_filename, std::ifstream::ate | std::ifstream::binary })
{
uint64_t file_size = log.tellg();
if (offset >= file_size)
{
error = "offset is out of bounds";
return false;
}
if (offset + size > file_size)
{
error = "offset + size if out of bounds";
return false;
}
if (size != 0)
{
log.seekg(offset);
output.resize(size);
log.read(&output.front(), size);
uint64_t read_bytes = log.gcount();
if (read_bytes != size)
{
error = std::string("read bytes: ") + epee::string_tools::num_to_string_fast(read_bytes);
return false;
}
if (!epee::zlib_helper::pack(output))
{
error = "zlib pack failed";
return false;
}
}
return true;
}
error = std::string("can't open ") + log_filename;
return false;
}
uint64_t get_log_file_size()
{
std::string log_filename = epee::log_space::log_singletone::get_actual_log_file_path();
std::ifstream in(log_filename, std::ifstream::ate | std::ifstream::binary);
return static_cast<uint64_t>(in.tellg());
}
}

View file

@ -48,6 +48,8 @@ namespace tools
return k;
}
bool get_log_chunk_gzipped(uint64_t offset, uint64_t size, std::string& output, std::string& error);
uint64_t get_log_file_size();
class signal_handler
{

View file

@ -54,6 +54,8 @@ namespace
const command_line::arg_descriptor<std::string> arg_generate_genesis = {"generate-genesis", "Generate genesis coinbase based on config file", "", true };
const command_line::arg_descriptor<uint64_t> arg_genesis_split_amount = { "genesis-split-amount", "Set split amount for generating genesis block", 0, true };
const command_line::arg_descriptor<std::string> arg_get_info_flags = { "getinfo-flags-hex", "Set of bits for rpc-get-daemon-info", "", true };
const command_line::arg_descriptor<int64_t> arg_set_peer_log_level = { "set-peer-log-level", "Set log level for remote peer", 0, true };
const command_line::arg_descriptor<uint64_t> arg_download_peer_log = { "download-peer-log", "Download log from remote peer (starting offset)", 0, true };
}
typedef COMMAND_REQUEST_STAT_INFO_T<t_currency_protocol_handler<core>::stat_info> COMMAND_REQUEST_STAT_INFO;
@ -839,7 +841,166 @@ bool generate_and_print_keys()
<< "PRIVATE KEY: " << epee::string_tools::pod_to_hex(sk);
return true;
}
//---------------------------------------------------------------------------------------------------------------
template<class command_t>
bool invoke_debug_command(po::variables_map& vm, const crypto::secret_key& sk, levin::levin_client_impl2& transport, peerid_type& peer_id, typename command_t::request& req, typename command_t::response& rsp)
{
if (!transport.is_connected())
{
if (!transport.connect(command_line::get_arg(vm, arg_ip), static_cast<int>(command_line::get_arg(vm, arg_port)), static_cast<int>(command_line::get_arg(vm, arg_timeout))))
{
std::cout << "{" << ENDL << " \"status\": \"ERROR: " << "Failed to connect to " << command_line::get_arg(vm, arg_ip) << ":" << command_line::get_arg(vm, arg_port) << "\"" << ENDL << "}" << ENDL;
return false;
}
}
if (!peer_id)
{
COMMAND_REQUEST_PEER_ID::request id_req = AUTO_VAL_INIT(id_req);
COMMAND_REQUEST_PEER_ID::response id_rsp = AUTO_VAL_INIT(id_rsp);
if (!net_utils::invoke_remote_command2(COMMAND_REQUEST_PEER_ID::ID, id_req, id_rsp, transport))
{
std::cout << "{" << ENDL << " \"status\": \"ERROR: " << "Failed to connect to " << command_line::get_arg(vm, arg_ip) << ":" << command_line::get_arg(vm, arg_port) << "\"" << ENDL << "}" << ENDL;
return false;
}
else
{
peer_id = id_rsp.my_id;
}
}
nodetool::proof_of_trust pot = AUTO_VAL_INIT(pot);
pot.peer_id = peer_id;
pot.time = time(NULL);
crypto::public_key pubk = AUTO_VAL_INIT(pubk);
string_tools::hex_to_pod(P2P_MAINTAINERS_PUB_KEY, pubk);
crypto::hash h = tools::get_proof_of_trust_hash(pot);
crypto::generate_signature(h, pubk, sk, pot.sign);
req.tr = pot;
return net_utils::invoke_remote_command2(command_t::ID, req, rsp, transport);
}
//---------------------------------------------------------------------------------------------------------------
bool handle_set_peer_log_level(po::variables_map& vm)
{
crypto::secret_key sk = AUTO_VAL_INIT(sk);
if (!get_private_key(sk, vm))
{
std::cout << "ERROR: secret key error" << ENDL;
return false;
}
int64_t log_level = command_line::get_arg(vm, arg_set_peer_log_level);
if (log_level < LOG_LEVEL_0 || log_level > LOG_LEVEL_MAX)
{
std::cout << "Error: invalid log level value: " << log_level << ENDL;
return false;
}
levin::levin_client_impl2 transport;
peerid_type peer_id = 0;
COMMAND_SET_LOG_LEVEL::request req = AUTO_VAL_INIT(req);
req.new_log_level = log_level;
COMMAND_SET_LOG_LEVEL::response rsp = AUTO_VAL_INIT(rsp);
if (!invoke_debug_command<COMMAND_SET_LOG_LEVEL>(vm, sk, transport, peer_id, req, rsp))
{
std::cout << "ERROR: invoking COMMAND_SET_LOG_LEVEL failed" << ENDL;
return false;
}
std::cout << "OK! Log level changed: " << rsp.old_log_level << " -> " << rsp.current_log_level << ENDL;
return true;
}
//---------------------------------------------------------------------------------------------------------------
bool handle_download_peer_log(po::variables_map& vm)
{
crypto::secret_key sk = AUTO_VAL_INIT(sk);
if (!get_private_key(sk, vm))
{
std::cout << "ERROR: secret key error" << ENDL;
return false;
}
uint64_t start_offset = command_line::get_arg(vm, arg_download_peer_log);
levin::levin_client_impl2 transport;
peerid_type peer_id = 0;
COMMAND_REQUEST_LOG::request req = AUTO_VAL_INIT(req);
COMMAND_REQUEST_LOG::response rsp = AUTO_VAL_INIT(rsp);
if (!invoke_debug_command<COMMAND_REQUEST_LOG>(vm, sk, transport, peer_id, req, rsp) || !rsp.error.empty())
{
std::cout << "ERROR: invoking COMMAND_REQUEST_LOG failed: " << rsp.error << ENDL;
return false;
}
std::cout << "Current log level: " << rsp.current_log_level << ENDL;
std::cout << "Current log size: " << rsp.current_log_size << ENDL;
if (start_offset >= rsp.current_log_size)
{
std::cout << "ERROR: invalid start offset: " << start_offset << ", log size: " << rsp.current_log_size << ENDL;
return false;
}
std::cout << "Downloading..." << ENDL;
std::string local_filename = tools::get_default_data_dir() + "/log_" + epee::string_tools::num_to_string_fast(peer_id) + ".log";
std::ofstream log{ local_filename, std::ifstream::binary };
if (!log)
{
std::cout << "Couldn't open " << local_filename << " for writing." << ENDL;
return false;
}
const uint64_t chunk_size = 1024 * 1024 * 5;
uint64_t end_offset = start_offset;
uint64_t bytes = 0;
while (true)
{
req.log_chunk_offset = end_offset;
req.log_chunk_size = std::min(chunk_size, rsp.current_log_size - req.log_chunk_offset);
if (req.log_chunk_size == 0)
break;
std::this_thread::sleep_for(std::chrono::seconds(1));
if (!invoke_debug_command<COMMAND_REQUEST_LOG>(vm, sk, transport, peer_id, req, rsp) || !rsp.error.empty())
{
std::cout << "ERROR: invoking COMMAND_REQUEST_LOG failed: " << rsp.error << ENDL;
return false;
}
if (!epee::zlib_helper::unpack(rsp.log_chunk))
{
std::cout << "ERROR: zip unpack failed" << ENDL;
return false;
}
if (rsp.log_chunk.size() != req.log_chunk_size)
{
std::cout << "ERROR: unpacked size: " << rsp.log_chunk.size() << ", requested: " << req.log_chunk_size << ENDL;
return false;
}
log.write(rsp.log_chunk.c_str(), rsp.log_chunk.size());
end_offset += req.log_chunk_size;
std::cout << end_offset - start_offset << " bytes downloaded" << ENDL;
}
std::cout << "Remote log from offset " << start_offset << " to offset " << end_offset << " (" << end_offset - start_offset << " bytes) " <<
"was successfully downloaded to " << local_filename;
return true;
}
//---------------------------------------------------------------------------------------------------------------
int main(int argc, char* argv[])
{
@ -874,7 +1035,9 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_params, arg_genesis_split_amount);
command_line::add_arg(desc_params, arg_get_info_flags);
command_line::add_arg(desc_params, arg_log_journal_len);
command_line::add_arg(desc_params, arg_set_peer_log_level);
command_line::add_arg(desc_params, arg_download_peer_log);
po::options_description desc_all;
@ -931,6 +1094,14 @@ int main(int argc, char* argv[])
{
return generate_genesis(command_line::get_arg(vm, arg_generate_genesis), 10000000000000000) ? 0 : 1;
}
else if (command_line::has_arg(vm, arg_set_peer_log_level))
{
return handle_set_peer_log_level(vm) ? 0 : 1;
}
else if (command_line::has_arg(vm, arg_download_peer_log))
{
return handle_download_peer_log(vm) ? 0 : 1;
}
else
{
std::cerr << "Not enough arguments." << ENDL;

View file

@ -77,27 +77,16 @@ namespace crypto {
memcpy(&res, tmp, 32);
}
void crypto_ops::keys_from_short(unsigned char* a_part, public_key &pub, secret_key &sec)
void crypto_ops::keys_from_default(unsigned char* a_part, public_key &pub, secret_key &sec, size_t brain_wallet_seed_size)
{
unsigned char tmp[64] = { 0 };
static_assert(sizeof tmp >= BRAINWALLET_SHORT_SEED_SIZE, "size mismatch");
memcpy(tmp, a_part, BRAINWALLET_SHORT_SEED_SIZE);
cn_fast_hash(tmp, 16, (char*)&tmp[16]);
cn_fast_hash(tmp, 32, (char*)&tmp[32]);
if (!(sizeof(tmp) >= brain_wallet_seed_size))
{
throw std::runtime_error("size mismatch");
}
sc_reduce(tmp);
memcpy(&sec, tmp, 32);
ge_p3 point;
ge_scalarmult_base(&point, &sec);
ge_p3_tobytes(&pub, &point);
}
void crypto_ops::keys_from_default(unsigned char* a_part, public_key &pub, secret_key &sec)
{
unsigned char tmp[64] = { 0 };
static_assert(sizeof tmp >= BRAINWALLET_DEFAULT_SEED_SIZE, "size mismatch");
memcpy(tmp, a_part, BRAINWALLET_DEFAULT_SEED_SIZE);
memcpy(tmp, a_part, brain_wallet_seed_size);
cn_fast_hash(tmp, 32, (char*)&tmp[32]);
@ -108,12 +97,14 @@ namespace crypto {
ge_p3_tobytes(&pub, &point);
}
void crypto_ops::generate_brain_keys(public_key &pub, secret_key &sec, std::string& seed)
void crypto_ops::generate_brain_keys(public_key &pub, secret_key &sec, std::string& seed, size_t brain_wallet_seed_size)
{
unsigned char tmp[BRAINWALLET_DEFAULT_SEED_SIZE];
generate_random_bytes(BRAINWALLET_DEFAULT_SEED_SIZE, tmp);
seed.assign((const char*)tmp, BRAINWALLET_DEFAULT_SEED_SIZE);
keys_from_default(tmp, pub, sec);
std::vector<unsigned char> tmp_vector;
tmp_vector.resize(brain_wallet_seed_size, 0);
unsigned char *tmp = &tmp_vector[0];
generate_random_bytes(brain_wallet_seed_size, tmp);
seed.assign((const char*)tmp, brain_wallet_seed_size);
keys_from_default(tmp, pub, sec, brain_wallet_seed_size);
}
static inline void hash_to_scalar(const void *data, size_t length, ec_scalar &res)

View file

@ -20,8 +20,6 @@
PUSH_WARNINGS
DISABLE_CLANG_WARNING(unused-private-field)
#define BRAINWALLET_DEFAULT_SEED_SIZE 32
#define BRAINWALLET_SHORT_SEED_SIZE 16
namespace crypto {
@ -75,12 +73,10 @@ namespace crypto {
static void generate_keys(public_key &, secret_key &);
friend void generate_keys(public_key &, secret_key &);
static void generate_brain_keys(public_key &, secret_key &, std::string& seed);
friend void generate_brain_keys(public_key &, secret_key &, std::string& seed);
static void keys_from_short(unsigned char* a_part, public_key &pub, secret_key &sec);
friend void keys_from_short(unsigned char* a_part, public_key &pub, secret_key &sec);
static void keys_from_default(unsigned char* a_part, public_key &pub, secret_key &sec);
friend void keys_from_default(unsigned char* a_part, public_key &pub, secret_key &sec);
static void generate_brain_keys(public_key &, secret_key &, std::string& seed, size_t brain_wallet_seed_size);
friend void generate_brain_keys(public_key &, secret_key &, std::string& seed, size_t brain_wallet_seed_size);
static void keys_from_default(unsigned char* a_part, public_key &pub, secret_key &sec, size_t brain_wallet_seed_size);
friend void keys_from_default(unsigned char* a_part, public_key &pub, secret_key &sec, size_t brain_wallet_seed_size);
static void dependent_key(const secret_key& first, secret_key& second);
friend void dependent_key(const secret_key& first, secret_key& second);
static bool check_key(const public_key &);
@ -139,19 +135,14 @@ namespace crypto {
crypto_ops::generate_keys(pub, sec);
}
inline void generate_brain_keys(public_key &pub, secret_key &sec, std::string& seed) {
crypto_ops::generate_brain_keys(pub, sec, seed);
}
inline void keys_from_short(unsigned char* a_part, public_key &pub, secret_key &sec)
{
crypto_ops::keys_from_short(a_part, pub, sec);
inline void generate_brain_keys(public_key &pub, secret_key &sec, std::string& seed, size_t brain_wallet_seed_size) {
crypto_ops::generate_brain_keys(pub, sec, seed, brain_wallet_seed_size);
}
inline void keys_from_default(unsigned char* a_part, public_key &pub, secret_key &sec)
inline void keys_from_default(unsigned char* a_part, public_key &pub, secret_key &sec, size_t brain_wallet_seed_size)
{
crypto_ops::keys_from_default(a_part, pub, sec);
crypto_ops::keys_from_default(a_part, pub, sec, brain_wallet_seed_size);
}
inline void dependent_key(const secret_key& first, secret_key& second){

View file

@ -41,9 +41,8 @@ namespace currency
}
//-----------------------------------------------------------------
void account_base::generate()
{
//generate_keys(m_keys.m_account_address.m_spend_public_key, m_keys.m_spend_secret_key);
generate_brain_keys(m_keys.m_account_address.m_spend_public_key, m_keys.m_spend_secret_key, m_seed);
{
generate_brain_keys(m_keys.m_account_address.m_spend_public_key, m_keys.m_spend_secret_key, m_seed, BRAINWALLET_DEFAULT_SEED_SIZE);
dependent_key(m_keys.m_spend_secret_key, m_keys.m_view_secret_key);
if (!crypto::secret_key_to_public_key(m_keys.m_view_secret_key, m_keys.m_account_address.m_view_public_key))
throw std::runtime_error("Failed to create public view key");
@ -62,12 +61,16 @@ namespace currency
return m_seed;
}
//-----------------------------------------------------------------
std::string account_base::get_restore_braindata() const
{
std::string restore_buff = get_restore_data();
std::vector<unsigned char> v;
v.assign((unsigned char*)restore_buff.data(), (unsigned char*)restore_buff.data() + restore_buff.size());
return tools::mnemonic_encoding::binary2text(v);
std::string seed_brain_data = tools::mnemonic_encoding::binary2text(v);
std::string timestamp_word = currency::get_word_from_timstamp(m_creation_timestamp);
seed_brain_data = seed_brain_data + timestamp_word;
return seed_brain_data;
}
//-----------------------------------------------------------------
bool account_base::restore_keys(const std::string& restore_data)
@ -75,11 +78,7 @@ namespace currency
//CHECK_AND_ASSERT_MES(restore_data.size() == ACCOUNT_RESTORE_DATA_SIZE, false, "wrong restore data size");
if (restore_data.size() == BRAINWALLET_DEFAULT_SEED_SIZE)
{
crypto::keys_from_default((unsigned char*)restore_data.data(), m_keys.m_account_address.m_spend_public_key, m_keys.m_spend_secret_key);
}
else if(restore_data.size() == BRAINWALLET_SHORT_SEED_SIZE)
{
crypto::keys_from_short((unsigned char*)restore_data.data(), m_keys.m_account_address.m_spend_public_key, m_keys.m_spend_secret_key);
crypto::keys_from_default((unsigned char*)restore_data.data(), m_keys.m_account_address.m_spend_public_key, m_keys.m_spend_secret_key, BRAINWALLET_DEFAULT_SEED_SIZE);
}
else
{
@ -94,15 +93,27 @@ namespace currency
return true;
}
//-----------------------------------------------------------------
bool account_base::restore_keys_from_braindata(const std::string& restore_data)
bool account_base::restore_keys_from_braindata(const std::string& restore_data_)
{
//cut the last timestamp word from restore_dats
std::list<std::string> words;
boost::split(words, restore_data_, boost::is_space());
CHECK_AND_ASSERT_THROW_MES(words.size() == BRAINWALLET_DEFAULT_WORDS_COUNT, "Words count missmatch: " << words.size());
std::string timestamp_word = words.back();
words.erase(--words.end());
std::string restore_data_local = boost::algorithm::join(words, " ");
std::vector<unsigned char> bin = tools::mnemonic_encoding::text2binary(restore_data);
std::vector<unsigned char> bin = tools::mnemonic_encoding::text2binary(restore_data_local);
if (!bin.size())
return false;
std::string restore_buff((const char*)&bin[0], bin.size());
return restore_keys(restore_buff);
bool r = restore_keys(restore_buff);
CHECK_AND_ASSERT_MES(r, false, "restore_keys failed");
m_creation_timestamp = get_timstamp_from_word(timestamp_word);
return true;
}
//-----------------------------------------------------------------
std::string account_base::get_public_address_str()

View file

@ -10,9 +10,9 @@
#include "crypto/crypto.h"
#include "serialization/keyvalue_serialization.h"
#define BRAINWALLET_DEFAULT_SEED_SIZE 32
#define ACCOUNT_RESTORE_DATA_SIZE BRAINWALLET_DEFAULT_SEED_SIZE
#define BRAINWALLET_DEFAULT_WORDS_COUNT 25

View file

@ -4254,7 +4254,7 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
return false;
}
uint64_t h = get_block_height(bl);
get_block_height(bl);
if(!check_block_timestamp_main(bl))
{

View file

@ -155,6 +155,10 @@
#define WALLET_FILE_SIGNATURE 0x1111012101101011LL //Bender's nightmare
#define WALLET_FILE_MAX_BODY_SIZE 0x88888888L //2GB
#define WALLET_FILE_MAX_KEYS_SIZE 10000 //
#define WALLET_BRAIN_DATE_OFFSET 1543622400
#define WALLET_BRAIN_DATE_QUANTUM 604800 //by last word we encode a number of week since launch of the project,
//which let us to address tools::mnemonic_encoding::NUMWORDS weeks after project launch
//which is about 30 years
#define OFFER_MAXIMUM_LIFE_TIME (60*60*24*30) // 30 days

View file

@ -26,54 +26,18 @@ using namespace epee;
#include "bc_attachments_helpers.h"
#include "genesis.h"
#include "genesis_acc.h"
#include "common/mnemonic-encoding.h"
namespace currency
{
//---------------------------------------------------------------
void get_transaction_prefix_hash(const transaction_prefix& tx, crypto::hash& h)
{
std::ostringstream s;
binary_archive<true> a(s);
::serialization::serialize(a, const_cast<transaction_prefix&>(tx));
std::string data = s.str();
crypto::cn_fast_hash(data.data(), data.size(), h);
}
//---------------------------------------------------------------
crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx)
{
crypto::hash h = null_hash;
get_transaction_prefix_hash(tx, h);
return h;
}
//---------------------------------------------------------------
bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx)
{
std::stringstream ss;
ss << tx_blob;
binary_archive<false> ba(ss);
bool r = ::serialization::serialize(ba, tx);
CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob");
return true;
}
//---------------------------------------------------------------
bool add_tx_extra_alias(transaction& tx, const extra_alias_entry& alinfo)
{
tx.extra.push_back(alinfo);
return true;
}
//---------------------------------------------------------------
bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx, crypto::hash& tx_hash)
{
std::stringstream ss;
ss << tx_blob;
binary_archive<false> ba(ss);
bool r = ::serialization::serialize(ba, tx);
CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob");
//TODO: validate tx
//crypto::cn_fast_hash(tx_blob.data(), tx_blob.size(), tx_hash);
get_transaction_prefix_hash(tx, tx_hash);
return true;
}
//---------------------------------------------------------------
/*
bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins,
@ -1246,6 +1210,24 @@ namespace currency
return reward;
}
//---------------------------------------------------------------
std::string get_word_from_timstamp(uint64_t timestamp)
{
uint64_t date_offset = timestamp ? timestamp - WALLET_BRAIN_DATE_OFFSET : 0;
uint64_t weeks_count = date_offset / WALLET_BRAIN_DATE_QUANTUM;
CHECK_AND_ASSERT_THROW_MES(weeks_count < std::numeric_limits<uint32_t>::max(), "internal error: unable to converto to uint32, val = " << weeks_count);
uint32_t weeks_count_32 = static_cast<uint32_t>(weeks_count);
return tools::mnemonic_encoding::word_by_num(weeks_count_32);
}
//---------------------------------------------------------------
uint64_t get_timstamp_from_word(std::string word)
{
uint64_t count_of_weeks = tools::mnemonic_encoding::num_by_word(word);
uint64_t timestamp = count_of_weeks * WALLET_BRAIN_DATE_QUANTUM + WALLET_BRAIN_DATE_OFFSET;
return timestamp;
}
//---------------------------------------------------------------
bool sign_multisig_input_in_tx(currency::transaction& tx, size_t ms_input_index, const currency::account_keys& keys, const currency::transaction& source_tx, bool *p_is_input_fully_signed /* = nullptr */)
{
#define LOC_CHK(cond, msg) CHECK_AND_ASSERT_MES(cond, false, msg << ", ms input index: " << ms_input_index << ", tx: " << get_transaction_hash(tx) << ", source tx: " << get_transaction_hash(source_tx))
@ -1620,11 +1602,7 @@ namespace currency
att.push_back(tsa);
return true;
}
//---------------------------------------------------------------
void get_blob_hash(const blobdata& blob, crypto::hash& res)
{
cn_fast_hash(blob.data(), blob.size(), res);
}
std::string print_fixed_decimal_point(uint64_t amount, size_t decimal_point)
{
@ -1648,32 +1626,6 @@ namespace currency
return std::to_string(amount) + '.' + r.substr(0, r.find_last_not_of('0') + 1);
}
//---------------------------------------------------------------
crypto::hash get_blob_hash(const blobdata& blob)
{
crypto::hash h = null_hash;
get_blob_hash(blob, h);
return h;
}
//---------------------------------------------------------------
crypto::hash get_transaction_hash(const transaction& t)
{
return get_transaction_prefix_hash(t);
}
//---------------------------------------------------------------
bool get_transaction_hash(const transaction& t, crypto::hash& res)
{
uint64_t blob_size = 0;
return get_object_hash(static_cast<const transaction_prefix&>(t), res, blob_size);
}
//---------------------------------------------------------------
bool get_transaction_hash(const transaction& t, crypto::hash& res, uint64_t& blob_size)
{
blob_size = 0;
bool r = get_object_hash(static_cast<const transaction_prefix&>(t), res, blob_size);
blob_size = get_object_blobsize(t, blob_size);
return r;
}
//---------------------------------------------------------------
/*bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t& blob_size)
{
@ -1926,27 +1878,6 @@ namespace currency
return ss.str();
}
//---------------------------------------------------------------
blobdata get_block_hashing_blob(const block& b)
{
blobdata blob = t_serializable_object_to_blob(static_cast<block_header>(b));
crypto::hash tree_root_hash = get_tx_tree_hash(b);
blob.append((const char*)&tree_root_hash, sizeof(tree_root_hash));
blob.append(tools::get_varint_data(b.tx_hashes.size() + 1));
return blob;
}
//---------------------------------------------------------------
bool get_block_hash(const block& b, crypto::hash& res)
{
return get_object_hash(get_block_hashing_blob(b), res);
}
//---------------------------------------------------------------
crypto::hash get_block_hash(const block& b)
{
crypto::hash p = null_hash;
get_block_hash(b, p);
return p;
}
//---------------------------------------------------------------
bool generate_genesis_block(block& bl)
{
//genesis block
@ -2052,101 +1983,9 @@ namespace currency
//---------------------------------------------------------------
bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b)
{
return t_unserializable_object_from_blob(b, b_blob);
return parse_and_validate_object_from_blob(b_blob, b);
}
//---------------------------------------------------------------
size_t get_object_blobsize(const transaction& t)
{
size_t tx_blob_size = get_object_blobsize(static_cast<const transaction_prefix&>(t));
return get_object_blobsize(t, tx_blob_size);
}
//---------------------------------------------------------------
size_t get_object_blobsize(const transaction& t, uint64_t prefix_blob_size)
{
size_t tx_blob_size = prefix_blob_size;
if (is_coinbase(t))
return tx_blob_size;
// for purged tx, with empty signatures and attachments, this function should return the blob size
// which the tx would have if the signatures and attachments were correctly filled with actual data
// 1. signatures
bool separately_signed_tx = get_tx_flags(t) & TX_FLAG_SIGNATURE_MODE_SEPARATE;
tx_blob_size += tools::get_varint_packed_size(t.vin.size()); // size of transaction::signatures (equals to total inputs count)
for (size_t i = 0; i != t.vin.size(); i++)
{
size_t sig_count = get_input_expected_signatures_count(t.vin[i]);
if (separately_signed_tx && i == t.vin.size() - 1)
++sig_count; // count in one more signature for the last input in a complete separately signed tx
tx_blob_size += tools::get_varint_packed_size(sig_count); // size of transaction::signatures[i]
tx_blob_size += sizeof(crypto::signature) * sig_count; // size of signatures' data itself
}
// 2. attachments (try to find extra_attachment_info in tx prefix and count it in if succeed)
extra_attachment_info eai = AUTO_VAL_INIT(eai);
bool got_eai = false;
if (separately_signed_tx)
{
// for separately-signed tx, try to obtain extra_attachment_info from the last input's etc_details
const std::vector<txin_etc_details_v>* p_etc_details = get_input_etc_details(t.vin.back());
got_eai = p_etc_details != nullptr && get_type_in_variant_container(*p_etc_details, eai);
}
if (!got_eai)
got_eai = get_type_in_variant_container(t.extra, eai); // then from the extra
if (got_eai)
tx_blob_size += eai.sz; // sz is a size of whole serialized attachment blob, including attachments vector size
else
tx_blob_size += tools::get_varint_packed_size(static_cast<size_t>(0)); // no extra_attachment_info found - just add zero vector's size, 'cause it's serialized anyway
return tx_blob_size;
}
//---------------------------------------------------------------
blobdata block_to_blob(const block& b)
{
return t_serializable_object_to_blob(b);
}
//---------------------------------------------------------------
bool block_to_blob(const block& b, blobdata& b_blob)
{
return t_serializable_object_to_blob(b, b_blob);
}
//---------------------------------------------------------------
blobdata tx_to_blob(const transaction& tx)
{
return t_serializable_object_to_blob(tx);
}
//---------------------------------------------------------------
bool tx_to_blob(const transaction& tx, blobdata& b_blob)
{
return t_serializable_object_to_blob(tx, b_blob);
}
//---------------------------------------------------------------
void get_tx_tree_hash(const std::vector<crypto::hash>& tx_hashes, crypto::hash& h)
{
tree_hash(tx_hashes.data(), tx_hashes.size(), h);
}
//---------------------------------------------------------------
crypto::hash get_tx_tree_hash(const std::vector<crypto::hash>& tx_hashes)
{
crypto::hash h = null_hash;
get_tx_tree_hash(tx_hashes, h);
return h;
}
//---------------------------------------------------------------
crypto::hash get_tx_tree_hash(const block& b)
{
std::vector<crypto::hash> txs_ids;
crypto::hash h = null_hash;
get_transaction_hash(b.miner_tx, h);
txs_ids.push_back(h);
BOOST_FOREACH(auto& th, b.tx_hashes)
txs_ids.push_back(th);
return get_tx_tree_hash(txs_ids);
}
//---------------------------------------------------------------
bool is_service_tx(const transaction& tx)
{

View file

@ -11,10 +11,12 @@
#include <unordered_set>
#include <unordered_map>
#include "currency_protocol/currency_protocol_defs.h"
#include "account.h"
#include "include_base_utils.h"
#include "currency_format_utils_abstract.h"
#include "common/crypto_stream_operators.h"
#include "currency_protocol/currency_protocol_defs.h"
#include "crypto/crypto.h"
#include "crypto/hash.h"
#include "difficulty.h"
@ -23,6 +25,8 @@
#include "bc_payments_id_service.h"
#include "bc_attachments_helpers_basic.h"
#include "blockchain_storage_basic.h"
#include "currency_format_utils_blocks.h"
#include "currency_format_utils_transactions.h"
// ------ get_tx_type_definition -------------
#define GUI_TX_TYPE_NORMAL 0
@ -42,31 +46,8 @@
//------
bool parse_hash256(const std::string str_hash, crypto::hash& hash);
template <class T>
std::ostream &print256(std::ostream &o, const T &v) {
return o << "<" << epee::string_tools::pod_to_hex(v) << ">";
}
template <class T>
std::ostream &print16(std::ostream &o, const T &v) {
return o << "<" << epee::string_tools::pod_to_hex(v).substr(0, 5) << "..>";
}
template <class T>
std::string print16(const T &v) {
return std::string("<") + epee::string_tools::pod_to_hex(v).substr(0, 5) + "..>";
}
namespace crypto {
inline std::ostream &operator <<(std::ostream &o, const crypto::public_key &v) { return print256(o, v); }
inline std::ostream &operator <<(std::ostream &o, const crypto::secret_key &v) { return print256(o, v); }
inline std::ostream &operator <<(std::ostream &o, const crypto::key_derivation &v) { return print256(o, v); }
inline std::ostream &operator <<(std::ostream &o, const crypto::key_image &v) { return print256(o, v); }
inline std::ostream &operator <<(std::ostream &o, const crypto::signature &v) { return print256(o, v); }
inline std::ostream &operator <<(std::ostream &o, const crypto::hash &v) { return print256(o, v); }
}
namespace currency
{
@ -182,10 +163,6 @@ namespace currency
//---------------------------------------------------------------
void get_transaction_prefix_hash(const transaction_prefix& tx, crypto::hash& h);
crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx);
bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx, crypto::hash& tx_hash);
bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx);
bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins,
size_t current_block_size,
uint64_t fee,
@ -234,14 +211,6 @@ namespace currency
bool is_tx_expired(const transaction& tx, uint64_t expiration_ts_median);
template<class t_type>
std::string print_t_array(const std::vector<t_type>& vec)
{
std::stringstream ss;
for (auto& v : vec)
ss << v << " ";
return ss.str();
}
uint64_t get_string_uint64_hash(const std::string& str);
bool construct_tx_out(const tx_destination_entry& de, const crypto::secret_key& tx_sec_key, size_t output_index, transaction& tx, std::set<uint16_t>& deriv_cache, uint8_t tx_outs_attr = CURRENCY_TO_KEY_OUT_RELAXED);
@ -302,8 +271,6 @@ namespace currency
bool generate_key_image_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki);
bool derive_public_key_from_target_address(const account_public_address& destination_addr, const crypto::secret_key& tx_sec_key, size_t index, crypto::public_key& out_eph_public_key, crypto::key_derivation& derivation);
bool derive_public_key_from_target_address(const account_public_address& destination_addr, const crypto::secret_key& tx_sec_key, size_t index, crypto::public_key& out_eph_public_key);
void get_blob_hash(const blobdata& blob, crypto::hash& res);
crypto::hash get_blob_hash(const blobdata& blob);
std::string short_hash_str(const crypto::hash& h);
bool is_mixattr_applicable_for_fake_outs_counter(uint8_t mix_attr, uint64_t fake_attr_count);
bool is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t current_blockchain_size, uint64_t current_time);
@ -316,13 +283,7 @@ namespace currency
uint64_t get_reward_from_miner_tx(const transaction& tx);
crypto::hash get_transaction_hash(const transaction& t);
bool get_transaction_hash(const transaction& t, crypto::hash& res);
bool get_transaction_hash(const transaction& t, crypto::hash& res, uint64_t& blob_size);
//bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t& blob_size);
blobdata get_block_hashing_blob(const block& b);
bool get_block_hash(const block& b, crypto::hash& res);
crypto::hash get_block_hash(const block& b);
bool generate_genesis_block(block& bl);
const crypto::hash& get_genesis_hash(bool need_to_set = false, const crypto::hash& h = null_hash);
bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b);
@ -389,18 +350,8 @@ namespace currency
bool fill_tx_rpc_details(tx_rpc_extended_info& tei, const transaction& tx, const transaction_chain_entry* ptce, const crypto::hash& h, uint64_t timestamp, bool is_short = false);
bool fill_block_rpc_details(block_rpc_extended_info& pei_rpc, const block_extended_info& bei_chain, const crypto::hash& h);
void append_per_block_increments_for_tx(const transaction& tx, std::unordered_map<uint64_t, uint32_t>& gindices);
/************************************************************************/
/* */
/************************************************************************/
template<class t_array>
struct array_hasher : std::unary_function<t_array&, std::size_t>
{
std::size_t operator()(const t_array& val) const
{
return boost::hash_range(&val.data[0], &val.data[sizeof(val.data)]);
}
};
std::string get_word_from_timstamp(uint64_t timestamp);
uint64_t get_timstamp_from_word(std::string word);
template<class t_txin_v>
typename std::conditional<std::is_const<t_txin_v>::value, const std::vector<txin_etc_details_v>, std::vector<txin_etc_details_v> >::type& get_txin_etc_options(t_txin_v& in)
@ -450,17 +401,6 @@ namespace currency
bool have_attachment_service_in_container(const std::vector<attachment_v>& av, const std::string& service_id, const std::string& instruction);
crypto::hash prepare_prefix_hash_for_sign(const transaction& tx, uint64_t in_index, const crypto::hash& tx_id);
//------------------------------------------------------------------------------------
template<class t_pod_type, class result_type>
result_type get_pod_checksum(const t_pod_type& bl)
{
const unsigned char* pbuf = reinterpret_cast<const unsigned char*>(&bl);
result_type summ = 0;
for (size_t i = 0; i != sizeof(t_pod_type)-1; i++)
summ += pbuf[i];
return summ;
}
//---------------------------------------------------------------
template<class tx_out_t>
bool is_out_to_acc(const account_keys& acc, const tx_out_t& out_key, const crypto::public_key& tx_pub_key, size_t output_index)
@ -469,83 +409,6 @@ namespace currency
generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation);
return is_out_to_acc(acc, out_key, derivation, output_index);
}
//---------------------------------------------------------------
template<typename specic_type_t, typename variant_t_container>
bool have_type_in_variant_container(const variant_t_container& av)
{
for (auto& ai : av)
{
if (ai.type() == typeid(specic_type_t))
{
return true;
}
}
return false;
}
//---------------------------------------------------------------
template<typename specic_type_t, typename variant_t_container>
size_t count_type_in_variant_container(const variant_t_container& av)
{
size_t result = 0;
for (auto& ai : av)
{
if (ai.type() == typeid(specic_type_t))
++result;
}
return result;
}
//---------------------------------------------------------------
template<typename specic_type_t, typename variant_t_container>
bool get_type_in_variant_container(const variant_t_container& av, specic_type_t& a)
{
for (auto& ai : av)
{
if (ai.type() == typeid(specic_type_t))
{
a = boost::get<specic_type_t>(ai);
return true;
}
}
return false;
}
//---------------------------------------------------------------
template<typename variant_container_t>
bool check_allowed_types_in_variant_container(const variant_container_t& container, const std::unordered_set<std::type_index>& allowed_types, bool elements_must_be_unique = true)
{
for (auto it = container.begin(); it != container.end(); ++it)
{
if (allowed_types.count(std::type_index(it->type())) == 0)
return false;
if (elements_must_be_unique)
{
for (auto jt = it + 1; jt != container.end(); ++jt)
if (it->type().hash_code() == jt->type().hash_code())
return false;
}
}
return true;
}
//---------------------------------------------------------------
template<typename variant_container_t>
bool check_allowed_types_in_variant_container(const variant_container_t& container, const variant_container_t& allowed_types_examples, bool elements_must_be_unique = true)
{
std::unordered_set<std::type_index> allowed_types;
for (auto& el : allowed_types_examples)
if (!allowed_types.insert(std::type_index(el.type())).second)
return false; // invalid allowed_types_examples container
return check_allowed_types_in_variant_container(container, allowed_types, elements_must_be_unique);
}
//---------------------------------------------------------------
template<typename variant_container_t>
std::string stringize_types_in_variant_container(const variant_container_t& container)
{
std::string result;
for (auto it = container.begin(); it != container.end(); ++it)
result = (result + it->type().name()) + (it + 1 != container.end() ? ", " : "");
return result;
}
//----------------------------------------------------------------------------------------------------
template<class t_container>
bool validate_attachment_info(const t_container& container, const std::vector<attachment_v>& attachments, bool allow_no_info_for_non_empty_attachments_container)
@ -692,51 +555,7 @@ namespace currency
return true;
}
//---------------------------------------------------------------
template<class t_object>
bool get_object_hash(const t_object& o, crypto::hash& res)
{
get_blob_hash(t_serializable_object_to_blob(o), res);
return true;
}
//---------------------------------------------------------------
template<class t_object>
crypto::hash get_object_hash(const t_object& o)
{
crypto::hash h;
get_object_hash(o, h);
return h;
}
//---------------------------------------------------------------
template<class t_object>
size_t get_object_blobsize(const t_object& o)
{
blobdata b = t_serializable_object_to_blob(o);
return b.size();
}
//---------------------------------------------------------------
size_t get_object_blobsize(const transaction& t);
size_t get_object_blobsize(const transaction& t, uint64_t prefix_blob_size);
//---------------------------------------------------------------
template<class t_object>
bool get_object_hash(const t_object& o, crypto::hash& res, uint64_t& blob_size)
{
blobdata bl = t_serializable_object_to_blob(o);
blob_size = bl.size();
get_blob_hash(bl, res);
return true;
}
//---------------------------------------------------------------
template <typename T>
std::string obj_to_json_str(const T& obj)
{
std::stringstream ss;
json_archive<true> ar(ss, true);
bool r = ::serialization::serialize(ar, const_cast<T&>(obj));
CHECK_AND_ASSERT_MES(r, "", "obj_to_json_str failed: serialization::serialize returned false");
return ss.str();
}
//---------------------------------------------------------------
// 62387455827 -> 455827 + 7000000 + 80000000 + 300000000 + 2000000000 + 60000000000, where 455827 <= dust_threshold
template<typename chunk_handler_t, typename dust_handler_t>
@ -843,18 +662,6 @@ namespace currency
}
//---------------------------------------------------------------
blobdata block_to_blob(const block& b);
bool block_to_blob(const block& b, blobdata& b_blob);
blobdata tx_to_blob(const transaction& b);
bool tx_to_blob(const transaction& b, blobdata& b_blob);
void get_tx_tree_hash(const std::vector<crypto::hash>& tx_hashes, crypto::hash& h);
crypto::hash get_tx_tree_hash(const std::vector<crypto::hash>& tx_hashes);
crypto::hash get_tx_tree_hash(const block& b);
#define CHECKED_GET_SPECIFIC_VARIANT(variant_var, specific_type, variable_name, fail_return_val) \
CHECK_AND_ASSERT_MES(variant_var.type() == typeid(specific_type), fail_return_val, "wrong variant type: " << variant_var.type().name() << ", expected " << typeid(specific_type).name()); \
specific_type& variable_name = boost::get<specific_type>(variant_var);
struct input_amount_getter : public boost::static_visitor<uint64_t>
{
template<class t_input>
@ -866,7 +673,6 @@ namespace currency
{
return boost::apply_visitor(input_amount_getter(), v);
}
//---------------------------------------------------------------
std::ostream& operator <<(std::ostream& o, const ref_by_id& r);
//---------------------------------------------------------------

View file

@ -0,0 +1,218 @@
// Copyright (c) 2018-2019 Zano Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#pragma once
#include <typeindex>
#include <unordered_set>
#include <unordered_map>
#include <list>
#include <memory>
#include "include_base_utils.h"
#include "serialization/keyvalue_serialization.h"
#include "storages/portable_storage_template_helper.h"
#include "crypto/crypto.h"
#include "crypto/hash.h"
#include "currency_core/currency_basic.h"
#include "currency_protocol/blobdatatype.h"
#include "common/crypto_stream_operators.h"
namespace currency
{
template<typename type_t>
std::string print_kv_structure(const type_t& v)
{
return epee::serialization::store_t_to_json(v);
}
template<class t_type>
std::string print_t_array(const std::vector<t_type>& vec)
{
std::stringstream ss;
for (auto& v : vec)
ss << v << " ";
return ss.str();
}
/************************************************************************/
/* */
/************************************************************************/
template<class t_array>
struct array_hasher : std::unary_function<t_array&, std::size_t>
{
std::size_t operator()(const t_array& val) const
{
return boost::hash_range(&val.data[0], &val.data[sizeof(val.data)]);
}
};
//------------------------------------------------------------------------------------
template<class t_pod_type, class result_type>
result_type get_pod_checksum(const t_pod_type& bl)
{
const unsigned char* pbuf = reinterpret_cast<const unsigned char*>(&bl);
result_type summ = 0;
for (size_t i = 0; i != sizeof(t_pod_type)-1; i++)
summ += pbuf[i];
return summ;
}
template<typename object_t>
bool parse_and_validate_object_from_blob(const blobdata& b_blob, object_t& b)
{
return t_unserializable_object_from_blob(b, b_blob);
}
//---------------------------------------------------------------
template<typename specic_type_t, typename variant_t_container>
bool have_type_in_variant_container(const variant_t_container& av)
{
for (auto& ai : av)
{
if (ai.type() == typeid(specic_type_t))
{
return true;
}
}
return false;
}
//---------------------------------------------------------------
template<typename specic_type_t, typename variant_t_container>
size_t count_type_in_variant_container(const variant_t_container& av)
{
size_t result = 0;
for (auto& ai : av)
{
if (ai.type() == typeid(specic_type_t))
++result;
}
return result;
}
//---------------------------------------------------------------
template<typename specic_type_t, typename variant_t_container>
bool get_type_in_variant_container(const variant_t_container& av, specic_type_t& a)
{
for (auto& ai : av)
{
if (ai.type() == typeid(specic_type_t))
{
a = boost::get<specic_type_t>(ai);
return true;
}
}
return false;
}
//---------------------------------------------------------------
template<typename variant_container_t>
bool check_allowed_types_in_variant_container(const variant_container_t& container, const std::unordered_set<std::type_index>& allowed_types, bool elements_must_be_unique = true)
{
for (auto it = container.begin(); it != container.end(); ++it)
{
if (allowed_types.count(std::type_index(it->type())) == 0)
return false;
if (elements_must_be_unique)
{
for (auto jt = it + 1; jt != container.end(); ++jt)
if (it->type().hash_code() == jt->type().hash_code())
return false;
}
}
return true;
}
//---------------------------------------------------------------
template<typename variant_container_t>
bool check_allowed_types_in_variant_container(const variant_container_t& container, const variant_container_t& allowed_types_examples, bool elements_must_be_unique = true)
{
std::unordered_set<std::type_index> allowed_types;
for (auto& el : allowed_types_examples)
if (!allowed_types.insert(std::type_index(el.type())).second)
return false; // invalid allowed_types_examples container
return check_allowed_types_in_variant_container(container, allowed_types, elements_must_be_unique);
}
//---------------------------------------------------------------
template<typename variant_container_t>
std::string stringize_types_in_variant_container(const variant_container_t& container)
{
std::string result;
for (auto it = container.begin(); it != container.end(); ++it)
result = (result + it->type().name()) + (it + 1 != container.end() ? ", " : "");
return result;
}
//---------------------------------------------------------------
inline
void get_blob_hash(const blobdata& blob, crypto::hash& res)
{
cn_fast_hash(blob.data(), blob.size(), res);
}
//---------------------------------------------------------------
inline
crypto::hash get_blob_hash(const blobdata& blob)
{
crypto::hash h = null_hash;
get_blob_hash(blob, h);
return h;
}
template<class t_object>
bool get_object_hash(const t_object& o, crypto::hash& res)
{
get_blob_hash(t_serializable_object_to_blob(o), res);
return true;
}
//---------------------------------------------------------------
template<class t_object>
crypto::hash get_object_hash(const t_object& o)
{
crypto::hash h;
get_object_hash(o, h);
return h;
}
//---------------------------------------------------------------
template<class t_object>
size_t get_object_blobsize(const t_object& o)
{
blobdata b = t_serializable_object_to_blob(o);
return b.size();
}
//---------------------------------------------------------------
template<class t_object>
bool get_object_hash(const t_object& o, crypto::hash& res, uint64_t& blob_size)
{
blobdata bl = t_serializable_object_to_blob(o);
blob_size = bl.size();
get_blob_hash(bl, res);
return true;
}
//---------------------------------------------------------------
template <typename T>
std::string obj_to_json_str(const T& obj)
{
std::stringstream ss;
json_archive<true> ar(ss, true);
bool r = ::serialization::serialize(ar, const_cast<T&>(obj));
CHECK_AND_ASSERT_MES(r, "", "obj_to_json_str failed: serialization::serialize returned false");
return ss.str();
}
//---------------------------------------------------------------
//---------------------------------------------------------------
size_t get_object_blobsize(const transaction& t);
size_t get_object_blobsize(const transaction& t, uint64_t prefix_blob_size);
#define CHECKED_GET_SPECIFIC_VARIANT(variant_var, specific_type, variable_name, fail_return_val) \
CHECK_AND_ASSERT_MES(variant_var.type() == typeid(specific_type), fail_return_val, "wrong variant type: " << variant_var.type().name() << ", expected " << typeid(specific_type).name()); \
specific_type& variable_name = boost::get<specific_type>(variant_var);
} // namespace currency

View file

@ -0,0 +1,67 @@
// Copyright (c) 2018-2019 Zano Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "currency_format_utils_blocks.h"
#include "serialization/serialization.h"
#include "currency_format_utils_abstract.h"
#include "currency_format_utils_transactions.h"
namespace currency
{
//---------------------------------------------------------------
blobdata get_block_hashing_blob(const block& b)
{
blobdata blob = t_serializable_object_to_blob(static_cast<block_header>(b));
crypto::hash tree_root_hash = get_tx_tree_hash(b);
blob.append((const char*)&tree_root_hash, sizeof(tree_root_hash));
blob.append(tools::get_varint_data(b.tx_hashes.size() + 1));
return blob;
}
//---------------------------------------------------------------
bool get_block_hash(const block& b, crypto::hash& res)
{
return get_object_hash(get_block_hashing_blob(b), res);
}
//---------------------------------------------------------------
crypto::hash get_block_hash(const block& b)
{
crypto::hash p = null_hash;
get_block_hash(b, p);
return p;
}
//---------------------------------------------------------------
blobdata block_to_blob(const block& b)
{
return t_serializable_object_to_blob(b);
}
//---------------------------------------------------------------
bool block_to_blob(const block& b, blobdata& b_blob)
{
return t_serializable_object_to_blob(b, b_blob);
}
//---------------------------------------------------------------
void get_tx_tree_hash(const std::vector<crypto::hash>& tx_hashes, crypto::hash& h)
{
tree_hash(tx_hashes.data(), tx_hashes.size(), h);
}
//---------------------------------------------------------------
crypto::hash get_tx_tree_hash(const std::vector<crypto::hash>& tx_hashes)
{
crypto::hash h = null_hash;
get_tx_tree_hash(tx_hashes, h);
return h;
}
//---------------------------------------------------------------
crypto::hash get_tx_tree_hash(const block& b)
{
std::vector<crypto::hash> txs_ids;
crypto::hash h = null_hash;
get_transaction_hash(b.miner_tx, h);
txs_ids.push_back(h);
BOOST_FOREACH(auto& th, b.tx_hashes)
txs_ids.push_back(th);
return get_tx_tree_hash(txs_ids);
}
}

View file

@ -0,0 +1,25 @@
// Copyright (c) 2018-2019 Zano Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#pragma once
#include "include_base_utils.h"
#include "crypto/crypto.h"
#include "currency_core/currency_basic.h"
#include "currency_protocol/blobdatatype.h"
namespace currency
{
blobdata get_block_hashing_blob(const block& b);
bool get_block_hash(const block& b, crypto::hash& res);
crypto::hash get_block_hash(const block& b);
blobdata block_to_blob(const block& b);
bool block_to_blob(const block& b, blobdata& b_blob);
void get_tx_tree_hash(const std::vector<crypto::hash>& tx_hashes, crypto::hash& h);
crypto::hash get_tx_tree_hash(const std::vector<crypto::hash>& tx_hashes);
crypto::hash get_tx_tree_hash(const block& b);
}

View file

@ -0,0 +1,134 @@
// Copyright (c) 2018-2019 Zano Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "currency_format_utils_transactions.h"
#include "serialization/serialization.h"
#include "currency_format_utils_abstract.h"
#include "currency_format_utils.h"
namespace currency
{
//---------------------------------------------------------------
void get_transaction_prefix_hash(const transaction_prefix& tx, crypto::hash& h)
{
std::ostringstream s;
binary_archive<true> a(s);
::serialization::serialize(a, const_cast<transaction_prefix&>(tx));
std::string data = s.str();
crypto::cn_fast_hash(data.data(), data.size(), h);
}
//---------------------------------------------------------------
crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx)
{
crypto::hash h = null_hash;
get_transaction_prefix_hash(tx, h);
return h;
}
//---------------------------------------------------------------
bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx)
{
std::stringstream ss;
ss << tx_blob;
binary_archive<false> ba(ss);
bool r = ::serialization::serialize(ba, tx);
CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob");
return true;
}
//---------------------------------------------------------------
bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx, crypto::hash& tx_hash)
{
std::stringstream ss;
ss << tx_blob;
binary_archive<false> ba(ss);
bool r = ::serialization::serialize(ba, tx);
CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob");
//TODO: validate tx
//crypto::cn_fast_hash(tx_blob.data(), tx_blob.size(), tx_hash);
get_transaction_prefix_hash(tx, tx_hash);
return true;
}
//---------------------------------------------------------------
crypto::hash get_transaction_hash(const transaction& t)
{
return get_transaction_prefix_hash(t);
}
//---------------------------------------------------------------
bool get_transaction_hash(const transaction& t, crypto::hash& res)
{
uint64_t blob_size = 0;
return get_object_hash(static_cast<const transaction_prefix&>(t), res, blob_size);
}
//---------------------------------------------------------------
bool get_transaction_hash(const transaction& t, crypto::hash& res, uint64_t& blob_size)
{
blob_size = 0;
bool r = get_object_hash(static_cast<const transaction_prefix&>(t), res, blob_size);
blob_size = get_object_blobsize(t, blob_size);
return r;
}
//---------------------------------------------------------------
size_t get_object_blobsize(const transaction& t)
{
size_t tx_blob_size = get_object_blobsize(static_cast<const transaction_prefix&>(t));
return get_object_blobsize(t, tx_blob_size);
}
//---------------------------------------------------------------
size_t get_object_blobsize(const transaction& t, uint64_t prefix_blob_size)
{
size_t tx_blob_size = prefix_blob_size;
if (is_coinbase(t))
return tx_blob_size;
// for purged tx, with empty signatures and attachments, this function should return the blob size
// which the tx would have if the signatures and attachments were correctly filled with actual data
// 1. signatures
bool separately_signed_tx = get_tx_flags(t) & TX_FLAG_SIGNATURE_MODE_SEPARATE;
tx_blob_size += tools::get_varint_packed_size(t.vin.size()); // size of transaction::signatures (equals to total inputs count)
for (size_t i = 0; i != t.vin.size(); i++)
{
size_t sig_count = get_input_expected_signatures_count(t.vin[i]);
if (separately_signed_tx && i == t.vin.size() - 1)
++sig_count; // count in one more signature for the last input in a complete separately signed tx
tx_blob_size += tools::get_varint_packed_size(sig_count); // size of transaction::signatures[i]
tx_blob_size += sizeof(crypto::signature) * sig_count; // size of signatures' data itself
}
// 2. attachments (try to find extra_attachment_info in tx prefix and count it in if succeed)
extra_attachment_info eai = AUTO_VAL_INIT(eai);
bool got_eai = false;
if (separately_signed_tx)
{
// for separately-signed tx, try to obtain extra_attachment_info from the last input's etc_details
const std::vector<txin_etc_details_v>* p_etc_details = get_input_etc_details(t.vin.back());
got_eai = p_etc_details != nullptr && get_type_in_variant_container(*p_etc_details, eai);
}
if (!got_eai)
got_eai = get_type_in_variant_container(t.extra, eai); // then from the extra
if (got_eai)
tx_blob_size += eai.sz; // sz is a size of whole serialized attachment blob, including attachments vector size
else
tx_blob_size += tools::get_varint_packed_size(static_cast<size_t>(0)); // no extra_attachment_info found - just add zero vector's size, 'cause it's serialized anyway
return tx_blob_size;
}
//---------------------------------------------------------------
blobdata tx_to_blob(const transaction& tx)
{
return t_serializable_object_to_blob(tx);
}
//---------------------------------------------------------------
bool tx_to_blob(const transaction& tx, blobdata& b_blob)
{
return t_serializable_object_to_blob(tx, b_blob);
}
}

View file

@ -0,0 +1,27 @@
// Copyright (c) 2018-2019 Zano Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#pragma once
#include "include_base_utils.h"
#include "crypto/crypto.h"
#include "currency_core/currency_basic.h"
#include "currency_protocol/blobdatatype.h"
namespace currency
{
void get_transaction_prefix_hash(const transaction_prefix& tx, crypto::hash& h);
crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx);
bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx, crypto::hash& tx_hash);
bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx);
crypto::hash get_transaction_hash(const transaction& t);
bool get_transaction_hash(const transaction& t, crypto::hash& res);
bool get_transaction_hash(const transaction& t, crypto::hash& res, uint64_t& blob_size);
size_t get_object_blobsize(const transaction& t);
size_t get_object_blobsize(const transaction& t, uint64_t prefix_blob_size);
blobdata tx_to_blob(const transaction& b);
bool tx_to_blob(const transaction& b, blobdata& b_blob);
}

View file

@ -184,7 +184,9 @@ namespace currency {
return (low + time_span - 1) / time_span;
}
wide_difficulty_type next_difficulty(vector<uint64_t> timestamps, vector<wide_difficulty_type> cumulative_difficulties, size_t target_seconds) {
wide_difficulty_type next_difficulty(vector<uint64_t>& timestamps, vector<wide_difficulty_type>& cumulative_difficulties, size_t target_seconds)
{
// timestamps - first is latest, back - is oldest timestamps
//cutoff DIFFICULTY_LAG
if(timestamps.size() > DIFFICULTY_WINDOW)
{
@ -210,7 +212,7 @@ namespace currency {
cut_begin = (length - (DIFFICULTY_WINDOW - 2 * DIFFICULTY_CUT) + 1) / 2;
cut_end = cut_begin + (DIFFICULTY_WINDOW - 2 * DIFFICULTY_CUT);
}
assert(/*cut_begin >= 0 &&*/ cut_begin + 2 <= cut_end && cut_end <= length);
CHECK_AND_ASSERT_THROW_MES(/*cut_begin >= 0 &&*/ cut_begin + 2 <= cut_end && cut_end <= length, "validation in next_difficulty is failed");
uint64_t time_span = timestamps[cut_begin] - timestamps[cut_end - 1];
if (time_span == 0) {
time_span = 1;

View file

@ -23,7 +23,7 @@ namespace currency
difficulty_type next_difficulty_old(std::vector<std::uint64_t> timestamps, std::vector<difficulty_type> cumulative_difficulties, size_t target_seconds);
bool check_hash(const crypto::hash &hash, wide_difficulty_type difficulty);
wide_difficulty_type next_difficulty(std::vector<std::uint64_t> timestamps, std::vector<wide_difficulty_type> cumulative_difficulties, size_t target_seconds);
wide_difficulty_type next_difficulty(std::vector<std::uint64_t>& timestamps, std::vector<wide_difficulty_type>& cumulative_difficulties, size_t target_seconds);
uint64_t difficulty_to_boundary(wide_difficulty_type difficulty);
void difficulty_to_boundary_long(wide_difficulty_type difficulty, crypto::hash& result);
}

View file

@ -104,7 +104,7 @@ namespace currency
{
std::list<blobdata> txs;
std::list<block_complete_entry> blocks;
std::list<crypto::hash> missed_ids;
std::list<crypto::hash> missed_ids;
uint64_t current_blockchain_height;
BEGIN_KV_SERIALIZE_MAP()
@ -168,3 +168,5 @@ namespace currency
};
}
#include "currency_protocol_defs_print.h"

View file

@ -0,0 +1,89 @@
// Copyright (c) 2014-2018 Zano Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#pragma once
#include "currency_core/currency_format_utils_abstract.h"
#include "currency_core/currency_format_utils_blocks.h"
#include "storages/portable_storage_to_json.h"
namespace currency
{
//------------------------------------------------------------------------------------------------------------------------
inline std::string print_complete_block_entry_list(const std::list<block_complete_entry>& blocks)
{
std::stringstream ss;
size_t i = 0;
for (const block_complete_entry& block_entry : blocks)
{
ss << "[" << i << "]";
block b = AUTO_VAL_INIT(b);
if (!parse_and_validate_object_from_blob(block_entry.block, b))
{
ss << "UNPARSED" << ENDL;
}
ss << get_block_hash(b) << "{....parent: " << b.prev_id << "....}" << ENDL;
i++;
}
return ss.str();
}
template<typename container_t>
std::string print_container_of_hashs(const container_t& cont, size_t indent)
{
std::stringstream ss;
std::string indent_str(indent, ' ');
for (const auto& h : cont)
{
ss << indent_str << h << ENDL;
}
return ss.str();
}
inline
std::string print_kv_structure(const NOTIFY_REQUEST_GET_OBJECTS::request& v)
{
std::stringstream ss;
ss << "blocks: {" << ENDL << print_container_of_hashs(v.blocks, 2) << ENDL << "}";
ss << "txs: {" << ENDL << print_container_of_hashs(v.txs, 2) << ENDL << "}";
return ss.str();
}
inline
std::string print_kv_structure(const NOTIFY_RESPONSE_GET_OBJECTS::request& v)
{
std::stringstream ss;
ss << "\"blocks\":{" << ENDL << print_complete_block_entry_list(v.blocks) << ENDL << "}, " << ENDL;
ss << "\"missed_ids\":" << ENDL;
::epee::serialization::dump_as_json(ss, v.missed_ids, 2);
ss << ENDL << "\"current_blockchain_height\":" << v.current_blockchain_height;
return ss.str();
}
inline
std::string print_kv_structure(const NOTIFY_REQUEST_CHAIN::request& v)
{
std::stringstream ss;
size_t i = 0;
ss << "block_ids: {" << ENDL << print_container_of_hashs(v.block_ids, 2) << ENDL << "}";
return ss.str();
}
inline
std::string print_kv_structure(const NOTIFY_RESPONSE_CHAIN_ENTRY::request& v)
{
std::stringstream ss;
ss << "start_height:" << v.start_height << ENDL;
ss << "total_height:" << v.total_height << ENDL;
ss << "block_ids: {" << ENDL;
for (const block_context_info& bei : v.m_block_ids)
{
ss << bei.h << ":" << bei.cumul_size << ENDL;
}
ss << "}";
return ss.str();
}
}

View file

@ -1,4 +1,4 @@
// Copyright (c) 2014-2018 Zano Project
// Copyright (c) 2014-2018 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Copyright (c) 2012-2013 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
@ -70,6 +70,8 @@ namespace currency
{
NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized<NOTIFY_REQUEST_CHAIN::request>();
m_core.get_short_chain_history(r.block_ids);
LOG_PRINT_L2("[NOTIFY]NOTIFY_REQUEST_CHAIN(on_callback): m_block_ids.size()=" << r.block_ids.size());
LOG_PRINT_L3("[NOTIFY]NOTIFY_REQUEST_CHAIN(on_callback): " << ENDL << print_kv_structure(r));
post_notify<NOTIFY_REQUEST_CHAIN>(r, context);
}
@ -308,7 +310,8 @@ namespace currency
NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized<NOTIFY_REQUEST_CHAIN::request>();
m_core.get_short_chain_history(r.block_ids);
LOG_PRINT_MAGENTA("State changed to state_synchronizing.", LOG_LEVEL_2);
LOG_PRINT_L2("[REQUEST]NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size() );
LOG_PRINT_L2("[NOTIFY]NOTIFY_REQUEST_CHAIN(on_orphaned): m_block_ids.size()=" << r.block_ids.size() );
LOG_PRINT_L3("[NOTIFY]NOTIFY_REQUEST_CHAIN(on_orphaned): " << ENDL << print_kv_structure(r));
post_notify<NOTIFY_REQUEST_CHAIN>(r, context);
}
@ -355,6 +358,9 @@ namespace currency
template<class t_core>
int t_currency_protocol_handler<t_core>::handle_request_get_objects(int command, NOTIFY_REQUEST_GET_OBJECTS::request& arg, currency_connection_context& context)
{
LOG_PRINT_L2("[HANDLE]NOTIFY_REQUEST_GET_OBJECTS: arg.blocks.size() = " << arg.blocks.size() << ", arg.txs.size()="<< arg.txs.size());
LOG_PRINT_L3("[HANDLE]NOTIFY_REQUEST_GET_OBJECTS: " << ENDL << currency::print_kv_structure(arg));
if (arg.blocks.size() > CURRENCY_PROTOCOL_MAX_BLOCKS_REQUEST_COUNT)
{
LOG_ERROR_CCONTEXT("Requested objects count is to big (" << arg.blocks.size() <<")expected not more then " << CURRENCY_PROTOCOL_MAX_BLOCKS_REQUEST_COUNT);
@ -367,8 +373,12 @@ namespace currency
LOG_ERROR_CCONTEXT("failed to handle request NOTIFY_REQUEST_GET_OBJECTS, dropping connection");
m_p2p->drop_connection(context);
}
LOG_PRINT_L2("[HANDLE]NOTIFY_RESPONSE_GET_OBJECTS: blocks.size()=" << rsp.blocks.size() << ", txs.size()=" << rsp.txs.size()
LOG_PRINT_L2("[NOTIFY]NOTIFY_RESPONSE_GET_OBJECTS: blocks.size()=" << rsp.blocks.size() << ", txs.size()=" << rsp.txs.size()
<< ", rsp.m_current_blockchain_height=" << rsp.current_blockchain_height << ", missed_ids.size()=" << rsp.missed_ids.size());
LOG_PRINT_L3("[NOTIFY]NOTIFY_RESPONSE_GET_OBJECTS: " << ENDL << currency::print_kv_structure(rsp));
post_notify<NOTIFY_RESPONSE_GET_OBJECTS>(rsp, context);
return 1;
}
@ -383,12 +393,17 @@ namespace currency
}
return false;
}
#define CHECK_STOP_FLAG__DROP_AND_RETURN_IF_SET(ret_v, msg) if (check_stop_flag_and_drop_cc(context)) { LOG_PRINT_YELLOW("Stop flag detected within NOTIFY_RESPONSE_GET_OBJECTS. " << msg, LOG_LEVEL_0); return ret_v; }
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
int t_currency_protocol_handler<t_core>::handle_response_get_objects(int command, NOTIFY_RESPONSE_GET_OBJECTS::request& arg, currency_connection_context& context)
{
LOG_PRINT_L2("NOTIFY_RESPONSE_GET_OBJECTS");
LOG_PRINT_L2("[HANDLE]NOTIFY_RESPONSE_GET_OBJECTS: arg.blocks.size()=" << arg.blocks.size() << ", arg.missed_ids.size()=" << arg.missed_ids.size() << ", arg.txs.size()=" << arg.txs.size());
LOG_PRINT_L3("[HANDLE]NOTIFY_RESPONSE_GET_OBJECTS: " << ENDL << currency::print_kv_structure(arg));
if(context.m_last_response_height > arg.current_blockchain_height)
{
LOG_ERROR_CCONTEXT("sent wrong NOTIFY_HAVE_OBJECTS: arg.m_current_blockchain_height=" << arg.current_blockchain_height
@ -420,7 +435,7 @@ namespace currency
total_blocks_parsing_time += block_parsing_time;
//to avoid concurrency in core between connections, suspend connections which delivered block later then first one
if(count = 2)
if(count == 2)
{
if(m_core.have_block(get_block_hash(b)))
{
@ -511,7 +526,8 @@ namespace currency
}
if(bvc.m_marked_as_orphaned)
{
LOG_PRINT_L0("Block received at sync phase was marked as orphaned, dropping connection");
LOG_PRINT_L0("Block received at sync phase was marked as orphaned, dropping connection, details on response: " << ENDL << print_kv_structure(arg));
m_p2p->drop_connection(context);
m_p2p->add_ip_fail(context.m_remote_ip);
return 1;
@ -565,13 +581,16 @@ namespace currency
template<class t_core>
int t_currency_protocol_handler<t_core>::handle_request_chain(int command, NOTIFY_REQUEST_CHAIN::request& arg, currency_connection_context& context)
{
LOG_PRINT_L2("[HANDLE]NOTIFY_REQUEST_CHAIN: block_ids.size()=" << arg.block_ids.size());
LOG_PRINT_L3("[HANDLE]NOTIFY_REQUEST_CHAIN: " << print_kv_structure(arg));
NOTIFY_RESPONSE_CHAIN_ENTRY::request r;
if(!m_core.find_blockchain_supplement(arg.block_ids, r))
{
LOG_ERROR_CCONTEXT("Failed to handle NOTIFY_REQUEST_CHAIN.");
return 1;
}
LOG_PRINT_L2("[HANDLE]NOTIFY_RESPONSE_CHAIN_ENTRY: m_start_height=" << r.start_height << ", m_total_height=" << r.total_height << ", m_block_ids.size()=" << r.m_block_ids.size());
LOG_PRINT_L2("[NOTIFY]NOTIFY_RESPONSE_CHAIN_ENTRY: m_start_height=" << r.start_height << ", m_total_height=" << r.total_height << ", m_block_ids.size()=" << r.m_block_ids.size());
LOG_PRINT_L3("[NOTIFY]NOTIFY_RESPONSE_CHAIN_ENTRY: " << print_kv_structure(r));
post_notify<NOTIFY_RESPONSE_CHAIN_ENTRY>(r, context);
return 1;
}
@ -599,14 +618,16 @@ namespace currency
context.m_priv.m_needed_objects.erase(it++);
}
LOG_PRINT_L2("[REQUESTING]NOTIFY_REQUEST_GET_OBJECTS: requested_cumulative_size=" << requested_cumulative_size << ", blocks.size()=" << req.blocks.size() << ", txs.size()=" << req.txs.size());
LOG_PRINT_L2("[NOTIFY]NOTIFY_REQUEST_GET_OBJECTS(req_missing): requested_cumulative_size=" << requested_cumulative_size << ", blocks.size()=" << req.blocks.size() << ", txs.size()=" << req.txs.size());
LOG_PRINT_L3("[NOTIFY]NOTIFY_REQUEST_GET_OBJECTS(req_missing): " << ENDL << currency::print_kv_structure(req));
post_notify<NOTIFY_REQUEST_GET_OBJECTS>(req, context);
}else if(context.m_last_response_height < context.m_remote_blockchain_height-1)
{//we have to fetch more objects ids, request blockchain entry
NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized<NOTIFY_REQUEST_CHAIN::request>();
m_core.get_short_chain_history(r.block_ids);
LOG_PRINT_L2("[REQUESTING]NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size() );
LOG_PRINT_L2("[NOTIFY]NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size() );
LOG_PRINT_L3("[NOTIFY]NOTIFY_REQUEST_CHAIN: " << ENDL << print_kv_structure(r) );
post_notify<NOTIFY_REQUEST_CHAIN>(r, context);
}else
{
@ -620,7 +641,7 @@ namespace currency
<< "\r\non connection [" << net_utils::print_connection_context_short(context)<< "]");
context.m_state = currency_connection_context::state_normal;
LOG_PRINT_GREEN("[HANDLE]NOTIFY_REQUEST_GET_OBJECTS: SYNCHRONIZED OK", LOG_LEVEL_0);
LOG_PRINT_GREEN("[REQUEST_MISSING_OBJECTS]: SYNCHRONIZED OK", LOG_LEVEL_0);
on_connection_synchronized();
}
return true;
@ -725,8 +746,9 @@ namespace currency
template<class t_core>
int t_currency_protocol_handler<t_core>::handle_response_chain_entry(int command, NOTIFY_RESPONSE_CHAIN_ENTRY::request& arg, currency_connection_context& context)
{
LOG_PRINT_L2("NOTIFY_RESPONSE_CHAIN_ENTRY: m_block_ids.size()=" << arg.m_block_ids.size()
LOG_PRINT_L2("[HANDLE]NOTIFY_RESPONSE_CHAIN_ENTRY: m_block_ids.size()=" << arg.m_block_ids.size()
<< ", m_start_height=" << arg.start_height << ", m_total_height=" << arg.total_height);
LOG_PRINT_L3("[HANDLE]NOTIFY_RESPONSE_CHAIN_ENTRY: " << ENDL << currency::print_kv_structure(arg));
if(!arg.m_block_ids.size())
{

View file

@ -21,6 +21,9 @@
"OPEN_WALLET": "Open existing wallet",
"RESTORE_WALLET": "Restore from backup",
"WALLET_DETAILS": "Wallet details",
"ASSIGN_ALIAS": "Assign alias",
"EDIT_ALIAS": "Edit alias",
"TRANSFER_ALIAS": "Transfer alias",
"CONTRACTS": "Contracts",
"NEW_PURCHASE": "New purchase",
"OLD_PURCHASE": "Purchase"
@ -148,6 +151,68 @@
"NAME_DUPLICATE": "Name is duplicate."
}
},
"ASSIGN_ALIAS": {
"NAME": {
"LABEL": "Unique name",
"PLACEHOLDER": "@ Enter alias",
"TOOLTIP": "An alias is a shortened form or your account. An alias can only include Latin letters, numbers and characters “.” and “-”. It must start with “@”."
},
"COMMENT": {
"LABEL": "Comment",
"PLACEHOLDER": "Enter comment",
"TOOLTIP": "The comment will be visible to anyone who wants to make a payment to your alias. You can provide details about your business, contacts, or include any text. Comments can be edited later."
},
"COST": "Cost to create alias {{value}} {{currency}}",
"BUTTON_ASSIGN": "Assign",
"BUTTON_CANCEL": "Cancel",
"FORM_ERRORS": {
"NAME_REQUIRED": "Name is required.",
"NAME_WRONG": "Alias has wrong name.",
"NAME_LENGTH": "The alias must be 6-25 characters long.",
"NAME_EXISTS": "Alias name already exists."
},
"ONE_ALIAS": "You can create only one alias per wallet",
"REQUEST_ADD_REG": "The alias will be assigned within 10 minutes"
},
"EDIT_ALIAS": {
"NAME": {
"LABEL": "Unique name",
"PLACEHOLDER": "@ Enter alias"
},
"COMMENT": {
"LABEL": "Comment",
"PLACEHOLDER": "Enter comment"
},
"FORM_ERRORS": {
"NO_MONEY": "You do not have enough funds to change the comment to this alias"
},
"COST": "Cost to edit alias {{value}} {{currency}}",
"BUTTON_EDIT": "Edit",
"BUTTON_CANCEL": "Cancel"
},
"TRANSFER_ALIAS": {
"NAME": {
"LABEL": "Unique name",
"PLACEHOLDER": "@ Enter alias"
},
"COMMENT": {
"LABEL": "Comment",
"PLACEHOLDER": "Enter comment"
},
"ADDRESS": {
"LABEL": "The account to which the alias will be transferred",
"PLACEHOLDER": "Enter account number"
},
"FORM_ERRORS": {
"WRONG_ADDRESS": "No wallet with this account exists",
"ALIAS_EXISTS": "This account already has an alias",
"NO_MONEY": "You do not have enough funds to transfer this alias"
},
"COST": "Cost to transfer alias {{value}} {{currency}}",
"BUTTON_TRANSFER": "Transfer",
"BUTTON_CANCEL": "Cancel",
"REQUEST_SEND_REG": "The alias will be transferred within 10 minutes"
},
"SEND": {
"ADDRESS": "Address",
"AMOUNT": "Amount",
@ -190,6 +255,8 @@
"UNDEFINED": "Undefined",
"COMPLETE_BUYER": "Successfully complete contract, return remaining pledge",
"COMPLETE_SELLER": "Successfully complete contract, receive payment on contract, and return pledge",
"CREATE_ALIAS": "Fee for assigning alias",
"UPDATE_ALIAS": "Fee for editing alias",
"MINED": "Mined funds",
"CREATE_CONTRACT": "Send contract offer",
"PLEDGE_CONTRACT": "Make pledge on offer",
@ -370,7 +437,14 @@
"FILE_RESTORED": "The wallet file was corrupted. We have recovered the keys and the wallet from the blockchain",
"FILE_NOT_FOUND": "File not found",
"FILE_EXIST": "A file with that name already exists. Enter another name to save the file under",
"FILE_NOT_SAVED": "You cannot save a wallet file in this folder. Please choose another folder."
"FILE_NOT_SAVED": "You cannot save a wallet file in this folder. Please choose another folder.",
"TX_TYPE_NORMAL": "Error. The payment from the wallet",
"TX_TYPE_NORMAL_TO": "to",
"TX_TYPE_NORMAL_END": "was not completed.",
"TX_TYPE_NEW_ALIAS": "Error. Failed to register alias to safe",
"TX_TYPE_NEW_ALIAS_END": "Please try again.",
"TX_TYPE_UPDATE_ALIAS": "Error. Failed to change comment to alias in safe",
"TX_TYPE_COIN_BASE": "Error. The payment was not completed."
},
"CONTEXT_MENU": {
"COPY": "copy",

View file

@ -141,8 +141,8 @@ button {
width: 100%;
min-width: 100%;
height: 100%;
min-height: 7rem;
max-height: 7rem;
min-height: 7.5rem;
max-height: 7.5rem;
overflow: hidden;
resize: none;
@ -157,6 +157,7 @@ button {
font-size: 1rem;
line-height: 1.4rem;
align-self: flex-end;
text-align: right;
@include themify($themes) {
color: themed(redTextColor);

View file

@ -1,4 +1,4 @@
app-main, app-create-wallet, app-open-wallet, app-restore-wallet, app-seed-phrase, app-wallet-details, app-settings, app-login {
app-main, app-create-wallet, app-open-wallet, app-restore-wallet, app-seed-phrase, app-wallet-details, app-assign-alias, app-edit-alias, app-transfer-alias, app-settings, app-login {
flex: 1 1 auto;
padding: 3rem;
min-width: 85rem;

View file

@ -19,6 +19,16 @@ app-wallet {
}
}
}
.alias {
.icon {
@include themify($themes) {
background-color: themed(blueTextColor);
}
}
}
}
.address {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,10 +1,11 @@
import {Directive, Input, ElementRef, HostListener, Renderer2, HostBinding} from '@angular/core';
import {Directive, Input, ElementRef, HostListener, Renderer2, HostBinding, OnDestroy} from '@angular/core';
import {ActivatedRoute} from '@angular/router';
@Directive({
selector: '[tooltip]'
})
export class TooltipDirective {
export class TooltipDirective implements OnDestroy {
@HostBinding('style.cursor') cursor = 'pointer';
@ -16,8 +17,9 @@ export class TooltipDirective {
tooltip: HTMLElement;
removeTooltipTimeout;
removeTooltipTimeoutInner;
constructor(private el: ElementRef, private renderer: Renderer2) {
constructor(private el: ElementRef, private renderer: Renderer2, private route: ActivatedRoute) {
}
@HostListener('mouseenter') onMouseEnter() {
@ -40,9 +42,9 @@ export class TooltipDirective {
}
hide() {
this.removeTooltipTimeout = setTimeout( () => {
this.removeTooltipTimeout = setTimeout(() => {
this.renderer.setStyle(this.tooltip, 'opacity', '0');
window.setTimeout(() => {
this.removeTooltipTimeoutInner = setTimeout(() => {
this.renderer.removeChild(document.body, this.tooltip);
this.tooltip = null;
}, this.delay);
@ -51,6 +53,7 @@ export class TooltipDirective {
cancelHide() {
clearTimeout(this.removeTooltipTimeout);
clearTimeout(this.removeTooltipTimeoutInner);
this.renderer.setStyle(this.tooltip, 'opacity', '1');
}
@ -62,10 +65,23 @@ export class TooltipDirective {
this.tooltip = this.tooltipInner;
}
this.renderer.appendChild(document.body, this.tooltip);
this.tooltip.addEventListener('mouseenter', () => {
this.cancelHide();
});
this.tooltip.addEventListener('mouseleave', () => {
if (this.tooltip) {
this.hide();
}
});
this.renderer.setStyle(document.body, 'position', 'relative');
this.renderer.setStyle(this.tooltip, 'position', 'absolute');
if (this.tooltipClass !== null) {
this.renderer.addClass(this.tooltip, this.tooltipClass);
const classes = this.tooltipClass.split(' ');
for (let i = 0; i < classes.length; i++) {
this.renderer.addClass(this.tooltip, classes[i]);
}
}
if (this.placement !== null) {
this.renderer.addClass(this.tooltip, 'ng-tooltip-' + this.placement);
@ -114,4 +130,14 @@ export class TooltipDirective {
this.renderer.setStyle(this.tooltip, 'left', hostPos.right + 'px');
}
}
ngOnDestroy() {
clearTimeout(this.removeTooltipTimeout);
clearTimeout(this.removeTooltipTimeoutInner);
if (this.tooltip) {
this.renderer.removeChild(document.body, this.tooltip);
this.tooltip = null;
}
}
}

View file

@ -13,7 +13,8 @@ export class Wallet {
mined_total: number;
tracking_hey: string;
alias?: string;
alias?: object;
wakeAlias?: boolean;
staking?: boolean;
new_messages?: number;
new_contracts?: number;
@ -45,7 +46,7 @@ export class Wallet {
this.mined_total = mined;
this.tracking_hey = tracking;
this.alias = '';
this.alias = {};
this.staking = false;
this.new_messages = 0;
this.new_contracts = 0;
@ -127,6 +128,15 @@ export class Wallet {
}
}
removeFromHistory(hash: string): void {
for (let i = 0; i < this.history.length; i++) {
if (this.history[i].tx_hash === hash) {
this.history.splice(i, 1);
break;
}
}
}
prepareContractsAfterOpen(items: any[], exp_med_ts, height_app, viewedContracts, notViewedContracts): void {
const wallet = this;
for (let i = 0; i < items.length; i++) {

View file

@ -38,10 +38,10 @@ export class HistoryTypeMessagesPipe implements PipeTransform {
// return '';
// case 3:
// return '';
// case 4:
// return '';
// case 5:
// return '';
case 4:
return this.translate.instant('HISTORY.TYPE_MESSAGES.CREATE_ALIAS');
case 5:
return this.translate.instant('HISTORY.TYPE_MESSAGES.UPDATE_ALIAS');
case 6:
return this.translate.instant('HISTORY.TYPE_MESSAGES.MINED');
case 7:

View file

@ -112,6 +112,14 @@ export class BackendService {
}
}
break;
case 'NOT_FOUND': if (command !== 'open_wallet' && command !== 'get_alias_info_by_name' && command !== 'get_alias_info_by_address') {
error_translate = this.translate.instant('ERRORS.FILE_NOT_FOUND');
params = JSON.parse(params);
if (params.path) {
error_translate += ': ' + params.path;
}
}
break;
case 'CANCELED':
case '':
break;
@ -135,7 +143,7 @@ export class BackendService {
}
private bigNumberParser(key, val) {
if (val.constructor.name === 'BigNumber' && ['balance', 'unlocked_balance', 'amount', 'fee', 'b_fee', 'to_pay', 'a_pledge', 'b_pledge'].indexOf(key) === -1) {
if (val.constructor.name === 'BigNumber' && ['balance', 'unlocked_balance', 'amount', 'fee', 'b_fee', 'to_pay', 'a_pledge', 'b_pledge', 'coast'].indexOf(key) === -1) {
return val.toNumber();
}
if (key === 'rcv' || key === 'spn') {
@ -363,7 +371,6 @@ export class BackendService {
this.runCommand('restore_wallet', params, callback);
}
sendMoney(from_wallet_id, to_address, amount, fee, mixin, comment, callback) {
const params = {
wallet_id: parseInt(from_wallet_id, 10),
@ -503,6 +510,82 @@ export class BackendService {
this.runCommand('set_localization_strings', params, callback);
}
registerAlias (wallet_id, alias, address, fee, comment, reward, callback) {
const params = {
wallet_id: wallet_id,
alias: {
alias: alias,
address: address,
tracking_key: '',
comment: comment
},
fee: this.moneyToIntPipe.transform(fee),
reward: this.moneyToIntPipe.transform(reward)
};
this.runCommand('request_alias_registration', params, callback);
}
updateAlias (wallet_id, alias, fee, callback) {
const params = {
wallet_id: wallet_id,
alias: {
alias: alias.name.replace('@', ''),
address: alias.address,
tracking_key: '',
comment: alias.comment
},
fee: this.moneyToIntPipe.transform(fee)
};
this.runCommand('request_alias_update', params, callback);
}
getAllAliases (callback) {
this.runCommand('get_all_aliases', {}, callback);
}
getAliasByName (value, callback) {
return this.runCommand('get_alias_info_by_name', value, callback);
}
getAliasByAddress (value, callback) {
return this.runCommand('get_alias_info_by_address', value, callback);
}
getAliasCoast (alias, callback) {
this.runCommand('get_alias_coast', {v: alias}, callback);
}
getWalletAlias(address) {
if (address != null && this.variablesService.daemon_state === 2) {
if (this.variablesService.aliasesChecked[address] == null) {
this.variablesService.aliasesChecked[address] = {};
if (this.variablesService.aliases.length) {
for (let i = 0, length = this.variablesService.aliases.length; i < length; i++) {
if (i in this.variablesService.aliases && this.variablesService.aliases[i]['address'] === address) {
this.variablesService.aliasesChecked[address]['name'] = this.variablesService.aliases[i].name;
this.variablesService.aliasesChecked[address]['address'] = this.variablesService.aliases[i].address;
this.variablesService.aliasesChecked[address]['comment'] = this.variablesService.aliases[i].comment;
return this.variablesService.aliasesChecked[address];
}
}
}
this.getAliasByAddress(address, (status, data) => {
if (status) {
this.variablesService.aliasesChecked[data.address]['name'] = '@' + data.alias;
this.variablesService.aliasesChecked[data.address]['address'] = data.address;
this.variablesService.aliasesChecked[data.address]['comment'] = data.comment;
}
});
}
return this.variablesService.aliasesChecked[address];
}
return {};
}
getPoolInfo(callback) {
this.runCommand('get_tx_pool_info', {}, callback);
}
}
@ -520,8 +603,6 @@ export class BackendService {
return this.runCommand('is_file_exist', path, callback);
},
isAutoStartEnabled: function (callback) {
this.runCommand('is_autostart_enabled', {}, function (status, data) {
if (angular.isFunction(callback)) {
@ -530,8 +611,6 @@ export class BackendService {
});
},
setLogLevel: function (level) {
return this.runCommand('set_log_level', asVal(level))
},
@ -558,66 +637,16 @@ export class BackendService {
})
},
resync_wallet: function (wallet_id, callback) {
this.runCommand('resync_wallet', {wallet_id: wallet_id}, callback);
},
registerAlias: function (wallet_id, alias, address, fee, comment, reward, callback) {
var params = {
"wallet_id": wallet_id,
"alias": {
"alias": alias,
"address": address,
"tracking_key": "",
"comment": comment
},
"fee": $filter('money_to_int')(fee),
"reward": $filter('money_to_int')(reward)
};
this.runCommand('request_alias_registration', params, callback);
},
updateAlias: function (wallet_id, alias, fee, callback) {
var params = {
wallet_id: wallet_id,
alias: {
"alias": alias.name.replace("@", ""),
"address": alias.address,
"tracking_key": "",
"comment": alias.comment
},
fee: $filter('money_to_int')(fee)
};
this.runCommand('request_alias_update', params, callback);
},
getAllAliases: function (callback) {
this.runCommand('get_all_aliases', {}, callback);
},
getAliasByName: function (value, callback) {
return this.runCommand('get_alias_info_by_name', value, callback);
},
getAliasByAddress: function (value, callback) {
return this.runCommand('get_alias_info_by_address', value, callback);
},
getPoolInfo: function (callback) {
this.runCommand('get_tx_pool_info', {}, callback);
},
storeFile: function (path, buff, callback) {
this.backendObject['store_to_file'](path, (typeof buff === 'string' ? buff : JSON.stringify(buff)), function (data) {
backendCallback(data, {}, callback, 'store_to_file');
});
},
getMiningEstimate: function (amount_coins, time, callback) {
var params = {
"amount_coins": $filter('money_to_int')(amount_coins),
@ -634,29 +663,15 @@ export class BackendService {
this.runCommand('backup_wallet_keys', params, callback);
},
getAliasCoast: function (alias, callback) {
this.runCommand('get_alias_coast', asVal(alias), callback);
},
setBlockedIcon: function (enabled, callback) {
var mode = (enabled) ? "blocked" : "normal";
Service.runCommand('bool_toggle_icon', mode, callback);
},
getWalletInfo: function (wallet_id, callback) {
this.runCommand('get_wallet_info', {wallet_id: wallet_id}, callback);
},
printText: function (content) {
return this.runCommand('print_text', {html_text: content});
},

View file

@ -21,7 +21,7 @@ export class ModalService {
);
this.components[length - 1].instance['type'] = type;
this.components[length - 1].instance['message'] = this.translate.instant(message);
this.components[length - 1].instance['message'] = message.length ? this.translate.instant(message) : '';
this.components[length - 1].instance['close'].subscribe(() => {
this.removeModal(length - 1);
});

View file

@ -38,9 +38,14 @@ export class VariablesService {
public wallets: Array<Wallet> = [];
public currentWallet: Wallet;
public aliases: any = [];
public aliasesChecked: any = {};
public aliasesUnconfirmed: any = [];
public enableAliasSearch = false;
getHeightAppEvent = new BehaviorSubject(null);
getRefreshStackingEvent = new BehaviorSubject(null);
getAliasChangedEvent = new BehaviorSubject(null);
public idle = new Idle()
.whenNotInteractive()
@ -70,6 +75,10 @@ export class VariablesService {
this.getHeightAppEvent.next(wallet_id);
}
changeAliases() {
this.getAliasChangedEvent.next(true);
}
setCurrentWallet(id): void {
this.wallets.forEach((wallet) => {
if (wallet.wallet_id === id) {

View file

@ -19,6 +19,9 @@ import { OpenWalletComponent } from './open-wallet/open-wallet.component';
import { RestoreWalletComponent } from './restore-wallet/restore-wallet.component';
import { SeedPhraseComponent } from './seed-phrase/seed-phrase.component';
import { WalletDetailsComponent } from './wallet-details/wallet-details.component';
import { AssignAliasComponent } from './assign-alias/assign-alias.component';
import { EditAliasComponent } from "./edit-alias/edit-alias.component";
import { TransferAliasComponent } from "./transfer-alias/transfer-alias.component";
const routes: Routes = [
{
@ -100,6 +103,18 @@ const routes: Routes = [
path: 'details',
component: WalletDetailsComponent
},
{
path: 'assign-alias',
component: AssignAliasComponent
},
{
path: 'edit-alias',
component: EditAliasComponent
},
{
path: 'transfer-alias',
component: TransferAliasComponent
},
{
path: 'settings',
component: SettingsComponent

View file

@ -7,6 +7,7 @@ import {VariablesService} from './_helpers/services/variables.service';
import {ContextMenuComponent} from 'ngx-contextmenu';
import {IntToMoneyPipe} from './_helpers/pipes/int-to-money.pipe';
import {BigNumber} from 'bignumber.js';
import {ModalService} from './_helpers/services/modal.service';
@Component({
selector: 'app-root',
@ -30,7 +31,8 @@ export class AppComponent implements OnInit, OnDestroy {
private router: Router,
private variablesService: VariablesService,
private ngZone: NgZone,
private intToMoneyPipe: IntToMoneyPipe
private intToMoneyPipe: IntToMoneyPipe,
private modalService: ModalService
) {
translate.addLangs(['en', 'fr']);
translate.setDefaultLang('en');
@ -64,7 +66,6 @@ export class AppComponent implements OnInit, OnDestroy {
this.backend.webkitLaunchedScript();
this.backend.is_remnotenode_mode_preconfigured((status, data) => {
// if (data === 'FALSE') {
// } else {
@ -99,7 +100,6 @@ export class AppComponent implements OnInit, OnDestroy {
this.onQuitRequest = true;
});
this.backend.eventSubscribe('update_wallet_status', (data) => {
console.log('----------------- update_wallet_status -----------------');
console.log(data);
@ -126,7 +126,6 @@ export class AppComponent implements OnInit, OnDestroy {
}
});
this.backend.eventSubscribe('wallet_sync_progress', (data) => {
console.log('----------------- wallet_sync_progress -----------------');
console.log(data);
@ -144,7 +143,6 @@ export class AppComponent implements OnInit, OnDestroy {
}
});
this.backend.eventSubscribe('update_daemon_state', (data) => {
console.log('----------------- update_daemon_state -----------------');
console.log('DAEMON:' + data.daemon_network_state);
@ -154,10 +152,10 @@ export class AppComponent implements OnInit, OnDestroy {
this.variablesService.setHeightApp(data.height);
this.ngZone.run(() => {
this.variablesService.daemon_state = data.daemon_network_state;
if (data.daemon_network_state === 1) {
const max = data.max_net_seen_height - data.synchronization_start_height;
const current = data.height - data.synchronization_start_height;
this.variablesService.daemon_state = data['daemon_network_state'];
if (data['daemon_network_state'] === 1) {
const max = data['max_net_seen_height'] - data['synchronization_start_height'];
const current = data.height - data['synchronization_start_height'];
const return_val = Math.floor((current * 100 / max) * 100) / 100;
if (max === 0 || return_val < 0) {
this.variablesService.sync.progress_value = 0;
@ -171,7 +169,8 @@ export class AppComponent implements OnInit, OnDestroy {
}
}
});
if (!this.firstOnlineState) {
if (!this.firstOnlineState && data['daemon_network_state'] === 2) {
this.getAliases();
this.backend.getDefaultFee((status_fee, data_fee) => {
this.variablesService.default_fee_big = new BigNumber(data_fee);
this.variablesService.default_fee = this.intToMoneyPipe.transform(data_fee);
@ -188,6 +187,19 @@ export class AppComponent implements OnInit, OnDestroy {
return;
}
if (this.variablesService.aliasesUnconfirmed.length) {
let alias = false;
for (let i = 0; i < this.variablesService.aliasesUnconfirmed.length; i++) {
if (this.variablesService.aliasesUnconfirmed[i].tx_hash === data.ti.tx_hash) {
alias = this.variablesService.aliasesUnconfirmed[i];
break;
}
}
if (alias) {
this.variablesService.aliasesUnconfirmed.splice(this.variablesService.aliasesUnconfirmed.indexOf(alias), 1);
}
}
const wallet_id = data.wallet_id;
const tr_info = data.ti;
@ -315,72 +327,138 @@ export class AppComponent implements OnInit, OnDestroy {
}
});
this.backend.eventSubscribe('money_transfer_cancel', (data) => {
console.log('----------------- money_transfer_cancel -----------------');
console.log(data);
// if (!data.ti) {
// return;
// }
//
// var wallet_id = data.wallet_id;
// var tr_info = data.ti;
// var wallet = $rootScope.getWalletById(wallet_id);
// if (wallet) {
// if ( tr_info.hasOwnProperty("contract") ){
// for (var i = 0; i < $rootScope.contracts.length; i++) {
// if ($rootScope.contracts[i].contract_id === tr_info.contract[0].contract_id && $rootScope.contracts[i].is_a === tr_info.contract[0].is_a) {
// if ($rootScope.contracts[i].state === 1 || $rootScope.contracts[i].state === 110) {
// $rootScope.contracts[i].isNew = true;
// $rootScope.contracts[i].state = 140;
// $rootScope.getContractsRecount(); //escrow_code
// }
// break;
// }
// }
// }
// angular.forEach(wallet.history, function (tr_item, key) {
// if (tr_item.tx_hash === tr_info.tx_hash) {
// wallet.history.splice(key, 1);
// }
// });
//
// var error_tr = '';
// switch (tr_info.tx_type) {
// case 0:
// error_tr = $filter('translate')('ERROR_GUI_TX_TYPE_NORMAL') + '<br>' +
// tr_info.tx_hash + '<br>' + wallet.name + '<br>' + wallet.address + '<br>' +
// $filter('translate')('ERROR_GUI_TX_TYPE_NORMAL_TO') + ' ' + $rootScope.moneyParse(tr_info.amount) + ' ' +
// $filter('translate')('ERROR_GUI_TX_TYPE_NORMAL_END');
// informer.error(error_tr);
// break;
// case 1:
// informer.error('ERROR_GUI_TX_TYPE_PUSH_OFFER');
// break;
// case 2:
// informer.error('ERROR_GUI_TX_TYPE_UPDATE_OFFER');
// break;
// case 3:
// informer.error('ERROR_GUI_TX_TYPE_CANCEL_OFFER');
// break;
// case 4:
// error_tr = $filter('translate')('ERROR_GUI_TX_TYPE_NEW_ALIAS') + '<br>' +
// tr_info.tx_hash + '<br>' + wallet.name + '<br>' + wallet.address + '<br>' +
// $filter('translate')('ERROR_GUI_TX_TYPE_NEW_ALIAS_END');
// informer.error(error_tr);
// break;
// case 5:
// error_tr = $filter('translate')('ERROR_GUI_TX_TYPE_UPDATE_ALIAS') + '<br>' +
// tr_info.tx_hash + '<br>' + wallet.name + '<br>' + wallet.address + '<br>' +
// $filter('translate')('ERROR_GUI_TX_TYPE_NEW_ALIAS_END');
// informer.error(error_tr);
// break;
// case 6:
// informer.error('ERROR_GUI_TX_TYPE_COIN_BASE');
// break;
// }
// }
if (!data.ti) {
return;
}
const wallet_id = data.wallet_id;
const tr_info = data.ti;
const wallet = this.variablesService.getWallet(wallet_id);
if (wallet) {
if (tr_info.hasOwnProperty('contract')) {
for (let i = 0; i < wallet.contracts.length; i++) {
if (wallet.contracts[i].contract_id === tr_info.contract[0].contract_id && wallet.contracts[i].is_a === tr_info.contract[0].is_a) {
if (wallet.contracts[i].state === 1 || wallet.contracts[i].state === 110) {
wallet.contracts[i].is_new = true;
wallet.contracts[i].state = 140;
wallet.recountNewContracts();
}
break;
}
}
}
wallet.removeFromHistory(tr_info.tx_hash);
let error_tr = '';
switch (tr_info.tx_type) {
case 0:
error_tr = this.translate.instant('ERRORS.TX_TYPE_NORMAL') + '<br>' +
tr_info.tx_hash + '<br>' + wallet.name + '<br>' + wallet.address + '<br>' +
this.translate.instant('ERRORS.TX_TYPE_NORMAL_TO') + ' ' + this.intToMoneyPipe.transform(tr_info.amount) + ' ' +
this.translate.instant('ERRORS.TX_TYPE_NORMAL_END');
break;
case 1:
// this.translate.instant('ERRORS.TX_TYPE_PUSH_OFFER');
break;
case 2:
// this.translate.instant('ERRORS.TX_TYPE_UPDATE_OFFER');
break;
case 3:
// this.translate.instant('ERRORS.TX_TYPE_CANCEL_OFFER');
break;
case 4:
error_tr = this.translate.instant('ERRORS.TX_TYPE_NEW_ALIAS') + '<br>' +
tr_info.tx_hash + '<br>' + wallet.name + '<br>' + wallet.address + '<br>' +
this.translate.instant('ERRORS.TX_TYPE_NEW_ALIAS_END');
break;
case 5:
error_tr = this.translate.instant('ERRORS.TX_TYPE_UPDATE_ALIAS') + '<br>' +
tr_info.tx_hash + '<br>' + wallet.name + '<br>' + wallet.address + '<br>' +
this.translate.instant('ERRORS.TX_TYPE_NEW_ALIAS_END');
break;
case 6:
error_tr = this.translate.instant('ERRORS.TX_TYPE_COIN_BASE');
break;
}
if (error_tr) {
this.modalService.prepareModal('error', error_tr);
}
}
});
this.backend.eventSubscribe('on_core_event', (data) => {
console.log('----------------- on_core_event -----------------');
console.log(data);
data = JSON.parse(data);
if (data.events != null) {
for (let i = 0, length = data.events.length; i < length; i++) {
switch (data.events[i].method) {
case 'CORE_EVENT_BLOCK_ADDED': break;
case 'CORE_EVENT_ADD_ALIAS':
if (this.variablesService.aliasesChecked[data.events[i].details.address] != null) {
this.variablesService.aliasesChecked[data.events[i].details.address]['name'] = '@' + data.events[i].details.alias;
this.variablesService.aliasesChecked[data.events[i].details.address]['address'] = data.events[i].details.address;
this.variablesService.aliasesChecked[data.events[i].details.address]['comment'] = data.events[i].details.comment;
}
if (this.variablesService.enableAliasSearch) {
const newAlias = {
name: '@' + data.events[i].details.alias,
address: data.events[i].details.address,
comment: data.events[i].details.comment
};
this.variablesService.aliases = this.variablesService.aliases.concat(newAlias);
// this.variablesService.aliases = this.variablesService.aliases.sort((a, b) => {
// if (a.name.length > b.name.length) return 1;
// if (a.name.length < b.name.length) return -1;
// if (a.name > b.name) return 1;
// if (a.name < b.name) return -1;
// return 0;
// });
this.variablesService.changeAliases();
}
break;
case 'CORE_EVENT_UPDATE_ALIAS':
for (const address in this.variablesService.aliasesChecked) {
if (this.variablesService.aliasesChecked.hasOwnProperty(address)) {
if (this.variablesService.aliasesChecked[address].name === '@' + data.events[i].details.alias) {
if (this.variablesService.aliasesChecked[address].address !== data.events[i].details.details.address) {
delete this.variablesService.aliasesChecked[address]['name'];
delete this.variablesService.aliasesChecked[address]['address'];
delete this.variablesService.aliasesChecked[address]['comment'];
} else {
this.variablesService.aliasesChecked[address].comment = data.events[i].details.details.comment;
}
break;
}
}
}
if (this.variablesService.aliasesChecked[data.events[i].details.details.address] != null) {
this.variablesService.aliasesChecked[data.events[i].details.details.address]['name'] = '@' + data.events[i].details.alias;
this.variablesService.aliasesChecked[data.events[i].details.details.address]['address'] = data.events[i].details.details.address;
this.variablesService.aliasesChecked[data.events[i].details.details.address]['comment'] = data.events[i].details.details.comment;
}
if (this.variablesService.enableAliasSearch) {
const CurrentAlias = this.variablesService.aliases.find((element) => element.name === '@' + data.events[i].details.alias);
if (CurrentAlias) {
CurrentAlias.address = data.events[i].details.details.address;
CurrentAlias.comment = data.events[i].details.details.comment;
}
}
this.variablesService.changeAliases();
break;
default: break;
}
}
}
});
this.intervalUpdateContractsState = setInterval(() => {
@ -454,6 +532,42 @@ export class AppComponent implements OnInit, OnDestroy {
);
}
getAliases() {
this.backend.getAllAliases((status, data, error) => {
if (error === 'CORE_BUSY') {
window.setTimeout(() => {
this.getAliases();
}, 10000);
} else if (error === 'OVERFLOW') {
this.variablesService.aliases = [];
this.variablesService.enableAliasSearch = false;
} else {
this.variablesService.enableAliasSearch = true;
if (data.aliases && data.aliases.length) {
this.variablesService.aliases = [];
data.aliases.forEach(alias => {
const newAlias = {
name: '@' + alias.alias,
address: alias.address,
comment: alias.comment
};
this.variablesService.aliases.push(newAlias);
});
this.variablesService.wallets.forEach(wallet => {
wallet.alias = this.backend.getWalletAlias(wallet.address);
});
this.variablesService.aliases = this.variablesService.aliases.sort((a, b) => {
if (a.name.length > b.name.length) return 1;
if (a.name.length < b.name.length) return -1;
if (a.name > b.name) return 1;
if (a.name < b.name) return -1;
return 0;
});
this.variablesService.changeAliases();
}
}
});
}
contextMenuCopy(target) {
if (target && (target['nodeName'].toUpperCase() === 'TEXTAREA' || target['nodeName'].toUpperCase() === 'INPUT')) {

View file

@ -13,6 +13,9 @@ import { OpenWalletComponent } from './open-wallet/open-wallet.component';
import { RestoreWalletComponent } from './restore-wallet/restore-wallet.component';
import { SeedPhraseComponent } from './seed-phrase/seed-phrase.component';
import { WalletDetailsComponent } from './wallet-details/wallet-details.component';
import { AssignAliasComponent } from './assign-alias/assign-alias.component';
import { EditAliasComponent } from './edit-alias/edit-alias.component';
import { TransferAliasComponent } from './transfer-alias/transfer-alias.component';
import { WalletComponent } from './wallet/wallet.component';
import { SendComponent } from './send/send.component';
import { ReceiveComponent } from './receive/receive.component';
@ -20,23 +23,26 @@ import { HistoryComponent } from './history/history.component';
import { ContractsComponent } from './contracts/contracts.component';
import { PurchaseComponent } from './purchase/purchase.component';
import { MessagesComponent } from './messages/messages.component';
import { TypingMessageComponent } from './typing-message/typing-message.component';
import { StakingComponent } from './staking/staking.component';
import { HttpClient, HttpClientModule } from '@angular/common/http';
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { TypingMessageComponent } from './typing-message/typing-message.component';
import { BackendService } from './_helpers/services/backend.service';
import { ModalService } from './_helpers/services/modal.service';
import { MoneyToIntPipe } from './_helpers/pipes/money-to-int.pipe';
import { IntToMoneyPipe } from './_helpers/pipes/int-to-money.pipe';
import { StakingSwitchComponent } from './_helpers/directives/staking-switch/staking-switch.component';
import { TooltipDirective } from './_helpers/directives/tooltip.directive';
import { HistoryTypeMessagesPipe } from './_helpers/pipes/history-type-messages.pipe';
import { ContractStatusMessagesPipe } from './_helpers/pipes/contract-status-messages.pipe';
import { ContractTimeLeftPipe } from './_helpers/pipes/contract-time-left.pipe';
import { TooltipDirective } from './_helpers/directives/tooltip.directive';
import { InputValidateDirective } from './_helpers/directives/input-validate/input-validate.directive';
import { StakingSwitchComponent } from './_helpers/directives/staking-switch/staking-switch.component';
import { ModalContainerComponent } from './_helpers/directives/modal-container/modal-container.component';
import { TransactionDetailsComponent } from './_helpers/directives/transaction-details/transaction-details.component';
import { ContextMenuModule } from 'ngx-contextmenu';
export function HttpLoaderFactory(httpClient: HttpClient) {
@ -45,9 +51,6 @@ export function HttpLoaderFactory(httpClient: HttpClient) {
import { ChartModule, HIGHCHARTS_MODULES, Highcharts} from 'angular-highcharts';
import { InputValidateDirective } from './_helpers/directives/input-validate/input-validate.directive';
import { ModalContainerComponent } from './_helpers/directives/modal-container/modal-container.component';
import { TransactionDetailsComponent } from './_helpers/directives/transaction-details/transaction-details.component';
// import * as more from 'highcharts/highcharts-more.src';
// import * as exporting from 'highcharts/modules/exporting.src';
// import * as highstock from 'highcharts/modules/stock.src';
@ -70,6 +73,9 @@ Highcharts.setOptions({
RestoreWalletComponent,
SeedPhraseComponent,
WalletDetailsComponent,
AssignAliasComponent,
EditAliasComponent,
TransferAliasComponent,
WalletComponent,
SendComponent,
ReceiveComponent,

View file

@ -0,0 +1,56 @@
<div class="content">
<div class="head">
<div class="breadcrumbs">
<span [routerLink]="['/wallet/' + wallet.wallet_id + '/history']">{{ wallet.name }}</span>
<span>{{ 'BREADCRUMBS.ASSIGN_ALIAS' | translate }}</span>
</div>
<button class="back-btn" (click)="back()">
<i class="icon back"></i>
<span>{{ 'COMMON.BACK' | translate }}</span>
</button>
</div>
<form class="form-assign" [formGroup]="assignForm">
<div class="input-block alias-name">
<label for="alias-name" tooltip="{{ 'ASSIGN_ALIAS.NAME.TOOLTIP' | translate }}" placement="bottom" tooltipClass="table-tooltip assign-alias-tooltip" [delay]="500">
{{ 'ASSIGN_ALIAS.NAME.LABEL' | translate }}
</label>
<input type="text" id="alias-name" formControlName="name" placeholder="{{ 'ASSIGN_ALIAS.NAME.PLACEHOLDER' | translate }}">
<div class="error-block" *ngIf="assignForm.controls['name'].invalid && (assignForm.controls['name'].dirty || assignForm.controls['name'].touched)">
<div *ngIf="assignForm.controls['name'].errors['required']">
{{ 'ASSIGN_ALIAS.FORM_ERRORS.NAME_REQUIRED' | translate }}
</div>
<div *ngIf="assignForm.controls['name'].errors['pattern'] && assignForm.get('name').value.length > 6 && assignForm.get('name').value.length <= 25">
{{ 'ASSIGN_ALIAS.FORM_ERRORS.NAME_WRONG' | translate }}
</div>
<div *ngIf="assignForm.get('name').value.length <= 6 || assignForm.get('name').value.length > 25">
{{ 'ASSIGN_ALIAS.FORM_ERRORS.NAME_LENGTH' | translate }}
</div>
</div>
<div class="error-block" *ngIf="alias.exists">
<div>
{{ 'ASSIGN_ALIAS.FORM_ERRORS.NAME_EXISTS' | translate }}
</div>
</div>
</div>
<div class="input-block textarea">
<label for="alias-comment" tooltip="{{ 'ASSIGN_ALIAS.COMMENT.TOOLTIP' | translate }}" placement="bottom" tooltipClass="table-tooltip assign-alias-tooltip" [delay]="500">
{{ 'ASSIGN_ALIAS.COMMENT.LABEL' | translate }}
</label>
<textarea id="alias-comment" formControlName="comment" placeholder="{{ 'ASSIGN_ALIAS.COMMENT.PLACEHOLDER' | translate }}"></textarea>
</div>
<div class="alias-cost">{{ "ASSIGN_ALIAS.COST" | translate : {value: alias.price | intToMoney, currency: variablesService.defaultCurrency} }}</div>
<div class="wrap-buttons">
<button type="button" class="blue-button" (click)="assignAlias()" [disabled]="!assignForm.valid || !canRegister || notEnoughMoney">{{ 'ASSIGN_ALIAS.BUTTON_ASSIGN' | translate }}</button>
<button type="button" class="blue-button" (click)="back()">{{ 'ASSIGN_ALIAS.BUTTON_CANCEL' | translate }}</button>
</div>
</form>
</div>

View file

@ -0,0 +1,30 @@
.form-assign {
margin: 2.4rem 0;
.alias-name {
width: 50%;
}
.alias-cost {
font-size: 1.3rem;
margin-top: 2rem;
}
.wrap-buttons {
display: flex;
justify-content: space-between;
margin: 2.5rem -0.7rem;
button {
margin: 0 0.7rem;
width: 15rem;
}
}
}
.assign-alias-tooltip {
font-size: 1.3rem;
line-height: 2rem;
padding: 1rem 1.5rem;
max-width: 46rem;
}

View file

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { AssignAliasComponent } from './assign-alias.component';
describe('AssignAliasComponent', () => {
let component: AssignAliasComponent;
let fixture: ComponentFixture<AssignAliasComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ AssignAliasComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AssignAliasComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -0,0 +1,115 @@
import {Component, NgZone, OnInit, OnDestroy} from '@angular/core';
import {FormGroup, FormControl, Validators} from '@angular/forms';
import {Location} from '@angular/common';
import {Router} from '@angular/router';
import {BackendService} from '../_helpers/services/backend.service';
import {VariablesService} from '../_helpers/services/variables.service';
import {ModalService} from '../_helpers/services/modal.service';
import {Wallet} from '../_helpers/models/wallet.model';
import {MoneyToIntPipe} from '../_helpers/pipes/money-to-int.pipe';
import {IntToMoneyPipe} from '../_helpers/pipes/int-to-money.pipe';
import BigNumber from 'bignumber.js';
import {Subscription} from "rxjs";
@Component({
selector: 'app-assign-alias',
templateUrl: './assign-alias.component.html',
styleUrls: ['./assign-alias.component.scss']
})
export class AssignAliasComponent implements OnInit, OnDestroy {
wallet: Wallet;
assignForm = new FormGroup({
name: new FormControl('', [Validators.required, Validators.pattern(/^@?[a-z0-9\.\-]{6,25}$/)]),
comment: new FormControl('')
});
assignFormSubscription: Subscription;
alias = {
name: '',
fee: this.variablesService.default_fee,
price: new BigNumber(0),
reward: '0',
rewardOriginal: '0',
comment: '',
exists: false
};
canRegister = false;
notEnoughMoney = false;
constructor(
private ngZone: NgZone,
private location: Location,
private router: Router,
private backend: BackendService,
private variablesService: VariablesService,
private modalService: ModalService,
private moneyToInt: MoneyToIntPipe,
private intToMoney: IntToMoneyPipe
) {}
ngOnInit() {
this.wallet = this.variablesService.currentWallet;
this.assignFormSubscription = this.assignForm.get('name').valueChanges.subscribe(value => {
this.canRegister = false;
this.alias.exists = false;
const newName = value.toLowerCase().replace('@', '');
if (!(this.assignForm.controls['name'].errors && this.assignForm.controls['name'].errors.hasOwnProperty('pattern')) && newName.length >= 6 && newName.length <= 25) {
this.backend.getAliasByName(newName, status => {
this.ngZone.run(() => {
this.alias.exists = status;
});
if (!status) {
this.alias.price = new BigNumber(0);
this.backend.getAliasCoast(newName, (statusPrice, dataPrice) => {
this.ngZone.run(() => {
if (statusPrice) {
this.alias.price = BigNumber.sum(dataPrice['coast'], this.variablesService.default_fee_big);
}
this.notEnoughMoney = this.alias.price.isGreaterThan(this.wallet.unlocked_balance);
this.alias.reward = this.intToMoney.transform(this.alias.price, false);
this.alias.rewardOriginal = this.intToMoney.transform(dataPrice['coast'], false);
this.canRegister = !this.notEnoughMoney;
});
});
} else {
this.notEnoughMoney = false;
this.alias.reward = '0';
this.alias.rewardOriginal = '0';
}
});
} else {
this.notEnoughMoney = false;
this.alias.reward = '0';
this.alias.rewardOriginal = '0';
}
this.alias.name = newName;
});
}
assignAlias() {
const alias = this.backend.getWalletAlias(this.wallet.address);
if (alias.hasOwnProperty('name')) {
this.modalService.prepareModal('info', 'ASSIGN_ALIAS.ONE_ALIAS');
} else {
this.alias.comment = this.assignForm.get('comment').value;
this.backend.registerAlias(this.wallet.wallet_id, this.alias.name, this.wallet.address, this.alias.fee, this.alias.comment, this.alias.rewardOriginal, (status, data) => {
if (status) {
this.variablesService.aliasesUnconfirmed.push({tx_hash: data.tx_hash, name: this.alias.name});
this.wallet.wakeAlias = true;
this.modalService.prepareModal('info', 'ASSIGN_ALIAS.REQUEST_ADD_REG');
this.ngZone.run(() => {
this.router.navigate(['/wallet/' + this.wallet.wallet_id]);
});
}
});
}
}
back() {
this.location.back();
}
ngOnDestroy() {
this.assignFormSubscription.unsubscribe();
}
}

View file

@ -49,7 +49,9 @@ export class CreateWalletComponent implements OnInit {
}
createWallet() {
this.router.navigate(['/seed-phrase'], {queryParams: {wallet_id: this.wallet.id}});
this.ngZone.run(() => {
this.router.navigate(['/seed-phrase'], {queryParams: {wallet_id: this.wallet.id}});
});
}
saveWallet() {

View file

@ -0,0 +1,44 @@
<div class="content">
<div class="head">
<div class="breadcrumbs">
<span [routerLink]="['/wallet/' + wallet.wallet_id + '/history']">{{ wallet.name }}</span>
<span>{{ 'BREADCRUMBS.EDIT_ALIAS' | translate }}</span>
</div>
<button class="back-btn" (click)="back()">
<i class="icon back"></i>
<span>{{ 'COMMON.BACK' | translate }}</span>
</button>
</div>
<form class="form-edit">
<div class="input-block alias-name">
<label for="alias-name">
{{ 'EDIT_ALIAS.NAME.LABEL' | translate }}
</label>
<input type="text" id="alias-name" [value]="alias.name" placeholder="{{ 'EDIT_ALIAS.NAME.PLACEHOLDER' | translate }}" readonly>
</div>
<div class="input-block textarea">
<label for="alias-comment">
{{ 'EDIT_ALIAS.COMMENT.LABEL' | translate }}
</label>
<textarea id="alias-comment" [(ngModel)]="alias.comment" [ngModelOptions]="{standalone: true}" placeholder="{{ 'EDIT_ALIAS.COMMENT.PLACEHOLDER' | translate }}"></textarea>
<div class="error-block" *ngIf="alias.comment.length > 0 && notEnoughMoney">
{{ 'EDIT_ALIAS.FORM_ERRORS.NO_MONEY' | translate }}
</div>
</div>
<div class="alias-cost">{{ "EDIT_ALIAS.COST" | translate : {value: variablesService.default_fee, currency: variablesService.defaultCurrency} }}</div>
<div class="wrap-buttons">
<button type="button" class="blue-button" (click)="updateAlias()" [disabled]="notEnoughMoney || oldAliasComment === alias.comment">{{ 'EDIT_ALIAS.BUTTON_EDIT' | translate }}</button>
<button type="button" class="blue-button" (click)="back()">{{ 'EDIT_ALIAS.BUTTON_CANCEL' | translate }}</button>
</div>
</form>
</div>

View file

@ -0,0 +1,23 @@
.form-edit {
margin: 2.4rem 0;
.alias-name {
width: 50%;
}
.alias-cost {
font-size: 1.3rem;
margin-top: 2rem;
}
.wrap-buttons {
display: flex;
justify-content: space-between;
margin: 2.5rem -0.7rem;
button {
margin: 0 0.7rem;
width: 15rem;
}
}
}

View file

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { EditAliasComponent } from './edit-alias.component';
describe('EditAliasComponent', () => {
let component: EditAliasComponent;
let fixture: ComponentFixture<EditAliasComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ EditAliasComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(EditAliasComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -0,0 +1,63 @@
import {Component, NgZone, OnInit} from '@angular/core';
import {Location} from '@angular/common';
import {Router} from '@angular/router';
import {BackendService} from "../_helpers/services/backend.service";
import {VariablesService} from "../_helpers/services/variables.service";
import {ModalService} from '../_helpers/services/modal.service';
import {Wallet} from "../_helpers/models/wallet.model";
@Component({
selector: 'app-edit-alias',
templateUrl: './edit-alias.component.html',
styleUrls: ['./edit-alias.component.scss']
})
export class EditAliasComponent implements OnInit {
wallet: Wallet;
alias: any;
oldAliasComment: 'string';
notEnoughMoney: boolean;
requestProcessing = false;
constructor(
private location: Location,
private router: Router,
private backend: BackendService,
private variablesService: VariablesService,
private modalService: ModalService,
private ngZone: NgZone
) {}
ngOnInit() {
this.wallet = this.variablesService.currentWallet;
const alias = this.backend.getWalletAlias(this.wallet.address);
this.alias = {
name: alias.name,
address: alias.address,
comment: alias.comment
};
this.oldAliasComment = alias.comment;
this.notEnoughMoney = this.wallet.unlocked_balance.isLessThan(this.variablesService.default_fee_big);
}
updateAlias() {
if (this.requestProcessing || this.notEnoughMoney || this.oldAliasComment === this.alias.comment) {
return;
}
this.requestProcessing = true;
this.backend.updateAlias(this.wallet.wallet_id, this.alias, this.variablesService.default_fee, (status) => {
if (status) {
this.modalService.prepareModal('success', '');
this.wallet.alias['comment'] = this.alias.comment;
this.ngZone.run(() => {
this.router.navigate(['/wallet/' + this.wallet.wallet_id]);
});
}
this.requestProcessing = false;
});
}
back() {
this.location.back();
}
}

View file

@ -38,7 +38,9 @@
</tr>
<tr class="transaction-details" [class.open]="index === openedDetails">
<td colspan="5">
<app-transaction-details *ngIf="index === openedDetails" [transaction]="item" [sizes]="calculatedWidth"></app-transaction-details>
<ng-container *ngIf="index === openedDetails">
<app-transaction-details [transaction]="item" [sizes]="calculatedWidth"></app-transaction-details>
</ng-container>
</td>
</tr>
</ng-container>

View file

@ -1,5 +1,6 @@
import {Component, OnInit, OnDestroy, AfterViewChecked, ViewChild, ElementRef} from '@angular/core';
import {VariablesService} from '../_helpers/services/variables.service';
import {ActivatedRoute} from '@angular/router';
@Component({
selector: 'app-history',
@ -7,14 +8,21 @@ import {VariablesService} from '../_helpers/services/variables.service';
styleUrls: ['./history.component.scss']
})
export class HistoryComponent implements OnInit, OnDestroy, AfterViewChecked {
parentRouting;
openedDetails = false;
calculatedWidth = [];
@ViewChild('head') head: ElementRef;
constructor(private variablesService: VariablesService) {}
constructor(
private route: ActivatedRoute,
private variablesService: VariablesService
) {}
ngOnInit() {}
ngOnInit() {
this.parentRouting = this.route.parent.params.subscribe(() => {
this.openedDetails = false;
});
}
ngAfterViewChecked() {
this.calculateWidth();
@ -48,6 +56,8 @@ export class HistoryComponent implements OnInit, OnDestroy, AfterViewChecked {
this.calculatedWidth.push(this.head.nativeElement.childNodes[4].clientWidth);
}
ngOnDestroy() {}
ngOnDestroy() {
this.parentRouting.unsubscribe();
}
}

View file

@ -96,6 +96,7 @@ export class LoginComponent implements OnInit, OnDestroy {
open_data['wi'].mined_total,
open_data['wi'].tracking_hey
);
new_wallet.alias = this.backend.getWalletAlias(new_wallet.address);
if (open_data.recent_history && open_data.recent_history.history) {
new_wallet.prepareHistory(open_data.recent_history.history);
}

View file

@ -98,6 +98,7 @@ export class OpenWalletComponent implements OnInit, OnDestroy {
open_data['wi'].mined_total,
open_data['wi'].tracking_hey
);
new_wallet.alias = this.backend.getWalletAlias(new_wallet.address);
if (open_data.recent_history && open_data.recent_history.history) {
new_wallet.prepareHistory(open_data.recent_history.history);
}

View file

@ -51,7 +51,9 @@ export class RestoreWalletComponent implements OnInit {
createWallet() {
this.router.navigate(['/seed-phrase'], {queryParams: {wallet_id: this.wallet.id}});
this.ngZone.run(() => {
this.router.navigate(['/seed-phrase'], {queryParams: {wallet_id: this.wallet.id}});
});
}
saveWallet() {
@ -80,6 +82,7 @@ export class RestoreWalletComponent implements OnInit {
restore_data['wi'].mined_total,
restore_data['wi'].tracking_hey
);
this.variablesService.opening_wallet.alias = this.backend.getWalletAlias(this.variablesService.opening_wallet.address);
if (restore_data.recent_history && restore_data.recent_history.history) {
this.variablesService.opening_wallet.prepareHistory(restore_data.recent_history.history);
}

View file

@ -13,7 +13,7 @@
<h3 class="seed-phrase-title">{{ 'SEED_PHRASE.TITLE' | translate }}</h3>
<div class="seed-phrase-content">
<div class="seed-phrase-content" (contextmenu)="variablesService.onContextMenuOnlyCopy($event, seedPhrase)">
<ng-container *ngFor="let word of seedPhrase.split(' '); let index = index">
<div class="word">{{(index + 1) + '. ' + word}}</div>
</ng-container>

View file

@ -9,7 +9,7 @@
<span class="balance">{{wallet.balance | intToMoney : '3' }} {{variablesService.defaultCurrency}}</span>
</div>
<div class="sidebar-account-row account-alias">
<span>{{wallet.alias}}</span>
<span>{{wallet.alias['name']}}</span>
<span>$ {{wallet.getMoneyEquivalent(variablesService.moneyEquivalent) | intToMoney | number : '1.2-2'}}</span>
</div>
<div class="sidebar-account-row account-staking" *ngIf="!(!wallet.loaded && variablesService.daemon_state === 2)">

View file

@ -1,4 +1,4 @@
import {Component, OnInit, OnDestroy} from '@angular/core';
import {Component, NgZone, OnInit, OnDestroy} from '@angular/core';
import {ActivatedRoute, NavigationStart, Router} from '@angular/router';
import {VariablesService} from '../_helpers/services/variables.service';
@ -15,9 +15,9 @@ export class SidebarComponent implements OnInit, OnDestroy {
constructor(
private route: ActivatedRoute,
private router: Router,
private variablesService: VariablesService
) {
}
private variablesService: VariablesService,
private ngZone: NgZone
) {}
ngOnInit() {
if (this.router.url.indexOf('/wallet/') !== -1) {
@ -47,14 +47,15 @@ export class SidebarComponent implements OnInit, OnDestroy {
});
}
ngOnDestroy() {
this.walletSubRouting.unsubscribe();
}
logOut() {
this.variablesService.stopCountdown();
this.variablesService.appPass = '';
this.router.navigate(['/login'], {queryParams: {type: 'auth'}});
this.ngZone.run(() => {
this.router.navigate(['/login'], {queryParams: {type: 'auth'}});
});
}
ngOnDestroy() {
this.walletSubRouting.unsubscribe();
}
}

View file

@ -0,0 +1,57 @@
<div class="content">
<div class="head">
<div class="breadcrumbs">
<span [routerLink]="['/wallet/' + wallet.wallet_id + '/history']">{{ wallet.name }}</span>
<span>{{ 'BREADCRUMBS.TRANSFER_ALIAS' | translate }}</span>
</div>
<button class="back-btn" (click)="back()">
<i class="icon back"></i>
<span>{{ 'COMMON.BACK' | translate }}</span>
</button>
</div>
<form class="form-transfer">
<div class="input-block alias-name">
<label for="alias-name">
{{ 'EDIT_ALIAS.NAME.LABEL' | translate }}
</label>
<input type="text" id="alias-name" [value]="alias.name" placeholder="{{ 'EDIT_ALIAS.NAME.PLACEHOLDER' | translate }}" readonly>
</div>
<div class="input-block textarea">
<label for="alias-comment">
{{ 'EDIT_ALIAS.COMMENT.LABEL' | translate }}
</label>
<textarea id="alias-comment" [value]="alias.comment" placeholder="{{ 'EDIT_ALIAS.COMMENT.PLACEHOLDER' | translate }}" readonly></textarea>
</div>
<div class="input-block alias-transfer-address">
<label for="alias-transfer">
{{ 'TRANSFER_ALIAS.ADDRESS.LABEL' | translate }}
</label>
<input type="text" id="alias-transfer" [(ngModel)]="transferAddress" [ngModelOptions]="{standalone: true}" (ngModelChange)="changeAddress()" placeholder="{{ 'TRANSFER_ALIAS.ADDRESS.PLACEHOLDER' | translate }}">
<div class="error-block" *ngIf="transferAddress.length > 0 && (transferAddressAlias || !transferAddressValid || (transferAddressValid && !permissionSend) || notEnoughMoney)">
<div *ngIf="!transferAddressValid">
{{ 'TRANSFER_ALIAS.FORM_ERRORS.WRONG_ADDRESS' | translate }}
</div>
<div *ngIf="transferAddressAlias || (transferAddressValid && !permissionSend)">
{{ 'TRANSFER_ALIAS.FORM_ERRORS.ALIAS_EXISTS' | translate }}
</div>
<div *ngIf="notEnoughMoney">
{{ 'TRANSFER_ALIAS.FORM_ERRORS.NO_MONEY' | translate }}
</div>
</div>
</div>
<div class="alias-cost">{{ "TRANSFER_ALIAS.COST" | translate : {value: variablesService.default_fee, currency: variablesService.defaultCurrency} }}</div>
<div class="wrap-buttons">
<button type="button" class="blue-button" (click)="transferAlias()" [disabled]="transferAddressAlias || !transferAddressValid || notEnoughMoney">{{ 'TRANSFER_ALIAS.BUTTON_TRANSFER' | translate }}</button>
<button type="button" class="blue-button" (click)="back()">{{ 'TRANSFER_ALIAS.BUTTON_CANCEL' | translate }}</button>
</div>
</form>
</div>

View file

@ -0,0 +1,23 @@
.form-transfer {
margin: 2.4rem 0;
.alias-name {
width: 50%;
}
.alias-cost {
font-size: 1.3rem;
margin-top: 2rem;
}
.wrap-buttons {
display: flex;
justify-content: space-between;
margin: 2.5rem -0.7rem;
button {
margin: 0 0.7rem;
width: 15rem;
}
}
}

View file

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { TransferAliasComponent } from './transfer-alias.component';
describe('TransferAliasComponent', () => {
let component: TransferAliasComponent;
let fixture: ComponentFixture<TransferAliasComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ TransferAliasComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TransferAliasComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -0,0 +1,108 @@
import {Component, NgZone, OnInit} from '@angular/core';
import {Location} from "@angular/common";
import {Router} from "@angular/router";
import {BackendService} from "../_helpers/services/backend.service";
import {VariablesService} from "../_helpers/services/variables.service";
import {ModalService} from "../_helpers/services/modal.service";
import {Wallet} from "../_helpers/models/wallet.model";
@Component({
selector: 'app-transfer-alias',
templateUrl: './transfer-alias.component.html',
styleUrls: ['./transfer-alias.component.scss']
})
export class TransferAliasComponent implements OnInit {
wallet: Wallet;
alias: any;
transferAddress = '';
transferAddressValid: boolean;
transferAddressAlias: boolean;
permissionSend: boolean;
notEnoughMoney: boolean;
requestProcessing = false;
constructor(
private location: Location,
private router: Router,
private backend: BackendService,
private variablesService: VariablesService,
private modalService: ModalService,
private ngZone: NgZone
) {}
ngOnInit() {
this.wallet = this.variablesService.currentWallet;
const alias = this.backend.getWalletAlias(this.wallet.address);
this.alias = {
name: alias.name,
address: alias.address,
comment: alias.comment,
tracking_key: alias.tracking_key
};
this.notEnoughMoney = this.wallet.unlocked_balance.isLessThan(this.variablesService.default_fee_big);
}
changeAddress() {
this.backend.validateAddress(this.transferAddress, status => {
this.transferAddressValid = status;
if (status) {
this.backend.getPoolInfo((statusPool, dataPool) => {
if (dataPool.hasOwnProperty('aliases_que') && dataPool.aliases_que.length) {
this.setStatus(!~dataPool.aliases_que.searchBy('address', this.transferAddress));
} else {
this.setStatus(status);
}
});
} else {
this.setStatus(false);
}
});
}
setStatus(statusSet) {
this.permissionSend = statusSet;
if (statusSet) {
this.backend.getAliasByAddress(this.transferAddress, (status, data) => {
this.ngZone.run(() => {
if (status) {
this.transferAddressAlias = true;
this.permissionSend = false;
} else {
this.transferAddressAlias = false;
}
});
});
} else {
this.ngZone.run(() => {
this.transferAddressAlias = false;
});
}
}
transferAlias() {
if (this.requestProcessing || !this.permissionSend || !this.transferAddressValid || this.notEnoughMoney) {
return;
}
this.requestProcessing = true;
const newAlias = {
name: this.alias.name,
address: this.transferAddress,
comment: this.alias.comment,
tracking_key: this.alias.tracking_key
};
this.backend.updateAlias(this.wallet.wallet_id, newAlias, this.variablesService.default_fee, (status, data) => {
if (status && data.hasOwnProperty('success') && data.success) {
this.modalService.prepareModal('info', 'TRANSFER_ALIAS.REQUEST_SEND_REG');
this.ngZone.run(() => {
this.router.navigate(['/wallet/' + this.wallet.wallet_id]);
});
}
this.requestProcessing = false;
});
}
back() {
this.location.back();
}
}

View file

@ -35,7 +35,7 @@
<label for="seed-phrase">{{ 'WALLET_DETAILS.LABEL_SEED_PHRASE' | translate }}</label>
<div class="seed-phrase" id="seed-phrase">
<div class="seed-phrase-hint" (click)="showSeedPhrase()" *ngIf="!showSeed">{{ 'WALLET_DETAILS.SEED_PHRASE_HINT' | translate }}</div>
<div class="seed-phrase-content" *ngIf="showSeed">
<div class="seed-phrase-content" *ngIf="showSeed" (contextmenu)="variablesService.onContextMenuOnlyCopy($event, seedPhrase)">
<ng-container *ngFor="let word of seedPhrase.split(' '); let index = index">
<div class="word">{{(index + 1) + '. ' + word}}</div>
</ng-container>

View file

@ -59,7 +59,9 @@ export class WalletDetailsComponent implements OnInit, OnDestroy {
onSubmitEdit() {
if (this.detailsForm.value) {
this.variablesService.currentWallet.name = this.detailsForm.get('name').value;
this.router.navigate(['/wallet/' + this.variablesService.currentWallet.wallet_id]);
this.ngZone.run(() => {
this.router.navigate(['/wallet/' + this.variablesService.currentWallet.wallet_id]);
});
}
}

View file

@ -1,10 +1,16 @@
<div class="header">
<div>
<h3>{{variablesService.currentWallet.name}}</h3>
<button (click)="openInBrowser('docs.zano.org/docs/how-to-get-alias')">
<!--<button (click)="openInBrowser('docs.zano.org/docs/how-to-get-alias')">-->
<button [routerLink]="['/assign-alias']" *ngIf="!variablesService.currentWallet.alias.hasOwnProperty('name')">
<i class="icon account"></i>
<span>{{ 'WALLET.REGISTER_ALIAS' | translate }}</span>
</button>
<div class="alias" *ngIf="variablesService.currentWallet.alias.hasOwnProperty('name')">
<span>{{variablesService.currentWallet.alias['name']}}</span>
<i class="icon edit" [routerLink]="['/edit-alias']"></i>
<i class="icon transfer" [routerLink]="['/transfer-alias']"></i>
</div>
</div>
<div>
<button [routerLink]="['/details']" routerLinkActive="active">
@ -22,7 +28,7 @@
<i #copyIcon class="icon copy" (click)="copyAddress()"></i>
</div>
<div class="balance">
<span [tooltip]="getTooltip()" [placement]="'bottom'" [tooltipClass]="'balance-tooltip'" [delay]="500" [timeout]="1000">{{variablesService.currentWallet.balance | intToMoney : '3'}} {{variablesService.defaultCurrency}}</span>
<span [tooltip]="getTooltip()" [placement]="'bottom'" [tooltipClass]="'balance-tooltip'" [delay]="300" [timeout]="0">{{variablesService.currentWallet.balance | intToMoney : '3'}} {{variablesService.defaultCurrency}}</span>
<span>$ {{variablesService.currentWallet.getMoneyEquivalent(variablesService.moneyEquivalent) | intToMoney | number : '1.2-2'}}</span>
</div>
<div class="tabs">

View file

@ -57,6 +57,27 @@
}
}
}
.alias {
display: flex;
align-items: center;
font-size: 1.3rem;
.icon {
cursor: pointer;
margin-right: 1.2rem;
width: 1.7rem;
height: 1.7rem;
&.edit {
mask: url(../../assets/icons/details.svg) no-repeat center;
}
&.transfer {
mask: url(../../assets/icons/send.svg) no-repeat center;
}
}
}
}
.address {

View file

@ -5,6 +5,7 @@ import {BackendService} from '../_helpers/services/backend.service';
import {TranslateService} from '@ngx-translate/core';
import {IntToMoneyPipe} from '../_helpers/pipes/int-to-money.pipe';
import {BigNumber} from 'bignumber.js';
import {Subscription} from "rxjs";
@Component({
selector: 'app-wallet',
@ -59,6 +60,7 @@ export class WalletComponent implements OnInit, OnDestroy {
active: false
}
];
aliasSubscription: Subscription;
constructor(
private route: ActivatedRoute,
@ -69,8 +71,7 @@ export class WalletComponent implements OnInit, OnDestroy {
private ngZone: NgZone,
private translate: TranslateService,
private intToMoneyPipe: IntToMoneyPipe
) {
}
) {}
ngOnInit() {
this.subRouting = this.route.params.subscribe(params => {
@ -80,6 +81,14 @@ export class WalletComponent implements OnInit, OnDestroy {
this.tabs[i].active = (this.tabs[i].link === '/' + this.route.snapshot.firstChild.url[0].path);
}
});
if (this.variablesService.currentWallet.alias.hasOwnProperty('name')) {
this.variablesService.currentWallet.wakeAlias = false;
}
this.aliasSubscription = this.variablesService.getAliasChangedEvent.subscribe(() => {
if (this.variablesService.currentWallet.alias.hasOwnProperty('name')) {
this.variablesService.currentWallet.wakeAlias = false;
}
})
}
changeTab(index) {
@ -90,7 +99,9 @@ export class WalletComponent implements OnInit, OnDestroy {
tab.active = false;
});
this.tabs[index].active = true;
this.router.navigate(['wallet/' + this.walletID + this.tabs[index].link]);
this.ngZone.run( () => {
this.router.navigate(['wallet/' + this.walletID + this.tabs[index].link]);
});
}
copyAddress() {
@ -129,6 +140,7 @@ export class WalletComponent implements OnInit, OnDestroy {
ngOnDestroy() {
this.subRouting.unsubscribe();
this.aliasSubscription.unsubscribe();
}
}

View file

@ -21,6 +21,9 @@
"OPEN_WALLET": "Open existing wallet",
"RESTORE_WALLET": "Restore from backup",
"WALLET_DETAILS": "Wallet details",
"ASSIGN_ALIAS": "Assign alias",
"EDIT_ALIAS": "Edit alias",
"TRANSFER_ALIAS": "Transfer alias",
"CONTRACTS": "Contracts",
"NEW_PURCHASE": "New purchase",
"OLD_PURCHASE": "Purchase"
@ -148,6 +151,68 @@
"NAME_DUPLICATE": "Name is duplicate."
}
},
"ASSIGN_ALIAS": {
"NAME": {
"LABEL": "Unique name",
"PLACEHOLDER": "@ Enter alias",
"TOOLTIP": "An alias is a shortened form or your account. An alias can only include Latin letters, numbers and characters “.” and “-”. It must start with “@”."
},
"COMMENT": {
"LABEL": "Comment",
"PLACEHOLDER": "Enter comment",
"TOOLTIP": "The comment will be visible to anyone who wants to make a payment to your alias. You can provide details about your business, contacts, or include any text. Comments can be edited later."
},
"COST": "Cost to create alias {{value}} {{currency}}",
"BUTTON_ASSIGN": "Assign",
"BUTTON_CANCEL": "Cancel",
"FORM_ERRORS": {
"NAME_REQUIRED": "Name is required.",
"NAME_WRONG": "Alias has wrong name.",
"NAME_LENGTH": "The alias must be 6-25 characters long.",
"NAME_EXISTS": "Alias name already exists."
},
"ONE_ALIAS": "You can create only one alias per wallet",
"REQUEST_ADD_REG": "The alias will be assigned within 10 minutes"
},
"EDIT_ALIAS": {
"NAME": {
"LABEL": "Unique name",
"PLACEHOLDER": "@ Enter alias"
},
"COMMENT": {
"LABEL": "Comment",
"PLACEHOLDER": "Enter comment"
},
"FORM_ERRORS": {
"NO_MONEY": "You do not have enough funds to change the comment to this alias"
},
"COST": "Cost to edit alias {{value}} {{currency}}",
"BUTTON_EDIT": "Edit",
"BUTTON_CANCEL": "Cancel"
},
"TRANSFER_ALIAS": {
"NAME": {
"LABEL": "Unique name",
"PLACEHOLDER": "@ Enter alias"
},
"COMMENT": {
"LABEL": "Comment",
"PLACEHOLDER": "Enter comment"
},
"ADDRESS": {
"LABEL": "The account to which the alias will be transferred",
"PLACEHOLDER": "Enter account number"
},
"FORM_ERRORS": {
"WRONG_ADDRESS": "No wallet with this account exists",
"ALIAS_EXISTS": "This account already has an alias",
"NO_MONEY": "You do not have enough funds to transfer this alias"
},
"COST": "Cost to transfer alias {{value}} {{currency}}",
"BUTTON_TRANSFER": "Transfer",
"BUTTON_CANCEL": "Cancel",
"REQUEST_SEND_REG": "The alias will be transferred within 10 minutes"
},
"SEND": {
"ADDRESS": "Address",
"AMOUNT": "Amount",
@ -190,6 +255,8 @@
"UNDEFINED": "Undefined",
"COMPLETE_BUYER": "Successfully complete contract, return remaining pledge",
"COMPLETE_SELLER": "Successfully complete contract, receive payment on contract, and return pledge",
"CREATE_ALIAS": "Fee for assigning alias",
"UPDATE_ALIAS": "Fee for editing alias",
"MINED": "Mined funds",
"CREATE_CONTRACT": "Send contract offer",
"PLEDGE_CONTRACT": "Make pledge on offer",
@ -370,7 +437,14 @@
"FILE_RESTORED": "The wallet file was corrupted. We have recovered the keys and the wallet from the blockchain",
"FILE_NOT_FOUND": "File not found",
"FILE_EXIST": "A file with that name already exists. Enter another name to save the file under",
"FILE_NOT_SAVED": "You cannot save a wallet file in this folder. Please choose another folder."
"FILE_NOT_SAVED": "You cannot save a wallet file in this folder. Please choose another folder.",
"TX_TYPE_NORMAL": "Error. The payment from the wallet",
"TX_TYPE_NORMAL_TO": "to",
"TX_TYPE_NORMAL_END": "was not completed.",
"TX_TYPE_NEW_ALIAS": "Error. Failed to register alias to safe",
"TX_TYPE_NEW_ALIAS_END": "Please try again.",
"TX_TYPE_UPDATE_ALIAS": "Error. Failed to change comment to alias in safe",
"TX_TYPE_COIN_BASE": "Error. The payment was not completed."
},
"CONTEXT_MENU": {
"COPY": "copy",

View file

@ -141,8 +141,8 @@ button {
width: 100%;
min-width: 100%;
height: 100%;
min-height: 7rem;
max-height: 7rem;
min-height: 7.5rem;
max-height: 7.5rem;
overflow: hidden;
resize: none;
@ -157,6 +157,7 @@ button {
font-size: 1rem;
line-height: 1.4rem;
align-self: flex-end;
text-align: right;
@include themify($themes) {
color: themed(redTextColor);

View file

@ -1,4 +1,4 @@
app-main, app-create-wallet, app-open-wallet, app-restore-wallet, app-seed-phrase, app-wallet-details, app-settings, app-login {
app-main, app-create-wallet, app-open-wallet, app-restore-wallet, app-seed-phrase, app-wallet-details, app-assign-alias, app-edit-alias, app-transfer-alias, app-settings, app-login {
flex: 1 1 auto;
padding: 3rem;
min-width: 85rem;

View file

@ -19,6 +19,16 @@ app-wallet {
}
}
}
.alias {
.icon {
@include themify($themes) {
background-color: themed(blueTextColor);
}
}
}
}
.address {

View file

@ -124,6 +124,8 @@ namespace nodetool
HANDLE_INVOKE_T2(COMMAND_REQUEST_STAT_INFO, &node_server::handle_get_stat_info)
HANDLE_INVOKE_T2(COMMAND_REQUEST_NETWORK_STATE, &node_server::handle_get_network_state)
HANDLE_INVOKE_T2(COMMAND_REQUEST_PEER_ID, &node_server::handle_get_peer_id)
HANDLE_INVOKE_T2(COMMAND_REQUEST_LOG, &node_server::handle_request_log)
HANDLE_INVOKE_T2(COMMAND_SET_LOG_LEVEL, &node_server::handle_set_log_level)
#endif
CHAIN_INVOKE_MAP_TO_OBJ_FORCE_CONTEXT(m_payload_handler, typename t_payload_net_handler::connection_context&)
END_INVOKE_MAP2()
@ -137,6 +139,8 @@ namespace nodetool
int handle_get_stat_info(int command, typename COMMAND_REQUEST_STAT_INFO::request& arg, typename COMMAND_REQUEST_STAT_INFO::response& rsp, p2p_connection_context& context);
int handle_get_network_state(int command, COMMAND_REQUEST_NETWORK_STATE::request& arg, COMMAND_REQUEST_NETWORK_STATE::response& rsp, p2p_connection_context& context);
int handle_get_peer_id(int command, COMMAND_REQUEST_PEER_ID::request& arg, COMMAND_REQUEST_PEER_ID::response& rsp, p2p_connection_context& context);
int handle_request_log(int command, COMMAND_REQUEST_LOG::request& arg, COMMAND_REQUEST_LOG::response& rsp, p2p_connection_context& context);
int handle_set_log_level(int command, COMMAND_SET_LOG_LEVEL::request& arg, COMMAND_SET_LOG_LEVEL::response& rsp, p2p_connection_context& context);
private:
#endif
bool init_config();

View file

@ -1039,6 +1039,7 @@ namespace nodetool
rsp.connections_count = m_net_server.get_config_object().get_connections_count();
rsp.incoming_connections_count = rsp.connections_count - get_outgoing_connections_count();
rsp.version = PROJECT_VERSION_LONG;
rsp.current_log_size = tools::get_log_file_size();
m_payload_handler.get_stat_info(arg.pr, rsp.payload_info);
return 1;
}
@ -1080,7 +1081,44 @@ namespace nodetool
rsp.my_id = m_config.m_peer_id;
return 1;
}
#endif
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
int node_server<t_payload_net_handler>::handle_request_log(int command, COMMAND_REQUEST_LOG::request& req, COMMAND_REQUEST_LOG::response& rsp, p2p_connection_context& context)
{
if (!check_trust(req.tr))
{
drop_connection(context);
return 1;
}
rsp.current_log_level = static_cast<int64_t>(log_space::get_set_log_detalisation_level());
tools::get_log_chunk_gzipped(req.log_chunk_offset, req.log_chunk_size, rsp.log_chunk, rsp.error);
rsp.current_log_size = tools::get_log_file_size();
return 1;
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
int node_server<t_payload_net_handler>::handle_set_log_level(int command, COMMAND_SET_LOG_LEVEL::request& req, COMMAND_SET_LOG_LEVEL::response& rsp, p2p_connection_context& context)
{
if (!check_trust(req.tr))
{
drop_connection(context);
return 1;
}
rsp.old_log_level = static_cast<int64_t>(log_space::get_set_log_detalisation_level());
log_space::get_set_log_detalisation_level(true, static_cast<int>(req.new_log_level));
rsp.current_log_level = static_cast<int64_t>(log_space::get_set_log_detalisation_level());
if (rsp.old_log_level != rsp.current_log_level)
{
LOG_PRINT_CC(context, "log level changed by debug command: " << rsp.old_log_level << " -> " << rsp.current_log_level, LOG_LEVEL_0);
}
return 1;
}
#endif // #ifdef ALLOW_DEBUG_COMMANDS
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
void node_server<t_payload_net_handler>::request_callback(const epee::net_utils::connection_context_base& context)

View file

@ -328,12 +328,14 @@ namespace nodetool
std::string version;
uint64_t connections_count;
uint64_t incoming_connections_count;
uint64_t current_log_size;
payload_stat_info payload_info;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(version)
KV_SERIALIZE(connections_count)
KV_SERIALIZE(incoming_connections_count)
KV_SERIALIZE(current_log_size)
KV_SERIALIZE(payload_info)
END_KV_SERIALIZE_MAP()
};
@ -397,7 +399,73 @@ namespace nodetool
};
};
#endif
/************************************************************************/
/* */
/************************************************************************/
struct COMMAND_REQUEST_LOG
{
const static int ID = P2P_COMMANDS_POOL_BASE + 7;
struct request
{
proof_of_trust tr;
uint64_t log_chunk_offset;
uint64_t log_chunk_size;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tr)
KV_SERIALIZE(log_chunk_offset)
KV_SERIALIZE(log_chunk_size)
END_KV_SERIALIZE_MAP()
};
struct response
{
int64_t current_log_level;
uint64_t current_log_size;
std::string error;
std::string log_chunk;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(current_log_level)
KV_SERIALIZE(current_log_size)
KV_SERIALIZE(error)
KV_SERIALIZE(log_chunk)
END_KV_SERIALIZE_MAP()
};
};
/************************************************************************/
/* */
/************************************************************************/
struct COMMAND_SET_LOG_LEVEL
{
const static int ID = P2P_COMMANDS_POOL_BASE + 8;
struct request
{
proof_of_trust tr;
int64_t new_log_level;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tr)
KV_SERIALIZE(new_log_level)
END_KV_SERIALIZE_MAP()
};
struct response
{
int64_t old_log_level;
int64_t current_log_level;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(old_log_level)
KV_SERIALIZE(current_log_level)
END_KV_SERIALIZE_MAP()
};
};
#endif // #ifdef ALLOW_DEBUG_COMMANDS
}

View file

@ -35,9 +35,13 @@ int test_big_difficulties(const char* dataFile)
end = n - DIFFICULTY_LAG;
begin = end - DIFFICULTY_WINDOW;
}
auto stamps = vector<uint64_t>(timestamps.begin() + begin, timestamps.begin() + end);
auto cumulative_diffs = vector<currency::wide_difficulty_type>(cumulative_difficulties.begin() + begin, cumulative_difficulties.begin() + end);
currency::wide_difficulty_type res = currency::next_difficulty(
vector<uint64_t>(timestamps.begin() + begin, timestamps.begin() + end),
vector<currency::wide_difficulty_type>(cumulative_difficulties.begin() + begin, cumulative_difficulties.begin() + end), DEFAULT_TEST_DIFFICULTY_TARGET);
stamps,
cumulative_diffs,
DEFAULT_TEST_DIFFICULTY_TARGET
);
if (res != difficulty) {
cerr << "Wrong wide difficulty for block " << n << endl
<< "Expected: " << difficulty << endl
@ -90,9 +94,13 @@ int main(int argc, char *argv[]) {
<< "Found: " << res << endl;
return 1;
}
auto stamps = vector<uint64_t>(timestamps.begin() + begin, timestamps.begin() + end);
auto cumulative_diffs = vector<currency::wide_difficulty_type>(wide_cumulative_difficulties.begin() + begin, wide_cumulative_difficulties.begin() + end);
currency::wide_difficulty_type wide_res = currency::next_difficulty(
vector<uint64_t>(timestamps.begin() + begin, timestamps.begin() + end),
vector<currency::wide_difficulty_type>(wide_cumulative_difficulties.begin() + begin, wide_cumulative_difficulties.begin() + end), DEFAULT_TEST_DIFFICULTY_TARGET);
stamps,
cumulative_diffs,
DEFAULT_TEST_DIFFICULTY_TARGET
);
if (wide_res.convert_to<uint64_t>() != res) {
cerr << "Wrong wide difficulty for block " << n << endl
<< "Expected: " << res << endl
@ -110,3 +118,4 @@ int main(int argc, char *argv[]) {
return 0;
}

View file

@ -13,6 +13,10 @@ using namespace epee;
#include "wallet/wallet2.h"
#include "currency_core/blockchain_storage.h"
using std::size_t;
using std::uint64_t;
using std::vector;
bool parse_file(const std::string& path, std::vector<std::vector<uint64_t>>& blocks, uint64_t reserve_size)
{
@ -41,11 +45,220 @@ bool parse_file(const std::string& path, std::vector<std::vector<uint64_t>>& blo
}
void run_difficulty_analysis(const std::string& path)
{
//hash_rate_analysis(path);
run_emulation(path);
#define BBR_DIFFICULTY_TARGET 120 // seconds
#define BBR_DIFFICULTY_WINDOW 720 // blocks
#define BBR_DIFFICULTY_LAG 15 // !!!
#define BBR_DIFFICULTY_CUT 60 // timestamps to cut after sorting
#define BBR_DIFFICULTY_STARTER 1
#define NEW_DIFFICULTY_WINDOW 360
#define NEW_DIFFICULTY_CUT_OLD 60 // timestamps to cut after sorting on the oldest timestamps
#define NEW_DIFFICULTY_CUT_LAST 0 // timestamps to cut after sorting on the most recent timestamps
const boost::multiprecision::uint256_t max128bit(std::numeric_limits<boost::multiprecision::uint128_t>::max());
currency::wide_difficulty_type bbr_next_difficulty(std::vector<uint64_t>& timestamps, std::vector<currency::wide_difficulty_type>& cumulative_difficulties, size_t target_seconds)
{
// timestamps - first is latest, back - is oldest timestamps
//cutoff DIFFICULTY_LAG
if (timestamps.size() > BBR_DIFFICULTY_WINDOW)
{
timestamps.resize(BBR_DIFFICULTY_WINDOW);
cumulative_difficulties.resize(BBR_DIFFICULTY_WINDOW);
}
size_t length = timestamps.size();
CHECK_AND_ASSERT_MES(length == cumulative_difficulties.size(), 0, "Check \"length == cumulative_difficulties.size()\" failed");
if (length <= 1) {
return BBR_DIFFICULTY_STARTER;
}
static_assert(BBR_DIFFICULTY_WINDOW >= 2, "Window is too small");
CHECK_AND_ASSERT_MES(length <= BBR_DIFFICULTY_WINDOW, 0, "length <= BBR_DIFFICULTY_WINDOW check failed, length=" << length);
sort(timestamps.begin(), timestamps.end(), std::greater<uint64_t>());
size_t cut_begin, cut_end;
static_assert(2 * BBR_DIFFICULTY_CUT <= BBR_DIFFICULTY_WINDOW - 2, "Cut length is too large");
if (length <= BBR_DIFFICULTY_WINDOW - 2 * BBR_DIFFICULTY_CUT) {
cut_begin = 0;
cut_end = length;
}
else {
cut_begin = (length - (BBR_DIFFICULTY_WINDOW - 2 * BBR_DIFFICULTY_CUT) + 1) / 2;
cut_end = cut_begin + (BBR_DIFFICULTY_WINDOW - 2 * BBR_DIFFICULTY_CUT);
}
CHECK_AND_ASSERT_THROW_MES(/*cut_begin >= 0 &&*/ cut_begin + 2 <= cut_end && cut_end <= length, "validation in next_difficulty is failed");
uint64_t time_span = timestamps[cut_begin] - timestamps[cut_end - 1];
if (time_span == 0) {
time_span = 1;
}
currency::wide_difficulty_type total_work = cumulative_difficulties[cut_begin] - cumulative_difficulties[cut_end - 1];
boost::multiprecision::uint256_t res = (boost::multiprecision::uint256_t(total_work) * target_seconds + time_span - 1) / time_span;
if (res > max128bit)
return 0; // to behave like previous implementation, may be better return max128bit?
return res.convert_to<currency::wide_difficulty_type>();
}
void get_cut_location_from_len(size_t length, size_t& cut_begin, size_t& cut_end, size_t REDEF_DIFFICULTY_WINDOW, size_t REDEF_DIFFICULTY_CUT_OLD, size_t REDEF_DIFFICULTY_CUT_LAST)
{
if (length <= REDEF_DIFFICULTY_WINDOW)
{
cut_begin = 0;
cut_end = length;
}
else
{
cut_begin = REDEF_DIFFICULTY_WINDOW - REDEF_DIFFICULTY_CUT_LAST + 1;
cut_end = cut_begin + (REDEF_DIFFICULTY_WINDOW - (REDEF_DIFFICULTY_CUT_OLD + REDEF_DIFFICULTY_CUT_LAST));
}
}
currency::wide_difficulty_type bbr_next_difficulty_configurable(std::vector<uint64_t>& timestamps, std::vector<currency::wide_difficulty_type>& cumulative_difficulties, size_t target_seconds, size_t REDEF_DIFFICULTY_WINDOW, size_t REDEF_DIFFICULTY_CUT_OLD, size_t REDEF_DIFFICULTY_CUT_LAST)
{
// timestamps - first is latest, back - is oldest timestamps
//cutoff DIFFICULTY_LAG
if (timestamps.size() > REDEF_DIFFICULTY_WINDOW)
{
timestamps.resize(REDEF_DIFFICULTY_WINDOW);
cumulative_difficulties.resize(REDEF_DIFFICULTY_WINDOW);
}
size_t length = timestamps.size();
CHECK_AND_ASSERT_MES(length == cumulative_difficulties.size(), 0, "Check \"length == cumulative_difficulties.size()\" failed");
if (length <= 1) {
return BBR_DIFFICULTY_STARTER;
}
CHECK_AND_ASSERT_THROW_MES(REDEF_DIFFICULTY_WINDOW >= 2, "Window is too small");
CHECK_AND_ASSERT_MES(length <= REDEF_DIFFICULTY_WINDOW, 0, "length <= REDEF_DIFFICULTY_WINDOW check failed, length=" << length);
sort(timestamps.begin(), timestamps.end(), std::greater<uint64_t>());
size_t cut_begin, cut_end;
CHECK_AND_ASSERT_THROW_MES( (REDEF_DIFFICULTY_CUT_OLD + REDEF_DIFFICULTY_CUT_LAST) <= REDEF_DIFFICULTY_WINDOW - 2, "Cut length is too large");
get_cut_location_from_len(length, cut_begin, cut_end, REDEF_DIFFICULTY_WINDOW, REDEF_DIFFICULTY_CUT_OLD, REDEF_DIFFICULTY_CUT_LAST);
CHECK_AND_ASSERT_THROW_MES(/*cut_begin >= 0 &&*/ cut_begin + 2 <= cut_end && cut_end <= length, "validation in next_difficulty is failed");
uint64_t time_span = timestamps[cut_begin] - timestamps[cut_end - 1];
if (time_span == 0) {
time_span = 1;
}
currency::wide_difficulty_type total_work = cumulative_difficulties[cut_begin] - cumulative_difficulties[cut_end - 1];
boost::multiprecision::uint256_t res = (boost::multiprecision::uint256_t(total_work) * target_seconds + time_span - 1) / time_span;
if (res > max128bit)
return 0; // to behave like previous implementation, may be better return max128bit?
return res.convert_to<currency::wide_difficulty_type>();
}
currency::wide_difficulty_type bbr_next_difficulty_composit(std::vector<uint64_t>& timestamps, std::vector<currency::wide_difficulty_type>& cumulative_difficulties, size_t target_seconds, size_t REDEF_DIFFICULTY_WINDOW, size_t REDEF_DIFFICULTY_CUT_OLD, size_t REDEF_DIFFICULTY_CUT_LAST)
{
sort(timestamps.begin(), timestamps.end(), std::greater<uint64_t>());
std::vector<uint64_t> timestamps_local = timestamps;
currency::wide_difficulty_type dif = bbr_next_difficulty_configurable(timestamps_local, cumulative_difficulties, target_seconds, REDEF_DIFFICULTY_WINDOW, REDEF_DIFFICULTY_CUT_OLD, REDEF_DIFFICULTY_CUT_LAST);
currency::wide_difficulty_type dif2 = bbr_next_difficulty_configurable(timestamps_local, cumulative_difficulties, target_seconds, 200, 5, 5);
currency::wide_difficulty_type dif3 = bbr_next_difficulty_configurable(timestamps_local, cumulative_difficulties, target_seconds, 40, 1, 1);
return (dif3 + dif2 + dif) / 3;
}
currency::wide_difficulty_type bbr_next_difficulty2(std::vector<uint64_t>& timestamps, std::vector<currency::wide_difficulty_type>& cumulative_difficulties, size_t target_seconds)
{
return bbr_next_difficulty_configurable(timestamps, cumulative_difficulties, target_seconds, NEW_DIFFICULTY_WINDOW, NEW_DIFFICULTY_CUT_OLD, NEW_DIFFICULTY_CUT_LAST);
}
uint64_t get_next_timestamp_by_difficulty_and_hashrate(uint64_t last_timestamp, currency::wide_difficulty_type difficulty, uint64_t hashrate)
{
uint64_t seconds = (difficulty / hashrate).convert_to<uint64_t>();
return last_timestamp + seconds;
}
void print_blocks(const std::vector<std::vector<uint64_t>>& blocks, const std::string& res_path)
{
std::stringstream ss;
for (size_t i = 0; i != blocks.size(); i++)
{
ss << std::left << std::setw(10) << i << std::left << std::setw(15) << blocks[i][0];
for (size_t j = 1; j != blocks[i].size()-1; j++)
{
ss << std::left << std::setw(15) << blocks[i][j];
}
ss << std::left << std::setw(20) << blocks[i][blocks[i].size() - 1] << ENDL;
}
file_io_utils::save_string_to_file(res_path, ss.str());
LOG_PRINT_L0("Done, saved to file " << res_path);
}
uint64_t get_hashrate_by_timestamp(const std::map<uint64_t, uint64_t> timestamp_to_hashrate, uint64_t timestamp)
{
auto it = timestamp_to_hashrate.lower_bound(timestamp);
if (it == timestamp_to_hashrate.end())
{
return 0;
}
if(it->first == timestamp)
return it->second;
if (it == timestamp_to_hashrate.begin())
{
LOG_ERROR("Internal error, lower_bound returned begin for timestamp " << timestamp);
return 0;
}
return (--it)->second;;
}
template<typename cb_t>
void perform_simulation_for_function(const std::map<uint64_t, uint64_t>& timestamp_to_hashrate, uint64_t index_in_result, const std::vector<std::vector<uint64_t>>& blocks, std::vector<std::vector<uint64_t>>& result_blocks, cb_t cb)
{
std::vector<uint64_t> timestamps;
std::vector<currency::wide_difficulty_type> cumul_difficulties;
timestamps.reserve(4010);
cumul_difficulties.reserve(4010);
timestamps.push_back(blocks[0][0]);
cumul_difficulties.push_back(blocks[0][1] * 120);
currency::wide_difficulty_type curren_difficulty = 0;
size_t index_in_result_blocks = 0;
while (true)
{
uint64_t hr = 0;
for (size_t i = 0; i != 10; i++)
{
if (timestamps.size() < BBR_DIFFICULTY_WINDOW)
{
curren_difficulty = blocks[index_in_result_blocks][1] * 120;
}
else
{
std::vector<uint64_t> backward_timestamps;
backward_timestamps.reserve(BBR_DIFFICULTY_WINDOW);
std::copy(timestamps.rbegin(), timestamps.rbegin() + BBR_DIFFICULTY_WINDOW - 1, std::back_inserter(backward_timestamps));
std::vector<currency::wide_difficulty_type> backward_cumul_difficulties;
backward_cumul_difficulties.reserve(BBR_DIFFICULTY_WINDOW);
std::copy(cumul_difficulties.rbegin(), cumul_difficulties.rbegin() + BBR_DIFFICULTY_WINDOW - 1, std::back_inserter(backward_cumul_difficulties));
uint64_t ts = timestamps.back();
curren_difficulty = cb(backward_timestamps, backward_cumul_difficulties, BBR_DIFFICULTY_TARGET);
}
cumul_difficulties.push_back(cumul_difficulties.back() + curren_difficulty);
hr = get_hashrate_by_timestamp(timestamp_to_hashrate, timestamps.back());
if (!hr)
break;
timestamps.push_back(get_next_timestamp_by_difficulty_and_hashrate(timestamps.back(), curren_difficulty, hr));
}
if (!hr)
break;
result_blocks[index_in_result_blocks][index_in_result] = timestamps.back();
result_blocks[index_in_result_blocks][index_in_result + 1] = curren_difficulty.convert_to<uint64_t>() / 120;
index_in_result_blocks++;
std::cout << index_in_result_blocks << "\r";
}
if (index_in_result_blocks < 410)
{
for (size_t k = index_in_result_blocks; k != 410; k++)
result_blocks[k][index_in_result] = result_blocks[k-1][index_in_result];
}
std::cout << "\n";
}
void run_emulation(const std::string& path)
@ -53,9 +266,54 @@ void run_emulation(const std::string& path)
//0 - timestamp, 1 - difficulty/120, 2 net hashrate (h/s)
std::vector<std::vector<uint64_t>> blocks;
std::vector<std::vector<uint64_t>> result_blocks;
blocks.reserve(401);
result_blocks.reserve(401);
// std::vector<uint64_t> timestamps, timestamps_new;
// std::vector<currency::wide_difficulty_type> cumul_difficulties, cumul_difficulties_new;
// timestamps.reserve(4010);
// cumul_difficulties.reserve(4010);
// timestamps_new.reserve(4010);
// cumul_difficulties_new.reserve(4010);
parse_file(path, blocks, 500);
result_blocks.resize(blocks.size() * 2);
for (auto& b : result_blocks) {b.resize(20);}
std::map<uint64_t, uint64_t> timestamp_to_hashrate;
for (uint64_t b_no = 0; b_no != blocks.size(); b_no++)
{
auto& b_line = blocks[b_no];
timestamp_to_hashrate[b_line[0]] = b_line[2];
result_blocks[b_no][0] = b_line[0];
result_blocks[b_no][1] = b_line[2];
}
uint64_t current_index = 2;
#define PERFORME_SIMULATION_FOR_FUNCTION(func_name, window_size, cut_old, cut_new ) \
perform_simulation_for_function(timestamp_to_hashrate, current_index, blocks, result_blocks, \
[&](std::vector<uint64_t>& timestamps, std::vector<currency::wide_difficulty_type>& cumulative_difficulties, size_t target_seconds) \
{ \
return func_name(timestamps, cumulative_difficulties, target_seconds, window_size, cut_old, cut_new); \
}); \
current_index+=2;
PERFORME_SIMULATION_FOR_FUNCTION(bbr_next_difficulty_configurable, BBR_DIFFICULTY_WINDOW, BBR_DIFFICULTY_CUT, BBR_DIFFICULTY_CUT);
PERFORME_SIMULATION_FOR_FUNCTION(bbr_next_difficulty_configurable, 500, 60, 60);
PERFORME_SIMULATION_FOR_FUNCTION(bbr_next_difficulty_configurable, 300, 60, 60);
PERFORME_SIMULATION_FOR_FUNCTION(bbr_next_difficulty_composit, 720, 60, 60);
print_blocks(result_blocks, path + "result.txt");
LOG_PRINT_L0("Done");
}
void run_difficulty_analysis(const std::string& path)
{
//hash_rate_analysis(path);
run_emulation(path);
}