diff options
Diffstat (limited to 'tests')
27 files changed, 1440 insertions, 80 deletions
diff --git a/tests/core_tests/block_validation.cpp b/tests/core_tests/block_validation.cpp index 566ec1440..55312bc15 100644 --- a/tests/core_tests/block_validation.cpp +++ b/tests/core_tests/block_validation.cpp @@ -640,3 +640,18 @@ bool gen_block_invalid_binary_format::check_all_blocks_purged(cryptonote::core& return true; } + +bool gen_block_late_v1_coinbase_tx::generate(std::vector<test_event_entry>& events) const +{ + BLOCK_VALIDATION_INIT_GENERATE(); + + block blk_1; + generator.construct_block_manually(blk_1, blk_0, miner_account, + test_generator::bf_major_ver | test_generator::bf_minor_ver, + HF_VERSION_MIN_V2_COINBASE_TX, HF_VERSION_MIN_V2_COINBASE_TX); + events.push_back(blk_1); + + DO_CALLBACK(events, "check_block_purged"); + + return true; +} diff --git a/tests/core_tests/block_validation.h b/tests/core_tests/block_validation.h index 4a65b029e..2393e1b01 100644 --- a/tests/core_tests/block_validation.h +++ b/tests/core_tests/block_validation.h @@ -206,3 +206,15 @@ struct gen_block_invalid_binary_format : public test_chain_unit_base private: size_t m_corrupt_blocks_begin_idx; }; + +struct gen_block_late_v1_coinbase_tx : public gen_block_verification_base<1> +{ + bool generate(std::vector<test_event_entry>& events) const; +}; +template<> +struct get_test_options<gen_block_late_v1_coinbase_tx> { + const std::pair<uint8_t, uint64_t> hard_forks[3] = {std::make_pair(1, 0), std::make_pair(HF_VERSION_MIN_V2_COINBASE_TX, 1), std::make_pair(0, 0)}; + const cryptonote::test_options test_options = { + hard_forks, 0 + }; +}; diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index cb35cfde2..4ee71466e 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -133,6 +133,7 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY(gen_block_has_invalid_tx); GENERATE_AND_PLAY(gen_block_is_too_big); GENERATE_AND_PLAY(gen_block_invalid_binary_format); // Takes up to 3 hours, if CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW == 500, up to 30 minutes, if CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW == 10 + GENERATE_AND_PLAY(gen_block_late_v1_coinbase_tx); // Transaction verification tests GENERATE_AND_PLAY(gen_tx_big_version); diff --git a/tests/functional_tests/CMakeLists.txt b/tests/functional_tests/CMakeLists.txt index fd49ba623..bc55da9e3 100644 --- a/tests/functional_tests/CMakeLists.txt +++ b/tests/functional_tests/CMakeLists.txt @@ -59,3 +59,7 @@ else() message(WARNING "functional_tests_rpc skipped, needs the 'requests' python module") set(CTEST_CUSTOM_TESTS_IGNORE ${CTEST_CUSTOM_TESTS_IGNORE} functional_tests_rpc) endif() + +add_test( + NAME check_missing_rpc_methods + COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/check_missing_rpc_methods.py" "${CMAKE_SOURCE_DIR}") diff --git a/tests/functional_tests/address_book.py b/tests/functional_tests/address_book.py new file mode 100755 index 000000000..8d8711ffc --- /dev/null +++ b/tests/functional_tests/address_book.py @@ -0,0 +1,304 @@ +#!/usr/bin/env python3 +#encoding=utf-8 + +# Copyright (c) 2019 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. + +"""Test wallet address book RPC +""" + +from __future__ import print_function +from framework.wallet import Wallet + +class AddressBookTest(): + def run_test(self): + self.create() + self.test_address_book() + + def create(self): + print('Creating wallet') + wallet = Wallet() + # close the wallet if any, will throw if none is loaded + try: wallet.close_wallet() + except: pass + seed = 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted' + res = wallet.restore_deterministic_wallet(seed = seed) + assert res.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm' + assert res.seed == seed + + def test_address_book(self): + print('Testing address book') + wallet = Wallet() + + # empty at start + res = wallet.get_address_book() + assert not 'entries' in res or (res.entries) == 0 + ok = False + try: wallet.get_address_book([0]) + except: ok = True + assert ok + ok = False + try: wallet.delete_address_book(0) + except: ok = True + assert ok + ok = False + try: wallet.edit_address_book(0, description = '') + except: ok = True + assert ok + + # add one + res = wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', description = 'self') + assert res.index == 0 + for get_all in [True, False]: + res = wallet.get_address_book() if get_all else wallet.get_address_book([0]) + assert len(res.entries) == 1 + e = res.entries[0] + assert e.index == 0 + assert e.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm' + assert e.payment_id == '' or e.payment_id == '0' * 16 or e.payment_id == '0' * 64 + assert e.description == 'self' + + # add a duplicate + res = wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', description = 'self') + assert res.index == 1 + res = wallet.get_address_book() + assert len(res.entries) == 2 + assert res.entries[0].index == 0 + assert res.entries[1].index == 1 + assert res.entries[0].address == res.entries[1].address + assert res.entries[0].payment_id == res.entries[1].payment_id + assert res.entries[0].description == res.entries[1].description + e = res.entries[1] + res = wallet.get_address_book([1]) + assert len(res.entries) == 1 + assert e == res.entries[0] + + # request (partially) out of range + ok = False + try: res = wallet.get_address_book[4, 2] + except: ok = True + assert ok + ok = False + try: res = wallet.get_address_book[0, 2] + except: ok = True + assert ok + ok = False + try: res = wallet.get_address_book[2, 0] + except: ok = True + assert ok + + # delete first + res = wallet.delete_address_book(0) + res = wallet.get_address_book() + assert len(res.entries) == 1 + assert res.entries[0].index == 0 + assert res.entries[0].address == e.address + assert res.entries[0].payment_id == e.payment_id + assert res.entries[0].description == e.description + + # delete (new) first + res = wallet.delete_address_book(0) + res = wallet.get_address_book() + assert not 'entries' in res or (res.entries) == 0 + + # add non-addresses + errors = 0 + try: wallet.add_address_book('', description = 'bad') + except: errors += 1 + try: wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm ', description = 'bad') + except: errors += 1 + try: wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDn', description = 'bad') + except: errors += 1 + try: wallet.add_address_book('9ujeXrjzf7bfeK3KZdCqnYaMwZVFuXemPU8Ubw335rj2FN1CdMiWNyFV3ksEfMFvRp9L9qum5UxkP5rN9aLcPxbH1au4WAB', description = 'bad') + except: errors += 1 + try: wallet.add_address_book('donate@example.com', description = 'bad') + except: errors += 1 + assert errors == 5 + res = wallet.get_address_book() + assert not 'entries' in res or len(res.entries) == 0 + + # openalias + res = wallet.add_address_book('donate@getmonero.org', description = 'dev fund') + assert res.index == 0 + res = wallet.get_address_book() + assert len(res.entries) == 1 + e = res.entries[0] + assert e.address == '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A' + assert e.description == 'dev fund' + + # UTF-8 + res = wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', description = u'あまやかす') + assert res.index == 1 + res = wallet.get_address_book([1]) + assert len(res.entries) == 1 + assert res.entries[0].description == u'あまやかす' + e = res.entries[0] + + # duplicate request + res = wallet.get_address_book([1, 1]) + assert len(res.entries) == 2 + assert res.entries[0] == e + assert res.entries[1] == e + + # payment IDs + res = wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', payment_id = '0' * 64) + assert res.index == 2 + ok = False + try: res = wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', payment_id = 'x' * 64) + except: ok = True + assert ok + ok = False + try: res = wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', payment_id = '0' * 65) + except: ok = True + assert ok + ok = False + try: res = wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', payment_id = '0' * 63) + except: ok = True + assert ok + ok = False + try: res = wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', payment_id = '0' * 16) + except: ok = True + assert ok + + # various address types + res = wallet.make_integrated_address() + integrated_address = res.integrated_address + integrated_address_payment_id = res.payment_id + ok = False + try: res = wallet.add_address_book(integrated_address, payment_id = '0' * 64) + except: ok = True + assert ok + res = wallet.add_address_book(integrated_address) + assert res.index == 3 + res = wallet.add_address_book('87KfgTZ8ER5D3Frefqnrqif11TjVsTPaTcp37kqqKMrdDRUhpJRczeR7KiBmSHF32UJLP3HHhKUDmEQyJrv2mV8yFDCq8eB') + assert res.index == 4 + + # get them back + res = wallet.get_address_book([0]) + assert len(res.entries) == 1 + assert res.entries[0].address == '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A' + assert res.entries[0].description == 'dev fund' + res = wallet.get_address_book([1]) + assert len(res.entries) == 1 + assert res.entries[0].address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm' + assert res.entries[0].description == u'あまやかす' + res = wallet.get_address_book([2]) + assert len(res.entries) == 1 + assert res.entries[0].address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm' + res = wallet.get_address_book([3]) + assert len(res.entries) == 1 + if False: # for now, the address book splits integrated addresses + assert res.entries[0].address == integrated_address + else: + assert res.entries[0].address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm' + assert res.entries[0].payment_id == integrated_address_payment_id + '0' * 48 + res = wallet.get_address_book([4]) + assert len(res.entries) == 1 + assert res.entries[0].address == '87KfgTZ8ER5D3Frefqnrqif11TjVsTPaTcp37kqqKMrdDRUhpJRczeR7KiBmSHF32UJLP3HHhKUDmEQyJrv2mV8yFDCq8eB' + + # edit + res = wallet.get_address_book([1]) + assert len(res.entries) == 1 + e = res.entries[0] + assert e.index == 1 + assert e.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm' + assert e.payment_id == '0' * 64 + assert e.description == u'あまやかす' + res = wallet.edit_address_book(1, payment_id = '1' * 64) + res = wallet.get_address_book([1]) + assert len(res.entries) == 1 + e = res.entries[0] + assert e.index == 1 + assert e.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm' + assert e.payment_id == '1' * 64 + assert e.description == u'あまやかす' + res = wallet.edit_address_book(1, description = '') + res = wallet.get_address_book([1]) + assert len(res.entries) == 1 + e = res.entries[0] + assert e.index == 1 + assert e.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm' + assert e.payment_id == '1' * 64 + assert e.description == '' + res = wallet.edit_address_book(1, description = 'えんしゅう') + res = wallet.get_address_book([1]) + assert len(res.entries) == 1 + e = res.entries[0] + assert e.index == 1 + assert e.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm' + assert e.payment_id == '1' * 64 + assert e.description == u'えんしゅう' + res = wallet.edit_address_book(1, address = '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A') + res = wallet.get_address_book([1]) + assert len(res.entries) == 1 + e = res.entries[0] + assert e.index == 1 + assert e.address == '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A' + assert e.payment_id == '1' * 64 + assert e.description == u'えんしゅう' + res = wallet.edit_address_book(1, payment_id = '') + res = wallet.get_address_book([1]) + assert len(res.entries) == 1 + e = res.entries[0] + assert e.index == 1 + assert e.address == '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A' + assert e.payment_id == '0' * 64 + assert e.description == u'えんしゅう' + ok = False + try: res = wallet.edit_address_book(1, address = '') + except: ok = True + assert ok + ok = False + try: res = wallet.edit_address_book(1, payment_id = 'asdnd') + except: ok = True + assert ok + ok = False + try: res = wallet.edit_address_book(1, address = 'address') + except: ok = True + assert ok + res = wallet.edit_address_book(1) + res = wallet.get_address_book([1]) + assert len(res.entries) == 1 + assert e == res.entries[0] + + # empty + wallet.delete_address_book(4) + wallet.delete_address_book(0) + res = wallet.get_address_book([0]) # entries above the deleted one collapse one slot up + assert len(res.entries) == 1 + assert res.entries[0].address == '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A' + assert res.entries[0].description == u'えんしゅう' + wallet.delete_address_book(2) + wallet.delete_address_book(0) + wallet.delete_address_book(0) + res = wallet.get_address_book() + assert not 'entries' in res or len(res.entries) == 0 + + +if __name__ == '__main__': + AddressBookTest().run_test() diff --git a/tests/functional_tests/blockchain.py b/tests/functional_tests/blockchain.py index 2c3f34c35..324af624a 100755 --- a/tests/functional_tests/blockchain.py +++ b/tests/functional_tests/blockchain.py @@ -53,7 +53,8 @@ class BlockchainTest(): def reset(self): print('Resetting blockchain') daemon = Daemon() - daemon.pop_blocks(1000) + res = daemon.get_height() + daemon.pop_blocks(res.height - 1) daemon.flush_txpool() def _test_generateblocks(self, blocks): @@ -330,6 +331,9 @@ class BlockchainTest(): for txid in [alt_blocks[0], alt_blocks[2], alt_blocks[4]]: assert len([chain for chain in res.chains if chain.block_hash == txid]) == 1 + print('Saving blockchain explicitely') + daemon.save_bc() + if __name__ == '__main__': BlockchainTest().run_test() diff --git a/tests/functional_tests/check_missing_rpc_methods.py b/tests/functional_tests/check_missing_rpc_methods.py new file mode 100644 index 000000000..6fadebf9b --- /dev/null +++ b/tests/functional_tests/check_missing_rpc_methods.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python + +from __future__ import print_function +import sys +import re + +USAGE = 'usage: check_untested_methods.py <rootdir>' +try: + rootdir = sys.argv[1] +except: + print(USAGE) + sys.exit(1) + +sys.path.insert(0, rootdir + '/utils/python-rpc') + +from framework import daemon +from framework import wallet + +modules = [ + { + 'name': 'daemon', + 'object': daemon.Daemon(), + 'path': rootdir + '/src/rpc/core_rpc_server.h', + 'ignore': [] + }, + { + 'name': 'wallet', + 'object': wallet.Wallet(), + 'path': rootdir + '/src/wallet/wallet_rpc_server.h', + 'ignore': [] + } +] + +error = False +for module in modules: + for line in open(module['path']).readlines(): + if 'MAP_URI_AUTO_JON2' in line or 'MAP_JON_RPC' in line: + match = re.search('.*\"(.*)\".*', line) + name = match.group(1) + if name in module['ignore'] or name.endswith('.bin'): + continue + if 'MAP_URI_AUTO_JON2' in line: + if not name.startswith('/'): + print('Error: %s does not start with /' % name) + error = True + name = name[1:] + if not hasattr(module['object'], name): + print('Error: %s API method %s does not have a matching function' % (module['name'], name)) + +sys.exit(1 if error else 0) diff --git a/tests/functional_tests/cold_signing.py b/tests/functional_tests/cold_signing.py index a722d8927..f915df77a 100755 --- a/tests/functional_tests/cold_signing.py +++ b/tests/functional_tests/cold_signing.py @@ -45,7 +45,8 @@ class ColdSigningTest(): def reset(self): print('Resetting blockchain') daemon = Daemon() - daemon.pop_blocks(1000) + res = daemon.get_height() + daemon.pop_blocks(res.height - 1) daemon.flush_txpool() def create(self, idx): diff --git a/tests/functional_tests/functional_tests_rpc.py b/tests/functional_tests/functional_tests_rpc.py index 77d0e4c4d..9043565c7 100755 --- a/tests/functional_tests/functional_tests_rpc.py +++ b/tests/functional_tests/functional_tests_rpc.py @@ -10,7 +10,7 @@ import string import os USAGE = 'usage: functional_tests_rpc.py <python> <srcdir> <builddir> [<tests-to-run> | all]' -DEFAULT_TESTS = ['bans', 'daemon_info', 'blockchain', 'wallet_address', 'integrated_address', 'mining', 'transfer', 'txpool', 'multisig', 'cold_signing', 'sign_message', 'proofs', 'get_output_distribution'] +DEFAULT_TESTS = ['address_book', 'bans', 'blockchain', 'cold_signing', 'daemon_info', 'get_output_distribution', 'integrated_address', 'mining', 'multisig', 'proofs', 'sign_message', 'transfer', 'txpool', 'uri', 'validate_address', 'wallet'] try: python = sys.argv[1] srcdir = sys.argv[2] @@ -36,9 +36,10 @@ except: N_MONERODS = 1 N_WALLETS = 4 +WALLET_DIRECTORY = builddir + "/functional-tests-directory" monerod_base = [builddir + "/bin/monerod", "--regtest", "--fixed-difficulty", "1", "--offline", "--no-igd", "--p2p-bind-port", "monerod_p2p_port", "--rpc-bind-port", "monerod_rpc_port", "--zmq-rpc-bind-port", "monerod_zmq_port", "--non-interactive", "--disable-dns-checkpoints", "--check-updates", "disabled", "--rpc-ssl", "disabled", "--log-level", "1"] -wallet_base = [builddir + "/bin/monero-wallet-rpc", "--wallet-dir", builddir + "/functional-tests-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", "--daemon-port", "18180", "--log-level", "1"] command_lines = [] processes = [] @@ -62,6 +63,8 @@ try: PYTHONPATH += ':' PYTHONPATH += srcdir + '/../../utils/python-rpc' os.environ['PYTHONPATH'] = PYTHONPATH + os.environ['WALLET_DIRECTORY'] = WALLET_DIRECTORY + os.environ['PYTHONIOENCODING'] = 'utf-8' for i in range(len(command_lines)): #print('Running: ' + str(command_lines[i])) processes.append(subprocess.Popen(command_lines[i], stdout = outputs[i])) @@ -133,6 +136,6 @@ else: if len(FAIL) == 0: print('Done, ' + str(len(PASS)) + '/' + str(len(tests)) + ' tests passed') else: - print('Done, ' + str(len(FAIL)) + '/' + str(len(tests)) + ' tests failed: ' + string.join(FAIL, ', ')) + print('Done, ' + str(len(FAIL)) + '/' + str(len(tests)) + ' tests failed: ' + ', '.join(FAIL)) sys.exit(0 if len(FAIL) == 0 else 1) diff --git a/tests/functional_tests/get_output_distribution.py b/tests/functional_tests/get_output_distribution.py index 93822e90a..077b094ba 100755 --- a/tests/functional_tests/get_output_distribution.py +++ b/tests/functional_tests/get_output_distribution.py @@ -44,7 +44,8 @@ class GetOutputDistributionTest(): def reset(self): print('Resetting blockchain') daemon = Daemon() - daemon.pop_blocks(1000) + res = daemon.get_height() + daemon.pop_blocks(res.height - 1) daemon.flush_txpool() def create(self): diff --git a/tests/functional_tests/mining.py b/tests/functional_tests/mining.py index 5c14d34fd..a08a45ad4 100755 --- a/tests/functional_tests/mining.py +++ b/tests/functional_tests/mining.py @@ -46,12 +46,15 @@ class MiningTest(): def run_test(self): self.reset() self.create() - self.mine() + self.mine(True) + self.mine(False) + self.submitblock() def reset(self): print('Resetting blockchain') daemon = Daemon() - daemon.pop_blocks(1000) + res = daemon.get_height() + daemon.pop_blocks(res.height - 1) daemon.flush_txpool() def create(self): @@ -62,8 +65,8 @@ class MiningTest(): except: pass res = wallet.restore_deterministic_wallet(seed = 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted') - def mine(self): - print("Test mining") + def mine(self, via_daemon): + print("Test mining via " + ("daemon" if via_daemon else "wallet")) daemon = Daemon() wallet = Wallet() @@ -76,7 +79,10 @@ class MiningTest(): res_status = daemon.mining_status() - res = daemon.start_mining('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', threads_count = 1) + if via_daemon: + res = daemon.start_mining('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', threads_count = 1) + else: + res = wallet.start_mining(threads_count = 1) res_status = daemon.mining_status() assert res_status.active == True @@ -101,7 +107,11 @@ class MiningTest(): timeout -= 1 assert timeout >= 0 - res = daemon.stop_mining() + if via_daemon: + res = daemon.stop_mining() + else: + res = wallet.stop_mining() + res_status = daemon.mining_status() assert res_status.active == False @@ -113,7 +123,10 @@ class MiningTest(): balance = res_getbalance.balance assert balance >= prev_balance + (new_height - prev_height) * 600000000000 - res = daemon.start_mining('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', threads_count = 1, do_background_mining = True) + if via_daemon: + res = daemon.start_mining('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', threads_count = 1, do_background_mining = True) + else: + res = wallet.start_mining(threads_count = 1, do_background_mining = True) res_status = daemon.mining_status() assert res_status.active == True assert res_status.threads_count == 1 @@ -122,10 +135,40 @@ class MiningTest(): assert res_status.block_reward >= 600000000000 # don't wait, might be a while if the machine is busy, which it probably is - res = daemon.stop_mining() + if via_daemon: + res = daemon.stop_mining() + else: + res = wallet.stop_mining() res_status = daemon.mining_status() assert res_status.active == False + def submitblock(self): + print("Test submitblock") + + daemon = Daemon() + res = daemon.get_height() + height = res.height + res = daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 5) + assert len(res.blocks) == 5 + hashes = res.blocks + blocks = [] + for block_hash in hashes: + res = daemon.getblock(hash = block_hash) + assert len(res.blob) > 0 and len(res.blob) % 2 == 0 + blocks.append(res.blob) + res = daemon.get_height() + assert res.height == height + 5 + res = daemon.pop_blocks(5) + res = daemon.get_height() + assert res.height == height + for i in range(len(hashes)): + block_hash = hashes[i] + assert len(block_hash) == 64 + res = daemon.submitblock(blocks[i]) + res = daemon.get_height() + assert res.height == height + i + 1 + assert res.hash == block_hash + if __name__ == '__main__': MiningTest().run_test() diff --git a/tests/functional_tests/multisig.py b/tests/functional_tests/multisig.py index b109acf91..e0d8b06a4 100755 --- a/tests/functional_tests/multisig.py +++ b/tests/functional_tests/multisig.py @@ -46,6 +46,8 @@ class MultisigTest(): self.mine('4ADHswEU3XBUee8pudBkZQd9beJainqNo1BQKkHJujAEPJyQrLj9U4dNm8HEMdHuWwKMFGzMUB3RCTvcTaW9kHpdRUDxgjW', 5) self.mine('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 60) + self.test_states() + self.create_multisig_wallets(2, 2, '493DsrfJPqiN3Suv9RcRDoZEbQtKZX1sNcGPA3GhkKYEEmivk8kjQrTdRdVc4ZbmzWJuE157z9NNUKmF2VDfdYDR3CziGMk') self.import_multisig_info([1, 0], 5) txid = self.transfer([1, 0]) @@ -79,7 +81,8 @@ class MultisigTest(): def reset(self): print('Resetting blockchain') daemon = Daemon() - daemon.pop_blocks(1000) + res = daemon.get_height() + daemon.pop_blocks(res.height - 1) daemon.flush_txpool() def mine(self, address, blocks): @@ -152,6 +155,72 @@ class MultisigTest(): assert res.threshold == M_threshold assert res.total == N_total + def test_states(self): + print('Testing multisig states') + seeds = [ + 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted', + 'peeled mixture ionic radar utopia puddle buying illness nuns gadget river spout cavernous bounced paradise drunk looking cottage jump tequila melting went winter adjust spout', + 'dilute gutter certain antics pamphlet macro enjoy left slid guarded bogeys upload nineteen bomb jubilee enhanced irritate turnip eggs swung jukebox loudly reduce sedan slid', + ] + info = [] + wallet = [None, None, None] + for i in range(3): + wallet[i] = Wallet(idx = i) + try: wallet[i].close_wallet() + except: pass + res = wallet[i].restore_deterministic_wallet(seed = seeds[i]) + res = wallet[i].is_multisig() + assert not res.multisig + res = wallet[i].prepare_multisig() + assert len(res.multisig_info) > 0 + info.append(res.multisig_info) + + for i in range(3): + ok = False + try: res = wallet[i].finalize_multisig(info) + except: ok = True + assert ok + ok = False + try: res = wallet[i].exchange_multisig_keys(info) + except: ok = True + assert ok + res = wallet[i].is_multisig() + assert not res.multisig + + res = wallet[0].make_multisig(info[0:2], 2) + res = wallet[0].is_multisig() + assert res.multisig + assert res.ready + + ok = False + try: res = wallet[0].finalize_multisig(info) + except: ok = True + assert ok + + ok = False + try: res = wallet[0].prepare_multisig() + except: ok = True + assert ok + + ok = False + try: res = wallet[0].make_multisig(info[0:2], 2) + except: ok = True + assert ok + + res = wallet[1].make_multisig(info, 2) + res = wallet[1].is_multisig() + assert res.multisig + assert not res.ready + + ok = False + try: res = wallet[1].prepare_multisig() + except: ok = True + assert ok + + ok = False + try: res = wallet[1].make_multisig(info[0:2], 2) + except: ok = True + assert ok def import_multisig_info(self, signers, expected_outputs): assert len(signers) >= 2 diff --git a/tests/functional_tests/proofs.py b/tests/functional_tests/proofs.py index 243929dc3..7beb3ec6e 100755 --- a/tests/functional_tests/proofs.py +++ b/tests/functional_tests/proofs.py @@ -44,12 +44,14 @@ class ProofsTest(): txid, tx_key, amount = self.transfer() self.check_tx_key(txid, tx_key, amount) self.check_tx_proof(txid, amount) + self.check_spend_proof(txid) self.check_reserve_proof() def reset(self): print('Resetting blockchain') daemon = Daemon() - daemon.pop_blocks(1000) + res = daemon.get_height() + daemon.pop_blocks(res.height - 1) daemon.flush_txpool() def mine(self, address, blocks): @@ -217,6 +219,40 @@ class ProofsTest(): except: ok = True assert ok or not res.good + def check_spend_proof(self, txid): + daemon = Daemon() + + print('Checking spend proof') + + self.wallet[0].refresh() + self.wallet[1].refresh() + + res = self.wallet[0].get_spend_proof(txid, message = 'foo') + assert len(res.signature) > 0 + signature = res.signature + res = self.wallet[1].check_spend_proof(txid, message = 'foo', signature = signature) + assert res.good + + res = self.wallet[0].get_spend_proof(txid, message = 'foobar') + assert len(res.signature) > 0 + signature2 = res.signature + res = self.wallet[1].check_spend_proof(txid, message = 'foobar', signature = signature2) + assert res.good + + ok = False + try: res = self.wallet[1].check_spend_proof('0' * 64, message = 'foo', signature = signature) + except: ok = True + assert ok or not res.good + + ok = False + try: res = self.wallet[1].check_spend_proof(txid, message = 'bar', signature = signature) + except: ok = True + assert ok or not res.good + + ok = False + try: res = self.wallet[1].check_spend_proof(txid, message = 'foo', signature = signature2) + except: ok = True + assert ok or not res.good def check_reserve_proof(self): daemon = Daemon() diff --git a/tests/functional_tests/speed.py b/tests/functional_tests/speed.py index ed1e332e9..71be785b8 100755 --- a/tests/functional_tests/speed.py +++ b/tests/functional_tests/speed.py @@ -47,14 +47,27 @@ from framework.wallet import Wallet class SpeedTest(): - def set_test_params(self): - self.num_nodes = 1 + def reset(self): + print('Resetting blockchain') + daemon = Daemon() + res = daemon.get_height() + daemon.pop_blocks(res.height - 1) + daemon.flush_txpool() def run_test(self): + self.reset() + daemon = Daemon() wallet = Wallet() - destinations = wallet.make_uniform_destinations('44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A',1,3) + # close the wallet if any, will throw if none is loaded + try: wallet.close_wallet() + except: pass + wallet.restore_deterministic_wallet('velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted') + + destinations = [] + for i in range(3): + destinations.append({"amount":1,"address":'44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A'}) self._test_speed_generateblocks(daemon=daemon, blocks=70) for i in range(1, 10): @@ -69,7 +82,6 @@ class SpeedTest(): start = time.time() res = daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', blocks) - # wallet seed: velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted print('generating ', blocks, 'blocks took: ', time.time() - start, 'seconds') @@ -77,7 +89,7 @@ class SpeedTest(): print('Test speed of transfer') start = time.time() - destinations = wallet.make_uniform_destinations('44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A',1) + destinations = [{"amount":1,"address":'44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A'}] res = wallet.transfer_split(destinations) print('generating tx took: ', time.time() - start, 'seconds') diff --git a/tests/functional_tests/transactions_flow_test.cpp b/tests/functional_tests/transactions_flow_test.cpp index 81004da93..218590a27 100644 --- a/tests/functional_tests/transactions_flow_test.cpp +++ b/tests/functional_tests/transactions_flow_test.cpp @@ -173,7 +173,7 @@ bool transactions_flow_test(std::string& working_folder, //wait for money, until balance will have enough money w1.refresh(true, blocks_fetched, received_money, ok); - while(w1.unlocked_balance(0) < amount_to_transfer) + while(w1.unlocked_balance(0, true) < amount_to_transfer) { misc_utils::sleep_no_w(1000); w1.refresh(true, blocks_fetched, received_money, ok); @@ -186,7 +186,7 @@ bool transactions_flow_test(std::string& working_folder, { tools::wallet2::transfer_container incoming_transfers; w1.get_transfers(incoming_transfers); - if(incoming_transfers.size() > FIRST_N_TRANSFERS && get_money_in_first_transfers(incoming_transfers, FIRST_N_TRANSFERS) < w1.unlocked_balance(0) ) + if(incoming_transfers.size() > FIRST_N_TRANSFERS && get_money_in_first_transfers(incoming_transfers, FIRST_N_TRANSFERS) < w1.unlocked_balance(0, true) ) { //lets go! size_t count = 0; @@ -221,7 +221,7 @@ bool transactions_flow_test(std::string& working_folder, for(i = 0; i != transactions_count; i++) { uint64_t amount_to_tx = (amount_to_transfer - transfered_money) > transfer_size ? transfer_size: (amount_to_transfer - transfered_money); - while(w1.unlocked_balance(0) < amount_to_tx + TEST_FEE) + while(w1.unlocked_balance(0, true) < amount_to_tx + TEST_FEE) { misc_utils::sleep_no_w(1000); LOG_PRINT_L0("not enough money, waiting for cashback or mining"); @@ -270,7 +270,7 @@ bool transactions_flow_test(std::string& working_folder, misc_utils::sleep_no_w(DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN*1000);//wait two blocks before sync on another wallet on another daemon } - uint64_t money_2 = w2.balance(0); + uint64_t money_2 = w2.balance(0, true); if(money_2 == transfered_money) { MGINFO_GREEN("-----------------------FINISHING TRANSACTIONS FLOW TEST OK-----------------------"); diff --git a/tests/functional_tests/transfer.py b/tests/functional_tests/transfer.py index 7ebda6ebd..b4264f72d 100755 --- a/tests/functional_tests/transfer.py +++ b/tests/functional_tests/transfer.py @@ -44,14 +44,20 @@ class TransferTest(): self.mine() self.transfer() self.check_get_bulk_payments() + self.check_get_payments() self.check_double_spend_detection() + self.sweep_dust() self.sweep_single() self.check_destinations() + self.check_tx_notes() + self.check_rescan() + self.check_is_key_image_spent() def reset(self): print('Resetting blockchain') daemon = Daemon() - daemon.pop_blocks(1000) + res = daemon.get_height() + daemon.pop_blocks(res.height - 1) daemon.flush_txpool() def create(self): @@ -114,7 +120,7 @@ class TransferTest(): except: ok = True assert ok - res = self.wallet[0].transfer([dst], ring_size = 11, payment_id = payment_id, get_tx_key = False) + res = self.wallet[0].transfer([dst], ring_size = 11, payment_id = payment_id, get_tx_key = False, get_tx_hex = True) assert len(res.tx_hash) == 32*2 txid = res.tx_hash assert len(res.tx_key) == 0 @@ -122,12 +128,19 @@ class TransferTest(): amount = res.amount assert res.fee > 0 fee = res.fee - assert len(res.tx_blob) == 0 + assert len(res.tx_blob) > 0 + blob_size = len(res.tx_blob) // 2 assert len(res.tx_metadata) == 0 assert len(res.multisig_txset) == 0 assert len(res.unsigned_txset) == 0 unsigned_txset = res.unsigned_txset + res = daemon.get_fee_estimate(10) + assert res.fee > 0 + assert res.quantization_mask > 0 + expected_fee = (res.fee * 1 * blob_size + res.quantization_mask - 1) // res.quantization_mask * res.quantization_mask + assert abs(1 - fee / expected_fee) < 0.01 + self.wallet[0].refresh() res = daemon.get_info() @@ -523,6 +536,28 @@ class TransferTest(): res = self.wallet[1].get_bulk_payments(["1111111122222222"]) assert len(res.payments) >= 1 # we have one of these + def check_get_payments(self): + print('Checking get_payments') + + daemon = Daemon() + res = daemon.get_info() + height = res.height + + self.wallet[0].refresh() + self.wallet[1].refresh() + + res = self.wallet[0].get_payments('1234500000012345abcde00000abcdeff1234500000012345abcde00000abcde') + assert 'payments' not in res or len(res.payments) == 0 + + res = self.wallet[1].get_payments('1234500000012345abcde00000abcdeff1234500000012345abcde00000abcde') + assert len(res.payments) >= 2 + + res = self.wallet[1].get_payments('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff') + assert 'payments' not in res or len(res.payments) == 0 + + res = self.wallet[1].get_payments(payment_id = '1111111122222222' + '0'*48) + assert len(res.payments) >= 1 # one tx to integrated address + def check_double_spend_detection(self): print('Checking double spend detection') txes = [[None, None], [None, None]] @@ -583,6 +618,13 @@ class TransferTest(): assert tx.in_pool assert tx.double_spend_seen + def sweep_dust(self): + print("Sweeping dust") + daemon = Daemon() + 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() @@ -603,11 +645,19 @@ class TransferTest(): self.wallet[0].refresh() res = self.wallet[0].get_balance() balance = res.balance - res = self.wallet[0].incoming_transfers(transfer_type = 'all') + res = daemon.is_key_image_spent([ki]) + assert len(res.spent_status) == 1 + assert res.spent_status[0] == 0 res = self.wallet[0].sweep_single('44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW', key_image = ki) assert len(res.tx_hash) == 64 tx_hash = res.tx_hash + res = daemon.is_key_image_spent([ki]) + assert len(res.spent_status) == 1 + assert res.spent_status[0] == 2 daemon.generateblocks('44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW', 1) + res = daemon.is_key_image_spent([ki]) + assert len(res.spent_status) == 1 + assert res.spent_status[0] == 1 self.wallet[0].refresh() res = self.wallet[0].get_balance() new_balance = res.balance @@ -687,7 +737,97 @@ class TransferTest(): daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 1) self.wallet[0].refresh() + def check_tx_notes(self): + daemon = Daemon() + + print('Testing tx notes') + res = self.wallet[0].get_transfers() + assert len(res['in']) > 0 + in_txid = res['in'][0].txid + assert len(res['out']) > 0 + out_txid = res['out'][0].txid + res = self.wallet[0].get_tx_notes([in_txid, out_txid]) + assert res.notes == ['', ''] + res = self.wallet[0].set_tx_notes([in_txid, out_txid], ['in txid', 'out txid']) + res = self.wallet[0].get_tx_notes([in_txid, out_txid]) + assert res.notes == ['in txid', 'out txid'] + res = self.wallet[0].get_tx_notes([out_txid, in_txid]) + assert res.notes == ['out txid', 'in txid'] + + def check_rescan(self): + daemon = Daemon() + + print('Testing rescan_spent') + res = self.wallet[0].incoming_transfers(transfer_type = 'all') + transfers = res.transfers + res = self.wallet[0].rescan_spent() + res = self.wallet[0].incoming_transfers(transfer_type = 'all') + assert transfers == res.transfers + + for hard in [False, True]: + print('Testing %s rescan_blockchain' % ('hard' if hard else 'soft')) + res = self.wallet[0].incoming_transfers(transfer_type = 'all') + transfers = res.transfers + res = self.wallet[0].get_transfers() + t_in = res['in'] + t_out = res.out + res = self.wallet[0].rescan_blockchain(hard = hard) + res = self.wallet[0].incoming_transfers(transfer_type = 'all') + assert transfers == res.transfers + res = self.wallet[0].get_transfers() + assert t_in == res['in'] + # some information can not be recovered for out txes + unrecoverable_fields = ['payment_id', 'destinations', 'note'] + old_t_out = [] + for x in t_out: + e = {} + for k in x.keys(): + if not k in unrecoverable_fields: + e[k] = x[k] + old_t_out.append(e) + new_t_out = [] + for x in res.out: + e = {} + for k in x.keys(): + if not k in unrecoverable_fields: + e[k] = x[k] + new_t_out.append(e) + 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() + print('Testing is_key_image_spent') + res = self.wallet[0].incoming_transfers(transfer_type = 'all') + transfers = res.transfers + ki = [x.key_image for x in transfers] + expected = [1 if x.spent else 0 for x in transfers] + res = daemon.is_key_image_spent(ki) + assert res.spent_status == expected + + res = self.wallet[0].incoming_transfers(transfer_type = 'available') + transfers = res.transfers + ki = [x.key_image for x in transfers] + expected = [0 for x in transfers] + res = daemon.is_key_image_spent(ki) + assert res.spent_status == expected + + res = self.wallet[0].incoming_transfers(transfer_type = 'unavailable') + transfers = res.transfers + ki = [x.key_image for x in transfers] + expected = [1 for x in transfers] + res = daemon.is_key_image_spent(ki) + assert res.spent_status == expected + + ki = [ki[-1]] * 5 + expected = [1] * len(ki) + res = daemon.is_key_image_spent(ki) + assert res.spent_status == expected + + ki = ['2'*64, '1'*64] + expected = [0, 0] + res = daemon.is_key_image_spent(ki) + assert res.spent_status == expected if __name__ == '__main__': diff --git a/tests/functional_tests/txpool.py b/tests/functional_tests/txpool.py index b6af4c84f..27ae89764 100755 --- a/tests/functional_tests/txpool.py +++ b/tests/functional_tests/txpool.py @@ -46,7 +46,8 @@ class TransferTest(): def reset(self): print('Resetting blockchain') daemon = Daemon() - daemon.pop_blocks(1000) + res = daemon.get_height() + daemon.pop_blocks(res.height - 1) daemon.flush_txpool() def create(self): @@ -81,6 +82,26 @@ class TransferTest(): return txes + def check_empty_pool(self): + daemon = 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() + assert res.pool_stats.bytes_total == 0 + assert res.pool_stats.bytes_min == 0 + assert res.pool_stats.bytes_max == 0 + assert res.pool_stats.bytes_med == 0 + assert res.pool_stats.fee_total == 0 + assert res.pool_stats.oldest == 0 + assert res.pool_stats.txs_total == 0 + assert res.pool_stats.num_failing == 0 + assert res.pool_stats.num_10m == 0 + assert res.pool_stats.num_not_relayed == 0 + assert res.pool_stats.histo_98pc == 0 + assert not 'histo' in res.pool_stats or len(res.pool_stats.histo) == 0 + assert res.pool_stats.num_double_spends == 0 + def check_txpool(self): daemon = Daemon() wallet = Wallet() @@ -89,6 +110,8 @@ class TransferTest(): height = res.height txpool_size = res.tx_pool_size + self.check_empty_pool() + txes = self.create_txes('46r4nYSevkfBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCLGwTerK', 5) res = daemon.get_info() @@ -97,6 +120,10 @@ class TransferTest(): res = daemon.get_transaction_pool() assert len(res.transactions) == txpool_size + total_bytes = 0 + total_fee = 0 + min_bytes = 99999999999999 + max_bytes = 0 for txid in txes.keys(): x = [x for x in res.transactions if x.id_hash == txid] assert len(x) == 1 @@ -110,9 +137,26 @@ class TransferTest(): assert x.fee == txes[txid].fee assert x.tx_blob == txes[txid].tx_blob + total_bytes += x.blob_size + total_fee += x.fee + min_bytes = min(min_bytes, x.blob_size) + max_bytes = max(max_bytes, x.blob_size) + res = daemon.get_transaction_pool_hashes() assert sorted(res.tx_hashes) == sorted(txes.keys()) + res = daemon.get_transaction_pool_stats() + assert res.pool_stats.bytes_total == total_bytes + assert res.pool_stats.bytes_min == min_bytes + assert res.pool_stats.bytes_max == max_bytes + assert res.pool_stats.bytes_med >= min_bytes and res.pool_stats.bytes_med <= max_bytes + assert res.pool_stats.fee_total == total_fee + assert res.pool_stats.txs_total == len(txes) + assert res.pool_stats.num_failing == 0 + assert res.pool_stats.num_10m == 0 + assert res.pool_stats.num_not_relayed == 0 + assert res.pool_stats.num_double_spends == 0 + print('Flushing 2 transactions') txes_keys = list(txes.keys()) daemon.flush_txpool([txes_keys[1], txes_keys[3]]) @@ -127,6 +171,42 @@ class TransferTest(): res = daemon.get_transaction_pool_hashes() assert sorted(res.tx_hashes) == sorted(new_keys) + res = daemon.get_transaction_pool() + assert len(res.transactions) == len(new_keys) + total_bytes = 0 + total_fee = 0 + min_bytes = 99999999999999 + max_bytes = 0 + for txid in new_keys: + x = [x for x in res.transactions if x.id_hash == txid] + assert len(x) == 1 + x = x[0] + assert x.kept_by_block == False + assert x.last_failed_id_hash == '0'*64 + assert x.double_spend_seen == False + assert x.weight >= x.blob_size + + assert x.blob_size * 2 == len(txes[txid].tx_blob) + assert x.fee == txes[txid].fee + assert x.tx_blob == txes[txid].tx_blob + + total_bytes += x.blob_size + total_fee += x.fee + min_bytes = min(min_bytes, x.blob_size) + max_bytes = max(max_bytes, x.blob_size) + + res = daemon.get_transaction_pool_stats() + assert res.pool_stats.bytes_total == total_bytes + assert res.pool_stats.bytes_min == min_bytes + assert res.pool_stats.bytes_max == max_bytes + assert res.pool_stats.bytes_med >= min_bytes and res.pool_stats.bytes_med <= max_bytes + assert res.pool_stats.fee_total == total_fee + assert res.pool_stats.txs_total == len(new_keys) + assert res.pool_stats.num_failing == 0 + assert res.pool_stats.num_10m == 0 + assert res.pool_stats.num_not_relayed == 0 + assert res.pool_stats.num_double_spends == 0 + print('Flushing unknown transactions') unknown_txids = ['1'*64, '2'*64, '3'*64] daemon.flush_txpool(unknown_txids) @@ -140,6 +220,8 @@ class TransferTest(): res = daemon.get_transaction_pool_hashes() assert not 'tx_hashes' in res or len(res.tx_hashes) == 0 + self.check_empty_pool() + print('Popping block') daemon.pop_blocks(1) res = daemon.get_transaction_pool_hashes() @@ -159,6 +241,9 @@ class TransferTest(): assert x.fee == txes[txid].fee assert x.tx_blob == txes[txid].tx_blob + daemon.flush_txpool() + self.check_empty_pool() + if __name__ == '__main__': TransferTest().run_test() diff --git a/tests/functional_tests/uri.py b/tests/functional_tests/uri.py new file mode 100755 index 000000000..f759b316a --- /dev/null +++ b/tests/functional_tests/uri.py @@ -0,0 +1,234 @@ +#!/usr/bin/env python3 +#encoding=utf-8 + +# Copyright (c) 2019 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. + +"""Test URI RPC +""" + +from __future__ import print_function +try: + from urllib import quote as urllib_quote +except: + from urllib.parse import quote as urllib_quote + +from framework.wallet import Wallet + +class URITest(): + def run_test(self): + self.create() + self.test_monero_uri() + + def create(self): + print('Creating wallet') + wallet = Wallet() + # close the wallet if any, will throw if none is loaded + try: wallet.close_wallet() + except: pass + seed = 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted' + res = wallet.restore_deterministic_wallet(seed = seed) + assert res.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm' + assert res.seed == seed + + def test_monero_uri(self): + print('Testing monero: URI') + wallet = Wallet() + + utf8string = [u'えんしゅう', u'あまやかす'] + quoted_utf8string = [urllib_quote(x.encode('utf8')) for x in utf8string] + + ok = False + try: res = wallet.make_uri() + except: ok = True + assert ok + ok = False + try: res = wallet.make_uri(address = '') + except: ok = True + assert ok + ok = False + try: res = wallet.make_uri(address = 'kjshdkj') + except: ok = True + assert ok + + for address in [ + '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', + '4BxSHvcgTwu25WooY4BVmgdcKwZu5EksVZSZkDd6ooxSVVqQ4ubxXkhLF6hEqtw96i9cf3cVfLw8UWe95bdDKfRQeYtPwLm1Jiw7AKt2LY', + '8AsN91rznfkBGTY8psSNkJBg9SZgxxGGRUhGwRptBhgr5XSQ1XzmA9m8QAnoxydecSh5aLJXdrgXwTDMMZ1AuXsN1EX5Mtm' + ]: + res = wallet.make_uri(address = address) + assert res.uri == 'monero:' + address + res = wallet.parse_uri(res.uri) + assert res.uri.address == address + assert res.uri.payment_id == '' + assert res.uri.amount == 0 + assert res.uri.tx_description == '' + assert res.uri.recipient_name == '' + assert not 'unknown_parameters' in res or len(res.unknown_parameters) == 0 + res = wallet.make_uri(address = address, amount = 11000000000) + assert res.uri == 'monero:' + address + '?tx_amount=0.011' or res.uri == 'monero:' + address + '?tx_amount=0.011000000000' + res = wallet.parse_uri(res.uri) + assert res.uri.address == address + assert res.uri.payment_id == '' + assert res.uri.amount == 11000000000 + assert res.uri.tx_description == '' + assert res.uri.recipient_name == '' + assert not 'unknown_parameters' in res or len(res.unknown_parameters) == 0 + + address = '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm' + + res = wallet.make_uri(address = address, tx_description = utf8string[0]) + assert res.uri == 'monero:' + address + '?tx_description=' + quoted_utf8string[0] + res = wallet.parse_uri(res.uri) + assert res.uri.address == address + assert res.uri.payment_id == '' + assert res.uri.amount == 0 + assert res.uri.tx_description == utf8string[0] + assert res.uri.recipient_name == '' + assert not 'unknown_parameters' in res or len(res.unknown_parameters) == 0 + + res = wallet.make_uri(address = address, recipient_name = utf8string[0]) + assert res.uri == 'monero:' + address + '?recipient_name=' + quoted_utf8string[0] + res = wallet.parse_uri(res.uri) + assert res.uri.address == address + assert res.uri.payment_id == '' + assert res.uri.amount == 0 + assert res.uri.tx_description == '' + assert res.uri.recipient_name == utf8string[0] + assert not 'unknown_parameters' in res or len(res.unknown_parameters) == 0 + + res = wallet.make_uri(address = address, recipient_name = utf8string[0], tx_description = utf8string[1]) + assert res.uri == 'monero:' + address + '?recipient_name=' + quoted_utf8string[0] + '&tx_description=' + quoted_utf8string[1] + res = wallet.parse_uri(res.uri) + assert res.uri.address == address + assert res.uri.payment_id == '' + assert res.uri.amount == 0 + assert res.uri.tx_description == utf8string[1] + assert res.uri.recipient_name == utf8string[0] + assert not 'unknown_parameters' in res or len(res.unknown_parameters) == 0 + + res = wallet.make_uri(address = address, recipient_name = utf8string[0], tx_description = utf8string[1], amount = 1000000000000) + assert res.uri == 'monero:' + address + '?tx_amount=1.000000000000&recipient_name=' + quoted_utf8string[0] + '&tx_description=' + quoted_utf8string[1] + res = wallet.parse_uri(res.uri) + assert res.uri.address == address + assert res.uri.payment_id == '' + assert res.uri.amount == 1000000000000 + assert res.uri.tx_description == utf8string[1] + assert res.uri.recipient_name == utf8string[0] + assert not 'unknown_parameters' in res or len(res.unknown_parameters) == 0 + + res = wallet.make_uri(address = address, recipient_name = utf8string[0], tx_description = utf8string[1], amount = 1000000000000, payment_id = '1' * 64) + assert res.uri == 'monero:' + address + '?tx_payment_id=' + '1' * 64 + '&tx_amount=1.000000000000&recipient_name=' + quoted_utf8string[0] + '&tx_description=' + quoted_utf8string[1] + res = wallet.parse_uri(res.uri) + assert res.uri.address == address + assert res.uri.payment_id == '1' * 64 + assert res.uri.amount == 1000000000000 + assert res.uri.tx_description == utf8string[1] + assert res.uri.recipient_name == utf8string[0] + assert not 'unknown_parameters' in res or len(res.unknown_parameters) == 0 + + # spaces must be encoded as %20 + res = wallet.make_uri(address = address, tx_description = ' ' + utf8string[1] + ' ' + utf8string[0] + ' ', amount = 1000000000000) + assert res.uri == 'monero:' + address + '?tx_amount=1.000000000000&tx_description=%20' + quoted_utf8string[1] + '%20' + quoted_utf8string[0] + '%20' + res = wallet.parse_uri(res.uri) + assert res.uri.address == address + assert res.uri.payment_id == '' + assert res.uri.amount == 1000000000000 + assert res.uri.tx_description == ' ' + utf8string[1] + ' ' + utf8string[0] + ' ' + assert res.uri.recipient_name == '' + assert not 'unknown_parameters' in res or len(res.unknown_parameters) == 0 + + # the example from the docs + res = wallet.parse_uri('monero:46BeWrHpwXmHDpDEUmZBWZfoQpdc6HaERCNmx1pEYL2rAcuwufPN9rXHHtyUA4QVy66qeFQkn6sfK8aHYjA3jk3o1Bv16em?tx_amount=239.39014&tx_description=donation') + assert res.uri.address == '46BeWrHpwXmHDpDEUmZBWZfoQpdc6HaERCNmx1pEYL2rAcuwufPN9rXHHtyUA4QVy66qeFQkn6sfK8aHYjA3jk3o1Bv16em' + assert res.uri.amount == 239390140000000 + assert res.uri.tx_description == 'donation' + assert res.uri.recipient_name == '' + assert res.uri.payment_id == '' + assert not 'unknown_parameters' in res or len(res.unknown_parameters) == 0 + + # malformed/invalid + for uri in [ + '', + ':', + 'monero', + 'notmonero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', + 'MONERO:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', + 'MONERO::42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', + 'monero:', + 'monero:badaddress', + 'monero:tx_amount=10', + 'monero:?tx_amount=10', + 'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm?tx_amount=-1', + 'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm?tx_amount=1e12', + 'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm?tx_amount=+12', + 'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm?tx_amount=1+2', + 'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm?tx_amount=A', + 'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm?tx_amount=0x2', + 'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm?tx_amount=222222222222222222222', + 'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDn?tx_amount=10', + 'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm&', + 'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm&tx_amount', + 'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm&tx_amount=', + 'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm&tx_amount=10=', + 'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm&tx_amount=10=&', + 'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm&tx_amount=10=&foo=bar', + 'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm?tx_amount=10&tx_amount=20', + 'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm?tx_payment_id=1111111111111111', + 'monero:4BxSHvcgTwu25WooY4BVmgdcKwZu5EksVZSZkDd6ooxSVVqQ4ubxXkhLF6hEqtw96i9cf3cVfLw8UWe95bdDKfRQeYtPwLm1Jiw7AKt2LY?tx_payment_id=' + '1' * 64, + 'monero:9ujeXrjzf7bfeK3KZdCqnYaMwZVFuXemPU8Ubw335rj2FN1CdMiWNyFV3ksEfMFvRp9L9qum5UxkP5rN9aLcPxbH1au4WAB', + 'monero:5K8mwfjumVseCcQEjNbf59Um6R9NfVUNkHTLhhPCmNvgDLVS88YW5tScnm83rw9mfgYtchtDDTW5jEfMhygi27j1QYphX38hg6m4VMtN29', + 'monero:7A1Hr63MfgUa8pkWxueD5xBqhQczkusYiCMYMnJGcGmuQxa7aDBxN1G7iCuLCNB3VPeb2TW7U9FdxB27xKkWKfJ8VhUZthF', + ]: + ok = False + try: res = wallet.parse_uri(uri) + except: ok = True + assert ok, res + + # unknown parameters but otherwise valid + res = wallet.parse_uri('monero:' + address + '?tx_amount=239.39014&foo=bar') + assert res.uri.address == address + assert res.uri.amount == 239390140000000 + assert res.unknown_parameters == ['foo=bar'], res + res = wallet.parse_uri('monero:' + address + '?tx_amount=239.39014&foo=bar&baz=quux') + assert res.uri.address == address + assert res.uri.amount == 239390140000000 + assert res.unknown_parameters == ['foo=bar', 'baz=quux'], res + res = wallet.parse_uri('monero:' + address + '?tx_amount=239.39014&%20=%20') + assert res.uri.address == address + assert res.uri.amount == 239390140000000 + assert res.unknown_parameters == ['%20=%20'], res + res = wallet.parse_uri('monero:' + address + '?tx_amount=239.39014&unknown=' + quoted_utf8string[0]) + assert res.uri.address == address + assert res.uri.amount == 239390140000000 + assert res.unknown_parameters == [u'unknown=' + quoted_utf8string[0]], res + + + +if __name__ == '__main__': + URITest().run_test() diff --git a/tests/functional_tests/validate_address.py b/tests/functional_tests/validate_address.py index 58748b0a2..7c3d8abfa 100755 --- a/tests/functional_tests/validate_address.py +++ b/tests/functional_tests/validate_address.py @@ -28,11 +28,10 @@ # 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. -import time - """Test address validation RPC calls """ +from __future__ import print_function from framework.wallet import Wallet class AddressValidationTest(): diff --git a/tests/functional_tests/wallet_address.py b/tests/functional_tests/wallet.py index eda52b432..5bb3ec80a 100755 --- a/tests/functional_tests/wallet_address.py +++ b/tests/functional_tests/wallet.py @@ -29,31 +29,54 @@ # 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. -"""Test transaction creation RPC calls - -Test the following RPCs: - - [TODO: many tests still need to be written] - +"""Test basic wallet functionality """ from __future__ import print_function +import sys +import os +import errno + from framework.wallet import Wallet from framework.daemon import Daemon -class WalletAddressTest(): +class WalletTest(): def run_test(self): self.reset() self.create() self.check_main_address() self.check_keys() self.create_subaddresses() + self.tags() + self.attributes() self.open_close() self.languages() + self.change_password() + self.store() + + def remove_file(self, name): + WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY'] + assert WALLET_DIRECTORY != '' + try: + os.unlink(WALLET_DIRECTORY + '/' + name) + except OSError as e: + if e.errno != errno.ENOENT: + raise + + def remove_wallet_files(self, name): + for suffix in ['', '.keys']: + self.remove_file(name + suffix) + + def file_exists(self, name): + WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY'] + assert WALLET_DIRECTORY != '' + return os.path.isfile(WALLET_DIRECTORY + '/' + name) def reset(self): print('Resetting blockchain') daemon = Daemon() - daemon.pop_blocks(1000) + res = daemon.get_height() + daemon.pop_blocks(res.height - 1) daemon.flush_txpool() def create(self): @@ -158,6 +181,103 @@ class WalletAddressTest(): res = wallet.get_address_index('82pP87g1Vkd3LUMssBCumk3MfyEsFqLAaGDf6oxddu61EgSFzt8gCwUD4tr3kp9TUfdPs2CnpD7xLZzyC1Ei9UsW3oyCWDf') assert res.index == {'major': 1, 'minor': 0} + res = wallet.label_account(0, "main") + + def tags(self): + print('Testing tags') + wallet = Wallet() + res = wallet.get_account_tags() + assert not 'account_tags' in res or len(res.account_tags) == 0 + ok = False + try: res = wallet.get_accounts('tag') + except: ok = True + assert ok or not 'subaddress_accounts' in res or res.subaddress_accounts == 0 + wallet.tag_accounts('tag0', [1]) + res = wallet.get_account_tags() + assert len(res.account_tags) == 1 + assert res.account_tags[0].tag == 'tag0' + assert res.account_tags[0].label == '' + assert res.account_tags[0].accounts == [1] + res = wallet.get_accounts('tag0') + assert len(res.subaddress_accounts) == 1 + assert res.subaddress_accounts[0].account_index == 1 + assert res.subaddress_accounts[0].base_address == '82pP87g1Vkd3LUMssBCumk3MfyEsFqLAaGDf6oxddu61EgSFzt8gCwUD4tr3kp9TUfdPs2CnpD7xLZzyC1Ei9UsW3oyCWDf' + assert res.subaddress_accounts[0].balance == 0 + assert res.subaddress_accounts[0].unlocked_balance == 0 + assert res.subaddress_accounts[0].label == 'idx1_new' + assert res.subaddress_accounts[0].tag == 'tag0' + wallet.untag_accounts([0]) + res = wallet.get_account_tags() + assert len(res.account_tags) == 1 + assert res.account_tags[0].tag == 'tag0' + assert res.account_tags[0].label == '' + assert res.account_tags[0].accounts == [1] + wallet.untag_accounts([1]) + res = wallet.get_account_tags() + assert not 'account_tags' in res or len(res.account_tags) == 0 + wallet.tag_accounts('tag0', [0]) + wallet.tag_accounts('tag1', [1]) + res = wallet.get_account_tags() + assert len(res.account_tags) == 2 + x = [x for x in res.account_tags if x.tag == 'tag0'] + assert len(x) == 1 + assert x[0].tag == 'tag0' + assert x[0].label == '' + assert x[0].accounts == [0] + x = [x for x in res.account_tags if x.tag == 'tag1'] + assert len(x) == 1 + assert x[0].tag == 'tag1' + assert x[0].label == '' + assert x[0].accounts == [1] + wallet.tag_accounts('tagA', [0, 1]) + res = wallet.get_account_tags() + assert len(res.account_tags) == 1 + assert res.account_tags[0].tag == 'tagA' + assert res.account_tags[0].label == '' + assert res.account_tags[0].accounts == [0, 1] + wallet.tag_accounts('tagB', [1, 0]) + res = wallet.get_account_tags() + assert len(res.account_tags) == 1 + assert res.account_tags[0].tag == 'tagB' + assert res.account_tags[0].label == '' + assert res.account_tags[0].accounts == [0, 1] + wallet.set_account_tag_description('tagB', 'tag B') + res = wallet.get_account_tags() + assert len(res.account_tags) == 1 + assert res.account_tags[0].tag == 'tagB' + assert res.account_tags[0].label == 'tag B' + assert res.account_tags[0].accounts == [0, 1] + res = wallet.get_accounts('tagB') + assert len(res.subaddress_accounts) == 2 + subaddress_accounts = [] + for x in res.subaddress_accounts: + assert x.balance == 0 + assert x.unlocked_balance == 0 + subaddress_accounts.append((x.account_index, x.base_address, x.label)) + assert sorted(subaddress_accounts) == [(0, '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 'main'), (1, '82pP87g1Vkd3LUMssBCumk3MfyEsFqLAaGDf6oxddu61EgSFzt8gCwUD4tr3kp9TUfdPs2CnpD7xLZzyC1Ei9UsW3oyCWDf', 'idx1_new')] + + def attributes(self): + print('Testing attributes') + wallet = Wallet() + + ok = False + try: res = wallet.get_attribute('foo') + except: ok = True + assert ok + res = wallet.set_attribute('foo', 'bar') + res = wallet.get_attribute('foo') + assert res.value == 'bar' + res = wallet.set_attribute('foo', 'いっしゅん') + res = wallet.get_attribute('foo') + assert res.value == u'いっしゅん' + ok = False + try: res = wallet.get_attribute('いちりゅう') + except: ok = True + assert ok + res = wallet.set_attribute('いちりゅう', 'いっぽう') + res = wallet.get_attribute('いちりゅう') + assert res.value == u'いっぽう' + def open_close(self): print('Testing open/close') wallet = Wallet() @@ -200,11 +320,72 @@ class WalletAddressTest(): languages = res.languages languages_local = res.languages_local for language in languages + languages_local: - print('Creating ' + language.encode('utf8') + ' wallet') + sys.stdout.write('Creating ' + language + ' wallet\n') wallet.create_wallet(filename = '', language = language) res = wallet.query_key('mnemonic') wallet.close_wallet() + def change_password(self): + print('Testing password change') + wallet = Wallet() + + # close the wallet if any, will throw if none is loaded + try: wallet.close_wallet() + except: pass + + self.remove_wallet_files('test1') + + seed = 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted' + res = wallet.restore_deterministic_wallet(seed = seed, filename = 'test1') + assert res.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm' + assert res.seed == seed + + wallet.close_wallet() + res = wallet.open_wallet('test1', password = '') + res = wallet.get_address() + assert res.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm' + + res = wallet.change_wallet_password(old_password = '', new_password = 'foo') + wallet.close_wallet() + + ok = False + try: res = wallet.open_wallet('test1', password = '') + except: ok = True + assert ok + + res = wallet.open_wallet('test1', password = 'foo') + res = wallet.get_address() + assert res.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm' + + wallet.close_wallet() + + self.remove_wallet_files('test1') + + def store(self): + print('Testing store') + wallet = Wallet() + + # close the wallet if any, will throw if none is loaded + try: wallet.close_wallet() + except: pass + + self.remove_wallet_files('test1') + + seed = 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted' + res = wallet.restore_deterministic_wallet(seed = seed, filename = 'test1') + assert res.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm' + assert res.seed == seed + + self.remove_file('test1') + assert self.file_exists('test1.keys') + assert not self.file_exists('test1') + wallet.store() + assert self.file_exists('test1.keys') + assert self.file_exists('test1') + + wallet.close_wallet() + self.remove_wallet_files('test1') + if __name__ == '__main__': - WalletAddressTest().run_test() + WalletTest().run_test() diff --git a/tests/performance_tests/check_hash.h b/tests/performance_tests/check_hash.h index 53746fec4..294654f37 100644 --- a/tests/performance_tests/check_hash.h +++ b/tests/performance_tests/check_hash.h @@ -29,6 +29,7 @@ #pragma once #include "string_tools.h" +#include "int-util.h" #include "cryptonote_basic/difficulty.h" template<uint64_t hash_target_high, uint64_t hash_target_low, uint64_t difficulty_high, uint64_t difficulty_low> @@ -44,13 +45,18 @@ public: difficulty = difficulty_high; difficulty = (difficulty << 64) | difficulty_low; boost::multiprecision::uint256_t hash_value = std::numeric_limits<boost::multiprecision::uint256_t>::max() / hash_target; - ((uint64_t*)&hash)[0] = (hash_value & 0xffffffffffffffff).convert_to<uint64_t>(); + uint64_t val; + val = (hash_value & 0xffffffffffffffff).convert_to<uint64_t>(); + ((uint64_t*)&hash)[0] = SWAP64LE(val); hash_value >>= 64; - ((uint64_t*)&hash)[1] = (hash_value & 0xffffffffffffffff).convert_to<uint64_t>(); + val = (hash_value & 0xffffffffffffffff).convert_to<uint64_t>(); + ((uint64_t*)&hash)[1] = SWAP64LE(val); hash_value >>= 64; - ((uint64_t*)&hash)[2] = (hash_value & 0xffffffffffffffff).convert_to<uint64_t>(); + val = (hash_value & 0xffffffffffffffff).convert_to<uint64_t>(); + ((uint64_t*)&hash)[2] = SWAP64LE(val); hash_value >>= 64; - ((uint64_t*)&hash)[3] = (hash_value & 0xffffffffffffffff).convert_to<uint64_t>(); + val = (hash_value & 0xffffffffffffffff).convert_to<uint64_t>(); + ((uint64_t*)&hash)[3] = SWAP64LE(val); return true; } diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index a5d179040..cac1fa943 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -117,7 +117,8 @@ target_link_libraries(unit_tests ${Boost_THREAD_LIBRARY} ${GTEST_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} - ${EXTRA_LIBRARIES}) + ${EXTRA_LIBRARIES} + ${ZMQ_LIB}) set_property(TARGET unit_tests PROPERTY FOLDER "tests") diff --git a/tests/unit_tests/difficulty.cpp b/tests/unit_tests/difficulty.cpp index a732e6969..e9e3272f0 100644 --- a/tests/unit_tests/difficulty.cpp +++ b/tests/unit_tests/difficulty.cpp @@ -27,6 +27,7 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "gtest/gtest.h" +#include "int-util.h" #include "cryptonote_basic/difficulty.h" static cryptonote::difficulty_type MKDIFF(uint64_t high, uint64_t low) @@ -42,13 +43,18 @@ static crypto::hash MKHASH(uint64_t high, uint64_t low) hash_target = (hash_target << 64) | low; boost::multiprecision::uint256_t hash_value = std::numeric_limits<boost::multiprecision::uint256_t>::max() / hash_target; crypto::hash h; - ((uint64_t*)&h)[0] = (hash_value & 0xffffffffffffffff).convert_to<uint64_t>(); + uint64_t val; + val = (hash_value & 0xffffffffffffffff).convert_to<uint64_t>(); + ((uint64_t*)&h)[0] = SWAP64LE(val); hash_value >>= 64; - ((uint64_t*)&h)[1] = (hash_value & 0xffffffffffffffff).convert_to<uint64_t>(); + val = (hash_value & 0xffffffffffffffff).convert_to<uint64_t>(); + ((uint64_t*)&h)[1] = SWAP64LE(val); hash_value >>= 64; - ((uint64_t*)&h)[2] = (hash_value & 0xffffffffffffffff).convert_to<uint64_t>(); + val = (hash_value & 0xffffffffffffffff).convert_to<uint64_t>(); + ((uint64_t*)&h)[2] = SWAP64LE(val); hash_value >>= 64; - ((uint64_t*)&h)[3] = (hash_value & 0xffffffffffffffff).convert_to<uint64_t>(); + val = (hash_value & 0xffffffffffffffff).convert_to<uint64_t>(); + ((uint64_t*)&h)[3] = SWAP64LE(val); return h; } diff --git a/tests/unit_tests/epee_levin_protocol_handler_async.cpp b/tests/unit_tests/epee_levin_protocol_handler_async.cpp index 7bdb4c43d..b27699a77 100644 --- a/tests/unit_tests/epee_levin_protocol_handler_async.cpp +++ b/tests/unit_tests/epee_levin_protocol_handler_async.cpp @@ -241,13 +241,13 @@ namespace m_in_data.assign(256, 't'); - m_req_head.m_signature = LEVIN_SIGNATURE; - m_req_head.m_cb = m_in_data.size(); + m_req_head.m_signature = SWAP64LE(LEVIN_SIGNATURE); + m_req_head.m_cb = SWAP64LE(m_in_data.size()); m_req_head.m_have_to_return_data = true; - m_req_head.m_command = expected_command; - m_req_head.m_return_code = LEVIN_OK; - m_req_head.m_flags = LEVIN_PACKET_REQUEST; - m_req_head.m_protocol_version = LEVIN_PROTOCOL_VER_1; + m_req_head.m_command = SWAP32LE(expected_command); + m_req_head.m_return_code = SWAP32LE(LEVIN_OK); + m_req_head.m_flags = SWAP32LE(LEVIN_PACKET_REQUEST); + m_req_head.m_protocol_version = SWAP32LE(LEVIN_PROTOCOL_VER_1); m_commands_handler.return_code(expected_return_code); m_commands_handler.invoke_out_buf(m_expected_invoke_out_buf); @@ -337,12 +337,12 @@ TEST_F(positive_test_connection_to_levin_protocol_handler_calls, handler_process std::string in_data(256, 'q'); epee::levin::bucket_head2 req_head; - req_head.m_signature = LEVIN_SIGNATURE; - req_head.m_cb = in_data.size(); + req_head.m_signature = SWAP64LE(LEVIN_SIGNATURE); + req_head.m_cb = SWAP64LE(in_data.size()); req_head.m_have_to_return_data = true; - req_head.m_command = expected_command; - req_head.m_flags = LEVIN_PACKET_REQUEST; - req_head.m_protocol_version = LEVIN_PROTOCOL_VER_1; + req_head.m_command = SWAP32LE(expected_command); + req_head.m_flags = SWAP32LE(LEVIN_PACKET_REQUEST); + req_head.m_protocol_version = SWAP32LE(LEVIN_PROTOCOL_VER_1); std::string buf(reinterpret_cast<const char*>(&req_head), sizeof(req_head)); buf += in_data; @@ -373,13 +373,13 @@ TEST_F(positive_test_connection_to_levin_protocol_handler_calls, handler_process // Check sent response ASSERT_EQ(expected_out_data, out_data); - ASSERT_EQ(LEVIN_SIGNATURE, resp_head.m_signature); - ASSERT_EQ(expected_command, resp_head.m_command); - ASSERT_EQ(expected_return_code, resp_head.m_return_code); - ASSERT_EQ(expected_out_data.size(), resp_head.m_cb); + ASSERT_EQ(LEVIN_SIGNATURE, SWAP64LE(resp_head.m_signature)); + ASSERT_EQ(expected_command, SWAP32LE(resp_head.m_command)); + ASSERT_EQ(expected_return_code, SWAP32LE(resp_head.m_return_code)); + ASSERT_EQ(expected_out_data.size(), SWAP64LE(resp_head.m_cb)); ASSERT_FALSE(resp_head.m_have_to_return_data); - ASSERT_EQ(LEVIN_PROTOCOL_VER_1, resp_head.m_protocol_version); - ASSERT_TRUE(0 != (resp_head.m_flags & LEVIN_PACKET_RESPONSE)); + ASSERT_EQ(SWAP32LE(LEVIN_PROTOCOL_VER_1), resp_head.m_protocol_version); + ASSERT_TRUE(0 != (SWAP32LE(resp_head.m_flags) & LEVIN_PACKET_RESPONSE)); } TEST_F(positive_test_connection_to_levin_protocol_handler_calls, handler_processes_handle_read_as_notify) @@ -392,12 +392,12 @@ TEST_F(positive_test_connection_to_levin_protocol_handler_calls, handler_process std::string in_data(256, 'e'); epee::levin::bucket_head2 req_head; - req_head.m_signature = LEVIN_SIGNATURE; - req_head.m_cb = in_data.size(); + req_head.m_signature = SWAP64LE(LEVIN_SIGNATURE); + req_head.m_cb = SWAP64LE(in_data.size()); req_head.m_have_to_return_data = false; - req_head.m_command = expected_command; - req_head.m_flags = LEVIN_PACKET_REQUEST; - req_head.m_protocol_version = LEVIN_PROTOCOL_VER_1; + req_head.m_command = SWAP32LE(expected_command); + req_head.m_flags = SWAP32LE(LEVIN_PACKET_REQUEST); + req_head.m_protocol_version = SWAP32LE(LEVIN_PROTOCOL_VER_1); std::string buf(reinterpret_cast<const char*>(&req_head), sizeof(req_head)); buf += in_data; @@ -618,7 +618,7 @@ TEST_F(test_levin_protocol_handler__hanle_recv_with_invalid_data, handles_two_re TEST_F(test_levin_protocol_handler__hanle_recv_with_invalid_data, handles_unexpected_response) { - m_req_head.m_flags = LEVIN_PACKET_RESPONSE; + m_req_head.m_flags = SWAP32LE(LEVIN_PACKET_RESPONSE); prepare_buf(); ASSERT_FALSE(m_conn->m_protocol_handler.handle_recv(m_buf.data(), m_buf.size())); diff --git a/tests/unit_tests/logging.cpp b/tests/unit_tests/logging.cpp index 056eae604..c8526abae 100644 --- a/tests/unit_tests/logging.cpp +++ b/tests/unit_tests/logging.cpp @@ -178,3 +178,20 @@ TEST(logging, last_precedence) cleanup(); } +TEST(logging, multiline) +{ + init(); + mlog_set_categories("global:INFO"); + MGINFO("first\nsecond\nthird"); + std::string str; + ASSERT_TRUE(load_log_to_string(log_filename, str)); + ASSERT_TRUE(nlines(str) == 3); + ASSERT_TRUE(str.find("global") != std::string::npos); + ASSERT_TRUE(str.find("first") != std::string::npos); + ASSERT_TRUE(str.find("second") != std::string::npos); + ASSERT_TRUE(str.find("third") != std::string::npos); + ASSERT_TRUE(str.find("first\nsecond") == std::string::npos); + ASSERT_TRUE(str.find("second\nthird") == std::string::npos); + cleanup(); +} + diff --git a/tests/unit_tests/net.cpp b/tests/unit_tests/net.cpp index f24ffb45c..253280d4d 100644 --- a/tests/unit_tests/net.cpp +++ b/tests/unit_tests/net.cpp @@ -40,6 +40,7 @@ #include <boost/range/adaptor/sliced.hpp> #include <boost/range/combine.hpp> #include <boost/system/error_code.hpp> +#include <boost/thread/scoped_thread.hpp> #include <boost/thread/thread.hpp> #include <boost/uuid/nil_generator.hpp> #include <boost/uuid/random_generator.hpp> @@ -59,6 +60,7 @@ #include "net/socks_connect.h" #include "net/parse.h" #include "net/tor_address.h" +#include "net/zmq.h" #include "p2p/net_peerlist_boost_serialization.h" #include "serialization/keyvalue_serialization.h" #include "storages/portable_storage.h" @@ -1040,7 +1042,8 @@ TEST(dandelionpp_map, dropped_connection_remapped) boost::uuids::random_generator random_uuid{}; std::vector<boost::uuids::uuid> connections{3}; - std::generate(connections.begin(), connections.end(), random_uuid); + for (auto &e: connections) + e = random_uuid(); std::sort(connections.begin(), connections.end()); // select 3 of 3 outgoing connections @@ -1070,7 +1073,8 @@ TEST(dandelionpp_map, dropped_connection_remapped) } std::map<boost::uuids::uuid, boost::uuids::uuid> mapping; std::vector<boost::uuids::uuid> in_connections{9}; - std::generate(in_connections.begin(), in_connections.end(), random_uuid); + for (auto &e: in_connections) + e = random_uuid(); { std::map<boost::uuids::uuid, std::size_t> used; std::multimap<boost::uuids::uuid, boost::uuids::uuid> inverse_mapping; @@ -1149,7 +1153,8 @@ TEST(dandelionpp_map, dropped_connection_remapped) } // map 8 new incoming connections across 3 outgoing links in_connections.resize(18); - std::generate(in_connections.begin() + 10, in_connections.end(), random_uuid); + for (size_t i = 10; i < in_connections.size(); ++i) + in_connections[i] = random_uuid(); { std::map<boost::uuids::uuid, std::size_t> used; for (const boost::uuids::uuid& connection : in_connections) @@ -1176,7 +1181,8 @@ TEST(dandelionpp_map, dropped_all_connections) boost::uuids::random_generator random_uuid{}; std::vector<boost::uuids::uuid> connections{8}; - std::generate(connections.begin(), connections.end(), random_uuid); + for (auto &e: connections) + e = random_uuid(); std::sort(connections.begin(), connections.end()); // select 3 of 8 outgoing connections @@ -1205,7 +1211,8 @@ TEST(dandelionpp_map, dropped_all_connections) } } std::vector<boost::uuids::uuid> in_connections{9}; - std::generate(in_connections.begin(), in_connections.end(), random_uuid); + for (auto &e: in_connections) + e = random_uuid(); { std::map<boost::uuids::uuid, std::size_t> used; std::map<boost::uuids::uuid, boost::uuids::uuid> mapping; @@ -1237,7 +1244,8 @@ TEST(dandelionpp_map, dropped_all_connections) // select 3 of 30 connections, only 7 should be remapped to new indexes (but all to new uuids) connections.resize(30); - std::generate(connections.begin(), connections.end(), random_uuid); + for (auto &e: connections) + e = random_uuid(); EXPECT_TRUE(mapper.update(connections)); { std::map<boost::uuids::uuid, std::size_t> used; @@ -1253,3 +1261,131 @@ TEST(dandelionpp_map, dropped_all_connections) EXPECT_EQ(3u, entry.second); } } + +TEST(zmq, error_codes) +{ + EXPECT_EQ( + std::addressof(net::zmq::error_category()), + std::addressof(net::zmq::make_error_code(0).category()) + ); + EXPECT_EQ( + std::make_error_condition(std::errc::not_a_socket), + net::zmq::make_error_code(ENOTSOCK) + ); + + EXPECT_TRUE( + []() -> expect<void> + { + MONERO_ZMQ_CHECK(zmq_msg_send(nullptr, nullptr, 0)); + return success(); + }().matches(std::errc::not_a_socket) + ); + + bool thrown = false; + try + { + MONERO_ZMQ_THROW("stuff"); + } + catch (const std::system_error& e) + { + thrown = true; + EXPECT_EQ(std::make_error_condition(std::errc::not_a_socket), e.code()); + } + EXPECT_TRUE(thrown); +} + +TEST(zmq, read_write) +{ + 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]))); + + ASSERT_TRUE(bool(net::zmq::send(epee::strspan<std::uint8_t>(message), send_socket.get()))); + + const expect<std::string> received = net::zmq::receive(recv_socket.get()); + ASSERT_TRUE(bool(received)); + EXPECT_EQ(message, *received); +} + +TEST(zmq, read_write_multipart) +{ + 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(999); + crypto::rand(message.size(), reinterpret_cast<std::uint8_t*>(std::addressof(message[0]))); + + for (unsigned i = 0; i < 3; ++i) + { + const expect<std::string> received = net::zmq::receive(recv_socket.get(), ZMQ_DONTWAIT); + ASSERT_FALSE(bool(received)); + EXPECT_EQ(net::zmq::make_error_code(EAGAIN), received.error()); + + const epee::span<const std::uint8_t> bytes{ + reinterpret_cast<const std::uint8_t*>(std::addressof(message[0])) + (i * 333), 333 + }; + ASSERT_TRUE(bool(net::zmq::send(bytes, send_socket.get(), (i == 2 ? 0 : ZMQ_SNDMORE)))); + } + + const expect<std::string> received = net::zmq::receive(recv_socket.get(), ZMQ_DONTWAIT); + ASSERT_TRUE(bool(received)); + EXPECT_EQ(message, *received); +} + +TEST(zmq, read_write_termination) +{ + net::zmq::context context{zmq_init(1)}; + ASSERT_NE(nullptr, context); + + // must be declared before sockets and after context + boost::scoped_thread<> thread{}; + + 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]))); + + ASSERT_TRUE(bool(net::zmq::send(epee::strspan<std::uint8_t>(message), send_socket.get(), ZMQ_SNDMORE))); + + expect<std::string> received = net::zmq::receive(recv_socket.get(), ZMQ_DONTWAIT); + ASSERT_FALSE(bool(received)); + EXPECT_EQ(net::zmq::make_error_code(EAGAIN), received.error()); + + thread = boost::scoped_thread<>{ + boost::thread{ + [&context] () { context.reset(); } + } + }; + + received = net::zmq::receive(recv_socket.get()); + ASSERT_FALSE(bool(received)); + EXPECT_EQ(net::zmq::make_error_code(ETERM), received.error()); +} + diff --git a/tests/unit_tests/output_distribution.cpp b/tests/unit_tests/output_distribution.cpp index 38f442c59..eec694d1e 100644 --- a/tests/unit_tests/output_distribution.cpp +++ b/tests/unit_tests/output_distribution.cpp @@ -93,7 +93,7 @@ bool get_output_distribution(uint64_t amount, uint64_t from, uint64_t to, uint64 crypto::hash get_block_hash(uint64_t height) { - crypto::hash hash; + crypto::hash hash = crypto::null_hash; *((uint64_t*)&hash) = height; return hash; } |