1
0
Fork 0
forked from lthn/blockchain

Merge branch 'develop' into release

This commit is contained in:
sowle 2019-12-25 06:43:49 +03:00
commit 0f04017d2e
No known key found for this signature in database
GPG key ID: C07A24B2D89D49FC
95 changed files with 5702 additions and 1329 deletions

View file

@ -1,3 +1,6 @@
[![Coverity Scan](https://scan.coverity.com/projects/18767/badge.svg)](https://scan.coverity.com/projects/zanoproject)
Building
--------

@ -1 +1 @@
Subproject commit a0ec89e46833e61d9b93850e85157d4bc93db1b2
Subproject commit b7ed67543fefb0878dba1c70dea2a81201041314

View file

@ -0,0 +1,116 @@
// Copyright (c) 2019, Zano Project
// Copyright (c) 2006-2019, Andrey N. Sabelnikov, www.sabelnikov.net
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of the Andrey N. Sabelnikov nor the
// names of its contributors may be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
#pragma once
#define COMBINE1(X,Y) X##Y // helper macro
#define COMBINE(X,Y) COMBINE1(X,Y)
#define _STR(X) #X
#define STR(X) _STR(X)
#if defined(_MSC_VER)
#define LOCAL_FUNCTION_DEF__ __FUNCTION__
#define UNUSED_ATTRIBUTE
#else
#define LOCAL_FUNCTION_DEF__ __FUNCTION__
#define UNUSED_ATTRIBUTE __attribute__((unused))
#endif
#define LOCATION_SS "[" << LOCAL_FUNCTION_DEF__ << ("] @ " __FILE__ ":" STR(__LINE__))
#define LOCATION_STR (std::string("[") + LOCAL_FUNCTION_DEF__ + "] @ " __FILE__ ":" STR(__LINE__))
//
// Try-catch helpers
//
#define TRY_ENTRY() try {
#define CATCH_ALL_DO_NOTHING() }catch(...) {}
#define CATCH_ENTRY_CUSTOM(location, custom_code, return_val) } \
catch(const std::exception& ex) \
{ \
(void)(ex); \
LOG_ERROR("Exception at [" << location << "], what=" << ex.what()); \
custom_code; \
return return_val; \
} \
catch(...) \
{ \
LOG_ERROR("Exception at [" << location << "], generic exception \"...\""); \
custom_code; \
return return_val; \
}
#define CATCH_ENTRY(location, return_val) CATCH_ENTRY_CUSTOM(location, (void)0, return_val)
#define CATCH_ENTRY2(return_val) CATCH_ENTRY_CUSTOM(LOCATION_SS, (void)0, return_val)
#define CATCH_ENTRY_L0(location, return_val) CATCH_ENTRY(location, return_val)
#define CATCH_ENTRY_L1(location, return_val) CATCH_ENTRY(location, return_val)
#define CATCH_ENTRY_L2(location, return_val) CATCH_ENTRY(location, return_val)
#define CATCH_ENTRY_L3(location, return_val) CATCH_ENTRY(location, return_val)
#define CATCH_ENTRY_L4(location, return_val) CATCH_ENTRY(location, return_val)
/// @brief Catches TRY_ENTRY without returning
/// @details Useful within a dtor - but only if nested within another try block
/// (since we can still potentially throw here). See NESTED_*ENTRY()
/// @todo Exception dispatcher class
#define CATCH_ENTRY_NO_RETURN_CUSTOM(location, custom_code) } \
catch(const std::exception& ex) \
{ \
(void)(ex); \
LOG_ERROR("Exception at [" << location << "], what=" << ex.what()); \
custom_code; \
} \
catch(...) \
{ \
LOG_ERROR("Exception at [" << location << "], generic exception \"...\""); \
custom_code; \
}
#define CATCH_ENTRY_NO_RETURN() CATCH_ENTRY_NO_RETURN_CUSTOM(LOCATION_SS, (void)0)
#define CATCH_ENTRY_WITH_FORWARDING_EXCEPTION() } \
catch(const std::exception& ex) \
{ \
LOG_ERROR("Exception at [" << LOCATION_SS << "], what=" << ex.what()); \
throw std::runtime_error(std::string("[EXCEPTION FORWARDED]: ") + ex.what()); \
} \
catch(...) \
{ \
LOG_ERROR("Exception at [" << LOCATION_SS << "], generic unknown exception \"...\""); \
throw std::runtime_error("[EXCEPTION FORWARDED]"); \
}
#define NESTED_TRY_ENTRY() try { TRY_ENTRY();
#define NESTED_CATCH_ENTRY(location) \
CATCH_ENTRY_NO_RETURN_CUSTOM(location, {}); \
} catch (...) {}

View file

@ -393,7 +393,21 @@ namespace misc_utils
auto res = container.insert(typename t_container_type::value_type(key, AUTO_VAL_INIT(typename t_container_type::mapped_type())));
return res.first->second;
}
}
template<class t_container_type>
typename t_container_type::iterator it_get_or_insert_value_initialized(t_container_type& container, const typename t_container_type::key_type& key)
{
auto it = container.find(key);
if (it != container.end())
{
return it;
}
auto res = container.insert(typename t_container_type::value_type(key, AUTO_VAL_INIT(typename t_container_type::mapped_type())));
return res.first;
}
} // namespace misc_utils
} // namespace epee

View file

@ -1,3 +1,4 @@
// Copyright (c) 2019, Zano Project
// Copyright (c) 2019, anonimal <anonimal@zano.org>
// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
// All rights reserved.
@ -59,6 +60,7 @@ PUSH_VS_WARNINGS
DISABLE_VS_WARNINGS(4100)
#include "misc_helpers.h"
#include "static_initializer.h"
#include "string_tools.h"
#include "time_helper.h"
@ -106,21 +108,6 @@ DISABLE_VS_WARNINGS(4100)
#endif
#define COMBINE1(X,Y) X##Y // helper macro
#define COMBINE(X,Y) COMBINE1(X,Y)
#define _STR(X) #X
#define STR(X) _STR(X)
#if defined(_MSC_VER)
#define LOCAL_FUNCTION_DEF__ __FUNCTION__
#define UNUSED_ATTRIBUTE
#else
#define LOCAL_FUNCTION_DEF__ __PRETTY_FUNCTION__
#define UNUSED_ATTRIBUTE __attribute__((unused))
#endif
#define LOCATION_SS "[" << LOCAL_FUNCTION_DEF__ << ("] @ " __FILE__ ":" STR(__LINE__))
#if !defined(DISABLE_RELEASE_LOGGING)
#define ENABLE_LOGGING_INTERNAL
#endif
@ -131,28 +118,29 @@ DISABLE_VS_WARNINGS(4100)
epee::log_space::log_singletone::enable_channel(ch_name); return true; \
});
#if defined(ENABLE_LOGGING_INTERNAL)
#define LOG_PRINT_CHANNEL_NO_PREFIX2(log_channel, log_name, x, y) {if ( y <= epee::log_space::log_singletone::get_log_detalisation_level() && epee::log_space::log_singletone::channel_enabled(log_channel))\
{std::stringstream ss________; ss________ << x << std::endl; epee::log_space::log_singletone::do_log_message(ss________.str() , y, epee::log_space::console_color_default, false, log_name);}}
{TRY_ENTRY();std::stringstream ss________; ss________ << x << std::endl; epee::log_space::log_singletone::do_log_message(ss________.str() , y, epee::log_space::console_color_default, false, log_name);CATCH_ALL_DO_NOTHING();}}
#define LOG_PRINT_CHANNEL_NO_PREFIX_NO_POSTFIX2(log_channel, log_name, x, y) {if ( y <= epee::log_space::log_singletone::get_log_detalisation_level() && epee::log_space::log_singletone::channel_enabled(log_channel))\
{std::stringstream ss________; ss________ << x; epee::log_space::log_singletone::do_log_message(ss________.str(), y, epee::log_space::console_color_default, false, log_name);}}
{TRY_ENTRY();std::stringstream ss________; ss________ << x; epee::log_space::log_singletone::do_log_message(ss________.str(), y, epee::log_space::console_color_default, false, log_name);CATCH_ALL_DO_NOTHING();}}
#define LOG_PRINT_CHANNEL_NO_POSTFIX2(log_channel, log_name, x, y) {if ( y <= epee::log_space::log_singletone::get_log_detalisation_level() && epee::log_space::log_singletone::channel_enabled(log_channel))\
{std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << x; epee::log_space::log_singletone::do_log_message(ss________.str(), y, epee::log_space::console_color_default, false, log_name);}}
{TRY_ENTRY();std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << x; epee::log_space::log_singletone::do_log_message(ss________.str(), y, epee::log_space::console_color_default, false, log_name);CATCH_ALL_DO_NOTHING();}}
#define LOG_PRINT_CHANNEL2(log_channel, log_name, x, y) {if ( y <= epee::log_space::log_singletone::get_log_detalisation_level() && epee::log_space::log_singletone::channel_enabled(log_channel))\
{std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << x << std::endl;epee::log_space::log_singletone::do_log_message(ss________.str(), y, epee::log_space::console_color_default, false, log_name);}}
{TRY_ENTRY();std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << x << std::endl;epee::log_space::log_singletone::do_log_message(ss________.str(), y, epee::log_space::console_color_default, false, log_name);CATCH_ALL_DO_NOTHING();}}
#define LOG_PRINT_CHANNEL_COLOR2(log_channel, log_name, x, y, color) {if ( y <= epee::log_space::log_singletone::get_log_detalisation_level() && epee::log_space::log_singletone::channel_enabled(log_channel))\
{std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << x << std::endl;epee::log_space::log_singletone::do_log_message(ss________.str(), y, color, false, log_name);}}
{TRY_ENTRY();std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << x << std::endl;epee::log_space::log_singletone::do_log_message(ss________.str(), y, color, false, log_name);CATCH_ALL_DO_NOTHING();}}
#define LOG_PRINT_CHANNEL_2_JORNAL(log_channel, log_name, x, y) {if ( y <= epee::log_space::log_singletone::get_log_detalisation_level() && epee::log_space::log_singletone::channel_enabled(log_channel))\
{std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << x << std::endl;epee::log_space::log_singletone::do_log_message(ss________.str(), y, epee::log_space::console_color_default, true, log_name);}}
{TRY_ENTRY();std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << x << std::endl;epee::log_space::log_singletone::do_log_message(ss________.str(), y, epee::log_space::console_color_default, true, log_name);CATCH_ALL_DO_NOTHING();}}
#define LOG_ERROR2(log_name, x) { \
std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << "[ERROR] Location: " << std::endl << LOCATION_SS << epee::misc_utils::get_callstack() << " Message:" << std::endl << x << std::endl; epee::log_space::log_singletone::do_log_message(ss________.str(), LOG_LEVEL_0, epee::log_space::console_color_red, true, log_name); LOCAL_ASSERT(0); epee::log_space::increase_error_count(LOG_DEFAULT_CHANNEL); }
TRY_ENTRY();std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << "[ERROR] Location: " << std::endl << LOCATION_SS << epee::misc_utils::get_callstack() << " Message:" << std::endl << x << std::endl; epee::log_space::log_singletone::do_log_message(ss________.str(), LOG_LEVEL_0, epee::log_space::console_color_red, true, log_name); LOCAL_ASSERT(0); epee::log_space::increase_error_count(LOG_DEFAULT_CHANNEL);CATCH_ALL_DO_NOTHING();}
#define LOG_FRAME2(log_name, x, y) epee::log_space::log_frame frame(x, y, log_name)
@ -212,67 +200,6 @@ DISABLE_VS_WARNINGS(4100)
#define ENDL std::endl
#define TRY_ENTRY() try {
#define CATCH_ENTRY_CUSTOM(location, custom_code, return_val) } \
catch(const std::exception& ex) \
{ \
(void)(ex); \
LOG_ERROR("Exception at [" << location << "], what=" << ex.what()); \
custom_code; \
return return_val; \
} \
catch(...) \
{ \
LOG_ERROR("Exception at [" << location << "], generic exception \"...\""); \
custom_code; \
return return_val; \
}
#define CATCH_ENTRY(location, return_val) CATCH_ENTRY_CUSTOM(location, (void)0, return_val)
#define CATCH_ENTRY2(return_val) CATCH_ENTRY_CUSTOM(LOCATION_SS, (void)0, return_val)
#define CATCH_ENTRY_L0(location, return_val) CATCH_ENTRY(location, return_val)
#define CATCH_ENTRY_L1(location, return_val) CATCH_ENTRY(location, return_val)
#define CATCH_ENTRY_L2(location, return_val) CATCH_ENTRY(location, return_val)
#define CATCH_ENTRY_L3(location, return_val) CATCH_ENTRY(location, return_val)
#define CATCH_ENTRY_L4(location, return_val) CATCH_ENTRY(location, return_val)
/// @brief Catches TRY_ENTRY without returning
/// @details Useful within a dtor - but only if nested within another try block
/// (since we can still potentially throw here). See NESTED_*ENTRY()
/// @todo Exception dispatcher class
#define CATCH_ENTRY_NO_RETURN(location, custom_code) } \
catch(const std::exception& ex) \
{ \
(void)(ex); \
LOG_ERROR("Exception at [" << location << "], what=" << ex.what()); \
custom_code; \
} \
catch(...) \
{ \
LOG_ERROR("Exception at [" << location << "], generic exception \"...\""); \
custom_code; \
}
#define CATCH_ENTRY_WITH_FORWARDING_EXCEPTION() } \
catch(const std::exception& ex) \
{ \
LOG_ERROR("Exception at [" << LOCATION_SS << "], what=" << ex.what()); \
throw std::runtime_error(std::string("[EXCEPTION FORWARDED]: ") + ex.what()); \
} \
catch(...) \
{ \
LOG_ERROR("Exception at [" << LOCATION_SS << "], generic unknown exception \"...\""); \
throw std::runtime_error("[EXCEPTION FORWARDED]"); \
}
#define NESTED_TRY_ENTRY() try { TRY_ENTRY();
#define NESTED_CATCH_ENTRY(location) \
CATCH_ENTRY_NO_RETURN(location, {}); \
} catch (...) {}
#define ASSERT_MES_AND_THROW(message) {LOG_ERROR(message); std::stringstream ss; ss << message; throw std::runtime_error(ss.str());}

View file

@ -213,8 +213,8 @@ class boosted_tcp_server
return true;
}
idle_callback_conext_base(boost::asio::io_service& io_serice)
: m_timer(io_serice)
idle_callback_conext_base(boost::asio::io_service& io_serice): m_timer(io_serice),
m_period(0)
{
}
boost::asio::deadline_timer m_timer;
@ -254,7 +254,14 @@ class boosted_tcp_server
if(!ptr->call_handler())
return true;
}
catch(...) {
catch(std::exception& e)
{
LOG_ERROR("exeption caught in boosted_tcp_server::global_timer_handler: " << e.what() << ENDL << "won't be called anymore");
return true;
}
catch(...)
{
LOG_ERROR("unknown exeption caught in boosted_tcp_server::global_timer_handler, it won't be called anymore");
return true;
}

View file

@ -870,8 +870,10 @@ using namespace std;
if(!u_c.port)
u_c.port = 80;//default for http
res = tr.connect(u_c.host, static_cast<int>(u_c.port), timeout);
CHECK_AND_ASSERT_MES(res, false, "failed to connect " << u_c.host << ":" << u_c.port);
if (!tr.connect(u_c.host, static_cast<int>(u_c.port), timeout))
{
LOG_PRINT_L2("invoke_request: cannot connect to " << u_c.host << ":" << u_c.port);
}
}
return tr.invoke(u_c.uri, method, body, ppresponse_info, additional_params);

View file

@ -51,6 +51,7 @@ namespace epee
}
};
//basic helpers for pod-to-hex serialization
template<class t_pod_type>
std::string transform_t_pod_to_str(const t_pod_type & a)
@ -61,7 +62,8 @@ namespace epee
t_pod_type transform_str_to_t_pod(const std::string& a)
{
t_pod_type res = AUTO_VAL_INIT(res);
epee::string_tools::hex_to_pod(a, res);
if (!epee::string_tools::hex_to_pod(a, res))
throw std::runtime_error(std::string("Unable to transform \"") + a + "\" to pod type " + typeid(t_pod_type).name());
return res;
}

View file

@ -0,0 +1,41 @@
// Copyright (c) 2006-2019, Andrey N. Sabelnikov, www.sabelnikov.net
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of the Andrey N. Sabelnikov nor the
// names of its contributors may be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
#pragma once
#include "keyvalue_serialization.h"
namespace epee
{
struct hexemizer
{
std::string blob;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_BLOB_AS_HEX_STRING(blob)
END_KV_SERIALIZE_MAP()
};
}

View file

@ -1,4 +1,5 @@
// Copyright (c) 2019, anonimal, <anonimal@zano.org>
// Copyright (c) 2019, Zano Project
// Copyright (c) 2019, anonimal, <anonimal@zano.org>
// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
// All rights reserved.
//
@ -24,12 +25,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
#ifndef __WINH_OBJ_H__
#define __WINH_OBJ_H__
#pragma once
#include <thread>
#include <condition_variable>
#include <atomic>
@ -40,7 +36,7 @@
#include "singleton.h"
#include "static_initializer.h"
#include "misc_helpers.h"
//#define DISABLE_DEADLOCK_GUARD
@ -57,9 +53,6 @@
}
namespace epee
{
@ -526,9 +519,11 @@ namespace epee
<< prev_it->second.func_name << " @ " << prev_it->second.block_location << std::endl << " |" << std::endl << " V" << std::endl;
prev_it = current_it;
}
ss << prev_it->second.thread_name << "(tid:" << prev_it->first << ") blocked by locker \"" << lock_name << "(owned by " << (*threads_chain.begin())->second.thread_name << " tid:" << (*threads_chain.begin())->first << ")] at "
<< func_name << " @ " << location << std::endl;
if (prev_it != m_thread_owned_locks.end())
{
ss << prev_it->second.thread_name << "(tid:" << prev_it->first << ") blocked by locker \"" << lock_name << "(owned by " << (*threads_chain.begin())->second.thread_name << " tid:" << (*threads_chain.begin())->first << ")] at "
<< func_name << " @ " << location << std::endl;
}
m_deadlock_journal.push_back(ss.str());
throw std::runtime_error(ss.str());
}
@ -605,14 +600,9 @@ namespace epee
~guarded_critical_region_t()
{
// TODO(unassigned): because one cannot forward-declare macros,
// the circular dependency created by misc_log_ex will not
// allow us to actually use these substitutions.
//NESTED_TRY_ENTRY();
TRY_ENTRY();
unlock();
//NESTED_CATCH_ENTRY(__func__);
CATCH_ALL_DO_NOTHING();
}
void unlock()
@ -708,6 +698,9 @@ namespace epee
#define CRITICAL_REGION_BEGIN1(x) CRITICAL_REGION_BEGIN_VAR(x, critical_region_var1)
#define CRITICAL_REGION_END() }
#define CIRITCAL_OPERATION(obj,op) {obj##_lock.lock();obj . op;obj##_lock.unlock();}
#define SHARED_CRITICAL_REGION_LOCAL(x) boost::shared_lock< boost::shared_mutex > critical_region_var(x)
#define EXCLUSIVE_CRITICAL_REGION_LOCAL(x) boost::unique_lock< boost::shared_mutex > critical_region_var(x)
@ -716,6 +709,3 @@ namespace epee
}
#endif

View file

@ -3,7 +3,6 @@ if(POLICY CMP0043)
cmake_policy(SET CMP0043 OLD)
endif()
###########
# using shared PCH -- this is unusual case for MSVC... so mystery, such hack, many wow. See also: https://stackoverflow.com/questions/645747/sharing-precompiled-headers-between-projects-in-visual-studio/4170902#4170902
# define USE_PCH to YES for using precomiled headers

View file

@ -89,8 +89,11 @@ namespace tools
mutable performance_data m_gperformance_data;
mutable std::unordered_map<container_handle, performance_data> m_performance_data_map;
public:
basic_db_accessor(std::shared_ptr<i_db_backend> backend, epee::shared_recursive_mutex& rwlock) :m_backend(backend), m_rwlock(rwlock), m_is_open(false)
{}
basic_db_accessor(std::shared_ptr<i_db_backend> backend, epee::shared_recursive_mutex& rwlock)
: m_backend(backend), m_rwlock(rwlock), m_is_open(false)
{
}
~basic_db_accessor()
{
close();
@ -239,8 +242,11 @@ namespace tools
bool close()
{
m_is_open = false;
if (!m_backend)
return true;
return m_backend->close();
}
bool open(const std::string& path, uint64_t cache_sz = CACHE_SIZE)
{
bool r = m_backend->open(path, cache_sz);
@ -513,7 +519,9 @@ namespace tools
~basic_key_value_accessor()
{
TRY_ENTRY();
bdb.unbind_parent_container(this);
CATCH_ALL_DO_NOTHING();
}
virtual bool on_write_transaction_begin()

View file

@ -11,8 +11,8 @@
#define BUF_SIZE 1024
#define CHECK_AND_ASSERT_MESS_LMDB_DB(rc, ret, mess) CHECK_AND_ASSERT_MES(res == MDB_SUCCESS, ret, "[DB ERROR]:(" << rc << ")" << mdb_strerror(rc) << ", [message]: " << mess);
#define CHECK_AND_ASSERT_THROW_MESS_LMDB_DB(rc, mess) CHECK_AND_ASSERT_THROW_MES(res == MDB_SUCCESS, "[DB ERROR]:(" << rc << ")" << mdb_strerror(rc) << ", [message]: " << mess);
#define CHECK_AND_ASSERT_MESS_LMDB_DB(rc, ret, mess) CHECK_AND_ASSERT_MES(rc == MDB_SUCCESS, ret, "[DB ERROR]:(" << rc << ")" << mdb_strerror(rc) << ", [message]: " << mess);
#define CHECK_AND_ASSERT_THROW_MESS_LMDB_DB(rc, mess) CHECK_AND_ASSERT_THROW_MES(rc == MDB_SUCCESS, "[DB ERROR]:(" << rc << ")" << mdb_strerror(rc) << ", [message]: " << mess);
#define ASSERT_MES_AND_THROW_LMDB(rc, mess) ASSERT_MES_AND_THROW("[DB ERROR]:(" << rc << ")" << mdb_strerror(rc) << ", [message]: " << mess);
#undef LOG_DEFAULT_CHANNEL

View file

@ -15,27 +15,34 @@ namespace tools
namespace db
{
inline
bool select_db_engine_from_arg(const boost::program_options::variables_map& vm, tools::db::basic_db_accessor& rdb)
bool select_db_engine_from_arg(const boost::program_options::variables_map& vm, tools::db::basic_db_accessor& rdb)
{
if (command_line::get_arg(vm, command_line::arg_db_engine) == ARG_DB_ENGINE_LMDB)
try
{
rdb.reset_backend(std::shared_ptr<tools::db::i_db_backend>(new tools::db::lmdb_db_backend));
if (command_line::get_arg(vm, command_line::arg_db_engine) == ARG_DB_ENGINE_LMDB)
{
rdb.reset_backend(std::shared_ptr<tools::db::i_db_backend>(new tools::db::lmdb_db_backend));
return true;
}
else if (command_line::get_arg(vm, command_line::arg_db_engine) == ARG_DB_ENGINE_MDBX)
{
#ifdef ENABLED_ENGINE_MDBX
rdb.reset_backend(std::shared_ptr<tools::db::i_db_backend>(new tools::db::mdbx_db_backend));
return true;
#else
LOG_PRINT_L0(" DB ENGINE: " << ARG_DB_ENGINE_MDBX << " is not suported by this build(see DISABLE_MDBX cmake option), STOPPING");
return false;
#endif
}
}
else if (command_line::get_arg(vm, command_line::arg_db_engine) == ARG_DB_ENGINE_MDBX)
catch (...)
{
#ifdef ENABLED_ENGINE_MDBX
rdb.reset_backend(std::shared_ptr<tools::db::i_db_backend>(new tools::db::mdbx_db_backend));
#else
LOG_PRINT_L0(" DB ENGINE: " << ARG_DB_ENGINE_MDBX << " is not suported by this build(see DISABLE_MDBX cmake option), STOPPING");
return false;
#endif
}
else
{
LOG_PRINT_RED_L0(" UNKNOWN DB ENGINE: " << command_line::get_arg(vm, command_line::arg_db_engine) << ", STOPPING");
LOG_ERROR("internal error: arg_db_engine command-line option could not be read (exception caught)");
return false;
}
return true;
LOG_PRINT_RED_L0(" UNKNOWN DB ENGINE: " << command_line::get_arg(vm, command_line::arg_db_engine) << ", STOPPING");
return false;
}
}
}
}

View file

@ -31,8 +31,10 @@ namespace bc_services
//------------------------------------------------------------------
bc_offers_service::~bc_offers_service()
{
TRY_ENTRY();
if (!m_deinitialized)
deinit();
CATCH_ENTRY_NO_RETURN();
}
//------------------------------------------------------------------
bool bc_offers_service::init(const std::string& config_folder, const boost::program_options::variables_map& vm)

View file

