diff --git a/API-VERSION-3.md b/API-VERSION-3.md index dc0186768f..7163fd23b8 100644 --- a/API-VERSION-3.md +++ b/API-VERSION-3.md @@ -8,10 +8,10 @@ For info about how [API versioning](https://xrpl.org/request-formatting.html#api ### Modifications to `tx` and `account_tx` -In API versions 1 and 2, the `tx_json` field in `tx` and `account_tx` responses includes server-added lower-case fields (`date`, `ledger_index`, and `ctid`) that are not part of the canonical signed transaction. In API version 3, these fields are removed from `tx_json` and are only present at the top-level `result` object. +In API version 2, the `tx_json` field in `tx` and `account_tx` responses includes server-added lower-case fields (`date`, `ledger_index`, and `ctid`) that are not part of the canonical signed transaction. In API version 3, these fields are removed from `tx_json` and are only present at the top-level result object. -- **Before (API v1 and v2)**: The `tx_json` object in the response contained `date`, `ledger_index`, and `ctid` fields alongside the canonical PascalCase transaction fields. -- **After (API v3)**: The `tx_json` object contains only the canonical signed transaction fields. The `date`, `ledger_index`, and `ctid` fields appear exclusively at the top-level `result` object. +- **Before (API v2)**: The `tx_json` object in the response contained `date`, `ledger_index`, and `ctid` fields alongside the canonical PascalCase transaction fields. +- **After (API v3)**: The `tx_json` object contains only the canonical signed transaction fields. The `date`, `ledger_index`, and `ctid` fields appear exclusively at the top-level result object. ### Modifications to `amm_info` diff --git a/src/test/rpc/AccountTx_test.cpp b/src/test/rpc/AccountTx_test.cpp index 8783697242..ab8644e61f 100644 --- a/src/test/rpc/AccountTx_test.cpp +++ b/src/test/rpc/AccountTx_test.cpp @@ -130,7 +130,7 @@ class AccountTx_test : public beast::unit_test::suite if (apiVersion >= 3) { // In API v3, server-added lower-case fields must - // not be in tx_json + // not be in tx_json, but must be at result level return (payment.isMember(jss::tx_json)) && (payment[jss::tx_json][jss::TransactionType] == jss::Payment) && (payment[jss::tx_json][jss::DeliverMax] == "10000000010") && @@ -139,6 +139,9 @@ class AccountTx_test : public beast::unit_test::suite (!payment[jss::tx_json].isMember(jss::date)) && (!payment[jss::tx_json].isMember(jss::ledger_index)) && (!payment[jss::tx_json].isMember(jss::ctid)) && + // date and ctid must be at the transaction + // object level (outside tx_json) in API v3 + (payment.isMember(jss::date)) && (payment.isMember(jss::ctid)) && (payment[jss::hash] == "9F3085D85F472D1CC29627F260DF68EDE59D42D1D0C33E345" "ECF0D4CE981D0A8") && diff --git a/src/test/rpc/Transaction_test.cpp b/src/test/rpc/Transaction_test.cpp index 128493a92e..58faa177f4 100644 --- a/src/test/rpc/Transaction_test.cpp +++ b/src/test/rpc/Transaction_test.cpp @@ -784,6 +784,8 @@ class Transaction_test : public beast::unit_test::suite BEAST_EXPECT(!tx_json.isMember(jss::date)); BEAST_EXPECT(!tx_json.isMember(jss::ledger_index)); BEAST_EXPECT(!tx_json.isMember(jss::ctid)); + // date must be at result level in API v3 + BEAST_EXPECT(result[jss::result].isMember(jss::date)); } else { diff --git a/src/xrpld/rpc/handlers/account/AccountTx.cpp b/src/xrpld/rpc/handlers/account/AccountTx.cpp index 7f2feb7f81..ea6e3040a7 100644 --- a/src/xrpld/rpc/handlers/account/AccountTx.cpp +++ b/src/xrpld/rpc/handlers/account/AccountTx.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -11,6 +12,7 @@ #include #include +#include #include #include #include @@ -290,10 +292,8 @@ populateJsonResponse( if (context.apiVersion > 1) { auto const opts = context.apiVersion >= 3 - ? JsonOptions::disable_API_prior_V2 | - JsonOptions::disable_API_prior_V3 - : JsonOptions::include_date | - JsonOptions::disable_API_prior_V2; + ? JsonOptions::disable_API_prior_V2 | JsonOptions::disable_API_prior_V3 + : JsonOptions::include_date | JsonOptions::disable_API_prior_V2; jvObj[json_tx] = txn->getJson(opts, false); jvObj[jss::hash] = to_string(txn->getID()); jvObj[jss::ledger_index] = txn->getLedger(); @@ -302,7 +302,20 @@ populateJsonResponse( if (auto closeTime = context.ledgerMaster.getCloseTimeBySeq(txn->getLedger())) + { jvObj[jss::close_time_iso] = to_string_iso(*closeTime); + if (context.apiVersion >= 3) + jvObj[jss::date] = closeTime->time_since_epoch().count(); + } + + if (context.apiVersion >= 3 && txnMeta) + { + uint32_t const lgrSeq = txn->getLedger(); + uint32_t const txnIdx = txnMeta->getIndex(); + uint32_t const netID = context.app.getNetworkIDService().getNetworkID(); + if (auto const ctid = RPC::encodeCTID(lgrSeq, txnIdx, netID)) + jvObj[jss::ctid] = *ctid; + } } else { diff --git a/src/xrpld/rpc/handlers/transaction/Tx.cpp b/src/xrpld/rpc/handlers/transaction/Tx.cpp index 56c55a3e0d..b96ff348eb 100644 --- a/src/xrpld/rpc/handlers/transaction/Tx.cpp +++ b/src/xrpld/rpc/handlers/transaction/Tx.cpp @@ -219,7 +219,11 @@ populateJsonResponse( { response[jss::ledger_index] = result.txn->getLedger(); if (result.closeTime) + { response[jss::close_time_iso] = to_string_iso(*result.closeTime); + if (context.apiVersion >= 3) + response[jss::date] = result.closeTime->time_since_epoch().count(); + } } } else