aboutsummaryrefslogtreecommitdiff
path: root/src/cryptonote_core
diff options
context:
space:
mode:
authorj-berman <justinberman@protonmail.com>2021-11-15 05:23:53 -0800
committerj-berman <justinberman@protonmail.com>2022-04-18 00:49:53 -0700
commitea87b30f8907ee11252433811e7a7d0c46758cca (patch)
tree61dedf56a781a83285be092b078019bebdc94f2e /src/cryptonote_core
parentMerge pull request #8207 (diff)
downloadmonero-ea87b30f8907ee11252433811e7a7d0c46758cca.tar.xz
Add view tags to outputs to reduce wallet scanning time
Implements view tags as proposed by @UkoeHB in MRL issue https://github.com/monero-project/research-lab/issues/73 At tx construction, the sender adds a 1-byte view tag to each output. The view tag is derived from the sender-receiver shared secret. When scanning for outputs, the receiver can check the view tag for a match, in order to reduce scanning time. When the view tag does not match, the wallet avoids the more expensive EC operations when deriving the output public key using the shared secret.
Diffstat (limited to 'src/cryptonote_core')
-rw-r--r--src/cryptonote_core/blockchain.cpp32
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp14
-rw-r--r--src/cryptonote_core/cryptonote_core.h3
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.cpp35
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.h10
5 files changed, 62 insertions, 32 deletions
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 5f9db676c..0b6a3abf3 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -1341,6 +1341,7 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std:
// one input, of type txin_gen, with height set to the block's height
// correct miner tx unlock time
// a non-overflowing tx amount (dubious necessity on this check)
+// valid output types
bool Blockchain::prevalidate_miner_transaction(const block& b, uint64_t height, uint8_t hf_version)
{
LOG_PRINT_L3("Blockchain::" << __func__);
@@ -1369,6 +1370,8 @@ bool Blockchain::prevalidate_miner_transaction(const block& b, uint64_t height,
return false;
}
+ CHECK_AND_ASSERT_MES(check_output_types(b.miner_tx, hf_version), false, "miner transaction has invalid output type(s) in block " << get_block_hash(b));
+
return true;
}
//------------------------------------------------------------------
@@ -3044,12 +3047,14 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
// from v4, forbid invalid pubkeys
if (hf_version >= 4) {
for (const auto &o: tx.vout) {
- if (o.target.type() == typeid(txout_to_key)) {
- const txout_to_key& out_to_key = boost::get<txout_to_key>(o.target);
- if (!crypto::check_key(out_to_key.key)) {
- tvc.m_invalid_output = true;
- return false;
- }
+ crypto::public_key output_public_key;
+ if (!get_output_public_key(o, output_public_key)) {
+ tvc.m_invalid_output = true;
+ return false;
+ }
+ if (!crypto::check_key(output_public_key)) {
+ tvc.m_invalid_output = true;
+ return false;
}
}
}
@@ -3166,6 +3171,13 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
}
}
+ // from v15, require view tags on outputs
+ if (!check_output_types(tx, hf_version))
+ {
+ tvc.m_invalid_output = true;
+ return false;
+ }
+
return true;
}
//------------------------------------------------------------------
@@ -3977,9 +3989,11 @@ bool Blockchain::check_tx_input(size_t tx_version, const txin_to_key& txin, cons
}
// The original code includes a check for the output corresponding to this input
- // to be a txout_to_key. This is removed, as the database does not store this info,
- // but only txout_to_key outputs are stored in the DB in the first place, done in
- // Blockchain*::add_output
+ // to be a txout_to_key. This is removed, as the database does not store this info.
+ // Only txout_to_key (and since HF_VERSION_VIEW_TAGS, txout_to_tagged_key)
+ // outputs are stored in the DB in the first place, done in Blockchain*::add_output.
+ // Additional type checks on outputs were also added via cryptonote::check_output_types
+ // and cryptonote::get_output_public_key (see Blockchain::check_tx_outputs).
m_output_keys.push_back(rct::ctkey({rct::pk2rct(pubkey), commitment}));
return true;
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 2d2b1cbcf..a78f5d673 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -1177,7 +1177,8 @@ namespace cryptonote
return false;
}
- if (!check_tx_inputs_ring_members_diff(tx))
+ const uint8_t hf_version = m_blockchain_storage.get_current_hard_fork_version();
+ if (!check_tx_inputs_ring_members_diff(tx, hf_version))
{
MERROR_VER("tx uses duplicate ring members");
return false;
@@ -1189,6 +1190,12 @@ namespace cryptonote
return false;
}
+ if (!check_output_types(tx, hf_version))
+ {
+ MERROR_VER("tx does not use valid output type(s)");
+ return false;
+ }
+
return true;
}
//-----------------------------------------------------------------------------------------------
@@ -1295,10 +1302,9 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------------------------------------
- bool core::check_tx_inputs_ring_members_diff(const transaction& tx) const
+ bool core::check_tx_inputs_ring_members_diff(const transaction& tx, const uint8_t hf_version) const
{
- const uint8_t version = m_blockchain_storage.get_current_hard_fork_version();
- if (version >= 6)
+ if (hf_version >= 6)
{
for(const auto& in: tx.vin)
{
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index 2e5248c5d..0b36730b6 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -1012,10 +1012,11 @@ namespace cryptonote
* @brief verify that each ring uses distinct members
*
* @param tx the transaction to check
+ * @param hf_version the hard fork version rules to use
*
* @return false if any ring uses duplicate members, true otherwise
*/
- bool check_tx_inputs_ring_members_diff(const transaction& tx) const;
+ bool check_tx_inputs_ring_members_diff(const transaction& tx, const uint8_t hf_version) const;
/**
* @brief verify that each input key image in a transaction is in
diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp
index 76192be5d..1d2024a05 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.cpp
+++ b/src/cryptonote_core/cryptonote_tx_utils.cpp
@@ -149,12 +149,17 @@ namespace cryptonote
r = crypto::derive_public_key(derivation, no, miner_address.m_spend_public_key, out_eph_public_key);
CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to derive_public_key(" << derivation << ", " << no << ", "<< miner_address.m_spend_public_key << ")");
- txout_to_key tk;
- tk.key = out_eph_public_key;
+ uint64_t amount = out_amounts[no];
+ summary_amounts += amount;
+
+ bool use_view_tags = hard_fork_version >= HF_VERSION_VIEW_TAGS;
+ crypto::view_tag view_tag;
+ if (use_view_tags)
+ crypto::derive_view_tag(derivation, no, view_tag);
tx_out out;
- summary_amounts += out.amount = out_amounts[no];
- out.target = tk;
+ cryptonote::set_tx_out(amount, out_eph_public_key, use_view_tags, view_tag, out);
+
tx.vout.push_back(out);
}
@@ -198,7 +203,7 @@ namespace cryptonote
return addr.m_view_public_key;
}
//---------------------------------------------------------------
- bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct, const rct::RCTConfig &rct_config, rct::multisig_out *msout, bool shuffle_outs)
+ bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct, const rct::RCTConfig &rct_config, rct::multisig_out *msout, bool shuffle_outs, bool use_view_tags)
{
hw::device &hwdev = sender_account_keys.get_device();
@@ -406,17 +411,16 @@ namespace cryptonote
{
CHECK_AND_ASSERT_MES(dst_entr.amount > 0 || tx.version > 1, false, "Destination with wrong amount: " << dst_entr.amount);
crypto::public_key out_eph_public_key;
+ crypto::view_tag view_tag;
hwdev.generate_output_ephemeral_keys(tx.version,sender_account_keys, txkey_pub, tx_key,
dst_entr, change_addr, output_index,
need_additional_txkeys, additional_tx_keys,
- additional_tx_public_keys, amount_keys, out_eph_public_key);
+ additional_tx_public_keys, amount_keys, out_eph_public_key,
+ use_view_tags, view_tag);
tx_out out;
- out.amount = dst_entr.amount;
- txout_to_key tk;
- tk.key = out_eph_public_key;
- out.target = tk;
+ cryptonote::set_tx_out(dst_entr.amount, out_eph_public_key, use_view_tags, view_tag, out);
tx.vout.push_back(out);
output_index++;
summary_outs_money += dst_entr.amount;
@@ -546,7 +550,9 @@ namespace cryptonote
}
for (size_t i = 0; i < tx.vout.size(); ++i)
{
- destinations.push_back(rct::pk2rct(boost::get<txout_to_key>(tx.vout[i].target).key));
+ crypto::public_key output_public_key;
+ get_output_public_key(tx.vout[i], output_public_key);
+ destinations.push_back(rct::pk2rct(output_public_key));
outamounts.push_back(tx.vout[i].amount);
amount_out += tx.vout[i].amount;
}
@@ -607,7 +613,7 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------
- bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct, const rct::RCTConfig &rct_config, rct::multisig_out *msout)
+ bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct, const rct::RCTConfig &rct_config, rct::multisig_out *msout, bool use_view_tags)
{
hw::device &hwdev = sender_account_keys.get_device();
hwdev.open_tx(tx_key);
@@ -627,7 +633,8 @@ namespace cryptonote
}
}
- bool r = construct_tx_with_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct, rct_config, msout);
+ bool shuffle_outs = true;
+ bool r = construct_tx_with_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct, rct_config, msout, shuffle_outs, use_view_tags);
hwdev.close_tx();
return r;
} catch(...) {
@@ -643,7 +650,7 @@ namespace cryptonote
crypto::secret_key tx_key;
std::vector<crypto::secret_key> additional_tx_keys;
std::vector<tx_destination_entry> destinations_copy = destinations;
- return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations_copy, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, { rct::RangeProofBorromean, 0}, NULL);
+ return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations_copy, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, { rct::RangeProofBorromean, 0}, NULL, false);
}
//---------------------------------------------------------------
bool generate_genesis_block(
diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h
index be6a6d946..f4ffb98ff 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.h
+++ b/src/cryptonote_core/cryptonote_tx_utils.h
@@ -119,21 +119,23 @@ namespace cryptonote
//---------------------------------------------------------------
crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::account_public_address>& change_addr);
bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry> &sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time);
- bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0 }, rct::multisig_out *msout = NULL, bool shuffle_outs = true);
- bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0 }, rct::multisig_out *msout = NULL);
+ bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0 }, rct::multisig_out *msout = NULL, bool shuffle_outs = true, bool use_view_tags = false);
+ bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0 }, rct::multisig_out *msout = NULL, bool use_view_tags = false);
bool generate_output_ephemeral_keys(const size_t tx_version, const cryptonote::account_keys &sender_account_keys, const crypto::public_key &txkey_pub, const crypto::secret_key &tx_key,
const cryptonote::tx_destination_entry &dst_entr, const boost::optional<cryptonote::account_public_address> &change_addr, const size_t output_index,
const bool &need_additional_txkeys, const std::vector<crypto::secret_key> &additional_tx_keys,
std::vector<crypto::public_key> &additional_tx_public_keys,
std::vector<rct::key> &amount_keys,
- crypto::public_key &out_eph_public_key) ;
+ crypto::public_key &out_eph_public_key,
+ const bool use_view_tags, crypto::view_tag &view_tag) ;
bool generate_output_ephemeral_keys(const size_t tx_version, const cryptonote::account_keys &sender_account_keys, const crypto::public_key &txkey_pub, const crypto::secret_key &tx_key,
const cryptonote::tx_destination_entry &dst_entr, const boost::optional<cryptonote::account_public_address> &change_addr, const size_t output_index,
const bool &need_additional_txkeys, const std::vector<crypto::secret_key> &additional_tx_keys,
std::vector<crypto::public_key> &additional_tx_public_keys,
std::vector<rct::key> &amount_keys,
- crypto::public_key &out_eph_public_key) ;
+ crypto::public_key &out_eph_public_key,
+ const bool use_view_tags, crypto::view_tag &view_tag) ;
bool generate_genesis_block(
block& bl