diff --git a/Builds/VisualStudio2013/RippleD.vcxproj b/Builds/VisualStudio2013/RippleD.vcxproj
index 0145acd94..e50b14567 100644
--- a/Builds/VisualStudio2013/RippleD.vcxproj
+++ b/Builds/VisualStudio2013/RippleD.vcxproj
@@ -3185,6 +3185,11 @@
True
+
+ True
+
+
+
True
@@ -3207,12 +3212,16 @@
+
+
+
+
diff --git a/Builds/VisualStudio2013/RippleD.vcxproj.filters b/Builds/VisualStudio2013/RippleD.vcxproj.filters
index 73e128367..0fc74ac9e 100644
--- a/Builds/VisualStudio2013/RippleD.vcxproj.filters
+++ b/Builds/VisualStudio2013/RippleD.vcxproj.filters
@@ -4371,6 +4371,12 @@
ripple\rpc\impl
+
+ ripple\rpc\impl
+
+
+ ripple\rpc\impl
+
ripple\rpc\impl
@@ -4398,6 +4404,9 @@
ripple\rpc
+
+ ripple\rpc
+
ripple\rpc
@@ -4407,6 +4416,9 @@
ripple\rpc
+
+ ripple\rpc
+
ripple\sitefiles\api
diff --git a/src/ripple/common/jsonrpc_fields.h b/src/ripple/common/jsonrpc_fields.h
index 632999731..de640bacb 100644
--- a/src/ripple/common/jsonrpc_fields.h
+++ b/src/ripple/common/jsonrpc_fields.h
@@ -64,6 +64,7 @@ JSS ( consensus );
JSS ( converge_time );
JSS ( converge_time_s );
JSS ( currency );
+JSS ( data );
JSS ( date );
JSS ( engine_result );
JSS ( engine_result_code );
diff --git a/src/ripple/rpc/Status.h b/src/ripple/rpc/Status.h
new file mode 100644
index 000000000..d16452f61
--- /dev/null
+++ b/src/ripple/rpc/Status.h
@@ -0,0 +1,121 @@
+//------------------------------------------------------------------------------
+/*
+ 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_STATUS_H
+#define RIPPLE_STATUS_H
+
+#include
+#include
+
+namespace ripple {
+namespace RPC {
+namespace New {
+
+/** Status represents the results of an operation that might fail.
+
+ It wraps the legacy codes TER and error_code_i, providing both a uniform
+ interface and a way to attach additional information to existing status
+ returns.
+
+ A Status can also be used to fill a Json::Value with a JSON-RPC 2.0
+ error response: see http://www.jsonrpc.org/specification#error_object
+ */
+struct Status
+{
+public:
+ enum class Type {none, TER, error_code_i};
+ using Code = int;
+ using Strings = std::vector ;
+
+ static const Code OK = 0;
+
+ Status () = default;
+
+ Status (Code code, Strings d = {})
+ : type_ (Type::none), code_ (code), messages_ (std::move (d))
+ {
+ }
+
+ Status (TER ter, Strings d = {})
+ : type_ (Type::TER), code_ (ter), messages_ (std::move (d))
+ {
+ }
+
+ Status (error_code_i e, Strings d = {})
+ : type_ (Type::error_code_i), code_ (e), messages_ (std::move (d))
+ {
+ }
+
+ /* Returns a representation of the integer status Code as a string.
+ If the Status is OK, the result is an empty string.
+ */
+ std::string codeString () const;
+
+ /** Fill a Json::Value. If the Status is OK, fillJson has no effect. */
+ void fillJson(Json::Value&);
+
+ /** Returns true if the Status is *not* OK. */
+ operator bool() const
+ {
+ return code_ != OK;
+ }
+
+ /** Returns true if the Status is OK. */
+ bool operator !() const
+ {
+ return ! bool (*this);
+ }
+
+ /** Returns the Status as a TER.
+ This may only be called if type() == Type::TER. */
+ TER toTER () const
+ {
+ assert (type_ == Type::TER);
+ return TER (code_);
+ }
+
+ /** Returns the Status as an error_code_i.
+ This may only be called if type() == Type::error_code_i. */
+ error_code_i toErrorCode() const
+ {
+ assert (type_ == Type::error_code_i);
+ return error_code_i (code_);
+ }
+
+ Strings const& messages() const
+ {
+ return messages_;
+ }
+
+ Type type() const
+ {
+ return type_;
+ }
+
+private:
+ Type type_ = Type::none;
+ Code code_ = OK;
+ Strings messages_;
+};
+
+} // namespace New
+} // namespace RPC
+} // ripple
+
+#endif
diff --git a/src/ripple/rpc/impl/Status.cpp b/src/ripple/rpc/impl/Status.cpp
new file mode 100644
index 000000000..a2e1ef971
--- /dev/null
+++ b/src/ripple/rpc/impl/Status.cpp
@@ -0,0 +1,77 @@
+//------------------------------------------------------------------------------
+/*
+ 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
+
+namespace ripple {
+namespace RPC {
+namespace New {
+
+std::string Status::codeString () const
+{
+ if (!*this)
+ return "";
+
+ if (type_ == Type::none)
+ return std::to_string (code_);
+
+ if (type_ == Status::Type::TER)
+ {
+ std::string s1, s2;
+
+ auto success = transResultInfo (toTER (), s1, s2);
+ assert (success);
+ (void) success;
+
+ return s1 + ": " + s2;
+ }
+
+ if (type_ == Status::Type::error_code_i)
+ {
+ auto info = get_error_info (toErrorCode ());
+ return info.token + ": " + info.message;
+ }
+
+ assert (false);
+ return "";
+}
+
+void Status::fillJson (Json::Value& value)
+{
+ static const std::string separator = ": ";
+
+ if (!*this)
+ return;
+
+ auto& error = value[jss::error];
+ error[jss::code] = code_;
+ error[jss::message] = codeString ();
+
+ // Are there any more messages?
+ if (!messages_.empty ())
+ {
+ auto& messages = error[jss::data];
+ for (auto& i: messages_)
+ messages.append (i);
+ }
+}
+
+} // namespace New
+} // namespace RPC
+} // ripple
diff --git a/src/ripple/rpc/impl/Status_test.cpp b/src/ripple/rpc/impl/Status_test.cpp
new file mode 100644
index 000000000..e7cfae779
--- /dev/null
+++ b/src/ripple/rpc/impl/Status_test.cpp
@@ -0,0 +1,214 @@
+//------------------------------------------------------------------------------
+/*
+ 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
+
+namespace ripple {
+namespace RPC {
+namespace New {
+
+class codeString_test : public beast::unit_test::suite
+{
+private:
+ template
+ std::string codeString (Type t)
+ {
+ return Status (t).codeString();
+ }
+
+ void test_OK ()
+ {
+ testcase ("OK");
+ {
+ auto s = codeString (Status ());
+ expect (s.empty(), "String for OK status");
+ }
+
+ {
+ auto s = codeString (Status::OK);
+ expect (s.empty(), "String for OK status");
+ }
+
+ {
+ auto s = codeString (0);
+ expect (s.empty(), "String for 0 status");
+ }
+
+ {
+ auto s = codeString (tesSUCCESS);
+ expect (s.empty(), "String for tesSUCCESS");
+ }
+
+ {
+ auto s = codeString (rpcSUCCESS);
+ expect (s.empty(), "String for rpcSUCCESS");
+ }
+ }
+
+ void test_error ()
+ {
+ testcase ("error");
+ {
+ auto s = codeString (23);
+ expect (s == "23", s);
+ }
+
+ {
+ auto s = codeString (temBAD_AMOUNT);
+ expect (s == "temBAD_AMOUNT: Can only send positive amounts.", s);
+ }
+
+ {
+ auto s = codeString (rpcBAD_SYNTAX);
+ expect (s == "badSyntax: Syntax error.", s);
+ }
+ }
+
+public:
+ void run()
+ {
+ test_OK ();
+ test_error ();
+ }
+};
+
+BEAST_DEFINE_TESTSUITE (codeString, Status, RPC);
+
+class fillJson_test : public beast::unit_test::suite
+{
+private:
+ Json::Value value_;
+
+ template
+ void fillJson (Type t)
+ {
+ value_.clear ();
+ Status (t).fillJson (value_);
+ }
+
+ void test_OK ()
+ {
+ testcase ("OK");
+ fillJson (Status ());
+ expect (value_.empty(), "Value for empty status");
+
+ fillJson (0);
+ expect (value_.empty(), "Value for 0 status");
+
+ fillJson (Status::OK);
+ expect (value_.empty(), "Value for OK status");
+
+ fillJson (tesSUCCESS);
+ expect (value_.empty(), "Value for tesSUCCESS");
+
+ fillJson (rpcSUCCESS);
+ expect (value_.empty(), "Value for rpcSUCCESS");
+ }
+
+ template
+ void expectFill (
+ std::string const& label,
+ Type status,
+ Status::Strings messages,
+ std::string const& message)
+ {
+ value_.clear ();
+ fillJson (Status (status, messages));
+
+ auto prefix = label + ": ";
+ expect (!value_.empty(), prefix + "No value");
+
+ auto error = value_[jss::error];
+ expect (!error.empty(), prefix + "No error.");
+
+ auto code = error[jss::code].asInt();
+ expect (status == code, prefix + "Wrong status " +
+ std::to_string (code) + " != " + std::to_string (status));
+
+ auto m = error[jss::message].asString ();
+ expect (m == message, m + " != " + message);
+
+ auto d = error[jss::data];
+ size_t s1 = d.size(), s2 = messages.size();
+ expect (s1 == s2, prefix + "Data sizes differ " +
+ std::to_string (s1) + " != " + std::to_string (s2));
+ for (auto i = 0; i < std::min (s1, s2); ++i)
+ {
+ auto ds = d[i].asString();
+ expect (ds == messages[i], prefix + ds + " != " + messages[i]);
+ }
+ }
+
+ void test_error ()
+ {
+ testcase ("error");
+ expectFill (
+ "temBAD_AMOUNT",
+ temBAD_AMOUNT,
+ {},
+ "temBAD_AMOUNT: Can only send positive amounts.");
+
+ expectFill (
+ "rpcBAD_SYNTAX",
+ rpcBAD_SYNTAX,
+ {"An error.", "Another error."},
+ "badSyntax: Syntax error.");
+
+ expectFill (
+ "integer message",
+ 23,
+ {"Stuff."},
+ "23");
+ }
+
+ void test_throw ()
+ {
+ testcase ("throw");
+ try
+ {
+ throw Status (temBAD_PATH, {"path=sdcdfd"});
+ }
+ catch (Status const& s)
+ {
+ expect (s.toTER () == temBAD_PATH, "temBAD_PATH wasn't thrown");
+ auto msgs = s.messages ();
+ expect (msgs.size () == 1, "Wrong number of messages");
+ expect (msgs[0] == "path=sdcdfd", msgs[0]);
+ }
+ catch (...)
+ {
+ expect (false, "Didn't catch a Status");
+ }
+ }
+
+public:
+ void run()
+ {
+ test_OK ();
+ test_error ();
+ test_throw ();
+ }
+};
+
+BEAST_DEFINE_TESTSUITE (fillJson, Status, RPC);
+
+} // namespace New
+} // namespace RPC
+} // ripple
diff --git a/src/ripple/unity/rpcx.cpp b/src/ripple/unity/rpcx.cpp
index 0da772201..52b9dc43d 100644
--- a/src/ripple/unity/rpcx.cpp
+++ b/src/ripple/unity/rpcx.cpp
@@ -35,6 +35,8 @@
#include
#include
#include
+#include
+#include
#include
#include