1
0
Fork 0
forked from lthn/blockchain

Merge branch 'timesync' into develop

This commit is contained in:
sowle 2019-06-11 18:25:36 +03:00
commit 02b85f22f5
13 changed files with 208 additions and 27 deletions

View file

@ -25,5 +25,7 @@ namespace command_line
const arg_descriptor<bool> arg_show_details = { "currency-details", "Display currency details" };
const arg_descriptor<bool> arg_show_rpc_autodoc = { "show_rpc_autodoc", "Display rpc auto-generated documentation template" };
const arg_descriptor<bool> arg_disable_upnp = { "disable-upnp", "Disable UPnP (enhances local network privacy)", false, true };
const arg_descriptor<bool> arg_disable_upnp = { "disable-upnp", "Disable UPnP (enhances local network privacy)", false, true };
const arg_descriptor<bool> arg_disable_stop_if_time_out_of_sync = { "disable-stop-if-time-out-of-sync", "Do not stop the daemon if serious time synchronization problem is detected", false, true };
}

View file

@ -185,4 +185,5 @@ namespace command_line
extern const arg_descriptor<bool> arg_show_details;
extern const arg_descriptor<bool> arg_show_rpc_autodoc;
extern const arg_descriptor<bool> arg_disable_upnp;
extern const arg_descriptor<bool> arg_disable_stop_if_time_out_of_sync;
}

View file

@ -21,6 +21,7 @@ using namespace epee;
#include <sys/utsname.h>
#endif
#include <boost/asio.hpp>
namespace tools
{
@ -588,4 +589,38 @@ std::string get_nix_version_display_string()
return static_cast<uint64_t>(in.tellg());
}
}
int64_t get_ntp_time(const std::string& host_name)
{
try
{
boost::asio::io_service io_service;
boost::asio::ip::udp::resolver resolver(io_service);
boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), host_name, "ntp");
boost::asio::ip::udp::endpoint receiver_endpoint = *resolver.resolve(query);
boost::asio::ip::udp::socket socket(io_service);
socket.open(boost::asio::ip::udp::v4());
boost::array<unsigned char, 48> send_buf = { 010, 0, 0, 0, 0, 0, 0, 0, 0 };
socket.send_to(boost::asio::buffer(send_buf), receiver_endpoint);
boost::array<unsigned long, 1024> recv_buf;
boost::asio::ip::udp::endpoint sender_endpoint;
size_t len = socket.receive_from(boost::asio::buffer(recv_buf), sender_endpoint);
time_t time_recv = ntohl((time_t)recv_buf[4]);
time_recv -= 2208988800U; //Unix time starts from 01/01/1970 == 2208988800U
return time_recv;
}
catch (const std::exception& e)
{
LOG_PRINT_L2("get_ntp_time(): exception: " << e.what());
return 0;
}
catch (...)
{
return 0;
}
}
} // namespace tools

View file

@ -333,4 +333,7 @@ namespace tools
static std::function<void(void)> m_handler;
static std::function<void(int, void*)> m_fatal_handler;
};
int64_t get_ntp_time(const std::string& host_name);
}

View file

