mirror of
https://github.com/XRPLF/clio.git
synced 2025-11-27 23:25:53 +00:00
Ledger handler
This commit is contained in:
@@ -66,7 +66,8 @@ target_sources(reporting PRIVATE
|
|||||||
handlers/RPCHelpers.cpp
|
handlers/RPCHelpers.cpp
|
||||||
handlers/AccountTx.cpp
|
handlers/AccountTx.cpp
|
||||||
handlers/LedgerData.cpp
|
handlers/LedgerData.cpp
|
||||||
handlers/BookOffers.cpp)
|
handlers/BookOffers.cpp
|
||||||
|
handlers/Ledger.cpp)
|
||||||
|
|
||||||
|
|
||||||
message(${Boost_LIBRARIES})
|
message(${Boost_LIBRARIES})
|
||||||
|
|||||||
53
handlers/Ledger.cpp
Normal file
53
handlers/Ledger.cpp
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#include <handlers/RPCHelpers.h>
|
||||||
|
#include <reporting/BackendInterface.h>
|
||||||
|
|
||||||
|
boost::json::object
|
||||||
|
doLedger(boost::json::object const& request, BackendInterface const& backend)
|
||||||
|
{
|
||||||
|
boost::json::object response;
|
||||||
|
if (!request.contains("ledger_index"))
|
||||||
|
{
|
||||||
|
response["error"] = "Please specify a ledger index";
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
uint32_t ledgerSequence = request.at("ledger_index").as_int64();
|
||||||
|
|
||||||
|
auto lgrInfo = backend.fetchLedgerBySequence(ledgerSequence);
|
||||||
|
if (!lgrInfo)
|
||||||
|
{
|
||||||
|
response["error"] = "ledger not found";
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
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();
|
||||||
|
auto txns = backend.fetchAllTransactionsInLedger(ledgerSequence);
|
||||||
|
response["transactions"] = boost::json::value(boost::json::array_kind);
|
||||||
|
boost::json::array& jsonTransactions =
|
||||||
|
response.at("transactions").as_array();
|
||||||
|
|
||||||
|
std::transform(
|
||||||
|
std::move_iterator(txns.begin()),
|
||||||
|
std::move_iterator(txns.end()),
|
||||||
|
std::back_inserter(jsonTransactions),
|
||||||
|
[](auto obj) {
|
||||||
|
boost::json::object entry;
|
||||||
|
auto [sttx, meta] = deserializeTxPlusMeta(obj);
|
||||||
|
entry["transaction"] = getJson(*sttx);
|
||||||
|
entry["meta"] = getJson(*meta);
|
||||||
|
return entry;
|
||||||
|
});
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
@@ -54,6 +54,9 @@ public:
|
|||||||
virtual std::optional<TransactionAndMetadata>
|
virtual std::optional<TransactionAndMetadata>
|
||||||
fetchTransaction(ripple::uint256 const& hash) const = 0;
|
fetchTransaction(ripple::uint256 const& hash) const = 0;
|
||||||
|
|
||||||
|
virtual std::vector<TransactionAndMetadata>
|
||||||
|
fetchAllTransactionsInLedger(uint32_t ledgerSequence) const = 0;
|
||||||
|
|
||||||
virtual LedgerPage
|
virtual LedgerPage
|
||||||
fetchLedgerPage(
|
fetchLedgerPage(
|
||||||
std::optional<ripple::uint256> const& cursor,
|
std::optional<ripple::uint256> const& cursor,
|
||||||
|
|||||||
@@ -225,6 +225,23 @@ CassandraBackend::fetchLedgerRange() const
|
|||||||
}
|
}
|
||||||
return range;
|
return range;
|
||||||
}
|
}
|
||||||
|
std::vector<TransactionAndMetadata>
|
||||||
|
CassandraBackend::fetchAllTransactionsInLedger(uint32_t ledgerSequence) const
|
||||||
|
{
|
||||||
|
CassandraStatement statement{selectAllTransactionsInLedger_};
|
||||||
|
CassandraResult result = executeSyncRead(statement);
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(error) << __func__ << " - no rows";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
std::vector<TransactionAndMetadata> txns;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
txns.push_back({result.getBytes(), result.getBytes()});
|
||||||
|
} while (result.nextRow());
|
||||||
|
return txns;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CassandraBackend::open()
|
CassandraBackend::open()
|
||||||
@@ -455,12 +472,19 @@ CassandraBackend::open()
|
|||||||
if (!executeSimpleStatement(query.str()))
|
if (!executeSimpleStatement(query.str()))
|
||||||
continue;
|
continue;
|
||||||
query = {};
|
query = {};
|
||||||
query << "CREATE TABLE IF NOT EXISTS " << tablePrefix << "transactions"
|
query
|
||||||
<< " ( hash blob PRIMARY KEY, sequence bigint, transaction "
|
<< "CREATE TABLE IF NOT EXISTS " << tablePrefix << "transactions"
|
||||||
|
<< " ( hash blob PRIMARY KEY, ledger_sequence bigint, transaction "
|
||||||
"blob, metadata blob)";
|
"blob, metadata blob)";
|
||||||
if (!executeSimpleStatement(query.str()))
|
if (!executeSimpleStatement(query.str()))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
query = {};
|
||||||
|
query << "CREATE INDEX ON " << tablePrefix
|
||||||
|
<< "transactions(ledger_sequence)";
|
||||||
|
if (!executeSimpleStatement(query.str()))
|
||||||
|
continue;
|
||||||
|
|
||||||
query = {};
|
query = {};
|
||||||
query << "SELECT * FROM " << tablePrefix << "transactions"
|
query << "SELECT * FROM " << tablePrefix << "transactions"
|
||||||
<< " LIMIT 1";
|
<< " LIMIT 1";
|
||||||
@@ -558,8 +582,9 @@ CassandraBackend::open()
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
query = {};
|
query = {};
|
||||||
query << "INSERT INTO " << tablePrefix << "transactions"
|
query
|
||||||
<< " (hash, sequence, transaction, metadata) VALUES (?, ?, "
|
<< "INSERT INTO " << tablePrefix << "transactions"
|
||||||
|
<< " (hash, ledger_sequence, transaction, metadata) VALUES (?, ?, "
|
||||||
"?, ?)";
|
"?, ?)";
|
||||||
if (!insertTransaction_.prepareStatement(query, session_.get()))
|
if (!insertTransaction_.prepareStatement(query, session_.get()))
|
||||||
continue;
|
continue;
|
||||||
@@ -602,6 +627,14 @@ CassandraBackend::open()
|
|||||||
if (!selectTransaction_.prepareStatement(query, session_.get()))
|
if (!selectTransaction_.prepareStatement(query, session_.get()))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
query = {};
|
||||||
|
query << "SELECT transaction,metadata FROM " << tablePrefix
|
||||||
|
<< "transactions"
|
||||||
|
<< " WHERE ledger_sequence = ?";
|
||||||
|
if (!selectAllTransactionsInLedger_.prepareStatement(
|
||||||
|
query, session_.get()))
|
||||||
|
continue;
|
||||||
|
|
||||||
query = {};
|
query = {};
|
||||||
query << "SELECT key FROM " << tablePrefix << "keys "
|
query << "SELECT key FROM " << tablePrefix << "keys "
|
||||||
<< " WHERE TOKEN(key) >= ? and created <= ?"
|
<< " WHERE TOKEN(key) >= ? and created <= ?"
|
||||||
|
|||||||
@@ -503,6 +503,7 @@ private:
|
|||||||
CassandraPreparedStatement insertObject_;
|
CassandraPreparedStatement insertObject_;
|
||||||
CassandraPreparedStatement insertTransaction_;
|
CassandraPreparedStatement insertTransaction_;
|
||||||
CassandraPreparedStatement selectTransaction_;
|
CassandraPreparedStatement selectTransaction_;
|
||||||
|
CassandraPreparedStatement selectAllTransactionsInLedger_;
|
||||||
CassandraPreparedStatement selectObject_;
|
CassandraPreparedStatement selectObject_;
|
||||||
CassandraPreparedStatement selectLedgerPageKeys_;
|
CassandraPreparedStatement selectLedgerPageKeys_;
|
||||||
CassandraPreparedStatement selectLedgerPage_;
|
CassandraPreparedStatement selectLedgerPage_;
|
||||||
@@ -760,6 +761,9 @@ public:
|
|||||||
std::optional<LedgerRange>
|
std::optional<LedgerRange>
|
||||||
fetchLedgerRange() const override;
|
fetchLedgerRange() const override;
|
||||||
|
|
||||||
|
std::vector<TransactionAndMetadata>
|
||||||
|
fetchAllTransactionsInLedger(uint32_t ledgerSequence) const override;
|
||||||
|
|
||||||
// Synchronously fetch the object with key key and store the result in
|
// Synchronously fetch the object with key key and store the result in
|
||||||
// pno
|
// pno
|
||||||
// @param key the key of the object
|
// @param key the key of the object
|
||||||
|
|||||||
@@ -761,6 +761,9 @@ CREATE TABLE IF NOT EXISTS transactions (
|
|||||||
transaction bytea NOT NULL,
|
transaction bytea NOT NULL,
|
||||||
metadata bytea NOT NULL
|
metadata bytea NOT NULL
|
||||||
);
|
);
|
||||||
|
-- Index for lookups by ledger hash.
|
||||||
|
CREATE INDEX IF NOT EXISTS ledgers_ledger_seq_idx ON transactions
|
||||||
|
USING hash (ledger_seq);
|
||||||
|
|
||||||
-- Table that maps accounts to transactions affecting them. Deletes from the
|
-- Table that maps accounts to transactions affecting them. Deletes from the
|
||||||
-- ledger table cascade here based on ledger_seq.
|
-- ledger table cascade here based on ledger_seq.
|
||||||
|
|||||||
@@ -285,6 +285,31 @@ PostgresBackend::fetchTransaction(ripple::uint256 const& hash) const
|
|||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
std::vector<TransactionAndMetadata>
|
||||||
|
PostgresBackend::fetchAllTransactionsInLedger(uint32_t ledgerSequence) const
|
||||||
|
{
|
||||||
|
PgQuery pgQuery(pgPool_);
|
||||||
|
std::stringstream sql;
|
||||||
|
sql << "SELECT transaction, metadata, ledger_seq FROM transactions WHERE "
|
||||||
|
<< "ledger_seq = " << std::to_string(ledgerSequence);
|
||||||
|
auto res = pgQuery(sql.str().data());
|
||||||
|
if (size_t numRows = checkResult(res, 3))
|
||||||
|
{
|
||||||
|
std::vector<TransactionAndMetadata> txns;
|
||||||
|
for (size_t i = 0; i < numRows; ++i)
|
||||||
|
{
|
||||||
|
char const* txn = res.c_str(0, 0);
|
||||||
|
char const* metadata = res.c_str(0, 1);
|
||||||
|
std::string_view txnView{txn};
|
||||||
|
std::string_view metadataView{metadata};
|
||||||
|
txns.push_back(
|
||||||
|
{{txnView.front(), txnView.back()},
|
||||||
|
{metadataView.front(), metadataView.back()}});
|
||||||
|
}
|
||||||
|
return txns;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
LedgerPage
|
LedgerPage
|
||||||
PostgresBackend::fetchLedgerPage(
|
PostgresBackend::fetchLedgerPage(
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ public:
|
|||||||
std::optional<TransactionAndMetadata>
|
std::optional<TransactionAndMetadata>
|
||||||
fetchTransaction(ripple::uint256 const& hash) const override;
|
fetchTransaction(ripple::uint256 const& hash) const override;
|
||||||
|
|
||||||
|
std::vector<TransactionAndMetadata>
|
||||||
|
fetchAllTransactionsInLedger(uint32_t ledgerSequence) const override;
|
||||||
|
|
||||||
LedgerPage
|
LedgerPage
|
||||||
fetchLedgerPage(
|
fetchLedgerPage(
|
||||||
std::optional<ripple::uint256> const& cursor,
|
std::optional<ripple::uint256> const& cursor,
|
||||||
|
|||||||
@@ -69,6 +69,8 @@ boost::json::object
|
|||||||
doBookOffers(
|
doBookOffers(
|
||||||
boost::json::object const& request,
|
boost::json::object const& request,
|
||||||
BackendInterface const& backend);
|
BackendInterface const& backend);
|
||||||
|
boost::json::object
|
||||||
|
doLedger(boost::json::object const& request, BackendInterface const& backend);
|
||||||
|
|
||||||
boost::json::object
|
boost::json::object
|
||||||
buildResponse(
|
buildResponse(
|
||||||
@@ -87,6 +89,7 @@ buildResponse(
|
|||||||
return doAccountTx(request, backend);
|
return doAccountTx(request, backend);
|
||||||
break;
|
break;
|
||||||
case ledger:
|
case ledger:
|
||||||
|
return doLedgerData(request, backend);
|
||||||
break;
|
break;
|
||||||
case ledger_data:
|
case ledger_data:
|
||||||
return doLedgerData(request, backend);
|
return doLedgerData(request, backend);
|
||||||
|
|||||||
Reference in New Issue
Block a user