@ -32,6 +32,7 @@
#include "storages/portable_storage_template_helper.h"
#include "basic_pow_helpers.h"
#include "version.h"
#include "tx_semantic_validation.h"
#undef LOG_DEFAULT_CHANNEL
#define LOG_DEFAULT_CHANNEL "core"
@ -152,6 +153,7 @@ void blockchain_storage::init_options(boost::program_options::options_descriptio
{
command_line::add_arg(desc, arg_db_cache_l1);
command_line::add_arg(desc, arg_db_cache_l2);
command_line::add_arg(desc, command_line::arg_db_engine);
}
//------------------------------------------------------------------
uint64_t blockchain_storage::get_block_h_older_then(uint64_t timestamp) const
@ -437,7 +439,7 @@ bool blockchain_storage::deinit()
return true;
}
//------------------------------------------------------------------
bool blockchain_storage::pop_block_from_blockchain()
bool blockchain_storage::pop_block_from_blockchain(transactions_map& onboard_transactions)
{
CRITICAL_REGION_LOCAL(m_read_lock);
@ -447,7 +449,7 @@ bool blockchain_storage::pop_block_from_blockchain()
CHECK_AND_ASSERT_MES(bei_ptr.get(), false, "pop_block_from_blockchain: can't pop from blockchain");
uint64_t fee_total = 0;
bool r = purge_block_data_from_blockchain(bei_ptr->bl, bei_ptr->bl.tx_hashes.size(), fee_total);
bool r = purge_block_data_from_blockchain(bei_ptr->bl, bei_ptr->bl.tx_hashes.size(), fee_total, onboard_transactions);
CHECK_AND_ASSERT_MES(r, false, "Failed to purge_block_data_from_blockchain for block " << get_block_hash(bei_ptr->bl) << " on height " << h);
pop_block_from_per_block_increments(bei_ptr->height);
@ -647,7 +649,7 @@ bool blockchain_storage::purge_transaction_keyimages_from_blockchain(const trans
return true;
}
//------------------------------------------------------------------
bool blockchain_storage::purge_transaction_from_blockchain(const crypto::hash& tx_id, uint64_t& fee)
bool blockchain_storage::purge_transaction_from_blockchain(const crypto::hash& tx_id, uint64_t& fee, transaction& tx_)
{
fee = 0;
CRITICAL_REGION_LOCAL(m_read_lock);
@ -655,6 +657,7 @@ bool blockchain_storage::purge_transaction_from_blockchain(const crypto::hash& t
auto tx_res_ptr = m_db_transactions.find(tx_id);
CHECK_AND_ASSERT_MES(tx_res_ptr != m_db_transactions.end(), false, "transaction " << tx_id << " is not found in blockchain index!!");
const transaction& tx = tx_res_ptr->tx;
tx_ = tx;
fee = get_tx_fee(tx_res_ptr->tx);
purge_transaction_keyimages_from_blockchain(tx, true);
@ -685,10 +688,11 @@ bool blockchain_storage::purge_transaction_from_blockchain(const crypto::hash& t
bool blockchain_storage::purge_block_data_from_blockchain(const block& b, size_t processed_tx_count)
{
uint64_t total_fee = 0;
return purge_block_data_from_blockchain(b, processed_tx_count, total_fee);
transactions_map onboard_transactions;
return purge_block_data_from_blockchain(b, processed_tx_count, total_fee, onboard_transactions);
}
//------------------------------------------------------------------
bool blockchain_storage::purge_block_data_from_blockchain(const block& bl, size_t processed_tx_count, uint64_t& fee_total)
bool blockchain_storage::purge_block_data_from_blockchain(const block& bl, size_t processed_tx_count, uint64_t& fee_total, transactions_map& onboard_transactions)
{
CRITICAL_REGION_LOCAL(m_read_lock);
fee_total = 0;
@ -697,11 +701,13 @@ bool blockchain_storage::purge_block_data_from_blockchain(const block& bl, size_
CHECK_AND_ASSERT_MES(processed_tx_count <= bl.tx_hashes.size(), false, "wrong processed_tx_count in purge_block_data_from_blockchain");
for(size_t count = 0; count != processed_tx_count; count++)
{
res = purge_transaction_from_blockchain(bl.tx_hashes[(processed_tx_count -1)- count], fee) && res;
transaction tx = AUTO_VAL_INIT(tx);
res = purge_transaction_from_blockchain(bl.tx_hashes[(processed_tx_count -1)- count], fee, tx) && res;
fee_total += fee;
onboard_transactions[bl.tx_hashes[(processed_tx_count - 1) - count]] = tx;
}
res = purge_transaction_from_blockchain(get_transaction_hash(bl.miner_tx), fee) && res;
transaction tx = AUTO_VAL_INIT(tx);
res = purge_transaction_from_blockchain(get_transaction_hash(bl.miner_tx), fee, tx) && res;
return res;
}
//------------------------------------------------------------------
@ -864,20 +870,22 @@ bool blockchain_storage::get_block_by_height(uint64_t h, block &blk) const
// invalid.push_back(v.first);
// }
//------------------------------------------------------------------
bool blockchain_storage::rollback_blockchain_switching(std::list<block>& original_chain, size_t rollback_height)
bool blockchain_storage::rollback_blockchain_switching(std::list<block_ws_txs>& original_chain, size_t rollback_height)
{
CRITICAL_REGION_LOCAL(m_read_lock);
//remove failed subchain
for(size_t i = m_db_blocks.size()-1; i >=rollback_height; i--)
{
bool r = pop_block_from_blockchain();
transactions_map ot;
bool r = pop_block_from_blockchain(ot);
CHECK_AND_ASSERT_MES(r, false, "PANIC!!! failed to remove block while chain switching during the rollback!");
}
//return back original chain
BOOST_FOREACH(auto& bl, original_chain)
BOOST_FOREACH(auto& oce, original_chain)
{
block_verification_context bvc = boost::value_initialized<block_verification_context>();
bool r = handle_block_to_main_chain(bl, bvc);
bvc.m_onboard_transactions.swap(oce.onboard_transactions);
bool r = handle_block_to_main_chain(oce.b, bvc);
CHECK_AND_ASSERT_MES(r && bvc.m_added_to_main_chain, false, "PANIC!!! failed to add (again) block while chain switching during the rollback!");
}
@ -940,13 +948,15 @@ bool blockchain_storage::switch_to_alternative_blockchain(alt_chain_type& alt_ch
);
//disconnecting old chain
std::list<block> disconnected_chain;
std::list<block_ws_txs> disconnected_chain;
for(size_t i = m_db_blocks.size()-1; i >=split_height; i--)
{
block b = m_db_blocks[i]->bl;
bool r = pop_block_from_blockchain();
CHECK_AND_ASSERT_MES(r, false, "failed to remove block " << get_block_hash(b) << " @ " << get_block_height(b) << " on chain switching");
disconnected_chain.push_front(b);
disconnected_chain.push_front(block_ws_txs());
block_ws_txs& bwt = disconnected_chain.front();
bwt.b = m_db_blocks[i]->bl;
bool r = pop_block_from_blockchain(bwt.onboard_transactions);
CHECK_AND_ASSERT_MES(r, false, "failed to remove block " << get_block_hash(bwt.b) << " @ " << get_block_height(bwt.b) << " on chain switching");
CHECK_AND_ASSERT_MES(validate_blockchain_prev_links(), false, "EPIC FAIL!");
}
@ -955,6 +965,7 @@ bool blockchain_storage::switch_to_alternative_blockchain(alt_chain_type& alt_ch
{
auto ch_ent = *alt_ch_iter;
block_verification_context bvc = boost::value_initialized<block_verification_context>();
bvc.m_onboard_transactions = ch_ent->second.onboard_transactions;
bool r = handle_block_to_main_chain(ch_ent->second.bl, bvc);
if(!r || !bvc.m_added_to_main_chain)
{
@ -976,7 +987,8 @@ bool blockchain_storage::switch_to_alternative_blockchain(alt_chain_type& alt_ch
for(auto& old_ch_ent : disconnected_chain)
{
block_verification_context bvc = boost::value_initialized<block_verification_context>();
bool r = handle_alternative_block(old_ch_ent, get_block_hash(old_ch_ent), bvc);
bvc.m_onboard_transactions.swap(old_ch_ent.onboard_transactions);
bool r = handle_alternative_block(old_ch_ent.b, get_block_hash(old_ch_ent.b), bvc);
if(!r)
{
LOG_ERROR("Failed to push ex-main chain blocks to alternative chain ");
@ -1285,6 +1297,35 @@ bool blockchain_storage::create_block_template(block& b,
const pos_entry& pe,
fill_block_template_func_t custom_fill_block_template_func /* = nullptr */) const
{
create_block_template_params params = AUTO_VAL_INIT(params);
params.miner_address = miner_address;
params.stakeholder_address = stakeholder_address;
params.ex_nonce = ex_nonce;
params.pos = pos;
params.pe = pe;
params.pcustom_fill_block_template_func = custom_fill_block_template_func;
create_block_template_response resp = AUTO_VAL_INIT(resp);
bool r = create_block_template(params, resp);
b = resp.b;
diffic = resp.diffic;
height = resp.height;
return r;
}
bool blockchain_storage::create_block_template(const create_block_template_params& params, create_block_template_response& resp) const
{
const account_public_address& miner_address = params.miner_address;
const account_public_address& stakeholder_address = params.stakeholder_address;
const blobdata& ex_nonce = params.ex_nonce;
bool pos = params.pos;
const pos_entry& pe = params.pe;
fill_block_template_func_t* pcustom_fill_block_template_func = params.pcustom_fill_block_template_func;
uint64_t& height = resp.height;
block& b = resp.b;
wide_difficulty_type& diffic = resp.diffic;
size_t median_size;
boost::multiprecision::uint128_t already_generated_coins;
CRITICAL_REGION_BEGIN(m_read_lock);
@ -1317,13 +1358,13 @@ bool blockchain_storage::create_block_template(block& b,
CRITICAL_REGION_END();
size_t txs_size;
uint64_t fee;
size_t txs_size = 0;
uint64_t fee = 0;
bool block_filled = false;
if (custom_fill_block_template_func == nullptr)
block_filled = m_tx_pool.fill_block_template(b, pos, median_size, already_generated_coins, txs_size, fee, height);
if (pcustom_fill_block_template_func == nullptr)
block_filled = m_tx_pool.fill_block_template(b, pos, median_size, already_generated_coins, txs_size, fee, height, params.explicit_txs);
else
block_filled = (*custom_fill_block_template_func)(b, pos, median_size, already_generated_coins, txs_size, fee, height);
block_filled = (*pcustom_fill_block_template_func)(b, pos, median_size, already_generated_coins, txs_size, fee, height);
if (!block_filled)
return false;
@ -1571,6 +1612,7 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto::
alt_block_extended_info abei = AUTO_VAL_INIT(abei);
abei.bl = b;
abei.onboard_transactions.swap(bvc.m_onboard_transactions);
abei.timestamp = m_core_runtime_config.get_core_time();
abei.height = alt_chain.size() ? it_prev->second.height + 1 : *ptr_main_prev + 1;
CHECK_AND_ASSERT_MES_CUSTOM(coinbase_height == abei.height, false, bvc.m_verification_failed = true, "block coinbase height doesn't match with altchain height, declined");
@ -1685,7 +1727,7 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto::
alt_chain.push_back(i_res.first);
//check if difficulty bigger then in main chain
bvc.height_difference = get_top_block_height() >= abei.height ? get_top_block_height() - abei.height : 0;
bvc.m_height_difference = get_top_block_height() >= abei.height ? get_top_block_height() - abei.height : 0;
crypto::hash proof = null_hash;
std::stringstream ss_pow_pos_info;
@ -1722,7 +1764,7 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto::
bvc.m_verification_failed = true;
return r;
}
bvc.added_to_altchain = true;
bvc.m_added_to_altchain = true;
//protect ourself from altchains container flood
if (m_alternative_chains.size() > m_core_runtime_config.max_alt_blocks)
@ -4732,6 +4774,18 @@ wide_difficulty_type blockchain_storage::get_last_alt_x_block_cumulative_precise
return 0;
}
//------------------------------------------------------------------
bool get_tx_from_cache(const crypto::hash& tx_id, transactions_map& tx_cache, transaction& tx, size_t& blob_size, uint64_t& fee)
{
auto it = tx_cache.find(tx_id);
if (it == tx_cache.end())
return false;
tx = it->second;
blob_size = get_object_blobsize(tx);
fee = get_tx_fee(tx);
return true;
}
//------------------------------------------------------------------
bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypto::hash& id, block_verification_context& bvc)
{
TIME_MEASURE_START_PD_MS(block_processing_time_0_ms);
@ -4846,13 +4900,17 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
size_t tx_processed_count = 0;
uint64_t fee_summary = 0;
uint64_t burned_coins = 0;
std::list<crypto::key_image> block_summary_kimages;
for(const crypto::hash& tx_id : bl.tx_hashes)
{
transaction tx;
size_t blob_size = 0;
uint64_t fee = 0;
if(!m_tx_pool.take_tx(tx_id, tx, blob_size, fee))
bool taken_from_cache = get_tx_from_cache(tx_id, bvc.m_onboard_transactions, tx, blob_size, fee);
bool taken_from_pool = m_tx_pool.take_tx(tx_id, tx, blob_size, fee);
if(!taken_from_cache && !taken_from_pool)
{
LOG_PRINT_L0("Block with id: " << id << " has at least one unknown transaction with id: " << tx_id);
purge_block_data_from_blockchain(bl, tx_processed_count);
@ -4861,6 +4919,15 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
return false;
}
if (!validate_tx_semantic(tx, blob_size))
{
LOG_PRINT_L0("Block with id: " << id << " has at least one transaction with wrong semantic, tx_id: " << tx_id);
purge_block_data_from_blockchain(bl, tx_processed_count);
//add_block_as_invalid(bl, id);
bvc.m_verification_failed = true;
return false;
}
append_per_block_increments_for_tx(tx, gindices);
//If we under checkpoints, ring signatures should be pruned
@ -4875,9 +4942,12 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
{
LOG_PRINT_L0("Block with id: " << id << " has at least one transaction (id: " << tx_id << ") with wrong inputs.");
currency::tx_verification_context tvc = AUTO_VAL_INIT(tvc);
bool add_res = m_tx_pool.add_tx(tx, tvc, true, true);
m_tx_pool.add_transaction_to_black_list(tx);
CHECK_AND_ASSERT_MES_NO_RET(add_res, "handle_block_to_main_chain: failed to add transaction back to transaction pool");
if (taken_from_pool)
{
bool add_res = m_tx_pool.add_tx(tx, tvc, true, true);
m_tx_pool.add_transaction_to_black_list(tx);
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 because of wrong inputs in transactions");
@ -4895,10 +4965,13 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
if(!add_transaction_from_block(tx, tx_id, id, current_bc_size, actual_timestamp))
{
LOG_PRINT_L0("Block with id: " << id << " failed to add transaction to blockchain storage");
currency::tx_verification_context tvc = AUTO_VAL_INIT(tvc);
bool add_res = m_tx_pool.add_tx(tx, tvc, true, true);
m_tx_pool.add_transaction_to_black_list(tx);
CHECK_AND_ASSERT_MES_NO_RET(add_res, "handle_block_to_main_chain: failed to add transaction back to transaction pool");
if (taken_from_pool)
{
currency::tx_verification_context tvc = AUTO_VAL_INIT(tvc);
bool add_res = m_tx_pool.add_tx(tx, tvc, true, true);
m_tx_pool.add_transaction_to_black_list(tx);
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);
bvc.m_verification_failed = true;
return false;
@ -4911,6 +4984,8 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
++tx_processed_count;
if (fee)
block_fees.push_back(fee);
read_keyimages_from_tx(tx, block_summary_kimages);
}
TIME_MEASURE_FINISH_PD(all_txs_insert_time_5);
@ -4945,8 +5020,6 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
if (is_pos_bl)
bei.stake_hash = proof_hash;
//////////////////////////////////////////////////////////////////////////
//old style cumulative difficulty collecting
@ -5084,17 +5157,17 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
<< "/" << etc_stuff_6
<< "))");
on_block_added(bei, id);
on_block_added(bei, id, block_summary_kimages);
bvc.m_added_to_main_chain = true;
return true;
}
//------------------------------------------------------------------
void blockchain_storage::on_block_added(const block_extended_info& bei, const crypto::hash& id)
void blockchain_storage::on_block_added(const block_extended_info& bei, const crypto::hash& id, const std::list<crypto::key_image>& bsk)
{
update_next_comulative_size_limit();
m_timestamps_median_cache.clear();
m_tx_pool.on_blockchain_inc(bei.height, id);
m_tx_pool.on_blockchain_inc(bei.height, id, bsk);
update_targetdata_cache_on_block_added(bei);
@ -5283,7 +5356,8 @@ bool blockchain_storage::truncate_blockchain(uint64_t to_height)
uint64_t inital_height = get_current_blockchain_size();
while (get_current_blockchain_size() > to_height)
{
pop_block_from_blockchain();
transactions_map ot;
pop_block_from_blockchain(ot);
}
CRITICAL_REGION_LOCAL(m_alternative_chains_lock);
m_alternative_chains.clear();
@ -6003,8 +6077,12 @@ bool blockchain_storage::validate_alt_block_txs(const block& b, const crypto::ha
for (auto tx_id : b.tx_hashes)
{
std::shared_ptr<transaction> tx_ptr;
CHECK_AND_ASSERT_MES(get_transaction_from_pool_or_db(tx_id, tx_ptr, split_height), false, "failed to get alt block tx " << tx_id << " with split_height == " << split_height);
transaction& tx = *tx_ptr;
auto it = abei.onboard_transactions.find(tx_id);
if (it == abei.onboard_transactions.end())
{
CHECK_AND_ASSERT_MES(get_transaction_from_pool_or_db(tx_id, tx_ptr, split_height), false, "failed to get alt block tx " << tx_id << " with split_height == " << split_height);
}
const transaction& tx = it == abei.onboard_transactions.end() ? *tx_ptr : it->second;
CHECK_AND_ASSERT_MES(tx.signatures.size() == tx.vin.size(), false, "invalid tx: tx.signatures.size() == " << tx.signatures.size() << ", tx.vin.size() == " << tx.vin.size());
for (size_t n = 0; n < tx.vin.size(); ++n)
{

View file

@ -156,6 +156,9 @@ namespace currency
//date added to alt chain storage
uint64_t timestamp;
//transactions associated with the block
transactions_map onboard_transactions;
};
typedef std::unordered_map<crypto::hash, alt_block_extended_info> alt_chain_container;
//typedef std::list<alt_chain_container::iterator> alt_chain_type;
@ -240,9 +243,10 @@ namespace currency
wide_difficulty_type get_next_diff_conditional2(bool pos, const alt_chain_type& alt_chain, uint64_t split_height, const alt_block_extended_info& abei) const;
wide_difficulty_type get_cached_next_difficulty(bool pos) const;
typedef bool fill_block_template_func_t(block &bl, bool pos, size_t median_size, const boost::multiprecision::uint128_t& already_generated_coins, size_t &total_size, uint64_t &fee, uint64_t height);
bool create_block_template(block& b, const account_public_address& miner_address, const account_public_address& stakeholder_address, wide_difficulty_type& di, uint64_t& height, const blobdata& ex_nonce, bool pos, const pos_entry& pe, fill_block_template_func_t custom_fill_block_template_func = nullptr) const;
bool create_block_template(block& b, const account_public_address& miner_address, wide_difficulty_type& di, uint64_t& height, const blobdata& ex_nonce) const;
bool create_block_template(const create_block_template_params& params, create_block_template_response& resp) const;
bool have_block(const crypto::hash& id) const;
size_t get_total_transactions()const;
@ -542,10 +546,10 @@ namespace currency
bool switch_to_alternative_blockchain(alt_chain_type& alt_chain);
void purge_alt_block_txs_hashs(const block& b);
void add_alt_block_txs_hashs(const block& b);
bool pop_block_from_blockchain();
bool pop_block_from_blockchain(transactions_map& onboard_transactions);
bool purge_block_data_from_blockchain(const block& b, size_t processed_tx_count);
bool purge_block_data_from_blockchain(const block& b, size_t processed_tx_count, uint64_t& fee);
bool purge_transaction_from_blockchain(const crypto::hash& tx_id, uint64_t& fee);
bool purge_block_data_from_blockchain(const block& b, size_t processed_tx_count, uint64_t& fee, transactions_map& onboard_transactions);
bool purge_transaction_from_blockchain(const crypto::hash& tx_id, uint64_t& fee, transaction& tx);
bool purge_transaction_keyimages_from_blockchain(const transaction& tx, bool strict_check);
wide_difficulty_type get_next_difficulty_for_alternative_chain(const alt_chain_type& alt_chain, block_extended_info& bei, bool pos) const;
bool handle_block_to_main_chain(const block& bl, block_verification_context& bvc);
@ -564,7 +568,7 @@ namespace currency
bool get_transaction_from_pool_or_db(const crypto::hash& tx_id, std::shared_ptr<transaction>& tx_ptr, uint64_t min_allowed_block_height = 0) const;
void get_last_n_x_blocks(uint64_t n, bool pos_blocks, std::list<std::shared_ptr<const block_extended_info>>& blocks) const;
bool prevalidate_miner_transaction(const block& b, uint64_t height, bool pos)const;
bool rollback_blockchain_switching(std::list<block>& original_chain, size_t rollback_height);
bool rollback_blockchain_switching(std::list<block_ws_txs>& original_chain, size_t rollback_height);
bool add_transaction_from_block(const transaction& tx, const crypto::hash& tx_id, const crypto::hash& bl_id, uint64_t bl_height, uint64_t timestamp);
bool push_transaction_to_global_outs_index(const transaction& tx, const crypto::hash& tx_id, std::vector<uint64_t>& global_indexes);
bool pop_transaction_from_global_index(const transaction& tx, const crypto::hash& tx_id);
@ -577,7 +581,7 @@ namespace currency
bool check_block_timestamp(std::vector<uint64_t> timestamps, const block& b)const;
std::vector<uint64_t> get_last_n_blocks_timestamps(size_t n)const;
const std::vector<txin_etc_details_v>& get_txin_etc_options(const txin_v& in)const;
void on_block_added(const block_extended_info& bei, const crypto::hash& id);
void on_block_added(const block_extended_info& bei, const crypto::hash& id, const std::list<crypto::key_image>& bsk);
void on_block_removed(const block_extended_info& bei);
void update_targetdata_cache_on_block_added(const block_extended_info& bei);
void update_targetdata_cache_on_block_removed(const block_extended_info& bei);

View file

@ -6,6 +6,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#pragma once
#include <unordered_map>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp>
@ -17,7 +18,7 @@
#include "currency_basic.h"
#include "difficulty.h"
#include "currency_protocol/blobdatatype.h"
namespace currency
{
@ -125,6 +126,32 @@ namespace currency
}
};
typedef bool fill_block_template_func_t(block &bl, bool pos, size_t median_size, const boost::multiprecision::uint128_t& already_generated_coins, size_t &total_size, uint64_t &fee, uint64_t height);
struct create_block_template_params
{
account_public_address miner_address;
account_public_address stakeholder_address;
blobdata ex_nonce;
bool pos = false;
pos_entry pe;
std::list<transaction> explicit_txs;
fill_block_template_func_t *pcustom_fill_block_template_func;
};
struct create_block_template_response
{
block b;
wide_difficulty_type diffic;
uint64_t height;
};
typedef std::unordered_map<crypto::hash, transaction> transactions_map;
struct block_ws_txs
{
block b;
transactions_map onboard_transactions;
};
}

View file

@ -19,6 +19,7 @@ using namespace epee;
#include "currency_format_utils.h"
#include "misc_language.h"
#include "string_coding.h"
#include "tx_semantic_validation.h"
#define MINIMUM_REQUIRED_FREE_SPACE_BYTES (1024 * 1024 * 100)
@ -185,13 +186,15 @@ namespace currency
//-----------------------------------------------------------------------------------------------
bool core::handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool kept_by_block)
{
CHECK_AND_ASSERT_MES(!kept_by_block, false, "Transaction associated with block came throw handle_incoming_tx!(not allowed anymore)");
tvc = boost::value_initialized<tx_verification_context>();
//want to process all transactions sequentially
TIME_MEASURE_START_MS(wait_lock_time);
CRITICAL_REGION_LOCAL(m_incoming_tx_lock);
TIME_MEASURE_FINISH_MS(wait_lock_time);
if(tx_blob.size() > get_max_tx_size())
if(tx_blob.size() > CURRENCY_MAX_TRANSACTION_BLOB_SIZE)
{
LOG_PRINT_L0("WRONG TRANSACTION BLOB, too big size " << tx_blob.size() << ", rejected");
tvc.m_verification_failed = true;
@ -210,19 +213,10 @@ namespace currency
TIME_MEASURE_FINISH_MS(parse_tx_time);
TIME_MEASURE_START_MS(check_tx_syntax_time);
if(!check_tx_syntax(tx))
{
LOG_PRINT_L0("WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " syntax, rejected");
tvc.m_verification_failed = true;
return false;
}
TIME_MEASURE_FINISH_MS(check_tx_syntax_time);
TIME_MEASURE_START_MS(check_tx_semantic_time);
if(!check_tx_semantic(tx, kept_by_block))
if(!validate_tx_semantic(tx, tx_blob.size()))
{
LOG_PRINT_L0("WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " semantic, rejected");
LOG_PRINT_L0("WRONG TRANSACTION SEMANTICS, Failed to check tx " << tx_hash << " semantic, rejected");
tvc.m_verification_failed = true;
return false;
}
@ -243,7 +237,6 @@ namespace currency
}
LOG_PRINT_L2("[CORE HANDLE_INCOMING_TX]: timing " << wait_lock_time
<< "/" << parse_tx_time
<< "/" << check_tx_syntax_time
<< "/" << check_tx_semantic_time
<< "/" << add_new_tx_time);
return r;
@ -296,88 +289,9 @@ namespace currency
return true;
}
//-----------------------------------------------------------------------------------------------
bool core::check_tx_semantic(const transaction& tx, bool kept_by_block)
{
if(!tx.vin.size())
{
LOG_PRINT_RED_L0("tx with empty inputs, rejected for tx id= " << get_transaction_hash(tx));
return false;
}
if(!check_inputs_types_supported(tx))
{
LOG_PRINT_RED_L0("unsupported input types for tx id= " << get_transaction_hash(tx));
return false;
}
if(!check_outs_valid(tx))
{
LOG_PRINT_RED_L0("tx with invalid outputs, rejected for tx id= " << get_transaction_hash(tx));
return false;
}
if(!check_money_overflow(tx))
{
LOG_PRINT_RED_L0("tx has money overflow, rejected for tx id= " << get_transaction_hash(tx));
return false;
}
uint64_t amount_in = 0;
get_inputs_money_amount(tx, amount_in);
uint64_t amount_out = get_outs_money_amount(tx);
if(amount_in < amount_out)
{
LOG_PRINT_RED_L0("tx with wrong amounts: ins " << amount_in << ", outs " << amount_out << ", rejected for tx id= " << get_transaction_hash(tx));
return false;
}
if(!kept_by_block && get_object_blobsize(tx) >= m_blockchain_storage.get_current_comulative_blocksize_limit() - CURRENCY_COINBASE_BLOB_RESERVED_SIZE)
{
LOG_PRINT_RED_L0("tx has too big size " << get_object_blobsize(tx) << ", expected no bigger than " << m_blockchain_storage.get_current_comulative_blocksize_limit() - CURRENCY_COINBASE_BLOB_RESERVED_SIZE);
return false;
}
//check if tx use different key images
if(!check_tx_inputs_keyimages_diff(tx))
{
LOG_PRINT_RED_L0("tx inputs have the same key images");
return false;
}
if(!check_tx_extra(tx))
{
LOG_PRINT_RED_L0("tx has wrong extra, rejected");
return false;
}
return true;
}
//-----------------------------------------------------------------------------------------------
bool core::check_tx_extra(const transaction& tx)
{
tx_extra_info ei = AUTO_VAL_INIT(ei);
bool r = parse_and_validate_tx_extra(tx, ei);
if(!r)
return false;
return true;
}
//-----------------------------------------------------------------------------------------------
bool core::check_tx_inputs_keyimages_diff(const transaction& tx)
{
std::unordered_set<crypto::key_image> ki;
BOOST_FOREACH(const auto& in, tx.vin)
{
if (in.type() == typeid(txin_to_key))
{
CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, false);
if (!ki.insert(tokey_in.k_image).second)
return false;
}
}
return true;
}
//-----------------------------------------------------------------------------------------------
bool core::add_new_tx(const transaction& tx, tx_verification_context& tvc, bool kept_by_block)
{
@ -419,6 +333,11 @@ namespace currency
return m_blockchain_storage.create_block_template(b, adr, stakeholder_address, diffic, height, ex_nonce, pos, pe);
}
//-----------------------------------------------------------------------------------------------
bool core::get_block_template(const create_block_template_params& params, create_block_template_response& resp)
{
return m_blockchain_storage.create_block_template(params, resp);
}
//-----------------------------------------------------------------------------------------------
bool core::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const
{
return m_blockchain_storage.find_blockchain_supplement(qblock_ids, resp);
@ -589,7 +508,6 @@ namespace currency
//-----------------------------------------------------------------------------------------------
bool core::handle_incoming_block(const blobdata& block_blob, block_verification_context& bvc, bool update_miner_blocktemplate)
{
bvc = AUTO_VAL_INIT_T(block_verification_context);
block b = AUTO_VAL_INIT(b);
if (!parse_block(block_blob, b, bvc))
{
@ -627,11 +545,6 @@ namespace currency
{
return parse_and_validate_tx_from_blob(blob, tx, tx_hash);
}
//-----------------------------------------------------------------------------------------------
bool core::check_tx_syntax(const transaction& tx)
{
return true;
}
//-----------------------------------------------------------------------------------------------
bool core::get_pool_transactions(std::list<transaction>& txs)
{

View file

@ -53,7 +53,8 @@ namespace currency
//-------------------- i_miner_handler -----------------------
virtual bool handle_block_found(const block& b, block_verification_context* p_verification_result = nullptr);
virtual bool get_block_template(block& b, const account_public_address& adr, const account_public_address& stakeholder_address, wide_difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce, bool pos = false, const pos_entry& pe = pos_entry());
virtual bool get_block_template(const create_block_template_params& params, create_block_template_response& resp);
bool get_block_template(block& b, const account_public_address& adr, const account_public_address& stakeholder_address, wide_difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce, bool pos = false, const pos_entry& pe = pos_entry());
miner& get_miner(){ return m_miner; }
static void init_options(boost::program_options::options_description& desc);
@ -118,12 +119,6 @@ namespace currency
bool add_new_block(const block& b, block_verification_context& bvc);
bool load_state_data();
bool parse_tx_from_blob(transaction& tx, crypto::hash& tx_hash, const blobdata& blob);
bool check_tx_extra(const transaction& tx);
bool check_tx_syntax(const transaction& tx);
//check correct values, amounts and all lightweight checks not related with database
bool check_tx_semantic(const transaction& tx, bool kept_by_block);
//check if tx already in memory pool or in main blockchain
bool is_key_image_spent(const crypto::key_image& key_im);
@ -132,7 +127,6 @@ namespace currency
bool update_miner_block_template();
bool handle_command_line(const boost::program_options::variables_map& vm);
bool on_update_blocktemplate_interval();
bool check_tx_inputs_keyimages_diff(const transaction& tx);
void notify_blockchain_update_listeners();

View file

@ -87,7 +87,7 @@ namespace currency
bool pos,
const pos_entry& pe)
{
uint64_t block_reward;
uint64_t block_reward = 0;
if (!get_block_reward(pos, median_size, current_block_size, already_generated_coins, block_reward, height))
{
LOG_ERROR("Block is too big");
@ -857,14 +857,6 @@ namespace currency
tx.extra.push_back(chs);
else
tx.attachment.push_back(chs);
LOG_PRINT_GREEN("ENCRYPTING ATTACHMENTS ON KEY: " << epee::string_tools::pod_to_hex(derivation)
<< " destination addr: " << currency::get_account_address_as_str(destination_addr)
<< " tx_random_key.sec" << tx_random_key.sec
<< " tx_random_key.pub" << tx_random_key.pub
<< " sender address: " << currency::get_account_address_as_str(sender_keys.m_account_address)
, LOG_LEVEL_0);
}
}
//---------------------------------------------------------------
@ -981,6 +973,8 @@ namespace currency
{
CHECK_AND_ASSERT_MES(destinations.size() <= CURRENCY_TX_MAX_ALLOWED_OUTS, false, "Too many outs (" << destinations.size() << ")! Tx can't be constructed.");
bool watch_only_mode = sender_account_keys.m_spend_secret_key == null_skey;
bool append_mode = false;
if (flags&TX_FLAG_SIGNATURE_MODE_SEPARATE && tx.vin.size())
append_mode = true;
@ -1196,10 +1190,12 @@ namespace currency
}
sigs.resize(src_entr.outputs.size());
crypto::generate_ring_signature(tx_hash_for_signature, boost::get<txin_to_key>(tx.vin[input_index]).k_image, keys_ptrs, in_contexts[in_context_index].in_ephemeral.sec, src_entr.real_output, sigs.data());
if (!watch_only_mode)
crypto::generate_ring_signature(tx_hash_for_signature, boost::get<txin_to_key>(tx.vin[input_index]).k_image, keys_ptrs, in_contexts[in_context_index].in_ephemeral.sec, src_entr.real_output, sigs.data());
ss_ring_s << "signatures:" << ENDL;
std::for_each(sigs.begin(), sigs.end(), [&](const crypto::signature& s){ss_ring_s << s << ENDL; });
ss_ring_s << "prefix_hash:" << tx_prefix_hash << ENDL << "in_ephemeral_key: " << in_contexts[in_context_index].in_ephemeral.sec << ENDL << "real_output: " << src_entr.real_output << ENDL;
std::for_each(sigs.begin(), sigs.end(), [&ss_ring_s](const crypto::signature& s) { ss_ring_s << s << ENDL; });
ss_ring_s << "prefix_hash: " << tx_prefix_hash << ENDL << "in_ephemeral_key: " << in_contexts[in_context_index].in_ephemeral.sec << ENDL << "real_output: " << src_entr.real_output << ENDL;
}
else
{
@ -1525,14 +1521,16 @@ namespace currency
bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::key_derivation& derivation, size_t output_index)
{
crypto::public_key pk;
derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk);
if (!derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk))
return false;
return pk == out_key.key;
}
//---------------------------------------------------------------
bool is_out_to_acc(const account_keys& acc, const txout_multisig& out_multisig, const crypto::key_derivation& derivation, size_t output_index)
{
crypto::public_key pk;
derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk);
if (!derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk))
return false;
auto it = std::find(out_multisig.keys.begin(), out_multisig.keys.end(), pk);
if (out_multisig.keys.end() == it)
return false;
@ -1593,7 +1591,8 @@ namespace currency
bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, std::vector<size_t>& outs, uint64_t& money_transfered, crypto::key_derivation& derivation)
{
money_transfered = 0;
generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation);
bool r = generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation);
CHECK_AND_ASSERT_MES(r, false, "unable to generate derivation from tx_pub = " << tx_pub_key << " * view_sec, invalid tx_pub?");
if (is_coinbase(tx) && get_block_height(tx) == 0 && tx_pub_key == ggenesis_tx_pub_key)
{
@ -2431,11 +2430,6 @@ namespace currency
return CURRENCY_MAX_BLOCK_SIZE;
}
//-----------------------------------------------------------------------------------------------
size_t get_max_tx_size()
{
return CURRENCY_MAX_TRANSACTION_BLOB_SIZE;
}
//-----------------------------------------------------------------------------------------------
uint64_t get_base_block_reward(bool is_pos, const boost::multiprecision::uint128_t& already_generated_coins, uint64_t height)
{
if (!height)
@ -2480,7 +2474,7 @@ namespace currency
div128_32(product_hi, product_lo, static_cast<uint32_t>(median_size), &reward_hi, &reward_lo);
div128_32(reward_hi, reward_lo, static_cast<uint32_t>(median_size), &reward_hi, &reward_lo);
CHECK_AND_ASSERT_MES(0 == reward_hi, false, "0 == reward_hi");
CHECK_AND_ASSERT_MES(reward_lo < base_reward, false, "reward_lo < base_reward, reward: " << reward << ", base_reward: " << base_reward << ", current_block_size: " << current_block_size << ", median_size: " << median_size);
CHECK_AND_ASSERT_MES(reward_lo < base_reward, false, "reward_lo < base_reward, reward_lo: " << reward_lo << ", base_reward: " << base_reward << ", current_block_size: " << current_block_size << ", median_size: " << median_size);
reward = reward_lo;
return true;

View file

@ -193,6 +193,16 @@ namespace currency
return get_object_blobsize(t, tx_blob_size);
}
//---------------------------------------------------------------
size_t get_objects_blobsize(const std::list<transaction>& ls)
{
size_t total = 0;
for (const auto& tx : ls)
{
total += get_object_blobsize(tx);
}
return total;
}
//---------------------------------------------------------------
size_t get_object_blobsize(const transaction& t, uint64_t prefix_blob_size)
{
size_t tx_blob_size = prefix_blob_size;
@ -246,5 +256,20 @@ namespace currency
{
return t_serializable_object_to_blob(tx, b_blob);
}
//---------------------------------------------------------------
bool read_keyimages_from_tx(const transaction& tx, std::list<crypto::key_image>& kil)
{
std::unordered_set<crypto::key_image> ki;
BOOST_FOREACH(const auto& in, tx.vin)
{
if (in.type() == typeid(txin_to_key))
{
CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, false);
if (!ki.insert(tokey_in.k_image).second)
return false;
}
}
return true;
}
}

View file

@ -92,7 +92,7 @@ namespace currency
inline void set_tx_flags(transaction& tx, uint64_t v) { set_tx_x_detail<etc_tx_details_flags>(tx, v); }
inline void set_tx_expiration_time(transaction& tx, uint64_t v) { set_tx_x_detail<etc_tx_details_expiration_time>(tx, v); }
account_public_address get_crypt_address_from_destinations(const account_keys& sender_account_keys, const std::vector<tx_destination_entry>& destinations);
//-----------------------------------------------------------------------------------------------
bool is_tx_expired(const transaction& tx, uint64_t expiration_ts_median);
uint64_t get_burned_amount(const transaction& tx);
@ -104,7 +104,11 @@ namespace currency
bool get_transaction_hash(const transaction& t, crypto::hash& res);
bool get_transaction_hash(const transaction& t, crypto::hash& res, uint64_t& blob_size);
size_t get_object_blobsize(const transaction& t);
size_t get_objects_blobsize(const std::list<transaction>& ls);
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);
bool read_keyimages_from_tx(const transaction& tx, std::list<crypto::key_image>& kil);
}

View file

@ -56,13 +56,16 @@ namespace currency
m_current_hash_rate(0),
m_last_hr_merge_time(0),
m_hashes(0),
m_config(AUTO_VAL_INIT(m_config))
m_config(AUTO_VAL_INIT(m_config)),
m_mine_address{}
{
}
//-----------------------------------------------------------------------------------------------------
miner::~miner()
{
TRY_ENTRY();
stop();
CATCH_ENTRY_NO_RETURN();
}
//-----------------------------------------------------------------------------------------------------
bool miner::set_block_template(const block& bl, const wide_difficulty_type& di, uint64_t height)

View file

@ -24,6 +24,7 @@ namespace currency
struct i_miner_handler
{
virtual bool handle_block_found(const block& b, block_verification_context* p_verification_result = nullptr) = 0;
virtual bool get_block_template(const create_block_template_params& params, create_block_template_response& resp) = 0;
virtual bool get_block_template(block& b, const account_public_address& adr, const account_public_address& stakeholder_address, wide_difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce, bool pos = false, const pos_entry& pe = pos_entry()) = 0;
protected:
~i_miner_handler(){};

View file

@ -50,7 +50,7 @@ namespace currency
m_db_transactions(m_db),
m_db_black_tx_list(m_db),
m_db_solo_options(m_db),
m_db_key_images_set(m_db),
// m_db_key_images_set(m_db),
m_db_alias_names(m_db),
m_db_alias_addresses(m_db),
m_db_storage_major_compatibility_version(TRANSACTION_POOL_OPTIONS_ID_STORAGE_MAJOR_COMPATIBILITY_VERSION, m_db_solo_options)
@ -255,7 +255,7 @@ namespace currency
td.receive_time = get_core_time();
m_db_transactions.set(id, td);
on_tx_add(tx, kept_by_block);
on_tx_add(id, tx, kept_by_block);
TIME_MEASURE_FINISH_PD(update_db_time);
return true;
@ -385,7 +385,7 @@ namespace currency
blob_size = txe_tr->blob_size;
fee = txe_tr->fee;
m_db_transactions.erase(id);
on_tx_remove(tx, txe_tr->kept_by_block);
on_tx_remove(id, tx, txe_tr->kept_by_block);
set_taken(id);
return true;
}
@ -472,7 +472,7 @@ namespace currency
for (auto& e : to_delete)
{
m_db_transactions.erase(e.hash);
on_tx_remove(e.tx, e.kept_by_block);
on_tx_remove(e.hash, e.tx, e.kept_by_block);
}
@ -613,8 +613,10 @@ namespace currency
return true;
}
//---------------------------------------------------------------------------------
bool tx_memory_pool::on_blockchain_inc(uint64_t new_block_height, const crypto::hash& top_block_id)
bool tx_memory_pool::on_blockchain_inc(uint64_t new_block_height, const crypto::hash& top_block_id, const std::list<crypto::key_image>& bsk)
{
return true;
}
//---------------------------------------------------------------------------------
@ -694,36 +696,33 @@ namespace currency
return false;
}
//---------------------------------------------------------------------------------
bool tx_memory_pool::insert_key_images(const transaction& tx, bool kept_by_block)
bool tx_memory_pool::insert_key_images(const crypto::hash &tx_id, const transaction& tx, bool kept_by_block)
{
CRITICAL_REGION_LOCAL(m_key_images_lock);
for(const auto& in : tx.vin)
{
if (in.type() == typeid(txin_to_key))
{
CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, true);//should never fail
uint64_t count = 0;
auto ki_entry_ptr = m_db_key_images_set.get(tokey_in.k_image);
if (ki_entry_ptr.get())
count = *ki_entry_ptr;
uint64_t count_before = count;
++count;
m_db_key_images_set.set(tokey_in.k_image, count);
LOG_PRINT_L2("tx pool: key image added: " << tokey_in.k_image << ", from tx " << get_transaction_hash(tx) << ", counter: " << count_before << " -> " << count);
const txin_to_key& tokey_in = boost::get<txin_to_key>(in);
auto& id_set = m_key_images[tokey_in.k_image];
size_t sz_before = id_set.size();
id_set.insert(tx_id);
LOG_PRINT_L2("tx pool: key image added: " << tokey_in.k_image << ", from tx " << tx_id << ", counter: " << sz_before << " -> " << id_set.size());
}
}
return false;
}
//---------------------------------------------------------------------------------
bool tx_memory_pool::on_tx_add(const transaction& tx, bool kept_by_block)
bool tx_memory_pool::on_tx_add(crypto::hash tx_id, const transaction& tx, bool kept_by_block)
{
insert_key_images(tx, kept_by_block);
insert_key_images(tx_id, tx, kept_by_block);
insert_alias_info(tx);
return true;
}
//---------------------------------------------------------------------------------
bool tx_memory_pool::on_tx_remove(const transaction& tx, bool kept_by_block)
bool tx_memory_pool::on_tx_remove(const crypto::hash &id, const transaction& tx, bool kept_by_block)
{
remove_key_images(tx, kept_by_block);
remove_key_images(id, tx, kept_by_block);
remove_alias_info(tx);
return true;
}
@ -757,34 +756,33 @@ namespace currency
return true;
}
//---------------------------------------------------------------------------------
bool tx_memory_pool::remove_key_images(const transaction& tx, bool kept_by_block)
bool tx_memory_pool::remove_key_images(const crypto::hash &tx_id, const transaction& tx, bool kept_by_block)
{
CRITICAL_REGION_LOCAL(m_key_images_lock);
for(const auto& in : tx.vin)
{
if (in.type() == typeid(txin_to_key))
{
CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, true);//should never fail
uint64_t count = 0;
auto ki_entry_ptr = m_db_key_images_set.get(tokey_in.k_image);
if (!ki_entry_ptr.get() || *ki_entry_ptr == 0)
{
LOG_ERROR("INTERNAL_ERROR: for tx " << get_transaction_hash(tx) << " key image " << tokey_in.k_image << " not found");
continue;
}
count = *ki_entry_ptr;
uint64_t count_before = count;
--count;
if (count)
m_db_key_images_set.set(tokey_in.k_image, count);
else
m_db_key_images_set.erase(tokey_in.k_image);
LOG_PRINT_L2("tx pool: key image removed: " << tokey_in.k_image << ", from tx " << get_transaction_hash(tx) << ", counter: " << count_before << " -> " << count);
{
const txin_to_key& tokey_in = boost::get<txin_to_key>(in);
auto it_map = epee::misc_utils::it_get_or_insert_value_initialized(m_key_images, tokey_in.k_image);
auto& id_set = it_map->second;
size_t count_before = id_set.size();
auto it_set = id_set.find(tx_id);
if(it_set != id_set.end())
id_set.erase(it_set);
size_t count_after = id_set.size();
if (id_set.size() == 0)
m_key_images.erase(it_map);
LOG_PRINT_L2("tx pool: key image removed: " << tokey_in.k_image << ", from tx " << tx_id << ", counter: " << count_before << " -> " << count_after);
}
}
return false;
}
//---------------------------------------------------------------------------------
bool tx_memory_pool::get_key_images_from_tx_pool(std::unordered_set<crypto::key_image>& key_images) const
bool tx_memory_pool::get_key_images_from_tx_pool(key_image_cache& key_images) const
{
m_db_transactions.enumerate_items([&](uint64_t i, const crypto::hash& h, const tx_details &tx_entry)
@ -793,7 +791,7 @@ namespace currency
{
if (in.type() == typeid(txin_to_key))
{
key_images.insert(boost::get<txin_to_key>(in).k_image);
key_images[boost::get<txin_to_key>(in).k_image].insert(h);
}
}
return true;
@ -804,9 +802,9 @@ namespace currency
//---------------------------------------------------------------------------------
bool tx_memory_pool::have_tx_keyimg_as_spent(const crypto::key_image& key_im)const
{
auto ptr = m_db_key_images_set.find(key_im);
if (ptr)
CRITICAL_REGION_LOCAL(m_key_images_lock);
auto it = m_key_images.find(key_im);
if (it != m_key_images.end())
return true;
return false;
}
@ -826,9 +824,9 @@ namespace currency
m_db.begin_transaction();
m_db_transactions.clear();
m_db_key_images_set.clear();
m_db.commit_transaction();
// should m_db_black_tx_list be cleared here?
CIRITCAL_OPERATION(m_key_images,clear());
}
//---------------------------------------------------------------------------------
void tx_memory_pool::clear()
@ -836,8 +834,8 @@ namespace currency
m_db.begin_transaction();
m_db_transactions.clear();
m_db_black_tx_list.clear();
m_db_key_images_set.clear();
m_db.commit_transaction();
CIRITCAL_OPERATION(m_key_images,clear());
}
//---------------------------------------------------------------------------------
bool tx_memory_pool::is_transaction_ready_to_go(tx_details& txd, const crypto::hash& id)const
@ -987,7 +985,9 @@ namespace currency
const boost::multiprecision::uint128_t& already_generated_coins,
size_t &total_size,
uint64_t &fee,
uint64_t height)
uint64_t height,
const std::list<transaction>& explicit_txs
)
{
LOCAL_READONLY_TRANSACTION();
//typedef transactions_container::value_type txv;
@ -998,8 +998,8 @@ namespace currency
txs_v.reserve(m_db_transactions.size());
std::vector<txv*> txs;
//std::transform(m_transactions.begin(), m_transactions.end(), txs.begin(), [](txv &a) -> txv * { return &a; });
//keep getting it as a values cz db items cache will keep it as unserialied object stored by shared ptrs
//keep getting it as a values cz db items cache will keep it as unserialised object stored by shared ptrs
m_db_transactions.enumerate_keys([&](uint64_t i, crypto::hash& k){txs_v.resize(i + 1); txs_v[i].first = k; return true;});
txs.resize(txs_v.size(), nullptr);
@ -1024,7 +1024,9 @@ namespace currency
return a_ > b_;
});
size_t current_size = 0;
size_t explicit_total_size = get_objects_blobsize(explicit_txs);
size_t current_size = explicit_total_size;
uint64_t current_fee = 0;
uint64_t best_money;
if (!get_block_reward(pos, median_size, CURRENCY_COINBASE_BLOB_RESERVED_SIZE, already_generated_coins, best_money, height)) {
@ -1133,6 +1135,12 @@ namespace currency
}
}
}
// add explicit transactions
for (const auto& tx : explicit_txs)
{
fee += get_tx_fee(tx);
bl.tx_hashes.push_back(get_transaction_hash(tx));
}
return true;
}
//---------------------------------------------------------------------------------
@ -1184,8 +1192,8 @@ namespace currency
res = m_db_transactions.init(TRANSACTION_POOL_CONTAINER_TRANSACTIONS);
CHECK_AND_ASSERT_MES(res, false, "Unable to init db container");
res = m_db_key_images_set.init(TRANSACTION_POOL_CONTAINER_KEY_IMAGES);
CHECK_AND_ASSERT_MES(res, false, "Unable to init db container");
// res = m_db_key_images_set.init(TRANSACTION_POOL_CONTAINER_KEY_IMAGES);
// CHECK_AND_ASSERT_MES(res, false, "Unable to init db container");
res = m_db_black_tx_list.init(TRANSACTION_POOL_CONTAINER_BLACK_TX_LIST);
CHECK_AND_ASSERT_MES(res, false, "Unable to init db container");
res = m_db_alias_names.init(TRANSACTION_POOL_CONTAINER_ALIAS_NAMES);
@ -1211,7 +1219,7 @@ namespace currency
{
LOG_PRINT_L1("DB at " << db_folder_path << " is about to be deleted and re-created...");
m_db_transactions.deinit();
m_db_key_images_set.deinit();
// m_db_key_images_set.deinit();
m_db_black_tx_list.deinit();
m_db_alias_names.deinit();
m_db_alias_addresses.deinit();
@ -1243,9 +1251,17 @@ namespace currency
});
LOG_PRINT_L2(ss.str());
}
load_keyimages_cache();
return true;
}
//---------------------------------------------------------------------------------
bool tx_memory_pool::load_keyimages_cache()
{
CRITICAL_REGION_LOCAL(m_key_images_lock);
return get_key_images_from_tx_pool(m_key_images);
}
//---------------------------------------------------------------------------------
bool tx_memory_pool::deinit()
{

View file

@ -81,6 +81,7 @@ namespace currency
epee::math_helper::average<uint64_t, 5> db_commit_time;
};
typedef std::unordered_map<crypto::key_image, std::set<crypto::hash>> key_image_cache;
tx_memory_pool(blockchain_storage& bchs, i_currency_protocol* pprotocol);
bool add_tx(const transaction &tx, const crypto::hash &id, uint64_t blob_size, tx_verification_context& tvc, bool kept_by_block, bool from_core = false);
@ -99,7 +100,7 @@ namespace currency
bool check_tx_multisig_ins_and_outs(const transaction& tx, bool check_against_pool_txs)const;
bool on_blockchain_inc(uint64_t new_block_height, const crypto::hash& top_block_id);
bool on_blockchain_inc(uint64_t new_block_height, const crypto::hash& top_block_id, const std::list<crypto::key_image>& bsk);
bool on_blockchain_dec(uint64_t new_block_height, const crypto::hash& top_block_id);
bool on_finalize_db_transaction();
bool add_transaction_to_black_list(const transaction& tx);
@ -117,7 +118,7 @@ namespace currency
// load/store operations
bool init(const std::string& config_folder, const boost::program_options::variables_map& vm);
bool deinit();
bool fill_block_template(block &bl, bool pos, size_t median_size, const boost::multiprecision::uint128_t& already_generated_coins, size_t &total_size, uint64_t &fee, uint64_t height);
bool fill_block_template(block &bl, bool pos, size_t median_size, const boost::multiprecision::uint128_t& already_generated_coins, size_t &total_size, uint64_t &fee, uint64_t height, const std::list<transaction>& explicit_txs);
bool get_transactions(std::list<transaction>& txs) const;
bool get_all_transactions_details(std::list<tx_rpc_extended_info>& txs)const;
bool get_all_transactions_brief_details(std::list<tx_rpc_brief_info>& txs)const;
@ -139,10 +140,10 @@ namespace currency
bool remove_stuck_transactions(); // made public to be called from coretests
private:
bool on_tx_add(const transaction& tx, bool kept_by_block);
bool on_tx_remove(const transaction& tx, bool kept_by_block);
bool insert_key_images(const transaction& tx, bool kept_by_block);
bool remove_key_images(const transaction& tx, bool kept_by_block);
bool on_tx_add(crypto::hash tx_id, const transaction& tx, bool kept_by_block);
bool on_tx_remove(const crypto::hash &tx_id, const transaction& tx, bool kept_by_block);
bool insert_key_images(const crypto::hash& tx_id, const transaction& tx, bool kept_by_block);
bool remove_key_images(const crypto::hash &tx_id, const transaction& tx, bool kept_by_block);
bool insert_alias_info(const transaction& tx);
bool remove_alias_info(const transaction& tx);
@ -150,16 +151,15 @@ namespace currency
void store_db_solo_options_values();
bool is_transaction_ready_to_go(tx_details& txd, const crypto::hash& id)const;
bool validate_alias_info(const transaction& tx, bool is_in_block)const;
bool get_key_images_from_tx_pool(std::unordered_set<crypto::key_image>& key_images)const;
//bool push_alias_info(const transaction& tx);
//bool pop_alias_info(const transaction& tx);
bool get_key_images_from_tx_pool(key_image_cache& key_images) const;
bool check_is_taken(const crypto::hash& id) const;
void set_taken(const crypto::hash& id);
void reset_all_taken();
bool load_keyimages_cache();
typedef tools::db::cached_key_value_accessor<crypto::hash, tx_details, true, false> transactions_container;
typedef tools::db::cached_key_value_accessor<crypto::hash, bool, false, false> hash_container;
typedef tools::db::cached_key_value_accessor<crypto::key_image, uint64_t, false, false> key_images_container;
//typedef tools::db::cached_key_value_accessor<crypto::key_image, uint64_t, false, false> key_images_container;
typedef tools::db::cached_key_value_accessor<uint64_t, uint64_t, false, true> solo_options_container;
typedef tools::db::cached_key_value_accessor<std::string, bool, false, false> aliases_container;
typedef tools::db::cached_key_value_accessor<account_public_address, bool, false, false> address_to_aliases_container;
@ -172,7 +172,7 @@ namespace currency
transactions_container m_db_transactions;
hash_container m_db_black_tx_list;
key_images_container m_db_key_images_set;
//key_images_container m_db_key_images_set;
aliases_container m_db_alias_names;
address_to_aliases_container m_db_alias_addresses;
solo_options_container m_db_solo_options;
@ -189,6 +189,9 @@ namespace currency
//in memory containers
mutable epee::critical_section m_taken_txs_lock;
std::unordered_set<crypto::hash> m_taken_txs;
mutable epee::critical_section m_key_images_lock;
key_image_cache m_key_images;
mutable epee::critical_section m_remove_stuck_txs_lock;