@ -32,10 +32,12 @@ namespace currency
m_blockchain_storage(m_mempool),
m_miner(this, m_blockchain_storage),
m_miner_address(boost::value_initialized<account_public_address>()),
m_starter_message_showed(false)
m_starter_message_showed(false),
m_stop_handler(nullptr)
{
set_currency_protocol(pprotocol);
}
//-----------------------------------------------------------------------------------
void core::set_currency_protocol(i_currency_protocol* pprotocol)
{
if(pprotocol)
@ -46,6 +48,11 @@ namespace currency
m_mempool.set_protocol(m_pprotocol);
}
//-----------------------------------------------------------------------------------
void core::set_stop_handler(i_stop_handler *handler)
{
m_stop_handler = handler;
}
//-----------------------------------------------------------------------------------
bool core::set_checkpoints(checkpoints&& chk_pts)
{
return m_blockchain_storage.set_checkpoints(std::move(chk_pts));

View file

@ -81,6 +81,8 @@ namespace currency
size_t get_alternative_blocks_count();
void set_currency_protocol(i_currency_protocol* pprotocol);
void set_stop_handler(i_stop_handler *handler);
i_stop_handler* get_stop_handler() const { return m_stop_handler; }
bool set_checkpoints(checkpoints&& chk_pts);
bool get_pool_transactions(std::list<transaction>& txs);
@ -138,6 +140,7 @@ namespace currency
blockchain_storage m_blockchain_storage;
tx_memory_pool m_mempool;
i_currency_protocol* m_pprotocol;
i_stop_handler* m_stop_handler;
critical_section m_incoming_tx_lock;
miner m_miner;
account_public_address m_miner_address;

View file

@ -66,7 +66,10 @@ namespace currency
uint64_t get_max_seen_height();
virtual size_t get_synchronized_connections_count();
virtual size_t get_synchronizing_connections_count();
int64_t get_net_time_delta_median();
bool add_time_delta_and_check_time_sync(int64_t delta);
bool get_last_time_sync_difference(int64_t& last_median2local_time_difference, int64_t& last_ntp2local_time_difference); // returns true if differences in allowed bounds
private:
//----------------- commands handlers ----------------------------------------------
@ -108,6 +111,11 @@ namespace currency
std::thread m_relay_que_thread;
std::atomic<bool> m_want_stop;
std::deque<int64_t> m_time_deltas;
std::mutex m_time_deltas_lock;
int64_t m_last_median2local_time_difference;
int64_t m_last_ntp2local_time_difference;
template<class t_parametr>
bool post_notify(typename t_parametr::request& arg, currency_connection_context& context)
{

View file

@ -12,26 +12,27 @@ namespace currency
//-----------------------------------------------------------------------------------------------------------------------
template<class t_core>
t_currency_protocol_handler<t_core>::t_currency_protocol_handler(t_core& rcore, nodetool::i_p2p_endpoint<connection_context>* p_net_layout):m_core(rcore),
m_p2p(p_net_layout),
m_syncronized_connections_count(0),
m_synchronized(false),
m_have_been_synchronized(false),
m_max_height_seen(0),
m_core_inital_height(0),
m_want_stop(false)
t_currency_protocol_handler<t_core>::t_currency_protocol_handler(t_core& rcore, nodetool::i_p2p_endpoint<connection_context>* p_net_layout)
: m_core(rcore)
, m_p2p(p_net_layout)
, m_syncronized_connections_count(0)
, m_synchronized(false)
, m_have_been_synchronized(false)
, m_max_height_seen(0)
, m_core_inital_height(0)
, m_want_stop(false)
, m_last_median2local_time_difference(0)
, m_last_ntp2local_time_difference(0)
{
if(!m_p2p)
m_p2p = &m_p2p_stub;
}
//-----------------------------------------------------------------------------------------------------------------------
template<class t_core>
t_currency_protocol_handler<t_core>::~t_currency_protocol_handler()
{
deinit();
}
template<class t_core>
t_currency_protocol_handler<t_core>::~t_currency_protocol_handler()
{
deinit();
}
//-----------------------------------------------------------------------------------------------------------------------
template<class t_core>
bool t_currency_protocol_handler<t_core>::init(const boost::program_options::variables_map& vm)
@ -121,7 +122,25 @@ namespace currency
if(context.m_state == currency_connection_context::state_befor_handshake && !is_inital)
return true;
context.m_time_delta = m_core.get_blockchain_storage().get_core_runtime_config().get_core_time() - hshd.core_time;
uint64_t local_time = m_core.get_blockchain_storage().get_core_runtime_config().get_core_time();
context.m_time_delta = local_time - hshd.core_time;
// for outgoing connections -- check time difference
if (!context.m_is_income)
{
if (!add_time_delta_and_check_time_sync(context.m_time_delta))
{
// serious time sync problem detected
i_stop_handler* ish(m_core.get_stop_handler());
if (ish != nullptr)
{
// this is daemon -- stop immediately
ish->stop_handling();
LOG_ERROR(ENDL << ENDL << "Serious time sync problem detected, daemon will stop immediately" << ENDL << ENDL);
return true;
}
}
}
if(context.m_state == currency_connection_context::state_synchronizing)
return true;
@ -673,8 +692,7 @@ namespace currency
{
std::list<relay_que_entry> local_que;
{
std::unique_lock<std::mutex> lk(m_relay_que_lock);
//m_relay_que_cv.wait(lk);
CRITICAL_REGION_LOCAL(m_relay_que_lock);
local_que.swap(m_relay_que);
}
if (local_que.size())
@ -751,6 +769,90 @@ namespace currency
return epee::misc_utils::median(deltas);
}
//------------------------------------------------------------------------------------------------------------------------
#define TIME_SYNC_DELTA_RING_BUFFER_SIZE 8
#define TIME_SYNC_DELTA_TO_LOCAL_MAX_DIFFERENCE (60 * 5) // max acceptable difference between time delta median among peers and local time (seconds)
#define TIME_SYNC_NTP_TO_LOCAL_MAX_DIFFERENCE (60 * 5) // max acceptable difference between NTP time and local time (seconds)
#define TIME_SYNC_NTP_SERVERS { "time.google.com", "0.pool.ntp.org", "1.pool.ntp.org", "2.pool.ntp.org", "3.pool.ntp.org" }
#define TIME_SYNC_NTP_ATTEMPTS_COUNT 3 // max number of attempts when getting time from NTP server
static int64_t get_ntp_time()
{
static const std::vector<std::string> ntp_servers TIME_SYNC_NTP_SERVERS;
for (size_t att = 0; att < TIME_SYNC_NTP_ATTEMPTS_COUNT; ++att)
{
size_t i = 0;
crypto::generate_random_bytes(sizeof(i), &i);
const std::string& ntp_server = ntp_servers[i % ntp_servers.size()];
LOG_PRINT_L3("NTP: trying to get time from " << ntp_server);
int64_t time = tools::get_ntp_time(ntp_server);
if (time > 0)
{
LOG_PRINT_L2("NTP: " << ntp_server << " responded with " << time << " (" << epee::misc_utils::get_time_str_v2(time) << ")");
return time;
}
LOG_PRINT_L2("NTP: cannot get time from " << ntp_server);
}
return 0; // smth went wrong
}
template<class t_core>
bool t_currency_protocol_handler<t_core>::add_time_delta_and_check_time_sync(int64_t time_delta)
{
CRITICAL_REGION_LOCAL(m_time_deltas_lock);
m_time_deltas.push_back(time_delta);
while (m_time_deltas.size() > TIME_SYNC_DELTA_RING_BUFFER_SIZE)
m_time_deltas.pop_front();
if (m_time_deltas.size() < TIME_SYNC_DELTA_RING_BUFFER_SIZE)
return true; // not enough data
std::vector<int64_t> time_deltas_copy(m_time_deltas.begin(), m_time_deltas.end());
m_last_median2local_time_difference = epee::misc_utils::median(time_deltas_copy);
LOG_PRINT_MAGENTA("TIME: network time difference is " << m_last_median2local_time_difference << " (max is " << TIME_SYNC_DELTA_TO_LOCAL_MAX_DIFFERENCE << ")", LOG_LEVEL_2);
if (std::abs(m_last_median2local_time_difference) > TIME_SYNC_DELTA_TO_LOCAL_MAX_DIFFERENCE)
{
int64_t ntp_time = get_ntp_time();
if (ntp_time == 0)
{
// error geting ntp time
LOG_PRINT_RED("TIME: network time difference is " << m_last_median2local_time_difference << " (max is " << TIME_SYNC_DELTA_TO_LOCAL_MAX_DIFFERENCE << ") but NTP servers did not respond", LOG_LEVEL_0);
return false;
}
// got ntp time correctly
// update local time, because getting ntp time could be time consuming
uint64_t local_time_2 = m_core.get_blockchain_storage().get_core_runtime_config().get_core_time();
m_last_ntp2local_time_difference = local_time_2 - ntp_time;
if (std::abs(m_last_ntp2local_time_difference) > TIME_SYNC_NTP_TO_LOCAL_MAX_DIFFERENCE)
{
// local time is out of sync
LOG_PRINT_RED("TIME: network time difference is " << m_last_median2local_time_difference << " (max is " << TIME_SYNC_DELTA_TO_LOCAL_MAX_DIFFERENCE << "), NTP time difference is " <<
m_last_ntp2local_time_difference << " (max is " << TIME_SYNC_NTP_TO_LOCAL_MAX_DIFFERENCE << ")", LOG_LEVEL_0);
return false;
}
// NTP time is OK
LOG_PRINT_YELLOW("TIME: network time difference is " << m_last_median2local_time_difference << " (max is " << TIME_SYNC_DELTA_TO_LOCAL_MAX_DIFFERENCE << "), NTP time difference is " <<
m_last_ntp2local_time_difference << " (max is " << TIME_SYNC_NTP_TO_LOCAL_MAX_DIFFERENCE << ")", LOG_LEVEL_1);
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
bool t_currency_protocol_handler<t_core>::get_last_time_sync_difference(int64_t& last_median2local_time_difference, int64_t& last_ntp2local_time_difference)
{
CRITICAL_REGION_LOCAL(m_time_deltas_lock);
last_median2local_time_difference = m_last_median2local_time_difference;
last_ntp2local_time_difference = m_last_ntp2local_time_difference;
return !(std::abs(m_last_median2local_time_difference) > TIME_SYNC_DELTA_TO_LOCAL_MAX_DIFFERENCE && std::abs(m_last_ntp2local_time_difference) > TIME_SYNC_NTP_TO_LOCAL_MAX_DIFFERENCE);
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
int t_currency_protocol_handler<t_core>::handle_response_chain_entry(int command, NOTIFY_RESPONSE_CHAIN_ENTRY::request& arg, currency_connection_context& context)
{
@ -806,7 +908,7 @@ namespace currency
{
#ifdef ASYNC_RELAY_MODE
{
std::unique_lock<std::mutex> lk(m_relay_que_lock);
CRITICAL_REGION_LOCAL(m_relay_que_lock);
m_relay_que.push_back(AUTO_VAL_INIT(relay_que_entry()));
m_relay_que.back().first = arg;
m_relay_que.back().second = exclude_context;

View file

@ -36,4 +36,14 @@ namespace currency
}
};
/************************************************************************/
/* */
/************************************************************************/
struct i_stop_handler
{
virtual void stop_handling() = 0;
};
}

View file

@ -81,6 +81,7 @@ int main(int argc, char* argv[])
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_show_rpc_autodoc);
command_line::add_arg(desc_cmd_sett, command_line::arg_disable_stop_if_time_out_of_sync);
arg_market_disable.default_value = true;
@ -167,6 +168,10 @@ int main(int argc, char* argv[])
cprotocol.set_p2p_endpoint(&p2psrv);
ccore.set_currency_protocol(&cprotocol);
daemon_cmmands_handler dch(p2psrv, rpc_server);
if (!command_line::get_arg(vm, command_line::arg_disable_stop_if_time_out_of_sync))
ccore.set_stop_handler(&dch);
//ccore.get_blockchain_storage().get_attachment_services_manager().add_service(&offers_service);
std::shared_ptr<currency::stratum_server> stratum_server_ptr;
if (stratum_enabled)
@ -304,7 +309,7 @@ int main(int argc, char* argv[])
LOG_PRINT_L0("Deinitializing p2p...");
p2psrv.deinit();
ccore.set_stop_handler(nullptr);
ccore.set_currency_protocol(NULL);
cprotocol.set_p2p_endpoint(NULL);

View file

@ -20,7 +20,7 @@
PUSH_WARNINGS
DISABLE_VS_WARNINGS(4100)
class daemon_cmmands_handler
class daemon_cmmands_handler : public currency::i_stop_handler
{
typedef nodetool::node_server<currency::t_currency_protocol_handler<currency::core> > srv_type;
srv_type& m_srv;
@ -75,7 +75,8 @@ public:
return true;
}
void stop_handling()
// interface currency::i_stop_handler
virtual void stop_handling() override
{
m_cmd_binder.stop_handling();
}

View file

@ -461,7 +461,7 @@ bool daemon_backend::update_state_info()
{
view::daemon_status_info dsi = AUTO_VAL_INIT(dsi);
currency::COMMAND_RPC_GET_INFO::request req = AUTO_VAL_INIT(req);
req.flags = COMMAND_RPC_GET_INFO_FLAG_EXPIRATIONS_MEDIAN;
req.flags = COMMAND_RPC_GET_INFO_FLAG_EXPIRATIONS_MEDIAN | COMMAND_RPC_GET_INFO_FLAG_NET_TIME_DELTA_MEDIAN;
currency::COMMAND_RPC_GET_INFO::response inf = AUTO_VAL_INIT(inf);
if (!m_rpc_proxy->call_COMMAND_RPC_GET_INFO(req, inf))
{

View file

@ -116,7 +116,11 @@ namespace currency
//conditional values
if (req.flags&COMMAND_RPC_GET_INFO_FLAG_NET_TIME_DELTA_MEDIAN)
res.net_time_delta_median = m_p2p.get_payload_object().get_net_time_delta_median();
{
int64_t last_median2local_time_diff, last_ntp2local_time_diff;
if (!m_p2p.get_payload_object().get_last_time_sync_difference(last_median2local_time_diff, last_ntp2local_time_diff))
res.net_time_delta_median = 1;
}
if (req.flags&COMMAND_RPC_GET_INFO_FLAG_CURRENT_NETWORK_HASHRATE_50)
res.current_network_hashrate_50 = m_core.get_blockchain_storage().get_current_hashrate(50);
if (req.flags&COMMAND_RPC_GET_INFO_FLAG_CURRENT_NETWORK_HASHRATE_350)