aboutsummaryrefslogtreecommitdiff
path: root/tests/functional_tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests/functional_tests')
-rw-r--r--tests/functional_tests/CMakeLists.txt4
-rwxr-xr-xtests/functional_tests/address_book.py304
-rwxr-xr-xtests/functional_tests/blockchain.py6
-rw-r--r--tests/functional_tests/check_missing_rpc_methods.py50
-rwxr-xr-xtests/functional_tests/cold_signing.py3
-rwxr-xr-xtests/functional_tests/functional_tests_rpc.py9
-rwxr-xr-xtests/functional_tests/get_output_distribution.py3
-rwxr-xr-xtests/functional_tests/mining.py59
-rwxr-xr-xtests/functional_tests/multisig.py71
-rwxr-xr-xtests/functional_tests/proofs.py38
-rwxr-xr-xtests/functional_tests/speed.py22
-rw-r--r--tests/functional_tests/transactions_flow_test.cpp10
-rwxr-xr-xtests/functional_tests/transfer.py148
-rwxr-xr-xtests/functional_tests/txpool.py87
-rwxr-xr-xtests/functional_tests/uri.py234
-rwxr-xr-xtests/functional_tests/validate_address.py3
-rwxr-xr-xtests/functional_tests/wallet.py (renamed from tests/functional_tests/wallet_address.py)199
17 files changed, 1208 insertions, 42 deletions
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 32b601d7a..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");
@@ -253,7 +253,7 @@ bool transactions_flow_test(std::string& working_folder,
transfered_money += amount_to_tx;
LOG_PRINT_L0("transferred " << amount_to_tx << ", i=" << i );
- tx_test_entry& ent = txs[get_transaction_hash(tx)] = boost::value_initialized<tx_test_entry>();
+ tx_test_entry& ent = txs[get_transaction_hash(tx)] = tx_test_entry{};
ent.amount_transfered = amount_to_tx;
ent.tx = tx;
//if(i % transactions_per_second)
@@ -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()