View file

@ -0,0 +1,94 @@
// Copyright (c) 2018-2019 Zano Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "tx_semantic_validation.h"
#include "currency_format_utils.h"
namespace currency
{
//-----------------------------------------------------------------------------------------------
bool check_tx_extra(const transaction& tx)
{
tx_extra_info ei = AUTO_VAL_INIT(ei);
bool r = parse_and_validate_tx_extra(tx, ei);
if (!r)
return false;
return true;
}
//-----------------------------------------------------------------------------------------------
bool check_tx_inputs_keyimages_diff(const transaction& tx)
{
std::unordered_set<crypto::key_image> ki;
BOOST_FOREACH(const auto& in, tx.vin)
{
if (in.type() == typeid(txin_to_key))
{
CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, false);
if (!ki.insert(tokey_in.k_image).second)
return false;
}
}
return true;
}
//-----------------------------------------------------------------------------------------------
bool validate_tx_semantic(const transaction& tx, size_t tx_block_size)
{
if (!tx.vin.size())
{
LOG_PRINT_RED_L0("tx with empty inputs, rejected for tx id= " << get_transaction_hash(tx));
return false;
}
if (!check_inputs_types_supported(tx))
{
LOG_PRINT_RED_L0("unsupported input types for tx id= " << get_transaction_hash(tx));
return false;
}
if (!check_outs_valid(tx))
{
LOG_PRINT_RED_L0("tx with invalid outputs, rejected for tx id= " << get_transaction_hash(tx));
return false;
}
if (!check_money_overflow(tx))
{
LOG_PRINT_RED_L0("tx has money overflow, rejected for tx id= " << get_transaction_hash(tx));
return false;
}
uint64_t amount_in = 0;
get_inputs_money_amount(tx, amount_in);
uint64_t amount_out = get_outs_money_amount(tx);
if (amount_in < amount_out)
{
LOG_PRINT_RED_L0("tx with wrong amounts: ins " << amount_in << ", outs " << amount_out << ", rejected for tx id= " << get_transaction_hash(tx));
return false;
}
if (tx_block_size >= CURRENCY_MAX_TRANSACTION_BLOB_SIZE)
{
LOG_PRINT_RED_L0("tx has too big size " << tx_block_size << ", expected no bigger than " << CURRENCY_BLOCK_GRANTED_FULL_REWARD_ZONE);
return false;
}
//check if tx use different key images
if (!check_tx_inputs_keyimages_diff(tx))
{
LOG_PRINT_RED_L0("tx inputs have the same key images");
return false;
}
if (!check_tx_extra(tx))
{
LOG_PRINT_RED_L0("tx has wrong extra, rejected");
return false;
}
return true;
}
}

View file

@ -0,0 +1,16 @@
// Copyright (c) 2018-2019 Zano Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#pragma once
#include "include_base_utils.h"
#include "currency_format_utils_transactions.h"
namespace currency
{
//check correct values, amounts and all lightweight checks not related with database
bool validate_tx_semantic(const transaction& tx, size_t tx_block_size);
}

View file

@ -25,7 +25,11 @@ namespace currency
bool m_verification_failed; //bad block, should drop connection
bool m_marked_as_orphaned;
bool m_already_exists;
bool added_to_altchain;
uint64_t height_difference;
bool m_added_to_altchain;
uint64_t m_height_difference;
//this is work like a first-level cache for transactions while block is getting handled. It lets transactions
//associated with the block to get handled directly to core without being handled by tx_pool(which makes full
//inputs validation, including signatures check)
transactions_map m_onboard_transactions;
};
}

View file

@ -305,14 +305,22 @@ namespace currency
//now actually process block
for(auto tx_blob_it = arg.b.txs.begin(); tx_blob_it!=arg.b.txs.end();tx_blob_it++)
{
currency::tx_verification_context tvc = AUTO_VAL_INIT(tvc);
m_core.handle_incoming_tx(*tx_blob_it, tvc, true);
if(tvc.m_verification_failed)
if (tx_blob_it->size() > CURRENCY_MAX_TRANSACTION_BLOB_SIZE)
{
LOG_PRINT_L0("Block verification failed: transaction verification failed, dropping connection");
LOG_ERROR("WRONG TRANSACTION BLOB, too big size " << tx_blob_it->size() << ", rejected");
m_p2p->drop_connection(context);
return 1;
}
crypto::hash tx_hash = null_hash;
transaction tx;
if (!parse_and_validate_tx_from_blob(*tx_blob_it, tx, tx_hash))
{
LOG_ERROR("WRONG TRANSACTION BLOB, Failed to parse, rejected");
m_p2p->drop_connection(context);
return 1;
}
bvc.m_onboard_transactions[tx_hash] = tx;
}
m_core.pause_mine();
@ -327,10 +335,10 @@ namespace currency
LOG_PRINT_GREEN("[HANDLE]NOTIFY_NEW_BLOCK EXTRA " << block_id
<< " bvc.m_added_to_main_chain=" << bvc.m_added_to_main_chain
//<< ", prevalidate_result=" << prevalidate_relayed
<< ", bvc.added_to_altchain=" << bvc.added_to_altchain
<< ", bvc.added_to_altchain=" << bvc.m_added_to_altchain
<< ", bvc.m_marked_as_orphaned=" << bvc.m_marked_as_orphaned, LOG_LEVEL_2);
if (bvc.m_added_to_main_chain || (bvc.added_to_altchain && bvc.height_difference < 2))
if (bvc.m_added_to_main_chain || (bvc.m_added_to_altchain && bvc.m_height_difference < 2))
{
if (true/*!prevalidate_relayed*/)
{
@ -521,27 +529,36 @@ namespace currency
{
CHECK_STOP_FLAG__DROP_AND_RETURN_IF_SET(1, "Blocks processing interrupted, connection dropped");
block_verification_context bvc = boost::value_initialized<block_verification_context>();
//process transactions
TIME_MEASURE_START(transactions_process_time);
for (const auto& tx_blob : block_entry.txs)
{
CHECK_STOP_FLAG__DROP_AND_RETURN_IF_SET(1, "Block txs processing interrupted, connection dropped");
tx_verification_context tvc = AUTO_VAL_INIT(tvc);
m_core.handle_incoming_tx(tx_blob, tvc, true);
if(tvc.m_verification_failed)
crypto::hash tx_id = null_hash;
transaction tx = AUTO_VAL_INIT(tx);
if (!parse_and_validate_tx_from_blob(tx_blob, tx, tx_id))
{
LOG_ERROR_CCONTEXT("transaction verification failed on NOTIFY_RESPONSE_GET_OBJECTS, \r\ntx_id = "
LOG_ERROR_CCONTEXT("failed to parse tx: "
<< string_tools::pod_to_hex(get_blob_hash(tx_blob)) << ", dropping connection");
m_p2p->drop_connection(context);
return 1;
}
bvc.m_onboard_transactions[tx_id] = tx;
// tx_verification_context tvc = AUTO_VAL_INIT(tvc);
// m_core.handle_incoming_tx(tx_blob, tvc, true);
// if(tvc.m_verification_failed)
// {
// LOG_ERROR_CCONTEXT("transaction verification failed on NOTIFY_RESPONSE_GET_OBJECTS, \r\ntx_id = "
// << string_tools::pod_to_hex(get_blob_hash(tx_blob)) << ", dropping connection");
// m_p2p->drop_connection(context);
// return 1;
// }
}
TIME_MEASURE_FINISH(transactions_process_time);
//process block
TIME_MEASURE_START(block_process_time);
block_verification_context bvc = boost::value_initialized<block_verification_context>();
m_core.handle_incoming_block(block_entry.block, bvc, false);
if (count > 2 && bvc.m_already_exists)

View file

@ -147,7 +147,6 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_cmd_sett, command_line::arg_disable_stop_if_time_out_of_sync);
command_line::add_arg(desc_cmd_sett, command_line::arg_disable_stop_on_low_free_space);
command_line::add_arg(desc_cmd_sett, command_line::arg_enable_offers_service);
command_line::add_arg(desc_cmd_sett, command_line::arg_db_engine);
arg_market_disable.default_value = true;

View file

@ -109,6 +109,11 @@ namespace tools
return m_rpc.on_submitblock(req, rsp, m_err_stub, m_cntxt_stub);
}
//------------------------------------------------------------------------------------------------------------------------------
bool call_COMMAND_RPC_SUBMITBLOCK2(const currency::COMMAND_RPC_SUBMITBLOCK2::request& req, currency::COMMAND_RPC_SUBMITBLOCK2::response& rsp) override
{
return m_rpc.on_submitblock2(req, rsp, m_err_stub, m_cntxt_stub);
}
//------------------------------------------------------------------------------------------------------------------------------
bool call_COMMAND_RPC_GET_POS_MINING_DETAILS(const currency::COMMAND_RPC_GET_POS_MINING_DETAILS::request& req, currency::COMMAND_RPC_GET_POS_MINING_DETAILS::response& rsp) override
{
return m_rpc.on_get_pos_mining_details(req, rsp, m_cntxt_stub);

View file

@ -120,7 +120,6 @@ bool daemon_backend::init(int argc, char* argv[], view::i_view* pview_handler)
command_line::add_arg(desc_cmd_sett, command_line::arg_log_level);
command_line::add_arg(desc_cmd_sett, command_line::arg_console);
command_line::add_arg(desc_cmd_sett, command_line::arg_show_details);
command_line::add_arg(desc_cmd_sett, command_line::arg_db_engine);
command_line::add_arg(desc_cmd_sett, arg_alloc_win_console);
command_line::add_arg(desc_cmd_sett, arg_html_folder);
command_line::add_arg(desc_cmd_sett, arg_xcode_stub);
@ -656,7 +655,7 @@ std::string daemon_backend::get_my_offers(const bc_services::core_offers_filter&
return API_RETURN_CODE_OK;
}
std::string daemon_backend::open_wallet(const std::wstring& path, const std::string& password, view::open_wallet_response& owr)
std::string daemon_backend::open_wallet(const std::wstring& path, const std::string& password, uint64_t txs_to_return, view::open_wallet_response& owr)
{
std::shared_ptr<tools::wallet2> w(new tools::wallet2());
owr.wallet_id = m_wallet_id_counter++;
@ -678,8 +677,7 @@ std::string daemon_backend::open_wallet(const std::wstring& path, const std::str
try
{
w->load(path, password);
w->get_recent_transfers_history(owr.recent_history.history, 0, 0);
owr.recent_history.total_history_items = w->get_recent_transfers_total_count();
w->get_recent_transfers_history(owr.recent_history.history, 0, txs_to_return, owr.recent_history.total_history_items);
//w->get_unconfirmed_transfers(owr.recent_history.unconfirmed);
w->get_unconfirmed_transfers(owr.recent_history.history);
//workaround for missed fee
@ -717,7 +715,7 @@ std::string daemon_backend::get_recent_transfers(size_t wallet_id, uint64_t offs
return API_RETURN_CODE_CORE_BUSY;
}
w->get()->get_recent_transfers_history(tr_hist.history, offset, count);
w->get()->get_recent_transfers_history(tr_hist.history, offset, count, tr_hist.total_history_items);
//workaround for missed fee
for (auto & he : tr_hist.history)
{

View file

@ -84,7 +84,7 @@ public:
bool start();
bool stop();
bool send_stop_signal();
std::string open_wallet(const std::wstring& path, const std::string& password, view::open_wallet_response& owr);
std::string open_wallet(const std::wstring& path, const std::string& password, uint64_t txs_to_return, view::open_wallet_response& owr);
std::string generate_wallet(const std::wstring& path, const std::string& password, view::open_wallet_response& owr);
std::string restore_wallet(const std::wstring& path, const std::string& password, const std::string& restore_key, view::open_wallet_response& owr);
std::string run_wallet(uint64_t wallet_id);

View file

@ -1602,7 +1602,7 @@ QString MainWindow::open_wallet(const QString& param)
//return que_call2<view::open_wallet_request>("open_wallet", param, [this](const view::open_wallet_request& owd, view::api_response& ar){
PREPARE_ARG_FROM_JSON(view::open_wallet_request, owd);
PREPARE_RESPONSE(view::open_wallet_response, ar);
ar.error_code = m_backend.open_wallet(epee::string_encoding::utf8_to_wstring(owd.path), owd.pass, ar.response_data);
ar.error_code = m_backend.open_wallet(epee::string_encoding::utf8_to_wstring(owd.path), owd.pass, owd.txs_to_return, ar.response_data);
return MAKE_RESPONSE(ar);
CATCH_ENTRY_FAIL_API_RESPONCE();
}
@ -1746,7 +1746,7 @@ QString MainWindow::get_recent_transfers(const QString& param)
LOG_API_TIMING();
PREPARE_ARG_FROM_JSON(view::get_recent_transfers_request, a);
PREPARE_RESPONSE(view::transfers_array, ar);
ar.error_code = m_backend.get_recent_transfers(a.wallet_id, a.offest, a.count, ar.response_data);
ar.error_code = m_backend.get_recent_transfers(a.wallet_id, a.offset, a.count, ar.response_data);
return MAKE_RESPONSE(ar);
CATCH_ENTRY_FAIL_API_RESPONCE();
}

View file

@ -373,22 +373,24 @@ public:
{
std::string pass;
std::string path;
uint64_t txs_to_return;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(pass)
KV_SERIALIZE(path)
KV_SERIALIZE(txs_to_return)
END_KV_SERIALIZE_MAP()
};
struct get_recent_transfers_request
{
uint64_t wallet_id;
uint64_t offest;
uint64_t offset;
uint64_t count;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(wallet_id)
KV_SERIALIZE(offest)
KV_SERIALIZE(offset)
KV_SERIALIZE(count)
END_KV_SERIALIZE_MAP()
};

View file

@ -53,7 +53,8 @@ $themes: (
tooltipCriticalBackgroundColor: #5f1d1d,
tooltipShadow: 0 0 1rem rgba(0, 0, 0, 0.5),
modalBackground: url(~src/assets/images/background-dark.png),
closeButtonColor: #556576
closeButtonColor: #556576,
hoverPage: #3a485a
),
gray: (
bodyBackgroundColor: #101417,
@ -109,7 +110,8 @@ $themes: (
tooltipCriticalBackgroundColor: #4c1919,
tooltipShadow: 0 0 1rem rgba(0, 0, 0, 0.5),
modalBackground: url(~src/assets/images/background-gray.png),
closeButtonColor: #515960
closeButtonColor: #515960,
hoverPage: #383e43
),
white: (
bodyBackgroundColor: #eeeeee,
@ -165,7 +167,8 @@ $themes: (
tooltipCriticalBackgroundColor: #e53935,
tooltipShadow: 0 0 1rem rgba(120, 120, 120, 0.5),
modalBackground: url(~src/assets/images/background-white.png),
closeButtonColor: #43454b
closeButtonColor: #43454b,
hoverPage: #ffffff
)
);

View file

@ -92,6 +92,41 @@ app-wallet {
background-color: themed(contentBackgroundColor);
}
}
.pagination-wrapper {
@include themify($themes) {
background-color: themed(contentBackgroundColor);
}
.pagination {
@include themify($themes) {
border-top: 0.2rem solid themed(transparentButtonBorderColor);
}
button {
@include themify($themes) {
background-color: themed(transparentButtonBorderColor);
color: themed(mainTextColor);
}
&.active {
@include themify($themes) {
background-color: themed(tableBackgroundColor);
color: themed(mainTextColor);
}
}
&:hover {
@include themify($themes) {
background-color: themed(hoverPage);
color: themed(mainTextColor);
}
}
}
}
}
}
}

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\Admin\Desktop\zano\src\gui\qt-daemon\html_source\src\polyfills.ts */"./src/polyfills.ts");
module.exports = __webpack_require__(/*! C:\Users\Admin\Desktop\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__(/*! c:\Users\Admin\Desktop\zano\src\gui\qt-daemon\html_source\src\polyfills.ts */"./src/polyfills.ts");
module.exports = __webpack_require__(/*! c:\Users\Admin\Desktop\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

@ -21,12 +21,17 @@ export class Wallet {
new_contracts?: number;
history: Array<Transaction> = [];
total_history_item?: number;
pages = [];
totalPages: number;
currentPage: number;
excluded_history: Array<Transaction> = [];
contracts: Array<Contract> = [];
progress?: number;
loaded?: boolean;
restore?: boolean;
send_data?: any = {
address: null,

View file

@ -374,10 +374,11 @@ export class BackendService {
this.runCommand('generate_wallet', params, callback);
}
openWallet(path, pass, testEmpty, callback) {
openWallet(path, pass, txs_to_return, testEmpty, callback) {
const params = {
path: path,
pass: pass
pass: pass,
txs_to_return: txs_to_return
};
params['testEmpty'] = !!(testEmpty);
this.runCommand('open_wallet', params, callback);
@ -631,6 +632,15 @@ export class BackendService {
}
}
getRecentTransfers( id, offset, count, callback) {
const params = {
wallet_id: id,
offset: offset,
count: count
};
this.runCommand('get_recent_transfers', params, callback);
}
getPoolInfo(callback) {
this.runCommand('get_tx_pool_info', {}, callback);
}

View file

@ -0,0 +1,16 @@
/* tslint:disable:no-unused-variable */
import { TestBed, async, inject } from '@angular/core/testing';
import { PaginationService } from './pagination.service';
describe('Service: Pagination', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [PaginationService]
});
});
it('should ...', inject([PaginationService], (service: PaginationService) => {
expect(service).toBeTruthy();
}));
});

View file

@ -0,0 +1,47 @@
import { Injectable, NgZone } from '@angular/core';
import { VariablesService } from './variables.service';
@Injectable({
providedIn: 'root'
})
export class PaginationService {
constructor(
private variables: VariablesService,
private ngZone: NgZone
) { }
paginate(currentPage = 1) {
if (currentPage < 1) {
currentPage = 1;
} else if (currentPage > this.variables.currentWallet.totalPages) {
currentPage = this.variables.currentWallet.totalPages;
}
let startPage: number, endPage: number;
if (this.variables.currentWallet.totalPages <= this.variables.maxPages) {
startPage = 1;
endPage = this.variables.currentWallet.totalPages;
} else {
const maxPagesBeforeCurrentPage = Math.floor(this.variables.maxPages / 2);
const maxPagesAfterCurrentPage = Math.ceil(this.variables.maxPages / 2) - 1;
if (currentPage <= maxPagesBeforeCurrentPage) {
startPage = 1;
this.variables.currentWallet.totalPages > this.variables.maxPages
? endPage = this.variables.maxPages
: endPage = this.variables.currentWallet.totalPages
;
} else if (currentPage + maxPagesAfterCurrentPage >= this.variables.currentWallet.totalPages) {
startPage = this.variables.currentWallet.totalPages - this.variables.maxPages + 1;
endPage = this.variables.currentWallet.totalPages;
} else {
startPage = currentPage - maxPagesBeforeCurrentPage;
endPage = currentPage + maxPagesAfterCurrentPage;
}
}
this.ngZone.run(() => {
this.variables.currentWallet.pages = Array.from(Array((endPage + 1) - startPage).keys()).map(i => startPage + i);
});
}
}

View file

@ -45,6 +45,9 @@ export class VariablesService {
wallets: []
};
public count = 40;
public maxPages = 5;
public wallets: Array<Wallet> = [];
public currentWallet: Wallet;
public selectWallet: number;

View file

