Skip to content

Commit

Permalink
implemente RPC errors proposal neo-project#156
Browse files Browse the repository at this point in the history
  • Loading branch information
Jim8y committed Aug 29, 2023
1 parent 4610b64 commit 20d5643
Show file tree
Hide file tree
Showing 13 changed files with 250 additions and 57 deletions.
2 changes: 1 addition & 1 deletion src/ApplicationLogs/LogReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ 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");
throw new RpcException(RpcErrorFactor.NewError(RpcErrorCode.UnknownStorageItem));

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
10 changes: 5 additions & 5 deletions src/OracleService/OracleService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -225,17 +225,17 @@ public JObject SubmitOracleResponse(JArray _params)
byte[] txSign = Convert.FromBase64String(_params[2].AsString());
byte[] msgSign = Convert.FromBase64String(_params[3].AsString());

if (finishedCache.ContainsKey(requestId)) throw new RpcException(-100, "Request has already finished");
if (finishedCache.ContainsKey(requestId)) throw new RpcException(RpcErrorFactor.NewError(RpcErrorCode.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 (!oracles.Any(p => p.Equals(oraclePub))) throw new RpcException(RpcErrorFactor.NewError(RpcErrorCode.OracleNotDesignatedNode, $"{oraclePub} isn't an oracle node"));
if (NativeContract.Oracle.GetRequest(snapshot, requestId) is null)
throw new RpcException(-100, "Request is not found");
throw new RpcException(RpcErrorFactor.NewError(RpcErrorCode.OracleRequestNotFound));
var data = Neo.Helper.Concat(oraclePub.ToArray(), BitConverter.GetBytes(requestId), txSign);
if (!Crypto.VerifySignature(data, msgSign, oraclePub)) throw new RpcException(-100, "Invalid sign");
if (!Crypto.VerifySignature(data, msgSign, oraclePub)) throw new RpcException(RpcErrorFactor.NewError(RpcErrorCode.InvalidSignature));

AddResponseTxSign(snapshot, requestId, oraclePub, txSign);
}
Expand Down Expand Up @@ -496,7 +496,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(RpcErrorFactor.NewError(RpcErrorCode.InvalidSignature, "Invalid response transaction sign"));

if (CheckTxSign(snapshot, task.Tx, task.Signs) || CheckTxSign(snapshot, task.BackupTx, task.BackupSigns))
{
Expand Down
130 changes: 130 additions & 0 deletions src/RpcServer/RpcError.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// Copyright (C) 2015-2023 The Neo Project.
//
// The Neo.Network.RPC is free software distributed under the MIT software license,
// see the accompanying file LICENSE in the main directory of the
// project 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.Collections.Generic;
namespace Neo.Plugins;

public class RpcError
{
public int Code { get; set; }
public string Message { get; set; }
public string Data { get; set; }
public RpcError(int code, string message, string data = "")
{
Code = code;
Message = message;
Data = data;
}

public static RpcError ParseError(string data) => new RpcError(RpcErrorCode.BadRequest, "Parse RpcError", data);

// Missing helper methods
public static RpcError InvalidRequestError(string data) => new RpcError(RpcErrorCode.InvalidRequest, "Invalid request", data);

public static RpcError MethodNotFoundError(string data) => new RpcError(RpcErrorCode.MethodNotFound, "Method not found", data);

public static RpcError InvalidParamsError(string data) => new RpcError(RpcErrorCode.InvalidParams, "Invalid params", data);

public static RpcError InternalServerError(string data) => new RpcError(RpcErrorCode.InternalServerError, "Internal RpcError", data);

public static RpcError ErrorWithCode(int code, string message) => new RpcError(code, message);

// Helper to wrap an existing RpcError with data
public static RpcError WrapErrorWithData(RpcError error, string data) => new RpcError(error.Code, error.Message, data);


public override string ToString()
{
if (string.IsNullOrEmpty(Data))
{
return $"{Message} ({Code})";
}
return $"{Message} ({Code}) - {Data}";
}

public string ErrorMessage => string.IsNullOrEmpty(Data) ? $"{Message}" : $"{Message} - {Data}";

}

public static class RpcErrorFactor
{
private static readonly Dictionary<int, string> DefaultMessages = new Dictionary<int, string> {

{RpcErrorCode.InternalServerError, "Internal server RpcError"},
{RpcErrorCode.BadRequest, "Bad request"},
{RpcErrorCode.InvalidRequest, "Invalid request"},
{RpcErrorCode.MethodNotFound, "Method not found"},
{RpcErrorCode.InvalidParams, "Invalid params"},

{RpcErrorCode.UnknownBlock, "Unknown block"},
{RpcErrorCode.UnknownContract, "Unknown contract"},
{RpcErrorCode.UnknownTransaction, "Unknown transaction"},
{RpcErrorCode.UnknownStorageItem, "Unknown storage item"},
{RpcErrorCode.UnknownScriptContainer, "Unknown script container"},
{RpcErrorCode.UnknownStateRoot, "Unknown state root"},
{RpcErrorCode.UnknownSession, "Unknown session"},
{RpcErrorCode.UnknownIterator, "Unknown iterator"},
{RpcErrorCode.UnknownHeight, "Unknown height"},

{RpcErrorCode.InsufficientFundsWallet, "Insufficient funds in wallet"},
{RpcErrorCode.WalletFeeLimit, "Wallet fee limit exceeded"},
{RpcErrorCode.NoOpenedWallet, "No opened wallet"},
{RpcErrorCode.WalletNotFound, "Wallet not found"},
{RpcErrorCode.WalletNotSupported, "Wallet not supported"},

{ RpcErrorCode.AccessDenied, "Access denied"},

{RpcErrorCode.VerificationFailed, "Inventory verification failed"},
{RpcErrorCode.AlreadyExists, "Inventory already exists"},
{RpcErrorCode.MempoolCapReached, "Memory pool capacity reached"},
{RpcErrorCode.AlreadyInPool, "Already in transaction pool"},
{RpcErrorCode.InsufficientNetworkFee, "Insufficient network fee"},
{RpcErrorCode.PolicyFailed, "Policy check failed"},
{RpcErrorCode.InvalidScript, "Invalid transaction script"},
{RpcErrorCode.InvalidAttribute, "Invalid transaction attribute"},
{RpcErrorCode.InvalidSignature, "Invalid transaction signature"},
{RpcErrorCode.InvalidSize, "Invalid inventory size"},
{RpcErrorCode.ExpiredTransaction, "Expired transaction"},
{RpcErrorCode.InsufficientFunds, "Insufficient funds for fee"},
{RpcErrorCode.InvalidVerificationFunction, "Invalid contract verification"},

{RpcErrorCode.SessionsDisabled, "State iterator sessions disabled"},
{RpcErrorCode.OracleDisabled, "Oracle service disabled"},
{RpcErrorCode.OracleRequestFinished, "Oracle request already finished"},
{RpcErrorCode.OracleRequestNotFound, "Oracle request not found"},
{RpcErrorCode.OracleNotDesignatedNode, "Not a designated oracle node"},
{RpcErrorCode.UnsupportedState, "Old state not supported"},
{RpcErrorCode.InvalidProof, "Invalid state proof"},
{RpcErrorCode.ExecutionFailed, "Contract execution failed"}

};

public static RpcError NewError(int code, string message = null, string data = "")
{
message ??= DefaultMessages[code];
return new RpcError(code, message, data);
}

public static RpcError NewCustomError(int code, string message)
{
return new RpcError(code, message, null);
}

public static bool Contains(int code)
{
return DefaultMessages.ContainsKey(code);
}

public static readonly RpcError ErrInvalidParams = NewError(RpcErrorCode.InvalidParams);

public static readonly RpcError ErrUnknownBlock = NewError(RpcErrorCode.UnknownBlock);

public static readonly RpcError ErrUnknownContract = NewError(RpcErrorCode.UnknownContract);
}
63 changes: 63 additions & 0 deletions src/RpcServer/RpcErrorCode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright (C) 2015-2023 The Neo Project.
//
// The Neo.Network.RPC is free software distributed under the MIT software license,
// see the accompanying file LICENSE in the main directory of the
// project 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.Collections.Generic;
namespace Neo.Plugins;
public static class RpcErrorCode
{

public const int InternalServerError = -32603;
public const int BadRequest = -32700;
public const int InvalidRequest = -32600;
public const int MethodNotFound = -32601;
public const int InvalidParams = -32602;

public const int UnknownBlock = -101;
public const int UnknownContract = -102;
public const int UnknownTransaction = -103;
public const int UnknownStorageItem = -104;
public const int UnknownScriptContainer = -105;
public const int UnknownStateRoot = -106;
public const int UnknownSession = -107;
public const int UnknownIterator = -108;
public const int UnknownHeight = -109;

public const int InsufficientFundsWallet = -300;
public const int WalletFeeLimit = -301;
public const int NoOpenedWallet = -302;
public const int WalletNotFound = -303;
public const int WalletNotSupported = -304;

public const int AccessDenied = -400;

public const int VerificationFailed = -500;
public const int AlreadyExists = -501;
public const int MempoolCapReached = -502;
public const int AlreadyInPool = -503;
public const int InsufficientNetworkFee = -504;
public const int PolicyFailed = -505;
public const int InvalidScript = -506;
public const int InvalidAttribute = -507;
public const int InvalidSignature = -508;
public const int InvalidSize = -509;
public const int ExpiredTransaction = -510;
public const int InsufficientFunds = -511;
public const int InvalidVerificationFunction = -512;

public const int SessionsDisabled = -601;
public const int OracleDisabled = -602;
public const int OracleRequestFinished = -603;
public const int OracleRequestNotFound = -604;
public const int OracleNotDesignatedNode = -605;
public const int UnsupportedState = -606;
public const int InvalidProof = -607;
public const int ExecutionFailed = -608;

}
4 changes: 2 additions & 2 deletions src/RpcServer/RpcException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,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 = (int)error.Code;
}
}
}
18 changes: 9 additions & 9 deletions src/RpcServer/RpcServer.Blockchain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ protected virtual JToken GetBlock(JArray _params)
block = NativeContract.Ledger.GetBlock(snapshot, hash);
}
if (block == null)
throw new RpcException(-100, "Unknown block");
throw new RpcException(RpcErrorFactor.NewError(RpcErrorCode.UnknownBlock));
if (verbose)
{
JObject json = Utility.BlockToJson(block, system.Settings);
Expand Down Expand Up @@ -81,7 +81,7 @@ protected virtual JToken GetBlockHash(JArray _params)
{
return NativeContract.Ledger.GetBlockHash(snapshot, height).ToString();
}
throw new RpcException(-100, "Invalid Height");
throw new RpcException(RpcErrorFactor.NewError(RpcErrorCode.UnknownHeight));
}

