From be6f426a3fe26d1df447d010e49e142645afa7a2 Mon Sep 17 00:00:00 2001
From: Nathan Dorfman <ndorf@rtfm.net>
Date: Fri, 15 Feb 2019 23:28:15 -0700
Subject: [PATCH] rpc: Allow submitting tx as hex blob over ZMQ

---
 src/rpc/daemon_handler.cpp  | 36 ++++++++++++++++++++++++++++++------
 src/rpc/daemon_handler.h    |  4 ++++
 src/rpc/daemon_messages.cpp | 17 +++++++++++++++++
 src/rpc/daemon_messages.h   |  8 ++++++++
 4 files changed, 59 insertions(+), 6 deletions(-)

diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp
index 871f7d368..84e227631 100644
--- a/src/rpc/daemon_handler.cpp
+++ b/src/rpc/daemon_handler.cpp
@@ -263,20 +263,43 @@ namespace rpc
 
   void DaemonHandler::handle(const SendRawTx::Request& req, SendRawTx::Response& res)
   {
-    auto tx_blob = cryptonote::tx_to_blob(req.tx);
+    handleTxBlob(cryptonote::tx_to_blob(req.tx), req.relay, res);
+  }
+
+  void DaemonHandler::handle(const SendRawTxHex::Request& req, SendRawTxHex::Response& res)
+  {
+    std::string tx_blob;
+    if(!epee::string_tools::parse_hexstr_to_binbuff(req.tx_as_hex, tx_blob))
+    {
+      MERROR("[SendRawTxHex]: Failed to parse tx from hexbuff: " << req.tx_as_hex);
+      res.status = Message::STATUS_FAILED;
+      res.error_details = "Invalid hex";
+      return;
+    }
+    handleTxBlob(tx_blob, req.relay, res);
+  }
+
+  void DaemonHandler::handleTxBlob(const std::string& tx_blob, bool relay, SendRawTx::Response& res)
+  {
+    if (!m_p2p.get_payload_object().is_synchronized())
+    {
+      res.status = Message::STATUS_FAILED;
+      res.error_details = "Not ready to accept transactions; try again later";
+      return;
+    }
 
     cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context);
     tx_verification_context tvc = AUTO_VAL_INIT(tvc);
 
-    if(!m_core.handle_incoming_tx(tx_blob, tvc, false, false, !req.relay) || tvc.m_verifivation_failed)
+    if(!m_core.handle_incoming_tx(tx_blob, tvc, false, false, !relay) || tvc.m_verifivation_failed)
     {
       if (tvc.m_verifivation_failed)
       {
-        LOG_PRINT_L0("[on_send_raw_tx]: tx verification failed");
+        MERROR("[SendRawTx]: tx verification failed");
       }
       else
       {
-        LOG_PRINT_L0("[on_send_raw_tx]: Failed to process tx");
+        MERROR("[SendRawTx]: Failed to process tx");
       }
       res.status = Message::STATUS_FAILED;
       res.error_details = "";
@@ -328,9 +351,9 @@ namespace rpc
       return;
     }
 
