aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormoneromooo-monero <moneromooo-monero@users.noreply.github.com>2019-03-12 21:30:28 +0000
committermoneromooo-monero <moneromooo-monero@users.noreply.github.com>2019-03-21 19:26:49 +0000
commit8dcd4d3d11368c7c599cb4c675ea172a816b52c9 (patch)
tree8b54728de6554c01a483950c5bdfa38345ff88e5
parentMerge pull request #5267 (diff)
downloadmonero-8dcd4d3d11368c7c599cb4c675ea172a816b52c9.tar.xz
functional_tests: improve RPC blockchain tests
-rwxr-xr-xtests/functional_tests/blockchain.py156
-rwxr-xr-xtests/functional_tests/daemon_info.py89
-rwxr-xr-xtests/functional_tests/speed.py2
-rw-r--r--tests/functional_tests/test_framework/daemon.py64
-rw-r--r--tests/functional_tests/test_framework/rpc.py40
-rw-r--r--tests/functional_tests/test_framework/wallet.py16
6 files changed, 295 insertions, 72 deletions
diff --git a/tests/functional_tests/blockchain.py b/tests/functional_tests/blockchain.py
index 983658a7c..bdd08680f 100755
--- a/tests/functional_tests/blockchain.py
+++ b/tests/functional_tests/blockchain.py
@@ -28,75 +28,129 @@
# 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 blockchain RPC calls
+import time
+
+"""Test daemon blockchain RPC calls
Test the following RPCs:
- get_info
- generateblocks
+ - misc block retrieval
+ - pop_blocks
- [TODO: many tests still need to be written]
"""
from test_framework.daemon import Daemon
-from test_framework.wallet import Wallet
class BlockchainTest():
def run_test(self):
- self._test_get_info()
- self._test_hardfork_info()
self._test_generateblocks(5)
- def _test_get_info(self):
- print('Test get_info')
-
- daemon = Daemon()
- res = daemon.get_info()
-
- # difficulty should be set to 1 for this test
- assert 'difficulty' in res.keys()
- assert res['difficulty'] == 1;
-
- # nettype should not be TESTNET
- assert 'testnet' in res.keys()
- assert res['testnet'] == False;
-
- # nettype should not be STAGENET
- assert 'stagenet' in res.keys()
- assert res['stagenet'] == False;
-
- # nettype should be FAKECHAIN
- assert 'nettype' in res.keys()
- assert res['nettype'] == "fakechain";
-
- # free_space should be > 0
- assert 'free_space' in res.keys()
- assert res['free_space'] > 0
-
- # height should be greater or equal to 1
- assert 'height' in res.keys()
- assert res['height'] >= 1
-
-
- def _test_hardfork_info(self):
- print('Test hard_fork_info')
-
- daemon = Daemon()
- res = daemon.hard_fork_info()
-
- # hard_fork version should be set at height 1
- assert 'earliest_height' in res.keys()
- assert res['earliest_height'] == 1;
-
-
def _test_generateblocks(self, blocks):
- print("Test generating", blocks, 'blocks')
+ assert blocks >= 2
+
+ print "Test generating", blocks, 'blocks'
daemon = Daemon()
- res = daemon.get_info()
- height = res['height']
- res = daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', blocks)
- assert res['height'] == height + blocks - 1
+ # check info/height before generating blocks
+ res_info = daemon.get_info()
+ height = res_info.height
+ 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)
+
+ # we should not see a block at height
+ ok = False
+ try: daemon.getblock(height)
+ except: ok = True
+ assert ok
+
+ # generate blocks
+ res_generateblocks = daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', blocks)
+
+ # check info/height after generateblocks blocks
+ assert res_generateblocks.height == height + blocks - 1
+ res_info = daemon.get_info()
+ assert res_info.height == height + blocks
+ assert res_info.top_block_hash != prev_block
+ res_height = daemon.get_height()
+ assert res_height.height == height + blocks
+
+ # get the blocks, check they have the right height
+ res_getblock = []
+ for n in range(blocks):
+ res_getblock.append(daemon.getblock(height + n))
+ block_header = res_getblock[n].block_header
+ assert abs(block_header.timestamp - time.time()) < 10 # within 10 seconds
+ assert block_header.height == height + n
+ 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 block_header.reward >= 600000000000 # tail emission
+ cumulative_difficulty += int(block_header.wide_difficulty)
+ assert cumulative_difficulty == int(block_header.wide_cumulative_difficulty)
+ assert block_header.block_size > 0
+ assert block_header.block_weight >= block_header.block_size
+ assert block_header.long_term_weight > 0
+ prev_block = block_header.hash
+
+ # we should not see a block after that
+ ok = False
+ try: daemon.getblock(height + blocks)
+ except: ok = True
+ assert ok
+
+ # getlastblockheader and by height/hash should return the same block
+ res_getlastblockheader = daemon.getlastblockheader()
+ assert res_getlastblockheader.block_header == block_header
+ res_getblockheaderbyhash = daemon.getblockheaderbyhash(prev_block)
+ assert res_getblockheaderbyhash.block_header == block_header
+ res_getblockheaderbyheight = daemon.getblockheaderbyheight(height + blocks - 1)
+ assert res_getblockheaderbyheight.block_header == block_header
+
+ # getting a block template after that should have the right height, etc
+ res_getblocktemplate = daemon.getblocktemplate('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm')
+ assert res_getblocktemplate.height == height + blocks
+ assert res_getblocktemplate.reserved_offset > 0
+ assert res_getblocktemplate.prev_hash == res_info.top_block_hash
+ 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
+
+ # diff etc should be the same
+ assert res_getblocktemplate.prev_hash == res_info.top_block_hash
+
+ res_getlastblockheader = daemon.getlastblockheader()
+
+ # pop a block
+ res_popblocks = daemon.pop_blocks(1)
+ assert res_popblocks.height == height + blocks - 1
+
+ res_info = daemon.get_info()
+ assert res_info.height == height + blocks - 1
+
+ # getlastblockheader and by height/hash should return the previous block
+ block_header = res_getblock[blocks - 2].block_header
+ block_header.depth = 0 # this will be different, ignore it
+ res_getlastblockheader = daemon.getlastblockheader()
+ assert res_getlastblockheader.block_header == block_header
+ res_getblockheaderbyhash = daemon.getblockheaderbyhash(block_header.hash)
+ assert res_getblockheaderbyhash.block_header == block_header
+ res_getblockheaderbyheight = daemon.getblockheaderbyheight(height + blocks - 2)
+ assert res_getblockheaderbyheight.block_header == block_header
+
+ # we should not see the popped block anymore
+ ok = False
+ try: daemon.getblock(height + blocks - 1)
+ except: ok = True
+ assert ok
if __name__ == '__main__':
diff --git a/tests/functional_tests/daemon_info.py b/tests/functional_tests/daemon_info.py
new file mode 100755
index 000000000..94c3fc3ac
--- /dev/null
+++ b/tests/functional_tests/daemon_info.py
@@ -0,0 +1,89 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2018 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 daemon RPC calls
+
+Test the following RPCs:
+ - get_info
+ - hard_fork_info
+
+"""
+
+from test_framework.daemon import Daemon
+
+class DaemonGetInfoTest():
+ def run_test(self):
+ self._test_hardfork_info()
+ self._test_get_info()
+
+ def _test_hardfork_info(self):
+ print('Test hard_fork_info')
+
+ daemon = Daemon()
+ res = daemon.hard_fork_info()
+
+ # hard_fork version should be set at height 1
+ assert 'earliest_height' in res.keys()
+ #assert res['earliest_height'] == 1;
+ assert res.earliest_height == 1
+
+ def _test_get_info(self):
+ print('Test get_info')
+
+ daemon = Daemon()
+ res = daemon.get_info()
+
+ # difficulty should be set to 1 for this test
+ assert 'difficulty' in res.keys()
+ assert res.difficulty == 1;
+
+ # nettype should not be TESTNET
+ assert 'testnet' in res.keys()
+ assert res.testnet == False;
+
+ # nettype should not be STAGENET
+ assert 'stagenet' in res.keys()
+ assert res.stagenet == False;
+
+ # nettype should be FAKECHAIN
+ assert 'nettype' in res.keys()
+ assert res.nettype == "fakechain";
+
+ # free_space should be > 0
+ assert 'free_space' in res.keys()
+ assert res.free_space > 0
+
+ # height should be greater or equal to 1
+ assert 'height' in res.keys()
+ assert res.height >= 1
+
+
+if __name__ == '__main__':
+ DaemonGetInfoTest().run_test()
diff --git a/tests/functional_tests/speed.py b/tests/functional_tests/speed.py
index 3d2af9a10..0ce90f939 100755
--- a/tests/functional_tests/speed.py
+++ b/tests/functional_tests/speed.py
@@ -58,7 +58,7 @@ class SpeedTest():
self._test_speed_generateblocks(daemon=daemon, blocks=70)
for i in range(1, 10):
- while wallet.get_balance()['unlocked_balance'] == 0:
+ while wallet.get_balance().unlocked_balance == 0:
print('Waiting for wallet to refresh...')
sleep(1)
self._test_speed_transfer_split(wallet=wallet)
diff --git a/tests/functional_tests/test_framework/daemon.py b/tests/functional_tests/test_framework/daemon.py
index f3490b232..c7619a434 100644
--- a/tests/functional_tests/test_framework/daemon.py
+++ b/tests/functional_tests/test_framework/daemon.py
@@ -32,8 +32,8 @@ from .rpc import JSONRPC
class Daemon(object):
- def __init__(self, protocol='http', host='127.0.0.1', port=18081, path='/json_rpc'):
- self.rpc = JSONRPC('{protocol}://{host}:{port}{path}'.format(protocol=protocol, host=host, port=port, path=path))
+ def __init__(self, protocol='http', host='127.0.0.1', port=18081):
+ self.rpc = JSONRPC('{protocol}://{host}:{port}'.format(protocol=protocol, host=host, port=port))
def getblocktemplate(self, address):
getblocktemplate = {
@@ -45,7 +45,7 @@ class Daemon(object):
'jsonrpc': '2.0',
'id': '0'
}
- return self.rpc.send_request(getblocktemplate)
+ return self.rpc.send_json_rpc_request(getblocktemplate)
def submitblock(self, block):
submitblock = {
@@ -54,7 +54,7 @@ class Daemon(object):
'jsonrpc': '2.0',
'id': '0'
}
- return self.rpc.send_request(submitblock)
+ return self.rpc.send_json_rpc_request(submitblock)
def getblock(self, height=0):
getblock = {
@@ -65,7 +65,39 @@ class Daemon(object):
'jsonrpc': '2.0',
'id': '0'
}
- return self.rpc.send_request(getblock)
+ return self.rpc.send_json_rpc_request(getblock)
+
+ def getlastblockheader(self):
+ getlastblockheader = {
+ 'method': 'getlastblockheader',
+ 'params': {
+ },
+ 'jsonrpc': '2.0',
+ 'id': '0'
+ }
+ return self.rpc.send_json_rpc_request(getlastblockheader)
+
+ def getblockheaderbyhash(self, hash):
+ getblockheaderbyhash = {
+ 'method': 'getblockheaderbyhash',
+ 'params': {
+ 'hash': hash,
+ },
+ 'jsonrpc': '2.0',
+ 'id': '0'
+ }
+ return self.rpc.send_json_rpc_request(getblockheaderbyhash)
+
+ def getblockheaderbyheight(self, height):
+ getblockheaderbyheight = {
+ 'method': 'getblockheaderbyheight',
+ 'params': {
+ 'height': height,
+ },
+ 'jsonrpc': '2.0',
+ 'id': '0'
+ }
+ return self.rpc.send_json_rpc_request(getblockheaderbyheight)
def get_connections(self):
get_connections = {
@@ -73,7 +105,7 @@ class Daemon(object):
'jsonrpc': '2.0',
'id': '0'
}
- return self.rpc.send_request(get_connections)
+ return self.rpc.send_json_rpc_request(get_connections)
def get_info(self):
get_info = {
@@ -81,7 +113,7 @@ class Daemon(object):
'jsonrpc': '2.0',
'id': '0'
}
- return self.rpc.send_request(get_info)
+ return self.rpc.send_json_rpc_request(get_info)
def hard_fork_info(self):
hard_fork_info = {
@@ -89,7 +121,7 @@ class Daemon(object):
'jsonrpc': '2.0',
'id': '0'
}
- return self.rpc.send_request(hard_fork_info)
+ return self.rpc.send_json_rpc_request(hard_fork_info)
def generateblocks(self, address, blocks=1):
generateblocks = {
@@ -102,4 +134,18 @@ class Daemon(object):
'jsonrpc': '2.0',
'id': '0'
}
- return self.rpc.send_request(generateblocks)
+ return self.rpc.send_json_rpc_request(generateblocks)
+
+ def get_height(self):
+ get_height = {
+ 'method': 'get_height',
+ 'jsonrpc': '2.0',
+ 'id': '0'
+ }
+ return self.rpc.send_request("/get_height", get_height)
+
+ def pop_blocks(self, nblocks = 1):
+ pop_blocks = {
+ 'nblocks' : nblocks,
+ }
+ return self.rpc.send_request("/pop_blocks", pop_blocks)
diff --git a/tests/functional_tests/test_framework/rpc.py b/tests/functional_tests/test_framework/rpc.py
index b21df7b93..b857be4d2 100644
--- a/tests/functional_tests/test_framework/rpc.py
+++ b/tests/functional_tests/test_framework/rpc.py
@@ -29,21 +29,55 @@
import requests
import json
+class Response(dict):
+ def __init__(self, d):
+ for k in d.keys():
+ if type(d[k]) == dict:
+ self[k] = Response(d[k])
+ elif type(d[k]) == list:
+ self[k] = []
+ for i in range(len(d[k])):
+ if type(d[k][i]) == dict:
+ self[k].append(Response(d[k][i]))
+ else:
+ self[k].append(d[k][i])
+ else:
+ self[k] = d[k]
+
+ def __getattr__(self, key):
+ return self[key]
+ def __setattr__(self, key, value):
+ self[key] = value
+ def __eq__(self, other):
+ if type(other) == dict:
+ return self == Response(other)
+ if self.keys() != other.keys():
+ return False
+ for k in self.keys():
+ if self[k] != other[k]:
+ return False
+ return True
+
class JSONRPC(object):
def __init__(self, url):
self.url = url
- def send_request(self, inputs):
+ def send_request(self, path, inputs, result_field = None):
res = requests.post(
- self.url,
+ self.url + path,
data=json.dumps(inputs),
headers={'content-type': 'application/json'})
res = res.json()
assert 'error' not in res, res
- return res['result']
+ if result_field:
+ res = res[result_field]
+
+ return Response(res)
+ def send_json_rpc_request(self, inputs):
+ return self.send_request("/json_rpc", inputs, 'result')
diff --git a/tests/functional_tests/test_framework/wallet.py b/tests/functional_tests/test_framework/wallet.py
index 357eab5b2..2ea2e4b68 100644
--- a/tests/functional_tests/test_framework/wallet.py
+++ b/tests/functional_tests/test_framework/wallet.py
@@ -32,8 +32,8 @@ from .rpc import JSONRPC
class Wallet(object):
- def __init__(self, protocol='http', host='127.0.0.1', port=18083, path='/json_rpc'):
- self.rpc = JSONRPC('{protocol}://{host}:{port}{path}'.format(protocol=protocol, host=host, port=port, path=path))
+ def __init__(self, protocol='http', host='127.0.0.1', port=18083):
+ self.rpc = JSONRPC('{protocol}://{host}:{port}'.format(protocol=protocol, host=host, port=port))
def make_uniform_destinations(self, address, transfer_amount, transfer_number_of_destinations=1):
destinations = []
@@ -60,7 +60,7 @@ class Wallet(object):
}
if(len(payment_id) > 0):
transfer['params'].update({'payment_id' : payment_id})
- return self.rpc.send_request(transfer)
+ return self.rpc.send_json_rpc_request(transfer)
def transfer_split(self, destinations, ringsize=7, payment_id=''):
print(destinations)
@@ -77,7 +77,7 @@ class Wallet(object):
}
if(len(payment_id) > 0):
transfer['params'].update({'payment_id' : payment_id})
- return self.rpc.send_request(transfer)
+ return self.rpc.send_json_rpc_request(transfer)
def create_wallet(self, index=''):
create_wallet = {
@@ -90,7 +90,7 @@ class Wallet(object):
'jsonrpc': '2.0',
'id': '0'
}
- return self.rpc.send_request(create_wallet)
+ return self.rpc.send_json_rpc_request(create_wallet)
def get_balance(self):
get_balance = {
@@ -98,7 +98,7 @@ class Wallet(object):
'jsonrpc': '2.0',
'id': '0'
}
- return self.rpc.send_request(get_balance)
+ return self.rpc.send_json_rpc_request(get_balance)
def sweep_dust(self):
sweep_dust = {
@@ -106,7 +106,7 @@ class Wallet(object):
'jsonrpc': '2.0',
'id': '0'
}
- return self.rpc.send_request(sweep_dust)
+ return self.rpc.send_json_rpc_request(sweep_dust)
def sweep_all(self, address):
sweep_all = {
@@ -117,4 +117,4 @@ class Wallet(object):
'jsonrpc': '2.0',
'id': '0'
}
- return self.rpc.send_request(sweep_all)
+ return self.rpc.send_json_rpc_request(sweep_all)