diff options
Diffstat (limited to 'src')
34 files changed, 829 insertions, 329 deletions
diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index 263948fa2..a163ef98c 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -1883,16 +1883,18 @@ public: } virtual ~db_txn_guard() { - if (active) - stop(); + stop(); } void stop() { - if (readonly) - db->block_rtxn_stop(); - else - db->block_wtxn_stop(); - active = false; + if (active) + { + if (readonly) + db->block_rtxn_stop(); + else + db->block_wtxn_stop(); + active = false; + } } void abort() { diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index db7fa6c7c..f80013d02 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -465,6 +465,32 @@ void mdb_txn_safe::increment_txns(int i) num_active_txns += i; } +#define TXN_PREFIX(flags); \ + mdb_txn_safe auto_txn; \ + mdb_txn_safe* txn_ptr = &auto_txn; \ + if (m_batch_active) \ + txn_ptr = m_write_txn; \ + else \ + { \ + if (auto mdb_res = lmdb_txn_begin(m_env, NULL, flags, auto_txn)) \ + throw0(DB_ERROR(lmdb_error(std::string("Failed to create a transaction for the db in ")+__FUNCTION__+": ", mdb_res).c_str())); \ + } \ + +#define TXN_PREFIX_RDONLY() \ + MDB_txn *m_txn; \ + mdb_txn_cursors *m_cursors; \ + mdb_txn_safe auto_txn; \ + bool my_rtxn = block_rtxn_start(&m_txn, &m_cursors); \ + if (my_rtxn) auto_txn.m_tinfo = m_tinfo.get(); \ + else auto_txn.uncheck() +#define TXN_POSTFIX_RDONLY() + +#define TXN_POSTFIX_SUCCESS() \ + do { \ + if (! m_batch_active) \ + auto_txn.commit(); \ + } while(0) + void lmdb_resized(MDB_env *env, int isactive) { mdb_txn_safe::prevent_new_txns(); @@ -713,21 +739,20 @@ uint64_t BlockchainLMDB::get_estimated_batch_size(uint64_t batch_num_blocks, uin } else { - MDB_txn *rtxn; - mdb_txn_cursors *rcurs; - bool my_rtxn = block_rtxn_start(&rtxn, &rcurs); - for (uint64_t block_num = block_start; block_num <= block_stop; ++block_num) { - // we have access to block weight, which will be greater or equal to block size, - // so use this as a proxy. If it's too much off, we might have to check actual size, - // which involves reading more data, so is not really wanted - size_t block_weight = get_block_weight(block_num); - total_block_size += block_weight; - // Track number of blocks being totalled here instead of assuming, in case - // some blocks were to be skipped for being outliers. - ++num_blocks_used; + TXN_PREFIX_RDONLY(); + for (uint64_t block_num = block_start; block_num <= block_stop; ++block_num) + { + // we have access to block weight, which will be greater or equal to block size, + // so use this as a proxy. If it's too much off, we might have to check actual size, + // which involves reading more data, so is not really wanted + size_t block_weight = get_block_weight(block_num); + total_block_size += block_weight; + // Track number of blocks being totalled here instead of assuming, in case + // some blocks were to be skipped for being outliers. + ++num_blocks_used; + } } - if (my_rtxn) block_rtxn_stop(); avg_block_size = total_block_size / (num_blocks_used ? num_blocks_used : 1); MDEBUG("average block size across recent " << num_blocks_used << " blocks: " << avg_block_size); } @@ -1678,32 +1703,6 @@ void BlockchainLMDB::unlock() check_open(); } -#define TXN_PREFIX(flags); \ - mdb_txn_safe auto_txn; \ - mdb_txn_safe* txn_ptr = &auto_txn; \ - if (m_batch_active) \ - txn_ptr = m_write_txn; \ - else \ - { \ - if (auto mdb_res = lmdb_txn_begin(m_env, NULL, flags, auto_txn)) \ - throw0(DB_ERROR(lmdb_error(std::string("Failed to create a transaction for the db in ")+__FUNCTION__+": ", mdb_res).c_str())); \ - } \ - -#define TXN_PREFIX_RDONLY() \ - MDB_txn *m_txn; \ - mdb_txn_cursors *m_cursors; \ - mdb_txn_safe auto_txn; \ - bool my_rtxn = block_rtxn_start(&m_txn, &m_cursors); \ - if (my_rtxn) auto_txn.m_tinfo = m_tinfo.get(); \ - else auto_txn.uncheck() -#define TXN_POSTFIX_RDONLY() - -#define TXN_POSTFIX_SUCCESS() \ - do { \ - if (! m_batch_active) \ - auto_txn.commit(); \ - } while(0) - // The below two macros are for DB access within block add/remove, whether // regular batch txn is in use or not. m_write_txn is used as a batch txn, even @@ -3923,13 +3922,20 @@ void BlockchainLMDB::block_rtxn_stop() const LOG_PRINT_L3("BlockchainLMDB::" << __func__); mdb_txn_reset(m_tinfo->m_ti_rtxn); memset(&m_tinfo->m_ti_rflags, 0, sizeof(m_tinfo->m_ti_rflags)); + /* cancel out the increment from rtxn_start */ + mdb_txn_safe::increment_txns(-1); } bool BlockchainLMDB::block_rtxn_start() const { MDB_txn *mtxn; mdb_txn_cursors *mcur; - return block_rtxn_start(&mtxn, &mcur); + /* auto_txn is only used for the create gate */ + mdb_txn_safe auto_txn; + bool ret = block_rtxn_start(&mtxn, &mcur); + if (ret) + auto_txn.increment_txns(1); /* remember there is an active readtxn */ + return ret; } void BlockchainLMDB::block_wtxn_start() diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp index 6ab6ff4fe..3fbd69949 100644 --- a/src/common/dns_utils.cpp +++ b/src/common/dns_utils.cpp @@ -102,7 +102,6 @@ get_builtin_ds(void) { static const char * const ds[] = { - ". IN DS 19036 8 2 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5\n", ". IN DS 20326 8 2 E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D\n", NULL }; diff --git a/src/crypto/hash-extra-jh.c b/src/crypto/hash-extra-jh.c index 4d7481c07..52efd4ae3 100644 --- a/src/crypto/hash-extra-jh.c +++ b/src/crypto/hash-extra-jh.c @@ -36,7 +36,9 @@ #include "jh.h" #include "hash-ops.h" +#define JH_HASH_BITLEN HASH_SIZE * 8 + void hash_extra_jh(const void *data, size_t length, char *hash) { - int r = jh_hash(HASH_SIZE * 8, data, 8 * length, (uint8_t*)hash); - assert(SUCCESS == r); + // No need to check for failure b/c jh_hash only fails for invalid hash size + jh_hash(JH_HASH_BITLEN, data, 8 * length, (uint8_t*)hash); } diff --git a/src/crypto/hash-extra-skein.c b/src/crypto/hash-extra-skein.c index 9ea9c4faa..3eacaba58 100644 --- a/src/crypto/hash-extra-skein.c +++ b/src/crypto/hash-extra-skein.c @@ -34,7 +34,9 @@ #include "hash-ops.h" #include "skein.h" +#define SKEIN_HASH_BITLEN HASH_SIZE * 8 + void hash_extra_skein(const void *data, size_t length, char *hash) { - int r = skein_hash(8 * HASH_SIZE, data, 8 * length, (uint8_t*)hash); - assert(SKEIN_SUCCESS == r); + // No need to check for failure b/c skein_hash only fails for invalid hash size + skein_hash(SKEIN_HASH_BITLEN, data, 8 * length, (uint8_t*)hash); } diff --git a/src/crypto/keccak.c b/src/crypto/keccak.c index f098cbdf0..6616d3530 100644 --- a/src/crypto/keccak.c +++ b/src/crypto/keccak.c @@ -123,7 +123,7 @@ void keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen) size_t i, rsiz, rsizw; static_assert(HASH_DATA_AREA <= sizeof(temp), "Bad keccak preconditions"); - if (mdlen <= 0 || (mdlen > 100 && sizeof(st) != (size_t)mdlen)) + if (mdlen <= 0 || (mdlen >= 100 && sizeof(st) != (size_t)mdlen)) { local_abort("Bad keccak use"); } diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 388013f96..829e5fc70 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -989,7 +989,7 @@ namespace cryptonote return true; } //--------------------------------------------------------------- - bool out_can_be_to_acc(const boost::optional<crypto::view_tag>& view_tag_opt, const crypto::key_derivation& derivation, const size_t output_index) + bool out_can_be_to_acc(const boost::optional<crypto::view_tag>& view_tag_opt, const crypto::key_derivation& derivation, const size_t output_index, hw::device* hwdev) { // If there is no view tag to check, the output can possibly belong to the account. // Will need to derive the output pub key to be certain whether or not the output belongs to the account. @@ -1002,7 +1002,15 @@ namespace cryptonote // Therefore can fail out early to avoid expensive crypto ops needlessly deriving output public key to // determine if output belongs to the account. crypto::view_tag derived_view_tag; - crypto::derive_view_tag(derivation, output_index, derived_view_tag); + if (hwdev != nullptr) + { + bool r = hwdev->derive_view_tag(derivation, output_index, derived_view_tag); + CHECK_AND_ASSERT_MES(r, false, "Failed to derive view tag"); + } + else + { + crypto::derive_view_tag(derivation, output_index, derived_view_tag); + } return view_tag == derived_view_tag; } //--------------------------------------------------------------- @@ -1012,7 +1020,7 @@ namespace cryptonote bool r = acc.get_device().generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation); CHECK_AND_ASSERT_MES(r, false, "Failed to generate key derivation"); crypto::public_key pk; - if (out_can_be_to_acc(view_tag_opt, derivation, output_index)) + if (out_can_be_to_acc(view_tag_opt, derivation, output_index, &acc.get_device())) { r = acc.get_device().derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk); CHECK_AND_ASSERT_MES(r, false, "Failed to derive public key"); @@ -1026,7 +1034,7 @@ namespace cryptonote CHECK_AND_ASSERT_MES(output_index < additional_tx_pub_keys.size(), false, "wrong number of additional tx pubkeys"); r = acc.get_device().generate_key_derivation(additional_tx_pub_keys[output_index], acc.m_view_secret_key, derivation); CHECK_AND_ASSERT_MES(r, false, "Failed to generate key derivation"); - if (out_can_be_to_acc(view_tag_opt, derivation, output_index)) + if (out_can_be_to_acc(view_tag_opt, derivation, output_index, &acc.get_device())) { r = acc.get_device().derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk); CHECK_AND_ASSERT_MES(r, false, "Failed to derive public key"); @@ -1040,7 +1048,7 @@ namespace cryptonote { // try the shared tx pubkey crypto::public_key subaddress_spendkey; - if (out_can_be_to_acc(view_tag_opt, derivation, output_index)) + if (out_can_be_to_acc(view_tag_opt, derivation, output_index, &hwdev)) { CHECK_AND_ASSERT_MES(hwdev.derive_subaddress_public_key(out_key, derivation, output_index, subaddress_spendkey), boost::none, "Failed to derive subaddress public key"); auto found = subaddresses.find(subaddress_spendkey); @@ -1052,7 +1060,7 @@ namespace cryptonote if (!additional_derivations.empty()) { CHECK_AND_ASSERT_MES(output_index < additional_derivations.size(), boost::none, "wrong number of additional derivations"); - if (out_can_be_to_acc(view_tag_opt, additional_derivations[output_index], output_index)) + if (out_can_be_to_acc(view_tag_opt, additional_derivations[output_index], output_index, &hwdev)) { CHECK_AND_ASSERT_MES(hwdev.derive_subaddress_public_key(out_key, additional_derivations[output_index], output_index, subaddress_spendkey), boost::none, "Failed to derive subaddress public key"); auto found = subaddresses.find(subaddress_spendkey); diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h index 8f5459ca7..b97c9d499 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.h +++ b/src/cryptonote_basic/cryptonote_format_utils.h @@ -91,7 +91,7 @@ namespace cryptonote bool get_encrypted_payment_id_from_tx_extra_nonce(const blobdata& extra_nonce, crypto::hash8& payment_id); void set_tx_out(const uint64_t amount, const crypto::public_key& output_public_key, const bool use_view_tags, const crypto::view_tag& view_tag, tx_out& out); bool check_output_types(const transaction& tx, const uint8_t hf_version); - bool out_can_be_to_acc(const boost::optional<crypto::view_tag>& view_tag_opt, const crypto::key_derivation& derivation, const size_t output_index); + bool out_can_be_to_acc(const boost::optional<crypto::view_tag>& view_tag_opt, const crypto::key_derivation& derivation, const size_t output_index, hw::device *hwdev = nullptr); bool is_out_to_acc(const account_keys& acc, const crypto::public_key& output_public_key, const crypto::public_key& tx_pub_key, const std::vector<crypto::public_key>& additional_tx_public_keys, size_t output_index, const boost::optional<crypto::view_tag>& view_tag_opt = boost::optional<crypto::view_tag>()); struct subaddress_receive_info { diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 7a94f6358..355d0de1a 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -159,6 +159,13 @@ namespace cryptonote bool deinit(); /** + * @brief get a set of blockchain checkpoint hashes + * + * @return set of blockchain checkpoint hashes + */ + const checkpoints& get_checkpoints() const { return m_checkpoints; } + + /** * @brief assign a set of blockchain checkpoint hashes * * @param chk_pts the set of checkpoints to assign diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index a78f5d673..31e4e0414 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -252,6 +252,10 @@ namespace cryptonote m_pprotocol = &m_protocol_stub; } //----------------------------------------------------------------------------------- + const checkpoints& core::get_checkpoints() const + { + return m_blockchain_storage.get_checkpoints(); + } void core::set_checkpoints(checkpoints&& chk_pts) { m_blockchain_storage.set_checkpoints(std::move(chk_pts)); @@ -1406,21 +1410,66 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- + bool core::notify_txpool_event(const epee::span<const cryptonote::blobdata> tx_blobs, epee::span<const crypto::hash> tx_hashes, epee::span<const cryptonote::transaction> txs, const std::vector<bool> &just_broadcasted) const + { + if (!m_zmq_pub) + return true; + + if (tx_blobs.size() != tx_hashes.size() || tx_blobs.size() != txs.size() || tx_blobs.size() != just_broadcasted.size()) + return false; + + /* Publish txs via ZMQ that are "just broadcasted" by the daemon. This is + done here in addition to `handle_incoming_txs` in order to guarantee txs + are pub'd via ZMQ when we know the daemon has/will broadcast to other + nodes & *after* the tx is visible in the pool. This should get called + when the user submits a tx to a daemon in the "fluff" epoch relaying txs + via a public network. */ + if (std::count(just_broadcasted.begin(), just_broadcasted.end(), true) == 0) + return true; + + std::vector<txpool_event> results{}; + results.resize(tx_blobs.size()); + for (std::size_t i = 0; i < results.size(); ++i) + { + results[i].tx = std::move(txs[i]); + results[i].hash = std::move(tx_hashes[i]); + results[i].blob_size = tx_blobs[i].size(); + results[i].weight = results[i].tx.pruned ? get_pruned_transaction_weight(results[i].tx) : get_transaction_weight(results[i].tx, results[i].blob_size); + results[i].res = just_broadcasted[i]; + } + + m_zmq_pub(std::move(results)); + + return true; + } + //----------------------------------------------------------------------------------------------- void core::on_transactions_relayed(const epee::span<const cryptonote::blobdata> tx_blobs, const relay_method tx_relay) { + // lock ensures duplicate txs aren't pub'd via zmq + CRITICAL_REGION_LOCAL(m_incoming_tx_lock); + std::vector<crypto::hash> tx_hashes{}; tx_hashes.resize(tx_blobs.size()); + std::vector<cryptonote::transaction> txs{}; + txs.resize(tx_blobs.size()); + for (std::size_t i = 0; i < tx_blobs.size(); ++i) { - cryptonote::transaction tx{}; - if (!parse_and_validate_tx_from_blob(tx_blobs[i], tx, tx_hashes[i])) + if (!parse_and_validate_tx_from_blob(tx_blobs[i], txs[i], tx_hashes[i])) { LOG_ERROR("Failed to parse relayed transaction"); return; } } - m_mempool.set_relayed(epee::to_span(tx_hashes), tx_relay); + + std::vector<bool> just_broadcasted{}; + just_broadcasted.reserve(tx_hashes.size()); + + m_mempool.set_relayed(epee::to_span(tx_hashes), tx_relay, just_broadcasted); + + if (m_zmq_pub && matches_category(tx_relay, relay_category::legacy)) + notify_txpool_event(tx_blobs, epee::to_span(tx_hashes), epee::to_span(txs), just_broadcasted); } //----------------------------------------------------------------------------------------------- bool core::get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash) diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 0b36730b6..6dc513570 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -437,6 +437,13 @@ namespace cryptonote void set_cryptonote_protocol(i_cryptonote_protocol* pprotocol); /** + * @copydoc Blockchain::get_checkpoints + * + * @note see Blockchain::get_checkpoints() + */ + const checkpoints& get_checkpoints() const; + + /** * @copydoc Blockchain::set_checkpoints * * @note see Blockchain::set_checkpoints() @@ -1036,6 +1043,13 @@ namespace cryptonote bool relay_txpool_transactions(); /** + * @brief sends notification of txpool events to subscribers + * + * @return true on success, false otherwise + */ + bool notify_txpool_event(const epee::span<const cryptonote::blobdata> tx_blobs, epee::span<const crypto::hash> tx_hashes, epee::span<const cryptonote::transaction> txs, const std::vector<bool> &just_broadcasted) const; + + /** * @brief checks DNS versions * * @return true on success, false otherwise diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index a68da0e62..2a514ceae 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -402,6 +402,19 @@ namespace cryptonote m_txpool_max_weight = bytes; } //--------------------------------------------------------------------------------- + void tx_memory_pool::reduce_txpool_weight(size_t weight) + { + if (weight > m_txpool_weight) + { + MERROR("Underflow in txpool weight"); + m_txpool_weight = 0; + } + else + { + m_txpool_weight -= weight; + } + } + //--------------------------------------------------------------------------------- void tx_memory_pool::prune(size_t bytes) { CRITICAL_REGION_LOCAL(m_transactions_lock); @@ -423,8 +436,14 @@ namespace cryptonote txpool_tx_meta_t meta; if (!m_blockchain.get_txpool_tx_meta(txid, meta)) { - MERROR("Failed to find tx_meta in txpool"); - return; + static bool warned = false; + if (!warned) + { + MERROR("Failed to find tx_meta in txpool (will only print once)"); + warned = true; + } + --it; + continue; } // don't prune the kept_by_block ones, they're likely added because we're adding a block with those if (meta.kept_by_block) @@ -442,7 +461,7 @@ namespace cryptonote // remove first, in case this throws, so key images aren't removed MINFO("Pruning tx " << txid << " from txpool: weight: " << meta.weight << ", fee/byte: " << it->first.first); m_blockchain.remove_txpool_tx(txid); - m_txpool_weight -= meta.weight; + reduce_txpool_weight(meta.weight); remove_transaction_keyimages(tx, txid); MINFO("Pruned tx " << txid << " from txpool: weight: " << meta.weight << ", fee/byte: " << it->first.first); m_txs_by_fee_and_receive_time.erase(it--); @@ -562,7 +581,7 @@ namespace cryptonote // remove first, in case this throws, so key images aren't removed m_blockchain.remove_txpool_tx(id); - m_txpool_weight -= tx_weight; + reduce_txpool_weight(tx_weight); remove_transaction_keyimages(tx, id); lock.commit(); } @@ -725,7 +744,7 @@ namespace cryptonote { // remove first, so we only remove key images if the tx removal succeeds m_blockchain.remove_txpool_tx(txid); - m_txpool_weight -= entry.second; + reduce_txpool_weight(entry.second); remove_transaction_keyimages(tx, txid); } } @@ -820,8 +839,10 @@ namespace cryptonote return true; } //--------------------------------------------------------------------------------- - void tx_memory_pool::set_relayed(const epee::span<const crypto::hash> hashes, const relay_method method) + void tx_memory_pool::set_relayed(const epee::span<const crypto::hash> hashes, const relay_method method, std::vector<bool> &just_broadcasted) { + just_broadcasted.clear(); + crypto::random_poisson_seconds embargo_duration{dandelionpp_embargo_average}; const auto now = std::chrono::system_clock::now(); uint64_t next_relay = uint64_t{std::numeric_limits<time_t>::max()}; @@ -831,12 +852,14 @@ namespace cryptonote LockedTXN lock(m_blockchain.get_db()); for (const auto& hash : hashes) { + bool was_just_broadcasted = false; try { txpool_tx_meta_t meta; if (m_blockchain.get_txpool_tx_meta(hash, meta)) { // txes can be received as "stem" or "fluff" in either order + const bool already_broadcasted = meta.matches(relay_category::broadcasted); meta.upgrade_relay_method(method); meta.relayed = true; @@ -849,6 +872,9 @@ namespace cryptonote meta.last_relayed_time = std::chrono::system_clock::to_time_t(now); m_blockchain.update_txpool_tx(hash, meta); + + // wait until db update succeeds to ensure tx is visible in the pool + was_just_broadcasted = !already_broadcasted && meta.matches(relay_category::broadcasted); } } catch (const std::exception &e) @@ -856,6 +882,7 @@ namespace cryptonote MERROR("Failed to update txpool transaction metadata: " << e.what()); // continue } + just_broadcasted.emplace_back(was_just_broadcasted); } lock.commit(); set_if_less(m_next_check, time_t(next_relay)); @@ -917,26 +944,61 @@ namespace cryptonote { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); - const relay_category category = include_sensitive ? relay_category::all : relay_category::broadcasted; - backlog.reserve(m_blockchain.get_txpool_tx_count(include_sensitive)); - txpool_tx_meta_t tmp_meta; - m_blockchain.for_all_txpool_txes([this, &backlog, &tmp_meta](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata_ref *bd){ - transaction tx; - if (!(meta.pruned ? parse_and_validate_tx_base_from_blob(*bd, tx) : parse_and_validate_tx_from_blob(*bd, tx))) + + std::vector<tx_block_template_backlog_entry> tmp; + uint64_t total_weight = 0; + + // First get everything from the mempool, filter it later + m_blockchain.for_all_txpool_txes([&tmp, &total_weight](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata_ref*){ + tmp.emplace_back(tx_block_template_backlog_entry{txid, meta.weight, meta.fee}); + total_weight += meta.weight; + return true; + }, false, include_sensitive ? relay_category::all : relay_category::broadcasted); + + // Limit backlog to 112.5% of current median weight. This is enough to mine a full block with the optimal block reward + const uint64_t median_weight = m_blockchain.get_current_cumulative_block_weight_median(); + const uint64_t max_backlog_weight = median_weight + (median_weight / 8); + + // If the total weight is too high, choose the best paying transactions + if (total_weight > max_backlog_weight) + std::sort(tmp.begin(), tmp.end(), [](const auto& a, const auto& b){ return a.fee * b.weight > b.fee * a.weight; }); + + backlog.clear(); + uint64_t w = 0; + + std::unordered_set<crypto::key_image> k_images; + + for (const tx_block_template_backlog_entry& e : tmp) + { + try { - MERROR("Failed to parse tx from txpool"); - // continue - return true; - } - tx.set_hash(txid); + txpool_tx_meta_t meta; + if (!m_blockchain.get_txpool_tx_meta(e.id, meta)) + continue; - tmp_meta = meta; + cryptonote::blobdata txblob; + if (!m_blockchain.get_txpool_tx_blob(e.id, txblob, relay_category::all)) + continue; - if (is_transaction_ready_to_go(tmp_meta, txid, *bd, tx)) - backlog.push_back({txid, meta.weight, meta.fee}); + cryptonote::transaction tx; + if (is_transaction_ready_to_go(meta, e.id, txblob, tx)) + { + if (have_key_images(k_images, tx)) + continue; + append_key_images(k_images, tx); - return true; - }, true, category); + backlog.push_back(e); + w += e.weight; + if (w > max_backlog_weight) + break; + } + } + catch (const std::exception &e) + { + MERROR("Failed to check transaction readiness: " << e.what()); + // continue, not fatal + } + } } //------------------------------------------------------------------ void tx_memory_pool::get_transaction_stats(struct txpool_stats& stats, bool include_sensitive) const diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 62bef6c06..45623fd14 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -266,7 +266,11 @@ namespace cryptonote void get_transaction_backlog(std::vector<tx_backlog_entry>& backlog, bool include_sensitive = false) const; /** - * @brief get (hash, weight, fee) for all transactions in the pool - the minimum required information to create a block template + * @brief get (hash, weight, fee) for transactions in the pool - the minimum required information to create a block template + * + * Not all transactions in the pool will be returned for performance reasons + * If there are too many transactions in the pool, only the highest-paying transactions + * will be returned - but enough for the miner to create a full block * * @param backlog return-by-reference that data * @param include_sensitive return stempool, anonymity-pool, and unrelayed txes @@ -353,8 +357,10 @@ namespace cryptonote * * @param hashes list of tx hashes that are about to be relayed * @param tx_relay update how the tx left this node + * @param just_broadcasted true if a tx was just broadcasted + * */ - void set_relayed(epee::span<const crypto::hash> hashes, relay_method tx_relay); + void set_relayed(epee::span<const crypto::hash> hashes, relay_method tx_relay, std::vector<bool> &just_broadcasted); /** * @brief get the total number of transactions in the pool @@ -406,6 +412,13 @@ namespace cryptonote */ void set_txpool_max_weight(size_t bytes); + /** + * @brief reduce the cumulative txpool weight by the weight provided + * + * @param weight the weight to reduce the total txpool weight by + */ + void reduce_txpool_weight(size_t weight); + #define CURRENT_MEMPOOL_ARCHIVE_VER 11 #define CURRENT_MEMPOOL_TX_DETAILS_ARCHIVE_VER 13 diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp index 73d9ebce1..3d90e0855 100644 --- a/src/daemon/main.cpp +++ b/src/daemon/main.cpp @@ -83,7 +83,7 @@ uint16_t parse_public_rpc_port(const po::variables_map &vm) } uint16_t rpc_port; - if (!string_tools::get_xtype_from_string(rpc_port, rpc_port_str)) + if (!epee::string_tools::get_xtype_from_string(rpc_port, rpc_port_str)) { throw std::runtime_error("invalid RPC port " + rpc_port_str); } diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index b6364ff77..0d3688c76 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -1063,7 +1063,7 @@ bool t_rpc_command_executor::print_transaction(crypto::hash transaction_hash, cryptonote::blobdata blob; std::string source = as_hex.empty() ? pruned_as_hex + prunable_as_hex : as_hex; bool pruned = !pruned_as_hex.empty() && prunable_as_hex.empty(); - if (!string_tools::parse_hexstr_to_binbuff(source, blob)) + if (!epee::string_tools::parse_hexstr_to_binbuff(source, blob)) { tools::fail_msg_writer() << "Failed to parse tx to get json format"; } diff --git a/src/device/device.hpp b/src/device/device.hpp index eca91006f..392703a24 100644 --- a/src/device/device.hpp +++ b/src/device/device.hpp @@ -177,6 +177,7 @@ namespace hw { virtual bool derive_public_key(const crypto::key_derivation &derivation, const std::size_t output_index, const crypto::public_key &pub, crypto::public_key &derived_pub) = 0; virtual bool secret_key_to_public_key(const crypto::secret_key &sec, crypto::public_key &pub) = 0; virtual bool generate_key_image(const crypto::public_key &pub, const crypto::secret_key &sec, crypto::key_image &image) = 0; + virtual bool derive_view_tag(const crypto::key_derivation &derivation, const std::size_t output_index, crypto::view_tag &view_tag) = 0; // alternative prototypes available in libringct rct::key scalarmultKey(const rct::key &P, const rct::key &a) diff --git a/src/device/device_default.hpp b/src/device/device_default.hpp index 7d3543652..58149cdbf 100644 --- a/src/device/device_default.hpp +++ b/src/device/device_default.hpp @@ -101,7 +101,7 @@ namespace hw { bool derive_public_key(const crypto::key_derivation &derivation, const std::size_t output_index, const crypto::public_key &pub, crypto::public_key &derived_pub) override; bool secret_key_to_public_key(const crypto::secret_key &sec, crypto::public_key &pub) override; bool generate_key_image(const crypto::public_key &pub, const crypto::secret_key &sec, crypto::key_image &image) override; - bool derive_view_tag(const crypto::key_derivation &derivation, const std::size_t output_index, crypto::view_tag &view_tag); + bool derive_view_tag(const crypto::key_derivation &derivation, const std::size_t output_index, crypto::view_tag &view_tag) override; /* ======================================================================= */ diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index aa73e998c..22a1fd986 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -43,6 +43,10 @@ namespace hw { #ifdef WITH_DEVICE_LEDGER + namespace { + bool apdu_verbose =true; + } + #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "device.ledger" @@ -266,6 +270,7 @@ namespace hw { #define INS_DERIVE_PUBLIC_KEY 0x36 #define INS_DERIVE_SECRET_KEY 0x38 #define INS_GEN_KEY_IMAGE 0x3A + #define INS_DERIVE_VIEW_TAG 0x3B #define INS_SECRET_KEY_ADD 0x3C #define INS_SECRET_KEY_SUB 0x3E #define INS_GENERATE_KEYPAIR 0x40 @@ -1308,6 +1313,54 @@ namespace hw { return true; } + bool device_ledger::derive_view_tag(const crypto::key_derivation &derivation, const std::size_t output_index, crypto::view_tag &view_tag){ + #ifdef DEBUG_HWDEVICE + crypto::key_derivation derivation_x; + if ((this->mode == TRANSACTION_PARSE) && has_view_key) { + derivation_x = derivation; + } else { + derivation_x = hw::ledger::decrypt(derivation); + } + const std::size_t output_index_x = output_index; + crypto::view_tag view_tag_x; + log_hexbuffer("derive_view_tag: [[IN]] derivation ", derivation_x.data, 32); + log_message ("derive_view_tag: [[IN]] output_index", std::to_string(output_index_x)); + this->controle_device->derive_view_tag(derivation_x, output_index_x, view_tag_x); + log_hexbuffer("derive_view_tag: [[OUT]] view_tag ", &view_tag_x.data, 1); + #endif + + if ((this->mode == TRANSACTION_PARSE) && has_view_key) { + //If we are in TRANSACTION_PARSE, the given derivation has been retrieved uncrypted (wihtout the help + //of the device), so continue that way. + MDEBUG( "derive_view_tag : PARSE mode with known viewkey"); + crypto::derive_view_tag(derivation, output_index, view_tag); + } else { + AUTO_LOCK_CMD(); + int offset = set_command_header_noopt(INS_DERIVE_VIEW_TAG); + //derivation + this->send_secret((unsigned char*)derivation.data, offset); + //index + this->buffer_send[offset+0] = output_index>>24; + this->buffer_send[offset+1] = output_index>>16; + this->buffer_send[offset+2] = output_index>>8; + this->buffer_send[offset+3] = output_index>>0; + offset += 4; + + this->buffer_send[4] = offset-5; + this->length_send = offset; + this->exchange(); + + //view tag + memmove(&view_tag.data, &this->buffer_recv[0], 1); + } + + #ifdef DEBUG_HWDEVICE + hw::ledger::check1("derive_view_tag", "view_tag", &view_tag_x.data, &view_tag.data); + #endif + + return true; + } + /* ======================================================================= */ /* TRANSACTION */ /* ======================================================================= */ @@ -1548,7 +1601,6 @@ namespace hw { const size_t output_index_x = output_index; const bool need_additional_txkeys_x = need_additional_txkeys; const bool use_view_tags_x = use_view_tags; - const crypto::view_tag view_tag_x = view_tag; std::vector<crypto::secret_key> additional_tx_keys_x; for (const auto &k: additional_tx_keys) { @@ -1558,6 +1610,7 @@ namespace hw { 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; + crypto::view_tag view_tag_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); @@ -1575,11 +1628,15 @@ namespace hw { if(need_additional_txkeys_x) { log_hexbuffer("generate_output_ephemeral_keys: [[IN]] additional_tx_keys[oi]", additional_tx_keys_x[output_index].data, 32); } + log_message ("generate_output_ephemeral_keys: [[IN]] use_view_tags", std::to_string(use_view_tags_x)); 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, use_view_tags_x, view_tag_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); } + if(use_view_tags_x) { + log_hexbuffer("generate_output_ephemeral_keys: [[OUT]] view_tag", &view_tag_x.data, 1); + } 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 @@ -1633,6 +1690,9 @@ namespace hw { memset(&this->buffer_send[offset], 0, 32); offset += 32; } + //use_view_tags + this->buffer_send[offset] = use_view_tags; + offset++; this->buffer_send[4] = offset-5; this->length_send = offset; @@ -1663,6 +1723,14 @@ namespace hw { recv_len -= 32; } + if (use_view_tags) + { + ASSERT_X(recv_len>=1, "Not enough data from device"); + memmove(&view_tag.data, &this->buffer_recv[offset], 1); + offset++; + recv_len -= 1; + } + // 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, need_additional_txkeys, output_index, @@ -1675,6 +1743,9 @@ namespace hw { 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); + if (use_view_tags) { + hw::ledger::check1("generate_output_ephemeral_keys", "view_tag", &view_tag_x.data, &view_tag.data); + } #endif return true; @@ -1860,7 +1931,7 @@ namespace hw { // ====== Aout, Bout, AKout, C, v, k ====== kv_offset = data_offset; - if (type==rct::RCTTypeBulletproof2 || type==rct::RCTTypeCLSAG) { + if (type==rct::RCTTypeBulletproof2 || type==rct::RCTTypeCLSAG || type==rct::RCTTypeBulletproofPlus) { C_offset = kv_offset+ (8)*outputs_size; } else { C_offset = kv_offset+ (32+32)*outputs_size; @@ -1877,7 +1948,7 @@ namespace hw { offset = set_command_header(INS_VALIDATE, 0x02, i+1); //options this->buffer_send[offset] = (i==outputs_size-1)? 0x00:0x80 ; - this->buffer_send[offset] |= (type==rct::RCTTypeBulletproof2 || type==rct::RCTTypeCLSAG)?0x02:0x00; + this->buffer_send[offset] |= (type==rct::RCTTypeBulletproof2 || type==rct::RCTTypeCLSAG || type==rct::RCTTypeBulletproofPlus)?0x02:0x00; offset += 1; //is_subaddress this->buffer_send[offset] = outKeys.is_subaddress; @@ -1898,7 +1969,7 @@ namespace hw { memmove(this->buffer_send+offset, data+C_offset,32); offset += 32; C_offset += 32; - if (type==rct::RCTTypeBulletproof2 || type==rct::RCTTypeCLSAG) { + if (type==rct::RCTTypeBulletproof2 || type==rct::RCTTypeCLSAG || type==rct::RCTTypeBulletproofPlus) { //k memset(this->buffer_send+offset, 0, 32); offset += 32; diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp index 074bfaa8d..5a1e7d75c 100644 --- a/src/device/device_ledger.hpp +++ b/src/device/device_ledger.hpp @@ -44,7 +44,7 @@ namespace hw { /* Minimal supported version */ #define MINIMAL_APP_VERSION_MAJOR 1 - #define MINIMAL_APP_VERSION_MINOR 6 + #define MINIMAL_APP_VERSION_MINOR 8 #define MINIMAL_APP_VERSION_MICRO 0 #define VERSION(M,m,u) ((M)<<16|(m)<<8|(u)) @@ -87,10 +87,6 @@ namespace hw { #define SW_PROTOCOL_NOT_SUPPORTED 0x6e00 #define SW_UNKNOWN 0x6f00 - namespace { - bool apdu_verbose =true; - } - void set_apdu_verbose(bool verbose); class ABPkeys { @@ -249,6 +245,7 @@ namespace hw { bool derive_public_key(const crypto::key_derivation &derivation, const std::size_t output_index, const crypto::public_key &pub, crypto::public_key &derived_pub) override; bool secret_key_to_public_key(const crypto::secret_key &sec, crypto::public_key &pub) override; bool generate_key_image(const crypto::public_key &pub, const crypto::secret_key &sec, crypto::key_image &image) override; + bool derive_view_tag(const crypto::key_derivation &derivation, const size_t output_index, crypto::view_tag &view_tag) override; /* ======================================================================= */ /* TRANSACTION */ diff --git a/src/device/log.cpp b/src/device/log.cpp index 9b882b784..b159632d9 100644 --- a/src/device/log.cpp +++ b/src/device/log.cpp @@ -165,6 +165,10 @@ namespace hw { void check8(const std::string &msg, const std::string &info, const char *h, const char *d, bool crypted) { check(msg, info, h, d, 8, crypted); } + + void check1(const std::string &msg, const std::string &info, const char *h, const char *d, bool crypted) { + check(msg, info, h, d, 1, crypted); + } #endif } diff --git a/src/device/log.hpp b/src/device/log.hpp index 660adc63e..a0c89ec71 100644 --- a/src/device/log.hpp +++ b/src/device/log.hpp @@ -75,6 +75,7 @@ namespace hw { void check32(const std::string &msg, const std::string &info, const char *h, const char *d, bool crypted=false); void check8(const std::string &msg, const std::string &info, const char *h, const char *d, bool crypted=false); + void check1(const std::string &msg, const std::string &info, const char *h, const char *d, bool crypted=false); void set_check_verbose(bool verbose); #endif diff --git a/src/device_trezor/device_trezor.cpp b/src/device_trezor/device_trezor.cpp index 6f7ae9a6b..3f7b10be4 100644 --- a/src/device_trezor/device_trezor.cpp +++ b/src/device_trezor/device_trezor.cpp @@ -324,8 +324,8 @@ namespace trezor { std::vector<protocol::ki::MoneroTransferDetails> mtds; std::vector<protocol::ki::MoneroExportedKeyImage> kis; - protocol::ki::key_image_data(wallet, transfers, mtds, client_version() <= 1); - protocol::ki::generate_commitment(mtds, transfers, req, client_version() <= 1); + protocol::ki::key_image_data(wallet, transfers, mtds); + protocol::ki::generate_commitment(mtds, transfers, req); EVENT_PROGRESS(0.); this->set_msg_addr<messages::monero::MoneroKeyImageExportInitRequest>(req.get()); @@ -511,7 +511,7 @@ namespace trezor { tools::wallet2::signed_tx_set & signed_tx, hw::tx_aux_data & aux_data) { - CHECK_AND_ASSERT_THROW_MES(unsigned_tx.transfers.first == 0, "Unsuported non zero offset"); + CHECK_AND_ASSERT_THROW_MES(std::get<0>(unsigned_tx.transfers) == 0, "Unsuported non zero offset"); TREZOR_AUTO_LOCK_CMD(); require_connected(); @@ -522,7 +522,7 @@ namespace trezor { const size_t num_tx = unsigned_tx.txes.size(); m_num_transations_to_sign = num_tx; signed_tx.key_images.clear(); - signed_tx.key_images.resize(unsigned_tx.transfers.second.size()); + signed_tx.key_images.resize(std::get<2>(unsigned_tx.transfers).size()); for(size_t tx_idx = 0; tx_idx < num_tx; ++tx_idx) { std::shared_ptr<protocol::tx::Signer> signer; @@ -566,8 +566,8 @@ namespace trezor { cpend.key_images = key_images; // KI sync - for(size_t cidx=0, trans_max=unsigned_tx.transfers.second.size(); cidx < trans_max; ++cidx){ - signed_tx.key_images[cidx] = unsigned_tx.transfers.second[cidx].m_key_image; + for(size_t cidx=0, trans_max=std::get<2>(unsigned_tx.transfers).size(); cidx < trans_max; ++cidx){ + signed_tx.key_images[cidx] = std::get<2>(unsigned_tx.transfers)[cidx].m_key_image; } size_t num_sources = cdata.tx_data.sources.size(); @@ -579,9 +579,9 @@ namespace trezor { CHECK_AND_ASSERT_THROW_MES(src_idx < cdata.tx.vin.size(), "Invalid idx_mapped"); size_t idx_map_src = cdata.tx_data.selected_transfers[idx_mapped]; - CHECK_AND_ASSERT_THROW_MES(idx_map_src >= unsigned_tx.transfers.first, "Invalid offset"); + CHECK_AND_ASSERT_THROW_MES(idx_map_src >= std::get<0>(unsigned_tx.transfers), "Invalid offset"); - idx_map_src -= unsigned_tx.transfers.first; + idx_map_src -= std::get<0>(unsigned_tx.transfers); CHECK_AND_ASSERT_THROW_MES(idx_map_src < signed_tx.key_images.size(), "Invalid key image index"); const auto vini = boost::get<cryptonote::txin_to_key>(cdata.tx.vin[src_idx]); @@ -635,11 +635,7 @@ namespace trezor { } // Step: sort - auto perm_req = signer->step_permutation(); - if (perm_req){ - auto perm_ack = this->client_exchange<messages::monero::MoneroTransactionInputsPermutationAck>(perm_req); - signer->step_permutation_ack(perm_ack); - } + signer->sort_ki(); EVENT_PROGRESS(3, 1, 1); // Step: input_vini @@ -697,13 +693,13 @@ namespace trezor { unsigned device_trezor::client_version() { auto trezor_version = get_version(); - if (trezor_version <= pack_version(2, 0, 10)){ - throw exc::TrezorException("Trezor firmware 2.0.10 and lower are not supported. Please update."); + if (trezor_version < pack_version(2, 4, 3)){ + throw exc::TrezorException("Minimal Trezor firmware version is 2.4.3. Please update."); } - unsigned client_version = 1; - if (trezor_version >= pack_version(2, 3, 1)){ - client_version = 3; + unsigned client_version = 3; + if (trezor_version >= pack_version(2, 5, 2)){ + client_version = 4; } #ifdef WITH_TREZOR_DEBUGGING @@ -739,14 +735,6 @@ namespace trezor { CHECK_AND_ASSERT_THROW_MES(init_msg, "TransactionInitRequest is empty"); CHECK_AND_ASSERT_THROW_MES(init_msg->has_tsx_data(), "TransactionInitRequest has no transaction data"); CHECK_AND_ASSERT_THROW_MES(m_features, "Device state not initialized"); // make sure the caller did not reset features - const bool nonce_required = init_msg->tsx_data().has_payment_id() && init_msg->tsx_data().payment_id().size() > 0; - - if (nonce_required && init_msg->tsx_data().payment_id().size() == 8){ - // Versions 2.0.9 and lower do not support payment ID - if (get_version() <= pack_version(2, 0, 9)) { - throw exc::TrezorException("Trezor firmware 2.0.9 and lower does not support transactions with short payment IDs or integrated addresses. Please update."); - } - } } void device_trezor::transaction_check(const protocol::tx::TData & tdata, const hw::tx_aux_data & aux_data) diff --git a/src/device_trezor/trezor/protocol.cpp b/src/device_trezor/trezor/protocol.cpp index a400e82c7..0e59a16ba 100644 --- a/src/device_trezor/trezor/protocol.cpp +++ b/src/device_trezor/trezor/protocol.cpp @@ -38,6 +38,7 @@ #include <crypto/hmac-keccak.h> #include <ringct/rctSigs.h> #include <ringct/bulletproofs.h> +#include <ringct/bulletproofs_plus.h> #include "cryptonote_config.h" #include <sodium.h> #include <sodium/crypto_verify_32.h> @@ -145,8 +146,7 @@ namespace ki { bool key_image_data(wallet_shim * wallet, const std::vector<tools::wallet2::transfer_details> & transfers, - std::vector<MoneroTransferDetails> & res, - bool need_all_additionals) + std::vector<MoneroTransferDetails> & res) { for(auto & td : transfers){ ::crypto::public_key tx_pub_key = wallet->get_tx_pub_key_from_received_outs(td); @@ -159,11 +159,7 @@ namespace ki { cres.set_internal_output_index(td.m_internal_output_index); cres.set_sub_addr_major(td.m_subaddr_index.major); cres.set_sub_addr_minor(td.m_subaddr_index.minor); - if (need_all_additionals) { - for (auto &aux : additional_tx_pub_keys) { - cres.add_additional_tx_pub_keys(key_to_string(aux)); - } - } else if (!additional_tx_pub_keys.empty() && additional_tx_pub_keys.size() > td.m_internal_output_index) { + if (!additional_tx_pub_keys.empty() && additional_tx_pub_keys.size() > td.m_internal_output_index) { cres.add_additional_tx_pub_keys(key_to_string(additional_tx_pub_keys[td.m_internal_output_index])); } } @@ -194,8 +190,7 @@ namespace ki { void generate_commitment(std::vector<MoneroTransferDetails> & mtds, const std::vector<tools::wallet2::transfer_details> & transfers, - std::shared_ptr<messages::monero::MoneroKeyImageExportInitRequest> & req, - bool need_subaddr_indices) + std::shared_ptr<messages::monero::MoneroKeyImageExportInitRequest> & req) { req = std::make_shared<messages::monero::MoneroKeyImageExportInitRequest>(); @@ -219,16 +214,6 @@ namespace ki { auto & st = search.first->second; st.insert(cur.m_subaddr_index.minor); } - - if (need_subaddr_indices) { - for (auto &x: sub_indices) { - auto subs = req->add_subs(); - subs->set_account(x.first); - for (auto minor : x.second) { - subs->add_minor_indices(minor); - } - } - } } void live_refresh_ack(const ::crypto::secret_key & view_key_priv, @@ -399,7 +384,7 @@ namespace tx { m_tx_idx = tx_idx; m_ct.tx_data = cur_src_tx(); m_multisig = false; - m_client_version = 1; + m_client_version = 3; } void Signer::extract_payment_id(){ @@ -474,25 +459,19 @@ namespace tx { auto & cur = src.outputs[i]; auto out = dst->add_outputs(); - if (i == src.real_output || need_ring_indices || client_version() <= 1) { + if (i == src.real_output || need_ring_indices) { out->set_idx(cur.first); } - if (i == src.real_output || need_ring_keys || client_version() <= 1) { + if (i == src.real_output || need_ring_keys) { translate_rct_key(out->mutable_key(), &(cur.second)); } } dst->set_real_out_tx_key(key_to_string(src.real_out_tx_key)); dst->set_real_output_in_tx_index(src.real_output_in_tx_index); - - if (client_version() <= 1) { - for (auto &cur : src.real_out_additional_tx_keys) { - dst->add_real_out_additional_tx_keys(key_to_string(cur)); - } - } else if (!src.real_out_additional_tx_keys.empty()) { + if (!src.real_out_additional_tx_keys.empty()) { dst->add_real_out_additional_tx_keys(key_to_string(src.real_out_additional_tx_keys.at(src.real_output_in_tx_index))); } - dst->set_amount(src.amount); dst->set_rct(src.rct); dst->set_mask(key_to_string(src.mask)); @@ -532,7 +511,7 @@ namespace tx { m_ct.tx.version = 2; m_ct.tx.unlock_time = tx.unlock_time; - m_client_version = (m_aux_data->client_version ? m_aux_data->client_version.get() : 1); + m_client_version = (m_aux_data->client_version ? m_aux_data->client_version.get() : 3); tsx_data.set_version(1); tsx_data.set_client_version(client_version()); @@ -543,18 +522,13 @@ namespace tx { tsx_data.set_monero_version(std::string(MONERO_VERSION) + "|" + MONERO_VERSION_TAG); tsx_data.set_hard_fork(m_aux_data->hard_fork ? m_aux_data->hard_fork.get() : 0); - if (client_version() <= 1){ - assign_to_repeatable(tsx_data.mutable_minor_indices(), tx.subaddr_indices.begin(), tx.subaddr_indices.end()); - } - // Rsig decision auto rsig_data = tsx_data.mutable_rsig_data(); 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.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); - } + CHECK_AND_ASSERT_THROW_MES(tx.rct_config.range_proof_type != rct::RangeProofBorromean, "Borromean rsig not supported"); + 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); generate_rsig_batch_sizes(m_ct.grouping_vct, m_ct.rsig_type, tx.splitted_dsts.size()); assign_to_repeatable(rsig_data->mutable_grouping(), m_ct.grouping_vct.begin(), m_ct.grouping_vct.end()); @@ -652,22 +626,6 @@ namespace tx { }); } - std::shared_ptr<messages::monero::MoneroTransactionInputsPermutationRequest> Signer::step_permutation(){ - sort_ki(); - if (client_version() >= 2){ - return nullptr; - } - - auto res = std::make_shared<messages::monero::MoneroTransactionInputsPermutationRequest>(); - assign_to_repeatable(res->mutable_perm(), m_ct.source_permutation.begin(), m_ct.source_permutation.end()); - - return res; - } - - void Signer::step_permutation_ack(std::shared_ptr<const messages::monero::MoneroTransactionInputsPermutationAck> ack){ - - } - std::shared_ptr<messages::monero::MoneroTransactionInputViniRequest> Signer::step_set_vini_input(size_t idx){ CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx_data.sources.size(), "Invalid transaction index"); CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx.vin.size(), "Invalid transaction index"); @@ -711,8 +669,10 @@ namespace tx { } void Signer::step_set_output_ack(std::shared_ptr<const messages::monero::MoneroTransactionSetOutputAck> ack){ + CHECK_AND_ASSERT_THROW_MES(is_req_bulletproof(), "Borromean rsig not supported"); cryptonote::tx_out tx_out; rct::Bulletproof bproof{}; + rct::BulletproofPlus bproof_plus{}; rct::ctkey out_pk{}; rct::ecdhTuple ecdh{}; @@ -727,7 +687,7 @@ namespace tx { rsig_buff = rsig_data.rsig(); } - if (client_version() >= 1 && rsig_data.has_mask()){ + if (rsig_data.has_mask()){ rct::key cmask{}; string_to_key(cmask, rsig_data.mask()); m_ct.rsig_gamma.emplace_back(cmask); @@ -751,22 +711,32 @@ namespace tx { memcpy(ecdh.amount.bytes, ack->ecdh_info().data(), 8); } - if (has_rsig && is_req_bulletproof() && !cn_deserialize(rsig_buff, bproof)){ - throw exc::ProtocolException("Cannot deserialize bulletproof rangesig"); - } - m_ct.tx.vout.emplace_back(tx_out); m_ct.tx_out_hmacs.push_back(ack->vouti_hmac()); m_ct.tx_out_pk.emplace_back(out_pk); m_ct.tx_out_ecdh.emplace_back(ecdh); - // ClientV0, if no rsig was generated on Trezor, do not continue. - // ClientV1+ generates BP after all masks in the current batch are generated - if (!has_rsig || (client_version() >= 1 && is_offloading())){ + rsig_v bp_obj{}; + if (has_rsig) { + bool deserialize_success; + if (is_req_bulletproof_plus()) { + deserialize_success = cn_deserialize(rsig_buff, bproof_plus); + bp_obj = bproof_plus; + } else { + deserialize_success = cn_deserialize(rsig_buff, bproof); + bp_obj = bproof; + } + if (!deserialize_success) { + throw exc::ProtocolException("Cannot deserialize bulletproof rangesig"); + } + } + + // Generates BP after all masks in the current batch are generated + if (!has_rsig || is_offloading()){ return; } - process_bproof(bproof); + process_bproof(bp_obj); m_ct.cur_batch_idx += 1; m_ct.cur_output_in_batch_idx = 0; } @@ -791,13 +761,21 @@ namespace tx { masks.push_back(m_ct.rsig_gamma[bidx]); } - auto bp = bulletproof_PROVE(amounts, masks); - auto serRsig = cn_serialize(bp); - m_ct.tx_out_rsigs.emplace_back(bp); + std::string serRsig; + if (is_req_bulletproof_plus()) { + auto bp = bulletproof_plus_PROVE(amounts, masks); + serRsig = cn_serialize(bp); + m_ct.tx_out_rsigs.emplace_back(bp); + } else { + auto bp = bulletproof_PROVE(amounts, masks); + serRsig = cn_serialize(bp); + m_ct.tx_out_rsigs.emplace_back(bp); + } + rsig_data.set_rsig(serRsig); } - void Signer::process_bproof(rct::Bulletproof & bproof){ + void Signer::process_bproof(rsig_v & bproof){ CHECK_AND_ASSERT_THROW_MES(m_ct.cur_batch_idx < m_ct.grouping_vct.size(), "Invalid batch index"); auto batch_size = m_ct.grouping_vct[m_ct.cur_batch_idx]; for (size_t i = 0; i < batch_size; ++i){ @@ -806,12 +784,22 @@ namespace tx { rct::key commitment = m_ct.tx_out_pk[bidx].mask; commitment = rct::scalarmultKey(commitment, rct::INV_EIGHT); - bproof.V.push_back(commitment); + if (is_req_bulletproof_plus()) { + boost::get<rct::BulletproofPlus>(bproof).V.push_back(commitment); + } else { + boost::get<rct::Bulletproof>(bproof).V.push_back(commitment); + } } m_ct.tx_out_rsigs.emplace_back(bproof); - if (!rct::bulletproof_VERIFY(boost::get<rct::Bulletproof>(m_ct.tx_out_rsigs.back()))) { - throw exc::ProtocolException("Returned range signature is invalid"); + if (is_req_bulletproof_plus()) { + if (!rct::bulletproof_plus_VERIFY(boost::get<rct::BulletproofPlus>(m_ct.tx_out_rsigs.back()))) { + throw exc::ProtocolException("Returned range signature is invalid"); + } + } else { + if (!rct::bulletproof_VERIFY(boost::get<rct::Bulletproof>(m_ct.tx_out_rsigs.back()))) { + throw exc::ProtocolException("Returned range signature is invalid"); + } } } @@ -840,6 +828,7 @@ namespace tx { } void Signer::step_all_outs_set_ack(std::shared_ptr<const messages::monero::MoneroTransactionAllOutSetAck> ack, hw::device &hwdev){ + CHECK_AND_ASSERT_THROW_MES(is_req_bulletproof(), "Borromean rsig not supported"); m_ct.rv = std::make_shared<rct::rctSig>(); m_ct.rv->txnFee = ack->rv().txn_fee(); m_ct.rv->type = static_cast<uint8_t>(ack->rv().rv_type()); @@ -864,24 +853,15 @@ namespace tx { // RctSig auto num_sources = m_ct.tx_data.sources.size(); - if (is_simple() || is_req_bulletproof()){ - auto dst = &m_ct.rv->pseudoOuts; - if (is_bulletproof()){ - dst = &m_ct.rv->p.pseudoOuts; - } - - dst->clear(); - for (const auto &pseudo_out : m_ct.pseudo_outs) { - dst->emplace_back(); - string_to_key(dst->back(), pseudo_out); - } - - m_ct.rv->mixRing.resize(num_sources); - } else { - m_ct.rv->mixRing.resize(m_ct.tsx_data.mixin()); - m_ct.rv->mixRing[0].resize(num_sources); + auto dst = &m_ct.rv->p.pseudoOuts; + dst->clear(); + for (const auto &pseudo_out : m_ct.pseudo_outs) { + dst->emplace_back(); + string_to_key(dst->back(), pseudo_out); } + m_ct.rv->mixRing.resize(num_sources); + CHECK_AND_ASSERT_THROW_MES(m_ct.tx_out_pk.size() == m_ct.tx_out_ecdh.size(), "Invalid vector sizes"); for(size_t i = 0; i < m_ct.tx_out_ecdh.size(); ++i){ m_ct.rv->outPk.push_back(m_ct.tx_out_pk[i]); @@ -889,10 +869,10 @@ namespace tx { } for(size_t i = 0; i < m_ct.tx_out_rsigs.size(); ++i){ - if (is_bulletproof()){ - m_ct.rv->p.bulletproofs.push_back(boost::get<rct::Bulletproof>(m_ct.tx_out_rsigs[i])); + if (is_req_bulletproof_plus()) { + m_ct.rv->p.bulletproofs_plus.push_back(boost::get<rct::BulletproofPlus>(m_ct.tx_out_rsigs[i])); } else { - m_ct.rv->p.rangeSigs.push_back(boost::get<rct::rangeSig>(m_ct.tx_out_rsigs[i])); + m_ct.rv->p.bulletproofs.push_back(boost::get<rct::Bulletproof>(m_ct.tx_out_rsigs[i])); } } @@ -936,8 +916,8 @@ namespace tx { void Signer::step_sign_input_ack(std::shared_ptr<const messages::monero::MoneroTransactionSignInputAck> ack){ m_ct.signatures.push_back(ack->signature()); - // Sync updated pseudo_outputs, client_version>=1, HF10+ - if (client_version() >= 1 && ack->has_pseudo_out()){ + // Sync updated pseudo_outputs + if (ack->has_pseudo_out()){ CHECK_AND_ASSERT_THROW_MES(m_ct.cur_input_idx < m_ct.pseudo_outs.size(), "Invalid pseudo-out index"); m_ct.pseudo_outs[m_ct.cur_input_idx] = ack->pseudo_out(); if (is_bulletproof()){ @@ -955,6 +935,8 @@ namespace tx { } void Signer::step_final_ack(std::shared_ptr<const messages::monero::MoneroTransactionFinalAck> ack){ + CHECK_AND_ASSERT_THROW_MES(is_clsag(), "Only CLSAGs signatures are supported"); + if (m_multisig){ auto & cout_key = ack->cout_key(); for(auto & cur : m_ct.couts){ @@ -975,47 +957,34 @@ namespace tx { m_ct.enc_keys = ack->tx_enc_keys(); // Opening the sealed signatures - if (client_version() >= 3){ - if(!ack->has_opening_key()){ - throw exc::ProtocolException("Client version 3+ requires sealed signatures"); - } + if(!ack->has_opening_key()){ + throw exc::ProtocolException("Client version 3+ requires sealed signatures"); + } - for(size_t i = 0; i < m_ct.signatures.size(); ++i){ - CHECK_AND_ASSERT_THROW_MES(m_ct.signatures[i].size() > crypto::chacha::TAG_SIZE, "Invalid signature size"); - std::string nonce = compute_sealing_key(ack->opening_key(), i, true); - std::string key = compute_sealing_key(ack->opening_key(), i, false); - size_t plen = m_ct.signatures[i].size() - crypto::chacha::TAG_SIZE; - std::unique_ptr<uint8_t[]> plaintext(new uint8_t[plen]); - uint8_t * buff = plaintext.get(); - - protocol::crypto::chacha::decrypt( - m_ct.signatures[i].data(), - m_ct.signatures[i].size(), - reinterpret_cast<const uint8_t *>(key.data()), - reinterpret_cast<const uint8_t *>(nonce.data()), - reinterpret_cast<char *>(buff), &plen); - m_ct.signatures[i].assign(reinterpret_cast<const char *>(buff), plen); - } + for(size_t i = 0; i < m_ct.signatures.size(); ++i){ + CHECK_AND_ASSERT_THROW_MES(m_ct.signatures[i].size() > crypto::chacha::TAG_SIZE, "Invalid signature size"); + std::string nonce = compute_sealing_key(ack->opening_key(), i, true); + std::string key = compute_sealing_key(ack->opening_key(), i, false); + size_t plen = m_ct.signatures[i].size() - crypto::chacha::TAG_SIZE; + std::unique_ptr<uint8_t[]> plaintext(new uint8_t[plen]); + uint8_t * buff = plaintext.get(); + + protocol::crypto::chacha::decrypt( + m_ct.signatures[i].data(), + m_ct.signatures[i].size(), + reinterpret_cast<const uint8_t *>(key.data()), + reinterpret_cast<const uint8_t *>(nonce.data()), + reinterpret_cast<char *>(buff), &plen); + m_ct.signatures[i].assign(reinterpret_cast<const char *>(buff), plen); } - if (m_ct.rv->type == rct::RCTTypeCLSAG){ - m_ct.rv->p.CLSAGs.reserve(m_ct.signatures.size()); - for (size_t i = 0; i < m_ct.signatures.size(); ++i) { - rct::clsag clsag; - if (!cn_deserialize(m_ct.signatures[i], clsag)) { - throw exc::ProtocolException("Cannot deserialize clsag[i]"); - } - m_ct.rv->p.CLSAGs.push_back(clsag); - } - } else { - m_ct.rv->p.MGs.reserve(m_ct.signatures.size()); - for (size_t i = 0; i < m_ct.signatures.size(); ++i) { - rct::mgSig mg; - if (!cn_deserialize(m_ct.signatures[i], mg)) { - throw exc::ProtocolException("Cannot deserialize mg[i]"); - } - m_ct.rv->p.MGs.push_back(mg); + m_ct.rv->p.CLSAGs.reserve(m_ct.signatures.size()); + for (size_t i = 0; i < m_ct.signatures.size(); ++i) { + rct::clsag clsag; + if (!cn_deserialize(m_ct.signatures[i], clsag)) { + throw exc::ProtocolException("Cannot deserialize clsag[i]"); } + m_ct.rv->p.CLSAGs.push_back(clsag); } m_ct.tx.rct_signatures = *(m_ct.rv); diff --git a/src/device_trezor/trezor/protocol.hpp b/src/device_trezor/trezor/protocol.hpp index 858db1520..7ffadd9aa 100644 --- a/src/device_trezor/trezor/protocol.hpp +++ b/src/device_trezor/trezor/protocol.hpp @@ -116,8 +116,7 @@ namespace ki { */ bool key_image_data(wallet_shim * wallet, const std::vector<tools::wallet2::transfer_details> & transfers, - std::vector<MoneroTransferDetails> & res, - bool need_all_additionals=false); + std::vector<MoneroTransferDetails> & res); /** * Computes a hash over MoneroTransferDetails. Commitment used in the KI sync. @@ -129,8 +128,7 @@ namespace ki { */ void generate_commitment(std::vector<MoneroTransferDetails> & mtds, const std::vector<tools::wallet2::transfer_details> & transfers, - std::shared_ptr<messages::monero::MoneroKeyImageExportInitRequest> & req, - bool need_subaddr_indices=false); + std::shared_ptr<messages::monero::MoneroKeyImageExportInitRequest> & req); /** * Processes Live refresh step response, parses KI, checks the signature @@ -166,7 +164,7 @@ namespace tx { ::crypto::secret_key compute_enc_key(const ::crypto::secret_key & private_view_key, const std::string & aux, const std::string & salt); std::string compute_sealing_key(const std::string & master_key, size_t idx, bool is_iv=false); - typedef boost::variant<rct::rangeSig, rct::Bulletproof> rsig_v; + typedef boost::variant<rct::Bulletproof, rct::BulletproofPlus> rsig_v; /** * Transaction signer state holder. @@ -232,8 +230,8 @@ namespace tx { } const tools::wallet2::transfer_details & get_transfer(size_t idx) const { - CHECK_AND_ASSERT_THROW_MES(idx < m_unsigned_tx->transfers.second.size() + m_unsigned_tx->transfers.first && idx >= m_unsigned_tx->transfers.first, "Invalid transfer index"); - return m_unsigned_tx->transfers.second[idx - m_unsigned_tx->transfers.first]; + CHECK_AND_ASSERT_THROW_MES(idx < std::get<2>(m_unsigned_tx->transfers).size() + std::get<0>(m_unsigned_tx->transfers) && idx >= std::get<0>(m_unsigned_tx->transfers), "Invalid transfer index"); + return std::get<2>(m_unsigned_tx->transfers)[idx - std::get<0>(m_unsigned_tx->transfers)]; } const tools::wallet2::transfer_details & get_source_transfer(size_t idx) const { @@ -247,7 +245,7 @@ namespace tx { void compute_integrated_indices(TsxData * tsx_data); bool should_compute_bp_now() const; void compute_bproof(messages::monero::MoneroTransactionRsigData & rsig_data); - void process_bproof(rct::Bulletproof & bproof); + void process_bproof(rsig_v & bproof); void set_tx_input(MoneroTransactionSourceEntry * dst, size_t idx, bool need_ring_keys=false, bool need_ring_indices=false); public: @@ -260,8 +258,6 @@ namespace tx { void step_set_input_ack(std::shared_ptr<const messages::monero::MoneroTransactionSetInputAck> ack); void sort_ki(); - std::shared_ptr<messages::monero::MoneroTransactionInputsPermutationRequest> step_permutation(); - void step_permutation_ack(std::shared_ptr<const messages::monero::MoneroTransactionInputsPermutationAck> ack); std::shared_ptr<messages::monero::MoneroTransactionInputViniRequest> step_set_vini_input(size_t idx); void step_set_vini_input_ack(std::shared_ptr<const messages::monero::MoneroTransactionInputViniAck> ack); @@ -290,11 +286,15 @@ namespace tx { return m_client_version; } - bool is_simple() const { + uint8_t get_rv_type() const { if (!m_ct.rv){ throw std::invalid_argument("RV not initialized"); } - auto tp = m_ct.rv->type; + return m_ct.rv->type; + } + + bool is_simple() const { + auto tp = get_rv_type(); return tp == rct::RCTTypeSimple; } @@ -302,12 +302,27 @@ namespace tx { return m_ct.tx_data.rct_config.range_proof_type != rct::RangeProofBorromean; } + bool is_req_clsag() const { + return is_req_bulletproof() && m_ct.tx_data.rct_config.bp_version >= 3; + } + + bool is_req_bulletproof_plus() const { + return is_req_bulletproof() && m_ct.tx_data.rct_config.bp_version == 4; // rct::genRctSimple + } + bool is_bulletproof() const { - if (!m_ct.rv){ - throw std::invalid_argument("RV not initialized"); - } - auto tp = m_ct.rv->type; - return tp == rct::RCTTypeBulletproof || tp == rct::RCTTypeBulletproof2 || tp == rct::RCTTypeCLSAG; + auto tp = get_rv_type(); + return rct::is_rct_bulletproof(tp) || rct::is_rct_bulletproof_plus(tp); + } + + bool is_bulletproof_plus() const { + auto tp = get_rv_type(); + return rct::is_rct_bulletproof_plus(tp); + } + + bool is_clsag() const { + auto tp = get_rv_type(); + return rct::is_rct_clsag(tp); } bool is_offloading() const { diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 5304333ff..8d13b7634 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -2290,6 +2290,12 @@ namespace cryptonote return m_bootstrap_daemon->handle_result(false, {}); } + if (bootstrap_daemon_height < m_core.get_checkpoints().get_max_height()) + { + MINFO("Bootstrap daemon height is lower than the latest checkpoint"); + return m_bootstrap_daemon->handle_result(false, {}); + } + if (!m_p2p.get_payload_object().no_sync()) { uint64_t top_height = m_core.get_current_blockchain_height(); diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 0274f4db8..b87412ca6 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -47,10 +47,6 @@ #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "daemon.rpc" -// yes, epee doesn't properly use its full namespace when calling its -// functions from macros. *sigh* -using namespace epee; - namespace cryptonote { /************************************************************************/ diff --git a/src/serialization/tuple.h b/src/serialization/tuple.h new file mode 100644 index 000000000..6d98e05b0 --- /dev/null +++ b/src/serialization/tuple.h @@ -0,0 +1,169 @@ +// Copyright (c) 2014-2020, 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 <memory> +#include "serialization.h" + +namespace serialization +{ + namespace detail + { + template <typename Archive, class T> + bool serialize_tuple_element(Archive& ar, T& e) + { + return ::do_serialize(ar, e); + } + + template <typename Archive> + bool serialize_tuple_element(Archive& ar, uint64_t& e) + { + ar.serialize_varint(e); + return true; + } + } +} + +template <template <bool> class Archive, class E0, class E1, class E2> +inline bool do_serialize(Archive<false>& ar, std::tuple<E0,E1,E2>& p) +{ + size_t cnt; + ar.begin_array(cnt); + if (!ar.good()) + return false; + if (cnt != 3) + return false; + + if (!::serialization::detail::serialize_tuple_element(ar, std::get<0>(p))) + return false; + if (!ar.good()) + return false; + ar.delimit_array(); + if (!::serialization::detail::serialize_tuple_element(ar, std::get<1>(p))) + return false; + if (!ar.good()) + return false; + ar.delimit_array(); + if (!::serialization::detail::serialize_tuple_element(ar, std::get<2>(p))) + return false; + if (!ar.good()) + return false; + + ar.end_array(); + return true; +} + +template <template <bool> class Archive, class E0, class E1, class E2> +inline bool do_serialize(Archive<true>& ar, std::tuple<E0,E1,E2>& p) +{ + ar.begin_array(3); + if (!ar.good()) + return false; + if(!::serialization::detail::serialize_tuple_element(ar, std::get<0>(p))) + return false; + if (!ar.good()) + return false; + ar.delimit_array(); + if(!::serialization::detail::serialize_tuple_element(ar, std::get<1>(p))) + return false; + if (!ar.good()) + return false; + ar.delimit_array(); + if(!::serialization::detail::serialize_tuple_element(ar, std::get<2>(p))) + return false; + if (!ar.good()) + return false; + ar.end_array(); + return true; +} + +template <template <bool> class Archive, class E0, class E1, class E2, class E3> +inline bool do_serialize(Archive<false>& ar, std::tuple<E0,E1,E2,E3>& p) +{ + size_t cnt; + ar.begin_array(cnt); + if (!ar.good()) + return false; + if (cnt != 4) + return false; + + if (!::serialization::detail::serialize_tuple_element(ar, std::get<0>(p))) + return false; + if (!ar.good()) + return false; + ar.delimit_array(); + if (!::serialization::detail::serialize_tuple_element(ar, std::get<1>(p))) + return false; + if (!ar.good()) + return false; + ar.delimit_array(); + if (!::serialization::detail::serialize_tuple_element(ar, std::get<2>(p))) + return false; + if (!ar.good()) + return false; + ar.delimit_array(); + if (!::serialization::detail::serialize_tuple_element(ar, std::get<3>(p))) + return false; + if (!ar.good()) + return false; + + ar.end_array(); + return true; +} + +template <template <bool> class Archive, class E0, class E1, class E2, class E3> +inline bool do_serialize(Archive<true>& ar, std::tuple<E0,E1,E2,E3>& p) +{ + ar.begin_array(4); + if (!ar.good()) + return false; + if(!::serialization::detail::serialize_tuple_element(ar, std::get<0>(p))) + return false; + if (!ar.good()) + return false; + ar.delimit_array(); + if(!::serialization::detail::serialize_tuple_element(ar, std::get<1>(p))) + return false; + if (!ar.good()) + return false; + ar.delimit_array(); + if(!::serialization::detail::serialize_tuple_element(ar, std::get<2>(p))) + return false; + if (!ar.good()) + return false; + ar.delimit_array(); + if(!::serialization::detail::serialize_tuple_element(ar, std::get<3>(p))) + return false; + if (!ar.good()) + return false; + ar.end_array(); + return true; +} + diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index a8f4e5a07..9b63ceca6 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -7912,8 +7912,10 @@ bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes, bool simple_wallet::accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs) { std::string extra_message; - if (!txs.transfers.second.empty()) - extra_message = (boost::format("%u outputs to import. ") % (unsigned)txs.transfers.second.size()).str(); + if (!std::get<2>(txs.new_transfers).empty()) + extra_message = (boost::format("%u outputs to import. ") % (unsigned)std::get<2>(txs.new_transfers).size()).str(); + else if (!std::get<2>(txs.transfers).empty()) + extra_message = (boost::format("%u outputs to import. ") % (unsigned)std::get<2>(txs.transfers).size()).str(); return accept_loaded_tx([&txs](){return txs.txes.size();}, [&txs](size_t n)->const tools::wallet2::tx_construction_data&{return txs.txes[n];}, extra_message); } //---------------------------------------------------------------------------------------------------- diff --git a/src/version.cpp.in b/src/version.cpp.in index c6d473bf9..91fdc9902 100644 --- a/src/version.cpp.in +++ b/src/version.cpp.in @@ -1,5 +1,5 @@ #define DEF_MONERO_VERSION_TAG "@VERSIONTAG@" -#define DEF_MONERO_VERSION "0.18.0.0" +#define DEF_MONERO_VERSION "0.18.1.0" #define DEF_MONERO_RELEASE_NAME "Fluorine Fermi" #define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG #define DEF_MONERO_VERSION_IS_RELEASE @VERSION_IS_RELEASE@ diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 1ee2e20b6..807dba33b 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1146,8 +1146,8 @@ UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_file // Check tx data and construct confirmation message std::string extra_message; - if (!transaction->m_unsigned_tx_set.transfers.second.empty()) - extra_message = (boost::format("%u outputs to import. ") % (unsigned)transaction->m_unsigned_tx_set.transfers.second.size()).str(); + if (!std::get<2>(transaction->m_unsigned_tx_set.transfers).empty()) + extra_message = (boost::format("%u outputs to import. ") % (unsigned)std::get<2>(transaction->m_unsigned_tx_set.transfers).size()).str(); transaction->checkLoadedTx([&transaction](){return transaction->m_unsigned_tx_set.txes.size();}, [&transaction](size_t n)->const tools::wallet2::tx_construction_data&{return transaction->m_unsigned_tx_set.txes[n];}, extra_message); setStatus(transaction->status(), transaction->errorString()); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 91522e46c..b6d95b6b7 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1218,7 +1218,8 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std m_export_format(ExportFormat::Binary), m_load_deprecated_formats(false), m_credits_target(0), - m_enable_multisig(false) + m_enable_multisig(false), + m_has_ever_refreshed_from_node(false) { set_rpc_client_secret_key(rct::rct2sk(rct::skGen())); } @@ -3424,6 +3425,10 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo uint64_t blocks_start_height; std::vector<cryptonote::block_complete_entry> blocks; std::vector<parsed_block> parsed_blocks; + // TODO moneromooo-monero says this about the "refreshed" variable: + // "I had to reorder some code to fix... a timing info leak IIRC. In turn, this undid something I had fixed before, ... a subtle race condition with the txpool. + // It was pretty subtle IIRC, and so I needed time to think about how to refix it after the move, and I never got to it." + // https://github.com/monero-project/monero/pull/6097 bool refreshed = false; std::shared_ptr<std::map<std::pair<uint64_t, uint64_t>, size_t>> output_tracker_cache; hw::device &hwdev = m_account.get_device(); @@ -3535,6 +3540,8 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo throw std::runtime_error("proxy exception in refresh thread"); } + m_has_ever_refreshed_from_node = true; + if(!first && blocks_start_height == next_blocks_start_height) { m_node_rpc_proxy.set_height(m_blockchain.size()); @@ -6604,9 +6611,9 @@ bool wallet2::sign_tx(const std::string &unsigned_filename, const std::string &s //---------------------------------------------------------------------------------------------------- bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pending_tx> &txs, signed_tx_set &signed_txes) { - if (!exported_txs.new_transfers.second.empty()) + if (!std::get<2>(exported_txs.new_transfers).empty()) import_outputs(exported_txs.new_transfers); - else + else if (!std::get<2>(exported_txs.transfers).empty()) import_outputs(exported_txs.transfers); // sign the transactions @@ -10800,7 +10807,7 @@ void wallet2::cold_sign_tx(const std::vector<pending_tx>& ptx_vector, signed_tx_ { txs.txes.push_back(get_construction_data_with_decrypted_short_payment_id(tx, m_account.get_device())); } - txs.transfers = std::make_pair(0, m_transfers); + txs.transfers = std::make_tuple(0, m_transfers.size(), m_transfers); auto dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev); CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface"); @@ -13103,18 +13110,29 @@ void wallet2::import_blockchain(const std::tuple<size_t, crypto::hash, std::vect m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx); } //---------------------------------------------------------------------------------------------------- -std::pair<uint64_t, std::vector<tools::wallet2::exported_transfer_details>> wallet2::export_outputs(bool all) const +std::tuple<uint64_t, uint64_t, std::vector<tools::wallet2::exported_transfer_details>> wallet2::export_outputs(bool all, uint32_t start, uint32_t count) const { PERF_TIMER(export_outputs); std::vector<tools::wallet2::exported_transfer_details> outs; + // invalid cases + THROW_WALLET_EXCEPTION_IF(count == 0, error::wallet_internal_error, "Nothing requested"); + THROW_WALLET_EXCEPTION_IF(!all && start > 0, error::wallet_internal_error, "Incremental mode is incompatible with non-zero start"); + + // valid cases: + // all: all outputs, subject to start/count + // !all: incremental, subject to count + // for convenience, start/count are allowed to go past the valid range, then nothing is returned + size_t offset = 0; if (!all) while (offset < m_transfers.size() && (m_transfers[offset].m_key_image_known && !m_transfers[offset].m_key_image_request)) ++offset; + else + offset = start; outs.reserve(m_transfers.size() - offset); - for (size_t n = offset; n < m_transfers.size(); ++n) + for (size_t n = offset; n < m_transfers.size() && n - offset < count; ++n) { const transfer_details &td = m_transfers[n]; @@ -13132,20 +13150,22 @@ std::pair<uint64_t, std::vector<tools::wallet2::exported_transfer_details>> wall etd.m_flags.m_key_image_partial = td.m_key_image_partial; etd.m_amount = td.m_amount; etd.m_additional_tx_keys = get_additional_tx_pub_keys_from_extra(td.m_tx); + etd.m_subaddr_index_major = td.m_subaddr_index.major; + etd.m_subaddr_index_minor = td.m_subaddr_index.minor; outs.push_back(etd); } - return std::make_pair(offset, outs); + return std::make_tuple(offset, m_transfers.size(), outs); } //---------------------------------------------------------------------------------------------------- -std::string wallet2::export_outputs_to_str(bool all) const +std::string wallet2::export_outputs_to_str(bool all, uint32_t start, uint32_t count) const { PERF_TIMER(export_outputs_to_str); std::stringstream oss; binary_archive<true> ar(oss); - auto outputs = export_outputs(all); + auto outputs = export_outputs(all, start, count); THROW_WALLET_EXCEPTION_IF(!::serialization::serialize(ar, outputs), error::wallet_internal_error, "Failed to serialize output data"); std::string magic(OUTPUT_EXPORT_FILE_MAGIC, strlen(OUTPUT_EXPORT_FILE_MAGIC)); @@ -13158,21 +13178,35 @@ std::string wallet2::export_outputs_to_str(bool all) const return magic + ciphertext; } //---------------------------------------------------------------------------------------------------- -size_t wallet2::import_outputs(const std::pair<uint64_t, std::vector<tools::wallet2::transfer_details>> &outputs) +size_t wallet2::import_outputs(const std::tuple<uint64_t, uint64_t, std::vector<tools::wallet2::transfer_details>> &outputs) { PERF_TIMER(import_outputs); - THROW_WALLET_EXCEPTION_IF(outputs.first > m_transfers.size(), error::wallet_internal_error, + THROW_WALLET_EXCEPTION_IF(m_has_ever_refreshed_from_node, error::wallet_internal_error, + "Hot wallets cannot import outputs"); + + // we can now import piecemeal + const size_t offset = std::get<0>(outputs); + const size_t num_outputs = std::get<1>(outputs); + const std::vector<tools::wallet2::transfer_details> &output_array = std::get<2>(outputs); + + THROW_WALLET_EXCEPTION_IF(offset > m_transfers.size(), error::wallet_internal_error, "Imported outputs omit more outputs that we know of"); - const size_t offset = outputs.first; + THROW_WALLET_EXCEPTION_IF(offset >= num_outputs, error::wallet_internal_error, + "Offset is larger than total outputs"); + THROW_WALLET_EXCEPTION_IF(output_array.size() > num_outputs - offset, error::wallet_internal_error, + "Offset is larger than total outputs"); + const size_t original_size = m_transfers.size(); - m_transfers.resize(offset + outputs.second.size()); - for (size_t i = 0; i < offset; ++i) - m_transfers[i].m_key_image_request = false; - for (size_t i = 0; i < outputs.second.size(); ++i) + if (offset + output_array.size() > m_transfers.size()) + m_transfers.resize(offset + output_array.size()); + else if (num_outputs < m_transfers.size()) + m_transfers.resize(num_outputs); + + for (size_t i = 0; i < output_array.size(); ++i) { - transfer_details td = outputs.second[i]; + transfer_details td = output_array[i]; // skip those we've already imported, or which have different data if (i + offset < original_size) @@ -13206,6 +13240,8 @@ process: THROW_WALLET_EXCEPTION_IF(td.m_internal_output_index >= td.m_tx.vout.size(), error::wallet_internal_error, "Internal index is out of range"); crypto::public_key out_key = td.get_public_key(); + if (should_expand(td.m_subaddr_index)) + create_one_off_subaddress(td.m_subaddr_index); bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, out_key, tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, in_ephemeral, td.m_key_image, m_account.get_device()); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image"); if (should_expand(td.m_subaddr_index)) @@ -13224,24 +13260,38 @@ process: return m_transfers.size(); } //---------------------------------------------------------------------------------------------------- -size_t wallet2::import_outputs(const std::pair<uint64_t, std::vector<tools::wallet2::exported_transfer_details>> &outputs) +size_t wallet2::import_outputs(const std::tuple<uint64_t, uint64_t, std::vector<tools::wallet2::exported_transfer_details>> &outputs) { PERF_TIMER(import_outputs); - THROW_WALLET_EXCEPTION_IF(outputs.first > m_transfers.size(), error::wallet_internal_error, + THROW_WALLET_EXCEPTION_IF(m_has_ever_refreshed_from_node, error::wallet_internal_error, + "Hot wallets cannot import outputs"); + + // we can now import piecemeal + const size_t offset = std::get<0>(outputs); + const size_t num_outputs = std::get<1>(outputs); + const std::vector<tools::wallet2::exported_transfer_details> &output_array = std::get<2>(outputs); + + THROW_WALLET_EXCEPTION_IF(offset > m_transfers.size(), error::wallet_internal_error, "Imported outputs omit more outputs that we know of. Try using export_outputs all."); - const size_t offset = outputs.first; + THROW_WALLET_EXCEPTION_IF(offset >= num_outputs, error::wallet_internal_error, + "Offset is larger than total outputs"); + THROW_WALLET_EXCEPTION_IF(output_array.size() > num_outputs - offset, error::wallet_internal_error, + "Offset is larger than total outputs"); + const size_t original_size = m_transfers.size(); - m_transfers.resize(offset + outputs.second.size()); - for (size_t i = 0; i < offset; ++i) - m_transfers[i].m_key_image_request = false; - for (size_t i = 0; i < outputs.second.size(); ++i) + if (offset + output_array.size() > m_transfers.size()) + m_transfers.resize(offset + output_array.size()); + else if (num_outputs < m_transfers.size()) + m_transfers.resize(num_outputs); + + for (size_t i = 0; i < output_array.size(); ++i) { - exported_transfer_details etd = outputs.second[i]; + exported_transfer_details etd = output_array[i]; transfer_details &td = m_transfers[i + offset]; - // setup td with "cheao" loaded data + // setup td with "cheap" loaded data td.m_block_height = 0; td.m_txid = crypto::null_hash; td.m_global_output_index = etd.m_global_output_index; @@ -13254,6 +13304,8 @@ size_t wallet2::import_outputs(const std::pair<uint64_t, std::vector<tools::wall td.m_key_image_known = etd.m_flags.m_key_image_known; td.m_key_image_request = etd.m_flags.m_key_image_request; td.m_key_image_partial = false; + td.m_subaddr_index.major = etd.m_subaddr_index_major; + td.m_subaddr_index.minor = etd.m_subaddr_index_minor; // skip those we've already imported, or which have different data if (i + offset < original_size) @@ -13294,6 +13346,8 @@ size_t wallet2::import_outputs(const std::pair<uint64_t, std::vector<tools::wall const crypto::public_key &tx_pub_key = etd.m_tx_pubkey; const std::vector<crypto::public_key> &additional_tx_pub_keys = etd.m_additional_tx_keys; const crypto::public_key& out_key = etd.m_pubkey; + if (should_expand(td.m_subaddr_index)) + create_one_off_subaddress(td.m_subaddr_index); bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, out_key, tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, in_ephemeral, td.m_key_image, m_account.get_device()); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image"); if (should_expand(td.m_subaddr_index)) @@ -13350,7 +13404,7 @@ size_t wallet2::import_outputs_from_str(const std::string &outputs_st) { std::string body(data, headerlen); - std::pair<uint64_t, std::vector<tools::wallet2::exported_transfer_details>> new_outputs; + std::tuple<uint64_t, uint64_t, std::vector<tools::wallet2::exported_transfer_details>> new_outputs; try { binary_archive<false> ar{epee::strspan<std::uint8_t>(body)}; @@ -13360,9 +13414,9 @@ size_t wallet2::import_outputs_from_str(const std::string &outputs_st) } catch (...) {} if (!loaded) - new_outputs.second.clear(); + std::get<2>(new_outputs).clear(); - std::pair<uint64_t, std::vector<tools::wallet2::transfer_details>> outputs; + std::tuple<uint64_t, uint64_t, std::vector<tools::wallet2::transfer_details>> outputs; if (!loaded) try { binary_archive<false> ar{epee::strspan<std::uint8_t>(body)}; @@ -13387,11 +13441,12 @@ size_t wallet2::import_outputs_from_str(const std::string &outputs_st) if (!loaded) { - outputs.first = 0; - outputs.second = {}; + std::get<0>(outputs) = 0; + std::get<1>(outputs) = 0; + std::get<2>(outputs) = {}; } - imported_outputs = new_outputs.second.empty() ? import_outputs(outputs) : import_outputs(new_outputs); + imported_outputs = !std::get<2>(new_outputs).empty() ? import_outputs(new_outputs) : !std::get<2>(outputs).empty() ? import_outputs(outputs) : 0; } catch (const std::exception &e) { diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 16e898ad8..1f84458a6 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -63,6 +63,7 @@ #include "serialization/crypto.h" #include "serialization/string.h" #include "serialization/pair.h" +#include "serialization/tuple.h" #include "serialization/containers.h" #include "wallet_errors.h" @@ -401,9 +402,13 @@ private: } m_flags; uint64_t m_amount; std::vector<crypto::public_key> m_additional_tx_keys; + uint32_t m_subaddr_index_major; + uint32_t m_subaddr_index_minor; BEGIN_SERIALIZE_OBJECT() - VERSION_FIELD(0) + VERSION_FIELD(1) + if (version < 1) + return false; FIELD(m_pubkey) VARINT_FIELD(m_internal_output_index) VARINT_FIELD(m_global_output_index) @@ -411,6 +416,8 @@ private: FIELD(m_flags.flags) VARINT_FIELD(m_amount) FIELD(m_additional_tx_keys) + VARINT_FIELD(m_subaddr_index_major) + VARINT_FIELD(m_subaddr_index_minor) END_SERIALIZE() }; @@ -667,16 +674,32 @@ private: struct unsigned_tx_set { std::vector<tx_construction_data> txes; - std::pair<size_t, wallet2::transfer_container> transfers; - std::pair<size_t, std::vector<wallet2::exported_transfer_details>> new_transfers; + std::tuple<uint64_t, uint64_t, wallet2::transfer_container> transfers; + std::tuple<uint64_t, uint64_t, std::vector<wallet2::exported_transfer_details>> new_transfers; BEGIN_SERIALIZE_OBJECT() - VERSION_FIELD(1) + VERSION_FIELD(2) FIELD(txes) - if (version >= 1) - FIELD(new_transfers) - else - FIELD(transfers) + if (version == 0) + { + std::pair<size_t, wallet2::transfer_container> v0_transfers; + FIELD(v0_transfers); + std::get<0>(transfers) = std::get<0>(v0_transfers); + std::get<1>(transfers) = std::get<0>(v0_transfers) + std::get<1>(v0_transfers).size(); + std::get<2>(transfers) = std::get<1>(v0_transfers); + return true; + } + if (version == 1) + { + std::pair<size_t, std::vector<wallet2::exported_transfer_details>> v1_transfers; + FIELD(v1_transfers); + std::get<0>(new_transfers) = std::get<0>(v1_transfers); + std::get<1>(new_transfers) = std::get<0>(v1_transfers) + std::get<1>(v1_transfers).size(); + std::get<2>(new_transfers) = std::get<1>(v1_transfers); + return true; + } + + FIELD(new_transfers) END_SERIALIZE() }; @@ -1206,11 +1229,17 @@ private: if(ver < 29) return; a & m_rpc_client_secret_key; + if(ver < 30) + { + m_has_ever_refreshed_from_node = false; + return; + } + a & m_has_ever_refreshed_from_node; } BEGIN_SERIALIZE_OBJECT() MAGIC_FIELD("monero wallet cache") - VERSION_FIELD(0) + VERSION_FIELD(1) FIELD(m_blockchain) FIELD(m_transfers) FIELD(m_account_public_address) @@ -1236,6 +1265,12 @@ private: FIELD(m_device_last_key_image_sync) FIELD(m_cold_key_images) FIELD(m_rpc_client_secret_key) + if (version < 1) + { + m_has_ever_refreshed_from_node = false; + return true; + } + FIELD(m_has_ever_refreshed_from_node) END_SERIALIZE() /*! @@ -1447,10 +1482,10 @@ private: bool verify_with_public_key(const std::string &data, const crypto::public_key &public_key, const std::string &signature) const; // Import/Export wallet data - std::pair<uint64_t, std::vector<tools::wallet2::exported_transfer_details>> export_outputs(bool all = false) const; - std::string export_outputs_to_str(bool all = false) const; - size_t import_outputs(const std::pair<uint64_t, std::vector<tools::wallet2::exported_transfer_details>> &outputs); - size_t import_outputs(const std::pair<uint64_t, std::vector<tools::wallet2::transfer_details>> &outputs); + std::tuple<uint64_t, uint64_t, std::vector<tools::wallet2::exported_transfer_details>> export_outputs(bool all = false, uint32_t start = 0, uint32_t count = 0xffffffff) const; + std::string export_outputs_to_str(bool all = false, uint32_t start = 0, uint32_t count = 0xffffffff) const; + size_t import_outputs(const std::tuple<uint64_t, uint64_t, std::vector<tools::wallet2::exported_transfer_details>> &outputs); + size_t import_outputs(const std::tuple<uint64_t, uint64_t, std::vector<tools::wallet2::transfer_details>> &outputs); size_t import_outputs_from_str(const std::string &outputs_st); payment_container export_payments() const; void import_payments(const payment_container &payments); @@ -1883,11 +1918,13 @@ private: ExportFormat m_export_format; bool m_load_deprecated_formats; + bool m_has_ever_refreshed_from_node; + static boost::mutex default_daemon_address_lock; static std::string default_daemon_address; }; } -BOOST_CLASS_VERSION(tools::wallet2, 29) +BOOST_CLASS_VERSION(tools::wallet2, 30) 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) @@ -1898,7 +1935,7 @@ BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 8) BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 6) BOOST_CLASS_VERSION(tools::wallet2::address_book_row, 18) BOOST_CLASS_VERSION(tools::wallet2::reserve_proof_entry, 0) -BOOST_CLASS_VERSION(tools::wallet2::unsigned_tx_set, 0) +BOOST_CLASS_VERSION(tools::wallet2::unsigned_tx_set, 1) BOOST_CLASS_VERSION(tools::wallet2::signed_tx_set, 1) BOOST_CLASS_VERSION(tools::wallet2::tx_construction_data, 4) BOOST_CLASS_VERSION(tools::wallet2::pending_tx, 3) @@ -1908,6 +1945,17 @@ namespace boost { namespace serialization { + template<class Archive, class F, class S, class T> + inline void serialize( + Archive & ar, + std::tuple<F, S, T> & t, + const unsigned int /* file_version */ + ){ + ar & boost::serialization::make_nvp("f", std::get<0>(t)); + ar & boost::serialization::make_nvp("s", std::get<1>(t)); + ar & boost::serialization::make_nvp("t", std::get<2>(t)); + } + template <class Archive> inline typename std::enable_if<!Archive::is_loading::value, void>::type initialize_transfer_details(Archive &a, tools::wallet2::transfer_details &x, const boost::serialization::version_type ver) { @@ -2268,7 +2316,17 @@ namespace boost inline void serialize(Archive &a, tools::wallet2::unsigned_tx_set &x, const boost::serialization::version_type ver) { a & x.txes; - a & x.transfers; + if (ver == 0) + { + // load old version + std::pair<size_t, tools::wallet2::transfer_container> old_transfers; + a & old_transfers; + std::get<0>(x.transfers) = std::get<0>(old_transfers); + std::get<1>(x.transfers) = std::get<0>(old_transfers) + std::get<1>(old_transfers).size(); + std::get<2>(x.transfers) = std::get<1>(old_transfers); + return; + } + throw std::runtime_error("Boost serialization not supported for newest unsigned_tx_set"); } template <class Archive> diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 7ec5fc7a1..a8684d633 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -2792,7 +2792,7 @@ namespace tools try { - res.outputs_data_hex = epee::string_tools::buff_to_hex_nodelimer(m_wallet->export_outputs_to_str(req.all)); + res.outputs_data_hex = epee::string_tools::buff_to_hex_nodelimer(m_wallet->export_outputs_to_str(req.all, req.start, req.count)); } catch (const std::exception &e) { @@ -3254,7 +3254,7 @@ namespace tools if (!m_wallet) return not_open(er); cryptonote::COMMAND_RPC_STOP_MINING::request daemon_req; cryptonote::COMMAND_RPC_STOP_MINING::response daemon_res; - bool r = m_wallet->invoke_http_json("/stop_mining", daemon_req, daemon_res); + bool r = m_wallet->invoke_http_json("/stop_mining", daemon_req, daemon_res, std::chrono::seconds(60)); // this waits till stopped, and if randomx has just started initializing its dataset, it might be a while if (!r || daemon_res.status != CORE_RPC_STATUS_OK) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index ecfc8e673..2cca323ee 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -1785,9 +1785,13 @@ namespace wallet_rpc struct request_t { bool all; + uint32_t start; + uint32_t count; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(all) + KV_SERIALIZE_OPT(start, 0u) + KV_SERIALIZE_OPT(count, 0xffffffffu) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; |