Skip to content

Commit 7e24adb

Browse files
authored
fix: Address NFT interactions with trustlines (XRPLF#5297)
The changes are focused on fixing NFT transactions bypassing the trustline authorization requirement and potential invariant violation when interacting with deep frozen trustlines.
1 parent 621df42 commit 7e24adb

7 files changed

Lines changed: 969 additions & 96 deletions

File tree

include/xrpl/protocol/detail/features.macro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
// If you add an amendment here, then do not forget to increment `numFeatures`
3333
// in include/xrpl/protocol/Feature.h.
3434

35+
XRPL_FIX (EnforceNFTokenTrustlineV2, Supported::yes, VoteBehavior::DefaultNo)
3536
XRPL_FIX (AMMv1_3, Supported::yes, VoteBehavior::DefaultNo)
3637
XRPL_FEATURE(PermissionedDEX, Supported::yes, VoteBehavior::DefaultNo)
3738
XRPL_FEATURE(Batch, Supported::yes, VoteBehavior::DefaultNo)

src/test/app/Freeze_test.cpp

Lines changed: 97 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1885,6 +1885,31 @@ class Freeze_test : public beast::unit_test::suite
18851885
env.close();
18861886
}
18871887

1888+
// Testing A1 nft buy offer when A2 deep frozen by issuer
1889+
if (features[featureDeepFreeze] &&
1890+
features[fixEnforceNFTokenTrustlineV2])
1891+
{
1892+
env(trust(G1, A2["USD"](0), tfSetFreeze | tfSetDeepFreeze));
1893+
env.close();
1894+
1895+
uint256 const nftID{token::getNextID(env, A2, 0u, tfTransferable)};
1896+
env(token::mint(A2, 0), txflags(tfTransferable));
1897+
env.close();
1898+
1899+
auto const buyIdx = keylet::nftoffer(A1, env.seq(A1)).key;
1900+
env(token::createOffer(A1, nftID, USD(10)), token::owner(A2));
1901+
env.close();
1902+
1903+
env(token::acceptBuyOffer(A2, buyIdx), ter(tecFROZEN));
1904+
env.close();
1905+
1906+
env(trust(G1, A2["USD"](0), tfClearFreeze | tfClearDeepFreeze));
1907+
env.close();
1908+
1909+
env(token::acceptBuyOffer(A2, buyIdx));
1910+
env.close();
1911+
}
1912+
18881913
// Testing A2 nft offer sell when A2 frozen by currency holder
18891914
{
18901915
auto const sellOfferIndex = createNFTSellOffer(env, A2, USD(10));
@@ -1944,6 +1969,68 @@ class Freeze_test : public beast::unit_test::suite
19441969
env(trust(A2, limit, tfClearFreeze | tfClearDeepFreeze));
19451970
env.close();
19461971
}
1972+
1973+
// Testing brokered offer acceptance
1974+
if (features[featureDeepFreeze] &&
1975+
features[fixEnforceNFTokenTrustlineV2])
1976+
{
1977+
Account broker{"broker"};
1978+
env.fund(XRP(10000), broker);
1979+
env.close();
1980+
env(trust(G1, broker["USD"](1000), tfSetFreeze | tfSetDeepFreeze));
1981+
env.close();
1982+
1983+
uint256 const nftID{token::getNextID(env, A2, 0u, tfTransferable)};
1984+
env(token::mint(A2, 0), txflags(tfTransferable));
1985+
env.close();
1986+
1987+
uint256 const sellIdx = keylet::nftoffer(A2, env.seq(A2)).key;
1988+
env(token::createOffer(A2, nftID, USD(10)), txflags(tfSellNFToken));
1989+
env.close();
1990+
auto const buyIdx = keylet::nftoffer(A1, env.seq(A1)).key;
1991+
env(token::createOffer(A1, nftID, USD(11)), token::owner(A2));
1992+
env.close();
1993+
1994+
env(token::brokerOffers(broker, buyIdx, sellIdx),
1995+
token::brokerFee(USD(1)),
1996+
ter(tecFROZEN));
1997+
env.close();
1998+
}
1999+
2000+
// Testing transfer fee
2001+
if (features[featureDeepFreeze] &&
2002+
features[fixEnforceNFTokenTrustlineV2])
2003+
{
2004+
Account minter{"minter"};
2005+
env.fund(XRP(10000), minter);
2006+
env.close();
2007+
env(trust(G1, minter["USD"](1000)));
2008+
env.close();
2009+
2010+
uint256 const nftID{
2011+
token::getNextID(env, minter, 0u, tfTransferable, 1u)};
2012+
env(token::mint(minter, 0),
2013+
token::xferFee(1u),
2014+
txflags(tfTransferable));
2015+
env.close();
2016+
2017+
uint256 const minterSellIdx =
2018+
keylet::nftoffer(minter, env.seq(minter)).key;
2019+
env(token::createOffer(minter, nftID, drops(1)),
2020+
txflags(tfSellNFToken));
2021+
env.close();
2022+
env(token::acceptSellOffer(A2, minterSellIdx));
2023+
env.close();
2024+
2025+
uint256 const sellIdx = keylet::nftoffer(A2, env.seq(A2)).key;
2026+
env(token::createOffer(A2, nftID, USD(100)),
2027+
txflags(tfSellNFToken));
2028+
env.close();
2029+
env(trust(G1, minter["USD"](1000), tfSetFreeze | tfSetDeepFreeze));
2030+
env.close();
2031+
env(token::acceptSellOffer(A1, sellIdx), ter(tecFROZEN));
2032+
env.close();
2033+
}
19472034
}
19482035

19492036
// Helper function to extract trustline flags from open ledger
@@ -2021,10 +2108,16 @@ class Freeze_test : public beast::unit_test::suite
20212108
using namespace test::jtx;
20222109
auto const sa = supported_amendments();
20232110
testAll(
2024-
sa - featureFlowCross - featureDeepFreeze - featurePermissionedDEX);
2025-
testAll(sa - featureFlowCross - featurePermissionedDEX);
2026-
testAll(sa - featureDeepFreeze - featurePermissionedDEX);
2027-
testAll(sa - featurePermissionedDEX);
2111+
sa - featureFlowCross - featureDeepFreeze - featurePermissionedDEX -
2112+
fixEnforceNFTokenTrustlineV2);
2113+
testAll(
2114+
sa - featureFlowCross - featurePermissionedDEX -
2115+
fixEnforceNFTokenTrustlineV2);
2116+
testAll(
2117+
sa - featureDeepFreeze - featurePermissionedDEX -
2118+
fixEnforceNFTokenTrustlineV2);
2119+
testAll(sa - featurePermissionedDEX - fixEnforceNFTokenTrustlineV2);
2120+
testAll(sa - fixEnforceNFTokenTrustlineV2);
20282121
testAll(sa);
20292122
}
20302123
};

0 commit comments

Comments
 (0)