aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authormoneromooo-monero <moneromooo-monero@users.noreply.github.com>2018-02-19 11:15:15 +0000
committermoneromooo-monero <moneromooo-monero@users.noreply.github.com>2018-03-16 10:32:26 +0000
commit41f727ce42d0fce9e40e4da7bf4abda92eef1016 (patch)
treefec7f8482cf1fdd7e927b91dbab563e785724a5e /src
parentMerge pull request #3410 (diff)
downloadmonero-41f727ce42d0fce9e40e4da7bf4abda92eef1016.tar.xz
add RPC to get a histogram of outputs of a given amount
Diffstat (limited to 'src')
-rw-r--r--src/blockchain_db/blockchain_db.h4
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp44
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.h3
-rw-r--r--src/cryptonote_core/blockchain.cpp46
-rw-r--r--src/cryptonote_core/blockchain.h23
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp5
-rw-r--r--src/cryptonote_core/cryptonote_core.h6
-rw-r--r--src/rpc/core_rpc_server.cpp35
-rw-r--r--src/rpc/core_rpc_server.h2
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h45
-rw-r--r--src/wallet/wallet2.cpp65
-rw-r--r--src/wallet/wallet2.h2
-rw-r--r--src/wallet/wallet_errors.h9
13 files changed, 282 insertions, 7 deletions
diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h
index cce288793..d7230f644 100644
--- a/src/blockchain_db/blockchain_db.h
+++ b/src/blockchain_db/blockchain_db.h
@@ -1448,7 +1448,9 @@ public:
*
* @return false if the function returns false for any output, otherwise true
*/
- virtual bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, size_t tx_idx)> f) const = 0;
+ virtual bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, uint64_t height, size_t tx_idx)> f) const = 0;
+ virtual bool for_all_outputs(uint64_t amount, const std::function<bool(uint64_t height)> &f) const = 0;
+
//
diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp
index f9c829013..9a755fb0c 100644
--- a/src/blockchain_db/lmdb/db_lmdb.cpp
+++ b/src/blockchain_db/lmdb/db_lmdb.cpp
@@ -2564,7 +2564,7 @@ bool BlockchainLMDB::for_all_transactions(std::function<bool(const crypto::hash&
return fret;
}
-bool BlockchainLMDB::for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, size_t tx_idx)> f) const
+bool BlockchainLMDB::for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, uint64_t height, size_t tx_idx)> f) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
@@ -2588,7 +2588,47 @@ bool BlockchainLMDB::for_all_outputs(std::function<bool(uint64_t amount, const c
uint64_t amount = *(const uint64_t*)k.mv_data;
outkey *ok = (outkey *)v.mv_data;
tx_out_index toi = get_output_tx_and_index_from_global(ok->output_id);
- if (!f(amount, toi.first, toi.second)) {
+ if (!f(amount, toi.first, ok->data.height, toi.second)) {
+ fret = false;
+ break;
+ }
+ }
+
+ TXN_POSTFIX_RDONLY();
+
+ return fret;
+}
+
+bool BlockchainLMDB::for_all_outputs(uint64_t amount, const std::function<bool(uint64_t height)> &f) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ TXN_PREFIX_RDONLY();
+ RCURSOR(output_amounts);
+
+ MDB_val_set(k, amount);
+ MDB_val v;
+ bool fret = true;
+
+ MDB_cursor_op op = MDB_SET;
+ while (1)
+ {
+ int ret = mdb_cursor_get(m_cur_output_amounts, &k, &v, op);
+ op = MDB_NEXT_DUP;
+ if (ret == MDB_NOTFOUND)
+ break;
+ if (ret)
+ throw0(DB_ERROR("Failed to enumerate outputs"));
+ uint64_t out_amount = *(const uint64_t*)k.mv_data;
+ if (amount != out_amount)
+ {
+ MERROR("Amount is not the expected amount");
+ fret = false;
+ break;
+ }
+ const outkey *ok = (const outkey *)v.mv_data;
+ if (!f(ok->data.height)) {
fret = false;
break;
}
diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h
index 1b76abf35..0aa4bb86e 100644
--- a/src/blockchain_db/lmdb/db_lmdb.h
+++ b/src/blockchain_db/lmdb/db_lmdb.h
@@ -255,7 +255,8 @@ public:
virtual bool for_all_key_images(std::function<bool(const crypto::key_image&)>) const;
virtual bool for_blocks_range(const uint64_t& h1, const uint64_t& h2, std::function<bool(uint64_t, const crypto::hash&, const cryptonote::block&)>) const;
virtual bool for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)>) const;
- virtual bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, size_t tx_idx)> f) const;
+ virtual bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, uint64_t height, size_t tx_idx)> f) const;
+ virtual bool for_all_outputs(uint64_t amount, const std::function<bool(uint64_t height)> &f) const;
virtual uint64_t add_block( const block& blk
, const size_t& block_size
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index a0031559b..0a6f1cdcd 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -1903,6 +1903,45 @@ void Blockchain::get_output_key_mask_unlocked(const uint64_t& amount, const uint
unlocked = is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first));
}
//------------------------------------------------------------------
+bool Blockchain::get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t &start_height, std::vector<uint64_t> &distribution, uint64_t &base) const
+{
+ // rct outputs don't exist before v3
+ if (amount == 0)
+ {
+ switch (m_nettype)
+ {
+ case STAGENET: start_height = stagenet_hard_forks[2].height; break;
+ case TESTNET: start_height = testnet_hard_forks[2].height; break;
+ case MAINNET: start_height = mainnet_hard_forks[2].height; break;
+ default: return false;
+ }
+ }
+ else
+ start_height = 0;
+ base = 0;
+
+ const uint64_t real_start_height = start_height;
+ if (from_height > start_height)
+ start_height = from_height;
+
+ distribution.clear();
+ uint64_t db_height = m_db->height();
+ if (start_height >= db_height)
+ return false;
+ distribution.resize(db_height - start_height, 0);
+ bool r = for_all_outputs(amount, [&](uint64_t height) {
+ CHECK_AND_ASSERT_MES(height >= real_start_height && height <= db_height, false, "Height not in expected range");
+ if (height >= start_height)
+ distribution[height - start_height]++;
+ else
+ base++;
+ return true;
+ });
+ if (!r)
+ return false;
+ return true;
+}
+//------------------------------------------------------------------
// This function takes a list of block hashes from another node
// on the network to find where the split point is between us and them.
// This is used to see what to send another node that needs to sync.
@@ -4441,11 +4480,16 @@ bool Blockchain::for_all_transactions(std::function<bool(const crypto::hash&, co
return m_db->for_all_transactions(f);
}
-bool Blockchain::for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, size_t tx_idx)> f) const
+bool Blockchain::for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, uint64_t height, size_t tx_idx)> f) const
{
return m_db->for_all_outputs(f);;
}
+bool Blockchain::for_all_outputs(uint64_t amount, std::function<bool(uint64_t height)> f) const
+{
+ return m_db->for_all_outputs(amount, f);;
+}
+
namespace cryptonote {
template bool Blockchain::get_transactions(const std::vector<crypto::hash>&, std::list<transaction>&, std::list<crypto::hash>&) const;
}
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index 1a52e20bf..4423199de 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -524,6 +524,17 @@ namespace cryptonote
bool get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const;
/**
+ * @brief gets per block distribution of outputs of a given amount
+ *
+ * @param amount the amount to get a ditribution for
+ * @param return-by-reference from_height the height before which we do not care about the data
+ * @param return-by-reference start_height the height of the first rct output
+ * @param return-by-reference distribution the start offset of the first rct output in this block (same as previous if none)
+ * @param return-by-reference base how many outputs of that amount are before the stated distribution
+ */
+ bool get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t &start_height, std::vector<uint64_t> &distribution, uint64_t &base) const;
+
+ /**
* @brief gets the global indices for outputs from a given transaction
*
* This function gets the global indices for all outputs belonging
@@ -857,7 +868,17 @@ namespace cryptonote
*
* @return false if any output fails the check, otherwise true
*/
- bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, size_t tx_idx)>) const;
+ bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, uint64_t height, size_t tx_idx)>) const;
+
+ /**
+ * @brief perform a check on all outputs of a given amount in the blockchain
+ *
+ * @param amount the amount to iterate through
+ * @param std::function the check to perform, pass/fail
+ *
+ * @return false if any output fails the check, otherwise true
+ */
+ bool for_all_outputs(uint64_t amount, std::function<bool(uint64_t height)>) const;
/**
* @brief get a reference to the BlockchainDB in use by Blockchain
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 8c0118803..a91cc9638 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -1105,6 +1105,11 @@ namespace cryptonote
return m_blockchain_storage.get_random_rct_outs(req, res);
}
//-----------------------------------------------------------------------------------------------
+ bool core::get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t &start_height, std::vector<uint64_t> &distribution, uint64_t &base) const
+ {
+ return m_blockchain_storage.get_output_distribution(amount, from_height, start_height, distribution, base);
+ }
+ //-----------------------------------------------------------------------------------------------
bool core::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<uint64_t>& indexs) const
{
return m_blockchain_storage.get_tx_outputs_gindexs(tx_id, indexs);
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index e1e430516..abf79be1d 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -571,6 +571,12 @@ namespace cryptonote
*/
bool get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const;
+ /**
+ * @copydoc Blockchain::get_output_distribution
+ *
+ * @brief get per block distribution of outputs of a given amount
+ */
+ bool get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t &start_height, std::vector<uint64_t> &distribution, uint64_t &base) const;
/**
* @copydoc miner::pause
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index 0e3e6757b..f649a0ca5 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -2076,6 +2076,41 @@ namespace cryptonote
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_get_output_distribution(const COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request& req, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response& res, epee::json_rpc::error& error_resp)
+ {
+ try
+ {
+ for (uint64_t amount: req.amounts)
+ {
+ std::vector<uint64_t> distribution;
+ uint64_t start_height, base;
+ if (!m_core.get_output_distribution(amount, req.from_height, start_height, distribution, base))
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
+ error_resp.message = "Failed to get rct distribution";
+ return false;
+ }
+ if (req.cumulative)
+ {
+ distribution[0] += base;
+ for (size_t n = 1; n < distribution.size(); ++n)
+ distribution[n] += distribution[n-1];
+ }
+ res.distributions.push_back({amount, start_height, std::move(distribution), base});
+ }
+ }
+ catch (const std::exception &e)
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
+ error_resp.message = "Failed to get output distribution";
+ return false;
+ }
+
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+
const command_line::arg_descriptor<std::string, false, true, 2> core_rpc_server::arg_rpc_bind_port = {
"rpc-bind-port"
diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h
index 4754a5d5f..a5755e062 100644
--- a/src/rpc/core_rpc_server.h
+++ b/src/rpc/core_rpc_server.h
@@ -153,6 +153,7 @@ namespace cryptonote
MAP_JON_RPC_WE_IF("relay_tx", on_relay_tx, COMMAND_RPC_RELAY_TX, !m_restricted)
MAP_JON_RPC_WE_IF("sync_info", on_sync_info, COMMAND_RPC_SYNC_INFO, !m_restricted)
MAP_JON_RPC_WE("get_txpool_backlog", on_get_txpool_backlog, COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG)
+ MAP_JON_RPC_WE_IF("get_output_distribution", on_get_output_distribution, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION, !m_restricted)
END_JSON_RPC_MAP()
END_URI_MAP2()
@@ -214,6 +215,7 @@ namespace cryptonote
bool on_relay_tx(const COMMAND_RPC_RELAY_TX::request& req, COMMAND_RPC_RELAY_TX::response& res, epee::json_rpc::error& error_resp);
bool on_sync_info(const COMMAND_RPC_SYNC_INFO::request& req, COMMAND_RPC_SYNC_INFO::response& res, epee::json_rpc::error& error_resp);
bool on_get_txpool_backlog(const COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG::response& res, epee::json_rpc::error& error_resp);
+ bool on_get_output_distribution(const COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request& req, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response& res, epee::json_rpc::error& error_resp);
//-----------------------
private:
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index a2c780376..660fb7889 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 18
+#define CORE_RPC_VERSION_MINOR 19
#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)
@@ -2203,4 +2203,47 @@ namespace cryptonote
END_KV_SERIALIZE_MAP()
};
};
+
+ struct COMMAND_RPC_GET_OUTPUT_DISTRIBUTION
+ {
+ struct request
+ {
+ std::vector<uint64_t> amounts;
+ uint64_t from_height;
+ bool cumulative;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(amounts)
+ KV_SERIALIZE_OPT(from_height, (uint64_t)0)
+ KV_SERIALIZE_OPT(cumulative, false)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct distribution
+ {
+ uint64_t amount;
+ uint64_t start_height;
+ std::vector<uint64_t> distribution;
+ uint64_t base;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(amount)
+ KV_SERIALIZE(start_height)
+ KV_SERIALIZE_CONTAINER_POD_AS_BLOB(distribution)
+ KV_SERIALIZE(base)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ std::string status;
+ std::vector<distribution> distributions;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(status)
+ KV_SERIALIZE(distributions)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
}
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index cb5e7bc67..dea584471 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -2266,6 +2266,71 @@ bool wallet2::refresh(uint64_t & blocks_fetched, bool& received_money, bool& ok)
return ok;
}
//----------------------------------------------------------------------------------------------------
+bool wallet2::get_output_distribution(uint64_t &start_height, std::vector<uint64_t> &distribution)
+{
+ uint32_t rpc_version;
+ boost::optional<std::string> result = m_node_rpc_proxy.get_rpc_version(rpc_version);
+ // no error
+ if (!!result)
+ {
+ // empty string -> not connection
+ THROW_WALLET_EXCEPTION_IF(result->empty(), tools::error::no_connection_to_daemon, "getversion");
+ THROW_WALLET_EXCEPTION_IF(*result == CORE_RPC_STATUS_BUSY, tools::error::daemon_busy, "getversion");
+ if (*result != CORE_RPC_STATUS_OK)
+ {
+ MDEBUG("Cannot determine daemon RPC version, not requesting rct distribution");
+ return false;
+ }
+ }
+ else
+ {
+ if (rpc_version >= MAKE_CORE_RPC_VERSION(1, 19))
+ {
+ MDEBUG("Daemon is recent enough, requesting rct distribution");
+ }
+ else
+ {
+ MDEBUG("Daemon is too old, not requesting rct distribution");
+ return false;
+ }
+ }
+
+ cryptonote::COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request req = AUTO_VAL_INIT(req);
+ cryptonote::COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response res = AUTO_VAL_INIT(res);
+ req.amounts.push_back(0);
+ m_daemon_rpc_mutex.lock();
+ bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_distribution", req, res, m_http_client, rpc_timeout);
+ m_daemon_rpc_mutex.unlock();
+ if (!r)
+ {
+ MWARNING("Failed to request output distribution: no connection to daemon");
+ return false;
+ }
+ if (res.status == CORE_RPC_STATUS_BUSY)
+ {
+ MWARNING("Failed to request output distribution: daemon is busy");
+ return false;
+ }
+ if (res.status != CORE_RPC_STATUS_OK)
+ {
+ MWARNING("Failed to request output distribution: " << res.status);
+ return false;
+ }
+ if (res.distributions.size() != 1)
+ {
+ MWARNING("Failed to request output distribution: not the expected single result");
+ return false;
+ }
+ if (res.distributions[0].amount != 0)
+ {
+ MWARNING("Failed to request output distribution: results are not for amount 0");
+ return false;
+ }
+ start_height = res.distributions[0].start_height;
+ distribution = std::move(res.distributions[0].distribution);
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
void wallet2::detach_blockchain(uint64_t height)
{
LOG_PRINT_L0("Detaching blockchain on height " << height);
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index d2e484e05..2a883dce7 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -1103,6 +1103,8 @@ namespace tools
rct::key get_multisig_k(size_t idx, const std::unordered_set<rct::key> &used_L) const;
void update_multisig_rescan_info(const std::vector<std::vector<rct::key>> &multisig_k, const std::vector<std::vector<tools::wallet2::multisig_info>> &info, size_t n);
+ bool get_output_distribution(uint64_t &start_height, std::vector<uint64_t> &distribution);
+
cryptonote::account_base m_account;
boost::optional<epee::net_utils::http::login> m_daemon_login;
std::string m_daemon_address;
diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h
index 32a0231b1..7611d3d31 100644
--- a/src/wallet/wallet_errors.h
+++ b/src/wallet/wallet_errors.h
@@ -85,6 +85,7 @@ namespace tools
// no_connection_to_daemon
// is_key_image_spent_error
// get_histogram_error
+ // get_output_distribution
// wallet_files_doesnt_correspond
//
// * - class with protected ctor
@@ -757,6 +758,14 @@ namespace tools
}
};
//----------------------------------------------------------------------------------------------------
+ struct get_output_distribution : public wallet_rpc_error
+ {
+ explicit get_output_distribution(std::string&& loc, const std::string& request)
+ : wallet_rpc_error(std::move(loc), "failed to get output distribution", request)
+ {
+ }
+ };
+ //----------------------------------------------------------------------------------------------------
struct wallet_files_doesnt_correspond : public wallet_logic_error
{
explicit wallet_files_doesnt_correspond(std::string&& loc, const std::string& keys_file, const std::string& wallet_file)