diff options
Diffstat (limited to 'src')
63 files changed, 1995 insertions, 605 deletions
diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 3391d9bff..9f71fd068 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -54,7 +54,7 @@ using epee::string_tools::pod_to_hex; using namespace crypto; // Increase when the DB structure changes -#define VERSION 4 +#define VERSION 5 namespace { @@ -274,7 +274,7 @@ typedef struct mdb_block_info_1 uint64_t bi_timestamp; uint64_t bi_coins; uint64_t bi_weight; // a size_t really but we need 32-bit compat - difficulty_type bi_diff; + uint64_t bi_diff; crypto::hash bi_hash; } mdb_block_info_1; @@ -284,7 +284,7 @@ typedef struct mdb_block_info_2 uint64_t bi_timestamp; uint64_t bi_coins; uint64_t bi_weight; // a size_t really but we need 32-bit compat - difficulty_type bi_diff; + uint64_t bi_diff; crypto::hash bi_hash; uint64_t bi_cum_rct; } mdb_block_info_2; @@ -295,13 +295,26 @@ typedef struct mdb_block_info_3 uint64_t bi_timestamp; uint64_t bi_coins; uint64_t bi_weight; // a size_t really but we need 32-bit compat - difficulty_type bi_diff; + uint64_t bi_diff; crypto::hash bi_hash; uint64_t bi_cum_rct; uint64_t bi_long_term_block_weight; } mdb_block_info_3; -typedef mdb_block_info_3 mdb_block_info; +typedef struct mdb_block_info_4 +{ + uint64_t bi_height; + uint64_t bi_timestamp; + uint64_t bi_coins; + uint64_t bi_weight; // a size_t really but we need 32-bit compat + uint64_t bi_diff_lo; + uint64_t bi_diff_hi; + crypto::hash bi_hash; + uint64_t bi_cum_rct; + uint64_t bi_long_term_block_weight; +} mdb_block_info_4; + +typedef mdb_block_info_4 mdb_block_info; typedef struct blk_height { crypto::hash bh_hash; @@ -757,7 +770,8 @@ void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t l bi.bi_timestamp = blk.timestamp; bi.bi_coins = coins_generated; bi.bi_weight = block_weight; - bi.bi_diff = cumulative_difficulty; + bi.bi_diff_hi = (cumulative_difficulty >> 64).convert_to<uint64_t>(); + bi.bi_diff_lo = (cumulative_difficulty << 64 >> 64).convert_to<uint64_t>(); bi.bi_hash = blk_hash; bi.bi_cum_rct = num_rct_outs; if (blk.major_version >= 4) @@ -2527,7 +2541,9 @@ difficulty_type BlockchainLMDB::get_block_cumulative_difficulty(const uint64_t& throw0(DB_ERROR("Error attempting to retrieve a cumulative difficulty from the db")); mdb_block_info *bi = (mdb_block_info *)result.mv_data; - difficulty_type ret = bi->bi_diff; + difficulty_type ret = bi->bi_diff_hi; + ret <<= 64; + ret |= bi->bi_diff_lo; TXN_POSTFIX_RDONLY(); return ret; } @@ -5040,6 +5056,133 @@ void BlockchainLMDB::migrate_3_4() txn.commit(); } +void BlockchainLMDB::migrate_4_5() +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + uint64_t i; + int result; + mdb_txn_safe txn(false); + MDB_val k, v; + char *ptr; + + MGINFO_YELLOW("Migrating blockchain from DB version 4 to 5 - this may take a while:"); + + do { + LOG_PRINT_L1("migrating block info:"); + + result = mdb_txn_begin(m_env, NULL, 0, txn); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str())); + + MDB_stat db_stats; + if ((result = mdb_stat(txn, m_blocks, &db_stats))) + throw0(DB_ERROR(lmdb_error("Failed to query m_blocks: ", result).c_str())); + const uint64_t blockchain_height = db_stats.ms_entries; + + /* the block_info table name is the same but the old version and new version + * have incompatible data. Create a new table. We want the name to be similar + * to the old name so that it will occupy the same location in the DB. + */ + MDB_dbi o_block_info = m_block_info; + lmdb_db_open(txn, "block_infn", MDB_INTEGERKEY | MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED, m_block_info, "Failed to open db handle for block_infn"); + mdb_set_dupsort(txn, m_block_info, compare_uint64); + + + MDB_cursor *c_blocks; + result = mdb_cursor_open(txn, m_blocks, &c_blocks); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to open a cursor for blocks: ", result).c_str())); + + MDB_cursor *c_old, *c_cur; + i = 0; + while(1) { + if (!(i % 1000)) { + if (i) { + LOGIF(el::Level::Info) { + std::cout << i << " / " << blockchain_height << " \r" << std::flush; + } + txn.commit(); + result = mdb_txn_begin(m_env, NULL, 0, txn); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str())); + } + result = mdb_cursor_open(txn, m_block_info, &c_cur); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to open a cursor for block_infn: ", result).c_str())); + result = mdb_cursor_open(txn, o_block_info, &c_old); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to open a cursor for block_info: ", result).c_str())); + if (!i) { + MDB_stat db_stat; + result = mdb_stat(txn, m_block_info, &db_stats); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to query m_block_info: ", result).c_str())); + i = db_stats.ms_entries; + } + } + result = mdb_cursor_get(c_old, &k, &v, MDB_NEXT); + if (result == MDB_NOTFOUND) { + txn.commit(); + break; + } + else if (result) + throw0(DB_ERROR(lmdb_error("Failed to get a record from block_info: ", result).c_str())); + const mdb_block_info_3 *bi_old = (const mdb_block_info_3*)v.mv_data; + mdb_block_info_4 bi; + bi.bi_height = bi_old->bi_height; + bi.bi_timestamp = bi_old->bi_timestamp; + bi.bi_coins = bi_old->bi_coins; + bi.bi_weight = bi_old->bi_weight; + bi.bi_diff_lo = bi_old->bi_diff; + bi.bi_diff_hi = 0; + bi.bi_hash = bi_old->bi_hash; + bi.bi_cum_rct = bi_old->bi_cum_rct; + bi.bi_long_term_block_weight = bi_old->bi_long_term_block_weight; + + MDB_val_set(nv, bi); + result = mdb_cursor_put(c_cur, (MDB_val *)&zerokval, &nv, MDB_APPENDDUP); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to put a record into block_infn: ", result).c_str())); + /* we delete the old records immediately, so the overall DB and mapsize should not grow. + * This is a little slower than just letting mdb_drop() delete it all at the end, but + * it saves a significant amount of disk space. + */ + result = mdb_cursor_del(c_old, 0); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to delete a record from block_info: ", result).c_str())); + i++; + } + + result = mdb_txn_begin(m_env, NULL, 0, txn); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str())); + /* Delete the old table */ + result = mdb_drop(txn, o_block_info, 1); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to delete old block_info table: ", result).c_str())); + + RENAME_DB("block_infn"); + mdb_dbi_close(m_env, m_block_info); + + lmdb_db_open(txn, "block_info", MDB_INTEGERKEY | MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED, m_block_info, "Failed to open db handle for block_infn"); + mdb_set_dupsort(txn, m_block_info, compare_uint64); + + txn.commit(); + } while(0); + + uint32_t version = 5; + v.mv_data = (void *)&version; + v.mv_size = sizeof(version); + MDB_val_str(vk, "version"); + result = mdb_txn_begin(m_env, NULL, 0, txn); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str())); + result = mdb_put(txn, m_properties, &vk, &v, 0); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to update version for the db: ", result).c_str())); + txn.commit(); +} + void BlockchainLMDB::migrate(const uint32_t oldversion) { if (oldversion < 1) @@ -5050,6 +5193,8 @@ void BlockchainLMDB::migrate(const uint32_t oldversion) migrate_2_3(); if (oldversion < 4) migrate_3_4(); + if (oldversion < 5) + migrate_4_5(); } } // namespace cryptonote diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index 9185bd409..2f89b77ac 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -418,6 +418,9 @@ private: // migrate from DB version 3 to 4 void migrate_3_4(); + // migrate from DB version 4 to 5 + void migrate_4_5(); + void cleanup_batch(); private: diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp index e4efdc3cb..8454595ac 100644 --- a/src/blockchain_utilities/blockchain_import.cpp +++ b/src/blockchain_utilities/blockchain_import.cpp @@ -294,7 +294,8 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path } // 4 byte magic + (currently) 1024 byte header structures - bootstrap.seek_to_first_chunk(import_file); + uint8_t major_version, minor_version; + bootstrap.seek_to_first_chunk(import_file, major_version, minor_version); std::string str1; char buffer1[1024]; @@ -415,7 +416,23 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path { str1.assign(buffer_block, chunk_size); bootstrap::block_package bp; - if (! ::serialization::parse_binary(str1, bp)) + bool res; + if (major_version == 0) + { + bootstrap::block_package_1 bp1; + res = ::serialization::parse_binary(str1, bp1); + if (res) + { + bp.block = std::move(bp1.block); + bp.txs = std::move(bp1.txs); + bp.block_weight = bp1.block_weight; + bp.cumulative_difficulty = bp1.cumulative_difficulty; + bp.coins_generated = bp1.coins_generated; + } + } + else + res = ::serialization::parse_binary(str1, bp); + if (!res) throw std::runtime_error("Error in deserialization of chunk"); int display_interval = 1000; diff --git a/src/blockchain_utilities/bootstrap_file.cpp b/src/blockchain_utilities/bootstrap_file.cpp index fb9a24f5d..252c79776 100644 --- a/src/blockchain_utilities/bootstrap_file.cpp +++ b/src/blockchain_utilities/bootstrap_file.cpp @@ -124,8 +124,8 @@ bool BootstrapFile::initialize_file() *m_raw_data_file << blob; bootstrap::file_info bfi; - bfi.major_version = 0; - bfi.minor_version = 1; + bfi.major_version = 1; + bfi.minor_version = 0; bfi.header_size = header_size; bootstrap::blocks_info bbi; @@ -323,7 +323,7 @@ bool BootstrapFile::store_blockchain_raw(Blockchain* _blockchain_storage, tx_mem return BootstrapFile::close(); } -uint64_t BootstrapFile::seek_to_first_chunk(std::ifstream& import_file) +uint64_t BootstrapFile::seek_to_first_chunk(std::ifstream& import_file, uint8_t &major_version, uint8_t &minor_version) { uint32_t file_magic; @@ -371,6 +371,8 @@ uint64_t BootstrapFile::seek_to_first_chunk(std::ifstream& import_file) uint64_t full_header_size = sizeof(file_magic) + bfi.header_size; import_file.seekg(full_header_size); + major_version = bfi.major_version; + minor_version = bfi.minor_version; return full_header_size; } @@ -461,7 +463,8 @@ uint64_t BootstrapFile::count_blocks(const std::string& import_file_path, std::s } uint64_t full_header_size; // 4 byte magic + length of header structures - full_header_size = seek_to_first_chunk(import_file); + uint8_t major_version, minor_version; + full_header_size = seek_to_first_chunk(import_file, major_version, minor_version); MINFO("Scanning blockchain from bootstrap file..."); bool quit = false; diff --git a/src/blockchain_utilities/bootstrap_file.h b/src/blockchain_utilities/bootstrap_file.h index 5fb2cf366..1e6ef5d81 100644 --- a/src/blockchain_utilities/bootstrap_file.h +++ b/src/blockchain_utilities/bootstrap_file.h @@ -60,7 +60,7 @@ public: uint64_t count_bytes(std::ifstream& import_file, uint64_t blocks, uint64_t& h, bool& quit); uint64_t count_blocks(const std::string& dir_path, std::streampos& start_pos, uint64_t& seek_height); uint64_t count_blocks(const std::string& dir_path); - uint64_t seek_to_first_chunk(std::ifstream& import_file); + uint64_t seek_to_first_chunk(std::ifstream& import_file, uint8_t &major_version, uint8_t &minor_version); bool store_blockchain_raw(cryptonote::Blockchain* cs, cryptonote::tx_memory_pool* txp, boost::filesystem::path& output_file, uint64_t use_block_height=0); diff --git a/src/blockchain_utilities/bootstrap_serialization.h b/src/blockchain_utilities/bootstrap_serialization.h index 554c6d56e..70b3eea7e 100644 --- a/src/blockchain_utilities/bootstrap_serialization.h +++ b/src/blockchain_utilities/bootstrap_serialization.h @@ -29,7 +29,7 @@ #pragma once #include "cryptonote_basic/cryptonote_boost_serialization.h" -#include "cryptonote_basic/difficulty.h" +#include "serialization/difficulty_type.h" namespace cryptonote @@ -66,6 +66,23 @@ namespace cryptonote END_SERIALIZE() }; + struct block_package_1 + { + cryptonote::block block; + std::vector<transaction> txs; + size_t block_weight; + uint64_t cumulative_difficulty; + uint64_t coins_generated; + + BEGIN_SERIALIZE() + FIELD(block) + FIELD(txs) + VARINT_FIELD(block_weight) + VARINT_FIELD(cumulative_difficulty) + VARINT_FIELD(coins_generated) + END_SERIALIZE() + }; + struct block_package { cryptonote::block block; @@ -78,7 +95,7 @@ namespace cryptonote FIELD(block) FIELD(txs) VARINT_FIELD(block_weight) - VARINT_FIELD(cumulative_difficulty) + FIELD(cumulative_difficulty) VARINT_FIELD(coins_generated) END_SERIALIZE() }; diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp index 711a8ba30..1a1155c7c 100644 --- a/src/common/dns_utils.cpp +++ b/src/common/dns_utils.cpp @@ -32,9 +32,9 @@ #include <stdlib.h> #include "include_base_utils.h" +#include "common/threadpool.h" #include <random> #include <boost/thread/mutex.hpp> -#include <boost/thread/thread.hpp> #include <boost/algorithm/string/join.hpp> #include <boost/optional.hpp> using namespace epee; @@ -293,9 +293,8 @@ DNSResolver::DNSResolver() : m_data(new DNSResolverData()) ub_ctx_delete(m_data->m_ub_context); m_data->m_ub_context = ub_ctx_create(); add_anchors(m_data->m_ub_context); - dns_public_addr = tools::dns_utils::parse_dns_public(DNS_PUBLIC); - for (const auto &ip: dns_public_addr) - ub_ctx_set_fwd(m_data->m_ub_context, string_copy(ip.c_str())); + for (const auto &ip: DEFAULT_DNS_PUBLIC_ADDR) + ub_ctx_set_fwd(m_data->m_ub_context, string_copy(ip)); ub_ctx_set_option(m_data->m_ub_context, string_copy("do-udp:"), string_copy("no")); ub_ctx_set_option(m_data->m_ub_context, string_copy("do-tcp:"), string_copy("yes")); } @@ -524,16 +523,16 @@ bool load_txt_records_from_dns(std::vector<std::string> &good_records, const std size_t first_index = dis(gen); // send all requests in parallel - std::vector<boost::thread> threads(dns_urls.size()); std::deque<bool> avail(dns_urls.size(), false), valid(dns_urls.size(), false); + tools::threadpool& tpool = tools::threadpool::getInstance(); + tools::threadpool::waiter waiter; for (size_t n = 0; n < dns_urls.size(); ++n) { - threads[n] = boost::thread([n, dns_urls, &records, &avail, &valid](){ + tpool.submit(&waiter,[n, dns_urls, &records, &avail, &valid](){ records[n] = tools::DNSResolver::instance().get_txt_record(dns_urls[n], avail[n], valid[n]); }); } - for (size_t n = 0; n < dns_urls.size(); ++n) - threads[n].join(); + waiter.wait(&tpool); size_t cur_index = first_index; do diff --git a/src/common/util.cpp b/src/common/util.cpp index 80b8a9e81..728efc294 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -80,6 +80,7 @@ using namespace epee; #include <boost/filesystem.hpp> #include <boost/algorithm/string.hpp> #include <boost/asio.hpp> +#include <boost/format.hpp> #include <openssl/sha.h> #undef MONERO_DEFAULT_LOG_CATEGORY @@ -1063,4 +1064,39 @@ std::string get_nix_version_display_string() strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tm); return std::string(buffer); } + + std::string get_human_readable_bytes(uint64_t bytes) + { + // Use 1024 for "kilo", 1024*1024 for "mega" and so on instead of the more modern and standard-conforming + // 1000, 1000*1000 and so on, to be consistent with other Monero code that also uses base 2 units + struct byte_map + { + const char* const format; + const std::uint64_t bytes; + }; + + static constexpr const byte_map sizes[] = + { + {"%.0f B", 1024}, + {"%.2f KB", 1024 * 1024}, + {"%.2f MB", std::uint64_t(1024) * 1024 * 1024}, + {"%.2f GB", std::uint64_t(1024) * 1024 * 1024 * 1024}, + {"%.2f TB", std::uint64_t(1024) * 1024 * 1024 * 1024 * 1024} + }; + + struct bytes_less + { + bool operator()(const byte_map& lhs, const byte_map& rhs) const noexcept + { + return lhs.bytes < rhs.bytes; + } + }; + + const auto size = std::upper_bound( + std::begin(sizes), std::end(sizes) - 1, byte_map{"", bytes}, bytes_less{} + ); + const std::uint64_t divisor = size->bytes / 1024; + return (boost::format(size->format) % (double(bytes) / divisor)).str(); + } + } diff --git a/src/common/util.h b/src/common/util.h index ef2305bf4..77a5a9af6 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -244,4 +244,6 @@ namespace tools void closefrom(int fd); std::string get_human_readable_timestamp(uint64_t ts); + + std::string get_human_readable_bytes(uint64_t bytes); } diff --git a/src/cryptonote_basic/cryptonote_boost_serialization.h b/src/cryptonote_basic/cryptonote_boost_serialization.h index 4ce9f3eeb..3dd98f0c6 100644 --- a/src/cryptonote_basic/cryptonote_boost_serialization.h +++ b/src/cryptonote_basic/cryptonote_boost_serialization.h @@ -40,6 +40,7 @@ #include <boost/archive/portable_binary_iarchive.hpp> #include <boost/archive/portable_binary_oarchive.hpp> #include "cryptonote_basic.h" +#include "difficulty.h" #include "common/unordered_containers_boost_serialization.h" #include "crypto/crypto.h" #include "ringct/rctTypes.h" @@ -339,6 +340,41 @@ namespace boost if (x.type == rct::RCTTypeBulletproof || x.type == rct::RCTTypeBulletproof2) a & x.p.pseudoOuts; } + + template <class Archive> + inline void serialize(Archive &a, rct::RCTConfig &x, const boost::serialization::version_type ver) + { + a & x.range_proof_type; + a & x.bp_version; + } + + template <class Archive> + inline void serialize(Archive &a, cryptonote::difficulty_type &x, const boost::serialization::version_type ver) + { + if (Archive::is_loading::value) + { + // load high part + uint64_t v = 0; + a & v; + x = v; + // load low part + x = x << 64; + a & v; + x += v; + } + else + { + // store high part + cryptonote::difficulty_type x_ = x >> 64; + uint64_t v = x_.convert_to<uint64_t>(); + a & v; + // store low part + x_ = x << 64 >> 64; + v = x_.convert_to<uint64_t>(); + a & v; + } + } + } } diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index c2ad97a64..094057b1f 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -987,15 +987,11 @@ namespace cryptonote { if (t.version == 1) return false; - static const crypto::hash empty_hash = { (char)0x70, (char)0xa4, (char)0x85, (char)0x5d, (char)0x04, (char)0xd8, (char)0xfa, (char)0x7b, (char)0x3b, (char)0x27, (char)0x82, (char)0xca, (char)0x53, (char)0xb6, (char)0x00, (char)0xe5, (char)0xc0, (char)0x03, (char)0xc7, (char)0xdc, (char)0xb2, (char)0x7d, (char)0x7e, (char)0x92, (char)0x3c, (char)0x23, (char)0xf7, (char)0x86, (char)0x01, (char)0x46, (char)0xd2, (char)0xc5 }; const unsigned int unprunable_size = t.unprunable_size; if (blob && unprunable_size) { CHECK_AND_ASSERT_MES(unprunable_size <= blob->size(), false, "Inconsistent transaction unprunable and blob sizes"); - if (blob->size() - unprunable_size == 0) - res = empty_hash; - else - cryptonote::get_blob_hash(epee::span<const char>(blob->data() + unprunable_size, blob->size() - unprunable_size), res); + cryptonote::get_blob_hash(epee::span<const char>(blob->data() + unprunable_size, blob->size() - unprunable_size), res); } else { @@ -1007,10 +1003,7 @@ namespace cryptonote const size_t mixin = t.vin.empty() ? 0 : t.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(t.vin[0]).key_offsets.size() - 1 : 0; bool r = tt.rct_signatures.p.serialize_rctsig_prunable(ba, t.rct_signatures.type, inputs, outputs, mixin); CHECK_AND_ASSERT_MES(r, false, "Failed to serialize rct signatures prunable"); - if (ss.str().empty()) - res = empty_hash; - else - cryptonote::get_blob_hash(ss.str(), res); + cryptonote::get_blob_hash(ss.str(), res); } return true; } @@ -1047,7 +1040,10 @@ namespace cryptonote } // prunable rct - hashes[2] = pruned_data_hash; + if (t.rct_signatures.type == rct::RCTTypeNull) + hashes[2] = crypto::null_hash; + else + hashes[2] = pruned_data_hash; // the tx hash is the hash of the 3 hashes crypto::hash res = cn_fast_hash(hashes, sizeof(hashes)); diff --git a/src/cryptonote_basic/difficulty.cpp b/src/cryptonote_basic/difficulty.cpp index 89b748a83..5162e53e6 100644 --- a/src/cryptonote_basic/difficulty.cpp +++ b/src/cryptonote_basic/difficulty.cpp @@ -102,7 +102,7 @@ namespace cryptonote { return a + b < a || (c && a + b == (uint64_t) -1); } - bool check_hash(const crypto::hash &hash, difficulty_type difficulty) { + bool check_hash_64(const crypto::hash &hash, uint64_t difficulty) { uint64_t low, high, top, cur; // First check the highest word, this will most likely fail for a random hash. mul(swap64le(((const uint64_t *) &hash)[3]), difficulty, top, high); @@ -119,7 +119,7 @@ namespace cryptonote { return !carry; } - difficulty_type next_difficulty(std::vector<std::uint64_t> timestamps, std::vector<difficulty_type> cumulative_difficulties, size_t target_seconds) { + uint64_t next_difficulty_64(std::vector<std::uint64_t> timestamps, std::vector<uint64_t> cumulative_difficulties, size_t target_seconds) { if(timestamps.size() > DIFFICULTY_WINDOW) { @@ -150,7 +150,7 @@ namespace cryptonote { if (time_span == 0) { time_span = 1; } - difficulty_type total_work = cumulative_difficulties[cut_end - 1] - cumulative_difficulties[cut_begin]; + uint64_t total_work = cumulative_difficulties[cut_end - 1] - cumulative_difficulties[cut_begin]; assert(total_work > 0); uint64_t low, high; mul(total_work, target_seconds, low, high); @@ -162,4 +162,81 @@ namespace cryptonote { return (low + time_span - 1) / time_span; } +#if defined(_MSC_VER) +#ifdef max +#undef max +#endif +#endif + + const difficulty_type max64bit(std::numeric_limits<std::uint64_t>::max()); + const boost::multiprecision::uint256_t max128bit(std::numeric_limits<boost::multiprecision::uint128_t>::max()); + const boost::multiprecision::uint512_t max256bit(std::numeric_limits<boost::multiprecision::uint256_t>::max()); + +#define FORCE_FULL_128_BITS + + bool check_hash_128(const crypto::hash &hash, difficulty_type difficulty) { +#ifndef FORCE_FULL_128_BITS + // fast check + if (difficulty >= max64bit && ((const uint64_t *) &hash)[3] > 0) + return false; +#endif + // usual slow check + boost::multiprecision::uint512_t hashVal = 0; +#ifdef FORCE_FULL_128_BITS + for(int i = 0; i < 4; i++) { // highest word is zero +#else + for(int i = 1; i < 4; i++) { // highest word is zero +#endif + hashVal <<= 64; + hashVal |= swap64le(((const uint64_t *) &hash)[3 - i]); + } + return hashVal * difficulty <= max256bit; + } + + bool check_hash(const crypto::hash &hash, difficulty_type difficulty) { + if (difficulty <= max64bit) // if can convert to small difficulty - do it + return check_hash_64(hash, difficulty.convert_to<std::uint64_t>()); + else + return check_hash_128(hash, difficulty); + } + + difficulty_type next_difficulty(std::vector<uint64_t> timestamps, std::vector<difficulty_type> cumulative_difficulties, size_t target_seconds) { + //cutoff DIFFICULTY_LAG + if(timestamps.size() > DIFFICULTY_WINDOW) + { + timestamps.resize(DIFFICULTY_WINDOW); + cumulative_difficulties.resize(DIFFICULTY_WINDOW); + } + + + size_t length = timestamps.size(); + assert(length == cumulative_difficulties.size()); + if (length <= 1) { + return 1; + } + static_assert(DIFFICULTY_WINDOW >= 2, "Window is too small"); + assert(length <= DIFFICULTY_WINDOW); + sort(timestamps.begin(), timestamps.end()); + size_t cut_begin, cut_end; + static_assert(2 * DIFFICULTY_CUT <= DIFFICULTY_WINDOW - 2, "Cut length is too large"); + if (length <= DIFFICULTY_WINDOW - 2 * DIFFICULTY_CUT) { + cut_begin = 0; + cut_end = length; + } else { + cut_begin = (length - (DIFFICULTY_WINDOW - 2 * DIFFICULTY_CUT) + 1) / 2; + cut_end = cut_begin + (DIFFICULTY_WINDOW - 2 * DIFFICULTY_CUT); + } + assert(/*cut_begin >= 0 &&*/ cut_begin + 2 <= cut_end && cut_end <= length); + uint64_t time_span = timestamps[cut_end - 1] - timestamps[cut_begin]; + if (time_span == 0) { + time_span = 1; + } + difficulty_type total_work = cumulative_difficulties[cut_end - 1] - cumulative_difficulties[cut_begin]; + assert(total_work > 0); + boost::multiprecision::uint256_t res = (boost::multiprecision::uint256_t(total_work) * target_seconds + time_span - 1) / time_span; + if(res > max128bit) + return 0; // to behave like previous implementation, may be better return max128bit? + return res.convert_to<difficulty_type>(); + } + } diff --git a/src/cryptonote_basic/difficulty.h b/src/cryptonote_basic/difficulty.h index 8da355b22..f7a9376fb 100644 --- a/src/cryptonote_basic/difficulty.h +++ b/src/cryptonote_basic/difficulty.h @@ -32,12 +32,13 @@ #include <cstdint> #include <vector> +#include <boost/multiprecision/cpp_int.hpp> #include "crypto/hash.h" namespace cryptonote { - typedef std::uint64_t difficulty_type; + typedef boost::multiprecision::uint128_t difficulty_type; /** * @brief checks if a hash fits the given difficulty @@ -51,6 +52,10 @@ namespace cryptonote * * @return true if valid, else false */ + bool check_hash_64(const crypto::hash &hash, uint64_t difficulty); + uint64_t next_difficulty_64(std::vector<std::uint64_t> timestamps, std::vector<uint64_t> cumulative_difficulties, size_t target_seconds); + + bool check_hash_128(const crypto::hash &hash, difficulty_type difficulty); bool check_hash(const crypto::hash &hash, difficulty_type difficulty); difficulty_type next_difficulty(std::vector<std::uint64_t> timestamps, std::vector<difficulty_type> cumulative_difficulties, size_t target_seconds); } diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp index 6628c8448..4e2edc20f 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -435,14 +435,15 @@ namespace cryptonote { MTRACE("Miner has received stop signal"); - if (!is_mining()) + CRITICAL_REGION_LOCAL(m_threads_lock); + bool mining = !m_threads.empty(); + if (!mining) { MTRACE("Not mining - nothing to stop" ); return true; } send_stop_signal(); - CRITICAL_REGION_LOCAL(m_threads_lock); // In case background mining was active and the miner threads are waiting // on the background miner to signal start. diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index f5bd9bbb5..83d3044f8 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -178,6 +178,7 @@ Blockchain::Blockchain(tx_memory_pool& tx_pool) : m_enforce_dns_checkpoints(false), m_max_prepare_blocks_threads(4), m_db_sync_on_blocks(true), m_db_sync_threshold(1), m_db_sync_mode(db_async), m_db_default_sync(false), m_fast_sync(true), m_show_time_stats(false), m_sync_counter(0), m_bytes_to_sync(0), m_cancel(false), m_long_term_block_weights_window(CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE), m_long_term_effective_median_block_weight(0), + m_long_term_block_weights_cache_tip_hash(crypto::null_hash), m_difficulty_for_next_block_top_hash(crypto::null_hash), m_difficulty_for_next_block(1), m_btc_valid(false) @@ -645,6 +646,8 @@ block Blockchain::pop_block_from_blockchain() block popped_block; std::vector<transaction> popped_txs; + CHECK_AND_ASSERT_THROW_MES(m_db->height() > 1, "Cannot pop the genesis block"); + try { m_db->pop_block(popped_block, popped_txs); @@ -1279,7 +1282,50 @@ void Blockchain::get_long_term_block_weights(std::vector<uint64_t>& weights, uin LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); + PERF_TIMER(get_long_term_block_weights); + + if (count == 0) + return; + + bool cached = false; + uint64_t blockchain_height = m_db->height(); + uint64_t tip_height = start_height + count - 1; + crypto::hash tip_hash = crypto::null_hash; + if (tip_height < blockchain_height && count == m_long_term_block_weights_cache.size()) + { + tip_hash = m_db->get_block_hash_from_height(tip_height); + cached = tip_hash == m_long_term_block_weights_cache_tip_hash; + } + + if (cached) + { + MTRACE("requesting " << count << " from " << start_height << ", cached"); + weights = m_long_term_block_weights_cache; + return; + } + + // in the vast majority of uncached cases, most is still cached, + // as we just move the window one block up: + if (tip_height > 0 && count == m_long_term_block_weights_cache.size() && tip_height < blockchain_height) + { + crypto::hash old_tip_hash = m_db->get_block_hash_from_height(tip_height - 1); + if (old_tip_hash == m_long_term_block_weights_cache_tip_hash) + { + weights = m_long_term_block_weights_cache; + for (size_t i = 1; i < weights.size(); ++i) + weights[i - 1] = weights[i]; + MTRACE("requesting " << count << " from " << start_height << ", incremental"); + weights.back() = m_db->get_block_long_term_weight(tip_height); + m_long_term_block_weights_cache = weights; + m_long_term_block_weights_cache_tip_hash = tip_hash; + return; + } + } + + MTRACE("requesting " << count << " from " << start_height << ", uncached"); weights = m_db->get_long_term_block_weights(start_height, count); + m_long_term_block_weights_cache = weights; + m_long_term_block_weights_cache_tip_hash = tip_hash; } //------------------------------------------------------------------ uint64_t Blockchain::get_current_cumulative_block_weight_limit() const @@ -1997,7 +2043,7 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc return true; } //------------------------------------------------------------------ -uint64_t Blockchain::block_difficulty(uint64_t i) const +difficulty_type Blockchain::block_difficulty(uint64_t i) const { LOG_PRINT_L3("Blockchain::" << __func__); // WARNING: this function does not take m_blockchain_lock, and thus should only call read only @@ -2196,7 +2242,11 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc bool result = find_blockchain_supplement(qblock_ids, resp.m_block_ids, resp.start_height, resp.total_height); if (result) - resp.cumulative_difficulty = m_db->get_block_cumulative_difficulty(resp.total_height - 1); + { + cryptonote::difficulty_type wide_cumulative_difficulty = m_db->get_block_cumulative_difficulty(resp.total_height - 1); + resp.cumulative_difficulty = (wide_cumulative_difficulty << 64 >> 64).convert_to<uint64_t>(); + resp.cumulative_difficulty_top64 = (wide_cumulative_difficulty >> 64).convert_to<uint64_t>(); + } return result; } diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 3b8169764..2cd4dc31b 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -653,7 +653,7 @@ namespace cryptonote * * @return the difficulty */ - uint64_t block_difficulty(uint64_t i) const; + difficulty_type block_difficulty(uint64_t i) const; /** * @brief gets blocks based on a list of block hashes @@ -1060,6 +1060,8 @@ namespace cryptonote uint64_t m_timestamps_and_difficulties_height; uint64_t m_long_term_block_weights_window; uint64_t m_long_term_effective_median_block_weight; + mutable crypto::hash m_long_term_block_weights_cache_tip_hash; + mutable std::vector<uint64_t> m_long_term_block_weights_cache; epee::critical_section m_difficulty_lock; crypto::hash m_difficulty_for_next_block_top_hash; diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 58acdb6bb..da413bbe2 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -921,6 +921,7 @@ namespace cryptonote catch (const std::exception &e) { MERROR_VER("Exception in handle_incoming_tx_pre: " << e.what()); + tvc[i].m_verifivation_failed = true; results[i].res = false; } }); @@ -951,6 +952,7 @@ namespace cryptonote catch (const std::exception &e) { MERROR_VER("Exception in handle_incoming_tx_post: " << e.what()); + tvc[i].m_verifivation_failed = true; results[i].res = false; } }); @@ -1782,12 +1784,28 @@ namespace cryptonote return f; } //----------------------------------------------------------------------------------------------- - static double probability(unsigned int blocks, unsigned int expected) + static double probability1(unsigned int blocks, unsigned int expected) { // https://www.umass.edu/wsp/resources/poisson/#computing return pow(expected, blocks) / (factorial(blocks) * exp(expected)); } //----------------------------------------------------------------------------------------------- + static double probability(unsigned int blocks, unsigned int expected) + { + double p = 0.0; + if (blocks <= expected) + { + for (unsigned int b = 0; b <= blocks; ++b) + p += probability1(b, expected); + } + else if (blocks > expected) + { + for (unsigned int b = blocks; b <= expected * 3 /* close enough */; ++b) + p += probability1(b, expected); + } + return p; + } + //----------------------------------------------------------------------------------------------- bool core::check_block_rate() { if (m_offline || m_target_blockchain_height > get_current_blockchain_height()) diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h index cb1561c2d..d38aa7474 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.h +++ b/src/cryptonote_core/cryptonote_tx_utils.h @@ -104,6 +104,13 @@ namespace cryptonote std::vector<rct::key> &amount_keys, crypto::public_key &out_eph_public_key) ; + bool generate_output_ephemeral_keys(const size_t tx_version, const cryptonote::account_keys &sender_account_keys, const crypto::public_key &txkey_pub, const crypto::secret_key &tx_key, + const cryptonote::tx_destination_entry &dst_entr, const boost::optional<cryptonote::account_public_address> &change_addr, const size_t output_index, + const bool &need_additional_txkeys, const std::vector<crypto::secret_key> &additional_tx_keys, + std::vector<crypto::public_key> &additional_tx_public_keys, + std::vector<rct::key> &amount_keys, + crypto::public_key &out_eph_public_key) ; + bool generate_genesis_block( block& bl , std::string const & genesis_tx diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h index d582e3e9c..3083a5b4c 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_defs.h +++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h @@ -34,6 +34,7 @@ #include "serialization/keyvalue_serialization.h" #include "cryptonote_basic/cryptonote_basic.h" #include "cryptonote_basic/blobdatatype.h" + namespace cryptonote { @@ -208,6 +209,7 @@ namespace cryptonote { uint64_t current_height; uint64_t cumulative_difficulty; + uint64_t cumulative_difficulty_top64; crypto::hash top_id; uint8_t top_version; uint32_t pruning_seed; @@ -215,6 +217,7 @@ namespace cryptonote BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(current_height) KV_SERIALIZE(cumulative_difficulty) + KV_SERIALIZE(cumulative_difficulty_top64) KV_SERIALIZE_VAL_POD_AS_BLOB(top_id) KV_SERIALIZE_OPT(top_version, (uint8_t)0) KV_SERIALIZE_OPT(pruning_seed, (uint32_t)0) @@ -245,12 +248,14 @@ namespace cryptonote uint64_t start_height; uint64_t total_height; uint64_t cumulative_difficulty; + uint64_t cumulative_difficulty_top64; std::vector<crypto::hash> m_block_ids; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(start_height) KV_SERIALIZE(total_height) KV_SERIALIZE(cumulative_difficulty) + KV_SERIALIZE(cumulative_difficulty_top64) KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_block_ids) END_KV_SERIALIZE_MAP() }; diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index b33867e8b..32f0afceb 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -398,7 +398,9 @@ namespace cryptonote { m_core.get_blockchain_top(hshd.current_height, hshd.top_id); hshd.top_version = m_core.get_ideal_hard_fork_version(hshd.current_height); - hshd.cumulative_difficulty = m_core.get_block_cumulative_difficulty(hshd.current_height); + difficulty_type wide_cumulative_difficulty = m_core.get_block_cumulative_difficulty(hshd.current_height); + hshd.cumulative_difficulty = (wide_cumulative_difficulty << 64 >> 64).convert_to<uint64_t>(); + hshd.cumulative_difficulty_top64 = (wide_cumulative_difficulty >> 64).convert_to<uint64_t>(); hshd.current_height +=1; hshd.pruning_seed = m_core.get_blockchain_pruning_seed(); return true; diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index b324ab99d..17b945c9a 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -127,6 +127,13 @@ bool t_command_parser_executor::print_connections(const std::vector<std::string> return m_executor.print_connections(); } +bool t_command_parser_executor::print_net_stats(const std::vector<std::string>& args) +{ + if (!args.empty()) return false; + + return m_executor.print_net_stats(); +} + bool t_command_parser_executor::print_blockchain_info(const std::vector<std::string>& args) { if(!args.size()) diff --git a/src/daemon/command_parser_executor.h b/src/daemon/command_parser_executor.h index bec6e4522..098018642 100644 --- a/src/daemon/command_parser_executor.h +++ b/src/daemon/command_parser_executor.h @@ -148,6 +148,8 @@ public: bool prune_blockchain(const std::vector<std::string>& args); bool check_blockchain_pruning(const std::vector<std::string>& args); + + bool print_net_stats(const std::vector<std::string>& args); }; } // namespace daemonize diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp index 94e4a8bf1..69ad6ff10 100644 --- a/src/daemon/command_server.cpp +++ b/src/daemon/command_server.cpp @@ -78,6 +78,11 @@ t_command_server::t_command_server( , "Print the current connections." ); m_command_lookup.set_handler( + "print_net_stats" + , std::bind(&t_command_parser_executor::print_net_stats, &m_parser, p::_1) + , "Print network statistics." + ); + m_command_lookup.set_handler( "print_bc" , std::bind(&t_command_parser_executor::print_blockchain_info, &m_parser, p::_1) , "print_bc <begin_height> [<end_height>]" diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 3d1d893ea..531c080de 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -115,6 +115,7 @@ t_daemon::t_daemon(t_daemon && other) { mp_internals = std::move(other.mp_internals); other.mp_internals.reset(nullptr); + public_rpc_port = other.public_rpc_port; } } @@ -125,6 +126,7 @@ t_daemon & t_daemon::operator=(t_daemon && other) { mp_internals = std::move(other.mp_internals); other.mp_internals.reset(nullptr); + public_rpc_port = other.public_rpc_port; } return *this; } diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 4ee67f571..c9ec5109e 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -627,6 +627,66 @@ bool t_rpc_command_executor::print_connections() { return true; } +bool t_rpc_command_executor::print_net_stats() +{ + cryptonote::COMMAND_RPC_GET_NET_STATS::request net_stats_req; + cryptonote::COMMAND_RPC_GET_NET_STATS::response net_stats_res; + cryptonote::COMMAND_RPC_GET_LIMIT::request limit_req; + cryptonote::COMMAND_RPC_GET_LIMIT::response limit_res; + + std::string fail_message = "Unsuccessful"; + + if (m_is_rpc) + { + if (!m_rpc_client->json_rpc_request(net_stats_req, net_stats_res, "get_net_stats", fail_message.c_str())) + { + return true; + } + if (!m_rpc_client->json_rpc_request(limit_req, limit_res, "get_limit", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_get_net_stats(net_stats_req, net_stats_res) || net_stats_res.status != CORE_RPC_STATUS_OK) + { + tools::fail_msg_writer() << make_error(fail_message, net_stats_res.status); + return true; + } + if (!m_rpc_server->on_get_limit(limit_req, limit_res) || limit_res.status != CORE_RPC_STATUS_OK) + { + tools::fail_msg_writer() << make_error(fail_message, limit_res.status); + return true; + } + } + + uint64_t seconds = (uint64_t)time(NULL) - net_stats_res.start_time; + uint64_t average = seconds > 0 ? net_stats_res.total_bytes_in / seconds : 0; + uint64_t limit = limit_res.limit_down * 1024; // convert to bytes, as limits are always kB/s + double percent = (double)average / (double)limit * 100.0; + tools::success_msg_writer() << boost::format("Received %u bytes (%s) in %u packets, average %s/s = %.2f%% of the limit of %s/s") + % net_stats_res.total_bytes_in + % tools::get_human_readable_bytes(net_stats_res.total_bytes_in) + % net_stats_res.total_packets_in + % tools::get_human_readable_bytes(average) + % percent + % tools::get_human_readable_bytes(limit); + + average = seconds > 0 ? net_stats_res.total_bytes_out / seconds : 0; + limit = limit_res.limit_up * 1024; + percent = (double)average / (double)limit * 100.0; + tools::success_msg_writer() << boost::format("Sent %u bytes (%s) in %u packets, average %s/s = %.2f%% of the limit of %s/s") + % net_stats_res.total_bytes_out + % tools::get_human_readable_bytes(net_stats_res.total_bytes_out) + % net_stats_res.total_packets_out + % tools::get_human_readable_bytes(average) + % percent + % tools::get_human_readable_bytes(limit); + + return true; +} + bool t_rpc_command_executor::print_blockchain_info(uint64_t start_block_index, uint64_t end_block_index) { cryptonote::COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::request req; cryptonote::COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::response res; diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h index 423132b79..3c2686b3f 100644 --- a/src/daemon/rpc_command_executor.h +++ b/src/daemon/rpc_command_executor.h @@ -160,6 +160,8 @@ public: bool prune_blockchain(); bool check_blockchain_pruning(); + + bool print_net_stats(); }; } // namespace daemonize diff --git a/src/device/CMakeLists.txt b/src/device/CMakeLists.txt index ffa1458b0..0b0686f61 100644 --- a/src/device/CMakeLists.txt +++ b/src/device/CMakeLists.txt @@ -75,4 +75,6 @@ target_link_libraries(device ${OPENSSL_CRYPTO_LIBRARIES} ${Boost_SERIALIZATION_LIBRARY} PRIVATE + version + ${Blocks} ${EXTRA_LIBRARIES}) diff --git a/src/device/device.hpp b/src/device/device.hpp index 65b38361b..2e485b1d6 100644 --- a/src/device/device.hpp +++ b/src/device/device.hpp @@ -27,21 +27,6 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // - -/* Note about debug: - * To debug Device you can def the following : - * #define DEBUG_HWDEVICE - * Activate debug mechanism: - * - Add more trace - * - All computation done by device are checked by default device. - * Required IODUMMYCRYPT_HWDEVICE or IONOCRYPT_HWDEVICE for fully working - * #define IODUMMYCRYPT_HWDEVICE 1 - * - It assumes sensitive data encryption is is off on device side. a XOR with 0x55. This allow Ledger Class to make check on clear value - * #define IONOCRYPT_HWDEVICE 1 - * - It assumes sensitive data encryption is off on device side. - */ - - #pragma once #include "crypto/crypto.h" @@ -211,6 +196,10 @@ namespace hw { /* TRANSACTION */ /* ======================================================================= */ + virtual void generate_tx_proof(const crypto::hash &prefix_hash, + const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r, + crypto::signature &sig) = 0; + virtual bool open_tx(crypto::secret_key &tx_key) = 0; virtual bool encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key) = 0; @@ -220,6 +209,8 @@ namespace hw { return encrypt_payment_id(payment_id, public_key, secret_key); } + virtual rct::key genCommitmentMask(const rct::key &amount_key) = 0; + virtual bool ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & sharedSec, bool short_amount) = 0; virtual bool ecdhDecode(rct::ecdhTuple & masked, const rct::key & sharedSec, bool short_amount) = 0; diff --git a/src/device/device_default.cpp b/src/device/device_default.cpp index 999fbc22f..dc06ce237 100644 --- a/src/device/device_default.cpp +++ b/src/device/device_default.cpp @@ -37,7 +37,6 @@ #include "cryptonote_core/cryptonote_tx_utils.h" #include "ringct/rctOps.h" -#include "log.hpp" #define ENCRYPTED_PAYMENT_ID_TAIL 0x8d #define CHACHA8_KEY_TAIL 0x8c @@ -273,6 +272,11 @@ namespace hw { /* ======================================================================= */ /* TRANSACTION */ /* ======================================================================= */ + void device_default::generate_tx_proof(const crypto::hash &prefix_hash, + const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r, + crypto::signature &sig) { + crypto::generate_tx_proof(prefix_hash, R, A, B, D, r, sig); + } bool device_default::open_tx(crypto::secret_key &tx_key) { cryptonote::keypair txkey = cryptonote::keypair::generate(*this); @@ -349,6 +353,10 @@ namespace hw { return true; } + rct::key device_default::genCommitmentMask(const rct::key &amount_key) { + return rct::genCommitmentMask(amount_key); + } + bool device_default::ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & sharedSec, bool short_amount) { rct::ecdhEncode(unmasked, sharedSec, short_amount); return true; diff --git a/src/device/device_default.hpp b/src/device/device_default.hpp index 90d39495b..5252d4129 100644 --- a/src/device/device_default.hpp +++ b/src/device/device_default.hpp @@ -107,10 +107,16 @@ namespace hw { /* TRANSACTION */ /* ======================================================================= */ + void generate_tx_proof(const crypto::hash &prefix_hash, + const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r, + crypto::signature &sig) override; + bool open_tx(crypto::secret_key &tx_key) override; bool encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key) override; + rct::key genCommitmentMask(const rct::key &amount_key) override; + bool ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & sharedSec, bool short_amount) override; bool ecdhDecode(rct::ecdhTuple & masked, const rct::key & sharedSec, bool short_amount) override; diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index 0f197272c..d6033e189 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -27,8 +27,8 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // +#include "version.h" #include "device_ledger.hpp" -#include "log.hpp" #include "ringct/rctOps.h" #include "cryptonote_basic/account.h" #include "cryptonote_basic/subaddress_index.h" @@ -173,6 +173,7 @@ namespace hw { #define INS_SET_SIGNATURE_MODE 0x72 #define INS_GET_ADDITIONAL_KEY 0x74 #define INS_STEALTH 0x76 + #define INS_GEN_COMMITMENT_MASK 0x77 #define INS_BLIND 0x78 #define INS_UNBLIND 0x7A #define INS_GEN_TXOUT_KEYS 0x7B @@ -180,6 +181,7 @@ namespace hw { #define INS_MLSAG 0x7E #define INS_CLOSE_TX 0x80 + #define INS_GET_TX_PROOF 0xA0 #define INS_GET_RESPONSE 0xc0 @@ -302,8 +304,24 @@ namespace hw { } bool device_ledger::reset() { - send_simple(INS_RESET); - return true; + reset_buffer(); + int offset = set_command_header_noopt(INS_RESET); + memmove(this->buffer_send+offset, MONERO_VERSION, strlen(MONERO_VERSION)); + offset += strlen(MONERO_VERSION); + this->buffer_send[4] = offset-5; + this->length_send = offset; + this->exchange(); + + ASSERT_X(this->length_recv>=3, "Communication error, less than three bytes received. Check your application version."); + + unsigned int device_version = 0; + device_version = VERSION(this->buffer_recv[0], this->buffer_recv[1], this->buffer_recv[2]); + + ASSERT_X (device_version >= MINIMAL_APP_VERSION, + "Unsupported device application version: " << VERSION_MAJOR(device_version)<<"."<<VERSION_MINOR(device_version)<<"."<<VERSION_MICRO(device_version) << + " At least " << MINIMAL_APP_VERSION_MAJOR<<"."<<MINIMAL_APP_VERSION_MINOR<<"."<<MINIMAL_APP_VERSION_MICRO<<" is required."); + + return true; } unsigned int device_ledger::exchange(unsigned int ok, unsigned int mask) { @@ -314,9 +332,9 @@ namespace hw { this->length_recv -= 2; this->sw = (this->buffer_recv[length_recv]<<8) | this->buffer_recv[length_recv+1]; + logRESP(); ASSERT_SW(this->sw,ok,msk); - logRESP(); return this->sw; } @@ -790,7 +808,11 @@ namespace hw { const crypto::secret_key a_x = hw::ledger::decrypt(a); const crypto::secret_key b_x = hw::ledger::decrypt(b); crypto::secret_key r_x; + rct::key aG_x; + log_hexbuffer("sc_secret_add: [[IN]] a ", (char*)a_x.data, 32); + log_hexbuffer("sc_secret_add: [[IN]] b ", (char*)b_x.data, 32); this->controle_device->sc_secret_add(r_x, a_x, b_x); + log_hexbuffer("sc_secret_add: [[OUT]] aG", (char*)r_x.data, 32); #endif int offset = set_command_header_noopt(INS_SECRET_KEY_ADD); @@ -825,6 +847,11 @@ namespace hw { #ifdef DEBUG_HWDEVICE crypto::public_key pub_x; crypto::secret_key sec_x; + crypto::secret_key recovery_key_x; + if (recover) { + recovery_key_x = hw::ledger::decrypt(recovery_key); + log_hexbuffer("generate_keys: [[IN]] pub", (char*)recovery_key_x.data, 32); + } #endif send_simple(INS_GENERATE_KEYPAIR); @@ -836,6 +863,9 @@ namespace hw { #ifdef DEBUG_HWDEVICE crypto::secret_key sec_clear = hw::ledger::decrypt(sec); sec_x = sec_clear; + log_hexbuffer("generate_keys: [[OUT]] pub", (char*)pub.data, 32); + log_hexbuffer("generate_keys: [[OUT]] sec", (char*)sec_clear.data, 32); + crypto::secret_key_to_public_key(sec_x,pub_x); hw::ledger::check32("generate_keys", "pub", pub_x.data, pub.data); #endif @@ -850,7 +880,7 @@ namespace hw { #ifdef DEBUG_HWDEVICE const crypto::public_key pub_x = pub; - const crypto::secret_key sec_x = hw::ledger::decrypt(sec); + const crypto::secret_key sec_x = (sec == rct::rct2sk(rct::I)) ? sec: hw::ledger::decrypt(sec); crypto::key_derivation derivation_x; log_hexbuffer("generate_key_derivation: [[IN]] pub ", pub_x.data, 32); log_hexbuffer("generate_key_derivation: [[IN]] sec ", sec_x.data, 32); @@ -866,7 +896,6 @@ namespace hw { assert(is_fake_view_key(sec)); r = crypto::generate_key_derivation(pub, this->viewkey, derivation); } else { - int offset = set_command_header_noopt(INS_GEN_KEY_DERIVATION); //pub memmove(this->buffer_send+offset, pub.data, 32); @@ -885,11 +914,11 @@ namespace hw { } #ifdef DEBUG_HWDEVICE crypto::key_derivation derivation_clear ; - if ((this->mode == TRANSACTION_PARSE) && has_view_key) { - derivation_clear = derivation; - }else { - derivation_clear = hw::ledger::decrypt(derivation); - } + if ((this->mode == TRANSACTION_PARSE) && has_view_key) { + derivation_clear = derivation; + } else { + derivation_clear = hw::ledger::decrypt(derivation); + } hw::ledger::check32("generate_key_derivation", "derivation", derivation_x.data, derivation_clear.data); #endif @@ -1050,7 +1079,7 @@ namespace hw { bool rc = this->controle_device->secret_key_to_public_key(sec_x, pub_x); log_hexbuffer("secret_key_to_public_key: [[OUT]] pub", pub_x.data, 32); if (!rc){ - log_message("secret_key_to_public_key", "secret_key rejected"); + log_message("FAIL secret_key_to_public_key", "secret_key rejected"); } #endif @@ -1112,6 +1141,75 @@ namespace hw { /* TRANSACTION */ /* ======================================================================= */ + void device_ledger::generate_tx_proof(const crypto::hash &prefix_hash, + const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r, + crypto::signature &sig) { + + AUTO_LOCK_CMD(); + + #ifdef DEBUG_HWDEVICE + const crypto::hash prefix_hash_x = prefix_hash; + const crypto::public_key R_x = R; + const crypto::public_key A_x = A; + const boost::optional<crypto::public_key> B_x = B; + const crypto::public_key D_x = D; + const crypto::secret_key r_x = hw::ledger::decrypt(r); + crypto::signature sig_x; + log_hexbuffer("generate_tx_proof: [[IN]] prefix_hash ", prefix_hash_x.data, 32); + log_hexbuffer("generate_tx_proof: [[IN]] R ", R_x.data, 32); + log_hexbuffer("generate_tx_proof: [[IN]] A ", A_x.data, 32); + if (B_x) { + log_hexbuffer("generate_tx_proof: [[IN]] B ", (*B_x).data, 32); + } + log_hexbuffer("generate_tx_proof: [[IN]] D ", D_x.data, 32); + log_hexbuffer("generate_tx_proof: [[IN]] r ", r_x.data, 32); + #endif + + + int offset = set_command_header(INS_GET_TX_PROOF); + //options + this->buffer_send[offset] = B?0x01:0x00; + offset += 1; + //prefix_hash + memmove(&this->buffer_send[offset], prefix_hash.data, 32); + offset += 32; + // R + memmove(&this->buffer_send[offset], R.data, 32); + offset += 32; + // A + memmove(&this->buffer_send[offset], A.data, 32); + offset += 32; + // B + if (B) { + memmove(&this->buffer_send[offset], (*B).data, 32); + } else { + memset(&this->buffer_send[offset], 0, 32); + } + offset += 32; + // D + memmove(&this->buffer_send[offset], D.data, 32); + offset += 32; + // r + memmove(&this->buffer_send[offset], r.data, 32); + offset += 32; + + this->buffer_send[4] = offset-5; + this->length_send = offset; + this->exchange(); + + memmove(sig.c.data, &this->buffer_recv[0], 32); + memmove(sig.r.data, &this->buffer_recv[32], 32); + #ifdef DEBUG_HWDEVICE + log_hexbuffer("GENERATE_TX_PROOF: **c** ", sig.c.data, sizeof( sig.c.data)); + log_hexbuffer("GENERATE_TX_PROOF: **r** ", sig.r.data, sizeof( sig.r.data)); + + this->controle_device->generate_tx_proof(prefix_hash_x, R_x, A_x, B_x, D_x, r_x, sig_x); + hw::ledger::check32("generate_tx_proof", "c", sig_x.c.data, sig.c.data); + hw::ledger::check32("generate_tx_proof", "r", sig_x.r.data, sig.r.data); + + #endif + } + bool device_ledger::open_tx(crypto::secret_key &tx_key) { AUTO_LOCK_CMD(); @@ -1130,7 +1228,11 @@ namespace hw { this->exchange(); memmove(tx_key.data, &this->buffer_recv[32], 32); - + #ifdef DEBUG_HWDEVICE + const crypto::secret_key r_x = hw::ledger::decrypt(tx_key); + log_hexbuffer("open_tx: [[OUT]] R ", (char*)&this->buffer_recv[0], 32); + log_hexbuffer("open_tx: [[OUT]] r ", r_x.data, 32); + #endif return true; } @@ -1141,7 +1243,11 @@ namespace hw { const crypto::public_key public_key_x = public_key; const crypto::secret_key secret_key_x = hw::ledger::decrypt(secret_key); crypto::hash8 payment_id_x = payment_id; + log_hexbuffer("encrypt_payment_id: [[IN]] payment_id ", payment_id_x.data, 32); + log_hexbuffer("encrypt_payment_id: [[IN]] public_key ", public_key_x.data, 32); + log_hexbuffer("encrypt_payment_id: [[IN]] secret_key ", secret_key_x.data, 32); this->controle_device->encrypt_payment_id(payment_id_x, public_key_x, secret_key_x); + log_hexbuffer("encrypt_payment_id: [[OUT]] payment_id ", payment_id_x.data, 32); #endif int offset = set_command_header_noopt(INS_STEALTH); @@ -1178,49 +1284,58 @@ namespace hw { #ifdef DEBUG_HWDEVICE const size_t &tx_version_x = tx_version; - const cryptonote::account_keys sender_account_keys_x = sender_account_keys; + const cryptonote::account_keys sender_account_keys_x = hw::ledger::decrypt(sender_account_keys); memmove((void*)sender_account_keys_x.m_view_secret_key.data, dbg_viewkey.data, 32); - const crypto::public_key &txkey_pub_x = txkey_pub; - const crypto::secret_key &tx_key_x = tx_key; - const cryptonote::tx_destination_entry &dst_entr_x = dst_entr; - const boost::optional<cryptonote::account_public_address> &change_addr_x = change_addr; - const size_t &output_index_x = output_index; - const bool &need_additional_txkeys_x = need_additional_txkeys; - const std::vector<crypto::secret_key> &additional_tx_keys_x = additional_tx_keys; + const crypto::public_key txkey_pub_x = txkey_pub; + const crypto::secret_key tx_key_x = hw::ledger::decrypt(tx_key); + const cryptonote::tx_destination_entry dst_entr_x = dst_entr; + const boost::optional<cryptonote::account_public_address> change_addr_x = change_addr; + const size_t output_index_x = output_index; + const bool need_additional_txkeys_x = need_additional_txkeys; + + std::vector<crypto::secret_key> additional_tx_keys_x; + for (const auto k: additional_tx_keys) { + additional_tx_keys_x.push_back(hw::ledger::decrypt(k)); + } + std::vector<crypto::public_key> additional_tx_public_keys_x; std::vector<rct::key> amount_keys_x; crypto::public_key out_eph_public_key_x; + + log_message ("generate_output_ephemeral_keys: [[IN]] tx_version", std::to_string(tx_version_x)); + //log_hexbuffer("generate_output_ephemeral_keys: [[IN]] sender_account_keys.view", sender_account_keys.m_sview_secret_key.data, 32); + //log_hexbuffer("generate_output_ephemeral_keys: [[IN]] sender_account_keys.spend", sender_account_keys.m_spend_secret_key.data, 32); + log_hexbuffer("generate_output_ephemeral_keys: [[IN]] txkey_pub", txkey_pub_x.data, 32); + log_hexbuffer("generate_output_ephemeral_keys: [[IN]] tx_key", tx_key_x.data, 32); + log_hexbuffer("generate_output_ephemeral_keys: [[IN]] dst_entr.view", dst_entr_x.addr.m_view_public_key.data, 32); + log_hexbuffer("generate_output_ephemeral_keys: [[IN]] dst_entr.spend", dst_entr_x.addr.m_spend_public_key.data, 32); + if (change_addr) { + log_hexbuffer("generate_output_ephemeral_keys: [[IN]] change_addr.view", (*change_addr_x).m_view_public_key.data, 32); + log_hexbuffer("generate_output_ephemeral_keys: [[IN]] change_addr.spend", (*change_addr_x).m_spend_public_key.data, 32); + } + log_message ("generate_output_ephemeral_keys: [[IN]] output_index", std::to_string(output_index_x)); + log_message ("generate_output_ephemeral_keys: [[IN]] need_additional_txkeys", std::to_string(need_additional_txkeys_x)); + if(need_additional_txkeys_x) { + log_hexbuffer("generate_output_ephemeral_keys: [[IN]] additional_tx_keys[oi]", additional_tx_keys_x[output_index].data, 32); + } this->controle_device->generate_output_ephemeral_keys(tx_version_x, sender_account_keys_x, txkey_pub_x, tx_key_x, dst_entr_x, change_addr_x, output_index_x, need_additional_txkeys_x, additional_tx_keys_x, additional_tx_public_keys_x, amount_keys_x, out_eph_public_key_x); + if(need_additional_txkeys_x) { + log_hexbuffer("additional_tx_public_keys_x: [[OUT]] additional_tx_public_keys_x", additional_tx_public_keys_x.back().data, 32); + } + log_hexbuffer("generate_output_ephemeral_keys: [[OUT]] amount_keys ", (char*)amount_keys_x.back().bytes, 32); + log_hexbuffer("generate_output_ephemeral_keys: [[OUT]] out_eph_public_key ", out_eph_public_key_x.data, 32); #endif + ASSERT_X(tx_version > 1, "TX version not supported"<<tx_version); + // make additional tx pubkey if necessary cryptonote::keypair additional_txkey; if (need_additional_txkeys) { additional_txkey.sec = additional_tx_keys[output_index]; } - //compute derivation, out_eph_public_key, and amount key in one shot on device, to ensure checkable link - const crypto::secret_key *sec; - bool is_change; - - if (change_addr && dst_entr.addr == *change_addr) - { - // sending change to yourself; derivation = a*R - is_change = true; - sec = &sender_account_keys.m_view_secret_key; - } - else - { - is_change = false; - if (dst_entr.is_subaddress && need_additional_txkeys) { - sec = &additional_txkey.sec; - } else { - sec = &tx_key; - } - } - int offset = set_command_header_noopt(INS_GEN_TXOUT_KEYS); //tx_version this->buffer_send[offset+0] = tx_version>>24; @@ -1228,8 +1343,11 @@ namespace hw { this->buffer_send[offset+2] = tx_version>>8; this->buffer_send[offset+3] = tx_version>>0; offset += 4; - //tx_sec - memmove(&this->buffer_send[offset], sec->data, 32); + //tx_key + memmove(&this->buffer_send[offset], tx_key.data, 32); + offset += 32; + //txkey_pub + memmove(&this->buffer_send[offset], txkey_pub.data, 32); offset += 32; //Aout memmove(&this->buffer_send[offset], dst_entr.addr.m_view_public_key.data, 32); @@ -1244,6 +1362,7 @@ namespace hw { this->buffer_send[offset+3] = output_index>>0; offset += 4; //is_change, + bool is_change = (change_addr && dst_entr.addr == *change_addr); this->buffer_send[offset] = is_change; offset++; //is_subaddress @@ -1252,6 +1371,13 @@ namespace hw { //need_additional_key this->buffer_send[offset] = need_additional_txkeys; offset++; + //additional_tx_key + if (need_additional_txkeys) { + memmove(&this->buffer_send[offset], additional_txkey.sec.data, 32); + } else { + memset(&this->buffer_send[offset], 0, 32); + } + offset += 32; this->buffer_send[4] = offset-5; this->length_send = offset; @@ -1259,15 +1385,8 @@ namespace hw { offset = 0; unsigned int recv_len = this->length_recv; - if (need_additional_txkeys) - { - ASSERT_X(recv_len>=32, "Not enought data from device"); - memmove(additional_txkey.pub.data, &this->buffer_recv[offset], 32); - additional_tx_public_keys.push_back(additional_txkey.pub); - offset += 32; - recv_len -= 32; - } - if (tx_version > 1) + + //if (tx_version > 1) { ASSERT_X(recv_len>=32, "Not enought data from device"); crypto::secret_key scalar1; @@ -1279,6 +1398,16 @@ namespace hw { ASSERT_X(recv_len>=32, "Not enought data from device"); memmove(out_eph_public_key.data, &this->buffer_recv[offset], 32); recv_len -= 32; + offset += 32; + + if (need_additional_txkeys) + { + ASSERT_X(recv_len>=32, "Not enought data from device"); + memmove(additional_txkey.pub.data, &this->buffer_recv[offset], 32); + additional_tx_public_keys.push_back(additional_txkey.pub); + offset += 32; + recv_len -= 32; + } // add ABPkeys this->add_output_key_mapping(dst_entr.addr.m_view_public_key, dst_entr.addr.m_spend_public_key, dst_entr.is_subaddress, is_change, @@ -1286,9 +1415,10 @@ namespace hw { amount_keys.back(), out_eph_public_key); #ifdef DEBUG_HWDEVICE + log_hexbuffer("generate_output_ephemeral_keys: clear amount_key", (const char*)hw::ledger::decrypt(amount_keys.back()).bytes, 32); hw::ledger::check32("generate_output_ephemeral_keys", "amount_key", (const char*)amount_keys_x.back().bytes, (const char*)hw::ledger::decrypt(amount_keys.back()).bytes); if (need_additional_txkeys) { - hw::ledger::check32("generate_output_ephemeral_keys", "additional_tx_key", additional_tx_keys_x.back().data, additional_tx_keys.back().data); + hw::ledger::check32("generate_output_ephemeral_keys", "additional_tx_key", additional_tx_public_keys_x.back().data, additional_tx_public_keys.back().data); } hw::ledger::check32("generate_output_ephemeral_keys", "out_eph_public_key", out_eph_public_key_x.data, out_eph_public_key.data); #endif @@ -1303,6 +1433,32 @@ namespace hw { return true; } + rct::key device_ledger::genCommitmentMask(const rct::key &AKout) { + #ifdef DEBUG_HWDEVICE + const rct::key AKout_x = hw::ledger::decrypt(AKout); + rct::key mask_x; + mask_x = this->controle_device->genCommitmentMask(AKout_x); + #endif + + rct::key mask; + int offset = set_command_header_noopt(INS_GEN_COMMITMENT_MASK); + // AKout + memmove(this->buffer_send+offset, AKout.bytes, 32); + offset += 32; + + this->buffer_send[4] = offset-5; + this->length_send = offset; + this->exchange(); + + memmove(mask.bytes, &this->buffer_recv[0], 32); + + #ifdef DEBUG_HWDEVICE + hw::ledger::check32("genCommitmentMask", "mask", (const char*)mask_x.bytes, (const char*)mask.bytes); + #endif + + return mask; + } + bool device_ledger::ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & AKout, bool short_amount) { AUTO_LOCK_CMD(); @@ -1334,6 +1490,7 @@ namespace hw { memmove(unmasked.mask.bytes, &this->buffer_recv[32], 32); #ifdef DEBUG_HWDEVICE + MDEBUG("ecdhEncode: Akout: "<<AKout_x); hw::ledger::check32("ecdhEncode", "amount", (char*)unmasked_x.amount.bytes, (char*)unmasked.amount.bytes); hw::ledger::check32("ecdhEncode", "mask", (char*)unmasked_x.mask.bytes, (char*)unmasked.mask.bytes); @@ -1374,6 +1531,7 @@ namespace hw { memmove(masked.mask.bytes, &this->buffer_recv[32], 32); #ifdef DEBUG_HWDEVICE + MDEBUG("ecdhEncode: Akout: "<<AKout_x); hw::ledger::check32("ecdhDecode", "amount", (char*)masked_x.amount.bytes, (char*)masked.amount.bytes); hw::ledger::check32("ecdhDecode", "mask", (char*)masked_x.mask.bytes,(char*) masked.mask.bytes); #endif diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp index 252354e1c..d4d98ce4a 100644 --- a/src/device/device_ledger.hpp +++ b/src/device/device_ledger.hpp @@ -33,6 +33,7 @@ #include <cstddef> #include <string> #include "device.hpp" +#include "log.hpp" #include "device_io_hid.hpp" #include <boost/thread/mutex.hpp> #include <boost/thread/recursive_mutex.hpp> @@ -41,6 +42,18 @@ namespace hw { namespace ledger { + /* Minimal supported version */ + #define MINIMAL_APP_VERSION_MAJOR 1 + #define MINIMAL_APP_VERSION_MINOR 3 + #define MINIMAL_APP_VERSION_MICRO 1 + + #define VERSION(M,m,u) ((M)<<16|(m)<<8|(u)) + #define VERSION_MAJOR(v) (((v)>>16)&0xFF) + #define VERSION_MINOR(v) (((v)>>8)&0xFF) + #define VERSION_MICRO(v) (((v)>>0)&0xFF) + + #define MINIMAL_APP_VERSION VERSION(MINIMAL_APP_VERSION_MAJOR, MINIMAL_APP_VERSION_MINOR, MINIMAL_APP_VERSION_MICRO) + void register_all(std::map<std::string, std::unique_ptr<device>> ®istry); #ifdef WITH_DEVICE_LEDGER @@ -190,11 +203,16 @@ namespace hw { /* ======================================================================= */ /* TRANSACTION */ /* ======================================================================= */ - + void generate_tx_proof(const crypto::hash &prefix_hash, + const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r, + crypto::signature &sig) override; + bool open_tx(crypto::secret_key &tx_key) override; bool encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key) override; + rct::key genCommitmentMask(const rct::key &amount_key) override; + bool ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & sharedSec, bool short_format) override; bool ecdhDecode(rct::ecdhTuple & masked, const rct::key & sharedSec, bool short_format) override; diff --git a/src/device/log.hpp b/src/device/log.hpp index fb7ba1fb0..bfe6e9edc 100644 --- a/src/device/log.hpp +++ b/src/device/log.hpp @@ -40,6 +40,19 @@ namespace hw { + /* Note about debug: + * To debug Device you can def the following : + * #define DEBUG_HWDEVICE + * Activate debug mechanism: + * - Add more trace + * - All computation done by device are checked by default device. + * Required IODUMMYCRYPT_HWDEVICE or IONOCRYPT_HWDEVICE for fully working + * #define IODUMMYCRYPT_HWDEVICE 1 + * - It assumes sensitive data encryption is is off on device side. a XOR with 0x55. This allow Ledger Class to make check on clear value + * #define IONOCRYPT_HWDEVICE 1 + * - It assumes sensitive data encryption is off on device side. + */ + void buffer_to_str(char *to_buff, size_t to_len, const char *buff, size_t len) ; void log_hexbuffer(const std::string &msg, const char* buff, size_t len); void log_message(const std::string &msg, const std::string &info ); diff --git a/src/device_trezor/trezor/protocol.cpp b/src/device_trezor/trezor/protocol.cpp index db2444df6..5fe08abbe 100644 --- a/src/device_trezor/trezor/protocol.cpp +++ b/src/device_trezor/trezor/protocol.cpp @@ -410,8 +410,8 @@ namespace tx { } } - static unsigned get_rsig_type(bool use_bulletproof, size_t num_outputs){ - if (!use_bulletproof){ + static unsigned get_rsig_type(const rct::RCTConfig &rct_config, size_t num_outputs){ + if (rct_config.range_proof_type == rct::RangeProofBorromean){ return rct::RangeProofBorromean; } else if (num_outputs > BULLETPROOF_MAX_OUTPUTS){ return rct::RangeProofMultiOutputBulletproof; @@ -506,9 +506,9 @@ namespace tx { // Rsig decision auto rsig_data = tsx_data.mutable_rsig_data(); - m_ct.rsig_type = get_rsig_type(tx.use_bulletproofs, tx.splitted_dsts.size()); + m_ct.rsig_type = get_rsig_type(tx.rct_config, tx.splitted_dsts.size()); rsig_data->set_rsig_type(m_ct.rsig_type); - if (tx.use_bulletproofs){ + if (tx.rct_config.range_proof_type != rct::RangeProofBorromean){ m_ct.bp_version = (m_aux_data->bp_version ? m_aux_data->bp_version.get() : 1); rsig_data->set_bp_version((uint32_t) m_ct.bp_version); } diff --git a/src/device_trezor/trezor/protocol.hpp b/src/device_trezor/trezor/protocol.hpp index 98ca52565..f58bf1039 100644 --- a/src/device_trezor/trezor/protocol.hpp +++ b/src/device_trezor/trezor/protocol.hpp @@ -281,7 +281,7 @@ namespace tx { } bool is_req_bulletproof() const { - return m_ct.tx_data.use_bulletproofs; + return m_ct.tx_data.rct_config.range_proof_type != rct::RangeProofBorromean; } bool is_bulletproof() const { diff --git a/src/mnemonics/electrum-words.cpp b/src/mnemonics/electrum-words.cpp index 48c9ab1ba..2dd40cc9a 100644 --- a/src/mnemonics/electrum-words.cpp +++ b/src/mnemonics/electrum-words.cpp @@ -340,9 +340,7 @@ namespace crypto const size_t expected = len * 3 / 32; if (seed.size() == expected/2) { - dst += ' '; // if electrum 12-word seed, duplicate - dst += dst; // if electrum 12-word seed, duplicate - dst.pop_back(); // trailing space + dst.append(dst.data(), dst.size()); // if electrum 12-word seed, duplicate } } diff --git a/src/net/CMakeLists.txt b/src/net/CMakeLists.txt index 8a3ee9e6f..738f858f0 100644 --- a/src/net/CMakeLists.txt +++ b/src/net/CMakeLists.txt @@ -26,8 +26,8 @@ # 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(net_sources error.cpp parse.cpp socks.cpp tor_address.cpp i2p_address.cpp) -set(net_headers error.h parse.h socks.h tor_address.h i2p_address.h) +set(net_sources error.cpp i2p_address.cpp parse.cpp socks.cpp socks_connect.cpp tor_address.cpp) +set(net_headers error.h i2p_address.h parse.h socks.h socks_connect.h tor_address.h) monero_add_library(net ${net_sources} ${net_headers}) target_link_libraries(net common epee ${Boost_ASIO_LIBRARY}) diff --git a/src/net/socks.cpp b/src/net/socks.cpp index 53154369b..5a27e16f4 100644 --- a/src/net/socks.cpp +++ b/src/net/socks.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2018, The Monero Project +// Copyright (c) 2018-2019, The Monero Project // // All rights reserved. // @@ -193,7 +193,7 @@ namespace socks else if (bytes < self.buffer().size()) self.done(socks::error::bad_write, std::move(self_)); else - boost::asio::async_read(self.proxy_, get_buffer(self), completed{std::move(self_)}); + boost::asio::async_read(self.proxy_, get_buffer(self), self.strand_.wrap(completed{std::move(self_)})); } } }; @@ -215,13 +215,13 @@ namespace socks if (error) self.done(error, std::move(self_)); else - boost::asio::async_write(self.proxy_, get_buffer(self), read{std::move(self_)}); + boost::asio::async_write(self.proxy_, get_buffer(self), self.strand_.wrap(read{std::move(self_)})); } } }; client::client(stream_type::socket&& proxy, socks::version ver) - : proxy_(std::move(proxy)), buffer_size_(0), buffer_(), ver_(ver) + : proxy_(std::move(proxy)), strand_(GET_IO_SERVICE(proxy_)), buffer_size_(0), buffer_(), ver_(ver) {} client::~client() {} @@ -296,7 +296,7 @@ namespace socks if (self && !self->buffer().empty()) { client& alias = *self; - alias.proxy_.async_connect(proxy_address, write{std::move(self)}); + alias.proxy_.async_connect(proxy_address, alias.strand_.wrap(write{std::move(self)})); return true; } return false; @@ -307,10 +307,26 @@ namespace socks if (self && !self->buffer().empty()) { client& alias = *self; - boost::asio::async_write(alias.proxy_, write::get_buffer(alias), read{std::move(self)}); + boost::asio::async_write(alias.proxy_, write::get_buffer(alias), alias.strand_.wrap(read{std::move(self)})); return true; } return false; } + + void client::async_close::operator()(boost::system::error_code error) + { + if (self_ && error != boost::system::errc::operation_canceled) + { + const std::shared_ptr<client> self = std::move(self_); + self->strand_.dispatch([self] () + { + if (self && self->proxy_.is_open()) + { + self->proxy_.shutdown(boost::asio::ip::tcp::socket::shutdown_both); + self->proxy_.close(); + } + }); + } + } } // socks } // net diff --git a/src/net/socks.h b/src/net/socks.h index 825937792..4d1d34e9e 100644 --- a/src/net/socks.h +++ b/src/net/socks.h @@ -1,4 +1,4 @@ -// Copyright (c) 2018, The Monero Project +// Copyright (c) 2018-2019, The Monero Project // // All rights reserved. // @@ -31,6 +31,7 @@ #include <cstdint> #include <boost/asio/ip/tcp.hpp> #include <boost/asio/io_service.hpp> +#include <boost/asio/strand.hpp> #include <boost/system/error_code.hpp> #include <boost/type_traits/integral_constant.hpp> #include <boost/utility/string_ref.hpp> @@ -92,6 +93,7 @@ namespace socks class client { boost::asio::ip::tcp::socket proxy_; + boost::asio::io_service::strand strand_; std::uint16_t buffer_size_; std::uint8_t buffer_[1024]; socks::version ver_; @@ -168,6 +170,8 @@ namespace socks \note Must use one of the `self->set_*_command` calls before using this function. + \note Only `async_close` can be invoked on `self` until the `done` + callback is invoked. \param self ownership of object is given to function. \param proxy_address of the socks server. @@ -182,11 +186,21 @@ namespace socks \note Must use one of the `self->set_*_command` calls before using the function. + \note Only `async_close` can be invoked on `self` until the `done` + callback is invoked. \param self ownership of object is given to function. \return False if `self->buffer().empty()` (no command set). */ static bool send(std::shared_ptr<client> self); + + /*! Callback for closing socket. Thread-safe with `*send` functions; + never blocks (uses strands). */ + struct async_close + { + std::shared_ptr<client> self_; + void operator()(boost::system::error_code error = boost::system::error_code{}); + }; }; template<typename Handler> diff --git a/src/net/socks_connect.cpp b/src/net/socks_connect.cpp new file mode 100644 index 000000000..a5557f6f8 --- /dev/null +++ b/src/net/socks_connect.cpp @@ -0,0 +1,90 @@ +// Copyright (c) 2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "socks_connect.h" + +#include <boost/system/error_code.hpp> +#include <boost/system/system_error.hpp> +#include <cstdint> +#include <memory> +#include <system_error> + +#include "net/error.h" +#include "net/net_utils_base.h" +#include "net/socks.h" +#include "string_tools.h" + +namespace net +{ +namespace socks +{ + boost::unique_future<boost::asio::ip::tcp::socket> + connector::operator()(const std::string& remote_host, const std::string& remote_port, boost::asio::steady_timer& timeout) const + { + struct future_socket + { + boost::promise<boost::asio::ip::tcp::socket> result_; + + void operator()(boost::system::error_code error, boost::asio::ip::tcp::socket&& socket) + { + if (error) + result_.set_exception(boost::system::system_error{error}); + else + result_.set_value(std::move(socket)); + } + }; + + boost::unique_future<boost::asio::ip::tcp::socket> out{}; + { + std::uint16_t port = 0; + if (!epee::string_tools::get_xtype_from_string(port, remote_port)) + throw std::system_error{net::error::invalid_port, "Remote port for socks proxy"}; + + bool is_set = false; + std::uint32_t ip_address = 0; + boost::promise<boost::asio::ip::tcp::socket> result{}; + out = result.get_future(); + const auto proxy = net::socks::make_connect_client( + boost::asio::ip::tcp::socket{GET_IO_SERVICE(timeout)}, net::socks::version::v4a, future_socket{std::move(result)} + ); + + if (epee::string_tools::get_ip_int32_from_string(ip_address, remote_host)) + is_set = proxy->set_connect_command(epee::net_utils::ipv4_network_address{ip_address, port}); + else + is_set = proxy->set_connect_command(remote_host, port); + + if (!is_set || !net::socks::client::connect_and_send(proxy, proxy_address)) + throw std::system_error{net::error::invalid_host, "Address for socks proxy"}; + + timeout.async_wait(net::socks::client::async_close{std::move(proxy)}); + } + + return out; + } +} // socks +} // net diff --git a/src/net/socks_connect.h b/src/net/socks_connect.h new file mode 100644 index 000000000..44b0fa2b3 --- /dev/null +++ b/src/net/socks_connect.h @@ -0,0 +1,55 @@ +// Copyright (c) 2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/steady_timer.hpp> +#include <boost/thread/future.hpp> +#include <string> + +namespace net +{ +namespace socks +{ + //! Primarily for use with `epee::net_utils::http_client`. + struct connector + { + boost::asio::ip::tcp::endpoint proxy_address; + + /*! Creates a new socket, asynchronously connects to `proxy_address`, + and requests a connection to `remote_host` on `remote_port`. Sets + socket as closed if `timeout` is reached. + + \return The socket if successful, and exception in the future with + error otherwise. */ + boost::unique_future<boost::asio::ip::tcp::socket> + operator()(const std::string& remote_host, const std::string& remote_port, boost::asio::steady_timer& timeout) const; + }; +} // socks +} // net diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index ba6e79d3f..f0aef384f 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -502,7 +502,7 @@ namespace nodetool else { memcpy(&m_network_id, &::config::NETWORK_ID, 16); - if (m_exclusive_peers.empty()) + if (m_exclusive_peers.empty() && !m_offline) { // for each hostname in the seed nodes list, attempt to DNS resolve and // add the result addresses as seed nodes @@ -2236,11 +2236,10 @@ namespace nodetool template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::set_rate_up_limit(const boost::program_options::variables_map& vm, int64_t limit) { - this->islimitup=true; + this->islimitup=(limit != -1) && (limit != default_limit_up); if (limit==-1) { limit=default_limit_up; - this->islimitup=false; } epee::net_utils::connection<epee::levin::async_protocol_handler<p2p_connection_context> >::set_rate_up_limit( limit ); @@ -2251,10 +2250,9 @@ namespace nodetool template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::set_rate_down_limit(const boost::program_options::variables_map& vm, int64_t limit) { - this->islimitdown=true; + this->islimitdown=(limit != -1) && (limit != default_limit_down); if(limit==-1) { limit=default_limit_down; - this->islimitdown=false; } epee::net_utils::connection<epee::levin::async_protocol_handler<p2p_connection_context> >::set_rate_down_limit( limit ); MINFO("Set limit-down to " << limit << " kB/s"); diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp index e39ba16fd..b5499262f 100644 --- a/src/ringct/rctOps.cpp +++ b/src/ringct/rctOps.cpp @@ -408,10 +408,10 @@ namespace rct { return res; } - //Computes aL where L is the curve order - bool isInMainSubgroup(const key & a) { + //Computes lA where l is the curve order + bool isInMainSubgroup(const key & A) { ge_p3 p3; - return toPointCheckOrder(&p3, a.bytes); + return toPointCheckOrder(&p3, A.bytes); } //Curve addition / subtractions diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index 25571238e..e877c13ce 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -79,12 +79,12 @@ namespace } namespace rct { - Bulletproof proveRangeBulletproof(keyV &C, keyV &masks, const std::vector<uint64_t> &amounts, epee::span<const key> sk) + Bulletproof proveRangeBulletproof(keyV &C, keyV &masks, const std::vector<uint64_t> &amounts, epee::span<const key> sk, hw::device &hwdev) { CHECK_AND_ASSERT_THROW_MES(amounts.size() == sk.size(), "Invalid amounts/sk sizes"); masks.resize(amounts.size()); for (size_t i = 0; i < masks.size(); ++i) - masks[i] = genCommitmentMask(sk[i]); + masks[i] = hwdev.genCommitmentMask(sk[i]); Bulletproof proof = bulletproof_PROVE(amounts, masks); CHECK_AND_ASSERT_THROW_MES(proof.V.size() == amounts.size(), "V does not have the expected size"); C = proof.V; @@ -804,7 +804,7 @@ namespace rct { else { const epee::span<const key> keys{&amount_keys[0], amount_keys.size()}; - rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, outamounts, keys)); + rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, outamounts, keys, hwdev)); #ifdef DBG CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof"); #endif @@ -833,7 +833,7 @@ namespace rct { else { const epee::span<const key> keys{&amount_keys[amounts_proved], batch_size}; - rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, batch_amounts, keys)); + rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, batch_amounts, keys, hwdev)); #ifdef DBG CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof"); #endif diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 56b0361a7..f5f1a2f9a 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -70,6 +70,13 @@ namespace { return (value + quantum - 1) / quantum * quantum; } + + void store_difficulty(cryptonote::difficulty_type difficulty, uint64_t &sdiff, std::string &swdiff, uint64_t &stop64) + { + sdiff = (difficulty << 64 >> 64).convert_to<uint64_t>(); + swdiff = difficulty.convert_to<std::string>(); + stop64 = (difficulty >> 64).convert_to<uint64_t>(); + } } namespace cryptonote @@ -219,7 +226,7 @@ namespace cryptonote ++res.height; // turn top block height into blockchain height res.top_block_hash = string_tools::pod_to_hex(top_hash); res.target_height = m_core.get_target_blockchain_height(); - res.difficulty = m_core.get_blockchain_storage().get_difficulty_for_next_block(); + store_difficulty(m_core.get_blockchain_storage().get_difficulty_for_next_block(), res.difficulty, res.wide_difficulty, res.difficulty_top64); res.target = m_core.get_blockchain_storage().get_difficulty_target(); res.tx_count = m_core.get_blockchain_storage().get_total_transactions() - res.height; //without coinbase res.tx_pool_size = m_core.get_pool_transactions_count(); @@ -236,7 +243,8 @@ namespace cryptonote res.testnet = net_type == TESTNET; res.stagenet = net_type == STAGENET; res.nettype = net_type == MAINNET ? "mainnet" : net_type == TESTNET ? "testnet" : net_type == STAGENET ? "stagenet" : "fakechain"; - res.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.height - 1); + store_difficulty(m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.height - 1), + res.cumulative_difficulty, res.wide_cumulative_difficulty, res.cumulative_difficulty_top64); res.block_size_limit = res.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit(); res.block_size_median = res.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median(); res.status = CORE_RPC_STATUS_OK; @@ -260,6 +268,23 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_get_net_stats(const COMMAND_RPC_GET_NET_STATS::request& req, COMMAND_RPC_GET_NET_STATS::response& res, const connection_context *ctx) + { + PERF_TIMER(on_get_net_stats); + // No bootstrap daemon check: Only ever get stats about local server + res.start_time = (uint64_t)m_core.get_start_time(); + { + CRITICAL_REGION_LOCAL(epee::net_utils::network_throttle_manager::m_lock_get_global_throttle_in); + epee::net_utils::network_throttle_manager::get_global_throttle_in().get_stats(res.total_packets_in, res.total_bytes_in); + } + { + CRITICAL_REGION_LOCAL(epee::net_utils::network_throttle_manager::m_lock_get_global_throttle_out); + epee::net_utils::network_throttle_manager::get_global_throttle_out().get_stats(res.total_packets_out, res.total_bytes_out); + } + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ class pruned_transaction { transaction& tx; public: @@ -1196,13 +1221,15 @@ namespace cryptonote block b; cryptonote::blobdata blob_reserve; blob_reserve.resize(req.reserve_size, 0); - if(!m_core.get_block_template(b, info.address, res.difficulty, res.height, res.expected_reward, blob_reserve)) + cryptonote::difficulty_type wdiff; + if(!m_core.get_block_template(b, info.address, wdiff, res.height, res.expected_reward, blob_reserve)) { error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; error_resp.message = "Internal error: failed to create block template"; LOG_ERROR("Failed to create block template"); return false; } + store_difficulty(wdiff, res.difficulty, res.wide_difficulty, res.difficulty_top64); blobdata block_blob = t_serializable_object_to_blob(b); crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(b.miner_tx); if(tx_pub_key == crypto::null_pkey) @@ -1375,13 +1402,16 @@ namespace cryptonote response.height = height; response.depth = m_core.get_current_blockchain_height() - height - 1; response.hash = string_tools::pod_to_hex(hash); - response.difficulty = m_core.get_blockchain_storage().block_difficulty(height); - response.cumulative_difficulty = response.block_weight = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(height); + store_difficulty(m_core.get_blockchain_storage().block_difficulty(height), + response.difficulty, response.wide_difficulty, response.difficulty_top64); + store_difficulty(m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(height), + response.cumulative_difficulty, response.wide_cumulative_difficulty, response.cumulative_difficulty_top64); response.reward = get_block_reward(blk); response.block_size = response.block_weight = m_core.get_blockchain_storage().get_db().get_block_weight(height); response.num_txes = blk.tx_hashes.size(); response.pow_hash = fill_pow_hash ? string_tools::pod_to_hex(get_block_longhash(blk, height)) : ""; response.long_term_weight = m_core.get_blockchain_storage().get_db().get_block_long_term_weight(height); + response.miner_tx_hash = string_tools::pod_to_hex(cryptonote::get_transaction_hash(blk.miner_tx)); return true; } //------------------------------------------------------------------------------------------------------------------------------ @@ -1707,7 +1737,8 @@ namespace cryptonote ++res.height; // turn top block height into blockchain height res.top_block_hash = string_tools::pod_to_hex(top_hash); res.target_height = m_core.get_target_blockchain_height(); - res.difficulty = m_core.get_blockchain_storage().get_difficulty_for_next_block(); + store_difficulty(m_core.get_blockchain_storage().get_difficulty_for_next_block(), + res.difficulty, res.wide_difficulty, res.difficulty_top64); res.target = m_core.get_blockchain_storage().get_current_hard_fork_version() < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2; res.tx_count = m_core.get_blockchain_storage().get_total_transactions() - res.height; //without coinbase res.tx_pool_size = m_core.get_pool_transactions_count(); @@ -1725,7 +1756,8 @@ namespace cryptonote res.stagenet = net_type == STAGENET; res.nettype = net_type == MAINNET ? "mainnet" : net_type == TESTNET ? "testnet" : net_type == STAGENET ? "stagenet" : "fakechain"; - res.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.height - 1); + store_difficulty(m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.height - 1), + res.cumulative_difficulty, res.wide_cumulative_difficulty, res.cumulative_difficulty_top64); res.block_size_limit = res.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit(); res.block_size_median = res.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median(); res.status = CORE_RPC_STATUS_OK; @@ -1741,7 +1773,9 @@ namespace cryptonote boost::shared_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex); res.was_bootstrap_ever_used = m_was_bootstrap_ever_used; } - res.database_size = restricted ? 0 : m_core.get_blockchain_storage().get_db().get_database_size(); + res.database_size = m_core.get_blockchain_storage().get_db().get_database_size(); + if (restricted) + res.database_size = round_up(res.database_size, 5ull * 1024 * 1024 * 1024); res.update_available = restricted ? false : m_core.is_update_available(); res.version = restricted ? "" : MONERO_VERSION; return true; @@ -1947,7 +1981,9 @@ namespace cryptonote std::list<std::pair<Blockchain::block_extended_info, std::vector<crypto::hash>>> chains = m_core.get_blockchain_storage().get_alternative_chains(); for (const auto &i: chains) { - res.chains.push_back(COMMAND_RPC_GET_ALTERNATE_CHAINS::chain_info{epee::string_tools::pod_to_hex(get_block_hash(i.first.bl)), i.first.height, i.second.size(), i.first.cumulative_difficulty, {}, std::string()}); + difficulty_type wdiff = i.first.cumulative_difficulty; + res.chains.push_back(COMMAND_RPC_GET_ALTERNATE_CHAINS::chain_info{epee::string_tools::pod_to_hex(get_block_hash(i.first.bl)), i.first.height, i.second.size(), 0, "", 0, {}, std::string()}); + store_difficulty(wdiff, res.chains.back().difficulty, res.chains.back().wide_difficulty, res.chains.back().difficulty_top64); res.chains.back().block_hashes.reserve(i.second.size()); for (const crypto::hash &block_id: i.second) res.chains.back().block_hashes.push_back(epee::string_tools::pod_to_hex(block_id)); diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index fe066b31b..8f5d83f1b 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -115,6 +115,7 @@ namespace cryptonote MAP_URI_AUTO_JON2_IF("/stop_daemon", on_stop_daemon, COMMAND_RPC_STOP_DAEMON, !m_restricted) MAP_URI_AUTO_JON2("/get_info", on_get_info, COMMAND_RPC_GET_INFO) MAP_URI_AUTO_JON2("/getinfo", on_get_info, COMMAND_RPC_GET_INFO) + MAP_URI_AUTO_JON2_IF("/get_net_stats", on_get_net_stats, COMMAND_RPC_GET_NET_STATS, !m_restricted) MAP_URI_AUTO_JON2("/get_limit", on_get_limit, COMMAND_RPC_GET_LIMIT) MAP_URI_AUTO_JON2_IF("/set_limit", on_set_limit, COMMAND_RPC_SET_LIMIT, !m_restricted) MAP_URI_AUTO_JON2_IF("/out_peers", on_out_peers, COMMAND_RPC_OUT_PEERS, !m_restricted) @@ -179,6 +180,7 @@ namespace cryptonote bool on_get_outs_bin(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res, const connection_context *ctx = NULL); bool on_get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res, const connection_context *ctx = NULL); bool on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res, const connection_context *ctx = NULL); + bool on_get_net_stats(const COMMAND_RPC_GET_NET_STATS::request& req, COMMAND_RPC_GET_NET_STATS::response& res, const connection_context *ctx = NULL); bool on_save_bc(const COMMAND_RPC_SAVE_BC::request& req, COMMAND_RPC_SAVE_BC::response& res, const connection_context *ctx = NULL); bool on_get_peer_list(const COMMAND_RPC_GET_PEER_LIST::request& req, COMMAND_RPC_GET_PEER_LIST::response& res, const connection_context *ctx = NULL); bool on_set_log_hash_rate(const COMMAND_RPC_SET_LOG_HASH_RATE::request& req, COMMAND_RPC_SET_LOG_HASH_RATE::response& res, const connection_context *ctx = NULL); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index f65c7c8dd..1f14267f6 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -84,7 +84,7 @@ namespace cryptonote // advance which version they will stop working with // Don't go over 32767 for any of these #define CORE_RPC_VERSION_MAJOR 2 -#define CORE_RPC_VERSION_MINOR 4 +#define CORE_RPC_VERSION_MINOR 5 #define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR) @@ -252,223 +252,6 @@ namespace cryptonote }; //----------------------------------------------- - struct COMMAND_RPC_GET_ADDRESS_TXS - { - struct request_t - { - std::string address; - std::string view_key; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(address) - KV_SERIALIZE(view_key) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<request_t> request; - - struct spent_output { - uint64_t amount; - std::string key_image; - std::string tx_pub_key; - uint64_t out_index; - uint32_t mixin; - - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(amount) - KV_SERIALIZE(key_image) - KV_SERIALIZE(tx_pub_key) - KV_SERIALIZE(out_index) - KV_SERIALIZE(mixin) - END_KV_SERIALIZE_MAP() - }; - - struct transaction - { - uint64_t id; - std::string hash; - uint64_t timestamp; - uint64_t total_received; - uint64_t total_sent; - uint64_t unlock_time; - uint64_t height; - std::list<spent_output> spent_outputs; - std::string payment_id; - bool coinbase; - bool mempool; - uint32_t mixin; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(id) - KV_SERIALIZE(hash) - KV_SERIALIZE(timestamp) - KV_SERIALIZE(total_received) - KV_SERIALIZE(total_sent) - KV_SERIALIZE(unlock_time) - KV_SERIALIZE(height) - KV_SERIALIZE(spent_outputs) - KV_SERIALIZE(payment_id) - KV_SERIALIZE(coinbase) - KV_SERIALIZE(mempool) - KV_SERIALIZE(mixin) - END_KV_SERIALIZE_MAP() - }; - - - struct response_t - { - //std::list<std::string> txs_as_json; - uint64_t total_received; - uint64_t total_received_unlocked = 0; // OpenMonero only - uint64_t scanned_height; - std::vector<transaction> transactions; - uint64_t blockchain_height; - uint64_t scanned_block_height; - std::string status; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(total_received) - KV_SERIALIZE(total_received_unlocked) - KV_SERIALIZE(scanned_height) - KV_SERIALIZE(transactions) - KV_SERIALIZE(blockchain_height) - KV_SERIALIZE(scanned_block_height) - KV_SERIALIZE(status) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<response_t> response; - }; - - //----------------------------------------------- - struct COMMAND_RPC_GET_ADDRESS_INFO - { - struct request_t - { - std::string address; - std::string view_key; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(address) - KV_SERIALIZE(view_key) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<request_t> request; - - struct spent_output - { - uint64_t amount; - std::string key_image; - std::string tx_pub_key; - uint64_t out_index; - uint32_t mixin; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(amount) - KV_SERIALIZE(key_image) - KV_SERIALIZE(tx_pub_key) - KV_SERIALIZE(out_index) - KV_SERIALIZE(mixin) - END_KV_SERIALIZE_MAP() - }; - - struct response_t - { - uint64_t locked_funds; - uint64_t total_received; - uint64_t total_sent; - uint64_t scanned_height; - uint64_t scanned_block_height; - uint64_t start_height; - uint64_t transaction_height; - uint64_t blockchain_height; - std::list<spent_output> spent_outputs; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(locked_funds) - KV_SERIALIZE(total_received) - KV_SERIALIZE(total_sent) - KV_SERIALIZE(scanned_height) - KV_SERIALIZE(scanned_block_height) - KV_SERIALIZE(start_height) - KV_SERIALIZE(transaction_height) - KV_SERIALIZE(blockchain_height) - KV_SERIALIZE(spent_outputs) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<response_t> response; - }; - - //----------------------------------------------- - struct COMMAND_RPC_GET_UNSPENT_OUTS - { - struct request_t - { - std::string amount; - std::string address; - std::string view_key; - // OpenMonero specific - uint64_t mixin; - bool use_dust; - std::string dust_threshold; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(amount) - KV_SERIALIZE(address) - KV_SERIALIZE(view_key) - KV_SERIALIZE(mixin) - KV_SERIALIZE(use_dust) - KV_SERIALIZE(dust_threshold) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<request_t> request; - - - struct output { - uint64_t amount; - std::string public_key; - uint64_t index; - uint64_t global_index; - std::string rct; - std::string tx_hash; - std::string tx_pub_key; - std::string tx_prefix_hash; - std::vector<std::string> spend_key_images; - uint64_t timestamp; - uint64_t height; - - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(amount) - KV_SERIALIZE(public_key) - KV_SERIALIZE(index) - KV_SERIALIZE(global_index) - KV_SERIALIZE(rct) - KV_SERIALIZE(tx_hash) - KV_SERIALIZE(tx_pub_key) - KV_SERIALIZE(tx_prefix_hash) - KV_SERIALIZE(spend_key_images) - KV_SERIALIZE(timestamp) - KV_SERIALIZE(height) - END_KV_SERIALIZE_MAP() - }; - - struct response_t - { - uint64_t amount; - std::list<output> outputs; - uint64_t per_kb_fee; - std::string status; - std::string reason; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(amount) - KV_SERIALIZE(outputs) - KV_SERIALIZE(per_kb_fee) - KV_SERIALIZE(status) - KV_SERIALIZE(reason) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<response_t> response; - }; - - //----------------------------------------------- struct COMMAND_RPC_GET_RANDOM_OUTS { struct request_t @@ -548,72 +331,6 @@ namespace cryptonote typedef epee::misc_utils::struct_init<response_t> response; }; //----------------------------------------------- - struct COMMAND_RPC_LOGIN - { - struct request_t - { - std::string address; - std::string view_key; - bool create_account; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(address) - KV_SERIALIZE(view_key) - KV_SERIALIZE(create_account) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<request_t> request; - - struct response_t - { - std::string status; - std::string reason; - bool new_address; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - KV_SERIALIZE(reason) - KV_SERIALIZE(new_address) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<response_t> response; - }; - //----------------------------------------------- - struct COMMAND_RPC_IMPORT_WALLET_REQUEST - { - struct request_t - { - std::string address; - std::string view_key; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(address) - KV_SERIALIZE(view_key) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<request_t> request; - - struct response_t - { - std::string payment_id; - uint64_t import_fee; - bool new_request; - bool request_fulfilled; - std::string payment_address; - std::string status; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(payment_id) - KV_SERIALIZE(import_fee) - KV_SERIALIZE(new_request) - KV_SERIALIZE(request_fulfilled) - KV_SERIALIZE(payment_address) - KV_SERIALIZE(status) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<response_t> response; - }; - //----------------------------------------------- struct COMMAND_RPC_GET_TRANSACTIONS { struct request_t @@ -943,6 +660,8 @@ namespace cryptonote uint64_t height; uint64_t target_height; uint64_t difficulty; + std::string wide_difficulty; + uint64_t difficulty_top64; uint64_t target; uint64_t tx_count; uint64_t tx_pool_size; @@ -958,6 +677,8 @@ namespace cryptonote std::string nettype; std::string top_block_hash; uint64_t cumulative_difficulty; + std::string wide_cumulative_difficulty; + uint64_t cumulative_difficulty_top64; uint64_t block_size_limit; uint64_t block_weight_limit; uint64_t block_size_median; @@ -978,6 +699,8 @@ namespace cryptonote KV_SERIALIZE(height) KV_SERIALIZE(target_height) KV_SERIALIZE(difficulty) + KV_SERIALIZE(wide_difficulty) + KV_SERIALIZE(difficulty_top64) KV_SERIALIZE(target) KV_SERIALIZE(tx_count) KV_SERIALIZE(tx_pool_size) @@ -993,6 +716,8 @@ namespace cryptonote KV_SERIALIZE(nettype) KV_SERIALIZE(top_block_hash) KV_SERIALIZE(cumulative_difficulty) + KV_SERIALIZE(wide_cumulative_difficulty) + KV_SERIALIZE(cumulative_difficulty_top64) KV_SERIALIZE(block_size_limit) KV_SERIALIZE_OPT(block_weight_limit, (uint64_t)0) KV_SERIALIZE(block_size_median) @@ -1014,6 +739,39 @@ namespace cryptonote //----------------------------------------------- + struct COMMAND_RPC_GET_NET_STATS + { + struct request_t + { + + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<request_t> request; + + + struct response_t + { + std::string status; + uint64_t start_time; + uint64_t total_packets_in; + uint64_t total_bytes_in; + uint64_t total_packets_out; + uint64_t total_bytes_out; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + KV_SERIALIZE(start_time) + KV_SERIALIZE(total_packets_in) + KV_SERIALIZE(total_bytes_in) + KV_SERIALIZE(total_packets_out) + KV_SERIALIZE(total_bytes_out) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<response_t> response; + }; + + //----------------------------------------------- struct COMMAND_RPC_STOP_MINING { struct request_t @@ -1149,6 +907,8 @@ namespace cryptonote struct response_t { uint64_t difficulty; + std::string wide_difficulty; + uint64_t difficulty_top64; uint64_t height; uint64_t reserved_offset; uint64_t expected_reward; @@ -1160,6 +920,8 @@ namespace cryptonote BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(difficulty) + KV_SERIALIZE(wide_difficulty) + KV_SERIALIZE(difficulty_top64) KV_SERIALIZE(height) KV_SERIALIZE(reserved_offset) KV_SERIALIZE(expected_reward) @@ -1226,14 +988,19 @@ namespace cryptonote uint64_t height; uint64_t depth; std::string hash; - difficulty_type difficulty; - difficulty_type cumulative_difficulty; + uint64_t difficulty; + std::string wide_difficulty; + uint64_t difficulty_top64; + uint64_t cumulative_difficulty; + std::string wide_cumulative_difficulty; + uint64_t cumulative_difficulty_top64; uint64_t reward; uint64_t block_size; uint64_t block_weight; uint64_t num_txes; std::string pow_hash; uint64_t long_term_weight; + std::string miner_tx_hash; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(major_version) @@ -1246,13 +1013,18 @@ namespace cryptonote KV_SERIALIZE(depth) KV_SERIALIZE(hash) KV_SERIALIZE(difficulty) + KV_SERIALIZE(wide_difficulty) + KV_SERIALIZE(difficulty_top64) KV_SERIALIZE(cumulative_difficulty) + KV_SERIALIZE(wide_cumulative_difficulty) + KV_SERIALIZE(cumulative_difficulty_top64) KV_SERIALIZE(reward) KV_SERIALIZE(block_size) KV_SERIALIZE_OPT(block_weight, (uint64_t)0) KV_SERIALIZE(num_txes) KV_SERIALIZE(pow_hash) KV_SERIALIZE_OPT(long_term_weight, (uint64_t)0) + KV_SERIALIZE(miner_tx_hash) END_KV_SERIALIZE_MAP() }; @@ -2248,6 +2020,8 @@ namespace cryptonote uint64_t height; uint64_t length; uint64_t difficulty; + std::string wide_difficulty; + uint64_t difficulty_top64; std::vector<std::string> block_hashes; std::string main_chain_parent_block; @@ -2256,6 +2030,8 @@ namespace cryptonote KV_SERIALIZE(height) KV_SERIALIZE(length) KV_SERIALIZE(difficulty) + KV_SERIALIZE(wide_difficulty) + KV_SERIALIZE(difficulty_top64) KV_SERIALIZE(block_hashes) KV_SERIALIZE(main_chain_parent_block) END_KV_SERIALIZE_MAP() diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp index 14b492786..540afe6b9 100644 --- a/src/rpc/daemon_handler.cpp +++ b/src/rpc/daemon_handler.cpp @@ -263,20 +263,43 @@ namespace rpc void DaemonHandler::handle(const SendRawTx::Request& req, SendRawTx::Response& res) { - auto tx_blob = cryptonote::tx_to_blob(req.tx); + handleTxBlob(cryptonote::tx_to_blob(req.tx), req.relay, res); + } + + void DaemonHandler::handle(const SendRawTxHex::Request& req, SendRawTxHex::Response& res) + { + std::string tx_blob; + if(!epee::string_tools::parse_hexstr_to_binbuff(req.tx_as_hex, tx_blob)) + { + MERROR("[SendRawTxHex]: Failed to parse tx from hexbuff: " << req.tx_as_hex); + res.status = Message::STATUS_FAILED; + res.error_details = "Invalid hex"; + return; + } + handleTxBlob(tx_blob, req.relay, res); + } + + void DaemonHandler::handleTxBlob(const std::string& tx_blob, bool relay, SendRawTx::Response& res) + { + if (!m_p2p.get_payload_object().is_synchronized()) + { + res.status = Message::STATUS_FAILED; + res.error_details = "Not ready to accept transactions; try again later"; + return; + } cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context); tx_verification_context tvc = AUTO_VAL_INIT(tvc); - if(!m_core.handle_incoming_tx(tx_blob, tvc, false, false, !req.relay) || tvc.m_verifivation_failed) + if(!m_core.handle_incoming_tx(tx_blob, tvc, false, false, !relay) || tvc.m_verifivation_failed) { if (tvc.m_verifivation_failed) { - LOG_PRINT_L0("[on_send_raw_tx]: tx verification failed"); + MERROR("[SendRawTx]: tx verification failed"); } else { - LOG_PRINT_L0("[on_send_raw_tx]: Failed to process tx"); + MERROR("[SendRawTx]: Failed to process tx"); } res.status = Message::STATUS_FAILED; res.error_details = ""; @@ -328,9 +351,9 @@ namespace rpc return; } - if(!tvc.m_should_be_relayed || !req.relay) + if(!tvc.m_should_be_relayed || !relay) { - LOG_PRINT_L0("[on_send_raw_tx]: tx accepted, but not relayed"); + MERROR("[SendRawTx]: tx accepted, but not relayed"); res.error_details = "Not relayed"; res.relayed = false; res.status = Message::STATUS_OK; @@ -413,7 +436,8 @@ namespace rpc auto& chain = m_core.get_blockchain_storage(); - res.info.difficulty = chain.get_difficulty_for_next_block(); + res.info.wide_difficulty = chain.get_difficulty_for_next_block(); + res.info.difficulty = (res.info.wide_difficulty << 64 >> 64).convert_to<uint64_t>(); res.info.target = chain.get_difficulty_target(); @@ -434,7 +458,8 @@ namespace rpc res.info.mainnet = m_core.get_nettype() == MAINNET; res.info.testnet = m_core.get_nettype() == TESTNET; res.info.stagenet = m_core.get_nettype() == STAGENET; - res.info.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.info.height - 1); + res.info.wide_cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.info.height - 1); + res.info.cumulative_difficulty = (res.info.wide_cumulative_difficulty << 64 >> 64).convert_to<uint64_t>(); res.info.block_size_limit = res.info.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit(); res.info.block_size_median = res.info.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median(); res.info.start_time = (uint64_t)m_core.get_start_time(); @@ -803,7 +828,8 @@ namespace rpc header.reward += out.amount; } - header.difficulty = m_core.get_blockchain_storage().block_difficulty(header.height); + header.wide_difficulty = m_core.get_blockchain_storage().block_difficulty(header.height); + header.difficulty = (header.wide_difficulty << 64 >> 64).convert_to<uint64_t>(); return true; } @@ -830,6 +856,7 @@ namespace rpc REQ_RESP_TYPES_MACRO(request_type, KeyImagesSpent, req_json, resp_message, handle); REQ_RESP_TYPES_MACRO(request_type, GetTxGlobalOutputIndices, req_json, resp_message, handle); REQ_RESP_TYPES_MACRO(request_type, SendRawTx, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, SendRawTxHex, req_json, resp_message, handle); REQ_RESP_TYPES_MACRO(request_type, GetInfo, req_json, resp_message, handle); REQ_RESP_TYPES_MACRO(request_type, StartMining, req_json, resp_message, handle); REQ_RESP_TYPES_MACRO(request_type, StopMining, req_json, resp_message, handle); diff --git a/src/rpc/daemon_handler.h b/src/rpc/daemon_handler.h index 87161784e..34723f676 100644 --- a/src/rpc/daemon_handler.h +++ b/src/rpc/daemon_handler.h @@ -68,6 +68,8 @@ class DaemonHandler : public RpcHandler void handle(const SendRawTx::Request& req, SendRawTx::Response& res); + void handle(const SendRawTxHex::Request& req, SendRawTxHex::Response& res); + void handle(const StartMining::Request& req, StartMining::Response& res); void handle(const GetInfo::Request& req, GetInfo::Response& res); @@ -136,6 +138,8 @@ class DaemonHandler : public RpcHandler bool getBlockHeaderByHash(const crypto::hash& hash_in, cryptonote::rpc::BlockHeaderResponse& response); + void handleTxBlob(const std::string& tx_blob, bool relay, SendRawTx::Response& res); + cryptonote::core& m_core; t_p2p& m_p2p; }; diff --git a/src/rpc/daemon_messages.cpp b/src/rpc/daemon_messages.cpp index ecd3683e5..cf0f5ece1 100644 --- a/src/rpc/daemon_messages.cpp +++ b/src/rpc/daemon_messages.cpp @@ -42,6 +42,7 @@ const char* const GetTransactions::name = "get_transactions"; const char* const KeyImagesSpent::name = "key_images_spent"; const char* const GetTxGlobalOutputIndices::name = "get_tx_global_output_indices"; const char* const SendRawTx::name = "send_raw_tx"; +const char* const SendRawTxHex::name = "send_raw_tx_hex"; const char* const StartMining::name = "start_mining"; const char* const StopMining::name = "stop_mining"; const char* const MiningStatus::name = "mining_status"; @@ -292,6 +293,22 @@ void SendRawTx::Response::fromJson(rapidjson::Value& val) GET_FROM_JSON_OBJECT(val, relayed, relayed); } +rapidjson::Value SendRawTxHex::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + INSERT_INTO_JSON_OBJECT(val, doc, tx_as_hex, tx_as_hex); + INSERT_INTO_JSON_OBJECT(val, doc, relay, relay); + + return val; +} + +void SendRawTxHex::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, tx_as_hex, tx_as_hex); + GET_FROM_JSON_OBJECT(val, relay, relay); +} + rapidjson::Value StartMining::Request::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); diff --git a/src/rpc/daemon_messages.h b/src/rpc/daemon_messages.h index 8ce56b6af..c0d9aed0a 100644 --- a/src/rpc/daemon_messages.h +++ b/src/rpc/daemon_messages.h @@ -171,6 +171,14 @@ BEGIN_RPC_MESSAGE_CLASS(SendRawTx); END_RPC_MESSAGE_RESPONSE; END_RPC_MESSAGE_CLASS; +BEGIN_RPC_MESSAGE_CLASS(SendRawTxHex); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(std::string, tx_as_hex); + RPC_MESSAGE_MEMBER(bool, relay); + END_RPC_MESSAGE_REQUEST; + using Response = SendRawTx::Response; +END_RPC_MESSAGE_CLASS; + BEGIN_RPC_MESSAGE_CLASS(StartMining); BEGIN_RPC_MESSAGE_REQUEST; RPC_MESSAGE_MEMBER(std::string, miner_address); diff --git a/src/rpc/message_data_structs.h b/src/rpc/message_data_structs.h index 26c5038f6..2a43811cf 100644 --- a/src/rpc/message_data_structs.h +++ b/src/rpc/message_data_structs.h @@ -30,6 +30,7 @@ #include "crypto/hash.h" #include "cryptonote_basic/cryptonote_basic.h" +#include "cryptonote_basic/difficulty.h" #include "ringct/rctSigs.h" #include "rpc/rpc_handler.h" @@ -165,6 +166,7 @@ namespace rpc uint64_t height; uint64_t depth; crypto::hash hash; + cryptonote::difficulty_type wide_difficulty; uint64_t difficulty; uint64_t reward; }; @@ -173,6 +175,7 @@ namespace rpc { uint64_t height; uint64_t target_height; + cryptonote::difficulty_type wide_difficulty; uint64_t difficulty; uint64_t target; uint64_t tx_count; @@ -187,6 +190,7 @@ namespace rpc bool stagenet; std::string nettype; crypto::hash top_block_hash; + cryptonote::difficulty_type wide_cumulative_difficulty; uint64_t cumulative_difficulty; uint64_t block_size_limit; uint64_t block_weight_limit; diff --git a/src/serialization/difficulty_type.h b/src/serialization/difficulty_type.h new file mode 100644 index 000000000..e32e24b78 --- /dev/null +++ b/src/serialization/difficulty_type.h @@ -0,0 +1,65 @@ +// Copyright (c) 2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include "cryptonote_basic/difficulty.h" +#include "serialization.h" + +template<> struct is_basic_type<cryptonote::difficulty_type> { typedef boost::true_type type; }; + +template <template <bool> class Archive> +inline bool do_serialize(Archive<false>& ar, cryptonote::difficulty_type &diff) +{ + uint64_t hi, lo; + ar.serialize_varint(hi); + if (!ar.stream().good()) + return false; + ar.serialize_varint(lo); + if (!ar.stream().good()) + return false; + diff = hi; + diff <<= 64; + diff += lo; + return true; +} + +template <template <bool> class Archive> +inline bool do_serialize(Archive<true>& ar, cryptonote::difficulty_type &diff) +{ + if (!ar.stream().good()) + return false; + const uint64_t hi = (diff >> 64).convert_to<uint64_t>(); + const uint64_t lo = (diff << 64 >> 64).convert_to<uint64_t>(); + ar.serialize_varint(hi); + ar.serialize_varint(lo); + if (!ar.stream().good()) + return false; + return true; +} + diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index f390b7675..8974bd1e0 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -116,7 +116,9 @@ typedef cryptonote::simple_wallet sw; #define LONG_PAYMENT_ID_SUPPORT_CHECK() \ do { \ if (!m_long_payment_id_support) { \ - fail_msg_writer() << tr("Long payment IDs are obsolete. Use --long-payment-id-support if you really must use one, and warn the recipient they are using an obsolete feature that will disappear in the future."); \ + fail_msg_writer() << tr("Warning: Long payment IDs are obsolete."); \ + fail_msg_writer() << tr("Long payment IDs are not encrypted on the blockchain, and will harm your privacy."); \ + fail_msg_writer() << tr("Use --long-payment-id-support if you really must use one, and warn the recipient they are using an obsolete feature that will disappear in the future."); \ return true; \ } \ } while(0) @@ -149,7 +151,7 @@ namespace const command_line::arg_descriptor<bool> arg_create_address_file = {"create-address-file", sw::tr("Create an address file for new wallets"), false}; const command_line::arg_descriptor<std::string> arg_subaddress_lookahead = {"subaddress-lookahead", tools::wallet2::tr("Set subaddress lookahead sizes to <major>:<minor>"), ""}; const command_line::arg_descriptor<bool> arg_use_english_language_names = {"use-english-language-names", sw::tr("Display English language names"), false}; - const command_line::arg_descriptor<bool> arg_long_payment_id_support = {"long-payment-id-support", sw::tr("Support obsolete long (unencrypted) payment ids"), false}; + const command_line::arg_descriptor<bool> arg_long_payment_id_support = {"long-payment-id-support-bad-for-privacy", sw::tr("Support obsolete long (unencrypted) payment ids (using them harms your privacy)"), false}; const command_line::arg_descriptor< std::vector<std::string> > arg_command = {"command", ""}; @@ -237,6 +239,9 @@ namespace const char* USAGE_MARK_OUTPUT_SPENT("mark_output_spent <amount>/<offset> | <filename> [add]"); const char* USAGE_MARK_OUTPUT_UNSPENT("mark_output_unspent <amount>/<offset>"); const char* USAGE_IS_OUTPUT_SPENT("is_output_spent <amount>/<offset>"); + const char* USAGE_FREEZE("freeze <key_image>"); + const char* USAGE_THAW("thaw <key_image>"); + const char* USAGE_FROZEN("frozen <key_image>"); const char* USAGE_VERSION("version"); const char* USAGE_HELP("help [<command>]"); @@ -2025,6 +2030,74 @@ bool simple_wallet::save_known_rings(const std::vector<std::string> &args) return true; } +bool simple_wallet::freeze_thaw(const std::vector<std::string> &args, bool freeze) +{ + if (args.empty()) + { + fail_msg_writer() << boost::format(tr("usage: %s <key_image>|<pubkey>")) % (freeze ? "freeze" : "thaw"); + return true; + } + crypto::key_image ki; + if (!epee::string_tools::hex_to_pod(args[0], ki)) + { + fail_msg_writer() << tr("failed to parse key image"); + return true; + } + try + { + if (freeze) + m_wallet->freeze(ki); + else + m_wallet->thaw(ki); + } + catch (const std::exception &e) + { + fail_msg_writer() << e.what(); + return true; + } + + return true; +} + +bool simple_wallet::freeze(const std::vector<std::string> &args) +{ + return freeze_thaw(args, true); +} + +bool simple_wallet::thaw(const std::vector<std::string> &args) +{ + return freeze_thaw(args, false); +} + +bool simple_wallet::frozen(const std::vector<std::string> &args) +{ + if (args.empty()) + { + size_t ntd = m_wallet->get_num_transfer_details(); + for (size_t i = 0; i < ntd; ++i) + { + if (!m_wallet->frozen(i)) + continue; + const tools::wallet2::transfer_details &td = m_wallet->get_transfer_details(i); + message_writer() << tr("Frozen: ") << td.m_key_image << " " << cryptonote::print_money(td.amount()); + } + } + else + { + crypto::key_image ki; + if (!epee::string_tools::hex_to_pod(args[0], ki)) + { + fail_msg_writer() << tr("failed to parse key image"); + return true; + } + if (m_wallet->frozen(ki)) + message_writer() << tr("Frozen: ") << ki; + else + message_writer() << tr("Not frozen: ") << ki; + } + return true; +} + bool simple_wallet::version(const std::vector<std::string> &args) { message_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; @@ -2600,7 +2673,7 @@ simple_wallet::simple_wallet() tr(USAGE_INCOMING_TRANSFERS), tr("Show the incoming transfers, all or filtered by availability and address index.\n\n" "Output format:\n" - "Amount, Spent(\"T\"|\"F\"), \"locked\"|\"unlocked\", RingCT, Global Index, Transaction Hash, Address Index, [Public Key, Key Image] ")); + "Amount, Spent(\"T\"|\"F\"), \"frozen\"|\"locked\"|\"unlocked\", RingCT, Global Index, Transaction Hash, Address Index, [Public Key, Key Image] ")); m_cmd_binder.set_handler("payments", boost::bind(&simple_wallet::show_payments, this, _1), tr(USAGE_PAYMENTS), @@ -3012,6 +3085,18 @@ simple_wallet::simple_wallet() boost::bind(&simple_wallet::blackballed, this, _1), tr(USAGE_IS_OUTPUT_SPENT), tr("Checks whether an output is marked as spent")); + m_cmd_binder.set_handler("freeze", + boost::bind(&simple_wallet::freeze, this, _1), + tr(USAGE_FREEZE), + tr("Freeze a single output by key image so it will not be used")); + m_cmd_binder.set_handler("thaw", + boost::bind(&simple_wallet::thaw, this, _1), + tr(USAGE_THAW), + tr("Thaw a single output by key image so it may be used again")); + m_cmd_binder.set_handler("frozen", + boost::bind(&simple_wallet::frozen, this, _1), + tr(USAGE_FROZEN), + tr("Checks whether a given output is currently frozen by key image")); m_cmd_binder.set_handler("version", boost::bind(&simple_wallet::version, this, _1), tr(USAGE_VERSION), @@ -4981,13 +5066,13 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args std::vector<uint64_t> heights; for (const auto &e: td.m_uses) heights.push_back(e.first); const std::pair<std::string, std::string> line = show_outputs_line(heights, blockchain_height, td.m_spent_height); - extra_string += tr("Heights: ") + line.first + "\n" + line.second; + extra_string += std::string("\n ") + tr("Used at heights: ") + line.first + "\n " + line.second; } message_writer(td.m_spent ? console_color_magenta : console_color_green, false) << boost::format("%21s%8s%12s%8s%16u%68s%16u%s") % print_money(td.amount()) % (td.m_spent ? tr("T") : tr("F")) % - (m_wallet->is_transfer_unlocked(td) ? tr("unlocked") : tr("locked")) % + (m_wallet->frozen(td) ? tr("[frozen]") : m_wallet->is_transfer_unlocked(td) ? tr("unlocked") : tr("locked")) % (td.is_rct() ? tr("RingCT") : tr("-")) % td.m_global_output_index % td.m_txid % @@ -5355,7 +5440,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri r = add_extra_nonce_to_tx_extra(extra, extra_nonce); local_args.pop_back(); payment_id_seen = true; - message_writer() << tr("Unencrypted payment IDs are bad for privacy: ask the recipient to use subaddresses instead"); + message_writer() << tr("Warning: Unencrypted payment IDs will harm your privacy: ask the recipient to use subaddresses instead"); } if(!r) { @@ -5465,7 +5550,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri { LONG_PAYMENT_ID_SUPPORT_CHECK(); set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id); - message_writer() << tr("Unencrypted payment IDs are bad for privacy: ask the recipient to use subaddresses instead"); + message_writer() << tr("Warning: Unencrypted payment IDs will harm your privacy: ask the recipient to use subaddresses instead"); } else { @@ -6871,11 +6956,6 @@ bool simple_wallet::set_tx_key(const std::vector<std::string> &args_) //---------------------------------------------------------------------------------------------------- bool simple_wallet::get_tx_proof(const std::vector<std::string> &args) { - if (m_wallet->key_on_device() && m_wallet->get_account().get_device().get_type() != hw::device::TREZOR) - { - fail_msg_writer() << tr("command not supported by HW wallet"); - return true; - } if (args.size() != 2 && args.size() != 3) { PRINT_USAGE(USAGE_GET_TX_PROOF); diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 7bcb92190..c9a5c55e8 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -238,9 +238,12 @@ namespace cryptonote bool blackball(const std::vector<std::string>& args); bool unblackball(const std::vector<std::string>& args); bool blackballed(const std::vector<std::string>& args); + bool freeze(const std::vector<std::string>& args); + bool thaw(const std::vector<std::string>& args); + bool frozen(const std::vector<std::string>& args); bool version(const std::vector<std::string>& args); - bool cold_sign_tx(const std::vector<tools::wallet2::pending_tx>& ptx_vector, tools::wallet2::signed_tx_set &exported_txs, std::vector<cryptonote::address_parse_info> &dsts_info, std::function<bool(const tools::wallet2::signed_tx_set &)> accept_func); + bool cold_sign_tx(const std::vector<tools::wallet2::pending_tx>& ptx_vector, tools::wallet2::signed_tx_set &exported_txs, std::vector<cryptonote::address_parse_info> &dsts_info, std::function<bool(const tools::wallet2::signed_tx_set &)> accept_func); uint64_t get_daemon_blockchain_height(std::string& err); bool try_connect_to_daemon(bool silent = false, uint32_t* version = nullptr); bool ask_wallet_create_if_needed(); @@ -253,6 +256,7 @@ namespace cryptonote void key_images_sync_intern(); void on_refresh_finished(uint64_t start_height, uint64_t fetched_blocks, bool is_init, bool received_money); std::pair<std::string, std::string> show_outputs_line(const std::vector<uint64_t> &heights, uint64_t blockchain_height, uint64_t highlight_height = std::numeric_limits<uint64_t>::max()) const; + bool freeze_thaw(const std::vector<std::string>& args, bool freeze); struct transfer_view { diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt index efd61cb5a..def23aff0 100644 --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt @@ -63,6 +63,7 @@ target_link_libraries(wallet cryptonote_core mnemonics device_trezor + net ${LMDB_LIBRARY} ${Boost_CHRONO_LIBRARY} ${Boost_SERIALIZATION_LIBRARY} diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index d7226b656..82986ba2d 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -2173,8 +2173,7 @@ void WalletImpl::pendingTxPostProcess(PendingTransactionImpl * pending) bool WalletImpl::doInit(const string &daemon_address, uint64_t upper_transaction_size_limit, bool ssl) { - // claim RPC so there's no in-memory encryption for now - if (!m_wallet->init(daemon_address, m_daemon_login, upper_transaction_size_limit, ssl)) + if (!m_wallet->init(daemon_address, m_daemon_login, tcp::endpoint{}, upper_transaction_size_limit)) return false; // in case new wallet, this will force fast-refresh (pulling hashes instead of blocks) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index a46da42db..d59ded49f 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -38,6 +38,7 @@ #include <boost/algorithm/string/trim.hpp> #include <boost/algorithm/string/split.hpp> #include <boost/algorithm/string/join.hpp> +#include <boost/asio/ip/address.hpp> #include <boost/range/adaptor/transformed.hpp> #include "include_base_utils.h" using namespace epee; @@ -75,6 +76,7 @@ using namespace epee; #include "ringdb.h" #include "device/device_cold.hpp" #include "device_trezor/device_trezor.hpp" +#include "net/socks_connect.h" extern "C" { @@ -231,6 +233,7 @@ namespace struct options { const command_line::arg_descriptor<std::string> daemon_address = {"daemon-address", tools::wallet2::tr("Use daemon instance at <host>:<port>"), ""}; const command_line::arg_descriptor<std::string> daemon_host = {"daemon-host", tools::wallet2::tr("Use daemon instance at host <arg> instead of localhost"), ""}; + const command_line::arg_descriptor<std::string> proxy = {"proxy", tools::wallet2::tr("[<ip>:]<port> socks proxy to use for daemon connections"), {}, true}; const command_line::arg_descriptor<bool> trusted_daemon = {"trusted-daemon", tools::wallet2::tr("Enable commands which rely on a trusted daemon"), false}; const command_line::arg_descriptor<bool> untrusted_daemon = {"untrusted-daemon", tools::wallet2::tr("Disable commands which rely on a trusted daemon"), false}; const command_line::arg_descriptor<std::string> password = {"password", tools::wallet2::tr("Wallet password (escape/quote as needed)"), "", true}; @@ -303,6 +306,8 @@ std::string get_weight_string(const cryptonote::transaction &tx, size_t blob_siz std::unique_ptr<tools::wallet2> make_basic(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) { + namespace ip = boost::asio::ip; + const bool testnet = command_line::get_arg(vm, opts.testnet); const bool stagenet = command_line::get_arg(vm, opts.stagenet); const network_type nettype = testnet ? TESTNET : stagenet ? STAGENET : MAINNET; @@ -352,6 +357,44 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl if (daemon_address.empty()) daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port); + boost::asio::ip::tcp::endpoint proxy{}; + if (command_line::has_arg(vm, opts.proxy)) + { + namespace ip = boost::asio::ip; + const boost::string_ref real_daemon = boost::string_ref{daemon_address}.substr(0, daemon_address.rfind(':')); + + // onion and i2p addresses contain information about the server cert + // which both authenticates and encrypts + const bool unencrypted_proxy = + !real_daemon.ends_with(".onion") && !real_daemon.ends_with(".i2p") && + daemon_ssl_allowed_certificates.empty() && daemon_ssl_allowed_fingerprints.empty(); + THROW_WALLET_EXCEPTION_IF( + unencrypted_proxy, + tools::error::wallet_internal_error, + std::string{"Use of --"} + opts.proxy.name + " requires --" + opts.daemon_ssl_allowed_certificates.name + " or --" + opts.daemon_ssl_allowed_fingerprints.name + " or use of a .onion/.i2p domain" + ); + + const auto proxy_address = command_line::get_arg(vm, opts.proxy); + + boost::string_ref proxy_port{proxy_address}; + boost::string_ref proxy_host = proxy_port.substr(0, proxy_port.rfind(":")); + if (proxy_port.size() == proxy_host.size()) + proxy_host = "127.0.0.1"; + else + proxy_port = proxy_port.substr(proxy_host.size() + 1); + + uint16_t port_value = 0; + THROW_WALLET_EXCEPTION_IF( + !epee::string_tools::get_xtype_from_string(port_value, std::string{proxy_port}), + tools::error::wallet_internal_error, + std::string{"Invalid port specified for --"} + opts.proxy.name + ); + + boost::system::error_code error{}; + proxy = ip::tcp::endpoint{ip::address::from_string(std::string{proxy_host}, error), port_value}; + THROW_WALLET_EXCEPTION_IF(bool(error), tools::error::wallet_internal_error, std::string{"Invalid IP address specified for --"} + opts.proxy.name); + } + boost::optional<bool> trusted_daemon; if (!command_line::is_arg_defaulted(vm, opts.trusted_daemon) || !command_line::is_arg_defaulted(vm, opts.untrusted_daemon)) trusted_daemon = command_line::get_arg(vm, opts.trusted_daemon) && !command_line::get_arg(vm, opts.untrusted_daemon); @@ -388,8 +431,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl std::transform(daemon_ssl_allowed_fingerprints.begin(), daemon_ssl_allowed_fingerprints.end(), ssl_allowed_fingerprints.begin(), epee::from_hex::vector); std::unique_ptr<tools::wallet2> wallet(new tools::wallet2(nettype, kdf_rounds, unattended)); - wallet->init(std::move(daemon_address), std::move(login), 0, *trusted_daemon, ssl_support, std::make_pair(daemon_ssl_private_key, daemon_ssl_certificate), ssl_allowed_certificates, ssl_allowed_fingerprints, daemon_ssl_allow_any_cert); - + wallet->init(std::move(daemon_address), std::move(login), std::move(proxy), 0, *trusted_daemon, ssl_support, std::make_pair(daemon_ssl_private_key, daemon_ssl_certificate), ssl_allowed_certificates, ssl_allowed_fingerprints, daemon_ssl_allow_any_cert); boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir); wallet->set_ring_database(ringdb_path.string()); wallet->get_message_store().set_options(vm); @@ -470,7 +512,7 @@ std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> generate_f GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, filename, std::string, String, true, std::string()); GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, scan_from_height, uint64_t, Uint64, false, 0); - const bool recover = field_scan_from_height_found; + const bool recover = true; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, password, std::string, String, false, std::string()); @@ -580,6 +622,8 @@ std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> generate_f wallet.reset(make_basic(vm, unattended, opts, password_prompter).release()); wallet->set_refresh_from_block_height(field_scan_from_height); wallet->explicit_refresh_from_block_height(field_scan_from_height_found); + if (!old_language.empty()) + wallet->set_seed_language(old_language); try { @@ -1046,6 +1090,7 @@ void wallet2::init_options(boost::program_options::options_description& desc_par const options opts{}; command_line::add_arg(desc_params, opts.daemon_address); command_line::add_arg(desc_params, opts.daemon_host); + command_line::add_arg(desc_params, opts.proxy); command_line::add_arg(desc_params, opts.trusted_daemon); command_line::add_arg(desc_params, opts.untrusted_daemon); command_line::add_arg(desc_params, opts.password); @@ -1109,7 +1154,7 @@ std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::varia } //---------------------------------------------------------------------------------------------------- -bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_weight_limit, bool trusted_daemon, epee::net_utils::ssl_support_t ssl_support, const std::pair<std::string, std::string> &private_key_and_certificate_path, const std::list<std::string> &allowed_certificates, const std::vector<std::vector<uint8_t>> &allowed_fingerprints, bool allow_any_cert) +bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, boost::asio::ip::tcp::endpoint proxy, uint64_t upper_transaction_weight_limit, bool trusted_daemon, epee::net_utils::ssl_support_t ssl_support, const std::pair<std::string, std::string> &private_key_and_certificate_path, const std::list<std::string> &allowed_certificates, const std::vector<std::vector<uint8_t>> &allowed_fingerprints, bool allow_any_cert) { m_checkpoints.init_default_checkpoints(m_nettype); if(m_http_client.is_connected()) @@ -1119,6 +1164,10 @@ bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils:: m_daemon_address = std::move(daemon_address); m_daemon_login = std::move(daemon_login); m_trusted_daemon = trusted_daemon; + if (proxy != boost::asio::ip::tcp::endpoint{}) + m_http_client.set_connector(net::socks::connector{std::move(proxy)}); + + // When switching from light wallet to full wallet, we need to reset the height we got from lw node. return m_http_client.set_server(get_daemon_address(), get_daemon_login(), ssl_support, private_key_and_certificate_path, allowed_certificates, allowed_fingerprints, allow_any_cert); } //---------------------------------------------------------------------------------------------------- @@ -1392,6 +1441,58 @@ void wallet2::set_unspent(size_t idx) td.m_spent_height = 0; } //---------------------------------------------------------------------------------------------------- +void wallet2::freeze(size_t idx) +{ + CHECK_AND_ASSERT_THROW_MES(idx < m_transfers.size(), "Invalid transfer_details index"); + transfer_details &td = m_transfers[idx]; + td.m_frozen = true; +} +//---------------------------------------------------------------------------------------------------- +void wallet2::thaw(size_t idx) +{ + CHECK_AND_ASSERT_THROW_MES(idx < m_transfers.size(), "Invalid transfer_details index"); + transfer_details &td = m_transfers[idx]; + td.m_frozen = false; +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::frozen(size_t idx) const +{ + CHECK_AND_ASSERT_THROW_MES(idx < m_transfers.size(), "Invalid transfer_details index"); + const transfer_details &td = m_transfers[idx]; + return td.m_frozen; +} +//---------------------------------------------------------------------------------------------------- +void wallet2::freeze(const crypto::key_image &ki) +{ + freeze(get_transfer_details(ki)); +} +//---------------------------------------------------------------------------------------------------- +void wallet2::thaw(const crypto::key_image &ki) +{ + thaw(get_transfer_details(ki)); +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::frozen(const crypto::key_image &ki) const +{ + return frozen(get_transfer_details(ki)); +} +//---------------------------------------------------------------------------------------------------- +size_t wallet2::get_transfer_details(const crypto::key_image &ki) const +{ + for (size_t idx = 0; idx < m_transfers.size(); ++idx) + { + const transfer_details &td = m_transfers[idx]; + if (td.m_key_image_known && td.m_key_image == ki) + return idx; + } + CHECK_AND_ASSERT_THROW_MES(false, "Key image not found"); +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::frozen(const transfer_details &td) const +{ + return td.m_frozen; +} +//---------------------------------------------------------------------------------------------------- void wallet2::check_acc_out_precomp(const tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, tx_scan_info_t &tx_scan_info) const { hw::device &hwdev = m_account.get_device(); @@ -1809,6 +1910,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote td.m_mask = rct::identity(); td.m_rct = false; } + td.m_frozen = false; set_unspent(m_transfers.size()-1); if (td.m_key_image_known) m_key_images[td.m_key_image] = m_transfers.size()-1; @@ -2406,7 +2508,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry " (height " + std::to_string(start_height) + "), local block id at this height: " + string_tools::pod_to_hex(m_blockchain[current_index])); - detach_blockchain(current_index); + detach_blockchain(current_index, output_tracker_cache); process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset, output_tracker_cache); } else @@ -2850,7 +2952,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo // MyMonero get_address_info needs to be called occasionally to trigger wallet sync. // This call is not really needed for other purposes and can be removed if mymonero changes their backend. - cryptonote::COMMAND_RPC_GET_ADDRESS_INFO::response res; + tools::COMMAND_RPC_GET_ADDRESS_INFO::response res; // Get basic info if(light_wallet_get_address_info(res)) { @@ -2998,7 +3100,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo // if we've got at least 10 blocks to refresh, assume we're starting // a long refresh, and setup a tracking output cache if we need to - if (m_track_uses && !output_tracker_cache && next_blocks.size() >= 10) + if (m_track_uses && (!output_tracker_cache || output_tracker_cache->empty()) && next_blocks.size() >= 10) output_tracker_cache = create_output_tracker_cache(); // switch to the new blocks from the daemon @@ -3139,7 +3241,7 @@ bool wallet2::get_rct_distribution(uint64_t &start_height, std::vector<uint64_t> return true; } //---------------------------------------------------------------------------------------------------- -void wallet2::detach_blockchain(uint64_t height) +void wallet2::detach_blockchain(uint64_t height, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache) { LOG_PRINT_L0("Detaching blockchain on height " << height); @@ -3156,11 +3258,21 @@ void wallet2::detach_blockchain(uint64_t height) wallet2::transfer_details &td = m_transfers[i]; if (td.m_spent && td.m_spent_height >= height) { - LOG_PRINT_L1("Resetting spent status for output " << i << ": " << td.m_key_image); + LOG_PRINT_L1("Resetting spent/frozen status for output " << i << ": " << td.m_key_image); set_unspent(i); + thaw(i); } } + for (transfer_details &td: m_transfers) + { + while (!td.m_uses.empty() && td.m_uses.back().first >= height) + td.m_uses.pop_back(); + } + + if (output_tracker_cache) + output_tracker_cache->clear(); + auto it = std::find_if(m_transfers.begin(), m_transfers.end(), [&](const transfer_details& td){return td.m_block_height >= height;}); size_t i_start = it - m_transfers.begin(); @@ -3490,7 +3602,8 @@ void wallet2::change_password(const std::string &filename, const epee::wipeable_ decrypt_keys(original_password); setup_keys(new_password); rewrite(filename, new_password); - store(); + if (!filename.empty()) + store(); } //---------------------------------------------------------------------------------------------------- /*! @@ -5179,7 +5292,8 @@ std::string wallet2::path() const //---------------------------------------------------------------------------------------------------- void wallet2::store() { - store_to("", epee::wipeable_string()); + if (!m_wallet_file.empty()) + store_to("", epee::wipeable_string()); } //---------------------------------------------------------------------------------------------------- void wallet2::store_to(const std::string &path, const epee::wipeable_string &password) @@ -5331,7 +5445,7 @@ std::map<uint32_t, uint64_t> wallet2::balance_per_subaddress(uint32_t index_majo std::map<uint32_t, uint64_t> amount_per_subaddr; for (const auto& td: m_transfers) { - if (td.m_subaddr_index.major == index_major && !td.m_spent) + if (td.m_subaddr_index.major == index_major && !td.m_spent && !td.m_frozen) { auto found = amount_per_subaddr.find(td.m_subaddr_index.minor); if (found == amount_per_subaddr.end()) @@ -5360,7 +5474,7 @@ std::map<uint32_t, uint64_t> wallet2::unlocked_balance_per_subaddress(uint32_t i std::map<uint32_t, uint64_t> amount_per_subaddr; for(const transfer_details& td: m_transfers) { - if(td.m_subaddr_index.major == index_major && !td.m_spent && is_transfer_unlocked(td)) + if(td.m_subaddr_index.major == index_major && !td.m_spent && !td.m_frozen && is_transfer_unlocked(td)) { auto found = amount_per_subaddr.find(td.m_subaddr_index.minor); if (found == amount_per_subaddr.end()) @@ -6012,12 +6126,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pendin LOG_PRINT_L1(" " << (n+1) << ": " << sd.sources.size() << " inputs, ring size " << sd.sources[0].outputs.size()); signed_txes.ptx.push_back(pending_tx()); tools::wallet2::pending_tx &ptx = signed_txes.ptx.back(); - rct::RCTConfig rct_config = { rct::RangeProofBorromean, 0 }; - if (sd.use_bulletproofs) - { - rct_config.range_proof_type = rct::RangeProofPaddedBulletproof; - rct_config.bp_version = use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1; - } + rct::RCTConfig rct_config = sd.rct_config; crypto::secret_key tx_key; std::vector<crypto::secret_key> additional_tx_keys; rct::multisig_out msout; @@ -6494,12 +6603,7 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto cryptonote::transaction tx; rct::multisig_out msout = ptx.multisig_sigs.front().msout; auto sources = sd.sources; - rct::RCTConfig rct_config = { rct::RangeProofBorromean, 0 }; - if (sd.use_bulletproofs) - { - rct_config.range_proof_type = rct::RangeProofPaddedBulletproof; - rct_config.bp_version = use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1; - } + rct::RCTConfig rct_config = sd.rct_config; bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources, sd.splitted_dsts, ptx.change_dts.addr, sd.extra, tx, sd.unlock_time, ptx.tx_key, ptx.additional_tx_keys, sd.use_rct, rct_config, &msout, false); THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_nettype); @@ -7960,7 +8064,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent ptx.construction_data.extra = tx.extra; ptx.construction_data.unlock_time = unlock_time; ptx.construction_data.use_rct = false; - ptx.construction_data.use_bulletproofs = false; + ptx.construction_data.rct_config = { rct::RangeProofBorromean, 0 }; ptx.construction_data.dests = dsts; // record which subaddress indices are being used as inputs ptx.construction_data.subaddr_account = subaddr_account; @@ -8243,7 +8347,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry ptx.construction_data.extra = tx.extra; ptx.construction_data.unlock_time = unlock_time; ptx.construction_data.use_rct = true; - ptx.construction_data.use_bulletproofs = !tx.rct_signatures.p.bulletproofs.empty(); + ptx.construction_data.rct_config = { tx.rct_signatures.p.bulletproofs.empty() ? rct::RangeProofBorromean : rct::RangeProofPaddedBulletproof, use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1}; ptx.construction_data.dests = dsts; // record which subaddress indices are being used as inputs ptx.construction_data.subaddr_account = subaddr_account; @@ -8264,7 +8368,7 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui for (size_t i = 0; i < m_transfers.size(); ++i) { const transfer_details& td = m_transfers[i]; - if (!td.m_spent && td.is_rct() && td.amount() >= needed_money && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1) + if (!td.m_spent && !td.m_frozen && td.is_rct() && td.amount() >= needed_money && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1) { LOG_PRINT_L2("We can use " << i << " alone: " << print_money(td.amount())); picks.push_back(i); @@ -8279,13 +8383,13 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui for (size_t i = 0; i < m_transfers.size(); ++i) { const transfer_details& td = m_transfers[i]; - if (!td.m_spent && !td.m_key_image_partial && td.is_rct() && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1) + if (!td.m_spent && !td.m_frozen && !td.m_key_image_partial && td.is_rct() && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1) { LOG_PRINT_L2("Considering input " << i << ", " << print_money(td.amount())); for (size_t j = i + 1; j < m_transfers.size(); ++j) { const transfer_details& td2 = m_transfers[j]; - if (!td2.m_spent && !td.m_key_image_partial && td2.is_rct() && td.amount() + td2.amount() >= needed_money && is_transfer_unlocked(td2) && td2.m_subaddr_index == td.m_subaddr_index) + if (!td2.m_spent && !td2.m_frozen && !td.m_key_image_partial && td2.is_rct() && td.amount() + td2.amount() >= needed_money && is_transfer_unlocked(td2) && td2.m_subaddr_index == td.m_subaddr_index) { // update our picks if those outputs are less related than any we // already found. If the same, don't update, and oldest suitable outputs @@ -8371,8 +8475,8 @@ bool wallet2::light_wallet_login(bool &new_address) { MDEBUG("Light wallet login request"); m_light_wallet_connected = false; - cryptonote::COMMAND_RPC_LOGIN::request request; - cryptonote::COMMAND_RPC_LOGIN::response response; + tools::COMMAND_RPC_LOGIN::request request; + tools::COMMAND_RPC_LOGIN::response response; request.address = get_account().get_public_address_str(m_nettype); request.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); // Always create account if it doesn't exist. @@ -8396,10 +8500,10 @@ bool wallet2::light_wallet_login(bool &new_address) return m_light_wallet_connected; } -bool wallet2::light_wallet_import_wallet_request(cryptonote::COMMAND_RPC_IMPORT_WALLET_REQUEST::response &response) +bool wallet2::light_wallet_import_wallet_request(tools::COMMAND_RPC_IMPORT_WALLET_REQUEST::response &response) { MDEBUG("Light wallet import wallet request"); - cryptonote::COMMAND_RPC_IMPORT_WALLET_REQUEST::request oreq; + tools::COMMAND_RPC_IMPORT_WALLET_REQUEST::request oreq; oreq.address = get_account().get_public_address_str(m_nettype); oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); m_daemon_rpc_mutex.lock(); @@ -8415,8 +8519,8 @@ void wallet2::light_wallet_get_unspent_outs() { MDEBUG("Getting unspent outs"); - cryptonote::COMMAND_RPC_GET_UNSPENT_OUTS::request oreq; - cryptonote::COMMAND_RPC_GET_UNSPENT_OUTS::response ores; + tools::COMMAND_RPC_GET_UNSPENT_OUTS::request oreq; + tools::COMMAND_RPC_GET_UNSPENT_OUTS::response ores; oreq.amount = "0"; oreq.address = get_account().get_public_address_str(m_nettype); @@ -8512,6 +8616,7 @@ void wallet2::light_wallet_get_unspent_outs() td.m_pk_index = 0; td.m_internal_output_index = o.index; td.m_spent = spent; + td.m_frozen = false; tx_out txout; txout.target = txout_to_key(public_key); @@ -8564,11 +8669,11 @@ void wallet2::light_wallet_get_unspent_outs() } } -bool wallet2::light_wallet_get_address_info(cryptonote::COMMAND_RPC_GET_ADDRESS_INFO::response &response) +bool wallet2::light_wallet_get_address_info(tools::COMMAND_RPC_GET_ADDRESS_INFO::response &response) { MTRACE(__FUNCTION__); - cryptonote::COMMAND_RPC_GET_ADDRESS_INFO::request request; + tools::COMMAND_RPC_GET_ADDRESS_INFO::request request; request.address = get_account().get_public_address_str(m_nettype); request.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); @@ -8584,8 +8689,8 @@ void wallet2::light_wallet_get_address_txs() { MDEBUG("Refreshing light wallet"); - cryptonote::COMMAND_RPC_GET_ADDRESS_TXS::request ireq; - cryptonote::COMMAND_RPC_GET_ADDRESS_TXS::response ires; + tools::COMMAND_RPC_GET_ADDRESS_TXS::request ireq; + tools::COMMAND_RPC_GET_ADDRESS_TXS::response ires; ireq.address = get_account().get_public_address_str(m_nettype); ireq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); @@ -8986,7 +9091,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp MDEBUG("Ignoring output " << i << " of amount " << print_money(td.amount()) << " which is below threshold " << print_money(fractional_threshold)); continue; } - if (!td.m_spent && !td.m_key_image_partial && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1) + if (!td.m_spent && !td.m_frozen && !td.m_key_image_partial && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1) { const uint32_t index_minor = td.m_subaddr_index.minor; auto find_predicate = [&index_minor](const std::pair<uint32_t, std::vector<size_t>>& x) { return x.first == index_minor; }; @@ -9418,7 +9523,7 @@ bool wallet2::sanity_check(const std::vector<wallet2::pending_tx> &ptx_vector, s THROW_WALLET_EXCEPTION_IF(ptx.change_dts.addr != ptx_vector[0].change_dts.addr, error::wallet_internal_error, "Change goes to several different addresses"); const auto it = m_subaddresses.find(ptx_vector[0].change_dts.addr.m_spend_public_key); - THROW_WALLET_EXCEPTION_IF(it == m_subaddresses.end(), error::wallet_internal_error, "Change address is not ours"); + THROW_WALLET_EXCEPTION_IF(change > 0 && it == m_subaddresses.end(), error::wallet_internal_error, "Change address is not ours"); required[ptx_vector[0].change_dts.addr].first += change; required[ptx_vector[0].change_dts.addr].second = ptx_vector[0].change_dts.is_subaddress; @@ -9466,7 +9571,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below for (size_t i = 0; i < m_transfers.size(); ++i) { const transfer_details& td = m_transfers[i]; - if (!td.m_spent && !td.m_key_image_partial && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && (subaddr_indices.empty() || subaddr_indices.count(td.m_subaddr_index.minor) == 1)) + if (!td.m_spent && !td.m_frozen && !td.m_key_image_partial && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && (subaddr_indices.empty() || subaddr_indices.count(td.m_subaddr_index.minor) == 1)) { fund_found = true; if (below == 0 || td.amount() < below) @@ -9514,7 +9619,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_single(const crypt for (size_t i = 0; i < m_transfers.size(); ++i) { const transfer_details& td = m_transfers[i]; - if (td.m_key_image_known && td.m_key_image == ki && !td.m_spent && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td)) + if (td.m_key_image_known && td.m_key_image == ki && !td.m_spent && !td.m_frozen && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td)) { if (td.is_rct() || is_valid_decomposed_amount(td.amount())) unused_transfers_indices.push_back(i); @@ -9734,8 +9839,14 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton } uint64_t a = 0; - for (size_t idx: unused_transfers_indices) a += m_transfers[idx].amount(); - for (size_t idx: unused_dust_indices) a += m_transfers[idx].amount(); + for (const TX &tx: txes) + { + for (size_t idx: tx.selected_transfers) + { + a += m_transfers[idx].amount(); + } + a -= tx.ptx.fee; + } std::vector<cryptonote::tx_destination_entry> synthetic_dsts(1, cryptonote::tx_destination_entry("", a, address, is_subaddress)); THROW_WALLET_EXCEPTION_IF(!sanity_check(ptx_vector, synthetic_dsts), error::wallet_internal_error, "Created transaction(s) failed sanity check"); @@ -9819,7 +9930,7 @@ bool wallet2::use_fork_rules(uint8_t version, int64_t early_blocks) const result = m_node_rpc_proxy.get_earliest_height(version, earliest_height); throw_on_rpc_response_error(result, "get_hard_fork_info"); - bool close_enough = height >= earliest_height - early_blocks && earliest_height != std::numeric_limits<uint64_t>::max(); // start using the rules that many blocks beforehand + bool close_enough = (int64_t)height >= (int64_t)earliest_height - early_blocks && earliest_height != std::numeric_limits<uint64_t>::max(); // start using the rules that many blocks beforehand if (close_enough) LOG_PRINT_L2("Using v" << (unsigned)version << " rules"); else @@ -9846,6 +9957,8 @@ std::vector<size_t> wallet2::select_available_outputs(const std::function<bool(c { if (i->m_spent) continue; + if (i->m_frozen) + continue; if (i->m_key_image_partial) continue; if (!is_transfer_unlocked(*i)) @@ -9861,7 +9974,7 @@ std::vector<uint64_t> wallet2::get_unspent_amounts_vector() const std::set<uint64_t> set; for (const auto &td: m_transfers) { - if (!td.m_spent) + if (!td.m_spent && !td.m_frozen) set.insert(td.is_rct() ? 0 : td.amount()); } std::vector<uint64_t> vector; @@ -9987,7 +10100,7 @@ void wallet2::discard_unmixable_outputs() std::vector<size_t> unmixable_outputs = select_available_unmixable_outputs(); for (size_t idx : unmixable_outputs) { - m_transfers[idx].m_spent = true; + freeze(idx); } } @@ -10374,7 +10487,7 @@ void wallet2::check_tx_key(const crypto::hash &txid, const crypto::secret_key &t void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, const cryptonote::account_public_address &address, uint64_t &received) const { received = 0; - hw::device &hwdev = m_account.get_device(); + for (size_t n = 0; n < tx.vout.size(); ++n) { const cryptonote::txout_to_key* const out_key = boost::get<cryptonote::txout_to_key>(std::addressof(tx.vout[n].target)); @@ -10382,13 +10495,13 @@ void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypt continue; crypto::public_key derived_out_key; - bool r = hwdev.derive_public_key(derivation, n, address.m_spend_public_key, derived_out_key); + bool r = crypto::derive_public_key(derivation, n, address.m_spend_public_key, derived_out_key); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to derive public key"); bool found = out_key->key == derived_out_key; crypto::key_derivation found_derivation = derivation; if (!found && !additional_derivations.empty()) { - r = hwdev.derive_public_key(additional_derivations[n], n, address.m_spend_public_key, derived_out_key); + r = crypto::derive_public_key(additional_derivations[n], n, address.m_spend_public_key, derived_out_key); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to derive public key"); found = out_key->key == derived_out_key; found_derivation = additional_derivations[n]; @@ -10404,9 +10517,9 @@ void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypt else { crypto::secret_key scalar1; - hwdev.derivation_to_scalar(found_derivation, n, scalar1); + crypto::derivation_to_scalar(found_derivation, n, scalar1); rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n]; - hwdev.ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tx.rct_signatures.type == rct::RCTTypeBulletproof2); + rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tx.rct_signatures.type == rct::RCTTypeBulletproof2); const rct::key C = tx.rct_signatures.outPk[n].mask; rct::key Ctmp; THROW_WALLET_EXCEPTION_IF(sc_check(ecdh_info.mask.bytes) != 0, error::wallet_internal_error, "Bad ECDH input mask"); @@ -10517,6 +10630,8 @@ std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::ac std::string wallet2::get_tx_proof(const cryptonote::transaction &tx, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message) const { + hw::device &hwdev = m_account.get_device(); + rct::key aP; // determine if the address is found in the subaddress hash table (i.e. whether the proof is outbound or inbound) const bool is_out = m_subaddresses.count(address.m_spend_public_key) == 0; @@ -10535,30 +10650,34 @@ std::string wallet2::get_tx_proof(const cryptonote::transaction &tx, const crypt shared_secret.resize(num_sigs); sig.resize(num_sigs); - shared_secret[0] = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(address.m_view_public_key), rct::sk2rct(tx_key))); + hwdev.scalarmultKey(aP, rct::pk2rct(address.m_view_public_key), rct::sk2rct(tx_key)); + shared_secret[0] = rct::rct2pk(aP); crypto::public_key tx_pub_key; if (is_subaddress) { - tx_pub_key = rct2pk(rct::scalarmultKey(rct::pk2rct(address.m_spend_public_key), rct::sk2rct(tx_key))); - crypto::generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, address.m_spend_public_key, shared_secret[0], tx_key, sig[0]); + hwdev.scalarmultKey(aP, rct::pk2rct(address.m_spend_public_key), rct::sk2rct(tx_key)); + tx_pub_key = rct2pk(aP); + hwdev.generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, address.m_spend_public_key, shared_secret[0], tx_key, sig[0]); } else { - crypto::secret_key_to_public_key(tx_key, tx_pub_key); - crypto::generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, boost::none, shared_secret[0], tx_key, sig[0]); + hwdev.secret_key_to_public_key(tx_key, tx_pub_key); + hwdev.generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, boost::none, shared_secret[0], tx_key, sig[0]); } for (size_t i = 1; i < num_sigs; ++i) { - shared_secret[i] = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(address.m_view_public_key), rct::sk2rct(additional_tx_keys[i - 1]))); + hwdev.scalarmultKey(aP, rct::pk2rct(address.m_view_public_key), rct::sk2rct(additional_tx_keys[i - 1])); + shared_secret[i] = rct::rct2pk(aP); if (is_subaddress) { - tx_pub_key = rct2pk(rct::scalarmultKey(rct::pk2rct(address.m_spend_public_key), rct::sk2rct(additional_tx_keys[i - 1]))); - crypto::generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, address.m_spend_public_key, shared_secret[i], additional_tx_keys[i - 1], sig[i]); + hwdev.scalarmultKey(aP, rct::pk2rct(address.m_spend_public_key), rct::sk2rct(additional_tx_keys[i - 1])); + tx_pub_key = rct2pk(aP); + hwdev.generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, address.m_spend_public_key, shared_secret[i], additional_tx_keys[i - 1], sig[i]); } else { - crypto::secret_key_to_public_key(additional_tx_keys[i - 1], tx_pub_key); - crypto::generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, boost::none, shared_secret[i], additional_tx_keys[i - 1], sig[i]); + hwdev.secret_key_to_public_key(additional_tx_keys[i - 1], tx_pub_key); + hwdev.generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, boost::none, shared_secret[i], additional_tx_keys[i - 1], sig[i]); } } sig_str = std::string("OutProofV1"); @@ -10574,25 +10693,27 @@ std::string wallet2::get_tx_proof(const cryptonote::transaction &tx, const crypt sig.resize(num_sigs); const crypto::secret_key& a = m_account.get_keys().m_view_secret_key; - shared_secret[0] = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(tx_pub_key), rct::sk2rct(a))); + hwdev.scalarmultKey(aP, rct::pk2rct(tx_pub_key), rct::sk2rct(a)); + shared_secret[0] = rct2pk(aP); if (is_subaddress) { - crypto::generate_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, address.m_spend_public_key, shared_secret[0], a, sig[0]); + hwdev.generate_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, address.m_spend_public_key, shared_secret[0], a, sig[0]); } else { - crypto::generate_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, boost::none, shared_secret[0], a, sig[0]); + hwdev.generate_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, boost::none, shared_secret[0], a, sig[0]); } for (size_t i = 1; i < num_sigs; ++i) { - shared_secret[i] = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(additional_tx_pub_keys[i - 1]), rct::sk2rct(a))); + hwdev.scalarmultKey(aP,rct::pk2rct(additional_tx_pub_keys[i - 1]), rct::sk2rct(a)); + shared_secret[i] = rct2pk(aP); if (is_subaddress) { - crypto::generate_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[i - 1], address.m_spend_public_key, shared_secret[i], a, sig[i]); + hwdev.generate_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[i - 1], address.m_spend_public_key, shared_secret[i], a, sig[i]); } else { - crypto::generate_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[i - 1], boost::none, shared_secret[i], a, sig[i]); + hwdev.generate_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[i - 1], boost::none, shared_secret[i], a, sig[i]); } } sig_str = std::string("InProofV1"); @@ -10770,7 +10891,7 @@ std::string wallet2::get_reserve_proof(const boost::optional<std::pair<uint32_t, for (size_t i = 0; i < m_transfers.size(); ++i) { const transfer_details &td = m_transfers[i]; - if (!td.m_spent && (!account_minreserve || account_minreserve->first == td.m_subaddr_index.major)) + if (!td.m_spent && !td.m_frozen && (!account_minreserve || account_minreserve->first == td.m_subaddr_index.major)) selected_transfers.push_back(i); } @@ -11517,6 +11638,8 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag for(size_t i = 0; i < offset; ++i) { const transfer_details &td = m_transfers[i]; + if (td.m_frozen) + continue; uint64_t amount = td.amount(); if (td.m_spent) spent += amount; @@ -11528,6 +11651,8 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag for(size_t i = 0; i < signed_key_images.size(); ++i) { const transfer_details &td = m_transfers[i + offset]; + if (td.m_frozen) + continue; uint64_t amount = td.amount(); if (td.m_spent) spent += amount; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index c25450fb3..5a67c20d0 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -62,6 +62,7 @@ #include "common/password.h" #include "node_rpc_proxy.h" #include "message_store.h" +#include "wallet_light_rpc.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "wallet.wallet2" @@ -265,6 +266,7 @@ namespace tools size_t m_internal_output_index; uint64_t m_global_output_index; bool m_spent; + bool m_frozen; uint64_t m_spent_height; crypto::key_image m_key_image; //TODO: key_image stored twice :( rct::key m_mask; @@ -290,6 +292,7 @@ namespace tools FIELD(m_internal_output_index) FIELD(m_global_output_index) FIELD(m_spent) + FIELD(m_frozen) FIELD(m_spent_height) FIELD(m_key_image) FIELD(m_mask) @@ -374,7 +377,7 @@ namespace tools std::vector<uint8_t> extra; uint64_t unlock_time; bool use_rct; - bool use_bulletproofs; + rct::RCTConfig rct_config; std::vector<cryptonote::tx_destination_entry> dests; // original setup, does not include change uint32_t subaddr_account; // subaddress account of your wallet to be used in this transfer std::set<uint32_t> subaddr_indices; // set of address indices used as inputs in this transfer @@ -387,7 +390,7 @@ namespace tools FIELD(extra) FIELD(unlock_time) FIELD(use_rct) - FIELD(use_bulletproofs) + FIELD(rct_config) FIELD(dests) FIELD(subaddr_account) FIELD(subaddr_indices) @@ -680,7 +683,9 @@ namespace tools bool deinit(); bool init(std::string daemon_address = "http://localhost:8080", - boost::optional<epee::net_utils::http::login> daemon_login = boost::none, uint64_t upper_transaction_weight_limit = 0, + boost::optional<epee::net_utils::http::login> daemon_login = boost::none, + boost::asio::ip::tcp::endpoint proxy = {}, + uint64_t upper_transaction_weight_limit = 0, bool trusted_daemon = true, epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect, const std::pair<std::string, std::string> &private_key_and_certificate_path = {}, @@ -1176,11 +1181,11 @@ namespace tools // fetch txs and store in m_payments void light_wallet_get_address_txs(); // get_address_info - bool light_wallet_get_address_info(cryptonote::COMMAND_RPC_GET_ADDRESS_INFO::response &response); + bool light_wallet_get_address_info(tools::COMMAND_RPC_GET_ADDRESS_INFO::response &response); // Login. new_address is true if address hasn't been used on lw node before. bool light_wallet_login(bool &new_address); // Send an import request to lw node. returns info about import fee, address and payment_id - bool light_wallet_import_wallet_request(cryptonote::COMMAND_RPC_IMPORT_WALLET_REQUEST::response &response); + bool light_wallet_import_wallet_request(tools::COMMAND_RPC_IMPORT_WALLET_REQUEST::response &response); // get random outputs from light wallet server void light_wallet_get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count); // Parse rct string @@ -1243,6 +1248,14 @@ namespace tools bool unblackball_output(const std::pair<uint64_t, uint64_t> &output); bool is_output_blackballed(const std::pair<uint64_t, uint64_t> &output) const; + void freeze(size_t idx); + void thaw(size_t idx); + bool frozen(size_t idx) const; + void freeze(const crypto::key_image &ki); + void thaw(const crypto::key_image &ki); + bool frozen(const crypto::key_image &ki) const; + bool frozen(const transfer_details &td) const; + // MMS ------------------------------------------------------------------------------------------------- mms::message_store& get_message_store() { return m_message_store; }; const mms::message_store& get_message_store() const { return m_message_store; }; @@ -1278,7 +1291,7 @@ namespace tools bool load_keys(const std::string& keys_file_name, const epee::wipeable_string& password); void process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen, const tx_cache_data &tx_cache_data, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL); void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const parsed_block &parsed_block, const crypto::hash& bl_id, uint64_t height, const std::vector<tx_cache_data> &tx_cache_data, size_t tx_cache_data_offset, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL); - void detach_blockchain(uint64_t height); + void detach_blockchain(uint64_t height, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL); void get_short_chain_history(std::list<crypto::hash>& ids, uint64_t granularity = 1) const; bool clear(); void clear_soft(bool keep_key_images=false); @@ -1325,6 +1338,7 @@ namespace tools bool get_ring(const crypto::chacha_key &key, const crypto::key_image &key_image, std::vector<uint64_t> &outs); crypto::chacha_key get_ringdb_key(); void setup_keys(const epee::wipeable_string &password); + size_t get_transfer_details(const crypto::key_image &ki) const; void register_devices(); hw::device& lookup_device(const std::string & device_descriptor); @@ -1479,7 +1493,7 @@ namespace tools }; } BOOST_CLASS_VERSION(tools::wallet2, 28) -BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 11) +BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 12) BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1) BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0) BOOST_CLASS_VERSION(tools::wallet2::multisig_tx_set, 1) @@ -1491,7 +1505,7 @@ BOOST_CLASS_VERSION(tools::wallet2::address_book_row, 17) BOOST_CLASS_VERSION(tools::wallet2::reserve_proof_entry, 0) BOOST_CLASS_VERSION(tools::wallet2::unsigned_tx_set, 0) BOOST_CLASS_VERSION(tools::wallet2::signed_tx_set, 1) -BOOST_CLASS_VERSION(tools::wallet2::tx_construction_data, 3) +BOOST_CLASS_VERSION(tools::wallet2::tx_construction_data, 4) BOOST_CLASS_VERSION(tools::wallet2::pending_tx, 3) BOOST_CLASS_VERSION(tools::wallet2::multisig_sig, 0) @@ -1541,6 +1555,10 @@ namespace boost { x.m_key_image_request = false; } + if (ver < 12) + { + x.m_frozen = false; + } } template <class Archive> @@ -1629,8 +1647,17 @@ namespace boost } a & x.m_key_image_request; if (ver < 11) + { + initialize_transfer_details(a, x, ver); return; + } a & x.m_uses; + if (ver < 12) + { + initialize_transfer_details(a, x, ver); + return; + } + a & x.m_frozen; } template <class Archive> @@ -1860,11 +1887,27 @@ namespace boost a & x.subaddr_account; a & x.subaddr_indices; if (ver < 2) + { + if (!typename Archive::is_saving()) + x.rct_config = { rct::RangeProofBorromean, 0 }; return; + } a & x.selected_transfers; if (ver < 3) + { + if (!typename Archive::is_saving()) + x.rct_config = { rct::RangeProofBorromean, 0 }; + return; + } + if (ver < 4) + { + bool use_bulletproofs = x.rct_config.range_proof_type != rct::RangeProofBorromean; + a & use_bulletproofs; + if (!typename Archive::is_saving()) + x.rct_config = { use_bulletproofs ? rct::RangeProofBulletproof : rct::RangeProofBorromean, 0 }; return; - a & x.use_bulletproofs; + } + a & x.rct_config; } template <class Archive> diff --git a/src/wallet/wallet_light_rpc.h b/src/wallet/wallet_light_rpc.h new file mode 100644 index 000000000..1d35cec33 --- /dev/null +++ b/src/wallet/wallet_light_rpc.h @@ -0,0 +1,320 @@ +// Copyright (c) 2014-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. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once +#include "cryptonote_basic/cryptonote_basic.h" +#include "crypto/hash.h" + +namespace tools +{ + //----------------------------------------------- + struct COMMAND_RPC_GET_ADDRESS_TXS + { + struct request_t + { + std::string address; + std::string view_key; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(address) + KV_SERIALIZE(view_key) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<request_t> request; + + struct spent_output { + uint64_t amount; + std::string key_image; + std::string tx_pub_key; + uint64_t out_index; + uint32_t mixin; + + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(amount) + KV_SERIALIZE(key_image) + KV_SERIALIZE(tx_pub_key) + KV_SERIALIZE(out_index) + KV_SERIALIZE(mixin) + END_KV_SERIALIZE_MAP() + }; + + struct transaction + { + uint64_t id; + std::string hash; + uint64_t timestamp; + uint64_t total_received; + uint64_t total_sent; + uint64_t unlock_time; + uint64_t height; + std::list<spent_output> spent_outputs; + std::string payment_id; + bool coinbase; + bool mempool; + uint32_t mixin; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(id) + KV_SERIALIZE(hash) + KV_SERIALIZE(timestamp) + KV_SERIALIZE(total_received) + KV_SERIALIZE(total_sent) + KV_SERIALIZE(unlock_time) + KV_SERIALIZE(height) + KV_SERIALIZE(spent_outputs) + KV_SERIALIZE(payment_id) + KV_SERIALIZE(coinbase) + KV_SERIALIZE(mempool) + KV_SERIALIZE(mixin) + END_KV_SERIALIZE_MAP() + }; + + + struct response_t + { + //std::list<std::string> txs_as_json; + uint64_t total_received; + uint64_t total_received_unlocked = 0; // OpenMonero only + uint64_t scanned_height; + std::vector<transaction> transactions; + uint64_t blockchain_height; + uint64_t scanned_block_height; + std::string status; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(total_received) + KV_SERIALIZE(total_received_unlocked) + KV_SERIALIZE(scanned_height) + KV_SERIALIZE(transactions) + KV_SERIALIZE(blockchain_height) + KV_SERIALIZE(scanned_block_height) + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<response_t> response; + }; + + //----------------------------------------------- + struct COMMAND_RPC_GET_ADDRESS_INFO + { + struct request_t + { + std::string address; + std::string view_key; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(address) + KV_SERIALIZE(view_key) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<request_t> request; + + struct spent_output + { + uint64_t amount; + std::string key_image; + std::string tx_pub_key; + uint64_t out_index; + uint32_t mixin; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(amount) + KV_SERIALIZE(key_image) + KV_SERIALIZE(tx_pub_key) + KV_SERIALIZE(out_index) + KV_SERIALIZE(mixin) + END_KV_SERIALIZE_MAP() + }; + + struct response_t + { + uint64_t locked_funds; + uint64_t total_received; + uint64_t total_sent; + uint64_t scanned_height; + uint64_t scanned_block_height; + uint64_t start_height; + uint64_t transaction_height; + uint64_t blockchain_height; + std::list<spent_output> spent_outputs; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(locked_funds) + KV_SERIALIZE(total_received) + KV_SERIALIZE(total_sent) + KV_SERIALIZE(scanned_height) + KV_SERIALIZE(scanned_block_height) + KV_SERIALIZE(start_height) + KV_SERIALIZE(transaction_height) + KV_SERIALIZE(blockchain_height) + KV_SERIALIZE(spent_outputs) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<response_t> response; + }; + + //----------------------------------------------- + struct COMMAND_RPC_GET_UNSPENT_OUTS + { + struct request_t + { + std::string amount; + std::string address; + std::string view_key; + // OpenMonero specific + uint64_t mixin; + bool use_dust; + std::string dust_threshold; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(amount) + KV_SERIALIZE(address) + KV_SERIALIZE(view_key) + KV_SERIALIZE(mixin) + KV_SERIALIZE(use_dust) + KV_SERIALIZE(dust_threshold) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<request_t> request; + + + struct output { + uint64_t amount; + std::string public_key; + uint64_t index; + uint64_t global_index; + std::string rct; + std::string tx_hash; + std::string tx_pub_key; + std::string tx_prefix_hash; + std::vector<std::string> spend_key_images; + uint64_t timestamp; + uint64_t height; + + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(amount) + KV_SERIALIZE(public_key) + KV_SERIALIZE(index) + KV_SERIALIZE(global_index) + KV_SERIALIZE(rct) + KV_SERIALIZE(tx_hash) + KV_SERIALIZE(tx_pub_key) + KV_SERIALIZE(tx_prefix_hash) + KV_SERIALIZE(spend_key_images) + KV_SERIALIZE(timestamp) + KV_SERIALIZE(height) + END_KV_SERIALIZE_MAP() + }; + + struct response_t + { + uint64_t amount; + std::list<output> outputs; + uint64_t per_kb_fee; + std::string status; + std::string reason; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(amount) + KV_SERIALIZE(outputs) + KV_SERIALIZE(per_kb_fee) + KV_SERIALIZE(status) + KV_SERIALIZE(reason) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<response_t> response; + }; + //----------------------------------------------- + struct COMMAND_RPC_LOGIN + { + struct request_t + { + std::string address; + std::string view_key; + bool create_account; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(address) + KV_SERIALIZE(view_key) + KV_SERIALIZE(create_account) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<request_t> request; + + struct response_t + { + std::string status; + std::string reason; + bool new_address; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + KV_SERIALIZE(reason) + KV_SERIALIZE(new_address) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<response_t> response; + }; + //----------------------------------------------- + struct COMMAND_RPC_IMPORT_WALLET_REQUEST + { + struct request_t + { + std::string address; + std::string view_key; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(address) + KV_SERIALIZE(view_key) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<request_t> request; + + struct response_t + { + std::string payment_id; + uint64_t import_fee; + bool new_request; + bool request_fulfilled; + std::string payment_address; + std::string status; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(payment_id) + KV_SERIALIZE(import_fee) + KV_SERIALIZE(new_request) + KV_SERIALIZE(request_fulfilled) + KV_SERIALIZE(payment_address) + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<response_t> response; + }; + //----------------------------------------------- +} diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 7040597df..f80263007 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -56,6 +56,8 @@ using namespace epee; #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "wallet.rpc" +#define DEFAULT_AUTO_REFRESH_PERIOD 20 // seconds + namespace { const command_line::arg_descriptor<std::string, true> arg_rpc_bind_port = {"rpc-bind-port", "Sets bind port for server"}; @@ -124,13 +126,18 @@ namespace tools { m_stop = false; m_net_server.add_idle_handler([this](){ + if (m_auto_refresh_period == 0) // disabled + return true; + if (boost::posix_time::microsec_clock::universal_time() < m_last_auto_refresh_time + boost::posix_time::seconds(m_auto_refresh_period)) + return true; try { if (m_wallet) m_wallet->refresh(m_wallet->is_trusted_daemon()); } catch (const std::exception& ex) { LOG_ERROR("Exception at while refreshing, what=" << ex.what()); } + m_last_auto_refresh_time = boost::posix_time::microsec_clock::universal_time(); return true; - }, 20000); + }, 1000); m_net_server.add_idle_handler([this](){ if (m_stop.load(std::memory_order_relaxed)) { @@ -263,6 +270,9 @@ namespace tools std::vector<std::vector<uint8_t>> allowed_fingerprints{ rpc_ssl_allowed_fingerprints.size() }; std::transform(rpc_ssl_allowed_fingerprints.begin(), rpc_ssl_allowed_fingerprints.end(), allowed_fingerprints.begin(), epee::from_hex::vector); + m_auto_refresh_period = DEFAULT_AUTO_REFRESH_PERIOD; + m_last_auto_refresh_time = boost::posix_time::min_date_time; + m_net_server.set_threads_prefix("RPC"); auto rng = [](size_t len, uint8_t *ptr) { return crypto::rand(len, ptr); }; return epee::http_server_impl_base<wallet_rpc_server, connection_context>::init( @@ -319,6 +329,8 @@ namespace tools entry.type = "out"; entry.subaddr_index = { pd.m_subaddr_account, 0 }; + for (uint32_t i: pd.m_subaddr_indices) + entry.subaddr_indices.push_back({pd.m_subaddr_account, i}); entry.address = m_wallet->get_subaddress_as_str({pd.m_subaddr_account, 0}); set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward()); } @@ -339,6 +351,8 @@ namespace tools entry.note = m_wallet->get_tx_note(txid); entry.type = is_failed ? "failed" : "pending"; entry.subaddr_index = { pd.m_subaddr_account, 0 }; + for (uint32_t i: pd.m_subaddr_indices) + entry.subaddr_indices.push_back({pd.m_subaddr_account, i}); entry.address = m_wallet->get_subaddress_as_str({pd.m_subaddr_account, 0}); set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward()); } @@ -887,15 +901,7 @@ namespace tools try { - uint64_t mixin; - if(req.ring_size != 0) - { - mixin = m_wallet->adjust_mixin(req.ring_size - 1); - } - else - { - mixin = m_wallet->adjust_mixin(req.mixin); - } + uint64_t mixin = m_wallet->adjust_mixin(req.ring_size ? req.ring_size - 1 : 0); uint32_t priority = m_wallet->adjust_priority(req.priority); std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices); @@ -947,15 +953,7 @@ namespace tools try { - uint64_t mixin; - if(req.ring_size != 0) - { - mixin = m_wallet->adjust_mixin(req.ring_size - 1); - } - else - { - mixin = m_wallet->adjust_mixin(req.mixin); - } + uint64_t mixin = m_wallet->adjust_mixin(req.ring_size ? req.ring_size - 1 : 0); uint32_t priority = m_wallet->adjust_priority(req.priority); LOG_PRINT_L2("on_transfer_split calling create_transactions_2"); std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices); @@ -1365,15 +1363,7 @@ namespace tools try { - uint64_t mixin; - if(req.ring_size != 0) - { - mixin = m_wallet->adjust_mixin(req.ring_size - 1); - } - else - { - mixin = m_wallet->adjust_mixin(req.mixin); - } + uint64_t mixin = m_wallet->adjust_mixin(req.ring_size ? req.ring_size - 1 : 0); uint32_t priority = m_wallet->adjust_priority(req.priority); std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, req.outputs, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices); @@ -1428,15 +1418,7 @@ namespace tools try { - uint64_t mixin; - if(req.ring_size != 0) - { - mixin = m_wallet->adjust_mixin(req.ring_size - 1); - } - else - { - mixin = m_wallet->adjust_mixin(req.mixin); - } + uint64_t mixin = m_wallet->adjust_mixin(req.ring_size ? req.ring_size - 1 : 0); uint32_t priority = m_wallet->adjust_priority(req.priority); std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_single(ki, dsts[0].addr, dsts[0].is_subaddress, req.outputs, mixin, req.unlock_time, priority, extra); @@ -2830,6 +2812,28 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_auto_refresh(const wallet_rpc::COMMAND_RPC_AUTO_REFRESH::request& req, wallet_rpc::COMMAND_RPC_AUTO_REFRESH::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (m_restricted) + { + er.code = WALLET_RPC_ERROR_CODE_DENIED; + er.message = "Command unavailable in restricted mode."; + return false; + } + try + { + m_auto_refresh_period = req.enable ? req.period ? req.period : DEFAULT_AUTO_REFRESH_PERIOD : 0; + MINFO("Auto refresh now " << (m_auto_refresh_period ? std::to_string(m_auto_refresh_period) + " seconds" : std::string("disabled"))); + return true; + } + catch (const std::exception& e) + { + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); + return false; + } + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_rescan_spent(const wallet_rpc::COMMAND_RPC_RESCAN_SPENT::request& req, wallet_rpc::COMMAND_RPC_RESCAN_SPENT::response& res, epee::json_rpc::error& er, const connection_context *ctx) { if (!m_wallet) return not_open(er); @@ -2932,7 +2936,7 @@ namespace tools er.message = "Invalid filename"; return false; } - std::string wallet_file = m_wallet_dir + "/" + req.filename; + std::string wallet_file = req.filename.empty() ? "" : (m_wallet_dir + "/" + req.filename); { std::vector<std::string> languages; crypto::ElectrumWords::get_language_list(languages); @@ -3217,12 +3221,6 @@ namespace tools } // early check for mandatory fields - if (req.filename.empty()) - { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = "field 'filename' is mandatory. Please provide a filename to save the restored wallet to."; - return false; - } if (req.viewkey.empty()) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; @@ -3251,7 +3249,7 @@ namespace tools er.message = "Invalid filename"; return false; } - std::string wallet_file = m_wallet_dir + "/" + req.filename; + std::string wallet_file = req.filename.empty() ? "" : (m_wallet_dir + "/" + req.filename); // check if wallet file already exists if (!wallet_file.empty()) { @@ -3361,7 +3359,8 @@ namespace tools { try { - m_wallet->store(); + if (!wallet_file.empty()) + m_wallet->store(); } catch (const std::exception &e) { @@ -3385,12 +3384,6 @@ namespace tools } // early check for mandatory fields - if (req.filename.empty()) - { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = "field 'filename' is mandatory. Please provide a filename to save the restored wallet to."; - return false; - } if (req.seed.empty()) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; @@ -3413,7 +3406,7 @@ namespace tools er.message = "Invalid filename"; return false; } - std::string wallet_file = m_wallet_dir + "/" + req.filename; + std::string wallet_file = req.filename.empty() ? "" : (m_wallet_dir + "/" + req.filename); // check if wallet file already exists if (!wallet_file.empty()) { @@ -3529,7 +3522,7 @@ namespace tools er.message = "Failed to encode seed"; return false; } - res.seed = electrum_words.data(); + res.seed = std::string(electrum_words.data(), electrum_words.size()); if (!wal) { diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index affaf10f7..2b52275b8 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -129,6 +129,7 @@ namespace tools MAP_JON_RPC_WE("add_address_book", on_add_address_book, wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY) MAP_JON_RPC_WE("delete_address_book",on_delete_address_book,wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY) MAP_JON_RPC_WE("refresh", on_refresh, wallet_rpc::COMMAND_RPC_REFRESH) + MAP_JON_RPC_WE("auto_refresh", on_auto_refresh, wallet_rpc::COMMAND_RPC_AUTO_REFRESH) MAP_JON_RPC_WE("rescan_spent", on_rescan_spent, wallet_rpc::COMMAND_RPC_RESCAN_SPENT) MAP_JON_RPC_WE("start_mining", on_start_mining, wallet_rpc::COMMAND_RPC_START_MINING) MAP_JON_RPC_WE("stop_mining", on_stop_mining, wallet_rpc::COMMAND_RPC_STOP_MINING) @@ -210,6 +211,7 @@ namespace tools bool on_add_address_book(const wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_delete_address_book(const wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_refresh(const wallet_rpc::COMMAND_RPC_REFRESH::request& req, wallet_rpc::COMMAND_RPC_REFRESH::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); + bool on_auto_refresh(const wallet_rpc::COMMAND_RPC_AUTO_REFRESH::request& req, wallet_rpc::COMMAND_RPC_AUTO_REFRESH::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_rescan_spent(const wallet_rpc::COMMAND_RPC_RESCAN_SPENT::request& req, wallet_rpc::COMMAND_RPC_RESCAN_SPENT::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_start_mining(const wallet_rpc::COMMAND_RPC_START_MINING::request& req, wallet_rpc::COMMAND_RPC_START_MINING::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_stop_mining(const wallet_rpc::COMMAND_RPC_STOP_MINING::request& req, wallet_rpc::COMMAND_RPC_STOP_MINING::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); @@ -256,5 +258,7 @@ namespace tools std::atomic<bool> m_stop; bool m_restricted; const boost::program_options::variables_map *m_vm; + uint32_t m_auto_refresh_period; + boost::posix_time::ptime m_last_auto_refresh_time; }; } diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index b0e8bed93..de3067a52 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -446,7 +446,6 @@ namespace wallet_rpc uint32_t account_index; std::set<uint32_t> subaddr_indices; uint32_t priority; - uint64_t mixin; uint64_t ring_size; uint64_t unlock_time; std::string payment_id; @@ -460,7 +459,6 @@ namespace wallet_rpc KV_SERIALIZE(account_index) KV_SERIALIZE(subaddr_indices) KV_SERIALIZE(priority) - KV_SERIALIZE_OPT(mixin, (uint64_t)0) KV_SERIALIZE_OPT(ring_size, (uint64_t)0) KV_SERIALIZE(unlock_time) KV_SERIALIZE(payment_id) @@ -505,7 +503,6 @@ namespace wallet_rpc uint32_t account_index; std::set<uint32_t> subaddr_indices; uint32_t priority; - uint64_t mixin; uint64_t ring_size; uint64_t unlock_time; std::string payment_id; @@ -519,7 +516,6 @@ namespace wallet_rpc KV_SERIALIZE(account_index) KV_SERIALIZE(subaddr_indices) KV_SERIALIZE(priority) - KV_SERIALIZE_OPT(mixin, (uint64_t)0) KV_SERIALIZE_OPT(ring_size, (uint64_t)0) KV_SERIALIZE(unlock_time) KV_SERIALIZE(payment_id) @@ -746,7 +742,6 @@ namespace wallet_rpc uint32_t account_index; std::set<uint32_t> subaddr_indices; uint32_t priority; - uint64_t mixin; uint64_t ring_size; uint64_t outputs; uint64_t unlock_time; @@ -762,7 +757,6 @@ namespace wallet_rpc KV_SERIALIZE(account_index) KV_SERIALIZE(subaddr_indices) KV_SERIALIZE(priority) - KV_SERIALIZE_OPT(mixin, (uint64_t)0) KV_SERIALIZE_OPT(ring_size, (uint64_t)0) KV_SERIALIZE_OPT(outputs, (uint64_t)1) KV_SERIALIZE(unlock_time) @@ -816,7 +810,6 @@ namespace wallet_rpc { std::string address; uint32_t priority; - uint64_t mixin; uint64_t ring_size; uint64_t outputs; uint64_t unlock_time; @@ -830,7 +823,6 @@ namespace wallet_rpc BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(address) KV_SERIALIZE(priority) - KV_SERIALIZE_OPT(mixin, (uint64_t)0) KV_SERIALIZE_OPT(ring_size, (uint64_t)0) KV_SERIALIZE_OPT(outputs, (uint64_t)1) KV_SERIALIZE(unlock_time) @@ -1359,6 +1351,7 @@ namespace wallet_rpc std::string type; uint64_t unlock_time; cryptonote::subaddress_index subaddr_index; + std::vector<cryptonote::subaddress_index> subaddr_indices; std::string address; bool double_spend_seen; uint64_t confirmations; @@ -1376,6 +1369,7 @@ namespace wallet_rpc KV_SERIALIZE(type); KV_SERIALIZE(unlock_time) KV_SERIALIZE(subaddr_index); + KV_SERIALIZE(subaddr_indices); KV_SERIALIZE(address); KV_SERIALIZE(double_spend_seen) KV_SERIALIZE_OPT(confirmations, (uint64_t)0) @@ -1930,6 +1924,28 @@ namespace wallet_rpc typedef epee::misc_utils::struct_init<response_t> response; }; + struct COMMAND_RPC_AUTO_REFRESH + { + struct request_t + { + bool enable; + uint32_t period; // seconds + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_OPT(enable, true) + KV_SERIALIZE_OPT(period, (uint32_t)0) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<request_t> request; + + struct response_t + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<response_t> response; + }; + struct COMMAND_RPC_START_MINING { struct request_t |