diff --git a/src/ripple/protocol/ErrorCodes.h b/src/ripple/protocol/ErrorCodes.h index 3f506ef87b..b9db815d85 100644 --- a/src/ripple/protocol/ErrorCodes.h +++ b/src/ripple/protocol/ErrorCodes.h @@ -115,16 +115,23 @@ namespace RPC { /** Maps an rpc error code to its token and default message. */ struct ErrorInfo { - ErrorInfo (error_code_i code_, std::string const& token_, - std::string const& message_) + // Default ctor needed to produce an empty std::array during constexpr eval. + 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_) , token (token_) , message (message_) { } error_code_i code; - std::string token; - std::string message; + Json::StaticString token; + Json::StaticString message; }; /** Returns an ErrorInfo that reflects the error code. */ diff --git a/src/ripple/protocol/impl/ErrorCodes.cpp b/src/ripple/protocol/impl/ErrorCodes.cpp index e46e7729bf..019a2c1da0 100644 --- a/src/ripple/protocol/impl/ErrorCodes.cpp +++ b/src/ripple/protocol/impl/ErrorCodes.cpp @@ -17,137 +17,149 @@ */ //============================================================================== -#include -#include #include #include -#include -#include -namespace std { - -template <> -struct hash -{ - explicit hash() = default; - - std::size_t operator() (ripple::error_code_i value) const - { - return value; - } -}; - -} namespace ripple { namespace RPC { 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: - using Map = std::unordered_map ; - - ErrorCategory () - : m_unknown (rpcUNKNOWN, "unknown", "An unknown error code.") - { - add (rpcACT_BITCOIN, "actBitcoin", "Account is bitcoin address."); - add (rpcACT_MALFORMED, "actMalformed", "Account malformed."); - add (rpcACT_NOT_FOUND, "actNotFound", "Account not found."); - add (rpcALREADY_MULTISIG, "alreadyMultisig", "Already multisigned."); - add (rpcALREADY_SINGLE_SIG, "alreadySingleSig", "Already single-signed."); - add (rpcAMENDMENT_BLOCKED, "amendmentBlocked", "Amendment blocked, need upgrade."); - add (rpcATX_DEPRECATED, "deprecated", "Use the new API or specify a ledger range."); - add (rpcBAD_FEATURE, "badFeature", "Feature unknown or invalid."); - add (rpcBAD_ISSUER, "badIssuer", "Issuer account malformed."); - add (rpcBAD_MARKET, "badMarket", "No such market."); - add (rpcBAD_SECRET, "badSecret", "Secret does not match account."); - add (rpcBAD_SEED, "badSeed", "Disallowed seed."); - add (rpcBAD_SYNTAX, "badSyntax", "Syntax error."); - add (rpcCHANNEL_MALFORMED, "channelMalformed", "Payment channel is malformed."); - add (rpcCHANNEL_AMT_MALFORMED, "channelAmtMalformed", "Payment channel amount is malformed."); - add (rpcCOMMAND_MISSING, "commandMissing", "Missing command entry."); - add (rpcDST_ACT_MALFORMED, "dstActMalformed", "Destination account is malformed."); - add (rpcDST_ACT_MISSING, "dstActMissing", "Destination account not provided."); - add (rpcDST_ACT_NOT_FOUND, "dstActNotFound", "Destination account not found."); - add (rpcDST_AMT_MALFORMED, "dstAmtMalformed", "Destination amount/currency/issuer is malformed."); - add (rpcDST_AMT_MISSING, "dstAmtMissing", "Destination amount/currency/issuer is missing."); - add (rpcDST_ISR_MALFORMED, "dstIsrMalformed", "Destination issuer is malformed."); - add (rpcFORBIDDEN, "forbidden", "Bad credentials."); - add (rpcHIGH_FEE, "highFee", "Current transaction fee exceeds your limit."); - add (rpcINTERNAL, "internal", "Internal error."); - add (rpcINVALID_PARAMS, "invalidParams", "Invalid parameters."); - add (rpcJSON_RPC, "json_rpc", "JSON-RPC transport error."); - add (rpcLGR_IDXS_INVALID, "lgrIdxsInvalid", "Ledger indexes invalid."); - add (rpcLGR_IDX_MALFORMED, "lgrIdxMalformed", "Ledger index malformed."); - add (rpcLGR_NOT_FOUND, "lgrNotFound", "Ledger not found."); - add (rpcLGR_NOT_VALIDATED, "lgrNotValidated", "Ledger not validated."); - add (rpcMASTER_DISABLED, "masterDisabled", "Master key is disabled."); - add (rpcNOT_ENABLED, "notEnabled", "Not enabled in configuration."); - add (rpcNOT_IMPL, "notImpl", "Not implemented."); - add (rpcNOT_READY, "notReady", "Not ready to handle this request."); - add (rpcNOT_SUPPORTED, "notSupported", "Operation not supported."); - add (rpcNO_CLOSED, "noClosed", "Closed ledger is unavailable."); - add (rpcNO_CURRENT, "noCurrent", "Current ledger is unavailable."); - add (rpcNO_EVENTS, "noEvents", "Current transport does not support events."); - add (rpcNO_NETWORK, "noNetwork", "Not synced to Ripple network."); - add (rpcNO_PERMISSION, "noPermission", "You don't have permission for this command."); - add (rpcNO_PF_REQUEST, "noPathRequest", "No pathfinding request in progress."); - add (rpcPUBLIC_MALFORMED, "publicMalformed", "Public key is malformed."); - add (rpcSIGNING_MALFORMED, "signingMalformed", "Signing of transaction is malformed."); - add (rpcSLOW_DOWN, "slowDown", "You are placing too much load on the server."); - add (rpcSRC_ACT_MALFORMED, "srcActMalformed", "Source account is malformed."); - add (rpcSRC_ACT_MISSING, "srcActMissing", "Source account not provided."); - add (rpcSRC_ACT_NOT_FOUND, "srcActNotFound", "Source account not found."); - add (rpcSRC_CUR_MALFORMED, "srcCurMalformed", "Source currency is 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(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 const result { - m_map.emplace (std::piecewise_construct, - std::forward_as_tuple (code), std::forward_as_tuple ( - code, token, message))}; - - if (! result.second) - Throw ("duplicate error code"); - } - -private: - Map m_map; - ErrorInfo const m_unknown; + {rpcACT_BITCOIN, "actBitcoin", "Account is bitcoin address."}, + {rpcACT_MALFORMED, "actMalformed", "Account malformed."}, + {rpcACT_NOT_FOUND, "actNotFound", "Account not found."}, + {rpcALREADY_MULTISIG, "alreadyMultisig", "Already multisigned."}, + {rpcALREADY_SINGLE_SIG, "alreadySingleSig", "Already single-signed."}, + {rpcAMENDMENT_BLOCKED, "amendmentBlocked", "Amendment blocked, need upgrade."}, + {rpcATX_DEPRECATED, "deprecated", "Use the new API or specify a ledger range."}, + {rpcBAD_FEATURE, "badFeature", "Feature unknown or invalid."}, + {rpcBAD_ISSUER, "badIssuer", "Issuer account malformed."}, + {rpcBAD_MARKET, "badMarket", "No such market."}, + {rpcBAD_SECRET, "badSecret", "Secret does not match account."}, + {rpcBAD_SEED, "badSeed", "Disallowed seed."}, + {rpcBAD_SYNTAX, "badSyntax", "Syntax error."}, + {rpcCHANNEL_MALFORMED, "channelMalformed", "Payment channel is malformed."}, + {rpcCHANNEL_AMT_MALFORMED, "channelAmtMalformed", "Payment channel amount is malformed."}, + {rpcCOMMAND_MISSING, "commandMissing", "Missing command entry."}, + {rpcDST_ACT_MALFORMED, "dstActMalformed", "Destination account is malformed."}, + {rpcDST_ACT_MISSING, "dstActMissing", "Destination account not provided."}, + {rpcDST_ACT_NOT_FOUND, "dstActNotFound", "Destination account not found."}, + {rpcDST_AMT_MALFORMED, "dstAmtMalformed", "Destination amount/currency/issuer is malformed."}, + {rpcDST_AMT_MISSING, "dstAmtMissing", "Destination amount/currency/issuer is missing."}, + {rpcDST_ISR_MALFORMED, "dstIsrMalformed", "Destination issuer is malformed."}, + {rpcFORBIDDEN, "forbidden", "Bad credentials."}, + {rpcHIGH_FEE, "highFee", "Current transaction fee exceeds your limit."}, + {rpcINTERNAL, "internal", "Internal error."}, + {rpcINVALID_PARAMS, "invalidParams", "Invalid parameters."}, + {rpcJSON_RPC, "json_rpc", "JSON-RPC transport error."}, + {rpcLGR_IDXS_INVALID, "lgrIdxsInvalid", "Ledger indexes invalid."}, + {rpcLGR_IDX_MALFORMED, "lgrIdxMalformed", "Ledger index malformed."}, + {rpcLGR_NOT_FOUND, "lgrNotFound", "Ledger not found."}, + {rpcLGR_NOT_VALIDATED, "lgrNotValidated", "Ledger not validated."}, + {rpcMASTER_DISABLED, "masterDisabled", "Master key is disabled."}, + {rpcNOT_ENABLED, "notEnabled", "Not enabled in configuration."}, + {rpcNOT_IMPL, "notImpl", "Not implemented."}, + {rpcNOT_READY, "notReady", "Not ready to handle this request."}, + {rpcNOT_SUPPORTED, "notSupported", "Operation not supported."}, + {rpcNO_CLOSED, "noClosed", "Closed ledger is unavailable."}, + {rpcNO_CURRENT, "noCurrent", "Current ledger is unavailable."}, + {rpcNO_EVENTS, "noEvents", "Current transport does not support events."}, + {rpcNO_NETWORK, "noNetwork", "Not synced to Ripple network."}, + {rpcNO_PERMISSION, "noPermission", "You don't have permission for this command."}, + {rpcNO_PF_REQUEST, "noPathRequest", "No pathfinding request in progress."}, + {rpcPUBLIC_MALFORMED, "publicMalformed", "Public key is malformed."}, + {rpcSIGNING_MALFORMED, "signingMalformed", "Signing of transaction is malformed."}, + {rpcSLOW_DOWN, "slowDown", "You are placing too much load on the server."}, + {rpcSRC_ACT_MALFORMED, "srcActMalformed", "Source account is malformed."}, + {rpcSRC_ACT_MISSING, "srcActMissing", "Source account not provided."}, + {rpcSRC_ACT_NOT_FOUND, "srcActNotFound", "Source account not found."}, + {rpcSRC_CUR_MALFORMED, "srcCurMalformed", "Source currency is malformed."}, + {rpcSRC_ISR_MALFORMED, "srcIsrMalformed", "Source issuer is malformed."}, + {rpcSTREAM_MALFORMED, "malformedStream", "Stream malformed."}, + {rpcTOO_BUSY, "tooBusy", "The server is too busy to help you now."}, + {rpcTXN_NOT_FOUND, "txnNotFound", "Transaction not found."}, + {rpcUNKNOWN_COMMAND, "unknownCmd", "Unknown method."}, + {rpcSENDMAX_MALFORMED, "sendMaxMalformed", "SendMax amount malformed."}, }; +// 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 +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 +constexpr auto +sortErrorInfos (ErrorInfo const (&unordered)[N]) -> ErrorInfoArray +{ + ErrorInfoArray 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) { - static detail::ErrorCategory const category; - return category.get (code); + if (code <= rpcSUCCESS || code > rpcLAST) + return detail::unknownError; + return detail::sortedErrorInfos.infos[code - 1]; } Json::Value make_error (error_code_i code) diff --git a/src/ripple/rpc/impl/Status.cpp b/src/ripple/rpc/impl/Status.cpp index 3f60e8dd97..a25ca3fe3a 100644 --- a/src/ripple/rpc/impl/Status.cpp +++ b/src/ripple/rpc/impl/Status.cpp @@ -18,6 +18,7 @@ //============================================================================== #include +#include namespace ripple { namespace RPC { @@ -46,7 +47,9 @@ std::string Status::codeString () const if (type_ == Status::Type::error_code_i) { 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);