Merge branch 'ximinez/lending-XLS-66-2' into ximinez/lending-XLS-66-ongoing

This commit is contained in:
Ed Hennis
2025-12-19 13:22:18 -05:00
committed by GitHub
17 changed files with 101 additions and 1023 deletions

View File

@@ -1,233 +0,0 @@
#include <xrpl/basics/contract.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/json/Object.h>
#include <xrpl/json/Output.h>
#include <xrpl/json/Writer.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <utility>
namespace Json {
Collection::Collection(Collection* parent, Writer* writer)
: parent_(parent), writer_(writer), enabled_(true)
{
checkWritable("Collection::Collection()");
if (parent_)
{
check(parent_->enabled_, "Parent not enabled in constructor");
parent_->enabled_ = false;
}
}
Collection::~Collection()
{
if (writer_)
writer_->finish();
if (parent_)
parent_->enabled_ = true;
}
Collection&
Collection::operator=(Collection&& that) noexcept
{
parent_ = that.parent_;
writer_ = that.writer_;
enabled_ = that.enabled_;
that.parent_ = nullptr;
that.writer_ = nullptr;
that.enabled_ = false;
return *this;
}
Collection::Collection(Collection&& that) noexcept
{
*this = std::move(that);
}
void
Collection::checkWritable(std::string const& label)
{
if (!enabled_)
xrpl::Throw<std::logic_error>(label + ": not enabled");
if (!writer_)
xrpl::Throw<std::logic_error>(label + ": not writable");
}
//------------------------------------------------------------------------------
Object::Root::Root(Writer& w) : Object(nullptr, &w)
{
writer_->startRoot(Writer::object);
}
Object
Object::setObject(std::string const& key)
{
checkWritable("Object::setObject");
if (writer_)
writer_->startSet(Writer::object, key);
return Object(this, writer_);
}
Array
Object::setArray(std::string const& key)
{
checkWritable("Object::setArray");
if (writer_)
writer_->startSet(Writer::array, key);
return Array(this, writer_);
}
//------------------------------------------------------------------------------
Object
Array::appendObject()
{
checkWritable("Array::appendObject");
if (writer_)
writer_->startAppend(Writer::object);
return Object(this, writer_);
}
Array
Array::appendArray()
{
checkWritable("Array::makeArray");
if (writer_)
writer_->startAppend(Writer::array);
return Array(this, writer_);
}
//------------------------------------------------------------------------------
Object::Proxy::Proxy(Object& object, std::string const& key)
: object_(object), key_(key)
{
}
Object::Proxy
Object::operator[](std::string const& key)
{
return Proxy(*this, key);
}
Object::Proxy
Object::operator[](Json::StaticString const& key)
{
return Proxy(*this, std::string(key));
}
//------------------------------------------------------------------------------
void
Array::append(Json::Value const& v)
{
auto t = v.type();
switch (t)
{
case Json::nullValue:
return append(nullptr);
case Json::intValue:
return append(v.asInt());
case Json::uintValue:
return append(v.asUInt());
case Json::realValue:
return append(v.asDouble());
case Json::stringValue:
return append(v.asString());
case Json::booleanValue:
return append(v.asBool());
case Json::objectValue: {
auto object = appendObject();
copyFrom(object, v);
return;
}
case Json::arrayValue: {
auto array = appendArray();
for (auto& item : v)
array.append(item);
return;
}
}
UNREACHABLE("Json::Array::append : invalid type"); // LCOV_EXCL_LINE
}
void
Object::set(std::string const& k, Json::Value const& v)
{
auto t = v.type();
switch (t)
{
case Json::nullValue:
return set(k, nullptr);
case Json::intValue:
return set(k, v.asInt());
case Json::uintValue:
return set(k, v.asUInt());
case Json::realValue:
return set(k, v.asDouble());
case Json::stringValue:
return set(k, v.asString());
case Json::booleanValue:
return set(k, v.asBool());
case Json::objectValue: {
auto object = setObject(k);
copyFrom(object, v);
return;
}
case Json::arrayValue: {
auto array = setArray(k);
for (auto& item : v)
array.append(item);
return;
}
}
UNREACHABLE("Json::Object::set : invalid type"); // LCOV_EXCL_LINE
}
//------------------------------------------------------------------------------
namespace {
template <class Object>
void
doCopyFrom(Object& to, Json::Value const& from)
{
XRPL_ASSERT(from.isObjectOrNull(), "Json::doCopyFrom : valid input type");
auto members = from.getMemberNames();
for (auto& m : members)
to[m] = from[m];
}
} // namespace
void
copyFrom(Json::Value& to, Json::Value const& from)
{
if (!to) // Short circuit this very common case.
to = from;
else
doCopyFrom(to, from);
}
void
copyFrom(Object& to, Json::Value const& from)
{
doCopyFrom(to, from);
}
WriterObject
stringWriterObject(std::string& s)
{
return WriterObject(stringOutput(s));
}
} // namespace Json

