diff --git a/src/Plugins/RpcServer/ParameterConverter.cs b/src/Plugins/RpcServer/ParameterConverter.cs index a6d1cdab59..e8fdb39252 100644 --- a/src/Plugins/RpcServer/ParameterConverter.cs +++ b/src/Plugins/RpcServer/ParameterConverter.cs @@ -299,6 +299,15 @@ internal static Address ToAddress(this JToken token, byte version) return new Address(scriptHash, version); } + internal static Address[] ToAddresses(this JToken token, byte version) + { + if (token is null) return null; + if (token is not JArray array) + throw new RpcException(RpcError.InvalidParams.WithData($"Invalid Addresses: {token}")); + + return array.Select(p => ToAddress(p, version)).ToArray(); + } + private static ContractParameter[] ToContractParameters(this JToken token) { if (token is null) return null; diff --git a/src/Plugins/RpcServer/RpcServer.Wallet.cs b/src/Plugins/RpcServer/RpcServer.Wallet.cs index 57b073c0e0..194c02ea3c 100644 --- a/src/Plugins/RpcServer/RpcServer.Wallet.cs +++ b/src/Plugins/RpcServer/RpcServer.Wallet.cs @@ -14,6 +14,7 @@ using Neo.Json; using Neo.Network.P2P.Payloads; using Neo.Persistence; +using Neo.Plugins.RpcServer.Model; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.VM; @@ -24,6 +25,7 @@ using System.IO; using System.Linq; using System.Numerics; +using Address = Neo.Plugins.RpcServer.Model.Address; using Helper = Neo.Wallets.Helper; namespace Neo.Plugins.RpcServer @@ -83,16 +85,16 @@ protected internal virtual JToken CloseWallet() /// Response format: /// {"jsonrpc": "2.0", "id": 1, "result": "A WIF-encoded private key as a string"} /// - /// An 1-element array containing the address(UInt160 or Base58Check address) as a string. + /// The address(UInt160 or Base58Check address) to export the private key for. /// The exported private key as a string. /// Thrown when no wallet is open or the address is invalid. [RpcMethod] - protected internal virtual JToken DumpPrivKey(JArray _params) + protected internal virtual JToken DumpPrivKey(Address address) { CheckWallet(); - var scriptHash = _params[0].AsString().AddressToScriptHash(system.Settings.AddressVersion); - var account = wallet.GetAccount(scriptHash); - if (account is null) throw new RpcException(RpcError.UnknownAccount.WithData($"{scriptHash}")); + + var account = wallet.GetAccount(address.ScriptHash); + if (account is null) throw new RpcException(RpcError.UnknownAccount.WithData($"{address.ScriptHash}")); return account.GetKey().Export(); } @@ -110,7 +112,8 @@ protected internal virtual JToken DumpPrivKey(JArray _params) protected internal virtual JToken GetNewAddress() { CheckWallet(); - WalletAccount account = wallet.CreateAccount(); + + var account = wallet.CreateAccount(); if (wallet is NEP6Wallet nep6) nep6.Save(); return account.Address; @@ -127,17 +130,16 @@ protected internal virtual JToken GetNewAddress() /// "result": {"balance": "0"} // An integer number in string, the balance of the specified asset in the wallet /// } /// - /// An 1-element(UInt160) array containing the asset ID as a string. + /// An 1-element(UInt160) array containing the asset ID as a string. /// A JSON object containing the balance of the specified asset. /// Thrown when no wallet is open or the asset ID is invalid. [RpcMethod] - protected internal virtual JToken GetWalletBalance(JArray _params) + protected internal virtual JToken GetWalletBalance(UInt160 assetId) { CheckWallet(); - UInt160 asset_id = Result.Ok_Or(() => UInt160.Parse(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid asset id: {_params[0]}")); - JObject json = new(); - json["balance"] = wallet.GetAvailable(system.StoreView, asset_id).Value.ToString(); - return json; + + var balance = wallet.GetAvailable(system.StoreView, assetId).Value; + return new JObject { ["balance"] = balance.ToString() }; } /// @@ -155,8 +157,9 @@ protected internal virtual JToken GetWalletBalance(JArray _params) protected internal virtual JToken GetWalletUnclaimedGas() { CheckWallet(); + // Datoshi is the smallest unit of GAS, 1 GAS = 10^8 Datoshi - BigInteger datoshi = BigInteger.Zero; + var datoshi = BigInteger.Zero; using (var snapshot = system.GetSnapshotCache()) { uint height = NativeContract.Ledger.CurrentIndex(snapshot) + 1; @@ -179,15 +182,15 @@ protected internal virtual JToken GetWalletUnclaimedGas() /// "result": {"address": "The Base58Check address", "haskey": true, "label": "The label", "watchonly": false} /// } /// - /// An 1-element(WIF-encoded private key) array containing the private key as a string. + /// The WIF-encoded private key to import. /// A JSON object containing information about the imported account. /// Thrown when no wallet is open or the private key is invalid. [RpcMethod] - protected internal virtual JToken ImportPrivKey(JArray _params) + protected internal virtual JToken ImportPrivKey(string privkey) { CheckWallet(); - string privkey = _params[0].AsString(); - WalletAccount account = wallet.Import(privkey); + + var account = wallet.Import(privkey); if (wallet is NEP6Wallet nep6wallet) nep6wallet.Save(); return new JObject @@ -208,22 +211,15 @@ protected internal virtual JToken ImportPrivKey(JArray _params) /// Response format: /// {"jsonrpc": "2.0", "id": 1, "result": {"networkfee": "The network fee(an integer number in string)"}} /// - /// An array containing the Base64-encoded transaction. + /// The raw transaction to calculate the network fee for. /// A JSON object containing the calculated network fee. /// Thrown when the input parameters are invalid or the transaction is malformed. [RpcMethod] - protected internal virtual JToken CalculateNetworkFee(JArray _params) + protected internal virtual JToken CalculateNetworkFee(byte[] tx) { - if (_params.Count == 0) - { - throw new RpcException(RpcError.InvalidParams.WithData("Params array is empty, need a raw transaction.")); - } - var tx = Result.Ok_Or(() => Convert.FromBase64String(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid tx: {_params[0]}")); - - JObject account = new(); - var networkfee = Helper.CalculateNetworkFee(tx.AsSerializable(), system.StoreView, system.Settings, wallet); - account["networkfee"] = networkfee.ToString(); - return account; + var transaction = Result.Ok_Or(() => tx.AsSerializable(), RpcErrorFactory.InvalidParams("Invalid tx.")); + var networkfee = Helper.CalculateNetworkFee(transaction, system.StoreView, system.Settings, wallet); + return new JObject { ["networkfee"] = networkfee.ToString() }; } /// @@ -245,12 +241,13 @@ protected internal virtual JToken ListAddress() CheckWallet(); return wallet.GetAccounts().Select(p => { - JObject account = new(); - account["address"] = p.Address; - account["haskey"] = p.HasKey; - account["label"] = p.Label; - account["watchonly"] = p.WatchOnly; - return account; + return new JObject + { + ["address"] = p.Address, + ["haskey"] = p.HasKey, + ["label"] = p.Label, + ["watchonly"] = p.WatchOnly + }; }).ToArray(); } @@ -261,20 +258,15 @@ protected internal virtual JToken ListAddress() /// Response format: /// {"jsonrpc": "2.0", "id": 1, "result": true} /// - /// - /// An array containing the following elements: - /// [0]: The path to the wallet file as a string. - /// [1]: The password to open the wallet as a string. - /// + /// The path to the wallet file. + /// The password to open the wallet. /// Returns true if the wallet was successfully opened. /// /// Thrown when the wallet file is not found, the wallet is not supported, or the password is invalid. /// [RpcMethod] - protected internal virtual JToken OpenWallet(JArray _params) + protected internal virtual JToken OpenWallet(string path, string password) { - string path = _params[0].AsString(); - string password = _params[1].AsString(); File.Exists(path).True_Or(RpcError.WalletNotFound); try { @@ -312,7 +304,8 @@ private void ProcessInvokeWithWallet(JObject result, Signer[] signers = null) result["exception"] = GetExceptionMessage(e); return; } - ContractParametersContext context = new(system.StoreView, tx, settings.Network); + + var context = new ContractParametersContext(system.StoreView, tx, settings.Network); wallet.Sign(context); if (context.Completed) { @@ -360,47 +353,33 @@ private void ProcessInvokeWithWallet(JObject result, Signer[] signers = null) /// } /// } /// - /// - /// An array containing the following elements: - /// [0]: The asset ID as a string. - /// [1]: The from address as a string. - /// [2]: The to address as a string. - /// [3]: The amount as a string. - /// [4] (optional): An array of signers, each containing: - /// - The address of the signer as a string. - /// + /// The asset ID as a string. + /// The from address as a string. + /// The to address as a string. + /// The amount as a string. + /// An array of signers, each containing: The address of the signer as a string. /// The transaction details if successful, or the contract parameters if signatures are incomplete. /// Thrown when no wallet is open, parameters are invalid, or there are insufficient funds. [RpcMethod] - protected internal virtual JToken SendFrom(JArray _params) + protected internal virtual JToken SendFrom(UInt160 assetId, Address from, Address to, string amount, Address[] signers = null) { CheckWallet(); - var assetId = Result.Ok_Or(() => UInt160.Parse(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid asset id: {_params[0]}")); - var from = _params[1].AsString().AddressToScriptHash(system.Settings.AddressVersion); - var to = _params[2].AsString().AddressToScriptHash(system.Settings.AddressVersion); using var snapshot = system.GetSnapshotCache(); var descriptor = new AssetDescriptor(snapshot, system.Settings, assetId); - var amount = new BigDecimal(BigInteger.Parse(_params[3].AsString()), descriptor.Decimals); - (amount.Sign > 0).True_Or(RpcErrorFactory.InvalidParams("Amount can't be negative.")); - var signers = _params.Count >= 5 - ? ((JArray)_params[4]).Select(p => new Signer() { Account = p.AsString().AddressToScriptHash(system.Settings.AddressVersion), Scopes = WitnessScope.CalledByEntry }).ToArray() - : null; - var tx = Result.Ok_Or(() => wallet.MakeTransaction(snapshot, - [ - new TransferOutput - { - AssetId = assetId, - Value = amount, - ScriptHash = to - } - ], from, signers), RpcError.InvalidRequest.WithData("Can not process this request.")).NotNull_Or(RpcError.InsufficientFunds); + var amountDecimal = new BigDecimal(BigInteger.Parse(amount), descriptor.Decimals); + (amountDecimal.Sign > 0).True_Or(RpcErrorFactory.InvalidParams("Amount can't be negative.")); + + var calls = signers.ToSigners(WitnessScope.CalledByEntry); + var tx = Result.Ok_Or( + () => wallet.MakeTransaction(snapshot, [new() { AssetId = assetId, Value = amountDecimal, ScriptHash = to.ScriptHash }], from.ScriptHash, calls), + RpcError.InvalidRequest.WithData("Can not process this request.")).NotNull_Or(RpcError.InsufficientFunds); var transContext = new ContractParametersContext(snapshot, tx, settings.Network); wallet.Sign(transContext); - if (!transContext.Completed) - return transContext.ToJson(); + + if (!transContext.Completed) return transContext.ToJson(); tx.Witnesses = transContext.GetWitnesses(); if (tx.Size > 1024) @@ -476,41 +455,46 @@ protected internal virtual JToken SendFrom(JArray _params) protected internal virtual JToken SendMany(JArray _params) { CheckWallet(); - int to_start = 0; + + int toStart = 0; + var addressVersion = system.Settings.AddressVersion; UInt160 from = null; if (_params[0] is JString) { - from = _params[0].AsString().AddressToScriptHash(system.Settings.AddressVersion); - to_start = 1; + from = _params[0].AsString().AddressToScriptHash(addressVersion); + toStart = 1; } - JArray to = Result.Ok_Or(() => (JArray)_params[to_start], RpcError.InvalidParams.WithData($"Invalid 'to' parameter: {_params[to_start]}")); + JArray to = Result.Ok_Or(() => (JArray)_params[toStart], RpcError.InvalidParams.WithData($"Invalid 'to' parameter: {_params[toStart]}")); (to.Count != 0).True_Or(RpcErrorFactory.InvalidParams("Argument 'to' can't be empty.")); - var signers = _params.Count >= to_start + 2 - ? ((JArray)_params[to_start + 1]).Select(p => new Signer() { Account = p.AsString().AddressToScriptHash(system.Settings.AddressVersion), Scopes = WitnessScope.CalledByEntry }).ToArray() + var signers = _params.Count >= toStart + 2 + ? ((JArray)_params[toStart + 1]) + .Select(p => new Signer() { Account = p.ToAddress(addressVersion).ScriptHash, Scopes = WitnessScope.CalledByEntry }) + .ToArray() : null; - TransferOutput[] outputs = new TransferOutput[to.Count]; + var outputs = new TransferOutput[to.Count]; using var snapshot = system.GetSnapshotCache(); for (int i = 0; i < to.Count; i++) { - UInt160 asset_id = UInt160.Parse(to[i]["asset"].AsString()); - AssetDescriptor descriptor = new(snapshot, system.Settings, asset_id); + var assetId = UInt160.Parse(to[i]["asset"].AsString()); + var descriptor = new AssetDescriptor(snapshot, system.Settings, assetId); outputs[i] = new TransferOutput { - AssetId = asset_id, + AssetId = assetId, Value = new BigDecimal(BigInteger.Parse(to[i]["value"].AsString()), descriptor.Decimals), ScriptHash = to[i]["address"].AsString().AddressToScriptHash(system.Settings.AddressVersion) }; - (outputs[i].Value.Sign > 0).True_Or(RpcErrorFactory.InvalidParams($"Amount of '{asset_id}' can't be negative.")); + (outputs[i].Value.Sign > 0).True_Or(RpcErrorFactory.InvalidParams($"Amount of '{assetId}' can't be negative.")); } - Transaction tx = wallet.MakeTransaction(snapshot, outputs, from, signers).NotNull_Or(RpcError.InsufficientFunds); - ContractParametersContext transContext = new(snapshot, tx, settings.Network); + var tx = wallet.MakeTransaction(snapshot, outputs, from, signers).NotNull_Or(RpcError.InsufficientFunds); + var transContext = new ContractParametersContext(snapshot, tx, settings.Network); wallet.Sign(transContext); - if (!transContext.Completed) - return transContext.ToJson(); + + if (!transContext.Completed) return transContext.ToJson(); + tx.Witnesses = transContext.GetWitnesses(); if (tx.Size > 1024) { @@ -551,35 +535,23 @@ protected internal virtual JToken SendMany(JArray _params) /// } /// } /// - /// - /// An array containing the following elements: - /// [0]: The asset ID as a string. - /// [1]: The to address as a string. - /// [2]: The amount as a string. - /// + /// The asset ID as a string. + /// The to address as a string. + /// The amount as a string. /// The transaction details if successful, or the contract parameters if signatures are incomplete. /// Thrown when no wallet is open, parameters are invalid, or there are insufficient funds. [RpcMethod] - protected internal virtual JToken SendToAddress(JArray _params) + protected internal virtual JToken SendToAddress(UInt160 assetId, Address to, string amount) { CheckWallet(); - var assetId = Result.Ok_Or(() => UInt160.Parse(_params[0].AsString()), - RpcError.InvalidParams.WithData($"Invalid asset hash: {_params[0]}")); - var to = _params[1].AsString().AddressToScriptHash(system.Settings.AddressVersion); using var snapshot = system.GetSnapshotCache(); var descriptor = new AssetDescriptor(snapshot, system.Settings, assetId); - var amount = new BigDecimal(BigInteger.Parse(_params[2].AsString()), descriptor.Decimals); - (amount.Sign > 0).True_Or(RpcError.InvalidParams); - var tx = wallet.MakeTransaction(snapshot, - [ - new TransferOutput - { - AssetId = assetId, - Value = amount, - ScriptHash = to - } - ]).NotNull_Or(RpcError.InsufficientFunds); + var amountDecimal = new BigDecimal(BigInteger.Parse(amount), descriptor.Decimals); + (amountDecimal.Sign > 0).True_Or(RpcErrorFactory.InvalidParams("Amount can't be negative.")); + + var tx = wallet.MakeTransaction(snapshot, [new() { AssetId = assetId, Value = amountDecimal, ScriptHash = to.ScriptHash }]) + .NotNull_Or(RpcError.InsufficientFunds); var transContext = new ContractParametersContext(snapshot, tx, settings.Network); wallet.Sign(transContext); @@ -630,46 +602,43 @@ protected internal virtual JToken SendToAddress(JArray _params) /// } /// } /// - /// - /// An array containing the following elements: - /// [0]: The transaction ID to cancel as a string. - /// [1]: The signers as an array of strings. - /// [2]: The extra fee as a string. - /// + /// The transaction ID to cancel as a string. + /// The signers as an array of strings. + /// The extra fee as a string. /// The details of the cancellation transaction. /// /// Thrown when no wallet is open, the transaction is already confirmed, or there are insufficient funds for the cancellation fee. /// [RpcMethod] - protected internal virtual JToken CancelTransaction(JArray _params) + protected internal virtual JToken CancelTransaction(UInt256 txid, Address[] signers, string extraFee = null) { CheckWallet(); - var txid = Result.Ok_Or(() => UInt256.Parse(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid txid: {_params[0]}")); - NativeContract.Ledger.GetTransactionState(system.StoreView, txid).Null_Or(RpcErrorFactory.AlreadyExists("This tx is already confirmed, can't be cancelled.")); + NativeContract.Ledger.GetTransactionState(system.StoreView, txid) + .Null_Or(RpcErrorFactory.AlreadyExists("This tx is already confirmed, can't be cancelled.")); + + if (signers is null || signers.Length == 0) throw new RpcException(RpcErrorFactory.BadRequest("No signer.")); var conflict = new TransactionAttribute[] { new Conflicts() { Hash = txid } }; - var signers = _params.Count >= 2 - ? ((JArray)_params[1]).Select(j => new Signer() { Account = j.AsString().AddressToScriptHash(system.Settings.AddressVersion), Scopes = WitnessScope.None }).ToArray() - : []; - signers.Any().True_Or(RpcErrorFactory.BadRequest("No signer.")); - Transaction tx = new Transaction + var noneSigners = signers.ToSigners(WitnessScope.None); + var tx = new Transaction { - Signers = signers, + Signers = noneSigners, Attributes = conflict, - Witnesses = Array.Empty(), + Witnesses = [], }; - tx = Result.Ok_Or(() => wallet.MakeTransaction(system.StoreView, new[] { (byte)OpCode.RET }, signers[0].Account, signers, conflict), RpcError.InsufficientFunds, true); - - if (system.MemPool.TryGetValue(txid, out Transaction conflictTx)) + tx = Result.Ok_Or( + () => wallet.MakeTransaction(system.StoreView, new[] { (byte)OpCode.RET }, noneSigners[0].Account, noneSigners, conflict), + RpcError.InsufficientFunds, true); + if (system.MemPool.TryGetValue(txid, out var conflictTx)) { tx.NetworkFee = Math.Max(tx.NetworkFee, conflictTx.NetworkFee) + 1; } - else if (_params.Count >= 3) + else if (extraFee is not null) { - var extraFee = _params[2].AsString(); - AssetDescriptor descriptor = new(system.StoreView, system.Settings, NativeContract.GAS.Hash); - (BigDecimal.TryParse(extraFee, descriptor.Decimals, out BigDecimal decimalExtraFee) && decimalExtraFee.Sign > 0).True_Or(RpcErrorFactory.InvalidParams("Incorrect amount format.")); + var descriptor = new AssetDescriptor(system.StoreView, system.Settings, NativeContract.GAS.Hash); + (BigDecimal.TryParse(extraFee, descriptor.Decimals, out var decimalExtraFee) && decimalExtraFee.Sign > 0) + .True_Or(RpcErrorFactory.InvalidParams("Incorrect amount format.")); tx.NetworkFee += (long)decimalExtraFee.Value; } @@ -715,29 +684,19 @@ protected internal virtual JToken CancelTransaction(JArray _params) /// } /// } /// - /// - /// An array containing the following elements: - /// [0]: The script hash as a string. - /// [1]: The arguments as an array of strings. - /// [2]: The JSON array of signers and witnesses. Optional. - /// + /// The script hash as a string. + /// The arguments as an array of strings. + /// The JSON array of signers and witnesses. Optional. /// A JSON object containing the result of the verification. /// /// Thrown when the script hash is invalid, the contract is not found, or the verification fails. /// [RpcMethod] - protected internal virtual JToken InvokeContractVerify(JArray _params) + protected internal virtual JToken InvokeContractVerify(UInt160 scriptHash, + ContractParameter[] args = null, SignersAndWitnesses signersAndWitnesses = default) { - var scriptHash = Result.Ok_Or(() => UInt160.Parse(_params[0].AsString()), - RpcError.InvalidParams.WithData($"Invalid script hash: {_params[0]}")); - - var args = _params.Count >= 2 - ? ((JArray)_params[1]).Select(p => ContractParameter.FromJson((JObject)p)).ToArray() - : []; - - var (signers, witnesses) = _params.Count >= 3 - ? ((JArray)_params[2]).ToSignersAndWitnesses(system.Settings.AddressVersion) - : default; + args ??= []; + var (signers, witnesses) = signersAndWitnesses; return GetVerificationResult(scriptHash, args, signers, witnesses); } @@ -752,17 +711,24 @@ protected internal virtual JToken InvokeContractVerify(JArray _params) private JObject GetVerificationResult(UInt160 scriptHash, ContractParameter[] args, Signer[] signers = null, Witness[] witnesses = null) { using var snapshot = system.GetSnapshotCache(); - var contract = NativeContract.ContractManagement.GetContract(snapshot, scriptHash).NotNull_Or(RpcError.UnknownContract); - var md = contract.Manifest.Abi.GetMethod(ContractBasicMethod.Verify, args.Count()).NotNull_Or(RpcErrorFactory.InvalidContractVerification(contract.Hash, args.Count())); - (md.ReturnType == ContractParameterType.Boolean).True_Or(RpcErrorFactory.InvalidContractVerification("The verify method doesn't return boolean value.")); - Transaction tx = new() + var contract = NativeContract.ContractManagement.GetContract(snapshot, scriptHash) + .NotNull_Or(RpcError.UnknownContract); + + var md = contract.Manifest.Abi.GetMethod(ContractBasicMethod.Verify, args.Count()) + .NotNull_Or(RpcErrorFactory.InvalidContractVerification(contract.Hash, args.Count())); + + (md.ReturnType == ContractParameterType.Boolean) + .True_Or(RpcErrorFactory.InvalidContractVerification("The verify method doesn't return boolean value.")); + + var tx = new Transaction { - Signers = signers ?? new Signer[] { new() { Account = scriptHash } }, - Attributes = Array.Empty(), + Signers = signers ?? [new() { Account = scriptHash }], + Attributes = [], Witnesses = witnesses, Script = new[] { (byte)OpCode.RET } }; - using ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot.CloneCache(), settings: system.Settings); + + using var engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot.CloneCache(), settings: system.Settings); engine.LoadContract(contract, md, CallFlags.ReadOnly); var invocationScript = Array.Empty(); @@ -773,15 +739,19 @@ private JObject GetVerificationResult(UInt160 scriptHash, ContractParameter[] ar sb.EmitPush(args[i]); invocationScript = sb.ToArray(); - tx.Witnesses ??= new Witness[] { new() { InvocationScript = invocationScript } }; + tx.Witnesses ??= [new() { InvocationScript = invocationScript }]; engine.LoadScript(new Script(invocationScript), configureState: p => p.CallFlags = CallFlags.None); } - JObject json = new(); - json["script"] = Convert.ToBase64String(invocationScript); - json["state"] = engine.Execute(); - // Gas consumed in the unit of datoshi, 1 GAS = 1e8 datoshi - json["gasconsumed"] = engine.FeeConsumed.ToString(); - json["exception"] = GetExceptionMessage(engine.FaultException); + + var json = new JObject() + { + ["script"] = Convert.ToBase64String(invocationScript), + ["state"] = engine.Execute(), + // Gas consumed in the unit of datoshi, 1 GAS = 1e8 datoshi + ["gasconsumed"] = engine.FeeConsumed.ToString(), + ["exception"] = GetExceptionMessage(engine.FaultException) + }; + try { json["stack"] = new JArray(engine.ResultStack.Select(p => p.ToJson(settings.MaxStackSize))); @@ -801,7 +771,7 @@ private JObject GetVerificationResult(UInt160 scriptHash, ContractParameter[] ar /// A JSON object containing the transaction details. private JObject SignAndRelay(DataCache snapshot, Transaction tx) { - ContractParametersContext context = new(snapshot, tx, settings.Network); + var context = new ContractParametersContext(snapshot, tx, settings.Network); wallet.Sign(context); if (context.Completed) { diff --git a/src/Plugins/RpcServer/RpcServer.cs b/src/Plugins/RpcServer/RpcServer.cs index 63968ee5d2..2d72e95b62 100644 --- a/src/Plugins/RpcServer/RpcServer.cs +++ b/src/Plugins/RpcServer/RpcServer.cs @@ -28,7 +28,6 @@ using System.Net.Security; using System.Reflection; using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading.Tasks; using Address = Neo.Plugins.RpcServer.Model.Address; @@ -66,6 +65,7 @@ public RpcServer(NeoSystem system, RpcServersSettings settings) // An address can be either UInt160 or Base58Check format. // If only UInt160 format is allowed, use UInt160 as parameter type. ParameterConverter.RegisterConversion
(token => token.ToAddress(addressVersion)); + ParameterConverter.RegisterConversion(token => token.ToAddresses(addressVersion)); localNode = system.LocalNode.Ask(new LocalNode.GetInstance()).Result; RegisterMethods(this); diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs index f0870c680a..eab4dd3ba2 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs @@ -17,7 +17,6 @@ using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.UnitTests; -using Neo.UnitTests.Extensions; using Neo.VM; using Neo.Wallets; using System; @@ -59,8 +58,7 @@ public void TestOpenWallet() const string Password = "123456"; File.WriteAllText(Path, WalletJson); - var paramsArray = new JArray(Path, Password); - var res = _rpcServer.OpenWallet(paramsArray); + var res = _rpcServer.OpenWallet(Path, Password); Assert.IsTrue(res.AsBoolean()); Assert.IsNotNull(_rpcServer.wallet); Assert.AreEqual("NVizn8DiExdmnpTQfjiVY3dox8uXg3Vrxv", _rpcServer.wallet.GetAccounts().FirstOrDefault()!.Address); @@ -77,20 +75,26 @@ public void TestOpenInvalidWallet() const string Password = "password"; File.Delete(Path); - var paramsArray = new JArray(Path, Password); - var exception = Assert.ThrowsExactly(() => _ = _rpcServer.OpenWallet(paramsArray), "Should throw RpcException for unsupported wallet"); + var exception = Assert.ThrowsExactly( + () => _ = _rpcServer.OpenWallet(Path, Password), + "Should throw RpcException for unsupported wallet"); Assert.AreEqual(RpcError.WalletNotFound.Code, exception.HResult); File.WriteAllText(Path, "{}"); - exception = Assert.ThrowsExactly(() => _ = _rpcServer.OpenWallet(paramsArray), "Should throw RpcException for unsupported wallet"); + exception = Assert.ThrowsExactly( + () => _ = _rpcServer.OpenWallet(Path, Password), + "Should throw RpcException for unsupported wallet"); File.Delete(Path); Assert.AreEqual(RpcError.WalletNotSupported.Code, exception.HResult); + var result = _rpcServer.CloseWallet(); Assert.IsTrue(result.AsBoolean()); Assert.IsNull(_rpcServer.wallet); File.WriteAllText(Path, WalletJson); - exception = Assert.ThrowsExactly(() => _ = _rpcServer.OpenWallet(paramsArray), "Should throw RpcException for unsupported wallet"); + exception = Assert.ThrowsExactly( + () => _ = _rpcServer.OpenWallet(Path, Password), + "Should throw RpcException for unsupported wallet"); Assert.AreEqual(RpcError.WalletNotSupported.Code, exception.HResult); Assert.AreEqual("Wallet not supported - Invalid password.", exception.Message); File.Delete(Path); @@ -102,9 +106,10 @@ public void TestDumpPrivKey() TestUtilOpenWallet(); var account = _rpcServer.wallet.GetAccounts().FirstOrDefault(); Assert.IsNotNull(account); + var privKey = account.GetKey().Export(); var address = account.Address; - var result = _rpcServer.DumpPrivKey(new JArray(address)); + var result = _rpcServer.DumpPrivKey(new JString(address).ToAddress(ProtocolSettings.Default.AddressVersion)); Assert.AreEqual(privKey, result.AsString()); TestUtilCloseWallet(); } @@ -117,9 +122,9 @@ public void TestDumpPrivKey_AddressNotInWallet() var key = new KeyPair(RandomNumberGenerator.GetBytes(32)); // Correct way to get ScriptHash from PublicKey var scriptHashNotInWallet = Contract.CreateSignatureRedeemScript(key.PublicKey).ToScriptHash(); - var addressNotInWallet = scriptHashNotInWallet.ToAddress(ProtocolSettings.Default.AddressVersion); + var notFound = scriptHashNotInWallet.ToAddress(ProtocolSettings.Default.AddressVersion); - var ex = Assert.ThrowsExactly(() => _rpcServer.DumpPrivKey(new JArray(addressNotInWallet))); + var ex = Assert.ThrowsExactly(() => _rpcServer.DumpPrivKey(new JString(notFound).AsParameter
())); Assert.AreEqual(RpcError.UnknownAccount.Code, ex.HResult); Assert.Contains($"Unknown account - {scriptHashNotInWallet}", ex.Message); TestUtilCloseWallet(); @@ -130,7 +135,7 @@ public void TestDumpPrivKey_InvalidAddressFormat() { TestUtilOpenWallet(); var invalidAddress = "NotAValidAddress"; - var ex = Assert.ThrowsExactly(() => _rpcServer.DumpPrivKey(new JArray(invalidAddress))); + var ex = Assert.ThrowsExactly(() => _rpcServer.DumpPrivKey(new JString(invalidAddress).AsParameter
())); TestUtilCloseWallet(); } @@ -149,9 +154,9 @@ public void TestGetWalletBalance() { TestUtilOpenWallet(); var assetId = NativeContract.NEO.Hash; - var paramsArray = new JArray(assetId.ToString()); - var result = _rpcServer.GetWalletBalance(paramsArray); + var result = _rpcServer.GetWalletBalance(assetId); Assert.IsInstanceOfType(result, typeof(JObject)); + var json = (JObject)result; Assert.IsTrue(json.ContainsProperty("balance")); TestUtilCloseWallet(); @@ -162,9 +167,9 @@ public void TestGetWalletBalanceInvalidAsset() { TestUtilOpenWallet(); var assetId = UInt160.Zero; - var paramsArray = new JArray(assetId.ToString()); - var result = _rpcServer.GetWalletBalance(paramsArray); + var result = _rpcServer.GetWalletBalance(assetId); Assert.IsInstanceOfType(result, typeof(JObject)); + var json = (JObject)result; Assert.IsTrue(json.ContainsProperty("balance")); TestUtilCloseWallet(); @@ -175,11 +180,10 @@ public void TestGetWalletBalance_InvalidAssetIdFormat() { TestUtilOpenWallet(); var invalidAssetId = "NotAValidAssetID"; - var paramsArray = new JArray(invalidAssetId); - var ex = Assert.ThrowsExactly(() => _rpcServer.GetWalletBalance(paramsArray)); + var ex = Assert.ThrowsExactly(() => _rpcServer.GetWalletBalance(new JString(invalidAssetId).AsParameter())); Assert.AreEqual(RpcError.InvalidParams.Code, ex.HResult); - Assert.Contains("Invalid asset id", ex.Message); + Assert.Contains("Invalid UInt160", ex.Message); TestUtilCloseWallet(); } @@ -197,9 +201,9 @@ public void TestImportPrivKey() { TestUtilOpenWallet(); var privKey = _walletAccount.GetKey().Export(); - var paramsArray = new JArray(privKey); - var result = _rpcServer.ImportPrivKey(paramsArray); + var result = _rpcServer.ImportPrivKey(privKey); Assert.IsInstanceOfType(result, typeof(JObject)); + var json = (JObject)result; Assert.IsTrue(json.ContainsProperty("address")); Assert.IsTrue(json.ContainsProperty("haskey")); @@ -212,8 +216,7 @@ public void TestImportPrivKey() public void TestImportPrivKeyNoWallet() { var privKey = _walletAccount.GetKey().Export(); - var paramsArray = new JArray(privKey); - var exception = Assert.ThrowsExactly(() => _ = _rpcServer.ImportPrivKey(paramsArray)); + var exception = Assert.ThrowsExactly(() => _ = _rpcServer.ImportPrivKey(privKey)); Assert.AreEqual(exception.HResult, RpcError.NoOpenedWallet.Code); } @@ -222,10 +225,9 @@ public void TestImportPrivKey_InvalidWIF() { TestUtilOpenWallet(); var invalidWif = "ThisIsAnInvalidWIFString"; - var paramsArray = new JArray(invalidWif); // Expect FormatException during WIF decoding - var ex = Assert.ThrowsExactly(() => _rpcServer.ImportPrivKey(paramsArray)); + var ex = Assert.ThrowsExactly(() => _rpcServer.ImportPrivKey(invalidWif)); TestUtilCloseWallet(); } @@ -237,10 +239,9 @@ public void TestImportPrivKey_KeyAlreadyExists() // Get a key already in the default test wallet var existingAccount = _rpcServer.wallet.GetAccounts().First(a => a.HasKey); var existingWif = existingAccount.GetKey().Export(); - var paramsArray = new JArray(existingWif); // Import the existing key - var result = (JObject)_rpcServer.ImportPrivKey(paramsArray); + var result = (JObject)_rpcServer.ImportPrivKey(existingWif); // Verify the returned account details match the existing one Assert.AreEqual(existingAccount.Address, result["address"].AsString()); @@ -260,9 +261,7 @@ public void TestCalculateNetworkFee() { var snapshot = _neoSystem.GetSnapshotCache(); var tx = TestUtils.CreateValidTx(snapshot, _wallet, _walletAccount); - var txBase64 = Convert.ToBase64String(tx.ToArray()); - var paramsArray = new JArray(txBase64); - var result = _rpcServer.CalculateNetworkFee(paramsArray); + var result = _rpcServer.CalculateNetworkFee(tx.ToArray()); Assert.IsInstanceOfType(result, typeof(JObject)); var json = (JObject)result; @@ -299,11 +298,12 @@ public void TestListAddress() public void TestSendFromNoWallet() { var assetId = NativeContract.GAS.Hash; - var from = _walletAccount.Address; - var to = _walletAccount.Address; + var from = new Address(_walletAccount.ScriptHash, ProtocolSettings.Default.AddressVersion); + var to = new Address(_walletAccount.ScriptHash, ProtocolSettings.Default.AddressVersion); var amount = "1"; - var paramsArray = new JArray(assetId.ToString(), from, to, amount); - var exception = Assert.ThrowsExactly(() => _ = _rpcServer.SendFrom(paramsArray), "Should throw RpcException for insufficient funds"); + var exception = Assert.ThrowsExactly( + () => _ = _rpcServer.SendFrom(assetId, from, to, amount), + "Should throw RpcException for insufficient funds"); Assert.AreEqual(exception.HResult, RpcError.NoOpenedWallet.Code); } @@ -311,20 +311,22 @@ public void TestSendFromNoWallet() public void TestSendFrom() { TestUtilOpenWallet(); + var assetId = NativeContract.GAS.Hash; - var from = _walletAccount.Address; - var to = _walletAccount.Address; + var from = new Address(_walletAccount.ScriptHash, ProtocolSettings.Default.AddressVersion); + var to = new Address(_walletAccount.ScriptHash, ProtocolSettings.Default.AddressVersion); var amount = "1"; - var paramsArray = new JArray(assetId.ToString(), from, to, amount); - var exception = Assert.ThrowsExactly(() => _ = _rpcServer.SendFrom(paramsArray)); + var exception = Assert.ThrowsExactly(() => _ = _rpcServer.SendFrom(assetId, from, to, amount)); Assert.AreEqual(exception.HResult, RpcError.InvalidRequest.Code); + TestUtilCloseWallet(); _rpcServer.wallet = _wallet; - JObject resp = (JObject)_rpcServer.SendFrom(paramsArray); + var resp = (JObject)_rpcServer.SendFrom(assetId, from, to, amount); Assert.AreEqual(12, resp.Count); Assert.AreEqual(resp["sender"], ValidatorAddress); - JArray signers = (JArray)resp["signers"]; + + var signers = (JArray)resp["signers"]; Assert.HasCount(1, signers); Assert.AreEqual(signers[0]["account"], ValidatorScriptHash.ToString()); Assert.AreEqual(nameof(WitnessScope.CalledByEntry), signers[0]["scopes"]); @@ -338,15 +340,18 @@ public void TestSendMany() var to = new JArray { new JObject { ["asset"] = NativeContract.GAS.Hash.ToString(), ["value"] = "1", ["address"] = _walletAccount.Address } }; - var paramsArray = new JArray(from, to); - var exception = Assert.ThrowsExactly(() => _ = _rpcServer.SendMany(paramsArray), "Should throw RpcException for insufficient funds"); + + var exception = Assert.ThrowsExactly( + () => _ = _rpcServer.SendMany(new JArray(from, to)), + "Should throw RpcException for insufficient funds"); Assert.AreEqual(exception.HResult, RpcError.NoOpenedWallet.Code); _rpcServer.wallet = _wallet; - JObject resp = (JObject)_rpcServer.SendMany(paramsArray); + var resp = (JObject)_rpcServer.SendMany(new JArray(from, to)); Assert.AreEqual(12, resp.Count); Assert.AreEqual(resp["sender"], ValidatorAddress); - JArray signers = (JArray)resp["signers"]; + + var signers = (JArray)resp["signers"]; Assert.HasCount(1, signers); Assert.AreEqual(signers[0]["account"], ValidatorScriptHash.ToString()); Assert.AreEqual(nameof(WitnessScope.CalledByEntry), signers[0]["scopes"]); @@ -357,17 +362,19 @@ public void TestSendMany() public void TestSendToAddress() { var assetId = NativeContract.GAS.Hash; - var to = _walletAccount.Address; + var to = new Address(_walletAccount.ScriptHash, ProtocolSettings.Default.AddressVersion); var amount = "1"; - var paramsArray = new JArray(assetId.ToString(), to, amount); - var exception = Assert.ThrowsExactly(() => _ = _rpcServer.SendToAddress(paramsArray), "Should throw RpcException for insufficient funds"); + var exception = Assert.ThrowsExactly( + () => _ = _rpcServer.SendToAddress(assetId, to, amount), + "Should throw RpcException for insufficient funds"); Assert.AreEqual(exception.HResult, RpcError.NoOpenedWallet.Code); _rpcServer.wallet = _wallet; - JObject resp = (JObject)_rpcServer.SendToAddress(paramsArray); + var resp = (JObject)_rpcServer.SendToAddress(assetId, to, amount); Assert.AreEqual(12, resp.Count); Assert.AreEqual(resp["sender"], ValidatorAddress); - JArray signers = (JArray)resp["signers"]; + + var signers = (JArray)resp["signers"]; Assert.HasCount(1, signers); Assert.AreEqual(signers[0]["account"], ValidatorScriptHash.ToString()); Assert.AreEqual(nameof(WitnessScope.CalledByEntry), signers[0]["scopes"]); @@ -379,13 +386,13 @@ public void TestSendToAddress_InvalidAssetId() { TestUtilOpenWallet(); var invalidAssetId = "NotAnAssetId"; - var to = _walletAccount.Address; + var to = new Address(_walletAccount.ScriptHash, ProtocolSettings.Default.AddressVersion); var amount = "1"; - var paramsArray = new JArray(invalidAssetId, to, amount); - var ex = Assert.ThrowsExactly(() => _rpcServer.SendToAddress(paramsArray)); + var ex = Assert.ThrowsExactly( + () => _rpcServer.SendToAddress(new JString(invalidAssetId).AsParameter(), to, amount)); Assert.AreEqual(RpcError.InvalidParams.Code, ex.HResult); - Assert.Contains("Invalid asset hash", ex.Message); + Assert.Contains("Invalid UInt160", ex.Message); TestUtilCloseWallet(); } @@ -396,10 +403,12 @@ public void TestSendToAddress_InvalidToAddress() var assetId = NativeContract.GAS.Hash; var invalidToAddress = "NotAnAddress"; var amount = "1"; - var paramsArray = new JArray(assetId.ToString(), invalidToAddress, amount); - var ex = Assert.ThrowsExactly(() => _rpcServer.SendToAddress(paramsArray)); + var ex = Assert.ThrowsExactly( + () => _rpcServer.SendToAddress(assetId, new JString(invalidToAddress).AsParameter
(), amount)); + // Expect FormatException from AddressToScriptHash + Assert.AreEqual(RpcError.InvalidParams.Code, ex.HResult); TestUtilCloseWallet(); } @@ -408,11 +417,10 @@ public void TestSendToAddress_NegativeAmount() { TestUtilOpenWallet(); var assetId = NativeContract.GAS.Hash; - var to = _walletAccount.Address; + var to = new Address(_walletAccount.ScriptHash, ProtocolSettings.Default.AddressVersion); var amount = "-1"; - var paramsArray = new JArray(assetId.ToString(), to, amount); - var ex = Assert.ThrowsExactly(() => _rpcServer.SendToAddress(paramsArray)); + var ex = Assert.ThrowsExactly(() => _rpcServer.SendToAddress(assetId, to, amount)); Assert.AreEqual(RpcError.InvalidParams.Code, ex.HResult); TestUtilCloseWallet(); } @@ -422,11 +430,10 @@ public void TestSendToAddress_ZeroAmount() { TestUtilOpenWallet(); var assetId = NativeContract.GAS.Hash; - var to = _walletAccount.Address; + var to = new Address(_walletAccount.ScriptHash, ProtocolSettings.Default.AddressVersion); var amount = "0"; - var paramsArray = new JArray(assetId.ToString(), to, amount); - var ex = Assert.ThrowsExactly(() => _rpcServer.SendToAddress(paramsArray)); + var ex = Assert.ThrowsExactly(() => _rpcServer.SendToAddress(assetId, to, amount)); Assert.AreEqual(RpcError.InvalidParams.Code, ex.HResult); // Implementation checks amount.Sign > 0 TestUtilCloseWallet(); @@ -437,13 +444,13 @@ public void TestSendToAddress_InsufficientFunds() { TestUtilOpenWallet(); var assetId = NativeContract.GAS.Hash; - var to = _walletAccount.Address; + + var to = new Address(_walletAccount.ScriptHash, ProtocolSettings.Default.AddressVersion); var hugeAmount = "100000000000000000"; // Exceeds likely balance - var paramsArray = new JArray(assetId.ToString(), to, hugeAmount); // With a huge amount, MakeTransaction might throw InvalidOperationException internally // before returning null to trigger the InsufficientFunds RpcException. - var ex = Assert.ThrowsExactly(() => _rpcServer.SendToAddress(paramsArray)); + var ex = Assert.ThrowsExactly(() => _rpcServer.SendToAddress(assetId, to, hugeAmount)); TestUtilCloseWallet(); } @@ -455,9 +462,8 @@ public void TestSendMany_InvalidFromAddress() var to = new JArray { new JObject { ["asset"] = NativeContract.GAS.Hash.ToString(), ["value"] = "1", ["address"] = _walletAccount.Address } }; - var paramsArray = new JArray(invalidFrom, to); - var ex = Assert.ThrowsExactly(() => _rpcServer.SendMany(paramsArray)); + var ex = Assert.ThrowsExactly(() => _rpcServer.SendMany(new JArray(invalidFrom, to))); TestUtilCloseWallet(); } @@ -488,7 +494,7 @@ public void TestDumpPrivKey_WhenWalletNotOpen() { _rpcServer.wallet = null; var exception = Assert.ThrowsExactly( - () => _ = _rpcServer.DumpPrivKey(new JArray(_walletAccount.Address)), + () => _ = _rpcServer.DumpPrivKey(new JString(_walletAccount.Address).AsParameter
()), "Should throw RpcException for no opened wallet"); Assert.AreEqual(exception.HResult, RpcError.NoOpenedWallet.Code); } @@ -508,7 +514,7 @@ public void TestGetWalletBalance_WhenWalletNotOpen() { _rpcServer.wallet = null; var exception = Assert.ThrowsExactly( - () => _ = _rpcServer.GetWalletBalance(new JArray(NativeContract.NEO.Hash.ToString())), + () => _ = _rpcServer.GetWalletBalance(NativeContract.NEO.Hash), "Should throw RpcException for no opened wallet"); Assert.AreEqual(exception.HResult, RpcError.NoOpenedWallet.Code); } @@ -529,7 +535,7 @@ public void TestImportPrivKey_WhenWalletNotOpen() _rpcServer.wallet = null; var privKey = _walletAccount.GetKey().Export(); var exception = Assert.ThrowsExactly( - () => _ = _rpcServer.ImportPrivKey(new JArray(privKey)), + () => _ = _rpcServer.ImportPrivKey(privKey), "Should throw RpcException for no opened wallet"); Assert.AreEqual(exception.HResult, RpcError.NoOpenedWallet.Code); } @@ -538,9 +544,8 @@ public void TestImportPrivKey_WhenWalletNotOpen() public void TestCalculateNetworkFee_InvalidTransactionFormat() { var invalidTxBase64 = "invalid_base64"; - var paramsArray = new JArray(invalidTxBase64); var exception = Assert.ThrowsExactly( - () => _ = _rpcServer.CalculateNetworkFee(paramsArray), + () => _ = _rpcServer.CalculateNetworkFee(invalidTxBase64.ToStrictUtf8Bytes()), "Should throw RpcException for invalid transaction format"); Assert.AreEqual(exception.HResult, RpcError.InvalidParams.Code); } @@ -563,49 +568,50 @@ public void TestListAddress_WhenWalletNotOpen() public void TestCancelTransaction() { TestUtilOpenWallet(); + var snapshot = _neoSystem.GetSnapshotCache(); var tx = TestUtils.CreateValidTx(snapshot, _wallet, _walletAccount); snapshot.Commit(); - var paramsArray = new JArray(tx.Hash.ToString(), new JArray(_walletAccount.Address)); + var address = new Address(_walletAccount.ScriptHash, ProtocolSettings.Default.AddressVersion); var exception = Assert.ThrowsExactly( - () => _ = _rpcServer.CancelTransaction(paramsArray), + () => _ = _rpcServer.CancelTransaction(tx.Hash, [address]), "Should throw RpcException for non-existing transaction"); Assert.AreEqual(RpcError.InsufficientFunds.Code, exception.HResult); // Test with invalid transaction id - var invalidParamsArray = new JArray("invalid_txid", new JArray(_walletAccount.Address)); + var invalidTxHash = "invalid_txid"; exception = Assert.ThrowsExactly( - () => _ = _rpcServer.CancelTransaction(invalidParamsArray), + () => _ = _rpcServer.CancelTransaction(new JString(invalidTxHash).AsParameter(), [address]), "Should throw RpcException for invalid txid"); Assert.AreEqual(exception.HResult, RpcError.InvalidParams.Code); // Test with no signer - invalidParamsArray = new JArray(tx.Hash.ToString()); exception = Assert.ThrowsExactly( - () => _ = _rpcServer.CancelTransaction(invalidParamsArray), + () => _ = _rpcServer.CancelTransaction(tx.Hash, []), "Should throw RpcException for invalid txid"); Assert.AreEqual(exception.HResult, RpcError.BadRequest.Code); // Test with null wallet _rpcServer.wallet = null; exception = Assert.ThrowsExactly( - () => _ = _rpcServer.CancelTransaction(paramsArray), + () => _ = _rpcServer.CancelTransaction(tx.Hash, [address]), "Should throw RpcException for no opened wallet"); Assert.AreEqual(exception.HResult, RpcError.NoOpenedWallet.Code); TestUtilCloseWallet(); // Test valid cancel _rpcServer.wallet = _wallet; - var resp = (JObject)_rpcServer.SendFrom([ - NativeContract.GAS.Hash.ToString(), - _walletAccount.Address, - _walletAccount.Address, + var resp = (JObject)_rpcServer.SendFrom( + NativeContract.GAS.Hash, + new Address(_walletAccount.ScriptHash, ProtocolSettings.Default.AddressVersion), + new Address(_walletAccount.ScriptHash, ProtocolSettings.Default.AddressVersion), "1" - ]); + ); - var txHash = resp["hash"].AsString(); - resp = (JObject)_rpcServer.CancelTransaction(new JArray(txHash, new JArray(ValidatorAddress), "1")); + var txHash = resp["hash"]; + resp = (JObject)_rpcServer.CancelTransaction( + txHash.AsParameter(), new JArray(ValidatorAddress).AsParameter(), "1"); Assert.AreEqual(12, resp.Count); Assert.AreEqual(resp["sender"], ValidatorAddress); @@ -621,16 +627,14 @@ public void TestCancelTransaction() public void TestInvokeContractVerify() { var scriptHash = UInt160.Parse("0x70cde1619e405cdef363ab66a1e8dce430d798d5"); - var paramsArray = new JArray(scriptHash.ToString()); var exception = Assert.ThrowsExactly( - () => _ = _rpcServer.InvokeContractVerify(paramsArray), + () => _ = _rpcServer.InvokeContractVerify(scriptHash), "Should throw RpcException for unknown contract"); Assert.AreEqual(exception.HResult, RpcError.UnknownContract.Code); // Test with invalid script hash - var invalidParamsArray = new JArray("invalid_script_hash"); exception = Assert.ThrowsExactly( - () => _ = _rpcServer.InvokeContractVerify(invalidParamsArray), + () => _ = _rpcServer.InvokeContractVerify(new JString("invalid_script_hash").AsParameter()), "Should throw RpcException for invalid script hash"); Assert.AreEqual(exception.HResult, RpcError.InvalidParams.Code); @@ -700,43 +704,47 @@ public void TestInvokeContractVerify() engine.SnapshotCache.Commit(); // invoke verify without signer; should return false - JObject resp = (JObject)_rpcServer.InvokeContractVerify([deployedScriptHash.ToString()]); - Assert.AreEqual(nameof(VMState.HALT), resp["state"]); - Assert.IsFalse(resp["stack"][0]["value"].AsBoolean()); + var resp = (JObject)_rpcServer.InvokeContractVerify(deployedScriptHash); + Assert.AreEqual(resp["state"], nameof(VMState.HALT)); + Assert.AreEqual(false, resp["stack"][0]["value"].AsBoolean()); // invoke verify with signer; should return true - resp = (JObject)_rpcServer.InvokeContractVerify([deployedScriptHash.ToString(), new JArray([]), validatorSigner]); - Assert.AreEqual(nameof(VMState.HALT), resp["state"]); - Assert.IsTrue(resp["stack"][0]["value"].AsBoolean()); + resp = (JObject)_rpcServer.InvokeContractVerify(deployedScriptHash, [], validatorSigner.AsParameter()); + Assert.AreEqual(resp["state"], nameof(VMState.HALT)); + Assert.AreEqual(true, resp["stack"][0]["value"].AsBoolean()); // invoke verify with wrong input value; should FAULT - resp = (JObject)_rpcServer.InvokeContractVerify([ - deployedScriptHash.ToString(), - new JArray([new JObject() { ["type"] = nameof(ContractParameterType.Integer), ["value"] = "0" }]), - validatorSigner - ]); - Assert.AreEqual(nameof(VMState.FAULT), resp["state"]); - Assert.AreEqual("Object reference not set to an instance of an object.", resp["exception"]); + resp = (JObject)_rpcServer.InvokeContractVerify( + deployedScriptHash, + new JArray([ + new JObject() { ["type"] = nameof(ContractParameterType.Integer), ["value"] = "0" } + ]).AsParameter(), + validatorSigner.AsParameter() + ); + Assert.AreEqual(resp["state"], nameof(VMState.FAULT)); + Assert.AreEqual(resp["exception"], "Object reference not set to an instance of an object."); // invoke verify with 1 param and signer; should return true - resp = (JObject)_rpcServer.InvokeContractVerify([ + resp = (JObject)_rpcServer.InvokeContractVerify( deployedScriptHash.ToString(), - new JArray([new JObject() { ["type"] = nameof(ContractParameterType.Integer), ["value"] = "32" }]), - validatorSigner, - ]); - Assert.AreEqual(nameof(VMState.HALT), resp["state"]); - Assert.IsTrue(resp["stack"][0]["value"].AsBoolean()); + new JArray([ + new JObject() { ["type"] = nameof(ContractParameterType.Integer), ["value"] = "32" } + ]).AsParameter(), + validatorSigner.AsParameter() + ); + Assert.AreEqual(resp["state"], nameof(VMState.HALT)); + Assert.AreEqual(true, resp["stack"][0]["value"].AsBoolean()); // invoke verify with 2 param (which does not exist); should throw Exception Assert.ThrowsExactly( - () => _ = _rpcServer.InvokeContractVerify([ + () => _ = _rpcServer.InvokeContractVerify( deployedScriptHash.ToString(), new JArray([ new JObject() { ["type"] = nameof(ContractParameterType.Integer), ["value"] = "32" }, new JObject() { ["type"] = nameof(ContractParameterType.Integer), ["value"] = "32" } - ]), - validatorSigner - ]), + ]).AsParameter(), + validatorSigner.AsParameter() + ), $"Invalid contract verification function - The smart contract {deployedScriptHash} haven't got verify method with 2 input parameters.", [] ); @@ -750,8 +758,7 @@ private void TestUtilOpenWallet([CallerMemberName] string callerMemberName = "") var path = $"wallet_{callerMemberName}.json"; File.WriteAllText(path, WalletJson); - var paramsArray = new JArray(path, Password); - _rpcServer.OpenWallet(paramsArray); + _rpcServer.OpenWallet(path, Password); } private void TestUtilCloseWallet() @@ -761,26 +768,5 @@ private void TestUtilCloseWallet() _rpcServer.CloseWallet(); File.Delete(Path); } - - private UInt160 TestUtilAddTestContract() - { - var state = TestUtils.GetContract(); - var storageKey = new StorageKey - { - Id = state.Id, - Key = new byte[] { 0x01 } - }; - - var storageItem = new StorageItem - { - Value = new byte[] { 0x01, 0x02, 0x03, 0x04 } - }; - - var snapshot = _neoSystem.GetSnapshotCache(); - snapshot.AddContract(state.Hash, state); - snapshot.Add(storageKey, storageItem); - snapshot.Commit(); - return state.Hash; - } } }