diff --git a/neo.UnitTests/Network/RPC/UT_RpcClient.cs b/neo.UnitTests/Network/RPC/UT_RpcClient.cs new file mode 100644 index 0000000000..955c481248 --- /dev/null +++ b/neo.UnitTests/Network/RPC/UT_RpcClient.cs @@ -0,0 +1,540 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Moq.Protected; +using Neo.IO; +using Neo.IO.Json; +using Neo.Ledger; +using Neo.Network.P2P.Payloads; +using Neo.Network.RPC; +using Neo.Network.RPC.Models; +using Neo.SmartContract; +using Neo.SmartContract.Manifest; +using Neo.VM; +using System; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace Neo.UnitTests.Network.RPC +{ + [TestClass] + public class UT_RpcClient + { + RpcClient rpc; + Mock handlerMock; + + [TestInitialize] + public void TestSetup() + { + handlerMock = new Mock(MockBehavior.Strict); + + // use real http client with mocked handler here + var httpClient = new HttpClient(handlerMock.Object) + { + BaseAddress = new Uri("http://seed1.neo.org:10331"), + }; + + rpc = new RpcClient(httpClient); + } + + private void MockResponse(string content) + { + handlerMock.Protected() + // Setup the PROTECTED method to mock + .Setup>( + "SendAsync", + ItExpr.IsAny(), + ItExpr.IsAny() + ) + // prepare the expected response of the mocked http call + .ReturnsAsync(new HttpResponseMessage() + { + StatusCode = HttpStatusCode.OK, + Content = new StringContent(content), + }) + .Verifiable(); + } + + private JObject CreateErrorResponse(JObject id, int code, string message, JObject data = null) + { + JObject response = CreateResponse(id); + response["error"] = new JObject(); + response["error"]["code"] = code; + response["error"]["message"] = message; + if (data != null) + response["error"]["data"] = data; + return response; + } + + private JObject CreateResponse(JObject id) + { + JObject response = new JObject(); + response["jsonrpc"] = "2.0"; + response["id"] = id; + return response; + } + + [TestMethod] + public void TestErrorResponse() + { + JObject response = CreateErrorResponse(null, -32700, "Parse error"); + MockResponse(response.ToString()); + try + { + var result = rpc.GetBlockHex("773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e"); + } + catch (RpcException ex) + { + Assert.AreEqual(-32700, ex.HResult); + Assert.AreEqual("Parse error", ex.Message); + } + } + + [TestMethod] + public void TestGetBestBlockHash() + { + JObject response = CreateResponse(1); + response["result"] = "000000002deadfa82cbc4682f5800"; + MockResponse(response.ToString()); + + var result = rpc.GetBestBlockHash(); + Assert.AreEqual("000000002deadfa82cbc4682f5800", result); + } + + [TestMethod] + public void TestGetBlockHex() + { + JObject response = CreateResponse(1); + response["result"] = "000000002deadfa82cbc4682f5800"; + MockResponse(response.ToString()); + + var result = rpc.GetBlockHex("773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e"); + Assert.AreEqual("000000002deadfa82cbc4682f5800", result); + } + + [TestMethod] + public void TestGetBlock() + { + // create block + var block = new Block(); + TestUtils.SetupBlockWithValues(block, UInt256.Zero, out UInt256 merkRootVal, out UInt160 val160, out ulong timestampVal, out uint indexVal, out Witness scriptVal, out Transaction[] transactionsVal, 0); + + block.Transactions = new[] + { + TestUtils.GetTransaction(), + TestUtils.GetTransaction(), + TestUtils.GetTransaction() + }; + + JObject json = block.ToJson(); + JObject response = CreateResponse(1); + response["result"] = json; + MockResponse(response.ToString()); + + var result = rpc.GetBlock("773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e"); + Assert.AreEqual(block.Hash.ToString(), result.Block.Hash.ToString()); + Assert.IsNull(result.Confirmations); + Assert.AreEqual(block.Transactions.Length, result.Block.Transactions.Length); + Assert.AreEqual(block.Transactions[0].Hash.ToString(), result.Block.Transactions[0].Hash.ToString()); + + // verbose with confirmations + json["confirmations"] = 20; + json["nextblockhash"] = "773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e"; + MockResponse(response.ToString()); + result = rpc.GetBlock("773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e"); + Assert.AreEqual(block.Hash.ToString(), result.Block.Hash.ToString()); + Assert.AreEqual(20, result.Confirmations); + Assert.AreEqual(block.Transactions.Length, result.Block.Transactions.Length); + Assert.AreEqual(block.Transactions[0].Hash.ToString(), result.Block.Transactions[0].Hash.ToString()); + } + + [TestMethod] + public void TestGetBlockCount() + { + JObject response = CreateResponse(1); + response["result"] = 100; + MockResponse(response.ToString()); + + var result = rpc.GetBlockCount(); + Assert.AreEqual(100, result); + } + + [TestMethod] + public void TestGetBlockHash() + { + JObject response = CreateResponse(1); + response["result"] = "0x4c1e879872344349067c3b1a30781eeb4f9040d3795db7922f513f6f9660b9b2"; + MockResponse(response.ToString()); + + var result = rpc.GetBlockHash(100); + Assert.AreEqual("0x4c1e879872344349067c3b1a30781eeb4f9040d3795db7922f513f6f9660b9b2", result); + } + + [TestMethod] + public void TestGetBlockHeaderHex() + { + JObject response = CreateResponse(1); + response["result"] = "0x4c1e879872344349067c3b1a30781eeb4f9040d3795db7922f513f6f9660b9b2"; + MockResponse(response.ToString()); + + var result = rpc.GetBlockHeaderHex("100"); + Assert.AreEqual("0x4c1e879872344349067c3b1a30781eeb4f9040d3795db7922f513f6f9660b9b2", result); + } + + [TestMethod] + public void TestGetBlockHeader() + { + Header header = new Header(); + TestUtils.SetupHeaderWithValues(header, UInt256.Zero, out UInt256 merkRootVal, out UInt160 val160, out ulong timestampVal, out uint indexVal, out Witness scriptVal); + + JObject json = header.ToJson(); + JObject response = CreateResponse(1); + response["result"] = json; + MockResponse(response.ToString()); + + var result = rpc.GetBlockHeader("100"); + Assert.AreEqual(header.Hash.ToString(), result.Header.Hash.ToString()); + Assert.IsNull(result.Confirmations); + + json["confirmations"] = 20; + json["nextblockhash"] = "4c1e879872344349067c3b1a30781eeb4f9040d3795db7922f513f6f9660b9b2"; + MockResponse(response.ToString()); + result = rpc.GetBlockHeader("100"); + Assert.AreEqual(header.Hash.ToString(), result.Header.Hash.ToString()); + Assert.AreEqual(20, result.Confirmations); + } + + [TestMethod] + public void TestGetBlockSysFee() + { + JObject response = CreateResponse(1); + response["result"] = "195500"; + MockResponse(response.ToString()); + + var result = rpc.GetBlockSysFee(100); + Assert.AreEqual("195500", result); + } + + [TestMethod] + public void TestGetConnectionCount() + { + JObject response = CreateResponse(1); + response["result"] = 9; + MockResponse(response.ToString()); + + var result = rpc.GetConnectionCount(); + Assert.AreEqual(9, result); + } + + [TestMethod] + public void TestGetContractState() + { + var sb = new ScriptBuilder(); + sb.EmitSysCall(InteropService.System_Runtime_GetInvocationCounter); + + ContractState state = new ContractState + { + Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP }.Concat(sb.ToArray()).ToArray(), + Manifest = ContractManifest.CreateDefault(UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01")) + }; + + JObject response = CreateResponse(1); + response["result"] = state.ToJson(); + MockResponse(response.ToString()); + + var result = rpc.GetContractState("17694b31cc7ee215cea2ded146e0b2b28768fc46"); + + Assert.AreEqual(state.Script.ToHexString(), result.Script.ToHexString()); + Assert.AreEqual(state.Manifest.Abi.EntryPoint.Name, result.Manifest.Abi.EntryPoint.Name); + } + + [TestMethod] + public void TestGetPeers() + { + JObject response = CreateResponse(1); + response["result"] = JObject.Parse(@"{ + ""unconnected"": [ + { + ""address"": ""::ffff:70.73.16.236"", + ""port"": 10333 + }, + { + ""address"": ""::ffff:82.95.77.148"", + ""port"": 10333 + }, + { + ""address"": ""::ffff:49.50.215.166"", + ""port"": 10333 + } + ], + ""bad"": [], + ""connected"": [ + { + ""address"": ""::ffff:139.219.106.33"", + ""port"": 10333 + }, + { + ""address"": ""::ffff:47.88.53.224"", + ""port"": 10333 + } + ] + }"); + MockResponse(response.ToString()); + + var result = rpc.GetPeers(); + Assert.AreEqual("::ffff:139.219.106.33", result.Connected[0].Address); + Assert.AreEqual("::ffff:82.95.77.148", result.Unconnected[1].Address); + } + + [TestMethod] + public void TestGetRawMempool() + { + JObject response = CreateResponse(1); + response["result"] = JObject.Parse(@"[ + ""0x9786cce0dddb524c40ddbdd5e31a41ed1f6b5c8a683c122f627ca4a007a7cf4e"", + ""0xb488ad25eb474f89d5ca3f985cc047ca96bc7373a6d3da8c0f192722896c1cd7"", + ""0xf86f6f2c08fbf766ebe59dc84bc3b8829f1053f0a01deb26bf7960d99fa86cd6"" + ]"); + MockResponse(response.ToString()); + + var result = rpc.GetRawMempool(); + Assert.AreEqual("0xb488ad25eb474f89d5ca3f985cc047ca96bc7373a6d3da8c0f192722896c1cd7", result[1]); + } + + [TestMethod] + public void TestGetRawMempoolBoth() + { + JObject json = new JObject(); + json["height"] = 65535; + json["verified"] = new JArray(new[] { "0x9786cce0dddb524c40ddbdd5e31a41ed1f6b5c8a683c122f627ca4a007a7cf4e" }.Select(p => (JObject)p)); + json["unverified"] = new JArray(new[] { "0xb488ad25eb474f89d5ca3f985cc047ca96bc7373a6d3da8c0f192722896c1cd7", "0xf86f6f2c08fbf766ebe59dc84bc3b8829f1053f0a01deb26bf7960d99fa86cd6" }.Select(p => (JObject)p)); + + JObject response = CreateResponse(1); + response["result"] = json; + MockResponse(response.ToString()); + + var result = rpc.GetRawMempoolBoth(); + Assert.AreEqual((uint)65535, result.Height); + Assert.AreEqual("0x9786cce0dddb524c40ddbdd5e31a41ed1f6b5c8a683c122f627ca4a007a7cf4e", result.Verified[0]); + Assert.AreEqual("0xf86f6f2c08fbf766ebe59dc84bc3b8829f1053f0a01deb26bf7960d99fa86cd6", result.UnVerified[1]); + } + + [TestMethod] + public void TestGetRawTransactionHex() + { + var json = TestUtils.GetTransaction().ToArray().ToHexString(); + + JObject response = CreateResponse(1); + response["result"] = json; + MockResponse(response.ToString()); + + //var result = rpc.GetRawTransactionHex("0x9786cce0dddb524c40ddbdd5e31a41ed1f6b5c8a683c122f627ca4a007a7cf4e"); + var result = rpc.GetRawTransactionHex(TestUtils.GetTransaction().Hash.ToString()); + Assert.AreEqual(json, result); + } + + [TestMethod] + public void TestGetRawTransaction() + { + var transaction = TestUtils.GetTransaction(); + JObject json = transaction.ToJson(); + JObject response = CreateResponse(1); + response["result"] = json; + MockResponse(response.ToString()); + + var result = rpc.GetRawTransaction("0x9786cce0dddb524c40ddbdd5e31a41ed1f6b5c8a683c122f627ca4a007a7cf4e"); + Assert.AreEqual(transaction.Hash, result.Transaction.Hash); + Assert.AreEqual(json.ToString(), result.ToJson().ToString()); + + json["blockhash"] = UInt256.Zero.ToString(); + json["confirmations"] = 100; + json["blocktime"] = 10; + MockResponse(response.ToString()); + + result = rpc.GetRawTransaction("0x9786cce0dddb524c40ddbdd5e31a41ed1f6b5c8a683c122f627ca4a007a7cf4e"); + Assert.AreEqual(transaction.Hash, result.Transaction.Hash); + Assert.AreEqual(100, result.Confirmations); + Assert.AreEqual(json.ToString(), result.ToJson().ToString()); + } + + [TestMethod] + public void TestGetStorage() + { + JObject json = "4c696e"; + JObject response = CreateResponse(1); + response["result"] = json; + MockResponse(response.ToString()); + + var result = rpc.GetStorage("03febccf81ac85e3d795bc5cbd4e84e907812aa3", "5065746572"); + Assert.AreEqual("4c696e", result); + } + + [TestMethod] + public void TestGetTransactionHeight() + { + JObject json = 10000; + JObject response = CreateResponse(1); + response["result"] = json; + MockResponse(response.ToString()); + + var result = rpc.GetTransactionHeight("9c909e1e3ba03290553a68d862e002c7a21ba302e043fc492fe069bf6a134d29"); + Assert.AreEqual(json.ToString(), result.ToString()); + } + + [TestMethod] + public void TestGetValidators() + { + JObject json = JObject.Parse(@"[ + { + ""publickey"": ""02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"", + ""votes"": ""46632420"", + ""active"": true + }, + { + ""publickey"": ""024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"", + ""votes"": ""46632420"", + ""active"": true + } + ]"); + JObject response = CreateResponse(1); + response["result"] = json; + MockResponse(response.ToString()); + + var result = rpc.GetValidators(); + Assert.AreEqual(((JArray)json)[0].ToString(), (result[0]).ToJson().ToString()); + } + + [TestMethod] + public void TestGetVersion() + { + JObject json = new JObject(); + json["tcpPort"] = 30001; + json["wsPort"] = 30002; + json["nonce"] = 1546258664; + json["useragent"] = "/NEO:2.7.5/"; + + var json1 = JObject.Parse(@"{ + ""tcpPort"": 30001, + ""wsPort"": 30002, + ""nonce"": 1546258664, + ""useragent"": ""/NEO:2.7.5/"" + }"); + Assert.AreEqual(json.ToString(), json1.ToString()); + + JObject response = CreateResponse(1); + response["result"] = json; + MockResponse(response.ToString()); + + var result = rpc.GetVersion(); + Assert.AreEqual(30001, result.TcpPort); + Assert.AreEqual("/NEO:2.7.5/", result.UserAgent); + } + + [TestMethod] + public void TestInvokeFunction() + { + JObject json = JObject.Parse(@" + { + ""script"": ""1426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf"", + ""state"": ""HALT"", + ""gas_consumed"": ""0.311"", + ""stack"": [ + { + ""type"": ""ByteArray"", + ""value"": ""262bec084432"" + } + ], + ""tx"":""d101361426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf000000000000000000000000"" + }"); + JObject response = CreateResponse(1); + response["result"] = json; + MockResponse(response.ToString()); + + var result = rpc.InvokeFunction("af7c7328eee5a275a3bcaee2bf0cf662b5e739be", "balanceOf", new[] { new RpcStack { Type = "Hash160", Value = "91b83e96f2a7c4fdf0c1688441ec61986c7cae26" } }); + Assert.AreEqual(json.ToString(), result.ToJson().ToString()); + } + + [TestMethod] + public void TestInvokeScript() + { + JObject json = JObject.Parse(@" + { + ""script"": ""1426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf"", + ""state"": ""HALT"", + ""gas_consumed"": ""0.311"", + ""stack"": [ + { + ""type"": ""ByteArray"", + ""value"": ""262bec084432"" + } + ], + ""tx"":""d101361426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf000000000000000000000000"" + }"); + JObject response = CreateResponse(1); + response["result"] = json; + MockResponse(response.ToString()); + + var result = rpc.InvokeScript("00046e616d656724058e5e1b6008847cd662728549088a9ee82191"); + Assert.AreEqual(json.ToString(), result.ToJson().ToString()); + } + + [TestMethod] + public void TestListPlugins() + { + JObject json = JObject.Parse(@"[{ + ""name"": ""SimplePolicyPlugin"", + ""version"": ""2.10.1.0"", + ""interfaces"": [ + ""ILogPlugin"", + ""IPolicyPlugin"" + ] + }]"); + JObject response = CreateResponse(1); + response["result"] = json; + MockResponse(response.ToString()); + + var result = rpc.ListPlugins(); + Assert.AreEqual(((JArray)json)[0].ToString(), result[0].ToJson().ToString()); + } + + [TestMethod] + public void TestSendRawTransaction() + { + JObject json = true; + JObject response = CreateResponse(1); + response["result"] = json; + MockResponse(response.ToString()); + + var result = rpc.SendRawTransaction("80000001195876cb34364dc38b730077156c6bc3a7fc570044a66fbfeeea56f71327e8ab0000029b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500c65eaf440000000f9a23e06f74cf86b8827a9108ec2e0f89ad956c9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc50092e14b5e00000030aab52ad93f6ce17ca07fa88fc191828c58cb71014140915467ecd359684b2dc358024ca750609591aa731a0b309c7fb3cab5cd0836ad3992aa0a24da431f43b68883ea5651d548feb6bd3c8e16376e6e426f91f84c58232103322f35c7819267e721335948d385fae5be66e7ba8c748ac15467dcca0693692dac"); + Assert.AreEqual(json.ToString(), ((JObject)result).ToString()); + } + + [TestMethod] + public void TestSubmitBlock() + { + JObject json = true; + JObject response = CreateResponse(1); + response["result"] = json; + MockResponse(response.ToString()); + + var result = rpc.SubmitBlock("03febccf81ac85e3d795bc5cbd4e84e907812aa3"); + Assert.AreEqual(json.ToString(), ((JObject)result).ToString()); + } + + [TestMethod] + public void TestValidateAddress() + { + JObject json = new JObject(); + json["address"] = "AQVh2pG732YvtNaxEGkQUei3YA4cvo7d2i"; + json["isvalid"] = false; + JObject response = CreateResponse(1); + response["result"] = json; + MockResponse(response.ToString()); + + var result = rpc.ValidateAddress("AQVh2pG732YvtNaxEGkQUei3YA4cvo7d2i"); + Assert.AreEqual(json.ToString(), result.ToJson().ToString()); + } + } +} diff --git a/neo/Ledger/ContractState.cs b/neo/Ledger/ContractState.cs index 8f97d4b363..da02db912f 100644 --- a/neo/Ledger/ContractState.cs +++ b/neo/Ledger/ContractState.cs @@ -64,5 +64,13 @@ public JObject ToJson() json["manifest"] = Manifest.ToJson(); return json; } + + public static ContractState FromJson(JObject json) + { + ContractState contractState = new ContractState(); + contractState.Script = json["script"].AsString().HexToBytes(); + contractState.Manifest = ContractManifest.FromJson(json["manifest"]); + return contractState; + } } } diff --git a/neo/Network/P2P/Payloads/Block.cs b/neo/Network/P2P/Payloads/Block.cs index 58faf54cb1..3f29bb9428 100644 --- a/neo/Network/P2P/Payloads/Block.cs +++ b/neo/Network/P2P/Payloads/Block.cs @@ -2,6 +2,7 @@ using Neo.IO; using Neo.IO.Json; using Neo.Ledger; +using Neo.Wallets; using System; using System.Collections.Generic; using System.IO; @@ -106,6 +107,16 @@ public override JObject ToJson() return json; } + public new static Block FromJson(JObject json) + { + Block block = new Block(); + BlockBase blockBase = block; + blockBase.FromJson(json); + block.ConsensusData = ConsensusData.FromJson(json["consensus_data"]); + block.Transactions = ((JArray)json["tx"]).Select(p => Transaction.FromJson(p)).ToArray(); + return block; + } + public TrimmedBlock Trim() { return new TrimmedBlock diff --git a/neo/Network/P2P/Payloads/BlockBase.cs b/neo/Network/P2P/Payloads/BlockBase.cs index 22ef72d021..8820c95538 100644 --- a/neo/Network/P2P/Payloads/BlockBase.cs +++ b/neo/Network/P2P/Payloads/BlockBase.cs @@ -6,6 +6,7 @@ using Neo.Wallets; using System; using System.IO; +using System.Linq; namespace Neo.Network.P2P.Payloads { @@ -32,7 +33,7 @@ public UInt256 Hash } } - public virtual int Size => + public virtual int Size => sizeof(uint) + //Version PrevHash.Size + //PrevHash MerkleRoot.Size + //MerkleRoot @@ -41,7 +42,7 @@ public UInt256 Hash NextConsensus.Size + //NextConsensus 1 + // Witness.Size; //Witness - + Witness[] IVerifiable.Witnesses { get @@ -111,6 +112,17 @@ public virtual JObject ToJson() return json; } + public void FromJson(JObject json) + { + Version = (uint)json["version"].AsNumber(); + PrevHash = UInt256.Parse(json["previousblockhash"].AsString()); + MerkleRoot = UInt256.Parse(json["merkleroot"].AsString()); + Timestamp = (ulong)json["time"].AsNumber(); + Index = (uint)json["index"].AsNumber(); + NextConsensus = json["nextconsensus"].AsString().ToScriptHash(); + Witness = ((JArray)json["witnesses"]).Select(p => Witness.FromJson(p)).FirstOrDefault(); + } + public virtual bool Verify(Snapshot snapshot) { Header prev_header = snapshot.GetHeader(PrevHash); diff --git a/neo/Network/P2P/Payloads/ConsensusData.cs b/neo/Network/P2P/Payloads/ConsensusData.cs index a48a8f1304..622c42e464 100644 --- a/neo/Network/P2P/Payloads/ConsensusData.cs +++ b/neo/Network/P2P/Payloads/ConsensusData.cs @@ -2,6 +2,7 @@ using Neo.IO; using Neo.IO.Json; using Neo.Ledger; +using System.Globalization; using System.IO; namespace Neo.Network.P2P.Payloads @@ -45,5 +46,14 @@ public JObject ToJson() json["nonce"] = Nonce.ToString("x16"); return json; } + + public static ConsensusData FromJson(JObject json) + { + ConsensusData block = new ConsensusData(); + block.PrimaryIndex = (uint)json["primary"].AsNumber(); + block.Nonce = ulong.Parse(json["nonce"].AsString(), NumberStyles.HexNumber); + return block; + } + } } diff --git a/neo/Network/P2P/Payloads/Header.cs b/neo/Network/P2P/Payloads/Header.cs index 149f4a020f..abd7fbd370 100644 --- a/neo/Network/P2P/Payloads/Header.cs +++ b/neo/Network/P2P/Payloads/Header.cs @@ -1,6 +1,9 @@ -using Neo.Ledger; +using Neo.IO.Json; +using Neo.Ledger; +using Neo.Wallets; using System; using System.IO; +using System.Linq; namespace Neo.Network.P2P.Payloads { @@ -51,5 +54,14 @@ public TrimmedBlock Trim() Hashes = new UInt256[0] }; } + + public new static Header FromJson(JObject json) + { + Header header = new Header(); + BlockBase blockBase = header; + blockBase.FromJson(json); + return header; + } + } } diff --git a/neo/Network/P2P/Payloads/Transaction.cs b/neo/Network/P2P/Payloads/Transaction.cs index 923fe6447f..40efe0e9d3 100644 --- a/neo/Network/P2P/Payloads/Transaction.cs +++ b/neo/Network/P2P/Payloads/Transaction.cs @@ -177,6 +177,21 @@ public JObject ToJson() return json; } + public static Transaction FromJson(JObject json) + { + Transaction tx = new Transaction(); + tx.Version = byte.Parse(json["version"].AsString()); + tx.Nonce = uint.Parse(json["nonce"].AsString()); + tx.Sender = json["sender"].AsString().ToScriptHash(); + tx.SystemFee = long.Parse(json["sys_fee"].AsString()); + tx.NetworkFee = long.Parse(json["net_fee"].AsString()); + tx.ValidUntilBlock = uint.Parse(json["valid_until_block"].AsString()); + tx.Attributes = ((JArray)json["attributes"]).Select(p => TransactionAttribute.FromJson(p)).ToArray(); + tx.Script = json["script"].AsString().HexToBytes(); + tx.Witnesses = ((JArray)json["witnesses"]).Select(p => Witness.FromJson(p)).ToArray(); + return tx; + } + bool IInventory.Verify(Snapshot snapshot) { return Verify(snapshot, Enumerable.Empty()); diff --git a/neo/Network/P2P/Payloads/TransactionAttribute.cs b/neo/Network/P2P/Payloads/TransactionAttribute.cs index 2f384fa046..99d1671ebd 100644 --- a/neo/Network/P2P/Payloads/TransactionAttribute.cs +++ b/neo/Network/P2P/Payloads/TransactionAttribute.cs @@ -33,5 +33,13 @@ public JObject ToJson() json["data"] = Data.ToHexString(); return json; } + + public static TransactionAttribute FromJson(JObject json) + { + TransactionAttribute transactionAttribute = new TransactionAttribute(); + transactionAttribute.Usage = (TransactionAttributeUsage)(byte.Parse(json["usage"].AsString())); + transactionAttribute.Data = json["data"].AsString().HexToBytes(); + return transactionAttribute; + } } } diff --git a/neo/Network/P2P/Payloads/Witness.cs b/neo/Network/P2P/Payloads/Witness.cs index 237a954642..d352882e17 100644 --- a/neo/Network/P2P/Payloads/Witness.cs +++ b/neo/Network/P2P/Payloads/Witness.cs @@ -45,5 +45,13 @@ public JObject ToJson() json["verification"] = VerificationScript.ToHexString(); return json; } + + public static Witness FromJson(JObject json) + { + Witness witness = new Witness(); + witness.InvocationScript = json["invocation"].AsString().HexToBytes(); + witness.VerificationScript = json["verification"].AsString().HexToBytes(); + return witness; + } } } diff --git a/neo/Network/RPC/Models/RpcBlock.cs b/neo/Network/RPC/Models/RpcBlock.cs new file mode 100644 index 0000000000..0d49b3b53a --- /dev/null +++ b/neo/Network/RPC/Models/RpcBlock.cs @@ -0,0 +1,37 @@ +using Neo.IO.Json; +using Neo.Network.P2P.Payloads; + +namespace Neo.Network.RPC.Models +{ + public class RpcBlock + { + public Block Block { get; set; } + + public int? Confirmations { get; set; } + + public UInt256 NextBlockHash { get; set; } + + public JObject ToJson() + { + JObject json = Block.ToJson(); + if (Confirmations != null) + { + json["confirmations"] = Confirmations; + json["nextblockhash"] = NextBlockHash.ToString(); + } + return json; + } + + public static RpcBlock FromJson(JObject json) + { + RpcBlock block = new RpcBlock(); + block.Block = Block.FromJson(json); + if (json["confirmations"] != null) + { + block.Confirmations = (int)json["confirmations"].AsNumber(); + block.NextBlockHash = UInt256.Parse(json["nextblockhash"].AsString()); + } + return block; + } + } +} diff --git a/neo/Network/RPC/Models/RpcBlockHeader.cs b/neo/Network/RPC/Models/RpcBlockHeader.cs new file mode 100644 index 0000000000..250acfd818 --- /dev/null +++ b/neo/Network/RPC/Models/RpcBlockHeader.cs @@ -0,0 +1,37 @@ +using Neo.IO.Json; +using Neo.Network.P2P.Payloads; + +namespace Neo.Network.RPC.Models +{ + public class RpcBlockHeader + { + public Header Header { get; set; } + + public int? Confirmations { get; set; } + + public UInt256 NextBlockHash { get; set; } + + public JObject ToJson() + { + JObject json = Header.ToJson(); + if (Confirmations != null) + { + json["confirmations"] = Confirmations; + json["nextblockhash"] = NextBlockHash.ToString(); + } + return json; + } + + public static RpcBlockHeader FromJson(JObject json) + { + RpcBlockHeader block = new RpcBlockHeader(); + block.Header = Header.FromJson(json); + if (json["confirmations"] != null) + { + block.Confirmations = (int)json["confirmations"].AsNumber(); + block.NextBlockHash = UInt256.Parse(json["nextblockhash"].AsString()); + } + return block; + } + } +} diff --git a/neo/Network/RPC/Models/RpcInvokeResult.cs b/neo/Network/RPC/Models/RpcInvokeResult.cs new file mode 100644 index 0000000000..9a0f646588 --- /dev/null +++ b/neo/Network/RPC/Models/RpcInvokeResult.cs @@ -0,0 +1,69 @@ +using Neo.IO.Json; +using Newtonsoft.Json; +using System.Linq; + +namespace Neo.Network.RPC.Models +{ + public class RpcInvokeResult + { + [JsonProperty(PropertyName = "script")] + public string Script { get; set; } + + [JsonProperty(PropertyName = "state")] + public string State { get; set; } + + [JsonProperty(PropertyName = "gas_consumed")] + public string GasConsumed { get; set; } + + [JsonProperty(PropertyName = "stack")] + public RpcStack[] Stack { get; set; } + + [JsonProperty(PropertyName = "tx")] + public string Tx { get; set; } + + public JObject ToJson() + { + JObject json = new JObject(); + json["script"] = Script; + json["state"] = State; + json["gas_consumed"] = GasConsumed; + json["stack"] = new JArray(Stack.Select(p => p.ToJson())); + json["tx"] = Tx; + return json; + } + + public static RpcInvokeResult FromJson(JObject json) + { + RpcInvokeResult invokeScriptResult = new RpcInvokeResult(); + invokeScriptResult.Script = json["script"].AsString(); + invokeScriptResult.State = json["state"].AsString(); + invokeScriptResult.GasConsumed = json["gas_consumed"].AsString(); + invokeScriptResult.Tx = json["tx"].AsString(); + invokeScriptResult.Stack = ((JArray)json["stack"]).Select(p => RpcStack.FromJson(p)).ToArray(); + return invokeScriptResult; + } + } + + public class RpcStack + { + public string Type { get; set; } + + public string Value { get; set; } + + public JObject ToJson() + { + JObject json = new JObject(); + json["type"] = Type; + json["value"] = Value; + return json; + } + + public static RpcStack FromJson(JObject json) + { + RpcStack stackJson = new RpcStack(); + stackJson.Type = json["type"].AsString(); + stackJson.Value = json["value"].AsString(); + return stackJson; + } + } +} diff --git a/neo/Network/RPC/Models/RpcNep5Balances.cs b/neo/Network/RPC/Models/RpcNep5Balances.cs new file mode 100644 index 0000000000..edcf8d7f5e --- /dev/null +++ b/neo/Network/RPC/Models/RpcNep5Balances.cs @@ -0,0 +1,57 @@ +using Neo.IO.Json; +using System.Linq; +using System.Numerics; + +namespace Neo.Network.RPC.Models +{ + public class RpcNep5Balances + { + public string Address { get; set; } + + public RpcNep5Balance[] Balances { get; set; } + + public JObject ToJson() + { + JObject json = new JObject(); + json["address"] = Address; + json["balance"] = Balances.Select(p => p.ToJson()).ToArray(); + return json; + } + + public static RpcNep5Balances FromJson(JObject json) + { + RpcNep5Balances nep5Balance = new RpcNep5Balances(); + nep5Balance.Address = json["address"].AsString(); + //List listBalance = new List(); + nep5Balance.Balances = ((JArray)json["balance"]).Select(p => RpcNep5Balance.FromJson(p)).ToArray(); + return nep5Balance; + } + } + + public class RpcNep5Balance + { + public UInt160 AssetHash { get; set; } + + public BigInteger Amount { get; set; } + + public uint LastUpdatedBlock { get; set; } + + public JObject ToJson() + { + JObject json = new JObject(); + json["asset_hash"] = AssetHash.ToArray().ToHexString(); + json["amount"] = Amount.ToString(); + json["last_updated_block"] = LastUpdatedBlock.ToString(); + return json; + } + + public static RpcNep5Balance FromJson(JObject json) + { + RpcNep5Balance balance = new RpcNep5Balance(); + balance.AssetHash = UInt160.Parse(json["asset_hash"].AsString()); + balance.Amount = BigInteger.Parse(json["amount"].AsString()); + balance.LastUpdatedBlock = uint.Parse(json["last_updated_block"].AsString()); + return balance; + } + } +} diff --git a/neo/Network/RPC/Models/RpcPeers.cs b/neo/Network/RPC/Models/RpcPeers.cs new file mode 100644 index 0000000000..5caa99a66a --- /dev/null +++ b/neo/Network/RPC/Models/RpcPeers.cs @@ -0,0 +1,55 @@ +using Neo.IO.Json; +using System.Linq; + +namespace Neo.Network.RPC.Models +{ + public class RpcPeers + { + public RpcPeer[] Unconnected { get; set; } + + public RpcPeer[] Bad { get; set; } + + public RpcPeer[] Connected { get; set; } + + public JObject ToJson() + { + JObject json = new JObject(); + json["unconnected"] = new JArray(Unconnected.Select(p => p.ToJson())); + json["bad"] = new JArray(Bad.Select(p => p.ToJson())); + json["connected"] = new JArray(Connected.Select(p => p.ToJson())); + return json; + } + + public static RpcPeers FromJson(JObject json) + { + RpcPeers result = new RpcPeers(); + result.Unconnected = ((JArray)json["unconnected"]).Select(p => RpcPeer.FromJson(p)).ToArray(); + result.Bad = ((JArray)json["bad"]).Select(p => RpcPeer.FromJson(p)).ToArray(); + result.Connected = ((JArray)json["connected"]).Select(p => RpcPeer.FromJson(p)).ToArray(); + return result; + } + } + + public class RpcPeer + { + public string Address { get; set; } + + public int Port { get; set; } + + public JObject ToJson() + { + JObject json = new JObject(); + json["address"] = Address; + json["port"] = Port; + return json; + } + + public static RpcPeer FromJson(JObject json) + { + RpcPeer peer = new RpcPeer(); + peer.Address = json["address"].AsString(); + peer.Port = int.Parse(json["port"].AsString()); + return peer; + } + } +} diff --git a/neo/Network/RPC/Models/RpcPlugin.cs b/neo/Network/RPC/Models/RpcPlugin.cs new file mode 100644 index 0000000000..465b3f2d17 --- /dev/null +++ b/neo/Network/RPC/Models/RpcPlugin.cs @@ -0,0 +1,32 @@ +using Neo.IO.Json; +using System.Linq; + +namespace Neo.Network.RPC.Models +{ + public class RpcPlugin + { + public string Name { get; set; } + + public string Version { get; set; } + + public string[] Interfaces { get; set; } + + public JObject ToJson() + { + JObject json = new JObject(); + json["name"] = Name; + json["version"] = Version; + json["interfaces"] = new JArray(Interfaces.Select(p => (JObject)p)); + return json; + } + + public static RpcPlugin FromJson(JObject json) + { + RpcPlugin plugin = new RpcPlugin(); + plugin.Name = json["name"].AsString(); + plugin.Version = json["version"].AsString(); + plugin.Interfaces = ((JArray)json["interfaces"]).Select(p => p.AsString()).ToArray(); + return plugin; + } + } +} diff --git a/neo/Network/RPC/Models/RpcRawMemPool.cs b/neo/Network/RPC/Models/RpcRawMemPool.cs new file mode 100644 index 0000000000..1841916ff2 --- /dev/null +++ b/neo/Network/RPC/Models/RpcRawMemPool.cs @@ -0,0 +1,32 @@ +using Neo.IO.Json; +using System.Linq; + +namespace Neo.Network.RPC.Models +{ + public class RpcRawMemPool + { + public uint Height { get; set; } + + public string[] Verified { get; set; } + + public string[] UnVerified { get; set; } + + public JObject ToJson() + { + JObject json = new JObject(); + json["height"] = Height; + json["verified"] = new JArray(Verified.Select(p=>(JObject)p)); + json["unverified"] = new JArray(UnVerified.Select(p => (JObject)p)); + return json; + } + + public static RpcRawMemPool FromJson(JObject json) + { + RpcRawMemPool rawMemPool = new RpcRawMemPool(); + rawMemPool.Height = uint.Parse(json["height"].AsString()); + rawMemPool.Verified = ((JArray)json["verified"]).Select(p => p.AsString()).ToArray(); + rawMemPool.UnVerified = ((JArray)json["unverified"]).Select(p => p.AsString()).ToArray(); + return rawMemPool; + } + } +} diff --git a/neo/Network/RPC/Models/RpcRequest.cs b/neo/Network/RPC/Models/RpcRequest.cs new file mode 100644 index 0000000000..9c2d2f05ec --- /dev/null +++ b/neo/Network/RPC/Models/RpcRequest.cs @@ -0,0 +1,37 @@ +using Neo.IO.Json; +using System.Linq; + +namespace Neo.Network.RPC.Models +{ + public class RpcRequest + { + public int Id { get; set; } + + public string Jsonrpc { get; set; } + + public string Method { get; set; } + + public JObject[] Params { get; set; } + + public static RpcRequest FromJson(JObject json) + { + return new RpcRequest + { + Id = (int)json["id"].AsNumber(), + Jsonrpc = json["jsonrpc"].AsString(), + Method = json["method"].AsString(), + Params = ((JArray)json["params"]).ToArray() + }; + } + + public JObject ToJson() + { + var json = new JObject(); + json["id"] = Id; + json["jsonrpc"] = Jsonrpc; + json["method"] = Method; + json["params"] = new JArray(Params); + return json; + } + } +} diff --git a/neo/Network/RPC/Models/RpcResponse.cs b/neo/Network/RPC/Models/RpcResponse.cs new file mode 100644 index 0000000000..ff20200519 --- /dev/null +++ b/neo/Network/RPC/Models/RpcResponse.cs @@ -0,0 +1,72 @@ +using Neo.IO.Json; + +namespace Neo.Network.RPC.Models +{ + public class RpcResponse + { + public int? Id { get; set; } + + public string Jsonrpc { get; set; } + + public RpcResponseError Error { get; set; } + + public JObject Result { get; set; } + + public string RawResponse { get; set; } + + public static RpcResponse FromJson(JObject json) + { + var response = new RpcResponse + { + Id = (int?)json["id"]?.AsNumber(), + Jsonrpc = json["jsonrpc"].AsString(), + Result = json["result"] + }; + + if (json["error"] != null) + { + response.Error = RpcResponseError.FromJson(json["error"]); + } + + return response; + } + + public JObject ToJson() + { + var json = new JObject(); + json["id"] = Id; + json["jsonrpc"] = Jsonrpc; + json["error"] = Error.ToJson(); + json["result"] = Result; + return json; + } + } + + public class RpcResponseError + { + public int Code { get; set; } + + public string Message { get; set; } + + public JObject Data { get; set; } + + public static RpcResponseError FromJson(JObject json) + { + return new RpcResponseError + { + Code = (int)json["code"].AsNumber(), + Message = json["message"].AsString(), + Data = json["data"], + }; + } + + public JObject ToJson() + { + var json = new JObject(); + json["code"] = Code; + json["message"] = Message; + json["data"] = Data; + return json; + } + } +} diff --git a/neo/Network/RPC/Models/RpcTransaction.cs b/neo/Network/RPC/Models/RpcTransaction.cs new file mode 100644 index 0000000000..3e45830ae6 --- /dev/null +++ b/neo/Network/RPC/Models/RpcTransaction.cs @@ -0,0 +1,41 @@ +using Neo.IO.Json; +using Neo.Network.P2P.Payloads; + +namespace Neo.Network.RPC.Models +{ + public class RpcTransaction + { + public Transaction Transaction { get; set; } + + public UInt256 BlockHash { get; set; } + + public int? Confirmations { get; set; } + + public uint? BlockTime { get; set; } + + public JObject ToJson() + { + JObject json = Transaction.ToJson(); + if (Confirmations != null) + { + json["blockhash"] = BlockHash.ToString(); + json["confirmations"] = Confirmations; + json["blocktime"] = BlockTime; + } + return json; + } + + public static RpcTransaction FromJson(JObject json) + { + RpcTransaction transaction = new RpcTransaction(); + transaction.Transaction = Transaction.FromJson(json); + if (json["confirmations"] != null) + { + transaction.BlockHash = UInt256.Parse(json["blockhash"].AsString()); + transaction.Confirmations = (int)json["confirmations"].AsNumber(); + transaction.BlockTime = (uint)json["blocktime"].AsNumber(); + } + return transaction; + } + } +} diff --git a/neo/Network/RPC/Models/RpcValidateAddressResult.cs b/neo/Network/RPC/Models/RpcValidateAddressResult.cs new file mode 100644 index 0000000000..5e59bf8eaf --- /dev/null +++ b/neo/Network/RPC/Models/RpcValidateAddressResult.cs @@ -0,0 +1,27 @@ +using Neo.IO.Json; + +namespace Neo.Network.RPC.Models +{ + public class RpcValidateAddressResult + { + public string Address { get; set; } + + public bool IsValid { get; set; } + + public JObject ToJson() + { + JObject json = new JObject(); + json["address"] = Address; + json["isvalid"] = IsValid; + return json; + } + + public static RpcValidateAddressResult FromJson(JObject json) + { + RpcValidateAddressResult validateAddress = new RpcValidateAddressResult(); + validateAddress.Address = json["address"].AsString(); + validateAddress.IsValid = json["isvalid"].AsBoolean(); + return validateAddress; + } + } +} diff --git a/neo/Network/RPC/Models/RpcValidator.cs b/neo/Network/RPC/Models/RpcValidator.cs new file mode 100644 index 0000000000..b694a6dfcd --- /dev/null +++ b/neo/Network/RPC/Models/RpcValidator.cs @@ -0,0 +1,32 @@ +using Neo.IO.Json; +using System.Numerics; + +namespace Neo.Network.RPC.Models +{ + public class RpcValidator + { + public string PublicKey { get; set; } + + public BigInteger Votes { get; set; } + + public bool Active { get; set; } + + public JObject ToJson() + { + JObject json = new JObject(); + json["publickey"] = PublicKey; + json["votes"] = Votes.ToString(); + json["active"] = Active; + return json; + } + + public static RpcValidator FromJson(JObject json) + { + RpcValidator validator = new RpcValidator(); + validator.PublicKey = json["publickey"].AsString(); + validator.Votes = BigInteger.Parse(json["votes"].AsString()); + validator.Active = json["active"].AsBoolean(); + return validator; + } + } +} diff --git a/neo/Network/RPC/Models/RpcVersion.cs b/neo/Network/RPC/Models/RpcVersion.cs new file mode 100644 index 0000000000..02f4dc3407 --- /dev/null +++ b/neo/Network/RPC/Models/RpcVersion.cs @@ -0,0 +1,35 @@ +using Neo.IO.Json; + +namespace Neo.Network.RPC.Models +{ + public class RpcVersion + { + public int TcpPort { get; set; } + + public int WsPort { get; set; } + + public uint Nonce { get; set; } + + public string UserAgent { get; set; } + + public JObject ToJson() + { + JObject json = new JObject(); + json["topPort"] = TcpPort.ToString(); + json["wsPort"] = WsPort.ToString(); + json["nonce"] = Nonce.ToString(); + json["useragent"] = UserAgent; + return json; + } + + public static RpcVersion FromJson(JObject json) + { + RpcVersion version = new RpcVersion(); + version.TcpPort = int.Parse(json["tcpPort"].AsString()); + version.WsPort = int.Parse(json["wsPort"].AsString()); + version.Nonce = uint.Parse(json["nonce"].AsString()); + version.UserAgent = json["useragent"].AsString(); + return version; + } + } +} diff --git a/neo/Network/RPC/RpcClient.cs b/neo/Network/RPC/RpcClient.cs new file mode 100644 index 0000000000..4b968163c7 --- /dev/null +++ b/neo/Network/RPC/RpcClient.cs @@ -0,0 +1,292 @@ +using Neo.IO.Json; +using Neo.Ledger; +using Neo.Network.RPC.Models; +using System; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; + +namespace Neo.Network.RPC +{ + public class RpcClient : IDisposable + { + private readonly HttpClient httpClient; + + public RpcClient(string url) + { + httpClient = new HttpClient() { BaseAddress = new Uri(url) }; + } + + public RpcClient(HttpClient client) + { + httpClient = client; + } + + public void Dispose() + { + httpClient?.Dispose(); + } + + public async Task SendAsync(RpcRequest request) + { + var requestJson = request.ToJson().ToString(); + var result = await httpClient.PostAsync(httpClient.BaseAddress, new StringContent(requestJson, Encoding.UTF8)); + var content = await result.Content.ReadAsStringAsync(); + var response = RpcResponse.FromJson(JObject.Parse(content)); + response.RawResponse = content; + + if (response.Error != null) + { + throw new RpcException(response.Error.Code, response.Error.Message); + } + + return response; + } + + public RpcResponse Send(RpcRequest request) + { + try + { + return SendAsync(request).Result; + } + catch (AggregateException ex) + { + throw ex.GetBaseException(); + } + } + + private JObject RpcSend(string method, params JObject[] paraArgs) + { + var request = new RpcRequest + { + Id = 1, + Jsonrpc = "2.0", + Method = method, + Params = paraArgs.Select(p => p).ToArray() + }; + return Send(request).Result; + } + + /// + /// Returns the hash of the tallest block in the main chain. + /// + public string GetBestBlockHash() + { + return RpcSend("getbestblockhash").AsString(); + } + + /// + /// Returns the hash of the tallest block in the main chain. + /// The serialized information of the block is returned, represented by a hexadecimal string. + /// + public string GetBlockHex(string hashOrIndex) + { + if (int.TryParse(hashOrIndex, out int index)) + { + return RpcSend("getblock", index).AsString(); + } + return RpcSend("getblock", hashOrIndex).AsString(); + } + + /// + /// Returns the hash of the tallest block in the main chain. + /// + public RpcBlock GetBlock(string hashOrIndex) + { + if (int.TryParse(hashOrIndex, out int index)) + { + return RpcBlock.FromJson(RpcSend("getblock", index, true)); + } + return RpcBlock.FromJson(RpcSend("getblock", hashOrIndex, true)); + } + + /// + /// Gets the number of blocks in the main chain. + /// + public int GetBlockCount() + { + return (int)RpcSend("getblockcount").AsNumber(); + } + + /// + /// Returns the hash value of the corresponding block, based on the specified index. + /// + public string GetBlockHash(int index) + { + return RpcSend("getblockhash", index).AsString(); + } + + /// + /// Returns the corresponding block header information according to the specified script hash. + /// + public string GetBlockHeaderHex(string hashOrIndex) + { + if (int.TryParse(hashOrIndex, out int index)) + { + return RpcSend("getblockheader", index).AsString(); + } + return RpcSend("getblockheader", hashOrIndex).AsString(); + } + + /// + /// Returns the corresponding block header information according to the specified script hash. + /// + public RpcBlockHeader GetBlockHeader(string hashOrIndex) + { + if (int.TryParse(hashOrIndex, out int index)) + { + return RpcBlockHeader.FromJson(RpcSend("getblockheader", index, true)); + } + return RpcBlockHeader.FromJson(RpcSend("getblockheader", hashOrIndex, true)); + } + + /// + /// Returns the system fees of the block, based on the specified index. + /// + public string GetBlockSysFee(int height) + { + return RpcSend("getblocksysfee", height).AsString(); + } + + /// + /// Gets the current number of connections for the node. + /// + public int GetConnectionCount() + { + return (int)RpcSend("getconnectioncount").AsNumber(); + } + + /// + /// Queries contract information, according to the contract script hash. + /// + public ContractState GetContractState(string hash) + { + return ContractState.FromJson(RpcSend("getcontractstate", hash)); + } + + /// + /// Gets the list of nodes that the node is currently connected/disconnected from. + /// + public RpcPeers GetPeers() + { + return RpcPeers.FromJson(RpcSend("getpeers")); + } + + /// + /// Obtains the list of unconfirmed transactions in memory. + /// + public string[] GetRawMempool() + { + return ((JArray)RpcSend("getrawmempool")).Select(p => p.AsString()).ToArray(); + } + + /// + /// Obtains the list of unconfirmed transactions in memory. + /// shouldGetUnverified = true + /// + public RpcRawMemPool GetRawMempoolBoth() + { + return RpcRawMemPool.FromJson(RpcSend("getrawmempool")); + } + + /// + /// Returns the corresponding transaction information, based on the specified hash value. + /// + public string GetRawTransactionHex(string txid) + { + return RpcSend("getrawtransaction", txid).AsString(); + } + + /// + /// Returns the corresponding transaction information, based on the specified hash value. + /// verbose = true + /// + public RpcTransaction GetRawTransaction(string txid) + { + return RpcTransaction.FromJson(RpcSend("getrawtransaction", txid, true)); + } + + /// + /// Returns the stored value, according to the contract script hash and the stored key. + /// + public string GetStorage(string script_hash, string key) + { + return RpcSend("getstorage", script_hash, key).AsString(); + } + + /// + /// Returns the block index in which the transaction is found. + /// + public uint GetTransactionHeight(string txid) + { + return uint.Parse(RpcSend("gettransactionheight", txid).AsString()); + } + + /// + /// Returns the current NEO consensus nodes information and voting status. + /// + public RpcValidator[] GetValidators() + { + return ((JArray)RpcSend("getvalidators")).Select(p => RpcValidator.FromJson(p)).ToArray(); + } + + /// + /// Returns the version information about the queried node. + /// + public RpcVersion GetVersion() + { + return RpcVersion.FromJson(RpcSend("getversion")); + } + + /// + /// Returns the result after calling a smart contract at scripthash with the given operation and parameters. + /// This RPC call does not affect the blockchain in any way. + /// + public RpcInvokeResult InvokeFunction(string address, string function, RpcStack[] stacks) + { + return RpcInvokeResult.FromJson(RpcSend("invokefunction", address, function, stacks.Select(p => p.ToJson()).ToArray())); + } + + /// + /// Returns the result after passing a script through the VM. + /// This RPC call does not affect the blockchain in any way. + /// + public RpcInvokeResult InvokeScript(string script) + { + return RpcInvokeResult.FromJson(RpcSend("invokescript", script)); + } + + /// + /// Returns a list of plugins loaded by the node. + /// + public RpcPlugin[] ListPlugins() + { + return ((JArray)RpcSend("listplugins")).Select(p => RpcPlugin.FromJson(p)).ToArray(); + } + + /// + /// Broadcasts a transaction over the NEO network. + /// + public bool SendRawTransaction(string rawTransaction) + { + return RpcSend("sendrawtransaction", rawTransaction).AsBoolean(); + } + + /// + /// Broadcasts a raw block over the NEO network. + /// + public bool SubmitBlock(string block) + { + return RpcSend("submitblock", block).AsBoolean(); + } + + /// + /// Verifies that the address is a correct NEO address. + /// + public RpcValidateAddressResult ValidateAddress(string address) + { + return RpcValidateAddressResult.FromJson(RpcSend("validateaddress", address)); + } + } +}