From ea87b30f8907ee11252433811e7a7d0c46758cca Mon Sep 17 00:00:00 2001 From: j-berman Date: Mon, 15 Nov 2021 05:23:53 -0800 Subject: 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. --- tests/core_tests/block_validation.cpp | 124 ++++++++++++++++++++++++++++ tests/core_tests/block_validation.h | 34 ++++++++ tests/core_tests/chaingen.cpp | 22 +++-- tests/core_tests/chaingen.h | 9 +- tests/core_tests/chaingen_main.cpp | 13 +++ tests/core_tests/rct.cpp | 108 ++++++++++++++++++++++-- tests/core_tests/rct.h | 70 +++++++++++++++- tests/crypto/main.cpp | 10 ++- tests/crypto/tests.txt | 56 +++++++++++++ tests/performance_tests/CMakeLists.txt | 1 + tests/performance_tests/derive_view_tag.h | 62 ++++++++++++++ tests/performance_tests/is_out_to_acc.h | 5 +- tests/performance_tests/main.cpp | 6 ++ tests/performance_tests/out_can_be_to_acc.h | 103 +++++++++++++++++++++++ 14 files changed, 603 insertions(+), 20 deletions(-) create mode 100644 tests/performance_tests/derive_view_tag.h create mode 100644 tests/performance_tests/out_can_be_to_acc.h (limited to 'tests') diff --git a/tests/core_tests/block_validation.cpp b/tests/core_tests/block_validation.cpp index 28f1de7d0..711f67b41 100644 --- a/tests/core_tests/block_validation.cpp +++ b/tests/core_tests/block_validation.cpp @@ -671,3 +671,127 @@ bool gen_block_low_coinbase::generate(std::vector& events) con return true; } + +bool gen_block_miner_tx_out_has_no_view_tag_before_hf_view_tags::generate(std::vector& events) const +{ + bool use_view_tags = false; + + BLOCK_VALIDATION_INIT_GENERATE(); + + MAKE_MINER_TX_MANUALLY(miner_tx, blk_0); + + CHECK_AND_ASSERT_MES(!cryptonote::get_output_view_tag(miner_tx.vout[0]), false, "output should not have a view tag"); + + crypto::public_key output_public_key; + crypto::view_tag view_tag; + cryptonote::get_output_public_key(miner_tx.vout[0], output_public_key); + + // explicitly call the setter to ensure it does not set a view tag on the miner tx output + cryptonote::set_tx_out(miner_tx.vout[0].amount, output_public_key, use_view_tags, view_tag, miner_tx.vout[0]); + CHECK_AND_ASSERT_MES(!cryptonote::get_output_view_tag(miner_tx.vout[0]), false, "output should still not have a view tag"); + + block blk_1; + generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx); + events.push_back(blk_1); + + DO_CALLBACK(events, "check_block_accepted"); + + return true; +} + +bool gen_block_miner_tx_out_has_no_view_tag_from_hf_view_tags::generate(std::vector& events) const +{ + bool use_view_tags = false; + + BLOCK_VALIDATION_INIT_GENERATE(); + + keypair txkey; + MAKE_MINER_TX_AND_KEY_AT_HF_MANUALLY(miner_tx, blk_0, HF_VERSION_VIEW_TAGS+1, &txkey); + + crypto::public_key output_public_key; + crypto::view_tag view_tag; + cryptonote::get_output_public_key(miner_tx.vout[0], output_public_key); + + // remove the view tag that is currently set on the miner tx output at this point + cryptonote::set_tx_out(miner_tx.vout[0].amount, output_public_key, use_view_tags, view_tag, miner_tx.vout[0]); + CHECK_AND_ASSERT_MES(!cryptonote::get_output_view_tag(miner_tx.vout[0]), false, "output should not have a view tag"); + + block blk_1; + generator.construct_block_manually(blk_1, blk_0, miner_account, + test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_miner_tx, + HF_VERSION_VIEW_TAGS+1, HF_VERSION_VIEW_TAGS+1, 0, crypto::hash(), 0, miner_tx); + events.push_back(blk_1); + + DO_CALLBACK(events, "check_block_purged"); + + return true; +} + +bool gen_block_miner_tx_out_has_view_tag_before_hf_view_tags::generate(std::vector& events) const +{ + bool use_view_tags = true; + + BLOCK_VALIDATION_INIT_GENERATE(); + + keypair txkey; + MAKE_MINER_TX_AND_KEY_MANUALLY(miner_tx, blk_0, &txkey); + + // derive the view tag for the miner tx output + crypto::key_derivation derivation; + crypto::public_key output_public_key; + crypto::view_tag view_tag; + crypto::generate_key_derivation(miner_account.get_keys().m_account_address.m_view_public_key, txkey.sec, derivation); + crypto::derive_public_key(derivation, 0, miner_account.get_keys().m_account_address.m_spend_public_key, output_public_key); + crypto::derive_view_tag(derivation, 0, view_tag); + + // set the view tag on the miner tx output + cryptonote::set_tx_out(miner_tx.vout[0].amount, output_public_key, use_view_tags, view_tag, miner_tx.vout[0]); + boost::optional actual_vt = cryptonote::get_output_view_tag(miner_tx.vout[0]); + CHECK_AND_ASSERT_MES(actual_vt && *actual_vt == view_tag, false, "unexpected output view tag"); + + block blk_1; + generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx); + events.push_back(blk_1); + + DO_CALLBACK(events, "check_block_purged"); + + return true; +} + +bool gen_block_miner_tx_out_has_view_tag_from_hf_view_tags::generate(std::vector& events) const +{ + bool use_view_tags = true; + + BLOCK_VALIDATION_INIT_GENERATE(); + + keypair txkey; + MAKE_MINER_TX_AND_KEY_AT_HF_MANUALLY(miner_tx, blk_0, HF_VERSION_VIEW_TAGS, &txkey); + + CHECK_AND_ASSERT_MES(cryptonote::get_output_view_tag(miner_tx.vout[0]), false, "output should have a view tag"); + + // derive the view tag for the miner tx output + crypto::key_derivation derivation; + crypto::public_key output_public_key; + crypto::view_tag view_tag; + crypto::generate_key_derivation(miner_account.get_keys().m_account_address.m_view_public_key, txkey.sec, derivation); + crypto::derive_public_key(derivation, 0, miner_account.get_keys().m_account_address.m_spend_public_key, output_public_key); + crypto::derive_view_tag(derivation, 0, view_tag); + + boost::optional actual_vt = cryptonote::get_output_view_tag(miner_tx.vout[0]); + CHECK_AND_ASSERT_MES(actual_vt && *actual_vt == view_tag, false, "unexpected output view tag"); + + // set the view tag on the miner tx output + cryptonote::set_tx_out(miner_tx.vout[0].amount, output_public_key, use_view_tags, view_tag, miner_tx.vout[0]); + boost::optional actual_vt_after_setting = cryptonote::get_output_view_tag(miner_tx.vout[0]); + CHECK_AND_ASSERT_MES(actual_vt_after_setting && *actual_vt_after_setting == view_tag, false, "unexpected output view tag after setting"); + + block blk_1; + generator.construct_block_manually(blk_1, blk_0, miner_account, + test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_miner_tx, + HF_VERSION_VIEW_TAGS, HF_VERSION_VIEW_TAGS, 0, crypto::hash(), 0, miner_tx); + events.push_back(blk_1); + + DO_CALLBACK(events, "check_block_accepted"); + + return true; +} diff --git a/tests/core_tests/block_validation.h b/tests/core_tests/block_validation.h index 2535039fb..3a157aaa9 100644 --- a/tests/core_tests/block_validation.h +++ b/tests/core_tests/block_validation.h @@ -230,3 +230,37 @@ struct get_test_options { hard_forks, 0 }; }; + +struct gen_block_miner_tx_out_has_no_view_tag_before_hf_view_tags : public gen_block_accepted_base<2> +{ + bool generate(std::vector& events) const; +}; + +struct gen_block_miner_tx_out_has_no_view_tag_from_hf_view_tags : public gen_block_verification_base<1> +{ + bool generate(std::vector& events) const; +}; +template<> +struct get_test_options { + const std::pair hard_forks[3] = {std::make_pair(1, 0), std::make_pair(HF_VERSION_VIEW_TAGS+1, 1), std::make_pair(0, 0)}; + const cryptonote::test_options test_options = { + hard_forks, 0 + }; +}; + +struct gen_block_miner_tx_out_has_view_tag_before_hf_view_tags : public gen_block_verification_base<1> +{ + bool generate(std::vector& events) const; +}; + +struct gen_block_miner_tx_out_has_view_tag_from_hf_view_tags : public gen_block_accepted_base<2> +{ + bool generate(std::vector& events) const; +}; +template<> +struct get_test_options { + const std::pair hard_forks[3] = {std::make_pair(1, 0), std::make_pair(HF_VERSION_VIEW_TAGS, 1), std::make_pair(0, 0)}; + const cryptonote::test_options test_options = { + hard_forks, 0 + }; +}; diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index 689b6801b..61195c7b0 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -462,7 +462,9 @@ bool init_output_indices(map_output_idx_t& outs, std::map(out.target), get_tx_pub_key_from_extra(tx), get_additional_tx_pub_keys_from_extra(tx), j)) { + crypto::public_key output_public_key; + cryptonote::get_output_public_key(out, output_public_key); + if (is_out_to_acc(from.get_keys(), output_public_key, get_tx_pub_key_from_extra(tx), get_additional_tx_pub_keys_from_extra(tx), j)) { outs_mine[out.amount].push_back(tx_global_idx); } } @@ -972,7 +974,7 @@ std::vector build_dsts(std::initializer_list= HF_VERSION_VIEW_TAGS; + crypto::view_tag view_tag; + if (use_view_tags) + crypto::derive_view_tag(derivation, 0, view_tag); + tx_out out; - out.amount = block_reward; - out.target = txout_to_key(out_eph_public_key); + cryptonote::set_tx_out(block_reward, out_eph_public_key, use_view_tags, view_tag, out); + tx.vout.push_back(out); - tx.version = 1; + if (hf_version >= HF_VERSION_DYNAMIC_FEE) + tx.version = 2; + else + tx.version = 1; tx.unlock_time = height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; return true; diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index c1e592b86..5c8c9f79f 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -423,7 +423,8 @@ uint64_t sum_amount(const std::vector& sources); bool construct_miner_tx_manually(size_t height, uint64_t already_generated_coins, const cryptonote::account_public_address& miner_address, cryptonote::transaction& tx, - uint64_t fee, cryptonote::keypair* p_txkey = nullptr); + uint64_t fee, uint8_t hf_version = 1, + cryptonote::keypair* p_txkey = nullptr); bool construct_tx_to_key(const std::vector& events, cryptonote::transaction& tx, const cryptonote::block& blk_head, const cryptonote::account_base& from, const var_addr_t& to, uint64_t amount, @@ -967,12 +968,14 @@ inline bool do_replay_file(const std::string& filename) std::list SET_NAME; \ MAKE_TX_MIX_LIST_RCT(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, NMIX, HEAD); -#define MAKE_MINER_TX_AND_KEY_MANUALLY(TX, BLK, KEY) \ +#define MAKE_MINER_TX_AND_KEY_AT_HF_MANUALLY(TX, BLK, HF_VERSION, KEY) \ transaction TX; \ if (!construct_miner_tx_manually(get_block_height(BLK) + 1, generator.get_already_generated_coins(BLK), \ - miner_account.get_keys().m_account_address, TX, 0, KEY)) \ + miner_account.get_keys().m_account_address, TX, 0, HF_VERSION, KEY)) \ return false; +#define MAKE_MINER_TX_AND_KEY_MANUALLY(TX, BLK, KEY) MAKE_MINER_TX_AND_KEY_AT_HF_MANUALLY(TX, BLK, 1, KEY) + #define MAKE_MINER_TX_MANUALLY(TX, BLK) MAKE_MINER_TX_AND_KEY_MANUALLY(TX, BLK, 0) #define SET_EVENT_VISITOR_SETT(VEC_EVENTS, SETT) VEC_EVENTS.push_back(event_visitor_settings(SETT)); diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index 2e2d170ff..b89dff445 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -133,6 +133,10 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY(gen_block_miner_tx_out_is_big); GENERATE_AND_PLAY(gen_block_miner_tx_has_no_out); GENERATE_AND_PLAY(gen_block_miner_tx_has_out_to_alice); + GENERATE_AND_PLAY(gen_block_miner_tx_out_has_no_view_tag_before_hf_view_tags); + GENERATE_AND_PLAY(gen_block_miner_tx_out_has_no_view_tag_from_hf_view_tags); + GENERATE_AND_PLAY(gen_block_miner_tx_out_has_view_tag_before_hf_view_tags); + GENERATE_AND_PLAY(gen_block_miner_tx_out_has_view_tag_from_hf_view_tags); GENERATE_AND_PLAY(gen_block_has_invalid_tx); GENERATE_AND_PLAY(gen_block_is_too_big); GENERATE_AND_PLAY(gen_block_invalid_binary_format); // Takes up to 3 hours, if CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW == 500, up to 30 minutes, if CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW == 10 @@ -219,6 +223,15 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY(gen_rct_tx_pre_rct_increase_vin_and_fee); GENERATE_AND_PLAY(gen_rct_tx_pre_rct_altered_extra); GENERATE_AND_PLAY(gen_rct_tx_rct_altered_extra); + GENERATE_AND_PLAY(gen_rct_tx_pre_rct_has_no_view_tag_before_hf_view_tags); + // TODO: base test needs to be restructured to handle pre rct outputs after HF v12 + // GENERATE_AND_PLAY(gen_rct_tx_pre_rct_has_no_view_tag_from_hf_view_tags); + GENERATE_AND_PLAY(gen_rct_tx_pre_rct_has_view_tag_before_hf_view_tags); + // GENERATE_AND_PLAY(gen_rct_tx_pre_rct_has_view_tag_from_hf_view_tags); + GENERATE_AND_PLAY(gen_rct_tx_rct_has_no_view_tag_before_hf_view_tags); + GENERATE_AND_PLAY(gen_rct_tx_rct_has_no_view_tag_from_hf_view_tags); + GENERATE_AND_PLAY(gen_rct_tx_rct_has_view_tag_before_hf_view_tags); + GENERATE_AND_PLAY(gen_rct_tx_rct_has_view_tag_from_hf_view_tags); GENERATE_AND_PLAY(gen_rct_tx_uses_output_too_early); GENERATE_AND_PLAY(gen_multisig_tx_valid_22_1_2); diff --git a/tests/core_tests/rct.cpp b/tests/core_tests/rct.cpp index 3a26de4c2..0926483fe 100644 --- a/tests/core_tests/rct.cpp +++ b/tests/core_tests/rct.cpp @@ -41,7 +41,7 @@ using namespace cryptonote; // Tests bool gen_rct_tx_validation_base::generate_with_full(std::vector& events, - const int *out_idx, int mixin, uint64_t amount_paid, size_t second_rewind, uint8_t last_version, const rct::RCTConfig &rct_config, bool valid, + const int *out_idx, int mixin, uint64_t amount_paid, size_t second_rewind, uint8_t last_version, const rct::RCTConfig &rct_config, bool use_view_tags, bool valid, const std::function &sources, std::vector &destinations)> &pre_tx, const std::function &post_tx) const { @@ -98,7 +98,9 @@ bool gen_rct_tx_validation_base::generate_with_full(std::vector(blocks[m].miner_tx.vout[index_in_tx].target).key, src.amount); + crypto::public_key output_public_key; + cryptonote::get_output_public_key(blocks[m].miner_tx.vout[index_in_tx], output_public_key); + src.push_output(m, output_public_key, src.amount); } src.real_out_tx_key = cryptonote::get_tx_pub_key_from_extra(blocks[n].miner_tx); src.real_output = n; @@ -139,10 +141,13 @@ bool gen_rct_tx_validation_base::generate_with_full(std::vector additional_tx_keys; std::unordered_map subaddresses; subaddresses[miner_accounts[0].get_keys().m_account_address.m_spend_public_key] = {0,0}; - bool r = construct_tx_and_get_tx_key(miner_accounts[0].get_keys(), subaddresses, sources, destinations, cryptonote::account_public_address{}, std::vector(), tx, 0, tx_key, additional_tx_keys, true, rct_config); + bool r = construct_tx_and_get_tx_key(miner_accounts[0].get_keys(), subaddresses, sources, destinations, cryptonote::account_public_address{}, std::vector(), tx, 0, tx_key, additional_tx_keys, true, rct_config, NULL, use_view_tags); CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction"); if (post_tx) @@ -244,7 +249,8 @@ bool gen_rct_tx_validation_base::generate_with(std::vector& ev const std::function &post_tx) const { const rct::RCTConfig rct_config { rct::RangeProofBorromean, 0 }; - return generate_with_full(events, out_idx, mixin, amount_paid, CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE, 4, rct_config, valid, pre_tx, post_tx); + bool use_view_tags = false; + return generate_with_full(events, out_idx, mixin, amount_paid, CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE, 4, rct_config, use_view_tags, valid, pre_tx, post_tx); } bool gen_rct_tx_valid_from_pre_rct::generate(std::vector& events) const @@ -517,11 +523,99 @@ bool gen_rct_tx_rct_altered_extra::generate(std::vector& event NULL, [&failed](transaction &tx) {std::string extra_nonce; crypto::hash pid = crypto::null_hash; set_payment_id_to_tx_extra_nonce(extra_nonce, pid); if (!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce)) failed = true; }) && !failed; } +bool gen_rct_tx_pre_rct_has_no_view_tag_before_hf_view_tags::generate(std::vector& events) const +{ + const int mixin = 2; + const int out_idx[] = {0, -1}; + const uint64_t amount_paid = 10000; + bool use_view_tags = false; + bool valid = true; + return generate_with_full(events, out_idx, mixin, amount_paid, 0, 0, {}, use_view_tags, valid, NULL, NULL); +} + +bool gen_rct_tx_pre_rct_has_no_view_tag_from_hf_view_tags::generate(std::vector& events) const +{ + const int mixin = 10; + const int out_idx[] = {0, -1}; + const uint64_t amount_paid = 10000; + const rct::RCTConfig rct_config { rct::RangeProofPaddedBulletproof, 3 }; + bool use_view_tags = false; + bool valid = false; + return generate_with_full(events, out_idx, mixin, amount_paid, CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE, HF_VERSION_VIEW_TAGS, rct_config, use_view_tags, valid, NULL, NULL); +} + +bool gen_rct_tx_pre_rct_has_view_tag_before_hf_view_tags::generate(std::vector& events) const +{ + const int mixin = 2; + const int out_idx[] = {0, -1}; + const uint64_t amount_paid = 10000; + bool use_view_tags = true; + bool valid = false; + return generate_with_full(events, out_idx, mixin, amount_paid, 0, 0, {}, use_view_tags, valid, NULL, NULL); +} + +bool gen_rct_tx_pre_rct_has_view_tag_from_hf_view_tags::generate(std::vector& events) const +{ + const int mixin = 10; + const int out_idx[] = {0, -1}; + const uint64_t amount_paid = 10000; + const rct::RCTConfig rct_config { rct::RangeProofPaddedBulletproof, 3 }; + bool use_view_tags = true; + bool valid = true; + return generate_with_full(events, out_idx, mixin, amount_paid, CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE, HF_VERSION_VIEW_TAGS, rct_config, use_view_tags, valid, NULL, NULL); +} + +bool gen_rct_tx_rct_has_no_view_tag_before_hf_view_tags::generate(std::vector& events) const +{ + const int mixin = 2; + const int out_idx[] = {1, -1}; + const uint64_t amount_paid = 10000; + const rct::RCTConfig rct_config { rct::RangeProofBorromean, 0 }; + bool use_view_tags = false; + bool valid = true; + return generate_with_full(events, out_idx, mixin, amount_paid, 0, 0, rct_config, use_view_tags, valid, NULL, NULL); +} + +bool gen_rct_tx_rct_has_no_view_tag_from_hf_view_tags::generate(std::vector& events) const +{ + const int mixin = 10; + const int out_idx[] = {1, -1}; + const uint64_t amount_paid = 10000; + const rct::RCTConfig rct_config { rct::RangeProofPaddedBulletproof, 3 }; + bool use_view_tags = false; + bool valid = false; + return generate_with_full(events, out_idx, mixin, amount_paid, CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE, HF_VERSION_VIEW_TAGS+1, rct_config, use_view_tags, valid, NULL, NULL); +} + +bool gen_rct_tx_rct_has_view_tag_before_hf_view_tags::generate(std::vector& events) const +{ + const int mixin = 2; + const int out_idx[] = {1, -1}; + const uint64_t amount_paid = 10000; + const rct::RCTConfig rct_config { rct::RangeProofBorromean, 0 }; + bool use_view_tags = true; + bool valid = false; + return generate_with_full(events, out_idx, mixin, amount_paid, 0, 0, rct_config, use_view_tags, valid, NULL, NULL); +} + +bool gen_rct_tx_rct_has_view_tag_from_hf_view_tags::generate(std::vector& events) const +{ + const int mixin = 10; + const int out_idx[] = {1, -1}; + const uint64_t amount_paid = 10000; + const rct::RCTConfig rct_config { rct::RangeProofPaddedBulletproof, 3 }; + bool use_view_tags = true; + bool valid = true; + return generate_with_full(events, out_idx, mixin, amount_paid, CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE, HF_VERSION_VIEW_TAGS, rct_config, use_view_tags, valid, NULL, NULL); +} + bool gen_rct_tx_uses_output_too_early::generate(std::vector& events) const { const int mixin = 10; const int out_idx[] = {1, -1}; const uint64_t amount_paid = 10000; const rct::RCTConfig rct_config { rct::RangeProofPaddedBulletproof, 2 }; - return generate_with_full(events, out_idx, mixin, amount_paid, CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE-3, HF_VERSION_ENFORCE_MIN_AGE, rct_config, false, NULL, NULL); + bool use_view_tags = false; + bool valid = false; + return generate_with_full(events, out_idx, mixin, amount_paid, CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE-3, HF_VERSION_ENFORCE_MIN_AGE, rct_config, use_view_tags, valid, NULL, NULL); } diff --git a/tests/core_tests/rct.h b/tests/core_tests/rct.h index dee074e4c..c20e8a580 100644 --- a/tests/core_tests/rct.h +++ b/tests/core_tests/rct.h @@ -70,7 +70,7 @@ struct gen_rct_tx_validation_base : public test_chain_unit_base } bool generate_with_full(std::vector& events, const int *out_idx, int mixin, - uint64_t amount_paid, size_t second_rewind, uint8_t last_version, const rct::RCTConfig &rct_config, bool valid, + uint64_t amount_paid, size_t second_rewind, uint8_t last_version, const rct::RCTConfig &rct_config, bool use_view_tags, bool valid, const std::function &sources, std::vector &destinations)> &pre_tx, const std::function &post_tx) const; bool generate_with(std::vector& events, const int *out_idx, int mixin, @@ -266,6 +266,74 @@ struct gen_rct_tx_rct_altered_extra : public gen_rct_tx_validation_base }; template<> struct get_test_options: public get_test_options {}; +struct gen_rct_tx_pre_rct_has_no_view_tag_before_hf_view_tags : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_rct_tx_pre_rct_has_no_view_tag_from_hf_view_tags : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options { + const std::pair hard_forks[5] = {std::make_pair(1, 0), std::make_pair(2, 1), std::make_pair(4, 65), std::make_pair(HF_VERSION_VIEW_TAGS, 69), std::make_pair(0, 0)}; + const cryptonote::test_options test_options = { + hard_forks, 0 + }; +}; + +struct gen_rct_tx_pre_rct_has_view_tag_before_hf_view_tags : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_rct_tx_pre_rct_has_view_tag_from_hf_view_tags : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options { + const std::pair hard_forks[5] = {std::make_pair(1, 0), std::make_pair(2, 1), std::make_pair(4, 65), std::make_pair(HF_VERSION_VIEW_TAGS, 69), std::make_pair(0, 0)}; + const cryptonote::test_options test_options = { + hard_forks, 0 + }; +}; + +struct gen_rct_tx_rct_has_no_view_tag_before_hf_view_tags : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_rct_tx_rct_has_no_view_tag_from_hf_view_tags : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options { + const std::pair hard_forks[5] = {std::make_pair(1, 0), std::make_pair(2, 1), std::make_pair(4, 65), std::make_pair(HF_VERSION_VIEW_TAGS+1, 69), std::make_pair(0, 0)}; + const cryptonote::test_options test_options = { + hard_forks, 0 + }; +}; + +struct gen_rct_tx_rct_has_view_tag_before_hf_view_tags : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_rct_tx_rct_has_view_tag_from_hf_view_tags : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options { + const std::pair hard_forks[5] = {std::make_pair(1, 0), std::make_pair(2, 1), std::make_pair(4, 65), std::make_pair(HF_VERSION_VIEW_TAGS, 69), std::make_pair(0, 0)}; + const cryptonote::test_options test_options = { + hard_forks, 0 + }; +}; + struct gen_rct_tx_uses_output_too_early : public gen_rct_tx_validation_base { bool generate(std::vector& events) const; diff --git a/tests/crypto/main.cpp b/tests/crypto/main.cpp index 59a2a7d77..045ffc08d 100644 --- a/tests/crypto/main.cpp +++ b/tests/crypto/main.cpp @@ -260,7 +260,6 @@ int main(int argc, char *argv[]) { goto error; } } else if (cmd == "check_ge_p3_identity") { - cerr << "Testing: " << cmd << endl; public_key point; bool expected_bad, expected_good, result_badfunc, result_goodfunc; get(input, point, expected_bad, expected_good); @@ -269,6 +268,15 @@ int main(int argc, char *argv[]) { if (expected_bad != result_badfunc || expected_good != result_goodfunc) { goto error; } + } else if (cmd == "derive_view_tag") { + key_derivation derivation; + size_t output_index; + view_tag expected, actual; + get(input, derivation, output_index, expected); + derive_view_tag(derivation, output_index, actual); + if (expected != actual) { + goto error; + } } else { throw ios_base::failure("Unknown function: " + cmd); } diff --git a/tests/crypto/tests.txt b/tests/crypto/tests.txt index 3a7fe5453..d387aa09d 100644 --- a/tests/crypto/tests.txt +++ b/tests/crypto/tests.txt @@ -5473,3 +5473,59 @@ check_ge_p3_identity 046e1450f147f3ade34d149973913cc75d4e7b9669eb1ed61da0f1d4a0b check_ge_p3_identity ca8a2f621cfc7aa3efcd7ddf55dce5352e757b38aca0869b050c0a27824e5c5e true true check_ge_p3_identity 64a247eef6087d86e1e9fa048a3c181fdb1728431f29ba738634bdc38f02a859 true true check_ge_p3_identity cff0c7170a41395b0658ee42b76545c45360736b973ab2f31f6f227b9415df67 true true +derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 0 76 +derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 1 d6 +derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 2 87 +derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 3 1b +derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 12 d6 +derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 13 e9 +derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 14 12 +derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 15 26 +derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 0 70 +derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 1 81 +derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 2 a0 +derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 3 ec +derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 12 22 +derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 13 0a +derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 14 87 +derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 15 76 +derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 0 93 +derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 1 67 +derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 2 9d +derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 3 2d +derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 12 63 +derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 13 cf +derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 14 ef +derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 15 10 +derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 0 90 +derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 1 5a +derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 2 de +derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 3 21 +derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 12 57 +derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 13 52 +derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 14 6f +derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 15 eb +derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 0 c6 +derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 1 60 +derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 2 f0 +derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 3 71 +derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 12 0e +derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 13 42 +derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 14 b2 +derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 15 61 +derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 0 4c +derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 1 9b +derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 2 64 +derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 3 ff +derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 12 e3 +derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 13 24 +derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 14 ea +derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 15 3b +derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 0 74 +derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 1 77 +derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 2 a9 +derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 3 44 +derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 12 75 +derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 13 05 +derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 14 ca +derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 15 00 diff --git a/tests/performance_tests/CMakeLists.txt b/tests/performance_tests/CMakeLists.txt index 03b124981..c079afe3a 100644 --- a/tests/performance_tests/CMakeLists.txt +++ b/tests/performance_tests/CMakeLists.txt @@ -43,6 +43,7 @@ set(performance_tests_headers generate_keypair.h signature.h is_out_to_acc.h + out_can_be_to_acc.h subaddress_expand.h range_proof.h bulletproof.h diff --git a/tests/performance_tests/derive_view_tag.h b/tests/performance_tests/derive_view_tag.h new file mode 100644 index 000000000..ee4efb16d --- /dev/null +++ b/tests/performance_tests/derive_view_tag.h @@ -0,0 +1,62 @@ +// Copyright (c) 2014-2021, 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 "crypto/crypto.h" +#include "cryptonote_basic/cryptonote_basic.h" + +#include "single_tx_test_base.h" + +class test_derive_view_tag : public single_tx_test_base +{ +public: + static const size_t loop_count = 10000; + + bool init() + { + if (!single_tx_test_base::init()) + return false; + + crypto::generate_key_derivation(m_tx_pub_key, m_bob.get_keys().m_view_secret_key, m_key_derivation); + + return true; + } + + bool test() + { + crypto::view_tag view_tag; + crypto::derive_view_tag(m_key_derivation, 0, view_tag); + return true; + } + +private: + crypto::key_derivation m_key_derivation; +}; diff --git a/tests/performance_tests/is_out_to_acc.h b/tests/performance_tests/is_out_to_acc.h index d1204ee68..75d4a5c46 100644 --- a/tests/performance_tests/is_out_to_acc.h +++ b/tests/performance_tests/is_out_to_acc.h @@ -43,8 +43,9 @@ public: bool test() { - const cryptonote::txout_to_key& tx_out = boost::get(m_tx.vout[0].target); - return cryptonote::is_out_to_acc(m_bob.get_keys(), tx_out, m_tx_pub_key, m_additional_tx_pub_keys, 0); + crypto::public_key output_public_key; + cryptonote::get_output_public_key(m_tx.vout[0], output_public_key); + return cryptonote::is_out_to_acc(m_bob.get_keys(), output_public_key, m_tx_pub_key, m_additional_tx_pub_keys, 0); } }; diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index 8bd1008cb..71c736977 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -42,6 +42,7 @@ #include "cn_slow_hash.h" #include "derive_public_key.h" #include "derive_secret_key.h" +#include "derive_view_tag.h" #include "ge_frombytes_vartime.h" #include "ge_tobytes.h" #include "generate_key_derivation.h" @@ -50,6 +51,7 @@ #include "generate_keypair.h" #include "signature.h" #include "is_out_to_acc.h" +#include "out_can_be_to_acc.h" #include "subaddress_expand.h" #include "sc_reduce32.h" #include "sc_check.h" @@ -194,6 +196,9 @@ int main(int argc, char** argv) TEST_PERFORMANCE0(filter, p, test_is_out_to_acc); TEST_PERFORMANCE0(filter, p, test_is_out_to_acc_precomp); + TEST_PERFORMANCE2(filter, p, test_out_can_be_to_acc, false, true); // no view tag, owned + TEST_PERFORMANCE2(filter, p, test_out_can_be_to_acc, true, false); // use view tag, not owned + TEST_PERFORMANCE2(filter, p, test_out_can_be_to_acc, true, true); // use view tag, owned TEST_PERFORMANCE0(filter, p, test_generate_key_image_helper); TEST_PERFORMANCE0(filter, p, test_generate_key_derivation); TEST_PERFORMANCE0(filter, p, test_generate_key_image); @@ -206,6 +211,7 @@ int main(int argc, char** argv) TEST_PERFORMANCE0(filter, p, test_sc_check); TEST_PERFORMANCE1(filter, p, test_signature, false); TEST_PERFORMANCE1(filter, p, test_signature, true); + TEST_PERFORMANCE0(filter, p, test_derive_view_tag); TEST_PERFORMANCE2(filter, p, test_wallet2_expand_subaddresses, 50, 200); diff --git a/tests/performance_tests/out_can_be_to_acc.h b/tests/performance_tests/out_can_be_to_acc.h new file mode 100644 index 000000000..86e236b7b --- /dev/null +++ b/tests/performance_tests/out_can_be_to_acc.h @@ -0,0 +1,103 @@ +// Copyright (c) 2014-2021, 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 "crypto/crypto.h" +#include "cryptonote_basic/cryptonote_basic.h" + +#include "single_tx_test_base.h" + +using namespace crypto; + +// use_view_tags: whether to enable view tag checking +// is_owned: whether the output is owned by us +template +class test_out_can_be_to_acc : public single_tx_test_base +{ + public: + static const size_t loop_count = 1000; + + bool init() + { + if (!single_tx_test_base::init()) + return false; + + crypto::key_derivation key_derivation; + crypto::view_tag vt; + + m_output_index = 0; + m_view_secret_key = m_bob.get_keys().m_view_secret_key; + m_spend_public_key = m_bob.get_keys().m_account_address.m_spend_public_key; + + cryptonote::get_output_public_key(m_tx.vout[m_output_index], m_output_public_key); + + if (use_view_tags) + { + crypto::generate_key_derivation(m_tx_pub_key, m_view_secret_key, key_derivation); + crypto::derive_view_tag(key_derivation, m_output_index, vt); + m_view_tag_opt = vt; + } + else + m_view_tag_opt = boost::optional(); + + return true; + } + + bool test() + { + // include key derivation to demonstrate performance improvement when using view tags + crypto::key_derivation key_derivation; + crypto::generate_key_derivation(m_tx_pub_key, m_view_secret_key, key_derivation); + + // if using view tags, this ensures we computed the view tag properly + if (!cryptonote::out_can_be_to_acc(m_view_tag_opt, key_derivation, m_output_index)) + return false; + + // if user owns output, this tests the output public key matches the derived + if (is_owned) + { + crypto::public_key output_public_key; + crypto::derive_public_key(key_derivation, m_output_index, m_spend_public_key, output_public_key); + + if (m_output_public_key != output_public_key) + return false; + } + + return true; + } + + private: + size_t m_output_index; + crypto::secret_key m_view_secret_key; + crypto::public_key m_spend_public_key; + crypto::public_key m_output_public_key; + boost::optional m_view_tag_opt; +}; -- cgit v1.2.3