-    if(!tvc.m_should_be_relayed || !req.relay)
+    if(!tvc.m_should_be_relayed || !relay)
     {
-      LOG_PRINT_L0("[on_send_raw_tx]: tx accepted, but not relayed");
+      MERROR("[SendRawTx]: tx accepted, but not relayed");
       res.error_details = "Not relayed";
       res.relayed = false;
       res.status = Message::STATUS_OK;
@@ -830,6 +853,7 @@ namespace rpc
       REQ_RESP_TYPES_MACRO(request_type, KeyImagesSpent, req_json, resp_message, handle);
       REQ_RESP_TYPES_MACRO(request_type, GetTxGlobalOutputIndices, req_json, resp_message, handle);
       REQ_RESP_TYPES_MACRO(request_type, SendRawTx, req_json, resp_message, handle);
+      REQ_RESP_TYPES_MACRO(request_type, SendRawTxHex, req_json, resp_message, handle);
       REQ_RESP_TYPES_MACRO(request_type, GetInfo, req_json, resp_message, handle);
       REQ_RESP_TYPES_MACRO(request_type, StartMining, req_json, resp_message, handle);
       REQ_RESP_TYPES_MACRO(request_type, StopMining, req_json, resp_message, handle);
diff --git a/src/rpc/daemon_handler.h b/src/rpc/daemon_handler.h
index 2c8ac3867..c3a130a04 100644
--- a/src/rpc/daemon_handler.h
+++ b/src/rpc/daemon_handler.h
@@ -68,6 +68,8 @@ class DaemonHandler : public RpcHandler
 
     void handle(const SendRawTx::Request& req, SendRawTx::Response& res);
 
+    void handle(const SendRawTxHex::Request& req, SendRawTxHex::Response& res);
+
     void handle(const StartMining::Request& req, StartMining::Response& res);
 
     void handle(const GetInfo::Request& req, GetInfo::Response& res);
@@ -136,6 +138,8 @@ class DaemonHandler : public RpcHandler
 
     bool getBlockHeaderByHash(const crypto::hash& hash_in, cryptonote::rpc::BlockHeaderResponse& response);
 
+    void handleTxBlob(const std::string& tx_blob, bool relay, SendRawTx::Response& res);
+
     cryptonote::core& m_core;
     t_p2p& m_p2p;
 };
diff --git a/src/rpc/daemon_messages.cpp b/src/rpc/daemon_messages.cpp
index 7c7442014..d4e518180 100644
--- a/src/rpc/daemon_messages.cpp
+++ b/src/rpc/daemon_messages.cpp
@@ -42,6 +42,7 @@ const char* const GetTransactions::name = "get_transactions";
 const char* const KeyImagesSpent::name = "key_images_spent";
 const char* const GetTxGlobalOutputIndices::name = "get_tx_global_output_indices";
 const char* const SendRawTx::name = "send_raw_tx";
+const char* const SendRawTxHex::name = "send_raw_tx_hex";
 const char* const StartMining::name = "start_mining";
 const char* const StopMining::name = "stop_mining";
 const char* const MiningStatus::name = "mining_status";
@@ -292,6 +293,22 @@ void SendRawTx::Response::fromJson(rapidjson::Value& val)
   GET_FROM_JSON_OBJECT(val, relayed, relayed);
 }
 
+rapidjson::Value SendRawTxHex::Request::toJson(rapidjson::Document& doc) const
+{
+  auto val = Message::toJson(doc);
+
+  INSERT_INTO_JSON_OBJECT(val, doc, tx_as_hex, tx_as_hex);
+  INSERT_INTO_JSON_OBJECT(val, doc, relay, relay);
+
+  return val;
+}
+
+void SendRawTxHex::Request::fromJson(rapidjson::Value& val)
+{
+  GET_FROM_JSON_OBJECT(val, tx_as_hex, tx_as_hex);
+  GET_FROM_JSON_OBJECT(val, relay, relay);
+}
+
 rapidjson::Value StartMining::Request::toJson(rapidjson::Document& doc) const
 {
   auto val = Message::toJson(doc);
diff --git a/src/rpc/daemon_messages.h b/src/rpc/daemon_messages.h
index d2014247c..6a13b4e69 100644
--- a/src/rpc/daemon_messages.h
+++ b/src/rpc/daemon_messages.h
@@ -171,6 +171,14 @@ BEGIN_RPC_MESSAGE_CLASS(SendRawTx);
   END_RPC_MESSAGE_RESPONSE;
 END_RPC_MESSAGE_CLASS;
 
+BEGIN_RPC_MESSAGE_CLASS(SendRawTxHex);
+  BEGIN_RPC_MESSAGE_REQUEST;
+    RPC_MESSAGE_MEMBER(std::string, tx_as_hex);
+    RPC_MESSAGE_MEMBER(bool, relay);
+  END_RPC_MESSAGE_REQUEST;
+  using Response = SendRawTx::Response;
+END_RPC_MESSAGE_CLASS;
+
 BEGIN_RPC_MESSAGE_CLASS(StartMining);
   BEGIN_RPC_MESSAGE_REQUEST;
     RPC_MESSAGE_MEMBER(std::string, miner_address);