feat: Add support for deep freeze (#1875)

fixes #1826
This commit is contained in:
Peter Chen
2025-03-05 11:04:44 -05:00
committed by GitHub
parent 5d2694d36c
commit 8a08c5e6ce
7 changed files with 527 additions and 26 deletions

View File

@@ -902,6 +902,26 @@ isGlobalFrozen(
return sle.isFlag(ripple::lsfGlobalFreeze);
}
bool
fetchAndCheckAnyFlagsExists(
BackendInterface const& backend,
std::uint32_t sequence,
ripple::Keylet const& keylet,
std::vector<std::uint32_t> const& flags,
boost::asio::yield_context yield
)
{
auto const blob = backend.fetchLedgerObject(keylet.key, sequence, yield);
if (!blob)
return false;
ripple::SerialIter it{blob->data(), blob->size()};
ripple::SLE const sle{it, keylet.key};
return std::ranges::any_of(flags, [sle](std::uint32_t flag) { return sle.isFlag(flag); });
}
bool
isFrozen(
BackendInterface const& backend,
@@ -915,35 +935,43 @@ isFrozen(
if (ripple::isXRP(currency))
return false;
auto key = ripple::keylet::account(issuer).key;
auto blob = backend.fetchLedgerObject(key, sequence, yield);
if (!blob)
return false;
ripple::SerialIter it{blob->data(), blob->size()};
ripple::SLE const sle{it, key};
if (sle.isFlag(ripple::lsfGlobalFreeze))
if (fetchAndCheckAnyFlagsExists(
backend, sequence, ripple::keylet::account(issuer), {ripple::lsfGlobalFreeze}, yield
))
return true;
if (issuer != account) {
key = ripple::keylet::line(account, issuer, currency).key;
blob = backend.fetchLedgerObject(key, sequence, yield);
auto const trustLineKeylet = ripple::keylet::line(account, issuer, currency);
return issuer != account &&
fetchAndCheckAnyFlagsExists(
backend,
sequence,
trustLineKeylet,
{(issuer > account) ? ripple::lsfHighFreeze : ripple::lsfLowFreeze},
yield
);
}
if (!blob)
return false;
bool
isDeepFrozen(
BackendInterface const& backend,
std::uint32_t sequence,
ripple::AccountID const& account,
ripple::Currency const& currency,
ripple::AccountID const& issuer,
boost::asio::yield_context yield
)
{
if (ripple::isXRP(currency))
return false;
ripple::SerialIter issuerIt{blob->data(), blob->size()};
ripple::SLE const issuerLine{issuerIt, key};
if (issuer == account)
return false;
auto frozen = (issuer > account) ? ripple::lsfHighFreeze : ripple::lsfLowFreeze;
auto const trustLineKeylet = ripple::keylet::line(account, issuer, currency);
if (issuerLine.isFlag(frozen))
return true;
}
return false;
return fetchAndCheckAnyFlagsExists(
backend, sequence, trustLineKeylet, {ripple::lsfHighDeepFreeze, ripple::lsfLowDeepFreeze}, yield
);
}
bool
@@ -1040,7 +1068,9 @@ ammAccountHolds(
ripple::SerialIter it{blob->data(), blob->size()};
ripple::SLE const sle{it, key};
if (zeroIfFrozen && isFrozen(backend, sequence, account, currency, issuer, yield)) {
if (zeroIfFrozen &&
(isFrozen(backend, sequence, account, currency, issuer, yield) ||
isDeepFrozen(backend, sequence, account, currency, issuer, yield))) {
amount.setIssue(ripple::Issue(currency, issuer));
amount.clear();
} else {

View File

@@ -428,6 +428,49 @@ isFrozen(
boost::asio::yield_context yield
);
/**
* @brief Fetches a ledger object and checks if any of the specified flag is set on the account.
*
* @param backend The backend to use
* @param sequence The sequence
* @param keylet The keylet representing the object
* @param flags The flags to check on the fetched `SLE`.
* @param yield The coroutine context
* @return true if any of the flag in flags are set for this account; false otherwise
*/
bool
fetchAndCheckAnyFlagsExists(
BackendInterface const& backend,
std::uint32_t sequence,
ripple::Keylet const& keylet,
std::vector<std::uint32_t> const& flags,
boost::asio::yield_context yield
);
/**
* @brief Whether the trustline is deep frozen.
*
* For deep freeze, (unlike regular freeze) we do not care which account has the high/low deep freeze flag.
* We only care about if the trustline is deep frozen or not.
*
* @param backend The backend to use
* @param sequence The sequence
* @param account The account
* @param currency The currency
* @param issuer The issuer
* @param yield The coroutine context
* @return true if the account is deep frozen; false otherwise
*/
bool
isDeepFrozen(
BackendInterface const& backend,
std::uint32_t sequence,
ripple::AccountID const& account,
ripple::Currency const& currency,
ripple::AccountID const& issuer,
boost::asio::yield_context yield
);
/**
* @brief Whether the account that owns a LPToken is frozen for the assets in the pool
*

View File

@@ -86,6 +86,8 @@ AccountLinesHandler::addLine(
bool const lineNoRipplePeer = (flags & (not viewLowest ? ripple::lsfLowNoRipple : ripple::lsfHighNoRipple)) != 0u;
bool const lineFreeze = (flags & (viewLowest ? ripple::lsfLowFreeze : ripple::lsfHighFreeze)) != 0u;
bool const lineFreezePeer = (flags & (not viewLowest ? ripple::lsfLowFreeze : ripple::lsfHighFreeze)) != 0u;
bool const lineDeepFreeze = (flags & (viewLowest ? ripple::lsfLowDeepFreeze : ripple::lsfHighFreeze)) != 0u;
bool const lineDeepFreezePeer = (flags & (not viewLowest ? ripple::lsfLowDeepFreeze : ripple::lsfHighFreeze)) != 0u;
ripple::STAmount const& saBalance = balance;
ripple::STAmount const& saLimit = lineLimit;
@@ -112,6 +114,12 @@ AccountLinesHandler::addLine(
if (lineFreezePeer)
line.freezePeer = true;
if (lineDeepFreeze)
line.deepFreeze = true;
if (lineDeepFreezePeer)
line.deepFreezePeer = true;
line.noRipple = lineNoRipple;
line.noRipplePeer = lineNoRipplePeer;
lines.push_back(line);
@@ -264,6 +272,12 @@ tag_invoke(
if (line.freezePeer)
obj[JS(freeze_peer)] = *(line.freezePeer);
if (line.deepFreeze)
obj[JS(deep_freeze)] = *(line.deepFreeze);
if (line.deepFreezePeer)
obj[JS(deep_freeze_peer)] = *(line.deepFreezePeer);
jv = std::move(obj);
}

View File

@@ -76,6 +76,8 @@ public:
std::optional<bool> peerAuthorized;
std::optional<bool> freeze;
std::optional<bool> freezePeer;
std::optional<bool> deepFreeze;
std::optional<bool> deepFreezePeer;
};
/**

View File

@@ -50,6 +50,7 @@
#include <array>
#include <cstddef>
#include <cstdint>
#include <optional>
#include <stdexcept>
#include <string>
#include <string_view>
@@ -67,6 +68,8 @@ constexpr auto kACCOUNT2 = "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun";
constexpr auto kINDEX1 = "E6DBAFC99223B42257915A63DFC6B0C032D4070F9A574B255AD97466726FC321";
constexpr auto kINDEX2 = "E6DBAFC99223B42257915A63DFC6B0C032D4070F9A574B255AD97466726FC322";
constexpr auto kTXN_ID = "E6DBAFC99223B42257915A63DFC6B0C032D4070F9A574B255AD97466726FC321";
constexpr auto kLEDGER_SEQ_OBJECT = 50;
constexpr auto kCURRENCY = "0158415500000000C1F76FF6ECB0BAC600000000";
constexpr auto kAMM_ACCOUNT = "rnW8FAPgpQgA6VoESnVrUVJHBdq9QAtRZs";
constexpr auto kISSUER = "rK9DrarGKnVEo2nYp5MfVRXRYf5yRX3mwD";
constexpr auto kLPTOKEN_CURRENCY = "037C35306B24AAB7FF90848206E003279AA47090";
@@ -78,6 +81,7 @@ class RPCHelpersTest : public util::prometheus::WithPrometheus, public MockBacke
void
SetUp() override
{
backend_->setRange(10, 300);
SyncAsioContextTest::SetUp();
}
void
@@ -558,6 +562,277 @@ TEST_F(RPCHelpersTest, ParseIssue)
);
}
TEST_F(RPCHelpersTest, FetchAndCheckAnyFlagExists_BlobDoesNotExist)
{
auto const account = getAccountIdWithString(kACCOUNT);
auto const issuerKey = ripple::keylet::account(account);
// returns empty blob
ON_CALL(*backend_, doFetchLedgerObject(issuerKey.key, kLEDGER_SEQ_OBJECT, _))
.WillByDefault(Return(std::optional<Blob>{}));
runSpawn([&](boost::asio::yield_context yield) {
// return false: blob doesn't exist
EXPECT_FALSE(
fetchAndCheckAnyFlagsExists(*backend_, kLEDGER_SEQ_OBJECT, issuerKey, {ripple::lsfHighDeepFreeze}, yield)
);
});
}
TEST_F(RPCHelpersTest, FetchAndCheckAnyFlagExistsBlobExists_AccountWithCorrectFlag)
{
auto const account = getAccountIdWithString(kACCOUNT);
auto const issuerKey = ripple::keylet::account(account);
// create account with highDeepFreeze Flag
auto const accountObject = createAccountRootObject(kACCOUNT, ripple::lsfHighDeepFreeze, 1, 10, 2, kTXN_ID, 3);
ON_CALL(*backend_, doFetchLedgerObject(issuerKey.key, kLEDGER_SEQ_OBJECT, _))
.WillByDefault(Return(accountObject.getSerializer().peekData()));
runSpawn([&](boost::asio::yield_context yield) {
// returns true: accountObject has the highDeepFreeze flag
EXPECT_TRUE(
fetchAndCheckAnyFlagsExists(*backend_, kLEDGER_SEQ_OBJECT, issuerKey, {ripple::lsfHighDeepFreeze}, yield)
);
});
}
TEST_F(RPCHelpersTest, FetchAndCheckAnyFlagExistsBlobExists_AccountFlagDoesNotExist)
{
auto const account = getAccountIdWithString(kACCOUNT);
auto const issuerKey = ripple::keylet::account(account);
// create account with highDeepFreeze Flag
auto const accountObject = createAccountRootObject(kACCOUNT, ripple::lsfLowDeepFreeze, 1, 10, 2, kTXN_ID, 3);
ON_CALL(*backend_, doFetchLedgerObject(issuerKey.key, kLEDGER_SEQ_OBJECT, _))
.WillByDefault(Return(accountObject.getSerializer().peekData()));
runSpawn([&](boost::asio::yield_context yield) {
// returns false: accountObject has the lowDeepFreeze flag
EXPECT_FALSE(
fetchAndCheckAnyFlagsExists(*backend_, kLEDGER_SEQ_OBJECT, issuerKey, {ripple::lsfHighDeepFreeze}, yield)
);
});
}
TEST_F(RPCHelpersTest, isGlobalFrozen_AccountIsGlobalFrozen)
{
auto const account = getAccountIdWithString(kACCOUNT);
auto const issuerKey = ripple::keylet::account(account);
auto const accountObject = createAccountRootObject(kACCOUNT, ripple::lsfGlobalFreeze, 1, 10, 2, kTXN_ID, 3);
ON_CALL(*backend_, doFetchLedgerObject(issuerKey.key, kLEDGER_SEQ_OBJECT, _))
.WillByDefault(Return(accountObject.getSerializer().peekData()));
runSpawn([&](boost::asio::yield_context yield) {
// returns false: accountObject has the lowDeepFreeze flag
EXPECT_TRUE(isGlobalFrozen(*backend_, kLEDGER_SEQ_OBJECT, account, yield));
});
}
TEST_F(RPCHelpersTest, isDeepFrozen_TrustLineIsDeepFrozen)
{
auto const account = getAccountIdWithString(kACCOUNT);
auto const account2 = getAccountIdWithString(kACCOUNT2);
// create a trustline between account and account2 and is deep frozen
auto const trustLineKey = ripple::keylet::line(account, account2, ripple::Currency{kCURRENCY}).key;
auto const trustlineDeepFrozen = createRippleStateLedgerObject(
"USD", kACCOUNT, 8, kACCOUNT, 1000, kACCOUNT2, 2000, kINDEX1, 2, ripple::lsfLowDeepFreeze
);
ON_CALL(*backend_, doFetchLedgerObject(trustLineKey, kLEDGER_SEQ_OBJECT, _))
.WillByDefault(Return(trustlineDeepFrozen.getSerializer().peekData()));
runSpawn([&](boost::asio::yield_context yield) {
EXPECT_TRUE(isDeepFrozen(*backend_, kLEDGER_SEQ_OBJECT, account, ripple::Currency{kCURRENCY}, account2, yield));
});
}
TEST_F(RPCHelpersTest, isDeepFrozen_TrustLineIsNotDeepFrozen)
{
auto const account = getAccountIdWithString(kACCOUNT);
auto const account2 = getAccountIdWithString(kACCOUNT2);
// create a trustline between account and account2 that is frozen (NOT DeepFrozen)
auto const trustLineKey = ripple::keylet::line(account, account2, ripple::Currency{kCURRENCY}).key;
auto const trustlineFrozen = createRippleStateLedgerObject(
"USD", kACCOUNT, 8, kACCOUNT, 1000, kACCOUNT2, 2000, kINDEX1, 2, ripple::lsfLowFreeze
);
ON_CALL(*backend_, doFetchLedgerObject(trustLineKey, kLEDGER_SEQ_OBJECT, _))
.WillByDefault(Return(trustlineFrozen.getSerializer().peekData()));
runSpawn([&](boost::asio::yield_context yield) {
EXPECT_FALSE(isDeepFrozen(*backend_, kLEDGER_SEQ_OBJECT, account, ripple::Currency{kCURRENCY}, account2, yield)
);
});
}
TEST_F(RPCHelpersTest, isDeepFrozen_IssuerAndAccountIsSameWillNotBeDeepFrozen)
{
auto const account = getAccountIdWithString(kACCOUNT);
auto const issuer = getAccountIdWithString(kACCOUNT2);
auto const trustLineKey = ripple::keylet::line(account, issuer, ripple::Currency{kCURRENCY}).key;
auto const trustlineDeepFrozen = createRippleStateLedgerObject(
"USD", kACCOUNT, 8, kACCOUNT, 1000, kACCOUNT2, 2000, kINDEX1, 2, ripple::lsfLowDeepFreeze
);
ON_CALL(*backend_, doFetchLedgerObject(trustLineKey, kLEDGER_SEQ_OBJECT, _))
.WillByDefault(Return(trustlineDeepFrozen.getSerializer().peekData()));
runSpawn([&](boost::asio::yield_context yield) {
// both accounts are same so trustline is not deep frozen
EXPECT_FALSE(isDeepFrozen(*backend_, kLEDGER_SEQ_OBJECT, account, ripple::Currency{kCURRENCY}, account, yield));
});
}
TEST_F(RPCHelpersTest, isFrozen_IssuerAccountIsGlobalFrozen)
{
auto const account = getAccountIdWithString(kACCOUNT);
auto const issuer = getAccountIdWithString(kACCOUNT2);
auto const accountObject = createAccountRootObject(kACCOUNT2, ripple::lsfGlobalFreeze, 1, 10, 2, kTXN_ID, 3);
auto const issuerKey = ripple::keylet::account(issuer).key;
ON_CALL(*backend_, doFetchLedgerObject(issuerKey, kLEDGER_SEQ_OBJECT, _))
.WillByDefault(Return(accountObject.getSerializer().peekData()));
runSpawn([&](boost::asio::yield_context yield) {
EXPECT_TRUE(isFrozen(*backend_, kLEDGER_SEQ_OBJECT, account, ripple::Currency{kCURRENCY}, issuer, yield));
});
}
TEST_F(RPCHelpersTest, isFrozen_IssuerAndAccountIsSameWillNotBeFrozen)
{
auto const account = getAccountIdWithString(kACCOUNT);
auto const issuer = getAccountIdWithString(kACCOUNT2);
auto const trustLineKey = ripple::keylet::line(account, issuer, ripple::Currency{kCURRENCY}).key;
auto const trustlineDeepFrozen = createRippleStateLedgerObject(
"USD", kACCOUNT, 8, kACCOUNT, 1000, kACCOUNT2, 2000, kINDEX1, 2, ripple::lsfHighFreeze
);
ON_CALL(*backend_, doFetchLedgerObject(trustLineKey, kLEDGER_SEQ_OBJECT, _))
.WillByDefault(Return(trustlineDeepFrozen.getSerializer().peekData()));
runSpawn([&](boost::asio::yield_context yield) {
EXPECT_FALSE(isFrozen(*backend_, kLEDGER_SEQ_OBJECT, account, ripple::Currency{kCURRENCY}, account, yield));
});
}
TEST_F(RPCHelpersTest, isFrozen_IssuerTrustLineIsFrozen)
{
auto const account = getAccountIdWithString(kACCOUNT);
auto const issuer = getAccountIdWithString(kACCOUNT2);
ripple::Currency const currency{kCURRENCY};
auto const trustLineKey = ripple::keylet::line(account, issuer, currency).key;
// issuer is higher than account, so the correct flag to set is High freeze
auto const trustlineFrozen = createRippleStateLedgerObject(
"USD", kACCOUNT, 8, kACCOUNT, 1000, kACCOUNT2, 2000, kINDEX1, 2, ripple::lsfHighFreeze
);
ON_CALL(*backend_, doFetchLedgerObject(trustLineKey, kLEDGER_SEQ_OBJECT, _))
.WillByDefault(Return(trustlineFrozen.getSerializer().peekData()));
runSpawn([&](boost::asio::yield_context yield) {
EXPECT_TRUE(isFrozen(*backend_, kLEDGER_SEQ_OBJECT, account, currency, issuer, yield));
});
}
TEST_F(RPCHelpersTest, isFrozen_IssuerWithLowFreezeIsNotFrozen)
{
auto const account = getAccountIdWithString(kACCOUNT);
auto const issuer = getAccountIdWithString(kACCOUNT2);
ripple::Currency const currency{kCURRENCY};
auto const trustLineKey = ripple::keylet::line(account, issuer, currency).key;
// issuer is higher than account, but the flag set here is low freeze
auto const trustlineFrozen = createRippleStateLedgerObject(
"USD", kACCOUNT, 8, kACCOUNT, 1000, kACCOUNT2, 2000, kINDEX1, 2, ripple::lsfLowFreeze
);
ON_CALL(*backend_, doFetchLedgerObject(trustLineKey, kLEDGER_SEQ_OBJECT, _))
.WillByDefault(Return(trustlineFrozen.getSerializer().peekData()));
runSpawn([&](boost::asio::yield_context yield) {
EXPECT_FALSE(isFrozen(*backend_, kLEDGER_SEQ_OBJECT, account, currency, issuer, yield));
});
}
TEST_F(RPCHelpersTest, AccountHolds_TrustLineNotfrozen)
{
auto const account = getAccountIdWithString(kACCOUNT);
auto const issuer = getAccountIdWithString(kACCOUNT2);
ripple::Currency const currency{kCURRENCY};
auto const trustLineKey = ripple::keylet::line(account, issuer, currency).key;
auto const trustLine =
createRippleStateLedgerObject(kCURRENCY, kACCOUNT2, 500, kACCOUNT, 1000, kACCOUNT2, 1000, kTXN_ID, 1, 0);
EXPECT_CALL(*backend_, doFetchLedgerObject(trustLineKey, kLEDGER_SEQ_OBJECT, _))
.WillOnce(Return(trustLine.getSerializer().peekData()));
runSpawn([&](boost::asio::yield_context yield) {
auto const result = accountHolds(
*backend_, *mockAmendmentCenterPtr_, kLEDGER_SEQ_OBJECT, account, currency, issuer, false, yield
);
// Check issuer has a balance of 500
EXPECT_EQ(result, ripple::STAmount(getIssue(kCURRENCY, kACCOUNT2), 500));
});
}
TEST_F(RPCHelpersTest, AccountHolds_NoTrustLine)
{
auto const account = getAccountIdWithString(kACCOUNT);
auto const issuer = getAccountIdWithString(kACCOUNT2);
ripple::Currency const currency{kCURRENCY};
auto const key = ripple::keylet::line(account, issuer, currency).key;
// return no trustline found
EXPECT_CALL(*backend_, doFetchLedgerObject(key, kLEDGER_SEQ_OBJECT, _)).WillOnce(Return(std::nullopt));
runSpawn([&](boost::asio::yield_context yield) {
auto const result = accountHolds(
*backend_, *mockAmendmentCenterPtr_, kLEDGER_SEQ_OBJECT, account, currency, issuer, false, yield
);
// balance is 0 as trustline is frozen
EXPECT_EQ(result, ripple::STAmount(getIssue(kCURRENCY, kACCOUNT2), 0));
});
}
TEST_F(RPCHelpersTest, AccountHolds_TrustLineButFrozen)
{
auto const account = getAccountIdWithString(kACCOUNT);
auto const issuer = getAccountIdWithString(kACCOUNT2);
ripple::Currency const currency{kCURRENCY};
// balance of 500, but trustline is frozen
auto const trustLineKey = ripple::keylet::line(account, issuer, currency).key;
auto const trustLine = createRippleStateLedgerObject(
kCURRENCY, kACCOUNT2, 500, kACCOUNT, 1000, kACCOUNT2, 1000, kTXN_ID, 1, ripple::lsfHighFreeze
);
ON_CALL(*backend_, doFetchLedgerObject(trustLineKey, kLEDGER_SEQ_OBJECT, _))
.WillByDefault(Return(trustLine.getSerializer().peekData()));
runSpawn([&](boost::asio::yield_context yield) {
auto const result = accountHolds(
*backend_, *mockAmendmentCenterPtr_, kLEDGER_SEQ_OBJECT, account, currency, issuer, true, yield
);
EXPECT_EQ(result, ripple::STAmount(getIssue(kCURRENCY, kACCOUNT2), 0));
});
}
TEST_F(RPCHelpersTest, AccountHoldsFixLPTAmendmentDisabled)
{
auto ammAccount = getAccountIdWithString(kAMM_ACCOUNT);

View File

@@ -739,7 +739,8 @@ TEST_F(RPCAccountLinesHandlerTest, OptionalResponseField)
"no_ripple": false,
"no_ripple_peer": true,
"peer_authorized": true,
"freeze_peer": true
"freeze_peer": true,
"deep_freeze_peer": true
},
{
"account": "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun",
@@ -752,7 +753,8 @@ TEST_F(RPCAccountLinesHandlerTest, OptionalResponseField)
"no_ripple": true,
"no_ripple_peer": false,
"authorized": true,
"freeze": true
"freeze": true,
"deep_freeze": true
}
]
})";
@@ -783,12 +785,14 @@ TEST_F(RPCAccountLinesHandlerTest, OptionalResponseField)
line1.setFlag(ripple::lsfHighAuth);
line1.setFlag(ripple::lsfHighNoRipple);
line1.setFlag(ripple::lsfHighFreeze);
line1.setFlag(ripple::lsfHighDeepFreeze);
bbs.push_back(line1.getSerializer().peekData());
auto line2 = createRippleStateLedgerObject("USD", kACCOUNT2, 20, kACCOUNT, 200, kACCOUNT2, 400, kTXN_ID, 0);
line2.setFlag(ripple::lsfLowAuth);
line2.setFlag(ripple::lsfLowNoRipple);
line2.setFlag(ripple::lsfLowFreeze);
line2.setFlag(ripple::lsfLowDeepFreeze);
bbs.push_back(line2.getSerializer().peekData());
ON_CALL(*backend_, doFetchLedgerObjects).WillByDefault(Return(bbs));

View File

@@ -1156,6 +1156,71 @@ generateNormalPathBookOffersTestBundles()
},
.ledgerObjectCalls = 6,
.mockedOffers = std::vector<ripple::STObject>{gets10USDPays20XRPOffer},
.expectedJson = fmt::format(
R"({{
"ledger_hash":"{}",
"ledger_index":300,
"offers":
[
{{
"Account":"{}",
"BookDirectory":"{}",
"BookNode":"0",
"Flags":0,
"LedgerEntryType":"Offer",
"OwnerNode":"0",
"PreviousTxnID":"0000000000000000000000000000000000000000000000000000000000000000",
"PreviousTxnLgrSeq":0,
"Sequence":0,
"TakerPays":"20",
"TakerGets":{{
"currency":"USD",
"issuer":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"value":"10"
}},
"index":"E6DBAFC99223B42257915A63DFC6B0C032D4070F9A574B255AD97466726FC321",
"owner_funds":"{}",
"quality":"{}",
"taker_gets_funded":{{
"currency":"USD",
"issuer":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"value":"0"
}},
"taker_pays_funded":"0"
}}
]
}})",
kLEDGER_HASH,
kACCOUNT2,
kPAYS20_XRP_GETS10_USD_BOOK_DIR,
0,
2
),
},
BookOffersNormalTestBundle{
.testName = "PaysXRPGetsUSDIsDeepFrozen",
.inputJson = paysXRPGetsUSDInputJson,
// prepare offer dir index
.mockedSuccessors =
std::map<ripple::uint256, std::optional<ripple::uint256>>{
{getsUSDPaysXRPBook, ripple::uint256{kPAYS20_XRP_GETS10_USD_BOOK_DIR}},
{ripple::uint256{kPAYS20_XRP_GETS10_USD_BOOK_DIR}, std::optional<ripple::uint256>{}}
},
.mockedLedgerObjects =
std::map<ripple::uint256, ripple::Blob>{
// book dir object
{ripple::uint256{kPAYS20_XRP_GETS10_USD_BOOK_DIR},
createOwnerDirLedgerObject({ripple::uint256{kINDEX2}}, kINDEX1).getSerializer().peekData()},
// gets issuer account object, is deep frozen so unfunded
{ripple::keylet::account(account).key,
createAccountRootObject(
kACCOUNT, ripple::lsfLowDeepFreeze, 2, 200, 2, kINDEX1, 2, kTRANSFER_RATE_X2
)
.getSerializer()
.peekData()},
},
.ledgerObjectCalls = 4,
.mockedOffers = std::vector<ripple::STObject>{gets10USDPays20XRPOffer},
.expectedJson = fmt::format(
R"({{
"ledger_hash":"{}",
@@ -1197,6 +1262,74 @@ generateNormalPathBookOffersTestBundles()
2
)
},
BookOffersNormalTestBundle{
.testName = "PaysXRPGetsUSDTrustLineFrozenAndIsDeepFrozen",
.inputJson = paysXRPGetsUSDInputJson,
// prepare offer dir index
.mockedSuccessors =
std::map<ripple::uint256, std::optional<ripple::uint256>>{
{getsUSDPaysXRPBook, ripple::uint256{kPAYS20_XRP_GETS10_USD_BOOK_DIR}},
{ripple::uint256{kPAYS20_XRP_GETS10_USD_BOOK_DIR}, std::optional<ripple::uint256>{}}
},
.mockedLedgerObjects =
std::map<ripple::uint256, ripple::Blob>{
// book dir object
{ripple::uint256{kPAYS20_XRP_GETS10_USD_BOOK_DIR},
createOwnerDirLedgerObject({ripple::uint256{kINDEX2}}, kINDEX1).getSerializer().peekData()},
// gets issuer account object, is deep frozen so unfunded
{ripple::keylet::account(account).key,
createAccountRootObject(
kACCOUNT, ripple::lsfLowDeepFreeze, 2, 200, 2, kINDEX1, 2, kTRANSFER_RATE_X2
)
.getSerializer()
.peekData()},
{ripple::keylet::line(account2, account, ripple::to_currency("USD")).key,
frozenTrustLine.getSerializer().peekData()},
},
.ledgerObjectCalls = 6,
.mockedOffers = std::vector<ripple::STObject>{gets10USDPays20XRPOffer},
.expectedJson = fmt::format(
R"({{
"ledger_hash":"{}",
"ledger_index":300,
"offers":
[
{{
"Account":"{}",
"BookDirectory":"{}",
"BookNode":"0",
"Flags":0,
"LedgerEntryType":"Offer",
"OwnerNode":"0",
"PreviousTxnID":"0000000000000000000000000000000000000000000000000000000000000000",
"PreviousTxnLgrSeq":0,
"Sequence":0,
"TakerPays":"20",
"TakerGets":{{
"currency":"USD",
"issuer":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"value":"10"
}},
"index":"E6DBAFC99223B42257915A63DFC6B0C032D4070F9A574B255AD97466726FC321",
"owner_funds":"{}",
"quality":"{}",
"taker_gets_funded":{{
"currency":"USD",
"issuer":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"value":"0"
}},
"taker_pays_funded":"0"
}}
]
}})",
kLEDGER_HASH,
kACCOUNT2,
kPAYS20_XRP_GETS10_USD_BOOK_DIR,
0,
2
)
}
};
}