@ -158,6 +158,17 @@ export class AppComponent implements OnInit, OnDestroy {
wallet.loaded = false;
} else if (wallet.progress === 100) {
wallet.loaded = true;
if (wallet.total_history_item) {
wallet.totalPages = Math.ceil( wallet.total_history_item / this.variablesService.count);
wallet.totalPages > this.variablesService.maxPages
? wallet.pages = new Array(5).fill(1).map((value, index) => value + index)
: wallet.pages = new Array(wallet.totalPages).fill(1).map((value, index) => value + index);
} else if (wallet.restore) {
wallet.totalPages = Math.ceil( wallet.history.length / this.variablesService.count);
wallet.totalPages > this.variablesService.maxPages
? wallet.pages = new Array(5).fill(1).map((value, index) => value + index)
: wallet.pages = new Array(wallet.totalPages).fill(1).map((value, index) => value + index);
}
}
});
}
@ -216,7 +227,6 @@ export class AppComponent implements OnInit, OnDestroy {
const tr_info = data.ti;
const wallet = this.variablesService.getWallet(wallet_id);
if (wallet) {
this.ngZone.run(() => {
@ -236,6 +246,13 @@ export class AppComponent implements OnInit, OnDestroy {
tr_exists = (!tr_exists) ? wallet.history.some(elem => elem.tx_hash === tr_info.tx_hash) : tr_exists;
wallet.prepareHistory([tr_info]);
if (wallet.restore) {
wallet.total_history_item = wallet.history.length;
wallet.totalPages = Math.ceil( wallet.total_history_item / this.variablesService.count);
wallet.totalPages > this.variablesService.maxPages
? wallet.pages = new Array(5).fill(1).map((value, index) => value + index)
: wallet.pages = new Array(wallet.totalPages).fill(1).map((value, index) => value + index);
}
if (tr_info.hasOwnProperty('contract')) {
const exp_med_ts = this.variablesService.exp_med_ts;

View file

@ -78,6 +78,10 @@ export class CreateWalletComponent implements OnInit {
generate_data['wi'].tracking_hey
);
this.variablesService.opening_wallet.alias = this.backend.getWalletAlias(generate_data['wi'].address);
this.variablesService.opening_wallet.total_history_item = 0;
this.variablesService.opening_wallet.pages = new Array(1).fill(1);
this.variablesService.opening_wallet.totalPages = 1;
this.variablesService.opening_wallet.currentPage = 1;
this.ngZone.run(() => {
this.walletSaved = true;
this.progressWidth = '50%';

View file

@ -155,7 +155,7 @@ export class LoginComponent implements OnInit, OnDestroy {
let openWallets = 0;
let runWallets = 0;
walletData.forEach((wallet, wallet_index) => {
this.backend.openWallet(wallet.path, wallet.pass, true, (open_status, open_data, open_error) => {
this.backend.openWallet(wallet.path, wallet.pass, this.variablesService.count, true, (open_status, open_data, open_error) => {
if (open_status || open_error === 'FILE_RESTORED') {
openWallets++;
this.ngZone.run(() => {
@ -177,8 +177,18 @@ export class LoginComponent implements OnInit, OnDestroy {
} else {
new_wallet.staking = false;
}
new_wallet.currentPage = 1;
if (open_data.recent_history && open_data.recent_history.history) {
new_wallet.total_history_item = open_data.recent_history.total_history_items;
new_wallet.totalPages = Math.ceil( open_data.recent_history.total_history_items / this.variablesService.count);
new_wallet.totalPages > this.variablesService.maxPages
? new_wallet.pages = new Array(5).fill(1).map((value, index) => value + index)
: new_wallet.pages = new Array(new_wallet.totalPages).fill(1).map((value, index) => value + index);
new_wallet.prepareHistory(open_data.recent_history.history);
} else {
new_wallet.total_history_item = 0;
new_wallet.pages = new Array(1).fill(1);
new_wallet.totalPages = 1;
}
this.backend.getContracts(open_data.wallet_id, (contracts_status, contracts_data) => {
if (contracts_status && contracts_data.hasOwnProperty('contracts')) {

View file

@ -36,7 +36,7 @@ export class OpenWalletModalComponent implements OnInit {
this.wallet = this.wallets[0];
this.wallet.pass = '';
this.backend.openWallet(this.wallet.path, '', true, (status, data, error) => {
this.backend.openWallet(this.wallet.path, '', this.variablesService.count, true, (status, data, error) => {
if (error === 'FILE_NOT_FOUND') {
this.wallet.notFound = true;
}
@ -54,7 +54,7 @@ export class OpenWalletModalComponent implements OnInit {
if (this.wallets.length === 0) {
return;
}
this.backend.openWallet(this.wallet.path, this.wallet.pass, false, (open_status, open_data, open_error) => {
this.backend.openWallet(this.wallet.path, this.wallet.pass, this.variablesService.count, false, (open_status, open_data, open_error) => {
if (open_error && open_error === 'FILE_NOT_FOUND') {
let error_translate = this.translate.instant('OPEN_WALLET.FILE_NOT_FOUND1');
error_translate += ':<br>' + this.wallet.path;
@ -87,7 +87,16 @@ export class OpenWalletModalComponent implements OnInit {
);
new_wallet.alias = this.backend.getWalletAlias(new_wallet.address);
if (open_data.recent_history && open_data.recent_history.history) {
new_wallet.total_history_item = open_data.recent_history.total_history_items;
new_wallet.totalPages = Math.ceil( open_data.recent_history.total_history_items / this.variablesService.count);
new_wallet.totalPages > this.variablesService.maxPages
? new_wallet.pages = new Array(5).fill(1).map((value, index) => value + index)
: new_wallet.pages = new Array(new_wallet.totalPages).fill(1).map((value, index) => value + index);
new_wallet.prepareHistory(open_data.recent_history.history);
} else {
new_wallet.total_history_item = 0;
new_wallet.pages = new Array(1).fill(1);
new_wallet.totalPages = 1;
}
this.backend.getContracts(open_data.wallet_id, (contracts_status, contracts_data) => {
if (contracts_status && contracts_data.hasOwnProperty('contracts')) {

View file

@ -61,7 +61,7 @@ export class OpenWalletComponent implements OnInit, OnDestroy {
openWallet() {
if (this.openForm.valid && this.openForm.get('name').value.length <= this.variablesService.maxWalletNameLength) {
this.backend.openWallet(this.filePath, this.openForm.get('password').value, false, (open_status, open_data, open_error) => {
this.backend.openWallet(this.filePath, this.openForm.get('password').value, this.variablesService.count, false, (open_status, open_data, open_error) => {
if (open_error && open_error === 'FILE_NOT_FOUND') {
let error_translate = this.translate.instant('OPEN_WALLET.FILE_NOT_FOUND1');
error_translate += ':<br>' + this.filePath;
@ -97,8 +97,18 @@ export class OpenWalletComponent implements OnInit, OnDestroy {
open_data['wi'].tracking_hey
);
new_wallet.alias = this.backend.getWalletAlias(new_wallet.address);
new_wallet.currentPage = 1;
if (open_data.recent_history && open_data.recent_history.history) {
new_wallet.total_history_item = open_data.recent_history.total_history_items;
new_wallet.totalPages = Math.ceil( open_data.recent_history.total_history_items / this.variablesService.count);
new_wallet.totalPages > this.variablesService.maxPages
? new_wallet.pages = new Array(5).fill(1).map((value, index) => value + index)
: new_wallet.pages = new Array(new_wallet.totalPages).fill(1).map((value, index) => value + index);
new_wallet.prepareHistory(open_data.recent_history.history);
} else {
new_wallet.total_history_item = 0;
new_wallet.pages = new Array(1).fill(1);
new_wallet.totalPages = 1;
}
this.backend.getContracts(open_data.wallet_id, (contracts_status, contracts_data) => {
if (contracts_status && contracts_data.hasOwnProperty('contracts')) {

View file

@ -83,7 +83,16 @@ export class RestoreWalletComponent implements OnInit {
restore_data['wi'].tracking_hey
);
this.variablesService.opening_wallet.alias = this.backend.getWalletAlias(this.variablesService.opening_wallet.address);
this.variablesService.opening_wallet.pages = new Array(1).fill(1);
this.variablesService.opening_wallet.totalPages = 1;
this.variablesService.opening_wallet.currentPage = 1;
this.variablesService.opening_wallet.total_history_item = 0;
this.variablesService.opening_wallet.restore = true;
if (restore_data.recent_history && restore_data.recent_history.history) {
this.variablesService.opening_wallet.totalPages = Math.ceil( restore_data.recent_history.total_history_items / this.variablesService.count);
this.variablesService.opening_wallet.totalPages > this.variablesService.maxPages
? this.variablesService.opening_wallet.pages = new Array(5).fill(1).map((value, index) => value + index)
: this.variablesService.opening_wallet.pages = new Array(this.variablesService.opening_wallet.totalPages).fill(1).map((value, index) => value + index);
this.variablesService.opening_wallet.prepareHistory(restore_data.recent_history.history);
}
this.backend.getContracts(this.variablesService.opening_wallet.wallet_id, (contracts_status, contracts_data) => {

View file

@ -46,6 +46,16 @@
<div #scrolledContent class="tabs-content scrolled-content">
<router-outlet></router-outlet>
</div>
<div *ngIf="activeTab === 'history'" class="pagination-wrapper">
<div class="pagination">
<button [disabled]="variablesService.currentWallet.currentPage === 1" (click)="setPage(variablesService.currentWallet.currentPage - 1)"><</button>
<ng-container *ngFor="let page of variablesService.currentWallet.pages">
<button [ngClass]="{ 'active': variablesService.currentWallet.currentPage === page }"
(click)="setPage(page)">{{page}}</button>
</ng-container>
<button [disabled]="variablesService.currentWallet.currentPage === variablesService.currentWallet.totalPages" (click)="setPage(variablesService.currentWallet.currentPage + 1)">></button>
</div>
</div>
</div>
<app-confirm-modal *ngIf="isModalDialogVisible" [title]=" 'WALLET.CONFIRM.TITLE' | translate " [message]=" 'WALLET.CONFIRM.MESSAGE' | translate " (confirmed)="confirmed($event)"></app-confirm-modal>

View file

@ -238,4 +238,18 @@
overflow-x: hidden;
overflow-y: overlay;
}
.pagination-wrapper {
.pagination {
padding: 1rem;
button {
margin-right: 0.5rem;
padding: 0;
width: 2.5rem;
height: 2.5rem;
font-size: 1.2rem;
}
}
}
}

View file

@ -1,12 +1,13 @@
import {Component, OnInit, OnDestroy, NgZone, ViewChild, ElementRef} from '@angular/core';
import {ActivatedRoute, Router, RoutesRecognized} from '@angular/router';
import {VariablesService} from '../_helpers/services/variables.service';
import {BackendService} from '../_helpers/services/backend.service';
import {TranslateService} from '@ngx-translate/core';
import {IntToMoneyPipe} from '../_helpers/pipes/int-to-money.pipe';
import {Subscription} from 'rxjs';
import { Component, OnInit, OnDestroy, NgZone, ViewChild, ElementRef } from '@angular/core';
import { ActivatedRoute, Router, RoutesRecognized } from '@angular/router';
import { VariablesService } from '../_helpers/services/variables.service';
import { BackendService } from '../_helpers/services/backend.service';
import { TranslateService } from '@ngx-translate/core';
import { IntToMoneyPipe } from '../_helpers/pipes/int-to-money.pipe';
import { Subscription } from 'rxjs';
import icons from '../../assets/icons/icons.json';
import { PaginationService } from '../_helpers/services/pagination.service';
@Component({
selector: 'app-wallet',
@ -22,6 +23,9 @@ export class WalletComponent implements OnInit, OnDestroy {
copyAnimationTimeout;
balanceTooltip;
isModalDialogVisible = false;
activeTab = 'history';
public currentPage = 1;
@ViewChild('scrolledContent') private scrolledContent: ElementRef;
@ -90,8 +94,9 @@ export class WalletComponent implements OnInit, OnDestroy {
public variablesService: VariablesService,
private ngZone: NgZone,
private translate: TranslateService,
private intToMoneyPipe: IntToMoneyPipe
) {}
private intToMoneyPipe: IntToMoneyPipe,
private pagination: PaginationService
) { }
ngOnInit() {
this.subRouting1 = this.route.params.subscribe(params => {
@ -103,7 +108,8 @@ export class WalletComponent implements OnInit, OnDestroy {
});
this.subRouting2 = this.router.events.subscribe(val => {
if (val instanceof RoutesRecognized) {
if ( val.state.root.firstChild && val.state.root.firstChild.firstChild ) {
this.activeTab = val.urlAfterRedirects.split('/').pop();
if (val.state.root.firstChild && val.state.root.firstChild.firstChild) {
for (let i = 0; i < this.tabs.length; i++) {
this.tabs[i].active = (this.tabs[i].link === '/' + val.state.root.firstChild.firstChild.url[0].path);
}
@ -137,7 +143,7 @@ export class WalletComponent implements OnInit, OnDestroy {
tab.active = false;
});
this.tabs[index].active = true;
this.ngZone.run( () => {
this.ngZone.run(() => {
this.scrolledContent.nativeElement.scrollTop = 0;
this.router.navigate(['wallet/' + this.walletID + this.tabs[index].link]);
});
@ -159,11 +165,11 @@ export class WalletComponent implements OnInit, OnDestroy {
this.balanceTooltip = document.createElement('div');
const available = document.createElement('span');
available.setAttribute('class', 'available');
available.innerHTML = this.translate.instant('WALLET.AVAILABLE_BALANCE', {available: this.intToMoneyPipe.transform(this.variablesService.currentWallet.unlocked_balance), currency: this.variablesService.defaultCurrency});
available.innerHTML = this.translate.instant('WALLET.AVAILABLE_BALANCE', { available: this.intToMoneyPipe.transform(this.variablesService.currentWallet.unlocked_balance), currency: this.variablesService.defaultCurrency });
this.balanceTooltip.appendChild(available);
const locked = document.createElement('span');
locked.setAttribute('class', 'locked');
locked.innerHTML = this.translate.instant('WALLET.LOCKED_BALANCE', {locked: this.intToMoneyPipe.transform(this.variablesService.currentWallet.balance.minus(this.variablesService.currentWallet.unlocked_balance)), currency: this.variablesService.defaultCurrency});
locked.innerHTML = this.translate.instant('WALLET.LOCKED_BALANCE', { locked: this.intToMoneyPipe.transform(this.variablesService.currentWallet.balance.minus(this.variablesService.currentWallet.unlocked_balance)), currency: this.variablesService.defaultCurrency });
this.balanceTooltip.appendChild(locked);
const link = document.createElement('span');
link.setAttribute('class', 'link');
@ -215,6 +221,29 @@ export class WalletComponent implements OnInit, OnDestroy {
});
}
public setPage(pageNumber: number) {
if (pageNumber === this.variablesService.currentWallet.currentPage) {
return;
}
this.variablesService.currentWallet.currentPage = pageNumber;
this.backend.getRecentTransfers(
this.walletID,
(this.variablesService.currentWallet.currentPage - 1) * this.variablesService.count,
this.variablesService.count, (status, data) => {
if (status && data.total_history_items) {
this.variablesService.currentWallet.history.splice(0, this.variablesService.currentWallet.history.length);
this.ngZone.run(() => {
this.pagination.paginate(this.variablesService.currentWallet.currentPage);
if (data.history.length !== 0) {
this.variablesService.currentWallet.restore = false;
this.variablesService.currentWallet.total_history_item = data.total_history_items;
this.variablesService.currentWallet.prepareHistory(data.history);
}
});
}
});
}
ngOnDestroy() {
this.subRouting1.unsubscribe();
this.subRouting2.unsubscribe();

View file

@ -53,7 +53,8 @@ $themes: (
tooltipCriticalBackgroundColor: #5f1d1d,
tooltipShadow: 0 0 1rem rgba(0, 0, 0, 0.5),
modalBackground: url(~src/assets/images/background-dark.png),
closeButtonColor: #556576
closeButtonColor: #556576,
hoverPage: #3a485a
),
gray: (
bodyBackgroundColor: #101417,
@ -109,7 +110,8 @@ $themes: (
tooltipCriticalBackgroundColor: #4c1919,
tooltipShadow: 0 0 1rem rgba(0, 0, 0, 0.5),
modalBackground: url(~src/assets/images/background-gray.png),
closeButtonColor: #515960
closeButtonColor: #515960,
hoverPage: #383e43
),
white: (
bodyBackgroundColor: #eeeeee,
@ -165,7 +167,8 @@ $themes: (
tooltipCriticalBackgroundColor: #e53935,
tooltipShadow: 0 0 1rem rgba(120, 120, 120, 0.5),
modalBackground: url(~src/assets/images/background-white.png),
closeButtonColor: #43454b
closeButtonColor: #43454b,
hoverPage: #ffffff
)
);

View file

@ -92,6 +92,41 @@ app-wallet {
background-color: themed(contentBackgroundColor);
}
}
.pagination-wrapper {
@include themify($themes) {
background-color: themed(contentBackgroundColor);
}
.pagination {
@include themify($themes) {
border-top: 0.2rem solid themed(transparentButtonBorderColor);
}
button {
@include themify($themes) {
background-color: themed(transparentButtonBorderColor);
color: themed(mainTextColor);
}
&.active {
@include themify($themes) {
background-color: themed(tableBackgroundColor);
color: themed(mainTextColor);
}
}
&:hover {
@include themify($themes) {
background-color: themed(hoverPage);
color: themed(mainTextColor);
}
}
}
}
}
}
}

View file

@ -80,7 +80,8 @@ namespace nodetool
m_ip_address{},
m_last_stat_request_time{},
m_use_only_priority_peers(false),
m_peer_livetime{}
m_peer_livetime{},
m_debug_requests_enabled(false)
{}

View file

@ -100,9 +100,9 @@ namespace nodetool
if (m_offline_mode)
return false;
//@#@ workaround
//@#@ temporary workaround
return true;
#if 0
CRITICAL_REGION_LOCAL(m_blocked_ips_lock);
auto it = m_blocked_ips.find(addr);
if(it == m_blocked_ips.end())
@ -114,6 +114,7 @@ namespace nodetool
return true;
}
return false;
#endif
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
@ -280,10 +281,10 @@ namespace nodetool
ADD_HARDCODED_SEED_NODE("94.130.137.230", P2P_DEFAULT_PORT);
ADD_HARDCODED_SEED_NODE("95.217.42.247", P2P_DEFAULT_PORT);
ADD_HARDCODED_SEED_NODE("94.130.160.115", P2P_DEFAULT_PORT);
ADD_HARDCODED_SEED_NODE("207.154.237.82", P2P_DEFAULT_PORT);
ADD_HARDCODED_SEED_NODE("207.154.240.198", P2P_DEFAULT_PORT);
ADD_HARDCODED_SEED_NODE("207.154.255.10", P2P_DEFAULT_PORT);
ADD_HARDCODED_SEED_NODE("207.154.228.141", P2P_DEFAULT_PORT);
ADD_HARDCODED_SEED_NODE("195.201.107.230", P2P_DEFAULT_PORT);
ADD_HARDCODED_SEED_NODE("95.217.46.49", P2P_DEFAULT_PORT);
ADD_HARDCODED_SEED_NODE("159.69.76.144", P2P_DEFAULT_PORT);
ADD_HARDCODED_SEED_NODE("144.76.183.143", P2P_DEFAULT_PORT);
#else
//TODO:
ADD_HARDCODED_SEED_NODE("95.217.43.225", P2P_DEFAULT_PORT);
@ -696,7 +697,7 @@ namespace nodetool
if(just_take_peerlist)
{
m_net_server.get_config_object().close(con.m_connection_id);
LOG_PRINT_CC_GREEN(con, "CONNECTION HANDSHAKED OK AND CLOSED.", LOG_LEVEL_2);
LOG_PRINT_CC_GREEN(con, "CONNECTION HANDSHAKED OK AND CLOSED with peer " << string_tools::get_ip_string_from_int32(na.ip) << ":" << string_tools::num_to_string_fast(na.port), LOG_LEVEL_2);
return true;
}
@ -707,7 +708,7 @@ namespace nodetool
m_peerlist.append_with_peer_white(pe_local);
//update last seen and push it to peerlist manager
LOG_PRINT_CC_GREEN(con, "CONNECTION HANDSHAKED OK.", LOG_LEVEL_2);
LOG_PRINT_CC_GREEN(con, "CONNECTION HANDSHAKED OK with peer " << string_tools::get_ip_string_from_int32(na.ip) << ":" << string_tools::num_to_string_fast(na.port), LOG_LEVEL_2);
return true;
}
//-----------------------------------------------------------------------------------

View file

@ -793,28 +793,45 @@ namespace currency
return false;
}
block b = AUTO_VAL_INIT(b);
wide_difficulty_type dt = 0;
currency::pos_entry pe = AUTO_VAL_INIT(pe);
pe.amount = req.pos_amount;
pe.index = req.pos_index;
pe.stake_unlock_time = req.stake_unlock_time;
//pe.keyimage key image will be set in the wallet
//pe.wallet_index is not included in serialization map, TODO: refactoring here
if (!m_core.get_block_template(b, miner_address, stakeholder_address, dt, res.height, req.extra_text, req.pos_block, pe))
create_block_template_params params = AUTO_VAL_INIT(params);
params.miner_address = miner_address;
params.stakeholder_address = stakeholder_address;
params.ex_nonce = req.extra_text;
params.pos = req.pos_block;
params.pe.amount = req.pos_amount;
params.pe.index = req.pos_index;
params.pe.stake_unlock_time = req.stake_unlock_time;
//params.pe.keyimage key image will be set in the wallet
//params.pe.wallet_index is not included in serialization map, TODO: refactoring here
params.pcustom_fill_block_template_func = nullptr;
if (req.explicit_transaction.size())
{
transaction tx = AUTO_VAL_INIT(tx);
if (!parse_and_validate_tx_from_blob(req.explicit_transaction, tx))
{
error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
error_resp.message = "Wrong parameters: explicit_transaction is invalid";
LOG_ERROR("Failed to parse explicit_transaction blob");
return false;
}
params.explicit_txs.push_back(tx);
}
create_block_template_response resp = AUTO_VAL_INIT(resp);
if (!m_core.get_block_template(params, resp))
{
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
error_resp.message = "Internal error: failed to create block template";
LOG_ERROR("Failed to create block template");
return false;
}
res.difficulty = dt.convert_to<std::string>();
blobdata block_blob = t_serializable_object_to_blob(b);
res.difficulty = resp.diffic.convert_to<std::string>();
blobdata block_blob = t_serializable_object_to_blob(resp.b);
res.blocktemplate_blob = string_tools::buff_to_hex_nodelimer(block_blob);
res.prev_hash = string_tools::pod_to_hex(b.prev_id);
res.prev_hash = string_tools::pod_to_hex(resp.b.prev_id);
res.height = resp.height;
//calculate epoch seed
res.seed = currency::ethash_epoch_to_seed(currency::ethash_height_to_epoch(res.height));
@ -851,7 +868,64 @@ namespace currency
block_verification_context bvc = AUTO_VAL_INIT(bvc);
if(!m_core.handle_block_found(b, &bvc))
{
if (bvc.added_to_altchain)
if (bvc.m_added_to_altchain)
{
error_resp.code = CORE_RPC_ERROR_CODE_BLOCK_ADDED_AS_ALTERNATIVE;
error_resp.message = "Block added as alternative";
return false;
}
error_resp.code = CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED;
error_resp.message = "Block not accepted";
return false;
}
//@#@
//temporary double check timestamp
if (time(NULL) - get_actual_timestamp(b) > 5)
{
LOG_PRINT_RED_L0("Found block (" << get_block_hash(b) << ") timestamp (" << get_actual_timestamp(b)
<< ") is suspiciously less (" << time(NULL) - get_actual_timestamp(b) << ") then curren time( " << time(NULL) << ")");
//mark node to make it easier to find it via scanner
m_core.get_blockchain_storage().get_performnce_data().epic_failure_happend = true;
}
//
res.status = "OK";
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_submitblock2(const COMMAND_RPC_SUBMITBLOCK2::request& req, COMMAND_RPC_SUBMITBLOCK2::response& res, epee::json_rpc::error& error_resp, connection_context& cntx)
{
CHECK_CORE_READY();
block b = AUTO_VAL_INIT(b);
if (!parse_and_validate_block_from_blob(req.b, b))
{
error_resp.code = CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB;
error_resp.message = "Wrong block blob";
return false;
}
block_verification_context bvc = AUTO_VAL_INIT(bvc);
for (const auto& txblob : req.explicit_txs)
{
crypto::hash tx_hash = AUTO_VAL_INIT(tx_hash);
transaction tx = AUTO_VAL_INIT(tx);
if (!parse_and_validate_tx_from_blob(txblob.blob, tx, tx_hash))
{
error_resp.code = CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB;
error_resp.message = "Wrong explicit tx blob";
return false;
}
bvc.m_onboard_transactions[tx_hash] = tx;
}
if (!m_core.handle_block_found(b, &bvc))
{
if (bvc.m_added_to_altchain)
{
error_resp.code = CORE_RPC_ERROR_CODE_BLOCK_ADDED_AS_ALTERNATIVE;
error_resp.message = "Block added as alternative";

View file

@ -63,6 +63,7 @@ namespace currency
bool on_getblockhash(const COMMAND_RPC_GETBLOCKHASH::request& req, COMMAND_RPC_GETBLOCKHASH::response& res, epee::json_rpc::error& error_resp, connection_context& cntx);
bool on_getblocktemplate(const COMMAND_RPC_GETBLOCKTEMPLATE::request& req, COMMAND_RPC_GETBLOCKTEMPLATE::response& res, epee::json_rpc::error& error_resp, connection_context& cntx);
bool on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMMAND_RPC_SUBMITBLOCK::response& res, epee::json_rpc::error& error_resp, connection_context& cntx);
bool on_submitblock2(const COMMAND_RPC_SUBMITBLOCK2::request& req, COMMAND_RPC_SUBMITBLOCK2::response& res, epee::json_rpc::error& error_resp, connection_context& cntx);
bool on_get_last_block_header(const COMMAND_RPC_GET_LAST_BLOCK_HEADER::request& req, COMMAND_RPC_GET_LAST_BLOCK_HEADER::response& res, epee::json_rpc::error& error_resp, connection_context& cntx);
bool on_get_block_header_by_hash(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::response& res, epee::json_rpc::error& error_resp, connection_context& cntx);
bool on_get_block_header_by_height(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response& res, epee::json_rpc::error& error_resp, connection_context& cntx);
@ -125,6 +126,7 @@ namespace currency
MAP_JON_RPC_WE("on_getblockhash", on_getblockhash, COMMAND_RPC_GETBLOCKHASH)
MAP_JON_RPC_WE("getblocktemplate", on_getblocktemplate, COMMAND_RPC_GETBLOCKTEMPLATE)
MAP_JON_RPC_WE("submitblock", on_submitblock, COMMAND_RPC_SUBMITBLOCK)
MAP_JON_RPC_WE("submitblock2", on_submitblock2, COMMAND_RPC_SUBMITBLOCK2)
MAP_JON_RPC_WE("getlastblockheader", on_get_last_block_header, COMMAND_RPC_GET_LAST_BLOCK_HEADER)
MAP_JON_RPC_WE("getblockheaderbyhash", on_get_block_header_by_hash, COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH)
MAP_JON_RPC_WE("getblockheaderbyheight", on_get_block_header_by_height, COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT)

View file

@ -5,6 +5,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#pragma once
#include "serialization/keyvalue_hexemizer.h"
#include "currency_protocol/currency_protocol_defs.h"
#include "currency_core/currency_basic.h"
#include "currency_core/difficulty.h"
@ -770,7 +771,7 @@ namespace currency
{
struct request
{
//uint64_t reserve_size; //max 255 bytes
blobdata explicit_transaction;
std::string extra_text;
std::string wallet_address;
std::string stakeholder_address;
@ -780,6 +781,7 @@ namespace currency
uint64_t stake_unlock_time;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_BLOB_AS_HEX_STRING(explicit_transaction)
KV_SERIALIZE(extra_text)
KV_SERIALIZE(wallet_address)
KV_SERIALIZE(stakeholder_address);
@ -824,6 +826,29 @@ namespace currency
};
};
struct COMMAND_RPC_SUBMITBLOCK2
{
struct request
{
std::string b; //hex encoded block blob
std::list<epee::hexemizer> explicit_txs; //hex encoded tx blobs
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_BLOB_AS_HEX_STRING(b)
KV_SERIALIZE(explicit_txs)
END_KV_SERIALIZE_MAP()
};
struct response
{
std::string status;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
END_KV_SERIALIZE_MAP()
};
};
struct block_header_response
{
uint8_t major_version;

View file

@ -49,6 +49,7 @@ namespace
const command_line::arg_descriptor<int> arg_daemon_port = {"daemon-port", "Use daemon instance at port <arg> instead of default", 0};
const command_line::arg_descriptor<uint32_t> arg_log_level = {"set-log", "", 0, true};
const command_line::arg_descriptor<bool> arg_do_pos_mining = { "do-pos-mining", "Do PoS mining", false, false };
const command_line::arg_descriptor<std::string> arg_pos_mining_reward_address = { "pos-mining-reward-address", "Block reward will be sent to the giving address if specified", "" };
const command_line::arg_descriptor<std::string> arg_restore_wallet = { "restore-wallet", "Restore wallet from the seed phrase and save it to <arg>", "" };
const command_line::arg_descriptor<bool> arg_offline_mode = { "offline-mode", "Don't connect to daemon, work offline (for cold-signing process)", false, true };
@ -190,7 +191,7 @@ simple_wallet::simple_wallet()
m_cmd_binder.set_handler("incoming_transfers", boost::bind(&simple_wallet::show_incoming_transfers, this, _1), "incoming_transfers [available|unavailable] - Show incoming transfers - all of them or filter them by availability");
m_cmd_binder.set_handler("incoming_counts", boost::bind(&simple_wallet::show_incoming_transfers_counts, this, _1), "incoming_transfers counts");
m_cmd_binder.set_handler("list_recent_transfers", boost::bind(&simple_wallet::list_recent_transfers, this, _1), "list_recent_transfers - Show recent maximum 1000 transfers");
m_cmd_binder.set_handler("list_recent_transfers_ex", boost::bind(&simple_wallet::list_recent_transfers_ex, this, _1), "list_recent_transfers_tx - Write recent transfer in json to wallet_recent_transfers.txt");
m_cmd_binder.set_handler("export_recent_transfers", boost::bind(&simple_wallet::export_recent_transfers, this, _1), "list_recent_transfers_tx - Write recent transfer in json to wallet_recent_transfers.txt");
m_cmd_binder.set_handler("list_outputs", boost::bind(&simple_wallet::list_outputs, this, _1), "list_outputs [spent|unspent] - Lists all the outputs that have ever been sent to this wallet if called without arguments, otherwise it lists only the spent or unspent outputs");
m_cmd_binder.set_handler("dump_transfers", boost::bind(&simple_wallet::dump_trunsfers, this, _1), "dump_transfers - Write transfers in json to dump_transfers.txt");
m_cmd_binder.set_handler("dump_keyimages", boost::bind(&simple_wallet::dump_key_images, this, _1), "dump_keyimages - Write key_images in json to dump_key_images.txt");
@ -207,6 +208,8 @@ simple_wallet::simple_wallet()
m_cmd_binder.set_handler("fix_collisions", boost::bind(&simple_wallet::fix_collisions, this, _1), "Rescan transfers for key image collisions");
m_cmd_binder.set_handler("scan_transfers_for_id", boost::bind(&simple_wallet::scan_transfers_for_id, this, _1), "Rescan transfers for tx_id");
m_cmd_binder.set_handler("scan_transfers_for_ki", boost::bind(&simple_wallet::scan_transfers_for_ki, this, _1), "Rescan transfers for key image");
m_cmd_binder.set_handler("print_utxo_distribution", boost::bind(&simple_wallet::print_utxo_distribution, this, _1), "Prints utxo distribution");
m_cmd_binder.set_handler("sweep_below", boost::bind(&simple_wallet::sweep_below, this, _1), "sweep_below <mixin_count> <address> <amount_lower_limit> [payment_id] - Tries to transfers all coins with amount below the given limit to the given address");
m_cmd_binder.set_handler("address", boost::bind(&simple_wallet::print_address, this, _1), "Show current wallet public address");
m_cmd_binder.set_handler("integrated_address", boost::bind(&simple_wallet::integrated_address, this, _1), "integrated_address [<payment_id>|<integrated_address] - encodes given payment_id along with wallet's address into an integrated address (random payment_id will be used if none is provided). Decodes given integrated_address into standard address");
@ -688,7 +691,7 @@ bool print_wti(const tools::wallet_public::wallet_transfer_info& wti)
std::string payment_id_placeholder;
if (wti.payment_id.size())
payment_id_placeholder = std::string("(payment_id:") + wti.payment_id + ")";
payment_id_placeholder = std::string("(payment_id:") + epee::string_tools::buff_to_hex_nodelimer(wti.payment_id) + ")";
static const std::string separator = ", ";
std::string remote_side;
@ -715,7 +718,8 @@ bool simple_wallet::list_recent_transfers(const std::vector<std::string>& args)
{
std::vector<tools::wallet_public::wallet_transfer_info> unconfirmed;
std::vector<tools::wallet_public::wallet_transfer_info> recent;
m_wallet->get_recent_transfers_history(recent, 0, 0);
uint64_t total = 0;
m_wallet->get_recent_transfers_history(recent, 0, 0, total);
m_wallet->get_unconfirmed_transfers(unconfirmed);
//workaround for missed fee
@ -736,11 +740,39 @@ bool simple_wallet::list_recent_transfers(const std::vector<std::string>& args)
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::list_recent_transfers_ex(const std::vector<std::string>& args)
std::string wti_to_text_line(const tools::wallet_public::wallet_transfer_info& wti)
{
stringstream ss;
ss << (wti.is_income ? "[INC]" : "[OUT]") << "\t"
<< epee::misc_utils::get_time_str(wti.timestamp) << "\t"
<< print_money(wti.amount) << "\t"
<< print_money(wti.fee) << "\t"
<< wti.remote_addresses << "\t"
<< wti.comment << "\t";
return ss.str();
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::export_recent_transfers(const std::vector<std::string>& args)
{
bool export_to_json = true;
bool ignore_pos = false;
if (args.size())
{
if (args[0] == "json")
export_to_json = true;
else if (args[0] == "txt")
export_to_json = false;
}
if (args.size() > 1)
{
if (args[1] == "ignore-pos")
ignore_pos = true;
}
std::vector<tools::wallet_public::wallet_transfer_info> unconfirmed;
std::vector<tools::wallet_public::wallet_transfer_info> recent;
m_wallet->get_recent_transfers_history(recent, 0, 0);
uint64_t total = 0;
m_wallet->get_recent_transfers_history(recent, 0, 0, total);
m_wallet->get_unconfirmed_transfers(unconfirmed);
//workaround for missed fee
stringstream ss;
@ -748,17 +780,28 @@ bool simple_wallet::list_recent_transfers_ex(const std::vector<std::string>& arg
ss << "Unconfirmed transfers: " << ENDL;
for (auto & wti : unconfirmed)
{
if(ignore_pos && wti.is_mining)
continue;
if (!wti.fee)
wti.fee = currency::get_tx_fee(wti.tx);
ss << epee::serialization::store_t_to_json(wti) << ENDL;
if(export_to_json)
ss << epee::serialization::store_t_to_json(wti) << ENDL;
else
ss << wti_to_text_line(wti) << ENDL;
}
ss << "Recent transfers: " << ENDL;
for (auto & wti : recent)
{
if (ignore_pos && wti.is_mining)
continue;
if (!wti.fee)
wti.fee = currency::get_tx_fee(wti.tx);
ss << epee::serialization::store_t_to_json(wti) << ENDL;
if (export_to_json)
ss << epee::serialization::store_t_to_json(wti) << ENDL;
else
ss << wti_to_text_line(wti) << ENDL;
}
LOG_PRINT_GREEN("Storing text to wallet_recent_transfers.txt....", LOG_LEVEL_0);
file_io_utils::save_string_to_file(log_space::log_singletone::get_default_log_folder() + "/wallet_recent_transfers.txt", ss.str());
@ -949,6 +992,18 @@ bool simple_wallet::scan_transfers_for_ki(const std::vector<std::string> &args)
print_td_list(td);
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::print_utxo_distribution(const std::vector<std::string> &args)
{
std::map<uint64_t, uint64_t> distribution;
m_wallet->get_utxo_distribution(distribution);
for (auto& e : distribution)
{
message_writer() << std::left << setw(25) << print_money(e.first) << "|" << e.second;
}
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::get_transfer_info(const std::vector<std::string> &args)
{
@ -1261,7 +1316,7 @@ bool simple_wallet::print_address(const std::vector<std::string> &args/* = std::
bool simple_wallet::show_seed(const std::vector<std::string> &args)
{
success_msg_writer() << "Here's your wallet's seed phrase. Write it down and keep in a safe place.";
success_msg_writer(true) << "Anyone who knows the following 25 words can access you wallet:";
success_msg_writer(true) << "Anyone who knows the following 25 words can access your wallet:";
std::cout << m_wallet->get_account().get_restore_braindata() << std::endl << std::flush;
return true;
}
@ -1269,7 +1324,7 @@ bool simple_wallet::show_seed(const std::vector<std::string> &args)
bool simple_wallet::spendkey(const std::vector<std::string> &args)
{
message_writer(epee::log_space::console_color_red, true, std::string())
<< "WARNING! Anyone who knows the following secret key can access you wallet and spend your coins.";
<< "WARNING! Anyone who knows the following secret key can access your wallet and spend your coins.";
const account_keys& keys = m_wallet->get_account().get_keys();
std::cout << "secret: " << epee::string_tools::pod_to_hex(keys.m_spend_secret_key) << std::endl;
@ -1281,7 +1336,7 @@ bool simple_wallet::spendkey(const std::vector<std::string> &args)
bool simple_wallet::viewkey(const std::vector<std::string> &args)
{
message_writer(epee::log_space::console_color_yellow, false, std::string())
<< "WARNING! Anyone who knows the following secret key can view you wallet (but can not spend your coins).";
<< "WARNING! Anyone who knows the following secret key can view your wallet (but can not spend your coins).";
const account_keys& keys = m_wallet->get_account().get_keys();
std::cout << "secret: " << epee::string_tools::pod_to_hex(keys.m_view_secret_key) << std::endl;
@ -1512,6 +1567,94 @@ bool simple_wallet::submit_transfer(const std::vector<std::string> &args)
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::sweep_below(const std::vector<std::string> &args)
{
bool r = false;
if (args.size() < 3 || args.size() > 4)
{
fail_msg_writer() << "invalid agruments count: " << args.size() << ", expected 3 or 4";
return true;
}
size_t fake_outs_count = 0;
if (!string_tools::get_xtype_from_string(fake_outs_count, args[0]))
{
fail_msg_writer() << "mixin_count should be non-negative integer, got " << args[0];
return true;
}
// parse payment_id
currency::payment_id_t payment_id;
if (args.size() == 4)
{
const std::string &payment_id_str = args.back();
r = parse_payment_id_from_hex_str(payment_id_str, payment_id);
if (!r)
{
fail_msg_writer() << "payment id has invalid format: \"" << payment_id_str << "\", expected hex string";
return true;
}
}
currency::account_public_address addr;
currency::payment_id_t integrated_payment_id;
if (!m_wallet->get_transfer_address(args[1], addr, integrated_payment_id))
{
fail_msg_writer() << "wrong address: " << args[1];
return true;
}
// handle integrated payment id
if (!integrated_payment_id.empty())
{
if (!payment_id.empty())
{
fail_msg_writer() << "address " << args[1] << " has integrated payment id " << epee::string_tools::buff_to_hex_nodelimer(integrated_payment_id) <<
" which is incompatible with payment id " << epee::string_tools::buff_to_hex_nodelimer(payment_id) << " that was already assigned to this transfer";
return true;
}
payment_id = integrated_payment_id; // remember integrated payment id as the main payment id
success_msg_writer() << "NOTE: using payment id " << epee::string_tools::buff_to_hex_nodelimer(payment_id) << " from integrated address " << args[1];
}
uint64_t amount = 0;
r = currency::parse_amount(amount, args[2]);
if (!r || amount == 0)
{
fail_msg_writer() << "incorrect amount: " << args[2];
return true;
}
try
{
uint64_t fee = m_wallet->get_core_runtime_config().tx_default_fee;
size_t outs_total = 0, outs_swept = 0;
uint64_t amount_total = 0, amount_swept = 0;
currency::transaction result_tx = AUTO_VAL_INIT(result_tx);
std::string filename = "zano_tx_unsigned";
m_wallet->sweep_below(fake_outs_count, addr, amount, payment_id, fee, outs_total, amount_total, outs_swept, &result_tx, &filename);
if (!get_inputs_money_amount(result_tx, amount_swept))
LOG_ERROR("get_inputs_money_amount failed, tx: " << obj_to_json_str(result_tx));
success_msg_writer(false) << outs_swept << " outputs (" << print_money_brief(amount_swept) << " coins) of " << outs_total << " total (" << print_money_brief(amount_total)
<< ") below the specified limit of " << print_money_brief(amount) << " were successfully swept";
if (m_wallet->is_watch_only())
success_msg_writer(true) << "Transaction prepared for signing and saved into \"" << filename << "\" file, use full wallet to sign transfer and then use \"submit_transfer\" on this wallet to broadcast the transaction to the network";
else
success_msg_writer(true) << "tx: " << get_transaction_hash(result_tx) << " size: " << get_object_blobsize(result_tx) << " bytes";
}
catch (const std::exception& e)
{
LOG_ERROR(e.what());
fail_msg_writer() << e.what();
return true;
}
return true;
}
//----------------------------------------------------------------------------------------------------
#ifdef WIN32
int wmain( int argc, wchar_t* argv_w[ ], wchar_t* envp[ ] )
#else
@ -1563,6 +1706,7 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_params, arg_dont_set_date);
command_line::add_arg(desc_params, arg_print_brain_wallet);
command_line::add_arg(desc_params, arg_do_pos_mining);
command_line::add_arg(desc_params, arg_pos_mining_reward_address);
command_line::add_arg(desc_params, arg_restore_wallet);
command_line::add_arg(desc_params, arg_offline_mode);
command_line::add_arg(desc_params, command_line::arg_log_file);
@ -1713,6 +1857,17 @@ int main(int argc, char* argv[])
break;
}
currency::account_public_address miner_address = wal.get_account().get_public_address();
if (command_line::has_arg(vm, arg_pos_mining_reward_address))
{
std::string arg_pos_mining_reward_address_str = command_line::get_arg(vm, arg_pos_mining_reward_address);
if (!arg_pos_mining_reward_address_str.empty())
{
r = get_account_address_from_str(miner_address, arg_pos_mining_reward_address_str);
CHECK_AND_ASSERT_MES(r, EXIT_FAILURE, "Failed to parse miner address from string: " << arg_pos_mining_reward_address_str);
LOG_PRINT_YELLOW("PoS reward will be sent to another address: " << arg_pos_mining_reward_address_str, LOG_LEVEL_0);
}
}
tools::wallet_rpc_server wrpc(wal);
bool r = wrpc.init(vm);
@ -1722,7 +1877,7 @@ int main(int argc, char* argv[])
wrpc.send_stop_signal();
});
LOG_PRINT_L0("Starting wallet rpc server");
wrpc.run(command_line::get_arg(vm, arg_do_pos_mining), offline_mode);
wrpc.run(command_line::get_arg(vm, arg_do_pos_mining), offline_mode, miner_address);
LOG_PRINT_L0("Stopped wallet rpc server");
try
{

View file

@ -55,7 +55,7 @@ namespace currency
bool refresh(const std::vector<std::string> &args);
bool show_balance(const std::vector<std::string> &args = std::vector<std::string>());
bool list_recent_transfers(const std::vector<std::string>& args);
bool list_recent_transfers_ex(const std::vector<std::string>& args);
bool export_recent_transfers(const std::vector<std::string>& args);
bool dump_trunsfers(const std::vector<std::string>& args);
bool dump_key_images(const std::vector<std::string>& args);
bool show_incoming_transfers(const std::vector<std::string> &args);
@ -67,6 +67,7 @@ namespace currency
bool fix_collisions(const std::vector<std::string> &args );
bool scan_transfers_for_id(const std::vector<std::string> &args);
bool scan_transfers_for_ki(const std::vector<std::string> &args);
bool print_utxo_distribution(const std::vector<std::string> &args);
bool show_blockchain_height(const std::vector<std::string> &args);
bool show_wallet_bcheight(const std::vector<std::string> &args);
bool transfer(const std::vector<std::string> &args);
@ -83,6 +84,7 @@ namespace currency
bool save_watch_only(const std::vector<std::string> &args);
bool sign_transfer(const std::vector<std::string> &args);
bool submit_transfer(const std::vector<std::string> &args);
bool sweep_below(const std::vector<std::string> &args);
bool get_alias_from_daemon(const std::string& alias_name, currency::extra_alias_entry_base& ai);
bool get_transfer_address(const std::string& adr_str, currency::account_public_address& addr);

View file

@ -532,7 +532,7 @@ namespace
r = m_p_core->handle_block_found(m_block_template, &bvc, false);
if (r)
{
if (!bvc.m_verification_failed && !bvc.added_to_altchain && bvc.m_added_to_main_chain && !bvc.m_already_exists && !bvc.m_marked_as_orphaned)
if (!bvc.m_verification_failed && !bvc.m_added_to_altchain && bvc.m_added_to_main_chain && !bvc.m_already_exists && !bvc.m_marked_as_orphaned)
{
LP_CC_WORKER_GREEN(p_ph->get_context(), "found block " << block_hash << " at height " << height << " was successfully added to the blockchain, difficulty " << m_network_difficulty, LOG_LEVEL_0);
r = update_block_template();
@ -544,7 +544,7 @@ namespace
{
LP_CC_WORKER_RED(p_ph->get_context(), "block " << block_hash << " at height " << height << " was NOT added to the blockchain:" << ENDL <<
" verification_failed: " << bvc.m_verification_failed << ENDL <<
" added_to_altchain: " << bvc.added_to_altchain << ENDL <<
" added_to_altchain: " << bvc.m_added_to_altchain << ENDL <<
" added_to_main_chain: " << bvc.m_added_to_main_chain << ENDL <<
" already_exists: " << bvc.m_already_exists << ENDL <<
" marked_as_orphaned: " << bvc.m_marked_as_orphaned, LOG_LEVEL_0);

View file

@ -5,9 +5,9 @@
#define PROJECT_MAJOR_VERSION "1"
#define PROJECT_MINOR_VERSION "1"
#define PROJECT_REVISION "3"
#define PROJECT_REVISION "4"
#define PROJECT_VERSION PROJECT_MAJOR_VERSION "." PROJECT_MINOR_VERSION "." PROJECT_REVISION
#define PROJECT_VERSION_BUILD_NO 70
#define PROJECT_VERSION_BUILD_NO 76
#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

@ -116,6 +116,11 @@ namespace tools
return invoke_http_json_rpc_update_is_disconnect("submitblock", req, rsp);
}
//------------------------------------------------------------------------------------------------------------------------------
bool default_http_core_proxy::call_COMMAND_RPC_SUBMITBLOCK2(const currency::COMMAND_RPC_SUBMITBLOCK2::request& req, currency::COMMAND_RPC_SUBMITBLOCK2::response& rsp)
{
return invoke_http_json_rpc_update_is_disconnect("submitblock2", req, rsp);
}
//------------------------------------------------------------------------------------------------------------------------------
bool default_http_core_proxy::check_connection()
{
CRITICAL_REGION_LOCAL(m_lock);

View file

@ -39,6 +39,7 @@ namespace tools
bool call_COMMAND_RPC_SCAN_POS(const currency::COMMAND_RPC_SCAN_POS::request& req, currency::COMMAND_RPC_SCAN_POS::response& rsp) override;
bool call_COMMAND_RPC_GETBLOCKTEMPLATE(const currency::COMMAND_RPC_GETBLOCKTEMPLATE::request& req, currency::COMMAND_RPC_GETBLOCKTEMPLATE::response& rsp) override;
bool call_COMMAND_RPC_SUBMITBLOCK(const currency::COMMAND_RPC_SUBMITBLOCK::request& req, currency::COMMAND_RPC_SUBMITBLOCK::response& rsp) override;
bool call_COMMAND_RPC_SUBMITBLOCK2(const currency::COMMAND_RPC_SUBMITBLOCK2::request& req, currency::COMMAND_RPC_SUBMITBLOCK2::response& rsp) override;
bool call_COMMAND_RPC_GET_POS_MINING_DETAILS(const currency::COMMAND_RPC_GET_POS_MINING_DETAILS::request& req, currency::COMMAND_RPC_GET_POS_MINING_DETAILS::response& rsp) override;
bool call_COMMAND_RPC_GET_BLOCKS_DETAILS(const currency::COMMAND_RPC_GET_BLOCKS_DETAILS::request& req, currency::COMMAND_RPC_GET_BLOCKS_DETAILS::response& res) override;
bool call_COMMAND_RPC_GET_CURRENT_CORE_TX_EXPIRATION_MEDIAN(const currency::COMMAND_RPC_GET_CURRENT_CORE_TX_EXPIRATION_MEDIAN::request& req, currency::COMMAND_RPC_GET_CURRENT_CORE_TX_EXPIRATION_MEDIAN::response& res) override;

View file

@ -36,6 +36,7 @@ namespace tools
virtual bool call_COMMAND_RPC_SCAN_POS(const currency::COMMAND_RPC_SCAN_POS::request& req, currency::COMMAND_RPC_SCAN_POS::response& rsp){ return false; }
virtual bool call_COMMAND_RPC_GETBLOCKTEMPLATE(const currency::COMMAND_RPC_GETBLOCKTEMPLATE::request& req, currency::COMMAND_RPC_GETBLOCKTEMPLATE::response& rsp){ return false; }
virtual bool call_COMMAND_RPC_SUBMITBLOCK(const currency::COMMAND_RPC_SUBMITBLOCK::request& req, currency::COMMAND_RPC_SUBMITBLOCK::response& rsp){ return false; }
virtual bool call_COMMAND_RPC_SUBMITBLOCK2(const currency::COMMAND_RPC_SUBMITBLOCK2::request& req, currency::COMMAND_RPC_SUBMITBLOCK2::response& rsp) { return false; }
virtual bool call_COMMAND_RPC_GET_POS_MINING_DETAILS(const currency::COMMAND_RPC_GET_POS_MINING_DETAILS::request& req, currency::COMMAND_RPC_GET_POS_MINING_DETAILS::response& rsp){ return false; }
virtual bool call_COMMAND_RPC_GET_BLOCKS_DETAILS(const currency::COMMAND_RPC_GET_BLOCKS_DETAILS::request& req, currency::COMMAND_RPC_GET_BLOCKS_DETAILS::response& res){ return false; }
virtual bool call_COMMAND_RPC_GET_OFFERS_EX(const currency::COMMAND_RPC_GET_OFFERS_EX::request& req, currency::COMMAND_RPC_GET_OFFERS_EX::response& res){ return false; }

View file

@ -147,6 +147,11 @@ bool wallet2::set_core_proxy(const std::shared_ptr<i_core_proxy>& proxy)
return true;
}
//----------------------------------------------------------------------------------------------------
void wallet2::set_pos_mint_packing_size(uint64_t new_size)
{
m_pos_mint_packing_size = new_size;
}
//----------------------------------------------------------------------------------------------------
std::shared_ptr<i_core_proxy> wallet2::get_core_proxy()
{
return m_core_proxy;
@ -385,7 +390,7 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t
if (coin_base_tx)
{
//last out in coinbase tx supposed to be change from coinstake
if (!(i_in_outs == outs.size() - 1 && !is_derived_from_coinbase))
if (!(o == tx.vout.size() - 1 && !is_derived_from_coinbase))
{
td.m_flags |= WALLET_TRANSFER_DETAIL_FLAG_MINED_TRANSFER;
}
@ -574,7 +579,17 @@ void wallet2::accept_proposal(const crypto::hash& contract_id, uint64_t b_accept
finalize_tx_param ftp = AUTO_VAL_INIT(ftp);
prepare_transaction(construct_param, ftp, tx);
mark_transfers_as_spent(ftp.selected_transfers, std::string("contract <") + epee::string_tools::pod_to_hex(contract_id) + "> has been accepted with tx <" + epee::string_tools::pod_to_hex(get_transaction_hash(tx)) + ">");
finalize_transaction(ftp, tx, one_time_key, true);
try
{
finalize_transaction(ftp, tx, one_time_key, true);
}
catch (...)
{
clear_transfers_from_flag(ftp.selected_transfers, WALLET_TRANSFER_DETAIL_FLAG_SPENT, std::string("exception in finalize_transaction, tx: ") + epee::string_tools::pod_to_hex(get_transaction_hash(tx)));
throw;
}
print_tx_sent_message(tx, "(contract <" + epee::string_tools::pod_to_hex(contract_id) + ">)", construct_param.fee);
if (p_acceptance_tx != nullptr)
@ -696,7 +711,16 @@ void wallet2::request_cancel_contract(const crypto::hash& contract_id, uint64_t
currency::transaction tx = AUTO_VAL_INIT(tx);
crypto::secret_key sk = AUTO_VAL_INIT(sk);
mark_transfers_as_spent(ftp.selected_transfers, std::string("contract <") + epee::string_tools::pod_to_hex(contract_id) + "> has been requested for cancellaton with tx <" + epee::string_tools::pod_to_hex(get_transaction_hash(tx)) + ">");
finalize_transaction(ftp, tx, sk, true);
try
{
finalize_transaction(ftp, tx, sk, true);
}
catch (...)
{
clear_transfers_from_flag(ftp.selected_transfers, WALLET_TRANSFER_DETAIL_FLAG_SPENT, std::string("exception in finalize_transaction, tx: ") + epee::string_tools::pod_to_hex(get_transaction_hash(tx)));
throw;
}
print_tx_sent_message(tx, "(transport for cancel proposal)", fee);
@ -1111,7 +1135,9 @@ void wallet2::pull_blocks(size_t& blocks_added, std::atomic<bool>& stop)
currency::COMMAND_RPC_GET_BLOCKS_DIRECT::response res = AUTO_VAL_INIT(res);
get_short_chain_history(req.block_ids);
bool r = m_core_proxy->call_COMMAND_RPC_GET_BLOCKS_DIRECT(req, res);
THROW_IF_TRUE_WALLET_EX(!r, error::no_connection_to_daemon, "getblocks.bin");
if (!r)
throw error::no_connection_to_daemon(LOCATION_STR, "getblocks.bin");
if (res.status == CORE_RPC_STATUS_GENESIS_MISMATCH)
{
WLT_LOG_MAGENTA("Reseting genesis block...", LOG_LEVEL_0);
@ -1293,12 +1319,11 @@ void wallet2::scan_tx_pool(bool& has_related_alias_in_unconfirmed)
currency::COMMAND_RPC_GET_TX_POOL::response res = AUTO_VAL_INIT(res);
bool r = m_core_proxy->call_COMMAND_RPC_GET_TX_POOL(req, res);
if (res.status == CORE_RPC_STATUS_BUSY)
throw error::daemon_busy("", "get_tx_pool");
THROW_IF_TRUE_WALLET_EX(!r, error::no_connection_to_daemon, "get_tx_pool");
THROW_IF_TRUE_WALLET_EX(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_tx_pool");
throw error::daemon_busy(LOCATION_STR, "get_tx_pool");
if (!r)
throw error::no_connection_to_daemon(LOCATION_STR, "get_tx_pool");
THROW_IF_TRUE_WALLET_EX(res.status != CORE_RPC_STATUS_OK, error::get_blocks_error, res.status);
//- @#@ ----- debug
#ifdef _DEBUG
@ -1591,22 +1616,22 @@ void wallet2::refresh(size_t & blocks_fetched, bool& received_money, std::atomic
if(!added_blocks)
break;
}
catch (error::no_connection_to_daemon&)
{
blocks_fetched += added_blocks;
if (++try_count > 3)
return;
WLT_LOG_L2("no connection to the daemon, wait and try pull_blocks again (try_count: " << try_count << ", blocks_fetched: " << blocks_fetched << ")");
std::this_thread::sleep_for(std::chrono::seconds(3));
}
catch (const std::exception& e)
{
blocks_fetched += added_blocks;
if(try_count < 3)
{
WLT_LOG_L1("Another try pull_blocks (try_count=" << try_count << "), exception: " << e.what());
++try_count;
}
else
{
WLT_LOG_ERROR("pull_blocks failed, try_count=" << try_count << ", exception: " << e.what());
return;
//throw;
}
WLT_LOG_ERROR("refresh->pull_blocks failed, try_count: " << try_count << ", blocks_fetched: " << blocks_fetched << ", exception: " << e.what());
return;
}
}
if(last_tx_hash_id != (m_transfers.size() ? get_transaction_hash(m_transfers.back().m_ptx_wallet_info->m_tx) : null_hash))
received_money = true;
@ -1907,7 +1932,7 @@ void wallet2::load_keys2ki(bool create_if_not_exist, bool& need_to_resync)
{
WLT_LOG_RED("m_pending_key_images size: " << m_pending_key_images.size() << " is GREATER than m_pending_key_images_file_container size: " << m_pending_key_images_file_container.size(), LOG_LEVEL_0);
WLT_LOG_RED("UNRECOVERABLE ERROR, wallet stops", LOG_LEVEL_0);
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(false, "UNRECOVERABLE ERROR, wallet stops: m_pending_key_images > m_pending_key_images_file_container");
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(false, "UNRECOVERABLE ERROR, wallet stops: m_pending_key_images > m_pending_key_images_file_container" << ENDL << "Missing/wrong " << string_encoding::convert_to_ansii(m_pending_ki_file) << " file?");
}
}
//----------------------------------------------------------------------------------------------------
@ -2094,7 +2119,7 @@ void wallet2::store(const std::wstring& path_to_save, const std::string& passwor
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(!data_file.fail(), "failed to open binary wallet file for saving: " << tmp_file_path.string());
data_file << header_buff << keys_buff;
WLT_LOG_L0("Storing to " << tmp_file_path.string() << " ...");
WLT_LOG_L0("Storing to temporary file " << tmp_file_path.string() << " ...");
r = tools::portble_serialize_obj_to_stream(*this, data_file);
if (!r)
@ -2107,15 +2132,39 @@ void wallet2::store(const std::wstring& path_to_save, const std::string& passwor
data_file.flush();
data_file.close();
WLT_LOG_L1("Stored successfully to temporary file " << tmp_file_path.string());
// for the sake of safety perform a double-renaming: wallet file -> old tmp, new tmp -> wallet file, remove old tmp
boost::filesystem::path tmp_old_file_path = boost::filesystem::path(path_to_save);
tmp_old_file_path += L".oldtmp_" + std::to_wstring(ts);
if (boost::filesystem::is_regular_file(path_to_save))
{
boost::filesystem::rename(path_to_save, tmp_old_file_path);
WLT_LOG_L1("Renamed: " << ascii_path_to_save << " -> " << tmp_old_file_path.string());
}
boost::filesystem::rename(tmp_file_path, path_to_save);
boost::filesystem::remove(tmp_old_file_path);
WLT_LOG_L1("Renamed: " << tmp_file_path.string() << " -> " << ascii_path_to_save);
if (boost::filesystem::remove(tmp_old_file_path))
{
WLT_LOG_L1("Removed temporary file: " << tmp_old_file_path.string());
}
bool path_to_save_exists = boost::filesystem::is_regular_file(path_to_save);
bool tmp_file_path_exists = boost::filesystem::is_regular_file(tmp_file_path);
bool tmp_old_file_path_exists = boost::filesystem::is_regular_file(tmp_old_file_path);
if (path_to_save_exists && !tmp_file_path_exists && !tmp_old_file_path_exists)
{
WLT_LOG_L0("Wallet was successfully stored to " << ascii_path_to_save);
}
else
{
WLT_LOG_ERROR("Wallet stroing to " << ascii_path_to_save << " might not be successfull: path_to_save_exists=" << path_to_save_exists << ", tmp_file_path_exists=" << tmp_file_path_exists << ", tmp_old_file_path_exists=" << tmp_old_file_path_exists);
throw tools::error::wallet_common_error(LOCATION_STR, "Wallet file storing might not be successfull. Please make sure you have backed up your seed phrase and check log for details.");
}
}
//----------------------------------------------------------------------------------------------------
void wallet2::store_watch_only(const std::wstring& path_to_save, const std::string& password) const
@ -2271,6 +2320,36 @@ void wallet2::get_transfers(wallet2::transfer_container& incoming_transfers) con
incoming_transfers = m_transfers;
}
//----------------------------------------------------------------------------------------------------
bool wallet2::generate_packing_transaction_if_needed(currency::transaction& tx, uint64_t fake_outputs_number)
{
prepare_free_transfers_cache(0);
auto it = m_found_free_amounts.find(CURRENCY_BLOCK_REWARD);
if (it == m_found_free_amounts.end() || it->second.size() <= m_pos_mint_packing_size)
return false;
//let's check if we have at least WALLET_POS_MINT_PACKING_SIZE transactions which is ready to go
size_t count = 0;
for (auto it_ind = it->second.begin(); it_ind != it->second.end() && count < m_pos_mint_packing_size; it_ind++)
{
if (is_transfer_ready_to_go(m_transfers[*it_ind], fake_outputs_number))
++count;
}
if (count <= m_pos_mint_packing_size)
return false;
construct_tx_param ctp = get_default_construct_tx_param();
currency::tx_destination_entry de = AUTO_VAL_INIT(de);
de.addr.push_back(m_account.get_public_address());
de.amount = m_pos_mint_packing_size*CURRENCY_BLOCK_REWARD;
ctp.dsts.push_back(de);
ctp.perform_packing = true;
TRY_ENTRY();
transfer(ctp, tx, false, nullptr);
CATCH_ENTRY2(false);
return true;
}
//----------------------------------------------------------------------------------------------------
std::string wallet2::get_transfers_str(bool include_spent /*= true*/, bool include_unspent /*= true*/) const
{
static const char* header = "index amount g_index flags block tx out# key image";
@ -2389,6 +2468,16 @@ void wallet2::sign_transfer_files(const std::string& tx_sources_file, const std:
THROW_IF_FALSE_WALLET_CMN_ERR_EX(r, "failed to store signed tx to file " << signed_tx_file);
}
//----------------------------------------------------------------------------------------------------
bool wallet2::get_utxo_distribution(std::map<uint64_t, uint64_t>& distribution)
{
prepare_free_transfers_cache(0);
for (auto ent : m_found_free_amounts)
{
distribution[ent.first] = ent.second.size();
}
return true;
}
//----------------------------------------------------------------------------------------------------
void wallet2::submit_transfer(const std::string& signed_tx_blob, currency::transaction& tx)
{
// decrypt sources
@ -2495,7 +2584,7 @@ uint64_t wallet2::get_recent_transfers_total_count()
return m_transfer_history.size();
}
//----------------------------------------------------------------------------------------------------
void wallet2::get_recent_transfers_history(std::vector<wallet_public::wallet_transfer_info>& trs, size_t offset, size_t count)
void wallet2::get_recent_transfers_history(std::vector<wallet_public::wallet_transfer_info>& trs, size_t offset, size_t count, uint64_t& total)
{
if (offset >= m_transfer_history.size())
return;
@ -2506,6 +2595,7 @@ void wallet2::get_recent_transfers_history(std::vector<wallet_public::wallet_tra
stop = m_transfer_history.rend();
trs.insert(trs.end(), start, stop);
total = m_transfer_history.size();
}
//----------------------------------------------------------------------------------------------------
bool wallet2::get_transfer_address(const std::string& adr_str, currency::account_public_address& addr, std::string& payment_id)
@ -2662,6 +2752,11 @@ bool wallet2::fill_mining_context(mining_context& ctx)
}
//------------------------------------------------------------------
bool wallet2::try_mint_pos()
{
return try_mint_pos(m_account.get_public_address());
}
//------------------------------------------------------------------
bool wallet2::try_mint_pos(const currency::account_public_address& miner_address)
{
mining_context ctx = AUTO_VAL_INIT(ctx);
WLT_LOG_L1("Starting PoS mining iteration");
@ -2690,7 +2785,7 @@ bool wallet2::try_mint_pos()
if (ctx.rsp.status == CORE_RPC_STATUS_OK)
{
build_minted_block(ctx.sp, ctx.rsp);
build_minted_block(ctx.sp, ctx.rsp, miner_address);
}
WLT_LOG_L0("PoS mining iteration finished, status: " << ctx.rsp.status << ", used " << ctx.sp.pos_entries.size() << " entries with total amount: " << print_money_brief(pos_entries_amount));
@ -2738,6 +2833,13 @@ bool wallet2::build_minted_block(const currency::COMMAND_RPC_SCAN_POS::request&
tmpl_req.pos_index = req.pos_entries[rsp.index].index;
tmpl_req.extra_text = m_miner_text_info;
tmpl_req.stake_unlock_time = req.pos_entries[rsp.index].stake_unlock_time;
//generate packing tx
transaction pack_tx = AUTO_VAL_INIT(pack_tx);
if (generate_packing_transaction_if_needed(pack_tx, 0))
{
tx_to_blob(pack_tx, tmpl_req.explicit_transaction);
WLT_LOG_GREEN("Pacling inputs: " << pack_tx.vin.size() << " inputs consolidated", LOG_LEVEL_0);
}
m_core_proxy->call_COMMAND_RPC_GETBLOCKTEMPLATE(tmpl_req, tmpl_rsp);
WLT_CHECK_AND_ASSERT_MES(tmpl_rsp.status == CORE_RPC_STATUS_OK, false, "Failed to create block template after kernel hash found!");
@ -2781,10 +2883,13 @@ bool wallet2::build_minted_block(const currency::COMMAND_RPC_SCAN_POS::request&
WLT_LOG_GREEN("Block constructed <" << get_block_hash(b) << ">, sending to core...", LOG_LEVEL_0);
currency::COMMAND_RPC_SUBMITBLOCK::request subm_req = AUTO_VAL_INIT(subm_req);
currency::COMMAND_RPC_SUBMITBLOCK::response subm_rsp = AUTO_VAL_INIT(subm_rsp);
subm_req.push_back(epee::string_tools::buff_to_hex_nodelimer(t_serializable_object_to_blob(b)));
m_core_proxy->call_COMMAND_RPC_SUBMITBLOCK(subm_req, subm_rsp);
currency::COMMAND_RPC_SUBMITBLOCK2::request subm_req = AUTO_VAL_INIT(subm_req);
currency::COMMAND_RPC_SUBMITBLOCK2::response subm_rsp = AUTO_VAL_INIT(subm_rsp);
subm_req.b = t_serializable_object_to_blob(b);
if (tmpl_req.explicit_transaction.size())
subm_req.explicit_txs.push_back(hexemizer{ tmpl_req.explicit_transaction });
m_core_proxy->call_COMMAND_RPC_SUBMITBLOCK2(subm_req, subm_rsp);
if (subm_rsp.status != CORE_RPC_STATUS_OK)
{
WLT_LOG_ERROR("Constructed block is not accepted by core, status: " << subm_rsp.status);
@ -3024,6 +3129,7 @@ void wallet2::dump_trunsfers(std::stringstream& ss, bool verbose) const
}
else
{
boost::io::ios_flags_saver ifs(ss);
ss << "index amount spent_h g_index block block_ts flg tx out# key image" << ENDL;
for (size_t i = 0; i != m_transfers.size(); i++)
{
@ -3068,39 +3174,6 @@ bool wallet2::get_contracts(escrow_contracts_container& contracts)
return true;
}
//----------------------------------------------------------------------------------------------------
bool wallet2::get_fake_offers(std::list<bc_services::offer_details_ex>& offers, uint64_t amount)
{
for (uint64_t i = 0; i != amount; i++)
{
bc_services::offer_details od;
od.offer_type = rand() % 4;
od.amount_primary = rand();
od.amount_target = rand();
od.bonus = get_random_rext(10);
od.target = get_random_rext(10);
od.location_country = get_random_rext(6);
od.location_city = get_random_rext(10);
od.contacts = get_random_rext(20);
od.comment = get_random_rext(30);
od.payment_types = get_random_rext(10);
od.deal_option = get_random_rext(10);
od.category = get_random_rext(4);
od.expiration_time = 3;
crypto::hash tx_id = crypto::rand<crypto::hash>();
offers.push_back(bc_services::offer_details_ex());
bc_services::offer_details_ex& odl = offers.back();
static_cast<bc_services::offer_details&>(odl) = od;
odl.timestamp = m_core_runtime_config.get_core_time();
odl.index_in_tx = 0;
odl.tx_hash = tx_id;
odl.stopped = false;
odl.fee = 10000;
}
return true;
}
//----------------------------------------------------------------------------------------------------
void wallet2::build_escrow_release_templates(crypto::hash multisig_id,
uint64_t fee,
currency::transaction& tx_release_template,
@ -3404,11 +3477,41 @@ void wallet2::send_escrow_proposal(const bc_services::contract_private_details&
print_tx_sent_message(tx, "(from multisig)", fee);
}
//----------------------------------------------------------------------------------------------------
bool wallet2::prepare_tx_sources_for_packing(uint64_t items_to_pack, size_t fake_outputs_count, std::vector<currency::tx_source_entry>& sources, std::vector<uint64_t>& selected_indicies, uint64_t& found_money)
{
prepare_free_transfers_cache(fake_outputs_count);
auto it = m_found_free_amounts.find(CURRENCY_BLOCK_REWARD);
if (it == m_found_free_amounts.end() || it->second.size() < m_pos_mint_packing_size)
return false;
for (auto set_it = it->second.begin(); set_it != it->second.end() && selected_indicies.size() <= m_pos_mint_packing_size; )
{
if (is_transfer_ready_to_go(m_transfers[*set_it], fake_outputs_count))
{
found_money += it->first;
selected_indicies.push_back(*set_it);
WLT_LOG_L2("Selected index: " << *set_it << ", transfer_details: " << ENDL << epee::serialization::store_t_to_json(m_transfers[*set_it]));
it->second.erase(set_it++);
}
else
set_it++;
}
if (!it->second.size())
m_found_free_amounts.erase(it);
return prepare_tx_sources(fake_outputs_count, sources, selected_indicies, found_money);
}
//----------------------------------------------------------------------------------------------------
bool wallet2::prepare_tx_sources(uint64_t needed_money, size_t fake_outputs_count, uint64_t dust_threshold, std::vector<currency::tx_source_entry>& sources, std::vector<uint64_t>& selected_indicies, uint64_t& found_money)
{
found_money = select_transfers(needed_money, fake_outputs_count, dust_threshold, selected_indicies);
THROW_IF_FALSE_WALLET_EX_MES(found_money >= needed_money, error::not_enough_money, "wallet_dump: " << ENDL << dump_trunsfers(false), found_money, needed_money, 0);
return prepare_tx_sources(fake_outputs_count, sources, selected_indicies, found_money);
}
//----------------------------------------------------------------------------------------------------
bool wallet2::prepare_tx_sources(size_t fake_outputs_count, std::vector<currency::tx_source_entry>& sources, std::vector<uint64_t>& selected_indicies, uint64_t& found_money)
{
typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry out_entry;
typedef currency::tx_source_entry::output_entry tx_output_entry;
@ -3571,17 +3674,18 @@ void wallet2::add_sent_tx_detailed_info(const transaction& tx,
//----------------------------------------------------------------------------------------------------
void wallet2::mark_transfers_with_flag(const std::vector<uint64_t>& selected_transfers, uint32_t flag, const std::string& reason /* = empty_string */, bool throw_if_flag_already_set /* = false */)
{
if (throw_if_flag_already_set)
{
for (uint64_t i : selected_transfers)
{
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(i < m_transfers.size(), "invalid transfer index given: " << i << ", m_transfers.size() == " << m_transfers.size());
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX((m_transfers[i].m_flags & flag) == 0, "transfer #" << i << " already has flag " << flag << ": " << m_transfers[i].m_flags << ", transfer info:" << ENDL << epee::serialization::store_t_to_json(m_transfers[i]));
}
}
// check all selected transfers prior to flag change
for (uint64_t i : selected_transfers)
{
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(i < m_transfers.size(), "invalid transfer index given: " << i << ", m_transfers.size() == " << m_transfers.size());
if (throw_if_flag_already_set)
{
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX((m_transfers[i].m_flags & flag) == 0, "transfer #" << i << " already has flag " << flag << ": " << m_transfers[i].m_flags << ", transfer info:" << ENDL << epee::serialization::store_t_to_json(m_transfers[i]));
}
}
for (uint64_t i : selected_transfers)
{
uint32_t flags_before = m_transfers[i].m_flags;
m_transfers[i].m_flags |= flag;
WLT_LOG_L1("marking transfer #" << std::setfill('0') << std::right << std::setw(3) << i << " with flag " << flag << " : " << flags_before << " -> " << m_transfers[i].m_flags <<
@ -3589,16 +3693,22 @@ void wallet2::mark_transfers_with_flag(const std::vector<uint64_t>& selected_tra
}
}
//----------------------------------------------------------------------------------------------------
void wallet2::clear_transfers_from_flag(const std::vector<uint64_t>& selected_transfers, uint32_t flag, const std::string& reason /* = empty_string */)
void wallet2::clear_transfers_from_flag(const std::vector<uint64_t>& selected_transfers, uint32_t flag, const std::string& reason /* = empty_string */) noexcept
{
TRY_ENTRY();
for (uint64_t i : selected_transfers)
{
THROW_IF_TRUE_WALLET_EX(i >= m_transfers.size(), error::wallet_internal_error, "i >= m_transfers.size()");
if (i >= m_transfers.size())
{
WLT_LOG_ERROR("INTERNAL ERROR: i: " << i << " >= m_transfers.size() : " << m_transfers.size());
continue;
}
uint32_t flags_before = m_transfers[i].m_flags;
m_transfers[i].m_flags &= ~flag;
WLT_LOG_L1("clearing transfer #" << std::setfill('0') << std::right << std::setw(3) << i << " from flag " << flag << " : " << flags_before << " -> " << m_transfers[i].m_flags <<
(reason.empty() ? "" : ", reason: ") << reason);
}
CATCH_ENTRY_NO_RETURN();
}
//----------------------------------------------------------------------------------------------------
void wallet2::exception_handler()
@ -4047,7 +4157,9 @@ void wallet2::prepare_transaction(const construct_tx_param& ctp, finalize_tx_par
uint64_t found_money = 0;
TIME_MEASURE_START_MS(prepare_tx_sources_time);
if (ctp.multisig_id == currency::null_hash)
if (ctp.perform_packing)
prepare_tx_sources_for_packing(WALLET_DEFAULT_POS_MINT_PACKING_SIZE, 0, ftp.sources, ftp.selected_transfers, found_money);
else if (ctp.multisig_id == currency::null_hash)
prepare_tx_sources(needed_money, ctp.fake_outputs_count, ctp.dust_policy.dust_threshold, ftp.sources, ftp.selected_transfers, found_money);
else
prepare_tx_sources(ctp.multisig_id, ftp.sources, found_money);
@ -4069,6 +4181,7 @@ void wallet2::prepare_transaction(const construct_tx_param& ctp, finalize_tx_par
ftp.shuffle = ctp.shuffle;
ftp.flags = ctp.flags;
ftp.multisig_id = ctp.multisig_id;
ftp.spend_pub_key = m_account.get_public_address().m_spend_public_key;
/* TODO
WLT_LOG_GREEN("[prepare_transaction]: get_needed_money_time: " << get_needed_money_time << " ms"
@ -4079,12 +4192,15 @@ void wallet2::prepare_transaction(const construct_tx_param& ctp, finalize_tx_par
LOG_LEVEL_0);*/
}
//----------------------------------------------------------------------------------------------------
void wallet2::finalize_transaction(const finalize_tx_param& ftp, currency::transaction& tx, crypto::secret_key& tx_key, bool broadcast_tx)
void wallet2::finalize_transaction(const finalize_tx_param& ftp, currency::transaction& tx, crypto::secret_key& tx_key, bool broadcast_tx, bool store_tx_secret_key /* = true */)
{
// NOTE: if broadcast_tx == true callback rise_on_transfer2() may be called at the end of this function.
// That callback may call balance(), so it's important to have all used/spending transfers
// to be correctly marked with corresponding flags PRIOR to calling finalize_transaction()
// broadcasting tx without secret key storing is forbidden to avoid lost key issues
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(!broadcast_tx || store_tx_secret_key, "finalize_tx is requested to broadcast a tx without storing the key");
//TIME_MEASURE_START_MS(construct_tx_time);
bool r = currency::construct_tx(m_account.get_keys(),
ftp.sources,
@ -4116,10 +4232,11 @@ void wallet2::finalize_transaction(const finalize_tx_param& ftp, currency::trans
}
//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);
if (store_tx_secret_key)
m_tx_keys.insert(std::make_pair(get_transaction_hash(tx), tx_key));
//TIME_MEASURE_START(send_transaction_to_network_time);
if (broadcast_tx)
send_transaction_to_network(tx);
@ -4161,7 +4278,7 @@ void wallet2::transfer(const std::vector<currency::tx_destination_entry>& dsts,
bool send_to_network,
std::string* p_signed_tx_blob_str)
{
TIME_MEASURE_START(precalculation_time);
//TIME_MEASURE_START(precalculation_time);
construct_tx_param ctp = AUTO_VAL_INIT(ctp);
ctp.attachments = attachments;
ctp.crypt_address = currency::get_crypt_address_from_destinations(m_account.get_keys(), dsts);
@ -4177,8 +4294,65 @@ void wallet2::transfer(const std::vector<currency::tx_destination_entry>& dsts,
ctp.split_strategy_id = destination_split_strategy_id;
ctp.tx_outs_attr = tx_outs_attr;
ctp.unlock_time = unlock_time;
TIME_MEASURE_FINISH(precalculation_time);
//TIME_MEASURE_FINISH(precalculation_time);
transfer(ctp, tx, send_to_network, p_signed_tx_blob_str);
}
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
construct_tx_param wallet2::get_default_construct_tx_param_inital()
{
construct_tx_param ctp = AUTO_VAL_INIT(ctp);
ctp.fee = m_core_runtime_config.tx_default_fee;
ctp.dust_policy = tools::tx_dust_policy(DEFAULT_DUST_THRESHOLD);
ctp.split_strategy_id = tools::detail::ssi_digit;
ctp.tx_outs_attr = CURRENCY_TO_KEY_OUT_RELAXED;
ctp.shuffle = 0;
return ctp;
}
const construct_tx_param& wallet2::get_default_construct_tx_param()
{
static construct_tx_param ctp = get_default_construct_tx_param_inital();
return ctp;
}
//----------------------------------------------------------------------------------------------------
bool wallet2::store_unsigned_tx_to_file_and_reserve_transfers(const finalize_tx_param& ftp, const std::string& filename, std::string* p_unsigned_tx_blob_str /* = nullptr */)
{
TIME_MEASURE_START(store_unsigned_tx_time);
blobdata bl = t_serializable_object_to_blob(ftp);
crypto::chacha_crypt(bl, m_account.get_keys().m_view_secret_key);
if (!filename.empty())
{
bool r = epee::file_io_utils::save_string_to_file(filename, bl);
CHECK_AND_ASSERT_MES(r, false, "failed to store unsigned tx to " << filename);
LOG_PRINT_L0("Transaction stored to " << filename << ". You need to sign this tx using a full-access wallet.");
}
else
{
CHECK_AND_ASSERT_MES(p_unsigned_tx_blob_str != nullptr, false, "empty filename and p_unsigned_tx_blob_str == null");
*p_unsigned_tx_blob_str = bl;
}
TIME_MEASURE_FINISH(store_unsigned_tx_time);
// reserve transfers at the very end
TIME_MEASURE_START(mark_transfers_as_spent_time);
mark_transfers_with_flag(ftp.selected_transfers, WALLET_TRANSFER_DETAIL_FLAG_COLD_SIG_RESERVATION, std::string("cold sig reservation for money transfer"), true);
TIME_MEASURE_FINISH(mark_transfers_as_spent_time);
WLT_LOG_GREEN("[wallet::store_unsigned_tx_to_file_and_reserve_transfers]"
<< " store_unsigned_tx_time: " << print_fixed_decimal_point(store_unsigned_tx_time, 3)
<< ", mark_transfers_as_spent_time: " << print_fixed_decimal_point(mark_transfers_as_spent_time, 3)
, LOG_LEVEL_1);
return true;
}
//----------------------------------------------------------------------------------------------------
void wallet2::transfer(const construct_tx_param& ctp,
currency::transaction &tx,
bool send_to_network,
std::string* p_signed_tx_blob_str)
{
TIME_MEASURE_START(prepare_transaction_time);
finalize_tx_param ftp = AUTO_VAL_INIT(ftp);
prepare_transaction(ctp, ftp);
@ -4186,29 +4360,9 @@ void wallet2::transfer(const std::vector<currency::tx_destination_entry>& dsts,
if (m_watch_only)
{
TIME_MEASURE_START(store_unsigned_tx_time);
ftp.spend_pub_key = m_account.get_public_address().m_spend_public_key;
blobdata bl = t_serializable_object_to_blob(ftp);
crypto::chacha_crypt(bl, m_account.get_keys().m_view_secret_key);
epee::file_io_utils::save_string_to_file("zano_tx_unsigned", bl);
LOG_PRINT_L0("Transaction stored to unsigned_zano_tx. You need to sign this tx using a full-access wallet.");
if (p_signed_tx_blob_str != nullptr)
*p_signed_tx_blob_str = bl;
TIME_MEASURE_FINISH(store_unsigned_tx_time);
// unlock transfers at the very end
TIME_MEASURE_START(mark_transfers_as_spent_time);
mark_transfers_with_flag(ftp.selected_transfers, WALLET_TRANSFER_DETAIL_FLAG_COLD_SIG_RESERVATION, std::string("cold sig reservation for money transfer"), true);
TIME_MEASURE_FINISH(mark_transfers_as_spent_time);
WLT_LOG_GREEN("[wallet::transfer]"
<< " precalculation_time: " << print_fixed_decimal_point(precalculation_time, 3)
<< ", prepare_transaction_time: " << print_fixed_decimal_point(prepare_transaction_time, 3)
<< ", store_unsigned_tx_time: " << print_fixed_decimal_point(store_unsigned_tx_time, 3)
<< ", mark_transfers_as_spent_time: " << print_fixed_decimal_point(mark_transfers_as_spent_time, 3)
, LOG_LEVEL_0);
bool r = store_unsigned_tx_to_file_and_reserve_transfers(ftp, "zano_tx_unsigned", p_signed_tx_blob_str);
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(r, "failed to store unsigned tx");
WLT_LOG_GREEN("[wallet::transfer]" << " prepare_transaction_time: " << print_fixed_decimal_point(prepare_transaction_time, 3), LOG_LEVEL_0);
return;
}
@ -4231,14 +4385,268 @@ void wallet2::transfer(const std::vector<currency::tx_destination_entry>& dsts,
WLT_LOG_GREEN("[wallet::transfer]"
<< " precalculation_time: " << print_fixed_decimal_point(precalculation_time, 3)
//<< " precalculation_time: " << print_fixed_decimal_point(precalculation_time, 3)
<< ", prepare_transaction_time: " << print_fixed_decimal_point(prepare_transaction_time, 3)
<< ", finalize_transaction_time: " << print_fixed_decimal_point(finalize_transaction_time, 3)
<< ", mark_transfers_as_spent_time: " << print_fixed_decimal_point(mark_transfers_as_spent_time, 3)
, LOG_LEVEL_0);
print_tx_sent_message(tx, std::string() + "(transfer)", fee);
print_tx_sent_message(tx, std::string() + "(transfer)", ctp.fee);
}
//----------------------------------------------------------------------------------------------------
void wallet2::sweep_below(size_t fake_outs_count, const currency::account_public_address& destination_addr, uint64_t threshold_amount, const currency::payment_id_t& payment_id,
uint64_t fee, size_t& outs_total, uint64_t& amount_total, size_t& outs_swept, currency::transaction* p_result_tx /* = nullptr */, std::string* p_filename_or_unsigned_tx_blob_str /* = nullptr */)
{
static const size_t estimated_bytes_per_input = 78;
const size_t estimated_max_inputs = static_cast<size_t>(CURRENCY_MAX_TRANSACTION_BLOB_SIZE / (estimated_bytes_per_input * (fake_outs_count + 1.5))); // estimated number of maximum tx inputs under the tx size limit
const size_t tx_sources_for_querying_random_outs_max = estimated_max_inputs * 2;
bool r = false;
outs_total = 0;
amount_total = 0;
outs_swept = 0;
std::vector<size_t> selected_transfers;
selected_transfers.reserve(m_transfers.size());
for (size_t i = 0; i < m_transfers.size(); ++i)
{
const transfer_details& td = m_transfers[i];
uint64_t amount = td.amount();
if (amount < threshold_amount &&
is_transfer_ready_to_go(td, fake_outs_count))
{
selected_transfers.push_back(i);
outs_total += 1;
amount_total += amount;
}
}
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(!selected_transfers.empty(), "No spendable outputs meet the criterion");
// sort by amount descending in order to spend bigger outputs first
std::sort(selected_transfers.begin(), selected_transfers.end(), [this](size_t a, size_t b) { return m_transfers[b].amount() < m_transfers[a].amount(); });
// limit RPC request with reasonable number of sources
if (selected_transfers.size() > tx_sources_for_querying_random_outs_max)
selected_transfers.erase(selected_transfers.begin() + tx_sources_for_querying_random_outs_max, selected_transfers.end());
typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry out_entry;
typedef currency::tx_source_entry::output_entry tx_output_entry;
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response rpc_get_random_outs_resp = AUTO_VAL_INIT(rpc_get_random_outs_resp);
if (fake_outs_count > 0)
{
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request req = AUTO_VAL_INIT(req);
req.use_forced_mix_outs = false;
req.outs_count = fake_outs_count + 1;
for (size_t i : selected_transfers)
req.amounts.push_back(m_transfers[i].amount());
r = m_core_proxy->call_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS(req, rpc_get_random_outs_resp);
THROW_IF_FALSE_WALLET_EX(r, error::no_connection_to_daemon, "getrandom_outs.bin");
THROW_IF_FALSE_WALLET_EX(rpc_get_random_outs_resp.status != CORE_RPC_STATUS_BUSY, error::daemon_busy, "getrandom_outs.bin");
THROW_IF_FALSE_WALLET_EX(rpc_get_random_outs_resp.status == CORE_RPC_STATUS_OK, error::get_random_outs_error, rpc_get_random_outs_resp.status);
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(rpc_get_random_outs_resp.outs.size() == selected_transfers.size(),
"daemon returned wrong number of amounts for getrandom_outs.bin: " << rpc_get_random_outs_resp.outs.size() << ", requested: " << selected_transfers.size());
std::vector<COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount> scanty_outs;
for (COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& amount_outs : rpc_get_random_outs_resp.outs)
{
if (amount_outs.outs.size() < fake_outs_count)
scanty_outs.push_back(amount_outs);
}
THROW_IF_FALSE_WALLET_EX(scanty_outs.empty(), error::not_enough_outs_to_mix, scanty_outs, fake_outs_count);
}
finalize_tx_param ftp = AUTO_VAL_INIT(ftp);
if (!payment_id.empty())
set_payment_id_to_tx(ftp.attachments, payment_id);
// put encrypted payer info into the extra
ftp.crypt_address = destination_addr;
currency::tx_payer txp = AUTO_VAL_INIT(txp);
txp.acc_addr = m_account.get_public_address();
ftp.extra.push_back(txp);
ftp.flags = 0;
// ftp.multisig_id -- not required
// ftp.prepared_destinations -- will be filled by prepare_tx_destinations
// ftp.selected_transfers -- needed only at stage of broadcasting or storing unsigned tx
ftp.shuffle = false;
// ftp.sources -- will be filled in try_construct_tx
ftp.spend_pub_key = m_account.get_public_address().m_spend_public_key; // needed for offline signing
ftp.tx_outs_attr = CURRENCY_TO_KEY_OUT_RELAXED;
ftp.unlock_time = 0;
enum try_construct_result_t {rc_ok = 0, rc_too_few_outputs = 1, rc_too_many_outputs = 2, rc_create_tx_failed = 3 };
auto get_result_t_str = [](try_construct_result_t t) -> const char*
{ return t == rc_ok ? "rc_ok" : t == rc_too_few_outputs ? "rc_too_few_outputs" : t == rc_too_many_outputs ? "rc_too_many_outputs" : t == rc_create_tx_failed ? "rc_create_tx_failed" : "unknown"; };
auto try_construct_tx = [this, &selected_transfers, &rpc_get_random_outs_resp, &fake_outs_count, &fee, &destination_addr]
(size_t st_index_upper_boundary, finalize_tx_param& ftp, uint64_t& amount_swept) -> try_construct_result_t
{
// prepare inputs
amount_swept = 0;
ftp.sources.clear();
ftp.sources.resize(st_index_upper_boundary);
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(st_index_upper_boundary <= selected_transfers.size(), "index_upper_boundary = " << st_index_upper_boundary << ", selected_transfers.size() = " << selected_transfers.size());
for (size_t st_index = 0; st_index < st_index_upper_boundary; ++st_index)
{
currency::tx_source_entry& src = ftp.sources[st_index];
size_t tr_index = selected_transfers[st_index];
transfer_details& td = m_transfers[tr_index];
src.transfer_index = tr_index;
src.amount = td.amount();
amount_swept += src.amount;
// populate src.outputs with mix-ins
if (rpc_get_random_outs_resp.outs.size())
{
rpc_get_random_outs_resp.outs[st_index].outs.sort([](const out_entry& a, const out_entry& b) { return a.global_amount_index < b.global_amount_index; });
for (out_entry& daemon_oe : rpc_get_random_outs_resp.outs[st_index].outs)
{
if (td.m_global_output_index == daemon_oe.global_amount_index)
continue;
tx_output_entry oe;
oe.first = daemon_oe.global_amount_index;
oe.second = daemon_oe.out_key;
src.outputs.push_back(oe);
if (src.outputs.size() >= fake_outs_count)
break;
}
}
// insert real output into src.outputs
auto it_to_insert = std::find_if(src.outputs.begin(), src.outputs.end(), [&](const tx_output_entry& a)
{
if (a.first.type().hash_code() == typeid(uint64_t).hash_code())
return boost::get<uint64_t>(a.first) >= td.m_global_output_index;
return false; // TODO: implement deterministics real output placement in case there're ref_by_id outs
});
tx_output_entry real_oe;
real_oe.first = td.m_global_output_index;
real_oe.second = boost::get<txout_to_key>(td.m_ptx_wallet_info->m_tx.vout[td.m_internal_output_index].target).key;
auto inserted_it = src.outputs.insert(it_to_insert, real_oe);
src.real_out_tx_key = get_tx_pub_key_from_extra(td.m_ptx_wallet_info->m_tx);
src.real_output = inserted_it - src.outputs.begin();
src.real_output_in_tx_index = td.m_internal_output_index;
//detail::print_source_entry(src);
}
if (amount_swept <= fee)
return rc_too_few_outputs;
// try to construct a transaction
std::vector<currency::tx_destination_entry> dsts({ tx_destination_entry(amount_swept - fee, destination_addr) });
prepare_tx_destinations(0, 0, detail::ssi_digit, tools::tx_dust_policy(), dsts, ftp.prepared_destinations);
currency::transaction tx = AUTO_VAL_INIT(tx);
crypto::secret_key tx_key = AUTO_VAL_INIT(tx_key);
try
{
finalize_transaction(ftp, tx, tx_key, false, false);
}
catch (error::tx_too_big)
{
return rc_too_many_outputs;
}
catch (...)
{
return rc_create_tx_failed;
}
return rc_ok;
};
size_t st_index_upper_boundary = std::min(selected_transfers.size(), estimated_max_inputs);
uint64_t amount_swept = 0;
try_construct_result_t res = try_construct_tx(st_index_upper_boundary, ftp, amount_swept);
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(res != rc_too_few_outputs, st_index_upper_boundary << " biggest unspent outputs have total amount of " << print_money_brief(amount_swept)
<< " which is less than required fee: " << print_money_brief(fee) << ", transaction cannot be constructed");
if (res == rc_too_many_outputs)
{
WLT_LOG_L1("sweep_below: first try of try_construct_tx(" << st_index_upper_boundary << ") returned " << get_result_t_str(res));
size_t low_bound = 0;
size_t high_bound = st_index_upper_boundary;
finalize_tx_param ftp_ok = ftp;
for (;;)
{
if (low_bound + 1 >= high_bound)
{
st_index_upper_boundary = low_bound;
res = rc_ok;
ftp = ftp_ok;
break;
}
st_index_upper_boundary = (low_bound + high_bound) / 2;
try_construct_result_t res = try_construct_tx(st_index_upper_boundary, ftp, amount_swept);
WLT_LOG_L1("sweep_below: try_construct_tx(" << st_index_upper_boundary << ") returned " << get_result_t_str(res));
if (res == rc_ok)
{
low_bound = st_index_upper_boundary;
ftp_ok = ftp;
}
else if (res == rc_too_many_outputs)
{
high_bound = st_index_upper_boundary;
}
else
break;
}
}
if (res != rc_ok)
{
uint64_t amount_min = UINT64_MAX, amount_max = 0, amount_sum = 0;
for (auto& i : selected_transfers)
{
uint64_t amount = m_transfers[i].amount();
amount_min = std::min(amount_min, amount);
amount_max = std::max(amount_max, amount);
amount_sum += amount;
}
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(false, "try_construct_tx failed with result: " << get_result_t_str(res) << " (" << res << ")" <<
", selected_transfers stats:\n" <<
" outs: " << selected_transfers.size() << ENDL <<
" amount min: " << print_money(amount_min) << ENDL <<
" amount max: " << print_money(amount_max) << ENDL <<
" amount avg: " << (selected_transfers.empty() ? std::string("n/a") : print_money(amount_sum / selected_transfers.size())));
}
// populate ftp.selected_transfers from ftp.sources
ftp.selected_transfers.clear();
for (size_t i = 0; i < ftp.sources.size(); ++i)
ftp.selected_transfers.push_back(ftp.sources[i].transfer_index);
outs_swept = ftp.sources.size();
if (m_watch_only)
{
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(p_filename_or_unsigned_tx_blob_str != nullptr, "p_filename_or_unsigned_tx_blob_str is null");
bool r = store_unsigned_tx_to_file_and_reserve_transfers(ftp, *p_filename_or_unsigned_tx_blob_str, p_filename_or_unsigned_tx_blob_str);
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(r, "failed to store unsigned tx");
return;
}
mark_transfers_as_spent(ftp.selected_transfers, "sweep_below");
transaction local_tx;
transaction* p_tx = p_result_tx != nullptr ? p_result_tx : &local_tx;
*p_tx = AUTO_VAL_INIT_T(transaction);
try
{
crypto::secret_key sk = AUTO_VAL_INIT(sk);
finalize_transaction(ftp, *p_tx, sk, true);
}
catch (...)
{
clear_transfers_from_flag(ftp.selected_transfers, WALLET_TRANSFER_DETAIL_FLAG_SPENT, std::string("exception on sweep_below, tx id (might be wrong): ") + epee::string_tools::pod_to_hex(get_transaction_hash(*p_tx)));
throw;
}
}
} // namespace tools

View file

@ -42,6 +42,8 @@
#define WALLET_DEFAULT_TX_SPENDABLE_AGE 10
#define WALLET_POS_MINT_CHECK_HEIGHT_INTERVAL 1
#define WALLET_DEFAULT_POS_MINT_PACKING_SIZE 100
#undef LOG_DEFAULT_CHANNEL
#define LOG_DEFAULT_CHANNEL "wallet"
ENABLE_CHANNEL_BY_DEFAULT("wallet");
@ -256,6 +258,7 @@ namespace tools
currency::account_public_address crypt_address;
uint8_t tx_outs_attr;
bool shuffle;
bool perform_packing;
};
struct finalize_tx_param
@ -313,7 +316,8 @@ namespace tools
m_last_sync_percent(0),
m_do_rise_transfer(false),
m_watch_only(false),
m_last_pow_block_h(0)
m_last_pow_block_h(0),
m_pos_mint_packing_size(WALLET_DEFAULT_POS_MINT_PACKING_SIZE)
{};
public:
wallet2() : m_stop(false),
@ -326,7 +330,8 @@ namespace tools
m_do_rise_transfer(false),
m_log_prefix("???"),
m_watch_only(false),
m_last_pow_block_h(0)
m_last_pow_block_h(0),
m_pos_mint_packing_size(WALLET_DEFAULT_POS_MINT_PACKING_SIZE)
{
m_core_runtime_config = currency::get_default_core_runtime_config();
};
@ -459,7 +464,7 @@ namespace tools
currency::account_base& get_account() { return m_account; }
const currency::account_base& get_account() const { return m_account; }
void get_recent_transfers_history(std::vector<wallet_public::wallet_transfer_info>& trs, size_t offset, size_t count);
void get_recent_transfers_history(std::vector<wallet_public::wallet_transfer_info>& trs, size_t offset, size_t count, uint64_t& total);
uint64_t get_recent_transfers_total_count();
void get_unconfirmed_transfers(std::vector<wallet_public::wallet_transfer_info>& trs);
void init(const std::string& daemon_address = "http://localhost:8080");
@ -491,6 +496,7 @@ namespace tools
bool set_core_proxy(const std::shared_ptr<i_core_proxy>& proxy);
void set_pos_mint_packing_size(uint64_t new_size);
std::shared_ptr<i_core_proxy> get_core_proxy();
uint64_t balance() const;
uint64_t balance(uint64_t& unloked, uint64_t& awaiting_in, uint64_t& awaiting_out, uint64_t& mined) const;
@ -538,6 +544,11 @@ namespace tools
const std::vector<currency::attachment_v>& attachments,
currency::transaction& tx);
void transfer(const construct_tx_param& ctp,
currency::transaction &tx,
bool send_to_network,
std::string* p_signed_tx_blob_str);
template<typename destination_split_strategy_t>
void transfer_from_contract(
const std::list<currency::account_keys>& owner_keys,
@ -611,6 +622,9 @@ namespace tools
void submit_transfer(const std::string& signed_tx_blob, currency::transaction& tx);
void submit_transfer_files(const std::string& signed_tx_file, currency::transaction& tx);
void sweep_below(size_t fake_outs_count, const currency::account_public_address& destination_addr, uint64_t threshold_amount, const currency::payment_id_t& payment_id,
uint64_t fee, size_t& outs_total, uint64_t& amount_total, size_t& outs_swept, currency::transaction* p_result_tx = nullptr, std::string* p_filename_or_unsigned_tx_blob_str = nullptr);
bool get_transfer_address(const std::string& adr_str, currency::account_public_address& addr, std::string& payment_id);
uint64_t get_blockchain_current_height() const { return m_blockchain.size(); }
@ -670,6 +684,7 @@ namespace tools
//PoS
//synchronous version of function
bool try_mint_pos();
bool try_mint_pos(const currency::account_public_address& miner_address); // block reward will be sent to miner_address, stake will be returned back to the wallet
//for unit tests
friend class ::test_generator;
@ -698,7 +713,6 @@ namespace tools
bool reset_password(const std::string& pass);
bool is_password_valid(const std::string& pass);
bool get_actual_offers(std::list<bc_services::offer_details_ex>& offers);
bool get_fake_offers(std::list<bc_services::offer_details_ex>& offers, uint64_t amount);
bool process_contract_info(wallet_public::wallet_transfer_info& wti, const std::vector<currency::payload_items_v>& decrypted_attach);
bool handle_proposal(wallet_public::wallet_transfer_info& wti, const bc_services::proposal_body& prop);
void accept_proposal(const crypto::hash& contract_id, uint64_t b_acceptance_fee, currency::transaction* p_acceptance_tx = nullptr);
@ -725,10 +739,11 @@ namespace tools
bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key) const;
void prepare_transaction(const construct_tx_param& ctp, finalize_tx_param& ftp, const currency::transaction& tx_for_mode_separate = currency::transaction());
void finalize_transaction(const finalize_tx_param& ftp, currency::transaction& tx, crypto::secret_key& tx_key, bool broadcast_tx);
void finalize_transaction(const finalize_tx_param& ftp, currency::transaction& tx, crypto::secret_key& tx_key, bool broadcast_tx, bool store_tx_secret_key = true);
std::string get_log_prefix() const { return m_log_prefix; }
static uint64_t get_max_unlock_time_from_receive_indices(const currency::transaction& tx, const money_transfer2_details& td);
bool get_utxo_distribution(std::map<uint64_t, uint64_t>& distribution);
private:
void add_transfers_to_expiration_list(const std::vector<uint64_t>& selected_transfers, uint64_t expiration, uint64_t change_amount, const crypto::hash& related_tx_id);
@ -784,7 +799,9 @@ private:
void process_genesis_if_needed(const currency::block& genesis);
bool build_escrow_proposal(bc_services::contract_private_details& ecrow_details, uint64_t fee, uint64_t unlock_time, currency::tx_service_attachment& att, std::vector<uint64_t>& selected_indicies);
bool prepare_tx_sources(uint64_t needed_money, size_t fake_outputs_count, uint64_t dust_threshold, std::vector<currency::tx_source_entry>& sources, std::vector<uint64_t>& selected_indicies, uint64_t& found_money);
bool prepare_tx_sources(size_t fake_outputs_count, std::vector<currency::tx_source_entry>& sources, std::vector<uint64_t>& selected_indicies, uint64_t& found_money);
bool prepare_tx_sources(crypto::hash multisig_id, std::vector<currency::tx_source_entry>& sources, uint64_t& found_money);
bool prepare_tx_sources_for_packing(uint64_t items_to_pack, size_t fake_outputs_count, std::vector<currency::tx_source_entry>& sources, std::vector<uint64_t>& selected_indicies, uint64_t& found_money);
uint64_t get_needed_money(uint64_t fee, const std::vector<currency::tx_destination_entry>& dsts);
void prepare_tx_destinations(uint64_t needed_money,
uint64_t found_money,
@ -800,7 +817,9 @@ private:
void change_contract_state(wallet_public::escrow_contract_details_basic& contract, uint32_t new_state, const crypto::hash& contract_id, const wallet_public::wallet_transfer_info& wti) const;
void change_contract_state(wallet_public::escrow_contract_details_basic& contract, uint32_t new_state, const crypto::hash& contract_id, const std::string& reason = "internal intention") const;
construct_tx_param get_default_construct_tx_param_inital();
const construct_tx_param& get_default_construct_tx_param();
uint64_t get_tx_expiration_median() const;
@ -837,12 +856,13 @@ private:
const std::vector<uint64_t>& selected_indicies);
void mark_transfers_as_spent(const std::vector<uint64_t>& selected_transfers, const std::string& reason = std::string());
void mark_transfers_with_flag(const std::vector<uint64_t>& selected_transfers, uint32_t flag, const std::string& reason = std::string(), bool throw_if_flag_already_set = false);
void clear_transfers_from_flag(const std::vector<uint64_t>& selected_transfers, uint32_t flag, const std::string& reason = std::string());
void clear_transfers_from_flag(const std::vector<uint64_t>& selected_transfers, uint32_t flag, const std::string& reason = std::string()) noexcept;
void exception_handler();
void exception_handler() const;
uint64_t get_minimum_allowed_fee_for_contract(const crypto::hash& ms_id);
void check_for_free_space_and_throw_if_it_lacks(const std::wstring& path, uint64_t exact_size_needed_if_known = UINT64_MAX);
bool generate_packing_transaction_if_needed(currency::transaction& tx, uint64_t fake_outputs_number);
bool store_unsigned_tx_to_file_and_reserve_transfers(const finalize_tx_param& ftp, const std::string& filename, std::string* p_unsigned_tx_blob_str = nullptr);
currency::account_base m_account;
@ -855,6 +875,7 @@ private:
std::atomic<uint64_t> m_local_bc_height; //temporary workaround
std::atomic<uint64_t> m_last_bc_timestamp;
bool m_do_rise_transfer;
uint64_t m_pos_mint_packing_size;
transfer_container m_transfers;
multisig_transfer_container m_multisig_transfers;

View file

@ -375,6 +375,45 @@ namespace wallet_public
};
};
struct COMMAND_SWEEP_BELOW
{
struct request
{
uint64_t mixin;
std::string address;
uint64_t amount;
std::string payment_id_hex;
uint64_t fee;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(mixin)
KV_SERIALIZE(address)
KV_SERIALIZE(amount)
KV_SERIALIZE(payment_id_hex)
KV_SERIALIZE(fee)
END_KV_SERIALIZE_MAP()
};
struct response
{
std::string tx_hash;
std::string tx_unsigned_hex;
uint64_t outs_total;
uint64_t amount_total;
uint64_t outs_swept;
uint64_t amount_swept;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash)
KV_SERIALIZE(tx_unsigned_hex)
KV_SERIALIZE(outs_total)
KV_SERIALIZE(amount_total)
KV_SERIALIZE(outs_swept)
KV_SERIALIZE(amount_swept)
END_KV_SERIALIZE_MAP()
};
};
struct COMMAND_SIGN_TRANSFER
{
struct request

View file

@ -51,7 +51,7 @@ namespace tools
wallet_rpc_server::wallet_rpc_server(wallet2& w):m_wallet(w), m_do_mint(false), m_deaf(false)
{}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::run(bool do_mint, bool offline_mode)
bool wallet_rpc_server::run(bool do_mint, bool offline_mode, const currency::account_public_address& miner_address)
{
static const uint64_t wallet_rpt_idle_work_period_ms = 2000;
@ -59,26 +59,41 @@ namespace tools
if (!offline_mode)
{
m_net_server.add_idle_handler([this]() -> bool
m_net_server.add_idle_handler([this, &miner_address]() -> bool
{
size_t blocks_fetched = 0;
bool received_money = false, ok = false;
std::atomic<bool> stop(false);
LOG_PRINT_L2("wallet RPC idle: refreshing...");
m_wallet.refresh(blocks_fetched, received_money, ok, stop);
if (stop)
try
{
LOG_PRINT_L1("wallet RPC idle: refresh failed");
return true;
}
size_t blocks_fetched = 0;
bool received_money = false, ok = false;
std::atomic<bool> stop(false);
LOG_PRINT_L2("wallet RPC idle: refreshing...");
m_wallet.refresh(blocks_fetched, received_money, ok, stop);
if (stop)
{
LOG_PRINT_L1("wallet RPC idle: refresh failed");
return true;
}
if (m_do_mint)
if (m_do_mint)
{
bool has_related_alias_in_unconfirmed = false;
LOG_PRINT_L2("wallet RPC idle: scanning tx pool...");
m_wallet.scan_tx_pool(has_related_alias_in_unconfirmed);
LOG_PRINT_L2("wallet RPC idle: trying to do PoS iteration...");
m_wallet.try_mint_pos(miner_address);
}
}
catch (error::no_connection_to_daemon&)
{
bool has_related_alias_in_unconfirmed = false;
LOG_PRINT_L2("wallet RPC idle: scanning tx pool...");
m_wallet.scan_tx_pool(has_related_alias_in_unconfirmed);
LOG_PRINT_L2("wallet RPC idle: tring to do PoS iteration...");
m_wallet.try_mint_pos();
LOG_PRINT_RED("no connection to the daemon", LOG_LEVEL_0);
}
catch(std::exception& e)
{
LOG_ERROR("exeption caught in wallet_rpc_server::idle_handler: " << e.what());
}
catch(...)
{
LOG_ERROR("unknown exeption caught in wallet_rpc_server::idle_handler");
}
return true;
@ -387,6 +402,93 @@ namespace tools
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_sweep_below(const wallet_public::COMMAND_SWEEP_BELOW::request& req, wallet_public::COMMAND_SWEEP_BELOW::response& res, epee::json_rpc::error& er, connection_context& cntx)
{
currency::payment_id_t payment_id;
if (!req.payment_id_hex.empty() && !currency::parse_payment_id_from_hex_str(req.payment_id_hex, payment_id))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
er.message = std::string("Invalid payment id: ") + req.payment_id_hex;
return false;
}
currency::account_public_address addr;
currency::payment_id_t integrated_payment_id;
if (!m_wallet.get_transfer_address(req.address, addr, integrated_payment_id))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
er.message = std::string("Invalid address: ") + req.address;
return false;
}
if (!integrated_payment_id.empty())
{
if (!payment_id.empty())
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
er.message = std::string("address ") + req.address + " has integrated payment id " + epee::string_tools::buff_to_hex_nodelimer(integrated_payment_id) +
" which is incompatible with payment id " + epee::string_tools::buff_to_hex_nodelimer(payment_id) + " that was already assigned to this transfer";
return false;
}
payment_id = integrated_payment_id;
}
if (req.fee < m_wallet.get_core_runtime_config().tx_pool_min_fee)
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_ARGUMENT;
er.message = std::string("Given fee is too low: ") + epee::string_tools::num_to_string_fast(req.fee) + ", minimum is: " + epee::string_tools::num_to_string_fast(m_wallet.get_core_runtime_config().tx_pool_min_fee);
return false;
}
try
{
currency::transaction tx = AUTO_VAL_INIT(tx);
size_t outs_total = 0, outs_swept = 0;
uint64_t amount_total = 0, amount_swept = 0;
std::string unsigned_tx_blob_str;
m_wallet.sweep_below(req.mixin, addr, req.amount, payment_id, req.fee, outs_total, amount_total, outs_swept, &tx, &unsigned_tx_blob_str);
get_inputs_money_amount(tx, amount_swept);
res.amount_swept = amount_swept;
res.amount_total = amount_total;
res.outs_swept = outs_swept;
res.outs_total = outs_total;
if (m_wallet.is_watch_only())
{
res.tx_unsigned_hex = epee::string_tools::buff_to_hex_nodelimer(unsigned_tx_blob_str); // watch-only wallets can't sign and relay transactions
// leave res.tx_hash empty, because tx has will change after signing
}
else
{
res.tx_hash = string_tools::pod_to_hex(currency::get_transaction_hash(tx));
}
}
catch (const tools::error::daemon_busy& e)
{
er.code = WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY;
er.message = e.what();
return false;
}
catch (const std::exception& e)
{
er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR;
er.message = e.what();
return false;
}
catch (...)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR";
return false;
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_sign_transfer(const wallet_public::COMMAND_SIGN_TRANSFER::request& req, wallet_public::COMMAND_SIGN_TRANSFER::response& res, epee::json_rpc::error& er, connection_context& cntx)
{
WALLET_RPC_BEGIN_TRY_ENTRY();

View file

@ -32,7 +32,7 @@ namespace tools
static void init_options(boost::program_options::options_description& desc);
bool init(const boost::program_options::variables_map& vm);
bool run(bool do_mint, bool offline_mode);
bool run(bool do_mint, bool offline_mode, const currency::account_public_address& miner_address);
bool handle_http_request(const epee::net_utils::http::http_request_info& query_info, epee::net_utils::http::http_response_info& response, connection_context& m_conn_context);
@ -47,6 +47,7 @@ namespace tools
MAP_JON_RPC_WE("get_bulk_payments", on_get_bulk_payments, wallet_public::COMMAND_RPC_GET_BULK_PAYMENTS)
MAP_JON_RPC_WE("make_integrated_address", on_make_integrated_address, wallet_public::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS)
MAP_JON_RPC_WE("split_integrated_address", on_split_integrated_address, wallet_public::COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS)
MAP_JON_RPC_WE("sweep_below", on_sweep_below, wallet_public::COMMAND_SWEEP_BELOW)
MAP_JON_RPC_WE("sign_transfer", on_sign_transfer, wallet_public::COMMAND_SIGN_TRANSFER)
MAP_JON_RPC_WE("submit_transfer", on_submit_transfer, wallet_public::COMMAND_SUBMIT_TRANSFER)
//contracts API
@ -73,6 +74,7 @@ namespace tools
bool on_get_bulk_payments(const wallet_public::COMMAND_RPC_GET_BULK_PAYMENTS::request& req, wallet_public::COMMAND_RPC_GET_BULK_PAYMENTS::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_make_integrated_address(const wallet_public::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::request& req, wallet_public::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_split_integrated_address(const wallet_public::COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS::request& req, wallet_public::COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_sweep_below(const wallet_public::COMMAND_SWEEP_BELOW::request& req, wallet_public::COMMAND_SWEEP_BELOW::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_sign_transfer(const wallet_public::COMMAND_SIGN_TRANSFER::request& req, wallet_public::COMMAND_SIGN_TRANSFER::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_submit_transfer(const wallet_public::COMMAND_SUBMIT_TRANSFER::request& req, wallet_public::COMMAND_SUBMIT_TRANSFER::response& res, epee::json_rpc::error& er, connection_context& cntx);

View file

@ -63,7 +63,7 @@ bool clean_data_directory()
for(auto& entry : boost::make_iterator_range(boost::filesystem::directory_iterator(config_folder), {}))
{
const std::string& fn_str = entry.path().filename().string();
std::string fn_str = entry.path().filename().string();
if (files.count(fn_str) != 0)
{
entries_to_remove.push_back(entry.path());
@ -222,7 +222,7 @@ bool gen_and_play_intermitted_by_blockchain_saveload(const char* const genclass_
auto after_init_cb = [crc, &alt_chains, &cps, core_time](currency::core& c)
{
c.get_blockchain_storage().set_core_runtime_config(crc); // restore runtime config
c.get_blockchain_storage().set_alternative_chains(alt_chains); // restore alt chains
c.get_blockchain_storage().set_alternative_chains(alt_chains); // restore alt chains
c.get_blockchain_storage().get_checkpoints() = cps; // restore checkpoints
test_core_time::adjust(core_time); // restore core time
};
@ -231,7 +231,7 @@ bool gen_and_play_intermitted_by_blockchain_saveload(const char* const genclass_
{
core_time = test_core_time::get_time(); // save core time
crc = c.get_blockchain_storage().get_core_runtime_config(); // save runtime config
c.get_blockchain_storage().get_alternative_chains(alt_chains); // save altchains
c.get_blockchain_storage().get_alternative_chains(alt_chains); // save altchains
cps = c.get_blockchain_storage().get_checkpoints(); // save checkpoints
if (last_iter)
core_state_after.fill(c);
@ -250,7 +250,7 @@ bool gen_and_play_intermitted_by_blockchain_saveload(const char* const genclass_
#define GENERATE_AND_PLAY(genclass) \
if(!postponed_tests.count(#genclass) && (run_single_test.empty() || run_single_test == #genclass)) \
if(!postponed_tests.count(#genclass) && (run_single_test.empty() || std::string::npos != std::string(#genclass).find(run_single_test))) \
{ \
TIME_MEASURE_START_MS(t); \
++tests_count; \
@ -359,6 +359,7 @@ private:
size_t m_ev_index;
test_core_listener* m_core_listener;
mutable std::unordered_map<crypto::hash, currency::transaction> m_onboard_txs;
bool m_txs_kept_by_block;
bool m_skip_txs_blobsize_check;
@ -389,11 +390,19 @@ public:
size_t pool_size = m_c.get_pool_transactions_count();
currency::tx_verification_context tvc = AUTO_VAL_INIT(tvc);
m_c.handle_incoming_tx(tx_blob, tvc, m_txs_kept_by_block);
bool tx_added = pool_size + 1 == m_c.get_pool_transactions_count();
bool r = check_tx_verification_context(tvc, tx_added, m_ev_index, tx, m_validator);
LOCAL_ASSERT(r);
CHECK_AND_NO_ASSERT_MES(r, false, "tx verification context check failed");
if (m_txs_kept_by_block)
{
m_onboard_txs[get_transaction_hash(tx)] = tx;
}
else
{
m_c.handle_incoming_tx(tx_blob, tvc, m_txs_kept_by_block);
bool tx_added = pool_size + 1 == m_c.get_pool_transactions_count();
bool r = check_tx_verification_context(tvc, tx_added, m_ev_index, tx, m_validator);
LOCAL_ASSERT(r);
CHECK_AND_NO_ASSERT_MES(r, false, "tx verification context check failed");
}
return true;
}
@ -403,6 +412,7 @@ public:
m_core_listener->before_block_pushed_to_core(b, blob_blk, m_c);
currency::block_verification_context bvc = AUTO_VAL_INIT(bvc);
bvc.m_onboard_transactions.swap(m_onboard_txs);
m_c.handle_incoming_block(blob_blk, bvc);
bool r = check_block_verification_context(bvc, m_ev_index, b, m_validator);
CHECK_AND_NO_ASSERT_MES(r, false, "block verification context check failed");
@ -655,7 +665,7 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_options, arg_run_single_test);
command_line::add_arg(desc_options, arg_enable_debug_asserts);
command_line::add_arg(desc_options, command_line::arg_data_dir, std::string("."));
command_line::add_arg(desc_options, command_line::arg_db_engine);
currency::core::init_options(desc_options);
bool r = command_line::handle_error_helper(desc_options, [&]()
{
@ -729,6 +739,7 @@ int main(int argc, char* argv[])
#undef MARK_TEST_AS_POSTPONED
GENERATE_AND_PLAY(pos_minting_tx_packing);
GENERATE_AND_PLAY(multisig_wallet_test);
GENERATE_AND_PLAY(multisig_wallet_test_many_dst);
@ -764,7 +775,7 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY(escrow_cancellation_acceptance_expiration);
// GENERATE_AND_PLAY(escrow_proposal_acceptance_in_alt_chain); -- work in progress
GENERATE_AND_PLAY(escrow_zero_amounts);
GENERATE_AND_PLAY(escrow_acceptance_and_balance);
GENERATE_AND_PLAY(escrow_balance);
GENERATE_AND_PLAY(escrow_altchain_meta_test<0>);
GENERATE_AND_PLAY(escrow_altchain_meta_test<1>);
@ -846,6 +857,7 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY(wallet_outputs_with_same_key_image);
GENERATE_AND_PLAY(wallet_unconfirmed_tx_expiration);
GENERATE_AND_PLAY(wallet_unconfimed_tx_balance);
GENERATE_AND_PLAY(packing_outputs_on_pos_minting_wallet);
GENERATE_AND_PLAY(wallet_rpc_integrated_address);
GENERATE_AND_PLAY(wallet_rpc_integrated_address_transfer);

View file

@ -42,7 +42,7 @@ struct gen_double_spend_in_the_same_block : public gen_double_spend_base< gen_do
{
static const uint64_t send_amount = MK_TEST_COINS(17);
static const bool has_invalid_tx = !txs_kept_by_block;
static const size_t expected_pool_txs_count = has_invalid_tx ? 1 : 2;
static const size_t expected_pool_txs_count = 1;
static const uint64_t expected_bob_balance = send_amount;
static const uint64_t expected_alice_balance = 0;
@ -55,7 +55,7 @@ struct gen_double_spend_in_different_blocks : public gen_double_spend_base< gen_
{
static const uint64_t send_amount = MK_TEST_COINS(17);
static const bool has_invalid_tx = !txs_kept_by_block;
static const size_t expected_pool_txs_count = has_invalid_tx ? 0 : 1;
static const size_t expected_pool_txs_count = 0;
static const uint64_t expected_bob_balance = 0;
static const uint64_t expected_alice_balance = send_amount - TESTS_DEFAULT_FEE;
@ -68,7 +68,7 @@ struct gen_double_spend_in_alt_chain_in_the_same_block : public gen_double_spend
{
static const uint64_t send_amount = MK_TEST_COINS(17);
static const bool has_invalid_tx = !txs_kept_by_block;
static const size_t expected_pool_txs_count = has_invalid_tx ? 1 : 2;
static const size_t expected_pool_txs_count = txs_kept_by_block ? 0 :1;
static const uint64_t expected_bob_balance = send_amount;
static const uint64_t expected_alice_balance = 0;
@ -81,14 +81,12 @@ struct gen_double_spend_in_alt_chain_in_different_blocks : public gen_double_spe
{
static const uint64_t send_amount = MK_TEST_COINS(17);
static const bool has_invalid_tx = !txs_kept_by_block;
static const size_t expected_pool_txs_count = has_invalid_tx ? 1 : 2;
static const size_t expected_pool_txs_count = txs_kept_by_block ? 0:1;
static const uint64_t expected_bob_balance = send_amount;
static const uint64_t expected_alice_balance = 0;
static const uint64_t expected_alice_balance = 0;
bool generate(std::vector<test_event_entry>& events) const;
};
class gen_double_spend_in_different_chains : public test_chain_unit_base
{
public:

View file

@ -3095,23 +3095,23 @@ bool escrow_zero_amounts::c1(currency::core& c, size_t ev_index, const std::vect
return true;
}
//------------------------------------------------------------------------------
escrow_acceptance_and_balance::escrow_acceptance_and_balance()
escrow_balance::escrow_balance()
: m_alice_bob_start_amount(0)
, m_alice_bob_start_chunk_amount(0)
, m_alice_fee_proposal(0)
, m_bob_fee_accept(0)
, m_bob_fee_release(0)
{
REGISTER_CALLBACK_METHOD(escrow_acceptance_and_balance, check_balance_after_proposal_not_confirmed);
REGISTER_CALLBACK_METHOD(escrow_acceptance_and_balance, check_balance_after_proposal_confirmed);
REGISTER_CALLBACK_METHOD(escrow_acceptance_and_balance, check_balance_after_acceptance_not_confirmed);
REGISTER_CALLBACK_METHOD(escrow_acceptance_and_balance, check_balance_after_acceptance_confirmed);
REGISTER_CALLBACK_METHOD(escrow_balance, c1);
}
bool escrow_acceptance_and_balance::generate(std::vector<test_event_entry>& events) const
bool escrow_balance::generate(std::vector<test_event_entry>& events) const
{
// Test idea: carefull check balances on each stage of escrow contract (including cancellation req and acc):
// 1) within wallet callback in the middle of contract function call
// 2) after tx was sent to network but not yet confirmed
// 3) after tx was confirmed
m_accounts.resize(TOTAL_ACCS_COUNT);
account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate();
account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate();
@ -3138,157 +3138,447 @@ bool escrow_acceptance_and_balance::generate(std::vector<test_event_entry>& even
REWIND_BLOCKS_N_WITH_TIME(events, blk_1r, blk_1, miner_acc, WALLET_DEFAULT_TX_SPENDABLE_AGE);
// prepare contract details
m_cpd = AUTO_VAL_INIT(m_cpd);
m_cpd.amount_a_pledge = MK_TEST_COINS(7);
m_cpd.amount_b_pledge = MK_TEST_COINS(5);
m_cpd.amount_to_pay = MK_TEST_COINS(3);
m_cpd.a_addr = alice_acc.get_public_address();
m_cpd.b_addr = bob_acc.get_public_address();
m_alice_fee_proposal = MK_TEST_COINS(4);
m_bob_fee_accept = MK_TEST_COINS(2);
m_bob_fee_release = MK_TEST_COINS(9); // Alice states that Bob should pay this much money for upcoming contract release (which will be sent by Alice)
DO_CALLBACK(events, "c1");
std::vector<tx_source_entry> used_sources;
return true;
}
bool escrow_balance::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
bool r = false, stub_bool = false;
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count());
std::shared_ptr<tools::wallet2> alice_wlt = init_playtime_test_wallet(events, c, m_accounts[ALICE_ACC_IDX]);
auto alice_bc = std::make_shared<wallet_callback_balance_checker>("Alice");
alice_wlt->callback(alice_bc);
std::shared_ptr<tools::wallet2> bob_wlt = init_playtime_test_wallet(events, c, m_accounts[BOB_ACC_IDX]);
auto bob_bc = std::make_shared<wallet_callback_balance_checker>("Bob");
bob_wlt->callback(bob_bc);
alice_bc->expect_balance(m_alice_bob_start_amount, 0);
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt,
m_alice_bob_start_amount, // total
true, UINT64_MAX,
m_alice_bob_start_amount, // unlocked
0, // mined
0, // awaited in
0 // awainted out
), false, "");
CHECK_AND_ASSERT_MES(alice_bc->check(), false, "balance callback check failed, see above");
bob_bc->expect_balance(m_alice_bob_start_amount, 0);
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Bob", bob_wlt,
m_alice_bob_start_amount, // total
true, UINT64_MAX,
m_alice_bob_start_amount, // unlocked
0, // mined
0, // awaited in
0 // awaited out
), false, "");
CHECK_AND_ASSERT_MES(bob_bc->check(), false, "balance callback check failed, see above");
//
// escrow proposal
bc_services::proposal_body prop = AUTO_VAL_INIT(prop);
transaction escrow_proposal_tx = AUTO_VAL_INIT(escrow_proposal_tx);
r = build_custom_escrow_proposal(events, blk_1r, alice_acc.get_keys(), m_cpd, 0, 0, 0, blk_1r.timestamp + 36000, 0, m_alice_fee_proposal, m_bob_fee_release, eccf_normal, escrow_proposal_tx, used_sources, &prop);
CHECK_AND_ASSERT_MES(r, false, "build_custom_escrow_proposal failed");
events.push_back(escrow_proposal_tx);
//
bc_services::contract_private_details cpd = AUTO_VAL_INIT(cpd);
cpd.amount_a_pledge = MK_TEST_COINS(7);
cpd.amount_b_pledge = MK_TEST_COINS(5);
cpd.amount_to_pay = MK_TEST_COINS(3);
cpd.a_addr = m_accounts[ALICE_ACC_IDX].get_public_address();
cpd.b_addr = m_accounts[BOB_ACC_IDX].get_public_address();
uint64_t alice_proposal_fee = MK_TEST_COINS(4);
uint64_t bob_acceptace_fee = MK_TEST_COINS(2);
uint64_t bob_release_fee = MK_TEST_COINS(9); // Alice states that Bob should pay this much money for upcoming contract release (which will be sent by Alice)
uint64_t alice_cancellation_request_fee = MK_TEST_COINS(1);
DO_CALLBACK(events, "check_balance_after_proposal_not_confirmed");
alice_bc->expect_balance(m_alice_bob_start_amount - alice_proposal_fee, m_alice_bob_start_amount - 2 * m_alice_bob_start_chunk_amount); // balace after sending the proposal
transaction proposal_tx = AUTO_VAL_INIT(proposal_tx);
transaction escrow_template_tx = AUTO_VAL_INIT(escrow_template_tx);
uint64_t expiration_time = test_core_time::get_time() + 60;
LOG_PRINT_GREEN("\n" "alice_wlt->send_escrow_proposal()", LOG_LEVEL_0);
alice_wlt->send_escrow_proposal(cpd, 0, 0, expiration_time, alice_proposal_fee, bob_release_fee, "", proposal_tx, escrow_template_tx);
MAKE_NEXT_BLOCK_TX1(events, blk_2, blk_1r, miner_acc, escrow_proposal_tx);
CHECK_AND_ASSERT_MES(alice_bc->check(), false, "balance callback check failed, see above");
DO_CALLBACK(events, "check_balance_after_proposal_confirmed");
tools::wallet2::escrow_contracts_container contracts;
r = alice_wlt->get_contracts(contracts);
CHECK_AND_ASSERT_MES(r && contracts.size() == 1, false, "get_contracts() for Alice failed");
crypto::hash contract_id = contracts.begin()->first;
MAKE_NEXT_BLOCK(events, blk_3, blk_2, miner_acc);
DO_CALLBACK(events, "check_balance_after_proposal_confirmed");
// escrow proposal acceptance
transaction escrow_normal_acceptance_tx = prop.tx_template;
uint64_t normal_acceptance_mask = eccf_normal;
r = build_custom_escrow_accept_proposal(events, blk_2, 0, bob_acc.get_keys(), m_cpd, 0, 0, 0, 0, m_bob_fee_accept, m_bob_fee_release, normal_acceptance_mask, prop.tx_onetime_secret_key, escrow_normal_acceptance_tx, used_sources);
CHECK_AND_ASSERT_MES(r, false, "build_custom_escrow_accept_proposal failed");
events.push_back(escrow_normal_acceptance_tx);
DO_CALLBACK(events, "check_balance_after_acceptance_not_confirmed");
MAKE_NEXT_BLOCK_TX1(events, blk_4, blk_3, miner_acc, escrow_normal_acceptance_tx);
DO_CALLBACK(events, "check_balance_after_acceptance_confirmed");
MAKE_NEXT_BLOCK(events, blk_5, blk_4, miner_acc);
DO_CALLBACK(events, "check_balance_after_acceptance_confirmed");
return true;
}
bool escrow_acceptance_and_balance::check_balance_after_proposal_not_confirmed(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
std::shared_ptr<tools::wallet2> alice_wlt = init_playtime_test_wallet(events, c, ALICE_ACC_IDX);
std::shared_ptr<tools::wallet2> bob_wlt = init_playtime_test_wallet(events, c, BOB_ACC_IDX);
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count());
// proposal tx is not confirmed yet
alice_bc->expect_balance();
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt,
m_alice_bob_start_amount - m_alice_fee_proposal, // total
m_alice_bob_start_amount - alice_proposal_fee, // total
true, UINT64_MAX,
m_alice_bob_start_amount - 2 * m_alice_bob_start_chunk_amount, // unlocked
0, // mined
MK_TEST_COINS(0), // awaited in
MK_TEST_COINS(0) // awainted out
0, // awaited in
0 // awainted out
), false, "");
CHECK_AND_ASSERT_MES(!alice_bc->called(), false, "balance callback check failed");
// Bob's balance should not change
bob_bc->expect_balance(m_alice_bob_start_amount, m_alice_bob_start_amount);
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Bob", bob_wlt,
m_alice_bob_start_amount, // total
true, UINT64_MAX,
m_alice_bob_start_amount, // unlocked
0, // mined
MK_TEST_COINS(0), // awaited in
MK_TEST_COINS(0) // awaited out
0, // awaited in
0 // awaited out
), false, "");
CHECK_AND_ASSERT_MES(bob_bc->check(), false, "balance callback check failed, see above");
return true;
}
bool escrow_acceptance_and_balance::check_balance_after_proposal_confirmed(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
std::shared_ptr<tools::wallet2> alice_wlt = init_playtime_test_wallet(events, c, ALICE_ACC_IDX);
std::shared_ptr<tools::wallet2> bob_wlt = init_playtime_test_wallet(events, c, BOB_ACC_IDX);
// mine a block to confirm escrow proposal tx
r = mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c);
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime failed");
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count());
// proposal tx is confirmed (balances should stay the same)
alice_bc->expect_balance(m_alice_bob_start_amount - alice_proposal_fee, m_alice_bob_start_amount - 2 * m_alice_bob_start_chunk_amount);
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt,
m_alice_bob_start_amount - m_alice_fee_proposal, // total
m_alice_bob_start_amount - alice_proposal_fee, // total
true, UINT64_MAX,
m_alice_bob_start_amount - 2 * m_alice_bob_start_chunk_amount, // unlocked
0, // mined
MK_TEST_COINS(0), // awaited in
MK_TEST_COINS(0) // awaited out
0, // awaited in
0 // awainted out
), false, "");
CHECK_AND_ASSERT_MES(alice_bc->check(), false, "balance callback check failed, see above");
bob_bc->expect_balance(m_alice_bob_start_amount, m_alice_bob_start_amount);
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Bob", bob_wlt,
m_alice_bob_start_amount, // total
true, UINT64_MAX,
m_alice_bob_start_amount, // unlocked
0, // mined
MK_TEST_COINS(0), // awaited in
MK_TEST_COINS(0) // awaited out
0, // awaited in
0 // awaited out
), false, "");
CHECK_AND_ASSERT_MES(bob_bc->check(), false, "balance callback check failed, see above");
return true;
}
//
// proposal acceptance
//
bob_bc->expect_balance(m_alice_bob_start_amount - cpd.amount_b_pledge - bob_release_fee - bob_acceptace_fee, m_alice_bob_start_amount - m_alice_bob_start_chunk_amount);
bool escrow_acceptance_and_balance::check_balance_after_acceptance_not_confirmed(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
std::shared_ptr<tools::wallet2> alice_wlt = init_playtime_test_wallet(events, c, ALICE_ACC_IDX);
std::shared_ptr<tools::wallet2> bob_wlt = init_playtime_test_wallet(events, c, BOB_ACC_IDX);
LOG_PRINT_GREEN("\n" "bob_wlt->accept_proposal()", LOG_LEVEL_0);
bob_wlt->accept_proposal(contract_id, bob_acceptace_fee);
CHECK_AND_ASSERT_MES(bob_bc->check(), false, "balance callback check failed, see above");
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count());
// acceptance tx is not confirmed yet
alice_bc->expect_balance(m_alice_bob_start_amount - alice_proposal_fee - cpd.amount_a_pledge - cpd.amount_to_pay, m_alice_bob_start_amount - 2 * m_alice_bob_start_chunk_amount);
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt,
m_alice_bob_start_amount - m_alice_fee_proposal - m_cpd.amount_a_pledge - m_cpd.amount_to_pay, // total
m_alice_bob_start_amount - alice_proposal_fee - cpd.amount_a_pledge - cpd.amount_to_pay, // total
true, UINT64_MAX,
m_alice_bob_start_amount - 2 * m_alice_bob_start_chunk_amount, // unlocked
0, // mined
MK_TEST_COINS(0), // awaited in
m_cpd.amount_a_pledge + m_cpd.amount_to_pay), // awaited out
false, "");
m_alice_bob_start_amount - 2 * m_alice_bob_start_chunk_amount, // unlocked
0, // mined
0, // awaited in
cpd.amount_a_pledge + cpd.amount_to_pay // awaited out
), false, "");
CHECK_AND_ASSERT_MES(alice_bc->check(), false, "balance callback check failed, see above");
bob_bc->expect_balance();
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Bob", bob_wlt,
m_alice_bob_start_amount - m_cpd.amount_b_pledge - m_bob_fee_release - m_bob_fee_accept, // total
m_alice_bob_start_amount - cpd.amount_b_pledge - bob_release_fee - bob_acceptace_fee, // total
true, UINT64_MAX,
m_alice_bob_start_amount - 1 * m_alice_bob_start_chunk_amount, // unlocked
0, // mined
MK_TEST_COINS(0), // awaited in
m_cpd.amount_b_pledge + m_bob_fee_release), // awaited out
false, "");
m_alice_bob_start_amount - 1 * m_alice_bob_start_chunk_amount, // unlocked
0, // mined
0, // awaited in
cpd.amount_b_pledge + bob_release_fee // awaited out
), false, "");
CHECK_AND_ASSERT_MES(!bob_bc->called(), false, "balance callback check failed");
return true;
}
bool escrow_acceptance_and_balance::check_balance_after_acceptance_confirmed(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
std::shared_ptr<tools::wallet2> alice_wlt = init_playtime_test_wallet(events, c, ALICE_ACC_IDX);
std::shared_ptr<tools::wallet2> bob_wlt = init_playtime_test_wallet(events, c, BOB_ACC_IDX);
// mine a block containing contract acceptance
r = mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c);
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime failed");
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count());
// acceptance tx is confirmed
alice_bc->expect_balance(m_alice_bob_start_amount - alice_proposal_fee - cpd.amount_a_pledge - cpd.amount_to_pay, m_alice_bob_start_amount - 2 * m_alice_bob_start_chunk_amount);
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt,
m_alice_bob_start_amount - m_alice_fee_proposal - m_cpd.amount_a_pledge - m_cpd.amount_to_pay, // total
m_alice_bob_start_amount - alice_proposal_fee - cpd.amount_a_pledge - cpd.amount_to_pay, // total
true, UINT64_MAX,
m_alice_bob_start_amount - 2 * m_alice_bob_start_chunk_amount, // unlocked
0, // mined
MK_TEST_COINS(0), // awaited in
MK_TEST_COINS(0) // awaited out
m_alice_bob_start_amount - 2 * m_alice_bob_start_chunk_amount, // unlocked
0, // mined
0, // awaited in
0 // awaited out
), false, "");
CHECK_AND_ASSERT_MES(alice_bc->check(), false, "balance callback check failed, see above");
bob_bc->expect_balance(m_alice_bob_start_amount - cpd.amount_b_pledge - bob_release_fee - bob_acceptace_fee, m_alice_bob_start_amount - 1 * m_alice_bob_start_chunk_amount);
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Bob", bob_wlt,
m_alice_bob_start_amount - m_cpd.amount_b_pledge - m_bob_fee_release - m_bob_fee_accept, // total
m_alice_bob_start_amount - cpd.amount_b_pledge - bob_release_fee - bob_acceptace_fee, // total
true, UINT64_MAX,
m_alice_bob_start_amount - 1 * m_alice_bob_start_chunk_amount, // unlocked
0, // mined
MK_TEST_COINS(0), // awaited in
MK_TEST_COINS(0) // awaited out
m_alice_bob_start_amount - 1 * m_alice_bob_start_chunk_amount, // unlocked
0, // mined
0, // awaited in
0 // awaited out
), false, "");
CHECK_AND_ASSERT_MES(bob_bc->check(), false, "balance callback check failed, see above");
//
// cancellation request
//
alice_bc->expect_balance(m_alice_bob_start_amount - alice_proposal_fee - cpd.amount_a_pledge - cpd.amount_to_pay - alice_cancellation_request_fee, m_alice_bob_start_amount - 3 * m_alice_bob_start_chunk_amount);
LOG_PRINT_GREEN("\n" "alice_wlt->request_cancel_contract()", LOG_LEVEL_0);
alice_wlt->request_cancel_contract(contract_id, alice_cancellation_request_fee, 60 * 60);
CHECK_AND_ASSERT_MES(alice_bc->check(), false, "balance callback check failed, see above");
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count());
// cancellation request is not confirmed yet
alice_bc->expect_balance();
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt,
m_alice_bob_start_amount - alice_proposal_fee - cpd.amount_a_pledge - cpd.amount_to_pay - alice_cancellation_request_fee, // total
true, UINT64_MAX,
m_alice_bob_start_amount - 3 * m_alice_bob_start_chunk_amount, // unlocked
0, // mined
0, // awaited in
0 // awaited out
), false, "");
CHECK_AND_ASSERT_MES(!alice_bc->called(), false, "balance callback check failed");
bob_bc->expect_balance(m_alice_bob_start_amount - cpd.amount_b_pledge - bob_release_fee - bob_acceptace_fee, m_alice_bob_start_amount - 1 * m_alice_bob_start_chunk_amount);
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Bob", bob_wlt,
m_alice_bob_start_amount - cpd.amount_b_pledge - bob_release_fee - bob_acceptace_fee, // total
true, UINT64_MAX,
m_alice_bob_start_amount - 1 * m_alice_bob_start_chunk_amount, // unlocked
0, // mined
0, // awaited in
0 // awaited out
), false, "");
CHECK_AND_ASSERT_MES(bob_bc->check(), false, "balance callback check failed, see above");
// mine a block containing cancellation request
r = mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c);
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime failed");
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count());
// cancellation request is confirmed
alice_bc->expect_balance(m_alice_bob_start_amount - alice_proposal_fee - cpd.amount_a_pledge - cpd.amount_to_pay - alice_cancellation_request_fee, m_alice_bob_start_amount - 3 * m_alice_bob_start_chunk_amount);
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt,
m_alice_bob_start_amount - alice_proposal_fee - cpd.amount_a_pledge - cpd.amount_to_pay - alice_cancellation_request_fee, // total
true, UINT64_MAX,
m_alice_bob_start_amount - 3 * m_alice_bob_start_chunk_amount, // unlocked
0, // mined
0, // awaited in
0 // awaited out
), false, "");
CHECK_AND_ASSERT_MES(alice_bc->check(), false, "balance callback check failed, see above");
bob_bc->expect_balance(m_alice_bob_start_amount - cpd.amount_b_pledge - bob_release_fee - bob_acceptace_fee, m_alice_bob_start_amount - 1 * m_alice_bob_start_chunk_amount);
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Bob", bob_wlt,
m_alice_bob_start_amount - cpd.amount_b_pledge - bob_release_fee - bob_acceptace_fee, // total
true, UINT64_MAX,
m_alice_bob_start_amount - 1 * m_alice_bob_start_chunk_amount, // unlocked
0, // mined
0, // awaited in
0 // awaited out
), false, "");
CHECK_AND_ASSERT_MES(bob_bc->check(), false, "balance callback check failed, see above");
//
// cancellation acceptance
//
bob_bc->expect_balance();
LOG_PRINT_GREEN("\n" "bob_wlt->accept_cancel_contract()", LOG_LEVEL_0);
bob_wlt->accept_cancel_contract(contract_id);
CHECK_AND_ASSERT_MES(!bob_bc->called(), false, "balance callback check failed");
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count());
// cancellation acceptance is not confirmed yet
alice_bc->expect_balance(m_alice_bob_start_amount - alice_proposal_fee - alice_cancellation_request_fee, m_alice_bob_start_amount - 3 * m_alice_bob_start_chunk_amount);
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt,
m_alice_bob_start_amount - alice_proposal_fee - alice_cancellation_request_fee, // total
true, UINT64_MAX,
m_alice_bob_start_amount - 3 * m_alice_bob_start_chunk_amount, // unlocked
0, // mined
cpd.amount_a_pledge + cpd.amount_to_pay, // awaited in
0 // awaited out
), false, "");
CHECK_AND_ASSERT_MES(alice_bc->check(), false, "balance callback check failed, see above");
bob_bc->expect_balance(m_alice_bob_start_amount - bob_release_fee - bob_acceptace_fee, m_alice_bob_start_amount - 1 * m_alice_bob_start_chunk_amount);
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Bob", bob_wlt,
m_alice_bob_start_amount - bob_release_fee - bob_acceptace_fee, // total
true, UINT64_MAX,
m_alice_bob_start_amount - 1 * m_alice_bob_start_chunk_amount, // unlocked
0, // mined
cpd.amount_b_pledge, // awaited in
0 // awaited out
), false, "");
CHECK_AND_ASSERT_MES(bob_bc->check(), false, "balance callback check failed, see above");
// mine a block containing cancellation acceptance
r = mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c);
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime failed");
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count());
// cancellation acceptance is confirmed
alice_bc->expect_balance(m_alice_bob_start_amount - alice_proposal_fee - alice_cancellation_request_fee, m_alice_bob_start_amount - 3 * m_alice_bob_start_chunk_amount);
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt,
m_alice_bob_start_amount - alice_proposal_fee - alice_cancellation_request_fee, // total
true, UINT64_MAX,
m_alice_bob_start_amount - 3 * m_alice_bob_start_chunk_amount, // unlocked
0, // mined
0, // awaited in
0 // awaited out
), false, "");
CHECK_AND_ASSERT_MES(alice_bc->check(), false, "balance callback check failed, see above");
bob_bc->expect_balance(m_alice_bob_start_amount - bob_release_fee - bob_acceptace_fee, m_alice_bob_start_amount - 1 * m_alice_bob_start_chunk_amount);
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Bob", bob_wlt,
m_alice_bob_start_amount - bob_release_fee - bob_acceptace_fee, // total
true, UINT64_MAX,
m_alice_bob_start_amount - 1 * m_alice_bob_start_chunk_amount, // unlocked
0, // mined
0, // awaited in
0 // awaited out
), false, "");
CHECK_AND_ASSERT_MES(bob_bc->check(), false, "balance callback check failed, see above");
//
// Stage 2 : check normal contract workflow
// don't check balances on request and accept as it was checked above
//
uint64_t alice_balance_before_stage_2 = m_alice_bob_start_amount - alice_proposal_fee - alice_cancellation_request_fee;
uint64_t bob_balance_before_stage_2 = m_alice_bob_start_amount - bob_release_fee - bob_acceptace_fee;
alice_bc->expect_balance(alice_balance_before_stage_2 - alice_proposal_fee, m_alice_bob_start_amount - 5 * m_alice_bob_start_chunk_amount);
LOG_PRINT_GREEN("\n" "stage2: alice_wlt->send_escrow_proposal()", LOG_LEVEL_0);
proposal_tx = AUTO_VAL_INIT(proposal_tx);
escrow_template_tx = AUTO_VAL_INIT(escrow_template_tx);
alice_wlt->send_escrow_proposal(cpd, 0, 0, expiration_time, alice_proposal_fee, bob_release_fee, "", proposal_tx, escrow_template_tx);
CHECK_AND_ASSERT_MES(alice_bc->check(), false, "balance callback check failed, see above");
contracts.clear();
r = alice_wlt->get_contracts(contracts);
CHECK_AND_ASSERT_MES(r && contracts.size() == 2, false, "get_contracts() for Alice failed");
// get new contract id
if (contract_id != contracts.begin()->first)
contract_id = contracts.begin()->first;
else
contract_id = (++contracts.begin())->first;
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count());
r = mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c);
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime failed");
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count());
bob_wlt->refresh();
bob_bc->expect_balance(bob_balance_before_stage_2 - cpd.amount_b_pledge - bob_release_fee - bob_acceptace_fee, m_alice_bob_start_amount - 2 * m_alice_bob_start_chunk_amount);
LOG_PRINT_GREEN("\n" "stage2: bob_wlt->accept_proposal()", LOG_LEVEL_0);
bob_wlt->accept_proposal(contract_id, bob_acceptace_fee);
CHECK_AND_ASSERT_MES(bob_bc->check(), false, "balance callback check failed, see above");
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count());
r = mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c);
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime failed");
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count());
alice_bc->expect_balance(alice_balance_before_stage_2 - alice_proposal_fee - cpd.amount_a_pledge - cpd.amount_to_pay, m_alice_bob_start_amount - 5 * m_alice_bob_start_chunk_amount);
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt,
alice_balance_before_stage_2 - alice_proposal_fee - cpd.amount_a_pledge - cpd.amount_to_pay, // total
true, UINT64_MAX,
m_alice_bob_start_amount - 5 * m_alice_bob_start_chunk_amount, // unlocked
0, // mined
0, // awaited in
0 // awaited out
), false, "");
CHECK_AND_ASSERT_MES(alice_bc->check(), false, "balance callback check failed");
bob_bc->expect_balance(bob_balance_before_stage_2 - cpd.amount_b_pledge - bob_release_fee - bob_acceptace_fee, m_alice_bob_start_amount - 2 * m_alice_bob_start_chunk_amount);
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Bob", bob_wlt,
bob_balance_before_stage_2 - cpd.amount_b_pledge - bob_release_fee - bob_acceptace_fee, // total
true, UINT64_MAX,
m_alice_bob_start_amount - 2 * m_alice_bob_start_chunk_amount, // unlocked
0, // mined
0, // awaited in
0 // awaited out
), false, "");
CHECK_AND_ASSERT_MES(bob_bc->check(), false, "balance callback check failed, see above");
//
// contract release
//
alice_bc->expect_balance();
alice_wlt->finish_contract(contract_id, BC_ESCROW_SERVICE_INSTRUCTION_RELEASE_NORMAL);
CHECK_AND_ASSERT_MES(!alice_bc->called(), false, "balance callback check failed");
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count());
// contract release tx is unconfirmed
alice_bc->expect_balance(alice_balance_before_stage_2 - alice_proposal_fee - cpd.amount_to_pay, m_alice_bob_start_amount - 5 * m_alice_bob_start_chunk_amount);
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt,
alice_balance_before_stage_2 - alice_proposal_fee - cpd.amount_to_pay, // total
true, UINT64_MAX,
m_alice_bob_start_amount - 5 * m_alice_bob_start_chunk_amount, // unlocked
0, // mined
cpd.amount_a_pledge, // awaited in
0 // awaited out
), false, "");
CHECK_AND_ASSERT_MES(alice_bc->check(), false, "balance callback check failed, see above");
bob_bc->expect_balance(bob_balance_before_stage_2 - bob_release_fee - bob_acceptace_fee + cpd.amount_to_pay, m_alice_bob_start_amount - 2 * m_alice_bob_start_chunk_amount);
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Bob", bob_wlt,
bob_balance_before_stage_2 - bob_release_fee - bob_acceptace_fee + cpd.amount_to_pay, // total
true, UINT64_MAX,
m_alice_bob_start_amount - 2 * m_alice_bob_start_chunk_amount, // unlocked
0, // mined
cpd.amount_b_pledge + cpd.amount_to_pay, // awaited in
0 // awaited out
), false, "");
CHECK_AND_ASSERT_MES(bob_bc->check(), false, "balance callback check failed, see above");
r = mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c);
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime failed");
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count());
// contract release tx is confirmed
alice_bc->expect_balance(alice_balance_before_stage_2 - alice_proposal_fee - cpd.amount_to_pay, m_alice_bob_start_amount - 5 * m_alice_bob_start_chunk_amount);
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt,
alice_balance_before_stage_2 - alice_proposal_fee - cpd.amount_to_pay, // total
true, UINT64_MAX,
m_alice_bob_start_amount - 5 * m_alice_bob_start_chunk_amount, // unlocked
0, // mined
0, // awaited in
0 // awaited out
), false, "");
CHECK_AND_ASSERT_MES(alice_bc->check(), false, "balance callback check failed, see above");
bob_bc->expect_balance(bob_balance_before_stage_2 - bob_release_fee - bob_acceptace_fee + cpd.amount_to_pay, m_alice_bob_start_amount - 2 * m_alice_bob_start_chunk_amount);
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Bob", bob_wlt,
bob_balance_before_stage_2 - bob_release_fee - bob_acceptace_fee + cpd.amount_to_pay, // total
true, UINT64_MAX,
m_alice_bob_start_amount - 2 * m_alice_bob_start_chunk_amount, // unlocked
0, // mined
0, // awaited in
0 // awaited out
), false, "");
CHECK_AND_ASSERT_MES(bob_bc->check(), false, "balance callback check failed, see above");
return true;
}

View file

@ -142,19 +142,12 @@ struct escrow_zero_amounts : public wallet_test
bool c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
};
struct escrow_acceptance_and_balance : public wallet_test
struct escrow_balance : public wallet_test
{
escrow_acceptance_and_balance();
escrow_balance();
bool generate(std::vector<test_event_entry>& events) const;
bool check_balance_after_proposal_not_confirmed(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
bool check_balance_after_proposal_confirmed(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
bool check_balance_after_acceptance_not_confirmed(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
bool check_balance_after_acceptance_confirmed(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
bool c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
mutable uint64_t m_alice_bob_start_amount;
mutable uint64_t m_alice_bob_start_chunk_amount;
mutable uint64_t m_alice_fee_proposal;
mutable uint64_t m_bob_fee_release;
mutable uint64_t m_bob_fee_accept;
mutable bc_services::contract_private_details m_cpd;
};

View file

@ -2361,7 +2361,7 @@ bool multisig_out_make_and_spent_in_altchain::generate(std::vector<test_event_en
MAKE_NEXT_BLOCK(events, blk_5b, blk_4b, miner_acc);
DO_CALLBACK_PARAMS(events, "check_top_block", params_top_block(get_block_height(blk_5b), get_block_hash(blk_5b)));
size_t tx_count = 1;
size_t tx_count = 0;
DO_CALLBACK_PARAMS(events, "check_tx_pool_count", tx_count);
DO_CALLBACK(events, "clear_tx_pool");

View file

@ -1008,3 +1008,117 @@ bool pos_altblocks_validation::generate(std::vector<test_event_entry>& events) c
return true;
}
//------------------------------------------------------------------
pos_minting_tx_packing::pos_minting_tx_packing()
: m_pos_mint_packing_size(5)
{
REGISTER_CALLBACK_METHOD(pos_minting_tx_packing, configure_core);
REGISTER_CALLBACK_METHOD(pos_minting_tx_packing, c1);
}
bool pos_minting_tx_packing::configure_core(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
currency::core_runtime_config pc = c.get_blockchain_storage().get_core_runtime_config();
pc.min_coinstake_age = 1;
pc.pos_minimum_heigh = 1;
c.get_blockchain_storage().set_core_runtime_config(pc);
return true;
}
bool pos_minting_tx_packing::pos_minting_tx_packing::generate(std::vector<test_event_entry>& events) const
{
bool r = false;
m_accounts.resize(TOTAL_ACCS_COUNT);
account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate();
account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate();
std::list<account_base> miner_acc_lst(1, miner_acc);
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time());
DO_CALLBACK(events, "configure_core");
REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 5);
m_alice_start_amount = 10 * CURRENCY_BLOCK_REWARD * m_pos_mint_packing_size;// +TESTS_DEFAULT_FEE;
transaction tx_1 = AUTO_VAL_INIT(tx_1);
r = construct_tx_with_many_outputs(events, blk_0r, miner_acc.get_keys(), alice_acc.get_public_address(), m_alice_start_amount, 10, TESTS_DEFAULT_FEE, tx_1);
CHECK_AND_ASSERT_MES(r, false, "construct_tx_with_many_outputs failed");
events.push_back(tx_1);
MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner_acc, tx_1);
REWIND_BLOCKS_N_WITH_TIME(events, blk_1r, blk_1, miner_acc, WALLET_DEFAULT_TX_SPENDABLE_AGE);
DO_CALLBACK(events, "c1");
return true;
}
bool pos_minting_tx_packing::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
bool r = false;
std::shared_ptr<tools::wallet2> alice_wlt = init_playtime_test_wallet(events, c, ALICE_ACC_IDX);
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt, m_alice_start_amount, true, UINT64_MAX, m_alice_start_amount), false, "");
size_t pos_entries_count = 0;
for (size_t i = 0; i < m_pos_mint_packing_size; ++i)
{
r = mine_next_pos_block_in_playtime_with_wallet(*alice_wlt, m_accounts[ALICE_ACC_IDX].get_public_address(), pos_entries_count);
CHECK_AND_ASSERT_MES(r, false, "mine_next_pos_block_in_playtime_with_wallet failed");
alice_wlt->refresh();
}
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt, m_alice_start_amount + CURRENCY_BLOCK_REWARD * m_pos_mint_packing_size, true, UINT64_MAX), false, "");
r = mine_next_pow_blocks_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c, WALLET_DEFAULT_TX_SPENDABLE_AGE);
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed");
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt,
m_alice_start_amount + CURRENCY_BLOCK_REWARD * m_pos_mint_packing_size, // total
true,
UINT64_MAX,
m_alice_start_amount + CURRENCY_BLOCK_REWARD * m_pos_mint_packing_size // unlocked
), false, "");
alice_wlt->set_pos_mint_packing_size(m_pos_mint_packing_size);
// no coinbase tx outputs should packed
r = alice_wlt->try_mint_pos();
CHECK_AND_ASSERT_MES(r, false, "try_mint_pos failed");
// make sure the wallet has only received new locked incoming reward
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt,
m_alice_start_amount + CURRENCY_BLOCK_REWARD * (m_pos_mint_packing_size + 1), // total
true,
UINT64_MAX,
m_alice_start_amount // unlocked (one output with amount == CURRENCY_BLOCK_REWARD * m_pos_mint_packing_size was spent as stake)
), false, "");
r = mine_next_pow_blocks_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c, WALLET_DEFAULT_TX_SPENDABLE_AGE);
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed");
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt,
m_alice_start_amount + CURRENCY_BLOCK_REWARD * (m_pos_mint_packing_size + 1), // total
true,
UINT64_MAX,
m_alice_start_amount + CURRENCY_BLOCK_REWARD * (m_pos_mint_packing_size + 1) // unlocked
), false, "");
// coinbase tx outputs should be packed now, there's enough coinbase outputs (> m_pos_mint_packing_size)
r = alice_wlt->try_mint_pos();
CHECK_AND_ASSERT_MES(r, false, "try_mint_pos failed");
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt,
m_alice_start_amount + CURRENCY_BLOCK_REWARD * (m_pos_mint_packing_size + 2), // total
true,
UINT64_MAX,
m_alice_start_amount + CURRENCY_BLOCK_REWARD
), false, "");
return true;
}

View file

@ -122,3 +122,14 @@ struct pos_altblocks_validation : public wallet_test
bool generate(std::vector<test_event_entry>& events) const;
bool configure_core(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
};
struct pos_minting_tx_packing : public wallet_test
{
pos_minting_tx_packing();
bool generate(std::vector<test_event_entry>& events) const;
bool configure_core(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
bool c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
mutable size_t m_pos_mint_packing_size;
mutable size_t m_alice_start_amount;
};

View file

@ -1514,12 +1514,12 @@ bool tx_key_image_pool_conflict::generate(std::vector<test_event_entry>& events)
events.push_back(tx_2);
events.push_back(event_visitor_settings(event_visitor_settings::set_txs_kept_by_block, false));
DO_CALLBACK_PARAMS(events, "check_tx_pool_count", static_cast<size_t>(3));
DO_CALLBACK_PARAMS(events, "check_tx_pool_count", static_cast<size_t>(1));
// make a block with tx_0 and put tx_0 to the blockchain
MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, m_miner_acc, tx_0);
DO_CALLBACK_PARAMS(events, "check_tx_pool_count", static_cast<size_t>(2));
DO_CALLBACK_PARAMS(events, "check_tx_pool_count", static_cast<size_t>(1));
// tx_1 and tx_2 is still in the pool
// it can never be added to any block as long as blk_1 is in the blockchain due to key image conflict
@ -1531,7 +1531,7 @@ bool tx_key_image_pool_conflict::generate(std::vector<test_event_entry>& events)
MAKE_NEXT_BLOCK_TX1(events, blk_1a, blk_0r, m_miner_acc, tx_1);
// however, it does not remove tx from the pool
DO_CALLBACK_PARAMS(events, "check_tx_pool_count", static_cast<size_t>(2));
DO_CALLBACK_PARAMS(events, "check_tx_pool_count", static_cast<size_t>(1));
//
// make sure stuck tx will be removed from the pool when it's too old
@ -1541,7 +1541,7 @@ bool tx_key_image_pool_conflict::generate(std::vector<test_event_entry>& events)
// remove_stuck_txs should not remove anything, tx_1 and tx_2 should be in the pool
DO_CALLBACK(events, "remove_stuck_txs");
DO_CALLBACK_PARAMS(events, "check_tx_pool_count", static_cast<size_t>(2));
DO_CALLBACK_PARAMS(events, "check_tx_pool_count", static_cast<size_t>(1));
// shift time by CURRENCY_MEMPOOL_TX_LIVETIME
events.push_back(event_core_time(CURRENCY_MEMPOOL_TX_LIVETIME + 1, true));
@ -1562,11 +1562,11 @@ bool tx_key_image_pool_conflict::generate(std::vector<test_event_entry>& events)
events.push_back(tx_2);
events.push_back(event_visitor_settings(event_visitor_settings::set_txs_kept_by_block, false));
DO_CALLBACK_PARAMS(events, "check_tx_pool_count", static_cast<size_t>(2));
DO_CALLBACK_PARAMS(events, "check_tx_pool_count", static_cast<size_t>(1));
// remove_stuck_txs should not remove anything, tx_1 and tx_2 should be in the pool
DO_CALLBACK(events, "remove_stuck_txs");
DO_CALLBACK_PARAMS(events, "check_tx_pool_count", static_cast<size_t>(2));
DO_CALLBACK_PARAMS(events, "check_tx_pool_count", static_cast<size_t>(1));
// rewind 50 blocks so tx_0 spending its key image will be deep enough
REWIND_BLOCKS_N_WITH_TIME(events, blk_3r, blk_3, m_miner_acc, 50);

View file

@ -3297,3 +3297,79 @@ bool wallet_unconfimed_tx_balance::c1(currency::core& c, size_t ev_index, const
return true;
}
//------------------------------------------------------------------------------
packing_outputs_on_pos_minting_wallet::packing_outputs_on_pos_minting_wallet()
{
REGISTER_CALLBACK_METHOD(packing_outputs_on_pos_minting_wallet, c1);
REGISTER_CALLBACK_METHOD(packing_outputs_on_pos_minting_wallet, set_core_config);
}
bool packing_outputs_on_pos_minting_wallet::generate(std::vector<test_event_entry>& events) const
{
// 0 10 11 21 22 <- blockchain height (assuming CURRENCY_MINED_MONEY_UNLOCK_WINDOW == 10)
// (0 )... (0r)- (1 )... (1r)- <- main chain
// tx_0 <- txs
GENERATE_ACCOUNT(miner_acc);
m_accounts.push_back(miner_acc);
//GENERATE_ACCOUNT(alice_acc);
//m_accounts.push_back(alice_acc);
// don't use MAKE_GENESIS_BLOCK here because it will mask 'generator'
currency::block blk_0 = AUTO_VAL_INIT(blk_0);
generator.construct_genesis_block(blk_0, miner_acc, test_core_time::get_time());
events.push_back(blk_0);
DO_CALLBACK(events, "set_core_config");
REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW+5);
//MAKE_TX_FEE(events, tx_0, miner_acc, alice_acc, MK_TEST_COINS(2000), TESTS_DEFAULT_FEE, blk_0r);
//MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner_acc, tx_0);
//REWIND_BLOCKS_N_WITH_TIME(events, blk_1r, blk_1, miner_acc, WALLET_DEFAULT_TX_SPENDABLE_AGE);
DO_CALLBACK(events, "c1");
return true;
}
bool packing_outputs_on_pos_minting_wallet::set_core_config(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
core_runtime_config crc = c.get_blockchain_storage().get_core_runtime_config();
crc.pos_minimum_heigh = TESTS_POS_CONFIG_POS_MINIMUM_HEIGH;
crc.min_coinstake_age = TESTS_POS_CONFIG_MIN_COINSTAKE_AGE;
c.get_blockchain_storage().set_core_runtime_config(crc);
return true;
}
bool packing_outputs_on_pos_minting_wallet::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
std::shared_ptr<tools::wallet2> miner_wlt = init_playtime_test_wallet(events, c, MINER_ACC_IDX);
size_t blocks_fetched = 0;
bool received_money;
std::atomic<bool> atomic_false = ATOMIC_VAR_INIT(false);
miner_wlt->refresh(blocks_fetched, received_money, atomic_false);
CHECK_AND_ASSERT_MES(blocks_fetched == CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 5, false, "Incorrect numbers of blocks fetched");
miner_wlt->set_pos_mint_packing_size(4);
check_balance_via_wallet(*miner_wlt.get(), "miner_wlt", MK_TEST_COINS(2000), 0, MK_TEST_COINS(2000), 0, 0);
miner_wlt->try_mint_pos();
CHECK_AND_ASSERT_MES(c.get_current_blockchain_size() == CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 7, false, "Incorrect blockchain height:" << c.get_current_blockchain_size());
miner_wlt->refresh(blocks_fetched, received_money, atomic_false);
CHECK_AND_ASSERT_MES(blocks_fetched == 1, false, "Incorrect numbers of blocks fetched");
block top_block = AUTO_VAL_INIT(top_block);
bool r = c.get_blockchain_storage().get_top_block(top_block);
CHECK_AND_ASSERT_MES(r && is_pos_block(top_block), false, "get_top_block failed or smth goes wrong");
uint64_t top_block_reward = get_outs_money_amount(top_block.miner_tx);
check_balance_via_wallet(*miner_wlt.get(), "miner_wlt", uint64_max, MK_TEST_COINS(2000) + top_block_reward, 0, 0, 0);
miner_wlt->reset_password(g_wallet_password);
miner_wlt->store(g_wallet_filename);
return true;
}

View file

@ -251,3 +251,11 @@ struct wallet_unconfimed_tx_balance : public wallet_test
bool generate(std::vector<test_event_entry>& events) const;
bool c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
};
struct packing_outputs_on_pos_minting_wallet : public wallet_test
{
packing_outputs_on_pos_minting_wallet();
bool generate(std::vector<test_event_entry>& events) const;
bool set_core_config(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
bool c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
};

View file

@ -43,3 +43,47 @@ protected:
mutable test_generator generator;
std::shared_ptr<tools::i_core_proxy> m_core_proxy;
};
// wallet callback helper to check balance in wallet callbacks
// see escrow_balance test for usage example
struct wallet_callback_balance_checker : public tools::i_wallet2_callback
{
wallet_callback_balance_checker(const std::string& label) : m_label(label), m_result(true), m_called(false), m_balance(UINT64_MAX), m_unlocked_balance(UINT64_MAX), m_total_mined(UINT64_MAX) {}
void expect_balance(uint64_t balance = UINT64_MAX, uint64_t unlocked_balance = UINT64_MAX, uint64_t total_mined = UINT64_MAX)
{
m_balance = balance;
m_unlocked_balance = unlocked_balance;
m_total_mined = total_mined;
m_called = false;
}
virtual void on_transfer2(const tools::wallet_public::wallet_transfer_info& wti, uint64_t balance, uint64_t unlocked_balance, uint64_t total_mined) override
{
m_called = true;
m_result = false;
CHECK_AND_ASSERT_MES(m_balance == UINT64_MAX || balance == m_balance, (void)(0), m_label << " balance is incorrect: " << currency::print_money_brief(balance) << ", expected: " << currency::print_money_brief(m_balance));
CHECK_AND_ASSERT_MES(m_unlocked_balance == UINT64_MAX || unlocked_balance == m_unlocked_balance, (void)(0), m_label << " unlocked balance is incorrect: " << currency::print_money_brief(unlocked_balance) << ", expected: " << currency::print_money_brief(m_unlocked_balance));
CHECK_AND_ASSERT_MES(m_total_mined == UINT64_MAX || total_mined == m_total_mined, (void)(0), m_label << " total mined is incorrect: " << currency::print_money_brief(total_mined) << ", expected: " << currency::print_money_brief(m_total_mined));
m_result = true;
}
bool check()
{
bool result = m_result;
m_result = false; // clear result to avoid errorneous successive calls to check() without calling except_balance()
return result;
}
bool called()
{
return m_called;
}
bool m_result;
bool m_called;
std::string m_label;
uint64_t m_balance;
uint64_t m_unlocked_balance;
uint64_t m_total_mined;
};

View file

@ -13,14 +13,14 @@
namespace fs = boost::filesystem;
std::string exec(const char* cmd)
std::string exec(const std::string& str)
{
std::array<char, 1024> buffer;
#if defined(WIN32)
std::unique_ptr<FILE, decltype(&_pclose)> pipe(_popen(cmd, "r"), _pclose);
std::unique_ptr<FILE, decltype(&_pclose)> pipe(_popen(str.c_str(), "r"), _pclose);
#else
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(str.c_str(), "r"), pclose);
#endif
if (!pipe)
@ -89,12 +89,13 @@ void free_space_check()
bool r = false;
#ifdef WIN32
output = exec("dir");
std::string command = "dir";
#else
output = exec("df -h");
std::string command = "df -h && df -i";
#endif
output = exec(command);
LOG_PRINT_L0("test command output:" << std::endl << output);
LOG_PRINT_L0("test command " << command << ", output:" << std::endl << output);
r = try_write_test_file(test_file_size);
LOG_PRINT_L0("test file write: " << (r ? "OK" : "fail"));
@ -122,12 +123,9 @@ void free_space_check()
}
// free space is not ok!
LOG_PRINT_YELLOW("1) fs::space() : available: " << si.available << ", free: " << si.free << ", capacity: " << si.capacity, LOG_LEVEL_0);
#ifdef WIN32
output = exec("dir");
#else
output = exec("df -h");
#endif
LOG_PRINT_YELLOW(output, LOG_LEVEL_0);
output = exec(command);
LOG_PRINT_YELLOW("executed command: " << command << ", output: " << std::endl << output, LOG_LEVEL_0);
// try one again asap
si = fs::space(current_path);

