Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement RPC errors proposal #156 #815

Merged
merged 61 commits into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
20d5643
implemente RPC errors proposal #156
Jim8y Aug 29, 2023
8af6a53
fix debug
Jim8y Aug 29, 2023
3128001
Merge branch 'master' into Add-error-codes-and-response-errors
Jim8y Aug 30, 2023
cf687c6
Apply suggestions from code review
shargon Aug 31, 2023
f5d082e
fix typo
Jim8y Sep 1, 2023
45e0a67
Format code
shargon Sep 1, 2023
d43fe32
one class per file
Jim8y Sep 1, 2023
814fda1
Merge branch 'Add-error-codes-and-response-errors' of github.com:Liao…
Jim8y Sep 1, 2023
0afc30e
code format
Jim8y Sep 1, 2023
a5be5b5
Merge branch 'master' into Add-error-codes-and-response-errors
Jim8y Sep 8, 2023
603527f
Clean code
shargon Sep 11, 2023
62c1dd7
Merge branch 'master' into Add-error-codes-and-response-errors
Jim8y Sep 15, 2023
e378e13
Merge branch 'master' into Add-error-codes-and-response-errors
Jim8y Sep 15, 2023
e7fac0c
Merge branch 'master' into Add-error-codes-and-response-errors
shargon Nov 12, 2023
b73dda6
fix error
Jim8y Nov 13, 2023
724bca9
Unify namespace
shargon Nov 13, 2023
c8b6554
Update .editorconfig
shargon Nov 13, 2023
2196c62
adopt old namespace style
Jim8y Nov 13, 2023
98be42f
format
Jim8y Nov 13, 2023
7df4a79
Merge branch 'Add-error-codes-and-response-errors' of github.com:Liao…
Jim8y Nov 13, 2023
41f3e01
Fix .editorconfig
shargon Nov 13, 2023
52e2b6d
Remove .editorconfig
shargon Nov 13, 2023
4024f84
Shargon's review
shargon Nov 13, 2023
ab1fb6b
Merge branches 'Add-error-codes-and-response-errors' and 'Add-error-c…
shargon Nov 13, 2023
6b0a4cd
Optimize code reusing static objects
shargon Nov 13, 2023
4a7ed21
Some uts
shargon Nov 13, 2023
c657526
Fix ut
shargon Nov 13, 2023
2bf8dfb
Remove cast
shargon Nov 13, 2023
c13494f
Remove static
shargon Nov 14, 2023
3a8d279
Comment not used
shargon Nov 14, 2023
4678745
Update src/StateService/StatePlugin.cs
shargon Nov 14, 2023
0fbce32
Some Anna's feedback
shargon Nov 14, 2023
e7a1c35
fix ut
shargon Nov 14, 2023
6bd3174
More info
shargon Nov 14, 2023
f887529
Add . at the end to all of them
shargon Nov 14, 2023
a55a7ea
Merge branch 'master' into Add-error-codes-and-response-errors
shargon Nov 17, 2023
2452299
update error code
Jim8y Nov 22, 2023
4048a69
add 600 error code and add extra parameter checks. update exception t…
Jim8y Nov 22, 2023
8e019f8
optimize code and fix ut
Jim8y Nov 22, 2023
513787f
use result to replace try-catch
Jim8y Nov 22, 2023
c96655b
update result with data parameter
Jim8y Nov 22, 2023
118cd7c
address more anna's comments
Jim8y Nov 22, 2023
ae408c9
fix oracle exception
Jim8y Nov 22, 2023
3191c2f
fix GetRelayResult
Jim8y Nov 22, 2023
c939556
fix iterator with result
Jim8y Nov 22, 2023
daa3f93
Update src/RpcServer/RpcError.cs
Jim8y Nov 22, 2023
46f524b
Update src/RpcServer/RpcServer.Node.cs
Jim8y Nov 22, 2023
015dab1
Update src/RpcServer/Result.cs
Jim8y Nov 24, 2023
ce6c0b0
add more result checking function and simplify the process of checkin…
Jim8y Nov 24, 2023
af121a3
add more params check
Jim8y Nov 24, 2023
635fa16
Merge branch 'master' into Add-error-codes-and-response-errors
Jim8y Dec 13, 2023
88a2e95
update exception
Jim8y Dec 13, 2023
5f0c5bd
Merge branch 'master' into Add-error-codes-and-response-errors
shargon Jan 8, 2024
f6ef535
all good now
Jim8y Jan 10, 2024
9704327
Merge branch 'master' into Add-error-codes-and-response-errors
shargon Jan 10, 2024
fccd69a
runs dotnet format. and fix errors
Jim8y Jan 10, 2024
25016f7
update Can't get next block validators
Jim8y Jan 11, 2024
68f577a
Merge branch 'master' into Add-error-codes-and-response-errors
Jim8y Jan 11, 2024
a744755
Merge branch 'master' into Add-error-codes-and-response-errors
Jim8y Feb 15, 2024
89c125b
Merge branch 'master' into Add-error-codes-and-response-errors
Jim8y Feb 16, 2024
2765606
Merge branch 'master' into Add-error-codes-and-response-errors
Jim8y Mar 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions src/ApplicationLogs/LogReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
20 changes: 9 additions & 11 deletions src/OracleService/OracleService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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))
{
Expand Down
116 changes: 116 additions & 0 deletions src/RpcServer/Result.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// Checks the execution result of a function and throws an exception if it is null or throw an exception.
/// </summary>
/// <param name="function">The function to execute</param>
/// <param name="err">The rpc error</param>
/// <param name="withData">Append extra base exception message</param>
/// <typeparam name="T">The return type</typeparam>
/// <returns>The execution result</returns>
/// <exception cref="RpcException">The Rpc exception</exception>
public static T Ok_Or<T>(this Func<T> 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);
}
}

