aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/blockchain_db/blockchain_db.cpp10
-rw-r--r--src/blockchain_db/blockchain_db.h5
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp72
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.h2
-rw-r--r--src/blockchain_utilities/CMakeLists.txt4
-rw-r--r--src/blockchain_utilities/blockchain_import.cpp6
-rw-r--r--src/blocks/CMakeLists.txt4
-rw-r--r--src/checkpoints/checkpoints.cpp10
-rw-r--r--src/checkpoints/checkpoints.h4
-rw-r--r--src/common/CMakeLists.txt6
-rw-r--r--src/common/download.cpp61
-rw-r--r--src/common/i18n.cpp43
-rw-r--r--src/common/memwipe.c106
-rw-r--r--src/common/password.cpp2
-rw-r--r--src/common/stack_trace.cpp3
-rw-r--r--src/common/threadpool.cpp8
-rw-r--r--src/common/updates.cpp2
-rw-r--r--src/common/util.cpp15
-rw-r--r--src/crypto/CMakeLists.txt5
-rw-r--r--src/crypto/blake256.c2
-rw-r--r--src/crypto/chacha.c (renamed from src/crypto/chacha8.c)16
-rw-r--r--src/crypto/chacha.h (renamed from src/crypto/chacha8.h)31
-rw-r--r--src/crypto/crypto.h2
-rw-r--r--src/cryptonote_basic/cryptonote_basic.h3
-rw-r--r--src/cryptonote_basic/cryptonote_basic_impl.cpp2
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.cpp13
-rw-r--r--src/cryptonote_core/blockchain.cpp62
-rw-r--r--src/cryptonote_core/blockchain.h28
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp59
-rw-r--r--src/cryptonote_core/cryptonote_core.h21
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.cpp6
-rw-r--r--src/cryptonote_core/tx_pool.cpp39
-rw-r--r--src/cryptonote_protocol/block_queue.cpp3
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.inl33
-rw-r--r--src/daemon/CMakeLists.txt4
-rw-r--r--src/daemon/command_parser_executor.cpp6
-rw-r--r--src/daemon/core.h1
-rw-r--r--src/daemon/daemon.cpp3
-rw-r--r--src/daemon/executor.cpp1
-rw-r--r--src/daemon/protocol.h2
-rw-r--r--src/daemon/rpc_command_executor.cpp4
-rw-r--r--src/debug_utilities/cn_deserialize.cpp8
-rw-r--r--src/debug_utilities/object_sizes.cpp2
-rw-r--r--src/mnemonics/CMakeLists.txt1
-rw-r--r--src/mnemonics/electrum-words.cpp2
-rw-r--r--src/p2p/CMakeLists.txt1
-rw-r--r--src/p2p/net_node.inl11
-rw-r--r--src/ringct/rctOps.cpp3
-rw-r--r--src/ringct/rctTypes.h2
-rw-r--r--src/rpc/CMakeLists.txt2
-rw-r--r--src/rpc/core_rpc_server.cpp37
-rw-r--r--src/rpc/daemon_handler.cpp4
-rw-r--r--src/rpc/message.cpp2
-rw-r--r--src/serialization/container.h113
-rw-r--r--src/serialization/crypto.h4
-rw-r--r--src/serialization/deque.h (renamed from src/common/memwipe.h)70
-rw-r--r--src/serialization/list.h72
-rw-r--r--src/serialization/serialization.h16
-rw-r--r--src/serialization/set.h85
-rw-r--r--src/serialization/unordered_set.h58
-rw-r--r--src/serialization/vector.h82
-rw-r--r--src/simplewallet/simplewallet.cpp449
-rw-r--r--src/simplewallet/simplewallet.h4
-rw-r--r--src/version.cpp.in2
-rw-r--r--src/wallet/api/unsigned_transaction.cpp4
-rw-r--r--src/wallet/api/utils.cpp5
-rw-r--r--src/wallet/api/wallet2_api.h1
-rw-r--r--src/wallet/node_rpc_proxy.cpp1
-rw-r--r--src/wallet/wallet2.cpp465
-rw-r--r--src/wallet/wallet2.h42
-rw-r--r--src/wallet/wallet_errors.h11
-rw-r--r--src/wallet/wallet_rpc_server.cpp525
-rw-r--r--src/wallet/wallet_rpc_server.h15
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h110
74 files changed, 1712 insertions, 1206 deletions
diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp
index 07b2451b0..7e77953c8 100644
--- a/src/blockchain_db/blockchain_db.cpp
+++ b/src/blockchain_db/blockchain_db.cpp
@@ -197,7 +197,7 @@ uint64_t BlockchainDB::add_block( const block& blk
{
// sanity
if (blk.tx_hashes.size() != txs.size())
- throw new std::runtime_error("Inconsistent tx/hashes sizes");
+ throw std::runtime_error("Inconsistent tx/hashes sizes");
block_txn_start(false);
@@ -283,7 +283,7 @@ block BlockchainDB::get_block_from_height(const uint64_t& height) const
blobdata bd = get_block_blob_from_height(height);
block b;
if (!parse_and_validate_block_from_blob(bd, b))
- throw new DB_ERROR("Failed to parse block from blob retrieved from the db");
+ throw DB_ERROR("Failed to parse block from blob retrieved from the db");
return b;
}
@@ -293,7 +293,7 @@ block BlockchainDB::get_block(const crypto::hash& h) const
blobdata bd = get_block_blob(h);
block b;
if (!parse_and_validate_block_from_blob(bd, b))
- throw new DB_ERROR("Failed to parse block from blob retrieved from the db");
+ throw DB_ERROR("Failed to parse block from blob retrieved from the db");
return b;
}
@@ -304,7 +304,7 @@ bool BlockchainDB::get_tx(const crypto::hash& h, cryptonote::transaction &tx) co
if (!get_tx_blob(h, bd))
return false;
if (!parse_and_validate_tx_from_blob(bd, tx))
- throw new DB_ERROR("Failed to parse transaction from blob retrieved from the db");
+ throw DB_ERROR("Failed to parse transaction from blob retrieved from the db");
return true;
}
@@ -313,7 +313,7 @@ transaction BlockchainDB::get_tx(const crypto::hash& h) const
{
transaction tx;
if (!get_tx(h, tx))
- throw new TX_DNE(std::string("tx with hash ").append(epee::string_tools::pod_to_hex(h)).append(" not found in db").c_str());
+ throw TX_DNE(std::string("tx with hash ").append(epee::string_tools::pod_to_hex(h)).append(" not found in db").c_str());
return tx;
}
diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h
index 88034a927..33c3341fa 100644
--- a/src/blockchain_db/blockchain_db.h
+++ b/src/blockchain_db/blockchain_db.h
@@ -1333,10 +1333,11 @@ public:
* @brief get a txpool transaction's metadata
*
* @param txid the transaction id of the transation to lookup
+ * @param meta the metadata to return
*
- * @return the metadata associated with that transaction
+ * @return true if the tx meta was found, false otherwise
*/
- virtual txpool_tx_meta_t get_txpool_tx_meta(const crypto::hash& txid) const = 0;
+ virtual bool get_txpool_tx_meta(const crypto::hash& txid, txpool_tx_meta_t &meta) const = 0;
/**
* @brief get a txpool transaction's blob
diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp
index 07a0e67b1..d19399bec 100644
--- a/src/blockchain_db/lmdb/db_lmdb.cpp
+++ b/src/blockchain_db/lmdb/db_lmdb.cpp
@@ -1621,7 +1621,7 @@ void BlockchainLMDB::remove_txpool_tx(const crypto::hash& txid)
}
}
-txpool_tx_meta_t BlockchainLMDB::get_txpool_tx_meta(const crypto::hash& txid) const
+bool BlockchainLMDB::get_txpool_tx_meta(const crypto::hash& txid, txpool_tx_meta_t &meta) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
@@ -1632,12 +1632,14 @@ txpool_tx_meta_t BlockchainLMDB::get_txpool_tx_meta(const crypto::hash& txid) co
MDB_val k = {sizeof(txid), (void *)&txid};
MDB_val v;
auto result = mdb_cursor_get(m_cur_txpool_meta, &k, &v, MDB_SET);
+ if (result == MDB_NOTFOUND)
+ return false;
if (result != 0)
throw1(DB_ERROR(lmdb_error("Error finding txpool tx meta: ", result).c_str()));
- const txpool_tx_meta_t meta = *(const txpool_tx_meta_t*)v.mv_data;
+ meta = *(const txpool_tx_meta_t*)v.mv_data;
TXN_POSTFIX_RDONLY();
- return meta;
+ return true;
}
bool BlockchainLMDB::get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const
@@ -2623,6 +2625,12 @@ bool BlockchainLMDB::batch_start(uint64_t batch_num_blocks, uint64_t batch_bytes
m_batch_active = true;
memset(&m_wcursors, 0, sizeof(m_wcursors));
+ if (m_tinfo.get())
+ {
+ if (m_tinfo->m_ti_rflags.m_rf_txn)
+ mdb_txn_reset(m_tinfo->m_ti_rtxn);
+ memset(&m_tinfo->m_ti_rflags, 0, sizeof(m_tinfo->m_ti_rflags));
+ }
LOG_PRINT_L3("batch transaction: begin");
return true;
@@ -2732,29 +2740,34 @@ void BlockchainLMDB::set_batch_transactions(bool batch_transactions)
bool BlockchainLMDB::block_rtxn_start(MDB_txn **mtxn, mdb_txn_cursors **mcur) const
{
bool ret = false;
+ mdb_threadinfo *tinfo;
if (m_write_txn && m_writer == boost::this_thread::get_id()) {
*mtxn = m_write_txn->m_txn;
*mcur = (mdb_txn_cursors *)&m_wcursors;
return ret;
}
- if (!m_tinfo.get())
+ /* Check for existing info and force reset if env doesn't match -
+ * only happens if env was opened/closed multiple times in same process
+ */
+ if (!(tinfo = m_tinfo.get()) || mdb_txn_env(tinfo->m_ti_rtxn) != m_env)
{
- m_tinfo.reset(new mdb_threadinfo);
- memset(&m_tinfo->m_ti_rcursors, 0, sizeof(m_tinfo->m_ti_rcursors));
- memset(&m_tinfo->m_ti_rflags, 0, sizeof(m_tinfo->m_ti_rflags));
- if (auto mdb_res = lmdb_txn_begin(m_env, NULL, MDB_RDONLY, &m_tinfo->m_ti_rtxn))
+ tinfo = new mdb_threadinfo;
+ m_tinfo.reset(tinfo);
+ memset(&tinfo->m_ti_rcursors, 0, sizeof(tinfo->m_ti_rcursors));
+ memset(&tinfo->m_ti_rflags, 0, sizeof(tinfo->m_ti_rflags));
+ if (auto mdb_res = lmdb_txn_begin(m_env, NULL, MDB_RDONLY, &tinfo->m_ti_rtxn))
throw0(DB_ERROR_TXN_START(lmdb_error("Failed to create a read transaction for the db: ", mdb_res).c_str()));
ret = true;
- } else if (!m_tinfo->m_ti_rflags.m_rf_txn)
+ } else if (!tinfo->m_ti_rflags.m_rf_txn)
{
- if (auto mdb_res = lmdb_txn_renew(m_tinfo->m_ti_rtxn))
+ if (auto mdb_res = lmdb_txn_renew(tinfo->m_ti_rtxn))
throw0(DB_ERROR_TXN_START(lmdb_error("Failed to renew a read transaction for the db: ", mdb_res).c_str()));
ret = true;
}
if (ret)
- m_tinfo->m_ti_rflags.m_rf_txn = true;
- *mtxn = m_tinfo->m_ti_rtxn;
- *mcur = &m_tinfo->m_ti_rcursors;
+ tinfo->m_ti_rflags.m_rf_txn = true;
+ *mtxn = tinfo->m_ti_rtxn;
+ *mcur = &tinfo->m_ti_rcursors;
if (ret)
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
@@ -2772,28 +2785,9 @@ void BlockchainLMDB::block_txn_start(bool readonly)
{
if (readonly)
{
- bool didit = false;
- if (m_write_txn && m_writer == boost::this_thread::get_id())
- return;
- if (!m_tinfo.get())
- {
- m_tinfo.reset(new mdb_threadinfo);
- memset(&m_tinfo->m_ti_rcursors, 0, sizeof(m_tinfo->m_ti_rcursors));
- memset(&m_tinfo->m_ti_rflags, 0, sizeof(m_tinfo->m_ti_rflags));
- if (auto mdb_res = lmdb_txn_begin(m_env, NULL, MDB_RDONLY, &m_tinfo->m_ti_rtxn))
- throw0(DB_ERROR_TXN_START(lmdb_error("Failed to create a read transaction for the db: ", mdb_res).c_str()));
- didit = true;
- } else if (!m_tinfo->m_ti_rflags.m_rf_txn)
- {
- if (auto mdb_res = lmdb_txn_renew(m_tinfo->m_ti_rtxn))
- throw0(DB_ERROR_TXN_START(lmdb_error("Failed to renew a read transaction for the db: ", mdb_res).c_str()));
- didit = true;
- }
- if (didit)
- {
- m_tinfo->m_ti_rflags.m_rf_txn = true;
- LOG_PRINT_L3("BlockchainLMDB::" << __func__ << " RO");
- }
+ MDB_txn *mtxn;
+ mdb_txn_cursors *mcur;
+ block_rtxn_start(&mtxn, &mcur);
return;
}
@@ -2818,6 +2812,12 @@ void BlockchainLMDB::block_txn_start(bool readonly)
throw0(DB_ERROR_TXN_START(lmdb_error("Failed to create a transaction for the db: ", mdb_res).c_str()));
}
memset(&m_wcursors, 0, sizeof(m_wcursors));
+ if (m_tinfo.get())
+ {
+ if (m_tinfo->m_ti_rflags.m_rf_txn)
+ mdb_txn_reset(m_tinfo->m_ti_rtxn);
+ memset(&m_tinfo->m_ti_rflags, 0, sizeof(m_tinfo->m_ti_rflags));
+ }
} else if (m_writer != boost::this_thread::get_id())
throw0(DB_ERROR_TXN_START((std::string("Attempted to start new write txn when batch txn already exists in ")+__FUNCTION__).c_str()));
}
@@ -2894,7 +2894,7 @@ uint64_t BlockchainLMDB::add_block(const block& blk, const size_t& block_size, c
{
BlockchainDB::add_block(blk, block_size, cumulative_difficulty, coins_generated, txs);
}
- catch (DB_ERROR_TXN_START& e)
+ catch (const DB_ERROR_TXN_START& e)
{
throw;
}
diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h
index 85b62b5db..ecd14f11b 100644
--- a/src/blockchain_db/lmdb/db_lmdb.h
+++ b/src/blockchain_db/lmdb/db_lmdb.h
@@ -246,7 +246,7 @@ public:
virtual uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const;
virtual bool txpool_has_tx(const crypto::hash &txid) const;
virtual void remove_txpool_tx(const crypto::hash& txid);
- virtual txpool_tx_meta_t get_txpool_tx_meta(const crypto::hash& txid) const;
+ virtual bool get_txpool_tx_meta(const crypto::hash& txid, txpool_tx_meta_t &meta) const;
virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const;
virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const;
virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)> f, bool include_blob = false, bool include_unrelayed_txes = true) const;
diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt
index bd32e0c55..6c55e8d2d 100644
--- a/src/blockchain_utilities/CMakeLists.txt
+++ b/src/blockchain_utilities/CMakeLists.txt
@@ -29,9 +29,9 @@
set(blocksdat "")
if(PER_BLOCK_CHECKPOINT)
if(APPLE)
- add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*)
+ add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*)
else()
- add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat)
+ add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat)
endif()
set(blocksdat "blocksdat.o")
endif()
diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp
index 70d1dd696..edb8881e0 100644
--- a/src/blockchain_utilities/blockchain_import.cpp
+++ b/src/blockchain_utilities/blockchain_import.cpp
@@ -459,7 +459,7 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path
// tx number 1: coinbase tx
// tx number 2 onwards: archived_txs
- for (transaction tx : archived_txs)
+ for (const transaction &tx : archived_txs)
{
// add blocks with verification.
// for Blockchain and blockchain_storage add_new_block().
@@ -594,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 = {"verify",
- "Verify blocks and transactions during import", true};
+ 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_batch = {"batch",
"Batch transactions for faster import", true};
const command_line::arg_descriptor<bool> arg_resume = {"resume",
diff --git a/src/blocks/CMakeLists.txt b/src/blocks/CMakeLists.txt
index 3a866af5b..9317d585b 100644
--- a/src/blocks/CMakeLists.txt
+++ b/src/blocks/CMakeLists.txt
@@ -30,8 +30,8 @@ if(APPLE)
add_library(blocks STATIC blockexports.c)
set_target_properties(blocks PROPERTIES LINKER_LANGUAGE C)
else()
- add_custom_command(OUTPUT blocks.o MAIN_DEPENDENCY blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocks.o blocks.dat)
- add_custom_command(OUTPUT testnet_blocks.o MAIN_DEPENDENCY testnet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/testnet_blocks.o testnet_blocks.dat)
+ add_custom_command(OUTPUT blocks.o MAIN_DEPENDENCY blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocks.o blocks.dat)
+ add_custom_command(OUTPUT testnet_blocks.o MAIN_DEPENDENCY testnet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/testnet_blocks.o testnet_blocks.dat)
add_library(blocks STATIC blocks.o testnet_blocks.o blockexports.c)
set_target_properties(blocks PROPERTIES LINKER_LANGUAGE C)
endif()
diff --git a/src/checkpoints/checkpoints.cpp b/src/checkpoints/checkpoints.cpp
index c66c4f5d6..67a313bc2 100644
--- a/src/checkpoints/checkpoints.cpp
+++ b/src/checkpoints/checkpoints.cpp
@@ -204,7 +204,7 @@ namespace cryptonote
return true;
}
- bool checkpoints::load_checkpoints_from_json(const std::string json_hashfile_fullpath)
+ bool checkpoints::load_checkpoints_from_json(const std::string &json_hashfile_fullpath)
{
boost::system::error_code errcode;
if (! (boost::filesystem::exists(json_hashfile_fullpath, errcode)))
@@ -218,7 +218,11 @@ namespace cryptonote
uint64_t prev_max_height = get_max_height();
LOG_PRINT_L1("Hard-coded max checkpoint height is " << prev_max_height);
t_hash_json hashes;
- epee::serialization::load_t_from_json_file(hashes, json_hashfile_fullpath);
+ if (!epee::serialization::load_t_from_json_file(hashes, json_hashfile_fullpath))
+ {
+ MERROR("Error loading checkpoints from " << json_hashfile_fullpath);
+ return false;
+ }
for (std::vector<t_hashline>::const_iterator it = hashes.hashlines.begin(); it != hashes.hashlines.end(); )
{
uint64_t height;
@@ -286,7 +290,7 @@ namespace cryptonote
return true;
}
- bool checkpoints::load_new_checkpoints(const std::string json_hashfile_fullpath, bool testnet, bool dns)
+ bool checkpoints::load_new_checkpoints(const std::string &json_hashfile_fullpath, bool testnet, bool dns)
{
bool result;
diff --git a/src/checkpoints/checkpoints.h b/src/checkpoints/checkpoints.h
index 3e034f6f0..83969f7b8 100644
--- a/src/checkpoints/checkpoints.h
+++ b/src/checkpoints/checkpoints.h
@@ -165,7 +165,7 @@ namespace cryptonote
*
* @return true if loading successful and no conflicts
*/
- bool load_new_checkpoints(const std::string json_hashfile_fullpath, bool testnet=false, bool dns=true);
+ bool load_new_checkpoints(const std::string &json_hashfile_fullpath, bool testnet=false, bool dns=true);
/**
* @brief load new checkpoints from json
@@ -174,7 +174,7 @@ namespace cryptonote
*
* @return true if loading successful and no conflicts
*/
- bool load_checkpoints_from_json(const std::string json_hashfile_fullpath);
+ bool load_checkpoints_from_json(const std::string &json_hashfile_fullpath);
/**
* @brief load new checkpoints from DNS
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 7ad08ea83..50887e35c 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -35,7 +35,6 @@ set(common_sources
download.cpp
util.cpp
i18n.cpp
- memwipe.c
password.cpp
perf_timer.cpp
threadpool.cpp
@@ -64,7 +63,6 @@ set(common_private_headers
util.h
varint.h
i18n.h
- memwipe.h
password.h
perf_timer.h
stack_trace.h
@@ -92,9 +90,5 @@ target_link_libraries(common
${OPENSSL_LIBRARIES}
${EXTRA_LIBRARIES})
-if(HAVE_C11)
-SET_PROPERTY(SOURCE memwipe.c PROPERTY COMPILE_FLAGS -std=c11)
-endif()
-
#monero_install_headers(common
# ${common_headers})
diff --git a/src/common/download.cpp b/src/common/download.cpp
index 28aac5a59..87814fa5e 100644
--- a/src/common/download.cpp
+++ b/src/common/download.cpp
@@ -33,6 +33,7 @@
#include <boost/thread/thread.hpp>
#include "cryptonote_config.h"
#include "include_base_utils.h"
+#include "file_io_utils.h"
#include "net/http_client.h"
#include "download.h"
@@ -74,9 +75,20 @@ namespace tools
try
{
boost::unique_lock<boost::mutex> lock(control->mutex);
- MINFO("Downloading " << control->uri << " to " << control->path);
+ std::ios_base::openmode mode = std::ios_base::out | std::ios_base::binary;
+ uint64_t existing_size = 0;
+ if (epee::file_io_utils::get_file_size(control->path, existing_size) && existing_size > 0)
+ {
+ MINFO("Resuming downloading " << control->uri << " to " << control->path << " from " << existing_size);
+ mode |= std::ios_base::app;
+ }
+ else
+ {
+ MINFO("Downloading " << control->uri << " to " << control->path);
+ mode |= std::ios_base::trunc;
+ }
std::ofstream f;
- f.open(control->path, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc);
+ f.open(control->path, mode);
if (!f.good()) {
MERROR("Failed to open file " << control->path);
control->result_cb(control->path, control->uri, control->success);
@@ -85,11 +97,13 @@ namespace tools
class download_client: public epee::net_utils::http::http_simple_client
{
public:
- download_client(download_async_handle control, std::ofstream &f):
- control(control), f(f), content_length(-1), total(0) {}
+ download_client(download_async_handle control, std::ofstream &f, uint64_t offset = 0):
+ control(control), f(f), content_length(-1), total(0), offset(offset) {}
virtual ~download_client() { f.close(); }
virtual bool on_header(const epee::net_utils::http::http_response_info &headers)
{
+ for (const auto &kv: headers.m_header_info.m_etc_fields)
+ MDEBUG("Header: " << kv.first << ": " << kv.second);
ssize_t length;
if (epee::string_tools::get_xtype_from_string(length, headers.m_header_info.m_content_length) && length >= 0)
{
@@ -104,6 +118,26 @@ namespace tools
return false;
}
}
+ if (offset > 0)
+ {
+ // we requested a range, so check if we're getting it, otherwise truncate
+ bool got_range = false;
+ const std::string prefix = "bytes=" + std::to_string(offset) + "-";
+ for (const auto &kv: headers.m_header_info.m_etc_fields)
+ {
+ if (kv.first == "Content-Range" && strncmp(kv.second.c_str(), prefix.c_str(), prefix.size()))
+ {
+ got_range = true;
+ break;
+ }
+ }
+ if (!got_range)
+ {
+ MWARNING("We did not get the requested range, downloading from start");
+ f.close();
+ f.open(control->path, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
+ }
+ }
return true;
}
virtual bool handle_target_data(std::string &piece_of_transfer)
@@ -130,7 +164,8 @@ namespace tools
std::ofstream &f;
ssize_t content_length;
size_t total;
- } client(control, f);
+ uint64_t offset;
+ } client(control, f, existing_size);
epee::net_utils::http::url_content u_c;
if (!epee::net_utils::parse_url(control->uri, u_c))
{
@@ -147,9 +182,10 @@ namespace tools
lock.unlock();
- uint16_t port = u_c.port ? u_c.port : 80;
+ bool ssl = u_c.schema == "https";
+ uint16_t port = u_c.port ? u_c.port : ssl ? 443 : 80;
MDEBUG("Connecting to " << u_c.host << ":" << port);
- client.set_server(u_c.host, std::to_string(port), boost::none);
+ client.set_server(u_c.host, std::to_string(port), boost::none, ssl);
if (!client.connect(std::chrono::seconds(30)))
{
boost::lock_guard<boost::mutex> lock(control->mutex);
@@ -159,7 +195,14 @@ namespace tools
}
MDEBUG("GETting " << u_c.uri);
const epee::net_utils::http::http_response_info *info = NULL;
- if (!client.invoke_get(u_c.uri, std::chrono::seconds(30), "", &info))
+ epee::net_utils::http::fields_list fields;
+ if (existing_size > 0)
+ {
+ const std::string range = "bytes=" + std::to_string(existing_size) + "-";
+ MDEBUG("Asking for range: " << range);
+ fields.push_back(std::make_pair("Range", range));
+ }
+ if (!client.invoke_get(u_c.uri, std::chrono::seconds(30), "", &info, fields))
{
boost::lock_guard<boost::mutex> lock(control->mutex);
MERROR("Failed to connect to " << control->uri);
@@ -189,7 +232,7 @@ namespace tools
MDEBUG("response body: " << info->m_body);
for (const auto &f: info->m_additional_fields)
MDEBUG("additional field: " << f.first << ": " << f.second);
- if (info->m_response_code != 200)
+ if (info->m_response_code != 200 && info->m_response_code != 206)
{
boost::lock_guard<boost::mutex> lock(control->mutex);
MERROR("Status code " << info->m_response_code);
diff --git a/src/common/i18n.cpp b/src/common/i18n.cpp
index 4a76e76fc..28a186bf0 100644
--- a/src/common/i18n.cpp
+++ b/src/common/i18n.cpp
@@ -35,6 +35,7 @@
#include "file_io_utils.h"
#include "common/util.h"
#include "common/i18n.h"
+#include "translation_files.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "i18n"
@@ -62,6 +63,7 @@ std::string i18n_get_language()
e = "en";
std::string language = e;
+ language = language.substr(0, language.find("."));
std::transform(language.begin(), language.end(), language.begin(), tolower);
return language;
}
@@ -137,25 +139,40 @@ int i18n_set_language(const char *directory, const char *base, std::string langu
i18n_log("Loading translations for language " << language);
boost::system::error_code ignored_ec;
- if (!boost::filesystem::exists(filename, ignored_ec)) {
+ if (boost::filesystem::exists(filename, ignored_ec)) {
+ if (!epee::file_io_utils::load_file_to_string(filename, contents)) {
+ i18n_log("Failed to load translations file: " << filename);
+ return -1;
+ }
+ } else {
i18n_log("Translations file not found: " << filename);
- const char *underscore = strchr(language.c_str(), '_');
- if (underscore) {
- std::string fallback_language = std::string(language, 0, underscore - language.c_str());
- filename = std::string(directory) + "/" + base + "_" + fallback_language + ".qm";
- i18n_log("Not found, loading translations for language " << fallback_language);
- if (!boost::filesystem::exists(filename, ignored_ec)) {
- i18n_log("Translations file not found: " << filename);
+ filename = std::string(base) + "_" + language + ".qm";
+ if (!find_embedded_file(filename, contents)) {
+ i18n_log("Embedded translations file not found: " << filename);
+ const char *underscore = strchr(language.c_str(), '_');
+ if (underscore) {
+ std::string fallback_language = std::string(language, 0, underscore - language.c_str());
+ filename = std::string(directory) + "/" + base + "_" + fallback_language + ".qm";
+ i18n_log("Loading translations for language " << fallback_language);
+ if (boost::filesystem::exists(filename, ignored_ec)) {
+ if (!epee::file_io_utils::load_file_to_string(filename, contents)) {
+ i18n_log("Failed to load translations file: " << filename);
+ return -1;
+ }
+ } else {
+ i18n_log("Translations file not found: " << filename);
+ filename = std::string(base) + "_" + fallback_language + ".qm";
+ if (!find_embedded_file(filename, contents)) {
+ i18n_log("Embedded translations file not found: " << filename);
+ return -1;
+ }
+ }
+ } else {
return -1;
}
}
}
- if (!epee::file_io_utils::load_file_to_string(filename, contents)) {
- i18n_log("Failed to load translations file: " << filename);
- return -1;
- }
-
data = (const unsigned char*)contents.c_str();
datalen = contents.size();
idx = 0;
diff --git a/src/common/memwipe.c b/src/common/memwipe.c
deleted file mode 100644
index da7e9f346..000000000
--- a/src/common/memwipe.c
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright (c) 2017, The Monero Project
-//
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without modification, are
-// permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this list of
-// conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice, this list
-// of conditions and the following disclaimer in the documentation and/or other
-// materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its contributors may be
-// used to endorse or promote products derived from this software without specific
-// prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
-// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
-// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
-// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-// Parts of this file Copyright (c) 2009-2015 The Bitcoin Core developers
-
-#define __STDC_WANT_LIB_EXT1__ 1
-#include <string.h>
-#include <stdlib.h>
-#ifdef HAVE_EXPLICIT_BZERO
-#include <strings.h>
-#endif
-#include "memwipe.h"
-
-#if defined(_MSC_VER)
-#define SCARECROW \
- __asm;
-#else
-#define SCARECROW \
- __asm__ __volatile__("" : : "r"(ptr) : "memory");
-#endif
-
-#ifdef HAVE_MEMSET_S
-
-void *memwipe(void *ptr, size_t n)
-{
- if (memset_s(ptr, n, 0, n))
- {
- abort();
- }
- SCARECROW // might as well...
- return ptr;
-}
-
-#elif defined HAVE_EXPLICIT_BZERO
-
-void *memwipe(void *ptr, size_t n)
-{
- explicit_bzero(ptr, n);
- SCARECROW
- return ptr;
-}
-
-#else
-
-/* The memory_cleanse implementation is taken from Bitcoin */
-
-/* Compilers have a bad habit of removing "superfluous" memset calls that
- * are trying to zero memory. For example, when memset()ing a buffer and
- * then free()ing it, the compiler might decide that the memset is
- * unobservable and thus can be removed.
- *
- * Previously we used OpenSSL which tried to stop this by a) implementing
- * memset in assembly on x86 and b) putting the function in its own file
- * for other platforms.
- *
- * This change removes those tricks in favour of using asm directives to
- * scare the compiler away. As best as our compiler folks can tell, this is
- * sufficient and will continue to be so.
- *
- * Adam Langley <agl@google.com>
- * Commit: ad1907fe73334d6c696c8539646c21b11178f20f
- * BoringSSL (LICENSE: ISC)
- */
-static void memory_cleanse(void *ptr, size_t len)
-{
- memset(ptr, 0, len);
-
- /* As best as we can tell, this is sufficient to break any optimisations that
- might try to eliminate "superfluous" memsets. If there's an easy way to
- detect memset_s, it would be better to use that. */
- SCARECROW
-}
-
-void *memwipe(void *ptr, size_t n)
-{
- memory_cleanse(ptr, n);
- SCARECROW
- return ptr;
-}
-
-#endif
diff --git a/src/common/password.cpp b/src/common/password.cpp
index dc0856160..011123300 100644
--- a/src/common/password.cpp
+++ b/src/common/password.cpp
@@ -46,7 +46,7 @@
#include "readline_buffer.h"
#endif
-#include "common/memwipe.h"
+#include "memwipe.h"
namespace
{
diff --git a/src/common/stack_trace.cpp b/src/common/stack_trace.cpp
index bcdf72b60..ed1093309 100644
--- a/src/common/stack_trace.cpp
+++ b/src/common/stack_trace.cpp
@@ -28,7 +28,10 @@
#if !defined __GNUC__ || defined __MINGW32__ || defined __MINGW64__ || defined __ANDROID__
#define USE_UNWIND
+#else
+#define ELPP_FEATURE_CRASH_LOG 1
#endif
+#include "easylogging++/easylogging++.h"
#include <stdexcept>
#ifdef USE_UNWIND
diff --git a/src/common/threadpool.cpp b/src/common/threadpool.cpp
index 20c5765b0..5d749e08e 100644
--- a/src/common/threadpool.cpp
+++ b/src/common/threadpool.cpp
@@ -34,6 +34,8 @@
#include "cryptonote_config.h"
#include "common/util.h"
+static __thread int depth = 0;
+
namespace tools
{
threadpool::threadpool() : running(true), active(0) {
@@ -60,11 +62,13 @@ threadpool::~threadpool() {
void threadpool::submit(waiter *obj, std::function<void()> f) {
entry e = {obj, f};
boost::unique_lock<boost::mutex> lock(mutex);
- if (active == max && !queue.empty()) {
+ if ((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;
f();
+ --depth;
} else {
if (obj)
obj->inc();
@@ -106,7 +110,9 @@ void threadpool::run() {
e = queue.front();
queue.pop_front();
lock.unlock();
+ ++depth;
e.f();
+ --depth;
if (e.wo)
e.wo->dec();
diff --git a/src/common/updates.cpp b/src/common/updates.cpp
index eff6754af..2d9c2d89c 100644
--- a/src/common/updates.cpp
+++ b/src/common/updates.cpp
@@ -99,7 +99,7 @@ namespace tools
std::string get_update_url(const std::string &software, const std::string &subdir, const std::string &buildtag, const std::string &version, bool user)
{
- const char *base = user ? "https://downloads.getmonero.org/" : "http://updates.getmonero.org/";
+ const char *base = user ? "https://downloads.getmonero.org/" : "https://updates.getmonero.org/";
#ifdef _WIN32
static const char *extension = strncmp(buildtag.c_str(), "install-", 8) ? ".zip" : ".exe";
#else
diff --git a/src/common/util.cpp b/src/common/util.cpp
index d9e12ffe4..a4a435104 100644
--- a/src/common/util.cpp
+++ b/src/common/util.cpp
@@ -39,6 +39,7 @@
#include "wipeable_string.h"
using namespace epee;
+#include "crypto/crypto.h"
#include "util.h"
#include "memwipe.h"
#include "cryptonote_config.h"
@@ -635,13 +636,13 @@ std::string get_nix_version_display_string()
int vercmp(const char *v0, const char *v1)
{
std::vector<std::string> f0, f1;
- boost::split(f0, v0, boost::is_any_of("."));
- boost::split(f1, v1, boost::is_any_of("."));
- while (f0.size() < f1.size())
- f0.push_back("0");
- while (f1.size() < f0.size())
- f1.push_back("0");
- for (size_t i = 0; i < f0.size(); ++i) {
+ boost::split(f0, v0, boost::is_any_of(".-"));
+ boost::split(f1, v1, boost::is_any_of(".-"));
+ for (size_t i = 0; i < std::max(f0.size(), f1.size()); ++i) {
+ if (i >= f0.size())
+ return -1;
+ if (i >= f1.size())
+ return 1;
int f0i = atoi(f0[i].c_str()), f1i = atoi(f1[i].c_str());
int n = f0i - f1i;
if (n)
diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt
index 1e06a0dfd..764b30273 100644
--- a/src/crypto/CMakeLists.txt
+++ b/src/crypto/CMakeLists.txt
@@ -29,7 +29,7 @@
set(crypto_sources
aesb.c
blake256.c
- chacha8.c
+ chacha.c
crypto-ops-data.c
crypto-ops.c
crypto.cpp
@@ -51,7 +51,7 @@ set(crypto_headers)
set(crypto_private_headers
blake256.h
- chacha8.h
+ chacha.h
crypto-ops.h
crypto.h
generic-ops.h
@@ -76,6 +76,7 @@ monero_add_library(cncrypto
${crypto_private_headers})
target_link_libraries(cncrypto
PUBLIC
+ epee
${Boost_SYSTEM_LIBRARY}
PRIVATE
${EXTRA_LIBRARIES})
diff --git a/src/crypto/blake256.c b/src/crypto/blake256.c
index 1e43f9c4d..95b2a6927 100644
--- a/src/crypto/blake256.c
+++ b/src/crypto/blake256.c
@@ -157,7 +157,7 @@ void blake256_update(state *S, const uint8_t *data, uint64_t datalen) {
int left = S->buflen >> 3;
int fill = 64 - left;
- if (left && (((datalen >> 3) & 0x3F) >= (unsigned) fill)) {
+ if (left && (((datalen >> 3)) >= (unsigned) fill)) {
memcpy((void *) (S->buf + left), (void *) data, fill);
S->t[0] += 512;
if (S->t[0] == 0) S->t[1]++;
diff --git a/src/crypto/chacha8.c b/src/crypto/chacha.c
index df135af59..f573083be 100644
--- a/src/crypto/chacha8.c
+++ b/src/crypto/chacha.c
@@ -8,7 +8,7 @@ Public domain.
#include <stdio.h>
#include <sys/param.h>
-#include "chacha8.h"
+#include "chacha.h"
#include "common/int-util.h"
#include "warnings.h"
@@ -40,7 +40,7 @@ static const char sigma[] = "expand 32-byte k";
DISABLE_GCC_AND_CLANG_WARNING(strict-aliasing)
-void chacha8(const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher) {
+static void chacha(unsigned rounds, const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher) {
uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
char* ctarget = 0;
@@ -89,7 +89,7 @@ void chacha8(const void* data, size_t length, const uint8_t* key, const uint8_t*
x13 = j13;
x14 = j14;
x15 = j15;
- for (i = 8;i > 0;i -= 2) {
+ for (i = rounds;i > 0;i -= 2) {
QUARTERROUND( x0, x4, x8,x12)
QUARTERROUND( x1, x5, x9,x13)
QUARTERROUND( x2, x6,x10,x14)
@@ -168,3 +168,13 @@ void chacha8(const void* data, size_t length, const uint8_t* key, const uint8_t*
data = (uint8_t*)data + 64;
}
}
+
+void chacha8(const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher)
+{
+ chacha(8, data, length, key, iv, cipher);
+}
+
+void chacha20(const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher)
+{
+ chacha(20, data, length, key, iv, cipher);
+}
diff --git a/src/crypto/chacha8.h b/src/crypto/chacha.h
index dcbe6a933..c11e4aa2f 100644
--- a/src/crypto/chacha8.h
+++ b/src/crypto/chacha.h
@@ -33,46 +33,51 @@
#include <stdint.h>
#include <stddef.h>
-#define CHACHA8_KEY_SIZE 32
-#define CHACHA8_IV_SIZE 8
+#define CHACHA_KEY_SIZE 32
+#define CHACHA_IV_SIZE 8
#if defined(__cplusplus)
#include <memory.h>
-#include "common/memwipe.h"
+#include "memwipe.h"
#include "hash.h"
namespace crypto {
extern "C" {
#endif
void chacha8(const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher);
+ void chacha20(const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher);
#if defined(__cplusplus)
}
- using chacha8_key = tools::scrubbed_arr<uint8_t, CHACHA8_KEY_SIZE>;
+ using chacha_key = tools::scrubbed_arr<uint8_t, CHACHA_KEY_SIZE>;
#pragma pack(push, 1)
- // MS VC 2012 doesn't interpret `class chacha8_iv` as POD in spite of [9.0.10], so it is a struct
- struct chacha8_iv {
- uint8_t data[CHACHA8_IV_SIZE];
+ // MS VC 2012 doesn't interpret `class chacha_iv` as POD in spite of [9.0.10], so it is a struct
+ struct chacha_iv {
+ uint8_t data[CHACHA_IV_SIZE];
};
#pragma pack(pop)
- static_assert(sizeof(chacha8_key) == CHACHA8_KEY_SIZE && sizeof(chacha8_iv) == CHACHA8_IV_SIZE, "Invalid structure size");
+ static_assert(sizeof(chacha_key) == CHACHA_KEY_SIZE && sizeof(chacha_iv) == CHACHA_IV_SIZE, "Invalid structure size");
- inline void chacha8(const void* data, std::size_t length, const chacha8_key& key, const chacha8_iv& iv, char* cipher) {
+ inline void chacha8(const void* data, std::size_t length, const chacha_key& key, const chacha_iv& iv, char* cipher) {
chacha8(data, length, key.data(), reinterpret_cast<const uint8_t*>(&iv), cipher);
}
- inline void generate_chacha8_key(const void *data, size_t size, chacha8_key& key) {
- static_assert(sizeof(chacha8_key) <= sizeof(hash), "Size of hash must be at least that of chacha8_key");
+ inline void chacha20(const void* data, std::size_t length, const chacha_key& key, const chacha_iv& iv, char* cipher) {
+ chacha20(data, length, key.data(), reinterpret_cast<const uint8_t*>(&iv), cipher);
+ }
+
+ inline void generate_chacha_key(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());
memcpy(&key, pwd_hash.data(), sizeof(key));
}
- inline void generate_chacha8_key(std::string password, chacha8_key& key) {
- return generate_chacha8_key(password.data(), password.size(), key);
+ inline void generate_chacha_key(std::string password, chacha_key& key) {
+ return generate_chacha_key(password.data(), password.size(), key);
}
}
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index 0ce5e6d7a..a929302c1 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -41,7 +41,7 @@
#include "common/pod-class.h"
#include "common/util.h"
-#include "common/memwipe.h"
+#include "memwipe.h"
#include "generic-ops.h"
#include "hex.h"
#include "span.h"
diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h
index 89dda8c3d..c81901f4e 100644
--- a/src/cryptonote_basic/cryptonote_basic.h
+++ b/src/cryptonote_basic/cryptonote_basic.h
@@ -36,7 +36,6 @@
#include <cstring> // memcmp
#include <sstream>
#include <atomic>
-#include "serialization/serialization.h"
#include "serialization/variant.h"
#include "serialization/vector.h"
#include "serialization/binary_archive.h"
@@ -259,7 +258,7 @@ namespace cryptonote
ar.tag("rctsig_prunable");
ar.begin_object();
r = rct_signatures.p.serialize_rctsig_prunable(ar, rct_signatures.type, vin.size(), vout.size(),
- vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(vin[0]).key_offsets.size() - 1 : 0);
+ vin.size() > 0 && vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(vin[0]).key_offsets.size() - 1 : 0);
if (!r || !ar.stream().good()) return false;
ar.end_object();
}
diff --git a/src/cryptonote_basic/cryptonote_basic_impl.cpp b/src/cryptonote_basic/cryptonote_basic_impl.cpp
index 1183fda06..929be0d5a 100644
--- a/src/cryptonote_basic/cryptonote_basic_impl.cpp
+++ b/src/cryptonote_basic/cryptonote_basic_impl.cpp
@@ -34,7 +34,7 @@ using namespace epee;
#include "cryptonote_basic_impl.h"
#include "string_tools.h"
#include "serialization/binary_utils.h"
-#include "serialization/vector.h"
+#include "serialization/container.h"
#include "cryptonote_format_utils.h"
#include "cryptonote_config.h"
#include "misc_language.h"
diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp
index 8f7ab94db..5f6dc3bd6 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.cpp
+++ b/src/cryptonote_basic/cryptonote_format_utils.cpp
@@ -35,6 +35,7 @@ using namespace epee;
#include <boost/algorithm/string.hpp>
#include "wipeable_string.h"
#include "string_tools.h"
+#include "serialization/string.h"
#include "cryptonote_format_utils.h"
#include "cryptonote_config.h"
#include "crypto/crypto.h"
@@ -630,17 +631,21 @@ namespace cryptonote
bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::public_key& tx_pub_key, const std::vector<crypto::public_key>& additional_tx_pub_keys, size_t output_index)
{
crypto::key_derivation derivation;
- generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation);
+ bool r = generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation);
+ CHECK_AND_ASSERT_MES(r, false, "Failed to generate key derivation");
crypto::public_key pk;
- derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk);
+ r = derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk);
+ CHECK_AND_ASSERT_MES(r, false, "Failed to derive public key");
if (pk == out_key.key)
return true;
// try additional tx pubkeys if available
if (!additional_tx_pub_keys.empty())
{
CHECK_AND_ASSERT_MES(output_index < additional_tx_pub_keys.size(), false, "wrong number of additional tx pubkeys");
- generate_key_derivation(additional_tx_pub_keys[output_index], acc.m_view_secret_key, derivation);
- derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk);
+ r = generate_key_derivation(additional_tx_pub_keys[output_index], acc.m_view_secret_key, derivation);
+ CHECK_AND_ASSERT_MES(r, false, "Failed to generate key derivation");
+ r = derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk);
+ CHECK_AND_ASSERT_MES(r, false, "Failed to derive public key");
return pk == out_key.key;
}
return false;
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 123bd194b..4af987c3b 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -321,6 +321,7 @@ bool Blockchain::init(BlockchainDB* db, const bool testnet, bool offline, const
if (!db->is_open())
{
LOG_ERROR("Attempted to init Blockchain with unopened DB");
+ delete db;
return false;
}
@@ -471,7 +472,7 @@ bool Blockchain::deinit()
// memory operation), otherwise we may cause a loop.
if (m_db == NULL)
{
- throw new DB_ERROR("The db pointer is null in Blockchain, the blockchain may be corrupt!");
+ throw DB_ERROR("The db pointer is null in Blockchain, the blockchain may be corrupt!");
}
try
@@ -489,7 +490,9 @@ bool Blockchain::deinit()
}
delete m_hardfork;
+ m_hardfork = NULL;
delete m_db;
+ m_db = NULL;
return true;
}
//------------------------------------------------------------------
@@ -2050,49 +2053,6 @@ bool Blockchain::get_transactions(const t_ids_container& txs_ids, t_tx_container
return true;
}
//------------------------------------------------------------------
-void Blockchain::print_blockchain(uint64_t start_index, uint64_t end_index) const
-{
- LOG_PRINT_L3("Blockchain::" << __func__);
- std::stringstream ss;
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- auto h = m_db->height();
- if(start_index > h)
- {
- MERROR("Wrong starter index set: " << start_index << ", expected max index " << h);
- return;
- }
-
- for(size_t i = start_index; i <= h && i != end_index; i++)
- {
- ss << "height " << i << ", timestamp " << m_db->get_block_timestamp(i) << ", cumul_dif " << m_db->get_block_cumulative_difficulty(i) << ", size " << m_db->get_block_size(i) << "\nid\t\t" << m_db->get_block_hash_from_height(i) << "\ndifficulty\t\t" << m_db->get_block_difficulty(i) << ", nonce " << m_db->get_block_from_height(i).nonce << ", tx_count " << m_db->get_block_from_height(i).tx_hashes.size() << std::endl;
- }
- MCINFO("globlal", "Current blockchain:" << std::endl << ss.str());
-}
-//------------------------------------------------------------------
-void Blockchain::print_blockchain_index() const
-{
- LOG_PRINT_L3("Blockchain::" << __func__);
- std::stringstream ss;
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- auto height = m_db->height();
- if (height != 0)
- {
- for(uint64_t i = 0; i <= height; i++)
- {
- ss << "height: " << i << ", hash: " << m_db->get_block_hash_from_height(i);
- }
- }
-
- MINFO("Current blockchain index:" << std::endl << ss.str());
-}
-//------------------------------------------------------------------
-//TODO: remove this function and references to it
-void Blockchain::print_blockchain_outs(const std::string& file) const
-{
- LOG_PRINT_L3("Blockchain::" << __func__);
- return;
-}
-//------------------------------------------------------------------
// 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.
@@ -2338,7 +2298,7 @@ void Blockchain::on_new_tx_from_block(const cryptonote::transaction &tx)
TIME_MEASURE_FINISH(a);
if(m_show_time_stats)
{
- size_t ring_size = tx.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(tx.vin[0]).key_offsets.size() : 0;
+ size_t ring_size = !tx.vin.empty() && tx.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(tx.vin[0]).key_offsets.size() : 0;
MINFO("HASH: " << "-" << " I/M/O: " << tx.vin.size() << "/" << ring_size << "/" << tx.vout.size() << " H: " << 0 << " chcktx: " << a);
}
}
@@ -2373,7 +2333,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, uint64_t& max_used_block_heigh
TIME_MEASURE_FINISH(a);
if(m_show_time_stats)
{
- size_t ring_size = tx.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(tx.vin[0]).key_offsets.size() : 0;
+ size_t ring_size = !tx.vin.empty() && tx.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(tx.vin[0]).key_offsets.size() : 0;
MINFO("HASH: " << get_transaction_hash(tx) << " I/M/O: " << tx.vin.size() << "/" << ring_size << "/" << tx.vout.size() << " H: " << max_used_block_height << " ms: " << a + m_fake_scan_time << " B: " << get_object_blobsize(tx));
}
if (!res)
@@ -2466,6 +2426,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
// mixRing - full and simple store it in opposite ways
if (rv.type == rct::RCTTypeFull || rv.type == rct::RCTTypeFullBulletproof)
{
+ CHECK_AND_ASSERT_MES(!pubkeys.empty() && !pubkeys[0].empty(), false, "empty pubkeys");
rv.mixRing.resize(pubkeys[0].size());
for (size_t m = 0; m < pubkeys[0].size(); ++m)
rv.mixRing[m].clear();
@@ -2480,6 +2441,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
}
else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeSimpleBulletproof)
{
+ CHECK_AND_ASSERT_MES(!pubkeys.empty() && !pubkeys[0].empty(), false, "empty pubkeys");
rv.mixRing.resize(pubkeys.size());
for (size_t n = 0; n < pubkeys.size(); ++n)
{
@@ -2811,7 +2773,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
}
for (size_t n = 0; n < tx.vin.size(); ++n)
{
- if (memcmp(&boost::get<txin_to_key>(tx.vin[n]).k_image, &rv.p.MGs[n].II[0], 32))
+ if (rv.p.MGs[n].II.empty() || memcmp(&boost::get<txin_to_key>(tx.vin[n]).k_image, &rv.p.MGs[n].II[0], 32))
{
MERROR_VER("Failed to check ringct signatures: mismatched key image");
return false;
@@ -2864,7 +2826,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
MERROR_VER("Failed to check ringct signatures: Bad MGs size");
return false;
}
- if (rv.p.MGs[0].II.size() != tx.vin.size())
+ if (rv.p.MGs.empty() || rv.p.MGs[0].II.size() != tx.vin.size())
{
MERROR_VER("Failed to check ringct signatures: mismatched II/vin sizes");
return false;
@@ -4236,9 +4198,9 @@ uint64_t Blockchain::get_txpool_tx_count(bool include_unrelayed_txes) const
return m_db->get_txpool_tx_count(include_unrelayed_txes);
}
-txpool_tx_meta_t Blockchain::get_txpool_tx_meta(const crypto::hash& txid) const
+bool Blockchain::get_txpool_tx_meta(const crypto::hash& txid, txpool_tx_meta_t &meta) const
{
- return m_db->get_txpool_tx_meta(txid);
+ return m_db->get_txpool_tx_meta(txid, meta);
}
bool Blockchain::get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index b76d0555f..25e573a2c 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -680,32 +680,6 @@ namespace cryptonote
//debug functions
/**
- * @brief prints data about a snippet of the blockchain
- *
- * if start_index is greater than the blockchain height, do nothing
- *
- * @param start_index height on chain to start at
- * @param end_index height on chain to end at
- */
- void print_blockchain(uint64_t start_index, uint64_t end_index) const;
-
- /**
- * @brief prints every block's hash
- *
- * WARNING: This function will absolutely crush a terminal in prints, so
- * it is recommended to redirect this output to a log file (or null sink
- * if a log file is already set up, as should be the default)
- */
- void print_blockchain_index() const;
-
- /**
- * @brief currently does nothing, candidate for removal
- *
- * @param file
- */
- void print_blockchain_outs(const std::string& file) const;
-
- /**
* @brief check the blockchain against a set of checkpoints
*
* If a block fails a checkpoint and enforce is enabled, the blockchain
@@ -940,7 +914,7 @@ namespace cryptonote
void update_txpool_tx(const crypto::hash &txid, const txpool_tx_meta_t &meta);
void remove_txpool_tx(const crypto::hash &txid);
uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const;
- txpool_tx_meta_t get_txpool_tx_meta(const crypto::hash& txid) const;
+ bool get_txpool_tx_meta(const crypto::hash& txid, txpool_tx_meta_t &meta) const;
bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const;
cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const;
bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false, bool include_unrelayed_txes = true) const;
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 415657f9c..adbc727b0 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -47,6 +47,7 @@ using namespace epee;
#include "cryptonote_config.h"
#include "cryptonote_tx_utils.h"
#include "misc_language.h"
+#include "file_io_utils.h"
#include <csignal>
#include "checkpoints/checkpoints.h"
#include "ringct/rctTypes.h"
@@ -377,7 +378,7 @@ namespace cryptonote
// folder might not be a directory, etc, etc
catch (...) { }
- BlockchainDB* db = new_db(db_type);
+ std::unique_ptr<BlockchainDB> db(new_db(db_type));
if (db == NULL)
{
LOG_ERROR("Attempted to use non-existent database type");
@@ -468,7 +469,7 @@ namespace cryptonote
m_blockchain_storage.set_user_options(blocks_threads,
blocks_per_sync, sync_mode, fast_sync);
- r = m_blockchain_storage.init(db, m_testnet, m_offline, test_options);
+ r = m_blockchain_storage.init(db.release(), m_testnet, m_offline, test_options);
r = m_mempool.init();
CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool");
@@ -1052,21 +1053,6 @@ namespace cryptonote
return m_blockchain_storage.find_blockchain_supplement(req_start_block, qblock_ids, blocks, total_height, start_height, max_count);
}
//-----------------------------------------------------------------------------------------------
- void core::print_blockchain(uint64_t start_index, uint64_t end_index) const
- {
- m_blockchain_storage.print_blockchain(start_index, end_index);
- }
- //-----------------------------------------------------------------------------------------------
- void core::print_blockchain_index() const
- {
- m_blockchain_storage.print_blockchain_index();
- }
- //-----------------------------------------------------------------------------------------------
- void core::print_blockchain_outs(const std::string& file)
- {
- m_blockchain_storage.print_blockchain_outs(file);
- }
- //-----------------------------------------------------------------------------------------------
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
{
return m_blockchain_storage.get_random_outs_for_amounts(req, res);
@@ -1452,27 +1438,56 @@ namespace cryptonote
if (!tools::sha256sum(path.string(), file_hash) || (hash != epee::string_tools::pod_to_hex(file_hash)))
{
MCDEBUG("updates", "We don't have that file already, downloading");
+ const std::string tmppath = path.string() + ".tmp";
+ if (epee::file_io_utils::is_file_exist(tmppath))
+ {
+ MCDEBUG("updates", "We have part of the file already, resuming download");
+ }
m_last_update_length = 0;
- m_update_download = tools::download_async(path.string(), url, [this, hash](const std::string &path, const std::string &uri, bool success) {
+ m_update_download = tools::download_async(tmppath, url, [this, hash, path](const std::string &tmppath, const std::string &uri, bool success) {
+ bool remove = false, good = true;
if (success)
{
crypto::hash file_hash;
- if (!tools::sha256sum(path, file_hash))
+ if (!tools::sha256sum(tmppath, file_hash))
{
- MCERROR("updates", "Failed to hash " << path);
+ MCERROR("updates", "Failed to hash " << tmppath);
+ remove = true;
+ good = false;
}
- if (hash != epee::string_tools::pod_to_hex(file_hash))
+ else if (hash != epee::string_tools::pod_to_hex(file_hash))
{
MCERROR("updates", "Download from " << uri << " does not match the expected hash");
+ remove = true;
+ good = false;
}
- MCLOG_CYAN(el::Level::Info, "updates", "New version downloaded to " << path);
}
else
{
MCERROR("updates", "Failed to download " << uri);
+ good = false;
}
boost::unique_lock<boost::mutex> lock(m_update_mutex);
m_update_download = 0;
+ if (success && !remove)
+ {
+ std::error_code e = tools::replace_file(tmppath, path.string());
+ if (e)
+ {
+ MCERROR("updates", "Failed to rename downloaded file");
+ good = false;
+ }
+ }
+ else if (remove)
+ {
+ if (!boost::filesystem::remove(tmppath))
+ {
+ MCERROR("updates", "Failed to remove invalid downloaded file");
+ good = false;
+ }
+ }
+ if (good)
+ MCLOG_CYAN(el::Level::Info, "updates", "New version downloaded to " << path.string());
}, [this](const std::string &path, const std::string &uri, size_t length, ssize_t content_length) {
if (length >= m_last_update_length + 1024 * 1024 * 10)
{
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index 9f84ed303..adc201fb5 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -601,20 +601,6 @@ namespace cryptonote
const Blockchain& get_blockchain_storage()const{return m_blockchain_storage;}
/**
- * @copydoc Blockchain::print_blockchain
- *
- * @note see Blockchain::print_blockchain
- */
- void print_blockchain(uint64_t start_index, uint64_t end_index) const;
-
- /**
- * @copydoc Blockchain::print_blockchain_index
- *
- * @note see Blockchain::print_blockchain_index
- */
- void print_blockchain_index() const;
-
- /**
* @copydoc tx_memory_pool::print_pool
*
* @note see tx_memory_pool::print_pool
@@ -622,13 +608,6 @@ namespace cryptonote
std::string print_pool(bool short_format) const;
/**
- * @copydoc Blockchain::print_blockchain_outs
- *
- * @note see Blockchain::print_blockchain_outs
- */
- void print_blockchain_outs(const std::string& file);
-
- /**
* @copydoc miner::on_synchronized
*
* @note see miner::on_synchronized
diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp
index 89f24a4d4..916b1e05a 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.cpp
+++ b/src/cryptonote_core/cryptonote_tx_utils.cpp
@@ -190,6 +190,12 @@ namespace cryptonote
//---------------------------------------------------------------
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)
{
+ if (sources.empty())
+ {
+ LOG_ERROR("Empty sources");
+ return false;
+ }
+
std::vector<rct::key> amount_keys;
tx.set_null();
amount_keys.clear();
diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp
index e6f217463..8773c1f74 100644
--- a/src/cryptonote_core/tx_pool.cpp
+++ b/src/cryptonote_core/tx_pool.cpp
@@ -371,7 +371,12 @@ namespace cryptonote
try
{
LockedTXN lock(m_blockchain);
- txpool_tx_meta_t meta = m_blockchain.get_txpool_tx_meta(id);
+ txpool_tx_meta_t meta;
+ if (!m_blockchain.get_txpool_tx_meta(id, meta))
+ {
+ MERROR("Failed to find tx in txpool");
+ return false;
+ }
cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(id);
if (!parse_and_validate_tx_from_blob(txblob, tx))
{
@@ -514,10 +519,13 @@ namespace cryptonote
{
try
{
- txpool_tx_meta_t meta = m_blockchain.get_txpool_tx_meta(it->first);
- meta.relayed = true;
- meta.last_relayed_time = now;
- m_blockchain.update_txpool_tx(it->first, meta);
+ txpool_tx_meta_t meta;
+ if (m_blockchain.get_txpool_tx_meta(it->first, meta))
+ {
+ meta.relayed = true;
+ meta.last_relayed_time = now;
+ m_blockchain.update_txpool_tx(it->first, meta);
+ }
}
catch (const std::exception &e)
{
@@ -696,7 +704,11 @@ namespace cryptonote
{
try
{
- meta = m_blockchain.get_txpool_tx_meta(tx_id_hash);
+ if (!m_blockchain.get_txpool_tx_meta(tx_id_hash, meta))
+ {
+ MERROR("Failed to get tx meta from txpool");
+ return false;
+ }
if (!meta.relayed)
// Do not include that transaction if in restricted mode and it's not relayed
continue;
@@ -918,7 +930,13 @@ namespace cryptonote
{
for (const crypto::hash &txid: it->second)
{
- txpool_tx_meta_t meta = m_blockchain.get_txpool_tx_meta(txid);
+ txpool_tx_meta_t meta;
+ if (!m_blockchain.get_txpool_tx_meta(txid, meta))
+ {
+ MERROR("Failed to find tx meta in txpool");
+ // continue, not fatal
+ continue;
+ }
if (!meta.double_spend_seen)
{
MDEBUG("Marking " << txid << " as double spending " << itk.k_image);
@@ -998,7 +1016,12 @@ namespace cryptonote
auto sorted_it = m_txs_by_fee_and_receive_time.begin();
while (sorted_it != m_txs_by_fee_and_receive_time.end())
{
- txpool_tx_meta_t meta = m_blockchain.get_txpool_tx_meta(sorted_it->second);
+ txpool_tx_meta_t meta;
+ if (!m_blockchain.get_txpool_tx_meta(sorted_it->second, meta))
+ {
+ MERROR(" failed to find tx meta");
+ continue;
+ }
LOG_PRINT_L2("Considering " << sorted_it->second << ", size " << meta.blob_size << ", current block size " << total_size << "/" << max_total_size << ", current coinbase " << print_money(best_coinbase));
// Can not exceed maximum block size
diff --git a/src/cryptonote_protocol/block_queue.cpp b/src/cryptonote_protocol/block_queue.cpp
index bfff35456..3844d3751 100644
--- a/src/cryptonote_protocol/block_queue.cpp
+++ b/src/cryptonote_protocol/block_queue.cpp
@@ -62,6 +62,7 @@ void block_queue::add_blocks(uint64_t height, std::list<cryptonote::block_comple
void block_queue::add_blocks(uint64_t height, uint64_t nblocks, const boost::uuids::uuid &connection_id, boost::posix_time::ptime time)
{
+ CHECK_AND_ASSERT_THROW_MES(nblocks > 0, "Empty span");
boost::unique_lock<boost::recursive_mutex> lock(mutex);
blocks.insert(span(height, nblocks, connection_id, time));
}
@@ -384,7 +385,7 @@ float block_queue::get_speed(const boost::uuids::uuid &connection_id) const
i->second = (i->second + span.rate) / 2;
}
float conn_rate = -1, best_rate = 0;
- for (auto i: speeds)
+ for (const auto &i: speeds)
{
if (i.first == connection_id)
conn_rate = i.second;
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
index 389e8ba84..5d25d1058 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
@@ -266,13 +266,17 @@ namespace cryptonote
return true;
// from v6, if the peer advertises a top block version, reject if it's not what it should be (will only work if no voting)
- const uint8_t version = m_core.get_ideal_hard_fork_version(hshd.current_height - 1);
- if (version >= 6 && version != hshd.top_version)
+ if (hshd.current_height > 0)
{
- if (version < hshd.top_version)
- MCLOG_RED(el::Level::Warning, "global", context << " peer claims higher version that we think - we may be forked from the network and a software upgrade may be needed");
- LOG_DEBUG_CC(context, "Ignoring due to wrong top version for block " << (hshd.current_height - 1) << ": " << (unsigned)hshd.top_version << ", expected " << (unsigned)version);
- return false;
+ 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)
+ 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");
+ return false;
+ }
}
context.m_remote_blockchain_height = hshd.current_height;
@@ -999,6 +1003,11 @@ skip:
MDEBUG(context << " next span in the queue has blocks " << start_height << "-" << (start_height + blocks.size() - 1)
<< ", we need " << previous_height);
+ if (blocks.empty())
+ {
+ MERROR("Next span has no blocks");
+ break;
+ }
block new_block;
if (!parse_and_validate_block_from_blob(blocks.front().block, new_block))
@@ -1050,6 +1059,11 @@ skip:
num_txs += block_entry.txs.size();
std::vector<tx_verification_context> tvc;
m_core.handle_incoming_txs(block_entry.txs, tvc, true, true, false);
+ if (tvc.size() != block_entry.txs.size())
+ {
+ LOG_ERROR_CCONTEXT("Internal error: tvc.size() != block_entry.txs.size()");
+ return true;
+ }
std::list<blobdata>::const_iterator it = block_entry.txs.begin();
for (size_t i = 0; i < tvc.size(); ++i, ++it)
{
@@ -1494,6 +1508,7 @@ skip:
NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized<NOTIFY_REQUEST_CHAIN::request>();
m_core.get_short_chain_history(r.block_ids);
+ CHECK_AND_ASSERT_MES(!r.block_ids.empty(), false, "Short chain history is empty");
if (!start_from_current_chain)
{
@@ -1581,6 +1596,12 @@ skip:
drop_connection(context, true, false);
return 1;
}
+ if (arg.total_height < arg.m_block_ids.size() || arg.start_height > arg.total_height - arg.m_block_ids.size())
+ {
+ LOG_ERROR_CCONTEXT("sent invalid start/nblocks/height, dropping connection");
+ drop_connection(context, true, false);
+ return 1;
+ }
context.m_remote_blockchain_height = arg.total_height;
context.m_last_response_height = arg.start_height + arg.m_block_ids.size()-1;
diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt
index ad84db450..49d3bc836 100644
--- a/src/daemon/CMakeLists.txt
+++ b/src/daemon/CMakeLists.txt
@@ -29,9 +29,9 @@
set(blocksdat "")
if(PER_BLOCK_CHECKPOINT)
if(APPLE)
- add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*)
+ add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*)
else()
- add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat)
+ add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat)
endif()
set(blocksdat "blocksdat.o")
endif()
diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp
index 5307b2472..8970f9407 100644
--- a/src/daemon/command_parser_executor.cpp
+++ b/src/daemon/command_parser_executor.cpp
@@ -173,7 +173,7 @@ bool t_command_parser_executor::print_block(const std::vector<std::string>& args
uint64_t height = boost::lexical_cast<uint64_t>(arg);
return m_executor.print_block_by_height(height);
}
- catch (boost::bad_lexical_cast&)
+ catch (const boost::bad_lexical_cast&)
{
crypto::hash block_hash;
if (parse_hash256(arg, block_hash))
@@ -420,7 +420,7 @@ bool t_command_parser_executor::out_peers(const std::vector<std::string>& args)
limit = std::stoi(args[0]);
}
- catch(std::exception& ex) {
+ catch(const std::exception& ex) {
_erro("stoi exception");
return false;
}
@@ -450,7 +450,7 @@ bool t_command_parser_executor::hard_fork_info(const std::vector<std::string>& a
try {
version = std::stoi(args[0]);
}
- catch(std::exception& ex) {
+ catch(const std::exception& ex) {
return false;
}
if (version <= 0 || version > 255)
diff --git a/src/daemon/core.h b/src/daemon/core.h
index 97f1cb8c1..bf4f9bb3d 100644
--- a/src/daemon/core.h
+++ b/src/daemon/core.h
@@ -31,7 +31,6 @@
#include "cryptonote_core/cryptonote_core.h"
#include "cryptonote_protocol/cryptonote_protocol_handler.h"
#include "misc_log_ex.h"
-#include "daemon/command_line_args.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "daemon"
diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp
index 3bc6ea392..55d868a3c 100644
--- a/src/daemon/daemon.cpp
+++ b/src/daemon/daemon.cpp
@@ -43,8 +43,9 @@
#include "daemon/protocol.h"
#include "daemon/rpc.h"
#include "daemon/command_server.h"
+#include "daemon/command_server.h"
+#include "daemon/command_line_args.h"
#include "version.h"
-#include "syncobj.h"
using namespace epee;
diff --git a/src/daemon/executor.cpp b/src/daemon/executor.cpp
index 6130bfa28..3379eeca4 100644
--- a/src/daemon/executor.cpp
+++ b/src/daemon/executor.cpp
@@ -30,7 +30,6 @@
#include "daemon/executor.h"
-#include "common/command_line.h"
#include "cryptonote_config.h"
#include "version.h"
diff --git a/src/daemon/protocol.h b/src/daemon/protocol.h
index fc5edbcaa..01de535d5 100644
--- a/src/daemon/protocol.h
+++ b/src/daemon/protocol.h
@@ -33,8 +33,6 @@
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "daemon"
-#include "common/scoped_message_writer.h"
-
namespace daemonize
{
diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp
index d7ee28baa..2e2411708 100644
--- a/src/daemon/rpc_command_executor.cpp
+++ b/src/daemon/rpc_command_executor.cpp
@@ -538,7 +538,7 @@ bool t_rpc_command_executor::print_blockchain_info(uint64_t start_block_index, u
std::cout << std::endl;
std::cout
<< "height: " << header.height << ", timestamp: " << header.timestamp << ", difficulty: " << header.difficulty
- << ", size: " << header.block_size << std::endl
+ << ", size: " << header.block_size << ", transactions: " << header.num_txes << std::endl
<< "major version: " << (unsigned)header.major_version << ", minor version: " << (unsigned)header.minor_version << std::endl
<< "block id: " << header.hash << ", previous block id: " << header.prev_hash << std::endl
<< "difficulty: " << header.difficulty << ", nonce " << header.nonce << ", reward " << cryptonote::print_money(header.reward) << std::endl;
@@ -1611,7 +1611,7 @@ bool t_rpc_command_executor::alt_chain_info()
}
tools::msg_writer() << boost::lexical_cast<std::string>(res.chains.size()) << " alternate chains found:";
- for (const auto chain: res.chains)
+ for (const auto &chain: res.chains)
{
uint64_t start_height = (chain.height - chain.length + 1);
tools::msg_writer() << chain.length << " blocks long, from height " << start_height << " (" << (ires.height - start_height - 1)
diff --git a/src/debug_utilities/cn_deserialize.cpp b/src/debug_utilities/cn_deserialize.cpp
index a59c04dc6..04c0935c8 100644
--- a/src/debug_utilities/cn_deserialize.cpp
+++ b/src/debug_utilities/cn_deserialize.cpp
@@ -154,7 +154,11 @@ int main(int argc, char* argv[])
std::cout << "Parsed transaction:" << std::endl;
std::cout << cryptonote::obj_to_json_str(tx) << std::endl;
- if (cryptonote::parse_tx_extra(tx.extra, fields))
+ bool parsed = cryptonote::parse_tx_extra(tx.extra, fields);
+ if (!parsed)
+ std::cout << "Failed to parse tx_extra" << std::endl;
+
+ if (!fields.empty())
{
std::cout << "tx_extra has " << fields.size() << " field(s)" << std::endl;
for (size_t n = 0; n < fields.size(); ++n)
@@ -171,7 +175,7 @@ int main(int argc, char* argv[])
}
else
{
- std::cout << "Failed to parse tx_extra" << std::endl;
+ std::cout << "No fields were found in tx_extra" << std::endl;
}
}
else
diff --git a/src/debug_utilities/object_sizes.cpp b/src/debug_utilities/object_sizes.cpp
index 82d8a4add..967742229 100644
--- a/src/debug_utilities/object_sizes.cpp
+++ b/src/debug_utilities/object_sizes.cpp
@@ -51,7 +51,7 @@ class size_logger
public:
~size_logger()
{
- for (const auto i: types)
+ for (const auto &i: types)
std::cout << std::to_string(i.first) << "\t" << i.second << std::endl;
}
void add(const char *type, size_t size) { types.insert(std::make_pair(size, type)); }
diff --git a/src/mnemonics/CMakeLists.txt b/src/mnemonics/CMakeLists.txt
index 5ce2198ae..79964e873 100644
--- a/src/mnemonics/CMakeLists.txt
+++ b/src/mnemonics/CMakeLists.txt
@@ -57,6 +57,7 @@ monero_add_library(mnemonics
${mnemonics_private_headers})
target_link_libraries(mnemonics
PUBLIC
+ epee
easylogging
${Boost_SYSTEM_LIBRARY}
PRIVATE
diff --git a/src/mnemonics/electrum-words.cpp b/src/mnemonics/electrum-words.cpp
index c16a095b1..f44ad40aa 100644
--- a/src/mnemonics/electrum-words.cpp
+++ b/src/mnemonics/electrum-words.cpp
@@ -205,6 +205,8 @@ namespace
*/
bool checksum_test(std::vector<std::string> seed, uint32_t unique_prefix_length)
{
+ if (seed.empty())
+ return false;
// The last word is the checksum.
std::string last_word = seed.back();
seed.pop_back();
diff --git a/src/p2p/CMakeLists.txt b/src/p2p/CMakeLists.txt
index 123b0a272..3fc053dc7 100644
--- a/src/p2p/CMakeLists.txt
+++ b/src/p2p/CMakeLists.txt
@@ -46,5 +46,6 @@ target_link_libraries(p2p
${Boost_FILESYSTEM_LIBRARY}
${Boost_SYSTEM_LIBRARY}
${Boost_THREAD_LIBRARY}
+ ${Boost_SERIALIZATION_LIBRARY}
PRIVATE
${EXTRA_LIBRARIES})
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index 55be7c2cb..269a9ba87 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -649,6 +649,10 @@ namespace nodetool
template<class t_payload_net_handler>
bool node_server<t_payload_net_handler>::send_stop_signal()
{
+ MDEBUG("[node] sending stop signal");
+ m_net_server.send_stop_signal();
+ MDEBUG("[node] Stop signal sent");
+
std::list<boost::uuids::uuid> connection_ids;
m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) {
connection_ids.push_back(cntxt.m_connection_id);
@@ -658,8 +662,7 @@ namespace nodetool
m_net_server.get_config_object().close(connection_id);
m_payload_handler.stop();
- m_net_server.send_stop_signal();
- MDEBUG("[node] Stop signal sent");
+
return true;
}
//-----------------------------------------------------------------------------------
@@ -1856,8 +1859,8 @@ namespace nodetool
template<class t_payload_net_handler>
bool node_server<t_payload_net_handler>::has_too_many_connections(const epee::net_utils::network_address &address)
{
- const uint8_t max_connections = 1;
- uint8_t count = 0;
+ const size_t max_connections = 1;
+ size_t count = 0;
m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt)
{
diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp
index a7311482c..cc46d0aa7 100644
--- a/src/ringct/rctOps.cpp
+++ b/src/ringct/rctOps.cpp
@@ -76,6 +76,7 @@ namespace rct {
//Generates a vector of secret key
//Mainly used in testing
keyV skvGen(size_t rows ) {
+ CHECK_AND_ASSERT_THROW_MES(rows > 0, "0 keys requested");
keyV rv(rows);
size_t i = 0;
crypto::rand(rows * sizeof(key), (uint8_t*)&rv[0]);
@@ -351,6 +352,7 @@ namespace rct {
//This takes the outputs and commitments
//and hashes them into a 32 byte sized key
key cn_fast_hash(const ctkeyV &PC) {
+ if (PC.empty()) return rct::hash2rct(crypto::cn_fast_hash("", 0));
key rv;
cn_fast_hash(rv, &PC[0], 64*PC.size());
return rv;
@@ -367,6 +369,7 @@ namespace rct {
//put them in the key vector and it concatenates them
//and then hashes them
key cn_fast_hash(const keyV &keys) {
+ if (keys.empty()) return rct::hash2rct(crypto::cn_fast_hash("", 0));
key rv;
cn_fast_hash(rv, &keys[0], keys.size() * sizeof(keys[0]));
//dp(rv);
diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h
index 5ea2dcc7c..2df797360 100644
--- a/src/ringct/rctTypes.h
+++ b/src/ringct/rctTypes.h
@@ -47,7 +47,7 @@ extern "C" {
#include "hex.h"
#include "span.h"
-#include "serialization/serialization.h"
+#include "serialization/vector.h"
#include "serialization/debug_archive.h"
#include "serialization/binary_archive.h"
#include "serialization/json_archive.h"
diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt
index 748c6b8c1..19ea93902 100644
--- a/src/rpc/CMakeLists.txt
+++ b/src/rpc/CMakeLists.txt
@@ -101,6 +101,7 @@ target_link_libraries(rpc_base
epee
${Boost_REGEX_LIBRARY}
${Boost_THREAD_LIBRARY}
+ ${Boost_PROGRAM_OPTIONS_LIBRARY}
PRIVATE
${EXTRA_LIBRARIES})
@@ -125,6 +126,7 @@ target_link_libraries(daemon_messages
target_link_libraries(daemon_rpc_server
LINK_PRIVATE
+ rpc
cryptonote_core
cryptonote_protocol
daemon_messages
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index 803588cbd..4966b107d 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -106,8 +106,9 @@ namespace cryptonote
if (rpc_config->login)
http_login.emplace(std::move(rpc_config->login->username), std::move(rpc_config->login->password).password());
+ auto rng = [](size_t len, uint8_t *ptr){ return crypto::rand(len, ptr); };
return epee::http_server_impl_base<core_rpc_server, connection_context>::init(
- std::move(port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login)
+ rng, std::move(port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login)
);
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -492,6 +493,11 @@ namespace cryptonote
{
if (std::find(missed_txs.begin(), missed_txs.end(), h) == missed_txs.end())
{
+ if (txs.empty())
+ {
+ 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)
{
@@ -1150,7 +1156,7 @@ namespace cryptonote
error_resp.message = "Internal error: can't get block by hash. Hash = " + req.hash + '.';
return false;
}
- if (blk.miner_tx.vin.front().type() != typeid(txin_gen))
+ if (blk.miner_tx.vin.size() != 1 || blk.miner_tx.vin.front().type() != typeid(txin_gen))
{
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
error_resp.message = "Internal error: coinbase transaction in the block has the wrong type";
@@ -1188,7 +1194,7 @@ namespace cryptonote
error_resp.message = "Internal error: can't get block by height. Height = " + boost::lexical_cast<std::string>(h) + ". Hash = " + epee::string_tools::pod_to_hex(block_hash) + '.';
return false;
}
- if (blk.miner_tx.vin.front().type() != typeid(txin_gen))
+ if (blk.miner_tx.vin.size() != 1 || blk.miner_tx.vin.front().type() != typeid(txin_gen))
{
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
error_resp.message = "Internal error: coinbase transaction in the block has the wrong type";
@@ -1274,7 +1280,7 @@ namespace cryptonote
error_resp.message = "Internal error: can't get block by hash. Hash = " + req.hash + '.';
return false;
}
- if (blk.miner_tx.vin.front().type() != typeid(txin_gen))
+ if (blk.miner_tx.vin.size() != 1 || blk.miner_tx.vin.front().type() != typeid(txin_gen))
{
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
error_resp.message = "Internal error: coinbase transaction in the block has the wrong type";
@@ -1436,19 +1442,25 @@ namespace cryptonote
{
failed = true;
}
- crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
- txids.push_back(txid);
+ else
+ {
+ crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
+ txids.push_back(txid);
+ }
}
}
if (!m_core.get_blockchain_storage().flush_txes_from_pool(txids))
{
- res.status = "Failed to remove one more tx";
+ res.status = "Failed to remove one or more tx(es)";
return false;
}
if (failed)
{
- res.status = "Failed to parse txid";
+ if (txids.empty())
+ res.status = "Failed to parse txid";
+ else
+ res.status = "Failed to parse some of the txids";
return false;
}
@@ -1705,13 +1717,16 @@ namespace cryptonote
PERF_TIMER(on_relay_tx);
bool failed = false;
+ res.status = "";
for (const auto &str: req.txids)
{
cryptonote::blobdata txid_data;
if(!epee::string_tools::parse_hexstr_to_binbuff(str, txid_data))
{
- res.status = std::string("Invalid transaction id: ") + str;
+ if (!res.status.empty()) res.status += ", ";
+ res.status += std::string("invalid transaction id: ") + str;
failed = true;
+ continue;
}
crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
@@ -1727,8 +1742,10 @@ namespace cryptonote
}
else
{
- res.status = std::string("Transaction not found in pool: ") + str;
+ if (!res.status.empty()) res.status += ", ";
+ res.status += std::string("transaction not found in pool: ") + str;
failed = true;
+ continue;
}
}
diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp
index 6643ce4e4..908f9e187 100644
--- a/src/rpc/daemon_handler.cpp
+++ b/src/rpc/daemon_handler.cpp
@@ -799,6 +799,10 @@ namespace rpc
}
header.hash = hash_in;
+ if (b.miner_tx.vin.size() != 1 || b.miner_tx.vin.front().type() != typeid(txin_gen))
+ {
+ return false;
+ }
header.height = boost::get<txin_gen>(b.miner_tx.vin.front()).height;
header.major_version = b.major_version;
diff --git a/src/rpc/message.cpp b/src/rpc/message.cpp
index d6da124d1..98b40e667 100644
--- a/src/rpc/message.cpp
+++ b/src/rpc/message.cpp
@@ -111,7 +111,7 @@ FullMessage::FullMessage(Message* message)
FullMessage::FullMessage(const std::string& json_string, bool request)
{
doc.Parse(json_string.c_str());
- if (doc.HasParseError())
+ if (doc.HasParseError() || !doc.IsObject())
{
throw cryptonote::json::PARSE_FAIL();
}
diff --git a/src/serialization/container.h b/src/serialization/container.h
new file mode 100644
index 000000000..978a59d2a
--- /dev/null
+++ b/src/serialization/container.h
@@ -0,0 +1,113 @@
+// Copyright (c) 2014-2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+
+#pragma once
+
+#include "serialization.h"
+
+namespace serialization
+{
+ namespace detail
+ {
+ template <typename Archive, class T>
+ bool serialize_container_element(Archive& ar, T& e)
+ {
+ return ::do_serialize(ar, e);
+ }
+
+ template <typename Archive>
+ bool serialize_container_element(Archive& ar, uint32_t& e)
+ {
+ ar.serialize_varint(e);
+ return true;
+ }
+
+ template <typename Archive>
+ bool serialize_container_element(Archive& ar, uint64_t& e)
+ {
+ ar.serialize_varint(e);
+ return true;
+ }
+
+ template <typename C>
+ void do_reserve(C &c, size_t N) {}
+ }
+}
+
+template <template <bool> class Archive, typename C>
+bool do_serialize_container(Archive<false> &ar, C &v)
+{
+ size_t cnt;
+ ar.begin_array(cnt);
+ if (!ar.stream().good())
+ return false;
+ v.clear();
+
+ // very basic sanity check
+ if (ar.remaining_bytes() < cnt) {
+ ar.stream().setstate(std::ios::failbit);
+ return false;
+ }
+
+ ::serialization::detail::do_reserve(v, cnt);
+
+ for (size_t i = 0; i < cnt; i++) {
+ if (i > 0)
+ ar.delimit_array();
+ typename C::value_type e;
+ if (!::serialization::detail::serialize_container_element(ar, e))
+ return false;
+ ::serialization::detail::do_add(v, std::move(e));
+ if (!ar.stream().good())
+ return false;
+ }
+ ar.end_array();
+ return true;
+}
+
+template <template <bool> class Archive, typename C>
+bool do_serialize_container(Archive<true> &ar, C &v)
+{
+ size_t cnt = v.size();
+ ar.begin_array(cnt);
+ for (auto i = v.begin(); i != v.end(); ++i)
+ {
+ if (!ar.stream().good())
+ return false;
+ if (i != v.begin())
+ ar.delimit_array();
+ if(!::serialization::detail::serialize_container_element(ar, const_cast<typename C::value_type&>(*i)))
+ return false;
+ if (!ar.stream().good())
+ return false;
+ }
+ ar.end_array();
+ return true;
+}
diff --git a/src/serialization/crypto.h b/src/serialization/crypto.h
index 4213f2e58..8083bdeb1 100644
--- a/src/serialization/crypto.h
+++ b/src/serialization/crypto.h
@@ -34,7 +34,7 @@
#include "serialization.h"
#include "debug_archive.h"
-#include "crypto/chacha8.h"
+#include "crypto/chacha.h"
#include "crypto/crypto.h"
#include "crypto/hash.h"
@@ -77,7 +77,7 @@ bool do_serialize(Archive<true> &ar, std::vector<crypto::signature> &v)
return true;
}
-BLOB_SERIALIZER(crypto::chacha8_iv);
+BLOB_SERIALIZER(crypto::chacha_iv);
BLOB_SERIALIZER(crypto::hash);
BLOB_SERIALIZER(crypto::hash8);
BLOB_SERIALIZER(crypto::public_key);
diff --git a/src/common/memwipe.h b/src/serialization/deque.h
index c3b4ce8ab..994d3f195 100644
--- a/src/common/memwipe.h
+++ b/src/serialization/deque.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -30,55 +30,35 @@
#pragma once
-#ifdef __cplusplus
-#include <array>
+#include <deque>
-extern "C" {
-#endif
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<false> &ar, std::deque<T> &v);
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<true> &ar, std::deque<T> &v);
-void *memwipe(void *src, size_t n);
-
-#ifdef __cplusplus
-}
-#endif
-
-#ifdef __cplusplus
-namespace tools {
-
- /// Scrubs data in the contained type upon destruction.
- ///
- /// Primarily useful for making sure that private keys don't stick around in
- /// memory after the objects that held them have gone out of scope.
- template <class T>
- struct scrubbed : public T {
- using type = T;
-
- ~scrubbed() {
- scrub();
+namespace serialization
+{
+ namespace detail
+ {
+ template <typename T>
+ void do_reserve(std::deque<T> &c, size_t N)
+ {
+ c.reserve(N);
}
- /// Destroy the contents of the contained type.
- void scrub() {
- static_assert(std::is_pod<T>::value,
- "T cannot be auto-scrubbed. T must be POD.");
- static_assert(std::is_trivially_destructible<T>::value,
- "T cannot be auto-scrubbed. T must be trivially destructable.");
- memwipe(this, sizeof(T));
+ template <typename T>
+ void do_add(std::deque<T> &c, T &&e)
+ {
+ c.emplace_back(std::move(e));
}
- };
+ }
+}
- template <class T, size_t N>
- using scrubbed_arr = scrubbed<std::array<T, N>>;
-} // namespace tools
+#include "serialization.h"
-// Partial specialization for std::is_pod<tools::scrubbed<T>> so that it can
-// pretend to be the containted type in those contexts.
-namespace std
-{
- template<class t_scrubbee>
- struct is_pod<tools::scrubbed<t_scrubbee>> {
- static const bool value = is_pod<t_scrubbee>::value;
- };
-}
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<false> &ar, std::deque<T> &v) { return do_serialize_container(ar, v); }
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<true> &ar, std::deque<T> &v) { return do_serialize_container(ar, v); }
-#endif // __cplusplus
diff --git a/src/serialization/list.h b/src/serialization/list.h
index d0fb72163..d725458e7 100644
--- a/src/serialization/list.h
+++ b/src/serialization/list.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2015, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -30,71 +30,29 @@
#pragma once
-#include "serialization.h"
+#include <list>
+
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<false> &ar, std::list<T> &v);
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<true> &ar, std::list<T> &v);
namespace serialization
{
namespace detail
{
- template <typename Archive, class T>
- bool serialize_list_element(Archive& ar, T& e)
- {
- return ::do_serialize(ar, e);
- }
-
- template <typename Archive>
- bool serialize_list_element(Archive& ar, uint64_t& e)
+ template <typename T>
+ void do_add(std::list<T> &c, T &&e)
{
- ar.serialize_varint(e);
- return true;
+ c.emplace_back(std::move(e));
}
}
}
-template <template <bool> class Archive, class T>
-bool do_serialize(Archive<false> &ar, std::list<T> &l)
-{
- size_t cnt;
- ar.begin_array(cnt);
- if (!ar.stream().good())
- return false;
- l.clear();
-
- // very basic sanity check
- if (ar.remaining_bytes() < cnt) {
- ar.stream().setstate(std::ios::failbit);
- return false;
- }
-
- for (size_t i = 0; i < cnt; i++) {
- if (i > 0)
- ar.delimit_array();
- l.push_back(T());
- T &t = l.back();
- if (!::serialization::detail::serialize_list_element(ar, t))
- return false;
- if (!ar.stream().good())
- return false;
- }
- ar.end_array();
- return true;
-}
+#include "serialization.h"
template <template <bool> class Archive, class T>
-bool do_serialize(Archive<true> &ar, std::list<T> &l)
-{
- size_t cnt = l.size();
- ar.begin_array(cnt);
- for (typename std::list<T>::iterator i = l.begin(); i != l.end(); ++i) {
- if (!ar.stream().good())
- return false;
- if (i != l.begin())
- ar.delimit_array();
- if(!::serialization::detail::serialize_list_element(ar, *i))
- return false;
- if (!ar.stream().good())
- return false;
- }
- ar.end_array();
- return true;
-}
+bool do_serialize(Archive<false> &ar, std::list<T> &v) { return do_serialize_container(ar, v); }
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<true> &ar, std::list<T> &v) { return do_serialize_container(ar, v); }
+
diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h
index 9e23f0791..56496c790 100644
--- a/src/serialization/serialization.h
+++ b/src/serialization/serialization.h
@@ -63,15 +63,17 @@ struct is_blob_type { typedef boost::false_type type; };
template <class T>
struct has_free_serializer { typedef boost::true_type type; };
-/*! \struct is_pair_type
+/*! \struct is_basic_type
*
* \brief a descriptor for dispatching serialize
*/
template <class T>
-struct is_pair_type { typedef boost::false_type type; };
+struct is_basic_type { typedef boost::false_type type; };
template<typename F, typename S>
-struct is_pair_type<std::pair<F,S>> { typedef boost::true_type type; };
+struct is_basic_type<std::pair<F,S>> { typedef boost::true_type type; };
+template<>
+struct is_basic_type<std::string> { typedef boost::true_type type; };
/*! \struct serializer
*
@@ -89,7 +91,7 @@ struct is_pair_type<std::pair<F,S>> { typedef boost::true_type type; };
template <class Archive, class T>
struct serializer{
static bool serialize(Archive &ar, T &v) {
- return serialize(ar, v, typename boost::is_integral<T>::type(), typename is_blob_type<T>::type(), typename is_pair_type<T>::type());
+ return serialize(ar, v, typename boost::is_integral<T>::type(), typename is_blob_type<T>::type(), typename is_basic_type<T>::type());
}
template<typename A>
static bool serialize(Archive &ar, T &v, boost::false_type, boost::true_type, A a) {
@@ -361,9 +363,3 @@ namespace serialization {
return r && check_stream_state(ar);
}
}
-
-#include "string.h"
-#include "vector.h"
-#include "list.h"
-#include "pair.h"
-#include "set.h"
diff --git a/src/serialization/set.h b/src/serialization/set.h
index 54b4eb3ab..e6eff62a9 100644
--- a/src/serialization/set.h
+++ b/src/serialization/set.h
@@ -30,98 +30,29 @@
#pragma once
-#include "serialization.h"
+#include <set>
template <template <bool> class Archive, class T>
bool do_serialize(Archive<false> &ar, std::set<T> &v);
template <template <bool> class Archive, class T>
bool do_serialize(Archive<true> &ar, std::set<T> &v);
-template <template <bool> class Archive, class T>
-bool do_serialize(Archive<false> &ar, std::unordered_set<T> &v);
-template <template <bool> class Archive, class T>
-bool do_serialize(Archive<true> &ar, std::unordered_set<T> &v);
namespace serialization
{
namespace detail
{
- template <typename Archive, class T>
- bool serialize_set_element(Archive& ar, T& e)
+ template <typename T>
+ void do_add(std::set<T> &c, T &&e)
{
- return ::do_serialize(ar, e);
- }
-
- template <typename Archive>
- bool serialize_set_element(Archive& ar, uint32_t& e)
- {
- ar.serialize_varint(e);
- return true;
- }
-
- template <typename Archive>
- bool serialize_set_element(Archive& ar, uint64_t& e)
- {
- ar.serialize_varint(e);
- return true;
+ c.insert(std::move(e));
}
}
}
-template <template <bool> class Archive, class T>
-bool do_serialize_set(Archive<false> &ar, T &v)
-{
- size_t cnt;
- ar.begin_array(cnt);
- if (!ar.stream().good())
- return false;
- v.clear();
-
- // very basic sanity check
- if (ar.remaining_bytes() < cnt) {
- ar.stream().setstate(std::ios::failbit);
- return false;
- }
-
- for (size_t i = 0; i < cnt; i++) {
- if (i > 0)
- ar.delimit_array();
- typename T::key_type k;
- if (!::serialization::detail::serialize_set_element(ar, k))
- return false;
- v.insert(std::move(k));
- if (!ar.stream().good())
- return false;
- }
- ar.end_array();
- return true;
-}
-
-template <template <bool> class Archive, class T>
-bool do_serialize_set(Archive<true> &ar, T &v)
-{
- size_t cnt = v.size();
- ar.begin_array(cnt);
- bool first = true;
- for (const typename T::key_type &k: v) {
- if (!ar.stream().good())
- return false;
- if (!first)
- ar.delimit_array();
- if(!::serialization::detail::serialize_set_element(ar, const_cast<typename T::key_type&>(k)))
- return false;
- if (!ar.stream().good())
- return false;
- first = false;
- }
- ar.end_array();
- return true;
-}
+#include "serialization.h"
template <template <bool> class Archive, class T>
-bool do_serialize(Archive<false> &ar, std::set<T> &v) { return do_serialize_set(ar, v); }
+bool do_serialize(Archive<false> &ar, std::set<T> &v) { return do_serialize_container(ar, v); }
template <template <bool> class Archive, class T>
-bool do_serialize(Archive<true> &ar, std::set<T> &v) { return do_serialize_set(ar, v); }
-template <template <bool> class Archive, class T>
-bool do_serialize(Archive<false> &ar, std::unordered_set<T> &v) { return do_serialize_set(ar, v); }
-template <template <bool> class Archive, class T>
-bool do_serialize(Archive<true> &ar, std::unordered_set<T> &v) { return do_serialize_set(ar, v); }
+bool do_serialize(Archive<true> &ar, std::set<T> &v) { return do_serialize_container(ar, v); }
+
diff --git a/src/serialization/unordered_set.h b/src/serialization/unordered_set.h
new file mode 100644
index 000000000..b277f0c4a
--- /dev/null
+++ b/src/serialization/unordered_set.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2014-2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+
+#pragma once
+
+#include <set>
+
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<false> &ar, std::unordered_set<T> &v);
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<true> &ar, std::unordered_set<T> &v);
+
+namespace serialization
+{
+ namespace detail
+ {
+ template <typename T>
+ void do_add(std::unordered_set<T> &c, T &&e)
+ {
+ c.insert(std::move(e));
+ }
+ }
+}
+
+#include "serialization.h"
+
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<false> &ar, std::unordered_set<T> &v) { return do_serialize_container(ar, v); }
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<true> &ar, std::unordered_set<T> &v) { return do_serialize_container(ar, v); }
+
diff --git a/src/serialization/vector.h b/src/serialization/vector.h
index 12fd59558..9cf3d8272 100644
--- a/src/serialization/vector.h
+++ b/src/serialization/vector.h
@@ -30,6 +30,7 @@
#pragma once
+#include <vector>
#include "serialization.h"
template <template <bool> class Archive, class T>
@@ -37,91 +38,28 @@ bool do_serialize(Archive<false> &ar, std::vector<T> &v);
template <template <bool> class Archive, class T>
bool do_serialize(Archive<true> &ar, std::vector<T> &v);
-template <template <bool> class Archive, class T>
-bool do_serialize(Archive<false> &ar, std::deque<T> &v);
-template <template <bool> class Archive, class T>
-bool do_serialize(Archive<true> &ar, std::deque<T> &v);
-
namespace serialization
{
namespace detail
{
- template <typename Archive, class T>
- bool serialize_vector_element(Archive& ar, T& e)
- {
- return ::do_serialize(ar, e);
- }
-
- template <typename Archive>
- bool serialize_vector_element(Archive& ar, uint32_t& e)
+ template <typename T>
+ void do_reserve(std::vector<T> &c, size_t N)
{
- ar.serialize_varint(e);
- return true;
+ c.reserve(N);
}
- template <typename Archive>
- bool serialize_vector_element(Archive& ar, uint64_t& e)
+ template <typename T>
+ void do_add(std::vector<T> &c, T &&e)
{
- ar.serialize_varint(e);
- return true;
+ c.emplace_back(std::move(e));
}
}
}
-template <template <bool> class Archive, class T>
-bool do_serialize_vd(Archive<false> &ar, T &v)
-{
- size_t cnt;
- ar.begin_array(cnt);
- if (!ar.stream().good())
- return false;
- v.clear();
-
- // very basic sanity check
- if (ar.remaining_bytes() < cnt) {
- ar.stream().setstate(std::ios::failbit);
- return false;
- }
-
- v.reserve(cnt);
- for (size_t i = 0; i < cnt; i++) {
- if (i > 0)
- ar.delimit_array();
- v.resize(i+1);
- if (!::serialization::detail::serialize_vector_element(ar, v[i]))
- return false;
- if (!ar.stream().good())
- return false;
- }
- ar.end_array();
- return true;
-}
-
-template <template <bool> class Archive, class T>
-bool do_serialize_vd(Archive<true> &ar, T &v)
-{
- size_t cnt = v.size();
- ar.begin_array(cnt);
- for (size_t i = 0; i < cnt; i++) {
- if (!ar.stream().good())
- return false;
- if (i > 0)
- ar.delimit_array();
- if(!::serialization::detail::serialize_vector_element(ar, v[i]))
- return false;
- if (!ar.stream().good())
- return false;
- }
- ar.end_array();
- return true;
-}
+#include "container.h"
template <template <bool> class Archive, class T>
-bool do_serialize(Archive<false> &ar, std::vector<T> &v) { return do_serialize_vd(ar, v); }
+bool do_serialize(Archive<false> &ar, std::vector<T> &v) { return do_serialize_container(ar, v); }
template <template <bool> class Archive, class T>
-bool do_serialize(Archive<true> &ar, std::vector<T> &v) { return do_serialize_vd(ar, v); }
+bool do_serialize(Archive<true> &ar, std::vector<T> &v) { return do_serialize_container(ar, v); }
-template <template <bool> class Archive, class T>
-bool do_serialize(Archive<false> &ar, std::deque<T> &v) { return do_serialize_vd(ar, v); }
-template <template <bool> class Archive, class T>
-bool do_serialize(Archive<true> &ar, std::deque<T> &v) { return do_serialize_vd(ar, v); }
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index 64e665fb3..b9a3e45b4 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -121,6 +121,7 @@ namespace
const command_line::arg_descriptor<std::string> arg_mnemonic_language = {"mnemonic-language", sw::tr("Language for mnemonic"), ""};
const command_line::arg_descriptor<std::string> arg_electrum_seed = {"electrum-seed", sw::tr("Specify Electrum seed for wallet recovery/creation"), ""};
const command_line::arg_descriptor<bool> arg_restore_deterministic_wallet = {"restore-deterministic-wallet", sw::tr("Recover wallet using Electrum-style mnemonic seed"), false};
+ 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("Create 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_allow_mismatched_daemon_version = {"allow-mismatched-daemon-version", sw::tr("Allow communicating with a daemon that uses a different RPC version"), false};
@@ -516,48 +517,55 @@ bool simple_wallet::spendkey(const std::vector<std::string> &args/* = std::vecto
bool simple_wallet::print_seed(bool encrypted)
{
bool success = false;
- std::string electrum_words;
+ std::string seed;
+ bool ready, multisig;
- if (m_wallet->multisig())
- {
- fail_msg_writer() << tr("wallet is multisig and has no seed");
- return true;
- }
if (m_wallet->watch_only())
{
fail_msg_writer() << tr("wallet is watch-only and has no seed");
return true;
}
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
- if (m_wallet->is_deterministic())
- {
- if (m_wallet->get_seed_language().empty())
- {
- std::string mnemonic_language = get_mnemonic_language();
- if (mnemonic_language.empty())
- return true;
- m_wallet->set_seed_language(mnemonic_language);
- }
- epee::wipeable_string seed_pass;
- if (encrypted)
+ multisig = m_wallet->multisig(&ready);
+ if (multisig)
+ {
+ if (!ready)
{
- auto pwd_container = tools::password_container::prompt(true, tr("Enter optional seed encryption passphrase, empty to see raw seed"));
- if (std::cin.eof() || !pwd_container)
- return true;
- seed_pass = pwd_container->password();
+ fail_msg_writer() << tr("wallet is multisig but not yet finalized");
+ return true;
}
+ }
+ else if (!m_wallet->is_deterministic())
+ {
+ fail_msg_writer() << tr("wallet is non-deterministic and has no seed");
+ return true;
+ }
- success = m_wallet->get_seed(electrum_words, seed_pass);
+ epee::wipeable_string seed_pass;
+ if (encrypted)
+ {
+#ifdef HAVE_READLINE
+ rdln::suspend_readline pause_readline;
+#endif
+ auto pwd_container = tools::password_container::prompt(true, tr("Enter optional seed encryption passphrase, empty to see raw seed"));
+ if (std::cin.eof() || !pwd_container)
+ return true;
+ seed_pass = pwd_container->password();
}
+ if (multisig)
+ success = m_wallet->get_multisig_seed(seed, seed_pass);
+ else if (m_wallet->is_deterministic())
+ success = m_wallet->get_seed(seed, seed_pass);
+
if (success)
{
- print_seed(electrum_words);
+ print_seed(seed);
}
else
{
- fail_msg_writer() << tr("wallet is non-deterministic and has no seed");
+ fail_msg_writer() << tr("Failed to retrieve seed");
}
return true;
}
@@ -1575,8 +1583,20 @@ simple_wallet::simple_wallet()
tr("Change the current log detail (level must be <0-4>)."));
m_cmd_binder.set_handler("account",
boost::bind(&simple_wallet::account, this, _1),
- tr("account [new <label text with white spaces allowed> | switch <index> | label <index> <label text with white spaces allowed>]"),
- tr("If no arguments are specified, the wallet shows all the existing accounts along with their balances. If the \"new\" argument is specified, the wallet creates a new account with its label initialized by the provided label text (which can be empty). If the \"switch\" argument is specified, the wallet switches to the account specified by <index>. If the \"label\" argument is specified, the wallet sets the label of the account specified by <index> to the provided label text."));
+ tr("account\n"
+ " account new <label text with white spaces allowed>\n"
+ " account switch <index> \n"
+ " account label <index> <label text with white spaces allowed>\n"
+ " account tag <tag_name> <account_index_1> [<account_index_2> ...]\n"
+ " account untag <account_index_1> [<account_index_2> ...]\n"
+ " account tag_description <tag_name> <description>"),
+ tr("If no arguments are specified, the wallet shows all the existing accounts along with their balances.\n"
+ "If the \"new\" argument is specified, the wallet creates a new account with its label initialized by the provided label text (which can be empty).\n"
+ "If the \"switch\" argument is specified, the wallet switches to the account specified by <index>.\n"
+ "If the \"label\" argument is specified, the wallet sets the label of the account specified by <index> to the provided label text.\n"
+ "If the \"tag\" argument is specified, a tag <tag_name> is assigned to the specified accounts <account_index_1>, <account_index_2>, ....\n"
+ "If the \"untag\" argument is specified, the tags assigned to the specified accounts <account_index_1>, <account_index_2> ..., are removed.\n"
+ "If the \"tag_description\" argument is specified, the tag <tag_name> is assigned an arbitrary text <description>."));
m_cmd_binder.set_handler("address",
boost::bind(&simple_wallet::print_address, this, _1),
tr("address [ new <label text with white spaces allowed> | all | <index_min> [<index_max>] | label <index> <label text with white spaces allowed>]"),
@@ -1972,6 +1992,8 @@ static bool might_be_partial_seed(std::string words)
//----------------------------------------------------------------------------------------------------
bool simple_wallet::init(const boost::program_options::variables_map& vm)
{
+ std::string multisig_keys;
+
if (!handle_command_line(vm))
return false;
@@ -1989,49 +2011,91 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
{
std::string old_language;
// check for recover flag. if present, require electrum word list (only recovery option for now).
- if (m_restore_deterministic_wallet)
+ if (m_restore_deterministic_wallet || m_restore_multisig_wallet)
{
if (m_non_deterministic)
{
- fail_msg_writer() << tr("can't specify both --restore-deterministic-wallet and --non-deterministic");
+ fail_msg_writer() << tr("can't specify both --restore-deterministic-wallet or --restore-multisig-wallet and --non-deterministic");
return false;
}
if (!m_wallet_file.empty())
{
- fail_msg_writer() << tr("--restore-deterministic-wallet uses --generate-new-wallet, not --wallet-file");
+ if (m_restore_multisig_wallet)
+ fail_msg_writer() << tr("--restore-multisig-wallet uses --generate-new-wallet, not --wallet-file");
+ else
+ fail_msg_writer() << tr("--restore-deterministic-wallet uses --generate-new-wallet, not --wallet-file");
return false;
}
if (m_electrum_seed.empty())
{
- m_electrum_seed = "";
- do
+ if (m_restore_multisig_wallet)
{
- const char *prompt = m_electrum_seed.empty() ? "Specify Electrum seed: " : "Electrum seed continued: ";
- std::string electrum_seed = input_line(prompt);
- if (std::cin.eof())
- return false;
- if (electrum_seed.empty())
+ const char *prompt = "Specify multisig seed: ";
+ m_electrum_seed = input_line(prompt);
+ if (std::cin.eof())
+ return false;
+ if (m_electrum_seed.empty())
+ {
+ fail_msg_writer() << tr("specify a recovery parameter with the --electrum-seed=\"multisig seed here\"");
+ return false;
+ }
+ }
+ else
+ {
+ m_electrum_seed = "";
+ do
{
- fail_msg_writer() << tr("specify a recovery parameter with the --electrum-seed=\"words list here\"");
- return false;
- }
- m_electrum_seed += electrum_seed + " ";
- } while (might_be_partial_seed(m_electrum_seed));
+ const char *prompt = m_electrum_seed.empty() ? "Specify Electrum seed: " : "Electrum seed continued: ";
+ std::string electrum_seed = input_line(prompt);
+ if (std::cin.eof())
+ return false;
+ if (electrum_seed.empty())
+ {
+ fail_msg_writer() << tr("specify a recovery parameter with the --electrum-seed=\"words list here\"");
+ return false;
+ }
+ m_electrum_seed += electrum_seed + " ";
+ } while (might_be_partial_seed(m_electrum_seed));
+ }
}
- if (!crypto::ElectrumWords::words_to_bytes(m_electrum_seed, m_recovery_key, old_language))
+ if (m_restore_multisig_wallet)
{
- fail_msg_writer() << tr("Electrum-style word list failed verification");
- return false;
+ if (!epee::string_tools::parse_hexstr_to_binbuff(m_electrum_seed, multisig_keys))
+ {
+ fail_msg_writer() << tr("Multisig seed failed verification");
+ return false;
+ }
+ }
+ else
+ {
+ if (!crypto::ElectrumWords::words_to_bytes(m_electrum_seed, m_recovery_key, old_language))
+ {
+ fail_msg_writer() << tr("Electrum-style word list failed verification");
+ return false;
+ }
}
+#ifdef HAVE_READLINE
+ rdln::suspend_readline pause_readline;
+#endif
auto pwd_container = tools::password_container::prompt(false, tr("Enter seed encryption passphrase, empty if none"));
if (std::cin.eof() || !pwd_container)
return false;
epee::wipeable_string seed_pass = pwd_container->password();
if (!seed_pass.empty())
- m_recovery_key = cryptonote::decrypt_key(m_recovery_key, seed_pass);
+ {
+ if (m_restore_multisig_wallet)
+ {
+ crypto::secret_key key;
+ crypto::cn_slow_hash(seed_pass.data(), seed_pass.size(), (crypto::hash&)key);
+ sc_reduce32((unsigned char*)key.data);
+ multisig_keys = m_wallet->decrypt(multisig_keys, key, true);
+ }
+ else
+ m_recovery_key = cryptonote::decrypt_key(m_recovery_key, seed_pass);
+ }
}
if (!m_generate_from_view_key.empty())
{
@@ -2342,7 +2406,11 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
return false;
}
m_wallet_file = m_generate_new;
- bool r = new_wallet(vm, m_recovery_key, m_restore_deterministic_wallet, m_non_deterministic, old_language);
+ bool r;
+ if (m_restore_multisig_wallet)
+ r = new_wallet(vm, multisig_keys, old_language);
+ else
+ r = new_wallet(vm, m_recovery_key, m_restore_deterministic_wallet, m_non_deterministic, old_language);
CHECK_AND_ASSERT_MES(r, false, tr("account creation failed"));
}
if (!m_restore_height && m_restoring)
@@ -2473,6 +2541,7 @@ bool simple_wallet::handle_command_line(const boost::program_options::variables_
m_mnemonic_language = command_line::get_arg(vm, arg_mnemonic_language);
m_electrum_seed = command_line::get_arg(vm, arg_electrum_seed);
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);
m_allow_mismatched_daemon_version = command_line::get_arg(vm, arg_allow_mismatched_daemon_version);
@@ -2483,7 +2552,8 @@ bool simple_wallet::handle_command_line(const boost::program_options::variables_
!m_generate_from_keys.empty() ||
!m_generate_from_multisig_keys.empty() ||
!m_generate_from_json.empty() ||
- m_restore_deterministic_wallet;
+ m_restore_deterministic_wallet ||
+ m_restore_multisig_wallet;
return true;
}
@@ -2630,11 +2700,11 @@ bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
success_msg_writer() <<
"**********************************************************************\n" <<
tr("Your wallet has been generated!\n"
- "To start synchronizing with the daemon, use \"refresh\" command.\n"
- "Use \"help\" command to see the list of available commands.\n"
+ "To start synchronizing with the daemon, use the \"refresh\" command.\n"
+ "Use the \"help\" command to see the list of available commands.\n"
"Use \"help <command>\" to see a command's documentation.\n"
- "Always use \"exit\" command when closing monero-wallet-cli to save your\n"
- "current session's state. Otherwise, you might need to synchronize \n"
+ "Always use the \"exit\" command when closing monero-wallet-cli to save \n"
+ "your current session's state. Otherwise, you might need to synchronize \n"
"your wallet again (your wallet keys are NOT at risk in any case).\n")
;
@@ -2683,6 +2753,49 @@ bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
return true;
}
//----------------------------------------------------------------------------------------------------
+bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
+ const std::string &multisig_keys, const std::string &old_language)
+{
+ auto rc = tools::wallet2::make_new(vm, password_prompter);
+ m_wallet = std::move(rc.first);
+ if (!m_wallet)
+ {
+ return false;
+ }
+
+ std::string mnemonic_language = old_language;
+
+ std::vector<std::string> language_list;
+ crypto::ElectrumWords::get_language_list(language_list);
+ if (mnemonic_language.empty() && std::find(language_list.begin(), language_list.end(), m_mnemonic_language) != language_list.end())
+ {
+ mnemonic_language = m_mnemonic_language;
+ }
+
+ m_wallet->set_seed_language(mnemonic_language);
+
+ try
+ {
+ m_wallet->generate(m_wallet_file, std::move(rc.second).password(), multisig_keys);
+ bool ready;
+ uint32_t threshold, total;
+ if (!m_wallet->multisig(&ready, &threshold, &total) || !ready)
+ {
+ fail_msg_writer() << tr("failed to generate new mutlisig wallet");
+ return false;
+ }
+ message_writer(console_color_white, true) << boost::format(tr("Generated new %u/%u multisig wallet: ")) % threshold % total
+ << m_wallet->get_account().get_public_address_str(m_wallet->testnet());
+ }
+ catch (const std::exception& e)
+ {
+ fail_msg_writer() << tr("failed to generate new wallet: ") << e.what();
+ return false;
+ }
+
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm)
{
if (!tools::wallet2::wallet_valid_path_format(m_wallet_file))
@@ -2759,7 +2872,7 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm)
}
success_msg_writer() <<
"**********************************************************************\n" <<
- tr("Use \"help\" command to see the list of available commands.\n") <<
+ tr("Use the \"help\" command to see the list of available commands.\n") <<
tr("Use \"help <command>\" to see a command's documentation.\n") <<
"**********************************************************************";
return true;
@@ -3119,6 +3232,8 @@ bool simple_wallet::show_balance_unlocked(bool detailed)
if (m_wallet->has_multisig_partial_key_images())
extra = tr(" (Some owned outputs have partial key images - import_multisig_info 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);
success_msg_writer() << tr("Balance: ") << print_money(m_wallet->balance(m_current_subaddress_account)) << ", "
<< tr("unlocked balance: ") << print_money(m_wallet->unlocked_balance(m_current_subaddress_account)) << extra;
std::map<uint32_t, uint64_t> balance_per_subaddress = m_wallet->balance_per_subaddress(m_current_subaddress_account);
@@ -3192,6 +3307,13 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args
{
if (!parse_subaddress_indices(local_args[0], subaddr_indices))
return true;
+ local_args.erase(local_args.begin());
+ }
+
+ if (local_args.size() > 0)
+ {
+ fail_msg_writer() << tr("usage: incoming_transfers [available|unavailable] [verbose] [index=<N>]");
+ return true;
}
tools::wallet2::transfer_container transfers;
@@ -4321,9 +4443,9 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
fail_msg_writer() << tr("Multiple transactions are created, which is not supposed to happen");
return true;
}
- if (ptx_vector[0].selected_transfers.size() > 1)
+ if (ptx_vector[0].selected_transfers.size() != 1)
{
- fail_msg_writer() << tr("The transaction uses multiple inputs, which is not supposed to happen");
+ fail_msg_writer() << tr("The transaction uses multiple or no inputs, which is not supposed to happen");
return true;
}
@@ -4377,90 +4499,9 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
}
}
- catch (const tools::error::daemon_busy&)
- {
- fail_msg_writer() << tr("daemon is busy. Please try again later.");
- }
- catch (const tools::error::no_connection_to_daemon&)
- {
- fail_msg_writer() << tr("no connection to daemon. Please make sure daemon is running.");
- }
- catch (const tools::error::wallet_rpc_error& e)
- {
- LOG_ERROR("RPC error: " << e.to_string());
- fail_msg_writer() << tr("RPC error: ") << e.what();
- }
- catch (const tools::error::get_random_outs_error &e)
- {
- fail_msg_writer() << tr("failed to get random outputs to mix: ") << e.what();
- }
- catch (const tools::error::not_enough_money& e)
- {
- LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, sent amount %s") %
- print_money(e.available()) %
- print_money(e.tx_amount()));
- fail_msg_writer() << tr("Not enough money in unlocked balance");
- }
- catch (const tools::error::tx_not_possible& e)
- {
- LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, transaction amount %s = %s + %s (fee)") %
- print_money(e.available()) %
- print_money(e.tx_amount() + e.fee()) %
- print_money(e.tx_amount()) %
- print_money(e.fee()));
- fail_msg_writer() << tr("Failed to find a way to create transactions. This is usually due to dust which is so small it cannot pay for itself in fees, or trying to send more money than the unlocked balance, or not leaving enough for fees");
- }
- catch (const tools::error::not_enough_outs_to_mix& e)
- {
- auto writer = fail_msg_writer();
- writer << tr("not enough outputs for specified mixin_count") << " = " << e.mixin_count() << ":";
- for (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 mix") << " = " << outs_for_amount.second;
- }
- }
- catch (const tools::error::tx_not_constructed&)
- {
- fail_msg_writer() << tr("transaction was not constructed");
- }
- catch (const tools::error::tx_rejected& e)
- {
- fail_msg_writer() << (boost::format(tr("transaction %s was rejected by daemon with status: ")) % get_transaction_hash(e.tx())) << e.status();
- std::string reason = e.reason();
- if (!reason.empty())
- fail_msg_writer() << tr("Reason: ") << reason;
- }
- catch (const tools::error::tx_sum_overflow& e)
- {
- fail_msg_writer() << e.what();
- }
- catch (const tools::error::zero_destination&)
- {
- fail_msg_writer() << tr("one of destinations is zero");
- }
- catch (const tools::error::tx_too_big& e)
- {
- fail_msg_writer() << tr("failed to find a suitable way to split transactions");
- }
- catch (const tools::error::transfer_error& e)
- {
- LOG_ERROR("unknown transfer error: " << e.to_string());
- fail_msg_writer() << tr("unknown transfer error: ") << e.what();
- }
- catch (const tools::error::multisig_export_needed& e)
- {
- LOG_ERROR("Multisig error: " << e.to_string());
- fail_msg_writer() << tr("Multisig error: ") << e.what();
- }
- catch (const tools::error::wallet_internal_error& e)
- {
- LOG_ERROR("internal error: " << e.to_string());
- fail_msg_writer() << tr("internal error: ") << e.what();
- }
catch (const std::exception& e)
{
- LOG_ERROR("unexpected error: " << e.what());
- fail_msg_writer() << tr("unexpected error: ") << e.what();
+ handle_transfer_exception(std::current_exception());
}
catch (...)
{
@@ -4494,6 +4535,12 @@ bool simple_wallet::sweep_below(const std::vector<std::string> &args_)
//----------------------------------------------------------------------------------------------------
bool simple_wallet::donate(const std::vector<std::string> &args_)
{
+ if(m_wallet->testnet())
+ {
+ fail_msg_writer() << tr("donations are not enabled on the testnet");
+ return true;
+ }
+
std::vector<std::string> local_args = args_;
if(local_args.empty() || local_args.size() > 5)
{
@@ -5175,7 +5222,7 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
try {
min_height = boost::lexical_cast<uint64_t>(local_args[0]);
}
- catch (boost::bad_lexical_cast &) {
+ catch (const boost::bad_lexical_cast &) {
fail_msg_writer() << tr("bad min_height parameter:") << " " << local_args[0];
return true;
}
@@ -5187,7 +5234,7 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
try {
max_height = boost::lexical_cast<uint64_t>(local_args[0]);
}
- catch (boost::bad_lexical_cast &) {
+ catch (const boost::bad_lexical_cast &) {
fail_msg_writer() << tr("bad max_height parameter:") << " " << local_args[0];
return true;
}
@@ -5350,7 +5397,7 @@ bool simple_wallet::unspent_outputs(const std::vector<std::string> &args_)
for (const auto& td : transfers)
{
uint64_t amount = td.amount();
- if (td.m_spent || amount < min_amount || amount > max_amount || td.m_subaddr_index.major != m_current_subaddress_account || subaddr_indices.count(td.m_subaddr_index.minor) == 0)
+ if (td.m_spent || amount < min_amount || amount > max_amount || td.m_subaddr_index.major != m_current_subaddress_account || (subaddr_indices.count(td.m_subaddr_index.minor) == 0 && !subaddr_indices.empty()))
continue;
amount_to_tds[amount].push_back(td);
if (min_height > td.m_block_height) min_height = td.m_block_height;
@@ -5513,6 +5560,9 @@ bool simple_wallet::account(const std::vector<std::string> &args/* = std::vector
// account new <label text with white spaces allowed>
// account switch <index>
// account label <index> <label text with white spaces allowed>
+ // account tag <tag_name> <account_index_1> [<account_index_2> ...]
+ // account untag <account_index_1> [<account_index_2> ...]
+ // account tag_description <tag_name> <description>
if (args.empty())
{
@@ -5577,18 +5627,128 @@ bool simple_wallet::account(const std::vector<std::string> &args/* = std::vector
fail_msg_writer() << e.what();
}
}
+ else if (command == "tag" && local_args.size() >= 2)
+ {
+ const std::string tag = local_args[0];
+ std::set<uint32_t> account_indices;
+ for (size_t i = 1; i < local_args.size(); ++i)
+ {
+ uint32_t account_index;
+ if (!epee::string_tools::get_xtype_from_string(account_index, local_args[i]))
+ {
+ fail_msg_writer() << tr("failed to parse index: ") << local_args[i];
+ return true;
+ }
+ account_indices.insert(account_index);
+ }
+ try
+ {
+ m_wallet->set_account_tag(account_indices, tag);
+ print_accounts(tag);
+ }
+ catch (const std::exception& e)
+ {
+ fail_msg_writer() << e.what();
+ }
+ }
+ else if (command == "untag" && local_args.size() >= 1)
+ {
+ std::set<uint32_t> account_indices;
+ for (size_t i = 0; i < local_args.size(); ++i)
+ {
+ uint32_t account_index;
+ if (!epee::string_tools::get_xtype_from_string(account_index, local_args[i]))
+ {
+ fail_msg_writer() << tr("failed to parse index: ") << local_args[i];
+ return true;
+ }
+ account_indices.insert(account_index);
+ }
+ try
+ {
+ m_wallet->set_account_tag(account_indices, "");
+ print_accounts();
+ }
+ catch (const std::exception& e)
+ {
+ fail_msg_writer() << e.what();
+ }
+ }
+ else if (command == "tag_description" && local_args.size() >= 1)
+ {
+ const std::string tag = local_args[0];
+ std::string description;
+ if (local_args.size() > 1)
+ {
+ local_args.erase(local_args.begin());
+ description = boost::join(local_args, " ");
+ }
+ try
+ {
+ m_wallet->set_account_tag_description(tag, description);
+ print_accounts(tag);
+ }
+ catch (const std::exception& e)
+ {
+ fail_msg_writer() << e.what();
+ }
+ }
else
{
- fail_msg_writer() << tr("usage: account [new <label text with white spaces allowed> | switch <index> | label <index> <label text with white spaces allowed>]");
+ fail_msg_writer() << tr("usage:\n"
+ " account\n"
+ " account new <label text with white spaces allowed>\n"
+ " account switch <index>\n"
+ " account label <index> <label text with white spaces allowed>\n"
+ " account tag <tag_name> <account_index_1> [<account_index_2> ...]\n"
+ " account untag <account_index_1> [<account_index_2> ...]\n"
+ " account tag_description <tag_name> <description>");
}
return true;
}
//----------------------------------------------------------------------------------------------------
void simple_wallet::print_accounts()
{
+ const std::pair<std::map<std::string, std::string>, std::vector<std::string>>& account_tags = m_wallet->get_account_tags();
+ size_t num_untagged_accounts = m_wallet->get_num_subaddress_accounts();
+ for (const std::pair<std::string, std::string>& p : account_tags.first)
+ {
+ const std::string& tag = p.first;
+ print_accounts(tag);
+ num_untagged_accounts -= std::count(account_tags.second.begin(), account_tags.second.end(), tag);
+ success_msg_writer() << "";
+ }
+
+ if (num_untagged_accounts > 0)
+ print_accounts("");
+
+ if (num_untagged_accounts < m_wallet->get_num_subaddress_accounts())
+ success_msg_writer() << tr("\nGrand total:\n Balance: ") << print_money(m_wallet->balance_all()) << tr(", unlocked balance: ") << print_money(m_wallet->unlocked_balance_all());
+}
+//----------------------------------------------------------------------------------------------------
+void simple_wallet::print_accounts(const std::string& tag)
+{
+ const std::pair<std::map<std::string, std::string>, std::vector<std::string>>& account_tags = m_wallet->get_account_tags();
+ if (tag.empty())
+ {
+ success_msg_writer() << tr("Untagged accounts:");
+ }
+ else
+ {
+ if (account_tags.first.count(tag) == 0)
+ {
+ fail_msg_writer() << boost::format(tr("Tag %s is unregistered.")) % tag;
+ return;
+ }
+ success_msg_writer() << tr("Accounts with tag: ") << tag;
+ success_msg_writer() << tr("Tag's description: ") << account_tags.first.find(tag)->second;
+ }
success_msg_writer() << boost::format(" %15s %21s %21s %21s") % tr("Account") % tr("Balance") % tr("Unlocked balance") % tr("Label");
+ uint64_t total_balance = 0, total_unlocked_balance = 0;
for (uint32_t account_index = 0; account_index < m_wallet->get_num_subaddress_accounts(); ++account_index)
{
+ if (account_tags.second[account_index] != tag)
+ continue;
success_msg_writer() << boost::format(tr(" %c%8u %6s %21s %21s %21s"))
% (m_current_subaddress_account == account_index ? '*' : ' ')
% account_index
@@ -5596,9 +5756,11 @@ void simple_wallet::print_accounts()
% print_money(m_wallet->balance(account_index))
% print_money(m_wallet->unlocked_balance(account_index))
% m_wallet->get_subaddress_label({account_index, 0});
+ total_balance += m_wallet->balance(account_index);
+ total_unlocked_balance += m_wallet->unlocked_balance(account_index);
}
success_msg_writer() << tr("----------------------------------------------------------------------------------");
- success_msg_writer() << boost::format(tr("%15s %21s %21s")) % "Total" % print_money(m_wallet->balance_all()) % print_money(m_wallet->unlocked_balance_all());
+ success_msg_writer() << boost::format(tr("%15s %21s %21s")) % "Total" % print_money(total_balance) % print_money(total_unlocked_balance);
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::print_address(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
@@ -6046,6 +6208,7 @@ bool simple_wallet::export_key_images(const std::vector<std::string> &args)
try
{
+ LOCK_IDLE_SCOPE();
if (!m_wallet->export_key_images(filename))
{
fail_msg_writer() << tr("failed to save file ") << filename;
@@ -6078,6 +6241,7 @@ bool simple_wallet::import_key_images(const std::vector<std::string> &args)
}
std::string filename = args[0];
+ LOCK_IDLE_SCOPE();
try
{
uint64_t spent = 0, unspent = 0;
@@ -6109,6 +6273,7 @@ bool simple_wallet::export_outputs(const std::vector<std::string> &args)
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
std::string filename = args[0];
+ LOCK_IDLE_SCOPE();
try
{
std::vector<tools::wallet2::transfer_details> outs = m_wallet->export_outputs();
@@ -6207,6 +6372,7 @@ bool simple_wallet::import_outputs(const std::vector<std::string> &args)
boost::archive::binary_iarchive ar(iss);
ar >> outputs;
}
+ LOCK_IDLE_SCOPE();
size_t n_outputs = m_wallet->import_outputs(outputs);
success_msg_writer() << boost::lexical_cast<std::string>(n_outputs) << " outputs imported";
}
@@ -6425,6 +6591,7 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_params, arg_command);
command_line::add_arg(desc_params, arg_restore_deterministic_wallet );
+ command_line::add_arg(desc_params, arg_restore_multisig_wallet );
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);
diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h
index c0c33f6b8..024077ca1 100644
--- a/src/simplewallet/simplewallet.h
+++ b/src/simplewallet/simplewallet.h
@@ -92,6 +92,8 @@ namespace cryptonote
bool recover, bool two_random, const std::string &old_language);
bool new_wallet(const boost::program_options::variables_map& vm, const cryptonote::account_public_address& address,
const boost::optional<crypto::secret_key>& spendkey, const crypto::secret_key& viewkey);
+ bool new_wallet(const boost::program_options::variables_map& vm,
+ const std::string &multisig_keys, const std::string &old_language);
bool open_wallet(const boost::program_options::variables_map& vm);
bool close_wallet();
@@ -153,6 +155,7 @@ namespace cryptonote
);
bool account(const std::vector<std::string> &args = std::vector<std::string>());
void print_accounts();
+ void print_accounts(const std::string& tag);
bool print_address(const std::vector<std::string> &args = std::vector<std::string>());
bool print_integrated_address(const std::vector<std::string> &args = std::vector<std::string>());
bool address_book(const std::vector<std::string> &args = std::vector<std::string>());
@@ -305,6 +308,7 @@ namespace cryptonote
crypto::secret_key m_recovery_key; // recovery key (used as random for wallet gen)
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;
bool m_allow_mismatched_daemon_version;
diff --git a/src/version.cpp.in b/src/version.cpp.in
index d1444f867..18d62db6b 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.11.0.0"
+#define DEF_MONERO_VERSION "0.11.1.0-master"
#define DEF_MONERO_RELEASE_NAME "Helium Hydra"
#define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG
diff --git a/src/wallet/api/unsigned_transaction.cpp b/src/wallet/api/unsigned_transaction.cpp
index 4c8c5ade2..d22719189 100644
--- a/src/wallet/api/unsigned_transaction.cpp
+++ b/src/wallet/api/unsigned_transaction.cpp
@@ -293,6 +293,10 @@ std::vector<std::string> UnsignedTransactionImpl::recipientAddress() const
// TODO: return integrated address if short payment ID exists
std::vector<string> result;
for (const auto &utx: m_unsigned_tx_set.txes) {
+ if (utx.dests.empty()) {
+ MERROR("empty destinations, skipped");
+ continue;
+ }
result.push_back(cryptonote::get_account_address_as_str(m_wallet.m_wallet->testnet(), utx.dests[0].is_subaddress, utx.dests[0].addr));
}
return result;
diff --git a/src/wallet/api/utils.cpp b/src/wallet/api/utils.cpp
index a9646e038..e54dd9f1c 100644
--- a/src/wallet/api/utils.cpp
+++ b/src/wallet/api/utils.cpp
@@ -48,6 +48,11 @@ bool isAddressLocal(const std::string &address)
}
}
+void onStartup()
+{
+ tools::on_startup();
+}
+
}
diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h
index 8593bd1f9..ab1a48d6e 100644
--- a/src/wallet/api/wallet2_api.h
+++ b/src/wallet/api/wallet2_api.h
@@ -42,6 +42,7 @@ namespace Monero {
namespace Utils {
bool isAddressLocal(const std::string &hostaddr);
+ void onStartup();
}
template<typename T>
diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp
index 9f30e4ac5..07185d12c 100644
--- a/src/wallet/node_rpc_proxy.cpp
+++ b/src/wallet/node_rpc_proxy.cpp
@@ -68,7 +68,6 @@ void NodeRPCProxy::invalidate()
boost::optional<std::string> NodeRPCProxy::get_rpc_version(uint32_t &rpc_version) const
{
- const time_t now = time(NULL);
if (m_rpc_version == 0)
{
epee::json_rpc::request<cryptonote::COMMAND_RPC_GET_VERSION::request> req_t = AUTO_VAL_INIT(req_t);
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 04c6ee236..3aad6a0f3 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -53,6 +53,7 @@ using namespace epee;
#include "profile_tools.h"
#include "crypto/crypto.h"
#include "serialization/binary_utils.h"
+#include "serialization/string.h"
#include "cryptonote_basic/blobdatatype.h"
#include "mnemonics/electrum-words.h"
#include "common/i18n.h"
@@ -62,7 +63,7 @@ using namespace epee;
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include "common/json_util.h"
-#include "common/memwipe.h"
+#include "memwipe.h"
#include "common/base58.h"
#include "ringct/rctSigs.h"
@@ -144,6 +145,16 @@ uint64_t calculate_fee(uint64_t fee_per_kb, const cryptonote::blobdata &blob, ui
return calculate_fee(fee_per_kb, blob.size(), fee_multiplier);
}
+std::string get_size_string(size_t sz)
+{
+ return std::to_string(sz) + " bytes (" + std::to_string((sz + 1023) / 1024) + " kB)";
+}
+
+std::string get_size_string(const cryptonote::blobdata &tx)
+{
+ return get_size_string(tx.size());
+}
+
std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variables_map& vm, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
{
const bool testnet = command_line::get_arg(vm, opts.testnet);
@@ -540,6 +551,11 @@ crypto::hash8 get_short_payment_id(const tools::wallet2::pending_tx &ptx)
{
if(get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
{
+ if (ptx.dests.empty())
+ {
+ MWARNING("Encrypted payment id found, but no destinations public key, cannot decrypt");
+ return crypto::null_hash8;
+ }
decrypt_payment_id(payment_id8, ptx.dests[0].addr.m_view_public_key, ptx.tx_key);
}
}
@@ -595,6 +611,7 @@ wallet2::wallet2(bool testnet, bool restricted):
m_min_output_value(0),
m_merge_destinations(false),
m_confirm_backlog(true),
+ m_confirm_backlog_threshold(0),
m_is_initialized(false),
m_restricted(restricted),
is_old_file_format(false),
@@ -710,10 +727,78 @@ bool wallet2::get_seed(std::string& electrum_words, const epee::wipeable_string
crypto::secret_key key = get_account().get_keys().m_spend_secret_key;
if (!passphrase.empty())
key = cryptonote::encrypt_key(key, passphrase);
- crypto::ElectrumWords::bytes_to_words(key, electrum_words, seed_language);
+ if (!crypto::ElectrumWords::bytes_to_words(key, electrum_words, seed_language))
+ {
+ std::cout << "Failed to create seed from key for language: " << seed_language << std::endl;
+ return false;
+ }
+
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
+bool wallet2::get_multisig_seed(std::string& seed, const epee::wipeable_string &passphrase, bool raw) const
+{
+ bool ready;
+ uint32_t threshold, total;
+ if (!multisig(&ready, &threshold, &total))
+ {
+ std::cout << "This is not a multisig wallet" << std::endl;
+ return false;
+ }
+ if (!ready)
+ {
+ std::cout << "This multisig wallet is not yet finalized" << std::endl;
+ return false;
+ }
+ if (!raw && seed_language.empty())
+ {
+ std::cout << "seed_language not set" << std::endl;
+ return false;
+ }
+
+ crypto::secret_key skey;
+ crypto::public_key pkey;
+ const account_keys &keys = get_account().get_keys();
+ std::string data;
+ data.append((const char*)&threshold, sizeof(uint32_t));
+ data.append((const char*)&total, sizeof(uint32_t));
+ skey = keys.m_spend_secret_key;
+ data.append((const char*)&skey, sizeof(skey));
+ pkey = keys.m_account_address.m_spend_public_key;
+ data.append((const char*)&pkey, sizeof(pkey));
+ skey = keys.m_view_secret_key;
+ data.append((const char*)&skey, sizeof(skey));
+ pkey = keys.m_account_address.m_view_public_key;
+ data.append((const char*)&pkey, sizeof(pkey));
+ for (const auto &skey: keys.m_multisig_keys)
+ data.append((const char*)&skey, sizeof(skey));
+ for (const auto &signer: m_multisig_signers)
+ data.append((const char*)&signer, sizeof(signer));
+
+ if (!passphrase.empty())
+ {
+ crypto::secret_key key;
+ crypto::cn_slow_hash(passphrase.data(), passphrase.size(), (crypto::hash&)key);
+ sc_reduce32((unsigned char*)key.data);
+ data = encrypt(data, key, true);
+ }
+
+ if (raw)
+ {
+ seed = epee::string_tools::buff_to_hex_nodelimer(data);
+ }
+ else
+ {
+ if (!crypto::ElectrumWords::bytes_to_words(data.data(), data.size(), seed, seed_language))
+ {
+ std::cout << "Failed to encode seed";
+ return false;
+ }
+ }
return true;
}
+//----------------------------------------------------------------------------------------------------
/*!
* \brief Gets the seed language
*/
@@ -986,7 +1071,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
LOG_PRINT_L0("Public key wasn't found in the transaction extra. Skipping transaction " << txid);
if(0 != m_callback)
m_callback->on_skip_transaction(height, txid, tx);
- return;
+ break;
}
int num_vouts_received = 0;
@@ -1951,6 +2036,11 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height,
pull_hashes(0, blocks_start_height, short_chain_history, hashes);
if (hashes.size() <= 3)
return;
+ if (blocks_start_height < m_blockchain.offset())
+ {
+ MERROR("Blocks start before blockchain offset: " << blocks_start_height << " " << m_blockchain.offset());
+ return;
+ }
if (hashes.size() + current_index < stop_height) {
drop_from_short_history(short_chain_history, 3);
std::list<crypto::hash>::iterator right = hashes.end();
@@ -2364,12 +2454,12 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
account_data = buffer.GetString();
// Encrypt the entire JSON object.
- crypto::chacha8_key key;
- crypto::generate_chacha8_key(password.data(), password.size(), key);
+ crypto::chacha_key key;
+ crypto::generate_chacha_key(password.data(), password.size(), key);
std::string cipher;
cipher.resize(account_data.size());
- keys_file_data.iv = crypto::rand<crypto::chacha8_iv>();
- crypto::chacha8(account_data.data(), account_data.size(), key, keys_file_data.iv, &cipher[0]);
+ keys_file_data.iv = crypto::rand<crypto::chacha_iv>();
+ crypto::chacha20(account_data.data(), account_data.size(), key, keys_file_data.iv, &cipher[0]);
keys_file_data.account_data = cipher;
std::string buf;
@@ -2397,6 +2487,7 @@ namespace
*/
bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_string& password)
{
+ rapidjson::Document json;
wallet2::keys_file_data keys_file_data;
std::string buf;
bool r = epee::file_io_utils::load_file_to_string(keys_file_name, buf);
@@ -2405,14 +2496,15 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
// Decrypt the contents
r = ::serialization::parse_binary(buf, keys_file_data);
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + keys_file_name + '\"');
- crypto::chacha8_key key;
- crypto::generate_chacha8_key(password.data(), password.size(), key);
+ crypto::chacha_key key;
+ crypto::generate_chacha_key(password.data(), password.size(), key);
std::string account_data;
account_data.resize(keys_file_data.account_data.size());
- crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
+ crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
+ if (json.Parse(account_data.c_str()).HasParseError() || !json.IsObject())
+ crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
// The contents should be JSON if the wallet follows the new format.
- rapidjson::Document json;
if (json.Parse(account_data.c_str()).HasParseError())
{
is_old_file_format = true;
@@ -2582,6 +2674,7 @@ bool wallet2::verify_password(const epee::wipeable_string& password) const
*/
bool wallet2::verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key)
{
+ rapidjson::Document json;
wallet2::keys_file_data keys_file_data;
std::string buf;
bool r = epee::file_io_utils::load_file_to_string(keys_file_name, buf);
@@ -2590,14 +2683,15 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip
// Decrypt the contents
r = ::serialization::parse_binary(buf, keys_file_data);
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + keys_file_name + '\"');
- crypto::chacha8_key key;
- crypto::generate_chacha8_key(password.data(), password.size(), key);
+ crypto::chacha_key key;
+ crypto::generate_chacha_key(password.data(), password.size(), key);
std::string account_data;
account_data.resize(keys_file_data.account_data.size());
- crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
+ crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
+ if (json.Parse(account_data.c_str()).HasParseError() || !json.IsObject())
+ crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
// The contents should be JSON if the wallet follows the new format.
- rapidjson::Document json;
if (json.Parse(account_data.c_str()).HasParseError())
{
// old format before JSON wallet key file format
@@ -2623,6 +2717,97 @@ 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
+ */
+void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password,
+ const std::string& multisig_data)
+{
+ clear();
+ prepare_file_names(wallet_);
+
+ if (!wallet_.empty())
+ {
+ boost::system::error_code ignored_ec;
+ THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_wallet_file, ignored_ec), error::file_exists, m_wallet_file);
+ THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file);
+ }
+
+ m_account.generate(rct::rct2sk(rct::zero()), true, false);
+
+ THROW_WALLET_EXCEPTION_IF(multisig_data.size() < 32, error::invalid_multisig_seed);
+ size_t offset = 0;
+ uint32_t threshold = *(uint32_t*)(multisig_data.data() + offset);
+ offset += sizeof(uint32_t);
+ uint32_t total = *(uint32_t*)(multisig_data.data() + offset);
+ offset += sizeof(uint32_t);
+ THROW_WALLET_EXCEPTION_IF(threshold < 2, error::invalid_multisig_seed);
+ THROW_WALLET_EXCEPTION_IF(total != threshold && total != threshold + 1, error::invalid_multisig_seed);
+ const size_t n_multisig_keys = total == threshold ? 1 : threshold;
+ THROW_WALLET_EXCEPTION_IF(multisig_data.size() != 8 + 32 * (4 + n_multisig_keys + total), error::invalid_multisig_seed);
+
+ std::vector<crypto::secret_key> multisig_keys;
+ std::vector<crypto::public_key> multisig_signers;
+ crypto::secret_key spend_secret_key = *(crypto::secret_key*)(multisig_data.data() + offset);
+ offset += sizeof(crypto::secret_key);
+ crypto::public_key spend_public_key = *(crypto::public_key*)(multisig_data.data() + offset);
+ offset += sizeof(crypto::public_key);
+ crypto::secret_key view_secret_key = *(crypto::secret_key*)(multisig_data.data() + offset);
+ offset += sizeof(crypto::secret_key);
+ crypto::public_key view_public_key = *(crypto::public_key*)(multisig_data.data() + offset);
+ offset += sizeof(crypto::public_key);
+ for (size_t n = 0; n < n_multisig_keys; ++n)
+ {
+ multisig_keys.push_back(*(crypto::secret_key*)(multisig_data.data() + offset));
+ offset += sizeof(crypto::secret_key);
+ }
+ for (size_t n = 0; n < total; ++n)
+ {
+ multisig_signers.push_back(*(crypto::public_key*)(multisig_data.data() + offset));
+ offset += sizeof(crypto::public_key);
+ }
+
+ crypto::public_key calculated_view_public_key;
+ THROW_WALLET_EXCEPTION_IF(!crypto::secret_key_to_public_key(view_secret_key, calculated_view_public_key), error::invalid_multisig_seed);
+ THROW_WALLET_EXCEPTION_IF(view_public_key != calculated_view_public_key, error::invalid_multisig_seed);
+ crypto::public_key local_signer;
+ THROW_WALLET_EXCEPTION_IF(!crypto::secret_key_to_public_key(spend_secret_key, local_signer), error::invalid_multisig_seed);
+ THROW_WALLET_EXCEPTION_IF(std::find(multisig_signers.begin(), multisig_signers.end(), local_signer) == multisig_signers.end(), error::invalid_multisig_seed);
+ rct::key skey = rct::zero();
+ for (const auto &msk: multisig_keys)
+ sc_add(skey.bytes, skey.bytes, rct::sk2rct(msk).bytes);
+ THROW_WALLET_EXCEPTION_IF(!(rct::rct2sk(skey) == spend_secret_key), error::invalid_multisig_seed);
+
+ m_account.make_multisig(view_secret_key, spend_secret_key, spend_public_key, multisig_keys);
+ m_account.finalize_multisig(spend_public_key);
+
+ m_account_public_address = m_account.get_keys().m_account_address;
+ m_watch_only = false;
+ m_multisig = true;
+ m_multisig_threshold = threshold;
+ m_multisig_signers = multisig_signers;
+
+ if (!wallet_.empty())
+ {
+ bool r = store_keys(m_keys_file, password, false);
+ THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
+
+ r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_testnet));
+ if(!r) MERROR("String with address text not saved");
+ }
+
+ cryptonote::block b;
+ generate_genesis(b);
+ m_blockchain.push_back(get_block_hash(b));
+ add_subaddress_account(tr("Primary account"));
+
+ if (!wallet_.empty())
+ store();
+}
+
+/*!
+ * \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
@@ -3283,7 +3468,7 @@ bool wallet2::check_connection(uint32_t *version, uint32_t timeout)
return true;
}
//----------------------------------------------------------------------------------------------------
-bool wallet2::generate_chacha8_key_from_secret_keys(crypto::chacha8_key &key) const
+bool wallet2::generate_chacha_key_from_secret_keys(crypto::chacha_key &key) const
{
const account_keys &keys = m_account.get_keys();
const crypto::secret_key &view_key = keys.m_view_secret_key;
@@ -3292,7 +3477,7 @@ bool wallet2::generate_chacha8_key_from_secret_keys(crypto::chacha8_key &key) co
memcpy(data.data(), &view_key, sizeof(view_key));
memcpy(data.data() + sizeof(view_key), &spend_key, sizeof(spend_key));
data[sizeof(data) - 1] = CHACHA8_KEY_TAIL;
- crypto::generate_chacha8_key(data.data(), sizeof(data), key);
+ crypto::generate_chacha_key(data.data(), sizeof(data), key);
return true;
}
//----------------------------------------------------------------------------------------------------
@@ -3332,34 +3517,46 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
r = ::serialization::parse_binary(buf, cache_file_data);
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + m_wallet_file + '\"');
- crypto::chacha8_key key;
- generate_chacha8_key_from_secret_keys(key);
+ crypto::chacha_key key;
+ generate_chacha_key_from_secret_keys(key);
std::string cache_data;
cache_data.resize(cache_file_data.cache_data.size());
- crypto::chacha8(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cache_data[0]);
+ crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cache_data[0]);
- std::stringstream iss;
- iss << cache_data;
try {
+ std::stringstream iss;
+ iss << cache_data;
boost::archive::portable_binary_iarchive ar(iss);
ar >> *this;
}
catch (...)
{
- LOG_PRINT_L0("Failed to open portable binary, trying unportable");
- boost::filesystem::copy_file(m_wallet_file, m_wallet_file + ".unportable", boost::filesystem::copy_option::overwrite_if_exists);
- iss.str("");
- iss << cache_data;
- boost::archive::binary_iarchive ar(iss);
- ar >> *this;
+ crypto::chacha8(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cache_data[0]);
+ try
+ {
+ std::stringstream iss;
+ iss << cache_data;
+ boost::archive::portable_binary_iarchive ar(iss);
+ ar >> *this;
+ }
+ catch (...)
+ {
+ LOG_PRINT_L0("Failed to open portable binary, trying unportable");
+ boost::filesystem::copy_file(m_wallet_file, m_wallet_file + ".unportable", boost::filesystem::copy_option::overwrite_if_exists);
+ std::stringstream iss;
+ iss.str("");
+ iss << cache_data;
+ boost::archive::binary_iarchive ar(iss);
+ ar >> *this;
+ }
}
}
catch (...)
{
LOG_PRINT_L1("Failed to load encrypted cache, trying unencrypted");
- std::stringstream iss;
- iss << buf;
try {
+ std::stringstream iss;
+ iss << buf;
boost::archive::portable_binary_iarchive ar(iss);
ar >> *this;
}
@@ -3367,6 +3564,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
{
LOG_PRINT_L0("Failed to open portable binary, trying unportable");
boost::filesystem::copy_file(m_wallet_file, m_wallet_file + ".unportable", boost::filesystem::copy_option::overwrite_if_exists);
+ std::stringstream iss;
iss.str("");
iss << buf;
boost::archive::binary_iarchive ar(iss);
@@ -3491,12 +3689,12 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
wallet2::cache_file_data cache_file_data = boost::value_initialized<wallet2::cache_file_data>();
cache_file_data.cache_data = oss.str();
- crypto::chacha8_key key;
- generate_chacha8_key_from_secret_keys(key);
+ crypto::chacha_key key;
+ generate_chacha_key_from_secret_keys(key);
std::string cipher;
cipher.resize(cache_file_data.cache_data.size());
- cache_file_data.iv = crypto::rand<crypto::chacha8_iv>();
- crypto::chacha8(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cipher[0]);
+ cache_file_data.iv = crypto::rand<crypto::chacha_iv>();
+ crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cipher[0]);
cache_file_data.cache_data = cipher;
const std::string new_file = same_file ? m_wallet_file + ".new" : path;
@@ -3504,14 +3702,6 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
const std::string old_keys_file = m_keys_file;
const std::string old_address_file = m_wallet_file + ".address.txt";
- // save to new file
- std::ofstream ostr;
- ostr.open(new_file, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc);
- binary_archive<true> oar(ostr);
- bool success = ::serialization::serialize(oar, cache_file_data);
- ostr.close();
- THROW_WALLET_EXCEPTION_IF(!success || !ostr.good(), error::file_save_error, new_file);
-
// save keys to the new file
// if we here, main wallet file is saved and we only need to save keys and address files
if (!same_file) {
@@ -3538,6 +3728,14 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
LOG_ERROR("error removing file: " << old_address_file);
}
} else {
+ // save to new file
+ std::ofstream ostr;
+ ostr.open(new_file, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc);
+ binary_archive<true> oar(ostr);
+ bool success = ::serialization::serialize(oar, cache_file_data);
+ ostr.close();
+ THROW_WALLET_EXCEPTION_IF(!success || !ostr.good(), error::file_save_error, new_file);
+
// here we have "*.new" file, we need to rename it to be without ".new"
std::error_code e = tools::replace_file(new_file, m_wallet_file);
THROW_WALLET_EXCEPTION_IF(e, error::file_save_error, m_wallet_file, e);
@@ -4050,6 +4248,11 @@ crypto::hash wallet2::get_payment_id(const pending_tx &ptx) const
crypto::hash8 payment_id8 = null_hash8;
if(get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
{
+ if (ptx.dests.empty())
+ {
+ MWARNING("Encrypted payment id found, but no destinations public key, cannot decrypt");
+ return crypto::null_hash;
+ }
if (decrypt_payment_id(payment_id8, ptx.dests[0].addr.m_view_public_key, ptx.tx_key))
{
memcpy(payment_id.data, payment_id8.data, 8);
@@ -4274,6 +4477,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_f
for (size_t n = 0; n < exported_txs.txes.size(); ++n)
{
tools::wallet2::tx_construction_data &sd = exported_txs.txes[n];
+ THROW_WALLET_EXCEPTION_IF(sd.sources.empty(), error::wallet_internal_error, "Empty sources");
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();
@@ -4950,6 +5154,7 @@ bool wallet2::tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_out
if (global_index == real_index) // don't re-add real one
return false;
auto item = std::make_tuple(global_index, tx_public_key, mask);
+ CHECK_AND_ASSERT_MES(!outs.empty(), false, "internal error: outs is empty");
if (std::find(outs.back().begin(), outs.back().end(), item) != outs.back().end()) // don't add duplicates
return false;
outs.back().push_back(item);
@@ -5114,7 +5319,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
// if there are just enough outputs to mix with, use all of them.
// Eventually this should become impossible.
uint64_t num_outs = 0, num_recent_outs = 0;
- for (auto he: resp_t.result.histogram)
+ for (const auto &he: resp_t.result.histogram)
{
if (he.amount == amount)
{
@@ -5715,7 +5920,19 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui
LOG_PRINT_L2("pick_preferred_rct_inputs: needed_money " << print_money(needed_money));
- // try to find two outputs
+ // try to find a rct input of enough size
+ for (size_t i = 0; i < m_transfers.size(); ++i)
+ {
+ const transfer_details& td = m_transfers[i];
+ if (!td.m_spent && td.is_rct() && td.amount() >= needed_money && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
+ {
+ LOG_PRINT_L2("We can use " << i << " alone: " << print_money(td.amount()));
+ picks.push_back(i);
+ return picks;
+ }
+ }
+
+ // then try to find two outputs
// this could be made better by picking one of the outputs to be a small one, since those
// are less useful since often below the needed money, so if one can be used in a pair,
// it gets rid of it for the future
@@ -5948,7 +6165,8 @@ void wallet2::light_wallet_get_unspent_outs()
add_tx_pub_key_to_extra(td.m_tx, tx_pub_key);
td.m_key_image = unspent_key_image;
- td.m_key_image_known = !m_watch_only;
+ td.m_key_image_known = !m_watch_only && !m_multisig;
+ td.m_key_image_partial = m_multisig;
td.m_amount = o.amount;
td.m_pk_index = 0;
td.m_internal_output_index = o.index;
@@ -6223,7 +6441,8 @@ bool wallet2::light_wallet_parse_rct_str(const std::string& rct_string, const cr
if (decrypt) {
// Decrypt the mask
crypto::key_derivation derivation;
- generate_key_derivation(tx_pub_key, get_account().get_keys().m_view_secret_key, derivation);
+ bool r = generate_key_derivation(tx_pub_key, get_account().get_keys().m_view_secret_key, derivation);
+ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation");
crypto::secret_key scalar;
crypto::derivation_to_scalar(derivation, internal_output_index, scalar);
sc_sub(decrypted_mask.bytes,encrypted_mask.bytes,rct::hash_to_scalar(rct::sk2rct(scalar)).bytes);
@@ -6621,6 +6840,17 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
const size_t estimated_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof);
needed_fee = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier);
+ uint64_t inputs = 0, outputs = needed_fee;
+ for (size_t idx: tx.selected_transfers) inputs += m_transfers[idx].amount();
+ for (const auto &o: tx.dsts) outputs += o.amount;
+
+ if (inputs < outputs)
+ {
+ LOG_PRINT_L2("We don't have enough for the basic fee, switching to adding_fee");
+ adding_fee = true;
+ goto skip_tx;
+ }
+
LOG_PRINT_L2("Trying to create a tx now, with " << tx.dsts.size() << " outputs and " <<
tx.selected_transfers.size() << " inputs");
if (use_rct)
@@ -6632,10 +6862,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier);
available_for_fee = test_ptx.fee + test_ptx.change_dts.amount + (!test_ptx.dust_added_to_fee ? test_ptx.dust : 0);
- LOG_PRINT_L2("Made a " << ((txBlob.size() + 1023) / 1024) << " kB tx, with " << print_money(available_for_fee) << " available for fee (" <<
+ LOG_PRINT_L2("Made a " << get_size_string(txBlob) << " tx, with " << print_money(available_for_fee) << " available for fee (" <<
print_money(needed_fee) << " needed)");
- if (needed_fee > available_for_fee && dsts[0].amount > 0)
+ if (needed_fee > available_for_fee && !dsts.empty() && dsts[0].amount > 0)
{
// we don't have enough for the fee, but we've only partially paid the current address,
// so we can take the fee from the paid amount, since we'll have to make another tx anyway
@@ -6674,11 +6904,11 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
txBlob = t_serializable_object_to_blob(test_ptx.tx);
needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier);
- LOG_PRINT_L2("Made an attempt at a final " << ((txBlob.size() + 1023)/1024) << " kB tx, with " << print_money(test_ptx.fee) <<
+ LOG_PRINT_L2("Made an attempt at a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change");
}
- LOG_PRINT_L2("Made a final " << ((txBlob.size() + 1023)/1024) << " kB tx, with " << print_money(test_ptx.fee) <<
+ LOG_PRINT_L2("Made a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change");
tx.tx = test_tx;
@@ -6696,6 +6926,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
}
+skip_tx:
// if unused_*_indices is empty while unused_*_indices_per_subaddr has multiple elements, and if we still have something to pay,
// pop front of unused_*_indices_per_subaddr and have unused_*_indices point to the front of unused_*_indices_per_subaddr
if ((!dsts.empty() && dsts[0].amount > 0) || adding_fee)
@@ -6730,7 +6961,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
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() <<
- ": " << (tx.bytes+1023)/1024 << " kB, sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() <<
+ ": " << 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);
@@ -6748,37 +6979,48 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below
THROW_WALLET_EXCEPTION_IF(unlocked_balance(subaddr_account) == 0, error::wallet_internal_error, "No unlocked balance in the entire wallet");
- std::map<uint32_t, uint64_t> balance_per_subaddr = unlocked_balance_per_subaddress(subaddr_account);
+ std::map<uint32_t, std::pair<std::vector<size_t>, std::vector<size_t>>> unused_transfer_dust_indices_per_subaddr;
- if (subaddr_indices.empty())
- {
- // in case subaddress index wasn't specified, choose non-empty subaddress randomly (with index=0 being chosen last)
- if (balance_per_subaddr.count(0) == 1 && balance_per_subaddr.size() > 1)
- balance_per_subaddr.erase(0);
- auto i = balance_per_subaddr.begin();
- std::advance(i, crypto::rand<size_t>() % balance_per_subaddr.size());
- subaddr_indices.insert(i->first);
- }
- for (uint32_t i : subaddr_indices)
- LOG_PRINT_L2("Spending from subaddress index " << i);
-
- // gather all dust and non-dust outputs of specified subaddress
+ // gather all dust and non-dust outputs of specified subaddress (if any) and below specified threshold (if any)
+ bool fund_found = false;
for (size_t i = 0; i < m_transfers.size(); ++i)
{
const transfer_details& td = m_transfers[i];
- if (!td.m_spent && !td.m_key_image_partial && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
+ if (!td.m_spent && !td.m_key_image_partial && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && (subaddr_indices.empty() || subaddr_indices.count(td.m_subaddr_index.minor) == 1))
{
+ fund_found = true;
if (below == 0 || td.amount() < below)
{
if ((td.is_rct()) || is_valid_decomposed_amount(td.amount()))
- unused_transfers_indices.push_back(i);
+ unused_transfer_dust_indices_per_subaddr[td.m_subaddr_index.minor].first.push_back(i);
else
- unused_dust_indices.push_back(i);
+ unused_transfer_dust_indices_per_subaddr[td.m_subaddr_index.minor].second.push_back(i);
}
}
}
+ THROW_WALLET_EXCEPTION_IF(!fund_found, error::wallet_internal_error, "No unlocked balance in the specified subaddress(es)");
+ THROW_WALLET_EXCEPTION_IF(unused_transfer_dust_indices_per_subaddr.empty(), error::wallet_internal_error, "The smallest amount found is not below the specified threshold");
- THROW_WALLET_EXCEPTION_IF(unused_transfers_indices.empty() && unused_dust_indices.empty(), error::not_enough_money, 0, 0, 0); // not sure if a new error class (something like 'cant_sweep_empty'?) should be introduced
+ if (subaddr_indices.empty())
+ {
+ // in case subaddress index wasn't specified, choose non-empty subaddress randomly (with index=0 being chosen last)
+ if (unused_transfer_dust_indices_per_subaddr.count(0) == 1 && unused_transfer_dust_indices_per_subaddr.size() > 1)
+ unused_transfer_dust_indices_per_subaddr.erase(0);
+ auto i = unused_transfer_dust_indices_per_subaddr.begin();
+ std::advance(i, crypto::rand<size_t>() % unused_transfer_dust_indices_per_subaddr.size());
+ unused_transfers_indices = i->second.first;
+ unused_dust_indices = i->second.second;
+ LOG_PRINT_L2("Spending from subaddress index " << i->first);
+ }
+ else
+ {
+ for (const auto& p : unused_transfer_dust_indices_per_subaddr)
+ {
+ unused_transfers_indices.insert(unused_transfers_indices.end(), p.second.first.begin(), p.second.first.end());
+ unused_dust_indices.insert(unused_dust_indices.end(), p.second.second.begin(), p.second.second.end());
+ LOG_PRINT_L2("Spending from subaddress index " << p.first);
+ }
+ }
return create_transactions_from(address, is_subaddress, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra, trusted_daemon);
}
@@ -6882,7 +7124,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier);
available_for_fee = test_ptx.fee + test_ptx.dests[0].amount + test_ptx.change_dts.amount;
- LOG_PRINT_L2("Made a " << ((txBlob.size() + 1023) / 1024) << " kB tx, with " << print_money(available_for_fee) << " available for fee (" <<
+ LOG_PRINT_L2("Made a " << get_size_string(txBlob) << " tx, with " << print_money(available_for_fee) << " available for fee (" <<
print_money(needed_fee) << " needed)");
THROW_WALLET_EXCEPTION_IF(needed_fee > available_for_fee, error::wallet_internal_error, "Transaction cannot pay for itself");
@@ -6898,11 +7140,11 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
txBlob = t_serializable_object_to_blob(test_ptx.tx);
needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier);
- LOG_PRINT_L2("Made an attempt at a final " << ((txBlob.size() + 1023)/1024) << " kB tx, with " << print_money(test_ptx.fee) <<
+ LOG_PRINT_L2("Made an attempt at a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change");
}
- LOG_PRINT_L2("Made a final " << ((txBlob.size() + 1023)/1024) << " kB tx, with " << print_money(test_ptx.fee) <<
+ LOG_PRINT_L2("Made a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change");
tx.tx = test_tx;
@@ -6929,7 +7171,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() <<
- ": " << (tx.bytes+1023)/1024 << " kB, sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() <<
+ ": " << 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);
@@ -7413,12 +7655,14 @@ void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_de
continue;
crypto::public_key derived_out_key;
- derive_public_key(derivation, n, address.m_spend_public_key, derived_out_key);
+ bool r = derive_public_key(derivation, n, address.m_spend_public_key, derived_out_key);
+ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to derive public key");
bool found = out_key->key == derived_out_key;
crypto::key_derivation found_derivation = derivation;
if (!found && !additional_derivations.empty())
{
- derive_public_key(additional_derivations[n], n, address.m_spend_public_key, derived_out_key);
+ r = derive_public_key(additional_derivations[n], n, address.m_spend_public_key, derived_out_key);
+ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to derive public key");
found = out_key->key == derived_out_key;
found_derivation = additional_derivations[n];
}
@@ -7818,6 +8062,46 @@ std::string wallet2::get_description() const
return get_attribute(ATTRIBUTE_DESCRIPTION);
}
+const std::pair<std::map<std::string, std::string>, std::vector<std::string>>& wallet2::get_account_tags()
+{
+ // ensure consistency
+ if (m_account_tags.second.size() != get_num_subaddress_accounts())
+ m_account_tags.second.resize(get_num_subaddress_accounts(), "");
+ for (const std::string& tag : m_account_tags.second)
+ {
+ if (!tag.empty() && m_account_tags.first.count(tag) == 0)
+ m_account_tags.first.insert({tag, ""});
+ }
+ for (auto i = m_account_tags.first.begin(); i != m_account_tags.first.end(); )
+ {
+ if (std::find(m_account_tags.second.begin(), m_account_tags.second.end(), i->first) == m_account_tags.second.end())
+ i = m_account_tags.first.erase(i);
+ else
+ ++i;
+ }
+ return m_account_tags;
+}
+
+void wallet2::set_account_tag(const std::set<uint32_t> account_indices, const std::string& tag)
+{
+ for (uint32_t account_index : account_indices)
+ {
+ THROW_WALLET_EXCEPTION_IF(account_index >= get_num_subaddress_accounts(), error::wallet_internal_error, "Account index out of bound");
+ if (m_account_tags.second[account_index] == tag)
+ MDEBUG("This tag is already assigned to this account");
+ else
+ m_account_tags.second[account_index] = tag;
+ }
+ get_account_tags();
+}
+
+void wallet2::set_account_tag_description(const std::string& tag, const std::string& description)
+{
+ THROW_WALLET_EXCEPTION_IF(tag.empty(), error::wallet_internal_error, "Tag must not be empty");
+ THROW_WALLET_EXCEPTION_IF(m_account_tags.first.count(tag) == 0, error::wallet_internal_error, "Tag is unregistered");
+ m_account_tags.first[tag] = description;
+}
+
std::string wallet2::sign(const std::string &data) const
{
crypto::hash hash;
@@ -7883,13 +8167,15 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle
for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
{
additional_derivations.push_back({});
- generate_key_derivation(additional_tx_pub_keys[i], keys.m_view_secret_key, additional_derivations.back());
+ bool r = generate_key_derivation(additional_tx_pub_keys[i], keys.m_view_secret_key, additional_derivations.back());
+ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation");
}
while (find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, pk_index++)) {
const crypto::public_key tx_pub_key = pub_key_field.pub_key;
crypto::key_derivation derivation;
- generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation);
+ bool r = generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation);
+ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation");
for (size_t i = 0; i < td.m_tx.vout.size(); ++i)
{
@@ -8176,13 +8462,15 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
const cryptonote::account_keys& keys = m_account.get_keys();
const crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(spent_tx);
crypto::key_derivation derivation;
- generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation);
+ bool r = generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation);
+ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation");
const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(spent_tx);
std::vector<crypto::key_derivation> additional_derivations;
for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
{
additional_derivations.push_back({});
- generate_key_derivation(additional_tx_pub_keys[i], keys.m_view_secret_key, additional_derivations.back());
+ r = generate_key_derivation(additional_tx_pub_keys[i], keys.m_view_secret_key, additional_derivations.back());
+ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation");
}
size_t output_index = 0;
for (const cryptonote::tx_out& out : spent_tx.vout)
@@ -8657,12 +8945,12 @@ size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs)
//----------------------------------------------------------------------------------------------------
std::string wallet2::encrypt(const std::string &plaintext, const crypto::secret_key &skey, bool authenticated) const
{
- crypto::chacha8_key key;
- crypto::generate_chacha8_key(&skey, sizeof(skey), key);
+ crypto::chacha_key key;
+ crypto::generate_chacha_key(&skey, sizeof(skey), key);
std::string ciphertext;
- crypto::chacha8_iv iv = crypto::rand<crypto::chacha8_iv>();
+ crypto::chacha_iv iv = crypto::rand<crypto::chacha_iv>();
ciphertext.resize(plaintext.size() + sizeof(iv) + (authenticated ? sizeof(crypto::signature) : 0));
- crypto::chacha8(plaintext.data(), plaintext.size(), key, iv, &ciphertext[sizeof(iv)]);
+ crypto::chacha20(plaintext.data(), plaintext.size(), key, iv, &ciphertext[sizeof(iv)]);
memcpy(&ciphertext[0], &iv, sizeof(iv));
if (authenticated)
{
@@ -8683,13 +8971,13 @@ std::string wallet2::encrypt_with_view_secret_key(const std::string &plaintext,
//----------------------------------------------------------------------------------------------------
std::string wallet2::decrypt(const std::string &ciphertext, const crypto::secret_key &skey, bool authenticated) const
{
- const size_t prefix_size = sizeof(chacha8_iv) + (authenticated ? sizeof(crypto::signature) : 0);
+ const size_t prefix_size = sizeof(chacha_iv) + (authenticated ? sizeof(crypto::signature) : 0);
THROW_WALLET_EXCEPTION_IF(ciphertext.size() < prefix_size,
error::wallet_internal_error, "Unexpected ciphertext size");
- crypto::chacha8_key key;
- crypto::generate_chacha8_key(&skey, sizeof(skey), key);
- const crypto::chacha8_iv &iv = *(const crypto::chacha8_iv*)&ciphertext[0];
+ crypto::chacha_key key;
+ crypto::generate_chacha_key(&skey, sizeof(skey), key);
+ const crypto::chacha_iv &iv = *(const crypto::chacha_iv*)&ciphertext[0];
std::string plaintext;
plaintext.resize(ciphertext.size() - prefix_size);
if (authenticated)
@@ -8702,7 +8990,7 @@ std::string wallet2::decrypt(const std::string &ciphertext, const crypto::secret
THROW_WALLET_EXCEPTION_IF(!crypto::check_signature(hash, pkey, signature),
error::wallet_internal_error, "Failed to authenticate ciphertext");
}
- crypto::chacha8(ciphertext.data() + sizeof(iv), ciphertext.size() - prefix_size, key, iv, &plaintext[0]);
+ crypto::chacha20(ciphertext.data() + sizeof(iv), ciphertext.size() - prefix_size, key, iv, &plaintext[0]);
return plaintext;
}
//----------------------------------------------------------------------------------------------------
@@ -8904,6 +9192,7 @@ uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, ui
throw std::runtime_error(oss.str());
}
cryptonote::block blk_min, blk_mid, blk_max;
+ if (res.blocks.size() < 3) throw std::runtime_error("Not enough blocks returned from daemon");
if (!parse_and_validate_block_from_blob(res.blocks[0].block, blk_min)) throw std::runtime_error("failed to parse blob at height " + std::to_string(height_min));
if (!parse_and_validate_block_from_blob(res.blocks[1].block, blk_mid)) throw std::runtime_error("failed to parse blob at height " + std::to_string(height_mid));
if (!parse_and_validate_block_from_blob(res.blocks[2].block, blk_max)) throw std::runtime_error("failed to parse blob at height " + std::to_string(height_max));
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 399287c3e..2fbe05f89 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -49,7 +49,7 @@
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "cryptonote_core/cryptonote_tx_utils.h"
#include "common/unordered_containers_boost_serialization.h"
-#include "crypto/chacha8.h"
+#include "crypto/chacha.h"
#include "crypto/hash.h"
#include "ringct/rctTypes.h"
#include "ringct/rctOps.h"
@@ -404,7 +404,7 @@ namespace tools
struct keys_file_data
{
- crypto::chacha8_iv iv;
+ crypto::chacha_iv iv;
std::string account_data;
BEGIN_SERIALIZE_OBJECT()
@@ -415,7 +415,7 @@ namespace tools
struct cache_file_data
{
- crypto::chacha8_iv iv;
+ crypto::chacha_iv iv;
std::string cache_data;
BEGIN_SERIALIZE_OBJECT()
@@ -436,6 +436,15 @@ namespace tools
typedef std::tuple<uint64_t, crypto::public_key, rct::key> get_outs_entry;
/*!
+ * \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
+ */
+ void generate(const std::string& wallet_, const epee::wipeable_string& password,
+ const std::string& multisig_data);
+
+ /*!
* \brief Generates a wallet or restores one.
* \param wallet_ Name of wallet file
* \param password Password of wallet file
@@ -610,6 +619,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 get_multisig_seed(std::string& seed, const epee::wipeable_string &passphrase = std::string(), bool raw = true) const;
// locked & unlocked balance of given or current subaddress account
uint64_t balance(uint32_t subaddr_index_major) const;
@@ -767,6 +777,9 @@ namespace tools
if(ver < 22)
return;
a & m_unconfirmed_payments;
+ if(ver < 23)
+ return;
+ a & m_account_tags;
}
/*!
@@ -863,6 +876,24 @@ namespace tools
void set_description(const std::string &description);
std::string get_description() const;
+ /*!
+ * \brief Get the list of registered account tags.
+ * \return first.Key=(tag's name), first.Value=(tag's label), second[i]=(i-th account's tag)
+ */
+ const std::pair<std::map<std::string, std::string>, std::vector<std::string>>& get_account_tags();
+ /*!
+ * \brief Set a tag to the given accounts.
+ * \param account_indices Indices of accounts.
+ * \param tag Tag's name. If empty, the accounts become untagged.
+ */
+ void set_account_tag(const std::set<uint32_t> account_indices, const std::string& tag);
+ /*!
+ * \brief Set the label of the given tag.
+ * \param tag Tag's name (which must be non-empty).
+ * \param label 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;
@@ -975,7 +1006,7 @@ namespace tools
void add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t amount_in, const std::vector<cryptonote::tx_destination_entry> &dests, const crypto::hash &payment_id, uint64_t change_amount, uint32_t subaddr_account, const std::set<uint32_t>& subaddr_indices);
void generate_genesis(cryptonote::block& b);
void check_genesis(const crypto::hash& genesis_hash) const; //throws
- bool generate_chacha8_key_from_secret_keys(crypto::chacha8_key &key) const;
+ 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 parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const;
@@ -1025,6 +1056,7 @@ namespace tools
std::unordered_map<crypto::hash, std::string> m_tx_notes;
std::unordered_map<std::string, std::string> m_attributes;
std::vector<tools::wallet2::address_book_row> m_address_book;
+ std::pair<std::map<std::string, std::string>, std::vector<std::string>> m_account_tags;
uint64_t m_upper_transaction_size_limit; //TODO: auto-calc this value or request from daemon, now use some fixed value
const std::vector<std::vector<tools::wallet2::multisig_info>> *m_multisig_rescan_info;
const std::vector<std::vector<rct::key>> *m_multisig_rescan_k;
@@ -1077,7 +1109,7 @@ namespace tools
std::unordered_map<crypto::public_key, std::map<uint64_t, crypto::key_image> > m_key_image_cache;
};
}
-BOOST_CLASS_VERSION(tools::wallet2, 22)
+BOOST_CLASS_VERSION(tools::wallet2, 23)
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)
diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h
index 234c22d85..023b53f28 100644
--- a/src/wallet/wallet_errors.h
+++ b/src/wallet/wallet_errors.h
@@ -60,6 +60,7 @@ namespace tools
// file_save_error
// invalid_password
// invalid_priority
+ // invalid_multisig_seed
// refresh_error *
// acc_outs_lookup_error
// block_parse_error
@@ -266,6 +267,16 @@ namespace tools
std::string to_string() const { return wallet_logic_error::to_string(); }
};
+ struct invalid_multisig_seed : public wallet_logic_error
+ {
+ explicit invalid_multisig_seed(std::string&& loc)
+ : wallet_logic_error(std::move(loc), "invalid multisig seed")
+ {
+ }
+
+ std::string to_string() const { return wallet_logic_error::to_string(); }
+ };
+
//----------------------------------------------------------------------------------------------------
struct invalid_pregenerated_random : public wallet_logic_error
{
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 0482b9dd6..fc2c43c04 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -89,6 +89,8 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
wallet_rpc_server::~wallet_rpc_server()
{
+ if (m_wallet)
+ delete m_wallet;
}
//------------------------------------------------------------------------------------------------------------------------------
void wallet_rpc_server::set_wallet(wallet2 *cr)
@@ -229,8 +231,9 @@ namespace tools
m_http_client.set_server(walvars->get_daemon_address(), walvars->get_daemon_login());
m_net_server.set_threads_prefix("RPC");
+ auto rng = [](size_t len, uint8_t *ptr) { return crypto::rand(len, ptr); };
return epee::http_server_impl_base<wallet_rpc_server, connection_context>::init(
- std::move(bind_port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login)
+ rng, std::move(bind_port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login)
);
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -356,13 +359,24 @@ namespace tools
if (!m_wallet) return not_open(er);
try
{
- res.addresses.resize(m_wallet->get_num_subaddresses(req.account_index));
+ res.addresses.clear();
+ std::vector<uint32_t> req_address_index;
+ if (req.address_index.empty())
+ {
+ for (uint32_t i = 0; i < m_wallet->get_num_subaddresses(req.account_index); ++i)
+ req_address_index.push_back(i);
+ }
+ else
+ {
+ req_address_index = req.address_index;
+ }
tools::wallet2::transfer_container transfers;
m_wallet->get_transfers(transfers);
- cryptonote::subaddress_index index = {req.account_index, 0};
- for (; index.minor < m_wallet->get_num_subaddresses(req.account_index); ++index.minor)
+ for (uint32_t i : req_address_index)
{
- auto& info = res.addresses[index.minor];
+ res.addresses.resize(res.addresses.size() + 1);
+ auto& info = res.addresses.back();
+ const cryptonote::subaddress_index index = {req.account_index, i};
info.address = m_wallet->get_subaddress_as_str(index);
info.label = m_wallet->get_subaddress_label(index);
info.address_index = index.minor;
@@ -418,14 +432,24 @@ namespace tools
res.total_balance = 0;
res.total_unlocked_balance = 0;
cryptonote::subaddress_index subaddr_index = {0,0};
+ const std::pair<std::map<std::string, std::string>, std::vector<std::string>> account_tags = m_wallet->get_account_tags();
+ if (!req.tag.empty() && account_tags.first.count(req.tag) == 0)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = (boost::format(tr("Tag %s is unregistered.")) % req.tag).str();
+ return false;
+ }
for (; subaddr_index.major < m_wallet->get_num_subaddress_accounts(); ++subaddr_index.major)
{
+ if (!req.tag.empty() && req.tag != account_tags.second[subaddr_index.major])
+ continue;
wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::subaddress_account_info info;
info.account_index = subaddr_index.major;
info.base_address = m_wallet->get_subaddress_as_str(subaddr_index);
info.balance = m_wallet->balance(subaddr_index.major);
info.unlocked_balance = m_wallet->unlocked_balance(subaddr_index.major);
info.label = m_wallet->get_subaddress_label(subaddr_index);
+ info.tag = account_tags.second[subaddr_index.major];
res.subaddress_accounts.push_back(info);
res.total_balance += info.balance;
res.total_unlocked_balance += info.unlocked_balance;
@@ -471,6 +495,66 @@ namespace tools
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ 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)
+ {
+ 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)
+ {
+ res.account_tags.resize(res.account_tags.size() + 1);
+ auto& info = res.account_tags.back();
+ info.tag = p.first;
+ info.label = p.second;
+ for (size_t i = 0; i < account_tags.second.size(); ++i)
+ {
+ if (account_tags.second[i] == info.tag)
+ info.accounts.push_back(i);
+ }
+ }
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ 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)
+ {
+ try
+ {
+ m_wallet->set_account_tag(req.accounts, req.tag);
+ }
+ 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_untag_accounts(const wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::response& res, epee::json_rpc::error& er)
+ {
+ try
+ {
+ m_wallet->set_account_tag(req.accounts, "");
+ }
+ 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_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)
+ {
+ try
+ {
+ m_wallet->set_account_tag_description(req.tag, req.description);
+ }
+ 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_getheight(const wallet_rpc::COMMAND_RPC_GET_HEIGHT::request& req, wallet_rpc::COMMAND_RPC_GET_HEIGHT::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
@@ -486,7 +570,7 @@ namespace tools
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
- bool wallet_rpc_server::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, epee::json_rpc::error& er)
+ bool wallet_rpc_server::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)
{
crypto::hash8 integrated_payment_id = crypto::null_hash8;
std::string extra_nonce;
@@ -541,6 +625,13 @@ namespace tools
}
}
+ if (at_least_one_destination && dsts.empty())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_ZERO_DESTINATION;
+ er.message = "No destinations for this transfer";
+ return false;
+ }
+
if (!payment_id.empty())
{
@@ -574,7 +665,98 @@ namespace tools
}
return true;
}
+ //------------------------------------------------------------------------------------------------------------------------------
+ static std::string ptx_to_string(const tools::wallet2::pending_tx &ptx)
+ {
+ std::ostringstream oss;
+ boost::archive::portable_binary_oarchive ar(oss);
+ try
+ {
+ ar << ptx;
+ }
+ catch (...)
+ {
+ return "";
+ }
+ return epee::string_tools::buff_to_hex_nodelimer(oss.str());
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ template<typename T> static bool is_error_value(const T &val) { return false; }
+ static bool is_error_value(const std::string &s) { return s.empty(); }
+ //------------------------------------------------------------------------------------------------------------------------------
+ template<typename T, typename V>
+ static bool fill(T &where, V s)
+ {
+ if (is_error_value(s)) return false;
+ where = std::move(s);
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ template<typename T, typename V>
+ static bool fill(std::list<T> &where, V s)
+ {
+ if (is_error_value(s)) return false;
+ where.emplace_back(std::move(s));
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ static uint64_t total_amount(const tools::wallet2::pending_tx &ptx)
+ {
+ uint64_t amount = 0;
+ for (const auto &dest: ptx.dests) amount += dest.amount;
+ return amount;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ 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,
+ 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)
+ {
+ if (get_tx_key)
+ {
+ std::string s = epee::string_tools::pod_to_hex(ptx.tx_key);
+ for (const crypto::secret_key& additional_tx_key : ptx.additional_tx_keys)
+ s += epee::string_tools::pod_to_hex(additional_tx_key);
+ fill(tx_key, s);
+ }
+ // Compute amount leaving wallet in tx. By convention dests does not include change outputs
+ fill(amount, total_amount(ptx));
+ fill(fee, ptx.fee);
+ }
+ if (m_wallet->multisig())
+ {
+ multisig_txset = epee::string_tools::buff_to_hex_nodelimer(m_wallet->save_multisig_tx(ptx_vector));
+ if (multisig_txset.empty())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "Failed to save multisig tx set after creation";
+ return false;
+ }
+ }
+ else
+ {
+ if (!do_not_relay)
+ m_wallet->commit_tx(ptx_vector);
+
+ // populate response with tx hashes
+ for (auto & ptx : ptx_vector)
+ {
+ bool r = fill(tx_hash, epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
+ r = r && (!get_tx_hex || fill(tx_blob, epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(ptx.tx))));
+ r = r && (!get_tx_metadata || fill(tx_metadata, ptx_to_string(ptx)));
+ if (!r)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "Failed to save tx info";
+ return false;
+ }
+ }
+ }
+ return true;
+ }
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er)
{
@@ -592,7 +774,7 @@ namespace tools
}
// validate the transfer requested and populate dsts & extra
- if (!validate_transfer(req.destinations, req.payment_id, dsts, extra, er))
+ if (!validate_transfer(req.destinations, req.payment_id, dsts, extra, true, er))
{
return false;
}
@@ -602,6 +784,13 @@ namespace tools
uint64_t mixin = m_wallet->adjust_mixin(req.mixin);
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon);
+ if (ptx_vector.empty())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_TX_NOT_POSSIBLE;
+ er.message = "No transaction created";
+ return false;
+ }
+
// reject proposed transactions if there are more than one. see on_transfer_split below.
if (ptx_vector.size() != 1)
{
@@ -610,55 +799,8 @@ namespace tools
return false;
}
- if (req.get_tx_key)
- {
- res.tx_key = epee::string_tools::pod_to_hex(ptx_vector.back().tx_key);
- for (const crypto::secret_key& additional_tx_key : ptx_vector.back().additional_tx_keys)
- res.tx_key += epee::string_tools::pod_to_hex(additional_tx_key);
- }
- res.fee = ptx_vector.back().fee;
-
- if (m_wallet->multisig())
- {
- res.multisig_txset = epee::string_tools::buff_to_hex_nodelimer(m_wallet->save_multisig_tx(ptx_vector));
- if (res.multisig_txset.empty())
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "Failed to save multisig tx set after creation";
- return false;
- }
- }
- else
- {
- if (!req.do_not_relay)
- m_wallet->commit_tx(ptx_vector);
-
- // populate response with tx hash
- res.tx_hash = epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx_vector.back().tx));
- if (req.get_tx_hex)
- {
- cryptonote::blobdata blob;
- tx_to_blob(ptx_vector.back().tx, blob);
- res.tx_blob = epee::string_tools::buff_to_hex_nodelimer(blob);
- }
- if (req.get_tx_metadata)
- {
- std::ostringstream oss;
- boost::archive::portable_binary_oarchive ar(oss);
- try
- {
- ar << ptx_vector.back();
- }
- catch (...)
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "Failed to save multisig tx set after creation";
- return false;
- }
- res.tx_metadata = epee::string_tools::buff_to_hex_nodelimer(oss.str());
- }
- }
- return true;
+ return fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.fee, res.multisig_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)
{
@@ -683,7 +825,7 @@ namespace tools
}
// validate the transfer requested and populate dsts & extra; RPC_TRANSFER::request and RPC_TRANSFER_SPLIT::request are identical types.
- if (!validate_transfer(req.destinations, req.payment_id, dsts, extra, er))
+ if (!validate_transfer(req.destinations, req.payment_id, dsts, extra, true, er))
{
return false;
}
@@ -691,82 +833,12 @@ namespace tools
try
{
uint64_t mixin = m_wallet->adjust_mixin(req.mixin);
- uint64_t ptx_amount;
- std::vector<wallet2::pending_tx> ptx_vector;
LOG_PRINT_L2("on_transfer_split calling create_transactions_2");
- ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon);
+ std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon);
LOG_PRINT_L2("on_transfer_split called create_transactions_2");
- // populate response with tx hashes
- for (const auto & ptx : ptx_vector)
- {
- if (req.get_tx_keys)
- {
- res.tx_key_list.push_back(epee::string_tools::pod_to_hex(ptx.tx_key));
- for (const crypto::secret_key& additional_tx_key : ptx.additional_tx_keys)
- res.tx_key_list.back() += epee::string_tools::pod_to_hex(additional_tx_key);
- }
- // Compute amount leaving wallet in tx. By convention dests does not include change outputs
- ptx_amount = 0;
- for(auto & dt: ptx.dests)
- ptx_amount += dt.amount;
- res.amount_list.push_back(ptx_amount);
-
- res.fee_list.push_back(ptx.fee);
- }
-
- if (m_wallet->multisig())
- {
- res.multisig_txset = epee::string_tools::buff_to_hex_nodelimer(m_wallet->save_multisig_tx(ptx_vector));
- if (res.multisig_txset.empty())
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "Failed to save multisig tx set after creation";
- return false;
- }
- }
-
- // populate response with tx hashes
- for (const auto & ptx : ptx_vector)
- {
- if (!req.do_not_relay)
- {
- LOG_PRINT_L2("on_transfer_split calling commit_tx");
- m_wallet->commit_tx(ptx_vector);
- LOG_PRINT_L2("on_transfer_split called commit_tx");
- }
-
- // populate response with tx hashes
- for (auto & ptx : ptx_vector)
- {
- res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
-
- if (req.get_tx_hex)
- {
- cryptonote::blobdata blob;
- tx_to_blob(ptx.tx, blob);
- res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob));
- }
- if (req.get_tx_metadata)
- {
- std::ostringstream oss;
- boost::archive::portable_binary_oarchive ar(oss);
- try
- {
- ar << ptx;
- }
- catch (...)
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "Failed to save multisig tx set after creation";
- return false;
- }
- res.tx_metadata_list.push_back(epee::string_tools::buff_to_hex_nodelimer(oss.str()));
- }
- }
- }
-
- return true;
+ 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,
+ 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)
{
@@ -790,69 +862,8 @@ namespace tools
{
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_unmixable_sweep_transactions(m_trusted_daemon);
- for (const auto & ptx : ptx_vector)
- {
- if (req.get_tx_keys)
- {
- res.tx_key_list.push_back(epee::string_tools::pod_to_hex(ptx.tx_key));
- }
- res.fee_list.push_back(ptx.fee);
- }
-
- if (m_wallet->multisig())
- {
- for (tools::wallet2::pending_tx &ptx: ptx_vector)
- {
- std::ostringstream oss;
- boost::archive::portable_binary_oarchive ar(oss);
- try
- {
- ar << ptx;
- }
- catch (...)
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "Failed to save multisig tx set after creation";
- return false;
- }
- res.multisig_txset.push_back(epee::string_tools::buff_to_hex_nodelimer(oss.str()));
- }
- }
- else
- {
- if (!req.do_not_relay)
- m_wallet->commit_tx(ptx_vector);
-
- // populate response with tx hashes
- for (auto & ptx : ptx_vector)
- {
- res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
- if (req.get_tx_hex)
- {
- cryptonote::blobdata blob;
- tx_to_blob(ptx.tx, blob);
- res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob));
- }
- if (req.get_tx_metadata)
- {
- std::ostringstream oss;
- boost::archive::portable_binary_oarchive ar(oss);
- try
- {
- ar << ptx;
- }
- catch (...)
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "Failed to save multisig tx set after creation";
- return false;
- }
- res.tx_metadata_list.push_back(epee::string_tools::buff_to_hex_nodelimer(oss.str()));
- }
- }
- }
-
- return true;
+ 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,
+ 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)
{
@@ -880,7 +891,7 @@ namespace tools
destination.push_back(wallet_rpc::transfer_destination());
destination.back().amount = 0;
destination.back().address = req.address;
- if (!validate_transfer(destination, req.payment_id, dsts, extra, er))
+ if (!validate_transfer(destination, req.payment_id, dsts, extra, true, er))
{
return false;
}
@@ -890,68 +901,8 @@ namespace tools
uint64_t mixin = m_wallet->adjust_mixin(req.mixin);
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, req.priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon);
- for (const auto & ptx : ptx_vector)
- {
- if (req.get_tx_keys)
- {
- res.tx_key_list.push_back(epee::string_tools::pod_to_hex(ptx.tx_key));
- }
- }
-
- if (m_wallet->multisig())
- {
- for (tools::wallet2::pending_tx &ptx: ptx_vector)
- {
- std::ostringstream oss;
- boost::archive::portable_binary_oarchive ar(oss);
- try
- {
- ar << ptx;
- }
- catch (...)
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "Failed to save multisig tx set after creation";
- return false;
- }
- res.multisig_txset.push_back(epee::string_tools::buff_to_hex_nodelimer(oss.str()));
- }
- }
- else
- {
- if (!req.do_not_relay)
- m_wallet->commit_tx(ptx_vector);
-
- // populate response with tx hashes
- for (auto & ptx : ptx_vector)
- {
- res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
- if (req.get_tx_hex)
- {
- cryptonote::blobdata blob;
- tx_to_blob(ptx.tx, blob);
- res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob));
- }
- if (req.get_tx_metadata)
- {
- std::ostringstream oss;
- boost::archive::portable_binary_oarchive ar(oss);
- try
- {
- ar << ptx;
- }
- catch (...)
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "Failed to save multisig tx set after creation";
- return false;
- }
- res.tx_metadata_list.push_back(epee::string_tools::buff_to_hex_nodelimer(oss.str()));
- }
- }
- }
-
- return true;
+ 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,
+ 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)
{
@@ -979,7 +930,7 @@ namespace tools
destination.push_back(wallet_rpc::transfer_destination());
destination.back().amount = 0;
destination.back().address = req.address;
- if (!validate_transfer(destination, req.payment_id, dsts, extra, er))
+ if (!validate_transfer(destination, req.payment_id, dsts, extra, true, er))
{
return false;
}
@@ -1017,63 +968,12 @@ namespace tools
return false;
}
- if (req.get_tx_key)
- {
- res.tx_key = epee::string_tools::pod_to_hex(ptx.tx_key);
- }
-
- if (m_wallet->multisig())
- {
- res.multisig_txset = epee::string_tools::buff_to_hex_nodelimer(m_wallet->save_multisig_tx(ptx_vector));
- if (res.multisig_txset.empty())
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "Failed to save multisig tx set after creation";
- return false;
- }
- }
- else
- {
- if (!req.do_not_relay)
- m_wallet->commit_tx(ptx_vector);
-
- // populate response with tx hashes
- res.tx_hash = epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx));
- if (req.get_tx_hex)
- {
- cryptonote::blobdata blob;
- tx_to_blob(ptx.tx, blob);
- res.tx_blob = epee::string_tools::buff_to_hex_nodelimer(blob);
- }
- if (req.get_tx_metadata)
- {
- std::ostringstream oss;
- boost::archive::portable_binary_oarchive ar(oss);
- try
- {
- ar << ptx;
- }
- catch (...)
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "Failed to save multisig tx set after creation";
- return false;
- }
- res.tx_metadata = epee::string_tools::buff_to_hex_nodelimer(oss.str());
- }
- }
- return true;
- }
- catch (const tools::error::daemon_busy& e)
- {
- er.code = WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY;
- er.message = e.what();
- 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,
+ res.tx_hash, req.get_tx_hex, res.tx_blob, req.get_tx_metadata, res.tx_metadata, er);
}
catch (const std::exception& e)
{
- er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR;
- er.message = e.what();
+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR);
return false;
}
catch (...)
@@ -1254,6 +1154,7 @@ namespace tools
rpc_payment.block_height = payment.m_block_height;
rpc_payment.unlock_time = payment.m_unlock_time;
rpc_payment.subaddr_index = payment.m_subaddr_index;
+ rpc_payment.address = m_wallet->get_subaddress_as_str(payment.m_subaddr_index);
res.payments.push_back(rpc_payment);
}
@@ -1280,6 +1181,7 @@ namespace tools
rpc_payment.block_height = payment.second.m_block_height;
rpc_payment.unlock_time = payment.second.m_unlock_time;
rpc_payment.subaddr_index = payment.second.m_subaddr_index;
+ rpc_payment.address = m_wallet->get_subaddress_as_str(payment.second.m_subaddr_index);
res.payments.push_back(std::move(rpc_payment));
}
@@ -1330,6 +1232,7 @@ namespace tools
rpc_payment.block_height = payment.m_block_height;
rpc_payment.unlock_time = payment.m_unlock_time;
rpc_payment.subaddr_index = payment.m_subaddr_index;
+ rpc_payment.address = m_wallet->get_subaddress_as_str(payment.m_subaddr_index);
res.payments.push_back(std::move(rpc_payment));
}
}
diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h
index 79f589623..88f2a85a4 100644
--- a/src/wallet/wallet_rpc_server.h
+++ b/src/wallet/wallet_rpc_server.h
@@ -74,6 +74,10 @@ namespace tools
MAP_JON_RPC_WE("get_accounts", on_get_accounts, wallet_rpc::COMMAND_RPC_GET_ACCOUNTS)
MAP_JON_RPC_WE("create_account", on_create_account, wallet_rpc::COMMAND_RPC_CREATE_ACCOUNT)
MAP_JON_RPC_WE("label_account", on_label_account, wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT)
+ MAP_JON_RPC_WE("get_account_tags", on_get_account_tags, wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS)
+ MAP_JON_RPC_WE("tag_accounts", on_tag_accounts, wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS)
+ MAP_JON_RPC_WE("untag_accounts", on_untag_accounts, wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS)
+ MAP_JON_RPC_WE("set_account_tag_description", on_set_account_tag_description, wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION)
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)
@@ -136,8 +140,12 @@ namespace tools
bool on_get_accounts(const wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::response& res, epee::json_rpc::error& er);
bool on_create_account(const wallet_rpc::COMMAND_RPC_CREATE_ACCOUNT::request& req, wallet_rpc::COMMAND_RPC_CREATE_ACCOUNT::response& res, epee::json_rpc::error& er);
bool on_label_account(const wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT::request& req, wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT::response& res, epee::json_rpc::error& er);
+ bool 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);
+ bool 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);
+ bool 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);
+ bool 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);
bool on_getheight(const wallet_rpc::COMMAND_RPC_GET_HEIGHT::request& req, wallet_rpc::COMMAND_RPC_GET_HEIGHT::response& res, epee::json_rpc::error& er);
- 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, epee::json_rpc::error& er);
+ 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_sweep_dust(const wallet_rpc::COMMAND_RPC_SWEEP_DUST::request& req, wallet_rpc::COMMAND_RPC_SWEEP_DUST::response& res, epee::json_rpc::error& er);
@@ -199,6 +207,11 @@ namespace tools
bool not_open(epee::json_rpc::error& er);
void handle_rpc_exception(const std::exception_ptr& e, epee::json_rpc::error& er, int default_error_code);
+ 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,
+ 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;
std::string m_wallet_dir;
tools::private_file rpc_login_file;
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index 57cc01e27..d797af5c1 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -95,8 +95,10 @@ namespace wallet_rpc
struct request
{
uint32_t account_index;
+ std::vector<uint32_t> address_index;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(account_index)
+ KV_SERIALIZE(address_index)
END_KV_SERIALIZE_MAP()
};
@@ -176,7 +178,10 @@ namespace wallet_rpc
{
struct request
{
+ std::string tag; // all accounts if empty, otherwise those accounts with this tag
+
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(tag)
END_KV_SERIALIZE_MAP()
};
@@ -187,6 +192,7 @@ namespace wallet_rpc
uint64_t balance;
uint64_t unlocked_balance;
std::string label;
+ std::string tag;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(account_index)
@@ -194,6 +200,7 @@ namespace wallet_rpc
KV_SERIALIZE(balance)
KV_SERIALIZE(unlocked_balance)
KV_SERIALIZE(label)
+ KV_SERIALIZE(tag)
END_KV_SERIALIZE_MAP()
};
@@ -252,6 +259,95 @@ namespace wallet_rpc
};
};
+ struct COMMAND_RPC_GET_ACCOUNT_TAGS
+ {
+ struct request
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct account_tag_info
+ {
+ std::string tag;
+ std::string label;
+ std::vector<uint32_t> accounts;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(tag);
+ KV_SERIALIZE(label);
+ KV_SERIALIZE(accounts);
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ std::vector<account_tag_info> account_tags;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(account_tags)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ struct COMMAND_RPC_TAG_ACCOUNTS
+ {
+ struct request
+ {
+ std::string tag;
+ std::set<uint32_t> accounts;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(tag)
+ KV_SERIALIZE(accounts)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ struct COMMAND_RPC_UNTAG_ACCOUNTS
+ {
+ struct request
+ {
+ std::set<uint32_t> accounts;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(accounts)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ struct COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION
+ {
+ struct request
+ {
+ std::string tag;
+ std::string description;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(tag)
+ KV_SERIALIZE(description)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
struct COMMAND_RPC_GET_HEIGHT
{
struct request
@@ -315,6 +411,7 @@ namespace wallet_rpc
std::string tx_hash;
std::string tx_key;
std::list<std::string> amount_keys;
+ uint64_t amount;
uint64_t fee;
std::string tx_blob;
std::string tx_metadata;
@@ -324,6 +421,7 @@ namespace wallet_rpc
KV_SERIALIZE(tx_hash)
KV_SERIALIZE(tx_key)
KV_SERIALIZE(amount_keys)
+ KV_SERIALIZE(amount)
KV_SERIALIZE(fee)
KV_SERIALIZE(tx_blob)
KV_SERIALIZE(tx_metadata)
@@ -424,14 +522,16 @@ namespace wallet_rpc
{
std::list<std::string> tx_hash_list;
std::list<std::string> tx_key_list;
+ std::list<uint64_t> amount_list;
std::list<uint64_t> fee_list;
std::list<std::string> tx_blob_list;
std::list<std::string> tx_metadata_list;
- std::list<std::string> multisig_txset;
+ std::string multisig_txset;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash_list)
KV_SERIALIZE(tx_key_list)
+ KV_SERIALIZE(amount_list)
KV_SERIALIZE(fee_list)
KV_SERIALIZE(tx_blob_list)
KV_SERIALIZE(tx_metadata_list)
@@ -486,14 +586,16 @@ namespace wallet_rpc
{
std::list<std::string> tx_hash_list;
std::list<std::string> tx_key_list;
+ std::list<uint64_t> amount_list;
std::list<uint64_t> fee_list;
std::list<std::string> tx_blob_list;
std::list<std::string> tx_metadata_list;
- std::list<std::string> multisig_txset;
+ std::string multisig_txset;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash_list)
KV_SERIALIZE(tx_key_list)
+ KV_SERIALIZE(amount_list)
KV_SERIALIZE(fee_list)
KV_SERIALIZE(tx_blob_list)
KV_SERIALIZE(tx_metadata_list)
@@ -535,6 +637,7 @@ namespace wallet_rpc
{
std::string tx_hash;
std::string tx_key;
+ uint64_t amount;
uint64_t fee;
std::string tx_blob;
std::string tx_metadata;
@@ -543,6 +646,7 @@ namespace wallet_rpc
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash)
KV_SERIALIZE(tx_key)
+ KV_SERIALIZE(amount)
KV_SERIALIZE(fee)
KV_SERIALIZE(tx_blob)
KV_SERIALIZE(tx_metadata)
@@ -601,6 +705,7 @@ namespace wallet_rpc
uint64_t block_height;
uint64_t unlock_time;
cryptonote::subaddress_index subaddr_index;
+ std::string address;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(payment_id)
@@ -609,6 +714,7 @@ namespace wallet_rpc
KV_SERIALIZE(block_height)
KV_SERIALIZE(unlock_time)
KV_SERIALIZE(subaddr_index)
+ KV_SERIALIZE(address)
END_KV_SERIALIZE_MAP()
};