diff options
Diffstat (limited to 'tests')
30 files changed, 2088 insertions, 131 deletions
diff --git a/tests/core_proxy/core_proxy.h b/tests/core_proxy/core_proxy.h index 8732c85cc..aad1bc962 100644 --- a/tests/core_proxy/core_proxy.h +++ b/tests/core_proxy/core_proxy.h @@ -73,7 +73,6 @@ namespace tests bool init(const boost::program_options::variables_map& vm); bool deinit(){return true;} bool get_short_chain_history(std::list<crypto::hash>& ids); - bool get_stat_info(cryptonote::core_stat_info& st_inf){return true;} bool have_block(const crypto::hash& id); void get_blockchain_top(uint64_t& height, crypto::hash& top_id); bool handle_incoming_tx(const cryptonote::tx_blob_entry& tx_blob, cryptonote::tx_verification_context& tvc, cryptonote::relay_method tx_relay, bool relayed); @@ -110,5 +109,7 @@ namespace tests bool pad_transactions() const { return false; } uint32_t get_blockchain_pruning_seed() const { return 0; } bool prune_blockchain(uint32_t pruning_seed) const { return true; } + bool get_txpool_complement(const std::vector<crypto::hash> &hashes, std::vector<cryptonote::blobdata> &txes) { return false; } + bool get_pool_transaction_hashes(std::vector<crypto::hash>& txs, bool include_unrelayed_txes = true) const { return false; } }; } diff --git a/tests/core_tests/bulletproofs.cpp b/tests/core_tests/bulletproofs.cpp index fd3f5114b..0a5b98605 100644 --- a/tests/core_tests/bulletproofs.cpp +++ b/tests/core_tests/bulletproofs.cpp @@ -64,7 +64,6 @@ bool gen_bp_tx_validation_base::generate_with(std::vector<test_event_entry>& eve false, "Failed to generate block"); events.push_back(blocks[n]); prev_block = blocks + n; - LOG_PRINT_L0("Initial miner tx " << n << ": " << obj_to_json_str(blocks[n].miner_tx)); } // rewind diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index 80ce7404b..453ee923d 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -116,7 +116,8 @@ struct event_visitor_settings { set_txs_keeped_by_block = 1 << 0, set_txs_do_not_relay = 1 << 1, - set_local_relay = 1 << 2 + set_local_relay = 1 << 2, + set_txs_stem = 1 << 3 }; event_visitor_settings(int a_mask = 0) @@ -548,6 +549,10 @@ public: { m_tx_relay = cryptonote::relay_method::none; } + else if (settings.mask & event_visitor_settings::set_txs_stem) + { + m_tx_relay = cryptonote::relay_method::stem; + } else { m_tx_relay = cryptonote::relay_method::fluff; diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index 23f3170b8..8a313140c 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -161,6 +161,8 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY(txpool_spend_key_all); GENERATE_AND_PLAY(txpool_double_spend_norelay); GENERATE_AND_PLAY(txpool_double_spend_local); + GENERATE_AND_PLAY(txpool_double_spend_keyimage); + GENERATE_AND_PLAY(txpool_stem_loop); // Double spend GENERATE_AND_PLAY(gen_double_spend_in_tx<false>); diff --git a/tests/core_tests/multisig.cpp b/tests/core_tests/multisig.cpp index e0c90423d..ba6e2d270 100644 --- a/tests/core_tests/multisig.cpp +++ b/tests/core_tests/multisig.cpp @@ -180,8 +180,6 @@ bool gen_multisig_tx_validation_base::generate_with(std::vector<test_event_entry false, "Failed to generate block"); events.push_back(blocks[n]); prev_block = blocks + n; - LOG_PRINT_L0("Initial miner tx " << n << ": " << obj_to_json_str(blocks[n].miner_tx)); - LOG_PRINT_L0("in block: " << obj_to_json_str(blocks[n])); } // rewind diff --git a/tests/core_tests/rct.cpp b/tests/core_tests/rct.cpp index dc77e408f..23638f62d 100644 --- a/tests/core_tests/rct.cpp +++ b/tests/core_tests/rct.cpp @@ -63,7 +63,6 @@ bool gen_rct_tx_validation_base::generate_with_full(std::vector<test_event_entry false, "Failed to generate block"); events.push_back(blocks[n]); prev_block = blocks + n; - LOG_PRINT_L0("Initial miner tx " << n << ": " << obj_to_json_str(blocks[n].miner_tx)); } // rewind diff --git a/tests/core_tests/tx_pool.cpp b/tests/core_tests/tx_pool.cpp index 537015dca..b60dd00fc 100644 --- a/tests/core_tests/tx_pool.cpp +++ b/tests/core_tests/tx_pool.cpp @@ -125,10 +125,12 @@ txpool_double_spend_base::txpool_double_spend_base() , m_no_relay_hashes() , m_all_hashes() , m_no_new_index(0) + , m_failed_index(0) , m_new_timestamp_index(0) , m_last_tx(crypto::hash{}) { REGISTER_CALLBACK_METHOD(txpool_double_spend_base, mark_no_new); + REGISTER_CALLBACK_METHOD(txpool_double_spend_base, mark_failed); REGISTER_CALLBACK_METHOD(txpool_double_spend_base, mark_timestamp_change); REGISTER_CALLBACK_METHOD(txpool_double_spend_base, timestamp_change_pause); REGISTER_CALLBACK_METHOD(txpool_double_spend_base, check_unchanged); @@ -143,6 +145,12 @@ bool txpool_double_spend_base::mark_no_new(cryptonote::core& /*c*/, size_t ev_in return true; } +bool txpool_double_spend_base::mark_failed(cryptonote::core& /*c*/, size_t ev_index, const std::vector<test_event_entry>& /*events*/) +{ + m_failed_index = ev_index + 1; + return true; +} + bool txpool_double_spend_base::mark_timestamp_change(cryptonote::core& /*c*/, size_t ev_index, const std::vector<test_event_entry>& /*events*/) { m_new_timestamp_index = ev_index + 1; @@ -483,6 +491,8 @@ bool txpool_double_spend_base::check_tx_verification_context(const cryptonote::t m_last_tx = cryptonote::get_transaction_hash(tx); if (m_no_new_index == event_idx) return !tvc.m_verifivation_failed && !tx_added; + else if (m_failed_index == event_idx) + return tvc.m_verifivation_failed;// && !tx_added; else return !tvc.m_verifivation_failed && tx_added; } @@ -542,7 +552,6 @@ bool txpool_double_spend_local::generate(std::vector<test_event_entry>& events) DO_CALLBACK(events, "mark_no_new"); events.push_back(tx_0); DO_CALLBACK(events, "check_txpool_spent_keys"); - DO_CALLBACK(events, "mark_timestamp_change"); DO_CALLBACK(events, "check_unchanged"); SET_EVENT_VISITOR_SETT(events, 0); DO_CALLBACK(events, "timestamp_change_pause"); @@ -559,3 +568,73 @@ bool txpool_double_spend_local::generate(std::vector<test_event_entry>& events) return true; } +bool txpool_double_spend_keyimage::generate(std::vector<test_event_entry>& events) const +{ + INIT_MEMPOOL_TEST(); + + DO_CALLBACK(events, "check_txpool_spent_keys"); + SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_local_relay); + DO_CALLBACK(events, "mark_no_new"); + + const std::size_t tx_index1 = events.size(); + MAKE_TX(events, tx_0, miner_account, bob_account, send_amount, blk_0); + + SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_txs_stem); + DO_CALLBACK(events, "increase_all_tx_count"); + DO_CALLBACK(events, "check_txpool_spent_keys"); + DO_CALLBACK(events, "mark_timestamp_change"); + DO_CALLBACK(events, "check_new_hidden"); + DO_CALLBACK(events, "timestamp_change_pause"); + DO_CALLBACK(events, "mark_no_new"); + const std::size_t tx_index2 = events.size(); + events.push_back(tx_0); + DO_CALLBACK(events, "check_txpool_spent_keys"); + DO_CALLBACK(events, "mark_timestamp_change"); + DO_CALLBACK(events, "check_unchanged"); + + // use same key image with different id + cryptonote::transaction tx_1; + { + auto events_copy = events; + events_copy.erase(events_copy.begin() + tx_index1); + events_copy.erase(events_copy.begin() + tx_index2 - 1); + MAKE_TX(events_copy, tx_temp, miner_account, bob_account, send_amount, blk_0); + tx_1 = tx_temp; + } + + // same key image + DO_CALLBACK(events, "timestamp_change_pause"); + DO_CALLBACK(events, "mark_failed"); + events.push_back(tx_1); + DO_CALLBACK(events, "check_unchanged"); + + return true; +} + +bool txpool_stem_loop::generate(std::vector<test_event_entry>& events) const +{ + INIT_MEMPOOL_TEST(); + + DO_CALLBACK(events, "check_txpool_spent_keys"); + SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_txs_stem); + DO_CALLBACK(events, "mark_no_new"); + + MAKE_TX(events, tx_0, miner_account, bob_account, send_amount, blk_0); + + DO_CALLBACK(events, "increase_all_tx_count"); + DO_CALLBACK(events, "check_txpool_spent_keys"); + DO_CALLBACK(events, "mark_timestamp_change"); + DO_CALLBACK(events, "check_new_hidden"); + DO_CALLBACK(events, "timestamp_change_pause"); + events.push_back(tx_0); + DO_CALLBACK(events, "increase_broadcasted_tx_count"); + DO_CALLBACK(events, "check_txpool_spent_keys"); + DO_CALLBACK(events, "mark_timestamp_change"); + DO_CALLBACK(events, "check_new_broadcasted"); + DO_CALLBACK(events, "timestamp_change_pause"); + DO_CALLBACK(events, "mark_no_new"); + events.push_back(tx_0); + DO_CALLBACK(events, "check_unchanged"); + + return true; +} diff --git a/tests/core_tests/tx_pool.h b/tests/core_tests/tx_pool.h index 996c76698..8a27e98f0 100644 --- a/tests/core_tests/tx_pool.h +++ b/tests/core_tests/tx_pool.h @@ -77,6 +77,7 @@ class txpool_double_spend_base : public txpool_base std::unordered_set<crypto::hash> m_no_relay_hashes; std::unordered_map<crypto::hash, uint64_t> m_all_hashes; size_t m_no_new_index; + size_t m_failed_index; size_t m_new_timestamp_index; crypto::hash m_last_tx; @@ -86,6 +87,7 @@ public: txpool_double_spend_base(); bool mark_no_new(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events); + bool mark_failed(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events); bool mark_timestamp_change(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events); //! Pause for 1 second, so that `receive_time` for tx meta changes (tx hidden from public rpc being updated) @@ -116,3 +118,21 @@ struct txpool_double_spend_local : txpool_double_spend_base bool generate(std::vector<test_event_entry>& events) const; }; + +struct txpool_double_spend_keyimage : txpool_double_spend_base +{ + txpool_double_spend_keyimage() + : txpool_double_spend_base() + {} + + bool generate(std::vector<test_event_entry>& events) const; +}; + +struct txpool_stem_loop : txpool_double_spend_base +{ + txpool_stem_loop() + : txpool_double_spend_base() + {} + + bool generate(std::vector<test_event_entry>& events) const; +}; diff --git a/tests/core_tests/v2_tests.cpp b/tests/core_tests/v2_tests.cpp index 8c2b51acf..d00d9b6f4 100644 --- a/tests/core_tests/v2_tests.cpp +++ b/tests/core_tests/v2_tests.cpp @@ -85,7 +85,6 @@ bool gen_v2_tx_validation_base::generate_with(std::vector<test_event_entry>& eve tx_source_entry& src = sources.back(); src.amount = blocks[0].miner_tx.vout[out_idx[out_idx_idx]].amount; - std::cout << "using " << print_money(src.amount) << " output at index " << out_idx[out_idx_idx] << std::endl; for (int m = 0; m <= mixin; ++m) { int idx; if (is_valid_decomposed_amount(src.amount)) diff --git a/tests/functional_tests/blockchain.py b/tests/functional_tests/blockchain.py index 78e0d8952..b8f8bac1a 100755 --- a/tests/functional_tests/blockchain.py +++ b/tests/functional_tests/blockchain.py @@ -203,10 +203,15 @@ class BlockchainTest(): res_sum = daemon.get_coinbase_tx_sum(i, 1) res_header = daemon.getblockheaderbyheight(i) assert res_sum.emission_amount == res_header.block_header.reward + assert res_sum.emission_amount_top64 == 0 + assert res_sum.emission_amount == int(res_sum.wide_emission_amount, 16) + assert res_sum.fee_amount == int(res_sum.wide_fee_amount, 16) res = daemon.get_coinbase_tx_sum(0, 1) assert res.emission_amount == 17592186044415 + assert res.emission_amount_top64 == 0 assert res.fee_amount == 0 + assert res.fee_amount_top64 == 0 sum_blocks = height + nblocks - 1 res = daemon.get_coinbase_tx_sum(0, sum_blocks) extrapolated = 17592186044415 + 17592186044415 * 2 * (sum_blocks - 1) diff --git a/tests/functional_tests/functional_tests_rpc.py b/tests/functional_tests/functional_tests_rpc.py index 5f2a3d077..42d14e91a 100755 --- a/tests/functional_tests/functional_tests_rpc.py +++ b/tests/functional_tests/functional_tests_rpc.py @@ -34,8 +34,8 @@ try: except: tests = DEFAULT_TESTS -N_MONERODS = 2 -N_WALLETS = 4 +N_MONERODS = 3 +N_WALLETS = 7 WALLET_DIRECTORY = builddir + "/functional-tests-directory" DIFFICULTY = 10 @@ -43,9 +43,17 @@ monerod_base = [builddir + "/bin/monerod", "--regtest", "--fixed-difficulty", st monerod_extra = [ [], ["--rpc-payment-address", "44SKxxLQw929wRF6BA9paQ1EWFshNnKhXM3qz6Mo3JGDE2YG3xyzVutMStEicxbQGRfrYvAAYxH6Fe8rnD56EaNwUiqhcwR", "--rpc-payment-difficulty", str(DIFFICULTY), "--rpc-payment-credits", "5000", "--data-dir", builddir + "/functional-tests-directory/monerod1"], + ["--rpc-restricted-bind-port", "18482", "--data-dir", builddir + "/functional-tests-directory/monerod2"] ] -wallet_base = [builddir + "/bin/monero-wallet-rpc", "--wallet-dir", WALLET_DIRECTORY, "--rpc-bind-port", "wallet_port", "--disable-rpc-login", "--rpc-ssl", "disabled", "--daemon-ssl", "disabled", "--daemon-port", "18180", "--log-level", "1"] +wallet_base = [builddir + "/bin/monero-wallet-rpc", "--wallet-dir", WALLET_DIRECTORY, "--rpc-bind-port", "wallet_port", "--disable-rpc-login", "--rpc-ssl", "disabled", "--daemon-ssl", "disabled", "--log-level", "1"] wallet_extra = [ + ["--daemon-port", "18180"], + ["--daemon-port", "18180"], + ["--daemon-port", "18180"], + ["--daemon-port", "18180"], + ["--daemon-port", "18182"], + ["--daemon-port", "18182"], + ["--daemon-port", "18182"] ] command_lines = [] diff --git a/tests/functional_tests/make_test_signature.cc b/tests/functional_tests/make_test_signature.cc index 789523de5..f31816841 100644 --- a/tests/functional_tests/make_test_signature.cc +++ b/tests/functional_tests/make_test_signature.cc @@ -27,15 +27,16 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <stdio.h> +#include "misc_language.h" #include "string_tools.h" #include "rpc/rpc_payment_signature.h" int main(int argc, const char **argv) { TRY_ENTRY(); - if (argc > 2) + if (argc > 3) { - fprintf(stderr, "usage: %s <secret_key>\n", argv[0]); + fprintf(stderr, "usage: %s [<secret_key> [N]]\n", argv[0]); return 1; } @@ -55,8 +56,23 @@ int main(int argc, const char **argv) fprintf(stderr, "invalid secret key\n"); return 1; } - std::string signature = cryptonote::make_rpc_payment_signature(skey); - printf("%s\n", signature.c_str()); + uint32_t count = 1; + if (argc == 3) + { + int i = atoi(argv[2]); + if (i <= 0) + { + fprintf(stderr, "invalid count\n"); + return 1; + } + count = (uint32_t)i; + } + while (count--) + { + std::string signature = cryptonote::make_rpc_payment_signature(skey); + epee::misc_utils::sleep_no_w(1); + printf("%s\n", signature.c_str()); + } return 0; CATCH_ENTRY_L0("main()", 1); } diff --git a/tests/functional_tests/rpc_payment.py b/tests/functional_tests/rpc_payment.py index 4c777ccd7..5f23c2022 100755 --- a/tests/functional_tests/rpc_payment.py +++ b/tests/functional_tests/rpc_payment.py @@ -31,6 +31,7 @@ from __future__ import print_function import subprocess import os +import time """Test daemon RPC payment calls """ @@ -43,6 +44,7 @@ class RPCPaymentTest(): self.make_test_signature = os.environ['MAKE_TEST_SIGNATURE'] assert len(self.make_test_signature) > 0 self.secret_key, self.public_key = self.get_keys() + self.signatures = [] self.reset() self.test_access_tracking() self.test_access_mining() @@ -56,8 +58,19 @@ class RPCPaymentTest(): assert len(fields) == 2 return fields + def refill_signatures(self): + self.signatures_time = time.time() + self.signatures = [] + signatures = subprocess.check_output([self.make_test_signature, self.secret_key, '256']).decode('utf-8') + for line in signatures.split(): + self.signatures.append(line.rstrip()) + def get_signature(self): - return subprocess.check_output([self.make_test_signature, self.secret_key]).decode('utf-8').rstrip() + if len(self.signatures) == 0 or self.signatures_time + 10 < time.time(): + self.refill_signatures() + s = self.signatures[0] + self.signatures = self.signatures[1:] + return s def reset(self): print('Resetting blockchain') @@ -143,6 +156,7 @@ class RPCPaymentTest(): found_valid = 0 found_invalid = 0 last_credits = 0 + loop_time = time.time() while found_valid == 0 or found_invalid == 0: nonce += 1 try: @@ -152,10 +166,17 @@ class RPCPaymentTest(): except Exception as e: found_invalid += 1 res = daemon.rpc_access_info(client = self.get_signature()) + cookie = res.cookie + loop_time = time.time() assert res.credits < last_credits or res.credits == 0 assert nonce < 1000 # can't find both valid and invalid -> the RPC probably fails last_credits = res.credits + if time.time() >= loop_time + 10: + res = daemon.rpc_access_info(client = self.get_signature()) + cookie = res.cookie + loop_time = time.time() + # we should now have 1 valid nonce, and a number of bad ones res = daemon.rpc_access_info(client = self.get_signature()) assert len(res.hashing_blob) > 39 @@ -176,6 +197,7 @@ class RPCPaymentTest(): assert e.nonces_dupe == 0 # Try random nonces till we find one that's valid so we get a load of credits + loop_time = time.time() while last_credits == 0: nonce += 1 try: @@ -186,6 +208,10 @@ class RPCPaymentTest(): except: found_invalid += 1 assert nonce < 1000 # can't find a valid none -> the RPC probably fails + if time.time() >= loop_time + 10: + res = daemon.rpc_access_info(client = self.get_signature()) + cookie = res.cookie + loop_time = time.time() # we should now have at least 5000 res = daemon.rpc_access_info(client = self.get_signature()) @@ -208,6 +234,7 @@ class RPCPaymentTest(): res = daemon.rpc_access_info(client = self.get_signature()) cookie = res.cookie old_cookie = cookie # we keep that so can submit a stale later + loop_time = time.time() while True: nonce += 1 try: @@ -218,6 +245,11 @@ class RPCPaymentTest(): found_invalid += 1 assert nonce < 1000 # can't find both valid and invalid -> the RPC probably fails + if time.time() >= loop_time + 10: + res = daemon.rpc_access_info(client = self.get_signature()) + cookie = res.cookie + loop_time = time.time() + res = daemon.rpc_access_data() assert len(res.entries) > 0 e = [x for x in res.entries if x['client'] == self.public_key] @@ -247,14 +279,17 @@ class RPCPaymentTest(): # find stales without updating cookie, one within 5 seconds (accepted), one later (rejected) res = daemon.rpc_access_info(client = self.get_signature()) + cookie = res.cookie # let the daemon update its timestamp, but use old cookie found_close_stale = 0 found_late_stale = 0 + loop_time = time.time() while found_close_stale == 0 or found_late_stale == 0: nonce += 1 try: res = daemon.rpc_access_submit_nonce(nonce = nonce, cookie = cookie, client = self.get_signature()) found_close_stale += 1 found_valid += 1 + time.sleep(15) # now we've got an early stale, wait till they become late stales except Exception as e: #if e[0]['error']['code'] == -18: # stale if "'code': -18" in str(e): # stale (ugly version, but also works with python 3) @@ -263,6 +298,11 @@ class RPCPaymentTest(): found_invalid += 1 assert nonce < 1000 # can't find both valid and invalid -> the RPC probably fails + if time.time() >= loop_time + 10: + res = daemon.rpc_access_info(client = self.get_signature()) + # cookie = res.cookie # let the daemon update its timestamp, but use old cookie + loop_time = time.time() + res = daemon.rpc_access_data() assert len(res.entries) > 0 e = [x for x in res.entries if x['client'] == self.public_key] diff --git a/tests/functional_tests/transfer.py b/tests/functional_tests/transfer.py index 0c942f48b..f7a39fa0c 100755 --- a/tests/functional_tests/transfer.py +++ b/tests/functional_tests/transfer.py @@ -55,7 +55,7 @@ class TransferTest(): def reset(self): print('Resetting blockchain') - daemon = Daemon() + daemon = Daemon(idx = 2) res = daemon.get_height() daemon.pop_blocks(res.height - 1) daemon.flush_txpool() @@ -69,7 +69,7 @@ class TransferTest(): ] self.wallet = [None] * len(seeds) for i in range(len(seeds)): - self.wallet[i] = Wallet(idx = i) + self.wallet[i] = Wallet(idx = i + 4) # close the wallet if any, will throw if none is loaded try: self.wallet[i].close_wallet() except: pass @@ -77,7 +77,7 @@ class TransferTest(): def mine(self): print("Mining some blocks") - daemon = Daemon() + daemon = Daemon(idx = 2) res = daemon.get_info() height = res.height @@ -89,7 +89,7 @@ class TransferTest(): assert res.height == height + 80 def transfer(self): - daemon = Daemon() + daemon = Daemon(idx = 2) print("Creating transfer to self") @@ -169,7 +169,7 @@ class TransferTest(): assert e.subaddr_indices == [{'major': 0, 'minor': 0}] assert e.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm' assert e.double_spend_seen == False - assert e.confirmations == 0 + assert not 'confirmations' in e or e.confirmations == 0 running_balances[0] -= 1000000000000 + fee @@ -256,7 +256,6 @@ class TransferTest(): assert res.too_big == False assert res.overspend == False assert res.fee_too_low == False - assert res.not_rct == False self.wallet[0].refresh() @@ -282,7 +281,7 @@ class TransferTest(): assert e.subaddr_indices == [{'major': 0, 'minor': 0}] assert e.address == '44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW' assert e.double_spend_seen == False - assert e.confirmations == 0 + assert not 'confirmations' in e or e.confirmations == 0 assert e.amount == amount assert e.fee == fee @@ -509,7 +508,7 @@ class TransferTest(): def check_get_bulk_payments(self): print('Checking get_bulk_payments') - daemon = Daemon() + daemon = Daemon(idx = 2) res = daemon.get_info() height = res.height @@ -545,7 +544,7 @@ class TransferTest(): def check_get_payments(self): print('Checking get_payments') - daemon = Daemon() + daemon = Daemon(idx = 2) res = daemon.get_info() height = res.height @@ -588,7 +587,8 @@ class TransferTest(): assert len(res.tx_blob_list) == 1 txes[i][1] = res.tx_blob_list[0] - daemon = Daemon() + daemon = Daemon(idx = 2) + restricted_daemon = Daemon(idx = 2, restricted_rpc = True) res = daemon.send_raw_transaction(txes[0][1]) assert res.not_relayed == False assert res.low_mixin == False @@ -598,8 +598,19 @@ class TransferTest(): assert res.too_big == False assert res.overspend == False assert res.fee_too_low == False - assert res.not_rct == False + res = restricted_daemon.send_raw_transaction(txes[0][1]) + assert res.not_relayed == False + assert res.low_mixin == False + assert res.double_spend == False + assert res.invalid_input == False + assert res.invalid_output == False + assert res.too_big == False + assert res.overspend == False + assert res.fee_too_low == False + + res = restricted_daemon.get_transactions([txes[0][0]]) + assert not 'txs' in res or len(res.txs) == 0 res = daemon.get_transactions([txes[0][0]]) assert len(res.txs) >= 1 tx = [tx for tx in res.txs if tx.tx_hash == txes[0][0]][0] @@ -615,9 +626,21 @@ class TransferTest(): assert res.too_big == False assert res.overspend == False assert res.fee_too_low == False - assert res.not_rct == False assert res.too_few_outputs == False + res = restricted_daemon.send_raw_transaction(txes[1][1]) + assert res.not_relayed == False + assert res.low_mixin == False + assert res.double_spend == True + assert res.invalid_input == False + assert res.invalid_output == False + assert res.too_big == False + assert res.overspend == False + assert res.fee_too_low == False + assert res.too_few_outputs == False + + res = restricted_daemon.get_transactions([txes[0][0]]) + assert not 'txs' in res or len(res.txs) == 0 res = daemon.get_transactions([txes[0][0]]) assert len(res.txs) >= 1 tx = [tx for tx in res.txs if tx.tx_hash == txes[0][0]][0] @@ -626,13 +649,13 @@ class TransferTest(): def sweep_dust(self): print("Sweeping dust") - daemon = Daemon() + daemon = Daemon(idx = 2) self.wallet[0].refresh() res = self.wallet[0].sweep_dust() assert not 'tx_hash_list' in res or len(res.tx_hash_list) == 0 # there's just one, but it cannot meet the fee def sweep_single(self): - daemon = Daemon() + daemon = Daemon(idx = 2) print("Sending single output") @@ -688,7 +711,7 @@ class TransferTest(): assert len([t for t in res.transfers if t.key_image == ki]) == 1 def check_destinations(self): - daemon = Daemon() + daemon = Daemon(idx = 2) print("Checking transaction destinations") @@ -744,7 +767,7 @@ class TransferTest(): self.wallet[0].refresh() def check_tx_notes(self): - daemon = Daemon() + daemon = Daemon(idx = 2) print('Testing tx notes') res = self.wallet[0].get_transfers() @@ -761,7 +784,7 @@ class TransferTest(): assert res.notes == ['out txid', 'in txid'] def check_rescan(self): - daemon = Daemon() + daemon = Daemon(idx = 2) print('Testing rescan_spent') res = self.wallet[0].incoming_transfers(transfer_type = 'all') @@ -801,7 +824,7 @@ class TransferTest(): assert sorted(old_t_out, key = lambda k: k['txid']) == sorted(new_t_out, key = lambda k: k['txid']) def check_is_key_image_spent(self): - daemon = Daemon() + daemon = Daemon(idx = 2) print('Testing is_key_image_spent') res = self.wallet[0].incoming_transfers(transfer_type = 'all') diff --git a/tests/functional_tests/txpool.py b/tests/functional_tests/txpool.py index 27ae89764..2d7f69f3c 100755 --- a/tests/functional_tests/txpool.py +++ b/tests/functional_tests/txpool.py @@ -45,14 +45,14 @@ class TransferTest(): def reset(self): print('Resetting blockchain') - daemon = Daemon() + daemon = Daemon(idx=2) res = daemon.get_height() daemon.pop_blocks(res.height - 1) daemon.flush_txpool() def create(self): print('Creating wallet') - wallet = Wallet() + wallet = Wallet(idx = 4) # close the wallet if any, will throw if none is loaded try: wallet.close_wallet() except: pass @@ -61,8 +61,8 @@ class TransferTest(): def mine(self): print("Mining some blocks") - daemon = Daemon() - wallet = Wallet() + daemon = Daemon(idx = 2) + wallet = Wallet(idx = 4) daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 80) wallet.refresh() @@ -70,8 +70,8 @@ class TransferTest(): def create_txes(self, address, ntxes): print('Creating ' + str(ntxes) + ' transactions') - daemon = Daemon() - wallet = Wallet() + daemon = Daemon(idx = 2) + wallet = Wallet(idx = 4) dst = {'address': address, 'amount': 1000000000000} @@ -83,8 +83,10 @@ class TransferTest(): return txes def check_empty_pool(self): - daemon = Daemon() + self.check_empty_rpc_pool(Daemon(idx = 2)) + self.check_empty_rpc_pool(Daemon(idx = 2, restricted_rpc = True)) + def check_empty_rpc_pool(self, daemon): res = daemon.get_transaction_pool_hashes() assert not 'tx_hashes' in res or len(res.tx_hashes) == 0 res = daemon.get_transaction_pool_stats() @@ -103,8 +105,9 @@ class TransferTest(): assert res.pool_stats.num_double_spends == 0 def check_txpool(self): - daemon = Daemon() - wallet = Wallet() + daemon = Daemon(idx = 2) + restricted_daemon = Daemon(idx = 2, restricted_rpc = True) + wallet = Wallet(idx = 4) res = daemon.get_info() height = res.height @@ -117,6 +120,7 @@ class TransferTest(): res = daemon.get_info() assert res.tx_pool_size == txpool_size + 5 txpool_size = res.tx_pool_size + self.check_empty_rpc_pool(restricted_daemon) res = daemon.get_transaction_pool() assert len(res.transactions) == txpool_size @@ -160,6 +164,7 @@ class TransferTest(): print('Flushing 2 transactions') txes_keys = list(txes.keys()) daemon.flush_txpool([txes_keys[1], txes_keys[3]]) + self.check_empty_rpc_pool(restricted_daemon) res = daemon.get_transaction_pool() assert len(res.transactions) == txpool_size - 2 assert len([x for x in res.transactions if x.id_hash == txes_keys[1]]) == 0 @@ -210,6 +215,7 @@ class TransferTest(): print('Flushing unknown transactions') unknown_txids = ['1'*64, '2'*64, '3'*64] daemon.flush_txpool(unknown_txids) + self.check_empty_rpc_pool(restricted_daemon) res = daemon.get_transaction_pool() assert len(res.transactions) == txpool_size - 2 diff --git a/tests/fuzz/http-client.cpp b/tests/fuzz/http-client.cpp index 1750838ae..ea6d5a2ad 100644 --- a/tests/fuzz/http-client.cpp +++ b/tests/fuzz/http-client.cpp @@ -29,6 +29,7 @@ #include "include_base_utils.h" #include "file_io_utils.h" #include "net/http_client.h" +#include "net/net_ssl.h" #include "fuzzer.h" class dummy_client @@ -46,6 +47,10 @@ public: data.clear(); return true; } + void set_ssl(epee::net_utils::ssl_options_t ssl_options) { } + bool is_connected(bool *ssl = NULL) { return true; } + uint64_t get_bytes_sent() const { return 1; } + uint64_t get_bytes_received() const { return 1; } void set_test_data(const std::string &s) { data = s; } diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index cda25dfc9..9c24a5ec7 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -34,6 +34,7 @@ set(unit_tests_sources blockchain_db.cpp block_queue.cpp block_reward.cpp + bootstrap_node_selector.cpp bulletproofs.cpp canonical_amounts.cpp chacha.cpp @@ -93,7 +94,8 @@ set(unit_tests_sources wipeable_string.cpp is_hdd.cpp aligned.cpp - rpc_version_str.cpp) + rpc_version_str.cpp + zmq_rpc.cpp) set(unit_tests_headers unit_tests_utils.h) @@ -106,6 +108,7 @@ target_link_libraries(unit_tests ringct cryptonote_protocol cryptonote_core + daemon_messages blockchain_db lmdb_lib rpc diff --git a/tests/unit_tests/bootstrap_node_selector.cpp b/tests/unit_tests/bootstrap_node_selector.cpp new file mode 100644 index 000000000..c609d1223 --- /dev/null +++ b/tests/unit_tests/bootstrap_node_selector.cpp @@ -0,0 +1,172 @@ +// Copyright (c) 2020, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <gtest/gtest.h> + +#include "rpc/bootstrap_node_selector.h" + +class bootstrap_node_selector : public ::testing::Test +{ +protected: + void SetUp() override + { + nodes.insert(white_nodes.begin(), white_nodes.end()); + nodes.insert(gray_nodes.begin(), gray_nodes.end()); + } + + const std::map<std::string, bool> white_nodes = { + { + "white_node_1:18089", true + }, + { + "white_node_2:18081", true + } + }; + const std::map<std::string, bool> gray_nodes = { + { + "gray_node_1:18081", false + }, + { + "gray_node_2:18089", false + } + }; + + std::map<std::string, bool> nodes; +}; + +TEST_F(bootstrap_node_selector, selector_auto_empty) +{ + cryptonote::bootstrap_node::selector_auto selector([]() { + return std::map<std::string, bool>(); + }); + + EXPECT_FALSE(selector.next_node()); +} + +TEST_F(bootstrap_node_selector, selector_auto_no_credentials) +{ + cryptonote::bootstrap_node::selector_auto selector([this]() { + return nodes; + }); + + for (size_t fails = 0; fails < nodes.size(); ++fails) + { + const auto current = selector.next_node(); + EXPECT_FALSE(current->credentials); + + selector.handle_result(current->address, false); + } +} + +TEST_F(bootstrap_node_selector, selector_auto_success) +{ + cryptonote::bootstrap_node::selector_auto selector([this]() { + return nodes; + }); + + auto current = selector.next_node(); + for (size_t fails = 0; fails < nodes.size(); ++fails) + { + selector.handle_result(current->address, true); + + current = selector.next_node(); + EXPECT_TRUE(white_nodes.count(current->address) > 0); + } +} + +TEST_F(bootstrap_node_selector, selector_auto_failure) +{ + cryptonote::bootstrap_node::selector_auto selector([this]() { + return nodes; + }); + + auto current = selector.next_node(); + for (size_t fails = 0; fails < nodes.size(); ++fails) + { + const auto previous = current; + + selector.handle_result(current->address, false); + + current = selector.next_node(); + EXPECT_NE(current->address, previous->address); + } +} + +TEST_F(bootstrap_node_selector, selector_auto_white_nodes_first) +{ + cryptonote::bootstrap_node::selector_auto selector([this]() { + return nodes; + }); + + for (size_t iterations = 0; iterations < 2; ++iterations) + { + for (size_t fails = 0; fails < white_nodes.size(); ++fails) + { + const auto current = selector.next_node(); + EXPECT_TRUE(white_nodes.count(current->address) > 0); + + selector.handle_result(current->address, false); + } + + for (size_t fails = 0; fails < gray_nodes.size(); ++fails) + { + const auto current = selector.next_node(); + EXPECT_TRUE(gray_nodes.count(current->address) > 0); + + selector.handle_result(current->address, false); + } + } +} + +TEST_F(bootstrap_node_selector, selector_auto_max_nodes) +{ + const size_t max_nodes = nodes.size() / 2; + + bool populated_once = false; + cryptonote::bootstrap_node::selector_auto selector([this, &populated_once]() { + if (!populated_once) + { + populated_once = true; + return nodes; + } + + return std::map<std::string, bool>(); + }, max_nodes); + + std::set<std::string> unique_nodes; + + for (size_t fails = 0; fails < nodes.size(); ++fails) + { + const auto current = selector.next_node(); + unique_nodes.insert(current->address); + + selector.handle_result(current->address, false); + } + + EXPECT_EQ(unique_nodes.size(), max_nodes); +} diff --git a/tests/unit_tests/bulletproofs.cpp b/tests/unit_tests/bulletproofs.cpp index 760769551..a73b80848 100644 --- a/tests/unit_tests/bulletproofs.cpp +++ b/tests/unit_tests/bulletproofs.cpp @@ -179,15 +179,6 @@ TEST(bulletproofs, invalid_31) ASSERT_FALSE(rct::bulletproof_VERIFY(proof)); } -TEST(bulletproofs, invalid_gamma_0) -{ - rct::key invalid_amount = rct::zero(); - invalid_amount[8] = 1; - rct::key gamma = rct::zero(); - rct::Bulletproof proof = bulletproof_PROVE(invalid_amount, gamma); - ASSERT_FALSE(rct::bulletproof_VERIFY(proof)); -} - static const char * const torsion_elements[] = { "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", diff --git a/tests/unit_tests/epee_utils.cpp b/tests/unit_tests/epee_utils.cpp index 6f887afda..4f42140b3 100644 --- a/tests/unit_tests/epee_utils.cpp +++ b/tests/unit_tests/epee_utils.cpp @@ -45,6 +45,7 @@ #include "boost/archive/portable_binary_iarchive.hpp" #include "boost/archive/portable_binary_oarchive.hpp" #include "byte_slice.h" +#include "crypto/crypto.h" #include "hex.h" #include "net/net_utils_base.h" #include "net/local_ip.h" @@ -823,14 +824,14 @@ TEST(ToHex, String) } -TEST(FromHex, String) +TEST(HexLocale, String) { // the source data to encode and decode std::vector<uint8_t> source{{ 0x00, 0xFF, 0x0F, 0xF0 }}; // encode and decode the data auto hex = epee::to_hex::string({ source.data(), source.size() }); - auto decoded = epee::from_hex::vector(hex); + auto decoded = epee::from_hex_locale::to_vector(hex); // encoded should be twice the size and should decode to the exact same data EXPECT_EQ(source.size() * 2, hex.size()); @@ -839,7 +840,10 @@ TEST(FromHex, String) // we will now create a padded hex string, we want to explicitly allow // decoding it this way also, ignoring spaces and colons between the numbers hex.assign("00:ff 0f:f0"); - EXPECT_EQ(source, epee::from_hex::vector(hex)); + EXPECT_EQ(source, epee::from_hex_locale::to_vector(hex)); + + hex.append("f0"); + EXPECT_EQ(source, epee::from_hex_locale::to_vector(boost::string_ref{hex.data(), hex.size() - 2})); } TEST(ToHex, Array) @@ -850,6 +854,17 @@ TEST(ToHex, Array) ); } +TEST(ToHex, ArrayFromPod) +{ + std::array<char, 64> expected{{'5', 'f', '2', 'b', '0', '1'}}; + std::fill(expected.begin() + 6, expected.end(), '0'); + + EXPECT_EQ( + expected, + (epee::to_hex::array(crypto::ec_point{{0x5F, 0x2B, 0x01, 0x00}})) + ); +} + TEST(ToHex, Ostream) { std::stringstream out; @@ -890,6 +905,46 @@ TEST(ToHex, Formatted) EXPECT_EQ(expected, out.str()); } +TEST(FromHex, ToString) +{ + static constexpr const char hex[] = u8"deadbeeffY"; + static constexpr const char binary[] = { + char(0xde), char(0xad), char(0xbe), char(0xef), 0x00 + }; + + std::string out{}; + EXPECT_FALSE(epee::from_hex::to_string(out, hex)); + + boost::string_ref portion{hex}; + portion.remove_suffix(1); + EXPECT_FALSE(epee::from_hex::to_string(out, portion)); + + portion.remove_suffix(1); + EXPECT_TRUE(epee::from_hex::to_string(out, portion)); + EXPECT_EQ(std::string{binary}, out); +} + +TEST(FromHex, ToBuffer) +{ + static constexpr const char hex[] = u8"deadbeeffY"; + static constexpr const std::uint8_t binary[] = {0xde, 0xad, 0xbe, 0xef}; + + std::vector<std::uint8_t> out{}; + out.resize(sizeof(binary)); + EXPECT_FALSE(epee::from_hex::to_buffer(epee::to_mut_span(out), hex)); + + boost::string_ref portion{hex}; + portion.remove_suffix(1); + EXPECT_FALSE(epee::from_hex::to_buffer(epee::to_mut_span(out), portion)); + + portion.remove_suffix(1); + EXPECT_FALSE(epee::from_hex::to_buffer({out.data(), out.size() - 1}, portion)); + + EXPECT_TRUE(epee::from_hex::to_buffer(epee::to_mut_span(out), portion)); + const std::vector<std::uint8_t> expected{std::begin(binary), std::end(binary)}; + EXPECT_EQ(expected, out); +} + TEST(StringTools, BuffToHex) { const std::vector<unsigned char> all_bytes = get_all_bytes(); diff --git a/tests/unit_tests/hmac_keccak.cpp b/tests/unit_tests/hmac_keccak.cpp index 3898d4a7a..ec7c90dbe 100644 --- a/tests/unit_tests/hmac_keccak.cpp +++ b/tests/unit_tests/hmac_keccak.cpp @@ -123,7 +123,7 @@ static void test_keccak_hmac(const size_t * chunks) } -TEST(keccak_hmac, ) +TEST(keccak_hmac, nullptr) { test_keccak_hmac(nullptr); } diff --git a/tests/unit_tests/json_serialization.cpp b/tests/unit_tests/json_serialization.cpp index 53d9f84c7..6f98d854d 100644 --- a/tests/unit_tests/json_serialization.cpp +++ b/tests/unit_tests/json_serialization.cpp @@ -3,6 +3,8 @@ #include <boost/range/adaptor/indexed.hpp> #include <gtest/gtest.h> #include <rapidjson/document.h> +#include <rapidjson/stringbuffer.h> +#include <rapidjson/writer.h> #include <vector> #include "crypto/hash.h" @@ -80,6 +82,27 @@ namespace return tx; } + + template<typename T> + T test_json(const T& value) + { + rapidjson::StringBuffer buffer; + { + rapidjson::Writer<rapidjson::StringBuffer> dest{buffer}; + cryptonote::json::toJsonValue(dest, value); + } + + rapidjson::Document doc; + doc.Parse(buffer.GetString()); + if (doc.HasParseError() || !doc.IsObject()) + { + throw cryptonote::json::PARSE_FAIL(); + } + + T out{}; + cryptonote::json::fromJsonValue(doc, out); + return out; + } } // anonymous TEST(JsonSerialization, MinerTransaction) @@ -91,11 +114,7 @@ TEST(JsonSerialization, MinerTransaction) crypto::hash tx_hash{}; ASSERT_TRUE(cryptonote::get_transaction_hash(miner_tx, tx_hash)); - rapidjson::Document doc; - cryptonote::json::toJsonValue(doc, miner_tx, doc); - - cryptonote::transaction miner_tx_copy; - cryptonote::json::fromJsonValue(doc, miner_tx_copy); + cryptonote::transaction miner_tx_copy = test_json(miner_tx); crypto::hash tx_copy_hash{}; ASSERT_TRUE(cryptonote::get_transaction_hash(miner_tx_copy, tx_copy_hash)); @@ -126,11 +145,7 @@ TEST(JsonSerialization, RegularTransaction) crypto::hash tx_hash{}; ASSERT_TRUE(cryptonote::get_transaction_hash(tx, tx_hash)); - rapidjson::Document doc; - cryptonote::json::toJsonValue(doc, tx, doc); - - cryptonote::transaction tx_copy; - cryptonote::json::fromJsonValue(doc, tx_copy); + cryptonote::transaction tx_copy = test_json(tx); crypto::hash tx_copy_hash{}; ASSERT_TRUE(cryptonote::get_transaction_hash(tx_copy, tx_copy_hash)); @@ -161,11 +176,7 @@ TEST(JsonSerialization, RingctTransaction) crypto::hash tx_hash{}; ASSERT_TRUE(cryptonote::get_transaction_hash(tx, tx_hash)); - rapidjson::Document doc; - cryptonote::json::toJsonValue(doc, tx, doc); - - cryptonote::transaction tx_copy; - cryptonote::json::fromJsonValue(doc, tx_copy); + cryptonote::transaction tx_copy = test_json(tx); crypto::hash tx_copy_hash{}; ASSERT_TRUE(cryptonote::get_transaction_hash(tx_copy, tx_copy_hash)); @@ -196,11 +207,7 @@ TEST(JsonSerialization, BulletproofTransaction) crypto::hash tx_hash{}; ASSERT_TRUE(cryptonote::get_transaction_hash(tx, tx_hash)); - rapidjson::Document doc; - cryptonote::json::toJsonValue(doc, tx, doc); - - cryptonote::transaction tx_copy; - cryptonote::json::fromJsonValue(doc, tx_copy); + cryptonote::transaction tx_copy = test_json(tx); crypto::hash tx_copy_hash{}; ASSERT_TRUE(cryptonote::get_transaction_hash(tx_copy, tx_copy_hash)); diff --git a/tests/unit_tests/keccak.cpp b/tests/unit_tests/keccak.cpp index f4d41a8fa..a869fbaec 100644 --- a/tests/unit_tests/keccak.cpp +++ b/tests/unit_tests/keccak.cpp @@ -54,10 +54,6 @@ extern "C" { keccak_finish(&ctx, md1); \ ASSERT_EQ(memcmp(md0, md1, 32), 0); -TEST(keccak, ) -{ -} - TEST(keccak, 0_and_0) { static const size_t chunks[] = {0}; diff --git a/tests/unit_tests/levin.cpp b/tests/unit_tests/levin.cpp index 38707f075..62b101a42 100644 --- a/tests/unit_tests/levin.cpp +++ b/tests/unit_tests/levin.cpp @@ -34,11 +34,14 @@ #include <gtest/gtest.h> #include <limits> #include <set> +#include <map> #include "byte_slice.h" #include "crypto/crypto.h" #include "cryptonote_basic/connection_context.h" +#include "cryptonote_config.h" #include "cryptonote_core/cryptonote_core.h" +#include "cryptonote_core/i_core_events.h" #include "cryptonote_protocol/cryptonote_protocol_defs.h" #include "cryptonote_protocol/levin_notify.h" #include "int-util.h" @@ -113,6 +116,44 @@ namespace std::deque<epee::byte_slice> send_queue_; }; + class test_core_events final : public cryptonote::i_core_events + { + std::map<cryptonote::relay_method, std::vector<cryptonote::blobdata>> relayed_; + + virtual void on_transactions_relayed(epee::span<const cryptonote::blobdata> txes, cryptonote::relay_method relay) override final + { + std::vector<cryptonote::blobdata>& cached = relayed_[relay]; + for (const auto& tx : txes) + cached.push_back(tx); + } + + public: + test_core_events() + : relayed_() + {} + + std::size_t relayed_method_size() const noexcept + { + return relayed_.size(); + } + + bool has_stem_txes() const noexcept + { + return relayed_.count(cryptonote::relay_method::stem); + } + + std::vector<cryptonote::blobdata> take_relayed(cryptonote::relay_method relay) + { + auto elems = relayed_.find(relay); + if (elems == relayed_.end()) + throw std::logic_error{"on_transactions_relayed empty"}; + + std::vector<cryptonote::blobdata> out{std::move(elems->second)}; + relayed_.erase(elems); + return out; + } + }; + class test_connection { test_endpoint endpoint_; @@ -146,6 +187,11 @@ namespace { return context_.m_connection_id; } + + bool is_incoming() const noexcept + { + return context_.m_is_income; + } }; struct received_message @@ -155,7 +201,7 @@ namespace std::string payload; }; - class test_receiver : public epee::levin::levin_commands_handler<cryptonote::levin::detail::p2p_context> + class test_receiver final : public epee::levin::levin_commands_handler<cryptonote::levin::detail::p2p_context> { std::deque<received_message> invoked_; std::deque<received_message> notified_; @@ -253,7 +299,8 @@ namespace random_generator_(), io_service_(), receiver_(), - contexts_() + contexts_(), + events_() { connections_->set_handler(std::addressof(receiver_), nullptr); } @@ -262,6 +309,7 @@ namespace { EXPECT_EQ(0u, receiver_.invoked_size()); EXPECT_EQ(0u, receiver_.notified_size()); + EXPECT_EQ(0u, events_.relayed_method_size()); } void add_connection(const bool is_incoming) @@ -283,6 +331,7 @@ namespace boost::asio::io_service io_service_; test_receiver receiver_; std::deque<test_connection> contexts_; + test_core_events events_; }; } @@ -434,11 +483,11 @@ TEST_F(levin_notify, defaulted) EXPECT_FALSE(status.has_noise); EXPECT_FALSE(status.connections_filled); } - EXPECT_TRUE(notifier.send_txs({}, random_generator_())); + EXPECT_TRUE(notifier.send_txs({}, random_generator_(), events_, cryptonote::relay_method::local)); std::vector<cryptonote::blobdata> txs(2); txs[0].resize(100, 'e'); - EXPECT_FALSE(notifier.send_txs(std::move(txs), random_generator_())); + EXPECT_FALSE(notifier.send_txs(std::move(txs), random_generator_(), events_, cryptonote::relay_method::local)); } TEST_F(levin_notify, fluff_without_padding) @@ -455,11 +504,221 @@ TEST_F(levin_notify, fluff_without_padding) } notifier.new_out_connection(); io_service_.poll(); + + std::vector<cryptonote::blobdata> txs(2); + txs[0].resize(100, 'f'); + txs[1].resize(200, 'e'); + + ASSERT_EQ(10u, contexts_.size()); + { + auto context = contexts_.begin(); + EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::fluff)); + + io_service_.reset(); + ASSERT_LT(0u, io_service_.poll()); + notifier.run_fluff(); + ASSERT_LT(0u, io_service_.poll()); + + EXPECT_EQ(0u, context->process_send_queue()); + for (++context; context != contexts_.end(); ++context) + EXPECT_EQ(1u, context->process_send_queue()); + + EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::fluff)); + std::sort(txs.begin(), txs.end()); + ASSERT_EQ(9u, receiver_.notified_size()); + for (unsigned count = 0; count < 9; ++count) + { + auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; + EXPECT_EQ(txs, notification.txs); + EXPECT_TRUE(notification._.empty()); + EXPECT_TRUE(notification.dandelionpp_fluff); + } + } +} + +TEST_F(levin_notify, stem_without_padding) +{ + cryptonote::levin::notify notifier = make_notifier(0, true, false); + + for (unsigned count = 0; count < 10; ++count) + add_connection(count % 2 == 0); + { const auto status = notifier.get_status(); EXPECT_FALSE(status.has_noise); - EXPECT_FALSE(status.connections_filled); // not tracked + EXPECT_FALSE(status.connections_filled); } + notifier.new_out_connection(); + io_service_.poll(); + + std::vector<cryptonote::blobdata> txs(2); + txs[0].resize(100, 'f'); + txs[1].resize(200, 'e'); + + std::vector<cryptonote::blobdata> sorted_txs = txs; + std::sort(sorted_txs.begin(), sorted_txs.end()); + + ASSERT_EQ(10u, contexts_.size()); + bool has_stemmed = false; + bool has_fluffed = false; + while (!has_stemmed || !has_fluffed) + { + auto context = contexts_.begin(); + EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::stem)); + + io_service_.reset(); + ASSERT_LT(0u, io_service_.poll()); + const bool is_stem = events_.has_stem_txes(); + EXPECT_EQ(txs, events_.take_relayed(is_stem ? cryptonote::relay_method::stem : cryptonote::relay_method::fluff)); + + if (!is_stem) + { + notifier.run_fluff(); + ASSERT_LT(0u, io_service_.poll()); + } + + std::size_t send_count = 0; + EXPECT_EQ(0u, context->process_send_queue()); + for (++context; context != contexts_.end(); ++context) + { + const std::size_t sent = context->process_send_queue(); + if (sent && is_stem) + EXPECT_EQ(1u, (context - contexts_.begin()) % 2); + send_count += sent; + } + + EXPECT_EQ(is_stem ? 1u : 9u, send_count); + ASSERT_EQ(is_stem ? 1u : 9u, receiver_.notified_size()); + for (unsigned count = 0; count < (is_stem ? 1u : 9u); ++count) + { + auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; + if (is_stem) + EXPECT_EQ(txs, notification.txs); + else + EXPECT_EQ(sorted_txs, notification.txs); + EXPECT_TRUE(notification._.empty()); + EXPECT_EQ(!is_stem, notification.dandelionpp_fluff); + } + + has_stemmed |= is_stem; + has_fluffed |= !is_stem; + notifier.run_epoch(); + } +} + +TEST_F(levin_notify, local_without_padding) +{ + cryptonote::levin::notify notifier = make_notifier(0, true, false); + + for (unsigned count = 0; count < 10; ++count) + add_connection(count % 2 == 0); + + { + const auto status = notifier.get_status(); + EXPECT_FALSE(status.has_noise); + EXPECT_FALSE(status.connections_filled); + } + notifier.new_out_connection(); + io_service_.poll(); + + std::vector<cryptonote::blobdata> txs(2); + txs[0].resize(100, 'f'); + txs[1].resize(200, 'e'); + + std::vector<cryptonote::blobdata> sorted_txs = txs; + std::sort(sorted_txs.begin(), sorted_txs.end()); + + ASSERT_EQ(10u, contexts_.size()); + bool has_stemmed = false; + bool has_fluffed = false; + while (!has_stemmed || !has_fluffed) + { + auto context = contexts_.begin(); + EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::local)); + + io_service_.reset(); + ASSERT_LT(0u, io_service_.poll()); + const bool is_stem = events_.has_stem_txes(); + EXPECT_EQ(txs, events_.take_relayed(is_stem ? cryptonote::relay_method::stem : cryptonote::relay_method::fluff)); + + if (!is_stem) + { + notifier.run_fluff(); + ASSERT_LT(0u, io_service_.poll()); + } + + std::size_t send_count = 0; + EXPECT_EQ(0u, context->process_send_queue()); + for (++context; context != contexts_.end(); ++context) + { + const std::size_t sent = context->process_send_queue(); + if (sent && is_stem) + EXPECT_EQ(1u, (context - contexts_.begin()) % 2); + send_count += sent; + } + + EXPECT_EQ(is_stem ? 1u : 9u, send_count); + ASSERT_EQ(is_stem ? 1u : 9u, receiver_.notified_size()); + for (unsigned count = 0; count < (is_stem ? 1u : 9u); ++count) + { + auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; + if (is_stem) + EXPECT_EQ(txs, notification.txs); + else + EXPECT_EQ(sorted_txs, notification.txs); + EXPECT_TRUE(notification._.empty()); + EXPECT_EQ(!is_stem, notification.dandelionpp_fluff); + } + + has_stemmed |= is_stem; + has_fluffed |= !is_stem; + notifier.run_epoch(); + } +} + +TEST_F(levin_notify, block_without_padding) +{ + cryptonote::levin::notify notifier = make_notifier(0, true, false); + + for (unsigned count = 0; count < 10; ++count) + add_connection(count % 2 == 0); + + { + const auto status = notifier.get_status(); + EXPECT_FALSE(status.has_noise); + EXPECT_FALSE(status.connections_filled); + } + notifier.new_out_connection(); + io_service_.poll(); + + std::vector<cryptonote::blobdata> txs(2); + txs[0].resize(100, 'e'); + txs[1].resize(200, 'f'); + + ASSERT_EQ(10u, contexts_.size()); + { + auto context = contexts_.begin(); + EXPECT_FALSE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::block)); + + io_service_.reset(); + ASSERT_EQ(0u, io_service_.poll()); + } +} + +TEST_F(levin_notify, none_without_padding) +{ + cryptonote::levin::notify notifier = make_notifier(0, true, false); + + for (unsigned count = 0; count < 10; ++count) + add_connection(count % 2 == 0); + + { + const auto status = notifier.get_status(); + EXPECT_FALSE(status.has_noise); + EXPECT_FALSE(status.connections_filled); + } + notifier.new_out_connection(); + io_service_.poll(); std::vector<cryptonote::blobdata> txs(2); txs[0].resize(100, 'e'); @@ -468,13 +727,44 @@ TEST_F(levin_notify, fluff_without_padding) ASSERT_EQ(10u, contexts_.size()); { auto context = contexts_.begin(); - EXPECT_TRUE(notifier.send_txs(txs, context->get_id())); + EXPECT_FALSE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::none)); + + io_service_.reset(); + ASSERT_EQ(0u, io_service_.poll()); + } +} + +TEST_F(levin_notify, fluff_with_padding) +{ + cryptonote::levin::notify notifier = make_notifier(0, true, true); + + for (unsigned count = 0; count < 10; ++count) + add_connection(count % 2 == 0); + + { + const auto status = notifier.get_status(); + EXPECT_FALSE(status.has_noise); + EXPECT_FALSE(status.connections_filled); + } + notifier.new_out_connection(); + io_service_.poll(); + + std::vector<cryptonote::blobdata> txs(2); + txs[0].resize(100, 'f'); + txs[1].resize(200, 'e'); + + ASSERT_EQ(10u, contexts_.size()); + { + auto context = contexts_.begin(); + EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::fluff)); io_service_.reset(); ASSERT_LT(0u, io_service_.poll()); notifier.run_fluff(); ASSERT_LT(0u, io_service_.poll()); + EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::fluff)); + std::sort(txs.begin(), txs.end()); EXPECT_EQ(0u, context->process_send_queue()); for (++context; context != contexts_.end(); ++context) EXPECT_EQ(1u, context->process_send_queue()); @@ -484,12 +774,13 @@ TEST_F(levin_notify, fluff_without_padding) { auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; EXPECT_EQ(txs, notification.txs); - EXPECT_TRUE(notification._.empty()); + EXPECT_FALSE(notification._.empty()); + EXPECT_TRUE(notification.dandelionpp_fluff); } } } -TEST_F(levin_notify, fluff_with_padding) +TEST_F(levin_notify, stem_with_padding) { cryptonote::levin::notify notifier = make_notifier(0, true, true); @@ -503,37 +794,181 @@ TEST_F(levin_notify, fluff_with_padding) } notifier.new_out_connection(); io_service_.poll(); + + std::vector<cryptonote::blobdata> txs(2); + txs[0].resize(100, 'e'); + txs[1].resize(200, 'f'); + + ASSERT_EQ(10u, contexts_.size()); + bool has_stemmed = false; + bool has_fluffed = false; + while (!has_stemmed || !has_fluffed) + { + auto context = contexts_.begin(); + EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::stem)); + + io_service_.reset(); + ASSERT_LT(0u, io_service_.poll()); + const bool is_stem = events_.has_stem_txes(); + EXPECT_EQ(txs, events_.take_relayed(is_stem ? cryptonote::relay_method::stem : cryptonote::relay_method::fluff)); + + if (!is_stem) + { + notifier.run_fluff(); + ASSERT_LT(0u, io_service_.poll()); + } + + std::size_t send_count = 0; + EXPECT_EQ(0u, context->process_send_queue()); + for (++context; context != contexts_.end(); ++context) + { + const std::size_t sent = context->process_send_queue(); + if (sent && is_stem) + { + EXPECT_EQ(1u, (context - contexts_.begin()) % 2); + EXPECT_FALSE(context->is_incoming()); + } + send_count += sent; + } + + EXPECT_EQ(is_stem ? 1u : 9u, send_count); + ASSERT_EQ(is_stem ? 1u : 9u, receiver_.notified_size()); + for (unsigned count = 0; count < (is_stem ? 1u : 9u); ++count) + { + auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; + EXPECT_EQ(txs, notification.txs); + EXPECT_FALSE(notification._.empty()); + EXPECT_EQ(!is_stem, notification.dandelionpp_fluff); + } + + has_stemmed |= is_stem; + has_fluffed |= !is_stem; + notifier.run_epoch(); + } +} + +TEST_F(levin_notify, local_with_padding) +{ + cryptonote::levin::notify notifier = make_notifier(0, true, true); + + for (unsigned count = 0; count < 10; ++count) + add_connection(count % 2 == 0); + { const auto status = notifier.get_status(); EXPECT_FALSE(status.has_noise); - EXPECT_FALSE(status.connections_filled); // not tracked + EXPECT_FALSE(status.connections_filled); } + notifier.new_out_connection(); + io_service_.poll(); std::vector<cryptonote::blobdata> txs(2); txs[0].resize(100, 'e'); txs[1].resize(200, 'f'); ASSERT_EQ(10u, contexts_.size()); + bool has_stemmed = false; + bool has_fluffed = false; + while (!has_stemmed || !has_fluffed) { auto context = contexts_.begin(); - EXPECT_TRUE(notifier.send_txs(txs, context->get_id())); + EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::local)); io_service_.reset(); ASSERT_LT(0u, io_service_.poll()); - notifier.run_fluff(); - ASSERT_LT(0u, io_service_.poll()); + const bool is_stem = events_.has_stem_txes(); + EXPECT_EQ(txs, events_.take_relayed(is_stem ? cryptonote::relay_method::stem : cryptonote::relay_method::fluff)); + + if (!is_stem) + { + notifier.run_fluff(); + ASSERT_LT(0u, io_service_.poll()); + } + std::size_t send_count = 0; EXPECT_EQ(0u, context->process_send_queue()); for (++context; context != contexts_.end(); ++context) - EXPECT_EQ(1u, context->process_send_queue()); + { + const std::size_t sent = context->process_send_queue(); + if (sent && is_stem) + { + EXPECT_EQ(1u, (context - contexts_.begin()) % 2); + EXPECT_FALSE(context->is_incoming()); + } + send_count += sent; + } - ASSERT_EQ(9u, receiver_.notified_size()); - for (unsigned count = 0; count < 9; ++count) + EXPECT_EQ(is_stem ? 1u : 9u, send_count); + ASSERT_EQ(is_stem ? 1u : 9u, receiver_.notified_size()); + for (unsigned count = 0; count < (is_stem ? 1u : 9u); ++count) { auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; EXPECT_EQ(txs, notification.txs); EXPECT_FALSE(notification._.empty()); + EXPECT_EQ(!is_stem, notification.dandelionpp_fluff); } + + has_stemmed |= is_stem; + has_fluffed |= !is_stem; + notifier.run_epoch(); + } +} + +TEST_F(levin_notify, block_with_padding) +{ + cryptonote::levin::notify notifier = make_notifier(0, true, true); + + for (unsigned count = 0; count < 10; ++count) + add_connection(count % 2 == 0); + + { + const auto status = notifier.get_status(); + EXPECT_FALSE(status.has_noise); + EXPECT_FALSE(status.connections_filled); + } + notifier.new_out_connection(); + io_service_.poll(); + + std::vector<cryptonote::blobdata> txs(2); + txs[0].resize(100, 'e'); + txs[1].resize(200, 'f'); + + ASSERT_EQ(10u, contexts_.size()); + { + auto context = contexts_.begin(); + EXPECT_FALSE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::block)); + + io_service_.reset(); + ASSERT_EQ(0u, io_service_.poll()); + } +} + +TEST_F(levin_notify, none_with_padding) +{ + cryptonote::levin::notify notifier = make_notifier(0, true, true); + + for (unsigned count = 0; count < 10; ++count) + add_connection(count % 2 == 0); + + { + const auto status = notifier.get_status(); + EXPECT_FALSE(status.has_noise); + EXPECT_FALSE(status.connections_filled); + } + notifier.new_out_connection(); + io_service_.poll(); + + std::vector<cryptonote::blobdata> txs(2); + txs[0].resize(100, 'e'); + txs[1].resize(200, 'f'); + + ASSERT_EQ(10u, contexts_.size()); + { + auto context = contexts_.begin(); + EXPECT_FALSE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::none)); + + io_service_.reset(); + ASSERT_EQ(0u, io_service_.poll()); } } @@ -551,11 +986,108 @@ TEST_F(levin_notify, private_fluff_without_padding) } notifier.new_out_connection(); io_service_.poll(); + + std::vector<cryptonote::blobdata> txs(2); + txs[0].resize(100, 'e'); + txs[1].resize(200, 'f'); + + ASSERT_EQ(10u, contexts_.size()); + { + auto context = contexts_.begin(); + EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::fluff)); + + io_service_.reset(); + ASSERT_LT(0u, io_service_.poll()); + notifier.run_fluff(); + io_service_.reset(); + ASSERT_LT(0u, io_service_.poll()); + + EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::fluff)); + + EXPECT_EQ(0u, context->process_send_queue()); + for (++context; context != contexts_.end(); ++context) + { + const bool is_incoming = ((context - contexts_.begin()) % 2 == 0); + EXPECT_EQ(is_incoming ? 0u : 1u, context->process_send_queue()); + } + + ASSERT_EQ(5u, receiver_.notified_size()); + for (unsigned count = 0; count < 5; ++count) + { + auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; + EXPECT_EQ(txs, notification.txs); + EXPECT_TRUE(notification._.empty()); + EXPECT_FALSE(notification.dandelionpp_fluff); + } + } +} + +TEST_F(levin_notify, private_stem_without_padding) +{ + // private mode always uses fluff but marked as stem + cryptonote::levin::notify notifier = make_notifier(0, false, false); + + for (unsigned count = 0; count < 10; ++count) + add_connection(count % 2 == 0); + + { + const auto status = notifier.get_status(); + EXPECT_FALSE(status.has_noise); + EXPECT_FALSE(status.connections_filled); + } + notifier.new_out_connection(); + io_service_.poll(); + + std::vector<cryptonote::blobdata> txs(2); + txs[0].resize(100, 'e'); + txs[1].resize(200, 'f'); + + ASSERT_EQ(10u, contexts_.size()); + { + auto context = contexts_.begin(); + EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::stem)); + + io_service_.reset(); + ASSERT_LT(0u, io_service_.poll()); + notifier.run_fluff(); + io_service_.reset(); + ASSERT_LT(0u, io_service_.poll()); + + EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::fluff)); + + EXPECT_EQ(0u, context->process_send_queue()); + for (++context; context != contexts_.end(); ++context) + { + const bool is_incoming = ((context - contexts_.begin()) % 2 == 0); + EXPECT_EQ(is_incoming ? 0u : 1u, context->process_send_queue()); + } + + ASSERT_EQ(5u, receiver_.notified_size()); + for (unsigned count = 0; count < 5; ++count) + { + auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; + EXPECT_EQ(txs, notification.txs); + EXPECT_TRUE(notification._.empty()); + EXPECT_FALSE(notification.dandelionpp_fluff); + } + } +} + +TEST_F(levin_notify, private_local_without_padding) +{ + // private mode always uses fluff but marked as stem + cryptonote::levin::notify notifier = make_notifier(0, false, false); + + for (unsigned count = 0; count < 10; ++count) + add_connection(count % 2 == 0); + { const auto status = notifier.get_status(); EXPECT_FALSE(status.has_noise); - EXPECT_FALSE(status.connections_filled); // not tracked + EXPECT_FALSE(status.connections_filled); } + notifier.new_out_connection(); + io_service_.poll(); std::vector<cryptonote::blobdata> txs(2); txs[0].resize(100, 'e'); @@ -564,7 +1096,7 @@ TEST_F(levin_notify, private_fluff_without_padding) ASSERT_EQ(10u, contexts_.size()); { auto context = contexts_.begin(); - EXPECT_TRUE(notifier.send_txs(txs, context->get_id())); + EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::local)); io_service_.reset(); ASSERT_LT(0u, io_service_.poll()); @@ -572,6 +1104,8 @@ TEST_F(levin_notify, private_fluff_without_padding) io_service_.reset(); ASSERT_LT(0u, io_service_.poll()); + EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::local)); + EXPECT_EQ(0u, context->process_send_queue()); for (++context; context != contexts_.end(); ++context) { @@ -585,10 +1119,71 @@ TEST_F(levin_notify, private_fluff_without_padding) auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; EXPECT_EQ(txs, notification.txs); EXPECT_TRUE(notification._.empty()); + EXPECT_FALSE(notification.dandelionpp_fluff); } } } +TEST_F(levin_notify, private_block_without_padding) +{ + // private mode always uses fluff but marked as stem + cryptonote::levin::notify notifier = make_notifier(0, false, false); + + for (unsigned count = 0; count < 10; ++count) + add_connection(count % 2 == 0); + + { + const auto status = notifier.get_status(); + EXPECT_FALSE(status.has_noise); + EXPECT_FALSE(status.connections_filled); + } + notifier.new_out_connection(); + io_service_.poll(); + + std::vector<cryptonote::blobdata> txs(2); + txs[0].resize(100, 'e'); + txs[1].resize(200, 'f'); + + ASSERT_EQ(10u, contexts_.size()); + { + auto context = contexts_.begin(); + EXPECT_FALSE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::block)); + + io_service_.reset(); + ASSERT_EQ(0u, io_service_.poll()); + } +} + +TEST_F(levin_notify, private_none_without_padding) +{ + // private mode always uses fluff but marked as stem + cryptonote::levin::notify notifier = make_notifier(0, false, false); + + for (unsigned count = 0; count < 10; ++count) + add_connection(count % 2 == 0); + + { + const auto status = notifier.get_status(); + EXPECT_FALSE(status.has_noise); + EXPECT_FALSE(status.connections_filled); + } + notifier.new_out_connection(); + io_service_.poll(); + + std::vector<cryptonote::blobdata> txs(2); + txs[0].resize(100, 'e'); + txs[1].resize(200, 'f'); + + ASSERT_EQ(10u, contexts_.size()); + { + auto context = contexts_.begin(); + EXPECT_FALSE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::none)); + + io_service_.reset(); + ASSERT_EQ(0u, io_service_.poll()); + } +} + TEST_F(levin_notify, private_fluff_with_padding) { cryptonote::levin::notify notifier = make_notifier(0, false, true); @@ -603,11 +1198,106 @@ TEST_F(levin_notify, private_fluff_with_padding) } notifier.new_out_connection(); io_service_.poll(); + + std::vector<cryptonote::blobdata> txs(2); + txs[0].resize(100, 'e'); + txs[1].resize(200, 'f'); + + ASSERT_EQ(10u, contexts_.size()); + { + auto context = contexts_.begin(); + EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::fluff)); + + io_service_.reset(); + ASSERT_LT(0u, io_service_.poll()); + notifier.run_fluff(); + io_service_.reset(); + ASSERT_LT(0u, io_service_.poll()); + + EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::fluff)); + + EXPECT_EQ(0u, context->process_send_queue()); + for (++context; context != contexts_.end(); ++context) + { + const bool is_incoming = ((context - contexts_.begin()) % 2 == 0); + EXPECT_EQ(is_incoming ? 0u : 1u, context->process_send_queue()); + } + + ASSERT_EQ(5u, receiver_.notified_size()); + for (unsigned count = 0; count < 5; ++count) + { + auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; + EXPECT_EQ(txs, notification.txs); + EXPECT_FALSE(notification._.empty()); + EXPECT_FALSE(notification.dandelionpp_fluff); + } + } +} + +TEST_F(levin_notify, private_stem_with_padding) +{ + cryptonote::levin::notify notifier = make_notifier(0, false, true); + + for (unsigned count = 0; count < 10; ++count) + add_connection(count % 2 == 0); + + { + const auto status = notifier.get_status(); + EXPECT_FALSE(status.has_noise); + EXPECT_FALSE(status.connections_filled); + } + notifier.new_out_connection(); + io_service_.poll(); + + std::vector<cryptonote::blobdata> txs(2); + txs[0].resize(100, 'e'); + txs[1].resize(200, 'f'); + + ASSERT_EQ(10u, contexts_.size()); + { + auto context = contexts_.begin(); + EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::stem)); + + io_service_.reset(); + ASSERT_LT(0u, io_service_.poll()); + notifier.run_fluff(); + io_service_.reset(); + ASSERT_LT(0u, io_service_.poll()); + + EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::fluff)); + + EXPECT_EQ(0u, context->process_send_queue()); + for (++context; context != contexts_.end(); ++context) + { + const bool is_incoming = ((context - contexts_.begin()) % 2 == 0); + EXPECT_EQ(is_incoming ? 0u : 1u, context->process_send_queue()); + } + + ASSERT_EQ(5u, receiver_.notified_size()); + for (unsigned count = 0; count < 5; ++count) + { + auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; + EXPECT_EQ(txs, notification.txs); + EXPECT_FALSE(notification._.empty()); + EXPECT_FALSE(notification.dandelionpp_fluff); + } + } +} + +TEST_F(levin_notify, private_local_with_padding) +{ + cryptonote::levin::notify notifier = make_notifier(0, false, true); + + for (unsigned count = 0; count < 10; ++count) + add_connection(count % 2 == 0); + { const auto status = notifier.get_status(); EXPECT_FALSE(status.has_noise); - EXPECT_FALSE(status.connections_filled); // not tracked + EXPECT_FALSE(status.connections_filled); } + notifier.new_out_connection(); + io_service_.poll(); std::vector<cryptonote::blobdata> txs(2); txs[0].resize(100, 'e'); @@ -616,7 +1306,7 @@ TEST_F(levin_notify, private_fluff_with_padding) ASSERT_EQ(10u, contexts_.size()); { auto context = contexts_.begin(); - EXPECT_TRUE(notifier.send_txs(txs, context->get_id())); + EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::local)); io_service_.reset(); ASSERT_LT(0u, io_service_.poll()); @@ -624,6 +1314,8 @@ TEST_F(levin_notify, private_fluff_with_padding) io_service_.reset(); ASSERT_LT(0u, io_service_.poll()); + EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::local)); + EXPECT_EQ(0u, context->process_send_queue()); for (++context; context != contexts_.end(); ++context) { @@ -637,6 +1329,301 @@ TEST_F(levin_notify, private_fluff_with_padding) auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; EXPECT_EQ(txs, notification.txs); EXPECT_FALSE(notification._.empty()); + EXPECT_FALSE(notification.dandelionpp_fluff); + } + } +} + +TEST_F(levin_notify, private_block_with_padding) +{ + cryptonote::levin::notify notifier = make_notifier(0, false, true); + + for (unsigned count = 0; count < 10; ++count) + add_connection(count % 2 == 0); + + { + const auto status = notifier.get_status(); + EXPECT_FALSE(status.has_noise); + EXPECT_FALSE(status.connections_filled); + } + notifier.new_out_connection(); + io_service_.poll(); + + std::vector<cryptonote::blobdata> txs(2); + txs[0].resize(100, 'e'); + txs[1].resize(200, 'f'); + + ASSERT_EQ(10u, contexts_.size()); + { + auto context = contexts_.begin(); + EXPECT_FALSE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::block)); + + io_service_.reset(); + ASSERT_EQ(0u, io_service_.poll()); + } +} + +TEST_F(levin_notify, private_none_with_padding) +{ + cryptonote::levin::notify notifier = make_notifier(0, false, true); + + for (unsigned count = 0; count < 10; ++count) + add_connection(count % 2 == 0); + + { + const auto status = notifier.get_status(); + EXPECT_FALSE(status.has_noise); + EXPECT_FALSE(status.connections_filled); + } + notifier.new_out_connection(); + io_service_.poll(); + + std::vector<cryptonote::blobdata> txs(2); + txs[0].resize(100, 'e'); + txs[1].resize(200, 'f'); + + ASSERT_EQ(10u, contexts_.size()); + { + auto context = contexts_.begin(); + EXPECT_FALSE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::none)); + + io_service_.reset(); + ASSERT_EQ(0u, io_service_.poll()); + } +} + +TEST_F(levin_notify, stem_mappings) +{ + static constexpr const unsigned test_connections_count = (CRYPTONOTE_DANDELIONPP_STEMS + 1) * 2; + + cryptonote::levin::notify notifier = make_notifier(0, true, false); + + for (unsigned count = 0; count < test_connections_count; ++count) + add_connection(count % 2 == 0); + + { + const auto status = notifier.get_status(); + EXPECT_FALSE(status.has_noise); + EXPECT_FALSE(status.connections_filled); + } + notifier.new_out_connection(); + io_service_.poll(); + + std::vector<cryptonote::blobdata> txs(2); + txs[0].resize(100, 'e'); + txs[1].resize(200, 'f'); + + ASSERT_EQ(test_connections_count, contexts_.size()); + for (;;) + { + auto context = contexts_.begin(); + EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::stem)); + + io_service_.reset(); + ASSERT_LT(0u, io_service_.poll()); + if (events_.has_stem_txes()) + break; + + EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::fluff)); + notifier.run_fluff(); + io_service_.reset(); + ASSERT_LT(0u, io_service_.poll()); + + EXPECT_EQ(0u, context->process_send_queue()); + for (++context; context != contexts_.end(); ++context) + EXPECT_EQ(1u, context->process_send_queue()); + + ASSERT_EQ(test_connections_count - 1, receiver_.notified_size()); + for (unsigned count = 0; count < test_connections_count - 1; ++count) + { + auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; + EXPECT_EQ(txs, notification.txs); + EXPECT_TRUE(notification._.empty()); + EXPECT_TRUE(notification.dandelionpp_fluff); + } + + notifier.run_epoch(); + io_service_.reset(); + ASSERT_LT(0u, io_service_.poll()); + } + EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::stem)); + + std::set<boost::uuids::uuid> used; + std::map<boost::uuids::uuid, boost::uuids::uuid> mappings; + { + std::size_t send_count = 0; + for (auto context = contexts_.begin(); context != contexts_.end(); ++context) + { + const std::size_t sent = context->process_send_queue(); + if (sent) + { + EXPECT_EQ(1u, (context - contexts_.begin()) % 2); + EXPECT_FALSE(context->is_incoming()); + used.insert(context->get_id()); + mappings[contexts_.front().get_id()] = context->get_id(); + } + send_count += sent; + } + + EXPECT_EQ(1u, send_count); + ASSERT_EQ(1u, receiver_.notified_size()); + for (unsigned count = 0; count < 1u; ++count) + { + auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; + EXPECT_EQ(txs, notification.txs); + EXPECT_TRUE(notification._.empty()); + EXPECT_FALSE(notification.dandelionpp_fluff); + } + } + + for (unsigned i = 0; i < contexts_.size() * 2; i += 2) + { + auto& incoming = contexts_[i % contexts_.size()]; + EXPECT_TRUE(notifier.send_txs(txs, incoming.get_id(), events_, cryptonote::relay_method::stem)); + + io_service_.reset(); + ASSERT_LT(0u, io_service_.poll()); + EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::stem)); + + std::size_t send_count = 0; + for (auto context = contexts_.begin(); context != contexts_.end(); ++context) + { + const std::size_t sent = context->process_send_queue(); + if (sent) + { + EXPECT_EQ(1u, (context - contexts_.begin()) % 2); + EXPECT_FALSE(context->is_incoming()); + used.insert(context->get_id()); + + auto inserted = mappings.emplace(incoming.get_id(), context->get_id()).first; + EXPECT_EQ(inserted->second, context->get_id()) << "incoming index " << i; + } + send_count += sent; + } + + EXPECT_EQ(1u, send_count); + ASSERT_EQ(1u, receiver_.notified_size()); + for (unsigned count = 0; count < 1u; ++count) + { + auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; + EXPECT_EQ(txs, notification.txs); + EXPECT_TRUE(notification._.empty()); + EXPECT_FALSE(notification.dandelionpp_fluff); + } + } + + EXPECT_EQ(CRYPTONOTE_DANDELIONPP_STEMS, used.size()); +} + +TEST_F(levin_notify, fluff_multiple) +{ + static constexpr const unsigned test_connections_count = (CRYPTONOTE_DANDELIONPP_STEMS + 1) * 2; + + cryptonote::levin::notify notifier = make_notifier(0, true, false); + + for (unsigned count = 0; count < test_connections_count; ++count) + add_connection(count % 2 == 0); + + { + const auto status = notifier.get_status(); + EXPECT_FALSE(status.has_noise); + EXPECT_FALSE(status.connections_filled); + } + notifier.new_out_connection(); + io_service_.poll(); + + std::vector<cryptonote::blobdata> txs(2); + txs[0].resize(100, 'e'); + txs[1].resize(200, 'f'); + + ASSERT_EQ(test_connections_count, contexts_.size()); + for (;;) + { + auto context = contexts_.begin(); + EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::stem)); + + io_service_.reset(); + ASSERT_LT(0u, io_service_.poll()); + if (!events_.has_stem_txes()) + break; + + EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::stem)); + + std::size_t send_count = 0; + EXPECT_EQ(0u, context->process_send_queue()); + for (++context; context != contexts_.end(); ++context) + { + const std::size_t sent = context->process_send_queue(); + if (sent) + { + EXPECT_EQ(1u, (context - contexts_.begin()) % 2); + EXPECT_FALSE(context->is_incoming()); + } + send_count += sent; + } + + EXPECT_EQ(1u, send_count); + ASSERT_EQ(1u, receiver_.notified_size()); + for (unsigned count = 0; count < 1; ++count) + { + auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; + EXPECT_EQ(txs, notification.txs); + EXPECT_TRUE(notification._.empty()); + EXPECT_FALSE(notification.dandelionpp_fluff); + } + + notifier.run_epoch(); + io_service_.reset(); + ASSERT_LT(0u, io_service_.poll()); + } + EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::fluff)); + notifier.run_fluff(); + io_service_.reset(); + ASSERT_LT(0u, io_service_.poll()); + { + auto context = contexts_.begin(); + EXPECT_EQ(0u, context->process_send_queue()); + for (++context; context != contexts_.end(); ++context) + EXPECT_EQ(1u, context->process_send_queue()); + + ASSERT_EQ(contexts_.size() - 1, receiver_.notified_size()); + for (unsigned count = 0; count < contexts_.size() - 1; ++count) + { + auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; + EXPECT_EQ(txs, notification.txs); + EXPECT_TRUE(notification._.empty()); + EXPECT_TRUE(notification.dandelionpp_fluff); + } + } + + for (unsigned i = 0; i < contexts_.size() * 2; i += 2) + { + auto& incoming = contexts_[i % contexts_.size()]; + EXPECT_TRUE(notifier.send_txs(txs, incoming.get_id(), events_, cryptonote::relay_method::stem)); + + io_service_.reset(); + ASSERT_LT(0u, io_service_.poll()); + notifier.run_fluff(); + io_service_.reset(); + ASSERT_LT(0u, io_service_.poll()); + + EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::fluff)); + + for (auto& context : contexts_) + { + if (std::addressof(incoming) == std::addressof(context)) + EXPECT_EQ(0u, context.process_send_queue()); + else + EXPECT_EQ(1u, context.process_send_queue()); + } + + ASSERT_EQ(contexts_.size() - 1, receiver_.notified_size()); + for (unsigned count = 0; count < contexts_.size() - 1; ++count) + { + auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; + EXPECT_EQ(txs, notification.txs); + EXPECT_TRUE(notification._.empty()); + EXPECT_TRUE(notification.dandelionpp_fluff); } } } @@ -676,10 +1663,12 @@ TEST_F(levin_notify, noise) EXPECT_EQ(0u, receiver_.notified_size()); } - EXPECT_TRUE(notifier.send_txs(txs, incoming_id)); + EXPECT_TRUE(notifier.send_txs(txs, incoming_id, events_, cryptonote::relay_method::local)); notifier.run_stems(); io_service_.reset(); ASSERT_LT(0u, io_service_.poll()); + + EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::local)); { std::size_t sent = 0; for (auto& context : contexts_) @@ -691,11 +1680,68 @@ TEST_F(levin_notify, noise) auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; EXPECT_EQ(txs, notification.txs); EXPECT_TRUE(notification._.empty()); + EXPECT_FALSE(notification.dandelionpp_fluff); } } txs[0].resize(3000, 'r'); - EXPECT_TRUE(notifier.send_txs(txs, incoming_id)); + EXPECT_TRUE(notifier.send_txs(txs, incoming_id, events_, cryptonote::relay_method::fluff)); + notifier.run_stems(); + io_service_.reset(); + ASSERT_LT(0u, io_service_.poll()); + + EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::fluff)); + { + std::size_t sent = 0; + for (auto& context : contexts_) + sent += context.process_send_queue(); + + EXPECT_EQ(2u, sent); + EXPECT_EQ(0u, receiver_.notified_size()); + } + + notifier.run_stems(); + io_service_.reset(); + ASSERT_LT(0u, io_service_.poll()); + { + std::size_t sent = 0; + for (auto& context : contexts_) + sent += context.process_send_queue(); + + ASSERT_EQ(2u, sent); + while (sent--) + { + auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; + EXPECT_EQ(txs, notification.txs); + EXPECT_TRUE(notification._.empty()); + EXPECT_FALSE(notification.dandelionpp_fluff); + } + } +} + +TEST_F(levin_notify, noise_stem) +{ + for (unsigned count = 0; count < 10; ++count) + add_connection(count % 2 == 0); + + std::vector<cryptonote::blobdata> txs(1); + txs[0].resize(1900, 'h'); + + const boost::uuids::uuid incoming_id = random_generator_(); + cryptonote::levin::notify notifier = make_notifier(2048, false, true); + + { + const auto status = notifier.get_status(); + EXPECT_TRUE(status.has_noise); + EXPECT_FALSE(status.connections_filled); + } + ASSERT_LT(0u, io_service_.poll()); + { + const auto status = notifier.get_status(); + EXPECT_TRUE(status.has_noise); + EXPECT_TRUE(status.connections_filled); + } + notifier.run_stems(); io_service_.reset(); ASSERT_LT(0u, io_service_.poll()); @@ -708,9 +1754,13 @@ TEST_F(levin_notify, noise) EXPECT_EQ(0u, receiver_.notified_size()); } + EXPECT_TRUE(notifier.send_txs(txs, incoming_id, events_, cryptonote::relay_method::stem)); notifier.run_stems(); io_service_.reset(); ASSERT_LT(0u, io_service_.poll()); + + // downgraded to local when being notified + EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::local)); { std::size_t sent = 0; for (auto& context : contexts_) @@ -722,6 +1772,7 @@ TEST_F(levin_notify, noise) auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; EXPECT_EQ(txs, notification.txs); EXPECT_TRUE(notification._.empty()); + EXPECT_FALSE(notification.dandelionpp_fluff); } } } diff --git a/tests/unit_tests/mul_div.cpp b/tests/unit_tests/mul_div.cpp index e3f7c34f3..c1a72ce51 100644 --- a/tests/unit_tests/mul_div.cpp +++ b/tests/unit_tests/mul_div.cpp @@ -82,7 +82,7 @@ namespace lo = mul128(0x1111111111111111, 0x1111111111111111, &hi); ASSERT_EQ(lo, 0x0fedcba987654321); - ASSERT_EQ(hi, 0x0123456789abcdf0);; + ASSERT_EQ(hi, 0x0123456789abcdf0); } TEST(mul128_with_carry_1_only, multiplies_correctly) diff --git a/tests/unit_tests/net.cpp b/tests/unit_tests/net.cpp index 262541bd2..f5aef4796 100644 --- a/tests/unit_tests/net.cpp +++ b/tests/unit_tests/net.cpp @@ -53,8 +53,10 @@ #include <memory> #include <type_traits> +#include "crypto/crypto.h" #include "net/dandelionpp.h" #include "net/error.h" +#include "net/i2p_address.h" #include "net/net_utils_base.h" #include "net/socks.h" #include "net/socks_connect.h" @@ -177,22 +179,33 @@ TEST(tor_address, valid) EXPECT_FALSE(address2.less(*address1)); EXPECT_TRUE(address1->less(address2)); - address2 = MONERO_UNWRAP(net::tor_address::make(std::string{v3_onion} + ":", 65535)); - - EXPECT_EQ(65535, address2.port()); - EXPECT_STREQ(v3_onion, address2.host_str()); - EXPECT_EQ(std::string{v3_onion} + ":65535", address2.str().c_str()); - EXPECT_TRUE(address2.is_blockable()); - EXPECT_FALSE(address2.equal(*address1)); - EXPECT_FALSE(address1->equal(address2)); - EXPECT_FALSE(address2 == *address1); - EXPECT_FALSE(*address1 == address2); - EXPECT_TRUE(address2 != *address1); - EXPECT_TRUE(*address1 != address2); - EXPECT_TRUE(address2.is_same_host(*address1)); - EXPECT_TRUE(address1->is_same_host(address2)); - EXPECT_FALSE(address2.less(*address1)); - EXPECT_TRUE(address1->less(address2)); + net::tor_address address3 = MONERO_UNWRAP(net::tor_address::make(std::string{v3_onion} + ":", 65535)); + + EXPECT_EQ(65535, address3.port()); + EXPECT_STREQ(v3_onion, address3.host_str()); + EXPECT_EQ(std::string{v3_onion} + ":65535", address3.str().c_str()); + EXPECT_TRUE(address3.is_blockable()); + EXPECT_FALSE(address3.equal(*address1)); + EXPECT_FALSE(address1->equal(address3)); + EXPECT_FALSE(address3 == *address1); + EXPECT_FALSE(*address1 == address3); + EXPECT_TRUE(address3 != *address1); + EXPECT_TRUE(*address1 != address3); + EXPECT_TRUE(address3.is_same_host(*address1)); + EXPECT_TRUE(address1->is_same_host(address3)); + EXPECT_FALSE(address3.less(*address1)); + EXPECT_TRUE(address1->less(address3)); + + EXPECT_FALSE(address3.equal(address2)); + EXPECT_FALSE(address2.equal(address3)); + EXPECT_FALSE(address3 == address2); + EXPECT_FALSE(address2 == address3); + EXPECT_TRUE(address3 != address2); + EXPECT_TRUE(address2 != address3); + EXPECT_FALSE(address3.is_same_host(address2)); + EXPECT_FALSE(address2.is_same_host(address3)); + EXPECT_TRUE(address3.less(address2)); + EXPECT_FALSE(address2.less(address3)); } TEST(tor_address, generic_network_address) @@ -220,7 +233,7 @@ TEST(tor_address, generic_network_address) namespace { - struct test_command + struct test_command_tor { net::tor_address tor; @@ -234,7 +247,7 @@ TEST(tor_address, epee_serializev_v2) { std::string buffer{}; { - test_command command{MONERO_UNWRAP(net::tor_address::make(v2_onion, 10))}; + test_command_tor command{MONERO_UNWRAP(net::tor_address::make(v2_onion, 10))}; EXPECT_FALSE(command.tor.is_unknown()); EXPECT_NE(net::tor_address{}, command.tor); EXPECT_STREQ(v2_onion, command.tor.host_str()); @@ -245,7 +258,7 @@ TEST(tor_address, epee_serializev_v2) EXPECT_TRUE(stg.store_to_binary(buffer)); } - test_command command{}; + test_command_tor command{}; { EXPECT_TRUE(command.tor.is_unknown()); EXPECT_EQ(net::tor_address{}, command.tor); @@ -271,7 +284,7 @@ TEST(tor_address, epee_serializev_v2) EXPECT_EQ(std::strlen(v2_onion), host.size()); host.push_back('k'); - EXPECT_TRUE(stg.set_value("host", host, stg.open_section("tor", nullptr, false))); + EXPECT_TRUE(stg.set_value("host", std::move(host), stg.open_section("tor", nullptr, false))); EXPECT_TRUE(command.load(stg)); // poor error reporting from `KV_SERIALIZE` } @@ -285,7 +298,7 @@ TEST(tor_address, epee_serializev_v3) { std::string buffer{}; { - test_command command{MONERO_UNWRAP(net::tor_address::make(v3_onion, 10))}; + test_command_tor command{MONERO_UNWRAP(net::tor_address::make(v3_onion, 10))}; EXPECT_FALSE(command.tor.is_unknown()); EXPECT_NE(net::tor_address{}, command.tor); EXPECT_STREQ(v3_onion, command.tor.host_str()); @@ -296,7 +309,7 @@ TEST(tor_address, epee_serializev_v3) EXPECT_TRUE(stg.store_to_binary(buffer)); } - test_command command{}; + test_command_tor command{}; { EXPECT_TRUE(command.tor.is_unknown()); EXPECT_EQ(net::tor_address{}, command.tor); @@ -322,7 +335,7 @@ TEST(tor_address, epee_serializev_v3) EXPECT_EQ(std::strlen(v3_onion), host.size()); host.push_back('k'); - EXPECT_TRUE(stg.set_value("host", host, stg.open_section("tor", nullptr, false))); + EXPECT_TRUE(stg.set_value("host", std::move(host), stg.open_section("tor", nullptr, false))); EXPECT_TRUE(command.load(stg)); // poor error reporting from `KV_SERIALIZE` } @@ -336,7 +349,7 @@ TEST(tor_address, epee_serialize_unknown) { std::string buffer{}; { - test_command command{net::tor_address::unknown()}; + test_command_tor command{net::tor_address::unknown()}; EXPECT_TRUE(command.tor.is_unknown()); EXPECT_EQ(net::tor_address{}, command.tor); EXPECT_STREQ(net::tor_address::unknown_str(), command.tor.host_str()); @@ -347,7 +360,7 @@ TEST(tor_address, epee_serialize_unknown) EXPECT_TRUE(stg.store_to_binary(buffer)); } - test_command command{}; + test_command_tor command{}; { EXPECT_TRUE(command.tor.is_unknown()); EXPECT_EQ(net::tor_address{}, command.tor); @@ -373,7 +386,7 @@ TEST(tor_address, epee_serialize_unknown) EXPECT_EQ(std::strlen(net::tor_address::unknown_str()), host.size()); host.push_back('k'); - EXPECT_TRUE(stg.set_value("host", host, stg.open_section("tor", nullptr, false))); + EXPECT_TRUE(stg.set_value("host", std::move(host), stg.open_section("tor", nullptr, false))); EXPECT_TRUE(command.load(stg)); // poor error reporting from `KV_SERIALIZE` } @@ -513,6 +526,374 @@ TEST(get_network_address, onion) EXPECT_EQ(net::error::invalid_port, address); } +namespace +{ + static constexpr const char b32_i2p[] = + "vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopn.b32.i2p"; + static constexpr const char b32_i2p_2[] = + "xmrto2bturnore26xmrto2bturnore26xmrto2bturnore26xmr2.b32.i2p"; +} + +TEST(i2p_address, constants) +{ + static_assert(!net::i2p_address::is_local(), "bad is_local() response"); + static_assert(!net::i2p_address::is_loopback(), "bad is_loopback() response"); + static_assert(net::i2p_address::get_type_id() == epee::net_utils::address_type::i2p, "bad get_type_id() response"); + + EXPECT_FALSE(net::i2p_address::is_local()); + EXPECT_FALSE(net::i2p_address::is_loopback()); + EXPECT_EQ(epee::net_utils::address_type::i2p, net::i2p_address::get_type_id()); + EXPECT_EQ(epee::net_utils::address_type::i2p, net::i2p_address::get_type_id()); +} + +TEST(i2p_address, invalid) +{ + EXPECT_TRUE(net::i2p_address::make("").has_error()); + EXPECT_TRUE(net::i2p_address::make(":").has_error()); + EXPECT_TRUE(net::i2p_address::make(".b32.i2p").has_error()); + EXPECT_TRUE(net::i2p_address::make(".b32.i2p:").has_error()); + EXPECT_TRUE(net::i2p_address::make(b32_i2p + 1).has_error()); + EXPECT_TRUE(net::i2p_address::make(boost::string_ref{b32_i2p, sizeof(b32_i2p) - 2}).has_error()); + EXPECT_TRUE(net::i2p_address::make(std::string{b32_i2p} + ":65536").has_error()); + EXPECT_TRUE(net::i2p_address::make(std::string{b32_i2p} + ":-1").has_error()); + + std::string i2p{b32_i2p}; + i2p.at(10) = 1; + EXPECT_TRUE(net::i2p_address::make(i2p).has_error()); +} + +TEST(i2p_address, unblockable_types) +{ + net::i2p_address i2p{}; + + ASSERT_NE(nullptr, i2p.host_str()); + EXPECT_STREQ("<unknown i2p host>", i2p.host_str()); + EXPECT_STREQ("<unknown i2p host>", i2p.str().c_str()); + EXPECT_EQ(0u, i2p.port()); + EXPECT_TRUE(i2p.is_unknown()); + EXPECT_FALSE(i2p.is_local()); + EXPECT_FALSE(i2p.is_loopback()); + EXPECT_EQ(epee::net_utils::address_type::i2p, i2p.get_type_id()); + EXPECT_EQ(epee::net_utils::zone::i2p, i2p.get_zone()); + + i2p = net::i2p_address::unknown(); + ASSERT_NE(nullptr, i2p.host_str()); + EXPECT_STREQ("<unknown i2p host>", i2p.host_str()); + EXPECT_STREQ("<unknown i2p host>", i2p.str().c_str()); + EXPECT_EQ(0u, i2p.port()); + EXPECT_TRUE(i2p.is_unknown()); + EXPECT_FALSE(i2p.is_local()); + EXPECT_FALSE(i2p.is_loopback()); + EXPECT_EQ(epee::net_utils::address_type::i2p, i2p.get_type_id()); + EXPECT_EQ(epee::net_utils::zone::i2p, i2p.get_zone()); + + EXPECT_EQ(net::i2p_address{}, net::i2p_address::unknown()); +} + +TEST(i2p_address, valid) +{ + const auto address1 = net::i2p_address::make(b32_i2p); + + ASSERT_TRUE(address1.has_value()); + EXPECT_EQ(0u, address1->port()); + EXPECT_STREQ(b32_i2p, address1->host_str()); + EXPECT_STREQ(b32_i2p, address1->str().c_str()); + EXPECT_TRUE(address1->is_blockable()); + + net::i2p_address address2{*address1}; + + EXPECT_EQ(0u, address2.port()); + EXPECT_STREQ(b32_i2p, address2.host_str()); + EXPECT_STREQ(b32_i2p, address2.str().c_str()); + EXPECT_TRUE(address2.is_blockable()); + EXPECT_TRUE(address2.equal(*address1)); + EXPECT_TRUE(address1->equal(address2)); + EXPECT_TRUE(address2 == *address1); + EXPECT_TRUE(*address1 == address2); + EXPECT_FALSE(address2 != *address1); + EXPECT_FALSE(*address1 != address2); + EXPECT_TRUE(address2.is_same_host(*address1)); + EXPECT_TRUE(address1->is_same_host(address2)); + EXPECT_FALSE(address2.less(*address1)); + EXPECT_FALSE(address1->less(address2)); + + address2 = MONERO_UNWRAP(net::i2p_address::make(std::string{b32_i2p_2} + ":6545")); + + EXPECT_EQ(6545, address2.port()); + EXPECT_STREQ(b32_i2p_2, address2.host_str()); + EXPECT_EQ(std::string{b32_i2p_2} + ":6545", address2.str().c_str()); + EXPECT_TRUE(address2.is_blockable()); + EXPECT_FALSE(address2.equal(*address1)); + EXPECT_FALSE(address1->equal(address2)); + EXPECT_FALSE(address2 == *address1); + EXPECT_FALSE(*address1 == address2); + EXPECT_TRUE(address2 != *address1); + EXPECT_TRUE(*address1 != address2); + EXPECT_FALSE(address2.is_same_host(*address1)); + EXPECT_FALSE(address1->is_same_host(address2)); + EXPECT_FALSE(address2.less(*address1)); + EXPECT_TRUE(address1->less(address2)); + + net::i2p_address address3 = MONERO_UNWRAP(net::i2p_address::make(std::string{b32_i2p} + ":", 65535)); + + EXPECT_EQ(65535, address3.port()); + EXPECT_STREQ(b32_i2p, address3.host_str()); + EXPECT_EQ(std::string{b32_i2p} + ":65535", address3.str().c_str()); + EXPECT_TRUE(address3.is_blockable()); + EXPECT_FALSE(address3.equal(*address1)); + EXPECT_FALSE(address1->equal(address3)); + EXPECT_FALSE(address3 == *address1); + EXPECT_FALSE(*address1 == address3); + EXPECT_TRUE(address3 != *address1); + EXPECT_TRUE(*address1 != address3); + EXPECT_TRUE(address3.is_same_host(*address1)); + EXPECT_TRUE(address1->is_same_host(address3)); + EXPECT_FALSE(address3.less(*address1)); + EXPECT_TRUE(address1->less(address3)); + + EXPECT_FALSE(address3.equal(address2)); + EXPECT_FALSE(address2.equal(address3)); + EXPECT_FALSE(address3 == address2); + EXPECT_FALSE(address2 == address3); + EXPECT_TRUE(address3 != address2); + EXPECT_TRUE(address2 != address3); + EXPECT_FALSE(address3.is_same_host(address2)); + EXPECT_FALSE(address2.is_same_host(address3)); + EXPECT_TRUE(address3.less(address2)); + EXPECT_FALSE(address2.less(address3)); +} + +TEST(i2p_address, generic_network_address) +{ + const epee::net_utils::network_address i2p1{MONERO_UNWRAP(net::i2p_address::make(b32_i2p, 8080))}; + const epee::net_utils::network_address i2p2{MONERO_UNWRAP(net::i2p_address::make(b32_i2p, 8080))}; + const epee::net_utils::network_address ip{epee::net_utils::ipv4_network_address{100, 200}}; + + EXPECT_EQ(i2p1, i2p2); + EXPECT_NE(ip, i2p1); + EXPECT_LT(ip, i2p1); + + EXPECT_STREQ(b32_i2p, i2p1.host_str().c_str()); + EXPECT_EQ(std::string{b32_i2p} + ":8080", i2p1.str()); + EXPECT_EQ(epee::net_utils::address_type::i2p, i2p1.get_type_id()); + EXPECT_EQ(epee::net_utils::address_type::i2p, i2p2.get_type_id()); + EXPECT_EQ(epee::net_utils::address_type::ipv4, ip.get_type_id()); + EXPECT_EQ(epee::net_utils::zone::i2p, i2p1.get_zone()); + EXPECT_EQ(epee::net_utils::zone::i2p, i2p2.get_zone()); + EXPECT_EQ(epee::net_utils::zone::public_, ip.get_zone()); + EXPECT_TRUE(i2p1.is_blockable()); + EXPECT_TRUE(i2p2.is_blockable()); + EXPECT_TRUE(ip.is_blockable()); +} + +namespace +{ + struct test_command_i2p + { + net::i2p_address i2p; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(i2p); + END_KV_SERIALIZE_MAP() + }; +} + +TEST(i2p_address, epee_serializev_b32) +{ + std::string buffer{}; + { + test_command_i2p command{MONERO_UNWRAP(net::i2p_address::make(b32_i2p, 10))}; + EXPECT_FALSE(command.i2p.is_unknown()); + EXPECT_NE(net::i2p_address{}, command.i2p); + EXPECT_STREQ(b32_i2p, command.i2p.host_str()); + EXPECT_EQ(10u, command.i2p.port()); + + epee::serialization::portable_storage stg{}; + EXPECT_TRUE(command.store(stg)); + EXPECT_TRUE(stg.store_to_binary(buffer)); + } + + test_command_i2p command{}; + { + EXPECT_TRUE(command.i2p.is_unknown()); + EXPECT_EQ(net::i2p_address{}, command.i2p); + EXPECT_STREQ(net::i2p_address::unknown_str(), command.i2p.host_str()); + EXPECT_EQ(0u, command.i2p.port()); + + epee::serialization::portable_storage stg{}; + EXPECT_TRUE(stg.load_from_binary(buffer)); + EXPECT_TRUE(command.load(stg)); + } + EXPECT_FALSE(command.i2p.is_unknown()); + EXPECT_NE(net::i2p_address{}, command.i2p); + EXPECT_STREQ(b32_i2p, command.i2p.host_str()); + EXPECT_EQ(10u, command.i2p.port()); + + // make sure that exceeding max buffer doesn't destroy i2p_address::_load + { + epee::serialization::portable_storage stg{}; + stg.load_from_binary(buffer); + + std::string host{}; + ASSERT_TRUE(stg.get_value("host", host, stg.open_section("i2p", nullptr, false))); + EXPECT_EQ(std::strlen(b32_i2p), host.size()); + + host.push_back('k'); + EXPECT_TRUE(stg.set_value("host", std::string{host}, stg.open_section("i2p", nullptr, false))); + EXPECT_TRUE(command.load(stg)); // poor error reporting from `KV_SERIALIZE` + } + + EXPECT_TRUE(command.i2p.is_unknown()); + EXPECT_EQ(net::i2p_address{}, command.i2p); + EXPECT_STRNE(b32_i2p, command.i2p.host_str()); + EXPECT_EQ(0u, command.i2p.port()); +} + +TEST(i2p_address, epee_serialize_unknown) +{ + std::string buffer{}; + { + test_command_i2p command{net::i2p_address::unknown()}; + EXPECT_TRUE(command.i2p.is_unknown()); + EXPECT_EQ(net::i2p_address{}, command.i2p); + EXPECT_STREQ(net::i2p_address::unknown_str(), command.i2p.host_str()); + EXPECT_EQ(0u, command.i2p.port()); + + epee::serialization::portable_storage stg{}; + EXPECT_TRUE(command.store(stg)); + EXPECT_TRUE(stg.store_to_binary(buffer)); + } + + test_command_i2p command{}; + { + EXPECT_TRUE(command.i2p.is_unknown()); + EXPECT_EQ(net::i2p_address{}, command.i2p); + EXPECT_STRNE(b32_i2p, command.i2p.host_str()); + EXPECT_EQ(0u, command.i2p.port()); + + epee::serialization::portable_storage stg{}; + EXPECT_TRUE(stg.load_from_binary(buffer)); + EXPECT_TRUE(command.load(stg)); + } + EXPECT_TRUE(command.i2p.is_unknown()); + EXPECT_EQ(net::i2p_address{}, command.i2p); + EXPECT_STREQ(net::i2p_address::unknown_str(), command.i2p.host_str()); + EXPECT_EQ(0u, command.i2p.port()); + + // make sure that exceeding max buffer doesn't destroy i2p_address::_load + { + epee::serialization::portable_storage stg{}; + stg.load_from_binary(buffer); + + std::string host{}; + ASSERT_TRUE(stg.get_value("host", host, stg.open_section("i2p", nullptr, false))); + EXPECT_EQ(std::strlen(net::i2p_address::unknown_str()), host.size()); + + host.push_back('k'); + EXPECT_TRUE(stg.set_value("host", std::string{host}, stg.open_section("i2p", nullptr, false))); + EXPECT_TRUE(command.load(stg)); // poor error reporting from `KV_SERIALIZE` + } + + EXPECT_TRUE(command.i2p.is_unknown()); + EXPECT_EQ(net::i2p_address{}, command.i2p); + EXPECT_STRNE(b32_i2p, command.i2p.host_str()); + EXPECT_EQ(0u, command.i2p.port()); +} + +TEST(i2p_address, boost_serialize_b32) +{ + std::string buffer{}; + { + const net::i2p_address i2p = MONERO_UNWRAP(net::i2p_address::make(b32_i2p, 10)); + EXPECT_FALSE(i2p.is_unknown()); + EXPECT_NE(net::i2p_address{}, i2p); + EXPECT_STREQ(b32_i2p, i2p.host_str()); + EXPECT_EQ(10u, i2p.port()); + + std::ostringstream stream{}; + { + boost::archive::portable_binary_oarchive archive{stream}; + archive << i2p; + } + buffer = stream.str(); + } + + net::i2p_address i2p{}; + { + EXPECT_TRUE(i2p.is_unknown()); + EXPECT_EQ(net::i2p_address{}, i2p); + EXPECT_STREQ(net::i2p_address::unknown_str(), i2p.host_str()); + EXPECT_EQ(0u, i2p.port()); + + std::istringstream stream{buffer}; + boost::archive::portable_binary_iarchive archive{stream}; + archive >> i2p; + } + EXPECT_FALSE(i2p.is_unknown()); + EXPECT_NE(net::i2p_address{}, i2p); + EXPECT_STREQ(b32_i2p, i2p.host_str()); + EXPECT_EQ(10u, i2p.port()); +} + +TEST(i2p_address, boost_serialize_unknown) +{ + std::string buffer{}; + { + const net::i2p_address i2p{}; + EXPECT_TRUE(i2p.is_unknown()); + EXPECT_EQ(net::i2p_address::unknown(), i2p); + EXPECT_STREQ(net::i2p_address::unknown_str(), i2p.host_str()); + EXPECT_EQ(0u, i2p.port()); + + std::ostringstream stream{}; + { + boost::archive::portable_binary_oarchive archive{stream}; + archive << i2p; + } + buffer = stream.str(); + } + + net::i2p_address i2p{}; + { + EXPECT_TRUE(i2p.is_unknown()); + EXPECT_EQ(net::i2p_address{}, i2p); + EXPECT_STREQ(net::i2p_address::unknown_str(), i2p.host_str()); + EXPECT_EQ(0u, i2p.port()); + + std::istringstream stream{buffer}; + boost::archive::portable_binary_iarchive archive{stream}; + archive >> i2p; + } + EXPECT_TRUE(i2p.is_unknown()); + EXPECT_EQ(net::i2p_address::unknown(), i2p); + EXPECT_STREQ(net::i2p_address::unknown_str(), i2p.host_str()); + EXPECT_EQ(0u, i2p.port()); +} + +TEST(get_network_address, i2p) +{ + expect<epee::net_utils::network_address> address = + net::get_network_address("i2p", 0); + EXPECT_EQ(net::error::unsupported_address, address); + + address = net::get_network_address(".b32.i2p", 0); + EXPECT_EQ(net::error::invalid_i2p_address, address); + + address = net::get_network_address(b32_i2p, 1000); + ASSERT_TRUE(bool(address)); + EXPECT_EQ(epee::net_utils::address_type::i2p, address->get_type_id()); + EXPECT_STREQ(b32_i2p, address->host_str().c_str()); + EXPECT_EQ(std::string{b32_i2p} + ":1000", address->str()); + + address = net::get_network_address(std::string{b32_i2p} + ":2000", 1000); + ASSERT_TRUE(bool(address)); + EXPECT_EQ(epee::net_utils::address_type::i2p, address->get_type_id()); + EXPECT_STREQ(b32_i2p, address->host_str().c_str()); + EXPECT_EQ(std::string{b32_i2p} + ":2000", address->str()); + + address = net::get_network_address(std::string{b32_i2p} + ":65536", 1000); + EXPECT_EQ(net::error::invalid_port, address); +} TEST(get_network_address, ipv4) { @@ -1321,6 +1702,45 @@ TEST(zmq, read_write) EXPECT_EQ(message, *received); } +TEST(zmq, read_write_slice) +{ + net::zmq::context context{zmq_init(1)}; + ASSERT_NE(nullptr, context); + + net::zmq::socket send_socket{zmq_socket(context.get(), ZMQ_REQ)}; + net::zmq::socket recv_socket{zmq_socket(context.get(), ZMQ_REP)}; + ASSERT_NE(nullptr, send_socket); + ASSERT_NE(nullptr, recv_socket); + + ASSERT_EQ(0u, zmq_bind(recv_socket.get(), "inproc://testing")); + ASSERT_EQ(0u, zmq_connect(send_socket.get(), "inproc://testing")); + + std::string message; + message.resize(1024); + crypto::rand(message.size(), reinterpret_cast<std::uint8_t*>(std::addressof(message[0]))); + + { + epee::byte_slice slice_message{{epee::strspan<std::uint8_t>(message)}}; + ASSERT_TRUE(bool(net::zmq::send(std::move(slice_message), send_socket.get()))); + EXPECT_TRUE(slice_message.empty()); + } + + const expect<std::string> received = net::zmq::receive(recv_socket.get()); + ASSERT_TRUE(bool(received)); + EXPECT_EQ(message, *received); +} + +TEST(zmq, write_slice_fail) +{ + std::string message; + message.resize(1024); + crypto::rand(message.size(), reinterpret_cast<std::uint8_t*>(std::addressof(message[0]))); + + epee::byte_slice slice_message{std::move(message)}; + EXPECT_FALSE(bool(net::zmq::send(std::move(slice_message), nullptr))); + EXPECT_TRUE(slice_message.empty()); +} + TEST(zmq, read_write_multipart) { net::zmq::context context{zmq_init(1)}; diff --git a/tests/unit_tests/node_server.cpp b/tests/unit_tests/node_server.cpp index c92f70b97..b656c4858 100644 --- a/tests/unit_tests/node_server.cpp +++ b/tests/unit_tests/node_server.cpp @@ -54,7 +54,6 @@ public: bool init(const boost::program_options::variables_map& vm) {return true ;} bool deinit(){return true;} bool get_short_chain_history(std::list<crypto::hash>& ids) const { return true; } - bool get_stat_info(cryptonote::core_stat_info& st_inf) const {return true;} bool have_block(const crypto::hash& id) const {return true;} void get_blockchain_top(uint64_t& height, crypto::hash& top_id)const{height=0;top_id=crypto::null_hash;} bool handle_incoming_tx(const cryptonote::tx_blob_entry& tx_blob, cryptonote::tx_verification_context& tvc, cryptonote::relay_method tx_relay, bool relayed) { return true; } @@ -91,6 +90,8 @@ public: bool prune_blockchain(uint32_t pruning_seed = 0) { return true; } bool is_within_compiled_block_hash_area(uint64_t height) const { return false; } bool has_block_weights(uint64_t height, uint64_t nblocks) const { return false; } + bool get_txpool_complement(const std::vector<crypto::hash> &hashes, std::vector<cryptonote::blobdata> &txes) { return false; } + bool get_pool_transaction_hashes(std::vector<crypto::hash>& txs, bool include_unrelayed_txes = true) const { return false; } void stop() {} }; diff --git a/tests/unit_tests/test_tx_utils.cpp b/tests/unit_tests/test_tx_utils.cpp index d8d760b07..deb45594d 100644 --- a/tests/unit_tests/test_tx_utils.cpp +++ b/tests/unit_tests/test_tx_utils.cpp @@ -44,7 +44,7 @@ namespace TEST(parse_tx_extra, handles_empty_extra) { - std::vector<uint8_t> extra;; + std::vector<uint8_t> extra; std::vector<cryptonote::tx_extra_field> tx_extra_fields; ASSERT_TRUE(cryptonote::parse_tx_extra(extra, tx_extra_fields)); ASSERT_TRUE(tx_extra_fields.empty()); diff --git a/tests/unit_tests/wipeable_string.cpp b/tests/unit_tests/wipeable_string.cpp index 44e050c5c..9911bd6f4 100644 --- a/tests/unit_tests/wipeable_string.cpp +++ b/tests/unit_tests/wipeable_string.cpp @@ -182,6 +182,7 @@ TEST(wipeable_string, split) ASSERT_TRUE(check_split(" foo bar baz ", {"foo", "bar", "baz"})); ASSERT_TRUE(check_split(" foo bar baz", {"foo", "bar", "baz"})); ASSERT_TRUE(check_split("foo bar baz ", {"foo", "bar", "baz"})); + ASSERT_TRUE(check_split("\tfoo\n bar\r\nbaz", {"foo", "bar", "baz"})); } TEST(wipeable_string, parse_hexstr) diff --git a/tests/unit_tests/zmq_rpc.cpp b/tests/unit_tests/zmq_rpc.cpp new file mode 100644 index 000000000..af1f1608b --- /dev/null +++ b/tests/unit_tests/zmq_rpc.cpp @@ -0,0 +1,55 @@ +// Copyright (c) 2020, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <gtest/gtest.h> + +#include "rpc/message.h" +#include "serialization/json_object.h" + +TEST(ZmqFullMessage, InvalidRequest) +{ + EXPECT_THROW( + (cryptonote::rpc::FullMessage{"{\"jsonrpc\":\"2.0\",\"id\":0,\"params\":[]}", true}), + cryptonote::json::MISSING_KEY + ); + EXPECT_THROW( + (cryptonote::rpc::FullMessage{"{\"jsonrpc\":\"2.0\",\"id\":0,\"method\":3,\"params\":[]}", true}), + cryptonote::json::WRONG_TYPE + ); +} + +TEST(ZmqFullMessage, Request) +{ + static constexpr const char request[] = "{\"jsonrpc\":\"2.0\",\"id\":0,\"method\":\"foo\",\"params\":[]}"; + EXPECT_NO_THROW( + (cryptonote::rpc::FullMessage{request, true}) + ); + + cryptonote::rpc::FullMessage parsed{request, true}; + EXPECT_STREQ("foo", parsed.getRequestType().c_str()); +} |