1
0
Fork 0
forked from lthn/blockchain

Merge branch 'develop' into libmdbx

This commit is contained in:
cryptozoidberg 2019-09-26 15:04:39 +02:00
commit 7eb7849f86
No known key found for this signature in database
GPG key ID: 22DEB97A54C6FDEC
101 changed files with 3627 additions and 1855 deletions

View file

@ -11,10 +11,9 @@ set_property(TARGET upnpc-static PROPERTY FOLDER "contrib/miniupnp")
set_property(TARGET zlibstatic PROPERTY FOLDER "contrib")
set_property(TARGET ${DB_ENGINE} PROPERTY FOLDER "contrib")
if(MSVC)
set_property(TARGET upnpc-static APPEND_STRING PROPERTY COMPILE_FLAGS " -wd4244 -wd4267")
else()
set_property(TARGET upnpc-static APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-undef -Wno-unused-result -Wno-unused-value")
set_property(TARGET upnpc-static APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-undef -Wno-unused-result -Wno-unused-value -Wno-implicit-fallthrough -Wno-discarded-qualifiers ")
set_property(TARGET zlibstatic APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-undef -Wno-unused-result -Wno-unused-value -Wno-implicit-fallthrough -Wno-discarded-qualifiers ")
endif()

View file

@ -4,7 +4,7 @@ set (lmdb_sources mdb.c midl.c)
include_directories("${CMAKE_CURRENT_SOURCE_DIR}")
if(NOT MSVC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-missing-field-initializers -Wno-missing-braces -Wno-aggregate-return")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-missing-field-initializers -Wno-missing-braces -Wno-aggregate-return -Wno-discarded-qualifiers -Wno-unused-but-set-variable -Wno-implicit-fallthrough -Wno-maybe-uninitialized ")
endif()
if(FREEBSD)
add_definitions(-DMDB_DSYNC=O_SYNC)

View file

@ -52,6 +52,7 @@
#endif
#include "include_base_utils.h"
#include "string_coding.h"
namespace epee
{
@ -222,13 +223,23 @@ namespace file_io_utils
return str_result;
}
#endif
inline const std::wstring& convert_utf8_to_wstring_if_needed(const std::wstring& s)
{
return s;
}
inline std::wstring convert_utf8_to_wstring_if_needed(const std::string& s)
{
return epee::string_encoding::utf8_to_wstring(s);
}
template<class t_string>
bool is_file_exist(const t_string& path)
{
boost::filesystem::path p(path);
return boost::filesystem::exists(p);
}
{
boost::filesystem::path p(convert_utf8_to_wstring_if_needed(path));
return boost::filesystem::exists(p);
}
/*
inline
@ -261,19 +272,18 @@ namespace file_io_utils
template<class t_string>
bool save_string_to_file_throw(const t_string& path_to_file, const std::string& str)
{
//std::ofstream fstream;
boost::filesystem::ofstream fstream;
fstream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
fstream.open(path_to_file, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc);
fstream << str;
fstream.close();
return true;
//std::ofstream fstream;
boost::filesystem::ofstream fstream;
fstream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
fstream.open(convert_utf8_to_wstring_if_needed(path_to_file), std::ios_base::binary | std::ios_base::out | std::ios_base::trunc);
fstream << str;
fstream.close();
return true;
}
template<class t_string>
bool save_string_to_file(const t_string& path_to_file, const std::string& str)
{
try
{
return save_string_to_file_throw(path_to_file, str);
@ -290,13 +300,13 @@ namespace file_io_utils
}
template<class t_string>
bool load_file_to_string(const t_string& path_to_file, std::string& target_str)
bool load_file_to_string(const t_string& path_to_file, std::string& target_str)
{
try
{
boost::filesystem::ifstream fstream;
//fstream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
fstream.open(path_to_file, std::ios_base::binary | std::ios_base::in | std::ios::ate);
fstream.open(convert_utf8_to_wstring_if_needed(path_to_file), std::ios_base::binary | std::ios_base::in | std::ios::ate);
if (!fstream.good())
return false;
std::ifstream::pos_type file_size = fstream.tellg();
@ -315,7 +325,6 @@ namespace file_io_utils
fstream.close();
return true;
}
catch (...)
{
return false;
@ -353,7 +362,7 @@ namespace file_io_utils
bool get_file_time(const std::string& path_to_file, OUT time_t& ft)
{
boost::system::error_code ec;
ft = boost::filesystem::last_write_time(boost::filesystem::path(path_to_file), ec);
ft = boost::filesystem::last_write_time(epee::string_encoding::utf8_to_wstring(path_to_file), ec);
if(!ec)
return true;
else
@ -364,7 +373,7 @@ namespace file_io_utils
bool set_file_time(const std::string& path_to_file, const time_t& ft)
{
boost::system::error_code ec;
boost::filesystem::last_write_time(boost::filesystem::path(path_to_file), ft, ec);
boost::filesystem::last_write_time(epee::string_encoding::utf8_to_wstring(path_to_file), ft, ec);
if(!ec)
return true;
else
@ -380,16 +389,18 @@ namespace file_io_utils
typedef int native_filesystem_handle;
#endif
// uses UTF-8 for unicode names for all systems
inline bool open_and_lock_file(const std::string file_path, native_filesystem_handle& h_file)
{
#ifdef WIN32
h_file = ::CreateFileA(file_path.c_str(), // name of the write
GENERIC_WRITE, // open for writing
0, // do not share
NULL, // default security
OPEN_ALWAYS, // create new file only
FILE_ATTRIBUTE_NORMAL, // normal file
NULL); // no attr. template
std::wstring file_path_w = epee::string_encoding::utf8_to_wstring(file_path);
h_file = ::CreateFileW(file_path_w.c_str(), // name of the file
GENERIC_WRITE, // open for writing
0, // do not share
NULL, // default security
OPEN_ALWAYS, // create new file only
FILE_ATTRIBUTE_NORMAL, // normal file
NULL); // no attr. template
if (h_file == INVALID_HANDLE_VALUE)
return false;
else
@ -465,20 +476,21 @@ namespace file_io_utils
bool copy_file(const std::string& source, const std::string& destination)
{
boost::system::error_code ec;
boost::filesystem::copy_file(source, destination, ec);
boost::filesystem::copy_file(epee::string_encoding::utf8_to_wstring(source), epee::string_encoding::utf8_to_wstring(destination), ec);
if (ec)
return false;
else
return true;
}
inline
bool append_string_to_file(const std::string& path_to_file, const std::string& str)
{
try
{
std::ofstream fstream;
boost::filesystem::ofstream fstream;
fstream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
fstream.open(path_to_file.c_str(), std::ios_base::binary | std::ios_base::out | std::ios_base::app);
fstream.open(epee::string_encoding::utf8_to_wstring(path_to_file), std::ios_base::binary | std::ios_base::out | std::ios_base::app);
fstream << str;
fstream.close();
return true;
@ -550,7 +562,7 @@ namespace file_io_utils
{
boost::filesystem::directory_iterator end_itr; // default construction yields past-the-end
for ( boost::filesystem::directory_iterator itr( path ); itr != end_itr; ++itr )
for ( boost::filesystem::directory_iterator itr( epee::string_encoding::utf8_to_wstring(path) ); itr != end_itr; ++itr )
{
if ( only_files && boost::filesystem::is_directory(itr->status()) )
{

View file

@ -65,6 +65,7 @@ DISABLE_VS_WARNINGS(4100)
#include "syncobj.h"
#include "sync_locked_object.h"
#include "string_coding.h"
#define LOG_LEVEL_SILENT -1
@ -694,13 +695,13 @@ namespace log_space
class file_output_stream : public ibase_log_stream
{
public:
typedef std::map<std::string, std::ofstream*> named_log_streams;
typedef std::map<std::string, boost::filesystem::ofstream*> named_log_streams;
file_output_stream( std::string default_log_file_name, std::string log_path )
file_output_stream( const std::string& default_log_file_name, const std::string& log_path )
{
m_default_log_filename = default_log_file_name;
m_max_logfile_size = 0;
m_default_log_path = log_path;
m_default_log_path_w = epee::string_encoding::utf8_to_wstring(log_path);
m_pdefault_file_stream = add_new_stream_and_open(default_log_file_name.c_str());
}
@ -718,20 +719,22 @@ namespace log_space
}
private:
named_log_streams m_log_file_names;
std::string m_default_log_path;
std::ofstream* m_pdefault_file_stream;
std::wstring m_default_log_path_w;
boost::filesystem::ofstream* m_pdefault_file_stream;
std::string m_log_rotate_cmd;
std::string m_default_log_filename;
uint64_t m_max_logfile_size;
std::ofstream* add_new_stream_and_open(const char* pstream_name)
// gets utf-8 encoded string
boost::filesystem::ofstream* add_new_stream_and_open(const char* pstream_name)
{
//log_space::rotate_log_file((m_default_log_path + "\\" + pstream_name).c_str());
boost::system::error_code ec;
boost::filesystem::create_directories(m_default_log_path, ec);
std::ofstream* pstream = (m_log_file_names[pstream_name] = new std::ofstream);
std::string target_path = m_default_log_path + "/" + pstream_name;
boost::filesystem::create_directories(m_default_log_path_w, ec);
boost::filesystem::ofstream* pstream = (m_log_file_names[pstream_name] = new boost::filesystem::ofstream);
std::wstring target_path = m_default_log_path_w + L"/" + epee::string_encoding::utf8_to_wstring(pstream_name);
pstream->open( target_path.c_str(), std::ios_base::out | std::ios::app /*ios_base::trunc */);
if(pstream->fail())
return NULL;
@ -754,7 +757,7 @@ namespace log_space
virtual bool out_buffer( const char* buffer, int buffer_len, int log_level, int color, const char* plog_name = NULL )
{
std::ofstream* m_target_file_stream = m_pdefault_file_stream;
boost::filesystem::ofstream* m_target_file_stream = m_pdefault_file_stream;
if(plog_name)
{ //find named stream
named_log_streams::iterator it = m_log_file_names.find(plog_name);
@ -769,9 +772,10 @@ namespace log_space
m_target_file_stream->write(buffer, buffer_len );
m_target_file_stream->flush();
/*
if(m_max_logfile_size)
{
std::ofstream::pos_type pt = m_target_file_stream->tellp();
boost::filesystem::ofstream::pos_type pt = m_target_file_stream->tellp();
uint64_t current_sz = pt;
if(current_sz > m_max_logfile_size)
{
@ -818,12 +822,13 @@ namespace log_space
misc_utils::call_sys_cmd(m_log_rotate_cmd_local_copy);
}
m_target_file_stream->open( (m_default_log_path + "/" + log_file_name).c_str(), std::ios_base::out | std::ios::app /*ios_base::trunc */);
m_target_file_stream->open( (m_default_log_path + "/" + log_file_name).c_str(), std::ios_base::out | std::ios::app / * ios_base::trunc * /);
if(m_target_file_stream->fail())
return false;
}
}
*/
return true;
}
int get_type(){return LOGGER_FILE;}

View file

@ -475,7 +475,7 @@ bool boosted_tcp_server<t_protocol_handler>::init_server(uint32_t port, const st
}
//-----------------------------------------------------------------------------
PUSH_WARNINGS
DISABLE_GCC_WARNING(maybe - uninitialized)
DISABLE_GCC_WARNING(maybe-uninitialized)
template<class t_protocol_handler>
bool boosted_tcp_server<t_protocol_handler>::init_server(const std::string port, const std::string& address)
{

View file

@ -42,6 +42,7 @@
#include <boost/filesystem.hpp>
#include "warnings.h"
#include "auto_val_init.h"
#include "string_coding.h"
#ifndef OUT
@ -536,38 +537,15 @@ POP_WARNINGS
return module_folder;
}
//----------------------------------------------------------------------------
#ifdef _WIN32
inline std::string get_current_module_path()
inline bool set_module_name_and_folder(const std::string& path_to_process_)
{
char pname [5000] = {0};
GetModuleFileNameA( NULL, pname, sizeof(pname));
pname[sizeof(pname)-1] = 0; //be happy ;)
return pname;
}
#endif
//----------------------------------------------------------------------------
inline bool set_module_name_and_folder(const std::string& path_to_process_)
{
std::string path_to_process = path_to_process_;
boost::system::error_code ec;
path_to_process = boost::filesystem::canonical(path_to_process, ec).string();
#ifdef _WIN32
path_to_process = get_current_module_path();
#endif
std::string::size_type a = path_to_process.rfind( '\\' );
if(a == std::string::npos )
{
a = path_to_process.rfind( '/' );
}
if ( a != std::string::npos )
{
get_current_module_name() = path_to_process.substr(a+1, path_to_process.size());
get_current_module_folder() = path_to_process.substr(0, a);
return true;
}else
return false;
boost::filesystem::path path(epee::string_encoding::utf8_to_wstring(path_to_process_));
}
get_current_module_folder() = epee::string_encoding::wstring_to_utf8(path.parent_path().wstring());
get_current_module_name() = epee::string_encoding::wstring_to_utf8(path.filename().wstring());
return true;
}
//----------------------------------------------------------------------------
inline bool trim_left(std::string& str)
{

View file

@ -145,7 +145,11 @@ epoch_context_full* create_epoch_context(
char* const alloc_data = static_cast<char*>(std::calloc(1, alloc_size));
if (!alloc_data)
return nullptr; // Signal out-of-memory by returning null pointer.
{
LOG_CUSTOM_WITH_CALLSTACK("CRITICAL: std::calloc(" << alloc_size << ") failed in create_epoch_context()", 0);
return nullptr; // Signal out-of-memory by returning null pointer.
}
LOG_CUSTOM("context for epoch " << epoch_number << " allocated, size: " << alloc_size << " bytes", 0);
hash512* const light_cache = reinterpret_cast<hash512*>(alloc_data + context_alloc_size);
const hash256 epoch_seed = calculate_epoch_seed(epoch_number);
@ -373,6 +377,33 @@ search_result search(const epoch_context_full& context, const hash256& header_ha
}
return {};
}
custom_log_level_function*& access_custom_log_level_function()
{
static custom_log_level_function* p_custom_log_level_function = nullptr;
return p_custom_log_level_function;
}
custom_log_function*& access_custom_log_function()
{
static custom_log_function* p_custom_log_function = nullptr;
return p_custom_log_function;
}
int get_custom_log_level()
{
if (access_custom_log_level_function() != nullptr)
return access_custom_log_level_function()();
return -1;
}
void custom_log(const std::string& m, bool add_callstack)
{
if (access_custom_log_function() != nullptr)
access_custom_log_function()(m, add_callstack);
}
} // namespace ethash
using namespace ethash;
@ -434,6 +465,8 @@ void ethash_destroy_epoch_context_full(epoch_context_full* context) noexcept
void ethash_destroy_epoch_context(epoch_context* context) noexcept
{
LOG_CUSTOM("context for epoch " << context->epoch_number << " is about to be freed", 0);
context->~epoch_context();
std::free(context);
}

View file

@ -20,6 +20,8 @@
#include <cstdint>
#include <cstring>
#include <memory>
#include <string>
#include <sstream>
namespace ethash
{
@ -157,4 +159,33 @@ const epoch_context& get_global_epoch_context(int epoch_number);
/// Get global shared epoch context with full dataset initialized.
std::shared_ptr<epoch_context_full> get_global_epoch_context_full(int epoch_number);
typedef int (custom_log_level_function)();
typedef void (custom_log_function)(const std::string& m, bool add_callstack);
custom_log_level_function*& access_custom_log_level_function();
custom_log_function*& access_custom_log_function();
int get_custom_log_level();
void custom_log(const std::string& m, bool add_callstack);
#define LOG_CUSTOM(msg, level) \
{ \
if (level <= ethash::get_custom_log_level()) \
{ \
std::stringstream ss; \
ss << msg << std::endl; \
ethash::custom_log(ss.str(), false); \
} \
}
#define LOG_CUSTOM_WITH_CALLSTACK(msg, level) \
{ \
if (level <= ethash::get_custom_log_level()) \
{ \
std::stringstream ss; \
ss << msg << std::endl; \
ethash::custom_log(ss.str(), true); \
} \
}
} // namespace ethash

View file

@ -21,8 +21,8 @@ namespace tools
bool serialize_obj_to_file(t_object& obj, const std::string& file_path)
{
TRY_ENTRY();
std::ofstream data_file;
data_file.open( file_path , std::ios_base::binary | std::ios_base::out| std::ios::trunc);
boost::filesystem::ofstream data_file;
data_file.open( epee::string_encoding::utf8_to_wstring(file_path) , std::ios_base::binary | std::ios_base::out| std::ios::trunc);
if(data_file.fail())
return false;
@ -64,8 +64,8 @@ namespace tools
{
TRY_ENTRY();
std::ifstream data_file;
data_file.open( file_path, std::ios_base::binary | std::ios_base::in);
boost::filesystem::ifstream data_file;
data_file.open( epee::string_encoding::utf8_to_wstring(file_path), std::ios_base::binary | std::ios_base::in);
if(data_file.fail())
return false;
boost::archive::binary_iarchive a(data_file);

View file

@ -659,28 +659,27 @@ namespace tools
{
return bdb.size(m_h);
}
size_t clear()
bool clear()
{
bdb.clear(m_h);
bool result = bdb.clear(m_h);
m_isolation.isolated_write_access<bool>([&](){
size_cache_valid = false;
return true;
});
return true;
return result;
}
bool erase_validate(const t_key& k)
{
auto res_ptr = this->get(k);
bdb.erase(m_h, k);
bool result = bdb.erase(m_h, k);
m_isolation.isolated_write_access<bool>([&](){
size_cache_valid = false;
return true;
});
return static_cast<bool>(res_ptr);
return result;
}
void erase(const t_key& k)
{
bdb.erase(m_h, k);
@ -861,13 +860,10 @@ namespace tools
operator t_value() const
{
static_assert(std::is_pod<t_value>::value, "t_value must be a POD type.");
std::shared_ptr<const t_value> value_ptr = m_accessor.template explicit_get<t_key, t_value, access_strategy_selector<is_t_strategy> >(m_key);
if (value_ptr.get())
return *value_ptr.get();
std::shared_ptr<const t_value> vptr = m_accessor.template explicit_get<t_key, t_value, access_strategy_selector<false> >(m_key);
if (vptr.get())
{
return *vptr.get();
}
return AUTO_VAL_INIT(t_value());
}
};

View file

@ -51,10 +51,6 @@ namespace tools
CHECK_AND_ASSERT_MESS_LMDB_DB(res, false, "Unable to mdb_env_set_mapsize");
m_path = path_;
#ifdef WIN32
m_path = epee::string_encoding::convert_ansii_to_utf8(m_path);
#endif
CHECK_AND_ASSERT_MES(tools::create_directories_if_necessary(m_path), false, "create_directories_if_necessary failed: " << m_path);
res = mdb_env_open(m_penv, m_path.c_str(), MDB_NORDAHEAD , 0644);

265
src/common/ntp.cpp Normal file
View file

@ -0,0 +1,265 @@
// Copyright (c) 2019 Zano Project
// Note: class udp_blocking_client is a slightly modified version of an example
// taken from https://www.boost.org/doc/libs/1_53_0/doc/html/boost_asio/example/timeouts/blocking_udp_client.cpp
//
// Copyright (c) 2003-2012 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include <cstdlib>
#include <iostream>
#include <boost/asio/deadline_timer.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/udp.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/array.hpp>
#include <epee/include/misc_log_ex.h>
#include <chrono>
#include "ntp.h"
using boost::asio::deadline_timer;
using boost::asio::ip::udp;
//----------------------------------------------------------------------
//
// This class manages socket timeouts by applying the concept of a deadline.
// Each asynchronous operation is given a deadline by which it must complete.
// Deadlines are enforced by an "actor" that persists for the lifetime of the
// client object:
//
// +----------------+
// | |
// | check_deadline |<---+
// | | |
// +----------------+ | async_wait()
// | |
// +---------+
//
// If the actor determines that the deadline has expired, any outstanding
// socket operations are cancelled. The socket operations themselves are
// implemented as transient actors:
//
// +---------------+
// | |
// | receive |
// | |
// +---------------+
// |
// async_- | +----------------+
// receive() | | |
// +--->| handle_receive |
// | |
// +----------------+
//
// The client object runs the io_service to block thread execution until the
// actor completes.
//
namespace
{
class udp_blocking_client
{
public:
udp_blocking_client(const udp::endpoint& listen_endpoint, udp::socket& socket, boost::asio::io_service& io_service)
: socket_(socket),
io_service_(io_service),
deadline_(io_service)
{
// No deadline is required until the first socket operation is started. We
// set the deadline to positive infinity so that the actor takes no action
// until a specific deadline is set.
deadline_.expires_at(boost::posix_time::pos_infin);
// Start the persistent actor that checks for deadline expiry.
check_deadline();
}
std::size_t receive(const boost::asio::mutable_buffer& buffer,
boost::posix_time::time_duration timeout, boost::system::error_code& ec)
{
// Set a deadline for the asynchronous operation.
deadline_.expires_from_now(timeout);
// Set up the variables that receive the result of the asynchronous
// operation. The error code is set to would_block to signal that the
// operation is incomplete. Asio guarantees that its asynchronous
// operations will never fail with would_block, so any other value in
// ec indicates completion.
ec = boost::asio::error::would_block;
std::size_t length = 0;
// Start the asynchronous operation itself. The handle_receive function
// used as a callback will update the ec and length variables.
socket_.async_receive(boost::asio::buffer(buffer),
boost::bind(&udp_blocking_client::handle_receive, _1, _2, &ec, &length));
// Block until the asynchronous operation has completed.
do io_service_.run_one(); while (ec == boost::asio::error::would_block);
return length;
}
private:
void check_deadline()
{
// Check whether the deadline has passed. We compare the deadline against
// the current time since a new asynchronous operation may have moved the
// deadline before this actor had a chance to run.
if (deadline_.expires_at() <= deadline_timer::traits_type::now())
{
// The deadline has passed. The outstanding asynchronous operation needs
// to be cancelled so that the blocked receive() function will return.
//
// Please note that cancel() has portability issues on some versions of
// Microsoft Windows, and it may be necessary to use close() instead.
// Consult the documentation for cancel() for further information.
socket_.cancel();
// There is no longer an active deadline. The expiry is set to positive
// infinity so that the actor takes no action until a new deadline is set.
deadline_.expires_at(boost::posix_time::pos_infin);
}
// Put the actor back to sleep.
deadline_.async_wait(boost::bind(&udp_blocking_client::check_deadline, this));
}
static void handle_receive(
const boost::system::error_code& ec, std::size_t length,
boost::system::error_code* out_ec, std::size_t* out_length)
{
*out_ec = ec;
*out_length = length;
}
private:
boost::asio::io_service& io_service_;
udp::socket& socket_;
deadline_timer deadline_;
};
#pragma pack(push, 1)
struct ntp_packet
{
uint8_t li_vn_mode; // Eight bits. li, vn, and mode.
// li. Two bits. Leap indicator.
// vn. Three bits. Version number of the protocol.
// mode. Three bits. Client will pick mode 3 for client.
uint8_t stratum; // Eight bits. Stratum level of the local clock.
uint8_t poll; // Eight bits. Maximum interval between successive messages.
uint8_t precision; // Eight bits. Precision of the local clock.
uint32_t rootDelay; // 32 bits. Total round trip delay time.
uint32_t rootDispersion; // 32 bits. Max error aloud from primary clock source.
uint32_t refId; // 32 bits. Reference clock identifier.
uint32_t refTm_s; // 32 bits. Reference time-stamp seconds.
uint32_t refTm_f; // 32 bits. Reference time-stamp fraction of a second.
uint64_t orig_tm; // 64 bits. Originate time-stamp (set by client)
uint32_t rxTm_s; // 32 bits. Received time-stamp seconds.
uint32_t rxTm_f; // 32 bits. Received time-stamp fraction of a second.
uint32_t txTm_s; // 32 bits and the most important field the client cares about. Transmit time-stamp seconds.
uint32_t txTm_f; // 32 bits. Transmit time-stamp fraction of a second.
}; // Total: 384 bits or 48 bytes.
#pragma pack(pop)
static_assert(sizeof(ntp_packet) == 48, "ntp_packet has invalid size");
} // namespace
namespace tools
{
int64_t get_ntp_time(const std::string& host_name, size_t timeout_sec)
{
try
{
boost::asio::io_service io_service;
boost::asio::ip::udp::resolver resolver(io_service);
boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), host_name, "ntp");
boost::asio::ip::udp::endpoint receiver_endpoint = *resolver.resolve(query);
boost::asio::ip::udp::socket socket(io_service);
socket.open(boost::asio::ip::udp::v4());
ntp_packet packet_sent = AUTO_VAL_INIT(packet_sent);
packet_sent.li_vn_mode = 0x1b;
auto packet_sent_time = std::chrono::high_resolution_clock::now();
socket.send_to(boost::asio::buffer(&packet_sent, sizeof packet_sent), receiver_endpoint);
ntp_packet packet_received = AUTO_VAL_INIT(packet_received);
boost::asio::ip::udp::endpoint sender_endpoint;
udp_blocking_client ubc(sender_endpoint, socket, io_service);
boost::system::error_code ec;
size_t len = ubc.receive(boost::asio::buffer(&packet_received, sizeof packet_received), boost::posix_time::seconds(timeout_sec), ec);
if (ec)
{
LOG_PRINT_L3("NTP: get_ntp_time(" << host_name << "): boost error: " << ec.message());
return 0;
}
auto packet_received_time = std::chrono::high_resolution_clock::now();
int64_t roundtrip_mcs = std::chrono::duration_cast<std::chrono::microseconds>(packet_received_time - packet_sent_time).count();
uint64_t roundtrip_s = roundtrip_mcs > 2000000 ? roundtrip_mcs / 2000000 : 0;
time_t ntp_time = ntohl(packet_received.txTm_s);
if (ntp_time <= 2208988800U)
{
LOG_PRINT_L3("NTP: get_ntp_time(" << host_name << "): wrong txTm_s: " << packet_received.txTm_s);
return 0;
}
// LOG_PRINT_L2("NTP: get_ntp_time(" << host_name << "): RAW time received: " << ntp_time << ", refTm_s: " << ntohl(packet_received.refTm_s) << ", stratum: " << packet_received.stratum << ", round: " << roundtrip_mcs);
ntp_time -= 2208988800U; // Unix time starts from 01/01/1970 == 2208988800U
ntp_time += roundtrip_s;
return ntp_time;
}
catch (const std::exception& e)
{
LOG_PRINT_L2("NTP: get_ntp_time(" << host_name << "): exception: " << e.what());
return 0;
}
catch (...)
{
LOG_PRINT_L2("NTP: get_ntp_time(" << host_name << "): unknown exception");
return 0;
}
}
#define TIME_SYNC_NTP_SERVERS "time1.google.com", "time2.google.com", "time3.google.com", "time4.google.com" /* , "pool.ntp.org" -- we need to request a vender zone before using this pool */
#define TIME_SYNC_NTP_TIMEOUT_SEC 5
#define TIME_SYNC_NTP_ATTEMPTS_COUNT 3 // max number of attempts when getting time from NTP server
int64_t get_ntp_time()
{
static const std::vector<std::string> ntp_servers { TIME_SYNC_NTP_SERVERS };
for (size_t att = 0; att < TIME_SYNC_NTP_ATTEMPTS_COUNT; ++att)
{
const std::string& ntp_server = ntp_servers[att % ntp_servers.size()];
LOG_PRINT_L3("NTP: trying to get time from " << ntp_server);
int64_t time = get_ntp_time(ntp_server, TIME_SYNC_NTP_TIMEOUT_SEC);
if (time > 0)
{
LOG_PRINT_L3("NTP: " << ntp_server << " responded with " << time << " (" << epee::misc_utils::get_time_str_v2(time) << ")");
return time;
}
}
LOG_PRINT_RED("NTP: unable to get time from NTP with " << TIME_SYNC_NTP_ATTEMPTS_COUNT << " trials", LOG_LEVEL_2);
return 0; // smth went wrong
}
} // namespace tools

18
src/common/ntp.h Normal file
View file

@ -0,0 +1,18 @@
// Copyright (c) 2019 Zano Project
#pragma once
#include <cstdint>
#include <string>
namespace tools
{
// requests current time via NTP from 'host_hame' using 'timeout_sec'
// may return zero -- means error
int64_t get_ntp_time(const std::string& host_name, size_t timeout_sec = 5);
// request time via predefined NTP servers
// may return zero -- mean error
int64_t get_ntp_time();
} // namespace tools