[RpcMethod]
Expand All @@ -102,7 +102,7 @@ protected virtual JToken GetBlockHeader(JArray _params)
header = NativeContract.Ledger.GetHeader(snapshot, hash);
}
if (header == null)
throw new RpcException(-100, "Unknown block");
throw new RpcException(RpcErrorFactor.NewError(RpcErrorCode.UnknownBlock));

if (verbose)
{
Expand All @@ -122,7 +122,7 @@ protected virtual JToken GetContractState(JArray _params)
{
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() ?? throw new RpcException(RpcErrorFactor.NewError(RpcErrorCode.UnknownContract));
}

private static UInt160 ToScriptHash(string keyword)
Expand Down Expand Up @@ -163,7 +163,7 @@ protected virtual JToken GetRawTransaction(JArray _params)
var snapshot = system.StoreView;
TransactionState state = NativeContract.Ledger.GetTransactionState(snapshot, hash);
tx ??= state?.Transaction;
if (tx is null) throw new RpcException(-100, "Unknown transaction");
if (tx is null) throw new RpcException(RpcErrorFactor.NewError(RpcErrorCode.UnknownTransaction));
if (!verbose) return Convert.ToBase64String(tx.ToArray());
JObject json = Utility.TransactionToJson(tx, system.Settings);
if (state is not null)
Expand All @@ -184,7 +184,7 @@ protected virtual JToken GetStorage(JArray _params)
{
UInt160 hash = UInt160.Parse(_params[0].AsString());
ContractState contract = NativeContract.ContractManagement.GetContract(snapshot, hash);
if (contract is null) throw new RpcException(-100, "Unknown contract");
if (contract is null) throw new RpcException(RpcErrorFactor.NewError(RpcErrorCode.UnknownContract));
id = contract.Id;
}
byte[] key = Convert.FromBase64String(_params[1].AsString());
Expand All @@ -193,7 +193,7 @@ protected virtual JToken GetStorage(JArray _params)
Id = id,
Key = key
});
if (item is null) throw new RpcException(-100, "Unknown storage");
if (item is null) throw new RpcException(RpcErrorFactor.NewError(RpcErrorCode.UnknownStorageItem));
return Convert.ToBase64String(item.Value.Span);
}

