diff --git a/src/ApplicationLogs/LogReader.cs b/src/ApplicationLogs/LogReader.cs
index 5c42d6a0d..73e4b6adf 100644
--- a/src/ApplicationLogs/LogReader.cs
+++ b/src/ApplicationLogs/LogReader.cs
@@ -59,10 +59,8 @@ protected override void OnSystemLoaded(NeoSystem system)
[RpcMethod]
public JToken GetApplicationLog(JArray _params)
{
- UInt256 hash = UInt256.Parse(_params[0].AsString());
- byte[] value = _db.TryGet(hash.ToArray());
- if (value is null)
- throw new RpcException(-100, "Unknown transaction/blockhash");
+ UInt256 hash = Result.Ok_Or(() => UInt256.Parse(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid transaction hash: {_params[0]}"));
+ byte[] value = _db.TryGet(hash.ToArray()).NotNull_Or(RpcError.UnknownScriptContainer);
JObject raw = (JObject)JToken.Parse(Neo.Utility.StrictUTF8.GetString(value));
//Additional optional "trigger" parameter to getapplicationlog for clients to be able to get just one execution result for a block.
diff --git a/src/OracleService/OracleService.cs b/src/OracleService/OracleService.cs
index 8959b028c..528485ccc 100644
--- a/src/OracleService/OracleService.cs
+++ b/src/OracleService/OracleService.cs
@@ -220,24 +220,22 @@ private async void OnTimer(object state)
[RpcMethod]
public JObject SubmitOracleResponse(JArray _params)
{
- if (status != OracleStatus.Running) throw new InvalidOperationException();
+ status.Equals(OracleStatus.Running).True_Or(RpcError.OracleDisabled);
ECPoint oraclePub = ECPoint.DecodePoint(Convert.FromBase64String(_params[0].AsString()), ECCurve.Secp256r1);
- ulong requestId = (ulong)_params[1].AsNumber();
- byte[] txSign = Convert.FromBase64String(_params[2].AsString());
- byte[] msgSign = Convert.FromBase64String(_params[3].AsString());
+ ulong requestId = Result.Ok_Or(() => (ulong)_params[1].AsNumber(), RpcError.InvalidParams.WithData($"Invalid requestId: {_params[1]}"));
+ byte[] txSign = Result.Ok_Or(() => Convert.FromBase64String(_params[2].AsString()), RpcError.InvalidParams.WithData($"Invalid txSign: {_params[2]}"));
+ byte[] msgSign = Result.Ok_Or(() => Convert.FromBase64String(_params[3].AsString()), RpcError.InvalidParams.WithData($"Invalid msgSign: {_params[3]}"));
- if (finishedCache.ContainsKey(requestId)) throw new RpcException(-100, "Request has already finished");
+ finishedCache.ContainsKey(requestId).False_Or(RpcError.OracleRequestFinished);
using (var snapshot = System.GetSnapshot())
{
uint height = NativeContract.Ledger.CurrentIndex(snapshot) + 1;
var oracles = NativeContract.RoleManagement.GetDesignatedByRole(snapshot, Role.Oracle, height);
- if (!oracles.Any(p => p.Equals(oraclePub))) throw new RpcException(-100, $"{oraclePub} isn't an oracle node");
- if (NativeContract.Oracle.GetRequest(snapshot, requestId) is null)
- throw new RpcException(-100, "Request is not found");
+ oracles.Any(p => p.Equals(oraclePub)).True_Or(RpcErrorFactory.OracleNotDesignatedNode(oraclePub));
+ NativeContract.Oracle.GetRequest(snapshot, requestId).NotNull_Or(RpcError.OracleRequestNotFound);
var data = Neo.Helper.Concat(oraclePub.ToArray(), BitConverter.GetBytes(requestId), txSign);
- if (!Crypto.VerifySignature(data, msgSign, oraclePub)) throw new RpcException(-100, "Invalid sign");
-
+ Crypto.VerifySignature(data, msgSign, oraclePub).True_Or(RpcErrorFactory.InvalidSignature($"Invalid oracle response transaction signature from '{oraclePub}'."));
AddResponseTxSign(snapshot, requestId, oraclePub, txSign);
}
return new JObject();
@@ -497,7 +495,7 @@ private void AddResponseTxSign(DataCache snapshot, ulong requestId, ECPoint orac
else if (Crypto.VerifySignature(task.BackupTx.GetSignData(System.Settings.Network), sign, oraclePub))
task.BackupSigns.TryAdd(oraclePub, sign);
else
- throw new RpcException(-100, "Invalid response transaction sign");
+ throw new RpcException(RpcErrorFactory.InvalidSignature($"Invalid oracle response transaction signature from '{oraclePub}'."));
if (CheckTxSign(snapshot, task.Tx, task.Signs) || CheckTxSign(snapshot, task.BackupTx, task.BackupSigns))
{
diff --git a/src/RpcServer/Result.cs b/src/RpcServer/Result.cs
new file mode 100644
index 000000000..0ead2482c
--- /dev/null
+++ b/src/RpcServer/Result.cs
@@ -0,0 +1,116 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// Result.cs file belongs to the neo project and is free
+// software distributed under the MIT software license, see the
+// accompanying file LICENSE in the main directory of the
+// repository or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+using System;
+namespace Neo.Plugins
+{
+ public static class Result
+ {
+ ///
+ /// Checks the execution result of a function and throws an exception if it is null or throw an exception.
+ ///
+ /// The function to execute
+ /// The rpc error
+ /// Append extra base exception message
+ /// The return type
+ /// The execution result
+ /// The Rpc exception
+ public static T Ok_Or(this Func function, RpcError err, bool withData = false)
+ {
+ try
+ {
+ var result = function();
+ if (result == null) throw new RpcException(err);
+ return result;
+ }
+ catch (Exception ex)
+ {
+ if (withData)
+ throw new RpcException(err.WithData(ex.GetBaseException().Message));
+ throw new RpcException(err);
+ }
+ }
+
+ ///
+ /// Checks the execution result and throws an exception if it is null.
+ ///
+ /// The execution result
+ /// The rpc error
+ /// The return type
+ /// The execution result
+ /// The Rpc exception
+ public static T NotNull_Or(this T result, RpcError err)
+ {
+ if (result == null) throw new RpcException(err);
+ return result;
+ }
+
+ ///
+ /// The execution result is true or throws an exception or null.
+ ///
+ /// The function to execute
+ /// the rpc exception code
+ /// the execution result
+ /// The rpc exception
+ public static bool True_Or(Func function, RpcError err)
+ {
+ try
+ {
+ var result = function();
+ if (!result.Equals(true)) throw new RpcException(err);
+ return result;
+ }
+ catch
+ {
+ throw new RpcException(err);
+ }
+ }
+
+ ///
+ /// Checks if the execution result is true or throws an exception.
+ ///
+ /// the execution result
+ /// the rpc exception code
+ /// the execution result
+ /// The rpc exception
+ public static bool True_Or(this bool result, RpcError err)
+ {
+ if (!result.Equals(true)) throw new RpcException(err);
+ return result;
+ }
+
+ ///
+ /// Checks if the execution result is false or throws an exception.
+ ///
+ /// the execution result
+ /// the rpc exception code
+ /// the execution result
+ /// The rpc exception
+ public static bool False_Or(this bool result, RpcError err)
+ {
+ if (!result.Equals(false)) throw new RpcException(err);
+ return result;
+ }
+
+ ///
+ /// Check if the execution result is null or throws an exception.
+ ///
+ /// The execution result
+ /// the rpc error
+ /// The execution result type
+ /// The execution result
+ /// the rpc exception
+ public static void Null_Or(this T result, RpcError err)
+ {
+ if (result != null) throw new RpcException(err);
+ }
+ }
+}
diff --git a/src/RpcServer/RpcError.cs b/src/RpcServer/RpcError.cs
new file mode 100644
index 000000000..713054018
--- /dev/null
+++ b/src/RpcServer/RpcError.cs
@@ -0,0 +1,103 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// RpcError.cs file belongs to the neo project and is free
+// software distributed under the MIT software license, see the
+// accompanying file LICENSE in the main directory of the
+// repository or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+using Neo.Json;
+
+namespace Neo.Plugins
+{
+ public class RpcError
+ {
+ #region Default Values
+
+ // https://www.jsonrpc.org/specification
+ // | code | message | meaning |
+ // |--------------------|-----------------|-----------------------------------------------------------------------------------|
+ // | -32700 | Parse error | Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text. |
+ // | -32600 | Invalid request | The JSON sent is not a valid Request object. |
+ // | -32601 | Method not found| The method does not exist / is not available. |
+ // | -32602 | Invalid params | Invalid method parameter(s). |
+ // | -32603 | Internal error | Internal JSON-RPC error. |
+ // | -32000 to -32099 | Server error | Reserved for implementation-defined server-errors. |
+ public static readonly RpcError InvalidRequest = new(-32600, "Invalid request");
+ public static readonly RpcError MethodNotFound = new(-32601, "Method not found");
+ public static readonly RpcError InvalidParams = new(-32602, "Invalid params");
+ public static readonly RpcError InternalServerError = new(-32603, "Internal server RpcError");
+ public static readonly RpcError BadRequest = new(-32700, "Bad request");
+
+ // https://github.com/neo-project/proposals/pull/156/files
+ public static readonly RpcError UnknownBlock = new(-101, "Unknown block");
+ public static readonly RpcError UnknownContract = new(-102, "Unknown contract");
+ public static readonly RpcError UnknownTransaction = new(-103, "Unknown transaction");
+ public static readonly RpcError UnknownStorageItem = new(-104, "Unknown storage item");
+ public static readonly RpcError UnknownScriptContainer = new(-105, "Unknown script container");
+ public static readonly RpcError UnknownStateRoot = new(-106, "Unknown state root");
+ public static readonly RpcError UnknownSession = new(-107, "Unknown session");
+ public static readonly RpcError UnknownIterator = new(-108, "Unknown iterator");
+ public static readonly RpcError UnknownHeight = new(-109, "Unknown height");
+
+ public static readonly RpcError InsufficientFundsWallet = new(-300, "Insufficient funds in wallet");
+ public static readonly RpcError WalletFeeLimit = new(-301, "Wallet fee limit exceeded", "The necessary fee is more than the Max_fee, this transaction is failed. Please increase your Max_fee value.");
+ public static readonly RpcError NoOpenedWallet = new(-302, "No opened wallet");
+ public static readonly RpcError WalletNotFound = new(-303, "Wallet not found");
+ public static readonly RpcError WalletNotSupported = new(-304, "Wallet not supported");
+
+ public static readonly RpcError VerificationFailed = new(-500, "Inventory verification failed");
+ public static readonly RpcError AlreadyExists = new(-501, "Inventory already exists");
+ public static readonly RpcError MempoolCapReached = new(-502, "Memory pool capacity reached");
+ public static readonly RpcError AlreadyInPool = new(-503, "Already in pool");
+ public static readonly RpcError InsufficientNetworkFee = new(-504, "Insufficient network fee");
+ public static readonly RpcError PolicyFailed = new(-505, "Policy check failed");
+ public static readonly RpcError InvalidScript = new(-509, "Invalid transaction script");
+ public static readonly RpcError InvalidAttribute = new(-507, "Invalid transaction attribute");
+ public static readonly RpcError InvalidSignature = new(-508, "Invalid signature");
+ public static readonly RpcError InvalidSize = new(-509, "Invalid inventory size");
+ public static readonly RpcError ExpiredTransaction = new(-510, "Expired transaction");
+ public static readonly RpcError InsufficientFunds = new(-511, "Insufficient funds for fee");
+ public static readonly RpcError InvalidContractVerification = new(-512, "Invalid contract verification function");
+
+ public static readonly RpcError AccessDenied = new(-600, "Access denied");
+ public static readonly RpcError SessionsDisabled = new(-601, "State iterator sessions disabled");
+ public static readonly RpcError OracleDisabled = new(-602, "Oracle service disabled");
+ public static readonly RpcError OracleRequestFinished = new(-603, "Oracle request already finished");
+ public static readonly RpcError OracleRequestNotFound = new(-604, "Oracle request not found");
+ public static readonly RpcError OracleNotDesignatedNode = new(-605, "Not a designated oracle node");
+ public static readonly RpcError UnsupportedState = new(-606, "Old state not supported");
+ public static readonly RpcError InvalidProof = new(-607, "Invalid state proof");
+ public static readonly RpcError ExecutionFailed = new(-608, "Contract execution failed");
+
+ #endregion
+
+ public int Code { get; set; }
+ public string Message { get; set; }
+ public string Data { get; set; }
+
+ public RpcError(int code, string message, string data = null)
+ {
+ Code = code;
+ Message = message;
+ Data = data;
+ }
+
+ public override string ToString() => string.IsNullOrEmpty(Data) ? $"{Message} ({Code})" : $"{Message} ({Code}) - {Data}";
+
+ public JToken ToJson()
+ {
+ JObject json = new();
+ json["code"] = Code;
+ json["message"] = ErrorMessage;
+ if (!string.IsNullOrEmpty(Data))
+ json["data"] = Data;
+ return json;
+ }
+
+ public string ErrorMessage => string.IsNullOrEmpty(Data) ? $"{Message}" : $"{Message} - {Data}";
+ }
+}
diff --git a/src/RpcServer/RpcErrorFactory.cs b/src/RpcServer/RpcErrorFactory.cs
new file mode 100644
index 000000000..328ba02f6
--- /dev/null
+++ b/src/RpcServer/RpcErrorFactory.cs
@@ -0,0 +1,43 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// RpcErrorFactory.cs file belongs to the neo project and is free
+// software distributed under the MIT software license, see the
+// accompanying file LICENSE in the main directory of the
+// repository or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+using Neo.Cryptography.ECC;
+
+namespace Neo.Plugins
+{
+ public static class RpcErrorFactory
+ {
+ public static RpcError WithData(this RpcError error, string data = null)
+ {
+ return new RpcError(error.Code, error.Message, data);
+ }
+
+ public static RpcError NewCustomError(int code, string message, string data = null)
+ {
+ return new RpcError(code, message, data);
+ }
+
+ #region Require data
+
+ public static RpcError MethodNotFound(string method) => RpcError.MethodNotFound.WithData($"The method '{method}' doesn't exists.");
+ public static RpcError AlreadyExists(string data) => RpcError.AlreadyExists.WithData(data);
+ public static RpcError InvalidParams(string data) => RpcError.InvalidParams.WithData(data);
+ public static RpcError BadRequest(string data) => RpcError.BadRequest.WithData(data);
+ public static RpcError InsufficientFundsWallet(string data) => RpcError.InsufficientFundsWallet.WithData(data);
+ public static RpcError VerificationFailed(string data) => RpcError.VerificationFailed.WithData(data);
+ public static RpcError InvalidContractVerification(UInt160 contractHash) => RpcError.InvalidContractVerification.WithData($"The smart contract {contractHash} haven't got verify method.");
+ public static RpcError InvalidContractVerification(string data) => RpcError.InvalidContractVerification.WithData(data);
+ public static RpcError InvalidSignature(string data) => RpcError.InvalidSignature.WithData(data);
+ public static RpcError OracleNotDesignatedNode(ECPoint oraclePub) => RpcError.OracleNotDesignatedNode.WithData($"{oraclePub} isn't an oracle node.");
+
+ #endregion
+ }
+}
diff --git a/src/RpcServer/RpcException.cs b/src/RpcServer/RpcException.cs
index 827dc6210..5c7b5675a 100644
--- a/src/RpcServer/RpcException.cs
+++ b/src/RpcServer/RpcException.cs
@@ -15,9 +15,9 @@ namespace Neo.Plugins
{
public class RpcException : Exception
{
- public RpcException(int code, string message) : base(message)
+ public RpcException(RpcError error) : base(error.ErrorMessage)
{
- HResult = code;
+ HResult = error.Code;
}
}
}
diff --git a/src/RpcServer/RpcServer.Blockchain.cs b/src/RpcServer/RpcServer.Blockchain.cs
index 4dced4e59..e381a19ef 100644
--- a/src/RpcServer/RpcServer.Blockchain.cs
+++ b/src/RpcServer/RpcServer.Blockchain.cs
@@ -33,7 +33,7 @@ protected virtual JToken GetBestBlockHash(JArray _params)
[RpcMethod]
protected virtual JToken GetBlock(JArray _params)
{
- JToken key = _params[0];
+ JToken key = Result.Ok_Or(() => _params[0], RpcError.InvalidParams.WithData($"Invalid Block Hash or Index: {_params[0]}"));
bool verbose = _params.Count >= 2 && _params[1].AsBoolean();
using var snapshot = system.GetSnapshot();
Block block;
@@ -47,8 +47,7 @@ protected virtual JToken GetBlock(JArray _params)
UInt256 hash = UInt256.Parse(key.AsString());
block = NativeContract.Ledger.GetBlock(snapshot, hash);
}
- if (block == null)
- throw new RpcException(-100, "Unknown block");
+ block.NotNull_Or(RpcError.UnknownBlock);
if (verbose)
{
JObject json = Utility.BlockToJson(block, system.Settings);
@@ -76,13 +75,13 @@ protected virtual JToken GetBlockCount(JArray _params)
[RpcMethod]
protected virtual JToken GetBlockHash(JArray _params)
{
- uint height = uint.Parse(_params[0].AsString());
+ uint height = Result.Ok_Or(() => uint.Parse(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid Height: {_params[0]}"));
var snapshot = system.StoreView;
if (height <= NativeContract.Ledger.CurrentIndex(snapshot))
{
return NativeContract.Ledger.GetBlockHash(snapshot, height).ToString();
}
- throw new RpcException(-100, "Invalid Height");
+ throw new RpcException(RpcError.UnknownHeight);
}
[RpcMethod]
@@ -95,16 +94,13 @@ protected virtual JToken GetBlockHeader(JArray _params)
if (key is JNumber)
{
uint height = uint.Parse(key.AsString());
- header = NativeContract.Ledger.GetHeader(snapshot, height);
+ header = NativeContract.Ledger.GetHeader(snapshot, height).NotNull_Or(RpcError.UnknownBlock);
}
else
{
UInt256 hash = UInt256.Parse(key.AsString());
- header = NativeContract.Ledger.GetHeader(snapshot, hash);
+ header = NativeContract.Ledger.GetHeader(snapshot, hash).NotNull_Or(RpcError.UnknownBlock);
}
- if (header == null)
- throw new RpcException(-100, "Unknown block");
-
if (verbose)
{
JObject json = header.ToJson(system.Settings);
@@ -124,13 +120,13 @@ protected virtual JToken GetContractState(JArray _params)
if (int.TryParse(_params[0].AsString(), out int contractId))
{
var contracts = NativeContract.ContractManagement.GetContractById(system.StoreView, contractId);
- return contracts?.ToJson() ?? throw new RpcException(-100, "Unknown contract");
+ return contracts?.ToJson().NotNull_Or(RpcError.UnknownContract);
}
else
{
UInt160 script_hash = ToScriptHash(_params[0].AsString());
ContractState contract = NativeContract.ContractManagement.GetContract(system.StoreView, script_hash);
- return contract?.ToJson() ?? throw new RpcException(-100, "Unknown contract");
+ return contract?.ToJson().NotNull_Or(RpcError.UnknownContract);
}
}
@@ -165,14 +161,14 @@ protected virtual JToken GetRawMemPool(JArray _params)
[RpcMethod]
protected virtual JToken GetRawTransaction(JArray _params)
{
- UInt256 hash = UInt256.Parse(_params[0].AsString());
+ UInt256 hash = Result.Ok_Or(() => UInt256.Parse(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid Transaction Hash: {_params[0]}"));
bool verbose = _params.Count >= 2 && _params[1].AsBoolean();
if (system.MemPool.TryGetValue(hash, out Transaction tx) && !verbose)
return Convert.ToBase64String(tx.ToArray());
var snapshot = system.StoreView;
TransactionState state = NativeContract.Ledger.GetTransactionState(snapshot, hash);
tx ??= state?.Transaction;
- if (tx is null) throw new RpcException(-100, "Unknown transaction");
+ tx.NotNull_Or(RpcError.UnknownTransaction);
if (!verbose) return Convert.ToBase64String(tx.ToArray());
JObject json = Utility.TransactionToJson(tx, system.Settings);
if (state is not null)
@@ -192,8 +188,7 @@ protected virtual JToken GetStorage(JArray _params)
if (!int.TryParse(_params[0].AsString(), out int id))
{
UInt160 hash = UInt160.Parse(_params[0].AsString());
- ContractState contract = NativeContract.ContractManagement.GetContract(snapshot, hash);
- if (contract is null) throw new RpcException(-100, "Unknown contract");
+ ContractState contract = NativeContract.ContractManagement.GetContract(snapshot, hash).NotNull_Or(RpcError.UnknownContract);
id = contract.Id;
}
byte[] key = Convert.FromBase64String(_params[1].AsString());
@@ -201,8 +196,7 @@ protected virtual JToken GetStorage(JArray _params)
{
Id = id,
Key = key
- });
- if (item is null) throw new RpcException(-100, "Unknown storage");
+ }).NotNull_Or(RpcError.UnknownStorageItem);
return Convert.ToBase64String(item.Value.Span);
}
@@ -213,8 +207,7 @@ protected virtual JToken FindStorage(JArray _params)
if (!int.TryParse(_params[0].AsString(), out int id))
{
UInt160 hash = UInt160.Parse(_params[0].AsString());
- ContractState contract = NativeContract.ContractManagement.GetContract(snapshot, hash);
- if (contract is null) throw new RpcException(-100, "Unknown contract");
+ ContractState contract = NativeContract.ContractManagement.GetContract(snapshot, hash).NotNull_Or(RpcError.UnknownContract);
id = contract.Id;
}
@@ -259,10 +252,10 @@ protected virtual JToken FindStorage(JArray _params)
[RpcMethod]
protected virtual JToken GetTransactionHeight(JArray _params)
{
- UInt256 hash = UInt256.Parse(_params[0].AsString());
+ UInt256 hash = Result.Ok_Or(() => UInt256.Parse(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid Transaction Hash: {_params[0]}"));
uint? height = NativeContract.Ledger.GetTransactionState(system.StoreView, hash)?.BlockIndex;
if (height.HasValue) return height.Value;
- throw new RpcException(-100, "Unknown transaction");
+ throw new RpcException(RpcError.UnknownTransaction);
}
[RpcMethod]
@@ -288,15 +281,24 @@ protected virtual JToken GetCandidates(JArray _params)
{
script = sb.EmitDynamicCall(NativeContract.NEO.Hash, "getCandidates", null).ToArray();
}
- using ApplicationEngine engine = ApplicationEngine.Run(script, snapshot, settings: system.Settings, gas: settings.MaxGasInvoke);
+ StackItem[] resultstack;
+ try
+ {
+ using ApplicationEngine engine = ApplicationEngine.Run(script, snapshot, settings: system.Settings, gas: settings.MaxGasInvoke);
+ resultstack = engine.ResultStack.ToArray();
+ }
+ catch
+ {
+ throw new RpcException(RpcError.InternalServerError.WithData("Can't get candidates."));
+ }
+
JObject json = new();
try
{
- var resultstack = engine.ResultStack.ToArray();
if (resultstack.Length > 0)
{
JArray jArray = new();
- var validators = NativeContract.NEO.GetNextBlockValidators(snapshot, system.Settings.ValidatorsCount);
+ var validators = NativeContract.NEO.GetNextBlockValidators(snapshot, system.Settings.ValidatorsCount) ?? throw new RpcException(RpcError.InternalServerError.WithData("Can't get next block validators."));
foreach (var item in resultstack)
{
@@ -314,10 +316,11 @@ protected virtual JToken GetCandidates(JArray _params)
}
}
}
- catch (InvalidOperationException)
+ catch
{
- json["exception"] = "Invalid result.";
+ throw new RpcException(RpcError.InternalServerError.WithData("Can't get next block validators"));
}
+
return json;
}
diff --git a/src/RpcServer/RpcServer.Node.cs b/src/RpcServer/RpcServer.Node.cs
index 1b08e62db..c45510080 100644
--- a/src/RpcServer/RpcServer.Node.cs
+++ b/src/RpcServer/RpcServer.Node.cs
@@ -53,15 +53,59 @@ protected virtual JToken GetPeers(JArray _params)
private static JObject GetRelayResult(VerifyResult reason, UInt256 hash)
{
- if (reason == VerifyResult.Succeed)
- {
- var ret = new JObject();
- ret["hash"] = hash.ToString();
- return ret;
- }
- else
+
+ switch (reason)
{
- throw new RpcException(-500, reason.ToString());
+ case VerifyResult.Succeed:
+ {
+ var ret = new JObject();
+ ret["hash"] = hash.ToString();
+ return ret;
+ }
+ case VerifyResult.AlreadyExists:
+ {
+ throw new RpcException(RpcError.AlreadyExists.WithData(reason.ToString()));
+ }
+ case VerifyResult.AlreadyInPool:
+ {
+ throw new RpcException(RpcError.AlreadyInPool.WithData(reason.ToString()));
+ }
+ case VerifyResult.OutOfMemory:
+ {
+ throw new RpcException(RpcError.MempoolCapReached.WithData(reason.ToString()));
+ }
+ case VerifyResult.InvalidScript:
+ {
+ throw new RpcException(RpcError.InvalidScript.WithData(reason.ToString()));
+ }
+ case VerifyResult.InvalidAttribute:
+ {
+ throw new RpcException(RpcError.InvalidAttribute.WithData(reason.ToString()));
+ }
+ case VerifyResult.InvalidSignature:
+ {
+ throw new RpcException(RpcError.InvalidSignature.WithData(reason.ToString()));
+ }
+ case VerifyResult.OverSize:
+ {
+ throw new RpcException(RpcError.InvalidSize.WithData(reason.ToString()));
+ }
+ case VerifyResult.Expired:
+ {
+ throw new RpcException(RpcError.ExpiredTransaction.WithData(reason.ToString()));
+ }
+ case VerifyResult.InsufficientFunds:
+ {
+ throw new RpcException(RpcError.InsufficientFunds.WithData(reason.ToString()));
+ }
+ case VerifyResult.PolicyFail:
+ {
+ throw new RpcException(RpcError.PolicyFailed.WithData(reason.ToString()));
+ }
+ default:
+ {
+ throw new RpcException(RpcError.VerificationFailed.WithData(reason.ToString()));
+ }
}
}
@@ -108,7 +152,7 @@ private static string StripPrefix(string s, string prefix)
[RpcMethod]
protected virtual JToken SendRawTransaction(JArray _params)
{
- Transaction tx = Convert.FromBase64String(_params[0].AsString()).AsSerializable();
+ Transaction tx = Result.Ok_Or(() => Convert.FromBase64String(_params[0].AsString()).AsSerializable(), RpcError.InvalidParams.WithData($"Invalid Transaction Format: {_params[0]}"));
RelayResult reason = system.Blockchain.Ask(tx).Result;
return GetRelayResult(reason.Result, tx.Hash);
}
@@ -116,7 +160,7 @@ protected virtual JToken SendRawTransaction(JArray _params)
[RpcMethod]
protected virtual JToken SubmitBlock(JArray _params)
{
- Block block = Convert.FromBase64String(_params[0].AsString()).AsSerializable();
+ Block block = Result.Ok_Or(() => Convert.FromBase64String(_params[0].AsString()).AsSerializable(), RpcError.InvalidParams.WithData($"Invalid Block Format: {_params[0]}"));
RelayResult reason = system.Blockchain.Ask(block).Result;
return GetRelayResult(reason.Result, block.Hash);
}
diff --git a/src/RpcServer/RpcServer.SmartContract.cs b/src/RpcServer/RpcServer.SmartContract.cs
index 6ccf10965..0fe6fc0c1 100644
--- a/src/RpcServer/RpcServer.SmartContract.cs
+++ b/src/RpcServer/RpcServer.SmartContract.cs
@@ -174,7 +174,7 @@ private static Signer[] SignersFromJson(JArray _params, ProtocolSettings setting
{
if (_params.Count > Transaction.MaxTransactionAttributes)
{
- throw new RpcException(-100, "Max allowed witness exceeded.");
+ throw new RpcException(RpcError.InvalidParams.WithData("Max allowed witness exceeded."));
}
var ret = _params.Select(u => new Signer
@@ -197,7 +197,7 @@ private static Witness[] WitnessesFromJson(JArray _params)
{
if (_params.Count > Transaction.MaxTransactionAttributes)
{
- throw new RpcException(-100, "Max allowed witness exceeded.");
+ throw new RpcException(RpcError.InvalidParams.WithData("Max allowed witness exceeded."));
}
return _params.Select(u => new
@@ -214,8 +214,8 @@ private static Witness[] WitnessesFromJson(JArray _params)
[RpcMethod]
protected virtual JToken InvokeFunction(JArray _params)
{
- UInt160 script_hash = UInt160.Parse(_params[0].AsString());
- string operation = _params[1].AsString();
+ UInt160 script_hash = Result.Ok_Or(() => UInt160.Parse(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid script hash {nameof(script_hash)}"));
+ string operation = Result.Ok_Or(() => _params[1].AsString(), RpcError.InvalidParams);
ContractParameter[] args = _params.Count >= 3 ? ((JArray)_params[2]).Select(p => ContractParameter.FromJson((JObject)p)).ToArray() : System.Array.Empty();
Signer[] signers = _params.Count >= 4 ? SignersFromJson((JArray)_params[3], system.Settings) : null;
Witness[] witnesses = _params.Count >= 4 ? WitnessesFromJson((JArray)_params[3]) : null;
@@ -232,7 +232,7 @@ protected virtual JToken InvokeFunction(JArray _params)
[RpcMethod]
protected virtual JToken InvokeScript(JArray _params)
{
- byte[] script = Convert.FromBase64String(_params[0].AsString());
+ byte[] script = Result.Ok_Or(() => Convert.FromBase64String(_params[0].AsString()), RpcError.InvalidParams);
Signer[] signers = _params.Count >= 2 ? SignersFromJson((JArray)_params[1], system.Settings) : null;
Witness[] witnesses = _params.Count >= 2 ? WitnessesFromJson((JArray)_params[1]) : null;
bool useDiagnostic = _params.Count >= 3 && _params[2].GetBoolean();
@@ -242,18 +242,18 @@ protected virtual JToken InvokeScript(JArray _params)
[RpcMethod]
protected virtual JToken TraverseIterator(JArray _params)
{
- Guid sid = Guid.Parse(_params[0].GetString());
- Guid iid = Guid.Parse(_params[1].GetString());
+ settings.SessionEnabled.True_Or(RpcError.SessionsDisabled);
+ 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();
- if (count > settings.MaxIteratorResultItems)
- throw new ArgumentOutOfRangeException(nameof(count));
+ Result.True_Or(() => count > settings.MaxIteratorResultItems, RpcError.InvalidParams.WithData($"Invalid iterator items count {nameof(count)}"));
Session session;
lock (sessions)
{
- session = sessions[sid];
+ session = Result.Ok_Or(() => sessions[sid], RpcError.UnknownSession);
session.ResetExpiration();
}
- IIterator iterator = session.Iterators[iid];
+ IIterator iterator = Result.Ok_Or(() => session.Iterators[iid], RpcError.UnknownIterator);
JArray json = new();
while (count-- > 0 && iterator.Next())
json.Add(iterator.Value(null).ToJson());
@@ -263,11 +263,15 @@ protected virtual JToken TraverseIterator(JArray _params)
[RpcMethod]
protected virtual JToken TerminateSession(JArray _params)
{
- Guid sid = Guid.Parse(_params[0].GetString());
- Session session;
+ settings.SessionEnabled.True_Or(RpcError.SessionsDisabled);
+ Guid sid = Result.Ok_Or(() => Guid.Parse(_params[0].GetString()), RpcError.InvalidParams.WithData("Invalid session id"));
+
+ Session session = null;
bool result;
lock (sessions)
- result = sessions.Remove(sid, out session);
+ {
+ result = Result.Ok_Or(() => sessions.Remove(sid, out session), RpcError.UnknownSession);
+ }
if (result) session.Dispose();
return result;
}
@@ -275,19 +279,10 @@ protected virtual JToken TerminateSession(JArray _params)
[RpcMethod]
protected virtual JToken GetUnclaimedGas(JArray _params)
{
- string address = _params[0].AsString();
+ string address = Result.Ok_Or(() => _params[0].AsString(), RpcError.InvalidParams.WithData($"Invalid address {nameof(address)}"));
JObject json = new();
- UInt160 script_hash;
- try
- {
- script_hash = AddressToScriptHash(address, system.Settings.AddressVersion);
- }
- catch
- {
- script_hash = null;
- }
- if (script_hash == null)
- throw new RpcException(-100, "Invalid address");
+ UInt160 script_hash = Result.Ok_Or(() => AddressToScriptHash(address, system.Settings.AddressVersion), RpcError.InvalidParams);
+
var snapshot = system.StoreView;
json["unclaimed"] = NativeContract.NEO.UnclaimedGas(snapshot, script_hash, NativeContract.Ledger.CurrentIndex(snapshot) + 1).ToString();
json["address"] = script_hash.ToAddress(system.Settings.AddressVersion);
diff --git a/src/RpcServer/RpcServer.Utilities.cs b/src/RpcServer/RpcServer.Utilities.cs
index 28da86829..f410e01b7 100644
--- a/src/RpcServer/RpcServer.Utilities.cs
+++ b/src/RpcServer/RpcServer.Utilities.cs
@@ -36,7 +36,7 @@ protected virtual JToken ListPlugins(JArray _params)
[RpcMethod]
protected virtual JToken ValidateAddress(JArray _params)
{
- string address = _params[0].AsString();
+ string address = Result.Ok_Or(() => _params[0].AsString(), RpcError.InvalidParams.WithData($"Invlid address format: {_params[0]}"));
JObject json = new();
UInt160 scriptHash;
try
diff --git a/src/RpcServer/RpcServer.Wallet.cs b/src/RpcServer/RpcServer.Wallet.cs
index 92d630061..cc552a58a 100644
--- a/src/RpcServer/RpcServer.Wallet.cs
+++ b/src/RpcServer/RpcServer.Wallet.cs
@@ -52,8 +52,7 @@ public override void Save() { }
private void CheckWallet()
{
- if (wallet is null)
- throw new RpcException(-400, "Access denied");
+ wallet.NotNull_Or(RpcError.NoOpenedWallet);
}
[RpcMethod]
@@ -156,9 +155,8 @@ protected virtual JToken OpenWallet(JArray _params)
{
string path = _params[0].AsString();
string password = _params[1].AsString();
- if (!File.Exists(path)) throw new FileNotFoundException();
- wallet = Wallet.Open(path, password, system.Settings)
- ?? throw new NotSupportedException();
+ File.Exists(path).True_Or(RpcError.WalletNotFound);
+ wallet = Wallet.Open(path, password, system.Settings).NotNull_Or(RpcError.WalletNotSupported);
return true;
}
@@ -200,8 +198,7 @@ protected virtual JToken SendFrom(JArray _params)
using var snapshot = system.GetSnapshot();
AssetDescriptor descriptor = new(snapshot, system.Settings, assetId);
BigDecimal amount = new(BigInteger.Parse(_params[3].AsString()), descriptor.Decimals);
- if (amount.Sign <= 0)
- throw new RpcException(-32602, "Invalid params");
+ (amount.Sign > 0).True_Or(RpcErrorFactory.InvalidParams("Amount can't be negative."));
Signer[] signers = _params.Count >= 5 ? ((JArray)_params[4]).Select(p => new Signer() { Account = AddressToScriptHash(p.AsString(), system.Settings.AddressVersion), Scopes = WitnessScope.CalledByEntry }).ToArray() : null;
Transaction tx = wallet.MakeTransaction(snapshot, new[]
@@ -212,9 +209,7 @@ protected virtual JToken SendFrom(JArray _params)
Value = amount,
ScriptHash = to
}
- }, from, signers);
- if (tx == null)
- throw new RpcException(-300, "Insufficient funds");
+ }, from, signers).NotNull_Or(RpcError.InsufficientFunds);
ContractParametersContext transContext = new(snapshot, tx, settings.Network);
wallet.Sign(transContext);
@@ -227,8 +222,7 @@ protected virtual JToken SendFrom(JArray _params)
if (tx.NetworkFee < calFee)
tx.NetworkFee = calFee;
}
- if (tx.NetworkFee > settings.MaxFee)
- throw new RpcException(-301, "The necessary fee is more than the Max_fee, this transaction is failed. Please increase your Max_fee value.");
+ (tx.NetworkFee <= settings.MaxFee).True_Or(RpcError.WalletFeeLimit);
return SignAndRelay(snapshot, tx);
}
@@ -243,9 +237,8 @@ protected virtual JToken SendMany(JArray _params)
from = AddressToScriptHash(_params[0].AsString(), system.Settings.AddressVersion);
to_start = 1;
}
- JArray to = (JArray)_params[to_start];
- if (to.Count == 0)
- throw new RpcException(-32602, "Invalid params");
+ JArray to = Result.Ok_Or(() => (JArray)_params[to_start], RpcError.InvalidParams.WithData($"Invalid 'to' parameter: {_params[to_start]}"));
+ (to.Count != 0).True_Or(RpcErrorFactory.InvalidParams("Argument 'to' can't be empty."));
Signer[] signers = _params.Count >= to_start + 2 ? ((JArray)_params[to_start + 1]).Select(p => new Signer() { Account = AddressToScriptHash(p.AsString(), system.Settings.AddressVersion), Scopes = WitnessScope.CalledByEntry }).ToArray() : null;
TransferOutput[] outputs = new TransferOutput[to.Count];
@@ -260,12 +253,9 @@ protected virtual JToken SendMany(JArray _params)
Value = new BigDecimal(BigInteger.Parse(to[i]["value"].AsString()), descriptor.Decimals),
ScriptHash = AddressToScriptHash(to[i]["address"].AsString(), system.Settings.AddressVersion)
};
- if (outputs[i].Value.Sign <= 0)
- throw new RpcException(-32602, "Invalid params");
+ (outputs[i].Value.Sign > 0).True_Or(RpcErrorFactory.InvalidParams($"Amount of '{asset_id}' can't be negative."));
}
- Transaction tx = wallet.MakeTransaction(snapshot, outputs, from, signers);
- if (tx == null)
- throw new RpcException(-300, "Insufficient funds");
+ Transaction tx = wallet.MakeTransaction(snapshot, outputs, from, signers).NotNull_Or(RpcError.InsufficientFunds);
ContractParametersContext transContext = new(snapshot, tx, settings.Network);
wallet.Sign(transContext);
@@ -278,8 +268,7 @@ protected virtual JToken SendMany(JArray _params)
if (tx.NetworkFee < calFee)
tx.NetworkFee = calFee;
}
- if (tx.NetworkFee > settings.MaxFee)
- throw new RpcException(-301, "The necessary fee is more than the Max_fee, this transaction is failed. Please increase your Max_fee value.");
+ (tx.NetworkFee <= settings.MaxFee).True_Or(RpcError.WalletFeeLimit);
return SignAndRelay(snapshot, tx);
}
@@ -287,13 +276,12 @@ protected virtual JToken SendMany(JArray _params)
protected virtual JToken SendToAddress(JArray _params)
{
CheckWallet();
- UInt160 assetId = UInt160.Parse(_params[0].AsString());
+ UInt160 assetId = Result.Ok_Or(() => UInt160.Parse(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid asset hash: {_params[0]}"));
UInt160 to = AddressToScriptHash(_params[1].AsString(), system.Settings.AddressVersion);
using var snapshot = system.GetSnapshot();
AssetDescriptor descriptor = new(snapshot, system.Settings, assetId);
BigDecimal amount = new(BigInteger.Parse(_params[2].AsString()), descriptor.Decimals);
- if (amount.Sign <= 0)
- throw new RpcException(-32602, "Invalid params");
+ (amount.Sign > 0).True_Or(RpcError.InvalidParams);
Transaction tx = wallet.MakeTransaction(snapshot, new[]
{
new TransferOutput
@@ -302,9 +290,7 @@ protected virtual JToken SendToAddress(JArray _params)
Value = amount,
ScriptHash = to
}
- });
- if (tx == null)
- throw new RpcException(-300, "Insufficient funds");
+ }).NotNull_Or(RpcError.InsufficientFunds);
ContractParametersContext transContext = new(snapshot, tx, settings.Network);
wallet.Sign(transContext);
@@ -317,8 +303,7 @@ protected virtual JToken SendToAddress(JArray _params)
if (tx.NetworkFee < calFee)
tx.NetworkFee = calFee;
}
- if (tx.NetworkFee > settings.MaxFee)
- throw new RpcException(-301, "The necessary fee is more than the Max_fee, this transaction is failed. Please increase your Max_fee value.");
+ (tx.NetworkFee <= settings.MaxFee).True_Or(RpcError.WalletFeeLimit);
return SignAndRelay(snapshot, tx);
}
@@ -326,20 +311,12 @@ protected virtual JToken SendToAddress(JArray _params)
protected virtual JToken CancelTransaction(JArray _params)
{
CheckWallet();
- var txid = UInt256.Parse(_params[0].AsString());
- TransactionState state = NativeContract.Ledger.GetTransactionState(system.StoreView, txid);
- if (state != null)
- {
- throw new RpcException(32700, "This tx is already confirmed, can't be cancelled.");
- }
+ 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."));
var conflict = new TransactionAttribute[] { new Conflicts() { Hash = txid } };
Signer[] signers = _params.Count >= 2 ? ((JArray)_params[1]).Select(j => new Signer() { Account = AddressToScriptHash(j.AsString(), system.Settings.AddressVersion), Scopes = WitnessScope.None }).ToArray() : Array.Empty();
- if (!signers.Any())
- {
- throw new RpcException(32701, "no signers");
- }
-
+ (!signers.Any()).True_Or(RpcErrorFactory.BadRequest("No signer."));
Transaction tx = new Transaction
{
Signers = signers,
@@ -347,14 +324,7 @@ protected virtual JToken CancelTransaction(JArray _params)
Witnesses = Array.Empty(),
};
- try
- {
- tx = wallet.MakeTransaction(system.StoreView, new[] { (byte)OpCode.RET }, signers[0].Account, signers, conflict);
- }
- catch (InvalidOperationException e)
- {
- throw new RpcException(-500, GetExceptionMessage(e));
- }
+ 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))
{
@@ -364,10 +334,8 @@ protected virtual JToken CancelTransaction(JArray _params)
{
var extraFee = _params[2].AsString();
AssetDescriptor descriptor = new(system.StoreView, system.Settings, NativeContract.GAS.Hash);
- if (!BigDecimal.TryParse(extraFee, descriptor.Decimals, out BigDecimal decimalExtraFee) || decimalExtraFee.Sign <= 0)
- {
- throw new RpcException(32702, "Incorrect Amount Format");
- }
+ (BigDecimal.TryParse(extraFee, descriptor.Decimals, out BigDecimal decimalExtraFee) && decimalExtraFee.Sign > 0).True_Or(RpcErrorFactory.InvalidParams("Incorrect amount format."));
+
tx.NetworkFee += (long)decimalExtraFee.Value;
};
return SignAndRelay(system.StoreView, tx);
@@ -376,7 +344,7 @@ protected virtual JToken CancelTransaction(JArray _params)
[RpcMethod]
protected virtual JToken InvokeContractVerify(JArray _params)
{
- UInt160 script_hash = UInt160.Parse(_params[0].AsString());
+ UInt160 script_hash = Result.Ok_Or(() => UInt160.Parse(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid script hash: {_params[0]}"));
ContractParameter[] args = _params.Count >= 2 ? ((JArray)_params[1]).Select(p => ContractParameter.FromJson((JObject)p)).ToArray() : Array.Empty();
Signer[] signers = _params.Count >= 3 ? SignersFromJson((JArray)_params[2], system.Settings) : null;
Witness[] witnesses = _params.Count >= 3 ? WitnessesFromJson((JArray)_params[2]) : null;
@@ -386,17 +354,9 @@ protected virtual JToken InvokeContractVerify(JArray _params)
private JObject GetVerificationResult(UInt160 scriptHash, ContractParameter[] args, Signer[] signers = null, Witness[] witnesses = null)
{
using var snapshot = system.GetSnapshot();
- var contract = NativeContract.ContractManagement.GetContract(snapshot, scriptHash);
- if (contract is null)
- {
- throw new RpcException(-100, "Unknown contract");
- }
- var md = contract.Manifest.Abi.GetMethod("verify", -1);
- if (md is null)
- throw new RpcException(-101, $"The smart contract {contract.Hash} haven't got verify method.");
- if (md.ReturnType != ContractParameterType.Boolean)
- throw new RpcException(-102, "The verify method doesn't return boolean value.");
-
+ var contract = NativeContract.ContractManagement.GetContract(snapshot, scriptHash).NotNull_Or(RpcError.UnknownContract);
+ var md = contract.Manifest.Abi.GetMethod("verify", -1).NotNull_Or(RpcErrorFactory.InvalidContractVerification(contract.Hash));
+ (md.ReturnType == ContractParameterType.Boolean).True_Or(RpcErrorFactory.InvalidContractVerification("The verify method doesn't return boolean value."));
Transaction tx = new()
{
Signers = signers ?? new Signer[] { new() { Account = scriptHash } },
diff --git a/src/RpcServer/RpcServer.cs b/src/RpcServer/RpcServer.cs
index 8023622f5..f77c2d1e3 100644
--- a/src/RpcServer/RpcServer.cs
+++ b/src/RpcServer/RpcServer.cs
@@ -80,14 +80,10 @@ private bool CheckAuth(HttpContext context)
return authvalues[0] == settings.RpcUser && authvalues[1] == settings.RpcPass;
}
- private static JObject CreateErrorResponse(JToken id, int code, string message, JToken data = null)
+ private static JObject CreateErrorResponse(JToken id, RpcError rpcError)
{
JObject response = CreateResponse(id);
- response["error"] = new JObject();
- response["error"]["code"] = code;
- response["error"]["message"] = message;
- if (data != null)
- response["error"]["data"] = data;
+ response["error"] = rpcError.ToJson();
return response;
}
@@ -238,13 +234,13 @@ public async Task ProcessAsync(HttpContext context)
JToken response;
if (request == null)
{
- response = CreateErrorResponse(null, -32700, "Parse error");
+ response = CreateErrorResponse(null, RpcError.BadRequest);
}
else if (request is JArray array)
{
if (array.Count == 0)
{
- response = CreateErrorResponse(request["id"], -32600, "Invalid Request");
+ response = CreateErrorResponse(request["id"], RpcError.InvalidRequest);
}
else
{
@@ -268,16 +264,14 @@ private async Task ProcessRequestAsync(HttpContext context, JObject req
JToken @params = request["params"] ?? new JArray();
if (!request.ContainsProperty("method") || @params is not JArray)
{
- return CreateErrorResponse(request["id"], -32600, "Invalid Request");
+ return CreateErrorResponse(request["id"], RpcError.InvalidRequest);
}
JObject response = CreateResponse(request["id"]);
try
{
string method = request["method"].AsString();
- if (!CheckAuth(context) || settings.DisabledMethods.Contains(method))
- throw new RpcException(-400, "Access denied");
- if (!methods.TryGetValue(method, out var func))
- throw new RpcException(-32601, "Method not found");
+ (CheckAuth(context) && !settings.DisabledMethods.Contains(method)).True_Or(RpcError.AccessDenied);
+ methods.TryGetValue(method, out var func).True_Or(RpcErrorFactory.MethodNotFound(method));
response["result"] = func((JArray)@params) switch
{
JToken result => result,
@@ -286,20 +280,20 @@ private async Task ProcessRequestAsync(HttpContext context, JObject req
};
return response;
}
- catch (FormatException)
+ catch (FormatException ex)
{
- return CreateErrorResponse(request["id"], -32602, "Invalid params");
+ return CreateErrorResponse(request["id"], RpcError.InvalidParams.WithData(ex.Message));
}
- catch (IndexOutOfRangeException)
+ catch (IndexOutOfRangeException ex)
{
- return CreateErrorResponse(request["id"], -32602, "Invalid params");
+ return CreateErrorResponse(request["id"], RpcError.InvalidParams.WithData(ex.Message));
}
catch (Exception ex)
{
#if DEBUG
- return CreateErrorResponse(request["id"], ex.HResult, ex.Message, ex.StackTrace);
+ return CreateErrorResponse(request["id"], RpcErrorFactory.NewCustomError(ex.HResult, ex.Message, ex.StackTrace));
#else
- return CreateErrorResponse(request["id"], ex.HResult, ex.Message);
+ return CreateErrorResponse(request["id"], RpcErrorFactory.NewCustomError(ex.HResult, ex.Message));
#endif
}
}
diff --git a/src/StateService/StatePlugin.cs b/src/StateService/StatePlugin.cs
index b760eed79..dda1e1aff 100644
--- a/src/StateService/StatePlugin.cs
+++ b/src/StateService/StatePlugin.cs
@@ -179,13 +179,10 @@ private void OnVerifyProof(UInt256 root_hash, string proof)
[RpcMethod]
public JToken GetStateRoot(JArray _params)
{
- uint index = uint.Parse(_params[0].AsString());
+ uint index = Result.Ok_Or(() => uint.Parse(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid state root index: {_params[0]}"));
using var snapshot = StateStore.Singleton.GetSnapshot();
- StateRoot state_root = snapshot.GetStateRoot(index);
- if (state_root is null)
- throw new RpcException(-100, "Unknown state root");
- else
- return state_root.ToJson();
+ StateRoot state_root = snapshot.GetStateRoot(index).NotNull_Or(RpcError.UnknownStateRoot);
+ return state_root.ToJson();
}
private string GetProof(Trie trie, int contract_id, byte[] key)
@@ -200,9 +197,7 @@ private string GetProof(Trie trie, int contract_id, byte[] key)
private string GetProof(Trie trie, StorageKey skey)
{
- var result = trie.TryGetProof(skey.ToArray(), out var proof);
- if (!result) throw new KeyNotFoundException();
-
+ trie.TryGetProof(skey.ToArray(), out var proof).True_Or(RpcError.UnknownStorageItem);
using MemoryStream ms = new();
using BinaryWriter writer = new(ms, Utility.StrictUTF8);
@@ -219,23 +214,19 @@ private string GetProof(Trie trie, StorageKey skey)
private string GetProof(UInt256 root_hash, UInt160 script_hash, byte[] key)
{
- if (!Settings.Default.FullState && StateStore.Singleton.CurrentLocalRootHash != root_hash)
- {
- throw new RpcException(-100, "Old state not supported");
- }
+ (!Settings.Default.FullState && StateStore.Singleton.CurrentLocalRootHash != root_hash).False_Or(RpcError.UnsupportedState);
using var store = StateStore.Singleton.GetStoreSnapshot();
var trie = new Trie(store, root_hash);
- var contract = GetHistoricalContractState(trie, script_hash);
- if (contract is null) throw new RpcException(-100, "Unknown contract");
+ var contract = GetHistoricalContractState(trie, script_hash).NotNull_Or(RpcError.UnknownContract);
return GetProof(trie, contract.Id, key);
}
[RpcMethod]
public JToken GetProof(JArray _params)
{
- UInt256 root_hash = UInt256.Parse(_params[0].AsString());
- UInt160 script_hash = UInt160.Parse(_params[1].AsString());
- byte[] key = Convert.FromBase64String(_params[2].AsString());
+ UInt256 root_hash = Result.Ok_Or(() => UInt256.Parse(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid root hash: {_params[0]}"));
+ UInt160 script_hash = Result.Ok_Or(() => UInt160.Parse(_params[1].AsString()), RpcError.InvalidParams.WithData($"Invalid script hash: {_params[1]}"));
+ byte[] key = Result.Ok_Or(() => Convert.FromBase64String(_params[2].AsString()), RpcError.InvalidParams.WithData($"Invalid key: {_params[2]}"));
return GetProof(root_hash, script_hash, key);
}
@@ -253,16 +244,15 @@ private string VerifyProof(UInt256 root_hash, byte[] proof)
proofs.Add(reader.ReadVarBytes());
}
- var value = Trie.VerifyProof(root_hash, key, proofs);
- if (value is null) throw new RpcException(-100, "Verification failed");
+ var value = Trie.VerifyProof(root_hash, key, proofs).NotNull_Or(RpcError.InvalidProof);
return Convert.ToBase64String(value);
}
[RpcMethod]
public JToken VerifyProof(JArray _params)
{
- UInt256 root_hash = UInt256.Parse(_params[0].AsString());
- byte[] proof_bytes = Convert.FromBase64String(_params[1].AsString());
+ UInt256 root_hash = Result.Ok_Or(() => UInt256.Parse(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid root hash: {_params[0]}"));
+ byte[] proof_bytes = Result.Ok_Or(() => Convert.FromBase64String(_params[1].AsString()), RpcError.InvalidParams.WithData($"Invalid proof: {_params[1]}"));
return VerifyProof(root_hash, proof_bytes);
}
@@ -294,23 +284,21 @@ private StorageKey ParseStorageKey(byte[] data)
[RpcMethod]
public JToken FindStates(JArray _params)
{
- var root_hash = UInt256.Parse(_params[0].AsString());
- if (!Settings.Default.FullState && StateStore.Singleton.CurrentLocalRootHash != root_hash)
- throw new RpcException(-100, "Old state not supported");
- var script_hash = UInt160.Parse(_params[1].AsString());
- var prefix = Convert.FromBase64String(_params[2].AsString());
+ var root_hash = Result.Ok_Or(() => UInt256.Parse(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid root hash: {_params[0]}"));
+ (!Settings.Default.FullState && StateStore.Singleton.CurrentLocalRootHash != root_hash).False_Or(RpcError.UnsupportedState);
+ var script_hash = Result.Ok_Or(() => UInt160.Parse(_params[1].AsString()), RpcError.InvalidParams.WithData($"Invalid script hash: {_params[1]}"));
+ var prefix = Result.Ok_Or(() => Convert.FromBase64String(_params[2].AsString()), RpcError.InvalidParams.WithData($"Invalid prefix: {_params[2]}"));
byte[] key = Array.Empty();
if (3 < _params.Count)
- key = Convert.FromBase64String(_params[3].AsString());
+ key = Result.Ok_Or(() => Convert.FromBase64String(_params[3].AsString()), RpcError.InvalidParams.WithData($"Invalid key: {_params[3]}"));
int count = Settings.Default.MaxFindResultItems;
if (4 < _params.Count)
- count = int.Parse(_params[4].AsString());
+ count = Result.Ok_Or(() => int.Parse(_params[4].AsString()), RpcError.InvalidParams.WithData($"Invalid count: {_params[4]}"));
if (Settings.Default.MaxFindResultItems < count)
count = Settings.Default.MaxFindResultItems;
using var store = StateStore.Singleton.GetStoreSnapshot();
var trie = new Trie(store, root_hash);
- var contract = GetHistoricalContractState(trie, script_hash);
- if (contract is null) throw new RpcException(-100, "Unknown contract");
+ var contract = GetHistoricalContractState(trie, script_hash).NotNull_Or(RpcError.UnknownContract);
StorageKey pkey = new()
{
Id = contract.Id,
@@ -352,16 +340,14 @@ public JToken FindStates(JArray _params)
[RpcMethod]
public JToken GetState(JArray _params)
{
- var root_hash = UInt256.Parse(_params[0].AsString());
- if (!Settings.Default.FullState && StateStore.Singleton.CurrentLocalRootHash != root_hash)
- throw new RpcException(-100, "Old state not supported");
- var script_hash = UInt160.Parse(_params[1].AsString());
- var key = Convert.FromBase64String(_params[2].AsString());
+ var root_hash = Result.Ok_Or(() => UInt256.Parse(_params[0].AsString()), RpcError.InvalidParams.WithData($"Invalid root hash: {_params[0]}"));
+ (!Settings.Default.FullState && StateStore.Singleton.CurrentLocalRootHash != root_hash).False_Or(RpcError.UnsupportedState);
+ var script_hash = Result.Ok_Or(() => UInt160.Parse(_params[1].AsString()), RpcError.InvalidParams.WithData($"Invalid script hash: {_params[1]}"));
+ var key = Result.Ok_Or(() => Convert.FromBase64String(_params[2].AsString()), RpcError.InvalidParams.WithData($"Invalid key: {_params[2]}"));
using var store = StateStore.Singleton.GetStoreSnapshot();
var trie = new Trie(store, root_hash);
- var contract = GetHistoricalContractState(trie, script_hash);
- if (contract is null) throw new RpcException(-100, "Unknown contract");
+ var contract = GetHistoricalContractState(trie, script_hash).NotNull_Or(RpcError.UnknownContract);
StorageKey skey = new()
{
Id = contract.Id,
diff --git a/src/TokensTracker/Trackers/NEP-11/Nep11Tracker.cs b/src/TokensTracker/Trackers/NEP-11/Nep11Tracker.cs
index bc80d77aa..35a579eb7 100644
--- a/src/TokensTracker/Trackers/NEP-11/Nep11Tracker.cs
+++ b/src/TokensTracker/Trackers/NEP-11/Nep11Tracker.cs
@@ -195,14 +195,13 @@ private void RecordTransferHistoryNep11(UInt160 contractHash, UInt160 from, UInt
[RpcMethod]
public JToken GetNep11Transfers(JArray _params)
{
- if (!_shouldTrackHistory) throw new RpcException(-32601, "Method not found");
+ _shouldTrackHistory.True_Or(RpcError.MethodNotFound);
UInt160 userScriptHash = GetScriptHashFromParam(_params[0].AsString());
// If start time not present, default to 1 week of history.
ulong startTime = _params.Count > 1 ? (ulong)_params[1].AsNumber() :
(DateTime.UtcNow - TimeSpan.FromDays(7)).ToTimestampMS();
ulong endTime = _params.Count > 2 ? (ulong)_params[2].AsNumber() : DateTime.UtcNow.ToTimestampMS();
-
- if (endTime < startTime) throw new RpcException(-32602, "Invalid params");
+ (endTime >= startTime).True_Or(RpcError.InvalidParams);
JObject json = new();
json["address"] = userScriptHash.ToAddress(_neoSystem.Settings.AddressVersion);
diff --git a/src/TokensTracker/Trackers/NEP-17/Nep17Tracker.cs b/src/TokensTracker/Trackers/NEP-17/Nep17Tracker.cs
index 989bc6a41..8ea2efa6a 100644
--- a/src/TokensTracker/Trackers/NEP-17/Nep17Tracker.cs
+++ b/src/TokensTracker/Trackers/NEP-17/Nep17Tracker.cs
@@ -73,7 +73,7 @@ public override void OnPersist(NeoSystem system, Block block, DataCache snapshot
}
}
- //update nep17 balance
+ //update nep17 balance
foreach (var balanceChangeRecord in balanceChangeRecords)
{
try
@@ -144,14 +144,14 @@ private void SaveNep17Balance(BalanceChangeRecord balanceChanged, DataCache snap
[RpcMethod]
public JToken GetNep17Transfers(JArray _params)
{
- if (!_shouldTrackHistory) throw new RpcException(-32601, "Method not found");
+ _shouldTrackHistory.True_Or(RpcError.MethodNotFound);
UInt160 userScriptHash = GetScriptHashFromParam(_params[0].AsString());
// If start time not present, default to 1 week of history.
ulong startTime = _params.Count > 1 ? (ulong)_params[1].AsNumber() :
(DateTime.UtcNow - TimeSpan.FromDays(7)).ToTimestampMS();
ulong endTime = _params.Count > 2 ? (ulong)_params[2].AsNumber() : DateTime.UtcNow.ToTimestampMS();
- if (endTime < startTime) throw new RpcException(-32602, "Invalid params");
+ (endTime >= startTime).True_Or(RpcError.InvalidParams);
JObject json = new();
json["address"] = userScriptHash.ToAddress(_neoSystem.Settings.AddressVersion);
diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcError.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcError.cs
new file mode 100644
index 000000000..d3c43a545
--- /dev/null
+++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcError.cs
@@ -0,0 +1,48 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// UT_RpcError.cs file belongs to the neo project and is free
+// software distributed under the MIT software license, see the
+// accompanying file LICENSE in the main directory of the
+// repository or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+
+namespace Neo.Plugins.RpcServer.Tests
+{
+ [TestClass]
+ public class UT_RpcError
+ {
+ [TestMethod]
+ public void AllDifferent()
+ {
+ HashSet codes = new();
+
+ foreach (RpcError error in typeof(RpcError)
+ .GetFields(BindingFlags.Static | BindingFlags.Public)
+ .Where(u => u.DeclaringType == typeof(RpcError))
+ .Select(u => u.GetValue(null))
+ .Cast())
+ {
+ Assert.IsTrue(codes.Add(error.ToString()));
+
+ if (error.Code == RpcError.WalletFeeLimit.Code)
+ Assert.IsNotNull(error.Data);
+ else
+ Assert.IsNull(error.Data);
+ }
+ }
+
+ [TestMethod]
+ public void TestJson()
+ {
+ Assert.AreEqual("{\"code\":-600,\"message\":\"Access denied\"}", RpcError.AccessDenied.ToJson().ToString(false));
+ }
+ }
+}