mirror of
https://github.com/XRPLF/clio.git
synced 2025-11-04 20:05:51 +00:00
add server info
This commit is contained in:
@@ -82,7 +82,8 @@ target_sources(reporting PRIVATE
|
||||
handlers/BookOffers.cpp
|
||||
handlers/LedgerRange.cpp
|
||||
handlers/Ledger.cpp
|
||||
handlers/LedgerEntry.cpp)
|
||||
handlers/LedgerEntry.cpp
|
||||
handlers/ServerInfo.cpp)
|
||||
|
||||
|
||||
message(${Boost_LIBRARIES})
|
||||
|
||||
@@ -104,7 +104,7 @@ doAccountInfo(
|
||||
{
|
||||
response["success"] = "fetched successfully!";
|
||||
if (!binary)
|
||||
response["object"] = getJson(sle);
|
||||
response["object"] = toJson(sle);
|
||||
else
|
||||
response["object"] = ripple::strHex(*dbResponse);
|
||||
response["db_time"] = time;
|
||||
@@ -124,7 +124,7 @@ doAccountInfo(
|
||||
// support multiple SignerLists on one account.
|
||||
auto const sleSigners = ledger->read(keylet::signers(accountID));
|
||||
if (sleSigners)
|
||||
jvSignerList.append(sleSigners->getJson(JsonOptions::none));
|
||||
jvSignerList.append(sleSigners->toJson(JsonOptions::none));
|
||||
|
||||
result[jss::account_data][jss::signer_lists] =
|
||||
std::move(jvSignerList);
|
||||
|
||||
@@ -110,8 +110,8 @@ doAccountTx(boost::json::object const& request, BackendInterface const& backend)
|
||||
if (!binary)
|
||||
{
|
||||
auto [txn, meta] = deserializeTxPlusMeta(txnPlusMeta);
|
||||
obj["transaction"] = getJson(*txn);
|
||||
obj["metadata"] = getJson(*meta);
|
||||
obj["transaction"] = toJson(*txn);
|
||||
obj["metadata"] = toJson(*meta);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -304,7 +304,7 @@ doBookOffers(
|
||||
ripple::SLE offer{it, obj.key};
|
||||
ripple::uint256 bookDir = offer.getFieldH256(ripple::sfBookDirectory);
|
||||
|
||||
boost::json::object offerJson = getJson(offer);
|
||||
boost::json::object offerJson = toJson(offer);
|
||||
offerJson["quality"] = ripple::amountFromQuality(getQuality(bookDir)).getText();
|
||||
jsonOffers.push_back(offerJson);
|
||||
}
|
||||
|
||||
@@ -37,19 +37,7 @@ doLedger(boost::json::object const& request, BackendInterface const& backend)
|
||||
}
|
||||
else
|
||||
{
|
||||
header["ledger_sequence"] = lgrInfo->seq;
|
||||
header["ledger_hash"] = ripple::strHex(lgrInfo->hash);
|
||||
header["txns_hash"] = ripple::strHex(lgrInfo->txHash);
|
||||
header["state_hash"] = ripple::strHex(lgrInfo->accountHash);
|
||||
header["parent_hash"] = ripple::strHex(lgrInfo->parentHash);
|
||||
header["total_coins"] = ripple::to_string(lgrInfo->drops);
|
||||
header["close_flags"] = lgrInfo->closeFlags;
|
||||
|
||||
// Always show fields that contribute to the ledger hash
|
||||
header["parent_close_time"] =
|
||||
lgrInfo->parentCloseTime.time_since_epoch().count();
|
||||
header["close_time"] = lgrInfo->closeTime.time_since_epoch().count();
|
||||
header["close_time_resolution"] = lgrInfo->closeTimeResolution.count();
|
||||
header = toJson(*lgrInfo);
|
||||
}
|
||||
response["header"] = header;
|
||||
if (getTransactions)
|
||||
@@ -70,8 +58,8 @@ doLedger(boost::json::object const& request, BackendInterface const& backend)
|
||||
if (!binary)
|
||||
{
|
||||
auto [sttx, meta] = deserializeTxPlusMeta(obj);
|
||||
entry["transaction"] = getJson(*sttx);
|
||||
entry["metadata"] = getJson(*meta);
|
||||
entry["transaction"] = toJson(*sttx);
|
||||
entry["metadata"] = toJson(*meta);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -59,7 +59,12 @@ doLedgerData(
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << __func__ << " : parsing cursor";
|
||||
cursor = ripple::uint256{};
|
||||
cursor->parseHex(request.at("cursor").as_string().c_str());
|
||||
if (!cursor->parseHex(request.at("cursor").as_string().c_str()))
|
||||
{
|
||||
response["error"] = "Invalid cursor";
|
||||
response["request"] = request;
|
||||
return response;
|
||||
}
|
||||
}
|
||||
bool binary =
|
||||
request.contains("binary") ? request.at("binary").as_bool() : false;
|
||||
@@ -91,7 +96,7 @@ doLedgerData(
|
||||
objects.push_back(entry);
|
||||
}
|
||||
else
|
||||
objects.push_back(getJson(sle));
|
||||
objects.push_back(toJson(sle));
|
||||
}
|
||||
response["objects"] = objects;
|
||||
if (returnedCursor)
|
||||
|
||||
@@ -47,7 +47,7 @@ doLedgerEntry(
|
||||
{
|
||||
ripple::STLedgerEntry sle{
|
||||
ripple::SerialIter{dbResponse->data(), dbResponse->size()}, key};
|
||||
response["object"] = getJson(sle);
|
||||
response["object"] = toJson(sle);
|
||||
}
|
||||
|
||||
return response;
|
||||
|
||||
@@ -42,7 +42,7 @@ deserializeTxPlusMeta(Backend::TransactionAndMetadata const& blobs)
|
||||
}
|
||||
|
||||
boost::json::object
|
||||
getJson(ripple::STBase const& obj)
|
||||
toJson(ripple::STBase const& obj)
|
||||
{
|
||||
auto start = std::chrono::system_clock::now();
|
||||
boost::json::value value = boost::json::parse(
|
||||
@@ -55,7 +55,7 @@ getJson(ripple::STBase const& obj)
|
||||
}
|
||||
|
||||
boost::json::object
|
||||
getJson(ripple::SLE const& sle)
|
||||
toJson(ripple::SLE const& sle)
|
||||
{
|
||||
auto start = std::chrono::system_clock::now();
|
||||
boost::json::value value = boost::json::parse(
|
||||
@@ -66,6 +66,27 @@ getJson(ripple::SLE const& sle)
|
||||
.count();
|
||||
return value.as_object();
|
||||
}
|
||||
|
||||
boost::json::object
|
||||
toJson(ripple::LedgerInfo const& lgrInfo)
|
||||
{
|
||||
boost::json::object header;
|
||||
header["ledger_sequence"] = lgrInfo.seq;
|
||||
header["ledger_hash"] = ripple::strHex(lgrInfo.hash);
|
||||
header["txns_hash"] = ripple::strHex(lgrInfo.txHash);
|
||||
header["state_hash"] = ripple::strHex(lgrInfo.accountHash);
|
||||
header["parent_hash"] = ripple::strHex(lgrInfo.parentHash);
|
||||
header["total_coins"] = ripple::to_string(lgrInfo.drops);
|
||||
header["close_flags"] = lgrInfo.closeFlags;
|
||||
|
||||
// Always show fields that contribute to the ledger hash
|
||||
header["parent_close_time"] =
|
||||
lgrInfo.parentCloseTime.time_since_epoch().count();
|
||||
header["close_time"] = lgrInfo.closeTime.time_since_epoch().count();
|
||||
header["close_time_resolution"] = lgrInfo.closeTimeResolution.count();
|
||||
return header;
|
||||
}
|
||||
|
||||
std::optional<uint32_t>
|
||||
ledgerSequenceFromRequest(
|
||||
boost::json::object const& request,
|
||||
|
||||
@@ -15,15 +15,19 @@ std::pair<
|
||||
deserializeTxPlusMeta(Backend::TransactionAndMetadata const& blobs);
|
||||
|
||||
boost::json::object
|
||||
getJson(ripple::STBase const& obj);
|
||||
toJson(ripple::STBase const& obj);
|
||||
|
||||
boost::json::object
|
||||
getJson(ripple::SLE const& sle);
|
||||
toJson(ripple::SLE const& sle);
|
||||
|
||||
boost::json::object
|
||||
toJson(ripple::LedgerInfo const& info);
|
||||
|
||||
std::optional<uint32_t>
|
||||
ledgerSequenceFromRequest(
|
||||
boost::json::object const& request,
|
||||
BackendInterface const& backend);
|
||||
|
||||
std::vector<unsigned char>
|
||||
ledgerInfoToBlob(ripple::LedgerInfo const& info);
|
||||
|
||||
|
||||
54
handlers/ServerInfo.cpp
Normal file
54
handlers/ServerInfo.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#include <handlers/RPCHelpers.h>
|
||||
#include <reporting/BackendInterface.h>
|
||||
boost::json::object
|
||||
doServerInfo(
|
||||
boost::json::object const& request,
|
||||
BackendInterface const& backend)
|
||||
{
|
||||
boost::json::object response;
|
||||
|
||||
auto rng = backend.fetchLedgerRange();
|
||||
if (!rng)
|
||||
{
|
||||
response["complete_ledgers"] = "empty";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string completeLedgers = std::to_string(rng->minSequence);
|
||||
if (rng->maxSequence != rng->minSequence)
|
||||
completeLedgers += "-" + std::to_string(rng->maxSequence);
|
||||
response["complete_ledgers"] = completeLedgers;
|
||||
}
|
||||
if (rng)
|
||||
{
|
||||
auto lgrInfo = backend.fetchLedgerBySequence(rng->maxSequence);
|
||||
response["validated_ledger"] = toJson(*lgrInfo);
|
||||
}
|
||||
|
||||
boost::json::array indexes;
|
||||
|
||||
if (rng)
|
||||
{
|
||||
uint32_t cur = rng->minSequence;
|
||||
while (cur <= rng->maxSequence + 1)
|
||||
{
|
||||
auto keyIndex = backend.getKeyIndexOfSeq(cur);
|
||||
assert(keyIndex.has_value());
|
||||
cur = keyIndex->keyIndex;
|
||||
auto page = backend.fetchLedgerPage({}, cur, 1);
|
||||
boost::json::object entry;
|
||||
entry["complete"] = page.warning.has_value();
|
||||
entry["sequence"] = cur;
|
||||
indexes.emplace_back(entry);
|
||||
cur = cur + 1;
|
||||
}
|
||||
}
|
||||
response["indexes"] = indexes;
|
||||
auto indexing = backend.getIndexer().getCurrentlyIndexing();
|
||||
if (indexing)
|
||||
response["indexing"] = *indexing;
|
||||
else
|
||||
response["indexing"] = "none";
|
||||
|
||||
return response;
|
||||
}
|
||||
@@ -63,8 +63,8 @@ doTx(boost::json::object const& request, BackendInterface const& backend)
|
||||
if (!binary)
|
||||
{
|
||||
auto [sttx, meta] = deserializeTxPlusMeta(dbResponse.value());
|
||||
response["transaction"] = getJson(*sttx);
|
||||
response["metadata"] = getJson(*meta);
|
||||
response["transaction"] = toJson(*sttx);
|
||||
response["metadata"] = toJson(*meta);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -10,7 +10,6 @@ BackendIndexer::BackendIndexer(boost::json::object const& config)
|
||||
};
|
||||
BackendIndexer::~BackendIndexer()
|
||||
{
|
||||
std::unique_lock lck(mutex_);
|
||||
work_.reset();
|
||||
ioThread_.join();
|
||||
}
|
||||
@@ -114,6 +113,21 @@ BackendIndexer::writeKeyFlagLedgerAsync(
|
||||
{
|
||||
try
|
||||
{
|
||||
{
|
||||
auto page =
|
||||
backend.fetchLedgerPage({}, nextFlag.keyIndex, 1);
|
||||
if (!page.warning)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< "writeKeyFlagLedger - "
|
||||
<< "flag ledger already written. flag = "
|
||||
<< std::to_string(nextFlag.keyIndex)
|
||||
<< " , ledger sequence = "
|
||||
<< std::to_string(ledgerSequence);
|
||||
return;
|
||||
}
|
||||
}
|
||||
indexing_ = nextFlag.keyIndex;
|
||||
auto start = std::chrono::system_clock::now();
|
||||
auto [objects, curCursor, warning] =
|
||||
backend.fetchLedgerPage(cursor, ledgerSequence, 2048);
|
||||
@@ -121,8 +135,6 @@ BackendIndexer::writeKeyFlagLedgerAsync(
|
||||
// no cursor means this is the first page
|
||||
if (!cursor)
|
||||
{
|
||||
// if there is no warning, we don't need to do a repair
|
||||
// warning only shows up on the first page
|
||||
if (warning)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
@@ -176,6 +188,7 @@ BackendIndexer::writeKeyFlagLedgerAsync(
|
||||
<< std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
end - begin)
|
||||
.count();
|
||||
indexing_ = 0;
|
||||
});
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< __func__
|
||||
@@ -207,6 +220,10 @@ BackendIndexer::finish(uint32_t ledgerSequence, BackendInterface const& backend)
|
||||
// write completion record
|
||||
ripple::uint256 zero = {};
|
||||
backend.writeKeys({zero}, keyIndex);
|
||||
// write next flag sychronously
|
||||
keyIndex = getKeyIndexOfSeq(ledgerSequence + 1);
|
||||
backend.writeKeys(keys, keyIndex);
|
||||
backend.writeKeys({zero}, keyIndex);
|
||||
}
|
||||
isFirst_ = false;
|
||||
keys = {};
|
||||
|
||||
@@ -84,6 +84,8 @@ class BackendIndexer
|
||||
std::optional<boost::asio::io_context::work> work_;
|
||||
std::thread ioThread_;
|
||||
|
||||
std::atomic_uint32_t indexing_ = 0;
|
||||
|
||||
uint32_t keyShift_ = 20;
|
||||
std::unordered_set<ripple::uint256> keys;
|
||||
|
||||
@@ -115,6 +117,14 @@ public:
|
||||
{
|
||||
return keyShift_;
|
||||
}
|
||||
std::optional<uint32_t>
|
||||
getCurrentlyIndexing()
|
||||
{
|
||||
uint32_t cur = indexing_.load();
|
||||
if (cur != 0)
|
||||
return cur;
|
||||
return {};
|
||||
}
|
||||
KeyIndex
|
||||
getKeyIndexOfSeq(uint32_t seq) const
|
||||
{
|
||||
@@ -171,16 +181,18 @@ public:
|
||||
if (commitRes)
|
||||
{
|
||||
if (isFirst_)
|
||||
{
|
||||
auto rng = fetchLedgerRangeNoThrow();
|
||||
if (rng && rng->minSequence != ledgerSequence)
|
||||
isFirst_ = false;
|
||||
indexer_.doKeysRepairAsync(*this, ledgerSequence);
|
||||
}
|
||||
if (indexer_.isKeyFlagLedger(ledgerSequence) || isFirst_)
|
||||
if (indexer_.isKeyFlagLedger(ledgerSequence))
|
||||
indexer_.writeKeyFlagLedgerAsync(ledgerSequence, *this);
|
||||
isFirst_ = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if commitRes is false, we are relinquishing control of ETL. We
|
||||
// reset isFirst_ to true so that way if we later regain control of
|
||||
// ETL, we trigger the index repair
|
||||
isFirst_ = true;
|
||||
}
|
||||
return commitRes;
|
||||
}
|
||||
|
||||
|
||||
@@ -212,6 +212,24 @@ public:
|
||||
", grpc port : " + grpcPort_ + " }";
|
||||
}
|
||||
|
||||
boost::json::object
|
||||
toJson() const
|
||||
{
|
||||
boost::json::object res;
|
||||
res["validated_range"] = getValidatedRange();
|
||||
res["is_connected"] = std::to_string(isConnected());
|
||||
res["ip"] = ip_;
|
||||
res["ws_port"] = wsPort_;
|
||||
res["grpc_port"] = grpcPort_;
|
||||
auto last = getLastMsgTime();
|
||||
if (last.time_since_epoch().count() != 0)
|
||||
res["last_msg_arrival_time"] = std::to_string(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now() - getLastMsgTime())
|
||||
.count());
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Download a ledger in full
|
||||
/// @param ledgerSequence sequence of the ledger to download
|
||||
/// @param writeQueue queue to push downloaded ledger objects
|
||||
@@ -341,16 +359,16 @@ public:
|
||||
// forwarded. return true;
|
||||
// }
|
||||
|
||||
// Json::Value
|
||||
// toJson() const
|
||||
// {
|
||||
// Json::Value ret(Json::arrayValue);
|
||||
// for (auto& src : sources_)
|
||||
// {
|
||||
// ret.append(src->toJson());
|
||||
// }
|
||||
// return ret;
|
||||
// }
|
||||
boost::json::array
|
||||
toJson() const
|
||||
{
|
||||
boost::json::array ret;
|
||||
for (auto& src : sources_)
|
||||
{
|
||||
ret.emplace_back(src->toJson());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
//
|
||||
// /// Randomly select a p2p node to forward a gRPC request to
|
||||
// /// @return gRPC stub to forward requests to p2p node
|
||||
|
||||
@@ -190,6 +190,13 @@ ReportingETL::publishLedger(uint32_t ledgerSequence, uint32_t maxAttempts)
|
||||
++numAttempts;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto lgr =
|
||||
flatMapBackend_->fetchLedgerBySequence(ledgerSequence);
|
||||
assert(lgr);
|
||||
publishLedger(*lgr);
|
||||
}
|
||||
}
|
||||
catch (Backend::DatabaseTimeout const& e)
|
||||
{
|
||||
|
||||
@@ -279,21 +279,23 @@ public:
|
||||
return numMarkers_;
|
||||
}
|
||||
|
||||
/*
|
||||
Json::Value
|
||||
boost::json::object
|
||||
getInfo()
|
||||
{
|
||||
Json::Value result(Json::objectValue);
|
||||
boost::json::object result;
|
||||
|
||||
result["etl_sources"] = loadBalancer_.toJson();
|
||||
result["is_writer"] = writing_.load();
|
||||
result["read_only"] = readOnly_;
|
||||
auto last = getLastPublish();
|
||||
if (last.time_since_epoch().count() != 0)
|
||||
result["last_publish_time"] = to_string(
|
||||
date::floor<std::chrono::microseconds>(getLastPublish()));
|
||||
result["last_publish_time"] = std::to_string(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now() - getLastPublish())
|
||||
.count());
|
||||
|
||||
return result;
|
||||
}
|
||||
*/
|
||||
|
||||
/// start all of the necessary components and begin ETL
|
||||
void
|
||||
|
||||
@@ -44,7 +44,8 @@ enum RPCCommand {
|
||||
ledger_data,
|
||||
book_offers,
|
||||
ledger_range,
|
||||
ledger_entry
|
||||
ledger_entry,
|
||||
server_info
|
||||
};
|
||||
std::unordered_map<std::string, RPCCommand> commandMap{
|
||||
{"tx", tx},
|
||||
@@ -54,7 +55,8 @@ std::unordered_map<std::string, RPCCommand> commandMap{
|
||||
{"ledger_entry", ledger_entry},
|
||||
{"account_info", account_info},
|
||||
{"ledger_data", ledger_data},
|
||||
{"book_offers", book_offers}};
|
||||
{"book_offers", book_offers},
|
||||
{"server_info", server_info}};
|
||||
|
||||
boost::json::object
|
||||
doAccountInfo(
|
||||
@@ -84,6 +86,10 @@ boost::json::object
|
||||
doLedgerRange(
|
||||
boost::json::object const& request,
|
||||
BackendInterface const& backend);
|
||||
boost::json::object
|
||||
doServerInfo(
|
||||
boost::json::object const& request,
|
||||
BackendInterface const& backend);
|
||||
|
||||
std::pair<boost::json::object, uint32_t>
|
||||
buildResponse(
|
||||
@@ -125,6 +131,10 @@ buildResponse(
|
||||
return {res, 1};
|
||||
}
|
||||
break;
|
||||
case server_info: {
|
||||
return {doServerInfo(request, backend), 1};
|
||||
break;
|
||||
}
|
||||
case account_info:
|
||||
return {doAccountInfo(request, backend), 1};
|
||||
break;
|
||||
@@ -170,10 +180,10 @@ public:
|
||||
void
|
||||
run()
|
||||
{
|
||||
// We need to be executing within a strand to perform async operations
|
||||
// on the I/O objects in this session. Although not strictly necessary
|
||||
// for single-threaded contexts, this example code is written to be
|
||||
// thread-safe by default.
|
||||
// We need to be executing within a strand to perform async
|
||||
// operations on the I/O objects in this session. Although not
|
||||
// strictly necessary for single-threaded contexts, this example
|
||||
// code is written to be thread-safe by default.
|
||||
boost::asio::dispatch(
|
||||
ws_.get_executor(),
|
||||
boost::beast::bind_front_handler(
|
||||
|
||||
Reference in New Issue
Block a user