Expand All @@ -205,7 +205,7 @@ protected virtual JToken FindStorage(JArray _params)
{
UInt160 hash = UInt160.Parse(_params[0].AsString());
ContractState contract = NativeContract.ContractManagement.GetContract(snapshot, hash);
if (contract is null) throw new RpcException(-100, "Unknown contract");
if (contract is null) throw new RpcException(RpcErrorFactor.NewError(RpcErrorCode.UnknownContract));
id = contract.Id;
}

Expand Down Expand Up @@ -253,7 +253,7 @@ protected virtual JToken GetTransactionHeight(JArray _params)
UInt256 hash = UInt256.Parse(_params[0].AsString());
uint? height = NativeContract.Ledger.GetTransactionState(system.StoreView, hash)?.BlockIndex;
if (height.HasValue) return height.Value;
throw new RpcException(-100, "Unknown transaction");
throw new RpcException(RpcErrorFactor.NewError(RpcErrorCode.UnknownTransaction));
}

[RpcMethod]
Expand Down
2 changes: 1 addition & 1 deletion src/RpcServer/RpcServer.Node.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ private static JObject GetRelayResult(VerifyResult reason, UInt256 hash)
}
else
{
throw new RpcException(-500, reason.ToString());
throw new RpcException(RpcErrorFactor.NewError(RpcErrorCode.VerificationFailed, reason.ToString()));
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/RpcServer/RpcServer.SmartContract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ protected virtual JToken GetUnclaimedGas(JArray _params)
script_hash = null;
}
if (script_hash == null)
throw new RpcException(-100, "Invalid address");
throw new RpcException(RpcErrorFactor.NewError(RpcErrorCode.InvalidParams));//-100, "Invalid address");
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);
Expand Down
Loading

0 comments on commit 20d5643

Please sign in to comment.