From bbcfa56171a931c1c6f2b50f9289350dd7c052c0 Mon Sep 17 00:00:00 2001 From: Krain Chen Date: Thu, 19 Mar 2020 15:58:57 +0800 Subject: [PATCH] adapt RpcClient to modules (#171) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * adapt RpcClient to modules * fix json issue * fix size calculate * Fix getnep5balances name issue * add wallet rpc methods, reconstruct rpc UT * PR correction * move models to rpc server * delete models from rpcclient * change plugins with model * format names * move models back to rpc client * fix build error * change test file * auto calculate network fee, update version * format code * fix issue #1416 Deal with ContractParameterType.Any * change GetStorage parameter to enable id query * add test for issue #1416 * PR correction * use snake_case in json * update neo version * Update src/RpcClient/RpcClient.cs Co-Authored-By: Shargon * Update src/RpcClient/RpcClient.cs Co-Authored-By: Shargon * PR correction * update neo version Co-authored-by: Vitor Nazário Coelho Co-authored-by: Nicole <43694095+nicolegys@users.noreply.github.com> Co-authored-by: Shargon --- README.md | 4 +- src/LevelDBStore/LevelDBStore.csproj | 4 +- src/RocksDBStore/RocksDBStore.csproj | 4 +- src/RpcClient/ContractClient.cs | 5 +- src/RpcClient/Models/RpcAccount.cs | 37 + src/RpcClient/Models/RpcApplicationLog.cs | 71 + src/RpcClient/Models/RpcBlock.cs | 14 +- src/RpcClient/Models/RpcBlockHeader.cs | 14 +- src/RpcClient/Models/RpcContractState.cs | 26 + src/RpcClient/Models/RpcInvokeResult.cs | 25 +- src/RpcClient/Models/RpcNep5Balances.cs | 29 +- src/RpcClient/Models/RpcNep5TokenInfo.cs | 2 +- src/RpcClient/Models/RpcNep5Transfers.cs | 81 ++ src/RpcClient/Models/RpcRawMemPool.cs | 13 +- src/RpcClient/Models/RpcRequest.cs | 10 +- src/RpcClient/Models/RpcResponse.cs | 12 +- src/RpcClient/Models/RpcTransaction.cs | 12 +- src/RpcClient/Models/RpcTransferOut.cs | 34 + src/RpcClient/Models/RpcVersion.cs | 16 +- src/RpcClient/Nep5API.cs | 11 +- src/RpcClient/RpcClient.cs | 283 +++- src/RpcClient/RpcClient.csproj | 4 +- src/RpcClient/TransactionManager.cs | 102 +- src/RpcClient/Utility.cs | 4 +- src/RpcClient/WalletAPI.cs | 25 +- src/RpcServer/RpcServer.Blockchain.cs | 2 +- src/RpcServer/RpcServer.Node.cs | 6 +- src/RpcServer/RpcServer.csproj | 4 +- src/StatesDumper/StatesDumper.csproj | 4 +- src/SystemLog/SystemLog.csproj | 4 +- .../Models/UT_RpcBlock.cs | 24 - .../Models/UT_RpcBlockHeader.cs | 24 - .../Models/UT_RpcNep5Balance.cs | 66 - .../Models/UT_RpcNep5Balances.cs | 66 - .../Models/UT_RpcPeer.cs | 23 - .../Models/UT_RpcPeers.cs | 44 - .../Models/UT_RpcRawMemPool.cs | 29 - .../Models/UT_RpcRequest.cs | 31 - .../Models/UT_RpcResponse.cs | 34 - .../Models/UT_RpcVersion.cs | 27 - .../Neo.Network.RPC.Tests.csproj | 6 + tests/Neo.Network.RPC.Tests/RpcTestCases.json | 1279 +++++++++++++++++ tests/Neo.Network.RPC.Tests/TestUtils.cs | 34 + tests/Neo.Network.RPC.Tests/UT_Nep5API.cs | 2 +- tests/Neo.Network.RPC.Tests/UT_RpcClient.cs | 612 ++++---- tests/Neo.Network.RPC.Tests/UT_RpcModels.cs | 139 ++ .../UT_TransactionManager.cs | 17 +- tests/Neo.Network.RPC.Tests/UT_Utility.cs | 2 +- tests/Neo.Network.RPC.Tests/UT_WalletAPI.cs | 13 +- 49 files changed, 2382 insertions(+), 952 deletions(-) create mode 100644 src/RpcClient/Models/RpcAccount.cs create mode 100644 src/RpcClient/Models/RpcApplicationLog.cs create mode 100644 src/RpcClient/Models/RpcContractState.cs create mode 100644 src/RpcClient/Models/RpcNep5Transfers.cs create mode 100644 src/RpcClient/Models/RpcTransferOut.cs delete mode 100644 tests/Neo.Network.RPC.Tests/Models/UT_RpcBlock.cs delete mode 100644 tests/Neo.Network.RPC.Tests/Models/UT_RpcBlockHeader.cs delete mode 100644 tests/Neo.Network.RPC.Tests/Models/UT_RpcNep5Balance.cs delete mode 100644 tests/Neo.Network.RPC.Tests/Models/UT_RpcNep5Balances.cs delete mode 100644 tests/Neo.Network.RPC.Tests/Models/UT_RpcPeer.cs delete mode 100644 tests/Neo.Network.RPC.Tests/Models/UT_RpcPeers.cs delete mode 100644 tests/Neo.Network.RPC.Tests/Models/UT_RpcRawMemPool.cs delete mode 100644 tests/Neo.Network.RPC.Tests/Models/UT_RpcRequest.cs delete mode 100644 tests/Neo.Network.RPC.Tests/Models/UT_RpcResponse.cs delete mode 100644 tests/Neo.Network.RPC.Tests/Models/UT_RpcVersion.cs create mode 100644 tests/Neo.Network.RPC.Tests/RpcTestCases.json create mode 100644 tests/Neo.Network.RPC.Tests/UT_RpcModels.cs diff --git a/README.md b/README.md index bb73063d3..4712b5be2 100644 --- a/README.md +++ b/README.md @@ -72,5 +72,5 @@ This module works in conjunction with RpcServer, otherwise, just local storage ( ## C# SDK ### RpcClient -The RpcClient Plugin is an individual SDK which is used to call NEO RPC methods for development using. -In order to use the module for interacting with NEP5 functionalities `RPC NEP5 Tracker` Plugin will be needed. +The RpcClient Project is an individual SDK that is used to interact with NEO blockchain through NEO RPC methods for development using. The main functions include RPC calling, Transaction making, Contract deployment & calling, and Asset transfering. +It needs a NEO node with the `RpcServer` plugin as a provider. And the provider needs more plugins like `RpcNep5Tracker` and `ApplicationLogs` if you want to call RPC methods supplied by the plugins. diff --git a/src/LevelDBStore/LevelDBStore.csproj b/src/LevelDBStore/LevelDBStore.csproj index 4df986e1a..1fadbf184 100644 --- a/src/LevelDBStore/LevelDBStore.csproj +++ b/src/LevelDBStore/LevelDBStore.csproj @@ -1,7 +1,7 @@ - 3.0.0-CI00847 + 3.0.0-CI00863 netstandard2.1 Neo true @@ -15,7 +15,7 @@ - + diff --git a/src/RocksDBStore/RocksDBStore.csproj b/src/RocksDBStore/RocksDBStore.csproj index fd9eaaada..26249747a 100644 --- a/src/RocksDBStore/RocksDBStore.csproj +++ b/src/RocksDBStore/RocksDBStore.csproj @@ -1,7 +1,7 @@ - 3.0.0-CI00847 + 3.0.0-CI00863 netstandard2.1 Neo.Plugins.Storage @@ -14,7 +14,7 @@ - + diff --git a/src/RpcClient/ContractClient.cs b/src/RpcClient/ContractClient.cs index bf29dbb87..c00b799e0 100644 --- a/src/RpcClient/ContractClient.cs +++ b/src/RpcClient/ContractClient.cs @@ -42,9 +42,8 @@ public RpcInvokeResult TestInvoke(UInt160 scriptHash, string operation, params o /// contract script /// contract manifest /// sender KeyPair - /// transaction NetworkFee, set to be 0 if you don't need higher priority /// - public Transaction CreateDeployContractTx(byte[] contractScript, ContractManifest manifest, KeyPair key, long networkFee = 0) + public Transaction CreateDeployContractTx(byte[] contractScript, ContractManifest manifest, KeyPair key) { byte[] script; using (ScriptBuilder sb = new ScriptBuilder()) @@ -55,7 +54,7 @@ public Transaction CreateDeployContractTx(byte[] contractScript, ContractManifes UInt160 sender = Contract.CreateSignatureRedeemScript(key.PublicKey).ToScriptHash(); Transaction tx = new TransactionManager(rpcClient, sender) - .MakeTransaction(script, null, null, networkFee) + .MakeTransaction(script, null, null) .AddSignature(key) .Sign() .Tx; diff --git a/src/RpcClient/Models/RpcAccount.cs b/src/RpcClient/Models/RpcAccount.cs new file mode 100644 index 000000000..8a4d78922 --- /dev/null +++ b/src/RpcClient/Models/RpcAccount.cs @@ -0,0 +1,37 @@ +using Neo.IO.Json; + +namespace Neo.Network.RPC.Models +{ + public class RpcAccount + { + public string Address { get; set; } + + public bool HasKey { get; set; } + + public string Label { get; set; } + + public bool WatchOnly { get; set; } + + public JObject ToJson() + { + return new JObject + { + ["address"] = Address, + ["haskey"] = HasKey, + ["label"] = Label, + ["watchonly"] = WatchOnly + }; + } + + public static RpcAccount FromJson(JObject json) + { + return new RpcAccount + { + Address = json["address"].AsString(), + HasKey = json["haskey"].AsBoolean(), + Label = json["label"]?.AsString(), + WatchOnly = json["watchonly"].AsBoolean(), + }; + } + } +} diff --git a/src/RpcClient/Models/RpcApplicationLog.cs b/src/RpcClient/Models/RpcApplicationLog.cs new file mode 100644 index 000000000..2b17a9dc9 --- /dev/null +++ b/src/RpcClient/Models/RpcApplicationLog.cs @@ -0,0 +1,71 @@ +using Neo.IO.Json; +using Neo.SmartContract; +using Neo.VM; +using System.Collections.Generic; +using System.Linq; + +namespace Neo.Network.RPC.Models +{ + public class RpcApplicationLog + { + public UInt256 TxId { get; set; } + + public TriggerType Trigger { get; set; } + + public VMState VMState { get; set; } + + public long GasConsumed { get; set; } + + public List Stack { get; set; } + + public List Notifications { get; set; } + + public JObject ToJson() + { + JObject json = new JObject(); + json["txid"] = TxId?.ToString(); + json["trigger"] = Trigger; + json["vmstate"] = VMState; + json["gas_consumed"] = GasConsumed.ToString(); + json["stack"] = Stack.Select(q => q.ToJson()).ToArray(); + json["notifications"] = Notifications.Select(q => q.ToJson()).ToArray(); + return json; + } + + public static RpcApplicationLog FromJson(JObject json) + { + RpcApplicationLog log = new RpcApplicationLog(); + log.TxId = json["txid"] is null ? null : UInt256.Parse(json["txid"].AsString()); + log.Trigger = json["trigger"].TryGetEnum(); + log.VMState = json["vmstate"].TryGetEnum(); + log.GasConsumed = long.Parse(json["gas_consumed"].AsString()); + log.Stack = ((JArray)json["stack"]).Select(p => ContractParameter.FromJson(p)).ToList(); + log.Notifications = ((JArray)json["notifications"]).Select(p => RpcNotifyEventArgs.FromJson(p)).ToList(); + return log; + } + } + + public class RpcNotifyEventArgs + { + public UInt160 Contract { get; set; } + + public ContractParameter State { get; set; } + + public JObject ToJson() + { + JObject json = new JObject(); + json["contract"] = Contract.ToString(); + json["state"] = State.ToJson(); + return json; + } + + public static RpcNotifyEventArgs FromJson(JObject json) + { + return new RpcNotifyEventArgs + { + Contract = UInt160.Parse(json["contract"].AsString()), + State = ContractParameter.FromJson(json["state"]) + }; + } + } +} diff --git a/src/RpcClient/Models/RpcBlock.cs b/src/RpcClient/Models/RpcBlock.cs index 1ff485f96..6689572e5 100644 --- a/src/RpcClient/Models/RpcBlock.cs +++ b/src/RpcClient/Models/RpcBlock.cs @@ -7,7 +7,7 @@ public class RpcBlock { public Block Block { get; set; } - public int Confirmations { get; set; } + public uint Confirmations { get; set; } public UInt256 NextBlockHash { get; set; } @@ -15,10 +15,7 @@ public JObject ToJson() { JObject json = Block.ToJson(); json["confirmations"] = Confirmations; - if (NextBlockHash != null) - { - json["nextblockhash"] = NextBlockHash.ToString(); - } + json["nextblockhash"] = NextBlockHash?.ToString(); return json; } @@ -26,11 +23,8 @@ public static RpcBlock FromJson(JObject json) { RpcBlock block = new RpcBlock(); block.Block = Block.FromJson(json); - block.Confirmations = (int)json["confirmations"].AsNumber(); - if (json["nextblockhash"] != null) - { - block.NextBlockHash = UInt256.Parse(json["nextblockhash"].AsString()); - } + block.Confirmations = (uint)json["confirmations"].AsNumber(); + block.NextBlockHash = json["nextblockhash"] is null ? null : UInt256.Parse(json["nextblockhash"].AsString()); return block; } } diff --git a/src/RpcClient/Models/RpcBlockHeader.cs b/src/RpcClient/Models/RpcBlockHeader.cs index 5346dffd9..94def029f 100644 --- a/src/RpcClient/Models/RpcBlockHeader.cs +++ b/src/RpcClient/Models/RpcBlockHeader.cs @@ -7,7 +7,7 @@ public class RpcBlockHeader { public Header Header { get; set; } - public int Confirmations { get; set; } + public uint Confirmations { get; set; } public UInt256 NextBlockHash { get; set; } @@ -15,10 +15,7 @@ public JObject ToJson() { JObject json = Header.ToJson(); json["confirmations"] = Confirmations; - if (NextBlockHash != null) - { - json["nextblockhash"] = NextBlockHash.ToString(); - } + json["nextblockhash"] = NextBlockHash?.ToString(); return json; } @@ -26,11 +23,8 @@ public static RpcBlockHeader FromJson(JObject json) { RpcBlockHeader block = new RpcBlockHeader(); block.Header = Header.FromJson(json); - block.Confirmations = (int)json["confirmations"].AsNumber(); - if (json["nextblockhash"] != null) - { - block.NextBlockHash = UInt256.Parse(json["nextblockhash"].AsString()); - } + block.Confirmations = (uint)json["confirmations"].AsNumber(); + block.NextBlockHash = json["nextblockhash"] is null ? null : UInt256.Parse(json["nextblockhash"].AsString()); return block; } } diff --git a/src/RpcClient/Models/RpcContractState.cs b/src/RpcClient/Models/RpcContractState.cs new file mode 100644 index 000000000..b87351884 --- /dev/null +++ b/src/RpcClient/Models/RpcContractState.cs @@ -0,0 +1,26 @@ +using Neo.IO.Json; +using Neo.Ledger; +using Neo.SmartContract.Manifest; +using System; + +public class RpcContractState +{ + public ContractState ContractState { get; set; } + + public JObject ToJson() + { + return ContractState.ToJson(); + } + + public static RpcContractState FromJson(JObject json) + { + RpcContractState state = new RpcContractState(); + state.ContractState = new ContractState + { + Id = (int)json["id"].AsNumber(), + Script = Convert.FromBase64String(json["script"].AsString()), + Manifest = ContractManifest.FromJson(json["manifest"]) + }; + return state; + } +} diff --git a/src/RpcClient/Models/RpcInvokeResult.cs b/src/RpcClient/Models/RpcInvokeResult.cs index a9c5f04c4..c58c0afe4 100644 --- a/src/RpcClient/Models/RpcInvokeResult.cs +++ b/src/RpcClient/Models/RpcInvokeResult.cs @@ -1,5 +1,6 @@ using Neo.IO.Json; using Neo.SmartContract; +using System; using System.Linq; namespace Neo.Network.RPC.Models @@ -8,19 +9,30 @@ public class RpcInvokeResult { public string Script { get; set; } - public string State { get; set; } + public VM.VMState State { get; set; } public string GasConsumed { get; set; } public ContractParameter[] Stack { get; set; } + 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())); + try + { + json["stack"] = new JArray(Stack.Select(p => p.ToJson())); + } + catch (InvalidOperationException) + { + // ContractParameter.ToJson() may cause InvalidOperationException + json["stack"] = "error: recursive reference"; + } + if (!string.IsNullOrEmpty(Tx)) json["tx"] = Tx; return json; } @@ -28,9 +40,14 @@ public static RpcInvokeResult FromJson(JObject json) { RpcInvokeResult invokeScriptResult = new RpcInvokeResult(); invokeScriptResult.Script = json["script"].AsString(); - invokeScriptResult.State = json["state"].AsString(); + invokeScriptResult.State = json["state"].TryGetEnum(); invokeScriptResult.GasConsumed = json["gas_consumed"].AsString(); - invokeScriptResult.Stack = ((JArray)json["stack"]).Select(p => ContractParameter.FromJson(p)).ToArray(); + try + { + invokeScriptResult.Stack = ((JArray)json["stack"]).Select(p => ContractParameter.FromJson(p)).ToArray(); + } + catch { } + invokeScriptResult.Tx = json["tx"]?.AsString(); return invokeScriptResult; } } diff --git a/src/RpcClient/Models/RpcNep5Balances.cs b/src/RpcClient/Models/RpcNep5Balances.cs index 74fe7be72..0b8f79c17 100644 --- a/src/RpcClient/Models/RpcNep5Balances.cs +++ b/src/RpcClient/Models/RpcNep5Balances.cs @@ -1,4 +1,6 @@ using Neo.IO.Json; +using Neo.Wallets; +using System.Collections.Generic; using System.Linq; using System.Numerics; @@ -6,24 +8,25 @@ namespace Neo.Network.RPC.Models { public class RpcNep5Balances { - public string Address { get; set; } + public UInt160 UserScriptHash { get; set; } - public RpcNep5Balance[] Balances { get; set; } + public List Balances { get; set; } public JObject ToJson() { JObject json = new JObject(); - json["address"] = Address; json["balance"] = Balances.Select(p => p.ToJson()).ToArray(); + json["address"] = UserScriptHash.ToAddress(); 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(); + RpcNep5Balances nep5Balance = new RpcNep5Balances + { + Balances = ((JArray)json["balance"]).Select(p => RpcNep5Balance.FromJson(p)).ToList(), + UserScriptHash = json["address"].AsString().ToScriptHash() + }; return nep5Balance; } } @@ -41,16 +44,18 @@ public JObject ToJson() JObject json = new JObject(); json["asset_hash"] = AssetHash.ToString(); json["amount"] = Amount.ToString(); - json["last_updated_block"] = LastUpdatedBlock.ToString(); + json["last_updated_block"] = LastUpdatedBlock; 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()); + RpcNep5Balance balance = new RpcNep5Balance + { + AssetHash = UInt160.Parse(json["asset_hash"].AsString()), + Amount = BigInteger.Parse(json["amount"].AsString()), + LastUpdatedBlock = (uint)json["last_updated_block"].AsNumber() + }; return balance; } } diff --git a/src/RpcClient/Models/RpcNep5TokenInfo.cs b/src/RpcClient/Models/RpcNep5TokenInfo.cs index 0f251a5a3..cb609b8a9 100644 --- a/src/RpcClient/Models/RpcNep5TokenInfo.cs +++ b/src/RpcClient/Models/RpcNep5TokenInfo.cs @@ -8,7 +8,7 @@ public class RpcNep5TokenInfo public string Symbol { get; set; } - public uint Decimals { get; set; } + public byte Decimals { get; set; } public BigInteger TotalSupply { get; set; } } diff --git a/src/RpcClient/Models/RpcNep5Transfers.cs b/src/RpcClient/Models/RpcNep5Transfers.cs new file mode 100644 index 000000000..f44180554 --- /dev/null +++ b/src/RpcClient/Models/RpcNep5Transfers.cs @@ -0,0 +1,81 @@ +using Neo.IO.Json; +using Neo.SmartContract; +using Neo.Wallets; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; + +namespace Neo.Network.RPC.Models +{ + public class RpcNep5Transfers + { + public UInt160 UserScriptHash { get; set; } + + public List Sent { get; set; } + + public List Received { get; set; } + + public JObject ToJson() + { + JObject json = new JObject(); + json["sent"] = Sent.Select(p => p.ToJson()).ToArray(); + json["received"] = Received.Select(p => p.ToJson()).ToArray(); + json["address"] = UserScriptHash.ToAddress(); + return json; + } + + public static RpcNep5Transfers FromJson(JObject json) + { + RpcNep5Transfers transfers = new RpcNep5Transfers + { + Sent = ((JArray)json["sent"]).Select(p => RpcNep5Transfer.FromJson(p)).ToList(), + Received = ((JArray)json["received"]).Select(p => RpcNep5Transfer.FromJson(p)).ToList(), + UserScriptHash = json["address"].AsString().ToScriptHash() + }; + return transfers; + } + } + + public class RpcNep5Transfer + { + public ulong TimestampMS { get; set; } + + public UInt160 AssetHash { get; set; } + + public UInt160 UserScriptHash { get; set; } + + public BigInteger Amount { get; set; } + + public uint BlockIndex { get; set; } + + public ushort TransferNotifyIndex { get; set; } + + public UInt256 TxHash { get; set; } + + public JObject ToJson() + { + JObject json = new JObject(); + json["timestamp"] = TimestampMS; + json["asset_hash"] = AssetHash.ToString(); + json["transfer_address"] = UserScriptHash.ToAddress(); + json["amount"] = Amount.ToString(); + json["block_index"] = BlockIndex; + json["transfer_notify_index"] = TransferNotifyIndex; + json["tx_hash"] = TxHash.ToString(); + return json; + } + + public static RpcNep5Transfer FromJson(JObject json) + { + RpcNep5Transfer transfer = new RpcNep5Transfer(); + transfer.TimestampMS = (ulong)json["timestamp"].AsNumber(); + transfer.AssetHash = UInt160.Parse(json["asset_hash"].AsString()); + transfer.UserScriptHash = json["transfer_address"].AsString().ToScriptHash(); + transfer.Amount = BigInteger.Parse(json["amount"].AsString()); + transfer.BlockIndex = (uint)json["block_index"].AsNumber(); + transfer.TransferNotifyIndex = (ushort)json["transfer_notify_index"].AsNumber(); + transfer.TxHash = UInt256.Parse(json["tx_hash"].AsString()); + return transfer; + } + } +} diff --git a/src/RpcClient/Models/RpcRawMemPool.cs b/src/RpcClient/Models/RpcRawMemPool.cs index c5ebd6341..61d8bcf99 100644 --- a/src/RpcClient/Models/RpcRawMemPool.cs +++ b/src/RpcClient/Models/RpcRawMemPool.cs @@ -1,4 +1,5 @@ using Neo.IO.Json; +using System.Collections.Generic; using System.Linq; namespace Neo.Network.RPC.Models @@ -7,16 +8,16 @@ public class RpcRawMemPool { public uint Height { get; set; } - public string[] Verified { get; set; } + public List Verified { get; set; } - public string[] UnVerified { get; set; } + public List 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)); + json["verified"] = new JArray(Verified.Select(p => (JObject)p.ToString())); + json["unverified"] = new JArray(UnVerified.Select(p => (JObject)p.ToString())); return json; } @@ -24,8 +25,8 @@ 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(); + rawMemPool.Verified = ((JArray)json["verified"]).Select(p => UInt256.Parse(p.AsString())).ToList(); + rawMemPool.UnVerified = ((JArray)json["unverified"]).Select(p => UInt256.Parse(p.AsString())).ToList(); return rawMemPool; } } diff --git a/src/RpcClient/Models/RpcRequest.cs b/src/RpcClient/Models/RpcRequest.cs index 1970adedb..6165e97f6 100644 --- a/src/RpcClient/Models/RpcRequest.cs +++ b/src/RpcClient/Models/RpcRequest.cs @@ -5,9 +5,9 @@ namespace Neo.Network.RPC.Models { public class RpcRequest { - public int Id { get; set; } + public JObject Id { get; set; } - public string Jsonrpc { get; set; } + public string JsonRpc { get; set; } public string Method { get; set; } @@ -17,8 +17,8 @@ public static RpcRequest FromJson(JObject json) { return new RpcRequest { - Id = (int)json["id"].AsNumber(), - Jsonrpc = json["jsonrpc"].AsString(), + Id = json["id"], + JsonRpc = json["jsonrpc"].AsString(), Method = json["method"].AsString(), Params = ((JArray)json["params"]).ToArray() }; @@ -28,7 +28,7 @@ public JObject ToJson() { var json = new JObject(); json["id"] = Id; - json["jsonrpc"] = Jsonrpc; + json["jsonrpc"] = JsonRpc; json["method"] = Method; json["params"] = new JArray(Params); return json; diff --git a/src/RpcClient/Models/RpcResponse.cs b/src/RpcClient/Models/RpcResponse.cs index e4ebcaed1..e8a62af14 100644 --- a/src/RpcClient/Models/RpcResponse.cs +++ b/src/RpcClient/Models/RpcResponse.cs @@ -4,9 +4,9 @@ namespace Neo.Network.RPC.Models { public class RpcResponse { - public int? Id { get; set; } + public JObject Id { get; set; } - public string Jsonrpc { get; set; } + public string JsonRpc { get; set; } public RpcResponseError Error { get; set; } @@ -18,8 +18,8 @@ public static RpcResponse FromJson(JObject json) { var response = new RpcResponse { - Id = (int?)json["id"]?.AsNumber(), - Jsonrpc = json["jsonrpc"].AsString(), + Id = json["id"], + JsonRpc = json["jsonrpc"].AsString(), Result = json["result"] }; @@ -35,8 +35,8 @@ public JObject ToJson() { var json = new JObject(); json["id"] = Id; - json["jsonrpc"] = Jsonrpc; - json["error"] = Error.ToJson(); + json["jsonrpc"] = JsonRpc; + json["error"] = Error?.ToJson(); json["result"] = Result; return json; } diff --git a/src/RpcClient/Models/RpcTransaction.cs b/src/RpcClient/Models/RpcTransaction.cs index 48b1e19bd..6b5a0743c 100644 --- a/src/RpcClient/Models/RpcTransaction.cs +++ b/src/RpcClient/Models/RpcTransaction.cs @@ -10,9 +10,9 @@ public class RpcTransaction public UInt256 BlockHash { get; set; } - public int? Confirmations { get; set; } + public uint? Confirmations { get; set; } - public uint? BlockTime { get; set; } + public ulong? BlockTime { get; set; } public VMState? VMState { get; set; } @@ -26,7 +26,7 @@ public JObject ToJson() json["blocktime"] = BlockTime; if (VMState != null) { - json["vmState"] = VMState; + json["vm_state"] = VMState; } } return json; @@ -39,9 +39,9 @@ public static RpcTransaction FromJson(JObject json) if (json["confirmations"] != null) { transaction.BlockHash = UInt256.Parse(json["blockhash"].AsString()); - transaction.Confirmations = (int)json["confirmations"].AsNumber(); - transaction.BlockTime = (uint)json["blocktime"].AsNumber(); - transaction.VMState = json["vmState"]?.TryGetEnum(); + transaction.Confirmations = (uint)json["confirmations"].AsNumber(); + transaction.BlockTime = (ulong)json["blocktime"].AsNumber(); + transaction.VMState = json["vm_state"]?.TryGetEnum(); } return transaction; } diff --git a/src/RpcClient/Models/RpcTransferOut.cs b/src/RpcClient/Models/RpcTransferOut.cs new file mode 100644 index 000000000..6f582dfe3 --- /dev/null +++ b/src/RpcClient/Models/RpcTransferOut.cs @@ -0,0 +1,34 @@ +using Neo.IO.Json; +using Neo.Wallets; + +namespace Neo.Network.RPC.Models +{ + public class RpcTransferOut + { + public UInt160 Asset { get; set; } + + public UInt160 ScriptHash { get; set; } + + public string Value { get; set; } + + public JObject ToJson() + { + return new JObject + { + ["asset"] = Asset.ToString(), + ["value"] = Value, + ["address"] = ScriptHash.ToAddress(), + }; + } + + public static RpcTransferOut FromJson(JObject json) + { + return new RpcTransferOut + { + Asset = UInt160.Parse(json["asset"].AsString()), + Value = json["value"].AsString(), + ScriptHash = json["address"].AsString().ToScriptHash(), + }; + } + } +} diff --git a/src/RpcClient/Models/RpcVersion.cs b/src/RpcClient/Models/RpcVersion.cs index 8163875b6..11d831d74 100644 --- a/src/RpcClient/Models/RpcVersion.cs +++ b/src/RpcClient/Models/RpcVersion.cs @@ -15,20 +15,20 @@ public class RpcVersion public JObject ToJson() { JObject json = new JObject(); - json["topPort"] = TcpPort.ToString(); - json["wsPort"] = WsPort.ToString(); - json["nonce"] = Nonce.ToString(); - json["useragent"] = UserAgent; + json["tcp_port"] = TcpPort; + json["ws_port"] = WsPort; + json["nonce"] = Nonce; + json["user_agent"] = 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(); + version.TcpPort = (int)json["tcp_port"].AsNumber(); + version.WsPort = (int)json["ws_port"].AsNumber(); + version.Nonce = (uint)json["nonce"].AsNumber(); + version.UserAgent = json["user_agent"].AsString(); return version; } } diff --git a/src/RpcClient/Nep5API.cs b/src/RpcClient/Nep5API.cs index 619c75528..558c5453e 100644 --- a/src/RpcClient/Nep5API.cs +++ b/src/RpcClient/Nep5API.cs @@ -57,9 +57,9 @@ public string Symbol(UInt160 scriptHash) /// /// contract script hash /// - public uint Decimals(UInt160 scriptHash) + public byte Decimals(UInt160 scriptHash) { - return (uint)TestInvoke(scriptHash, "decimals").Stack.Single().ToStackItem().GetBigInteger(); + return (byte)TestInvoke(scriptHash, "decimals").Stack.Single().ToStackItem().GetBigInteger(); } /// @@ -90,7 +90,7 @@ public RpcNep5TokenInfo GetTokenInfo(UInt160 scriptHash) { Name = result[0].ToStackItem().GetString(), Symbol = result[1].ToStackItem().GetString(), - Decimals = (uint)result[2].ToStackItem().GetBigInteger(), + Decimals = (byte)result[2].ToStackItem().GetBigInteger(), TotalSupply = result[3].ToStackItem().GetBigInteger() }; } @@ -102,16 +102,15 @@ public RpcNep5TokenInfo GetTokenInfo(UInt160 scriptHash) /// from KeyPair /// to account script hash /// transfer amount - /// netwotk fee, set to be 0 will auto calculate the least fee /// - public Transaction CreateTransferTx(UInt160 scriptHash, KeyPair fromKey, UInt160 to, BigInteger amount, long networkFee = 0) + public Transaction CreateTransferTx(UInt160 scriptHash, KeyPair fromKey, UInt160 to, BigInteger amount) { var sender = Contract.CreateSignatureRedeemScript(fromKey.PublicKey).ToScriptHash(); Cosigner[] cosigners = new[] { new Cosigner { Scopes = WitnessScope.CalledByEntry, Account = sender } }; byte[] script = scriptHash.MakeScript("transfer", sender, to, amount); Transaction tx = new TransactionManager(rpcClient, sender) - .MakeTransaction(script, null, cosigners, networkFee) + .MakeTransaction(script, null, cosigners) .AddSignature(fromKey) .Sign() .Tx; diff --git a/src/RpcClient/RpcClient.cs b/src/RpcClient/RpcClient.cs index 2bb4fcdec..057cc9556 100644 --- a/src/RpcClient/RpcClient.cs +++ b/src/RpcClient/RpcClient.cs @@ -7,6 +7,8 @@ using System.Collections.Generic; using System.Linq; using System.Net.Http; +using System.Net.Http.Headers; +using System.Numerics; using System.Text; using System.Threading.Tasks; @@ -17,11 +19,16 @@ namespace Neo.Network.RPC /// public class RpcClient : IDisposable { - private readonly HttpClient httpClient; + private HttpClient httpClient; - public RpcClient(string url) + public RpcClient(string url, string rpcUser = default, string rpcPass = default) { httpClient = new HttpClient() { BaseAddress = new Uri(url) }; + if (!string.IsNullOrEmpty(rpcUser) && !string.IsNullOrEmpty(rpcPass)) + { + string token = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{rpcUser}:{rpcPass}")); + httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", token); + } } public RpcClient(HttpClient client) @@ -29,10 +36,28 @@ public RpcClient(HttpClient client) httpClient = client; } + #region IDisposable Support + private bool disposedValue = false; // To detect redundant calls + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + httpClient?.Dispose(); + } + + httpClient = null; + disposedValue = true; + } + } + public void Dispose() { - httpClient?.Dispose(); + Dispose(true); } + #endregion public async Task SendAsync(RpcRequest request) { @@ -67,13 +92,15 @@ public virtual JObject RpcSend(string method, params JObject[] paraArgs) var request = new RpcRequest { Id = 1, - Jsonrpc = "2.0", + JsonRpc = "2.0", Method = method, - Params = paraArgs.Select(p => p).ToArray() + Params = paraArgs }; return Send(request).Result; } + #region Blockchain + /// /// Returns the hash of the tallest block in the main chain. /// @@ -150,17 +177,9 @@ public RpcBlockHeader GetBlockHeader(string hashOrIndex) /// /// Returns the system fees of the block, based on the specified index. /// - public string GetBlockSysFee(int height) + public BigInteger 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(); + return BigInteger.Parse(RpcSend("getblocksysfee", height).AsString()); } /// @@ -168,15 +187,7 @@ public int GetConnectionCount() /// 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")); + return RpcContractState.FromJson(RpcSend("getcontractstate", hash)).ContractState; } /// @@ -199,34 +210,39 @@ public RpcRawMemPool GetRawMempoolBoth() /// /// Returns the corresponding transaction information, based on the specified hash value. /// - public string GetRawTransactionHex(string txid) + public string GetRawTransactionHex(string txHash) { - return RpcSend("getrawtransaction", txid).AsString(); + return RpcSend("getrawtransaction", txHash).AsString(); } /// /// Returns the corresponding transaction information, based on the specified hash value. /// verbose = true /// - public RpcTransaction GetRawTransaction(string txid) + public RpcTransaction GetRawTransaction(string txHash) { - return RpcTransaction.FromJson(RpcSend("getrawtransaction", txid, true)); + return RpcTransaction.FromJson(RpcSend("getrawtransaction", txHash, true)); } /// - /// Returns the stored value, according to the contract script hash and the stored key. + /// Returns the stored value, according to the contract script hash (or Id) and the stored key. /// - public string GetStorage(string script_hash, string key) + public string GetStorage(string scriptHashOrId, string key) { - return RpcSend("getstorage", script_hash, key).AsString(); + if (int.TryParse(scriptHashOrId, out int id)) + { + return RpcSend("getstorage", id, key).AsString(); + } + + return RpcSend("getstorage", scriptHashOrId, key).AsString(); } /// /// Returns the block index in which the transaction is found. /// - public uint GetTransactionHeight(string txid) + public uint GetTransactionHeight(string txHash) { - return uint.Parse(RpcSend("gettransactionheight", txid).AsString()); + return uint.Parse(RpcSend("gettransactionheight", txHash).AsString()); } /// @@ -237,6 +253,26 @@ public RpcValidator[] GetValidators() return ((JArray)RpcSend("getvalidators")).Select(p => RpcValidator.FromJson(p)).ToArray(); } + #endregion Blockchain + + #region Node + + /// + /// Gets the current number of connections for the node. + /// + public int GetConnectionCount() + { + return (int)RpcSend("getconnectioncount").AsNumber(); + } + + /// + /// Gets the list of nodes that the node is currently connected/disconnected from. + /// + public RpcPeers GetPeers() + { + return RpcPeers.FromJson(RpcSend("getpeers")); + } + /// /// Returns the version information about the queried node. /// @@ -245,13 +281,41 @@ public RpcVersion GetVersion() return RpcVersion.FromJson(RpcSend("getversion")); } + /// + /// Broadcasts a serialized transaction over the NEO network. + /// + public UInt256 SendRawTransaction(byte[] rawTransaction) + { + return UInt256.Parse(RpcSend("sendrawtransaction", rawTransaction.ToHexString())["hash"].AsString()); + } + + /// + /// Broadcasts a transaction over the NEO network. + /// + public UInt256 SendRawTransaction(Transaction transaction) + { + return SendRawTransaction(transaction.ToArray()); + } + + /// + /// Broadcasts a serialized block over the NEO network. + /// + public UInt256 SubmitBlock(byte[] block) + { + return UInt256.Parse(RpcSend("submitblock", block.ToHexString())["hash"].AsString()); + } + + #endregion Node + + #region SmartContract + /// /// 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) + public RpcInvokeResult InvokeFunction(string scriptHash, string operation, RpcStack[] stacks) { - return RpcInvokeResult.FromJson(RpcSend("invokefunction", address, function, stacks.Select(p => p.ToJson()).ToArray())); + return RpcInvokeResult.FromJson(RpcSend("invokefunction", scriptHash, operation, stacks.Select(p => p.ToJson()).ToArray())); } /// @@ -268,6 +332,10 @@ public RpcInvokeResult InvokeScript(byte[] script, params UInt160[] scriptHashes return RpcInvokeResult.FromJson(RpcSend("invokescript", parameters.ToArray())); } + #endregion SmartContract + + #region Utilities + /// /// Returns a list of plugins loaded by the node. /// @@ -277,35 +345,156 @@ public RpcPlugin[] ListPlugins() } /// - /// Broadcasts a serialized transaction over the NEO network. + /// Verifies that the address is a correct NEO address. /// - public bool SendRawTransaction(byte[] rawTransaction) + public RpcValidateAddressResult ValidateAddress(string address) { - return RpcSend("sendrawtransaction", rawTransaction.ToHexString()).AsBoolean(); + return RpcValidateAddressResult.FromJson(RpcSend("validateaddress", address)); } + #endregion Utilities + + #region Wallet + /// - /// Broadcasts a transaction over the NEO network. + /// Close the wallet opened by RPC. /// - public bool SendRawTransaction(Transaction transaction) + public bool CloseWallet() { - return SendRawTransaction(transaction.ToArray()); + return RpcSend("closewallet").AsBoolean(); } /// - /// Broadcasts a serialized block over the NEO network. + /// Exports the private key of the specified address. /// - public bool SubmitBlock(byte[] block) + public string DumpPrivKey(string address) { - return RpcSend("submitblock", block.ToHexString()).AsBoolean(); + return RpcSend("dumpprivkey", address).AsString(); } /// - /// Verifies that the address is a correct NEO address. + /// Returns the balance of the corresponding asset in the wallet, based on the specified asset Id. + /// This method applies to assets that conform to NEP-5 standards. /// - public RpcValidateAddressResult ValidateAddress(string address) + /// new address as string + public BigDecimal GetBalance(string assetId) { - return RpcValidateAddressResult.FromJson(RpcSend("validateaddress", address)); + byte decimals = new Nep5API(this).Decimals(UInt160.Parse(assetId)); + BigInteger balance = BigInteger.Parse(RpcSend("getbalance", assetId)["balance"].AsString()); + return new BigDecimal(balance, decimals); + } + + /// + /// Creates a new account in the wallet opened by RPC. + /// + public string GetNewAddress() + { + return RpcSend("getnewaddress").AsString(); + } + + /// + /// Gets the amount of unclaimed GAS in the wallet. + /// + public BigInteger GetUnclaimedGas() + { + return BigInteger.Parse(RpcSend("getunclaimedgas").AsString()); + } + + /// + /// Imports the private key to the wallet. + /// + public RpcAccount ImportPrivKey(string wif) + { + return RpcAccount.FromJson(RpcSend("importprivkey", wif)); + } + + /// + /// Lists all the accounts in the current wallet. + /// + public List ListAddress() + { + return ((JArray)RpcSend("listaddress")).Select(p => RpcAccount.FromJson(p)).ToList(); } + + /// + /// Open wallet file in the provider's machine. + /// By default, this method is disabled by RpcServer config.json. + /// + public bool OpenWallet(string path, string password) + { + return RpcSend("openwallet", path, password).AsBoolean(); + } + + /// + /// Transfer from the specified address to the destination address. + /// + /// This function returns Signed Transaction JSON if successful, ContractParametersContext JSON if signing failed. + public JObject SendFrom(string assetId, string fromAddress, string toAddress, string amount) + { + return RpcSend("sendfrom", assetId, fromAddress, toAddress, amount); + } + + /// + /// Bulk transfer order, and you can specify a sender address. + /// + /// This function returns Signed Transaction JSON if successful, ContractParametersContext JSON if signing failed. + public JObject SendMany(string fromAddress, IEnumerable outputs) + { + var parameters = new List(); + if (!string.IsNullOrEmpty(fromAddress)) + { + parameters.Add(fromAddress); + } + parameters.Add(outputs.Select(p => p.ToJson()).ToArray()); + + return RpcSend("sendmany", paraArgs: parameters.ToArray()); + } + + /// + /// Transfer asset from the wallet to the destination address. + /// + /// This function returns Signed Transaction JSON if successful, ContractParametersContext JSON if signing failed. + public JObject SendToAddress(string assetId, string address, string amount) + { + return RpcSend("sendtoaddress", assetId, address, amount); + } + + #endregion Utilities + + #region Plugins + + /// + /// Returns the contract log based on the specified txHash. The complete contract logs are stored under the ApplicationLogs directory. + /// This method is provided by the plugin ApplicationLogs. + /// + public RpcApplicationLog GetApplicationLog(string txHash) + { + return RpcApplicationLog.FromJson(RpcSend("getapplicationlog", txHash)); + } + + /// + /// Returns all the NEP-5 transaction information occurred in the specified address. + /// This method is provided by the plugin RpcNep5Tracker. + /// + /// The address to query the transaction information. + /// The start block Timestamp, default to seven days before UtcNow + /// The end block Timestamp, default to UtcNow + public RpcNep5Transfers GetNep5Transfers(string address, ulong? startTimestamp = default, ulong? endTimestamp = default) + { + startTimestamp ??= 0; + endTimestamp ??= DateTime.UtcNow.ToTimestampMS(); + return RpcNep5Transfers.FromJson(RpcSend("getnep5transfers", address, startTimestamp, endTimestamp)); + } + + /// + /// Returns the balance of all NEP-5 assets in the specified address. + /// This method is provided by the plugin RpcNep5Tracker. + /// + public RpcNep5Balances GetNep5Balances(string address) + { + return RpcNep5Balances.FromJson(RpcSend("getnep5balances", address)); + } + + #endregion Plugins } } diff --git a/src/RpcClient/RpcClient.csproj b/src/RpcClient/RpcClient.csproj index 2f4aed74b..a6d1ea769 100644 --- a/src/RpcClient/RpcClient.csproj +++ b/src/RpcClient/RpcClient.csproj @@ -1,7 +1,7 @@ - 3.0.0-CI00847 + 3.0.0-CI00863 netstandard2.1 Neo.Network.RPC The Neo Project @@ -14,7 +14,7 @@ - + diff --git a/src/RpcClient/TransactionManager.cs b/src/RpcClient/TransactionManager.cs index 8d345f651..43d182c8b 100644 --- a/src/RpcClient/TransactionManager.cs +++ b/src/RpcClient/TransactionManager.cs @@ -6,6 +6,7 @@ using Neo.SmartContract.Native; using Neo.Wallets; using System; +using System.Collections.Generic; using System.Linq; namespace Neo.Network.RPC @@ -20,11 +21,18 @@ public class TransactionManager private readonly Nep5API nep5API; private readonly UInt160 sender; + private class SignItem { public Contract Contract; public HashSet KeyPairs; } + /// /// The Transaction context to manage the witnesses /// private ContractParametersContext context; + /// + /// This container stores the keys for sign the transaction + /// + private List signStore; + /// /// The Transaction managed by this class /// @@ -49,9 +57,8 @@ public TransactionManager(RpcClient rpc, UInt160 sender) /// Transaction Script /// Transaction Attributes /// Transaction Cosigners - /// Transaction NetworkFee, will set to estimate value(with only basic signatures) when networkFee is 0 /// - public TransactionManager MakeTransaction(byte[] script, TransactionAttribute[] attributes = null, Cosigner[] cosigners = null, long networkFee = 0) + public TransactionManager MakeTransaction(byte[] script, TransactionAttribute[] attributes = null, Cosigner[] cosigners = null) { var random = new Random(); uint height = rpcClient.GetBlockCount() - 1; @@ -82,21 +89,16 @@ public TransactionManager MakeTransaction(byte[] script, TransactionAttribute[] } context = new ContractParametersContext(Tx); + signStore = new List(); - // set networkfee to estimate value when networkFee is 0 - Tx.NetworkFee = networkFee == 0 ? CalculateNetworkFee(true) : networkFee; - - var gasBalance = nep5API.BalanceOf(NativeContract.GAS.Hash, sender); - if (gasBalance >= Tx.SystemFee + Tx.NetworkFee) return this; - throw new InvalidOperationException($"Insufficient GAS in address: {sender.ToAddress()}"); + return this; } /// /// Calculate NetworkFee /// - /// assuming the witnesses are basic Signature Contract if set to true /// - private long CalculateNetworkFee(bool isEstimate = false) + private long CalculateNetworkFee() { long networkFee = 0; UInt160[] hashes = Tx.GetScriptHashesForVerifying(null); @@ -104,30 +106,19 @@ private long CalculateNetworkFee(bool isEstimate = false) foreach (UInt160 hash in hashes) { byte[] witness_script = null; - if (isEstimate) - { - // assuming the witnesses are basic Signature Contract - var dummyKey = new byte[32]; - dummyKey[31] = 0x01; - KeyPair one = new KeyPair(dummyKey); - witness_script = Contract.CreateSignatureRedeemScript(one.PublicKey); - } - else + + // calculate NetworkFee + witness_script = signStore.FirstOrDefault(p => p.Contract.ScriptHash == hash)?.Contract?.Script; + if (witness_script is null || witness_script.Length == 0) { - // calculate NetworkFee with context items - witness_script = context.GetScript(hash); - if (witness_script is null || witness_script.Length == 0) + try { - try - { - witness_script = rpcClient.GetContractState(hash.ToString())?.Script; - } - catch { } + witness_script = rpcClient.GetContractState(hash.ToString())?.Script; } - - if (witness_script is null) continue; + catch { } } + if (witness_script is null) continue; networkFee += Wallet.CalculateNetworkFee(witness_script, ref size); } networkFee += size * policyAPI.GetFeePerByte(); @@ -142,13 +133,7 @@ private long CalculateNetworkFee(bool isEstimate = false) public TransactionManager AddSignature(KeyPair key) { var contract = Contract.CreateSignatureContract(key.PublicKey); - - byte[] signature = Tx.Sign(key); - if (!context.AddSignature(contract, key.PublicKey, signature)) - { - throw new Exception("AddSignature failed!"); - } - + AddSignItem(contract, key); return this; } @@ -161,14 +146,26 @@ public TransactionManager AddSignature(KeyPair key) public TransactionManager AddMultiSig(KeyPair key, int m, params ECPoint[] publicKeys) { Contract contract = Contract.CreateMultiSigContract(m, publicKeys); + AddSignItem(contract, key); + return this; + } - byte[] signature = Tx.Sign(key); - if (!context.AddSignature(contract, key.PublicKey, signature)) + private void AddSignItem(Contract contract, KeyPair key) + { + if (!Tx.GetScriptHashesForVerifying(null).Contains(contract.ScriptHash)) { - throw new Exception("AddMultiSig failed!"); + throw new Exception($"Add SignItem error: Mismatch ScriptHash ({contract.ScriptHash.ToString()})"); } - return this; + SignItem item = signStore.FirstOrDefault(p => p.Contract.ScriptHash == contract.ScriptHash); + if (item is null) + { + signStore.Add(new SignItem { Contract = contract, KeyPairs = new HashSet { key } }); + } + else if (!item.KeyPairs.Contains(key)) + { + item.KeyPairs.Add(key); + } } /// @@ -201,19 +198,28 @@ public TransactionManager AddWitness(UInt160 scriptHash, params object[] paramet /// public TransactionManager Sign() { + // Calculate NetworkFee + Tx.NetworkFee = CalculateNetworkFee(); + var gasBalance = nep5API.BalanceOf(NativeContract.GAS.Hash, sender); + if (gasBalance < Tx.SystemFee + Tx.NetworkFee) + throw new InvalidOperationException($"Insufficient GAS in address: {sender.ToAddress()}"); + + // Sign with signStore + foreach (var item in signStore) + foreach (var key in item.KeyPairs) + { + byte[] signature = Tx.Sign(key); + if (!context.AddSignature(item.Contract, key.PublicKey, signature)) + { + throw new Exception("AddSignature failed!"); + } + } + // Verify witness count if (!context.Completed) { throw new Exception($"Please add signature or witness first!"); } - - // Calculate NetworkFee - long leastNetworkFee = CalculateNetworkFee(); - if (Tx.NetworkFee < leastNetworkFee) - { - throw new InvalidOperationException("Insufficient NetworkFee"); - } - Tx.Witnesses = context.GetWitnesses(); return this; } diff --git a/src/RpcClient/Utility.cs b/src/RpcClient/Utility.cs index 198c31dd9..bf7c95fe2 100644 --- a/src/RpcClient/Utility.cs +++ b/src/RpcClient/Utility.cs @@ -46,7 +46,7 @@ public static KeyPair GetKeyPair(string key) /// Parse address, scripthash or public key string to UInt160 /// /// account address, scripthash or public key string - /// Example: address ("AV556nYUwyJKNv8Xy7hVMLQnkmKPukw6x5"), scripthash ("0x6a38cd693b615aea24dd00de12a9f5836844da91"), public key ("02f9ec1fd0a98796cf75b586772a4ddd41a0af07a1dbdf86a7238f74fb72503575") + /// Example: address ("Ncm9TEzrp8SSer6Wa3UCSLTRnqzwVhCfuE"), scripthash ("0xb0a31817c80ad5f87b6ed390ecb3f9d312f7ceb8"), public key ("02f9ec1fd0a98796cf75b586772a4ddd41a0af07a1dbdf86a7238f74fb72503575") /// public static UInt160 GetScriptHash(string account) { @@ -82,7 +82,7 @@ internal static BigInteger ToBigInteger(this decimal amount, uint decimals) var (numerator, denominator) = Fraction(amount); if (factor < denominator) { - throw new OverflowException("The decimal places is too long."); + throw new ArgumentException("The decimal places is too long."); } BigInteger res = factor * numerator / denominator; diff --git a/src/RpcClient/WalletAPI.cs b/src/RpcClient/WalletAPI.cs index e1ab3b777..afb44e6d4 100644 --- a/src/RpcClient/WalletAPI.cs +++ b/src/RpcClient/WalletAPI.cs @@ -34,7 +34,7 @@ public WalletAPI(RpcClient rpc) /// Get unclaimed gas with address, scripthash or public key string /// /// address, scripthash or public key string - /// Example: address ("AV556nYUwyJKNv8Xy7hVMLQnkmKPukw6x5"), scripthash ("0x6a38cd693b615aea24dd00de12a9f5836844da91"), public key ("02f9ec1fd0a98796cf75b586772a4ddd41a0af07a1dbdf86a7238f74fb72503575") + /// Example: address ("Ncm9TEzrp8SSer6Wa3UCSLTRnqzwVhCfuE"), scripthash ("0xb0a31817c80ad5f87b6ed390ecb3f9d312f7ceb8"), public key ("02f9ec1fd0a98796cf75b586772a4ddd41a0af07a1dbdf86a7238f74fb72503575") /// public decimal GetUnclaimedGas(string account) { @@ -59,7 +59,7 @@ public decimal GetUnclaimedGas(UInt160 account) /// Get Neo Balance /// /// address, scripthash or public key string - /// Example: address ("AV556nYUwyJKNv8Xy7hVMLQnkmKPukw6x5"), scripthash ("0x6a38cd693b615aea24dd00de12a9f5836844da91"), public key ("02f9ec1fd0a98796cf75b586772a4ddd41a0af07a1dbdf86a7238f74fb72503575") + /// Example: address ("Ncm9TEzrp8SSer6Wa3UCSLTRnqzwVhCfuE"), scripthash ("0xb0a31817c80ad5f87b6ed390ecb3f9d312f7ceb8"), public key ("02f9ec1fd0a98796cf75b586772a4ddd41a0af07a1dbdf86a7238f74fb72503575") /// public uint GetNeoBalance(string account) { @@ -71,7 +71,7 @@ public uint GetNeoBalance(string account) /// Get Gas Balance /// /// address, scripthash or public key string - /// Example: address ("AV556nYUwyJKNv8Xy7hVMLQnkmKPukw6x5"), scripthash ("0x6a38cd693b615aea24dd00de12a9f5836844da91"), public key ("02f9ec1fd0a98796cf75b586772a4ddd41a0af07a1dbdf86a7238f74fb72503575") + /// Example: address ("Ncm9TEzrp8SSer6Wa3UCSLTRnqzwVhCfuE"), scripthash ("0xb0a31817c80ad5f87b6ed390ecb3f9d312f7ceb8"), public key ("02f9ec1fd0a98796cf75b586772a4ddd41a0af07a1dbdf86a7238f74fb72503575") /// public decimal GetGasBalance(string account) { @@ -84,7 +84,7 @@ public decimal GetGasBalance(string account) /// /// token script hash, Example: "0x43cf98eddbe047e198a3e5d57006311442a0ca15"(NEO) /// address, scripthash or public key string - /// Example: address ("AV556nYUwyJKNv8Xy7hVMLQnkmKPukw6x5"), scripthash ("0x6a38cd693b615aea24dd00de12a9f5836844da91"), public key ("02f9ec1fd0a98796cf75b586772a4ddd41a0af07a1dbdf86a7238f74fb72503575") + /// Example: address ("Ncm9TEzrp8SSer6Wa3UCSLTRnqzwVhCfuE"), scripthash ("0xb0a31817c80ad5f87b6ed390ecb3f9d312f7ceb8"), public key ("02f9ec1fd0a98796cf75b586772a4ddd41a0af07a1dbdf86a7238f74fb72503575") /// public BigInteger GetTokenBalance(string tokenHash, string account) { @@ -124,14 +124,13 @@ public Transaction ClaimGas(KeyPair keyPair) /// /// Transfer NEP5 token balance, with common data types /// - /// nep5 token script hash, Example: scripthash ("0x6a38cd693b615aea24dd00de12a9f5836844da91") + /// nep5 token script hash, Example: scripthash ("0xb0a31817c80ad5f87b6ed390ecb3f9d312f7ceb8") /// wif or private key /// Example: WIF ("KyXwTh1hB76RRMquSvnxZrJzQx7h9nQP2PCRL38v6VDb5ip3nf1p"), PrivateKey ("450d6c2a04b5b470339a745427bae6828400cf048400837d73c415063835e005") /// address or account script hash /// token amount - /// netwotk fee, set to be 0 will auto calculate the least fee /// - public Transaction Transfer(string tokenHash, string fromKey, string toAddress, decimal amount, decimal networkFee = 0) + public Transaction Transfer(string tokenHash, string fromKey, string toAddress, decimal amount) { UInt160 scriptHash = Utility.GetScriptHash(tokenHash); var decimals = nep5API.Decimals(scriptHash); @@ -139,22 +138,20 @@ public Transaction Transfer(string tokenHash, string fromKey, string toAddress, KeyPair from = Utility.GetKeyPair(fromKey); UInt160 to = Utility.GetScriptHash(toAddress); BigInteger amountInteger = amount.ToBigInteger(decimals); - BigInteger networkFeeInteger = networkFee.ToBigInteger(NativeContract.GAS.Decimals); - return Transfer(scriptHash, from, to, amountInteger, (long)networkFeeInteger); + return Transfer(scriptHash, from, to, amountInteger); } /// /// Transfer NEP5 token balance /// /// contract script hash - /// from KeyPair + /// from KeyPair /// to account script hash - /// transfer amount - /// netwotk fee, set to be 0 will auto calculate the least fee + /// transfer amount /// - public Transaction Transfer(UInt160 scriptHash, KeyPair from, UInt160 to, BigInteger amountInteger, BigInteger networkFeeInteger = default) + public Transaction Transfer(UInt160 scriptHash, KeyPair from, UInt160 to, BigInteger amountInteger) { - Transaction transaction = nep5API.CreateTransferTx(scriptHash, from, to, amountInteger, (long)networkFeeInteger); + Transaction transaction = nep5API.CreateTransferTx(scriptHash, from, to, amountInteger); rpcClient.SendRawTransaction(transaction); return transaction; } diff --git a/src/RpcServer/RpcServer.Blockchain.cs b/src/RpcServer/RpcServer.Blockchain.cs index c689757cc..0f5e9cd8a 100644 --- a/src/RpcServer/RpcServer.Blockchain.cs +++ b/src/RpcServer/RpcServer.Blockchain.cs @@ -157,7 +157,7 @@ private JObject GetRawTransaction(JArray _params) json["blockhash"] = header.Hash.ToString(); json["confirmations"] = Blockchain.Singleton.Height - header.Index + 1; json["blocktime"] = header.Timestamp; - json["vmState"] = txState.VMState; + json["vm_state"] = txState.VMState; } return json; } diff --git a/src/RpcServer/RpcServer.Node.cs b/src/RpcServer/RpcServer.Node.cs index 2b1018e8d..cdec3f405 100644 --- a/src/RpcServer/RpcServer.Node.cs +++ b/src/RpcServer/RpcServer.Node.cs @@ -59,10 +59,10 @@ private static JObject GetRelayResult(RelayResultReason reason, UInt256 hash) private JObject GetVersion(JArray _params) { JObject json = new JObject(); - json["tcpPort"] = LocalNode.Singleton.ListenerTcpPort; - json["wsPort"] = LocalNode.Singleton.ListenerWsPort; + json["tcp_port"] = LocalNode.Singleton.ListenerTcpPort; + json["ws_port"] = LocalNode.Singleton.ListenerWsPort; json["nonce"] = LocalNode.Nonce; - json["useragent"] = LocalNode.UserAgent; + json["user_agent"] = LocalNode.UserAgent; return json; } diff --git a/src/RpcServer/RpcServer.csproj b/src/RpcServer/RpcServer.csproj index 4b571e502..de60f6a7d 100644 --- a/src/RpcServer/RpcServer.csproj +++ b/src/RpcServer/RpcServer.csproj @@ -1,7 +1,7 @@ - 3.0.0-CI00847 + 3.0.0-CI00863 netstandard2.1 Neo.Plugins @@ -15,7 +15,7 @@ - + diff --git a/src/StatesDumper/StatesDumper.csproj b/src/StatesDumper/StatesDumper.csproj index b9c4144ec..4cf60681d 100644 --- a/src/StatesDumper/StatesDumper.csproj +++ b/src/StatesDumper/StatesDumper.csproj @@ -1,7 +1,7 @@ - 3.0.0-CI00847 + 3.0.0-CI00863 netstandard2.1 Neo.Plugins @@ -14,7 +14,7 @@ - + diff --git a/src/SystemLog/SystemLog.csproj b/src/SystemLog/SystemLog.csproj index 952373bc1..d5dc065c4 100644 --- a/src/SystemLog/SystemLog.csproj +++ b/src/SystemLog/SystemLog.csproj @@ -1,7 +1,7 @@ - 3.0.0-CI00847 + 3.0.0-CI00863 netstandard2.1 Neo.Plugins @@ -14,7 +14,7 @@ - + diff --git a/tests/Neo.Network.RPC.Tests/Models/UT_RpcBlock.cs b/tests/Neo.Network.RPC.Tests/Models/UT_RpcBlock.cs deleted file mode 100644 index 61887f784..000000000 --- a/tests/Neo.Network.RPC.Tests/Models/UT_RpcBlock.cs +++ /dev/null @@ -1,24 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Network.RPC.Models; - -namespace Neo.Network.RPC.Tests.Models -{ - [TestClass] - public class UT_RpcBlock - { - [TestMethod] - public void TestToJson() - { - var rpcBlock = new RpcBlock - { - Block = TestUtils.GetBlock(1), - Confirmations = 1, - NextBlockHash = UInt256.Zero - }; - var json = rpcBlock.ToJson(); - json["previousblockhash"].AsString().Should().Be("0x0000000000000000000000000000000000000000000000000000000000000000"); - json["confirmations"].AsNumber().Should().Be(1); - } - } -} diff --git a/tests/Neo.Network.RPC.Tests/Models/UT_RpcBlockHeader.cs b/tests/Neo.Network.RPC.Tests/Models/UT_RpcBlockHeader.cs deleted file mode 100644 index f756ab572..000000000 --- a/tests/Neo.Network.RPC.Tests/Models/UT_RpcBlockHeader.cs +++ /dev/null @@ -1,24 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Network.RPC.Models; - -namespace Neo.Network.RPC.Tests.Models -{ - [TestClass] - public class UT_RpcBlockHeader - { - [TestMethod] - public void TestToJson() - { - var rpcBlockHeader = new RpcBlockHeader - { - Header = TestUtils.GetHeader(), - Confirmations = 1, - NextBlockHash = UInt256.Zero - }; - var json = rpcBlockHeader.ToJson(); - json["previousblockhash"].AsString().Should().Be("0x0000000000000000000000000000000000000000000000000000000000000000"); - json["confirmations"].AsNumber().Should().Be(1); - } - } -} diff --git a/tests/Neo.Network.RPC.Tests/Models/UT_RpcNep5Balance.cs b/tests/Neo.Network.RPC.Tests/Models/UT_RpcNep5Balance.cs deleted file mode 100644 index 1aeda4998..000000000 --- a/tests/Neo.Network.RPC.Tests/Models/UT_RpcNep5Balance.cs +++ /dev/null @@ -1,66 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; -using Neo.Network.RPC.Models; -using System.Numerics; - -namespace Neo.Network.RPC.Tests.Models -{ - [TestClass] - public class UT_RpcNep5Balance - { - private RpcNep5Balance balance; - - [TestInitialize] - public void Setup() - { - balance = new RpcNep5Balance(); - } - - [TestMethod] - public void TestAssetHash() - { - balance.AssetHash = UInt160.Zero; - balance.AssetHash.Should().Be(UInt160.Zero); - } - - [TestMethod] - public void TestAmount() - { - balance.Amount = BigInteger.Zero; - balance.Amount.Should().Be(BigInteger.Zero); - } - - [TestMethod] - public void TestLastUpdatedBlock() - { - balance.LastUpdatedBlock = 0; - balance.LastUpdatedBlock.Should().Be(0); - } - - [TestMethod] - public void TestToJson() - { - balance.AssetHash = UInt160.Zero; - balance.Amount = BigInteger.Zero; - balance.LastUpdatedBlock = 0; - var json = balance.ToJson(); - json["asset_hash"].AsString().Should().Be("0x0000000000000000000000000000000000000000"); - json["amount"].AsNumber().Should().Be(0); - json["last_updated_block"].AsNumber().Should().Be(0); - } - - [TestMethod] - public void TestFromJson() - { - var json = new JObject(); - json["asset_hash"] = "0x0000000000000000000000000000000000000000"; - json["amount"] = "0"; - json["last_updated_block"] = "0"; - var rpcNep5Balance = RpcNep5Balance.FromJson(json); - rpcNep5Balance.AssetHash.Should().Be(UInt160.Zero); - rpcNep5Balance.Amount.Should().Be(BigInteger.Zero); - rpcNep5Balance.LastUpdatedBlock.Should().Be(0); - } - } -} diff --git a/tests/Neo.Network.RPC.Tests/Models/UT_RpcNep5Balances.cs b/tests/Neo.Network.RPC.Tests/Models/UT_RpcNep5Balances.cs deleted file mode 100644 index da0752222..000000000 --- a/tests/Neo.Network.RPC.Tests/Models/UT_RpcNep5Balances.cs +++ /dev/null @@ -1,66 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; -using Neo.Network.RPC.Models; -using System.Numerics; - -namespace Neo.Network.RPC.Tests.Models -{ - [TestClass] - public class UT_RpcNep5Balances - { - private RpcNep5Balances balances; - - [TestInitialize] - public void Setup() - { - balances = new RpcNep5Balances() - { - Address = "abc", - Balances = new RpcNep5Balance[] { - new RpcNep5Balance() - { - AssetHash = UInt160.Zero, - Amount = BigInteger.Zero, - LastUpdatedBlock = 0 - }, - new RpcNep5Balance() - { - AssetHash = UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01"), - Amount = new BigInteger(1), - LastUpdatedBlock = 1 - } - } - }; - } - - [TestMethod] - public void TestAddress() - { - balances.Address.Should().Be("abc"); - } - - [TestMethod] - public void TestBalances() - { - balances.Balances.Length.Should().Be(2); - } - - [TestMethod] - public void TestToJson() - { - var json = balances.ToJson(); - json["address"].AsString().Should().Be("abc"); - ((JArray)json["balance"]).Count.Should().Be(2); - } - - [TestMethod] - public void TestFromJson() - { - var json = balances.ToJson(); - var rpcNep5Balances = RpcNep5Balances.FromJson(json); - rpcNep5Balances.Address.Should().Be("abc"); - rpcNep5Balances.Balances.Length.Should().Be(2); - } - } -} diff --git a/tests/Neo.Network.RPC.Tests/Models/UT_RpcPeer.cs b/tests/Neo.Network.RPC.Tests/Models/UT_RpcPeer.cs deleted file mode 100644 index 838aad486..000000000 --- a/tests/Neo.Network.RPC.Tests/Models/UT_RpcPeer.cs +++ /dev/null @@ -1,23 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Network.RPC.Models; - -namespace Neo.Network.RPC.Tests.Models -{ - [TestClass] - public class UT_RpcPeer - { - [TestMethod] - public void TestToJson() - { - var rpcPeer = new RpcPeer() - { - Address = "abc", - Port = 800 - }; - var json = rpcPeer.ToJson(); - json["address"].AsString().Should().Be("abc"); - json["port"].AsNumber().Should().Be(800); - } - } -} diff --git a/tests/Neo.Network.RPC.Tests/Models/UT_RpcPeers.cs b/tests/Neo.Network.RPC.Tests/Models/UT_RpcPeers.cs deleted file mode 100644 index fba07ea70..000000000 --- a/tests/Neo.Network.RPC.Tests/Models/UT_RpcPeers.cs +++ /dev/null @@ -1,44 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; -using Neo.Network.RPC.Models; - -namespace Neo.Network.RPC.Tests.Models -{ - [TestClass] - public class UT_RpcPeers - { - [TestMethod] - public void TestToJson() - { - var rpcPeers = new RpcPeers() - { - Unconnected = new RpcPeer[] { - new RpcPeer() - { - Address = "Unconnected", - Port = 600 - } - }, - Bad = new RpcPeer[] { - new RpcPeer() - { - Address = "Bad", - Port = 700 - } - }, - Connected = new RpcPeer[] { - new RpcPeer() - { - Address = "Connected", - Port = 800 - } - } - }; - var json = rpcPeers.ToJson(); - ((JArray)json["unconnected"]).Count.Should().Be(1); - ((JArray)json["bad"]).Count.Should().Be(1); - ((JArray)json["connected"]).Count.Should().Be(1); - } - } -} diff --git a/tests/Neo.Network.RPC.Tests/Models/UT_RpcRawMemPool.cs b/tests/Neo.Network.RPC.Tests/Models/UT_RpcRawMemPool.cs deleted file mode 100644 index fbfd6719c..000000000 --- a/tests/Neo.Network.RPC.Tests/Models/UT_RpcRawMemPool.cs +++ /dev/null @@ -1,29 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Network.RPC.Models; - -namespace Neo.Network.RPC.Tests.Models -{ - [TestClass] - public class UT_RpcRawMemPool - { - [TestMethod] - public void TestToJson() - { - var pool = new RpcRawMemPool - { - Height = 1, - Verified = new string[] { - "a", "b" - }, - UnVerified = new string[] { - "c", "d" - } - }; - var json = pool.ToJson(); - json["height"].AsNumber().Should().Be(1); - json["verified"].AsString().Should().Be("a,b"); - json["unverified"].AsString().Should().Be("c,d"); - } - } -} diff --git a/tests/Neo.Network.RPC.Tests/Models/UT_RpcRequest.cs b/tests/Neo.Network.RPC.Tests/Models/UT_RpcRequest.cs deleted file mode 100644 index 3aec7e9d1..000000000 --- a/tests/Neo.Network.RPC.Tests/Models/UT_RpcRequest.cs +++ /dev/null @@ -1,31 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; -using Neo.Network.RPC.Models; - -namespace Neo.Network.RPC.Tests.Models -{ - [TestClass] - public class UT_RpcRequest - { - [TestMethod] - public void TestFromJson() - { - var req = new RpcRequest() - { - Id = 1, - Jsonrpc = "myrpc", - Method = "get", - Params = new JObject[] { - new JBoolean(true) - } - }; - var json = req.ToJson(); - var rpcRequest = RpcRequest.FromJson(json); - rpcRequest.Jsonrpc.Should().Be("myrpc"); - rpcRequest.Method.Should().Be("get"); - rpcRequest.Id.Should().Be(1); - rpcRequest.Params.Length.Should().Be(1); - } - } -} diff --git a/tests/Neo.Network.RPC.Tests/Models/UT_RpcResponse.cs b/tests/Neo.Network.RPC.Tests/Models/UT_RpcResponse.cs deleted file mode 100644 index 6265fb436..000000000 --- a/tests/Neo.Network.RPC.Tests/Models/UT_RpcResponse.cs +++ /dev/null @@ -1,34 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; -using Neo.Network.RPC.Models; - -namespace Neo.Network.RPC.Tests.Models -{ - [TestClass] - public class UT_RpcResponse - { - [TestMethod] - public void TestToJson() - { - var error = new RpcResponseError() - { - Code = 0, - Message = "msg", - Data = new JBoolean(true) - }; - var rep = new RpcResponse() - { - Id = 1, - Jsonrpc = "rpc", - Error = error, - Result = new JBoolean(true) - }; - var json = rep.ToJson(); - json["id"].AsNumber().Should().Be(1); - json["jsonrpc"].AsString().Should().Be("rpc"); - json["error"].AsString().Should().Be(error.ToJson().AsString()); - json["result"].AsBoolean().Should().BeTrue(); - } - } -} diff --git a/tests/Neo.Network.RPC.Tests/Models/UT_RpcVersion.cs b/tests/Neo.Network.RPC.Tests/Models/UT_RpcVersion.cs deleted file mode 100644 index e6ce63500..000000000 --- a/tests/Neo.Network.RPC.Tests/Models/UT_RpcVersion.cs +++ /dev/null @@ -1,27 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Network.RPC.Models; - -namespace Neo.Network.RPC.Tests.Models -{ - [TestClass] - public class UT_RpcVersion - { - [TestMethod] - public void TestToJson() - { - var version = new RpcVersion() - { - TcpPort = 800, - WsPort = 900, - Nonce = 1, - UserAgent = "agent" - }; - var json = version.ToJson(); - json["topPort"].AsNumber().Should().Be(800); - json["wsPort"].AsNumber().Should().Be(900); - json["nonce"].AsNumber().Should().Be(1); - json["useragent"].AsString().Should().Be("agent"); - } - } -} diff --git a/tests/Neo.Network.RPC.Tests/Neo.Network.RPC.Tests.csproj b/tests/Neo.Network.RPC.Tests/Neo.Network.RPC.Tests.csproj index 3c4da3a16..af5c7c416 100644 --- a/tests/Neo.Network.RPC.Tests/Neo.Network.RPC.Tests.csproj +++ b/tests/Neo.Network.RPC.Tests/Neo.Network.RPC.Tests.csproj @@ -19,4 +19,10 @@ + + + PreserveNewest + + + diff --git a/tests/Neo.Network.RPC.Tests/RpcTestCases.json b/tests/Neo.Network.RPC.Tests/RpcTestCases.json new file mode 100644 index 000000000..95c7b1409 --- /dev/null +++ b/tests/Neo.Network.RPC.Tests/RpcTestCases.json @@ -0,0 +1,1279 @@ +[ + { + "Name": "sendrawtransactionerror", + "Request": { + "jsonrpc": "2.0", + "method": "sendrawtransaction", + "params": [ "00b5077e7bb8cef712d3f9b3ec90d36e7bf8d50ac81718a3b000e1f5050000000080778e0600000000e31420000001b8cef712d3f9b3ec90d36e7bf8d50ac81718a3b0015c0300e40b54020000000c1400000000000000000000000000000000000000000c14b8cef712d3f9b3ec90d36e7bf8d50ac81718a3b013c00c087472616e736665720c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b5201420c4077a5ffdc41dce3b06418879a26bac0fcea06af4a833446c90f63a3e80b85d13e07ca9084989e7c7e4535187409058dc7233ba8ee4638593605134e2c2c8f3c9f290c2102f9ec1fd0a98796cf75b586772a4ddd41a0af07a1dbdf86a7238f74fb725035750b410a906ad4" ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "error": { + "code": -500, + "message": "InsufficientFunds", + "data": " at Neo.Plugins.RpcServer.GetRelayResult(RelayResultReason reason, UInt256 hash)\r\n at Neo.Network.RPC.Models.RpcServer.SendRawTransaction(JArray _params)\r\n at Neo.Network.RPC.Models.RpcServer.ProcessRequest(HttpContext context, JObject request)" + } + } + }, + { + "Name": "getbestblockhash", + "Request": { + "jsonrpc": "2.0", + "method": "getbestblockhash", + "params": [], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": "0x530de76326a8662d1b730ba4fbdf011051eabd142015587e846da42376adf35f" + } + }, + { + "Name": "getblockhex", + "Request": { + "jsonrpc": "2.0", + "method": "getblock", + "params": [ 0 ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": "0000000000000000000000000000000000000000000000000000000000000000000000002bbb6298fc7039330cdfd2e4dfbe976ee72c4cba6c16d68f0b49ab1bca685b7388ea19ef55010000000000009903b0c3d292988febe5f306a02f654ea2eb16290100011102001dac2b7c000000000000000000ca61e52e881d41374e640f819cd118cc153b21a7000000000000000000000000000000000000000000000541123e7fe801000111" + } + }, + { + "Name": "getblockhex", + "Request": { + "jsonrpc": "2.0", + "method": "getblock", + "params": [ "0x5bb05a3b857c9eaaf94dc33fa5b4a2278601eefcdb90703698972ef7d029e445" ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": "0000000000000000000000000000000000000000000000000000000000000000000000002bbb6298fc7039330cdfd2e4dfbe976ee72c4cba6c16d68f0b49ab1bca685b7388ea19ef55010000000000009903b0c3d292988febe5f306a02f654ea2eb16290100011102001dac2b7c000000000000000000ca61e52e881d41374e640f819cd118cc153b21a7000000000000000000000000000000000000000000000541123e7fe801000111" + } + }, + { + "Name": "getblock", + "Request": { + "jsonrpc": "2.0", + "method": "getblock", + "params": [ 0, true ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": { + "hash": "0x5bb05a3b857c9eaaf94dc33fa5b4a2278601eefcdb90703698972ef7d029e445", + "size": 171, + "version": 0, + "previousblockhash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "merkleroot": "0x735b68ca1bab490b8fd6166cba4c2ce76e97bedfe4d2df0c333970fc9862bb2b", + "time": 1468595301000, + "index": 0, + "nextconsensus": "NZs2zXSPuuv9ZF6TDGSWT1RBmE8rfGj7UW", + "witnesses": [ + { + "invocation": "", + "verification": "EQ==" + } + ], + "consensus_data": { + "primary": 0, + "nonce": "000000007c2bac1d" + }, + "tx": [ + { + "hash": "0xb13fd7940186896233272811e61b69a0c4b7dd576fdf3b89987c8cdee2d71e37", + "size": 57, + "version": 0, + "nonce": 0, + "sender": "NeN4xPMn4kHoj7G8Lciq9oorgLTvqt4qi1", + "sys_fee": "0", + "net_fee": "0", + "valid_until_block": 0, + "attributes": [], + "cosigners": [], + "script": "QRI+f+g=", + "witnesses": [ + { + "invocation": "", + "verification": "EQ==" + } + ] + } + ], + "confirmations": 2671, + "nextblockhash": "0x423173109798b038019b35129417b55cc4b5976ac79978dfab8ea2512d155f69" + } + } + }, + { + "Name": "getblock", + "Request": { + "jsonrpc": "2.0", + "method": "getblock", + "params": [ "0x5bb05a3b857c9eaaf94dc33fa5b4a2278601eefcdb90703698972ef7d029e445", true ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": { + "hash": "0x5bb05a3b857c9eaaf94dc33fa5b4a2278601eefcdb90703698972ef7d029e445", + "size": 171, + "version": 0, + "previousblockhash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "merkleroot": "0x735b68ca1bab490b8fd6166cba4c2ce76e97bedfe4d2df0c333970fc9862bb2b", + "time": 1468595301000, + "index": 0, + "nextconsensus": "NZs2zXSPuuv9ZF6TDGSWT1RBmE8rfGj7UW", + "witnesses": [ + { + "invocation": "", + "verification": "EQ==" + } + ], + "consensus_data": { + "primary": 0, + "nonce": "000000007c2bac1d" + }, + "tx": [ + { + "hash": "0xb13fd7940186896233272811e61b69a0c4b7dd576fdf3b89987c8cdee2d71e37", + "size": 57, + "version": 0, + "nonce": 0, + "sender": "NeN4xPMn4kHoj7G8Lciq9oorgLTvqt4qi1", + "sys_fee": "0", + "net_fee": "0", + "valid_until_block": 0, + "attributes": [], + "cosigners": [], + "script": "QRI+f+g=", + "witnesses": [ + { + "invocation": "", + "verification": "EQ==" + } + ] + } + ], + "confirmations": 2671, + "nextblockhash": "0x423173109798b038019b35129417b55cc4b5976ac79978dfab8ea2512d155f69" + } + } + }, + { + "Name": "getblockcount", + "Request": { + "jsonrpc": "2.0", + "method": "getblockcount", + "params": [], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": 2691 + } + }, + { + "Name": "getblockhash", + "Request": { + "jsonrpc": "2.0", + "method": "getblockhash", + "params": [ 0 ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": "0x5bb05a3b857c9eaaf94dc33fa5b4a2278601eefcdb90703698972ef7d029e445" + } + }, + { + "Name": "getblockheaderhex", + "Request": { + "jsonrpc": "2.0", + "method": "getblockheader", + "params": [ 0 ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": "0000000000000000000000000000000000000000000000000000000000000000000000002bbb6298fc7039330cdfd2e4dfbe976ee72c4cba6c16d68f0b49ab1bca685b7388ea19ef55010000000000009903b0c3d292988febe5f306a02f654ea2eb16290100011100" + } + }, + { + "Name": "getblockheaderhex", + "Request": { + "jsonrpc": "2.0", + "method": "getblockheader", + "params": [ "0x5bb05a3b857c9eaaf94dc33fa5b4a2278601eefcdb90703698972ef7d029e445" ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": "0000000000000000000000000000000000000000000000000000000000000000000000002bbb6298fc7039330cdfd2e4dfbe976ee72c4cba6c16d68f0b49ab1bca685b7388ea19ef55010000000000009903b0c3d292988febe5f306a02f654ea2eb16290100011100" + } + }, + { + "Name": "getblockheader", + "Request": { + "jsonrpc": "2.0", + "method": "getblockheader", + "params": [ 0, true ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": { + "hash": "0x5bb05a3b857c9eaaf94dc33fa5b4a2278601eefcdb90703698972ef7d029e445", + "size": 105, + "version": 0, + "previousblockhash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "merkleroot": "0x735b68ca1bab490b8fd6166cba4c2ce76e97bedfe4d2df0c333970fc9862bb2b", + "time": 1468595301000, + "index": 0, + "nextconsensus": "NZs2zXSPuuv9ZF6TDGSWT1RBmE8rfGj7UW", + "witnesses": [ + { + "invocation": "", + "verification": "EQ==" + } + ], + "confirmations": 2700, + "nextblockhash": "0x423173109798b038019b35129417b55cc4b5976ac79978dfab8ea2512d155f69" + } + } + }, + { + "Name": "getblockheader", + "Request": { + "jsonrpc": "2.0", + "method": "getblockheader", + "params": [ "0x5bb05a3b857c9eaaf94dc33fa5b4a2278601eefcdb90703698972ef7d029e445", true ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": { + "hash": "0x5bb05a3b857c9eaaf94dc33fa5b4a2278601eefcdb90703698972ef7d029e445", + "size": 105, + "version": 0, + "previousblockhash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "merkleroot": "0x735b68ca1bab490b8fd6166cba4c2ce76e97bedfe4d2df0c333970fc9862bb2b", + "time": 1468595301000, + "index": 0, + "nextconsensus": "NZs2zXSPuuv9ZF6TDGSWT1RBmE8rfGj7UW", + "witnesses": [ + { + "invocation": "", + "verification": "EQ==" + } + ], + "confirmations": 2700, + "nextblockhash": "0x423173109798b038019b35129417b55cc4b5976ac79978dfab8ea2512d155f69" + } + } + }, + { + "Name": "getblocksysfee", + "Request": { + "jsonrpc": "2.0", + "method": "getblocksysfee", + "params": [ 100 ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": "300000000" + } + }, + { + "Name": "getcontractstate", + "Request": { + "jsonrpc": "2.0", + "method": "getcontractstate", + "params": [ "0x8c23f196d8a1bfd103a9dcb1f9ccf0c611377d3b" ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": { + "id": -2, + "hash": "0x8c23f196d8a1bfd103a9dcb1f9ccf0c611377d3b", + "script": "QetD9PQ=", + "manifest": { + "groups": [], + "features": { + "storage": true, + "payable": false + }, + "abi": { + "hash": "0x8c23f196d8a1bfd103a9dcb1f9ccf0c611377d3b", + "entryPoint": { + "name": "Main", + "parameters": [ + { + "name": "operation", + "type": "String" + }, + { + "name": "args", + "type": "Array" + } + ], + "returnType": "Any" + }, + "methods": [ + { + "name": "getSysFeeAmount", + "parameters": [ + { + "name": "index", + "type": "Integer" + } + ], + "returnType": "Integer" + }, + { + "name": "name", + "parameters": [], + "returnType": "String" + }, + { + "name": "symbol", + "parameters": [], + "returnType": "String" + }, + { + "name": "decimals", + "parameters": [], + "returnType": "Integer" + }, + { + "name": "totalSupply", + "parameters": [], + "returnType": "Integer" + }, + { + "name": "balanceOf", + "parameters": [ + { + "name": "account", + "type": "Hash160" + } + ], + "returnType": "Integer" + }, + { + "name": "transfer", + "parameters": [ + { + "name": "from", + "type": "Hash160" + }, + { + "name": "to", + "type": "Hash160" + }, + { + "name": "amount", + "type": "Integer" + } + ], + "returnType": "Boolean" + }, + { + "name": "onPersist", + "parameters": [], + "returnType": "Boolean" + }, + { + "name": "supportedStandards", + "parameters": [], + "returnType": "Array" + } + ], + "events": [ + { + "name": "Transfer", + "parameters": [ + { + "name": "from", + "type": "Hash160" + }, + { + "name": "to", + "type": "Hash160" + }, + { + "name": "amount", + "type": "Integer" + } + ], + "returnType": "Signature" + } + ] + }, + "permissions": [ + { + "contract": "*", + "methods": "*" + } + ], + "trusts": [], + "safeMethods": [ + "getSysFeeAmount", + "name", + "symbol", + "decimals", + "totalSupply", + "balanceOf", + "supportedStandards" + ], + "extra": null + } + } + } + }, + { + "Name": "getrawmempool", + "Request": { + "jsonrpc": "2.0", + "method": "getrawmempool", + "params": [], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": [ "0x9786cce0dddb524c40ddbdd5e31a41ed1f6b5c8a683c122f627ca4a007a7cf4e", "0xb488ad25eb474f89d5ca3f985cc047ca96bc7373a6d3da8c0f192722896c1cd7" ] + } + }, + { + "Name": "getrawmempoolboth", + "Request": { + "jsonrpc": "2.0", + "method": "getrawmempool", + "params": [ true ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": { + "height": 2846, + "verified": [ "0x9786cce0dddb524c40ddbdd5e31a41ed1f6b5c8a683c122f627ca4a007a7cf4e" ], + "unverified": [ "0xb488ad25eb474f89d5ca3f985cc047ca96bc7373a6d3da8c0f192722896c1cd7" ] + } + } + }, + { + "Name": "getrawtransactionhex", + "Request": { + "jsonrpc": "2.0", + "method": "getrawtransaction", + "params": [ "0xd159457e12f6b0d4910fc6729f426be5d0c06f0f149d0836db9f634a5cf216fb" ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": "004cdec1396925aa554712439a9c613ba114efa3fac23ddbca00e1f50500000000466a130000000000311d200000016925aa554712439a9c613ba114efa3fac23ddbca015d030010a5d4e80000000c149903b0c3d292988febe5f306a02f654ea2eb16290c146925aa554712439a9c613ba114efa3fac23ddbca13c00c087472616e736665720c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b523901420c401f85b40d7fa12164aa1d4d18b06ca470f2c89572dc5b901ab1667faebb587cf536454b98a09018adac72376c5e7c5d164535155b763564347aa47b69aa01b3cc290c2103aa052fbcb8e5b33a4eefd662536f8684641f04109f1d5e69cdda6f084890286a0b410a906ad4" + } + }, + { + "Name": "getrawtransaction", + "Request": { + "jsonrpc": "2.0", + "method": "getrawtransaction", + "params": [ "0xd159457e12f6b0d4910fc6729f426be5d0c06f0f149d0836db9f634a5cf216fb", true ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": { + "hash": "0xd159457e12f6b0d4910fc6729f426be5d0c06f0f149d0836db9f634a5cf216fb", + "size": 272, + "version": 0, + "nonce": 969006668, + "sender": "NVVwFw6XyhtRCFQ8SpUTMdPyYt4Vd9A1XQ", + "sys_fee": "100000000", + "net_fee": "1272390", + "valid_until_block": 2104625, + "attributes": [], + "cosigners": [ + { + "account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", + "scopes": "CalledByEntry" + } + ], + "script": "AwAQpdToAAAADBSZA7DD0pKYj+vl8wagL2VOousWKQwUaSWqVUcSQ5qcYTuhFO+j+sI928oTwAwIdHJhbnNmZXIMFDt9NxHG8Mz5sdypA9G/odiW8SOMQWJ9W1I5", + "witnesses": [ + { + "invocation": "DEAfhbQNf6EhZKodTRiwbKRw8siVctxbkBqxZn+uu1h89TZFS5igkBitrHI3bF58XRZFNRVbdjVkNHqke2mqAbPM", + "verification": "DCEDqgUvvLjlszpO79ZiU2+GhGQfBBCfHV5pzdpvCEiQKGoLQQqQatQ=" + } + ], + "blockhash": "0xc1ed259e394c9cd93c1e0eb1e0f144c0d10da64861a24c0084f0d98270b698f1", + "confirmations": 643, + "blocktime": 1579417249620, + "vm_state": "HALT" + } + } + }, + { + "Name": "getstorage", + "Request": { + "jsonrpc": "2.0", + "method": "getstorage", + "params": [ "0x8c23f196d8a1bfd103a9dcb1f9ccf0c611377d3b", "146925aa554712439a9c613ba114efa3fac23ddbca" ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": "410121064c5d11a2a700" + } + }, + { + "Name": "getstorage", + "Request": { + "jsonrpc": "2.0", + "method": "getstorage", + "params": [ -2, "146925aa554712439a9c613ba114efa3fac23ddbca" ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": "410121064c5d11a2a700" + } + }, + { + "Name": "gettransactionheight", + "Request": { + "jsonrpc": "2.0", + "method": "gettransactionheight", + "params": [ "0xd159457e12f6b0d4910fc6729f426be5d0c06f0f149d0836db9f634a5cf216fb" ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": 2226 + } + }, + { + "Name": "getvalidators", + "Request": { + "jsonrpc": "2.0", + "method": "getvalidators", + "params": [], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": [ + { + "publickey": "03aa052fbcb8e5b33a4eefd662536f8684641f04109f1d5e69cdda6f084890286a", + "votes": "0", + "active": true + } + ] + } + }, + + + { + "Name": "getconnectioncount", + "Request": { + "jsonrpc": "2.0", + "method": "getconnectioncount", + "params": [], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": 0 + } + }, + { + "Name": "getpeers", + "Request": { + "jsonrpc": "2.0", + "method": "getpeers", + "params": [], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": { + "unconnected": [ + { + "address": "::ffff:70.73.16.236", + "port": 10333 + } + ], + "bad": [], + "connected": [ + { + "address": "::ffff:139.219.106.33", + "port": 10333 + }, + { + "address": "::ffff:47.88.53.224", + "port": 10333 + } + ] + } + } + }, + { + "Name": "getversion", + "Request": { + "jsonrpc": "2.0", + "method": "getversion", + "params": [], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": { + "tcp_port": 20333, + "ws_port": 20334, + "nonce": 592651621, + "user_agent": "/Neo:3.0.0-preview1/" + } + } + }, + { + "Name": "sendrawtransaction", + "Request": { + "jsonrpc": "2.0", + "method": "sendrawtransaction", + "params": [ "00142449186925aa554712439a9c613ba114efa3fac23ddbca00e1f50500000000a65a1300000000001d20200000016925aa554712439a9c613ba114efa3fac23ddbca01590200e1f5050c149903b0c3d292988febe5f306a02f654ea2eb16290c146925aa554712439a9c613ba114efa3fac23ddbca13c00c087472616e736665720c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b523901420c4076507ef5922e84aac57f09cb699ecbb10bc3d9c06ef908bed812c35fd2651531de27091f1e6e81566454fcc1c5f129d1051b08704c3fec3f6ed793563cfd30bb290c2103aa052fbcb8e5b33a4eefd662536f8684641f04109f1d5e69cdda6f084890286a0b410a906ad4" ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": { + "hash": "0x32cda8771aca33da2f7a022b33468fd658abd2521b58dd6ba1ffb89883903405" + } + } + }, + { + "Name": "submitblock", + "Request": { + "jsonrpc": "2.0", + "method": "submitblock", + "params": [ "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100010000" ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": { + "hash": "0xa11c9d14748f967178fe22fdcfb829354ae6ccb86824675e147cb128f16d8171" + } + } + }, + + + { + "Name": "invokefunction", + "Request": { + "jsonrpc": "2.0", + "method": "invokefunction", + "params": [ + "0x8c23f196d8a1bfd103a9dcb1f9ccf0c611377d3b", + "balanceOf", + [ + { + "type": "Hash160", + "value": "91b83e96f2a7c4fdf0c1688441ec61986c7cae26" + } + ] + ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": { + "script": "0c1426ae7c6c9861ec418468c1f0fdc4a7f2963eb89111c00c0962616c616e63654f660c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b52", + "state": "HALT", + "gas_consumed": "2007570", + "stack": [ + { + "type": "Integer", + "value": "0" + } + ], + "tx": "00d1eb88136925aa554712439a9c613ba114efa3fac23ddbca00e1f50500000000269f1200000000004520200000003e0c1426ae7c6c9861ec418468c1f0fdc4a7f2963eb89111c00c0962616c616e63654f660c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b5201420c40794c91299bba340ea2505c777d15ca898f75bcce686461066a2b8018cc1de114a122dcdbc77b447ac7db5fb1584f1533b164fbc8f30ddf5bd6acf016a125e983290c2103aa052fbcb8e5b33a4eefd662536f8684641f04109f1d5e69cdda6f084890286a0b410a906ad4" + } + } + }, + { + "Name": "invokescript", + "Request": { + "jsonrpc": "2.0", + "method": "invokescript", + "params": [ "10c30c046e616d650c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b5210c30c0673796d626f6c0c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b5210c30c08646563696d616c730c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b5210c30c0b746f74616c537570706c790c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b52" ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": { + "script": "10c30c046e616d650c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b5210c30c0673796d626f6c0c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b5210c30c08646563696d616c730c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b5210c30c0b746f74616c537570706c790c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b52", + "state": "HALT", + "gas_consumed": "5061560", + "stack": [ + { + "type": "ByteArray", + "value": "R0FT" + }, + { + "type": "ByteArray", + "value": "Z2Fz" + }, + { + "type": "Integer", + "value": "8" + }, + { + "type": "Integer", + "value": "3001101329992600" + } + ], + "tx": "00769d16556925aa554712439a9c613ba114efa3fac23ddbca00e1f505000000009e021400000000005620200000009910c30c046e616d650c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b5210c30c0673796d626f6c0c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b5210c30c08646563696d616c730c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b5210c30c0b746f74616c537570706c790c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b5201420c40c848d0fcbf5e6a820508242ea8b7ccbeed3caefeed5db570537279c2154f7cfd8b0d8f477f37f4e6ca912935b732684d57c455dff7aa525ad4ab000931f22208290c2103aa052fbcb8e5b33a4eefd662536f8684641f04109f1d5e69cdda6f084890286a0b410a906ad4" + } + } + }, + + + { + "Name": "listplugins", + "Request": { + "jsonrpc": "2.0", + "method": "listplugins", + "params": [], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": [ + { + "name": "ApplicationLogs", + "version": "3.0.0.0", + "interfaces": [ + "IPersistencePlugin" + ] + }, + { + "name": "LevelDBStore", + "version": "3.0.0.0", + "interfaces": [ + "IStoragePlugin" + ] + }, + { + "name": "RpcNep5Tracker", + "version": "3.0.0.0", + "interfaces": [ + "IPersistencePlugin" + ] + }, + { + "name": "RpcServer", + "version": "3.0.0.0", + "interfaces": [] + } + ] + } + }, + { + "Name": "validateaddress", + "Request": { + "jsonrpc": "2.0", + "method": "validateaddress", + "params": [ "NZs2zXSPuuv9ZF6TDGSWT1RBmE8rfGj7UW" ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": { + "address": "NZs2zXSPuuv9ZF6TDGSWT1RBmE8rfGj7UW", + "isvalid": true + } + } + }, + + + { + "Name": "closewallet", + "Request": { + "jsonrpc": "2.0", + "method": "closewallet", + "params": [], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": true + } + }, + { + "Name": "dumpprivkey", + "Request": { + "jsonrpc": "2.0", + "method": "dumpprivkey", + "params": [ "NVVwFw6XyhtRCFQ8SpUTMdPyYt4Vd9A1XQ" ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": "KyoYyZpoccbR6KZ25eLzhMTUxREwCpJzDsnuodGTKXSG8fDW9t7x" + } + }, + { + "Name": "getbalance", + "Request": { + "jsonrpc": "2.0", + "method": "getbalance", + "params": [ "0x8c23f196d8a1bfd103a9dcb1f9ccf0c611377d3b" ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": { + "balance": "3001101329992600" + } + } + }, + // mock decimals + { + "Name": "invokescript", + "Request": { + "jsonrpc": "2.0", + "method": "invokescript", + "params": [ "10c30c08646563696d616c730c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b52" ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": { + "script": "10c30c08646563696d616c730c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b52", + "state": "HALT", + "gas_consumed": "5061560", + "stack": [ + { + "type": "Integer", + "value": "8" + } + ], + "tx": "00769d16556925aa554712439a9c613ba114efa3fac23ddbca00e1f505000000009e021400000000005620200000009910c30c046e616d650c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b5210c30c0673796d626f6c0c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b5210c30c08646563696d616c730c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b5210c30c0b746f74616c537570706c790c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b5201420c40c848d0fcbf5e6a820508242ea8b7ccbeed3caefeed5db570537279c2154f7cfd8b0d8f477f37f4e6ca912935b732684d57c455dff7aa525ad4ab000931f22208290c2103aa052fbcb8e5b33a4eefd662536f8684641f04109f1d5e69cdda6f084890286a0b410a906ad4" + } + } + }, + { + "Name": "getnewaddress", + "Request": { + "jsonrpc": "2.0", + "method": "getnewaddress", + "params": [], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": "NXpCs9kcDkPvfyAobNYmFg8yfRZaDopDbf" + } + }, + { + "Name": "getunclaimedgas", + "Request": { + "jsonrpc": "2.0", + "method": "getunclaimedgas", + "params": [], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": "735870007400" + } + }, + { + "Name": "importprivkey", + "Request": { + "jsonrpc": "2.0", + "method": "importprivkey", + "params": [ "KyoYyZpoccbR6KZ25eLzhMTUxREwCpJzDsnuodGTKXSG8fDW9t7x" ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": { + "address": "NVVwFw6XyhtRCFQ8SpUTMdPyYt4Vd9A1XQ", + "haskey": true, + "label": null, + "watchonly": false + } + } + }, + { + "Name": "listaddress", + "Request": { + "jsonrpc": "2.0", + "method": "listaddress", + "params": [], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": [ + { + "address": "NVVwFw6XyhtRCFQ8SpUTMdPyYt4Vd9A1XQ", + "haskey": true, + "label": null, + "watchonly": false + }, + { + "address": "NZs2zXSPuuv9ZF6TDGSWT1RBmE8rfGj7UW", + "haskey": true, + "label": null, + "watchonly": false + } + ] + } + }, + { + "Name": "openwallet", + "Request": { + "jsonrpc": "2.0", + "method": "openwallet", + "params": [ "D:\\temp\\3.json", "1111" ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": true + } + }, + { + "Name": "sendfrom", + "Request": { + "jsonrpc": "2.0", + "method": "sendfrom", + "params": [ "0x8c23f196d8a1bfd103a9dcb1f9ccf0c611377d3b", "NVVwFw6XyhtRCFQ8SpUTMdPyYt4Vd9A1XQ", "NZs2zXSPuuv9ZF6TDGSWT1RBmE8rfGj7UW", "100.123" ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": { + "hash": "0x035facc3be1fc57da1690e3d2f8214f449d368437d8557ffabb2d408caf9ad76", + "size": 272, + "version": 0, + "nonce": 1553700339, + "sender": "NVVwFw6XyhtRCFQ8SpUTMdPyYt4Vd9A1XQ", + "sys_fee": "100000000", + "net_fee": "1272390", + "valid_until_block": 2105487, + "attributes": [], + "cosigners": [ + { + "account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", + "scopes": "CalledByEntry" + } + ], + "script": "A+CSx1QCAAAADBSZA7DD0pKYj+vl8wagL2VOousWKQwUaSWqVUcSQ5qcYTuhFO+j+sI928oTwAwIdHJhbnNmZXIMFDt9NxHG8Mz5sdypA9G/odiW8SOMQWJ9W1I5", + "witnesses": [ + { + "invocation": "DEDOA/QF5jYT2TCl9T94fFwAncuBhVhciISaq4fZ3WqGarEoT/0iDo3RIwGjfRW0mm/SV3nAVGEQeZInLqKQ98HX", + "verification": "DCEDqgUvvLjlszpO79ZiU2+GhGQfBBCfHV5pzdpvCEiQKGoLQQqQatQ=" + } + ] + } + } + }, + { + "Name": "sendmany", + "Request": { + "jsonrpc": "2.0", + "method": "sendmany", + "params": [ + "NVVwFw6XyhtRCFQ8SpUTMdPyYt4Vd9A1XQ", + [ + { + "asset": "0x9bde8f209c88dd0e7ca3bf0af0f476cdd8207789", + "value": "10", + "address": "NVVwFw6XyhtRCFQ8SpUTMdPyYt4Vd9A1XQ" + }, + { + "asset": "0x8c23f196d8a1bfd103a9dcb1f9ccf0c611377d3b", + "value": "1.2345", + "address": "NZs2zXSPuuv9ZF6TDGSWT1RBmE8rfGj7UW" + } + ] + ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": { + "hash": "0x542e64a9048bbe1ee565b840c41ccf9b5a1ef11f52e5a6858a523938a20c53ec", + "size": 483, + "version": 0, + "nonce": 34429660, + "sender": "NUMK37TV9yYKbJr1Gufh74nZiM623eBLqX", + "sys_fee": "100000000", + "net_fee": "2483780", + "valid_until_block": 2105494, + "attributes": [], + "cosigners": [ + { + "account": "0x36d6200fb4c9737c7b552d2b5530ab43605c5869", + "scopes": "CalledByEntry" + }, + { + "account": "0x9a55ca1006e2c359bbc8b9b0de6458abdff98b5c", + "scopes": "CalledByEntry" + } + ], + "script": "GgwUaSWqVUcSQ5qcYTuhFO+j+sI928oMFGlYXGBDqzBVKy1Ve3xzybQPINY2E8AMCHRyYW5zZmVyDBSJdyDYzXb08Aq/o3wO3YicII/em0FifVtSOQKQslsHDBSZA7DD0pKYj+vl8wagL2VOousWKQwUXIv536tYZN6wuci7WcPiBhDKVZoTwAwIdHJhbnNmZXIMFDt9NxHG8Mz5sdypA9G/odiW8SOMQWJ9W1I5", + "witnesses": [ + { + "invocation": "DECOdTEWg1WkuHN0GNV67kwxeuKADyC6TO59vTaU5dK6K1BGt8+EM6L3TdMga4qB2J+Meez8eYwZkSSRubkuvfr9", + "verification": "DCECeiS9CyBqFJwNKzonOs/yzajOraFep4IqFJVxBe6TesULQQqQatQ=" + }, + { + "invocation": "DEB1Laj6lvjoBJLTgE/RdvbJiXOmaKp6eNWDJt+p8kxnW6jbeKoaBRZWfUflqrKV7mZEE2JHA5MxrL5TkRIvsL5K", + "verification": "DCECkXL4gxd936eGEDt3KWfIuAsBsQcfyyBUcS8ggF6lZnwLQQqQatQ=" + } + ] + } + } + }, + { + "Name": "sendtoaddress", + "Request": { + "jsonrpc": "2.0", + "method": "sendtoaddress", + "params": [ "0x8c23f196d8a1bfd103a9dcb1f9ccf0c611377d3b", "NVVwFw6XyhtRCFQ8SpUTMdPyYt4Vd9A1XQ", "100.123" ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": { + "hash": "0xee5fc3f57d9f9bc9695c88ecc504444aab622b1680b1cb0848d5b6e39e7fd118", + "size": 381, + "version": 0, + "nonce": 330056065, + "sender": "NUMK37TV9yYKbJr1Gufh74nZiM623eBLqX", + "sys_fee": "100000000", + "net_fee": "2381780", + "valid_until_block": 2105500, + "attributes": [], + "cosigners": [ + { + "account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", + "scopes": "CalledByEntry" + } + ], + "script": "A+CSx1QCAAAADBRpJapVRxJDmpxhO6EU76P6wj3bygwUaSWqVUcSQ5qcYTuhFO+j+sI928oTwAwIdHJhbnNmZXIMFDt9NxHG8Mz5sdypA9G/odiW8SOMQWJ9W1I5", + "witnesses": [ + { + "invocation": "DECruSKmQKs0Y2cxplKROjPx8HKiyiYrrPn7zaV9zwHPumLzFc8DvgIo2JxmTnJsORyygN/su8mTmSLLb3PesBvY", + "verification": "DCECkXL4gxd936eGEDt3KWfIuAsBsQcfyyBUcS8ggF6lZnwLQQqQatQ=" + }, + { + "invocation": "DECS5npCs5PwsPUAQ01KyHyCev27dt3kDdT1Vi0K8PwnEoSlxYTOGGQCAwaiNEXSyBdBmT6unhZydmFnkezD7qzW", + "verification": "DCEDqgUvvLjlszpO79ZiU2+GhGQfBBCfHV5pzdpvCEiQKGoLQQqQatQ=" + } + ] + } + } + }, + + + { + "Name": "getapplicationlog", + "Request": { + "jsonrpc": "2.0", + "method": "getapplicationlog", + "params": [ "0x183cd84359cd9f8b956afcd02403ec07361c1dba55f0800241b4ef2b28e88bbb" ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": { + "txid": "0x183cd84359cd9f8b956afcd02403ec07361c1dba55f0800241b4ef2b28e88bbb", + "trigger": "Application", + "vmstate": "HALT", + "gas_consumed": "9007810", + "stack": [], + "notifications": [ + { + "contract": "0x8c23f196d8a1bfd103a9dcb1f9ccf0c611377d3b", + "state": { + "type": "Array", + "value": [ + { + "type": "ByteArray", + "value": "VHJhbnNmZXI=" + }, + { + "type": "Any" + }, + { + "type": "ByteArray", + "value": "eU9We10tADBFvus3caGe5hjcaNE=" + }, + { + "type": "Integer", + "value": "6321697534537" + } + ] + } + }, + { + "contract": "0x9bde8f209c88dd0e7ca3bf0af0f476cdd8207789", + "state": { + "type": "Array", + "value": [ + { + "type": "ByteArray", + "value": "VHJhbnNmZXI=" + }, + { + "type": "ByteArray", + "value": "eU9We10tADBFvus3caGe5hjcaNE=" + }, + { + "type": "ByteArray", + "value": "eU9We10tADBFvus3caGe5hjcaNE=" + }, + { + "type": "Integer", + "value": "99999961" + } + ] + } + } + ] + } + } + }, + { + "Name": "getnep5transfers", + "Request": { + "jsonrpc": "2.0", + "method": "getnep5transfers", + "params": [ "NVVwFw6XyhtRCFQ8SpUTMdPyYt4Vd9A1XQ", 0, 1868595301000 ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": { + "sent": [ + { + "timestamp": 1579250114541, + "asset_hash": "0x8c23f196d8a1bfd103a9dcb1f9ccf0c611377d3b", + "transfer_address": "NVVwFw6XyhtRCFQ8SpUTMdPyYt4Vd9A1XQ", + "amount": "1000000000", + "block_index": 603, + "transfer_notify_index": 0, + "tx_hash": "0x5e177b8d1dc33e9103c0cfd42f6dbf4efbe43029e2d6a18ea5ba0cb8437056b3" + }, + { + "timestamp": 1579406581635, + "asset_hash": "0x8c23f196d8a1bfd103a9dcb1f9ccf0c611377d3b", + "transfer_address": "NUMK37TV9yYKbJr1Gufh74nZiM623eBLqX", + "amount": "1000000000", + "block_index": 1525, + "transfer_notify_index": 0, + "tx_hash": "0xc9c618b48972b240e0058d97b8d79b807ad51015418c84012765298526aeb77d" + } + ], + "received": [ + { + "timestamp": 1579250114541, + "asset_hash": "0x8c23f196d8a1bfd103a9dcb1f9ccf0c611377d3b", + "transfer_address": "NVVwFw6XyhtRCFQ8SpUTMdPyYt4Vd9A1XQ", + "amount": "1000000000", + "block_index": 603, + "transfer_notify_index": 0, + "tx_hash": "0x5e177b8d1dc33e9103c0cfd42f6dbf4efbe43029e2d6a18ea5ba0cb8437056b3" + } + ], + "address": "NVVwFw6XyhtRCFQ8SpUTMdPyYt4Vd9A1XQ" + } + } + }, + { + "Name": "getnep5balances", + "Request": { + "jsonrpc": "2.0", + "method": "getnep5balances", + "params": [ "NVVwFw6XyhtRCFQ8SpUTMdPyYt4Vd9A1XQ" ], + "id": 1 + }, + "Response": { + "jsonrpc": "2.0", + "id": 1, + "result": { + "balance": [ + { + "asset_hash": "0x8c23f196d8a1bfd103a9dcb1f9ccf0c611377d3b", + "amount": "719978585420", + "last_updated_block": 3101 + }, + { + "asset_hash": "0x9bde8f209c88dd0e7ca3bf0af0f476cdd8207789", + "amount": "89999810", + "last_updated_block": 3096 + } + ], + "address": "NVVwFw6XyhtRCFQ8SpUTMdPyYt4Vd9A1XQ" + } + } + } +] diff --git a/tests/Neo.Network.RPC.Tests/TestUtils.cs b/tests/Neo.Network.RPC.Tests/TestUtils.cs index dd1b45aa0..2c4128376 100644 --- a/tests/Neo.Network.RPC.Tests/TestUtils.cs +++ b/tests/Neo.Network.RPC.Tests/TestUtils.cs @@ -1,10 +1,16 @@ +using Neo.IO.Json; using Neo.Network.P2P.Payloads; +using Neo.Network.RPC.Models; +using System.Collections.Generic; +using System.IO; using System.Linq; namespace Neo.Network.RPC.Tests { internal static class TestUtils { + public readonly static List RpcTestCases = ((JArray)JObject.Parse(File.ReadAllText("RpcTestCases.json"))).Select(p => RpcTestCase.FromJson(p)).ToList(); + public static Block GetBlock(int txCount) { return new Block @@ -46,4 +52,32 @@ public static Transaction GetTransaction() }; } } + + internal class RpcTestCase + { + public string Name { get; set; } + public RpcRequest Request { get; set; } + public RpcResponse Response { get; set; } + + public JObject ToJson() + { + return new JObject + { + ["Name"] = Name, + ["Request"] = Request.ToJson(), + ["Response"] = Response.ToJson(), + }; + } + + public static RpcTestCase FromJson(JObject json) + { + return new RpcTestCase + { + Name = json["Name"].AsString(), + Request = RpcRequest.FromJson(json["Request"]), + Response = RpcResponse.FromJson(json["Response"]), + }; + } + + } } diff --git a/tests/Neo.Network.RPC.Tests/UT_Nep5API.cs b/tests/Neo.Network.RPC.Tests/UT_Nep5API.cs index b21482441..8454d4afa 100644 --- a/tests/Neo.Network.RPC.Tests/UT_Nep5API.cs +++ b/tests/Neo.Network.RPC.Tests/UT_Nep5API.cs @@ -63,7 +63,7 @@ public void TestGetDecimals() UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(NativeContract.GAS.Decimals) }); var result = nep5API.Decimals(NativeContract.GAS.Hash); - Assert.AreEqual(NativeContract.GAS.Decimals, (byte)result); + Assert.AreEqual(NativeContract.GAS.Decimals, result); } [TestMethod] diff --git a/tests/Neo.Network.RPC.Tests/UT_RpcClient.cs b/tests/Neo.Network.RPC.Tests/UT_RpcClient.cs index b6ccbad17..4020555ca 100644 --- a/tests/Neo.Network.RPC.Tests/UT_RpcClient.cs +++ b/tests/Neo.Network.RPC.Tests/UT_RpcClient.cs @@ -4,12 +4,8 @@ using Moq.Protected; using Neo.IO; using Neo.IO.Json; -using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.Network.RPC.Models; -using Neo.SmartContract; -using Neo.SmartContract.Manifest; -using Neo.VM; using System; using System.Linq; using System.Net; @@ -37,522 +33,414 @@ public void TestSetup() }; rpc = new RpcClient(httpClient); + foreach (var test in TestUtils.RpcTestCases) + { + MockResponse(test.Request, test.Response); + } } - private void MockResponse(string content) + private void MockResponse(RpcRequest request, RpcResponse response) { handlerMock.Protected() // Setup the PROTECTED method to mock .Setup>( "SendAsync", - ItExpr.IsAny(), + ItExpr.Is(p => p.Content.ReadAsStringAsync().Result == request.ToJson().ToString()), ItExpr.IsAny() ) // prepare the expected response of the mocked http call .ReturnsAsync(new HttpResponseMessage() { StatusCode = HttpStatusCode.OK, - Content = new StringContent(content), + Content = new StringContent(response.ToJson().ToString()), }) .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()); + var test = TestUtils.RpcTestCases.Find(p => p.Name == (nameof(rpc.SendRawTransaction) + "error").ToLower()); try { - var result = rpc.GetBlockHex("773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e"); + var result = rpc.SendRawTransaction(test.Request.Params[0].AsString().HexToBytes().AsSerializable()); } catch (RpcException ex) { - Assert.AreEqual(-32700, ex.HResult); - Assert.AreEqual("Parse error", ex.Message); + Assert.AreEqual(-500, ex.HResult); + Assert.AreEqual("InsufficientFunds", ex.Message); } } [TestMethod] - public void TestGetBestBlockHash() + public void TestConstructorByUrlAndDispose() { - JObject response = CreateResponse(1); - response["result"] = "000000002deadfa82cbc4682f5800"; - MockResponse(response.ToString()); + //dummy url for test + var client = new RpcClient("http://www.xxx.yyy"); + Action action = () => client.Dispose(); + action.Should().NotThrow(); + } + + [TestMethod] + public void TestConstructorWithBasicAuth() + { + var client = new RpcClient("http://www.xxx.yyy", "krain", "123456"); + client.Dispose(); + } + + #region Blockchain + [TestMethod] + public void TestGetBestBlockHash() + { + var test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.GetBestBlockHash).ToLower()); var result = rpc.GetBestBlockHash(); - Assert.AreEqual("000000002deadfa82cbc4682f5800", result); + Assert.AreEqual(test.Response.Result.AsString(), result); } [TestMethod] public void TestGetBlockHex() { - JObject response = CreateResponse(1); - response["result"] = "000000002deadfa82cbc4682f5800"; - MockResponse(response.ToString()); - - var result = rpc.GetBlockHex("773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e"); - Assert.AreEqual("000000002deadfa82cbc4682f5800", result); + var tests = TestUtils.RpcTestCases.Where(p => p.Name == nameof(rpc.GetBlockHex).ToLower()); + foreach (var test in tests) + { + var result = rpc.GetBlockHex(test.Request.Params[0].AsString()); + Assert.AreEqual(test.Response.Result.AsString(), result); + } } [TestMethod] public void TestGetBlock() { - // create block - var block = TestUtils.GetBlock(3); - - JObject json = block.ToJson(); - json["confirmations"] = 20; - 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.NextBlockHash); - 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()); - - // 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("0x773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e", result.NextBlockHash.ToString()); - Assert.AreEqual(block.Transactions.Length, result.Block.Transactions.Length); - Assert.AreEqual(block.Transactions[0].Hash.ToString(), result.Block.Transactions[0].Hash.ToString()); + var tests = TestUtils.RpcTestCases.Where(p => p.Name == nameof(rpc.GetBlock).ToLower()); + foreach (var test in tests) + { + var result = rpc.GetBlock(test.Request.Params[0].AsString()); + Assert.AreEqual(test.Response.Result.AsString(), result.ToJson().ToString()); + } } [TestMethod] public void TestGetBlockCount() { - JObject response = CreateResponse(1); - response["result"] = 100; - MockResponse(response.ToString()); - + var test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.GetBlockCount).ToLower()); var result = rpc.GetBlockCount(); - Assert.AreEqual(100u, result); + Assert.AreEqual(test.Response.Result.AsString(), result.ToString()); } [TestMethod] public void TestGetBlockHash() { - JObject response = CreateResponse(1); - response["result"] = "0x4c1e879872344349067c3b1a30781eeb4f9040d3795db7922f513f6f9660b9b2"; - MockResponse(response.ToString()); - - var result = rpc.GetBlockHash(100); - Assert.AreEqual("0x4c1e879872344349067c3b1a30781eeb4f9040d3795db7922f513f6f9660b9b2", result); + var test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.GetBlockHash).ToLower()); + var result = rpc.GetBlockHash((int)test.Request.Params[0].AsNumber()); + Assert.AreEqual(test.Response.Result.AsString(), result.ToString()); } [TestMethod] public void TestGetBlockHeaderHex() { - JObject response = CreateResponse(1); - response["result"] = "0x4c1e879872344349067c3b1a30781eeb4f9040d3795db7922f513f6f9660b9b2"; - MockResponse(response.ToString()); - - var result = rpc.GetBlockHeaderHex("100"); - Assert.AreEqual("0x4c1e879872344349067c3b1a30781eeb4f9040d3795db7922f513f6f9660b9b2", result); + var tests = TestUtils.RpcTestCases.Where(p => p.Name == nameof(rpc.GetBlockHeaderHex).ToLower()); + foreach (var test in tests) + { + var result = rpc.GetBlockHeaderHex(test.Request.Params[0].AsString()); + Assert.AreEqual(test.Response.Result.AsString(), result); + } } [TestMethod] public void TestGetBlockHeader() { - Header header = TestUtils.GetHeader(); + var tests = TestUtils.RpcTestCases.Where(p => p.Name == nameof(rpc.GetBlockHeader).ToLower()); + foreach (var test in tests) + { + var result = rpc.GetBlockHeader(test.Request.Params[0].AsString()); + Assert.AreEqual(test.Response.Result.ToString(), result.ToJson().ToString()); + } + } - JObject json = header.ToJson(); - json["confirmations"] = 20; - JObject response = CreateResponse(1); - response["result"] = json; - MockResponse(response.ToString()); + [TestMethod] + public void TestGetBlockSysFee() + { + var test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.GetBlockSysFee).ToLower()); + var result = rpc.GetBlockSysFee((int)test.Request.Params[0].AsNumber()); + Assert.AreEqual(test.Response.Result.AsString(), result.ToString()); + } - var result = rpc.GetBlockHeader("100"); - Assert.AreEqual(header.Hash.ToString(), result.Header.Hash.ToString()); - Assert.IsNull(result.NextBlockHash); - Assert.AreEqual(20, result.Confirmations); + [TestMethod] + public void TestGetContractState() + { + var tests = TestUtils.RpcTestCases.Where(p => p.Name == nameof(rpc.GetContractState).ToLower()); + foreach (var test in tests) + { + var result = rpc.GetContractState(test.Request.Params[0].AsString()); + Assert.AreEqual(test.Response.Result.ToString(), result.ToJson().ToString()); + } + } - 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 TestGetRawMempool() + { + var test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.GetRawMempool).ToLower()); + var result = rpc.GetRawMempool(); + Assert.AreEqual(test.Response.Result.ToString(), ((JArray)result.Select(p => (JObject)p).ToArray()).ToString()); } [TestMethod] - public void TestGetBlockSysFee() + public void TestGetRawMempoolBoth() { - JObject response = CreateResponse(1); - response["result"] = "195500"; - MockResponse(response.ToString()); + var test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.GetRawMempoolBoth).ToLower()); + var result = rpc.GetRawMempoolBoth(); + Assert.AreEqual(test.Response.Result.ToString(), result.ToJson().ToString()); + } - var result = rpc.GetBlockSysFee(100); - Assert.AreEqual("195500", result); + [TestMethod] + public void TestGetRawTransactionHex() + { + var test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.GetRawTransactionHex).ToLower()); + var result = rpc.GetRawTransactionHex(test.Request.Params[0].AsString()); + Assert.AreEqual(test.Response.Result.AsString(), result); } [TestMethod] - public void TestGetConnectionCount() + public void TestGetRawTransaction() { - JObject response = CreateResponse(1); - response["result"] = 9; - MockResponse(response.ToString()); + var test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.GetRawTransaction).ToLower()); + var result = rpc.GetRawTransaction(test.Request.Params[0].AsString()); + Assert.AreEqual(test.Response.Result.ToString(), result.ToJson().ToString()); + } - var result = rpc.GetConnectionCount(); - Assert.AreEqual(9, result); + [TestMethod] + public void TestGetStorage() + { + var test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.GetStorage).ToLower()); + var result = rpc.GetStorage(test.Request.Params[0].AsString(), test.Request.Params[1].AsString()); + Assert.AreEqual(test.Response.Result.AsString(), result); } [TestMethod] - public void TestGetContractState() + public void TestGetTransactionHeight() { - byte[] script; - using (var sb = new ScriptBuilder()) - { - sb.EmitSysCall(InteropService.Runtime.GetInvocationCounter); - script = sb.ToArray(); - } + var test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.GetTransactionHeight).ToLower()); + var result = rpc.GetTransactionHeight(test.Request.Params[0].AsString()); + Assert.AreEqual(test.Response.Result.ToString(), result.ToString()); + } - ContractState state = new ContractState - { - Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP }.Concat(script).ToArray(), - Manifest = ContractManifest.CreateDefault(UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01")) - }; + [TestMethod] + public void TestGetValidators() + { + var test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.GetValidators).ToLower()); + var result = rpc.GetValidators(); + Assert.AreEqual(test.Response.Result.ToString(), ((JArray)result.Select(p => p.ToJson()).ToArray()).ToString()); + } - JObject response = CreateResponse(1); - response["result"] = state.ToJson(); - MockResponse(response.ToString()); + #endregion Blockchain - var result = rpc.GetContractState("17694b31cc7ee215cea2ded146e0b2b28768fc46"); + #region Node - Assert.AreEqual(state.Script.ToHexString(), result.Script.ToHexString()); - Assert.AreEqual(state.Manifest.Abi.EntryPoint.Name, result.Manifest.Abi.EntryPoint.Name); + [TestMethod] + public void TestGetConnectionCount() + { + var test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.GetConnectionCount).ToLower()); + var result = rpc.GetConnectionCount(); + Assert.AreEqual(test.Response.Result.ToString(), result.ToString()); } [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 test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.GetPeers).ToLower()); 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); + Assert.AreEqual(test.Response.Result.ToString(), result.ToJson().ToString()); } [TestMethod] - public void TestGetRawMempool() + public void TestGetVersion() { - JObject response = CreateResponse(1); - response["result"] = JObject.Parse(@"[ - ""0x9786cce0dddb524c40ddbdd5e31a41ed1f6b5c8a683c122f627ca4a007a7cf4e"", - ""0xb488ad25eb474f89d5ca3f985cc047ca96bc7373a6d3da8c0f192722896c1cd7"", - ""0xf86f6f2c08fbf766ebe59dc84bc3b8829f1053f0a01deb26bf7960d99fa86cd6"" - ]"); - MockResponse(response.ToString()); + var test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.GetVersion).ToLower()); + var result = rpc.GetVersion(); + Assert.AreEqual(test.Response.Result.ToString(), result.ToJson().ToString()); + } - var result = rpc.GetRawMempool(); - Assert.AreEqual("0xb488ad25eb474f89d5ca3f985cc047ca96bc7373a6d3da8c0f192722896c1cd7", result[1]); + [TestMethod] + public void TestSendRawTransaction() + { + var test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.SendRawTransaction).ToLower()); + var result = rpc.SendRawTransaction(test.Request.Params[0].AsString().HexToBytes().AsSerializable()); + Assert.AreEqual(test.Response.Result["hash"].AsString(), result.ToString()); } [TestMethod] - public void TestGetRawMempoolBoth() + public void TestSubmitBlock() { - 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)); + var test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.SubmitBlock).ToLower()); + var block = TestUtils.GetBlock(2).Hash; + var result = rpc.SubmitBlock(test.Request.Params[0].AsString().HexToBytes()); + Assert.AreEqual(test.Response.Result["hash"].AsString(), result.ToString()); + } - JObject response = CreateResponse(1); - response["result"] = json; - MockResponse(response.ToString()); + #endregion Node - var result = rpc.GetRawMempoolBoth(); - Assert.AreEqual(65535u, result.Height); - Assert.AreEqual("0x9786cce0dddb524c40ddbdd5e31a41ed1f6b5c8a683c122f627ca4a007a7cf4e", result.Verified[0]); - Assert.AreEqual("0xf86f6f2c08fbf766ebe59dc84bc3b8829f1053f0a01deb26bf7960d99fa86cd6", result.UnVerified[1]); + #region SmartContract + + [TestMethod] + public void TestInvokeFunction() + { + var test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.InvokeFunction).ToLower()); + var result = rpc.InvokeFunction(test.Request.Params[0].AsString(), test.Request.Params[1].AsString(), + ((JArray)test.Request.Params[2]).Select(p => RpcStack.FromJson(p)).ToArray()); + Assert.AreEqual(test.Response.Result.ToString(), result.ToJson().ToString()); } [TestMethod] - public void TestGetRawTransactionHex() + public void TestInvokeScript() { - var json = TestUtils.GetTransaction().ToArray().ToHexString(); + var test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.InvokeScript).ToLower()); + var result = rpc.InvokeScript(test.Request.Params[0].AsString().HexToBytes()); + Assert.AreEqual(test.Response.Result.ToString(), result.ToJson().ToString()); + } - JObject response = CreateResponse(1); - response["result"] = json; - MockResponse(response.ToString()); + #endregion SmartContract - //var result = rpc.GetRawTransactionHex("0x9786cce0dddb524c40ddbdd5e31a41ed1f6b5c8a683c122f627ca4a007a7cf4e"); - var result = rpc.GetRawTransactionHex(TestUtils.GetTransaction().Hash.ToString()); - Assert.AreEqual(json, result); - } + #region Utilities [TestMethod] - public void TestGetRawTransaction() + public void TestListPlugins() { - 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()); + var test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.ListPlugins).ToLower()); + var result = rpc.ListPlugins(); + Assert.AreEqual(test.Response.Result.ToString(), ((JArray)result.Select(p => p.ToJson()).ToArray()).ToString()); + } - // make the code compatible with the old version - json["blockhash"] = UInt256.Zero.ToString(); - json["confirmations"] = 100; - json["blocktime"] = 10; - MockResponse(response.ToString()); + [TestMethod] + public void TestValidateAddress() + { + var test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.ValidateAddress).ToLower()); + var result = rpc.ValidateAddress(test.Request.Params[0].AsString()); + Assert.AreEqual(test.Response.Result.ToString(), result.ToJson().ToString()); + } - result = rpc.GetRawTransaction("0x9786cce0dddb524c40ddbdd5e31a41ed1f6b5c8a683c122f627ca4a007a7cf4e"); - Assert.AreEqual(transaction.Hash, result.Transaction.Hash); - Assert.AreEqual(100, result.Confirmations); - Assert.AreEqual(null, result.VMState); - Assert.AreEqual(json.ToString(), result.ToJson().ToString()); + #endregion Utilities - json["vmState"] = VMState.HALT; - MockResponse(response.ToString()); + #region Wallet - result = rpc.GetRawTransaction("0x9786cce0dddb524c40ddbdd5e31a41ed1f6b5c8a683c122f627ca4a007a7cf4e"); - Assert.AreEqual(transaction.Hash, result.Transaction.Hash); - Assert.AreEqual(100, result.Confirmations); - Assert.AreEqual(VMState.HALT, result.VMState); - Assert.AreEqual(json.ToString(), result.ToJson().ToString()); + [TestMethod] + public void TestCloseWallet() + { + var test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.CloseWallet).ToLower()); + var result = rpc.CloseWallet(); + Assert.AreEqual(test.Response.Result.AsBoolean(), result); } [TestMethod] - public void TestGetStorage() + public void TestDumpPrivKey() { - JObject json = "4c696e"; - JObject response = CreateResponse(1); - response["result"] = json; - MockResponse(response.ToString()); - - var result = rpc.GetStorage("03febccf81ac85e3d795bc5cbd4e84e907812aa3", "5065746572"); - Assert.AreEqual("4c696e", result); + var test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.DumpPrivKey).ToLower()); + var result = rpc.DumpPrivKey(test.Request.Params[0].AsString()); + Assert.AreEqual(test.Response.Result.AsString(), result); } [TestMethod] - public void TestGetTransactionHeight() + public void TestGetBalance() { - JObject json = 10000; - JObject response = CreateResponse(1); - response["result"] = json; - MockResponse(response.ToString()); - - var result = rpc.GetTransactionHeight("9c909e1e3ba03290553a68d862e002c7a21ba302e043fc492fe069bf6a134d29"); - Assert.AreEqual(json.ToString(), result.ToString()); + var test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.GetBalance).ToLower()); + var result = rpc.GetBalance(test.Request.Params[0].AsString()); + Assert.AreEqual(test.Response.Result["balance"].AsString(), result.Value.ToString()); } [TestMethod] - public void TestGetValidators() + public void TestGetNewAddress() { - 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()); + var test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.GetNewAddress).ToLower()); + var result = rpc.GetNewAddress(); + Assert.AreEqual(test.Response.Result.AsString(), result); } [TestMethod] - public void TestGetVersion() + public void TestGetUnclaimedGas() { - 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); + var test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.GetUnclaimedGas).ToLower()); + var result = rpc.GetUnclaimedGas(); + Assert.AreEqual(test.Response.Result.AsString(), result.ToString()); } [TestMethod] - public void TestInvokeFunction() + public void TestImportPrivKey() { - JObject json = JObject.Parse(@" - { - ""script"": ""1426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf"", - ""state"": ""HALT"", - ""gas_consumed"": ""0.311"", - ""stack"": [ - { - ""type"": ""ByteArray"", - ""value"": ""262bec084432"" - } - ] - }"); - 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()); + var test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.ImportPrivKey).ToLower()); + var result = rpc.ImportPrivKey(test.Request.Params[0].AsString()); + Assert.AreEqual(test.Response.Result.ToString(), result.ToJson().ToString()); } [TestMethod] - public void TestInvokeScript() + public void TestListAddress() { - JObject json = JObject.Parse(@" - { - ""script"": ""1426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf"", - ""state"": ""HALT"", - ""gas_consumed"": ""0.311"", - ""stack"": [ - { - ""type"": ""ByteArray"", - ""value"": ""262bec084432"" - } - ] - }"); - JObject response = CreateResponse(1); - response["result"] = json; - MockResponse(response.ToString()); - - var result = rpc.InvokeScript("00046e616d656724058e5e1b6008847cd662728549088a9ee82191".HexToBytes()); - Assert.AreEqual(json.ToString(), result.ToJson().ToString()); + var test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.ListAddress).ToLower()); + var result = rpc.ListAddress(); + Assert.AreEqual(test.Response.Result.ToString(), ((JArray)result.Select(p => p.ToJson()).ToArray()).ToString()); } [TestMethod] - public void TestListPlugins() + public void TestOpenWallet() { - 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()); + var test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.OpenWallet).ToLower()); + var result = rpc.OpenWallet(test.Request.Params[0].AsString(), test.Request.Params[1].AsString()); + Assert.AreEqual(test.Response.Result.AsBoolean(), result); } [TestMethod] - public void TestSendRawTransaction() + public void TestSendFrom() { - JObject json = true; - JObject response = CreateResponse(1); - response["result"] = json; - MockResponse(response.ToString()); - - var result = rpc.SendRawTransaction("80000001195876cb34364dc38b730077156c6bc3a7fc570044a66fbfeeea56f71327e8ab0000029b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500c65eaf440000000f9a23e06f74cf86b8827a9108ec2e0f89ad956c9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc50092e14b5e00000030aab52ad93f6ce17ca07fa88fc191828c58cb71014140915467ecd359684b2dc358024ca750609591aa731a0b309c7fb3cab5cd0836ad3992aa0a24da431f43b68883ea5651d548feb6bd3c8e16376e6e426f91f84c58232103322f35c7819267e721335948d385fae5be66e7ba8c748ac15467dcca0693692dac".HexToBytes()); - Assert.AreEqual(json.ToString(), ((JObject)result).ToString()); + var test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.SendFrom).ToLower()); + var result = rpc.SendFrom(test.Request.Params[0].AsString(), test.Request.Params[1].AsString(), + test.Request.Params[2].AsString(), test.Request.Params[3].AsString()); + Assert.AreEqual(test.Response.Result.ToString(), result.ToString()); } [TestMethod] - public void TestSubmitBlock() + public void TestSendMany() { - JObject json = true; - JObject response = CreateResponse(1); - response["result"] = json; - MockResponse(response.ToString()); - - var result = rpc.SubmitBlock("03febccf81ac85e3d795bc5cbd4e84e907812aa3".HexToBytes()); - Assert.AreEqual(json.ToString(), ((JObject)result).ToString()); + var test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.SendMany).ToLower()); + var result = rpc.SendMany(test.Request.Params[0].AsString(), ((JArray)test.Request.Params[1]).Select(p => RpcTransferOut.FromJson(p))); + Assert.AreEqual(test.Response.Result.ToString(), result.ToString()); } [TestMethod] - public void TestValidateAddress() + public void TestSendToAddress() { - JObject json = new JObject(); - json["address"] = "AQVh2pG732YvtNaxEGkQUei3YA4cvo7d2i"; - json["isvalid"] = false; - JObject response = CreateResponse(1); - response["result"] = json; - MockResponse(response.ToString()); + var test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.SendToAddress).ToLower()); + var result = rpc.SendToAddress(test.Request.Params[0].AsString(), test.Request.Params[1].AsString(), test.Request.Params[2].AsString()); + Assert.AreEqual(test.Response.Result.ToString(), result.ToString()); + } + + #endregion Wallet + + #region Plugins - var result = rpc.ValidateAddress("AQVh2pG732YvtNaxEGkQUei3YA4cvo7d2i"); - Assert.AreEqual(json.ToString(), result.ToJson().ToString()); + [TestMethod()] + public void GetApplicationLogTest() + { + var test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.GetApplicationLog).ToLower()); + var result = rpc.GetApplicationLog(test.Request.Params[0].AsString()); + Assert.AreEqual(test.Response.Result.ToString(), result.ToJson().ToString()); } - [TestMethod] - public void TestConstructorByUrlAndDispose() + [TestMethod()] + public void GetNep5TransfersTest() { - //dummy url for test - var client = new RpcClient("http://www.xxx.yyy"); - Action action = () => client.Dispose(); - action.Should().NotThrow(); + var test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.GetNep5Transfers).ToLower()); + var result = rpc.GetNep5Transfers(test.Request.Params[0].AsString(), (ulong)test.Request.Params[1].AsNumber(), + (ulong)test.Request.Params[2].AsNumber()); + Assert.AreEqual(test.Response.Result.ToString(), result.ToJson().ToString()); } + + [TestMethod()] + public void GetNep5BalancesTest() + { + var test = TestUtils.RpcTestCases.Find(p => p.Name == nameof(rpc.GetNep5Balances).ToLower()); + var result = rpc.GetNep5Balances(test.Request.Params[0].AsString()); + Assert.AreEqual(test.Response.Result.ToString(), result.ToJson().ToString()); + } + + #endregion Plugins } } diff --git a/tests/Neo.Network.RPC.Tests/UT_RpcModels.cs b/tests/Neo.Network.RPC.Tests/UT_RpcModels.cs new file mode 100644 index 000000000..313f757f6 --- /dev/null +++ b/tests/Neo.Network.RPC.Tests/UT_RpcModels.cs @@ -0,0 +1,139 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO.Json; +using Neo.Network.RPC.Models; +using System.Linq; + +namespace Neo.Network.RPC.Tests +{ + [TestClass()] + public class UT_RpcModels + { + [TestMethod()] + public void TestRpcAccount() + { + JObject json = TestUtils.RpcTestCases.Find(p => p.Name == nameof(RpcClient.ImportPrivKey).ToLower()).Response.Result; + var item = RpcAccount.FromJson(json); + Assert.AreEqual(json.ToString(), item.ToJson().ToString()); + } + + [TestMethod()] + public void TestRpcApplicationLog() + { + JObject json = TestUtils.RpcTestCases.Find(p => p.Name == nameof(RpcClient.GetApplicationLog).ToLower()).Response.Result; + var item = RpcApplicationLog.FromJson(json); + Assert.AreEqual(json.ToString(), item.ToJson().ToString()); + } + + [TestMethod()] + public void TestRpcBlock() + { + JObject json = TestUtils.RpcTestCases.Find(p => p.Name == nameof(RpcClient.GetBlock).ToLower()).Response.Result; + var item = RpcBlock.FromJson(json); + Assert.AreEqual(json.ToString(), item.ToJson().ToString()); + } + + [TestMethod()] + public void TestRpcBlockHeader() + { + JObject json = TestUtils.RpcTestCases.Find(p => p.Name == nameof(RpcClient.GetBlockHeader).ToLower()).Response.Result; + var item = RpcBlockHeader.FromJson(json); + Assert.AreEqual(json.ToString(), item.ToJson().ToString()); + } + + [TestMethod()] + public void TestGetContractState() + { + JObject json = TestUtils.RpcTestCases.Find(p => p.Name == nameof(RpcClient.GetContractState).ToLower()).Response.Result; + var item = RpcContractState.FromJson(json); + Assert.AreEqual(json.ToString(), item.ToJson().ToString()); + } + + [TestMethod()] + public void TestRpcInvokeResult() + { + JObject json = TestUtils.RpcTestCases.Find(p => p.Name == nameof(RpcClient.InvokeFunction).ToLower()).Response.Result; + var item = RpcInvokeResult.FromJson(json); + Assert.AreEqual(json.ToString(), item.ToJson().ToString()); + } + + [TestMethod()] + public void TestRpcNep5Balances() + { + JObject json = TestUtils.RpcTestCases.Find(p => p.Name == nameof(RpcClient.GetNep5Balances).ToLower()).Response.Result; + var item = RpcNep5Balances.FromJson(json); + Assert.AreEqual(json.ToString(), item.ToJson().ToString()); + } + + [TestMethod()] + public void TestRpcNep5Transfers() + { + JObject json = TestUtils.RpcTestCases.Find(p => p.Name == nameof(RpcClient.GetNep5Transfers).ToLower()).Response.Result; + var item = RpcNep5Transfers.FromJson(json); + Assert.AreEqual(json.ToString(), item.ToJson().ToString()); + } + + [TestMethod()] + public void TestRpcPeers() + { + JObject json = TestUtils.RpcTestCases.Find(p => p.Name == nameof(RpcClient.GetPeers).ToLower()).Response.Result; + var item = RpcPeers.FromJson(json); + Assert.AreEqual(json.ToString(), item.ToJson().ToString()); + } + + [TestMethod()] + public void TestRpcPlugin() + { + JObject json = TestUtils.RpcTestCases.Find(p => p.Name == nameof(RpcClient.ListPlugins).ToLower()).Response.Result; + var item = ((JArray)json).Select(p => RpcPlugin.FromJson(p)); + Assert.AreEqual(json.ToString(), ((JArray)item.Select(p => p.ToJson()).ToArray()).ToString()); + } + + [TestMethod()] + public void TestRpcRawMemPool() + { + JObject json = TestUtils.RpcTestCases.Find(p => p.Name == nameof(RpcClient.GetRawMempoolBoth).ToLower()).Response.Result; + var item = RpcRawMemPool.FromJson(json); + Assert.AreEqual(json.ToString(), item.ToJson().ToString()); + } + + [TestMethod()] + public void TestRpcTransaction() + { + JObject json = TestUtils.RpcTestCases.Find(p => p.Name == nameof(RpcClient.GetRawTransaction).ToLower()).Response.Result; + var item = RpcTransaction.FromJson(json); + Assert.AreEqual(json.ToString(), item.ToJson().ToString()); + } + + [TestMethod()] + public void TestRpcTransferOut() + { + JObject json = TestUtils.RpcTestCases.Find(p => p.Name == nameof(RpcClient.SendMany).ToLower()).Request.Params[1]; + var item = ((JArray)json).Select(p => RpcTransferOut.FromJson(p)); + Assert.AreEqual(json.ToString(), ((JArray)item.Select(p => p.ToJson()).ToArray()).ToString()); + } + + [TestMethod()] + public void TestRpcValidateAddressResult() + { + JObject json = TestUtils.RpcTestCases.Find(p => p.Name == nameof(RpcClient.ValidateAddress).ToLower()).Response.Result; + var item = RpcValidateAddressResult.FromJson(json); + Assert.AreEqual(json.ToString(), item.ToJson().ToString()); + } + + [TestMethod()] + public void TestRpcValidator() + { + JObject json = TestUtils.RpcTestCases.Find(p => p.Name == nameof(RpcClient.GetValidators).ToLower()).Response.Result; + var item = ((JArray)json).Select(p => RpcValidator.FromJson(p)); + Assert.AreEqual(json.ToString(), ((JArray)item.Select(p => p.ToJson()).ToArray()).ToString()); + } + + [TestMethod()] + public void TestRpcVersion() + { + JObject json = TestUtils.RpcTestCases.Find(p => p.Name == nameof(RpcClient.GetVersion).ToLower()).Response.Result; + var item = RpcVersion.FromJson(json); + Assert.AreEqual(json.ToString(), item.ToJson().ToString()); + } + } +} diff --git a/tests/Neo.Network.RPC.Tests/UT_TransactionManager.cs b/tests/Neo.Network.RPC.Tests/UT_TransactionManager.cs index 56925ff58..b48c71519 100644 --- a/tests/Neo.Network.RPC.Tests/UT_TransactionManager.cs +++ b/tests/Neo.Network.RPC.Tests/UT_TransactionManager.cs @@ -36,7 +36,7 @@ public void TestSetup() public static Mock MockRpcClient(UInt160 sender, byte[] script) { - var mockRpc = new Mock(MockBehavior.Strict, "http://seed1.neo.org:10331"); + var mockRpc = new Mock(MockBehavior.Strict, "http://seed1.neo.org:10331", null, null); // MockHeight mockRpc.Setup(p => p.RpcSend("getblockcount")).Returns(100).Verifiable(); @@ -67,7 +67,7 @@ public static void MockInvokeScript(Mock mockClient, byte[] script, p Stack = parameters, GasConsumed = "100", Script = script.ToHexString(), - State = "" + State = VMState.HALT }; mockClient.Setup(p => p.RpcSend("invokescript", It.Is(j => j[0].AsString() == script.ToHexString()))) @@ -90,12 +90,11 @@ public void TestMakeTransaction() }; byte[] script = new byte[1]; - txManager.MakeTransaction(script, attributes, null, 60000); + txManager.MakeTransaction(script, attributes, null); var tx = txManager.Tx; Assert.AreEqual("53616d706c6555726c", tx.Attributes[0].Data.ToHexString()); - Assert.AreEqual(0, tx.SystemFee % (long)NativeContract.GAS.Factor); - Assert.AreEqual(60000, tx.NetworkFee); + Assert.AreEqual(1, tx.SystemFee / (long)NativeContract.GAS.Factor); } [TestMethod] @@ -130,7 +129,7 @@ public void TestSign() Assert.IsTrue(Crypto.VerifySignature(tx.GetHashData(), signature, keyPair1.PublicKey.EncodePoint(false).Skip(1).ToArray())); // verify network fee - long networkFee = tx.Size * (long)1000 + ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.GetPrice(InteropService.Crypto.ECDsaVerify, null); + long networkFee = tx.Size * (long)1000 + ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.GetPrice(InteropService.Crypto.ECDsaVerify, null, null); Assert.AreEqual(networkFee, tx.NetworkFee); // duplicate sign should not add new witness @@ -138,7 +137,7 @@ public void TestSign() Assert.AreEqual(1, txManager.Tx.Witnesses.Length); // throw exception when the KeyPair is wrong - Assert.ThrowsException(() => txManager.AddSignature(keyPair2)); + Assert.ThrowsException(() => txManager.AddSignature(keyPair2).Sign()); } [TestMethod] @@ -159,7 +158,7 @@ public void TestSignMulti() }; byte[] script = new byte[1]; - txManager.MakeTransaction(script, null, cosigners, 0_10000000) + txManager.MakeTransaction(script, null, cosigners) .AddMultiSig(keyPair1, 2, keyPair1.PublicKey, keyPair2.PublicKey) .AddMultiSig(keyPair2, 2, keyPair1.PublicKey, keyPair2.PublicKey) .AddSignature(keyPair1) @@ -182,7 +181,7 @@ public void TestAddWitness() }; byte[] script = new byte[1]; - txManager.MakeTransaction(script, null, cosigners, 0_10000000); + txManager.MakeTransaction(script, null, cosigners); txManager.AddWitness(UInt160.Zero); txManager.AddSignature(keyPair1); txManager.Sign(); diff --git a/tests/Neo.Network.RPC.Tests/UT_Utility.cs b/tests/Neo.Network.RPC.Tests/UT_Utility.cs index 00098b092..870cfe5a1 100644 --- a/tests/Neo.Network.RPC.Tests/UT_Utility.cs +++ b/tests/Neo.Network.RPC.Tests/UT_Utility.cs @@ -68,7 +68,7 @@ public void TestToBigInteger() amount = 1.23456789m; decimals = 4; - Assert.ThrowsException(() => result = amount.ToBigInteger(decimals)); + Assert.ThrowsException(() => result = amount.ToBigInteger(decimals)); } } } diff --git a/tests/Neo.Network.RPC.Tests/UT_WalletAPI.cs b/tests/Neo.Network.RPC.Tests/UT_WalletAPI.cs index a2f738048..72b83af8a 100644 --- a/tests/Neo.Network.RPC.Tests/UT_WalletAPI.cs +++ b/tests/Neo.Network.RPC.Tests/UT_WalletAPI.cs @@ -2,7 +2,6 @@ using Moq; using Neo.IO.Json; using Neo.Network.P2P.Payloads; -using Neo.Network.RPC; using Neo.Network.RPC.Models; using Neo.SmartContract; using Neo.SmartContract.Native; @@ -26,7 +25,7 @@ public void TestSetup() { keyPair1 = new KeyPair(Wallet.GetPrivateKeyFromWIF("KyXwTh1hB76RRMquSvnxZrJzQx7h9nQP2PCRL38v6VDb5ip3nf1p")); sender = Contract.CreateSignatureRedeemScript(keyPair1.PublicKey).ToScriptHash(); - address1 = Neo.Wallets.Helper.ToAddress(sender); + address1 = Wallets.Helper.ToAddress(sender); rpcClientMock = UT_TransactionManager.MockRpcClient(sender, new byte[0]); walletAPI = new WalletAPI(rpcClientMock.Object); } @@ -80,7 +79,9 @@ public void TestClaimGas() byte[] testScript = NativeContract.NEO.Hash.MakeScript("transfer", sender, sender, new BigInteger(1_00000000)); UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(1_10000000) }); - rpcClientMock.Setup(p => p.RpcSend("sendrawtransaction", It.IsAny())).Returns(true); + var json = new JObject(); + json["hash"] = UInt256.Zero.ToString(); + rpcClientMock.Setup(p => p.RpcSend("sendrawtransaction", It.IsAny())).Returns(json); var tranaction = walletAPI.ClaimGas(keyPair1.Export()); Assert.AreEqual(testScript.ToHexString(), tranaction.Script.ToHexString()); @@ -95,9 +96,11 @@ public void TestTransfer() byte[] testScript = NativeContract.GAS.Hash.MakeScript("transfer", sender, UInt160.Zero, NativeContract.GAS.Factor * 100); UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(1_10000000) }); - rpcClientMock.Setup(p => p.RpcSend("sendrawtransaction", It.IsAny())).Returns(true); + var json = new JObject(); + json["hash"] = UInt256.Zero.ToString(); + rpcClientMock.Setup(p => p.RpcSend("sendrawtransaction", It.IsAny())).Returns(json); - var tranaction = walletAPI.Transfer(NativeContract.GAS.Hash.ToString(), keyPair1.Export(), UInt160.Zero.ToAddress(), 100, 1.1m); + var tranaction = walletAPI.Transfer(NativeContract.GAS.Hash.ToString(), keyPair1.Export(), UInt160.Zero.ToAddress(), 100); Assert.AreEqual(testScript.ToHexString(), tranaction.Script.ToHexString()); }