diff --git a/src/Plugins/RpcServer/RpcException.cs b/src/Plugins/RpcServer/RpcException.cs index a24d0200da..b829cfe9e1 100644 --- a/src/Plugins/RpcServer/RpcException.cs +++ b/src/Plugins/RpcServer/RpcException.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Json; using System; namespace Neo.Plugins.RpcServer @@ -39,5 +40,17 @@ public static void ThrowIfNull(T value, string paramName, RpcError error) { if (value is null) throw new RpcException(error.WithData($"Parameter '{paramName}' is null")); } + + /// + /// Throws an exception if the number of parameters is less than the required number. + /// + /// The parameters to check. + /// The required number of parameters. + /// The error code to throw. + public static void ThrowIfTooFew(JArray @params, int requiredArgs, RpcError error) + { + if ((@params is null && requiredArgs > 0) || (@params is not null && @params.Count < requiredArgs)) + throw new RpcException(error.WithData($"Too few arguments. Expected at least {requiredArgs} but got {@params?.Count ?? 0}.")); + } } } diff --git a/src/Plugins/RpcServer/RpcServer.SmartContract.cs b/src/Plugins/RpcServer/RpcServer.SmartContract.cs index b552559498..8346076d0f 100644 --- a/src/Plugins/RpcServer/RpcServer.SmartContract.cs +++ b/src/Plugins/RpcServer/RpcServer.SmartContract.cs @@ -236,6 +236,8 @@ private static JObject ToJson(StackItem item, Session session) [RpcMethod] protected internal virtual JToken InvokeFunction(JArray _params) { + RpcException.ThrowIfTooFew(_params, 2, RpcError.InvalidParams); // ScriptHash, Operation + var scriptHash = Result.Ok_Or(() => UInt160.Parse(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid script hash `{_params[0]}`")); @@ -327,6 +329,8 @@ protected internal virtual JToken InvokeFunction(JArray _params) [RpcMethod] protected internal virtual JToken InvokeScript(JArray _params) { + RpcException.ThrowIfTooFew(_params, 1, RpcError.InvalidParams); // Script + var script = Result.Ok_Or(() => Convert.FromBase64String(_params[0].AsString()), RpcError.InvalidParams); var (signers, witnesses) = _params.Count >= 2 ? ((JArray)_params[1]).ToSignersAndWitnesses(system.Settings.AddressVersion) @@ -360,6 +364,8 @@ protected internal virtual JToken InvokeScript(JArray _params) protected internal virtual JToken TraverseIterator(JArray _params) { settings.SessionEnabled.True_Or(RpcError.SessionsDisabled); + RpcException.ThrowIfTooFew(_params, 3, RpcError.InvalidParams); // SessionId, IteratorId, Count + Guid sid = Result.Ok_Or(() => Guid.Parse(_params[0].GetString()), RpcError.InvalidParams.WithData($"Invalid session id {nameof(sid)}")); Guid iid = Result.Ok_Or(() => Guid.Parse(_params[1].GetString()), RpcError.InvalidParams.WithData($"Invliad iterator id {nameof(iid)}")); int count = _params[2].GetInt32(); @@ -400,8 +406,9 @@ protected internal virtual JToken TraverseIterator(JArray _params) protected internal virtual JToken TerminateSession(JArray _params) { settings.SessionEnabled.True_Or(RpcError.SessionsDisabled); - Guid sid = Result.Ok_Or(() => Guid.Parse(_params[0].GetString()), RpcError.InvalidParams.WithData("Invalid session id")); + RpcException.ThrowIfTooFew(_params, 1, RpcError.InvalidParams); // SessionId + var sid = Result.Ok_Or(() => Guid.Parse(_params[0].GetString()), RpcError.InvalidParams.WithData("Invalid session id")); Session session = null; bool result; lock (sessions) @@ -438,6 +445,8 @@ protected internal virtual JToken TerminateSession(JArray _params) [RpcMethod] protected internal virtual JToken GetUnclaimedGas(JArray _params) { + RpcException.ThrowIfTooFew(_params, 1, RpcError.InvalidParams); // Address + var address = Result.Ok_Or(() => _params[0].AsString(), RpcError.InvalidParams.WithData($"Invalid address `{_params[0]}`")); var json = new JObject(); diff --git a/src/Plugins/RpcServer/RpcServer.Utilities.cs b/src/Plugins/RpcServer/RpcServer.Utilities.cs index 79d6a4e993..408c7b616d 100644 --- a/src/Plugins/RpcServer/RpcServer.Utilities.cs +++ b/src/Plugins/RpcServer/RpcServer.Utilities.cs @@ -64,6 +64,8 @@ protected internal virtual JToken ListPlugins(JArray _params) [RpcMethod] protected internal virtual JToken ValidateAddress(JArray _params) { + RpcException.ThrowIfTooFew(_params, 1, RpcError.InvalidParams); // Address + string address = Result.Ok_Or(() => _params[0].AsString(), RpcError.InvalidParams.WithData($"Invlid address format: {_params[0]}")); UInt160 scriptHash; try diff --git a/src/Plugins/RpcServer/RpcServer.Wallet.cs b/src/Plugins/RpcServer/RpcServer.Wallet.cs index 9713009942..fb2b09a60c 100644 --- a/src/Plugins/RpcServer/RpcServer.Wallet.cs +++ b/src/Plugins/RpcServer/RpcServer.Wallet.cs @@ -90,7 +90,9 @@ protected internal virtual JToken CloseWallet(JArray _params) [RpcMethod] protected internal virtual JToken DumpPrivKey(JArray _params) { + RpcException.ThrowIfTooFew(_params, 1, RpcError.InvalidParams); // Address CheckWallet(); + var scriptHash = _params[0].AsString().AddressToScriptHash(system.Settings.AddressVersion); var account = wallet.GetAccount(scriptHash); return account.GetKey().Export(); @@ -133,7 +135,9 @@ protected internal virtual JToken GetNewAddress(JArray _params) [RpcMethod] protected internal virtual JToken GetWalletBalance(JArray _params) { + RpcException.ThrowIfTooFew(_params, 1, RpcError.InvalidParams); // 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(); @@ -186,7 +190,9 @@ protected internal virtual JToken GetWalletUnclaimedGas(JArray _params) [RpcMethod] protected internal virtual JToken ImportPrivKey(JArray _params) { + RpcException.ThrowIfTooFew(_params, 1, RpcError.InvalidParams); // PrivateKey CheckWallet(); + string privkey = _params[0].AsString(); WalletAccount account = wallet.Import(privkey); if (wallet is NEP6Wallet nep6wallet) @@ -215,10 +221,8 @@ protected internal virtual JToken ImportPrivKey(JArray _params) [RpcMethod] protected internal virtual JToken CalculateNetworkFee(JArray _params) { - if (_params.Count == 0) - { - throw new RpcException(RpcError.InvalidParams.WithData("Params array is empty, need a raw transaction.")); - } + RpcException.ThrowIfTooFew(_params, 1, RpcError.InvalidParams); // Tx + var tx = Result.Ok_Or(() => Convert.FromBase64String(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid tx: {_params[0]}")); JObject account = new(); @@ -275,6 +279,8 @@ protected internal virtual JToken ListAddress(JArray _params) [RpcMethod] protected internal virtual JToken OpenWallet(JArray _params) { + RpcException.ThrowIfTooFew(_params, 2, RpcError.InvalidParams); // Path, Password + string path = _params[0].AsString(); string password = _params[1].AsString(); File.Exists(path).True_Or(RpcError.WalletNotFound); @@ -376,7 +382,9 @@ private void ProcessInvokeWithWallet(JObject result, Signer[] signers = null) [RpcMethod] protected internal virtual JToken SendFrom(JArray _params) { + RpcException.ThrowIfTooFew(_params, 4, RpcError.InvalidParams); // AssetId, From, To, Amount 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); @@ -477,7 +485,9 @@ protected internal virtual JToken SendFrom(JArray _params) [RpcMethod] protected internal virtual JToken SendMany(JArray _params) { + RpcException.ThrowIfTooFew(_params, 1, RpcError.InvalidParams); // From CheckWallet(); + int to_start = 0; UInt160 from = null; if (_params[0] is JString) @@ -564,7 +574,9 @@ protected internal virtual JToken SendMany(JArray _params) [RpcMethod] protected internal virtual JToken SendToAddress(JArray _params) { + RpcException.ThrowIfTooFew(_params, 3, RpcError.InvalidParams); // AssetId, To, 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); @@ -645,6 +657,7 @@ protected internal virtual JToken SendToAddress(JArray _params) [RpcMethod] protected internal virtual JToken CancelTransaction(JArray _params) { + RpcException.ThrowIfTooFew(_params, 1, RpcError.InvalidParams); // Txid 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.")); @@ -730,6 +743,8 @@ protected internal virtual JToken CancelTransaction(JArray _params) [RpcMethod] protected internal virtual JToken InvokeContractVerify(JArray _params) { + RpcException.ThrowIfTooFew(_params, 1, RpcError.InvalidParams); // ScriptHash + var scriptHash = Result.Ok_Or(() => UInt160.Parse(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid script hash: {_params[0]}")); diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Node.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Node.cs index 9a087720e7..b7038be811 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Node.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Node.cs @@ -13,7 +13,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Extensions; using Neo.Json; -using Neo.Ledger; using Neo.Network.P2P; using Neo.Network.P2P.Payloads; using Neo.Persistence.Providers; diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs index 3fbd7607b8..85864c2959 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs @@ -495,5 +495,32 @@ public void TestGetUnclaimedGas_InvalidAddress() // The underlying error is likely FormatException during AddressToScriptHash StringAssert.Contains(ex.Message, RpcError.InvalidParams.Message); // Fix based on test output } + + [TestMethod] + public void TestSmartContractToFewArguments() + { + // InvokeFunction + var ex = Assert.ThrowsExactly( + () => _rpcServer.InvokeFunction(new JArray(NeoToken.NEO.Hash.ToString()))); + Assert.AreEqual(RpcError.InvalidParams.Code, ex.HResult); + + + // InvokeScript + ex = Assert.ThrowsExactly(() => _rpcServer.InvokeScript(new JArray())); + Assert.AreEqual(RpcError.InvalidParams.Code, ex.HResult); + + // TraverseIterator + ex = Assert.ThrowsExactly( + () => _rpcServer.TraverseIterator(new JArray(Guid.NewGuid().ToString(), Guid.NewGuid().ToString()))); + Assert.AreEqual(RpcError.InvalidParams.Code, ex.HResult); + + // TerminateSession + ex = Assert.ThrowsExactly(() => _rpcServer.TerminateSession(new JArray())); + Assert.AreEqual(RpcError.InvalidParams.Code, ex.HResult); + + // GetUnclaimedGas + ex = Assert.ThrowsExactly(() => _rpcServer.GetUnclaimedGas(new JArray())); + Assert.AreEqual(RpcError.InvalidParams.Code, ex.HResult); + } } } diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Utilities.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Utilities.cs index cd4d4927c2..a2d62be22f 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Utilities.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Utilities.cs @@ -76,5 +76,12 @@ public void TestValidateAddress_WrongLength() Assert.AreEqual(resp["address"], longAddr); Assert.AreEqual(resp["isvalid"], false); } + + [TestMethod] + public void TestUtilitiesToFewArguments() + { + var ex = Assert.ThrowsExactly(() => _rpcServer.ValidateAddress(new JArray())); + Assert.AreEqual(RpcError.InvalidParams.Code, ex.HResult); + } } } diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs index 6e0b9b611f..c925b90384 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs @@ -14,7 +14,6 @@ using Neo.Json; using Neo.Network.P2P.Payloads; using Neo.SmartContract; -using Neo.SmartContract.Manifest; using Neo.SmartContract.Native; using Neo.UnitTests; using Neo.UnitTests.Extensions; @@ -664,5 +663,49 @@ private UInt160 TestUtilAddTestContract() snapshot.Commit(); return state.Hash; } + + [TestMethod] + public void TestWalletsToFewArguments() + { + // DumpPrivKey + var ex = Assert.ThrowsExactly(() => _rpcServer.DumpPrivKey(new JArray())); + Assert.AreEqual(RpcError.InvalidParams.Code, ex.HResult); + + // GetWalletBalance + ex = Assert.ThrowsExactly(() => _rpcServer.GetWalletBalance(new JArray())); + Assert.AreEqual(RpcError.InvalidParams.Code, ex.HResult); + + // ImportPrivKey + ex = Assert.ThrowsExactly(() => _rpcServer.ImportPrivKey(new JArray())); + Assert.AreEqual(RpcError.InvalidParams.Code, ex.HResult); + + // CalculateNetworkFee + ex = Assert.ThrowsExactly(() => _rpcServer.CalculateNetworkFee(new JArray())); + Assert.AreEqual(RpcError.InvalidParams.Code, ex.HResult); + + // OpenWallet + ex = Assert.ThrowsExactly(() => _rpcServer.OpenWallet(new JArray("path/to/wallet.json"))); + Assert.AreEqual(RpcError.InvalidParams.Code, ex.HResult); + + // SendFrom + ex = Assert.ThrowsExactly(() => _rpcServer.SendFrom(new JArray())); + Assert.AreEqual(RpcError.InvalidParams.Code, ex.HResult); + + // SendMany + ex = Assert.ThrowsExactly(() => _rpcServer.SendMany(new JArray())); + Assert.AreEqual(RpcError.InvalidParams.Code, ex.HResult); + + // SendToAddress + ex = Assert.ThrowsExactly(() => _rpcServer.SendToAddress(new JArray())); + Assert.AreEqual(RpcError.InvalidParams.Code, ex.HResult); + + // CancelTransaction + ex = Assert.ThrowsExactly(() => _rpcServer.CancelTransaction(new JArray())); + Assert.AreEqual(RpcError.InvalidParams.Code, ex.HResult); + + // InvokeContractVerify + ex = Assert.ThrowsExactly(() => _rpcServer.InvokeContractVerify(new JArray())); + Assert.AreEqual(RpcError.InvalidParams.Code, ex.HResult); + } } } diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs index 97479adf4b..139bb06233 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs @@ -37,9 +37,6 @@ public partial class UT_RpcServer private readonly NEP6Wallet _wallet = TestUtils.GenerateTestWallet("123"); private WalletAccount _walletAccount; - const byte NativePrefixAccount = 20; - const byte NativePrefixTotalSupply = 11; - [TestInitialize] public void TestSetup() {