/// <summary>
/// Checks the execution result and throws an exception if it is null.
/// </summary>
/// <param name="result">The execution result</param>
/// <param name="err">The rpc error</param>
/// <typeparam name="T">The return type</typeparam>
/// <returns>The execution result</returns>
/// <exception cref="RpcException">The Rpc exception</exception>
public static T NotNull_Or<T>(this T result, RpcError err)
{
if (result == null) throw new RpcException(err);
return result;
}

/// <summary>
/// The execution result is true or throws an exception or null.
/// </summary>
/// <param name="function">The function to execute</param>
/// <param name="err">the rpc exception code</param>
/// <returns>the execution result</returns>
/// <exception cref="RpcException">The rpc exception</exception>
public static bool True_Or(Func<bool> function, RpcError err)
{
try
{
var result = function();
if (!result.Equals(true)) throw new RpcException(err);
return result;
}
catch
{
throw new RpcException(err);
}
}

/// <summary>
/// Checks if the execution result is true or throws an exception.
/// </summary>
/// <param name="result">the execution result</param>
/// <param name="err">the rpc exception code</param>
/// <returns>the execution result</returns>
/// <exception cref="RpcException">The rpc exception</exception>
public static bool True_Or(this bool result, RpcError err)
{
if (!result.Equals(true)) throw new RpcException(err);
return result;
}

/// <summary>
/// Checks if the execution result is false or throws an exception.
/// </summary>
/// <param name="result">the execution result</param>
/// <param name="err">the rpc exception code</param>
/// <returns>the execution result</returns>
/// <exception cref="RpcException">The rpc exception</exception>
public static bool False_Or(this bool result, RpcError err)
{
if (!result.Equals(false)) throw new RpcException(err);
return result;
}

/// <summary>
/// Check if the execution result is null or throws an exception.
/// </summary>
/// <param name="result">The execution result</param>
/// <param name="err">the rpc error</param>
/// <typeparam name="T">The execution result type</typeparam>
/// <returns>The execution result</returns>
/// <exception cref="RpcException">the rpc exception</exception>
public static void Null_Or<T>(this T result, RpcError err)
{
if (result != null) throw new RpcException(err);
}
}
}
103 changes: 103 additions & 0 deletions src/RpcServer/RpcError.cs
Original file line number Diff line number Diff line change
@@ -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}";
}
}
43 changes: 43 additions & 0 deletions src/RpcServer/RpcErrorFactory.cs
Original file line number Diff line number Diff line change
@@ -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
}
}
4 changes: 2 additions & 2 deletions src/RpcServer/RpcException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}
Loading
Loading