Improved human readable JSON-RPC error messages

This commit is contained in:
Vinnie Falco
2013-12-12 21:17:54 -05:00
parent ef7810bc95
commit 9d07ddeae1
22 changed files with 1682 additions and 839 deletions

View File

@@ -604,6 +604,12 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\rpc\impl\ErrorCodes.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\rpc\impl\Handler.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@@ -1732,6 +1738,12 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple_data\protocol\STParsedJSON.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple_data\protocol\TER.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@@ -2326,6 +2338,7 @@
<ClInclude Include="..\..\src\ripple\rocksdb\rocksdb\util\string_util.h" />
<ClInclude Include="..\..\src\ripple\rocksdb\rocksdb\util\testharness.h" />
<ClInclude Include="..\..\src\ripple\rocksdb\rocksdb\util\testutil.h" />
<ClInclude Include="..\..\src\ripple\rpc\api\ErrorCodes.h" />
<ClInclude Include="..\..\src\ripple\rpc\api\Handler.h" />
<ClInclude Include="..\..\src\ripple\rpc\api\Manager.h" />
<ClInclude Include="..\..\src\ripple\rpc\api\Service.h" />
@@ -2561,6 +2574,7 @@
<ClInclude Include="..\..\src\ripple_data\protocol\SerializedObjectTemplate.h" />
<ClInclude Include="..\..\src\ripple_data\protocol\SerializedTypes.h" />
<ClInclude Include="..\..\src\ripple_data\protocol\Serializer.h" />
<ClInclude Include="..\..\src\ripple_data\protocol\STParsedJSON.h" />
<ClInclude Include="..\..\src\ripple_data\protocol\TER.h" />
<ClInclude Include="..\..\src\ripple_data\protocol\TxFlags.h" />
<ClInclude Include="..\..\src\ripple_data\protocol\TxFormats.h" />

View File

@@ -1437,6 +1437,12 @@
<ClCompile Include="..\..\src\ripple_app\peers\PeerSet.cpp">
<Filter>[2] Old Ripple\ripple_app\peers</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple_data\protocol\STParsedJSON.cpp">
<Filter>[2] Old Ripple\ripple_data\protocol</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\rpc\impl\ErrorCodes.cpp">
<Filter>[1] Ripple\rpc\impl</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\src\ripple_basics\containers\KeyCache.h">
@@ -2916,6 +2922,12 @@
<ClInclude Include="..\..\src\ripple_app\peers\PeerSet.h">
<Filter>[2] Old Ripple\ripple_app\peers</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple_data\protocol\STParsedJSON.h">
<Filter>[2] Old Ripple\ripple_data\protocol</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\rpc\api\ErrorCodes.h">
<Filter>[1] Ripple\rpc\api</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="..\..\src\ripple_data\protocol\ripple.proto">

View File

@@ -0,0 +1,208 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_RPC_ERRORCODES_H_INCLUDED
#define RIPPLE_RPC_ERRORCODES_H_INCLUDED
namespace ripple {
// VFALCO NOTE These are outside the RPC namespace
enum error_code_i
{
rpcUNKNOWN = -1, // Represents codes not listed in this enumeration
rpcSUCCESS = 0,
rpcBAD_SYNTAX, // Must be 1 to print usage to command line.
rpcJSON_RPC,
rpcFORBIDDEN,
// Error numbers beyond this line are not stable between versions.
// Programs should use error tokens.
// Misc failure
rpcLOAD_FAILED,
rpcNO_PERMISSION,
rpcNO_EVENTS,
rpcNOT_STANDALONE,
rpcTOO_BUSY,
rpcSLOW_DOWN,
// Networking
rpcNO_CLOSED,
rpcNO_CURRENT,
rpcNO_NETWORK,
// Ledger state
rpcACT_EXISTS,
rpcACT_NOT_FOUND,
rpcINSUF_FUNDS,
rpcLGR_NOT_FOUND,
rpcNICKNAME_MISSING,
rpcNO_ACCOUNT,
rpcNO_PATH,
rpcPASSWD_CHANGED,
rpcSRC_MISSING,
rpcSRC_UNCLAIMED,
rpcTXN_NOT_FOUND,
rpcWRONG_SEED,
// Malformed command
rpcINVALID_PARAMS,
rpcUNKNOWN_COMMAND,
rpcNO_PF_REQUEST,
// Bad parameter
rpcACT_BITCOIN,
rpcACT_MALFORMED,
rpcQUALITY_MALFORMED,
rpcBAD_BLOB,
rpcBAD_FEATURE,
rpcBAD_ISSUER,
rpcBAD_MARKET,
rpcBAD_SECRET,
rpcBAD_SEED,
rpcCOMMAND_MISSING,
rpcDST_ACT_MALFORMED,
rpcDST_ACT_MISSING,
rpcDST_AMT_MALFORMED,
rpcDST_ISR_MALFORMED,
rpcGETS_ACT_MALFORMED,
rpcGETS_AMT_MALFORMED,
rpcHOST_IP_MALFORMED,
rpcLGR_IDXS_INVALID,
rpcLGR_IDX_MALFORMED,
rpcNICKNAME_MALFORMED,
rpcNICKNAME_PERM,
rpcPAYS_ACT_MALFORMED,
rpcPAYS_AMT_MALFORMED,
rpcPORT_MALFORMED,
rpcPUBLIC_MALFORMED,
rpcSRC_ACT_MALFORMED,
rpcSRC_ACT_MISSING,
rpcSRC_ACT_NOT_FOUND,
rpcSRC_AMT_MALFORMED,
rpcSRC_CUR_MALFORMED,
rpcSRC_ISR_MALFORMED,
rpcATX_DEPRECATED,
// Internal error (should never happen)
rpcINTERNAL, // Generic internal error.
rpcFAIL_GEN_DECRPYT,
rpcNOT_IMPL,
rpcNOT_SUPPORTED,
rpcNO_GEN_DECRPYT,
};
//------------------------------------------------------------------------------
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_)
: code (code_)
, token (token_)
, message (message_)
{ }
error_code_i code;
std::string token;
std::string message;
};
/** Returns an ErrorInfo that reflects the error code. */
ErrorInfo const& get_error_info (error_code_i code);
/** Add or update the json update to reflect the error code. */
/** @{ */
void inject_error (error_code_i code, Json::Value& json);
inline void inject_error (int code, Json::Value& json)
{ inject_error (error_code_i (code), json); }
void inject_error (error_code_i code, std::string const& message, Json::Value& json);
/** @} */
/** Returns a new json object that reflects the error code. */
/** @{ */
Json::Value make_error (error_code_i code);
Json::Value make_error (error_code_i code, std::string const& message);
/** @} */
/** Returns a new json object that indicates invalid parameters. */
/** @{ */
inline Json::Value make_param_error (std::string const& message)
{
return make_error (rpcINVALID_PARAMS, message);
}
inline std::string missing_field_message (std::string const& name)
{
return "Missing field '" + name + "'.";
}
inline Json::Value missing_field_error (std::string const& name)
{
return make_param_error (missing_field_message (name));
}
inline std::string object_field_message (std::string const& name)
{
return "Invalid field '" + name + "', not object.";
}
inline Json::Value object_field_error (std::string const& name)
{
return make_param_error (object_field_message (name));
}
inline std::string invalid_field_message (std::string const& name)
{
return "Invalid field '" + name + "'.";
}
inline Json::Value invalid_field_error (std::string const& name)
{
return make_param_error (object_field_message (name));
}
inline std::string expected_field_message (
std::string const& name, std::string const& type)
{
return "Invalid field '" + name + "', not " + type + ".";
}
inline Json::Value expected_field_error (
std::string const& name, std::string const& type)
{
return make_param_error (expected_field_message (name, type));
}
/** @} */
/** Returns `true` if the json contains an rpc error specification. */
bool contains_error (Json::Value const& json);
}
}
#endif

View File

@@ -0,0 +1,169 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
namespace ripple {
namespace RPC {
namespace detail {
class ErrorCategory
{
public:
typedef boost::unordered_map <error_code_i, ErrorInfo> Map;
ErrorCategory ()
: m_unknown (rpcUNKNOWN, "unknown", "An unknown error code.")
{
add (rpcACT_BITCOIN, "actBitcoin", "Account is bitcoin address.");
add (rpcACT_EXISTS, "actExists", "Account already exists.");
add (rpcACT_MALFORMED, "actMalformed", "Account malformed.");
add (rpcACT_NOT_FOUND, "actNotFound", "Account not found.");
add (rpcBAD_BLOB, "badBlob", "Blob must be a non-empty hex string.");
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 (rpcCOMMAND_MISSING, "commandMissing", "Missing command entry.");
add (rpcDST_ACT_MALFORMED, "dstActMalformed", "Destination account is malformed.");
add (rpcDST_ACT_MISSING, "dstActMissing", "Destination account does not exist.");
add (rpcDST_AMT_MALFORMED, "dstAmtMalformed", "Destination amount/currency/issuer is malformed.");
add (rpcDST_ISR_MALFORMED, "dstIsrMalformed", "Destination issuer is malformed.");
add (rpcFORBIDDEN, "forbidden", "Bad credentials.");
add (rpcFAIL_GEN_DECRPYT, "failGenDecrypt", "Failed to decrypt generator.");
add (rpcGETS_ACT_MALFORMED, "getsActMalformed", "Gets account malformed.");
add (rpcGETS_AMT_MALFORMED, "getsAmtMalformed", "Gets amount malformed.");
add (rpcHOST_IP_MALFORMED, "hostIpMalformed", "Host IP is malformed.");
add (rpcINSUF_FUNDS, "insufFunds", "Insufficient funds.");
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 (rpcNICKNAME_MALFORMED, "nicknameMalformed", "Nickname is malformed.");
add (rpcNICKNAME_MISSING, "nicknameMissing", "Nickname does not exist.");
add (rpcNICKNAME_PERM, "nicknamePerm", "Account does not control nickname.");
add (rpcNOT_IMPL, "notImpl", "Not implemented.");
add (rpcNO_ACCOUNT, "noAccount", "No such account.");
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_GEN_DECRPYT, "noGenDectypt", "Password failed to decrypt master public generator.");
add (rpcNO_NETWORK, "noNetwork", "Network not available.");
add (rpcNO_PATH, "noPath", "Unable to find a ripple path.");
add (rpcNO_PERMISSION, "noPermission", "You don't have permission for this command.");
add (rpcNO_PF_REQUEST, "noPathRequest", "No pathfinding request in progress.");
add (rpcNOT_STANDALONE, "notStandAlone", "Operation valid in debug mode only.");
add (rpcNOT_SUPPORTED, "notSupported", "Operation not supported.");
add (rpcPASSWD_CHANGED, "passwdChanged", "Wrong key, password changed.");
add (rpcPAYS_ACT_MALFORMED, "paysActMalformed", "Pays account malformed.");
add (rpcPAYS_AMT_MALFORMED, "paysAmtMalformed", "Pays amount malformed.");
add (rpcPORT_MALFORMED, "portMalformed", "Port is malformed.");
add (rpcPUBLIC_MALFORMED, "publicMalformed", "Public key is malformed.");
add (rpcQUALITY_MALFORMED, "qualityMalformed", "Quality malformed.");
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_AMT_MALFORMED, "srcAmtMalformed", "Source amount/currency/issuer is malformed.");
add (rpcSRC_CUR_MALFORMED, "srcCurMalformed", "Source currency is malformed.");
add (rpcSRC_ISR_MALFORMED, "srcIsrMalformed", "Source issuer is malformed.");
add (rpcSRC_UNCLAIMED, "srcUnclaimed", "Source account is not claimed.");
add (rpcTXN_NOT_FOUND, "txnNotFound", "Transaction not found.");
add (rpcUNKNOWN_COMMAND, "unknownCmd", "Unknown method.");
add (rpcWRONG_SEED, "wrongSeed", "The regular key does not point as the master key.");
add (rpcTOO_BUSY, "tooBusy", "The server is too busy to help you now.");
add (rpcSLOW_DOWN, "slowDown", "You are placing too much load on the server.");
add (rpcATX_DEPRECATED, "deprecated", "Use the new API or specify a ledger range.");
}
ErrorInfo const& get (error_code_i code) const
{
Map::const_iterator const iter (m_map.find (code));
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> result (
m_map.emplace (boost::unordered::piecewise_construct,
boost::make_tuple (code), boost::make_tuple (
code, token, message)));
check_postcondition (result.second);
}
private:
Map m_map;
ErrorInfo m_unknown;
};
}
//------------------------------------------------------------------------------
ErrorInfo const& get_error_info (error_code_i code)
{
static detail::ErrorCategory category;
return category.get (code);
}
void inject_error (error_code_i code, Json::Value& json)
{
ErrorInfo const& info (get_error_info (code));
json ["error"] = info.token;
json ["error_code"] = info.code;
json ["error_message"] = info.message;
}
void inject_error (error_code_i code, std::string const& message, Json::Value& json)
{
ErrorInfo const& info (get_error_info (code));
json ["error"] = info.token;
json ["error_code"] = info.code;
json ["error_message"] = message;
}
Json::Value make_error (error_code_i code)
{
Json::Value json;
inject_error (code, json);
return json;
}
Json::Value make_error (error_code_i code, std::string const& message)
{
Json::Value json;
inject_error (code, message, json);
return json;
}
bool contains_error (Json::Value const& json)
{
if (json.isObject() && json.isMember ("error"))
return true;
return false;
}
}
}

