mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 02:55:50 +00:00
Construct ErrorCodes lookup table at compile time
This commit is contained in:
committed by
Nik Bougalis
parent
185f2baf76
commit
872478d965
@@ -115,16 +115,23 @@ namespace RPC {
|
|||||||
/** Maps an rpc error code to its token and default message. */
|
/** Maps an rpc error code to its token and default message. */
|
||||||
struct ErrorInfo
|
struct ErrorInfo
|
||||||
{
|
{
|
||||||
ErrorInfo (error_code_i code_, std::string const& token_,
|
// Default ctor needed to produce an empty std::array during constexpr eval.
|
||||||
std::string const& message_)
|
constexpr ErrorInfo ()
|
||||||
|
: code (rpcUNKNOWN)
|
||||||
|
, token ("unknown")
|
||||||
|
, message ("An unknown error code.")
|
||||||
|
{ }
|
||||||
|
|
||||||
|
constexpr ErrorInfo (error_code_i code_, char const* token_,
|
||||||
|
char const* message_)
|
||||||
: code (code_)
|
: code (code_)
|
||||||
, token (token_)
|
, token (token_)
|
||||||
, message (message_)
|
, message (message_)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
error_code_i code;
|
error_code_i code;
|
||||||
std::string token;
|
Json::StaticString token;
|
||||||
std::string message;
|
Json::StaticString message;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Returns an ErrorInfo that reflects the error code. */
|
/** Returns an ErrorInfo that reflects the error code. */
|
||||||
|
|||||||
@@ -17,137 +17,149 @@
|
|||||||
*/
|
*/
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#include <ripple/basics/contract.h>
|
|
||||||
#include <ripple/basics/safe_cast.h>
|
|
||||||
#include <ripple/protocol/ErrorCodes.h>
|
#include <ripple/protocol/ErrorCodes.h>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <unordered_map>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
namespace std {
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct hash <ripple::error_code_i>
|
|
||||||
{
|
|
||||||
explicit hash() = default;
|
|
||||||
|
|
||||||
std::size_t operator() (ripple::error_code_i value) const
|
|
||||||
{
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
namespace RPC {
|
namespace RPC {
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
class ErrorCategory
|
// Unordered array of ErrorInfos, so we don't have to maintain the list
|
||||||
|
// ordering by hand.
|
||||||
|
//
|
||||||
|
// This array will be omitted from the object file; only the sorted version
|
||||||
|
// will remain in the object file. But the string literals will remain.
|
||||||
|
constexpr static ErrorInfo unorderedErrorInfos[]
|
||||||
{
|
{
|
||||||
public:
|
{rpcACT_BITCOIN, "actBitcoin", "Account is bitcoin address."},
|
||||||
using Map = std::unordered_map <error_code_i, ErrorInfo>;
|
{rpcACT_MALFORMED, "actMalformed", "Account malformed."},
|
||||||
|
{rpcACT_NOT_FOUND, "actNotFound", "Account not found."},
|
||||||
ErrorCategory ()
|
{rpcALREADY_MULTISIG, "alreadyMultisig", "Already multisigned."},
|
||||||
: m_unknown (rpcUNKNOWN, "unknown", "An unknown error code.")
|
{rpcALREADY_SINGLE_SIG, "alreadySingleSig", "Already single-signed."},
|
||||||
{
|
{rpcAMENDMENT_BLOCKED, "amendmentBlocked", "Amendment blocked, need upgrade."},
|
||||||
add (rpcACT_BITCOIN, "actBitcoin", "Account is bitcoin address.");
|
{rpcATX_DEPRECATED, "deprecated", "Use the new API or specify a ledger range."},
|
||||||
add (rpcACT_MALFORMED, "actMalformed", "Account malformed.");
|
{rpcBAD_FEATURE, "badFeature", "Feature unknown or invalid."},
|
||||||
add (rpcACT_NOT_FOUND, "actNotFound", "Account not found.");
|
{rpcBAD_ISSUER, "badIssuer", "Issuer account malformed."},
|
||||||
add (rpcALREADY_MULTISIG, "alreadyMultisig", "Already multisigned.");
|
{rpcBAD_MARKET, "badMarket", "No such market."},
|
||||||
add (rpcALREADY_SINGLE_SIG, "alreadySingleSig", "Already single-signed.");
|
{rpcBAD_SECRET, "badSecret", "Secret does not match account."},
|
||||||
add (rpcAMENDMENT_BLOCKED, "amendmentBlocked", "Amendment blocked, need upgrade.");
|
{rpcBAD_SEED, "badSeed", "Disallowed seed."},
|
||||||
add (rpcATX_DEPRECATED, "deprecated", "Use the new API or specify a ledger range.");
|
{rpcBAD_SYNTAX, "badSyntax", "Syntax error."},
|
||||||
add (rpcBAD_FEATURE, "badFeature", "Feature unknown or invalid.");
|
{rpcCHANNEL_MALFORMED, "channelMalformed", "Payment channel is malformed."},
|
||||||
add (rpcBAD_ISSUER, "badIssuer", "Issuer account malformed.");
|
{rpcCHANNEL_AMT_MALFORMED, "channelAmtMalformed", "Payment channel amount is malformed."},
|
||||||
add (rpcBAD_MARKET, "badMarket", "No such market.");
|
{rpcCOMMAND_MISSING, "commandMissing", "Missing command entry."},
|
||||||
add (rpcBAD_SECRET, "badSecret", "Secret does not match account.");
|
{rpcDST_ACT_MALFORMED, "dstActMalformed", "Destination account is malformed."},
|
||||||
add (rpcBAD_SEED, "badSeed", "Disallowed seed.");
|
{rpcDST_ACT_MISSING, "dstActMissing", "Destination account not provided."},
|
||||||
add (rpcBAD_SYNTAX, "badSyntax", "Syntax error.");
|
{rpcDST_ACT_NOT_FOUND, "dstActNotFound", "Destination account not found."},
|
||||||
add (rpcCHANNEL_MALFORMED, "channelMalformed", "Payment channel is malformed.");
|
{rpcDST_AMT_MALFORMED, "dstAmtMalformed", "Destination amount/currency/issuer is malformed."},
|
||||||
add (rpcCHANNEL_AMT_MALFORMED, "channelAmtMalformed", "Payment channel amount is malformed.");
|
{rpcDST_AMT_MISSING, "dstAmtMissing", "Destination amount/currency/issuer is missing."},
|
||||||
add (rpcCOMMAND_MISSING, "commandMissing", "Missing command entry.");
|
{rpcDST_ISR_MALFORMED, "dstIsrMalformed", "Destination issuer is malformed."},
|
||||||
add (rpcDST_ACT_MALFORMED, "dstActMalformed", "Destination account is malformed.");
|
{rpcFORBIDDEN, "forbidden", "Bad credentials."},
|
||||||
add (rpcDST_ACT_MISSING, "dstActMissing", "Destination account not provided.");
|
{rpcHIGH_FEE, "highFee", "Current transaction fee exceeds your limit."},
|
||||||
add (rpcDST_ACT_NOT_FOUND, "dstActNotFound", "Destination account not found.");
|
{rpcINTERNAL, "internal", "Internal error."},
|
||||||
add (rpcDST_AMT_MALFORMED, "dstAmtMalformed", "Destination amount/currency/issuer is malformed.");
|
{rpcINVALID_PARAMS, "invalidParams", "Invalid parameters."},
|
||||||
add (rpcDST_AMT_MISSING, "dstAmtMissing", "Destination amount/currency/issuer is missing.");
|
{rpcJSON_RPC, "json_rpc", "JSON-RPC transport error."},
|
||||||
add (rpcDST_ISR_MALFORMED, "dstIsrMalformed", "Destination issuer is malformed.");
|
{rpcLGR_IDXS_INVALID, "lgrIdxsInvalid", "Ledger indexes invalid."},
|
||||||
add (rpcFORBIDDEN, "forbidden", "Bad credentials.");
|
{rpcLGR_IDX_MALFORMED, "lgrIdxMalformed", "Ledger index malformed."},
|
||||||
add (rpcHIGH_FEE, "highFee", "Current transaction fee exceeds your limit.");
|
{rpcLGR_NOT_FOUND, "lgrNotFound", "Ledger not found."},
|
||||||
add (rpcINTERNAL, "internal", "Internal error.");
|
{rpcLGR_NOT_VALIDATED, "lgrNotValidated", "Ledger not validated."},
|
||||||
add (rpcINVALID_PARAMS, "invalidParams", "Invalid parameters.");
|
{rpcMASTER_DISABLED, "masterDisabled", "Master key is disabled."},
|
||||||
add (rpcJSON_RPC, "json_rpc", "JSON-RPC transport error.");
|
{rpcNOT_ENABLED, "notEnabled", "Not enabled in configuration."},
|
||||||
add (rpcLGR_IDXS_INVALID, "lgrIdxsInvalid", "Ledger indexes invalid.");
|
{rpcNOT_IMPL, "notImpl", "Not implemented."},
|
||||||
add (rpcLGR_IDX_MALFORMED, "lgrIdxMalformed", "Ledger index malformed.");
|
{rpcNOT_READY, "notReady", "Not ready to handle this request."},
|
||||||
add (rpcLGR_NOT_FOUND, "lgrNotFound", "Ledger not found.");
|
{rpcNOT_SUPPORTED, "notSupported", "Operation not supported."},
|
||||||
add (rpcLGR_NOT_VALIDATED, "lgrNotValidated", "Ledger not validated.");
|
{rpcNO_CLOSED, "noClosed", "Closed ledger is unavailable."},
|
||||||
add (rpcMASTER_DISABLED, "masterDisabled", "Master key is disabled.");
|
{rpcNO_CURRENT, "noCurrent", "Current ledger is unavailable."},
|
||||||
add (rpcNOT_ENABLED, "notEnabled", "Not enabled in configuration.");
|
{rpcNO_EVENTS, "noEvents", "Current transport does not support events."},
|
||||||
add (rpcNOT_IMPL, "notImpl", "Not implemented.");
|
{rpcNO_NETWORK, "noNetwork", "Not synced to Ripple network."},
|
||||||
add (rpcNOT_READY, "notReady", "Not ready to handle this request.");
|
{rpcNO_PERMISSION, "noPermission", "You don't have permission for this command."},
|
||||||
add (rpcNOT_SUPPORTED, "notSupported", "Operation not supported.");
|
{rpcNO_PF_REQUEST, "noPathRequest", "No pathfinding request in progress."},
|
||||||
add (rpcNO_CLOSED, "noClosed", "Closed ledger is unavailable.");
|
{rpcPUBLIC_MALFORMED, "publicMalformed", "Public key is malformed."},
|
||||||
add (rpcNO_CURRENT, "noCurrent", "Current ledger is unavailable.");
|
{rpcSIGNING_MALFORMED, "signingMalformed", "Signing of transaction is malformed."},
|
||||||
add (rpcNO_EVENTS, "noEvents", "Current transport does not support events.");
|
{rpcSLOW_DOWN, "slowDown", "You are placing too much load on the server."},
|
||||||
add (rpcNO_NETWORK, "noNetwork", "Not synced to Ripple network.");
|
{rpcSRC_ACT_MALFORMED, "srcActMalformed", "Source account is malformed."},
|
||||||
add (rpcNO_PERMISSION, "noPermission", "You don't have permission for this command.");
|
{rpcSRC_ACT_MISSING, "srcActMissing", "Source account not provided."},
|
||||||
add (rpcNO_PF_REQUEST, "noPathRequest", "No pathfinding request in progress.");
|
{rpcSRC_ACT_NOT_FOUND, "srcActNotFound", "Source account not found."},
|
||||||
add (rpcPUBLIC_MALFORMED, "publicMalformed", "Public key is malformed.");
|
{rpcSRC_CUR_MALFORMED, "srcCurMalformed", "Source currency is malformed."},
|
||||||
add (rpcSIGNING_MALFORMED, "signingMalformed", "Signing of transaction is malformed.");
|
{rpcSRC_ISR_MALFORMED, "srcIsrMalformed", "Source issuer is malformed."},
|
||||||
add (rpcSLOW_DOWN, "slowDown", "You are placing too much load on the server.");
|
{rpcSTREAM_MALFORMED, "malformedStream", "Stream malformed."},
|
||||||
add (rpcSRC_ACT_MALFORMED, "srcActMalformed", "Source account is malformed.");
|
{rpcTOO_BUSY, "tooBusy", "The server is too busy to help you now."},
|
||||||
add (rpcSRC_ACT_MISSING, "srcActMissing", "Source account not provided.");
|
{rpcTXN_NOT_FOUND, "txnNotFound", "Transaction not found."},
|
||||||
add (rpcSRC_ACT_NOT_FOUND, "srcActNotFound", "Source account not found.");
|
{rpcUNKNOWN_COMMAND, "unknownCmd", "Unknown method."},
|
||||||
add (rpcSRC_CUR_MALFORMED, "srcCurMalformed", "Source currency is malformed.");
|
{rpcSENDMAX_MALFORMED, "sendMaxMalformed", "SendMax amount malformed."},
|
||||||
add (rpcSRC_ISR_MALFORMED, "srcIsrMalformed", "Source issuer is malformed.");
|
|
||||||
add (rpcSTREAM_MALFORMED, "malformedStream", "Stream malformed.");
|
|
||||||
add (rpcTOO_BUSY, "tooBusy", "The server is too busy to help you now.");
|
|
||||||
add (rpcTXN_NOT_FOUND, "txnNotFound", "Transaction not found.");
|
|
||||||
add (rpcUNKNOWN_COMMAND, "unknownCmd", "Unknown method.");
|
|
||||||
add (rpcSENDMAX_MALFORMED, "sendMaxMalformed", "SendMax amount malformed.");
|
|
||||||
|
|
||||||
// Verify that the number of entries in m_map equals the number of
|
|
||||||
// enums that have descriptions. That skips rpcUNKNOWN and rpcSUCCESS,
|
|
||||||
// which are -1 and 0 respectively.
|
|
||||||
assert (safe_cast<int>(rpcLAST) == m_map.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorInfo const& get (error_code_i code) const
|
|
||||||
{
|
|
||||||
Map::const_iterator const iter {m_map.find (code)};
|
|
||||||
assert (iter != m_map.end());
|
|
||||||
if (iter != m_map.end())
|
|
||||||
return iter->second;
|
|
||||||
return m_unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void add (error_code_i code, std::string const& token,
|
|
||||||
std::string const& message)
|
|
||||||
{
|
|
||||||
std::pair <Map::iterator, bool> const result {
|
|
||||||
m_map.emplace (std::piecewise_construct,
|
|
||||||
std::forward_as_tuple (code), std::forward_as_tuple (
|
|
||||||
code, token, message))};
|
|
||||||
|
|
||||||
if (! result.second)
|
|
||||||
Throw<std::invalid_argument> ("duplicate error code");
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Map m_map;
|
|
||||||
ErrorInfo const m_unknown;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// C++ does not allow you to return an array from a function. You must
|
||||||
|
// return an object which may in turn contain an array. The following
|
||||||
|
// struct is simply defined so the enclosed array can be returned from a
|
||||||
|
// constexpr function.
|
||||||
|
//
|
||||||
|
// In C++17 this struct can be replaced by a std::array. But in C++14
|
||||||
|
// the constexpr methods of a std::array are not sufficient to perform the
|
||||||
|
// necessary work at compile time.
|
||||||
|
template <int N>
|
||||||
|
struct ErrorInfoArray
|
||||||
|
{
|
||||||
|
// Visual Studio doesn't treat a templated aggregate as an aggregate.
|
||||||
|
// So, for Visual Studio, we define a constexpr default constructor.
|
||||||
|
constexpr ErrorInfoArray()
|
||||||
|
: infos {}
|
||||||
|
{ }
|
||||||
|
|
||||||
|
ErrorInfo infos[N];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sort and validate unorderedErrorInfos at compile time. Should be
|
||||||
|
// converted to consteval when get to C++20.
|
||||||
|
template<int N>
|
||||||
|
constexpr auto
|
||||||
|
sortErrorInfos (ErrorInfo const (&unordered)[N]) -> ErrorInfoArray<N>
|
||||||
|
{
|
||||||
|
ErrorInfoArray<N> ret;
|
||||||
|
|
||||||
|
for (ErrorInfo const& info : unordered)
|
||||||
|
{
|
||||||
|
if (info.code <= rpcSUCCESS || info.code > rpcLAST)
|
||||||
|
throw (std::out_of_range ("Invalid error_code_i"));
|
||||||
|
|
||||||
|
// The first valid code follows rpcSUCCESS immediately.
|
||||||
|
static_assert (rpcSUCCESS == 0, "Unexpected error_code_i layout.");
|
||||||
|
int const index {info.code - 1};
|
||||||
|
|
||||||
|
if (ret.infos[index].code != rpcUNKNOWN)
|
||||||
|
throw (std::invalid_argument ("Duplicate error_code_i in list"));
|
||||||
|
|
||||||
|
ret.infos[index].code = info.code;
|
||||||
|
ret.infos[index].token = info.token;
|
||||||
|
ret.infos[index].message = info.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that all entries are filled in starting with 1 and proceeding
|
||||||
|
// to rpcLAST.
|
||||||
|
int expect = 0;
|
||||||
|
for (ErrorInfo const& info : ret.infos)
|
||||||
|
{
|
||||||
|
if (info.code != ++expect)
|
||||||
|
throw (std::invalid_argument ("Empty error_code_i in list"));
|
||||||
|
}
|
||||||
|
if (expect != rpcLAST)
|
||||||
|
throw (std::invalid_argument ("Insufficient list entries"));
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr auto sortedErrorInfos {sortErrorInfos(unorderedErrorInfos)};
|
||||||
|
constexpr ErrorInfo unknownError;
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
ErrorInfo const& get_error_info (error_code_i code)
|
ErrorInfo const& get_error_info (error_code_i code)
|
||||||
{
|
{
|
||||||
static detail::ErrorCategory const category;
|
if (code <= rpcSUCCESS || code > rpcLAST)
|
||||||
return category.get (code);
|
return detail::unknownError;
|
||||||
|
return detail::sortedErrorInfos.infos[code - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
Json::Value make_error (error_code_i code)
|
Json::Value make_error (error_code_i code)
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#include <ripple/rpc/Status.h>
|
#include <ripple/rpc/Status.h>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
namespace RPC {
|
namespace RPC {
|
||||||
@@ -46,7 +47,9 @@ std::string Status::codeString () const
|
|||||||
if (type_ == Status::Type::error_code_i)
|
if (type_ == Status::Type::error_code_i)
|
||||||
{
|
{
|
||||||
auto info = get_error_info (toErrorCode ());
|
auto info = get_error_info (toErrorCode ());
|
||||||
return info.token + ": " + info.message;
|
std::ostringstream sStr;
|
||||||
|
sStr << info.token.c_str() << ": " << info.message.c_str();
|
||||||
|
return sStr.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
assert (false);
|
assert (false);
|
||||||
|
|||||||
Reference in New Issue
Block a user