From f900390fcbebb11bd4c4070a1177625c0d54c3fe Mon Sep 17 00:00:00 2001 From: andrew Date: Tue, 21 May 2019 17:39:13 +0300 Subject: [PATCH 1/4] Added check against auth sample at tx_memory_pool::validate_rta_tx, GNRTA-283 --- .../stake_transaction_processor.cpp | 15 +++++++++ .../stake_transaction_processor.h | 2 ++ src/cryptonote_core/tx_pool.cpp | 31 +++++++++++++++++++ src/cryptonote_core/tx_pool.h | 1 + 4 files changed, 49 insertions(+) diff --git a/src/cryptonote_core/stake_transaction_processor.cpp b/src/cryptonote_core/stake_transaction_processor.cpp index 75ac555ee..cecd0a4e6 100644 --- a/src/cryptonote_core/stake_transaction_processor.cpp +++ b/src/cryptonote_core/stake_transaction_processor.cpp @@ -614,3 +614,18 @@ void StakeTransactionProcessor::invoke_update_blockchain_based_list_handler(bool invoke_update_blockchain_based_list_handler_impl(depth); } + +bool StakeTransactionProcessor::get_auth_sample_keys(const uint64_t auth_sample_height, + const std::string& payment_id, std::vector& auth_sample_keys) const +{ + const size_t depth = m_blockchain_based_list->block_height() - auth_sample_height; + auto& tiers = m_blockchain_based_list->tiers(depth); + auto bbl_idxs = makeBBLindexes(tiers); + + std::vector bbqs_idxs; + graft::generator::select_AuthSample(payment_id, bbl_idxs, bbqs_idxs); + auth_sample_keys = fromIndexes(tiers, bbqs_idxs); + + return (auth_sample_keys.size() == graft::generator::AUTH_SAMPLE_SIZE); +} + diff --git a/src/cryptonote_core/stake_transaction_processor.h b/src/cryptonote_core/stake_transaction_processor.h index aa020bc69..a01fb8000 100644 --- a/src/cryptonote_core/stake_transaction_processor.h +++ b/src/cryptonote_core/stake_transaction_processor.h @@ -44,6 +44,8 @@ class StakeTransactionProcessor /// Force invoke update handler for blockchain based list void invoke_update_blockchain_based_list_handler(bool force = true, size_t depth = 1); + bool get_auth_sample_keys(uint64_t auth_sample_height, const std::string& payment_id, std::vector& auth_sample_keys) const; + private: void init_storages_impl(); void process_block(uint64_t block_index, const block& block, const crypto::hash& block_hash, bool update_storage = true); diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 8a103109b..76ec1faf0 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -47,6 +47,7 @@ #include "crypto/hash.h" #include "stake_transaction_processor.h" #include "graft_rta_config.h" +#include "utils/sample_generator.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "txpool" @@ -1116,6 +1117,10 @@ namespace cryptonote } } #endif + + if(!belongs_to_auth_sample(rta_hdr)) + return false; + for (const crypto::public_key &key : rta_hdr.keys) { result &= validate_supernode(rta_hdr.auth_sample_height, key); if (!result) { @@ -1132,4 +1137,30 @@ namespace cryptonote supernode_stake * stake = const_cast(m_stp->find_supernode_stake(height, epee::string_tools::pod_to_hex(id))); return stake ? stake->amount >= config::graft::TIER1_STAKE_AMOUNT : false; }; + + bool tx_memory_pool::belongs_to_auth_sample(const rta_header& rta_hdr) const + { + bool ok = false; // ok is true when every supernode from rta_hdr is member of auth_sample + std::vector auth_sample_keys; + if(ok = (m_stp->get_auth_sample_keys(rta_hdr.auth_sample_height, rta_hdr.payment_id, auth_sample_keys))) + { + for(const auto& kr : rta_hdr.keys) + { + bool found = false; + for(const auto& kas : auth_sample_keys) + if(found = (kr == kas)) + break; + if(!(ok = found)) + { + MERROR("Key " << kr << " does not belong to auth-sample"); + break; + } + } + } + else + MERROR("Obtaining of auth sample keys is failed"); + + return ok; + } } + diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 7b06e2bc5..c1784b7ce 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -466,6 +466,7 @@ namespace cryptonote bool validate_rta_tx(const crypto::hash &txid, const std::vector &rta_signs, const cryptonote::rta_header &rta_hdr) const; bool validate_supernode(uint64_t height, const crypto::public_key &id) const; + bool belongs_to_auth_sample(const rta_header& rta_hdr) const; //TODO: confirm the below comments and investigate whether or not this // is the desired behavior From c52eab595c2601431f94a9b4d5aeed46cac6f107 Mon Sep 17 00:00:00 2001 From: andrew Date: Wed, 22 May 2019 13:46:55 +0300 Subject: [PATCH 2/4] Applied bug-fix in selectSample func plus minor changes --- src/cryptonote_core/tx_pool.cpp | 28 ++++++++++++++++++---------- src/utils/sample_generator.h | 5 ----- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 76ec1faf0..11fde81d1 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -47,7 +47,6 @@ #include "crypto/hash.h" #include "stake_transaction_processor.h" #include "graft_rta_config.h" -#include "utils/sample_generator.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "txpool" @@ -1141,18 +1140,14 @@ namespace cryptonote bool tx_memory_pool::belongs_to_auth_sample(const rta_header& rta_hdr) const { bool ok = false; // ok is true when every supernode from rta_hdr is member of auth_sample - std::vector auth_sample_keys; - if(ok = (m_stp->get_auth_sample_keys(rta_hdr.auth_sample_height, rta_hdr.payment_id, auth_sample_keys))) + std::vector ask; // auth sample keys + if(ok = (m_stp->get_auth_sample_keys(rta_hdr.auth_sample_height, rta_hdr.payment_id, ask))) { - for(const auto& kr : rta_hdr.keys) + for(const auto& key : rta_hdr.keys) { - bool found = false; - for(const auto& kas : auth_sample_keys) - if(found = (kr == kas)) - break; - if(!(ok = found)) + if(!(ok = std::any_of(ask.cbegin(), ask.cend(), [&key](const crypto::public_key& k) { return key == k; }))) { - MERROR("Key " << kr << " does not belong to auth-sample"); + MERROR("Key " << key << " does not belong to auth-sample"); break; } } @@ -1160,6 +1155,19 @@ namespace cryptonote else MERROR("Obtaining of auth sample keys is failed"); +#if 0 + { + std::ostringstream m; + m << std::endl << "### DBG: keys to be checked against belonging to auth sample (cnt:" + << rta_hdr.keys.size() << "):"; + + for(const auto& k : rta_hdr.keys) m << std::endl << k; + m << std::endl << "### DBG: keys of auth sample (cnt:" << ask.size() << "):"; + for(const auto& k : ask) m << std::endl << k; + MDEBUG(m.str()); + } +#endif + return ok; } } diff --git a/src/utils/sample_generator.h b/src/utils/sample_generator.h index a05de71b7..a1a59ebcd 100644 --- a/src/utils/sample_generator.h +++ b/src/utils/sample_generator.h @@ -134,11 +134,6 @@ bool selectSample(size_t sample_size, const Tiers& bbl_tiers, std::vector& ou auto& dst = tier_supernodes[i]; dst.reserve(sample_size); uniform_select(do_not_seed{}, sample_size, src, dst); - if (dst.size() != sample_size) - { - LOG_ERROR("unable to select supernodes for " << prefix << " sample"); - return false; - } MDEBUG("..." << dst.size() << " supernodes has been selected for tier " << (i + 1) << " from blockchain based list with " << src.size() << " supernodes"); } From 3fdebd9ad56a8259d4bd80e6d2bbb12035a7fd7b Mon Sep 17 00:00:00 2001 From: andrew Date: Fri, 24 May 2019 18:18:52 +0300 Subject: [PATCH 3/4] Added signature rta tx validation --- .gitignore | 3 +- src/cryptonote_core/tx_pool.cpp | 114 +++++++++++++- src/cryptonote_core/tx_pool.h | 8 +- tests/supernode_tests/CMakeLists.txt | 2 + tests/supernode_tests/rta_tx_validation.cpp | 161 ++++++++++++++++++++ 5 files changed, 282 insertions(+), 6 deletions(-) create mode 100644 tests/supernode_tests/rta_tx_validation.cpp diff --git a/.gitignore b/.gitignore index a27982af1..95042d303 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ log/ # vim swap files *.swp *.swo +.ycm* TAGS !TAGS/ tags @@ -103,4 +104,4 @@ local.properties .texlipse .idea/ -/testnet \ No newline at end of file +/testnet diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 11fde81d1..b695ed3a8 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -48,6 +48,10 @@ #include "stake_transaction_processor.h" #include "graft_rta_config.h" +#include "utils/sample_generator.h" // bad idea to include the whole heavy header just +// because of one const - graft::generator::AUTH_SAMPLE_SIZE +// It's better to hold all const's in separate lightweight header + #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "txpool" @@ -1085,16 +1089,116 @@ namespace cryptonote return true; } + + //--------------------------------------------------------------------------------- + + uint32_t get_rta_hdr_supernode_public_key_offset(const rta_header& rta_hdr) + { + return (rta_hdr.keys.size() == 8) ? 0 : 3; // supernode public keys offset + } + + bool check_rta_sign_count(const std::vector& rta_signs, const crypto::hash& txid) + { + const uint32_t cnt = rta_signs.size(); + // according to spec/design there can be 6-8 signatures and we do check it in here + const bool ok = !((cnt < 6) || (cnt > graft::generator::AUTH_SAMPLE_SIZE)); + if(!ok) + MERROR("Wrong amount of signatures:" << cnt << " for tx:" << txid << " It should be 6-" + << graft::generator::AUTH_SAMPLE_SIZE << "."); + return ok; + } + + bool check_rta_keys_count(const rta_header& rta_hdr, const crypto::hash& txid) + { + const uint32_t cnt = rta_hdr.keys.size(); + + // so far there can be cases when we have only graft::generator::AUTH_SAMPLE_SIZE + // records (3 starting are missing) + const bool ok = (cnt == graft::generator::AUTH_SAMPLE_SIZE) + || (cnt == (3 + graft::generator::AUTH_SAMPLE_SIZE)); + + if(!ok) + MERROR("Failed to validate rta tx, wrong amount (" + << cnt << ") of auth sample keys for tx:" << txid << ". Expected " + << (3 + graft::generator::AUTH_SAMPLE_SIZE)); + + return ok; + } + + bool check_rta_sign_key_indexes(const std::vector& rta_signs, + const crypto::hash& txid, const uint32_t sn_pkeys_off) + { + bool ok = true; + for(const auto& rs : rta_signs) + { + if((rs.key_index < sn_pkeys_off) + || (rs.key_index > (sn_pkeys_off + graft::generator::AUTH_SAMPLE_SIZE - 1))) + { + MERROR("Signature: " << rs.signature << " has wrong key index: " + << rs.key_index << ", tx: " << txid); + ok = false; + break; + } + } + return ok; + } + + bool check_rta_signatures(const std::vector& rta_signs, + const rta_header& rta_hdr, const crypto::hash& txid, const uint32_t sn_pkeys_off) + { + bool ok = true; + for(const auto& rs : rta_signs) + { + const int32_t idx = rs.key_index + sn_pkeys_off; + + if(idx > (int32_t)(rta_hdr.keys.size() - 1)) + { + MERROR("Fail at check_rta_signatures - out of index!"); + return false; + } + + const auto& key = rta_hdr.keys[idx]; + if(!crypto::check_signature(txid, key, rs.signature)) + { + MERROR("Failed to validate rta tx " << std::endl + << "signature: " << epee::string_tools::pod_to_hex(rs.signature) << std::endl + << "for key: " << key << std::endl + << "tx-id: " << epee::string_tools::pod_to_hex(txid)); + ok = false; + break; + } + } + return ok; + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::validate_rta_tx(const crypto::hash &txid, const std::vector &rta_signs, const rta_header &rta_hdr) const { bool result = true; + if(!check_rta_sign_count(rta_signs, txid)) + return false; + + if(!check_rta_keys_count(rta_hdr, txid)) + return false; + + const auto sn_pkeys_off = get_rta_hdr_supernode_public_key_offset(rta_hdr); + + if(!check_rta_sign_key_indexes(rta_signs, txid, sn_pkeys_off)) + return false; + + if(!check_rta_signatures(rta_signs, rta_hdr, txid, sn_pkeys_off)) + return false; + +#if 0 + if (rta_hdr.keys.size() == 0) { MERROR("Failed to validate rta tx, missing auth sample keys for tx: " << txid ); return false; } -#if 0 // don't validate signatures for rta mining + + // don't validate signatures for rta mining if (rta_hdr.keys.size() != rta_signs.size()) { MERROR("Failed to validate rta tx: " << txid << ", keys.size() != signatures.size()"); return false; @@ -1117,7 +1221,7 @@ namespace cryptonote } #endif - if(!belongs_to_auth_sample(rta_hdr)) + if(!belongs_to_auth_sample(rta_hdr, sn_pkeys_off)) return false; for (const crypto::public_key &key : rta_hdr.keys) { @@ -1137,14 +1241,15 @@ namespace cryptonote return stake ? stake->amount >= config::graft::TIER1_STAKE_AMOUNT : false; }; - bool tx_memory_pool::belongs_to_auth_sample(const rta_header& rta_hdr) const + bool tx_memory_pool::belongs_to_auth_sample(const rta_header& rta_hdr, const uint32_t sn_pkeys_off) const { bool ok = false; // ok is true when every supernode from rta_hdr is member of auth_sample std::vector ask; // auth sample keys if(ok = (m_stp->get_auth_sample_keys(rta_hdr.auth_sample_height, rta_hdr.payment_id, ask))) { - for(const auto& key : rta_hdr.keys) + for(uint32_t i = sn_pkeys_off, cnt = rta_hdr.keys.size(); i < cnt; ++i) { + const auto& key = rta_hdr.keys[i]; if(!(ok = std::any_of(ask.cbegin(), ask.cend(), [&key](const crypto::public_key& k) { return key == k; }))) { MERROR("Key " << key << " does not belong to auth-sample"); @@ -1170,5 +1275,6 @@ namespace cryptonote return ok; } + } diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index c1784b7ce..4c292bee2 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -466,7 +466,7 @@ namespace cryptonote bool validate_rta_tx(const crypto::hash &txid, const std::vector &rta_signs, const cryptonote::rta_header &rta_hdr) const; bool validate_supernode(uint64_t height, const crypto::public_key &id) const; - bool belongs_to_auth_sample(const rta_header& rta_hdr) const; + bool belongs_to_auth_sample(const rta_header& rta_hdr, uint32_t sn_pkeys_off) const; //TODO: confirm the below comments and investigate whether or not this // is the desired behavior @@ -516,6 +516,12 @@ namespace cryptonote Blockchain& m_blockchain; //!< reference to the Blockchain object StakeTransactionProcessor * m_stp = nullptr; }; + + bool check_rta_sign_count(const std::vector& rta_signs, const crypto::hash& txid); + bool check_rta_keys_count(const rta_header& rta_hdr, const crypto::hash& txid); + bool check_rta_sign_key_indexes(const std::vector& rta_signs, const crypto::hash& txid, uint32_t sn_pkeys_off); + bool check_rta_signatures(const std::vector& rta_signs, const rta_header& rta_hdr, const crypto::hash& txid, uint32_t sn_pkeys_off); + uint32_t get_rta_hdr_supernode_public_key_offset(const rta_header& rta_hdr); } namespace boost diff --git a/tests/supernode_tests/CMakeLists.txt b/tests/supernode_tests/CMakeLists.txt index e531bf78f..e96630edb 100644 --- a/tests/supernode_tests/CMakeLists.txt +++ b/tests/supernode_tests/CMakeLists.txt @@ -36,6 +36,7 @@ set(supernode_tests_sources walletproxy_test.cpp graft_wallet_tests.cpp graft_splitted_tx_test.cpp + rta_tx_validation.cpp ) set(supernode_tests_headers @@ -74,3 +75,4 @@ endif () add_test( NAME supernode_tests COMMAND supernode_tests) + diff --git a/tests/supernode_tests/rta_tx_validation.cpp b/tests/supernode_tests/rta_tx_validation.cpp new file mode 100644 index 000000000..75b7c16ac --- /dev/null +++ b/tests/supernode_tests/rta_tx_validation.cpp @@ -0,0 +1,161 @@ +// Copyright (c) 2017, The Graft 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) 2014-2017 The Monero Project + +#include "gtest/gtest.h" + +#include "cryptonote_core/tx_pool.h" +#include "cryptonote_basic/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_basic.h" + +#include + +#include +using u32 = std::uint32_t; +using i32 = std::int32_t; + +using cryptonote::rta_header; +using cryptonote::rta_signature; + +crypto::hash get_txid(void) +{ + const std::string str_txid = "199ea366ca190788e12e7849c9625dd7cad33d2b926de10294005fc20cd41576"; + crypto::hash txid; + epee::string_tools::hex_to_pod(str_txid, txid); + return txid; +} + +TEST(RtaTxValidationFlow2, check_rta_sign_count) +{ + std::vector rs; + for(u32 i = 0, cnt = 5; i < cnt; ++i) + rs.emplace_back(rta_signature()); + + ASSERT_FALSE(cryptonote::check_rta_sign_count(rs, get_txid())); + + rs.emplace_back(rta_signature()); + ASSERT_TRUE(cryptonote::check_rta_sign_count(rs, get_txid())); + + rs.emplace_back(rta_signature()); + ASSERT_TRUE(cryptonote::check_rta_sign_count(rs, get_txid())); + + rs.emplace_back(rta_signature()); + ASSERT_TRUE(cryptonote::check_rta_sign_count(rs, get_txid())); + + rs.emplace_back(rta_signature()); + ASSERT_FALSE(cryptonote::check_rta_sign_count(rs, get_txid())); +} + +TEST(RtaTxValidationFlow2, calc_offset_to_supernodes_pub_keys_in_rta_hdr) +{ + rta_header rh; + for(u32 i = 0, cnt = 8; i < cnt; ++i) + rh.keys.emplace_back(crypto::public_key()); + + ASSERT_EQ(get_rta_hdr_supernode_public_key_offset(rh), 0); + + for(u32 i = 0, cnt = 3; i < cnt; ++i) + rh.keys.emplace_back(crypto::public_key()); + + ASSERT_EQ(get_rta_hdr_supernode_public_key_offset(rh), 3); +} + +TEST(RtaTxValidationFlow2, check_rta_keys_count) +{ + rta_header rh; + for(u32 i = 0, cnt = 8; i < cnt; ++i) + { + const u32 key_cnt = rh.keys.size(); + if((key_cnt == 8) || (key_cnt == (3 + 8))) + ASSERT_TRUE(check_rta_keys_count(rh, get_txid())); + else + ASSERT_FALSE(check_rta_keys_count(rh, get_txid())); + } +} + +TEST(RtaTxValidationFlow2, check_rta_sign_key_indexes) +{ + std::vector rs; + rs.emplace_back(rta_signature()); + auto& sign = rs.back(); + + sign.key_index = 0; + ASSERT_TRUE(cryptonote::check_rta_sign_key_indexes(rs, get_txid(), 0)); + ASSERT_FALSE(cryptonote::check_rta_sign_key_indexes(rs, get_txid(), 1)); + + sign.key_index = 3; + ASSERT_TRUE(cryptonote::check_rta_sign_key_indexes(rs, get_txid(), 3)); + + sign.key_index = 7; + ASSERT_TRUE(cryptonote::check_rta_sign_key_indexes(rs, get_txid(), 3)); + + sign.key_index = 10; + ASSERT_TRUE(cryptonote::check_rta_sign_key_indexes(rs, get_txid(), 3)); + + sign.key_index = 11; + ASSERT_FALSE(cryptonote::check_rta_sign_key_indexes(rs, get_txid(), 3)); +} + +TEST(RtaTxValidationFlow2, check_rta_signatures) +{ + std::vector rs; + rta_header rh; + + auto sn_pkeys_off = get_rta_hdr_supernode_public_key_offset(rh); + ASSERT_EQ(sn_pkeys_off, 3); + + // nothing to check - then ok + ASSERT_TRUE(cryptonote::check_rta_signatures(rs, rh, get_txid(), sn_pkeys_off)); + + // create case 'out of index' error + rs.emplace_back(rta_signature()); + ASSERT_FALSE(cryptonote::check_rta_signatures(rs, rh, get_txid(), sn_pkeys_off)); + + sn_pkeys_off = 0; + + crypto::hash txid; + epee::string_tools::hex_to_pod("57fd3427123988a99aae02ce20312b61a88a39692f3462769947467c6e4c3961", txid); + + rh.keys.emplace_back(crypto::public_key()); + auto& pkey = rh.keys.back(); + epee::string_tools::hex_to_pod("a5e61831eb296ad2b18e4b4b00ec0ff160e30b2834f8d1eda4f28d9656a2ec75", pkey); + + auto& sign = rs.back(); + sign.key_index = 0; + epee::string_tools::hex_to_pod("cd89c4cbb1697ebc641e77fdcd843ff9b2feaf37cfeee078045ef1bb8f0efe0b", sign.signature.c); + epee::string_tools::hex_to_pod("b5fd0131fbc314121d9c19e046aea55140165441941906a757e574b8b775c008", sign.signature.r); + ASSERT_TRUE(cryptonote::check_rta_signatures(rs, rh, txid, sn_pkeys_off)); + + epee::string_tools::hex_to_pod("92c1259cddde43602eeac1ab825dc12ffc915c9cfe57abcca04c8405df338359", txid); + epee::string_tools::hex_to_pod("9fa6c7fd338517c7d45b3693fbc91d4a28cd8cc226c4217f3e2694ae89a6f3dc", pkey); + epee::string_tools::hex_to_pod("b027582f0d05bacb3ebe4e5f12a8a9d65e987cc1e99b759dca3fee84289efa51", sign.signature.c); + epee::string_tools::hex_to_pod("24ad37550b985ed4f2db0ab6f44d2ebbc195a7123fd39441d3a57e0f70ecf608", sign.signature.r); + ASSERT_FALSE(cryptonote::check_rta_signatures(rs, rh, txid, sn_pkeys_off)); +} + From 3f74c2b38a20f1a867d06f42498be7ebcbe40235 Mon Sep 17 00:00:00 2001 From: andrew Date: Wed, 29 May 2019 16:43:03 +0300 Subject: [PATCH 4/4] Fixed PR-review issues --- src/common/CMakeLists.txt | 8 +- src/common/rta_kit.cpp | 160 ++++++++++++++++++++ src/common/rta_kit.h | 56 +++++++ src/cryptonote_core/tx_pool.cpp | 144 ++---------------- src/cryptonote_core/tx_pool.h | 7 - tests/supernode_tests/rta_tx_validation.cpp | 75 ++++++--- 6 files changed, 292 insertions(+), 158 deletions(-) create mode 100644 src/common/rta_kit.cpp create mode 100644 src/common/rta_kit.h diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 55b8ad3e6..1c8a14251 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -39,7 +39,9 @@ set(common_sources perf_timer.cpp task_region.cpp thread_group.cpp - updates.cpp) + updates.cpp + rta_kit.cpp + ) if (STACK_TRACE) list(APPEND common_sources stack_trace.cpp) @@ -68,7 +70,9 @@ set(common_private_headers stack_trace.h task_region.h thread_group.h - updates.h) + updates.h + rta_kit.h + ) monero_private_headers(common ${common_private_headers}) diff --git a/src/common/rta_kit.cpp b/src/common/rta_kit.cpp new file mode 100644 index 000000000..8a40fa7f2 --- /dev/null +++ b/src/common/rta_kit.cpp @@ -0,0 +1,160 @@ +// Copyright (c) 2019, The Graft 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) 2014-2019 The Monero Project + +#include + +#include "common/rta_kit.h" +#include "crypto/hash.h" +#include "crypto/crypto.h" +#include "misc_log_ex.h" +#include "cryptonote_basic/cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_basic_impl.h" + +#include "utils/sample_generator.h" // bad idea to include the whole heavy header just +// because of one const - graft::generator::AUTH_SAMPLE_SIZE +// It's better to hold all const's in separate lightweight header + +namespace rta::flow2::validation { + +bool belongs_to_auth_sample(const std::vector& auth_sample_pkeys, + const rta_header& rta_hdr, const u32 auth_sample_pkeys_off) +{ + bool ok = false; // ok is true when every supernode from rta_hdr is member of auth_sample + for(u32 i = auth_sample_pkeys_off, cnt = rta_hdr.keys.size(); i < cnt; ++i) + { + const auto& key = rta_hdr.keys[i]; + if(!(ok = std::any_of(auth_sample_pkeys.cbegin(), auth_sample_pkeys.cend(), + [&key](const crypto::public_key& k) { return key == k; }))) + { + MERROR("Key " << key << " does not belong to auth-sample"); + break; + } + } + +#if 0 + { + std::ostringstream m; + m << std::endl << "### DBG: keys to be checked against belonging to auth sample (cnt:" + << (rta_hdr.keys.size() - auth_sample_pkeys_off) << "):"; + + for(u32 i = auth_sample_pkeys_off, cnt = rta_hdr.keys.size(); i < cnt; ++i) + m << std::endl << rta_hdr.keys[i]; + + m << std::endl << "### DBG: keys of auth sample (cnt:" << auth_sample_pkeys.size() << "):"; + for(const auto& k : auth_sample_pkeys) m << std::endl << k; + MDEBUG(m.str()); + } +#endif + + return ok; +} + +bool check_rta_signatures(const std::vector& rta_signs, + const rta_header& rta_hdr, const crypto::hash& txid, const u32 auth_sample_pkeys_off) +{ + bool ok = true; + for(const auto& rs : rta_signs) + { + const i32 idx = rs.key_index + auth_sample_pkeys_off; + + if(idx > (i32)(rta_hdr.keys.size() - 1)) + { + MERROR("Fail at check_rta_signatures - out of index!"); + return false; + } + + const auto& key = rta_hdr.keys[idx]; + if(!crypto::check_signature(txid, key, rs.signature)) + { + MERROR("Failed to validate rta tx " << std::endl + << "signature: " << epee::string_tools::pod_to_hex(rs.signature) << std::endl + << "for key: " << key << std::endl + << "tx-id: " << epee::string_tools::pod_to_hex(txid)); + ok = false; + break; + } + } + return ok; +} + +bool check_rta_keys_count(const rta_header& rta_hdr, const crypto::hash& txid) +{ + const uint32_t cnt = rta_hdr.keys.size(); + + // so far there can be cases when we have only graft::generator::AUTH_SAMPLE_SIZE + // records (3 starting are missing) + const bool ok = (cnt == graft::generator::AUTH_SAMPLE_SIZE) + || (cnt == (3 + graft::generator::AUTH_SAMPLE_SIZE)); + + if(!ok) + MERROR("Failed to validate rta tx, wrong amount (" + << cnt << ") of auth sample keys for tx:" << txid << ". Expected " + << (3 + graft::generator::AUTH_SAMPLE_SIZE)); + + return ok; +} + +bool check_rta_sign_key_indexes(const std::vector& rta_signs, + const crypto::hash& txid, const u32 auth_sample_pkeys_off) +{ + bool ok = true; + for(const auto& rs : rta_signs) + { + if((rs.key_index < auth_sample_pkeys_off) + || (rs.key_index > (auth_sample_pkeys_off + graft::generator::AUTH_SAMPLE_SIZE - 1))) + { + MERROR("Signature: " << rs.signature << " has wrong key index: " + << rs.key_index << ", tx: " << txid); + ok = false; + break; + } + } + return ok; +} + +bool check_rta_sign_count(const std::vector& rta_signs, const crypto::hash& txid) +{ + const uint32_t cnt = rta_signs.size(); + // according to spec/design there can be 6-8 signatures and we do check it in here + const bool ok = !((cnt < 6) || (cnt > graft::generator::AUTH_SAMPLE_SIZE)); + if(!ok) + MERROR("Wrong amount of signatures:" << cnt << " for tx:" << txid << " It should be 6-" + << graft::generator::AUTH_SAMPLE_SIZE << "."); + return ok; +} + +u32 get_auth_sample_public_key_offset(const rta_header& rta_hdr) +{ + return (rta_hdr.keys.size() == 8) ? 0 : 3; // supernode public keys offset +} + +} + + diff --git a/src/common/rta_kit.h b/src/common/rta_kit.h new file mode 100644 index 000000000..acc9729bf --- /dev/null +++ b/src/common/rta_kit.h @@ -0,0 +1,56 @@ +// Copyright (c) 2019, The Graft 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) 2014-2019 The Monero Project + +#include // for std::uint32_t and such +#include + +namespace cryptonote { struct rta_signature; struct rta_header; } +namespace crypto { struct hash; struct public_key; } + +namespace rta::flow2::validation { + +using i32 = std::int32_t; +using u32 = std::uint32_t; + +using cryptonote::rta_signature; +using cryptonote::rta_header; + +bool belongs_to_auth_sample(const std::vector& auth_sample_pkeys, + const rta_header& rta_hdr, u32 auth_sample_pkeys_off); + +bool check_rta_signatures(const std::vector& rta_signs, const rta_header& rta_hdr, const crypto::hash& txid, u32 auth_sample_pkeys_off); + +bool check_rta_sign_count(const std::vector& rta_signs, const crypto::hash& txid); +bool check_rta_keys_count(const rta_header& rta_hdr, const crypto::hash& txid); +bool check_rta_sign_key_indexes(const std::vector& rta_signs, const crypto::hash& txid, u32 auth_sample_pkeys_off); +u32 get_auth_sample_public_key_offset(const rta_header& rta_hdr); + +} + diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index b695ed3a8..2f5d8658c 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -41,6 +41,7 @@ #include "blockchain_db/blockchain_db.h" #include "common/boost_serialization_helper.h" #include "common/int-util.h" +#include "common/rta_kit.h" #include "misc_language.h" #include "warnings.h" #include "common/perf_timer.h" @@ -48,10 +49,6 @@ #include "stake_transaction_processor.h" #include "graft_rta_config.h" -#include "utils/sample_generator.h" // bad idea to include the whole heavy header just -// because of one const - graft::generator::AUTH_SAMPLE_SIZE -// It's better to hold all const's in separate lightweight header - #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "txpool" @@ -1088,107 +1085,25 @@ namespace cryptonote { return true; } - - - //--------------------------------------------------------------------------------- - - uint32_t get_rta_hdr_supernode_public_key_offset(const rta_header& rta_hdr) - { - return (rta_hdr.keys.size() == 8) ? 0 : 3; // supernode public keys offset - } - - bool check_rta_sign_count(const std::vector& rta_signs, const crypto::hash& txid) - { - const uint32_t cnt = rta_signs.size(); - // according to spec/design there can be 6-8 signatures and we do check it in here - const bool ok = !((cnt < 6) || (cnt > graft::generator::AUTH_SAMPLE_SIZE)); - if(!ok) - MERROR("Wrong amount of signatures:" << cnt << " for tx:" << txid << " It should be 6-" - << graft::generator::AUTH_SAMPLE_SIZE << "."); - return ok; - } - - bool check_rta_keys_count(const rta_header& rta_hdr, const crypto::hash& txid) - { - const uint32_t cnt = rta_hdr.keys.size(); - - // so far there can be cases when we have only graft::generator::AUTH_SAMPLE_SIZE - // records (3 starting are missing) - const bool ok = (cnt == graft::generator::AUTH_SAMPLE_SIZE) - || (cnt == (3 + graft::generator::AUTH_SAMPLE_SIZE)); - - if(!ok) - MERROR("Failed to validate rta tx, wrong amount (" - << cnt << ") of auth sample keys for tx:" << txid << ". Expected " - << (3 + graft::generator::AUTH_SAMPLE_SIZE)); - - return ok; - } - - bool check_rta_sign_key_indexes(const std::vector& rta_signs, - const crypto::hash& txid, const uint32_t sn_pkeys_off) - { - bool ok = true; - for(const auto& rs : rta_signs) - { - if((rs.key_index < sn_pkeys_off) - || (rs.key_index > (sn_pkeys_off + graft::generator::AUTH_SAMPLE_SIZE - 1))) - { - MERROR("Signature: " << rs.signature << " has wrong key index: " - << rs.key_index << ", tx: " << txid); - ok = false; - break; - } - } - return ok; - } - - bool check_rta_signatures(const std::vector& rta_signs, - const rta_header& rta_hdr, const crypto::hash& txid, const uint32_t sn_pkeys_off) - { - bool ok = true; - for(const auto& rs : rta_signs) - { - const int32_t idx = rs.key_index + sn_pkeys_off; - - if(idx > (int32_t)(rta_hdr.keys.size() - 1)) - { - MERROR("Fail at check_rta_signatures - out of index!"); - return false; - } - - const auto& key = rta_hdr.keys[idx]; - if(!crypto::check_signature(txid, key, rs.signature)) - { - MERROR("Failed to validate rta tx " << std::endl - << "signature: " << epee::string_tools::pod_to_hex(rs.signature) << std::endl - << "for key: " << key << std::endl - << "tx-id: " << epee::string_tools::pod_to_hex(txid)); - ok = false; - break; - } - } - return ok; - } - //--------------------------------------------------------------------------------- bool tx_memory_pool::validate_rta_tx(const crypto::hash &txid, const std::vector &rta_signs, const rta_header &rta_hdr) const { bool result = true; - if(!check_rta_sign_count(rta_signs, txid)) + if(!rta::flow2::validation::check_rta_sign_count(rta_signs, txid)) return false; - if(!check_rta_keys_count(rta_hdr, txid)) + if(!rta::flow2::validation::check_rta_keys_count(rta_hdr, txid)) return false; - const auto sn_pkeys_off = get_rta_hdr_supernode_public_key_offset(rta_hdr); + const auto auth_sample_pkeys_off = + rta::flow2::validation::get_auth_sample_public_key_offset(rta_hdr); - if(!check_rta_sign_key_indexes(rta_signs, txid, sn_pkeys_off)) + if(!rta::flow2::validation::check_rta_sign_key_indexes(rta_signs, txid, auth_sample_pkeys_off)) return false; - if(!check_rta_signatures(rta_signs, rta_hdr, txid, sn_pkeys_off)) + if(!rta::flow2::validation::check_rta_signatures(rta_signs, rta_hdr, txid, auth_sample_pkeys_off)) return false; #if 0 @@ -1221,7 +1136,14 @@ namespace cryptonote } #endif - if(!belongs_to_auth_sample(rta_hdr, sn_pkeys_off)) + std::vector ask; // auth sample keys + if(!m_stp->get_auth_sample_keys(rta_hdr.auth_sample_height, rta_hdr.payment_id, ask)) + { + MERROR("Obtaining of auth sample keys is failed"); + return false; + } + + if(!rta::flow2::validation::belongs_to_auth_sample(ask, rta_hdr, auth_sample_pkeys_off)) return false; for (const crypto::public_key &key : rta_hdr.keys) { @@ -1240,41 +1162,5 @@ namespace cryptonote supernode_stake * stake = const_cast(m_stp->find_supernode_stake(height, epee::string_tools::pod_to_hex(id))); return stake ? stake->amount >= config::graft::TIER1_STAKE_AMOUNT : false; }; - - bool tx_memory_pool::belongs_to_auth_sample(const rta_header& rta_hdr, const uint32_t sn_pkeys_off) const - { - bool ok = false; // ok is true when every supernode from rta_hdr is member of auth_sample - std::vector ask; // auth sample keys - if(ok = (m_stp->get_auth_sample_keys(rta_hdr.auth_sample_height, rta_hdr.payment_id, ask))) - { - for(uint32_t i = sn_pkeys_off, cnt = rta_hdr.keys.size(); i < cnt; ++i) - { - const auto& key = rta_hdr.keys[i]; - if(!(ok = std::any_of(ask.cbegin(), ask.cend(), [&key](const crypto::public_key& k) { return key == k; }))) - { - MERROR("Key " << key << " does not belong to auth-sample"); - break; - } - } - } - else - MERROR("Obtaining of auth sample keys is failed"); - -#if 0 - { - std::ostringstream m; - m << std::endl << "### DBG: keys to be checked against belonging to auth sample (cnt:" - << rta_hdr.keys.size() << "):"; - - for(const auto& k : rta_hdr.keys) m << std::endl << k; - m << std::endl << "### DBG: keys of auth sample (cnt:" << ask.size() << "):"; - for(const auto& k : ask) m << std::endl << k; - MDEBUG(m.str()); - } -#endif - - return ok; - } - } diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 4c292bee2..7b06e2bc5 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -466,7 +466,6 @@ namespace cryptonote bool validate_rta_tx(const crypto::hash &txid, const std::vector &rta_signs, const cryptonote::rta_header &rta_hdr) const; bool validate_supernode(uint64_t height, const crypto::public_key &id) const; - bool belongs_to_auth_sample(const rta_header& rta_hdr, uint32_t sn_pkeys_off) const; //TODO: confirm the below comments and investigate whether or not this // is the desired behavior @@ -516,12 +515,6 @@ namespace cryptonote Blockchain& m_blockchain; //!< reference to the Blockchain object StakeTransactionProcessor * m_stp = nullptr; }; - - bool check_rta_sign_count(const std::vector& rta_signs, const crypto::hash& txid); - bool check_rta_keys_count(const rta_header& rta_hdr, const crypto::hash& txid); - bool check_rta_sign_key_indexes(const std::vector& rta_signs, const crypto::hash& txid, uint32_t sn_pkeys_off); - bool check_rta_signatures(const std::vector& rta_signs, const rta_header& rta_hdr, const crypto::hash& txid, uint32_t sn_pkeys_off); - uint32_t get_rta_hdr_supernode_public_key_offset(const rta_header& rta_hdr); } namespace boost diff --git a/tests/supernode_tests/rta_tx_validation.cpp b/tests/supernode_tests/rta_tx_validation.cpp index 75b7c16ac..be81a5748 100644 --- a/tests/supernode_tests/rta_tx_validation.cpp +++ b/tests/supernode_tests/rta_tx_validation.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017, The Graft Project +// Copyright (c) 2019, The Graft Project // // All rights reserved. // @@ -26,13 +26,14 @@ // 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) 2014-2017 The Monero Project +// Parts of this file are originally copyright (c) 2014-2019 The Monero Project #include "gtest/gtest.h" #include "cryptonote_core/tx_pool.h" #include "cryptonote_basic/cryptonote_format_utils.h" #include "cryptonote_basic/cryptonote_basic.h" +#include "common/rta_kit.h" #include @@ -51,25 +52,27 @@ crypto::hash get_txid(void) return txid; } +using namespace rta::flow2::validation; + TEST(RtaTxValidationFlow2, check_rta_sign_count) { std::vector rs; for(u32 i = 0, cnt = 5; i < cnt; ++i) rs.emplace_back(rta_signature()); - ASSERT_FALSE(cryptonote::check_rta_sign_count(rs, get_txid())); + ASSERT_FALSE(check_rta_sign_count(rs, get_txid())); rs.emplace_back(rta_signature()); - ASSERT_TRUE(cryptonote::check_rta_sign_count(rs, get_txid())); + ASSERT_TRUE(check_rta_sign_count(rs, get_txid())); rs.emplace_back(rta_signature()); - ASSERT_TRUE(cryptonote::check_rta_sign_count(rs, get_txid())); + ASSERT_TRUE(check_rta_sign_count(rs, get_txid())); rs.emplace_back(rta_signature()); - ASSERT_TRUE(cryptonote::check_rta_sign_count(rs, get_txid())); + ASSERT_TRUE(check_rta_sign_count(rs, get_txid())); rs.emplace_back(rta_signature()); - ASSERT_FALSE(cryptonote::check_rta_sign_count(rs, get_txid())); + ASSERT_FALSE(check_rta_sign_count(rs, get_txid())); } TEST(RtaTxValidationFlow2, calc_offset_to_supernodes_pub_keys_in_rta_hdr) @@ -78,12 +81,12 @@ TEST(RtaTxValidationFlow2, calc_offset_to_supernodes_pub_keys_in_rta_hdr) for(u32 i = 0, cnt = 8; i < cnt; ++i) rh.keys.emplace_back(crypto::public_key()); - ASSERT_EQ(get_rta_hdr_supernode_public_key_offset(rh), 0); + ASSERT_EQ(get_auth_sample_public_key_offset(rh), 0); for(u32 i = 0, cnt = 3; i < cnt; ++i) rh.keys.emplace_back(crypto::public_key()); - ASSERT_EQ(get_rta_hdr_supernode_public_key_offset(rh), 3); + ASSERT_EQ(get_auth_sample_public_key_offset(rh), 3); } TEST(RtaTxValidationFlow2, check_rta_keys_count) @@ -106,20 +109,20 @@ TEST(RtaTxValidationFlow2, check_rta_sign_key_indexes) auto& sign = rs.back(); sign.key_index = 0; - ASSERT_TRUE(cryptonote::check_rta_sign_key_indexes(rs, get_txid(), 0)); - ASSERT_FALSE(cryptonote::check_rta_sign_key_indexes(rs, get_txid(), 1)); + ASSERT_TRUE(check_rta_sign_key_indexes(rs, get_txid(), 0)); + ASSERT_FALSE(check_rta_sign_key_indexes(rs, get_txid(), 1)); sign.key_index = 3; - ASSERT_TRUE(cryptonote::check_rta_sign_key_indexes(rs, get_txid(), 3)); + ASSERT_TRUE(check_rta_sign_key_indexes(rs, get_txid(), 3)); sign.key_index = 7; - ASSERT_TRUE(cryptonote::check_rta_sign_key_indexes(rs, get_txid(), 3)); + ASSERT_TRUE(check_rta_sign_key_indexes(rs, get_txid(), 3)); sign.key_index = 10; - ASSERT_TRUE(cryptonote::check_rta_sign_key_indexes(rs, get_txid(), 3)); + ASSERT_TRUE(check_rta_sign_key_indexes(rs, get_txid(), 3)); sign.key_index = 11; - ASSERT_FALSE(cryptonote::check_rta_sign_key_indexes(rs, get_txid(), 3)); + ASSERT_FALSE(check_rta_sign_key_indexes(rs, get_txid(), 3)); } TEST(RtaTxValidationFlow2, check_rta_signatures) @@ -127,15 +130,15 @@ TEST(RtaTxValidationFlow2, check_rta_signatures) std::vector rs; rta_header rh; - auto sn_pkeys_off = get_rta_hdr_supernode_public_key_offset(rh); + auto sn_pkeys_off = get_auth_sample_public_key_offset(rh); ASSERT_EQ(sn_pkeys_off, 3); // nothing to check - then ok - ASSERT_TRUE(cryptonote::check_rta_signatures(rs, rh, get_txid(), sn_pkeys_off)); + ASSERT_TRUE(check_rta_signatures(rs, rh, get_txid(), sn_pkeys_off)); // create case 'out of index' error rs.emplace_back(rta_signature()); - ASSERT_FALSE(cryptonote::check_rta_signatures(rs, rh, get_txid(), sn_pkeys_off)); + ASSERT_FALSE(check_rta_signatures(rs, rh, get_txid(), sn_pkeys_off)); sn_pkeys_off = 0; @@ -150,12 +153,44 @@ TEST(RtaTxValidationFlow2, check_rta_signatures) sign.key_index = 0; epee::string_tools::hex_to_pod("cd89c4cbb1697ebc641e77fdcd843ff9b2feaf37cfeee078045ef1bb8f0efe0b", sign.signature.c); epee::string_tools::hex_to_pod("b5fd0131fbc314121d9c19e046aea55140165441941906a757e574b8b775c008", sign.signature.r); - ASSERT_TRUE(cryptonote::check_rta_signatures(rs, rh, txid, sn_pkeys_off)); + ASSERT_TRUE(check_rta_signatures(rs, rh, txid, sn_pkeys_off)); epee::string_tools::hex_to_pod("92c1259cddde43602eeac1ab825dc12ffc915c9cfe57abcca04c8405df338359", txid); epee::string_tools::hex_to_pod("9fa6c7fd338517c7d45b3693fbc91d4a28cd8cc226c4217f3e2694ae89a6f3dc", pkey); epee::string_tools::hex_to_pod("b027582f0d05bacb3ebe4e5f12a8a9d65e987cc1e99b759dca3fee84289efa51", sign.signature.c); epee::string_tools::hex_to_pod("24ad37550b985ed4f2db0ab6f44d2ebbc195a7123fd39441d3a57e0f70ecf608", sign.signature.r); - ASSERT_FALSE(cryptonote::check_rta_signatures(rs, rh, txid, sn_pkeys_off)); + ASSERT_FALSE(check_rta_signatures(rs, rh, txid, sn_pkeys_off)); +} + +TEST(RtaTxValidationFlow2, belongs_to_auth_sample) +{ + static const std::vector auth_sample_pkeys = + { + "9fa6c7fd338517c7d45b3693fbc91d4a28cd8cc226c4217f3e2694ae89a6f3dc" + , "9fa6c7fd338517c7d45b3693fbc91d4a28cd8cc226c4217f3e2694ae89a6f301" + , "9fa6c7fd338517c7d45b3693fbc91d4a28cd8cc226c4217f3e2694ae89a6f302" + , "9fa6c7fd338517c7d45b3693fbc91d4a28cd8cc226c4217f3e2694ae89a6f303" + , "9fa6c7fd338517c7d45b3693fbc91d4a28cd8cc226c4217f3e2694ae89a6f304" + , "9fa6c7fd338517c7d45b3693fbc91d4a28cd8cc226c4217f3e2694ae89a6f305" + , "9fa6c7fd338517c7d45b3693fbc91d4a28cd8cc226c4217f3e2694ae89a6f306" + , "9fa6c7fd338517c7d45b3693fbc91d4a28cd8cc226c4217f3e2694ae89a6f307" + }; + + std::vector ask; + for(const auto& k : auth_sample_pkeys) + { + crypto::public_key pk; + epee::string_tools::hex_to_pod(k, pk); + ask.emplace_back(pk); + } + + rta_header rh; + rh.keys = ask; + + const auto auth_sample_pkeys_off = get_auth_sample_public_key_offset(rh); + ASSERT_TRUE(belongs_to_auth_sample(ask, rh, auth_sample_pkeys_off)); + + epee::string_tools::hex_to_pod("9fa6c7fd338517c7d45b3693fbc91d4a28cd8cc226c4217f3e2694ae89a60000", rh.keys[7]); + ASSERT_FALSE(belongs_to_auth_sample(ask, rh, auth_sample_pkeys_off)); }