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.h42
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp413
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.h26
-rw-r--r--src/blockchain_utilities/CMakeLists.txt4
-rw-r--r--src/blockchain_utilities/blockchain_blackball.cpp175
-rw-r--r--src/blockchain_utilities/blockchain_import.cpp29
-rw-r--r--src/blockchain_utilities/blockchain_usage.cpp15
-rw-r--r--src/blocks/checkpoints.datbin191524 -> 198276 bytes
-rw-r--r--src/checkpoints/checkpoints.cpp2
-rw-r--r--src/common/CMakeLists.txt1
-rw-r--r--src/common/dns_utils.cpp2
-rw-r--r--src/common/password.cpp2
-rw-r--r--src/common/threadpool.cpp43
-rw-r--r--src/common/threadpool.h21
-rw-r--r--src/common/util.cpp237
-rw-r--r--src/common/util.h19
-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/account.cpp2
-rw-r--r--src/cryptonote_basic/connection_context.h12
-rw-r--r--src/cryptonote_basic/cryptonote_basic_impl.cpp19
-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_basic/hardfork.cpp18
-rw-r--r--src/cryptonote_basic/miner.cpp5
-rw-r--r--src/cryptonote_config.h56
-rw-r--r--src/cryptonote_core/blockchain.cpp312
-rw-r--r--src/cryptonote_core/blockchain.h49
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp113
-rw-r--r--src/cryptonote_core/cryptonote_core.h59
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.cpp15
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.h4
-rw-r--r--src/cryptonote_core/tx_pool.cpp106
-rw-r--r--src/cryptonote_core/tx_pool.h10
-rw-r--r--src/cryptonote_protocol/block_queue.cpp18
-rw-r--r--src/cryptonote_protocol/block_queue.h20
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_defs.h16
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.inl100
-rw-r--r--src/daemon/command_line_args.h5
-rw-r--r--src/daemon/command_parser_executor.cpp34
-rw-r--r--src/daemon/command_parser_executor.h2
-rw-r--r--src/daemon/command_server.cpp5
-rw-r--r--src/daemon/main.cpp51
-rw-r--r--src/daemon/rpc_command_executor.cpp20
-rw-r--r--src/debug_utilities/cn_deserialize.cpp3
-rw-r--r--src/debug_utilities/object_sizes.cpp5
-rw-r--r--src/device/device.hpp1
-rw-r--r--src/device/device_default.cpp4
-rw-r--r--src/device/device_default.hpp1
-rw-r--r--src/device/device_ledger.cpp47
-rw-r--r--src/device/device_ledger.hpp6
-rw-r--r--src/device/log.cpp12
-rw-r--r--src/device/log.hpp8
-rw-r--r--src/gen_multisig/gen_multisig.cpp6
-rw-r--r--src/mnemonics/electrum-words.cpp28
-rw-r--r--src/p2p/net_node.h7
-rw-r--r--src/p2p/net_node.inl102
-rw-r--r--src/ringct/rctOps.cpp5
-rw-r--r--src/ringct/rctOps.h1
-rw-r--r--src/ringct/rctSigs.cpp22
-rw-r--r--src/rpc/core_rpc_server.cpp171
-rw-r--r--src/rpc/core_rpc_server.h2
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h43
-rw-r--r--src/rpc/daemon_handler.cpp38
-rw-r--r--src/rpc/daemon_messages.h2
-rw-r--r--src/rpc/get_output_distribution_cache.h113
-rw-r--r--src/rpc/rpc_args.cpp2
-rw-r--r--src/rpc/zmq_server.cpp4
-rw-r--r--src/simplewallet/simplewallet.cpp427
-rw-r--r--src/simplewallet/simplewallet.h4
-rw-r--r--src/version.cpp.in2
-rw-r--r--src/wallet/api/address_book.h14
-rw-r--r--src/wallet/api/pending_transaction.cpp53
-rw-r--r--src/wallet/api/pending_transaction.h25
-rw-r--r--src/wallet/api/subaddress.h8
-rw-r--r--src/wallet/api/transaction_info.h30
-rw-r--r--src/wallet/api/unsigned_transaction.h23
-rw-r--r--src/wallet/api/wallet.cpp726
-rw-r--r--src/wallet/api/wallet.h202
-rw-r--r--src/wallet/api/wallet2_api.h151
-rw-r--r--src/wallet/api/wallet_manager.cpp64
-rw-r--r--src/wallet/api/wallet_manager.h52
-rw-r--r--src/wallet/ringdb.cpp18
-rw-r--r--src/wallet/ringdb.h1
-rw-r--r--src/wallet/wallet2.cpp1090
-rw-r--r--src/wallet/wallet2.h143
-rw-r--r--src/wallet/wallet_args.cpp24
-rw-r--r--src/wallet/wallet_args.h7
-rw-r--r--src/wallet/wallet_rpc_server.cpp269
-rw-r--r--src/wallet/wallet_rpc_server.h12
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h130
-rw-r--r--src/wallet/wallet_rpc_server_error_codes.h4
97 files changed, 4652 insertions, 1719 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..564016fc9 100644
--- a/src/blockchain_db/blockchain_db.h
+++ b/src/blockchain_db/blockchain_db.h
@@ -148,6 +148,7 @@ struct txpool_tx_meta_t
uint8_t relayed;
uint8_t do_not_relay;
uint8_t double_spend_seen: 1;
+ uint8_t bf_padding: 7;
uint8_t padding[76]; // till 192 bytes
};
@@ -398,9 +399,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 +528,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 +1123,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 +1456,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 +1521,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..367bfa49e 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);
@@ -1130,6 +1178,9 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
throw DB_ERROR("Database could not be opened");
}
+ if (tools::is_hdd(filename.c_str()))
+ MCLOG_RED(el::Level::Warning, "global", "The blockchain is on a rotating drive: this will be very slow, use a SSD if possible");
+
m_folder = filename;
#ifdef __OpenBSD__
@@ -1199,6 +1250,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");
@@ -1252,20 +1306,21 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
auto get_result = mdb_get(txn, m_properties, &k, &v);
if(get_result == MDB_SUCCESS)
{
- if (*(const uint32_t*)v.mv_data > VERSION)
+ const uint32_t db_version = *(const uint32_t*)v.mv_data;
+ if (db_version > VERSION)
{
- MWARNING("Existing lmdb database was made by a later version. We don't know how it will change yet.");
+ MWARNING("Existing lmdb database was made by a later version (" << db_version << "). We don't know how it will change yet.");
compatible = false;
}
#if VERSION > 0
- else if (*(const uint32_t*)v.mv_data < VERSION)
+ else if (db_version < VERSION)
{
// Note that there was a schema change within version 0 as well.
// See commit e5d2680094ee15889934fe28901e4e133cda56f2 2015/07/10
// We don't handle the old format previous to that commit.
txn.commit();
m_open = true;
- migrate(*(const uint32_t *)v.mv_data);
+ migrate(db_version);
return;
}
#endif
@@ -1346,6 +1401,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 +1420,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 +2123,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 +2134,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 +2145,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 +2210,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 +2255,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 +2269,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 +2308,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 +2385,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 +2404,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 +2414,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 +2633,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 +2661,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 +3217,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 +3243,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 +3255,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 +3310,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 +3442,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 +3453,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 +3978,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/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt
index a5dd69556..338ec3e4b 100644
--- a/src/blockchain_utilities/CMakeLists.txt
+++ b/src/blockchain_utilities/CMakeLists.txt
@@ -100,7 +100,6 @@ target_link_libraries(blockchain_import
PRIVATE
cryptonote_core
blockchain_db
- p2p
version
epee
${Boost_FILESYSTEM_LIBRARY}
@@ -127,7 +126,6 @@ target_link_libraries(blockchain_export
PRIVATE
cryptonote_core
blockchain_db
- p2p
version
epee
${Boost_FILESYSTEM_LIBRARY}
@@ -150,7 +148,6 @@ target_link_libraries(blockchain_blackball
wallet
cryptonote_core
blockchain_db
- p2p
version
epee
${Boost_FILESYSTEM_LIBRARY}
@@ -173,7 +170,6 @@ target_link_libraries(blockchain_usage
PRIVATE
cryptonote_core
blockchain_db
- p2p
version
epee
${Boost_FILESYSTEM_LIBRARY}
diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp
index 40ce898d9..52d2938cd 100644
--- a/src/blockchain_utilities/blockchain_blackball.cpp
+++ b/src/blockchain_utilities/blockchain_blackball.cpp
@@ -28,8 +28,13 @@
#include <boost/range/adaptor/transformed.hpp>
#include <boost/algorithm/string.hpp>
+#include <boost/archive/portable_binary_iarchive.hpp>
+#include <boost/archive/portable_binary_oarchive.hpp>
+#include "common/unordered_containers_boost_serialization.h"
#include "common/command_line.h"
#include "common/varint.h"
+#include "serialization/crypto.h"
+#include "cryptonote_basic/cryptonote_boost_serialization.h"
#include "cryptonote_core/tx_pool.h"
#include "cryptonote_core/cryptonote_core.h"
#include "cryptonote_core/blockchain.h"
@@ -49,9 +54,17 @@ struct output_data
{
uint64_t amount;
uint64_t index;
+ output_data(): amount(0), index(0) {}
output_data(uint64_t a, uint64_t i): amount(a), index(i) {}
bool operator==(const output_data &other) const { return other.amount == amount && other.index == index; }
+ template <typename t_archive> void serialize(t_archive &a, const unsigned int ver)
+ {
+ a & amount;
+ a & index;
+ }
};
+BOOST_CLASS_VERSION(output_data, 0)
+
namespace std
{
template<> struct hash<output_data>
@@ -64,8 +77,38 @@ namespace std
return reinterpret_cast<const std::size_t &>(h);
}
};
+ template<> struct hash<std::vector<uint64_t>>
+ {
+ size_t operator()(const std::vector<uint64_t> &v) const
+ {
+ crypto::hash h;
+ crypto::cn_fast_hash(v.data(), v.size() * sizeof(uint64_t), h);
+ return reinterpret_cast<const std::size_t &>(h);
+ }
+ };
}
+struct blackball_state_t
+{
+ std::unordered_map<crypto::key_image, std::vector<uint64_t>> relative_rings;
+ std::unordered_map<output_data, std::unordered_set<crypto::key_image>> outputs;
+ std::unordered_map<std::string, uint64_t> processed_heights;
+ std::unordered_set<output_data> spent;
+ std::unordered_map<std::vector<uint64_t>, size_t> ring_instances;
+
+ template <typename t_archive> void serialize(t_archive &a, const unsigned int ver)
+ {
+ a & relative_rings;
+ a & outputs;
+ a & processed_heights;
+ a & spent;
+ if (ver < 1)
+ return;
+ a & ring_instances;
+ }
+};
+BOOST_CLASS_VERSION(blackball_state_t, 1)
+
static std::string get_default_db_path()
{
boost::filesystem::path dir = tools::get_default_data_dir();
@@ -75,7 +118,7 @@ static std::string get_default_db_path()
return dir.string();
}
-static bool for_all_transactions(const std::string &filename, const std::function<bool(const cryptonote::transaction_prefix&)> &f)
+static bool for_all_transactions(const std::string &filename, uint64_t &start_idx, const std::function<bool(const cryptonote::transaction_prefix&)> &f)
{
MDB_env *env;
MDB_dbi dbi;
@@ -109,7 +152,9 @@ static bool for_all_transactions(const std::string &filename, const std::functio
MDB_val v;
bool fret = true;
- MDB_cursor_op op = MDB_FIRST;
+ k.mv_size = sizeof(uint64_t);
+ k.mv_data = &start_idx;
+ MDB_cursor_op op = MDB_SET;
while (1)
{
int ret = mdb_cursor_get(cur, &k, &v, op);
@@ -119,6 +164,12 @@ static bool for_all_transactions(const std::string &filename, const std::functio
if (ret)
throw std::runtime_error("Failed to enumerate transactions: " + std::string(mdb_strerror(ret)));
+ if (k.mv_size != sizeof(uint64_t))
+ throw std::runtime_error("Bad key size");
+ const uint64_t idx = *(uint64_t*)k.mv_data;
+ if (idx < start_idx)
+ continue;
+
cryptonote::transaction_prefix tx;
blobdata bd;
bd.assign(reinterpret_cast<char*>(v.mv_data), v.mv_size);
@@ -128,6 +179,7 @@ static bool for_all_transactions(const std::string &filename, const std::functio
bool r = do_serialize(ba, tx);
CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob");
+ start_idx = *(uint64_t*)k.mv_data;
if (!f(tx)) {
fret = false;
break;
@@ -142,6 +194,24 @@ static bool for_all_transactions(const std::string &filename, const std::functio
return fret;
}
+static std::vector<uint64_t> canonicalize(const std::vector<uint64_t> &v)
+{
+ std::vector<uint64_t> c;
+ c.reserve(v.size());
+ c.push_back(v[0]);
+ for (size_t n = 1; n < v.size(); ++n)
+ {
+ if (v[n] != 0)
+ c.push_back(v[n]);
+ }
+ if (c.size() < v.size())
+ {
+ MINFO("Ring has duplicate member(s): " <<
+ boost::join(v | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " "));
+ }
+ return c;
+}
+
int main(int argc, char* argv[])
{
TRY_ENTRY();
@@ -165,7 +235,7 @@ int main(int argc, char* argv[])
"blackball-db-dir", "Specify blackball database directory",
get_default_db_path(),
{{ &arg_testnet_on, &arg_stagenet_on }},
- [](std::array<bool, 2> testnet_stagenet, bool defaulted, std::string val) {
+ [](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])
@@ -253,7 +323,8 @@ int main(int argc, char* argv[])
return 1;
}
std::vector<std::unique_ptr<Blockchain>> core_storage(inputs.size());
- tx_memory_pool m_mempool(*(Blockchain*)NULL);
+ Blockchain *blockchain = NULL;
+ tx_memory_pool m_mempool(*blockchain);
for (size_t n = 0; n < inputs.size(); ++n)
{
core_storage[n].reset(new Blockchain(m_mempool));
@@ -307,17 +378,41 @@ int main(int argc, char* argv[])
LOG_PRINT_L0("Scanning for blackballable outputs...");
size_t done = 0;
- std::unordered_map<crypto::key_image, std::vector<uint64_t>> relative_rings;
- std::unordered_map<output_data, std::unordered_set<crypto::key_image>> outputs;
- std::unordered_set<output_data> spent, newly_spent;
+ blackball_state_t state;
+ std::unordered_set<output_data> newly_spent;
+ const std::string state_file_path = (boost::filesystem::path(output_file_path) / "blackball-state.bin").string();
+
+ LOG_PRINT_L0("Loading state data from " << state_file_path);
+ std::ifstream state_data_in;
+ state_data_in.open(state_file_path, std::ios_base::binary | std::ios_base::in);
+ if (!state_data_in.fail())
+ {
+ try
+ {
+ boost::archive::portable_binary_iarchive a(state_data_in);
+ a >> state;
+ }
+ catch (const std::exception &e)
+ {
+ MERROR("Failed to load state data from " << state_file_path << ", restarting from scratch");
+ state = blackball_state_t();
+ }
+ state_data_in.close();
+ }
+ uint64_t start_blackballed_outputs = state.spent.size();
cryptonote::block b = core_storage[0]->get_db().get_block_from_height(0);
tools::ringdb ringdb(output_file_path.string(), epee::string_tools::pod_to_hex(get_block_hash(b)));
for (size_t n = 0; n < inputs.size(); ++n)
{
- LOG_PRINT_L0("Reading blockchain from " << inputs[n]);
- for_all_transactions(inputs[n], [&](const cryptonote::transaction_prefix &tx)->bool
+ const std::string canonical = boost::filesystem::canonical(inputs[n]).string();
+ uint64_t start_idx = 0;
+ auto it = state.processed_heights.find(canonical);
+ if (it != state.processed_heights.end())
+ start_idx = it->second;
+ LOG_PRINT_L0("Reading blockchain from " << inputs[n] << " from " << start_idx);
+ for_all_transactions(inputs[n], start_idx, [&](const cryptonote::transaction_prefix &tx)->bool
{
for (const auto &in: tx.vin)
{
@@ -330,27 +425,39 @@ int main(int argc, char* argv[])
const std::vector<uint64_t> absolute = cryptonote::relative_output_offsets_to_absolute(txin.key_offsets);
if (n == 0)
for (uint64_t out: absolute)
- outputs[output_data(txin.amount, out)].insert(txin.k_image);
+ state.outputs[output_data(txin.amount, out)].insert(txin.k_image);
- std::vector<uint64_t> new_ring = txin.key_offsets;
+ std::vector<uint64_t> new_ring = canonicalize(txin.key_offsets);
const uint32_t ring_size = txin.key_offsets.size();
+ state.ring_instances[new_ring] += 1;
if (ring_size == 1)
{
- const crypto::public_key pkey = core_storage[n]->get_output_key(txin.amount, txin.key_offsets[0]);
+ const crypto::public_key pkey = core_storage[n]->get_output_key(txin.amount, absolute[0]);
MINFO("Blackballing output " << pkey << ", due to being used in a 1-ring");
ringdb.blackball(pkey);
- newly_spent.insert(output_data(txin.amount, txin.key_offsets[0]));
- spent.insert(output_data(txin.amount, txin.key_offsets[0]));
+ newly_spent.insert(output_data(txin.amount, absolute[0]));
+ state.spent.insert(output_data(txin.amount, absolute[0]));
}
- else if (relative_rings.find(txin.k_image) != relative_rings.end())
+ else if (state.ring_instances[new_ring] == new_ring.size())
+ {
+ for (size_t o = 0; o < new_ring.size(); ++o)
+ {
+ const crypto::public_key pkey = core_storage[n]->get_output_key(txin.amount, absolute[o]);
+ MINFO("Blackballing output " << pkey << ", due to being used in " << new_ring.size() << " identical " << new_ring.size() << "-rings");
+ ringdb.blackball(pkey);
+ newly_spent.insert(output_data(txin.amount, absolute[o]));
+ state.spent.insert(output_data(txin.amount, absolute[o]));
+ }
+ }
+ else if (state.relative_rings.find(txin.k_image) != state.relative_rings.end())
{
MINFO("Key image " << txin.k_image << " already seen: rings " <<
- boost::join(relative_rings[txin.k_image] | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " ") <<
+ boost::join(state.relative_rings[txin.k_image] | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " ") <<
", " << boost::join(txin.key_offsets | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " "));
- if (relative_rings[txin.k_image] != txin.key_offsets)
+ if (state.relative_rings[txin.k_image] != txin.key_offsets)
{
MINFO("Rings are different");
- const std::vector<uint64_t> r0 = cryptonote::relative_output_offsets_to_absolute(relative_rings[txin.k_image]);
+ const std::vector<uint64_t> r0 = cryptonote::relative_output_offsets_to_absolute(state.relative_rings[txin.k_image]);
const std::vector<uint64_t> r1 = cryptonote::relative_output_offsets_to_absolute(txin.key_offsets);
std::vector<uint64_t> common;
for (uint64_t out: r0)
@@ -368,7 +475,7 @@ int main(int argc, char* argv[])
MINFO("Blackballing output " << pkey << ", due to being used in rings with a single common element");
ringdb.blackball(pkey);
newly_spent.insert(output_data(txin.amount, common[0]));
- spent.insert(output_data(txin.amount, common[0]));
+ state.spent.insert(output_data(txin.amount, common[0]));
}
else
{
@@ -380,10 +487,11 @@ int main(int argc, char* argv[])
}
}
}
- relative_rings[txin.k_image] = new_ring;
+ state.relative_rings[txin.k_image] = new_ring;
}
return true;
});
+ state.processed_heights[canonical] = start_idx;
}
while (!newly_spent.empty())
@@ -394,15 +502,15 @@ int main(int argc, char* argv[])
for (const output_data &od: work_spent)
{
- for (const crypto::key_image &ki: outputs[od])
+ for (const crypto::key_image &ki: state.outputs[od])
{
- std::vector<uint64_t> absolute = cryptonote::relative_output_offsets_to_absolute(relative_rings[ki]);
+ std::vector<uint64_t> absolute = cryptonote::relative_output_offsets_to_absolute(state.relative_rings[ki]);
size_t known = 0;
uint64_t last_unknown = 0;
for (uint64_t out: absolute)
{
output_data new_od(od.amount, out);
- if (spent.find(new_od) != spent.end())
+ if (state.spent.find(new_od) != state.spent.end())
++known;
else
last_unknown = out;
@@ -414,12 +522,31 @@ int main(int argc, char* argv[])
absolute.size() << "-ring where all other outputs are known to be spent");
ringdb.blackball(pkey);
newly_spent.insert(output_data(od.amount, last_unknown));
- spent.insert(output_data(od.amount, last_unknown));
+ state.spent.insert(output_data(od.amount, last_unknown));
}
}
}
}
+ LOG_PRINT_L0("Saving state data to " << state_file_path);
+ std::ofstream state_data_out;
+ state_data_out.open(state_file_path, std::ios_base::binary | std::ios_base::out | std::ios::trunc);
+ if (!state_data_out.fail())
+ {
+ try
+ {
+ boost::archive::portable_binary_oarchive a(state_data_out);
+ a << state;
+ }
+ catch (const std::exception &e)
+ {
+ MERROR("Failed to save state data to " << state_file_path);
+ }
+ state_data_out.close();
+ }
+
+ uint64_t diff = state.spent.size() - start_blackballed_outputs;
+ LOG_PRINT_L0(std::to_string(diff) << " new outputs blackballed, " << state.spent.size() << " total outputs blackballed");
LOG_PRINT_L0("Blockchain blackball data exported OK");
return 0;
diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp
index caa549c13..7291dbd68 100644
--- a/src/blockchain_utilities/blockchain_import.cpp
+++ b/src/blockchain_utilities/blockchain_import.cpp
@@ -33,6 +33,7 @@
#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
+#include <unistd.h>
#include "misc_log_ex.h"
#include "bootstrap_file.h"
#include "bootstrap_serialization.h"
@@ -164,7 +165,7 @@ int pop_blocks(cryptonote::core& core, int num_blocks)
return num_blocks;
}
-int check_flush(cryptonote::core &core, std::list<block_complete_entry> &blocks, bool force)
+int check_flush(cryptonote::core &core, std::vector<block_complete_entry> &blocks, bool force)
{
if (blocks.empty())
return 0;
@@ -176,7 +177,7 @@ int check_flush(cryptonote::core &core, std::list<block_complete_entry> &blocks,
if (!force && new_height % HASH_OF_HASHES_STEP)
return 0;
- std::list<crypto::hash> hashes;
+ std::vector<crypto::hash> hashes;
for (const auto &b: blocks)
{
cryptonote::block block;
@@ -312,7 +313,7 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path
MINFO("Reading blockchain from bootstrap file...");
std::cout << ENDL;
- std::list<block_complete_entry> blocks;
+ std::vector<block_complete_entry> blocks;
// Skip to start_height before we start adding.
{
@@ -437,7 +438,7 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path
{
cryptonote::blobdata block;
cryptonote::block_to_blob(bp.block, block);
- std::list<cryptonote::blobdata> txs;
+ std::vector<cryptonote::blobdata> txs;
for (const auto &tx: bp.txs)
{
txs.push_back(cryptonote::blobdata());
@@ -593,8 +594,8 @@ int main(int argc, char* argv[])
const command_line::arg_descriptor<std::string> arg_database = {
"database", available_dbs.c_str(), default_db_type
};
- const command_line::arg_descriptor<bool> arg_verify = {"guard-against-pwnage",
- "Verify blocks and transactions during import (only disable if you exported the file yourself)", true};
+ const command_line::arg_descriptor<bool> arg_noverify = {"dangerous-unverified-import",
+ "Blindly trust the import file and use potentially malicious blocks and transactions during import (only enable if you exported the file yourself)", false};
const command_line::arg_descriptor<bool> arg_batch = {"batch",
"Batch transactions for faster import", true};
const command_line::arg_descriptor<bool> arg_resume = {"resume",
@@ -614,7 +615,7 @@ int main(int argc, char* argv[])
// call add_options() directly for these arguments since
// command_line helpers support only boolean switch, not boolean argument
desc_cmd_sett.add_options()
- (arg_verify.name, make_semantic(arg_verify), arg_verify.description)
+ (arg_noverify.name, make_semantic(arg_noverify), arg_noverify.description)
(arg_batch.name, make_semantic(arg_batch), arg_batch.description)
(arg_resume.name, make_semantic(arg_resume), arg_resume.description)
;
@@ -633,7 +634,7 @@ int main(int argc, char* argv[])
if (! r)
return 1;
- opt_verify = command_line::get_arg(vm, arg_verify);
+ opt_verify = !command_line::get_arg(vm, arg_noverify);
opt_batch = command_line::get_arg(vm, arg_batch);
opt_resume = command_line::get_arg(vm, arg_resume);
block_stop = command_line::get_arg(vm, arg_block_stop);
@@ -738,6 +739,18 @@ int main(int argc, char* argv[])
MINFO("bootstrap file path: " << import_file_path);
MINFO("database path: " << m_config_folder);
+ if (!opt_verify)
+ {
+ MCLOG_RED(el::Level::Warning, "global", "\n"
+ "Import is set to proceed WITHOUT VERIFICATION.\n"
+ "This is a DANGEROUS operation: if the file was tampered with in transit, or obtained from a malicious source,\n"
+ "you could end up with a compromised database. It is recommended to NOT use " << arg_noverify.name << ".\n"
+ "*****************************************************************************************\n"
+ "You have 90 seconds to press ^C or terminate this program before unverified import starts\n"
+ "*****************************************************************************************");
+ sleep(90);
+ }
+
cryptonote::cryptonote_protocol_stub pr; //TODO: stub only for this kind of test, make real validation of relayed objects
cryptonote::core core(&pr);
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..085558504 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/dns_utils.cpp b/src/common/dns_utils.cpp
index 1ecdae8ec..33f60bc3c 100644
--- a/src/common/dns_utils.cpp
+++ b/src/common/dns_utils.cpp
@@ -230,7 +230,7 @@ DNSResolver::DNSResolver() : m_data(new DNSResolverData())
if (use_dns_public)
{
for (const auto &ip: dns_public_addr)
- ub_ctx_set_fwd(m_data->m_ub_context, ip.c_str());
+ ub_ctx_set_fwd(m_data->m_ub_context, string_copy(ip.c_str()));
ub_ctx_set_option(m_data->m_ub_context, string_copy("do-udp:"), string_copy("no"));
ub_ctx_set_option(m_data->m_ub_context, string_copy("do-tcp:"), string_copy("yes"));
}
diff --git a/src/common/password.cpp b/src/common/password.cpp
index 9336a14fc..3ce2ba42a 100644
--- a/src/common/password.cpp
+++ b/src/common/password.cpp
@@ -164,7 +164,7 @@ namespace
while (true)
{
if (message)
- std::cout << message <<": ";
+ std::cout << message <<": " << std::flush;
if (!read_from_tty(pass1))
return false;
if (verify)
diff --git a/src/common/threadpool.cpp b/src/common/threadpool.cpp
index 51e071577..6b69e2a12 100644
--- a/src/common/threadpool.cpp
+++ b/src/common/threadpool.cpp
@@ -36,16 +36,17 @@
#include "common/util.h"
static __thread int depth = 0;
+static __thread bool is_leaf = false;
namespace tools
{
-threadpool::threadpool() : running(true), active(0) {
+threadpool::threadpool(unsigned int max_threads) : running(true), active(0) {
boost::thread::attributes attrs;
attrs.set_stack_size(THREAD_STACK_SIZE);
- max = tools::get_max_concurrency();
- size_t i = max;
+ max = max_threads ? max_threads : tools::get_max_concurrency();
+ size_t i = max ? max - 1 : 0;
while(i--) {
- threads.push_back(boost::thread(attrs, boost::bind(&threadpool::run, this)));
+ threads.push_back(boost::thread(attrs, boost::bind(&threadpool::run, this, false)));
}
}
@@ -60,25 +61,30 @@ threadpool::~threadpool() {
}
}
-void threadpool::submit(waiter *obj, std::function<void()> f) {
- entry e = {obj, f};
+void threadpool::submit(waiter *obj, std::function<void()> f, bool leaf) {
+ CHECK_AND_ASSERT_THROW_MES(!is_leaf, "A leaf routine is using a thread pool");
boost::unique_lock<boost::mutex> lock(mutex);
- if ((active == max && !queue.empty()) || depth > 0) {
+ if (!leaf && ((active == max && !queue.empty()) || depth > 0)) {
// if all available threads are already running
// and there's work waiting, just run in current thread
lock.unlock();
++depth;
+ is_leaf = leaf;
f();
--depth;
+ is_leaf = false;
} else {
if (obj)
obj->inc();
- queue.push_back(e);
+ if (leaf)
+ queue.push_front({obj, f, leaf});
+ else
+ queue.push_back({obj, f, leaf});
has_work.notify_one();
}
}
-int threadpool::get_max_concurrency() {
+unsigned int threadpool::get_max_concurrency() const {
return max;
}
@@ -91,7 +97,7 @@ threadpool::waiter::~waiter()
}
try
{
- wait();
+ wait(NULL);
}
catch (const std::exception &e)
{
@@ -99,9 +105,12 @@ threadpool::waiter::~waiter()
}
}
-void threadpool::waiter::wait() {
+void threadpool::waiter::wait(threadpool *tpool) {
+ if (tpool)
+ tpool->run(true);
boost::unique_lock<boost::mutex> lock(mt);
- while(num) cv.wait(lock);
+ while(num)
+ cv.wait(lock);
}
void threadpool::waiter::inc() {
@@ -113,15 +122,19 @@ void threadpool::waiter::dec() {
const boost::unique_lock<boost::mutex> lock(mt);
num--;
if (!num)
- cv.notify_one();
+ cv.notify_all();
}
-void threadpool::run() {
+void threadpool::run(bool flush) {
boost::unique_lock<boost::mutex> lock(mutex);
while (running) {
entry e;
while(queue.empty() && running)
+ {
+ if (flush)
+ return;
has_work.wait(lock);
+ }
if (!running) break;
active++;
@@ -129,8 +142,10 @@ void threadpool::run() {
queue.pop_front();
lock.unlock();
++depth;
+ is_leaf = e.leaf;
e.f();
--depth;
+ is_leaf = false;
if (e.wo)
e.wo->dec();
diff --git a/src/common/threadpool.h b/src/common/threadpool.h
index 34152541c..a43e38a76 100644
--- a/src/common/threadpool.h
+++ b/src/common/threadpool.h
@@ -46,6 +46,9 @@ public:
static threadpool instance;
return instance;
}
+ static threadpool *getNewForUnitTests(unsigned max_threads = 0) {
+ return new threadpool(max_threads);
+ }
// The waiter lets the caller know when all of its
// tasks are completed.
@@ -56,7 +59,7 @@ public:
public:
void inc();
void dec();
- void wait(); //! Wait for a set of tasks to finish.
+ void wait(threadpool *tpool); //! Wait for a set of tasks to finish.
waiter() : num(0){}
~waiter();
};
@@ -64,25 +67,27 @@ public:
// Submit a task to the pool. The waiter pointer may be
// NULL if the caller doesn't care to wait for the
// task to finish.
- void submit(waiter *waiter, std::function<void()> f);
+ void submit(waiter *waiter, std::function<void()> f, bool leaf = false);
+
+ unsigned int get_max_concurrency() const;
- int get_max_concurrency();
+ ~threadpool();
private:
- threadpool();
- ~threadpool();
+ threadpool(unsigned int max_threads = 0);
typedef struct entry {
waiter *wo;
std::function<void()> f;
+ bool leaf;
} entry;
std::deque<entry> queue;
boost::condition_variable has_work;
boost::mutex mutex;
std::vector<boost::thread> threads;
- int active;
- int max;
+ unsigned int active;
+ unsigned int max;
bool running;
- void run();
+ void run(bool flush = false);
};
}
diff --git a/src/common/util.cpp b/src/common/util.cpp
index d01da0fb7..7d9d7b408 100644
--- a/src/common/util.cpp
+++ b/src/common/util.cpp
@@ -34,6 +34,17 @@
#include <gnu/libc-version.h>
#endif
+#ifdef __GLIBC__
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ustat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <string.h>
+#include <ctype.h>
+#include <string>
+#endif
+
#include "unbound.h"
#include "include_base_utils.h"
@@ -43,6 +54,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::...
@@ -183,6 +195,73 @@ namespace tools
catch (...) {}
}
+ file_locker::file_locker(const std::string &filename)
+ {
+#ifdef WIN32
+ m_fd = INVALID_HANDLE_VALUE;
+ std::wstring filename_wide;
+ try
+ {
+ filename_wide = string_tools::utf8_to_utf16(filename);
+ }
+ catch (const std::exception &e)
+ {
+ MERROR("Failed to convert path \"" << filename << "\" to UTF-16: " << e.what());
+ return;
+ }
+ m_fd = CreateFileW(filename_wide.c_str(), GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (m_fd != INVALID_HANDLE_VALUE)
+ {
+ OVERLAPPED ov;
+ memset(&ov, 0, sizeof(ov));
+ if (!LockFileEx(m_fd, LOCKFILE_FAIL_IMMEDIATELY | LOCKFILE_EXCLUSIVE_LOCK, 0, 1, 0, &ov))
+ {
+ MERROR("Failed to lock " << filename << ": " << std::error_code(GetLastError(), std::system_category()));
+ CloseHandle(m_fd);
+ m_fd = INVALID_HANDLE_VALUE;
+ }
+ }
+ else
+ {
+ MERROR("Failed to open " << filename << ": " << std::error_code(GetLastError(), std::system_category()));
+ }
+#else
+ m_fd = open(filename.c_str(), O_RDONLY | O_CREAT, 0666);
+ if (m_fd != -1)
+ {
+ if (flock(m_fd, LOCK_EX | LOCK_NB) == -1)
+ {
+ MERROR("Failed to lock " << filename << ": " << std::strerror(errno));
+ close(m_fd);
+ m_fd = -1;
+ }
+ }
+ else
+ {
+ MERROR("Failed to open " << filename << ": " << std::strerror(errno));
+ }
+#endif
+ }
+ file_locker::~file_locker()
+ {
+ if (locked())
+ {
+#ifdef WIN32
+ CloseHandle(m_fd);
+#else
+ close(m_fd);
+#endif
+ }
+ }
+ bool file_locker::locked() const
+ {
+#ifdef WIN32
+ return m_fd != INVALID_HANDLE_VALUE;
+#else
+ return m_fd != -1;
+#endif
+ }
+
#ifdef WIN32
std::string get_windows_version_display_string()
{
@@ -439,10 +518,15 @@ std::string get_nix_version_display_string()
if (SHGetSpecialFolderPathW(NULL, psz_path, nfolder, iscreate))
{
- int size_needed = WideCharToMultiByte(CP_UTF8, 0, psz_path, wcslen(psz_path), NULL, 0, NULL, NULL);
- std::string folder_name(size_needed, 0);
- WideCharToMultiByte(CP_UTF8, 0, psz_path, wcslen(psz_path), &folder_name[0], size_needed, NULL, NULL);
- return folder_name;
+ try
+ {
+ return string_tools::utf16_to_utf8(psz_path);
+ }
+ catch (const std::exception &e)
+ {
+ MERROR("utf16_to_utf8 failed: " << e.what());
+ return "";
+ }
}
LOG_ERROR("SHGetSpecialFolderPathW() failed, could not obtain requested path.");
@@ -503,18 +587,20 @@ std::string get_nix_version_display_string()
int code;
#if defined(WIN32)
// Maximizing chances for success
- WCHAR wide_replacement_name[1000];
- MultiByteToWideChar(CP_UTF8, 0, replacement_name.c_str(), replacement_name.size() + 1, wide_replacement_name, 1000);
- WCHAR wide_replaced_name[1000];
- MultiByteToWideChar(CP_UTF8, 0, replaced_name.c_str(), replaced_name.size() + 1, wide_replaced_name, 1000);
-
- DWORD attributes = ::GetFileAttributesW(wide_replaced_name);
+ std::wstring wide_replacement_name;
+ try { wide_replacement_name = string_tools::utf8_to_utf16(replacement_name); }
+ catch (...) { return std::error_code(GetLastError(), std::system_category()); }
+ std::wstring wide_replaced_name;
+ try { wide_replaced_name = string_tools::utf8_to_utf16(replaced_name); }
+ catch (...) { return std::error_code(GetLastError(), std::system_category()); }
+
+ DWORD attributes = ::GetFileAttributesW(wide_replaced_name.c_str());
if (INVALID_FILE_ATTRIBUTES != attributes)
{
- ::SetFileAttributesW(wide_replaced_name, attributes & (~FILE_ATTRIBUTE_READONLY));
+ ::SetFileAttributesW(wide_replaced_name.c_str(), attributes & (~FILE_ATTRIBUTE_READONLY));
}
- bool ok = 0 != ::MoveFileExW(wide_replacement_name, wide_replaced_name, MOVEFILE_REPLACE_EXISTING);
+ bool ok = 0 != ::MoveFileExW(wide_replacement_name.c_str(), wide_replaced_name.c_str(), MOVEFILE_REPLACE_EXISTING);
code = ok ? 0 : static_cast<int>(::GetLastError());
#else
bool ok = 0 == std::rename(replacement_name.c_str(), replaced_name.c_str());
@@ -527,7 +613,10 @@ std::string get_nix_version_display_string()
{
ub_ctx *ctx = ub_ctx_create();
if (!ctx) return false; // cheat a bit, should not happen unless OOM
- ub_ctx_zone_add(ctx, "monero", "unbound"); // this calls ub_ctx_finalize first, then errors out with UB_SYNTAX
+ char *monero = strdup("monero"), *unbound = strdup("unbound");
+ ub_ctx_zone_add(ctx, monero, unbound); // this calls ub_ctx_finalize first, then errors out with UB_SYNTAX
+ free(unbound);
+ free(monero);
// if no threads, bails out early with UB_NOERROR, otherwise fails with UB_AFTERFINAL id already finalized
bool with_threads = ub_ctx_async(ctx, 1) != 0; // UB_AFTERFINAL is not defined in public headers, check any error
ub_ctx_delete(ctx);
@@ -557,10 +646,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__
@@ -590,6 +717,65 @@ std::string get_nix_version_display_string()
#endif
}
+ bool is_hdd(const char *path)
+ {
+#ifdef __GLIBC__
+ std::string device = "";
+ struct stat st, dst;
+ if (stat(path, &st) < 0)
+ return 0;
+
+ DIR *dir = opendir("/dev/block");
+ if (!dir)
+ return 0;
+ struct dirent *de;
+ while ((de = readdir(dir)))
+ {
+ if (strcmp(de->d_name, ".") && strcmp(de->d_name, ".."))
+ {
+ std::string dev_path = std::string("/dev/block/") + de->d_name;
+ char resolved[PATH_MAX];
+ if (realpath(dev_path.c_str(), resolved) && !strncmp(resolved, "/dev/", 5))
+ {
+ if (stat(resolved, &dst) == 0)
+ {
+ if (dst.st_rdev == st.st_dev)
+ {
+ // take out trailing digits (eg, sda1 -> sda)
+ char *ptr = resolved;
+ while (*ptr)
+ ++ptr;
+ while (ptr > resolved && isdigit(*--ptr))
+ *ptr = 0;
+ device = resolved + 5;
+ break;
+ }
+ }
+ }
+ }
+ }
+ closedir(dir);
+
+ if (device.empty())
+ return 0;
+
+ std::string sys_path = "/sys/block/" + device + "/queue/rotational";
+ FILE *f = fopen(sys_path.c_str(), "r");
+ if (!f)
+ return false;
+ char s[8];
+ char *ptr = fgets(s, sizeof(s), f);
+ fclose(f);
+ if (!ptr)
+ return 0;
+ s[sizeof(s) - 1] = 0;
+ int n = atoi(s); // returns 0 on parse error
+ return n == 1;
+#else
+ return 0;
+#endif
+ }
+
namespace
{
boost::mutex max_concurrency_lock;
@@ -615,6 +801,13 @@ std::string get_nix_version_display_string()
bool is_local_address(const std::string &address)
{
+ // always assume Tor/I2P addresses to be untrusted by default
+ if (boost::ends_with(address, ".onion") || boost::ends_with(address, ".i2p"))
+ {
+ MDEBUG("Address '" << address << "' is Tor/I2P, non local");
+ return false;
+ }
+
// extract host
epee::net_utils::http::url_content u_c;
if (!epee::net_utils::parse_url(address, u_c))
@@ -708,4 +901,22 @@ std::string get_nix_version_display_string()
return false;
return true;
}
+
+ boost::optional<std::pair<uint32_t, uint32_t>> parse_subaddress_lookahead(const std::string& str)
+ {
+ auto pos = str.find(":");
+ bool r = pos != std::string::npos;
+ uint32_t major;
+ r = r && epee::string_tools::get_xtype_from_string(major, str.substr(0, pos));
+ uint32_t minor;
+ r = r && epee::string_tools::get_xtype_from_string(minor, str.substr(pos + 1));
+ if (r)
+ {
+ return std::make_pair(major, minor);
+ }
+ else
+ {
+ return {};
+ }
+ }
}
diff --git a/src/common/util.h b/src/common/util.h
index d3ba47a4f..a57a85fee 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -32,6 +32,7 @@
#include <boost/thread/locks.hpp>
#include <boost/thread/mutex.hpp>
+#include <boost/optional.hpp>
#include <system_error>
#include <csignal>
#include <cstdio>
@@ -90,6 +91,20 @@ namespace tools
const std::string& filename() const noexcept { return m_filename; }
};
+ class file_locker
+ {
+ public:
+ file_locker(const std::string &filename);
+ ~file_locker();
+ bool locked() const;
+ private:
+#ifdef WIN32
+ HANDLE m_fd;
+#else
+ int m_fd;
+#endif
+ };
+
/*! \brief Returns the default data directory.
*
* \details Windows < Vista: C:\\Documents and Settings\\Username\\Application Data\\CRYPTONOTE_NAME
@@ -212,4 +227,8 @@ namespace tools
bool sha256sum(const uint8_t *data, size_t len, crypto::hash &hash);
bool sha256sum(const std::string &filename, crypto::hash &hash);
+
+ bool is_hdd(const char *path);
+
+ boost::optional<std::pair<uint32_t, uint32_t>> parse_subaddress_lookahead(const std::string& str);
}
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/account.cpp b/src/cryptonote_basic/account.cpp
index bab991d19..aac6ec22b 100644
--- a/src/cryptonote_basic/account.cpp
+++ b/src/cryptonote_basic/account.cpp
@@ -157,7 +157,7 @@ DISABLE_VS_WARNINGS(4244 4345)
void account_base::create_from_viewkey(const cryptonote::account_public_address& address, const crypto::secret_key& viewkey)
{
crypto::secret_key fake;
- memset(&fake, 0, sizeof(fake));
+ memset(&unwrap(fake), 0, sizeof(fake));
create_from_keys(address, fake, viewkey);
}
//-----------------------------------------------------------------
diff --git a/src/cryptonote_basic/connection_context.h b/src/cryptonote_basic/connection_context.h
index 5cd1709ab..eb73ab0ea 100644
--- a/src/cryptonote_basic/connection_context.h
+++ b/src/cryptonote_basic/connection_context.h
@@ -52,7 +52,7 @@ namespace cryptonote
};
state m_state;
- std::list<crypto::hash> m_needed_objects;
+ std::vector<crypto::hash> m_needed_objects;
std::unordered_set<crypto::hash> m_requested_objects;
uint64_t m_remote_blockchain_height;
uint64_t m_last_response_height;
@@ -67,15 +67,15 @@ namespace cryptonote
switch (s)
{
case cryptonote_connection_context::state_before_handshake:
- return "state_before_handshake";
+ return "before_handshake";
case cryptonote_connection_context::state_synchronizing:
- return "state_synchronizing";
+ return "synchronizing";
case cryptonote_connection_context::state_standby:
- return "state_standby";
+ return "standby";
case cryptonote_connection_context::state_idle:
- return "state_idle";
+ return "idle";
case cryptonote_connection_context::state_normal:
- return "state_normal";
+ return "normal";
default:
return "unknown";
}
diff --git a/src/cryptonote_basic/cryptonote_basic_impl.cpp b/src/cryptonote_basic/cryptonote_basic_impl.cpp
index 08a95d2e9..cff23695f 100644
--- a/src/cryptonote_basic/cryptonote_basic_impl.cpp
+++ b/src/cryptonote_basic/cryptonote_basic_impl.cpp
@@ -162,10 +162,7 @@ namespace cryptonote {
, account_public_address const & adr
)
{
- uint64_t address_prefix = nettype == TESTNET ?
- (subaddress ? config::testnet::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX : config::testnet::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX) : nettype == STAGENET ?
- (subaddress ? config::stagenet::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX : config::stagenet::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX) :
- (subaddress ? config::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX : config::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX);
+ uint64_t address_prefix = subaddress ? get_config(nettype).CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX : get_config(nettype).CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX;
return tools::base58::encode_addr(address_prefix, t_serializable_object_to_blob(adr));
}
@@ -176,7 +173,7 @@ namespace cryptonote {
, crypto::hash8 const & payment_id
)
{
- uint64_t integrated_address_prefix = nettype == TESTNET ? config::testnet::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX : nettype == STAGENET ? config::stagenet::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX : config::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX;
+ uint64_t integrated_address_prefix = get_config(nettype).CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX;
integrated_address iadr = {
adr, payment_id
@@ -201,15 +198,9 @@ namespace cryptonote {
, std::string const & str
)
{
- uint64_t address_prefix = nettype == TESTNET ?
- config::testnet::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX : nettype == STAGENET ?
- config::stagenet::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX : config::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX;
- uint64_t integrated_address_prefix = nettype == TESTNET ?
- config::testnet::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX : nettype == STAGENET ?
- config::stagenet::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX : config::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX;
- uint64_t subaddress_prefix = nettype == TESTNET ?
- config::testnet::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX : nettype == STAGENET ?
- config::stagenet::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX : config::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX;
+ uint64_t address_prefix = get_config(nettype).CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX;
+ uint64_t integrated_address_prefix = get_config(nettype).CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX;
+ uint64_t subaddress_prefix = get_config(nettype).CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX;
if (2 * sizeof(public_address_outer_blob) != str.size())
{
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_basic/hardfork.cpp b/src/cryptonote_basic/hardfork.cpp
index 95f1ecab9..f05b25901 100644
--- a/src/cryptonote_basic/hardfork.cpp
+++ b/src/cryptonote_basic/hardfork.cpp
@@ -379,20 +379,24 @@ uint8_t HardFork::get_ideal_version(uint64_t height) const
uint64_t HardFork::get_earliest_ideal_height_for_version(uint8_t version) const
{
- for (unsigned int n = heights.size() - 1; n > 0; --n) {
- if (heights[n].version <= version)
- return heights[n].height;
+ uint64_t height = std::numeric_limits<uint64_t>::max();
+ for (auto i = heights.rbegin(); i != heights.rend(); ++i) {
+ if (i->version >= version) {
+ height = i->height;
+ } else {
+ break;
+ }
}
- return 0;
+ return height;
}
uint8_t HardFork::get_next_version() const
{
CRITICAL_REGION_LOCAL(lock);
uint64_t height = db.height();
- for (unsigned int n = heights.size() - 1; n > 0; --n) {
- if (height >= heights[n].height) {
- return heights[n < heights.size() - 1 ? n + 1 : n].version;
+ for (auto i = heights.rbegin(); i != heights.rend(); ++i) {
+ if (height >= i->height) {
+ return (i == heights.rbegin() ? i : (i - 1))->version;
}
}
return original_version;
diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp
index 2c777f5a2..3a3222f9b 100644
--- a/src/cryptonote_basic/miner.cpp
+++ b/src/cryptonote_basic/miner.cpp
@@ -328,6 +328,11 @@ namespace cryptonote
LOG_PRINT_L0("Background mining controller thread started" );
}
+ if(get_ignore_battery())
+ {
+ MINFO("Ignoring battery");
+ }
+
return true;
}
//-----------------------------------------------------------------------------------------------------
diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h
index 3e64073dd..a0dcf2df1 100644
--- a/src/cryptonote_config.h
+++ b/src/cryptonote_config.h
@@ -203,4 +203,60 @@ namespace cryptonote
FAKECHAIN,
UNDEFINED = 255
};
+ struct config_t
+ {
+ uint64_t const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX;
+ uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX;
+ uint64_t const CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX;
+ uint16_t const P2P_DEFAULT_PORT;
+ uint16_t const RPC_DEFAULT_PORT;
+ uint16_t const ZMQ_RPC_DEFAULT_PORT;
+ boost::uuids::uuid const NETWORK_ID;
+ std::string const GENESIS_TX;
+ uint32_t const GENESIS_NONCE;
+ };
+ inline const config_t& get_config(network_type nettype)
+ {
+ static const config_t mainnet = {
+ ::config::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX,
+ ::config::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX,
+ ::config::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX,
+ ::config::P2P_DEFAULT_PORT,
+ ::config::RPC_DEFAULT_PORT,
+ ::config::ZMQ_RPC_DEFAULT_PORT,
+ ::config::NETWORK_ID,
+ ::config::GENESIS_TX,
+ ::config::GENESIS_NONCE
+ };
+ static const config_t testnet = {
+ ::config::testnet::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX,
+ ::config::testnet::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX,
+ ::config::testnet::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX,
+ ::config::testnet::P2P_DEFAULT_PORT,
+ ::config::testnet::RPC_DEFAULT_PORT,
+ ::config::testnet::ZMQ_RPC_DEFAULT_PORT,
+ ::config::testnet::NETWORK_ID,
+ ::config::testnet::GENESIS_TX,
+ ::config::testnet::GENESIS_NONCE
+ };
+ static const config_t stagenet = {
+ ::config::stagenet::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX,
+ ::config::stagenet::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX,
+ ::config::stagenet::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX,
+ ::config::stagenet::P2P_DEFAULT_PORT,
+ ::config::stagenet::RPC_DEFAULT_PORT,
+ ::config::stagenet::ZMQ_RPC_DEFAULT_PORT,
+ ::config::stagenet::NETWORK_ID,
+ ::config::stagenet::GENESIS_TX,
+ ::config::stagenet::GENESIS_NONCE
+ };
+ switch (nettype)
+ {
+ case MAINNET: return mainnet;
+ case TESTNET: return testnet;
+ case STAGENET: return stagenet;
+ case FAKECHAIN: return mainnet;
+ default: throw std::runtime_error("Invalid network type");
+ }
+ };
}
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index f02a1f8d6..73ce98366 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__);
}
@@ -240,6 +242,7 @@ bool Blockchain::scan_outputkeys_for_indexes(size_t tx_version, const txin_to_ke
MDEBUG("Additional outputs needed: " << absolute_offsets.size() - outputs.size());
std::vector < uint64_t > add_offsets;
std::vector<output_data_t> add_outputs;
+ add_outputs.reserve(absolute_offsets.size() - outputs.size());
for (size_t i = outputs.size(); i < absolute_offsets.size(); i++)
add_offsets.push_back(absolute_offsets[i]);
try
@@ -391,18 +394,7 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline
MINFO("Blockchain not loaded, generating genesis block.");
block bl = boost::value_initialized<block>();
block_verification_context bvc = boost::value_initialized<block_verification_context>();
- if (m_nettype == TESTNET)
- {
- generate_genesis_block(bl, config::testnet::GENESIS_TX, config::testnet::GENESIS_NONCE);
- }
- else if (m_nettype == STAGENET)
- {
- generate_genesis_block(bl, config::stagenet::GENESIS_TX, config::stagenet::GENESIS_NONCE);
- }
- else
- {
- generate_genesis_block(bl, config::GENESIS_TX, config::GENESIS_NONCE);
- }
+ generate_genesis_block(bl, get_config(m_nettype).GENESIS_TX, get_config(m_nettype).GENESIS_NONCE);
add_new_block(bl, bvc);
CHECK_AND_ASSERT_MES(!bvc.m_verifivation_failed, false, "Failed to add genesis block to blockchain");
}
@@ -441,6 +433,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 (!m_db->is_read_only())
+ {
+ 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 +623,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,6 +796,18 @@ 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__);
+
+ crypto::hash top_hash = get_tail_id();
+ {
+ 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
+ if (top_hash == m_difficulty_for_next_block_top_hash)
+ return m_difficulty_for_next_block;
+ }
+
CRITICAL_REGION_LOCAL(m_blockchain_lock);
std::vector<uint64_t> timestamps;
std::vector<difficulty_type> difficulties;
@@ -760,7 +817,7 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
// then when the next block difficulty is queried, push the latest height data and
// pop the oldest one from the list. This only requires 1x read per height instead
// of doing 735 (DIFFICULTY_BLOCKS_COUNT).
- if (m_timestamps_and_difficulties_height != 0 && ((height - m_timestamps_and_difficulties_height) == 1))
+ if (m_timestamps_and_difficulties_height != 0 && ((height - m_timestamps_and_difficulties_height) == 1) && m_timestamps.size() >= DIFFICULTY_BLOCKS_COUNT)
{
uint64_t index = height - 1;
m_timestamps.push_back(m_db->get_block_timestamp(index));
@@ -783,6 +840,11 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
timestamps.clear();
difficulties.clear();
+ if (height > offset)
+ {
+ timestamps.reserve(height - offset);
+ difficulties.reserve(height - offset);
+ }
for (; offset < height; offset++)
{
timestamps.push_back(m_db->get_block_timestamp(offset));
@@ -794,7 +856,12 @@ 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);
+
+ CRITICAL_REGION_LOCAL1(m_difficulty_lock);
+ 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
@@ -1098,6 +1165,7 @@ void Blockchain::get_last_n_blocks_sizes(std::vector<size_t>& sz, size_t count)
m_db->block_txn_start(true);
// add size of last <count> blocks to vector <sz> (or less, if blockchain size < count)
size_t start_offset = h - std::min<size_t>(h, count);
+ sz.reserve(sz.size() + h - start_offset);
for(size_t i = start_offset; i < h; i++)
{
sz.push_back(m_db->get_block_size(i));
@@ -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.");
@@ -1289,6 +1363,7 @@ bool Blockchain::complete_timestamps_vector(uint64_t start_top_height, std::vect
size_t need_elements = BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW - timestamps.size();
CHECK_AND_ASSERT_MES(start_top_height < m_db->height(), false, "internal error: passed start_height not < " << " m_db->height() -- " << start_top_height << " >= " << m_db->height());
size_t stop_offset = start_top_height > need_elements ? start_top_height - need_elements : 0;
+ timestamps.reserve(timestamps.size() + start_top_height - stop_offset);
while (start_top_height != stop_offset)
{
timestamps.push_back(m_db->get_block_timestamp(start_top_height));
@@ -1488,7 +1563,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
return true;
}
//------------------------------------------------------------------
-bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks, std::list<cryptonote::blobdata>& txs) const
+bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::vector<std::pair<cryptonote::blobdata,block>>& blocks, std::vector<cryptonote::blobdata>& txs) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
@@ -1502,7 +1577,7 @@ bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::list<std::
for(const auto& blk : blocks)
{
- std::list<crypto::hash> missed_ids;
+ std::vector<crypto::hash> missed_ids;
get_transactions_blobs(blk.second.tx_hashes, txs, missed_ids);
CHECK_AND_ASSERT_MES(!missed_ids.size(), false, "has missed transactions in own block in main blockchain");
}
@@ -1510,14 +1585,16 @@ bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::list<std::
return true;
}
//------------------------------------------------------------------
-bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks) const
+bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::vector<std::pair<cryptonote::blobdata,block>>& blocks) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
- if(start_offset >= m_db->height())
+ const uint64_t height = m_db->height();
+ if(start_offset >= height)
return false;
- for(size_t i = start_offset; i < start_offset + count && i < m_db->height();i++)
+ blocks.reserve(blocks.size() + height - start_offset);
+ for(size_t i = start_offset; i < start_offset + count && i < height;i++)
{
blocks.push_back(std::make_pair(m_db->get_block_blob_from_height(i), block()));
if (!parse_and_validate_block_from_blob(blocks.back().first, blocks.back().second))
@@ -1542,17 +1619,20 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO
CRITICAL_REGION_LOCAL(m_blockchain_lock);
m_db->block_txn_start(true);
rsp.current_blockchain_height = get_current_blockchain_height();
- std::list<std::pair<cryptonote::blobdata,block>> blocks;
+ std::vector<std::pair<cryptonote::blobdata,block>> blocks;
get_blocks(arg.blocks, blocks, rsp.missed_ids);
- for (const auto& bl: blocks)
+ for (auto& bl: blocks)
{
- std::list<crypto::hash> missed_tx_ids;
- std::list<cryptonote::blobdata> txs;
+ std::vector<crypto::hash> missed_tx_ids;
+ std::vector<cryptonote::blobdata> txs;
+
+ rsp.blocks.push_back(block_complete_entry());
+ block_complete_entry& e = rsp.blocks.back();
// FIXME: s/rsp.missed_ids/missed_tx_id/ ? Seems like rsp.missed_ids
// is for missed blocks, not missed transactions as well.
- get_transactions_blobs(bl.second.tx_hashes, txs, missed_tx_ids);
+ get_transactions_blobs(bl.second.tx_hashes, e.txs, missed_tx_ids);
if (missed_tx_ids.size() != 0)
{
@@ -1564,35 +1644,28 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO
// append missed transaction hashes to response missed_ids field,
// as done below if any standalone transactions were requested
// and missed.
- rsp.missed_ids.splice(rsp.missed_ids.end(), missed_tx_ids);
- m_db->block_txn_stop();
+ rsp.missed_ids.insert(rsp.missed_ids.end(), missed_tx_ids.begin(), missed_tx_ids.end());
+ m_db->block_txn_stop();
return false;
}
- rsp.blocks.push_back(block_complete_entry());
- block_complete_entry& e = rsp.blocks.back();
//pack block
- e.block = bl.first;
- //pack transactions
- for (const cryptonote::blobdata& tx: txs)
- e.txs.push_back(tx);
- }
- //get another transactions, if need
- std::list<cryptonote::blobdata> txs;
- get_transactions_blobs(arg.txs, txs, rsp.missed_ids);
- //pack aside transactions
- for (const auto& tx: txs)
- rsp.txs.push_back(tx);
+ e.block = std::move(bl.first);
+ }
+ //get and pack other transactions, if needed
+ std::vector<cryptonote::blobdata> txs;
+ get_transactions_blobs(arg.txs, rsp.txs, rsp.missed_ids);
m_db->block_txn_stop();
return true;
}
//------------------------------------------------------------------
-bool Blockchain::get_alternative_blocks(std::list<block>& blocks) const
+bool Blockchain::get_alternative_blocks(std::vector<block>& blocks) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ blocks.reserve(m_alternative_chains.size());
for (const auto& alt_bl: m_alternative_chains)
{
blocks.push_back(alt_bl.second.bl);
@@ -1882,14 +1955,21 @@ bool Blockchain::get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMA
res.outs.clear();
res.outs.reserve(req.outputs.size());
- for (const auto &i: req.outputs)
+ try
{
- // get tx_hash, tx_out_index from DB
- const output_data_t od = m_db->get_output_key(i.amount, i.index);
- tx_out_index toi = m_db->get_output_tx_and_index(i.amount, i.index);
- bool unlocked = is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first));
+ for (const auto &i: req.outputs)
+ {
+ // get tx_hash, tx_out_index from DB
+ const output_data_t od = m_db->get_output_key(i.amount, i.index);
+ tx_out_index toi = m_db->get_output_tx_and_index(i.amount, i.index);
+ bool unlocked = is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first));
- res.outs.push_back({od.pubkey, od.commitment, unlocked, od.height, toi.first});
+ res.outs.push_back({od.pubkey, od.commitment, unlocked, od.height, toi.first});
+ }
+ }
+ catch (const std::exception &e)
+ {
+ return false;
}
return true;
}
@@ -1903,7 +1983,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 +2004,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
@@ -1952,9 +2017,9 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc
// make sure the request includes at least the genesis block, otherwise
// how can we expect to sync from the client that the block list came from?
- if(!qblock_ids.size() /*|| !req.m_total_height*/)
+ if(!qblock_ids.size())
{
- MCERROR("net.p2p", "Client sent wrong NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << qblock_ids.size() << /*", m_height=" << req.m_total_height <<*/ ", dropping connection");
+ MCERROR("net.p2p", "Client sent wrong NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << qblock_ids.size() << ", dropping connection");
return false;
}
@@ -2020,6 +2085,9 @@ uint64_t Blockchain::block_difficulty(uint64_t i) const
return 0;
}
//------------------------------------------------------------------
+template<typename T> void reserve_container(std::vector<T> &v, size_t N) { v.reserve(N); }
+template<typename T> void reserve_container(std::list<T> &v, size_t N) { }
+//------------------------------------------------------------------
//TODO: return type should be void, throw on exception
// alternatively, return true only if no blocks missed
template<class t_ids_container, class t_blocks_container, class t_missed_container>
@@ -2028,20 +2096,24 @@ bool Blockchain::get_blocks(const t_ids_container& block_ids, t_blocks_container
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ reserve_container(blocks, block_ids.size());
for (const auto& block_hash : block_ids)
{
try
{
- blocks.push_back(std::make_pair(m_db->get_block_blob(block_hash), block()));
- if (!parse_and_validate_block_from_blob(blocks.back().first, blocks.back().second))
+ uint64_t height = 0;
+ if (m_db->block_exists(block_hash, &height))
{
- LOG_ERROR("Invalid block");
- return false;
+ blocks.push_back(std::make_pair(m_db->get_block_blob_from_height(height), block()));
+ if (!parse_and_validate_block_from_blob(blocks.back().first, blocks.back().second))
+ {
+ LOG_ERROR("Invalid block: " << block_hash);
+ blocks.pop_back();
+ missed_bs.push_back(block_hash);
+ }
}
- }
- catch (const BLOCK_DNE& e)
- {
- missed_bs.push_back(block_hash);
+ else
+ missed_bs.push_back(block_hash);
}
catch (const std::exception& e)
{
@@ -2054,17 +2126,20 @@ 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);
+ reserve_container(txs, txs_ids.size());
for (const auto& tx_hash : txs_ids)
{
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);
@@ -2083,6 +2158,7 @@ bool Blockchain::get_transactions(const t_ids_container& txs_ids, t_tx_container
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ reserve_container(txs, txs_ids.size());
for (const auto& tx_hash : txs_ids)
{
try
@@ -2111,7 +2187,7 @@ bool Blockchain::get_transactions(const t_ids_container& txs_ids, t_tx_container
// Find the split point between us and foreign blockchain and return
// (by reference) the most recent common block hash along with up to
// BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT additional (more recent) hashes.
-bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::list<crypto::hash>& hashes, uint64_t& start_height, uint64_t& current_height) const
+bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::vector<crypto::hash>& hashes, uint64_t& start_height, uint64_t& current_height) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
@@ -2125,6 +2201,7 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc
m_db->block_txn_start(true);
current_height = get_current_blockchain_height();
size_t count = 0;
+ hashes.reserve(std::max((size_t)(current_height - start_height), (size_t)BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT));
for(size_t i = start_height; i < current_height && count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT; i++, count++)
{
hashes.push_back(m_db->get_block_hash_from_height(i));
@@ -2140,7 +2217,7 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc
CRITICAL_REGION_LOCAL(m_blockchain_lock);
bool result = find_blockchain_supplement(qblock_ids, resp.m_block_ids, resp.start_height, resp.total_height);
- resp.cumulative_difficulty = m_db->get_block_cumulative_difficulty(m_db->height() - 1);
+ resp.cumulative_difficulty = m_db->get_block_cumulative_difficulty(resp.total_height - 1);
return result;
}
@@ -2149,7 +2226,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::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_count) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
@@ -2175,18 +2252,28 @@ bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, cons
m_db->block_txn_start(true);
total_height = get_current_blockchain_height();
size_t count = 0, size = 0;
+ blocks.reserve(std::min(std::min(max_count, (size_t)10000), (size_t)(total_height - start_height)));
for(size_t i = start_height; i < total_height && count < max_count && (size < FIND_BLOCKCHAIN_SUPPLEMENT_MAX_SIZE || count < 3); i++, count++)
{
blocks.resize(blocks.size()+1);
- blocks.back().first = m_db->get_block_blob_from_height(i);
+ blocks.back().first.first = m_db->get_block_blob_from_height(i);
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);
+ CHECK_AND_ASSERT_MES(parse_and_validate_block_from_blob(blocks.back().first.first, b), false, "internal error, invalid block");
+ blocks.back().first.second = get_miner_tx_hash ? cryptonote::get_transaction_hash(b.miner_tx) : crypto::null_hash;
+ std::vector<crypto::hash> mis;
+ std::vector<cryptonote::blobdata> txs;
+ get_transactions_blobs(b.tx_hashes, txs, 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)
+ size += blocks.back().first.first.size();
+ for (const auto &t: txs)
size += t.size();
+
+ CHECK_AND_ASSERT_MES(txs.size() == b.tx_hashes.size(), false, "mismatched sizes of b.tx_hashes and txs");
+ blocks.back().second.reserve(txs.size());
+ for (size_t i = 0; i < txs.size(); ++i)
+ {
+ blocks.back().second.push_back(std::make_pair(b.tx_hashes[i], std::move(txs[i])));
+ }
}
m_db->block_txn_stop();
return true;
@@ -2217,19 +2304,19 @@ bool Blockchain::have_block(const crypto::hash& id) const
if(m_db->block_exists(id))
{
- LOG_PRINT_L3("block exists in main chain");
+ LOG_PRINT_L2("block " << id << " found in main chain");
return true;
}
if(m_alternative_chains.count(id))
{
- LOG_PRINT_L3("block found in m_alternative_chains");
+ LOG_PRINT_L2("block " << id << " found in m_alternative_chains");
return true;
}
if(m_invalid_blocks.count(id))
{
- LOG_PRINT_L3("block found in m_invalid_blocks");
+ LOG_PRINT_L2("block " << id << " found in m_invalid_blocks");
return true;
}
@@ -2658,6 +2745,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(&tpool); });
int threads = tpool.get_max_concurrency();
for (const auto& txin : tx.vin)
@@ -2719,7 +2807,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
{
// ND: Speedup
// 1. Thread ring signature verification if possible.
- tpool.submit(&waiter, boost::bind(&Blockchain::check_ring_signature, this, std::cref(tx_prefix_hash), std::cref(in_to_key.k_image), std::cref(pubkeys[sig_index]), std::cref(tx.signatures[sig_index]), std::ref(results[sig_index])));
+ tpool.submit(&waiter, boost::bind(&Blockchain::check_ring_signature, this, std::cref(tx_prefix_hash), std::cref(in_to_key.k_image), std::cref(pubkeys[sig_index]), std::cref(tx.signatures[sig_index]), std::ref(results[sig_index])), true);
}
else
{
@@ -2743,7 +2831,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
sig_index++;
}
if (tx.version == 1 && threads > 1)
- waiter.wait();
+ waiter.wait(&tpool);
if (tx.version == 1)
{
@@ -2915,6 +3003,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
void Blockchain::check_ring_signature(const crypto::hash &tx_prefix_hash, const crypto::key_image &key_image, const std::vector<rct::ctkey> &pubkeys, const std::vector<crypto::signature>& sig, uint64_t &result)
{
std::vector<const crypto::public_key *> p_output_keys;
+ p_output_keys.reserve(pubkeys.size());
for (auto &key : pubkeys)
{
// rct::key and crypto::public_key have the same structure, avoid object ctor/memcpy
@@ -3011,6 +3100,7 @@ uint64_t Blockchain::get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks) cons
const uint64_t min_block_size = get_min_block_size(version);
std::vector<size_t> sz;
get_last_n_blocks_sizes(sz, CRYPTONOTE_REWARD_BLOCKS_WINDOW - grace_blocks);
+ sz.reserve(grace_blocks);
for (size_t i = 0; i < grace_blocks; ++i)
sz.push_back(min_block_size);
@@ -3127,10 +3217,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 +3238,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)
@@ -3168,12 +3258,13 @@ bool Blockchain::check_block_timestamp(const block& b) const
// need most recent 60 blocks, get index of first of those
size_t offset = h - BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW;
+ timestamps.reserve(h - offset);
for(;offset < h; ++offset)
{
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)
@@ -3194,7 +3285,7 @@ void Blockchain::return_tx_to_pool(std::vector<transaction> &txs)
}
}
//------------------------------------------------------------------
-bool Blockchain::flush_txes_from_pool(const std::list<crypto::hash> &txids)
+bool Blockchain::flush_txes_from_pool(const std::vector<crypto::hash> &txids)
{
CRITICAL_REGION_LOCAL(m_tx_pool);
@@ -3384,6 +3475,7 @@ leave:
// Iterate over the block's transaction hashes, grabbing each
// from the tx_pool and validating them. Each is then added
// to txs. Keys spent in each are added to <keys> by the double spend check.
+ txs.reserve(bl.tx_hashes.size());
for (const crypto::hash& tx_id : bl.tx_hashes)
{
transaction tx;
@@ -3797,7 +3889,7 @@ void Blockchain::output_scan_worker(const uint64_t amount, const std::vector<uin
}
}
-uint64_t Blockchain::prevalidate_block_hashes(uint64_t height, const std::list<crypto::hash> &hashes)
+uint64_t Blockchain::prevalidate_block_hashes(uint64_t height, const std::vector<crypto::hash> &hashes)
{
// new: . . . . . X X X X X . . . . . .
// pre: A A A A B B B B C C C C D D D D
@@ -3867,7 +3959,7 @@ uint64_t Blockchain::prevalidate_block_hashes(uint64_t height, const std::list<c
// add to the known hashes array
if (!valid)
{
- MWARNING("invalid hash for blocks " << n * HASH_OF_HASHES_STEP << " - " << (n * HASH_OF_HASHES_STEP + HASH_OF_HASHES_STEP - 1));
+ MDEBUG("invalid hash for blocks " << n * HASH_OF_HASHES_STEP << " - " << (n * HASH_OF_HASHES_STEP + HASH_OF_HASHES_STEP - 1));
break;
}
@@ -3900,7 +3992,7 @@ uint64_t Blockchain::prevalidate_block_hashes(uint64_t height, const std::list<c
// vs [k_image, output_keys] (m_scan_table). This is faster because it takes advantage of bulk queries
// and is threaded if possible. The table (m_scan_table) will be used later when querying output
// keys.
-bool Blockchain::prepare_handle_incoming_blocks(const std::list<block_complete_entry> &blocks_entry)
+bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete_entry> &blocks_entry)
{
MTRACE("Blockchain::" << __func__);
TIME_MEASURE_START(prepare);
@@ -3966,6 +4058,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::list<block_complete_e
for (uint64_t i = 0; i < threads; i++)
{
+ blocks[i].reserve(batches + 1);
for (int j = 0; j < batches; j++)
{
block block;
@@ -4024,11 +4117,11 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::list<block_complete_e
tools::threadpool::waiter waiter;
for (uint64_t i = 0; i < threads; i++)
{
- tpool.submit(&waiter, boost::bind(&Blockchain::block_longhash_worker, this, thread_height, std::cref(blocks[i]), std::ref(maps[i])));
+ tpool.submit(&waiter, boost::bind(&Blockchain::block_longhash_worker, this, thread_height, std::cref(blocks[i]), std::ref(maps[i])), true);
thread_height += blocks[i].size();
}
- waiter.wait();
+ waiter.wait(&tpool);
if (m_cancel)
return false;
@@ -4163,9 +4256,9 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::list<block_complete_e
for (size_t i = 0; i < amounts.size(); i++)
{
uint64_t amount = amounts[i];
- tpool.submit(&waiter, boost::bind(&Blockchain::output_scan_worker, this, amount, std::cref(offset_map[amount]), std::ref(tx_map[amount]), std::ref(transactions[i])));
+ tpool.submit(&waiter, boost::bind(&Blockchain::output_scan_worker, this, amount, std::cref(offset_map[amount]), std::ref(tx_map[amount]), std::ref(transactions[i])), true);
}
- waiter.wait();
+ waiter.wait(&tpool);
}
else
{
@@ -4322,9 +4415,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 +4458,7 @@ void Blockchain::cancel()
}
#if defined(PER_BLOCK_CHECKPOINT)
-static const char expected_block_hashes_hash[] = "1d3df1a177bd6f752d87c0d7b960e502605742721afb39953265f1e0f7f9b01f";
+static const char expected_block_hashes_hash[] = "0924bc1c47aae448321fde949554be192878dd800e6489379865218f84eacbca";
void Blockchain::load_compiled_in_block_hashes()
{
const bool testnet = m_nettype == TESTNET;
@@ -4429,7 +4522,7 @@ void Blockchain::load_compiled_in_block_hashes()
// for tx hashes will fail in handle_block_to_main_chain(..)
CRITICAL_REGION_LOCAL(m_tx_pool);
- std::list<transaction> txs;
+ std::vector<transaction> txs;
m_tx_pool.get_transactions(txs);
size_t blob_size;
@@ -4476,9 +4569,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
@@ -4492,5 +4585,6 @@ 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(const std::vector<crypto::hash>&, std::vector<transaction>&, std::vector<crypto::hash>&) const;
+template bool Blockchain::get_transactions_blobs(const std::vector<crypto::hash>&, std::vector<cryptonote::blobdata>&, std::vector<crypto::hash>&, bool) const;
}
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index 4423199de..36d6b8609 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -157,7 +157,7 @@ namespace cryptonote
*
* @return false if start_offset > blockchain height, else true
*/
- bool get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks, std::list<cryptonote::blobdata>& txs) const;
+ bool get_blocks(uint64_t start_offset, size_t count, std::vector<std::pair<cryptonote::blobdata,block>>& blocks, std::vector<cryptonote::blobdata>& txs) const;
/**
* @brief get blocks from blocks based on start height and count
@@ -168,7 +168,7 @@ namespace cryptonote
*
* @return false if start_offset > blockchain height, else true
*/
- bool get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks) const;
+ bool get_blocks(uint64_t start_offset, size_t count, std::vector<std::pair<cryptonote::blobdata,block>>& blocks) const;
/**
* @brief compiles a list of all blocks stored as alternative chains
@@ -177,7 +177,7 @@ namespace cryptonote
*
* @return true
*/
- bool get_alternative_blocks(std::list<block>& blocks) const;
+ bool get_alternative_blocks(std::vector<block>& blocks) const;
/**
* @brief returns the number of alternative blocks stored
@@ -213,7 +213,7 @@ namespace cryptonote
*
* @return false on erroneous blocks, else true
*/
- bool prepare_handle_incoming_blocks(const std::list<block_complete_entry> &blocks);
+ bool prepare_handle_incoming_blocks(const std::vector<block_complete_entry> &blocks);
/**
* @brief incoming blocks post-processing, cleanup, and disk sync
@@ -373,7 +373,7 @@ namespace cryptonote
*
* @return true if a block found in common, else false
*/
- bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::list<crypto::hash>& hashes, uint64_t& start_height, uint64_t& current_height) const;
+ bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::vector<crypto::hash>& hashes, uint64_t& start_height, uint64_t& current_height) const;
/**
* @brief get recent block hashes for a foreign chain
@@ -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::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, 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;
@@ -792,6 +795,13 @@ namespace cryptonote
uint8_t get_hard_fork_version(uint64_t height) const { return m_hardfork->get(height); }
/**
+ * @brief returns the earliest block a given version may activate
+ *
+ * @return the height
+ */
+ uint64_t get_earliest_ideal_height_for_version(uint8_t version) const { return m_hardfork->get_earliest_ideal_height_for_version(version); }
+
+ /**
* @brief get information about hardfork voting for a version
*
* @param version the version in question
@@ -819,7 +829,7 @@ namespace cryptonote
*
* @return false if any removals fail, otherwise true
*/
- bool flush_txes_from_pool(const std::list<crypto::hash> &txids);
+ bool flush_txes_from_pool(const std::vector<crypto::hash> &txids);
/**
* @brief return a histogram of outputs on the blockchain
@@ -827,10 +837,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 +867,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
@@ -940,7 +952,7 @@ namespace cryptonote
bool is_within_compiled_block_hash_area(uint64_t height) const;
bool is_within_compiled_block_hash_area() const { return is_within_compiled_block_hash_area(m_db->height()); }
- uint64_t prevalidate_block_hashes(uint64_t height, const std::list<crypto::hash> &hashes);
+ uint64_t prevalidate_block_hashes(uint64_t height, const std::vector<crypto::hash> &hashes);
void lock();
void unlock();
@@ -1006,6 +1018,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 +1307,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 +1325,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..910bf0c1f 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -170,7 +170,6 @@ namespace cryptonote
m_last_dns_checkpoints_update(0),
m_last_json_checkpoints_update(0),
m_disable_dns_checkpoints(false),
- m_threadpool(tools::threadpool::getInstance()),
m_update_download(0),
m_nettype(UNDEFINED)
{
@@ -324,19 +323,19 @@ namespace cryptonote
top_id = m_blockchain_storage.get_tail_id(height);
}
//-----------------------------------------------------------------------------------------------
- bool core::get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks, std::list<cryptonote::blobdata>& txs) const
+ bool core::get_blocks(uint64_t start_offset, size_t count, std::vector<std::pair<cryptonote::blobdata,block>>& blocks, std::vector<cryptonote::blobdata>& txs) const
{
return m_blockchain_storage.get_blocks(start_offset, count, blocks, txs);
}
//-----------------------------------------------------------------------------------------------
- bool core::get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks) const
+ bool core::get_blocks(uint64_t start_offset, size_t count, std::vector<std::pair<cryptonote::blobdata,block>>& blocks) const
{
return m_blockchain_storage.get_blocks(start_offset, count, blocks);
}
//-----------------------------------------------------------------------------------------------
- bool core::get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks) const
+ bool core::get_blocks(uint64_t start_offset, size_t count, std::vector<block>& blocks) const
{
- std::list<std::pair<cryptonote::blobdata, cryptonote::block>> bs;
+ std::vector<std::pair<cryptonote::blobdata, cryptonote::block>> bs;
if (!m_blockchain_storage.get_blocks(start_offset, count, bs))
return false;
for (const auto &b: bs)
@@ -344,7 +343,7 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------------------------------------
- bool core::get_transactions(const std::vector<crypto::hash>& txs_ids, std::list<cryptonote::blobdata>& txs, std::list<crypto::hash>& missed_txs) const
+ bool core::get_transactions(const std::vector<crypto::hash>& txs_ids, std::vector<cryptonote::blobdata>& txs, std::vector<crypto::hash>& missed_txs) const
{
return m_blockchain_storage.get_transactions_blobs(txs_ids, txs, missed_txs);
}
@@ -355,12 +354,12 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------------------------------------
- bool core::get_transactions(const std::vector<crypto::hash>& txs_ids, std::list<transaction>& txs, std::list<crypto::hash>& missed_txs) const
+ bool core::get_transactions(const std::vector<crypto::hash>& txs_ids, std::vector<transaction>& txs, std::vector<crypto::hash>& missed_txs) const
{
return m_blockchain_storage.get_transactions(txs_ids, txs, missed_txs);
}
//-----------------------------------------------------------------------------------------------
- bool core::get_alternative_blocks(std::list<block>& blocks) const
+ bool core::get_alternative_blocks(std::vector<block>& blocks) const
{
return m_blockchain_storage.get_alternative_blocks(blocks);
}
@@ -438,6 +437,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 +458,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 +478,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 +650,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");
@@ -705,18 +672,20 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------------------------------------
- bool core::handle_incoming_txs(const std::list<blobdata>& tx_blobs, std::vector<tx_verification_context>& tvc, bool keeped_by_block, bool relayed, bool do_not_relay)
+ bool core::handle_incoming_txs(const std::vector<blobdata>& tx_blobs, std::vector<tx_verification_context>& tvc, bool keeped_by_block, bool relayed, bool do_not_relay)
{
TRY_ENTRY();
+ CRITICAL_REGION_LOCAL(m_incoming_tx_lock);
struct result { bool res; cryptonote::transaction tx; crypto::hash hash; crypto::hash prefix_hash; bool in_txpool; bool in_blockchain; };
std::vector<result> results(tx_blobs.size());
tvc.resize(tx_blobs.size());
+ tools::threadpool& tpool = tools::threadpool::getInstance();
tools::threadpool::waiter waiter;
- std::list<blobdata>::const_iterator it = tx_blobs.begin();
+ std::vector<blobdata>::const_iterator it = tx_blobs.begin();
for (size_t i = 0; i < tx_blobs.size(); i++, ++it) {
- m_threadpool.submit(&waiter, [&, i, it] {
+ tpool.submit(&waiter, [&, i, it] {
try
{
results[i].res = handle_incoming_tx_pre(*it, tvc[i], results[i].tx, results[i].hash, results[i].prefix_hash, keeped_by_block, relayed, do_not_relay);
@@ -728,7 +697,7 @@ namespace cryptonote
}
});
}
- waiter.wait();
+ waiter.wait(&tpool);
it = tx_blobs.begin();
for (size_t i = 0; i < tx_blobs.size(); i++, ++it) {
if (!results[i].res)
@@ -743,7 +712,7 @@ namespace cryptonote
}
else
{
- m_threadpool.submit(&waiter, [&, i, it] {
+ tpool.submit(&waiter, [&, i, it] {
try
{
results[i].res = handle_incoming_tx_post(*it, tvc[i], results[i].tx, results[i].hash, results[i].prefix_hash, keeped_by_block, relayed, do_not_relay);
@@ -756,7 +725,7 @@ namespace cryptonote
});
}
}
- waiter.wait();
+ waiter.wait(&tpool);
bool ok = true;
it = tx_blobs.begin();
@@ -783,7 +752,7 @@ namespace cryptonote
//-----------------------------------------------------------------------------------------------
bool core::handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay)
{
- std::list<cryptonote::blobdata> tx_blobs;
+ std::vector<cryptonote::blobdata> tx_blobs;
tx_blobs.push_back(tx_blob);
std::vector<tx_verification_context> tvcv(1);
bool r = handle_incoming_txs(tx_blobs, tvcv, keeped_by_block, relayed, do_not_relay);
@@ -949,8 +918,8 @@ namespace cryptonote
const uint64_t end = start_offset + count - 1;
m_blockchain_storage.for_blocks_range(start_offset, end,
[this, &emission_amount, &total_fee_amount](uint64_t, const crypto::hash& hash, const block& b){
- std::list<transaction> txs;
- std::list<crypto::hash> missed_txs;
+ std::vector<transaction> txs;
+ std::vector<crypto::hash> missed_txs;
uint64_t coinbase_amount = get_outs_money_amount(b.miner_tx);
this->get_transactions(b.tx_hashes, txs, missed_txs);
uint64_t tx_fee_amount = 0;
@@ -1046,7 +1015,7 @@ namespace cryptonote
bool core::relay_txpool_transactions()
{
// we attempt to relay txes that should be relayed, but were not
- std::list<std::pair<crypto::hash, cryptonote::blobdata>> txs;
+ std::vector<std::pair<crypto::hash, cryptonote::blobdata>> txs;
if (m_mempool.get_relayable_transactions(txs) && !txs.empty())
{
cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context);
@@ -1064,7 +1033,7 @@ namespace cryptonote
//-----------------------------------------------------------------------------------------------
void core::on_transaction_relayed(const cryptonote::blobdata& tx_blob)
{
- std::list<std::pair<crypto::hash, cryptonote::blobdata>> txs;
+ std::vector<std::pair<crypto::hash, cryptonote::blobdata>> txs;
cryptonote::transaction tx;
crypto::hash tx_hash, tx_prefix_hash;
if (!parse_and_validate_tx_from_blob(tx_blob, tx, tx_hash, tx_prefix_hash))
@@ -1086,9 +1055,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::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, 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, get_miner_tx_hash, 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 +1075,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
@@ -1143,7 +1112,7 @@ namespace cryptonote
{
block_verification_context bvc = boost::value_initialized<block_verification_context>();
m_miner.pause();
- std::list<block_complete_entry> blocks;
+ std::vector<block_complete_entry> blocks;
try
{
blocks.push_back(get_block_complete_entry(b, m_mempool));
@@ -1167,8 +1136,8 @@ namespace cryptonote
cryptonote_connection_context exclude_context = boost::value_initialized<cryptonote_connection_context>();
NOTIFY_NEW_BLOCK::request arg = AUTO_VAL_INIT(arg);
arg.current_blockchain_height = m_blockchain_storage.get_current_blockchain_height();
- std::list<crypto::hash> missed_txs;
- std::list<cryptonote::blobdata> txs;
+ std::vector<crypto::hash> missed_txs;
+ std::vector<cryptonote::blobdata> txs;
m_blockchain_storage.get_transactions_blobs(b.tx_hashes, txs, missed_txs);
if(missed_txs.size() && m_blockchain_storage.get_block_id_by_height(get_block_height(b)) != get_block_hash(b))
{
@@ -1204,7 +1173,7 @@ namespace cryptonote
}
//-----------------------------------------------------------------------------------------------
- bool core::prepare_handle_incoming_blocks(const std::list<block_complete_entry> &blocks)
+ bool core::prepare_handle_incoming_blocks(const std::vector<block_complete_entry> &blocks)
{
m_incoming_tx_lock.lock();
m_blockchain_storage.prepare_handle_incoming_blocks(blocks);
@@ -1297,7 +1266,7 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------------------------------------
- bool core::get_pool_transactions(std::list<transaction>& txs, bool include_sensitive_data) const
+ bool core::get_pool_transactions(std::vector<transaction>& txs, bool include_sensitive_data) const
{
m_mempool.get_transactions(txs, include_sensitive_data);
return true;
@@ -1418,6 +1387,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);
@@ -1428,6 +1402,11 @@ namespace cryptonote
return get_blockchain_storage().get_hard_fork_version(height);
}
//-----------------------------------------------------------------------------------------------
+ uint64_t core::get_earliest_ideal_height_for_version(uint8_t version) const
+ {
+ return get_blockchain_storage().get_earliest_ideal_height_for_version(version);
+ }
+ //-----------------------------------------------------------------------------------------------
bool core::check_updates()
{
static const char software[] = "monero";
@@ -1575,7 +1554,7 @@ namespace cryptonote
return m_target_blockchain_height;
}
//-----------------------------------------------------------------------------------------------
- uint64_t core::prevalidate_block_hashes(uint64_t height, const std::list<crypto::hash> &hashes)
+ uint64_t core::prevalidate_block_hashes(uint64_t height, const std::vector<crypto::hash> &hashes)
{
return get_blockchain_storage().prevalidate_block_hashes(height, hashes);
}
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index abf79be1d..03000383e 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -39,7 +39,6 @@
#include "cryptonote_protocol/cryptonote_protocol_handler_common.h"
#include "storages/portable_storage_template_helper.h"
#include "common/download.h"
-#include "common/threadpool.h"
#include "common/command_line.h"
#include "tx_pool.h"
#include "blockchain.h"
@@ -134,7 +133,7 @@ namespace cryptonote
*
* @return true if the transactions made it to the transaction pool, otherwise false
*/
- bool handle_incoming_txs(const std::list<blobdata>& tx_blobs, std::vector<tx_verification_context>& tvc, bool keeped_by_block, bool relayed, bool do_not_relay);
+ bool handle_incoming_txs(const std::vector<blobdata>& tx_blobs, std::vector<tx_verification_context>& tvc, bool keeped_by_block, bool relayed, bool do_not_relay);
/**
* @brief handles an incoming block
@@ -157,7 +156,7 @@ namespace cryptonote
*
* @note see Blockchain::prepare_handle_incoming_blocks
*/
- bool prepare_handle_incoming_blocks(const std::list<block_complete_entry> &blocks);
+ bool prepare_handle_incoming_blocks(const std::vector<block_complete_entry> &blocks);
/**
* @copydoc Blockchain::cleanup_handle_incoming_blocks
@@ -309,25 +308,25 @@ namespace cryptonote
void get_blockchain_top(uint64_t& height, crypto::hash& top_id) const;
/**
- * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::list<std::pair<cryptonote::blobdata,block>>&, std::list<transaction>&) const
+ * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::vector<std::pair<cryptonote::blobdata,block>>&, std::vector<transaction>&) const
*
- * @note see Blockchain::get_blocks(uint64_t, size_t, std::list<std::pair<cryptonote::blobdata,block>>&, std::list<transaction>&) const
+ * @note see Blockchain::get_blocks(uint64_t, size_t, std::vector<std::pair<cryptonote::blobdata,block>>&, std::vector<transaction>&) const
*/
- bool get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks, std::list<cryptonote::blobdata>& txs) const;
+ bool get_blocks(uint64_t start_offset, size_t count, std::vector<std::pair<cryptonote::blobdata,block>>& blocks, std::vector<cryptonote::blobdata>& txs) const;
/**
- * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::list<std::pair<cryptonote::blobdata,block>>&) const
+ * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::vector<std::pair<cryptonote::blobdata,block>>&) const
*
- * @note see Blockchain::get_blocks(uint64_t, size_t, std::list<std::pair<cryptonote::blobdata,block>>&) const
+ * @note see Blockchain::get_blocks(uint64_t, size_t, std::vector<std::pair<cryptonote::blobdata,block>>&) const
*/
- bool get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks) const;
+ bool get_blocks(uint64_t start_offset, size_t count, std::vector<std::pair<cryptonote::blobdata,block>>& blocks) const;
/**
- * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::list<std::pair<cryptonote::blobdata,block>>&) const
+ * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::vector<std::pair<cryptonote::blobdata,block>>&) const
*
- * @note see Blockchain::get_blocks(uint64_t, size_t, std::list<std::pair<cryptonote::blobdata,block>>&) const
+ * @note see Blockchain::get_blocks(uint64_t, size_t, std::vector<std::pair<cryptonote::blobdata,block>>&) const
*/
- bool get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks) const;
+ bool get_blocks(uint64_t start_offset, size_t count, std::vector<block>& blocks) const;
/**
* @copydoc Blockchain::get_blocks(const t_ids_container&, t_blocks_container&, t_missed_container&) const
@@ -352,14 +351,14 @@ namespace cryptonote
*
* @note see Blockchain::get_transactions
*/
- bool get_transactions(const std::vector<crypto::hash>& txs_ids, std::list<cryptonote::blobdata>& txs, std::list<crypto::hash>& missed_txs) const;
+ bool get_transactions(const std::vector<crypto::hash>& txs_ids, std::vector<cryptonote::blobdata>& txs, std::vector<crypto::hash>& missed_txs) const;
/**
* @copydoc Blockchain::get_transactions
*
* @note see Blockchain::get_transactions
*/
- bool get_transactions(const std::vector<crypto::hash>& txs_ids, std::list<transaction>& txs, std::list<crypto::hash>& missed_txs) const;
+ bool get_transactions(const std::vector<crypto::hash>& txs_ids, std::vector<transaction>& txs, std::vector<crypto::hash>& missed_txs) const;
/**
* @copydoc Blockchain::get_block_by_hash
@@ -371,9 +370,9 @@ namespace cryptonote
/**
* @copydoc Blockchain::get_alternative_blocks
*
- * @note see Blockchain::get_alternative_blocks(std::list<block>&) const
+ * @note see Blockchain::get_alternative_blocks(std::vector<block>&) const
*/
- bool get_alternative_blocks(std::list<block>& blocks) const;
+ bool get_alternative_blocks(std::vector<block>& blocks) const;
/**
* @copydoc Blockchain::get_alternative_blocks_count
@@ -430,7 +429,7 @@ namespace cryptonote
*
* @note see tx_memory_pool::get_transactions
*/
- bool get_pool_transactions(std::list<transaction>& txs, bool include_unrelayed_txes = true) const;
+ bool get_pool_transactions(std::vector<transaction>& txs, bool include_unrelayed_txes = true) const;
/**
* @copydoc tx_memory_pool::get_txpool_backlog
@@ -513,11 +512,11 @@ namespace cryptonote
bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const;
/**
- * @copydoc Blockchain::find_blockchain_supplement(const uint64_t, const std::list<crypto::hash>&, std::list<std::pair<cryptonote::blobdata, std::list<cryptonote::blobdata> > >&, uint64_t&, uint64_t&, size_t) const
+ * @copydoc Blockchain::find_blockchain_supplement(const uint64_t, const std::list<crypto::hash>&, std::vector<std::pair<cryptonote::blobdata, std::vector<cryptonote::blobdata> > >&, uint64_t&, uint64_t&, size_t) const
*
- * @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
+ * @note see Blockchain::find_blockchain_supplement(const uint64_t, const std::list<crypto::hash>&, std::vector<std::pair<cryptonote::blobdata, std::vector<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::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_count) const;
/**
* @brief gets some stats about the daemon
@@ -576,7 +575,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 +641,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
@@ -656,6 +662,13 @@ namespace cryptonote
uint8_t get_hard_fork_version(uint64_t height) const;
/**
+ * @brief return the earliest block a given version may activate
+ *
+ * @return what it says above
+ */
+ uint64_t get_earliest_ideal_height_for_version(uint8_t version) const;
+
+ /**
* @brief gets start_time
*
*/
@@ -750,7 +763,7 @@ namespace cryptonote
*
* @return number of usable blocks
*/
- uint64_t prevalidate_block_hashes(uint64_t height, const std::list<crypto::hash> &hashes);
+ uint64_t prevalidate_block_hashes(uint64_t height, const std::vector<crypto::hash> &hashes);
/**
* @brief get free disk space on the blockchain partition
@@ -977,8 +990,6 @@ namespace cryptonote
std::unordered_set<crypto::hash> bad_semantics_txes[2];
boost::mutex bad_semantics_txes_lock;
- tools::threadpool& m_threadpool;
-
enum {
UPDATES_DISABLED,
UPDATES_NOTIFY,
diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp
index db4ab9e11..071ce591e 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.cpp
+++ b/src/cryptonote_core/cryptonote_tx_utils.cpp
@@ -29,6 +29,7 @@
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#include <unordered_set>
+#include <random>
#include "include_base_utils.h"
#include "string_tools.h"
using namespace epee;
@@ -194,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();
@@ -314,9 +315,10 @@ namespace cryptonote
tx.vin.push_back(input_to_key);
}
- // "Shuffle" outs
- std::vector<tx_destination_entry> shuffled_dsts(destinations);
- std::random_shuffle(shuffled_dsts.begin(), shuffled_dsts.end(), [](unsigned int i) { return crypto::rand<unsigned int>() % i; });
+ 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());
@@ -599,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);
@@ -628,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 0af9737a7..8dee2b922 100644
--- a/src/cryptonote_core/tx_pool.cpp
+++ b/src/cryptonote_core/tx_pool.cpp
@@ -239,6 +239,7 @@ namespace cryptonote
meta.relayed = relayed;
meta.do_not_relay = do_not_relay;
meta.double_spend_seen = have_tx_keyimges_as_spent(tx);
+ meta.bf_padding = 0;
memset(meta.padding, 0, sizeof(meta.padding));
try
{
@@ -278,6 +279,7 @@ namespace cryptonote
meta.relayed = relayed;
meta.do_not_relay = do_not_relay;
meta.double_spend_seen = false;
+ meta.bf_padding = 0;
memset(meta.padding, 0, sizeof(meta.padding));
try
@@ -556,11 +558,12 @@ namespace cryptonote
}
//---------------------------------------------------------------------------------
//TODO: investigate whether boolean return is appropriate
- bool tx_memory_pool::get_relayable_transactions(std::list<std::pair<crypto::hash, cryptonote::blobdata>> &txs) const
+ bool tx_memory_pool::get_relayable_transactions(std::vector<std::pair<crypto::hash, cryptonote::blobdata>> &txs) const
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
const uint64_t now = time(NULL);
+ txs.reserve(m_blockchain.get_txpool_tx_count());
m_blockchain.for_all_txpool_txes([this, now, &txs](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *){
// 0 fee transactions are never relayed
if(meta.fee > 0 && !meta.do_not_relay && now - meta.last_relayed_time > get_relay_delay(now, meta.receive_time))
@@ -588,7 +591,7 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------------------------
- void tx_memory_pool::set_relayed(const std::list<std::pair<crypto::hash, cryptonote::blobdata>> &txs)
+ void tx_memory_pool::set_relayed(const std::vector<std::pair<crypto::hash, cryptonote::blobdata>> &txs)
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
@@ -621,10 +624,11 @@ namespace cryptonote
return m_blockchain.get_txpool_tx_count(include_unrelayed_txes);
}
//---------------------------------------------------------------------------------
- void tx_memory_pool::get_transactions(std::list<transaction>& txs, bool include_unrelayed_txes) const
+ void tx_memory_pool::get_transactions(std::vector<transaction>& txs, bool include_unrelayed_txes) const
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
+ txs.reserve(m_blockchain.get_txpool_tx_count(include_unrelayed_txes));
m_blockchain.for_all_txpool_txes([&txs](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){
transaction tx;
if (!parse_and_validate_tx_from_blob(*bd, tx))
@@ -642,6 +646,7 @@ namespace cryptonote
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
+ txs.reserve(m_blockchain.get_txpool_tx_count(include_unrelayed_txes));
m_blockchain.for_all_txpool_txes([&txs](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){
txs.push_back(txid);
return true;
@@ -653,6 +658,7 @@ namespace cryptonote
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
const uint64_t now = time(NULL);
+ backlog.reserve(m_blockchain.get_txpool_tx_count(include_unrelayed_txes));
m_blockchain.for_all_txpool_txes([&backlog, now](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){
backlog.push_back({meta.blob_size, meta.fee, meta.receive_time - now});
return true;
@@ -741,6 +747,8 @@ namespace cryptonote
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
+ tx_infos.reserve(m_blockchain.get_txpool_tx_count());
+ key_image_infos.reserve(m_blockchain.get_txpool_tx_count());
m_blockchain.for_all_txpool_txes([&tx_infos, key_image_infos, include_sensitive_data](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){
tx_info txi;
txi.id_hash = epee::string_tools::pod_to_hex(txid);
@@ -811,6 +819,8 @@ namespace cryptonote
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
+ tx_infos.reserve(m_blockchain.get_txpool_tx_count());
+ key_image_infos.reserve(m_blockchain.get_txpool_tx_count());
m_blockchain.for_all_txpool_txes([&tx_infos, key_image_infos](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){
cryptonote::rpc::tx_in_pool txi;
txi.tx_hash = txid;
@@ -927,8 +937,26 @@ namespace cryptonote
m_transactions_lock.unlock();
}
//---------------------------------------------------------------------------------
- bool tx_memory_pool::is_transaction_ready_to_go(txpool_tx_meta_t& txd, transaction &tx) const
+ bool tx_memory_pool::is_transaction_ready_to_go(txpool_tx_meta_t& txd, const cryptonote::blobdata &txblob, transaction &tx) const
{
+ struct transction_parser
+ {
+ transction_parser(const cryptonote::blobdata &txblob, transaction &tx): txblob(txblob), tx(tx), parsed(false) {}
+ cryptonote::transaction &operator()()
+ {
+ if (!parsed)
+ {
+ if (!parse_and_validate_tx_from_blob(txblob, tx))
+ throw std::runtime_error("failed to parse transaction blob");
+ parsed = true;
+ }
+ return tx;
+ }
+ const cryptonote::blobdata &txblob;
+ transaction &tx;
+ bool parsed;
+ } lazy_tx(txblob, tx);
+
//not the best implementation at this time, sorry :(
//check is ring_signature already checked ?
if(txd.max_used_block_id == null_hash)
@@ -938,7 +966,7 @@ namespace cryptonote
return false;//we already sure that this tx is broken for this height
tx_verification_context tvc;
- if(!m_blockchain.check_tx_inputs(tx, txd.max_used_block_height, txd.max_used_block_id, tvc))
+ if(!m_blockchain.check_tx_inputs(lazy_tx(), txd.max_used_block_height, txd.max_used_block_id, tvc))
{
txd.last_failed_height = m_blockchain.get_current_blockchain_height()-1;
txd.last_failed_id = m_blockchain.get_block_id_by_height(txd.last_failed_height);
@@ -955,7 +983,7 @@ namespace cryptonote
return false;
//check ring signature again, it is possible (with very small chance) that this transaction become again valid
tx_verification_context tvc;
- if(!m_blockchain.check_tx_inputs(tx, txd.max_used_block_height, txd.max_used_block_id, tvc))
+ if(!m_blockchain.check_tx_inputs(lazy_tx(), txd.max_used_block_height, txd.max_used_block_id, tvc))
{
txd.last_failed_height = m_blockchain.get_current_blockchain_height()-1;
txd.last_failed_id = m_blockchain.get_block_id_by_height(txd.last_failed_height);
@@ -964,7 +992,7 @@ namespace cryptonote
}
}
//if we here, transaction seems valid, but, anyway, check for key_images collisions with blockchain, just to be sure
- if(m_blockchain.have_tx_keyimges_as_spent(tx))
+ if(m_blockchain.have_tx_keyimges_as_spent(lazy_tx()))
{
txd.double_spend_seen = true;
return false;
@@ -1068,10 +1096,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);
@@ -1144,18 +1168,21 @@ namespace cryptonote
cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(sorted_it->second);
cryptonote::transaction tx;
- if (!parse_and_validate_tx_from_blob(txblob, tx))
- {
- MERROR("Failed to parse tx from txpool");
- sorted_it++;
- continue;
- }
// Skip transactions that are not ready to be
// included into the blockchain or that are
// missing key images
const cryptonote::txpool_tx_meta_t original_meta = meta;
- bool ready = is_transaction_ready_to_go(meta, tx);
+ bool ready = false;
+ try
+ {
+ ready = is_transaction_ready_to_go(meta, txblob, tx);
+ }
+ catch (const std::exception &e)
+ {
+ MERROR("Failed to check transaction readiness: " << e.what());
+ // continue, not fatal
+ }
if (memcmp(&original_meta, &meta, sizeof(meta)))
{
try
@@ -1268,24 +1295,33 @@ namespace cryptonote
m_spent_key_images.clear();
m_txpool_size = 0;
std::vector<crypto::hash> remove;
- bool r = m_blockchain.for_all_txpool_txes([this, &remove](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd) {
- cryptonote::transaction tx;
- if (!parse_and_validate_tx_from_blob(*bd, tx))
- {
- MWARNING("Failed to parse tx from txpool, removing");
- remove.push_back(txid);
- }
- if (!insert_key_images(tx, meta.kept_by_block))
- {
- MFATAL("Failed to insert key images from txpool tx");
+
+ // first add the not kept by block, then the kept by block,
+ // to avoid rejection due to key image collision
+ for (int pass = 0; pass < 2; ++pass)
+ {
+ const bool kept = pass == 1;
+ bool r = m_blockchain.for_all_txpool_txes([this, &remove, kept](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd) {
+ if (!!kept != !!meta.kept_by_block)
+ return true;
+ cryptonote::transaction tx;
+ if (!parse_and_validate_tx_from_blob(*bd, tx))
+ {
+ MWARNING("Failed to parse tx from txpool, removing");
+ remove.push_back(txid);
+ }
+ if (!insert_key_images(tx, meta.kept_by_block))
+ {
+ MFATAL("Failed to insert key images from txpool tx");
+ return false;
+ }
+ m_txs_by_fee_and_receive_time.emplace(std::pair<double, time_t>(meta.fee / (double)meta.blob_size, meta.receive_time), txid);
+ m_txpool_size += meta.blob_size;
+ return true;
+ }, true);
+ if (!r)
return false;
- }
- m_txs_by_fee_and_receive_time.emplace(std::pair<double, time_t>(meta.fee / (double)meta.blob_size, meta.receive_time), txid);
- m_txpool_size += meta.blob_size;
- return true;
- }, true);
- if (!r)
- return false;
+ }
if (!remove.empty())
{
LockedTXN lock(m_blockchain);
diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h
index 19cd83ed9..5ccb71196 100644
--- a/src/cryptonote_core/tx_pool.h
+++ b/src/cryptonote_core/tx_pool.h
@@ -237,7 +237,7 @@ namespace cryptonote
* @param include_unrelayed_txes include unrelayed txes in the result
*
*/
- void get_transactions(std::list<transaction>& txs, bool include_unrelayed_txes = true) const;
+ void get_transactions(std::vector<transaction>& txs, bool include_unrelayed_txes = true) const;
/**
* @brief get a list of all transaction hashes in the pool
@@ -324,14 +324,14 @@ namespace cryptonote
*
* @return true
*/
- bool get_relayable_transactions(std::list<std::pair<crypto::hash, cryptonote::blobdata>>& txs) const;
+ bool get_relayable_transactions(std::vector<std::pair<crypto::hash, cryptonote::blobdata>>& txs) const;
/**
* @brief tell the pool that certain transactions were just relayed
*
* @param txs the list of transactions (and their hashes)
*/
- void set_relayed(const std::list<std::pair<crypto::hash, cryptonote::blobdata>>& txs);
+ void set_relayed(const std::vector<std::pair<crypto::hash, cryptonote::blobdata>>& txs);
/**
* @brief get the total number of transactions in the pool
@@ -499,10 +499,12 @@ namespace cryptonote
* @brief check if a transaction is a valid candidate for inclusion in a block
*
* @param txd the transaction to check (and info about it)
+ * @param txblob the transaction blob to check
+ * @param tx the parsed transaction, if successful
*
* @return true if the transaction is good to go, otherwise false
*/
- bool is_transaction_ready_to_go(txpool_tx_meta_t& txd, transaction &tx) const;
+ bool is_transaction_ready_to_go(txpool_tx_meta_t& txd, const cryptonote::blobdata &txblob, transaction &tx) const;
/**
* @brief mark all transactions double spending the one passed
diff --git a/src/cryptonote_protocol/block_queue.cpp b/src/cryptonote_protocol/block_queue.cpp
index 9ae33d540..c39d67ceb 100644
--- a/src/cryptonote_protocol/block_queue.cpp
+++ b/src/cryptonote_protocol/block_queue.cpp
@@ -50,10 +50,10 @@ namespace std {
namespace cryptonote
{
-void block_queue::add_blocks(uint64_t height, std::list<cryptonote::block_complete_entry> bcel, const boost::uuids::uuid &connection_id, float rate, size_t size)
+void block_queue::add_blocks(uint64_t height, std::vector<cryptonote::block_complete_entry> bcel, const boost::uuids::uuid &connection_id, float rate, size_t size)
{
boost::unique_lock<boost::recursive_mutex> lock(mutex);
- std::list<crypto::hash> hashes;
+ std::vector<crypto::hash> hashes;
bool has_hashes = remove_span(height, &hashes);
blocks.insert(span(height, std::move(bcel), connection_id, rate, size));
if (has_hashes)
@@ -97,7 +97,7 @@ void block_queue::flush_stale_spans(const std::set<boost::uuids::uuid> &live_con
}
}
-bool block_queue::remove_span(uint64_t start_block_height, std::list<crypto::hash> *hashes)
+bool block_queue::remove_span(uint64_t start_block_height, std::vector<crypto::hash> *hashes)
{
boost::unique_lock<boost::recursive_mutex> lock(mutex);
for (block_map::iterator i = blocks.begin(); i != blocks.end(); ++i)
@@ -172,7 +172,7 @@ bool block_queue::requested(const crypto::hash &hash) const
return false;
}
-std::pair<uint64_t, uint64_t> block_queue::reserve_span(uint64_t first_block_height, uint64_t last_block_height, uint64_t max_blocks, const boost::uuids::uuid &connection_id, const std::list<crypto::hash> &block_hashes, boost::posix_time::ptime time)
+std::pair<uint64_t, uint64_t> block_queue::reserve_span(uint64_t first_block_height, uint64_t last_block_height, uint64_t max_blocks, const boost::uuids::uuid &connection_id, const std::vector<crypto::hash> &block_hashes, boost::posix_time::ptime time)
{
boost::unique_lock<boost::recursive_mutex> lock(mutex);
@@ -183,14 +183,14 @@ std::pair<uint64_t, uint64_t> block_queue::reserve_span(uint64_t first_block_hei
}
uint64_t span_start_height = last_block_height - block_hashes.size() + 1;
- std::list<crypto::hash>::const_iterator i = block_hashes.begin();
+ std::vector<crypto::hash>::const_iterator i = block_hashes.begin();
while (i != block_hashes.end() && requested(*i))
{
++i;
++span_start_height;
}
uint64_t span_length = 0;
- std::list<crypto::hash> hashes;
+ std::vector<crypto::hash> hashes;
while (i != block_hashes.end() && span_length < max_blocks)
{
hashes.push_back(*i);
@@ -230,7 +230,7 @@ std::pair<uint64_t, uint64_t> block_queue::get_start_gap_span() const
return std::make_pair(current_height + 1, first_span_height - current_height - 1);
}
-std::pair<uint64_t, uint64_t> block_queue::get_next_span_if_scheduled(std::list<crypto::hash> &hashes, boost::uuids::uuid &connection_id, boost::posix_time::ptime &time) const
+std::pair<uint64_t, uint64_t> block_queue::get_next_span_if_scheduled(std::vector<crypto::hash> &hashes, boost::uuids::uuid &connection_id, boost::posix_time::ptime &time) const
{
boost::unique_lock<boost::recursive_mutex> lock(mutex);
if (blocks.empty())
@@ -248,7 +248,7 @@ std::pair<uint64_t, uint64_t> block_queue::get_next_span_if_scheduled(std::list<
return std::make_pair(i->start_block_height, i->nblocks);
}
-void block_queue::set_span_hashes(uint64_t start_height, const boost::uuids::uuid &connection_id, std::list<crypto::hash> hashes)
+void block_queue::set_span_hashes(uint64_t start_height, const boost::uuids::uuid &connection_id, std::vector<crypto::hash> hashes)
{
boost::unique_lock<boost::recursive_mutex> lock(mutex);
for (block_map::iterator i = blocks.begin(); i != blocks.end(); ++i)
@@ -264,7 +264,7 @@ void block_queue::set_span_hashes(uint64_t start_height, const boost::uuids::uui
}
}
-bool block_queue::get_next_span(uint64_t &height, std::list<cryptonote::block_complete_entry> &bcel, boost::uuids::uuid &connection_id, bool filled) const
+bool block_queue::get_next_span(uint64_t &height, std::vector<cryptonote::block_complete_entry> &bcel, boost::uuids::uuid &connection_id, bool filled) const
{
boost::unique_lock<boost::recursive_mutex> lock(mutex);
if (blocks.empty())
diff --git a/src/cryptonote_protocol/block_queue.h b/src/cryptonote_protocol/block_queue.h
index 69ddaa435..9059e89ac 100644
--- a/src/cryptonote_protocol/block_queue.h
+++ b/src/cryptonote_protocol/block_queue.h
@@ -31,7 +31,7 @@
#pragma once
#include <string>
-#include <list>
+#include <vector>
#include <set>
#include <boost/thread/recursive_mutex.hpp>
#include <boost/uuid/uuid.hpp>
@@ -49,15 +49,15 @@ namespace cryptonote
struct span
{
uint64_t start_block_height;
- std::list<crypto::hash> hashes;
- std::list<cryptonote::block_complete_entry> blocks;
+ std::vector<crypto::hash> hashes;
+ std::vector<cryptonote::block_complete_entry> blocks;
boost::uuids::uuid connection_id;
uint64_t nblocks;
float rate;
size_t size;
boost::posix_time::ptime time;
- span(uint64_t start_block_height, std::list<cryptonote::block_complete_entry> blocks, const boost::uuids::uuid &connection_id, float rate, size_t size):
+ span(uint64_t start_block_height, std::vector<cryptonote::block_complete_entry> blocks, const boost::uuids::uuid &connection_id, float rate, size_t size):
start_block_height(start_block_height), blocks(std::move(blocks)), connection_id(connection_id), nblocks(this->blocks.size()), rate(rate), size(size), time() {}
span(uint64_t start_block_height, uint64_t nblocks, const boost::uuids::uuid &connection_id, boost::posix_time::ptime time):
start_block_height(start_block_height), connection_id(connection_id), nblocks(nblocks), rate(0.0f), size(0), time(time) {}
@@ -67,21 +67,21 @@ namespace cryptonote
typedef std::set<span> block_map;
public:
- void add_blocks(uint64_t height, std::list<cryptonote::block_complete_entry> bcel, const boost::uuids::uuid &connection_id, float rate, size_t size);
+ void add_blocks(uint64_t height, std::vector<cryptonote::block_complete_entry> bcel, const boost::uuids::uuid &connection_id, float rate, size_t size);
void add_blocks(uint64_t height, uint64_t nblocks, const boost::uuids::uuid &connection_id, boost::posix_time::ptime time = boost::date_time::min_date_time);
void flush_spans(const boost::uuids::uuid &connection_id, bool all = false);
void flush_stale_spans(const std::set<boost::uuids::uuid> &live_connections);
- bool remove_span(uint64_t start_block_height, std::list<crypto::hash> *hashes = NULL);
+ bool remove_span(uint64_t start_block_height, std::vector<crypto::hash> *hashes = NULL);
void remove_spans(const boost::uuids::uuid &connection_id, uint64_t start_block_height);
uint64_t get_max_block_height() const;
void print() const;
std::string get_overview() const;
- std::pair<uint64_t, uint64_t> reserve_span(uint64_t first_block_height, uint64_t last_block_height, uint64_t max_blocks, const boost::uuids::uuid &connection_id, const std::list<crypto::hash> &block_hashes, boost::posix_time::ptime time = boost::posix_time::microsec_clock::universal_time());
+ std::pair<uint64_t, uint64_t> reserve_span(uint64_t first_block_height, uint64_t last_block_height, uint64_t max_blocks, const boost::uuids::uuid &connection_id, const std::vector<crypto::hash> &block_hashes, boost::posix_time::ptime time = boost::posix_time::microsec_clock::universal_time());
bool is_blockchain_placeholder(const span &span) const;
std::pair<uint64_t, uint64_t> get_start_gap_span() const;
- std::pair<uint64_t, uint64_t> get_next_span_if_scheduled(std::list<crypto::hash> &hashes, boost::uuids::uuid &connection_id, boost::posix_time::ptime &time) const;
- void set_span_hashes(uint64_t start_height, const boost::uuids::uuid &connection_id, std::list<crypto::hash> hashes);
- bool get_next_span(uint64_t &height, std::list<cryptonote::block_complete_entry> &bcel, boost::uuids::uuid &connection_id, bool filled = true) const;
+ std::pair<uint64_t, uint64_t> get_next_span_if_scheduled(std::vector<crypto::hash> &hashes, boost::uuids::uuid &connection_id, boost::posix_time::ptime &time) const;
+ void set_span_hashes(uint64_t start_height, const boost::uuids::uuid &connection_id, std::vector<crypto::hash> hashes);
+ bool get_next_span(uint64_t &height, std::vector<cryptonote::block_complete_entry> &bcel, boost::uuids::uuid &connection_id, bool filled = true) const;
bool has_next_span(const boost::uuids::uuid &connection_id, bool &filled) const;
size_t get_data_size() const;
size_t get_num_filled_spans_prefix() const;
diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h
index cf0043287..db159f0f4 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_defs.h
+++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h
@@ -109,7 +109,7 @@ namespace cryptonote
struct block_complete_entry
{
blobdata block;
- std::list<blobdata> txs;
+ std::vector<blobdata> txs;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(block)
KV_SERIALIZE(txs)
@@ -145,7 +145,7 @@ namespace cryptonote
struct request
{
- std::list<blobdata> txs;
+ std::vector<blobdata> txs;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(txs)
@@ -161,8 +161,8 @@ namespace cryptonote
struct request
{
- std::list<crypto::hash> txs;
- std::list<crypto::hash> blocks;
+ std::vector<crypto::hash> txs;
+ std::vector<crypto::hash> blocks;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(txs)
@@ -177,9 +177,9 @@ namespace cryptonote
struct request
{
- std::list<blobdata> txs;
- std::list<block_complete_entry> blocks;
- std::list<crypto::hash> missed_ids;
+ std::vector<blobdata> txs;
+ std::vector<block_complete_entry> blocks;
+ std::vector<crypto::hash> missed_ids;
uint64_t current_blockchain_height;
BEGIN_KV_SERIALIZE_MAP()
@@ -230,7 +230,7 @@ namespace cryptonote
uint64_t start_height;
uint64_t total_height;
uint64_t cumulative_difficulty;
- std::list<crypto::hash> m_block_ids;
+ std::vector<crypto::hash> m_block_ids;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(start_height)
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
index 5d91ad569..56aa1dc06 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;
@@ -350,7 +351,7 @@ namespace cryptonote
return 1;
}
m_core.pause_mine();
- std::list<block_complete_entry> blocks;
+ std::vector<block_complete_entry> blocks;
blocks.push_back(arg.b);
m_core.prepare_handle_incoming_blocks(blocks);
for(auto tx_blob_it = arg.b.txs.begin(); tx_blob_it!=arg.b.txs.end();tx_blob_it++)
@@ -437,7 +438,7 @@ namespace cryptonote
}
}
- std::list<blobdata> have_tx;
+ std::vector<blobdata> have_tx;
// Instead of requesting missing transactions by hash like BTC,
// we do it by index (thanks to a suggestion from moneromooo) because
@@ -577,8 +578,8 @@ namespace cryptonote
else
{
std::vector<crypto::hash> tx_ids;
- std::list<transaction> txes;
- std::list<crypto::hash> missing;
+ std::vector<transaction> txes;
+ std::vector<crypto::hash> missing;
tx_ids.push_back(tx_hash);
if (m_core.get_transactions(tx_ids, txes, missing) && missing.empty())
{
@@ -625,7 +626,7 @@ namespace cryptonote
b.block = arg.b.block;
b.txs = have_tx;
- std::list<block_complete_entry> blocks;
+ std::vector<block_complete_entry> blocks;
blocks.push_back(b);
m_core.prepare_handle_incoming_blocks(blocks);
@@ -686,8 +687,8 @@ namespace cryptonote
{
MLOG_P2P_MESSAGE("Received NOTIFY_REQUEST_FLUFFY_MISSING_TX (" << arg.missing_tx_indices.size() << " txes), block hash " << arg.block_hash);
- std::list<std::pair<cryptonote::blobdata, block>> local_blocks;
- std::list<cryptonote::blobdata> local_txs;
+ std::vector<std::pair<cryptonote::blobdata, block>> local_blocks;
+ std::vector<cryptonote::blobdata> local_txs;
block b;
if (!m_core.get_block_by_hash(arg.block_hash, b))
@@ -724,8 +725,8 @@ namespace cryptonote
}
}
- std::list<cryptonote::transaction> txs;
- std::list<crypto::hash> missed;
+ std::vector<cryptonote::transaction> txs;
+ std::vector<crypto::hash> missed;
if (!m_core.get_transactions(txids, txs, missed))
{
LOG_ERROR_CCONTEXT("Failed to handle request NOTIFY_REQUEST_FLUFFY_MISSING_TX, "
@@ -773,10 +774,12 @@ namespace cryptonote
return 1;
}
- for(auto tx_blob_it = arg.txs.begin(); tx_blob_it!=arg.txs.end();)
+ std::vector<cryptonote::blobdata> newtxs;
+ newtxs.reserve(arg.txs.size());
+ for (size_t i = 0; i < arg.txs.size(); ++i)
{
cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc);
- m_core.handle_incoming_tx(*tx_blob_it, tvc, false, true, false);
+ m_core.handle_incoming_tx(arg.txs[i], tvc, false, true, false);
if(tvc.m_verifivation_failed)
{
LOG_PRINT_CCONTEXT_L1("Tx verification failed, dropping connection");
@@ -784,10 +787,9 @@ namespace cryptonote
return 1;
}
if(tvc.m_should_be_relayed)
- ++tx_blob_it;
- else
- arg.txs.erase(tx_blob_it++);
+ newtxs.push_back(std::move(arg.txs[i]));
}
+ arg.txs = std::move(newtxs);
if(arg.txs.size())
{
@@ -795,7 +797,7 @@ namespace cryptonote
relay_transactions(arg, context);
}
- return true;
+ return 1;
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
@@ -995,7 +997,7 @@ skip:
{
const uint64_t previous_height = m_core.get_current_blockchain_height();
uint64_t start_height;
- std::list<cryptonote::block_complete_entry> blocks;
+ std::vector<cryptonote::block_complete_entry> blocks;
boost::uuids::uuid span_connection_id;
if (!m_block_queue.get_next_span(start_height, blocks, span_connection_id))
{
@@ -1008,6 +1010,7 @@ skip:
if (blocks.empty())
{
MERROR("Next span has no blocks");
+ m_block_queue.remove_spans(span_connection_id, start_height);
break;
}
@@ -1015,6 +1018,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 +1035,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,9 +1069,9 @@ 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();
+ std::vector<blobdata>::const_iterator it = block_entry.txs.begin();
for (size_t i = 0; i < tvc.size(); ++i, ++it)
{
if(tvc[i].m_verifivation_failed)
@@ -1075,7 +1080,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 +1109,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 +1128,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");
@@ -1244,7 +1249,7 @@ skip:
template<class t_core>
bool t_cryptonote_protocol_handler<t_core>::should_download_next_span(cryptonote_connection_context& context) const
{
- std::list<crypto::hash> hashes;
+ std::vector<crypto::hash> hashes;
boost::uuids::uuid span_connection_id;
boost::posix_time::ptime request_time;
std::pair<uint64_t, uint64_t> span;
@@ -1263,7 +1268,7 @@ skip:
// we might be in a weird case where there is a filled next span,
// but it starts higher than the current height
uint64_t height;
- std::list<cryptonote::block_complete_entry> bcel;
+ std::vector<cryptonote::block_complete_entry> bcel;
if (!m_block_queue.get_next_span(height, bcel, span_connection_id, true))
return false;
if (height > m_core.get_current_blockchain_height())
@@ -1363,13 +1368,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));
}
}
@@ -1411,7 +1416,7 @@ skip:
{
if (span.second == 0)
{
- std::list<crypto::hash> hashes;
+ std::vector<crypto::hash> hashes;
boost::uuids::uuid span_connection_id;
boost::posix_time::ptime time;
span = m_block_queue.get_next_span_if_scheduled(hashes, span_connection_id, time);
@@ -1437,14 +1442,18 @@ skip:
goto skip;
}
// take out blocks we already have
- while (!context.m_needed_objects.empty() && m_core.have_block(context.m_needed_objects.front()))
+ size_t skip = 0;
+ while (skip < context.m_needed_objects.size() && m_core.have_block(context.m_needed_objects[skip]))
{
// if we're popping the last hash, record it so we can ask again from that hash,
// this prevents never being able to progress on peers we get old hash lists from
- if (context.m_needed_objects.size() == 1)
- context.m_last_known_hash = context.m_needed_objects.front();
- context.m_needed_objects.pop_front();
+ if (skip + 1 == context.m_needed_objects.size())
+ context.m_last_known_hash = context.m_needed_objects[skip];
+ ++skip;
}
+ if (skip > 0)
+ context.m_needed_objects = std::vector<crypto::hash>(context.m_needed_objects.begin() + skip, context.m_needed_objects.end());
+
const uint64_t first_block_height = context.m_last_response_height - context.m_needed_objects.size() + 1;
span = m_block_queue.reserve_span(first_block_height, context.m_last_response_height, count_limit, context.m_connection_id, context.m_needed_objects);
MDEBUG(context << " span from " << first_block_height << ": " << span.first << "/" << span.second);
@@ -1452,7 +1461,7 @@ skip:
if (span.second == 0 && !force_next_span)
{
MDEBUG(context << " still no span reserved, we may be in the corner case of next span scheduled and everything else scheduled/filled");
- std::list<crypto::hash> hashes;
+ std::vector<crypto::hash> hashes;
boost::uuids::uuid span_connection_id;
boost::posix_time::ptime time;
span = m_block_queue.get_next_span_if_scheduled(hashes, span_connection_id, time);
@@ -1483,23 +1492,21 @@ skip:
MERROR("ERROR: skip " << skip << ", m_needed_objects " << context.m_needed_objects.size() << ", first_context_block_height" << first_context_block_height);
return false;
}
- while (skip--)
- context.m_needed_objects.pop_front();
+ if (skip > 0)
+ context.m_needed_objects = std::vector<crypto::hash>(context.m_needed_objects.begin() + skip, context.m_needed_objects.end());
if (context.m_needed_objects.size() < span.second)
{
MERROR("ERROR: span " << span.first << "/" << span.second << ", m_needed_objects " << context.m_needed_objects.size());
return false;
}
- auto it = context.m_needed_objects.begin();
for (size_t n = 0; n < span.second; ++n)
{
- req.blocks.push_back(*it);
+ req.blocks.push_back(context.m_needed_objects[n]);
++count;
- context.m_requested_objects.insert(*it);
- auto j = it++;
- context.m_needed_objects.erase(j);
+ context.m_requested_objects.insert(context.m_needed_objects[n]);
}
+ context.m_needed_objects = std::vector<crypto::hash>(context.m_needed_objects.begin() + span.second, context.m_needed_objects.end());
}
context.m_last_request_time = boost::posix_time::microsec_clock::universal_time();
@@ -1626,10 +1633,10 @@ skip:
}
uint64_t n_use_blocks = m_core.prevalidate_block_hashes(arg.start_height, arg.m_block_ids);
- if (n_use_blocks == 0)
+ if (n_use_blocks + HASH_OF_HASHES_STEP <= arg.m_block_ids.size())
{
- LOG_ERROR_CCONTEXT("Peer yielded no usable blocks, dropping connection");
- drop_connection(context, false, false);
+ LOG_ERROR_CCONTEXT("Most blocks are invalid, dropping connection");
+ drop_connection(context, true, false);
return 1;
}
@@ -1660,7 +1667,7 @@ skip:
{
NOTIFY_NEW_FLUFFY_BLOCK::request fluffy_arg = AUTO_VAL_INIT(fluffy_arg);
fluffy_arg.current_blockchain_height = arg.current_blockchain_height;
- std::list<blobdata> fluffy_txs;
+ std::vector<blobdata> fluffy_txs;
fluffy_arg.b = arg.b;
fluffy_arg.b.txs = fluffy_txs;
@@ -1693,7 +1700,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>
@@ -1708,6 +1715,9 @@ skip:
template<class t_core>
void t_cryptonote_protocol_handler<t_core>::drop_connection(cryptonote_connection_context &context, bool add_fail, bool flush_all_spans)
{
+ LOG_DEBUG_CC(context, "dropping connection id " << context.m_connection_id <<
+ ", add_fail " << add_fail << ", flush_all_spans " << flush_all_spans);
+
if (add_fail)
m_p2p->add_host_fail(context.m_remote_address);
diff --git a/src/daemon/command_line_args.h b/src/daemon/command_line_args.h
index 4673590aa..cba71bf3b 100644
--- a/src/daemon/command_line_args.h
+++ b/src/daemon/command_line_args.h
@@ -72,6 +72,11 @@ namespace daemon_args
, "Specify maximum log file size [B]"
, MAX_LOG_FILE_SIZE
};
+ const command_line::arg_descriptor<std::size_t> arg_max_log_files = {
+ "max-log-files"
+ , "Specify maximum number of rotated log files to be saved (no limit by setting to 0)"
+ , MAX_LOG_FILES
+ };
const command_line::arg_descriptor<std::string> arg_log_level = {
"log-level"
, ""
diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp
index 7a89ebc0c..aa688294d 100644
--- a/src/daemon/command_parser_executor.cpp
+++ b/src/daemon/command_parser_executor.cpp
@@ -27,6 +27,8 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "common/dns_utils.h"
+#include "common/command_line.h"
+#include "version.h"
#include "daemon/command_parser_executor.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
@@ -314,7 +316,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;
@@ -325,12 +327,26 @@ bool t_command_parser_executor::start_mining(const std::vector<std::string>& arg
if(args.size() == 4)
{
- ignore_battery = args[3] == "true";
+ if(args[3] == "true" || command_line::is_yes(args[3]) || args[3] == "1")
+ {
+ ignore_battery = true;
+ }
+ else if(args[3] != "false" && !command_line::is_no(args[3]) && args[3] != "0")
+ {
+ return false;
+ }
}
if(args.size() >= 3)
{
- do_background_mining = args[2] == "true";
+ if(args[2] == "true" || command_line::is_yes(args[2]) || args[2] == "1")
+ {
+ do_background_mining = true;
+ }
+ else if(args[2] != "false" && !command_line::is_no(args[2]) && args[2] != "0")
+ {
+ return false;
+ }
}
if(args.size() >= 2)
@@ -379,8 +395,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 +413,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 +431,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 +674,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..88bb1fd0c 100644
--- a/src/daemon/main.cpp
+++ b/src/daemon/main.cpp
@@ -84,6 +84,7 @@ int main(int argc, char const * argv[])
command_line::add_arg(core_settings, daemon_args::arg_log_file);
command_line::add_arg(core_settings, daemon_args::arg_log_level);
command_line::add_arg(core_settings, daemon_args::arg_max_log_file_size);
+ command_line::add_arg(core_settings, daemon_args::arg_max_log_files);
command_line::add_arg(core_settings, daemon_args::arg_max_concurrency);
command_line::add_arg(core_settings, daemon_args::arg_zmq_rpc_bind_ip);
command_line::add_arg(core_settings, daemon_args::arg_zmq_rpc_bind_port);
@@ -138,6 +139,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 +193,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
@@ -204,7 +204,7 @@ int main(int argc, char const * argv[])
if (!command_line::is_arg_defaulted(vm, daemon_args::arg_log_file))
log_file_path = command_line::get_arg(vm, daemon_args::arg_log_file);
log_file_path = bf::absolute(log_file_path, relative_path_base);
- mlog_configure(log_file_path.string(), true, command_line::get_arg(vm, daemon_args::arg_max_log_file_size));
+ mlog_configure(log_file_path.string(), true, command_line::get_arg(vm, daemon_args::arg_max_log_file_size), command_line::get_arg(vm, daemon_args::arg_max_log_files));
// Set log level
if (!command_line::is_arg_defaulted(vm, daemon_args::arg_log_level))
@@ -263,6 +263,9 @@ int main(int argc, char const * argv[])
}
else
{
+#ifdef HAVE_READLINE
+ rdln::suspend_readline pause_readline;
+#endif
std::cerr << "Unknown command: " << command.front() << std::endl;
return 1;
}
diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp
index 73b8d1a18..956c84a01 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()))
@@ -972,7 +976,7 @@ bool t_rpc_command_executor::print_transaction_pool_stats() {
}
else
{
- memset(&res.pool_stats, 0, sizeof(res.pool_stats));
+ res.pool_stats = {};
if (!m_rpc_server->on_get_transaction_pool_stats(req, res, false) || res.status != CORE_RPC_STATUS_OK)
{
tools::fail_msg_writer() << make_error(fail_message, res.status);
@@ -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;
}
@@ -1891,7 +1895,7 @@ bool t_rpc_command_executor::sync_info()
for (const auto &s: res.spans)
if (s.rate > 0.0f && s.connection_id == p.info.connection_id)
nblocks += s.nblocks, size += s.size;
- tools::success_msg_writer() << address << " " << epee::string_tools::pad_string(p.info.peer_id, 16, '0', true) << " " << p.info.height << " " << p.info.current_download << " kB/s, " << nblocks << " blocks / " << size/1e6 << " MB queued";
+ tools::success_msg_writer() << address << " " << epee::string_tools::pad_string(p.info.peer_id, 16, '0', true) << " " << epee::string_tools::pad_string(p.info.state, 16) << " " << p.info.height << " " << p.info.current_download << " kB/s, " << nblocks << " blocks / " << size/1e6 << " MB queued";
}
uint64_t total_size = 0;
diff --git a/src/debug_utilities/cn_deserialize.cpp b/src/debug_utilities/cn_deserialize.cpp
index dfbd3b864..6c09b0f18 100644
--- a/src/debug_utilities/cn_deserialize.cpp
+++ b/src/debug_utilities/cn_deserialize.cpp
@@ -27,6 +27,8 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <boost/filesystem.hpp>
+#include <boost/algorithm/string/join.hpp>
+#include <boost/range/adaptor/transformed.hpp>
#include "cryptonote_basic/cryptonote_basic.h"
#include "cryptonote_basic/tx_extra.h"
#include "cryptonote_core/blockchain.h"
@@ -51,6 +53,7 @@ static void print_extra_fields(const std::vector<cryptonote::tx_extra_field> &fi
else if (typeid(cryptonote::tx_extra_pub_key) == fields[n].type()) std::cout << "extra pub key: " << boost::get<cryptonote::tx_extra_pub_key>(fields[n]).pub_key;
else if (typeid(cryptonote::tx_extra_nonce) == fields[n].type()) std::cout << "extra nonce: " << epee::string_tools::buff_to_hex_nodelimer(boost::get<cryptonote::tx_extra_nonce>(fields[n]).nonce);
else if (typeid(cryptonote::tx_extra_merge_mining_tag) == fields[n].type()) std::cout << "extra merge mining tag: depth " << boost::get<cryptonote::tx_extra_merge_mining_tag>(fields[n]).depth << ", merkle root " << boost::get<cryptonote::tx_extra_merge_mining_tag>(fields[n]).merkle_root;
+ else if (typeid(cryptonote::tx_extra_additional_pub_keys) == fields[n].type()) std::cout << "additional tx pubkeys: " << boost::join(boost::get<cryptonote::tx_extra_additional_pub_keys>(fields[n]).data | boost::adaptors::transformed([](const crypto::public_key &key){ return epee::string_tools::pod_to_hex(key); }), ", " );
else if (typeid(cryptonote::tx_extra_mysterious_minergate) == fields[n].type()) std::cout << "extra minergate custom: " << epee::string_tools::buff_to_hex_nodelimer(boost::get<cryptonote::tx_extra_mysterious_minergate>(fields[n]).data);
else std::cout << "unknown";
std::cout << std::endl;
diff --git a/src/debug_utilities/object_sizes.cpp b/src/debug_utilities/object_sizes.cpp
index a3d037a59..ab8839636 100644
--- a/src/debug_utilities/object_sizes.cpp
+++ b/src/debug_utilities/object_sizes.cpp
@@ -94,6 +94,11 @@ int main(int argc, char* argv[])
SL(nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core>>);
SL(nodetool::p2p_connection_context_t<cryptonote::t_cryptonote_protocol_handler<cryptonote::core>::connection_context>);
SL(nodetool::network_address_old);
+ SL(nodetool::peerlist_entry_base<nodetool::network_address_old>);
+
+ SL(nodetool::network_config);
+ SL(nodetool::basic_node_data);
+ SL(cryptonote::CORE_SYNC_DATA);
SL(tools::wallet2::transfer_details);
SL(tools::wallet2::payment_details);
diff --git a/src/device/device.hpp b/src/device/device.hpp
index db489ff0c..9df0cb39d 100644
--- a/src/device/device.hpp
+++ b/src/device/device.hpp
@@ -145,6 +145,7 @@ namespace hw {
virtual bool sc_secret_add( crypto::secret_key &r, const crypto::secret_key &a, const crypto::secret_key &b) = 0;
virtual 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) = 0;
virtual bool generate_key_derivation(const crypto::public_key &pub, const crypto::secret_key &sec, crypto::key_derivation &derivation) = 0;
+ virtual 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) = 0;
virtual bool derivation_to_scalar(const crypto::key_derivation &derivation, const size_t output_index, crypto::ec_scalar &res) = 0;
virtual 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) = 0;
virtual 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) = 0;
diff --git a/src/device/device_default.cpp b/src/device/device_default.cpp
index 56bd1e164..0071f7d4f 100644
--- a/src/device/device_default.cpp
+++ b/src/device/device_default.cpp
@@ -264,6 +264,10 @@ namespace hw {
return true;
}
+ bool device_default::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){
+ return true;
+ }
+
/* ======================================================================= */
/* TRANSACTION */
/* ======================================================================= */
diff --git a/src/device/device_default.hpp b/src/device/device_default.hpp
index 53942f53a..771fbba72 100644
--- a/src/device/device_default.hpp
+++ b/src/device/device_default.hpp
@@ -93,6 +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) 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.cpp b/src/device/device_ledger.cpp
index ee1497438..f716f8ded 100644
--- a/src/device/device_ledger.cpp
+++ b/src/device/device_ledger.cpp
@@ -48,6 +48,15 @@ namespace hw {
/* ===================================================================== */
/* === Debug ==== */
/* ===================================================================== */
+ #ifdef WIN32
+ static char *pcsc_stringify_error(LONG rv) {
+ static __thread char out[20];
+ sprintf_s(out, sizeof(out), "0x%08lX", rv);
+
+ return out;
+ }
+ #endif
+
void set_apdu_verbose(bool verbose) {
apdu_verbose = verbose;
}
@@ -56,6 +65,7 @@ namespace hw {
#define ASSERT_RV(rv) CHECK_AND_ASSERT_THROW_MES((rv)==SCARD_S_SUCCESS, "Fail SCard API : (" << (rv) << ") "<< pcsc_stringify_error(rv)<<" Device="<<this->id<<", hCard="<<hCard<<", hContext="<<hContext);
#define ASSERT_SW(sw,ok,msk) CHECK_AND_ASSERT_THROW_MES(((sw)&(mask))==(ok), "Wrong Device Status : SW=" << std::hex << (sw) << " (EXPECT=" << std::hex << (ok) << ", MASK=" << std::hex << (mask) << ")") ;
#define ASSERT_T0(exp) CHECK_AND_ASSERT_THROW_MES(exp, "Protocol assert failure: "#exp ) ;
+ #define ASSERT_X(exp,msg) CHECK_AND_ASSERT_THROW_MES(exp, msg);
#ifdef DEBUG_HWDEVICE
crypto::secret_key dbg_viewkey;
@@ -125,6 +135,10 @@ namespace hw {
return sec == crypto::null_skey;
}
+ bool operator==(const crypto::key_derivation &d0, const crypto::key_derivation &d1) {
+ return !memcmp(&d0, &d1, sizeof(d0));
+ }
+
/* ===================================================================== */
/* === Device ==== */
/* ===================================================================== */
@@ -173,14 +187,15 @@ namespace hw {
void device_ledger::logCMD() {
if (apdu_verbose) {
char strbuffer[1024];
- sprintf(strbuffer, "%.02x %.02x %.02x %.02x %.02x ",
+ snprintf(strbuffer, sizeof(strbuffer), "%.02x %.02x %.02x %.02x %.02x ",
this->buffer_send[0],
this->buffer_send[1],
this->buffer_send[2],
this->buffer_send[3],
this->buffer_send[4]
);
- buffer_to_str(strbuffer+strlen(strbuffer), sizeof(strbuffer), (char*)(this->buffer_send+5), this->length_send-5);
+ const size_t len = strlen(strbuffer);
+ buffer_to_str(strbuffer+len, sizeof(strbuffer)-len, (char*)(this->buffer_send+5), this->length_send-5);
MDEBUG( "CMD :" << strbuffer);
}
}
@@ -188,11 +203,12 @@ namespace hw {
void device_ledger::logRESP() {
if (apdu_verbose) {
char strbuffer[1024];
- sprintf(strbuffer, "%.02x%.02x ",
+ snprintf(strbuffer, sizeof(strbuffer), "%.02x%.02x ",
this->buffer_recv[this->length_recv-2],
this->buffer_recv[this->length_recv-1]
);
- buffer_to_str(strbuffer+strlen(strbuffer), sizeof(strbuffer), (char*)(this->buffer_recv), this->length_recv-2);
+ const size_t len = strlen(strbuffer);
+ buffer_to_str(strbuffer+len, sizeof(strbuffer)-len, (char*)(this->buffer_recv), this->length_recv-2);
MDEBUG( "RESP :" << strbuffer);
}
@@ -279,7 +295,7 @@ namespace hw {
unsigned int device_ledger::exchange(unsigned int ok, unsigned int mask) {
LONG rv;
- int sw;
+ unsigned int sw;
ASSERT_T0(this->length_send <= BUFFER_SEND_SIZE);
logCMD();
@@ -288,6 +304,7 @@ namespace hw {
SCARD_PCI_T0, this->buffer_send, this->length_send,
NULL, this->buffer_recv, &this->length_recv);
ASSERT_RV(rv);
+ ASSERT_T0(this->length_recv >= 2);
ASSERT_T0(this->length_recv <= BUFFER_RECV_SIZE);
logRESP();
@@ -391,7 +408,7 @@ namespace hw {
}
}
- if (mszReaders) {
+ if (rv == SCARD_S_SUCCESS && mszReaders) {
#ifdef SCARD_AUTOALLOCATE
SCardFreeMemory(this->hContext, mszReaders);
#else
@@ -1094,6 +1111,24 @@ namespace hw {
return r;
}
+ bool device_ledger::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) {
+ const crypto::public_key *pkey=NULL;
+ if (derivation == main_derivation) {
+ pkey = &tx_pub_key;
+ MDEBUG("conceal derivation with main tx pub key");
+ } else {
+ for(size_t n=0; n < additional_derivations.size();++n) {
+ if(derivation == additional_derivations[n]) {
+ pkey = &additional_tx_pub_keys[n];
+ MDEBUG("conceal derivation with additionnal tx pub key");
+ break;
+ }
+ }
+ }
+ ASSERT_X(pkey, "Mismatched derivation on scan info");
+ return this->generate_key_derivation(*pkey, crypto::null_skey, derivation);
+ }
+
bool device_ledger::derivation_to_scalar(const crypto::key_derivation &derivation, const size_t output_index, crypto::ec_scalar &res) {
AUTO_LOCK_CMD();
int offset;
diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp
index 652cfb34b..b62bdf959 100644
--- a/src/device/device_ledger.hpp
+++ b/src/device/device_ledger.hpp
@@ -33,8 +33,13 @@
#include <cstddef>
#include <string>
#include "device.hpp"
+#ifdef WIN32
+#include <winscard.h>
+#define MAX_ATR_SIZE 33
+#else
#include <PCSC/winscard.h>
#include <PCSC/wintypes.h>
+#endif
#include <boost/thread/mutex.hpp>
#include <boost/thread/recursive_mutex.hpp>
@@ -169,6 +174,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) 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/log.cpp b/src/device/log.cpp
index cbbcfc953..1707524fb 100644
--- a/src/device/log.cpp
+++ b/src/device/log.cpp
@@ -45,13 +45,13 @@ namespace hw {
}
}
- void log_hexbuffer(std::string msg, const char* buff, size_t len) {
+ void log_hexbuffer(const std::string &msg, const char* buff, size_t len) {
char logstr[1025];
buffer_to_str(logstr, sizeof(logstr), buff, len);
MDEBUG(msg<< ": " << logstr);
}
- void log_message(std::string msg, std::string info ) {
+ void log_message(const std::string &msg, const std::string &info ) {
MDEBUG(msg << ": " << info);
}
@@ -122,16 +122,18 @@ namespace hw {
rct::keyV decrypt(const rct::keyV &keys) {
rct::keyV x ;
+ x.reserve(keys.size());
for (unsigned int j = 0; j<keys.size(); j++) {
x.push_back(decrypt(keys[j]));
}
return x;
}
- static void check(std::string msg, std::string info, const char *h, const char *d, int len, bool crypted) {
+ static void check(const std::string &msg, const std::string &info, const char *h, const char *d, size_t len, bool crypted) {
char dd[32];
char logstr[128];
+ CHECK_AND_ASSERT_THROW_MES(len <= sizeof(dd), "invalid len");
memmove(dd,d,len);
if (crypted) {
CHECK_AND_ASSERT_THROW_MES(len<=32, "encrypted data greater than 32");
@@ -149,11 +151,11 @@ namespace hw {
}
}
- void check32(std::string msg, std::string info, const char *h, const char *d, bool crypted) {
+ void check32(const std::string &msg, const std::string &info, const char *h, const char *d, bool crypted) {
check(msg, info, h, d, 32, crypted);
}
- void check8(std::string msg, std::string info, const char *h, const char *d, bool crypted) {
+ void check8(const std::string &msg, const std::string &info, const char *h, const char *d, bool crypted) {
check(msg, info, h, d, 8, crypted);
}
#endif
diff --git a/src/device/log.hpp b/src/device/log.hpp
index 1ab572c2c..1d1635dc1 100644
--- a/src/device/log.hpp
+++ b/src/device/log.hpp
@@ -44,8 +44,8 @@ namespace hw {
namespace ledger {
void buffer_to_str(char *to_buff, size_t to_len, const char *buff, size_t len) ;
- void log_hexbuffer(std::string msg, const char* buff, size_t len);
- void log_message(std::string msg, std::string info );
+ void log_hexbuffer(const std::string &msg, const char* buff, size_t len);
+ void log_message(const std::string &msg, const std::string &info );
#ifdef DEBUG_HWDEVICE
#define TRACK printf("file %s:%d\n",__FILE__, __LINE__)
//#define TRACK MCDEBUG("ledger"," At file " << __FILE__ << ":" << __LINE__)
@@ -59,8 +59,8 @@ namespace hw {
crypto::ec_scalar decrypt(const crypto::ec_scalar &res);
rct::keyV decrypt(const rct::keyV &keys);
- void check32(std::string msg, std::string info, const char *h, const char *d, bool crypted=false);
- void check8(std::string msg, std::string info, const char *h, const char *d, bool crypted=false);
+ void check32(const std::string &msg, const std::string &info, const char *h, const char *d, bool crypted=false);
+ void check8(const std::string &msg, const std::string &info, const char *h, const char *d, bool crypted=false);
void set_check_verbose(bool verbose);
#endif
diff --git a/src/gen_multisig/gen_multisig.cpp b/src/gen_multisig/gen_multisig.cpp
index 943589b4a..03e0a7946 100644
--- a/src/gen_multisig/gen_multisig.cpp
+++ b/src/gen_multisig/gen_multisig.cpp
@@ -174,7 +174,9 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_params, arg_stagenet);
command_line::add_arg(desc_params, arg_create_address_file);
- const auto vm = wallet_args::main(
+ boost::optional<po::variables_map> vm;
+ bool should_terminate = false;
+ std::tie(vm, should_terminate) = wallet_args::main(
argc, argv,
"monero-gen-multisig [(--testnet|--stagenet)] [--filename-base=<filename>] [--scheme=M/N] [--threshold=M] [--participants=N]",
genms::tr("This program generates a set of multisig wallets - use this simpler scheme only if all the participants trust each other"),
@@ -185,6 +187,8 @@ int main(int argc, char* argv[])
);
if (!vm)
return 1;
+ if (should_terminate)
+ return 0;
bool testnet, stagenet;
uint32_t threshold = 0, total = 0;
diff --git a/src/mnemonics/electrum-words.cpp b/src/mnemonics/electrum-words.cpp
index 6a2a3e0c4..7dd09ecb9 100644
--- a/src/mnemonics/electrum-words.cpp
+++ b/src/mnemonics/electrum-words.cpp
@@ -67,6 +67,9 @@
#include "language_base.h"
#include "singleton.h"
+#undef MONERO_DEFAULT_LOG_CATEGORY
+#define MONERO_DEFAULT_LOG_CATEGORY "mnemonic"
+
namespace
{
uint32_t create_checksum_index(const std::vector<std::string> &word_list,
@@ -152,6 +155,7 @@ namespace
if (full_match)
{
*language = *it1;
+ MINFO("Full match for language " << (*language)->get_english_language_name());
return true;
}
// Some didn't match. Clear the index array.
@@ -164,9 +168,11 @@ namespace
if (fallback)
{
*language = fallback;
+ MINFO("Fallback match for language " << (*language)->get_english_language_name());
return true;
}
+ MINFO("No match found");
return false;
}
@@ -217,7 +223,9 @@ namespace
checksum;
std::string trimmed_last_word = last_word.length() > unique_prefix_length ? Language::utf8prefix(last_word, unique_prefix_length) :
last_word;
- return trimmed_checksum == trimmed_last_word;
+ bool ret = trimmed_checksum == trimmed_last_word;
+ MINFO("Checksum is %s" << (ret ? "valid" : "invalid"));
+ return ret;
}
}
@@ -253,7 +261,10 @@ namespace crypto
boost::split(seed, words, boost::is_any_of(" "), boost::token_compress_on);
if (len % 4)
+ {
+ MERROR("Invalid seed: not a multiple of 4");
return false;
+ }
bool has_checksum = true;
if (len)
@@ -263,6 +274,7 @@ namespace crypto
if (seed.size() != expected/2 && seed.size() != expected &&
seed.size() != expected + 1)
{
+ MERROR("Invalid seed: unexpected number of words");
return false;
}
@@ -274,6 +286,7 @@ namespace crypto
Language::Base *language;
if (!find_seed_language(seed, has_checksum, matched_indices, &language))
{
+ MERROR("Invalid seed: language not found");
return false;
}
language_name = language->get_language_name();
@@ -284,6 +297,7 @@ namespace crypto
if (!checksum_test(seed, language->get_unique_prefix_length()))
{
// Checksum fail
+ MERROR("Invalid seed: invalid checksum");
return false;
}
seed.pop_back();
@@ -300,7 +314,11 @@ namespace crypto
val = w1 + word_list_length * (((word_list_length - w1) + w2) % word_list_length) +
word_list_length * word_list_length * (((word_list_length - w2) + w3) % word_list_length);
- if (!(val % word_list_length == w1)) return false;
+ if (!(val % word_list_length == w1))
+ {
+ MERROR("Invalid seed: mumble mumble");
+ return false;
+ }
dst.append((const char*)&val, 4); // copy 4 bytes to position
}
@@ -332,9 +350,15 @@ namespace crypto
{
std::string s;
if (!words_to_bytes(words, s, sizeof(dst), true, language_name))
+ {
+ MERROR("Invalid seed: failed to convert words to bytes");
return false;
+ }
if (s.size() != sizeof(dst))
+ {
+ MERROR("Invalid seed: wrong output size");
return false;
+ }
dst = *(const crypto::secret_key*)s.data();
return true;
}
diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h
index 3010b43ad..90e2f78b1 100644
--- a/src/p2p/net_node.h
+++ b/src/p2p/net_node.h
@@ -244,6 +244,7 @@ namespace nodetool
bool check_connection_and_handshake_with_peer(const epee::net_utils::network_address& na, uint64_t last_seen_stamp);
bool gray_peerlist_housekeeping();
+ bool check_incoming_connections();
void kill() { ///< will be called e.g. from deinit()
_info("Killing the net_node");
@@ -307,6 +308,7 @@ namespace nodetool
epee::math_helper::once_a_time_seconds<1> m_connections_maker_interval;
epee::math_helper::once_a_time_seconds<60*30, false> m_peerlist_store_interval;
epee::math_helper::once_a_time_seconds<60> m_gray_peerlist_housekeeping_interval;
+ epee::math_helper::once_a_time_seconds<900, false> m_incoming_connections_interval;
std::string m_bind_ip;
std::string m_port;
@@ -316,6 +318,7 @@ namespace nodetool
std::list<epee::net_utils::network_address> m_priority_peers;
std::vector<epee::net_utils::network_address> m_exclusive_peers;
std::vector<epee::net_utils::network_address> m_seed_nodes;
+ bool m_fallback_seed_nodes_added;
std::list<nodetool::peerlist_entry> m_command_line_peers;
uint64_t m_peer_livetime;
//keep connections to initiate some interactions
@@ -334,8 +337,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..85470f799 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"
@@ -69,6 +62,7 @@
namespace nodetool
{
+ inline bool append_net_address(std::vector<epee::net_utils::network_address> & seed_nodes, std::string const & addr, uint16_t default_port);
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
void node_server<t_payload_net_handler>::init_options(boost::program_options::options_description& desc)
@@ -280,10 +274,22 @@ namespace nodetool
{
nodetool::peerlist_entry pe = AUTO_VAL_INIT(pe);
pe.id = crypto::rand<uint64_t>();
- const uint16_t default_port = testnet ? ::config::testnet::P2P_DEFAULT_PORT : stagenet ? ::config::stagenet::P2P_DEFAULT_PORT : ::config::P2P_DEFAULT_PORT;
+ const uint16_t default_port = cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT;
bool r = parse_peer_from_string(pe.adr, pr_str, default_port);
- CHECK_AND_ASSERT_MES(r, false, "Failed to parse address from string: " << pr_str);
- m_command_line_peers.push_back(pe);
+ if (r)
+ {
+ m_command_line_peers.push_back(pe);
+ continue;
+ }
+ std::vector<epee::net_utils::network_address> resolved_addrs;
+ r = append_net_address(resolved_addrs, pr_str, default_port);
+ CHECK_AND_ASSERT_MES(r, false, "Failed to parse or resolve address from string: " << pr_str);
+ for (const epee::net_utils::network_address& addr : resolved_addrs)
+ {
+ pe.id = crypto::rand<uint64_t>();
+ pe.adr = addr;
+ m_command_line_peers.push_back(pe);
+ }
}
}
@@ -334,24 +340,31 @@ namespace nodetool
return true;
}
//-----------------------------------------------------------------------------------
- inline void append_net_address(
+ inline bool append_net_address(
std::vector<epee::net_utils::network_address> & seed_nodes
, std::string const & addr
+ , uint16_t default_port
)
{
using namespace boost::asio;
+ std::string host = addr;
+ std::string port = std::to_string(default_port);
size_t pos = addr.find_last_of(':');
- CHECK_AND_ASSERT_MES_NO_RET(std::string::npos != pos && addr.length() - 1 != pos && 0 != pos, "Failed to parse seed address from string: '" << addr << '\'');
- std::string host = addr.substr(0, pos);
- std::string port = addr.substr(pos + 1);
+ if (std::string::npos != pos)
+ {
+ CHECK_AND_ASSERT_MES(addr.length() - 1 != pos && 0 != pos, false, "Failed to parse seed address from string: '" << addr << '\'');
+ host = addr.substr(0, pos);
+ port = addr.substr(pos + 1);
+ }
+ MINFO("Resolving node address: host=" << host << ", port=" << port);
io_service io_srv;
ip::tcp::resolver resolver(io_srv);
ip::tcp::resolver::query query(host, port, boost::asio::ip::tcp::resolver::query::canonical_name);
boost::system::error_code ec;
ip::tcp::resolver::iterator i = resolver.resolve(query, ec);
- CHECK_AND_ASSERT_MES_NO_RET(!ec, "Failed to resolve host name '" << host << "': " << ec.message() << ':' << ec.value());
+ CHECK_AND_ASSERT_MES(!ec, false, "Failed to resolve host name '" << host << "': " << ec.message() << ':' << ec.value());
ip::tcp::resolver::iterator iend;
for (; i != iend; ++i)
@@ -361,14 +374,14 @@ namespace nodetool
{
epee::net_utils::network_address na{epee::net_utils::ipv4_network_address{boost::asio::detail::socket_ops::host_to_network_long(endpoint.address().to_v4().to_ulong()), endpoint.port()}};
seed_nodes.push_back(na);
- MINFO("Added seed node: " << na.str());
+ MINFO("Added node: " << na.str());
}
else
{
MWARNING("IPv6 unsupported, skip '" << host << "' -> " << endpoint.address().to_v6().to_string(ec));
- throw std::runtime_error("IPv6 unsupported");
}
}
+ return true;
}
//-----------------------------------------------------------------------------------
@@ -412,6 +425,7 @@ namespace nodetool
bool res = handle_command_line(vm);
CHECK_AND_ASSERT_MES(res, false, "Failed to handle command line");
+ m_fallback_seed_nodes_added = false;
if (m_nettype == cryptonote::TESTNET)
{
memcpy(&m_network_id, &::config::testnet::NETWORK_ID, 16);
@@ -490,7 +504,7 @@ namespace nodetool
if (result.size())
{
for (const auto& addr_string : result)
- full_addrs.insert(addr_string + ":" + std::to_string(m_nettype == cryptonote::TESTNET ? ::config::testnet::P2P_DEFAULT_PORT : m_nettype == cryptonote::STAGENET ? ::config::stagenet::P2P_DEFAULT_PORT : ::config::P2P_DEFAULT_PORT));
+ full_addrs.insert(addr_string + ":" + std::to_string(cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT));
}
++i;
}
@@ -505,6 +519,7 @@ namespace nodetool
for (const auto &peer: get_seed_nodes(cryptonote::MAINNET))
full_addrs.insert(peer);
+ m_fallback_seed_nodes_added = true;
}
}
}
@@ -512,7 +527,7 @@ namespace nodetool
for (const auto& full_addr : full_addrs)
{
MDEBUG("Seed node: " << full_addr);
- append_net_address(m_seed_nodes, full_addr);
+ append_net_address(m_seed_nodes, full_addr, cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT);
}
MDEBUG("Number of seed nodes: " << m_seed_nodes.size());
@@ -1141,7 +1156,6 @@ namespace nodetool
size_t try_count = 0;
size_t current_index = crypto::rand<size_t>()%m_seed_nodes.size();
- bool fallback_nodes_added = false;
while(true)
{
if(m_net_server.is_stop_signal_sent())
@@ -1151,15 +1165,21 @@ namespace nodetool
break;
if(++try_count > m_seed_nodes.size())
{
- if (!fallback_nodes_added)
+ if (!m_fallback_seed_nodes_added)
{
MWARNING("Failed to connect to any of seed peers, trying fallback seeds");
+ current_index = m_seed_nodes.size();
for (const auto &peer: get_seed_nodes(m_nettype))
{
MDEBUG("Fallback seed node: " << peer);
- append_net_address(m_seed_nodes, peer);
+ append_net_address(m_seed_nodes, peer, cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT);
+ }
+ m_fallback_seed_nodes_added = true;
+ if (current_index == m_seed_nodes.size())
+ {
+ MWARNING("No fallback seeds, continuing without seeds");
+ break;
}
- fallback_nodes_added = true;
// continue for another few cycles
}
else
@@ -1300,6 +1320,20 @@ namespace nodetool
m_connections_maker_interval.do_call(boost::bind(&node_server<t_payload_net_handler>::connections_maker, this));
m_gray_peerlist_housekeeping_interval.do_call(boost::bind(&node_server<t_payload_net_handler>::gray_peerlist_housekeeping, this));
m_peerlist_store_interval.do_call(boost::bind(&node_server<t_payload_net_handler>::store_config, this));
+ m_incoming_connections_interval.do_call(boost::bind(&node_server<t_payload_net_handler>::check_incoming_connections, this));
+ return true;
+ }
+ //-----------------------------------------------------------------------------------
+ template<class t_payload_net_handler>
+ bool node_server<t_payload_net_handler>::check_incoming_connections()
+ {
+ if (m_offline || m_hide_my_port)
+ return true;
+ if (get_incoming_connections_count() == 0)
+ {
+ const el::Level level = el::Level::Warning;
+ MCLOG_RED(level, "global", "No incoming connections - check firewalls/routers allow port " << get_this_peer_port());
+ }
return true;
}
//-----------------------------------------------------------------------------------
@@ -1814,10 +1848,20 @@ namespace nodetool
for(const std::string& pr_str: perrs)
{
epee::net_utils::network_address na = AUTO_VAL_INIT(na);
- const uint16_t default_port = m_nettype == cryptonote::TESTNET ? ::config::testnet::P2P_DEFAULT_PORT : m_nettype == cryptonote::STAGENET ? ::config::stagenet::P2P_DEFAULT_PORT : ::config::P2P_DEFAULT_PORT;
+ const uint16_t default_port = cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT;
bool r = parse_peer_from_string(na, pr_str, default_port);
- CHECK_AND_ASSERT_MES(r, false, "Failed to parse address from string: " << pr_str);
- container.push_back(na);
+ if (r)
+ {
+ container.push_back(na);
+ continue;
+ }
+ std::vector<epee::net_utils::network_address> resolved_addrs;
+ r = append_net_address(resolved_addrs, pr_str, default_port);
+ CHECK_AND_ASSERT_MES(r, false, "Failed to parse or resolve address from string: " << pr_str);
+ for (const epee::net_utils::network_address& addr : resolved_addrs)
+ {
+ container.push_back(addr);
+ }
}
return true;
diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp
index cc46d0aa7..68cc43128 100644
--- a/src/ringct/rctOps.cpp
+++ b/src/ringct/rctOps.cpp
@@ -134,12 +134,9 @@ namespace rct {
}
key zeroCommit(xmr_amount amount) {
- key mask = identity();
- mask = scalarmultBase(mask);
key am = d2h(amount);
key bH = scalarmultH(am);
- addKeys(mask, mask, bH);
- return mask;
+ return addKeys(G, bH);
}
key commit(xmr_amount amount, const key &mask) {
diff --git a/src/ringct/rctOps.h b/src/ringct/rctOps.h
index 3f8f6955c..f8889af5c 100644
--- a/src/ringct/rctOps.h
+++ b/src/ringct/rctOps.h
@@ -62,6 +62,7 @@ namespace rct {
static const key Z = { {0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 } };
static const key I = { {0x01, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 } };
static const key L = { {0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 } };
+ static const key G = { {0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 } };
//Creates a zero scalar
inline key zero() { return Z; }
diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp
index 4ecf62cec..cc6fbe738 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);
@@ -860,9 +862,9 @@ namespace rct {
results[i] = verBulletproof(rv.p.bulletproofs[i]);
else
results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]);
- });
+ }, true);
}
- waiter.wait();
+ waiter.wait(&tpool);
for (size_t i = 0; i < rv.outPk.size(); ++i) {
if (!results[i]) {
@@ -968,9 +970,9 @@ namespace rct {
results[i] = verBulletproof(rv.p.bulletproofs[i]);
else
results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]);
- });
+ }, true);
}
- waiter.wait();
+ waiter.wait(&tpool);
for (size_t i = 0; i < results.size(); ++i) {
if (!results[i]) {
@@ -987,9 +989,9 @@ namespace rct {
for (size_t i = 0 ; i < rv.mixRing.size() ; i++) {
tpool.submit(&waiter, [&, i] {
results[i] = verRctMGSimple(message, rv.p.MGs[i], rv.mixRing[i], pseudoOuts[i]);
- });
+ }, true);
}
- waiter.wait();
+ waiter.wait(&tpool);
for (size_t i = 0; i < results.size(); ++i) {
if (!results[i]) {
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index 69e527fd8..b55b1994b 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
@@ -209,20 +210,12 @@ namespace cryptonote
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
- static cryptonote::blobdata get_pruned_tx_blob(const cryptonote::blobdata &blobdata)
+ static cryptonote::blobdata get_pruned_tx_blob(cryptonote::transaction &tx)
{
- cryptonote::transaction tx;
-
- if (!cryptonote::parse_and_validate_tx_from_blob(blobdata, tx))
- {
- MERROR("Failed to parse and validate tx from blob");
- return blobdata;
- }
-
std::stringstream ss;
binary_archive<true> ba(ss);
bool r = tx.serialize_base(ba);
- CHECK_AND_ASSERT_MES(r, blobdata, "Failed to serialize rct signatures base");
+ CHECK_AND_ASSERT_MES(r, cryptonote::blobdata(), "Failed to serialize rct signatures base");
return ss.str();
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -233,50 +226,47 @@ namespace cryptonote
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCKS_FAST>(invoke_http_mode::BIN, "/getblocks.bin", req, res, r))
return r;
- std::list<std::pair<cryptonote::blobdata, std::list<cryptonote::blobdata> > > bs;
+ std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, 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, !req.no_miner_tx, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT))
{
res.status = "Failed";
return false;
}
size_t pruned_size = 0, unpruned_size = 0, ntxes = 0;
+ res.blocks.reserve(bs.size());
+ res.output_indices.reserve(bs.size());
for(auto& bd: bs)
{
res.blocks.resize(res.blocks.size()+1);
- res.blocks.back().block = bd.first;
- pruned_size += bd.first.size();
- unpruned_size += bd.first.size();
+ res.blocks.back().block = bd.first.first;
+ pruned_size += bd.first.first.size();
+ unpruned_size += bd.first.first.size();
res.output_indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices());
res.output_indices.back().indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::tx_output_indices());
- block b;
- if (!parse_and_validate_block_from_blob(bd.first, b))
+ if (!req.no_miner_tx)
{
- res.status = "Invalid block";
- return false;
- }
- bool r = m_core.get_tx_outputs_gindexs(get_transaction_hash(b.miner_tx), res.output_indices.back().indices.back().indices);
- if (!r)
- {
- res.status = "Failed";
- return false;
+ bool r = m_core.get_tx_outputs_gindexs(bd.first.second, res.output_indices.back().indices.back().indices);
+ if (!r)
+ {
+ res.status = "Failed";
+ return false;
+ }
}
- size_t txidx = 0;
ntxes += bd.second.size();
- for (std::list<cryptonote::blobdata>::iterator i = bd.second.begin(); i != bd.second.end(); ++i)
+ res.blocks.back().txs.reserve(bd.second.size());
+ res.output_indices.back().indices.reserve(bd.second.size());
+ for (std::vector<std::pair<crypto::hash, 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));
- i->clear();
- i->shrink_to_fit();
+ unpruned_size += i->second.size();
+ res.blocks.back().txs.push_back(std::move(i->second));
+ i->second.clear();
+ i->second.shrink_to_fit();
pruned_size += res.blocks.back().txs.back().size();
res.output_indices.back().indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::tx_output_indices());
- bool r = m_core.get_tx_outputs_gindexs(b.tx_hashes[txidx++], res.output_indices.back().indices.back().indices);
+ bool r = m_core.get_tx_outputs_gindexs(i->first, res.output_indices.back().indices.back().indices);
if (!r)
{
res.status = "Failed";
@@ -296,7 +286,7 @@ namespace cryptonote
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_ALT_BLOCKS_HASHES>(invoke_http_mode::JON, "/get_alt_blocks_hashes", req, res, r))
return r;
- std::list<block> blks;
+ std::vector<block> blks;
if(!m_core.get_alternative_blocks(blks))
{
@@ -338,8 +328,8 @@ namespace cryptonote
res.status = "Error retrieving block at height " + std::to_string(height);
return true;
}
- std::list<transaction> txs;
- std::list<crypto::hash> missed_txs;
+ std::vector<transaction> txs;
+ std::vector<crypto::hash> missed_txs;
m_core.get_transactions(blk.tx_hashes, txs, missed_txs);
res.blocks.resize(res.blocks.size() + 1);
res.blocks.back().block = block_to_blob(blk);
@@ -554,8 +544,8 @@ namespace cryptonote
}
vh.push_back(*reinterpret_cast<const crypto::hash*>(b.data()));
}
- std::list<crypto::hash> missed_txs;
- std::list<transaction> txs;
+ std::vector<crypto::hash> missed_txs;
+ std::vector<transaction> txs;
bool r = m_core.get_transactions(vh, txs, missed_txs);
if(!r)
{
@@ -576,25 +566,26 @@ namespace cryptonote
if(r)
{
// sort to match original request
- std::list<transaction> sorted_txs;
+ std::vector<transaction> sorted_txs;
std::vector<tx_info>::const_iterator i;
+ unsigned txs_processed = 0;
for (const crypto::hash &h: vh)
{
if (std::find(missed_txs.begin(), missed_txs.end(), h) == missed_txs.end())
{
- if (txs.empty())
+ if (txs.size() == txs_processed)
{
res.status = "Failed: internal error - txs is empty";
return true;
}
// core returns the ones it finds in the right order
- if (get_transaction_hash(txs.front()) != h)
+ if (get_transaction_hash(txs[txs_processed]) != h)
{
res.status = "Failed: tx hash mismatch";
return true;
}
- sorted_txs.push_back(std::move(txs.front()));
- txs.pop_front();
+ sorted_txs.push_back(std::move(txs[txs_processed]));
+ ++txs_processed;
}
else if ((i = std::find_if(pool_tx_info.begin(), pool_tx_info.end(), [h](const tx_info &txi) { return epee::string_tools::pod_to_hex(h) == txi.id_hash; })) != pool_tx_info.end())
{
@@ -605,7 +596,7 @@ namespace cryptonote
return true;
}
sorted_txs.push_back(tx);
- missed_txs.remove(h);
+ missed_txs.erase(std::find(missed_txs.begin(), missed_txs.end(), h));
pool_tx_hashes.insert(h);
const std::string hash_string = epee::string_tools::pod_to_hex(h);
for (const auto &ti: pool_tx_info)
@@ -624,7 +615,7 @@ namespace cryptonote
LOG_PRINT_L2("Found " << found_in_pool << "/" << vh.size() << " transactions in the pool");
}
- std::list<std::string>::const_iterator txhi = req.txs_hashes.begin();
+ std::vector<std::string>::const_iterator txhi = req.txs_hashes.begin();
std::vector<crypto::hash>::const_iterator vhi = vh.begin();
for(auto& tx: txs)
{
@@ -633,7 +624,7 @@ namespace cryptonote
crypto::hash tx_hash = *vhi++;
e.tx_hash = *txhi++;
- blobdata blob = t_serializable_object_to_blob(tx);
+ blobdata blob = req.prune ? get_pruned_tx_blob(tx) : t_serializable_object_to_blob(tx);
e.as_hex = string_tools::buff_to_hex_nodelimer(blob);
if (req.decode_as_json)
e.as_json = obj_to_json_str(tx);
@@ -1229,7 +1220,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;
@@ -1245,6 +1236,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;
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -1334,7 +1326,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;
@@ -1375,7 +1367,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;
@@ -1424,7 +1416,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;
@@ -1457,7 +1449,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;
@@ -1511,7 +1503,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;
@@ -1664,10 +1656,10 @@ namespace cryptonote
PERF_TIMER(on_flush_txpool);
bool failed = false;
- std::list<crypto::hash> txids;
+ std::vector<crypto::hash> txids;
if (req.txids.empty())
{
- std::list<transaction> pool_txs;
+ std::vector<transaction> pool_txs;
bool r = m_core.get_pool_transactions(pool_txs);
if (!r)
{
@@ -1724,7 +1716,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)
{
@@ -2078,24 +2070,87 @@ 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});
+ if (req.cumulative)
+ {
+ auto &distribution = res.distributions.back().distribution;
+ distribution[0] += d.cached_base;
+ for (size_t n = 1; n < distribution.size(); ++n)
+ distribution[n] += distribution[n-1];
+ }
+ 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 (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;
+ }
+
if (req.cumulative)
{
distribution[0] += base;
for (size_t n = 1; n < distribution.size(); ++n)
distribution[n] += distribution[n-1];
}
+
res.distributions.push_back({amount, start_height, std::move(distribution), base});
}
}
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 660fb7889..49b730149 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)
@@ -83,10 +83,12 @@ namespace cryptonote
std::list<crypto::hash> block_ids; //*first 10 blocks id goes sequential, next goes in pow(2,n) offset, like 2, 4, 8, 16, 32, 64 and so on, and the last one is always genesis block */
uint64_t start_height;
bool prune;
+ bool no_miner_tx;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(block_ids)
KV_SERIALIZE(start_height)
KV_SERIALIZE(prune)
+ KV_SERIALIZE_OPT(no_miner_tx, false)
END_KV_SERIALIZE_MAP()
};
@@ -110,7 +112,7 @@ namespace cryptonote
struct response
{
- std::list<block_complete_entry> blocks;
+ std::vector<block_complete_entry> blocks;
uint64_t start_height;
uint64_t current_height;
std::string status;
@@ -188,7 +190,7 @@ namespace cryptonote
struct response
{
- std::list<crypto::hash> m_block_ids;
+ std::vector<crypto::hash> m_block_ids;
uint64_t start_height;
uint64_t current_height;
std::string status;
@@ -273,7 +275,7 @@ namespace cryptonote
uint64_t total_received;
uint64_t total_received_unlocked = 0; // OpenMonero only
uint64_t scanned_height;
- std::list<transaction> transactions;
+ std::vector<transaction> transactions;
uint64_t blockchain_height;
uint64_t scanned_block_height;
std::string status;
@@ -561,12 +563,14 @@ namespace cryptonote
{
struct request
{
- std::list<std::string> txs_hashes;
+ std::vector<std::string> txs_hashes;
bool decode_as_json;
+ bool prune;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(txs_hashes)
KV_SERIALIZE(decode_as_json)
+ KV_SERIALIZE_OPT(prune, false)
END_KV_SERIALIZE_MAP()
};
@@ -596,11 +600,11 @@ namespace cryptonote
struct response
{
// older compatibility stuff
- std::list<std::string> txs_as_hex; //transactions blobs as hex (old compat)
- std::list<std::string> txs_as_json; //transactions decoded as json (old compat)
+ std::vector<std::string> txs_as_hex; //transactions blobs as hex (old compat)
+ std::vector<std::string> txs_as_json; //transactions decoded as json (old compat)
// in both old and new
- std::list<std::string> missed_tx; //not found transactions
+ std::vector<std::string> missed_tx; //not found transactions
// new style
std::vector<entry> txs;
@@ -1163,6 +1167,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)
@@ -1178,6 +1183,7 @@ namespace cryptonote
KV_SERIALIZE(reward)
KV_SERIALIZE(block_size)
KV_SERIALIZE(num_txes)
+ KV_SERIALIZE(pow_hash)
END_KV_SERIALIZE_MAP()
};
@@ -1185,7 +1191,10 @@ namespace cryptonote
{
struct request
{
+ bool fill_pow_hash;
+
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_OPT(fill_pow_hash, false);
END_KV_SERIALIZE_MAP()
};
@@ -1209,9 +1218,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()
};
@@ -1235,9 +1246,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()
};
@@ -1262,10 +1275,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()
};
@@ -1552,6 +1567,8 @@ namespace cryptonote
std::vector<txpool_histo> histo;
uint32_t num_double_spends;
+ txpool_stats(): bytes_total(0), bytes_min(0), bytes_max(0), bytes_med(0), fee_total(0), oldest(0), txs_total(0), num_failing(0), num_10m(0), num_not_relayed(0), histo_98pc(0), num_double_spends(0) {}
+
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(bytes_total)
KV_SERIALIZE(bytes_min)
@@ -1618,10 +1635,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()
};
@@ -1703,7 +1722,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()
@@ -1914,7 +1933,7 @@ namespace cryptonote
{
struct request
{
- std::list<std::string> txids;
+ std::vector<std::string> txids;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(txids)
@@ -2131,7 +2150,7 @@ namespace cryptonote
{
struct request
{
- std::list<std::string> txids;
+ std::vector<std::string> txids;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(txids)
@@ -2210,11 +2229,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..55858cc2a 100644
--- a/src/rpc/daemon_handler.cpp
+++ b/src/rpc/daemon_handler.cpp
@@ -50,9 +50,9 @@ namespace rpc
void DaemonHandler::handle(const GetBlocksFast::Request& req, GetBlocksFast::Response& res)
{
- std::list<std::pair<blobdata, std::list<blobdata> > > blocks;
+ std::vector<std::pair<std::pair<blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, 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, true, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT))
{
res.status = Message::STATUS_FAILED;
res.error_details = "core::find_blockchain_supplement() returned false";
@@ -62,9 +62,6 @@ namespace rpc
res.blocks.resize(blocks.size());
res.output_indices.resize(blocks.size());
- //TODO: really need to switch uses of std::list to std::vector unless
- // it's a huge performance concern
-
auto it = blocks.begin();
uint64_t block_count = 0;
@@ -72,7 +69,7 @@ namespace rpc
{
cryptonote::rpc::block_with_transactions& bwt = res.blocks[block_count];
- if (!parse_and_validate_block_from_blob(it->first, bwt.block))
+ if (!parse_and_validate_block_from_blob(it->first.first, bwt.block))
{
res.blocks.clear();
res.output_indices.clear();
@@ -89,11 +86,11 @@ namespace rpc
res.error_details = "incorrect number of transactions retrieved for block";
return;
}
- std::list<transaction> txs;
- for (const auto& blob : it->second)
+ std::vector<transaction> txs;
+ for (const auto& p : it->second)
{
txs.resize(txs.size() + 1);
- if (!parse_and_validate_tx_from_blob(blob, txs.back()))
+ if (!parse_and_validate_tx_from_blob(p.second, txs.back()))
{
res.blocks.clear();
res.output_indices.clear();
@@ -163,10 +160,10 @@ namespace rpc
void DaemonHandler::handle(const GetTransactions::Request& req, GetTransactions::Response& res)
{
- std::list<cryptonote::transaction> found_txs;
- std::list<crypto::hash> missed_hashes;
+ std::vector<cryptonote::transaction> found_txs_vec;
+ std::vector<crypto::hash> missed_vec;
- bool r = m_core.get_transactions(req.tx_hashes, found_txs, missed_hashes);
+ bool r = m_core.get_transactions(req.tx_hashes, found_txs_vec, missed_vec);
// TODO: consider fixing core::get_transactions to not hide exceptions
if (!r)
@@ -176,20 +173,7 @@ namespace rpc
return;
}
- size_t num_found = found_txs.size();
-
- // std::list is annoying
- std::vector<cryptonote::transaction> found_txs_vec
- {
- std::make_move_iterator(std::begin(found_txs)),
- std::make_move_iterator(std::end(found_txs))
- };
-
- std::vector<crypto::hash> missed_vec
- {
- std::make_move_iterator(std::begin(missed_hashes)),
- std::make_move_iterator(std::end(missed_hashes))
- };
+ size_t num_found = found_txs_vec.size();
std::vector<uint64_t> heights(num_found);
std::vector<bool> in_pool(num_found, false);
@@ -204,7 +188,7 @@ namespace rpc
// if any missing from blockchain, check in tx pool
if (!missed_vec.empty())
{
- std::list<cryptonote::transaction> pool_txs;
+ std::vector<cryptonote::transaction> pool_txs;
m_core.get_pool_transactions(pool_txs);
diff --git a/src/rpc/daemon_messages.h b/src/rpc/daemon_messages.h
index 1495c845f..8fff369df 100644
--- a/src/rpc/daemon_messages.h
+++ b/src/rpc/daemon_messages.h
@@ -106,7 +106,7 @@ BEGIN_RPC_MESSAGE_CLASS(GetHashesFast);
RPC_MESSAGE_MEMBER(uint64_t, start_height);
END_RPC_MESSAGE_REQUEST;
BEGIN_RPC_MESSAGE_RESPONSE;
- RPC_MESSAGE_MEMBER(std::list<crypto::hash>, hashes);
+ RPC_MESSAGE_MEMBER(std::vector<crypto::hash>, hashes);
RPC_MESSAGE_MEMBER(uint64_t, start_height);
RPC_MESSAGE_MEMBER(uint64_t, current_height);
END_RPC_MESSAGE_RESPONSE;
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/rpc/rpc_args.cpp b/src/rpc/rpc_args.cpp
index d4a6138ba..d4044d11b 100644
--- a/src/rpc/rpc_args.cpp
+++ b/src/rpc/rpc_args.cpp
@@ -102,7 +102,7 @@ namespace cryptonote
{
if (!config.login)
{
- LOG_ERROR(arg.rpc_access_control_origins.name << tr(" requires RFC server password --") << arg.rpc_login.name << tr(" cannot be empty"));
+ LOG_ERROR(arg.rpc_access_control_origins.name << tr(" requires RPC server password --") << arg.rpc_login.name << tr(" cannot be empty"));
return boost::none;
}
diff --git a/src/rpc/zmq_server.cpp b/src/rpc/zmq_server.cpp
index 3aee8c4c7..edd3e6669 100644
--- a/src/rpc/zmq_server.cpp
+++ b/src/rpc/zmq_server.cpp
@@ -104,6 +104,10 @@ bool ZmqServer::addTCPSocket(std::string address, std::string port)
rep_socket->setsockopt(ZMQ_RCVTIMEO, &DEFAULT_RPC_RECV_TIMEOUT_MS, sizeof(DEFAULT_RPC_RECV_TIMEOUT_MS));
+ if (address.empty())
+ address = "*";
+ if (port.empty())
+ port = "*";
std::string bind_address = addr_prefix + address + std::string(":") + port;
rep_socket->bind(bind_address.c_str());
}
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index b2651026f..16866a80d 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
@@ -89,8 +90,6 @@ typedef cryptonote::simple_wallet sw;
#define MIN_RING_SIZE 7 // Used to inform user about min ring size -- does not track actual protocol
-#define OUTPUT_EXPORT_FILE_MAGIC "Monero output export\003"
-
#define LOCK_IDLE_SCOPE() \
bool auto_refresh_enabled = m_auto_refresh_enabled.load(std::memory_order_relaxed); \
m_auto_refresh_enabled.store(false, std::memory_order_relaxed); \
@@ -130,6 +129,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};
@@ -379,21 +379,10 @@ namespace
boost::optional<std::pair<uint32_t, uint32_t>> parse_subaddress_lookahead(const std::string& str)
{
- auto pos = str.find(":");
- bool r = pos != std::string::npos;
- uint32_t major;
- r = r && epee::string_tools::get_xtype_from_string(major, str.substr(0, pos));
- uint32_t minor;
- r = r && epee::string_tools::get_xtype_from_string(minor, str.substr(pos + 1));
- if (r)
- {
- return std::make_pair(major, minor);
- }
- else
- {
+ auto r = tools::parse_subaddress_lookahead(str);
+ if (!r)
fail_msg_writer() << tr("invalid format for subaddress lookahead; must be <major>:<minor>");
- return {};
- }
+ return r;
}
void handle_transfer_exception(const std::exception_ptr &e, bool trusted_daemon)
@@ -506,7 +495,7 @@ namespace
}
if (warn_of_possible_attack)
- fail_msg_writer() << tr("There was an error, which could mean the node may be trying to get you to retry creating a transaction, and zero in on which outputs you own. Or it could be a bona fide error. It may be prudent to disconnect from this node, and not try to send a tranasction immediately. Alternatively, connect to another node so the original node cannot correlate information.");
+ fail_msg_writer() << tr("There was an error, which could mean the node may be trying to get you to retry creating a transaction, and zero in on which outputs you own. Or it could be a bona fide error. It may be prudent to disconnect from this node, and not try to send a transaction immediately. Alternatively, connect to another node so the original node cannot correlate information.");
}
bool check_file_overwrite(const std::string &filename)
@@ -758,7 +747,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 +1065,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 +1217,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 (...)
{
@@ -1369,9 +1358,117 @@ bool simple_wallet::print_ring(const std::vector<std::string> &args)
bool simple_wallet::set_ring(const std::vector<std::string> &args)
{
crypto::key_image key_image;
+
+ // try filename first
+ if (args.size() == 1)
+ {
+ if (!epee::file_io_utils::is_file_exist(args[0]))
+ {
+ fail_msg_writer() << tr("File doesn't exist");
+ return true;
+ }
+
+ char str[4096];
+ std::unique_ptr<FILE, tools::close_file> f(fopen(args[0].c_str(), "r"));
+ if (f)
+ {
+ while (!feof(f.get()))
+ {
+ if (!fgets(str, sizeof(str), f.get()))
+ break;
+ const size_t len = strlen(str);
+ if (len > 0 && str[len - 1] == '\n')
+ str[len - 1] = 0;
+ if (!str[0])
+ continue;
+ char key_image_str[65], type_str[9];
+ int read_after_key_image = 0, read = 0;
+ int fields = sscanf(str, "%64[abcdefABCDEF0123456789] %n%8s %n", key_image_str, &read_after_key_image, type_str, &read);
+ if (fields != 2)
+ {
+ fail_msg_writer() << tr("Invalid ring specification: ") << str;
+ continue;
+ }
+ key_image_str[64] = 0;
+ type_str[8] = 0;
+ crypto::key_image key_image;
+ if (read_after_key_image == 0 || !epee::string_tools::hex_to_pod(key_image_str, key_image))
+ {
+ fail_msg_writer() << tr("Invalid key image: ") << str;
+ continue;
+ }
+ if (read == read_after_key_image+8 || (strcmp(type_str, "absolute") && strcmp(type_str, "relative")))
+ {
+ fail_msg_writer() << tr("Invalid ring type, expected relative or abosolute: ") << str;
+ continue;
+ }
+ bool relative = !strcmp(type_str, "relative");
+ if (read < 0 || (size_t)read > strlen(str))
+ {
+ fail_msg_writer() << tr("Error reading line: ") << str;
+ continue;
+ }
+ bool valid = true;
+ std::vector<uint64_t> ring;
+ const char *ptr = str + read;
+ while (*ptr)
+ {
+ unsigned long offset;
+ int elements = sscanf(ptr, "%lu %n", &offset, &read);
+ if (elements == 0 || read <= 0 || (size_t)read > strlen(str))
+ {
+ fail_msg_writer() << tr("Error reading line: ") << str;
+ valid = false;
+ break;
+ }
+ ring.push_back(offset);
+ ptr += read;
+ MGINFO("read offset: " << offset);
+ }
+ if (!valid)
+ continue;
+ if (ring.empty())
+ {
+ fail_msg_writer() << tr("Invalid ring: ") << str;
+ continue;
+ }
+ if (relative)
+ {
+ for (size_t n = 1; n < ring.size(); ++n)
+ {
+ if (ring[n] <= 0)
+ {
+ fail_msg_writer() << tr("Invalid relative ring: ") << str;
+ valid = false;
+ break;
+ }
+ }
+ }
+ else
+ {
+ for (size_t n = 1; n < ring.size(); ++n)
+ {
+ if (ring[n] <= ring[n-1])
+ {
+ fail_msg_writer() << tr("Invalid absolute ring: ") << str;
+ valid = false;
+ break;
+ }
+ }
+ }
+ if (!valid)
+ continue;
+ if (!m_wallet->set_ring(key_image, ring, relative))
+ fail_msg_writer() << tr("Failed to set ring for key image: ") << key_image << ". " << tr("Continuing.");
+ }
+ f.reset();
+ }
+ return true;
+ }
+
if (args.size() < 3)
{
- fail_msg_writer() << tr("usage: set_ring <key_image> absolute|relative <index> [<index>...]");
+ fail_msg_writer() << tr("usage: set_ring <filename> | ( <key_image> absolute|relative <index> [<index>...] )");
return true;
}
@@ -1584,6 +1681,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();
@@ -1650,6 +1753,9 @@ bool simple_wallet::set_default_ring_size(const std::vector<std::string> &args/*
return true;
}
+ if (ring_size != 0 && ring_size != DEFAULT_MIX+1)
+ message_writer() << tr("WARNING: this is a non default ring size, which may harm your privacy. Default is recommended.");
+
const auto pwd_container = get_and_verify_password();
if (pwd_container)
{
@@ -2017,7 +2123,7 @@ simple_wallet::simple_wallet()
tr("Stop mining in the daemon."));
m_cmd_binder.set_handler("set_daemon",
boost::bind(&simple_wallet::set_daemon, this, _1),
- tr("set_daemon <host>[:<port>]"),
+ tr("set_daemon <host>[:<port>] [trusted|untrusted]"),
tr("Set another daemon to connect to."));
m_cmd_binder.set_handler("save_bc",
boost::bind(&simple_wallet::save_bc, this, _1),
@@ -2071,8 +2177,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_raw]"),
+ tr("Sign a transaction from a file. If the parameter \"export_raw\" is specified, transaction raw hex data suitable for the daemon RPC /sendrawtransaction is exported."));
m_cmd_binder.set_handler("submit_transfer",
boost::bind(&simple_wallet::submit_transfer, this, _1),
tr("Submit a signed transaction from a file."));
@@ -2142,7 +2248,7 @@ simple_wallet::simple_wallet()
"refresh-type <full|optimize-coinbase|no-coinbase|default>\n "
" Set the wallet's refresh behaviour.\n "
"priority [0|1|2|3|4]\n "
- " Set the fee too default/unimportant/normal/elevated/priority.\n "
+ " Set the fee to default/unimportant/normal/elevated/priority.\n "
"confirm-missing-payment-id <1|0>\n "
"ask-password <1|0>\n "
"unit <monero|millinero|micronero|nanonero|piconero>\n "
@@ -2314,7 +2420,7 @@ simple_wallet::simple_wallet()
tr("Print the ring(s) used to spend a given key image or transaction (if the ring size is > 1)"));
m_cmd_binder.set_handler("set_ring",
boost::bind(&simple_wallet::set_ring, this, _1),
- tr("set_ring <key_image> absolute|relative <index> [<index>...]"),
+ tr("set_ring <filename> | ( <key_image> absolute|relative <index> [<index>...] )"),
tr("Set the ring used for a given key image, so it can be reused in a fork"));
m_cmd_binder.set_handler("save_known_rings",
boost::bind(&simple_wallet::save_known_rings, this, _1),
@@ -2332,6 +2438,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>]"),
@@ -2436,8 +2546,24 @@ bool simple_wallet::set_log(const std::vector<std::string> &args)
fail_msg_writer() << tr("usage: set_log <log_level_number_0-4> | <categories>");
return true;
}
- if (!args.empty())
- mlog_set_log(args[0].c_str());
+ if(!args.empty())
+ {
+ uint16_t level = 0;
+ if(epee::string_tools::get_xtype_from_string(level, args[0]))
+ {
+ if(4 < level)
+ {
+ fail_msg_writer() << tr("wrong number range, use: set_log <log_level_number_0-4> | <categories>");
+ return true;
+ }
+ mlog_set_log_level(level);
+ }
+ else
+ {
+ mlog_set_log(args[0].c_str());
+ }
+ }
+
success_msg_writer() << "New log categories: " << mlog_get_categories();
return true;
}
@@ -3103,18 +3229,22 @@ 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;
+ m_trusted_daemon = false;
+ 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())
@@ -3148,7 +3278,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);
@@ -3475,6 +3608,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
{
@@ -3635,7 +3779,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;
@@ -3653,7 +3797,6 @@ bool simple_wallet::start_mining(const std::vector<std::string>& args)
req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
bool ok = true;
- size_t max_mining_threads_count = (std::max)(tools::get_max_concurrency(), static_cast<unsigned>(2));
size_t arg_size = args.size();
if(arg_size >= 3)
{
@@ -3669,7 +3812,7 @@ bool simple_wallet::start_mining(const std::vector<std::string>& args)
{
uint16_t num = 1;
ok = string_tools::get_xtype_from_string(num, args[0]);
- ok = ok && (1 <= num && num <= max_mining_threads_count);
+ ok = ok && 1 <= num;
req.threads_count = num;
}
else
@@ -3679,8 +3822,7 @@ bool simple_wallet::start_mining(const std::vector<std::string>& args)
if (!ok)
{
- fail_msg_writer() << tr("invalid arguments. Please use start_mining [<number_of_threads>] [do_bg_mining] [ignore_battery], "
- "<number_of_threads> should be from 1 to ") << max_mining_threads_count;
+ fail_msg_writer() << tr("invalid arguments. Please use start_mining [<number_of_threads>] [do_bg_mining] [ignore_battery]");
return true;
}
@@ -3739,13 +3881,40 @@ bool simple_wallet::set_daemon(const std::vector<std::string>& args)
// If no port has been provided, use the default from config
if (!match[3].length())
{
- int daemon_port = m_wallet->nettype() == cryptonote::TESTNET ? config::testnet::RPC_DEFAULT_PORT : m_wallet->nettype() == cryptonote::STAGENET ? config::stagenet::RPC_DEFAULT_PORT : config::RPC_DEFAULT_PORT;
+ int daemon_port = get_config(m_wallet->nettype()).RPC_DEFAULT_PORT;
daemon_url = match[1] + match[2] + std::string(":") + std::to_string(daemon_port);
} else {
daemon_url = args[0];
}
LOCK_IDLE_SCOPE();
m_wallet->init(daemon_url);
+
+ if (args.size() == 2)
+ {
+ if (args[1] == "trusted")
+ m_trusted_daemon = true;
+ else if (args[1] == "untrusted")
+ m_trusted_daemon = false;
+ else
+ {
+ fail_msg_writer() << tr("Expected trusted or untrusted, got ") << args[1] << ": assuming untrusted";
+ m_trusted_daemon = false;
+ }
+ }
+ else
+ {
+ m_trusted_daemon = false;
+ try
+ {
+ 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) { }
+ }
+ success_msg_writer() << boost::format("Daemon set to %s, %s") % daemon_url % (*m_trusted_daemon ? tr("trusted") : tr("untrusted"));
} else {
fail_msg_writer() << tr("This does not seem to be a valid daemon URL.");
}
@@ -3816,7 +3985,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();
@@ -3837,7 +4006,7 @@ bool simple_wallet::refresh_main(uint64_t start_height, bool reset, bool is_init
{
m_in_manual_refresh.store(true, std::memory_order_relaxed);
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);});
- m_wallet->refresh(start_height, fetched_blocks);
+ m_wallet->refresh(is_daemon_trusted(), start_height, fetched_blocks);
ok = true;
// Clear line "Height xxx of xxx"
std::cout << "\r \r";
@@ -3909,6 +4078,8 @@ bool simple_wallet::show_balance_unlocked(bool detailed)
std::string extra;
if (m_wallet->has_multisig_partial_key_images())
extra = tr(" (Some owned outputs have partial key images - import_multisig_info needed)");
+ else if (m_wallet->has_unknown_key_images())
+ extra += tr(" (Some owned outputs have missing key images - import_key_images needed)");
success_msg_writer() << tr("Currently selected account: [") << m_current_subaddress_account << tr("] ") << m_wallet->get_subaddress_label({m_current_subaddress_account, 0});
const std::string tag = m_wallet->get_account_tags().second[m_current_subaddress_account];
success_msg_writer() << tr("Tag: ") << (tag.empty() ? std::string{tr("(No tag assigned)")} : tag);
@@ -4131,7 +4302,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;
@@ -4405,6 +4576,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
}
vector<cryptonote::tx_destination_entry> dsts;
+ size_t num_subaddresses = 0;
for (size_t i = 0; i < local_args.size(); i += 2)
{
cryptonote::address_parse_info info;
@@ -4416,6 +4588,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
}
de.addr = info.address;
de.is_subaddress = info.is_subaddress;
+ num_subaddresses += info.is_subaddress;
if (info.has_payment_id)
{
@@ -4448,7 +4621,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
}
// prompt is there is no payment id and confirmation is required
- if (!payment_id_seen && m_wallet->confirm_missing_payment_id())
+ if (!payment_id_seen && m_wallet->confirm_missing_payment_id() && dsts.size() > num_subaddresses)
{
std::string accepted = input_line(tr("No payment id is included with this transaction. Is this okay? (Y/Yes/N/No): "));
if (std::cin.eof())
@@ -4477,16 +4650,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;
}
@@ -4600,6 +4773,23 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
if (!print_ring_members(ptx_vector, prompt))
return true;
}
+ bool default_ring_size = true;
+ for (const auto &ptx: ptx_vector)
+ {
+ for (const auto &vin: ptx.tx.vin)
+ {
+ if (vin.type() == typeid(txin_to_key))
+ {
+ const txin_to_key& in_to_key = boost::get<txin_to_key>(vin);
+ if (in_to_key.key_offsets.size() != DEFAULT_MIX + 1)
+ default_ring_size = false;
+ }
+ }
+ }
+ if (m_wallet->confirm_non_default_ring_size() && !default_ring_size)
+ {
+ prompt << tr("WARNING: this is a non default ring size, which may harm your privacy. Default is recommended.");
+ }
prompt << ENDL << tr("Is this okay? (Y/Yes/N/No): ");
std::string accepted = input_line(prompt.str());
@@ -4645,7 +4835,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 (...)
{
@@ -4682,7 +4872,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())
{
@@ -4751,9 +4941,23 @@ bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_)
commit_or_save(ptx_vector, m_do_not_relay);
}
}
+ catch (const tools::error::not_enough_unlocked_money& e)
+ {
+ fail_msg_writer() << tr("Not enough money in unlocked balance");
+ std::string accepted = input_line((boost::format(tr("Discarding %s of unmixable outputs that cannot be spent, which can be undone by \"rescan_spent\". Is this okay? (Y/Yes/N/No): ")) % print_money(e.available())).str());
+ if (std::cin.eof())
+ return true;
+ if (command_line::is_yes(accepted))
+ {
+ try
+ {
+ m_wallet->discard_unmixable_outputs(is_daemon_trusted());
+ } catch (...) {}
+ }
+ }
catch (const std::exception &e)
{
- handle_transfer_exception(std::current_exception(), m_trusted_daemon);
+ handle_transfer_exception(std::current_exception(), is_daemon_trusted());
}
catch (...)
{
@@ -4884,7 +5088,7 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &a
}
// prompt is there is no payment id and confirmation is required
- if (!payment_id_seen && m_wallet->confirm_missing_payment_id())
+ if (!payment_id_seen && m_wallet->confirm_missing_payment_id() && !info.is_subaddress)
{
std::string accepted = input_line(tr("No payment id is included with this transaction. Is this okay? (Y/Yes/N/No): "));
if (std::cin.eof())
@@ -4902,7 +5106,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())
{
@@ -4986,7 +5190,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 (...)
{
@@ -5097,7 +5301,7 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
}
// prompt if there is no payment id and confirmation is required
- if (!payment_id_seen && m_wallet->confirm_missing_payment_id())
+ if (!payment_id_seen && m_wallet->confirm_missing_payment_id() && !info.is_subaddress)
{
std::string accepted = input_line(tr("No payment id is included with this transaction. Is this okay? (Y/Yes/N/No): "));
if (std::cin.eof())
@@ -5115,7 +5319,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())
{
@@ -5185,7 +5389,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 (...)
{
@@ -5250,7 +5454,7 @@ bool simple_wallet::donate(const std::vector<std::string> &args_)
local_args.push_back(amount_str);
if (!payment_id_str.empty())
local_args.push_back(payment_id_str);
- message_writer() << tr("Donating ") << amount_str << " to The Monero Project (donate.getmonero.org or "<< MONERO_DONATION_ADDR <<").";
+ message_writer() << (boost::format(tr("Donating %s %s to The Monero Project (donate.getmonero.org or %s).")) % amount_str % cryptonote::get_unit(cryptonote::get_default_decimal_point()) % MONERO_DONATION_ADDR).str();
transfer_new(local_args);
return true;
}
@@ -5420,9 +5624,9 @@ bool simple_wallet::sign_transfer(const std::vector<std::string> &args_)
fail_msg_writer() << tr("This is a watch only wallet");
return true;
}
- if (args_.size() > 1 || (args_.size() == 1 && args_[0] != "export"))
+ if (args_.size() > 1 || (args_.size() == 1 && args_[0] != "export_raw"))
{
- fail_msg_writer() << tr("usage: sign_transfer [export]");
+ fail_msg_writer() << tr("usage: sign_transfer [export_raw]");
return true;
}
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
@@ -5490,7 +5694,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 (...)
{
@@ -5967,10 +6171,7 @@ static std::string get_human_readable_timestamp(uint64_t ts)
#endif
uint64_t now = time(NULL);
uint64_t diff = ts > now ? ts - now : now - ts;
- if (diff > 24*3600)
- strftime(buffer, sizeof(buffer), "%Y-%m-%d", &tm);
- else
- strftime(buffer, sizeof(buffer), "%I:%M:%S %p", &tm);
+ strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tm);
return std::string(buffer);
}
//----------------------------------------------------------------------------------------------------
@@ -6082,7 +6283,7 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
payment_id = payment_id.substr(0,16);
std::string note = m_wallet->get_tx_note(pd.m_tx_hash);
- output.insert(std::make_pair(pd.m_block_height, std::make_pair(true, (boost::format("%16.16s %20.20s %s %s %d %s %s") % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount) % string_tools::pod_to_hex(pd.m_tx_hash) % payment_id % pd.m_subaddr_index.minor % "-" % note).str())));
+ output.insert(std::make_pair(pd.m_block_height, std::make_pair(true, (boost::format("%25.25s %20.20s %s %s %d %s %s") % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount) % string_tools::pod_to_hex(pd.m_tx_hash) % payment_id % pd.m_subaddr_index.minor % "-" % note).str())));
}
}
@@ -6115,7 +6316,7 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
payment_id = payment_id.substr(0,16);
std::string note = m_wallet->get_tx_note(i->first);
- output.insert(std::make_pair(pd.m_block_height, std::make_pair(false, (boost::format("%16.16s %20.20s %s %s %14.14s %s %s - %s") % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount_in - change - fee) % string_tools::pod_to_hex(i->first) % payment_id % print_money(fee) % dests % print_subaddr_indices(pd.m_subaddr_indices) % note).str())));
+ output.insert(std::make_pair(pd.m_block_height, std::make_pair(false, (boost::format("%25.25s %20.20s %s %s %14.14s %s %s - %s") % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount_in - change - fee) % string_tools::pod_to_hex(i->first) % payment_id % print_money(fee) % dests % print_subaddr_indices(pd.m_subaddr_indices) % note).str())));
}
}
@@ -6141,7 +6342,7 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
std::string double_spend_note;
if (i->second.m_double_spend_seen)
double_spend_note = tr("[Double spend seen on the network: this transaction may or may not end up being mined] ");
- message_writer() << (boost::format("%8.8s %6.6s %16.16s %20.20s %s %s %d %s %s%s") % "pool" % "in" % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount) % string_tools::pod_to_hex(pd.m_tx_hash) % payment_id % pd.m_subaddr_index.minor % "-" % note % double_spend_note).str();
+ message_writer() << (boost::format("%8.8s %6.6s %25.25s %20.20s %s %s %d %s %s%s") % "pool" % "in" % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount) % string_tools::pod_to_hex(pd.m_tx_hash) % payment_id % pd.m_subaddr_index.minor % "-" % note % double_spend_note).str();
}
}
catch (const std::exception& e)
@@ -6164,7 +6365,7 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
std::string note = m_wallet->get_tx_note(i->first);
bool is_failed = pd.m_state == tools::wallet2::unconfirmed_transfer_details::failed;
if ((failed && is_failed) || (!is_failed && pending)) {
- message_writer() << (boost::format("%8.8s %6.6s %16.16s %20.20s %s %s %14.14s %s - %s") % (is_failed ? tr("failed") : tr("pending")) % tr("out") % get_human_readable_timestamp(pd.m_timestamp) % print_money(amount - pd.m_change - fee) % string_tools::pod_to_hex(i->first) % payment_id % print_money(fee) % print_subaddr_indices(pd.m_subaddr_indices) % note).str();
+ message_writer() << (boost::format("%8.8s %6.6s %25.25s %20.20s %s %s %14.14s %s - %s") % (is_failed ? tr("failed") : tr("pending")) % tr("out") % get_human_readable_timestamp(pd.m_timestamp) % print_money(amount - pd.m_change - fee) % string_tools::pod_to_hex(i->first) % payment_id % print_money(fee) % print_subaddr_indices(pd.m_subaddr_indices) % note).str();
}
}
}
@@ -6331,7 +6532,7 @@ void simple_wallet::wallet_idle_thread()
{
uint64_t fetched_blocks;
if (try_connect_to_daemon(true))
- m_wallet->refresh(0, fetched_blocks);
+ m_wallet->refresh(is_daemon_trusted(), 0, fetched_blocks);
}
catch(...) {}
m_auto_refresh_refreshing = false;
@@ -7078,7 +7279,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;
@@ -7133,19 +7334,8 @@ bool simple_wallet::export_outputs(const std::vector<std::string> &args)
LOCK_IDLE_SCOPE();
try
{
- std::vector<tools::wallet2::transfer_details> outs = m_wallet->export_outputs();
-
- std::stringstream oss;
- boost::archive::portable_binary_oarchive ar(oss);
- ar << outs;
-
- std::string magic(OUTPUT_EXPORT_FILE_MAGIC, strlen(OUTPUT_EXPORT_FILE_MAGIC));
- const cryptonote::account_public_address &keys = m_wallet->get_account().get_keys().m_account_address;
- std::string header;
- header += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key));
- header += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key));
- std::string ciphertext = m_wallet->encrypt_with_view_secret_key(header + oss.str());
- bool r = epee::file_io_utils::save_string_to_file(filename, magic + ciphertext);
+ std::string data = m_wallet->export_outputs_to_str();
+ bool r = epee::file_io_utils::save_string_to_file(filename, data);
if (!r)
{
fail_msg_writer() << tr("failed to save file ") << filename;
@@ -7184,63 +7374,16 @@ bool simple_wallet::import_outputs(const std::vector<std::string> &args)
fail_msg_writer() << tr("failed to read file ") << filename;
return true;
}
- const size_t magiclen = strlen(OUTPUT_EXPORT_FILE_MAGIC);
- if (data.size() < magiclen || memcmp(data.data(), OUTPUT_EXPORT_FILE_MAGIC, magiclen))
- {
- fail_msg_writer() << "Bad output export file magic in " << filename;
- return true;
- }
-
- try
- {
- data = m_wallet->decrypt_with_view_secret_key(std::string(data, magiclen));
- }
- catch (const std::exception &e)
- {
- fail_msg_writer() << "Failed to decrypt " << filename << ": " << e.what();
- return true;
- }
-
- const size_t headerlen = 2 * sizeof(crypto::public_key);
- if (data.size() < headerlen)
- {
- fail_msg_writer() << "Bad data size from file " << filename;
- return true;
- }
- const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[0];
- const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[sizeof(crypto::public_key)];
- const cryptonote::account_public_address &keys = m_wallet->get_account().get_keys().m_account_address;
- if (public_spend_key != keys.m_spend_public_key || public_view_key != keys.m_view_public_key)
- {
- fail_msg_writer() << "Outputs from " << filename << " are for a different account";
- return true;
- }
try
{
- std::string body(data, headerlen);
- std::stringstream iss;
- iss << body;
- std::vector<tools::wallet2::transfer_details> outputs;
- try
- {
- boost::archive::portable_binary_iarchive ar(iss);
- ar >> outputs;
- }
- catch (...)
- {
- iss.str("");
- iss << body;
- boost::archive::binary_iarchive ar(iss);
- ar >> outputs;
- }
LOCK_IDLE_SCOPE();
- size_t n_outputs = m_wallet->import_outputs(outputs);
+ size_t n_outputs = m_wallet->import_outputs_from_str(data);
success_msg_writer() << boost::lexical_cast<std::string>(n_outputs) << " outputs imported";
}
catch (const std::exception &e)
{
- fail_msg_writer() << "Failed to import outputs: " << e.what();
+ fail_msg_writer() << "Failed to import outputs " << filename << ": " << e.what();
return true;
}
@@ -7282,8 +7425,12 @@ bool simple_wallet::show_transfer(const std::vector<std::string> &args)
if (pd.m_unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER)
{
uint64_t bh = std::max(pd.m_unlock_time, pd.m_block_height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE);
+ uint64_t last_block_reward = m_wallet->get_last_block_reward();
+ uint64_t suggested_threshold = last_block_reward ? (pd.m_amount + last_block_reward - 1) / last_block_reward : 0;
if (bh >= last_block_height)
success_msg_writer() << "Locked: " << (bh - last_block_height) << " blocks to unlock";
+ else if (suggested_threshold > 0)
+ success_msg_writer() << std::to_string(last_block_height - bh) << " confirmations (" << suggested_threshold << " suggested threshold)";
else
success_msg_writer() << std::to_string(last_block_height - bh) << " confirmations";
}
@@ -7464,6 +7611,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);
@@ -7474,10 +7622,12 @@ int main(int argc, char* argv[])
po::positional_options_description positional_options;
positional_options.add(arg_command.name, -1);
- const auto vm = wallet_args::main(
+ boost::optional<po::variables_map> vm;
+ bool should_terminate = false;
+ std::tie(vm, should_terminate) = wallet_args::main(
argc, argv,
"monero-wallet-cli [--wallet-file=<file>|--generate-new-wallet=<file>] [<COMMAND>]",
- sw::tr("This is the command line monero wallet. It needs to connect to a monero\ndaemon to work correctly."),
+ sw::tr("This is the command line monero wallet. It needs to connect to a monero\ndaemon to work correctly.\nWARNING: Do not reuse your Monero keys on another fork, UNLESS this fork has key reuse mitigations built in. Doing so will harm your privacy."),
desc_params,
positional_options,
[](const std::string &s, bool emphasis){ tools::scoped_message_writer(emphasis ? epee::console_color_white : epee::console_color_default, true) << s; },
@@ -7489,6 +7639,11 @@ int main(int argc, char* argv[])
return 1;
}
+ if (should_terminate)
+ {
+ return 0;
+ }
+
cryptonote::simple_wallet w;
const bool r = w.init(*vm);
CHECK_AND_ASSERT_MES(r, 1, sw::tr("Failed to initialize wallet"));
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..55ba51f50 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.3.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/address_book.h b/src/wallet/api/address_book.h
index 7d9200550..f4ca68efd 100644
--- a/src/wallet/api/address_book.h
+++ b/src/wallet/api/address_book.h
@@ -42,16 +42,16 @@ public:
~AddressBookImpl();
// Fetches addresses from Wallet2
- void refresh();
- std::vector<AddressBookRow*> getAll() const;
- bool addRow(const std::string &dst_addr , const std::string &payment_id, const std::string &description);
- bool deleteRow(std::size_t rowId);
+ void refresh() override;
+ std::vector<AddressBookRow*> getAll() const override;
+ bool addRow(const std::string &dst_addr , const std::string &payment_id, const std::string &description) override;
+ bool deleteRow(std::size_t rowId) override;
// Error codes. See AddressBook:ErrorCode enum in wallet2_api.h
- std::string errorString() const {return m_errorString;}
- int errorCode() const {return m_errorCode;}
+ std::string errorString() const override {return m_errorString;}
+ int errorCode() const override {return m_errorCode;}
- int lookupPaymentID(const std::string &payment_id) const;
+ int lookupPaymentID(const std::string &payment_id) const override;
private:
void clearRows();
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..50b9f07ef 100644
--- a/src/wallet/api/pending_transaction.h
+++ b/src/wallet/api/pending_transaction.h
@@ -43,18 +43,22 @@ class PendingTransactionImpl : public PendingTransaction
public:
PendingTransactionImpl(WalletImpl &wallet);
~PendingTransactionImpl();
- int status() const;
- std::string errorString() const;
- bool commit(const std::string &filename = "", bool overwrite = false);
- uint64_t amount() const;
- uint64_t dust() const;
- uint64_t fee() const;
- std::vector<std::string> txid() const;
- uint64_t txCount() const;
- std::vector<uint32_t> subaddrAccount() const;
- std::vector<std::set<uint32_t>> subaddrIndices() const;
+ int status() const override;
+ std::string errorString() const override;
+ bool commit(const std::string &filename = "", bool overwrite = false) override;
+ uint64_t amount() const override;
+ uint64_t dust() const override;
+ uint64_t fee() const override;
+ std::vector<std::string> txid() const override;
+ uint64_t txCount() const override;
+ std::vector<uint32_t> subaddrAccount() const override;
+ std::vector<std::set<uint32_t>> subaddrIndices() const override;
// TODO: continue with interface;
+ std::string multisigSignData() override;
+ void signMultisigTx() override;
+ std::vector<std::string> signersKeys() const override;
+
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/subaddress.h b/src/wallet/api/subaddress.h
index 3f9e37ac8..f3db7d97b 100644
--- a/src/wallet/api/subaddress.h
+++ b/src/wallet/api/subaddress.h
@@ -40,10 +40,10 @@ public:
~SubaddressImpl();
// Fetches addresses from Wallet2
- void refresh(uint32_t accountIndex);
- std::vector<SubaddressRow*> getAll() const;
- void addRow(uint32_t accountIndex, const std::string &label);
- void setLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label);
+ void refresh(uint32_t accountIndex) override;
+ std::vector<SubaddressRow*> getAll() const override;
+ void addRow(uint32_t accountIndex, const std::string &label) override;
+ void setLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label) override;
private:
void clearRows();
diff --git a/src/wallet/api/transaction_info.h b/src/wallet/api/transaction_info.h
index 5df9a44ef..37e0445d9 100644
--- a/src/wallet/api/transaction_info.h
+++ b/src/wallet/api/transaction_info.h
@@ -42,24 +42,24 @@ public:
TransactionInfoImpl();
~TransactionInfoImpl();
//! in/out
- virtual int direction() const;
+ virtual int direction() const override;
//! true if hold
- virtual bool isPending() const;
- virtual bool isFailed() const;
- virtual uint64_t amount() const;
+ virtual bool isPending() const override;
+ virtual bool isFailed() const override;
+ virtual uint64_t amount() const override;
//! always 0 for incoming txes
- virtual uint64_t fee() const;
- virtual uint64_t blockHeight() const;
- virtual std::set<uint32_t> subaddrIndex() const;
- virtual uint32_t subaddrAccount() const;
- virtual std::string label() const;
+ virtual uint64_t fee() const override;
+ virtual uint64_t blockHeight() const override;
+ virtual std::set<uint32_t> subaddrIndex() const override;
+ virtual uint32_t subaddrAccount() const override;
+ virtual std::string label() const override;
- virtual std::string hash() const;
- virtual std::time_t timestamp() const;
- virtual std::string paymentId() const;
- virtual const std::vector<Transfer> &transfers() const;
- virtual uint64_t confirmations() const;
- virtual uint64_t unlockTime() const;
+ virtual std::string hash() const override;
+ virtual std::time_t timestamp() const override;
+ virtual std::string paymentId() const override;
+ virtual const std::vector<Transfer> &transfers() const override;
+ virtual uint64_t confirmations() const override;
+ virtual uint64_t unlockTime() const override;
private:
int m_direction;
diff --git a/src/wallet/api/unsigned_transaction.h b/src/wallet/api/unsigned_transaction.h
index a35630535..8a3330014 100644
--- a/src/wallet/api/unsigned_transaction.h
+++ b/src/wallet/api/unsigned_transaction.h
@@ -43,19 +43,18 @@ class UnsignedTransactionImpl : public UnsignedTransaction
public:
UnsignedTransactionImpl(WalletImpl &wallet);
~UnsignedTransactionImpl();
- int status() const;
- std::string errorString() const;
- std::vector<uint64_t> amount() const;
- std::vector<uint64_t> dust() const;
- std::vector<uint64_t> fee() const;
- std::vector<uint64_t> mixin() const;
- std::vector<std::string> paymentId() const;
- std::vector<std::string> recipientAddress() const;
- uint64_t txCount() const;
+ int status() const override;
+ std::string errorString() const override;
+ std::vector<uint64_t> amount() const override;
+ std::vector<uint64_t> fee() const override;
+ std::vector<uint64_t> mixin() const override;
+ std::vector<std::string> paymentId() const override;
+ std::vector<std::string> recipientAddress() const override;
+ uint64_t txCount() const override;
// sign txs and save to file
- bool sign(const std::string &signedFileName);
- std::string confirmationMessage() const {return m_confirmationMessage;}
- uint64_t minMixinCount() const;
+ bool sign(const std::string &signedFileName) override;
+ std::string confirmationMessage() const override {return m_confirmationMessage;}
+ uint64_t minMixinCount() const override;
private:
// Callback function to check all loaded tx's and generate confirmationMessage
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index b02884f67..3f6bfec9e 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -60,7 +60,7 @@ namespace Monero {
namespace {
// copy-pasted from simplewallet
- static const size_t DEFAULT_MIXIN = 4;
+ static const size_t DEFAULT_MIXIN = 6;
static const int DEFAULT_REFRESH_INTERVAL_MILLIS = 1000 * 10;
// limit maximum refresh interval as one minute
static const int MAX_REFRESH_INTERVAL_MILLIS = 1000 * 60 * 1;
@@ -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) {
@@ -338,6 +372,7 @@ WalletImpl::WalletImpl(NetworkType nettype)
, m_trustedDaemon(false)
, m_wallet2Callback(nullptr)
, m_recoveringFromSeed(false)
+ , m_recoveringFromDevice(false)
, m_synchronized(false)
, m_rebuildWalletCache(false)
, m_is_connected(false)
@@ -385,6 +420,7 @@ bool WalletImpl::create(const std::string &path, const std::string &password, co
clearStatus();
m_recoveringFromSeed = false;
+ m_recoveringFromDevice = false;
bool keys_file_exists;
bool wallet_file_exists;
tools::wallet2::wallet_exists(path, keys_file_exists, wallet_file_exists);
@@ -395,9 +431,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 +442,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 +469,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 +507,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 +537,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 +548,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;
@@ -524,71 +556,95 @@ bool WalletImpl::recoverFromKeysWithPassword(const std::string &path,
}
// parse view secret key
+ bool has_viewkey = true;
+ crypto::secret_key viewkey;
if (viewkey_string.empty()) {
- m_errorString = tr("No view key supplied, cancelled");
- m_status = Status_Error;
- return false;
+ if(has_spendkey) {
+ has_viewkey = false;
+ }
+ else {
+ setStatusError(tr("Neither view key nor spend 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;
- return false;
+ if(has_viewkey) {
+ cryptonote::blobdata viewkey_data;
+ if(!epee::string_tools::parse_hexstr_to_binbuff(viewkey_string, viewkey_data) || viewkey_data.size() != sizeof(crypto::secret_key))
+ {
+ setStatusError(tr("failed to parse secret view key"));
+ return false;
+ }
+ viewkey = *reinterpret_cast<const crypto::secret_key*>(viewkey_data.data());
}
- crypto::secret_key viewkey = *reinterpret_cast<const crypto::secret_key*>(viewkey_data.data());
-
// check the spend and view keys match the given address
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;
- return false;
- }
- if (info.address.m_view_public_key != pkey) {
- m_errorString = tr("view key does not match address");
- m_status = Status_Error;
- return false;
+ if(has_viewkey) {
+ if (!crypto::secret_key_to_public_key(viewkey, pkey)) {
+ setStatusError(tr("failed to verify secret view key"));
+ return false;
+ }
+ if (info.address.m_view_public_key != pkey) {
+ setStatusError(tr("view key does not match address"));
+ return false;
+ }
}
try
{
- if (has_spendkey) {
+ if (has_spendkey && has_viewkey) {
m_wallet->generate(path, password, info.address, spendkey, viewkey);
- setSeedLanguage(language);
- LOG_PRINT_L1("Generated new wallet from keys with seed language: " + language);
+ LOG_PRINT_L1("Generated new wallet from spend key and view key");
}
- else {
+ if(!has_spendkey && has_viewkey) {
m_wallet->generate(path, password, info.address, viewkey);
LOG_PRINT_L1("Generated new view only wallet from keys");
}
+ if(has_spendkey && !has_viewkey) {
+ m_wallet->generate(path, password, spendkey, true, false, false);
+ setSeedLanguage(language);
+ LOG_PRINT_L1("Generated deterministic wallet from spend key with seed language: " + language);
+ }
}
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;
}
+bool WalletImpl::recoverFromDevice(const std::string &path, const std::string &password, const std::string &device_name)
+{
+ clearStatus();
+ m_recoveringFromSeed = false;
+ m_recoveringFromDevice = true;
+ try
+ {
+ m_wallet->restore(path, password, device_name);
+ LOG_PRINT_L1("Generated new wallet from device: " + device_name);
+ }
+ catch (const std::exception& e) {
+ setStatusError(string(tr("failed to generate new wallet: ")) + e.what());
+ return false;
+ }
+ return true;
+}
bool WalletImpl::open(const std::string &path, const std::string &password)
{
clearStatus();
m_recoveringFromSeed = false;
+ m_recoveringFromDevice = false;
try {
// TODO: handle "deprecated"
// Check if wallet cache exists
@@ -599,16 +655,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,18 +676,17 @@ 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;
}
m_recoveringFromSeed = true;
+ m_recoveringFromDevice = false;
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 +698,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)
@@ -668,11 +721,11 @@ bool WalletImpl::close(bool store)
LOG_PRINT_L1("Calling wallet::stop...");
m_wallet->stop();
LOG_PRINT_L1("wallet::stop done");
+ m_wallet->deinit();
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 +751,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 +774,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
@@ -753,6 +813,16 @@ std::string WalletImpl::publicSpendKey() const
return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_spend_public_key);
}
+std::string WalletImpl::publicMultisigSignerKey() const
+{
+ try {
+ crypto::public_key signer = m_wallet->get_multisig_signer_public_key();
+ return epee::string_tools::pod_to_hex(signer);
+ } catch (const std::exception&) {
+ return "";
+ }
+}
+
std::string WalletImpl::path() const
{
return m_wallet->path();
@@ -769,11 +839,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 +876,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 +889,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;
@@ -837,6 +905,16 @@ void WalletImpl::setRecoveringFromSeed(bool recoveringFromSeed)
m_recoveringFromSeed = recoveringFromSeed;
}
+void WalletImpl::setRecoveringFromDevice(bool recoveringFromDevice)
+{
+ m_recoveringFromDevice = recoveringFromDevice;
+}
+
+void WalletImpl::setSubaddressLookahead(uint32_t major, uint32_t minor)
+{
+ m_wallet->set_subaddress_lookahead(major, minor);
+}
+
uint64_t WalletImpl::balance(uint32_t accountIndex) const
{
return m_wallet->balance(accountIndex);
@@ -870,12 +948,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 +967,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 +993,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 +1026,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 +1034,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 +1045,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 +1061,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 +1069,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 +1085,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 +1098,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 +1130,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,9 +1143,134 @@ 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:
@@ -1117,8 +1306,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 +1331,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 +1340,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 +1371,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 +1405,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 +1413,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 +1458,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 +1490,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 +1592,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 +1600,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 +1609,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 +1619,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 +1627,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 +1636,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 +1645,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 +1667,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 +1695,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 +1723,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 +1744,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 +1764,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 +1774,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 +1783,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;
}
}
@@ -1694,13 +1821,57 @@ bool WalletImpl::verifySignedMessage(const std::string &message, const std::stri
return m_wallet->verify(message, info.address, signature);
}
+std::string WalletImpl::signMultisigParticipant(const std::string &message) const
+{
+ clearStatus();
+
+ bool ready = false;
+ if (!m_wallet->multisig(&ready) || !ready) {
+ m_status = Status_Error;
+ m_errorString = tr("The wallet must be in multisig ready state");
+ return {};
+ }
+
+ try {
+ return m_wallet->sign_multisig_participant(message);
+ } catch (const std::exception& e) {
+ m_status = Status_Error;
+ m_errorString = e.what();
+ }
+
+ return {};
+}
+
+bool WalletImpl::verifyMessageWithPublicKey(const std::string &message, const std::string &publicKey, const std::string &signature) const
+{
+ clearStatus();
+
+ cryptonote::blobdata pkeyData;
+ if(!epee::string_tools::parse_hexstr_to_binbuff(publicKey, pkeyData) || pkeyData.size() != sizeof(crypto::public_key))
+ {
+ m_status = Status_Error;
+ m_errorString = tr("Given string is not a key");
+ return false;
+ }
+
+ try {
+ crypto::public_key pkey = *reinterpret_cast<const crypto::public_key*>(pkeyData.data());
+ return m_wallet->verify_with_public_key(message, pkey, signature);
+ } catch (const std::exception& e) {
+ m_status = Status_Error;
+ m_errorString = e.what();
+ }
+
+ return false;
+}
+
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 +1906,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");
@@ -1752,7 +1941,7 @@ void WalletImpl::refreshThreadFunc()
// if auto refresh enabled, we wait for the "m_refreshIntervalSeconds" interval.
// if not - we wait forever
if (m_refreshIntervalMillis > 0) {
- boost::posix_time::milliseconds wait_for_ms(m_refreshIntervalMillis);
+ boost::posix_time::milliseconds wait_for_ms(m_refreshIntervalMillis.load());
m_refreshCV.timed_wait(lock, wait_for_ms);
} else {
m_refreshCV.wait(lock);
@@ -1760,7 +1949,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();
@@ -1777,7 +1966,7 @@ void WalletImpl::doRefresh()
// Syncing daemon and refreshing wallet simultaneously is very resource intensive.
// Disable refresh if wallet is disconnected or daemon isn't synced.
if (m_wallet->light_wallet() || daemonSynced()) {
- m_wallet->refresh();
+ m_wallet->refresh(trustedDaemon());
if (!m_synchronized) {
m_synchronized = true;
}
@@ -1792,8 +1981,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();
@@ -1839,7 +2027,7 @@ bool WalletImpl::isNewWallet() const
// with the daemon (pull hashes instead of pull blocks).
// If wallet cache is rebuilt, creation height stored in .keys is used.
// Watch only wallet is a copy of an existing wallet.
- return !(blockChainHeight() > 1 || m_recoveringFromSeed || m_rebuildWalletCache) && !watchOnly();
+ return !(blockChainHeight() > 1 || m_recoveringFromSeed || m_recoveringFromDevice || m_rebuildWalletCache) && !watchOnly();
}
bool WalletImpl::doInit(const string &daemon_address, uint64_t upper_transaction_size_limit, bool ssl)
@@ -1882,16 +2070,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 +2103,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 +2111,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 +2122,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 +2139,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 +2156,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 +2178,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..eefb2fe94 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -57,7 +57,7 @@ public:
bool create(const std::string &path, const std::string &password,
const std::string &language);
bool createWatchOnly(const std::string &path, const std::string &password,
- const std::string &language) const;
+ const std::string &language) const override;
bool open(const std::string &path, const std::string &password);
bool recover(const std::string &path,const std::string &password,
const std::string &seed);
@@ -76,104 +76,124 @@ public:
const std::string &address_string,
const std::string &viewkey_string,
const std::string &spendkey_string = "");
+ bool recoverFromDevice(const std::string &path,
+ const std::string &password,
+ const std::string &device_name);
bool close(bool store = true);
- std::string seed() const;
- std::string getSeedLanguage() const;
- void setSeedLanguage(const std::string &arg);
+ std::string seed() const override;
+ std::string getSeedLanguage() const override;
+ void setSeedLanguage(const std::string &arg) override;
// void setListener(Listener *) {}
- int status() const;
- std::string errorString() const;
- 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;
- std::string secretViewKey() const;
- std::string publicViewKey() const;
- std::string secretSpendKey() const;
- std::string publicSpendKey() const;
- std::string path() const;
- bool store(const std::string &path);
- std::string filename() const;
- std::string keysFilename() const;
- bool init(const std::string &daemon_address, uint64_t upper_transaction_size_limit = 0, const std::string &daemon_username = "", const std::string &daemon_password = "", bool use_ssl = false, bool lightWallet = false);
- bool connectToDaemon();
- ConnectionStatus connected() const;
- void setTrustedDaemon(bool arg);
- bool trustedDaemon() const;
- uint64_t balance(uint32_t accountIndex = 0) const;
- uint64_t unlockedBalance(uint32_t accountIndex = 0) const;
- uint64_t blockChainHeight() const;
- uint64_t approximateBlockChainHeight() const;
- uint64_t daemonBlockChainHeight() const;
- uint64_t daemonBlockChainTargetHeight() const;
- bool synchronized() const;
- bool refresh();
- void refreshAsync();
- void setAutoRefreshInterval(int millis);
- int autoRefreshInterval() const;
- void setRefreshFromBlockHeight(uint64_t refresh_from_block_height);
- uint64_t getRefreshFromBlockHeight() const { return m_wallet->get_refresh_from_block_height(); };
- void setRecoveringFromSeed(bool recoveringFromSeed);
- bool watchOnly() const;
- bool rescanSpent();
- NetworkType nettype() const {return static_cast<NetworkType>(m_wallet->nettype());}
- void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const;
- bool useForkRules(uint8_t version, int64_t early_blocks) const;
-
- void addSubaddressAccount(const std::string& label);
- size_t numSubaddressAccounts() const;
- size_t numSubaddresses(uint32_t accountIndex) const;
- void addSubaddress(uint32_t accountIndex, const std::string& label);
- std::string getSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex) const;
- void setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label);
+ int status() const override;
+ std::string errorString() const override;
+ void statusWithErrorString(int& status, std::string& errorString) const override;
+ bool setPassword(const std::string &password) override;
+ std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const override;
+ std::string integratedAddress(const std::string &payment_id) const override;
+ std::string secretViewKey() const override;
+ std::string publicViewKey() const override;
+ std::string secretSpendKey() const override;
+ std::string publicSpendKey() const override;
+ std::string publicMultisigSignerKey() const override;
+ std::string path() const override;
+ bool store(const std::string &path) override;
+ std::string filename() const override;
+ std::string keysFilename() const override;
+ bool init(const std::string &daemon_address, uint64_t upper_transaction_size_limit = 0, const std::string &daemon_username = "", const std::string &daemon_password = "", bool use_ssl = false, bool lightWallet = false) override;
+ bool connectToDaemon() override;
+ ConnectionStatus connected() const override;
+ void setTrustedDaemon(bool arg) override;
+ bool trustedDaemon() const override;
+ uint64_t balance(uint32_t accountIndex = 0) const override;
+ uint64_t unlockedBalance(uint32_t accountIndex = 0) const override;
+ uint64_t blockChainHeight() const override;
+ uint64_t approximateBlockChainHeight() const override;
+ uint64_t daemonBlockChainHeight() const override;
+ uint64_t daemonBlockChainTargetHeight() const override;
+ bool synchronized() const override;
+ bool refresh() override;
+ void refreshAsync() override;
+ void setAutoRefreshInterval(int millis) override;
+ int autoRefreshInterval() const override;
+ void setRefreshFromBlockHeight(uint64_t refresh_from_block_height) override;
+ uint64_t getRefreshFromBlockHeight() const override { return m_wallet->get_refresh_from_block_height(); };
+ void setRecoveringFromSeed(bool recoveringFromSeed) override;
+ void setRecoveringFromDevice(bool recoveringFromDevice) override;
+ void setSubaddressLookahead(uint32_t major, uint32_t minor) override;
+ bool watchOnly() const override;
+ bool rescanSpent() override;
+ NetworkType nettype() const override {return static_cast<NetworkType>(m_wallet->nettype());}
+ void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const override;
+ bool useForkRules(uint8_t version, int64_t early_blocks) const override;
+
+ void addSubaddressAccount(const std::string& label) override;
+ size_t numSubaddressAccounts() const override;
+ size_t numSubaddresses(uint32_t accountIndex) const override;
+ void addSubaddress(uint32_t accountIndex, const std::string& label) override;
+ std::string getSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex) const override;
+ void setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label) override;
+
+ 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,
uint32_t subaddr_account = 0,
- std::set<uint32_t> subaddr_indices = {});
- virtual PendingTransaction * createSweepUnmixableTransaction();
- bool submitTransaction(const std::string &fileName);
- virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename);
- bool exportKeyImages(const std::string &filename);
- bool importKeyImages(const std::string &filename);
-
- virtual void disposeTransaction(PendingTransaction * t);
- virtual TransactionHistory * history();
- virtual AddressBook * addressBook();
- virtual Subaddress * subaddress();
- virtual SubaddressAccount * subaddressAccount();
- virtual void setListener(WalletListener * l);
- virtual uint32_t defaultMixin() const;
- virtual void setDefaultMixin(uint32_t arg);
- virtual bool setUserNote(const std::string &txid, const std::string &note);
- virtual std::string getUserNote(const std::string &txid) const;
- virtual std::string getTxKey(const std::string &txid) const;
- virtual bool checkTxKey(const std::string &txid, std::string tx_key, const std::string &address, uint64_t &received, bool &in_pool, uint64_t &confirmations);
- virtual std::string getTxProof(const std::string &txid, const std::string &address, const std::string &message) const;
- virtual bool checkTxProof(const std::string &txid, const std::string &address, const std::string &message, const std::string &signature, bool &good, uint64_t &received, bool &in_pool, uint64_t &confirmations);
- virtual std::string getSpendProof(const std::string &txid, const std::string &message) const;
- virtual bool checkSpendProof(const std::string &txid, const std::string &message, const std::string &signature, bool &good) const;
- virtual std::string getReserveProof(bool all, uint32_t account_index, uint64_t amount, const std::string &message) const;
- virtual bool checkReserveProof(const std::string &address, const std::string &message, const std::string &signature, bool &good, uint64_t &total, uint64_t &spent) const;
- virtual std::string signMessage(const std::string &message);
- virtual bool verifySignedMessage(const std::string &message, const std::string &address, const std::string &signature) const;
- virtual void startRefresh();
- virtual void pauseRefresh();
- virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error);
- virtual std::string getDefaultDataDir() const;
- virtual bool lightWalletLogin(bool &isNewWallet) const;
- virtual bool lightWalletImportWalletRequest(std::string &payment_id, uint64_t &fee, bool &new_request, bool &request_fulfilled, std::string &payment_address, std::string &status);
- virtual bool blackballOutputs(const std::vector<std::string> &pubkeys, bool add);
- virtual bool unblackballOutput(const std::string &pubkey);
- virtual bool getRing(const std::string &key_image, std::vector<uint64_t> &ring) const;
- virtual bool getRings(const std::string &txid, std::vector<std::pair<std::string, std::vector<uint64_t>>> &rings) const;
- virtual bool setRing(const std::string &key_image, const std::vector<uint64_t> &ring, bool relative);
- virtual void segregatePreForkOutputs(bool segregate);
- virtual void segregationHeight(uint64_t height);
- virtual void keyReuseMitigation2(bool mitigation);
+ std::set<uint32_t> subaddr_indices = {}) override;
+ virtual PendingTransaction * createSweepUnmixableTransaction() override;
+ bool submitTransaction(const std::string &fileName) override;
+ virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) override;
+ bool exportKeyImages(const std::string &filename) override;
+ bool importKeyImages(const std::string &filename) override;
+
+ virtual void disposeTransaction(PendingTransaction * t) override;
+ virtual TransactionHistory * history() override;
+ virtual AddressBook * addressBook() override;
+ virtual Subaddress * subaddress() override;
+ virtual SubaddressAccount * subaddressAccount() override;
+ virtual void setListener(WalletListener * l) override;
+ virtual uint32_t defaultMixin() const override;
+ virtual void setDefaultMixin(uint32_t arg) override;
+ virtual bool setUserNote(const std::string &txid, const std::string &note) override;
+ virtual std::string getUserNote(const std::string &txid) const override;
+ virtual std::string getTxKey(const std::string &txid) const override;
+ virtual bool checkTxKey(const std::string &txid, std::string tx_key, const std::string &address, uint64_t &received, bool &in_pool, uint64_t &confirmations) override;
+ virtual std::string getTxProof(const std::string &txid, const std::string &address, const std::string &message) const override;
+ virtual bool checkTxProof(const std::string &txid, const std::string &address, const std::string &message, const std::string &signature, bool &good, uint64_t &received, bool &in_pool, uint64_t &confirmations) override;
+ virtual std::string getSpendProof(const std::string &txid, const std::string &message) const override;
+ virtual bool checkSpendProof(const std::string &txid, const std::string &message, const std::string &signature, bool &good) const override;
+ virtual std::string getReserveProof(bool all, uint32_t account_index, uint64_t amount, const std::string &message) const override;
+ virtual bool checkReserveProof(const std::string &address, const std::string &message, const std::string &signature, bool &good, uint64_t &total, uint64_t &spent) const override;
+ virtual std::string signMessage(const std::string &message) override;
+ virtual bool verifySignedMessage(const std::string &message, const std::string &address, const std::string &signature) const override;
+ virtual std::string signMultisigParticipant(const std::string &message) const override;
+ virtual bool verifyMessageWithPublicKey(const std::string &message, const std::string &publicKey, const std::string &signature) const override;
+ virtual void startRefresh() override;
+ virtual void pauseRefresh() override;
+ virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error) override;
+ virtual std::string getDefaultDataDir() const override;
+ virtual bool lightWalletLogin(bool &isNewWallet) const override;
+ virtual bool lightWalletImportWalletRequest(std::string &payment_id, uint64_t &fee, bool &new_request, bool &request_fulfilled, std::string &payment_address, std::string &status) override;
+ virtual bool blackballOutputs(const std::vector<std::string> &pubkeys, bool add) override;
+ virtual bool unblackballOutput(const std::string &pubkey) override;
+ virtual bool getRing(const std::string &key_image, std::vector<uint64_t> &ring) const override;
+ virtual bool getRings(const std::string &txid, std::vector<std::pair<std::string, std::vector<uint64_t>>> &rings) const override;
+ virtual bool setRing(const std::string &key_image, const std::vector<uint64_t> &ring, bool relative) override;
+ virtual void segregatePreForkOutputs(bool segregate) override;
+ virtual void segregationHeight(uint64_t height) override;
+ virtual void keyReuseMitigation2(bool mitigation) override;
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 +211,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;
@@ -216,6 +237,7 @@ private:
// so it shouldn't be considered as new and pull blocks (slow-refresh)
// instead of pulling hashes (fast-refresh)
std::atomic<bool> m_recoveringFromSeed;
+ std::atomic<bool> m_recoveringFromDevice;
std::atomic<bool> m_synchronized;
std::atomic<bool> m_rebuildWalletCache;
// cache connection status to avoid unnecessary RPC calls
diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h
index d4e41c5aa..f54255e91 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); }
@@ -409,6 +444,12 @@ struct Wallet
virtual std::string publicSpendKey() const = 0;
/*!
+ * \brief publicMultisigSignerKey - returns public signer key
+ * \return - public multisignature signer key or empty string if wallet is not multisig
+ */
+ virtual std::string publicMultisigSignerKey() const = 0;
+
+ /*!
* \brief store - stores wallet to file.
* \param path - main filename to store wallet to. additionally stores address file and keys file.
* to store to the same file - just pass empty string;
@@ -468,6 +509,21 @@ struct Wallet
*/
virtual void setRecoveringFromSeed(bool recoveringFromSeed) = 0;
+ /*!
+ * \brief setRecoveringFromDevice - set state to recovering from device
+ *
+ * \param recoveringFromDevice - true/false
+ */
+ virtual void setRecoveringFromDevice(bool recoveringFromDevice) = 0;
+
+ /*!
+ * \brief setSubaddressLookahead - set size of subaddress lookahead
+ *
+ * \param major - size fot the major index
+ * \param minor - size fot the minor index
+ */
+ virtual void setSubaddressLookahead(uint32_t major, uint32_t minor) = 0;
+
/**
* @brief connectToDaemon - connects to the daemon. TODO: check if it can be removed
* @return
@@ -556,7 +612,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 +685,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
@@ -747,6 +846,21 @@ struct Wallet
*/
virtual bool verifySignedMessage(const std::string &message, const std::string &addres, const std::string &signature) const = 0;
+ /*!
+ * \brief signMultisigParticipant signs given message with the multisig public signer key
+ * \param message message to sign
+ * \return signature in case of success. Sets status to Error and return empty string in case of error
+ */
+ virtual std::string signMultisigParticipant(const std::string &message) const = 0;
+ /*!
+ * \brief verifyMessageWithPublicKey verifies that message was signed with the given public key
+ * \param message message
+ * \param publicKey hex encoded public key
+ * \param signature signature of the message
+ * \return true if the signature is correct. false and sets error state in case of error
+ */
+ virtual bool verifyMessageWithPublicKey(const std::string &message, const std::string &publicKey, const std::string &signature) const = 0;
+
virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error) = 0;
virtual std::string getDefaultDataDir() const = 0;
@@ -916,6 +1030,23 @@ struct WalletManager
}
/*!
+ * \brief creates wallet using hardware device.
+ * \param path Name of wallet file to be created
+ * \param password Password of wallet file
+ * \param nettype Network type
+ * \param deviceName Device name
+ * \param restoreHeight restore from start height (0 sets to current height)
+ * \param subaddressLookahead Size of subaddress lookahead (empty sets to some default low value)
+ * \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully)
+ */
+ virtual Wallet * createWalletFromDevice(const std::string &path,
+ const std::string &password,
+ NetworkType nettype,
+ const std::string &deviceName,
+ uint64_t restoreHeight = 0,
+ const std::string &subaddressLookahead = "") = 0;
+
+ /*!
* \brief Closes wallet. In case operation succeeded, wallet object deleted. in case operation failed, wallet object not deleted
* \param wallet previously opened / created wallet instance
* \return None
@@ -956,25 +1087,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..99eadc82f 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,
@@ -123,6 +114,26 @@ Wallet *WalletManagerImpl::createWalletFromKeys(const std::string &path,
return wallet;
}
+Wallet *WalletManagerImpl::createWalletFromDevice(const std::string &path,
+ const std::string &password,
+ NetworkType nettype,
+ const std::string &deviceName,
+ uint64_t restoreHeight,
+ const std::string &subaddressLookahead)
+{
+ WalletImpl * wallet = new WalletImpl(nettype);
+ if(restoreHeight > 0){
+ wallet->setRefreshFromBlockHeight(restoreHeight);
+ }
+ auto lookahead = tools::parse_subaddress_lookahead(subaddressLookahead);
+ if (lookahead)
+ {
+ wallet->setSubaddressLookahead(lookahead->first, lookahead->second);
+ }
+ wallet->recoverFromDevice(path, password, deviceName);
+ return wallet;
+}
+
bool WalletManagerImpl::closeWallet(Wallet *wallet, bool store)
{
WalletImpl * wallet_ = dynamic_cast<WalletImpl*>(wallet);
@@ -193,16 +204,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 +224,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 +297,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 +307,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..656a7142c 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 {
@@ -38,13 +39,13 @@ class WalletManagerImpl : public WalletManager
{
public:
Wallet * createWallet(const std::string &path, const std::string &password,
- const std::string &language, NetworkType nettype);
- Wallet * openWallet(const std::string &path, const std::string &password, NetworkType nettype);
+ const std::string &language, NetworkType nettype) override;
+ Wallet * openWallet(const std::string &path, const std::string &password, NetworkType nettype) override;
virtual Wallet * recoveryWallet(const std::string &path,
const std::string &password,
const std::string &mnemonic,
NetworkType nettype,
- uint64_t restoreHeight);
+ uint64_t restoreHeight) override;
virtual Wallet * createWalletFromKeys(const std::string &path,
const std::string &password,
const std::string &language,
@@ -52,9 +53,9 @@ public:
uint64_t restoreHeight,
const std::string &addressString,
const std::string &viewKeyString,
- const std::string &spendKeyString = "");
+ const std::string &spendKeyString = "") override;
// next two methods are deprecated - use the above version which allow setting of a password
- virtual Wallet * recoveryWallet(const std::string &path, const std::string &mnemonic, NetworkType nettype, uint64_t restoreHeight);
+ virtual Wallet * recoveryWallet(const std::string &path, const std::string &mnemonic, NetworkType nettype, uint64_t restoreHeight) override;
// deprecated: use createWalletFromKeys(..., password, ...) instead
virtual Wallet * createWalletFromKeys(const std::string &path,
const std::string &language,
@@ -62,28 +63,35 @@ public:
uint64_t restoreHeight,
const std::string &addressString,
const std::string &viewKeyString,
- const std::string &spendKeyString = "");
- virtual bool closeWallet(Wallet *wallet, bool store = true);
- bool walletExists(const std::string &path);
- bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key) const;
- 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 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;
+ const std::string &spendKeyString = "") override;
+ virtual Wallet * createWalletFromDevice(const std::string &path,
+ const std::string &password,
+ NetworkType nettype,
+ const std::string &deviceName,
+ uint64_t restoreHeight = 0,
+ const std::string &subaddressLookahead = "") override;
+ virtual bool closeWallet(Wallet *wallet, bool store = true) override;
+ bool walletExists(const std::string &path) override;
+ bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key) const override;
+ std::vector<std::string> findWallets(const std::string &path) override;
+ std::string errorString() const override;
+ void setDaemonAddress(const std::string &address) override;
+ bool connected(uint32_t *version = NULL) override;
+ uint64_t blockchainHeight() override;
+ uint64_t blockchainTargetHeight() override;
+ uint64_t networkDifficulty() override;
+ double miningHashRate() override;
+ uint64_t blockTarget() override;
+ bool isMining() override;
+ bool startMining(const std::string &address, uint32_t threads = 1, bool background_mining = false, bool ignore_battery = true) override;
+ bool stopMining() override;
+ std::string resolveOpenAlias(const std::string &address, bool &dnssec_valid) const override;
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 b06444132..40a43b8e7 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -110,11 +110,18 @@ using namespace cryptonote;
#define MULTISIG_EXPORT_FILE_MAGIC "Monero multisig export\001"
-#define SEGREGATION_FORK_HEIGHT 1564965
+#define OUTPUT_EXPORT_FILE_MAGIC "Monero output export\003"
+
+#define SEGREGATION_FORK_HEIGHT 1546000
#define TESTNET_SEGREGATION_FORK_HEIGHT 1000000
#define STAGENET_SEGREGATION_FORK_HEIGHT 1000000
#define SEGREGATION_FORK_VICINITY 1500 /* blocks */
+#define FIRST_REFRESH_GRANULARITY 1024
+
+static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1";
+
+std::atomic<unsigned int> tools::wallet2::key_ref::refs(0);
namespace
{
@@ -141,13 +148,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;
}
};
@@ -192,6 +201,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
{
const bool testnet = command_line::get_arg(vm, opts.testnet);
const bool stagenet = command_line::get_arg(vm, opts.stagenet);
+ const network_type nettype = testnet ? TESTNET : stagenet ? STAGENET : MAINNET;
const bool restricted = command_line::get_arg(vm, opts.restricted);
auto daemon_address = command_line::get_arg(vm, opts.daemon_address);
@@ -220,13 +230,13 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
if (!daemon_port)
{
- daemon_port = testnet ? config::testnet::RPC_DEFAULT_PORT : stagenet ? config::stagenet::RPC_DEFAULT_PORT : config::RPC_DEFAULT_PORT;
+ daemon_port = get_config(nettype).RPC_DEFAULT_PORT;
}
if (daemon_address.empty())
daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port);
- std::unique_ptr<tools::wallet2> wallet(new tools::wallet2(testnet ? TESTNET : stagenet ? STAGENET : MAINNET, restricted));
+ std::unique_ptr<tools::wallet2> wallet(new tools::wallet2(nettype, restricted));
wallet->init(std::move(daemon_address), std::move(login));
boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir);
wallet->set_ring_database(ringdb_path.string());
@@ -650,9 +660,11 @@ wallet2::wallet2(network_type nettype, bool restricted):
m_default_priority(0),
m_refresh_type(RefreshOptimizeCoinbase),
m_auto_refresh(true),
+ m_first_refresh_done(false),
m_refresh_from_block_height(0),
m_explicit_refresh_from_block_height(true),
m_confirm_missing_payment_id(true),
+ m_confirm_non_default_ring_size(true),
m_ask_password(true),
m_min_output_count(0),
m_min_output_value(0),
@@ -678,7 +690,8 @@ wallet2::wallet2(network_type nettype, bool restricted):
m_light_wallet_unlocked_balance(0),
m_key_on_device(false),
m_ring_history_saved(false),
- m_ringdb()
+ m_ringdb(),
+ m_last_block_reward(0)
{
}
@@ -762,8 +775,6 @@ bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::
m_daemon_address = std::move(daemon_address);
m_daemon_login = std::move(daemon_login);
// When switching from light wallet to full wallet, we need to reset the height we got from lw node.
- if(m_light_wallet)
- m_local_bc_height = m_blockchain.size();
return m_http_client.set_server(get_daemon_address(), get_daemon_login(), ssl);
}
//----------------------------------------------------------------------------------------------------
@@ -939,6 +950,7 @@ void wallet2::expand_subaddresses(const cryptonote::subaddress_index& index)
}
m_subaddress_labels.resize(index.major + 1, {"Untitled account"});
m_subaddress_labels[index.major].resize(index.minor + 1);
+ get_account_tags();
}
else if (m_subaddress_labels[index.major].size() <= index.minor)
{
@@ -1008,7 +1020,7 @@ void wallet2::set_unspent(size_t idx)
void wallet2::check_acc_out_precomp(const tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, tx_scan_info_t &tx_scan_info) const
{
hw::device &hwdev = m_account.get_device();
- std::unique_lock<hw::device> hwdev_lock (hwdev);
+ boost::unique_lock<hw::device> hwdev_lock (hwdev);
hwdev.set_mode(hw::device::TRANSACTION_PARSE);
if (o.target.type() != typeid(txout_to_key))
{
@@ -1028,6 +1040,33 @@ void wallet2::check_acc_out_precomp(const tx_out &o, const crypto::key_derivatio
tx_scan_info.error = false;
}
//----------------------------------------------------------------------------------------------------
+void wallet2::check_acc_out_precomp(const tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info) const
+{
+ if (!is_out_data || i >= is_out_data->received.size())
+ return check_acc_out_precomp(o, derivation, additional_derivations, i, tx_scan_info);
+
+ tx_scan_info.received = is_out_data->received[i];
+ if(tx_scan_info.received)
+ {
+ tx_scan_info.money_transfered = o.amount; // may be 0 for ringct outputs
+ }
+ else
+ {
+ tx_scan_info.money_transfered = 0;
+ }
+ tx_scan_info.error = false;
+}
+//----------------------------------------------------------------------------------------------------
+void wallet2::check_acc_out_precomp_once(const tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info, bool &already_seen) const
+{
+ tx_scan_info.received = boost::none;
+ if (already_seen)
+ return;
+ check_acc_out_precomp(o, derivation, additional_derivations, i, is_out_data, tx_scan_info);
+ if (tx_scan_info.received)
+ already_seen = true;
+}
+//----------------------------------------------------------------------------------------------------
static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation &derivation, unsigned int i, rct::key & mask, hw::device &hwdev)
{
crypto::secret_key scalar1;
@@ -1081,16 +1120,48 @@ void wallet2::scan_output(const cryptonote::transaction &tx, const crypto::publi
++num_vouts_received;
}
//----------------------------------------------------------------------------------------------------
-void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen)
+void wallet2::cache_tx_data(const cryptonote::transaction& tx, const crypto::hash &txid, tx_cache_data &tx_cache_data) const
{
- //ensure device is let in NONE mode in any case
- hw::device &hwdev = m_account.get_device();
-
- std::unique_lock<hw::device> hwdev_lock (hwdev);
- hw::reset_mode rst(hwdev);
- hwdev_lock.unlock();
+ const cryptonote::account_keys& keys = m_account.get_keys();
+
+ if(!parse_tx_extra(tx.extra, tx_cache_data.tx_extra_fields))
+ {
+ // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key
+ LOG_PRINT_L0("Transaction extra has unsupported format: " << txid);
+ tx_cache_data.tx_extra_fields.clear();
+ return;
+ }
+
+ // Don't try to extract tx public key if tx has no ouputs
+ const bool is_miner = tx.vin.size() == 1 && tx.vin[0].type() == typeid(cryptonote::txin_gen);
+ if (!is_miner || m_refresh_type != RefreshType::RefreshNoCoinbase)
+ {
+ const size_t rec_size = is_miner && m_refresh_type == RefreshType::RefreshOptimizeCoinbase ? 1 : tx.vout.size();
+ if (!tx.vout.empty())
+ {
+ // if tx.vout is not empty, we loop through all tx pubkeys
+ const std::vector<boost::optional<cryptonote::subaddress_receive_info>> rec(rec_size, boost::none);
+
+ tx_extra_pub_key pub_key_field;
+ size_t pk_index = 0;
+ while (find_tx_extra_field_by_type(tx_cache_data.tx_extra_fields, pub_key_field, pk_index++))
+ tx_cache_data.primary.push_back({pub_key_field.pub_key, {}, rec});
- // In this function, tx (probably) only contains the base information
+ // additional tx pubkeys and derivations for multi-destination transfers involving one or more subaddresses
+ tx_extra_additional_pub_keys additional_tx_pub_keys;
+ std::vector<crypto::key_derivation> additional_derivations;
+ if (find_tx_extra_field_by_type(tx_cache_data.tx_extra_fields, additional_tx_pub_keys))
+ {
+ for (size_t i = 0; i < additional_tx_pub_keys.data.size(); ++i)
+ tx_cache_data.additional.push_back({additional_tx_pub_keys.data[i], {}, {}});
+ }
+ }
+ }
+}
+//----------------------------------------------------------------------------------------------------
+void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen, const tx_cache_data &tx_cache_data)
+{
+ // In this function, tx (probably) only contains the base information
// (that is, the prunable stuff may or may not be included)
if (!miner_tx && !pool)
process_unconfirmed(txid, tx, height);
@@ -1098,16 +1169,21 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
std::unordered_map<cryptonote::subaddress_index, uint64_t> tx_money_got_in_outs; // per receiving subaddress index
crypto::public_key tx_pub_key = null_pkey;
- std::vector<tx_extra_field> tx_extra_fields;
- if(!parse_tx_extra(tx.extra, tx_extra_fields))
+ std::vector<tx_extra_field> local_tx_extra_fields;
+ if (tx_cache_data.tx_extra_fields.empty())
{
- // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key
- LOG_PRINT_L0("Transaction extra has unsupported format: " << txid);
+ if(!parse_tx_extra(tx.extra, local_tx_extra_fields))
+ {
+ // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key
+ LOG_PRINT_L0("Transaction extra has unsupported format: " << txid);
+ }
}
+ const std::vector<tx_extra_field> &tx_extra_fields = tx_cache_data.tx_extra_fields.empty() ? local_tx_extra_fields : tx_cache_data.tx_extra_fields;
// Don't try to extract tx public key if tx has no ouputs
size_t pk_index = 0;
std::vector<tx_scan_info_t> tx_scan_info(tx.vout.size());
+ std::deque<bool> output_found(tx.vout.size(), false);
while (!tx.vout.empty())
{
// if tx.vout is not empty, we loop through all tx pubkeys
@@ -1122,6 +1198,11 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
m_callback->on_skip_transaction(height, txid, tx);
break;
}
+ if (!tx_cache_data.primary.empty())
+ {
+ THROW_WALLET_EXCEPTION_IF(tx_cache_data.primary.size() < pk_index || pub_key_field.pub_key != tx_cache_data.primary[pk_index - 1].pkey,
+ error::wallet_internal_error, "tx_cache_data is out of sync");
+ }
int num_vouts_received = 0;
tx_pub_key = pub_key_field.pub_key;
@@ -1130,28 +1211,55 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
const cryptonote::account_keys& keys = m_account.get_keys();
crypto::key_derivation derivation;
- hwdev_lock.lock();
- hwdev.set_mode(hw::device::TRANSACTION_PARSE);
- if (!hwdev.generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation))
+ std::vector<crypto::key_derivation> additional_derivations;
+ tx_extra_additional_pub_keys additional_tx_pub_keys;
+ const wallet2::is_out_data *is_out_data_ptr = NULL;
+ if (tx_cache_data.primary.empty())
{
- MWARNING("Failed to generate key derivation from tx pubkey, skipping");
- static_assert(sizeof(derivation) == sizeof(rct::key), "Mismatched sizes of key_derivation and rct::key");
- memcpy(&derivation, rct::identity().bytes, sizeof(derivation));
- }
+ hw::device &hwdev = m_account.get_device();
+ boost::unique_lock<hw::device> hwdev_lock (hwdev);
+ hw::reset_mode rst(hwdev);
- // additional tx pubkeys and derivations for multi-destination transfers involving one or more subaddresses
- std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(tx);
- std::vector<crypto::key_derivation> additional_derivations;
- for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
+ hwdev.set_mode(hw::device::TRANSACTION_PARSE);
+ if (!hwdev.generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation))
+ {
+ MWARNING("Failed to generate key derivation from tx pubkey in " << txid << ", skipping");
+ static_assert(sizeof(derivation) == sizeof(rct::key), "Mismatched sizes of key_derivation and rct::key");
+ memcpy(&derivation, rct::identity().bytes, sizeof(derivation));
+ }
+
+ if (pk_index == 1)
+ {
+ // additional tx pubkeys and derivations for multi-destination transfers involving one or more subaddresses
+ if (find_tx_extra_field_by_type(tx_extra_fields, additional_tx_pub_keys))
+ {
+ for (size_t i = 0; i < additional_tx_pub_keys.data.size(); ++i)
+ {
+ additional_derivations.push_back({});
+ if (!hwdev.generate_key_derivation(additional_tx_pub_keys.data[i], keys.m_view_secret_key, additional_derivations.back()))
+ {
+ MWARNING("Failed to generate key derivation from additional tx pubkey in " << txid << ", skipping");
+ memcpy(&additional_derivations.back(), rct::identity().bytes, sizeof(crypto::key_derivation));
+ }
+ }
+ }
+ }
+ }
+ else
{
- additional_derivations.push_back({});
- if (!hwdev.generate_key_derivation(additional_tx_pub_keys[i], keys.m_view_secret_key, additional_derivations.back()))
+ THROW_WALLET_EXCEPTION_IF(pk_index - 1 >= tx_cache_data.primary.size(),
+ error::wallet_internal_error, "pk_index out of range of tx_cache_data");
+ is_out_data_ptr = &tx_cache_data.primary[pk_index - 1];
+ derivation = tx_cache_data.primary[pk_index - 1].derivation;
+ if (pk_index == 1)
{
- MWARNING("Failed to generate key derivation from tx pubkey, skipping");
- additional_derivations.pop_back();
+ for (size_t n = 0; n < tx_cache_data.additional.size(); ++n)
+ {
+ additional_tx_pub_keys.data.push_back(tx_cache_data.additional[n].pkey);
+ additional_derivations.push_back(tx_cache_data.additional[n].derivation);
+ }
}
}
- hwdev_lock.unlock();
if (miner_tx && m_refresh_type == RefreshNoCoinbase)
{
@@ -1159,7 +1267,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
}
else if (miner_tx && m_refresh_type == RefreshOptimizeCoinbase)
{
- check_acc_out_precomp(tx.vout[0], derivation, additional_derivations, 0, tx_scan_info[0]);
+ check_acc_out_precomp_once(tx.vout[0], derivation, additional_derivations, 0, is_out_data_ptr, tx_scan_info[0], output_found[0]);
THROW_WALLET_EXCEPTION_IF(tx_scan_info[0].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys());
// this assumes that the miner tx pays a single address
@@ -1169,60 +1277,60 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
// the first one was already checked
for (size_t i = 1; i < tx.vout.size(); ++i)
{
- tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i,
- std::ref(tx_scan_info[i])));
+ tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp_once, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i,
+ std::cref(is_out_data_ptr), std::ref(tx_scan_info[i]), std::ref(output_found[i])), true);
}
- waiter.wait();
+ waiter.wait(&tpool);
// then scan all outputs from 0
- hwdev_lock.lock();
+ hw::device &hwdev = m_account.get_device();
+ boost::unique_lock<hw::device> hwdev_lock (hwdev);
hwdev.set_mode(hw::device::NONE);
for (size_t i = 0; i < tx.vout.size(); ++i)
{
THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys());
if (tx_scan_info[i].received)
{
- hwdev.generate_key_derivation(tx_pub_key, keys.m_view_secret_key, tx_scan_info[i].received->derivation);
+ hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys.data, derivation, additional_derivations);
scan_output(tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs);
}
}
- hwdev_lock.unlock();
}
}
- else if (tx.vout.size() > 1 && tools::threadpool::getInstance().get_max_concurrency() > 1)
+ else if (tx.vout.size() > 1 && tools::threadpool::getInstance().get_max_concurrency() > 1 && !is_out_data_ptr)
{
for (size_t i = 0; i < tx.vout.size(); ++i)
{
- tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i,
- std::ref(tx_scan_info[i])));
+ tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp_once, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i,
+ std::cref(is_out_data_ptr), std::ref(tx_scan_info[i]), std::ref(output_found[i])), true);
}
- waiter.wait();
+ waiter.wait(&tpool);
- hwdev_lock.lock();
+ hw::device &hwdev = m_account.get_device();
+ boost::unique_lock<hw::device> hwdev_lock (hwdev);
hwdev.set_mode(hw::device::NONE);
for (size_t i = 0; i < tx.vout.size(); ++i)
{
THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys());
if (tx_scan_info[i].received)
{
- hwdev.generate_key_derivation(tx_pub_key, keys.m_view_secret_key, tx_scan_info[i].received->derivation);
+ hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys.data, derivation, additional_derivations);
scan_output(tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs);
}
}
- hwdev_lock.unlock();
}
else
{
for (size_t i = 0; i < tx.vout.size(); ++i)
{
- check_acc_out_precomp(tx.vout[i], derivation, additional_derivations, i, tx_scan_info[i]);
+ check_acc_out_precomp_once(tx.vout[i], derivation, additional_derivations, i, is_out_data_ptr, tx_scan_info[i], output_found[i]);
THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys());
if (tx_scan_info[i].received)
{
- hwdev_lock.lock();
+ hw::device &hwdev = m_account.get_device();
+ boost::unique_lock<hw::device> hwdev_lock (hwdev);
hwdev.set_mode(hw::device::NONE);
- hwdev.generate_key_derivation(tx_pub_key, keys.m_view_secret_key, tx_scan_info[i].received->derivation);
+ hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys.data, derivation, additional_derivations);
scan_output(tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs);
- hwdev_lock.unlock();
}
}
}
@@ -1298,20 +1406,20 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index);
}
}
- else if (m_transfers[kit->second].m_spent || m_transfers[kit->second].amount() >= tx.vout[o].amount)
+ else if (m_transfers[kit->second].m_spent || m_transfers[kit->second].amount() >= tx_scan_info[o].amount)
{
LOG_ERROR("Public key " << epee::string_tools::pod_to_hex(kit->first)
- << " from received " << print_money(tx.vout[o].amount) << " output already exists with "
+ << " from received " << print_money(tx_scan_info[o].amount) << " output already exists with "
<< (m_transfers[kit->second].m_spent ? "spent" : "unspent") << " "
- << print_money(m_transfers[kit->second].amount()) << ", received output ignored");
+ << print_money(m_transfers[kit->second].amount()) << " in tx " << m_transfers[kit->second].m_txid << ", received output ignored");
}
else
{
LOG_ERROR("Public key " << epee::string_tools::pod_to_hex(kit->first)
- << " from received " << print_money(tx.vout[o].amount) << " output already exists with "
+ << " from received " << print_money(tx_scan_info[o].amount) << " output already exists with "
<< print_money(m_transfers[kit->second].amount()) << ", replacing with new output");
// The new larger output replaced a previous smaller one
- tx_money_got_in_outs[tx_scan_info[o].received->index] -= tx.vout[o].amount;
+ tx_money_got_in_outs[tx_scan_info[o].received->index] -= tx_scan_info[o].amount;
if (!pool)
{
@@ -1562,12 +1670,11 @@ void wallet2::process_outgoing(const crypto::hash &txid, const cryptonote::trans
add_rings(tx);
}
//----------------------------------------------------------------------------------------------------
-void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height, const cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices &o_indices)
+void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const parsed_block &parsed_block, const crypto::hash& bl_id, uint64_t height, const std::vector<tx_cache_data> &tx_cache_data, size_t tx_cache_data_offset)
{
- size_t txidx = 0;
- THROW_WALLET_EXCEPTION_IF(bche.txs.size() + 1 != o_indices.indices.size(), error::wallet_internal_error,
+ THROW_WALLET_EXCEPTION_IF(bche.txs.size() + 1 != parsed_block.o_indices.indices.size(), error::wallet_internal_error,
"block transactions=" + std::to_string(bche.txs.size()) +
- " not match with daemon response size=" + std::to_string(o_indices.indices.size()));
+ " not match with daemon response size=" + std::to_string(parsed_block.o_indices.indices.size()));
//handle transactions from new block
@@ -1575,39 +1682,38 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry
if(b.timestamp + 60*60*24 > m_account.get_createtime() && height >= m_refresh_from_block_height)
{
TIME_MEASURE_START(miner_tx_handle_time);
- process_new_transaction(get_transaction_hash(b.miner_tx), b.miner_tx, o_indices.indices[txidx++].indices, height, b.timestamp, true, false, false);
+ if (m_refresh_type != RefreshNoCoinbase)
+ process_new_transaction(get_transaction_hash(b.miner_tx), b.miner_tx, parsed_block.o_indices.indices[0].indices, height, b.timestamp, true, false, false, tx_cache_data[tx_cache_data_offset]);
+ ++tx_cache_data_offset;
TIME_MEASURE_FINISH(miner_tx_handle_time);
TIME_MEASURE_START(txs_handle_time);
THROW_WALLET_EXCEPTION_IF(bche.txs.size() != b.tx_hashes.size(), error::wallet_internal_error, "Wrong amount of transactions for block");
- size_t idx = 0;
- for (const auto& txblob: bche.txs)
+ THROW_WALLET_EXCEPTION_IF(bche.txs.size() != parsed_block.txes.size(), error::wallet_internal_error, "Wrong amount of transactions for block");
+ for (size_t idx = 0; idx < b.tx_hashes.size(); ++idx)
{
- cryptonote::transaction tx;
- bool r = parse_and_validate_tx_base_from_blob(txblob, tx);
- THROW_WALLET_EXCEPTION_IF(!r, error::tx_parse_error, txblob);
- process_new_transaction(b.tx_hashes[idx], tx, o_indices.indices[txidx++].indices, height, b.timestamp, false, false, false);
- ++idx;
+ process_new_transaction(b.tx_hashes[idx], parsed_block.txes[idx], parsed_block.o_indices.indices[idx+1].indices, height, b.timestamp, false, false, false, tx_cache_data[tx_cache_data_offset++]);
}
TIME_MEASURE_FINISH(txs_handle_time);
+ m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
LOG_PRINT_L2("Processed block: " << bl_id << ", height " << height << ", " << miner_tx_handle_time + txs_handle_time << "(" << miner_tx_handle_time << "/" << txs_handle_time <<")ms");
}else
{
- if (!(height % 100))
+ if (!(height % 128))
LOG_PRINT_L2( "Skipped block by timestamp, height: " << height << ", block time " << b.timestamp << ", account time " << m_account.get_createtime());
}
m_blockchain.push_back(bl_id);
- ++m_local_bc_height;
if (0 != m_callback)
m_callback->on_new_block(height, b);
}
//----------------------------------------------------------------------------------------------------
-void wallet2::get_short_chain_history(std::list<crypto::hash>& ids) const
+void wallet2::get_short_chain_history(std::list<crypto::hash>& ids, uint64_t granularity) const
{
size_t i = 0;
size_t current_multiplier = 1;
- size_t sz = m_blockchain.size() - m_blockchain.offset();
+ size_t blockchain_size = std::max((size_t)(m_blockchain.size() / granularity * granularity), m_blockchain.offset());
+ size_t sz = blockchain_size - m_blockchain.offset();
if(!sz)
{
ids.push_back(m_blockchain.genesis());
@@ -1642,7 +1748,7 @@ void wallet2::parse_block_round(const cryptonote::blobdata &blob, cryptonote::bl
bl_id = get_block_hash(bl);
}
//----------------------------------------------------------------------------------------------------
-void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::list<cryptonote::block_complete_entry> &blocks, std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices)
+void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices)
{
cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::request req = AUTO_VAL_INIT(req);
cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response res = AUTO_VAL_INIT(res);
@@ -1677,6 +1783,7 @@ void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height,
}
req.start_height = start_height;
+ req.no_miner_tx = m_refresh_type == RefreshNoCoinbase;
m_daemon_rpc_mutex.lock();
bool r = net_utils::invoke_http_bin("/getblocks.bin", req, res, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
@@ -1688,11 +1795,11 @@ void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height,
boost::lexical_cast<std::string>(res.output_indices.size()) + ") sizes from daemon");
blocks_start_height = res.start_height;
- blocks = res.blocks;
- o_indices = res.output_indices;
+ blocks = std::move(res.blocks);
+ o_indices = std::move(res.output_indices);
}
//----------------------------------------------------------------------------------------------------
-void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::list<crypto::hash> &hashes)
+void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::vector<crypto::hash> &hashes)
{
cryptonote::COMMAND_RPC_GET_HASHES_FAST::request req = AUTO_VAL_INIT(req);
cryptonote::COMMAND_RPC_GET_HASHES_FAST::response res = AUTO_VAL_INIT(res);
@@ -1707,88 +1814,117 @@ void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height,
THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_hashes_error, res.status);
blocks_start_height = res.start_height;
- hashes = res.m_block_ids;
+ hashes = std::move(res.m_block_ids);
}
//----------------------------------------------------------------------------------------------------
-void wallet2::process_blocks(uint64_t start_height, const std::list<cryptonote::block_complete_entry> &blocks, const std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices, uint64_t& blocks_added)
+void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cryptonote::block_complete_entry> &blocks, const std::vector<parsed_block> &parsed_blocks, uint64_t& blocks_added)
{
size_t current_index = start_height;
blocks_added = 0;
- size_t tx_o_indices_idx = 0;
- THROW_WALLET_EXCEPTION_IF(blocks.size() != o_indices.size(), error::wallet_internal_error, "size mismatch");
+ THROW_WALLET_EXCEPTION_IF(blocks.size() != parsed_blocks.size(), error::wallet_internal_error, "size mismatch");
THROW_WALLET_EXCEPTION_IF(!m_blockchain.is_in_bounds(current_index), error::wallet_internal_error, "Index out of bounds of hashchain");
tools::threadpool& tpool = tools::threadpool::getInstance();
- int threads = tpool.get_max_concurrency();
- if (threads > 1)
+ tools::threadpool::waiter waiter;
+
+ size_t num_txes = 0;
+ std::vector<tx_cache_data> tx_cache_data;
+ for (size_t i = 0; i < blocks.size(); ++i)
+ num_txes += 1 + parsed_blocks[i].txes.size();
+ tx_cache_data.resize(num_txes);
+ size_t txidx = 0;
+ for (size_t i = 0; i < blocks.size(); ++i)
{
- std::vector<crypto::hash> round_block_hashes(threads);
- std::vector<cryptonote::block> round_blocks(threads);
- std::deque<bool> error(threads);
- size_t blocks_size = blocks.size();
- std::list<block_complete_entry>::const_iterator blocki = blocks.begin();
- for (size_t b = 0; b < blocks_size; b += threads)
+ THROW_WALLET_EXCEPTION_IF(parsed_blocks[i].txes.size() != parsed_blocks[i].block.tx_hashes.size(),
+ error::wallet_internal_error, "Mismatched parsed_blocks[i].txes.size() and parsed_blocks[i].block.tx_hashes.size()");
+ if (m_refresh_type != RefreshNoCoinbase)
+ tpool.submit(&waiter, [&, i, txidx](){ cache_tx_data(parsed_blocks[i].block.miner_tx, get_transaction_hash(parsed_blocks[i].block.miner_tx), tx_cache_data[txidx]); });
+ ++txidx;
+ for (size_t idx = 0; idx < parsed_blocks[i].txes.size(); ++idx)
{
- size_t round_size = std::min((size_t)threads, blocks_size - b);
- tools::threadpool::waiter waiter;
+ tpool.submit(&waiter, [&, i, idx, txidx](){ cache_tx_data(parsed_blocks[i].txes[idx], parsed_blocks[i].block.tx_hashes[idx], tx_cache_data[txidx]); });
+ ++txidx;
+ }
+ }
+ THROW_WALLET_EXCEPTION_IF(txidx != num_txes, error::wallet_internal_error, "txidx does not match tx_cache_data size");
+ waiter.wait(&tpool);
- std::list<block_complete_entry>::const_iterator tmpblocki = blocki;
- for (size_t i = 0; i < round_size; ++i)
- {
- tpool.submit(&waiter, boost::bind(&wallet2::parse_block_round, this, std::cref(tmpblocki->block),
- std::ref(round_blocks[i]), std::ref(round_block_hashes[i]), std::ref(error[i])));
- ++tmpblocki;
- }
- waiter.wait();
- tmpblocki = blocki;
- for (size_t i = 0; i < round_size; ++i)
- {
- THROW_WALLET_EXCEPTION_IF(error[i], error::block_parse_error, tmpblocki->block);
- ++tmpblocki;
- }
- for (size_t i = 0; i < round_size; ++i)
- {
- const crypto::hash &bl_id = round_block_hashes[i];
- cryptonote::block &bl = round_blocks[i];
+ hw::device &hwdev = m_account.get_device();
+ hw::reset_mode rst(hwdev);
+ hwdev.set_mode(hw::device::TRANSACTION_PARSE);
+ const cryptonote::account_keys &keys = m_account.get_keys();
- if(current_index >= m_blockchain.size())
- {
- process_new_blockchain_entry(bl, *blocki, bl_id, current_index, o_indices[b+i]);
- ++blocks_added;
- }
- else if(bl_id != m_blockchain[current_index])
- {
- //split detected here !!!
- THROW_WALLET_EXCEPTION_IF(current_index == start_height, error::wallet_internal_error,
- "wrong daemon response: split starts from the first block in response " + string_tools::pod_to_hex(bl_id) +
- " (height " + std::to_string(start_height) + "), local block id at this height: " +
- string_tools::pod_to_hex(m_blockchain[current_index]));
-
- detach_blockchain(current_index);
- process_new_blockchain_entry(bl, *blocki, bl_id, current_index, o_indices[b+i]);
- }
- else
+ auto gender = [&](wallet2::is_out_data &iod) {
+ boost::unique_lock<hw::device> hwdev_lock(hwdev);
+ if (!hwdev.generate_key_derivation(iod.pkey, keys.m_view_secret_key, iod.derivation))
+ {
+ MWARNING("Failed to generate key derivation from tx pubkey, skipping");
+ static_assert(sizeof(iod.derivation) == sizeof(rct::key), "Mismatched sizes of key_derivation and rct::key");
+ memcpy(&iod.derivation, rct::identity().bytes, sizeof(iod.derivation));
+ }
+ };
+
+ for (auto &slot: tx_cache_data)
+ {
+ for (auto &iod: slot.primary)
+ tpool.submit(&waiter, [&gender, &iod]() { gender(iod); }, true);
+ for (auto &iod: slot.additional)
+ tpool.submit(&waiter, [&gender, &iod]() { gender(iod); }, true);
+ }
+ waiter.wait(&tpool);
+
+ auto geniod = [&](const cryptonote::transaction &tx, size_t n_vouts, size_t txidx) {
+ for (size_t k = 0; k < n_vouts; ++k)
+ {
+ const auto &o = tx.vout[k];
+ if (o.target.type() == typeid(cryptonote::txout_to_key))
+ {
+ std::vector<crypto::key_derivation> additional_derivations;
+ for (const auto &iod: tx_cache_data[txidx].additional)
+ additional_derivations.push_back(iod.derivation);
+ const auto &key = boost::get<txout_to_key>(o.target).key;
+ for (size_t l = 0; l < tx_cache_data[txidx].primary.size(); ++l)
{
- LOG_PRINT_L2("Block is already in blockchain: " << string_tools::pod_to_hex(bl_id));
+ THROW_WALLET_EXCEPTION_IF(tx_cache_data[txidx].primary[l].received.size() != n_vouts,
+ error::wallet_internal_error, "Unexpected received array size");
+ tx_cache_data[txidx].primary[l].received[k] = is_out_to_acc_precomp(m_subaddresses, key, tx_cache_data[txidx].primary[l].derivation, additional_derivations, k, hwdev);
+ additional_derivations.clear();
}
- ++current_index;
- ++blocki;
}
}
- }
- else
+ };
+
+ txidx = 0;
+ for (size_t i = 0; i < blocks.size(); ++i)
{
- for(auto& bl_entry: blocks)
+ if (m_refresh_type != RefreshType::RefreshNoCoinbase)
+ {
+ THROW_WALLET_EXCEPTION_IF(txidx >= tx_cache_data.size(), error::wallet_internal_error, "txidx out of range");
+ const size_t n_vouts = m_refresh_type == RefreshType::RefreshOptimizeCoinbase ? 1 : parsed_blocks[i].block.miner_tx.vout.size();
+ tpool.submit(&waiter, [&, i, txidx](){ geniod(parsed_blocks[i].block.miner_tx, n_vouts, txidx); }, true);
+ }
+ ++txidx;
+ for (size_t j = 0; j < parsed_blocks[i].txes.size(); ++j)
+ {
+ THROW_WALLET_EXCEPTION_IF(txidx >= tx_cache_data.size(), error::wallet_internal_error, "txidx out of range");
+ tpool.submit(&waiter, [&, i, j, txidx](){ geniod(parsed_blocks[i].txes[j], parsed_blocks[i].txes[j].vout.size(), txidx); }, true);
+ ++txidx;
+ }
+ }
+ THROW_WALLET_EXCEPTION_IF(txidx != tx_cache_data.size(), error::wallet_internal_error, "txidx did not reach expected value");
+ waiter.wait(&tpool);
+ hwdev.set_mode(hw::device::NONE);
+
+ size_t tx_cache_data_offset = 0;
+ for (size_t i = 0; i < blocks.size(); ++i)
{
- cryptonote::block bl;
- bool r = cryptonote::parse_and_validate_block_from_blob(bl_entry.block, bl);
- THROW_WALLET_EXCEPTION_IF(!r, error::block_parse_error, bl_entry.block);
+ const crypto::hash &bl_id = parsed_blocks[i].hash;
+ const cryptonote::block &bl = parsed_blocks[i].block;
- crypto::hash bl_id = get_block_hash(bl);
if(current_index >= m_blockchain.size())
{
- process_new_blockchain_entry(bl, bl_entry, bl_id, current_index, o_indices[tx_o_indices_idx]);
+ process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset);
++blocks_added;
}
else if(bl_id != m_blockchain[current_index])
@@ -1800,32 +1936,30 @@ void wallet2::process_blocks(uint64_t start_height, const std::list<cryptonote::
string_tools::pod_to_hex(m_blockchain[current_index]));
detach_blockchain(current_index);
- process_new_blockchain_entry(bl, bl_entry, bl_id, current_index, o_indices[tx_o_indices_idx]);
+ process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset);
}
else
{
LOG_PRINT_L2("Block is already in blockchain: " << string_tools::pod_to_hex(bl_id));
}
-
++current_index;
- ++tx_o_indices_idx;
- }
+ tx_cache_data_offset += 1 + parsed_blocks[i].txes.size();
}
}
//----------------------------------------------------------------------------------------------------
-void wallet2::refresh()
+void wallet2::refresh(bool trusted_daemon)
{
uint64_t blocks_fetched = 0;
- refresh(0, blocks_fetched);
+ refresh(trusted_daemon, 0, blocks_fetched);
}
//----------------------------------------------------------------------------------------------------
-void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched)
+void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched)
{
bool received_money = false;
- refresh(start_height, blocks_fetched, received_money);
+ refresh(trusted_daemon, start_height, blocks_fetched, received_money);
}
//----------------------------------------------------------------------------------------------------
-void wallet2::pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, const std::list<cryptonote::block_complete_entry> &prev_blocks, std::list<cryptonote::block_complete_entry> &blocks, std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices, bool &error)
+void wallet2::pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, const std::vector<cryptonote::block_complete_entry> &prev_blocks, const std::vector<parsed_block> &prev_parsed_blocks, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<parsed_block> &parsed_blocks, bool &error)
{
error = false;
@@ -1834,18 +1968,53 @@ void wallet2::pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_hei
drop_from_short_history(short_chain_history, 3);
// prepend the last 3 blocks, should be enough to guard against a block or two's reorg
- cryptonote::block bl;
- std::list<cryptonote::block_complete_entry>::const_reverse_iterator i = prev_blocks.rbegin();
+ std::vector<parsed_block>::const_reverse_iterator i = prev_parsed_blocks.rbegin();
for (size_t n = 0; n < std::min((size_t)3, prev_blocks.size()); ++n)
{
- bool ok = cryptonote::parse_and_validate_block_from_blob(i->block, bl);
- THROW_WALLET_EXCEPTION_IF(!ok, error::block_parse_error, i->block);
- short_chain_history.push_front(cryptonote::get_block_hash(bl));
+ short_chain_history.push_front(i->hash);
++i;
}
// pull the new blocks
+ std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> o_indices;
pull_blocks(start_height, blocks_start_height, short_chain_history, blocks, o_indices);
+ THROW_WALLET_EXCEPTION_IF(blocks.size() != o_indices.size(), error::wallet_internal_error, "Mismatched sizes of blocks and o_indices");
+
+ tools::threadpool& tpool = tools::threadpool::getInstance();
+ tools::threadpool::waiter waiter;
+ parsed_blocks.resize(blocks.size());
+ for (size_t i = 0; i < blocks.size(); ++i)
+ {
+ tpool.submit(&waiter, boost::bind(&wallet2::parse_block_round, this, std::cref(blocks[i].block),
+ std::ref(parsed_blocks[i].block), std::ref(parsed_blocks[i].hash), std::ref(parsed_blocks[i].error)), true);
+ }
+ waiter.wait(&tpool);
+ for (size_t i = 0; i < blocks.size(); ++i)
+ {
+ if (parsed_blocks[i].error)
+ {
+ error = true;
+ break;
+ }
+ parsed_blocks[i].o_indices = std::move(o_indices[i]);
+ }
+
+ boost::mutex error_lock;
+ for (size_t i = 0; i < blocks.size(); ++i)
+ {
+ parsed_blocks[i].txes.resize(blocks[i].txs.size());
+ for (size_t j = 0; j < blocks[i].txs.size(); ++j)
+ {
+ tpool.submit(&waiter, [&, i, j](){
+ if (!parse_and_validate_tx_base_from_blob(blocks[i].txs[j], parsed_blocks[i].txes[j]))
+ {
+ boost::unique_lock<boost::mutex> lock(error_lock);
+ error = true;
+ }
+ }, true);
+ }
+ }
+ waiter.wait(&tpool);
}
catch(...)
{
@@ -2034,6 +2203,7 @@ void wallet2::update_pool_state(bool refreshed)
req.txs_hashes.push_back(epee::string_tools::pod_to_hex(p.first));
MDEBUG("asking for " << txids.size() << " transactions");
req.decode_as_json = false;
+ req.prune = false;
m_daemon_rpc_mutex.lock();
bool r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
@@ -2057,7 +2227,7 @@ void wallet2::update_pool_state(bool refreshed)
[tx_hash](const std::pair<crypto::hash, bool> &e) { return e.first == tx_hash; });
if (i != txids.end())
{
- process_new_transaction(tx_hash, tx, std::vector<uint64_t>(), 0, time(NULL), false, true, tx_entry.double_spend_seen);
+ process_new_transaction(tx_hash, tx, std::vector<uint64_t>(), 0, time(NULL), false, true, tx_entry.double_spend_seen, {});
m_scanned_pool_txs[0].insert(tx_hash);
if (m_scanned_pool_txs[0].size() > 5000)
{
@@ -2101,7 +2271,7 @@ void wallet2::update_pool_state(bool refreshed)
//----------------------------------------------------------------------------------------------------
void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history)
{
- std::list<crypto::hash> hashes;
+ std::vector<crypto::hash> hashes;
const uint64_t checkpoint_height = m_checkpoints.get_max_height();
if (stop_height > checkpoint_height && m_blockchain.size()-1 < checkpoint_height)
@@ -2111,7 +2281,6 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height,
while (missing_blocks-- > 0)
m_blockchain.push_back(crypto::null_hash); // maybe a bit suboptimal, but deque won't do huge reallocs like vector
m_blockchain.push_back(m_checkpoints.get_points().at(checkpoint_height));
- m_local_bc_height = m_blockchain.size();
short_chain_history.clear();
get_short_chain_history(short_chain_history);
}
@@ -2129,7 +2298,7 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height,
}
if (hashes.size() + current_index < stop_height) {
drop_from_short_history(short_chain_history, 3);
- std::list<crypto::hash>::iterator right = hashes.end();
+ std::vector<crypto::hash>::iterator right = hashes.end();
// prepend 3 more
for (int i = 0; i<3; i++) {
right--;
@@ -2141,10 +2310,9 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height,
{
if(current_index >= m_blockchain.size())
{
- if (!(current_index % 1000))
+ if (!(current_index % 1024))
LOG_PRINT_L2( "Skipped block by height: " << current_index);
m_blockchain.push_back(bl_id);
- ++m_local_bc_height;
if (0 != m_callback)
{ // FIXME: this isn't right, but simplewallet just logs that we got a block.
@@ -2190,8 +2358,10 @@ bool wallet2::delete_address_book_row(std::size_t row_id) {
}
//----------------------------------------------------------------------------------------------------
-void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& received_money)
+void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched, bool& received_money)
{
+ key_ref kref(*this);
+
if(m_light_wallet) {
// MyMonero get_address_info needs to be called occasionally to trigger wallet sync.
@@ -2205,7 +2375,6 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
// Update lw heights
m_light_wallet_scanned_block_height = res.scanned_block_height;
m_light_wallet_blockchain_height = res.blockchain_height;
- m_local_bc_height = res.blockchain_height;
// If new height - call new_block callback
if(m_light_wallet_blockchain_height != prev_height)
{
@@ -2234,12 +2403,12 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
tools::threadpool& tpool = tools::threadpool::getInstance();
tools::threadpool::waiter waiter;
uint64_t blocks_start_height;
- std::list<cryptonote::block_complete_entry> blocks;
- std::vector<COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> o_indices;
+ std::vector<cryptonote::block_complete_entry> blocks;
+ std::vector<parsed_block> parsed_blocks;
bool refreshed = false;
// pull the first set of blocks
- get_short_chain_history(short_chain_history);
+ get_short_chain_history(short_chain_history, (m_first_refresh_done || trusted_daemon) ? 1 : FIRST_REFRESH_GRANULARITY);
m_run.store(true, std::memory_order_relaxed);
if (start_height > m_blockchain.size() || m_refresh_from_block_height > m_blockchain.size()) {
if (!start_height)
@@ -2248,7 +2417,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
fast_refresh(start_height, blocks_start_height, short_chain_history);
// regenerate the history now that we've got a full set of hashes
short_chain_history.clear();
- get_short_chain_history(short_chain_history);
+ get_short_chain_history(short_chain_history, (m_first_refresh_done || trusted_daemon) ? 1 : FIRST_REFRESH_GRANULARITY);
start_height = 0;
// and then fall through to regular refresh processing
}
@@ -2256,31 +2425,34 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
// If stop() is called during fast refresh we don't need to continue
if(!m_run.load(std::memory_order_relaxed))
return;
- pull_blocks(start_height, blocks_start_height, short_chain_history, blocks, o_indices);
// always reset start_height to 0 to force short_chain_ history to be used on
// subsequent pulls in this refresh.
start_height = 0;
+ bool first = true;
while(m_run.load(std::memory_order_relaxed))
{
try
{
// pull the next set of blocks while we're processing the current one
uint64_t next_blocks_start_height;
- std::list<cryptonote::block_complete_entry> next_blocks;
- std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> next_o_indices;
+ std::vector<cryptonote::block_complete_entry> next_blocks;
+ std::vector<parsed_block> next_parsed_blocks;
bool error = false;
- if (blocks.empty())
+ if (!first && blocks.empty())
{
refreshed = false;
break;
}
- tpool.submit(&waiter, [&]{pull_next_blocks(start_height, next_blocks_start_height, short_chain_history, blocks, next_blocks, next_o_indices, error);});
+ tpool.submit(&waiter, [&]{pull_and_parse_next_blocks(start_height, next_blocks_start_height, short_chain_history, blocks, parsed_blocks, next_blocks, next_parsed_blocks, error);});
- process_blocks(blocks_start_height, blocks, o_indices, added_blocks);
- blocks_fetched += added_blocks;
- waiter.wait();
- if(blocks_start_height == next_blocks_start_height)
+ if (!first)
+ {
+ process_parsed_blocks(blocks_start_height, blocks, parsed_blocks, added_blocks);
+ blocks_fetched += added_blocks;
+ }
+ waiter.wait(&tpool);
+ if(!first && blocks_start_height == next_blocks_start_height)
{
m_node_rpc_proxy.set_height(m_blockchain.size());
refreshed = true;
@@ -2289,8 +2461,9 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
// switch to the new blocks from the daemon
blocks_start_height = next_blocks_start_height;
- blocks = next_blocks;
- o_indices = next_o_indices;
+ blocks = std::move(next_blocks);
+ parsed_blocks = std::move(next_parsed_blocks);
+ first = false;
// handle error from async fetching thread
if (error)
@@ -2301,7 +2474,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
catch (const std::exception&)
{
blocks_fetched += added_blocks;
- waiter.wait();
+ waiter.wait(&tpool);
if(try_count < 3)
{
LOG_PRINT_L1("Another try pull_blocks (try_count=" << try_count << ")...");
@@ -2328,14 +2501,16 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
LOG_PRINT_L1("Failed to check pending transactions");
}
+ m_first_refresh_done = true;
+
LOG_PRINT_L1("Refresh done, blocks received: " << blocks_fetched << ", balance (all accounts): " << print_money(balance_all()) << ", unlocked: " << print_money(unlocked_balance_all()));
}
//----------------------------------------------------------------------------------------------------
-bool wallet2::refresh(uint64_t & blocks_fetched, bool& received_money, bool& ok)
+bool wallet2::refresh(bool trusted_daemon, uint64_t & blocks_fetched, bool& received_money, bool& ok)
{
try
{
- refresh(0, blocks_fetched, received_money);
+ refresh(trusted_daemon, 0, blocks_fetched, received_money);
ok = true;
}
catch (...)
@@ -2419,7 +2594,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;
@@ -2456,7 +2631,6 @@ void wallet2::detach_blockchain(uint64_t height)
size_t blocks_detached = m_blockchain.size() - height;
m_blockchain.crop(height);
- m_local_bc_height -= blocks_detached;
for (auto it = m_payments.begin(); it != m_payments.end(); )
{
@@ -2480,6 +2654,7 @@ void wallet2::detach_blockchain(uint64_t height)
bool wallet2::deinit()
{
m_is_initialized=false;
+ m_keys_file_locker.reset();
return true;
}
//----------------------------------------------------------------------------------------------------
@@ -2498,7 +2673,6 @@ bool wallet2::clear()
m_scanned_pool_txs[0].clear();
m_scanned_pool_txs[1].clear();
m_address_book.clear();
- m_local_bc_height = 1;
m_subaddresses.clear();
m_subaddress_labels.clear();
return true;
@@ -2584,6 +2758,9 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
value2.SetInt(m_confirm_missing_payment_id ? 1 :0);
json.AddMember("confirm_missing_payment_id", value2, json.GetAllocator());
+ value2.SetInt(m_confirm_non_default_ring_size ? 1 :0);
+ json.AddMember("confirm_non_default_ring_size", value2, json.GetAllocator());
+
value2.SetInt(m_ask_password ? 1 :0);
json.AddMember("ask_password", value2, json.GetAllocator());
@@ -2623,6 +2800,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);
@@ -2638,10 +2821,12 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
crypto::chacha20(account_data.data(), account_data.size(), key, keys_file_data.iv, &cipher[0]);
keys_file_data.account_data = cipher;
+ m_keys_file_locker.reset();
std::string buf;
r = ::serialization::dump_binary(keys_file_data, buf);
r = r && epee::file_io_utils::save_string_to_file(keys_file_name, buf); //and never touch wallet_keys_file again, only read
CHECK_AND_ASSERT_MES(r, false, "failed to generate wallet keys file " << keys_file_name);
+ m_keys_file_locker.reset(new tools::file_locker(m_keys_file));
return true;
}
@@ -2685,6 +2870,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_auto_refresh = true;
m_refresh_type = RefreshType::RefreshDefault;
m_confirm_missing_payment_id = true;
+ m_confirm_non_default_ring_size = true;
m_ask_password = true;
m_min_output_count = 0;
m_min_output_value = 0;
@@ -2696,6 +2882,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())
@@ -2788,6 +2976,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_refresh_from_block_height = field_refresh_height;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_missing_payment_id, int, Int, false, true);
m_confirm_missing_payment_id = field_confirm_missing_payment_id;
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_non_default_ring_size, int, Int, false, true);
+ m_confirm_non_default_ring_size = field_confirm_non_default_ring_size;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ask_password, int, Int, false, true);
m_ask_password = field_ask_password;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_decimal_point, int, Int, false, CRYPTONOTE_DISPLAY_DECIMAL_POINT);
@@ -2818,6 +3008,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
{
@@ -2863,6 +3057,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
@@ -2913,9 +3108,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)
@@ -3000,6 +3196,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
cryptonote::block b;
generate_genesis(b);
m_blockchain.push_back(get_block_hash(b));
+ m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
add_subaddress_account(tr("Primary account"));
if (!wallet_.empty())
@@ -3008,12 +3205,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)
@@ -3057,6 +3255,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip
cryptonote::block b;
generate_genesis(b);
m_blockchain.push_back(get_block_hash(b));
+ m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
add_subaddress_account(tr("Primary account"));
if (!wallet_.empty())
@@ -3109,9 +3308,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,
@@ -3150,6 +3351,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
cryptonote::block b;
generate_genesis(b);
m_blockchain.push_back(get_block_hash(b));
+ m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
add_subaddress_account(tr("Primary account"));
if (!wallet_.empty())
@@ -3158,10 +3360,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,
@@ -3200,6 +3404,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
cryptonote::block b;
generate_genesis(b);
m_blockchain.push_back(get_block_hash(b));
+ m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
add_subaddress_account(tr("Primary account"));
if (!wallet_.empty())
@@ -3240,6 +3445,13 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p
cryptonote::block b;
generate_genesis(b);
m_blockchain.push_back(get_block_hash(b));
+ if (m_subaddress_lookahead_major == SUBADDRESS_LOOKAHEAD_MAJOR && m_subaddress_lookahead_minor == SUBADDRESS_LOOKAHEAD_MINOR)
+ {
+ // the default lookahead setting (50:200) is clearly too much for hardware wallet
+ m_subaddress_lookahead_major = 5;
+ m_subaddress_lookahead_minor = 20;
+ }
+ m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
add_subaddress_account(tr("Primary account"));
if (!wallet_.empty()) {
store();
@@ -3336,6 +3548,7 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
cryptonote::block b;
generate_genesis(b);
m_blockchain.push_back(get_block_hash(b));
+ m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
add_subaddress_account(tr("Primary account"));
if (!m_wallet_file.empty())
@@ -3591,6 +3804,14 @@ bool wallet2::has_multisig_partial_key_images() const
return false;
}
+bool wallet2::has_unknown_key_images() const
+{
+ for (const auto &td: m_transfers)
+ if (!td.m_key_image_known)
+ return true;
+ return false;
+}
+
/*!
* \brief Rewrites to the wallet file for wallet upgrade (doesn't generate key, assumes it's already there)
* \param wallet_name Name of wallet file (should exist)
@@ -3608,8 +3829,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)
{
@@ -3734,12 +3956,17 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
boost::system::error_code e;
bool exists = boost::filesystem::exists(m_keys_file, e);
THROW_WALLET_EXCEPTION_IF(e || !exists, error::file_not_found, m_keys_file);
+ m_keys_file_locker.reset(new tools::file_locker(m_keys_file));
+ THROW_WALLET_EXCEPTION_IF(!m_keys_file_locker->locked(), error::wallet_internal_error, "internal error: \"" + m_keys_file + "\" is opened by another wallet program");
+ // this temporary unlocking is necessary for Windows (otherwise the file couldn't be loaded).
+ m_keys_file_locker.reset();
if (!load_keys(m_keys_file, password))
{
THROW_WALLET_EXCEPTION_IF(true, error::file_read_error, m_keys_file);
}
LOG_PRINT_L0("Loaded wallet keys file, with public address: " << m_account.get_public_address_str(m_nettype));
+ m_keys_file_locker.reset(new tools::file_locker(m_keys_file));
//keys loaded ok!
//try to load wallet file. but even if we failed, it is not big problem
@@ -3752,7 +3979,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
{
wallet2::cache_file_data cache_file_data;
std::string buf;
- bool r = epee::file_io_utils::load_file_to_string(m_wallet_file, buf);
+ bool r = epee::file_io_utils::load_file_to_string(m_wallet_file, buf, std::numeric_limits<size_t>::max());
THROW_WALLET_EXCEPTION_IF(!r, error::file_read_error, m_wallet_file);
// try to read it as an encrypted cache
@@ -3829,6 +4056,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
if (m_blockchain.empty())
{
m_blockchain.push_back(genesis_hash);
+ m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx);
}
else
{
@@ -3840,8 +4068,6 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
if (get_num_subaddress_accounts() == 0)
add_subaddress_account(tr("Primary account"));
- m_local_bc_height = m_blockchain.size();
-
try
{
find_and_save_rings(false);
@@ -3855,6 +4081,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");
@@ -4215,11 +4446,11 @@ void wallet2::rescan_blockchain(bool refresh)
generate_genesis(genesis);
crypto::hash genesis_hash = get_block_hash(genesis);
m_blockchain.push_back(genesis_hash);
+ m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx);
add_subaddress_account(tr("Primary account"));
- m_local_bc_height = 1;
if (refresh)
- this->refresh();
+ this->refresh(false);
}
//----------------------------------------------------------------------------------------------------
bool wallet2::is_transfer_unlocked(const transfer_details& td) const
@@ -4232,7 +4463,7 @@ bool wallet2::is_transfer_unlocked(uint64_t unlock_time, uint64_t block_height)
if(!is_tx_spendtime_unlocked(unlock_time, block_height))
return false;
- if(block_height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE > m_local_bc_height)
+ if(block_height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE > get_blockchain_current_height())
return false;
return true;
@@ -4243,7 +4474,7 @@ bool wallet2::is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t block_heig
if(unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER)
{
//interpret as block index
- if(m_local_bc_height-1 + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS >= unlock_time)
+ if(get_blockchain_current_height()-1 + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS >= unlock_time)
return true;
else
return false;
@@ -4628,6 +4859,15 @@ void wallet2::commit_tx(std::vector<pending_tx>& ptx_vector)
bool wallet2::save_tx(const std::vector<pending_tx>& ptx_vector, const std::string &filename) const
{
LOG_PRINT_L0("saving " << ptx_vector.size() << " transactions");
+ std::string ciphertext = dump_tx_to_str(ptx_vector);
+ if (ciphertext.empty())
+ return false;
+ return epee::file_io_utils::save_string_to_file(filename, ciphertext);
+}
+//----------------------------------------------------------------------------------------------------
+std::string wallet2::dump_tx_to_str(const std::vector<pending_tx> &ptx_vector) const
+{
+ LOG_PRINT_L0("saving " << ptx_vector.size() << " transactions");
unsigned_tx_set txs;
for (auto &tx: ptx_vector)
{
@@ -4647,11 +4887,11 @@ bool wallet2::save_tx(const std::vector<pending_tx>& ptx_vector, const std::stri
}
catch (...)
{
- return false;
+ return std::string();
}
LOG_PRINT_L2("Saving unsigned tx data: " << oss.str());
std::string ciphertext = encrypt_with_view_secret_key(oss.str());
- return epee::file_io_utils::save_string_to_file(filename, std::string(UNSIGNED_TX_PREFIX) + ciphertext);
+ return std::string(UNSIGNED_TX_PREFIX) + ciphertext;
}
//----------------------------------------------------------------------------------------------------
bool wallet2::load_unsigned_tx(const std::string &unsigned_filename, unsigned_tx_set &exported_txs) const
@@ -4669,10 +4909,17 @@ bool wallet2::load_unsigned_tx(const std::string &unsigned_filename, unsigned_tx
LOG_PRINT_L0("Failed to load from " << unsigned_filename);
return false;
}
+
+ return parse_unsigned_tx_from_str(s, exported_txs);
+}
+//----------------------------------------------------------------------------------------------------
+bool wallet2::parse_unsigned_tx_from_str(const std::string &unsigned_tx_st, unsigned_tx_set &exported_txs) const
+{
+ std::string s = unsigned_tx_st;
const size_t magiclen = strlen(UNSIGNED_TX_PREFIX) - 1;
if (strncmp(s.c_str(), UNSIGNED_TX_PREFIX, magiclen))
{
- LOG_PRINT_L0("Bad magic from " << unsigned_filename);
+ LOG_PRINT_L0("Bad magic from unsigned tx");
return false;
}
s = s.substr(magiclen);
@@ -4688,7 +4935,7 @@ bool wallet2::load_unsigned_tx(const std::string &unsigned_filename, unsigned_tx
}
catch (...)
{
- LOG_PRINT_L0("Failed to parse data from " << unsigned_filename);
+ LOG_PRINT_L0("Failed to parse data from unsigned tx");
return false;
}
}
@@ -4705,19 +4952,19 @@ bool wallet2::load_unsigned_tx(const std::string &unsigned_filename, unsigned_tx
}
catch (...)
{
- LOG_PRINT_L0("Failed to parse data from " << unsigned_filename);
+ LOG_PRINT_L0("Failed to parse data from unsigned tx");
return false;
}
}
catch (const std::exception &e)
{
- LOG_PRINT_L0("Failed to decrypt " << unsigned_filename << ": " << e.what());
+ LOG_PRINT_L0("Failed to decrypt unsigned tx: " << e.what());
return false;
}
}
else
{
- LOG_PRINT_L0("Unsupported version in " << unsigned_filename);
+ LOG_PRINT_L0("Unsupported version in unsigned tx");
return false;
}
LOG_PRINT_L1("Loaded tx unsigned data from binary: " << exported_txs.txes.size() << " transactions");
@@ -4738,14 +4985,12 @@ bool wallet2::sign_tx(const std::string &unsigned_filename, const std::string &s
}
return sign_tx(exported_txs, signed_filename, txs, export_raw);
}
-
//----------------------------------------------------------------------------------------------------
-bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_filename, std::vector<wallet2::pending_tx> &txs, bool export_raw)
+bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pending_tx> &txs, signed_tx_set &signed_txes)
{
import_outputs(exported_txs.transfers);
// sign the transactions
- signed_tx_set signed_txes;
for (size_t n = 0; n < exported_txs.txes.size(); ++n)
{
tools::wallet2::tx_construction_data &sd = exported_txs.txes[n];
@@ -4753,11 +4998,10 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_f
LOG_PRINT_L1(" " << (n+1) << ": " << sd.sources.size() << " inputs, ring size " << sd.sources[0].outputs.size());
signed_txes.ptx.push_back(pending_tx());
tools::wallet2::pending_tx &ptx = signed_txes.ptx.back();
- bool bulletproof = sd.use_rct && !ptx.tx.rct_signatures.p.bulletproofs.empty();
crypto::secret_key tx_key;
std::vector<crypto::secret_key> additional_tx_keys;
rct::multisig_out msout;
- bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sd.sources, sd.splitted_dsts, sd.change_dts.addr, sd.extra, ptx.tx, sd.unlock_time, tx_key, additional_tx_keys, sd.use_rct, bulletproof, m_multisig ? &msout : NULL);
+ bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sd.sources, sd.splitted_dsts, sd.change_dts.addr, sd.extra, ptx.tx, sd.unlock_time, tx_key, additional_tx_keys, sd.use_rct, sd.use_bulletproofs, m_multisig ? &msout : NULL);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_nettype);
// we don't test tx size, because we don't know the current limit, due to not having a blockchain,
// and it's a bit pointless to fail there anyway, since it'd be a (good) guess only. We sign anyway,
@@ -4807,20 +5051,21 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_f
signed_txes.key_images[i] = m_transfers[i].m_key_image;
}
- // save as binary
- std::ostringstream oss;
- boost::archive::portable_binary_oarchive ar(oss);
- try
- {
- ar << signed_txes;
- }
- catch(...)
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
+bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_filename, std::vector<wallet2::pending_tx> &txs, bool export_raw)
+{
+ // sign the transactions
+ signed_tx_set signed_txes;
+ std::string ciphertext = sign_tx_dump_to_str(exported_txs, txs, signed_txes);
+ if (ciphertext.empty())
{
+ LOG_PRINT_L0("Failed to sign unsigned_tx_set");
return false;
}
- LOG_PRINT_L3("Saving signed tx data (with encryption): " << oss.str());
- std::string ciphertext = encrypt_with_view_secret_key(oss.str());
- if (!epee::file_io_utils::save_string_to_file(signed_filename, std::string(SIGNED_TX_PREFIX) + ciphertext))
+
+ if (!epee::file_io_utils::save_string_to_file(signed_filename, ciphertext))
{
LOG_PRINT_L0("Failed to save file to " << signed_filename);
return false;
@@ -4842,6 +5087,32 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_f
return true;
}
//----------------------------------------------------------------------------------------------------
+std::string wallet2::sign_tx_dump_to_str(unsigned_tx_set &exported_txs, std::vector<wallet2::pending_tx> &ptx, signed_tx_set &signed_txes)
+{
+ // sign the transactions
+ bool r = sign_tx(exported_txs, ptx, signed_txes);
+ if (!r)
+ {
+ LOG_PRINT_L0("Failed to sign unsigned_tx_set");
+ return std::string();
+ }
+
+ // save as binary
+ std::ostringstream oss;
+ boost::archive::portable_binary_oarchive ar(oss);
+ try
+ {
+ ar << signed_txes;
+ }
+ catch(...)
+ {
+ return std::string();
+ }
+ LOG_PRINT_L3("Saving signed tx data (with encryption): " << oss.str());
+ std::string ciphertext = encrypt_with_view_secret_key(oss.str());
+ return std::string(SIGNED_TX_PREFIX) + ciphertext;
+}
+//----------------------------------------------------------------------------------------------------
bool wallet2::load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func)
{
std::string s;
@@ -4859,10 +5130,20 @@ bool wallet2::load_tx(const std::string &signed_filename, std::vector<tools::wal
LOG_PRINT_L0("Failed to load from " << signed_filename);
return false;
}
+
+ return parse_tx_from_str(s, ptx, accept_func);
+}
+//----------------------------------------------------------------------------------------------------
+bool wallet2::parse_tx_from_str(const std::string &signed_tx_st, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set &)> accept_func)
+{
+ std::string s = signed_tx_st;
+ boost::system::error_code errcode;
+ signed_tx_set signed_txs;
+
const size_t magiclen = strlen(SIGNED_TX_PREFIX) - 1;
if (strncmp(s.c_str(), SIGNED_TX_PREFIX, magiclen))
{
- LOG_PRINT_L0("Bad magic from " << signed_filename);
+ LOG_PRINT_L0("Bad magic from signed transaction");
return false;
}
s = s.substr(magiclen);
@@ -4878,7 +5159,7 @@ bool wallet2::load_tx(const std::string &signed_filename, std::vector<tools::wal
}
catch (...)
{
- LOG_PRINT_L0("Failed to parse data from " << signed_filename);
+ LOG_PRINT_L0("Failed to parse data from signed transaction");
return false;
}
}
@@ -4895,23 +5176,23 @@ bool wallet2::load_tx(const std::string &signed_filename, std::vector<tools::wal
}
catch (...)
{
- LOG_PRINT_L0("Failed to parse decrypted data from " << signed_filename);
+ LOG_PRINT_L0("Failed to parse decrypted data from signed transaction");
return false;
}
}
catch (const std::exception &e)
{
- LOG_PRINT_L0("Failed to decrypt " << signed_filename << ": " << e.what());
+ LOG_PRINT_L0("Failed to decrypt signed transaction: " << e.what());
return false;
}
}
else
{
- LOG_PRINT_L0("Unsupported version in " << signed_filename);
+ LOG_PRINT_L0("Unsupported version in signed transaction");
return false;
}
LOG_PRINT_L0("Loaded signed tx data from binary: " << signed_txs.ptx.size() << " transactions");
- for (auto &ptx: signed_txs.ptx) LOG_PRINT_L0(cryptonote::obj_to_json_str(ptx.tx));
+ for (auto &c_ptx: signed_txs.ptx) LOG_PRINT_L0(cryptonote::obj_to_json_str(c_ptx.tx));
if (accept_func && !accept_func(signed_txs))
{
@@ -4988,7 +5269,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;
@@ -5000,8 +5281,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)
@@ -5129,8 +5414,7 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto
cryptonote::transaction tx;
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, sd.use_bulletproofs, &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),
@@ -5523,36 +5807,52 @@ bool wallet2::set_ring_database(const std::string &filename)
return true;
}
+crypto::chacha_key wallet2::get_ringdb_key()
+{
+ if (!m_ringdb_key)
+ {
+ MINFO("caching ringdb key");
+ crypto::chacha_key key;
+ generate_chacha_key_from_secret_keys(key);
+ m_ringdb_key = key;
+ }
+ return *m_ringdb_key;
+}
+
+void wallet2::clear_ringdb_key()
+{
+ MINFO("clearing ringdb key");
+ m_ringdb_key = boost::none;
+}
+
bool wallet2::add_rings(const crypto::chacha_key &key, const cryptonote::transaction_prefix &tx)
{
if (!m_ringdb)
- return true;
+ return false;
try { return m_ringdb->add_rings(key, tx); }
catch (const std::exception &e) { return false; }
}
bool wallet2::add_rings(const cryptonote::transaction_prefix &tx)
{
- crypto::chacha_key key;
- generate_chacha_key_from_secret_keys(key);
- try { return add_rings(key, tx); }
+ key_ref kref(*this);
+ try { return add_rings(get_ringdb_key(), tx); }
catch (const std::exception &e) { return false; }
}
bool wallet2::remove_rings(const cryptonote::transaction_prefix &tx)
{
if (!m_ringdb)
- return true;
- crypto::chacha_key key;
- generate_chacha_key_from_secret_keys(key);
- try { return m_ringdb->remove_rings(key, tx); }
+ return false;
+ key_ref kref(*this);
+ try { return m_ringdb->remove_rings(get_ringdb_key(), tx); }
catch (const std::exception &e) { return false; }
}
bool wallet2::get_ring(const crypto::chacha_key &key, const crypto::key_image &key_image, std::vector<uint64_t> &outs)
{
if (!m_ringdb)
- return true;
+ return false;
try { return m_ringdb->get_ring(key, key_image, outs); }
catch (const std::exception &e) { return false; }
}
@@ -5582,22 +5882,18 @@ bool wallet2::get_rings(const crypto::hash &txid, std::vector<std::pair<crypto::
bool wallet2::get_ring(const crypto::key_image &key_image, std::vector<uint64_t> &outs)
{
- crypto::chacha_key key;
- generate_chacha_key_from_secret_keys(key);
-
- try { return get_ring(key, key_image, outs); }
+ key_ref kref(*this);
+ try { return get_ring(get_ringdb_key(), key_image, outs); }
catch (const std::exception &e) { return false; }
}
bool wallet2::set_ring(const crypto::key_image &key_image, const std::vector<uint64_t> &outs, bool relative)
{
if (!m_ringdb)
- return true;
-
- crypto::chacha_key key;
- generate_chacha_key_from_secret_keys(key);
+ return false;
- try { return m_ringdb->set_ring(key, key_image, outs, relative); }
+ key_ref kref(*this);
+ try { return m_ringdb->set_ring(get_ringdb_key(), key_image, outs, relative); }
catch (const std::exception &e) { return false; }
}
@@ -5606,47 +5902,55 @@ bool wallet2::find_and_save_rings(bool force)
if (!force && m_ring_history_saved)
return true;
if (!m_ringdb)
- return true;
+ return false;
+ key_ref kref(*this);
COMMAND_RPC_GET_TRANSACTIONS::request req = AUTO_VAL_INIT(req);
COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res);
MDEBUG("Finding and saving rings...");
// get payments we made
+ std::vector<crypto::hash> txs_hashes;
std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>> payments;
get_payments_out(payments, 0, std::numeric_limits<uint64_t>::max(), boost::none, std::set<uint32_t>());
for (const std::pair<crypto::hash,wallet2::confirmed_transfer_details> &entry: payments)
{
const crypto::hash &txid = entry.first;
- req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
+ txs_hashes.push_back(txid);
}
- MDEBUG("Found " << std::to_string(req.txs_hashes.size()) << " transactions");
+ MDEBUG("Found " << std::to_string(txs_hashes.size()) << " transactions");
// get those transactions from the daemon
- req.decode_as_json = false;
- bool r;
+ static const size_t SLICE_SIZE = 200;
+ for (size_t slice = 0; slice < txs_hashes.size(); slice += SLICE_SIZE)
{
- const boost::lock_guard<boost::mutex> lock{m_daemon_rpc_mutex};
- r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout);
- }
- THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gettransactions");
- THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions");
- THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::wallet_internal_error, "gettransactions");
- THROW_WALLET_EXCEPTION_IF(res.txs.size() != req.txs_hashes.size(), error::wallet_internal_error,
- "daemon returned wrong response for gettransactions, wrong txs count = " +
- std::to_string(res.txs.size()) + ", expected " + std::to_string(req.txs_hashes.size()));
-
- MDEBUG("Scanning " << res.txs.size() << " transactions");
-
- crypto::chacha_key key;
- generate_chacha_key_from_secret_keys(key);
+ req.decode_as_json = false;
+ req.prune = false;
+ req.txs_hashes.clear();
+ size_t ntxes = slice + SLICE_SIZE > txs_hashes.size() ? txs_hashes.size() - slice : SLICE_SIZE;
+ for (size_t s = slice; s < slice + ntxes; ++s)
+ req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txs_hashes[s]));
+ bool r;
+ {
+ const boost::lock_guard<boost::mutex> lock{m_daemon_rpc_mutex};
+ r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout);
+ }
+ THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gettransactions");
+ THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions");
+ THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::wallet_internal_error, "gettransactions");
+ THROW_WALLET_EXCEPTION_IF(res.txs.size() != req.txs_hashes.size(), error::wallet_internal_error,
+ "daemon returned wrong response for gettransactions, wrong txs count = " +
+ std::to_string(res.txs.size()) + ", expected " + std::to_string(req.txs_hashes.size()));
- auto it = req.txs_hashes.begin();
- for (size_t i = 0; i < res.txs.size(); ++i, ++it)
- {
+ MDEBUG("Scanning " << res.txs.size() << " transactions");
+ THROW_WALLET_EXCEPTION_IF(slice + res.txs.size() > txs_hashes.size(), error::wallet_internal_error, "Unexpected tx array size");
+ auto it = req.txs_hashes.begin();
+ for (size_t i = 0; i < res.txs.size(); ++i, ++it)
+ {
const auto &tx_info = res.txs[i];
+ THROW_WALLET_EXCEPTION_IF(tx_info.tx_hash != epee::string_tools::pod_to_hex(txs_hashes[slice + i]), error::wallet_internal_error, "Wrong txid received");
THROW_WALLET_EXCEPTION_IF(tx_info.tx_hash != *it, error::wallet_internal_error, "Wrong txid received");
cryptonote::blobdata bd;
THROW_WALLET_EXCEPTION_IF(!epee::string_tools::parse_hexstr_to_binbuff(tx_info.as_hex, bd), error::wallet_internal_error, "failed to parse tx from hexstr");
@@ -5654,10 +5958,11 @@ bool wallet2::find_and_save_rings(bool force)
crypto::hash tx_hash, tx_prefix_hash;
THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(bd, tx, tx_hash, tx_prefix_hash), error::wallet_internal_error, "failed to parse tx from blob");
THROW_WALLET_EXCEPTION_IF(epee::string_tools::pod_to_hex(tx_hash) != tx_info.tx_hash, error::wallet_internal_error, "txid mismatch");
- THROW_WALLET_EXCEPTION_IF(!add_rings(key, tx), error::wallet_internal_error, "Failed to save ring");
+ THROW_WALLET_EXCEPTION_IF(!add_rings(get_ringdb_key(), tx), error::wallet_internal_error, "Failed to save ring");
+ }
}
- MINFO("Found and saved rings for " << res.txs.size() << " transactions");
+ MINFO("Found and saved rings for " << txs_hashes.size() << " transactions");
m_ring_history_saved = true;
return true;
}
@@ -5665,7 +5970,7 @@ bool wallet2::find_and_save_rings(bool force)
bool wallet2::blackball_output(const crypto::public_key &output)
{
if (!m_ringdb)
- return true;
+ return false;
try { return m_ringdb->blackball(output); }
catch (const std::exception &e) { return false; }
}
@@ -5673,7 +5978,7 @@ bool wallet2::blackball_output(const crypto::public_key &output)
bool wallet2::set_blackballed_outputs(const std::vector<crypto::public_key> &outputs, bool add)
{
if (!m_ringdb)
- return true;
+ return false;
try
{
bool ret = true;
@@ -5689,7 +5994,7 @@ bool wallet2::set_blackballed_outputs(const std::vector<crypto::public_key> &out
bool wallet2::unblackball_output(const crypto::public_key &output)
{
if (!m_ringdb)
- return true;
+ return false;
try { return m_ringdb->unblackball(output); }
catch (const std::exception &e) { return false; }
}
@@ -5697,7 +6002,7 @@ bool wallet2::unblackball_output(const crypto::public_key &output)
bool wallet2::is_output_blackballed(const crypto::public_key &output) const
{
if (!m_ringdb)
- return true;
+ return false;
try { return m_ringdb->blackballed(output); }
catch (const std::exception &e) { return false; }
}
@@ -5832,9 +6137,6 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
return;
}
- crypto::chacha_key key;
- generate_chacha_key_from_secret_keys(key);
-
if (fake_outputs_count > 0)
{
uint64_t segregation_fork_height = get_segregation_fork_height();
@@ -5874,9 +6176,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");
@@ -5893,6 +6196,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];
@@ -6010,7 +6314,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
if (td.m_key_image_known && !td.m_key_image_partial)
{
std::vector<uint64_t> ring;
- if (get_ring(key, td.m_key_image, ring))
+ if (get_ring(get_ringdb_key(), td.m_key_image, ring))
{
MINFO("This output has a known ring, reusing (size " << ring.size() << ")");
THROW_WALLET_EXCEPTION_IF(ring.size() > fake_outputs_count + 1, error::wallet_internal_error,
@@ -6202,7 +6506,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
if (td.m_key_image_known && !td.m_key_image_partial)
{
std::vector<uint64_t> ring;
- if (get_ring(key, td.m_key_image, ring))
+ if (get_ring(get_ringdb_key(), td.m_key_image, ring))
{
for (uint64_t out: ring)
{
@@ -6420,6 +6724,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
ptx.construction_data.extra = tx.extra;
ptx.construction_data.unlock_time = unlock_time;
ptx.construction_data.use_rct = false;
+ ptx.construction_data.use_bulletproofs = false;
ptx.construction_data.dests = dsts;
// record which subaddress indices are being used as inputs
ptx.construction_data.subaddr_account = subaddr_account;
@@ -6632,7 +6937,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);
@@ -6675,6 +6980,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
ptx.construction_data.extra = tx.extra;
ptx.construction_data.unlock_time = unlock_time;
ptx.construction_data.use_rct = true;
+ ptx.construction_data.use_bulletproofs = !tx.rct_signatures.p.bulletproofs.empty();
ptx.construction_data.dests = dsts;
// record which subaddress indices are being used as inputs
ptx.construction_data.subaddr_account = subaddr_account;
@@ -7285,7 +7591,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
{
//ensure device is let in NONE mode in any case
hw::device &hwdev = m_account.get_device();
- std::unique_lock<hw::device> hwdev_lock (hwdev);
+ boost::unique_lock<hw::device> hwdev_lock (hwdev);
hw::reset_mode rst(hwdev);
if(m_light_wallet) {
@@ -7302,8 +7608,11 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
cryptonote::transaction tx;
pending_tx ptx;
size_t bytes;
+ uint64_t needed_fee;
std::vector<std::vector<tools::wallet2::get_outs_entry>> outs;
+ TX() : bytes(0), needed_fee(0) {}
+
void add(const account_public_address &addr, bool is_subaddress, uint64_t amount, unsigned int original_output_index, bool merge_destinations) {
if (merge_destinations)
{
@@ -7696,6 +8005,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
tx.ptx = test_ptx;
tx.bytes = txBlob.size();
tx.outs = outs;
+ tx.needed_fee = needed_fee;
accumulated_fee += test_ptx.fee;
accumulated_change += test_ptx.change_dts.amount;
adding_fee = false;
@@ -7747,7 +8057,7 @@ skip_tx:
fake_outs_count, /* CONST size_t fake_outputs_count, */
tx.outs, /* MOD std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, */
unlock_time, /* CONST uint64_t unlock_time, */
- needed_fee, /* CONST uint64_t fee, */
+ tx.needed_fee, /* CONST uint64_t fee, */
extra, /* const std::vector<uint8_t>& extra, */
test_tx, /* OUT cryptonote::transaction& tx, */
test_ptx, /* OUT cryptonote::transaction& tx, */
@@ -7758,7 +8068,7 @@ skip_tx:
fake_outs_count,
tx.outs,
unlock_time,
- needed_fee,
+ tx.needed_fee,
extra,
detail::digit_split_strategy,
tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD),
@@ -7779,7 +8089,7 @@ skip_tx:
for (size_t idx: tx.selected_transfers)
tx_money += m_transfers[idx].amount();
LOG_PRINT_L1(" Transaction " << (1+std::distance(txes.begin(), i)) << "/" << txes.size() <<
- ": " << get_size_string(tx.bytes) << ", sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() <<
+ " " << get_transaction_hash(tx.ptx.tx) << ": " << get_size_string(tx.bytes) << ", sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() <<
" outputs to " << tx.dsts.size() << " destination(s), including " <<
print_money(tx.ptx.fee) << " fee, " << print_money(tx.ptx.change_dts.amount) << " change");
ptx_vector.push_back(tx.ptx);
@@ -7868,7 +8178,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
{
//ensure device is let in NONE mode in any case
hw::device &hwdev = m_account.get_device();
- std::unique_lock<hw::device> hwdev_lock (hwdev);
+ boost::unique_lock<hw::device> hwdev_lock (hwdev);
hw::reset_mode rst(hwdev);
uint64_t accumulated_fee, accumulated_outputs, accumulated_change;
@@ -7878,7 +8188,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
cryptonote::transaction tx;
pending_tx ptx;
size_t bytes;
+ uint64_t needed_fee;
std::vector<std::vector<get_outs_entry>> outs;
+
+ TX() : bytes(0), needed_fee(0) {}
};
std::vector<TX> txes;
uint64_t needed_fee, available_for_fee = 0;
@@ -7976,6 +8289,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
tx.ptx = test_ptx;
tx.bytes = txBlob.size();
tx.outs = outs;
+ tx.needed_fee = needed_fee;
accumulated_fee += test_ptx.fee;
accumulated_change += test_ptx.change_dts.amount;
if (!unused_transfers_indices.empty() || !unused_dust_indices.empty())
@@ -7996,10 +8310,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
cryptonote::transaction test_tx;
pending_tx test_ptx;
if (use_rct) {
- transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, needed_fee, extra,
+ transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, tx.needed_fee, extra,
test_tx, test_ptx, bulletproof);
} else {
- transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, needed_fee, extra,
+ transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, tx.needed_fee, extra,
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
}
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
@@ -8016,7 +8330,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
for (size_t idx: tx.selected_transfers)
tx_money += m_transfers[idx].amount();
LOG_PRINT_L1(" Transaction " << (1+std::distance(txes.begin(), i)) << "/" << txes.size() <<
- ": " << get_size_string(tx.bytes) << ", sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() <<
+ " " << get_transaction_hash(tx.ptx.tx) << ": " << get_size_string(tx.bytes) << ", sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() <<
" outputs to " << tx.dsts.size() << " destination(s), including " <<
print_money(tx.ptx.fee) << " fee, " << print_money(tx.ptx.change_dts.amount) << " change");
ptx_vector.push_back(tx.ptx);
@@ -8104,6 +8418,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");
@@ -8140,6 +8455,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");
@@ -8160,14 +8477,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);
}
//----------------------------------------------------------------------------------------------------
@@ -8200,6 +8517,16 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions(bo
return create_transactions_from(m_account_public_address, false, unmixable_transfer_outputs, unmixable_dust_outputs, 0 /*fake_outs_count */, 0 /* unlock_time */, 1 /*priority */, std::vector<uint8_t>(), trusted_daemon);
}
+//----------------------------------------------------------------------------------------------------
+void wallet2::discard_unmixable_outputs(bool trusted_daemon)
+{
+ // may throw
+ std::vector<size_t> unmixable_outputs = select_available_unmixable_outputs(trusted_daemon);
+ for (size_t idx : unmixable_outputs)
+ {
+ m_transfers[idx].m_spent = true;
+ }
+}
bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const
{
@@ -8223,6 +8550,7 @@ std::string wallet2::get_spend_proof(const crypto::hash &txid, const std::string
COMMAND_RPC_GET_TRANSACTIONS::request req = AUTO_VAL_INIT(req);
req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
req.decode_as_json = false;
+ req.prune = false;
COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res);
bool r;
{
@@ -8342,6 +8670,7 @@ bool wallet2::check_spend_proof(const crypto::hash &txid, const std::string &mes
COMMAND_RPC_GET_TRANSACTIONS::request req = AUTO_VAL_INIT(req);
req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
req.decode_as_json = false;
+ req.prune = false;
COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res);
bool r;
{
@@ -8371,8 +8700,9 @@ bool wallet2::check_spend_proof(const crypto::hash &txid, const std::string &mes
}
std::vector<std::vector<crypto::signature>> signatures = { std::vector<crypto::signature>(1) };
const size_t sig_len = tools::base58::encode(std::string((const char *)&signatures[0][0], sizeof(crypto::signature))).size();
- THROW_WALLET_EXCEPTION_IF(sig_str.size() != header_len + num_sigs * sig_len,
- error::wallet_internal_error, "incorrect signature size");
+ if( sig_str.size() != header_len + num_sigs * sig_len ) {
+ return false;
+ }
// decode base58
signatures.clear();
@@ -8464,6 +8794,8 @@ void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_de
COMMAND_RPC_GET_TRANSACTIONS::request req;
COMMAND_RPC_GET_TRANSACTIONS::response res;
req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
+ req.decode_as_json = false;
+ req.prune = false;
m_daemon_rpc_mutex.lock();
bool ok = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client);
m_daemon_rpc_mutex.unlock();
@@ -8600,6 +8932,8 @@ std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::ac
COMMAND_RPC_GET_TRANSACTIONS::request req;
COMMAND_RPC_GET_TRANSACTIONS::response res;
req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
+ req.decode_as_json = false;
+ req.prune = false;
m_daemon_rpc_mutex.lock();
bool ok = net_utils::invoke_http_json("/gettransactions", req, res, m_http_client);
m_daemon_rpc_mutex.unlock();
@@ -8710,6 +9044,8 @@ bool wallet2::check_tx_proof(const crypto::hash &txid, const cryptonote::account
COMMAND_RPC_GET_TRANSACTIONS::request req;
COMMAND_RPC_GET_TRANSACTIONS::response res;
req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
+ req.decode_as_json = false;
+ req.prune = false;
m_daemon_rpc_mutex.lock();
bool ok = net_utils::invoke_http_json("/gettransactions", req, res, m_http_client);
m_daemon_rpc_mutex.unlock();
@@ -8943,6 +9279,8 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr
COMMAND_RPC_GET_TRANSACTIONS::response gettx_res;
for (size_t i = 0; i < proofs.size(); ++i)
gettx_req.txs_hashes.push_back(epee::string_tools::pod_to_hex(proofs[i].txid));
+ gettx_req.decode_as_json = false;
+ gettx_req.prune = false;
m_daemon_rpc_mutex.lock();
bool ok = net_utils::invoke_http_json("/gettransactions", gettx_req, gettx_res, m_http_client);
m_daemon_rpc_mutex.unlock();
@@ -9094,9 +9432,9 @@ uint64_t wallet2::get_daemon_blockchain_target_height(string &err)
uint64_t wallet2::get_approximate_blockchain_height() const
{
// time of v2 fork
- const time_t fork_time = m_nettype == TESTNET ? 1448285909 : m_nettype == STAGENET ? (time_t)-1/*TODO*/ : 1458748658;
+ const time_t fork_time = m_nettype == TESTNET ? 1448285909 : m_nettype == STAGENET ? 1520937818 : 1458748658;
// v2 fork block
- const uint64_t fork_block = m_nettype == TESTNET ? 624634 : m_nettype == STAGENET ? (uint64_t)-1/*TODO*/ : 1009827;
+ const uint64_t fork_block = m_nettype == TESTNET ? 624634 : m_nettype == STAGENET ? 32000 : 1009827;
// avg seconds per block
const int seconds_per_block = DIFFICULTY_TARGET_V2;
// Calculated blockchain height
@@ -9217,6 +9555,40 @@ bool wallet2::verify(const std::string &data, const cryptonote::account_public_a
memcpy(&s, decoded.data(), sizeof(s));
return crypto::check_signature(hash, address.m_spend_public_key, s);
}
+
+std::string wallet2::sign_multisig_participant(const std::string& data) const
+{
+ CHECK_AND_ASSERT_THROW_MES(m_multisig, "Wallet is not multisig");
+
+ crypto::hash hash;
+ crypto::cn_fast_hash(data.data(), data.size(), hash);
+ const cryptonote::account_keys &keys = m_account.get_keys();
+ crypto::signature signature;
+ crypto::generate_signature(hash, get_multisig_signer_public_key(), keys.m_spend_secret_key, signature);
+ return MULTISIG_SIGNATURE_MAGIC + tools::base58::encode(std::string((const char *)&signature, sizeof(signature)));
+}
+
+bool wallet2::verify_with_public_key(const std::string &data, const crypto::public_key &public_key, const std::string &signature) const
+{
+ if (signature.size() < MULTISIG_SIGNATURE_MAGIC.size() || signature.substr(0, MULTISIG_SIGNATURE_MAGIC.size()) != MULTISIG_SIGNATURE_MAGIC) {
+ MERROR("Signature header check error");
+ return false;
+ }
+ crypto::hash hash;
+ crypto::cn_fast_hash(data.data(), data.size(), hash);
+ std::string decoded;
+ if (!tools::base58::decode(signature.substr(MULTISIG_SIGNATURE_MAGIC.size()), decoded)) {
+ MERROR("Signature decoding error");
+ return false;
+ }
+ crypto::signature s;
+ if (sizeof(s) != decoded.size()) {
+ MERROR("Signature decoding error");
+ return false;
+ }
+ memcpy(&s, decoded.data(), sizeof(s));
+ return crypto::check_signature(hash, public_key, s);
+}
//----------------------------------------------------------------------------------------------------
crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const
{
@@ -9472,7 +9844,7 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
std::unordered_set<crypto::hash> spent_txids; // For each spent key image, search for a tx in m_transfers that uses it as input.
std::vector<size_t> swept_transfers; // If such a spending tx wasn't found in m_transfers, this means the spending tx
// was created by sweep_all, so we can't know the spent height and other detailed info.
- for(size_t i = 0; i < m_transfers.size(); ++i)
+ for(size_t i = 0; i < signed_key_images.size(); ++i)
{
transfer_details &td = m_transfers[i];
uint64_t amount = td.amount();
@@ -9517,6 +9889,7 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
COMMAND_RPC_GET_TRANSACTIONS::request gettxs_req;
COMMAND_RPC_GET_TRANSACTIONS::response gettxs_res;
gettxs_req.decode_as_json = false;
+ gettxs_req.prune = false;
for (const crypto::hash& spent_txid : spent_txids)
gettxs_req.txs_hashes.push_back(epee::string_tools::pod_to_hex(spent_txid));
m_daemon_rpc_mutex.lock();
@@ -9694,7 +10067,7 @@ void wallet2::import_blockchain(const std::tuple<size_t, crypto::hash, std::vect
generate_genesis(genesis);
crypto::hash genesis_hash = get_block_hash(genesis);
check_genesis(genesis_hash);
- m_local_bc_height = m_blockchain.size();
+ m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx);
}
//----------------------------------------------------------------------------------------------------
std::vector<tools::wallet2::transfer_details> wallet2::export_outputs() const
@@ -9712,6 +10085,23 @@ std::vector<tools::wallet2::transfer_details> wallet2::export_outputs() const
return outs;
}
//----------------------------------------------------------------------------------------------------
+std::string wallet2::export_outputs_to_str() const
+{
+ std::vector<tools::wallet2::transfer_details> outs = export_outputs();
+
+ std::stringstream oss;
+ boost::archive::portable_binary_oarchive ar(oss);
+ ar << outs;
+
+ std::string magic(OUTPUT_EXPORT_FILE_MAGIC, strlen(OUTPUT_EXPORT_FILE_MAGIC));
+ const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address;
+ std::string header;
+ header += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key));
+ header += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key));
+ std::string ciphertext = encrypt_with_view_secret_key(header + oss.str());
+ return magic + ciphertext;
+}
+//----------------------------------------------------------------------------------------------------
size_t wallet2::import_outputs(const std::vector<tools::wallet2::transfer_details> &outputs)
{
m_transfers.clear();
@@ -9744,6 +10134,67 @@ size_t wallet2::import_outputs(const std::vector<tools::wallet2::transfer_detail
return m_transfers.size();
}
//----------------------------------------------------------------------------------------------------
+size_t wallet2::import_outputs_from_str(const std::string &outputs_st)
+{
+ std::string data = outputs_st;
+ const size_t magiclen = strlen(OUTPUT_EXPORT_FILE_MAGIC);
+ if (data.size() < magiclen || memcmp(data.data(), OUTPUT_EXPORT_FILE_MAGIC, magiclen))
+ {
+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Bad magic from outputs"));
+ }
+
+ try
+ {
+ data = decrypt_with_view_secret_key(std::string(data, magiclen));
+ }
+ catch (const std::exception &e)
+ {
+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to decrypt outputs: ") + e.what());
+ }
+
+ const size_t headerlen = 2 * sizeof(crypto::public_key);
+ if (data.size() < headerlen)
+ {
+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Bad data size for outputs"));
+ }
+ const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[0];
+ const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[sizeof(crypto::public_key)];
+ const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address;
+ if (public_spend_key != keys.m_spend_public_key || public_view_key != keys.m_view_public_key)
+ {
+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Outputs from are for a different account"));
+ }
+
+ size_t imported_outputs = 0;
+ try
+ {
+ std::string body(data, headerlen);
+ std::stringstream iss;
+ iss << body;
+ std::vector<tools::wallet2::transfer_details> outputs;
+ try
+ {
+ boost::archive::portable_binary_iarchive ar(iss);
+ ar >> outputs;
+ }
+ catch (...)
+ {
+ iss.str("");
+ iss << body;
+ boost::archive::binary_iarchive ar(iss);
+ ar >> outputs;
+ }
+
+ imported_outputs = import_outputs(outputs);
+ }
+ catch (const std::exception &e)
+ {
+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to import outputs") + e.what());
+ }
+
+ return imported_outputs;
+}
+//----------------------------------------------------------------------------------------------------
crypto::public_key wallet2::get_multisig_signer_public_key(const crypto::secret_key &spend_skey) const
{
crypto::public_key pkey;
@@ -10016,7 +10467,7 @@ size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs)
m_multisig_rescan_info = &info;
try
{
- refresh();
+ refresh(false);
}
catch (...) {}
m_multisig_rescan_info = NULL;
@@ -10443,17 +10894,6 @@ uint64_t wallet2::get_segregation_fork_height() const
}
//----------------------------------------------------------------------------------------------------
void wallet2::generate_genesis(cryptonote::block& b) const {
- if (m_nettype == TESTNET)
- {
- cryptonote::generate_genesis_block(b, config::testnet::GENESIS_TX, config::testnet::GENESIS_NONCE);
- }
- else if (m_nettype == STAGENET)
- {
- cryptonote::generate_genesis_block(b, config::stagenet::GENESIS_TX, config::stagenet::GENESIS_NONCE);
- }
- else
- {
- cryptonote::generate_genesis_block(b, config::GENESIS_TX, config::GENESIS_NONCE);
- }
+ cryptonote::generate_genesis_block(b, get_config(m_nettype).GENESIS_TX, get_config(m_nettype).GENESIS_NONCE);
}
}
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index ca8bee965..4d22b6915 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -320,6 +320,7 @@ namespace tools
std::vector<uint8_t> extra;
uint64_t unlock_time;
bool use_rct;
+ bool use_bulletproofs;
std::vector<cryptonote::tx_destination_entry> dests; // original setup, does not include change
uint32_t subaddr_account; // subaddress account of your wallet to be used in this transfer
std::set<uint32_t> subaddr_indices; // set of address indices used as inputs in this transfer
@@ -332,6 +333,7 @@ namespace tools
FIELD(extra)
FIELD(unlock_time)
FIELD(use_rct)
+ FIELD(use_bulletproofs)
FIELD(dests)
FIELD(subaddr_account)
FIELD(subaddr_indices)
@@ -452,6 +454,39 @@ namespace tools
typedef std::tuple<uint64_t, crypto::public_key, rct::key> get_outs_entry;
+ struct parsed_block
+ {
+ crypto::hash hash;
+ cryptonote::block block;
+ std::vector<cryptonote::transaction> txes;
+ cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices o_indices;
+ bool error;
+ };
+
+ struct is_out_data
+ {
+ crypto::public_key pkey;
+ crypto::key_derivation derivation;
+ std::vector<boost::optional<cryptonote::subaddress_receive_info>> received;
+ };
+
+ struct tx_cache_data
+ {
+ std::vector<cryptonote::tx_extra_field> tx_extra_fields;
+ std::vector<is_out_data> primary;
+ std::vector<is_out_data> additional;
+ };
+
+ struct key_ref
+ {
+ key_ref(tools::wallet2 &w): wallet(w) { ++refs; }
+ ~key_ref() { if (!--refs) wallet.clear_ringdb_key(); }
+
+ private:
+ tools::wallet2 &wallet;
+ static std::atomic<unsigned int> refs;
+ };
+
/*!
* \brief Generates a wallet or restores one.
* \param wallet_ Name of wallet file
@@ -477,21 +512,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 +598,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);
@@ -640,10 +677,10 @@ namespace tools
* \brief Tells if the wallet file is deprecated.
*/
bool is_deprecated() const;
- void refresh();
- void refresh(uint64_t start_height, uint64_t & blocks_fetched);
- void refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& received_money);
- bool refresh(uint64_t & blocks_fetched, bool& received_money, bool& ok);
+ void refresh(bool trusted_daemon);
+ void refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched);
+ void refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched, bool& received_money);
+ bool refresh(bool trusted_daemon, uint64_t & blocks_fetched, bool& received_money, bool& ok);
void set_refresh_type(RefreshType refresh_type) { m_refresh_type = refresh_type; }
RefreshType get_refresh_type() const { return m_refresh_type; }
@@ -653,6 +690,7 @@ namespace tools
bool watch_only() const { return m_watch_only; }
bool multisig(bool *ready = NULL, uint32_t *threshold = NULL, uint32_t *total = NULL) const;
bool has_multisig_partial_key_images() const;
+ bool has_unknown_key_images() const;
bool get_multisig_seed(std::string& seed, const epee::wipeable_string &passphrase = std::string(), bool raw = true) const;
bool key_on_device() const { return m_key_on_device; }
@@ -682,17 +720,23 @@ namespace tools
void commit_tx(pending_tx& ptx_vector);
void commit_tx(std::vector<pending_tx>& ptx_vector);
bool save_tx(const std::vector<pending_tx>& ptx_vector, const std::string &filename) const;
+ std::string dump_tx_to_str(const std::vector<pending_tx> &ptx_vector) const;
std::string save_multisig_tx(multisig_tx_set txs);
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
bool sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_filename, std::vector<wallet2::pending_tx> &ptx, bool export_raw = false);
+ bool sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pending_tx> &ptx, signed_tx_set &signed_txs);
+ std::string sign_tx_dump_to_str(unsigned_tx_set &exported_txs, std::vector<wallet2::pending_tx> &ptx, signed_tx_set &signed_txes);
// load unsigned_tx_set from file.
bool load_unsigned_tx(const std::string &unsigned_filename, unsigned_tx_set &exported_txs) const;
+ bool parse_unsigned_tx_from_str(const std::string &unsigned_tx_st, unsigned_tx_set &exported_txs) const;
bool load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func = NULL);
+ bool parse_tx_from_str(const std::string &signed_tx_st, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set &)> accept_func);
std::vector<pending_tx> create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, bool trusted_daemon);
std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, bool trusted_daemon); // pass subaddr_indices by value on purpose
std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, bool trusted_daemon);
@@ -704,6 +748,7 @@ namespace tools
bool sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto::hash> &txids);
bool sign_multisig_tx_to_file(multisig_tx_set &exported_txs, const std::string &filename, std::vector<crypto::hash> &txids);
std::vector<pending_tx> create_unmixable_sweep_transactions(bool trusted_daemon);
+ void discard_unmixable_outputs(bool trusted_daemon);
bool check_connection(uint32_t *version = NULL, uint32_t timeout = 200000);
void get_transfers(wallet2::transfer_container& incoming_transfers) const;
void get_payments(const crypto::hash& payment_id, std::list<wallet2::payment_details>& payments, uint64_t min_height = 0, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const;
@@ -713,11 +758,14 @@ namespace tools
void get_unconfirmed_payments_out(std::list<std::pair<crypto::hash,wallet2::unconfirmed_transfer_details>>& unconfirmed_payments, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const;
void get_unconfirmed_payments(std::list<std::pair<crypto::hash,wallet2::pool_payment_details>>& unconfirmed_payments, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const;
- uint64_t get_blockchain_current_height() const { return m_local_bc_height; }
+ uint64_t get_blockchain_current_height() const { return m_light_wallet_blockchain_height ? m_light_wallet_blockchain_height : m_blockchain.size(); }
void rescan_spent();
void rescan_blockchain(bool refresh = true);
bool is_transfer_unlocked(const transfer_details& td) const;
bool is_transfer_unlocked(uint64_t unlock_time, uint64_t block_height) const;
+
+ uint64_t get_last_block_reward() const { return m_last_block_reward; }
+
template <class t_archive>
inline void serialize(t_archive &a, const unsigned int ver)
{
@@ -819,6 +867,9 @@ namespace tools
if(ver < 24)
return;
a & m_ring_history_saved;
+ if(ver < 25)
+ return;
+ a & m_last_block_reward;
}
/*!
@@ -874,6 +925,8 @@ namespace tools
void key_reuse_mitigation2(bool value) { m_key_reuse_mitigation2 = value; }
uint64_t segregation_height() const { return m_segregation_height; }
void segregation_height(uint64_t height) { m_segregation_height = height; }
+ bool confirm_non_default_ring_size() const { return m_confirm_non_default_ring_size; }
+ void confirm_non_default_ring_size(bool always) { m_confirm_non_default_ring_size = always; }
bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const;
void check_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations);
@@ -957,16 +1010,34 @@ 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);
std::string sign(const std::string &data) const;
bool verify(const std::string &data, const cryptonote::account_public_address &address, const std::string &signature) const;
+ /*!
+ * \brief sign_multisig_participant signs given message with the multisig public signer key
+ * \param data message to sign
+ * \throws if wallet is not multisig
+ * \return signature
+ */
+ std::string sign_multisig_participant(const std::string& data) const;
+ /*!
+ * \brief verify_with_public_key verifies message was signed with given public key
+ * \param data message
+ * \param public_key public key to check signature
+ * \param signature signature of the message
+ * \return true if the signature is correct
+ */
+ bool verify_with_public_key(const std::string &data, const crypto::public_key &public_key, const std::string &signature) const;
+
// Import/Export wallet data
std::vector<tools::wallet2::transfer_details> export_outputs() const;
+ std::string export_outputs_to_str() const;
size_t import_outputs(const std::vector<tools::wallet2::transfer_details> &outputs);
+ size_t import_outputs_from_str(const std::string &outputs_st);
payment_container export_payments() const;
void import_payments(const payment_container &payments);
void import_payments_out(const std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>> &confirmed_payments);
@@ -1088,17 +1159,17 @@ namespace tools
* \param password Password of wallet file
*/
bool load_keys(const std::string& keys_file_name, const epee::wipeable_string& password);
- void process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen);
- void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height, const cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices &o_indices);
+ void process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen, const tx_cache_data &tx_cache_data);
+ void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const parsed_block &parsed_block, const crypto::hash& bl_id, uint64_t height, const std::vector<tx_cache_data> &tx_cache_data, size_t tx_cache_data_offset);
void detach_blockchain(uint64_t height);
- void get_short_chain_history(std::list<crypto::hash>& ids) const;
+ void get_short_chain_history(std::list<crypto::hash>& ids, uint64_t granularity = 1) const;
bool is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t block_height) const;
bool clear();
- void pull_blocks(uint64_t start_height, uint64_t& blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::list<cryptonote::block_complete_entry> &blocks, std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices);
- void pull_hashes(uint64_t start_height, uint64_t& blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::list<crypto::hash> &hashes);
+ void pull_blocks(uint64_t start_height, uint64_t& blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices);
+ void pull_hashes(uint64_t start_height, uint64_t& blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::vector<crypto::hash> &hashes);
void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history);
- void pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, const std::list<cryptonote::block_complete_entry> &prev_blocks, std::list<cryptonote::block_complete_entry> &blocks, std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices, bool &error);
- void process_blocks(uint64_t start_height, const std::list<cryptonote::block_complete_entry> &blocks, const std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices, uint64_t& blocks_added);
+ void pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, const std::vector<cryptonote::block_complete_entry> &prev_blocks, const std::vector<parsed_block> &prev_parsed_blocks, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<parsed_block> &parsed_blocks, bool &error);
+ void process_parsed_blocks(uint64_t start_height, const std::vector<cryptonote::block_complete_entry> &blocks, const std::vector<parsed_block> &parsed_blocks, uint64_t& blocks_added);
uint64_t select_transfers(uint64_t needed_money, std::vector<size_t> unused_transfers_indices, std::vector<size_t>& selected_transfers, bool trusted_daemon) const;
bool prepare_file_names(const std::string& file_path);
void process_unconfirmed(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height);
@@ -1109,6 +1180,8 @@ namespace tools
bool generate_chacha_key_from_secret_keys(crypto::chacha_key &key) const;
crypto::hash get_payment_id(const pending_tx &ptx) const;
void check_acc_out_precomp(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, tx_scan_info_t &tx_scan_info) const;
+ void check_acc_out_precomp(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info) const;
+ void check_acc_out_precomp_once(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info, bool &already_seen) const;
void parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const;
uint64_t get_upper_transaction_size_limit() const;
std::vector<uint64_t> get_unspent_amounts_vector() const;
@@ -1133,11 +1206,16 @@ namespace tools
bool add_rings(const cryptonote::transaction_prefix &tx);
bool remove_rings(const cryptonote::transaction_prefix &tx);
bool get_ring(const crypto::chacha_key &key, const crypto::key_image &key_image, std::vector<uint64_t> &outs);
+ crypto::chacha_key get_ringdb_key();
+ void cache_ringdb_key();
+ void clear_ringdb_key();
bool get_output_distribution(uint64_t &start_height, std::vector<uint64_t> &distribution);
uint64_t get_segregation_fork_height() const;
+ void cache_tx_data(const cryptonote::transaction& tx, const crypto::hash &txid, tx_cache_data &tx_cache_data) const;
+
cryptonote::account_base m_account;
boost::optional<epee::net_utils::http::login> m_daemon_login;
std::string m_daemon_address;
@@ -1145,7 +1223,6 @@ namespace tools
std::string m_keys_file;
epee::net_utils::http::http_simple_client m_http_client;
hashchain m_blockchain;
- std::atomic<uint64_t> m_local_bc_height; //temporary workaround
std::unordered_map<crypto::hash, unconfirmed_transfer_details> m_unconfirmed_txs;
std::unordered_map<crypto::hash, confirmed_transfer_details> m_confirmed_txs;
std::unordered_multimap<crypto::hash, pool_payment_details> m_unconfirmed_payments;
@@ -1189,11 +1266,13 @@ namespace tools
uint32_t m_default_priority;
RefreshType m_refresh_type;
bool m_auto_refresh;
+ bool m_first_refresh_done;
uint64_t m_refresh_from_block_height;
// If m_refresh_from_block_height is explicitly set to zero we need this to differentiate it from the case that
// m_refresh_from_block_height was defaulted to zero.*/
bool m_explicit_refresh_from_block_height;
bool m_confirm_missing_payment_id;
+ bool m_confirm_non_default_ring_size;
bool m_ask_password;
uint32_t m_min_output_count;
uint64_t m_min_output_value;
@@ -1227,9 +1306,13 @@ namespace tools
std::string m_ring_database;
bool m_ring_history_saved;
std::unique_ptr<ringdb> m_ringdb;
+ boost::optional<crypto::chacha_key> m_ringdb_key;
+
+ uint64_t m_last_block_reward;
+ std::unique_ptr<tools::file_locker> m_keys_file_locker;
};
}
-BOOST_CLASS_VERSION(tools::wallet2, 24)
+BOOST_CLASS_VERSION(tools::wallet2, 25)
BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 9)
BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1)
BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0)
@@ -1242,7 +1325,7 @@ BOOST_CLASS_VERSION(tools::wallet2::address_book_row, 17)
BOOST_CLASS_VERSION(tools::wallet2::reserve_proof_entry, 0)
BOOST_CLASS_VERSION(tools::wallet2::unsigned_tx_set, 0)
BOOST_CLASS_VERSION(tools::wallet2::signed_tx_set, 0)
-BOOST_CLASS_VERSION(tools::wallet2::tx_construction_data, 2)
+BOOST_CLASS_VERSION(tools::wallet2::tx_construction_data, 3)
BOOST_CLASS_VERSION(tools::wallet2::pending_tx, 3)
BOOST_CLASS_VERSION(tools::wallet2::multisig_sig, 0)
@@ -1589,6 +1672,9 @@ namespace boost
if (ver < 2)
return;
a & x.selected_transfers;
+ if (ver < 3)
+ return;
+ a & x.use_bulletproofs;
}
template <class Archive>
@@ -1874,6 +1960,7 @@ namespace tools
ptx.construction_data.extra = tx.extra;
ptx.construction_data.unlock_time = unlock_time;
ptx.construction_data.use_rct = false;
+ ptx.construction_data.use_bulletproofs = false;
ptx.construction_data.dests = dsts;
// record which subaddress indices are being used as inputs
ptx.construction_data.subaddr_account = subaddr_account;
diff --git a/src/wallet/wallet_args.cpp b/src/wallet/wallet_args.cpp
index a6ff63dd3..a629eb506 100644
--- a/src/wallet/wallet_args.cpp
+++ b/src/wallet/wallet_args.cpp
@@ -82,7 +82,7 @@ namespace wallet_args
return i18n_translate(str, "wallet_args");
}
- boost::optional<boost::program_options::variables_map> main(
+ std::pair<boost::optional<boost::program_options::variables_map>, bool> main(
int argc, char** argv,
const char* const usage,
const char* const notice,
@@ -101,6 +101,7 @@ namespace wallet_args
const command_line::arg_descriptor<std::string> arg_log_level = {"log-level", "0-4 or categories", ""};
const command_line::arg_descriptor<std::size_t> arg_max_log_file_size = {"max-log-file-size", "Specify maximum log file size [B]", MAX_LOG_FILE_SIZE};
+ const command_line::arg_descriptor<std::size_t> arg_max_log_files = {"max-log-files", "Specify maximum number of rotated log files to be saved (no limit by setting to 0)", MAX_LOG_FILES};
const command_line::arg_descriptor<uint32_t> arg_max_concurrency = {"max-concurrency", wallet_args::tr("Max number of threads to use for a parallel job"), DEFAULT_MAX_CONCURRENCY};
const command_line::arg_descriptor<std::string> arg_log_file = {"log-file", wallet_args::tr("Specify log file"), ""};
const command_line::arg_descriptor<std::string> arg_config_file = {"config-file", wallet_args::tr("Config file"), "", true};
@@ -119,6 +120,7 @@ namespace wallet_args
command_line::add_arg(desc_params, arg_log_file);
command_line::add_arg(desc_params, arg_log_level);
command_line::add_arg(desc_params, arg_max_log_file_size);
+ command_line::add_arg(desc_params, arg_max_log_files);
command_line::add_arg(desc_params, arg_max_concurrency);
command_line::add_arg(desc_params, arg_config_file);
@@ -127,6 +129,7 @@ namespace wallet_args
po::options_description desc_all;
desc_all.add(desc_general).add(desc_params);
po::variables_map vm;
+ bool should_terminate = false;
bool r = command_line::handle_error_helper(desc_all, [&]()
{
auto parser = po::command_line_parser(argc, argv).options(desc_all).positional(positional_options);
@@ -139,12 +142,14 @@ namespace wallet_args
"daemon to work correctly.") << ENDL;
Print(print) << wallet_args::tr("Usage:") << ENDL << " " << usage;
Print(print) << desc_all;
- return false;
+ should_terminate = true;
+ return true;
}
else if (command_line::get_arg(vm, command_line::arg_version))
{
Print(print) << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")";
- return false;
+ should_terminate = true;
+ return true;
}
if(command_line::has_arg(vm, arg_config_file))
@@ -167,18 +172,25 @@ namespace wallet_args
return true;
});
if (!r)
- return boost::none;
+ return {boost::none, true};
+
+ if (should_terminate)
+ return {std::move(vm), should_terminate};
std::string log_path;
if (!command_line::is_arg_defaulted(vm, arg_log_file))
log_path = command_line::get_arg(vm, arg_log_file);
else
log_path = mlog_get_default_log_path(default_log_name);
- mlog_configure(log_path, log_to_console, command_line::get_arg(vm, arg_max_log_file_size));
+ mlog_configure(log_path, log_to_console, command_line::get_arg(vm, arg_max_log_file_size), command_line::get_arg(vm, arg_max_log_files));
if (!command_line::is_arg_defaulted(vm, arg_log_level))
{
mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str());
}
+ else if (!log_to_console)
+ {
+ mlog_set_categories("");
+ }
if (notice)
Print(print) << notice << ENDL;
@@ -196,6 +208,6 @@ namespace wallet_args
Print(print) << boost::format(wallet_args::tr("Logging to %s")) % log_path;
- return {std::move(vm)};
+ return {std::move(vm), should_terminate};
}
}
diff --git a/src/wallet/wallet_args.h b/src/wallet/wallet_args.h
index af6685845..a1f251144 100644
--- a/src/wallet/wallet_args.h
+++ b/src/wallet/wallet_args.h
@@ -44,8 +44,11 @@ namespace wallet_args
concurrency. Log file and concurrency arguments are handled, along with basic
global init for the wallet process.
- \return The list of parsed options, iff there are no errors.*/
- boost::optional<boost::program_options::variables_map> main(
+ \return
+ pair.first: The list of parsed options, iff there are no errors.
+ pair.second: Should the execution terminate succesfully without actually launching the application
+ */
+ std::pair<boost::optional<boost::program_options::variables_map>, bool> main(
int argc, char** argv,
const char* const usage,
const char* const notice,
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index a9d211532..b9cf99635 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -74,6 +74,21 @@ namespace
}
return pwd_container;
}
+ //------------------------------------------------------------------------------------------------------------------------------
+ void set_confirmations(tools::wallet_rpc::transfer_entry &entry, uint64_t blockchain_height, uint64_t block_reward)
+ {
+ if (entry.height >= blockchain_height)
+ {
+ entry.confirmations = 0;
+ entry.suggested_confirmations_threshold = 0;
+ return;
+ }
+ entry.confirmations = blockchain_height - entry.height;
+ if (block_reward == 0)
+ entry.suggested_confirmations_threshold = 0;
+ else
+ entry.suggested_confirmations_threshold = (entry.amount + block_reward - 1) / block_reward;
+ }
}
namespace tools
@@ -104,7 +119,7 @@ namespace tools
m_stop = false;
m_net_server.add_idle_handler([this](){
try {
- if (m_wallet) m_wallet->refresh();
+ if (m_wallet) m_wallet->refresh(m_trusted_daemon);
} catch (const std::exception& ex) {
LOG_ERROR("Exception at while refreshing, what=" << ex.what());
}
@@ -258,6 +273,7 @@ namespace tools
entry.type = "in";
entry.subaddr_index = pd.m_subaddr_index;
entry.address = m_wallet->get_subaddress_as_str(pd.m_subaddr_index);
+ set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward());
}
//------------------------------------------------------------------------------------------------------------------------------
void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::confirmed_transfer_details &pd)
@@ -284,6 +300,7 @@ namespace tools
entry.type = "out";
entry.subaddr_index = { pd.m_subaddr_account, 0 };
entry.address = m_wallet->get_subaddress_as_str({pd.m_subaddr_account, 0});
+ set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward());
}
//------------------------------------------------------------------------------------------------------------------------------
void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::unconfirmed_transfer_details &pd)
@@ -303,6 +320,7 @@ namespace tools
entry.type = is_failed ? "failed" : "pending";
entry.subaddr_index = { pd.m_subaddr_account, 0 };
entry.address = m_wallet->get_subaddress_as_str({pd.m_subaddr_account, 0});
+ set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward());
}
//------------------------------------------------------------------------------------------------------------------------------
void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &payment_id, const tools::wallet2::pool_payment_details &ppd)
@@ -322,6 +340,7 @@ namespace tools
entry.type = "pool";
entry.subaddr_index = pd.m_subaddr_index;
entry.address = m_wallet->get_subaddress_as_str(pd.m_subaddr_index);
+ set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward());
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er)
@@ -362,6 +381,7 @@ namespace tools
if (!m_wallet) return not_open(er);
try
{
+ THROW_WALLET_EXCEPTION_IF(req.account_index >= m_wallet->get_num_subaddress_accounts(), error::account_index_outofbound);
res.addresses.clear();
std::vector<uint32_t> req_address_index;
if (req.address_index.empty())
@@ -377,6 +397,7 @@ namespace tools
m_wallet->get_transfers(transfers);
for (uint32_t i : req_address_index)
{
+ THROW_WALLET_EXCEPTION_IF(i >= m_wallet->get_num_subaddresses(req.account_index), error::address_index_outofbound);
res.addresses.resize(res.addresses.size() + 1);
auto& info = res.addresses.back();
const cryptonote::subaddress_index index = {req.account_index, i};
@@ -500,6 +521,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_get_account_tags(const wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS::request& req, wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS::response& res, epee::json_rpc::error& er)
{
+ if (!m_wallet) return not_open(er);
const std::pair<std::map<std::string, std::string>, std::vector<std::string>> account_tags = m_wallet->get_account_tags();
for (const std::pair<std::string, std::string>& p : account_tags.first)
{
@@ -518,6 +540,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_tag_accounts(const wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS::response& res, epee::json_rpc::error& er)
{
+ if (!m_wallet) return not_open(er);
try
{
m_wallet->set_account_tag(req.accounts, req.tag);
@@ -532,6 +555,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_untag_accounts(const wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::response& res, epee::json_rpc::error& er)
{
+ if (!m_wallet) return not_open(er);
try
{
m_wallet->set_account_tag(req.accounts, "");
@@ -546,6 +570,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_set_account_tag_description(const wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::request& req, wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::response& res, epee::json_rpc::error& er)
{
+ if (!m_wallet) return not_open(er);
try
{
m_wallet->set_account_tag_description(req.tag, req.description);
@@ -712,7 +737,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
template<typename Ts, typename Tu>
bool wallet_rpc_server::fill_response(std::vector<tools::wallet2::pending_tx> &ptx_vector,
- bool get_tx_key, Ts& tx_key, Tu &amount, Tu &fee, std::string &multisig_txset, bool do_not_relay,
+ bool get_tx_key, Ts& tx_key, Tu &amount, Tu &fee, std::string &multisig_txset, std::string &unsigned_txset, bool do_not_relay,
Ts &tx_hash, bool get_tx_hex, Ts &tx_blob, bool get_tx_metadata, Ts &tx_metadata, epee::json_rpc::error &er)
{
for (const auto & ptx : ptx_vector)
@@ -741,7 +766,16 @@ namespace tools
}
else
{
- if (!do_not_relay)
+ if (m_wallet->watch_only()){
+ unsigned_txset = epee::string_tools::buff_to_hex_nodelimer(m_wallet->dump_tx_to_str(ptx_vector));
+ if (unsigned_txset.empty())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "Failed to save unsigned tx set after creation";
+ return false;
+ }
+ }
+ else if (!do_not_relay)
m_wallet->commit_tx(ptx_vector);
// populate response with tx hashes
@@ -811,7 +845,7 @@ namespace tools
return false;
}
- return fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.fee, res.multisig_txset, req.do_not_relay,
+ return fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.fee, res.multisig_txset, res.unsigned_txset, req.do_not_relay,
res.tx_hash, req.get_tx_hex, res.tx_blob, req.get_tx_metadata, res.tx_metadata, er);
}
catch (const std::exception& e)
@@ -858,7 +892,7 @@ namespace tools
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon);
LOG_PRINT_L2("on_transfer_split called create_transactions_2");
- return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, req.do_not_relay,
+ return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay,
res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er);
}
catch (const std::exception& e)
@@ -869,6 +903,141 @@ namespace tools
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_sign_transfer(const wallet_rpc::COMMAND_RPC_SIGN_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SIGN_TRANSFER::response& res, epee::json_rpc::error& er)
+ {
+ if (!m_wallet) return not_open(er);
+ if (m_wallet->restricted())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_DENIED;
+ er.message = "Command unavailable in restricted mode.";
+ return false;
+ }
+ if (m_wallet->key_on_device())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "command not supported by HW wallet";
+ return false;
+ }
+ if(m_wallet->watch_only())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WATCH_ONLY;
+ er.message = "command not supported by watch-only wallet";
+ return false;
+ }
+
+ cryptonote::blobdata blob;
+ if (!epee::string_tools::parse_hexstr_to_binbuff(req.unsigned_txset, blob))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_HEX;
+ er.message = "Failed to parse hex.";
+ return false;
+ }
+
+ tools::wallet2::unsigned_tx_set exported_txs;
+ if(!m_wallet->parse_unsigned_tx_from_str(blob, exported_txs))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA;
+ er.message = "cannot load unsigned_txset";
+ return false;
+ }
+
+ std::vector<tools::wallet2::pending_tx> ptxs;
+ try
+ {
+ tools::wallet2::signed_tx_set signed_txs;
+ std::string ciphertext = m_wallet->sign_tx_dump_to_str(exported_txs, ptxs, signed_txs);
+ if (ciphertext.empty())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_SIGN_UNSIGNED;
+ er.message = "Failed to sign unsigned tx";
+ return false;
+ }
+
+ res.signed_txset = epee::string_tools::buff_to_hex_nodelimer(ciphertext);
+ }
+ catch (const std::exception &e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_SIGN_UNSIGNED;
+ er.message = std::string("Failed to sign unsigned tx: ") + e.what();
+ return false;
+ }
+
+ for (auto &ptx: ptxs)
+ {
+ res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
+ }
+
+ if (req.export_raw)
+ {
+ for (auto &ptx: ptxs)
+ {
+ res.tx_raw_list.push_back(epee::string_tools::buff_to_hex_nodelimer(cryptonote::tx_to_blob(ptx.tx)));
+ }
+ }
+
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_submit_transfer(const wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::response& res, epee::json_rpc::error& er)
+ {
+ if (!m_wallet) return not_open(er);
+ if (m_wallet->restricted())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_DENIED;
+ er.message = "Command unavailable in restricted mode.";
+ return false;
+ }
+ if (m_wallet->key_on_device())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "command not supported by HW wallet";
+ return false;
+ }
+
+ cryptonote::blobdata blob;
+ if (!epee::string_tools::parse_hexstr_to_binbuff(req.tx_data_hex, blob))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_HEX;
+ er.message = "Failed to parse hex.";
+ return false;
+ }
+
+ std::vector<tools::wallet2::pending_tx> ptx_vector;
+ try
+ {
+ bool r = m_wallet->parse_tx_from_str(blob, ptx_vector, NULL);
+ if (!r)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_SIGNED_TX_DATA;
+ er.message = "Failed to parse signed tx data.";
+ return false;
+ }
+ }
+ catch (const std::exception &e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_SIGNED_TX_DATA;
+ er.message = std::string("Failed to parse signed tx: ") + e.what();
+ return false;
+ }
+
+ try
+ {
+ for (auto &ptx: ptx_vector)
+ {
+ m_wallet->commit_tx(ptx);
+ res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
+ }
+ }
+ catch (const std::exception &e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_SIGNED_SUBMISSION;
+ er.message = std::string("Failed to submit signed tx: ") + e.what();
+ return false;
+ }
+
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_sweep_dust(const wallet_rpc::COMMAND_RPC_SWEEP_DUST::request& req, wallet_rpc::COMMAND_RPC_SWEEP_DUST::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
@@ -883,7 +1052,7 @@ namespace tools
{
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_unmixable_sweep_transactions(m_trusted_daemon);
- return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, req.do_not_relay,
+ return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay,
res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er);
}
catch (const std::exception& e)
@@ -931,7 +1100,7 @@ namespace tools
uint32_t priority = m_wallet->adjust_priority(req.priority);
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon);
- return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, req.do_not_relay,
+ return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay,
res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er);
}
catch (const std::exception& e)
@@ -1007,7 +1176,7 @@ namespace tools
return false;
}
- return fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.fee, res.multisig_txset, req.do_not_relay,
+ return fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.fee, res.multisig_txset, res.unsigned_txset, req.do_not_relay,
res.tx_hash, req.get_tx_hex, res.tx_blob, req.get_tx_metadata, res.tx_metadata, er);
}
catch (const std::exception& e)
@@ -1974,6 +2143,72 @@ namespace tools
return false;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_export_outputs(const wallet_rpc::COMMAND_RPC_EXPORT_OUTPUTS::request& req, wallet_rpc::COMMAND_RPC_EXPORT_OUTPUTS::response& res, epee::json_rpc::error& er)
+ {
+ if (!m_wallet) return not_open(er);
+ if (m_wallet->restricted())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_DENIED;
+ er.message = "Command unavailable in restricted mode.";
+ return false;
+ }
+ if (m_wallet->key_on_device())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "command not supported by HW wallet";
+ return false;
+ }
+
+ try
+ {
+ res.outputs_data_hex = epee::string_tools::buff_to_hex_nodelimer(m_wallet->export_outputs_to_str());
+ }
+ catch (const std::exception &e)
+ {
+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
+ return false;
+ }
+
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_import_outputs(const wallet_rpc::COMMAND_RPC_IMPORT_OUTPUTS::request& req, wallet_rpc::COMMAND_RPC_IMPORT_OUTPUTS::response& res, epee::json_rpc::error& er)
+ {
+ if (!m_wallet) return not_open(er);
+ if (m_wallet->restricted())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_DENIED;
+ er.message = "Command unavailable in restricted mode.";
+ return false;
+ }
+ if (m_wallet->key_on_device())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "command not supported by HW wallet";
+ return false;
+ }
+
+ cryptonote::blobdata blob;
+ if (!epee::string_tools::parse_hexstr_to_binbuff(req.outputs_data_hex, blob))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_HEX;
+ er.message = "Failed to parse hex.";
+ return false;
+ }
+
+ try
+ {
+ res.num_imported = m_wallet->import_outputs_from_str(blob);
+ }
+ catch (const std::exception &e)
+ {
+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
+ return false;
+ }
+
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_export_key_images(const wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
@@ -2054,6 +2289,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_make_uri(const wallet_rpc::COMMAND_RPC_MAKE_URI::request& req, wallet_rpc::COMMAND_RPC_MAKE_URI::response& res, epee::json_rpc::error& er)
{
+ if (!m_wallet) return not_open(er);
std::string error;
std::string uri = m_wallet->make_uri(req.address, req.payment_id, req.amount, req.tx_description, req.recipient_name, error);
if (uri.empty())
@@ -2267,6 +2503,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_stop_mining(const wallet_rpc::COMMAND_RPC_STOP_MINING::request& req, wallet_rpc::COMMAND_RPC_STOP_MINING::response& res, epee::json_rpc::error& er)
{
+ if (!m_wallet) return not_open(er);
cryptonote::COMMAND_RPC_STOP_MINING::request daemon_req;
cryptonote::COMMAND_RPC_STOP_MINING::response daemon_res;
bool r = m_wallet->invoke_http_json("/stop_mining", daemon_req, daemon_res);
@@ -2876,6 +3113,12 @@ namespace tools
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_get_version(const wallet_rpc::COMMAND_RPC_GET_VERSION::request& req, wallet_rpc::COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& er)
+ {
+ res.version = WALLET_RPC_VERSION;
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
}
int main(int argc, char** argv) {
@@ -2895,7 +3138,9 @@ int main(int argc, char** argv) {
command_line::add_arg(desc_params, arg_wallet_dir);
command_line::add_arg(desc_params, arg_prompt_for_password);
- const auto vm = wallet_args::main(
+ boost::optional<po::variables_map> vm;
+ bool should_terminate = false;
+ std::tie(vm, should_terminate) = wallet_args::main(
argc, argv,
"monero-wallet-rpc [--wallet-file=<file>|--generate-from-json=<file>|--wallet-dir=<directory>] [--rpc-bind-port=<port>]",
tools::wallet_rpc_server::tr("This is the RPC monero wallet. It needs to connect to a monero\ndaemon to work correctly."),
@@ -2909,6 +3154,10 @@ int main(int argc, char** argv) {
{
return 1;
}
+ if (should_terminate)
+ {
+ return 0;
+ }
std::unique_ptr<tools::wallet2> wal;
try
@@ -2974,7 +3223,7 @@ int main(int argc, char** argv) {
wal->stop();
});
- wal->refresh();
+ wal->refresh(command_line::get_arg(*vm, arg_trusted_daemon));
// if we ^C during potentially length load/refresh, there's no server loop yet
if (quit)
{
diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h
index 2ec53cc80..9cb67c593 100644
--- a/src/wallet/wallet_rpc_server.h
+++ b/src/wallet/wallet_rpc_server.h
@@ -84,6 +84,8 @@ namespace tools
MAP_JON_RPC_WE("getheight", on_getheight, wallet_rpc::COMMAND_RPC_GET_HEIGHT)
MAP_JON_RPC_WE("transfer", on_transfer, wallet_rpc::COMMAND_RPC_TRANSFER)
MAP_JON_RPC_WE("transfer_split", on_transfer_split, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT)
+ MAP_JON_RPC_WE("sign_transfer", on_sign_transfer, wallet_rpc::COMMAND_RPC_SIGN_TRANSFER)
+ MAP_JON_RPC_WE("submit_transfer", on_submit_transfer, wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER)
MAP_JON_RPC_WE("sweep_dust", on_sweep_dust, wallet_rpc::COMMAND_RPC_SWEEP_DUST)
MAP_JON_RPC_WE("sweep_unmixable", on_sweep_dust, wallet_rpc::COMMAND_RPC_SWEEP_DUST)
MAP_JON_RPC_WE("sweep_all", on_sweep_all, wallet_rpc::COMMAND_RPC_SWEEP_ALL)
@@ -114,6 +116,8 @@ namespace tools
MAP_JON_RPC_WE("get_transfer_by_txid", on_get_transfer_by_txid, wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID)
MAP_JON_RPC_WE("sign", on_sign, wallet_rpc::COMMAND_RPC_SIGN)
MAP_JON_RPC_WE("verify", on_verify, wallet_rpc::COMMAND_RPC_VERIFY)
+ MAP_JON_RPC_WE("export_outputs", on_export_outputs, wallet_rpc::COMMAND_RPC_EXPORT_OUTPUTS)
+ MAP_JON_RPC_WE("import_outputs", on_import_outputs, wallet_rpc::COMMAND_RPC_IMPORT_OUTPUTS)
MAP_JON_RPC_WE("export_key_images", on_export_key_images, wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES)
MAP_JON_RPC_WE("import_key_images", on_import_key_images, wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES)
MAP_JON_RPC_WE("make_uri", on_make_uri, wallet_rpc::COMMAND_RPC_MAKE_URI)
@@ -135,6 +139,7 @@ namespace tools
MAP_JON_RPC_WE("finalize_multisig", on_finalize_multisig, wallet_rpc::COMMAND_RPC_FINALIZE_MULTISIG)
MAP_JON_RPC_WE("sign_multisig", on_sign_multisig, wallet_rpc::COMMAND_RPC_SIGN_MULTISIG)
MAP_JON_RPC_WE("submit_multisig", on_submit_multisig, wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG)
+ MAP_JON_RPC_WE("get_version", on_get_version, wallet_rpc::COMMAND_RPC_GET_VERSION)
END_JSON_RPC_MAP()
END_URI_MAP2()
@@ -154,6 +159,8 @@ namespace tools
bool validate_transfer(const std::list<wallet_rpc::transfer_destination>& destinations, const std::string& payment_id, std::vector<cryptonote::tx_destination_entry>& dsts, std::vector<uint8_t>& extra, bool at_least_one_destination, epee::json_rpc::error& er);
bool on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er);
bool on_transfer_split(const wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::response& res, epee::json_rpc::error& er);
+ bool on_sign_transfer(const wallet_rpc::COMMAND_RPC_SIGN_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SIGN_TRANSFER::response& res, epee::json_rpc::error& er);
+ bool on_submit_transfer(const wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::response& res, epee::json_rpc::error& er);
bool on_sweep_dust(const wallet_rpc::COMMAND_RPC_SWEEP_DUST::request& req, wallet_rpc::COMMAND_RPC_SWEEP_DUST::response& res, epee::json_rpc::error& er);
bool on_sweep_all(const wallet_rpc::COMMAND_RPC_SWEEP_ALL::request& req, wallet_rpc::COMMAND_RPC_SWEEP_ALL::response& res, epee::json_rpc::error& er);
bool on_sweep_single(const wallet_rpc::COMMAND_RPC_SWEEP_SINGLE::request& req, wallet_rpc::COMMAND_RPC_SWEEP_SINGLE::response& res, epee::json_rpc::error& er);
@@ -182,6 +189,8 @@ namespace tools
bool on_get_transfer_by_txid(const wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID::response& res, epee::json_rpc::error& er);
bool on_sign(const wallet_rpc::COMMAND_RPC_SIGN::request& req, wallet_rpc::COMMAND_RPC_SIGN::response& res, epee::json_rpc::error& er);
bool on_verify(const wallet_rpc::COMMAND_RPC_VERIFY::request& req, wallet_rpc::COMMAND_RPC_VERIFY::response& res, epee::json_rpc::error& er);
+ bool on_export_outputs(const wallet_rpc::COMMAND_RPC_EXPORT_OUTPUTS::request& req, wallet_rpc::COMMAND_RPC_EXPORT_OUTPUTS::response& res, epee::json_rpc::error& er);
+ bool on_import_outputs(const wallet_rpc::COMMAND_RPC_IMPORT_OUTPUTS::request& req, wallet_rpc::COMMAND_RPC_IMPORT_OUTPUTS::response& res, epee::json_rpc::error& er);
bool on_export_key_images(const wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er);
bool on_import_key_images(const wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er);
bool on_make_uri(const wallet_rpc::COMMAND_RPC_MAKE_URI::request& req, wallet_rpc::COMMAND_RPC_MAKE_URI::response& res, epee::json_rpc::error& er);
@@ -203,6 +212,7 @@ namespace tools
bool on_finalize_multisig(const wallet_rpc::COMMAND_RPC_FINALIZE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_FINALIZE_MULTISIG::response& res, epee::json_rpc::error& er);
bool on_sign_multisig(const wallet_rpc::COMMAND_RPC_SIGN_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_SIGN_MULTISIG::response& res, epee::json_rpc::error& er);
bool on_submit_multisig(const wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG::response& res, epee::json_rpc::error& er);
+ bool on_get_version(const wallet_rpc::COMMAND_RPC_GET_VERSION::request& req, wallet_rpc::COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& er);
//json rpc v2
bool on_query_key(const wallet_rpc::COMMAND_RPC_QUERY_KEY::request& req, wallet_rpc::COMMAND_RPC_QUERY_KEY::response& res, epee::json_rpc::error& er);
@@ -217,7 +227,7 @@ namespace tools
template<typename Ts, typename Tu>
bool fill_response(std::vector<tools::wallet2::pending_tx> &ptx_vector,
- bool get_tx_key, Ts& tx_key, Tu &amount, Tu &fee, std::string &multisig_txset, bool do_not_relay,
+ bool get_tx_key, Ts& tx_key, Tu &amount, Tu &fee, std::string &multisig_txset, std::string &unsigned_txset, bool do_not_relay,
Ts &tx_hash, bool get_tx_hex, Ts &tx_blob, bool get_tx_metadata, Ts &tx_metadata, epee::json_rpc::error &er);
wallet2 *m_wallet;
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index a0f43c9b9..1bd572add 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -39,6 +39,17 @@
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.rpc"
+// When making *any* change here, bump minor
+// If the change is incompatible, then bump major and set minor to 0
+// This ensures WALLET_RPC_VERSION always increases, that every change
+// has its own version, and that clients can just test major to see
+// whether they can talk to a given wallet without having to know in
+// advance which version they will stop working with
+// Don't go over 32767 for any of these
+#define WALLET_RPC_VERSION_MAJOR 1
+#define WALLET_RPC_VERSION_MINOR 1
+#define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor))
+#define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR)
namespace tools
{
namespace wallet_rpc
@@ -419,6 +430,7 @@ namespace wallet_rpc
std::string tx_blob;
std::string tx_metadata;
std::string multisig_txset;
+ std::string unsigned_txset;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash)
@@ -429,6 +441,7 @@ namespace wallet_rpc
KV_SERIALIZE(tx_blob)
KV_SERIALIZE(tx_metadata)
KV_SERIALIZE(multisig_txset)
+ KV_SERIALIZE(unsigned_txset)
END_KV_SERIALIZE_MAP()
};
};
@@ -484,6 +497,7 @@ namespace wallet_rpc
std::list<std::string> tx_blob_list;
std::list<std::string> tx_metadata_list;
std::string multisig_txset;
+ std::string unsigned_txset;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash_list)
@@ -493,6 +507,55 @@ namespace wallet_rpc
KV_SERIALIZE(tx_blob_list)
KV_SERIALIZE(tx_metadata_list)
KV_SERIALIZE(multisig_txset)
+ KV_SERIALIZE(unsigned_txset)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ struct COMMAND_RPC_SIGN_TRANSFER
+ {
+ struct request
+ {
+ std::string unsigned_txset;
+ bool export_raw;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(unsigned_txset)
+ KV_SERIALIZE_OPT(export_raw, false)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ std::string signed_txset;
+ std::list<std::string> tx_hash_list;
+ std::list<std::string> tx_raw_list;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(signed_txset)
+ KV_SERIALIZE(tx_hash_list)
+ KV_SERIALIZE(tx_raw_list)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ struct COMMAND_RPC_SUBMIT_TRANSFER
+ {
+ struct request
+ {
+ std::string tx_data_hex;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(tx_data_hex)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ std::list<std::string> tx_hash_list;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(tx_hash_list)
END_KV_SERIALIZE_MAP()
};
};
@@ -532,6 +595,7 @@ namespace wallet_rpc
std::list<std::string> tx_blob_list;
std::list<std::string> tx_metadata_list;
std::string multisig_txset;
+ std::string unsigned_txset;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash_list)
@@ -541,6 +605,7 @@ namespace wallet_rpc
KV_SERIALIZE(tx_blob_list)
KV_SERIALIZE(tx_metadata_list)
KV_SERIALIZE(multisig_txset)
+ KV_SERIALIZE(unsigned_txset)
END_KV_SERIALIZE_MAP()
};
};
@@ -598,6 +663,7 @@ namespace wallet_rpc
std::list<std::string> tx_blob_list;
std::list<std::string> tx_metadata_list;
std::string multisig_txset;
+ std::string unsigned_txset;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash_list)
@@ -607,6 +673,7 @@ namespace wallet_rpc
KV_SERIALIZE(tx_blob_list)
KV_SERIALIZE(tx_metadata_list)
KV_SERIALIZE(multisig_txset)
+ KV_SERIALIZE(unsigned_txset)
END_KV_SERIALIZE_MAP()
};
};
@@ -651,6 +718,7 @@ namespace wallet_rpc
std::string tx_blob;
std::string tx_metadata;
std::string multisig_txset;
+ std::string unsigned_txset;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash)
@@ -660,6 +728,7 @@ namespace wallet_rpc
KV_SERIALIZE(tx_blob)
KV_SERIALIZE(tx_metadata)
KV_SERIALIZE(multisig_txset)
+ KV_SERIALIZE(unsigned_txset)
END_KV_SERIALIZE_MAP()
};
};
@@ -1125,6 +1194,8 @@ namespace wallet_rpc
cryptonote::subaddress_index subaddr_index;
std::string address;
bool double_spend_seen;
+ uint64_t confirmations;
+ uint64_t suggested_confirmations_threshold;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(txid);
@@ -1140,6 +1211,8 @@ namespace wallet_rpc
KV_SERIALIZE(subaddr_index);
KV_SERIALIZE(address);
KV_SERIALIZE(double_spend_seen)
+ KV_SERIALIZE_OPT(confirmations, (uint64_t)0)
+ KV_SERIALIZE_OPT(suggested_confirmations_threshold, (uint64_t)0)
END_KV_SERIALIZE_MAP()
};
@@ -1364,6 +1437,45 @@ namespace wallet_rpc
};
};
+ struct COMMAND_RPC_EXPORT_OUTPUTS
+ {
+ struct request
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ std::string outputs_data_hex;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(outputs_data_hex);
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ struct COMMAND_RPC_IMPORT_OUTPUTS
+ {
+ struct request
+ {
+ std::string outputs_data_hex;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(outputs_data_hex);
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ uint64_t num_imported;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(num_imported);
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
struct COMMAND_RPC_EXPORT_KEY_IMAGES
{
struct request
@@ -1848,5 +1960,23 @@ namespace wallet_rpc
};
};
+ struct COMMAND_RPC_GET_VERSION
+ {
+ struct request
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ uint32_t version;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(version)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
}
}
diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h
index d47467940..f127ae240 100644
--- a/src/wallet/wallet_rpc_server_error_codes.h
+++ b/src/wallet/wallet_rpc_server_error_codes.h
@@ -69,3 +69,7 @@
#define WALLET_RPC_ERROR_CODE_MULTISIG_SUBMISSION -36
#define WALLET_RPC_ERROR_CODE_NOT_ENOUGH_UNLOCKED_MONEY -37
#define WALLET_RPC_ERROR_CODE_NO_DAEMON_CONNECTION -38
+#define WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA -39
+#define WALLET_RPC_ERROR_CODE_BAD_SIGNED_TX_DATA -40
+#define WALLET_RPC_ERROR_CODE_SIGNED_SUBMISSION -41
+#define WALLET_RPC_ERROR_CODE_SIGN_UNSIGNED -42