diff --git a/neo/Core/Blockchain.py b/neo/Core/Blockchain.py index 88ee05b20..242a6ad9b 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): + pass + def GetHeaderHash(self, height): pass diff --git a/neo/Core/FunctionCode.py b/neo/Core/FunctionCode.py index 4e9c20c85..f54b3c13f 100644 --- a/neo/Core/FunctionCode.py +++ b/neo/Core/FunctionCode.py @@ -1,7 +1,7 @@ from neocore.IO.Mixins import SerializableMixin from neocore.Cryptography.Crypto import Crypto from neocore.BigInteger import BigInteger -from neo.SmartContract.ContractParameterType import ContractParameterType +from neo.SmartContract.ContractParameterType import ContractParameterType, ToName class FunctionCode(SerializableMixin): @@ -104,9 +104,11 @@ def ToJson(self): Returns: dict: """ + parameters = self.ParameterList.hex() + paramlist = [ToName(ContractParameterType.FromString(parameters[i:i + 2]).value) for i in range(0, len(parameters), 2)] return { 'hash': self.ScriptHash().To0xString(), 'script': self.Script.hex(), - 'parameters': self.ParameterList.hex(), - 'returntype': self.ReturnType if type(self.ReturnType) is int else self.ReturnType.hex() + 'parameters': paramlist, + 'returntype': ToName(self.ReturnType) if type(self.ReturnType) is int else ToName(int(self.ReturnType)) } diff --git a/neo/Core/State/ContractState.py b/neo/Core/State/ContractState.py index a26863d7c..7eb121ecf 100644 --- a/neo/Core/State/ContractState.py +++ b/neo/Core/State/ContractState.py @@ -183,8 +183,6 @@ def ToJson(self): Returns: dict: """ - codejson = self.Code.ToJson() - name = 'Contract' try: @@ -192,10 +190,11 @@ def ToJson(self): except Exception as e: pass - jsn = { + jsn = {'version': self.StateVersion} + + jsn_code = self.Code.ToJson() - 'version': self.StateVersion, - 'code': codejson, + jsn_contract = { 'name': name, 'code_version': self.CodeVersion.decode('utf-8'), 'author': self.Author.decode('utf-8'), @@ -208,6 +207,9 @@ def ToJson(self): } } + jsn.update(jsn_code) + jsn.update(jsn_contract) + if self._nep_token: jsn['token'] = self._nep_token.ToJson() diff --git a/neo/Core/TX/test_transactions.py b/neo/Core/TX/test_transactions.py index e6fa11c92..bba35c819 100644 --- a/neo/Core/TX/test_transactions.py +++ b/neo/Core/TX/test_transactions.py @@ -90,8 +90,8 @@ def test_publish_tx_deserialize(self): self.assertEqual(contract['description'], 'Lock your assets until a timestamp.') self.assertEqual(contract['code']['hash'], '0xffbd1a7ad1e2348b6b3822426f364bfb4bcce3b9') - self.assertEqual(contract['code']['returntype'], 1) - self.assertEqual(contract['code']['parameters'], '020500') + self.assertEqual(contract['code']['returntype'], "Boolean") + self.assertEqual(contract['code']['parameters'], ['Integer', 'ByteArray', 'Signature']) self.assertEqual(Fixed8.FromDecimal(settings.ALL_FEES['PublishTransaction']), tx.SystemFee()) ir = b'd100644011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111081234567890abcdef0415cd5b0769cc4ee2f1c9f4e0782756dabf246d0a4fe60a035400000000' diff --git a/neo/Implementations/Blockchains/LevelDB/LevelDBBlockchain.py b/neo/Implementations/Blockchains/LevelDB/LevelDBBlockchain.py index 02ef4f25b..33732f80b 100644 --- a/neo/Implementations/Blockchains/LevelDB/LevelDBBlockchain.py +++ b/neo/Implementations/Blockchains/LevelDB/LevelDBBlockchain.py @@ -371,6 +371,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/Show.py b/neo/Prompt/Commands/Show.py index b3214ad1f..50a088af5 100644 --- a/neo/Prompt/Commands/Show.py +++ b/neo/Prompt/Commands/Show.py @@ -6,6 +6,7 @@ from neo.Prompt.Utils import get_arg from neo.Core.Blockchain import Blockchain from neocore.UInt256 import UInt256 +from neocore.UInt160 import UInt160 from neo.IO.MemoryStream import StreamManager from neo.Network.NodeLeader import NodeLeader from neo.Implementations.Notifications.LevelDB.NotificationDB import NotificationDB @@ -25,6 +26,11 @@ def __init__(self): 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 useful data') @@ -70,11 +76,11 @@ def execute(self, arguments): print("Could not locate block %s" % item) return else: - print("please specify a block") + print("please specify a supported block attribute: index or scripthash") return def command_desc(self): - p1 = ParameterDesc('index/hash', 'the index or scripthash of the block') + p1 = ParameterDesc('attribute', 'the block index or scripthash') p2 = ParameterDesc('tx', 'arg to only show block transactions', optional=True) return CommandDesc('block', 'show a specified block', [p1, p2]) @@ -94,11 +100,11 @@ def execute(self, arguments): print("Could not locate header %s\n" % item) return else: - print("Please specify a header") + print("Please specify a supported header attribute: index or scripthash") return def command_desc(self): - p1 = ParameterDesc('index/hash', 'the index or scripthash of the block header') + p1 = ParameterDesc('attribute', 'the header index or scripthash') return CommandDesc('header', 'show the header of a specified block', [p1]) @@ -167,3 +173,199 @@ def execute(self, arguments=None): 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 + print(out) + 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) + if item is not None: + if item[0:2] == "0x": + item = item[2:] + + events = [] + + if len(item) == 34 and item[0] == 'A': + events = NotificationDB.instance().get_by_addr(item) + + elif len(item) == 40: + events = NotificationDB.instance().get_by_contract(item) + + 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: + print("Could not find notifications from args: %s" % arguments) + 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 + else: + print("Please specify a supported attribute: a block index, an address, or contract scripthash") + return + + def command_desc(self): + p1 = ParameterDesc('attribute', 'the block index, an address, or contract scripthash 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) + if item is not None: + account = Blockchain.Default().GetAccountState(item, print_all_accounts=True) + + if account is not None: + print(json.dumps(account.ToJson(), indent=4)) + return account.ToJson() + else: + print("Account %s not found" % item) + return + else: + print("Please specify an account address") + 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 is not None: + if item.lower() == "all": + assets = Blockchain.Default().ShowAllAssets() + assetlist = [] + for asset in assets: + state = Blockchain.Default().GetAssetState(asset.decode('utf-8')).ToJson() + asset_dict = {state['name']: state['assetId']} + assetlist.append(asset_dict) + print(json.dumps(assetlist, indent=4)) + return assetlist + + 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: + print("Could not find asset from args: %s" % arguments) + return + + asset = Blockchain.Default().GetAssetState(assetId.ToBytes()) + + if asset is not None: + print(json.dumps(asset.ToJson(), indent=4)) + return asset.ToJson() + else: + print("Asset %s not found" % item) + return + else: + print('Please specify a supported attribute: asset name, assetId, or "all" shows all assets') + return + + def command_desc(self): + p1 = ParameterDesc('attribute', 'the asset name, assetId, 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 is not None: + if item.lower() == "all": + contracts = Blockchain.Default().ShowAllContracts() + contractlist = [] + for contract in contracts: + state = Blockchain.Default().GetContract(contract.decode('utf-8')).ToJson() + contract_dict = {state['name']: state['hash']} + contractlist.append(contract_dict) + print(json.dumps(contractlist, indent=4)) + return contractlist + + try: + hash = UInt160.ParseString(item).ToBytes() + except Exception: + print("Could not find contract from args: %s" % arguments) + return + + contract = Blockchain.Default().GetContract(hash) + + if contract is not None: + contract.DetermineIsNEP5() + print(json.dumps(contract.ToJson(), indent=4)) + return contract.ToJson() + else: + print("Contract %s not found" % item) + return + else: + print('Please specify a supported attribute: contract scripthash or "all"') + return + + def command_desc(self): + p1 = ParameterDesc('attribute', 'the contract scripthash, or "all" shows all contracts') + return CommandDesc('contract', 'show a specified smart contract', [p1]) diff --git a/neo/Prompt/Commands/tests/test_show_commands.py b/neo/Prompt/Commands/tests/test_show_commands.py index a297abc0c..942168bdd 100644 --- a/neo/Prompt/Commands/tests/test_show_commands.py +++ b/neo/Prompt/Commands/tests/test_show_commands.py @@ -156,3 +156,199 @@ def test_show_nodes(self): # 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) + + # test with no input + args = ['notifications'] + 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 contract + contract_hash = "31730cc9a1844891a3bafd1aa929a4142860d8d3" + args = ['notifications', contract_hash] + res = CommandShow().execute(args) + self.assertTrue(res) + self.assertEqual(len(res), 1) + jsn = res[0].ToJson() + self.assertEqual(jsn['notify_type'], 'transfer') + self.assertIn(contract_hash, jsn['contract']) + + # good test with contract 0x hash + contract_hash = "0x31730cc9a1844891a3bafd1aa929a4142860d8d3" + args = ['notifications', contract_hash] + res = CommandShow().execute(args) + self.assertTrue(res) + self.assertEqual(len(res), 1) + jsn = res[0].ToJson() + self.assertEqual(jsn['notify_type'], 'transfer') + self.assertEqual(contract_hash, jsn['contract']) + + # test contract not on the blockchain + contract_hash = "3a4acd3647086e7c44398aac0349802e6a171129" # NEX token hash + args = ['notifications', contract_hash] + 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 block with no notifications + args = ['notifications', "1"] + res = CommandShow().execute(args) + self.assertFalse(res) + + # test bad block + index = Blockchain.Default().Height + 1 + args = ['notifications', str(index)] + res = CommandShow().execute(args) + self.assertFalse(res) + + # test invalid input + args = ['notifications', "blah"] + res = CommandShow().execute(args) + self.assertFalse(res) + + def test_show_account(self): + # setup + wallet_1_addr = 'AJQ6FoaSXDFzA6wLnyZ1nFN7SGSN2oNTc3' + + # test no account address entered + args = ['account'] + res = CommandShow().execute(args) + self.assertFalse(res) + + # 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): + # test no asset entered + args = ['asset'] + res = CommandShow().execute(args) + self.assertFalse(res) + + # show all assets + args = ['asset', 'all'] + res = CommandShow().execute(args) + self.assertTrue(res) + self.assertEqual(len(res), 2) + self.assertEqual(res[1]['NEO'], "0xc56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b") + self.assertEqual(res[0]['NEOGas'], "0x602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7") + + # 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): + # test no contract entered + args = ['contract'] + res = CommandShow().execute(args) + self.assertFalse(res) + + # show all contracts + args = ['contract', 'all'] + res = CommandShow().execute(args) + self.assertTrue(res) + self.assertEqual(len(res), 6) + self.assertEqual(res[0]["test NEX Template V4"], '0x31730cc9a1844891a3bafd1aa929a4142860d8d3') + + # 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 with a contract scripthash not on the blockchain + args = ['contract', '3a4acd3647086e7c44398aac0349802e6a171129'] # NEX token hash + res = CommandShow().execute(args) + self.assertFalse(res) + + # query bad input + args = ['contract', 'blah'] + res = CommandShow().execute(args) + self.assertFalse(res) diff --git a/neo/SmartContract/ContractParameterType.py b/neo/SmartContract/ContractParameterType.py index 470979b16..25b876653 100755 --- a/neo/SmartContract/ContractParameterType.py +++ b/neo/SmartContract/ContractParameterType.py @@ -96,7 +96,7 @@ def ToName(param_type): for item in items: name = item[0] - val = int(item[1]) + val = int(item[1].value) if val == param_type: return name diff --git a/neo/SmartContract/SmartContractEvent.py b/neo/SmartContract/SmartContractEvent.py index 7e027ca37..e484dc47e 100644 --- a/neo/SmartContract/SmartContractEvent.py +++ b/neo/SmartContract/SmartContractEvent.py @@ -161,7 +161,7 @@ def ToJson(self): if self.event_type in [SmartContractEvent.CONTRACT_CREATED, SmartContractEvent.CONTRACT_MIGRATED]: jsn['contract'] = self.contract.ToJson() - del jsn['contract']['code']['script'] + del jsn['contract']['script'] if self.token: jsn['token'] = self.token.ToJson() diff --git a/neo/api/JSONRPC/test_json_rpc_api.py b/neo/api/JSONRPC/test_json_rpc_api.py index b016035af..6af137f4a 100644 --- a/neo/api/JSONRPC/test_json_rpc_api.py +++ b/neo/api/JSONRPC/test_json_rpc_api.py @@ -255,9 +255,9 @@ def test_get_contract_state(self): res = json.loads(self.app.home(mock_req)) self.assertEqual(res['result']['code_version'], '') self.assertEqual(res['result']['properties']['storage'], True) - self.assertEqual(res['result']['code']['hash'], '0xb9fbcff6e50fd381160b822207231233dd3c56c2') - self.assertEqual(res['result']['code']['returntype'], 5) - self.assertEqual(res['result']['code']['parameters'], '0710') + self.assertEqual(res['result']['hash'], '0xb9fbcff6e50fd381160b822207231233dd3c56c2') + self.assertEqual(res['result']['returntype'], "ByteArray") + self.assertEqual(res['result']['parameters'], ["String", "Array"]) def test_get_contract_state_0x(self): contract_hash = "0xb9fbcff6e50fd381160b822207231233dd3c56c2"