diff --git a/src/rpc/RPCHelpers.cpp b/src/rpc/RPCHelpers.cpp index 9c4a3ee9f..b7f23ed81 100644 --- a/src/rpc/RPCHelpers.cpp +++ b/src/rpc/RPCHelpers.cpp @@ -24,6 +24,7 @@ #include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/common/Types.hpp" +#include "util/AccountUtils.hpp" #include "util/Profiler.hpp" #include "util/log/Logger.hpp" #include "web/Context.hpp" @@ -186,14 +187,14 @@ accountFromStringStrict(std::string const& account) if (blob && ripple::publicKeyType(ripple::makeSlice(*blob))) { publicKey = ripple::PublicKey(ripple::Slice{blob->data(), blob->size()}); } else { - publicKey = ripple::parseBase58(ripple::TokenType::AccountPublic, account); + publicKey = util::parseBase58Wrapper(ripple::TokenType::AccountPublic, account); } std::optional result; if (publicKey) { result = ripple::calcAccountID(*publicKey); } else { - result = ripple::parseBase58(account); + result = util::parseBase58Wrapper(account); } return result; @@ -799,7 +800,7 @@ getAccountsFromTransaction(boost::json::object const& transaction) auto inObject = getAccountsFromTransaction(value.as_object()); accounts.insert(accounts.end(), inObject.begin(), inObject.end()); } else if (value.is_string()) { - auto const account = ripple::parseBase58(boost::json::value_to(value)); + auto const account = util::parseBase58Wrapper(boost::json::value_to(value)); if (account) { accounts.push_back(*account); } diff --git a/src/rpc/common/Validators.cpp b/src/rpc/common/Validators.cpp index fbd6d806b..f357f618a 100644 --- a/src/rpc/common/Validators.cpp +++ b/src/rpc/common/Validators.cpp @@ -22,6 +22,7 @@ #include "rpc/Errors.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/Types.hpp" +#include "util/AccountUtils.hpp" #include #include @@ -114,7 +115,7 @@ CustomValidator AccountBase58Validator = if (!value.is_string()) return Error{Status{RippledError::rpcINVALID_PARAMS, std::string(key) + "NotString"}}; - auto const account = ripple::parseBase58(boost::json::value_to(value)); + auto const account = util::parseBase58Wrapper(boost::json::value_to(value)); if (!account || account->isZero()) return Error{Status{ClioError::rpcMALFORMED_ADDRESS}}; diff --git a/src/rpc/handlers/GatewayBalances.hpp b/src/rpc/handlers/GatewayBalances.hpp index c03fbbcf7..e3998142d 100644 --- a/src/rpc/handlers/GatewayBalances.hpp +++ b/src/rpc/handlers/GatewayBalances.hpp @@ -25,6 +25,7 @@ #include "rpc/common/Specs.hpp" #include "rpc/common/Types.hpp" #include "rpc/common/Validators.hpp" +#include "util/AccountUtils.hpp" #include #include @@ -116,14 +117,14 @@ class GatewayBalancesHandler { auto const wallets = value.is_array() ? value.as_array() : boost::json::array{value}; auto const getAccountID = [](auto const& j) -> std::optional { if (j.is_string()) { - auto const pk = ripple::parseBase58( + auto const pk = util::parseBase58Wrapper( ripple::TokenType::AccountPublic, boost::json::value_to(j) ); if (pk) return ripple::calcAccountID(*pk); - return ripple::parseBase58(boost::json::value_to(j)); + return util::parseBase58Wrapper(boost::json::value_to(j)); } return {}; diff --git a/src/rpc/handlers/GetAggregatePrice.cpp b/src/rpc/handlers/GetAggregatePrice.cpp index 9eab54eba..59bcb2cad 100644 --- a/src/rpc/handlers/GetAggregatePrice.cpp +++ b/src/rpc/handlers/GetAggregatePrice.cpp @@ -23,6 +23,7 @@ #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/Types.hpp" +#include "util/AccountUtils.hpp" #include #include @@ -263,7 +264,7 @@ tag_invoke(boost::json::value_to_tag, boost::js for (auto const& oracle : jsonObject.at(JS(oracles)).as_array()) { input.oracles.push_back(GetAggregatePriceHandler::Oracle{ .documentId = boost::json::value_to(oracle.as_object().at(JS(oracle_document_id))), - .account = *ripple::parseBase58( + .account = *util::parseBase58Wrapper( boost::json::value_to(oracle.as_object().at(JS(account))) ) }); diff --git a/src/rpc/handlers/LedgerEntry.cpp b/src/rpc/handlers/LedgerEntry.cpp index ac264438e..f9d069e6e 100644 --- a/src/rpc/handlers/LedgerEntry.cpp +++ b/src/rpc/handlers/LedgerEntry.cpp @@ -23,6 +23,7 @@ #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/Types.hpp" +#include "util/AccountUtils.hpp" #include #include @@ -62,9 +63,9 @@ LedgerEntryHandler::process(LedgerEntryHandler::Input input, Context const& ctx) if (input.index) { key = ripple::uint256{std::string_view(*(input.index))}; } else if (input.accountRoot) { - key = ripple::keylet::account(*ripple::parseBase58(*(input.accountRoot))).key; + key = ripple::keylet::account(*util::parseBase58Wrapper(*(input.accountRoot))).key; } else if (input.did) { - key = ripple::keylet::did(*ripple::parseBase58(*(input.did))).key; + key = ripple::keylet::did(*util::parseBase58Wrapper(*(input.did))).key; } else if (input.directory) { auto const keyOrStatus = composeKeyFromDirectory(*input.directory); if (auto const status = std::get_if(&keyOrStatus)) @@ -73,13 +74,14 @@ LedgerEntryHandler::process(LedgerEntryHandler::Input input, Context const& ctx) key = std::get(keyOrStatus); } else if (input.offer) { auto const id = - ripple::parseBase58(boost::json::value_to(input.offer->at(JS(account)))); + util::parseBase58Wrapper(boost::json::value_to(input.offer->at(JS(account))) + ); key = ripple::keylet::offer(*id, boost::json::value_to(input.offer->at(JS(seq)))).key; } else if (input.rippleStateAccount) { - auto const id1 = ripple::parseBase58( + auto const id1 = util::parseBase58Wrapper( boost::json::value_to(input.rippleStateAccount->at(JS(accounts)).as_array().at(0)) ); - auto const id2 = ripple::parseBase58( + auto const id2 = util::parseBase58Wrapper( boost::json::value_to(input.rippleStateAccount->at(JS(accounts)).as_array().at(1)) ); auto const currency = @@ -88,20 +90,22 @@ LedgerEntryHandler::process(LedgerEntryHandler::Input input, Context const& ctx) key = ripple::keylet::line(*id1, *id2, currency).key; } else if (input.escrow) { auto const id = - ripple::parseBase58(boost::json::value_to(input.escrow->at(JS(owner)))); + util::parseBase58Wrapper(boost::json::value_to(input.escrow->at(JS(owner))) + ); key = ripple::keylet::escrow(*id, input.escrow->at(JS(seq)).as_int64()).key; } else if (input.depositPreauth) { - auto const owner = ripple::parseBase58( + auto const owner = util::parseBase58Wrapper( boost::json::value_to(input.depositPreauth->at(JS(owner))) ); - auto const authorized = ripple::parseBase58( + auto const authorized = util::parseBase58Wrapper( boost::json::value_to(input.depositPreauth->at(JS(authorized))) ); key = ripple::keylet::depositPreauth(*owner, *authorized).key; } else if (input.ticket) { auto const id = - ripple::parseBase58(boost::json::value_to(input.ticket->at(JS(account)))); + util::parseBase58Wrapper(boost::json::value_to(input.ticket->at(JS(account)) + )); key = ripple::getTicketIndex(*id, input.ticket->at(JS(ticket_seq)).as_int64()); } else if (input.amm) { @@ -112,7 +116,8 @@ LedgerEntryHandler::process(LedgerEntryHandler::Input input, Context const& ctx) return ripple::xrpIssue(); } auto const issuer = - ripple::parseBase58(boost::json::value_to(assetJson.at(JS(issuer)))); + util::parseBase58Wrapper(boost::json::value_to(assetJson.at(JS(issuer))) + ); return ripple::Issue{currency, *issuer}; }; @@ -125,7 +130,7 @@ LedgerEntryHandler::process(LedgerEntryHandler::Input input, Context const& ctx) return Error{Status{ClioError::rpcMALFORMED_REQUEST}}; if (input.bridgeAccount) { - auto const bridgeAccount = ripple::parseBase58(*(input.bridgeAccount)); + auto const bridgeAccount = util::parseBase58Wrapper(*(input.bridgeAccount)); auto const chainType = ripple::STXChainBridge::srcChain(bridgeAccount == input.bridge->lockingChainDoor()); if (bridgeAccount != input.bridge->door(chainType)) @@ -201,7 +206,7 @@ LedgerEntryHandler::composeKeyFromDirectory(boost::json::object const& directory } auto const ownerID = - ripple::parseBase58(boost::json::value_to(directory.at(JS(owner)))); + util::parseBase58Wrapper(boost::json::value_to(directory.at(JS(owner)))); return ripple::keylet::page(ripple::keylet::ownerDir(*ownerID), subIndex).key; } @@ -262,10 +267,10 @@ tag_invoke(boost::json::value_to_tag, boost::json::va }; auto const parseBridgeFromJson = [](boost::json::value const& bridgeJson) { - auto const lockingDoor = *ripple::parseBase58( + auto const lockingDoor = *util::parseBase58Wrapper( boost::json::value_to(bridgeJson.at(ripple::sfLockingChainDoor.getJsonName().c_str())) ); - auto const issuingDoor = *ripple::parseBase58( + auto const issuingDoor = *util::parseBase58Wrapper( boost::json::value_to(bridgeJson.at(ripple::sfIssuingChainDoor.getJsonName().c_str())) ); auto const lockingIssue = @@ -278,7 +283,7 @@ tag_invoke(boost::json::value_to_tag, boost::json::va auto const parseOracleFromJson = [](boost::json::value const& json) { auto const account = - ripple::parseBase58(boost::json::value_to(json.at(JS(account)))); + util::parseBase58Wrapper(boost::json::value_to(json.at(JS(account)))); auto const documentId = boost::json::value_to(json.at(JS(oracle_document_id))); return ripple::keylet::oracle(*account, documentId).key; diff --git a/src/rpc/handlers/LedgerEntry.hpp b/src/rpc/handlers/LedgerEntry.hpp index 19bf517a3..8f809c1b7 100644 --- a/src/rpc/handlers/LedgerEntry.hpp +++ b/src/rpc/handlers/LedgerEntry.hpp @@ -28,6 +28,7 @@ #include "rpc/common/Specs.hpp" #include "rpc/common/Types.hpp" #include "rpc/common/Validators.hpp" +#include "util/AccountUtils.hpp" #include #include @@ -136,9 +137,11 @@ class LedgerEntryHandler { } auto const id1 = - ripple::parseBase58(boost::json::value_to(value.as_array()[0])); + util::parseBase58Wrapper(boost::json::value_to(value.as_array()[0]) + ); auto const id2 = - ripple::parseBase58(boost::json::value_to(value.as_array()[1])); + util::parseBase58Wrapper(boost::json::value_to(value.as_array()[1]) + ); if (!id1 || !id2) return Error{Status{ClioError::rpcMALFORMED_ADDRESS, "malformedAddresses"}}; diff --git a/src/util/AccountUtils.hpp b/src/util/AccountUtils.hpp new file mode 100644 index 000000000..f1f4e1e2f --- /dev/null +++ b/src/util/AccountUtils.hpp @@ -0,0 +1,67 @@ +//------------------------------------------------------------------------------ +/* + This file is part of clio: https://github.com/XRPLF/clio + Copyright (c) 2024, the clio developers. + + Permission to use, copy, modify, and 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. +*/ +//============================================================================== + +#pragma once + +#include + +#include +#include +#include + +namespace util { + +/** + * @brief A wrapper of parseBase58 function. It adds the check if all characters in the input string are alphanumeric. + * If not, it returns an empty optional, instead of calling the parseBase58 function. + * + * @tparam T The type of the value to parse to. + * @param str The string to parse. + * @return An optional with the parsed value, or an empty optional if the parse fails. + */ +template +[[nodiscard]] std::optional +parseBase58Wrapper(std::string const& str) +{ + if (!std::all_of(std::begin(str), std::end(str), [](unsigned char c) { return std::isalnum(c); })) + return std::nullopt; + + return ripple::parseBase58(str); +} + +/** + * @brief A wrapper of parseBase58 function. It add the check if all characters in the input string are alphanumeric. If + * not, it returns an empty optional, instead of calling the parseBase58 function. + * + * @tparam T The type of the value to parse to. + * @param type The type of the token to parse. + * @param str The string to parse. + * @return An optional with the parsed value, or an empty optional if the parse fails. + */ +template +[[nodiscard]] std::optional +parseBase58Wrapper(ripple::TokenType type, std::string const& str) +{ + if (!std::all_of(std::begin(str), std::end(str), [](unsigned char c) { return std::isalnum(c); })) + return std::nullopt; + + return ripple::parseBase58(type, str); +} + +} // namespace util diff --git a/tests/common/util/TestObject.cpp b/tests/common/util/TestObject.cpp index 8e94390a0..1e891ea1a 100644 --- a/tests/common/util/TestObject.cpp +++ b/tests/common/util/TestObject.cpp @@ -21,6 +21,7 @@ #include "data/DBHelpers.hpp" #include "data/Types.hpp" +#include "util/AccountUtils.hpp" #include "util/Assert.hpp" #include @@ -60,7 +61,7 @@ constexpr static auto INDEX1 = "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B2501 ripple::AccountID GetAccountIDWithString(std::string_view id) { - return ripple::parseBase58(std::string(id)).value(); + return util::parseBase58Wrapper(std::string(id)).value(); } ripple::uint256 @@ -154,11 +155,11 @@ CreatePaymentTransactionObject( { ripple::STObject obj(ripple::sfTransaction); obj.setFieldU16(ripple::sfTransactionType, ripple::ttPAYMENT); - auto account = ripple::parseBase58(std::string(accountId1)); + auto account = util::parseBase58Wrapper(std::string(accountId1)); obj.setAccountID(ripple::sfAccount, account.value()); obj.setFieldAmount(ripple::sfAmount, ripple::STAmount(amount, false)); obj.setFieldAmount(ripple::sfFee, ripple::STAmount(fee, false)); - auto account2 = ripple::parseBase58(std::string(accountId2)); + auto account2 = util::parseBase58Wrapper(std::string(accountId2)); obj.setAccountID(ripple::sfDestination, account2.value()); obj.setFieldU32(ripple::sfSequence, seq); char const* key = "test"; @@ -258,14 +259,14 @@ CreateCreateOfferTransactionObject( { ripple::STObject obj(ripple::sfTransaction); obj.setFieldU16(ripple::sfTransactionType, ripple::ttOFFER_CREATE); - auto account = ripple::parseBase58(std::string(accountId)); + auto account = util::parseBase58Wrapper(std::string(accountId)); obj.setAccountID(ripple::sfAccount, account.value()); auto amount = ripple::STAmount(fee, false); obj.setFieldAmount(ripple::sfFee, amount); obj.setFieldU32(ripple::sfSequence, seq); // add amount ripple::Issue const issue1( - ripple::Currency{currency}, ripple::parseBase58(std::string(issuer)).value() + ripple::Currency{currency}, util::parseBase58Wrapper(std::string(issuer)).value() ); if (reverse) { obj.setFieldAmount(ripple::sfTakerPays, ripple::STAmount(issue1, takerGets)); @@ -288,11 +289,11 @@ GetIssue(std::string_view currency, std::string_view issuerId) if (currency.size() == 3) { return ripple::Issue( ripple::to_currency(std::string(currency)), - ripple::parseBase58(std::string(issuerId)).value() + util::parseBase58Wrapper(std::string(issuerId)).value() ); } return ripple::Issue( - ripple::Currency{currency}, ripple::parseBase58(std::string(issuerId)).value() + ripple::Currency{currency}, util::parseBase58Wrapper(std::string(issuerId)).value() ); } @@ -636,7 +637,7 @@ CreateMintNFTTxWithMetadata( // tx ripple::STObject tx(ripple::sfTransaction); tx.setFieldU16(ripple::sfTransactionType, ripple::ttNFTOKEN_MINT); - auto account = ripple::parseBase58(std::string(accountId)); + auto account = util::parseBase58Wrapper(std::string(accountId)); tx.setAccountID(ripple::sfAccount, account.value()); auto amount = ripple::STAmount(fee, false); tx.setFieldAmount(ripple::sfFee, amount); @@ -693,7 +694,7 @@ CreateAcceptNFTOfferTxWithMetadata(std::string_view accountId, uint32_t seq, uin // tx ripple::STObject tx(ripple::sfTransaction); tx.setFieldU16(ripple::sfTransactionType, ripple::ttNFTOKEN_ACCEPT_OFFER); - auto account = ripple::parseBase58(std::string(accountId)); + auto account = util::parseBase58Wrapper(std::string(accountId)); tx.setAccountID(ripple::sfAccount, account.value()); auto amount = ripple::STAmount(fee, false); tx.setFieldAmount(ripple::sfFee, amount); @@ -737,7 +738,7 @@ CreateCancelNFTOffersTxWithMetadata( // tx ripple::STObject tx(ripple::sfTransaction); tx.setFieldU16(ripple::sfTransactionType, ripple::ttNFTOKEN_CANCEL_OFFER); - auto account = ripple::parseBase58(std::string(accountId)); + auto account = util::parseBase58Wrapper(std::string(accountId)); tx.setAccountID(ripple::sfAccount, account.value()); auto amount = ripple::STAmount(fee, false); tx.setFieldAmount(ripple::sfFee, amount); @@ -791,7 +792,7 @@ CreateCreateNFTOfferTxWithMetadata( // tx ripple::STObject tx(ripple::sfTransaction); tx.setFieldU16(ripple::sfTransactionType, ripple::ttNFTOKEN_CREATE_OFFER); - auto account = ripple::parseBase58(std::string(accountId)); + auto account = util::parseBase58Wrapper(std::string(accountId)); tx.setAccountID(ripple::sfAccount, account.value()); auto amount = ripple::STAmount(fee, false); tx.setFieldAmount(ripple::sfFee, amount); @@ -839,7 +840,7 @@ CreateOracleSetTxWithMetadata( // tx ripple::STObject tx(ripple::sfTransaction); tx.setFieldU16(ripple::sfTransactionType, ripple::ttORACLE_SET); - auto account = ripple::parseBase58(std::string(accountId)); + auto account = util::parseBase58Wrapper(std::string(accountId)); tx.setAccountID(ripple::sfAccount, account.value()); auto amount = ripple::STAmount(fee, false); tx.setFieldAmount(ripple::sfFee, amount); @@ -909,7 +910,7 @@ CreateAMMObject( amm.setFieldIssue(ripple::sfAsset2, ripple::STIssue{ripple::sfAsset2, GetIssue(asset2Currency, asset2Issuer)}); ripple::Issue const issue1( ripple::Currency{lpTokenBalanceIssueCurrency}, - ripple::parseBase58(std::string(accountId)).value() + util::parseBase58Wrapper(std::string(accountId)).value() ); amm.setFieldAmount(ripple::sfLPTokenBalance, ripple::STAmount(issue1, lpTokenBalanceIssueAmount)); amm.setFieldU32(ripple::sfFlags, 0); diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 83e2421ff..d384f0fbf 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -92,6 +92,7 @@ target_sources( rpc/JsonBoolTests.cpp rpc/RPCHelpersTests.cpp rpc/WorkQueueTests.cpp + util/AccountUtilsTests.cpp util/AssertTests.cpp # Async framework util/async/AnyExecutionContextTests.cpp diff --git a/tests/unit/rpc/BaseTests.cpp b/tests/unit/rpc/BaseTests.cpp index d195b6e58..f18d5e8d3 100644 --- a/tests/unit/rpc/BaseTests.cpp +++ b/tests/unit/rpc/BaseTests.cpp @@ -426,6 +426,9 @@ TEST_F(RPCBaseTest, AccountValidator) failingInput = json::parse(R"({ "account": "02000000000000000000000000000000000000000000000000000000000000000" })"); ASSERT_FALSE(spec.process(failingInput)); + failingInput = json::parse(R"({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jp?" })"); + ASSERT_FALSE(spec.process(failingInput)); + auto passingInput = json::parse(R"({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn" })"); ASSERT_TRUE(spec.process(passingInput)); @@ -434,6 +437,28 @@ TEST_F(RPCBaseTest, AccountValidator) ASSERT_TRUE(spec.process(passingInput)); } +TEST_F(RPCBaseTest, AccountBase58Validator) +{ + auto spec = RpcSpec{ + {"account", validation::AccountBase58Validator}, + }; + auto failingInput = json::parse(R"({ "account": 256 })"); + ASSERT_FALSE(spec.process(failingInput)); + + failingInput = json::parse(R"({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jp" })"); + ASSERT_FALSE(spec.process(failingInput)); + + failingInput = + json::parse(R"({ "account": "020000000000000000000000000000000000000000000000000000000000000000" })"); + ASSERT_FALSE(spec.process(failingInput)); + + failingInput = json::parse(R"({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jp?" })"); + ASSERT_FALSE(spec.process(failingInput)); + + auto passingInput = json::parse(R"({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn" })"); + ASSERT_TRUE(spec.process(passingInput)); +} + TEST_F(RPCBaseTest, AccountMarkerValidator) { auto spec = RpcSpec{ diff --git a/tests/unit/util/AccountUtilsTests.cpp b/tests/unit/util/AccountUtilsTests.cpp new file mode 100644 index 000000000..8ed9da019 --- /dev/null +++ b/tests/unit/util/AccountUtilsTests.cpp @@ -0,0 +1,40 @@ +//------------------------------------------------------------------------------ +/* + This file is part of clio: https://github.com/XRPLF/clio + Copyright (c) 2024, the clio developers. + + Permission to use, copy, modify, and 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 "util/AccountUtils.hpp" + +#include +#include +#include +#include + +constexpr static auto ACCOUNT = "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn"; + +TEST(AccountUtils, parseBase58Wrapper) +{ + EXPECT_FALSE(util::parseBase58Wrapper("rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jp!")); + EXPECT_TRUE(util::parseBase58Wrapper(ACCOUNT)); + + EXPECT_TRUE(util::parseBase58Wrapper( + ripple::TokenType::NodePrivate, "paQmjZ37pKKPMrgadBLsuf9ab7Y7EUNzh27LQrZqoexpAs31nJi" + )); + EXPECT_FALSE(util::parseBase58Wrapper( + ripple::TokenType::NodePrivate, "??paQmjZ37pKKPMrgadBLsuf9ab7Y7EUNzh27LQrZqoexpAs31n" + )); +}