aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/blockchain_db/blockchain_db.h16
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp86
-rw-r--r--src/common/dns_utils.cpp1
-rw-r--r--src/crypto/hash-extra-jh.c6
-rw-r--r--src/crypto/hash-extra-skein.c6
-rw-r--r--src/crypto/keccak.c2
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.cpp20
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.h2
-rw-r--r--src/cryptonote_core/blockchain.h7
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp55
-rw-r--r--src/cryptonote_core/cryptonote_core.h14
-rw-r--r--src/cryptonote_core/tx_pool.cpp106
-rw-r--r--src/cryptonote_core/tx_pool.h17
-rw-r--r--src/daemon/main.cpp2
-rw-r--r--src/daemon/rpc_command_executor.cpp2
-rw-r--r--src/device/device.hpp1
-rw-r--r--src/device/device_default.hpp2
-rw-r--r--src/device/device_ledger.cpp79
-rw-r--r--src/device/device_ledger.hpp7
-rw-r--r--src/device/log.cpp4
-rw-r--r--src/device/log.hpp1
-rw-r--r--src/device_trezor/device_trezor.cpp40
-rw-r--r--src/device_trezor/trezor/protocol.cpp225
-rw-r--r--src/device_trezor/trezor/protocol.hpp49
-rw-r--r--src/rpc/core_rpc_server.cpp6
-rw-r--r--src/rpc/core_rpc_server.h4
-rw-r--r--src/serialization/tuple.h169
-rw-r--r--src/simplewallet/simplewallet.cpp6
-rw-r--r--src/version.cpp.in2
-rw-r--r--src/wallet/api/wallet.cpp4
-rw-r--r--src/wallet/wallet2.cpp119
-rw-r--r--src/wallet/wallet2.h90
-rw-r--r--src/wallet/wallet_rpc_server.cpp4
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h4
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;