diff --git a/src/libxrpl/protocol/STInteger.cpp b/src/libxrpl/protocol/STInteger.cpp index a90e21491c..470ad0b666 100644 --- a/src/libxrpl/protocol/STInteger.cpp +++ b/src/libxrpl/protocol/STInteger.cpp @@ -62,8 +62,10 @@ STUInt8::getText() const if (transResultInfo(TER::fromInt(value_), token, human)) return human; + // LCOV_EXCL_START JLOG(debugLog().error()) << "Unknown result code in metadata: " << value_; + // LCOV_EXCL_STOP } return std::to_string(value_); @@ -80,8 +82,10 @@ STUInt8::getJson(JsonOptions) const if (transResultInfo(TER::fromInt(value_), token, human)) return token; + // LCOV_EXCL_START JLOG(debugLog().error()) << "Unknown result code in metadata: " << value_; + // LCOV_EXCL_STOP } return value_; @@ -171,6 +175,26 @@ template <> std::string STUInt32::getText() const { + if (getFName() == sfPermissionValue) + { + auto const permissionValue = + static_cast(value_); + auto const granular = + Permission::getInstance().getGranularName(permissionValue); + + if (granular) + { + return *granular; + } + else + { + auto const txType = + Permission::getInstance().permissionToTxType(value_); + auto item = TxFormats::getInstance().findByType(txType); + if (item != nullptr) + return item->getName(); + } + } return std::to_string(value_); } diff --git a/src/test/protocol/STInteger_test.cpp b/src/test/protocol/STInteger_test.cpp new file mode 100644 index 0000000000..2bcc084a4c --- /dev/null +++ b/src/test/protocol/STInteger_test.cpp @@ -0,0 +1,121 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2025 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include +#include +#include +#include +#include + +namespace ripple { + +struct STInteger_test : public beast::unit_test::suite +{ + void + testUInt8() + { + STUInt8 u8(42); + BEAST_EXPECT(u8.value() == 42); + BEAST_EXPECT(u8.getText() == "42"); + BEAST_EXPECT(u8.getSType() == STI_UINT8); + BEAST_EXPECT(u8.getJson(JsonOptions::none) == 42); + + // there is some special handling for sfTransactionResult + STUInt8 tr(sfTransactionResult, 0); + BEAST_EXPECT(tr.value() == 0); + BEAST_EXPECT( + tr.getText() == + "The transaction was applied. Only final in a validated ledger."); + BEAST_EXPECT(tr.getSType() == STI_UINT8); + BEAST_EXPECT(tr.getJson(JsonOptions::none) == "tesSUCCESS"); + } + + void + testUInt16() + { + STUInt16 u16(65535); + BEAST_EXPECT(u16.value() == 65535); + BEAST_EXPECT(u16.getText() == "65535"); + BEAST_EXPECT(u16.getSType() == STI_UINT16); + BEAST_EXPECT(u16.getJson(JsonOptions::none) == 65535); + + // there is some special handling for sfLedgerEntryType + STUInt16 let(sfLedgerEntryType, ltACCOUNT_ROOT); + BEAST_EXPECT(let.value() == ltACCOUNT_ROOT); + BEAST_EXPECT(let.getText() == "AccountRoot"); + BEAST_EXPECT(let.getSType() == STI_UINT16); + BEAST_EXPECT(let.getJson(JsonOptions::none) == "AccountRoot"); + + // there is some special handling for sfTransactionType + STUInt16 tlt(sfTransactionType, ttPAYMENT); + BEAST_EXPECT(tlt.value() == ttPAYMENT); + BEAST_EXPECT(tlt.getText() == "Payment"); + BEAST_EXPECT(tlt.getSType() == STI_UINT16); + BEAST_EXPECT(tlt.getJson(JsonOptions::none) == "Payment"); + } + + void + testUInt32() + { + STUInt32 u32(1234567890); + BEAST_EXPECT(u32.value() == 1234567890); + BEAST_EXPECT(u32.getText() == "1234567890"); + BEAST_EXPECT(u32.getSType() == STI_UINT32); + BEAST_EXPECT(u32.getJson(JsonOptions::none) == 1234567890); + + // there is some special handling for sfPermissionValue + STUInt32 pv(sfPermissionValue, ttPAYMENT + 1); + BEAST_EXPECT(pv.value() == ttPAYMENT + 1); + BEAST_EXPECT(pv.getText() == "Payment"); + BEAST_EXPECT(pv.getSType() == STI_UINT32); + BEAST_EXPECT(pv.getJson(JsonOptions::none) == "Payment"); + STUInt32 pv2(sfPermissionValue, PaymentMint); + BEAST_EXPECT(pv2.value() == PaymentMint); + BEAST_EXPECT(pv2.getText() == "PaymentMint"); + BEAST_EXPECT(pv2.getSType() == STI_UINT32); + BEAST_EXPECT(pv2.getJson(JsonOptions::none) == "PaymentMint"); + } + + void + testUInt64() + { + STUInt64 u64(0x123456789ABCDEF0ull); + BEAST_EXPECT(u64.value() == 0x123456789ABCDEF0ull); + BEAST_EXPECT(u64.getText() == "1311768467463790320"); + BEAST_EXPECT(u64.getSType() == STI_UINT64); + + // By default, getJson returns hex string + auto jsonVal = u64.getJson(JsonOptions::none); + BEAST_EXPECT(jsonVal.isString()); + BEAST_EXPECT(jsonVal.asString() == "123456789abcdef0"); + } + + void + run() override + { + testUInt8(); + testUInt16(); + testUInt32(); + testUInt64(); + } +}; + +BEAST_DEFINE_TESTSUITE(STInteger, protocol, ripple); + +} // namespace ripple diff --git a/src/test/protocol/STParsedJSON_test.cpp b/src/test/protocol/STParsedJSON_test.cpp index bd62196a99..305aab9e36 100644 --- a/src/test/protocol/STParsedJSON_test.cpp +++ b/src/test/protocol/STParsedJSON_test.cpp @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ /* This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. + Copyright (c) 2025 Ripple Labs Inc. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -19,14 +19,15 @@ #include +#include #include +#include #include namespace ripple { class STParsedJSON_test : public beast::unit_test::suite { -public: bool parseJSONString(std::string const& json, Json::Value& to) { @@ -323,6 +324,202 @@ public: } } + void + testUInt8() + { + Json::Value j; + j[sfCloseResolution] = 42; + STParsedJSONObject obj("Test", j); + BEAST_EXPECT(obj.object.has_value()); + BEAST_EXPECT(obj.object->isFieldPresent(sfCloseResolution)); + BEAST_EXPECT(obj.object->getFieldU8(sfCloseResolution) == 42); + } + + void + testUInt16() + { + Json::Value j; + j[sfLedgerEntryType] = 65535; + STParsedJSONObject obj("Test", j); + BEAST_EXPECT(obj.object.has_value()); + BEAST_EXPECT(obj.object->isFieldPresent(sfLedgerEntryType)); + BEAST_EXPECT(obj.object->getFieldU16(sfLedgerEntryType) == 65535); + } + + void + testUInt32() + { + Json::Value j; + j[sfNetworkID] = 4294967295u; + STParsedJSONObject obj("Test", j); + BEAST_EXPECT(obj.object.has_value()); + BEAST_EXPECT(obj.object->isFieldPresent(sfNetworkID)); + BEAST_EXPECT(obj.object->getFieldU32(sfNetworkID) == 4294967295u); + } + + void + testUInt64() + { + Json::Value j; + j[sfIndexNext] = "abcdefabcdef"; + STParsedJSONObject obj("Test", j); + BEAST_EXPECT(obj.object.has_value()); + BEAST_EXPECT(obj.object->isFieldPresent(sfIndexNext)); + BEAST_EXPECT( + obj.object->getFieldU64(sfIndexNext) == 188900977659375ull); + } + + void + testBlob() + { + Json::Value j; + j[sfPublicKey] = "DEADBEEF"; + STParsedJSONObject obj("Test", j); + BEAST_EXPECT(obj.object.has_value()); + BEAST_EXPECT(obj.object->isFieldPresent(sfPublicKey)); + auto const& blob = obj.object->getFieldVL(sfPublicKey); + BEAST_EXPECT(blob.size() == 4); + BEAST_EXPECT(blob[0] == 0xDE); + BEAST_EXPECT(blob[1] == 0xAD); + BEAST_EXPECT(blob[2] == 0xBE); + BEAST_EXPECT(blob[3] == 0xEF); + } + + void + testVector256() + { + Json::Value j; + Json::Value arr(Json::arrayValue); + arr.append( + "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"); + arr.append( + "FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210"); + j[sfHashes] = arr; + STParsedJSONObject obj("Test", j); + BEAST_EXPECT(obj.object.has_value()); + BEAST_EXPECT(obj.object->isFieldPresent(sfHashes)); + auto const& vec = obj.object->getFieldV256(sfHashes); + BEAST_EXPECT(vec.size() == 2); + BEAST_EXPECT(vec[0].size() == 32); + BEAST_EXPECT(vec[1].size() == 32); + } + + void + testAccount() + { + Json::Value j; + j[sfAccount] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"; + STParsedJSONObject obj("Test", j); + BEAST_EXPECT(obj.object.has_value()); + BEAST_EXPECT(obj.object->isFieldPresent(sfAccount)); + auto const& acct = obj.object->getAccountID(sfAccount); + BEAST_EXPECT(acct.size() == 20); + } + + void + testCurrency() + { + Json::Value j; + j[sfBaseAsset] = "USD"; + STParsedJSONObject obj("Test", j); + BEAST_EXPECT(obj.object.has_value()); + BEAST_EXPECT(obj.object->isFieldPresent(sfBaseAsset)); + auto const& curr = obj.object->getFieldCurrency(sfBaseAsset); + BEAST_EXPECT(curr.currency().size() == 20); + } + + void + testHash128() + { + Json::Value j; + j[sfEmailHash] = "0123456789ABCDEF0123456789ABCDEF"; + STParsedJSONObject obj("Test", j); + BEAST_EXPECT(obj.object.has_value()); + BEAST_EXPECT(obj.object->isFieldPresent(sfEmailHash)); + BEAST_EXPECT(obj.object->getFieldH128(sfEmailHash).size() == 16); + } + + void + testHash160() + { + Json::Value j; + j[sfTakerPaysCurrency] = "0123456789ABCDEF0123456789ABCDEF01234567"; + STParsedJSONObject obj("Test", j); + BEAST_EXPECT(obj.object.has_value()); + BEAST_EXPECT(obj.object->isFieldPresent(sfTakerPaysCurrency)); + BEAST_EXPECT( + obj.object->getFieldH160(sfTakerPaysCurrency).size() == 20); + } + + void + testHash256() + { + Json::Value j; + j[sfLedgerHash] = + "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"; + STParsedJSONObject obj("Test", j); + BEAST_EXPECT(obj.object.has_value()); + BEAST_EXPECT(obj.object->isFieldPresent(sfLedgerHash)); + BEAST_EXPECT(obj.object->getFieldH256(sfLedgerHash).size() == 32); + } + + void + testAmount() + { + Json::Value j; + j[sfAmount] = "1000000"; + STParsedJSONObject obj("Test", j); + BEAST_EXPECT(obj.object.has_value()); + BEAST_EXPECT(obj.object->isFieldPresent(sfAmount)); + BEAST_EXPECT(obj.object->getFieldAmount(sfAmount) == STAmount(1000000)); + } + + void + testPathSet() + { + Json::Value j; + Json::Value path(Json::arrayValue); + Json::Value elem(Json::objectValue); + elem["account"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"; + path.append(elem); + Json::Value pathset(Json::arrayValue); + pathset.append(path); + j[sfPaths] = pathset; + STParsedJSONObject obj("Test", j); + BEAST_EXPECT(obj.object.has_value()); + BEAST_EXPECT(obj.object->isFieldPresent(sfPaths)); + auto const& ps = obj.object->getFieldPathSet(sfPaths); + BEAST_EXPECT(!ps.empty()); + } + + void + testObject() + { + Json::Value j; + Json::Value objVal(Json::objectValue); + objVal[sfTransactionResult] = 1; + j[sfTransactionMetaData] = objVal; + STParsedJSONObject obj("Test", j); + BEAST_EXPECT(obj.object.has_value()); + BEAST_EXPECT(obj.object->isFieldPresent(sfTransactionMetaData)); + } + + void + testArray() + { + Json::Value j; + Json::Value arr(Json::arrayValue); + Json::Value elem(Json::objectValue); + elem[sfTransactionResult] = 2; + Json::Value elem2(Json::objectValue); + elem2[sfTransactionMetaData] = elem; + arr.append(elem2); + j[sfSignerEntries] = arr; + STParsedJSONObject obj("Test", j); + BEAST_EXPECT(obj.object.has_value()); + BEAST_EXPECT(obj.object->isFieldPresent(sfSignerEntries)); + } + void run() override { @@ -331,6 +528,22 @@ public: testParseJSONArrayWithInvalidChildrenObjects(); testParseJSONArray(); testParseJSONEdgeCases(); + + testUInt8(); + testUInt16(); + testUInt32(); + testUInt64(); + testBlob(); + testVector256(); + testAccount(); + testCurrency(); + testHash128(); + testHash160(); + testHash256(); + testAmount(); + testPathSet(); + testObject(); + testArray(); } };