aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/blockchain_db/blockchain_db.h2
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp38
-rw-r--r--src/blockchain_utilities/CMakeLists.txt22
-rw-r--r--src/blockchain_utilities/blockchain_blackball.cpp57
-rw-r--r--src/blockchain_utilities/blockchain_import.cpp8
-rw-r--r--src/blocks/CMakeLists.txt35
-rw-r--r--src/blocks/blockexports.c87
-rw-r--r--src/blocks/blocks.cpp31
-rw-r--r--src/blocks/blocks.dat0
-rw-r--r--src/blocks/blocks.h14
-rw-r--r--src/common/expect.h4
-rw-r--r--src/common/password.cpp47
-rw-r--r--src/common/perf_timer.cpp8
-rw-r--r--src/common/perf_timer.h18
-rw-r--r--src/common/spawn.cpp3
-rw-r--r--src/common/threadpool.cpp10
-rw-r--r--src/common/util.cpp75
-rw-r--r--src/common/util.h5
-rw-r--r--src/cryptonote_basic/account.cpp1
-rw-r--r--src/cryptonote_basic/cryptonote_basic.h1
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.cpp95
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.h3
-rw-r--r--src/cryptonote_basic/miner.cpp4
-rw-r--r--src/cryptonote_config.h1
-rw-r--r--src/cryptonote_core/CMakeLists.txt7
-rw-r--r--src/cryptonote_core/blockchain.cpp45
-rw-r--r--src/cryptonote_core/blockchain.h23
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp58
-rw-r--r--src/cryptonote_core/cryptonote_core.h11
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.cpp6
-rw-r--r--src/cryptonote_core/tx_pool.cpp51
-rw-r--r--src/cryptonote_core/tx_pool.h13
-rw-r--r--src/daemon/CMakeLists.txt21
-rw-r--r--src/daemon/core.h8
-rw-r--r--src/daemon/daemon.cpp14
-rw-r--r--src/daemon/rpc_command_executor.cpp16
-rw-r--r--src/daemonizer/windows_daemonizer.inl1
-rw-r--r--src/debug_utilities/object_sizes.cpp1
-rw-r--r--src/device/CMakeLists.txt7
-rw-r--r--src/device/device.cpp25
-rw-r--r--src/device/device.hpp8
-rw-r--r--src/device/device_default.cpp2
-rw-r--r--src/device/device_io_hid.cpp71
-rw-r--r--src/device/device_io_hid.hpp20
-rw-r--r--src/device/device_ledger.cpp15
-rw-r--r--src/gen_multisig/gen_multisig.cpp17
-rw-r--r--src/multisig/multisig.cpp44
-rw-r--r--src/multisig/multisig.h24
-rw-r--r--src/p2p/net_node.cpp1
-rw-r--r--src/p2p/net_node.inl22
-rw-r--r--src/ringct/rctSigs.cpp49
-rw-r--r--src/rpc/core_rpc_server.cpp49
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h3
-rw-r--r--src/rpc/daemon_messages.h2
-rw-r--r--src/rpc/message.h2
-rw-r--r--src/serialization/serialization.h23
-rw-r--r--src/simplewallet/simplewallet.cpp251
-rw-r--r--src/simplewallet/simplewallet.h1
-rw-r--r--src/version.cpp.in2
-rw-r--r--src/wallet/api/wallet.cpp69
-rw-r--r--src/wallet/api/wallet.h4
-rw-r--r--src/wallet/api/wallet2_api.h17
-rw-r--r--src/wallet/ringdb.cpp4
-rw-r--r--src/wallet/wallet2.cpp476
-rw-r--r--src/wallet/wallet2.h17
-rw-r--r--src/wallet/wallet_errors.h9
-rw-r--r--src/wallet/wallet_rpc_server.cpp227
-rw-r--r--src/wallet/wallet_rpc_server.h5
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h92
-rw-r--r--src/wallet/wallet_rpc_server_error_codes.h1
70 files changed, 1740 insertions, 663 deletions
diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h
index 396ae7544..71c46d76b 100644
--- a/src/blockchain_db/blockchain_db.h
+++ b/src/blockchain_db/blockchain_db.h
@@ -544,7 +544,7 @@ public:
/**
* @brief An empty constructor.
*/
- BlockchainDB(): m_open(false) { }
+ BlockchainDB(): m_hardfork(NULL), m_open(false) { }
/**
* @brief An empty destructor.
diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp
index 824598f8c..84a083c26 100644
--- a/src/blockchain_db/lmdb/db_lmdb.cpp
+++ b/src/blockchain_db/lmdb/db_lmdb.cpp
@@ -649,7 +649,7 @@ uint64_t BlockchainLMDB::get_estimated_batch_size(uint64_t batch_num_blocks, uin
{
MDB_txn *rtxn;
mdb_txn_cursors *rcurs;
- block_rtxn_start(&rtxn, &rcurs);
+ bool my_rtxn = block_rtxn_start(&rtxn, &rcurs);
for (uint64_t block_num = block_start; block_num <= block_stop; ++block_num)
{
// we have access to block weight, which will be greater or equal to block size,
@@ -661,7 +661,7 @@ uint64_t BlockchainLMDB::get_estimated_batch_size(uint64_t batch_num_blocks, uin
// some blocks were to be skipped for being outliers.
++num_blocks_used;
}
- block_rtxn_stop();
+ if (my_rtxn) block_rtxn_stop();
avg_block_size = total_block_size / num_blocks_used;
MDEBUG("average block size across recent " << num_blocks_used << " blocks: " << avg_block_size);
}
@@ -1208,7 +1208,7 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
if (is_hdd_result)
{
if (is_hdd_result.value())
- MCLOG_RED(el::Level::Warning, "global", "The blockchain is on a rotating drive: this will be very slow, use a SSD if possible");
+ MCLOG_RED(el::Level::Warning, "global", "The blockchain is on a rotating drive: this will be very slow, use an SSD if possible");
}
m_folder = filename;
@@ -1980,22 +1980,36 @@ std::vector<uint64_t> BlockchainLMDB::get_block_cumulative_rct_outputs(const std
MDB_val v;
uint64_t prev_height = heights[0];
+ uint64_t range_begin = 0, range_end = 0;
for (uint64_t height: heights)
{
- if (height == prev_height + 1)
+ if (height >= range_begin && height < range_end)
{
- MDB_val k2;
- result = mdb_cursor_get(m_cur_block_info, &k2, &v, MDB_NEXT);
+ // nohting to do
}
else
{
- v.mv_size = sizeof(uint64_t);
- v.mv_data = (void*)&height;
- result = mdb_cursor_get(m_cur_block_info, (MDB_val *)&zerokval, &v, MDB_GET_BOTH);
+ if (height == prev_height + 1)
+ {
+ MDB_val k2;
+ result = mdb_cursor_get(m_cur_block_info, &k2, &v, MDB_NEXT_MULTIPLE);
+ range_begin = ((const mdb_block_info*)v.mv_data)->bi_height;
+ range_end = range_begin + v.mv_size / sizeof(mdb_block_info); // whole records please
+ if (height < range_begin || height >= range_end)
+ throw0(DB_ERROR(("Height " + std::to_string(height) + " not included in multuple record range: " + std::to_string(range_begin) + "-" + std::to_string(range_end)).c_str()));
+ }
+ else
+ {
+ v.mv_size = sizeof(uint64_t);
+ v.mv_data = (void*)&height;
+ result = mdb_cursor_get(m_cur_block_info, (MDB_val *)&zerokval, &v, MDB_GET_BOTH);
+ range_begin = height;
+ range_end = range_begin + 1;
+ }
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Error attempting to retrieve rct distribution from the db: ", result).c_str()));
}
- if (result)
- throw0(DB_ERROR(lmdb_error("Error attempting to retrieve rct distribution from the db: ", result).c_str()));
- const mdb_block_info *bi = (const mdb_block_info *)v.mv_data;
+ const mdb_block_info *bi = ((const mdb_block_info *)v.mv_data) + (height - range_begin);
res.push_back(bi->bi_cum_rct);
prev_height = height;
}
diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt
index ecd7b754c..6e6e4c6f1 100644
--- a/src/blockchain_utilities/CMakeLists.txt
+++ b/src/blockchain_utilities/CMakeLists.txt
@@ -26,20 +26,6 @@
# 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.
-set(blocksdat "")
-if(PER_BLOCK_CHECKPOINT)
- if(APPLE AND DEPENDS)
- add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} --target=x86_64-apple-darwin11 -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.*)
- elseif(APPLE AND NOT DEPENDS)
- 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.*)
- elseif(LINUX_32)
- 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} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat)
- 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} ${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()
-
set(blockchain_import_sources
blockchain_import.cpp
bootstrap_file.cpp
@@ -119,8 +105,7 @@ monero_private_headers(blockchain_depth
monero_add_executable(blockchain_import
${blockchain_import_sources}
- ${blockchain_import_private_headers}
- ${blocksdat})
+ ${blockchain_import_private_headers})
target_link_libraries(blockchain_import
PRIVATE
@@ -132,7 +117,8 @@ target_link_libraries(blockchain_import
${Boost_SYSTEM_LIBRARY}
${Boost_THREAD_LIBRARY}
${CMAKE_THREAD_LIBS_INIT}
- ${EXTRA_LIBRARIES})
+ ${EXTRA_LIBRARIES}
+ ${Blocks})
if(ARCH_WIDTH)
target_compile_definitions(blockchain_import
@@ -184,7 +170,7 @@ target_link_libraries(blockchain_blackball
set_property(TARGET blockchain_blackball
PROPERTY
- OUTPUT_NAME "monero-blockchain-blackball")
+ OUTPUT_NAME "monero-blockchain-mark-spent-outputs")
install(TARGETS blockchain_blackball DESTINATION bin)
diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp
index 5eb2acc79..d2ce5cf76 100644
--- a/src/blockchain_utilities/blockchain_blackball.cpp
+++ b/src/blockchain_utilities/blockchain_blackball.cpp
@@ -226,7 +226,7 @@ static void init(std::string cache_filename)
bool tx_active = false;
int dbr;
- MINFO("Creating blackball cache in " << cache_filename);
+ MINFO("Creating spent output cache in " << cache_filename);
tools::create_directories_if_necessary(cache_filename);
@@ -536,12 +536,15 @@ static uint64_t get_num_spent_outputs()
return count;
}
-static void add_spent_output(MDB_cursor *cur, const output_data &od)
+static bool add_spent_output(MDB_cursor *cur, const output_data &od)
{
MDB_val k = {sizeof(od.amount), (void*)&od.amount};
MDB_val v = {sizeof(od.offset), (void*)&od.offset};
- int dbr = mdb_cursor_put(cur, &k, &v, 0);
- CHECK_AND_ASSERT_THROW_MES(!dbr || dbr == MDB_KEYEXIST, "Failed to add spent output: " + std::string(mdb_strerror(dbr)));
+ int dbr = mdb_cursor_put(cur, &k, &v, MDB_NODUPDATA);
+ if (dbr == MDB_KEYEXIST)
+ return false;
+ CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to add spent output: " + std::string(mdb_strerror(dbr)));
+ return true;
}
static bool is_output_spent(MDB_cursor *cur, const output_data &od)
@@ -1016,7 +1019,7 @@ int main(int argc, char* argv[])
po::options_description desc_cmd_only("Command line options");
po::options_description desc_cmd_sett("Command line options and settings options");
const command_line::arg_descriptor<std::string> arg_blackball_db_dir = {
- "blackball-db-dir", "Specify blackball database directory",
+ "spent-output-db-dir", "Specify spent output database directory",
get_default_db_path(),
};
const command_line::arg_descriptor<std::string> arg_log_level = {"log-level", "0-4 or categories", ""};
@@ -1073,7 +1076,7 @@ int main(int argc, char* argv[])
return 1;
}
- mlog_configure(mlog_get_default_log_path("monero-blockchain-blackball.log"), true);
+ mlog_configure(mlog_get_default_log_path("monero-blockchain-find-spent-outputs.log"), true);
if (!command_line::is_arg_defaulted(vm, arg_log_level))
mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str());
else
@@ -1111,10 +1114,10 @@ int main(int argc, char* argv[])
return 1;
}
- const std::string cache_dir = (output_file_path / "blackball-cache").string();
+ const std::string cache_dir = (output_file_path / "spent-outputs-cache").string();
init(cache_dir);
- LOG_PRINT_L0("Scanning for blackballable outputs...");
+ LOG_PRINT_L0("Scanning for spent outputs...");
size_t done = 0;
@@ -1153,8 +1156,8 @@ int main(int argc, char* argv[])
if (!is_output_spent(cur, output_data(output.first, output.second)))
{
blackballs.push_back(output);
- add_spent_output(cur, output_data(output.first, output.second));
- inc_stat(txn, output.first ? "pre-rct-extra" : "rct-ring-extra");
+ if (add_spent_output(cur, output_data(output.first, output.second)))
+ inc_stat(txn, output.first ? "pre-rct-extra" : "rct-ring-extra");
}
}
if (!blackballs.empty())
@@ -1212,12 +1215,12 @@ int main(int argc, char* argv[])
const std::pair<uint64_t, uint64_t> output = std::make_pair(txin.amount, absolute[0]);
if (opt_verbose)
{
- MINFO("Blackballing output " << output.first << "/" << output.second << ", due to being used in a 1-ring");
+ MINFO("Marking output " << output.first << "/" << output.second << " as spent, due to being used in a 1-ring");
std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush;
}
blackballs.push_back(output);
- add_spent_output(cur, output_data(txin.amount, absolute[0]));
- inc_stat(txn, txin.amount ? "pre-rct-ring-size-1" : "rct-ring-size-1");
+ if (add_spent_output(cur, output_data(txin.amount, absolute[0])))
+ inc_stat(txn, txin.amount ? "pre-rct-ring-size-1" : "rct-ring-size-1");
}
else if (n == 0 && instances == new_ring.size())
{
@@ -1226,12 +1229,12 @@ int main(int argc, char* argv[])
const std::pair<uint64_t, uint64_t> output = std::make_pair(txin.amount, absolute[o]);
if (opt_verbose)
{
- MINFO("Blackballing output " << output.first << "/" << output.second << ", due to being used in " << new_ring.size() << " identical " << new_ring.size() << "-rings");
+ MINFO("Marking output " << output.first << "/" << output.second << " as spent, due to being used in " << new_ring.size() << " identical " << new_ring.size() << "-rings");
std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush;
}
blackballs.push_back(output);
- add_spent_output(cur, output_data(txin.amount, absolute[o]));
- inc_stat(txn, txin.amount ? "pre-rct-duplicate-rings" : "rct-duplicate-rings");
+ if (add_spent_output(cur, output_data(txin.amount, absolute[o])))
+ inc_stat(txn, txin.amount ? "pre-rct-duplicate-rings" : "rct-duplicate-rings");
}
}
else if (n == 0 && opt_check_subsets && get_ring_subset_instances(txn, txin.amount, new_ring) >= new_ring.size())
@@ -1241,12 +1244,12 @@ int main(int argc, char* argv[])
const std::pair<uint64_t, uint64_t> output = std::make_pair(txin.amount, absolute[o]);
if (opt_verbose)
{
- MINFO("Blackballing output " << output.first << "/" << output.second << ", due to being used in " << new_ring.size() << " subsets of " << new_ring.size() << "-rings");
+ MINFO("Marking output " << output.first << "/" << output.second << " as spent, due to being used in " << new_ring.size() << " subsets of " << new_ring.size() << "-rings");
std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush;
}
blackballs.push_back(output);
- add_spent_output(cur, output_data(txin.amount, absolute[o]));
- inc_stat(txn, txin.amount ? "pre-rct-subset-rings" : "rct-subset-rings");
+ if (add_spent_output(cur, output_data(txin.amount, absolute[o])))
+ inc_stat(txn, txin.amount ? "pre-rct-subset-rings" : "rct-subset-rings");
}
}
else if (n > 0 && get_relative_ring(txn, txin.k_image, relative_ring))
@@ -1277,12 +1280,12 @@ int main(int argc, char* argv[])
const std::pair<uint64_t, uint64_t> output = std::make_pair(txin.amount, common[0]);
if (opt_verbose)
{
- MINFO("Blackballing output " << output.first << "/" << output.second << ", due to being used in rings with a single common element");
+ MINFO("Marking output " << output.first << "/" << output.second << " as spent, due to being used in rings with a single common element");
std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush;
}
blackballs.push_back(output);
- add_spent_output(cur, output_data(txin.amount, common[0]));
- inc_stat(txn, txin.amount ? "pre-rct-key-image-attack" : "rct-key-image-attack");
+ if (add_spent_output(cur, output_data(txin.amount, common[0])))
+ inc_stat(txn, txin.amount ? "pre-rct-key-image-attack" : "rct-key-image-attack");
}
else
{
@@ -1389,13 +1392,13 @@ int main(int argc, char* argv[])
const std::pair<uint64_t, uint64_t> output = std::make_pair(od.amount, last_unknown);
if (opt_verbose)
{
- MINFO("Blackballing output " << output.first << "/" << output.second << ", due to being used in a " <<
+ MINFO("Marking output " << output.first << "/" << output.second << " as spent, due to being used in a " <<
absolute.size() << "-ring where all other outputs are known to be spent");
}
blackballs.push_back(output);
- add_spent_output(cur, output_data(od.amount, last_unknown));
+ if (add_spent_output(cur, output_data(od.amount, last_unknown)))
+ inc_stat(txn, od.amount ? "pre-rct-chain-reaction" : "rct-chain-reaction");
work_spent.push_back(output_data(od.amount, last_unknown));
- inc_stat(txn, od.amount ? "pre-rct-chain-reaction" : "rct-chain-reaction");
}
}
@@ -1417,7 +1420,7 @@ int main(int argc, char* argv[])
skip_secondary_passes:
uint64_t diff = get_num_spent_outputs() - start_blackballed_outputs;
- LOG_PRINT_L0(std::to_string(diff) << " new outputs blackballed, " << get_num_spent_outputs() << " total outputs blackballed");
+ LOG_PRINT_L0(std::to_string(diff) << " new outputs marked as spent, " << get_num_spent_outputs() << " total outputs marked as spent");
MDB_txn *txn;
dbr = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn);
@@ -1457,7 +1460,7 @@ skip_secondary_passes:
mdb_txn_abort(txn);
}
- LOG_PRINT_L0("Blockchain blackball data exported OK");
+ LOG_PRINT_L0("Blockchain spent output data exported OK");
close_db(env0, txn0, cur0, dbi0);
close();
return 0;
diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp
index 9ec768d26..7f92ecd87 100644
--- a/src/blockchain_utilities/blockchain_import.cpp
+++ b/src/blockchain_utilities/blockchain_import.cpp
@@ -37,6 +37,7 @@
#include "misc_log_ex.h"
#include "bootstrap_file.h"
#include "bootstrap_serialization.h"
+#include "blocks/blocks.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "serialization/binary_utils.h" // dump_binary(), parse_binary()
#include "serialization/json_utils.h" // dump_json()
@@ -758,7 +759,12 @@ int main(int argc, char* argv[])
{
core.disable_dns_checkpoints(true);
- if (!core.init(vm, NULL))
+#if defined(PER_BLOCK_CHECKPOINT)
+ const GetCheckpointsCallback& get_checkpoints = blocks::GetCheckpointsData;
+#else
+ const GetCheckpointsCallback& get_checkpoints = nullptr;
+#endif
+ if (!core.init(vm, nullptr, nullptr, get_checkpoints))
{
std::cerr << "Failed to initialize core" << ENDL;
return 1;
diff --git a/src/blocks/CMakeLists.txt b/src/blocks/CMakeLists.txt
index ebb5408cc..30d85adbf 100644
--- a/src/blocks/CMakeLists.txt
+++ b/src/blocks/CMakeLists.txt
@@ -26,20 +26,23 @@
# 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.
-if(APPLE)
- add_library(blocks STATIC blockexports.c)
- set_target_properties(blocks PROPERTIES LINKER_LANGUAGE C)
-else()
- if(LINUX_32)
- add_custom_command(OUTPUT blocks.o MAIN_DEPENDENCY blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -m elf_i386 ${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} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/testnet_blocks.o testnet_blocks.dat)
- add_custom_command(OUTPUT stagenet_blocks.o MAIN_DEPENDENCY stagenet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/stagenet_blocks.o stagenet_blocks.dat)
- else()
- 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_custom_command(OUTPUT stagenet_blocks.o MAIN_DEPENDENCY stagenet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/stagenet_blocks.o stagenet_blocks.dat)
- endif()
- add_library(blocks STATIC blocks.o testnet_blocks.o stagenet_blocks.o blockexports.c)
- set_target_properties(blocks PROPERTIES LINKER_LANGUAGE C)
-endif()
+set(GENERATED_SOURCES "")
+foreach(BLOB_NAME checkpoints testnet_blocks stagenet_blocks)
+ set(OUTPUT_C_SOURCE "generated_${BLOB_NAME}.c")
+ list(APPEND GENERATED_SOURCES ${OUTPUT_C_SOURCE})
+ set(INPUT_DAT_FILE "${BLOB_NAME}.dat")
+ add_custom_command(
+ OUTPUT ${OUTPUT_C_SOURCE}
+ MAIN_DEPENDENCY ${INPUT_DAT_FILE}
+ COMMAND
+ cd ${CMAKE_CURRENT_BINARY_DIR} &&
+ echo "'#include\t<stddef.h>'" > ${OUTPUT_C_SOURCE} &&
+ echo "'const\tunsigned\tchar\t${BLOB_NAME}[]={'" >> ${OUTPUT_C_SOURCE} &&
+ od -v -An -tu1 ${CMAKE_CURRENT_SOURCE_DIR}/${INPUT_DAT_FILE} | sed -e "'s/[0-9]\\{1,\\}/&,/g'" -e "'$$s/.$$//'" >> ${OUTPUT_C_SOURCE} &&
+ echo "'};'" >> ${OUTPUT_C_SOURCE} &&
+ echo "'const\tsize_t\t${BLOB_NAME}_len\t=\tsizeof(${BLOB_NAME});'" >> ${OUTPUT_C_SOURCE}
+ )
+endforeach()
+
+add_library(blocks STATIC blocks.cpp ${GENERATED_SOURCES})
diff --git a/src/blocks/blockexports.c b/src/blocks/blockexports.c
deleted file mode 100644
index 0154b0413..000000000
--- a/src/blocks/blockexports.c
+++ /dev/null
@@ -1,87 +0,0 @@
-#include <stddef.h>
-
-#if defined(__APPLE__)
-#include <mach-o/getsect.h>
-#ifdef BUILD_SHARED_LIBS
-#if !defined(__LP64__)
-const struct mach_header _mh_execute_header;
-#else
-const struct mach_header_64 _mh_execute_header;
-#endif
-#else
-#if !defined(__LP64__)
-extern const struct mach_header _mh_execute_header;
-#else
-extern const struct mach_header_64 _mh_execute_header;
-#endif
-#endif
-
-const unsigned char *get_blocks_dat_start(int testnet, int stagenet)
-{
- size_t size;
- if (testnet)
- return getsectiondata(&_mh_execute_header, "__DATA", "__testnet_blocks_dat", &size);
- else if (stagenet)
- return getsectiondata(&_mh_execute_header, "__DATA", "__stagenet_blocks_dat", &size);
- else
- return getsectiondata(&_mh_execute_header, "__DATA", "__blocks_dat", &size);
-}
-
-size_t get_blocks_dat_size(int testnet, int stagenet)
-{
- size_t size;
- if (testnet)
- getsectiondata(&_mh_execute_header, "__DATA", "__testnet_blocks_dat", &size);
- else if (stagenet)
- getsectiondata(&_mh_execute_header, "__DATA", "__stagenet_blocks_dat", &size);
- else
- getsectiondata(&_mh_execute_header, "__DATA", "__blocks_dat", &size);
- return size;
-}
-
-#else
-
-#if defined(_WIN32) && !defined(_WIN64)
-#define _binary_blocks_start binary_blocks_dat_start
-#define _binary_blocks_end binary_blocks_dat_end
-#define _binary_testnet_blocks_start binary_testnet_blocks_dat_start
-#define _binary_testnet_blocks_end binary_testnet_blocks_dat_end
-#define _binary_stagenet_blocks_start binary_stagenet_blocks_dat_start
-#define _binary_stagenet_blocks_end binary_stagenet_blocks_dat_end
-#else
-#define _binary_blocks_start _binary_blocks_dat_start
-#define _binary_blocks_end _binary_blocks_dat_end
-#define _binary_testnet_blocks_start _binary_testnet_blocks_dat_start
-#define _binary_testnet_blocks_end _binary_testnet_blocks_dat_end
-#define _binary_stagenet_blocks_start _binary_stagenet_blocks_dat_start
-#define _binary_stagenet_blocks_end _binary_stagenet_blocks_dat_end
-#endif
-
-extern const unsigned char _binary_blocks_start[];
-extern const unsigned char _binary_blocks_end[];
-extern const unsigned char _binary_testnet_blocks_start[];
-extern const unsigned char _binary_testnet_blocks_end[];
-extern const unsigned char _binary_stagenet_blocks_start[];
-extern const unsigned char _binary_stagenet_blocks_end[];
-
-const unsigned char *get_blocks_dat_start(int testnet, int stagenet)
-{
- if (testnet)
- return _binary_testnet_blocks_start;
- else if (stagenet)
- return _binary_stagenet_blocks_start;
- else
- return _binary_blocks_start;
-}
-
-size_t get_blocks_dat_size(int testnet, int stagenet)
-{
- if (testnet)
- return (size_t) (_binary_testnet_blocks_end - _binary_testnet_blocks_start);
- else if (stagenet)
- return (size_t) (_binary_stagenet_blocks_end - _binary_stagenet_blocks_start);
- else
- return (size_t) (_binary_blocks_end - _binary_blocks_start);
-}
-
-#endif
diff --git a/src/blocks/blocks.cpp b/src/blocks/blocks.cpp
new file mode 100644
index 000000000..0661f8448
--- /dev/null
+++ b/src/blocks/blocks.cpp
@@ -0,0 +1,31 @@
+#include "blocks.h"
+
+#include <unordered_map>
+
+extern const unsigned char checkpoints[];
+extern const size_t checkpoints_len;
+extern const unsigned char stagenet_blocks[];
+extern const size_t stagenet_blocks_len;
+extern const unsigned char testnet_blocks[];
+extern const size_t testnet_blocks_len;
+
+namespace blocks
+{
+
+ const std::unordered_map<cryptonote::network_type, const epee::span<const unsigned char>, std::hash<size_t>> CheckpointsByNetwork = {
+ {cryptonote::network_type::MAINNET, {checkpoints, checkpoints_len}},
+ {cryptonote::network_type::STAGENET, {stagenet_blocks, stagenet_blocks_len}},
+ {cryptonote::network_type::TESTNET, {testnet_blocks, testnet_blocks_len}}
+ };
+
+ const epee::span<const unsigned char> GetCheckpointsData(cryptonote::network_type network)
+ {
+ const auto it = CheckpointsByNetwork.find(network);
+ if (it != CheckpointsByNetwork.end())
+ {
+ return it->second;
+ }
+ return nullptr;
+ }
+
+}
diff --git a/src/blocks/blocks.dat b/src/blocks/blocks.dat
deleted file mode 100644
index e69de29bb..000000000
--- a/src/blocks/blocks.dat
+++ /dev/null
diff --git a/src/blocks/blocks.h b/src/blocks/blocks.h
index ec683f47e..14e391319 100644
--- a/src/blocks/blocks.h
+++ b/src/blocks/blocks.h
@@ -1,16 +1,12 @@
#ifndef SRC_BLOCKS_BLOCKS_H_
#define SRC_BLOCKS_BLOCKS_H_
-#ifdef __cplusplus
-extern "C" {
-#endif
+#include "cryptonote_config.h"
+#include "span.h"
-const unsigned char *get_blocks_dat_start(int testnet, int stagenet);
-size_t get_blocks_dat_size(int testnet, int stagenet);
-
-#ifdef __cplusplus
+namespace blocks
+{
+ const epee::span<const unsigned char> GetCheckpointsData(cryptonote::network_type network);
}
-#endif
-
#endif /* SRC_BLOCKS_BLOCKS_H_ */
diff --git a/src/common/expect.h b/src/common/expect.h
index 326242502..72e4060a7 100644
--- a/src/common/expect.h
+++ b/src/common/expect.h
@@ -350,7 +350,9 @@ public:
using error_type = std::error_code;
//! Create a successful object.
- expect() = default;
+ expect() noexcept
+ : code_()
+ {}
expect(std::error_code const& code) noexcept
: code_(code)
diff --git a/src/common/password.cpp b/src/common/password.cpp
index b32bedae2..b3c51128f 100644
--- a/src/common/password.cpp
+++ b/src/common/password.cpp
@@ -56,43 +56,59 @@ namespace
bool read_from_tty(epee::wipeable_string& pass, bool hide_input)
{
- static constexpr const char BACKSPACE = 8;
-
HANDLE h_cin = ::GetStdHandle(STD_INPUT_HANDLE);
DWORD mode_old;
::GetConsoleMode(h_cin, &mode_old);
- DWORD mode_new = mode_old & ~((hide_input ? ENABLE_ECHO_INPUT : 0) | ENABLE_LINE_INPUT);
+ DWORD mode_new = mode_old & ~(hide_input ? ENABLE_ECHO_INPUT : 0);
::SetConsoleMode(h_cin, mode_new);
bool r = true;
pass.reserve(tools::password_container::max_password_size);
+ std::vector<int> chlen;
+ chlen.reserve(tools::password_container::max_password_size);
while (pass.size() < tools::password_container::max_password_size)
{
DWORD read;
- char ch;
- r = (TRUE == ::ReadConsoleA(h_cin, &ch, 1, &read, NULL));
+ wchar_t ucs2_ch;
+ r = (TRUE == ::ReadConsoleW(h_cin, &ucs2_ch, 1, &read, NULL));
r &= (1 == read);
+
if (!r)
{
break;
}
- else if (ch == '\n' || ch == '\r')
+ else if (ucs2_ch == L'\r')
+ {
+ continue;
+ }
+ else if (ucs2_ch == L'\n')
{
std::cout << std::endl;
break;
}
- else if (ch == BACKSPACE)
+ else if (ucs2_ch == L'\b')
{
if (!pass.empty())
{
- pass.pop_back();
+ int len = chlen.back();
+ chlen.pop_back();
+ while(len-- > 0)
+ pass.pop_back();
}
+ continue;
}
- else
- {
- pass.push_back(ch);
- }
+
+ char utf8_ch[8] = {0};
+ int len;
+ if((len = WideCharToMultiByte(CP_UTF8, 0, &ucs2_ch, 1, utf8_ch, sizeof(utf8_ch), NULL, NULL)) <= 0)
+ break;
+
+ if(pass.size() + len >= tools::password_container::max_password_size)
+ break;
+
+ chlen.push_back(len);
+ pass += utf8_ch;
}
::SetConsoleMode(h_cin, mode_old);
@@ -146,6 +162,13 @@ namespace
if (!aPass.empty())
{
aPass.pop_back();
+ if (!hide_input)
+ std::cout << "\b\b\b \b\b\b" << std::flush;
+ }
+ else
+ {
+ if (!hide_input)
+ std::cout << "\b\b \b\b" << std::flush;
}
}
else
diff --git a/src/common/perf_timer.cpp b/src/common/perf_timer.cpp
index 6910ebdd4..47f01de65 100644
--- a/src/common/perf_timer.cpp
+++ b/src/common/perf_timer.cpp
@@ -104,11 +104,11 @@ PerformanceTimer::PerformanceTimer(bool paused): started(true), paused(paused)
ticks = get_tick_count();
}
-LoggingPerformanceTimer::LoggingPerformanceTimer(const std::string &s, uint64_t unit, el::Level l): PerformanceTimer(), name(s), unit(unit), level(l)
+LoggingPerformanceTimer::LoggingPerformanceTimer(const std::string &s, const std::string &cat, uint64_t unit, el::Level l): PerformanceTimer(), name(s), cat(cat), unit(unit), level(l)
{
if (!performance_timers)
{
- MLOG(level, "PERF ----------");
+ MCLOG(level, cat.c_str(), "PERF ----------");
performance_timers = new std::vector<LoggingPerformanceTimer*>();
}
else
@@ -117,7 +117,7 @@ LoggingPerformanceTimer::LoggingPerformanceTimer(const std::string &s, uint64_t
if (!pt->started && !pt->paused)
{
size_t size = 0; for (const auto *tmp: *performance_timers) if (!tmp->paused) ++size;
- MLOG(pt->level, "PERF " << std::string((size-1) * 2, ' ') << " " << pt->name);
+ MCLOG(pt->level, cat.c_str(), "PERF " << std::string((size-1) * 2, ' ') << " " << pt->name);
pt->started = true;
}
}
@@ -137,7 +137,7 @@ LoggingPerformanceTimer::~LoggingPerformanceTimer()
char s[12];
snprintf(s, sizeof(s), "%8llu ", (unsigned long long)(ticks_to_ns(ticks) / (1000000000 / unit)));
size_t size = 0; for (const auto *tmp: *performance_timers) if (!tmp->paused || tmp==this) ++size;
- MLOG(level, "PERF " << s << std::string(size * 2, ' ') << " " << name);
+ MCLOG(level, cat.c_str(), "PERF " << s << std::string(size * 2, ' ') << " " << name);
if (performance_timers->empty())
{
delete performance_timers;
diff --git a/src/common/perf_timer.h b/src/common/perf_timer.h
index 675d6234d..1d4dee5b5 100644
--- a/src/common/perf_timer.h
+++ b/src/common/perf_timer.h
@@ -33,9 +33,6 @@
#include <memory>
#include "misc_log_ex.h"
-#undef MONERO_DEFAULT_LOG_CATEGORY
-#define MONERO_DEFAULT_LOG_CATEGORY "perf"
-
namespace tools
{
@@ -67,23 +64,24 @@ protected:
class LoggingPerformanceTimer: public PerformanceTimer
{
public:
- LoggingPerformanceTimer(const std::string &s, uint64_t unit, el::Level l = el::Level::Debug);
+ LoggingPerformanceTimer(const std::string &s, const std::string &cat, uint64_t unit, el::Level l = el::Level::Debug);
~LoggingPerformanceTimer();
private:
std::string name;
+ std::string cat;
uint64_t unit;
el::Level level;
};
void set_performance_timer_log_level(el::Level level);
-#define PERF_TIMER_UNIT(name, unit) tools::LoggingPerformanceTimer pt_##name(#name, unit, tools::performance_timer_log_level)
-#define PERF_TIMER_UNIT_L(name, unit, l) tools::LoggingPerformanceTimer pt_##name(#name, unit, l)
-#define PERF_TIMER(name) PERF_TIMER_UNIT(name, 1000)
-#define PERF_TIMER_L(name, l) PERF_TIMER_UNIT_L(name, 1000, l)
-#define PERF_TIMER_START_UNIT(name, unit) std::unique_ptr<tools::LoggingPerformanceTimer> pt_##name(new tools::LoggingPerformanceTimer(#name, unit, el::Level::Info))
-#define PERF_TIMER_START(name) PERF_TIMER_START_UNIT(name, 1000)
+#define PERF_TIMER_UNIT(name, unit) tools::LoggingPerformanceTimer pt_##name(#name, "perf." MONERO_DEFAULT_LOG_CATEGORY, unit, tools::performance_timer_log_level)
+#define PERF_TIMER_UNIT_L(name, unit, l) tools::LoggingPerformanceTimer pt_##name(#name, "perf." MONERO_DEFAULT_LOG_CATEGORY, unit, l)
+#define PERF_TIMER(name) PERF_TIMER_UNIT(name, 1000000)
+#define PERF_TIMER_L(name, l) PERF_TIMER_UNIT_L(name, 1000000, l)
+#define PERF_TIMER_START_UNIT(name, unit) std::unique_ptr<tools::LoggingPerformanceTimer> pt_##name(new tools::LoggingPerformanceTimer(#name, "perf." MONERO_DEFAULT_LOG_CATEGORY, unit, el::Level::Info))
+#define PERF_TIMER_START(name) PERF_TIMER_START_UNIT(name, 1000000)
#define PERF_TIMER_STOP(name) do { pt_##name.reset(NULL); } while(0)
#define PERF_TIMER_PAUSE(name) pt_##name->pause()
#define PERF_TIMER_RESUME(name) pt_##name->resume()
diff --git a/src/common/spawn.cpp b/src/common/spawn.cpp
index 59f11675c..0a2ce8387 100644
--- a/src/common/spawn.cpp
+++ b/src/common/spawn.cpp
@@ -38,6 +38,7 @@
#endif
#include "misc_log_ex.h"
+#include "util.h"
#include "spawn.h"
namespace tools
@@ -101,6 +102,8 @@ int spawn(const char *filename, const std::vector<std::string>& args, bool wait)
// child
if (pid == 0)
{
+ tools::closefrom(3);
+ close(0);
char *envp[] = {NULL};
execve(filename, argv, envp);
MERROR("Failed to execve: " << strerror(errno));
diff --git a/src/common/threadpool.cpp b/src/common/threadpool.cpp
index 5ea04a353..37825e31d 100644
--- a/src/common/threadpool.cpp
+++ b/src/common/threadpool.cpp
@@ -51,11 +51,19 @@ threadpool::threadpool(unsigned int max_threads) : running(true), active(0) {
}
threadpool::~threadpool() {
+ try
{
const boost::unique_lock<boost::mutex> lock(mutex);
running = false;
has_work.notify_all();
}
+ catch (...)
+ {
+ // if the lock throws, we're just do it without a lock and hope,
+ // since the alternative is terminate
+ running = false;
+ has_work.notify_all();
+ }
for (size_t i = 0; i<threads.size(); i++) {
try { threads[i].join(); }
catch (...) { /* ignore */ }
@@ -91,11 +99,13 @@ unsigned int threadpool::get_max_concurrency() const {
threadpool::waiter::~waiter()
{
+ try
{
boost::unique_lock<boost::mutex> lock(mt);
if (num)
MERROR("wait should have been called before waiter dtor - waiting now");
}
+ catch (...) { /* ignore */ }
try
{
wait(NULL);
diff --git a/src/common/util.cpp b/src/common/util.cpp
index c56c77505..43973c511 100644
--- a/src/common/util.cpp
+++ b/src/common/util.cpp
@@ -28,6 +28,7 @@
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+#include <unistd.h>
#include <cstdio>
#ifdef __GLIBC__
@@ -233,7 +234,7 @@ namespace tools
MERROR("Failed to open " << filename << ": " << std::error_code(GetLastError(), std::system_category()));
}
#else
- m_fd = open(filename.c_str(), O_RDONLY | O_CREAT, 0666);
+ m_fd = open(filename.c_str(), O_RDONLY | O_CREAT | O_CLOEXEC, 0666);
if (m_fd != -1)
{
if (flock(m_fd, LOCK_EX | LOCK_NB) == -1)
@@ -307,10 +308,19 @@ namespace tools
StringCchCopy(pszOS, BUFSIZE, TEXT("Microsoft "));
// Test for the specific product.
+ if ( osvi.dwMajorVersion == 10 )
+ {
+ if ( osvi.dwMinorVersion == 0 )
+ {
+ if( osvi.wProductType == VER_NT_WORKSTATION )
+ StringCchCat(pszOS, BUFSIZE, TEXT("Windows 10 "));
+ else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2016 " ));
+ }
+ }
if ( osvi.dwMajorVersion == 6 )
{
- if( osvi.dwMinorVersion == 0 )
+ if ( osvi.dwMinorVersion == 0 )
{
if( osvi.wProductType == VER_NT_WORKSTATION )
StringCchCat(pszOS, BUFSIZE, TEXT("Windows Vista "));
@@ -324,6 +334,20 @@ namespace tools
else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2008 R2 " ));
}
+ if ( osvi.dwMinorVersion == 2 )
+ {
+ if( osvi.wProductType == VER_NT_WORKSTATION )
+ StringCchCat(pszOS, BUFSIZE, TEXT("Windows 8 "));
+ else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2012 " ));
+ }
+
+ if ( osvi.dwMinorVersion == 3 )
+ {
+ if( osvi.wProductType == VER_NT_WORKSTATION )
+ StringCchCat(pszOS, BUFSIZE, TEXT("Windows 8.1 "));
+ else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2012 R2 " ));
+ }
+
pGPI = (PGPI) GetProcAddress(
GetModuleHandle(TEXT("kernel32.dll")),
"GetProductInfo");
@@ -939,4 +963,51 @@ std::string get_nix_version_display_string()
}
return newval;
}
+
+#ifdef _WIN32
+ std::string input_line_win()
+ {
+ HANDLE hConIn = CreateFileW(L"CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
+ DWORD oldMode;
+
+ FlushConsoleInputBuffer(hConIn);
+ GetConsoleMode(hConIn, &oldMode);
+ SetConsoleMode(hConIn, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT);
+
+ wchar_t buffer[1024];
+ DWORD read;
+
+ ReadConsoleW(hConIn, buffer, sizeof(buffer)/sizeof(wchar_t)-1, &read, nullptr);
+ buffer[read] = 0;
+
+ SetConsoleMode(hConIn, oldMode);
+ CloseHandle(hConIn);
+
+ int size_needed = WideCharToMultiByte(CP_UTF8, 0, buffer, -1, NULL, 0, NULL, NULL);
+ std::string buf(size_needed, '\0');
+ WideCharToMultiByte(CP_UTF8, 0, buffer, -1, &buf[0], size_needed, NULL, NULL);
+ buf.pop_back(); //size_needed includes null that we needed to have space for
+ return buf;
+ }
+#endif
+
+ void closefrom(int fd)
+ {
+#if defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__ || defined __DragonFly__
+ ::closefrom(fd);
+#else
+#if defined __GLIBC__
+ const int sc_open_max = sysconf(_SC_OPEN_MAX);
+ const int MAX_FDS = std::min(65536, sc_open_max);
+#else
+ const int MAX_FDS = 65536;
+#endif
+ while (fd < MAX_FDS)
+ {
+ close(fd);
+ ++fd;
+ }
+#endif
+ }
+
}
diff --git a/src/common/util.h b/src/common/util.h
index 0e0b50520..e793a42b5 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -235,4 +235,9 @@ namespace tools
boost::optional<std::pair<uint32_t, uint32_t>> parse_subaddress_lookahead(const std::string& str);
std::string glob_to_regex(const std::string &val);
+#ifdef _WIN32
+ std::string input_line_win();
+#endif
+
+ void closefrom(int fd);
}
diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp
index e891a748d..1dc1ad71d 100644
--- a/src/cryptonote_basic/account.cpp
+++ b/src/cryptonote_basic/account.cpp
@@ -136,6 +136,7 @@ DISABLE_VS_WARNINGS(4244 4345)
void account_base::set_null()
{
m_keys = account_keys();
+ m_creation_timestamp = 0;
}
//-----------------------------------------------------------------
void account_base::forget_spend_key()
diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h
index d4558ef7b..b0eabb0aa 100644
--- a/src/cryptonote_basic/cryptonote_basic.h
+++ b/src/cryptonote_basic/cryptonote_basic.h
@@ -47,7 +47,6 @@
#include "crypto/crypto.h"
#include "crypto/hash.h"
#include "misc_language.h"
-#include "tx_extra.h"
#include "ringct/rctTypes.h"
#include "device/device.hpp"
diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp
index 5fcfa33f6..e26aac76b 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.cpp
+++ b/src/cryptonote_basic/cryptonote_format_utils.cpp
@@ -199,6 +199,16 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------
+ bool parse_and_validate_tx_prefix_from_blob(const blobdata& tx_blob, transaction_prefix& tx)
+ {
+ std::stringstream ss;
+ ss << tx_blob;
+ binary_archive<false> ba(ss);
+ bool r = ::serialization::serialize_noeof(ba, tx);
+ CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction prefix from blob");
+ return true;
+ }
+ //---------------------------------------------------------------
bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash)
{
std::stringstream ss;
@@ -435,6 +445,91 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------
+ template<typename T>
+ static bool pick(binary_archive<true> &ar, std::vector<tx_extra_field> &fields, uint8_t tag)
+ {
+ std::vector<tx_extra_field>::iterator it;
+ while ((it = std::find_if(fields.begin(), fields.end(), [](const tx_extra_field &f) { return f.type() == typeid(T); })) != fields.end())
+ {
+ bool r = ::do_serialize(ar, tag);
+ CHECK_AND_NO_ASSERT_MES_L1(r, false, "failed to serialize tx extra field");
+ r = ::do_serialize(ar, boost::get<T>(*it));
+ CHECK_AND_NO_ASSERT_MES_L1(r, false, "failed to serialize tx extra field");
+ fields.erase(it);
+ }
+ return true;
+ }
+ //---------------------------------------------------------------
+ bool sort_tx_extra(const std::vector<uint8_t>& tx_extra, std::vector<uint8_t> &sorted_tx_extra, bool allow_partial)
+ {
+ std::vector<tx_extra_field> tx_extra_fields;
+
+ if(tx_extra.empty())
+ {
+ sorted_tx_extra.clear();
+ return true;
+ }
+
+ std::string extra_str(reinterpret_cast<const char*>(tx_extra.data()), tx_extra.size());
+ std::istringstream iss(extra_str);
+ binary_archive<false> ar(iss);
+
+ bool eof = false;
+ size_t processed = 0;
+ while (!eof)
+ {
+ tx_extra_field field;
+ bool r = ::do_serialize(ar, field);
+ if (!r)
+ {
+ MWARNING("failed to deserialize extra field. extra = " << string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast<const char*>(tx_extra.data()), tx_extra.size())));
+ if (!allow_partial)
+ return false;
+ break;
+ }
+ tx_extra_fields.push_back(field);
+ processed = iss.tellg();
+
+ std::ios_base::iostate state = iss.rdstate();
+ eof = (EOF == iss.peek());
+ iss.clear(state);
+ }
+ if (!::serialization::check_stream_state(ar))
+ {
+ MWARNING("failed to deserialize extra field. extra = " << string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast<const char*>(tx_extra.data()), tx_extra.size())));
+ if (!allow_partial)
+ return false;
+ }
+ MTRACE("Sorted " << processed << "/" << tx_extra.size());
+
+ std::ostringstream oss;
+ binary_archive<true> nar(oss);
+
+ // sort by:
+ if (!pick<tx_extra_pub_key>(nar, tx_extra_fields, TX_EXTRA_TAG_PUBKEY)) return false;
+ if (!pick<tx_extra_additional_pub_keys>(nar, tx_extra_fields, TX_EXTRA_TAG_ADDITIONAL_PUBKEYS)) return false;
+ if (!pick<tx_extra_nonce>(nar, tx_extra_fields, TX_EXTRA_NONCE)) return false;
+ if (!pick<tx_extra_merge_mining_tag>(nar, tx_extra_fields, TX_EXTRA_MERGE_MINING_TAG)) return false;
+ if (!pick<tx_extra_mysterious_minergate>(nar, tx_extra_fields, TX_EXTRA_MYSTERIOUS_MINERGATE_TAG)) return false;
+ if (!pick<tx_extra_padding>(nar, tx_extra_fields, TX_EXTRA_TAG_PADDING)) return false;
+
+ // if not empty, someone added a new type and did not add a case above
+ if (!tx_extra_fields.empty())
+ {
+ MERROR("tx_extra_fields not empty after sorting, someone forgot to add a case above");
+ return false;
+ }
+
+ std::string oss_str = oss.str();
+ if (allow_partial && processed < tx_extra.size())
+ {
+ MDEBUG("Appending unparsed data");
+ oss_str += std::string((const char*)tx_extra.data() + processed, tx_extra.size() - processed);
+ }
+ sorted_tx_extra = std::vector<uint8_t>(oss_str.begin(), oss_str.end());
+ return true;
+ }
+ //---------------------------------------------------------------
crypto::public_key get_tx_pub_key_from_extra(const std::vector<uint8_t>& tx_extra, size_t pk_index)
{
std::vector<tx_extra_field> tx_extra_fields;
diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h
index bf71eb591..8d33b1ca4 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.h
+++ b/src/cryptonote_basic/cryptonote_format_utils.h
@@ -31,6 +31,7 @@
#pragma once
#include "blobdatatype.h"
#include "cryptonote_basic_impl.h"
+#include "tx_extra.h"
#include "account.h"
#include "subaddress_index.h"
#include "include_base_utils.h"
@@ -48,6 +49,7 @@ namespace cryptonote
//---------------------------------------------------------------
void get_transaction_prefix_hash(const transaction_prefix& tx, crypto::hash& h);
crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx);
+ bool parse_and_validate_tx_prefix_from_blob(const blobdata& tx_blob, transaction_prefix& tx);
bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash);
bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx);
bool parse_and_validate_tx_base_from_blob(const blobdata& tx_blob, transaction& tx);
@@ -64,6 +66,7 @@ namespace cryptonote
}
bool parse_tx_extra(const std::vector<uint8_t>& tx_extra, std::vector<tx_extra_field>& tx_extra_fields);
+ bool sort_tx_extra(const std::vector<uint8_t>& tx_extra, std::vector<uint8_t> &sorted_tx_extra, bool allow_partial = false);
crypto::public_key get_tx_pub_key_from_extra(const std::vector<uint8_t>& tx_extra, size_t pk_index = 0);
crypto::public_key get_tx_pub_key_from_extra(const transaction_prefix& tx, size_t pk_index = 0);
crypto::public_key get_tx_pub_key_from_extra(const transaction& tx, size_t pk_index = 0);
diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp
index 2bd43de94..d8ca2dd35 100644
--- a/src/cryptonote_basic/miner.cpp
+++ b/src/cryptonote_basic/miner.cpp
@@ -201,7 +201,7 @@ namespace cryptonote
float hr = static_cast<float>(total_hr)/static_cast<float>(m_last_hash_rates.size());
const auto flags = std::cout.flags();
const auto precision = std::cout.precision();
- std::cout << "hashrate: " << std::setprecision(4) << std::fixed << hr << flags << precision << ENDL;
+ std::cout << "hashrate: " << std::setprecision(4) << std::fixed << hr << std::setiosflags(flags) << std::setprecision(precision) << ENDL;
}
}
m_last_hr_merge_time = misc_utils::get_tick_count();
@@ -637,7 +637,7 @@ namespace cryptonote
boost::tribool battery_powered(on_battery_power());
if(!indeterminate( battery_powered ))
{
- on_ac_power = !battery_powered;
+ on_ac_power = !(bool)battery_powered;
}
}
diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h
index a6858ce7c..c62eeb738 100644
--- a/src/cryptonote_config.h
+++ b/src/cryptonote_config.h
@@ -30,6 +30,7 @@
#pragma once
+#include <stdexcept>
#include <string>
#include <boost/uuid/uuid.hpp>
diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt
index 72844db66..231489fdb 100644
--- a/src/cryptonote_core/CMakeLists.txt
+++ b/src/cryptonote_core/CMakeLists.txt
@@ -41,12 +41,6 @@ set(cryptonote_core_private_headers
tx_pool.h
cryptonote_tx_utils.h)
-if(PER_BLOCK_CHECKPOINT)
- set(Blocks "blocks")
-else()
- set(Blocks "")
-endif()
-
monero_private_headers(cryptonote_core
${cryptonote_core_private_headers})
monero_add_library(cryptonote_core
@@ -69,5 +63,4 @@ target_link_libraries(cryptonote_core
${Boost_SYSTEM_LIBRARY}
${Boost_THREAD_LIBRARY}
PRIVATE
- ${Blocks}
${EXTRA_LIBRARIES})
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index eb869b795..77b6d0b69 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -53,9 +53,6 @@
#include "ringct/rctSigs.h"
#include "common/perf_timer.h"
#include "common/notify.h"
-#if defined(PER_BLOCK_CHECKPOINT)
-#include "blocks/blocks.h"
-#endif
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "blockchain"
@@ -341,9 +338,12 @@ uint64_t Blockchain::get_current_blockchain_height() const
//------------------------------------------------------------------
//FIXME: possibly move this into the constructor, to avoid accidentally
// dereferencing a null BlockchainDB pointer
-bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline, const cryptonote::test_options *test_options, difficulty_type fixed_difficulty)
+bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline, const cryptonote::test_options *test_options, difficulty_type fixed_difficulty, const GetCheckpointsCallback& get_checkpoints/* = nullptr*/)
{
LOG_PRINT_L3("Blockchain::" << __func__);
+
+ CHECK_AND_ASSERT_MES(nettype != FAKECHAIN || test_options, false, "fake chain network type used without options");
+
CRITICAL_REGION_LOCAL(m_tx_pool);
CRITICAL_REGION_LOCAL1(m_blockchain_lock);
@@ -439,7 +439,7 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline
#if defined(PER_BLOCK_CHECKPOINT)
if (m_nettype != FAKECHAIN)
- load_compiled_in_block_hashes();
+ load_compiled_in_block_hashes(get_checkpoints);
#endif
MINFO("Blockchain initialized. last block: " << m_db->height() - 1 << ", " << epee::misc_utils::get_time_interval_string(timestamp_diff) << " time ago, current difficulty: " << get_difficulty_for_next_block());
@@ -883,6 +883,15 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
return diff;
}
//------------------------------------------------------------------
+std::vector<time_t> Blockchain::get_last_block_timestamps(unsigned int blocks) const
+{
+ std::vector<time_t> timestamps(blocks);
+ uint64_t height = m_db->height();
+ while (blocks--)
+ timestamps[blocks] = m_db->get_block_timestamp(height - blocks - 1);
+ return timestamps;
+}
+//------------------------------------------------------------------
// This function removes blocks from the blockchain until it gets to the
// position where the blockchain switch started and then re-adds the blocks
// that had been removed.
@@ -2364,7 +2373,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
const bool bulletproof = rct::is_rct_bulletproof(tx.rct_signatures.type);
if (bulletproof || !tx.rct_signatures.p.bulletproofs.empty())
{
- MERROR("Bulletproofs are not allowed before v8");
+ MERROR_VER("Bulletproofs are not allowed before v8");
tvc.m_invalid_output = true;
return false;
}
@@ -2377,7 +2386,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
const bool borromean = rct::is_rct_borromean(tx.rct_signatures.type);
if (borromean)
{
- MERROR("Borromean range proofs are not allowed after v8");
+ MERROR_VER("Borromean range proofs are not allowed after v8");
tvc.m_invalid_output = true;
return false;
}
@@ -4404,19 +4413,21 @@ void Blockchain::cancel()
#if defined(PER_BLOCK_CHECKPOINT)
static const char expected_block_hashes_hash[] = "954cb2bbfa2fe6f74b2cdd22a1a4c767aea249ad47ad4f7c9445f0f03260f511";
-void Blockchain::load_compiled_in_block_hashes()
+void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints)
{
- const bool testnet = m_nettype == TESTNET;
- const bool stagenet = m_nettype == STAGENET;
- if (m_fast_sync && get_blocks_dat_start(testnet, stagenet) != nullptr && get_blocks_dat_size(testnet, stagenet) > 0)
+ if (get_checkpoints == nullptr || !m_fast_sync)
{
- MINFO("Loading precomputed blocks (" << get_blocks_dat_size(testnet, stagenet) << " bytes)");
-
+ return;
+ }
+ const epee::span<const unsigned char> &checkpoints = get_checkpoints(m_nettype);
+ if (!checkpoints.empty())
+ {
+ MINFO("Loading precomputed blocks (" << checkpoints.size() << " bytes)");
if (m_nettype == MAINNET)
{
// first check hash
crypto::hash hash;
- if (!tools::sha256sum(get_blocks_dat_start(testnet, stagenet), get_blocks_dat_size(testnet, stagenet), hash))
+ if (!tools::sha256sum(checkpoints.data(), checkpoints.size(), hash))
{
MERROR("Failed to hash precomputed blocks data");
return;
@@ -4436,9 +4447,9 @@ void Blockchain::load_compiled_in_block_hashes()
}
}
- if (get_blocks_dat_size(testnet, stagenet) > 4)
+ if (checkpoints.size() > 4)
{
- const unsigned char *p = get_blocks_dat_start(testnet, stagenet);
+ const unsigned char *p = checkpoints.data();
const uint32_t nblocks = *p | ((*(p+1))<<8) | ((*(p+2))<<16) | ((*(p+3))<<24);
if (nblocks > (std::numeric_limits<uint32_t>::max() - 4) / sizeof(hash))
{
@@ -4446,7 +4457,7 @@ void Blockchain::load_compiled_in_block_hashes()
return;
}
const size_t size_needed = 4 + nblocks * sizeof(crypto::hash);
- if(nblocks > 0 && nblocks > (m_db->height() + HASH_OF_HASHES_STEP - 1) / HASH_OF_HASHES_STEP && get_blocks_dat_size(testnet, stagenet) >= size_needed)
+ if(nblocks > 0 && nblocks > (m_db->height() + HASH_OF_HASHES_STEP - 1) / HASH_OF_HASHES_STEP && checkpoints.size() >= size_needed)
{
p += sizeof(uint32_t);
m_blocks_hash_of_hashes.reserve(nblocks);
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index ab66fac8b..f140d7719 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -38,9 +38,11 @@
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>
#include <atomic>
+#include <functional>
#include <unordered_map>
#include <unordered_set>
+#include "span.h"
#include "syncobj.h"
#include "string_tools.h"
#include "cryptonote_basic/cryptonote_basic.h"
@@ -73,6 +75,15 @@ namespace cryptonote
db_nosync //!< Leave syncing up to the backing db (safest, but slowest because of disk I/O)
};
+ /**
+ * @brief Callback routine that returns checkpoints data for specific network type
+ *
+ * @param network network type
+ *
+ * @return checkpoints data, empty span if there ain't any checkpoints for specific network type
+ */
+ typedef std::function<const epee::span<const unsigned char>(cryptonote::network_type network)> GetCheckpointsCallback;
+
/************************************************************************/
/* */
/************************************************************************/
@@ -117,10 +128,11 @@ namespace cryptonote
* @param offline true if running offline, else false
* @param test_options test parameters
* @param fixed_difficulty fixed difficulty for testing purposes; 0 means disabled
+ * @param get_checkpoints if set, will be called to get checkpoints data
*
* @return true on success, false if any initialization steps fail
*/
- bool init(BlockchainDB* db, const network_type nettype = MAINNET, bool offline = false, const cryptonote::test_options *test_options = NULL, difficulty_type fixed_difficulty = 0);
+ bool init(BlockchainDB* db, const network_type nettype = MAINNET, bool offline = false, const cryptonote::test_options *test_options = NULL, difficulty_type fixed_difficulty = 0, const GetCheckpointsCallback& get_checkpoints = nullptr);
/**
* @brief Initialize the Blockchain state
@@ -952,6 +964,11 @@ namespace cryptonote
*/
void on_new_tx_from_block(const cryptonote::transaction &tx);
+ /**
+ * @brief returns the timestamps of the last N blocks
+ */
+ std::vector<time_t> get_last_block_timestamps(unsigned int blocks) const;
+
private:
// TODO: evaluate whether or not each of these typedefs are left over from blockchain_storage
@@ -1369,8 +1386,10 @@ namespace cryptonote
* A (possibly empty) set of block hashes can be compiled into the
* monero daemon binary. This function loads those hashes into
* a useful state.
+ *
+ * @param get_checkpoints if set, will be called to get checkpoints data
*/
- void load_compiled_in_block_hashes();
+ void load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints);
/**
* @brief expands v2 transaction data from blockchain
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 69e3c708b..d8c38bf9e 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -389,7 +389,7 @@ namespace cryptonote
return m_blockchain_storage.get_alternative_blocks_count();
}
//-----------------------------------------------------------------------------------------------
- bool core::init(const boost::program_options::variables_map& vm, const char *config_subdir, const cryptonote::test_options *test_options)
+ bool core::init(const boost::program_options::variables_map& vm, const char *config_subdir, const cryptonote::test_options *test_options, const GetCheckpointsCallback& get_checkpoints/* = nullptr */)
{
start_time = std::time(nullptr);
@@ -567,7 +567,7 @@ namespace cryptonote
regtest_hard_forks
};
const difficulty_type fixed_difficulty = command_line::get_arg(vm, arg_fixed_difficulty);
- r = m_blockchain_storage.init(db.release(), m_nettype, m_offline, regtest ? &regtest_test_options : test_options, fixed_difficulty);
+ r = m_blockchain_storage.init(db.release(), m_nettype, m_offline, regtest ? &regtest_test_options : test_options, fixed_difficulty, get_checkpoints);
r = m_mempool.init(max_txpool_weight);
CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool");
@@ -856,16 +856,19 @@ namespace cryptonote
}
waiter.wait(&tpool);
it = tx_blobs.begin();
+ std::vector<bool> already_have(tx_blobs.size(), false);
for (size_t i = 0; i < tx_blobs.size(); i++, ++it) {
if (!results[i].res)
continue;
if(m_mempool.have_tx(results[i].hash))
{
LOG_PRINT_L2("tx " << results[i].hash << "already have transaction in tx_pool");
+ already_have[i] = true;
}
else if(m_blockchain_storage.have_tx(results[i].hash))
{
LOG_PRINT_L2("tx " << results[i].hash << " already have transaction in blockchain");
+ already_have[i] = true;
}
else
{
@@ -887,7 +890,7 @@ namespace cryptonote
std::vector<tx_verification_batch_info> tx_info;
tx_info.reserve(tx_blobs.size());
for (size_t i = 0; i < tx_blobs.size(); i++) {
- if (!results[i].res)
+ if (!results[i].res || already_have[i])
continue;
tx_info.push_back({&results[i].tx, results[i].hash, tvc[i], results[i].res});
}
@@ -897,6 +900,8 @@ namespace cryptonote
bool ok = true;
it = tx_blobs.begin();
for (size_t i = 0; i < tx_blobs.size(); i++, ++it) {
+ if (already_have[i])
+ continue;
if (!results[i].res)
{
ok = false;
@@ -1489,6 +1494,7 @@ namespace cryptonote
m_txpool_auto_relayer.do_call(boost::bind(&core::relay_txpool_transactions, this));
m_check_updates_interval.do_call(boost::bind(&core::check_updates, this));
m_check_disk_space_interval.do_call(boost::bind(&core::check_disk_space, this));
+ m_block_rate_interval.do_call(boost::bind(&core::check_block_rate, this));
m_miner.on_idle();
m_mempool.on_idle();
return true;
@@ -1677,6 +1683,52 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------------------------------------
+ double factorial(unsigned int n)
+ {
+ if (n <= 1)
+ return 1.0;
+ double f = n;
+ while (n-- > 1)
+ f *= n;
+ return f;
+ }
+ //-----------------------------------------------------------------------------------------------
+ static double probability(unsigned int blocks, unsigned int expected)
+ {
+ // https://www.umass.edu/wsp/resources/poisson/#computing
+ return pow(expected, blocks) / (factorial(blocks) * exp(expected));
+ }
+ //-----------------------------------------------------------------------------------------------
+ bool core::check_block_rate()
+ {
+ if (m_offline || m_target_blockchain_height > get_current_blockchain_height())
+ {
+ MDEBUG("Not checking block rate, offline or syncing");
+ return true;
+ }
+
+ static constexpr double threshold = 1. / (864000 / DIFFICULTY_TARGET_V2); // one false positive every 10 days
+
+ const time_t now = time(NULL);
+ const std::vector<time_t> timestamps = m_blockchain_storage.get_last_block_timestamps(60);
+
+ static const unsigned int seconds[] = { 5400, 1800, 600 };
+ for (size_t n = 0; n < sizeof(seconds)/sizeof(seconds[0]); ++n)
+ {
+ unsigned int b = 0;
+ for (time_t ts: timestamps) b += ts >= now - seconds[n];
+ const double p = probability(b, seconds[n] / DIFFICULTY_TARGET_V2);
+ MDEBUG("blocks in the last " << seconds[n] / 60 << " minutes: " << b << " (probability " << p << ")");
+ if (p < threshold)
+ {
+ MWARNING("There were " << b << " blocks in the last " << seconds[n] / 60 << " minutes, there might be large hash rate changes, or we might be partitioned, cut off from the Monero network or under attack. Or it could be just sheer bad luck.");
+ break; // no need to look further
+ }
+ }
+
+ return true;
+ }
+ //-----------------------------------------------------------------------------------------------
void core::set_target_blockchain_height(uint64_t target_blockchain_height)
{
m_target_blockchain_height = target_blockchain_height;
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index 58fe5b7b5..80c452f53 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -244,10 +244,11 @@ namespace cryptonote
* @param vm command line parameters
* @param config_subdir subdirectory for config storage
* @param test_options configuration options for testing
+ * @param get_checkpoints if set, will be called to get checkpoints data, must return checkpoints data pointer and size or nullptr if there ain't any checkpoints for specific network type
*
* @return false if one of the init steps fails, otherwise true
*/
- bool init(const boost::program_options::variables_map& vm, const char *config_subdir = NULL, const test_options *test_options = NULL);
+ bool init(const boost::program_options::variables_map& vm, const char *config_subdir = NULL, const test_options *test_options = NULL, const GetCheckpointsCallback& get_checkpoints = nullptr);
/**
* @copydoc Blockchain::reset_and_set_genesis_block
@@ -945,6 +946,13 @@ namespace cryptonote
*/
bool check_disk_space();
+ /**
+ * @brief checks block rate, and warns if it's too slow
+ *
+ * @return true on success, false otherwise
+ */
+ bool check_block_rate();
+
bool m_test_drop_download = true; //!< whether or not to drop incoming blocks (for testing)
uint64_t m_test_drop_download_height = 0; //!< height under which to drop incoming blocks, if doing so
@@ -969,6 +977,7 @@ namespace cryptonote
epee::math_helper::once_a_time_seconds<60*2, false> m_txpool_auto_relayer; //!< interval for checking re-relaying txpool transactions
epee::math_helper::once_a_time_seconds<60*60*12, true> m_check_updates_interval; //!< interval for checking for new versions
epee::math_helper::once_a_time_seconds<60*10, true> m_check_disk_space_interval; //!< interval for checking for disk space
+ epee::math_helper::once_a_time_seconds<90, false> m_block_rate_interval; //!< interval for checking block rate
std::atomic<bool> m_starter_message_showed; //!< has the "daemon will sync now" message been shown?
diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp
index 4bc33b56b..4fc2736a6 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.cpp
+++ b/src/cryptonote_core/cryptonote_tx_utils.cpp
@@ -38,6 +38,7 @@ using namespace epee;
#include "cryptonote_tx_utils.h"
#include "cryptonote_config.h"
#include "cryptonote_basic/miner.h"
+#include "cryptonote_basic/tx_extra.h"
#include "crypto/crypto.h"
#include "crypto/hash.h"
#include "ringct/rctSigs.h"
@@ -84,6 +85,8 @@ namespace cryptonote
if(!extra_nonce.empty())
if(!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce))
return false;
+ if (!sort_tx_extra(tx.extra, tx.extra))
+ return false;
txin_gen in;
in.height = height;
@@ -434,6 +437,9 @@ namespace cryptonote
add_additional_tx_pub_keys_to_extra(tx.extra, additional_tx_public_keys);
}
+ if (!sort_tx_extra(tx.extra, tx.extra))
+ return false;
+
//check money
if(summary_outs_money > summary_inputs_money )
{
diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp
index c31a2073b..553a22298 100644
--- a/src/cryptonote_core/tx_pool.cpp
+++ b/src/cryptonote_core/tx_pool.cpp
@@ -250,7 +250,7 @@ namespace cryptonote
CRITICAL_REGION_LOCAL1(m_blockchain);
LockedTXN lock(m_blockchain);
m_blockchain.add_txpool_tx(tx, meta);
- if (!insert_key_images(tx, kept_by_block))
+ if (!insert_key_images(tx, id, kept_by_block))
return false;
m_txs_by_fee_and_receive_time.emplace(std::pair<double, std::time_t>(fee / (double)tx_weight, receive_time), id);
}
@@ -290,9 +290,10 @@ namespace cryptonote
{
CRITICAL_REGION_LOCAL1(m_blockchain);
LockedTXN lock(m_blockchain);
- m_blockchain.remove_txpool_tx(get_transaction_hash(tx));
+ const crypto::hash txid = get_transaction_hash(tx);
+ m_blockchain.remove_txpool_tx(txid);
m_blockchain.add_txpool_tx(tx, meta);
- if (!insert_key_images(tx, kept_by_block))
+ if (!insert_key_images(tx, txid, kept_by_block))
return false;
m_txs_by_fee_and_receive_time.emplace(std::pair<double, std::time_t>(fee / (double)tx_weight, receive_time), id);
}
@@ -371,8 +372,8 @@ namespace cryptonote
continue;
}
cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(txid);
- cryptonote::transaction tx;
- if (!parse_and_validate_tx_from_blob(txblob, tx))
+ cryptonote::transaction_prefix tx;
+ if (!parse_and_validate_tx_prefix_from_blob(txblob, tx))
{
MERROR("Failed to parse tx from txpool");
return;
@@ -381,7 +382,7 @@ namespace cryptonote
MINFO("Pruning tx " << txid << " from txpool: weight: " << it->first.second << ", fee/byte: " << it->first.first);
m_blockchain.remove_txpool_tx(txid);
m_txpool_weight -= it->first.second;
- remove_transaction_keyimages(tx);
+ remove_transaction_keyimages(tx, txid);
MINFO("Pruned tx " << txid << " from txpool: weight: " << it->first.second << ", fee/byte: " << it->first.first);
m_txs_by_fee_and_receive_time.erase(it--);
changed = true;
@@ -398,11 +399,10 @@ namespace cryptonote
MINFO("Pool weight after pruning is larger than limit: " << m_txpool_weight << "/" << bytes);
}
//---------------------------------------------------------------------------------
- bool tx_memory_pool::insert_key_images(const transaction &tx, bool kept_by_block)
+ bool tx_memory_pool::insert_key_images(const transaction_prefix &tx, const crypto::hash &id, bool kept_by_block)
{
for(const auto& in: tx.vin)
{
- const crypto::hash id = get_transaction_hash(tx);
CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, txin, false);
std::unordered_set<crypto::hash>& kei_image_set = m_spent_key_images[txin.k_image];
CHECK_AND_ASSERT_MES(kept_by_block || kei_image_set.size() == 0, false, "internal error: kept_by_block=" << kept_by_block
@@ -418,19 +418,17 @@ namespace cryptonote
//FIXME: Can return early before removal of all of the key images.
// At the least, need to make sure that a false return here
// is treated properly. Should probably not return early, however.
- bool tx_memory_pool::remove_transaction_keyimages(const transaction& tx)
+ bool tx_memory_pool::remove_transaction_keyimages(const transaction_prefix& tx, const crypto::hash &actual_hash)
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
// ND: Speedup
- // 1. Move transaction hash calcuation outside of loop. ._.
- crypto::hash actual_hash = get_transaction_hash(tx);
for(const txin_v& vi: tx.vin)
{
CHECKED_GET_SPECIFIC_VARIANT(vi, const txin_to_key, txin, false);
auto it = m_spent_key_images.find(txin.k_image);
CHECK_AND_ASSERT_MES(it != m_spent_key_images.end(), false, "failed to find transaction input in key images. img=" << txin.k_image << ENDL
- << "transaction id = " << get_transaction_hash(tx));
+ << "transaction id = " << actual_hash);
std::unordered_set<crypto::hash>& key_image_set = it->second;
CHECK_AND_ASSERT_MES(key_image_set.size(), false, "empty key_image set, img=" << txin.k_image << ENDL
<< "transaction id = " << actual_hash);
@@ -483,7 +481,7 @@ namespace cryptonote
// remove first, in case this throws, so key images aren't removed
m_blockchain.remove_txpool_tx(id);
m_txpool_weight -= tx_weight;
- remove_transaction_keyimages(tx);
+ remove_transaction_keyimages(tx, id);
}
catch (const std::exception &e)
{
@@ -515,7 +513,7 @@ namespace cryptonote
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
- std::unordered_set<crypto::hash> remove;
+ std::list<std::pair<crypto::hash, uint64_t>> remove;
m_blockchain.for_all_txpool_txes([this, &remove](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata*) {
uint64_t tx_age = time(nullptr) - meta.receive_time;
@@ -533,7 +531,7 @@ namespace cryptonote
m_txs_by_fee_and_receive_time.erase(sorted_it);
}
m_timed_out_transactions.insert(txid);
- remove.insert(txid);
+ remove.push_back(std::make_pair(txid, meta.weight));
}
return true;
}, false);
@@ -541,13 +539,14 @@ namespace cryptonote
if (!remove.empty())
{
LockedTXN lock(m_blockchain);
- for (const crypto::hash &txid: remove)
+ for (const std::pair<crypto::hash, uint64_t> &entry: remove)
{
+ const crypto::hash &txid = entry.first;
try
{
cryptonote::blobdata bd = m_blockchain.get_txpool_tx_blob(txid);
- cryptonote::transaction tx;
- if (!parse_and_validate_tx_from_blob(bd, tx))
+ cryptonote::transaction_prefix tx;
+ if (!parse_and_validate_tx_prefix_from_blob(bd, tx))
{
MERROR("Failed to parse tx from txpool");
// continue
@@ -556,8 +555,8 @@ namespace cryptonote
{
// remove first, so we only remove key images if the tx removal succeeds
m_blockchain.remove_txpool_tx(txid);
- m_txpool_weight -= get_transaction_weight(tx, bd.size());
- remove_transaction_keyimages(tx);
+ m_txpool_weight -= entry.second;
+ remove_transaction_keyimages(tx, txid);
}
}
catch (const std::exception &e)
@@ -1041,7 +1040,7 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------------------------
- bool tx_memory_pool::have_key_images(const std::unordered_set<crypto::key_image>& k_images, const transaction& tx)
+ bool tx_memory_pool::have_key_images(const std::unordered_set<crypto::key_image>& k_images, const transaction_prefix& tx)
{
for(size_t i = 0; i!= tx.vin.size(); i++)
{
@@ -1052,7 +1051,7 @@ namespace cryptonote
return false;
}
//---------------------------------------------------------------------------------
- bool tx_memory_pool::append_key_images(std::unordered_set<crypto::key_image>& k_images, const transaction& tx)
+ bool tx_memory_pool::append_key_images(std::unordered_set<crypto::key_image>& k_images, const transaction_prefix& tx)
{
for(size_t i = 0; i!= tx.vin.size(); i++)
{
@@ -1301,7 +1300,7 @@ namespace cryptonote
// remove tx from db first
m_blockchain.remove_txpool_tx(txid);
m_txpool_weight -= get_transaction_weight(tx, txblob.size());
- remove_transaction_keyimages(tx);
+ remove_transaction_keyimages(tx, txid);
auto sorted_it = find_tx_in_sorted_container(txid);
if (sorted_it == m_txs_by_fee_and_receive_time.end())
{
@@ -1344,14 +1343,14 @@ namespace cryptonote
bool r = m_blockchain.for_all_txpool_txes([this, &remove, kept](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd) {
if (!!kept != !!meta.kept_by_block)
return true;
- cryptonote::transaction tx;
- if (!parse_and_validate_tx_from_blob(*bd, tx))
+ cryptonote::transaction_prefix tx;
+ if (!parse_and_validate_tx_prefix_from_blob(*bd, tx))
{
MWARNING("Failed to parse tx from txpool, removing");
remove.push_back(txid);
return true;
}
- if (!insert_key_images(tx, meta.kept_by_block))
+ if (!insert_key_images(tx, txid, meta.kept_by_block))
{
MFATAL("Failed to insert key images from txpool tx");
return false;
diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h
index 892cadc69..7a0cc23bf 100644
--- a/src/cryptonote_core/tx_pool.h
+++ b/src/cryptonote_core/tx_pool.h
@@ -434,7 +434,7 @@ namespace cryptonote
*
* @return true on success, false on error
*/
- bool insert_key_images(const transaction &tx, bool kept_by_block);
+ bool insert_key_images(const transaction_prefix &tx, const crypto::hash &txid, bool kept_by_block);
/**
* @brief remove old transactions from the pool
@@ -478,10 +478,11 @@ namespace cryptonote
* a transaction from the pool.
*
* @param tx the transaction
+ * @param txid the transaction's hash
*
* @return false if any key images to be removed cannot be found, otherwise true
*/
- bool remove_transaction_keyimages(const transaction& tx);
+ bool remove_transaction_keyimages(const transaction_prefix& tx, const crypto::hash &txid);
/**
* @brief check if any of a transaction's spent key images are present in a given set
@@ -491,7 +492,7 @@ namespace cryptonote
*
* @return true if any key images present in the set, otherwise false
*/
- static bool have_key_images(const std::unordered_set<crypto::key_image>& kic, const transaction& tx);
+ static bool have_key_images(const std::unordered_set<crypto::key_image>& kic, const transaction_prefix& tx);
/**
* @brief append the key images from a transaction to the given set
@@ -501,7 +502,7 @@ namespace cryptonote
*
* @return false if any append fails, otherwise true
*/
- static bool append_key_images(std::unordered_set<crypto::key_image>& kic, const transaction& tx);
+ static bool append_key_images(std::unordered_set<crypto::key_image>& kic, const transaction_prefix& tx);
/**
* @brief check if a transaction is a valid candidate for inclusion in a block
@@ -509,11 +510,11 @@ namespace cryptonote
* @param txd the transaction to check (and info about it)
* @param txid the txid of the transaction to check
* @param txblob the transaction blob to check
- * @param tx the parsed transaction, if successful
+ * @param tx the parsed transaction prefix, if successful
*
* @return true if the transaction is good to go, otherwise false
*/
- bool is_transaction_ready_to_go(txpool_tx_meta_t& txd, const crypto::hash &txid, const cryptonote::blobdata &txblob, transaction &tx) const;
+ bool is_transaction_ready_to_go(txpool_tx_meta_t& txd, const crypto::hash &txid, const cryptonote::blobdata &txblob, transaction&tx) const;
/**
* @brief mark all transactions double spending the one passed
diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt
index f645836a4..117790455 100644
--- a/src/daemon/CMakeLists.txt
+++ b/src/daemon/CMakeLists.txt
@@ -26,20 +26,6 @@
# 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.
-set(blocksdat "")
-if(PER_BLOCK_CHECKPOINT)
- if(APPLE AND DEPENDS)
- add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} --target=x86_64-apple-darwin11 -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.*)
- elseif(APPLE AND NOT DEPENDS)
- 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.*)
- elseif(LINUX_32)
- 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} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat)
- 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} ${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()
-
set(daemon_sources
command_parser_executor.cpp
command_server.cpp
@@ -81,9 +67,7 @@ monero_private_headers(daemon
monero_add_executable(daemon
${daemon_sources}
${daemon_headers}
- ${daemon_private_headers}
- ${blocksdat}
-)
+ ${daemon_private_headers})
target_link_libraries(daemon
PRIVATE
rpc
@@ -106,7 +90,8 @@ target_link_libraries(daemon
${CMAKE_THREAD_LIBS_INIT}
${ZMQ_LIB}
${GNU_READLINE_LIBRARY}
- ${EXTRA_LIBRARIES})
+ ${EXTRA_LIBRARIES}
+ ${Blocks})
set_property(TARGET daemon
PROPERTY
OUTPUT_NAME "monerod")
diff --git a/src/daemon/core.h b/src/daemon/core.h
index 475f418d6..d1defd573 100644
--- a/src/daemon/core.h
+++ b/src/daemon/core.h
@@ -28,6 +28,7 @@
#pragma once
+#include "blocks/blocks.h"
#include "cryptonote_core/cryptonote_core.h"
#include "cryptonote_protocol/cryptonote_protocol_handler.h"
#include "misc_log_ex.h"
@@ -85,7 +86,12 @@ public:
//initialize core here
MGINFO("Initializing core...");
std::string config_subdir = get_config_subdir();
- if (!m_core.init(m_vm_HACK, config_subdir.empty() ? NULL : config_subdir.c_str()))
+#if defined(PER_BLOCK_CHECKPOINT)
+ const cryptonote::GetCheckpointsCallback& get_checkpoints = blocks::GetCheckpointsData;
+#else
+ const cryptonote::GetCheckpointsCallback& get_checkpoints = nullptr;
+#endif
+ if (!m_core.init(m_vm_HACK, config_subdir.empty() ? NULL : config_subdir.c_str(), nullptr, get_checkpoints))
{
return false;
}
diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp
index ea24e32eb..49d6d49cf 100644
--- a/src/daemon/daemon.cpp
+++ b/src/daemon/daemon.cpp
@@ -136,7 +136,19 @@ bool t_daemon::run(bool interactive)
{
throw std::runtime_error{"Can't run stopped daemon"};
}
- tools::signal_handler::install(std::bind(&daemonize::t_daemon::stop_p2p, this));
+
+ std::atomic<bool> stop(false), shutdown(false);
+ boost::thread stop_thread = boost::thread([&stop, &shutdown, this] {
+ while (!stop)
+ epee::misc_utils::sleep_no_w(100);
+ if (shutdown)
+ this->stop_p2p();
+ });
+ epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){
+ stop = true;
+ stop_thread.join();
+ });
+ tools::signal_handler::install([&stop, &shutdown](int){ stop = shutdown = true; });
try
{
diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp
index 6464d372f..34d74d642 100644
--- a/src/daemon/rpc_command_executor.cpp
+++ b/src/daemon/rpc_command_executor.cpp
@@ -449,7 +449,7 @@ bool t_rpc_command_executor::show_status() {
% get_sync_percentage(ires)
% (ires.testnet ? "testnet" : ires.stagenet ? "stagenet" : "mainnet")
% bootstrap_msg
- % (!has_mining_info ? "mining info unavailable" : mining_busy ? "syncing" : mres.active ? ( ( mres.is_background_mining_enabled ? "smart " : "" ) + std::string("mining at ") + get_mining_speed(mres.speed) ) : "not mining")
+ % (!has_mining_info ? "mining info unavailable" : mining_busy ? "syncing" : mres.active ? ( ( mres.is_background_mining_enabled ? "smart " : "" ) + std::string("mining at ") + get_mining_speed(mres.speed) + std::string(" to ") + mres.address ) : "not mining")
% get_mining_speed(ires.difficulty / ires.target)
% (unsigned)hfres.version
% get_fork_extra_info(hfres.earliest_height, net_height, ires.target)
@@ -1717,11 +1717,14 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks)
cryptonote::COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::response bhres;
cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request fereq;
cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response feres;
+ cryptonote::COMMAND_RPC_HARD_FORK_INFO::request hfreq;
+ cryptonote::COMMAND_RPC_HARD_FORK_INFO::response hfres;
epee::json_rpc::error error_resp;
std::string fail_message = "Problem fetching info";
fereq.grace_blocks = 0;
+ hfreq.version = HF_VERSION_PER_BYTE_FEE;
if (m_is_rpc)
{
if (!m_rpc_client->rpc_request(ireq, ires, "/getinfo", fail_message.c_str()))
@@ -1732,6 +1735,10 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks)
{
return true;
}
+ if (!m_rpc_client->json_rpc_request(hfreq, hfres, "hard_fork_info", fail_message.c_str()))
+ {
+ return true;
+ }
}
else
{
@@ -1745,10 +1752,15 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks)
tools::fail_msg_writer() << make_error(fail_message, feres.status);
return true;
}
+ if (!m_rpc_server->on_hard_fork_info(hfreq, hfres, error_resp) || hfres.status != CORE_RPC_STATUS_OK)
+ {
+ tools::fail_msg_writer() << make_error(fail_message, hfres.status);
+ return true;
+ }
}
tools::msg_writer() << "Height: " << ires.height << ", diff " << ires.difficulty << ", cum. diff " << ires.cumulative_difficulty
- << ", target " << ires.target << " sec" << ", dyn fee " << cryptonote::print_money(feres.fee) << "/kB";
+ << ", target " << ires.target << " sec" << ", dyn fee " << cryptonote::print_money(feres.fee) << "/" << (hfres.enabled ? "byte" : "kB");
if (nblocks > 0)
{
diff --git a/src/daemonizer/windows_daemonizer.inl b/src/daemonizer/windows_daemonizer.inl
index 8077f29fb..7e61e3603 100644
--- a/src/daemonizer/windows_daemonizer.inl
+++ b/src/daemonizer/windows_daemonizer.inl
@@ -31,6 +31,7 @@
#include "common/util.h"
#include "daemonizer/windows_service.h"
#include "daemonizer/windows_service_runner.h"
+#include "cryptonote_core/cryptonote_core.h"
#include <shlobj.h>
#include <boost/filesystem/operations.hpp>
diff --git a/src/debug_utilities/object_sizes.cpp b/src/debug_utilities/object_sizes.cpp
index ab8839636..2281d0734 100644
--- a/src/debug_utilities/object_sizes.cpp
+++ b/src/debug_utilities/object_sizes.cpp
@@ -29,6 +29,7 @@
#include <map>
#include "cryptonote_basic/cryptonote_basic.h"
#include "cryptonote_basic/tx_extra.h"
+#include "cryptonote_core/cryptonote_core.h"
#include "cryptonote_core/blockchain.h"
#include "p2p/p2p_protocol_defs.h"
#include "net/connection_basic.hpp"
diff --git a/src/device/CMakeLists.txt b/src/device/CMakeLists.txt
index 8f446f42a..727134f75 100644
--- a/src/device/CMakeLists.txt
+++ b/src/device/CMakeLists.txt
@@ -58,12 +58,6 @@ endif()
set(device_private_headers)
-if(PER_BLOCK_CHECKPOINT)
- set(Blocks "blocks")
-else()
- set(Blocks "")
-endif()
-
monero_private_headers(device
${device_private_headers})
@@ -79,5 +73,4 @@ target_link_libraries(device
ringct_basic
${OPENSSL_CRYPTO_LIBRARIES}
PRIVATE
- ${Blocks}
${EXTRA_LIBRARIES})
diff --git a/src/device/device.cpp b/src/device/device.cpp
index 50041baef..d5e3031ff 100644
--- a/src/device/device.cpp
+++ b/src/device/device.cpp
@@ -41,13 +41,26 @@ namespace hw {
/* SETUP */
/* ======================================================================= */
- static std::unique_ptr<device_registry> registry;
+ static device_registry *get_device_registry(bool clear = false){
+ static device_registry *registry = new device_registry();
+ if (clear)
+ {
+ delete registry;
+ registry = NULL;
+ }
+ return registry;
+ }
+
+ static void clear_device_registry(){
+ get_device_registry(true);
+ }
device_registry::device_registry(){
hw::core::register_all(registry);
#ifdef WITH_DEVICE_LEDGER
hw::ledger::register_all(registry);
#endif
+ atexit(clear_device_registry);
}
bool device_registry::register_device(const std::string & device_name, device * hw_device){
@@ -80,18 +93,12 @@ namespace hw {
}
device& get_device(const std::string & device_descriptor) {
- if (!registry){
- registry.reset(new device_registry());
- }
-
+ device_registry *registry = get_device_registry();
return registry->get_device(device_descriptor);
}
bool register_device(const std::string & device_name, device * hw_device){
- if (!registry){
- registry.reset(new device_registry());
- }
-
+ device_registry *registry = get_device_registry();
return registry->register_device(device_name, hw_device);
}
diff --git a/src/device/device.hpp b/src/device/device.hpp
index cb9117650..815a0ab93 100644
--- a/src/device/device.hpp
+++ b/src/device/device.hpp
@@ -85,7 +85,7 @@ namespace hw {
public:
- device() {}
+ device(): mode(NONE) {}
device(const device &hwdev) {}
virtual ~device() {}
@@ -115,7 +115,8 @@ namespace hw {
virtual bool connect(void) = 0;
virtual bool disconnect(void) = 0;
- virtual bool set_mode(device_mode mode) = 0;
+ virtual bool set_mode(device_mode mode) { this->mode = mode; return true; }
+ virtual device_mode get_mode() const { return mode; }
virtual device_type get_type() const = 0;
@@ -202,6 +203,9 @@ namespace hw {
virtual bool mlsag_sign(const rct::key &c, const rct::keyV &xx, const rct::keyV &alpha, const size_t rows, const size_t dsRows, rct::keyV &ss) = 0;
virtual bool close_tx(void) = 0;
+
+ protected:
+ device_mode mode;
} ;
struct reset_mode {
diff --git a/src/device/device_default.cpp b/src/device/device_default.cpp
index a4f40e041..68f40d91e 100644
--- a/src/device/device_default.cpp
+++ b/src/device/device_default.cpp
@@ -83,7 +83,7 @@ namespace hw {
}
bool device_default::set_mode(device_mode mode) {
- return true;
+ return device::set_mode(mode);
}
/* ======================================================================= */
diff --git a/src/device/device_io_hid.cpp b/src/device/device_io_hid.cpp
index 562aca8b8..1aadfb9ea 100644
--- a/src/device/device_io_hid.cpp
+++ b/src/device/device_io_hid.cpp
@@ -1,3 +1,18 @@
+// Copyright (c) 2017-2018, 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.
//
@@ -13,6 +28,7 @@
//
#if defined(HAVE_HIDAPI)
+#include <boost/scope_exit.hpp>
#include "log.hpp"
#include "device_io_hid.hpp"
@@ -69,11 +85,47 @@ namespace hw {
void device_io_hid::connect(void *params) {
hid_conn_params *p = (struct hid_conn_params*)params;
- this->connect(p->vid, p->pid, p->interface_number, p->usage_page, p->interface_OR_page);
+ this->connect(p->vid, p->pid, p->interface_number, p->usage_page);
}
- void device_io_hid::connect(unsigned int vid, unsigned int pid, unsigned int interface_number, unsigned int usage_page, bool interface_OR_page ) {
- hid_device_info *hwdev_info, *hwdev_info_list;
+ hid_device_info *device_io_hid::find_device(hid_device_info *devices_list, boost::optional<int> interface_number, boost::optional<unsigned short> usage_page) {
+ bool select_any = !interface_number && !usage_page;
+
+ MDEBUG( "Looking for " <<
+ (select_any ? "any HID Device" : "HID Device with") <<
+ (interface_number ? (" interface_number " + std::to_string(interface_number.value())) : "") <<
+ ((interface_number && usage_page) ? " or" : "") <<
+ (usage_page ? (" usage_page " + std::to_string(usage_page.value())) : ""));
+
+ hid_device_info *result = nullptr;
+ for (; devices_list != nullptr; devices_list = devices_list->next) {
+ BOOST_SCOPE_EXIT(&devices_list, &result) {
+ MDEBUG( (result == devices_list ? "SELECTED" : "SKIPPED ") <<
+ " HID Device" <<
+ " path " << safe_hid_path(devices_list) <<
+ " interface_number " << devices_list->interface_number <<
+ " usage_page " << devices_list->usage_page);
+ }
+ BOOST_SCOPE_EXIT_END
+
+ if (result != nullptr) {
+ continue;
+ }
+
+ if (select_any) {
+ result = devices_list;
+ } else if (interface_number && devices_list->interface_number == interface_number.value()) {
+ result = devices_list;
+ } else if (usage_page && devices_list->usage_page == usage_page.value()) {
+ result = devices_list;
+ }
+ }
+
+ return result;
+ }
+
+ void device_io_hid::connect(unsigned int vid, unsigned int pid, boost::optional<int> interface_number, boost::optional<unsigned short> usage_page) {
+ hid_device_info *hwdev_info_list;
hid_device *hwdev;
this->disconnect();
@@ -81,17 +133,8 @@ namespace hw {
hwdev_info_list = hid_enumerate(vid, pid);
ASSERT_X(hwdev_info_list, "Unable to enumerate device "+std::to_string(vid)+":"+std::to_string(vid)+ ": "+ safe_hid_error(this->usb_device));
hwdev = NULL;
- hwdev_info = hwdev_info_list;
- while (hwdev_info) {
- if ((interface_OR_page && ((usage_page == 0xffa0) || (interface_number == 0))) ||
- ((usage_page == 0xffa0) && (interface_number == 0)) ) {
- MDEBUG("HID Device found: " << safe_hid_path(hwdev_info));
- hwdev = hid_open_path(hwdev_info->path);
- break;
- } else {
- MDEBUG("HID Device discard: " << safe_hid_path(hwdev_info) << "("+std::to_string(hwdev_info->usage_page) << "," << std::to_string(hwdev_info->interface_number) << ")");
- }
- hwdev_info = hwdev_info->next;
+ if (hid_device_info *device = find_device(hwdev_info_list, interface_number, usage_page)) {
+ hwdev = hid_open_path(device->path);
}
hid_free_enumeration(hwdev_info_list);
ASSERT_X(hwdev, "Unable to open device "+std::to_string(pid)+":"+std::to_string(vid));
diff --git a/src/device/device_io_hid.hpp b/src/device/device_io_hid.hpp
index 560208c77..bb0f0a814 100644
--- a/src/device/device_io_hid.hpp
+++ b/src/device/device_io_hid.hpp
@@ -29,6 +29,7 @@
#if defined(HAVE_HIDAPI)
+#include <boost/optional/optional.hpp>
#include <hidapi/hidapi.h>
#include "device_io.hpp"
@@ -52,9 +53,8 @@ namespace hw {
struct hid_conn_params {
unsigned int vid;
unsigned int pid;
- unsigned int interface_number;
- unsigned int usage_page;
- bool interface_OR_page ;
+ int interface_number;
+ unsigned short usage_page;
};
@@ -82,17 +82,15 @@ namespace hw {
unsigned int wrapCommand(const unsigned char *command, size_t command_len, unsigned char *out, size_t out_len);
unsigned int unwrapReponse(const unsigned char *data, size_t data_len, unsigned char *out, size_t out_len);
+ hid_device_info *find_device(hid_device_info *devices_list, boost::optional<int> interface_number, boost::optional<unsigned short> usage_page);
public:
bool hid_verbose = false;
- const unsigned int OR_SELECT = 1;
- const unsigned int AND_SELECT = 2;
-
- const unsigned char DEFAULT_CHANNEL = 0x0001;
- const unsigned char DEFAULT_TAG = 0x01;
- const unsigned int DEFAULT_PACKET_SIZE = 64;
- const unsigned int DEFAULT_TIMEOUT = 120000;
+ static const unsigned short DEFAULT_CHANNEL = 0x0001;
+ static const unsigned char DEFAULT_TAG = 0x01;
+ static const unsigned int DEFAULT_PACKET_SIZE = 64;
+ static const unsigned int DEFAULT_TIMEOUT = 120000;
device_io_hid(unsigned short channel, unsigned char tag, unsigned int packet_zize, unsigned int timeout);
device_io_hid();
@@ -100,7 +98,7 @@ namespace hw {
void init();
void connect(void *params);
- void connect(unsigned int vid, unsigned int pid, unsigned int interface_number, unsigned int usage_page, bool interface_OR_page );
+ void connect(unsigned int vid, unsigned int pid, boost::optional<int> interface_number, boost::optional<unsigned short> usage_page);
bool connected() const;
int exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len);
void disconnect();
diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp
index 456eda739..0a86e6987 100644
--- a/src/device/device_ledger.cpp
+++ b/src/device/device_ledger.cpp
@@ -48,21 +48,12 @@ namespace hw {
/* ===================================================================== */
/* === Debug ==== */
/* ===================================================================== */
- #ifdef WIN32
- static char *pcsc_stringify_error(LONG rv) {
- static __thread char out[20];
- sprintf_s(out, sizeof(out), "0x%08lX", rv);
-
- return out;
- }
- #endif
void set_apdu_verbose(bool verbose) {
apdu_verbose = verbose;
}
#define TRACKD MTRACE("hw")
- #define ASSERT_RV(rv) CHECK_AND_ASSERT_THROW_MES((rv)==SCARD_S_SUCCESS, "Fail SCard API : (" << (rv) << ") "<< pcsc_stringify_error(rv)<<" Device="<<this->id<<", hCard="<<hCard<<", hContext="<<hContext);
#define ASSERT_SW(sw,ok,msk) CHECK_AND_ASSERT_THROW_MES(((sw)&(mask))==(ok), "Wrong Device Status : SW=" << std::hex << (sw) << " (EXPECT=" << std::hex << (ok) << ", MASK=" << std::hex << (mask) << ")") ;
#define ASSERT_T0(exp) CHECK_AND_ASSERT_THROW_MES(exp, "Protocol assert failure: "#exp ) ;
#define ASSERT_X(exp,msg) CHECK_AND_ASSERT_THROW_MES(exp, msg);
@@ -185,7 +176,7 @@ namespace hw {
#define INS_GET_RESPONSE 0xc0
- device_ledger::device_ledger(): hw_device(0x0101, 0x05, 64, 10000) {
+ device_ledger::device_ledger(): hw_device(0x0101, 0x05, 64, 120000) {
this->id = device_id++;
this->reset_buffer();
this->mode = NONE;
@@ -349,7 +340,7 @@ namespace hw {
bool device_ledger::connect(void) {
this->disconnect();
- hw_device.connect(0x2c97,0x0001, 0, 0xffa0, hw_device.OR_SELECT);
+ hw_device.connect(0x2c97, 0x0001, 0, 0xffa0);
this->reset();
#ifdef DEBUG_HWDEVICE
cryptonote::account_public_address pubkey;
@@ -405,7 +396,7 @@ namespace hw {
CHECK_AND_ASSERT_THROW_MES(false, " device_ledger::set_mode(unsigned int mode): invalid mode: "<<mode);
}
MDEBUG("Switch to mode: " <<mode);
- return true;
+ return device::set_mode(mode);
}
diff --git a/src/gen_multisig/gen_multisig.cpp b/src/gen_multisig/gen_multisig.cpp
index 69be70e1b..8262e86f7 100644
--- a/src/gen_multisig/gen_multisig.cpp
+++ b/src/gen_multisig/gen_multisig.cpp
@@ -104,7 +104,7 @@ static bool generate_multisig(uint32_t threshold, uint32_t total, const std::str
wallets[n]->decrypt_keys(pwd_container->password());
if (!tools::wallet2::verify_multisig_info(wallets[n]->get_multisig_info(), sk[n], pk[n]))
{
- tools::fail_msg_writer() << tr("Failed to verify multisig info");
+ tools::fail_msg_writer() << genms::tr("Failed to verify multisig info");
return false;
}
wallets[n]->encrypt_keys(pwd_container->password());
@@ -130,8 +130,8 @@ static bool generate_multisig(uint32_t threshold, uint32_t total, const std::str
ss << " " << name << std::endl;
}
- // finalize step if needed
- if (!extra_info[0].empty())
+ //exchange keys unless exchange_multisig_keys returns no extra info
+ while (!extra_info[0].empty())
{
std::unordered_set<crypto::public_key> pkeys;
std::vector<crypto::public_key> signers(total);
@@ -145,11 +145,7 @@ static bool generate_multisig(uint32_t threshold, uint32_t total, const std::str
}
for (size_t n = 0; n < total; ++n)
{
- if (!wallets[n]->finalize_multisig(pwd_container->password(), pkeys, signers))
- {
- tools::fail_msg_writer() << genms::tr("Error finalizing multisig");
- return false;
- }
+ extra_info[n] = wallets[n]->exchange_multisig_keys(pwd_container->password(), pkeys, signers);
}
}
@@ -246,11 +242,6 @@ int main(int argc, char* argv[])
return 1;
}
- if (threshold != total-1 && threshold != total)
- {
- tools::fail_msg_writer() << genms::tr("Error: unsupported scheme: only N/N and N-1/N are supported");
- return 1;
- }
bool create_address_file = command_line::get_arg(*vm, arg_create_address_file);
if (!generate_multisig(threshold, total, basename, testnet ? TESTNET : stagenet ? STAGENET : MAINNET, create_address_file))
return 1;
diff --git a/src/multisig/multisig.cpp b/src/multisig/multisig.cpp
index a0a788b7d..33d0a312f 100644
--- a/src/multisig/multisig.cpp
+++ b/src/multisig/multisig.cpp
@@ -84,6 +84,43 @@ namespace cryptonote
}
}
//-----------------------------------------------------------------
+ std::vector<crypto::public_key> generate_multisig_derivations(const account_keys &keys, const std::vector<crypto::public_key> &derivations)
+ {
+ std::vector<crypto::public_key> multisig_keys;
+ crypto::secret_key blinded_skey = get_multisig_blinded_secret_key(keys.m_spend_secret_key);
+ for (const auto &k: derivations)
+ {
+ rct::key d = rct::scalarmultKey(rct::pk2rct(k), rct::sk2rct(blinded_skey));
+ multisig_keys.push_back(rct::rct2pk(d));
+ }
+
+ return multisig_keys;
+ }
+ //-----------------------------------------------------------------
+ crypto::secret_key calculate_multisig_signer_key(const std::vector<crypto::secret_key>& multisig_keys)
+ {
+ rct::key secret_key = rct::zero();
+ for (const auto &k: multisig_keys)
+ {
+ sc_add(secret_key.bytes, secret_key.bytes, (const unsigned char*)k.data);
+ }
+
+ return rct::rct2sk(secret_key);
+ }
+ //-----------------------------------------------------------------
+ std::vector<crypto::secret_key> calculate_multisig_keys(const std::vector<crypto::public_key>& derivations)
+ {
+ std::vector<crypto::secret_key> multisig_keys;
+ multisig_keys.reserve(derivations.size());
+
+ for (const auto &k: derivations)
+ {
+ multisig_keys.emplace_back(get_multisig_blinded_secret_key(rct::rct2sk(rct::pk2rct(k))));
+ }
+
+ return multisig_keys;
+ }
+ //-----------------------------------------------------------------
crypto::secret_key generate_multisig_view_secret_key(const crypto::secret_key &skey, const std::vector<crypto::secret_key> &skeys)
{
rct::key view_skey = rct::sk2rct(get_multisig_blinded_secret_key(skey));
@@ -92,7 +129,7 @@ namespace cryptonote
return rct::rct2sk(view_skey);
}
//-----------------------------------------------------------------
- crypto::public_key generate_multisig_N1_N_spend_public_key(const std::vector<crypto::public_key> &pkeys)
+ crypto::public_key generate_multisig_M_N_spend_public_key(const std::vector<crypto::public_key> &pkeys)
{
rct::key spend_public_key = rct::identity();
for (const auto &pk: pkeys)
@@ -141,4 +178,9 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------
+ uint32_t multisig_rounds_required(uint32_t participants, uint32_t threshold)
+ {
+ CHECK_AND_ASSERT_THROW_MES(participants >= threshold, "participants must be greater or equal than threshold");
+ return participants - threshold + 1;
+ }
}
diff --git a/src/multisig/multisig.h b/src/multisig/multisig.h
index f95611441..93a756812 100644
--- a/src/multisig/multisig.h
+++ b/src/multisig/multisig.h
@@ -41,9 +41,31 @@ namespace cryptonote
crypto::secret_key get_multisig_blinded_secret_key(const crypto::secret_key &key);
void generate_multisig_N_N(const account_keys &keys, const std::vector<crypto::public_key> &spend_keys, std::vector<crypto::secret_key> &multisig_keys, rct::key &spend_skey, rct::key &spend_pkey);
void generate_multisig_N1_N(const account_keys &keys, const std::vector<crypto::public_key> &spend_keys, std::vector<crypto::secret_key> &multisig_keys, rct::key &spend_skey, rct::key &spend_pkey);
+ /**
+ * @brief generate_multisig_derivations performs common DH key derivation.
+ * Each middle round in M/N scheme is DH exchange of public multisig keys of other participants multiplied by secret spend key of current participant.
+ * this functions does the following: new multisig key = secret spend * public multisig key
+ * @param keys - current wallet's keys
+ * @param derivations - public multisig keys of other participants
+ * @return new public multisig keys derived from previous round. This data needs to be exchange with other participants
+ */
+ std::vector<crypto::public_key> generate_multisig_derivations(const account_keys &keys, const std::vector<crypto::public_key> &derivations);
+ crypto::secret_key calculate_multisig_signer_key(const std::vector<crypto::secret_key>& derivations);
+ /**
+ * @brief calculate_multisig_keys. Calculates secret multisig keys from others' participants ones as follows: mi = H(Mi)
+ * @param derivations - others' participants public multisig keys.
+ * @return vector of current wallet's multisig secret keys
+ */
+ std::vector<crypto::secret_key> calculate_multisig_keys(const std::vector<crypto::public_key>& derivations);
crypto::secret_key generate_multisig_view_secret_key(const crypto::secret_key &skey, const std::vector<crypto::secret_key> &skeys);
- crypto::public_key generate_multisig_N1_N_spend_public_key(const std::vector<crypto::public_key> &pkeys);
+ /**
+ * @brief generate_multisig_M_N_spend_public_key calculates multisig wallet's spend public key by summing all of public multisig keys
+ * @param pkeys unique public multisig keys
+ * @return multisig wallet's spend public key
+ */
+ crypto::public_key generate_multisig_M_N_spend_public_key(const std::vector<crypto::public_key> &pkeys);
bool generate_multisig_key_image(const account_keys &keys, size_t multisig_key_index, const crypto::public_key& out_key, crypto::key_image& ki);
void generate_multisig_LR(const crypto::public_key pkey, const crypto::secret_key &k, crypto::public_key &L, crypto::public_key &R);
bool generate_multisig_composite_key_image(const account_keys &keys, const std::unordered_map<crypto::public_key, cryptonote::subaddress_index>& subaddresses, const crypto::public_key& out_key, const crypto::public_key &tx_public_key, const std::vector<crypto::public_key>& additional_tx_public_keys, size_t real_output_index, const std::vector<crypto::key_image> &pkis, crypto::key_image &ki);
+ uint32_t multisig_rounds_required(uint32_t participants, uint32_t threshold);
}
diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp
index c9ca63f43..e9d2061e8 100644
--- a/src/p2p/net_node.cpp
+++ b/src/p2p/net_node.cpp
@@ -29,6 +29,7 @@
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#include "common/command_line.h"
+#include "cryptonote_core/cryptonote_core.h"
#include "net_node.h"
namespace nodetool
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index 74924e4f4..a61b6107f 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -655,10 +655,14 @@ namespace nodetool
{
kill();
m_peerlist.deinit();
- m_net_server.deinit_server();
- // remove UPnP port mapping
- if(!m_no_igd)
- delete_upnp_port_mapping(m_listening_port);
+
+ if (!m_offline)
+ {
+ m_net_server.deinit_server();
+ // remove UPnP port mapping
+ if(!m_no_igd)
+ delete_upnp_port_mapping(m_listening_port);
+ }
return store_config();
}
//-----------------------------------------------------------------------------------
@@ -937,7 +941,7 @@ namespace nodetool
bool res = m_net_server.connect(epee::string_tools::get_ip_string_from_int32(ipv4.ip()),
epee::string_tools::num_to_string_fast(ipv4.port()),
m_config.m_net_config.connection_timeout,
- con, m_bind_ip.empty() ? "0.0.0.0" : m_bind_ip);
+ con);
if(!res)
{
@@ -1002,7 +1006,7 @@ namespace nodetool
bool res = m_net_server.connect(epee::string_tools::get_ip_string_from_int32(ipv4.ip()),
epee::string_tools::num_to_string_fast(ipv4.port()),
m_config.m_net_config.connection_timeout,
- con, m_bind_ip.empty() ? "0.0.0.0" : m_bind_ip);
+ con);
if (!res) {
bool is_priority = is_priority_node(na);
@@ -1617,7 +1621,7 @@ namespace nodetool
return false;
}
return true;
- }, m_bind_ip.empty() ? "0.0.0.0" : m_bind_ip);
+ });
if(!r)
{
LOG_WARNING_CC(context, "Failed to call connect_async, network error.");
@@ -2042,7 +2046,7 @@ namespace nodetool
char lanAddress[64];
result = UPNP_GetValidIGD(deviceList, &urls, &igdData, lanAddress, sizeof lanAddress);
freeUPNPDevlist(deviceList);
- if (result != 0) {
+ if (result > 0) {
if (result == 1) {
std::ostringstream portString;
portString << port;
@@ -2088,7 +2092,7 @@ namespace nodetool
char lanAddress[64];
result = UPNP_GetValidIGD(deviceList, &urls, &igdData, lanAddress, sizeof lanAddress);
freeUPNPDevlist(deviceList);
- if (result != 0) {
+ if (result > 0) {
if (result == 1) {
std::ostringstream portString;
portString << port;
diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp
index 0d1789a38..181e89c45 100644
--- a/src/ringct/rctSigs.cpp
+++ b/src/ringct/rctSigs.cpp
@@ -44,6 +44,19 @@ using namespace std;
#define CHECK_AND_ASSERT_MES_L1(expr, ret, message) {if(!(expr)) {MCERROR("verify", message); return ret;}}
+namespace
+{
+ rct::Bulletproof make_dummy_bulletproof(size_t n_outs)
+ {
+ const rct::key I = rct::identity();
+ size_t nrl = 0;
+ while ((1u << nrl) < n_outs)
+ ++nrl;
+ nrl += 6;
+ return rct::Bulletproof{rct::keyV(n_outs, I), I, I, I, I, I, I, rct::keyV(nrl, I), rct::keyV(nrl, I), I, I, I};
+ }
+}
+
namespace rct {
Bulletproof proveRangeBulletproof(key &C, key &mask, uint64_t amount)
{
@@ -762,10 +775,20 @@ namespace rct {
if (range_proof_type == RangeProofPaddedBulletproof)
{
rct::keyV C, masks;
- rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, outamounts));
- #ifdef DBG
- CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof");
- #endif
+ if (hwdev.get_mode() == hw::device::TRANSACTION_CREATE_FAKE)
+ {
+ // use a fake bulletproof for speed
+ rv.p.bulletproofs.push_back(make_dummy_bulletproof(outamounts.size()));
+ C = rct::keyV(outamounts.size(), I);
+ masks = rct::keyV(outamounts.size(), I);
+ }
+ else
+ {
+ rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, outamounts));
+ #ifdef DBG
+ CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof");
+ #endif
+ }
for (i = 0; i < outamounts.size(); ++i)
{
rv.outPk[i].mask = rct::scalarmult8(C[i]);
@@ -782,10 +805,20 @@ namespace rct {
std::vector<uint64_t> batch_amounts(batch_size);
for (i = 0; i < batch_size; ++i)
batch_amounts[i] = outamounts[i + amounts_proved];
- rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, batch_amounts));
- #ifdef DBG
- CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof");
- #endif
+ if (hwdev.get_mode() == hw::device::TRANSACTION_CREATE_FAKE)
+ {
+ // use a fake bulletproof for speed
+ rv.p.bulletproofs.push_back(make_dummy_bulletproof(batch_amounts.size()));
+ C = rct::keyV(batch_amounts.size(), I);
+ masks = rct::keyV(batch_amounts.size(), I);
+ }
+ else
+ {
+ rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, batch_amounts));
+ #ifdef DBG
+ CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof");
+ #endif
+ }
for (i = 0; i < batch_size; ++i)
{
rv.outPk[i + amounts_proved].mask = rct::scalarmult8(C[i]);
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index be511a2d2..aa9d3d64b 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -212,23 +212,15 @@ namespace cryptonote
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
- static cryptonote::blobdata get_pruned_tx_blob(cryptonote::transaction &tx)
- {
- std::stringstream ss;
- binary_archive<true> ba(ss);
- bool r = tx.serialize_base(ba);
- CHECK_AND_ASSERT_MES(r, cryptonote::blobdata(), "Failed to serialize rct signatures base");
- return ss.str();
- }
- //------------------------------------------------------------------------------------------------------------------------------
- static cryptonote::blobdata get_pruned_tx_json(cryptonote::transaction &tx)
- {
- std::stringstream ss;
- json_archive<true> ar(ss);
- bool r = tx.serialize_base(ar);
- CHECK_AND_ASSERT_MES(r, cryptonote::blobdata(), "Failed to serialize rct signatures base");
- return ss.str();
- }
+ class pruned_transaction {
+ transaction& tx;
+ public:
+ pruned_transaction(transaction& tx) : tx(tx) {}
+ BEGIN_SERIALIZE_OBJECT()
+ bool r = tx.serialize_base(ar);
+ if (!r) return false;
+ END_SERIALIZE()
+ };
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res)
{
@@ -564,10 +556,11 @@ namespace cryptonote
crypto::hash tx_hash = *vhi++;
e.tx_hash = *txhi++;
- blobdata blob = req.prune ? get_pruned_tx_blob(tx) : t_serializable_object_to_blob(tx);
+ pruned_transaction pruned_tx{tx};
+ blobdata blob = req.prune ? t_serializable_object_to_blob(pruned_tx) : t_serializable_object_to_blob(tx);
e.as_hex = string_tools::buff_to_hex_nodelimer(blob);
if (req.decode_as_json)
- e.as_json = req.prune ? get_pruned_tx_json(tx) : obj_to_json_str(tx);
+ e.as_json = req.prune ? obj_to_json_str(pruned_tx) : obj_to_json_str(tx);
e.in_pool = pool_tx_hashes.find(tx_hash) != pool_tx_hashes.end();
if (e.in_pool)
{
@@ -924,6 +917,8 @@ namespace cryptonote
return r;
m_core.get_pool_transactions_and_spent_keys_info(res.transactions, res.spent_key_images, !request_has_rpc_origin || !m_restricted);
+ for (tx_info& txi : res.transactions)
+ txi.tx_blob = epee::string_tools::buff_to_hex_nodelimer(txi.tx_blob);
res.status = CORE_RPC_STATUS_OK;
return true;
}
@@ -1090,7 +1085,7 @@ namespace cryptonote
{
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
error_resp.message = "Internal error: failed to create block template";
- LOG_ERROR("Failed to tx pub key in coinbase extra");
+ LOG_ERROR("Failed to get tx pub key in coinbase extra");
return false;
}
res.reserved_offset = slow_memmem((void*)block_blob.data(), block_blob.size(), &tx_pub_key, sizeof(tx_pub_key));
@@ -2118,6 +2113,8 @@ namespace cryptonote
try
{
+ // 0 is placeholder for the whole chain
+ const uint64_t req_to_height = req.to_height ? req.to_height : (m_core.get_current_blockchain_height() - 1);
for (uint64_t amount: req.amounts)
{
static struct D
@@ -2130,7 +2127,7 @@ namespace cryptonote
} d;
boost::unique_lock<boost::mutex> lock(d.mutex);
- if (d.cached && amount == 0 && d.cached_from == req.from_height && d.cached_to == req.to_height)
+ if (d.cached && amount == 0 && d.cached_from == req.from_height && d.cached_to == req_to_height)
{
res.distributions.push_back({amount, d.cached_start_height, req.binary, d.cached_distribution, d.cached_base});
if (!req.cumulative)
@@ -2145,23 +2142,23 @@ namespace cryptonote
std::vector<uint64_t> distribution;
uint64_t start_height, base;
- if (!m_core.get_output_distribution(amount, req.from_height, req.to_height, start_height, distribution, base))
+ if (!m_core.get_output_distribution(amount, req.from_height, req_to_height, start_height, distribution, base))
{
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
error_resp.message = "Failed to get rct distribution";
return false;
}
- if (req.to_height > 0 && req.to_height >= req.from_height)
+ if (req_to_height > 0 && req_to_height >= req.from_height)
{
uint64_t offset = std::max(req.from_height, start_height);
- if (offset <= req.to_height && req.to_height - offset + 1 < distribution.size())
- distribution.resize(req.to_height - offset + 1);
+ if (offset <= req_to_height && req_to_height - offset + 1 < distribution.size())
+ distribution.resize(req_to_height - offset + 1);
}
if (amount == 0)
{
d.cached_from = req.from_height;
- d.cached_to = req.to_height;
+ d.cached_to = req_to_height;
d.cached_distribution = distribution;
d.cached_start_height = start_height;
d.cached_base = base;
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index 3b654d4cb..b229841d6 100644
--- a/src/rpc/core_rpc_server_commands_defs.h
+++ b/src/rpc/core_rpc_server_commands_defs.h
@@ -782,9 +782,6 @@ namespace cryptonote
std::string tx_as_hex;
bool do_not_relay;
- request() {}
- explicit request(const transaction &);
-
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_as_hex)
KV_SERIALIZE_OPT(do_not_relay, false)
diff --git a/src/rpc/daemon_messages.h b/src/rpc/daemon_messages.h
index 8fff369df..8488bab0b 100644
--- a/src/rpc/daemon_messages.h
+++ b/src/rpc/daemon_messages.h
@@ -67,7 +67,7 @@ class classname \
// NOTE: when using a type with multiple template parameters,
// replace any comma in the template specifier with the macro
// above, or the preprocessor will eat the comma in a bad way.
-#define RPC_MESSAGE_MEMBER(type, name) type name;
+#define RPC_MESSAGE_MEMBER(type, name) type name = {}
namespace cryptonote
diff --git a/src/rpc/message.h b/src/rpc/message.h
index 16b8e92fc..56087b998 100644
--- a/src/rpc/message.h
+++ b/src/rpc/message.h
@@ -65,7 +65,7 @@ namespace rpc
static const char* STATUS_BAD_REQUEST;
static const char* STATUS_BAD_JSON;
- Message() : status(STATUS_OK) { }
+ Message() : status(STATUS_OK), rpc_version(0) { }
virtual ~Message() { }
diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h
index 5fc382a1e..2050e88e2 100644
--- a/src/serialization/serialization.h
+++ b/src/serialization/serialization.h
@@ -318,7 +318,7 @@ namespace serialization {
* \brief self explanatory
*/
template<class Stream>
- bool do_check_stream_state(Stream& s, boost::mpl::bool_<true>)
+ bool do_check_stream_state(Stream& s, boost::mpl::bool_<true>, bool noeof)
{
return s.good();
}
@@ -329,13 +329,13 @@ namespace serialization {
* \detailed Also checks to make sure that the stream is not at EOF
*/
template<class Stream>
- bool do_check_stream_state(Stream& s, boost::mpl::bool_<false>)
+ bool do_check_stream_state(Stream& s, boost::mpl::bool_<false>, bool noeof)
{
bool result = false;
if (s.good())
{
std::ios_base::iostate state = s.rdstate();
- result = EOF == s.peek();
+ result = noeof || EOF == s.peek();
s.clear(state);
}
return result;
@@ -347,9 +347,9 @@ namespace serialization {
* \brief calls detail::do_check_stream_state for ar
*/
template<class Archive>
- bool check_stream_state(Archive& ar)
+ bool check_stream_state(Archive& ar, bool noeof = false)
{
- return detail::do_check_stream_state(ar.stream(), typename Archive::is_saving());
+ return detail::do_check_stream_state(ar.stream(), typename Archive::is_saving(), noeof);
}
/*! \fn serialize
@@ -360,6 +360,17 @@ namespace serialization {
inline bool serialize(Archive &ar, T &v)
{
bool r = do_serialize(ar, v);
- return r && check_stream_state(ar);
+ return r && check_stream_state(ar, false);
+ }
+
+ /*! \fn serialize
+ *
+ * \brief serializes \a v into \a ar
+ */
+ template <class Archive, class T>
+ inline bool serialize_noeof(Archive &ar, T &v)
+ {
+ bool r = do_serialize(ar, v);
+ return r && check_stream_state(ar, true);
}
}
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index 90f8535d7..f171d35b6 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -137,47 +137,6 @@ namespace
const command_line::arg_descriptor< std::vector<std::string> > arg_command = {"command", ""};
-#ifdef WIN32
- // Translate from CP850 to UTF-8;
- // std::getline for a Windows console returns a string in CP437 or CP850; as simplewallet,
- // like all of Monero, is assumed to work internally with UTF-8 throughout, even on Windows
- // (although only implemented partially), a translation to UTF-8 is needed for input.
- //
- // Note that if a program is started inside the MSYS2 shell somebody already translates
- // console input to UTF-8, but it's not clear how one could detect that in order to avoid
- // double-translation; this code here thus breaks UTF-8 input within a MSYS2 shell,
- // unfortunately.
- //
- // Note also that input for passwords is NOT translated, to remain compatible with any
- // passwords containing special characters that predate this switch to UTF-8 support.
- template<typename T>
- static T cp850_to_utf8(const T &cp850_str)
- {
- boost::locale::generator gen;
- gen.locale_cache_enabled(true);
- std::locale loc = gen("en_US.CP850");
- const boost::locale::conv::method_type how = boost::locale::conv::default_method;
- T result;
- const char *begin = cp850_str.data();
- const char *end = begin + cp850_str.size();
- result.reserve(end-begin);
- typedef std::back_insert_iterator<T> inserter_type;
- inserter_type inserter(result);
- boost::locale::utf::code_point c;
- while(begin!=end) {
- c=boost::locale::utf::utf_traits<char>::template decode<char const *>(begin,end);
- if(c==boost::locale::utf::illegal || c==boost::locale::utf::incomplete) {
- if(how==boost::locale::conv::stop)
- throw boost::locale::conv::conversion_error();
- }
- else {
- boost::locale::utf::utf_traits<char>::template encode<inserter_type>(c,inserter);
- }
- }
- return result;
- }
-#endif
-
std::string input_line(const std::string& prompt)
{
#ifdef HAVE_READLINE
@@ -186,9 +145,10 @@ namespace
std::cout << prompt;
std::string buf;
+#ifdef _WIN32
+ buf = tools::input_line_win();
+#else
std::getline(std::cin, buf);
-#ifdef WIN32
- buf = cp850_to_utf8(buf);
#endif
return epee::string_tools::trim(buf);
@@ -208,10 +168,6 @@ namespace
epee::wipeable_string buf = pwd_container->password();
-#ifdef WIN32
- buf = cp850_to_utf8(buf);
-#endif
-
buf.trim();
return buf;
}
@@ -224,14 +180,14 @@ namespace
auto pwd_container = tools::password_container::prompt(verify, prompt);
if (!pwd_container)
{
- tools::fail_msg_writer() << tr("failed to read wallet password");
+ tools::fail_msg_writer() << sw::tr("failed to read wallet password");
}
return pwd_container;
}
boost::optional<tools::password_container> default_password_prompter(bool verify)
{
- return password_prompter(verify ? tr("Enter a new password for the wallet") : tr("Wallet password"), verify);
+ return password_prompter(verify ? sw::tr("Enter a new password for the wallet") : sw::tr("Wallet password"), verify);
}
inline std::string interpret_rpc_response(bool ok, const std::string& status)
@@ -309,7 +265,7 @@ namespace
}
else
{
- fail_msg_writer() << tr("invalid argument: must be either 0/1, true/false, y/n, yes/no");
+ fail_msg_writer() << sw::tr("invalid argument: must be either 0/1, true/false, y/n, yes/no");
return false;
}
}
@@ -365,18 +321,18 @@ namespace
std::string dnssec_str;
if (dnssec_valid)
{
- dnssec_str = tr("DNSSEC validation passed");
+ dnssec_str = sw::tr("DNSSEC validation passed");
}
else
{
- dnssec_str = tr("WARNING: DNSSEC validation was unsuccessful, this address may not be correct!");
+ dnssec_str = sw::tr("WARNING: DNSSEC validation was unsuccessful, this address may not be correct!");
}
std::stringstream prompt;
- prompt << tr("For URL: ") << url
+ prompt << sw::tr("For URL: ") << url
<< ", " << dnssec_str << std::endl
- << tr(" Monero Address = ") << addresses[0]
+ << sw::tr(" Monero Address = ") << addresses[0]
<< std::endl
- << tr("Is this OK? (Y/n) ")
+ << sw::tr("Is this OK? (Y/n) ")
;
// prompt the user for confirmation given the dns query and dnssec status
std::string confirm_dns_ok = input_line(prompt.str());
@@ -386,7 +342,7 @@ namespace
}
if (!command_line::is_yes(confirm_dns_ok))
{
- std::cout << tr("you have cancelled the transfer request") << std::endl;
+ std::cout << sw::tr("you have cancelled the transfer request") << std::endl;
return {};
}
return addresses[0];
@@ -407,7 +363,7 @@ namespace
uint32_t subaddr_index;
if(!epee::string_tools::get_xtype_from_string(subaddr_index, subaddr_index_str))
{
- fail_msg_writer() << tr("failed to parse index: ") << subaddr_index_str;
+ fail_msg_writer() << sw::tr("failed to parse index: ") << subaddr_index_str;
subaddr_indices.clear();
return false;
}
@@ -420,7 +376,7 @@ namespace
{
auto r = tools::parse_subaddress_lookahead(str);
if (!r)
- fail_msg_writer() << tr("invalid format for subaddress lookahead; must be <major>:<minor>");
+ fail_msg_writer() << sw::tr("invalid format for subaddress lookahead; must be <major>:<minor>");
return r;
}
@@ -433,27 +389,27 @@ namespace
}
catch (const tools::error::daemon_busy&)
{
- fail_msg_writer() << tr("daemon is busy. Please try again later.");
+ fail_msg_writer() << sw::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.");
+ fail_msg_writer() << sw::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();
+ fail_msg_writer() << sw::tr("RPC error: ") << e.what();
}
catch (const tools::error::get_outs_error &e)
{
- fail_msg_writer() << tr("failed to get random outputs to mix: ") << e.what();
+ fail_msg_writer() << sw::tr("failed to get random outputs to mix: ") << e.what();
}
catch (const tools::error::not_enough_unlocked_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");
+ fail_msg_writer() << sw::tr("Not enough money in unlocked balance");
warn_of_possible_attack = false;
}
catch (const tools::error::not_enough_money& e)
@@ -461,7 +417,7 @@ namespace
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");
+ fail_msg_writer() << sw::tr("Not enough money in unlocked balance");
warn_of_possible_attack = false;
}
catch (const tools::error::tx_not_possible& e)
@@ -471,30 +427,30 @@ namespace
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");
+ fail_msg_writer() << sw::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");
warn_of_possible_attack = false;
}
catch (const tools::error::not_enough_outs_to_mix& e)
{
auto writer = fail_msg_writer();
- writer << tr("not enough outputs for specified ring size") << " = " << (e.mixin_count() + 1) << ":";
+ writer << sw::tr("not enough outputs for specified ring size") << " = " << (e.mixin_count() + 1) << ":";
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 use") << " = " << outs_for_amount.second;
+ writer << "\n" << sw::tr("output amount") << " = " << print_money(outs_for_amount.first) << ", " << sw::tr("found outputs to use") << " = " << outs_for_amount.second;
}
- writer << tr("Please use sweep_unmixable.");
+ writer << sw::tr("Please use sweep_unmixable.");
}
catch (const tools::error::tx_not_constructed&)
- {
- fail_msg_writer() << tr("transaction was not constructed");
+ {
+ fail_msg_writer() << sw::tr("transaction was not constructed");
warn_of_possible_attack = false;
}
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();
+ fail_msg_writer() << (boost::format(sw::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;
+ fail_msg_writer() << sw::tr("Reason: ") << reason;
}
catch (const tools::error::tx_sum_overflow& e)
{
@@ -503,38 +459,38 @@ namespace
}
catch (const tools::error::zero_destination&)
{
- fail_msg_writer() << tr("one of destinations is zero");
+ fail_msg_writer() << sw::tr("one of destinations is zero");
warn_of_possible_attack = false;
}
catch (const tools::error::tx_too_big& e)
{
- fail_msg_writer() << tr("failed to find a suitable way to split transactions");
+ fail_msg_writer() << sw::tr("failed to find a suitable way to split transactions");
warn_of_possible_attack = false;
}
catch (const tools::error::transfer_error& e)
{
LOG_ERROR("unknown transfer error: " << e.to_string());
- fail_msg_writer() << tr("unknown transfer error: ") << e.what();
+ fail_msg_writer() << sw::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();
+ fail_msg_writer() << sw::tr("Multisig error: ") << e.what();
warn_of_possible_attack = false;
}
catch (const tools::error::wallet_internal_error& e)
{
LOG_ERROR("internal error: " << e.to_string());
- fail_msg_writer() << tr("internal error: ") << e.what();
+ fail_msg_writer() << sw::tr("internal error: ") << e.what();
}
catch (const std::exception& e)
{
LOG_ERROR("unexpected error: " << e.what());
- fail_msg_writer() << tr("unexpected error: ") << e.what();
+ fail_msg_writer() << sw::tr("unexpected error: ") << e.what();
}
if (warn_of_possible_attack)
- fail_msg_writer() << tr("There was an error, which could mean the node may be trying to get you to retry creating a transaction, and zero in on which outputs you own. Or it could be a bona fide error. It may be prudent to disconnect from this node, and not try to send a transaction immediately. Alternatively, connect to another node so the original node cannot correlate information.");
+ fail_msg_writer() << sw::tr("There was an error, which could mean the node may be trying to get you to retry creating a transaction, and zero in on which outputs you own. Or it could be a bona fide error. It may be prudent to disconnect from this node, and not try to send a transaction immediately. Alternatively, connect to another node so the original node cannot correlate information.");
}
bool check_file_overwrite(const std::string &filename)
@@ -544,10 +500,10 @@ namespace
{
if (boost::ends_with(filename, ".keys"))
{
- fail_msg_writer() << boost::format(tr("File %s likely stores wallet private keys! Use a different file name.")) % filename;
+ fail_msg_writer() << boost::format(sw::tr("File %s likely stores wallet private keys! Use a different file name.")) % filename;
return false;
}
- return command_line::is_yes(input_line((boost::format(tr("File %s already exists. Are you sure to overwrite it? (Y/Yes/N/No): ")) % filename).str()));
+ return command_line::is_yes(input_line((boost::format(sw::tr("File %s already exists. Are you sure to overwrite it? (Y/Yes/N/No): ")) % filename).str()));
}
return true;
}
@@ -968,7 +924,7 @@ bool simple_wallet::make_multisig(const std::vector<std::string> &args)
{
success_msg_writer() << tr("Another step is needed");
success_msg_writer() << multisig_extra_info;
- success_msg_writer() << tr("Send this multisig info to all other participants, then use finalize_multisig <info1> [<info2>...] with others' multisig info");
+ success_msg_writer() << tr("Send this multisig info to all other participants, then use exchange_multisig_keys <info1> [<info2>...] with others' multisig info");
return true;
}
}
@@ -1042,6 +998,61 @@ bool simple_wallet::finalize_multisig(const std::vector<std::string> &args)
return true;
}
+bool simple_wallet::exchange_multisig_keys(const std::vector<std::string> &args) {
+ bool ready;
+ if (m_wallet->key_on_device())
+ {
+ fail_msg_writer() << tr("command not supported by HW wallet");
+ return true;
+ }
+ if (!m_wallet->multisig(&ready))
+ {
+ fail_msg_writer() << tr("This wallet is not multisig");
+ return true;
+ }
+ if (ready)
+ {
+ fail_msg_writer() << tr("This wallet is already finalized");
+ return true;
+ }
+
+ const auto orig_pwd_container = get_and_verify_password();
+ if(orig_pwd_container == boost::none)
+ {
+ fail_msg_writer() << tr("Your original password was incorrect.");
+ return true;
+ }
+
+ if (args.size() < 2)
+ {
+ fail_msg_writer() << tr("usage: exchange_multisig_keys <multisiginfo1> [<multisiginfo2>...]");
+ return true;
+ }
+
+ try
+ {
+ std::string multisig_extra_info = m_wallet->exchange_multisig_keys(orig_pwd_container->password(), args);
+ if (!multisig_extra_info.empty())
+ {
+ message_writer() << tr("Another step is needed");
+ message_writer() << multisig_extra_info;
+ message_writer() << tr("Send this multisig info to all other participants, then use exchange_multisig_keys <info1> [<info2>...] with others' multisig info");
+ return true;
+ } else {
+ uint32_t threshold, total;
+ m_wallet->multisig(NULL, &threshold, &total);
+ success_msg_writer() << tr("Multisig wallet has been successfully created. Current wallet type: ") << threshold << "/" << total;
+ }
+ }
+ catch (const std::exception &e)
+ {
+ fail_msg_writer() << tr("Failed to perform multisig keys exchange: ") << e.what();
+ return true;
+ }
+
+ return true;
+}
+
bool simple_wallet::export_multisig(const std::vector<std::string> &args)
{
bool ready;
@@ -1629,7 +1640,7 @@ bool simple_wallet::blackball(const std::vector<std::string> &args)
uint64_t amount = std::numeric_limits<uint64_t>::max(), offset, num_offsets;
if (args.size() == 0)
{
- fail_msg_writer() << tr("usage: blackball <amount>/<offset> | <filename> [add]");
+ fail_msg_writer() << tr("usage: mark_output_spent <amount>/<offset> | <filename> [add]");
return true;
}
@@ -1707,7 +1718,7 @@ bool simple_wallet::blackball(const std::vector<std::string> &args)
}
catch (const std::exception &e)
{
- fail_msg_writer() << tr("Failed to blackball output: ") << e.what();
+ fail_msg_writer() << tr("Failed to mark output spent: ") << e.what();
}
return true;
@@ -1718,7 +1729,7 @@ bool simple_wallet::unblackball(const std::vector<std::string> &args)
std::pair<uint64_t, uint64_t> output;
if (args.size() != 1)
{
- fail_msg_writer() << tr("usage: unblackball <amount>/<offset>");
+ fail_msg_writer() << tr("usage: mark_output_unspent <amount>/<offset>");
return true;
}
@@ -1734,7 +1745,7 @@ bool simple_wallet::unblackball(const std::vector<std::string> &args)
}
catch (const std::exception &e)
{
- fail_msg_writer() << tr("Failed to unblackball output: ") << e.what();
+ fail_msg_writer() << tr("Failed to mark output unspent: ") << e.what();
}
return true;
@@ -1745,7 +1756,7 @@ bool simple_wallet::blackballed(const std::vector<std::string> &args)
std::pair<uint64_t, uint64_t> output;
if (args.size() != 1)
{
- fail_msg_writer() << tr("usage: blackballed <amount>/<offset>");
+ fail_msg_writer() << tr("usage: is_output_spent <amount>/<offset>");
return true;
}
@@ -1758,13 +1769,13 @@ bool simple_wallet::blackballed(const std::vector<std::string> &args)
try
{
if (m_wallet->is_output_blackballed(output))
- message_writer() << tr("Blackballed: ") << output.first << "/" << output.second;
+ message_writer() << tr("Spent: ") << output.first << "/" << output.second;
else
- message_writer() << tr("not blackballed: ") << output.first << "/" << output.second;
+ message_writer() << tr("Not spent: ") << output.first << "/" << output.second;
}
catch (const std::exception &e)
{
- fail_msg_writer() << tr("Failed to unblackball output: ") << e.what();
+ fail_msg_writer() << tr("Failed to check whether output is spent: ") << e.what();
}
return true;
@@ -2392,7 +2403,7 @@ simple_wallet::simple_wallet()
"store-tx-info <1|0>\n "
" Whether to store outgoing tx info (destination address, payment ID, tx secret key) for future reference.\n "
"default-ring-size <n>\n "
- " Set the default ring size (default and minimum is 5).\n "
+ " Set the default ring size (obsolete).\n "
"auto-refresh <1|0>\n "
" Whether to automatically synchronize new blocks from the daemon.\n "
"refresh-type <full|optimize-coinbase|no-coinbase|default>\n "
@@ -2473,7 +2484,15 @@ simple_wallet::simple_wallet()
m_cmd_binder.set_handler("show_transfers",
boost::bind(&simple_wallet::show_transfers, this, _1),
tr("show_transfers [in|out|pending|failed|pool|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]]"),
- tr("Show the incoming/outgoing transfers within an optional height range."));
+ // Seemingly broken formatting to compensate for the backslash before the quotes.
+ tr("Show the incoming/outgoing transfers within an optional height range.\n\n"
+ "Output format:\n"
+ "In or Coinbase: Block Number, \"block\"|\"in\", Time, Amount, Transaction Hash, Payment ID, Subaddress Index, \"-\", Note\n"
+ "Out: Block Number, \"out\", Time, Amount*, Transaction Hash, Payment ID, Fee, Destinations, Input addresses**, \"-\", Note\n"
+ "Pool: \"pool\", \"in\", Time, Amount, Transaction Hash, Payment Id, Subaddress Index, \"-\", Note, Double Spend Note\n"
+ "Pending or Failed: \"failed\"|\"pending\", \"out\", Time, Amount*, Transaction Hash, Payment ID, Fee, Input addresses**, \"-\", Note\n\n"
+ "* Excluding change and fee.\n"
+ "** Set of address indices used as inputs in this transfer."));
m_cmd_binder.set_handler("unspent_outputs",
boost::bind(&simple_wallet::unspent_outputs, this, _1),
tr("unspent_outputs [index=<N1>[,<N2>,...]] [<min_amount> [<max_amount>]]"),
@@ -2552,6 +2571,10 @@ simple_wallet::simple_wallet()
boost::bind(&simple_wallet::finalize_multisig, this, _1),
tr("finalize_multisig <string> [<string>...]"),
tr("Turn this wallet into a multisig wallet, extra step for N-1/N wallets"));
+ m_cmd_binder.set_handler("exchange_multisig_keys",
+ boost::bind(&simple_wallet::exchange_multisig_keys, this, _1),
+ tr("exchange_multisig_keys <string> [<string>...]"),
+ tr("Performs extra multisig keys exchange rounds. Needed for arbitrary M/N multisig wallets"));
m_cmd_binder.set_handler("export_multisig_info",
boost::bind(&simple_wallet::export_multisig, this, _1),
tr("export_multisig_info <filename>"),
@@ -2584,18 +2607,18 @@ simple_wallet::simple_wallet()
boost::bind(&simple_wallet::save_known_rings, this, _1),
tr("save_known_rings"),
tr("Save known rings to the shared rings database"));
- m_cmd_binder.set_handler("blackball",
+ m_cmd_binder.set_handler("mark_output_spent",
boost::bind(&simple_wallet::blackball, this, _1),
- tr("blackball <amount>/<offset> | <filename> [add]"),
- tr("Blackball output(s) so they never get selected as fake outputs in a ring"));
- m_cmd_binder.set_handler("unblackball",
+ tr("mark_output_spent <amount>/<offset> | <filename> [add]"),
+ tr("Mark output(s) as spent so they never get selected as fake outputs in a ring"));
+ m_cmd_binder.set_handler("mark_output_unspent",
boost::bind(&simple_wallet::unblackball, this, _1),
- tr("unblackball <amount>/<offset>"),
- tr("Unblackballs an output so it may get selected as a fake output in a ring"));
- m_cmd_binder.set_handler("blackballed",
+ tr("mark_output_unspent <amount>/<offset>"),
+ tr("Marks an output as unspent so it may get selected as a fake output in a ring"));
+ m_cmd_binder.set_handler("is_output_spent",
boost::bind(&simple_wallet::blackballed, this, _1),
- tr("blackballed <amount>/<offset>"),
- tr("Checks whether an output is blackballed"));
+ tr("is_output_spent <amount>/<offset>"),
+ tr("Checks whether an output is marked as spent"));
m_cmd_binder.set_handler("version",
boost::bind(&simple_wallet::version, this, _1),
tr("version"),
@@ -3017,7 +3040,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
return false;
}
crypto::secret_key viewkey;
- if (viewkey_string.hex_to_pod(unwrap(unwrap(viewkey))))
+ if (!viewkey_string.hex_to_pod(unwrap(unwrap(viewkey))))
{
fail_msg_writer() << tr("failed to parse view key secret key");
return false;
@@ -4700,8 +4723,6 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
if (!try_connect_to_daemon())
return true;
- SCOPED_WALLET_UNLOCK();
-
std::vector<std::string> local_args = args_;
std::set<uint32_t> subaddr_indices;
@@ -4921,6 +4942,8 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
}
}
+ SCOPED_WALLET_UNLOCK();
+
try
{
// figure out what tx will be necessary
@@ -6621,16 +6644,16 @@ static std::string get_human_readable_timespan(std::chrono::seconds seconds)
{
uint64_t ts = seconds.count();
if (ts < 60)
- return std::to_string(ts) + tr(" seconds");
+ return std::to_string(ts) + sw::tr(" seconds");
if (ts < 3600)
- return std::to_string((uint64_t)(ts / 60)) + tr(" minutes");
+ return std::to_string((uint64_t)(ts / 60)) + sw::tr(" minutes");
if (ts < 3600 * 24)
- return std::to_string((uint64_t)(ts / 3600)) + tr(" hours");
+ return std::to_string((uint64_t)(ts / 3600)) + sw::tr(" hours");
if (ts < 3600 * 24 * 30.5)
- return std::to_string((uint64_t)(ts / (3600 * 24))) + tr(" days");
+ return std::to_string((uint64_t)(ts / (3600 * 24))) + sw::tr(" days");
if (ts < 3600 * 24 * 365.25)
- return std::to_string((uint64_t)(ts / (3600 * 24 * 30.5))) + tr(" months");
- return tr("a long time");
+ return std::to_string((uint64_t)(ts / (3600 * 24 * 30.5))) + sw::tr(" months");
+ return sw::tr("a long time");
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
@@ -7035,12 +7058,6 @@ bool simple_wallet::run()
void simple_wallet::stop()
{
m_cmd_binder.stop_handling();
-
- m_idle_run.store(false, std::memory_order_relaxed);
- m_wallet->stop();
- // make the background refresh thread quit
- boost::unique_lock<boost::mutex> lock(m_idle_mutex);
- m_idle_cond.notify_one();
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::account(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
@@ -8140,7 +8157,7 @@ int main(int argc, char* argv[])
if (!command.empty())
{
if (!w.process_command(command))
- fail_msg_writer() << tr("Unknown command: ") << command.front();
+ fail_msg_writer() << sw::tr("Unknown command: ") << command.front();
w.stop();
w.deinit();
}
diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h
index 2d23d6248..39b715b73 100644
--- a/src/simplewallet/simplewallet.h
+++ b/src/simplewallet/simplewallet.h
@@ -210,6 +210,7 @@ namespace cryptonote
bool prepare_multisig(const std::vector<std::string>& args);
bool make_multisig(const std::vector<std::string>& args);
bool finalize_multisig(const std::vector<std::string> &args);
+ bool exchange_multisig_keys(const std::vector<std::string> &args);
bool export_multisig(const std::vector<std::string>& args);
bool import_multisig(const std::vector<std::string>& args);
bool accept_loaded_tx(const tools::wallet2::multisig_tx_set &txs);
diff --git a/src/version.cpp.in b/src/version.cpp.in
index 55075a064..ff2405811 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.13.0.1-rc"
+#define DEF_MONERO_VERSION "0.13.0.0-master"
#define DEF_MONERO_RELEASE_NAME "Beryllium Bullet"
#define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index 236928348..ddf2d74ff 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -96,6 +96,9 @@ namespace {
throw runtime_error("Multisig wallet is not finalized yet");
}
}
+ void checkMultisigWalletReady(const std::unique_ptr<tools::wallet2> &wallet) {
+ return checkMultisigWalletReady(wallet.get());
+ }
void checkMultisigWalletNotReady(const tools::wallet2* wallet) {
if (!wallet) {
@@ -111,6 +114,9 @@ namespace {
throw runtime_error("Multisig wallet is already finalized");
}
}
+ void checkMultisigWalletNotReady(const std::unique_ptr<tools::wallet2> &wallet) {
+ return checkMultisigWalletNotReady(wallet.get());
+ }
}
struct Wallet2CallbackImpl : public tools::i_wallet2_callback
@@ -375,16 +381,17 @@ WalletImpl::WalletImpl(NetworkType nettype, uint64_t kdf_rounds)
, m_synchronized(false)
, m_rebuildWalletCache(false)
, m_is_connected(false)
+ , m_refreshShouldRescan(false)
{
- m_wallet = std::make_unique<tools::wallet2>(static_cast<cryptonote::network_type>(nettype), kdf_rounds, true);
- m_history = std::make_unique<TransactionHistoryImpl>(this);
- m_wallet2Callback = std::make_unique<Wallet2CallbackImpl>(this);
- m_wallet->callback(m_wallet2Callback);
+ m_wallet.reset(new tools::wallet2(static_cast<cryptonote::network_type>(nettype), kdf_rounds, true));
+ m_history.reset(new TransactionHistoryImpl(this));
+ m_wallet2Callback.reset(new Wallet2CallbackImpl(this));
+ m_wallet->callback(m_wallet2Callback.get());
m_refreshThreadDone = false;
m_refreshEnabled = false;
- m_addressBook = std::make_unique<AddressBookImpl>(this);
- m_subaddress = std::make_unique<SubaddressImpl>(this);
- m_subaddressAccount = std::make_unique<SubaddressAccountImpl>(this);
+ m_addressBook.reset(new AddressBookImpl(this));
+ m_subaddress.reset(new SubaddressImpl(this));
+ m_subaddressAccount.reset(new SubaddressAccountImpl(this));
m_refreshIntervalMillis = DEFAULT_REFRESH_INTERVAL_MILLIS;
@@ -399,6 +406,7 @@ WalletImpl::~WalletImpl()
{
LOG_PRINT_L1(__FUNCTION__);
+ m_wallet->callback(NULL);
// Pause refresh thread - prevents refresh from starting again
pauseRefresh();
// Close wallet - stores cache and stops ongoing refresh operation
@@ -769,7 +777,7 @@ bool WalletImpl::setPassword(const std::string &password)
{
clearStatus();
try {
- m_wallet->rewrite(m_wallet->get_wallet_file(), password);
+ m_wallet->change_password(m_wallet->get_wallet_file(), m_password, password);
m_password = password;
} catch (const std::exception &e) {
setStatusError(e.what());
@@ -1004,6 +1012,20 @@ void WalletImpl::refreshAsync()
m_refreshCV.notify_one();
}
+bool WalletImpl::rescanBlockchain()
+{
+ clearStatus();
+ m_refreshShouldRescan = true;
+ doRefresh();
+ return status() == Status_Ok;
+}
+
+void WalletImpl::rescanBlockchainAsync()
+{
+ m_refreshShouldRescan = true;
+ refreshAsync();
+}
+
void WalletImpl::setAutoRefreshInterval(int millis)
{
if (millis > MAX_REFRESH_INTERVAL_MILLIS) {
@@ -1181,6 +1203,20 @@ string WalletImpl::makeMultisig(const vector<string>& info, uint32_t threshold)
return string();
}
+std::string WalletImpl::exchangeMultisigKeys(const std::vector<std::string> &info) {
+ try {
+ clearStatus();
+ checkMultisigWalletNotReady(m_wallet);
+
+ return m_wallet->exchange_multisig_keys(epee::wipeable_string(m_password), info);
+ } catch (const exception& e) {
+ LOG_ERROR("Error on exchanging multisig keys: ") << e.what();
+ setStatusError(string(tr("Failed to make multisig: ")) + e.what());
+ }
+
+ return string();
+}
+
bool WalletImpl::finalizeMultisig(const vector<string>& extraMultisigInfo) {
try {
clearStatus();
@@ -1963,6 +1999,7 @@ void WalletImpl::refreshThreadFunc()
LOG_PRINT_L3(__FUNCTION__ << ": refresh lock acquired...");
LOG_PRINT_L3(__FUNCTION__ << ": m_refreshEnabled: " << m_refreshEnabled);
LOG_PRINT_L3(__FUNCTION__ << ": m_status: " << status());
+ LOG_PRINT_L3(__FUNCTION__ << ": m_refreshShouldRescan: " << m_refreshShouldRescan);
if (m_refreshEnabled) {
LOG_PRINT_L3(__FUNCTION__ << ": refreshing...");
doRefresh();
@@ -1973,12 +2010,16 @@ void WalletImpl::refreshThreadFunc()
void WalletImpl::doRefresh()
{
+ bool rescan = m_refreshShouldRescan.exchange(false);
// synchronizing async and sync refresh calls
boost::lock_guard<boost::mutex> guarg(m_refreshMutex2);
- try {
+ do try {
+ LOG_PRINT_L3(__FUNCTION__ << ": doRefresh, rescan = "<<rescan);
// Syncing daemon and refreshing wallet simultaneously is very resource intensive.
// Disable refresh if wallet is disconnected or daemon isn't synced.
if (m_wallet->light_wallet() || daemonSynced()) {
+ if(rescan)
+ m_wallet->rescan_blockchain(false);
m_wallet->refresh(trustedDaemon());
if (!m_synchronized) {
m_synchronized = true;
@@ -1995,7 +2036,9 @@ void WalletImpl::doRefresh()
}
} catch (const std::exception &e) {
setStatusError(e.what());
- }
+ break;
+ }while(!rescan && (rescan=m_refreshShouldRescan.exchange(false))); // repeat if not rescanned and rescan was requested
+
if (m_wallet2Callback->getListener()) {
m_wallet2Callback->getListener()->refreshed();
}
@@ -2140,7 +2183,7 @@ bool WalletImpl::blackballOutputs(const std::vector<std::string> &outputs, bool
bool ret = m_wallet->set_blackballed_outputs(raw_outputs, add);
if (!ret)
{
- setStatusError(tr("Failed to set blackballed outputs"));
+ setStatusError(tr("Failed to mark outputs as spent"));
return false;
}
return true;
@@ -2162,7 +2205,7 @@ bool WalletImpl::blackballOutput(const std::string &amount, const std::string &o
bool ret = m_wallet->blackball_output(std::make_pair(raw_amount, raw_offset));
if (!ret)
{
- setStatusError(tr("Failed to blackball output"));
+ setStatusError(tr("Failed to mark output as spent"));
return false;
}
return true;
@@ -2184,7 +2227,7 @@ bool WalletImpl::unblackballOutput(const std::string &amount, const std::string
bool ret = m_wallet->unblackball_output(std::make_pair(raw_amount, raw_offset));
if (!ret)
{
- setStatusError(tr("Failed to unblackball output"));
+ setStatusError(tr("Failed to mark output as unspent"));
return false;
}
return true;
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
index 8e2af347d..b4637b8e6 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -114,6 +114,8 @@ public:
bool synchronized() const override;
bool refresh() override;
void refreshAsync() override;
+ bool rescanBlockchain() override;
+ void rescanBlockchainAsync() override;
void setAutoRefreshInterval(int millis) override;
int autoRefreshInterval() const override;
void setRefreshFromBlockHeight(uint64_t refresh_from_block_height) override;
@@ -137,6 +139,7 @@ public:
MultisigState multisig() const override;
std::string getMultisigInfo() const override;
std::string makeMultisig(const std::vector<std::string>& info, uint32_t threshold) override;
+ std::string exchangeMultisigKeys(const std::vector<std::string> &info) override;
bool finalizeMultisig(const std::vector<std::string>& extraMultisigInfo) override;
bool exportMultisigImages(std::string& images) override;
size_t importMultisigImages(const std::vector<std::string>& images) override;
@@ -231,6 +234,7 @@ private:
std::atomic<bool> m_refreshEnabled;
std::atomic<bool> m_refreshThreadDone;
std::atomic<int> m_refreshIntervalMillis;
+ std::atomic<bool> m_refreshShouldRescan;
// synchronizing refresh loop;
boost::mutex m_refreshMutex;
diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h
index 68ea26262..82627de29 100644
--- a/src/wallet/api/wallet2_api.h
+++ b/src/wallet/api/wallet2_api.h
@@ -644,6 +644,17 @@ struct Wallet
virtual void refreshAsync() = 0;
/**
+ * @brief rescanBlockchain - rescans the wallet, updating transactions from daemon
+ * @return - true if refreshed successfully;
+ */
+ virtual bool rescanBlockchain() = 0;
+
+ /**
+ * @brief rescanBlockchainAsync - rescans wallet asynchronously, starting from genesys
+ */
+ virtual void rescanBlockchainAsync() = 0;
+
+ /**
* @brief setAutoRefreshInterval - setup interval for automatic refresh.
* @param seconds - interval in millis. if zero or less than zero - automatic refresh disabled;
*/
@@ -707,6 +718,12 @@ struct Wallet
*/
virtual std::string makeMultisig(const std::vector<std::string>& info, uint32_t threshold) = 0;
/**
+ * @brief exchange_multisig_keys - provides additional key exchange round for arbitrary multisig schemes (like N-1/N, M/N)
+ * @param info - base58 encoded key derivations returned by makeMultisig or exchangeMultisigKeys function call
+ * @return new info string if more rounds required or an empty string if wallet creation is done
+ */
+ virtual std::string exchangeMultisigKeys(const std::vector<std::string> &info) = 0;
+ /**
* @brief finalizeMultisig - finalizes N - 1 / N multisig wallets creation
* @param extraMultisigInfo - wallet participants' extra multisig info obtained with makeMultisig call
* @return true if success
diff --git a/src/wallet/ringdb.cpp b/src/wallet/ringdb.cpp
index e5995e7fb..f562d6c06 100644
--- a/src/wallet/ringdb.cpp
+++ b/src/wallet/ringdb.cpp
@@ -412,13 +412,13 @@ bool ringdb::blackball_worker(const std::vector<std::pair<uint64_t, uint64_t>> &
switch (op)
{
case BLACKBALL_BLACKBALL:
- MDEBUG("Blackballing output " << output.first << "/" << output.second);
+ MDEBUG("Marking output " << output.first << "/" << output.second << " as spent");
dbr = mdb_cursor_put(cursor, &key, &data, MDB_APPENDDUP);
if (dbr == MDB_KEYEXIST)
dbr = 0;
break;
case BLACKBALL_UNBLACKBALL:
- MDEBUG("Unblackballing output " << output.first << "/" << output.second);
+ MDEBUG("Marking output " << output.first << "/" << output.second << " as unspent");
dbr = mdb_cursor_get(cursor, &key, &data, MDB_GET_BOTH);
if (dbr == 0)
dbr = mdb_cursor_del(cursor, 0);
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index e6ab10756..37b60c5d7 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -37,6 +37,8 @@
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <boost/algorithm/string/split.hpp>
+#include <boost/algorithm/string/join.hpp>
+#include <boost/range/adaptor/transformed.hpp>
#include "include_base_utils.h"
using namespace epee;
@@ -114,14 +116,17 @@ using namespace cryptonote;
#define OUTPUT_EXPORT_FILE_MAGIC "Monero output export\003"
-#define SEGREGATION_FORK_HEIGHT 1546000
-#define TESTNET_SEGREGATION_FORK_HEIGHT 1000000
-#define STAGENET_SEGREGATION_FORK_HEIGHT 1000000
+#define SEGREGATION_FORK_HEIGHT 99999999
+#define TESTNET_SEGREGATION_FORK_HEIGHT 99999999
+#define STAGENET_SEGREGATION_FORK_HEIGHT 99999999
#define SEGREGATION_FORK_VICINITY 1500 /* blocks */
#define FIRST_REFRESH_GRANULARITY 1024
+#define GAMMA_PICK_HALF_WINDOW 5
+
static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1";
+static const std::string MULTISIG_EXTRA_INFO_MAGIC = "MultisigxV1";
namespace
{
@@ -133,6 +138,42 @@ namespace
dir /= ".shared-ringdb";
return dir.string();
}
+
+ std::string pack_multisignature_keys(const std::string& prefix, const std::vector<crypto::public_key>& keys, const crypto::secret_key& signer_secret_key)
+ {
+ std::string data;
+ crypto::public_key signer;
+ CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(signer_secret_key, signer), "Failed to derive public spend key");
+ data += std::string((const char *)&signer, sizeof(crypto::public_key));
+
+ for (const auto &key: keys)
+ {
+ data += std::string((const char *)&key, sizeof(crypto::public_key));
+ }
+
+ data.resize(data.size() + sizeof(crypto::signature));
+
+ crypto::hash hash;
+ crypto::cn_fast_hash(data.data(), data.size() - sizeof(crypto::signature), hash);
+ crypto::signature &signature = *(crypto::signature*)&data[data.size() - sizeof(crypto::signature)];
+ crypto::generate_signature(hash, signer, signer_secret_key, signature);
+
+ return MULTISIG_EXTRA_INFO_MAGIC + tools::base58::encode(data);
+ }
+
+ std::vector<crypto::public_key> secret_keys_to_public_keys(const std::vector<crypto::secret_key>& keys)
+ {
+ std::vector<crypto::public_key> public_keys;
+ public_keys.reserve(keys.size());
+
+ std::transform(keys.begin(), keys.end(), std::back_inserter(public_keys), [] (const crypto::secret_key& k) -> crypto::public_key {
+ crypto::public_key p;
+ CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(k, p), "Failed to derive public spend key");
+ return p;
+ });
+
+ return public_keys;
+ }
}
namespace
@@ -258,7 +299,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
trusted_daemon = false;
if (tools::is_local_address(daemon_address))
{
- MINFO(tr("Daemon is local, assuming trusted"));
+ MINFO(tools::wallet2::tr("Daemon is local, assuming trusted"));
trusted_daemon = true;
}
}
@@ -310,7 +351,7 @@ boost::optional<tools::password_container> get_password(const boost::program_opt
THROW_WALLET_EXCEPTION_IF(!password_prompter, tools::error::wallet_internal_error, tools::wallet2::tr("no password specified; use --prompt-for-password to prompt for a password"));
- return password_prompter(verify ? tr("Enter a new password for the wallet") : tr("Wallet password"), verify);
+ return password_prompter(verify ? tools::wallet2::tr("Enter a new password for the wallet") : tools::wallet2::tr("Wallet password"), verify);
}
std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> generate_from_json(const std::string& json_file, const boost::program_options::variables_map& vm, bool unattended, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
@@ -772,10 +813,12 @@ wallet_keys_unlocker::~wallet_keys_unlocker()
wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
m_multisig_rescan_info(NULL),
m_multisig_rescan_k(NULL),
+ m_upper_transaction_weight_limit(0),
m_run(true),
m_callback(0),
m_trusted_daemon(false),
m_nettype(nettype),
+ m_multisig_rounds_passed(0),
m_always_confirm_transfers(true),
m_print_ring_members(false),
m_store_tx_info(true),
@@ -803,6 +846,9 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
m_is_initialized(false),
m_kdf_rounds(kdf_rounds),
is_old_file_format(false),
+ m_watch_only(false),
+ m_multisig(false),
+ m_multisig_threshold(0),
m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex),
m_subaddress_lookahead_major(SUBADDRESS_LOOKAHEAD_MAJOR),
m_subaddress_lookahead_minor(SUBADDRESS_LOOKAHEAD_MINOR),
@@ -2918,6 +2964,7 @@ bool wallet2::clear()
m_address_book.clear();
m_subaddresses.clear();
m_subaddress_labels.clear();
+ m_multisig_rounds_passed = 0;
return true;
}
@@ -2932,6 +2979,7 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
{
std::string account_data;
std::string multisig_signers;
+ std::string multisig_derivations;
cryptonote::account_base account = m_account;
crypto::chacha_key key;
@@ -2984,6 +3032,14 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
CHECK_AND_ASSERT_MES(r, false, "failed to serialize wallet multisig signers");
value.SetString(multisig_signers.c_str(), multisig_signers.length());
json.AddMember("multisig_signers", value, json.GetAllocator());
+
+ r = ::serialization::dump_binary(m_multisig_derivations, multisig_derivations);
+ CHECK_AND_ASSERT_MES(r, false, "failed to serialize wallet multisig derivations");
+ value.SetString(multisig_derivations.c_str(), multisig_derivations.length());
+ json.AddMember("multisig_derivations", value, json.GetAllocator());
+
+ value2.SetUint(m_multisig_rounds_passed);
+ json.AddMember("multisig_rounds_passed", value2, json.GetAllocator());
}
value2.SetInt(m_always_confirm_transfers ? 1 :0);
@@ -3156,6 +3212,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_multisig = false;
m_multisig_threshold = 0;
m_multisig_signers.clear();
+ m_multisig_rounds_passed = 0;
+ m_multisig_derivations.clear();
m_always_confirm_transfers = false;
m_print_ring_members = false;
m_default_mixin = 0;
@@ -3214,6 +3272,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_multisig = field_multisig;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, multisig_threshold, unsigned int, Uint, m_multisig, 0);
m_multisig_threshold = field_multisig_threshold;
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, multisig_rounds_passed, unsigned int, Uint, false, 0);
+ m_multisig_rounds_passed = field_multisig_rounds_passed;
if (m_multisig)
{
if (!json.HasMember("multisig_signers"))
@@ -3234,6 +3294,24 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
LOG_ERROR("Field multisig_signers found in JSON, but failed to parse");
return false;
}
+
+ //previous version of multisig does not have this field
+ if (json.HasMember("multisig_derivations"))
+ {
+ if (!json["multisig_derivations"].IsString())
+ {
+ LOG_ERROR("Field multisig_derivations found in JSON, but not String");
+ return false;
+ }
+ const char *field_multisig_derivations = json["multisig_derivations"].GetString();
+ std::string multisig_derivations = std::string(field_multisig_derivations, field_multisig_derivations + json["multisig_derivations"].GetStringLength());
+ r = ::serialization::parse_binary(multisig_derivations, m_multisig_derivations);
+ if (!r)
+ {
+ LOG_ERROR("Field multisig_derivations found in JSON, but failed to parse");
+ return false;
+ }
+ }
}
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, always_confirm_transfers, int, Int, false, true);
m_always_confirm_transfers = field_always_confirm_transfers;
@@ -3541,6 +3619,7 @@ bool wallet2::query_device(hw::device::device_type& device_type, const std::stri
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]);
+ device_type = hw::device::device_type::SOFTWARE;
// The contents should be JSON if the wallet follows the new format.
if (json.Parse(account_data.c_str()).HasParseError())
{
@@ -3869,12 +3948,12 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
CHECK_AND_ASSERT_THROW_MES(!view_keys.empty(), "empty view keys");
CHECK_AND_ASSERT_THROW_MES(view_keys.size() == spend_keys.size(), "Mismatched view/spend key sizes");
CHECK_AND_ASSERT_THROW_MES(threshold > 1 && threshold <= spend_keys.size() + 1, "Invalid threshold");
- CHECK_AND_ASSERT_THROW_MES(threshold == spend_keys.size() || threshold == spend_keys.size() + 1, "Unsupported threshold case");
std::string extra_multisig_info;
- crypto::hash hash;
-
- clear();
+ std::vector<crypto::secret_key> multisig_keys;
+ rct::key spend_pkey = rct::identity();
+ rct::key spend_skey;
+ std::vector<crypto::public_key> multisig_signers;
// decrypt keys
epee::misc_utils::auto_scope_leave_caller keys_reencryptor;
@@ -3887,43 +3966,78 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this, chacha_key]() { m_account.encrypt_keys(chacha_key); m_account.decrypt_viewkey(chacha_key); });
}
- MINFO("Creating spend key...");
- std::vector<crypto::secret_key> multisig_keys;
- rct::key spend_pkey, spend_skey;
+ // In common multisig scheme there are 4 types of key exchange rounds:
+ // 1. First round is exchange of view secret keys and public spend keys.
+ // 2. Middle round is exchange of derivations: Ki = b * Mj, where b - spend secret key,
+ // M - public multisig key (in first round it equals to public spend key), K - new public multisig key.
+ // 3. Secret spend establishment round sets your secret multisig keys as follows: kl = H(Ml), where M - is *your* public multisig key,
+ // k - secret multisig key used to sign transactions. k and M are sets of keys, of course.
+ // And secret spend key as the sum of all participant's secret multisig keys
+ // 4. Last round establishes multisig wallet's public spend key. Participants exchange their public multisig keys
+ // and calculate common spend public key as sum of all unique participants' public multisig keys.
+ // Note that N/N scheme has only first round. N-1/N has 2 rounds: first and last. Common M/N has all 4 rounds.
+
+ // IMPORTANT: wallet's public spend key is not equal to secret_spend_key * G!
+ // Wallet's public spend key is the sum of unique public multisig keys of all participants.
+ // secret_spend_key * G = public signer key
+
if (threshold == spend_keys.size() + 1)
{
+ // In N / N case we only need to do one round and calculate secret multisig keys and new secret spend key
+ MINFO("Creating spend key...");
+
+ // Calculates all multisig keys and spend key
cryptonote::generate_multisig_N_N(get_account().get_keys(), spend_keys, multisig_keys, spend_skey, spend_pkey);
+
+ // Our signer key is b * G, where b is secret spend key.
+ multisig_signers = spend_keys;
+ multisig_signers.push_back(get_multisig_signer_public_key(get_account().get_keys().m_spend_secret_key));
}
- else if (threshold == spend_keys.size())
+ else
{
- cryptonote::generate_multisig_N1_N(get_account().get_keys(), spend_keys, multisig_keys, spend_skey, spend_pkey);
+ // We just got public spend keys of all participants and deriving multisig keys (set of Mi = b * Bi).
+ // note that derivations are public keys as DH exchange suppose it to be
+ auto derivations = cryptonote::generate_multisig_derivations(get_account().get_keys(), spend_keys);
- // We need an extra step, so we package all the composite public keys
- // we know about, and make a signed string out of them
- std::string data;
- crypto::public_key signer;
- CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(rct::rct2sk(spend_skey), signer), "Failed to derive public spend key");
- data += std::string((const char *)&signer, sizeof(crypto::public_key));
+ spend_pkey = rct::identity();
+ multisig_signers = std::vector<crypto::public_key>(spend_keys.size() + 1, crypto::null_pkey);
- for (const auto &msk: multisig_keys)
+ if (threshold == spend_keys.size())
{
- rct::key pmsk = rct::scalarmultBase(rct::sk2rct(msk));
- data += std::string((const char *)&pmsk, sizeof(crypto::public_key));
+ // N - 1 / N case
+
+ // We need an extra step, so we package all the composite public keys
+ // we know about, and make a signed string out of them
+ MINFO("Creating spend key...");
+
+ // Calculating set of our secret multisig keys as follows: mi = H(Mi),
+ // where mi - secret multisig key, Mi - others' participants public multisig key
+ multisig_keys = cryptonote::calculate_multisig_keys(derivations);
+
+ // calculating current participant's spend secret key as sum of all secret multisig keys for current participant.
+ // IMPORTANT: participant's secret spend key is not an entire wallet's secret spend!
+ // Entire wallet's secret spend is sum of all unique secret multisig keys
+ // among all of participants and is not held by anyone!
+ spend_skey = rct::sk2rct(cryptonote::calculate_multisig_signer_key(multisig_keys));
+
+ // Preparing data for the last round to calculate common public spend key. The data contains public multisig keys.
+ extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, secret_keys_to_public_keys(multisig_keys), rct::rct2sk(spend_skey));
}
+ else
+ {
+ // M / N case
+ MINFO("Preparing keys for next exchange round...");
- data.resize(data.size() + sizeof(crypto::signature));
- crypto::cn_fast_hash(data.data(), data.size() - sizeof(signature), hash);
- crypto::signature &signature = *(crypto::signature*)&data[data.size() - sizeof(crypto::signature)];
- crypto::generate_signature(hash, signer, rct::rct2sk(spend_skey), signature);
+ // Preparing data for middle round - packing new public multisig keys to exchage with others.
+ extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, derivations, m_account.get_keys().m_spend_secret_key);
+ spend_skey = rct::sk2rct(m_account.get_keys().m_spend_secret_key);
- extra_multisig_info = std::string("MultisigxV1") + tools::base58::encode(data);
- }
- else
- {
- CHECK_AND_ASSERT_THROW_MES(false, "Unsupported threshold case");
+ // Need to store middle keys to be able to proceed in case of wallet shutdown.
+ m_multisig_derivations = derivations;
+ }
}
- // the multisig view key is shared by all, make one all can derive
+ clear();
MINFO("Creating view key...");
crypto::secret_key view_skey = cryptonote::generate_multisig_view_secret_key(get_account().get_keys().m_view_secret_key, view_keys);
@@ -3935,18 +4049,10 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
m_account_public_address = m_account.get_keys().m_account_address;
m_watch_only = false;
m_multisig = true;
- m_multisig_threshold = threshold;
m_key_device_type = hw::device::device_type::SOFTWARE;
-
- if (threshold == spend_keys.size() + 1)
- {
- m_multisig_signers = spend_keys;
- m_multisig_signers.push_back(get_multisig_signer_public_key());
- }
- else
- {
- m_multisig_signers = std::vector<crypto::public_key>(spend_keys.size() + 1, crypto::null_pkey);
- }
+ m_multisig_threshold = threshold;
+ m_multisig_signers = multisig_signers;
+ ++m_multisig_rounds_passed;
// re-encrypt keys
keys_reencryptor = epee::misc_utils::auto_scope_leave_caller();
@@ -3961,13 +4067,147 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
return extra_multisig_info;
}
-std::string wallet2::make_multisig(const epee::wipeable_string &password,
- const std::vector<std::string> &info,
- uint32_t threshold)
+std::string wallet2::exchange_multisig_keys(const epee::wipeable_string &password,
+ const std::vector<std::string> &info)
+{
+ THROW_WALLET_EXCEPTION_IF(info.empty(),
+ error::wallet_internal_error, "Empty multisig info");
+
+ if (info[0].substr(0, MULTISIG_EXTRA_INFO_MAGIC.size()) != MULTISIG_EXTRA_INFO_MAGIC)
+ {
+ THROW_WALLET_EXCEPTION_IF(false,
+ error::wallet_internal_error, "Unsupported info string");
+ }
+
+ std::vector<crypto::public_key> signers;
+ std::unordered_set<crypto::public_key> pkeys;
+
+ THROW_WALLET_EXCEPTION_IF(!unpack_extra_multisig_info(info, signers, pkeys),
+ error::wallet_internal_error, "Bad extra multisig info");
+
+ return exchange_multisig_keys(password, pkeys, signers);
+}
+
+std::string wallet2::exchange_multisig_keys(const epee::wipeable_string &password,
+ std::unordered_set<crypto::public_key> derivations,
+ std::vector<crypto::public_key> signers)
+{
+ CHECK_AND_ASSERT_THROW_MES(!derivations.empty(), "empty pkeys");
+ CHECK_AND_ASSERT_THROW_MES(!signers.empty(), "empty signers");
+
+ bool ready = false;
+ CHECK_AND_ASSERT_THROW_MES(multisig(&ready), "The wallet is not multisig");
+ CHECK_AND_ASSERT_THROW_MES(!ready, "Multisig wallet creation process has already been finished");
+
+ // keys are decrypted
+ epee::misc_utils::auto_scope_leave_caller keys_reencryptor;
+ if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
+ {
+ crypto::chacha_key chacha_key;
+ crypto::generate_chacha_key(password.data(), password.size(), chacha_key, m_kdf_rounds);
+ m_account.encrypt_viewkey(chacha_key);
+ m_account.decrypt_keys(chacha_key);
+ keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this, chacha_key]() { m_account.encrypt_keys(chacha_key); m_account.decrypt_viewkey(chacha_key); });
+ }
+
+ if (m_multisig_rounds_passed == multisig_rounds_required(m_multisig_signers.size(), m_multisig_threshold) - 1)
+ {
+ // the last round is passed and we have to calculate spend public key
+ // add ours if not included
+ crypto::public_key local_signer = get_multisig_signer_public_key();
+
+ if (std::find(signers.begin(), signers.end(), local_signer) == signers.end())
+ {
+ signers.push_back(local_signer);
+ for (const auto &msk: get_account().get_multisig_keys())
+ {
+ derivations.insert(rct::rct2pk(rct::scalarmultBase(rct::sk2rct(msk))));
+ }
+ }
+
+ CHECK_AND_ASSERT_THROW_MES(signers.size() == m_multisig_signers.size(), "Bad signers size");
+
+ // Summing all of unique public multisig keys to calculate common public spend key
+ crypto::public_key spend_public_key = cryptonote::generate_multisig_M_N_spend_public_key(std::vector<crypto::public_key>(derivations.begin(), derivations.end()));
+ m_account_public_address.m_spend_public_key = spend_public_key;
+ m_account.finalize_multisig(spend_public_key);
+
+ m_multisig_signers = signers;
+ std::sort(m_multisig_signers.begin(), m_multisig_signers.end(), [](const crypto::public_key &e0, const crypto::public_key &e1){ return memcmp(&e0, &e1, sizeof(e0)); });
+
+ ++m_multisig_rounds_passed;
+ m_multisig_derivations.clear();
+
+ // keys are encrypted again
+ keys_reencryptor = epee::misc_utils::auto_scope_leave_caller();
+
+ if (!m_wallet_file.empty())
+ {
+ bool r = store_keys(m_keys_file, password, false);
+ THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
+
+ if (boost::filesystem::exists(m_wallet_file + ".address.txt"))
+ {
+ r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype));
+ if(!r) MERROR("String with address text not saved");
+ }
+ }
+
+ m_subaddresses.clear();
+ m_subaddress_labels.clear();
+ add_subaddress_account(tr("Primary account"));
+
+ if (!m_wallet_file.empty())
+ store();
+
+ return {};
+ }
+
+ // Below are either middle or secret spend key establishment rounds
+
+ for (const auto& key: m_multisig_derivations)
+ derivations.erase(key);
+
+ // Deriving multisig keys (set of Mi = b * Bi) according to DH from other participants' multisig keys.
+ auto new_derivations = cryptonote::generate_multisig_derivations(get_account().get_keys(), std::vector<crypto::public_key>(derivations.begin(), derivations.end()));
+
+ std::string extra_multisig_info;
+ if (m_multisig_rounds_passed == multisig_rounds_required(m_multisig_signers.size(), m_multisig_threshold) - 2) // next round is last
+ {
+ // Next round is last therefore we are performing secret spend establishment round as described above.
+ MINFO("Creating spend key...");
+
+ // Calculating our secret multisig keys by hashing our public multisig keys.
+ auto multisig_keys = cryptonote::calculate_multisig_keys(std::vector<crypto::public_key>(new_derivations.begin(), new_derivations.end()));
+ // And summing it to get personal secret spend key
+ crypto::secret_key spend_skey = cryptonote::calculate_multisig_signer_key(multisig_keys);
+
+ m_account.make_multisig(m_account.get_keys().m_view_secret_key, spend_skey, rct::rct2pk(rct::identity()), multisig_keys);
+
+ // Packing public multisig keys to exchange with others and calculate common public spend key in the last round
+ extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, secret_keys_to_public_keys(multisig_keys), spend_skey);
+ }
+ else
+ {
+ // This is just middle round
+ MINFO("Preparing keys for next exchange round...");
+ extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, new_derivations, m_account.get_keys().m_spend_secret_key);
+ m_multisig_derivations = new_derivations;
+ }
+
+ ++m_multisig_rounds_passed;
+
+ create_keys_file(m_wallet_file, false, password, boost::filesystem::exists(m_wallet_file + ".address.txt"));
+ return extra_multisig_info;
+}
+
+void wallet2::unpack_multisig_info(const std::vector<std::string>& info,
+ std::vector<crypto::public_key> &public_keys,
+ std::vector<crypto::secret_key> &secret_keys) const
{
// parse all multisig info
- std::vector<crypto::secret_key> secret_keys(info.size());
- std::vector<crypto::public_key> public_keys(info.size());
+ public_keys.resize(info.size());
+ secret_keys.resize(info.size());
for (size_t i = 0; i < info.size(); ++i)
{
THROW_WALLET_EXCEPTION_IF(!verify_multisig_info(info[i], secret_keys[i], public_keys[i]),
@@ -4011,75 +4251,51 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
"Found local spend public key, but not local view secret key - something very weird");
}
}
+}
+std::string wallet2::make_multisig(const epee::wipeable_string &password,
+ const std::vector<std::string> &info,
+ uint32_t threshold)
+{
+ std::vector<crypto::secret_key> secret_keys(info.size());
+ std::vector<crypto::public_key> public_keys(info.size());
+ unpack_multisig_info(info, public_keys, secret_keys);
return make_multisig(password, secret_keys, public_keys, threshold);
}
bool wallet2::finalize_multisig(const epee::wipeable_string &password, std::unordered_set<crypto::public_key> pkeys, std::vector<crypto::public_key> signers)
{
- CHECK_AND_ASSERT_THROW_MES(!pkeys.empty(), "empty pkeys");
-
- // keys are decrypted
- epee::misc_utils::auto_scope_leave_caller keys_reencryptor;
- if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
- {
- crypto::chacha_key chacha_key;
- crypto::generate_chacha_key(password.data(), password.size(), chacha_key, m_kdf_rounds);
- m_account.encrypt_viewkey(chacha_key);
- m_account.decrypt_keys(chacha_key);
- keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this, chacha_key]() { m_account.encrypt_keys(chacha_key); m_account.decrypt_viewkey(chacha_key); });
- }
+ exchange_multisig_keys(password, pkeys, signers);
+ return true;
+}
- // add ours if not included
- crypto::public_key local_signer;
- CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(get_account().get_keys().m_spend_secret_key, local_signer),
- "Failed to derive public spend key");
- if (std::find(signers.begin(), signers.end(), local_signer) == signers.end())
+bool wallet2::unpack_extra_multisig_info(const std::vector<std::string>& info,
+ std::vector<crypto::public_key> &signers,
+ std::unordered_set<crypto::public_key> &pkeys) const
+{
+ // parse all multisig info
+ signers.resize(info.size(), crypto::null_pkey);
+ for (size_t i = 0; i < info.size(); ++i)
{
- signers.push_back(local_signer);
- for (const auto &msk: get_account().get_multisig_keys())
- {
- pkeys.insert(rct::rct2pk(rct::scalarmultBase(rct::sk2rct(msk))));
- }
+ if (!verify_extra_multisig_info(info[i], pkeys, signers[i]))
+ {
+ return false;
+ }
}
- CHECK_AND_ASSERT_THROW_MES(signers.size() == m_multisig_signers.size(), "Bad signers size");
-
- crypto::public_key spend_public_key = cryptonote::generate_multisig_N1_N_spend_public_key(std::vector<crypto::public_key>(pkeys.begin(), pkeys.end()));
- m_account_public_address.m_spend_public_key = spend_public_key;
- m_account.finalize_multisig(spend_public_key);
-
- m_multisig_signers = signers;
- std::sort(m_multisig_signers.begin(), m_multisig_signers.end(), [](const crypto::public_key &e0, const crypto::public_key &e1){ return memcmp(&e0, &e1, sizeof(e0)); });
-
- // keys are encrypted again
- keys_reencryptor = epee::misc_utils::auto_scope_leave_caller();
-
- create_keys_file(m_wallet_file, false, password, boost::filesystem::exists(m_wallet_file + ".address.txt"));
-
- m_subaddresses.clear();
- m_subaddress_labels.clear();
- add_subaddress_account(tr("Primary account"));
-
- if (!m_wallet_file.empty())
- store();
-
return true;
}
bool wallet2::finalize_multisig(const epee::wipeable_string &password, const std::vector<std::string> &info)
{
- // parse all multisig info
std::unordered_set<crypto::public_key> public_keys;
- std::vector<crypto::public_key> signers(info.size(), crypto::null_pkey);
- for (size_t i = 0; i < info.size(); ++i)
+ std::vector<crypto::public_key> signers;
+ if (!unpack_extra_multisig_info(info, signers, public_keys))
{
- if (!verify_extra_multisig_info(info[i], public_keys, signers[i]))
- {
- MERROR("Bad multisig info");
- return false;
- }
+ MERROR("Bad multisig info");
+ return false;
}
+
return finalize_multisig(password, public_keys, signers);
}
@@ -4142,14 +4358,13 @@ bool wallet2::verify_multisig_info(const std::string &data, crypto::secret_key &
bool wallet2::verify_extra_multisig_info(const std::string &data, std::unordered_set<crypto::public_key> &pkeys, crypto::public_key &signer)
{
- const size_t header_len = strlen("MultisigxV1");
- if (data.size() < header_len || data.substr(0, header_len) != "MultisigxV1")
+ if (data.size() < MULTISIG_EXTRA_INFO_MAGIC.size() || data.substr(0, MULTISIG_EXTRA_INFO_MAGIC.size()) != MULTISIG_EXTRA_INFO_MAGIC)
{
MERROR("Multisig info header check error");
return false;
}
std::string decoded;
- if (!tools::base58::decode(data.substr(header_len), decoded))
+ if (!tools::base58::decode(data.substr(MULTISIG_EXTRA_INFO_MAGIC.size()), decoded))
{
MERROR("Multisig info decoding error");
return false;
@@ -5357,10 +5572,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pendin
rct::RangeProofType range_proof_type = rct::RangeProofBorromean;
if (sd.use_bulletproofs)
{
- range_proof_type = rct::RangeProofBulletproof;
- for (const rct::Bulletproof &proof: ptx.tx.rct_signatures.p.bulletproofs)
- if (proof.V.size() > 1)
- range_proof_type = rct::RangeProofPaddedBulletproof;
+ range_proof_type = rct::RangeProofPaddedBulletproof;
}
crypto::secret_key tx_key;
std::vector<crypto::secret_key> additional_tx_keys;
@@ -5404,6 +5616,10 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pendin
ptx.construction_data = sd;
txs.push_back(ptx);
+
+ // add tx keys only to ptx
+ txs.back().tx_key = tx_key;
+ txs.back().additional_tx_keys = additional_tx_keys;
}
// add key images
@@ -6589,10 +6805,29 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
error::get_output_distribution, "Decreasing offsets in rct distribution: " +
std::to_string(block_offset) + ": " + std::to_string(rct_offsets[block_offset]) + ", " +
std::to_string(block_offset + 1) + ": " + std::to_string(rct_offsets[block_offset + 1]));
- uint64_t n_rct = rct_offsets[block_offset + 1] - rct_offsets[block_offset];
+ uint64_t first_block_offset = block_offset, last_block_offset = block_offset;
+ for (size_t half_window = 0; half_window < GAMMA_PICK_HALF_WINDOW; ++half_window)
+ {
+ // end when we have a non empty block
+ uint64_t cum0 = first_block_offset > 0 ? rct_offsets[first_block_offset] - rct_offsets[first_block_offset - 1] : rct_offsets[0];
+ if (cum0 > 1)
+ break;
+ uint64_t cum1 = last_block_offset > 0 ? rct_offsets[last_block_offset] - rct_offsets[last_block_offset - 1] : rct_offsets[0];
+ if (cum1 > 1)
+ break;
+ if (first_block_offset == 0 && last_block_offset >= rct_offsets.size() - 2)
+ break;
+ // expand up to bounds
+ if (first_block_offset > 0)
+ --first_block_offset;
+ if (last_block_offset < rct_offsets.size() - 1)
+ ++last_block_offset;
+ }
+ const uint64_t n_rct = rct_offsets[last_block_offset] - (first_block_offset == 0 ? 0 : rct_offsets[first_block_offset - 1]);
if (n_rct == 0)
return rct_offsets[block_offset] ? rct_offsets[block_offset] - 1 : 0;
- return rct_offsets[block_offset] + crypto::rand<uint64_t>() % n_rct;
+ MDEBUG("Picking 1/" << n_rct << " in " << (last_block_offset - first_block_offset + 1) << " blocks centered around " << block_offset);
+ return rct_offsets[first_block_offset] + crypto::rand<uint64_t>() % n_rct;
};
size_t num_selected_transfers = 0;
@@ -6762,6 +6997,8 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
LOG_PRINT_L1("Selecting real output: " << td.m_global_output_index << " for " << print_money(amount));
}
+ std::unordered_map<const char*, std::set<uint64_t>> picks;
+
// while we still need more mixins
uint64_t num_usable_outs = num_outs;
bool allow_blackballed = false;
@@ -6776,7 +7013,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
// outputs, we still need to reach the minimum ring size)
if (allow_blackballed)
break;
- MINFO("Not enough non blackballed outputs, we'll allow blackballed ones");
+ MINFO("Not enough output not marked as spent, we'll allow outputs marked as spent");
allow_blackballed = true;
num_usable_outs = num_outs;
}
@@ -6860,11 +7097,15 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
}
seen_indices.emplace(i);
- LOG_PRINT_L2("picking " << i << " as " << type);
+ picks[type].insert(i);
req.outputs.push_back({amount, i});
++num_found;
}
+ for (const auto &pick: picks)
+ MDEBUG("picking " << pick.first << " outputs: " <<
+ boost::join(pick.second | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " "));
+
// if we had enough unusable outputs, we might fall off here and still
// have too few outputs, so we stuff with one to keep counts good, and
// we'll error out later
@@ -6880,8 +7121,15 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
[](const get_outputs_out &a, const get_outputs_out &b) { return a.index < b.index; });
}
- for (auto i: req.outputs)
- LOG_PRINT_L1("asking for output " << i.index << " for " << print_money(i.amount));
+ if (ELPP->vRegistry()->allowed(el::Level::Debug, MONERO_DEFAULT_LOG_CATEGORY))
+ {
+ std::map<uint64_t, std::set<uint64_t>> outs;
+ for (const auto &i: req.outputs)
+ outs[i.amount].insert(i.index);
+ for (const auto &o: outs)
+ MDEBUG("asking for outputs with amount " << print_money(o.first) << ": " <<
+ boost::join(o.second | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " "));
+ }
// get the keys for those
m_daemon_rpc_mutex.lock();
@@ -10339,7 +10587,7 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
+ boost::lexical_cast<std::string>(signed_key_images.size()) + ", key image " + epee::string_tools::pod_to_hex(key_image));
THROW_WALLET_EXCEPTION_IF(!crypto::check_ring_signature((const crypto::hash&)key_image, key_image, pkeys, &signature),
- error::wallet_internal_error, "Signature check failed: input " + boost::lexical_cast<std::string>(n) + "/"
+ error::signature_check_failed, boost::lexical_cast<std::string>(n) + "/"
+ boost::lexical_cast<std::string>(signed_key_images.size()) + ", key image " + epee::string_tools::pod_to_hex(key_image)
+ ", signature " + epee::string_tools::pod_to_hex(signature) + ", pubkey " + epee::string_tools::pod_to_hex(*pkeys[0]));
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 7857f36f1..680196f01 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -574,6 +574,14 @@ namespace tools
const std::vector<crypto::secret_key> &view_keys,
const std::vector<crypto::public_key> &spend_keys,
uint32_t threshold);
+ std::string exchange_multisig_keys(const epee::wipeable_string &password,
+ const std::vector<std::string> &info);
+ /*!
+ * \brief Any but first round of keys exchange
+ */
+ std::string exchange_multisig_keys(const epee::wipeable_string &password,
+ std::unordered_set<crypto::public_key> pkeys,
+ std::vector<crypto::public_key> signers);
/*!
* \brief Finalizes creation of a multisig wallet
*/
@@ -1248,6 +1256,12 @@ namespace tools
bool get_rct_distribution(uint64_t &start_height, std::vector<uint64_t> &distribution);
uint64_t get_segregation_fork_height() const;
+ void unpack_multisig_info(const std::vector<std::string>& info,
+ std::vector<crypto::public_key> &public_keys,
+ std::vector<crypto::secret_key> &secret_keys) const;
+ bool unpack_extra_multisig_info(const std::vector<std::string>& info,
+ std::vector<crypto::public_key> &signers,
+ std::unordered_set<crypto::public_key> &pkeys) const;
void cache_tx_data(const cryptonote::transaction& tx, const crypto::hash &txid, tx_cache_data &tx_cache_data) const;
@@ -1298,6 +1312,9 @@ namespace tools
bool m_multisig; /*!< if > 1 spend secret key will not match spend public key */
uint32_t m_multisig_threshold;
std::vector<crypto::public_key> m_multisig_signers;
+ //in case of general M/N multisig wallet we should perform N - M + 1 key exchange rounds and remember how many rounds are passed.
+ uint32_t m_multisig_rounds_passed;
+ std::vector<crypto::public_key> m_multisig_derivations;
bool m_always_confirm_transfers;
bool m_print_ring_members;
bool m_store_tx_info; /*!< request txkey to be returned in RPC, and store in the wallet cache file */
diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h
index bc518d04a..b3141985d 100644
--- a/src/wallet/wallet_errors.h
+++ b/src/wallet/wallet_errors.h
@@ -72,6 +72,7 @@ namespace tools
// tx_parse_error
// get_tx_pool_error
// out_of_hashchain_bounds_error
+ // signature_check_failed
// transfer_error *
// get_outs_general_error
// not_enough_unlocked_money
@@ -418,6 +419,14 @@ namespace tools
std::string to_string() const { return refresh_error::to_string(); }
};
//----------------------------------------------------------------------------------------------------
+ struct signature_check_failed : public wallet_logic_error
+ {
+ explicit signature_check_failed(std::string&& loc, const std::string& message)
+ : wallet_logic_error(std::move(loc), "Signature check failed " + message)
+ {
+ }
+ };
+ //----------------------------------------------------------------------------------------------------
struct transfer_error : public wallet_logic_error
{
protected:
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 2b5ef3157..5e6100dfd 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -981,6 +981,8 @@ namespace tools
for (auto &ptx: ptxs)
{
res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
+ if (req.get_tx_keys)
+ res.tx_key_list.push_back(epee::string_tools::pod_to_hex(ptx.tx_key));
}
if (req.export_raw)
@@ -994,6 +996,171 @@ namespace tools
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_describe_transfer(const wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::response& res, epee::json_rpc::error& er)
+ {
+ if (!m_wallet) return not_open(er);
+ if (m_restricted)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_DENIED;
+ er.message = "Command unavailable in restricted mode.";
+ return false;
+ }
+ if (m_wallet->key_on_device())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "command not supported by HW wallet";
+ return false;
+ }
+ if(m_wallet->watch_only())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WATCH_ONLY;
+ er.message = "command not supported by watch-only wallet";
+ return false;
+ }
+
+ tools::wallet2::unsigned_tx_set exported_txs;
+ try
+ {
+ cryptonote::blobdata blob;
+ if (!epee::string_tools::parse_hexstr_to_binbuff(req.unsigned_txset, blob))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_HEX;
+ er.message = "Failed to parse hex.";
+ return false;
+ }
+ if(!m_wallet->parse_unsigned_tx_from_str(blob, exported_txs))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA;
+ er.message = "cannot load unsigned_txset";
+ return false;
+ }
+ }
+ catch (const std::exception &e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA;
+ er.message = "failed to parse unsigned transfers: " + std::string(e.what());
+ return false;
+ }
+
+ std::vector<tools::wallet2::pending_tx> ptx;
+ try
+ {
+ // gather info to ask the user
+ std::unordered_map<cryptonote::account_public_address, std::pair<std::string, uint64_t>> dests;
+ int first_known_non_zero_change_index = -1;
+ for (size_t n = 0; n < exported_txs.txes.size(); ++n)
+ {
+ const tools::wallet2::tx_construction_data &cd = exported_txs.txes[n];
+ res.desc.push_back({0, 0, std::numeric_limits<uint32_t>::max(), 0, {}, "", 0, "", 0, 0, ""});
+ wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::transfer_description &desc = res.desc.back();
+
+ std::vector<cryptonote::tx_extra_field> tx_extra_fields;
+ bool has_encrypted_payment_id = false;
+ crypto::hash8 payment_id8 = crypto::null_hash8;
+ if (cryptonote::parse_tx_extra(cd.extra, tx_extra_fields))
+ {
+ cryptonote::tx_extra_nonce extra_nonce;
+ if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
+ {
+ crypto::hash payment_id;
+ if(cryptonote::get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
+ {
+ desc.payment_id = epee::string_tools::pod_to_hex(payment_id8);
+ has_encrypted_payment_id = true;
+ }
+ else if (cryptonote::get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
+ {
+ desc.payment_id = epee::string_tools::pod_to_hex(payment_id);
+ }
+ }
+ }
+
+ for (size_t s = 0; s < cd.sources.size(); ++s)
+ {
+ desc.amount_in += cd.sources[s].amount;
+ size_t ring_size = cd.sources[s].outputs.size();
+ if (ring_size < desc.ring_size)
+ desc.ring_size = ring_size;
+ }
+ for (size_t d = 0; d < cd.splitted_dsts.size(); ++d)
+ {
+ const cryptonote::tx_destination_entry &entry = cd.splitted_dsts[d];
+ std::string address = cryptonote::get_account_address_as_str(m_wallet->nettype(), entry.is_subaddress, entry.addr);
+ if (has_encrypted_payment_id && !entry.is_subaddress)
+ address = cryptonote::get_account_integrated_address_as_str(m_wallet->nettype(), entry.addr, payment_id8);
+ auto i = dests.find(entry.addr);
+ if (i == dests.end())
+ dests.insert(std::make_pair(entry.addr, std::make_pair(address, entry.amount)));
+ else
+ i->second.second += entry.amount;
+ desc.amount_out += entry.amount;
+ }
+ if (cd.change_dts.amount > 0)
+ {
+ auto it = dests.find(cd.change_dts.addr);
+ if (it == dests.end())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA;
+ er.message = "Claimed change does not go to a paid address";
+ return false;
+ }
+ if (it->second.second < cd.change_dts.amount)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA;
+ er.message = "Claimed change is larger than payment to the change address";
+ return false;
+ }
+ if (cd.change_dts.amount > 0)
+ {
+ if (first_known_non_zero_change_index == -1)
+ first_known_non_zero_change_index = n;
+ const tools::wallet2::tx_construction_data &cdn = exported_txs.txes[first_known_non_zero_change_index];
+ if (memcmp(&cd.change_dts.addr, &cdn.change_dts.addr, sizeof(cd.change_dts.addr)))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA;
+ er.message = "Change goes to more than one address";
+ return false;
+ }
+ }
+ desc.change_amount += cd.change_dts.amount;
+ it->second.second -= cd.change_dts.amount;
+ if (it->second.second == 0)
+ dests.erase(cd.change_dts.addr);
+ }
+
+ size_t n_dummy_outputs = 0;
+ for (auto i = dests.begin(); i != dests.end(); )
+ {
+ if (i->second.second > 0)
+ {
+ desc.recipients.push_back({i->second.first, i->second.second});
+ }
+ else
+ ++desc.dummy_outputs;
+ ++i;
+ }
+
+ if (desc.change_amount > 0)
+ {
+ const tools::wallet2::tx_construction_data &cd0 = exported_txs.txes[0];
+ desc.change_address = get_account_address_as_str(m_wallet->nettype(), cd0.subaddr_account > 0, cd0.change_dts.addr);
+ }
+
+ desc.fee = desc.amount_in - desc.amount_out;
+ desc.unlock_time = cd.unlock_time;
+ desc.extra = epee::to_hex::string({cd.extra.data(), cd.extra.size()});
+ }
+ }
+ catch (const std::exception &e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA;
+ er.message = "failed to parse unsigned transfers";
+ return false;
+ }
+
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_submit_transfer(const wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
@@ -1577,6 +1744,7 @@ namespace tools
epee::wipeable_string seed;
if (!m_wallet->get_seed(seed))
{
+ er.code = WALLET_RPC_ERROR_CODE_NON_DETERMINISTIC;
er.message = "The wallet is non-deterministic. Cannot display seed.";
return false;
}
@@ -2822,8 +2990,7 @@ namespace tools
{
try
{
- m_wallet->rewrite(m_wallet->get_wallet_file(), req.new_password);
- m_wallet->store();
+ m_wallet->change_password(m_wallet->get_wallet_file(), req.old_password, req.new_password);
LOG_PRINT_L0("Wallet password changed.");
}
catch (const std::exception& e)
@@ -2906,6 +3073,11 @@ namespace tools
er.code = WALLET_RPC_ERROR_CODE_ADDRESS_INDEX_OUT_OF_BOUNDS;
er.message = e.what();
}
+ catch (const error::signature_check_failed& e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_SIGNATURE;
+ er.message = e.what();
+ }
catch (const std::exception& e)
{
er.code = default_error_code;
@@ -3125,7 +3297,7 @@ namespace tools
return false;
}
- if (req.multisig_info.size() < threshold - 1)
+ if (req.multisig_info.size() < 1 || req.multisig_info.size() > total)
{
er.code = WALLET_RPC_ERROR_CODE_THRESHOLD_NOT_REACHED;
er.message = "Needs multisig info from more participants";
@@ -3152,6 +3324,55 @@ namespace tools
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_exchange_multisig_keys(const wallet_rpc::COMMAND_RPC_EXCHANGE_MULTISIG_KEYS::request& req, wallet_rpc::COMMAND_RPC_EXCHANGE_MULTISIG_KEYS::response& res, epee::json_rpc::error& er)
+ {
+ if (!m_wallet) return not_open(er);
+ if (m_restricted)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_DENIED;
+ er.message = "Command unavailable in restricted mode.";
+ return false;
+ }
+ bool ready;
+ uint32_t threshold, total;
+ if (!m_wallet->multisig(&ready, &threshold, &total))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_NOT_MULTISIG;
+ er.message = "This wallet is not multisig";
+ return false;
+ }
+
+ if (ready)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_ALREADY_MULTISIG;
+ er.message = "This wallet is multisig, and already finalized";
+ return false;
+ }
+
+ if (req.multisig_info.size() < 1 || req.multisig_info.size() > total)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_THRESHOLD_NOT_REACHED;
+ er.message = "Needs multisig info from more participants";
+ return false;
+ }
+
+ try
+ {
+ res.multisig_info = m_wallet->exchange_multisig_keys(req.password, req.multisig_info);
+ if (res.multisig_info.empty())
+ {
+ res.address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
+ }
+ }
+ catch (const std::exception &e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = std::string("Error calling exchange_multisig_info: ") + e.what();
+ return false;
+ }
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_sign_multisig(const wallet_rpc::COMMAND_RPC_SIGN_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_SIGN_MULTISIG::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h
index ab7917a78..887723ed5 100644
--- a/src/wallet/wallet_rpc_server.h
+++ b/src/wallet/wallet_rpc_server.h
@@ -35,6 +35,7 @@
#include <string>
#include "common/util.h"
#include "net/http_server_impl_base.h"
+#include "math_helper.h"
#include "wallet_rpc_server_commands_defs.h"
#include "wallet2.h"
@@ -86,6 +87,7 @@ namespace tools
MAP_JON_RPC_WE("transfer", on_transfer, wallet_rpc::COMMAND_RPC_TRANSFER)
MAP_JON_RPC_WE("transfer_split", on_transfer_split, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT)
MAP_JON_RPC_WE("sign_transfer", on_sign_transfer, wallet_rpc::COMMAND_RPC_SIGN_TRANSFER)
+ MAP_JON_RPC_WE("describe_transfer", on_describe_transfer, wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER)
MAP_JON_RPC_WE("submit_transfer", on_submit_transfer, wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER)
MAP_JON_RPC_WE("sweep_dust", on_sweep_dust, wallet_rpc::COMMAND_RPC_SWEEP_DUST)
MAP_JON_RPC_WE("sweep_unmixable", on_sweep_dust, wallet_rpc::COMMAND_RPC_SWEEP_DUST)
@@ -141,6 +143,7 @@ namespace tools
MAP_JON_RPC_WE("export_multisig_info", on_export_multisig, wallet_rpc::COMMAND_RPC_EXPORT_MULTISIG)
MAP_JON_RPC_WE("import_multisig_info", on_import_multisig, wallet_rpc::COMMAND_RPC_IMPORT_MULTISIG)
MAP_JON_RPC_WE("finalize_multisig", on_finalize_multisig, wallet_rpc::COMMAND_RPC_FINALIZE_MULTISIG)
+ MAP_JON_RPC_WE("exchange_multisig_keys", on_exchange_multisig_keys, wallet_rpc::COMMAND_RPC_EXCHANGE_MULTISIG_KEYS)
MAP_JON_RPC_WE("sign_multisig", on_sign_multisig, wallet_rpc::COMMAND_RPC_SIGN_MULTISIG)
MAP_JON_RPC_WE("submit_multisig", on_submit_multisig, wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG)
MAP_JON_RPC_WE("get_version", on_get_version, wallet_rpc::COMMAND_RPC_GET_VERSION)
@@ -165,6 +168,7 @@ namespace tools
bool on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er);
bool on_transfer_split(const wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::response& res, epee::json_rpc::error& er);
bool on_sign_transfer(const wallet_rpc::COMMAND_RPC_SIGN_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SIGN_TRANSFER::response& res, epee::json_rpc::error& er);
+ bool on_describe_transfer(const wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::response& res, epee::json_rpc::error& er);
bool on_submit_transfer(const wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::response& res, epee::json_rpc::error& er);
bool on_sweep_dust(const wallet_rpc::COMMAND_RPC_SWEEP_DUST::request& req, wallet_rpc::COMMAND_RPC_SWEEP_DUST::response& res, epee::json_rpc::error& er);
bool on_sweep_all(const wallet_rpc::COMMAND_RPC_SWEEP_ALL::request& req, wallet_rpc::COMMAND_RPC_SWEEP_ALL::response& res, epee::json_rpc::error& er);
@@ -218,6 +222,7 @@ namespace tools
bool on_export_multisig(const wallet_rpc::COMMAND_RPC_EXPORT_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_EXPORT_MULTISIG::response& res, epee::json_rpc::error& er);
bool on_import_multisig(const wallet_rpc::COMMAND_RPC_IMPORT_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_IMPORT_MULTISIG::response& res, epee::json_rpc::error& er);
bool on_finalize_multisig(const wallet_rpc::COMMAND_RPC_FINALIZE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_FINALIZE_MULTISIG::response& res, epee::json_rpc::error& er);
+ bool on_exchange_multisig_keys(const wallet_rpc::COMMAND_RPC_EXCHANGE_MULTISIG_KEYS::request& req, wallet_rpc::COMMAND_RPC_EXCHANGE_MULTISIG_KEYS::response& res, epee::json_rpc::error& er);
bool on_sign_multisig(const wallet_rpc::COMMAND_RPC_SIGN_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_SIGN_MULTISIG::response& res, epee::json_rpc::error& er);
bool on_submit_multisig(const wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG::response& res, epee::json_rpc::error& er);
bool on_get_version(const wallet_rpc::COMMAND_RPC_GET_VERSION::request& req, wallet_rpc::COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& er);
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index e46745339..924f3a0f1 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -47,7 +47,7 @@
// advance which version they will stop working with
// Don't go over 32767 for any of these
#define WALLET_RPC_VERSION_MAJOR 1
-#define WALLET_RPC_VERSION_MINOR 4
+#define WALLET_RPC_VERSION_MINOR 5
#define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor))
#define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR)
namespace tools
@@ -531,16 +531,79 @@ namespace wallet_rpc
};
};
+ struct COMMAND_RPC_DESCRIBE_TRANSFER
+ {
+ struct recipient
+ {
+ std::string address;
+ uint64_t amount;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(amount)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct transfer_description
+ {
+ uint64_t amount_in;
+ uint64_t amount_out;
+ uint32_t ring_size;
+ uint64_t unlock_time;
+ std::list<recipient> recipients;
+ std::string payment_id;
+ uint64_t change_amount;
+ std::string change_address;
+ uint64_t fee;
+ uint32_t dummy_outputs;
+ std::string extra;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(amount_in)
+ KV_SERIALIZE(amount_out)
+ KV_SERIALIZE(ring_size)
+ KV_SERIALIZE(unlock_time)
+ KV_SERIALIZE(recipients)
+ KV_SERIALIZE(payment_id)
+ KV_SERIALIZE(change_amount)
+ KV_SERIALIZE(change_address)
+ KV_SERIALIZE(fee)
+ KV_SERIALIZE(dummy_outputs)
+ KV_SERIALIZE(extra)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct request
+ {
+ std::string unsigned_txset;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(unsigned_txset)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ std::list<transfer_description> desc;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(desc)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
struct COMMAND_RPC_SIGN_TRANSFER
{
struct request
{
std::string unsigned_txset;
bool export_raw;
+ bool get_tx_keys;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(unsigned_txset)
KV_SERIALIZE_OPT(export_raw, false)
+ KV_SERIALIZE_OPT(get_tx_keys, false)
END_KV_SERIALIZE_MAP()
};
@@ -549,11 +612,13 @@ namespace wallet_rpc
std::string signed_txset;
std::list<std::string> tx_hash_list;
std::list<std::string> tx_raw_list;
+ std::list<std::string> tx_key_list;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(signed_txset)
KV_SERIALIZE(tx_hash_list)
KV_SERIALIZE(tx_raw_list)
+ KV_SERIALIZE(tx_key_list)
END_KV_SERIALIZE_MAP()
};
};
@@ -1990,6 +2055,31 @@ namespace wallet_rpc
};
};
+ struct COMMAND_RPC_EXCHANGE_MULTISIG_KEYS
+ {
+ struct request
+ {
+ std::string password;
+ std::vector<std::string> multisig_info;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(password)
+ KV_SERIALIZE(multisig_info)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ std::string address;
+ std::string multisig_info;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(multisig_info)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
struct COMMAND_RPC_SIGN_MULTISIG
{
struct request
diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h
index f127ae240..9b3a2847d 100644
--- a/src/wallet/wallet_rpc_server_error_codes.h
+++ b/src/wallet/wallet_rpc_server_error_codes.h
@@ -73,3 +73,4 @@
#define WALLET_RPC_ERROR_CODE_BAD_SIGNED_TX_DATA -40
#define WALLET_RPC_ERROR_CODE_SIGNED_SUBMISSION -41
#define WALLET_RPC_ERROR_CODE_SIGN_UNSIGNED -42
+#define WALLET_RPC_ERROR_CODE_NON_DETERMINISTIC -43