diff options
Diffstat (limited to 'tests/functional_tests')
-rw-r--r-- | tests/functional_tests/CMakeLists.txt | 12 | ||||
-rwxr-xr-x | tests/functional_tests/bans.py | 117 | ||||
-rwxr-xr-x | tests/functional_tests/blockchain.py | 179 | ||||
-rwxr-xr-x | tests/functional_tests/cold_signing.py | 29 | ||||
-rwxr-xr-x | tests/functional_tests/functional_tests_rpc.py | 3 | ||||
-rwxr-xr-x | tests/functional_tests/mining.py | 7 | ||||
-rwxr-xr-x | tests/functional_tests/multisig.py | 24 | ||||
-rwxr-xr-x | tests/functional_tests/proofs.py | 7 | ||||
-rwxr-xr-x | tests/functional_tests/transfer.py | 144 | ||||
-rwxr-xr-x | tests/functional_tests/txpool.py | 7 | ||||
-rwxr-xr-x | tests/functional_tests/wallet_address.py | 58 |
11 files changed, 577 insertions, 10 deletions
diff --git a/tests/functional_tests/CMakeLists.txt b/tests/functional_tests/CMakeLists.txt index 60060f56f..fd49ba623 100644 --- a/tests/functional_tests/CMakeLists.txt +++ b/tests/functional_tests/CMakeLists.txt @@ -50,6 +50,12 @@ target_link_libraries(functional_tests ${CMAKE_THREAD_LIBS_INIT} ${EXTRA_LIBRARIES}) -add_test( - NAME functional_tests_rpc - COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/functional_tests_rpc.py" "${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_BINARY_DIR}" all) +execute_process(COMMAND ${PYTHON_EXECUTABLE} "-c" "import requests; print('OK')" OUTPUT_VARIABLE REQUESTS_OUTPUT OUTPUT_STRIP_TRAILING_WHITESPACE) +if (REQUESTS_OUTPUT STREQUAL "OK") + add_test( + NAME functional_tests_rpc + COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/functional_tests_rpc.py" "${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_BINARY_DIR}" all) +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() diff --git a/tests/functional_tests/bans.py b/tests/functional_tests/bans.py new file mode 100755 index 000000000..bb3051a6a --- /dev/null +++ b/tests/functional_tests/bans.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 + +# 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. + +import time + +"""Test peer baning RPC calls + +Test the following RPCs: + - set_bans + - get_bans + +""" + +from framework.daemon import Daemon + +class BanTest(): + def run_test(self): + print 'Testing bans' + + daemon = Daemon() + res = daemon.get_bans() + assert 'bans' not in res or len(res.bans) == 0 + + daemon.set_bans([{'host': '1.2.3.4', 'ban': True, 'seconds': 100}]) + res = daemon.get_bans() + assert len(res.bans) == 1 + assert res.bans[0].host == '1.2.3.4' + assert res.bans[0].seconds >= 98 and res.bans[0].seconds <= 100 # allow for slow RPC + + daemon.set_bans([{'host': '5.6.7.8', 'ban': True, 'seconds': 100}]) + res = daemon.get_bans() + assert len(res.bans) == 2 + for i in range(2): + assert res.bans[i].host == '1.2.3.4' or res.bans[i].host == '5.6.7.8' + assert res.bans[i].seconds >= 7 and res.bans[0].seconds <= 100 # allow for slow RPC + + daemon.set_bans([{'host': '1.2.3.4', 'ban': False}]) + res = daemon.get_bans() + assert len(res.bans) == 1 + assert res.bans[0].host == '5.6.7.8' + assert res.bans[0].seconds >= 98 and res.bans[0].seconds <= 100 # allow for slow RPC + + time.sleep(2) + + res = daemon.get_bans() + assert len(res.bans) == 1 + assert res.bans[0].host == '5.6.7.8' + assert res.bans[0].seconds >= 96 and res.bans[0].seconds <= 98 # allow for slow RPC + + daemon.set_bans([{'host': '3.4.5.6', 'ban': False}]) + res = daemon.get_bans() + assert len(res.bans) == 1 + assert res.bans[0].host == '5.6.7.8' + assert res.bans[0].seconds >= 96 and res.bans[0].seconds <= 98 # allow for slow RPC + + daemon.set_bans([{'host': '3.4.5.6', 'ban': True, 'seconds': 2}]) + res = daemon.get_bans() + assert len(res.bans) == 2 + for i in range(2): + assert res.bans[i].host == '5.6.7.8' or res.bans[i].host == '3.4.5.6' + if res.bans[i].host == '5.6.7.8': + assert res.bans[i].seconds >= 96 and res.bans[0].seconds <= 98 # allow for slow RPC + else: + assert res.bans[i].seconds >= 1 and res.bans[0].seconds <= 2 # allow for slow RPC + + time.sleep(2) + res = daemon.get_bans() + assert len(res.bans) == 1 + assert res.bans[0].host == '5.6.7.8' + assert res.bans[0].seconds >= 94 and res.bans[0].seconds <= 96 # allow for slow RPC + + daemon.set_bans([{'host': '5.6.7.8', 'ban': True, 'seconds': 20}]) + res = daemon.get_bans() + assert len(res.bans) == 1 + assert res.bans[0].host == '5.6.7.8' + assert res.bans[0].seconds >= 18 and res.bans[0].seconds <= 20 # allow for slow RPC + + daemon.set_bans([{'host': '5.6.7.8', 'ban': True, 'seconds': 200}]) + res = daemon.get_bans() + assert len(res.bans) == 1 + assert res.bans[0].host == '5.6.7.8' + assert res.bans[0].seconds >= 198 and res.bans[0].seconds <= 200 # allow for slow RPC + + daemon.set_bans([{'host': '5.6.7.8', 'ban': False}]) + res = daemon.get_bans() + assert 'bans' not in res or len(res.bans) == 0 + + +if __name__ == '__main__': + BanTest().run_test() diff --git a/tests/functional_tests/blockchain.py b/tests/functional_tests/blockchain.py index d805fccda..6376b86d0 100755 --- a/tests/functional_tests/blockchain.py +++ b/tests/functional_tests/blockchain.py @@ -45,7 +45,15 @@ from framework.daemon import Daemon class BlockchainTest(): def run_test(self): + self.reset() self._test_generateblocks(5) + self._test_alt_chains() + + def reset(self): + print 'Resetting blockchain' + daemon = Daemon() + daemon.pop_blocks(1000) + daemon.flush_txpool() def _test_generateblocks(self, blocks): assert blocks >= 2 @@ -60,8 +68,8 @@ class BlockchainTest(): prev_block = res_info.top_block_hash res_height = daemon.get_height() assert res_height.height == height - assert int(res_info.wide_cumulative_difficulty) == (res_info.cumulative_difficulty_top64 << 64) + res_info.cumulative_difficulty - cumulative_difficulty = int(res_info.wide_cumulative_difficulty) + assert int(res_info.wide_cumulative_difficulty, 16) == (res_info.cumulative_difficulty_top64 << 64) + res_info.cumulative_difficulty + cumulative_difficulty = int(res_info.wide_cumulative_difficulty, 16) # we should not see a block at height ok = False @@ -90,11 +98,11 @@ class BlockchainTest(): assert block_header.orphan_status == False assert block_header.depth == blocks - n - 1 assert block_header.prev_hash == prev_block, prev_block - assert int(block_header.wide_difficulty) == (block_header.difficulty_top64 << 64) + block_header.difficulty - assert int(block_header.wide_cumulative_difficulty) == (block_header.cumulative_difficulty_top64 << 64) + block_header.cumulative_difficulty + assert int(block_header.wide_difficulty, 16) == (block_header.difficulty_top64 << 64) + block_header.difficulty + assert int(block_header.wide_cumulative_difficulty, 16) == (block_header.cumulative_difficulty_top64 << 64) + block_header.cumulative_difficulty assert block_header.reward >= 600000000000 # tail emission - cumulative_difficulty += int(block_header.wide_difficulty) - assert cumulative_difficulty == int(block_header.wide_cumulative_difficulty) + cumulative_difficulty += int(block_header.wide_difficulty, 16) + assert cumulative_difficulty == int(block_header.wide_cumulative_difficulty, 16) assert block_header.block_size > 0 assert block_header.block_weight >= block_header.block_size assert block_header.long_term_weight > 0 @@ -122,7 +130,7 @@ class BlockchainTest(): assert res_getblocktemplate.expected_reward >= 600000000000 assert len(res_getblocktemplate.blocktemplate_blob) > 0 assert len(res_getblocktemplate.blockhashing_blob) > 0 - assert int(res_getblocktemplate.wide_difficulty) == (res_getblocktemplate.difficulty_top64 << 64) + res_getblocktemplate.difficulty + assert int(res_getblocktemplate.wide_difficulty, 16) == (res_getblocktemplate.difficulty_top64 << 64) + res_getblocktemplate.difficulty # diff etc should be the same assert res_getblocktemplate.prev_hash == res_info.top_block_hash @@ -152,6 +160,163 @@ class BlockchainTest(): except: ok = True assert ok + # get transactions + res = daemon.get_info() + assert res.height == height + blocks - 1 + nblocks = height + blocks - 1 + res = daemon.getblockheadersrange(0, nblocks - 1) + assert len(res.headers) == nblocks + assert res.headers[-1] == block_header + txids = [x.miner_tx_hash for x in res.headers] + res = daemon.get_transactions(txs_hashes = txids) + assert len(res.txs) == nblocks + assert not 'missed_txs' in res or len(res.missed_txs) == 0 + running_output_index = 0 + for i in range(len(txids)): + tx = res.txs[i] + assert tx.tx_hash == txids[i] + assert not tx.double_spend_seen + assert not tx.in_pool + assert tx.block_height == i + if i > 0: + for idx in tx.output_indices: + assert idx == running_output_index + running_output_index += 1 + res_out = daemon.get_outs([{'amount': 0, 'index': i} for i in tx.output_indices], get_txid = True) + assert len(res_out.outs) == len(tx.output_indices) + for out in res_out.outs: + assert len(out.key) == 64 + assert len(out.mask) == 64 + assert not out.unlocked + assert out.height == i + 1 + assert out.txid == txids[i + 1] + + for i in range(height + nblocks - 1): + res_sum = daemon.get_coinbase_tx_sum(i, 1) + res_header = daemon.getblockheaderbyheight(i) + assert res_sum.emission_amount == res_header.block_header.reward + + res = daemon.get_coinbase_tx_sum(0, 1) + assert res.emission_amount == 17592186044415 + assert res.fee_amount == 0 + sum_blocks = height + nblocks - 1 + res = daemon.get_coinbase_tx_sum(0, sum_blocks) + extrapolated = 17592186044415 + 17592186044415 * 2 * (sum_blocks - 1) + assert res.emission_amount < extrapolated and res.emission_amount > extrapolated - 1e12 + assert res.fee_amount == 0 + sum_blocks_emission = res.emission_amount + res = daemon.get_coinbase_tx_sum(1, sum_blocks) + assert res.emission_amount == sum_blocks_emission - 17592186044415 + assert res.fee_amount == 0 + + res = daemon.get_output_distribution([0, 1, 17592186044415], 0, 0) + assert len(res.distributions) == 3 + for a in range(3): + assert res.distributions[a].amount == [0, 1, 17592186044415][a] + assert res.distributions[a].start_height == 0 + assert res.distributions[a].base == 0 + assert len(res.distributions[a].distribution) == height + nblocks - 1 + assert res.distributions[a].binary == False + for i in range(height + nblocks - 1): + assert res.distributions[a].distribution[i] == (1 if i > 0 and a == 0 else 1 if a == 2 and i == 0 else 0) + + res = daemon.get_output_histogram([], min_count = 0, max_count = 0) + assert len(res.histogram) == 2 + for i in range(2): + assert res.histogram[i].amount in [0, 17592186044415] + assert res.histogram[i].total_instances in [height + nblocks - 2, 1] + assert res.histogram[i].unlocked_instances == 0 + assert res.histogram[i].recent_instances == 0 + + def _test_alt_chains(self): + print('Testing alt chains') + daemon = Daemon() + res = daemon.get_alt_blocks_hashes() + starting_alt_blocks = res.blks_hashes if 'blks_hashes' in res else [] + res = daemon.get_info() + root_block_hash = res.top_block_hash + height = res.height + prev_hash = res.top_block_hash + res_template = daemon.getblocktemplate('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm') + nonce = 0 + + # 5 siblings + alt_blocks = [None] * 5 + for i in range(len(alt_blocks)): + res = daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 1, prev_block = prev_hash, starting_nonce = nonce) + assert res.height == height + assert len(res.blocks) == 1 + txid = res.blocks[0] + res = daemon.getblockheaderbyhash(txid) + nonce = res.block_header.nonce + print('mined ' + ('alt' if res.block_header.orphan_status else 'tip') + ' block ' + str(height) + ', nonce ' + str(nonce)) + assert res.block_header.prev_hash == prev_hash + assert res.block_header.orphan_status == (i > 0) + alt_blocks[i] = txid + nonce += 1 + + print 'mining 3 on 1' + # three more on [1] + chain1 = [] + res = daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 3, prev_block = alt_blocks[1], starting_nonce = nonce) + assert res.height == height + 3 + assert len(res.blocks) == 3 + blk_hash = res.blocks[2] + res = daemon.getblockheaderbyhash(blk_hash) + nonce = res.block_header.nonce + assert not res.block_header.orphan_status + nonce += 1 + chain1.append(blk_hash) + chain1.append(res.block_header.prev_hash) + + print('Checking alt blocks match') + res = daemon.get_alt_blocks_hashes() + assert len(res.blks_hashes) == len(starting_alt_blocks) + 4 + for txid in alt_blocks: + assert txid in res.blks_hashes or txid == alt_blocks[1] + + print 'mining 4 on 3' + # 4 more on [3], the chain will reorg when we mine the 4th + top_block_hash = blk_hash + prev_block = alt_blocks[3] + for i in range(4): + res = daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 1, prev_block = prev_block) + assert res.height == height + 1 + i + assert len(res.blocks) == 1 + prev_block = res.blocks[-1] + res = daemon.getblockheaderbyhash(res.blocks[-1]) + assert res.block_header.orphan_status == (i < 3) + + res = daemon.get_info() + assert res.height == ((height + 4) if i < 3 else height + 5) + assert res.top_block_hash == (top_block_hash if i < 3 else prev_block) + + res = daemon.get_info() + assert res.height == height + 5 + assert res.top_block_hash == prev_block + + print('Checking alt blocks match') + res = daemon.get_alt_blocks_hashes() + blks_hashes = res.blks_hashes + assert len(blks_hashes) == len(starting_alt_blocks) + 7 + for txid in alt_blocks: + assert txid in blks_hashes or txid == alt_blocks[3] + for txid in chain1: + assert txid in blks_hashes + + res = daemon.get_alternate_chains() + assert len(res.chains) == 4 + tips = [chain.block_hash for chain in res.chains] + for txid in tips: + assert txid in blks_hashes + for chain in res.chains: + assert chain.length in [1, 4] + assert chain.length == len(chain.block_hashes) + assert chain.height == height + chain.length - 1 # all happen start at the same height + assert chain.main_chain_parent_block == root_block_hash + 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 + if __name__ == '__main__': BlockchainTest().run_test() diff --git a/tests/functional_tests/cold_signing.py b/tests/functional_tests/cold_signing.py index 6895aec60..59a879e0a 100755 --- a/tests/functional_tests/cold_signing.py +++ b/tests/functional_tests/cold_signing.py @@ -38,10 +38,17 @@ from framework.wallet import Wallet class ColdSigningTest(): def run_test(self): + self.reset() self.create(0) self.mine() self.transfer() + def reset(self): + print 'Resetting blockchain' + daemon = Daemon() + daemon.pop_blocks(1000) + daemon.flush_txpool() + def create(self, idx): print 'Creating hot and cold wallet' @@ -89,6 +96,12 @@ class ColdSigningTest(): dst = {'address': '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 'amount': 1000000000000} payment_id = '1234500000012345abcde00000abcdeff1234500000012345abcde00000abcde' + self.hot_wallet.refresh() + res = self.hot_wallet.export_outputs() + self.cold_wallet.import_outputs(res.outputs_data_hex) + res = self.cold_wallet.export_key_images(True) + self.hot_wallet.import_key_images(res.signed_key_images, offset = res.offset) + res = self.hot_wallet.transfer([dst], ring_size = 11, payment_id = payment_id, get_tx_key = False) assert len(res.tx_hash) == 32*2 txid = res.tx_hash @@ -104,6 +117,22 @@ class ColdSigningTest(): unsigned_txset = res.unsigned_txset print 'Signing transaction with cold wallet' + res = self.cold_wallet.describe_transfer(unsigned_txset = unsigned_txset) + assert len(res.desc) == 1 + desc = res.desc[0] + assert desc.amount_in >= amount + fee + assert desc.amount_out == desc.amount_in - fee + assert desc.ring_size == 11 + assert desc.unlock_time == 0 + assert desc.payment_id == payment_id + assert desc.change_amount == desc.amount_in - 1000000000000 - fee + assert desc.change_address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm' + assert desc.fee == fee + assert len(desc.recipients) == 1 + rec = desc.recipients[0] + assert rec.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm' + assert rec.amount == 1000000000000 + res = self.cold_wallet.sign_transfer(unsigned_txset) assert len(res.signed_txset) > 0 signed_txset = res.signed_txset diff --git a/tests/functional_tests/functional_tests_rpc.py b/tests/functional_tests/functional_tests_rpc.py index f2fef7e95..e754b4e33 100755 --- a/tests/functional_tests/functional_tests_rpc.py +++ b/tests/functional_tests/functional_tests_rpc.py @@ -98,6 +98,7 @@ FAIL = [] for test in tests: try: print('[TEST STARTED] ' + test) + sys.stdout.flush() cmd = [python, srcdir + '/' + test + ".py"] subprocess.check_call(cmd) PASS.append(test) @@ -133,3 +134,5 @@ 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, ', ')) + +sys.exit(0 if len(FAIL) == 0 else 1) diff --git a/tests/functional_tests/mining.py b/tests/functional_tests/mining.py index 1b189beb2..78dc68640 100755 --- a/tests/functional_tests/mining.py +++ b/tests/functional_tests/mining.py @@ -43,9 +43,16 @@ from framework.wallet import Wallet class MiningTest(): def run_test(self): + self.reset() self.create() self.mine() + def reset(self): + print 'Resetting blockchain' + daemon = Daemon() + daemon.pop_blocks(1000) + daemon.flush_txpool() + def create(self): print 'Creating wallet' wallet = Wallet() diff --git a/tests/functional_tests/multisig.py b/tests/functional_tests/multisig.py index a0e8551cd..476e3a02d 100755 --- a/tests/functional_tests/multisig.py +++ b/tests/functional_tests/multisig.py @@ -38,6 +38,7 @@ from framework.wallet import Wallet class MultisigTest(): def run_test(self): + self.reset() self.mine('493DsrfJPqiN3Suv9RcRDoZEbQtKZX1sNcGPA3GhkKYEEmivk8kjQrTdRdVc4ZbmzWJuE157z9NNUKmF2VDfdYDR3CziGMk', 5) self.mine('42jSRGmmKN96V2j3B8X2DbiNThBXW1tSi1rW1uwkqbyURenq3eC3yosNm8HEMdHuWwKMFGzMUB3RCTvcTaW9kHpdRPP7p5y', 5) self.mine('47fF32AdrmXG84FcPY697uZdd42pMMGiH5UpiTRTt3YX2pZC7t7wkzEMStEicxbQGRfrYvAAYxH6Fe8rnD56EaNwUgxRd53', 5) @@ -68,6 +69,12 @@ class MultisigTest(): self.import_multisig_info([0, 1, 2, 3], 6) self.check_transaction(txid) + def reset(self): + print 'Resetting blockchain' + daemon = Daemon() + daemon.pop_blocks(1000) + daemon.flush_txpool() + def mine(self, address, blocks): print("Mining some blocks") daemon = Daemon() @@ -129,6 +136,7 @@ class MultisigTest(): addresses.append(res.address) for i in range(N_total): assert addresses[i] == expected_address + self.wallet_address = expected_address for i in range(N_total): res = self.wallet[i].is_multisig() @@ -181,6 +189,22 @@ class MultisigTest(): for i in range(len(signers[1:])): print('Signing multisig transaction with wallet ' + str(signers[i+1])) + res = self.wallet[signers[i+1]].describe_transfer(multisig_txset = multisig_txset) + assert len(res.desc) == 1 + desc = res.desc[0] + assert desc.amount_in >= amount + fee + assert desc.amount_out == desc.amount_in - fee + assert desc.ring_size == 11 + assert desc.unlock_time == 0 + assert desc.payment_id == '0000000000000000' + assert desc.change_amount == desc.amount_in - 1000000000000 - fee + assert desc.change_address == self.wallet_address + assert desc.fee == fee + assert len(desc.recipients) == 1 + rec = desc.recipients[0] + assert rec.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm' + assert rec.amount == 1000000000000 + res = self.wallet[signers[i+1]].sign_multisig(multisig_txset) multisig_txset = res.tx_data_hex assert len(res.tx_hash_list if 'tx_hash_list' in res else []) == (i == len(signers[1:]) - 1) diff --git a/tests/functional_tests/proofs.py b/tests/functional_tests/proofs.py index 0a0b6304d..844131095 100755 --- a/tests/functional_tests/proofs.py +++ b/tests/functional_tests/proofs.py @@ -38,6 +38,7 @@ from framework.wallet import Wallet class ProofsTest(): def run_test(self): + self.reset() self.mine('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 80) self.create_wallets() txid, tx_key, amount = self.transfer() @@ -45,6 +46,12 @@ class ProofsTest(): self.check_tx_proof(txid, amount) self.check_reserve_proof() + def reset(self): + print 'Resetting blockchain' + daemon = Daemon() + daemon.pop_blocks(1000) + daemon.flush_txpool() + def mine(self, address, blocks): print("Mining some blocks") daemon = Daemon() diff --git a/tests/functional_tests/transfer.py b/tests/functional_tests/transfer.py index b7a85f1d6..1ff641d1f 100755 --- a/tests/functional_tests/transfer.py +++ b/tests/functional_tests/transfer.py @@ -29,6 +29,7 @@ # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import time +import json """Test simple transfers """ @@ -38,10 +39,19 @@ from framework.wallet import Wallet class TransferTest(): def run_test(self): + self.reset() self.create() self.mine() self.transfer() self.check_get_bulk_payments() + self.check_double_spend_detection() + self.sweep_single() + + def reset(self): + print 'Resetting blockchain' + daemon = Daemon() + daemon.pop_blocks(1000) + daemon.flush_txpool() def create(self): print 'Creating wallets' @@ -62,9 +72,14 @@ class TransferTest(): print("Mining some blocks") daemon = Daemon() + res = daemon.get_info() + height = res.height + daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 80) for i in range(len(self.wallet)): self.wallet[i].refresh() + res = self.wallet[i].get_height() + assert res.height == height + 80 def transfer(self): daemon = Daemon() @@ -169,6 +184,27 @@ class TransferTest(): assert e.double_spend_seen == False assert e.confirmations == 1 + res = self.wallet[0].get_height() + wallet_height = res.height + res = self.wallet[0].get_transfer_by_txid(txid) + assert len(res.transfers) == 1 + assert res.transfers[0] == res.transfer + t = res.transfer + assert t.txid == txid + assert t.payment_id == payment_id + assert t.height == wallet_height - 1 + assert t.timestamp > 0 + assert t.amount == 0 # to self, so it's just "pay a fee" really + assert t.fee == fee + assert t.note == '' + assert len(t.destinations) == 1 + assert t.destinations[0] == {'address': '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 'amount': 1000000000000} + assert t.type == 'out' + assert t.unlock_time == 0 + assert t.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm' + assert t.double_spend_seen == False + assert t.confirmations == 1 + res = self.wallet[0].get_balance() assert res.balance == running_balances[0] assert res.unlocked_balance <= res.balance @@ -483,5 +519,113 @@ class TransferTest(): res = self.wallet[2].get_bulk_payments(payment_ids = ['1'*64, '1234500000012345abcde00000abcdeff1234500000012345abcde00000abcde', '2'*64]) assert len(res.payments) >= 1 # one tx was sent + def check_double_spend_detection(self): + print('Checking double spend detection') + txes = [[None, None], [None, None]] + for i in range(2): + self.wallet[0].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') + self.wallet[0].refresh() + res = self.wallet[0].get_balance() + unlocked_balance = res.unlocked_balance + res = self.wallet[0].sweep_all(address = '44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW', do_not_relay = True, get_tx_hex = True) + assert len(res.tx_hash_list) == 1 + assert len(res.tx_hash_list[0]) == 32*2 + txes[i][0] = res.tx_hash_list[0] + assert len(res.fee_list) == 1 + assert res.fee_list[0] > 0 + assert len(res.amount_list) == 1 + assert res.amount_list[0] == unlocked_balance - res.fee_list[0] + assert len(res.tx_blob_list) > 0 + assert len(res.tx_blob_list[0]) > 0 + assert not 'tx_metadata_list' in res or len(res.tx_metadata_list) == 0 + assert not 'multisig_txset' in res or len(res.multisig_txset) == 0 + assert not 'unsigned_txset' in res or len(res.unsigned_txset) == 0 + assert len(res.tx_blob_list) == 1 + txes[i][1] = res.tx_blob_list[0] + + daemon = Daemon() + res = 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 + assert res.not_rct == False + + 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] + assert tx.in_pool + assert not tx.double_spend_seen + + res = 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.not_rct == False + + 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] + assert tx.in_pool + assert tx.double_spend_seen + + def sweep_single(self): + daemon = Daemon() + + print("Sending single output") + + daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 1) + self.wallet[0].refresh() + res = self.wallet[0].incoming_transfers(transfer_type = 'available') + for t in res.transfers: + assert not t.spent + assert len(res.transfers) > 8 # we mined a lot + index = 8 + assert not res.transfers[index].spent + assert res.transfers[index].amount > 0 + ki = res.transfers[index].key_image + amount = res.transfers[index].amount + daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 10) # ensure unlocked + self.wallet[0].refresh() + res = self.wallet[0].get_balance() + balance = res.balance + res = self.wallet[0].incoming_transfers(transfer_type = 'all') + res = self.wallet[0].sweep_single('44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW', key_image = ki) + assert len(res.tx_hash) == 64 + tx_hash = res.tx_hash + daemon.generateblocks('44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW', 1) + self.wallet[0].refresh() + res = self.wallet[0].get_balance() + new_balance = res.balance + res = daemon.get_transactions([tx_hash], decode_as_json = True) + assert len(res.txs) == 1 + tx = res.txs[0] + assert tx.tx_hash == tx_hash + assert not tx.in_pool + assert len(tx.as_json) > 0 + try: + j = json.loads(tx.as_json) + except: + j = None + assert j + assert new_balance == balance - amount + assert len(j['vin']) == 1 + assert j['vin'][0]['key']['k_image'] == ki + self.wallet[0].refresh() + res = self.wallet[0].incoming_transfers(transfer_type = 'available') + assert len([t for t in res.transfers if t.key_image == ki]) == 0 + res = self.wallet[0].incoming_transfers(transfer_type = 'unavailable') + assert len([t for t in res.transfers if t.key_image == ki]) == 1 + + if __name__ == '__main__': TransferTest().run_test() diff --git a/tests/functional_tests/txpool.py b/tests/functional_tests/txpool.py index 71109c9e5..d74395f10 100755 --- a/tests/functional_tests/txpool.py +++ b/tests/functional_tests/txpool.py @@ -38,10 +38,17 @@ from framework.wallet import Wallet class TransferTest(): def run_test(self): + self.reset() self.create() self.mine() self.check_txpool() + def reset(self): + print 'Resetting blockchain' + daemon = Daemon() + daemon.pop_blocks(1000) + daemon.flush_txpool() + def create(self): print 'Creating wallet' wallet = Wallet() diff --git a/tests/functional_tests/wallet_address.py b/tests/functional_tests/wallet_address.py index 66a1633ca..8a55521c6 100755 --- a/tests/functional_tests/wallet_address.py +++ b/tests/functional_tests/wallet_address.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +#encoding=utf-8 # Copyright (c) 2019 The Monero Project # @@ -38,13 +39,23 @@ Test the following RPCs: """ from framework.wallet import Wallet +from framework.daemon import Daemon class WalletAddressTest(): def run_test(self): + self.reset() self.create() self.check_main_address() self.check_keys() self.create_subaddresses() + self.open_close() + self.languages() + + def reset(self): + print 'Resetting blockchain' + daemon = Daemon() + daemon.pop_blocks(1000) + daemon.flush_txpool() def create(self): print 'Creating wallet' @@ -148,5 +159,52 @@ class WalletAddressTest(): res = wallet.get_address_index('82pP87g1Vkd3LUMssBCumk3MfyEsFqLAaGDf6oxddu61EgSFzt8gCwUD4tr3kp9TUfdPs2CnpD7xLZzyC1Ei9UsW3oyCWDf') assert res.index == {'major': 1, 'minor': 0} + def open_close(self): + print 'Testing open/close' + wallet = Wallet() + + res = wallet.get_address() + assert res.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm' + + wallet.close_wallet() + ok = False + try: res = wallet.get_address() + except: ok = True + assert ok + + wallet.restore_deterministic_wallet(seed = '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') + res = wallet.get_address() + assert res.address == '44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW' + + wallet.close_wallet() + ok = False + try: wallet.get_address() + except: ok = True + assert ok + + 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') + res = wallet.get_address() + assert res.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm' + + def languages(self): + print('Testing languages') + wallet = Wallet() + res = wallet.get_languages() + assert 'English' in res.languages + assert 'English' in res.languages_local + assert 'Dutch' in res.languages + assert 'Nederlands' in res.languages_local + assert 'Japanese' in res.languages + assert u'日本語' in res.languages_local + try: wallet.close_wallet() + except: pass + languages = res.languages + for language in languages: + print 'Creating ' + str(language) + ' wallet' + wallet.create_wallet(filename = '', language = language) + res = wallet.query_key('mnemonic') + wallet.close_wallet() + + if __name__ == '__main__': WalletAddressTest().run_test() |