Allow Json parser understand TER strings where appropriate

This commit is contained in:
Edward Hennis
2017-04-05 17:12:12 -04:00
committed by Scott Schurr
parent 7e9ac16c22
commit 46004158a2
8 changed files with 293 additions and 6 deletions

View File

@@ -4849,6 +4849,10 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\test\protocol\TER_test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\test\protocol\types_test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>

View File

@@ -5556,6 +5556,9 @@
<ClCompile Include="..\..\src\test\protocol\STTx_test.cpp">
<Filter>test\protocol</Filter>
</ClCompile>
<ClCompile Include="..\..\src\test\protocol\TER_test.cpp">
<Filter>test\protocol</Filter>
</ClCompile>
<ClCompile Include="..\..\src\test\protocol\types_test.cpp">
<Filter>test\protocol</Filter>
</ClCompile>

View File

@@ -20,6 +20,7 @@
#ifndef RIPPLE_PROTOCOL_TER_H_INCLUDED
#define RIPPLE_PROTOCOL_TER_H_INCLUDED
#include <boost/optional.hpp>
#include <string>
namespace ripple {
@@ -254,6 +255,10 @@ extern
std::string
transHuman (TER code);
extern
boost::optional<TER>
transCode(std::string const& token);
} // ripple
#endif

View File

@@ -31,6 +31,7 @@
#include <ripple/protocol/STInteger.h>
#include <ripple/protocol/STParsedJSON.h>
#include <ripple/protocol/STPathSet.h>
#include <ripple/protocol/TER.h>
#include <ripple/protocol/TxFormats.h>
#include <ripple/protocol/types.h>
#include <ripple/protocol/impl/STVar.h>
@@ -189,31 +190,61 @@ static boost::optional<detail::STVar> parseLeaf (
case STI_UINT8:
try
{
constexpr auto minValue = std::numeric_limits<std::uint8_t>::min();
constexpr auto maxValue = std::numeric_limits<std::uint8_t>::max();
if (value.isString ())
{
// VFALCO TODO wtf?
std::string const strValue = value.asString();
if (!strValue.empty() &&
((strValue[0] < '0') || (strValue[0] > '9')))
{
if (field == sfTransactionResult)
{
auto ter = transCode(strValue);
if (!ter || *ter < minValue || *ter > maxValue)
{
error = out_of_range(json_name, fieldName);
return ret;
}
ret = detail::make_stvar<STUInt8>(field,
static_cast<std::uint8_t>(*ter));
}
else
{
error = bad_type(json_name, fieldName);
return ret;
}
}
else
{
ret = detail::make_stvar <STUInt8>(field,
beast::lexicalCastThrow <std::uint8_t>(strValue));
}
}
else if (value.isInt ())
{
if (value.asInt () < 0 || value.asInt () > 255)
if (value.asInt () < minValue || value.asInt () > maxValue)
{
error = out_of_range (json_name, fieldName);
return ret;
}
ret = detail::make_stvar <STUInt8> (field,
static_cast <unsigned char> (value.asInt ()));
static_cast <std::uint8_t> (value.asInt ()));
}
else if (value.isUInt ())
{
if (value.asUInt () > 255)
if (value.asUInt () > maxValue)
{
error = out_of_range (json_name, fieldName);
return ret;
}
ret = detail::make_stvar <STUInt8> (field,
static_cast <unsigned char> (value.asUInt ()));
static_cast <std::uint8_t> (value.asUInt ()));
}
else
{

View File

@@ -19,12 +19,19 @@
#include <BeastConfig.h>
#include <ripple/protocol/TER.h>
#include <boost/range/adaptor/transformed.hpp>
#include <unordered_map>
#include <type_traits>
namespace ripple {
bool transResultInfo (TER code, std::string& token, std::string& text)
namespace detail {
static
std::unordered_map<
std::underlying_type_t<TER>,
std::pair<char const* const, char const* const>> const&
transResults()
{
static
std::unordered_map<
@@ -141,6 +148,14 @@ bool transResultInfo (TER code, std::string& token, std::string& text)
{ tesSUCCESS, { "tesSUCCESS", "The transaction was applied. Only final in a validated ledger." } },
};
return results;
}
}
bool transResultInfo (TER code, std::string& token, std::string& text)
{
auto& results = detail::transResults();
auto const r = results.find (
static_cast<std::underlying_type_t<TER>> (code));
@@ -169,4 +184,35 @@ std::string transHuman (TER code)
return transResultInfo (code, token, text) ? text : "-";
}
boost::optional<TER>
transCode(std::string const& token)
{
static
auto const results = []
{
auto& byTer = detail::transResults();
auto range = boost::make_iterator_range(byTer.begin(),
byTer.end());
auto tRange = boost::adaptors::transform(
range,
[](auto const& r)
{
return std::make_pair(r.second.first, r.first);
}
);
std::unordered_map<
std::string,
std::underlying_type_t<TER>> const
byToken(tRange.begin(), tRange.end());
return byToken;
}();
auto const r = results.find(token);
if (r == results.end())
return boost::none;
return static_cast<TER>(r->second);
}
} // ripple

View File

