feat: Add Support Credentials for Clio (#1712)

Rippled PR: [here](https://github.com/XRPLF/rippled/pull/5103)
This commit is contained in:
Peter Chen
2024-11-14 14:52:03 -05:00
committed by GitHub
parent 0e25c0cabc
commit 67d99457f2
23 changed files with 1560 additions and 99 deletions

View File

@@ -28,24 +28,34 @@
#include <boost/json/parse.hpp>
#include <fmt/core.h>
#include <fmt/format.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/chrono.h>
#include <xrpl/basics/strHex.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/STArray.h>
#include <chrono>
#include <optional>
#include <ranges>
#include <string>
#include <string_view>
#include <vector>
constexpr static auto ACCOUNT = "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn";
constexpr static auto ACCOUNT2 = "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun";
constexpr static auto LEDGERHASH = "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652";
constexpr static auto INDEX1 = "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC";
constexpr static auto INDEX2 = "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515B1";
constexpr static auto Account = "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn";
constexpr static auto Account2 = "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun";
constexpr static auto LedgerHash = "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652";
constexpr static auto Index1 = "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC";
constexpr static auto Index2 = "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515B1";
constexpr static std::string_view CredentialType = "credType";
constexpr static auto CredentialHash = "F245428267E6177AEEFDD4FEA3533285712A4B1091CF82A7EA7BC39A62C3FB1A";
constexpr static auto RANGEMIN = 10;
constexpr static auto RANGEMAX = 30;
constexpr static auto RangeMin = 10;
constexpr static auto RangeMax = 30;
using namespace rpc;
namespace json = boost::json;
@@ -156,6 +166,38 @@ generateTestValuesForParametersTest()
"invalidParams",
"ledgerIndexMalformed",
},
{
"CredentialsNotArray",
R"({
"source_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"destination_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"credentials": "x"
})",
"invalidParams",
"Invalid parameters.",
},
{
"CredentialsNotStringsInArray",
R"({
"source_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"destination_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652",
"credentials": [123]
})",
"invalidParams",
"Item is not a valid uint256 type.",
},
{
"CredentialsNotHexedStringInArray",
R"({
"source_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"destination_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652",
"credentials": ["234", "432"]
})",
"invalidParams",
"Item is not a valid uint256 type.",
}
};
}
@@ -184,10 +226,10 @@ TEST_P(DepositAuthorizedParameterTest, InvalidParams)
TEST_F(RPCDepositAuthorizedTest, LedgerNotExistViaIntSequence)
{
backend->setRange(RANGEMIN, RANGEMAX);
backend->setRange(RangeMin, RangeMax);
EXPECT_CALL(*backend, fetchLedgerBySequence).Times(1);
ON_CALL(*backend, fetchLedgerBySequence(RANGEMAX, _)).WillByDefault(Return(std::nullopt));
ON_CALL(*backend, fetchLedgerBySequence(RangeMax, _)).WillByDefault(Return(std::nullopt));
runSpawn([&, this](auto yield) {
auto const handler = AnyHandler{DepositAuthorizedHandler{backend}};
@@ -197,9 +239,9 @@ TEST_F(RPCDepositAuthorizedTest, LedgerNotExistViaIntSequence)
"destination_account": "{}",
"ledger_index": {}
}})",
ACCOUNT,
ACCOUNT2,
RANGEMAX
Account,
Account2,
RangeMax
));
auto const output = handler.process(req, Context{yield});
@@ -213,10 +255,10 @@ TEST_F(RPCDepositAuthorizedTest, LedgerNotExistViaIntSequence)
TEST_F(RPCDepositAuthorizedTest, LedgerNotExistViaStringSequence)
{
backend->setRange(RANGEMIN, RANGEMAX);
backend->setRange(RangeMin, RangeMax);
EXPECT_CALL(*backend, fetchLedgerBySequence).Times(1);
ON_CALL(*backend, fetchLedgerBySequence(RANGEMAX, _)).WillByDefault(Return(std::nullopt));
ON_CALL(*backend, fetchLedgerBySequence(RangeMax, _)).WillByDefault(Return(std::nullopt));
runSpawn([&, this](auto yield) {
auto const handler = AnyHandler{DepositAuthorizedHandler{backend}};
@@ -226,9 +268,9 @@ TEST_F(RPCDepositAuthorizedTest, LedgerNotExistViaStringSequence)
"destination_account": "{}",
"ledger_index": "{}"
}})",
ACCOUNT,
ACCOUNT2,
RANGEMAX
Account,
Account2,
RangeMax
));
auto const output = handler.process(req, Context{yield});
@@ -242,10 +284,10 @@ TEST_F(RPCDepositAuthorizedTest, LedgerNotExistViaStringSequence)
TEST_F(RPCDepositAuthorizedTest, LedgerNotExistViaHash)
{
backend->setRange(RANGEMIN, RANGEMAX);
backend->setRange(RangeMin, RangeMax);
EXPECT_CALL(*backend, fetchLedgerByHash).Times(1);
ON_CALL(*backend, fetchLedgerByHash(ripple::uint256{LEDGERHASH}, _)).WillByDefault(Return(std::nullopt));
ON_CALL(*backend, fetchLedgerByHash(ripple::uint256{LedgerHash}, _)).WillByDefault(Return(std::nullopt));
runSpawn([&, this](auto yield) {
auto const handler = AnyHandler{DepositAuthorizedHandler{backend}};
@@ -255,9 +297,9 @@ TEST_F(RPCDepositAuthorizedTest, LedgerNotExistViaHash)
"destination_account": "{}",
"ledger_hash": "{}"
}})",
ACCOUNT,
ACCOUNT2,
LEDGERHASH
Account,
Account2,
LedgerHash
));
auto const output = handler.process(req, Context{yield});
@@ -273,9 +315,9 @@ TEST_F(RPCDepositAuthorizedTest, SourceAccountDoesNotExist)
{
backend->setRange(10, 30);
auto ledgerHeader = CreateLedgerHeader(LEDGERHASH, 30);
auto ledgerHeader = CreateLedgerHeader(LedgerHash, 30);
ON_CALL(*backend, fetchLedgerByHash(ripple::uint256{LEDGERHASH}, _)).WillByDefault(Return(ledgerHeader));
ON_CALL(*backend, fetchLedgerByHash(ripple::uint256{LedgerHash}, _)).WillByDefault(Return(ledgerHeader));
EXPECT_CALL(*backend, fetchLedgerByHash).Times(1);
ON_CALL(*backend, doFetchLedgerObject).WillByDefault(Return(std::optional<Blob>{}));
@@ -287,9 +329,9 @@ TEST_F(RPCDepositAuthorizedTest, SourceAccountDoesNotExist)
"destination_account": "{}",
"ledger_hash": "{}"
}})",
ACCOUNT,
ACCOUNT2,
LEDGERHASH
Account,
Account2,
LedgerHash
));
runSpawn([&, this](auto yield) {
@@ -308,14 +350,14 @@ TEST_F(RPCDepositAuthorizedTest, DestinationAccountDoesNotExist)
{
backend->setRange(10, 30);
auto ledgerHeader = CreateLedgerHeader(LEDGERHASH, 30);
auto ledgerHeader = CreateLedgerHeader(LedgerHash, 30);
ON_CALL(*backend, fetchLedgerByHash(ripple::uint256{LEDGERHASH}, _)).WillByDefault(Return(ledgerHeader));
ON_CALL(*backend, fetchLedgerByHash(ripple::uint256{LedgerHash}, _)).WillByDefault(Return(ledgerHeader));
EXPECT_CALL(*backend, fetchLedgerByHash).Times(1);
auto const accountRoot = CreateAccountRootObject(ACCOUNT, 0, 2, 200, 2, INDEX1, 2);
auto const accountRoot = CreateAccountRootObject(Account, 0, 2, 200, 2, Index1, 2);
ON_CALL(*backend, doFetchLedgerObject(_, _, _)).WillByDefault(Return(accountRoot.getSerializer().peekData()));
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::account(GetAccountIDWithString(ACCOUNT2)).key, _, _))
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::account(GetAccountIDWithString(Account2)).key, _, _))
.WillByDefault(Return(std::optional<Blob>{}));
EXPECT_CALL(*backend, doFetchLedgerObject).Times(2);
@@ -326,9 +368,9 @@ TEST_F(RPCDepositAuthorizedTest, DestinationAccountDoesNotExist)
"destination_account": "{}",
"ledger_hash": "{}"
}})",
ACCOUNT,
ACCOUNT2,
LEDGERHASH
Account,
Account2,
LedgerHash
));
runSpawn([&, this](auto yield) {
@@ -357,12 +399,12 @@ TEST_F(RPCDepositAuthorizedTest, AccountsAreEqual)
backend->setRange(10, 30);
auto ledgerHeader = CreateLedgerHeader(LEDGERHASH, 30);
auto ledgerHeader = CreateLedgerHeader(LedgerHash, 30);
ON_CALL(*backend, fetchLedgerByHash(ripple::uint256{LEDGERHASH}, _)).WillByDefault(Return(ledgerHeader));
ON_CALL(*backend, fetchLedgerByHash(ripple::uint256{LedgerHash}, _)).WillByDefault(Return(ledgerHeader));
EXPECT_CALL(*backend, fetchLedgerByHash).Times(1);
auto const accountRoot = CreateAccountRootObject(ACCOUNT, 0, 2, 200, 2, INDEX1, 2);
auto const accountRoot = CreateAccountRootObject(Account, 0, 2, 200, 2, Index1, 2);
ON_CALL(*backend, doFetchLedgerObject).WillByDefault(Return(accountRoot.getSerializer().peekData()));
EXPECT_CALL(*backend, doFetchLedgerObject).Times(2);
@@ -372,9 +414,9 @@ TEST_F(RPCDepositAuthorizedTest, AccountsAreEqual)
"destination_account": "{}",
"ledger_hash": "{}"
}})",
ACCOUNT,
ACCOUNT,
LEDGERHASH
Account,
Account,
LedgerHash
));
runSpawn([&, this](auto yield) {
@@ -400,17 +442,17 @@ TEST_F(RPCDepositAuthorizedTest, DifferentAccountsNoDepositAuthFlag)
backend->setRange(10, 30);
auto ledgerHeader = CreateLedgerHeader(LEDGERHASH, 30);
auto ledgerHeader = CreateLedgerHeader(LedgerHash, 30);
ON_CALL(*backend, fetchLedgerByHash(ripple::uint256{LEDGERHASH}, _)).WillByDefault(Return(ledgerHeader));
ON_CALL(*backend, fetchLedgerByHash(ripple::uint256{LedgerHash}, _)).WillByDefault(Return(ledgerHeader));
EXPECT_CALL(*backend, fetchLedgerByHash).Times(1);
auto const account1Root = CreateAccountRootObject(ACCOUNT, 0, 2, 200, 2, INDEX1, 2);
auto const account2Root = CreateAccountRootObject(ACCOUNT2, 0, 2, 200, 2, INDEX2, 2);
auto const account1Root = CreateAccountRootObject(Account, 0, 2, 200, 2, Index1, 2);
auto const account2Root = CreateAccountRootObject(Account2, 0, 2, 200, 2, Index2, 2);
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::account(GetAccountIDWithString(ACCOUNT)).key, _, _))
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::account(GetAccountIDWithString(Account)).key, _, _))
.WillByDefault(Return(account1Root.getSerializer().peekData()));
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::account(GetAccountIDWithString(ACCOUNT2)).key, _, _))
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::account(GetAccountIDWithString(Account2)).key, _, _))
.WillByDefault(Return(account2Root.getSerializer().peekData()));
EXPECT_CALL(*backend, doFetchLedgerObject).Times(2);
@@ -420,9 +462,9 @@ TEST_F(RPCDepositAuthorizedTest, DifferentAccountsNoDepositAuthFlag)
"destination_account": "{}",
"ledger_hash": "{}"
}})",
ACCOUNT,
ACCOUNT2,
LEDGERHASH
Account,
Account2,
LedgerHash
));
runSpawn([&, this](auto yield) {
@@ -448,18 +490,18 @@ TEST_F(RPCDepositAuthorizedTest, DifferentAccountsWithDepositAuthFlagReturnsFals
backend->setRange(10, 30);
auto ledgerHeader = CreateLedgerHeader(LEDGERHASH, 30);
auto ledgerHeader = CreateLedgerHeader(LedgerHash, 30);
ON_CALL(*backend, fetchLedgerByHash(ripple::uint256{LEDGERHASH}, _)).WillByDefault(Return(ledgerHeader));
ON_CALL(*backend, fetchLedgerByHash(ripple::uint256{LedgerHash}, _)).WillByDefault(Return(ledgerHeader));
EXPECT_CALL(*backend, fetchLedgerByHash).Times(1);
auto const account1Root = CreateAccountRootObject(ACCOUNT, 0, 2, 200, 2, INDEX1, 2);
auto const account2Root = CreateAccountRootObject(ACCOUNT2, ripple::lsfDepositAuth, 2, 200, 2, INDEX2, 2);
auto const account1Root = CreateAccountRootObject(Account, 0, 2, 200, 2, Index1, 2);
auto const account2Root = CreateAccountRootObject(Account2, ripple::lsfDepositAuth, 2, 200, 2, Index2, 2);
ON_CALL(*backend, doFetchLedgerObject(_, _, _)).WillByDefault(Return(std::nullopt));
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::account(GetAccountIDWithString(ACCOUNT)).key, _, _))
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::account(GetAccountIDWithString(Account)).key, _, _))
.WillByDefault(Return(account1Root.getSerializer().peekData()));
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::account(GetAccountIDWithString(ACCOUNT2)).key, _, _))
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::account(GetAccountIDWithString(Account2)).key, _, _))
.WillByDefault(Return(account2Root.getSerializer().peekData()));
EXPECT_CALL(*backend, doFetchLedgerObject).Times(3);
@@ -469,9 +511,9 @@ TEST_F(RPCDepositAuthorizedTest, DifferentAccountsWithDepositAuthFlagReturnsFals
"destination_account": "{}",
"ledger_hash": "{}"
}})",
ACCOUNT,
ACCOUNT2,
LEDGERHASH
Account,
Account2,
LedgerHash
));
runSpawn([&, this](auto yield) {
@@ -497,18 +539,18 @@ TEST_F(RPCDepositAuthorizedTest, DifferentAccountsWithDepositAuthFlagReturnsTrue
backend->setRange(10, 30);
auto ledgerHeader = CreateLedgerHeader(LEDGERHASH, 30);
auto ledgerHeader = CreateLedgerHeader(LedgerHash, 30);
ON_CALL(*backend, fetchLedgerByHash(ripple::uint256{LEDGERHASH}, _)).WillByDefault(Return(ledgerHeader));
ON_CALL(*backend, fetchLedgerByHash(ripple::uint256{LedgerHash}, _)).WillByDefault(Return(ledgerHeader));
EXPECT_CALL(*backend, fetchLedgerByHash).Times(1);
auto const account1Root = CreateAccountRootObject(ACCOUNT, 0, 2, 200, 2, INDEX1, 2);
auto const account2Root = CreateAccountRootObject(ACCOUNT2, ripple::lsfDepositAuth, 2, 200, 2, INDEX2, 2);
auto const account1Root = CreateAccountRootObject(Account, 0, 2, 200, 2, Index1, 2);
auto const account2Root = CreateAccountRootObject(Account2, ripple::lsfDepositAuth, 2, 200, 2, Index2, 2);
ON_CALL(*backend, doFetchLedgerObject(_, _, _)).WillByDefault(Return(std::optional<Blob>{{1, 2, 3}}));
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::account(GetAccountIDWithString(ACCOUNT)).key, _, _))
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::account(GetAccountIDWithString(Account)).key, _, _))
.WillByDefault(Return(account1Root.getSerializer().peekData()));
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::account(GetAccountIDWithString(ACCOUNT2)).key, _, _))
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::account(GetAccountIDWithString(Account2)).key, _, _))
.WillByDefault(Return(account2Root.getSerializer().peekData()));
EXPECT_CALL(*backend, doFetchLedgerObject).Times(3);
@@ -518,9 +560,9 @@ TEST_F(RPCDepositAuthorizedTest, DifferentAccountsWithDepositAuthFlagReturnsTrue
"destination_account": "{}",
"ledger_hash": "{}"
}})",
ACCOUNT,
ACCOUNT2,
LEDGERHASH
Account,
Account2,
LedgerHash
));
runSpawn([&, this](auto yield) {
@@ -531,3 +573,380 @@ TEST_F(RPCDepositAuthorizedTest, DifferentAccountsWithDepositAuthFlagReturnsTrue
EXPECT_EQ(*output.result, json::parse(expectedOut));
});
}
TEST_F(RPCDepositAuthorizedTest, CredentialAcceptedAndNotExpiredReturnsTrue)
{
static auto const expectedOut = fmt::format(
R"({{
"ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652",
"ledger_index": 30,
"validated": true,
"deposit_authorized": true,
"source_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"destination_account": "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun",
"credentials": ["{}"]
}})",
CredentialHash // CREDENTIALHASH should match credentialIndex
);
backend->setRange(10, 30);
auto ledgerHeader = CreateLedgerHeader(LedgerHash, 30);
EXPECT_CALL(*backend, fetchLedgerByHash(ripple::uint256{LedgerHash}, _)).WillOnce(Return(ledgerHeader));
auto const account1Root = CreateAccountRootObject(Account, 0, 2, 200, 2, Index1, 2);
auto const account2Root = CreateAccountRootObject(Account2, ripple::lsfDepositAuth, 2, 200, 2, Index2, 2);
auto const credential = CreateCredentialObject(Account, Account2, CredentialType);
auto const credentialIndex = ripple::keylet::credential(
GetAccountIDWithString(Account),
GetAccountIDWithString(Account2),
ripple::Slice(CredentialType.data(), CredentialType.size())
)
.key;
ON_CALL(*backend, doFetchLedgerObject(_, _, _)).WillByDefault(Return(std::optional<Blob>{{1, 2, 3}}));
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::account(GetAccountIDWithString(Account)).key, _, _))
.WillByDefault(Return(account1Root.getSerializer().peekData()));
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::account(GetAccountIDWithString(Account2)).key, _, _))
.WillByDefault(Return(account2Root.getSerializer().peekData()));
ON_CALL(*backend, doFetchLedgerObject(credentialIndex, _, _))
.WillByDefault(Return(credential.getSerializer().peekData()));
EXPECT_CALL(*backend, doFetchLedgerObject).Times(4);
auto const input = json::parse(fmt::format(
R"({{
"source_account": "{}",
"destination_account": "{}",
"ledger_hash": "{}",
"credentials": ["{}"]
}})",
Account,
Account2,
LedgerHash,
ripple::strHex(credentialIndex)
));
runSpawn([&, this](auto yield) {
auto const handler = AnyHandler{DepositAuthorizedHandler{backend}};
auto const output = handler.process(input, Context{yield});
ASSERT_TRUE(output);
EXPECT_EQ(*output.result, json::parse(expectedOut));
});
}
TEST_F(RPCDepositAuthorizedTest, CredentialNotAuthorizedReturnsFalse)
{
backend->setRange(10, 30);
auto ledgerHeader = CreateLedgerHeader(LedgerHash, 30);
EXPECT_CALL(*backend, fetchLedgerByHash(ripple::uint256{LedgerHash}, _)).WillOnce(Return(ledgerHeader));
auto const account1Root = CreateAccountRootObject(Account, 0, 2, 200, 2, Index1, 2);
auto const account2Root = CreateAccountRootObject(Account2, ripple::lsfDepositAuth, 2, 200, 2, Index2, 2);
auto const credential = CreateCredentialObject(Account, Account2, CredentialType, false);
auto const credentialIndex = ripple::keylet::credential(
GetAccountIDWithString(Account),
GetAccountIDWithString(Account2),
ripple::Slice(CredentialType.data(), CredentialType.size())
)
.key;
ON_CALL(*backend, doFetchLedgerObject(_, _, _)).WillByDefault(Return(std::optional<Blob>{{1, 2, 3}}));
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::account(GetAccountIDWithString(Account)).key, _, _))
.WillByDefault(Return(account1Root.getSerializer().peekData()));
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::account(GetAccountIDWithString(Account2)).key, _, _))
.WillByDefault(Return(account2Root.getSerializer().peekData()));
ON_CALL(*backend, doFetchLedgerObject(credentialIndex, _, _))
.WillByDefault(Return(credential.getSerializer().peekData()));
EXPECT_CALL(*backend, doFetchLedgerObject).Times(3);
auto const input = json::parse(fmt::format(
R"({{
"source_account": "{}",
"destination_account": "{}",
"ledger_hash": "{}",
"credentials": ["{}"]
}})",
Account,
Account2,
LedgerHash,
ripple::strHex(credentialIndex)
));
runSpawn([&, this](auto yield) {
auto const handler = AnyHandler{DepositAuthorizedHandler{backend}};
auto const output = handler.process(input, Context{yield});
ASSERT_FALSE(output);
auto const err = rpc::makeError(output.result.error());
EXPECT_EQ(err.at("error").as_string(), "badCredentials");
EXPECT_EQ(err.at("error_message").as_string(), "credentials aren't accepted");
});
}
TEST_F(RPCDepositAuthorizedTest, CredentialExpiredReturnsFalse)
{
backend->setRange(10, 30);
auto ledgerHeader = CreateLedgerHeader(LedgerHash, 30, 100);
// set parent close time to 500 seconds
ledgerHeader.parentCloseTime = ripple::NetClock::time_point{std::chrono::seconds{500}};
EXPECT_CALL(*backend, fetchLedgerByHash(ripple::uint256{LedgerHash}, _)).WillOnce(Return(ledgerHeader));
auto const account1Root = CreateAccountRootObject(Account, 0, 2, 200, 2, Index1, 2);
auto const account2Root = CreateAccountRootObject(Account2, ripple::lsfDepositAuth, 2, 200, 2, Index2, 2);
// credential expire time is 23 seconds, so credential will fail
auto const expiredCredential = CreateCredentialObject(Account, Account2, CredentialType, true, 23);
auto const credentialIndex = ripple::keylet::credential(
GetAccountIDWithString(Account),
GetAccountIDWithString(Account2),
ripple::Slice(CredentialType.data(), CredentialType.size())
)
.key;
ON_CALL(*backend, doFetchLedgerObject(_, _, _)).WillByDefault(Return(std::optional<Blob>{{1, 2, 3}}));
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::account(GetAccountIDWithString(Account)).key, _, _))
.WillByDefault(Return(account1Root.getSerializer().peekData()));
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::account(GetAccountIDWithString(Account2)).key, _, _))
.WillByDefault(Return(account2Root.getSerializer().peekData()));
ON_CALL(*backend, doFetchLedgerObject(credentialIndex, _, _))
.WillByDefault(Return(expiredCredential.getSerializer().peekData()));
EXPECT_CALL(*backend, doFetchLedgerObject).Times(3);
auto const input = json::parse(fmt::format(
R"({{
"source_account": "{}",
"destination_account": "{}",
"ledger_hash": "{}",
"credentials": ["{}"]
}})",
Account,
Account2,
LedgerHash,
ripple::strHex(credentialIndex)
));
runSpawn([&, this](auto yield) {
auto const handler = AnyHandler{DepositAuthorizedHandler{backend}};
auto const output = handler.process(input, Context{yield});
ASSERT_FALSE(output);
auto const err = rpc::makeError(output.result.error());
EXPECT_EQ(err.at("error").as_string(), "badCredentials");
EXPECT_EQ(err.at("error_message").as_string(), "credentials are expired");
});
}
TEST_F(RPCDepositAuthorizedTest, DuplicateCredentialsReturnsFalse)
{
backend->setRange(10, 30);
auto ledgerHeader = CreateLedgerHeader(LedgerHash, 30, 34);
EXPECT_CALL(*backend, fetchLedgerByHash(ripple::uint256{LedgerHash}, _)).WillOnce(Return(ledgerHeader));
auto const account1Root = CreateAccountRootObject(Account, 0, 2, 200, 2, Index1, 2);
auto const account2Root = CreateAccountRootObject(Account2, ripple::lsfDepositAuth, 2, 200, 2, Index2, 2);
auto const credential = CreateCredentialObject(Account, Account2, CredentialType);
auto const credentialIndex = ripple::keylet::credential(
GetAccountIDWithString(Account),
GetAccountIDWithString(Account2),
ripple::Slice(CredentialType.data(), CredentialType.size())
)
.key;
ON_CALL(*backend, doFetchLedgerObject(_, _, _)).WillByDefault(Return(std::optional<Blob>{{1, 2, 3}}));
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::account(GetAccountIDWithString(Account)).key, _, _))
.WillByDefault(Return(account1Root.getSerializer().peekData()));
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::account(GetAccountIDWithString(Account2)).key, _, _))
.WillByDefault(Return(account2Root.getSerializer().peekData()));
ON_CALL(*backend, doFetchLedgerObject(credentialIndex, _, _))
.WillByDefault(Return(credential.getSerializer().peekData()));
EXPECT_CALL(*backend, doFetchLedgerObject).Times(3);
auto const input = json::parse(fmt::format(
R"({{
"source_account": "{}",
"destination_account": "{}",
"ledger_hash": "{}",
"credentials": ["{}", "{}"]
}})",
Account,
Account2,
LedgerHash,
ripple::strHex(credentialIndex),
ripple::strHex(credentialIndex)
));
runSpawn([&, this](auto yield) {
auto const handler = AnyHandler{DepositAuthorizedHandler{backend}};
auto const output = handler.process(input, Context{yield});
ASSERT_FALSE(output);
auto const err = rpc::makeError(output.result.error());
EXPECT_EQ(err.at("error").as_string(), "badCredentials");
EXPECT_EQ(err.at("error_message").as_string(), "duplicates in credentials.");
});
}
TEST_F(RPCDepositAuthorizedTest, NoElementsInCredentialsReturnsFalse)
{
backend->setRange(10, 30);
auto ledgerHeader = CreateLedgerHeader(LedgerHash, 30, 34);
EXPECT_CALL(*backend, fetchLedgerByHash(ripple::uint256{LedgerHash}, _)).WillOnce(Return(ledgerHeader));
auto const account1Root = CreateAccountRootObject(Account, 0, 2, 200, 2, Index1, 2);
auto const account2Root = CreateAccountRootObject(Account2, ripple::lsfDepositAuth, 2, 200, 2, Index2, 2);
ON_CALL(*backend, doFetchLedgerObject(_, _, _)).WillByDefault(Return(std::optional<Blob>{{1, 2, 3}}));
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::account(GetAccountIDWithString(Account)).key, _, _))
.WillByDefault(Return(account1Root.getSerializer().peekData()));
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::account(GetAccountIDWithString(Account2)).key, _, _))
.WillByDefault(Return(account2Root.getSerializer().peekData()));
EXPECT_CALL(*backend, doFetchLedgerObject).Times(2);
auto const input = json::parse(fmt::format(
R"({{
"source_account": "{}",
"destination_account": "{}",
"ledger_hash": "{}",
"credentials": []
}})",
Account,
Account2,
LedgerHash
));
runSpawn([&, this](auto yield) {
auto const handler = AnyHandler{DepositAuthorizedHandler{backend}};
auto const output = handler.process(input, Context{yield});
ASSERT_FALSE(output);
auto const err = rpc::makeError(output.result.error());
EXPECT_EQ(err.at("error").as_string(), "invalidParams");
EXPECT_EQ(err.at("error_message").as_string(), "credential array has no elements.");
});
}
TEST_F(RPCDepositAuthorizedTest, MoreThanMaxNumberOfCredentialsReturnsFalse)
{
backend->setRange(10, 30);
auto ledgerHeader = CreateLedgerHeader(LedgerHash, 30, 34);
EXPECT_CALL(*backend, fetchLedgerByHash(ripple::uint256{LedgerHash}, _)).WillOnce(Return(ledgerHeader));
auto const account1Root = CreateAccountRootObject(Account, 0, 2, 200, 2, Index1, 2);
auto const account2Root = CreateAccountRootObject(Account2, ripple::lsfDepositAuth, 2, 200, 2, Index2, 2);
auto const credential = CreateCredentialObject(Account, Account2, CredentialType);
auto const credentialIndex = ripple::keylet::credential(
GetAccountIDWithString(Account),
GetAccountIDWithString(Account2),
ripple::Slice(CredentialType.data(), CredentialType.size())
)
.key;
ON_CALL(*backend, doFetchLedgerObject(_, _, _)).WillByDefault(Return(std::optional<Blob>{{1, 2, 3}}));
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::account(GetAccountIDWithString(Account)).key, _, _))
.WillByDefault(Return(account1Root.getSerializer().peekData()));
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::account(GetAccountIDWithString(Account2)).key, _, _))
.WillByDefault(Return(account2Root.getSerializer().peekData()));
ON_CALL(*backend, doFetchLedgerObject(credentialIndex, _, _))
.WillByDefault(Return(credential.getSerializer().peekData()));
EXPECT_CALL(*backend, doFetchLedgerObject).Times(2);
std::vector<std::string> credentials(9, ripple::strHex(credentialIndex));
auto const input = json::parse(fmt::format(
R"({{
"source_account": "{}",
"destination_account": "{}",
"ledger_hash": "{}",
"credentials": [{}]
}})",
Account,
Account2,
LedgerHash,
fmt::join(
credentials | std::views::transform([](std::string const& cred) { return fmt::format("\"{}\"", cred); }),
", "
)
));
runSpawn([&, this](auto yield) {
auto const handler = AnyHandler{DepositAuthorizedHandler{backend}};
auto const output = handler.process(input, Context{yield});
ASSERT_FALSE(output);
auto const err = rpc::makeError(output.result.error());
EXPECT_EQ(err.at("error").as_string(), "invalidParams");
EXPECT_EQ(err.at("error_message").as_string(), "credential array too long.");
});
}
TEST_F(RPCDepositAuthorizedTest, DifferenSubjectAccountForCredentialReturnsFalse)
{
backend->setRange(10, 30);
auto ledgerHeader = CreateLedgerHeader(LedgerHash, 30);
EXPECT_CALL(*backend, fetchLedgerByHash(ripple::uint256{LedgerHash}, _)).WillOnce(Return(ledgerHeader));
auto const account1Root = CreateAccountRootObject(Account, 0, 2, 200, 2, Index1, 2);
auto const account2Root = CreateAccountRootObject(Account2, ripple::lsfDepositAuth, 2, 200, 2, Index2, 2);
// reverse the subject and issuer account. Now subject is ACCOUNT2
auto const credential = CreateCredentialObject(Account2, Account, CredentialType);
auto const credentialIndex = ripple::keylet::credential(
GetAccountIDWithString(Account2),
GetAccountIDWithString(Account),
ripple::Slice(CredentialType.data(), CredentialType.size())
)
.key;
ON_CALL(*backend, doFetchLedgerObject(_, _, _)).WillByDefault(Return(std::optional<Blob>{{1, 2, 3}}));
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::account(GetAccountIDWithString(Account)).key, _, _))
.WillByDefault(Return(account1Root.getSerializer().peekData()));
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::account(GetAccountIDWithString(Account2)).key, _, _))
.WillByDefault(Return(account2Root.getSerializer().peekData()));
ON_CALL(*backend, doFetchLedgerObject(credentialIndex, _, _))
.WillByDefault(Return(credential.getSerializer().peekData()));
EXPECT_CALL(*backend, doFetchLedgerObject).Times(3);
auto const input = json::parse(fmt::format(
R"({{
"source_account": "{}",
"destination_account": "{}",
"ledger_hash": "{}",
"credentials": ["{}"]
}})",
Account,
Account2,
LedgerHash,
ripple::strHex(credentialIndex)
));
runSpawn([&, this](auto yield) {
auto const handler = AnyHandler{DepositAuthorizedHandler{backend}};
auto const output = handler.process(input, Context{yield});
ASSERT_FALSE(output);
auto const err = rpc::makeError(output.result.error());
EXPECT_EQ(err.at("error").as_string(), "badCredentials");
EXPECT_EQ(err.at("error_message").as_string(), "credentials don't belong to the root account");
});
}