From 761902864a36f580c47ab9ee2d85be296a4a5fec Mon Sep 17 00:00:00 2001 From: Scott Schurr Date: Thu, 9 Oct 2014 15:12:02 -0700 Subject: [PATCH] Refactor STParsedJSON to parse an object or array [RIPD-480] The implementation of multi-sign has a SigningAccounts array as a member of the outermost object. This array could not be parsed by the previous implementation of STParsedJSON, which only knew how to parse objects. This refactor supports the required parsing. The refactor divides the parsing into three separate functions: o parseNoRecurse() which parses most rippled data types. o parseObject() which parses object types that may contain arbitrary other types. o parseArray() which parses object types that may contain arbitrary other types. The change is required by the multi-sign implementation, but is independent. So the parsing change is going in as a separate commit. The parsing is still far from perfect. But this was as much as needs doing to accomplish the ends and mitigate risk of breaking the parser. --- src/ripple/app/main/Application.cpp | 2 +- src/ripple/app/misc/SerializedTransaction.cpp | 2 +- src/ripple/data/protocol/STObject.cpp | 4 +- src/ripple/data/protocol/STParsedJSON.cpp | 1278 +++++++++-------- src/ripple/data/protocol/STParsedJSON.h | 75 +- src/ripple/rpc/handlers/RipplePathFind.cpp | 2 +- src/ripple/rpc/impl/TransactionSign.cpp | 2 +- 7 files changed, 745 insertions(+), 620 deletions(-) diff --git a/src/ripple/app/main/Application.cpp b/src/ripple/app/main/Application.cpp index cb6ccde199..b46f4ac375 100644 --- a/src/ripple/app/main/Application.cpp +++ b/src/ripple/app/main/Application.cpp @@ -1214,7 +1214,7 @@ bool ApplicationImp::loadOldLedger ( uIndex.SetHex (entry["index"].asString()); entry.removeMember ("index"); - STParsedJSON stp ("sle", ledger.get()[index]); + STParsedJSONObject stp ("sle", ledger.get()[index]); // m_journal.info << "json: " << stp.object->getJson(0); if (stp.object && (uIndex.isNonZero())) diff --git a/src/ripple/app/misc/SerializedTransaction.cpp b/src/ripple/app/misc/SerializedTransaction.cpp index 0c615e7ac3..daf51b4c7d 100644 --- a/src/ripple/app/misc/SerializedTransaction.cpp +++ b/src/ripple/app/misc/SerializedTransaction.cpp @@ -395,7 +395,7 @@ public: pass (); } - STParsedJSON parsed ("test", j.getJson (0)); + STParsedJSONObject parsed ("test", j.getJson (0)); std::unique_ptr new_obj (std::move (parsed.object)); if (new_obj.get () == nullptr) diff --git a/src/ripple/data/protocol/STObject.cpp b/src/ripple/data/protocol/STObject.cpp index 757491167f..dcfc245aed 100644 --- a/src/ripple/data/protocol/STObject.cpp +++ b/src/ripple/data/protocol/STObject.cpp @@ -942,7 +942,7 @@ public: Json::Value faultyJson; bool parsedOK (parseJSONString(faulty, faultyJson)); unexpected(!parsedOK, "failed to parse"); - STParsedJSON parsed ("test", faultyJson); + STParsedJSONObject parsed ("test", faultyJson); expect (parsed.object.get() == nullptr, "It should have thrown. " "Immediate children of STArray encoded as json must " @@ -965,7 +965,7 @@ public: bool parsedOK (parseJSONString(json, jsonObject)); if (parsedOK) { - STParsedJSON parsed ("test", jsonObject); + STParsedJSONObject parsed ("test", jsonObject); Json::FastWriter writer; std::string const& serialized ( writer.write (parsed.object->getJson(0))); diff --git a/src/ripple/data/protocol/STParsedJSON.cpp b/src/ripple/data/protocol/STParsedJSON.cpp index 9de6835d9a..5c42002f25 100644 --- a/src/ripple/data/protocol/STParsedJSON.cpp +++ b/src/ripple/data/protocol/STParsedJSON.cpp @@ -24,6 +24,9 @@ namespace ripple { +namespace STParsedJSONDetail +{ + template static T range_check_cast (U value, T minimum, T maximum) { @@ -33,16 +36,7 @@ static T range_check_cast (U value, T minimum, T maximum) return static_cast (value); } -//------------------------------------------------------------------------------ - -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, +static std::string make_name (std::string const& object, std::string const& field) { if (field.empty ()) @@ -51,74 +45,625 @@ std::string STParsedJSON::make_name (std::string const& object, return object + "." + field; } -Json::Value STParsedJSON::not_an_object (std::string const& object, +static Json::Value 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, +static Json::Value not_an_object (std::string const& object) +{ + return not_an_object (object, ""); +} + +static Json::Value not_an_array (std::string const& object) +{ + return RPC::make_error (rpcINVALID_PARAMS, + "Field '" + object + "' is not a JSON array."); +} + +static Json::Value 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, +static Json::Value 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, +static Json::Value 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, +static Json::Value 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, +static Json::Value invalid_data (std::string const& object) +{ + return invalid_data (object, ""); +} + +static Json::Value 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, +static Json::Value 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) +static Json::Value too_deep (std::string const& object) { return RPC::make_error (rpcINVALID_PARAMS, - "Field '" + make_name (object, field) + "' exceeds nesting depth limit."); + "Field '" + object + "' exceeds nesting depth limit."); } -Json::Value STParsedJSON::singleton_expected (std::string const& object) +static Json::Value singleton_expected (std::string const& object, + unsigned int index) { return RPC::make_error (rpcINVALID_PARAMS, - "Field '" + object + - "' must be an object with a single key/object value."); + "Field '" + object + "[" + std::to_string (index) + + "]' 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) +// This function is used by parseObject to parse any JSON type that doesn't +// recurse. Everything represented here is a leaf-type. +static std::unique_ptr parseLeaf ( + std::string const& json_name, + std::string const& fieldName, + SField::ptr name, + Json::Value const& value, + Json::Value& error) +{ + std::unique_ptr ret; + + SField::ref field = SField::getField (fieldName); + + if (field == sfInvalid) + { + error = unknown_field (json_name, fieldName); + return ret; + } + + 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 ret; + } + + ret = std::make_unique (field, + range_check_cast ( + value.asInt (), 0, 255)); + } + else if (value.isUInt ()) + { + if (value.asUInt () > 255) + { + error = out_of_range (json_name, fieldName); + return ret; + } + + ret = std::make_unique (field, + range_check_cast ( + value.asUInt (), 0, 255)); + } + else + { + error = bad_type (json_name, fieldName); + return ret; + } + } + catch (...) + { + error = invalid_data (json_name, fieldName); + return ret; + } + break; + + case STI_UINT16: + try + { + if (value.isString ()) + { + std::string const strValue = value.asString (); + + if (! strValue.empty () && + ((strValue[0] < '0') || (strValue[0] > '9'))) + { + if (field == sfTransactionType) + { + TxType const txType (TxFormats::getInstance(). + findTypeByName (strValue)); + + ret = std::make_unique (field, + static_cast (txType)); + + if (*name == sfGeneric) + name = &sfTransaction; + } + else if (field == sfLedgerEntryType) + { + LedgerEntryType const type ( + LedgerFormats::getInstance(). + findTypeByName (strValue)); + + ret = std::make_unique (field, + static_cast (type)); + + if (*name == sfGeneric) + name = &sfLedgerEntry; + } + else + { + error = invalid_data (json_name, fieldName); + return ret; + } + } + else + { + ret = std::make_unique (field, + beast::lexicalCastThrow (strValue)); + } + } + else if (value.isInt ()) + { + ret = std::make_unique (field, + range_check_cast ( + value.asInt (), 0, 65535)); + } + else if (value.isUInt ()) + { + ret = std::make_unique (field, + range_check_cast ( + value.asUInt (), 0, 65535)); + } + else + { + error = bad_type (json_name, fieldName); + return ret; + } + } + catch (...) + { + error = invalid_data (json_name, fieldName); + return ret; + } + + break; + + case STI_UINT32: + try + { + if (value.isString ()) + { + ret = std::make_unique (field, + beast::lexicalCastThrow ( + value.asString ())); + } + else if (value.isInt ()) + { + ret = std::make_unique (field, + range_check_cast ( + value.asInt (), 0u, 4294967295u)); + } + else if (value.isUInt ()) + { + ret = std::make_unique (field, + static_cast (value.asUInt ())); + } + else + { + error = bad_type (json_name, fieldName); + return ret; + } + } + catch (...) + { + error = invalid_data (json_name, fieldName); + return ret; + } + + break; + + case STI_UINT64: + try + { + if (value.isString ()) + { + ret = std::make_unique (field, + uintFromHex (value.asString ())); + } + else if (value.isInt ()) + { + ret = std::make_unique (field, + range_check_cast ( + value.asInt (), 0, 18446744073709551615ull)); + } + else if (value.isUInt ()) + { + ret = std::make_unique (field, + static_cast (value.asUInt ())); + } + else + { + error = bad_type (json_name, fieldName); + return ret; + } + } + catch (...) + { + error = invalid_data (json_name, fieldName); + return ret; + } + + break; + + case STI_HASH128: + try + { + if (value.isString ()) + { + ret = std::make_unique (field, value.asString ()); + } + else + { + error = bad_type (json_name, fieldName); + return ret; + } + } + catch (...) + { + error = invalid_data (json_name, fieldName); + return ret; + } + + break; + + case STI_HASH160: + try + { + if (value.isString ()) + { + ret = std::make_unique (field, value.asString ()); + } + else + { + error = bad_type (json_name, fieldName); + return ret; + } + } + catch (...) + { + error = invalid_data (json_name, fieldName); + return ret; + } + + break; + + case STI_HASH256: + try + { + if (value.isString ()) + { + ret = std::make_unique (field, value.asString ()); + } + else + { + error = bad_type (json_name, fieldName); + return ret; + } + } + catch (...) + { + error = invalid_data (json_name, fieldName); + return ret; + } + + break; + + case STI_VL: + if (! value.isString ()) + { + error = bad_type (json_name, fieldName); + return ret; + } + + try + { + std::pair vBlob (strUnHex (value.asString ())); + + if (!vBlob.second) + throw std::invalid_argument ("invalid data"); + + ret = std::make_unique (field, vBlob.first); + } + catch (...) + { + error = invalid_data (json_name, fieldName); + return ret; + } + + break; + + case STI_AMOUNT: + try + { + ret = std::make_unique (amountFromJson (field, value)); + } + catch (...) + { + error = invalid_data (json_name, fieldName); + return ret; + } + + break; + + case STI_VECTOR256: + if (! value.isArray ()) + { + error = array_expected (json_name, fieldName); + return ret; + } + + try + { + std::unique_ptr tail = + std::make_unique (field); + for (Json::UInt i = 0; value.isValidIndex (i); ++i) + { + uint256 s; + s.SetHex (value[i].asString ()); + tail->push_back (s); + } + ret = std::move (tail); + } + catch (...) + { + error = invalid_data (json_name, fieldName); + return ret; + } + + break; + + case STI_PATHSET: + if (!value.isArray ()) + { + error = array_expected (json_name, fieldName); + return ret; + } + + try + { + std::unique_ptr tail = + std::make_unique (field); + + 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 ret; + } + + 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 ret; + } + + Json::Value const& account = pathEl["account"]; + Json::Value const& currency = pathEl["currency"]; + Json::Value const& issuer = pathEl["issuer"]; + bool hasCurrency = false; + Account uAccount, uIssuer; + Currency uCurrency; + + if (! account.isNull ()) + { + // human account id + if (! account.isString ()) + { + error = string_expected (element_name, "account"); + return ret; + } + + 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 ret; + } + + uAccount = a.getAccountID (); + } + } + + if (!currency.isNull ()) + { + // human currency + if (!currency.isString ()) + { + error = string_expected (element_name, "currency"); + return ret; + } + + hasCurrency = true; + + if (currency.asString ().size () == 40) + { + uCurrency.SetHex (currency.asString ()); + } + else if (!to_currency (uCurrency, currency.asString ())) + { + error = invalid_data (element_name, "currency"); + return ret; + } + } + + if (!issuer.isNull ()) + { + // human account id + if (!issuer.isString ()) + { + error = string_expected (element_name, "issuer"); + return ret; + } + + if (issuer.asString ().size () == 40) + { + uIssuer.SetHex (issuer.asString ()); + } + else + { + RippleAddress a; + + if (!a.setAccountID (issuer.asString ())) + { + error = invalid_data (element_name, "issuer"); + return ret; + } + + uIssuer = a.getAccountID (); + } + } + + p.emplace_back (uAccount, uCurrency, uIssuer, hasCurrency); + } + + tail->push_back (p); + } + ret = std::move (tail); + } + catch (...) + { + error = invalid_data (json_name, fieldName); + return ret; + } + + break; + + case STI_ACCOUNT: + { + if (! value.isString ()) + { + error = bad_type (json_name, fieldName); + return ret; + } + + std::string const strValue = value.asString (); + + try + { + if (value.size () == 40) // 160-bit hex account value + { + Account account; + account.SetHex (strValue); + ret = std::make_unique (field, account); + } + else + { + // ripple address + RippleAddress a; + + if (!a.setAccountID (strValue)) + { + error = invalid_data (json_name, fieldName); + return ret; + } + + ret = + std::make_unique (field, a.getAccountID ()); + } + } + catch (...) + { + error = invalid_data (json_name, fieldName); + return ret; + } + } + break; + + default: + error = bad_type (json_name, fieldName); + return ret; + } + + return ret; +} + +static const int maxDepth = 64; + +// Forward declaration since parseObject() and parseArray() call each other. +static bool parseArray ( + std::string const& json_name, + Json::Value const& json, + SField::ref inName, + int depth, + std::unique_ptr & sub_array, + Json::Value& error); + + + +static bool parseObject ( + std::string const& json_name, + Json::Value const& json, + SField::ref inName, + int depth, + std::unique_ptr & sub_object, + Json::Value& error) { if (! json.isObject ()) { @@ -126,11 +671,19 @@ bool STParsedJSON::parse (std::string const& json_name, return false; } + if (depth > maxDepth) + { + error = too_deep (json_name); + return false; + } + SField::ptr name (&inName); boost::ptr_vector data; Json::Value::Members members (json.getMemberNames ()); + int itrCount = 0; + for (Json::Value::Members::iterator it (members.begin ()); it != members.end (); ++it) { @@ -147,494 +700,8 @@ bool STParsedJSON::parse (std::string const& json_name, 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, - beast::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, - beast::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 - { - std::pair ret(strUnHex (value.asString ())); - - if (!ret.second) - throw std::invalid_argument ("invalid data"); - - data.push_back (new STVariableLength (field, ret.first)); - } - catch (...) - { - error = invalid_data (json_name, fieldName); - return false; - } - - break; - - case STI_AMOUNT: - try - { - data.push_back (new STAmount (amountFromJson (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 ())); - assert (tail); - - for (Json::UInt i = 0; value.isValidIndex (i); ++i) - { - uint256 s; - s.SetHex (value[i].asString ()); - tail->push_back (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 ()); - assert (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; - } - - Json::Value const& account = pathEl["account"]; - Json::Value const& currency = pathEl["currency"]; - Json::Value const& issuer = pathEl["issuer"]; - bool hasCurrency = false; - Account uAccount, uIssuer; - Currency uCurrency; - - 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 (!to_currency (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.emplace_back (uAccount, uCurrency, uIssuer, hasCurrency); - } - - tail->push_back (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 - { - Account account; - account.SetHex (strValue); - data.push_back (new STAccount (field, account)); - } - 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; + // Object-style containers (which recurse). case STI_OBJECT: case STI_TRANSACTION: case STI_LEDGERENTRY: @@ -645,17 +712,11 @@ bool STParsedJSON::parse (std::string const& json_name, 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_)); + bool const success (parseObject (json_name + "." + fieldName, + value, field, depth + 1, sub_object_, error)); if (! success) return false; data.push_back (sub_object_.release ()); @@ -668,60 +729,16 @@ bool STParsedJSON::parse (std::string const& json_name, break; + // Array-style containers (which recurse). 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 ()); - assert (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 nameField (SField::getField(objectName)); - - if (nameField == sfInvalid) - { - error = unknown_field (json_name, objectName); - return false; - } - - 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 || (sub_object_->getFName().fieldType != STI_OBJECT)) - return false; - } - tail->push_back (*sub_object_); - } + std::unique_ptr sub_array_; + bool const success (parseArray (json_name + "." + fieldName, + value, field, depth + 1, sub_array_, error)); + if (! success) + return false; + data.push_back (sub_array_.release ()); } catch (...) { @@ -731,14 +748,123 @@ bool STParsedJSON::parse (std::string const& json_name, break; + // Everything else (types that don't recurse). default: - error = bad_type (json_name, fieldName); - return false; + { + std::unique_ptr serTyp = + parseLeaf (json_name, fieldName, name, value, error); + + if (!serTyp) + return false; + + data.push_back (serTyp.release ()); + } + + break; } } - sub_object.reset (new STObject (*name, data)); + sub_object = std::make_unique (*name, data); return true; } +static bool parseArray ( + std::string const& json_name, + Json::Value const& json, + SField::ref inName, + int depth, + std::unique_ptr & sub_array, + Json::Value& error) +{ + if (! json.isArray ()) + { + error = not_an_array (json_name); + return false; + } + + if (depth > maxDepth) + { + error = too_deep (json_name); + return false; + } + + try + { + std::unique_ptr tail = std::make_unique (inName); + + for (Json::UInt i = 0; json.isValidIndex (i); ++i) + { + bool const isObject (json[i].isObject()); + bool const singleKey (isObject ? json[i].size() == 1 : true); + + if (!isObject || !singleKey) + { + error = singleton_expected (json_name, i); + 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 (json[i].getMemberNames()[0]);; + SField::ref nameField (SField::getField(objectName)); + + if (nameField == sfInvalid) + { + error = unknown_field (json_name, objectName); + return false; + } + + Json::Value const objectFields (json[i][objectName]); + + std::unique_ptr sub_object_; + { + std::stringstream ss; + ss << json_name << "." << + "[" << i << "]." << objectName; + bool const success (parseObject (ss.str (), objectFields, + nameField, depth + 1, sub_object_, error)); + + if (! success || + (sub_object_->getFName().fieldType != STI_OBJECT)) + { + return false; + } + } + tail->push_back (*sub_object_); + } + sub_array = std::move (tail); + } + catch (...) + { + error = invalid_data (json_name); + return false; + } + return true; +} + +} // STParsedJSONDetail + +//------------------------------------------------------------------------------ + +STParsedJSONObject::STParsedJSONObject ( + std::string const& name, + Json::Value const& json) +{ + using namespace STParsedJSONDetail; + parseObject (name, json, sfGeneric, 0, object, error); +} + +//------------------------------------------------------------------------------ + +STParsedJSONArray::STParsedJSONArray ( + std::string const& name, + Json::Value const& json) +{ + using namespace STParsedJSONDetail; + parseArray (name, json, sfGeneric, 0, array, error); +} + + + } // ripple diff --git a/src/ripple/data/protocol/STParsedJSON.h b/src/ripple/data/protocol/STParsedJSON.h index 0873677184..51ba336d39 100644 --- a/src/ripple/data/protocol/STParsedJSON.h +++ b/src/ripple/data/protocol/STParsedJSON.h @@ -22,10 +22,10 @@ namespace ripple { -/** Holds the serialized result of parsing input JSON. +/** Holds the serialized result of parsing an input JSON object. This does validation and checking on the provided JSON. */ -class STParsedJSON +class STParsedJSONObject { public: /** Parses and creates an STParsedJSON object. @@ -35,50 +35,49 @@ public: @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); + STParsedJSONObject (std::string const& name, Json::Value const& json); + + STParsedJSONObject () = delete; + STParsedJSONObject (STParsedJSONObject const&) = delete; + STParsedJSONObject& operator= (STParsedJSONObject const&) = delete; + ~STParsedJSONObject () = default; /** 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); }; +/** Holds the serialized result of parsing an input JSON array. + This does validation and checking on the provided JSON. +*/ +class STParsedJSONArray +{ +public: + /** Parses and creates an STParsedJSON array. + The result of the parsing is stored in array and error. + Exceptions: + Does not throw. + @param name The name of the JSON field, used in diagnostics. + @param json The JSON-RPC to parse. + */ + STParsedJSONArray (std::string const& name, Json::Value const& json); + + STParsedJSONArray () = delete; + STParsedJSONArray (STParsedJSONArray const&) = delete; + STParsedJSONArray& operator= (STParsedJSONArray const&) = delete; + ~STParsedJSONArray () = default; + + /** The STArray if the parse was successful. */ + std::unique_ptr array; + + /** On failure, an appropriate set of error values. */ + Json::Value error; +}; + + + } // ripple #endif diff --git a/src/ripple/rpc/handlers/RipplePathFind.cpp b/src/ripple/rpc/handlers/RipplePathFind.cpp index f917fc69cb..8531bae8af 100644 --- a/src/ripple/rpc/handlers/RipplePathFind.cpp +++ b/src/ripple/rpc/handlers/RipplePathFind.cpp @@ -198,7 +198,7 @@ Json::Value doRipplePathFind (RPC::Context& context) if (context.params_.isMember("paths")) { - STParsedJSON paths ("paths", context.params_["paths"]); + STParsedJSONObject paths ("paths", context.params_["paths"]); if (paths.object.get() == nullptr) return paths.error; else diff --git a/src/ripple/rpc/impl/TransactionSign.cpp b/src/ripple/rpc/impl/TransactionSign.cpp index b571bffecc..10c50c767b 100644 --- a/src/ripple/rpc/impl/TransactionSign.cpp +++ b/src/ripple/rpc/impl/TransactionSign.cpp @@ -324,7 +324,7 @@ Json::Value transactionSign ( } } - STParsedJSON parsed ("tx_json", tx_json); + STParsedJSONObject parsed ("tx_json", tx_json); if (!parsed.object.get()) { jvResult ["error"] = parsed.error ["error"];