diff --git a/src/ripple/app/ledger/impl/LedgerToJson.cpp b/src/ripple/app/ledger/impl/LedgerToJson.cpp index 81e84303933..42e09107d46 100644 --- a/src/ripple/app/ledger/impl/LedgerToJson.cpp +++ b/src/ripple/app/ledger/impl/LedgerToJson.cpp @@ -17,6 +17,7 @@ */ //============================================================================== +#include #include #include #include @@ -26,6 +27,7 @@ #include #include #include +#include namespace ripple { @@ -153,6 +155,17 @@ fillJsonTx( txn, {txn->getTransactionID(), fill.ledger.seq(), *stMeta}); } + + const bool validated = RPC::isValidated( + fill.context->ledgerMaster, fill.ledger, fill.context->app); + txJson[jss::validated] = validated; + if (validated) + { + if (auto close_time = + fill.context->ledgerMaster.getCloseTimeBySeq( + fill.ledger.seq())) + txJson[jss::close_time_iso] = to_string_iso(*close_time); + } } else { diff --git a/src/ripple/app/misc/NetworkOPs.cpp b/src/ripple/app/misc/NetworkOPs.cpp index be7819b845e..0a7d56d6a67 100644 --- a/src/ripple/app/misc/NetworkOPs.cpp +++ b/src/ripple/app/misc/NetworkOPs.cpp @@ -3125,6 +3125,8 @@ NetworkOPsImp::transJson( jvObj[jss::transaction][jss::date] = ledger->info().closeTime.time_since_epoch().count(); jvObj[jss::validated] = true; + if (auto close_time = m_ledgerMaster.getCloseTimeBySeq(ledger->seq())) + jvObj[jss::close_time_iso] = to_string_iso(*close_time); // WRITEME: Put the account next seq here } @@ -3179,13 +3181,8 @@ NetworkOPsImp::transJson( { jvTx[jss::tx_json] = jvTx.removeMember(jss::transaction); jvTx[jss::hash] = hash; - if (!owner_funds.empty()) - { - if (!jvTx.isMember(jss::meta)) - jvTx[jss::meta] = Json::Value(Json::objectValue); - jvTx[jss::meta][jss::owner_funds] = owner_funds; - } + jvTx[jss::owner_funds] = owner_funds; // TODO set `jvObj[jss::close_time_iso]` if validated } diff --git a/src/ripple/basics/chrono.h b/src/ripple/basics/chrono.h index f50d765d58f..886e1665210 100644 --- a/src/ripple/basics/chrono.h +++ b/src/ripple/basics/chrono.h @@ -74,6 +74,22 @@ to_string(NetClock::time_point tp) system_clock::time_point{tp.time_since_epoch() + 946684800s}); } +template +std::string +to_string_iso(date::sys_time tp) +{ + return date::format("%FT%TZ", tp); +} + +inline std::string +to_string_iso(NetClock::time_point tp) +{ + // 2000-01-01 00:00:00 UTC is 946684800s from 1970-01-01 00:00:00 UTC + using namespace std::chrono; + return to_string_iso( + system_clock::time_point{tp.time_since_epoch() + 946684800s}); +} + /** A clock for measuring elapsed time. The epoch is unspecified. diff --git a/src/ripple/protocol/jss.h b/src/ripple/protocol/jss.h index 281a410127d..8a701defad8 100644 --- a/src/ripple/protocol/jss.h +++ b/src/ripple/protocol/jss.h @@ -230,6 +230,8 @@ JSS(close); // out: BookChanges JSS(close_flags); // out: LedgerToJson JSS(close_time); // in: Application, out: NetworkOPs, // RCLCxPeerPos, LedgerToJson +JSS(close_time_iso); // out: Tx, NetworkOPs, TransactionEntry + // AccountTx, LedgerToJson JSS(close_time_estimated); // in: Application, out: LedgerToJson JSS(close_time_human); // out: LedgerToJson JSS(close_time_offset); // out: NetworkOPs diff --git a/src/ripple/rpc/handlers/AccountTx.cpp b/src/ripple/rpc/handlers/AccountTx.cpp index 2a4ae3648e3..8f130daadf5 100644 --- a/src/ripple/rpc/handlers/AccountTx.cpp +++ b/src/ripple/rpc/handlers/AccountTx.cpp @@ -334,7 +334,6 @@ populateJsonResponse( jvObj[json_tx] = txn->getJson( JsonOptions::include_date, false, {std::ref(hash)}); jvObj[jss::hash] = hash; - // TODO set `jvObj[jss::close_time_iso]` } else jvObj[json_tx] = @@ -351,6 +350,11 @@ populateJsonResponse( insertDeliveredAmount( jvObj[jss::meta], context, txn, *txnMeta); insertNFTSyntheticInJson(jvObj, sttx, *txnMeta); + if (auto closeTime = + context.ledgerMaster.getCloseTimeBySeq( + txnMeta->getIndex())) + jvObj[jss::close_time_iso] = + to_string_iso(*closeTime); } } } diff --git a/src/ripple/rpc/handlers/TransactionEntry.cpp b/src/ripple/rpc/handlers/TransactionEntry.cpp index b7956168848..db826f57d37 100644 --- a/src/ripple/rpc/handlers/TransactionEntry.cpp +++ b/src/ripple/rpc/handlers/TransactionEntry.cpp @@ -17,6 +17,7 @@ */ //============================================================================== +#include #include #include #include @@ -77,7 +78,18 @@ doTransactionEntry(RPC::JsonContext& context) jvResult[jss::tx_json] = sttx->getJson(JsonOptions::none, false, {std::ref(hash)}); jvResult[jss::hash] = hash; - // TODO set `jvResult[jss::close_time_iso]` + + bool const validated = RPC::isValidated( + context.ledgerMaster, *lpLedger, context.app); + + jvResult[jss::validated] = validated; + if (validated) + { + if (auto closeTime = context.ledgerMaster.getCloseTimeBySeq( + lpLedger->seq())) + jvResult[jss::close_time_iso] = + to_string_iso(*closeTime); + } } else jvResult[jss::tx_json] = sttx->getJson(JsonOptions::none); diff --git a/src/ripple/rpc/handlers/Tx.cpp b/src/ripple/rpc/handlers/Tx.cpp index 3769f38a05d..786d82298ac 100644 --- a/src/ripple/rpc/handlers/Tx.cpp +++ b/src/ripple/rpc/handlers/Tx.cpp @@ -55,6 +55,7 @@ struct TxResult std::variant, Blob> meta; bool validated = false; std::optional ctid; + std::optional closeTime; TxSearched searchedAll; }; @@ -140,6 +141,9 @@ doTxPostgres(RPC::Context& context, TxArgs const& args) *(args.hash), res.txn->getLedger(), *meta); } res.validated = true; + // TODO Where to get closeTime from when reading from database ? + // I assume we should not use ladgerMaster here ? + // res.closeTime = return {res, rpcSUCCESS}; } else @@ -269,6 +273,9 @@ doTxHelp(RPC::Context& context, TxArgs args) } result.validated = isValidated( context.ledgerMaster, ledger->info().seq, ledger->info().hash); + if (result.validated) + result.closeTime = + context.ledgerMaster.getCloseTimeBySeq(txn->getLedger()); // compute outgoing CTID uint32_t lgrSeq = ledger->info().seq; @@ -328,7 +335,9 @@ populateJsonResponse( context.apiVersion); } response[jss::hash] = hash; - // TODO set `response[jss::close_time_iso]` + if (result.closeTime) + response[jss::close_time_iso] = + to_string_iso(*result.closeTime); } else {