Added More Test Cases and Command Improvements

Tweaks to Commands:
1. Now catches and raises a more descriptive error when the node returns malformed JSON or JSON with non-unicode characters in it.
2. Removed get_txpool_backlog and get_output_distribution because they are actually binary RPC commands in disguise, maybe they can be added later.
3. Improved input validation for start_mining.
4. Catches NOT MINING status in set_log_hash_rate and raises descriptive error.
5. Adds abaility in out_peers and in_peers to set unlimited peers by passing -1 as arg.
6. Make input more intuitive in get_outs.
7. Shortened names of get_all_known_log_levels and get_all_known_log_categories.
8. Other very minor tweaks.
pull/90/head
Jeffrey Ryan 3 years ago
parent 27b3c4b54c
commit c7e5e966c7

@ -200,7 +200,13 @@ class JSONRPCDaemon(object):
code=rsp.status_code,
method=method))
result = rsp.json()
try:
result = rsp.json()
except ValueError as e:
_log.error(u"Could not parse JSON response from '{url}' during method '{method}'. Response:\n{resp}".format(
url=self.url, method=method, resp=rsp.text))
raise RPCError("Daemon returned an unreadable JSON response. It may contain unparseable binary characters.")
_ppresult = json.dumps(result, indent=2, sort_keys=True)
_log.debug(u"Result:\n{result}".format(result=_ppresult))
@ -870,60 +876,6 @@ class JSONRPCDaemon(object):
return self.raw_jsonrpc_request('sync_info')
def get_txpool_backlog(self):
"""
Get all transaction pool backlog.
Output:
{
"backlog": list; list of tx_backlog_entry with the following structure (in binary form)
{
"blob_size": unsigned int (in binary form).
"fee": unsigned int (in binary form).
"time_in_pool": unsigned int (in binary form).
}
"status": str; General RPC error code. "OK" means everything looks good.
"untrusted": bool; True for bootstrap mode, False for full sync mode.
}
"""
return self.raw_jsonrpc_request('get_txpool_backlog')
def get_output_distribution(self, amounts, cumulative=False, from_height=0, to_height=0):
"""
Get distribution of outputs for givens amount on the blockchain in a certain range of heights.
RingCT outputs are found with amount 0.
:param list amounts: amounts to look for of type int for atomic units, or Decimal for full Monero amounts
:param bool cumulative: true if the result should be cumulative
:param int from_height: starting height to check from
:param int to_height: ending height to check up to
Output:
{
"distributions": list; distribution informations with the following structure
{
"amount": unsigned int
"base": unsigned int
"distribution": array of unsigned int
"start_height": unsigned int
}
"status": str; General RPC error code. "OK" means everything looks good.
}
"""
# Coerce amounts paramter
if isinstance(amounts, (int, Decimal)):
amounts = [to_atomic(amounts) if isinstance(amounts, Decimal) else amounts]
elif not amounts:
raise ValueError('amounts must have at least one element')
return self.raw_jsonrpc_request('get_output_distribution', params={
'amounts': amounts,
'cumulative': cumulative,
'from_height': from_height,
'to_height': to_height})
# Other RPC Methods (https://www.getmonero.org/resources/developer-guides/daemon-rpc.html#other-daemon-rpc-calls)
def get_height(self):
@ -997,10 +949,10 @@ class JSONRPCDaemon(object):
return self.raw_request('/get_transactions', data={
'txs_hashes': tx_hashes,
'decode_as_json': decode_as_json,
'prune': prune})
'decode_as_json': bool(decode_as_json),
'prune': bool(prune)})
def get_alt_block_hashes(self):
def get_alt_blocks_hashes(self):
"""
Get the known blocks hashes which are not on the main chain.
@ -1012,7 +964,7 @@ class JSONRPCDaemon(object):
}
"""
return self.raw_request('/get_alt_block_hashes')
return self.raw_request('/get_alt_blocks_hashes')
def is_key_image_spent(self, key_images):
"""
@ -1035,7 +987,7 @@ class JSONRPCDaemon(object):
key_images = self._validate_hashlist(key_images)
return self.raw_request('/is_key_image_spent', data={'key images': key_images})
return self.raw_request('/is_key_image_spent', data={'key_images': key_images})
def send_raw_transaction(self, tx_as_hex, do_not_relay=False):
"""
@ -1082,17 +1034,26 @@ class JSONRPCDaemon(object):
}
"""
try:
address.address(miner_address)
except ValueError:
raise ValueError('miner_address "{}" does not match any recognized wallet format'.format(miner_address))
if isinstance(miner_address, address.Address):
miner_address = repr(miner_address)
else:
try:
converted_addr = address.address(miner_address)
except ValueError:
raise ValueError('miner_address "{}" does not match any recognized address format'.format(miner_address))
if not isinstance(converted_addr, address.Address):
raise ValueError('miner_address must be a "standard" monero address (i.e. it must start with a "4")')
if not isinstance(threads_count, int):
raise TypeError("threads_count must be an int")
if threads_count < 0:
raise ValueError('threads_count < 0')
return self.raw_request('/start_mining', data={
'do_background_mining': do_background_mining,
'ignore_battery': ignore_battery,
'do_background_mining': bool(do_background_mining),
'ignore_battery': bool(ignore_battery),
'miner_address': miner_address,
'threads_count': threads_count})
@ -1159,7 +1120,7 @@ class JSONRPCDaemon(object):
return self.raw_request('/get_peer_list')
def set_log_hashrate(self, visible):
def set_log_hash_rate(self, visible):
"""
Set the log hash rate display mode.
@ -1171,7 +1132,13 @@ class JSONRPCDaemon(object):
}
"""
return self.set_log_hashrate('/set_log_hashrate', data={'visible': visible})
resp = self.raw_request('/set_log_hash_rate', data={'visible': bool(visible)})
if resp['status'] == 'NOT MINING':
raise RPCError('The node at "{url}" is not currently mining and therefore cannot set its hash rate log visibility.'
.format(url=self.url))
return resp
def set_log_level(self, level):
"""
@ -1217,7 +1184,7 @@ class JSONRPCDaemon(object):
category, level = cat_str.split(':')
if category not in self._KNOWN_LOG_CATEGORIES:
_log.warn(u"Unrecognized log category: \"{}\"",format(category))
_log.warn(u"Unrecognized log category: \"{}\"".format(category))
if level not in self._KNOWN_LOG_LEVELS:
_log.warn(u"Unrecognized log level: \"{}\"".format(level))
@ -1257,6 +1224,7 @@ class JSONRPCDaemon(object):
"relayed": bool; States if this transaction has been relayed
"tx_blob": unsigned int; Hexadecimal blob represnting the transaction.
"tx_json": json string; JSON structure of all information in the transaction (see get_transactions() for structure information)
}
"status": str; General RPC error code. "OK" means everything looks good.
}
"""
@ -1274,10 +1242,11 @@ class JSONRPCDaemon(object):
"bytes_med" unsigned int; Median transaction size in pool.
"bytes_min" unsigned int; Min transaction size in pool.
"bytes_total": unsigned int; total size of all transactions in pool.
"histo": {
"txs": unsigned int; number of transactions.
"bytes": unsigned int; size in bytes.
}
"histo": list; histogram of transaction sizes with the following structure
{
"txs": unsigned int; number of transactions.
"bytes": unsigned int; size in bytes.
}
"histo_98pc": unsigned int; the time 98% of txes are "younger" than.
"num_10m": unsigned int; number of transactions in pool for more than 10 minutes.
"num_double_spends": unsigned int; number of double spend transactions.
@ -1346,7 +1315,7 @@ class JSONRPCDaemon(object):
"""
Limit number of outgoing peers.
:param int out_peers_arg: Max number of outgoing peers
:param int out_peers_arg: Max number of outgoing peers. If less than zero, allow unlimited outgoing peers
Output:
{
@ -1354,15 +1323,18 @@ class JSONRPCDaemon(object):
}
"""
out_peers_arg = int(out_peers_arg)
if out_peers_arg < 0:
out_peers_arg = 2**32 - 1
elif out_peers_arg > 2**32 - 1:
raise ValueError('out_peers_arg "{}" is too large'.format(out_peers_arg))
return self.raw_request('/out_peers', data={'out_peers': out_peers_arg})
return self.raw_request('/out_peers', data={'out_peers': int(out_peers_arg)})
def in_peers(self, in_peers_arg):
"""
Limit number of Incoming peers.
Limit number of incoming peers.
:param int in_peers_arg: Max number of incoming peers
:param int in_peers_arg: Max number of incoming peers. If less than zero, allow unlimited incoming peers
Output:
{
@ -1370,23 +1342,21 @@ class JSONRPCDaemon(object):
}
"""
in_peers_arg = int(in_peers_arg)
if in_peers_arg < 0:
in_peers_arg = 2**32 - 1
elif in_peers_arg > 2**32 - 1:
raise ValueError('in_peers_arg "{}" is too large'.format(in_peers_arg))
return self.raw_request('/in_peers', data={'in_peers': in_peers_arg})
return self.raw_request('/in_peers', data={'in_peers': int(in_peers_arg)})
def get_outs(self, outputs, get_txid=True):
def get_outs(self, amount, index, get_txid=True):
"""
Get information about one-time outputs (stealth addresses).
:param list outputs: array of output structures, as defined below
:param bool get_txid: If true, a txid will included for each output in the response.
Structure for outputs parameter:
{
"amount": unsigned int in atomic units or Decimal in full monero units (1e12 atmomic units)
"index": unsigned int; output's global index
}
:param amount: list or single element of output amount. Decimal for full monero amounts or int for atmoic units
:param index: list of single element of output index. int
:param bool get_txid: Optional. If true, a txid will included for each output in the response.
Output:
{
@ -1403,16 +1373,17 @@ class JSONRPCDaemon(object):
}
"""
# Validate outputs paramter
if isinstance(outputs, dict):
outputs = [outputs]
for i, out in enumerate(outputs):
if 'amount' not in out or 'index' not in out:
raise ValueError('structure of outputs is malformed')
amount, index = out['amount'], out['index']
if isinstance(amount, Decimal):
amount = to_atomic(amount)
outputs[i] = {'amount': amount, 'index': index}
if isinstance(index, int): # single element
outputs = [{'amount': amount if isinstance(amount, int) else to_atomic(amount), 'index': index}]
else: # multiple elements
if len(amount) != len(index):
raise ValueError('length of amount and index do not match')
outputs = []
for a, i in zip(amount, index):
outputs.append({
'amount': a if isinstance(a, int) else to_atomic(a),
'index': i})
return self.raw_request('/get_outs', data={
'outputs': outputs,
@ -1455,11 +1426,11 @@ class JSONRPCDaemon(object):
# Supporting class methods
@classmethod
def get_all_known_log_categories(cls):
def known_log_categories(cls):
return cls._KNOWN_LOG_CATEGORIES
@classmethod
def get_all_known_log_levels(cls):
def known_log_levels(cls):
return cls._KNOWN_LOG_LEVELS
# private methods

@ -0,0 +1,6 @@
{
"blks_hashes": ["9c2277c5470234be8b32382cdf8094a103aba4fcd5e875a6fc159dc2ec00e011","637c0e0f0558e284493f38a5fcca3615db59458d90d3a5eff0a18ff59b83f46f","6f3adc174a2e8082819ebb965c96a095e3e8b63929ad9be2d705ad9c086a6b1c","697cf03c89a9b118f7bdf11b1b3a6a028d7b3617d2d0ed91322c5709acf75625","d99b3cf3ac6f17157ac7526782a3c3b9537f89d07e069f9ce7821d74bd9cad0e","e97b62109a6303233dcd697fa8545c9fcbc0bf8ed2268fede57ddfc36d8c939c","70ff822066a53ad64b04885c89bbe5ce3e537cdc1f7fa0dc55317986f01d1788","b0d36b209bd0d4442b55ea2f66b5c633f522401f921f5a85ea6f113fd2988866"],
"status": "OK",
"untrusted": false
}

@ -0,0 +1,6 @@
{
"hash": "228c0538b7ba7d28fdd58ed310326db61ea052038bdb42652f6e1852cf666325",
"height": 2294632,
"status": "OK",
"untrusted": false
}

@ -0,0 +1,6 @@
{
"limit_down": 8192,
"limit_up": 2048,
"status": "OK",
"untrusted": false
}

@ -0,0 +1,36 @@
{
"credits": 0,
"outs": [
{
"height": 1224094,
"key": "fc13952b8b9c193d4c875e750e88a0da8a7d348f95c019cfde93762d68298dd7",
"mask": "bf99dc047048605f6e0aeebc937477ae6e9e3143e1be1b48af225b41f809e44e",
"txid": "687f9b1d6fa409a13e84c682e90127b1953e10efe679c114a01d7db77f474d50",
"unlocked": true
},
{
"height": 1224094,
"key": "23212433aec99219a823f107bcd27cb45a292d5c831b096d8e655e77e133b27e",
"mask": "3ad2a785a992b1491713efc809996e7007c9fabaf962edf10961d60aaa0dace7",
"txid": "687f9b1d6fa409a13e84c682e90127b1953e10efe679c114a01d7db77f474d50",
"unlocked": true
},
{
"height": 999999,
"key": "fc22266e72afb339559e8938c99ee86157bfea20cd6115c477c40a53bc173378",
"mask": "02fa353aa84ea8c44c8023065d7941606b1fa5c264dccf46dc6494ebe9606f20",
"txid": "2a5d456439f7ae27b5d26e493651c0e24e1d7e02b6d9d019c89d562ce0658472",
"unlocked": true
},
{
"height": 999999,
"key": "e20315663e3d278421797c4098c828cad5220849d08c3d26fee72003d4cda698",
"mask": "100c6f1342b71b73edddc5492be923182f00a683488ec3a2a1c7a949cbe57768",
"txid": "2a5d456439f7ae27b5d26e493651c0e24e1d7e02b6d9d019c89d562ce0658472",
"unlocked": true
}
],
"status": "OK",
"top_hash": "",
"untrusted": false
}

@ -0,0 +1,15 @@
{
"credits": 0,
"outs": [
{
"height": 1222460,
"key": "9c7055cb5b790f1eebf10b7b8fbe01241eb736b5766d15554da7099bbcdc4b44",
"mask": "42e37af85cddaeccbea6fe597037c9377045a682e66661260868877b9440af70",
"txid": "b357374ad4636f17520b6c2fdcf0fb5e6a1185fed2aef509b19b5100d04ae552",
"unlocked": true
}
],
"status": "OK",
"top_hash": "",
"untrusted": false
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

@ -0,0 +1,62 @@
{
"credits": 0,
"pool_stats": {
"bytes_max": 17922,
"bytes_med": 1967,
"bytes_min": 1452,
"bytes_total": 75438,
"fee_total": 586900000,
"histo": [
{
"bytes": 3419,
"txs": 2
},
{
"bytes": 3423,
"txs": 2
},
{
"bytes": 1455,
"txs": 1
},
{
"bytes": 14851,
"txs": 1
},
{
"bytes": 0,
"txs": 0
},
{
"bytes": 0,
"txs": 0
},
{
"bytes": 22776,
"txs": 5
},
{
"bytes": 0,
"txs": 0
},
{
"bytes": 0,
"txs": 0
},
{
"bytes": 29514,
"txs": 6
}
],
"histo_98pc": 0,
"num_10m": 0,
"num_double_spends": 0,
"num_failing": 0,
"num_not_relayed": 0,
"oldest": 1613865632,
"txs_total": 17
},
"status": "OK",
"top_hash": "",
"untrusted": false
}

File diff suppressed because one or more lines are too long

@ -0,0 +1,5 @@
{
"in_peers": 128,
"status": "OK",
"untrusted": false
}

@ -0,0 +1,5 @@
{
"in_peers": 4294967295,
"status": "OK",
"untrusted": false
}

@ -0,0 +1,12 @@
{
"credits": 0,
"spent_status": [
1,
1,
0,
2
],
"status": "OK",
"top_hash": "",
"untrusted": false
}

@ -0,0 +1 @@
{"status": "OK", "untrusted": false}

@ -0,0 +1,19 @@
{
"active": false,
"address": "",
"bg_idle_threshold": 0,
"bg_ignore_battery": false,
"bg_min_idle_seconds": 0,
"bg_target": 0,
"block_reward": 0,
"block_target": 120,
"difficulty": 250236969769,
"difficulty_top64": 0,
"is_background_mining_enabled": false,
"pow_algorithm": "RandomX",
"speed": 0,
"status": "OK",
"threads_count": 0,
"untrusted": false,
"wide_difficulty": "0x3a43492329"
}

@ -0,0 +1,19 @@
{
"active": true,
"address": "497e4umLC7pfJ5TSSuU1QY8E1Nh5h5cWfYnvvpTrYFKiQriWfVYeVn2KH8Hpp3AeDRbCSxTvZuUZ1WYd8PGLqM4r5P5hjNQ",
"bg_idle_threshold": 0,
"bg_ignore_battery": false,
"bg_min_idle_seconds": 0,
"bg_target": 0,
"block_reward": 1162352277907,
"block_target": 120,
"difficulty": 252551179535,
"difficulty_top64": 0,
"is_background_mining_enabled": false,
"pow_algorithm": "RandomX",
"speed": 6,
"status": "OK",
"threads_count": 4,
"untrusted": false,
"wide_difficulty": "0x3acd392d0f"
}

@ -0,0 +1,5 @@
{
"out_peers": 16,
"status": "OK",
"untrusted": false
}

@ -0,0 +1,5 @@
{
"out_peers": 4294967295,
"status": "OK",
"untrusted": false
}

@ -0,0 +1,4 @@
{
"status": "OK",
"untrusted": false
}

@ -0,0 +1,5 @@
{
"categories": "default:INFO",
"status": "OK",
"untrusted": false
}

@ -0,0 +1,5 @@
{
"categories": "",
"status": "OK",
"untrusted": false
}

@ -0,0 +1,5 @@
{
"categories": "logging:INFO,net:FATAL",
"status": "OK",
"untrusted": false
}

@ -0,0 +1,4 @@
{
"status": "OK",
"untrusted": false
}

@ -0,0 +1,4 @@
{
"status": "NOT MINING",
"untrusted": false
}

@ -0,0 +1,4 @@
{
"status": "OK",
"untrusted": false
}

@ -0,0 +1,4 @@
{
"status": "OK",
"untrusted": false
}

@ -0,0 +1,10 @@
{
"auto_uri": "",
"hash": "",
"path": "",
"status": "OK",
"untrusted": false,
"update": false,
"user_uri": "",
"version": ""
}

@ -17,6 +17,25 @@ class JSONRPCDaemonTestCase(JSONTestCase):
mempool_url = 'http://127.0.0.1:18081/get_transaction_pool'
transactions_url = 'http://127.0.0.1:18081/get_transactions'
sendrawtransaction_url = 'http://127.0.0.1:18081/sendrawtransaction'
getheight_url = 'http://127.0.0.1:18081/get_height'
getaltblockshashes_url = 'http://127.0.0.1:18081/get_alt_blocks_hashes'
iskeyimagespent_url = 'http://127.0.0.1:18081/is_key_image_spent'
startmining_url = 'http://127.0.0.1:18081/start_mining'
stopmining_url = 'http://127.0.0.1:18081/stop_mining'
miningstatus_url = 'http://127.0.0.1:18081/mining_status'
savebc_url = 'http://127.0.0.1:18081/save_bc'
getpeerlist_url = 'http://127.0.0.1:18081/get_peer_list'
setloghashrate_url = 'http://127.0.0.1:18081/set_log_hash_rate'
setloglevel_url = 'http://127.0.0.1:18081/set_log_level'
setlogcategories_url = 'http://127.0.0.1:18081/set_log_categories'
gettransactionpoolstats_url = 'http://127.0.0.1:18081/get_transaction_pool_stats'
stopdaemon_url = 'http://127.0.0.1:18081/stop_daemon'
getlimit_url = 'http://127.0.0.1:18081/get_limit'
setlimit_url = 'http://127.0.0.1:18081/set_limit'
outpeers_url = 'http://127.0.0.1:18081/out_peers'
inpeers_url = 'http://127.0.0.1:18081/in_peers'
getouts_url = 'http://127.0.0.1:18081/get_outs'
update_url = 'http://127.0.0.1:18081/update'
data_subdir = 'test_jsonrpcdaemon'
def setUp(self):
@ -788,4 +807,401 @@ class JSONRPCDaemonTestCase(JSONTestCase):
@responses.activate
def test_get_output_distribution(self):
pass
pass
@responses.activate
def test_get_height(self):
responses.add(responses.POST, self.getheight_url,
json=self._read('test_get_height_2294632.json'),
status=200)
resp = self.backend.get_height()
self.assertEqual(resp['height'], 2294632)
self.assertEqual(resp['hash'], '228c0538b7ba7d28fdd58ed310326db61ea052038bdb42652f6e1852cf666325')
@responses.activate
def test_get_transactions(self):
responses.add(responses.POST, self.transactions_url,
json=self._read('test_get_transactions.json'),
status=200)
resp = self.backend.get_transactions(['c6802ca1805784c7330e71521a318c302f88625b0eab80740cfab24af7b0cb93',
'ae476cae3ad2b44c6c2614b9435adbe304e24c59d410fe3b4dd046a9021d66b9'], decode_as_json=True, prune=False)
self.assertEqual(resp['txs_as_hex'][0],
'02000102000bbdef820aa8e5f901a3cc79a337b5d601aadb01af55fb44d90b8f0c850bda856b62f04c77bf46f46954b8caa82097b9386b0a501277262'
'c68601ed917ac020002d4871f51832c1951212b3d87395a8df5594e927588221e665a018c098968cc8b000262d52033f1a3c921f1db56eca9b5dd50f3'
'6ecf76e22e67cc694fb7c214a609ef2c0164d167ad3d6f762414567f5812585fe7f48d3400cd51d999b020899658526567020901795c32b0b5ba9ce30'
'580cfe81a73e3d349bd53dfa8fa66a93993f6921ff0457386671f45ef33760947513b71ec6398ff0e2ebb5edde433e452b9035112714a290806bedd15'
'cd5293ea33be5bec57e983042840d75f66b7ca31c599f4ca0179a0ea918ad50e7e2d10784dffb56eeb39d4c92236c4904c1ecebd5b90ad6b797f9bace'
'3566394d8a2558232b8f8262317dcc3da8038bc46119eb891371d71f940e15801c6ddf29ddf034c0e277ce87d4343cb821fece9c74b1e04ecec914c34'
'c5b7bb518042b1ca36de80e451aca0c44d93298d4a4ec31a2a1aa8b5b437a6aab043ed66a681ba1f5f7773fa140176ddcdb2a0e7c9ae3163094671bf3'
'48d5504bc2b168891c1a599ce00acfe3a430c9c58370fe53b96a4d64155b03b5f30ee0307f95bde576a409f86100e2bcfe81d19d2407134310dce6ff9'
'53f668e810d48f12f3fd61b6c0f00819a18b75ae762b03dd97ba40ffc5a791318bf019f989e85f09385b4b9b74c8500321fb918e13f3bbd0a2fdf5749'
'318f152e2a0f994f11b848e0157e0b3c44afab6653ef0815ad1e0d76ba1141d5abb61f8b078a57413d3374b3aa78fbea4de0604d470a12a21da6fea3c'
'765e9ddec5d50b8f76f079f9c61c17b25822612418444181adf9e334a695aaabec779edf8842dbdbae99461c608dc0096113d8da040c3b5bd94dc796a'
'a1daaa740839fd0f4363fa60b8c3e84fddc43075ae595d323dc7cff3db06df16d2eb08bdceca623de357e6cfd59deedcba29a203bbbbaadcc18bdd1ce'
'03ba2d1bd16bc5e666b8006b473f339841199c5c183c5b78b420bd896cd50b2aa7b9f9fcba3615abe0f734c830320a8e9830976bbcae9a8e77676c4a3'
'd944a3dca84fed10d242b12da93e9e37be272a933b45b23b8f4c20c8dc6bc3f0d56878639b7900f505e6060806939e9f7f417fd10965fc564c7f00893'
'b920b6c32ff2e546dd40ec4414a110b052e97d3d74255190c0032f0826f87f155779e21e9b5b6a9300d6a2bde804ec0e107cde3f600ab5572cdda0964'
'5147cbbfcbf4c3681cb6965770ebb2b298151c354e45aab0e1ab77d414a30362a0091230d667e4ac44f5448cdf319b170c92f20d8c44b620f3b451732'
'8bf4ece003dba9db9a71d0531ddd329adbcc4f6c408caeaebabe477bc1084f1da40cc4ca03e7ecc5f173167accf433667b9d3b61fdfd8cfe3498951e8'
'912d6ef9a79401e0913b53dc6a14a8e117d053f82020e808b45e15b85d7102d1ccc87739c22124b058b2ab71e67f71598bf2b43813fb408df5ecafb1c'
'a6c9761256958b017d7ab00d4a0a3fdb9ed61908d6559b853d254d86bd50fd85b349a8a9c5386850e4b9ad0984eed4614a896488fcb1b63cf795a56c1'
'2425dfbfe19ee09ac4375e6eb220e0ff2254d80d1f9fb865d5b176003779f5064ae0949aee694ac89df0fee4d1f3104d8bf76b06773b27c6ca86e69fb'
'94146ca082a76af02d3e76c6e635fc3bb524084f89e8428a61207a4660e9c666e3e74762a62330d56753ad8fa7cf1267f5490c3ff7596b99bdda95e1b'
'd409107ec45e09a0650f0a8c32dfddfbca2000602c705427b0fb904ef582e435fef3de34b6a3a07a67031fea098a28695cce2908f230e80cadb40036b'
'6da75aef391ce13231674edb807af5e04f686027cae475274007b85ff58b13527f6b1125438e734ac8a1206255a332824004a1a2de925584140398089'
'f34018fce44f334f3b283e58900ffa58f7c2a63641f7d4d946fb44e2b18f4ce36baf1b06c026e4055dadb7b06a239a003ba571528a0f16f0f1dd4cbd730')
self.assertEqual(resp['txs'][0]['block_height'], 2295433)
self.assertEqual(resp['txs'][0]['block_timestamp'], 1613160690)
self.assertEqual(resp['txs'][1]['in_pool'], True)
json.loads(resp['txs'][0]['as_json'])
@responses.activate
def test_get_alt_blocks_hashes(self):
responses.add(responses.POST, self.getaltblockshashes_url,
json=self._read('test_get_alt_blocks_hashes_doc_example.json'),
status=200)
resp = self.backend.get_alt_blocks_hashes()
hashes = ["9c2277c5470234be8b32382cdf8094a103aba4fcd5e875a6fc159dc2ec00e011","637c0e0f0558e284493f38a5fcca3615db59458d90d3a5eff0a18ff59b83f46f",
"6f3adc174a2e8082819ebb965c96a095e3e8b63929ad9be2d705ad9c086a6b1c","697cf03c89a9b118f7bdf11b1b3a6a028d7b3617d2d0ed91322c5709acf75625",
"d99b3cf3ac6f17157ac7526782a3c3b9537f89d07e069f9ce7821d74bd9cad0e","e97b62109a6303233dcd697fa8545c9fcbc0bf8ed2268fede57ddfc36d8c939c",
"70ff822066a53ad64b04885c89bbe5ce3e537cdc1f7fa0dc55317986f01d1788","b0d36b209bd0d4442b55ea2f66b5c633f522401f921f5a85ea6f113fd2988866"]
self.assertEqual(resp['blks_hashes'], hashes)
@responses.activate
def test_is_key_image_spent(self):
responses.add(responses.POST, self.iskeyimagespent_url,
json=self._read('test_is_key_image_spent.json'),
status=200)
key_imgs = ['8d1bd8181bf7d857bdb281e0153d84cd55a3fcaa57c3e570f4a49f935850b5e3', '7319134bfc50668251f5b899c66b005805ee255c136f0e1cecbb0f3a912e09d4',
'8d1bd8181bf7d857bdb281e0153d84cd55a3fcaa57c3e570f4a49f935850b5e2', 'fbbd6ac46dc4905c455f3b51595da6b135a5e9a64c6c181875558649be0ab183']
resp = self.backend.is_key_image_spent(key_imgs)
self.assertEqual(resp['spent_status'], [1, 1, 0, 2])
@responses.activate
def test_send_raw_transaction(self):
pass
@responses.activate
def test_start_mining(self):
responses.add(responses.POST, self.startmining_url,
json=self._read('test_mining.json'),
status=200)
mining_addr = '497e4umLC7pfJ5TSSuU1QY8E1Nh5h5cWfYnvvpTrYFKiQriWfVYeVn2KH8Hpp3AeDRbCSxTvZuUZ1WYd8PGLqM4r5P5hjNQ'
resp = self.backend.start_mining(False, True, mining_addr, 4)
self.assertEqual(resp['status'], 'OK')
with self.assertRaises(ValueError):
self.backend.start_mining(False, True, mining_addr, -1)
with self.assertRaises(ValueError):
self.backend.start_mining(False, True, "meepmoopthisisntarealminingaddress", 2)
@responses.activate
def test_stop_mining(self):
responses.add(responses.POST, self.stopmining_url,
json=self._read('test_mining.json'),
status=200)
resp = self.backend.stop_mining()
self.assertEqual(resp['status'], 'OK')
@responses.activate
def test_mining_status_off(self):
responses.add(responses.POST, self.miningstatus_url,
json=self._read('test_mining_status_off.json'),
status=200)
resp = self.backend.mining_status()
self.assertEqual(resp['active'], False)
self.assertEqual(resp['difficulty'], 250236969769)
self.assertEqual(resp['pow_algorithm'], 'RandomX')
@responses.activate
def test_mining_status_on(self):
responses.add(responses.POST, self.miningstatus_url,
json=self._read('test_mining_status_on.json'),
status=200)
resp = self.backend.mining_status()
self.assertEqual(resp['active'], True)
self.assertEqual(resp['difficulty'], 252551179535)
self.assertEqual(resp['pow_algorithm'], 'RandomX')
self.assertEqual(resp['address'], "497e4umLC7pfJ5TSSuU1QY8E1Nh5h5cWfYnvvpTrYFKiQriWfVYeVn2KH8Hpp3AeDRbCSxTvZuUZ1WYd8PGLqM4r5P5hjNQ")
@responses.activate
def test_save_bc(self):
responses.add(responses.POST, self.savebc_url,
json=self._read('test_save_bc.json'),
status=200)
resp = self.backend.save_bc()
self.assertEqual(resp['status'], 'OK')
@responses.activate
def test_get_peer_list(self):
responses.add(responses.POST, self.getpeerlist_url,
json=self._read('test_get_peer_list.json'),
status=200)
resp = self.backend.get_peer_list()
white0 = resp['white_list'][0]
gray0 = resp['gray_list'][0]
self.assertEqual(white0['host'], '204.8.15.5')
self.assertEqual(white0['ip'], 84871372)
self.assertEqual(white0['port'], 18080)
self.assertEqual(white0['id'], 702714784157243868)
self.assertEqual(gray0['host'], '92.233.45.0')
self.assertEqual(gray0['ip'], 3008860)
self.assertEqual(gray0['port'], 5156)
self.assertEqual(gray0['id'], 779474923786790553)
@responses.activate
def test_set_log_hashrate_mining(self):
responses.add(responses.POST, self.setloghashrate_url,
json=self._read('test_set_log_hash_rate_mining.json'),
status=200)
resp = self.backend.set_log_hash_rate(True)
self.assertEqual(resp['status'], 'OK')
@responses.activate
def test_set_log_hashrate_notmining(self):
responses.add(responses.POST, self.setloghashrate_url,
json=self._read('test_set_log_hash_rate_notmining.json'),
status=200)
with self.assertRaises(RPCError):
resp = self.backend.set_log_hash_rate(False)
@responses.activate
def test_set_log_level(self):
responses.add(responses.POST, self.setloglevel_url,
json=self._read('test_set_log_level.json'),
status=200)
resp = self.backend.set_log_level(1)
with self.assertRaises(ValueError):
resp = self.backend.set_log_level(5)
self.assertEqual(JSONRPCDaemon.known_log_levels(), JSONRPCDaemon._KNOWN_LOG_LEVELS)
@responses.activate
def test_set_log_categories_default(self):
responses.add(responses.POST, self.setlogcategories_url,
json=self._read('test_set_log_categories_default.json'),
status=200)
resp = self.backend.set_log_categories('default:INFO')
self.assertEqual(resp['status'], 'OK')
self.assertEqual(resp['categories'], 'default:INFO')
self.assertEqual(JSONRPCDaemon.known_log_categories(), JSONRPCDaemon._KNOWN_LOG_CATEGORIES)
@responses.activate
def test_set_log_categories_multiple(self):
responses.add(responses.POST, self.setlogcategories_url,
json=self._read('test_set_log_categories_multiple.json'),
status=200)
resp = self.backend.set_log_categories(['logging:INFO', 'net:FATAL'])
self.assertEqual(resp['categories'], 'logging:INFO,net:FATAL')
self.assertEqual(resp['status'], 'OK')
@responses.activate
def test_set_log_categories_empty(self):
responses.add(responses.POST, self.setlogcategories_url,
json=self._read('test_set_log_categories_empty.json'),
status=200)
resp = self.backend.set_log_categories()
self.assertEqual(resp['categories'], '')
self.assertEqual(resp['status'], 'OK')
@responses.activate
def test_get_transaction_pool(self):
responses.add(responses.POST, self.mempool_url,
json=self._read('test_get_transaction_pool.json'),
status=200)
resp = self.backend.get_transaction_pool()
self.assertEqual(resp['spent_key_images'][0]['id_hash'], '1ccff101d8ee93903bde0a8ef99ebc99ccd5150f7157b93b758cd456458d4166')
self.assertEqual(resp['spent_key_images'][0]['txs_hashes'][0], '73fe0207e25d59dce5cd6f7369edf33a04ac56409eca3b28ad837c43640ef83f')
self.assertEqual(resp['transactions'][0]['id_hash'], 'ea020cf2595c017d5fd4d0d427b8ff02b1857e996136b041c0f7fd6dffc4c72c')
@responses.activate
def test_get_transaction_pool_stats(self):
responses.add(responses.POST, self.gettransactionpoolstats_url,
json=self._read('test_get_transaction_pool_stats.json'),
status=200)
resp = self.backend.get_transaction_pool_stats()
self.assertEqual(resp['pool_stats']['bytes_total'], 75438)
self.assertEqual(resp['pool_stats']['txs_total'], 17)
self.assertEqual(resp['pool_stats']['histo'][0]['bytes'], 3419)
self.assertEqual(resp['pool_stats']['histo'][0]['txs'], 2)
@responses.activate
def test_stop_daemon(self):
responses.add(responses.POST, self.stopdaemon_url,
json=self._read('test_stop_daemon.json'),
status=200)
resp = self.backend.stop_daemon()
self.assertEqual(resp['status'], 'OK')
@responses.activate
def test_get_limit(self):
responses.add(responses.POST, self.getlimit_url,
json=self._read('test_get_limit.json'),
status=200)
resp = self.backend.get_limit()
self.assertEqual(resp['limit_down'], 8192)
self.assertEqual(resp['limit_up'], 2048)
self.assertEqual(resp['status'], 'OK')
@responses.activate
def test_out_peers(self):
responses.add(responses.POST, self.outpeers_url,
json=self._read('test_out_peers.json'),
status=200)
resp = self.backend.out_peers(16)
self.assertEqual(resp['out_peers'], 16)
with self.assertRaises(ValueError):
resp = self.backend.out_peers(2**32)
@responses.activate
def test_out_peers_unlimited(self):
responses.add(responses.POST, self.outpeers_url,
json=self._read('test_out_peers_unlimited.json'),
status=200)
resp = self.backend.out_peers(-1)
self.assertEqual(resp['out_peers'], 2**32 - 1)
@responses.activate
def test_in_peers(self):
responses.add(responses.POST, self.inpeers_url,
json=self._read('test_in_peers.json'),
status=200)
resp = self.backend.in_peers(128)
self.assertEqual(resp['in_peers'], 128)
with self.assertRaises(ValueError):
resp = self.backend.in_peers(2**32)
@responses.activate
def test_in_peers_unlimited(self):
responses.add(responses.POST, self.inpeers_url,
json=self._read('test_in_peers_unlimited.json'),
status=200)
resp = self.backend.in_peers(-1)
self.assertEqual(resp['in_peers'], 2**32 - 1)
@responses.activate
def test_get_outs(self):
responses.add(responses.POST, self.getouts_url,
json=self._read('test_get_outs_multiple.json'),
status=200)
a = [0, decimal.Decimal('0'), decimal.Decimal('20'), int(3e12)]
i = [20000, 20001, 50630, 232237]
resp = self.backend.get_outs(a, i)
self.assertEqual(resp['outs'][0]['height'], 1224094)
self.assertEqual(resp['outs'][0]['key'], 'fc13952b8b9c193d4c875e750e88a0da8a7d348f95c019cfde93762d68298dd7')
self.assertEqual(resp['outs'][0]['mask'], 'bf99dc047048605f6e0aeebc937477ae6e9e3143e1be1b48af225b41f809e44e')
self.assertEqual(resp['outs'][0]['txid'], '687f9b1d6fa409a13e84c682e90127b1953e10efe679c114a01d7db77f474d50')
self.assertEqual(resp['outs'][0]['unlocked'], True)
self.assertEqual(resp['outs'][3]['height'], 999999)
self.assertEqual(resp['outs'][3]['key'], 'e20315663e3d278421797c4098c828cad5220849d08c3d26fee72003d4cda698')
self.assertEqual(resp['outs'][3]['mask'], '100c6f1342b71b73edddc5492be923182f00a683488ec3a2a1c7a949cbe57768')
self.assertEqual(resp['outs'][3]['txid'], '2a5d456439f7ae27b5d26e493651c0e24e1d7e02b6d9d019c89d562ce0658472')
self.assertEqual(resp['outs'][3]['unlocked'], True)
@responses.activate
def test_get_outs_single(self):
responses.add(responses.POST, self.getouts_url,
json=self._read('test_get_outs_single.json'),
status=200)
resp = self.backend.get_outs(0, 10000)
self.assertEqual(resp['outs'][0]['height'], 1222460)
self.assertEqual(resp['outs'][0]['key'], '9c7055cb5b790f1eebf10b7b8fbe01241eb736b5766d15554da7099bbcdc4b44')
self.assertEqual(resp['outs'][0]['mask'], '42e37af85cddaeccbea6fe597037c9377045a682e66661260868877b9440af70')
self.assertEqual(resp['outs'][0]['txid'], 'b357374ad4636f17520b6c2fdcf0fb5e6a1185fed2aef509b19b5100d04ae552')
self.assertEqual(resp['outs'][0]['unlocked'], True)
@responses.activate
def test_update_check_none(self):
responses.add(responses.POST, self.update_url,
json=self._read('test_update_check_none.json'),
status=200)
resp = self.backend.update('check')
self.assertEqual(resp['update'], False)
self.assertEqual(resp['auto_uri'], '')
self.assertEqual(resp['hash'], '')
self.assertEqual(resp['path'], '')
self.assertEqual(resp['user_uri'], '')
self.assertEqual(resp['version'], '')
self.assertEqual(resp['status'], 'OK')
with self.assertRaises(ValueError):
self.backend.update('badcommandrightherebuddyolpal')
@responses.activate
def test_restricted_false(self):
responses.add(responses.POST, self.jsonrpc_url,
json=self._read('test_sync_info.json'),
status=200)
self.assertFalse(self.backend.restricted())
@responses.activate
def test_restricted_true(self):
responses.add(responses.POST, self.jsonrpc_url,
json=self._read('test_method_not_found.json'),
status=200)
self.assertTrue(self.backend.restricted())
Loading…
Cancel
Save