Electroneum
daemon.cpp
Go to the documentation of this file.
1 // Copyright (c) 2014-2018, The Monero Project
2 //
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without modification, are
6 // permitted provided that the following conditions are met:
7 //
8 // 1. Redistributions of source code must retain the above copyright notice, this list of
9 // conditions and the following disclaimer.
10 //
11 // 2. Redistributions in binary form must reproduce the above copyright notice, this list
12 // of conditions and the following disclaimer in the documentation and/or other
13 // materials provided with the distribution.
14 //
15 // 3. Neither the name of the copyright holder nor the names of its contributors may be
16 // used to endorse or promote products derived from this software without specific
17 // prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
20 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22 // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
27 // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 
29 #include "daemon.h"
30 #include <common/command_line.h>
31 
32 using namespace std;
33 using namespace daemonize;
34 namespace po = boost::program_options;
35 
37 {
39  req2.do_not_relay = true; // Do not relay in test setup, only one daemon running.
41 }
42 
43 void mock_daemon::init_options(boost::program_options::options_description & option_spec)
44 {
45  cryptonote::core::init_options(option_spec);
46  t_node_server::init_options(option_spec);
48 
51 }
52 
53 void mock_daemon::default_options(boost::program_options::variables_map & vm)
54 {
55  std::vector<std::string> exclusive_nodes{"127.0.0.1:65525"};
56  tools::options::set_option(vm, nodetool::arg_p2p_add_exclusive_node, po::variable_value(exclusive_nodes, false));
57 
58  tools::options::set_option(vm, nodetool::arg_p2p_bind_ip, po::variable_value(std::string("127.0.0.1"), false));
59  tools::options::set_option(vm, nodetool::arg_no_igd, po::variable_value(true, false));
60  tools::options::set_option(vm, cryptonote::arg_offline, po::variable_value(true, false));
61  tools::options::set_option(vm, "disable-dns-checkpoints", po::variable_value(true, false));
62 
63  const char *test_mainnet = getenv("TEST_MAINNET");
64  if (!test_mainnet || atoi(test_mainnet) == 0)
65  {
66  tools::options::set_option(vm, cryptonote::arg_testnet_on, po::variable_value(true, false));
67  }
68 
69  // By default pick non-standard ports to avoid confusion with possibly locally running daemons (mainnet/testnet)
70  const char *test_p2p_port = getenv("TEST_P2P_PORT");
71  auto p2p_port = std::string(test_p2p_port && strlen(test_p2p_port) > 0 ? test_p2p_port : "61340");
72  tools::options::set_option(vm, nodetool::arg_p2p_bind_port, po::variable_value(p2p_port, false));
73 
74  const char *test_rpc_port = getenv("TEST_RPC_PORT");
75  auto rpc_port = std::string(test_rpc_port && strlen(test_rpc_port) > 0 ? test_rpc_port : "61341");
76  tools::options::set_option(vm, cryptonote::core_rpc_server::arg_rpc_bind_port, po::variable_value(rpc_port, false));
77 
78  const char *test_zmq_port = getenv("TEST_ZMQ_PORT");
79  auto zmq_port = std::string(test_zmq_port && strlen(test_zmq_port) > 0 ? test_zmq_port : "61342");
80  tools::options::set_option(vm, daemon_args::arg_zmq_rpc_bind_port, po::variable_value(zmq_port, false));
81 
82  po::notify(vm);
83 }
84 
85 void mock_daemon::set_ports(boost::program_options::variables_map & vm, unsigned initial_port)
86 {
87  CHECK_AND_ASSERT_THROW_MES(initial_port < 65535-2, "Invalid port number");
88  tools::options::set_option(vm, nodetool::arg_p2p_bind_port, po::variable_value(std::to_string(initial_port), false));
89  tools::options::set_option(vm, cryptonote::core_rpc_server::arg_rpc_bind_port, po::variable_value(std::to_string(initial_port + 1), false));
90  tools::options::set_option(vm, daemon_args::arg_zmq_rpc_bind_port, po::variable_value(std::to_string(initial_port + 2), false));
91  po::notify(vm);
92 }
93 
94 void mock_daemon::load_params(boost::program_options::variables_map const & vm)
95 {
100 }
101 
103 {
104  if(m_http_client.is_connected())
105  m_http_client.disconnect();
106 
107  if (!m_terminated)
108  {
109  try
110  {
111  stop();
112  }
113  catch (...)
114  {
115  MERROR("Failed to stop");
116  }
117  }
118 
119  if (!m_deinitalized)
120  {
121  deinit();
122  }
123 }
124 
126 {
127  m_deinitalized = false;
129  m_rpc_server.nettype(m_network_type);
130 
131  CHECK_AND_ASSERT_THROW_MES(m_protocol.init(m_vm), "Failed to initialize cryptonote protocol.");
132  CHECK_AND_ASSERT_THROW_MES(m_rpc_server.init(m_vm, false, main_rpc_port), "Failed to initialize RPC server.");
133 
134  if (m_start_p2p)
135  CHECK_AND_ASSERT_THROW_MES(m_server.init(m_vm), "Failed to initialize p2p server.");
136 
137  if(m_http_client.is_connected())
138  m_http_client.disconnect();
139 
140  CHECK_AND_ASSERT_THROW_MES(m_http_client.set_server(rpc_addr(), boost::none, epee::net_utils::ssl_support_t::e_ssl_support_disabled), "RPC client init fail");
141 }
142 
144 {
145  if(m_http_client.is_connected())
146  m_http_client.disconnect();
147 
148  try
149  {
150  m_rpc_server.deinit();
151  }
152  catch (...)
153  {
154  MERROR("Failed to deinitialize RPC server...");
155  }
156 
157  if (m_start_p2p)
158  {
159  try
160  {
161  m_server.deinit();
162  }
163  catch (...)
164  {
165  MERROR("Failed to deinitialize p2p...");
166  }
167  }
168 
169  try
170  {
171  m_protocol.deinit();
172  m_protocol.set_p2p_endpoint(nullptr);
173  }
174  catch (...)
175  {
176  MERROR("Failed to stop cryptonote protocol!");
177  }
178 
179  m_deinitalized = true;
180 }
181 
183 {
184  init();
185  run();
186 }
187 
189 {
190  stop();
191  deinit();
192 }
193 
194 void mock_daemon::try_init_and_run(boost::optional<unsigned> initial_port)
195 {
196  const unsigned max_attempts = 3;
197  for(unsigned attempts=0; attempts < max_attempts; ++attempts)
198  {
199  if (initial_port)
200  {
201  set_ports(m_vm, initial_port.get());
202  load_params(m_vm);
203  MDEBUG("Ports changed, RPC: " << rpc_addr());
204  }
205 
206  try
207  {
208  init_and_run();
209  return;
210  }
211  catch(const std::exception &e)
212  {
213  MWARNING("Could not init and start, attempt: " << attempts << ", reason: " << e.what());
214  if (attempts + 1 >= max_attempts)
215  {
216  throw;
217  }
218  }
219  }
220 }
221 
223 {
224  m_run_thread = boost::thread(boost::bind(&mock_daemon::run_main, this));
225 }
226 
228 {
229  CHECK_AND_ASSERT_THROW_MES(!m_terminated, "Can't run stopped daemon");
230  CHECK_AND_ASSERT_THROW_MES(!m_start_zmq || m_start_p2p, "ZMQ requires P2P");
231  boost::thread stop_thread = boost::thread([this] {
232  while (!this->m_stopped)
234  this->stop_p2p();
235  });
236 
238  m_stopped = true;
239  stop_thread.join();
240  });
241 
242  try
243  {
244  CHECK_AND_ASSERT_THROW_MES(m_rpc_server.run(2, false), "Failed to start RPC");
245  cryptonote::rpc::DaemonHandler rpc_daemon_handler(*m_core, m_server);
246  cryptonote::rpc::ZmqServer zmq_server(rpc_daemon_handler);
247 
248  if (m_start_zmq)
249  {
250  if (!zmq_server.addTCPSocket("127.0.0.1", m_zmq_bind_port))
251  {
252  MERROR("Failed to add TCP Socket (127.0.0.1:" << m_zmq_bind_port << ") to ZMQ RPC Server");
253 
254  stop_rpc();
255  return false;
256  }
257 
258  MINFO("Starting ZMQ server...");
259  zmq_server.run();
260 
261  MINFO("ZMQ server started at 127.0.0.1: " << m_zmq_bind_port);
262  }
263 
264  if (m_start_p2p)
265  {
266  m_server.run(); // blocks until p2p goes down
267  }
268  else
269  {
270  while (!this->m_stopped)
272  }
273 
274  if (m_start_zmq)
275  zmq_server.stop();
276 
277  stop_rpc();
278  return true;
279  }
280  catch (std::exception const & ex)
281  {
282  MFATAL("Uncaught exception! " << ex.what());
283  return false;
284  }
285  catch (...)
286  {
287  MFATAL("Uncaught exception!");
288  return false;
289  }
290 }
291 
293 {
294  CHECK_AND_ASSERT_THROW_MES(!m_terminated, "Can't stop stopped daemon");
295  m_stopped = true;
296  m_terminated = true;
297  m_run_thread.join();
298 }
299 
301 {
302  m_rpc_server.send_stop_signal();
303  m_rpc_server.timed_wait_server_stop(5000);
304 }
305 
307 {
308  if (m_start_p2p)
309  m_server.send_stop_signal();
310 }
311 
312 void mock_daemon::mine_blocks(size_t num_blocks, const std::string &miner_address)
313 {
314  bool blocks_mined = false;
315  const uint64_t start_height = get_height();
316  const auto mining_timeout = std::chrono::seconds(30);
317  MDEBUG("Current height before mining: " << start_height);
318 
319  start_mining(miner_address);
320  auto mining_started = std::chrono::system_clock::now();
321 
322  while(true) {
324  const uint64_t cur_height = get_height();
325 
326  if (cur_height - start_height >= num_blocks)
327  {
328  MDEBUG("Cur blocks: " << cur_height << " start: " << start_height);
329  blocks_mined = true;
330  break;
331  }
332 
333  auto current_time = std::chrono::system_clock::now();
334  if (mining_timeout < current_time - mining_started)
335  {
336  break;
337  }
338  }
339 
340  stop_mining();
341  CHECK_AND_ASSERT_THROW_MES(blocks_mined, "Mining failed in the time limit");
342 }
343 
344 constexpr const std::chrono::seconds mock_daemon::rpc_timeout;
345 
346 void mock_daemon::start_mining(const std::string &miner_address, uint64_t threads_count, bool do_background_mining, bool ignore_battery)
347 {
349  req.miner_address = miner_address;
350  req.threads_count = threads_count;
351  req.do_background_mining = do_background_mining;
352  req.ignore_battery = ignore_battery;
353 
355  bool r = epee::net_utils::invoke_http_json("/start_mining", req, resp, m_http_client, rpc_timeout);
356  CHECK_AND_ASSERT_THROW_MES(r, "RPC error - start mining");
357  CHECK_AND_ASSERT_THROW_MES(resp.status != CORE_RPC_STATUS_BUSY, "Daemon busy");
358  CHECK_AND_ASSERT_THROW_MES(resp.status == CORE_RPC_STATUS_OK, "Daemon response invalid: " << resp.status);
359 }
360 
362 {
365  bool r = epee::net_utils::invoke_http_json("/stop_mining", req, resp, m_http_client, rpc_timeout);
366  CHECK_AND_ASSERT_THROW_MES(r, "RPC error - stop mining");
367  CHECK_AND_ASSERT_THROW_MES(resp.status != CORE_RPC_STATUS_BUSY, "Daemon busy");
368  CHECK_AND_ASSERT_THROW_MES(resp.status == CORE_RPC_STATUS_OK, "Daemon response invalid: " << resp.status);
369 }
370 
372 {
373  return m_core->get_blockchain_storage().get_current_blockchain_height();
374 }
const char * res
Definition: hmac_keccak.cpp:41
#define MERROR(x)
Definition: misc_log_ex.h:73
#define CHECK_AND_ASSERT_THROW_MES(expr, message)
Definition: misc_log_ex.h:173
void init_options(boost::program_options::options_description &hidden_options, boost::program_options::options_description &normal_options)
#define MINFO(x)
Definition: misc_log_ex.h:75
static constexpr const std::chrono::seconds rpc_timeout
Definition: daemon.h:79
#define MFATAL(x)
Definition: misc_log_ex.h:72
::std::string string
Definition: gtest-port.h:1097
auto_scope_leave_caller create_scope_leave_handler(t_scope_leave_handler f)
static void init_options(boost::program_options::options_description &option_spec)
Definition: daemon.cpp:43
virtual ~mock_daemon()
Definition: daemon.cpp:102
bool invoke_http_json(const boost::string_ref uri, const t_request &out_struct, t_response &result_struct, t_transport &transport, std::chrono::milliseconds timeout=std::chrono::seconds(15), const boost::string_ref method="GET")
void stop_mining()
Definition: daemon.cpp:361
void init()
Definition: daemon.cpp:125
#define CORE_RPC_STATUS_BUSY
uint64_t num_blocks(const std::vector< test_event_entry > &events)
Definition: chaingen.cpp:1044
const command_line::arg_descriptor< std::string > arg_zmq_rpc_bind_ip
STL namespace.
void run()
Definition: daemon.cpp:222
const command_line::arg_descriptor< std::string, false, true, 2 > arg_p2p_bind_port
Definition: net_node.cpp:112
const command_line::arg_descriptor< std::string > arg_p2p_bind_ip
Definition: net_node.cpp:111
#define CORE_RPC_STATUS_OK
#define MDEBUG(x)
Definition: misc_log_ex.h:76
void load_params(boost::program_options::variables_map const &vm)
Definition: daemon.cpp:94
const command_line::arg_descriptor< std::string, false, true, 2 > arg_zmq_rpc_bind_port
const command_line::arg_descriptor< bool, false > arg_testnet_on
boost::shared_ptr< call_befor_die_base > auto_scope_leave_caller
void mine_blocks(size_t num_blocks, const std::string &miner_address)
Definition: daemon.cpp:312
unsigned __int64 uint64_t
Definition: stdint.h:136
bool addTCPSocket(boost::string_ref address, boost::string_ref port)
Definition: zmq_server.cpp:103
uint64_t get_height()
Definition: daemon.cpp:371
static void init_options(boost::program_options::options_description &desc)
adds command line options to the given options set
bool run_main()
Definition: daemon.cpp:227
const command_line::arg_descriptor< std::vector< std::string > > arg_p2p_add_exclusive_node
Definition: net_node.cpp:129
static void set_ports(boost::program_options::variables_map &vm, unsigned initial_port)
Definition: daemon.cpp:85
bool sleep_no_w(long ms)
#define MWARNING(x)
Definition: misc_log_ex.h:74
void start_mining(const std::string &miner_address, uint64_t threads_count=1, bool do_background_mining=false, bool ignore_battery=true)
Definition: daemon.cpp:346
void stop_p2p()
Definition: daemon.cpp:306
void add_arg(boost::program_options::options_description &description, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg, bool unique=true)
Definition: command_line.h:188
const command_line::arg_descriptor< bool > arg_offline
static void set_option(boost::program_options::variables_map &vm, const std::string &key, const boost::program_options::variable_value &pv)
void init_and_run()
Definition: daemon.cpp:182
void stop()
Definition: daemon.cpp:292
static void default_options(boost::program_options::variables_map &vm)
Definition: daemon.cpp:53
const command_line::arg_descriptor< bool > arg_no_igd
Definition: net_node.cpp:137
T get_arg(const boost::program_options::variables_map &vm, const arg_descriptor< T, false, true > &arg)
Definition: command_line.h:271
bool on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request &req, COMMAND_RPC_SEND_RAW_TX::response &res, const connection_context *ctx=NULL)
void stop_rpc()
Definition: daemon.cpp:300
void deinit()
Definition: daemon.cpp:143
void try_init_and_run(boost::optional< unsigned > initial_port=boost::none)
Definition: daemon.cpp:194
static const command_line::arg_descriptor< std::string, false, true, 2 > arg_rpc_bind_port
std::string to_string(t_connection_type type)
static void init_options(boost::program_options::options_description &desc)
bool on_send_raw_tx_2(const cryptonote::COMMAND_RPC_SEND_RAW_TX::request &req, cryptonote::COMMAND_RPC_SEND_RAW_TX::response &res, const cryptonote::core_rpc_server::connection_context *ctx)
Definition: daemon.cpp:36
void stop_and_deinit()
Definition: daemon.cpp:188