diff --git a/Builds/VisualStudio2012/RippleD.vcxproj b/Builds/VisualStudio2012/RippleD.vcxproj
index 8982619cab..1c8697aae5 100644
--- a/Builds/VisualStudio2012/RippleD.vcxproj
+++ b/Builds/VisualStudio2012/RippleD.vcxproj
@@ -604,6 +604,12 @@
true
true
+
+ true
+ true
+ true
+ true
+
true
true
@@ -1732,6 +1738,12 @@
true
true
+
+ true
+ true
+ true
+ true
+
true
true
@@ -2326,6 +2338,7 @@
+
@@ -2561,6 +2574,7 @@
+
diff --git a/Builds/VisualStudio2012/RippleD.vcxproj.filters b/Builds/VisualStudio2012/RippleD.vcxproj.filters
index d4143c7127..83a6202470 100644
--- a/Builds/VisualStudio2012/RippleD.vcxproj.filters
+++ b/Builds/VisualStudio2012/RippleD.vcxproj.filters
@@ -1437,6 +1437,12 @@
[2] Old Ripple\ripple_app\peers
+
+ [2] Old Ripple\ripple_data\protocol
+
+
+ [1] Ripple\rpc\impl
+
@@ -2916,6 +2922,12 @@
[2] Old Ripple\ripple_app\peers
+
+ [2] Old Ripple\ripple_data\protocol
+
+
+ [1] Ripple\rpc\api
+
diff --git a/src/ripple/rpc/api/ErrorCodes.h b/src/ripple/rpc/api/ErrorCodes.h
new file mode 100644
index 0000000000..ddca504d1a
--- /dev/null
+++ b/src/ripple/rpc/api/ErrorCodes.h
@@ -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
diff --git a/src/ripple/rpc/impl/ErrorCodes.cpp b/src/ripple/rpc/impl/ErrorCodes.cpp
new file mode 100644
index 0000000000..05ecf03ee7
--- /dev/null
+++ b/src/ripple/rpc/impl/ErrorCodes.cpp
@@ -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 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 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;
+}
+
+}
+}
diff --git a/src/ripple/rpc/ripple_rpc.cpp b/src/ripple/rpc/ripple_rpc.cpp
index 291ac130e2..f6195d4a87 100644
--- a/src/ripple/rpc/ripple_rpc.cpp
+++ b/src/ripple/rpc/ripple_rpc.cpp
@@ -24,6 +24,7 @@
#include "beast/modules/beast_core/system/BeforeBoost.h"
#include
+#include "impl/ErrorCodes.cpp"
# include "impl/ManagerImpl.h"
#include "impl/Manager.cpp"
#include "impl/Handler.cpp"
diff --git a/src/ripple/rpc/ripple_rpc.h b/src/ripple/rpc/ripple_rpc.h
index 7b58e3f704..12aeabc69d 100644
--- a/src/ripple/rpc/ripple_rpc.h
+++ b/src/ripple/rpc/ripple_rpc.h
@@ -27,5 +27,6 @@
# include "api/Handler.h"
# include "api/Service.h"
#include "api/Manager.h"
+#include "api/ErrorCodes.h"
#endif
diff --git a/src/ripple_app/misc/SerializedTransaction.cpp b/src/ripple_app/misc/SerializedTransaction.cpp
index 3f4dfc95d4..de056793e9 100644
--- a/src/ripple_app/misc/SerializedTransaction.cpp
+++ b/src/ripple_app/misc/SerializedTransaction.cpp
@@ -344,9 +344,11 @@ public:
pass ();
}
- std::unique_ptr new_obj = STObject::parseJson (j.getJson (0), sfGeneric);
+ STParsedJSON parsed ("test", j.getJson (0));
+ std::unique_ptr 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)
{
@@ -361,4 +363,4 @@ public:
}
};
-static SerializedTransactionTests serializedTransactionTests;
\ No newline at end of file
+static SerializedTransactionTests serializedTransactionTests;
diff --git a/src/ripple_app/ripple_app_pt8.cpp b/src/ripple_app/ripple_app_pt8.cpp
index 01b20f958e..42814d546a 100644
--- a/src/ripple_app/ripple_app_pt8.cpp
+++ b/src/ripple_app/ripple_app_pt8.cpp
@@ -26,6 +26,8 @@
#pragma warning (disable: 4309) // truncation of constant value
#endif
+#include "../ripple/rpc/api/ErrorCodes.h"
+
namespace ripple
{
diff --git a/src/ripple_app/rpc/RPCHandler.cpp b/src/ripple_app/rpc/RPCHandler.cpp
index 40c8446ff3..c1c84b1c8f 100644
--- a/src/ripple_app/rpc/RPCHandler.cpp
+++ b/src/ripple_app/rpc/RPCHandler.cpp
@@ -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 ();
+ Json::Value jvResult;
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")
- && (
- "AccountSet" == sType
- || "Payment" == sType
- || "OfferCreate" == sType
- || "OfferCancel" == sType
- || "TrustSet" == sType))
+ 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 (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,16 +331,21 @@ Json::Value RPCHandler::transactionSign (Json::Value params, bool bSubmit, bool
std::unique_ptr sopTrans;
- try
{
- sopTrans = STObject::parseJson (txJSON);
- }
- catch (std::exception& e)
- {
- jvResult["error"] = "malformedTransaction";
- jvResult["error_exception"] = e.what ();
-
- return jvResult;
+ 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());
+ }
+ else
+ {
+ 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 ();
- Ledger::pointer lpLedger;
- Json::Value jvResult = lookupLedger (params, lpLedger);
-
+ // Get the current ledger
+ Ledger::pointer 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
+inline bool is_xrp (UnsignedInteger const& value)
+{
+ return value.isZero();
+}
+
+template
+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);
+ Ledger::pointer 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 ();
}
- RippleAddress raTakerID;
- if (!params.isMember ("taker"))
+ 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 ["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)
diff --git a/src/ripple_data/protocol/STParsedJSON.cpp b/src/ripple_data/protocol/STParsedJSON.cpp
new file mode 100644
index 0000000000..dab344f462
--- /dev/null
+++ b/src/ripple_data/protocol/STParsedJSON.cpp
@@ -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 & sub_object)
+{
+ if (! json.isObject ())
+ {
+ error = not_an_object (json_name);
+ return false;
+ }
+
+ SField::ptr name (&inName);
+
+ boost::ptr_vector 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 (
+ 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 (
+ 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 (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 (type)));
+
+ if (*name == sfGeneric)
+ name = &sfLedgerEntry;
+ }
+ else
+ {
+ error = invalid_data (json_name, fieldName);
+ return false;
+ }
+ }
+ else
+ {
+ data.push_back (new STUInt16 (field,
+ lexicalCastThrow (strValue)));
+ }
+ }
+ else if (value.isInt ())
+ {
+ data.push_back (new STUInt16 (field,
+ range_check_cast (
+ value.asInt (), 0, 65535)));
+ }
+ else if (value.isUInt ())
+ {
+ data.push_back (new STUInt16 (field,
+ range_check_cast (
+ 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 (value.asString ())));
+ }
+ else if (value.isInt ())
+ {
+ data.push_back (new STUInt32 (field,
+ range_check_cast (value.asInt (), 0u, 4294967295u)));
+ }
+ else if (value.isUInt ())
+ {
+ data.push_back (new STUInt32 (field,
+ static_cast (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 (
+ value.asInt (), 0, 18446744073709551615ull)));
+ }
+ else if (value.isUInt ())
+ {
+ data.push_back (new STUInt64 (field,
+ static_cast (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 (&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 (&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 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 (&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 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;
+}
+
+}
diff --git a/src/ripple_data/protocol/STParsedJSON.h b/src/ripple_data/protocol/STParsedJSON.h
new file mode 100644
index 0000000000..218cede18a
--- /dev/null
+++ b/src/ripple_data/protocol/STParsedJSON.h
@@ -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 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 & sub_object);
+};
+
+}
+
+#endif
diff --git a/src/ripple_data/protocol/SerializedObject.cpp b/src/ripple_data/protocol/SerializedObject.cpp
index cc3504b6fe..cfe64dc062 100644
--- a/src/ripple_data/protocol/SerializedObject.cpp
+++ b/src/ripple_data/protocol/SerializedObject.cpp
@@ -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::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::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 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 (terCode);
- else
- data.push_back (new STUInt8 (field, lexicalCastThrow (value.asString ())));
- }
-
- data.push_back (new STUInt8 (field, lexicalCastThrow (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 (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 (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 (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 (type)));
-
- if (*name == sfGeneric)
- name = &sfLedgerEntry;
- }
- else
- throw std::runtime_error ("Invalid field data");
- }
- else
- data.push_back (new STUInt16 (field, lexicalCastThrow (strValue)));
- }
- else if (value.isInt ())
- data.push_back (new STUInt16 (field, range_check_cast (value.asInt (), 0, 65535)));
- else if (value.isUInt ())
- data.push_back (new STUInt16 (field, range_check_cast (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 (value.asString ())));
- }
- else if (value.isInt ())
- {
- data.push_back (new STUInt32 (field, range_check_cast (value.asInt (), 0u, 4294967295u)));
- }
- else if (value.isUInt ())
- {
- data.push_back (new STUInt32 (field, static_cast (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 (value.asInt (), 0, 18446744073709551615ull)));
- else if (value.isUInt ())
- data.push_back (new STUInt64 (field, static_cast (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 (&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 (&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 (&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 (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,16 +1303,15 @@ public:
bool parsedOK (parseJSONString(json, jsonObject));
if (parsedOK)
{
- std::unique_ptr so;
- so = STObject::parseJson (jsonObject);
- Json::FastWriter writer;
- std::string const& serialized (writer.write(so->getJson(0)));
- bool serializedOK = serialized == json;
- unexpected (!serializedOK, serialized + " should equal: " + json);
+ STParsedJSON parsed ("test", jsonObject);
+ Json::FastWriter writer;
+ std::string const& serialized (
+ writer.write (parsed.object->getJson(0)));
+ expect (serialized == json, serialized + " should equal: " + json);
}
else
{
- fail ("Couldn't parse json: " + json);
+ fail ("Couldn't parse json: " + json);
}
}
diff --git a/src/ripple_data/protocol/SerializedObject.h b/src/ripple_data/protocol/SerializedObject.h
index 1548c78467..66f4c5f639 100644
--- a/src/ripple_data/protocol/SerializedObject.h
+++ b/src/ripple_data/protocol/SerializedObject.h
@@ -48,17 +48,17 @@ public:
setType (type);
}
- std::unique_ptr oClone () const
+ STObject (SField::ref name, boost::ptr_vector& data) : SerializedType (name), mType (NULL)
+ {
+ mData.swap (data);
+ }
+
+ std::unique_ptr oClone () const
{
return std::unique_ptr (new STObject (*this));
}
- static std::unique_ptr parseJson (const Json::Value & value, SField::ref name = sfGeneric, int depth = 0);
-
- virtual ~STObject ()
- {
- ;
- }
+ virtual ~STObject () { }
static std::unique_ptr deserialize (SerializerIterator & sit, SField::ref name);
@@ -295,26 +295,11 @@ private:
}
*/
- // VFALCO TODO these parameters should not be const references.
- template
- 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 (value);
- }
-
STObject* duplicate () const
{
return new STObject (*this);
}
- STObject (SField::ref name, boost::ptr_vector& data) : SerializedType (name), mType (NULL)
- {
- mData.swap (data);
- }
-
private:
boost::ptr_vector mData;
const SOTemplate* mType;
@@ -322,6 +307,16 @@ private:
//------------------------------------------------------------------------------
+// VFALCO TODO these parameters should not be const references.
+template
+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 (value);
+}
+
inline STObject::iterator range_begin (STObject& x)
{
return x.begin ();
diff --git a/src/ripple_data/protocol/SerializedObjectTemplate.cpp b/src/ripple_data/protocol/SerializedObjectTemplate.cpp
index affa61398b..9ee719763d 100644
--- a/src/ripple_data/protocol/SerializedObjectTemplate.cpp
+++ b/src/ripple_data/protocol/SerializedObjectTemplate.cpp
@@ -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
diff --git a/src/ripple_data/protocol/SerializedObjectTemplate.h b/src/ripple_data/protocol/SerializedObjectTemplate.h
index 6c4e541b11..b1aff7833c 100644
--- a/src/ripple_data/protocol/SerializedObjectTemplate.h
+++ b/src/ripple_data/protocol/SerializedObjectTemplate.h
@@ -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 value_type;
+ typedef std::vector 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 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 mTypes;
+ list_type mTypes;
std::vector mIndex; // field num -> index
};
diff --git a/src/ripple_data/protocol/SerializedTypes.h b/src/ripple_data/protocol/SerializedTypes.h
index bee83323f6..41a8f16b99 100644
--- a/src/ripple_data/protocol/SerializedTypes.h
+++ b/src/ripple_data/protocol/SerializedTypes.h
@@ -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 (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::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::const_iterator range_end (const STPathSet& x)
return x.end ();
}
+//------------------------------------------------------------------------------
+
class STVector256 : public SerializedType
{
public:
@@ -1582,4 +1632,3 @@ private:
};
#endif
-// vim:ts=4
diff --git a/src/ripple_data/ripple_data.cpp b/src/ripple_data/ripple_data.cpp
index 6692adba8b..2c4ca57b7c 100644
--- a/src/ripple_data/ripple_data.cpp
+++ b/src/ripple_data/ripple_data.cpp
@@ -44,6 +44,7 @@
#include
#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
{
diff --git a/src/ripple_data/ripple_data.h b/src/ripple_data/ripple_data.h
index 9c4b1536d8..080334c19d 100644
--- a/src/ripple_data/ripple_data.h
+++ b/src/ripple_data/ripple_data.h
@@ -51,6 +51,8 @@ namespace ripple {
}
+#include "protocol/STParsedJSON.h"
+
//------------------------------------------------------------------------------
namespace boost
diff --git a/src/ripple_net/ripple_net.cpp b/src/ripple_net/ripple_net.cpp
index d2addab349..82114af828 100644
--- a/src/ripple_net/ripple_net.cpp
+++ b/src/ripple_net/ripple_net.cpp
@@ -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
{
diff --git a/src/ripple_net/rpc/RPCCall.cpp b/src/ripple_net/rpc/RPCCall.cpp
index 853af3a273..f8950df344 100644
--- a/src/ripple_net/rpc/RPCCall.cpp
+++ b/src/ripple_net/rpc/RPCCall.cpp
@@ -78,7 +78,8 @@ private:
}
else
{
- return rpcError (rpcINVALID_PARAMS);
+ return RPC::make_param_error (std::string ("Invalid currency/issuer '") +
+ strCurrencyIssuer + "'");
}
}
diff --git a/src/ripple_net/rpc/RPCErr.cpp b/src/ripple_net/rpc/RPCErr.cpp
index 3d4e91f538..fa01f544df 100644
--- a/src/ripple_net/rpc/RPCErr.cpp
+++ b/src/ripple_net/rpc/RPCErr.cpp
@@ -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 (iError);
- jvResult["error_message"] = i >= 0 ? errorInfoA[i].pMessage : lexicalCast (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;
+}
+
diff --git a/src/ripple_net/rpc/RPCErr.h b/src/ripple_net/rpc/RPCErr.h
index c08ea36323..59d6c4a87d 100644
--- a/src/ripple_net/rpc/RPCErr.h
+++ b/src/ripple_net/rpc/RPCErr.h
@@ -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