diff --git a/neo/Core/Blockchain.py b/neo/Core/Blockchain.py index 88ee05b20..195ac17e6 100644 --- a/neo/Core/Blockchain.py +++ b/neo/Core/Blockchain.py @@ -308,6 +308,9 @@ def GetAssetState(self, assetId): def SearchAssetState(self, query): pass + def ShowAllAssets(self, query): + pass + def GetHeaderHash(self, height): pass diff --git a/neo/Implementations/Blockchains/LevelDB/LevelDBBlockchain.py b/neo/Implementations/Blockchains/LevelDB/LevelDBBlockchain.py index c46ed96e0..49a22e9b9 100644 --- a/neo/Implementations/Blockchains/LevelDB/LevelDBBlockchain.py +++ b/neo/Implementations/Blockchains/LevelDB/LevelDBBlockchain.py @@ -365,6 +365,12 @@ def GetAssetState(self, assetId): return asset + def ShowAllAssets(self): + + assets = DBCollection(self._db, DBPrefix.ST_Asset, AssetState) + keys = assets.Keys + return keys + def GetTransaction(self, hash): if type(hash) is str: diff --git a/neo/Implementations/Blockchains/LevelDB/test_LevelDBBlockchain.py b/neo/Implementations/Blockchains/LevelDB/test_LevelDBBlockchain.py index 48155c5ea..2277321c0 100644 --- a/neo/Implementations/Blockchains/LevelDB/test_LevelDBBlockchain.py +++ b/neo/Implementations/Blockchains/LevelDB/test_LevelDBBlockchain.py @@ -74,3 +74,7 @@ def test_GetHeaderBy(self): invalid_bc_height = self._blockchain.Height + 1 block = self._blockchain.GetHeaderBy(invalid_bc_height) self.assertEqual(block, None) + + def test_ShowAllAssets(self): + assets = Blockchain.Default().ShowAllAssets() + self.assertEqual(len(assets), 2) diff --git a/neo/Prompt/Commands/Search.py b/neo/Prompt/Commands/Search.py new file mode 100644 index 000000000..4404f92b9 --- /dev/null +++ b/neo/Prompt/Commands/Search.py @@ -0,0 +1,75 @@ +from neo.Prompt.CommandBase import CommandBase, CommandDesc, ParameterDesc +from neo.Prompt.PromptData import PromptData +from neo.Prompt.Utils import get_arg +from neo.Core.Blockchain import Blockchain +from prompt_toolkit.shortcuts import print_formatted_text +from prompt_toolkit.formatted_text import FormattedText +from neo.logging import log_manager +import json + + +logger = log_manager.getLogger() + + +class CommandSearch(CommandBase): + def __init__(self): + super().__init__() + + self.register_sub_command(CommandSearchAsset()) + self.register_sub_command(CommandSearchContract()) + + def command_desc(self): + return CommandDesc('search', 'search the blockchain') + + def execute(self, arguments): + item = get_arg(arguments) + + if not item: + print("run `search help` to see supported queries") + return + + try: + return self.execute_sub_command(item, arguments[1:]) + except KeyError: + print(f"search: {item} is an invalid parameter") + return + + +class CommandSearchAsset(CommandBase): + def __init__(self): + super().__init__() + + def execute(self, arguments): + item = get_arg(arguments) + + results = Blockchain.Default().SearchAssetState(item) + print("Found %s results for %s" % (len(results), item)) + for asset in results: + bjson = json.dumps(asset.ToJson(), indent=4) + tokens = [("class:number", bjson)] + print_formatted_text(FormattedText(tokens), style=PromptData.Prompt.token_style) + return results + + def command_desc(self): + p1 = ParameterDesc('query', 'supports name, issuer, or admin searches') + return CommandDesc('asset', 'perform an asset search', [p1]) + + +class CommandSearchContract(CommandBase): + def __init__(self): + super().__init__() + + def execute(self, arguments): + item = get_arg(arguments) + + contracts = Blockchain.Default().SearchContracts(query=item) + print("Found %s results for %s" % (len(contracts), item)) + for contract in contracts: + bjson = json.dumps(contract.ToJson(), indent=4) + tokens = [("class:number", bjson)] + print_formatted_text(FormattedText(tokens), style=PromptData.Prompt.token_style) + return contracts + + def command_desc(self): + p1 = ParameterDesc('query', 'supports name, author, description, or email searches') + return CommandDesc('contract', 'perform a contract search', [p1]) diff --git a/neo/Prompt/Commands/Show.py b/neo/Prompt/Commands/Show.py new file mode 100644 index 000000000..026e8b6a9 --- /dev/null +++ b/neo/Prompt/Commands/Show.py @@ -0,0 +1,339 @@ +import os +import psutil +import datetime +from neo.Prompt.CommandBase import CommandBase, CommandDesc, ParameterDesc +from neo.Prompt.PromptData import PromptData +from neo.Prompt.Utils import get_arg +from neo.Core.Blockchain import Blockchain +from prompt_toolkit.shortcuts import print_formatted_text +from prompt_toolkit.formatted_text import FormattedText +from neocore.UInt256 import UInt256 +from neo.IO.MemoryStream import StreamManager +from neo.Network.NodeLeader import NodeLeader +from neo.Implementations.Notifications.LevelDB.NotificationDB import NotificationDB +from neo.logging import log_manager +import json + + +logger = log_manager.getLogger() + + +class CommandShow(CommandBase): + def __init__(self): + super().__init__() + + self.register_sub_command(CommandShowBlock()) + self.register_sub_command(CommandShowHeader()) + self.register_sub_command(CommandShowTx()) + self.register_sub_command(CommandShowMem()) + self.register_sub_command(CommandShowNodes(), ['node']) + self.register_sub_command(CommandShowState()) + self.register_sub_command(CommandShowNotifications()) + self.register_sub_command(CommandShowAccount()) + self.register_sub_command(CommandShowAsset()) + self.register_sub_command(CommandShowContract()) + + def command_desc(self): + return CommandDesc('show', 'show data from the blockchain') + + def execute(self, arguments): + item = get_arg(arguments) + + if not item: + print("run `show help` to see supported queries") + return + + try: + return self.execute_sub_command(item, arguments[1:]) + except KeyError: + print(f"show: {item} is an invalid parameter") + return + + +class CommandShowBlock(CommandBase): + def __init__(self): + super().__init__() + + def execute(self, arguments): + item = get_arg(arguments) + txarg = get_arg(arguments, 1) + + block = Blockchain.Default().GetBlock(item) + + if block is not None: + block.LoadTransactions() + bjson = json.dumps(block.ToJson(), indent=4) + if txarg and 'tx' in txarg: + txs = [] + for tx in block.FullTransactions: + tjson = json.dumps(tx.ToJson(), indent=4) + tokens = [("class:number", tjson)] + print_formatted_text(FormattedText(tokens), style=PromptData.Prompt.token_style) + txs.append(tx.ToJson()) + return txs + + tokens = [("class:number", bjson)] + print_formatted_text(FormattedText(tokens), style=PromptData.Prompt.token_style) + return block.ToJson() + + else: + print("Could not locate block %s" % item) + return + + def command_desc(self): + p1 = ParameterDesc('index/hash', 'the index or scripthash of the block') + p2 = ParameterDesc('tx', 'arg to only show block transactions', optional=True) + return CommandDesc('block', 'show a specified block', [p1, p2]) + + +class CommandShowHeader(CommandBase): + def __init__(self): + super().__init__() + + def execute(self, arguments): + item = get_arg(arguments) + + header = Blockchain.Default().GetHeaderBy(item) + if header is not None: + hjson = (json.dumps(header.ToJson(), indent=4)) + tokens = [("class:number", hjson)] + print_formatted_text(FormattedText(tokens), style=PromptData.Prompt.token_style) + return header.ToJson() + else: + print("Could not locate header %s\n" % item) + return + + def command_desc(self): + p1 = ParameterDesc('index/hash', 'the index or scripthash of the block header') + return CommandDesc('header', 'show the header of a specified block', [p1]) + + +class CommandShowTx(CommandBase): + def __init__(self): + super().__init__() + + def execute(self, arguments): + try: + txid = UInt256.ParseString(get_arg(arguments)) + tx, height = Blockchain.Default().GetTransaction(txid) + if height > -1: + jsn = tx.ToJson() + jsn['height'] = height + jsn['unspents'] = [uns.ToJson(tx.outputs.index(uns)) for uns in + Blockchain.Default().GetAllUnspent(txid)] + tokens = [("class:command", json.dumps(jsn, indent=4))] + print_formatted_text(FormattedText(tokens), style=PromptData.Prompt.token_style) + return jsn + else: + print(f"Could not find transaction for hash {txid}") + return + except Exception as e: + print("Could not find transaction from args: %s (%s)" % (e, arguments)) + return + + def command_desc(self): + p1 = ParameterDesc('hash', 'the scripthash of the transaction') + return CommandDesc('tx', 'show a specified transaction', [p1]) + + +class CommandShowMem(CommandBase): + def __init__(self): + super().__init__() + + def execute(self, arguments=None): + process = psutil.Process(os.getpid()) + total = process.memory_info().rss + totalmb = total / (1024 * 1024) + out = "Total: %s MB\n" % totalmb + out += "Total buffers: %s\n" % StreamManager.TotalBuffers() + print_formatted_text(FormattedText([("class:number", out)]), style=PromptData.Prompt.token_style) + return out + + def command_desc(self): + return CommandDesc('mem', 'show memory in use and number of buffers') + + +class CommandShowNodes(CommandBase): + def __init__(self): + super().__init__() + + def execute(self, arguments=None): + if len(NodeLeader.Instance().Peers) > 0: + out = "Total Connected: %s\n" % len(NodeLeader.Instance().Peers) + for peer in NodeLeader.Instance().Peers: + out += "Peer %s - IO: %s\n" % (peer.Name(), peer.IOStats()) + print_formatted_text(FormattedText([("class:number", out)]), style=PromptData.Prompt.token_style) + return out + else: + print("Not connected yet\n") + return + + def command_desc(self): + return CommandDesc('nodes', 'show connected peers') + + +class CommandShowState(CommandBase): + def __init__(self): + super().__init__() + + def execute(self, arguments=None): + height = Blockchain.Default().Height + headers = Blockchain.Default().HeaderHeight + + diff = height - PromptData.Prompt.start_height + now = datetime.datetime.utcnow() + difftime = now - PromptData.Prompt.start_dt + + mins = difftime / datetime.timedelta(minutes=1) + secs = mins * 60 + + bpm = 0 + tps = 0 + if diff > 0 and mins > 0: + bpm = diff / mins + tps = Blockchain.Default().TXProcessed / secs + + out = "Progress: %s / %s\n" % (height, headers) + out += "Block-cache length %s\n" % Blockchain.Default().BlockCacheCount + out += "Blocks since program start %s\n" % diff + out += "Time elapsed %s mins\n" % mins + out += "Blocks per min %s \n" % bpm + out += "TPS: %s \n" % tps + tokens = [("class:number", out)] + print_formatted_text(FormattedText(tokens), style=PromptData.Prompt.token_style) + return out + + def command_desc(self): + return CommandDesc('state', 'show the status of the node') + + +class CommandShowNotifications(CommandBase): + def __init__(self): + super().__init__() + + def execute(self, arguments): + if NotificationDB.instance() is None: + print("No notification DB Configured") + return + + item = get_arg(arguments, 0) + events = [] + if len(item) == 34 and item[0] == 'A': + addr = item + events = NotificationDB.instance().get_by_addr(addr) + else: + try: + block_height = int(item) + if block_height < Blockchain.Default().Height: + events = NotificationDB.instance().get_by_block(block_height) + else: + print("Block %s not found" % block_height) + return + except Exception as e: + print("Could not parse block height %s" % e) + return + + if len(events): + [print(json.dumps(e.ToJson(), indent=4)) for e in events] + return events + else: + print("No events found for %s" % item) + return + + def command_desc(self): + p1 = ParameterDesc('block_index/address', 'the block or address to show notifications for') + return CommandDesc('notifications', 'show specified contract execution notifications', [p1]) + + +class CommandShowAccount(CommandBase): + def __init__(self): + super().__init__() + + def execute(self, arguments): + item = get_arg(arguments) + + account = Blockchain.Default().GetAccountState(item, print_all_accounts=True) + + if account is not None: + bjson = json.dumps(account.ToJson(), indent=4) + tokens = [("class:number", bjson)] + print_formatted_text(FormattedText(tokens), style=PromptData.Prompt.token_style) + return account.ToJson() + else: + print("Account %s not found" % item) + return + + def command_desc(self): + p1 = ParameterDesc('address', 'the address to show') + return CommandDesc('account', 'show the assets (NEO/GAS) held by a specified address', [p1]) + + +class CommandShowAsset(CommandBase): + def __init__(self): + super().__init__() + + def execute(self, arguments): + item = get_arg(arguments) + + if item.lower() == "all": + assets = Blockchain.Default().ShowAllAssets() + print("Assets: %s" % assets) + return assets + + if item.lower() == 'neo': + assetId = Blockchain.Default().SystemShare().Hash + elif item.lower() == 'gas': + assetId = Blockchain.Default().SystemCoin().Hash + else: + try: + assetId = UInt256.ParseString(item) + except Exception as e: + print("Could not find assetId from args: %s (%s)" % (e, arguments)) + return + + asset = Blockchain.Default().GetAssetState(assetId.ToBytes()) + + if asset is not None: + bjson = json.dumps(asset.ToJson(), indent=4) + tokens = [("class:number", bjson)] + print_formatted_text(FormattedText(tokens), style=PromptData.Prompt.token_style) + return asset.ToJson() + else: + print("Asset %s not found" % item) + return + + def command_desc(self): + p1 = ParameterDesc('name/assetId/all', 'the name or assetId of the asset, or "all" shows all assets\n\n') + f"{' ':>17} Example:\n" + f"{' ':>20} 'neo' or 'c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b'\n" + f"{' ':>20} 'gas' or '602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7'\n" + return CommandDesc('asset', 'show a specified asset', [p1]) + + +class CommandShowContract(CommandBase): + def __init__(self): + super().__init__() + + def execute(self, arguments): + item = get_arg(arguments) + + if item.lower() == "all": + contracts = Blockchain.Default().ShowAllContracts() + print("Contracts: %s" % contracts) + return contracts + + contract = Blockchain.Default().GetContract(item) + + if contract is not None: + contract.DetermineIsNEP5() + bjson = json.dumps(contract.ToJson(), indent=4) + tokens = [("class:number", bjson)] + print_formatted_text(FormattedText(tokens), style=PromptData.Prompt.token_style) + return contract.ToJson() + else: + print("Contract %s not found" % item) + return + + def command_desc(self): + p1 = ParameterDesc('hash/all', 'the scripthash of the contract, or "all" shows all contracts') + return CommandDesc('contract', 'show a specified smart contract', [p1]) diff --git a/neo/Prompt/Commands/tests/test_search_commands.py b/neo/Prompt/Commands/tests/test_search_commands.py new file mode 100644 index 000000000..3bcc15f1d --- /dev/null +++ b/neo/Prompt/Commands/tests/test_search_commands.py @@ -0,0 +1,79 @@ +import os +from neo.Settings import settings +from neo.Utils.BlockchainFixtureTestCase import BlockchainFixtureTestCase +from neo.Prompt.Commands.Search import CommandSearch +from neo.Prompt.PromptData import PromptData +from neo.bin.prompt import PromptInterface + + +class CommandShowTestCase(BlockchainFixtureTestCase): + + @classmethod + def leveldb_testpath(self): + return os.path.join(settings.DATA_DIR_PATH, 'fixtures/test_chain') + + @classmethod + def tearDown(cls): + PromptData.Prompt = None + + def test_search(self): + # with no subcommand + res = CommandSearch().execute(None) + self.assertFalse(res) + + # with invalid command + args = ['badcommand'] + res = CommandSearch().execute(args) + self.assertFalse(res) + + def test_search_asset(self): + # setup + PromptInterface() + + # successful search by name + args = ['asset', "AntShare"] + res = CommandSearch().execute(args) + self.assertTrue(res) + + # successful search by issuer and admin (same address) + args = ['asset', "Abf2qMs1pzQb8kYk9RuxtUb9jtRKJVuBJt"] + res = CommandSearch().execute(args) + self.assertTrue(res) + + # unsuccessful search + args = ['asset', 'blah'] + res = CommandSearch().execute(args) + self.assertFalse(res) + + def test_search_contract(self): + # setup + PromptInterface() + + # successful search by name + args = ['contract', "test NEX Template V4"] + res = CommandSearch().execute(args) + self.assertTrue(res) + self.assertEqual(len(res), 1) + + # successful search by author + args = ['contract', "dauTT"] + res = CommandSearch().execute(args) + self.assertTrue(res) + self.assertEqual(len(res), 3) + + # successful search by description + args = ['contract', "neo-ico-template"] + res = CommandSearch().execute(args) + self.assertTrue(res) + self.assertEqual(len(res), 1) + + # successful search by email (as entered) + args = ['contract', ""] + res = CommandSearch().execute(args) + self.assertTrue(res) + self.assertEqual(len(res), 6) + + # bad search input + args = ['contract', "blah"] + res = CommandSearch().execute(args) + self.assertFalse(res) diff --git a/neo/Prompt/Commands/tests/test_show_commands.py b/neo/Prompt/Commands/tests/test_show_commands.py new file mode 100644 index 000000000..2426c2d90 --- /dev/null +++ b/neo/Prompt/Commands/tests/test_show_commands.py @@ -0,0 +1,304 @@ +import os +from neo.Settings import settings +from neo.Utils.BlockchainFixtureTestCase import BlockchainFixtureTestCase +from neo.Prompt.Commands.Show import CommandShow +from neo.Prompt.Commands.Wallet import CommandWallet +from neo.Prompt.PromptData import PromptData +from neo.bin.prompt import PromptInterface +from copy import deepcopy +from neo.Network.NodeLeader import NodeLeader, NeoNode +from neo.Core.Blockchain import Blockchain +from neo.Implementations.Wallets.peewee.UserWallet import UserWallet +from mock import patch + + +class CommandShowTestCase(BlockchainFixtureTestCase): + + @classmethod + def leveldb_testpath(self): + return os.path.join(settings.DATA_DIR_PATH, 'fixtures/test_chain') + + @classmethod + def tearDown(cls): + PromptData.Prompt = None + PromptData.Wallet = None + + def test_show(self): + # with no subcommand + res = CommandShow().execute(None) + self.assertFalse(res) + + # with invalid command + args = ['badcommand'] + res = CommandShow().execute(args) + self.assertFalse(res) + + def test_show_block(self): + # setup + PromptInterface() + + # show good block by index + args = ['block', '9'] + res = CommandShow().execute(args) + self.assertTrue(res) + self.assertEqual(res['index'], 9) + self.assertIn('tx', res) + + # show good block by hash + args = ['block', "0x7c5b4c8a70336bf68e8679be7c9a2a15f85c0f6d0e14389019dcc3edfab2bb4b"] + res = CommandShow().execute(args) + self.assertTrue(res) + self.assertEqual(res['index'], 9) + self.assertIn('tx', res) + + # show the block's transactions only + args = ['block', '9', "tx"] + res = CommandShow().execute(args) + self.assertTrue(res) + self.assertEqual(len(res), 2) + self.assertEqual(res[0]['type'], "MinerTransaction") + self.assertEqual(res[1]['type'], "ContractTransaction") + + # request bad block + args = ['block', 'blah'] + res = CommandShow().execute(args) + self.assertFalse(res) + + def test_show_header(self): + # setup + PromptInterface() + + # show good header by index + args = ['header', '9'] + res = CommandShow().execute(args) + self.assertTrue(res) + self.assertEqual(res['index'], 9) + self.assertNotIn('tx', res) + + # show good header by hash + args = ['header', "0x7c5b4c8a70336bf68e8679be7c9a2a15f85c0f6d0e14389019dcc3edfab2bb4b"] + res = CommandShow().execute(args) + self.assertTrue(res) + self.assertEqual(res['index'], 9) + self.assertNotIn('tx', res) + + # request bad header + args = ['header', 'blah'] + res = CommandShow().execute(args) + self.assertFalse(res) + + def test_show_tx(self): + # setup + PromptInterface() + + # show good tx + txid = '0x83df8bd085fcb60b2789f7d0a9f876e5f3908567f7877fcba835e899b9dea0b5' + args = ['tx', txid] + res = CommandShow().execute(args) + self.assertTrue(res) + self.assertEqual(res['txid'], txid) + self.assertIn('height', res) + self.assertIn('unspents', res) + + # query a bad tx + args = ['tx', '0x83df8bd085fcb60b2789f7d0a9f876e5f3908567f7877fcba835e899b9dea0b6'] + res = CommandShow().execute(args) + self.assertFalse(res) + + # query with bad args + args = ['tx', 'blah'] + res = CommandShow().execute(args) + self.assertFalse(res) + + def test_show_mem(self): + # setup + PromptInterface() + + args = ['mem'] + res = CommandShow().execute(args) + self.assertTrue(res) + + def test_show_nodes(self): + # setup + PromptInterface() + + # query nodes with no NodeLeader.Instance() + with patch('neo.Network.NodeLeader.NodeLeader.Instance'): + args = ['nodes'] + res = CommandShow().execute(args) + self.assertFalse(res) + + # query nodes with connected peers + # first make sure we have a predictable state + leader = NodeLeader.Instance() + old_leader = deepcopy(leader) + leader.ADDRS = ["127.0.0.1:20333", "127.0.0.2:20334"] + leader.DEAD_ADDRS = ["127.0.0.1:20335"] + test_node = NeoNode() + test_node.host = "127.0.0.1" + test_node.port = 20333 + leader.Peers = [test_node] + + # now show nodes + with patch('neo.Network.NeoNode.NeoNode.Name', return_value="test name"): + args = ['nodes'] + res = CommandShow().execute(args) + self.assertTrue(res) + self.assertIn('Total Connected: 1', res) + self.assertIn('Peer test name - IO: 0.0 MB in / 0.0 MB out', res) + + # now use "node" + args = ['node'] + res = CommandShow().execute(args) + self.assertTrue(res) + self.assertIn('Total Connected: 1', res) + self.assertIn('Peer test name - IO: 0.0 MB in / 0.0 MB out', res) + + # restore whatever state the instance was in + NodeLeader._LEAD = old_leader + + def test_show_state(self): + # setup + PromptInterface() + + args = ['state'] + res = CommandShow().execute(args) + self.assertTrue(res) + + def test_show_notifications(self): + # setup + wallet_1_addr = 'AJQ6FoaSXDFzA6wLnyZ1nFN7SGSN2oNTc3' + + # test with no NotificationDB + with patch('neo.Implementations.Notifications.LevelDB.NotificationDB.NotificationDB.instance', return_value=None): + args = ['notifications', wallet_1_addr] + res = CommandShow().execute(args) + self.assertFalse(res) + + # good test with address + args = ['notifications', wallet_1_addr] + res = CommandShow().execute(args) + self.assertTrue(res) + self.assertEqual(len(res), 1) + jsn = res[0].ToJson() + self.assertEqual(jsn['notify_type'], 'transfer') + self.assertEqual(jsn['addr_from'], wallet_1_addr) + + # test an address with no notifications + args = ['notifications', 'AZiE7xfyJALW7KmADWtCJXGGcnduYhGiCX'] + res = CommandShow().execute(args) + self.assertFalse(res) + + # good test with block index + args = ['notifications', "12337"] + res = CommandShow().execute(args) + self.assertTrue(res) + self.assertEqual(len(res), 1) + jsn = res[0].ToJson() + self.assertEqual(jsn['notify_type'], 'transfer') + self.assertEqual(jsn['block'], 12337) + + # test bad block + index = Blockchain.Default().Height + 1 + args = ['notifications', str(index)] + res = CommandShow().execute(args) + self.assertFalse(res) + + # test invalid block input + args = ['notifications', "blah"] + res = CommandShow().execute(args) + self.assertFalse(res) + + def test_show_account(self): + # setup + PromptInterface() + wallet_1_addr = 'AJQ6FoaSXDFzA6wLnyZ1nFN7SGSN2oNTc3' + + # test good account + args = ['account', wallet_1_addr] + res = CommandShow().execute(args) + self.assertTrue(res) + self.assertEqual(res['address'], wallet_1_addr) + self.assertIn('balances', res) + + # test empty account + with patch('neo.Prompt.PromptData.PromptData.Prompt'): + with patch('neo.Prompt.Commands.Wallet.prompt', side_effect=["testpassword", "testpassword"]): + args = ['create', 'testwallet.wallet'] + res = CommandWallet().execute(args) + self.assertTrue(res) + self.assertIsInstance(res, UserWallet) + + addr = res.Addresses[0] + args = ['account', addr] + res = CommandShow().execute(args) + self.assertFalse(res) + + # remove test wallet + os.remove("testwallet.wallet") + + def test_show_asset(self): + # setup + PromptInterface() + + # show all assets + args = ['asset', 'all'] + res = CommandShow().execute(args) + self.assertTrue(res) + self.assertIn("602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7", str(res)) + self.assertIn("c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b", str(res)) + + # query with "neo" + args = ['asset', 'neo'] + res = CommandShow().execute(args) + self.assertTrue(res) + self.assertEqual(res['assetId'], "0xc56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b") + self.assertEqual(res['name'], "NEO") + + # query with "gas" + args = ['asset', 'gas'] + res = CommandShow().execute(args) + self.assertTrue(res) + self.assertEqual(res['assetId'], "0x602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7") + self.assertEqual(res['name'], "NEOGas") + + # query with scripthash + args = ['asset', 'c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b'] + res = CommandShow().execute(args) + self.assertTrue(res) + self.assertEqual(res['assetId'], "0xc56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b") + self.assertEqual(res['name'], "NEO") + + # query with bad asset + args = ['asset', 'c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9e'] + res = CommandShow().execute(args) + self.assertFalse(res) + + # query with bad input + args = ['asset', 'blah'] + res = CommandShow().execute(args) + self.assertFalse(res) + + def test_show_contract(self): + # setup + PromptInterface() + + # show all contracts + args = ['contract', 'all'] + res = CommandShow().execute(args) + self.assertTrue(res) + res = list(res) + self.assertEqual(len(res), 6) + + # query with contract scripthash + args = ['contract', '31730cc9a1844891a3bafd1aa929a4142860d8d3'] + res = CommandShow().execute(args) + self.assertTrue(res) + self.assertEqual(res['name'], "test NEX Template V4") + self.assertEqual(res['token']['name'], "NEX Template V4") + self.assertEqual(res['token']['symbol'], "NXT4") + + # query bad input + args = ['contract', 'blah'] + res = CommandShow().execute(args) + self.assertFalse(res) diff --git a/neo/bin/prompt.py b/neo/bin/prompt.py index a955e0c83..e8d0290a9 100755 --- a/neo/bin/prompt.py +++ b/neo/bin/prompt.py @@ -16,6 +16,8 @@ from neo.Implementations.Notifications.LevelDB.NotificationDB import NotificationDB from neo.Network.NodeLeader import NodeLeader from neo.Prompt.Commands.Wallet import CommandWallet +from neo.Prompt.Commands.Show import CommandShow +from neo.Prompt.Commands.Search import CommandSearch from neo.Prompt.PromptData import PromptData from neo.Prompt.InputParser import InputParser from neo.Settings import settings, PrivnetConnectionError @@ -70,7 +72,7 @@ class PromptInterface: _known_things = [] _commands = [ - CommandWallet(), + CommandWallet(), CommandShow(), CommandSearch() ] _command_descs = [desc for c in _commands for desc in c.command_descs_with_sub_commands()]