aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/blockchain_db/berkeleydb/db_bdb.cpp4
-rw-r--r--src/blockchain_db/berkeleydb/db_bdb.h4
-rw-r--r--src/blockchain_db/blockchain_db.cpp13
-rw-r--r--src/blockchain_db/blockchain_db.h41
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp401
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.h26
-rw-r--r--src/blockchain_utilities/blockchain_usage.cpp15
-rw-r--r--src/blocks/checkpoints.datbin191524 -> 197348 bytes
-rw-r--r--src/checkpoints/checkpoints.cpp2
-rw-r--r--src/common/CMakeLists.txt1
-rw-r--r--src/common/util.cpp39
-rw-r--r--src/crypto/chacha.h4
-rw-r--r--src/crypto/crypto.cpp35
-rw-r--r--src/crypto/crypto.h10
-rw-r--r--src/crypto/slow-hash.c2
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.cpp101
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.h4
-rw-r--r--src/cryptonote_core/blockchain.cpp129
-rw-r--r--src/cryptonote_core/blockchain.h28
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp57
-rw-r--r--src/cryptonote_core/cryptonote_core.h11
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.cpp16
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.h4
-rw-r--r--src/cryptonote_core/tx_pool.cpp4
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.inl24
-rw-r--r--src/daemon/command_parser_executor.cpp15
-rw-r--r--src/daemon/command_parser_executor.h2
-rw-r--r--src/daemon/command_server.cpp5
-rw-r--r--src/daemon/main.cpp45
-rw-r--r--src/daemon/rpc_command_executor.cpp16
-rw-r--r--src/device/device_default.hpp2
-rw-r--r--src/device/device_ledger.hpp2
-rw-r--r--src/p2p/net_node.h4
-rw-r--r--src/p2p/net_node.inl13
-rw-r--r--src/ringct/rctSigs.cpp10
-rw-r--r--src/rpc/core_rpc_server.cpp92
-rw-r--r--src/rpc/core_rpc_server.h2
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h19
-rw-r--r--src/rpc/daemon_handler.cpp2
-rw-r--r--src/rpc/get_output_distribution_cache.h113
-rw-r--r--src/simplewallet/simplewallet.cpp86
-rw-r--r--src/simplewallet/simplewallet.h4
-rw-r--r--src/version.cpp.in2
-rw-r--r--src/wallet/api/pending_transaction.cpp53
-rw-r--r--src/wallet/api/pending_transaction.h5
-rw-r--r--src/wallet/api/wallet.cpp587
-rw-r--r--src/wallet/api/wallet.h15
-rw-r--r--src/wallet/api/wallet2_api.h98
-rw-r--r--src/wallet/api/wallet_manager.cpp44
-rw-r--r--src/wallet/api/wallet_manager.h16
-rw-r--r--src/wallet/ringdb.cpp18
-rw-r--r--src/wallet/ringdb.h1
-rw-r--r--src/wallet/wallet2.cpp96
-rw-r--r--src/wallet/wallet2.h29
54 files changed, 1712 insertions, 659 deletions
diff --git a/src/blockchain_db/berkeleydb/db_bdb.cpp b/src/blockchain_db/berkeleydb/db_bdb.cpp
index 3a66ecb93..e1b76ec1e 100644
--- a/src/blockchain_db/berkeleydb/db_bdb.cpp
+++ b/src/blockchain_db/berkeleydb/db_bdb.cpp
@@ -313,7 +313,7 @@ void BlockchainBDB::remove_block()
throw1(DB_ERROR("Failed to add removal of block hash to db transaction"));
}
-void BlockchainBDB::add_transaction_data(const crypto::hash& blk_hash, const transaction& tx, const crypto::hash& tx_hash)
+void BlockchainBDB::add_transaction_data(const crypto::hash& blk_hash, const transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prunable_hash)
{
LOG_PRINT_L3("BlockchainBDB::" << __func__);
check_open();
@@ -655,7 +655,7 @@ bool BlockchainBDB::for_all_blocks(std::function<bool(uint64_t, const crypto::ha
return ret;
}
-bool BlockchainBDB::for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)> f) const
+bool BlockchainBDB::for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)> f, bool pruned) const
{
LOG_PRINT_L3("BlockchainBDB::" << __func__);
check_open();
diff --git a/src/blockchain_db/berkeleydb/db_bdb.h b/src/blockchain_db/berkeleydb/db_bdb.h
index 238a90686..cecbba28f 100644
--- a/src/blockchain_db/berkeleydb/db_bdb.h
+++ b/src/blockchain_db/berkeleydb/db_bdb.h
@@ -360,7 +360,7 @@ private:
virtual void remove_block();
- virtual void add_transaction_data(const crypto::hash& blk_hash, const transaction& tx, const crypto::hash& tx_hash);
+ virtual void add_transaction_data(const crypto::hash& blk_hash, const transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prunable_hash);
virtual void remove_transaction_data(const crypto::hash& tx_hash, const transaction& tx);
@@ -381,7 +381,7 @@ private:
virtual bool for_all_key_images(std::function<bool(const crypto::key_image&)>) const;
virtual bool for_all_blocks(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_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)>, bool pruned) const;
virtual bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, size_t tx_idx)> f) const;
// Hard fork related storage
diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp
index 9f760dc0d..88ac34255 100644
--- a/src/blockchain_db/blockchain_db.cpp
+++ b/src/blockchain_db/blockchain_db.cpp
@@ -121,10 +121,10 @@ void BlockchainDB::pop_block()
pop_block(blk, txs);
}
-void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transaction& tx, const crypto::hash* tx_hash_ptr)
+void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transaction& tx, const crypto::hash* tx_hash_ptr, const crypto::hash* tx_prunable_hash_ptr)
{
bool miner_tx = false;
- crypto::hash tx_hash;
+ crypto::hash tx_hash, tx_prunable_hash;
if (!tx_hash_ptr)
{
// should only need to compute hash for miner transactions
@@ -135,6 +135,13 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transacti
{
tx_hash = *tx_hash_ptr;
}
+ if (tx.version >= 2)
+ {
+ if (!tx_prunable_hash_ptr)
+ tx_prunable_hash = get_transaction_prunable_hash(tx);
+ else
+ tx_prunable_hash = *tx_prunable_hash_ptr;
+ }
for (const txin_v& tx_input : tx.vin)
{
@@ -161,7 +168,7 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transacti
}
}
- uint64_t tx_id = add_transaction_data(blk_hash, tx, tx_hash);
+ uint64_t tx_id = add_transaction_data(blk_hash, tx, tx_hash, tx_prunable_hash);
std::vector<uint64_t> amount_output_indices;
diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h
index d7230f644..19ba32340 100644
--- a/src/blockchain_db/blockchain_db.h
+++ b/src/blockchain_db/blockchain_db.h
@@ -398,9 +398,10 @@ private:
* @param blk_hash the hash of the block containing the transaction
* @param tx the transaction to be added
* @param tx_hash the hash of the transaction
+ * @param tx_prunable_hash the hash of the prunable part of the transaction
* @return the transaction ID
*/
- virtual uint64_t add_transaction_data(const crypto::hash& blk_hash, const transaction& tx, const crypto::hash& tx_hash) = 0;
+ virtual uint64_t add_transaction_data(const crypto::hash& blk_hash, const transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prunable_hash) = 0;
/**
* @brief remove data about a transaction
@@ -526,8 +527,9 @@ protected:
* @param blk_hash hash of the block which has the transaction
* @param tx the transaction to add
* @param tx_hash_ptr the hash of the transaction, if already calculated
+ * @param tx_prunable_hash_ptr the hash of the prunable part of the transaction, if already calculated
*/
- void add_transaction(const crypto::hash& blk_hash, const transaction& tx, const crypto::hash* tx_hash_ptr = NULL);
+ void add_transaction(const crypto::hash& blk_hash, const transaction& tx, const crypto::hash* tx_hash_ptr = NULL, const crypto::hash* tx_prunable_hash_ptr = NULL);
mutable uint64_t time_tx_exists = 0; //!< a performance metric
uint64_t time_commit1 = 0; //!< a performance metric
@@ -1120,6 +1122,33 @@ public:
virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const = 0;
/**
+ * @brief fetches the pruned transaction blob with the given hash
+ *
+ * The subclass should return the pruned transaction stored which has the given
+ * hash.
+ *
+ * If the transaction does not exist, the subclass should return false.
+ *
+ * @param h the hash to look for
+ *
+ * @return true iff the transaction was found
+ */
+ virtual bool get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const = 0;
+
+ /**
+ * @brief fetches the prunable transaction hash
+ *
+ * The subclass should return the hash of the prunable transaction data.
+ *
+ * If the transaction hash does not exist, the subclass should return false.
+ *
+ * @param h the tx hash to look for
+ *
+ * @return true iff the transaction was found
+ */
+ virtual bool get_prunable_tx_hash(const crypto::hash& tx_hash, crypto::hash &prunable_hash) const = 0;
+
+ /**
* @brief fetches the total number of transactions ever
*
* The subclass should return a count of all the transactions from
@@ -1426,10 +1455,11 @@ public:
* not found. Current implementations simply return false.
*
* @param std::function fn the function to run
+ * @param bool pruned whether to only get pruned tx data, or the whole
*
* @return false if the function returns false for any transaction, otherwise true
*/
- virtual bool for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)>) const = 0;
+ virtual bool for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)>, bool pruned) const = 0;
/**
* @brief runs a function over all outputs stored
@@ -1490,10 +1520,13 @@ public:
* @param amounts optional set of amounts to lookup
* @param unlocked whether to restrict count to unlocked outputs
* @param recent_cutoff timestamp to determine whether an output is recent
+ * @param min_count return only amounts with at least that many instances
*
* @return a set of amount/instances
*/
- virtual 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) const = 0;
+ virtual 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) const = 0;
+
+ virtual bool get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t to_height, std::vector<uint64_t> &distribution, uint64_t &base) const = 0;
/**
* @brief is BlockchainDB in read-only mode?
diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp
index 9a755fb0c..300fb6d2f 100644
--- a/src/blockchain_db/lmdb/db_lmdb.cpp
+++ b/src/blockchain_db/lmdb/db_lmdb.cpp
@@ -52,9 +52,8 @@
using epee::string_tools::pod_to_hex;
using namespace crypto;
-// Increase when the DB changes in a non backward compatible way, and there
-// is no automatic conversion, so that a full resync is needed.
-#define VERSION 1
+// Increase when the DB structure changes
+#define VERSION 2
namespace
{
@@ -164,7 +163,9 @@ int compare_string(const MDB_val *a, const MDB_val *b)
* block_heights block hash block height
* block_info block ID {block metadata}
*
- * txs txn ID txn blob
+ * txs_pruned txn ID pruned txn blob
+ * txs_prunable txn ID prunable txn blob
+ * txs_prunable_hash txn ID prunable txn hash
* tx_indices txn hash {txn ID, metadata}
* tx_outputs txn ID [txn amount output indices]
*
@@ -189,6 +190,9 @@ const char* const LMDB_BLOCK_HEIGHTS = "block_heights";
const char* const LMDB_BLOCK_INFO = "block_info";
const char* const LMDB_TXS = "txs";
+const char* const LMDB_TXS_PRUNED = "txs_pruned";
+const char* const LMDB_TXS_PRUNABLE = "txs_prunable";
+const char* const LMDB_TXS_PRUNABLE_HASH = "txs_prunable_hash";
const char* const LMDB_TX_INDICES = "tx_indices";
const char* const LMDB_TX_OUTPUTS = "tx_outputs";
@@ -764,7 +768,7 @@ void BlockchainLMDB::remove_block()
throw1(DB_ERROR(lmdb_error("Failed to add removal of block info to db transaction: ", result).c_str()));
}
-uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, const transaction& tx, const crypto::hash& tx_hash)
+uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, const transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prunable_hash)
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
@@ -774,7 +778,9 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
int result;
uint64_t tx_id = get_tx_count();
- CURSOR(txs)
+ CURSOR(txs_pruned)
+ CURSOR(txs_prunable)
+ CURSOR(txs_prunable_hash)
CURSOR(tx_indices)
MDB_val_set(val_tx_id, tx_id);
@@ -800,10 +806,35 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
if (result)
throw0(DB_ERROR(lmdb_error("Failed to add tx data to db transaction: ", result).c_str()));
- MDB_val_copy<blobdata> blob(tx_to_blob(tx));
- result = mdb_cursor_put(m_cur_txs, &val_tx_id, &blob, MDB_APPEND);
+ cryptonote::blobdata blob = tx_to_blob(tx);
+ MDB_val_copy<blobdata> blobval(blob);
+
+ std::stringstream ss;
+ binary_archive<true> ba(ss);
+ bool r = const_cast<cryptonote::transaction&>(tx).serialize_base(ba);
+ if (!r)
+ throw0(DB_ERROR("Failed to serialize pruned tx"));
+ std::string pruned = ss.str();
+ MDB_val_copy<blobdata> pruned_blob(pruned);
+ result = mdb_cursor_put(m_cur_txs_pruned, &val_tx_id, &pruned_blob, MDB_APPEND);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to add pruned tx blob to db transaction: ", result).c_str()));
+
+ if (pruned.size() > blob.size())
+ throw0(DB_ERROR("pruned tx size is larger than tx size"));
+ cryptonote::blobdata prunable(blob.data() + pruned.size(), blob.size() - pruned.size());
+ MDB_val_copy<blobdata> prunable_blob(prunable);
+ result = mdb_cursor_put(m_cur_txs_prunable, &val_tx_id, &prunable_blob, MDB_APPEND);
if (result)
- throw0(DB_ERROR(lmdb_error("Failed to add tx blob to db transaction: ", result).c_str()));
+ throw0(DB_ERROR(lmdb_error("Failed to add prunable tx blob to db transaction: ", result).c_str()));
+
+ if (tx.version > 1)
+ {
+ MDB_val_set(val_prunable_hash, tx_prunable_hash);
+ result = mdb_cursor_put(m_cur_txs_prunable_hash, &val_tx_id, &val_prunable_hash, MDB_APPEND);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to add prunable tx prunable hash to db transaction: ", result).c_str()));
+ }
return tx_id;
}
@@ -819,7 +850,9 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const
mdb_txn_cursors *m_cursors = &m_wcursors;
CURSOR(tx_indices)
- CURSOR(txs)
+ CURSOR(txs_pruned)
+ CURSOR(txs_prunable)
+ CURSOR(txs_prunable_hash)
CURSOR(tx_outputs)
MDB_val_set(val_h, tx_hash);
@@ -829,11 +862,26 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const
txindex *tip = (txindex *)val_h.mv_data;
MDB_val_set(val_tx_id, tip->data.tx_id);
- if ((result = mdb_cursor_get(m_cur_txs, &val_tx_id, NULL, MDB_SET)))
- throw1(DB_ERROR(lmdb_error("Failed to locate tx for removal: ", result).c_str()));
- result = mdb_cursor_del(m_cur_txs, 0);
+ if ((result = mdb_cursor_get(m_cur_txs_pruned, &val_tx_id, NULL, MDB_SET)))
+ throw1(DB_ERROR(lmdb_error("Failed to locate pruned tx for removal: ", result).c_str()));
+ result = mdb_cursor_del(m_cur_txs_pruned, 0);
+ if (result)
+ throw1(DB_ERROR(lmdb_error("Failed to add removal of pruned tx to db transaction: ", result).c_str()));
+
+ if ((result = mdb_cursor_get(m_cur_txs_prunable, &val_tx_id, NULL, MDB_SET)))
+ throw1(DB_ERROR(lmdb_error("Failed to locate prunable tx for removal: ", result).c_str()));
+ result = mdb_cursor_del(m_cur_txs_prunable, 0);
if (result)
- throw1(DB_ERROR(lmdb_error("Failed to add removal of tx to db transaction: ", result).c_str()));
+ throw1(DB_ERROR(lmdb_error("Failed to add removal of prunable tx to db transaction: ", result).c_str()));
+
+ if (tx.version > 1)
+ {
+ if ((result = mdb_cursor_get(m_cur_txs_prunable_hash, &val_tx_id, NULL, MDB_SET)))
+ throw1(DB_ERROR(lmdb_error("Failed to locate prunable hash tx for removal: ", result).c_str()));
+ result = mdb_cursor_del(m_cur_txs_prunable_hash, 0);
+ if (result)
+ throw1(DB_ERROR(lmdb_error("Failed to add removal of prunable hash tx to db transaction: ", result).c_str()));
+ }
remove_tx_outputs(tip->data.tx_id, tx);
@@ -1199,6 +1247,9 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
lmdb_db_open(txn, LMDB_BLOCK_HEIGHTS, MDB_INTEGERKEY | MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED, m_block_heights, "Failed to open db handle for m_block_heights");
lmdb_db_open(txn, LMDB_TXS, MDB_INTEGERKEY | MDB_CREATE, m_txs, "Failed to open db handle for m_txs");
+ lmdb_db_open(txn, LMDB_TXS_PRUNED, MDB_INTEGERKEY | MDB_CREATE, m_txs_pruned, "Failed to open db handle for m_txs_pruned");
+ lmdb_db_open(txn, LMDB_TXS_PRUNABLE, MDB_INTEGERKEY | MDB_CREATE, m_txs_prunable, "Failed to open db handle for m_txs_prunable");
+ lmdb_db_open(txn, LMDB_TXS_PRUNABLE_HASH, MDB_INTEGERKEY | MDB_CREATE, m_txs_prunable_hash, "Failed to open db handle for m_txs_prunable_hash");
lmdb_db_open(txn, LMDB_TX_INDICES, MDB_INTEGERKEY | MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED, m_tx_indices, "Failed to open db handle for m_tx_indices");
lmdb_db_open(txn, LMDB_TX_OUTPUTS, MDB_INTEGERKEY | MDB_CREATE, m_tx_outputs, "Failed to open db handle for m_tx_outputs");
@@ -1346,6 +1397,7 @@ void BlockchainLMDB::sync()
void BlockchainLMDB::safesyncmode(const bool onoff)
{
+ MINFO("switching safe mode " << (onoff ? "on" : "off"));
mdb_env_set_flags(m_env, MDB_NOSYNC|MDB_MAPASYNC, !onoff);
}
@@ -1364,8 +1416,12 @@ void BlockchainLMDB::reset()
throw0(DB_ERROR(lmdb_error("Failed to drop m_block_info: ", result).c_str()));
if (auto result = mdb_drop(txn, m_block_heights, 0))
throw0(DB_ERROR(lmdb_error("Failed to drop m_block_heights: ", result).c_str()));
- if (auto result = mdb_drop(txn, m_txs, 0))
- throw0(DB_ERROR(lmdb_error("Failed to drop m_txs: ", result).c_str()));
+ if (auto result = mdb_drop(txn, m_txs_pruned, 0))
+ throw0(DB_ERROR(lmdb_error("Failed to drop m_txs_pruned: ", result).c_str()));
+ if (auto result = mdb_drop(txn, m_txs_prunable, 0))
+ throw0(DB_ERROR(lmdb_error("Failed to drop m_txs_prunable: ", result).c_str()));
+ if (auto result = mdb_drop(txn, m_txs_prunable_hash, 0))
+ throw0(DB_ERROR(lmdb_error("Failed to drop m_txs_prunable_hash: ", result).c_str()));
if (auto result = mdb_drop(txn, m_tx_indices, 0))
throw0(DB_ERROR(lmdb_error("Failed to drop m_tx_indices: ", result).c_str()));
if (auto result = mdb_drop(txn, m_tx_outputs, 0))
@@ -2063,7 +2119,6 @@ bool BlockchainLMDB::tx_exists(const crypto::hash& h) const
TXN_PREFIX_RDONLY();
RCURSOR(tx_indices);
- RCURSOR(txs);
MDB_val_set(key, h);
bool tx_found = false;
@@ -2075,8 +2130,6 @@ bool BlockchainLMDB::tx_exists(const crypto::hash& h) const
else if (get_result != MDB_NOTFOUND)
throw0(DB_ERROR(lmdb_error(std::string("DB error attempting to fetch transaction index from hash ") + epee::string_tools::pod_to_hex(h) + ": ", get_result).c_str()));
- // This isn't needed as part of the check. we're not checking consistency of db.
- // get_result = mdb_cursor_get(m_cur_txs, &val_tx_index, &result, MDB_SET);
TIME_MEASURE_FINISH(time1);
time_tx_exists += time1;
@@ -2088,11 +2141,6 @@ bool BlockchainLMDB::tx_exists(const crypto::hash& h) const
return false;
}
- // Below not needed due to above comment.
- // if (get_result == MDB_NOTFOUND)
- // throw0(DB_ERROR(std::string("transaction with hash ").append(epee::string_tools::pod_to_hex(h)).append(" not found at index").c_str()));
- // else if (get_result)
- // throw0(DB_ERROR(lmdb_error(std::string("DB error attempting to fetch transaction ") + epee::string_tools::pod_to_hex(h) + " at index: ", get_result).c_str()));
return true;
}
@@ -2158,7 +2206,43 @@ bool BlockchainLMDB::get_tx_blob(const crypto::hash& h, cryptonote::blobdata &bd
TXN_PREFIX_RDONLY();
RCURSOR(tx_indices);
- RCURSOR(txs);
+ RCURSOR(txs_pruned);
+ RCURSOR(txs_prunable);
+
+ MDB_val_set(v, h);
+ MDB_val result0, result1;
+ auto get_result = mdb_cursor_get(m_cur_tx_indices, (MDB_val *)&zerokval, &v, MDB_GET_BOTH);
+ if (get_result == 0)
+ {
+ txindex *tip = (txindex *)v.mv_data;
+ MDB_val_set(val_tx_id, tip->data.tx_id);
+ get_result = mdb_cursor_get(m_cur_txs_pruned, &val_tx_id, &result0, MDB_SET);
+ if (get_result == 0)
+ {
+ get_result = mdb_cursor_get(m_cur_txs_prunable, &val_tx_id, &result1, MDB_SET);
+ }
+ }
+ if (get_result == MDB_NOTFOUND)
+ return false;
+ else if (get_result)
+ throw0(DB_ERROR(lmdb_error("DB error attempting to fetch tx from hash", get_result).c_str()));
+
+ bd.assign(reinterpret_cast<char*>(result0.mv_data), result0.mv_size);
+ bd.append(reinterpret_cast<char*>(result1.mv_data), result1.mv_size);
+
+ TXN_POSTFIX_RDONLY();
+
+ return true;
+}
+
+bool BlockchainLMDB::get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobdata &bd) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ TXN_PREFIX_RDONLY();
+ RCURSOR(tx_indices);
+ RCURSOR(txs_pruned);
MDB_val_set(v, h);
MDB_val result;
@@ -2167,7 +2251,7 @@ bool BlockchainLMDB::get_tx_blob(const crypto::hash& h, cryptonote::blobdata &bd
{
txindex *tip = (txindex *)v.mv_data;
MDB_val_set(val_tx_id, tip->data.tx_id);
- get_result = mdb_cursor_get(m_cur_txs, &val_tx_id, &result, MDB_SET);
+ get_result = mdb_cursor_get(m_cur_txs_pruned, &val_tx_id, &result, MDB_SET);
}
if (get_result == MDB_NOTFOUND)
return false;
@@ -2181,6 +2265,36 @@ bool BlockchainLMDB::get_tx_blob(const crypto::hash& h, cryptonote::blobdata &bd
return true;
}
+bool BlockchainLMDB::get_prunable_tx_hash(const crypto::hash& tx_hash, crypto::hash &prunable_hash) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ TXN_PREFIX_RDONLY();
+ RCURSOR(tx_indices);
+ RCURSOR(txs_prunable_hash);
+
+ MDB_val_set(v, tx_hash);
+ MDB_val result, val_tx_prunable_hash;
+ auto get_result = mdb_cursor_get(m_cur_tx_indices, (MDB_val *)&zerokval, &v, MDB_GET_BOTH);
+ if (get_result == 0)
+ {
+ txindex *tip = (txindex *)v.mv_data;
+ MDB_val_set(val_tx_id, tip->data.tx_id);
+ get_result = mdb_cursor_get(m_cur_txs_prunable_hash, &val_tx_id, &result, MDB_SET);
+ }
+ if (get_result == MDB_NOTFOUND)
+ return false;
+ else if (get_result)
+ throw0(DB_ERROR(lmdb_error("DB error attempting to fetch tx prunable hash from tx hash", get_result).c_str()));
+
+ prunable_hash = *(const crypto::hash*)result.mv_data;
+
+ TXN_POSTFIX_RDONLY();
+
+ return true;
+}
+
uint64_t BlockchainLMDB::get_tx_count() const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
@@ -2190,8 +2304,8 @@ uint64_t BlockchainLMDB::get_tx_count() const
int result;
MDB_stat db_stats;
- if ((result = mdb_stat(m_txn, m_txs, &db_stats)))
- throw0(DB_ERROR(lmdb_error("Failed to query m_txs: ", result).c_str()));
+ if ((result = mdb_stat(m_txn, m_txs_pruned, &db_stats)))
+ throw0(DB_ERROR(lmdb_error("Failed to query m_txs_pruned: ", result).c_str()));
TXN_POSTFIX_RDONLY();
@@ -2267,7 +2381,6 @@ output_data_t BlockchainLMDB::get_output_key(const uint64_t &global_index) const
TXN_PREFIX_RDONLY();
RCURSOR(output_txs);
RCURSOR(tx_indices);
- RCURSOR(txs);
output_data_t od;
MDB_val_set(v, global_index);
@@ -2287,7 +2400,7 @@ output_data_t BlockchainLMDB::get_output_key(const uint64_t &global_index) const
txindex *tip = (txindex *)val_h.mv_data;
MDB_val_set(val_tx_id, tip->data.tx_id);
MDB_val result;
- get_result = mdb_cursor_get(m_cur_txs, &val_tx_id, &result, MDB_SET);
+ get_result = mdb_cursor_get(m_cur_txs_pruned, &val_tx_id, &result, MDB_SET);
if (get_result == MDB_NOTFOUND)
throw1(TX_DNE(std::string("tx with hash ").append(epee::string_tools::pod_to_hex(ot->tx_hash)).append(" not found in db").c_str()));
else if (get_result)
@@ -2297,7 +2410,7 @@ output_data_t BlockchainLMDB::get_output_key(const uint64_t &global_index) const
bd.assign(reinterpret_cast<char*>(result.mv_data), result.mv_size);
transaction tx;
- if (!parse_and_validate_tx_from_blob(bd, tx))
+ if (!parse_and_validate_tx_base_from_blob(bd, tx))
throw0(DB_ERROR("Failed to parse tx from blob retrieved from the db"));
const tx_out tx_output = tx.vout[ot->local_index];
@@ -2516,13 +2629,14 @@ bool BlockchainLMDB::for_blocks_range(const uint64_t& h1, const uint64_t& h2, st
return fret;
}
-bool BlockchainLMDB::for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)> f) const
+bool BlockchainLMDB::for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)> f, bool pruned) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
TXN_PREFIX_RDONLY();
- RCURSOR(txs);
+ RCURSOR(txs_pruned);
+ RCURSOR(txs_prunable);
RCURSOR(tx_indices);
MDB_val k;
@@ -2543,16 +2657,29 @@ bool BlockchainLMDB::for_all_transactions(std::function<bool(const crypto::hash&
const crypto::hash hash = ti->key;
k.mv_data = (void *)&ti->data.tx_id;
k.mv_size = sizeof(ti->data.tx_id);
- ret = mdb_cursor_get(m_cur_txs, &k, &v, MDB_SET);
+
+ ret = mdb_cursor_get(m_cur_txs_pruned, &k, &v, MDB_SET);
if (ret == MDB_NOTFOUND)
break;
if (ret)
throw0(DB_ERROR(lmdb_error("Failed to enumerate transactions: ", ret).c_str()));
+ transaction tx;
blobdata bd;
bd.assign(reinterpret_cast<char*>(v.mv_data), v.mv_size);
- transaction tx;
- if (!parse_and_validate_tx_from_blob(bd, tx))
- throw0(DB_ERROR("Failed to parse tx from blob retrieved from the db"));
+ if (pruned)
+ {
+ if (!parse_and_validate_tx_base_from_blob(bd, tx))
+ throw0(DB_ERROR("Failed to parse tx from blob retrieved from the db"));
+ }
+ else
+ {
+ ret = mdb_cursor_get(m_cur_txs_prunable, &k, &v, MDB_SET);
+ if (ret)
+ throw0(DB_ERROR(lmdb_error("Failed to get prunable tx data the db: ", ret).c_str()));
+ bd.append(reinterpret_cast<char*>(v.mv_data), v.mv_size);
+ if (!parse_and_validate_tx_from_blob(bd, tx))
+ throw0(DB_ERROR("Failed to parse tx from blob retrieved from the db"));
+ }
if (!f(hash, tx)) {
fret = false;
break;
@@ -3086,7 +3213,7 @@ void BlockchainLMDB::get_output_tx_and_index(const uint64_t& amount, const std::
LOG_PRINT_L3("db3: " << db3);
}
-std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> BlockchainLMDB::get_output_histogram(const std::vector<uint64_t> &amounts, bool unlocked, uint64_t recent_cutoff) const
+std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> BlockchainLMDB::get_output_histogram(const std::vector<uint64_t> &amounts, bool unlocked, uint64_t recent_cutoff, uint64_t min_count) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
@@ -3112,7 +3239,8 @@ std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> BlockchainLMDB::get
mdb_size_t num_elems = 0;
mdb_cursor_count(m_cur_output_amounts, &num_elems);
uint64_t amount = *(const uint64_t*)k.mv_data;
- histogram[amount] = std::make_tuple(num_elems, 0, 0);
+ if (num_elems >= min_count)
+ histogram[amount] = std::make_tuple(num_elems, 0, 0);
}
}
else
@@ -3123,13 +3251,15 @@ std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> BlockchainLMDB::get
int ret = mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_SET);
if (ret == MDB_NOTFOUND)
{
- histogram[amount] = std::make_tuple(0, 0, 0);
+ if (0 >= min_count)
+ histogram[amount] = std::make_tuple(0, 0, 0);
}
else if (ret == MDB_SUCCESS)
{
mdb_size_t num_elems = 0;
mdb_cursor_count(m_cur_output_amounts, &num_elems);
- histogram[amount] = std::make_tuple(num_elems, 0, 0);
+ if (num_elems >= min_count)
+ histogram[amount] = std::make_tuple(num_elems, 0, 0);
}
else
{
@@ -3176,6 +3306,47 @@ std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> BlockchainLMDB::get
return histogram;
}
+bool BlockchainLMDB::get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t to_height, std::vector<uint64_t> &distribution, uint64_t &base) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ TXN_PREFIX_RDONLY();
+ RCURSOR(output_amounts);
+
+ distribution.clear();
+ const uint64_t db_height = height();
+ if (from_height >= db_height)
+ return false;
+ distribution.resize(db_height - from_height, 0);
+
+ bool fret = true;
+ MDB_val_set(k, amount);
+ MDB_val v;
+ 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"));
+ const outkey *ok = (const outkey *)v.mv_data;
+ const uint64_t height = ok->data.height;
+ if (height >= from_height)
+ distribution[height - from_height]++;
+ else
+ base++;
+ if (to_height > 0 && height > to_height)
+ break;
+ }
+
+ TXN_POSTFIX_RDONLY();
+
+ return true;
+}
+
void BlockchainLMDB::check_hard_fork_info()
{
}
@@ -3267,7 +3438,7 @@ void BlockchainLMDB::fixup()
ptr = (char *)k.mv_data; \
ptr[sizeof(name)-2] = 's'
-#define LOGIF(y) if (ELPP->vRegistry()->allowed(y, MONERO_DEFAULT_LOG_CATEGORY))
+#define LOGIF(y) if (ELPP->vRegistry()->allowed(y, "global"))
void BlockchainLMDB::migrate_0_1()
{
@@ -3278,7 +3449,7 @@ void BlockchainLMDB::migrate_0_1()
MDB_val k, v;
char *ptr;
- MLOG_YELLOW(el::Level::Info, "Migrating blockchain from DB version 0 to 1 - this may take a while:");
+ MGINFO_YELLOW("Migrating blockchain from DB version 0 to 1 - this may take a while:");
MINFO("updating blocks, hf_versions, outputs, txs, and spent_keys tables...");
do {
@@ -3803,11 +3974,155 @@ void BlockchainLMDB::migrate_0_1()
txn.commit();
}
+void BlockchainLMDB::migrate_1_2()
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ uint64_t i, z;
+ int result;
+ mdb_txn_safe txn(false);
+ MDB_val k, v;
+ char *ptr;
+
+ MGINFO_YELLOW("Migrating blockchain from DB version 1 to 2 - this may take a while:");
+ MINFO("updating txs_pruned and txs_prunable tables...");
+
+ do {
+ result = mdb_txn_begin(m_env, NULL, 0, txn);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str()));
+
+ MDB_stat db_stats_txs;
+ MDB_stat db_stats_txs_pruned;
+ MDB_stat db_stats_txs_prunable;
+ MDB_stat db_stats_txs_prunable_hash;
+ if ((result = mdb_stat(txn, m_txs, &db_stats_txs)))
+ throw0(DB_ERROR(lmdb_error("Failed to query m_txs: ", result).c_str()));
+ if ((result = mdb_stat(txn, m_txs_pruned, &db_stats_txs_pruned)))
+ throw0(DB_ERROR(lmdb_error("Failed to query m_txs_pruned: ", result).c_str()));
+ if ((result = mdb_stat(txn, m_txs_prunable, &db_stats_txs_prunable)))
+ throw0(DB_ERROR(lmdb_error("Failed to query m_txs_prunable: ", result).c_str()));
+ if ((result = mdb_stat(txn, m_txs_prunable_hash, &db_stats_txs_prunable_hash)))
+ throw0(DB_ERROR(lmdb_error("Failed to query m_txs_prunable_hash: ", result).c_str()));
+ if (db_stats_txs_pruned.ms_entries != db_stats_txs_prunable.ms_entries)
+ throw0(DB_ERROR("Mismatched sizes for txs_pruned and txs_prunable"));
+ if (db_stats_txs_pruned.ms_entries == db_stats_txs.ms_entries)
+ {
+ txn.commit();
+ MINFO("txs already migrated");
+ break;
+ }
+
+ MINFO("updating txs tables:");
+
+ MDB_cursor *c_old, *c_cur0, *c_cur1, *c_cur2;
+ i = 0;
+
+ while(1) {
+ if (!(i % 1000)) {
+ if (i) {
+ result = mdb_stat(txn, m_txs, &db_stats_txs);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to query m_txs: ", result).c_str()));
+ LOGIF(el::Level::Info) {
+ std::cout << i << " / " << (i + db_stats_txs.ms_entries) << " \r" << std::flush;
+ }
+ txn.commit();
+ result = mdb_txn_begin(m_env, NULL, 0, txn);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str()));
+ }
+ result = mdb_cursor_open(txn, m_txs_pruned, &c_cur0);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to open a cursor for txs_pruned: ", result).c_str()));
+ result = mdb_cursor_open(txn, m_txs_prunable, &c_cur1);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to open a cursor for txs_prunable: ", result).c_str()));
+ result = mdb_cursor_open(txn, m_txs_prunable_hash, &c_cur2);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to open a cursor for txs_prunable_hash: ", result).c_str()));
+ result = mdb_cursor_open(txn, m_txs, &c_old);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to open a cursor for txs: ", result).c_str()));
+ if (!i) {
+ i = db_stats_txs_pruned.ms_entries;
+ }
+ }
+ MDB_val_set(k, i);
+ result = mdb_cursor_get(c_old, &k, &v, MDB_SET);
+ if (result == MDB_NOTFOUND) {
+ txn.commit();
+ break;
+ }
+ else if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to get a record from txs: ", result).c_str()));
+
+ cryptonote::blobdata bd;
+ bd.assign(reinterpret_cast<char*>(v.mv_data), v.mv_size);
+ transaction tx;
+ if (!parse_and_validate_tx_from_blob(bd, tx))
+ throw0(DB_ERROR("Failed to parse tx from blob retrieved from the db"));
+ std::stringstream ss;
+ binary_archive<true> ba(ss);
+ bool r = tx.serialize_base(ba);
+ if (!r)
+ throw0(DB_ERROR("Failed to serialize pruned tx"));
+ std::string pruned = ss.str();
+
+ if (pruned.size() > bd.size())
+ throw0(DB_ERROR("Pruned tx is larger than raw tx"));
+ if (memcmp(pruned.data(), bd.data(), pruned.size()))
+ throw0(DB_ERROR("Pruned tx is not a prefix of the raw tx"));
+
+ MDB_val nv;
+ nv.mv_data = (void*)pruned.data();
+ nv.mv_size = pruned.size();
+ result = mdb_cursor_put(c_cur0, (MDB_val *)&k, &nv, 0);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to put a record into txs_pruned: ", result).c_str()));
+
+ nv.mv_data = (void*)(bd.data() + pruned.size());
+ nv.mv_size = bd.size() - pruned.size();
+ result = mdb_cursor_put(c_cur1, (MDB_val *)&k, &nv, 0);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to put a record into txs_prunable: ", result).c_str()));
+
+ if (tx.version > 1)
+ {
+ crypto::hash prunable_hash = get_transaction_prunable_hash(tx);
+ MDB_val_set(val_prunable_hash, prunable_hash);
+ result = mdb_cursor_put(c_cur2, (MDB_val *)&k, &val_prunable_hash, 0);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to put a record into txs_prunable_hash: ", result).c_str()));
+ }
+
+ result = mdb_cursor_del(c_old, 0);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to delete a record from txs: ", result).c_str()));
+
+ i++;
+ }
+ } while(0);
+
+ uint32_t version = 2;
+ v.mv_data = (void *)&version;
+ v.mv_size = sizeof(version);
+ MDB_val_copy<const char *> vk("version");
+ result = mdb_txn_begin(m_env, NULL, 0, txn);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str()));
+ result = mdb_put(txn, m_properties, &vk, &v, 0);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to update version for the db: ", result).c_str()));
+ txn.commit();
+}
+
void BlockchainLMDB::migrate(const uint32_t oldversion)
{
switch(oldversion) {
case 0:
migrate_0_1(); /* FALLTHRU */
+ case 1:
+ migrate_1_2(); /* FALLTHRU */
default:
;
}
diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h
index 0aa4bb86e..cc1b06ca0 100644
--- a/src/blockchain_db/lmdb/db_lmdb.h
+++ b/src/blockchain_db/lmdb/db_lmdb.h
@@ -50,6 +50,9 @@ typedef struct mdb_txn_cursors
MDB_cursor *m_txc_output_amounts;
MDB_cursor *m_txc_txs;
+ MDB_cursor *m_txc_txs_pruned;
+ MDB_cursor *m_txc_txs_prunable;
+ MDB_cursor *m_txc_txs_prunable_hash;
MDB_cursor *m_txc_tx_indices;
MDB_cursor *m_txc_tx_outputs;
@@ -67,6 +70,9 @@ typedef struct mdb_txn_cursors
#define m_cur_output_txs m_cursors->m_txc_output_txs
#define m_cur_output_amounts m_cursors->m_txc_output_amounts
#define m_cur_txs m_cursors->m_txc_txs
+#define m_cur_txs_pruned m_cursors->m_txc_txs_pruned
+#define m_cur_txs_prunable m_cursors->m_txc_txs_prunable
+#define m_cur_txs_prunable_hash m_cursors->m_txc_txs_prunable_hash
#define m_cur_tx_indices m_cursors->m_txc_tx_indices
#define m_cur_tx_outputs m_cursors->m_txc_tx_outputs
#define m_cur_spent_keys m_cursors->m_txc_spent_keys
@@ -83,6 +89,9 @@ typedef struct mdb_rflags
bool m_rf_output_txs;
bool m_rf_output_amounts;
bool m_rf_txs;
+ bool m_rf_txs_pruned;
+ bool m_rf_txs_prunable;
+ bool m_rf_txs_prunable_hash;
bool m_rf_tx_indices;
bool m_rf_tx_outputs;
bool m_rf_spent_keys;
@@ -218,6 +227,8 @@ public:
virtual uint64_t get_tx_unlock_time(const crypto::hash& h) const;
virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const;
+ virtual bool get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const;
+ virtual bool get_prunable_tx_hash(const crypto::hash& tx_hash, crypto::hash &prunable_hash) const;
virtual uint64_t get_tx_count() const;
@@ -254,7 +265,7 @@ 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_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)>, bool pruned) 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;
@@ -287,10 +298,13 @@ public:
* @param amounts optional set of amounts to lookup
* @param unlocked whether to restrict count to unlocked outputs
* @param recent_cutoff timestamp to determine which outputs are recent
+ * @param min_count return only amounts with at least that many instances
*
* @return a set of amount/instances
*/
- 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) const;
+ 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) const;
+
+ bool get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t to_height, std::vector<uint64_t> &distribution, uint64_t &base) const;
private:
void do_resize(uint64_t size_increase=0);
@@ -308,7 +322,7 @@ private:
virtual void remove_block();
- virtual uint64_t add_transaction_data(const crypto::hash& blk_hash, const transaction& tx, const crypto::hash& tx_hash);
+ virtual uint64_t add_transaction_data(const crypto::hash& blk_hash, const transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prunable_hash);
virtual void remove_transaction_data(const crypto::hash& tx_hash, const transaction& tx);
@@ -370,6 +384,9 @@ private:
// migrate from DB version 0 to 1
void migrate_0_1();
+ // migrate from DB version 1 to 2
+ void migrate_1_2();
+
void cleanup_batch();
private:
@@ -380,6 +397,9 @@ private:
MDB_dbi m_block_info;
MDB_dbi m_txs;
+ MDB_dbi m_txs_pruned;
+ MDB_dbi m_txs_prunable;
+ MDB_dbi m_txs_prunable_hash;
MDB_dbi m_tx_indices;
MDB_dbi m_tx_outputs;
diff --git a/src/blockchain_utilities/blockchain_usage.cpp b/src/blockchain_utilities/blockchain_usage.cpp
index 7c3c83167..38a0b2648 100644
--- a/src/blockchain_utilities/blockchain_usage.cpp
+++ b/src/blockchain_utilities/blockchain_usage.cpp
@@ -234,7 +234,7 @@ int main(int argc, char* argv[])
}
}
return true;
- });
+ }, true);
std::unordered_map<uint64_t, uint64_t> counts;
size_t total = 0;
@@ -243,10 +243,17 @@ int main(int argc, char* argv[])
counts[out.second.size()]++;
total++;
}
- for (const auto &c: counts)
+ if (total > 0)
+ {
+ for (const auto &c: counts)
+ {
+ float percent = 100.f * c.second / total;
+ MINFO(std::to_string(c.second) << " outputs used " << c.first << " times (" << percent << "%)");
+ }
+ }
+ else
{
- float percent = 100.f * c.second / total;
- MINFO(std::to_string(c.second) << " outputs used " << c.first << " times (" << percent << "%)");
+ MINFO("No outputs to process");
}
LOG_PRINT_L0("Blockchain usage exported OK");
diff --git a/src/blocks/checkpoints.dat b/src/blocks/checkpoints.dat
index cff103804..501a55673 100644
--- a/src/blocks/checkpoints.dat
+++ b/src/blocks/checkpoints.dat
Binary files differ
diff --git a/src/checkpoints/checkpoints.cpp b/src/checkpoints/checkpoints.cpp
index 2c11af4b2..ef1ee171d 100644
--- a/src/checkpoints/checkpoints.cpp
+++ b/src/checkpoints/checkpoints.cpp
@@ -207,7 +207,7 @@ namespace cryptonote
ADD_CHECKPOINT(1390000, "a8f5649dd4ded60eedab475f2bec8c934681c07e3cf640e9be0617554f13ff6c");
ADD_CHECKPOINT(1450000, "ac94e8860093bc7c83e4e91215cba1d663421ecf4067a0ae609c3a8b52bcfac2");
ADD_CHECKPOINT(1530000, "01759bce497ec38e63c78b1038892169203bb78f87e488172f6b854fcd63ba7e");
-
+ ADD_CHECKPOINT(1579000, "7d0d7a2346373afd41ed1e744a939fc5d474a7dbaa257be5c6fff4009e789241");
return true;
}
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 66fd8d7ad..808ef7630 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -87,6 +87,7 @@ target_link_libraries(common
${Boost_SYSTEM_LIBRARY}
${Boost_THREAD_LIBRARY}
${Boost_REGEX_LIBRARY}
+ ${Boost_CHRONO_LIBRARY}
PRIVATE
${OPENSSL_LIBRARIES}
${EXTRA_LIBRARIES})
diff --git a/src/common/util.cpp b/src/common/util.cpp
index 1877acb57..7e77e19b1 100644
--- a/src/common/util.cpp
+++ b/src/common/util.cpp
@@ -43,6 +43,7 @@ using namespace epee;
#include "crypto/crypto.h"
#include "util.h"
+#include "stack_trace.h"
#include "memwipe.h"
#include "cryptonote_config.h"
#include "net/http_client.h" // epee::net_utils::...
@@ -560,10 +561,48 @@ std::string get_nix_version_display_string()
}
return false;
}
+
+#ifdef STACK_TRACE
+#ifdef _WIN32
+ // https://stackoverflow.com/questions/1992816/how-to-handle-seg-faults-under-windows
+ static LONG WINAPI windows_crash_handler(PEXCEPTION_POINTERS pExceptionInfo)
+ {
+ tools::log_stack_trace("crashing");
+ exit(1);
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+ static void setup_crash_dump()
+ {
+ SetUnhandledExceptionFilter(windows_crash_handler);
+ }
+#else
+ static void posix_crash_handler(int signal)
+ {
+ tools::log_stack_trace(("crashing with fatal signal " + std::to_string(signal)).c_str());
+#ifdef NDEBUG
+ _exit(1);
+#else
+ abort();
+#endif
+ }
+ static void setup_crash_dump()
+ {
+ signal(SIGSEGV, posix_crash_handler);
+ signal(SIGBUS, posix_crash_handler);
+ signal(SIGILL, posix_crash_handler);
+ signal(SIGFPE, posix_crash_handler);
+ }
+#endif
+#else
+ static void setup_crash_dump() {}
+#endif
+
bool on_startup()
{
mlog_configure("", true);
+ setup_crash_dump();
+
sanitize_locale();
#ifdef __GLIBC__
diff --git a/src/crypto/chacha.h b/src/crypto/chacha.h
index 7a120931a..2b3ed8043 100644
--- a/src/crypto/chacha.h
+++ b/src/crypto/chacha.h
@@ -73,14 +73,14 @@ namespace crypto {
static_assert(sizeof(chacha_key) <= sizeof(hash), "Size of hash must be at least that of chacha_key");
tools::scrubbed_arr<char, HASH_SIZE> pwd_hash;
crypto::cn_slow_hash(data, size, pwd_hash.data(), 0/*variant*/, 0/*prehashed*/);
- memcpy(&key, pwd_hash.data(), sizeof(key));
+ memcpy(&unwrap(key), pwd_hash.data(), sizeof(key));
}
inline void generate_chacha_key_prehashed(const void *data, size_t size, chacha_key& key) {
static_assert(sizeof(chacha_key) <= sizeof(hash), "Size of hash must be at least that of chacha_key");
tools::scrubbed_arr<char, HASH_SIZE> pwd_hash;
crypto::cn_slow_hash(data, size, pwd_hash.data(), 0/*variant*/, 1/*prehashed*/);
- memcpy(&key, pwd_hash.data(), sizeof(key));
+ memcpy(&unwrap(key), pwd_hash.data(), sizeof(key));
}
inline void generate_chacha_key(std::string password, chacha_key& key) {
diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp
index 494027560..f4ef751d3 100644
--- a/src/crypto/crypto.cpp
+++ b/src/crypto/crypto.cpp
@@ -70,8 +70,6 @@ namespace crypto {
#include "random.h"
}
- boost::mutex random_lock;
-
static inline unsigned char *operator &(ec_point &point) {
return &reinterpret_cast<unsigned char &>(point);
}
@@ -88,6 +86,13 @@ namespace crypto {
return &reinterpret_cast<const unsigned char &>(scalar);
}
+ void generate_random_bytes_thread_safe(size_t N, uint8_t *bytes)
+ {
+ static boost::mutex random_lock;
+ boost::lock_guard<boost::mutex> lock(random_lock);
+ generate_random_bytes_not_thread_safe(N, bytes);
+ }
+
/* generate a random 32-byte (256-bit) integer and copy it to res */
static inline void random_scalar_not_thread_safe(ec_scalar &res) {
unsigned char tmp[64];
@@ -96,8 +101,10 @@ namespace crypto {
memcpy(&res, tmp, 32);
}
static inline void random_scalar(ec_scalar &res) {
- boost::lock_guard<boost::mutex> lock(random_lock);
- random_scalar_not_thread_safe(res);
+ unsigned char tmp[64];
+ generate_random_bytes_thread_safe(64, tmp);
+ sc_reduce(tmp);
+ memcpy(&res, tmp, 32);
}
void hash_to_scalar(const void *data, size_t length, ec_scalar &res) {
@@ -124,9 +131,9 @@ namespace crypto {
random_scalar(rng);
}
sec = rng;
- sc_reduce32(&sec); // reduce in case second round of keys (sendkeys)
+ sc_reduce32(&unwrap(sec)); // reduce in case second round of keys (sendkeys)
- ge_scalarmult_base(&point, &sec);
+ ge_scalarmult_base(&point, &unwrap(sec));
ge_p3_tobytes(&pub, &point);
return rng;
@@ -139,10 +146,10 @@ namespace crypto {
bool crypto_ops::secret_key_to_public_key(const secret_key &sec, public_key &pub) {
ge_p3 point;
- if (sc_check(&sec) != 0) {
+ if (sc_check(&unwrap(sec)) != 0) {
return false;
}
- ge_scalarmult_base(&point, &sec);
+ ge_scalarmult_base(&point, &unwrap(sec));
ge_p3_tobytes(&pub, &point);
return true;
}
@@ -155,7 +162,7 @@ namespace crypto {
if (ge_frombytes_vartime(&point, &key1) != 0) {
return false;
}
- ge_scalarmult(&point2, &key2, &point);
+ ge_scalarmult(&point2, &unwrap(key2), &point);
ge_mul8(&point3, &point2);
ge_p1p1_to_p2(&point2, &point3);
ge_tobytes(&derivation, &point2);
@@ -199,7 +206,7 @@ namespace crypto {
ec_scalar scalar;
assert(sc_check(&base) == 0);
derivation_to_scalar(derivation, output_index, scalar);
- sc_add(&derived_key, &base, &scalar);
+ sc_add(&unwrap(derived_key), &unwrap(base), &scalar);
}
bool crypto_ops::derive_subaddress_public_key(const public_key &out_key, const key_derivation &derivation, std::size_t output_index, public_key &derived_key) {
@@ -254,7 +261,7 @@ namespace crypto {
ge_scalarmult_base(&tmp3, &k);
ge_p3_tobytes(&buf.comm, &tmp3);
hash_to_scalar(&buf, sizeof(s_comm), sig.c);
- sc_mulsub(&sig.r, &sig.c, &sec, &k);
+ sc_mulsub(&sig.r, &sig.c, &unwrap(sec), &k);
}
bool crypto_ops::check_signature(const hash &prefix_hash, const public_key &pub, const signature &sig) {
@@ -347,7 +354,7 @@ namespace crypto {
hash_to_scalar(&buf, sizeof(buf), sig.c);
// sig.r = k - sig.c*r
- sc_mulsub(&sig.r, &sig.c, &r, &k);
+ sc_mulsub(&sig.r, &sig.c, &unwrap(r), &k);
}
bool crypto_ops::check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig) {
@@ -451,7 +458,7 @@ namespace crypto {
ge_p2 point2;
assert(sc_check(&sec) == 0);
hash_to_ec(pub, point);
- ge_scalarmult(&point2, &sec, &point);
+ ge_scalarmult(&point2, &unwrap(sec), &point);
ge_tobytes(&image, &point2);
}
@@ -530,7 +537,7 @@ POP_WARNINGS
}
hash_to_scalar(buf.get(), rs_comm_size(pubs_count), h);
sc_sub(&sig[sec_index].c, &h, &sum);
- sc_mulsub(&sig[sec_index].r, &sig[sec_index].c, &sec, &k);
+ sc_mulsub(&sig[sec_index].r, &sig[sec_index].c, &unwrap(sec), &k);
}
bool crypto_ops::check_ring_signature(const hash &prefix_hash, const key_image &image,
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index 81ebfb9e2..9ea0f2ec0 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -53,8 +53,6 @@ namespace crypto {
#include "random.h"
}
- extern boost::mutex random_lock;
-
#pragma pack(push, 1)
POD_CLASS ec_point {
char data[32];
@@ -149,11 +147,12 @@ namespace crypto {
const public_key *const *, std::size_t, const signature *);
};
+ void generate_random_bytes_thread_safe(size_t N, uint8_t *bytes);
+
/* Generate N random bytes
*/
inline void rand(size_t N, uint8_t *bytes) {
- boost::lock_guard<boost::mutex> lock(random_lock);
- generate_random_bytes_not_thread_safe(N, bytes);
+ generate_random_bytes_thread_safe(N, bytes);
}
/* Generate a value filled with random bytes.
@@ -161,8 +160,7 @@ namespace crypto {
template<typename T>
typename std::enable_if<std::is_pod<T>::value, T>::type rand() {
typename std::remove_cv<T>::type res;
- boost::lock_guard<boost::mutex> lock(random_lock);
- generate_random_bytes_not_thread_safe(sizeof(T), &res);
+ generate_random_bytes_thread_safe(sizeof(T), (uint8_t*)&res);
return res;
}
diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c
index d7dcbd274..35e98f2f5 100644
--- a/src/crypto/slow-hash.c
+++ b/src/crypto/slow-hash.c
@@ -524,7 +524,7 @@ void slow_hash_free_state(void)
else
{
#if defined(_MSC_VER) || defined(__MINGW32__)
- VirtualFree(hp_state, MEMORY, MEM_RELEASE);
+ VirtualFree(hp_state, 0, MEM_RELEASE);
#else
munmap(hp_state, MEMORY);
#endif
diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp
index ae7c1c0ae..428be1c9c 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.cpp
+++ b/src/cryptonote_basic/cryptonote_format_utils.cpp
@@ -124,6 +124,40 @@ namespace cryptonote
return h;
}
//---------------------------------------------------------------
+ bool expand_transaction_1(transaction &tx, bool base_only)
+ {
+ if (tx.version >= 2 && !is_coinbase(tx))
+ {
+ rct::rctSig &rv = tx.rct_signatures;
+ if (rv.outPk.size() != tx.vout.size())
+ {
+ LOG_PRINT_L1("Failed to parse transaction from blob, bad outPk size in tx " << get_transaction_hash(tx));
+ return false;
+ }
+ for (size_t n = 0; n < tx.rct_signatures.outPk.size(); ++n)
+ rv.outPk[n].dest = rct::pk2rct(boost::get<txout_to_key>(tx.vout[n].target).key);
+
+ if (!base_only)
+ {
+ const bool bulletproof = rv.type == rct::RCTTypeFullBulletproof || rv.type == rct::RCTTypeSimpleBulletproof;
+ if (bulletproof)
+ {
+ if (rv.p.bulletproofs.size() != tx.vout.size())
+ {
+ LOG_PRINT_L1("Failed to parse transaction from blob, bad bulletproofs size in tx " << get_transaction_hash(tx));
+ return false;
+ }
+ for (size_t n = 0; n < rv.outPk.size(); ++n)
+ {
+ rv.p.bulletproofs[n].V.resize(1);
+ rv.p.bulletproofs[n].V[0] = rv.outPk[n].mask;
+ }
+ }
+ }
+ }
+ return true;
+ }
+ //---------------------------------------------------------------
bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx)
{
std::stringstream ss;
@@ -131,6 +165,7 @@ namespace cryptonote
binary_archive<false> ba(ss);
bool r = ::serialization::serialize(ba, tx);
CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob");
+ CHECK_AND_ASSERT_MES(expand_transaction_1(tx, false), false, "Failed to expand transaction data");
tx.invalidate_hashes();
return true;
}
@@ -142,6 +177,7 @@ namespace cryptonote
binary_archive<false> ba(ss);
bool r = tx.serialize_base(ba);
CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob");
+ CHECK_AND_ASSERT_MES(expand_transaction_1(tx, true), false, "Failed to expand transaction data");
return true;
}
//---------------------------------------------------------------
@@ -152,6 +188,7 @@ namespace cryptonote
binary_archive<false> ba(ss);
bool r = ::serialization::serialize(ba, tx);
CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob");
+ CHECK_AND_ASSERT_MES(expand_transaction_1(tx, false), false, "Failed to expand transaction data");
tx.invalidate_hashes();
//TODO: validate tx
@@ -742,6 +779,61 @@ namespace cryptonote
return get_transaction_hash(t, res, NULL);
}
//---------------------------------------------------------------
+ bool calculate_transaction_prunable_hash(const transaction& t, crypto::hash& res)
+ {
+ if (t.version == 1)
+ return false;
+ transaction &tt = const_cast<transaction&>(t);
+ std::stringstream ss;
+ binary_archive<true> ba(ss);
+ const size_t inputs = t.vin.size();
+ const size_t outputs = t.vout.size();
+ const size_t mixin = t.vin.empty() ? 0 : t.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(t.vin[0]).key_offsets.size() - 1 : 0;
+ bool r = tt.rct_signatures.p.serialize_rctsig_prunable(ba, t.rct_signatures.type, inputs, outputs, mixin);
+ CHECK_AND_ASSERT_MES(r, false, "Failed to serialize rct signatures prunable");
+ cryptonote::get_blob_hash(ss.str(), res);
+ return true;
+ }
+ //---------------------------------------------------------------
+ crypto::hash get_transaction_prunable_hash(const transaction& t)
+ {
+ crypto::hash res;
+ CHECK_AND_ASSERT_THROW_MES(calculate_transaction_prunable_hash(t, res), "Failed to calculate tx prunable hash");
+ return res;
+ }
+ //---------------------------------------------------------------
+ crypto::hash get_pruned_transaction_hash(const transaction& t, const crypto::hash &pruned_data_hash)
+ {
+ // v1 transactions hash the entire blob
+ CHECK_AND_ASSERT_THROW_MES(t.version > 1, "Hash for pruned v1 tx cannot be calculated");
+
+ // v2 transactions hash different parts together, than hash the set of those hashes
+ crypto::hash hashes[3];
+
+ // prefix
+ get_transaction_prefix_hash(t, hashes[0]);
+
+ transaction &tt = const_cast<transaction&>(t);
+
+ // base rct
+ {
+ std::stringstream ss;
+ binary_archive<true> ba(ss);
+ const size_t inputs = t.vin.size();
+ const size_t outputs = t.vout.size();
+ bool r = tt.rct_signatures.serialize_rctsig_base(ba, inputs, outputs);
+ CHECK_AND_ASSERT_THROW_MES(r, "Failed to serialize rct signatures base");
+ cryptonote::get_blob_hash(ss.str(), hashes[1]);
+ }
+
+ // prunable rct
+ hashes[2] = pruned_data_hash;
+
+ // the tx hash is the hash of the 3 hashes
+ crypto::hash res = cn_fast_hash(hashes, sizeof(hashes));
+ return res;
+ }
+ //---------------------------------------------------------------
bool calculate_transaction_hash(const transaction& t, crypto::hash& res, size_t* blob_size)
{
// v1 transactions hash the entire blob
@@ -777,14 +869,7 @@ namespace cryptonote
}
else
{
- std::stringstream ss;
- binary_archive<true> ba(ss);
- const size_t inputs = t.vin.size();
- const size_t outputs = t.vout.size();
- const size_t mixin = t.vin.empty() ? 0 : t.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(t.vin[0]).key_offsets.size() - 1 : 0;
- bool r = tt.rct_signatures.p.serialize_rctsig_prunable(ba, t.rct_signatures.type, inputs, outputs, mixin);
- CHECK_AND_ASSERT_MES(r, false, "Failed to serialize rct signatures prunable");
- cryptonote::get_blob_hash(ss.str(), hashes[2]);
+ CHECK_AND_ASSERT_MES(calculate_transaction_prunable_hash(t, hashes[2]), false, "Failed to get tx prunable hash");
}
// the tx hash is the hash of the 3 hashes
diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h
index 79466e9c4..8a5296d5b 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.h
+++ b/src/cryptonote_basic/cryptonote_format_utils.h
@@ -100,7 +100,11 @@ namespace cryptonote
bool get_transaction_hash(const transaction& t, crypto::hash& res);
bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t& blob_size);
bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t* blob_size);
+ bool calculate_transaction_prunable_hash(const transaction& t, crypto::hash& res);
+ crypto::hash get_transaction_prunable_hash(const transaction& t);
bool calculate_transaction_hash(const transaction& t, crypto::hash& res, size_t* blob_size);
+ crypto::hash get_pruned_transaction_hash(const transaction& t, const crypto::hash &pruned_data_hash);
+
blobdata get_block_hashing_blob(const block& b);
bool calculate_block_hash(const block& b, crypto::hash& res);
bool get_block_hash(const block& b, crypto::hash& res);
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index f02a1f8d6..54d8fac31 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -156,7 +156,9 @@ static const struct {
//------------------------------------------------------------------
Blockchain::Blockchain(tx_memory_pool& tx_pool) :
m_db(), m_tx_pool(tx_pool), m_hardfork(NULL), m_timestamps_and_difficulties_height(0), m_current_block_cumul_sz_limit(0), m_current_block_cumul_sz_median(0),
- m_enforce_dns_checkpoints(false), m_max_prepare_blocks_threads(4), m_db_blocks_per_sync(1), m_db_sync_mode(db_async), m_db_default_sync(false), m_fast_sync(true), m_show_time_stats(false), m_sync_counter(0), m_cancel(false)
+ m_enforce_dns_checkpoints(false), m_max_prepare_blocks_threads(4), m_db_blocks_per_sync(1), m_db_sync_mode(db_async), m_db_default_sync(false), m_fast_sync(true), m_show_time_stats(false), m_sync_counter(0), m_cancel(false),
+ m_difficulty_for_next_block_top_hash(crypto::null_hash),
+ m_difficulty_for_next_block(1)
{
LOG_PRINT_L3("Blockchain::" << __func__);
}
@@ -441,6 +443,53 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline
MINFO("Blockchain initialized. last block: " << m_db->height() - 1 << ", " << epee::misc_utils::get_time_interval_string(timestamp_diff) << " time ago, current difficulty: " << get_difficulty_for_next_block());
m_db->block_txn_stop();
+ uint64_t num_popped_blocks = 0;
+ while (true)
+ {
+ const uint64_t top_height = m_db->height() - 1;
+ const crypto::hash top_id = m_db->top_block_hash();
+ const block top_block = m_db->get_top_block();
+ const uint8_t ideal_hf_version = get_ideal_hard_fork_version(top_height);
+ if (ideal_hf_version <= 1 || ideal_hf_version == top_block.major_version)
+ {
+ if (num_popped_blocks > 0)
+ MGINFO("Initial popping done, top block: " << top_id << ", top height: " << top_height << ", block version: " << (uint64_t)top_block.major_version);
+ break;
+ }
+ else
+ {
+ if (num_popped_blocks == 0)
+ MGINFO("Current top block " << top_id << " at height " << top_height << " has version " << (uint64_t)top_block.major_version << " which disagrees with the ideal version " << (uint64_t)ideal_hf_version);
+ if (num_popped_blocks % 100 == 0)
+ MGINFO("Popping blocks... " << top_height);
+ ++num_popped_blocks;
+ block popped_block;
+ std::vector<transaction> popped_txs;
+ try
+ {
+ m_db->pop_block(popped_block, popped_txs);
+ }
+ // anything that could cause this to throw is likely catastrophic,
+ // so we re-throw
+ catch (const std::exception& e)
+ {
+ MERROR("Error popping block from blockchain: " << e.what());
+ throw;
+ }
+ catch (...)
+ {
+ MERROR("Error popping block from blockchain, throwing!");
+ throw;
+ }
+ }
+ }
+ if (num_popped_blocks > 0)
+ {
+ m_timestamps_and_difficulties_height = 0;
+ m_hardfork->reorganize_from_chain_height(get_current_blockchain_height());
+ m_tx_pool.on_blockchain_dec(m_db->height()-1, get_tail_id());
+ }
+
update_next_cumulative_size_limit();
return true;
}
@@ -584,6 +633,12 @@ block Blockchain::pop_block_from_blockchain()
}
}
}
+
+ m_blocks_longhash_table.clear();
+ m_scan_table.clear();
+ m_blocks_txs_check.clear();
+ m_check_txin_table.clear();
+
update_next_cumulative_size_limit();
m_tx_pool.on_blockchain_dec(m_db->height()-1, get_tail_id());
@@ -751,7 +806,17 @@ bool Blockchain::get_block_by_hash(const crypto::hash &h, block &blk, bool *orph
difficulty_type Blockchain::get_difficulty_for_next_block()
{
LOG_PRINT_L3("Blockchain::" << __func__);
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
+
+ CRITICAL_REGION_LOCAL(m_difficulty_lock);
+ // we can call this without the blockchain lock, it might just give us
+ // something a bit out of date, but that's fine since anything which
+ // requires the blockchain lock will have acquired it in the first place,
+ // and it will be unlocked only when called from the getinfo RPC
+ crypto::hash top_hash = get_tail_id();
+ if (top_hash == m_difficulty_for_next_block_top_hash)
+ return m_difficulty_for_next_block;
+
+ CRITICAL_REGION_LOCAL1(m_blockchain_lock);
std::vector<uint64_t> timestamps;
std::vector<difficulty_type> difficulties;
auto height = m_db->height();
@@ -794,7 +859,10 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
m_difficulties = difficulties;
}
size_t target = get_difficulty_target();
- return next_difficulty(timestamps, difficulties, target);
+ difficulty_type diff = next_difficulty(timestamps, difficulties, target);
+ m_difficulty_for_next_block_top_hash = top_hash;
+ m_difficulty_for_next_block = diff;
+ return diff;
}
//------------------------------------------------------------------
// This function removes blocks from the blockchain until it gets to the
@@ -1142,6 +1210,12 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
b.prev_id = get_tail_id();
b.timestamp = time(NULL);
+ uint64_t median_ts;
+ if (!check_block_timestamp(b, median_ts))
+ {
+ b.timestamp = median_ts;
+ }
+
diffic = get_difficulty_for_next_block();
CHECK_AND_ASSERT_MES(diffic, false, "difficulty overhead.");
@@ -1903,7 +1977,7 @@ 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
+bool Blockchain::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
{
// rct outputs don't exist before v3
if (amount == 0)
@@ -1924,22 +1998,7 @@ bool Blockchain::get_output_distribution(uint64_t amount, uint64_t from_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;
+ return m_db->get_output_distribution(amount, start_height, to_height, distribution, base);
}
//------------------------------------------------------------------
// This function takes a list of block hashes from another node
@@ -2054,7 +2113,7 @@ bool Blockchain::get_blocks(const t_ids_container& block_ids, t_blocks_container
//TODO: return type should be void, throw on exception
// alternatively, return true only if no transactions missed
template<class t_ids_container, class t_tx_container, class t_missed_container>
-bool Blockchain::get_transactions_blobs(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) const
+bool Blockchain::get_transactions_blobs(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs, bool pruned) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
@@ -2064,7 +2123,9 @@ bool Blockchain::get_transactions_blobs(const t_ids_container& txs_ids, t_tx_con
try
{
cryptonote::blobdata tx;
- if (m_db->get_tx_blob(tx_hash, tx))
+ if (pruned && m_db->get_pruned_tx_blob(tx_hash, tx))
+ txs.push_back(std::move(tx));
+ else if (!pruned && m_db->get_tx_blob(tx_hash, tx))
txs.push_back(std::move(tx));
else
missed_txs.push_back(tx_hash);
@@ -2149,7 +2210,7 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc
// find split point between ours and foreign blockchain (or start at
// blockchain height <req_start_block>), and return up to max_count FULL
// blocks by reference.
-bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<cryptonote::blobdata, std::list<cryptonote::blobdata> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const
+bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<cryptonote::blobdata, std::list<cryptonote::blobdata> > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, size_t max_count) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
@@ -2182,7 +2243,7 @@ bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, cons
block b;
CHECK_AND_ASSERT_MES(parse_and_validate_block_from_blob(blocks.back().first, b), false, "internal error, invalid block");
std::list<crypto::hash> mis;
- get_transactions_blobs(b.tx_hashes, blocks.back().second, mis);
+ get_transactions_blobs(b.tx_hashes, blocks.back().second, mis, pruned);
CHECK_AND_ASSERT_MES(!mis.size(), false, "internal error, transaction from block not found");
size += blocks.back().first.size();
for (const auto &t: blocks.back().second)
@@ -2658,6 +2719,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
tools::threadpool& tpool = tools::threadpool::getInstance();
tools::threadpool::waiter waiter;
+ const auto waiter_guard = epee::misc_utils::create_scope_leave_handler([&]() { waiter.wait(); });
int threads = tpool.get_max_concurrency();
for (const auto& txin : tx.vin)
@@ -3127,10 +3189,10 @@ uint64_t Blockchain::get_adjusted_time() const
}
//------------------------------------------------------------------
//TODO: revisit, has changed a bit on upstream
-bool Blockchain::check_block_timestamp(std::vector<uint64_t>& timestamps, const block& b) const
+bool Blockchain::check_block_timestamp(std::vector<uint64_t>& timestamps, const block& b, uint64_t& median_ts) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
- uint64_t median_ts = epee::misc_utils::median(timestamps);
+ median_ts = epee::misc_utils::median(timestamps);
if(b.timestamp < median_ts)
{
@@ -3148,7 +3210,7 @@ bool Blockchain::check_block_timestamp(std::vector<uint64_t>& timestamps, const
// true if the block's timestamp is not less than the timestamp of the
// median of the selected blocks
// false otherwise
-bool Blockchain::check_block_timestamp(const block& b) const
+bool Blockchain::check_block_timestamp(const block& b, uint64_t& median_ts) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
if(b.timestamp > get_adjusted_time() + CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT)
@@ -3173,7 +3235,7 @@ bool Blockchain::check_block_timestamp(const block& b) const
timestamps.push_back(m_db->get_block_timestamp(offset));
}
- return check_block_timestamp(timestamps, b);
+ return check_block_timestamp(timestamps, b, median_ts);
}
//------------------------------------------------------------------
void Blockchain::return_tx_to_pool(std::vector<transaction> &txs)
@@ -4322,9 +4384,9 @@ uint64_t Blockchain::get_difficulty_target() const
return get_current_hard_fork_version() < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2;
}
-std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> Blockchain:: get_output_histogram(const std::vector<uint64_t> &amounts, bool unlocked, uint64_t recent_cutoff) const
+std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> Blockchain:: get_output_histogram(const std::vector<uint64_t> &amounts, bool unlocked, uint64_t recent_cutoff, uint64_t min_count) const
{
- return m_db->get_output_histogram(amounts, unlocked, recent_cutoff);
+ return m_db->get_output_histogram(amounts, unlocked, recent_cutoff, min_count);
}
std::list<std::pair<Blockchain::block_extended_info,uint64_t>> Blockchain::get_alternative_chains() const
@@ -4365,7 +4427,7 @@ void Blockchain::cancel()
}
#if defined(PER_BLOCK_CHECKPOINT)
-static const char expected_block_hashes_hash[] = "1d3df1a177bd6f752d87c0d7b960e502605742721afb39953265f1e0f7f9b01f";
+static const char expected_block_hashes_hash[] = "59261c03b54bcb21bd463f9fe40a94f40840a12642e9a3b3bfb11b35839a5fe3";
void Blockchain::load_compiled_in_block_hashes()
{
const bool testnet = m_nettype == TESTNET;
@@ -4476,9 +4538,9 @@ bool Blockchain::for_blocks_range(const uint64_t& h1, const uint64_t& h2, std::f
return m_db->for_blocks_range(h1, h2, f);
}
-bool Blockchain::for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)> f) const
+bool Blockchain::for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)> f, bool pruned) const
{
- return m_db->for_all_transactions(f);
+ return m_db->for_all_transactions(f, pruned);
}
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
@@ -4493,4 +4555,5 @@ bool Blockchain::for_all_outputs(uint64_t amount, std::function<bool(uint64_t he
namespace cryptonote {
template bool Blockchain::get_transactions(const std::vector<crypto::hash>&, std::list<transaction>&, std::list<crypto::hash>&) const;
+template bool Blockchain::get_transactions_blobs(const std::vector<crypto::hash>&, std::list<cryptonote::blobdata>&, std::list<crypto::hash>&, bool) const;
}
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index 4423199de..769e608ca 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -415,11 +415,12 @@ namespace cryptonote
* @param blocks return-by-reference the blocks and their transactions
* @param total_height return-by-reference our current blockchain height
* @param start_height return-by-reference the height of the first block returned
+ * @param pruned whether to return full or pruned tx blobs
* @param max_count the max number of blocks to get
*
* @return true if a block found in common or req_start_block specified, else false
*/
- bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<cryptonote::blobdata, std::list<cryptonote::blobdata> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const;
+ bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<cryptonote::blobdata, std::list<cryptonote::blobdata> > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, size_t max_count) const;
/**
* @brief retrieves a set of blocks and their transactions, and possibly other transactions
@@ -527,12 +528,13 @@ namespace cryptonote
* @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 from_height the height before which we do not care about the data
+ * @param to_height the height after 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;
+ 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;
/**
* @brief gets the global indices for outputs from a given transaction
@@ -678,11 +680,12 @@ namespace cryptonote
* @param txs_ids a container of hashes for which to get the corresponding transactions
* @param txs return-by-reference a container to store result transactions in
* @param missed_txs return-by-reference a container to store missed transactions in
+ * @param pruned whether to return full or pruned blobs
*
* @return false if an unexpected exception occurs, else true
*/
template<class t_ids_container, class t_tx_container, class t_missed_container>
- bool get_transactions_blobs(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) const;
+ bool get_transactions_blobs(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs, bool pruned = false) const;
template<class t_ids_container, class t_tx_container, class t_missed_container>
bool get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) const;
@@ -827,10 +830,11 @@ namespace cryptonote
* @param amounts optional set of amounts to lookup
* @param unlocked whether to restrict instances to unlocked ones
* @param recent_cutoff timestamp to consider outputs as recent
+ * @param min_count return only amounts with at least that many instances
*
* @return a set of amount/instances
*/
- 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) const;
+ 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;
/**
* @brief perform a check on all key images in the blockchain
@@ -856,10 +860,11 @@ namespace cryptonote
* @brief perform a check on all transactions in the blockchain
*
* @param std::function the check to perform, pass/fail
+ * @param bool pruned whether to return pruned txes only
*
* @return false if any transaction fails the check, otherwise true
*/
- bool for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)>) const;
+ bool for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)>, bool pruned) const;
/**
* @brief perform a check on all outputs in the blockchain
@@ -1006,6 +1011,10 @@ namespace cryptonote
std::vector<difficulty_type> m_difficulties;
uint64_t m_timestamps_and_difficulties_height;
+ epee::critical_section m_difficulty_lock;
+ crypto::hash m_difficulty_for_next_block_top_hash;
+ difficulty_type m_difficulty_for_next_block;
+
boost::asio::io_service m_async_service;
boost::thread_group m_async_pool;
std::unique_ptr<boost::asio::io_service::work> m_async_work_idle;
@@ -1291,10 +1300,12 @@ namespace cryptonote
* false otherwise
*
* @param b the block to be checked
+ * @param median_ts return-by-reference the median of timestamps
*
* @return true if the block's timestamp is valid, otherwise false
*/
- bool check_block_timestamp(const block& b) const;
+ bool check_block_timestamp(const block& b, uint64_t& median_ts) const;
+ bool check_block_timestamp(const block& b) const { uint64_t median_ts; return check_block_timestamp(b, median_ts); }
/**
* @brief checks a block's timestamp
@@ -1307,7 +1318,8 @@ namespace cryptonote
*
* @return true if the block's timestamp is valid, otherwise false
*/
- bool check_block_timestamp(std::vector<uint64_t>& timestamps, const block& b) const;
+ bool check_block_timestamp(std::vector<uint64_t>& timestamps, const block& b, uint64_t& median_ts) const;
+ bool check_block_timestamp(std::vector<uint64_t>& timestamps, const block& b) const { uint64_t median_ts; return check_block_timestamp(timestamps, b, median_ts); }
/**
* @brief get the "adjusted time"
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 4d852fc99..d2796deeb 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -438,6 +438,7 @@ namespace cryptonote
std::vector<std::string> options;
boost::trim(db_sync_mode);
boost::split(options, db_sync_mode, boost::is_any_of(" :"));
+ const bool db_sync_mode_is_default = command_line::is_arg_defaulted(vm, cryptonote::arg_db_sync_mode);
for(const auto &option : options)
MDEBUG("option: " << option);
@@ -458,18 +459,18 @@ namespace cryptonote
{
safemode = true;
db_flags = DBF_SAFE;
- sync_mode = db_nosync;
+ sync_mode = db_sync_mode_is_default ? db_defaultsync : db_nosync;
}
else if(options[0] == "fast")
{
db_flags = DBF_FAST;
- sync_mode = db_async;
+ sync_mode = db_sync_mode_is_default ? db_defaultsync : db_async;
}
else if(options[0] == "fastest")
{
db_flags = DBF_FASTEST;
blocks_per_sync = 1000; // default to fastest:async:1000
- sync_mode = db_async;
+ sync_mode = db_sync_mode_is_default ? db_defaultsync : db_async;
}
else
db_flags = DEFAULT_FLAGS;
@@ -478,9 +479,9 @@ namespace cryptonote
if(options.size() >= 2 && !safemode)
{
if(options[1] == "sync")
- sync_mode = db_sync;
+ sync_mode = db_sync_mode_is_default ? db_defaultsync : db_sync;
else if(options[1] == "async")
- sync_mode = db_async;
+ sync_mode = db_sync_mode_is_default ? db_defaultsync : db_async;
}
if(options.size() >= 3 && !safemode)
@@ -650,39 +651,6 @@ namespace cryptonote
return false;
}
- // resolve outPk references in rct txes
- // outPk aren't the only thing that need resolving for a fully resolved tx,
- // but outPk (1) are needed now to check range proof semantics, and
- // (2) do not need access to the blockchain to find data
- if (tx.version >= 2)
- {
- rct::rctSig &rv = tx.rct_signatures;
- if (rv.outPk.size() != tx.vout.size())
- {
- LOG_PRINT_L1("WRONG TRANSACTION BLOB, Bad outPk size in tx " << tx_hash << ", rejected");
- tvc.m_verifivation_failed = true;
- return false;
- }
- for (size_t n = 0; n < tx.rct_signatures.outPk.size(); ++n)
- rv.outPk[n].dest = rct::pk2rct(boost::get<txout_to_key>(tx.vout[n].target).key);
-
- const bool bulletproof = rv.type == rct::RCTTypeFullBulletproof || rv.type == rct::RCTTypeSimpleBulletproof;
- if (bulletproof)
- {
- if (rv.p.bulletproofs.size() != tx.vout.size())
- {
- LOG_PRINT_L1("WRONG TRANSACTION BLOB, Bad bulletproofs size in tx " << tx_hash << ", rejected");
- tvc.m_verifivation_failed = true;
- return false;
- }
- for (size_t n = 0; n < rv.outPk.size(); ++n)
- {
- rv.p.bulletproofs[n].V.resize(1);
- rv.p.bulletproofs[n].V[0] = rv.outPk[n].mask;
- }
- }
- }
-
if (keeped_by_block && get_blockchain_storage().is_within_compiled_block_hash_area())
{
MTRACE("Skipping semantics check for tx kept by block in embedded hash area");
@@ -1086,9 +1054,9 @@ namespace cryptonote
return m_blockchain_storage.find_blockchain_supplement(qblock_ids, resp);
}
//-----------------------------------------------------------------------------------------------
- bool core::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<cryptonote::blobdata, std::list<cryptonote::blobdata> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const
+ bool core::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<cryptonote::blobdata, std::list<cryptonote::blobdata> > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, size_t max_count) const
{
- return m_blockchain_storage.find_blockchain_supplement(req_start_block, qblock_ids, blocks, total_height, start_height, max_count);
+ return m_blockchain_storage.find_blockchain_supplement(req_start_block, qblock_ids, blocks, total_height, start_height, pruned, max_count);
}
//-----------------------------------------------------------------------------------------------
bool core::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) const
@@ -1106,9 +1074,9 @@ 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
+ bool core::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
{
- return m_blockchain_storage.get_output_distribution(amount, from_height, start_height, distribution, base);
+ return m_blockchain_storage.get_output_distribution(amount, from_height, to_height, start_height, distribution, base);
}
//-----------------------------------------------------------------------------------------------
bool core::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<uint64_t>& indexs) const
@@ -1418,6 +1386,11 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------------------------------------
+ uint8_t core::get_ideal_hard_fork_version() const
+ {
+ return get_blockchain_storage().get_ideal_hard_fork_version();
+ }
+ //-----------------------------------------------------------------------------------------------
uint8_t core::get_ideal_hard_fork_version(uint64_t height) const
{
return get_blockchain_storage().get_ideal_hard_fork_version(height);
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index abf79be1d..17b5680e5 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -517,7 +517,7 @@ namespace cryptonote
*
* @note see Blockchain::find_blockchain_supplement(const uint64_t, const std::list<crypto::hash>&, std::list<std::pair<cryptonote::blobdata, std::list<transaction> > >&, uint64_t&, uint64_t&, size_t) const
*/
- bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<cryptonote::blobdata, std::list<cryptonote::blobdata> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const;
+ bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<cryptonote::blobdata, std::list<cryptonote::blobdata> > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, size_t max_count) const;
/**
* @brief gets some stats about the daemon
@@ -576,7 +576,7 @@ namespace cryptonote
*
* @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;
+ 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;
/**
* @copydoc miner::pause
@@ -642,6 +642,13 @@ namespace cryptonote
uint64_t get_target_blockchain_height() const;
/**
+ * @brief returns the newest hardfork version known to the blockchain
+ *
+ * @return the version
+ */
+ uint8_t get_ideal_hard_fork_version() const;
+
+ /**
* @brief return the ideal hard fork version for a given block height
*
* @return what it says above
diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp
index c2252fcc7..071ce591e 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.cpp
+++ b/src/cryptonote_core/cryptonote_tx_utils.cpp
@@ -195,7 +195,7 @@ namespace cryptonote
return addr.m_view_public_key;
}
//---------------------------------------------------------------
- bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct, bool bulletproof, rct::multisig_out *msout)
+ bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct, bool bulletproof, rct::multisig_out *msout, bool shuffle_outs)
{
hw::device &hwdev = sender_account_keys.get_device();
@@ -315,9 +315,10 @@ namespace cryptonote
tx.vin.push_back(input_to_key);
}
- // "Shuffle" outs
- std::vector<tx_destination_entry> shuffled_dsts(destinations);
- std::shuffle(shuffled_dsts.begin(), shuffled_dsts.end(), std::default_random_engine(crypto::rand<unsigned int>()));
+ if (shuffle_outs)
+ {
+ std::shuffle(destinations.begin(), destinations.end(), std::default_random_engine(crypto::rand<unsigned int>()));
+ }
// sort ins by their key image
std::vector<size_t> ins_order(sources.size());
@@ -364,7 +365,7 @@ namespace cryptonote
uint64_t summary_outs_money = 0;
//fill outputs
size_t output_index = 0;
- for(const tx_destination_entry& dst_entr: shuffled_dsts)
+ for(const tx_destination_entry& dst_entr: destinations)
{
CHECK_AND_ASSERT_MES(dst_entr.amount > 0 || tx.version > 1, false, "Destination with wrong amount: " << dst_entr.amount);
crypto::key_derivation derivation;
@@ -600,7 +601,7 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------
- bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct, bool bulletproof, rct::multisig_out *msout)
+ bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct, bool bulletproof, rct::multisig_out *msout)
{
hw::device &hwdev = sender_account_keys.get_device();
hwdev.open_tx(tx_key);
@@ -629,7 +630,8 @@ namespace cryptonote
subaddresses[sender_account_keys.m_account_address.m_spend_public_key] = {0,0};
crypto::secret_key tx_key;
std::vector<crypto::secret_key> additional_tx_keys;
- return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, false, NULL);
+ std::vector<tx_destination_entry> destinations_copy = destinations;
+ return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations_copy, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, false, NULL);
}
//---------------------------------------------------------------
bool generate_genesis_block(
diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h
index 1c390078d..a5d149fca 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.h
+++ b/src/cryptonote_core/cryptonote_tx_utils.h
@@ -90,8 +90,8 @@ namespace cryptonote
//---------------------------------------------------------------
crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::account_public_address>& change_addr);
bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry> &sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time);
- bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, bool bulletproof = false, rct::multisig_out *msout = NULL);
- bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, bool bulletproof = false, rct::multisig_out *msout = NULL);
+ bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, bool bulletproof = false, rct::multisig_out *msout = NULL, bool shuffle_outs = true);
+ bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, bool bulletproof = false, rct::multisig_out *msout = NULL);
bool generate_genesis_block(
block& bl
diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp
index 5dfbc1dd4..bf1fe476e 100644
--- a/src/cryptonote_core/tx_pool.cpp
+++ b/src/cryptonote_core/tx_pool.cpp
@@ -1068,10 +1068,6 @@ namespace cryptonote
//TODO: investigate whether boolean return is appropriate
bool tx_memory_pool::fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee, uint64_t &expected_reward, uint8_t version)
{
- // Warning: This function takes already_generated_
- // coins as an argument and appears to do nothing
- // with it.
-
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
index 5d91ad569..91c6c5d5e 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
@@ -271,7 +271,7 @@ namespace cryptonote
const uint8_t version = m_core.get_ideal_hard_fork_version(hshd.current_height - 1);
if (version >= 6 && version != hshd.top_version)
{
- if (version < hshd.top_version)
+ if (version < hshd.top_version && version == m_core.get_ideal_hard_fork_version())
MCLOG_RED(el::Level::Warning, "global", context << " peer claims higher version that we think (" <<
(unsigned)hshd.top_version << " for " << (hshd.current_height - 1) << " instead of " << (unsigned)version <<
") - we may be forked from the network and a software upgrade may be needed");
@@ -308,7 +308,8 @@ namespace cryptonote
<< " [Your node is " << abs_diff << " blocks (" << ((abs_diff - diff_v2) / (24 * 60 * 60 / DIFFICULTY_TARGET_V1)) + (diff_v2 / (24 * 60 * 60 / DIFFICULTY_TARGET_V2)) << " days) "
<< (0 <= diff ? std::string("behind") : std::string("ahead"))
<< "] " << ENDL << "SYNCHRONIZATION started");
- m_core.safesyncmode(false);
+ if (hshd.current_height >= m_core.get_current_blockchain_height() + 5) // don't switch to unsafe mode just for a few blocks
+ m_core.safesyncmode(false);
}
LOG_PRINT_L1("Remote blockchain height: " << hshd.current_height << ", id: " << hshd.top_id);
context.m_state = cryptonote_connection_context::state_synchronizing;
@@ -795,7 +796,7 @@ namespace cryptonote
relay_transactions(arg, context);
}
- return true;
+ return 1;
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
@@ -1008,6 +1009,7 @@ skip:
if (blocks.empty())
{
MERROR("Next span has no blocks");
+ m_block_queue.remove_spans(span_connection_id, start_height);
break;
}
@@ -1015,6 +1017,7 @@ skip:
if (!parse_and_validate_block_from_blob(blocks.front().block, new_block))
{
MERROR("Failed to parse block, but it should already have been parsed");
+ m_block_queue.remove_spans(span_connection_id, start_height);
break;
}
bool parent_known = m_core.have_block(new_block.prev_id);
@@ -1031,6 +1034,7 @@ skip:
// this can happen if a connection was sicced onto a late span, if it did not have those blocks,
// since we don't know that at the sic time
LOG_ERROR_CCONTEXT("Got block with unknown parent which was not requested - querying block hashes");
+ m_block_queue.remove_spans(span_connection_id, start_height);
context.m_needed_objects.clear();
context.m_last_response_height = 0;
goto skip;
@@ -1064,7 +1068,7 @@ skip:
if (tvc.size() != block_entry.txs.size())
{
LOG_ERROR_CCONTEXT("Internal error: tvc.size() != block_entry.txs.size()");
- return true;
+ return 1;
}
std::list<blobdata>::const_iterator it = block_entry.txs.begin();
for (size_t i = 0; i < tvc.size(); ++i, ++it)
@@ -1075,7 +1079,7 @@ skip:
LOG_ERROR_CCONTEXT("transaction verification failed on NOTIFY_RESPONSE_GET_OBJECTS, tx_id = "
<< epee::string_tools::pod_to_hex(get_blob_hash(*it)) << ", dropping connection");
drop_connection(context, false, true);
- return true;
+ return 1;
}))
LOG_ERROR_CCONTEXT("span connection id not found");
@@ -1104,7 +1108,7 @@ skip:
if (!m_p2p->for_connection(span_connection_id, [&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t f)->bool{
LOG_PRINT_CCONTEXT_L1("Block verification failed, dropping connection");
drop_connection(context, true, true);
- return true;
+ return 1;
}))
LOG_ERROR_CCONTEXT("span connection id not found");
@@ -1123,7 +1127,7 @@ skip:
if (!m_p2p->for_connection(span_connection_id, [&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t f)->bool{
LOG_PRINT_CCONTEXT_L1("Block received at sync phase was marked as orphaned, dropping connection");
drop_connection(context, true, true);
- return true;
+ return 1;
}))
LOG_ERROR_CCONTEXT("span connection id not found");
@@ -1363,13 +1367,13 @@ skip:
MDEBUG(context << " we have the next span, and it is scheduled, resuming");
++context.m_callback_request_count;
m_p2p->request_callback(context);
- return 1;
+ return true;
}
for (size_t n = 0; n < 50; ++n)
{
if (m_stopping)
- return 1;
+ return true;
boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
}
}
@@ -1693,7 +1697,7 @@ skip:
m_p2p->relay_notify_to_list(NOTIFY_NEW_FLUFFY_BLOCK::ID, fluffyBlob, fluffyConnections);
m_p2p->relay_notify_to_list(NOTIFY_NEW_BLOCK::ID, fullBlob, fullConnections);
- return 1;
+ return true;
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp
index 7a89ebc0c..34d9fb4c8 100644
--- a/src/daemon/command_parser_executor.cpp
+++ b/src/daemon/command_parser_executor.cpp
@@ -27,6 +27,7 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "common/dns_utils.h"
+#include "version.h"
#include "daemon/command_parser_executor.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
@@ -314,7 +315,7 @@ bool t_command_parser_executor::start_mining(const std::vector<std::string>& arg
return true;
}
if(nettype != cryptonote::MAINNET)
- std::cout << "Mining to a " << (nettype == cryptonote::TESTNET ? "testnet" : "stagenet") << "address, make sure this is intentional!" << std::endl;
+ std::cout << "Mining to a " << (nettype == cryptonote::TESTNET ? "testnet" : "stagenet") << " address, make sure this is intentional!" << std::endl;
uint64_t threads_count = 1;
bool do_background_mining = false;
bool ignore_battery = false;
@@ -379,8 +380,6 @@ bool t_command_parser_executor::set_limit(const std::vector<std::string>& args)
std::cout << "failed to parse argument" << std::endl;
return false;
}
- if (limit > 0)
- limit *= 1024;
return m_executor.set_limit(limit, limit);
}
@@ -399,8 +398,6 @@ bool t_command_parser_executor::set_limit_up(const std::vector<std::string>& arg
std::cout << "failed to parse argument" << std::endl;
return false;
}
- if (limit > 0)
- limit *= 1024;
return m_executor.set_limit(0, limit);
}
@@ -419,8 +416,6 @@ bool t_command_parser_executor::set_limit_down(const std::vector<std::string>& a
std::cout << "failed to parse argument" << std::endl;
return false;
}
- if (limit > 0)
- limit *= 1024;
return m_executor.set_limit(limit, 0);
}
@@ -664,4 +659,10 @@ bool t_command_parser_executor::sync_info(const std::vector<std::string>& args)
return m_executor.sync_info();
}
+bool t_command_parser_executor::version(const std::vector<std::string>& args)
+{
+ std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << std::endl;
+ return true;
+}
+
} // namespace daemonize
diff --git a/src/daemon/command_parser_executor.h b/src/daemon/command_parser_executor.h
index 2c09a4748..a70070171 100644
--- a/src/daemon/command_parser_executor.h
+++ b/src/daemon/command_parser_executor.h
@@ -138,6 +138,8 @@ public:
bool relay_tx(const std::vector<std::string>& args);
bool sync_info(const std::vector<std::string>& args);
+
+ bool version(const std::vector<std::string>& args);
};
} // namespace daemonize
diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp
index a50dbea69..144603597 100644
--- a/src/daemon/command_server.cpp
+++ b/src/daemon/command_server.cpp
@@ -280,6 +280,11 @@ t_command_server::t_command_server(
, std::bind(&t_command_parser_executor::sync_info, &m_parser, p::_1)
, "Print information about the blockchain sync state."
);
+ m_command_lookup.set_handler(
+ "version"
+ , std::bind(&t_command_parser_executor::version, &m_parser, p::_1)
+ , "Print version information."
+ );
}
bool t_command_server::process_command_str(const std::string& cmd)
diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp
index 50384b2a6..49494e889 100644
--- a/src/daemon/main.cpp
+++ b/src/daemon/main.cpp
@@ -138,6 +138,28 @@ int main(int argc, char const * argv[])
return 0;
}
+ std::string config = command_line::get_arg(vm, daemon_args::arg_config_file);
+ boost::filesystem::path config_path(config);
+ boost::system::error_code ec;
+ if (bf::exists(config_path, ec))
+ {
+ try
+ {
+ po::store(po::parse_config_file<char>(config_path.string<std::string>().c_str(), core_settings), vm);
+ }
+ catch (const std::exception &e)
+ {
+ // log system isn't initialized yet
+ std::cerr << "Error parsing config file: " << e.what() << std::endl;
+ throw;
+ }
+ }
+ else if (!command_line::is_arg_defaulted(vm, daemon_args::arg_config_file))
+ {
+ std::cerr << "Can't find config file " << config << std::endl;
+ return 1;
+ }
+
const bool testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
const bool stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on);
if (testnet && stagenet)
@@ -170,29 +192,6 @@ int main(int argc, char const * argv[])
//bf::path relative_path_base = daemonizer::get_relative_path_base(vm);
bf::path relative_path_base = data_dir;
- std::string config = command_line::get_arg(vm, daemon_args::arg_config_file);
-
- boost::filesystem::path data_dir_path(data_dir);
- boost::filesystem::path config_path(config);
- if (!config_path.has_parent_path())
- {
- config_path = data_dir / config_path;
- }
-
- boost::system::error_code ec;
- if (bf::exists(config_path, ec))
- {
- try
- {
- po::store(po::parse_config_file<char>(config_path.string<std::string>().c_str(), core_settings), vm);
- }
- catch (const std::exception &e)
- {
- // log system isn't initialized yet
- std::cerr << "Error parsing config file: " << e.what() << std::endl;
- throw;
- }
- }
po::notify(vm);
// log_file_path
diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp
index 73b8d1a18..487853441 100644
--- a/src/daemon/rpc_command_executor.cpp
+++ b/src/daemon/rpc_command_executor.cpp
@@ -74,6 +74,7 @@ namespace {
<< "depth: " << boost::lexical_cast<std::string>(header.depth) << std::endl
<< "hash: " << header.hash << std::endl
<< "difficulty: " << boost::lexical_cast<std::string>(header.difficulty) << std::endl
+ << "POW hash: " << header.pow_hash << std::endl
<< "reward: " << boost::lexical_cast<std::string>(header.reward);
}
@@ -654,6 +655,7 @@ bool t_rpc_command_executor::print_block_by_hash(crypto::hash block_hash) {
epee::json_rpc::error error_resp;
req.hash = epee::string_tools::pod_to_hex(block_hash);
+ req.fill_pow_hash = true;
std::string fail_message = "Unsuccessful";
@@ -685,6 +687,7 @@ bool t_rpc_command_executor::print_block_by_height(uint64_t height) {
epee::json_rpc::error error_resp;
req.height = height;
+ req.fill_pow_hash = true;
std::string fail_message = "Unsuccessful";
@@ -720,6 +723,7 @@ bool t_rpc_command_executor::print_transaction(crypto::hash transaction_hash,
req.txs_hashes.push_back(epee::string_tools::pod_to_hex(transaction_hash));
req.decode_as_json = false;
+ req.prune = false;
if (m_is_rpc)
{
if (!m_rpc_client->rpc_request(req, res, "/gettransactions", fail_message.c_str()))
@@ -1176,8 +1180,8 @@ bool t_rpc_command_executor::get_limit()
}
}
- tools::msg_writer() << "limit-down is " << res.limit_down/1024 << " kB/s";
- tools::msg_writer() << "limit-up is " << res.limit_up/1024 << " kB/s";
+ tools::msg_writer() << "limit-down is " << res.limit_down << " kB/s";
+ tools::msg_writer() << "limit-up is " << res.limit_up << " kB/s";
return true;
}
@@ -1207,8 +1211,8 @@ bool t_rpc_command_executor::set_limit(int64_t limit_down, int64_t limit_up)
}
}
- tools::msg_writer() << "Set limit-down to " << res.limit_down/1024 << " kB/s";
- tools::msg_writer() << "Set limit-up to " << res.limit_up/1024 << " kB/s";
+ tools::msg_writer() << "Set limit-down to " << res.limit_down << " kB/s";
+ tools::msg_writer() << "Set limit-up to " << res.limit_up << " kB/s";
return true;
}
@@ -1235,7 +1239,7 @@ bool t_rpc_command_executor::get_limit_up()
}
}
- tools::msg_writer() << "limit-up is " << res.limit_up/1024 << " kB/s";
+ tools::msg_writer() << "limit-up is " << res.limit_up << " kB/s";
return true;
}
@@ -1262,7 +1266,7 @@ bool t_rpc_command_executor::get_limit_down()
}
}
- tools::msg_writer() << "limit-down is " << res.limit_down/1024 << " kB/s";
+ tools::msg_writer() << "limit-down is " << res.limit_down << " kB/s";
return true;
}
diff --git a/src/device/device_default.hpp b/src/device/device_default.hpp
index 649fed862..771fbba72 100644
--- a/src/device/device_default.hpp
+++ b/src/device/device_default.hpp
@@ -93,7 +93,7 @@ namespace hw {
bool sc_secret_add(crypto::secret_key &r, const crypto::secret_key &a, const crypto::secret_key &b) override;
crypto::secret_key generate_keys(crypto::public_key &pub, crypto::secret_key &sec, const crypto::secret_key& recovery_key = crypto::secret_key(), bool recover = false) override;
bool generate_key_derivation(const crypto::public_key &pub, const crypto::secret_key &sec, crypto::key_derivation &derivation) override;
- bool conceal_derivation(crypto::key_derivation &derivation, const crypto::public_key &tx_pub_key, const std::vector<crypto::public_key> &additional_tx_pub_keys, const crypto::key_derivation &main_derivation, const std::vector<crypto::key_derivation> &additional_derivations);
+ bool conceal_derivation(crypto::key_derivation &derivation, const crypto::public_key &tx_pub_key, const std::vector<crypto::public_key> &additional_tx_pub_keys, const crypto::key_derivation &main_derivation, const std::vector<crypto::key_derivation> &additional_derivations) override;
bool derivation_to_scalar(const crypto::key_derivation &derivation, const size_t output_index, crypto::ec_scalar &res) override;
bool derive_secret_key(const crypto::key_derivation &derivation, const std::size_t output_index, const crypto::secret_key &sec, crypto::secret_key &derived_sec) override;
bool derive_public_key(const crypto::key_derivation &derivation, const std::size_t output_index, const crypto::public_key &pub, crypto::public_key &derived_pub) override;
diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp
index 9f73bd070..f1fcaab87 100644
--- a/src/device/device_ledger.hpp
+++ b/src/device/device_ledger.hpp
@@ -169,7 +169,7 @@ namespace hw {
bool sc_secret_add(crypto::secret_key &r, const crypto::secret_key &a, const crypto::secret_key &b) override;
crypto::secret_key generate_keys(crypto::public_key &pub, crypto::secret_key &sec, const crypto::secret_key& recovery_key = crypto::secret_key(), bool recover = false) override;
bool generate_key_derivation(const crypto::public_key &pub, const crypto::secret_key &sec, crypto::key_derivation &derivation) override;
- bool conceal_derivation(crypto::key_derivation &derivation, const crypto::public_key &tx_pub_key, const std::vector<crypto::public_key> &additional_tx_pub_keys, const crypto::key_derivation &main_derivation, const std::vector<crypto::key_derivation> &additional_derivations);
+ bool conceal_derivation(crypto::key_derivation &derivation, const crypto::public_key &tx_pub_key, const std::vector<crypto::public_key> &additional_tx_pub_keys, const crypto::key_derivation &main_derivation, const std::vector<crypto::key_derivation> &additional_derivations) override;
bool derivation_to_scalar(const crypto::key_derivation &derivation, const size_t output_index, crypto::ec_scalar &res) override;
bool derive_secret_key(const crypto::key_derivation &derivation, const std::size_t output_index, const crypto::secret_key &sec, crypto::secret_key &derived_sec) override;
bool derive_public_key(const crypto::key_derivation &derivation, const std::size_t output_index, const crypto::public_key &pub, crypto::public_key &derived_pub) override;
diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h
index 3010b43ad..4606f66ee 100644
--- a/src/p2p/net_node.h
+++ b/src/p2p/net_node.h
@@ -334,8 +334,8 @@ namespace nodetool
cryptonote::network_type m_nettype;
};
- const int64_t default_limit_up = 2048;
- const int64_t default_limit_down = 8192;
+ const int64_t default_limit_up = 2048; // kB/s
+ const int64_t default_limit_down = 8192; // kB/s
extern const command_line::arg_descriptor<std::string> arg_p2p_bind_ip;
extern const command_line::arg_descriptor<std::string, false, true, 2> arg_p2p_bind_port;
extern const command_line::arg_descriptor<uint32_t> arg_p2p_external_port;
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index 3b0d1c394..9b21705ec 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -49,16 +49,9 @@
#include "storages/levin_abstract_invoke2.h"
#include "cryptonote_core/cryptonote_core.h"
-// We have to look for miniupnpc headers in different places, dependent on if its compiled or external
-#ifdef UPNP_STATIC
- #include <miniupnpc/miniupnpc.h>
- #include <miniupnpc/upnpcommands.h>
- #include <miniupnpc/upnperrors.h>
-#else
- #include "miniupnpc.h"
- #include "upnpcommands.h"
- #include "upnperrors.h"
-#endif
+#include <miniupnp/miniupnpc/miniupnpc.h>
+#include <miniupnp/miniupnpc/upnpcommands.h>
+#include <miniupnp/miniupnpc/upnperrors.h>
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "net.p2p"
diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp
index 4ecf62cec..777b4d13a 100644
--- a/src/ringct/rctSigs.cpp
+++ b/src/ringct/rctSigs.cpp
@@ -42,6 +42,8 @@ using namespace std;
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "ringct"
+#define CHECK_AND_ASSERT_MES_L1(expr, ret, message) {if(!(expr)) {MCERROR("verify", message); return ret;}}
+
namespace rct {
bool is_simple(int type)
{
@@ -135,8 +137,8 @@ namespace rct {
bool verifyBorromean(const boroSig &bb, const key64 P1, const key64 P2) {
ge_p3 P1_p3[64], P2_p3[64];
for (size_t i = 0 ; i < 64 ; ++i) {
- CHECK_AND_ASSERT_MES(ge_frombytes_vartime(&P1_p3[i], P1[i].bytes) == 0, false, "point conv failed");
- CHECK_AND_ASSERT_MES(ge_frombytes_vartime(&P2_p3[i], P2[i].bytes) == 0, false, "point conv failed");
+ CHECK_AND_ASSERT_MES_L1(ge_frombytes_vartime(&P1_p3[i], P1[i].bytes) == 0, false, "point conv failed");
+ CHECK_AND_ASSERT_MES_L1(ge_frombytes_vartime(&P2_p3[i], P2[i].bytes) == 0, false, "point conv failed");
}
return verifyBorromean(bb, P1_p3, P2_p3);
}
@@ -356,9 +358,9 @@ namespace rct {
ge_cached cached;
ge_p3 p3;
ge_p1p1 p1;
- CHECK_AND_ASSERT_MES(ge_frombytes_vartime(&p3, H2[i].bytes) == 0, false, "point conv failed");
+ CHECK_AND_ASSERT_MES_L1(ge_frombytes_vartime(&p3, H2[i].bytes) == 0, false, "point conv failed");
ge_p3_to_cached(&cached, &p3);
- CHECK_AND_ASSERT_MES(ge_frombytes_vartime(&asCi[i], as.Ci[i].bytes) == 0, false, "point conv failed");
+ CHECK_AND_ASSERT_MES_L1(ge_frombytes_vartime(&asCi[i], as.Ci[i].bytes) == 0, false, "point conv failed");
ge_sub(&p1, &asCi[i], &cached);
ge_p3_to_cached(&cached, &asCi[i]);
ge_p1p1_to_p3(&CiH[i], &p1);
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index 79792e387..aa105567c 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -47,6 +47,7 @@ using namespace epee;
#include "rpc/rpc_args.h"
#include "core_rpc_server_error_codes.h"
#include "p2p/net_node.h"
+#include "get_output_distribution_cache.h"
#include "version.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
@@ -218,18 +219,6 @@ namespace cryptonote
return ss.str();
}
//------------------------------------------------------------------------------------------------------------------------------
- static cryptonote::blobdata get_pruned_tx_blob(const cryptonote::blobdata &blobdata)
- {
- cryptonote::transaction tx;
-
- if (!cryptonote::parse_and_validate_tx_from_blob(blobdata, tx))
- {
- MERROR("Failed to parse and validate tx from blob");
- return cryptonote::blobdata();
- }
- return get_pruned_tx_blob(tx);
- }
- //------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res)
{
PERF_TIMER(on_get_blocks);
@@ -239,7 +228,7 @@ namespace cryptonote
std::list<std::pair<cryptonote::blobdata, std::list<cryptonote::blobdata> > > bs;
- if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT))
+ if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.start_height, req.prune, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT))
{
res.status = "Failed";
return false;
@@ -271,10 +260,7 @@ namespace cryptonote
for (std::list<cryptonote::blobdata>::iterator i = bd.second.begin(); i != bd.second.end(); ++i)
{
unpruned_size += i->size();
- if (req.prune)
- res.blocks.back().txs.push_back(get_pruned_tx_blob(std::move(*i)));
- else
- res.blocks.back().txs.push_back(std::move(*i));
+ res.blocks.back().txs.push_back(std::move(*i));
i->clear();
i->shrink_to_fit();
pruned_size += res.blocks.back().txs.back().size();
@@ -1233,7 +1219,7 @@ namespace cryptonote
return reward;
}
//------------------------------------------------------------------------------------------------------------------------------
- bool core_rpc_server::fill_block_header_response(const block& blk, bool orphan_status, uint64_t height, const crypto::hash& hash, block_header_response& response)
+ bool core_rpc_server::fill_block_header_response(const block& blk, bool orphan_status, uint64_t height, const crypto::hash& hash, block_header_response& response, bool fill_pow_hash)
{
PERF_TIMER(fill_block_header_response);
response.major_version = blk.major_version;
@@ -1249,6 +1235,7 @@ namespace cryptonote
response.reward = get_block_reward(blk);
response.block_size = m_core.get_blockchain_storage().get_db().get_block_size(height);
response.num_txes = blk.tx_hashes.size();
+ response.pow_hash = fill_pow_hash ? string_tools::pod_to_hex(get_block_longhash(blk, height)) : "";
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -1338,7 +1325,7 @@ namespace cryptonote
error_resp.message = "Internal error: can't get last block.";
return false;
}
- bool response_filled = fill_block_header_response(last_block, false, last_block_height, last_block_hash, res.block_header);
+ bool response_filled = fill_block_header_response(last_block, false, last_block_height, last_block_hash, res.block_header, req.fill_pow_hash);
if (!response_filled)
{
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
@@ -1379,7 +1366,7 @@ namespace cryptonote
return false;
}
uint64_t block_height = boost::get<txin_gen>(blk.miner_tx.vin.front()).height;
- bool response_filled = fill_block_header_response(blk, orphan, block_height, block_hash, res.block_header);
+ bool response_filled = fill_block_header_response(blk, orphan, block_height, block_hash, res.block_header, req.fill_pow_hash);
if (!response_filled)
{
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
@@ -1428,7 +1415,7 @@ namespace cryptonote
return false;
}
res.headers.push_back(block_header_response());
- bool response_filled = fill_block_header_response(blk, false, block_height, block_hash, res.headers.back());
+ bool response_filled = fill_block_header_response(blk, false, block_height, block_hash, res.headers.back(), req.fill_pow_hash);
if (!response_filled)
{
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
@@ -1461,7 +1448,7 @@ namespace cryptonote
error_resp.message = "Internal error: can't get block by height. Height = " + std::to_string(req.height) + '.';
return false;
}
- bool response_filled = fill_block_header_response(blk, false, req.height, block_hash, res.block_header);
+ bool response_filled = fill_block_header_response(blk, false, req.height, block_hash, res.block_header, req.fill_pow_hash);
if (!response_filled)
{
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
@@ -1515,7 +1502,7 @@ namespace cryptonote
return false;
}
uint64_t block_height = boost::get<txin_gen>(blk.miner_tx.vin.front()).height;
- bool response_filled = fill_block_header_response(blk, orphan, block_height, block_hash, res.block_header);
+ bool response_filled = fill_block_header_response(blk, orphan, block_height, block_hash, res.block_header, req.fill_pow_hash);
if (!response_filled)
{
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
@@ -1728,7 +1715,7 @@ namespace cryptonote
std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> histogram;
try
{
- histogram = m_core.get_blockchain_storage().get_output_histogram(req.amounts, req.unlocked, req.recent_cutoff);
+ histogram = m_core.get_blockchain_storage().get_output_histogram(req.amounts, req.unlocked, req.recent_cutoff, req.min_count);
}
catch (const std::exception &e)
{
@@ -2082,24 +2069,79 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
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)
{
+ PERF_TIMER(on_get_output_distribution);
try
{
for (uint64_t amount: req.amounts)
{
+ static struct D
+ {
+ boost::mutex mutex;
+ std::vector<uint64_t> cached_distribution;
+ uint64_t cached_from, cached_to, cached_start_height, cached_base;
+ bool cached;
+ D(): cached_from(0), cached_to(0), cached_start_height(0), cached_base(0), cached(false) {}
+ } d;
+ boost::unique_lock<boost::mutex> lock(d.mutex);
+
+ if (d.cached && amount == 0 && d.cached_from == req.from_height && d.cached_to == req.to_height)
+ {
+ res.distributions.push_back({amount, d.cached_start_height, d.cached_distribution, d.cached_base});
+ continue;
+ }
+
+ // this is a slow operation, so we have precomputed caches of common cases
+ bool found = false;
+ for (const auto &slot: get_output_distribution_cache)
+ {
+ if (slot.amount == amount && slot.from_height == req.from_height && slot.to_height == req.to_height)
+ {
+ res.distributions.push_back({amount, slot.start_height, slot.distribution, slot.base});
+ found = true;
+ if (req.cumulative)
+ {
+ auto &distribution = res.distributions.back().distribution;
+ distribution[0] += slot.base;
+ for (size_t n = 1; n < distribution.size(); ++n)
+ distribution[n] += distribution[n-1];
+ }
+ break;
+ }
+ }
+ if (found)
+ continue;
+
std::vector<uint64_t> distribution;
uint64_t start_height, base;
- if (!m_core.get_output_distribution(amount, req.from_height, start_height, distribution, base))
+ if (!m_core.get_output_distribution(amount, req.from_height, req.to_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.to_height > 0 && req.to_height >= req.from_height)
+ {
+ uint64_t offset = std::max(req.from_height, start_height);
+ if (offset <= req.to_height && req.to_height - offset + 1 < distribution.size())
+ distribution.resize(req.to_height - offset + 1);
+ }
if (req.cumulative)
{
distribution[0] += base;
for (size_t n = 1; n < distribution.size(); ++n)
distribution[n] += distribution[n-1];
}
+
+ if (amount == 0)
+ {
+ d.cached_from = req.from_height;
+ d.cached_to = req.to_height;
+ d.cached_distribution = distribution;
+ d.cached_start_height = start_height;
+ d.cached_base = base;
+ d.cached = true;
+ }
+
res.distributions.push_back({amount, start_height, std::move(distribution), base});
}
}
diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h
index 86e41e047..324f219f8 100644
--- a/src/rpc/core_rpc_server.h
+++ b/src/rpc/core_rpc_server.h
@@ -224,7 +224,7 @@ private:
//utils
uint64_t get_block_reward(const block& blk);
- bool fill_block_header_response(const block& blk, bool orphan_status, uint64_t height, const crypto::hash& hash, block_header_response& response);
+ bool fill_block_header_response(const block& blk, bool orphan_status, uint64_t height, const crypto::hash& hash, block_header_response& response, bool fill_pow_hash);
enum invoke_http_mode { JON, BIN, JON_RPC };
template <typename COMMAND_TYPE>
bool use_bootstrap_daemon_if_necessary(const invoke_http_mode &mode, const std::string &command_name, const typename COMMAND_TYPE::request& req, typename COMMAND_TYPE::response& res, bool &r);
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index df5b4893f..70e186848 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 19
+#define CORE_RPC_VERSION_MINOR 20
#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)
@@ -1165,6 +1165,7 @@ namespace cryptonote
uint64_t reward;
uint64_t block_size;
uint64_t num_txes;
+ std::string pow_hash;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(major_version)
@@ -1180,6 +1181,7 @@ namespace cryptonote
KV_SERIALIZE(reward)
KV_SERIALIZE(block_size)
KV_SERIALIZE(num_txes)
+ KV_SERIALIZE(pow_hash)
END_KV_SERIALIZE_MAP()
};
@@ -1187,7 +1189,10 @@ namespace cryptonote
{
struct request
{
+ bool fill_pow_hash;
+
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_OPT(fill_pow_hash, false);
END_KV_SERIALIZE_MAP()
};
@@ -1211,9 +1216,11 @@ namespace cryptonote
struct request
{
std::string hash;
+ bool fill_pow_hash;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(hash)
+ KV_SERIALIZE_OPT(fill_pow_hash, false);
END_KV_SERIALIZE_MAP()
};
@@ -1237,9 +1244,11 @@ namespace cryptonote
struct request
{
uint64_t height;
+ bool fill_pow_hash;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(height)
+ KV_SERIALIZE_OPT(fill_pow_hash, false);
END_KV_SERIALIZE_MAP()
};
@@ -1264,10 +1273,12 @@ namespace cryptonote
{
std::string hash;
uint64_t height;
+ bool fill_pow_hash;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(hash)
KV_SERIALIZE(height)
+ KV_SERIALIZE_OPT(fill_pow_hash, false);
END_KV_SERIALIZE_MAP()
};
@@ -1620,10 +1631,12 @@ namespace cryptonote
{
uint64_t start_height;
uint64_t end_height;
+ bool fill_pow_hash;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(start_height)
KV_SERIALIZE(end_height)
+ KV_SERIALIZE_OPT(fill_pow_hash, false);
END_KV_SERIALIZE_MAP()
};
@@ -1705,7 +1718,7 @@ namespace cryptonote
{
struct request
{
- int64_t limit_down;
+ int64_t limit_down; // all limits (for get and set) are kB/s
int64_t limit_up;
BEGIN_KV_SERIALIZE_MAP()
@@ -2212,11 +2225,13 @@ namespace cryptonote
{
std::vector<uint64_t> amounts;
uint64_t from_height;
+ uint64_t to_height;
bool cumulative;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amounts)
KV_SERIALIZE_OPT(from_height, (uint64_t)0)
+ KV_SERIALIZE_OPT(to_height, (uint64_t)0)
KV_SERIALIZE_OPT(cumulative, false)
END_KV_SERIALIZE_MAP()
};
diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp
index 29020aa57..39f169cdf 100644
--- a/src/rpc/daemon_handler.cpp
+++ b/src/rpc/daemon_handler.cpp
@@ -52,7 +52,7 @@ namespace rpc
{
std::list<std::pair<blobdata, std::list<blobdata> > > blocks;
- if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, blocks, res.current_height, res.start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT))
+ if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, blocks, res.current_height, res.start_height, req.prune, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT))
{
res.status = Message::STATUS_FAILED;
res.error_details = "core::find_blockchain_supplement() returned false";
diff --git a/src/rpc/get_output_distribution_cache.h b/src/rpc/get_output_distribution_cache.h
new file mode 100644
index 000000000..6495e7d4c
--- /dev/null
+++ b/src/rpc/get_output_distribution_cache.h
@@ -0,0 +1,113 @@
+static const struct
+{
+ uint64_t amount;
+ uint64_t from_height;
+ uint64_t to_height;
+ uint64_t start_height;
+ uint64_t base;
+ std::vector<uint64_t> distribution;
+}
+get_output_distribution_cache[] =
+{
+ {
+ 0,
+ 1544704,
+ 1546001,
+ 1544704,
+ 5143500,
+ {
+ 5, 38, 37, 33, 39, 7, 1, 1, 5, 9, 7, 5, 17, 5, 3, 9, 3, 17, 5, 17, 1, 1, 15, 13, 3, 10, 5, 3, 34, 1, 45, 7,
+ 5, 17, 5, 22, 3, 1, 17, 16, 5, 1, 3, 43, 5, 13, 3, 23, 9, 7, 9, 13, 1, 11, 1, 17, 1, 3, 16, 11, 5, 11, 7, 7,
+ 33, 11, 7, 1, 5, 1, 21, 19, 1, 17, 1, 49, 17, 3, 3, 9, 35, 46, 46, 39, 26, 33, 21, 3, 23, 3, 9, 37, 1, 33, 11, 32,
+ 1, 13, 16, 12, 3, 21, 1, 18, 3, 19, 1, 25, 5, 3, 18, 7, 17, 5, 9, 15, 7, 7, 11, 9, 9, 17, 5, 16, 1, 3, 13, 3,
+ 5, 5, 5, 13, 5, 9, 5, 13, 3, 17, 15, 36, 13, 3, 20, 12, 6, 23, 17, 10, 22, 23, 1, 7, 21, 6, 23, 1, 3, 19, 13, 1,
+ 3, 43, 35, 13, 1, 31, 7, 3, 17, 1, 15, 5, 11, 15, 24, 1, 18, 13, 5, 15, 1, 29, 3, 3, 13, 3, 15, 7, 17, 3, 1, 1,
+ 17, 1, 1, 45, 39, 27, 45, 46, 34, 7, 3, 3, 9, 3, 3, 11, 7, 5, 9, 25, 19, 3, 33, 1, 5, 17, 1, 45, 4, 1, 45, 11,
+ 44, 32, 3, 1, 3, 7, 17, 15, 5, 45, 35, 41, 1, 35, 3, 3, 19, 1, 9, 17, 29, 29, 3, 1, 13, 1, 3, 47, 21, 13, 7, 1,
+ 7, 5, 1, 11, 1, 40, 9, 7, 3, 3, 13, 25, 1, 47, 5, 7, 3, 7, 31, 40, 34, 6, 3, 15, 3, 31, 5, 13, 27, 9, 12, 21,
+ 3, 1, 19, 1, 19, 5, 47, 49, 47, 42, 50, 34, 29, 23, 1, 5, 9, 16, 11, 7, 1, 19, 7, 5, 1, 15, 1, 1, 9, 13, 9, 5,
+ 27, 3, 3, 29, 1, 33, 3, 9, 5, 35, 5, 1, 17, 7, 3, 39, 3, 28, 19, 1, 1, 9, 1, 3, 27, 1, 37, 3, 1, 1, 16, 3,
+ 25, 11, 5, 3, 33, 45, 17, 11, 7, 22, 9, 1, 5, 5, 5, 15, 1, 15, 9, 7, 11, 13, 37, 49, 46, 38, 11, 1, 25, 1, 13, 18,
+ 3, 7, 39, 3, 37, 19, 35, 3, 1, 3, 19, 1, 3, 15, 21, 3, 27, 1, 45, 48, 1, 13, 29, 9, 1, 1, 46, 43, 5, 15, 3, 7,
+ 29, 26, 5, 5, 21, 37, 17, 21, 3, 13, 1, 5, 1, 17, 5, 31, 13, 1, 11, 3, 46, 9, 3, 7, 1, 1, 41, 1, 21, 1, 5, 12,
+ 7, 13, 9, 25, 1, 47, 47, 48, 48, 48, 48, 48, 47, 48, 45, 45, 33, 52, 50, 46, 45, 47, 35, 41, 38, 35, 42, 38, 34, 41, 39, 35,
+ 51, 51, 45, 43, 49, 52, 53, 45, 42, 46, 37, 53, 49, 41, 46, 49, 46, 47, 48, 37, 41, 33, 43, 38, 15, 3, 3, 27, 11, 5, 23, 13,
+ 1, 1, 37, 3, 15, 3, 30, 13, 3, 45, 12, 3, 5, 11, 1, 1, 21, 9, 11, 19, 1, 1, 1, 25, 5, 21, 3, 1, 32, 44, 3, 33,
+ 11, 7, 5, 23, 1, 37, 47, 48, 48, 48, 48, 48, 48, 46, 47, 47, 50, 45, 49, 50, 46, 47, 49, 45, 51, 49, 50, 49, 49, 46, 47, 48,
+ 46, 48, 46, 50, 46, 43, 46, 46, 48, 47, 46, 47, 45, 49, 46, 43, 50, 45, 45, 49, 45, 48, 45, 49, 48, 45, 45, 51, 45, 51, 45, 46,
+ 52, 45, 45, 51, 51, 52, 44, 45, 52, 50, 50, 46, 47, 51, 51, 46, 47, 47, 47, 50, 47, 51, 48, 49, 51, 50, 48, 48, 48, 50, 49, 49,
+ 52, 52, 49, 50, 49, 49, 49, 51, 52, 49, 52, 50, 49, 47, 29, 15, 39, 17, 31, 5, 40, 5, 18, 23, 25, 7, 35, 26, 5, 31, 49, 22,
+ 3, 17, 7, 49, 7, 49, 47, 12, 44, 46, 36, 15, 3, 1, 47, 13, 35, 40, 5, 21, 19, 39, 21, 33, 31, 29, 1, 1, 37, 1, 15, 47,
+ 7, 7, 47, 41, 13, 3, 47, 31, 9, 33, 13, 43, 29, 5, 1, 9, 33, 7, 27, 15, 15, 25, 5, 43, 22, 31, 7, 1, 47, 1, 15, 27,
+ 3, 27, 45, 15, 1, 36, 17, 1, 23, 39, 38, 45, 7, 7, 19, 7, 11, 47, 33, 16, 3, 45, 45, 45, 9, 27, 3, 3, 21, 3, 7, 21,
+ 7, 3, 43, 1, 17, 1, 45, 37, 46, 5, 5, 13, 46, 40, 48, 48, 45, 34, 1, 46, 19, 25, 9, 7, 47, 23, 37, 31, 3, 25, 13, 46,
+ 31, 25, 5, 46, 35, 52, 11, 23, 27, 4, 15, 11, 11, 11, 9, 34, 7, 9, 15, 34, 9, 27, 37, 28, 25, 45, 13, 30, 5, 25, 15, 7,
+ 3, 19, 27, 1, 7, 11, 1, 32, 3, 45, 11, 9, 21, 25, 9, 13, 13, 1, 7, 1, 33, 11, 5, 3, 3, 27, 27, 5, 3, 37, 17, 17,
+ 3, 7, 5, 13, 1, 3, 44, 45, 26, 25, 1, 13, 3, 13, 3, 11, 1, 11, 7, 45, 3, 3, 1, 43, 1, 19, 3, 1, 15, 5, 39, 7,
+ 7, 1, 9, 1, 11, 19, 3, 35, 29, 7, 15, 11, 40, 7, 44, 38, 34, 7, 9, 7, 1, 27, 1, 9, 5, 45, 1, 21, 3, 1, 5, 9,
+ 3, 21, 23, 33, 3, 1, 7, 3, 3, 7, 41, 9, 7, 1, 5, 31, 9, 7, 1, 1, 11, 41, 51, 20, 9, 47, 39, 17, 9, 35, 1, 41,
+ 1, 19, 1, 19, 15, 1, 13, 5, 23, 15, 9, 15, 17, 1, 15, 27, 33, 31, 29, 7, 13, 1, 5, 45, 5, 1, 1, 11, 1, 13, 3, 7,
+ 9, 1, 13, 39, 3, 33, 5, 3, 7, 7, 5, 29, 11, 1, 7, 1, 15, 3, 13, 3, 15, 3, 3, 1, 5, 1, 9, 1, 44, 49, 24, 25,
+ 1, 1, 34, 22, 7, 5, 5, 5, 10, 9, 13, 3, 9, 1, 9, 19, 7, 43, 48, 7, 11, 7, 3, 3, 7, 21, 1, 1, 3, 3, 11, 31,
+ 1, 1, 13, 22, 23, 7, 27, 9, 3, 3, 21, 1, 35, 21, 9, 11, 13, 39, 1, 3, 7, 23, 3, 28, 3, 45, 47, 38, 32, 37, 34, 1,
+ 23, 3, 3, 1, 19, 19, 1, 5, 13, 1, 5, 11, 38, 3, 1, 36, 13, 1, 1, 23, 5, 17, 11, 1, 13, 1, 3, 7, 11, 3, 33, 7,
+ 19, 5, 5, 1, 1, 3, 5, 41, 1, 3, 25, 1, 7, 7, 9, 3, 11, 3, 13, 5, 7, 1, 3, 9, 1, 1, 43, 47, 47, 47, 17, 7,
+ 17, 3, 19, 1, 9, 9, 33, 22, 1, 25, 1, 3, 3, 32, 5, 1, 23, 9, 5, 1, 31, 5, 9, 1, 3, 7, 19, 1, 12, 11, 5, 1,
+ 1, 9, 25, 15, 15, 13, 5, 3, 15, 1, 17, 1, 1, 5, 5, 41, 11, 15, 7, 3, 21, 21, 35, 22, 46, 35, 3, 27, 5, 3, 45, 22,
+ 27, 1, 19, 9, 1, 25, 1, 29, 3, 5, 25, 17, 27, 5, 19, 5, 25, 7, 19, 1, 9, 21, 3, 7, 29, 27, 17, 3, 3, 15, 7, 19,
+ 5, 25, 1, 23, 45, 4, 31, 1, 37, 14, 29, 3, 29, 1, 23, 29, 19, 11, 1, 13, 13, 9, 1, 25, 1, 33, 1, 37, 37, 23, 7, 21,
+ 7, 3, 13, 7, 3, 7, 21, 11, 9, 1, 31, 3, 1, 7, 39, 46, 3, 30,
+ },
+ },
+ {
+ 0,
+ 1562704,
+ 1564001,
+ 1562704,
+ 5521986,
+ {
+ 35, 45, 23, 3, 44, 47, 44, 3, 17, 35, 7, 11, 7, 29, 43, 27, 11, 7, 5, 31, 13, 9, 45, 45, 7, 42, 17, 15, 19, 11, 45, 19,
+ 45, 46, 45, 46, 32, 34, 43, 34, 46, 47, 45, 30, 17, 45, 46, 36, 35, 38, 19, 9, 23, 17, 3, 19, 31, 41, 35, 24, 15, 45, 15, 5,
+ 11, 5, 19, 11, 11, 7, 15, 19, 45, 34, 7, 7, 29, 1, 9, 36, 7, 44, 45, 33, 25, 8, 17, 7, 44, 43, 48, 45, 42, 46, 40, 44,
+ 1, 43, 45, 46, 46, 35, 19, 19, 23, 5, 13, 19, 7, 16, 9, 3, 25, 34, 3, 27, 9, 39, 3, 43, 21, 1, 45, 45, 39, 25, 23, 13,
+ 39, 39, 3, 45, 43, 46, 44, 40, 39, 33, 45, 47, 38, 45, 45, 39, 47, 47, 45, 46, 35, 45, 43, 47, 45, 40, 34, 42, 42, 48, 49, 47,
+ 47, 48, 47, 45, 43, 48, 37, 48, 41, 45, 48, 34, 42, 44, 9, 19, 27, 1, 47, 47, 43, 25, 29, 5, 5, 21, 39, 35, 43, 37, 13, 45,
+ 25, 31, 26, 47, 45, 23, 23, 39, 32, 25, 44, 47, 35, 47, 15, 17, 7, 9, 5, 35, 31, 3, 45, 47, 46, 13, 17, 48, 45, 9, 13, 45,
+ 45, 31, 1, 53, 44, 46, 39, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 47, 47, 47, 47, 47, 47, 47, 47, 53, 49, 45, 45, 50,
+ 50, 27, 43, 46, 47, 46, 45, 48, 36, 42, 42, 46, 45, 47, 41, 45, 43, 47, 38, 48, 47, 33, 11, 45, 46, 34, 42, 32, 3, 45, 37, 45,
+ 15, 3, 45, 29, 31, 9, 5, 3, 27, 5, 21, 25, 7, 15, 46, 34, 5, 3, 17, 3, 9, 13, 7, 11, 3, 1, 34, 13, 7, 45, 33, 26,
+ 9, 5, 9, 41, 43, 34, 3, 35, 3, 17, 37, 11, 17, 3, 15, 27, 15, 45, 46, 13, 26, 16, 11, 7, 5, 45, 38, 45, 45, 22, 44, 44,
+ 43, 6, 11, 35, 15, 44, 17, 27, 13, 3, 40, 5, 9, 7, 35, 19, 5, 5, 1, 28, 33, 15, 45, 5, 29, 3, 31, 12, 5, 32, 24, 3,
+ 23, 13, 47, 45, 42, 46, 39, 21, 21, 1, 44, 44, 47, 41, 5, 1, 11, 36, 20, 5, 45, 39, 45, 45, 44, 45, 32, 22, 40, 11, 38, 1,
+ 45, 46, 37, 9, 23, 9, 15, 44, 7, 16, 38, 46, 11, 14, 24, 19, 19, 7, 26, 25, 45, 37, 17, 1, 35, 1, 3, 28, 3, 11, 22, 13,
+ 3, 1, 7, 38, 5, 3, 1, 26, 1, 3, 43, 44, 46, 45, 21, 11, 1, 44, 27, 1, 11, 26, 10, 44, 45, 45, 45, 47, 47, 45, 48, 45,
+ 38, 9, 5, 44, 46, 27, 3, 12, 1, 11, 3, 44, 43, 1, 5, 2, 46, 17, 13, 19, 1, 12, 7, 23, 1, 17, 6, 13, 3, 5, 27, 7,
+ 46, 36, 19, 25, 1, 1, 3, 8, 15, 3, 45, 45, 45, 37, 6, 15, 3, 5, 38, 14, 41, 1, 13, 45, 45, 39, 44, 29, 43, 48, 51, 50,
+ 37, 5, 17, 46, 47, 31, 5, 42, 49, 38, 39, 24, 7, 11, 44, 35, 21, 6, 15, 5, 47, 13, 28, 45, 34, 27, 24, 15, 35, 13, 7, 25,
+ 43, 13, 14, 5, 3, 5, 46, 45, 45, 35, 5, 21, 28, 3, 13, 4, 30, 19, 45, 45, 40, 37, 5, 40, 17, 9, 3, 9, 13, 4, 17, 33,
+ 44, 39, 17, 45, 28, 5, 11, 45, 19, 45, 21, 44, 31, 43, 49, 48, 15, 3, 1, 44, 45, 43, 11, 1, 1, 27, 43, 45, 39, 3, 1, 3,
+ 5, 31, 1, 43, 23, 19, 7, 5, 45, 31, 11, 7, 3, 9, 5, 7, 13, 43, 47, 45, 46, 47, 14, 3, 25, 45, 7, 17, 32, 21, 3, 17,
+ 5, 11, 31, 40, 45, 20, 45, 45, 32, 38, 47, 38, 45, 41, 49, 30, 45, 5, 36, 31, 22, 3, 46, 45, 13, 21, 23, 5, 46, 45, 33, 19,
+ 25, 1, 7, 13, 5, 44, 23, 29, 23, 24, 7, 5, 37, 13, 29, 13, 7, 17, 7, 43, 3, 21, 7, 44, 1, 19, 15, 9, 34, 43, 26, 3,
+ 17, 5, 6, 5, 7, 3, 33, 35, 43, 41, 48, 47, 30, 45, 19, 7, 5, 33, 11, 34, 25, 1, 21, 11, 29, 7, 1, 4, 5, 10, 14, 3,
+ 44, 11, 47, 45, 33, 3, 9, 7, 40, 23, 9, 1, 3, 1, 7, 5, 9, 9, 6, 11, 45, 41, 45, 19, 5, 11, 10, 39, 9, 19, 3, 11,
+ 43, 42, 1, 13, 35, 5, 32, 7, 5, 5, 43, 37, 3, 32, 17, 3, 23, 1, 13, 45, 17, 1, 21, 45, 43, 46, 49, 47, 45, 30, 9, 31,
+ 19, 42, 19, 44, 17, 14, 19, 25, 1, 7, 5, 45, 19, 13, 7, 3, 1, 1, 9, 21, 37, 9, 11, 1, 3, 37, 27, 13, 5, 21, 33, 3,
+ 27, 9, 27, 1, 39, 1, 46, 21, 10, 13, 21, 40, 22, 35, 41, 9, 33, 3, 17, 8, 45, 46, 42, 46, 47, 47, 47, 48, 48, 47, 43, 48,
+ 37, 39, 50, 35, 3, 40, 45, 40, 46, 36, 34, 28, 9, 9, 37, 9, 5, 7, 13, 31, 1, 7, 3, 3, 44, 45, 25, 15, 1, 21, 43, 25,
+ 1, 38, 34, 42, 31, 23, 33, 35, 37, 20, 7, 15, 3, 7, 7, 27, 45, 45, 48, 47, 45, 44, 47, 23, 25, 36, 11, 3, 18, 24, 27, 13,
+ 41, 13, 5, 5, 7, 19, 15, 7, 5, 14, 45, 45, 37, 1, 5, 17, 21, 41, 17, 37, 53, 41, 45, 45, 45, 45, 45, 45, 45, 45, 43, 47,
+ 47, 48, 53, 47, 47, 47, 49, 27, 45, 47, 47, 47, 47, 45, 45, 45, 47, 43, 48, 34, 34, 43, 46, 15, 37, 21, 5, 27, 11, 1, 9, 7,
+ 19, 15, 1, 1, 19, 3, 36, 27, 29, 13, 21, 9, 17, 5, 16, 45, 23, 34, 3, 1, 7, 25, 28, 13, 29, 15, 11, 19, 17, 1, 27, 23,
+ 31, 19, 41, 41, 40, 47, 28, 31, 26, 26, 36, 17, 5, 1, 23, 1, 45, 34, 49, 51, 34, 43, 37, 5, 41, 15, 5, 21, 1, 7, 9, 19,
+ 5, 11, 39, 19, 45, 45, 38, 17, 9, 1, 15, 11, 5, 13, 47, 46, 48, 45, 19, 32, 7, 19, 5, 7, 23, 29, 5, 45, 41, 37, 1, 5,
+ 27, 5, 5, 7, 19, 9, 1, 35, 48, 38, 38, 39, 42, 43, 21, 23, 43, 41, 7, 3, 7, 13, 1, 46, 47, 46, 23, 46, 45, 25, 7, 9,
+ 21, 7, 41, 13, 20, 1, 21, 15, 37, 5, 40, 45, 45, 5, 45, 46, 15, 33, 46, 12, 13, 7, 24, 7, 5, 30, 7, 46, 13, 8, 44, 35,
+ 45, 33, 40, 36, 47, 47, 29, 43, 36, 43, 45, 42, 36, 19, 7, 7, 43, 3, 44, 25, 48, 29, 11, 45, 30, 1, 17, 13, 25, 1, 48, 45,
+ 45, 45, 44, 49, 37, 9, 21, 17, 15, 7, 15, 25, 1, 1, 9, 43, 33, 11, 3, 29, 45, 45, 9, 7, 7, 27, 47, 45, 47, 48, 45, 47,
+ 26, 1, 43, 15, 45, 17, 1, 5, 35, 31, 9, 3, 9, 19, 9, 21, 43, 5, 27, 1, 5, 9, 4, 34, 19, 3, 7, 11, 45, 46, 45, 45,
+ 46, 47, 47, 44, 45, 43, 27, 9, 17, 15, 19, 44, 45, 46, 47, 47, 45, 45,
+ }
+ }
+};
+
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index 33615ffe2..a45d444f1 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -62,6 +62,7 @@
#include "ringct/rctSigs.h"
#include "multisig/multisig.h"
#include "wallet/wallet_args.h"
+#include "version.h"
#include <stdexcept>
#ifdef WIN32
@@ -130,6 +131,7 @@ namespace
const command_line::arg_descriptor<bool> arg_restore_multisig_wallet = {"restore-multisig-wallet", sw::tr("Recover multisig wallet using Electrum-style mnemonic seed"), false};
const command_line::arg_descriptor<bool> arg_non_deterministic = {"non-deterministic", sw::tr("Generate non-deterministic view and spend keys"), false};
const command_line::arg_descriptor<bool> arg_trusted_daemon = {"trusted-daemon", sw::tr("Enable commands which rely on a trusted daemon"), false};
+ const command_line::arg_descriptor<bool> arg_untrusted_daemon = {"untrusted-daemon", sw::tr("Disable commands which rely on a trusted daemon"), false};
const command_line::arg_descriptor<bool> arg_allow_mismatched_daemon_version = {"allow-mismatched-daemon-version", sw::tr("Allow communicating with a daemon that uses a different RPC version"), false};
const command_line::arg_descriptor<uint64_t> arg_restore_height = {"restore-height", sw::tr("Restore from specific blockchain height"), 0};
const command_line::arg_descriptor<bool> arg_do_not_relay = {"do-not-relay", sw::tr("The newly created transaction will not be relayed to the monero network"), false};
@@ -758,7 +760,7 @@ bool simple_wallet::print_fee_info(const std::vector<std::string> &args/* = std:
}
const uint64_t per_kb_fee = m_wallet->get_per_kb_fee();
const uint64_t typical_size_kb = 13;
- message_writer() << (boost::format(tr("Current fee is %s monero per kB")) % print_money(per_kb_fee)).str();
+ message_writer() << (boost::format(tr("Current fee is %s %s per kB")) % print_money(per_kb_fee) % cryptonote::get_unit(cryptonote::get_default_decimal_point())).str();
std::vector<uint64_t> fees;
for (uint32_t priority = 1; priority <= 4; ++priority)
@@ -1076,7 +1078,7 @@ bool simple_wallet::import_multisig(const std::vector<std::string> &args)
fail_msg_writer() << tr("Failed to import multisig info: ") << e.what();
return true;
}
- if (m_trusted_daemon)
+ if (is_daemon_trusted())
{
try
{
@@ -1228,7 +1230,7 @@ bool simple_wallet::submit_multisig(const std::vector<std::string> &args)
}
catch (const std::exception &e)
{
- handle_transfer_exception(std::current_exception(), m_trusted_daemon);
+ handle_transfer_exception(std::current_exception(), is_daemon_trusted());
}
catch (...)
{
@@ -1584,6 +1586,12 @@ bool simple_wallet::save_known_rings(const std::vector<std::string> &args)
return true;
}
+bool simple_wallet::version(const std::vector<std::string> &args)
+{
+ message_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")";
+ return true;
+}
+
bool simple_wallet::set_always_confirm_transfers(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
const auto pwd_container = get_and_verify_password();
@@ -2074,8 +2082,8 @@ simple_wallet::simple_wallet()
tr("Donate <amount> to the development team (donate.getmonero.org)."));
m_cmd_binder.set_handler("sign_transfer",
boost::bind(&simple_wallet::sign_transfer, this, _1),
- tr("sign_transfer <file>"),
- tr("Sign a transaction from a <file>."));
+ tr("sign_transfer [export]"),
+ tr("Sign a transaction from a file."));
m_cmd_binder.set_handler("submit_transfer",
boost::bind(&simple_wallet::submit_transfer, this, _1),
tr("Submit a signed transaction from a file."));
@@ -2335,6 +2343,10 @@ simple_wallet::simple_wallet()
boost::bind(&simple_wallet::blackballed, this, _1),
tr("blackballed <output public key>"),
tr("Checks whether an output is blackballed"));
+ m_cmd_binder.set_handler("version",
+ boost::bind(&simple_wallet::version, this, _1),
+ tr("version"),
+ tr("Returns version information"));
m_cmd_binder.set_handler("help",
boost::bind(&simple_wallet::help, this, _1),
tr("help [<command>]"),
@@ -3106,18 +3118,21 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
return false;
}
- // set --trusted-daemon if local
- try
+ // set --trusted-daemon if local and not overridden
+ if (!m_trusted_daemon)
{
- if (tools::is_local_address(m_wallet->get_daemon_address()))
+ try
{
- MINFO(tr("Daemon is local, assuming trusted"));
- m_trusted_daemon = true;
+ if (tools::is_local_address(m_wallet->get_daemon_address()))
+ {
+ MINFO(tr("Daemon is local, assuming trusted"));
+ m_trusted_daemon = true;
+ }
}
+ catch (const std::exception &e) { }
}
- catch (const std::exception &e) { }
- if (!m_trusted_daemon)
+ if (!is_daemon_trusted())
message_writer() << (boost::format(tr("Warning: using an untrusted daemon at %s, privacy will be lessened")) % m_wallet->get_daemon_address()).str();
if (m_wallet->get_ring_database().empty())
@@ -3151,7 +3166,10 @@ bool simple_wallet::handle_command_line(const boost::program_options::variables_
m_restore_deterministic_wallet = command_line::get_arg(vm, arg_restore_deterministic_wallet);
m_restore_multisig_wallet = command_line::get_arg(vm, arg_restore_multisig_wallet);
m_non_deterministic = command_line::get_arg(vm, arg_non_deterministic);
- m_trusted_daemon = command_line::get_arg(vm, arg_trusted_daemon);
+ if (!command_line::is_arg_defaulted(vm, arg_trusted_daemon) || !command_line::is_arg_defaulted(vm, arg_untrusted_daemon))
+ m_trusted_daemon = command_line::get_arg(vm, arg_trusted_daemon) && !command_line::get_arg(vm, arg_untrusted_daemon);
+ if (!command_line::is_arg_defaulted(vm, arg_trusted_daemon) && !command_line::is_arg_defaulted(vm, arg_untrusted_daemon))
+ message_writer() << tr("--trusted-daemon and --untrusted-daemon are both seen, assuming untrusted");
m_allow_mismatched_daemon_version = command_line::get_arg(vm, arg_allow_mismatched_daemon_version);
m_restore_height = command_line::get_arg(vm, arg_restore_height);
m_do_not_relay = command_line::get_arg(vm, arg_do_not_relay);
@@ -3478,6 +3496,17 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm)
fail_msg_writer() << tr("wallet file path not valid: ") << m_wallet_file;
return false;
}
+
+ bool keys_file_exists;
+ bool wallet_file_exists;
+
+ tools::wallet2::wallet_exists(m_wallet_file, keys_file_exists, wallet_file_exists);
+ if(!keys_file_exists)
+ {
+ fail_msg_writer() << tr("Key file not found. Failed to open wallet");
+ return false;
+ }
+
epee::wipeable_string password;
try
{
@@ -3638,7 +3667,7 @@ bool simple_wallet::save_watch_only(const std::vector<std::string> &args/* = std
//----------------------------------------------------------------------------------------------------
bool simple_wallet::start_mining(const std::vector<std::string>& args)
{
- if (!m_trusted_daemon)
+ if (!is_daemon_trusted())
{
fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon");
return true;
@@ -3819,7 +3848,7 @@ void simple_wallet::on_skip_transaction(uint64_t height, const crypto::hash &txi
//----------------------------------------------------------------------------------------------------
bool simple_wallet::refresh_main(uint64_t start_height, bool reset, bool is_init)
{
- if (!try_connect_to_daemon())
+ if (!try_connect_to_daemon(is_init))
return true;
LOCK_IDLE_SCOPE();
@@ -4134,7 +4163,7 @@ bool simple_wallet::show_blockchain_height(const std::vector<std::string>& args)
//----------------------------------------------------------------------------------------------------
bool simple_wallet::rescan_spent(const std::vector<std::string> &args)
{
- if (!m_trusted_daemon)
+ if (!is_daemon_trusted())
{
fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon");
return true;
@@ -4480,16 +4509,16 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
return true;
}
unlock_block = bc_height + locked_blocks;
- ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, m_trusted_daemon);
+ ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, is_daemon_trusted());
break;
case TransferNew:
- ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, m_trusted_daemon);
+ ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, is_daemon_trusted());
break;
default:
LOG_ERROR("Unknown transfer method, using original");
/* FALLTHRU */
case TransferOriginal:
- ptx_vector = m_wallet->create_transactions(dsts, fake_outs_count, 0 /* unlock_time */, priority, extra, m_trusted_daemon);
+ ptx_vector = m_wallet->create_transactions(dsts, fake_outs_count, 0 /* unlock_time */, priority, extra, is_daemon_trusted());
break;
}
@@ -4665,7 +4694,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
}
catch (const std::exception &e)
{
- handle_transfer_exception(std::current_exception(), m_trusted_daemon);
+ handle_transfer_exception(std::current_exception(), is_daemon_trusted());
}
catch (...)
{
@@ -4702,7 +4731,7 @@ bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_)
try
{
// figure out what tx will be necessary
- auto ptx_vector = m_wallet->create_unmixable_sweep_transactions(m_trusted_daemon);
+ auto ptx_vector = m_wallet->create_unmixable_sweep_transactions(is_daemon_trusted());
if (ptx_vector.empty())
{
@@ -4773,7 +4802,7 @@ bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_)
}
catch (const std::exception &e)
{
- handle_transfer_exception(std::current_exception(), m_trusted_daemon);
+ handle_transfer_exception(std::current_exception(), is_daemon_trusted());
}
catch (...)
{
@@ -4922,7 +4951,7 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &a
try
{
// figure out what tx will be necessary
- auto ptx_vector = m_wallet->create_transactions_all(below, info.address, info.is_subaddress, fake_outs_count, 0 /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, m_trusted_daemon);
+ auto ptx_vector = m_wallet->create_transactions_all(below, info.address, info.is_subaddress, fake_outs_count, 0 /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, is_daemon_trusted());
if (ptx_vector.empty())
{
@@ -5006,7 +5035,7 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &a
}
catch (const std::exception& e)
{
- handle_transfer_exception(std::current_exception(), m_trusted_daemon);
+ handle_transfer_exception(std::current_exception(), is_daemon_trusted());
}
catch (...)
{
@@ -5135,7 +5164,7 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
try
{
// figure out what tx will be necessary
- auto ptx_vector = m_wallet->create_transactions_single(ki, info.address, info.is_subaddress, fake_outs_count, 0 /* unlock_time */, priority, extra, m_trusted_daemon);
+ auto ptx_vector = m_wallet->create_transactions_single(ki, info.address, info.is_subaddress, fake_outs_count, 0 /* unlock_time */, priority, extra, is_daemon_trusted());
if (ptx_vector.empty())
{
@@ -5205,7 +5234,7 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
}
catch (const std::exception& e)
{
- handle_transfer_exception(std::current_exception(), m_trusted_daemon);
+ handle_transfer_exception(std::current_exception(), is_daemon_trusted());
}
catch (...)
{
@@ -5510,7 +5539,7 @@ bool simple_wallet::submit_transfer(const std::vector<std::string> &args_)
}
catch (const std::exception& e)
{
- handle_transfer_exception(std::current_exception(), m_trusted_daemon);
+ handle_transfer_exception(std::current_exception(), is_daemon_trusted());
}
catch (...)
{
@@ -7098,7 +7127,7 @@ bool simple_wallet::import_key_images(const std::vector<std::string> &args)
fail_msg_writer() << tr("command not supported by HW wallet");
return true;
}
- if (!m_trusted_daemon)
+ if (!is_daemon_trusted())
{
fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon");
return true;
@@ -7484,6 +7513,7 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_params, arg_non_deterministic );
command_line::add_arg(desc_params, arg_electrum_seed );
command_line::add_arg(desc_params, arg_trusted_daemon);
+ command_line::add_arg(desc_params, arg_untrusted_daemon);
command_line::add_arg(desc_params, arg_allow_mismatched_daemon_version);
command_line::add_arg(desc_params, arg_restore_height);
command_line::add_arg(desc_params, arg_do_not_relay);
diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h
index f26f69353..7a788d432 100644
--- a/src/simplewallet/simplewallet.h
+++ b/src/simplewallet/simplewallet.h
@@ -218,6 +218,7 @@ namespace cryptonote
bool blackball(const std::vector<std::string>& args);
bool unblackball(const std::vector<std::string>& args);
bool blackballed(const std::vector<std::string>& args);
+ bool version(const std::vector<std::string>& args);
uint64_t get_daemon_blockchain_height(std::string& err);
bool try_connect_to_daemon(bool silent = false, uint32_t* version = nullptr);
@@ -228,6 +229,7 @@ namespace cryptonote
bool print_ring_members(const std::vector<tools::wallet2::pending_tx>& ptx_vector, std::ostream& ostr);
std::string get_prompt() const;
bool print_seed(bool encrypted);
+ bool is_daemon_trusted() const { return *m_trusted_daemon; }
/*!
* \brief Prints the seed with a nice message
@@ -330,7 +332,7 @@ namespace cryptonote
bool m_restore_deterministic_wallet; // recover flag
bool m_restore_multisig_wallet; // recover flag
bool m_non_deterministic; // old 2-random generation
- bool m_trusted_daemon;
+ boost::optional<bool> m_trusted_daemon;
bool m_allow_mismatched_daemon_version;
bool m_restoring; // are we restoring, by whatever method?
uint64_t m_restore_height; // optional
diff --git a/src/version.cpp.in b/src/version.cpp.in
index f83a85d9d..9fed91d99 100644
--- a/src/version.cpp.in
+++ b/src/version.cpp.in
@@ -1,5 +1,5 @@
#define DEF_MONERO_VERSION_TAG "@VERSIONTAG@"
-#define DEF_MONERO_VERSION "0.12.0.0-master"
+#define DEF_MONERO_VERSION "0.12.1.0-master"
#define DEF_MONERO_RELEASE_NAME "Lithium Luna"
#define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG
diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp
index ff4619f0f..8d200220d 100644
--- a/src/wallet/api/pending_transaction.cpp
+++ b/src/wallet/api/pending_transaction.cpp
@@ -34,6 +34,7 @@
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "cryptonote_basic/cryptonote_basic_impl.h"
+#include "common/base58.h"
#include <memory>
#include <vector>
@@ -102,6 +103,11 @@ bool PendingTransactionImpl::commit(const std::string &filename, bool overwrite)
}
// Commit tx
else {
+ auto multisigState = m_wallet.multisig();
+ if (multisigState.isMultisig && m_signers.size() < multisigState.threshold) {
+ throw runtime_error("Not enough signers to send multisig transaction");
+ }
+
m_wallet.pauseRefresh();
while (!m_pending_tx.empty()) {
auto & ptx = m_pending_tx.back();
@@ -188,6 +194,53 @@ std::vector<std::set<uint32_t>> PendingTransactionImpl::subaddrIndices() const
return result;
}
+std::string PendingTransactionImpl::multisigSignData() {
+ try {
+ if (!m_wallet.multisig().isMultisig) {
+ throw std::runtime_error("wallet is not multisig");
+ }
+
+ auto cipher = m_wallet.m_wallet->save_multisig_tx(m_pending_tx);
+ return epee::string_tools::buff_to_hex_nodelimer(cipher);
+ } catch (const std::exception& e) {
+ m_status = Status_Error;
+ m_errorString = std::string(tr("Couldn't multisig sign data: ")) + e.what();
+ }
+
+ return std::string();
+}
+
+void PendingTransactionImpl::signMultisigTx() {
+ try {
+ std::vector<crypto::hash> ignore;
+
+ tools::wallet2::multisig_tx_set txSet;
+ txSet.m_ptx = m_pending_tx;
+ txSet.m_signers = m_signers;
+
+ if (!m_wallet.m_wallet->sign_multisig_tx(txSet, ignore)) {
+ throw std::runtime_error("couldn't sign multisig transaction");
+ }
+
+ std::swap(m_pending_tx, txSet.m_ptx);
+ std::swap(m_signers, txSet.m_signers);
+ } catch (const std::exception& e) {
+ m_status = Status_Error;
+ m_errorString = std::string(tr("Couldn't sign multisig transaction: ")) + e.what();
+ }
+}
+
+std::vector<std::string> PendingTransactionImpl::signersKeys() const {
+ std::vector<std::string> keys;
+ keys.reserve(m_signers.size());
+
+ for (const auto& signer: m_signers) {
+ keys.emplace_back(tools::base58::encode(cryptonote::t_serializable_object_to_blob(signer)));
+ }
+
+ return keys;
+}
+
}
namespace Bitmonero = Monero;
diff --git a/src/wallet/api/pending_transaction.h b/src/wallet/api/pending_transaction.h
index d0bd66eb5..4f963c134 100644
--- a/src/wallet/api/pending_transaction.h
+++ b/src/wallet/api/pending_transaction.h
@@ -55,6 +55,10 @@ public:
std::vector<std::set<uint32_t>> subaddrIndices() const;
// TODO: continue with interface;
+ std::string multisigSignData();
+ void signMultisigTx();
+ std::vector<std::string> signersKeys() const;
+
private:
friend class WalletImpl;
WalletImpl &m_wallet;
@@ -62,6 +66,7 @@ private:
int m_status;
std::string m_errorString;
std::vector<tools::wallet2::pending_tx> m_pending_tx;
+ std::unordered_set<crypto::public_key> m_signers;
};
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index b78a471b9..fdecacd8f 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -69,14 +69,48 @@ namespace {
// Connection timeout 30 sec
static const int DEFAULT_CONNECTION_TIMEOUT_MILLIS = 1000 * 30;
- std::string get_default_ringdb_path()
+ std::string get_default_ringdb_path(cryptonote::network_type nettype)
{
boost::filesystem::path dir = tools::get_default_data_dir();
// remove .bitmonero, replace with .shared-ringdb
dir = dir.remove_filename();
dir /= ".shared-ringdb";
+ if (nettype == cryptonote::TESTNET)
+ dir /= "testnet";
+ else if (nettype == cryptonote::STAGENET)
+ dir /= "stagenet";
return dir.string();
}
+
+ void checkMultisigWalletReady(const tools::wallet2* wallet) {
+ if (!wallet) {
+ throw runtime_error("Wallet is not initialized yet");
+ }
+
+ bool ready;
+ if (!wallet->multisig(&ready)) {
+ throw runtime_error("Wallet is not multisig");
+ }
+
+ if (!ready) {
+ throw runtime_error("Multisig wallet is not finalized yet");
+ }
+ }
+
+ void checkMultisigWalletNotReady(const tools::wallet2* wallet) {
+ if (!wallet) {
+ throw runtime_error("Wallet is not initialized yet");
+ }
+
+ bool ready;
+ if (!wallet->multisig(&ready)) {
+ throw runtime_error("Wallet is not multisig");
+ }
+
+ if (ready) {
+ throw runtime_error("Multisig wallet is already finalized");
+ }
+ }
}
struct Wallet2CallbackImpl : public tools::i_wallet2_callback
@@ -305,14 +339,14 @@ uint64_t Wallet::maximumAllowedAmount()
return std::numeric_limits<uint64_t>::max();
}
-void Wallet::init(const char *argv0, const char *default_log_base_name) {
+void Wallet::init(const char *argv0, const char *default_log_base_name, const std::string &log_path, bool console) {
#ifdef WIN32
// Activate UTF-8 support for Boost filesystem classes on Windows
std::locale::global(boost::locale::generator().generate(""));
boost::filesystem::path::imbue(std::locale());
#endif
epee::string_tools::set_module_name_and_folder(argv0);
- mlog_configure(mlog_get_default_log_path(default_log_base_name), true);
+ mlog_configure(log_path.empty() ? mlog_get_default_log_path(default_log_base_name) : log_path.c_str(), console);
}
void Wallet::debug(const std::string &category, const std::string &str) {
@@ -395,9 +429,9 @@ bool WalletImpl::create(const std::string &path, const std::string &password, co
// add logic to error out if new wallet requested but named wallet file exists
if (keys_file_exists || wallet_file_exists) {
- m_errorString = "attempting to generate or restore wallet, but specified file(s) exist. Exiting to not risk overwriting.";
- LOG_ERROR(m_errorString);
- m_status = Status_Critical;
+ std::string error = "attempting to generate or restore wallet, but specified file(s) exist. Exiting to not risk overwriting.";
+ LOG_ERROR(error);
+ setStatusCritical(error);
return false;
}
// TODO: validate language
@@ -406,11 +440,10 @@ bool WalletImpl::create(const std::string &path, const std::string &password, co
try {
recovery_val = m_wallet->generate(path, password, secret_key, false, false);
m_password = password;
- m_status = Status_Ok;
+ clearStatus();
} catch (const std::exception &e) {
LOG_ERROR("Error creating wallet: " << e.what());
- m_status = Status_Critical;
- m_errorString = e.what();
+ setStatusCritical(e.what());
return false;
}
@@ -434,9 +467,9 @@ bool WalletImpl::createWatchOnly(const std::string &path, const std::string &pas
// add logic to error out if new wallet requested but named wallet file exists
if (keys_file_exists || wallet_file_exists) {
- m_errorString = "attempting to generate view only wallet, but specified file(s) exist. Exiting to not risk overwriting.";
- LOG_ERROR(m_errorString);
- m_status = Status_Error;
+ std::string error = "attempting to generate view only wallet, but specified file(s) exist. Exiting to not risk overwriting.";
+ LOG_ERROR(error);
+ setStatusError(error);
return false;
}
// TODO: validate language
@@ -472,11 +505,10 @@ bool WalletImpl::createWatchOnly(const std::string &path, const std::string &pas
uint64_t spent = 0;
uint64_t unspent = 0;
view_wallet->import_key_images(key_images,spent,unspent,false);
- m_status = Status_Ok;
+ clearStatus();
} catch (const std::exception &e) {
LOG_ERROR("Error creating view only wallet: " << e.what());
- m_status = Status_Error;
- m_errorString = e.what();
+ setStatusError(e.what());
return false;
}
// Store wallet
@@ -503,8 +535,7 @@ bool WalletImpl::recoverFromKeysWithPassword(const std::string &path,
cryptonote::address_parse_info info;
if(!get_account_address_from_str(info, m_wallet->nettype(), address_string))
{
- m_errorString = tr("failed to parse address");
- m_status = Status_Error;
+ setStatusError(tr("failed to parse address"));
return false;
}
@@ -515,8 +546,7 @@ bool WalletImpl::recoverFromKeysWithPassword(const std::string &path,
cryptonote::blobdata spendkey_data;
if(!epee::string_tools::parse_hexstr_to_binbuff(spendkey_string, spendkey_data) || spendkey_data.size() != sizeof(crypto::secret_key))
{
- m_errorString = tr("failed to parse secret spend key");
- m_status = Status_Error;
+ setStatusError(tr("failed to parse secret spend key"));
return false;
}
has_spendkey = true;
@@ -525,15 +555,13 @@ bool WalletImpl::recoverFromKeysWithPassword(const std::string &path,
// parse view secret key
if (viewkey_string.empty()) {
- m_errorString = tr("No view key supplied, cancelled");
- m_status = Status_Error;
+ setStatusError(tr("No view key supplied, cancelled"));
return false;
}
cryptonote::blobdata viewkey_data;
if(!epee::string_tools::parse_hexstr_to_binbuff(viewkey_string, viewkey_data) || viewkey_data.size() != sizeof(crypto::secret_key))
{
- m_errorString = tr("failed to parse secret view key");
- m_status = Status_Error;
+ setStatusError(tr("failed to parse secret view key"));
return false;
}
crypto::secret_key viewkey = *reinterpret_cast<const crypto::secret_key*>(viewkey_data.data());
@@ -542,24 +570,20 @@ bool WalletImpl::recoverFromKeysWithPassword(const std::string &path,
crypto::public_key pkey;
if(has_spendkey) {
if (!crypto::secret_key_to_public_key(spendkey, pkey)) {
- m_errorString = tr("failed to verify secret spend key");
- m_status = Status_Error;
+ setStatusError(tr("failed to verify secret spend key"));
return false;
}
if (info.address.m_spend_public_key != pkey) {
- m_errorString = tr("spend key does not match address");
- m_status = Status_Error;
+ setStatusError(tr("spend key does not match address"));
return false;
}
}
if (!crypto::secret_key_to_public_key(viewkey, pkey)) {
- m_errorString = tr("failed to verify secret view key");
- m_status = Status_Error;
+ setStatusError(tr("failed to verify secret view key"));
return false;
}
if (info.address.m_view_public_key != pkey) {
- m_errorString = tr("view key does not match address");
- m_status = Status_Error;
+ setStatusError(tr("view key does not match address"));
return false;
}
@@ -577,8 +601,7 @@ bool WalletImpl::recoverFromKeysWithPassword(const std::string &path,
}
catch (const std::exception& e) {
- m_errorString = string(tr("failed to generate new wallet: ")) + e.what();
- m_status = Status_Error;
+ setStatusError(string(tr("failed to generate new wallet: ")) + e.what());
return false;
}
return true;
@@ -599,16 +622,15 @@ bool WalletImpl::open(const std::string &path, const std::string &password)
// Rebuilding wallet cache, using refresh height from .keys file
m_rebuildWalletCache = true;
}
- m_wallet->set_ring_database(get_default_ringdb_path());
+ m_wallet->set_ring_database(get_default_ringdb_path(m_wallet->nettype()));
m_wallet->load(path, password);
m_password = password;
} catch (const std::exception &e) {
LOG_ERROR("Error opening wallet: " << e.what());
- m_status = Status_Critical;
- m_errorString = e.what();
+ setStatusCritical(e.what());
}
- return m_status == Status_Ok;
+ return status() == Status_Ok;
}
bool WalletImpl::recover(const std::string &path, const std::string &seed)
@@ -621,9 +643,8 @@ bool WalletImpl::recover(const std::string &path, const std::string &password, c
clearStatus();
m_errorString.clear();
if (seed.empty()) {
- m_errorString = "Electrum seed is empty";
- LOG_ERROR(m_errorString);
- m_status = Status_Error;
+ LOG_ERROR("Electrum seed is empty");
+ setStatusError(tr("Electrum seed is empty"));
return false;
}
@@ -631,8 +652,7 @@ bool WalletImpl::recover(const std::string &path, const std::string &password, c
crypto::secret_key recovery_key;
std::string old_language;
if (!crypto::ElectrumWords::words_to_bytes(seed, recovery_key, old_language)) {
- m_errorString = "Electrum-style word list failed verification";
- m_status = Status_Error;
+ setStatusError(tr("Electrum-style word list failed verification"));
return false;
}
@@ -644,10 +664,9 @@ bool WalletImpl::recover(const std::string &path, const std::string &password, c
m_wallet->generate(path, password, recovery_key, true, false);
} catch (const std::exception &e) {
- m_status = Status_Critical;
- m_errorString = e.what();
+ setStatusCritical(e.what());
}
- return m_status == Status_Ok;
+ return status() == Status_Ok;
}
bool WalletImpl::close(bool store)
@@ -671,8 +690,7 @@ bool WalletImpl::close(bool store)
result = true;
clearStatus();
} catch (const std::exception &e) {
- m_status = Status_Critical;
- m_errorString = e.what();
+ setStatusCritical(e.what());
LOG_ERROR("Error closing wallet: " << e.what());
}
return result;
@@ -698,14 +716,22 @@ void WalletImpl::setSeedLanguage(const std::string &arg)
int WalletImpl::status() const
{
+ boost::lock_guard<boost::mutex> l(m_statusMutex);
return m_status;
}
std::string WalletImpl::errorString() const
{
+ boost::lock_guard<boost::mutex> l(m_statusMutex);
return m_errorString;
}
+void WalletImpl::statusWithErrorString(int& status, std::string& errorString) const {
+ boost::lock_guard<boost::mutex> l(m_statusMutex);
+ status = m_status;
+ errorString = m_errorString;
+}
+
bool WalletImpl::setPassword(const std::string &password)
{
clearStatus();
@@ -713,10 +739,9 @@ bool WalletImpl::setPassword(const std::string &password)
m_wallet->rewrite(m_wallet->get_wallet_file(), password);
m_password = password;
} catch (const std::exception &e) {
- m_status = Status_Error;
- m_errorString = e.what();
+ setStatusError(e.what());
}
- return m_status == Status_Ok;
+ return status() == Status_Ok;
}
std::string WalletImpl::address(uint32_t accountIndex, uint32_t addressIndex) const
@@ -769,11 +794,11 @@ bool WalletImpl::store(const std::string &path)
}
} catch (const std::exception &e) {
LOG_ERROR("Error saving wallet: " << e.what());
- m_status = Status_Error;
- m_errorString = e.what();
+ setStatusError(e.what());
+ return false;
}
- return m_status == Status_Ok;
+ return true;
}
string WalletImpl::filename() const
@@ -806,8 +831,7 @@ bool WalletImpl::lightWalletImportWalletRequest(std::string &payment_id, uint64_
{
cryptonote::COMMAND_RPC_IMPORT_WALLET_REQUEST::response response;
if(!m_wallet->light_wallet_import_wallet_request(response)){
- m_errorString = tr("Failed to send import wallet request");
- m_status = Status_Error;
+ setStatusError(tr("Failed to send import wallet request"));
return false;
}
fee = response.import_fee;
@@ -820,8 +844,7 @@ bool WalletImpl::lightWalletImportWalletRequest(std::string &payment_id, uint64_
catch (const std::exception &e)
{
LOG_ERROR("Error sending import wallet request: " << e.what());
- m_errorString = e.what();
- m_status = Status_Error;
+ setStatusError(e.what());
return false;
}
return true;
@@ -870,12 +893,9 @@ uint64_t WalletImpl::daemonBlockChainHeight() const
if (!err.empty()) {
LOG_ERROR(__FUNCTION__ << ": " << err);
result = 0;
- m_errorString = err;
- m_status = Status_Error;
-
+ setStatusError(err);
} else {
- m_status = Status_Ok;
- m_errorString = "";
+ clearStatus();
}
return result;
}
@@ -892,12 +912,9 @@ uint64_t WalletImpl::daemonBlockChainTargetHeight() const
if (!err.empty()) {
LOG_ERROR(__FUNCTION__ << ": " << err);
result = 0;
- m_errorString = err;
- m_status = Status_Error;
-
+ setStatusError(err);
} else {
- m_status = Status_Ok;
- m_errorString = "";
+ clearStatus();
}
// Target height can be 0 when daemon is synced. Use blockchain height instead.
if(result == 0)
@@ -921,8 +938,10 @@ bool WalletImpl::synchronized() const
bool WalletImpl::refresh()
{
clearStatus();
+ //TODO: make doRefresh return bool to know whether the error occured during refresh or not
+ //otherwise one may try, say, to send transaction, transfer fails and this method returns false
doRefresh();
- return m_status == Status_Ok;
+ return status() == Status_Ok;
}
void WalletImpl::refreshAsync()
@@ -952,8 +971,7 @@ UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_file
clearStatus();
UnsignedTransactionImpl * transaction = new UnsignedTransactionImpl(*this);
if (!m_wallet->load_unsigned_tx(unsigned_filename, transaction->m_unsigned_tx_set)){
- m_errorString = tr("Failed to load unsigned transactions");
- m_status = Status_Error;
+ setStatusError(tr("Failed to load unsigned transactions"));
}
// Check tx data and construct confirmation message
@@ -961,8 +979,7 @@ UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_file
if (!transaction->m_unsigned_tx_set.transfers.empty())
extra_message = (boost::format("%u outputs to import. ") % (unsigned)transaction->m_unsigned_tx_set.transfers.size()).str();
transaction->checkLoadedTx([&transaction](){return transaction->m_unsigned_tx_set.txes.size();}, [&transaction](size_t n)->const tools::wallet2::tx_construction_data&{return transaction->m_unsigned_tx_set.txes[n];}, extra_message);
- m_status = transaction->status();
- m_errorString = transaction->errorString();
+ setStatus(transaction->status(), transaction->errorString());
return transaction;
}
@@ -973,14 +990,12 @@ bool WalletImpl::submitTransaction(const string &fileName) {
bool r = m_wallet->load_tx(fileName, transaction->m_pending_tx);
if (!r) {
- m_errorString = tr("Failed to load transaction from file");
- m_status = Status_Ok;
+ setStatus(Status_Ok, tr("Failed to load transaction from file"));
return false;
}
if(!transaction->commit()) {
- m_errorString = transaction->m_errorString;
- m_status = Status_Error;
+ setStatusError(transaction->m_errorString);
return false;
}
@@ -991,8 +1006,7 @@ bool WalletImpl::exportKeyImages(const string &filename)
{
if (m_wallet->watch_only())
{
- m_errorString = tr("Wallet is view only");
- m_status = Status_Error;
+ setStatusError(tr("Wallet is view only"));
return false;
}
@@ -1000,16 +1014,14 @@ bool WalletImpl::exportKeyImages(const string &filename)
{
if (!m_wallet->export_key_images(filename))
{
- m_errorString = tr("failed to save file ") + filename;
- m_status = Status_Error;
+ setStatusError(tr("failed to save file ") + filename);
return false;
}
}
catch (const std::exception &e)
{
LOG_ERROR("Error exporting key images: " << e.what());
- m_errorString = e.what();
- m_status = Status_Error;
+ setStatusError(e.what());
return false;
}
return true;
@@ -1018,8 +1030,7 @@ bool WalletImpl::exportKeyImages(const string &filename)
bool WalletImpl::importKeyImages(const string &filename)
{
if (!trustedDaemon()) {
- m_status = Status_Error;
- m_errorString = tr("Key images can only be imported with a trusted daemon");
+ setStatusError(tr("Key images can only be imported with a trusted daemon"));
return false;
}
try
@@ -1032,8 +1043,7 @@ bool WalletImpl::importKeyImages(const string &filename)
catch (const std::exception &e)
{
LOG_ERROR("Error exporting key images: " << e.what());
- m_errorString = string(tr("Failed to import key images: ")) + e.what();
- m_status = Status_Error;
+ setStatusError(string(tr("Failed to import key images: ")) + e.what());
return false;
}
@@ -1065,8 +1075,7 @@ std::string WalletImpl::getSubaddressLabel(uint32_t accountIndex, uint32_t addre
catch (const std::exception &e)
{
LOG_ERROR("Error getting subaddress label: ") << e.what();
- m_errorString = string(tr("Failed to get subaddress label: ")) + e.what();
- m_status = Status_Error;
+ setStatusError(string(tr("Failed to get subaddress label: ")) + e.what());
return "";
}
}
@@ -1079,11 +1088,136 @@ void WalletImpl::setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex
catch (const std::exception &e)
{
LOG_ERROR("Error setting subaddress label: ") << e.what();
- m_errorString = string(tr("Failed to set subaddress label: ")) + e.what();
- m_status = Status_Error;
+ setStatusError(string(tr("Failed to set subaddress label: ")) + e.what());
}
}
+MultisigState WalletImpl::multisig() const {
+ MultisigState state;
+ state.isMultisig = m_wallet->multisig(&state.isReady, &state.threshold, &state.total);
+
+ return state;
+}
+
+string WalletImpl::getMultisigInfo() const {
+ try {
+ clearStatus();
+ return m_wallet->get_multisig_info();
+ } catch (const exception& e) {
+ LOG_ERROR("Error on generating multisig info: ") << e.what();
+ setStatusError(string(tr("Failed to get multisig info: ")) + e.what());
+ }
+
+ return string();
+}
+
+string WalletImpl::makeMultisig(const vector<string>& info, uint32_t threshold) {
+ try {
+ clearStatus();
+
+ if (m_wallet->multisig()) {
+ throw runtime_error("Wallet is already multisig");
+ }
+
+ return m_wallet->make_multisig(epee::wipeable_string(m_password), info, threshold);
+ } catch (const exception& e) {
+ LOG_ERROR("Error on making multisig wallet: ") << e.what();
+ setStatusError(string(tr("Failed to make multisig: ")) + e.what());
+ }
+
+ return string();
+}
+
+bool WalletImpl::finalizeMultisig(const vector<string>& extraMultisigInfo) {
+ try {
+ clearStatus();
+ checkMultisigWalletNotReady(m_wallet);
+
+ if (m_wallet->finalize_multisig(epee::wipeable_string(m_password), extraMultisigInfo)) {
+ return true;
+ }
+
+ setStatusError(tr("Failed to finalize multisig wallet creation"));
+ } catch (const exception& e) {
+ LOG_ERROR("Error on finalizing multisig wallet creation: ") << e.what();
+ setStatusError(string(tr("Failed to finalize multisig wallet creation: ")) + e.what());
+ }
+
+ return false;
+}
+
+bool WalletImpl::exportMultisigImages(string& images) {
+ try {
+ clearStatus();
+ checkMultisigWalletReady(m_wallet);
+
+ auto blob = m_wallet->export_multisig();
+ images = epee::string_tools::buff_to_hex_nodelimer(blob);
+ return true;
+ } catch (const exception& e) {
+ LOG_ERROR("Error on exporting multisig images: ") << e.what();
+ setStatusError(string(tr("Failed to export multisig images: ")) + e.what());
+ }
+
+ return false;
+}
+
+size_t WalletImpl::importMultisigImages(const vector<string>& images) {
+ try {
+ clearStatus();
+ checkMultisigWalletReady(m_wallet);
+
+ std::vector<std::string> blobs;
+ blobs.reserve(images.size());
+
+ for (const auto& image: images) {
+ std::string blob;
+ if (!epee::string_tools::parse_hexstr_to_binbuff(image, blob)) {
+ LOG_ERROR("Failed to parse imported multisig images");
+ setStatusError(tr("Failed to parse imported multisig images"));
+ return 0;
+ }
+
+ blobs.emplace_back(std::move(blob));
+ }
+
+ return m_wallet->import_multisig(blobs);
+ } catch (const exception& e) {
+ LOG_ERROR("Error on importing multisig images: ") << e.what();
+ setStatusError(string(tr("Failed to import multisig images: ")) + e.what());
+ }
+
+ return 0;
+}
+
+PendingTransaction* WalletImpl::restoreMultisigTransaction(const string& signData) {
+ try {
+ clearStatus();
+ checkMultisigWalletReady(m_wallet);
+
+ string binary;
+ if (!epee::string_tools::parse_hexstr_to_binbuff(signData, binary)) {
+ throw runtime_error("Failed to deserialize multisig transaction");
+ }
+
+ tools::wallet2::multisig_tx_set txSet;
+ if (!m_wallet->load_multisig_tx(binary, txSet, {})) {
+ throw runtime_error("couldn't parse multisig transaction data");
+ }
+
+ auto ptx = new PendingTransactionImpl(*this);
+ ptx->m_pending_tx = txSet.m_ptx;
+ ptx->m_signers = txSet.m_signers;
+
+ return ptx;
+ } catch (exception& e) {
+ LOG_ERROR("Error on restoring multisig transaction: ") << e.what();
+ setStatusError(string(tr("Failed to restore multisig transaction: ")) + e.what());
+ }
+
+ return nullptr;
+}
+
// TODO:
// 1 - properly handle payment id (add another menthod with explicit 'payment_id' param)
// 2 - check / design how "Transaction" can be single interface
@@ -1117,8 +1251,7 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
do {
if(!cryptonote::get_account_address_from_str(info, m_wallet->nettype(), dst_addr)) {
// TODO: copy-paste 'if treating as an address fails, try as url' from simplewallet.cpp:1982
- m_status = Status_Error;
- m_errorString = "Invalid destination address";
+ setStatusError(tr("Invalid destination address"));
break;
}
@@ -1143,8 +1276,7 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
}
if (!r) {
- m_status = Status_Error;
- m_errorString = tr("payment id has invalid format, expected 16 or 64 character hex string: ") + payment_id;
+ setStatusError(tr("payment id has invalid format, expected 16 or 64 character hex string: ") + payment_id);
break;
}
}
@@ -1153,8 +1285,7 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, info.payment_id);
bool r = add_extra_nonce_to_tx_extra(extra, extra_nonce);
if (!r) {
- m_status = Status_Error;
- m_errorString = tr("Failed to add short payment id: ") + epee::string_tools::pod_to_hex(info.payment_id);
+ setStatusError(tr("Failed to add short payment id: ") + epee::string_tools::pod_to_hex(info.payment_id));
break;
}
}
@@ -1185,40 +1316,33 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
extra, subaddr_account, subaddr_indices, m_trustedDaemon);
}
+ if (multisig().isMultisig) {
+ transaction->m_signers = m_wallet->make_multisig_tx_set(transaction->m_pending_tx).m_signers;
+ }
} catch (const tools::error::daemon_busy&) {
// TODO: make it translatable with "tr"?
- m_errorString = tr("daemon is busy. Please try again later.");
- m_status = Status_Error;
+ setStatusError(tr("daemon is busy. Please try again later."));
} catch (const tools::error::no_connection_to_daemon&) {
- m_errorString = tr("no connection to daemon. Please make sure daemon is running.");
- m_status = Status_Error;
+ setStatusError(tr("no connection to daemon. Please make sure daemon is running."));
} catch (const tools::error::wallet_rpc_error& e) {
- m_errorString = tr("RPC error: ") + e.to_string();
- m_status = Status_Error;
+ setStatusError(tr("RPC error: ") + e.to_string());
} catch (const tools::error::get_random_outs_error &e) {
- m_errorString = (boost::format(tr("failed to get random outputs to mix: %s")) % e.what()).str();
- m_status = Status_Error;
-
+ setStatusError((boost::format(tr("failed to get random outputs to mix: %s")) % e.what()).str());
} catch (const tools::error::not_enough_unlocked_money& e) {
- m_status = Status_Error;
std::ostringstream writer;
writer << boost::format(tr("not enough money to transfer, available only %s, sent amount %s")) %
print_money(e.available()) %
print_money(e.tx_amount());
- m_errorString = writer.str();
-
+ setStatusError(writer.str());
} catch (const tools::error::not_enough_money& e) {
- m_status = Status_Error;
std::ostringstream writer;
writer << boost::format(tr("not enough money to transfer, overall balance only %s, sent amount %s")) %
print_money(e.available()) %
print_money(e.tx_amount());
- m_errorString = writer.str();
-
+ setStatusError(writer.str());
} catch (const tools::error::tx_not_possible& e) {
- m_status = Status_Error;
std::ostringstream writer;
writer << boost::format(tr("not enough money to transfer, available only %s, transaction amount %s = %s + %s (fee)")) %
@@ -1226,8 +1350,7 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
print_money(e.tx_amount() + e.fee()) %
print_money(e.tx_amount()) %
print_money(e.fee());
- m_errorString = writer.str();
-
+ setStatusError(writer.str());
} catch (const tools::error::not_enough_outs_to_mix& e) {
std::ostringstream writer;
writer << tr("not enough outputs for specified ring size") << " = " << (e.mixin_count() + 1) << ":";
@@ -1235,42 +1358,31 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
writer << "\n" << tr("output amount") << " = " << print_money(outs_for_amount.first) << ", " << tr("found outputs to use") << " = " << outs_for_amount.second;
}
writer << "\n" << tr("Please sweep unmixable outputs.");
- m_errorString = writer.str();
- m_status = Status_Error;
+ setStatusError(writer.str());
} catch (const tools::error::tx_not_constructed&) {
- m_errorString = tr("transaction was not constructed");
- m_status = Status_Error;
+ setStatusError(tr("transaction was not constructed"));
} catch (const tools::error::tx_rejected& e) {
std::ostringstream writer;
writer << (boost::format(tr("transaction %s was rejected by daemon with status: ")) % get_transaction_hash(e.tx())) << e.status();
- m_errorString = writer.str();
- m_status = Status_Error;
+ setStatusError(writer.str());
} catch (const tools::error::tx_sum_overflow& e) {
- m_errorString = e.what();
- m_status = Status_Error;
+ setStatusError(e.what());
} catch (const tools::error::zero_destination&) {
- m_errorString = tr("one of destinations is zero");
- m_status = Status_Error;
+ setStatusError(tr("one of destinations is zero"));
} catch (const tools::error::tx_too_big& e) {
- m_errorString = tr("failed to find a suitable way to split transactions");
- m_status = Status_Error;
+ setStatusError(tr("failed to find a suitable way to split transactions"));
} catch (const tools::error::transfer_error& e) {
- m_errorString = string(tr("unknown transfer error: ")) + e.what();
- m_status = Status_Error;
+ setStatusError(string(tr("unknown transfer error: ")) + e.what());
} catch (const tools::error::wallet_internal_error& e) {
- m_errorString = string(tr("internal error: ")) + e.what();
- m_status = Status_Error;
+ setStatusError(string(tr("internal error: ")) + e.what());
} catch (const std::exception& e) {
- m_errorString = string(tr("unexpected error: ")) + e.what();
- m_status = Status_Error;
+ setStatusError(string(tr("unexpected error: ")) + e.what());
} catch (...) {
- m_errorString = tr("unknown error");
- m_status = Status_Error;
+ setStatusError(tr("unknown error"));
}
} while (false);
- transaction->m_status = m_status;
- transaction->m_errorString = m_errorString;
+ statusWithErrorString(transaction->m_status, transaction->m_errorString);
// Resume refresh thread
startRefresh();
return transaction;
@@ -1291,38 +1403,31 @@ PendingTransaction *WalletImpl::createSweepUnmixableTransaction()
} catch (const tools::error::daemon_busy&) {
// TODO: make it translatable with "tr"?
- m_errorString = tr("daemon is busy. Please try again later.");
- m_status = Status_Error;
+ setStatusError(tr("daemon is busy. Please try again later."));
} catch (const tools::error::no_connection_to_daemon&) {
- m_errorString = tr("no connection to daemon. Please make sure daemon is running.");
- m_status = Status_Error;
+ setStatusError(tr("no connection to daemon. Please make sure daemon is running."));
} catch (const tools::error::wallet_rpc_error& e) {
- m_errorString = tr("RPC error: ") + e.to_string();
- m_status = Status_Error;
+ setStatusError(tr("RPC error: ") + e.to_string());
} catch (const tools::error::get_random_outs_error&) {
- m_errorString = tr("failed to get random outputs to mix");
- m_status = Status_Error;
-
+ setStatusError(tr("failed to get random outputs to mix"));
} catch (const tools::error::not_enough_unlocked_money& e) {
- m_status = Status_Error;
+ setStatusError("");
std::ostringstream writer;
writer << boost::format(tr("not enough money to transfer, available only %s, sent amount %s")) %
print_money(e.available()) %
print_money(e.tx_amount());
- m_errorString = writer.str();
-
+ setStatusError(writer.str());
} catch (const tools::error::not_enough_money& e) {
- m_status = Status_Error;
+ setStatusError("");
std::ostringstream writer;
writer << boost::format(tr("not enough money to transfer, overall balance only %s, sent amount %s")) %
print_money(e.available()) %
print_money(e.tx_amount());
- m_errorString = writer.str();
-
+ setStatusError(writer.str());
} catch (const tools::error::tx_not_possible& e) {
- m_status = Status_Error;
+ setStatusError("");
std::ostringstream writer;
writer << boost::format(tr("not enough money to transfer, available only %s, transaction amount %s = %s + %s (fee)")) %
@@ -1330,50 +1435,38 @@ PendingTransaction *WalletImpl::createSweepUnmixableTransaction()
print_money(e.tx_amount() + e.fee()) %
print_money(e.tx_amount()) %
print_money(e.fee());
- m_errorString = writer.str();
-
+ setStatusError(writer.str());
} catch (const tools::error::not_enough_outs_to_mix& e) {
std::ostringstream writer;
writer << tr("not enough outputs for specified ring size") << " = " << (e.mixin_count() + 1) << ":";
for (const std::pair<uint64_t, uint64_t> outs_for_amount : e.scanty_outs()) {
writer << "\n" << tr("output amount") << " = " << print_money(outs_for_amount.first) << ", " << tr("found outputs to use") << " = " << outs_for_amount.second;
}
- m_errorString = writer.str();
- m_status = Status_Error;
+ setStatusError(writer.str());
} catch (const tools::error::tx_not_constructed&) {
- m_errorString = tr("transaction was not constructed");
- m_status = Status_Error;
+ setStatusError(tr("transaction was not constructed"));
} catch (const tools::error::tx_rejected& e) {
std::ostringstream writer;
writer << (boost::format(tr("transaction %s was rejected by daemon with status: ")) % get_transaction_hash(e.tx())) << e.status();
- m_errorString = writer.str();
- m_status = Status_Error;
+ setStatusError(writer.str());
} catch (const tools::error::tx_sum_overflow& e) {
- m_errorString = e.what();
- m_status = Status_Error;
+ setStatusError(e.what());
} catch (const tools::error::zero_destination&) {
- m_errorString = tr("one of destinations is zero");
- m_status = Status_Error;
+ setStatusError(tr("one of destinations is zero"));
} catch (const tools::error::tx_too_big& e) {
- m_errorString = tr("failed to find a suitable way to split transactions");
- m_status = Status_Error;
+ setStatusError(tr("failed to find a suitable way to split transactions"));
} catch (const tools::error::transfer_error& e) {
- m_errorString = string(tr("unknown transfer error: ")) + e.what();
- m_status = Status_Error;
+ setStatusError(string(tr("unknown transfer error: ")) + e.what());
} catch (const tools::error::wallet_internal_error& e) {
- m_errorString = string(tr("internal error: ")) + e.what();
- m_status = Status_Error;
+ setStatusError(string(tr("internal error: ")) + e.what());
} catch (const std::exception& e) {
- m_errorString = string(tr("unexpected error: ")) + e.what();
- m_status = Status_Error;
+ setStatusError(string(tr("unexpected error: ")) + e.what());
} catch (...) {
- m_errorString = tr("unknown error");
- m_status = Status_Error;
+ setStatusError(tr("unknown error"));
}
} while (false);
- transaction->m_status = m_status;
- transaction->m_errorString = m_errorString;
+ statusWithErrorString(transaction->m_status, transaction->m_errorString);
return transaction;
}
@@ -1444,8 +1537,7 @@ std::string WalletImpl::getTxKey(const std::string &txid_str) const
crypto::hash txid;
if(!epee::string_tools::hex_to_pod(txid_str, txid))
{
- m_status = Status_Error;
- m_errorString = tr("Failed to parse txid");
+ setStatusError(tr("Failed to parse txid"));
return "";
}
@@ -1453,7 +1545,7 @@ std::string WalletImpl::getTxKey(const std::string &txid_str) const
std::vector<crypto::secret_key> additional_tx_keys;
if (m_wallet->get_tx_key(txid, tx_key, additional_tx_keys))
{
- m_status = Status_Ok;
+ clearStatus();
std::ostringstream oss;
oss << epee::string_tools::pod_to_hex(tx_key);
for (size_t i = 0; i < additional_tx_keys.size(); ++i)
@@ -1462,8 +1554,7 @@ std::string WalletImpl::getTxKey(const std::string &txid_str) const
}
else
{
- m_status = Status_Error;
- m_errorString = tr("no tx keys found for this txid");
+ setStatusError(tr("no tx keys found for this txid"));
return "";
}
}
@@ -1473,8 +1564,7 @@ bool WalletImpl::checkTxKey(const std::string &txid_str, std::string tx_key_str,
crypto::hash txid;
if (!epee::string_tools::hex_to_pod(txid_str, txid))
{
- m_status = Status_Error;
- m_errorString = tr("Failed to parse txid");
+ setStatusError(tr("Failed to parse txid"));
return false;
}
@@ -1482,8 +1572,7 @@ bool WalletImpl::checkTxKey(const std::string &txid_str, std::string tx_key_str,
std::vector<crypto::secret_key> additional_tx_keys;
if (!epee::string_tools::hex_to_pod(tx_key_str.substr(0, 64), tx_key))
{
- m_status = Status_Error;
- m_errorString = tr("Failed to parse tx key");
+ setStatusError(tr("Failed to parse tx key"));
return false;
}
tx_key_str = tx_key_str.substr(64);
@@ -1492,8 +1581,7 @@ bool WalletImpl::checkTxKey(const std::string &txid_str, std::string tx_key_str,
additional_tx_keys.resize(additional_tx_keys.size() + 1);
if (!epee::string_tools::hex_to_pod(tx_key_str.substr(0, 64), additional_tx_keys.back()))
{
- m_status = Status_Error;
- m_errorString = tr("Failed to parse tx key");
+ setStatusError(tr("Failed to parse tx key"));
return false;
}
tx_key_str = tx_key_str.substr(64);
@@ -1502,21 +1590,19 @@ bool WalletImpl::checkTxKey(const std::string &txid_str, std::string tx_key_str,
cryptonote::address_parse_info info;
if (!cryptonote::get_account_address_from_str(info, m_wallet->nettype(), address_str))
{
- m_status = Status_Error;
- m_errorString = tr("Failed to parse address");
+ setStatusError(tr("Failed to parse address"));
return false;
}
try
{
m_wallet->check_tx_key(txid, tx_key, additional_tx_keys, info.address, received, in_pool, confirmations);
- m_status = Status_Ok;
+ clearStatus();
return true;
}
catch (const std::exception &e)
{
- m_status = Status_Error;
- m_errorString = e.what();
+ setStatusError(e.what());
return false;
}
}
@@ -1526,28 +1612,25 @@ std::string WalletImpl::getTxProof(const std::string &txid_str, const std::strin
crypto::hash txid;
if (!epee::string_tools::hex_to_pod(txid_str, txid))
{
- m_status = Status_Error;
- m_errorString = tr("Failed to parse txid");
+ setStatusError(tr("Failed to parse txid"));
return "";
}
cryptonote::address_parse_info info;
if (!cryptonote::get_account_address_from_str(info, m_wallet->nettype(), address_str))
{
- m_status = Status_Error;
- m_errorString = tr("Failed to parse address");
+ setStatusError(tr("Failed to parse address"));
return "";
}
try
{
- m_status = Status_Ok;
+ clearStatus();
return m_wallet->get_tx_proof(txid, info.address, info.is_subaddress, message);
}
catch (const std::exception &e)
{
- m_status = Status_Error;
- m_errorString = e.what();
+ setStatusError(e.what());
return "";
}
}
@@ -1557,29 +1640,26 @@ bool WalletImpl::checkTxProof(const std::string &txid_str, const std::string &ad
crypto::hash txid;
if (!epee::string_tools::hex_to_pod(txid_str, txid))
{
- m_status = Status_Error;
- m_errorString = tr("Failed to parse txid");
+ setStatusError(tr("Failed to parse txid"));
return false;
}
cryptonote::address_parse_info info;
if (!cryptonote::get_account_address_from_str(info, m_wallet->nettype(), address_str))
{
- m_status = Status_Error;
- m_errorString = tr("Failed to parse address");
+ setStatusError(tr("Failed to parse address"));
return false;
}
try
{
good = m_wallet->check_tx_proof(txid, info.address, info.is_subaddress, message, signature, received, in_pool, confirmations);
- m_status = Status_Ok;
+ clearStatus();
return true;
}
catch (const std::exception &e)
{
- m_status = Status_Error;
- m_errorString = e.what();
+ setStatusError(e.what());
return false;
}
}
@@ -1588,20 +1668,18 @@ std::string WalletImpl::getSpendProof(const std::string &txid_str, const std::st
crypto::hash txid;
if(!epee::string_tools::hex_to_pod(txid_str, txid))
{
- m_status = Status_Error;
- m_errorString = tr("Failed to parse txid");
+ setStatusError(tr("Failed to parse txid"));
return "";
}
try
{
- m_status = Status_Ok;
+ clearStatus();
return m_wallet->get_spend_proof(txid, message);
}
catch (const std::exception &e)
{
- m_status = Status_Error;
- m_errorString = e.what();
+ setStatusError(e.what());
return "";
}
}
@@ -1611,21 +1689,19 @@ bool WalletImpl::checkSpendProof(const std::string &txid_str, const std::string
crypto::hash txid;
if(!epee::string_tools::hex_to_pod(txid_str, txid))
{
- m_status = Status_Error;
- m_errorString = tr("Failed to parse txid");
+ setStatusError(tr("Failed to parse txid"));
return false;
}
try
{
- m_status = Status_Ok;
+ clearStatus();
good = m_wallet->check_spend_proof(txid, message, signature);
return true;
}
catch (const std::exception &e)
{
- m_status = Status_Error;
- m_errorString = e.what();
+ setStatusError(e.what());
return false;
}
}
@@ -1633,7 +1709,7 @@ bool WalletImpl::checkSpendProof(const std::string &txid_str, const std::string
std::string WalletImpl::getReserveProof(bool all, uint32_t account_index, uint64_t amount, const std::string &message) const {
try
{
- m_status = Status_Ok;
+ clearStatus();
boost::optional<std::pair<uint32_t, uint64_t>> account_minreserve;
if (!all)
{
@@ -1643,8 +1719,7 @@ std::string WalletImpl::getReserveProof(bool all, uint32_t account_index, uint64
}
catch (const std::exception &e)
{
- m_status = Status_Error;
- m_errorString = e.what();
+ setStatusError(e.what());
return "";
}
}
@@ -1653,28 +1728,25 @@ bool WalletImpl::checkReserveProof(const std::string &address, const std::string
cryptonote::address_parse_info info;
if (!cryptonote::get_account_address_from_str(info, m_wallet->nettype(), address))
{
- m_status = Status_Error;
- m_errorString = tr("Failed to parse address");
+ setStatusError(tr("Failed to parse address"));
return false;
}
if (info.is_subaddress)
{
- m_status = Status_Error;
- m_errorString = tr("Address must not be a subaddress");
+ setStatusError(tr("Address must not be a subaddress"));
return false;
}
good = false;
try
{
- m_status = Status_Ok;
+ clearStatus();
good = m_wallet->check_reserve_proof(info.address, message, signature, total, spent);
return true;
}
catch (const std::exception &e)
{
- m_status = Status_Error;
- m_errorString = e.what();
+ setStatusError(e.what());
return false;
}
}
@@ -1697,10 +1769,10 @@ bool WalletImpl::verifySignedMessage(const std::string &message, const std::stri
bool WalletImpl::connectToDaemon()
{
bool result = m_wallet->check_connection(NULL, DEFAULT_CONNECTION_TIMEOUT_MILLIS);
- m_status = result ? Status_Ok : Status_Error;
if (!result) {
- m_errorString = "Error connecting to daemon at " + m_wallet->get_daemon_address();
+ setStatusError("Error connecting to daemon at " + m_wallet->get_daemon_address());
} else {
+ clearStatus();
// start refreshing here
}
return result;
@@ -1735,10 +1807,28 @@ bool WalletImpl::watchOnly() const
void WalletImpl::clearStatus() const
{
+ boost::lock_guard<boost::mutex> l(m_statusMutex);
m_status = Status_Ok;
m_errorString.clear();
}
+void WalletImpl::setStatusError(const std::string& message) const
+{
+ setStatus(Status_Error, message);
+}
+
+void WalletImpl::setStatusCritical(const std::string& message) const
+{
+ setStatus(Status_Critical, message);
+}
+
+void WalletImpl::setStatus(int status, const std::string& message) const
+{
+ boost::lock_guard<boost::mutex> l(m_statusMutex);
+ m_status = status;
+ m_errorString = message;
+}
+
void WalletImpl::refreshThreadFunc()
{
LOG_PRINT_L3(__FUNCTION__ << ": starting refresh thread");
@@ -1760,7 +1850,7 @@ void WalletImpl::refreshThreadFunc()
LOG_PRINT_L3(__FUNCTION__ << ": refresh lock acquired...");
LOG_PRINT_L3(__FUNCTION__ << ": m_refreshEnabled: " << m_refreshEnabled);
- LOG_PRINT_L3(__FUNCTION__ << ": m_status: " << m_status);
+ LOG_PRINT_L3(__FUNCTION__ << ": m_status: " << status());
if (m_refreshEnabled) {
LOG_PRINT_L3(__FUNCTION__ << ": refreshing...");
doRefresh();
@@ -1792,8 +1882,7 @@ void WalletImpl::doRefresh()
LOG_PRINT_L3(__FUNCTION__ << ": skipping refresh - daemon is not synced");
}
} catch (const std::exception &e) {
- m_status = Status_Error;
- m_errorString = e.what();
+ setStatusError(e.what());
}
if (m_wallet2Callback->getListener()) {
m_wallet2Callback->getListener()->refreshed();
@@ -1882,16 +1971,14 @@ bool WalletImpl::rescanSpent()
{
clearStatus();
if (!trustedDaemon()) {
- m_status = Status_Error;
- m_errorString = tr("Rescan spent can only be used with a trusted daemon");
+ setStatusError(tr("Rescan spent can only be used with a trusted daemon"));
return false;
}
try {
m_wallet->rescan_spent();
} catch (const std::exception &e) {
LOG_ERROR(__FUNCTION__ << " error: " << e.what());
- m_status = Status_Error;
- m_errorString = e.what();
+ setStatusError(e.what());
return false;
}
return true;
@@ -1917,8 +2004,7 @@ bool WalletImpl::blackballOutputs(const std::vector<std::string> &pubkeys, bool
crypto::public_key pkey;
if (!epee::string_tools::hex_to_pod(str, pkey))
{
- m_status = Status_Error;
- m_errorString = tr("Failed to parse output public key");
+ setStatusError(tr("Failed to parse output public key"));
return false;
}
raw_pubkeys.push_back(pkey);
@@ -1926,8 +2012,7 @@ bool WalletImpl::blackballOutputs(const std::vector<std::string> &pubkeys, bool
bool ret = m_wallet->set_blackballed_outputs(raw_pubkeys, add);
if (!ret)
{
- m_status = Status_Error;
- m_errorString = tr("Failed to set blackballed outputs");
+ setStatusError(tr("Failed to set blackballed outputs"));
return false;
}
return true;
@@ -1938,15 +2023,13 @@ bool WalletImpl::unblackballOutput(const std::string &pubkey)
crypto::public_key raw_pubkey;
if (!epee::string_tools::hex_to_pod(pubkey, raw_pubkey))
{
- m_status = Status_Error;
- m_errorString = tr("Failed to parse output public key");
+ setStatusError(tr("Failed to parse output public key"));
return false;
}
bool ret = m_wallet->unblackball_output(raw_pubkey);
if (!ret)
{
- m_status = Status_Error;
- m_errorString = tr("Failed to unblackball output");
+ setStatusError(tr("Failed to unblackball output"));
return false;
}
return true;
@@ -1957,15 +2040,13 @@ bool WalletImpl::getRing(const std::string &key_image, std::vector<uint64_t> &ri
crypto::key_image raw_key_image;
if (!epee::string_tools::hex_to_pod(key_image, raw_key_image))
{
- m_status = Status_Error;
- m_errorString = tr("Failed to parse key image");
+ setStatusError(tr("Failed to parse key image"));
return false;
}
bool ret = m_wallet->get_ring(raw_key_image, ring);
if (!ret)
{
- m_status = Status_Error;
- m_errorString = tr("Failed to get ring");
+ setStatusError(tr("Failed to get ring"));
return false;
}
return true;
@@ -1976,16 +2057,14 @@ bool WalletImpl::getRings(const std::string &txid, std::vector<std::pair<std::st
crypto::hash raw_txid;
if (!epee::string_tools::hex_to_pod(txid, raw_txid))
{
- m_status = Status_Error;
- m_errorString = tr("Failed to parse txid");
+ setStatusError(tr("Failed to parse txid"));
return false;
}
std::vector<std::pair<crypto::key_image, std::vector<uint64_t>>> raw_rings;
bool ret = m_wallet->get_rings(raw_txid, raw_rings);
if (!ret)
{
- m_status = Status_Error;
- m_errorString = tr("Failed to get rings");
+ setStatusError(tr("Failed to get rings"));
return false;
}
for (const auto &r: raw_rings)
@@ -2000,15 +2079,13 @@ bool WalletImpl::setRing(const std::string &key_image, const std::vector<uint64_
crypto::key_image raw_key_image;
if (!epee::string_tools::hex_to_pod(key_image, raw_key_image))
{
- m_status = Status_Error;
- m_errorString = tr("Failed to parse key image");
+ setStatusError(tr("Failed to parse key image"));
return false;
}
bool ret = m_wallet->set_ring(raw_key_image, ring, relative);
if (!ret)
{
- m_status = Status_Error;
- m_errorString = tr("Failed to set ring");
+ setStatusError(tr("Failed to set ring"));
return false;
}
return true;
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
index 4929c9673..e0e627c36 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -83,6 +83,7 @@ public:
// void setListener(Listener *) {}
int status() const;
std::string errorString() const;
+ void statusWithErrorString(int& status, std::string& errorString) const override;
bool setPassword(const std::string &password);
std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const;
std::string integratedAddress(const std::string &payment_id) const;
@@ -126,6 +127,14 @@ public:
std::string getSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex) const;
void setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label);
+ MultisigState multisig() const override;
+ std::string getMultisigInfo() const override;
+ std::string makeMultisig(const std::vector<std::string>& info, uint32_t threshold) override;
+ bool finalizeMultisig(const std::vector<std::string>& extraMultisigInfo) override;
+ bool exportMultisigImages(std::string& images) override;
+ size_t importMultisigImages(const std::vector<std::string>& images) override;
+ PendingTransaction* restoreMultisigTransaction(const std::string& signData) override;
+
PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id,
optional<uint64_t> amount, uint32_t mixin_count,
PendingTransaction::Priority priority = PendingTransaction::Priority_Low,
@@ -174,6 +183,9 @@ public:
private:
void clearStatus() const;
+ void setStatusError(const std::string& message) const;
+ void setStatusCritical(const std::string& message) const;
+ void setStatus(int status, const std::string& message) const;
void refreshThreadFunc();
void doRefresh();
bool daemonSynced() const;
@@ -191,7 +203,8 @@ private:
friend class SubaddressAccountImpl;
tools::wallet2 * m_wallet;
- mutable std::atomic<int> m_status;
+ mutable boost::mutex m_statusMutex;
+ mutable int m_status;
mutable std::string m_errorString;
std::string m_password;
TransactionHistoryImpl * m_history;
diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h
index d4e41c5aa..27d290e68 100644
--- a/src/wallet/api/wallet2_api.h
+++ b/src/wallet/api/wallet2_api.h
@@ -100,6 +100,30 @@ struct PendingTransaction
virtual uint64_t txCount() const = 0;
virtual std::vector<uint32_t> subaddrAccount() const = 0;
virtual std::vector<std::set<uint32_t>> subaddrIndices() const = 0;
+
+ /**
+ * @brief multisigSignData
+ * @return encoded multisig transaction with signers' keys.
+ * Transfer this data to another wallet participant to sign it.
+ * Assumed use case is:
+ * 1. Initiator:
+ * auto data = pendingTransaction->multisigSignData();
+ * 2. Signer1:
+ * pendingTransaction = wallet->restoreMultisigTransaction(data);
+ * pendingTransaction->signMultisigTx();
+ * auto signed = pendingTransaction->multisigSignData();
+ * 3. Signer2:
+ * pendingTransaction = wallet->restoreMultisigTransaction(signed);
+ * pendingTransaction->signMultisigTx();
+ * pendingTransaction->commit();
+ */
+ virtual std::string multisigSignData() = 0;
+ virtual void signMultisigTx() = 0;
+ /**
+ * @brief signersKeys
+ * @return vector of base58-encoded signers' public keys
+ */
+ virtual std::vector<std::string> signersKeys() const = 0;
};
/**
@@ -291,6 +315,15 @@ struct SubaddressAccount
virtual void refresh() = 0;
};
+struct MultisigState {
+ MultisigState() : isMultisig(false), isReady(false), threshold(0), total(0) {}
+
+ bool isMultisig;
+ bool isReady;
+ uint32_t threshold;
+ uint32_t total;
+};
+
struct WalletListener
{
virtual ~WalletListener() = 0;
@@ -358,9 +391,11 @@ struct Wallet
virtual std::string getSeedLanguage() const = 0;
virtual void setSeedLanguage(const std::string &arg) = 0;
//! returns wallet status (Status_Ok | Status_Error)
- virtual int status() const = 0;
+ virtual int status() const = 0; //deprecated: use safe alternative statusWithErrorString
//! in case error status, returns error string
- virtual std::string errorString() const = 0;
+ virtual std::string errorString() const = 0; //deprecated: use safe alternative statusWithErrorString
+ //! returns both error and error string atomically. suggested to use in instead of status() and errorString()
+ virtual void statusWithErrorString(int& status, std::string& errorString) const = 0;
virtual bool setPassword(const std::string &password) = 0;
virtual std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const = 0;
std::string mainAddress() const { return address(0, 0); }
@@ -556,7 +591,8 @@ struct Wallet
}
static uint64_t maximumAllowedAmount();
// Easylogger wrapper
- static void init(const char *argv0, const char *default_log_base_name);
+ static void init(const char *argv0, const char *default_log_base_name) { init(argv0, default_log_base_name, "", true); }
+ static void init(const char *argv0, const char *default_log_base_name, const std::string &log_path, bool console);
static void debug(const std::string &category, const std::string &str);
static void info(const std::string &category, const std::string &str);
static void warning(const std::string &category, const std::string &str);
@@ -628,6 +664,48 @@ struct Wallet
*/
virtual void setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label) = 0;
+ /**
+ * @brief multisig - returns current state of multisig wallet creation process
+ * @return MultisigState struct
+ */
+ virtual MultisigState multisig() const = 0;
+ /**
+ * @brief getMultisigInfo
+ * @return serialized and signed multisig info string
+ */
+ virtual std::string getMultisigInfo() const = 0;
+ /**
+ * @brief makeMultisig - switches wallet in multisig state. The one and only creation phase for N / N wallets
+ * @param info - vector of multisig infos from other participants obtained with getMulitisInfo call
+ * @param threshold - number of required signers to make valid transaction. Must be equal to number of participants (N) or N - 1
+ * @return in case of N / N wallets returns empty string since no more key exchanges needed. For N - 1 / N wallets returns base58 encoded extra multisig info
+ */
+ virtual std::string makeMultisig(const std::vector<std::string>& info, uint32_t threshold) = 0;
+ /**
+ * @brief finalizeMultisig - finalizes N - 1 / N multisig wallets creation
+ * @param extraMultisigInfo - wallet participants' extra multisig info obtained with makeMultisig call
+ * @return true if success
+ */
+ virtual bool finalizeMultisig(const std::vector<std::string>& extraMultisigInfo) = 0;
+ /**
+ * @brief exportMultisigImages - exports transfers' key images
+ * @param images - output paramter for hex encoded array of images
+ * @return true if success
+ */
+ virtual bool exportMultisigImages(std::string& images) = 0;
+ /**
+ * @brief importMultisigImages - imports other participants' multisig images
+ * @param images - array of hex encoded arrays of images obtained with exportMultisigImages
+ * @return number of imported images
+ */
+ virtual size_t importMultisigImages(const std::vector<std::string>& images) = 0;
+
+ /**
+ * @brief restoreMultisigTransaction creates PendingTransaction from signData
+ * @param signData encrypted unsigned transaction. Obtained with PendingTransaction::multisigSignData
+ * @return PendingTransaction
+ */
+ virtual PendingTransaction* restoreMultisigTransaction(const std::string& signData) = 0;
/*!
* \brief createTransaction creates transaction. if dst_addr is an integrated address, payment_id is ignored
* \param dst_addr destination address as string
@@ -956,25 +1034,25 @@ struct WalletManager
virtual void setDaemonAddress(const std::string &address) = 0;
//! returns whether the daemon can be reached, and its version number
- virtual bool connected(uint32_t *version = NULL) const = 0;
+ virtual bool connected(uint32_t *version = NULL) = 0;
//! returns current blockchain height
- virtual uint64_t blockchainHeight() const = 0;
+ virtual uint64_t blockchainHeight() = 0;
//! returns current blockchain target height
- virtual uint64_t blockchainTargetHeight() const = 0;
+ virtual uint64_t blockchainTargetHeight() = 0;
//! returns current network difficulty
- virtual uint64_t networkDifficulty() const = 0;
+ virtual uint64_t networkDifficulty() = 0;
//! returns current mining hash rate (0 if not mining)
- virtual double miningHashRate() const = 0;
+ virtual double miningHashRate() = 0;
//! returns current block target
- virtual uint64_t blockTarget() const = 0;
+ virtual uint64_t blockTarget() = 0;
//! returns true iff mining
- virtual bool isMining() const = 0;
+ virtual bool isMining() = 0;
//! starts mining with the set number of threads
virtual bool startMining(const std::string &address, uint32_t threads = 1, bool background_mining = false, bool ignore_battery = true) = 0;
diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp
index 80f5780b5..a63716576 100644
--- a/src/wallet/api/wallet_manager.cpp
+++ b/src/wallet/api/wallet_manager.cpp
@@ -47,15 +47,6 @@ namespace epee {
unsigned int g_test_dbg_lock_sleep = 0;
}
-namespace {
- template<typename Request, typename Response>
- bool connect_and_invoke(const std::string& address, const std::string& path, const Request& request, Response& response)
- {
- epee::net_utils::http::http_simple_client client{};
- return client.set_server(address, boost::none) && epee::net_utils::invoke_http_json(path, request, response, client);
- }
-}
-
namespace Monero {
Wallet *WalletManagerImpl::createWallet(const std::string &path, const std::string &password,
@@ -193,16 +184,19 @@ std::string WalletManagerImpl::errorString() const
void WalletManagerImpl::setDaemonAddress(const std::string &address)
{
m_daemonAddress = address;
+ if(m_http_client.is_connected())
+ m_http_client.disconnect();
+ m_http_client.set_server(address, boost::none);
}
-bool WalletManagerImpl::connected(uint32_t *version) const
+bool WalletManagerImpl::connected(uint32_t *version)
{
epee::json_rpc::request<cryptonote::COMMAND_RPC_GET_VERSION::request> req_t = AUTO_VAL_INIT(req_t);
epee::json_rpc::response<cryptonote::COMMAND_RPC_GET_VERSION::response, std::string> resp_t = AUTO_VAL_INIT(resp_t);
req_t.jsonrpc = "2.0";
req_t.id = epee::serialization::storage_entry(0);
req_t.method = "get_version";
- if (!connect_and_invoke(m_daemonAddress, "/json_rpc", req_t, resp_t))
+ if (!epee::net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client))
return false;
if (version)
@@ -210,65 +204,65 @@ bool WalletManagerImpl::connected(uint32_t *version) const
return true;
}
-uint64_t WalletManagerImpl::blockchainHeight() const
+uint64_t WalletManagerImpl::blockchainHeight()
{
cryptonote::COMMAND_RPC_GET_INFO::request ireq;
cryptonote::COMMAND_RPC_GET_INFO::response ires;
- if (!connect_and_invoke(m_daemonAddress, "/getinfo", ireq, ires))
+ if (!epee::net_utils::invoke_http_json("/getinfo", ireq, ires, m_http_client))
return 0;
return ires.height;
}
-uint64_t WalletManagerImpl::blockchainTargetHeight() const
+uint64_t WalletManagerImpl::blockchainTargetHeight()
{
cryptonote::COMMAND_RPC_GET_INFO::request ireq;
cryptonote::COMMAND_RPC_GET_INFO::response ires;
- if (!connect_and_invoke(m_daemonAddress, "/getinfo", ireq, ires))
+ if (!epee::net_utils::invoke_http_json("/getinfo", ireq, ires, m_http_client))
return 0;
return ires.target_height >= ires.height ? ires.target_height : ires.height;
}
-uint64_t WalletManagerImpl::networkDifficulty() const
+uint64_t WalletManagerImpl::networkDifficulty()
{
cryptonote::COMMAND_RPC_GET_INFO::request ireq;
cryptonote::COMMAND_RPC_GET_INFO::response ires;
- if (!connect_and_invoke(m_daemonAddress, "/getinfo", ireq, ires))
+ if (!epee::net_utils::invoke_http_json("/getinfo", ireq, ires, m_http_client))
return 0;
return ires.difficulty;
}
-double WalletManagerImpl::miningHashRate() const
+double WalletManagerImpl::miningHashRate()
{
cryptonote::COMMAND_RPC_MINING_STATUS::request mreq;
cryptonote::COMMAND_RPC_MINING_STATUS::response mres;
epee::net_utils::http::http_simple_client http_client;
- if (!connect_and_invoke(m_daemonAddress, "/mining_status", mreq, mres))
+ if (!epee::net_utils::invoke_http_json("/mining_status", mreq, mres, m_http_client))
return 0.0;
if (!mres.active)
return 0.0;
return mres.speed;
}
-uint64_t WalletManagerImpl::blockTarget() const
+uint64_t WalletManagerImpl::blockTarget()
{
cryptonote::COMMAND_RPC_GET_INFO::request ireq;
cryptonote::COMMAND_RPC_GET_INFO::response ires;
- if (!connect_and_invoke(m_daemonAddress, "/getinfo", ireq, ires))
+ if (!epee::net_utils::invoke_http_json("/getinfo", ireq, ires, m_http_client))
return 0;
return ires.target;
}
-bool WalletManagerImpl::isMining() const
+bool WalletManagerImpl::isMining()
{
cryptonote::COMMAND_RPC_MINING_STATUS::request mreq;
cryptonote::COMMAND_RPC_MINING_STATUS::response mres;
- if (!connect_and_invoke(m_daemonAddress, "/mining_status", mreq, mres))
+ if (!epee::net_utils::invoke_http_json("/mining_status", mreq, mres, m_http_client))
return false;
return mres.active;
}
@@ -283,7 +277,7 @@ bool WalletManagerImpl::startMining(const std::string &address, uint32_t threads
mreq.ignore_battery = ignore_battery;
mreq.do_background_mining = background_mining;
- if (!connect_and_invoke(m_daemonAddress, "/start_mining", mreq, mres))
+ if (!epee::net_utils::invoke_http_json("/start_mining", mreq, mres, m_http_client))
return false;
return mres.status == CORE_RPC_STATUS_OK;
}
@@ -293,7 +287,7 @@ bool WalletManagerImpl::stopMining()
cryptonote::COMMAND_RPC_STOP_MINING::request mreq;
cryptonote::COMMAND_RPC_STOP_MINING::response mres;
- if (!connect_and_invoke(m_daemonAddress, "/stop_mining", mreq, mres))
+ if (!epee::net_utils::invoke_http_json("/stop_mining", mreq, mres, m_http_client))
return false;
return mres.status == CORE_RPC_STATUS_OK;
}
diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h
index 409a6d499..26238b658 100644
--- a/src/wallet/api/wallet_manager.h
+++ b/src/wallet/api/wallet_manager.h
@@ -30,6 +30,7 @@
#include "wallet/api/wallet2_api.h"
+#include "net/http_client.h"
#include <string>
namespace Monero {
@@ -69,13 +70,13 @@ public:
std::vector<std::string> findWallets(const std::string &path);
std::string errorString() const;
void setDaemonAddress(const std::string &address);
- bool connected(uint32_t *version = NULL) const;
- uint64_t blockchainHeight() const;
- uint64_t blockchainTargetHeight() const;
- uint64_t networkDifficulty() const;
- double miningHashRate() const;
- uint64_t blockTarget() const;
- bool isMining() const;
+ bool connected(uint32_t *version = NULL);
+ uint64_t blockchainHeight();
+ uint64_t blockchainTargetHeight();
+ uint64_t networkDifficulty();
+ double miningHashRate();
+ uint64_t blockTarget();
+ bool isMining();
bool startMining(const std::string &address, uint32_t threads = 1, bool background_mining = false, bool ignore_battery = true);
bool stopMining();
std::string resolveOpenAlias(const std::string &address, bool &dnssec_valid) const;
@@ -84,6 +85,7 @@ private:
WalletManagerImpl() {}
friend struct WalletManagerFactory;
std::string m_daemonAddress;
+ epee::net_utils::http::http_simple_client m_http_client;
std::string m_errorString;
};
diff --git a/src/wallet/ringdb.cpp b/src/wallet/ringdb.cpp
index 44992520f..3f2634c8b 100644
--- a/src/wallet/ringdb.cpp
+++ b/src/wallet/ringdb.cpp
@@ -190,7 +190,8 @@ namespace tools
{
ringdb::ringdb(std::string filename, const std::string &genesis):
- filename(filename)
+ filename(filename),
+ env(NULL)
{
MDB_txn *txn;
bool tx_active = false;
@@ -227,9 +228,18 @@ ringdb::ringdb(std::string filename, const std::string &genesis):
ringdb::~ringdb()
{
- mdb_dbi_close(env, dbi_rings);
- mdb_dbi_close(env, dbi_blackballs);
- mdb_env_close(env);
+ close();
+}
+
+void ringdb::close()
+{
+ if (env)
+ {
+ mdb_dbi_close(env, dbi_rings);
+ mdb_dbi_close(env, dbi_blackballs);
+ mdb_env_close(env);
+ env = NULL;
+ }
}
bool ringdb::add_rings(const crypto::chacha_key &chacha_key, const cryptonote::transaction_prefix &tx)
diff --git a/src/wallet/ringdb.h b/src/wallet/ringdb.h
index 2bd1ac149..6b4bce124 100644
--- a/src/wallet/ringdb.h
+++ b/src/wallet/ringdb.h
@@ -41,6 +41,7 @@ namespace tools
{
public:
ringdb(std::string filename, const std::string &genesis);
+ void close();
~ringdb();
bool add_rings(const crypto::chacha_key &chacha_key, const cryptonote::transaction_prefix &tx);
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 490c05ad3..3af449455 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -141,13 +141,15 @@ struct options {
const command_line::arg_descriptor<bool> testnet = {"testnet", tools::wallet2::tr("For testnet. Daemon must also be launched with --testnet flag"), false};
const command_line::arg_descriptor<bool> stagenet = {"stagenet", tools::wallet2::tr("For stagenet. Daemon must also be launched with --stagenet flag"), false};
const command_line::arg_descriptor<bool> restricted = {"restricted-rpc", tools::wallet2::tr("Restricts to view-only commands"), false};
- const command_line::arg_descriptor<std::string, false, true> shared_ringdb_dir = {
+ const command_line::arg_descriptor<std::string, false, true, 2> shared_ringdb_dir = {
"shared-ringdb-dir", tools::wallet2::tr("Set shared ring database path"),
get_default_ringdb_path(),
- testnet,
- [](bool testnet, bool defaulted, std::string val)->std::string {
- if (testnet)
+ {{ &testnet, &stagenet }},
+ [](std::array<bool, 2> testnet_stagenet, bool defaulted, std::string val)->std::string {
+ if (testnet_stagenet[0])
return (boost::filesystem::path(val) / "testnet").string();
+ else if (testnet_stagenet[1])
+ return (boost::filesystem::path(val) / "stagenet").string();
return val;
}
};
@@ -2421,7 +2423,7 @@ void wallet2::detach_blockchain(uint64_t height)
// size 1 2 3 4 5 6 7 8 9
// block 0 1 2 3 4 5 6 7 8
// C
- THROW_WALLET_EXCEPTION_IF(height <= m_checkpoints.get_max_height() && m_blockchain.size() > m_checkpoints.get_max_height(),
+ THROW_WALLET_EXCEPTION_IF(height < m_blockchain.offset() && m_blockchain.size() > m_blockchain.offset(),
error::wallet_internal_error, "Daemon claims reorg below last checkpoint");
size_t transfers_detached = 0;
@@ -2628,6 +2630,12 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
value2.SetUint(m_segregation_height);
json.AddMember("segregation_height", value2, json.GetAllocator());
+ value2.SetUint(m_subaddress_lookahead_major);
+ json.AddMember("subaddress_lookahead_major", value2, json.GetAllocator());
+
+ value2.SetUint(m_subaddress_lookahead_minor);
+ json.AddMember("subaddress_lookahead_minor", value2, json.GetAllocator());
+
// Serialize the JSON object
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
@@ -2702,6 +2710,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_segregate_pre_fork_outputs = true;
m_key_reuse_mitigation2 = true;
m_segregation_height = 0;
+ m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR;
+ m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR;
m_key_on_device = false;
}
else if(json.IsObject())
@@ -2826,6 +2836,10 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_key_reuse_mitigation2 = field_key_reuse_mitigation2;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, segregation_height, int, Uint, false, 0);
m_segregation_height = field_segregation_height;
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_major, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MAJOR);
+ m_subaddress_lookahead_major = field_subaddress_lookahead_major;
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_minor, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MINOR);
+ m_subaddress_lookahead_minor = field_subaddress_lookahead_minor;
}
else
{
@@ -2871,6 +2885,7 @@ bool wallet2::verify_password(const epee::wipeable_string& password) const
* \param keys_file_name Keys file to verify password for
* \param password Password to verify
* \param no_spend_key If set = only verify view keys, otherwise also spend keys
+ * \param hwdev The hardware device to use
* \return true if password is correct
*
* for verification only
@@ -2921,9 +2936,10 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip
/*!
* \brief Generates a wallet or restores one.
- * \param wallet_ Name of wallet file
- * \param password Password of wallet file
- * \param multisig_data The multisig restore info and keys
+ * \param wallet_ Name of wallet file
+ * \param password Password of wallet file
+ * \param multisig_data The multisig restore info and keys
+ * \param create_address_file Whether to create an address file
*/
void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password,
const std::string& multisig_data, bool create_address_file)
@@ -3016,12 +3032,13 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
/*!
* \brief Generates a wallet or restores one.
- * \param wallet_ Name of wallet file
- * \param password Password of wallet file
- * \param recovery_param If it is a restore, the recovery key
- * \param recover Whether it is a restore
- * \param two_random Whether it is a non-deterministic wallet
- * \return The secret key of the generated wallet
+ * \param wallet_ Name of wallet file
+ * \param password Password of wallet file
+ * \param recovery_param If it is a restore, the recovery key
+ * \param recover Whether it is a restore
+ * \param two_random Whether it is a non-deterministic wallet
+ * \param create_address_file Whether to create an address file
+ * \return The secret key of the generated wallet
*/
crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password,
const crypto::secret_key& recovery_param, bool recover, bool two_random, bool create_address_file)
@@ -3117,9 +3134,11 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip
/*!
* \brief Creates a watch only wallet from a public address and a view secret key.
-* \param wallet_ Name of wallet file
-* \param password Password of wallet file
-* \param viewkey view secret key
+* \param wallet_ Name of wallet file
+* \param password Password of wallet file
+* \param account_public_address The account's public address
+* \param viewkey view secret key
+* \param create_address_file Whether to create an address file
*/
void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password,
const cryptonote::account_public_address &account_public_address,
@@ -3166,10 +3185,12 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
/*!
* \brief Creates a wallet from a public address and a spend/view secret key pair.
-* \param wallet_ Name of wallet file
-* \param password Password of wallet file
-* \param spendkey spend secret key
-* \param viewkey view secret key
+* \param wallet_ Name of wallet file
+* \param password Password of wallet file
+* \param account_public_address The account's public address
+* \param spendkey spend secret key
+* \param viewkey view secret key
+* \param create_address_file Whether to create an address file
*/
void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password,
const cryptonote::account_public_address &account_public_address,
@@ -3616,8 +3637,9 @@ void wallet2::rewrite(const std::string& wallet_name, const epee::wipeable_strin
}
/*!
* \brief Writes to a file named based on the normal wallet (doesn't generate key, assumes it's already there)
- * \param wallet_name Base name of wallet file
- * \param password Password for wallet file
+ * \param wallet_name Base name of wallet file
+ * \param password Password for wallet file
+ * \param new_keys_filename [OUT] Name of new keys file
*/
void wallet2::write_watch_only_wallet(const std::string& wallet_name, const epee::wipeable_string& password, std::string &new_keys_filename)
{
@@ -3863,6 +3885,11 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
void wallet2::trim_hashchain()
{
uint64_t height = m_checkpoints.get_max_height();
+
+ for (const transfer_details &td: m_transfers)
+ if (td.m_block_height < height)
+ height = td.m_block_height;
+
if (!m_blockchain.empty() && m_blockchain.size() == m_blockchain.offset())
{
MINFO("Fixing empty hashchain");
@@ -4996,7 +5023,7 @@ bool wallet2::save_multisig_tx(const multisig_tx_set &txs, const std::string &fi
return epee::file_io_utils::save_string_to_file(filename, ciphertext);
}
//----------------------------------------------------------------------------------------------------
-std::string wallet2::save_multisig_tx(const std::vector<pending_tx>& ptx_vector)
+wallet2::multisig_tx_set wallet2::make_multisig_tx_set(const std::vector<pending_tx>& ptx_vector) const
{
multisig_tx_set txs;
txs.m_ptx = ptx_vector;
@@ -5008,8 +5035,12 @@ std::string wallet2::save_multisig_tx(const std::vector<pending_tx>& ptx_vector)
}
txs.m_signers.insert(get_multisig_signer_public_key());
+ return txs;
+}
- return save_multisig_tx(txs);
+std::string wallet2::save_multisig_tx(const std::vector<pending_tx>& ptx_vector)
+{
+ return save_multisig_tx(make_multisig_tx_set(ptx_vector));
}
//----------------------------------------------------------------------------------------------------
bool wallet2::save_multisig_tx(const std::vector<pending_tx>& ptx_vector, const std::string &filename)
@@ -5138,7 +5169,7 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto
rct::multisig_out msout = ptx.multisig_sigs.front().msout;
auto sources = sd.sources;
const bool bulletproof = sd.use_rct && (ptx.tx.rct_signatures.type == rct::RCTTypeFullBulletproof || ptx.tx.rct_signatures.type == rct::RCTTypeSimpleBulletproof);
- bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources, sd.splitted_dsts, ptx.change_dts.addr, sd.extra, tx, sd.unlock_time, ptx.tx_key, ptx.additional_tx_keys, sd.use_rct, bulletproof, &msout);
+ bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources, sd.splitted_dsts, ptx.change_dts.addr, sd.extra, tx, sd.unlock_time, ptx.tx_key, ptx.additional_tx_keys, sd.use_rct, bulletproof, &msout, false);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_nettype);
THROW_WALLET_EXCEPTION_IF(get_transaction_prefix_hash (tx) != get_transaction_prefix_hash(ptx.tx),
@@ -5893,9 +5924,10 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
auto end = std::unique(req_t.amounts.begin(), req_t.amounts.end());
req_t.amounts.resize(std::distance(req_t.amounts.begin(), end));
req_t.from_height = std::max<uint64_t>(segregation_fork_height, RECENT_OUTPUT_BLOCKS) - RECENT_OUTPUT_BLOCKS;
+ req_t.to_height = segregation_fork_height + 1;
req_t.cumulative = true;
m_daemon_rpc_mutex.lock();
- bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_distribution", req_t, resp_t, m_http_client, rpc_timeout);
+ bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_distribution", req_t, resp_t, m_http_client, rpc_timeout * 1000);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected");
THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_distribution");
@@ -5912,6 +5944,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
{
THROW_WALLET_EXCEPTION_IF(d.start_height > segregation_fork_height, error::get_output_distribution, "Distribution start_height too high");
THROW_WALLET_EXCEPTION_IF(segregation_fork_height - d.start_height >= d.distribution.size(), error::get_output_distribution, "Distribution size too small");
+ THROW_WALLET_EXCEPTION_IF(segregation_fork_height - RECENT_OUTPUT_BLOCKS - d.start_height >= d.distribution.size(), error::get_output_distribution, "Distribution size too small");
THROW_WALLET_EXCEPTION_IF(segregation_fork_height <= RECENT_OUTPUT_BLOCKS, error::wallet_internal_error, "Fork height too low");
THROW_WALLET_EXCEPTION_IF(segregation_fork_height - RECENT_OUTPUT_BLOCKS < d.start_height, error::get_output_distribution, "Bad start height");
uint64_t till_fork = d.distribution[segregation_fork_height - d.start_height];
@@ -6651,7 +6684,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
LOG_PRINT_L2("Creating supplementary multisig transaction");
cryptonote::transaction ms_tx;
auto sources_copy_copy = sources_copy;
- bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources_copy_copy, splitted_dsts, change_dts.addr, extra, ms_tx, unlock_time,tx_key, additional_tx_keys, true, bulletproof, &msout);
+ bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources_copy_copy, splitted_dsts, change_dts.addr, extra, ms_tx, unlock_time,tx_key, additional_tx_keys, true, bulletproof, &msout, false);
LOG_PRINT_L2("constructed tx, r="<<r);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype);
THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit);
@@ -8123,6 +8156,7 @@ std::vector<size_t> wallet2::select_available_outputs_from_histogram(uint64_t co
req_t.min_count = count;
req_t.max_count = 0;
req_t.unlocked = unlocked;
+ req_t.recent_cutoff = 0;
bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "select_available_outputs_from_histogram");
@@ -8159,6 +8193,8 @@ uint64_t wallet2::get_num_rct_outputs()
req_t.amounts.push_back(0);
req_t.min_count = 0;
req_t.max_count = 0;
+ req_t.unlocked = true;
+ req_t.recent_cutoff = 0;
bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_num_rct_outputs");
@@ -8179,14 +8215,14 @@ const wallet2::transfer_details &wallet2::get_transfer_details(size_t idx) const
std::vector<size_t> wallet2::select_available_unmixable_outputs(bool trusted_daemon)
{
// request all outputs with less than 3 instances
- const size_t min_mixin = use_fork_rules(6, 10) ? 4 : 2; // v6 increases min mixin from 2 to 4
+ const size_t min_mixin = use_fork_rules(7, 10) ? 6 : use_fork_rules(6, 10) ? 4 : 2; // v6 increases min mixin from 2 to 4, v7 to 6
return select_available_outputs_from_histogram(min_mixin + 1, false, true, false, trusted_daemon);
}
//----------------------------------------------------------------------------------------------------
std::vector<size_t> wallet2::select_available_mixable_outputs(bool trusted_daemon)
{
// request all outputs with at least 3 instances, so we can use mixin 2 with
- const size_t min_mixin = use_fork_rules(6, 10) ? 4 : 2; // v6 increases min mixin from 2 to 4
+ const size_t min_mixin = use_fork_rules(7, 10) ? 6 : use_fork_rules(6, 10) ? 4 : 2; // v6 increases min mixin from 2 to 4, v7 to 6
return select_available_outputs_from_histogram(min_mixin + 1, true, true, true, trusted_daemon);
}
//----------------------------------------------------------------------------------------------------
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 69b63876a..5e0dd076b 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -477,21 +477,23 @@ namespace tools
bool two_random = false, bool create_address_file = false);
/*!
* \brief Creates a wallet from a public address and a spend/view secret key pair.
- * \param wallet_ Name of wallet file
- * \param password Password of wallet file
- * \param viewkey view secret key
- * \param spendkey spend secret key
- * \param create_address_file Whether to create an address file
+ * \param wallet_ Name of wallet file
+ * \param password Password of wallet file
+ * \param account_public_address The account's public address
+ * \param spendkey spend secret key
+ * \param viewkey view secret key
+ * \param create_address_file Whether to create an address file
*/
void generate(const std::string& wallet, const epee::wipeable_string& password,
const cryptonote::account_public_address &account_public_address,
const crypto::secret_key& spendkey, const crypto::secret_key& viewkey, bool create_address_file = false);
/*!
* \brief Creates a watch only wallet from a public address and a view secret key.
- * \param wallet_ Name of wallet file
- * \param password Password of wallet file
- * \param viewkey view secret key
- * \param create_address_file Whether to create an address file
+ * \param wallet_ Name of wallet file
+ * \param password Password of wallet file
+ * \param account_public_address The account's public address
+ * \param viewkey view secret key
+ * \param create_address_file Whether to create an address file
*/
void generate(const std::string& wallet, const epee::wipeable_string& password,
const cryptonote::account_public_address &account_public_address,
@@ -561,9 +563,9 @@ namespace tools
void load(const std::string& wallet, const epee::wipeable_string& password);
void store();
/*!
- * \brief store_to - stores wallet to another file(s), deleting old ones
- * \param path - path to the wallet file (keys and address filenames will be generated based on this filename)
- * \param password - password to protect new wallet (TODO: probably better save the password in the wallet object?)
+ * \brief store_to Stores wallet to another file(s), deleting old ones
+ * \param path Path to the wallet file (keys and address filenames will be generated based on this filename)
+ * \param password Password to protect new wallet (TODO: probably better save the password in the wallet object?)
*/
void store_to(const std::string &path, const epee::wipeable_string &password);
@@ -686,6 +688,7 @@ namespace tools
bool save_multisig_tx(const multisig_tx_set &txs, const std::string &filename);
std::string save_multisig_tx(const std::vector<pending_tx>& ptx_vector);
bool save_multisig_tx(const std::vector<pending_tx>& ptx_vector, const std::string &filename);
+ multisig_tx_set make_multisig_tx_set(const std::vector<pending_tx>& ptx_vector) const;
// load unsigned tx from file and sign it. Takes confirmation callback as argument. Used by the cli wallet
bool sign_tx(const std::string &unsigned_filename, const std::string &signed_filename, std::vector<wallet2::pending_tx> &ptx, std::function<bool(const unsigned_tx_set&)> accept_func = NULL, bool export_raw = false);
// sign unsigned tx. Takes unsigned_tx_set as argument. Used by GUI
@@ -959,7 +962,7 @@ namespace tools
/*!
* \brief Set the label of the given tag.
* \param tag Tag's name (which must be non-empty).
- * \param label Tag's description.
+ * \param description Tag's description.
*/
void set_account_tag_description(const std::string& tag, const std::string& description);