View file

@ -27,6 +27,9 @@ int main(int argc, char** argv)
epee::string_tools::set_module_name_and_folder(argv[0]);
epee::log_space::get_set_log_detalisation_level(true, LOG_LEVEL_2);
epee::log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL, LOG_LEVEL_2);
epee::log_space::log_singletone::add_logger(LOGGER_FILE,
epee::log_space::log_singletone::get_default_log_file().c_str(),
epee::log_space::log_singletone::get_default_log_folder().c_str());
//run_serialization_performance_test();
//return 1;

View file

@ -428,3 +428,23 @@ TEST(db_accessor_tests, median_db_cache_test)
m_naive_median.print_to_file(folder_name + "/naive_median_2.txt");
}
TEST(db_accessor_tests, dtor_without_init)
{
epee::shared_recursive_mutex m_rw_lock;
{
tools::db::basic_db_accessor m_db(nullptr, m_rw_lock);
ASSERT_FALSE(m_db.is_open());
}
// make sure dtor called successfully with no exceptions
{
tools::db::basic_db_accessor m_db(nullptr, m_rw_lock);
m_db.close();
ASSERT_FALSE(m_db.is_open());
}
}

View file

@ -5,6 +5,8 @@
#include <memory>
#include <thread>
#include <atomic>
#include <boost/format.hpp>
#include <bitset>
#include "epee/include/include_base_utils.h"
@ -14,6 +16,7 @@
#include "common/db_abstract_accessor.h"
#include "common/db_backend_lmdb.h"
#include "serialization/serialization.h"
#include "common/db_backend_mdbx.h"
using namespace tools;
@ -890,7 +893,8 @@ namespace lmdb_test
//////////////////////////////////////////////////////////////////////////////
// 2gb_test
//////////////////////////////////////////////////////////////////////////////
TEST(lmdb, 2gb_test)
template<typename db_backend_t>
void db_2gb_test()
{
bool r = false;
epee::shared_recursive_mutex rw_lock;
@ -899,9 +903,9 @@ namespace lmdb_test
static const uint64_t buffer_size = 64 * 1024; // 64 KB
static const uint64_t db_total_size = static_cast<uint64_t>(2.1 * 1024 * 1024 * 1024); // 2.1 GB -- a bit more than 2GB to test 2GB boundary
static const std::string db_file_path = "2gb_lmdb_test";
static const std::string db_file_path = std::string("2gb_") + typeid(db_backend_t).name() + "_test";
std::shared_ptr<db::lmdb_db_backend> lmdb_ptr = std::make_shared<db::lmdb_db_backend>();
std::shared_ptr<db_backend_t> lmdb_ptr = std::make_shared<db_backend_t>();
db::basic_db_accessor bdba(lmdb_ptr, rw_lock);
//
@ -922,6 +926,27 @@ namespace lmdb_test
buffer.resize(buffer_size);
crypto::generate_random_bytes(buffer_size, buffer.data());
std::vector<std::vector<uint8_t>> buffer_paranoidal_copies;
buffer_paranoidal_copies.resize(3);
for (size_t i = 0; i < buffer_paranoidal_copies.size(); ++i)
buffer_paranoidal_copies[i].assign(buffer.begin(), buffer.end());
auto check_buffer_paranoidal_copies = [&]() {
for (size_t buffer_index = 0; buffer_index < buffer_paranoidal_copies.size(); ++buffer_index)
{
for(size_t i = 0; i < buffer_size; ++i)
{
if (buffer[i] != buffer_paranoidal_copies[buffer_index][i])
{
std::cout << "!!! buffer differs from paranoidal copy #" << buffer_index << " at byte " << i << ": " << static_cast<uint32_t>(buffer[i]) << " != " << static_cast<uint32_t>(buffer_paranoidal_copies[buffer_index][i]) << std::endl;
break;
}
}
}
};
check_buffer_paranoidal_copies();
uint64_t total_data = 0;
for (uint64_t key = 0; key < db_total_size / buffer_size; ++key)
{
@ -963,7 +988,58 @@ namespace lmdb_test
ASSERT_TRUE(r);
ASSERT_EQ(buffer_size, out_buffer.size());
ASSERT_TRUE(0 == memcmp(buffer.data(), out_buffer.c_str(), buffer_size));
if (memcmp(buffer.data(), out_buffer.c_str(), buffer_size) != 0)
{
// read data doesn't match with written one
std::cout << "ERROR: data missmatch at key " << key << ", total_data = " << total_data << std::endl;
// paranoid checks
check_buffer_paranoidal_copies();
size_t wrong_bytes = 0;
size_t wrong_bytes_min_idx = SIZE_MAX;
size_t wrong_bytes_max_idx = 0;
for (size_t i = 0; i < buffer_size; ++i)
{
if (buffer[i] != static_cast<unsigned char>(out_buffer[i]))
{
++wrong_bytes;
if (wrong_bytes_min_idx == SIZE_MAX)
wrong_bytes_min_idx = i;
if (wrong_bytes_max_idx < i)
wrong_bytes_max_idx = i;
if (wrong_bytes < 10)
{
std::cout << "wrong byte at buffer offset " << boost::format("0x%04x") % i << ", file offset " << boost::format("0x%08x") % (total_data + i) << ": " <<
boost::format("%02x") % static_cast<unsigned int>(static_cast<unsigned char>(out_buffer[i])) << " = " << std::bitset<8>(out_buffer[i]) << " instead of " <<
boost::format("%02x") % static_cast<unsigned int>(buffer[i]) << " = " << std::bitset<8>(buffer[i]) << std::endl;
}
}
}
std::cout << "wrong bytes: " << wrong_bytes << " of " << buffer_size << " (" << std::fixed << std::setprecision(2) << 100.0 * wrong_bytes / buffer_size << "%)" << std::endl;
size_t line_len = 32;
size_t wrong_bytes_min_line = wrong_bytes_min_idx / line_len;
size_t wrong_bytes_max_line = wrong_bytes_max_idx / line_len + 1;
for (size_t l = wrong_bytes_min_line; l < wrong_bytes_max_line; ++l)
{
std::cout << boost::format("\n0x%04x ") % ( l * line_len );
for(size_t i = l * line_len; i < (l + 1) * line_len; ++i)
std::cout << boost::format("%02x") % static_cast<unsigned int>(buffer[i]) << (i % 4 == 3 ? " " : "");
std::cout << " ";
for(size_t i = l * line_len; i < (l + 1) * line_len; ++i)
std::cout << boost::format("%02x") % static_cast<unsigned int>(static_cast<unsigned char>(out_buffer[i])) << (i % 4 == 3 ? " " : "");
}
std::cout << std::endl;
ASSERT_TRUE(false);
}
total_data += buffer_size;
if (key % 1024 == 0)
@ -979,4 +1055,14 @@ namespace lmdb_test
}
TEST(lmdb, 2gb_test)
{
db_2gb_test<db::lmdb_db_backend>();
}
TEST(mdbx, 2gb_test)
{
db_2gb_test<db::mdbx_db_backend>();
}
} // namespace lmdb_test

View file

@ -38,9 +38,9 @@ if [ $? -ne 0 ]; then
exit 1
fi
make -j connectivity_tool;
make -j connectivity_tool daemon simplewallet
if [ $? -ne 0 ]; then
echo "Failed to make!"
echo "Failed to make binaries!"
exit 1
fi
@ -66,11 +66,17 @@ if [ $? -ne 0 ]; then
exit 1
fi
cp zanod simplewallet Zano.app/Contents/MacOS/
if [ $? -ne 0 ]; then
echo "Failed to copy binaries to Zano.app folder"
exit 1
fi
# fix boost libs paths in main executable and libs to workaround El Capitan's SIP restrictions
source ../../../utils/macosx_fix_boost_libs_path.sh
fix_boost_libs_in_binary @executable_path/../Frameworks/boost_libs Zano.app/Contents/MacOS/Zano
fix_boost_libs_in_binary @executable_path/../Frameworks/boost_libs Zano.app/Contents/MacOS/simplewallet
fix_boost_libs_in_binary @executable_path/../Frameworks/boost_libs Zano.app/Contents/MacOS/zanod
fix_boost_libs_in_libs @executable_path/../Frameworks/boost_libs Zano.app/Contents/Frameworks/boost_libs