View File

@@ -24,6 +24,7 @@
#include "beast/modules/beast_core/system/BeforeBoost.h"
#include <boost/unordered_map.hpp>
#include "impl/ErrorCodes.cpp"
# include "impl/ManagerImpl.h"
#include "impl/Manager.cpp"
#include "impl/Handler.cpp"

View File

@@ -27,5 +27,6 @@
# include "api/Handler.h"
# include "api/Service.h"
#include "api/Manager.h"
#include "api/ErrorCodes.h"
#endif

View File

@@ -344,9 +344,11 @@ public:
pass ();
}
std::unique_ptr<STObject> new_obj = STObject::parseJson (j.getJson (0), sfGeneric);
STParsedJSON parsed ("test", j.getJson (0));
std::unique_ptr <STObject> new_obj (std::move (parsed.object));
if (new_obj.get () == NULL) fail ("Unable to build object from json");
if (new_obj.get () == nullptr)
fail ("Unable to build object from json");
if (STObject (j) != *new_obj)
{

View File

@@ -26,6 +26,8 @@
#pragma warning (disable: 4309) // truncation of constant value
#endif
#include "../ripple/rpc/api/ErrorCodes.h"
namespace ripple
{

View File

@@ -90,53 +90,56 @@ int LegacyPathFind::maxInProgress (2);
Json::Value RPCHandler::transactionSign (Json::Value params, bool bSubmit, bool bFailHard, Application::ScopedLockType& mlh)
{
if (getApp().getFeeTrack().isLoadedCluster() && (mRole != Config::ADMIN))
return rpcError(rpcTOO_BUSY);
Json::Value jvResult;
RippleAddress naSeed;
RippleAddress raSrcAddressID;
bool bOffline = params.isMember ("offline") && params["offline"].asBool ();
WriteLog (lsDEBUG, RPCHandler) << boost::str (boost::format ("transactionSign: %s") % params);
if (!bOffline && !getConfig ().RUN_STANDALONE && (getApp().getLedgerMaster().getValidatedLedgerAge() > 120))
{
if (! params.isMember ("secret"))
return RPC::missing_field_error ("secret");
if (! params.isMember ("tx_json"))
return RPC::missing_field_error ("tx_json");
RippleAddress naSeed;
if (! naSeed.setSeedGeneric (params["secret"].asString ()))
return RPC::make_error (rpcBAD_SEED,
RPC::invalid_field_message ("secret"));
Json::Value tx_json (params ["tx_json"]);
if (! tx_json.isObject ())
return RPC::object_field_error ("tx_json");
if (! tx_json.isMember ("TransactionType"))
return RPC::missing_field_error ("tx_json.TransactionType");
std::string const sType = tx_json ["TransactionType"].asString ();
if (! tx_json.isMember ("Account"))
return RPC::make_error (rpcSRC_ACT_MISSING,
RPC::missing_field_message ("tx_json.Account"));
RippleAddress raSrcAddressID;
if (! raSrcAddressID.setAccountID (tx_json["Account"].asString ()))
return RPC::make_error (rpcSRC_ACT_MALFORMED,
RPC::invalid_field_message ("tx_json.Account"));
bool const bOffline (
params.isMember ("offline") && params["offline"].asBool ());
if (! tx_json.isMember ("Sequence") && bOffline)
return RPC::missing_field_error ("tx_json.Sequence");
// Check for current ledger
if (!bOffline && !getConfig ().RUN_STANDALONE &&
(getApp().getLedgerMaster().getValidatedLedgerAge() > 120))
return rpcError (rpcNO_CURRENT);
}
if (!params.isMember ("secret") || !params.isMember ("tx_json"))
{
return rpcError (rpcINVALID_PARAMS);
}
Json::Value txJSON = params["tx_json"];
if (!txJSON.isObject ())
{
return rpcError (rpcINVALID_PARAMS);
}
if (!naSeed.setSeedGeneric (params["secret"].asString ()))
{
return rpcError (rpcBAD_SEED);
}
if (!txJSON.isMember ("Account"))
{
return rpcError (rpcSRC_ACT_MISSING);
}
if (!raSrcAddressID.setAccountID (txJSON["Account"].asString ()))
{
return rpcError (rpcSRC_ACT_MALFORMED);
}
if (!txJSON.isMember ("TransactionType"))
{
return rpcError (rpcINVALID_PARAMS);
}
std::string sType = txJSON["TransactionType"].asString ();
// Check for load
if (getApp().getFeeTrack().isLoadedCluster() && (mRole != Config::ADMIN))
return rpcError(rpcTOO_BUSY);
Ledger::pointer lSnapshot = mNetOps->getCurrentLedger ();
AccountState::pointer asSrc = bOffline
@@ -153,40 +156,41 @@ Json::Value RPCHandler::transactionSign (Json::Value params, bool bSubmit, bool
return rpcError (rpcSRC_ACT_NOT_FOUND);
}
if (!txJSON.isMember ("Fee")
&& (
if (! tx_json.isMember ("Fee") && (
"AccountSet" == sType
|| "Payment" == sType
|| "OfferCreate" == sType
|| "OfferCancel" == sType
|| "TrustSet" == sType))
{
// VFALCO TODO This needs to be fixed
// feeReq = lSnapshot->scaleFeeLoad(,
txJSON["Fee"] = (int) getConfig ().FEE_DEFAULT;
tx_json["Fee"] = (int) getConfig ().FEE_DEFAULT;
}
if ("Payment" == sType)
{
RippleAddress dstAccountID;
if (!txJSON.isMember ("Destination"))
{
return rpcError (rpcDST_ACT_MISSING);
}
if (! tx_json.isMember ("Amount"))
return RPC::missing_field_error ("tx_json.Amount");
if (!dstAccountID.setAccountID (txJSON["Destination"].asString ()))
{
return rpcError (rpcDST_ACT_MALFORMED);
}
STAmount amount;
if (txJSON.isMember ("Paths") && params.isMember ("build_path"))
{
// Asking to build a path when providing one is an error.
return rpcError (rpcINVALID_PARAMS);
}
if (! amount.bSetJson (tx_json ["Amount"]))
return RPC::invalid_field_error ("tx_json.Amount");
if (!txJSON.isMember ("Paths") && txJSON.isMember ("Amount") && params.isMember ("build_path"))
if (!tx_json.isMember ("Destination"))
return RPC::missing_field_error ("tx_json.Destination");
if (!dstAccountID.setAccountID (tx_json["Destination"].asString ()))
return RPC::invalid_field_error ("tx_json.Destination");
if (tx_json.isMember ("Paths") && params.isMember ("build_path"))
return RPC::make_error (rpcINVALID_PARAMS,
"Cannot specify both 'tx_json.Paths' and 'tx_json.build_path'");
if (!tx_json.isMember ("Paths") && tx_json.isMember ("Amount") && params.isMember ("build_path"))
{
// Need a ripple path.
STPathSet spsPaths;
@@ -194,29 +198,22 @@ Json::Value RPCHandler::transactionSign (Json::Value params, bool bSubmit, bool
uint160 uSrcIssuerID;
STAmount saSendMax;
STAmount saSend;
if (!txJSON.isMember ("Amount") // Amount required.
|| !saSend.bSetJson (txJSON["Amount"])) // Must be valid.
return rpcError (rpcDST_AMT_MALFORMED);
if (txJSON.isMember ("SendMax"))
if (tx_json.isMember ("SendMax"))
{
if (!saSendMax.bSetJson (txJSON["SendMax"]))
return rpcError (rpcINVALID_PARAMS);
if (!saSendMax.bSetJson (tx_json ["SendMax"]))
return RPC::invalid_field_error ("tx_json.SendMax");
}
else
{
// If no SendMax, default to Amount with sender as issuer.
saSendMax = saSend;
saSendMax = amount;
saSendMax.setIssuer (raSrcAddressID.getAccountID ());
}
if (saSendMax.isNative () && saSend.isNative ())
{
// Asking to build a path for XRP to XRP is an error.
return rpcError (rpcINVALID_PARAMS);
}
if (saSendMax.isNative () && amount.isNative ())
return RPC::make_error (rpcINVALID_PARAMS,
"Cannot build XRP to XRP paths.");
{
LegacyPathFind lpf (mRole == Config::ADMIN);
@@ -226,7 +223,7 @@ Json::Value RPCHandler::transactionSign (Json::Value params, bool bSubmit, bool
bool bValid;
RippleLineCache::pointer cache = boost::make_shared<RippleLineCache> (lSnapshot);
Pathfinder pf (cache, raSrcAddressID, dstAccountID,
saSendMax.getCurrency (), saSendMax.getIssuer (), saSend, bValid);
saSendMax.getCurrency (), saSendMax.getIssuer (), amount, bValid);
STPath extraPath;
if (!bValid || !pf.findPaths (getConfig ().PATH_SEARCH_OLD, 4, spsPaths, extraPath))
@@ -242,23 +239,23 @@ Json::Value RPCHandler::transactionSign (Json::Value params, bool bSubmit, bool
if (!spsPaths.isEmpty ())
{
txJSON["Paths"] = spsPaths.getJson (0);
tx_json["Paths"] = spsPaths.getJson (0);
}
}
}
}
if (!txJSON.isMember ("Fee")
if (!tx_json.isMember ("Fee")
&& (
"AccountSet" == txJSON["TransactionType"].asString ()
|| "OfferCreate" == txJSON["TransactionType"].asString ()
|| "OfferCancel" == txJSON["TransactionType"].asString ()
|| "TrustSet" == txJSON["TransactionType"].asString ()))
"AccountSet" == tx_json["TransactionType"].asString ()
|| "OfferCreate" == tx_json["TransactionType"].asString ()
|| "OfferCancel" == tx_json["TransactionType"].asString ()
|| "TrustSet" == tx_json["TransactionType"].asString ()))
{
txJSON["Fee"] = (int) getConfig ().FEE_DEFAULT;
tx_json["Fee"] = (int) getConfig ().FEE_DEFAULT;
}
if (!txJSON.isMember ("Sequence"))
if (!tx_json.isMember ("Sequence"))
{
if (bOffline)
{
@@ -267,11 +264,11 @@ Json::Value RPCHandler::transactionSign (Json::Value params, bool bSubmit, bool
}
else
{
txJSON["Sequence"] = asSrc->getSeq ();
tx_json["Sequence"] = asSrc->getSeq ();
}
}
if (!txJSON.isMember ("Flags")) txJSON["Flags"] = 0;
if (!tx_json.isMember ("Flags")) tx_json["Flags"] = 0;
if (!bOffline)
{
@@ -334,17 +331,22 @@ Json::Value RPCHandler::transactionSign (Json::Value params, bool bSubmit, bool
std::unique_ptr<STObject> sopTrans;
try
{
sopTrans = STObject::parseJson (txJSON);
STParsedJSON parsed ("tx_json", tx_json);
if (parsed.object.get() != nullptr)
{
// VFALCO NOTE No idea why this doesn't compile.
//sopTrans = parsed.object;
sopTrans.reset (parsed.object.release());
}
catch (std::exception& e)
else
{
jvResult["error"] = "malformedTransaction";
jvResult["error_exception"] = e.what ();
jvResult ["error"] = parsed.error ["error"];
jvResult ["error_code"] = parsed.error ["error_code"];
jvResult ["error_message"] = parsed.error ["error_message"];
return jvResult;
}
}
sopTrans->setFieldVL (sfSigningPubKey, naAccountPublic.getAccountPublic ());
@@ -634,24 +636,29 @@ Json::Value RPCHandler::accountFromString (Ledger::ref lrLedger, RippleAddress&
Json::Value RPCHandler::doAccountCurrencies (Json::Value params, Resource::Charge& loadType, Application::ScopedLockType& masterLockHolder)
{
masterLockHolder.unlock ();
// Get the current ledger
Ledger::pointer lpLedger;
Json::Value jvResult = lookupLedger (params, lpLedger);
Json::Value jvResult (lookupLedger (params, lpLedger));
if (!lpLedger)
return jvResult;
if (!params.isMember ("account") && !params.isMember ("ident"))
return rpcError (rpcINVALID_PARAMS);
if (! params.isMember ("account") && ! params.isMember ("ident"))
return RPC::missing_field_error ("account");
std::string strIdent = params.isMember ("account") ? params["account"].asString () : params["ident"].asString ();
bool bIndex;
int iIndex = params.isMember ("account_index") ? params["account_index"].asUInt () : 0;
bool bStrict = params.isMember ("strict") && params["strict"].asBool ();
RippleAddress naAccount;
std::string const strIdent (params.isMember ("account")
? params["account"].asString ()
: params["ident"].asString ());
int const iIndex (params.isMember ("account_index")
? params["account_index"].asUInt ()
: 0);
bool const bStrict (params.isMember ("strict") && params["strict"].asBool ());
// Get info on account.
Json::Value jvAccepted = accountFromString (lpLedger, naAccount, bIndex, strIdent, iIndex, bStrict);
bool bIndex; // out param
RippleAddress naAccount; // out param
Json::Value jvAccepted (accountFromString (
lpLedger, naAccount, bIndex, strIdent, iIndex, bStrict));
if (!jvAccepted.empty ())
return jvAccepted;
@@ -707,7 +714,7 @@ Json::Value RPCHandler::doAccountInfo (Json::Value params, Resource::Charge& loa
return jvResult;
if (!params.isMember ("account") && !params.isMember ("ident"))
return rpcError (rpcINVALID_PARAMS);
return RPC::missing_field_error ("account");
std::string strIdent = params.isMember ("account") ? params["account"].asString () : params["ident"].asString ();
bool bIndex;
@@ -759,7 +766,7 @@ Json::Value RPCHandler::doConnect (Json::Value params, Resource::Charge& loadTyp
return "cannot connect in standalone mode";
if (!params.isMember ("ip"))
return rpcError (rpcINVALID_PARAMS);
return RPC::missing_field_error ("ip");
std::string strIp = params["ip"].asString ();
int iPort = params.isMember ("port") ? params["port"].asInt () : -1;
@@ -777,7 +784,7 @@ Json::Value RPCHandler::doConnect (Json::Value params, Resource::Charge& loadTyp
Json::Value RPCHandler::doDataDelete (Json::Value params, Resource::Charge& loadType, Application::ScopedLockType& masterLockHolder)
{
if (!params.isMember ("key"))
return rpcError (rpcINVALID_PARAMS);
return RPC::missing_field_error ("key");
std::string strKey = params["key"].asString ();
@@ -803,7 +810,7 @@ Json::Value RPCHandler::doDataDelete (Json::Value params, Resource::Charge& load
Json::Value RPCHandler::doDataFetch (Json::Value params, Resource::Charge& loadType, Application::ScopedLockType& masterLockHolder)
{
if (!params.isMember ("key"))
return rpcError (rpcINVALID_PARAMS);
return RPC::missing_field_error ("key");
std::string strKey = params["key"].asString ();
std::string strValue;
@@ -827,8 +834,9 @@ Json::Value RPCHandler::doDataFetch (Json::Value params, Resource::Charge& loadT
Json::Value RPCHandler::doDataStore (Json::Value params, Resource::Charge& loadType, Application::ScopedLockType& masterLockHolder)
{
if (!params.isMember ("key")
|| !params.isMember ("value"))
return rpcError (rpcINVALID_PARAMS);
return RPC::missing_field_error ("key");
if (!params.isMember ("value")
return RPC::missing_field_error ("value");
std::string strKey = params["key"].asString ();
std::string strValue = params["value"].asString ();
@@ -888,7 +896,7 @@ Json::Value RPCHandler::doNicknameInfo (Json::Value params)
Json::Value RPCHandler::doOwnerInfo (Json::Value params, Resource::Charge& loadType, Application::ScopedLockType& masterLockHolder)
{
if (!params.isMember ("account") && !params.isMember ("ident"))
return rpcError (rpcINVALID_PARAMS);
return RPC::missing_field_error ("account");
std::string strIdent = params.isMember ("account") ? params["account"].asString () : params["ident"].asString ();
bool bIndex;
@@ -1051,12 +1059,12 @@ Json::Value RPCHandler::doProofCreate (Json::Value params, Resource::Charge& loa
if (params.isMember ("difficulty"))
{
if (!params["difficulty"].isIntegral ())
return rpcError (rpcINVALID_PARAMS);
return RPC::invalid_field_error ("difficulty");
int iDifficulty = params["difficulty"].asInt ();
int const iDifficulty (params["difficulty"].asInt ());
if (iDifficulty < 0 || iDifficulty > ProofOfWorkFactory::kMaxDifficulty)
return rpcError (rpcINVALID_PARAMS);
return RPC::invalid_field_error ("difficulty");
pgGen->setDifficulty (iDifficulty);
}
@@ -1088,12 +1096,12 @@ Json::Value RPCHandler::doProofSolve (Json::Value params, Resource::Charge& load
Json::Value jvResult;
if (!params.isMember ("token"))
return rpcError (rpcINVALID_PARAMS);
return RPC::missing_field_error ("token");
std::string strToken = params["token"].asString ();
if (!ProofOfWork::validateToken (strToken))
return rpcError (rpcINVALID_PARAMS);
return RPC::invalid_field_error ("token");
ProofOfWork powProof (strToken);
uint256 uSolution = powProof.solve ();
@@ -1119,10 +1127,10 @@ Json::Value RPCHandler::doProofVerify (Json::Value params, Resource::Charge& loa
Json::Value jvResult;
if (!params.isMember ("token"))
return rpcError (rpcINVALID_PARAMS);
return RPC::missing_field_error ("token");
if (!params.isMember ("solution"))
return rpcError (rpcINVALID_PARAMS);
return RPC::missing_field_error ("solution");
std::string strToken = params["token"].asString ();
uint256 uSolution (params["solution"].asString ());
@@ -1137,12 +1145,12 @@ Json::Value RPCHandler::doProofVerify (Json::Value params, Resource::Charge& loa
if (params.isMember ("difficulty"))
{
if (!params["difficulty"].isIntegral ())
return rpcError (rpcINVALID_PARAMS);
return RPC::invalid_field_error ("difficulty");
int iDifficulty = params["difficulty"].asInt ();
if (iDifficulty < 0 || iDifficulty > ProofOfWorkFactory::kMaxDifficulty)
return rpcError (rpcINVALID_PARAMS);
return RPC::missing_field_error ("difficulty");
pgGen->setDifficulty (iDifficulty);
}
@@ -1192,7 +1200,7 @@ Json::Value RPCHandler::doAccountLines (Json::Value params, Resource::Charge& lo
return jvResult;
if (!params.isMember ("account"))
return rpcError (rpcINVALID_PARAMS);
return RPC::missing_field_error ("account");
std::string strIdent = params["account"].asString ();
bool bIndex = params.isMember ("account_index");
@@ -1303,7 +1311,7 @@ Json::Value RPCHandler::doAccountOffers (Json::Value params, Resource::Charge& l
return jvResult;
if (!params.isMember ("account"))
return rpcError (rpcINVALID_PARAMS);
return RPC::missing_field_error ("account");
std::string strIdent = params["account"].asString ();
bool bIndex = params.isMember ("account_index");
@@ -1334,6 +1342,33 @@ Json::Value RPCHandler::doAccountOffers (Json::Value params, Resource::Charge& l
return jvResult;
}
template <class UnsignedInteger>
inline bool is_xrp (UnsignedInteger const& value)
{
return value.isZero();
}
template <class UnsignedInteger>
inline bool is_not_xrp (UnsignedInteger const& value)
{
return ! is_xrp (value);
}
inline uint160 const& xrp_issuer ()
{
return ACCOUNT_XRP;
}
inline uint160 const& xrp_currency ()
{
return CURRENCY_XRP;
}
inline uint160 const& neutral_issuer ()
{
return ACCOUNT_ONE;
}
// {
// "ledger_hash" : ledger, // Optional.
// "ledger_index" : ledger_index, // Optional.
@@ -1348,94 +1383,164 @@ Json::Value RPCHandler::doBookOffers (Json::Value params, Resource::Charge& load
{
masterLockHolder.unlock ();
// VFALCO TODO Here is a terrible place for this kind of business
// logic. It needs to be moved elsewhere and documented,
// and encapsulated into a function.
if (getApp().getJobQueue ().getJobCountGE (jtCLIENT) > 200)
{
return rpcError (rpcTOO_BUSY);
}
Ledger::pointer lpLedger;
Json::Value jvResult = lookupLedger (params, lpLedger);
Json::Value jvResult (lookupLedger (params, lpLedger));
if (!lpLedger)
return jvResult;
if (!params.isMember ("taker_pays") || !params.isMember ("taker_gets") || !params["taker_pays"].isObject () || !params["taker_gets"].isObject ())
return rpcError (rpcINVALID_PARAMS);
if (!params.isMember ("taker_pays"))
return RPC::missing_field_error ("taker_pays");
uint160 uTakerPaysCurrencyID;
uint160 uTakerPaysIssuerID;
const Json::Value& jvTakerPays = params["taker_pays"];
if (!params.isMember ("taker_gets"))
return RPC::missing_field_error ("taker_gets");
// Parse mandatory currency.
if (!jvTakerPays.isMember ("currency")
|| !STAmount::currencyFromString (uTakerPaysCurrencyID, jvTakerPays["currency"].asString ()))
if (!params["taker_pays"].isObject ())
return RPC::object_field_error ("taker_pays");
if (!params["taker_gets"].isObject ())
return RPC::object_field_error ("taker_gets");
Json::Value const& taker_pays (params["taker_pays"]);
if (!taker_pays.isMember ("currency"))
return RPC::missing_field_error ("taker_pays.currency");
if (! taker_pays ["currency"].isString ())
return RPC::expected_field_error ("taker_pays.currency", "string");
Json::Value const& taker_gets = params["taker_gets"];
if (! taker_gets.isMember ("currency"))
return RPC::missing_field_error ("taker_gets.currency");
if (! taker_gets ["currency"].isString ())
return RPC::expected_field_error ("taker_gets.currency", "string");
uint160 pay_currency;
if (! STAmount::currencyFromString (
pay_currency, taker_pays ["currency"].asString ()))
{
WriteLog (lsINFO, RPCHandler) << "Bad taker_pays currency.";
return rpcError (rpcSRC_CUR_MALFORMED);
return RPC::make_error (rpcSRC_CUR_MALFORMED,
"Invalid field 'taker_pays.currency', bad currency.");
}
// Parse optional issuer.
else if (((jvTakerPays.isMember ("issuer"))
&& (!jvTakerPays["issuer"].isString ()
|| !STAmount::issuerFromString (uTakerPaysIssuerID, jvTakerPays["issuer"].asString ())))
// Don't allow illegal issuers.
|| (!uTakerPaysCurrencyID != !uTakerPaysIssuerID)
|| ACCOUNT_ONE == uTakerPaysIssuerID)
uint160 get_currency;
if (! STAmount::currencyFromString (
get_currency, taker_gets ["currency"].asString ()))
{
WriteLog (lsINFO, RPCHandler) << "Bad taker_pays issuer.";
return rpcError (rpcSRC_ISR_MALFORMED);
WriteLog (lsINFO, RPCHandler) << "Bad taker_gets currency.";
return RPC::make_error (rpcDST_AMT_MALFORMED,
"Invalid field 'taker_gets.currency', bad currency.");
}
uint160 uTakerGetsCurrencyID;
uint160 uTakerGetsIssuerID;
const Json::Value& jvTakerGets = params["taker_gets"];
uint160 pay_issuer;
// Parse mandatory currency.
if (!jvTakerGets.isMember ("currency")
|| !STAmount::currencyFromString (uTakerGetsCurrencyID, jvTakerGets["currency"].asString ()))
if (taker_pays.isMember ("issuer"))
{
WriteLog (lsINFO, RPCHandler) << "Bad taker_pays currency.";
if (! taker_pays ["issuer"].isString())
return RPC::expected_field_error ("taker_pays.issuer", "string");
return rpcError (rpcSRC_CUR_MALFORMED);
if (! STAmount::issuerFromString (
pay_issuer, taker_pays ["issuer"].asString ()))
return RPC::make_error (rpcSRC_ISR_MALFORMED,
"Invalid field 'taker_pays.issuer', bad issuer.");
if (pay_issuer == neutral_issuer ())
return RPC::make_error (rpcSRC_ISR_MALFORMED,
"Invalid field 'taker_pays.issuer', bad issuer account one.");
}
// Parse optional issuer.
else if (((jvTakerGets.isMember ("issuer"))
&& (!jvTakerGets["issuer"].isString ()
|| !STAmount::issuerFromString (uTakerGetsIssuerID, jvTakerGets["issuer"].asString ())))
// Don't allow illegal issuers.
|| (!uTakerGetsCurrencyID != !uTakerGetsIssuerID)
|| ACCOUNT_ONE == uTakerGetsIssuerID)
else
{
WriteLog (lsINFO, RPCHandler) << "Bad taker_gets issuer.";
return rpcError (rpcDST_ISR_MALFORMED);
pay_issuer = xrp_issuer ();
}
if (uTakerPaysCurrencyID == uTakerGetsCurrencyID
&& uTakerPaysIssuerID == uTakerGetsIssuerID)
if (is_xrp (pay_currency) && ! is_xrp (pay_issuer))
return RPC::make_error (rpcSRC_ISR_MALFORMED,
"Unneeded field 'taker_pays.issuer' for XRP currency specification.");
if (is_not_xrp (pay_currency) && is_xrp (pay_issuer))
return RPC::make_error (rpcSRC_ISR_MALFORMED,
"Invalid field 'taker_pays.issuer', expected non-XRP issuer.");
uint160 get_issuer;
if (taker_gets.isMember ("issuer"))
{
WriteLog (lsINFO, RPCHandler) << "taker_gets same as taker_pays.";
if (! taker_gets ["issuer"].isString())
return RPC::expected_field_error ("taker_gets.issuer", "string");
return rpcError (rpcBAD_MARKET);
if (! STAmount::issuerFromString (
get_issuer, taker_gets ["issuer"].asString ()))
return RPC::make_error (rpcDST_ISR_MALFORMED,
"Invalid field 'taker_gets.issuer', bad issuer.");
if (get_issuer == neutral_issuer ())
return RPC::make_error (rpcDST_ISR_MALFORMED,
"Invalid field 'taker_gets.issuer', bad issuer account one.");
}
else
{
get_issuer = xrp_issuer ();
}
if (is_xrp (get_currency) && ! is_xrp (get_issuer))
return RPC::make_error (rpcDST_ISR_MALFORMED,
"Unneeded field 'taker_gets.issuer' for XRP currency specification.");
if (is_not_xrp (get_currency) && is_xrp (get_issuer))
return RPC::make_error (rpcDST_ISR_MALFORMED,
"Invalid field 'taker_gets.issuer', expected non-XRP issuer.");
RippleAddress raTakerID;
if (!params.isMember ("taker"))
if (params.isMember ("taker"))
{
if (! params ["taker"].isString ())
return RPC::expected_field_error ("taker", "string");
if (! raTakerID.setAccountID (params ["taker"].asString ()))
return RPC::invalid_field_error ("taker");
}
else
{
raTakerID.setAccountID (ACCOUNT_ONE);
}
else if (!raTakerID.setAccountID (params["taker"].asString ()))
if (pay_currency == get_currency && pay_issuer == get_issuer)
{
return rpcError (rpcBAD_ISSUER);
WriteLog (lsINFO, RPCHandler) << "taker_gets same as taker_pays.";
return RPC::make_error (rpcBAD_MARKET);
}
const bool bProof = params.isMember ("proof");
const unsigned int iLimit = params.isMember ("limit") ? params["limit"].asUInt () : 0;
const Json::Value jvMarker = params.isMember ("marker") ? params["marker"] : Json::Value (Json::nullValue);
if (params.isMember ("limit") && ! params ["limit"].isUInt())
return RPC::expected_field_error (
"taker_pays.currency", "unsigned integer");
unsigned int const iLimit (params.isMember ("limit")
? params ["limit"].asUInt ()
: 0);
bool const bProof (params.isMember ("proof"));
Json::Value const jvMarker (params.isMember ("marker")
? params["marker"]
: Json::Value (Json::nullValue));
mNetOps->getBookPage (lpLedger, pay_currency, pay_issuer,
get_currency, get_issuer, raTakerID.getAccountID (),
bProof, iLimit, jvMarker, jvResult);
mNetOps->getBookPage (lpLedger, uTakerPaysCurrencyID, uTakerPaysIssuerID, uTakerGetsCurrencyID, uTakerGetsIssuerID, raTakerID.getAccountID (), bProof, iLimit, jvMarker, jvResult);
loadType = Resource::feeMediumBurdenRPC;
return jvResult;
@@ -3573,33 +3678,35 @@ Json::Value RPCHandler::doSubscribe (Json::Value params, Resource::Charge& loadT
|| !jvSubRequest["taker_gets"].isObject ())
return rpcError (rpcINVALID_PARAMS);
RippleCurrency uTakerPaysCurrencyID;
RippleIssuer uTakerPaysIssuerID;
RippleCurrency uTakerGetsCurrencyID;
RippleIssuer uTakerGetsIssuerID;
// VFALCO TODO Use RippleAsset here
RippleCurrency pay_currency;
RippleIssuer pay_issuer;
RippleCurrency get_currency;
RippleIssuer get_issuer;
bool bBoth = (jvSubRequest.isMember ("both") && jvSubRequest["both"].asBool ())
|| (jvSubRequest.isMember ("both_sides") && jvSubRequest["both_sides"].asBool ()); // DEPRECATED
bool bSnapshot = (jvSubRequest.isMember ("snapshot") && jvSubRequest["snapshot"].asBool ())
|| (jvSubRequest.isMember ("state_now") && jvSubRequest["state_now"].asBool ()); // DEPRECATED
Json::Value jvTakerPays = jvSubRequest["taker_pays"];
Json::Value jvTakerGets = jvSubRequest["taker_gets"];
Json::Value taker_pays = jvSubRequest["taker_pays"];
Json::Value taker_gets = jvSubRequest["taker_gets"];
// Parse mandatory currency.
if (!jvTakerPays.isMember ("currency")
|| !STAmount::currencyFromString (uTakerPaysCurrencyID, jvTakerPays["currency"].asString ()))
if (!taker_pays.isMember ("currency")
|| !STAmount::currencyFromString (pay_currency, taker_pays["currency"].asString ()))
{
WriteLog (lsINFO, RPCHandler) << "Bad taker_pays currency.";
return rpcError (rpcSRC_CUR_MALFORMED);
}
// Parse optional issuer.
else if (((jvTakerPays.isMember ("issuer"))
&& (!jvTakerPays["issuer"].isString ()
|| !STAmount::issuerFromString (uTakerPaysIssuerID, jvTakerPays["issuer"].asString ())))
else if (((taker_pays.isMember ("issuer"))
&& (!taker_pays["issuer"].isString ()
|| !STAmount::issuerFromString (pay_issuer, taker_pays["issuer"].asString ())))
// Don't allow illegal issuers.
|| (!uTakerPaysCurrencyID != !uTakerPaysIssuerID)
|| ACCOUNT_ONE == uTakerPaysIssuerID)
|| (!pay_currency != !pay_issuer)
|| ACCOUNT_ONE == pay_issuer)
{
WriteLog (lsINFO, RPCHandler) << "Bad taker_pays issuer.";
@@ -3607,28 +3714,28 @@ Json::Value RPCHandler::doSubscribe (Json::Value params, Resource::Charge& loadT
}
// Parse mandatory currency.
if (!jvTakerGets.isMember ("currency")
|| !STAmount::currencyFromString (uTakerGetsCurrencyID, jvTakerGets["currency"].asString ()))
if (!taker_gets.isMember ("currency")
|| !STAmount::currencyFromString (get_currency, taker_gets["currency"].asString ()))
{
WriteLog (lsINFO, RPCHandler) << "Bad taker_pays currency.";
return rpcError (rpcSRC_CUR_MALFORMED);
}
// Parse optional issuer.
else if (((jvTakerGets.isMember ("issuer"))
&& (!jvTakerGets["issuer"].isString ()
|| !STAmount::issuerFromString (uTakerGetsIssuerID, jvTakerGets["issuer"].asString ())))
else if (((taker_gets.isMember ("issuer"))
&& (!taker_gets["issuer"].isString ()
|| !STAmount::issuerFromString (get_issuer, taker_gets["issuer"].asString ())))
// Don't allow illegal issuers.
|| (!uTakerGetsCurrencyID != !uTakerGetsIssuerID)
|| ACCOUNT_ONE == uTakerGetsIssuerID)
|| (!get_currency != !get_issuer)
|| ACCOUNT_ONE == get_issuer)
{
WriteLog (lsINFO, RPCHandler) << "Bad taker_gets issuer.";
return rpcError (rpcDST_ISR_MALFORMED);
}
if (uTakerPaysCurrencyID == uTakerGetsCurrencyID
&& uTakerPaysIssuerID == uTakerGetsIssuerID)
if (pay_currency == get_currency
&& pay_issuer == get_issuer)
{
WriteLog (lsINFO, RPCHandler) << "taker_gets same as taker_pays.";
@@ -3646,17 +3753,17 @@ Json::Value RPCHandler::doSubscribe (Json::Value params, Resource::Charge& loadT
return rpcError (rpcBAD_ISSUER);
}
if (!Ledger::isValidBook (uTakerPaysCurrencyID, uTakerPaysIssuerID, uTakerGetsCurrencyID, uTakerGetsIssuerID))
if (!Ledger::isValidBook (pay_currency, pay_issuer, get_currency, get_issuer))
{
WriteLog (lsWARNING, RPCHandler) << "Bad market: " <<
uTakerPaysCurrencyID << ":" << uTakerPaysIssuerID << " -> " <<
uTakerGetsCurrencyID << ":" << uTakerGetsIssuerID;
pay_currency << ":" << pay_issuer << " -> " <<
get_currency << ":" << get_issuer;
return rpcError (rpcBAD_MARKET);
}
mNetOps->subBook (ispSub, uTakerPaysCurrencyID, uTakerGetsCurrencyID, uTakerPaysIssuerID, uTakerGetsIssuerID);
mNetOps->subBook (ispSub, pay_currency, get_currency, pay_issuer, get_issuer);
if (bBoth) mNetOps->subBook (ispSub, uTakerGetsCurrencyID, uTakerPaysCurrencyID, uTakerGetsIssuerID, uTakerPaysIssuerID);
if (bBoth) mNetOps->subBook (ispSub, get_currency, pay_currency, get_issuer, pay_issuer);
if (bSnapshot)
{
@@ -3673,17 +3780,17 @@ Json::Value RPCHandler::doSubscribe (Json::Value params, Resource::Charge& loadT
Json::Value jvBids (Json::objectValue);
Json::Value jvAsks (Json::objectValue);
mNetOps->getBookPage (lpLedger, uTakerPaysCurrencyID, uTakerPaysIssuerID, uTakerGetsCurrencyID, uTakerGetsIssuerID, raTakerID.getAccountID (), false, 0, jvMarker, jvBids);
mNetOps->getBookPage (lpLedger, pay_currency, pay_issuer, get_currency, get_issuer, raTakerID.getAccountID (), false, 0, jvMarker, jvBids);
if (jvBids.isMember ("offers")) jvResult["bids"] = jvBids["offers"];
mNetOps->getBookPage (lpLedger, uTakerGetsCurrencyID, uTakerGetsIssuerID, uTakerPaysCurrencyID, uTakerPaysIssuerID, raTakerID.getAccountID (), false, 0, jvMarker, jvAsks);
mNetOps->getBookPage (lpLedger, get_currency, get_issuer, pay_currency, pay_issuer, raTakerID.getAccountID (), false, 0, jvMarker, jvAsks);
if (jvAsks.isMember ("offers")) jvResult["asks"] = jvAsks["offers"];
}
else
{
mNetOps->getBookPage (lpLedger, uTakerPaysCurrencyID, uTakerPaysIssuerID, uTakerGetsCurrencyID, uTakerGetsIssuerID, raTakerID.getAccountID (), false, 0, jvMarker, jvResult);
mNetOps->getBookPage (lpLedger, pay_currency, pay_issuer, get_currency, get_issuer, raTakerID.getAccountID (), false, 0, jvMarker, jvResult);
}
}
}
@@ -3811,31 +3918,31 @@ Json::Value RPCHandler::doUnsubscribe (Json::Value params, Resource::Charge& loa
|| !jvSubRequest["taker_gets"].isObject ())
return rpcError (rpcINVALID_PARAMS);
uint160 uTakerPaysCurrencyID;
uint160 uTakerPaysIssuerID;
uint160 uTakerGetsCurrencyID;
uint160 uTakerGetsIssuerID;
uint160 pay_currency;
uint160 pay_issuer;
uint160 get_currency;
uint160 get_issuer;
bool bBoth = (jvSubRequest.isMember ("both") && jvSubRequest["both"].asBool ())
|| (jvSubRequest.isMember ("both_sides") && jvSubRequest["both_sides"].asBool ()); // DEPRECATED
Json::Value jvTakerPays = jvSubRequest["taker_pays"];
Json::Value jvTakerGets = jvSubRequest["taker_gets"];
Json::Value taker_pays = jvSubRequest["taker_pays"];
Json::Value taker_gets = jvSubRequest["taker_gets"];
// Parse mandatory currency.
if (!jvTakerPays.isMember ("currency")
|| !STAmount::currencyFromString (uTakerPaysCurrencyID, jvTakerPays["currency"].asString ()))
if (!taker_pays.isMember ("currency")
|| !STAmount::currencyFromString (pay_currency, taker_pays["currency"].asString ()))
{
WriteLog (lsINFO, RPCHandler) << "Bad taker_pays currency.";
return rpcError (rpcSRC_CUR_MALFORMED);
}
// Parse optional issuer.
else if (((jvTakerPays.isMember ("issuer"))
&& (!jvTakerPays["issuer"].isString ()
|| !STAmount::issuerFromString (uTakerPaysIssuerID, jvTakerPays["issuer"].asString ())))
else if (((taker_pays.isMember ("issuer"))
&& (!taker_pays["issuer"].isString ()
|| !STAmount::issuerFromString (pay_issuer, taker_pays["issuer"].asString ())))
// Don't allow illegal issuers.
|| (!uTakerPaysCurrencyID != !uTakerPaysIssuerID)
|| ACCOUNT_ONE == uTakerPaysIssuerID)
|| (!pay_currency != !pay_issuer)
|| ACCOUNT_ONE == pay_issuer)
{
WriteLog (lsINFO, RPCHandler) << "Bad taker_pays issuer.";
@@ -3843,37 +3950,37 @@ Json::Value RPCHandler::doUnsubscribe (Json::Value params, Resource::Charge& loa
}
// Parse mandatory currency.
if (!jvTakerGets.isMember ("currency")
|| !STAmount::currencyFromString (uTakerGetsCurrencyID, jvTakerGets["currency"].asString ()))
if (!taker_gets.isMember ("currency")
|| !STAmount::currencyFromString (get_currency, taker_gets["currency"].asString ()))
{
WriteLog (lsINFO, RPCHandler) << "Bad taker_pays currency.";
return rpcError (rpcSRC_CUR_MALFORMED);
}
// Parse optional issuer.
else if (((jvTakerGets.isMember ("issuer"))
&& (!jvTakerGets["issuer"].isString ()
|| !STAmount::issuerFromString (uTakerGetsIssuerID, jvTakerGets["issuer"].asString ())))
else if (((taker_gets.isMember ("issuer"))
&& (!taker_gets["issuer"].isString ()
|| !STAmount::issuerFromString (get_issuer, taker_gets["issuer"].asString ())))
// Don't allow illegal issuers.
|| (!uTakerGetsCurrencyID != !uTakerGetsIssuerID)
|| ACCOUNT_ONE == uTakerGetsIssuerID)
|| (!get_currency != !get_issuer)
|| ACCOUNT_ONE == get_issuer)
{
WriteLog (lsINFO, RPCHandler) << "Bad taker_gets issuer.";
return rpcError (rpcDST_ISR_MALFORMED);
}
if (uTakerPaysCurrencyID == uTakerGetsCurrencyID
&& uTakerPaysIssuerID == uTakerGetsIssuerID)
if (pay_currency == get_currency
&& pay_issuer == get_issuer)
{
WriteLog (lsINFO, RPCHandler) << "taker_gets same as taker_pays.";
return rpcError (rpcBAD_MARKET);
}
mNetOps->unsubBook (ispSub->getSeq (), uTakerPaysCurrencyID, uTakerGetsCurrencyID, uTakerPaysIssuerID, uTakerGetsIssuerID);
mNetOps->unsubBook (ispSub->getSeq (), pay_currency, get_currency, pay_issuer, get_issuer);
if (bBoth) mNetOps->unsubBook (ispSub->getSeq (), uTakerGetsCurrencyID, uTakerPaysCurrencyID, uTakerGetsIssuerID, uTakerPaysIssuerID);
if (bBoth) mNetOps->unsubBook (ispSub->getSeq (), get_currency, pay_currency, get_issuer, pay_issuer);
}
}
@@ -3889,12 +3996,12 @@ Json::Value RPCHandler::doRpcCommand (const std::string& strMethod, Json::Value
WriteLog (lsTRACE, RPCHandler) << "doRpcCommand:" << strMethod << ":" << jvParams;
if (!jvParams.isArray () || jvParams.size () > 1)
return rpcError (rpcINVALID_PARAMS);
return logRPCError (rpcError (rpcINVALID_PARAMS));
Json::Value params = jvParams.size () ? jvParams[0u] : Json::Value (Json::objectValue);
if (!params.isObject ())
return rpcError (rpcINVALID_PARAMS);
return logRPCError (rpcError (rpcINVALID_PARAMS));
// Provide the JSON-RPC method as the field "command" in the request.
params["command"] = strMethod;
@@ -3922,7 +4029,7 @@ Json::Value RPCHandler::doRpcCommand (const std::string& strMethod, Json::Value
jvResult["status"] = "success";
}
return jvResult;
return logRPCError (jvResult);
}
Json::Value RPCHandler::doInternal (Json::Value params, Resource::Charge& loadType, Application::ScopedLockType& masterLockHolder)

View File

@@ -0,0 +1,717 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
namespace ripple {
STParsedJSON::STParsedJSON (std::string const& name, Json::Value const& json)
{
parse (name, json, sfGeneric, 0, object);
}
//------------------------------------------------------------------------------
std::string STParsedJSON::make_name (std::string const& object,
std::string const& field)
{
if (field.empty ())
return object;
return object + "." + field;
}
Json::Value STParsedJSON::not_an_object (std::string const& object,
std::string const& field)
{
return RPC::make_error (rpcINVALID_PARAMS,
"Field '" + make_name (object, field) + "' is not a JSON object.");
}
Json::Value STParsedJSON::unknown_field (std::string const& object,
std::string const& field)
{
return RPC::make_error (rpcINVALID_PARAMS,
"Field '" + make_name (object, field) + "' is unknown.");
}
Json::Value STParsedJSON::out_of_range (std::string const& object,
std::string const& field)
{
return RPC::make_error (rpcINVALID_PARAMS,
"Field '" + make_name (object, field) + "' is out of range.");
}
Json::Value STParsedJSON::bad_type (std::string const& object,
std::string const& field)
{
return RPC::make_error (rpcINVALID_PARAMS,
"Field '" + make_name (object, field) + "' has bad type.");
}
Json::Value STParsedJSON::invalid_data (std::string const& object,
std::string const& field)
{
return RPC::make_error (rpcINVALID_PARAMS,
"Field '" + make_name (object, field) + "' has invalid data.");
}
Json::Value STParsedJSON::array_expected (std::string const& object,
std::string const& field)
{
return RPC::make_error (rpcINVALID_PARAMS,
"Field '" + make_name (object, field) + "' must be a JSON array.");
}
Json::Value STParsedJSON::string_expected (std::string const& object,
std::string const& field)
{
return RPC::make_error (rpcINVALID_PARAMS,
"Field '" + make_name (object, field) + "' must be a string.");
}
Json::Value STParsedJSON::too_deep (std::string const& object,
std::string const& field)
{
return RPC::make_error (rpcINVALID_PARAMS,
"Field '" + make_name (object, field) + "' exceeds nesting depth limit.");
}
Json::Value STParsedJSON::singleton_expected (std::string const& object)
{
return RPC::make_error (rpcINVALID_PARAMS,
"Field '" + object +
"' must be an object with a single key/object value.");
}
//------------------------------------------------------------------------------
bool STParsedJSON::parse (std::string const& json_name,
Json::Value const& json, SField::ref inName, int depth,
std::unique_ptr <STObject>& sub_object)
{
if (! json.isObject ())
{
error = not_an_object (json_name);
return false;
}
SField::ptr name (&inName);
boost::ptr_vector<SerializedType> data;
Json::Value::Members members (json.getMemberNames ());
for (Json::Value::Members::iterator it (members.begin ());
it != members.end (); ++it)
{
std::string const& fieldName = *it;
Json::Value const& value = json [fieldName];
SField::ref field = SField::getField (fieldName);
if (field == sfInvalid)
{
error = unknown_field (json_name, fieldName);
return false;
}
switch (field.fieldType)
{
case STI_UINT8:
try
{
if (value.isString ())
{
// VFALCO TODO wtf?
}
else if (value.isInt ())
{
if (value.asInt () < 0 || value.asInt () > 255)
{
error = out_of_range (json_name, fieldName);
return false;
}
data.push_back (new STUInt8 (field,
range_check_cast <unsigned char> (
value.asInt (), 0, 255)));
}
else if (value.isUInt ())
{
if (value.asUInt () > 255)
{
error = out_of_range (json_name, fieldName);
return false;
}
data.push_back (new STUInt8 (field,
range_check_cast <unsigned char> (
value.asUInt (), 0, 255)));
}
else
{
error = bad_type (json_name, fieldName);
return false;
}
}
catch (...)
{
error = invalid_data (json_name, fieldName);
return false;
}
break;
case STI_UINT16:
try
{
if (value.isString ())
{
std::string strValue = value.asString ();
if (! strValue.empty () &&
((strValue[0] < '0') || (strValue[0] > '9')))
{
if (field == sfTransactionType)
{
TxType const txType (TxFormats::getInstance()->
findTypeByName (strValue));
data.push_back (new STUInt16 (field,
static_cast <uint16> (txType)));
if (*name == sfGeneric)
name = &sfTransaction;
}
else if (field == sfLedgerEntryType)
{
LedgerEntryType const type (LedgerFormats::getInstance()->
findTypeByName (strValue));
data.push_back (new STUInt16 (field,
static_cast <uint16> (type)));
if (*name == sfGeneric)
name = &sfLedgerEntry;
}
else
{
error = invalid_data (json_name, fieldName);
return false;
}
}
else
{
data.push_back (new STUInt16 (field,
lexicalCastThrow <uint16> (strValue)));
}
}
else if (value.isInt ())
{
data.push_back (new STUInt16 (field,
range_check_cast <uint16> (
value.asInt (), 0, 65535)));
}
else if (value.isUInt ())
{
data.push_back (new STUInt16 (field,
range_check_cast <uint16> (
value.asUInt (), 0, 65535)));
}
else
{
error = bad_type (json_name, fieldName);
return false;
}
}
catch (...)
{
error = invalid_data (json_name, fieldName);
return false;
}
break;
case STI_UINT32:
try
{
if (value.isString ())
{
data.push_back (new STUInt32 (field,
lexicalCastThrow <uint32> (value.asString ())));
}
else if (value.isInt ())
{
data.push_back (new STUInt32 (field,
range_check_cast <uint32> (value.asInt (), 0u, 4294967295u)));
}
else if (value.isUInt ())
{
data.push_back (new STUInt32 (field,
static_cast <uint32> (value.asUInt ())));
}
else
{
error = bad_type (json_name, fieldName);
return false;
}
}
catch (...)
{
error = invalid_data (json_name, fieldName);
return false;
}
break;
case STI_UINT64:
try
{
if (value.isString ())
{
data.push_back (new STUInt64 (field,
uintFromHex (value.asString ())));
}
else if (value.isInt ())
{
data.push_back (new STUInt64 (field,
range_check_cast<uint64> (
value.asInt (), 0, 18446744073709551615ull)));
}
else if (value.isUInt ())
{
data.push_back (new STUInt64 (field,
static_cast <uint64> (value.asUInt ())));
}
else
{
error = bad_type (json_name, fieldName);
return false;
}
}
catch (...)
{
error = invalid_data (json_name, fieldName);
return false;
}
break;
case STI_HASH128:
try
{
if (value.isString ())
{
data.push_back (new STHash128 (field, value.asString ()));
}
else
{
error = bad_type (json_name, fieldName);
return false;
}
}
catch (...)
{
error = invalid_data (json_name, fieldName);
return false;
}
break;
case STI_HASH160:
try
{
if (value.isString ())
{
data.push_back (new STHash160 (field, value.asString ()));
}
else
{
error = bad_type (json_name, fieldName);
return false;
}
}
catch (...)
{
error = invalid_data (json_name, fieldName);
return false;
}
break;
case STI_HASH256:
try
{
if (value.isString ())
{
data.push_back (new STHash256 (field, value.asString ()));
}
else
{
error = bad_type (json_name, fieldName);
return false;
}
}
catch (...)
{
error = invalid_data (json_name, fieldName);
return false;
}
break;
case STI_VL:
if (! value.isString ())
{
error = bad_type (json_name, fieldName);
return false;
}
try
{
data.push_back (new STVariableLength (field,
strUnHex (value.asString ())));
}
catch (...)
{
error = invalid_data (json_name, fieldName);
return false;
}
break;
case STI_AMOUNT:
try
{
data.push_back (new STAmount (field, value));
}
catch (...)
{
error = invalid_data (json_name, fieldName);
return false;
}
break;
case STI_VECTOR256:
if (! value.isArray ())
{
error = array_expected (json_name, fieldName);
return false;
}
try
{
data.push_back (new STVector256 (field));
STVector256* tail (dynamic_cast <STVector256*> (&data.back ()));
check_precondition (tail);
for (Json::UInt i = 0; !json.isValidIndex (i); ++i)
{
uint256 s;
s.SetHex (json[i].asString ());
tail->addValue (s);
}
}
catch (...)
{
error = invalid_data (json_name, fieldName);
return false;
}
break;
case STI_PATHSET:
if (!value.isArray ())
{
error = array_expected (json_name, fieldName);
return false;
}
try
{
data.push_back (new STPathSet (field));
STPathSet* tail = dynamic_cast <STPathSet*> (&data.back ());
check_precondition (tail);
for (Json::UInt i = 0; value.isValidIndex (i); ++i)
{
STPath p;
if (!value[i].isArray ())
{
std::stringstream ss;
ss << fieldName << "[" << i << "]";
error = array_expected (json_name, ss.str ());
return false;
}
for (Json::UInt j = 0; value[i].isValidIndex (j); ++j)
{
std::stringstream ss;
ss << fieldName << "[" << i << "][" << j << "]";
std::string const element_name (
json_name + "." + ss.str());
// each element in this path has some combination of account,
// currency, or issuer
Json::Value pathEl = value[i][j];
if (!pathEl.isObject ())
{
error = not_an_object (element_name);
return false;
}
const Json::Value& account = pathEl["account"];
const Json::Value& currency = pathEl["currency"];
const Json::Value& issuer = pathEl["issuer"];
bool hasCurrency = false;
uint160 uAccount, uCurrency, uIssuer;
if (! account.isNull ())
{
// human account id
if (! account.isString ())
{
error = string_expected (element_name, "account");
return false;
}
std::string const strValue (account.asString ());
if (value.size () == 40) // 160-bit hex account value
uAccount.SetHex (strValue);
{
RippleAddress a;
if (! a.setAccountID (strValue))
{
error = invalid_data (element_name, "account");
return false;
}
uAccount = a.getAccountID ();
}
}
if (!currency.isNull ())
{
// human currency
if (!currency.isString ())
{
error = string_expected (element_name, "currency");
return false;
}
hasCurrency = true;
if (currency.asString ().size () == 40)
{
uCurrency.SetHex (currency.asString ());
}
else if (!STAmount::currencyFromString (
uCurrency, currency.asString ()))
{
error = invalid_data (element_name, "currency");
return false;
}
}
if (!issuer.isNull ())
{
// human account id
if (!issuer.isString ())
{
error = string_expected (element_name, "issuer");
return false;
}
if (issuer.asString ().size () == 40)
{
uIssuer.SetHex (issuer.asString ());
}
else
{
RippleAddress a;
if (!a.setAccountID (issuer.asString ()))
{
error = invalid_data (element_name, "issuer");
return false;
}
uIssuer = a.getAccountID ();
}
}
p.addElement (STPathElement (uAccount, uCurrency, uIssuer, hasCurrency));
}
tail->addPath (p);
}
}
catch (...)
{
error = invalid_data (json_name, fieldName);
return false;
}
break;
case STI_ACCOUNT:
{
if (! value.isString ())
{
error = bad_type (json_name, fieldName);
return false;
}
std::string strValue = value.asString ();
try
{
if (value.size () == 40) // 160-bit hex account value
{
uint160 v;
v.SetHex (strValue);
data.push_back (new STAccount (field, v));
}
else
{
// ripple address
RippleAddress a;
if (!a.setAccountID (strValue))
{
error = invalid_data (json_name, fieldName);
return false;
}
data.push_back (new STAccount (field, a.getAccountID ()));
}
}
catch (...)
{
error = invalid_data (json_name, fieldName);
return false;
}
}
break;
case STI_OBJECT:
case STI_TRANSACTION:
case STI_LEDGERENTRY:
case STI_VALIDATION:
if (! value.isObject ())
{
error = not_an_object (json_name, fieldName);
return false;
}
if (depth > 64)
{
error = too_deep (json_name, fieldName);
return false;
}
try
{
std::unique_ptr <STObject> sub_object_;
bool const success (parse (json_name + "." + fieldName,
value, field, depth + 1, sub_object_));
if (! success)
return false;
data.push_back (sub_object_.release ());
}
catch (...)
{
error = invalid_data (json_name, fieldName);
return false;
}
break;
case STI_ARRAY:
if (! value.isArray ())
{
error = array_expected (json_name, fieldName);
return false;
}
try
{
data.push_back (new STArray (field));
STArray* tail = dynamic_cast<STArray*> (&data.back ());
check_precondition (tail);
for (Json::UInt i = 0; value.isValidIndex (i); ++i)
{
bool const isObject (value[i].isObject());
bool const singleKey (isObject
? value [i].size() == 1
: true);
if (!isObject || !singleKey)
{
std::stringstream ss;
ss << json_name << "." << fieldName << "[" << i << "]";
error = singleton_expected (ss.str ());
return false;
}
// TODO: There doesn't seem to be a nice way to get just the
// first/only key in an object without copying all keys into
// a vector
std::string const objectName (value[i].getMemberNames()[0]);;
SField::ref const nameField (SField::getField(objectName));
Json::Value const objectFields (value[i][objectName]);
std::unique_ptr <STObject> sub_object_;
{
std::stringstream ss;
ss << json_name << "." << fieldName <<
"[" << i << "]." << objectName;
bool const success (parse (ss.str (), objectFields,
nameField, depth + 1, sub_object_));
if (! success)
return false;
}
tail->push_back (*sub_object_);
}
}
catch (...)
{
error = invalid_data (json_name, fieldName);
return false;
}
break;
default:
error = bad_type (json_name, fieldName);
return false;
}
}
sub_object.reset (new STObject (*name, data));
return true;
}
}

View File

@@ -0,0 +1,84 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_DATA_STPARSEDJSON_H
#define RIPPLE_DATA_STPARSEDJSON_H
namespace ripple {
/** Holds the serialized result of parsing input JSON.
This does validation and checking on the provided JSON.
*/
class STParsedJSON
{
public:
/** Parses and creates an STParsedJSON object.
The result of the parsing is stored in object and error.
Exceptions:
Does not throw.
@param name The name of the JSON field, used in diagnostics.
@param json The JSON-RPC to parse.
*/
STParsedJSON (std::string const& name,
Json::Value const& json);
/** The STObject if the parse was successful. */
std::unique_ptr <STObject> object;
/** On failure, an appropriate set of error values. */
Json::Value error;
private:
static std::string make_name (std::string const& object,
std::string const& field);
static Json::Value not_an_object (std::string const& object,
std::string const& field = std::string());
static Json::Value unknown_field (std::string const& object,
std::string const& field = std::string());
static Json::Value out_of_range (std::string const& object,
std::string const& field = std::string());
static Json::Value bad_type (std::string const& object,
std::string const& field = std::string());
static Json::Value invalid_data (std::string const& object,
std::string const& field = std::string());
static Json::Value array_expected (std::string const& object,
std::string const& field = std::string());
static Json::Value string_expected (std::string const& object,
std::string const& field = std::string());
static Json::Value too_deep (std::string const& object,
std::string const& field = std::string());
static Json::Value singleton_expected (
std::string const& object);
bool parse (std::string const& json_name, Json::Value const& json,
SField::ref inName, int depth, std::unique_ptr <STObject>& sub_object);
};
}
#endif

View File

@@ -138,7 +138,7 @@ void STObject::set (const SOTemplate& type)
mData.clear ();
mType = &type;
BOOST_FOREACH (const SOElement * elem, type.peek ())
for (SOTemplate::value_type const& elem : type.peek ())
{
if (elem->flags != SOE_REQUIRED)
giveObject (makeNonPresentObject (elem->e_field));
@@ -154,7 +154,7 @@ bool STObject::setType (const SOTemplate& type)
mType = &type;
BOOST_FOREACH (const SOElement * elem, type.peek ())
for (SOTemplate::value_type const& elem : type.peek ())
{
bool match = false;
@@ -208,7 +208,7 @@ bool STObject::isValidForType ()
{
boost::ptr_vector<SerializedType>::iterator it = mData.begin ();
BOOST_FOREACH (const SOElement * elem, mType->peek ())
for (SOTemplate::value_type const& elem : mType->peek ())
{
if (it == mData.end ())
return false;
@@ -1232,381 +1232,6 @@ void STArray::sort (bool (*compare) (const STObject&, const STObject&))
value.sort (compare);
}
std::unique_ptr<STObject> STObject::parseJson (const Json::Value& object, SField::ref inName, int depth)
{
if (!object.isObject ())
throw std::runtime_error ("Value is not an object");
SField::ptr name = &inName;
boost::ptr_vector<SerializedType> data;
Json::Value::Members members (object.getMemberNames ());
for (Json::Value::Members::iterator it = members.begin (), end = members.end (); it != end; ++it)
{
const std::string& fieldName = *it;
const Json::Value& value = object[fieldName];
SField::ref field = SField::getField (fieldName);
if (field == sfInvalid)
throw std::runtime_error ("Unknown field: " + fieldName);
switch (field.fieldType)
{
case STI_UINT8:
if (value.isString ())
{
#if 0
if (field == sfTransactionResult)
{
TER terCode;
if (FUNCTION_THAT_DOESNT_EXIST (value.asString (), terCode))
value = static_cast<int> (terCode);
else
data.push_back (new STUInt8 (field, lexicalCastThrow <unsigned char> (value.asString ())));
}
data.push_back (new STUInt8 (field, lexicalCastThrow <unsigned char> (value.asString ())));
#endif
}
else if (value.isInt ())
{
if (value.asInt () < 0 || value.asInt () > 255)
throw std::runtime_error ("value out of range");
data.push_back (new STUInt8 (field, range_check_cast<unsigned char> (value.asInt (), 0, 255)));
}
else if (value.isUInt ())
{
if (value.asUInt () > 255)
throw std::runtime_error ("value out of range");
data.push_back (new STUInt8 (field, range_check_cast<unsigned char> (value.asUInt (), 0, 255)));
}
else
throw std::runtime_error ("Incorrect type");
break;
case STI_UINT16:
if (value.isString ())
{
std::string strValue = value.asString ();
if (!strValue.empty () && ((strValue[0] < '0') || (strValue[0] > '9')))
{
if (field == sfTransactionType)
{
// Retrieve type from name. Throws if not found.
TxType const txType = TxFormats::getInstance()->findTypeByName (strValue);
data.push_back (new STUInt16 (field, static_cast<uint16> (txType)));
if (*name == sfGeneric)
name = &sfTransaction;
}
else if (field == sfLedgerEntryType)
{
LedgerEntryType const type = LedgerFormats::getInstance()->findTypeByName (strValue);
data.push_back (new STUInt16 (field, static_cast<uint16> (type)));
if (*name == sfGeneric)
name = &sfLedgerEntry;
}
else
throw std::runtime_error ("Invalid field data");
}
else
data.push_back (new STUInt16 (field, lexicalCastThrow <uint16> (strValue)));
}
else if (value.isInt ())
data.push_back (new STUInt16 (field, range_check_cast<uint16> (value.asInt (), 0, 65535)));
else if (value.isUInt ())
data.push_back (new STUInt16 (field, range_check_cast<uint16> (value.asUInt (), 0, 65535)));
else
throw std::runtime_error ("Incorrect type");
break;
case STI_UINT32:
if (value.isString ())
{
data.push_back (new STUInt32 (field, lexicalCastThrow <uint32> (value.asString ())));
}
else if (value.isInt ())
{
data.push_back (new STUInt32 (field, range_check_cast <uint32> (value.asInt (), 0u, 4294967295u)));
}
else if (value.isUInt ())
{
data.push_back (new STUInt32 (field, static_cast<uint32> (value.asUInt ())));
}
else
{
throw std::runtime_error ("Incorrect type");
}
break;
case STI_UINT64:
if (value.isString ())
data.push_back (new STUInt64 (field, uintFromHex (value.asString ())));
else if (value.isInt ())
data.push_back (new STUInt64 (field,
range_check_cast<uint64> (value.asInt (), 0, 18446744073709551615ull)));
else if (value.isUInt ())
data.push_back (new STUInt64 (field, static_cast<uint64> (value.asUInt ())));
else
throw std::runtime_error ("Incorrect type");
break;
case STI_HASH128:
if (value.isString ())
data.push_back (new STHash128 (field, value.asString ()));
else
throw std::runtime_error ("Incorrect type");
break;
case STI_HASH160:
if (value.isString ())
data.push_back (new STHash160 (field, value.asString ()));
else
throw std::runtime_error ("Incorrect type");
break;
case STI_HASH256:
if (value.isString ())
data.push_back (new STHash256 (field, value.asString ()));
else
throw std::runtime_error ("Incorrect type");
break;
case STI_VL:
if (!value.isString ())
throw std::runtime_error ("Incorrect type");
data.push_back (new STVariableLength (field, strUnHex (value.asString ())));
break;
case STI_AMOUNT:
data.push_back (new STAmount (field, value));
break;
case STI_VECTOR256:
if (!value.isArray ())
throw std::runtime_error ("Incorrect type");
{
data.push_back (new STVector256 (field));
STVector256* tail = dynamic_cast<STVector256*> (&data.back ());
assert (tail);
for (Json::UInt i = 0; !object.isValidIndex (i); ++i)
{
uint256 s;
s.SetHex (object[i].asString ());
tail->addValue (s);
}
}
break;
case STI_PATHSET:
if (!value.isArray ())
throw std::runtime_error ("Path set must be array");
{
data.push_back (new STPathSet (field));
STPathSet* tail = dynamic_cast<STPathSet*> (&data.back ());
assert (tail);
for (Json::UInt i = 0; value.isValidIndex (i); ++i)
{
STPath p;
if (!value[i].isArray ())
throw std::runtime_error ("Path must be array");
for (Json::UInt j = 0; value[i].isValidIndex (j); ++j)
{
// each element in this path has some combination of account, currency, or issuer
Json::Value pathEl = value[i][j];
if (!pathEl.isObject ())
throw std::runtime_error ("Path elements must be objects");
const Json::Value& account = pathEl["account"];
const Json::Value& currency = pathEl["currency"];
const Json::Value& issuer = pathEl["issuer"];
bool hasCurrency = false;
uint160 uAccount, uCurrency, uIssuer;
if (!account.isNull ())
{
// human account id
if (!account.isString ())
throw std::runtime_error ("path element accounts must be strings");
std::string strValue = account.asString ();
if (value.size () == 40) // 160-bit hex account value
uAccount.SetHex (strValue);
{
RippleAddress a;
if (!a.setAccountID (strValue))
throw std::runtime_error ("Account in path element invalid");
uAccount = a.getAccountID ();
}
}
if (!currency.isNull ())
{
// human currency
if (!currency.isString ())
throw std::runtime_error ("path element currencies must be strings");
hasCurrency = true;
if (currency.asString ().size () == 40)
uCurrency.SetHex (currency.asString ());
else if (!STAmount::currencyFromString (uCurrency, currency.asString ()))
throw std::runtime_error ("invalid currency");
}
if (!issuer.isNull ())
{
// human account id
if (!issuer.isString ())
throw std::runtime_error ("path element issuers must be strings");
if (issuer.asString ().size () == 40)
uIssuer.SetHex (issuer.asString ());
else
{
RippleAddress a;
if (!a.setAccountID (issuer.asString ()))
throw std::runtime_error ("path element issuer invalid");
uIssuer = a.getAccountID ();
}
}
p.addElement (STPathElement (uAccount, uCurrency, uIssuer, hasCurrency));
}
tail->addPath (p);
}
}
break;
case STI_ACCOUNT:
{
if (!value.isString ())
throw std::runtime_error ("Incorrect type");
std::string strValue = value.asString ();
if (value.size () == 40) // 160-bit hex account value
{
uint160 v;
v.SetHex (strValue);
data.push_back (new STAccount (field, v));
}
else
{
// ripple address
RippleAddress a;
if (!a.setAccountID (strValue))
{
WriteLog (lsINFO, STObject) << "Invalid acccount JSON: " << fieldName << ": " << strValue;
throw std::runtime_error ("Account invalid");
}
data.push_back (new STAccount (field, a.getAccountID ()));
}
}
break;
case STI_OBJECT:
case STI_TRANSACTION:
case STI_LEDGERENTRY:
case STI_VALIDATION:
if (!value.isObject ())
throw std::runtime_error ("Inner value is not an object");
if (depth > 64)
throw std::runtime_error ("Json nest depth exceeded");
data.push_back (parseJson (value, field, depth + 1).release ());
break;
case STI_ARRAY:
if (!value.isArray ())
throw std::runtime_error ("Inner value is not an array");
{
data.push_back (new STArray (field));
STArray* tail = dynamic_cast<STArray*> (&data.back ());
assert (tail);
for (Json::UInt i = 0; value.isValidIndex (i); ++i)
{
bool isObject (value[i].isObject());
bool singleKey (true);
if (isObject)
{
singleKey = value[i].size() == 1;
}
if (!isObject || !singleKey)
{
std::stringstream err;
err << "First level children of `"
<< field.getName()
<< "`must be objects containing a single key with"
<< " an object value";
throw std::runtime_error (err.str());
}
// TODO: There doesn't seem to be a nice way to get just the
// first/only key in an object without copying all keys into
// a vector
std::string objectName (value[i].getMemberNames()[0]);;
SField::ref nameField (SField::getField(objectName));
Json::Value objectFields (value[i][objectName]);
tail->push_back (*STObject::parseJson (objectFields,
nameField,
depth + 1));
}
}
break;
default:
throw std::runtime_error ("Invalid field type");
}
}
return std::unique_ptr<STObject> (new STObject (*name, data));
}
//------------------------------------------------------------------------------
class SerializedObjectTests : public UnitTest
@@ -1656,8 +1281,9 @@ public:
Json::Value faultyJson;
bool parsedOK (parseJSONString(faulty, faultyJson));
unexpected(!parsedOK, "failed to parse");
so = STObject::parseJson (faultyJson);
fail ("It should have thrown. "
STParsedJSON parsed ("test", faultyJson);
expect (parsed.object.get() == nullptr,
"It should have thrown. "
"Immediate children of STArray encoded as json must "
"have one key only.");
}
@@ -1677,12 +1303,11 @@ public:
bool parsedOK (parseJSONString(json, jsonObject));
if (parsedOK)
{
std::unique_ptr<STObject> so;
so = STObject::parseJson (jsonObject);
STParsedJSON parsed ("test", jsonObject);
Json::FastWriter writer;
std::string const& serialized (writer.write(so->getJson(0)));
bool serializedOK = serialized == json;
unexpected (!serializedOK, serialized + " should equal: " + json);
std::string const& serialized (
writer.write (parsed.object->getJson(0)));
expect (serialized == json, serialized + " should equal: " + json);
}
else
{

View File

@@ -48,17 +48,17 @@ public:
setType (type);
}
std::unique_ptr<STObject> oClone () const
STObject (SField::ref name, boost::ptr_vector<SerializedType>& data) : SerializedType (name), mType (NULL)
{
mData.swap (data);
}
std::unique_ptr <STObject> oClone () const
{
return std::unique_ptr<STObject> (new STObject (*this));
}
static std::unique_ptr<STObject> parseJson (const Json::Value & value, SField::ref name = sfGeneric, int depth = 0);
virtual ~STObject ()
{
;
}
virtual ~STObject () { }
static std::unique_ptr<SerializedType> deserialize (SerializerIterator & sit, SField::ref name);
@@ -295,26 +295,11 @@ private:
}
*/
// VFALCO TODO these parameters should not be const references.
template <typename T, typename U>
static T range_check_cast (const U& value, const T& minimum, const T& maximum)
{
if ((value < minimum) || (value > maximum))
throw std::runtime_error ("Value out of range");
return static_cast<T> (value);
}
STObject* duplicate () const
{
return new STObject (*this);
}
STObject (SField::ref name, boost::ptr_vector<SerializedType>& data) : SerializedType (name), mType (NULL)
{
mData.swap (data);
}
private:
boost::ptr_vector<SerializedType> mData;
const SOTemplate* mType;
@@ -322,6 +307,16 @@ private:
//------------------------------------------------------------------------------
// VFALCO TODO these parameters should not be const references.
template <typename T, typename U>
static T range_check_cast (const U& value, const T& minimum, const T& maximum)
{
if ((value < minimum) || (value > maximum))
throw std::runtime_error ("Value out of range");
return static_cast<T> (value);
}
inline STObject::iterator range_begin (STObject& x)
{
return x.begin ();

View File

@@ -47,7 +47,7 @@ void SOTemplate::push_back (SOElement const& r)
// Append the new element.
//
mTypes.push_back (new SOElement (r));
mTypes.push_back (value_type (new SOElement (r)));
}
int SOTemplate::getIndex (SField::ref f) const

View File

@@ -53,18 +53,18 @@ public:
//------------------------------------------------------------------------------
/** Defines the fields and their attributes within a SerializedObject.
Each subclass of SerializedObject will provide its own template
describing the available fields and their metadata attributes.
*/
class SOTemplate
{
public:
/** Create an empty template.
typedef std::unique_ptr <SOElement const> value_type;
typedef std::vector <value_type> list_type;
/** Create an empty template.
After creating the template, call @ref push_back with the
desired fields.
@see push_back
*/
SOTemplate ();
@@ -72,21 +72,19 @@ public:
// VFALCO NOTE Why do we even bother with the 'private' keyword if
// this function is present?
//
std::vector <SOElement const*> const& peek () const
list_type const& peek () const
{
return mTypes;
}
/** Add an element to the template.
*/
/** Add an element to the template. */
void push_back (SOElement const& r);
/** Retrieve the position of a named field.
*/
/** Retrieve the position of a named field. */
int getIndex (SField::ref) const;
private:
std::vector <SOElement const*> mTypes;
list_type mTypes;
std::vector <int> mIndex; // field num -> index
};

View File

@@ -65,6 +65,20 @@ static inline const uint160& get_u160_one ()
#define ACCOUNT_XRP get_u160_zero()
#define ACCOUNT_ONE get_u160_one() // Used as a place holder.
//------------------------------------------------------------------------------
/** A type which can be exported to a well known binary format.
A SerializedType:
- Always a field
- Can always go inside an eligible enclosing SerializedType
(such as STArray)
- Has a field name
Like JSON, a SerializedObject is a basket which has rules
on what it can hold.
*/
// VFALCO TODO Document this as it looks like a central class.
// STObject is derived from it
//
@@ -88,6 +102,9 @@ public:
return std::unique_ptr<SerializedType> (new SerializedType (name));
}
/** A SerializeType is a field.
This sets the name.
*/
void setFName (SField::ref n)
{
fName = &n;
@@ -160,19 +177,27 @@ private:
}
};
//------------------------------------------------------------------------------
inline SerializedType* new_clone (const SerializedType& s)
{
return s.clone ().release ();
SerializedType* const copy (s.clone ().release ());
assert (typeid (*copy) == typeid (s));
return copy;
}
inline void delete_clone (const SerializedType* s)
{
boost::checked_delete (s);
}
inline std::ostream& operator<< (std::ostream& out, const SerializedType& t)
{
return out << t.getFullText ();
}
//------------------------------------------------------------------------------
class STUInt8 : public SerializedType
{
public:
@@ -230,6 +255,8 @@ private:
static STUInt8* construct (SerializerIterator&, SField::ref f);
};
//------------------------------------------------------------------------------
class STUInt16 : public SerializedType
{
public:
@@ -287,6 +314,8 @@ private:
static STUInt16* construct (SerializerIterator&, SField::ref name);
};
//------------------------------------------------------------------------------
class STUInt32 : public SerializedType
{
public:
@@ -344,10 +373,11 @@ private:
static STUInt32* construct (SerializerIterator&, SField::ref name);
};
//------------------------------------------------------------------------------
class STUInt64 : public SerializedType
{
public:
STUInt64 (uint64 v = 0) : value (v)
{
;
@@ -401,6 +431,8 @@ private:
static STUInt64* construct (SerializerIterator&, SField::ref name);
};
//------------------------------------------------------------------------------
// Internal form:
// 1: If amount is zero, then value is zero and offset is -100
// 2: Otherwise:
@@ -805,6 +837,8 @@ private:
extern const STAmount saZero;
extern const STAmount saOne;
//------------------------------------------------------------------------------
class STHash128 : public SerializedType
{
public:
@@ -876,6 +910,8 @@ private:
static STHash128* construct (SerializerIterator&, SField::ref name);
};
//------------------------------------------------------------------------------
class STHash160 : public SerializedType
{
public:
@@ -947,6 +983,8 @@ private:
static STHash160* construct (SerializerIterator&, SField::ref name);
};
//------------------------------------------------------------------------------
class STHash256 : public SerializedType
{
public:
@@ -1018,6 +1056,8 @@ private:
static STHash256* construct (SerializerIterator&, SField::ref);
};
//------------------------------------------------------------------------------
// variable length byte string
class STVariableLength : public SerializedType
{
@@ -1091,6 +1131,8 @@ private:
static STVariableLength* construct (SerializerIterator&, SField::ref);
};
//------------------------------------------------------------------------------
class STAccount : public STVariableLength
{
public:
@@ -1137,6 +1179,8 @@ private:
static STAccount* construct (SerializerIterator&, SField::ref);
};
//------------------------------------------------------------------------------
class STPathElement
{
private:
@@ -1223,6 +1267,8 @@ private:
uint160 mIssuerID;
};
//------------------------------------------------------------------------------
class STPath
{
public:
@@ -1322,6 +1368,8 @@ inline std::vector<STPathElement>::const_iterator range_end (const STPath& x)
return x.end ();
}
//------------------------------------------------------------------------------
// A set of zero or more payment paths
class STPathSet : public SerializedType
{
@@ -1482,6 +1530,8 @@ inline std::vector<STPath>::const_iterator range_end (const STPathSet& x)
return x.end ();
}
//------------------------------------------------------------------------------
class STVector256 : public SerializedType
{
public:
@@ -1582,4 +1632,3 @@ private:
};
#endif
// vim:ts=4

View File

@@ -44,6 +44,7 @@
#include <openssl/err.h>
#include "../ripple/sslutil/ripple_sslutil.h"
#include "../ripple/rpc/api/ErrorCodes.h"
// VFALCO TODO fix these warnings!
#if BEAST_MSVC
@@ -55,6 +56,8 @@
#undef min
#endif
#include "protocol/STParsedJSON.cpp"
namespace ripple
{

View File

@@ -51,6 +51,8 @@ namespace ripple {
}
#include "protocol/STParsedJSON.h"
//------------------------------------------------------------------------------
namespace boost

View File

@@ -42,6 +42,12 @@
#include "../ripple_websocket/ripple_websocket.h" // for HTTPClient, RPCDoor
// VFALCO NOTE This is the "new new new" where individual headers are included
// directly (instead of th emodule header). The corresponding .cpp
// still uses the unity style inclusion.
//
#include "../ripple/rpc/api/ErrorCodes.h"
namespace ripple
{

View File

@@ -78,7 +78,8 @@ private:
}
else
{
return rpcError (rpcINVALID_PARAMS);
return RPC::make_param_error (std::string ("Invalid currency/issuer '") +
strCurrencyIssuer + "'");
}
}

View File

@@ -17,104 +17,32 @@
*/
//==============================================================================
struct RPCErr; // for Log
struct RPCErr;
SETUP_LOG (RPCErr)
// VFALCO NOTE Deprecated function
Json::Value rpcError (int iError, Json::Value jvResult)
{
static struct
{
int iError;
const char* pToken;
const char* pMessage;
} errorInfoA[] =
{
{ rpcACT_BITCOIN, "actBitcoin", "Account is bitcoin address." },
{ rpcACT_EXISTS, "actExists", "Account already exists." },
{ rpcACT_MALFORMED, "actMalformed", "Account malformed." },
{ rpcACT_NOT_FOUND, "actNotFound", "Account not found." },
{ rpcBAD_BLOB, "badBlob", "Blob must be a non-empty hex string." },
{ 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." },
{ rpcCOMMAND_MISSING, "commandMissing", "Missing command entry." },
{ rpcDST_ACT_MALFORMED, "dstActMalformed", "Destination account is malformed." },
{ rpcDST_ACT_MISSING, "dstActMissing", "Destination account does not exist." },
{ rpcDST_AMT_MALFORMED, "dstAmtMalformed", "Destination amount/currency/issuer is malformed." },
{ rpcDST_ISR_MALFORMED, "dstIsrMalformed", "Destination issuer is malformed." },
{ rpcFORBIDDEN, "forbidden", "Bad credentials." },
{ rpcFAIL_GEN_DECRPYT, "failGenDecrypt", "Failed to decrypt generator." },
{ rpcGETS_ACT_MALFORMED, "getsActMalformed", "Gets account malformed." },
{ rpcGETS_AMT_MALFORMED, "getsAmtMalformed", "Gets amount malformed." },
{ rpcHOST_IP_MALFORMED, "hostIpMalformed", "Host IP is malformed." },
{ rpcINSUF_FUNDS, "insufFunds", "Insufficient funds." },
{ 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." },
{ rpcNICKNAME_MALFORMED, "nicknameMalformed", "Nickname is malformed." },
{ rpcNICKNAME_MISSING, "nicknameMissing", "Nickname does not exist." },
{ rpcNICKNAME_PERM, "nicknamePerm", "Account does not control nickname." },
{ rpcNOT_IMPL, "notImpl", "Not implemented." },
{ rpcNO_ACCOUNT, "noAccount", "No such account." },
{ rpcNO_CLOSED, "noClosed", "Closed ledger is unavailable." },
{ rpcNO_CURRENT, "noCurrent", "Current ledger is unavailable." },
{ rpcNO_EVENTS, "noEvents", "Current transport does not support events." },
{ rpcNO_GEN_DECRPYT, "noGenDectypt", "Password failed to decrypt master public generator." },
{ rpcNO_NETWORK, "noNetwork", "Network not available." },
{ rpcNO_PATH, "noPath", "Unable to find a ripple path." },
{ rpcNO_PERMISSION, "noPermission", "You don't have permission for this command." },
{ rpcNO_PF_REQUEST, "noPathRequest", "No pathfinding request in progress." },
{ rpcNOT_STANDALONE, "notStandAlone", "Operation valid in debug mode only." },
{ rpcNOT_SUPPORTED, "notSupported", "Operation not supported." },
{ rpcPASSWD_CHANGED, "passwdChanged", "Wrong key, password changed." },
{ rpcPAYS_ACT_MALFORMED, "paysActMalformed", "Pays account malformed." },
{ rpcPAYS_AMT_MALFORMED, "paysAmtMalformed", "Pays amount malformed." },
{ rpcPORT_MALFORMED, "portMalformed", "Port is malformed." },
{ rpcPUBLIC_MALFORMED, "publicMalformed", "Public key is malformed." },
{ rpcQUALITY_MALFORMED, "qualityMalformed", "Quality malformed." },
{ 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_AMT_MALFORMED, "srcAmtMalformed", "Source amount/currency/issuer is malformed." },
{ rpcSRC_CUR_MALFORMED, "srcCurMalformed", "Source currency is malformed." },
{ rpcSRC_ISR_MALFORMED, "srcIsrMalformed", "Source issuer is malformed." },
{ rpcSRC_UNCLAIMED, "srcUnclaimed", "Source account is not claimed." },
{ rpcTXN_NOT_FOUND, "txnNotFound", "Transaction not found." },
{ rpcUNKNOWN_COMMAND, "unknownCmd", "Unknown method." },
{ rpcWRONG_SEED, "wrongSeed", "The regular key does not point as the master key." },
{ rpcTOO_BUSY, "tooBusy", "The server is too busy to help you now." },
{ rpcSLOW_DOWN, "slowDown", "You are placing too much load on the server." },
{ rpcATX_DEPRECATED, "deprecated", "Use the new API or specify a ledger range." },
};
int i;
for (i = NUMBER (errorInfoA); i-- && errorInfoA[i].iError != iError;)
;
jvResult["error"] = i >= 0 ? errorInfoA[i].pToken : lexicalCast <std::string> (iError);
jvResult["error_message"] = i >= 0 ? errorInfoA[i].pMessage : lexicalCast <std::string> (iError);
jvResult["error_code"] = iError;
if (i >= 0)
{
WriteLog (lsDEBUG, RPCErr) << "rpcError: "
<< errorInfoA[i].pToken << ": " << errorInfoA[i].pMessage << std::endl;
}
RPC::inject_error (iError, jvResult);
return jvResult;
}
// VFALCO NOTE Deprecated function
bool isRpcError (Json::Value jvResult)
{
return jvResult.isObject () && jvResult.isMember ("error");
}
// vim:ts=4
Json::Value const& logRPCError (Json::Value const& json)
{
if (RPC::contains_error (json))
{
WriteLog (lsDEBUG, RPCErr) <<
"rpcError: " << json ["error"] <<
": " << json ["error_message"];
}
return json;
}

View File

@@ -20,92 +20,11 @@
#ifndef RIPPLE_NET_RPC_RPCERR_H_INCLUDED
#define RIPPLE_NET_RPC_RPCERR_H_INCLUDED
enum
{
rpcSUCCESS = 0,
rpcBAD_SYNTAX, // Must be 1 to print usage to command line.
rpcJSON_RPC,
rpcFORBIDDEN,
// Error numbers beyond this line are not stable between versions.
// Programs should use error tokens.
// Misc failure
rpcLOAD_FAILED,
rpcNO_PERMISSION,
rpcNO_EVENTS,
rpcNOT_STANDALONE,
rpcTOO_BUSY,
rpcSLOW_DOWN,
// Networking
rpcNO_CLOSED,
rpcNO_CURRENT,
rpcNO_NETWORK,
// Ledger state
rpcACT_EXISTS,
rpcACT_NOT_FOUND,
rpcINSUF_FUNDS,
rpcLGR_NOT_FOUND,
rpcNICKNAME_MISSING,
rpcNO_ACCOUNT,
rpcNO_PATH,
rpcPASSWD_CHANGED,
rpcSRC_MISSING,
rpcSRC_UNCLAIMED,
rpcTXN_NOT_FOUND,
rpcWRONG_SEED,
// Malformed command
rpcINVALID_PARAMS,
rpcUNKNOWN_COMMAND,
rpcNO_PF_REQUEST,
// Bad parameter
rpcACT_BITCOIN,
rpcACT_MALFORMED,
rpcQUALITY_MALFORMED,
rpcBAD_BLOB,
rpcBAD_FEATURE,
rpcBAD_ISSUER,
rpcBAD_MARKET,
rpcBAD_SECRET,
rpcBAD_SEED,
rpcCOMMAND_MISSING,
rpcDST_ACT_MALFORMED,
rpcDST_ACT_MISSING,
rpcDST_AMT_MALFORMED,
rpcDST_ISR_MALFORMED,
rpcGETS_ACT_MALFORMED,
rpcGETS_AMT_MALFORMED,
rpcHOST_IP_MALFORMED,
rpcLGR_IDXS_INVALID,
rpcLGR_IDX_MALFORMED,
rpcNICKNAME_MALFORMED,
rpcNICKNAME_PERM,
rpcPAYS_ACT_MALFORMED,
rpcPAYS_AMT_MALFORMED,
rpcPORT_MALFORMED,
rpcPUBLIC_MALFORMED,
rpcSRC_ACT_MALFORMED,
rpcSRC_ACT_MISSING,
rpcSRC_ACT_NOT_FOUND,
rpcSRC_AMT_MALFORMED,
rpcSRC_CUR_MALFORMED,
rpcSRC_ISR_MALFORMED,
rpcATX_DEPRECATED,
// Internal error (should never happen)
rpcINTERNAL, // Generic internal error.
rpcFAIL_GEN_DECRPYT,
rpcNOT_IMPL,
rpcNOT_SUPPORTED,
rpcNO_GEN_DECRPYT,
};
Json::Value const& logRPCError (Json::Value const& json);
// VFALCO NOTE these are deprecated
bool isRpcError (Json::Value jvResult);
Json::Value rpcError (int iError, Json::Value jvResult = Json::Value (Json::objectValue));
Json::Value rpcError (int iError,
Json::Value jvResult = Json::Value (Json::objectValue));
#endif
// vim:ts=4