diff --git a/src/RpcClient/Models/RpcNativeContract.cs b/src/RpcClient/Models/RpcNativeContract.cs new file mode 100644 index 000000000..7ca671271 --- /dev/null +++ b/src/RpcClient/Models/RpcNativeContract.cs @@ -0,0 +1,39 @@ +using Neo.IO.Json; +using Neo.SmartContract; +using Neo.SmartContract.Manifest; + +namespace Neo.Network.RPC.Models +{ + public class RpcNativeContract + { + public int Id { get; set; } + public UInt160 Hash { get; set; } + public NefFile Nef { get; set; } + public ContractManifest Manifest { get; set; } + public uint ActiveBlockIndex { get; set; } + + public static RpcNativeContract FromJson(JObject json) + { + return new RpcNativeContract + { + Id = (int)json["id"].AsNumber(), + Hash = UInt160.Parse(json["hash"].AsString()), + Nef = RpcNefFile.FromJson(json["nef"]), + Manifest = ContractManifest.FromJson(json["manifest"]), + ActiveBlockIndex = (uint)(json["activeblockindex"]?.AsNumber() ?? 0) + }; + } + + public JObject ToJson() + { + return new JObject + { + ["id"] = Id, + ["hash"] = Hash.ToString(), + ["nef"] = Nef.ToJson(), + ["manifest"] = Manifest.ToJson(), + ["activeblockindex"] = ActiveBlockIndex + }; + } + } +} diff --git a/src/RpcClient/RpcClient.cs b/src/RpcClient/RpcClient.cs index a02447e4a..2f18051eb 100644 --- a/src/RpcClient/RpcClient.cs +++ b/src/RpcClient/RpcClient.cs @@ -208,6 +208,15 @@ public static ContractState ContractStateFromJson(JObject json) }; } + /// + /// Get all native contracts. + /// + public async Task GetNativeContractsAsync() + { + var result = await RpcSendAsync(GetRpcName()).ConfigureAwait(false); + return ((JArray)result).Select(p => RpcNativeContract.FromJson(p)).ToArray(); + } + /// /// Obtains the list of unconfirmed transactions in memory. /// diff --git a/src/RpcServer/RpcServer.Blockchain.cs b/src/RpcServer/RpcServer.Blockchain.cs index de2175be2..fa81782ca 100644 --- a/src/RpcServer/RpcServer.Blockchain.cs +++ b/src/RpcServer/RpcServer.Blockchain.cs @@ -228,5 +228,11 @@ protected virtual JObject GetCommittee(JArray _params) using SnapshotView snapshot = Blockchain.Singleton.GetSnapshot(); return new JArray(NativeContract.NEO.GetCommittee(snapshot).Select(p => (JObject)p.ToString())); } + + [RpcMethod] + protected virtual JObject GetNativeContracts(JArray _params) + { + return new JArray(NativeContract.Contracts.Select(p => p.NativeContractToJson())); + } } } diff --git a/src/RpcServer/Utility.cs b/src/RpcServer/Utility.cs index 0ac336351..a4349cf47 100644 --- a/src/RpcServer/Utility.cs +++ b/src/RpcServer/Utility.cs @@ -1,10 +1,7 @@ using Neo.IO.Json; using Neo.Network.P2P.Payloads; using Neo.SmartContract.Native; -using System; -using System.Collections.Generic; using System.Linq; -using System.Text; namespace Neo.Plugins { @@ -24,5 +21,17 @@ public static JObject TransactionToJson(Transaction tx) json["netfee"] = new BigDecimal(tx.NetworkFee, NativeContract.GAS.Decimals).ToString(); return json; } + + public static JObject NativeContractToJson(this NativeContract contract) + { + return new JObject + { + ["id"] = contract.Id, + ["hash"] = contract.Hash.ToString(), + ["nef"] = contract.Nef.ToJson(), + ["manifest"] = contract.Manifest.ToJson(), + ["activeblockindex"] = contract.ActiveBlockIndex + }; + } } } diff --git a/tests/Neo.Network.RPC.Tests/RpcTestCases.json b/tests/Neo.Network.RPC.Tests/RpcTestCases.json index 57b3533a3..92b81a0b4 100644 --- a/tests/Neo.Network.RPC.Tests/RpcTestCases.json +++ b/tests/Neo.Network.RPC.Tests/RpcTestCases.json @@ -1005,6 +1005,1107 @@ } } }, + { + "Name": "getnativecontractsasync", + "Request": { + "jsonrpc": "2.0", + "id": 1, + "method": "getnativecontracts", + "params": [] + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": [ + { + "id": -1, + "hash": "0xbee421fdbb3e791265d2104cb34934f53fcc0e45", + "nef": { + "magic": 860243278, + "compiler": "ScriptBuilder", + "version": "3.0", + "tokens": [], + "script": "D0Ea93tn", + "checksum": 4234537066 + }, + "manifest": { + "name": "ContractManagement", + "groups": [], + "supportedstandards": [], + "abi": { + "methods": [ + { + "name": "getMinimumDeploymentFee", + "parameters": [], + "offset": 0, + "returntype": "Integer", + "safe": true + }, + { + "name": "setMinimumDeploymentFee", + "parameters": [ + { + "name": "value", + "type": "Integer" + } + ], + "offset": 0, + "returntype": "Void", + "safe": false + }, + { + "name": "getContract", + "parameters": [ + { + "name": "hash", + "type": "Hash160" + } + ], + "offset": 0, + "returntype": "Array", + "safe": true + }, + { + "name": "deploy", + "parameters": [ + { + "name": "nefFile", + "type": "ByteArray" + }, + { + "name": "manifest", + "type": "ByteArray" + } + ], + "offset": 0, + "returntype": "Array", + "safe": false + }, + { + "name": "update", + "parameters": [ + { + "name": "nefFile", + "type": "ByteArray" + }, + { + "name": "manifest", + "type": "ByteArray" + } + ], + "offset": 0, + "returntype": "Void", + "safe": false + }, + { + "name": "destroy", + "parameters": [], + "offset": 0, + "returntype": "Void", + "safe": false + } + ], + "events": [ + { + "name": "Deploy", + "parameters": [ + { + "name": "Hash", + "type": "Hash160" + } + ] + }, + { + "name": "Update", + "parameters": [ + { + "name": "Hash", + "type": "Hash160" + } + ] + }, + { + "name": "Destroy", + "parameters": [ + { + "name": "Hash", + "type": "Hash160" + } + ] + } + ] + }, + "permissions": [ + { + "contract": "*", + "methods": "*" + } + ], + "trusts": [], + "extra": null + }, + "activeblockindex": 0 + }, + { + "id": -2, + "hash": "0x4961bf0ab79370b23dc45cde29f568d0e0fa6e93", + "nef": { + "magic": 860243278, + "compiler": "ScriptBuilder", + "version": "3.0", + "tokens": [], + "script": "AP5BGvd7Zw==", + "checksum": 4259551467 + }, + "manifest": { + "name": "NeoToken", + "groups": [], + "supportedstandards": [ + "NEP-17" + ], + "abi": { + "methods": [ + { + "name": "totalSupply", + "parameters": [], + "offset": 0, + "returntype": "Integer", + "safe": true + }, + { + "name": "setGasPerBlock", + "parameters": [ + { + "name": "gasPerBlock", + "type": "Integer" + } + ], + "offset": 0, + "returntype": "Boolean", + "safe": false + }, + { + "name": "getGasPerBlock", + "parameters": [], + "offset": 0, + "returntype": "Integer", + "safe": true + }, + { + "name": "unclaimedGas", + "parameters": [ + { + "name": "account", + "type": "Hash160" + }, + { + "name": "end", + "type": "Integer" + } + ], + "offset": 0, + "returntype": "Integer", + "safe": true + }, + { + "name": "registerCandidate", + "parameters": [ + { + "name": "pubkey", + "type": "ByteArray" + } + ], + "offset": 0, + "returntype": "Boolean", + "safe": false + }, + { + "name": "unregisterCandidate", + "parameters": [ + { + "name": "pubkey", + "type": "ByteArray" + } + ], + "offset": 0, + "returntype": "Boolean", + "safe": false + }, + { + "name": "vote", + "parameters": [ + { + "name": "account", + "type": "Hash160" + }, + { + "name": "voteTo", + "type": "ByteArray" + } + ], + "offset": 0, + "returntype": "Boolean", + "safe": false + }, + { + "name": "getCandidates", + "parameters": [], + "offset": 0, + "returntype": "Array", + "safe": true + }, + { + "name": "getCommittee", + "parameters": [], + "offset": 0, + "returntype": "Array", + "safe": true + }, + { + "name": "getNextBlockValidators", + "parameters": [], + "offset": 0, + "returntype": "Array", + "safe": true + }, + { + "name": "balanceOf", + "parameters": [ + { + "name": "account", + "type": "Hash160" + } + ], + "offset": 0, + "returntype": "Integer", + "safe": true + }, + { + "name": "transfer", + "parameters": [ + { + "name": "from", + "type": "Hash160" + }, + { + "name": "to", + "type": "Hash160" + }, + { + "name": "amount", + "type": "Integer" + }, + { + "name": "data", + "type": "Any" + } + ], + "offset": 0, + "returntype": "Boolean", + "safe": false + }, + { + "name": "symbol", + "parameters": [], + "offset": 0, + "returntype": "String", + "safe": true + }, + { + "name": "decimals", + "parameters": [], + "offset": 0, + "returntype": "Integer", + "safe": true + } + ], + "events": [ + { + "name": "Transfer", + "parameters": [ + { + "name": "from", + "type": "Hash160" + }, + { + "name": "to", + "type": "Hash160" + }, + { + "name": "amount", + "type": "Integer" + } + ] + } + ] + }, + "permissions": [ + { + "contract": "*", + "methods": "*" + } + ], + "trusts": [], + "extra": null + }, + "activeblockindex": 0 + }, + { + "id": -3, + "hash": "0x9ac04cf223f646de5f7faccafe34e30e5d4382a2", + "nef": { + "magic": 860243278, + "compiler": "ScriptBuilder", + "version": "3.0", + "tokens": [], + "script": "AP1BGvd7Zw==", + "checksum": 571607263 + }, + "manifest": { + "name": "GasToken", + "groups": [], + "supportedstandards": [ + "NEP-17" + ], + "abi": { + "methods": [ + { + "name": "totalSupply", + "parameters": [], + "offset": 0, + "returntype": "Integer", + "safe": true + }, + { + "name": "balanceOf", + "parameters": [ + { + "name": "account", + "type": "Hash160" + } + ], + "offset": 0, + "returntype": "Integer", + "safe": true + }, + { + "name": "transfer", + "parameters": [ + { + "name": "from", + "type": "Hash160" + }, + { + "name": "to", + "type": "Hash160" + }, + { + "name": "amount", + "type": "Integer" + }, + { + "name": "data", + "type": "Any" + } + ], + "offset": 0, + "returntype": "Boolean", + "safe": false + }, + { + "name": "symbol", + "parameters": [], + "offset": 0, + "returntype": "String", + "safe": true + }, + { + "name": "decimals", + "parameters": [], + "offset": 0, + "returntype": "Integer", + "safe": true + } + ], + "events": [ + { + "name": "Transfer", + "parameters": [ + { + "name": "from", + "type": "Hash160" + }, + { + "name": "to", + "type": "Hash160" + }, + { + "name": "amount", + "type": "Integer" + } + ] + } + ] + }, + "permissions": [ + { + "contract": "*", + "methods": "*" + } + ], + "trusts": [], + "extra": null + }, + "activeblockindex": 0 + }, + { + "id": -4, + "hash": "0xc939a4af1c762e5edca36d4b61c06ba82c4c6ff5", + "nef": { + "magic": 860243278, + "compiler": "ScriptBuilder", + "version": "3.0", + "tokens": [], + "script": "APxBGvd7Zw==", + "checksum": 1792437732 + }, + "manifest": { + "name": "PolicyContract", + "groups": [], + "supportedstandards": [], + "abi": { + "methods": [ + { + "name": "getMaxTransactionsPerBlock", + "parameters": [], + "offset": 0, + "returntype": "Integer", + "safe": true + }, + { + "name": "getMaxBlockSize", + "parameters": [], + "offset": 0, + "returntype": "Integer", + "safe": true + }, + { + "name": "getMaxBlockSystemFee", + "parameters": [], + "offset": 0, + "returntype": "Integer", + "safe": true + }, + { + "name": "getFeePerByte", + "parameters": [], + "offset": 0, + "returntype": "Integer", + "safe": true + }, + { + "name": "getExecFeeFactor", + "parameters": [], + "offset": 0, + "returntype": "Integer", + "safe": true + }, + { + "name": "getStoragePrice", + "parameters": [], + "offset": 0, + "returntype": "Integer", + "safe": true + }, + { + "name": "isBlocked", + "parameters": [ + { + "name": "account", + "type": "Hash160" + } + ], + "offset": 0, + "returntype": "Boolean", + "safe": true + }, + { + "name": "setMaxBlockSize", + "parameters": [ + { + "name": "value", + "type": "Integer" + } + ], + "offset": 0, + "returntype": "Boolean", + "safe": false + }, + { + "name": "setMaxTransactionsPerBlock", + "parameters": [ + { + "name": "value", + "type": "Integer" + } + ], + "offset": 0, + "returntype": "Boolean", + "safe": false + }, + { + "name": "setMaxBlockSystemFee", + "parameters": [ + { + "name": "value", + "type": "Integer" + } + ], + "offset": 0, + "returntype": "Boolean", + "safe": false + }, + { + "name": "setFeePerByte", + "parameters": [ + { + "name": "value", + "type": "Integer" + } + ], + "offset": 0, + "returntype": "Boolean", + "safe": false + }, + { + "name": "setExecFeeFactor", + "parameters": [ + { + "name": "value", + "type": "Integer" + } + ], + "offset": 0, + "returntype": "Boolean", + "safe": false + }, + { + "name": "setStoragePrice", + "parameters": [ + { + "name": "value", + "type": "Integer" + } + ], + "offset": 0, + "returntype": "Boolean", + "safe": false + }, + { + "name": "blockAccount", + "parameters": [ + { + "name": "account", + "type": "Hash160" + } + ], + "offset": 0, + "returntype": "Boolean", + "safe": false + }, + { + "name": "unblockAccount", + "parameters": [ + { + "name": "account", + "type": "Hash160" + } + ], + "offset": 0, + "returntype": "Boolean", + "safe": false + } + ], + "events": [] + }, + "permissions": [ + { + "contract": "*", + "methods": "*" + } + ], + "trusts": [], + "extra": null + }, + "activeblockindex": 0 + }, + { + "id": -5, + "hash": "0xf4bbd95569e8dda2cb84eb609a1488ddd0d9fa91", + "nef": { + "magic": 860243278, + "compiler": "ScriptBuilder", + "version": "3.0", + "tokens": [], + "script": "APtBGvd7Zw==", + "checksum": 1875564659 + }, + "manifest": { + "name": "RoleManagement", + "groups": [], + "supportedstandards": [], + "abi": { + "methods": [ + { + "name": "getDesignatedByRole", + "parameters": [ + { + "name": "role", + "type": "Integer" + }, + { + "name": "index", + "type": "Integer" + } + ], + "offset": 0, + "returntype": "Array", + "safe": true + }, + { + "name": "designateAsRole", + "parameters": [ + { + "name": "role", + "type": "Integer" + }, + { + "name": "nodes", + "type": "Array" + } + ], + "offset": 0, + "returntype": "Void", + "safe": false + } + ], + "events": [] + }, + "permissions": [ + { + "contract": "*", + "methods": "*" + } + ], + "trusts": [], + "extra": null + }, + "activeblockindex": 0 + }, + { + "id": -6, + "hash": "0x8cd3889136056b3304ec59f6d424b8767710ed79", + "nef": { + "magic": 860243278, + "compiler": "ScriptBuilder", + "version": "3.0", + "tokens": [], + "script": "APpBGvd7Zw==", + "checksum": 3970697217 + }, + "manifest": { + "name": "OracleContract", + "groups": [], + "supportedstandards": [], + "abi": { + "methods": [ + { + "name": "finish", + "parameters": [], + "offset": 0, + "returntype": "Void", + "safe": false + }, + { + "name": "request", + "parameters": [ + { + "name": "url", + "type": "String" + }, + { + "name": "filter", + "type": "String" + }, + { + "name": "callback", + "type": "String" + }, + { + "name": "userData", + "type": "Any" + }, + { + "name": "gasForResponse", + "type": "Integer" + } + ], + "offset": 0, + "returntype": "Void", + "safe": false + }, + { + "name": "verify", + "parameters": [], + "offset": 0, + "returntype": "Boolean", + "safe": true + } + ], + "events": [ + { + "name": "OracleRequest", + "parameters": [ + { + "name": "Id", + "type": "Integer" + }, + { + "name": "RequestContract", + "type": "Hash160" + }, + { + "name": "Url", + "type": "String" + }, + { + "name": "Filter", + "type": "String" + } + ] + }, + { + "name": "OracleResponse", + "parameters": [ + { + "name": "Id", + "type": "Integer" + }, + { + "name": "OriginalTx", + "type": "Hash256" + } + ] + } + ] + }, + "permissions": [ + { + "contract": "*", + "methods": "*" + } + ], + "trusts": [], + "extra": null + }, + "activeblockindex": 0 + }, + { + "id": -7, + "hash": "0xa92fbe5bf164170a624474841485b20b45a26047", + "nef": { + "magic": 860243278, + "compiler": "ScriptBuilder", + "version": "3.0", + "tokens": [], + "script": "APlBGvd7Zw==", + "checksum": 2817451339 + }, + "manifest": { + "name": "NameService", + "groups": [], + "supportedstandards": [], + "abi": { + "methods": [ + { + "name": "addRoot", + "parameters": [ + { + "name": "root", + "type": "String" + } + ], + "offset": 0, + "returntype": "Void", + "safe": false + }, + { + "name": "setPrice", + "parameters": [ + { + "name": "price", + "type": "Integer" + } + ], + "offset": 0, + "returntype": "Void", + "safe": false + }, + { + "name": "getPrice", + "parameters": [], + "offset": 0, + "returntype": "Integer", + "safe": true + }, + { + "name": "isAvailable", + "parameters": [ + { + "name": "name", + "type": "String" + } + ], + "offset": 0, + "returntype": "Boolean", + "safe": true + }, + { + "name": "register", + "parameters": [ + { + "name": "name", + "type": "String" + }, + { + "name": "owner", + "type": "Hash160" + } + ], + "offset": 0, + "returntype": "Boolean", + "safe": false + }, + { + "name": "renew", + "parameters": [ + { + "name": "name", + "type": "String" + } + ], + "offset": 0, + "returntype": "Integer", + "safe": false + }, + { + "name": "setAdmin", + "parameters": [ + { + "name": "name", + "type": "String" + }, + { + "name": "admin", + "type": "Hash160" + } + ], + "offset": 0, + "returntype": "Void", + "safe": false + }, + { + "name": "setRecord", + "parameters": [ + { + "name": "name", + "type": "String" + }, + { + "name": "type", + "type": "Integer" + }, + { + "name": "data", + "type": "String" + } + ], + "offset": 0, + "returntype": "Void", + "safe": false + }, + { + "name": "getRecord", + "parameters": [ + { + "name": "name", + "type": "String" + }, + { + "name": "type", + "type": "Integer" + } + ], + "offset": 0, + "returntype": "String", + "safe": true + }, + { + "name": "deleteRecord", + "parameters": [ + { + "name": "name", + "type": "String" + }, + { + "name": "type", + "type": "Integer" + } + ], + "offset": 0, + "returntype": "Void", + "safe": false + }, + { + "name": "resolve", + "parameters": [ + { + "name": "name", + "type": "String" + }, + { + "name": "type", + "type": "Integer" + } + ], + "offset": 0, + "returntype": "String", + "safe": true + }, + { + "name": "totalSupply", + "parameters": [], + "offset": 0, + "returntype": "Integer", + "safe": true + }, + { + "name": "ownerOf", + "parameters": [ + { + "name": "tokenId", + "type": "ByteArray" + } + ], + "offset": 0, + "returntype": "Hash160", + "safe": true + }, + { + "name": "properties", + "parameters": [ + { + "name": "tokenId", + "type": "ByteArray" + } + ], + "offset": 0, + "returntype": "Any", + "safe": true + }, + { + "name": "balanceOf", + "parameters": [ + { + "name": "owner", + "type": "Hash160" + } + ], + "offset": 0, + "returntype": "Integer", + "safe": true + }, + { + "name": "tokensOf", + "parameters": [ + { + "name": "owner", + "type": "Hash160" + } + ], + "offset": 0, + "returntype": "Any", + "safe": true + }, + { + "name": "transfer", + "parameters": [ + { + "name": "to", + "type": "Hash160" + }, + { + "name": "tokenId", + "type": "ByteArray" + } + ], + "offset": 0, + "returntype": "Boolean", + "safe": false + }, + { + "name": "symbol", + "parameters": [], + "offset": 0, + "returntype": "String", + "safe": true + }, + { + "name": "decimals", + "parameters": [], + "offset": 0, + "returntype": "Integer", + "safe": true + } + ], + "events": [ + { + "name": "Transfer", + "parameters": [ + { + "name": "from", + "type": "Hash160" + }, + { + "name": "to", + "type": "Hash160" + }, + { + "name": "amount", + "type": "Integer" + }, + { + "name": "tokenId", + "type": "ByteArray" + } + ] + } + ] + }, + "permissions": [ + { + "contract": "*", + "methods": "*" + } + ], + "trusts": [], + "extra": null + }, + "activeblockindex": 0 + } + ] + } + }, { "Name": "getrawmempoolasync", "Request": { diff --git a/tests/Neo.Network.RPC.Tests/UT_RpcClient.cs b/tests/Neo.Network.RPC.Tests/UT_RpcClient.cs index 9add97bec..bc90da031 100644 --- a/tests/Neo.Network.RPC.Tests/UT_RpcClient.cs +++ b/tests/Neo.Network.RPC.Tests/UT_RpcClient.cs @@ -177,6 +177,17 @@ public async Task TestGetContractState() } } + [TestMethod] + public async Task TestGetNativeContracts() + { + var tests = TestUtils.RpcTestCases.Where(p => p.Name == nameof(rpc.GetNativeContractsAsync).ToLower()); + foreach (var test in tests) + { + var result = await rpc.GetNativeContractsAsync(); + Assert.AreEqual(test.Response.Result.ToString(), ((JArray)result.Select(p => p.ToJson()).ToArray()).ToString()); + } + } + [TestMethod] public async Task TestGetRawMempool() {