View File

@@ -17,7 +17,7 @@ namespace BuildInfo {
// and follow the format described at http://semver.org/
//------------------------------------------------------------------------------
// clang-format off
char const* const versionString = "3.1.0-b0"
char const* const versionString = "3.2.0-b0"
// clang-format on
#if defined(DEBUG) || defined(SANITIZER)

View File

@@ -160,6 +160,24 @@ constexpr ErrorInfo unknownError;
//------------------------------------------------------------------------------
void
inject_error(error_code_i code, Json::Value& json)
{
ErrorInfo const& info(get_error_info(code));
json[jss::error] = info.token;
json[jss::error_code] = info.code;
json[jss::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[jss::error] = info.token;
json[jss::error_code] = info.code;
json[jss::error_message] = message;
}
ErrorInfo const&
get_error_info(error_code_i code)
{

View File

@@ -9,7 +9,7 @@ struct RPCErr;
// VFALCO NOTE Deprecated function
Json::Value
rpcError(int iError)
rpcError(error_code_i iError)
{
Json::Value jvResult(Json::objectValue);
RPC::inject_error(iError, jvResult);

View File

@@ -1,239 +0,0 @@
#include <test/json/TestOutputSuite.h>
#include <xrpl/beast/unit_test.h>
#include <xrpl/json/Object.h>
namespace Json {
class JsonObject_test : public xrpl::test::TestOutputSuite
{
void
setup(std::string const& testName)
{
testcase(testName);
output_.clear();
}
std::unique_ptr<WriterObject> writerObject_;
Object&
makeRoot()
{
writerObject_ =
std::make_unique<WriterObject>(stringWriterObject(output_));
return **writerObject_;
}
void
expectResult(std::string const& expected)
{
writerObject_.reset();
TestOutputSuite::expectResult(expected);
}
public:
void
testTrivial()
{
setup("trivial");
{
auto& root = makeRoot();
(void)root;
}
expectResult("{}");
}
void
testSimple()
{
setup("simple");
{
auto& root = makeRoot();
root["hello"] = "world";
root["skidoo"] = 23;
root["awake"] = false;
root["temperature"] = 98.6;
}
expectResult(
"{\"hello\":\"world\","
"\"skidoo\":23,"
"\"awake\":false,"
"\"temperature\":98.6}");
}
void
testOneSub()
{
setup("oneSub");
{
auto& root = makeRoot();
root.setArray("ar");
}
expectResult("{\"ar\":[]}");
}
void
testSubs()
{
setup("subs");
{
auto& root = makeRoot();
{
// Add an array with three entries.
auto array = root.setArray("ar");
array.append(23);
array.append(false);
array.append(23.5);
}
{
// Add an object with one entry.
auto obj = root.setObject("obj");
obj["hello"] = "world";
}
{
// Add another object with two entries.
Json::Value value;
value["h"] = "w";
value["f"] = false;
root["obj2"] = value;
}
}
// Json::Value has an unstable order...
auto case1 =
"{\"ar\":[23,false,23.5],"
"\"obj\":{\"hello\":\"world\"},"
"\"obj2\":{\"h\":\"w\",\"f\":false}}";
auto case2 =
"{\"ar\":[23,false,23.5],"
"\"obj\":{\"hello\":\"world\"},"
"\"obj2\":{\"f\":false,\"h\":\"w\"}}";
writerObject_.reset();
BEAST_EXPECT(output_ == case1 || output_ == case2);
}
void
testSubsShort()
{
setup("subsShort");
{
auto& root = makeRoot();
{
// Add an array with three entries.
auto array = root.setArray("ar");
array.append(23);
array.append(false);
array.append(23.5);
}
// Add an object with one entry.
root.setObject("obj")["hello"] = "world";
{
// Add another object with two entries.
auto object = root.setObject("obj2");
object.set("h", "w");
object.set("f", false);
}
}
expectResult(
"{\"ar\":[23,false,23.5],"
"\"obj\":{\"hello\":\"world\"},"
"\"obj2\":{\"h\":\"w\",\"f\":false}}");
}
void
testFailureObject()
{
{
setup("object failure assign");
auto& root = makeRoot();
auto obj = root.setObject("o1");
expectException([&]() { root["fail"] = "complete"; });
}
{
setup("object failure object");
auto& root = makeRoot();
auto obj = root.setObject("o1");
expectException([&]() { root.setObject("o2"); });
}
{
setup("object failure Array");
auto& root = makeRoot();
auto obj = root.setArray("o1");
expectException([&]() { root.setArray("o2"); });
}
}
void
testFailureArray()
{
{
setup("array failure append");
auto& root = makeRoot();
auto array = root.setArray("array");
auto subarray = array.appendArray();
auto fail = [&]() { array.append("fail"); };
expectException(fail);
}
{
setup("array failure appendArray");
auto& root = makeRoot();
auto array = root.setArray("array");
auto subarray = array.appendArray();
auto fail = [&]() { array.appendArray(); };
expectException(fail);
}
{
setup("array failure appendObject");
auto& root = makeRoot();
auto array = root.setArray("array");
auto subarray = array.appendArray();
auto fail = [&]() { array.appendObject(); };
expectException(fail);
}
}
void
testKeyFailure()
{
setup("repeating keys");
auto& root = makeRoot();
root.set("foo", "bar");
root.set("baz", 0);
// setting key again throws in !NDEBUG builds
auto set_again = [&]() { root.set("foo", "bar"); };
#ifdef NDEBUG
set_again();
pass();
#else
expectException(set_again);
#endif
}
void
run() override
{
testTrivial();
testSimple();
testOneSub();
testSubs();
testSubsShort();
testFailureObject();
testFailureArray();
testKeyFailure();
}
};
BEAST_DEFINE_TESTSUITE(JsonObject, json, xrpl);
} // namespace Json

View File

@@ -3,7 +3,6 @@
#include <xrpld/rpc/RPCCall.h>
#include <xrpl/basics/contract.h>
#include <xrpl/json/Object.h>
#include <xrpl/protocol/ErrorCodes.h>
#include <xrpl/protocol/HashPrefix.h>
#include <xrpl/protocol/Indexes.h>
@@ -83,7 +82,7 @@ cmdToJSONRPC(
// If paramsObj is not empty, put it in a [params] array.
if (paramsObj.begin() != paramsObj.end())
{
auto& paramsArray = Json::setArray(jv, jss::params);
auto& paramsArray = jv[jss::params] = Json::arrayValue;
paramsArray.append(paramsObj);
}
if (paramsObj.isMember(jss::jsonrpc))

View File

@@ -7,7 +7,6 @@
#include <xrpld/rpc/Context.h>
#include <xrpl/basics/chrono.h>
#include <xrpl/json/Object.h>
#include <xrpl/protocol/serialize.h>
namespace xrpl {
@@ -42,10 +41,9 @@ struct LedgerFill
std::optional<NetClock::time_point> closeTime;
};
/** Given a Ledger and options, fill a Json::Object or Json::Value with a
/** Given a Ledger and options, fill a Json::Value with a
description of the ledger.
*/
void
addJson(Json::Value&, LedgerFill const&);
@@ -53,6 +51,10 @@ addJson(Json::Value&, LedgerFill const&);
Json::Value
getJson(LedgerFill const&);
/** Copy all the keys and values from one object into another. */
void
copyFrom(Json::Value& to, Json::Value const& from);
} // namespace xrpl
#endif

View File

@@ -32,10 +32,9 @@ isBinary(LedgerFill const& fill)
return fill.options & LedgerFill::binary;
}
template <class Object>
void
fillJson(
Object& json,
Json::Value& json,
bool closed,
LedgerHeader const& info,
bool bFull,
@@ -78,9 +77,8 @@ fillJson(
}
}
template <class Object>
void
fillJsonBinary(Object& json, bool closed, LedgerHeader const& info)
fillJsonBinary(Json::Value& json, bool closed, LedgerHeader const& info)
{
if (!closed)
json[jss::closed] = false;
@@ -207,11 +205,10 @@ fillJsonTx(
return txJson;
}
template <class Object>
void
fillJsonTx(Object& json, LedgerFill const& fill)
fillJsonTx(Json::Value& json, LedgerFill const& fill)
{
auto&& txns = setArray(json, jss::transactions);
auto& txns = json[jss::transactions] = Json::arrayValue;
auto bBinary = isBinary(fill);
auto bExpanded = isExpanded(fill);
@@ -238,12 +235,11 @@ fillJsonTx(Object& json, LedgerFill const& fill)
}
}
template <class Object>
void
fillJsonState(Object& json, LedgerFill const& fill)
fillJsonState(Json::Value& json, LedgerFill const& fill)
{
auto& ledger = fill.ledger;
auto&& array = Json::setArray(json, jss::accountState);
auto& array = json[jss::accountState] = Json::arrayValue;
auto expanded = isExpanded(fill);
auto binary = isBinary(fill);
@@ -251,7 +247,7 @@ fillJsonState(Object& json, LedgerFill const& fill)
{
if (binary)
{
auto&& obj = appendObject(array);
auto& obj = array.append(Json::objectValue);
obj[jss::hash] = to_string(sle->key());
obj[jss::tx_blob] = serializeHex(*sle);
}
@@ -262,17 +258,16 @@ fillJsonState(Object& json, LedgerFill const& fill)
}
}
template <class Object>
void
fillJsonQueue(Object& json, LedgerFill const& fill)
fillJsonQueue(Json::Value& json, LedgerFill const& fill)
{
auto&& queueData = Json::setArray(json, jss::queue_data);
auto& queueData = json[jss::queue_data] = Json::arrayValue;
auto bBinary = isBinary(fill);
auto bExpanded = isExpanded(fill);
for (auto const& tx : fill.txQueue)
{
auto&& txJson = appendObject(queueData);
auto& txJson = queueData.append(Json::objectValue);
txJson[jss::fee_level] = to_string(tx.feeLevel);
if (tx.lastValid)
txJson[jss::LastLedgerSequence] = *tx.lastValid;
@@ -297,9 +292,8 @@ fillJsonQueue(Object& json, LedgerFill const& fill)
}
}
template <class Object>
void
fillJson(Object& json, LedgerFill const& fill)
fillJson(Json::Value& json, LedgerFill const& fill)
{
// TODO: what happens if bBinary and bExtracted are both set?
// Is there a way to report this back?
@@ -327,7 +321,7 @@ fillJson(Object& json, LedgerFill const& fill)
void
addJson(Json::Value& json, LedgerFill const& fill)
{
auto&& object = Json::addObject(json, jss::ledger);
auto& object = json[jss::ledger] = Json::objectValue;
fillJson(object, fill);
if ((fill.options & LedgerFill::dumpQueue) && !fill.txQueue.empty())
@@ -342,4 +336,20 @@ getJson(LedgerFill const& fill)
return json;
}
void
copyFrom(Json::Value& to, Json::Value const& from)
{
if (!to) // Short circuit this very common case.
to = from;
else
{
// TODO: figure out if there is a way to remove this clause
// or check that it does/needs to do a deep copy
XRPL_ASSERT(from.isObjectOrNull(), "copyFrom : invalid input type");
auto const members = from.getMemberNames();
for (auto const& m : members)
to[m] = from[m];
}
}
} // namespace xrpl

View File

@@ -94,9 +94,8 @@ public:
/** Apply the Status to a JsonObject
*/
template <class Object>
void
inject(Object& object) const
inject(Json::Value& object) const
{
if (auto ec = toErrorCode())
{

View File

@@ -3,8 +3,6 @@
#include <xrpld/app/main/Application.h>
#include <xrpl/json/Object.h>
namespace xrpl {
Json::Value

View File

@@ -75,6 +75,43 @@ LedgerHandler::check()
return Status::OK;
}
void
LedgerHandler::writeResult(Json::Value& value)
{
if (ledger_)
{
copyFrom(value, result_);
addJson(value, {*ledger_, &context_, options_, queueTxs_});
}
else
{
auto& master = context_.app.getLedgerMaster();
{
auto& closed = value[jss::closed] = Json::objectValue;
addJson(closed, {*master.getClosedLedger(), &context_, 0});
}
{
auto& open = value[jss::open] = Json::objectValue;
addJson(open, {*master.getCurrentLedger(), &context_, 0});
}
}
Json::Value warnings{Json::arrayValue};
if (context_.params.isMember(jss::type))
{
Json::Value& w = warnings.append(Json::objectValue);
w[jss::id] = warnRPC_FIELDS_DEPRECATED;
w[jss::message] =
"Some fields from your request are deprecated. Please check the "
"documentation at "
"https://xrpl.org/docs/references/http-websocket-apis/ "
"and update your request. Field `type` is deprecated.";
}
if (warnings.size())
value[jss::warnings] = std::move(warnings);
}
} // namespace RPC
std::pair<org::xrpl::rpc::v1::GetLedgerResponse, grpc::Status>

View File

@@ -9,7 +9,6 @@
#include <xrpld/rpc/Status.h>
#include <xrpld/rpc/detail/Handler.h>
#include <xrpl/json/Object.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/ApiVersion.h>
#include <xrpl/protocol/jss.h>
@@ -37,9 +36,8 @@ public:
Status
check();
template <class Object>
void
writeResult(Object&);
writeResult(Json::Value&);
static constexpr char name[] = "ledger";
@@ -59,49 +57,6 @@ private:
int options_ = 0;
};
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
// Implementation.
template <class Object>
void
LedgerHandler::writeResult(Object& value)
{
if (ledger_)
{
Json::copyFrom(value, result_);
addJson(value, {*ledger_, &context_, options_, queueTxs_});
}
else
{
auto& master = context_.app.getLedgerMaster();
{
auto&& closed = Json::addObject(value, jss::closed);
addJson(closed, {*master.getClosedLedger(), &context_, 0});
}
{
auto&& open = Json::addObject(value, jss::open);
addJson(open, {*master.getCurrentLedger(), &context_, 0});
}
}
Json::Value warnings{Json::arrayValue};
if (context_.params.isMember(jss::type))
{
Json::Value& w = warnings.append(Json::objectValue);
w[jss::id] = warnRPC_FIELDS_DEPRECATED;
w[jss::message] =
"Some fields from your request are deprecated. Please check the "
"documentation at "
"https://xrpl.org/docs/references/http-websocket-apis/ "
"and update your request. Field `type` is deprecated.";
}
if (warnings.size())
value[jss::warnings] = std::move(warnings);
}
} // namespace RPC
} // namespace xrpl

View File

@@ -20,9 +20,8 @@ public:
return Status::OK;
}
template <class Object>
void
writeResult(Object& obj)
writeResult(Json::Value& obj)
{
setVersion(obj, apiVersion_, betaEnabled_);
}