aboutsummaryrefslogtreecommitdiff
path: root/src/rpc
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpc')
-rw-r--r--src/rpc/CMakeLists.txt7
-rwxr-xr-x[-rw-r--r--]src/rpc/core_rpc_server.cpp208
-rw-r--r--src/rpc/core_rpc_server.h4
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h408
-rw-r--r--src/rpc/core_rpc_server_error_codes.h1
-rw-r--r--src/rpc/daemon_handler.cpp17
-rw-r--r--src/rpc/message.cpp53
-rwxr-xr-x[-rw-r--r--]src/rpc/rpc_args.cpp18
-rwxr-xr-x[-rw-r--r--]src/rpc/rpc_args.h2
9 files changed, 610 insertions, 108 deletions
diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt
index b5c38b1a8..23bb6aaae 100644
--- a/src/rpc/CMakeLists.txt
+++ b/src/rpc/CMakeLists.txt
@@ -117,10 +117,3 @@ target_link_libraries(daemon_rpc_server
${EXTRA_LIBRARIES})
target_include_directories(daemon_rpc_server PUBLIC ${ZMQ_INCLUDE_PATH})
target_include_directories(obj_daemon_rpc_server PUBLIC ${ZMQ_INCLUDE_PATH})
-
-
-add_dependencies(rpc
- version)
-
-add_dependencies(daemon_rpc_server
- version)
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index 1f7f4a1ff..b3ce30d0c 100644..100755
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -50,6 +50,16 @@ using namespace epee;
#define MAX_RESTRICTED_FAKE_OUTS_COUNT 40
#define MAX_RESTRICTED_GLOBAL_FAKE_OUTS_COUNT 500
+namespace
+{
+ void add_reason(std::string &reasons, const char *reason)
+ {
+ if (!reasons.empty())
+ reasons += ", ";
+ reasons += reason;
+ }
+}
+
namespace cryptonote
{
@@ -91,7 +101,7 @@ namespace cryptonote
http_login.emplace(std::move(rpc_config->login->username), std::move(rpc_config->login->password).password());
return epee::http_server_impl_base<core_rpc_server, connection_context>::init(
- std::move(port), std::move(rpc_config->bind_ip), std::move(http_login)
+ std::move(port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login)
);
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -128,11 +138,7 @@ namespace cryptonote
{
CHECK_CORE_BUSY();
crypto::hash top_hash;
- if (!m_core.get_blockchain_top(res.height, top_hash))
- {
- res.status = "Failed";
- return false;
- }
+ m_core.get_blockchain_top(res.height, top_hash);
++res.height; // turn top block height into blockchain height
res.top_block_hash = string_tools::pod_to_hex(top_hash);
res.target_height = m_core.get_target_blockchain_height();
@@ -478,18 +484,31 @@ namespace cryptonote
bool r = m_core.get_pool_transactions(pool_txs);
if(r)
{
- for (std::list<transaction>::const_iterator i = pool_txs.begin(); i != pool_txs.end(); ++i)
+ // sort to match original request
+ std::list<transaction> sorted_txs;
+ std::list<cryptonote::transaction>::const_iterator i;
+ for (const crypto::hash &h: vh)
{
- crypto::hash tx_hash = get_transaction_hash(*i);
- std::list<crypto::hash>::iterator mi = std::find(missed_txs.begin(), missed_txs.end(), tx_hash);
- if (mi != missed_txs.end())
+ if (std::find(missed_txs.begin(), missed_txs.end(), h) == missed_txs.end())
{
- pool_tx_hashes.insert(tx_hash);
- missed_txs.erase(mi);
- txs.push_back(*i);
+ // core returns the ones it finds in the right order
+ if (get_transaction_hash(txs.front()) != h)
+ {
+ res.status = "Failed: tx hash mismatch";
+ return true;
+ }
+ sorted_txs.push_back(std::move(txs.front()));
+ txs.pop_front();
+ }
+ else if ((i = std::find_if(pool_txs.begin(), pool_txs.end(), [h](cryptonote::transaction &tx) { return h == cryptonote::get_transaction_hash(tx); })) != pool_txs.end())
+ {
+ sorted_txs.push_back(*i);
+ missed_txs.remove(h);
+ pool_tx_hashes.insert(h);
++found_in_pool;
}
}
+ txs = sorted_txs;
}
LOG_PRINT_L2("Found " << found_in_pool << "/" << vh.size() << " transactions in the pool");
}
@@ -510,11 +529,12 @@ namespace cryptonote
e.in_pool = pool_tx_hashes.find(tx_hash) != pool_tx_hashes.end();
if (e.in_pool)
{
- e.block_height = std::numeric_limits<uint64_t>::max();
+ e.block_height = e.block_timestamp = std::numeric_limits<uint64_t>::max();
}
else
{
e.block_height = m_core.get_blockchain_storage().get_db().get_tx_block_height(tx_hash);
+ e.block_timestamp = m_core.get_blockchain_storage().get_db().get_block_timestamp(e.block_height);
}
// fill up old style responses too, in case an old wallet asks
@@ -623,31 +643,33 @@ namespace cryptonote
tx_verification_context tvc = AUTO_VAL_INIT(tvc);
if(!m_core.handle_incoming_tx(tx_blob, tvc, false, false, req.do_not_relay) || tvc.m_verifivation_failed)
{
- if (tvc.m_verifivation_failed)
- {
- LOG_PRINT_L0("[on_send_raw_tx]: tx verification failed");
- }
- else
- {
- LOG_PRINT_L0("[on_send_raw_tx]: Failed to process tx");
- }
res.status = "Failed";
+ res.reason = "";
if ((res.low_mixin = tvc.m_low_mixin))
- res.reason = "ring size too small";
+ add_reason(res.reason, "ring size too small");
if ((res.double_spend = tvc.m_double_spend))
- res.reason = "double spend";
+ add_reason(res.reason, "double spend");
if ((res.invalid_input = tvc.m_invalid_input))
- res.reason = "invalid input";
+ add_reason(res.reason, "invalid input");
if ((res.invalid_output = tvc.m_invalid_output))
- res.reason = "invalid output";
+ add_reason(res.reason, "invalid output");
if ((res.too_big = tvc.m_too_big))
- res.reason = "too big";
+ add_reason(res.reason, "too big");
if ((res.overspend = tvc.m_overspend))
- res.reason = "overspend";
+ add_reason(res.reason, "overspend");
if ((res.fee_too_low = tvc.m_fee_too_low))
- res.reason = "fee too low";
+ add_reason(res.reason, "fee too low");
if ((res.not_rct = tvc.m_not_rct))
- res.reason = "tx is not ringct";
+ add_reason(res.reason, "tx is not ringct");
+ const std::string punctuation = res.reason.empty() ? "" : ": ";
+ if (tvc.m_verifivation_failed)
+ {
+ LOG_PRINT_L0("[on_send_raw_tx]: tx verification failed" << punctuation << res.reason);
+ }
+ else
+ {
+ LOG_PRINT_L0("[on_send_raw_tx]: Failed to process tx" << punctuation << res.reason);
+ }
return true;
}
@@ -671,13 +693,19 @@ namespace cryptonote
bool core_rpc_server::on_start_mining(const COMMAND_RPC_START_MINING::request& req, COMMAND_RPC_START_MINING::response& res)
{
CHECK_CORE_READY();
- account_public_address adr;
- if(!get_account_address_from_str(adr, m_testnet, req.miner_address))
+ cryptonote::address_parse_info info;
+ if(!get_account_address_from_str(info, m_testnet, req.miner_address))
{
res.status = "Failed, wrong address";
LOG_PRINT_L0(res.status);
return true;
}
+ if (info.is_subaddress)
+ {
+ res.status = "Mining to subaddress isn't supported yet";
+ LOG_PRINT_L0(res.status);
+ return true;
+ }
unsigned int concurrency_count = boost::thread::hardware_concurrency() * 4;
@@ -699,7 +727,7 @@ namespace cryptonote
boost::thread::attributes attrs;
attrs.set_stack_size(THREAD_STACK_SIZE);
- if(!m_core.get_miner().start(adr, static_cast<size_t>(req.threads_count), attrs, req.do_background_mining, req.ignore_battery))
+ if(!m_core.get_miner().start(info.address, static_cast<size_t>(req.threads_count), attrs, req.do_background_mining, req.ignore_battery))
{
res.status = "Failed, mining not started";
LOG_PRINT_L0(res.status);
@@ -733,7 +761,7 @@ namespace cryptonote
res.speed = lMiner.get_speed();
res.threads_count = lMiner.get_threads_count();
const account_public_address& lMiningAdr = lMiner.get_mining_address();
- res.address = get_account_address_as_str(m_testnet, lMiningAdr);
+ res.address = get_account_address_as_str(m_testnet, false, lMiningAdr);
}
res.status = CORE_RPC_STATUS_OK;
@@ -810,6 +838,7 @@ namespace cryptonote
bool core_rpc_server::on_set_log_categories(const COMMAND_RPC_SET_LOG_CATEGORIES::request& req, COMMAND_RPC_SET_LOG_CATEGORIES::response& res)
{
mlog_set_log(req.categories.c_str());
+ res.categories = mlog_get_categories();
res.status = CORE_RPC_STATUS_OK;
return true;
}
@@ -912,19 +941,25 @@ namespace cryptonote
return false;
}
- cryptonote::account_public_address acc = AUTO_VAL_INIT(acc);
+ cryptonote::address_parse_info info;
- if(!req.wallet_address.size() || !cryptonote::get_account_address_from_str(acc, m_testnet, req.wallet_address))
+ if(!req.wallet_address.size() || !cryptonote::get_account_address_from_str(info, m_testnet, req.wallet_address))
{
error_resp.code = CORE_RPC_ERROR_CODE_WRONG_WALLET_ADDRESS;
error_resp.message = "Failed to parse wallet address";
return false;
}
+ if (info.is_subaddress)
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_MINING_TO_SUBADDRESS;
+ error_resp.message = "Mining to subaddress is not supported yet";
+ return false;
+ }
block b = AUTO_VAL_INIT(b);
cryptonote::blobdata blob_reserve;
blob_reserve.resize(req.reserve_size, 0);
- if(!m_core.get_block_template(b, acc, res.difficulty, res.height, res.expected_reward, blob_reserve))
+ if(!m_core.get_block_template(b, info.address, res.difficulty, res.height, res.expected_reward, blob_reserve))
{
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
error_resp.message = "Internal error: failed to create block template";
@@ -948,7 +983,7 @@ namespace cryptonote
LOG_ERROR("Failed to find tx pub key in blockblob");
return false;
}
- res.reserved_offset += sizeof(tx_pub_key) + 3; //3 bytes: tag for TX_EXTRA_TAG_PUBKEY(1 byte), tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte)
+ res.reserved_offset += sizeof(tx_pub_key) + 2; //2 bytes: tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte)
if(res.reserved_offset + req.reserve_size > block_blob.size())
{
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
@@ -1048,13 +1083,7 @@ namespace cryptonote
}
uint64_t last_block_height;
crypto::hash last_block_hash;
- bool have_last_block_hash = m_core.get_blockchain_top(last_block_height, last_block_hash);
- if (!have_last_block_hash)
- {
- error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
- error_resp.message = "Internal error: can't get last block hash.";
- return false;
- }
+ m_core.get_blockchain_top(last_block_height, last_block_hash);
block last_block;
bool have_last_block = m_core.get_block_by_hash(last_block_hash, last_block);
if (!have_last_block)
@@ -1287,11 +1316,7 @@ namespace cryptonote
}
crypto::hash top_hash;
- if (!m_core.get_blockchain_top(res.height, top_hash))
- {
- res.status = "Failed";
- return false;
- }
+ m_core.get_blockchain_top(res.height, top_hash);
++res.height; // turn top block height into blockchain height
res.top_block_hash = string_tools::pod_to_hex(top_hash);
res.target_height = m_core.get_target_blockchain_height();
@@ -1383,7 +1408,7 @@ namespace cryptonote
}
else
{
- na.reset(new epee::net_utils::ipv4_network_address(i->ip, 0));
+ na = epee::net_utils::ipv4_network_address{i->ip, 0};
}
if (i->ban)
m_p2p.block_host(na, i->seconds);
@@ -1522,23 +1547,62 @@ namespace cryptonote
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_get_limit(const COMMAND_RPC_GET_LIMIT::request& req, COMMAND_RPC_GET_LIMIT::response& res)
+ {
+ res.limit_down = epee::net_utils::connection_basic::get_rate_down_limit();
+ res.limit_up = epee::net_utils::connection_basic::get_rate_up_limit();
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_set_limit(const COMMAND_RPC_SET_LIMIT::request& req, COMMAND_RPC_SET_LIMIT::response& res)
+ {
+ // -1 = reset to default
+ // 0 = do not modify
+
+ if (req.limit_down > 0)
+ {
+ epee::net_utils::connection_basic::set_rate_down_limit(req.limit_down);
+ }
+ else if (req.limit_down < 0)
+ {
+ if (req.limit_down != -1)
+ {
+ res.status = CORE_RPC_ERROR_CODE_WRONG_PARAM;
+ return false;
+ }
+ epee::net_utils::connection_basic::set_rate_down_limit(nodetool::default_limit_down * 1024);
+ }
+
+ if (req.limit_up > 0)
+ {
+ epee::net_utils::connection_basic::set_rate_up_limit(req.limit_up);
+ }
+ else if (req.limit_up < 0)
+ {
+ if (req.limit_up != -1)
+ {
+ res.status = CORE_RPC_ERROR_CODE_WRONG_PARAM;
+ return false;
+ }
+ epee::net_utils::connection_basic::set_rate_up_limit(nodetool::default_limit_up * 1024);
+ }
+
+ res.limit_down = epee::net_utils::connection_basic::get_rate_down_limit();
+ res.limit_up = epee::net_utils::connection_basic::get_rate_up_limit();
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_out_peers(const COMMAND_RPC_OUT_PEERS::request& req, COMMAND_RPC_OUT_PEERS::response& res)
{
- // TODO
- /*if (m_p2p.get_outgoing_connections_count() > req.out_peers)
- {
- m_p2p.m_config.m_net_config.connections_count = req.out_peers;
- if (m_p2p.get_outgoing_connections_count() > req.out_peers)
- {
- int count = m_p2p.get_outgoing_connections_count() - req.out_peers;
- m_p2p.delete_connections(count);
- }
- }
-
- else
- m_p2p.m_config.m_net_config.connections_count = req.out_peers;
- */
- return true;
+ size_t n_connections = m_p2p.get_outgoing_connections_count();
+ size_t n_delete = (n_connections > req.out_peers) ? n_connections - req.out_peers : 0;
+ m_p2p.m_config.m_net_config.connections_count = req.out_peers;
+ if (n_delete)
+ m_p2p.delete_connections(n_delete);
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_start_save_graph(const COMMAND_RPC_START_SAVE_GRAPH::request& req, COMMAND_RPC_START_SAVE_GRAPH::response& res)
@@ -1560,8 +1624,10 @@ namespace cryptonote
static const char software[] = "monero";
#ifdef BUILD_TAG
static const char buildtag[] = BOOST_PP_STRINGIZE(BUILD_TAG);
+ static const char subdir[] = "cli";
#else
static const char buildtag[] = "source";
+ static const char subdir[] = "source";
#endif
if (req.command != "check" && req.command != "download" && req.command != "update")
@@ -1584,8 +1650,8 @@ namespace cryptonote
}
res.update = true;
res.version = version;
- res.user_uri = tools::get_update_url(software, "cli", buildtag, version, true);
- res.auto_uri = tools::get_update_url(software, "cli", buildtag, version, false);
+ res.user_uri = tools::get_update_url(software, subdir, buildtag, version, true);
+ res.auto_uri = tools::get_update_url(software, subdir, buildtag, version, false);
res.hash = hash;
if (req.command == "check")
{
@@ -1703,11 +1769,7 @@ namespace cryptonote
}
crypto::hash top_hash;
- if (!m_core.get_blockchain_top(res.height, top_hash))
- {
- res.status = "Failed";
- return false;
- }
+ m_core.get_blockchain_top(res.height, top_hash);
++res.height; // turn top block height into blockchain height
res.target_height = m_core.get_target_blockchain_height();
diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h
index dbbe07972..73a308a72 100644
--- a/src/rpc/core_rpc_server.h
+++ b/src/rpc/core_rpc_server.h
@@ -97,6 +97,8 @@ namespace cryptonote
MAP_URI_AUTO_JON2("/get_transaction_pool_stats", on_get_transaction_pool_stats, COMMAND_RPC_GET_TRANSACTION_POOL_STATS)
MAP_URI_AUTO_JON2_IF("/stop_daemon", on_stop_daemon, COMMAND_RPC_STOP_DAEMON, !m_restricted)
MAP_URI_AUTO_JON2("/getinfo", on_get_info, COMMAND_RPC_GET_INFO)
+ MAP_URI_AUTO_JON2("/get_limit", on_get_limit, COMMAND_RPC_GET_LIMIT)
+ MAP_URI_AUTO_JON2_IF("/set_limit", on_set_limit, COMMAND_RPC_SET_LIMIT, !m_restricted)
MAP_URI_AUTO_JON2_IF("/out_peers", on_out_peers, COMMAND_RPC_OUT_PEERS, !m_restricted)
MAP_URI_AUTO_JON2_IF("/start_save_graph", on_start_save_graph, COMMAND_RPC_START_SAVE_GRAPH, !m_restricted)
MAP_URI_AUTO_JON2_IF("/stop_save_graph", on_stop_save_graph, COMMAND_RPC_STOP_SAVE_GRAPH, !m_restricted)
@@ -155,6 +157,8 @@ namespace cryptonote
bool on_get_transaction_pool_hashes(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::response& res);
bool on_get_transaction_pool_stats(const COMMAND_RPC_GET_TRANSACTION_POOL_STATS::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_STATS::response& res);
bool on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res);
+ bool on_get_limit(const COMMAND_RPC_GET_LIMIT::request& req, COMMAND_RPC_GET_LIMIT::response& res);
+ bool on_set_limit(const COMMAND_RPC_SET_LIMIT::request& req, COMMAND_RPC_SET_LIMIT::response& res);
bool on_out_peers(const COMMAND_RPC_OUT_PEERS::request& req, COMMAND_RPC_OUT_PEERS::response& res);
bool on_start_save_graph(const COMMAND_RPC_START_SAVE_GRAPH::request& req, COMMAND_RPC_START_SAVE_GRAPH::response& res);
bool on_stop_save_graph(const COMMAND_RPC_STOP_SAVE_GRAPH::request& req, COMMAND_RPC_STOP_SAVE_GRAPH::response& res);
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index 88327dd75..ee2a79eb4 100644
--- a/src/rpc/core_rpc_server_commands_defs.h
+++ b/src/rpc/core_rpc_server_commands_defs.h
@@ -49,7 +49,7 @@ namespace cryptonote
// advance which version they will stop working with
// Don't go over 32767 for any of these
#define CORE_RPC_VERSION_MAJOR 1
-#define CORE_RPC_VERSION_MINOR 13
+#define CORE_RPC_VERSION_MINOR 15
#define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor))
#define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR)
@@ -195,6 +195,358 @@ namespace cryptonote
};
//-----------------------------------------------
+ struct COMMAND_RPC_GET_ADDRESS_TXS
+ {
+ struct request
+ {
+ std::string address;
+ std::string view_key;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(view_key)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct spent_output {
+ uint64_t amount;
+ std::string key_image;
+ std::string tx_pub_key;
+ uint64_t out_index;
+ uint32_t mixin;
+
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(amount)
+ KV_SERIALIZE(key_image)
+ KV_SERIALIZE(tx_pub_key)
+ KV_SERIALIZE(out_index)
+ KV_SERIALIZE(mixin)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct transaction
+ {
+ uint64_t id;
+ std::string hash;
+ uint64_t timestamp;
+ uint64_t total_received;
+ uint64_t total_sent;
+ uint64_t unlock_time;
+ uint64_t height;
+ std::list<spent_output> spent_outputs;
+ std::string payment_id;
+ bool coinbase;
+ bool mempool;
+ uint32_t mixin;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(id)
+ KV_SERIALIZE(hash)
+ KV_SERIALIZE(timestamp)
+ KV_SERIALIZE(total_received)
+ KV_SERIALIZE(total_sent)
+ KV_SERIALIZE(unlock_time)
+ KV_SERIALIZE(height)
+ KV_SERIALIZE(spent_outputs)
+ KV_SERIALIZE(payment_id)
+ KV_SERIALIZE(coinbase)
+ KV_SERIALIZE(mempool)
+ KV_SERIALIZE(mixin)
+ END_KV_SERIALIZE_MAP()
+ };
+
+
+ struct response
+ {
+ //std::list<std::string> txs_as_json;
+ uint64_t total_received;
+ uint64_t total_received_unlocked = 0; // OpenMonero only
+ uint64_t scanned_height;
+ std::list<transaction> transactions;
+ uint64_t blockchain_height;
+ uint64_t scanned_block_height;
+ std::string status;
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(total_received)
+ KV_SERIALIZE(total_received_unlocked)
+ KV_SERIALIZE(scanned_height)
+ KV_SERIALIZE(transactions)
+ KV_SERIALIZE(blockchain_height)
+ KV_SERIALIZE(scanned_block_height)
+ KV_SERIALIZE(status)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ //-----------------------------------------------
+ struct COMMAND_RPC_GET_ADDRESS_INFO
+ {
+ struct request
+ {
+ std::string address;
+ std::string view_key;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(view_key)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct spent_output
+ {
+ uint64_t amount;
+ std::string key_image;
+ std::string tx_pub_key;
+ uint64_t out_index;
+ uint32_t mixin;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(amount)
+ KV_SERIALIZE(key_image)
+ KV_SERIALIZE(tx_pub_key)
+ KV_SERIALIZE(out_index)
+ KV_SERIALIZE(mixin)
+ END_KV_SERIALIZE_MAP()
+ };
+
+
+
+ struct response
+ {
+ uint64_t locked_funds;
+ uint64_t total_received;
+ uint64_t total_sent;
+ uint64_t scanned_height;
+ uint64_t scanned_block_height;
+ uint64_t start_height;
+ uint64_t transaction_height;
+ uint64_t blockchain_height;
+ std::list<spent_output> spent_outputs;
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(locked_funds)
+ KV_SERIALIZE(total_received)
+ KV_SERIALIZE(total_sent)
+ KV_SERIALIZE(scanned_height)
+ KV_SERIALIZE(scanned_block_height)
+ KV_SERIALIZE(start_height)
+ KV_SERIALIZE(transaction_height)
+ KV_SERIALIZE(blockchain_height)
+ KV_SERIALIZE(spent_outputs)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ //-----------------------------------------------
+ struct COMMAND_RPC_GET_UNSPENT_OUTS
+ {
+ struct request
+ {
+ std::string amount;
+ std::string address;
+ std::string view_key;
+ // OpenMonero specific
+ uint64_t mixin;
+ bool use_dust;
+ std::string dust_threshold;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(amount)
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(view_key)
+ KV_SERIALIZE(mixin)
+ KV_SERIALIZE(use_dust)
+ KV_SERIALIZE(dust_threshold)
+ END_KV_SERIALIZE_MAP()
+ };
+
+
+ struct output {
+ uint64_t amount;
+ std::string public_key;
+ uint64_t index;
+ uint64_t global_index;
+ std::string rct;
+ std::string tx_hash;
+ std::string tx_pub_key;
+ std::string tx_prefix_hash;
+ std::vector<std::string> spend_key_images;
+ uint64_t timestamp;
+ uint64_t height;
+
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(amount)
+ KV_SERIALIZE(public_key)
+ KV_SERIALIZE(index)
+ KV_SERIALIZE(global_index)
+ KV_SERIALIZE(rct)
+ KV_SERIALIZE(tx_hash)
+ KV_SERIALIZE(tx_pub_key)
+ KV_SERIALIZE(tx_prefix_hash)
+ KV_SERIALIZE(spend_key_images)
+ KV_SERIALIZE(timestamp)
+ KV_SERIALIZE(height)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ uint64_t amount;
+ std::list<output> outputs;
+ uint64_t per_kb_fee;
+ std::string status;
+ std::string reason;
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(amount)
+ KV_SERIALIZE(outputs)
+ KV_SERIALIZE(per_kb_fee)
+ KV_SERIALIZE(status)
+ KV_SERIALIZE(reason)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ //-----------------------------------------------
+ struct COMMAND_RPC_GET_RANDOM_OUTS
+ {
+ struct request
+ {
+ std::vector<std::string> amounts;
+ uint32_t count;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(amounts)
+ KV_SERIALIZE(count)
+ END_KV_SERIALIZE_MAP()
+ };
+
+
+ struct output {
+ std::string public_key;
+ uint64_t global_index;
+ std::string rct; // 64+64+64 characters long (<rct commit> + <encrypted mask> + <rct amount>)
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(public_key)
+ KV_SERIALIZE(global_index)
+ KV_SERIALIZE(rct)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct amount_out {
+ uint64_t amount;
+ std::vector<output> outputs;
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(amount)
+ KV_SERIALIZE(outputs)
+ END_KV_SERIALIZE_MAP()
+
+ };
+
+ struct response
+ {
+ std::vector<amount_out> amount_outs;
+ std::string Error;
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(amount_outs)
+ KV_SERIALIZE(Error)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+ //-----------------------------------------------
+ struct COMMAND_RPC_SUBMIT_RAW_TX
+ {
+ struct request
+ {
+ std::string address;
+ std::string view_key;
+ std::string tx;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(view_key)
+ KV_SERIALIZE(tx)
+ END_KV_SERIALIZE_MAP()
+ };
+
+
+ struct response
+ {
+ std::string status;
+ std::string error;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(status)
+ KV_SERIALIZE(error)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+ //-----------------------------------------------
+ struct COMMAND_RPC_LOGIN
+ {
+ struct request
+ {
+ std::string address;
+ std::string view_key;
+ bool create_account;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(view_key)
+ KV_SERIALIZE(create_account)
+ END_KV_SERIALIZE_MAP()
+ };
+
+
+ struct response
+ {
+ std::string status;
+ std::string reason;
+ bool new_address;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(status)
+ KV_SERIALIZE(reason)
+ KV_SERIALIZE(new_address)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+ //-----------------------------------------------
+ struct COMMAND_RPC_IMPORT_WALLET_REQUEST
+ {
+ struct request
+ {
+ std::string address;
+ std::string view_key;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(view_key)
+ END_KV_SERIALIZE_MAP()
+ };
+
+
+ struct response
+ {
+ std::string payment_id;
+ uint64_t import_fee;
+ bool new_request;
+ bool request_fulfilled;
+ std::string payment_address;
+ std::string status;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(payment_id)
+ KV_SERIALIZE(import_fee)
+ KV_SERIALIZE(new_request)
+ KV_SERIALIZE(request_fulfilled)
+ KV_SERIALIZE(payment_address)
+ KV_SERIALIZE(status)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+ //-----------------------------------------------
struct COMMAND_RPC_GET_TRANSACTIONS
{
struct request
@@ -215,6 +567,7 @@ namespace cryptonote
std::string as_json;
bool in_pool;
uint64_t block_height;
+ uint64_t block_timestamp;
std::vector<uint64_t> output_indices;
BEGIN_KV_SERIALIZE_MAP()
@@ -223,6 +576,7 @@ namespace cryptonote
KV_SERIALIZE(as_json)
KV_SERIALIZE(in_pool)
KV_SERIALIZE(block_height)
+ KV_SERIALIZE(block_timestamp)
KV_SERIALIZE(output_indices)
END_KV_SERIALIZE_MAP()
};
@@ -979,8 +1333,11 @@ namespace cryptonote
struct response
{
std::string status;
+ std::string categories;
+
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
+ KV_SERIALIZE(categories)
END_KV_SERIALIZE_MAP()
};
};
@@ -1242,6 +1599,55 @@ namespace cryptonote
};
};
+ struct COMMAND_RPC_GET_LIMIT
+ {
+ struct request
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ std::string status;
+ uint64_t limit_up;
+ uint64_t limit_down;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(status)
+ KV_SERIALIZE(limit_up)
+ KV_SERIALIZE(limit_down)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ struct COMMAND_RPC_SET_LIMIT
+ {
+ struct request
+ {
+ int64_t limit_down;
+ int64_t limit_up;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(limit_down)
+ KV_SERIALIZE(limit_up)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ std::string status;
+ uint64_t limit_up;
+ uint64_t limit_down;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(status)
+ KV_SERIALIZE(limit_up)
+ KV_SERIALIZE(limit_down)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
struct COMMAND_RPC_OUT_PEERS
{
struct request
diff --git a/src/rpc/core_rpc_server_error_codes.h b/src/rpc/core_rpc_server_error_codes.h
index 269cce2b1..bd90d37aa 100644
--- a/src/rpc/core_rpc_server_error_codes.h
+++ b/src/rpc/core_rpc_server_error_codes.h
@@ -41,5 +41,6 @@
#define CORE_RPC_ERROR_CODE_CORE_BUSY -9
#define CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB_SIZE -10
#define CORE_RPC_ERROR_CODE_UNSUPPORTED_RPC -11
+#define CORE_RPC_ERROR_CODE_MINING_TO_SUBADDRESS -12
diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp
index 53eeb5e76..4d3fbf491 100644
--- a/src/rpc/daemon_handler.cpp
+++ b/src/rpc/daemon_handler.cpp
@@ -412,14 +412,21 @@ namespace rpc
void DaemonHandler::handle(const StartMining::Request& req, StartMining::Response& res)
{
- account_public_address adr;
- if(!get_account_address_from_str(adr, m_core.get_testnet(), req.miner_address))
+ cryptonote::address_parse_info info;
+ if(!get_account_address_from_str(info, m_core.get_testnet(), req.miner_address))
{
res.error_details = "Failed, wrong address";
LOG_PRINT_L0(res.error_details);
res.status = Message::STATUS_FAILED;
return;
}
+ if (info.is_subaddress)
+ {
+ res.error_details = "Failed, mining to subaddress isn't supported yet";
+ LOG_PRINT_L0(res.error_details);
+ res.status = Message::STATUS_FAILED;
+ return;
+ }
unsigned int concurrency_count = boost::thread::hardware_concurrency() * 4;
@@ -442,7 +449,7 @@ namespace rpc
boost::thread::attributes attrs;
attrs.set_stack_size(THREAD_STACK_SIZE);
- if(!m_core.get_miner().start(adr, static_cast<size_t>(req.threads_count), attrs, req.do_background_mining, req.ignore_battery))
+ if(!m_core.get_miner().start(info.address, static_cast<size_t>(req.threads_count), attrs, req.do_background_mining, req.ignore_battery))
{
res.error_details = "Failed, mining not started";
LOG_PRINT_L0(res.error_details);
@@ -518,7 +525,7 @@ namespace rpc
res.speed = lMiner.get_speed();
res.threads_count = lMiner.get_threads_count();
const account_public_address& lMiningAdr = lMiner.get_mining_address();
- res.address = get_account_address_as_str(m_core.get_testnet(), lMiningAdr);
+ res.address = get_account_address_as_str(m_core.get_testnet(), false, lMiningAdr);
}
res.status = Message::STATUS_OK;
@@ -542,7 +549,7 @@ namespace rpc
{
if (m_core.get_current_blockchain_height() <= req.height)
{
- res.hash = cryptonote::null_hash;
+ res.hash = crypto::null_hash;
res.status = Message::STATUS_FAILED;
res.error_details = "height given is higher than current chain height";
return;
diff --git a/src/rpc/message.cpp b/src/rpc/message.cpp
index 086820180..d6da124d1 100644
--- a/src/rpc/message.cpp
+++ b/src/rpc/message.cpp
@@ -45,6 +45,15 @@ const char* Message::STATUS_FAILED = "Failed";
const char* Message::STATUS_BAD_REQUEST = "Invalid request type";
const char* Message::STATUS_BAD_JSON = "Malformed json";
+namespace
+{
+constexpr const char error_field[] = "error";
+constexpr const char id_field[] = "id";
+constexpr const char method_field[] = "method";
+constexpr const char params_field[] = "params";
+constexpr const char result_field[] = "result";
+}
+
rapidjson::Value Message::toJson(rapidjson::Document& doc) const
{
rapidjson::Value val(rapidjson::kObjectType);
@@ -70,8 +79,8 @@ FullMessage::FullMessage(const std::string& request, Message* message)
{
doc.SetObject();
- doc.AddMember("method", rapidjson::StringRef(request.c_str()), doc.GetAllocator());
- doc.AddMember("params", message->toJson(doc), doc.GetAllocator());
+ doc.AddMember(method_field, rapidjson::StringRef(request.c_str()), doc.GetAllocator());
+ doc.AddMember(params_field, message->toJson(doc), doc.GetAllocator());
// required by JSON-RPC 2.0 spec
doc.AddMember("jsonrpc", rapidjson::Value("2.0"), doc.GetAllocator());
@@ -86,7 +95,7 @@ FullMessage::FullMessage(Message* message)
if (message->status == Message::STATUS_OK)
{
- doc.AddMember("response", message->toJson(doc), doc.GetAllocator());
+ doc.AddMember(result_field, message->toJson(doc), doc.GetAllocator());
}
else
{
@@ -111,14 +120,14 @@ FullMessage::FullMessage(const std::string& json_string, bool request)
if (request)
{
- OBJECT_HAS_MEMBER_OR_THROW(doc, "method")
- OBJECT_HAS_MEMBER_OR_THROW(doc, "params")
+ OBJECT_HAS_MEMBER_OR_THROW(doc, method_field)
+ OBJECT_HAS_MEMBER_OR_THROW(doc, params_field)
}
else
{
- if (!doc.HasMember("response") && !doc.HasMember("error"))
+ if (!doc.HasMember(result_field) && !doc.HasMember(error_field))
{
- throw cryptonote::json::MISSING_KEY("error/response");
+ throw cryptonote::json::MISSING_KEY("error/result");
}
}
}
@@ -126,9 +135,9 @@ FullMessage::FullMessage(const std::string& json_string, bool request)
std::string FullMessage::getJson()
{
- if (!doc.HasMember("id"))
+ if (!doc.HasMember(id_field))
{
- doc.AddMember("id", rapidjson::Value("unused"), doc.GetAllocator());
+ doc.AddMember(id_field, rapidjson::Value("unused"), doc.GetAllocator());
}
rapidjson::StringBuffer buf;
@@ -142,24 +151,24 @@ std::string FullMessage::getJson()
std::string FullMessage::getRequestType() const
{
- OBJECT_HAS_MEMBER_OR_THROW(doc, "method")
- return doc["method"].GetString();
+ OBJECT_HAS_MEMBER_OR_THROW(doc, method_field)
+ return doc[method_field].GetString();
}
rapidjson::Value& FullMessage::getMessage()
{
- if (doc.HasMember("params"))
+ if (doc.HasMember(params_field))
{
- return doc["params"];
+ return doc[params_field];
}
- else if (doc.HasMember("response"))
+ else if (doc.HasMember(result_field))
{
- return doc["response"];
+ return doc[result_field];
}
//else
- OBJECT_HAS_MEMBER_OR_THROW(doc, "error")
- return doc["error"];
+ OBJECT_HAS_MEMBER_OR_THROW(doc, error_field)
+ return doc[error_field];
}
@@ -172,20 +181,20 @@ rapidjson::Value FullMessage::getMessageCopy()
rapidjson::Value& FullMessage::getID()
{
- OBJECT_HAS_MEMBER_OR_THROW(doc, "id")
- return doc["id"];
+ OBJECT_HAS_MEMBER_OR_THROW(doc, id_field)
+ return doc[id_field];
}
void FullMessage::setID(rapidjson::Value& id)
{
- auto itr = doc.FindMember("id");
+ auto itr = doc.FindMember(id_field);
if (itr != doc.MemberEnd())
{
itr->value = id;
}
else
{
- doc.AddMember("id", id, doc.GetAllocator());
+ doc.AddMember(id_field, id, doc.GetAllocator());
}
}
@@ -193,7 +202,7 @@ cryptonote::rpc::error FullMessage::getError()
{
cryptonote::rpc::error err;
err.use = false;
- if (doc.HasMember("error"))
+ if (doc.HasMember(error_field))
{
GET_FROM_JSON_OBJECT(doc, err, error);
err.use = true;
diff --git a/src/rpc/rpc_args.cpp b/src/rpc/rpc_args.cpp
index 4435f74d1..93309bf3c 100644..100755
--- a/src/rpc/rpc_args.cpp
+++ b/src/rpc/rpc_args.cpp
@@ -28,6 +28,7 @@
//
#include "rpc_args.h"
+#include <boost/algorithm/string.hpp>
#include <boost/asio/ip/address.hpp>
#include "common/command_line.h"
#include "common/i18n.h"
@@ -38,6 +39,7 @@ namespace cryptonote
: rpc_bind_ip({"rpc-bind-ip", rpc_args::tr("Specify ip to bind rpc server"), "127.0.0.1"})
, rpc_login({"rpc-login", rpc_args::tr("Specify username[:password] required for RPC server"), "", true})
, confirm_external_bind({"confirm-external-bind", rpc_args::tr("Confirm rpc-bind-ip value is NOT a loopback (local) IP")})
+ , rpc_access_control_origins({"rpc-access-control-origins", rpc_args::tr("Specify a comma separated list of origins to allow cross origin resource sharing"), ""})
{}
const char* rpc_args::tr(const char* str) { return i18n_translate(str, "cryptonote::rpc_args"); }
@@ -48,6 +50,7 @@ namespace cryptonote
command_line::add_arg(desc, arg.rpc_bind_ip);
command_line::add_arg(desc, arg.rpc_login);
command_line::add_arg(desc, arg.confirm_external_bind);
+ command_line::add_arg(desc, arg.rpc_access_control_origins);
}
boost::optional<rpc_args> rpc_args::process(const boost::program_options::variables_map& vm)
@@ -91,6 +94,21 @@ namespace cryptonote
}
}
+ auto access_control_origins_input = command_line::get_arg(vm, arg.rpc_access_control_origins);
+ if (!access_control_origins_input.empty())
+ {
+ if (!config.login)
+ {
+ LOG_ERROR(arg.rpc_access_control_origins.name << tr(" requires RFC server password --") << arg.rpc_login.name << tr(" cannot be empty"));
+ return boost::none;
+ }
+
+ std::vector<std::string> access_control_origins;
+ boost::split(access_control_origins, access_control_origins_input, boost::is_any_of(","));
+ std::for_each(access_control_origins.begin(), access_control_origins.end(), boost::bind(&boost::trim<std::string>, _1, std::locale::classic()));
+ config.access_control_origins = std::move(access_control_origins);
+ }
+
return {std::move(config)};
}
}
diff --git a/src/rpc/rpc_args.h b/src/rpc/rpc_args.h
index d6e7bab07..72b5aa706 100644..100755
--- a/src/rpc/rpc_args.h
+++ b/src/rpc/rpc_args.h
@@ -53,6 +53,7 @@ namespace cryptonote
const command_line::arg_descriptor<std::string> rpc_bind_ip;
const command_line::arg_descriptor<std::string> rpc_login;
const command_line::arg_descriptor<bool> confirm_external_bind;
+ const command_line::arg_descriptor<std::string> rpc_access_control_origins;
};
static const char* tr(const char* str);
@@ -62,6 +63,7 @@ namespace cryptonote
static boost::optional<rpc_args> process(const boost::program_options::variables_map& vm);
std::string bind_ip;
+ std::vector<std::string> access_control_origins;
boost::optional<tools::login> login; // currently `boost::none` if unspecified by user
};
}