@@ -19,6 +19,7 @@
#include <BeastConfig.h>
#include <ripple/basics/Log.h>
#include <ripple/protocol/JsonFields.h>
#include <ripple/protocol/SecretKey.h>
#include <ripple/protocol/st.h>
#include <ripple/json/json_reader.h>
@@ -88,6 +89,7 @@ public:
if (parsedOK)
{
STParsedJSONObject parsed ("test", jsonObject);
BEAST_EXPECT(parsed.object);
std::string const& serialized (
to_string (parsed.object->getJson(0)));
BEAST_EXPECT(serialized == json);
@@ -98,6 +100,138 @@ public:
}
}
void testParseJSONEdgeCases()
{
testcase("parse json object");
{
std::string const goodJson(
R"({"CloseResolution":19,"Method":250,)"
R"("TransactionResult":"tecFROZEN"})");
Json::Value jv;
if (BEAST_EXPECT(parseJSONString(goodJson, jv)))
{
STParsedJSONObject parsed("test", jv);
if (BEAST_EXPECT(parsed.object))
{
std::string const& serialized(
to_string(parsed.object->getJson(0)));
BEAST_EXPECT(serialized == goodJson);
}
}
}
{
std::string const goodJson(
R"({"CloseResolution":19,"Method":"250",)"
R"("TransactionResult":"tecFROZEN"})");
std::string const expectedJson(
R"({"CloseResolution":19,"Method":250,)"
R"("TransactionResult":"tecFROZEN"})");
Json::Value jv;
if (BEAST_EXPECT(parseJSONString(goodJson, jv)))
{
// Integer values are always parsed as int,
// unless they're too big. We want a small uint.
jv["CloseResolution"] = Json::UInt(19);
STParsedJSONObject parsed("test", jv);
if (BEAST_EXPECT(parsed.object))
{
std::string const& serialized(
to_string(parsed.object->getJson(0)));
BEAST_EXPECT(serialized == expectedJson);
}
}
}
{
std::string const json(
R"({"CloseResolution":19,"Method":250,)"
R"("TransactionResult":"terQUEUED"})");
Json::Value jv;
if (BEAST_EXPECT(parseJSONString(json, jv)))
{
STParsedJSONObject parsed("test", jv);
BEAST_EXPECT(!parsed.object);
BEAST_EXPECT(parsed.error);
BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
BEAST_EXPECT(parsed.error[jss::error_message] ==
"Field 'test.TransactionResult' is out of range.");
}
}
{
std::string const json(
R"({"CloseResolution":19,"Method":"pony",)"
R"("TransactionResult":"tesSUCCESS"})");
Json::Value jv;
if (BEAST_EXPECT(parseJSONString(json, jv)))
{
STParsedJSONObject parsed("test", jv);
BEAST_EXPECT(!parsed.object);
BEAST_EXPECT(parsed.error);
BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
BEAST_EXPECT(parsed.error[jss::error_message] ==
"Field 'test.Method' has bad type.");
}
}
{
std::string const json(
R"({"CloseResolution":19,"Method":3294967296,)"
R"("TransactionResult":"tesSUCCESS"})");
Json::Value jv;
if (BEAST_EXPECT(parseJSONString(json, jv)))
{
STParsedJSONObject parsed("test", jv);
BEAST_EXPECT(!parsed.object);
BEAST_EXPECT(parsed.error);
BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
BEAST_EXPECT(parsed.error[jss::error_message] ==
"Field 'test.Method' is out of range.");
}
}
{
std::string const json(
R"({"CloseResolution":-10,"Method":42,)"
R"("TransactionResult":"tesSUCCESS"})");
Json::Value jv;
if (BEAST_EXPECT(parseJSONString(json, jv)))
{
STParsedJSONObject parsed("test", jv);
BEAST_EXPECT(!parsed.object);
BEAST_EXPECT(parsed.error);
BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
BEAST_EXPECT(parsed.error[jss::error_message] ==
"Field 'test.CloseResolution' is out of range.");
}
}
{
std::string const json(
R"({"CloseResolution":19,"Method":3.141592653,)"
R"("TransactionResult":"tesSUCCESS"})");
Json::Value jv;
if (BEAST_EXPECT(parseJSONString(json, jv)))
{
STParsedJSONObject parsed("test", jv);
BEAST_EXPECT(!parsed.object);
BEAST_EXPECT(parsed.error);
BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
BEAST_EXPECT(parsed.error[jss::error_message] ==
"Field 'test.Method' has bad type.");
}
}
}
void testSerialization ()
{
testcase ("serialization");
@@ -510,6 +644,7 @@ public:
testSerialization();
testParseJSONArray();
testParseJSONArrayWithInvalidChildrenObjects();
testParseJSONEdgeCases();
}
};

View File

@@ -0,0 +1,62 @@
//------------------------------------------------------------------------------
/*
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.
*/
//==============================================================================
#include <BeastConfig.h>
#include <ripple/protocol/TER.h>
#include <ripple/beast/unit_test.h>
namespace ripple {
struct TER_test : public beast::unit_test::suite
{
void
testTransResultInfo()
{
for (auto i = -400; i < 400; ++i)
{
TER t = static_cast<TER>(i);
auto inRange = isTelLocal(t) ||
isTemMalformed(t) ||
isTefFailure(t) ||
isTerRetry(t) ||
isTesSuccess(t) ||
isTecClaim(t);
std::string token, text;
auto good = transResultInfo(t, token, text);
BEAST_EXPECT(inRange || !good);
BEAST_EXPECT(transToken(t) == (good ? token : "-"));
BEAST_EXPECT(transHuman(t) == (good ? text : "-"));
auto code = transCode(token);
BEAST_EXPECT(good == !!code);
BEAST_EXPECT(!code || *code == t);
}
}
void
run() override
{
testTransResultInfo();
}
};
BEAST_DEFINE_TESTSUITE(TER,protocol,ripple);
}

View File

@@ -31,5 +31,6 @@
#include <test/protocol/STAmount_test.cpp>
#include <test/protocol/STObject_test.cpp>
#include <test/protocol/STTx_test.cpp>
#include <test/protocol/TER_test.cpp>
#include <test/protocol/types_test.cpp>
#include <test/protocol/XRPAmount_test.cpp>