diff --git a/src/etl/ETLState.cpp b/src/etl/ETLState.cpp index cd0bbe0f8..f0ca5cd9f 100644 --- a/src/etl/ETLState.cpp +++ b/src/etl/ETLState.cpp @@ -40,7 +40,7 @@ tag_invoke(boost::json::value_to_tag, boost::json::value const& jv) if (jsonObject.contains(JS(result)) && jsonObject.at(JS(result)).as_object().contains(JS(info))) { auto const rippledInfo = jsonObject.at(JS(result)).as_object().at(JS(info)).as_object(); if (rippledInfo.contains(JS(network_id))) - state.networkID.emplace(boost::json::value_to(rippledInfo.at(JS(network_id)))); + state.networkID = boost::json::value_to(rippledInfo.at(JS(network_id))); } return state; diff --git a/src/etl/ETLState.hpp b/src/etl/ETLState.hpp index 0302aab5f..3b447d985 100644 --- a/src/etl/ETLState.hpp +++ b/src/etl/ETLState.hpp @@ -38,7 +38,12 @@ namespace etl { * @brief This class is responsible for fetching and storing the state of the ETL information, such as the network id */ struct ETLState { - std::optional networkID; + /* + * NOTE: Rippled NetworkID: Mainnet = 0; Testnet = 1; Devnet = 2 + * However, if rippled is running on neither of these (ie. standalone mode) rippled will default to 0, but + * is not included in the stateOpt response. Must manually add it here. + */ + uint32_t networkID{0}; /** * @brief Fetch the ETL state from the rippled server diff --git a/src/etl/LoadBalancer.cpp b/src/etl/LoadBalancer.cpp index a26c5bb58..fa0010a0b 100644 --- a/src/etl/LoadBalancer.cpp +++ b/src/etl/LoadBalancer.cpp @@ -142,12 +142,11 @@ LoadBalancer::LoadBalancer( if (!stateOpt) { LOG(log_.warn()) << "Failed to fetch ETL state from source = " << source->toString() << " Please check the configuration and network"; - } else if (etlState_ && etlState_->networkID && stateOpt->networkID && - etlState_->networkID != stateOpt->networkID) { + } else if (etlState_ && etlState_->networkID != stateOpt->networkID) { checkOnETLFailure(fmt::format( "ETL sources must be on the same network. Source network id = {} does not match others network id = {}", - *(stateOpt->networkID), - *(etlState_->networkID) + stateOpt->networkID, + etlState_->networkID )); } else { etlState_ = stateOpt; diff --git a/src/etlng/LoadBalancer.cpp b/src/etlng/LoadBalancer.cpp index fa34af78e..d6b96c7f9 100644 --- a/src/etlng/LoadBalancer.cpp +++ b/src/etlng/LoadBalancer.cpp @@ -142,12 +142,11 @@ LoadBalancer::LoadBalancer( if (!stateOpt) { LOG(log_.warn()) << "Failed to fetch ETL state from source = " << source->toString() << " Please check the configuration and network"; - } else if (etlState_ && etlState_->networkID && stateOpt->networkID && - etlState_->networkID != stateOpt->networkID) { + } else if (etlState_ && etlState_->networkID != stateOpt->networkID) { checkOnETLFailure(fmt::format( "ETL sources must be on the same network. Source network id = {} does not match others network id = {}", - *(stateOpt->networkID), - *(etlState_->networkID) + stateOpt->networkID, + etlState_->networkID )); } else { etlState_ = stateOpt; diff --git a/src/rpc/handlers/Tx.hpp b/src/rpc/handlers/Tx.hpp index acc5de4a3..fe15929c9 100644 --- a/src/rpc/handlers/Tx.hpp +++ b/src/rpc/handlers/Tx.hpp @@ -21,7 +21,6 @@ #include "data/BackendInterface.hpp" #include "data/Types.hpp" -#include "etl/ETLService.hpp" #include "etlng/ETLServiceInterface.hpp" #include "rpc/Errors.hpp" #include "rpc/JS.hpp" @@ -39,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -214,17 +214,15 @@ public: // input.transaction might be not available, get hash via tx object if (txn.contains(JS(hash))) output.hash = txn.at(JS(hash)).as_string(); + } - // append ctid here to mimic rippled 1.12 behavior: return ctid even binary=true - // rippled will change it in the future, ctid should be part of tx json which not available in binary - // mode - auto const txnIdx = boost::json::value_to(meta.at("TransactionIndex")); - if (txnIdx <= 0xFFFFU && dbResponse->ledgerSequence < 0x0FFF'FFFFUL && currentNetId && - *currentNetId <= 0xFFFFU) { - output.ctid = rpc::encodeCTID( - dbResponse->ledgerSequence, static_cast(txnIdx), static_cast(*currentNetId) - ); - } + // append ctid here to mimic rippled behavior + auto const txnIdx = boost::json::value_to(meta.at("TransactionIndex")); + if (txnIdx <= 0xFFFFU && dbResponse->ledgerSequence < 0x0FFF'FFFFUL && currentNetId && + *currentNetId <= 0xFFFFU) { + output.ctid = rpc::encodeCTID( + dbResponse->ledgerSequence, static_cast(txnIdx), static_cast(*currentNetId) + ); } output.date = dbResponse->date; @@ -281,12 +279,10 @@ private: if (output.tx) { obj[JS(tx_json)] = *output.tx; obj[JS(tx_json)].as_object()[JS(date)] = output.date; + if (output.ctid) + obj[JS(tx_json)].as_object()[JS(ctid)] = *output.ctid; + obj[JS(tx_json)].as_object()[JS(ledger_index)] = output.ledgerIndex; - // move ctid from tx_json to root - if (obj[JS(tx_json)].as_object().contains(JS(ctid))) { - obj[JS(ctid)] = obj[JS(tx_json)].as_object()[JS(ctid)]; - obj[JS(tx_json)].as_object().erase(JS(ctid)); - } // move hash from tx_json to root if (obj[JS(tx_json)].as_object().contains(JS(hash))) { obj[JS(hash)] = obj[JS(tx_json)].as_object()[JS(hash)]; diff --git a/tests/unit/etl/ETLStateTests.cpp b/tests/unit/etl/ETLStateTests.cpp index 2d48b6635..7fc046f35 100644 --- a/tests/unit/etl/ETLStateTests.cpp +++ b/tests/unit/etl/ETLStateTests.cpp @@ -58,8 +58,7 @@ TEST_F(ETLStateTest, NetworkIdValid) EXPECT_CALL(source, forwardToRippled).WillOnce(Return(json.as_object())); auto const state = etl::ETLState::fetchETLStateFromSource(source); ASSERT_TRUE(state.has_value()); - ASSERT_TRUE(state->networkID.has_value()); - EXPECT_EQ(state->networkID.value(), 12); + EXPECT_EQ(state->networkID, 12); } TEST_F(ETLStateTest, NetworkIdInvalid) @@ -76,7 +75,7 @@ TEST_F(ETLStateTest, NetworkIdInvalid) EXPECT_CALL(source, forwardToRippled).WillOnce(Return(json.as_object())); auto const state = etl::ETLState::fetchETLStateFromSource(source); ASSERT_TRUE(state.has_value()); - EXPECT_FALSE(state->networkID.has_value()); + EXPECT_NE(state->networkID, 12); } TEST_F(ETLStateTest, ResponseHasError) diff --git a/tests/unit/rpc/ErrorTests.cpp b/tests/unit/rpc/ErrorTests.cpp index 05f039697..81ecbb76b 100644 --- a/tests/unit/rpc/ErrorTests.cpp +++ b/tests/unit/rpc/ErrorTests.cpp @@ -67,6 +67,7 @@ TEST(RPCErrorsTest, StatusAsBool) RippledError::rpcUNKNOWN_COMMAND, RippledError::rpcTOO_BUSY, RippledError::rpcNO_NETWORK, + RippledError::rpcWRONG_NETWORK, RippledError::rpcACT_MALFORMED, RippledError::rpcBAD_MARKET, ClioError::RpcMalformedCurrency, diff --git a/tests/unit/rpc/handlers/TxTests.cpp b/tests/unit/rpc/handlers/TxTests.cpp index fe41646e5..a96695586 100644 --- a/tests/unit/rpc/handlers/TxTests.cpp +++ b/tests/unit/rpc/handlers/TxTests.cpp @@ -71,6 +71,7 @@ constexpr auto kDEFAULT_OUT1 = R"({ "TakerPays": "300", "TransactionType": "OfferCreate", "hash": "2E2FBAAFF767227FE4381C4BE9855986A6B9F96C62F6E443731AB36F7BBB8A08", + "ctid": "C000006400640000", "meta": { "AffectedNodes": [ { @@ -119,8 +120,10 @@ constexpr auto kDEFAULT_OUT2 = R"({ "TransactionIndex": 100, "TransactionResult": "tesSUCCESS" }, + "ctid": "C000006400640000", "tx_json": { "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", + "ctid": "C000006400640000", "date": 123456, "Fee": "2", "ledger_index": 100, @@ -492,7 +495,8 @@ TEST_F(RPCTxTest, ReturnBinary) "date": 123456, "ledger_index": 100, "inLedger": 100, - "validated": true + "validated": true, + "ctid": "C000006400640000" })"; TransactionAndMetadata tx; @@ -578,6 +582,7 @@ TEST_F(RPCTxTest, MintNFT) "SigningPubKey": "74657374", "TransactionType": "NFTokenMint", "hash": "C74463F49CFDCBEF3E9902672719918CDE5042DC7E7660BEBD1D1105C4B6DFF4", + "ctid": "C000006400000000", "meta": {{ "AffectedNodes": [ {{ @@ -829,6 +834,7 @@ TEST_F(RPCTxTest, CTIDNotMatch) ASSERT_FALSE(output); auto const err = rpc::makeError(output.result.error()); + // TODO: https://github.com/XRPLF/clio/issues/2002 EXPECT_EQ(err.at("error_code").as_uint64(), 4); EXPECT_EQ( err.at("error_message").as_string(),