Electroneum
daemon_handler.cpp
Go to the documentation of this file.
1 // Copyright (c) 2017-2019, 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_handler.h"
30 
31 // likely included by daemon_handler.h's includes,
32 // but including here for clarity
36 #include "ringct/rctSigs.h"
37 #include "version.h"
38 
39 namespace cryptonote
40 {
41 
42 namespace rpc
43 {
44 
45  void DaemonHandler::handle(const GetHeight::Request& req, GetHeight::Response& res)
46  {
47  res.height = m_core.get_current_blockchain_height();
48 
49  res.status = Message::STATUS_OK;
50  }
51 
52  void DaemonHandler::handle(const GetBlocksFast::Request& req, GetBlocksFast::Response& res)
53  {
54  std::vector<std::pair<std::pair<blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, blobdata> > > > blocks;
55 
56  if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, blocks, res.current_height, res.start_height, req.prune, true, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT))
57  {
58  res.status = Message::STATUS_FAILED;
59  res.error_details = "core::find_blockchain_supplement() returned false";
60  return;
61  }
62 
63  res.blocks.resize(blocks.size());
64  res.output_indices.resize(blocks.size());
65 
66  auto it = blocks.begin();
67 
68  uint64_t block_count = 0;
69  while (it != blocks.end())
70  {
71  cryptonote::rpc::block_with_transactions& bwt = res.blocks[block_count];
72 
73  if (!parse_and_validate_block_from_blob(it->first.first, bwt.block))
74  {
75  res.blocks.clear();
76  res.output_indices.clear();
77  res.status = Message::STATUS_FAILED;
78  res.error_details = "failed retrieving a requested block";
79  return;
80  }
81 
82  if (it->second.size() != bwt.block.tx_hashes.size())
83  {
84  res.blocks.clear();
85  res.output_indices.clear();
86  res.status = Message::STATUS_FAILED;
87  res.error_details = "incorrect number of transactions retrieved for block";
88  return;
89  }
90 
91  cryptonote::rpc::block_output_indices& indices = res.output_indices[block_count];
92 
93  // miner tx output indices
94  {
96  if (!m_core.get_tx_outputs_gindexs(get_transaction_hash(bwt.block.miner_tx), tx_indices))
97  {
98  res.status = Message::STATUS_FAILED;
99  res.error_details = "core::get_tx_outputs_gindexs() returned false";
100  return;
101  }
102  indices.push_back(std::move(tx_indices));
103  }
104 
105  auto hash_it = bwt.block.tx_hashes.begin();
106  bwt.transactions.reserve(it->second.size());
107  for (const auto& blob : it->second)
108  {
109  bwt.transactions.emplace_back();
110  if (!parse_and_validate_tx_from_blob(blob.second, bwt.transactions.back()))
111  {
112  res.blocks.clear();
113  res.output_indices.clear();
114  res.status = Message::STATUS_FAILED;
115  res.error_details = "failed retrieving a requested transaction";
116  return;
117  }
118 
120  if (!m_core.get_tx_outputs_gindexs(*hash_it, tx_indices))
121  {
122  res.status = Message::STATUS_FAILED;
123  res.error_details = "core::get_tx_outputs_gindexs() returned false";
124  return;
125  }
126 
127  indices.push_back(std::move(tx_indices));
128  ++hash_it;
129  }
130 
131  it++;
132  block_count++;
133  }
134 
135  res.status = Message::STATUS_OK;
136  }
137 
138  void DaemonHandler::handle(const GetHashesFast::Request& req, GetHashesFast::Response& res)
139  {
140  res.start_height = req.start_height;
141 
142  auto& chain = m_core.get_blockchain_storage();
143 
144  if (!chain.find_blockchain_supplement(req.known_hashes, res.hashes, res.start_height, res.current_height, false))
145  {
146  res.status = Message::STATUS_FAILED;
147  res.error_details = "Blockchain::find_blockchain_supplement() returned false";
148  return;
149  }
150 
151  res.status = Message::STATUS_OK;
152  }
153 
154  void DaemonHandler::handle(const GetTransactions::Request& req, GetTransactions::Response& res)
155  {
156  std::vector<cryptonote::transaction> found_txs_vec;
157  std::vector<crypto::hash> missed_vec;
158 
159  bool r = m_core.get_transactions(req.tx_hashes, found_txs_vec, missed_vec);
160 
161  // TODO: consider fixing core::get_transactions to not hide exceptions
162  if (!r)
163  {
164  res.status = Message::STATUS_FAILED;
165  res.error_details = "core::get_transactions() returned false (exception caught there)";
166  return;
167  }
168 
169  size_t num_found = found_txs_vec.size();
170 
171  std::vector<uint64_t> heights(num_found);
172  std::vector<bool> in_pool(num_found, false);
173  std::vector<crypto::hash> found_hashes(num_found);
174 
175  for (size_t i=0; i < num_found; i++)
176  {
177  found_hashes[i] = get_transaction_hash(found_txs_vec[i]);
178  heights[i] = m_core.get_blockchain_storage().get_db().get_tx_block_height(found_hashes[i]);
179  }
180 
181  // if any missing from blockchain, check in tx pool
182  if (!missed_vec.empty())
183  {
184  std::vector<cryptonote::transaction> pool_txs;
185 
186  m_core.get_pool_transactions(pool_txs);
187 
188  for (const auto& tx : pool_txs)
189  {
191 
192  auto itr = std::find(missed_vec.begin(), missed_vec.end(), h);
193 
194  if (itr != missed_vec.end())
195  {
196  found_hashes.push_back(h);
197  found_txs_vec.push_back(tx);
198  heights.push_back(std::numeric_limits<uint64_t>::max());
199  in_pool.push_back(true);
200  missed_vec.erase(itr);
201  }
202  }
203  }
204 
205  for (size_t i=0; i < found_hashes.size(); i++)
206  {
208  info.height = heights[i];
209  info.in_pool = in_pool[i];
210  info.transaction = std::move(found_txs_vec[i]);
211 
212  res.txs.emplace(found_hashes[i], std::move(info));
213  }
214 
215  res.missed_hashes = std::move(missed_vec);
216  res.status = Message::STATUS_OK;
217  }
218 
219  void DaemonHandler::handle(const KeyImagesSpent::Request& req, KeyImagesSpent::Response& res)
220  {
221  res.spent_status.resize(req.key_images.size(), KeyImagesSpent::STATUS::UNSPENT);
222 
223  std::vector<bool> chain_spent_status;
224  std::vector<bool> pool_spent_status;
225 
226  m_core.are_key_images_spent(req.key_images, chain_spent_status);
227  m_core.are_key_images_spent_in_pool(req.key_images, pool_spent_status);
228 
229  if ((chain_spent_status.size() != req.key_images.size()) || (pool_spent_status.size() != req.key_images.size()))
230  {
231  res.status = Message::STATUS_FAILED;
232  res.error_details = "tx_pool::have_key_images_as_spent() gave vectors of wrong size(s).";
233  return;
234  }
235 
236  for(size_t i=0; i < req.key_images.size(); i++)
237  {
238  if ( chain_spent_status[i] )
239  {
241  }
242  else if ( pool_spent_status[i] )
243  {
244  res.spent_status[i] = KeyImagesSpent::STATUS::SPENT_IN_POOL;
245  }
246  }
247 
248  res.status = Message::STATUS_OK;
249  }
250 
251  void DaemonHandler::handle(const GetTxGlobalOutputIndices::Request& req, GetTxGlobalOutputIndices::Response& res)
252  {
253  if (!m_core.get_tx_outputs_gindexs(req.tx_hash, res.output_indices))
254  {
255  res.status = Message::STATUS_FAILED;
256  res.error_details = "core::get_tx_outputs_gindexs() returned false";
257  return;
258  }
259 
260  res.status = Message::STATUS_OK;
261 
262  }
263 
264  void DaemonHandler::handle(const SendRawTx::Request& req, SendRawTx::Response& res)
265  {
266  handleTxBlob(cryptonote::tx_to_blob(req.tx), req.relay, res);
267  }
268 
269  void DaemonHandler::handle(const SendRawTxHex::Request& req, SendRawTxHex::Response& res)
270  {
271  std::string tx_blob;
272  if(!epee::string_tools::parse_hexstr_to_binbuff(req.tx_as_hex, tx_blob))
273  {
274  MERROR("[SendRawTxHex]: Failed to parse tx from hexbuff: " << req.tx_as_hex);
275  res.status = Message::STATUS_FAILED;
276  res.error_details = "Invalid hex";
277  return;
278  }
279  handleTxBlob(tx_blob, req.relay, res);
280  }
281 
282  void DaemonHandler::handleTxBlob(const std::string& tx_blob, bool relay, SendRawTx::Response& res)
283  {
284  if (!m_p2p.get_payload_object().is_synchronized())
285  {
286  res.status = Message::STATUS_FAILED;
287  res.error_details = "Not ready to accept transactions; try again later";
288  return;
289  }
290 
291  cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context);
293 
294  if(!m_core.handle_incoming_tx(tx_blob, tvc, false, false, !relay) || tvc.m_verification_failed)
295  {
296  if (tvc.m_verification_failed)
297  {
298  MERROR("[SendRawTx]: tx verification failed");
299  }
300  else
301  {
302  MERROR("[SendRawTx]: Failed to process tx");
303  }
304  res.status = Message::STATUS_FAILED;
305  res.error_details = "";
306 
307  if (tvc.m_low_mixin)
308  {
309  res.error_details = "mixin too low";
310  }
311  if (tvc.m_double_spend)
312  {
313  if (!res.error_details.empty()) res.error_details += " and ";
314  res.error_details = "double spend";
315  }
316  if (tvc.m_utxo_nonexistent)
317  {
318  if (!res.error_details.empty()) res.error_details += " and ";
319  res.error_details = "utxo is already spent or is nonexistent";
320  }
321  if (tvc.m_invalid_input)
322  {
323  if (!res.error_details.empty()) res.error_details += " and ";
324  res.error_details = "invalid input";
325  }
326  if (tvc.m_invalid_output)
327  {
328  if (!res.error_details.empty()) res.error_details += " and ";
329  res.error_details = "invalid output";
330  }
331  if (tvc.m_too_big)
332  {
333  if (!res.error_details.empty()) res.error_details += " and ";
334  res.error_details = "too big";
335  }
336  if (tvc.m_overspend)
337  {
338  if (!res.error_details.empty()) res.error_details += " and ";
339  res.error_details = "overspend";
340  }
341  if (tvc.m_fee_too_low)
342  {
343  if (!res.error_details.empty()) res.error_details += " and ";
344  res.error_details = "fee too low";
345  }
346  if (tvc.m_not_rct)
347  {
348  if (!res.error_details.empty()) res.error_details += " and ";
349  res.error_details = "tx is not ringct";
350  }
351  if (tvc.m_portal_outbound_tx)
352  {
353  if (!res.error_details.empty()) res.error_details += " and ";
354  res.error_details = "this is an outbound transaction from the smartchain bridge portal address";
355  }
357  {
358  if (!res.error_details.empty()) res.error_details += " and ";
359  res.error_details = "the bridge source address in the tx extra is invalid";
360  }
362  {
363  if (!res.error_details.empty()) res.error_details += " and ";
364  res.error_details = "the bridge smartchain address in the tx extra is invalid";
365  }
366  if (res.error_details.empty())
367  {
368  res.error_details = "an unknown issue was found with the transaction";
369  }
370 
371  return;
372  }
373 
374  if(!tvc.m_should_be_relayed || !relay)
375  {
376  MERROR("[SendRawTx]: tx accepted, but not relayed");
377  res.error_details = "Not relayed";
378  res.relayed = false;
379  res.status = Message::STATUS_OK;
380 
381  return;
382  }
383 
385  r.txs.push_back(tx_blob);
386  m_core.get_protocol()->relay_transactions(r, fake_context);
387 
388  //TODO: make sure that tx has reached other nodes here, probably wait to receive reflections from other nodes
389  res.status = Message::STATUS_OK;
390  res.relayed = true;
391 
392  return;
393  }
394 
395  void DaemonHandler::handle(const StartMining::Request& req, StartMining::Response& res)
396  {
398  if(!get_account_address_from_str(info, m_core.get_nettype(), req.miner_address))
399  {
400  res.error_details = "Failed, wrong address";
401  LOG_PRINT_L0(res.error_details);
402  res.status = Message::STATUS_FAILED;
403  return;
404  }
405  if (info.is_subaddress)
406  {
407  res.error_details = "Failed, mining to subaddress isn't supported yet";
408  LOG_PRINT_L0(res.error_details);
409  res.status = Message::STATUS_FAILED;
410  return;
411  }
412 
413  unsigned int concurrency_count = boost::thread::hardware_concurrency() * 4;
414 
415  // if we couldn't detect threads, set it to a ridiculously high number
416  if(concurrency_count == 0)
417  {
418  concurrency_count = 257;
419  }
420 
421  // if there are more threads requested than the hardware supports
422  // then we fail and log that.
423  if(req.threads_count > concurrency_count)
424  {
425  res.error_details = "Failed, too many threads relative to CPU cores.";
426  LOG_PRINT_L0(res.error_details);
427  res.status = Message::STATUS_FAILED;
428  return;
429  }
430 
431  if(!m_core.get_miner().start(info.address, static_cast<size_t>(req.threads_count), req.do_background_mining, req.ignore_battery))
432  {
433  res.error_details = "Failed, mining not started";
434  LOG_PRINT_L0(res.error_details);
435  res.status = Message::STATUS_FAILED;
436  return;
437  }
438  res.status = Message::STATUS_OK;
439  res.error_details = "";
440 
441  }
442 
443  void DaemonHandler::handle(const GetInfo::Request& req, GetInfo::Response& res)
444  {
445  res.info.height = m_core.get_current_blockchain_height();
446 
447  res.info.target_height = m_core.get_target_blockchain_height();
448 
449  if (res.info.height > res.info.target_height)
450  {
451  res.info.target_height = res.info.height;
452  }
453 
454  auto& chain = m_core.get_blockchain_storage();
455 
456  res.info.wide_difficulty = chain.get_difficulty_for_next_block();
457  res.info.difficulty = (res.info.wide_difficulty & 0xffffffffffffffff).convert_to<uint64_t>();
458 
459  res.info.target = chain.get_difficulty_target();
460 
461  res.info.tx_count = chain.get_total_transactions() - res.info.height; //without coinbase
462 
463  res.info.tx_pool_size = m_core.get_pool_transactions_count();
464 
465  res.info.alt_blocks_count = chain.get_alternative_blocks_count();
466 
467  uint64_t total_conn = m_p2p.get_public_connections_count();
468  res.info.outgoing_connections_count = m_p2p.get_public_outgoing_connections_count();
469  res.info.incoming_connections_count = total_conn - res.info.outgoing_connections_count;
470 
471  res.info.white_peerlist_size = m_p2p.get_public_white_peers_count();
472 
473  res.info.grey_peerlist_size = m_p2p.get_public_gray_peers_count();
474 
475  res.info.mainnet = m_core.get_nettype() == MAINNET;
476  res.info.testnet = m_core.get_nettype() == TESTNET;
477  res.info.stagenet = m_core.get_nettype() == STAGENET;
478  res.info.wide_cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.info.height - 1);
479  res.info.cumulative_difficulty = (res.info.wide_cumulative_difficulty & 0xffffffffffffffff).convert_to<uint64_t>();
480  res.info.block_size_limit = res.info.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit();
481  res.info.block_size_median = res.info.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median();
482  res.info.start_time = (uint64_t)m_core.get_start_time();
483  res.info.version = ELECTRONEUM_VERSION;
484 
485  res.status = Message::STATUS_OK;
486  res.error_details = "";
487  }
488 
489  void DaemonHandler::handle(const StopMining::Request& req, StopMining::Response& res)
490  {
491  if(!m_core.get_miner().stop())
492  {
493  res.error_details = "Failed, mining not stopped";
494  LOG_PRINT_L0(res.error_details);
495  res.status = Message::STATUS_FAILED;
496  return;
497  }
498 
499  res.status = Message::STATUS_OK;
500  res.error_details = "";
501  }
502 
503  void DaemonHandler::handle(const MiningStatus::Request& req, MiningStatus::Response& res)
504  {
505  const cryptonote::miner& lMiner = m_core.get_miner();
506  res.active = lMiner.is_mining();
507  res.is_background_mining_enabled = lMiner.get_is_background_mining_enabled();
508 
509  if ( lMiner.is_mining() ) {
510  res.speed = lMiner.get_speed();
511  res.threads_count = lMiner.get_threads_count();
512  const account_public_address& lMiningAdr = lMiner.get_mining_address();
513  res.address = get_account_address_as_str(m_core.get_nettype(), false, lMiningAdr);
514  }
515 
516  res.status = Message::STATUS_OK;
517  res.error_details = "";
518  }
519 
520  void DaemonHandler::handle(const SaveBC::Request& req, SaveBC::Response& res)
521  {
523  {
524  res.status = Message::STATUS_FAILED;
525  res.error_details = "Error storing the blockchain";
526  }
527  else
528  {
529  res.status = Message::STATUS_OK;
530  }
531  }
532 
533  void DaemonHandler::handle(const GetBlockHash::Request& req, GetBlockHash::Response& res)
534  {
535  if (m_core.get_current_blockchain_height() <= req.height)
536  {
537  res.hash = crypto::null_hash;
538  res.status = Message::STATUS_FAILED;
539  res.error_details = "height given is higher than current chain height";
540  return;
541  }
542 
543  res.hash = m_core.get_block_id_by_height(req.height);
544 
545  res.status = Message::STATUS_OK;
546  }
547 
548  void DaemonHandler::handle(const GetBlockTemplate::Request& req, GetBlockTemplate::Response& res)
549  {
550  res.status = Message::STATUS_FAILED;
551  res.error_details = "RPC method not yet implemented.";
552  }
553 
554  void DaemonHandler::handle(const SubmitBlock::Request& req, SubmitBlock::Response& res)
555  {
556  res.status = Message::STATUS_FAILED;
557  res.error_details = "RPC method not yet implemented.";
558  }
559 
560  void DaemonHandler::handle(const GetLastBlockHeader::Request& req, GetLastBlockHeader::Response& res)
561  {
562  const crypto::hash block_hash = m_core.get_tail_id();
563 
564  if (!getBlockHeaderByHash(block_hash, res.header))
565  {
566  res.status = Message::STATUS_FAILED;
567  res.error_details = "Requested block does not exist";
568  return;
569  }
570 
571  res.status = Message::STATUS_OK;
572  }
573 
574  void DaemonHandler::handle(const GetBlockHeaderByHash::Request& req, GetBlockHeaderByHash::Response& res)
575  {
576  if (!getBlockHeaderByHash(req.hash, res.header))
577  {
578  res.status = Message::STATUS_FAILED;
579  res.error_details = "Requested block does not exist";
580  return;
581  }
582 
583  res.status = Message::STATUS_OK;
584  }
585 
586  void DaemonHandler::handle(const GetBlockHeaderByHeight::Request& req, GetBlockHeaderByHeight::Response& res)
587  {
588  const crypto::hash block_hash = m_core.get_block_id_by_height(req.height);
589 
590  if (!getBlockHeaderByHash(block_hash, res.header))
591  {
592  res.status = Message::STATUS_FAILED;
593  res.error_details = "Requested block does not exist";
594  return;
595  }
596 
597  res.status = Message::STATUS_OK;
598  }
599 
600  void DaemonHandler::handle(const GetBlockHeadersByHeight::Request& req, GetBlockHeadersByHeight::Response& res)
601  {
602  res.headers.resize(req.heights.size());
603 
604  for (size_t i=0; i < req.heights.size(); i++)
605  {
606  const crypto::hash block_hash = m_core.get_block_id_by_height(req.heights[i]);
607 
608  if (!getBlockHeaderByHash(block_hash, res.headers[i]))
609  {
610  res.status = Message::STATUS_FAILED;
611  res.error_details = "A requested block does not exist";
612  return;
613  }
614  }
615 
616  res.status = Message::STATUS_OK;
617  }
618 
619  void DaemonHandler::handle(const GetBlock::Request& req, GetBlock::Response& res)
620  {
621  res.status = Message::STATUS_FAILED;
622  res.error_details = "RPC method not yet implemented.";
623  }
624 
625  void DaemonHandler::handle(const GetPeerList::Request& req, GetPeerList::Response& res)
626  {
627  res.status = Message::STATUS_FAILED;
628  res.error_details = "RPC method not yet implemented.";
629  }
630 
631  void DaemonHandler::handle(const SetLogHashRate::Request& req, SetLogHashRate::Response& res)
632  {
633  res.status = Message::STATUS_FAILED;
634  res.error_details = "RPC method not yet implemented.";
635  }
636 
637  void DaemonHandler::handle(const SetLogLevel::Request& req, SetLogLevel::Response& res)
638  {
639  if (req.level < 0 || req.level > 4)
640  {
641  res.status = Message::STATUS_FAILED;
642  res.error_details = "Error: log level not valid";
643  }
644  else
645  {
646  res.status = Message::STATUS_OK;
647  mlog_set_log_level(req.level);
648  }
649  }
650 
651  void DaemonHandler::handle(const GetTransactionPool::Request& req, GetTransactionPool::Response& res)
652  {
653  bool r = m_core.get_pool_for_rpc(res.transactions, res.key_images);
654 
655  if (!r) res.status = Message::STATUS_FAILED;
656  else res.status = Message::STATUS_OK;
657  }
658 
659  void DaemonHandler::handle(const GetConnections::Request& req, GetConnections::Response& res)
660  {
661  res.status = Message::STATUS_FAILED;
662  res.error_details = "RPC method not yet implemented.";
663  }
664 
665  void DaemonHandler::handle(const GetBlockHeadersRange::Request& req, GetBlockHeadersRange::Response& res)
666  {
667  res.status = Message::STATUS_FAILED;
668  res.error_details = "RPC method not yet implemented.";
669  }
670 
671  void DaemonHandler::handle(const StopDaemon::Request& req, StopDaemon::Response& res)
672  {
673  res.status = Message::STATUS_FAILED;
674  res.error_details = "RPC method not yet implemented.";
675  }
676 
677  void DaemonHandler::handle(const StartSaveGraph::Request& req, StartSaveGraph::Response& res)
678  {
679  res.status = Message::STATUS_FAILED;
680  res.error_details = "RPC method not yet implemented.";
681  }
682 
683  void DaemonHandler::handle(const StopSaveGraph::Request& req, StopSaveGraph::Response& res)
684  {
685  res.status = Message::STATUS_FAILED;
686  res.error_details = "RPC method not yet implemented.";
687  }
688 
689  void DaemonHandler::handle(const HardForkInfo::Request& req, HardForkInfo::Response& res)
690  {
691  const Blockchain &blockchain = m_core.get_blockchain_storage();
692  uint8_t version = req.version > 0 ? req.version : blockchain.get_ideal_hard_fork_version();
693  res.info.version = blockchain.get_current_hard_fork_version();
694  res.info.enabled = blockchain.get_hard_fork_voting_info(version, res.info.window, res.info.votes, res.info.threshold, res.info.earliest_height, res.info.voting);
695  res.info.state = blockchain.get_hard_fork_state();
696  res.status = Message::STATUS_OK;
697  }
698 
699  void DaemonHandler::handle(const GetBans::Request& req, GetBans::Response& res)
700  {
701  res.status = Message::STATUS_FAILED;
702  res.error_details = "RPC method not yet implemented.";
703  }
704 
705  void DaemonHandler::handle(const SetBans::Request& req, SetBans::Response& res)
706  {
707  res.status = Message::STATUS_FAILED;
708  res.error_details = "RPC method not yet implemented.";
709  }
710 
711  void DaemonHandler::handle(const FlushTransactionPool::Request& req, FlushTransactionPool::Response& res)
712  {
713  res.status = Message::STATUS_FAILED;
714  res.error_details = "RPC method not yet implemented.";
715  }
716 
717  void DaemonHandler::handle(const GetOutputHistogram::Request& req, GetOutputHistogram::Response& res)
718  {
719  std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t> > histogram;
720  try
721  {
722  histogram = m_core.get_blockchain_storage().get_output_histogram(req.amounts, req.unlocked, req.recent_cutoff);
723  }
724  catch (const std::exception &e)
725  {
726  res.status = Message::STATUS_FAILED;
727  res.error_details = e.what();
728  return;
729  }
730 
731  res.histogram.clear();
732  res.histogram.reserve(histogram.size());
733  for (const auto &i: histogram)
734  {
735  if (std::get<0>(i.second) >= req.min_count && (std::get<0>(i.second) <= req.max_count || req.max_count == 0))
736  res.histogram.emplace_back(output_amount_count{i.first, std::get<0>(i.second), std::get<1>(i.second), std::get<2>(i.second)});
737  }
738 
739  res.status = Message::STATUS_OK;
740  }
741 
742  void DaemonHandler::handle(const GetOutputKeys::Request& req, GetOutputKeys::Response& res)
743  {
744  try
745  {
746  for (const auto& i : req.outputs)
747  {
749  rct::key mask;
750  bool unlocked;
751  m_core.get_blockchain_storage().get_output_key_mask_unlocked(i.amount, i.index, key, mask, unlocked);
752  res.keys.emplace_back(output_key_mask_unlocked{key, mask, unlocked});
753  }
754  }
755  catch (const std::exception& e)
756  {
757  res.status = Message::STATUS_FAILED;
758  res.error_details = e.what();
759  return;
760  }
761 
762  res.status = Message::STATUS_OK;
763  }
764 
765  void DaemonHandler::handle(const GetRPCVersion::Request& req, GetRPCVersion::Response& res)
766  {
767  res.version = DAEMON_RPC_VERSION_ZMQ;
768  res.status = Message::STATUS_OK;
769  }
770 
771  void DaemonHandler::handle(const GetFeeEstimate::Request& req, GetFeeEstimate::Response& res)
772  {
773  res.hard_fork_version = m_core.get_blockchain_storage().get_current_hard_fork_version();
774  res.estimated_base_fee = m_core.get_blockchain_storage().get_dynamic_base_fee_estimate(req.num_grace_blocks);
775 
776  if (res.hard_fork_version < HF_VERSION_PER_BYTE_FEE)
777  {
778  res.size_scale = 1024; // per KiB fee
779  res.fee_mask = 1;
780  }
781  else
782  {
783  res.size_scale = 1; // per byte fee
785  }
786  res.status = Message::STATUS_OK;
787  }
788 
789  void DaemonHandler::handle(const GetOutputDistribution::Request& req, GetOutputDistribution::Response& res)
790  {
791  try
792  {
793  res.distributions.reserve(req.amounts.size());
794 
795  const uint64_t req_to_height = req.to_height ? req.to_height : (m_core.get_current_blockchain_height() - 1);
796  for (std::uint64_t amount : req.amounts)
797  {
798  auto data = rpc::RpcHandler::get_output_distribution([this](uint64_t amount, uint64_t from, uint64_t to, uint64_t &start_height, std::vector<uint64_t> &distribution, uint64_t &base) { return m_core.get_output_distribution(amount, from, to, start_height, distribution, base); }, amount, req.from_height, req_to_height, [this](uint64_t height) { return m_core.get_blockchain_storage().get_db().get_block_hash_from_height(height); }, req.cumulative, m_core.get_current_blockchain_height());
799  if (!data)
800  {
801  res.distributions.clear();
802  res.status = Message::STATUS_FAILED;
803  res.error_details = "Failed to get output distribution";
804  return;
805  }
806  res.distributions.push_back(output_distribution{std::move(*data), amount, req.cumulative});
807  }
808  res.status = Message::STATUS_OK;
809  }
810  catch (const std::exception& e)
811  {
812  res.distributions.clear();
813  res.status = Message::STATUS_FAILED;
814  res.error_details = e.what();
815  }
816  }
817 
818  bool DaemonHandler::getBlockHeaderByHash(const crypto::hash& hash_in, cryptonote::rpc::BlockHeaderResponse& header)
819  {
820  block b;
821 
822  if (!m_core.get_block_by_hash(hash_in, b))
823  {
824  return false;
825  }
826 
827  header.hash = hash_in;
828  if (b.miner_tx.vin.size() != 1 || b.miner_tx.vin.front().type() != typeid(txin_gen))
829  {
830  return false;
831  }
832  header.height = boost::get<txin_gen>(b.miner_tx.vin.front()).height;
833 
834  header.major_version = b.major_version;
835  header.minor_version = b.minor_version;
836  header.timestamp = b.timestamp;
837  header.nonce = b.nonce;
838  header.prev_id = b.prev_id;
839 
840  header.depth = m_core.get_current_blockchain_height() - header.height - 1;
841 
842  header.reward = 0;
843  for (const auto& out : b.miner_tx.vout)
844  {
845  header.reward += out.amount;
846  }
847 
849  header.difficulty = (header.wide_difficulty & 0xffffffffffffffff).convert_to<uint64_t>();
850 
851  return true;
852  }
853 
855  {
856  MDEBUG("Handling RPC request: " << request);
857 
858  Message* resp_message = NULL;
859 
860  try
861  {
862  FullMessage req_full(request, true);
863 
864  rapidjson::Value& req_json = req_full.getMessage();
865 
866  const std::string request_type = req_full.getRequestType();
867 
868  // create correct Message subclass and call handle() on it
869  REQ_RESP_TYPES_MACRO(request_type, GetHeight, req_json, resp_message, handle);
870  REQ_RESP_TYPES_MACRO(request_type, GetBlocksFast, req_json, resp_message, handle);
871  REQ_RESP_TYPES_MACRO(request_type, GetHashesFast, req_json, resp_message, handle);
872  REQ_RESP_TYPES_MACRO(request_type, GetTransactions, req_json, resp_message, handle);
873  REQ_RESP_TYPES_MACRO(request_type, KeyImagesSpent, req_json, resp_message, handle);
874  REQ_RESP_TYPES_MACRO(request_type, GetTxGlobalOutputIndices, req_json, resp_message, handle);
875  REQ_RESP_TYPES_MACRO(request_type, SendRawTx, req_json, resp_message, handle);
876  REQ_RESP_TYPES_MACRO(request_type, SendRawTxHex, req_json, resp_message, handle);
877  REQ_RESP_TYPES_MACRO(request_type, GetInfo, req_json, resp_message, handle);
878  REQ_RESP_TYPES_MACRO(request_type, StartMining, req_json, resp_message, handle);
879  REQ_RESP_TYPES_MACRO(request_type, StopMining, req_json, resp_message, handle);
880  REQ_RESP_TYPES_MACRO(request_type, MiningStatus, req_json, resp_message, handle);
881  REQ_RESP_TYPES_MACRO(request_type, SaveBC, req_json, resp_message, handle);
882  REQ_RESP_TYPES_MACRO(request_type, GetBlockHash, req_json, resp_message, handle);
883  REQ_RESP_TYPES_MACRO(request_type, GetLastBlockHeader, req_json, resp_message, handle);
884  REQ_RESP_TYPES_MACRO(request_type, GetBlockHeaderByHash, req_json, resp_message, handle);
885  REQ_RESP_TYPES_MACRO(request_type, GetBlockHeaderByHeight, req_json, resp_message, handle);
886  REQ_RESP_TYPES_MACRO(request_type, GetBlockHeadersByHeight, req_json, resp_message, handle);
887  REQ_RESP_TYPES_MACRO(request_type, GetPeerList, req_json, resp_message, handle);
888  REQ_RESP_TYPES_MACRO(request_type, SetLogLevel, req_json, resp_message, handle);
889  REQ_RESP_TYPES_MACRO(request_type, GetTransactionPool, req_json, resp_message, handle);
890  REQ_RESP_TYPES_MACRO(request_type, HardForkInfo, req_json, resp_message, handle);
891  REQ_RESP_TYPES_MACRO(request_type, GetOutputHistogram, req_json, resp_message, handle);
892  REQ_RESP_TYPES_MACRO(request_type, GetOutputKeys, req_json, resp_message, handle);
893  REQ_RESP_TYPES_MACRO(request_type, GetRPCVersion, req_json, resp_message, handle);
894  REQ_RESP_TYPES_MACRO(request_type, GetFeeEstimate, req_json, resp_message, handle);
895  REQ_RESP_TYPES_MACRO(request_type, GetOutputDistribution, req_json, resp_message, handle);
896 
897  // if none of the request types matches
898  if (resp_message == NULL)
899  {
900  return BAD_REQUEST(request_type, req_full.getID());
901  }
902 
903  FullMessage resp_full = FullMessage::responseMessage(resp_message, req_full.getID());
904 
905  const std::string response = resp_full.getJson();
906  delete resp_message;
907  resp_message = NULL;
908 
909  MDEBUG("Returning RPC response: " << response);
910 
911  return response;
912  }
913  catch (const std::exception& e)
914  {
915  if (resp_message)
916  {
917  delete resp_message;
918  }
919 
920  return BAD_JSON(e.what());
921  }
922  }
923 
924 } // namespace rpc
925 
926 } // namespace cryptonote
const char * res
Definition: hmac_keccak.cpp:41
#define MERROR(x)
Definition: misc_log_ex.h:73
uint64_t get_current_cumulative_block_weight_limit() const
gets the block weight limit based on recent blocks
cryptonote::difficulty_type wide_difficulty
virtual uint64_t get_tx_block_height(const crypto::hash &h) const =0
fetches the height of a transaction&#39;s block
std::vector< crypto::hash > tx_hashes
crypto::hash get_tail_id() const
get the hash of the most recent block on the blockchain
#define COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT
std::string get_account_address_as_str(network_type nettype, bool subaddress, account_public_address const &adr)
void get_output_key_mask_unlocked(const uint64_t &amount, const uint64_t &index, crypto::public_key &key, rct::key &mask, bool &unlocked) const
gets an output&#39;s key and unlocked state
std::vector< tx_output_indices > block_output_indices
SendRawTx::Response Response
::std::string string
Definition: gtest-port.h:1097
epee::misc_utils::struct_init< response_t > response
virtual crypto::hash get_block_hash_from_height(const uint64_t &height) const =0
fetch a block&#39;s hash
std::string BAD_REQUEST(const std::string &request)
Definition: message.cpp:259
uint64_t height
Definition: blockchain.cpp:91
bool store_blockchain()
stores the blockchain
Definition: blockchain.cpp:630
const char * key
Definition: hmac_keccak.cpp:39
bool are_key_images_spent_in_pool(const std::vector< crypto::key_image > &key_im, std::vector< bool > &spent) const
check if multiple key images are spent in the transaction pool
#define LOG_PRINT_L0(x)
Definition: misc_log_ex.h:99
crypto::hash get_block_id_by_height(uint64_t height) const
gets a block&#39;s hash given a height
std::vector< uint64_t > tx_output_indices
virtual difficulty_type get_block_cumulative_difficulty(const uint64_t &height) const =0
fetch a block&#39;s cumulative difficulty
virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS::request &arg, cryptonote_connection_context &exclude_context)=0
network_type get_nettype() const
get the network type we&#39;re on
unsigned char uint8_t
Definition: stdint.h:124
static const char * STATUS_FAILED
Definition: message.h:64
rapidjson::Value & getID()
Definition: message.cpp:182
uint64_t get_current_cumulative_block_weight_median() const
gets the block weight median based on recent blocks (same window as for the limit) ...
#define MDEBUG(x)
Definition: misc_log_ex.h:76
uint8_t get_current_hard_fork_version() const
gets the current hardfork version in use/voted for
Definition: blockchain.h:825
Holds cryptonote related classes and helpers.
Definition: ban.cpp:40
bool get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t to_height, uint64_t &start_height, std::vector< uint64_t > &distribution, uint64_t &base) const
gets per block distribution of outputs of a given amount
std::string getRequestType() const
Definition: message.cpp:152
bool start(const account_public_address &adr, size_t threads_count, bool do_background=false, bool ignore_battery=false)
Definition: miner.cpp:372
blobdata tx_to_blob(const transaction &tx)
bool handle_incoming_tx(const blobdata &tx_blob, tx_verification_context &tvc, bool keeped_by_block, bool relayed, bool do_not_relay)
handles an incoming transaction
std::map< uint64_t, std::tuple< uint64_t, uint64_t, uint64_t > > get_output_histogram(const std::vector< uint64_t > &amounts, bool unlocked, uint64_t recent_cutoff, uint64_t min_count=0) const
return a histogram of outputs on the blockchain
uint64_t get_current_blockchain_height() const
get the current height of the blockchain
uint32_t get_threads_count() const
Definition: miner.cpp:368
GenericValue< UTF8<> > Value
GenericValue with UTF8 encoding.
Definition: document.h:2116
bool get_pool_transactions(std::vector< transaction > &txs, bool include_unrelayed_txes=true) const
get a list of all transactions in the pool
i_cryptonote_protocol * get_protocol()
get the cryptonote protocol instance
HardFork::State get_hard_fork_state() const
gets the hardfork voting state object
static uint64_t get_fee_quantization_mask()
get fee quantization mask
uint64_t get_target_blockchain_height() const
gets the target blockchain height
const account_public_address & get_mining_address() const
Definition: miner.cpp:363
void handle(const GetHeight::Request &req, GetHeight::Response &res)
unsigned __int64 uint64_t
Definition: stdint.h:136
const char *const ELECTRONEUM_VERSION
#define REQ_RESP_TYPES_MACRO(runtime_str, type, reqjson, resp_message_ptr, handler)
Definition: message.h:39
bool get_block_by_hash(const crypto::hash &h, block &blk, bool *orphan=NULL) const
gets the block with a given hash
bool parse_and_validate_tx_from_blob(const blobdata &tx_blob, transaction &tx)
uint64_t get_speed() const
Definition: miner.cpp:428
POD_CLASS public_key
Definition: crypto.h:76
version
Supported socks variants.
Definition: socks.h:57
const BlockchainDB & get_db() const
get a reference to the BlockchainDB in use by Blockchain
Definition: blockchain.h:963
size_t get_pool_transactions_count() const
get the total number of transactions in the pool
CXA_THROW_INFO_T * info
Definition: stack_trace.cpp:91
uint64_t get_dynamic_base_fee_estimate(uint64_t grace_blocks) const
get dynamic per kB or byte fee estimate for the next few blocks
miner & get_miner()
gets the miner instance
Blockchain & get_blockchain_storage()
gets the Blockchain instance
const T & move(const T &t)
Definition: gtest-port.h:1317
bool get_tx_outputs_gindexs(const crypto::hash &tx_id, std::vector< uint64_t > &indexs) const
gets the global indices for outputs from a given transaction
uint8_t get_ideal_hard_fork_version() const
returns the newest hardfork version known to the blockchain
Definition: blockchain.h:832
crypto::hash get_transaction_hash(const transaction &t)
difficulty_type block_difficulty(uint64_t i) const
gets the difficulty of the block with a given height
static FullMessage responseMessage(Message *message)
Definition: message.cpp:226
bool get_account_address_from_str(address_parse_info &info, network_type nettype, std::string const &str)
#define AUTO_VAL_INIT(v)
Definition: misc_language.h:53
bool get_is_background_mining_enabled() const
Definition: miner.cpp:610
bool get_hard_fork_voting_info(uint8_t version, uint32_t &window, uint32_t &votes, uint32_t &threshold, uint64_t &earliest_height, uint8_t &voting) const
get information about hardfork voting for a version
POD_CLASS hash
Definition: hash.h:50
bool is_mining() const
Definition: miner.cpp:358
bool are_key_images_spent(const std::vector< crypto::key_image > &key_im, std::vector< bool > &spent) const
check if multiple key images are spent
static boost::optional< output_distribution_data > get_output_distribution(const std::function< bool(uint64_t, uint64_t, uint64_t, uint64_t &, std::vector< uint64_t > &, uint64_t &)> &f, uint64_t amount, uint64_t from_height, uint64_t to_height, const std::function< crypto::hash(uint64_t)> &get_hash, bool cumulative, uint64_t blockchain_height)
Definition: rpc_handler.cpp:29
rapidjson::Value & getMessage()
Definition: message.cpp:158
static const char * STATUS_OK
Definition: message.h:62
#define HF_VERSION_PER_BYTE_FEE
epee::misc_utils::struct_init< request_t > request
bool get_transactions(const std::vector< crypto::hash > &txs_ids, std::vector< cryptonote::blobdata > &txs, std::vector< crypto::hash > &missed_txs) const
bool get_pool_for_rpc(std::vector< cryptonote::rpc::tx_in_pool > &tx_infos, cryptonote::rpc::key_images_with_tx_hashes &key_image_infos) const
get information about all transactions and key images in the pool
void mlog_set_log_level(int level)
Definition: mlog.cpp:282
bool parse_hexstr_to_binbuff(const epee::span< const char > s, epee::span< char > &res)
Definition: string_tools.h:92
bool find_blockchain_supplement(const std::list< crypto::hash > &qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request &resp) const
get recent block hashes for a foreign chain
bool parse_and_validate_block_from_blob(const blobdata &b_blob, block &b, crypto::hash *block_hash)
std::vector< cryptonote::transaction > transactions
std::string BAD_JSON(const std::string &error_details)
Definition: message.cpp:281
std::time_t get_start_time() const
gets start_time