diff --git a/Builds/VisualStudio2015/RippleD.vcxproj b/Builds/VisualStudio2015/RippleD.vcxproj
index 7fd435d34..0e806b05c 100644
--- a/Builds/VisualStudio2015/RippleD.vcxproj
+++ b/Builds/VisualStudio2015/RippleD.vcxproj
@@ -4849,6 +4849,10 @@
True
True
+
+ True
+ True
+
True
True
diff --git a/Builds/VisualStudio2015/RippleD.vcxproj.filters b/Builds/VisualStudio2015/RippleD.vcxproj.filters
index c33bafc99..fc091b25c 100644
--- a/Builds/VisualStudio2015/RippleD.vcxproj.filters
+++ b/Builds/VisualStudio2015/RippleD.vcxproj.filters
@@ -5556,6 +5556,9 @@
test\protocol
+
+ test\protocol
+
test\protocol
diff --git a/src/ripple/protocol/TER.h b/src/ripple/protocol/TER.h
index 811de43e9..45cd2903f 100644
--- a/src/ripple/protocol/TER.h
+++ b/src/ripple/protocol/TER.h
@@ -20,6 +20,7 @@
#ifndef RIPPLE_PROTOCOL_TER_H_INCLUDED
#define RIPPLE_PROTOCOL_TER_H_INCLUDED
+#include
#include
namespace ripple {
@@ -254,6 +255,10 @@ extern
std::string
transHuman (TER code);
+extern
+boost::optional
+transCode(std::string const& token);
+
} // ripple
#endif
diff --git a/src/ripple/protocol/impl/STParsedJSON.cpp b/src/ripple/protocol/impl/STParsedJSON.cpp
index 5aa7374dd..603324884 100644
--- a/src/ripple/protocol/impl/STParsedJSON.cpp
+++ b/src/ripple/protocol/impl/STParsedJSON.cpp
@@ -31,6 +31,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -189,31 +190,61 @@ static boost::optional parseLeaf (
case STI_UINT8:
try
{
+ constexpr auto minValue = std::numeric_limits::min();
+ constexpr auto maxValue = std::numeric_limits::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(field,
+ static_cast(*ter));
+ }
+ else
+ {
+ error = bad_type(json_name, fieldName);
+ return ret;
+ }
+ }
+ else
+ {
+ ret = detail::make_stvar (field,
+ beast::lexicalCastThrow (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 (field,
- static_cast (value.asInt ()));
+ static_cast (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 (field,
- static_cast (value.asUInt ()));
+ static_cast (value.asUInt ()));
}
else
{
diff --git a/src/ripple/protocol/impl/TER.cpp b/src/ripple/protocol/impl/TER.cpp
index b4674923e..0ee0bc27e 100644
--- a/src/ripple/protocol/impl/TER.cpp
+++ b/src/ripple/protocol/impl/TER.cpp
@@ -19,12 +19,19 @@
#include
#include
+#include
#include
#include
namespace ripple {
-bool transResultInfo (TER code, std::string& token, std::string& text)
+namespace detail {
+
+static
+std::unordered_map<
+ std::underlying_type_t,
+ std::pair> 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> (code));
@@ -169,4 +184,35 @@ std::string transHuman (TER code)
return transResultInfo (code, token, text) ? text : "-";
}
+boost::optional
+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> const
+ byToken(tRange.begin(), tRange.end());
+ return byToken;
+ }();
+
+ auto const r = results.find(token);
+
+ if (r == results.end())
+ return boost::none;
+
+ return static_cast(r->second);
+}
+
} // ripple
diff --git a/src/test/protocol/STObject_test.cpp b/src/test/protocol/STObject_test.cpp
index 6dd569d96..7c7d4305f 100644
--- a/src/test/protocol/STObject_test.cpp
+++ b/src/test/protocol/STObject_test.cpp
@@ -19,6 +19,7 @@
#include
#include
+#include
#include
#include
#include
@@ -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();
}
};
diff --git a/src/test/protocol/TER_test.cpp b/src/test/protocol/TER_test.cpp
new file mode 100644
index 000000000..77949d0c3
--- /dev/null
+++ b/src/test/protocol/TER_test.cpp
@@ -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
+#include
+#include
+
+namespace ripple {
+
+struct TER_test : public beast::unit_test::suite
+{
+ void
+ testTransResultInfo()
+ {
+ for (auto i = -400; i < 400; ++i)
+ {
+ TER t = static_cast(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);
+
+}
diff --git a/src/test/unity/protocol_test_unity.cpp b/src/test/unity/protocol_test_unity.cpp
index 363d8b7eb..a971e34f6 100644
--- a/src/test/unity/protocol_test_unity.cpp
+++ b/src/test/unity/protocol_test_unity.cpp
@@ -31,5 +31,6 @@
#include
#include
#include
+#include
#include
#include
\ No newline at end of file