View file

@ -24,6 +24,8 @@ using namespace epee;
#include <boost/asio.hpp>
#include "string_coding.h"
namespace tools
{
std::function<void(void)> signal_handler::m_handler;
@ -450,18 +452,22 @@ std::string get_nix_version_display_string()
#ifdef WIN32
std::string get_special_folder_path(int nfolder, bool iscreate)
std::wstring get_special_folder_path_w(int nfolder, bool iscreate)
{
namespace fs = boost::filesystem;
char psz_path[MAX_PATH] = "";
wchar_t psz_path[MAX_PATH] = L"";
if(SHGetSpecialFolderPathA(NULL, psz_path, nfolder, iscreate))
if (SHGetSpecialFolderPathW(NULL, psz_path, nfolder, iscreate))
{
return psz_path;
}
LOG_ERROR("SHGetSpecialFolderPathA() failed, could not obtain requested path.");
return "";
LOG_ERROR("SHGetSpecialFolderPathW(" << nfolder << ", " << iscreate << ") failed, could not obtain requested path.");
return L"";
}
std::string get_special_folder_path_utf8(int nfolder, bool iscreate)
{
return epee::string_encoding::wstring_to_utf8(get_special_folder_path_w(nfolder, iscreate));
}
#endif
@ -476,9 +482,9 @@ std::string get_nix_version_display_string()
#ifdef WIN32
// Windows
#ifdef _M_X64
config_folder = get_special_folder_path(CSIDL_APPDATA, true) + "/" + CURRENCY_NAME_SHORT;
config_folder = get_special_folder_path_utf8(CSIDL_APPDATA, true) + "/" + CURRENCY_NAME_SHORT;
#else
config_folder = get_special_folder_path(CSIDL_APPDATA, true) + "/" + CURRENCY_NAME_SHORT + "-x86";
config_folder = get_special_folder_path_utf8(CSIDL_APPDATA, true) + "/" + CURRENCY_NAME_SHORT + "-x86";
#endif
#else
std::string pathRet;
@ -518,7 +524,7 @@ std::string get_nix_version_display_string()
std::string wallets_dir;
#ifdef WIN32
// Windows
wallets_dir = get_special_folder_path(CSIDL_PERSONAL, true) + "/" + CURRENCY_NAME_BASE;
wallets_dir = get_special_folder_path_utf8(CSIDL_PERSONAL, true) + "/" + CURRENCY_NAME_BASE;
#else
std::string pathRet;
char* pszHome = getenv("HOME");
@ -553,7 +559,7 @@ std::string get_nix_version_display_string()
{
namespace fs = boost::filesystem;
boost::system::error_code ec;
fs::path fs_path(path);
fs::path fs_path = epee::string_encoding::utf8_to_wstring(path);
if (fs::is_directory(fs_path, ec))
{
return true;
@ -652,38 +658,4 @@ std::string get_nix_version_display_string()
return static_cast<uint64_t>(in.tellg());
}
int64_t get_ntp_time(const std::string& host_name)
{
try
{
boost::asio::io_service io_service;
boost::asio::ip::udp::resolver resolver(io_service);
boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), host_name, "ntp");
boost::asio::ip::udp::endpoint receiver_endpoint = *resolver.resolve(query);
boost::asio::ip::udp::socket socket(io_service);
socket.open(boost::asio::ip::udp::v4());
boost::array<unsigned char, 48> send_buf = { { 010, 0, 0, 0, 0, 0, 0, 0, 0 } };
socket.send_to(boost::asio::buffer(send_buf), receiver_endpoint);
boost::array<unsigned long, 1024> recv_buf;
boost::asio::ip::udp::endpoint sender_endpoint;
size_t len = socket.receive_from(boost::asio::buffer(recv_buf), sender_endpoint);
time_t time_recv = ntohl((time_t)recv_buf[4]);
time_recv -= 2208988800U; //Unix time starts from 01/01/1970 == 2208988800U
return time_recv;
}
catch (const std::exception& e)
{
LOG_PRINT_L2("get_ntp_time(): exception: " << e.what());
return 0;
}
catch (...)
{
return 0;
}
}
} // namespace tools

View file

@ -17,6 +17,7 @@
#include "crypto/hash.h"
#include "misc_language.h"
#include "p2p/p2p_protocol_defs.h"
#include "ntp.h"
#if defined(WIN32)
#include <dbghelp.h>
@ -274,6 +275,4 @@ namespace tools
static std::function<void(int, void*)> m_fatal_handler;
};
int64_t get_ntp_time(const std::string& host_name);
}

View file

