feat: Support Permissioned DEX (#2152)

Fix: https://github.com/XRPLF/clio/issues/2143

Will add tests
This commit is contained in:
Ayaz Salikhov
2025-06-06 17:12:18 +01:00
committed by GitHub
parent 2bf582839e
commit 13524be6cc
11 changed files with 230 additions and 24 deletions

View File

@@ -60,15 +60,14 @@ constexpr auto kLEDGER_HASH = "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF2
constexpr auto kINDEX1 = "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC";
constexpr auto kINDEX2 = "E6DBAFC99223B42257915A63DFC6B0C032D4070F9A574B255AD97466726FC321";
// 20 USD : 10 XRP
constexpr auto kPAYS20_USD_GETS10_XRP_BOOK_DIR = "43B83ADC452B85FCBADA6CAEAC5181C255A213630D58FFD455071AFD498D0000";
// 20 XRP : 10 USD
constexpr auto kPAYS20_XRP_GETS10_USD_BOOK_DIR = "7B1767D41DBCE79D9585CF9D0262A5FEC45E5206FF524F8B55071AFD498D0000";
// transfer rate x2
constexpr auto kTRANSFER_RATE_X2 = 2000000000;
constexpr auto kDOMAIN = "F10D0CC9A0F9A3CBF585B80BE09A186483668FDBDD39AA7E3370F3649CE134E5";
struct ParameterTestBundle {
std::string testName;
std::string testJson;
@@ -314,6 +313,57 @@ generateParameterBookOffersTestBundles()
.expectedError = "invalidParams",
.expectedErrorMessage = "Invalid field 'taker'."
},
ParameterTestBundle{
.testName = "Domain_InvalidType",
.testJson = R"JSON({
"taker_pays" :
{
"currency" : "CNY",
"issuer" : "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B"
},
"taker_gets" :
{
"currency" : "XRP"
},
"domain": 0
})JSON",
.expectedError = "invalidParams",
.expectedErrorMessage = "Invalid parameters."
},
ParameterTestBundle{
.testName = "Domain_InvalidInt",
.testJson = R"JSON({
"taker_pays" :
{
"currency" : "CNY",
"issuer" : "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B"
},
"taker_gets" :
{
"currency" : "XRP"
},
"domain": "123"
})JSON",
.expectedError = "invalidParams",
.expectedErrorMessage = "domainMalformed"
},
ParameterTestBundle{
.testName = "Domain_InvalidObject",
.testJson = R"JSON({
"taker_pays" :
{
"currency" : "CNY",
"issuer" : "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B"
},
"taker_gets" :
{
"currency" : "XRP"
},
"domain": {}
})JSON",
.expectedError = "invalidParams",
.expectedErrorMessage = "Invalid parameters."
},
ParameterTestBundle{
.testName = "LimitNotInt",
.testJson = R"JSON({
@@ -610,11 +660,29 @@ generateNormalPathBookOffersTestBundles()
kPAYS20_USD_GETS10_XRP_BOOK_DIR
);
auto const gets10XRPPays20USDOfferWithDomain = createOfferLedgerObject(
kACCOUNT2,
10,
20,
ripple::to_string(ripple::xrpCurrency()),
ripple::to_string(ripple::to_currency("USD")),
toBase58(ripple::xrpAccount()),
kACCOUNT,
kPAYS20_USD_GETS10_XRP_BOOK_DIR,
kDOMAIN
);
auto const getsXRPPaysUSDBook = getBookBase(
rpc::parseBook(ripple::to_currency("USD"), account, ripple::xrpCurrency(), ripple::xrpAccount()).value()
rpc::parseBook(ripple::to_currency("USD"), account, ripple::xrpCurrency(), ripple::xrpAccount(), std::nullopt)
.value()
);
auto const getsXRPPaysUSDBookWithDomain = getBookBase(
rpc::parseBook(ripple::to_currency("USD"), account, ripple::xrpCurrency(), ripple::xrpAccount(), kDOMAIN)
.value()
);
auto const getsUSDPaysXRPBook = getBookBase(
rpc::parseBook(ripple::xrpCurrency(), ripple::xrpAccount(), ripple::to_currency("USD"), account).value()
rpc::parseBook(ripple::xrpCurrency(), ripple::xrpAccount(), ripple::to_currency("USD"), account, std::nullopt)
.value()
);
auto const getsXRPPaysUSDInputJson = fmt::format(
@@ -632,6 +700,23 @@ generateNormalPathBookOffersTestBundles()
kACCOUNT
);
auto const getsXRPPaysUSDInputJsonWithDomain = fmt::format(
R"JSON({{
"taker_gets":
{{
"currency": "XRP"
}},
"taker_pays":
{{
"currency": "USD",
"issuer": "{}"
}},
"domain": "{}"
}})JSON",
kACCOUNT,
kDOMAIN
);
auto const paysXRPGetsUSDInputJson = fmt::format(
R"JSON({{
"taker_pays":
@@ -845,6 +930,69 @@ generateNormalPathBookOffersTestBundles()
2
)
},
BookOffersNormalTestBundle{
.testName = "PaysUSDGetsXRPFrozenWithDomain",
.inputJson = getsXRPPaysUSDInputJsonWithDomain,
// prepare offer dir index
.mockedSuccessors =
std::map<ripple::uint256, std::optional<ripple::uint256>>{
{getsXRPPaysUSDBookWithDomain, ripple::uint256{kPAYS20_USD_GETS10_XRP_BOOK_DIR}},
{ripple::uint256{kPAYS20_USD_GETS10_XRP_BOOK_DIR}, std::optional<ripple::uint256>{}}
},
.mockedLedgerObjects =
std::map<ripple::uint256, ripple::Blob>{
// book dir object
{ripple::uint256{kPAYS20_USD_GETS10_XRP_BOOK_DIR},
createOwnerDirLedgerObject({ripple::uint256{kINDEX2}}, kINDEX1).getSerializer().peekData()},
// pays issuer account object
{ripple::keylet::account(account).key,
createAccountRootObject(kACCOUNT, ripple::lsfGlobalFreeze, 2, 200, 2, kINDEX1, 2)
.getSerializer()
.peekData()}
},
.ledgerObjectCalls = 3,
.mockedOffers = std::vector<ripple::STObject>{gets10XRPPays20USDOfferWithDomain},
.expectedJson = fmt::format(
R"JSON({{
"ledger_hash":"{}",
"ledger_index":300,
"offers":
[
{{
"Account":"{}",
"BookDirectory":"43B83ADC452B85FCBADA6CAEAC5181C255A213630D58FFD455071AFD498D0000",
"BookNode":"0",
"DomainID": "F10D0CC9A0F9A3CBF585B80BE09A186483668FDBDD39AA7E3370F3649CE134E5",
"Flags":0,
"LedgerEntryType":"Offer",
"OwnerNode":"0",
"PreviousTxnID":"0000000000000000000000000000000000000000000000000000000000000000",
"PreviousTxnLgrSeq":0,
"Sequence":0,
"TakerGets":"10",
"TakerPays":{{
"currency":"USD",
"issuer":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"value":"20"
}},
"index":"E6DBAFC99223B42257915A63DFC6B0C032D4070F9A574B255AD97466726FC321",
"owner_funds":"{}",
"quality":"{}",
"taker_gets_funded":"0",
"taker_pays_funded":{{
"currency":"USD",
"issuer":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"value":"0"
}}
}}
]
}})JSON",
kLEDGER_HASH,
kACCOUNT2,
0,
2
)
},
BookOffersNormalTestBundle{
.testName = "GetsUSDPaysXRPFrozen",
.inputJson = paysXRPGetsUSDInputJson,
@@ -1450,7 +1598,8 @@ TEST_F(RPCBookOffersHandlerTest, Limit)
EXPECT_CALL(*backend_, doFetchSuccessorKey).Times(1);
auto const getsXRPPaysUSDBook = getBookBase(
rpc::parseBook(ripple::to_currency("USD"), issuer, ripple::xrpCurrency(), ripple::xrpAccount()).value()
rpc::parseBook(ripple::to_currency("USD"), issuer, ripple::xrpCurrency(), ripple::xrpAccount(), std::nullopt)
.value()
);
ON_CALL(*backend_, doFetchSuccessorKey(getsXRPPaysUSDBook, seq, _))
.WillByDefault(Return(ripple::uint256{kPAYS20_USD_GETS10_XRP_BOOK_DIR}));
@@ -1523,7 +1672,8 @@ TEST_F(RPCBookOffersHandlerTest, LimitMoreThanMax)
EXPECT_CALL(*backend_, doFetchSuccessorKey).Times(1);
auto const getsXRPPaysUSDBook = getBookBase(
rpc::parseBook(ripple::to_currency("USD"), issuer, ripple::xrpCurrency(), ripple::xrpAccount()).value()
rpc::parseBook(ripple::to_currency("USD"), issuer, ripple::xrpCurrency(), ripple::xrpAccount(), std::nullopt)
.value()
);
ON_CALL(*backend_, doFetchSuccessorKey(getsXRPPaysUSDBook, seq, _))
.WillByDefault(Return(ripple::uint256{kPAYS20_USD_GETS10_XRP_BOOK_DIR}));

View File

@@ -822,11 +822,13 @@ TEST_F(RPCSubscribeHandlerTest, BooksBothSnapshotSet)
auto const issuer = getAccountIdWithString(kACCOUNT);
auto const getsXRPPaysUSDBook = getBookBase(
rpc::parseBook(ripple::to_currency("USD"), issuer, ripple::xrpCurrency(), ripple::xrpAccount()).value()
rpc::parseBook(ripple::to_currency("USD"), issuer, ripple::xrpCurrency(), ripple::xrpAccount(), std::nullopt)
.value()
);
auto const reversedBook = getBookBase(
rpc::parseBook(ripple::xrpCurrency(), ripple::xrpAccount(), ripple::to_currency("USD"), issuer).value()
rpc::parseBook(ripple::xrpCurrency(), ripple::xrpAccount(), ripple::to_currency("USD"), issuer, std::nullopt)
.value()
);
ON_CALL(*backend_, doFetchSuccessorKey(getsXRPPaysUSDBook, kMAX_SEQ, _))
@@ -992,11 +994,13 @@ TEST_F(RPCSubscribeHandlerTest, BooksBothUnsetSnapshotSet)
auto const issuer = getAccountIdWithString(kACCOUNT);
auto const getsXRPPaysUSDBook = getBookBase(
rpc::parseBook(ripple::to_currency("USD"), issuer, ripple::xrpCurrency(), ripple::xrpAccount()).value()
rpc::parseBook(ripple::to_currency("USD"), issuer, ripple::xrpCurrency(), ripple::xrpAccount(), std::nullopt)
.value()
);
auto const reversedBook = getBookBase(
rpc::parseBook(ripple::xrpCurrency(), ripple::xrpAccount(), ripple::to_currency("USD"), issuer).value()
rpc::parseBook(ripple::xrpCurrency(), ripple::xrpAccount(), ripple::to_currency("USD"), issuer, std::nullopt)
.value()
);
ON_CALL(*backend_, doFetchSuccessorKey(getsXRPPaysUSDBook, kMAX_SEQ, _))