@ -10,14 +10,14 @@
//#include "initializer.h"
#include "random.h"
static void generate_system_random_bytes(size_t n, void *result);
static_assert(RANDOM_STATE_SIZE >= HASH_DATA_AREA, "Invalid RANDOM_STATE_SIZE");
#if defined(_WIN32)
#include <windows.h>
#include <wincrypt.h>
static void generate_system_random_bytes(size_t n, void *result) {
void generate_system_random_bytes(size_t n, void *result) {
HCRYPTPROV prov;
#define must_succeed(x) do if (!(x)) assert(0); while (0)
if(!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
@ -40,7 +40,7 @@ static void generate_system_random_bytes(size_t n, void *result) {
#include <sys/types.h>
#include <unistd.h>
static void generate_system_random_bytes(size_t n, void *result) {
void generate_system_random_bytes(size_t n, void *result) {
int fd;
if ((fd = open("/dev/urandom", O_RDONLY | O_NOCTTY | O_CLOEXEC)) < 0) {
err(EXIT_FAILURE, "open /dev/urandom");
@ -68,7 +68,7 @@ static void generate_system_random_bytes(size_t n, void *result) {
#endif
/* static */ union hash_state state; // NOTE: 'static' is commented out here to be able to store/load global random generator state in tests (see also random_helper.h)
static union hash_state state;
#if !defined(NDEBUG)
static volatile int curstate; /* To catch thread safety problems. */
@ -105,6 +105,57 @@ void grant_random_initialize(void)
}
}
void random_prng_initialize_with_seed(uint64_t seed)
{
grant_random_initialize();
#if !defined(NDEBUG)
assert(curstate == 1);
curstate = 3;
#endif
memset(&state, 0, sizeof state);
memcpy(&state, &seed, sizeof seed);
for(size_t i = 0, count = seed & 31; i < count; ++i)
hash_permutation(&state);
#if !defined(NDEBUG)
assert(curstate == 3);
curstate = 1;
#endif
}
void random_prng_get_state(void *state_buffer, const size_t buffer_size)
{
grant_random_initialize();
#if !defined(NDEBUG)
assert(curstate == 1);
curstate = 4;
#endif
assert(sizeof state == buffer_size);
memcpy(state_buffer, &state, buffer_size);
#if !defined(NDEBUG)
assert(curstate == 4);
curstate = 1;
#endif
}
void random_prng_set_state(const void *state_buffer, const size_t buffer_size)
{
grant_random_initialize();
#if !defined(NDEBUG)
assert(curstate == 1);
curstate = 5;
#endif
assert(sizeof state == buffer_size);
memcpy(&state, state_buffer, buffer_size);
#if !defined(NDEBUG)
assert(curstate == 5);
curstate = 1;
#endif
}
void generate_random_bytes(size_t n, void *result) {
grant_random_initialize();
#if !defined(NDEBUG)

View file

@ -1,3 +1,5 @@
// Copyright (c) 2018-2019 Zano Project
// Copyright (c) 2014-2018 The Boolberry developers
// Copyright (c) 2012-2013 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@ -5,6 +7,31 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
// use the cryptographically secure Pseudo-Random Number Generator provided by the operating system
void generate_system_random_bytes(size_t n, void *result);
void generate_random_bytes(size_t n, void *result);
void grant_random_initialize(void);
// checks if PRNG is initialized and initializes it if necessary
void grant_random_initialize(void);
#define RANDOM_STATE_SIZE 200
// explicitly define USE_INSECURE_RANDOM_RPNG_ROUTINES for using random_initialize_with_seed
#ifdef USE_INSECURE_RANDOM_RPNG_ROUTINES
// reinitializes PRNG with the given seed
// !!!ATTENTION!!!! Improper use of this routine may lead to SECURITY BREACH!
// Use with care and ONLY for tests or debug purposes!
void random_prng_initialize_with_seed(uint64_t seed);
// gets internal RPNG state (state_buffer should be 200 bytes long)
void random_prng_get_state(void *state_buffer, const size_t buffer_size);
// sets internal RPNG state (state_buffer should be 200 bytes long)
// !!!ATTENTION!!!! Improper use of this routine may lead to SECURITY BREACH!
// Use with care and ONLY for tests or debug purposes!
void random_prng_set_state(const void *state_buffer, const size_t buffer_size);
#endif // #ifdef USE_INSECURE_RANDOM_RPNG_ROUTINES

View file

@ -22,17 +22,31 @@ namespace currency
{
//--------------------------------------------------------------
//global object
// crypto::ethash::cache_manager cache;
// void ethash_set_use_dag(bool use_dag)
// {
// cache.set_use_dag(use_dag);
// }
// //------------------------------------------------------------------
// const uint8_t* ethash_get_dag(uint64_t epoch, uint64_t& dag_size)
// {
// return cache.get_dag(epoch, dag_size);
// }
int ethash_custom_log_get_level()
{
return epee::log_space::get_set_log_detalisation_level();
}
//--------------------------------------------------------------
void ethash_custom_log(const std::string& m, bool add_callstack)
{
std::string msg = epee::log_space::log_singletone::get_prefix_entry() + "[ETHASH]" + m;
if (add_callstack)
msg = msg + "callstask: " + epee::misc_utils::get_callstack();
epee::log_space::log_singletone::do_log_message(msg, LOG_LEVEL_0, epee::log_space::console_color_default, true, LOG_DEFAULT_TARGET);
}
//--------------------------------------------------------------
void init_ethash_log_if_necessary()
{
static bool inited = false;
if (inited)
return;
ethash::access_custom_log_level_function() = &ethash_custom_log_get_level;
ethash::access_custom_log_function() = &ethash_custom_log;
inited = true;
}
//------------------------------------------------------------------
int ethash_height_to_epoch(uint64_t height)
{
@ -49,6 +63,7 @@ namespace currency
//--------------------------------------------------------------
crypto::hash get_block_longhash(uint64_t height, const crypto::hash& block_header_hash, uint64_t nonce)
{
init_ethash_log_if_necessary();
int epoch = ethash_height_to_epoch(height);
std::shared_ptr<ethash::epoch_context_full> p_context = progpow::get_global_epoch_context_full(static_cast<int>(epoch));
CHECK_AND_ASSERT_THROW_MES(p_context, "progpow::get_global_epoch_context_full returned null");

View file

@ -155,7 +155,6 @@ void blockchain_storage::init_options(boost::program_options::options_descriptio
command_line::add_arg(desc, arg_db_cache_l2);
}
//------------------------------------------------------------------
uint64_t blockchain_storage::get_block_h_older_then(uint64_t timestamp) const
{
// get avarage block position
@ -223,12 +222,12 @@ bool blockchain_storage::init(const std::string& config_folder, const boost::pro
m_config_folder = config_folder;
// remove old incompartible DB
// remove old incompatible DB
const std::string old_db_folder_path = m_config_folder + "/" CURRENCY_BLOCKCHAINDATA_FOLDERNAME_OLD;
if (boost::filesystem::exists(old_db_folder_path))
if (boost::filesystem::exists(epee::string_encoding::utf8_to_wstring(old_db_folder_path)))
{
LOG_PRINT_YELLOW("Removing old DB in " << old_db_folder_path << "...", LOG_LEVEL_0);
boost::filesystem::remove_all(old_db_folder_path);
boost::filesystem::remove_all(epee::string_encoding::utf8_to_wstring(old_db_folder_path));
}
const std::string db_folder_path = m_config_folder + "/" CURRENCY_BLOCKCHAINDATA_FOLDERNAME;
@ -242,7 +241,7 @@ bool blockchain_storage::init(const std::string& config_folder, const boost::pro
{
// if DB could not be opened -- try to remove the whole folder and re-open DB
LOG_PRINT_YELLOW("Failed to initialize database in folder: " << db_folder_path << ", first attempt", LOG_LEVEL_0);
boost::filesystem::remove_all(db_folder_path);
boost::filesystem::remove_all(epee::string_encoding::utf8_to_wstring(db_folder_path));
res = m_db.open(db_folder_path, cache_size_l1);
CHECK_AND_ASSERT_MES(res, false, "Failed to initialize database in folder: " << db_folder_path << ", second attempt");
}
@ -312,7 +311,7 @@ bool blockchain_storage::init(const std::string& config_folder, const boost::pro
m_db_addr_to_alias.deinit();
m_db_per_block_gindex_incs.deinit();
m_db.close();
size_t files_removed = boost::filesystem::remove_all(db_folder_path);
size_t files_removed = boost::filesystem::remove_all(epee::string_encoding::utf8_to_wstring(db_folder_path));
LOG_PRINT_L1(files_removed << " files at " << db_folder_path << " removed");
// try to re-create DB and re-init containers
@ -1820,10 +1819,19 @@ bool blockchain_storage::is_reorganize_required(const block_extended_info& main_
wide_difficulty_type main_pow_diff_begin = get_last_alt_x_block_cumulative_precise_adj_difficulty(alt_chain_type(), connection_point.height - 1, false);
main_cumul_diff.pow_diff = main_pow_diff_end - main_pow_diff_begin;
//TODO: measurment of precise cumulative difficult
//TODO: measurement of precise cumulative difficult
wide_difficulty_type alt = get_a_to_b_relative_cumulative_difficulty(difficulty_pos_at_split_point, difficulty_pow_at_split_point, alt_cumul_diff, main_cumul_diff);
wide_difficulty_type main = get_a_to_b_relative_cumulative_difficulty(difficulty_pos_at_split_point, difficulty_pow_at_split_point, main_cumul_diff, alt_cumul_diff);
LOG_PRINT_L1("[FORK_CHOICE]: " << ENDL
<< "difficulty_pow_at_split_point:" << difficulty_pow_at_split_point << ENDL
<< "difficulty_pos_at_split_point:" << difficulty_pos_at_split_point << ENDL
<< "alt_cumul_diff.pow_diff:" << alt_cumul_diff.pow_diff << ENDL
<< "alt_cumul_diff.pos_diff:" << alt_cumul_diff.pos_diff << ENDL
<< "main_cumul_diff.pow_diff:" << main_cumul_diff.pow_diff << ENDL
<< "main_cumul_diff.pos_diff:" << main_cumul_diff.pos_diff << ENDL
<< "alt:" << alt << ENDL
<< "main:" << main << ENDL
);
if (main < alt)
return true;
else if (main > alt)
@ -1838,7 +1846,7 @@ bool blockchain_storage::is_reorganize_required(const block_extended_info& main_
if (std::memcmp(&main_chain_bei.stake_hash, &proof_alt, sizeof(main_chain_bei.stake_hash)) >= 0)
return false;
LOG_PRINT_L2("[is_reorganize_required]:TRUE, \"by order of memcmp\" main_stake_hash:" << &main_chain_bei.stake_hash << ", alt_stake_hash" << proof_alt);
LOG_PRINT_L1("[is_reorganize_required]:TRUE, \"by order of memcmp\" main_stake_hash:" << &main_chain_bei.stake_hash << ", alt_stake_hash" << proof_alt);
return true;
}
}
@ -2635,7 +2643,8 @@ void blockchain_storage::print_blockchain_with_tx(uint64_t start_index, uint64_t
{
boost::filesystem::ofstream ss;
ss.exceptions(/*std::ifstream::failbit |*/ std::ifstream::badbit);
ss.open(log_space::log_singletone::get_default_log_folder() + "/blockchain_with_tx.txt", std::ios_base::binary | std::ios_base::out | std::ios_base::trunc);
ss.open(epee::string_encoding::utf8_to_wstring(log_space::log_singletone::get_default_log_folder() + "/blockchain_with_tx.txt"),
std::ios_base::binary | std::ios_base::out | std::ios_base::trunc);
CRITICAL_REGION_LOCAL(m_read_lock);
@ -2714,7 +2723,6 @@ void blockchain_storage::print_db_cache_perfeormance_data() const
void blockchain_storage::get_last_n_x_blocks(uint64_t n, bool pos_blocks, std::list<std::shared_ptr<const block_extended_info>>& blocks) const
{
uint64_t count = 0;
bool looking_for_a_pos = true;
for (uint64_t i = m_db_blocks.size() - 1; i != 0; --i)
{
auto block_ptr = m_db_blocks[i];
@ -3909,14 +3917,14 @@ bool blockchain_storage::is_tx_spendtime_unlocked(uint64_t unlock_time) const
}
//------------------------------------------------------------------
bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index, const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, uint64_t& max_related_block_height, uint64_t& max_unlock_time) const
bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index, const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, uint64_t& max_related_block_height, uint64_t& source_max_unlock_time_for_pos_coinbase) const
{
CRITICAL_REGION_LOCAL(m_read_lock);
//TIME_MEASURE_START_PD(tx_check_inputs_loop_ch_in_get_keys_loop);
std::vector<crypto::public_key> output_keys;
if(!get_output_keys_for_input_with_checks(tx, txin, output_keys, max_related_block_height, max_unlock_time))
if(!get_output_keys_for_input_with_checks(tx, txin, output_keys, max_related_block_height, source_max_unlock_time_for_pos_coinbase))
{
LOG_PRINT_L0("Failed to get output keys for input #" << in_index << " (amount = " << print_money(txin.amount) << ", key_offset.size = " << txin.key_offsets.size() << ")");
return false;
@ -3935,7 +3943,7 @@ bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index,
// 1) source tx unlock time validity
// 2) mixin restrictions
// 3) general gindex/ref_by_id corectness
bool blockchain_storage::get_output_keys_for_input_with_checks(const transaction& tx, const txin_to_key& txin, std::vector<crypto::public_key>& output_keys, uint64_t& max_related_block_height, uint64_t& max_unlock_time) const
bool blockchain_storage::get_output_keys_for_input_with_checks(const transaction& tx, const txin_to_key& txin, std::vector<crypto::public_key>& output_keys, uint64_t& max_related_block_height, uint64_t& source_max_unlock_time_for_pos_coinbase) const
{
CRITICAL_REGION_LOCAL(m_read_lock);
@ -3943,10 +3951,13 @@ bool blockchain_storage::get_output_keys_for_input_with_checks(const transaction
{
std::vector<crypto::public_key >& m_results_collector;
const blockchain_storage& m_bch;
uint64_t& m_max_unlock_time;
uint64_t& m_source_max_unlock_time_for_pos_coinbase;
outputs_visitor(std::vector<crypto::public_key>& results_collector,
const blockchain_storage& bch,
uint64_t& max_unlock_time) :m_results_collector(results_collector), m_bch(bch), m_max_unlock_time(max_unlock_time)
uint64_t& source_max_unlock_time_for_pos_coinbase)
: m_results_collector(results_collector)
, m_bch(bch)
, m_source_max_unlock_time_for_pos_coinbase(source_max_unlock_time_for_pos_coinbase)
{}
bool handle_output(const transaction& source_tx, const transaction& validated_tx, const tx_out& out, uint64_t out_i)
{
@ -3955,8 +3966,9 @@ bool blockchain_storage::get_output_keys_for_input_with_checks(const transaction
//let coinbase sources for PoS block to have locked inputs, the outputs supposed to be locked same way, except the reward
if (is_coinbase(validated_tx) && is_pos_block(validated_tx))
{
if (source_out_unlock_time > m_max_unlock_time)
m_max_unlock_time = source_out_unlock_time;
CHECK_AND_ASSERT_MES(should_unlock_value_be_treated_as_block_height(source_out_unlock_time), false, "source output #" << out_i << " is locked by time, not by height, which is not allowed for PoS coinbase");
if (source_out_unlock_time > m_source_max_unlock_time_for_pos_coinbase)
m_source_max_unlock_time_for_pos_coinbase = source_out_unlock_time;
}
else
{
@ -3967,7 +3979,6 @@ bool blockchain_storage::get_output_keys_for_input_with_checks(const transaction
}
}
if(out.target.type() != typeid(txout_to_key))
{
LOG_PRINT_L0("Output have wrong type id, which=" << out.target.which());
@ -3979,7 +3990,7 @@ bool blockchain_storage::get_output_keys_for_input_with_checks(const transaction
}
};
outputs_visitor vi(output_keys, *this, max_unlock_time);
outputs_visitor vi(output_keys, *this, source_max_unlock_time_for_pos_coinbase);
return scan_outputkeys_for_indexes(tx, txin, vi, max_related_block_height);
}
//------------------------------------------------------------------
@ -4437,13 +4448,13 @@ bool blockchain_storage::validate_tx_for_hardfork_specific_terms(const transacti
return true;
}
//------------------------------------------------------------------
bool blockchain_storage::validate_pos_coinbase_outs_unlock_time(const transaction& miner_tx, uint64_t staked_amount, uint64_t max_unlock_time)const
bool blockchain_storage::validate_pos_coinbase_outs_unlock_time(const transaction& miner_tx, uint64_t staked_amount, uint64_t source_max_unlock_time)const
{
uint64_t major_unlock_time = get_tx_x_detail<etc_tx_details_unlock_time>(miner_tx);
if (major_unlock_time)
{
//if there was etc_tx_details_unlock_time present in tx, then ignore etc_tx_details_unlock_time2
if (major_unlock_time < max_unlock_time)
if (major_unlock_time < source_max_unlock_time)
return false;
else
return true;
@ -4457,11 +4468,13 @@ bool blockchain_storage::validate_pos_coinbase_outs_unlock_time(const transactio
CHECK_AND_ASSERT_MES(ut2.unlock_time_array.size() == miner_tx.vout.size(), false, "ut2.unlock_time_array.size()<" << ut2.unlock_time_array.size()
<< "> != miner_tx.vout.size()<" << miner_tx.vout.size() << ">");
uint64_t amount_of_coins_in_unlock_in_range = 0;
uint64_t amount_of_coins_in_unlock_in_range = 0; // amount of outputs locked for at least the same time
for (uint64_t i = 0; i != miner_tx.vout.size(); i++)
{
if (ut2.unlock_time_array[i] >= max_unlock_time)
uint64_t unlock_value = ut2.unlock_time_array[i];
CHECK_AND_ASSERT_MES(should_unlock_value_be_treated_as_block_height(unlock_value), false, "output #" << i << " is locked by time, not buy height, which is not allowed for PoS coinbase");
if (unlock_value >= source_max_unlock_time)
amount_of_coins_in_unlock_in_range += miner_tx.vout[i].amount;
}
@ -4548,22 +4561,22 @@ bool blockchain_storage::validate_pos_block(const block& b,
{
// Do coinstake input validation for main chain only.
// Txs in alternative PoS blocks (including miner_tx) are validated by validate_alt_block_txs()
uint64_t max_unlock_time = 0;
r = check_tx_input(b.miner_tx, 1, coinstake_in, id, b.miner_tx.signatures[0], max_related_block_height, max_unlock_time);
uint64_t source_max_unlock_time_for_pos_coinbase = 0;
r = check_tx_input(b.miner_tx, 1, coinstake_in, id, b.miner_tx.signatures[0], max_related_block_height, source_max_unlock_time_for_pos_coinbase);
CHECK_AND_ASSERT_MES(r, false, "Failed to validate coinstake input in miner tx, block_id = " << get_block_hash(b));
if (get_block_height(b) > m_core_runtime_config.hard_fork1_starts_after_height)
{
uint64_t last_pow_h = get_last_x_block_height(false);
CHECK_AND_ASSERT_MES(max_related_block_height <= last_pow_h, false, "Failed to failed to validate coinbase in pos block, condition failed: max_related_block_height(" << max_related_block_height << ") < last_pow_h(" << last_pow_h << ")");
CHECK_AND_ASSERT_MES(max_related_block_height <= last_pow_h, false, "Failed to validate coinbase in PoS block, condition failed: max_related_block_height(" << max_related_block_height << ") <= last_pow_h(" << last_pow_h << ")");
//let's check that coinbase amount and unlock time
r = validate_pos_coinbase_outs_unlock_time(b.miner_tx, coinstake_in.amount, max_unlock_time);
r = validate_pos_coinbase_outs_unlock_time(b.miner_tx, coinstake_in.amount, source_max_unlock_time_for_pos_coinbase);
CHECK_AND_ASSERT_MES(r, false, "Failed to validate_pos_coinbase_outs_unlock_time() in miner tx, block_id = " << get_block_hash(b)
<< "max_unlock_time=" << max_unlock_time);
<< "source_max_unlock_time_for_pos_coinbase=" << source_max_unlock_time_for_pos_coinbase);
}
else
{
CHECK_AND_ASSERT_MES(is_tx_spendtime_unlocked(max_unlock_time), false, "Failed to failed to validate coinbase in pos block, condition failed: is_tx_spendtime_unlocked(max_unlock_time)(" << max_unlock_time << ")");
CHECK_AND_ASSERT_MES(is_tx_spendtime_unlocked(source_max_unlock_time_for_pos_coinbase), false, "Failed to validate coinbase in PoS block, condition failed: is_tx_spendtime_unlocked(source_max_unlock_time_for_pos_coinbase)(" << source_max_unlock_time_for_pos_coinbase << ")");
}
}
@ -4856,7 +4869,7 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
CHECK_AND_ASSERT_MES_NO_RET(add_res, "handle_block_to_main_chain: failed to add transaction back to transaction pool");
purge_block_data_from_blockchain(bl, tx_processed_count);
add_block_as_invalid(bl, id);
LOG_PRINT_L0("Block with id " << id << " added as invalid becouse of wrong inputs in transactions");
LOG_PRINT_L0("Block with id " << id << " added as invalid because of wrong inputs in transactions");
bvc.m_verification_failed = true;
return false;
}
@ -5356,7 +5369,7 @@ bool blockchain_storage::build_stake_modifier(stake_modifier_type& sm, const alt
else
{
bool r = string_tools::parse_tpod_from_hex_string(POS_STARTER_KERNEL_HASH, sm.last_pos_kernel_id);
CHECK_AND_ASSERT_MES(r, false, "Failed to parse POS_STARTER_MODFIFIER");
CHECK_AND_ASSERT_MES(r, false, "Failed to parse POS_STARTER_KERNEL_HASH");
}
sm.last_pow_id = get_block_hash(pbei_last_pow->bl);

View file

@ -267,14 +267,14 @@ namespace currency
uint64_t get_aliases_count()const;
uint64_t get_block_h_older_then(uint64_t timestamp) const;
bool validate_tx_service_attachmens_in_services(const tx_service_attachment& a, size_t i, const transaction& tx)const;
bool check_tx_input(const transaction& tx, size_t in_index, const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, uint64_t& max_related_block_height, uint64_t& max_unlock_time)const;
bool check_tx_input(const transaction& tx, size_t in_index, const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, uint64_t& max_related_block_height, uint64_t& source_max_unlock_time_for_pos_coinbase)const;
bool check_tx_input(const transaction& tx, size_t in_index, const txin_multisig& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, uint64_t& max_related_block_height)const;
bool check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t& max_used_block_height)const;
bool check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash) const;
bool check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t& max_used_block_height, crypto::hash& max_used_block_id)const;
bool check_ms_input(const transaction& tx, size_t in_index, const txin_multisig& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, const transaction& source_tx, size_t out_n) const;
bool validate_tx_for_hardfork_specific_terms(const transaction& tx, const crypto::hash& tx_id, uint64_t block_height) const;
bool get_output_keys_for_input_with_checks(const transaction& tx, const txin_to_key& txin, std::vector<crypto::public_key>& output_keys, uint64_t& max_related_block_height, uint64_t& max_unlock_time) const;
bool get_output_keys_for_input_with_checks(const transaction& tx, const txin_to_key& txin, std::vector<crypto::public_key>& output_keys, uint64_t& max_related_block_height, uint64_t& source_max_unlock_time_for_pos_coinbase) const;
bool check_tokey_input(const transaction& tx, size_t in_index, const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, const std::vector<const crypto::public_key*>& output_keys_ptrs) const;
uint64_t get_current_comulative_blocksize_limit()const;
uint64_t get_current_hashrate(size_t aprox_count)const;
@ -312,7 +312,7 @@ namespace currency
bool build_stake_modifier(stake_modifier_type& sm, const alt_chain_type& alt_chain = alt_chain_type(), uint64_t split_height = 0, crypto::hash *p_last_block_hash = nullptr) const;
bool scan_pos(const COMMAND_RPC_SCAN_POS::request& sp, COMMAND_RPC_SCAN_POS::response& rsp)const;
bool validate_pos_coinbase_outs_unlock_time(const transaction& miner_tx, uint64_t staked_amount, uint64_t max_unlock_time)const;
bool validate_pos_coinbase_outs_unlock_time(const transaction& miner_tx, uint64_t staked_amount, uint64_t source_max_unlock_time)const;
bool validate_pos_block(const block& b, const crypto::hash& id, bool for_altchain)const;
bool validate_pos_block(const block& b, wide_difficulty_type basic_diff, const crypto::hash& id, bool for_altchain)const;
bool validate_pos_block(const block& b,

View file

@ -63,6 +63,7 @@ namespace currency
FIELD(block_cumulative_size)
FIELD(cumulative_diff_adjusted)
FIELD(cumulative_diff_precise)
FIELD(cumulative_diff_precise_adjusted)
FIELD(difficulty)
FIELD(already_generated_coins)
FIELD(stake_hash)

View file

@ -210,7 +210,7 @@
#define CURRENT_TRANSACTION_CHAIN_ENTRY_ARCHIVE_VER 3
#define CURRENT_BLOCK_EXTENDED_INFO_ARCHIVE_VER 1
#define BLOCKCHAIN_STORAGE_MAJOR_COMPATIBILITY_VERSION CURRENCY_FORMATION_VERSION + 8
#define BLOCKCHAIN_STORAGE_MAJOR_COMPATIBILITY_VERSION CURRENCY_FORMATION_VERSION + 9
#define BLOCKCHAIN_STORAGE_MINOR_COMPATIBILITY_VERSION 1
@ -227,13 +227,12 @@
#define BLOCK_MINOR_VERSION_GENESIS 0
#define BLOCK_MAJOR_VERSION_INITAL 0
#ifndef TESTNET
#define ZANO_HARDFORK_1_AFTER_HEIGHT 172200
#define ZANO_HARDFORK_1_AFTER_HEIGHT 194624
#else
#define ZANO_HARDFORK_1_AFTER_HEIGHT 1440
#endif
static_assert(CURRENCY_MINER_TX_MAX_OUTS <= CURRENCY_TX_MAX_ALLOWED_OUTS, "Miner tx must obey normal tx max outs limit");
static_assert(PREMINE_AMOUNT / WALLET_MAX_ALLOWED_OUTPUT_AMOUNT < CURRENCY_MINER_TX_MAX_OUTS, "Premine can't be divided into reasonable number of outs");

View file

@ -18,6 +18,7 @@ using namespace epee;
#include "currency_core/currency_config.h"
#include "currency_format_utils.h"
#include "misc_language.h"
#include "string_coding.h"
#define MINIMUM_REQUIRED_FREE_SPACE_BYTES (1024 * 1024 * 100)
@ -717,10 +718,27 @@ namespace currency
//-----------------------------------------------------------------------------------------------
bool core::check_if_free_space_critically_low(uint64_t* p_available_space /* = nullptr */)
{
boost::filesystem::space_info si = boost::filesystem::space(m_config_folder);
if (p_available_space != nullptr)
*p_available_space = si.available;
return si.available < MINIMUM_REQUIRED_FREE_SPACE_BYTES;
namespace fs = boost::filesystem;
try
{
CHECK_AND_ASSERT_MES(tools::create_directories_if_necessary(m_config_folder), false, "create_directories_if_necessary failed: " << m_config_folder);
std::wstring config_folder_w = epee::string_encoding::utf8_to_wstring(m_config_folder);
fs::space_info si = fs::space(config_folder_w);
if (p_available_space != nullptr)
*p_available_space = si.available;
return si.available < MINIMUM_REQUIRED_FREE_SPACE_BYTES;
}
catch (std::exception& e)
{
LOG_ERROR("failed to determine free space: " << e.what());
return false;
}
catch (...)
{
LOG_ERROR("failed to determine free space: unknown exception");
return false;
}
}
void core::check_free_space()

View file

@ -2715,7 +2715,7 @@ namespace currency
boost::multiprecision::uint1024_t basic_sum = boost::multiprecision::uint1024_t(a_pow_cumulative_difficulty) + (boost::multiprecision::uint1024_t(a_pos_cumulative_difficulty)*difficulty_pow_at_split_point) / difficulty_pos_at_split_point;
boost::multiprecision::uint1024_t res =
(basic_sum * a_pow_cumulative_difficulty * a_pos_cumulative_difficulty) / (boost::multiprecision::uint1024_t(a_pow_cumulative_difficulty)*a_pos_cumulative_difficulty);
(basic_sum * a_pow_cumulative_difficulty * a_pos_cumulative_difficulty) / (boost::multiprecision::uint1024_t(b_pow_cumulative_difficulty)*b_pos_cumulative_difficulty);
if (res > boost::math::tools::max_value<wide_difficulty_type>())
{

View file

@ -88,7 +88,7 @@ namespace currency
return 0;
CHECK_AND_ASSERT_THROW_MES(ut2.unlock_time_array.size() > o_i, "unlock_time_array.size=" << ut2.unlock_time_array.size()
<< " is less then o_i=" << o_i << " in tx: " << get_transaction_hash(tx));
<< " is less or equal to o_i=" << o_i << " in tx: " << get_transaction_hash(tx));
return ut2.unlock_time_array[o_i];
}

View file

@ -85,6 +85,7 @@ namespace currency
uint64_t get_tx_unlock_time(const transaction& tx, uint64_t o_i);
uint64_t get_tx_max_unlock_time(const transaction& tx);
bool get_tx_max_min_unlock_time(const transaction& tx, uint64_t& max_unlock_time, uint64_t& min_unlock_time);
inline bool should_unlock_value_be_treated_as_block_height(uint64_t v) { return v < CURRENCY_MAX_BLOCK_NUMBER; }
inline uint64_t get_tx_flags(const transaction& tx) { return get_tx_x_detail<etc_tx_details_flags>(tx); }
inline uint64_t get_tx_expiration_time(const transaction& tx) {return get_tx_x_detail<etc_tx_details_expiration_time>(tx); }
inline void set_tx_unlock_time(transaction& tx, uint64_t v) { set_tx_x_detail<etc_tx_details_unlock_time>(tx, v); }
@ -106,4 +107,4 @@ namespace currency
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

@ -164,7 +164,7 @@ namespace currency
else
{
// this tx has no fee
LOG_ERROR("Transaction with id= " << id << " has too small fee: " << tx_fee << ", expected fee: " << m_blockchain.get_core_runtime_config().tx_pool_min_fee);
LOG_PRINT_RED_L0("Transaction with id= " << id << " has too small fee: " << tx_fee << ", expected fee: " << m_blockchain.get_core_runtime_config().tx_pool_min_fee);
tvc.m_verification_failed = false;
tvc.m_should_be_relayed = false;
tvc.m_added_to_pool = false;
@ -1125,10 +1125,10 @@ namespace currency
// remove old incompartible DB
const std::string old_db_folder_path = m_config_folder + "/" CURRENCY_POOLDATA_FOLDERNAME_OLD;
if (boost::filesystem::exists(old_db_folder_path))
if (boost::filesystem::exists(epee::string_encoding::utf8_to_wstring(old_db_folder_path)))
{
LOG_PRINT_YELLOW("Removing old DB in " << old_db_folder_path << "...", LOG_LEVEL_0);
boost::filesystem::remove_all(old_db_folder_path);
boost::filesystem::remove_all(epee::string_encoding::utf8_to_wstring(old_db_folder_path));
}
const std::string db_folder_path = m_config_folder + "/" CURRENCY_POOLDATA_FOLDERNAME;
@ -1142,7 +1142,7 @@ namespace currency
{
// if DB could not be opened -- try to remove the whole folder and re-open DB
LOG_PRINT_YELLOW("Failed to initialize database in folder: " << db_folder_path << ", first attempt", LOG_LEVEL_0);
boost::filesystem::remove_all(db_folder_path);
boost::filesystem::remove_all(epee::string_encoding::utf8_to_wstring(db_folder_path));
res = m_db.open(db_folder_path, cache_size_l1);
CHECK_AND_ASSERT_MES(res, false, "Failed to initialize database in folder: " << db_folder_path << ", second attempt");
}
@ -1182,7 +1182,7 @@ namespace currency
m_db_alias_addresses.deinit();
m_db_solo_options.deinit();
m_db.close();
size_t files_removed = boost::filesystem::remove_all(db_folder_path);
size_t files_removed = boost::filesystem::remove_all(epee::string_encoding::utf8_to_wstring(db_folder_path));
LOG_PRINT_L1(files_removed << " files at " << db_folder_path << " removed");
// try to re-create DB and re-init containers

View file

@ -770,36 +770,14 @@ namespace currency
#define TIME_SYNC_DELTA_RING_BUFFER_SIZE 8
#define TIME_SYNC_DELTA_TO_LOCAL_MAX_DIFFERENCE (60 * 5) // max acceptable difference between time delta median among peers and local time (seconds)
#define TIME_SYNC_NTP_TO_LOCAL_MAX_DIFFERENCE (60 * 5) // max acceptable difference between NTP time and local time (seconds)
#define TIME_SYNC_NTP_SERVERS { "time.google.com", "0.pool.ntp.org", "1.pool.ntp.org", "2.pool.ntp.org", "3.pool.ntp.org" }
#define TIME_SYNC_NTP_ATTEMPTS_COUNT 3 // max number of attempts when getting time from NTP server
static int64_t get_ntp_time()
{
static const std::vector<std::string> ntp_servers TIME_SYNC_NTP_SERVERS;
for (size_t att = 0; att < TIME_SYNC_NTP_ATTEMPTS_COUNT; ++att)
{
size_t i = 0;
crypto::generate_random_bytes(sizeof(i), &i);
const std::string& ntp_server = ntp_servers[i % ntp_servers.size()];
LOG_PRINT_L3("NTP: trying to get time from " << ntp_server);
int64_t time = tools::get_ntp_time(ntp_server);
if (time > 0)
{
LOG_PRINT_L2("NTP: " << ntp_server << " responded with " << time << " (" << epee::misc_utils::get_time_str_v2(time) << ")");
return time;
}
LOG_PRINT_L2("NTP: cannot get time from " << ntp_server);
}
return 0; // smth went wrong
}
template<class t_core>
bool t_currency_protocol_handler<t_core>::add_time_delta_and_check_time_sync(int64_t time_delta)
{
CRITICAL_REGION_LOCAL(m_time_deltas_lock);
auto get_core_time = [this] { return m_core.get_blockchain_storage().get_core_runtime_config().get_core_time(); };
m_time_deltas.push_back(time_delta);
while (m_time_deltas.size() > TIME_SYNC_DELTA_RING_BUFFER_SIZE)
m_time_deltas.pop_front();
@ -813,7 +791,8 @@ namespace currency
LOG_PRINT_MAGENTA("TIME: network time difference is " << m_last_median2local_time_difference << " (max is " << TIME_SYNC_DELTA_TO_LOCAL_MAX_DIFFERENCE << ")", LOG_LEVEL_2);
if (std::abs(m_last_median2local_time_difference) > TIME_SYNC_DELTA_TO_LOCAL_MAX_DIFFERENCE)
{
int64_t ntp_time = get_ntp_time();
int64_t ntp_time = tools::get_ntp_time();
LOG_PRINT_L2("NTP: received time " << ntp_time << " (" << epee::misc_utils::get_time_str_v2(ntp_time) << "), diff: " << std::showpos << get_core_time() - ntp_time);
if (ntp_time == 0)
{
// error geting ntp time
@ -823,7 +802,7 @@ namespace currency
// got ntp time correctly
// update local time, because getting ntp time could be time consuming
uint64_t local_time_2 = m_core.get_blockchain_storage().get_core_runtime_config().get_core_time();
uint64_t local_time_2 = get_core_time();
m_last_ntp2local_time_difference = local_time_2 - ntp_time;
if (std::abs(m_last_ntp2local_time_difference) > TIME_SYNC_NTP_TO_LOCAL_MAX_DIFFERENCE)
{

View file

@ -87,6 +87,13 @@ struct core_critical_error_handler_t : public currency::i_critical_error_handler
bool dont_stop_on_low_space;
};
void terminate_handler_func()
{
LOG_ERROR("\n\nTERMINATE HANDLER\n"); // should print callstack
std::fflush(nullptr); // all open output streams are flushed
std::abort(); // default terminate handler's behavior
}
int main(int argc, char* argv[])
{
try
@ -112,6 +119,9 @@ int main(int argc, char* argv[])
// setup custom callstack retrieving function
epee::misc_utils::get_callstack(tools::get_callstack);
// setup custom terminate functions
std::set_terminate(&terminate_handler_func);
po::options_description desc_cmd_only("Command line options");
po::options_description desc_cmd_sett("Command line options and settings options", 130, 83);
@ -162,8 +172,8 @@ int main(int argc, char* argv[])
std::string data_dir = command_line::get_arg(vm, command_line::arg_data_dir);
std::string config = command_line::get_arg(vm, command_line::arg_config_file);
boost::filesystem::path data_dir_path(data_dir);
boost::filesystem::path config_path(config);
boost::filesystem::path data_dir_path(epee::string_encoding::utf8_to_wstring(data_dir));
boost::filesystem::path config_path(epee::string_encoding::utf8_to_wstring(config));
if (!config_path.has_parent_path())
{
config_path = data_dir_path / config_path;

View file

@ -70,6 +70,9 @@ public:
m_cmd_binder.set_handler("print_tx_outputs_usage", boost::bind(&daemon_commands_handler::print_tx_outputs_usage, this, _1), "Analyse if tx outputs for involved in subsequent transactions");
m_cmd_binder.set_handler("print_difficulties_of_last_n_blocks", boost::bind(&daemon_commands_handler::print_difficulties_of_last_n_blocks, this, _1), "Print difficulties of last n blocks");
#ifdef _DEBUG
m_cmd_binder.set_handler("debug_set_time_adj", boost::bind(&daemon_commands_handler::debug_set_time_adj, this, _1), "DEBUG: set core time adjustment");
#endif
}
bool start_handling()
@ -499,8 +502,15 @@ private:
bool r = m_srv.get_payload_object().get_core().get_blockchain_storage().get_main_blocks_rpc_details(height, 1, false, blocks);
if (r && blocks.size())
{
currency::block b = AUTO_VAL_INIT(b);
m_srv.get_payload_object().get_core().get_blockchain_storage().get_block_by_height(height, b);
currency::blobdata blob = block_to_blob(b);
std::string block_hex = epee::string_tools::buff_to_hex_nodelimer(blob);
currency::block_rpc_extended_info& rbei = blocks.back();
LOG_PRINT_GREEN("------------------ block_id: " << rbei.id << " ------------------" << ENDL << epee::serialization::store_t_to_json(rbei), LOG_LEVEL_0);
LOG_PRINT_GREEN("------------------ block_id: " << rbei.id << " ------------------" <<
ENDL << epee::serialization::store_t_to_json(rbei) << ENDL
<< " ------------------ hex_blob: " << ENDL << block_hex, LOG_LEVEL_0);
}
else
{
@ -523,8 +533,14 @@ private:
if (r)
{
currency::block b = AUTO_VAL_INIT(b);
m_srv.get_payload_object().get_core().get_blockchain_storage().get_block_by_hash(block_hash, b);
currency::blobdata blob = block_to_blob(b);
std::string block_hex = epee::string_tools::buff_to_hex_nodelimer(blob);
// currency::block& block = bei.bl;
LOG_PRINT_GREEN("------------------ block_id: " << bei.id << " ------------------" << ENDL << epee::serialization::store_t_to_json(bei), LOG_LEVEL_0);
LOG_PRINT_GREEN("------------------ block_id: " << bei.id << " ------------------" << ENDL
<< epee::serialization::store_t_to_json(bei) << ENDL
<< " ------------------ hex_blob: " << ENDL << block_hex, LOG_LEVEL_0);
}
else
{
@ -811,7 +827,49 @@ private:
LOG_PRINT_L0(ENDL << epee::deadlock_guard_singleton::get_dlg_state());
return true;
}
//--------------------------------------------------------------------------------
#ifdef _DEBUG
static std::atomic<int64_t>& debug_core_time_shift_accessor()
{
static std::atomic<int64_t> time_shift(0);
return time_shift;
}
static uint64_t debug_core_time_function()
{
return (int64_t)time(NULL) + debug_core_time_shift_accessor().load(std::memory_order_relaxed);
}
bool debug_set_time_adj(const std::vector<std::string>& args)
{
if (args.size() != 1)
{
LOG_PRINT_RED("one arg required: signed time shift in seconds", LOG_LEVEL_0);
return false;
}
int64_t time_shift = 0;
if (!epee::string_tools::string_to_num_fast(args[0], time_shift))
{
LOG_PRINT_RED("could not parse: " << args[0], LOG_LEVEL_0);
return false;
}
uint64_t time_before = debug_core_time_function();
debug_core_time_shift_accessor().store(time_shift);
uint64_t time_after = debug_core_time_function();
currency::blockchain_storage& bcs = m_srv.get_payload_object().get_core().get_blockchain_storage();
currency::core_runtime_config crc = bcs.get_core_runtime_config();
crc.get_core_time = &debug_core_time_function;
bcs.set_core_runtime_config(crc);
LOG_PRINT_L0("debug time shift set to " << time_shift << " : time before: " << time_before << ", time_after: " << time_after);
return true;
}
#endif

View file

@ -66,6 +66,13 @@ daemon_backend::~daemon_backend()
stop();
}
void terminate_handler_func()
{
LOG_ERROR("\n\nTERMINATE HANDLER\n"); // should print callstack
std::fflush(nullptr); // all open output streams are flushed
std::abort(); // default terminate handler's behavior
}
bool daemon_backend::init(int argc, char* argv[], view::i_view* pview_handler)
{
m_stop_singal_sent = false;
@ -88,6 +95,9 @@ bool daemon_backend::init(int argc, char* argv[], view::i_view* pview_handler)
// setup custom callstack retrieving function
epee::misc_utils::get_callstack(tools::get_callstack);
// setup custom terminate functions
std::set_terminate(&terminate_handler_func);
//#if !defined(NDEBUG)
// log_space::log_singletone::add_logger(LOGGER_DEBUGGER, nullptr, nullptr);
//#endif
@ -144,8 +154,8 @@ bool daemon_backend::init(int argc, char* argv[], view::i_view* pview_handler)
m_data_dir = command_line::get_arg(m_vm, command_line::arg_data_dir);
std::string config = command_line::get_arg(m_vm, command_line::arg_config_file);
boost::filesystem::path data_dir_path(m_data_dir);
boost::filesystem::path config_path(config);
boost::filesystem::path data_dir_path(epee::string_encoding::utf8_to_wstring(m_data_dir));
boost::filesystem::path config_path(epee::string_encoding::utf8_to_wstring(config));
if (!config_path.has_parent_path())
{
config_path = data_dir_path / config_path;
@ -383,6 +393,7 @@ bool daemon_backend::deinit_local_daemon()
LOG_PRINT_L0("Deinitializing p2p...");
//dsi.text_state = "Deinitializing p2p";
m_pview->update_daemon_status(dsi);
m_p2psrv.deinit();
m_ccore.set_currency_protocol(NULL);
m_cprotocol.set_p2p_endpoint(NULL);
@ -429,7 +440,8 @@ void daemon_backend::main_worker(const po::variables_map& m_vm)
//m_pview->update_daemon_status(dsi);
try
{
wo.second.stop = true;
wo.second.major_stop = true;
wo.second.stop_for_refresh = true;
wo.second.w.unlocked_get()->stop();
wo.second.w->get()->store();
@ -543,7 +555,8 @@ void daemon_backend::init_wallet_entry(wallet_vs_options& wo, uint64_t id)
{
wo.wallet_id = id;
wo.do_mining = false;
wo.stop = false;
wo.major_stop = false;
wo.stop_for_refresh = false;
wo.plast_daemon_height = &m_last_daemon_height;
wo.plast_daemon_network_state = &m_last_daemon_network_state;
wo.plast_daemon_is_disconnected = &m_last_daemon_is_disconnected;
@ -837,7 +850,8 @@ std::string daemon_backend::close_wallet(size_t wallet_id)
try
{
it->second.stop = true;
it->second.major_stop = true;
it->second.stop_for_refresh = true;
it->second.w.unlocked_get()->stop();
it->second.w->get()->store();
@ -1488,8 +1502,9 @@ void daemon_backend::wallet_vs_options::worker_func()
epee::math_helper::once_a_time_seconds<1> scan_pool_interval;
epee::math_helper::once_a_time_seconds<POS_WALLET_MINING_SCAN_INTERVAL> pos_minin_interval;
view::wallet_status_info wsi = AUTO_VAL_INIT(wsi);
while (!stop)
while (!major_stop)
{
stop_for_refresh = false;
try
{
wsi.wallet_state = view::wallet_status_info::wallet_state_ready;
@ -1516,7 +1531,7 @@ void daemon_backend::wallet_vs_options::worker_func()
prepare_wallet_status_info(*this, wsi);
pview->update_wallet_status(wsi);
}
w->get()->refresh(stop);
w->get()->refresh(stop_for_refresh);
w->get()->resend_unconfirmed();
{
auto w_ptr = *w; // get locked exclusive access to the wallet first (it's more likely that wallet is locked for a long time than 'offers')
@ -1548,11 +1563,11 @@ void daemon_backend::wallet_vs_options::worker_func()
});
}
if (stop)
if (major_stop || stop_for_refresh)
break;
//******************************************************************************************
//mining zone
if (do_mining)
if (do_mining && *plast_daemon_network_state == currency::COMMAND_RPC_GET_INFO::daemon_network_state_online)
{
pos_minin_interval.do_call([this](){
tools::wallet2::mining_context ctx = AUTO_VAL_INIT(ctx);
@ -1614,7 +1629,8 @@ void daemon_backend::wallet_vs_options::worker_func()
daemon_backend::wallet_vs_options::~wallet_vs_options()
{
do_mining = false;
stop = true;
major_stop = true;
stop_for_refresh = true;
break_mining_loop = true;
if (miner_thread.joinable())
miner_thread.join();

View file

@ -56,7 +56,10 @@ public:
currency::core_runtime_config core_conf;
epee::locked_object<std::shared_ptr<tools::wallet2>, wallet_lock_time_watching_policy> w;
std::atomic<bool> do_mining;
std::atomic<bool> stop;
std::atomic<bool> major_stop;
std::atomic<bool> stop_for_refresh; //use separate var for passing to "refresh" member function,
//because it can be changed there due to internal interruption logis
std::atomic<bool> break_mining_loop;
std::atomic<uint64_t> wallet_state;
std::atomic<uint64_t> last_wallet_synch_height;

View file

@ -75,14 +75,14 @@ namespace gui_tools
{
namespace fs = boost::filesystem;
char pszPath[MAX_PATH] = "";
wchar_t pszPath[MAX_PATH] = L"";
if (SHGetSpecialFolderPathA(NULL, pszPath, nFolder, fCreate))
if (SHGetSpecialFolderPathW(NULL, pszPath, nFolder, fCreate))
{
return fs::path(pszPath);
}
//LogPrintf("SHGetSpecialFolderPathA() failed, could not obtain requested path.\n");
//LogPrintf("SHGetSpecialFolderPathW() failed, could not obtain requested path.\n");
return fs::path("");
}
#endif

View file

@ -316,12 +316,12 @@ bool MainWindow::load_app_config()
CATCH_ENTRY2(false);
}
bool MainWindow::init(const std::string& htmlPath)
bool MainWindow::init(const std::string& html_path)
{
TRY_ENTRY();
//QtWebEngine::initialize();
init_tray_icon(htmlPath);
set_html_path(htmlPath);
init_tray_icon(html_path);
set_html_path(html_path);
m_backend.subscribe_to_core_events(this);
@ -354,7 +354,7 @@ void MainWindow::on_menu_show()
CATCH_ENTRY2(void());
}
void MainWindow::init_tray_icon(const std::string& htmlPath)
void MainWindow::init_tray_icon(const std::string& html_path)
{
TRY_ENTRY();
if (!QSystemTrayIcon::isSystemTrayAvailable())
@ -384,14 +384,14 @@ void MainWindow::init_tray_icon(const std::string& htmlPath)
//setup icon
#ifdef TARGET_OS_MAC
m_normal_icon_path = htmlPath + "/files/app22macos.png"; // X11 tray icon size is 22x22
m_blocked_icon_path = htmlPath + "/files/app22macos_blocked.png"; // X11 tray icon size is 22x22
m_normal_icon_path = html_path + "/files/app22macos.png"; // X11 tray icon size is 22x22
m_blocked_icon_path = html_path + "/files/app22macos_blocked.png"; // X11 tray icon size is 22x22
#else
m_normal_icon_path = htmlPath + "/files/app22windows.png"; // X11 tray icon size is 22x22
m_blocked_icon_path = htmlPath + "/files/app22windows_blocked.png"; // X11 tray icon size
m_normal_icon_path = html_path + "/files/app22windows.png"; // X11 tray icon size is 22x22
m_blocked_icon_path = html_path + "/files/app22windows_blocked.png"; // X11 tray icon size
#endif
//setWindowIcon(QIcon(iconPath.c_str()));
QIcon qi(m_normal_icon_path.c_str());
QIcon qi( QString::fromWCharArray(epee::string_encoding::utf8_to_wstring(m_normal_icon_path).c_str()) );
qi.setIsMask(true);
m_tray_icon->setIcon(qi);
m_tray_icon->setToolTip(CURRENCY_NAME_BASE);
@ -411,7 +411,7 @@ void MainWindow::bool_toggle_icon(const QString& param)
else
path = m_normal_icon_path;
QIcon qi(path.c_str());
QIcon qi( QString::fromWCharArray(epee::string_encoding::utf8_to_wstring(path).c_str()) );
qi.setIsMask(true);
m_tray_icon->setIcon(qi);
CATCH_ENTRY2(void());
@ -771,7 +771,7 @@ bool MainWindow::set_html_path(const std::string& path)
TRY_ENTRY();
//init_tray_icon(path);
#ifdef _MSC_VER
QString url = QString::fromUtf8(epee::string_encoding::convert_ansii_to_utf8(path).c_str()) + "/index.html";
QString url = QString::fromUtf8(path.c_str()) + "/index.html";
load_file(url);
#else
// load_file(QString((std::string("file://") + path + "/index.html").c_str()));
@ -1037,7 +1037,7 @@ QString MainWindow::get_secure_app_data(const QString& param)
const app_data_file_binary_header* phdr = reinterpret_cast<const app_data_file_binary_header*>(app_data_buff.data());
if (phdr->m_signature != APP_DATA_FILE_BINARY_SIGNATURE)
{
LOG_ERROR("password missmatch: provided pass: " << pwd.pass);
LOG_ERROR("password missmatch");
view::api_response ar;
ar.error_code = API_RETURN_CODE_WRONG_PASSWORD;
return MAKE_RESPONSE(ar);
@ -1232,7 +1232,7 @@ QString MainWindow::have_secure_app_data()
view::api_response ar = AUTO_VAL_INIT(ar);
boost::system::error_code ec;
if (boost::filesystem::exists(m_backend.get_config_folder() + "/" + GUI_SECURE_CONFIG_FILENAME, ec))
if (boost::filesystem::exists(epee::string_encoding::utf8_to_wstring(m_backend.get_config_folder() + "/" + GUI_SECURE_CONFIG_FILENAME), ec))
ar.error_code = API_RETURN_CODE_TRUE;
else
ar.error_code = API_RETURN_CODE_FALSE;
@ -1240,6 +1240,7 @@ QString MainWindow::have_secure_app_data()
return MAKE_RESPONSE(ar);
CATCH_ENTRY_FAIL_API_RESPONCE();
}
QString MainWindow::drop_secure_app_data()
{
TRY_ENTRY();
@ -1247,13 +1248,14 @@ QString MainWindow::drop_secure_app_data()
view::api_response ar = AUTO_VAL_INIT(ar);
boost::system::error_code ec;
if (boost::filesystem::remove(m_backend.get_config_folder() + "/" + GUI_SECURE_CONFIG_FILENAME, ec))
if (boost::filesystem::remove(epee::string_encoding::utf8_to_wstring(m_backend.get_config_folder() + "/" + GUI_SECURE_CONFIG_FILENAME), ec))
ar.error_code = API_RETURN_CODE_TRUE;
else
ar.error_code = API_RETURN_CODE_FALSE;
return MAKE_RESPONSE(ar);
CATCH_ENTRY_FAIL_API_RESPONCE();
}
QString MainWindow::get_all_aliases()
{
TRY_ENTRY();

View file

@ -535,8 +535,10 @@
"SUCCESS_IMPORT": "Contacts are imported",
"SUCCESS_EXPORT": "Contacts are exported",
"ERROR_IMPORT": "Error is occured while reading file!",
"ERROR_TYPE_FILE": "Please import valid .csv file.",
"ERROR_EXPORT": "Invalid file type. Save file as .csv"
"ERROR_TYPE_FILE": "Please import valid .csv file",
"ERROR_EXPORT": "Invalid file type. Save file as .csv",
"ERROR_EMPTY_LIST": "Contact list is empty",
"ERROR_IMPORT_EMPTY": "File is empty"
},
"ERRORS": {
"NO_MONEY": "Not enough money",

View file

@ -535,8 +535,10 @@
"SUCCESS_IMPORT": "Contacts are imported",
"SUCCESS_EXPORT": "Contacts are exported",
"ERROR_IMPORT": "Error is occured while reading file!",
"ERROR_TYPE_FILE": "Please import valid .csv file.",
"ERROR_EXPORT": "Invalid file type. Save file as .csv"
"ERROR_TYPE_FILE": "Please import valid .csv file",
"ERROR_EXPORT": "Invalid file type. Save file as .csv",
"ERROR_EMPTY_LIST": "Contact list is empty",
"ERROR_IMPORT_EMPTY": "File is empty"
},
"ERRORS": {
"NO_MONEY": "Not enough money",

View file

@ -535,8 +535,10 @@
"SUCCESS_IMPORT": "Contacts are imported",
"SUCCESS_EXPORT": "Contacts are exported",
"ERROR_IMPORT": "Error is occured while reading file!",
"ERROR_TYPE_FILE": "Please import valid .csv file.",
"ERROR_EXPORT": "Invalid file type. Save file as .csv"
"ERROR_TYPE_FILE": "Please import valid .csv file",
"ERROR_EXPORT": "Invalid file type. Save file as .csv",
"ERROR_EMPTY_LIST": "Contact list is empty",
"ERROR_IMPORT_EMPTY": "File is empty"
},
"ERRORS": {
"NO_MONEY": "Not enough money",

View file

@ -535,8 +535,10 @@
"SUCCESS_IMPORT": "Contacts are imported",
"SUCCESS_EXPORT": "Contacts are exported",
"ERROR_IMPORT": "Error is occured while reading file!",
"ERROR_TYPE_FILE": "Please import valid .csv file.",
"ERROR_EXPORT": "Invalid file type. Save file as .csv"
"ERROR_TYPE_FILE": "Please import valid .csv file",
"ERROR_EXPORT": "Invalid file type. Save file as .csv",
"ERROR_EMPTY_LIST": "Contact list is empty",
"ERROR_IMPORT_EMPTY": "File is empty"
},
"ERRORS": {
"NO_MONEY": "Not enough money",

View file

@ -535,8 +535,10 @@
"SUCCESS_IMPORT": "Contacts are imported",
"SUCCESS_EXPORT": "Contacts are exported",
"ERROR_IMPORT": "Error is occured while reading file!",
"ERROR_TYPE_FILE": "Please import valid .csv file.",
"ERROR_EXPORT": "Invalid file type. Save file as .csv"
"ERROR_TYPE_FILE": "Please import valid .csv file",
"ERROR_EXPORT": "Invalid file type. Save file as .csv",
"ERROR_EMPTY_LIST": "Contact list is empty",
"ERROR_IMPORT_EMPTY": "File is empty"
},
"ERRORS": {
"NO_MONEY": "Not enough money",

View file

@ -10,7 +10,7 @@
"FORM_ERRORS": {
"PASS_REQUIRED": "Passwort ist erforderlich",
"CONFIRM_REQUIRED": "Bestätigung ist erforderlich",
"MISMATCH": "Mismatch"
"MISMATCH": "Fehlanpassung"
}
},
"COMMON": {
@ -34,7 +34,7 @@
"TITLE": "Wallets",
"ADD_NEW": "+ Hinzufügen",
"ACCOUNT": {
"STAKING": "Staking (Anlegen)",
"STAKING": "Staking",
"MESSAGES": "Neue Angebote/Nachrichten",
"SYNCING": "Wallet synchronisieren"
},
@ -159,15 +159,15 @@
"PASS_NOT_MATCH": "Old password not match",
"CONFIRM_NOT_MATCH": "Confirm password not match"
},
"LAST_BUILD": "Current build: {{value}}",
"APP_LOG_TITLE": "Log level:"
"LAST_BUILD": "Aktueller Build: {{value}}",
"APP_LOG_TITLE": "Log-Level:"
},
"WALLET": {
"REGISTER_ALIAS": "Alias registrieren",
"DETAILS": "Details",
"LOCK": "Verschlüsseln",
"AVAILABLE_BALANCE": "Verfügbar <b>{{verfügbar}} {{Währung}}<b/>",
"LOCKED_BALANCE": "Gesperrt <b>{{gesperrt}} {{Währung}}<b/>",
"AVAILABLE_BALANCE": "Verfügbar <b>{{available}} {{currency}}<b/>",
"LOCKED_BALANCE": "Gesperrt <b>{{locked}} {{currency}}<b/>",
"LOCKED_BALANCE_LINK": "Was bedeutet das?",
"TABS": {
"SEND": "Senden",
@ -175,7 +175,7 @@
"HISTORY": "Verlauf",
"CONTRACTS": "Verträge",
"MESSAGES": "Nachrichten",
"STAKING": "Staking (Anlegen)"
"STAKING": "Staking"
}
},
"WALLET_DETAILS": {
@ -195,14 +195,14 @@
"NAME": {
"LABEL": "Alias",
"PLACEHOLDER": "@ Alias eingeben",
"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 “@”."
"TOOLTIP": "Ein Alias ist eine verkürzte Form Ihres Kontos. Ein Alias kann nur lateinische Buchstaben, Zahlen und die Zeichen „.“ und “-” enthalten. Es muss mit “@” beginnen."
},
"COMMENT": {
"LABEL": "Kommentar",
"PLACEHOLDER": "",
"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."
"TOOLTIP": "Der Kommentar wird für jeden sichtbar sein, der eine Zahlung an Ihren Alias vornehmen möchte. Sie können Details über Ihre Geschäfte, Kontakte oder Text angeben. Kommentare können später bearbeitet werden."
},
"COST": "Alias fee {{value}} {{currency}}",
"COST": "Alias-Gebühr {{value}} {{currency}}",
"BUTTON_ASSIGN": "Zuweisen",
"FORM_ERRORS": {
"NAME_REQUIRED": "Name ist erforderlich",
@ -228,7 +228,7 @@
"NO_MONEY": "Sie haben nicht genügend Geldmittel, um den Kommentar zu diesem Alias zu ändern",
"MAX_LENGTH": "Maximale Kommentarlänge erreicht"
},
"COST": "Gebühr {{Wert}} {{Währung}}",
"COST": "Gebühr {{value}} {{currency}}",
"BUTTON_EDIT": "Bearbeiten"
},
"TRANSFER_ALIAS": {
@ -249,7 +249,7 @@
"ALIAS_EXISTS": "Dieses Konto hat bereits einen Alias",
"NO_MONEY": "Du hast nicht genug Geldmittel, um diesen Alias zu transferieren"
},
"COST": "Transfer fee {{value}} {{currency}}",
"COST": "Überweisungsgebühr {{value}} {{currency}}",
"BUTTON_TRANSFER": "Transfer",
"BUTTON_CANCEL": "Abbrechen",
"REQUEST_SEND_REG": "Der Alias wird innerhalb von 10 Minuten übertragen"
@ -271,14 +271,14 @@
"AMOUNT_REQUIRED": "Betrag ist erforderlich",
"AMOUNT_ZERO": "Betrag ist Null",
"FEE_REQUIRED": "Gebühr ist erforderlich",
"FEE_MINIMUM": "Mindestgebühr: {{Gebühr}}",
"FEE_MINIMUM": "Mindestgebühr: {{fee}}",
"MAX_LENGTH": "Maximale Kommentarlänge erreicht"
}
},
"HISTORY": {
"STATUS": "Status",
"STATUS_TOOLTIP": "Confirmations {{current}}/{{total}}",
"LOCK_TOOLTIP": "Gesperrt bis {{Datum}}",
"STATUS_TOOLTIP": "Bestätigungen {{current}}/{{total}}",
"LOCK_TOOLTIP": "Gesperrt bis {{date}}",
"SEND": "Gesendet",
"RECEIVED": "Empfangen",
"DATE": "Datum",
@ -289,7 +289,7 @@
"PAYMENT_ID": "Zahlungs-ID",
"ID": "Transaktions-ID",
"SIZE": "Transaktionsgröße",
"SIZE_VALUE": "{{Wert}} Bytes",
"SIZE_VALUE": "{{value}} Bytes",
"HEIGHT": "Höhe",
"CONFIRMATION": "Bestätigung",
"INPUTS": "Inputs",
@ -306,10 +306,10 @@
"POW_REWARD": "POW-Belohnung",
"POS_REWARD": "POS-Belohnung",
"CREATE_CONTRACT": "Vertragsvorschlag",
"PLEDGE_CONTRACT": "Contract deposit",
"PLEDGE_CONTRACT": "Vertrag-Einzahlung",
"NULLIFY_CONTRACT": "Einzahlungen verbrennen",
"PROPOSAL_CANCEL_CONTRACT": "Stornierungsanfrage",
"CANCEL_CONTRACT": "Cancel and return deposits"
"CANCEL_CONTRACT": "Abbrechen und Einzahlungen zurückzahlen"
}
},
"CONTRACTS": {
@ -322,17 +322,17 @@
"STATUS": "Status",
"COMMENTS": "Kommentare",
"PURCHASE_BUTTON": "Neuer Kauf",
"LISTING_BUTTON": "Create listing",
"LISTING_BUTTON": "Auflistung erstellen",
"TIME_LEFT": {
"REMAINING_LESS_ONE": "Weniger als eine Stunde, um zu antworten",
"REMAINING_ONE": "{{Zeit}} verbleibende Stunde",
"REMAINING_MANY": "{{Zeit}} verbleibende Stunden",
"REMAINING_MANY_ALT": "{{Zeit}} verbleibende Stunden",
"REMAINING_ONE_RESPONSE": "{{Zeit}} verbleibende Stunde",
"REMAINING_MANY_RESPONSE": "{{Zeit}} verbleibende Stunde",
"REMAINING_MANY_ALT_RESPONSE": "{{Zeit}} verbleibende Stunden",
"REMAINING_ONE_WAITING": "Warte für {{Zeit}} Stunde",
"REMAINING_MANY_WAITING": "Warte für {{Zeit}} Stunden",
"REMAINING_ONE": "{{time}} verbleibende Stunde",
"REMAINING_MANY": "{{time}} verbleibende Stunden",
"REMAINING_MANY_ALT": "{{time}} verbleibende Stunden",
"REMAINING_ONE_RESPONSE": "{{time}} verbleibende Stunde",
"REMAINING_MANY_RESPONSE": "{{time}} verbleibende Stunde",
"REMAINING_MANY_ALT_RESPONSE": "{{time}} verbleibende Stunden",
"REMAINING_ONE_WAITING": "Warte für {{time}} Stunde",
"REMAINING_MANY_WAITING": "Warte für {{time}} Stunden",
"REMAINING_MANY_ALT_WAITING": "Warte für {{Zeit}} Stunden"
},
"STATUS_MESSAGES": {
@ -354,7 +354,7 @@
"BUYER": {
"WAITING": "Warte auf Antwort",
"IGNORED": "Verkäufer ignorierte Ihren Vertragsvorschlag",
"ACCEPTED": "Seller accepted your contract proposal",
"ACCEPTED": "Verkäufer hat Ihren Vertragsvorschlag akzeptiert",
"WAIT": "Warten auf die Bestätigung der Einzahlungen",
"WAITING_SELLER": "Warte auf Sendung",
"COMPLETED": "Vertrag abgeschlossen",
@ -381,7 +381,7 @@
"SEND_BUTTON": "Senden",
"FORM_ERRORS": {
"DESC_REQUIRED": "Beschreibung erforderlich",
"DESC_MAXIMUM": "Maximum field length reached",
"DESC_MAXIMUM": "Maximale Feldlänge erreicht",
"SELLER_REQUIRED": "Adresse benötigt",
"SELLER_NOT_VALID": "Ungültige Adresse",
"ALIAS_NOT_VALID": "Ungültiger Alias",
@ -390,7 +390,7 @@
"YOUR_DEPOSIT_REQUIRED": "Einzahlung erforderlich",
"SELLER_DEPOSIT_REQUIRED": "Verkäufer-Einzahlung erforderlich",
"SELLER_SAME": "Anderes Konto verwenden",
"COMMENT_MAXIMUM": "Maximum field length reached"
"COMMENT_MAXIMUM": "Maximale Feldlänge erreicht"
},
"PROGRESS_NEW": "Neuer Kauf",
"PROGRESS_WAIT": "Warte auf Antwort",
@ -428,7 +428,7 @@
"NEED_MONEY": "Unzureichendes Guthaben",
"BUTTON_MAKE_PLEDGE": "Akzeptieren und Einzahlung tätigen",
"BUTTON_IGNORE": "Angebot ignorieren und ausblenden",
"BUTTON_NULLIFY": "Terminate and burn deposits",
"BUTTON_NULLIFY": "Beenden und Einzahlungen verbrennen",
"BUTTON_RECEIVED": "Einzahlungen abschließen und freigeben",
"BUTTON_CANCEL_BUYER": "Einzahlungen abbrechen und zurückzahlen",
"BUTTON_NOT_CANCEL": "Anfrage ignorieren",
@ -464,7 +464,7 @@
}
},
"STAKING": {
"TITLE": "Staking (Anlegen)",
"TITLE": "Staking",
"TITLE_PENDING": "Ausstehend",
"TITLE_TOTAL": "Gesamt",
"TITLE_PERIOD": "Zeitspanne:",
@ -518,7 +518,7 @@
"ADDRESS_NOT_VALID": "Adresse ungültig",
"SET_MASTER_PASSWORD": "Master-Passwort festlegen",
"ADDRESS_DUBLICATED": "Adresse existiert bereits",
"MAX_LENGTH": "Maximum notes length reached",
"MAX_LENGTH": "Maximale Länge der Notiz erreicht",
"NAME_LENGTH": "Der Name muss 4-25 Zeichen lang sein"
},
"BUTTON": {
@ -532,19 +532,21 @@
},
"SUCCESS_SENT": "Kontakt hinzugefügt",
"SUCCESS_SAVE": "Kontakt wurde bearbeitet",
"SUCCESS_IMPORT": "Contacts are imported",
"SUCCESS_EXPORT": "Contacts are exported",
"SUCCESS_IMPORT": "Kontakte wurden importiert",
"SUCCESS_EXPORT": "Kontakte wurden exportiert",
"ERROR_IMPORT": "Fehler beim Lesen der Datei!",
"ERROR_TYPE_FILE": "Bitte importieren Sie eine gültige .csv Datei.",
"ERROR_EXPORT": "Invalid file type. Save file as .csv"
"ERROR_TYPE_FILE": "Please import valid .csv file",
"ERROR_EXPORT": "Ungültiger Dateityp. Datei als .csv speichern",
"ERROR_EMPTY_LIST": "Contact list is empty",
"ERROR_IMPORT_EMPTY": "File is empty"
},
"ERRORS": {
"NO_MONEY": "Nicht genügend Geld",
"NOT_ENOUGH_MONEY": "Unzureichendes Guthaben im Konto",
"CORE_BUSY": "Interner Fehler: Kern ist beschäftigt",
"DAEMON_BUSY": "Interner Fehler: Daemon ist beschäftigt",
"NO_MONEY_REMOVE_OFFER": "There is no fee for deleting an offer, but in order to protect the network against flood transactions you need to have at least {{fee}} {{currency}} in your wallet",
"NOT_ENOUGH_OUTPUTS_TO_MIX": "Mix-in number is too big for current blockchain state. There are not enough unspent outputs to mix with",
"NO_MONEY_REMOVE_OFFER": "Es gibt keine Gebühr für das Löschen eines Angebots, aber um das Netzwerk vor Spam-Transaktionen zu schützen, müssen Sie mindestens {{fee}} {{currency}} in Ihrer Wallet haben",
"NOT_ENOUGH_OUTPUTS_TO_MIX": "Mix-in-Nummer ist zu groß für den aktuellen Blockchain-Status. Nicht genügend unverbrauchte outputs zum Mischen",
"TRANSACTION_IS_TO_BIG": "Transaktion überschreitet das Netzwerk-Limit. Sendet benötigten Betrag mit mehreren Transaktionen.",
"TRANSFER_ATTEMPT": "Keine Verbindung zum Zano-Netzwerk",
"ACCESS_DENIED": "Zugriff verweigert",
@ -561,7 +563,7 @@
"TX_TYPE_NORMAL_END": "wurde nicht abgeschlossen.",
"TX_TYPE_NEW_ALIAS": "Fehler. Fehler beim Registrieren des Alias zum Speichern",
"TX_TYPE_NEW_ALIAS_END": "Bitte nochmals versuchen.",
"TX_TYPE_UPDATE_ALIAS": "Error. Failed to change comment to alias in safe",
"TX_TYPE_UPDATE_ALIAS": "Fehler. Fehlgeschlagen Kommentar von gespeichertem Alias zu ändern",
"TX_TYPE_COIN_BASE": "Fehler. Die Zahlung wurde nicht abgeschlossen."
},
"CONTEXT_MENU": {
@ -577,8 +579,8 @@
"INCOME_TRANSFER_CONFIRMED": "Zahlung erhalten",
"MINED": "Mined",
"LOCKED": "Blockiert",
"IS_MINIMIZE": "Zano application is minimized to the system tray",
"RESTORE": "You can recover it by clicking or using the context menu",
"IS_MINIMIZE": "Zano-Anwendung wird auf die Systemleiste minimiert",
"RESTORE": "Sie können es wiederherstellen, indem Sie auf das Kontextmenü benutzen oder anklicken ",
"TRAY_MENU_SHOW": "Größe ändern",
"TRAY_MENU_MINIMIZE": "Minimieren"
}

View file

@ -535,8 +535,10 @@
"SUCCESS_IMPORT": "Contacts are imported",
"SUCCESS_EXPORT": "Contacts are exported",
"ERROR_IMPORT": "Error is occured while reading file!",
"ERROR_TYPE_FILE": "Please import valid .csv file.",
"ERROR_EXPORT": "Invalid file type. Save file as .csv"
"ERROR_TYPE_FILE": "Please import valid .csv file",
"ERROR_EXPORT": "Invalid file type. Save file as .csv",
"ERROR_EMPTY_LIST": "Contact list is empty",
"ERROR_IMPORT_EMPTY": "File is empty"
},
"ERRORS": {
"NO_MONEY": "Not enough money",

View file

@ -39,15 +39,18 @@
"SYNCING": "Syncing wallet"
},
"CONTACTS": "Contacts",
"CONTACTS_TOOLTIP": "Contacts option available only with Master Password enabled",
"SETTINGS": "Settings",
"LOG_OUT": "Log out",
"LOG_OUT_TOOLTIP": "Logout option available only with Master Password enabled",
"SYNCHRONIZATION": {
"OFFLINE": "Offline",
"ONLINE": "Online",
"ERROR": "System error",
"COMPLETE": "Completion",
"SYNCING": "Syncing blockchain",
"LOADING": "Loading blockchain data"
"SYNCING": "Syncing block",
"LOADING": "Loading blockchain data",
"SLASH": "/"
},
"UPDATE": {
"STANDARD": "Update available",

View file

@ -535,8 +535,10 @@
"SUCCESS_IMPORT": "Contacts are imported",
"SUCCESS_EXPORT": "Contacts are exported",
"ERROR_IMPORT": "Error is occured while reading file!",
"ERROR_TYPE_FILE": "Please import valid .csv file.",
"ERROR_EXPORT": "Invalid file type. Save file as .csv"
"ERROR_TYPE_FILE": "Please import valid .csv file",
"ERROR_EXPORT": "Invalid file type. Save file as .csv",
"ERROR_EMPTY_LIST": "Contact list is empty",
"ERROR_IMPORT_EMPTY": "File is empty"
},
"ERRORS": {
"NO_MONEY": "Not enough money",

View file

@ -535,8 +535,10 @@
"SUCCESS_IMPORT": "Contacts are imported",
"SUCCESS_EXPORT": "Contacts are exported",
"ERROR_IMPORT": "Error is occured while reading file!",
"ERROR_TYPE_FILE": "Please import valid .csv file.",
"ERROR_EXPORT": "Invalid file type. Save file as .csv"
"ERROR_TYPE_FILE": "Please import valid .csv file",
"ERROR_EXPORT": "Invalid file type. Save file as .csv",
"ERROR_EMPTY_LIST": "Contact list is empty",
"ERROR_IMPORT_EMPTY": "File is empty"
},
"ERRORS": {
"NO_MONEY": "Not enough money",

View file

@ -535,8 +535,10 @@
"SUCCESS_IMPORT": "Contacts are imported",
"SUCCESS_EXPORT": "Contacts are exported",
"ERROR_IMPORT": "Erreur lors de la lecture du fichier !",
"ERROR_TYPE_FILE": "Veuillez importer un fichier .csv valide.",
"ERROR_EXPORT": "Invalid file type. Save file as .csv"
"ERROR_TYPE_FILE": "Please import valid .csv file",
"ERROR_EXPORT": "Invalid file type. Save file as .csv",
"ERROR_EMPTY_LIST": "Contact list is empty",
"ERROR_IMPORT_EMPTY": "File is empty"
},
"ERRORS": {
"NO_MONEY": "Pas assez de fonds",

View file

@ -535,8 +535,10 @@
"SUCCESS_IMPORT": "Contacts are imported",
"SUCCESS_EXPORT": "Contacts are exported",
"ERROR_IMPORT": "Error is occured while reading file!",
"ERROR_TYPE_FILE": "Please import valid .csv file.",
"ERROR_EXPORT": "Invalid file type. Save file as .csv"
"ERROR_TYPE_FILE": "Please import valid .csv file",
"ERROR_EXPORT": "Invalid file type. Save file as .csv",
"ERROR_EMPTY_LIST": "Contact list is empty",
"ERROR_IMPORT_EMPTY": "File is empty"
},
"ERRORS": {
"NO_MONEY": "Not enough money",

View file

@ -535,8 +535,10 @@
"SUCCESS_IMPORT": "Contacts are imported",
"SUCCESS_EXPORT": "Contacts are exported",
"ERROR_IMPORT": "Error is occured while reading file!",
"ERROR_TYPE_FILE": "Please import valid .csv file.",
"ERROR_EXPORT": "Invalid file type. Save file as .csv"
"ERROR_TYPE_FILE": "Please import valid .csv file",
"ERROR_EXPORT": "Invalid file type. Save file as .csv",
"ERROR_EMPTY_LIST": "Contact list is empty",
"ERROR_IMPORT_EMPTY": "File is empty"
},
"ERRORS": {
"NO_MONEY": "Not enough money",

View file

@ -535,8 +535,10 @@
"SUCCESS_IMPORT": "Contacts are imported",
"SUCCESS_EXPORT": "Contacts are exported",
"ERROR_IMPORT": "Si è verificato un errore durante la lettura del file!",
"ERROR_TYPE_FILE": "Importa un file .csv valido.",
"ERROR_EXPORT": "Invalid file type. Save file as .csv"
"ERROR_TYPE_FILE": "Please import valid .csv file",
"ERROR_EXPORT": "Invalid file type. Save file as .csv",
"ERROR_EMPTY_LIST": "Contact list is empty",
"ERROR_IMPORT_EMPTY": "File is empty"
},
"ERRORS": {
"NO_MONEY": "Non abbastanza denaro",

View file

@ -535,8 +535,10 @@
"SUCCESS_IMPORT": "Contacts are imported",
"SUCCESS_EXPORT": "Contacts are exported",
"ERROR_IMPORT": "Error is occured while reading file!",
"ERROR_TYPE_FILE": "Please import valid .csv file.",
"ERROR_EXPORT": "Invalid file type. Save file as .csv"
"ERROR_TYPE_FILE": "Please import valid .csv file",
"ERROR_EXPORT": "Invalid file type. Save file as .csv",
"ERROR_EMPTY_LIST": "Contact list is empty",
"ERROR_IMPORT_EMPTY": "File is empty"
},
"ERRORS": {
"NO_MONEY": "Not enough money",

View file

@ -535,8 +535,10 @@
"SUCCESS_IMPORT": "Contacts are imported",
"SUCCESS_EXPORT": "Contacts are exported",
"ERROR_IMPORT": "Error is occured while reading file!",
"ERROR_TYPE_FILE": "Please import valid .csv file.",
"ERROR_EXPORT": "Invalid file type. Save file as .csv"
"ERROR_TYPE_FILE": "Please import valid .csv file",
"ERROR_EXPORT": "Invalid file type. Save file as .csv",
"ERROR_EMPTY_LIST": "Contact list is empty",
"ERROR_IMPORT_EMPTY": "File is empty"
},
"ERRORS": {
"NO_MONEY": "Not enough money",

View file

@ -535,8 +535,10 @@
"SUCCESS_IMPORT": "Contacts are imported",
"SUCCESS_EXPORT": "Contacts are exported",
"ERROR_IMPORT": "Error is occured while reading file!",
"ERROR_TYPE_FILE": "Please import valid .csv file.",
"ERROR_EXPORT": "Invalid file type. Save file as .csv"
"ERROR_TYPE_FILE": "Please import valid .csv file",
"ERROR_EXPORT": "Invalid file type. Save file as .csv",
"ERROR_EMPTY_LIST": "Contact list is empty",
"ERROR_IMPORT_EMPTY": "File is empty"
},
"ERRORS": {
"NO_MONEY": "Not enough money",

View file

@ -535,8 +535,10 @@
"SUCCESS_IMPORT": "Contacts are imported",
"SUCCESS_EXPORT": "Contacts are exported",
"ERROR_IMPORT": "Error is occured while reading file!",
"ERROR_TYPE_FILE": "Please import valid .csv file.",
"ERROR_EXPORT": "Invalid file type. Save file as .csv"
"ERROR_TYPE_FILE": "Please import valid .csv file",
"ERROR_EXPORT": "Invalid file type. Save file as .csv",
"ERROR_EMPTY_LIST": "Contact list is empty",
"ERROR_IMPORT_EMPTY": "File is empty"
},
"ERRORS": {
"NO_MONEY": "Not enough money",

View file

@ -535,8 +535,10 @@
"SUCCESS_IMPORT": "Contacts are imported",
"SUCCESS_EXPORT": "Contacts are exported",
"ERROR_IMPORT": "Error is occured while reading file!",
"ERROR_TYPE_FILE": "Please import valid .csv file.",
"ERROR_EXPORT": "Invalid file type. Save file as .csv"
"ERROR_TYPE_FILE": "Please import valid .csv file",
"ERROR_EXPORT": "Invalid file type. Save file as .csv",
"ERROR_EMPTY_LIST": "Contact list is empty",
"ERROR_IMPORT_EMPTY": "File is empty"
},
"ERRORS": {
"NO_MONEY": "Not enough money",

View file

@ -535,8 +535,10 @@
"SUCCESS_IMPORT": "Contacts are imported",
"SUCCESS_EXPORT": "Contacts are exported",
"ERROR_IMPORT": "Error is occured while reading file!",
"ERROR_TYPE_FILE": "Please import valid .csv file.",
"ERROR_EXPORT": "Invalid file type. Save file as .csv"
"ERROR_TYPE_FILE": "Please import valid .csv file",
"ERROR_EXPORT": "Invalid file type. Save file as .csv",
"ERROR_EMPTY_LIST": "Contact list is empty",
"ERROR_IMPORT_EMPTY": "File is empty"
},
"ERRORS": {
"NO_MONEY": "Not enough money",

View file

@ -535,8 +535,10 @@
"SUCCESS_IMPORT": "Contacts are imported",
"SUCCESS_EXPORT": "Contacts are exported",
"ERROR_IMPORT": "Error is occured while reading file!",
"ERROR_TYPE_FILE": "Please import valid .csv file.",
"ERROR_EXPORT": "Invalid file type. Save file as .csv"
"ERROR_TYPE_FILE": "Please import valid .csv file",
"ERROR_EXPORT": "Invalid file type. Save file as .csv",
"ERROR_EMPTY_LIST": "Contact list is empty",
"ERROR_IMPORT_EMPTY": "File is empty"
},
"ERRORS": {
"NO_MONEY": "Not enough money",

View file

@ -535,8 +535,10 @@
"SUCCESS_IMPORT": "Contacts are imported",
"SUCCESS_EXPORT": "Contacts are exported",
"ERROR_IMPORT": "Error is occured while reading file!",
"ERROR_TYPE_FILE": "Please import valid .csv file.",
"ERROR_EXPORT": "Invalid file type. Save file as .csv"
"ERROR_TYPE_FILE": "Please import valid .csv file",
"ERROR_EXPORT": "Invalid file type. Save file as .csv",
"ERROR_EMPTY_LIST": "Contact list is empty",
"ERROR_IMPORT_EMPTY": "File is empty"
},
"ERRORS": {
"NO_MONEY": "Not enough money",

View file

@ -535,8 +535,10 @@
"SUCCESS_IMPORT": "Contacts are imported",
"SUCCESS_EXPORT": "Contacts are exported",
"ERROR_IMPORT": "Error is occured while reading file!",
"ERROR_TYPE_FILE": "Please import valid .csv file.",
"ERROR_EXPORT": "Invalid file type. Save file as .csv"
"ERROR_TYPE_FILE": "Please import valid .csv file",
"ERROR_EXPORT": "Invalid file type. Save file as .csv",
"ERROR_EMPTY_LIST": "Contact list is empty",
"ERROR_IMPORT_EMPTY": "File is empty"
},
"ERRORS": {
"NO_MONEY": "Not enough money",

View file

@ -535,8 +535,10 @@
"SUCCESS_IMPORT": "Contacts are imported",
"SUCCESS_EXPORT": "Contacts are exported",
"ERROR_IMPORT": "Error is occured while reading file!",
"ERROR_TYPE_FILE": "Please import valid .csv file.",
"ERROR_EXPORT": "Invalid file type. Save file as .csv"
"ERROR_TYPE_FILE": "Please import valid .csv file",
"ERROR_EXPORT": "Invalid file type. Save file as .csv",
"ERROR_EMPTY_LIST": "Contact list is empty",
"ERROR_IMPORT_EMPTY": "File is empty"
},
"ERRORS": {
"NO_MONEY": "Not enough money",

View file

@ -535,8 +535,10 @@
"SUCCESS_IMPORT": "Contacts are imported",
"SUCCESS_EXPORT": "Contacts are exported",
"ERROR_IMPORT": "Error is occured while reading file!",
"ERROR_TYPE_FILE": "Please import valid .csv file.",
"ERROR_EXPORT": "Invalid file type. Save file as .csv"
"ERROR_TYPE_FILE": "Please import valid .csv file",
"ERROR_EXPORT": "Invalid file type. Save file as .csv",
"ERROR_EMPTY_LIST": "Contact list is empty",
"ERROR_IMPORT_EMPTY": "File is empty"
},
"ERRORS": {
"NO_MONEY": "Not enough money",

View file

@ -535,8 +535,10 @@
"SUCCESS_IMPORT": "Contacts are imported",
"SUCCESS_EXPORT": "Contacts are exported",
"ERROR_IMPORT": "Error is occured while reading file!",
"ERROR_TYPE_FILE": "Please import valid .csv file.",
"ERROR_EXPORT": "Invalid file type. Save file as .csv"
"ERROR_TYPE_FILE": "Please import valid .csv file",
"ERROR_EXPORT": "Invalid file type. Save file as .csv",
"ERROR_EMPTY_LIST": "Contact list is empty",
"ERROR_IMPORT_EMPTY": "File is empty"
},
"ERRORS": {
"NO_MONEY": "Not enough money",

View file

@ -535,8 +535,10 @@
"SUCCESS_IMPORT": "Contacts are imported",
"SUCCESS_EXPORT": "Contacts are exported",
"ERROR_IMPORT": "Error is occured while reading file!",
"ERROR_TYPE_FILE": "Please import valid .csv file.",
"ERROR_EXPORT": "Invalid file type. Save file as .csv"
"ERROR_TYPE_FILE": "Please import valid .csv file",
"ERROR_EXPORT": "Invalid file type. Save file as .csv",
"ERROR_EMPTY_LIST": "Contact list is empty",
"ERROR_IMPORT_EMPTY": "File is empty"
},
"ERRORS": {
"NO_MONEY": "Not enough money",

View file

@ -535,8 +535,10 @@
"SUCCESS_IMPORT": "Contacts are imported",
"SUCCESS_EXPORT": "Contacts are exported",
"ERROR_IMPORT": "Error is occured while reading file!",
"ERROR_TYPE_FILE": "Please import valid .csv file.",
"ERROR_EXPORT": "Invalid file type. Save file as .csv"
"ERROR_TYPE_FILE": "Please import valid .csv file",
"ERROR_EXPORT": "Invalid file type. Save file as .csv",
"ERROR_EMPTY_LIST": "Contact list is empty",
"ERROR_IMPORT_EMPTY": "File is empty"
},
"ERRORS": {
"NO_MONEY": "Not enough money",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -5800,8 +5800,8 @@ __webpack_require__.r(__webpack_exports__);
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
__webpack_require__(/*! C:\Users\butsd\Documents\Projects\Projects now\zano\src\gui\qt-daemon\html_source\src\polyfills.ts */"./src/polyfills.ts");
module.exports = __webpack_require__(/*! C:\Users\butsd\Documents\Projects\Projects now\zano\src\gui\qt-daemon\html_source\node_modules\@angular-devkit\build-angular\src\angular-cli-files\models\jit-polyfills.js */"./node_modules/@angular-devkit/build-angular/src/angular-cli-files/models/jit-polyfills.js");
__webpack_require__(/*! d:\Projects\zano\src\gui\qt-daemon\html_source\src\polyfills.ts */"./src/polyfills.ts");
module.exports = __webpack_require__(/*! d:\Projects\zano\src\gui\qt-daemon\html_source\node_modules\@angular-devkit\build-angular\src\angular-cli-files\models\jit-polyfills.js */"./node_modules/@angular-devkit/build-angular/src/angular-cli-files/models/jit-polyfills.js");
/***/ })

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 it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -22,6 +22,7 @@ export class VariablesService {
public exp_med_ts = 0;
public net_time_delta_median = 0;
public height_app = 0;
public height_max = 0;
public last_build_available = '';
public last_build_displaymode = 0;
public daemon_state = 3;
@ -59,18 +60,23 @@ export class VariablesService {
getExpMedTsEvent = new BehaviorSubject(null);
getHeightAppEvent = new BehaviorSubject(null);
getHeightMaxEvent = new BehaviorSubject(null);
getRefreshStackingEvent = new BehaviorSubject(null);
getAliasChangedEvent = new BehaviorSubject(null);
public idle = new Idle()
.whenNotInteractive()
.do(() => {
this.ngZone.run(() => {
if (this.appPass == '') {
this.restartCountdown();
} else {
this.ngZone.run(() => {
this.idle.stop();
this.appPass = '';
this.appLogin = false;
this.router.navigate(['/login'], {queryParams: {type: 'auth'}});
});
}
});
public allContextMenu: ContextMenuComponent;
@ -94,6 +100,13 @@ export class VariablesService {
}
}
setHeightMax(height: number) {
if (height !== this.height_max) {
this.height_max = height;
this.getHeightMaxEvent.next(height);
}
}
setRefreshStacking(wallet_id: number) {
this.getHeightAppEvent.next(wallet_id);
}

View file

@ -172,6 +172,7 @@ export class AppComponent implements OnInit, OnDestroy {
this.variablesService.last_build_available = data.last_build_available;
this.variablesService.last_build_displaymode = data.last_build_displaymode;
this.variablesService.setHeightApp(data.height);
this.variablesService.setHeightMax(data.max_net_seen_height);
this.ngZone.run(() => {
this.variablesService.daemon_state = data['daemon_network_state'];

View file

@ -49,8 +49,8 @@ export class ExportImportComponent implements OnInit {
header: true
};
const elements = this.papa.parse(data, options);
if (elements.data && !elements.errors.length) {
const isArray = Array.isArray(elements.data);
if (isArray && elements.data.length !== 0 && !elements.errors.length) {
if (!this.variablesService.contacts.length) {
elements.data.forEach(element => {
this.variablesService.contacts.push(element);

View file

@ -108,6 +108,8 @@ export class LoginComponent implements OnInit, OnDestroy {
this.variablesService.dataIsLoaded = true;
this.variablesService.startCountdown();
this.variablesService.appPass = appPass;
const isEmptyObject = Object.keys(data).length === 0 && data.constructor === Object;
if (this.variablesService.wallets.length) {
this.ngZone.run(() => {
this.router.navigate(['/wallet/' + this.variablesService.wallets[0].wallet_id]);
@ -131,7 +133,7 @@ export class LoginComponent implements OnInit, OnDestroy {
}
}
if (!data.hasOwnProperty('wallets') && !data.hasOwnProperty('contacts')) {
if (data.length !== 0) {
if (data.length !== 0 && !isEmptyObject) {
this.getWalletData(data);
} else {
this.ngZone.run(() => {

View file

@ -16,7 +16,8 @@
background-position: center;
background-size: 200%;
padding: 2rem;
width: 34rem;
min-width: 34rem;
max-width: 64rem;
.title {
font-size: 1.8rem;
@ -28,6 +29,8 @@
.wallet-path {
font-size: 1.3rem;
margin: 5rem 0 2rem;
word-wrap: break-word;
line-height: 2rem;
}
.wrap-button {

View file

@ -38,32 +38,48 @@
</div>
</div>
<div class="sidebar-settings">
<div class="wrap-button" routerLinkActive="active">
<button (click)="contactsRoute()" [class.disabled]="variablesService.daemon_state !== 2" [disabled]="variablesService.daemon_state !== 2">
<div class="wrap-button" routerLinkActive="active" *ngIf="variablesService.appPass === ''; else contactsShow" tooltip="{{ 'SIDEBAR.CONTACTS_TOOLTIP' | translate }}" placement="top" tooltipClass="table-tooltip account-tooltip" [delay]="500">
<button (click)="contactsRoute()" [class.disabled]="variablesService.daemon_state !== 2 || variablesService.appPass === ''" [disabled]="variablesService.daemon_state !== 2 || variablesService.appPass === ''">
<i class="icon contacts"></i>
<span>{{ 'SIDEBAR.CONTACTS' | translate }}</span>
</button>
</div>
<ng-template #contactsShow>
<div class="wrap-button" routerLinkActive="active">
<button (click)="contactsRoute()">
<i class="icon contacts"></i>
<span>{{ 'SIDEBAR.CONTACTS' | translate }}</span>
</button>
</div>
</ng-template>
<div class="wrap-button" routerLinkActive="active">
<button [routerLink]="['/settings']">
<i class="icon settings"></i>
<span>{{ 'SIDEBAR.SETTINGS' | translate }}</span>
</button>
</div>
<div class="wrap-button">
<button (click)="logOut()">
<div class="wrap-button" *ngIf="variablesService.appPass === ''; else masterPass" tooltip="{{ 'SIDEBAR.LOG_OUT_TOOLTIP' | translate }}" placement="bottom" tooltipClass="table-tooltip account-tooltip" [delay]="500">
<button (click)="logOut()" [class.disabled]="variablesService.appPass === ''" [disabled]="variablesService.appPass === ''">
<i class="icon logout"></i>
<span>{{ 'SIDEBAR.LOG_OUT' | translate }}</span>
</button>
</div>
<ng-template #masterPass>
<div class="wrap-button">
<button (click)="logOut()">
<i class="icon logout"></i>
<span>{{ 'SIDEBAR.LOG_OUT' | translate }}</span>
</button>
</div>
</ng-template>
</div>
<div class="sidebar-synchronization-status">
<div class="sidebar-synchronization-status" [ngStyle]="{'align-items': variablesService.daemon_state === 1 ? 'flex-start' : 'center'}">
<div class="status-container">
<span class="offline" *ngIf="variablesService.daemon_state === 0">
{{ 'SIDEBAR.SYNCHRONIZATION.OFFLINE' | translate }}
</span>
<span class="syncing" *ngIf="variablesService.daemon_state === 1">
{{ 'SIDEBAR.SYNCHRONIZATION.SYNCING' | translate }}
{{ 'SIDEBAR.SYNCHRONIZATION.SYNCING' | translate }} {{ variablesService.height_app }}{{ 'SIDEBAR.SYNCHRONIZATION.SLASH' | translate }}{{ variablesService.height_max }}
</span>
<span class="online" *ngIf="variablesService.daemon_state === 2">
{{ 'SIDEBAR.SYNCHRONIZATION.ONLINE' | translate }}
@ -118,3 +134,4 @@
<i class="icon time" tooltip="{{ 'SIDEBAR.UPDATE.TIME_TOOLTIP' | translate }}" placement="right-bottom" tooltipClass="update-tooltip important" [delay]="500"></i>
</div>
</div>

View file

@ -253,7 +253,7 @@
}
.syncing, .loading {
line-height: 4rem;
line-height: 5rem;
}
.progress-bar-container {
@ -279,7 +279,7 @@
font-size: 1.3rem;
line-height: 0.7rem;
padding-left: 0.7rem;
}
}
}
.loading {

View file

@ -39,15 +39,18 @@
"SYNCING": "Syncing wallet"
},
"CONTACTS": "Contacts",
"CONTACTS_TOOLTIP": "Contacts option available only with Master Password enabled",
"SETTINGS": "Settings",
"LOG_OUT": "Log out",
"LOG_OUT_TOOLTIP": "Logout option available only with Master Password enabled",
"SYNCHRONIZATION": {
"OFFLINE": "Offline",
"ONLINE": "Online",
"ERROR": "System error",
"COMPLETE": "Completion",
"SYNCING": "Syncing blockchain",
"LOADING": "Loading blockchain data"
"SYNCING": "Syncing block",
"LOADING": "Loading blockchain data",
"SLASH": "/"
},
"UPDATE": {
"STANDARD": "Update available",

View file

@ -42,25 +42,37 @@ int main(int argc, char *argv[])
#endif
epee::string_tools::set_module_name_and_folder(argv[0]);
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#ifdef WIN32
WCHAR sz_file_name[MAX_PATH + 1] = L"";
::GetModuleFileNameW(NULL, sz_file_name, MAX_PATH + 1);
std::string path_to_process_utf8 = epee::string_encoding::wstring_to_utf8(sz_file_name);
#else
std::string path_to_process_utf8 = argv[0];
#endif
TRY_ENTRY();
epee::string_tools::set_module_name_and_folder(path_to_process_utf8);
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#ifdef _MSC_VER
#if _MSC_VER >= 1910
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); //HiDPI pixmaps
qputenv("QT_SCALE_FACTOR", "0.75");
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); //HiDPI pixmaps
qputenv("QT_SCALE_FACTOR", "0.75");
#endif
#endif
QApplication app(argc, argv);
MainWindow viewer;
if (!viewer.init_backend(argc, argv))
{
static_cast<view::i_view*>(&viewer)->show_msg_box("Failed to initialize backend, check debug logs for more details.");
return 1;
}
app.installNativeEventFilter(&viewer);
viewer.setWindowTitle(CURRENCY_NAME_BASE);
viewer.show_inital();
return app.exec();
QApplication app(argc, argv);
MainWindow viewer;
if (!viewer.init_backend(argc, argv))
{
static_cast<view::i_view*>(&viewer)->show_msg_box("Failed to initialize backend, check debug logs for more details.");
return 1;
}
app.installNativeEventFilter(&viewer);
viewer.setWindowTitle(CURRENCY_NAME_BASE);
viewer.show_inital();
int res = app.exec();
LOG_PRINT_L0("Process exit with code: " << res);
return res;
CATCH_ENTRY2(0);
}

View file

@ -131,11 +131,14 @@ namespace nodetool
HANDLE_INVOKE_T2(COMMAND_TIMED_SYNC, &node_server::handle_timed_sync)
HANDLE_INVOKE_T2(COMMAND_PING, &node_server::handle_ping)
#ifdef ALLOW_DEBUG_COMMANDS
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)
if (m_debug_requests_enabled)
{
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()
@ -239,6 +242,7 @@ namespace nodetool
bool m_allow_local_ip;
bool m_hide_my_port;
bool m_offline_mode;
bool m_debug_requests_enabled;
uint64_t m_startup_time;
//critical_section m_connections_lock;

View file

@ -36,6 +36,7 @@ namespace nodetool
const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_seed_node = {"seed-node", "Connect to a node to retrieve peer addresses, and disconnect"};
const command_line::arg_descriptor<bool> arg_p2p_hide_my_port = {"hide-my-port", "Do not announce yourself as peerlist candidate", false, true};
const command_line::arg_descriptor<bool> arg_p2p_offline_mode = { "offline-mode", "Don't connect to any node and reject any connections", false, true };
const command_line::arg_descriptor<bool> arg_p2p_disable_debug_reqs = { "disable-debug-p2p-requests", "Disable p2p debug requests", false, true };
}
//-----------------------------------------------------------------------------------
@ -51,6 +52,7 @@ namespace nodetool
command_line::add_arg(desc, arg_p2p_seed_node);
command_line::add_arg(desc, arg_p2p_hide_my_port);
command_line::add_arg(desc, arg_p2p_offline_mode);
command_line::add_arg(desc, arg_p2p_disable_debug_reqs);
command_line::add_arg(desc, arg_p2p_use_only_priority_nodes);
}
//-----------------------------------------------------------------------------------
@ -158,6 +160,7 @@ namespace nodetool
m_external_port = command_line::get_arg(vm, arg_p2p_external_port);
m_allow_local_ip = command_line::get_arg(vm, arg_p2p_allow_local_ip);
m_offline_mode = command_line::get_arg(vm, arg_p2p_offline_mode);
m_debug_requests_enabled = !command_line::get_arg(vm, arg_p2p_disable_debug_reqs);
if (m_offline_mode)
{

View file

@ -154,11 +154,6 @@ namespace
return message_writer(color ? epee::log_space::console_color_green : epee::log_space::console_color_default, false, std::string(), LOG_LEVEL_2);
}
message_writer success_msg_writer(epee::log_space::console_colors color)
{
return message_writer(color, true, std::string(), LOG_LEVEL_2);
}
message_writer fail_msg_writer()
{
return message_writer(epee::log_space::console_color_red, true, "Error: ", LOG_LEVEL_0);

View file

@ -1,7 +1,12 @@
#include "misc_language.h"
#define BUILD_COMMIT_ID "@VERSION@"
#define PROJECT_VERSION "1.0"
#define PROJECT_VERSION_BUILD_NO 49
#define PROJECT_MAJOR_VERSION "1"
#define PROJECT_MINOR_VERSION "1"
#define PROJECT_REVISION "0"
#define PROJECT_VERSION PROJECT_MAJOR_VERSION "." PROJECT_MINOR_VERSION "." PROJECT_REVISION
#define PROJECT_VERSION_BUILD_NO 59
#define PROJECT_VERSION_BUILD_NO_STR STRINGIFY_EXPAND(PROJECT_VERSION_BUILD_NO)
#define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO_STR "[" BUILD_COMMIT_ID "]"

View file

@ -1890,7 +1890,7 @@ void wallet2::load_keys2ki(bool create_if_not_exist, bool& need_to_resync)
m_pending_key_images.clear();
for (size_t i = 0, size = m_pending_key_images_file_container.size(); i < size; ++i)
{
out_key_to_ki item = AUTO_VAL_INIT(item);
out_key_to_ki item = AUTO_VAL_INIT_T(out_key_to_ki);
ok = m_pending_key_images_file_container.get_item(i, item);
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(ok, "m_pending_key_images_file_container.get_item() failed for index " << i << ", size: " << m_pending_key_images_file_container.size());
ok = m_pending_key_images.insert(std::make_pair(item.out_key, item.key_image)).second;
@ -4002,7 +4002,7 @@ void wallet2::prepare_transaction(const construct_tx_param& ctp, finalize_tx_par
//----------------------------------------------------------------------------------------------------
void wallet2::finalize_transaction(const finalize_tx_param& ftp, currency::transaction& tx, crypto::secret_key& tx_key, bool broadcast_tx)
{
TIME_MEASURE_START_MS(construct_tx_time);
//TIME_MEASURE_START_MS(construct_tx_time);
bool r = currency::construct_tx(m_account.get_keys(),
ftp.sources,
ftp.prepared_destinations,
@ -4016,10 +4016,10 @@ void wallet2::finalize_transaction(const finalize_tx_param& ftp, currency::trans
ftp.tx_outs_attr,
ftp.shuffle,
ftp.flags);
TIME_MEASURE_FINISH_MS(construct_tx_time);
//TIME_MEASURE_FINISH_MS(construct_tx_time);
THROW_IF_FALSE_WALLET_EX(r, error::tx_not_constructed, ftp.sources, ftp.prepared_destinations, ftp.unlock_time);
TIME_MEASURE_START_MS(sign_ms_input_time);
//TIME_MEASURE_START_MS(sign_ms_input_time);
if (ftp.multisig_id != currency::null_hash)
{
// In case there's multisig input is used -- sign it partially with this wallet's keys (we don't have any others here).
@ -4031,21 +4031,21 @@ void wallet2::finalize_transaction(const finalize_tx_param& ftp, currency::trans
r = sign_multisig_input_in_tx(tx, 0, m_account.get_keys(), ms_source_tx, &is_tx_input_fully_signed); // it's assumed that ms input is the first one (index 0)
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(r && !is_tx_input_fully_signed, "sign_multisig_input_in_tx failed: r = " << r << ", is_tx_input_fully_signed = " << is_tx_input_fully_signed);
}
TIME_MEASURE_FINISH_MS(sign_ms_input_time);
//TIME_MEASURE_FINISH_MS(sign_ms_input_time);
m_tx_keys.insert(std::make_pair(get_transaction_hash(tx), tx_key));
THROW_IF_FALSE_WALLET_EX(get_object_blobsize(tx) < CURRENCY_MAX_TRANSACTION_BLOB_SIZE, error::tx_too_big, tx, m_upper_transaction_size_limit);
TIME_MEASURE_START(send_transaction_to_network_time);
//TIME_MEASURE_START(send_transaction_to_network_time);
if (broadcast_tx)
send_transaction_to_network(tx);
TIME_MEASURE_FINISH(send_transaction_to_network_time);
//TIME_MEASURE_FINISH(send_transaction_to_network_time);
TIME_MEASURE_START(add_sent_tx_detailed_info_time);
//TIME_MEASURE_START(add_sent_tx_detailed_info_time);
if (broadcast_tx)
add_sent_tx_detailed_info(tx, ftp.prepared_destinations, ftp.selected_transfers);
TIME_MEASURE_FINISH(add_sent_tx_detailed_info_time);
//TIME_MEASURE_FINISH(add_sent_tx_detailed_info_time);
/* TODO
WLT_LOG_GREEN("[prepare_transaction]: get_needed_money_time: " << get_needed_money_time << " ms"

View file

@ -4,6 +4,8 @@
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#define USE_INSECURE_RANDOM_RPNG_ROUTINES // turns on pseudorandom number generator manupulations for tests
#include <vector>
#include <iostream>
#include <sstream>
@ -176,7 +178,7 @@ bool test_generator::add_block_info(const currency::block& b, const std::list<cu
sk.stake_modifier.last_pos_kernel_id = chain[pos_idx]->ks_hash;
else
{
CHECK_AND_ASSERT_MES(string_tools::parse_tpod_from_hex_string(POS_STARTER_KERNEL_HASH, sk.stake_modifier.last_pos_kernel_id), false, "Failed to parse POS_STARTER_MODFIFIER");
CHECK_AND_ASSERT_MES(string_tools::parse_tpod_from_hex_string(POS_STARTER_KERNEL_HASH, sk.stake_modifier.last_pos_kernel_id), false, "Failed to parse POS_STARTER_KERNEL_HASH");
}
uint64_t pow_idx = get_last_block_of_type(false, chain);
sk.stake_modifier.last_pow_id = get_block_hash(chain[pow_idx]->b);
@ -686,7 +688,7 @@ bool test_generator::build_stake_modifier(stake_modifier_type& sm, const test_ge
else
{
bool r = string_tools::parse_tpod_from_hex_string(POS_STARTER_KERNEL_HASH, sm.last_pos_kernel_id);
CHECK_AND_ASSERT_MES(r, false, "Failed to parse POS_STARTER_MODFIFIER");
CHECK_AND_ASSERT_MES(r, false, "Failed to parse POS_STARTER_KERNEL_HASH");
}
sm.last_pow_id = get_block_hash(blck_chain[last_pow_i]->b);
@ -1030,44 +1032,46 @@ namespace
bool init_output_indices(map_output_idx_t& outs, map_output_t& outs_mine, const std::vector<currency::block>& blockchain, const map_hash2tx_t& mtx, const currency::account_keys& acc_keys)
{
for(const block& blk : blockchain)
for (const block& blk : blockchain)
{
volatile uint64_t height = get_block_height(blk);
std::vector<const transaction*> vtx;
vtx.push_back(&blk.miner_tx);
for (const crypto::hash &h : blk.tx_hashes)
{
std::vector<const transaction*> vtx;
vtx.push_back(&blk.miner_tx);
for(const crypto::hash &h : blk.tx_hashes)
{
const map_hash2tx_t::const_iterator cit = mtx.find(h);
CHECK_AND_ASSERT_MES(cit != mtx.end(), false, "block at height " << get_block_height(blk) << " contains a reference to unknown tx " << h);
vtx.push_back(cit->second);
}
for (size_t i = 0; i < vtx.size(); i++)
{
const transaction &tx = *vtx[i];
crypto::key_derivation derivation;
bool r = generate_key_derivation(get_tx_pub_key_from_extra(tx), acc_keys.m_view_secret_key, derivation);
CHECK_AND_ASSERT_MES(r, false, "generate_key_derivation failed");
for (size_t j = 0; j < tx.vout.size(); ++j)
{
const tx_out &out = tx.vout[j];
output_index oi(out.target, out.amount, boost::get<txin_gen>(*blk.miner_tx.vin.begin()).height, i, j, &blk, vtx[i]);
if (out.target.type() == typeid(txout_to_key))
{
outs[out.amount].push_back(oi);
size_t tx_global_idx = outs[out.amount].size() - 1;
outs[out.amount][tx_global_idx].idx = tx_global_idx;
// Is out to me?
if (is_out_to_acc(acc_keys, boost::get<txout_to_key>(out.target), derivation, j))
outs_mine[out.amount].push_back(tx_global_idx);
}
}
}
const map_hash2tx_t::const_iterator cit = mtx.find(h);
CHECK_AND_ASSERT_MES(cit != mtx.end(), false, "block at height " << get_block_height(blk) << " contains a reference to unknown tx " << h);
vtx.push_back(cit->second);
}
return true;
for (size_t i = 0; i < vtx.size(); i++)
{
const transaction &tx = *vtx[i];
crypto::key_derivation derivation;
bool r = generate_key_derivation(get_tx_pub_key_from_extra(tx), acc_keys.m_view_secret_key, derivation);
CHECK_AND_ASSERT_MES(r, false, "generate_key_derivation failed");
for (size_t j = 0; j < tx.vout.size(); ++j)
{
const tx_out &out = tx.vout[j];
output_index oi(out.target, out.amount, boost::get<txin_gen>(*blk.miner_tx.vin.begin()).height, i, j, &blk, vtx[i]);
if (out.target.type() == typeid(txout_to_key))
{
outs[out.amount].push_back(oi);
size_t tx_global_idx = outs[out.amount].size() - 1;
outs[out.amount][tx_global_idx].idx = tx_global_idx;
// Is out to me?
if (is_out_to_acc(acc_keys, boost::get<txout_to_key>(out.target), derivation, j))
outs_mine[out.amount].push_back(tx_global_idx);
}
}
}
}
return true;
}
bool init_spent_output_indices(map_output_idx_t& outs, map_output_t& outs_mine, const std::vector<currency::block>& blockchain, const map_hash2tx_t& mtx, const currency::account_keys& from)

View file

@ -9,6 +9,8 @@
#include <string>
#include <iostream>
#define USE_INSECURE_RANDOM_RPNG_ROUTINES // turns on pseudorandom number generator manupulations for tests
#include "currency_core/currency_basic.h"
#include "currency_core/currency_core.h"
#include "wallet/wallet2.h"
@ -1124,13 +1126,13 @@ void append_vector_by_another_vector(U& dst, const V& src)
#define MAKE_TX_ATTACH(EVENTS, TX_VAR, FROM, TO, AMOUNT, HEAD, ATTACH) MAKE_TX_ATTACH_FEE(EVENTS, TX_VAR, FROM, TO, AMOUNT, TESTS_DEFAULT_FEE, HEAD, ATTACH)
#define MAKE_MINER_TX_AND_KEY_MANUALLY(TX, BLK, KEY) \
transaction TX; \
if (!construct_miner_tx_manually(get_block_height(BLK) + 1, generator.get_already_generated_coins(BLK), \
miner_account.get_keys().m_account_address, TX, 0, KEY)) \
#define MAKE_MINER_TX_AND_KEY_MANUALLY(TX, PREV_BLOCK, P_KEYPAIR) \
transaction TX; \
if (!construct_miner_tx_manually(get_block_height(PREV_BLOCK) + 1, generator.get_already_generated_coins(PREV_BLOCK), \
miner_account.get_keys().m_account_address, TX, 0, P_KEYPAIR)) \
return false;
#define MAKE_MINER_TX_MANUALLY(TX, BLK) MAKE_MINER_TX_AND_KEY_MANUALLY(TX, BLK, 0)
#define MAKE_MINER_TX_MANUALLY(TX, PREV_BLOCK) MAKE_MINER_TX_AND_KEY_MANUALLY(TX, PREV_BLOCK, nullptr)
#define SET_EVENT_VISITOR_SETT(VEC_EVENTS, SETT, VAL) VEC_EVENTS.push_back(event_visitor_settings(SETT, VAL));

View file

@ -947,6 +947,7 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY(hard_fork_1_unlock_time_2_in_coinbase);
GENERATE_AND_PLAY(hard_fork_1_chain_switch_pow_only);
GENERATE_AND_PLAY(hard_fork_1_checkpoint_basic_test);
GENERATE_AND_PLAY(hard_fork_1_pos_locked_height_vs_time);
//GENERATE_AND_PLAY(gen_block_reward); */
@ -1023,3 +1024,11 @@ void tx2log(const currency::transaction& tx)
currency::transaction ltx = tx;
LOG_PRINT("!dbg transaction: " << currency::get_transaction_hash(ltx) << ENDL << currency::obj_to_json_str(ltx), LOG_LEVEL_0);
}
const char* amount2log(const uint64_t amount)
{
static std::string s;
s = currency::print_money_brief(amount);
LOG_PRINT("!dbg amount: " << s, LOG_LEVEL_0);
return s.c_str();
}

View file

@ -10,6 +10,20 @@
using namespace currency;
template<typename extra_t>
void remove_unlock_v1_entries_from_extra(extra_t& extra)
{
extra.erase(std::remove_if(extra.begin(), extra.end(), [](extra_v& extra_element) { return extra_element.type() == typeid(etc_tx_details_unlock_time); }), extra.end());
}
template<typename extra_t>
void remove_unlock_v2_entries_from_extra(extra_t& extra)
{
extra.erase(std::remove_if(extra.begin(), extra.end(), [](extra_v& extra_element) { return extra_element.type() == typeid(etc_tx_details_unlock_time2); }), extra.end());
}
//------------------------------------------------------------------------------
hard_fork_1_base_test::hard_fork_1_base_test(size_t hardfork_height)
: m_hardfork_height(hardfork_height)
{
@ -28,7 +42,6 @@ bool hard_fork_1_base_test::configure_core(currency::core& c, size_t ev_index, c
//------------------------------------------------------------------------------
hard_fork_1_unlock_time_2_in_normal_tx::hard_fork_1_unlock_time_2_in_normal_tx()
: hard_fork_1_base_test(12)
{
@ -299,22 +312,28 @@ bool hard_fork_1_checkpoint_basic_test::generate(std::vector<test_event_entry>&
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time());
generator.set_hardfork_height(m_hardfork_height);
DO_CALLBACK(events, "configure_core");
DO_CALLBACK_PARAMS(events, "set_checkpoint", params_checkpoint(CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 2));
DO_CALLBACK_PARAMS(events, "set_checkpoint", params_checkpoint(2 * CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 7));
REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
DO_CALLBACK(events, "check_being_in_cp_zone"); // make sure CP was has passed
//
// before hardfork 1
//
std::vector<tx_source_entry> sources;
CHECK_AND_ASSERT_MES(fill_tx_sources(sources, events, blk_0r, miner_acc.get_keys(), MK_TEST_COINS(90) + TESTS_DEFAULT_FEE, 0), false, "");
uint64_t stake_lock_time = 100; // locked till block 100
std::vector<tx_destination_entry> destinations;
CHECK_AND_ASSERT_MES(fill_tx_sources_and_destinations(events, blk_0r, miner_acc, alice_acc, MK_TEST_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations), false, "");
destinations.push_back(tx_destination_entry(MK_TEST_COINS(90), alice_acc.get_public_address()));
// set unlock_time_2, should be rejected before hardfork 1
std::vector<extra_v> extra;
etc_tx_details_unlock_time2 ut2 = AUTO_VAL_INIT(ut2);
ut2.unlock_time_array.resize(destinations.size());
ut2.unlock_time_array[0] = 1; // not zero, unlocked from block 1
ut2.unlock_time_array[0] = stake_lock_time;
extra.push_back(ut2);
transaction tx_0 = AUTO_VAL_INIT(tx_0);
crypto::secret_key tx_sec_key;
@ -329,21 +348,120 @@ bool hard_fork_1_checkpoint_basic_test::generate(std::vector<test_event_entry>&
DO_CALLBACK(events, "clear_tx_pool");
MAKE_NEXT_BLOCK(events, blk_1, blk_0r, miner_acc);
MAKE_NEXT_BLOCK(events, blk_2, blk_1, miner_acc); // <-- checkpoint
MAKE_NEXT_BLOCK(events, blk_2, blk_1, miner_acc);
MAKE_NEXT_BLOCK(events, blk_3, blk_2, miner_acc); // <-- hard fork
MAKE_NEXT_BLOCK(events, blk_4, blk_3, miner_acc);
// make sure hardfork went okay
CHECK_AND_ASSERT_MES(blk_3.major_version != CURRENT_BLOCK_MAJOR_VERSION && blk_4.major_version == CURRENT_BLOCK_MAJOR_VERSION, false, "hardfork did not happen as expected");
//
// after hardfork 1
//
// now tx_0 is okay and can be added to the blockchain
events.push_back(tx_0);
MAKE_NEXT_BLOCK_TX1(events, blk_5, blk_4, miner_acc, tx_0);
REWIND_BLOCKS_N_WITH_TIME(events, blk_5r, blk_5, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
CREATE_TEST_WALLET(alice_wlt, alice_acc, blk_0);
REFRESH_TEST_WALLET_AT_GEN_TIME(events, alice_wlt, blk_5r, 2 * CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 5);
CHECK_TEST_WALLET_BALANCE_AT_GEN_TIME(alice_wlt, MK_TEST_COINS(90));
// try to mine a PoS block using locked coins
block blk_6;
{
const block& prev_block = blk_5r;
const transaction& stake = tx_0;
const account_base& stakeholder = alice_acc;
crypto::hash prev_id = get_block_hash(prev_block);
size_t height = get_block_height(prev_block) + 1;
currency::wide_difficulty_type diff = generator.get_difficulty_for_next_block(prev_id, false);
crypto::public_key stake_tx_pub_key = get_tx_pub_key_from_extra(stake);
size_t stake_output_idx = 0;
size_t stake_output_gidx = 0;
uint64_t stake_output_amount = stake.vout[stake_output_idx].amount;
crypto::key_image stake_output_key_image;
keypair kp;
generate_key_image_helper(stakeholder.get_keys(), stake_tx_pub_key, stake_output_idx, kp, stake_output_key_image);
crypto::public_key stake_output_pubkey = boost::get<txout_to_key>(stake.vout[stake_output_idx].target).key;
pos_block_builder pb;
pb.step1_init_header(height, prev_id);
pb.m_block.major_version = CURRENT_BLOCK_MAJOR_VERSION;
pb.step2_set_txs(std::vector<transaction>());
pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, prev_block.timestamp);
pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(prev_block), miner_acc.get_public_address(), stakeholder.get_public_address());
// set etc_tx_details_unlock_time2
remove_unlock_v1_entries_from_extra(pb.m_block.miner_tx.extra); // clear already set unlock
std::vector<extra_v> extra;
etc_tx_details_unlock_time2 ut2 = AUTO_VAL_INIT(ut2);
ut2.unlock_time_array.push_back(height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW); // reward lock
for(size_t i = 0; i < pb.m_block.miner_tx.vout.size() - 1; ++i)
ut2.unlock_time_array.push_back(stake_lock_time); // using the same lock time as stake input
extra.push_back(ut2);
pb.m_block.miner_tx.extra.push_back(ut2);
pb.step5_sign(stake_tx_pub_key, stake_output_idx, stake_output_pubkey, stakeholder);
blk_6 = pb.m_block;
}
events.push_back(blk_6);
generator.add_block_info(blk_6, std::list<transaction>()); // add modified block info
MAKE_NEXT_BLOCK(events, blk_7, blk_6, miner_acc); // <-- checkpoint
MAKE_NEXT_BLOCK(events, blk_8, blk_7, miner_acc);
DO_CALLBACK(events, "check_not_being_in_cp_zone"); // make sure CP was has passed
REWIND_BLOCKS_N_WITH_TIME(events, blk_8r, blk_8, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
// make sure locked Alice's coins still can't be spent
sources.clear();
r = fill_tx_sources(sources, events, blk_8r, alice_acc.get_keys(), MK_TEST_COINS(90), 0 /* nmix */, true /* check for spends */, false /* check for unlock time */);
CHECK_AND_ASSERT_MES(r, false, "fill_tx_sources failed");
transaction tx_1 = AUTO_VAL_INIT(tx_1);
r = construct_tx(alice_acc.get_keys(), sources, std::vector<tx_destination_entry>{ tx_destination_entry(MK_TEST_COINS(90) - TESTS_DEFAULT_FEE, miner_acc.get_public_address()) },
empty_attachment, tx_1, stake_lock_time /* try to use stake unlock time -- should not work as it is not a coinbase */);
CHECK_AND_ASSERT_MES(r, false, "construct_tx failed");
DO_CALLBACK(events, "mark_invalid_tx");
events.push_back(tx_1);
// mine another PoS block using the same stake after a checkpoint
std::list<currency::account_base> pos_stakeing_accounts{alice_acc};
MAKE_NEXT_POS_BLOCK(events, blk_9, blk_8r, miner_acc, pos_stakeing_accounts);
return true;
}
//------------------------------------------------------------------------------
struct unique_amount_params
{
unique_amount_params(uint64_t amount, uint64_t count) : amount(amount), count(count) {}
uint64_t amount;
uint64_t count;
};
// check that the given amount has only one non-zero gidit
// 3000 => true
// 11000 => false
bool does_amount_have_one_non_zero_digit(uint64_t amount)
{
size_t count = 0;
auto f = [&count](uint64_t){ ++count; };
decompose_amount_into_digits(amount, DEFAULT_DUST_THRESHOLD, f, f);
return count == 1;
}
hard_fork_1_pos_and_locked_coins::hard_fork_1_pos_and_locked_coins()
: hard_fork_1_base_test(13) // hardfork height
, m_unique_amount(TESTS_DEFAULT_FEE * 9)
: hard_fork_1_base_test(25) // hardfork height
{
REGISTER_CALLBACK_METHOD(hard_fork_1_pos_and_locked_coins, check_outputs_with_unique_amount);
}
@ -353,14 +471,24 @@ bool hard_fork_1_pos_and_locked_coins::generate(std::vector<test_event_entry>& e
bool r = false;
GENERATE_ACCOUNT(miner_acc);
GENERATE_ACCOUNT(alice_acc);
GENERATE_ACCOUNT(bob_acc);
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time());
generator.set_hardfork_height(m_hardfork_height);
DO_CALLBACK(events, "configure_core");
REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 3);
DO_CALLBACK_PARAMS(events, "check_outputs_with_unique_amount", static_cast<size_t>(0));
const uint64_t unique_amount_alice = TESTS_DEFAULT_FEE * 9;
const uint64_t unique_amount_bob = TESTS_DEFAULT_FEE * 3;
CHECK_AND_ASSERT_MES(does_amount_have_one_non_zero_digit(unique_amount_alice), false, "does_amount_have_one_non_zero_digit failed for Alice");
CHECK_AND_ASSERT_MES(does_amount_have_one_non_zero_digit(unique_amount_bob), false, "does_amount_have_one_non_zero_digit failed for Bob");
// make sure no outputs have such unique amounts
DO_CALLBACK_PARAMS(events, "check_outputs_with_unique_amount", unique_amount_params(unique_amount_alice, 0) );
DO_CALLBACK_PARAMS(events, "check_outputs_with_unique_amount", unique_amount_params(unique_amount_bob, 0) );
// create few locked outputs in the blockchain with unique amount
// tx_0 : miner -> Alice
std::vector<extra_v> extra;
etc_tx_details_unlock_time ut = AUTO_VAL_INIT(ut);
ut.v = 100; // locked until block 100
@ -368,56 +496,379 @@ bool hard_fork_1_pos_and_locked_coins::generate(std::vector<test_event_entry>& e
std::vector<tx_destination_entry> destinations;
for (size_t i = 0; i < 5; ++i)
destinations.push_back(tx_destination_entry(m_unique_amount, alice_acc.get_public_address()));
destinations.push_back(tx_destination_entry(unique_amount_alice, alice_acc.get_public_address()));
transaction tx_0 = AUTO_VAL_INIT(tx_0);
r = construct_tx_to_key(events, tx_0, blk_0r, miner_acc, destinations, TESTS_DEFAULT_FEE, 0, 0, extra);
CHECK_AND_ASSERT_MES(r, false, "construct_tx failed");
events.push_back(tx_0);
// tx_1 : miner -> Bob
extra.clear();
uint64_t ut2_unlock_time = 100; // locked until block 100
etc_tx_details_unlock_time2 ut2 = AUTO_VAL_INIT(ut2);
destinations.clear();
for (size_t i = 0; i < 5; ++i)
{
destinations.push_back(tx_destination_entry(unique_amount_bob, bob_acc.get_public_address()));
ut2.unlock_time_array.push_back(ut2_unlock_time);
}
ut2.unlock_time_array.push_back(ut2_unlock_time);
extra.push_back(ut2);
transaction tx_1 = AUTO_VAL_INIT(tx_1);
r = construct_tx_to_key(events, tx_1, blk_0r, miner_acc, destinations, TESTS_DEFAULT_FEE, 0, 0, extra);
CHECK_AND_ASSERT_MES(r, false, "construct_tx failed");
events.push_back(tx_1);
MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner_acc, tx_0);
DO_CALLBACK_PARAMS(events, "check_outputs_with_unique_amount", static_cast<size_t>(5));
// block with tx_1 should be rejected because etc_tx_details_unlock_time2 is not allowed prior to hardfork 1
DO_CALLBACK(events, "mark_invalid_block");
MAKE_NEXT_BLOCK_TX1(events, blk_1b, blk_1, miner_acc, tx_1);
REWIND_BLOCKS_N_WITH_TIME(events, blk_1r, blk_1, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
block blk_0a;
DO_CALLBACK_PARAMS(events, "check_outputs_with_unique_amount", unique_amount_params(unique_amount_alice, 5) );
// make sure outputs with m_unique_amount are still locked
r = false;
try
{
crypto::hash prev_id = get_block_hash(blk_0);
size_t height = get_block_height(blk_0) + 1;
MAKE_TX(events, tx_1_bad, alice_acc, miner_acc, unique_amount_alice - TESTS_DEFAULT_FEE, blk_1r);
}
catch (std::runtime_error&)
{
r = true;
}
CHECK_AND_ASSERT_MES(r, false, "exception was not cought as expected");
// try to make a PoS block with locked stake before the hardfork
block blk_2b;
{
const block& prev_block = blk_1r;
const transaction& stake = tx_0;
crypto::hash prev_id = get_block_hash(prev_block);
size_t height = get_block_height(prev_block) + 1;
currency::wide_difficulty_type diff = generator.get_difficulty_for_next_block(prev_id, false);
const transaction& stake = blk_0.miner_tx;
crypto::public_key stake_tx_pub_key = get_tx_pub_key_from_extra(stake);
size_t stake_output_idx = 0;
size_t stake_output_gidx = 0;
uint64_t stake_output_amount = stake.vout[stake_output_idx].amount;
crypto::key_image stake_output_key_image;
keypair kp;
generate_key_image_helper(miner_acc.get_keys(), stake_tx_pub_key, stake_output_idx, kp, stake_output_key_image);
generate_key_image_helper(alice_acc.get_keys(), stake_tx_pub_key, stake_output_idx, kp, stake_output_key_image);
crypto::public_key stake_output_pubkey = boost::get<txout_to_key>(stake.vout[stake_output_idx].target).key;
pos_block_builder pb;
pb.step1_init_header(height, prev_id);
pb.step2_set_txs(std::vector<transaction>());
pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, blk_0r.timestamp);
pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(blk_0r), miner_acc.get_public_address());
pb.step5_sign(stake_tx_pub_key, stake_output_idx, stake_output_pubkey, miner_acc);
blk_0a = pb.m_block;
pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, prev_block.timestamp);
pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(prev_block), miner_acc.get_public_address());
pb.step5_sign(stake_tx_pub_key, stake_output_idx, stake_output_pubkey, alice_acc);
blk_2b = pb.m_block;
}
// it should not be accepted, because stake coins is still locked
DO_CALLBACK(events, "mark_invalid_block");
events.push_back(blk_2b);
MAKE_NEXT_BLOCK(events, blk_2, blk_1r, miner_acc);
MAKE_NEXT_BLOCK(events, blk_3, blk_2, miner_acc);
// make sure hardfork went okay
CHECK_AND_ASSERT_MES(blk_2.major_version != CURRENT_BLOCK_MAJOR_VERSION && blk_3.major_version == CURRENT_BLOCK_MAJOR_VERSION, false, "hardfork did not happen as expected");
// try to make a PoS block with locked stake after the hardfork
block blk_4b;
{
const block& prev_block = blk_3;
const transaction& stake = tx_0;
crypto::hash prev_id = get_block_hash(prev_block);
size_t height = get_block_height(prev_block) + 1;
currency::wide_difficulty_type diff = generator.get_difficulty_for_next_block(prev_id, false);
crypto::public_key stake_tx_pub_key = get_tx_pub_key_from_extra(stake);
size_t stake_output_idx = 0;
size_t stake_output_gidx = 0;
uint64_t stake_output_amount = stake.vout[stake_output_idx].amount;
crypto::key_image stake_output_key_image;
keypair kp;
generate_key_image_helper(alice_acc.get_keys(), stake_tx_pub_key, stake_output_idx, kp, stake_output_key_image);
crypto::public_key stake_output_pubkey = boost::get<txout_to_key>(stake.vout[stake_output_idx].target).key;
pos_block_builder pb;
pb.step1_init_header(height, prev_id);
pb.m_block.major_version = CURRENT_BLOCK_MAJOR_VERSION;
pb.step2_set_txs(std::vector<transaction>());
pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, prev_block.timestamp);
pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(prev_block), miner_acc.get_public_address());
pb.step5_sign(stake_tx_pub_key, stake_output_idx, stake_output_pubkey, alice_acc);
blk_4b = pb.m_block;
}
// it should not be accepted, because stake coins is still locked
DO_CALLBACK(events, "mark_invalid_block");
events.push_back(blk_4b);
// blk_4 with tx_1 (etc_tx_details_unlock_time2) should be accepted after hardfork 1
MAKE_NEXT_BLOCK_TX1(events, blk_4, blk_3, miner_acc, tx_1);
block prev = blk_4;
for(size_t i = 0; i < CURRENCY_MINED_MONEY_UNLOCK_WINDOW; ++i)
{
MAKE_NEXT_POS_BLOCK(events, b, prev, miner_acc, std::list<currency::account_base>{miner_acc});
prev = b;
}
//REWIND_BLOCKS_N_WITH_TIME(events, blk_4r, blk_4, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
block blk_5;
{
const block& prev_block = prev;
const transaction& stake = tx_1;
crypto::hash prev_id = get_block_hash(prev_block);
size_t height = get_block_height(prev_block) + 1;
currency::wide_difficulty_type diff = generator.get_difficulty_for_next_block(prev_id, false);
crypto::public_key stake_tx_pub_key = get_tx_pub_key_from_extra(stake);
size_t stake_output_idx = 0;
size_t stake_output_gidx = 0;
uint64_t stake_output_amount = stake.vout[stake_output_idx].amount;
crypto::key_image stake_output_key_image;
keypair kp;
generate_key_image_helper(bob_acc.get_keys(), stake_tx_pub_key, stake_output_idx, kp, stake_output_key_image);
crypto::public_key stake_output_pubkey = boost::get<txout_to_key>(stake.vout[stake_output_idx].target).key;
pos_block_builder pb;
pb.step1_init_header(height, prev_id);
pb.m_block.major_version = CURRENT_BLOCK_MAJOR_VERSION;
pb.step2_set_txs(std::vector<transaction>());
pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, prev_block.timestamp);
pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(prev_block), miner_acc.get_public_address());
pb.step5_sign(stake_tx_pub_key, stake_output_idx, stake_output_pubkey, bob_acc);
blk_5 = pb.m_block;
}
// it should not be accepted, because stake coins is still locked
DO_CALLBACK(events, "mark_invalid_block");
events.push_back(blk_5);
return true;
}
bool hard_fork_1_pos_and_locked_coins::check_outputs_with_unique_amount(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
size_t expected_outputs_count = 0;
unique_amount_params p(0, 0);
const std::string& params = boost::get<callback_entry>(events[ev_index]).callback_params;
CHECK_AND_ASSERT_MES(epee::string_tools::hex_to_pod(params, expected_outputs_count), false, "hex_to_pod failed, params = " << params);
CHECK_AND_ASSERT_MES(epee::string_tools::hex_to_pod(params, p), false, "hex_to_pod failed, params = " << params);
std::list<crypto::public_key> pub_keys;
bool r = c.get_outs(m_unique_amount, pub_keys);
bool r = c.get_outs(p.amount, pub_keys);
CHECK_AND_ASSERT_MES(r && pub_keys.size() == expected_outputs_count, false, "amount " << print_money_brief(m_unique_amount) << ": " << pub_keys.size() << " != " << expected_outputs_count);
CHECK_AND_ASSERT_MES(r && pub_keys.size() == p.count, false, "amount " << print_money_brief(p.amount) << ": " << pub_keys.size() << " != " << p.count);
return true;
}
//------------------------------------------------------------------------------
hard_fork_1_pos_locked_height_vs_time::hard_fork_1_pos_locked_height_vs_time()
: hard_fork_1_base_test(11)
{
}
bool hard_fork_1_pos_locked_height_vs_time::generate(std::vector<test_event_entry>& events) const
{
// Test idea: make sure it's impossible to use height-locked coins as PoS stake IF they are ts-locked (not height-locked) in some outputs
// (because it could possibly be used to unlock coins eralier)
bool r = false;
GENERATE_ACCOUNT(miner_acc);
GENERATE_ACCOUNT(alice_acc);
GENERATE_ACCOUNT(bob_acc);
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time());
generator.set_hardfork_height(m_hardfork_height);
DO_CALLBACK(events, "configure_core");
REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 1);
// create few locked outputs in the blockchain with unique amount
// tx_0 : miner -> Alice, miner -> Bob
uint64_t stake_unlock_time = 100; // locked until block 100
uint64_t stake_amount = MK_TEST_COINS(10);
std::vector<extra_v> extra;
etc_tx_details_unlock_time ut = AUTO_VAL_INIT(ut);
ut.v = stake_unlock_time;
extra.push_back(ut);
std::vector<tx_destination_entry> destinations;
destinations.push_back(tx_destination_entry(stake_amount, alice_acc.get_public_address()));
destinations.push_back(tx_destination_entry(stake_amount, bob_acc.get_public_address()));
transaction tx_0 = AUTO_VAL_INIT(tx_0);
r = construct_tx_to_key(events, tx_0, blk_0r, miner_acc, destinations, TESTS_DEFAULT_FEE, 0, 0, extra);
CHECK_AND_ASSERT_MES(r, false, "construct_tx failed");
events.push_back(tx_0);
MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner_acc, tx_0); // first block after hardfork
// make sure hardfork went okay
CHECK_AND_ASSERT_MES(blk_0r.major_version != CURRENT_BLOCK_MAJOR_VERSION && blk_1.major_version == CURRENT_BLOCK_MAJOR_VERSION, false, "hardfork did not happen as expected");
REWIND_BLOCKS_N_WITH_TIME(events, blk_1r, blk_1, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 1);
block blk_b1;
{
const block& prev_block = blk_1r;
const transaction& stake = tx_0;
const account_base& stakeholder = alice_acc;
crypto::hash prev_id = get_block_hash(prev_block);
size_t height = get_block_height(prev_block) + 1;
currency::wide_difficulty_type diff = generator.get_difficulty_for_next_block(prev_id, false);
crypto::public_key stake_tx_pub_key = get_tx_pub_key_from_extra(stake);
size_t stake_output_idx = 0;
size_t stake_output_gidx = 0;
uint64_t stake_output_amount = stake.vout[stake_output_idx].amount;
crypto::key_image stake_output_key_image;
keypair kp;
generate_key_image_helper(stakeholder.get_keys(), stake_tx_pub_key, stake_output_idx, kp, stake_output_key_image);
crypto::public_key stake_output_pubkey = boost::get<txout_to_key>(stake.vout[stake_output_idx].target).key;
pos_block_builder pb;
pb.step1_init_header(height, prev_id);
pb.m_block.major_version = CURRENT_BLOCK_MAJOR_VERSION;
pb.step2_set_txs(std::vector<transaction>());
pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, prev_block.timestamp);
pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(prev_block), miner_acc.get_public_address(), stakeholder.get_public_address());
// set etc_tx_details_unlock_time2
remove_unlock_v1_entries_from_extra(pb.m_block.miner_tx.extra); // clear already set unlock
std::vector<extra_v> extra;
etc_tx_details_unlock_time2 ut2 = AUTO_VAL_INIT(ut2);
ut2.unlock_time_array.push_back(height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW); // reward lock
for(size_t i = 0; i < pb.m_block.miner_tx.vout.size() - 1; ++i)
ut2.unlock_time_array.push_back(test_core_time::get_time() - 1000); // stake locked by time and it's already passed
extra.push_back(ut2);
pb.m_block.miner_tx.extra.push_back(ut2);
pb.step5_sign(stake_tx_pub_key, stake_output_idx, stake_output_pubkey, stakeholder);
blk_b1 = pb.m_block;
}
// should not pass as using time-locking in outputs
DO_CALLBACK(events, "mark_invalid_block");
events.push_back(blk_b1);
block blk_b2;
{
const block& prev_block = blk_1r;
const transaction& stake = tx_0;
const account_base& stakeholder = alice_acc;
crypto::hash prev_id = get_block_hash(prev_block);
size_t height = get_block_height(prev_block) + 1;
currency::wide_difficulty_type diff = generator.get_difficulty_for_next_block(prev_id, false);
crypto::public_key stake_tx_pub_key = get_tx_pub_key_from_extra(stake);
size_t stake_output_idx = 0;
size_t stake_output_gidx = 0;
uint64_t stake_output_amount = stake.vout[stake_output_idx].amount;
crypto::key_image stake_output_key_image;
keypair kp;
generate_key_image_helper(stakeholder.get_keys(), stake_tx_pub_key, stake_output_idx, kp, stake_output_key_image);
crypto::public_key stake_output_pubkey = boost::get<txout_to_key>(stake.vout[stake_output_idx].target).key;
pos_block_builder pb;
pb.step1_init_header(height, prev_id);
pb.m_block.major_version = CURRENT_BLOCK_MAJOR_VERSION;
pb.step2_set_txs(std::vector<transaction>());
pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, prev_block.timestamp);
pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(prev_block), miner_acc.get_public_address(), stakeholder.get_public_address());
// set etc_tx_details_unlock_time2
remove_unlock_v1_entries_from_extra(pb.m_block.miner_tx.extra); // clear already set unlock
std::vector<extra_v> extra;
etc_tx_details_unlock_time2 ut2 = AUTO_VAL_INIT(ut2);
ut2.unlock_time_array.push_back(height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW); // reward lock
for(size_t i = 0; i < pb.m_block.miner_tx.vout.size() - 1; ++i)
ut2.unlock_time_array.push_back(stake_unlock_time - 1); // stake locked by 1 less height that stake_unlock_time, that is incorrect as lock time of this coin is decreased
extra.push_back(ut2);
pb.m_block.miner_tx.extra.push_back(ut2);
pb.step5_sign(stake_tx_pub_key, stake_output_idx, stake_output_pubkey, stakeholder);
blk_b2 = pb.m_block;
}
// should no pass because stake output has less lock time than stake input
DO_CALLBACK(events, "mark_invalid_block");
events.push_back(blk_b2);
block blk_good;
{
const block& prev_block = blk_1r;
const transaction& stake = tx_0;
const account_base& stakeholder = alice_acc;
crypto::hash prev_id = get_block_hash(prev_block);
size_t height = get_block_height(prev_block) + 1;
currency::wide_difficulty_type diff = generator.get_difficulty_for_next_block(prev_id, false);
crypto::public_key stake_tx_pub_key = get_tx_pub_key_from_extra(stake);
size_t stake_output_idx = 0;
size_t stake_output_gidx = 0;
uint64_t stake_output_amount = stake.vout[stake_output_idx].amount;
crypto::key_image stake_output_key_image;
keypair kp;
generate_key_image_helper(stakeholder.get_keys(), stake_tx_pub_key, stake_output_idx, kp, stake_output_key_image);
crypto::public_key stake_output_pubkey = boost::get<txout_to_key>(stake.vout[stake_output_idx].target).key;
pos_block_builder pb;
pb.step1_init_header(height, prev_id);
pb.m_block.major_version = CURRENT_BLOCK_MAJOR_VERSION;
pb.step2_set_txs(std::vector<transaction>());
pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, prev_block.timestamp);
pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(prev_block), miner_acc.get_public_address(), stakeholder.get_public_address());
// set etc_tx_details_unlock_time2
remove_unlock_v1_entries_from_extra(pb.m_block.miner_tx.extra); // clear already set unlock
std::vector<extra_v> extra;
etc_tx_details_unlock_time2 ut2 = AUTO_VAL_INIT(ut2);
ut2.unlock_time_array.push_back(height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW); // reward lock
for(size_t i = 0; i < pb.m_block.miner_tx.vout.size() - 1; ++i)
ut2.unlock_time_array.push_back(stake_unlock_time); // using the same lock time as stake input
extra.push_back(ut2);
pb.m_block.miner_tx.extra.push_back(ut2);
pb.step5_sign(stake_tx_pub_key, stake_output_idx, stake_output_pubkey, stakeholder);
blk_good = pb.m_block;
}
// should pass okay
events.push_back(blk_good);
generator.add_block_info(blk_good, std::list<transaction>()); // add modified block info
MAKE_NEXT_BLOCK(events, blk_2, blk_good, miner_acc);
std::vector<tx_source_entry> sources;
r = fill_tx_sources(sources, events, blk_2, alice_acc.get_keys(), stake_amount / 2 + TESTS_DEFAULT_FEE, 0 /* nmix */, true /* check for spends */, false /* check for unlock time */);
CHECK_AND_ASSERT_MES(r, false, "fill_tx_sources failed");
transaction tx_1 = AUTO_VAL_INIT(tx_1);
r = construct_tx(alice_acc.get_keys(), sources, std::vector<tx_destination_entry>{ tx_destination_entry(stake_amount / 2, miner_acc.get_public_address()) },
empty_attachment, tx_1, stake_unlock_time /* try to use stake unlock time -- should not work as it is not a coinbase */);
CHECK_AND_ASSERT_MES(r, false, "construct_tx failed");
DO_CALLBACK(events, "mark_invalid_tx");
events.push_back(tx_1);
return true;
}

View file

@ -45,6 +45,10 @@ struct hard_fork_1_pos_and_locked_coins : public hard_fork_1_base_test
bool generate(std::vector<test_event_entry>& events) const;
bool check_outputs_with_unique_amount(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
uint64_t m_unique_amount;
};
struct hard_fork_1_pos_locked_height_vs_time : public hard_fork_1_base_test
{
hard_fork_1_pos_locked_height_vs_time();
bool generate(std::vector<test_event_entry>& events) const;
};

View file

@ -21,7 +21,7 @@ hard_fork_1_locked_mining_test::hard_fork_1_locked_mining_test()
bool hard_fork_1_locked_mining_test::generate(std::vector<test_event_entry>& events) const
{
random_state_test_restorer::reset_random();
// Test idea: make sure PoS mining on locked coins is possible
GENERATE_ACCOUNT(preminer_acc);
GENERATE_ACCOUNT(miner_acc);
@ -36,20 +36,30 @@ bool hard_fork_1_locked_mining_test::generate(std::vector<test_event_entry>& eve
DO_CALLBACK(events, "configure_core");
REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 3);
//construc tx that locks transaction for some period of time
// make a couple of huge txs
//construct tx that locks coins for some period of time
//make a couple of huge txs
bool r = false;
std::vector<extra_v> extra;
std::vector<tx_source_entry> sources_1;
r = fill_tx_sources(sources_1, events, blk_0r, miner_acc.get_keys(), 2000000000000+TESTS_DEFAULT_FEE, 0);
r = fill_tx_sources(sources_1, events, blk_0r, miner_acc.get_keys(), CURRENCY_BLOCK_REWARD + TESTS_DEFAULT_FEE, 0);
CHECK_AND_ASSERT_MES(r, false, "fill_tx_sources failed");
std::vector<tx_destination_entry> destinations({ tx_destination_entry(2010000000000, pos_miner_acc.get_public_address()) });
std::vector<tx_destination_entry> destinations({ tx_destination_entry(CURRENCY_BLOCK_REWARD, pos_miner_acc.get_public_address()) });
crypto::secret_key stub;
transaction tx_1 = AUTO_VAL_INIT(tx_1);
r = construct_tx(miner_acc.get_keys(), sources_1, destinations, extra, empty_attachment, tx_1, stub, get_block_height(blk_0r)+2000);
uint64_t unlock_time = get_block_height(blk_0r) + 2000;
r = construct_tx(miner_acc.get_keys(), sources_1, destinations, extra, empty_attachment, tx_1, stub, unlock_time);
CHECK_AND_ASSERT_MES(r, false, "construct_tx failed");
events.push_back(tx_1); // push it to the pool
uint64_t ut1 = get_tx_x_detail<etc_tx_details_unlock_time>(tx_1);
etc_tx_details_unlock_time2 ut2 = AUTO_VAL_INIT(ut2);
get_type_in_variant_container(tx_1.extra, ut2);
std::stringstream ss;
for (auto v : ut2.unlock_time_array)
ss << v << " ";
LOG_PRINT_YELLOW("tx1: ut1: " << ut1 << ", ut2: " << ss.str(), LOG_LEVEL_0);
MAKE_NEXT_BLOCK_TX1(events, blk_0r_tx, blk_0r, miner_acc, tx_1);
@ -61,13 +71,30 @@ bool hard_fork_1_locked_mining_test::generate(std::vector<test_event_entry>& eve
events.push_back(event_core_time(next_blk_pow.timestamp - 10));
last_block = next_blk_pow;
}
MAKE_NEXT_BLOCK(events, blk_1, last_block, miner_acc);
MAKE_NEXT_POS_BLOCK(events, blk_2, blk_1, miner_acc, miner_acc_lst);
MAKE_NEXT_BLOCK(events, blk_3, blk_2, miner_acc);
std::list<currency::account_base> accounts_2;
accounts_2.push_back(pos_miner_acc);
//let's try to mint PoS block from locked account
//mint PoS block from locked account into an altchain
MAKE_NEXT_POS_BLOCK(events, next_blk_pos, last_block, pos_miner_acc, accounts_2);
DO_CALLBACK(events, "c1");
// switch chains
MAKE_NEXT_BLOCK(events, blk_2a, next_blk_pos, miner_acc);
MAKE_NEXT_BLOCK(events, blk_3a, blk_2a, miner_acc);
MAKE_NEXT_BLOCK(events, blk_4a, blk_3a, miner_acc);
// make sure switch happened
DO_CALLBACK_PARAMS(events, "check_top_block", params_top_block(blk_4a));
REWIND_BLOCKS_N_WITH_TIME(events, blk_4ar, blk_4a, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
// mint another PoS block from locked account
MAKE_NEXT_POS_BLOCK(events, blk_5a, blk_4ar, pos_miner_acc, accounts_2);
return true;
}

View file

@ -78,7 +78,7 @@ void pos_block_builder::step3_build_stake_kernel(
if (last_pos_block_kernel_hash == null_hash)
{
bool r = string_tools::parse_tpod_from_hex_string(POS_STARTER_KERNEL_HASH, m_stake_kernel.stake_modifier.last_pos_kernel_id);
CHECK_AND_ASSERT_THROW_MES(r, "Failed to parse POS_STARTER_MODFIFIER");
CHECK_AND_ASSERT_THROW_MES(r, "Failed to parse POS_STARTER_KERNEL_HASH");
}
wide_difficulty_type stake_difficulty = difficulty / stake_output_amount;
@ -105,9 +105,21 @@ void pos_block_builder::step3_build_stake_kernel(
m_step = 3;
}
void pos_block_builder::step4_generate_coinbase_tx(size_t median_size,
const boost::multiprecision::uint128_t& already_generated_coins,
const account_public_address &reward_and_stake_receiver_address,
const blobdata& extra_nonce,
size_t max_outs,
const extra_alias_entry& alias,
keypair tx_one_time_key)
{
step4_generate_coinbase_tx(median_size, already_generated_coins, reward_and_stake_receiver_address, reward_and_stake_receiver_address, extra_nonce, max_outs, alias, tx_one_time_key);
}
void pos_block_builder::step4_generate_coinbase_tx(size_t median_size,
const boost::multiprecision::uint128_t& already_generated_coins,
const account_public_address &reward_receiver_address,
const account_public_address &stakeholder_address,
const blobdata& extra_nonce,
size_t max_outs,
const extra_alias_entry& alias,
@ -118,7 +130,7 @@ void pos_block_builder::step4_generate_coinbase_tx(size_t median_size,
// generate miner tx using incorrect current_block_size only for size estimation
size_t estimated_block_size = m_txs_total_size;
bool r = construct_homemade_pos_miner_tx(m_height, median_size, already_generated_coins, estimated_block_size, m_total_fee, m_pos_stake_amount, m_stake_kernel.kimage,
m_pos_stake_output_gindex, reward_receiver_address, m_block.miner_tx, extra_nonce, max_outs, alias, tx_one_time_key);
m_pos_stake_output_gindex, reward_receiver_address, stakeholder_address, m_block.miner_tx, extra_nonce, max_outs, tx_one_time_key);
CHECK_AND_ASSERT_THROW_MES(r, "construct_homemade_pos_miner_tx failed");
estimated_block_size = m_txs_total_size + get_object_blobsize(m_block.miner_tx);
@ -126,7 +138,7 @@ void pos_block_builder::step4_generate_coinbase_tx(size_t median_size,
for (size_t try_count = 0; try_count != 10; ++try_count)
{
r = construct_homemade_pos_miner_tx(m_height, median_size, already_generated_coins, estimated_block_size, m_total_fee, m_pos_stake_amount, m_stake_kernel.kimage,
m_pos_stake_output_gindex, reward_receiver_address, m_block.miner_tx, extra_nonce, max_outs, alias, tx_one_time_key);
m_pos_stake_output_gindex, reward_receiver_address, stakeholder_address, m_block.miner_tx, extra_nonce, max_outs, tx_one_time_key);
CHECK_AND_ASSERT_THROW_MES(r, "construct_homemade_pos_miner_tx failed");
cumulative_size = m_txs_total_size + get_object_blobsize(m_block.miner_tx);
@ -171,11 +183,11 @@ bool construct_homemade_pos_miner_tx(size_t height, size_t median_size, const bo
uint64_t pos_stake_amount,
crypto::key_image pos_stake_keyimage,
size_t pos_stake_gindex,
const account_public_address &miner_address,
const account_public_address &reward_receiving_address,
const account_public_address &stakeholder_address,
transaction& tx,
const blobdata& extra_nonce /*= blobdata()*/,
size_t max_outs /*= CURRENCY_MINER_TX_MAX_OUTS*/,
const extra_alias_entry& alias /*= alias_info()*/,
keypair tx_one_time_key /*= keypair::generate()*/)
{
boost::value_initialized<transaction> new_tx;
@ -189,12 +201,6 @@ bool construct_homemade_pos_miner_tx(size_t height, size_t median_size, const bo
bool r = get_block_reward(true, median_size, current_block_size, already_generated_coins, block_reward, height);
CHECK_AND_ASSERT_MES(r, false, "Block is too big");
block_reward += fee;
block_reward += pos_stake_amount;
uint64_t alias_reward = 0;
if (!alias.m_alias.empty())
alias_reward = currency::get_alias_coast_from_fee(alias.m_alias, TESTS_DEFAULT_FEE);
block_reward -= alias_reward;
// decompose reward into outputs and populate tx.vout
std::vector<size_t> out_amounts;
@ -202,14 +208,15 @@ bool construct_homemade_pos_miner_tx(size_t height, size_t median_size, const bo
[&out_amounts](uint64_t a_chunk) { out_amounts.push_back(a_chunk); },
[&out_amounts](uint64_t a_dust) { out_amounts.push_back(a_dust); });
CHECK_AND_ASSERT_MES(1 <= max_outs, false, "max_out must be non-zero");
while (max_outs < out_amounts.size())
CHECK_AND_ASSERT_MES(2 <= max_outs, false, "max_out must be greather than 1");
while (out_amounts.size() + 1 > max_outs)
{
out_amounts[out_amounts.size() - 2] += out_amounts.back();
out_amounts.resize(out_amounts.size() - 1);
}
bool burn_money = miner_address.m_spend_public_key == null_pkey && miner_address.m_view_public_key == null_pkey; // if true, burn money, so no one on Earth can spend them
// reward
bool burn_money = reward_receiving_address.m_spend_public_key == null_pkey && reward_receiving_address.m_view_public_key == null_pkey; // if true, burn reward, so no one on Earth can spend them
for (size_t output_index = 0; output_index < out_amounts.size(); ++output_index)
{
txout_to_key tk;
@ -218,7 +225,7 @@ bool construct_homemade_pos_miner_tx(size_t height, size_t median_size, const bo
if (!burn_money)
{
r = currency::derive_public_key_from_target_address(miner_address, tx_one_time_key.sec, output_index, tk.key); // derivation(view_pub; tx_sec).derive(output_index, spend_pub) => output pub key
r = currency::derive_public_key_from_target_address(reward_receiving_address, tx_one_time_key.sec, output_index, tk.key); // derivation(view_pub; tx_sec).derive(output_index, spend_pub) => output pub key
CHECK_AND_ASSERT_MES(r, false, "failed to derive_public_key_from_target_address");
}
@ -228,36 +235,31 @@ bool construct_homemade_pos_miner_tx(size_t height, size_t median_size, const bo
tx.vout.push_back(out);
}
// stake
burn_money = stakeholder_address.m_spend_public_key == null_pkey && stakeholder_address.m_view_public_key == null_pkey; // if true, burn stake
{
txout_to_key tk;
tk.key = null_pkey; // null means burn money
tk.mix_attr = 0;
if (!burn_money)
{
r = currency::derive_public_key_from_target_address(stakeholder_address, tx_one_time_key.sec, tx.vout.size(), tk.key);
CHECK_AND_ASSERT_MES(r, false, "failed to derive_public_key_from_target_address");
}
tx_out out;
out.amount = pos_stake_amount;
out.target = tk;
tx.vout.push_back(out);
}
// take care about extra
add_tx_pub_key_to_extra(tx, tx_one_time_key.pub);
if (extra_nonce.size())
if (!add_tx_extra_userdata(tx, extra_nonce))
return false;
if (alias.m_alias.size())
{
if (!add_tx_extra_alias(tx, alias))
return false;
// decompose alias reward into digits and create additional outputs
out_amounts.clear();
decompose_amount_into_digits(alias_reward, DEFAULT_DUST_THRESHOLD, [&out_amounts](uint64_t a_chunk) { out_amounts.push_back(a_chunk); }, [&out_amounts](uint64_t a_dust) { out_amounts.push_back(a_dust); });
while (out_amounts.size() > max_outs)
{
out_amounts[out_amounts.size() - 2] += out_amounts.back();
out_amounts.resize(out_amounts.size() - 1);
}
for (size_t output_index = 0; output_index < out_amounts.size(); ++output_index)
{
txout_to_key tk;
tk.key = null_pkey; // burn money for the sake of alias!
tk.mix_attr = 0;
tx_out out;
out.amount = out_amounts[output_index];
out.target = tk;
tx.vout.push_back(out);
}
}
// populate ins with 1) money-generating and 2) PoS
txin_gen in;
in.height = height;

View file

@ -27,11 +27,20 @@ struct pos_block_builder
void step4_generate_coinbase_tx(size_t median_size,
const boost::multiprecision::uint128_t& already_generated_coins,
const currency::account_public_address &reward_receiver_address,
const currency::account_public_address &stakeholder_address,
const currency::blobdata& extra_nonce = currency::blobdata(),
size_t max_outs = CURRENCY_MINER_TX_MAX_OUTS,
const currency::extra_alias_entry& alias = currency::extra_alias_entry(),
currency::keypair tx_one_time_key = currency::keypair::generate());
void step4_generate_coinbase_tx(size_t median_size,
const boost::multiprecision::uint128_t& already_generated_coins,
const currency::account_public_address &reward_and_stake_receiver_address,
const currency::blobdata& extra_nonce = currency::blobdata(),
size_t max_outs = CURRENCY_MINER_TX_MAX_OUTS,
const currency::extra_alias_entry& alias = currency::extra_alias_entry(),
currency::keypair tx_one_time_key = currency::keypair::generate());
void step5_sign(const crypto::public_key& stake_tx_pub_key, size_t stake_tx_out_index, const crypto::public_key& stake_tx_out_pub_key, const currency::account_base& stakeholder_account);
currency::block m_block;
@ -50,11 +59,11 @@ bool construct_homemade_pos_miner_tx(size_t height, size_t median_size, const bo
uint64_t pos_stake_amount,
crypto::key_image pos_stake_keyimage,
size_t pos_stake_gindex,
const currency::account_public_address &miner_address,
const currency::account_public_address &reward_receiving_address,
const currency::account_public_address &stakeholder_address,
currency::transaction& tx,
const currency::blobdata& extra_nonce = currency::blobdata(),
size_t max_outs = CURRENCY_MINER_TX_MAX_OUTS,
const currency::extra_alias_entry& alias = currency::extra_alias_entry(),
currency::keypair tx_one_time_key = currency::keypair::generate());
bool mine_next_pos_block_in_playtime_sign_cb(currency::core& c, const currency::block& prev_block, const currency::block& coinstake_scr_block, const currency::account_base& acc,

View file

@ -6,6 +6,21 @@
#include "chaingen.h"
#include "random_helper.h"
random_state_test_restorer::random_state_test_restorer()
{
crypto::random_prng_get_state(&m_state, sizeof m_state);
}
random_state_test_restorer::~random_state_test_restorer()
{
crypto::random_prng_set_state(&m_state, sizeof m_state);
}
void random_state_test_restorer::reset_random(uint64_t seed /* = 0 */)
{
crypto::random_prng_initialize_with_seed(seed);
}
std::string get_random_text(size_t len)
{
static const char text_chars[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789" "~!@#$%^&*()-=_+\\/?,.<>|{}[]`';:\" ";
@ -45,7 +60,7 @@ bool random_state_manupulation_test()
// NOTE: If the test fails, it's most likely that random state permutation procedure was changed OR the state can't be correctly stored/loaded.
static const uint64_t my_own_random_seed = 4669201609102990671;
static const char* my_random_str = "18b79ebb56744e9bafa462631c6f7d760af2b788";
static const char* my_random_str = "760af2b78894c6a441731e2b354011da6ac98ddc";
static const size_t rnd_buf_len = 20;
uint64_t first_random_after_state_saved = 0;

View file

@ -1,40 +1,28 @@
// Copyright (c) 2014-2018 Zano Project
// Copyright (c) 2014-2019 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#pragma once
#define RANDOM_STATE_SIZE 200
extern "C" volatile union hash_state state; // field defined in random.c
static_assert(RANDOM_STATE_SIZE >= HASH_DATA_AREA, "Invalid RANDOM_STATE_SIZE");
// DISCLAIMER: designed for tests puposes ONLY!
// This class is not intended to be used neither in multi-threaded environment nor in production.
#ifdef USE_INSECURE_RANDOM_RPNG_ROUTINES
// Remebers random state at ctor, restores it at dtor
struct random_state_test_restorer
{
random_state_test_restorer()
{
memcpy(m_state, const_cast<hash_state*>(&::state), RANDOM_STATE_SIZE);
}
~random_state_test_restorer()
{
memcpy(const_cast<hash_state*>(&::state), m_state, RANDOM_STATE_SIZE);
}
static void reset_random(uint64_t seed = 0)
{
memset(const_cast<hash_state*>(&::state), 0, RANDOM_STATE_SIZE);
memcpy(const_cast<hash_state*>(&::state), &seed, sizeof seed);
}
random_state_test_restorer();
~random_state_test_restorer();
static void reset_random(uint64_t seed = 0);
private:
uint8_t m_state[RANDOM_STATE_SIZE];
};
#endif // #ifdef USE_INSECURE_RANDOM_RPNG_ROUTINES
std::string get_random_text(size_t len);
bool random_state_manupulation_test();

View file

@ -121,7 +121,7 @@ bool generate_events(currency::core& c, cct_events_t& events, const cct_wallets_
{
const transaction& tx = boost::get<const currency::transaction>(*it);
uint64_t max_used_block_height = 0;
r = bcs.check_tx_inputs(tx, get_transaction_hash(tx), &max_used_block_height);
r = bcs.check_tx_inputs(tx, get_transaction_hash(tx), max_used_block_height);
if (r && max_used_block_height <= prev_block.height)
txs.push_back(&tx); // filter out tx that are using too recent outputs -- yep, some txs will be dropped forever
}

View file

@ -9,15 +9,35 @@
#include "single_tx_test_base.h"
uint64_t g_antioptimisation = 0;
class test_generate_key_derivation : public single_tx_test_base
{
public:
static const size_t loop_count = 1000;
static const size_t loop_count = 1;
std::list<currency::account_base> accounts;
bool init()
{
for (size_t i = 0; i != 10000; i++)
{
accounts.push_back(currency::account_base());
accounts.back().generate();
}
return single_tx_test_base::init();
}
bool test()
{
crypto::key_derivation recv_derivation;
crypto::generate_key_derivation(m_tx_pub_key, m_bob.get_keys().m_view_secret_key, recv_derivation);
for (auto &a : accounts)
{
crypto::key_derivation recv_derivation = AUTO_VAL_INIT(recv_derivation);
crypto::generate_key_derivation(m_tx_pub_key, a.get_keys().m_view_secret_key, recv_derivation);
g_antioptimisation ^= *(uint64_t*)(&recv_derivation);
}
return true;
}
};

View file

@ -68,7 +68,7 @@ int main(int argc, char** argv)
*/
//TEST_PERFORMANCE0(test_is_out_to_acc);
//TEST_PERFORMANCE0(test_generate_key_image_helper);
//TEST_PERFORMANCE0(test_generate_key_derivation);
TEST_PERFORMANCE0(test_generate_key_derivation);
//TEST_PERFORMANCE0(test_generate_key_image);
//TEST_PERFORMANCE0(test_derive_public_key);
//TEST_PERFORMANCE0(test_derive_secret_key);

View file

@ -1,5 +1,10 @@
// Copyright (c) 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 <algorithm>
#define USE_INSECURE_RANDOM_RPNG_ROUTINES // turns on random manupulation for tests
#include "gtest/gtest.h"
#include "currency_core/currency_format_utils.h"
#include "common/db_abstract_accessor.h"
@ -8,7 +13,6 @@
#include "common/util.h"
#include "misc_log_ex.h"
#include "crypto/crypto.h"
#include "../core_tests/random_helper.h"
#include "serialization/serialization.h"
#include "file_io_utils.h"
@ -311,7 +315,7 @@ struct bcs_stub_t
TEST(db_accessor_tests, median_db_cache_test)
{
random_state_test_restorer::reset_random(); // make this test deterministic (the same crypto::rand() sequence)
crypto::random_prng_initialize_with_seed(0); // make this test deterministic (the same crypto::rand() sequence)
epee::shared_recursive_mutex m_rw_lock;
tools::db::basic_db_accessor m_db(std::shared_ptr<tools::db::i_db_backend>(new tools::db::lmdb_db_backend), m_rw_lock);

View file

@ -0,0 +1,76 @@
// Copyright (c) 2012-2013 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "gtest/gtest.h"
#include "include_base_utils.h"
#include "currency_core/currency_format_utils.h"
bool if_alt_chain_stronger(const currency::wide_difficulty_type& pos, const currency::wide_difficulty_type& pow)
{
currency::difficulties main_cumul_diff;
main_cumul_diff.pos_diff = 400000;
main_cumul_diff.pow_diff = 4000;
currency::difficulties alt_cumul_diff;
alt_cumul_diff.pow_diff = pow;
alt_cumul_diff.pos_diff = pos;
static currency::wide_difficulty_type difficulty_pos_at_split_point = 400000;
static currency::wide_difficulty_type difficulty_pow_at_split_point = 4000;
currency::wide_difficulty_type main = currency::get_a_to_b_relative_cumulative_difficulty(difficulty_pos_at_split_point, difficulty_pow_at_split_point, main_cumul_diff, alt_cumul_diff);
currency::wide_difficulty_type alt = currency::get_a_to_b_relative_cumulative_difficulty(difficulty_pos_at_split_point, difficulty_pow_at_split_point, alt_cumul_diff, main_cumul_diff);
if (alt > main)
return true;
return false;
}
TEST(fork_choice_rule_test, fork_choice_rule_test_1)
{
// std::stringstream ss;
// for (uint64_t pos = 100000; pos < 1000001; pos += 10000)
// {
// for (uint64_t pow = 100; pow < 18000; pow += 100)
// {
// bool r = if_alt_chain_stronger(pos, pow);
// if(r)
// ss << pos << "\t" << pow << std::endl;
// //ss << pos << "\t" << pow << "\t" << (r ? "1" : "0") << std::endl;
//
//
// }
// }
// bool r = epee::file_io_utils::save_string_to_file("stat.txt", ss.str());
bool res = false;
res = if_alt_chain_stronger(1000000, 1000);
ASSERT_FALSE(res);
res = if_alt_chain_stronger(1000000, 1500);
ASSERT_TRUE(res);
res = if_alt_chain_stronger(800000, 1700);
ASSERT_FALSE(res);
res = if_alt_chain_stronger(800000, 2000);
ASSERT_TRUE(res);
res = if_alt_chain_stronger(600000, 2200);
ASSERT_FALSE(res);
res = if_alt_chain_stronger(600000, 2800);
ASSERT_TRUE(res);
res = if_alt_chain_stronger(400000, 3999);
ASSERT_FALSE(res);
res = if_alt_chain_stronger(400000, 4001);
ASSERT_TRUE(res);
res = if_alt_chain_stronger(200000, 7000);
ASSERT_FALSE(res);
res = if_alt_chain_stronger(200000, 7700);
ASSERT_TRUE(res);
res = if_alt_chain_stronger(200000, 7000);
ASSERT_FALSE(res);
res = if_alt_chain_stronger(200000, 7700);
ASSERT_TRUE(res);
res = if_alt_chain_stronger(100000, 10000);
ASSERT_FALSE(res);
res = if_alt_chain_stronger(200000, 14000);
ASSERT_TRUE(res);
}

View file

@ -1,17 +1,895 @@
// Copyright (c) 2019 Zano Project
// Copyright (c) 2018 The Boolberry developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <memory>
#include <thread>
#include <atomic>
#include "epee/include/include_base_utils.h"
#define USE_INSECURE_RANDOM_RPNG_ROUTINES // turns on random manupulation for tests
#include "crypto/crypto.h"
#include "gtest/gtest.h"
#include "common/db_backend_lmdb.h"
#include "common/db_abstract_accessor.h"
#include "common/db_backend_lmdb.h"
#include "serialization/serialization.h"
using namespace tools;
namespace lmdb_test
{
crypto::hash null_hash = AUTO_VAL_INIT(null_hash);
template<typename T>
T random_t_from_range(T from, T to)
{
if (from >= to)
return from;
T result;
crypto::generate_random_bytes(sizeof result, &result);
return from + result % (to - from + 1);
}
//////////////////////////////////////////////////////////////////////////////
// basic_test
//////////////////////////////////////////////////////////////////////////////
TEST(lmdb, basic_test)
{
std::shared_ptr<db::lmdb_db_backend> lmdb_ptr = std::make_shared<db::lmdb_db_backend>();
epee::shared_recursive_mutex db_lock;
db::basic_db_accessor dbb(lmdb_ptr, db_lock);
bool r = false;
r = dbb.open("test_lmdb");
ASSERT_TRUE(r);
db::container_handle tid_decapod;
r = lmdb_ptr->open_container("decapod", tid_decapod);
ASSERT_TRUE(r);
ASSERT_TRUE(lmdb_ptr->begin_transaction());
ASSERT_TRUE(lmdb_ptr->clear(tid_decapod));
uint64_t key = 10;
std::string buf = "nxjdu47flrp20soam19e7nfhxbcy48owks03of92sbf31n1oqkanmdb47";
r = lmdb_ptr->set(tid_decapod, (char*)&key, sizeof key, buf.c_str(), buf.size());
ASSERT_TRUE(r);
ASSERT_TRUE(lmdb_ptr->commit_transaction());
r = dbb.close();
ASSERT_TRUE(r);
r = dbb.open("test_lmdb");
ASSERT_TRUE(r);
r = lmdb_ptr->open_container("decapod", tid_decapod);
ASSERT_TRUE(r);
ASSERT_TRUE(lmdb_ptr->begin_transaction());
std::string out_buffer;
r = lmdb_ptr->get(tid_decapod, (char*)&key, sizeof key, out_buffer);
ASSERT_TRUE(r);
ASSERT_EQ(buf, out_buffer);
ASSERT_TRUE(lmdb_ptr->commit_transaction());
r = dbb.close();
ASSERT_TRUE(r);
}
//////////////////////////////////////////////////////////////////////////////
// multithread_test_1
//////////////////////////////////////////////////////////////////////////////
struct multithread_test_1_t : public db::i_db_callback
{
enum { c_keys_count = 128 };
crypto::hash m_keys[c_keys_count];
size_t m_randomly_mixed_indexes_1[c_keys_count];
size_t m_randomly_mixed_indexes_2[c_keys_count];
epee::shared_recursive_mutex m_db_lock;
std::shared_ptr<db::lmdb_db_backend> m_lmdb_adapter;
db::basic_db_accessor m_dbb;
db::container_handle m_table_id;
size_t m_counter;
multithread_test_1_t()
: m_lmdb_adapter(std::make_shared<db::lmdb_db_backend>())
, m_dbb(m_lmdb_adapter, m_db_lock)
, m_table_id(0)
, m_counter(0)
{
for (size_t i = 0; i < c_keys_count; ++i)
{
m_keys[i] = i == 0 ? null_hash : crypto::cn_fast_hash(&m_keys[i - 1], sizeof(crypto::hash));
m_randomly_mixed_indexes_1[i] = i;
m_randomly_mixed_indexes_2[i] = i;
}
// prepare m_randomly_mixed_indexes_1 and m_randomly_mixed_indexes_2
for (size_t i = 0; i < c_keys_count * 100; ++i)
{
size_t a = random_t_from_range<size_t>(0, c_keys_count - 1);
size_t b = random_t_from_range<size_t>(0, c_keys_count - 1);
size_t c = random_t_from_range<size_t>(0, c_keys_count - 1);
size_t d = random_t_from_range<size_t>(0, c_keys_count - 1);
size_t tmp = m_randomly_mixed_indexes_1[a];
m_randomly_mixed_indexes_1[a] = m_randomly_mixed_indexes_1[b];
m_randomly_mixed_indexes_1[b] = tmp;
tmp = m_randomly_mixed_indexes_2[c];
m_randomly_mixed_indexes_2[c] = m_randomly_mixed_indexes_2[d];
m_randomly_mixed_indexes_2[d] = tmp;
}
}
void adder_thread(std::atomic<bool>& stop_flag)
{
epee::log_space::log_singletone::set_thread_log_prefix("[ adder ] ");
//epee::misc_utils::sleep_no_w(1000);
size_t i = 0;
for(size_t n = 0; n < 1000; ++n)
{
// get pseudorandom key index
size_t key_index = m_randomly_mixed_indexes_1[i];
i = (i + 1) % c_keys_count;
const crypto::hash& key = m_keys[key_index];
bool r = m_lmdb_adapter->begin_transaction();
CHECK_AND_ASSERT_MES_NO_RET(r, "begin_transaction");
// get a value by the given key
std::string value;
if (!m_lmdb_adapter->get(m_table_id, (const char*)&key, sizeof key, value))
{
// if such key does not exist -- add it
char buffer[128];
crypto::generate_random_bytes(sizeof buffer, buffer);
r = m_lmdb_adapter->set(m_table_id, (const char*)&key, sizeof key, buffer, sizeof buffer);
CHECK_AND_ASSERT_MES_NO_RET(r, "set");
size_t table_size = m_lmdb_adapter->size(m_table_id);
r = m_lmdb_adapter->commit_transaction();
CHECK_AND_ASSERT_MES_NO_RET(r, "commit_transaction");
LOG_PRINT_L1("added key index " << key_index << ", table size: " << table_size);
}
else
{
// if key exists in the table - do nothing
m_lmdb_adapter->abort_transaction();
}
epee::misc_utils::sleep_no_w(1);
}
LOG_PRINT_L0("adder_thread stopped");
}
void deleter_thread(std::atomic<bool>& stop_flag)
{
epee::log_space::log_singletone::set_thread_log_prefix("[deleter] ");
epee::misc_utils::sleep_no_w(1000);
// get pseudorandom key index
size_t i = 0;
while(!stop_flag)
{
size_t key_index = m_randomly_mixed_indexes_2[i];
i = (i + 1) % c_keys_count;
const crypto::hash& key = m_keys[key_index];
bool r = m_lmdb_adapter->begin_transaction();
CHECK_AND_ASSERT_MES_NO_RET(r, "begin_transaction");
// get a value by the given key
std::string value;
if (m_lmdb_adapter->get(m_table_id, (const char*)&key, sizeof key, value))
{
// if key exists in the table -- remove it
r = m_lmdb_adapter->erase(m_table_id, (const char*)&key, sizeof key);
CHECK_AND_ASSERT_MES_NO_RET(r, "erase");
size_t table_size = m_lmdb_adapter->size(m_table_id);
r = m_lmdb_adapter->commit_transaction();
CHECK_AND_ASSERT_MES_NO_RET(r, "commit_transaction");
LOG_PRINT_L1("erased key index " << key_index << ", table size: " << table_size);
if (table_size == 2)
break;
}
else
{
// if no such key exists in the table - do nothing
m_lmdb_adapter->abort_transaction();
}
epee::misc_utils::sleep_no_w(1);
}
LOG_PRINT_L0("deleter_thread stopped");
}
void reader_thread()
{
epee::log_space::log_singletone::set_thread_log_prefix("[reader ] ");
epee::misc_utils::sleep_no_w(1000);
// get pseudorandom key index
size_t i = 17;
uint64_t sum = 0; // just for fun
for(;;)
{
size_t key_index = m_randomly_mixed_indexes_2[i];
i = (i + 1) % c_keys_count;
const crypto::hash& key = m_keys[key_index];
bool r = m_lmdb_adapter->begin_transaction(true); // request Read-Only transaction
CHECK_AND_ASSERT_MES_NO_RET(r, "begin_transaction(RO=true)");
// get a value by the given key
std::string value;
if (m_lmdb_adapter->get(m_table_id, (const char*)&key, sizeof key, value))
{
sum += *reinterpret_cast<const uint64_t*>(value.data());
size_t table_size = m_lmdb_adapter->size(m_table_id);
r = m_lmdb_adapter->commit_transaction();
CHECK_AND_ASSERT_MES_NO_RET(r, "commit_transaction");
if (table_size == 2)
break;
}
else
{
// if no such key exists in the table - do nothing
m_lmdb_adapter->abort_transaction();
}
epee::misc_utils::sleep_no_w(1);
}
LOG_PRINT_L0("reader_thread stopped, sum = " << sum);
}
bool check()
{
size_t table_size = m_lmdb_adapter->size(m_table_id);
CHECK_AND_ASSERT_MES(table_size == 2, false, "2 elements are expected to left");
m_counter = 0;
bool r = m_lmdb_adapter->begin_transaction();
CHECK_AND_ASSERT_MES(r, false, "begin_transaction");
m_lmdb_adapter->enumerate(m_table_id, this);
r = m_lmdb_adapter->commit_transaction();
CHECK_AND_ASSERT_MES(r, false, "commit_transaction");
return m_counter == 2;
}
// class i_db_visitor
virtual bool on_enum_item(size_t i, const void* key_data, size_t key_size, const void* value_data, size_t value_size) override
{
CHECK_AND_ASSERT_MES(key_size == sizeof(crypto::hash), false, "invalid key size: " << key_size);
const crypto::hash *p_key = reinterpret_cast<const crypto::hash*>(key_data);
size_t key_index = SIZE_MAX;
for(size_t j = 0; j < c_keys_count && key_index == SIZE_MAX; ++j)
if (m_keys[j] == *p_key)
key_index = j;
CHECK_AND_ASSERT_MES(key_index != SIZE_MAX, false, "visitor gets a non-existing key");
LOG_PRINT_L1("visitor: #" << i << ", key index: " << key_index);
if (i != m_counter)
{
LOG_ERROR("invalid visitor index i: " << i << ", m_counter: " << m_counter);
m_counter = SIZE_MAX;
return false;
}
++m_counter;
return true;
};
bool run()
{
bool r = m_dbb.open("multithread_test_1_t");
CHECK_AND_ASSERT_MES(r, false, "m_dbb.open");
r = m_lmdb_adapter->open_container("table1_", m_table_id);
CHECK_AND_ASSERT_MES(r, false, "open_table");
r = m_lmdb_adapter->begin_transaction();
CHECK_AND_ASSERT_MES(r, false, "begin_transaction");
r = m_lmdb_adapter->clear(m_table_id);
CHECK_AND_ASSERT_MES(r, false, "clear_table");
r = m_lmdb_adapter->commit_transaction();
CHECK_AND_ASSERT_MES(r, false, "commit_transaction");
std::atomic<bool> stop_adder(false), stop_deleter(false);
std::thread adder_t(&multithread_test_1_t::adder_thread, this, std::ref(stop_adder));
std::thread deleter_t(&multithread_test_1_t::deleter_thread, this, std::ref(stop_deleter));
std::vector<std::thread> readers_t;
const size_t reader_threads_count = 2;
for (size_t i = 0; i < reader_threads_count; ++i)
readers_t.emplace_back(std::thread(&multithread_test_1_t::reader_thread, this));
for (auto& t : readers_t)
t.join();
adder_t.join();
//stop_deleter = true;
deleter_t.join();
r = check();
m_dbb.close();
return r;
}
};
TEST(lmdb, multithread_test_1)
{
char prng_state[200] = {};
crypto::random_prng_get_state(prng_state, sizeof prng_state); // store current RPNG state
crypto::random_prng_initialize_with_seed(0); // this mades this test deterministic
bool result = false;
try
{
multithread_test_1_t t;
result = t.run();
}
catch (std::exception& e)
{
LOG_ERROR("Caught exception: " << e.what());
}
// restore PRNG state to keep other tests unaffected
crypto::random_prng_get_state(prng_state, sizeof prng_state);
ASSERT_TRUE(result);
}
//////////////////////////////////////////////////////////////////////////////
// bridge_basic_test
//////////////////////////////////////////////////////////////////////////////
struct simple_serializable_t
{
std::string name;
uint64_t number;
BEGIN_SERIALIZE_OBJECT()
FIELD(name)
FIELD(number)
END_SERIALIZE()
};
inline bool operator==(const simple_serializable_t &lhs, const simple_serializable_t &rhs)
{
return lhs.name == rhs.name &&
lhs.number == rhs.number;
}
struct simple_pod_t
{
char c;
uint64_t u;
float f;
};
inline bool operator==(const simple_pod_t &_v1, const simple_pod_t &_v2)
{
return std::memcmp(&_v1, &_v2, sizeof _v1) == 0;
}
TEST(lmdb, bridge_basic_test)
{
std::shared_ptr<db::lmdb_db_backend> lmdb_ptr = std::make_shared<db::lmdb_db_backend>();
epee::shared_recursive_mutex db_lock;
db::basic_db_accessor dbb(lmdb_ptr, db_lock);
bool r = false;
r = dbb.open("bridge_basic_test");
ASSERT_TRUE(r);
db::container_handle tid_decapod;
r = lmdb_ptr->open_container("decapod", tid_decapod);
ASSERT_TRUE(r);
ASSERT_TRUE(dbb.begin_transaction());
ASSERT_TRUE(dbb.clear(tid_decapod));
const char key[] = "nxjdu47flrp20soam19e7nfhxbcy48owks03of92sbf31n1oqkanmdb47";
simple_serializable_t s_object;
s_object.number = 1001100102;
s_object.name = "bender";
r = dbb.set_t_object(tid_decapod, key, s_object);
ASSERT_TRUE(r);
dbb.commit_transaction();
ASSERT_TRUE(dbb.begin_transaction());
simple_serializable_t s_object2;
r = dbb.get_t_object(tid_decapod, key, s_object2);
ASSERT_TRUE(r);
ASSERT_EQ(s_object, s_object2);
// del object by key and make sure it does not exist anymore
r = dbb.erase(tid_decapod, key);
ASSERT_TRUE(r);
r = dbb.get_t_object(tid_decapod, key, s_object2);
ASSERT_FALSE(r);
// second erase shoud also fail
r = dbb.erase(tid_decapod, key);
ASSERT_FALSE(r);
dbb.commit_transaction();
// POD type
const char key_pod[] = "alqocyfu7sbxhaoo5kdnrt77tgwderhjs9a9sdjf324nfjksd9f0s90f2";
ASSERT_TRUE(dbb.begin_transaction());
simple_pod_t p_object1 = {' ', 0xf7f7f7f7d3d3d3d3ull, 2.002319f};
r = dbb.set_pod_object(tid_decapod, key_pod, p_object1);
ASSERT_TRUE(r);
dbb.commit_transaction();
ASSERT_TRUE(dbb.begin_transaction());
simple_pod_t p_object2;
r = dbb.get_pod_object(tid_decapod, key_pod, p_object2);
ASSERT_TRUE(r);
ASSERT_EQ(p_object1, p_object2);
// del object by key and make sure it does not exist anymore
r = dbb.erase(tid_decapod, key_pod);
ASSERT_TRUE(r);
r = dbb.get_pod_object(tid_decapod, key_pod, p_object2);
ASSERT_FALSE(r);
// second erase shoud also fail
r = dbb.erase(tid_decapod, key_pod);
ASSERT_FALSE(r);
dbb.commit_transaction();
r = dbb.close();
ASSERT_TRUE(r);
}
//////////////////////////////////////////////////////////////////////////////
// single_value_test
//////////////////////////////////////////////////////////////////////////////
struct serializable_string
{
serializable_string() {}
explicit serializable_string(const std::string& v) : v(v) {}
std::string v;
BEGIN_SERIALIZE_OBJECT()
FIELD(v)
END_SERIALIZE()
};
TEST(lmdb, single_value_test)
{
const std::string options_table_name("options");
std::shared_ptr<db::lmdb_db_backend> lmdb_ptr = std::make_shared<db::lmdb_db_backend>();
epee::shared_recursive_mutex db_lock;
db::basic_db_accessor dbb(lmdb_ptr, db_lock);
db::basic_key_value_accessor<uint64_t, uint64_t /* does not matter */, false /* does not matter */ > options_container(dbb);
db::solo_db_value<uint64_t, uint64_t, decltype(options_container), false> option_uint64(0, options_container);
db::solo_db_value<uint64_t, serializable_string, decltype(options_container), true> option_serializable_obj(1, options_container);
db::solo_db_value<uint64_t, crypto::hash, decltype(options_container), false> option_hash(2, options_container);
ASSERT_TRUE(dbb.open("single_value_test"));
// clear table
db::container_handle options_tid;
ASSERT_TRUE(lmdb_ptr->open_container(options_table_name, options_tid));
ASSERT_TRUE(dbb.begin_transaction());
ASSERT_TRUE(dbb.clear(options_tid));
dbb.commit_transaction();
ASSERT_TRUE(options_container.init(options_table_name));
// check defaults
ASSERT_TRUE(dbb.begin_transaction());
uint64_t v = option_uint64;
ASSERT_EQ(v, 0);
serializable_string ss = option_serializable_obj;
ASSERT_EQ(ss.v, std::string(""));
crypto::hash h = option_hash;
ASSERT_EQ(h, null_hash);
dbb.commit_transaction();
// set single values
ASSERT_TRUE(dbb.begin_transaction());
option_uint64 = 97;
option_serializable_obj = serializable_string("New York advertising men");
option_hash = crypto::cn_fast_hash(options_table_name.c_str(), options_table_name.size());
dbb.commit_transaction();
ASSERT_TRUE(dbb.close());
// reopen DB
ASSERT_TRUE(dbb.open("single_value_test"));
ASSERT_TRUE(options_container.init(options_table_name));
// get single value
ASSERT_TRUE(dbb.begin_transaction());
v = option_uint64;
ASSERT_EQ(v, 97);
ss = option_serializable_obj;
ASSERT_EQ(ss.v, "New York advertising men");
h = option_hash;
ASSERT_EQ(h, crypto::cn_fast_hash(options_table_name.c_str(), options_table_name.size()));
dbb.commit_transaction();
ASSERT_TRUE(dbb.close());
}
//////////////////////////////////////////////////////////////////////////////
// array_basic_test
//////////////////////////////////////////////////////////////////////////////
TEST(lmdb, array_basic_test)
{
bool r = false;
const std::string array_table_name("test_array");
std::shared_ptr<db::lmdb_db_backend> lmdb_ptr = std::make_shared<db::lmdb_db_backend>();
epee::shared_recursive_mutex db_lock;
db::basic_db_accessor dbb(lmdb_ptr, db_lock);
db::basic_key_to_array_accessor<uint64_t, serializable_string, true> db_array(dbb);
ASSERT_TRUE(dbb.open("array_basic_test"));
// clear table
db::container_handle tid;
ASSERT_TRUE(lmdb_ptr->open_container(array_table_name, tid));
ASSERT_TRUE(dbb.begin_transaction());
ASSERT_TRUE(dbb.clear(tid));
dbb.commit_transaction();
ASSERT_TRUE(db_array.init(array_table_name));
// check defaults
ASSERT_TRUE(dbb.begin_transaction());
size_t count = db_array.get_item_size(97);
ASSERT_EQ(count, 0);
std::shared_ptr<const serializable_string> ptr;
r = false;
try
{
ptr = db_array.get_subitem(97, 0);
}
catch (...)
{
r = true;
}
ASSERT_TRUE(r);
dbb.commit_transaction();
// write
ASSERT_TRUE(dbb.begin_transaction());
db_array.push_back_item(97, serializable_string("507507507507507507507507"));
db_array.push_back_item(97, serializable_string("787878787878787878787878"));
db_array.push_back_item(97, serializable_string("ringing phone"));
count = db_array.get_item_size(97);
ASSERT_EQ(count, 3);
dbb.commit_transaction();
ASSERT_TRUE(dbb.begin_transaction());
db_array.push_back_item(97, serializable_string("ring"));
db_array.push_back_item(97, serializable_string("ring"));
db_array.push_back_item(97, serializable_string("ring"));
count = db_array.get_item_size(97);
ASSERT_EQ(count, 6);
dbb.abort_transaction();
ASSERT_TRUE(dbb.begin_transaction());
count = db_array.get_item_size(97);
ASSERT_EQ(count, 3);
dbb.commit_transaction();
ASSERT_TRUE(dbb.close());
// reopen DB
ASSERT_TRUE(dbb.open("array_basic_test"));
ASSERT_TRUE(db_array.init(array_table_name));
ASSERT_TRUE(dbb.begin_transaction());
count = db_array.get_item_size(97);
ASSERT_EQ(count, 3);
ptr = db_array.get_subitem(97, 0);
ASSERT_TRUE((bool)ptr);
ASSERT_EQ(ptr->v, "507507507507507507507507");
ptr = db_array.get_subitem(97, 1);
ASSERT_TRUE((bool)ptr);
ASSERT_EQ(ptr->v, "787878787878787878787878");
ptr = db_array.get_subitem(97, 2);
ASSERT_TRUE((bool)ptr);
ASSERT_EQ(ptr->v, "ringing phone");
ASSERT_EQ(db_array.get_item_size(555), 0);
db_array.pop_back_item(555);
ASSERT_EQ(db_array.get_item_size(555), 0);
db_array.pop_back_item(97);
db_array.pop_back_item(97);
dbb.commit_transaction();
ASSERT_TRUE(dbb.begin_transaction());
count = db_array.get_item_size(97);
ASSERT_EQ(count, 1);
ptr = db_array.get_subitem(97, 0);
ASSERT_TRUE((bool)ptr);
ASSERT_EQ(ptr->v, "507507507507507507507507");
dbb.commit_transaction();
ASSERT_TRUE(dbb.close());
}
//////////////////////////////////////////////////////////////////////////////
// array_accessor_test
//////////////////////////////////////////////////////////////////////////////
TEST(lmdb, array_accessor_test)
{
bool r = false;
const std::string array_table_name("array");
std::shared_ptr<db::lmdb_db_backend> lmdb_ptr = std::make_shared<db::lmdb_db_backend>();
epee::shared_recursive_mutex db_lock;
db::basic_db_accessor dbb(lmdb_ptr, db_lock);
db::array_accessor<serializable_string, true> db_array(dbb);
ASSERT_TRUE(dbb.open("array_accessor_test"));
// clear table
db::container_handle tid;
ASSERT_TRUE(lmdb_ptr->open_container(array_table_name, tid));
ASSERT_TRUE(dbb.begin_transaction());
ASSERT_TRUE(dbb.clear(tid));
dbb.commit_transaction();
// check defaults
ASSERT_TRUE(db_array.init(array_table_name));
ASSERT_TRUE(db_array.begin_transaction(true));
size_t count = db_array.size();
ASSERT_EQ(count, 0);
count = db_array.size_no_cache();
ASSERT_EQ(count, 0);
std::shared_ptr<const serializable_string> ptr;
r = false;
try
{
ptr = db_array.back();
}
catch(...)
{
r = true;
}
ASSERT_TRUE(r);
ASSERT_FALSE(db_array.clear()); // clear() should fail on read-only transaction
r = false;
try
{
ptr = db_array[0];
}
catch(...)
{
r = true;
}
ASSERT_TRUE(r);
db_array.commit_transaction();
ASSERT_TRUE(db_array.begin_transaction());
db_array.push_back(serializable_string("A"));
db_array.push_back(serializable_string("B"));
db_array.push_back(serializable_string("C"));
db_array.push_back(serializable_string("D"));
db_array.push_back(serializable_string("E"));
db_array.commit_transaction();
ASSERT_TRUE(db_array.begin_transaction());
ptr = db_array[4];
ASSERT_EQ(ptr->v, "E");
db_array.pop_back();
ASSERT_EQ(db_array.size(), 4);
r = false;
try
{
ptr = db_array[4];
}
catch(...)
{
r = true;
}
ASSERT_TRUE(r);
ptr = db_array[3];
ASSERT_EQ(ptr->v, "D");
db_array.push_back(serializable_string("X"));
ptr = db_array[4];
ASSERT_EQ(ptr->v, "X");
ASSERT_TRUE(db_array.clear());
db_array.commit_transaction();
ASSERT_TRUE(db_array.begin_transaction(true));
count = db_array.size();
ASSERT_EQ(count, 0);
db_array.commit_transaction();
}
//////////////////////////////////////////////////////////////////////////////
// key_value_test
//////////////////////////////////////////////////////////////////////////////
TEST(lmdb, key_value_test)
{
bool r = false;
const std::string array_table_name("key-value");
std::shared_ptr<db::lmdb_db_backend> lmdb_ptr = std::make_shared<db::lmdb_db_backend>();
epee::shared_recursive_mutex db_lock;
db::basic_db_accessor dbb(lmdb_ptr, db_lock);
db::basic_key_value_accessor<uint64_t, serializable_string, true> db_key_value_map(dbb);
ASSERT_TRUE(dbb.open("key-value_test"));
// clear table
db::container_handle tid;
ASSERT_TRUE(lmdb_ptr->open_container(array_table_name, tid));
ASSERT_TRUE(dbb.begin_transaction());
ASSERT_TRUE(dbb.clear(tid));
dbb.commit_transaction();
// check defaults
ASSERT_TRUE(db_key_value_map.init(array_table_name));
ASSERT_TRUE(db_key_value_map.begin_transaction(true));
size_t count = db_key_value_map.size();
ASSERT_EQ(count, 0);
count = db_key_value_map.size_no_cache();
ASSERT_EQ(count, 0);
std::shared_ptr<const serializable_string> ptr;
r = false;
try
{
ptr = db_key_value_map[99];
}
catch (std::out_of_range&)
{
r = true;
}
ASSERT_TRUE(r);
ptr = db_key_value_map.get(99);
ASSERT_TRUE(!ptr);
ASSERT_FALSE(db_key_value_map.clear()); // clear() should fail on read-only transaction
db_key_value_map.commit_transaction();
ASSERT_TRUE(db_key_value_map.begin_transaction());
db_key_value_map.set(99, serializable_string("A"));
db_key_value_map.set(100, serializable_string("B"));
db_key_value_map.set(101, serializable_string("C"));
db_key_value_map.set(102, serializable_string("D"));
db_key_value_map.commit_transaction();
ASSERT_TRUE(db_key_value_map.begin_transaction());
ASSERT_EQ(db_key_value_map.size(), 4);
ptr = db_key_value_map[102];
ASSERT_EQ(ptr->v, "D");
ASSERT_TRUE(db_key_value_map.erase_validate(102));
ASSERT_EQ(db_key_value_map.size(), 3);
db_key_value_map.erase(102);
ASSERT_EQ(db_key_value_map.size(), 3);
ASSERT_FALSE(db_key_value_map.erase_validate(102));
ASSERT_EQ(db_key_value_map.size(), 3);
r = false;
try
{
ptr = db_key_value_map[0];
}
catch (std::out_of_range&)
{
r = true;
}
ASSERT_TRUE(r);
ptr = db_key_value_map[99];
ASSERT_EQ(ptr->v, "A");
db_key_value_map.set(102, serializable_string("W"));
ASSERT_EQ(db_key_value_map.size(), 4);
ptr = db_key_value_map[102];
ASSERT_EQ(ptr->v, "W");
ASSERT_TRUE(db_key_value_map.clear());
db_key_value_map.commit_transaction();
ASSERT_TRUE(db_key_value_map.begin_transaction(true));
ASSERT_EQ(db_key_value_map.size(), 0);
db_key_value_map.commit_transaction();
}
//////////////////////////////////////////////////////////////////////////////
// 2gb_test
//////////////////////////////////////////////////////////////////////////////
TEST(lmdb, 2gb_test)
{
bool r = false;
@ -54,7 +932,7 @@ namespace lmdb_test
if (key % 1024 == 0)
{
ASSERT_TRUE(lmdb_ptr->commit_transaction());
ASSERT_TRUE(lmdb_ptr->resize_if_needed());
//ASSERT_TRUE(lmdb_ptr->resize_if_needed());
ASSERT_TRUE(lmdb_ptr->begin_transaction());
std::cout << total_data / 1024 / 1024 << " MB written to DB" << ENDL;
}
@ -101,4 +979,4 @@ namespace lmdb_test
}
}
} // namespace lmdb_test

View file

@ -40,19 +40,19 @@ if [ $? -ne 0 ]; then
exit 1
fi
make -j daemon Zano;
make -j1 daemon Zano;
if [ $? -ne 0 ]; then
echo "Failed to make!"
exit 1
fi
make -j simplewallet;
make -j1 simplewallet;
if [ $? -ne 0 ]; then
echo "Failed to make!"
exit 1
fi
make -j connectivity_tool;
make -j1 connectivity_tool;
if [ $? -ne 0 ]; then
echo "Failed to make!"
exit 1

Some files were not shown because too many files have changed in this diff Show more