feat: Add DynamicMPT in account_mptoken_issuances (#2820)

Support DynamicMPT for the  account_mptoken_issuances handler.

Related commit:
eed757e0c4

The original spec for `DynamicMPT` can be found here:
https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0094-dynamic-MPT

---------

Co-authored-by: Sergey Kuznetsov <skuznetsov@ripple.com>
This commit is contained in:
yinyiqian1
2025-12-10 06:36:24 -05:00
committed by GitHub
parent c7c270cc03
commit 06ea05891d
8 changed files with 609 additions and 251 deletions

View File

@@ -56,6 +56,7 @@ AccountMPTokenIssuancesHandler::addMPTokenIssuance(
{
MPTokenIssuanceResponse issuance;
issuance.MPTokenIssuanceID = ripple::strHex(sle.key());
issuance.issuer = ripple::to_string(account);
issuance.sequence = sle.getFieldU32(ripple::sfSequence);
auto const flags = sle.getFieldU32(ripple::sfFlags);
@@ -73,6 +74,24 @@ AccountMPTokenIssuancesHandler::addMPTokenIssuance(
setFlag(issuance.mptCanTransfer, ripple::lsfMPTCanTransfer);
setFlag(issuance.mptCanClawback, ripple::lsfMPTCanClawback);
if (sle.isFieldPresent(ripple::sfMutableFlags)) {
auto const mutableFlags = sle.getFieldU32(ripple::sfMutableFlags);
auto const setMutableFlag = [&](std::optional<bool>& field, std::uint32_t mask) {
if ((mutableFlags & mask) != 0u)
field = true;
};
setMutableFlag(issuance.mptCanMutateCanLock, ripple::lsmfMPTCanMutateCanLock);
setMutableFlag(issuance.mptCanMutateRequireAuth, ripple::lsmfMPTCanMutateRequireAuth);
setMutableFlag(issuance.mptCanMutateCanEscrow, ripple::lsmfMPTCanMutateCanEscrow);
setMutableFlag(issuance.mptCanMutateCanTrade, ripple::lsmfMPTCanMutateCanTrade);
setMutableFlag(issuance.mptCanMutateCanTransfer, ripple::lsmfMPTCanMutateCanTransfer);
setMutableFlag(issuance.mptCanMutateCanClawback, ripple::lsmfMPTCanMutateCanClawback);
setMutableFlag(issuance.mptCanMutateMetadata, ripple::lsmfMPTCanMutateMetadata);
setMutableFlag(issuance.mptCanMutateTransferFee, ripple::lsmfMPTCanMutateTransferFee);
}
if (sle.isFieldPresent(ripple::sfTransferFee))
issuance.transferFee = sle.getFieldU16(ripple::sfTransferFee);
@@ -201,6 +220,7 @@ tag_invoke(
)
{
auto obj = boost::json::object{
{JS(mpt_issuance_id), issuance.MPTokenIssuanceID},
{JS(issuer), issuance.issuer},
{JS(sequence), issuance.sequence},
};
@@ -227,6 +247,15 @@ tag_invoke(
setIfPresent("mpt_can_transfer", issuance.mptCanTransfer);
setIfPresent("mpt_can_clawback", issuance.mptCanClawback);
setIfPresent("mpt_can_mutate_can_lock", issuance.mptCanMutateCanLock);
setIfPresent("mpt_can_mutate_require_auth", issuance.mptCanMutateRequireAuth);
setIfPresent("mpt_can_mutate_can_escrow", issuance.mptCanMutateCanEscrow);
setIfPresent("mpt_can_mutate_can_trade", issuance.mptCanMutateCanTrade);
setIfPresent("mpt_can_mutate_can_transfer", issuance.mptCanMutateCanTransfer);
setIfPresent("mpt_can_mutate_can_clawback", issuance.mptCanMutateCanClawback);
setIfPresent("mpt_can_mutate_metadata", issuance.mptCanMutateMetadata);
setIfPresent("mpt_can_mutate_transfer_fee", issuance.mptCanMutateTransferFee);
jv = std::move(obj);
}

View File

@@ -61,6 +61,7 @@ public:
* @brief A struct to hold data for one MPTokenIssuance response.
*/
struct MPTokenIssuanceResponse {
std::string MPTokenIssuanceID;
std::string issuer;
uint32_t sequence{};
@@ -80,6 +81,15 @@ public:
std::optional<bool> mptCanTrade;
std::optional<bool> mptCanTransfer;
std::optional<bool> mptCanClawback;
std::optional<bool> mptCanMutateCanLock;
std::optional<bool> mptCanMutateRequireAuth;
std::optional<bool> mptCanMutateCanEscrow;
std::optional<bool> mptCanMutateCanTrade;
std::optional<bool> mptCanMutateCanTransfer;
std::optional<bool> mptCanMutateCanClawback;
std::optional<bool> mptCanMutateMetadata;
std::optional<bool> mptCanMutateTransferFee;
};
/**

View File

@@ -54,6 +54,7 @@ AccountMPTokensHandler::addMPToken(std::vector<MPTokenResponse>& mpts, ripple::S
MPTokenResponse token{};
auto const flags = sle.getFieldU32(ripple::sfFlags);
token.MPTokenID = ripple::strHex(sle.key());
token.account = ripple::to_string(sle.getAccountID(ripple::sfAccount));
token.MPTokenIssuanceID = ripple::strHex(sle.getFieldH192(ripple::sfMPTokenIssuanceID));
token.MPTAmount = sle.getFieldU64(ripple::sfMPTAmount);
@@ -170,6 +171,7 @@ void
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, AccountMPTokensHandler::MPTokenResponse const& mptoken)
{
auto obj = boost::json::object{
{"mpt_id", mptoken.MPTokenID},
{JS(account), mptoken.account},
{JS(mpt_issuance_id), mptoken.MPTokenIssuanceID},
{JS(mpt_amount), mptoken.MPTAmount},

View File

@@ -59,6 +59,7 @@ public:
* @brief A struct to hold data for one MPToken response.
*/
struct MPTokenResponse {
std::string MPTokenID;
std::string account;
std::string MPTokenIssuanceID;
uint64_t MPTAmount{};

View File

@@ -1452,7 +1452,8 @@ createMptIssuanceObject(
std::optional<std::uint8_t> assetScale,
std::optional<std::uint64_t> maxAmount,
std::optional<std::uint64_t> lockedAmount,
std::optional<std::string_view> domainId
std::optional<std::string_view> domainId,
std::optional<std::uint32_t> mutableFlags
)
{
ripple::STObject mptIssuance(ripple::sfLedgerEntry);
@@ -1479,6 +1480,8 @@ createMptIssuanceObject(
}
if (domainId.has_value())
mptIssuance.setFieldH256(ripple::sfDomainID, ripple::uint256{*domainId});
if (mutableFlags.has_value())
mptIssuance.setFieldU32(ripple::sfMutableFlags, *mutableFlags);
return mptIssuance;
}

View File

@@ -461,7 +461,8 @@ createMptIssuanceObject(
std::optional<std::uint8_t> assetScale = std::nullopt,
std::optional<std::uint64_t> maxAmount = std::nullopt,
std::optional<std::uint64_t> lockedAmount = std::nullopt,
std::optional<std::string_view> domainId = std::nullopt
std::optional<std::string_view> domainId = std::nullopt,
std::optional<std::uint32_t> mutableFlags = std::nullopt
);
[[nodiscard]] ripple::STObject

View File

@@ -62,6 +62,7 @@ constexpr auto kISSUANCE_INDEX2 = "B6DBAFC99223B42257915A63DFC6B0C032D4070F9A574
constexpr uint64_t kISSUANCE1_MAX_AMOUNT = 10000;
constexpr uint64_t kISSUANCE1_OUTSTANDING_AMOUNT = 5000;
constexpr uint8_t kISSUANCE1_ASSET_SCALE = 2;
constexpr uint16_t kISSUANCE1_TRANSFER_FEE = 10;
// unique values for issuance2
constexpr uint64_t kISSUANCE2_MAX_AMOUNT = 20000;
@@ -75,6 +76,7 @@ constexpr auto kISSUANCE2_DOMAIN_ID_HEX = "E6DBAFC99223B42257915A63DFC6B0C032D40
// define expected JSON for mpt issuances
auto const kISSUANCE_OUT1 = fmt::format(
R"JSON({{
"mpt_issuance_id": "{}",
"issuer": "{}",
"sequence": 1,
"maximum_amount": {},
@@ -85,6 +87,7 @@ auto const kISSUANCE_OUT1 = fmt::format(
"mpt_require_auth": true,
"mpt_can_transfer": true
}})JSON",
kISSUANCE_INDEX1,
kACCOUNT,
kISSUANCE1_MAX_AMOUNT,
kISSUANCE1_OUTSTANDING_AMOUNT,
@@ -93,6 +96,7 @@ auto const kISSUANCE_OUT1 = fmt::format(
auto const kISSUANCE_OUT2 = fmt::format(
R"JSON({{
"mpt_issuance_id": "{}",
"issuer": "{}",
"sequence": 2,
"maximum_amount": {},
@@ -105,6 +109,7 @@ auto const kISSUANCE_OUT2 = fmt::format(
"mpt_locked": true,
"mpt_can_clawback": true
}})JSON",
kISSUANCE_INDEX2,
kACCOUNT,
kISSUANCE2_MAX_AMOUNT,
kISSUANCE2_OUTSTANDING_AMOUNT,
@@ -293,7 +298,7 @@ TEST_F(RPCAccountMPTokenIssuancesHandlerTest, NonExistLedgerViaLedgerIntIndex)
// ledger not found via hash (seq > max)
TEST_F(RPCAccountMPTokenIssuancesHandlerTest, LedgerSeqOutOfRangeByHash)
{
auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, 31);
auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, 31);
EXPECT_CALL(*backend_, fetchLedgerByHash(ripple::uint256{kLEDGER_HASH}, _)).WillOnce(Return(ledgerHeader));
auto const input = json::parse(
fmt::format(
@@ -341,7 +346,7 @@ TEST_F(RPCAccountMPTokenIssuancesHandlerTest, LedgerSeqOutOfRangeByIndex)
// account not exist
TEST_F(RPCAccountMPTokenIssuancesHandlerTest, NonExistAccount)
{
auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
EXPECT_CALL(*backend_, fetchLedgerByHash(ripple::uint256{kLEDGER_HASH}, _)).WillOnce(Return(ledgerHeader));
// fetch account object return empty
EXPECT_CALL(*backend_, doFetchLedgerObject).WillOnce(Return(std::optional<Blob>{}));
@@ -369,13 +374,13 @@ TEST_F(RPCAccountMPTokenIssuancesHandlerTest, NonExistAccount)
// fetch mptoken issuances via account successfully
TEST_F(RPCAccountMPTokenIssuancesHandlerTest, DefaultParameters)
{
auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(ledgerHeader));
// return non-empty account
auto account = getAccountIdWithString(kACCOUNT);
auto accountKk = ripple::keylet::account(account).key;
auto owneDirKk = ripple::keylet::ownerDir(account).key;
auto const account = getAccountIdWithString(kACCOUNT);
auto const accountKk = ripple::keylet::account(account).key;
auto const owneDirKk = ripple::keylet::ownerDir(account).key;
ON_CALL(*backend_, doFetchLedgerObject(accountKk, _, _)).WillByDefault(Return(Blob{'f', 'a', 'k', 'e'}));
// return two mptoken issuance objects
@@ -385,33 +390,36 @@ TEST_F(RPCAccountMPTokenIssuancesHandlerTest, DefaultParameters)
ON_CALL(*backend_, doFetchLedgerObject(owneDirKk, _, _)).WillByDefault(Return(ownerDir.getSerializer().peekData()));
// mocking mptoken issuance ledger objects
std::vector<Blob> bbs;
auto const issuance1 = createMptIssuanceObject(
kACCOUNT,
1,
std::nullopt,
ripple::lsfMPTCanTrade | ripple::lsfMPTRequireAuth | ripple::lsfMPTCanTransfer | ripple::lsfMPTCanEscrow,
kISSUANCE1_OUTSTANDING_AMOUNT,
std::nullopt,
kISSUANCE1_ASSET_SCALE,
kISSUANCE1_MAX_AMOUNT
);
auto const bbs = std::vector<Blob>{
createMptIssuanceObject(
kACCOUNT,
1,
std::nullopt,
ripple::lsfMPTCanTrade | ripple::lsfMPTRequireAuth | ripple::lsfMPTCanTransfer | ripple::lsfMPTCanEscrow,
kISSUANCE1_OUTSTANDING_AMOUNT,
std::nullopt,
kISSUANCE1_ASSET_SCALE,
kISSUANCE1_MAX_AMOUNT
)
.getSerializer()
.peekData(),
auto const issuance2 = createMptIssuanceObject(
kACCOUNT,
2,
kISSUANCE2_METADATA,
ripple::lsfMPTLocked | ripple::lsfMPTCanLock | ripple::lsfMPTCanClawback,
kISSUANCE2_OUTSTANDING_AMOUNT,
kISSUANCE2_TRANSFER_FEE,
std::nullopt,
kISSUANCE2_MAX_AMOUNT,
kISSUANCE2_LOCKED_AMOUNT,
kISSUANCE2_DOMAIN_ID_HEX
);
createMptIssuanceObject(
kACCOUNT,
2,
kISSUANCE2_METADATA,
ripple::lsfMPTLocked | ripple::lsfMPTCanLock | ripple::lsfMPTCanClawback,
kISSUANCE2_OUTSTANDING_AMOUNT,
kISSUANCE2_TRANSFER_FEE,
std::nullopt,
kISSUANCE2_MAX_AMOUNT,
kISSUANCE2_LOCKED_AMOUNT,
kISSUANCE2_DOMAIN_ID_HEX
)
.getSerializer()
.peekData()
};
bbs.push_back(issuance1.getSerializer().peekData());
bbs.push_back(issuance2.getSerializer().peekData());
EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs));
runSpawn([this](auto yield) {
@@ -434,7 +442,7 @@ TEST_F(RPCAccountMPTokenIssuancesHandlerTest, DefaultParameters)
kISSUANCE_OUT2
);
auto const input = json::parse(fmt::format(R"JSON({{"account": "{}"}})JSON", kACCOUNT));
auto handler = AnyHandler{AccountMPTokenIssuancesHandler{this->backend_}};
auto const handler = AnyHandler{AccountMPTokenIssuancesHandler{this->backend_}};
auto const output = handler.process(input, Context{yield});
ASSERT_TRUE(output);
@@ -445,22 +453,23 @@ TEST_F(RPCAccountMPTokenIssuancesHandlerTest, DefaultParameters)
TEST_F(RPCAccountMPTokenIssuancesHandlerTest, UseLimit)
{
constexpr int kLIMIT = 20;
auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
ON_CALL(*backend_, fetchLedgerBySequence).WillByDefault(Return(ledgerHeader));
auto account = getAccountIdWithString(kACCOUNT);
auto accountKk = ripple::keylet::account(account).key;
auto owneDirKk = ripple::keylet::ownerDir(account).key;
auto const account = getAccountIdWithString(kACCOUNT);
auto const accountKk = ripple::keylet::account(account).key;
auto const owneDirKk = ripple::keylet::ownerDir(account).key;
ON_CALL(*backend_, doFetchLedgerObject(accountKk, _, _)).WillByDefault(Return(Blob{'f', 'a', 'k', 'e'}));
std::vector<ripple::uint256> indexes;
std::vector<Blob> bbs;
for (int i = 0; i < 50; ++i) {
indexes.emplace_back(kISSUANCE_INDEX1);
auto const issuance = createMptIssuanceObject(kACCOUNT, i);
bbs.push_back(issuance.getSerializer().peekData());
}
auto const indexes = std::vector<ripple::uint256>(50, ripple::uint256{kISSUANCE_INDEX1});
auto const bbs = [&]() {
std::vector<Blob> v;
v.reserve(50);
for (int i = 0; i < 50; ++i) {
v.push_back(createMptIssuanceObject(kACCOUNT, i).getSerializer().peekData());
}
return v;
}();
ripple::STObject ownerDir = createOwnerDirLedgerObject(indexes, kISSUANCE_INDEX1);
ownerDir.setFieldU64(ripple::sfIndexNext, 99);
@@ -482,7 +491,7 @@ TEST_F(RPCAccountMPTokenIssuancesHandlerTest, UseLimit)
)
);
auto handler = AnyHandler{AccountMPTokenIssuancesHandler{this->backend_}};
auto const handler = AnyHandler{AccountMPTokenIssuancesHandler{this->backend_}};
auto const output = handler.process(input, Context{yield});
ASSERT_TRUE(output);
@@ -531,27 +540,25 @@ TEST_F(RPCAccountMPTokenIssuancesHandlerTest, MarkerOutput)
{
constexpr auto kNEXT_PAGE = 99;
constexpr auto kLIMIT = 15;
auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(ledgerHeader));
auto account = getAccountIdWithString(kACCOUNT);
auto accountKk = ripple::keylet::account(account).key;
auto ownerDirKk = ripple::keylet::ownerDir(account).key;
auto ownerDir2Kk = ripple::keylet::page(ripple::keylet::ownerDir(account), kNEXT_PAGE).key;
auto const account = getAccountIdWithString(kACCOUNT);
auto const accountKk = ripple::keylet::account(account).key;
auto const ownerDirKk = ripple::keylet::ownerDir(account).key;
auto const ownerDir2Kk = ripple::keylet::page(ripple::keylet::ownerDir(account), kNEXT_PAGE).key;
ON_CALL(*backend_, doFetchLedgerObject(accountKk, _, _)).WillByDefault(Return(Blob{'f', 'a', 'k', 'e'}));
EXPECT_CALL(*backend_, doFetchLedgerObject).Times(3);
std::vector<ripple::uint256> indexes;
indexes.reserve(10);
for (int i = 0; i < 10; ++i) {
indexes.emplace_back(kISSUANCE_INDEX1);
}
std::vector<Blob> bbs;
bbs.reserve(kLIMIT);
for (int i = 0; i < kLIMIT; ++i) {
bbs.push_back(createMptIssuanceObject(kACCOUNT, i).getSerializer().peekData());
}
auto const indexes = std::vector<ripple::uint256>(10, ripple::uint256{kISSUANCE_INDEX1});
auto const bbs = [&]() {
std::vector<Blob> v;
v.reserve(kLIMIT);
for (int i = 0; i < kLIMIT; ++i) {
v.push_back(createMptIssuanceObject(kACCOUNT, i).getSerializer().peekData());
}
return v;
}();
EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs));
// mock the first directory page
@@ -577,7 +584,7 @@ TEST_F(RPCAccountMPTokenIssuancesHandlerTest, MarkerOutput)
kLIMIT
)
);
auto handler = AnyHandler{AccountMPTokenIssuancesHandler{this->backend_}};
auto const handler = AnyHandler{AccountMPTokenIssuancesHandler{this->backend_}};
auto const output = handler.process(input, Context{yield});
ASSERT_TRUE(output);
auto const& resultJson = (*output.result).as_object();
@@ -594,21 +601,24 @@ TEST_F(RPCAccountMPTokenIssuancesHandlerTest, MarkerInput)
constexpr auto kNEXT_PAGE = 99;
constexpr auto kLIMIT = 15;
auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(ledgerHeader));
auto account = getAccountIdWithString(kACCOUNT);
auto accountKk = ripple::keylet::account(account).key;
auto ownerDirKk = ripple::keylet::page(ripple::keylet::ownerDir(account), kNEXT_PAGE).key;
auto const account = getAccountIdWithString(kACCOUNT);
auto const accountKk = ripple::keylet::account(account).key;
auto const ownerDirKk = ripple::keylet::page(ripple::keylet::ownerDir(account), kNEXT_PAGE).key;
ON_CALL(*backend_, doFetchLedgerObject(accountKk, _, _)).WillByDefault(Return(Blob{'f', 'a', 'k', 'e'}));
EXPECT_CALL(*backend_, doFetchLedgerObject).Times(3);
std::vector<Blob> bbs;
std::vector<ripple::uint256> indexes;
for (int i = 0; i < kLIMIT; ++i) {
indexes.emplace_back(kISSUANCE_INDEX1);
bbs.push_back(createMptIssuanceObject(kACCOUNT, i).getSerializer().peekData());
}
auto const indexes = std::vector<ripple::uint256>(kLIMIT, ripple::uint256{kISSUANCE_INDEX1});
auto const bbs = [&]() {
std::vector<Blob> v;
v.reserve(kLIMIT);
for (int i = 0; i < kLIMIT; ++i) {
v.push_back(createMptIssuanceObject(kACCOUNT, i).getSerializer().peekData());
}
return v;
}();
ripple::STObject ownerDir = createOwnerDirLedgerObject(indexes, kISSUANCE_INDEX1);
ownerDir.setFieldU64(ripple::sfIndexNext, 0);
@@ -631,7 +641,7 @@ TEST_F(RPCAccountMPTokenIssuancesHandlerTest, MarkerInput)
kNEXT_PAGE
)
);
auto handler = AnyHandler{AccountMPTokenIssuancesHandler{this->backend_}};
auto const handler = AnyHandler{AccountMPTokenIssuancesHandler{this->backend_}};
auto const output = handler.process(input, Context{yield});
ASSERT_TRUE(output);
@@ -643,47 +653,48 @@ TEST_F(RPCAccountMPTokenIssuancesHandlerTest, MarkerInput)
TEST_F(RPCAccountMPTokenIssuancesHandlerTest, LimitLessThanMin)
{
auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(ledgerHeader));
auto account = getAccountIdWithString(kACCOUNT);
auto accountKk = ripple::keylet::account(account).key;
auto owneDirKk = ripple::keylet::ownerDir(account).key;
ON_CALL(*backend_, doFetchLedgerObject(accountKk, _, _)).WillByDefault(Return(Blob{'f', 'a', 'k', 'e'}));
auto const account = getAccountIdWithString(kACCOUNT);
auto const accountKk = ripple::keylet::account(account).key;
auto const owneDirKk = ripple::keylet::ownerDir(account).key;
EXPECT_CALL(*backend_, doFetchLedgerObject(accountKk, _, _)).WillOnce(Return(Blob{'f', 'a', 'k', 'e'}));
ripple::STObject const ownerDir = createOwnerDirLedgerObject(
{ripple::uint256{kISSUANCE_INDEX1}, ripple::uint256{kISSUANCE_INDEX2}}, kISSUANCE_INDEX1
);
ON_CALL(*backend_, doFetchLedgerObject(owneDirKk, _, _)).WillByDefault(Return(ownerDir.getSerializer().peekData()));
EXPECT_CALL(*backend_, doFetchLedgerObject).Times(2);
EXPECT_CALL(*backend_, doFetchLedgerObject(owneDirKk, _, _)).WillOnce(Return(ownerDir.getSerializer().peekData()));
std::vector<Blob> bbs;
auto const issuance1 = createMptIssuanceObject(
kACCOUNT,
1,
std::nullopt,
ripple::lsfMPTCanTrade | ripple::lsfMPTRequireAuth | ripple::lsfMPTCanTransfer | ripple::lsfMPTCanEscrow,
kISSUANCE1_OUTSTANDING_AMOUNT,
std::nullopt,
kISSUANCE1_ASSET_SCALE,
kISSUANCE1_MAX_AMOUNT
);
auto const bbs = std::vector<Blob>{
createMptIssuanceObject(
kACCOUNT,
1,
std::nullopt,
ripple::lsfMPTCanTrade | ripple::lsfMPTRequireAuth | ripple::lsfMPTCanTransfer | ripple::lsfMPTCanEscrow,
kISSUANCE1_OUTSTANDING_AMOUNT,
std::nullopt,
kISSUANCE1_ASSET_SCALE,
kISSUANCE1_MAX_AMOUNT
)
.getSerializer()
.peekData(),
auto const issuance2 = createMptIssuanceObject(
kACCOUNT,
2,
kISSUANCE2_METADATA,
ripple::lsfMPTLocked | ripple::lsfMPTCanLock | ripple::lsfMPTCanClawback,
kISSUANCE2_OUTSTANDING_AMOUNT,
kISSUANCE2_TRANSFER_FEE,
std::nullopt,
kISSUANCE2_MAX_AMOUNT,
kISSUANCE2_LOCKED_AMOUNT,
kISSUANCE2_DOMAIN_ID_HEX
);
bbs.push_back(issuance1.getSerializer().peekData());
bbs.push_back(issuance2.getSerializer().peekData());
createMptIssuanceObject(
kACCOUNT,
2,
kISSUANCE2_METADATA,
ripple::lsfMPTLocked | ripple::lsfMPTCanLock | ripple::lsfMPTCanClawback,
kISSUANCE2_OUTSTANDING_AMOUNT,
kISSUANCE2_TRANSFER_FEE,
std::nullopt,
kISSUANCE2_MAX_AMOUNT,
kISSUANCE2_LOCKED_AMOUNT,
kISSUANCE2_DOMAIN_ID_HEX
)
.getSerializer()
.peekData()
};
EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs));
@@ -718,7 +729,7 @@ TEST_F(RPCAccountMPTokenIssuancesHandlerTest, LimitLessThanMin)
kISSUANCE_OUT2
);
auto handler = AnyHandler{AccountMPTokenIssuancesHandler{this->backend_}};
auto const handler = AnyHandler{AccountMPTokenIssuancesHandler{this->backend_}};
auto const output = handler.process(input, Context{yield});
ASSERT_TRUE(output);
EXPECT_EQ(json::parse(correctOutput), *output.result);
@@ -727,47 +738,48 @@ TEST_F(RPCAccountMPTokenIssuancesHandlerTest, LimitLessThanMin)
TEST_F(RPCAccountMPTokenIssuancesHandlerTest, LimitMoreThanMax)
{
auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(ledgerHeader));
auto account = getAccountIdWithString(kACCOUNT);
auto accountKk = ripple::keylet::account(account).key;
auto owneDirKk = ripple::keylet::ownerDir(account).key;
ON_CALL(*backend_, doFetchLedgerObject(accountKk, _, _)).WillByDefault(Return(Blob{'f', 'a', 'k', 'e'}));
auto const account = getAccountIdWithString(kACCOUNT);
auto const accountKk = ripple::keylet::account(account).key;
auto const owneDirKk = ripple::keylet::ownerDir(account).key;
EXPECT_CALL(*backend_, doFetchLedgerObject(accountKk, _, _)).WillOnce(Return(Blob{'f', 'a', 'k', 'e'}));
ripple::STObject const ownerDir = createOwnerDirLedgerObject(
{ripple::uint256{kISSUANCE_INDEX1}, ripple::uint256{kISSUANCE_INDEX2}}, kISSUANCE_INDEX1
);
ON_CALL(*backend_, doFetchLedgerObject(owneDirKk, _, _)).WillByDefault(Return(ownerDir.getSerializer().peekData()));
EXPECT_CALL(*backend_, doFetchLedgerObject).Times(2);
EXPECT_CALL(*backend_, doFetchLedgerObject(owneDirKk, _, _)).WillOnce(Return(ownerDir.getSerializer().peekData()));
std::vector<Blob> bbs;
auto const issuance1 = createMptIssuanceObject(
kACCOUNT,
1,
std::nullopt,
ripple::lsfMPTCanTrade | ripple::lsfMPTRequireAuth | ripple::lsfMPTCanTransfer | ripple::lsfMPTCanEscrow,
kISSUANCE1_OUTSTANDING_AMOUNT,
std::nullopt,
kISSUANCE1_ASSET_SCALE,
kISSUANCE1_MAX_AMOUNT
);
auto const bbs = std::vector<Blob>{
createMptIssuanceObject(
kACCOUNT,
1,
std::nullopt,
ripple::lsfMPTCanTrade | ripple::lsfMPTRequireAuth | ripple::lsfMPTCanTransfer | ripple::lsfMPTCanEscrow,
kISSUANCE1_OUTSTANDING_AMOUNT,
std::nullopt,
kISSUANCE1_ASSET_SCALE,
kISSUANCE1_MAX_AMOUNT
)
.getSerializer()
.peekData(),
auto const issuance2 = createMptIssuanceObject(
kACCOUNT,
2,
kISSUANCE2_METADATA,
ripple::lsfMPTLocked | ripple::lsfMPTCanLock | ripple::lsfMPTCanClawback,
kISSUANCE2_OUTSTANDING_AMOUNT,
kISSUANCE2_TRANSFER_FEE,
std::nullopt,
kISSUANCE2_MAX_AMOUNT,
kISSUANCE2_LOCKED_AMOUNT,
kISSUANCE2_DOMAIN_ID_HEX
);
bbs.push_back(issuance1.getSerializer().peekData());
bbs.push_back(issuance2.getSerializer().peekData());
createMptIssuanceObject(
kACCOUNT,
2,
kISSUANCE2_METADATA,
ripple::lsfMPTLocked | ripple::lsfMPTCanLock | ripple::lsfMPTCanClawback,
kISSUANCE2_OUTSTANDING_AMOUNT,
kISSUANCE2_TRANSFER_FEE,
std::nullopt,
kISSUANCE2_MAX_AMOUNT,
kISSUANCE2_LOCKED_AMOUNT,
kISSUANCE2_DOMAIN_ID_HEX
)
.getSerializer()
.peekData()
};
EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs));
@@ -802,7 +814,7 @@ TEST_F(RPCAccountMPTokenIssuancesHandlerTest, LimitMoreThanMax)
kISSUANCE_OUT2
);
auto handler = AnyHandler{AccountMPTokenIssuancesHandler{this->backend_}};
auto const handler = AnyHandler{AccountMPTokenIssuancesHandler{this->backend_}};
auto const output = handler.process(input, Context{yield});
ASSERT_TRUE(output);
EXPECT_EQ(json::parse(correctOutput), *output.result);
@@ -811,17 +823,16 @@ TEST_F(RPCAccountMPTokenIssuancesHandlerTest, LimitMoreThanMax)
TEST_F(RPCAccountMPTokenIssuancesHandlerTest, EmptyResult)
{
auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(ledgerHeader));
auto account = getAccountIdWithString(kACCOUNT);
auto accountKk = ripple::keylet::account(account).key;
auto owneDirKk = ripple::keylet::ownerDir(account).key;
ON_CALL(*backend_, doFetchLedgerObject(accountKk, _, _)).WillByDefault(Return(Blob{'f', 'a', 'k', 'e'}));
auto const account = getAccountIdWithString(kACCOUNT);
auto const accountKk = ripple::keylet::account(account).key;
auto const owneDirKk = ripple::keylet::ownerDir(account).key;
EXPECT_CALL(*backend_, doFetchLedgerObject(accountKk, _, _)).WillOnce(Return(Blob{'f', 'a', 'k', 'e'}));
ripple::STObject const ownerDir = createOwnerDirLedgerObject({}, kISSUANCE_INDEX1);
ON_CALL(*backend_, doFetchLedgerObject(owneDirKk, _, _)).WillByDefault(Return(ownerDir.getSerializer().peekData()));
EXPECT_CALL(*backend_, doFetchLedgerObject).Times(2);
EXPECT_CALL(*backend_, doFetchLedgerObject(owneDirKk, _, _)).WillOnce(Return(ownerDir.getSerializer().peekData()));
runSpawn([this](auto yield) {
auto const input = json::parse(
@@ -832,9 +843,292 @@ TEST_F(RPCAccountMPTokenIssuancesHandlerTest, EmptyResult)
kACCOUNT
)
);
auto handler = AnyHandler{AccountMPTokenIssuancesHandler{this->backend_}};
auto const handler = AnyHandler{AccountMPTokenIssuancesHandler{this->backend_}};
auto const output = handler.process(input, Context{yield});
ASSERT_TRUE(output);
EXPECT_EQ((*output.result).as_object().at("mpt_issuances").as_array().size(), 0);
});
}
TEST_F(RPCAccountMPTokenIssuancesHandlerTest, MutableFlags)
{
uint32_t const mutableFlags1 = ripple::lsmfMPTCanMutateCanLock | ripple::lsmfMPTCanMutateRequireAuth |
ripple::lsmfMPTCanMutateCanEscrow | ripple::lsmfMPTCanMutateCanTrade;
uint32_t const mutableFlags2 = ripple::lsmfMPTCanMutateCanTransfer | ripple::lsmfMPTCanMutateCanClawback |
ripple::lsmfMPTCanMutateMetadata | ripple::lsmfMPTCanMutateTransferFee;
auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(ledgerHeader));
auto const account = getAccountIdWithString(kACCOUNT);
auto const accountKk = ripple::keylet::account(account).key;
auto const owneDirKk = ripple::keylet::ownerDir(account).key;
EXPECT_CALL(*backend_, doFetchLedgerObject(accountKk, _, _)).WillOnce(Return(Blob{'f', 'a', 'k', 'e'}));
ripple::STObject const ownerDir = createOwnerDirLedgerObject(
{ripple::uint256{kISSUANCE_INDEX1}, ripple::uint256{kISSUANCE_INDEX2}}, kISSUANCE_INDEX1
);
EXPECT_CALL(*backend_, doFetchLedgerObject(owneDirKk, _, _)).WillOnce(Return(ownerDir.getSerializer().peekData()));
auto const bbs = std::vector<Blob>{
createMptIssuanceObject(
kACCOUNT,
3,
std::nullopt,
ripple::lsfMPTCanTransfer,
kISSUANCE1_OUTSTANDING_AMOUNT,
kISSUANCE1_TRANSFER_FEE,
std::nullopt,
std::nullopt,
std::nullopt,
std::nullopt,
mutableFlags1
)
.getSerializer()
.peekData(),
createMptIssuanceObject(
kACCOUNT,
5,
kISSUANCE2_METADATA,
ripple::lsfMPTCanTransfer,
kISSUANCE2_OUTSTANDING_AMOUNT,
kISSUANCE2_TRANSFER_FEE,
std::nullopt,
std::nullopt,
std::nullopt,
std::nullopt,
mutableFlags2
)
.getSerializer()
.peekData()
};
EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs));
runSpawn([this](auto yield) {
auto const input = json::parse(
fmt::format(
R"JSON({{
"account": "{}"
}})JSON",
kACCOUNT
)
);
auto const correctOutput = fmt::format(
R"JSON({{
"account": "{}",
"ledger_hash": "{}",
"ledger_index": 30,
"validated": true,
"limit": 200,
"mpt_issuances": [
{{
"mpt_issuance_id": "{}",
"issuer": "{}",
"sequence": 3,
"outstanding_amount": {},
"transfer_fee": {},
"mpt_can_transfer": true,
"mpt_can_mutate_can_lock": true,
"mpt_can_mutate_require_auth": true,
"mpt_can_mutate_can_escrow": true,
"mpt_can_mutate_can_trade": true
}},
{{
"mpt_issuance_id": "{}",
"issuer": "{}",
"sequence": 5,
"outstanding_amount": {},
"transfer_fee": {},
"mptoken_metadata": "{}",
"mpt_can_transfer": true,
"mpt_can_mutate_can_transfer": true,
"mpt_can_mutate_can_clawback": true,
"mpt_can_mutate_metadata": true,
"mpt_can_mutate_transfer_fee": true
}}
]
}})JSON",
kACCOUNT,
kLEDGER_HASH,
kISSUANCE_INDEX1,
kACCOUNT,
kISSUANCE1_OUTSTANDING_AMOUNT,
kISSUANCE1_TRANSFER_FEE,
kISSUANCE_INDEX2,
kACCOUNT,
kISSUANCE2_OUTSTANDING_AMOUNT,
kISSUANCE2_TRANSFER_FEE,
kISSUANCE2_METADATA_HEX
);
auto const handler = AnyHandler{AccountMPTokenIssuancesHandler{this->backend_}};
auto const output = handler.process(input, Context{yield});
ASSERT_TRUE(output);
EXPECT_EQ(json::parse(correctOutput), *output.result);
});
}
struct SingleFlagTest {
std::string testName;
uint32_t flag;
std::string expectedJsonKey;
};
struct AccountMPTokenIssuancesImmutableFlagsTest : RPCAccountMPTokenIssuancesHandlerTest,
WithParamInterface<SingleFlagTest> {};
static auto
generateSingleFlagTests()
{
return std::vector<SingleFlagTest>{
{"Locked", ripple::lsfMPTLocked, "mpt_locked"},
{"CanLock", ripple::lsfMPTCanLock, "mpt_can_lock"},
{"RequireAuth", ripple::lsfMPTRequireAuth, "mpt_require_auth"},
{"CanEscrow", ripple::lsfMPTCanEscrow, "mpt_can_escrow"},
{"CanTrade", ripple::lsfMPTCanTrade, "mpt_can_trade"},
{"CanTransfer", ripple::lsfMPTCanTransfer, "mpt_can_transfer"},
{"CanClawback", ripple::lsfMPTCanClawback, "mpt_can_clawback"},
};
}
INSTANTIATE_TEST_SUITE_P(
RPCAccountMPTokenIssuancesImmutableFlagsGroup,
AccountMPTokenIssuancesImmutableFlagsTest,
ValuesIn(generateSingleFlagTests()),
tests::util::kNAME_GENERATOR
);
TEST_P(AccountMPTokenIssuancesImmutableFlagsTest, SingleFlag)
{
auto const testParams = GetParam();
auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(ledgerHeader));
auto const account = getAccountIdWithString(kACCOUNT);
auto const accountKk = ripple::keylet::account(account).key;
auto const owneDirKk = ripple::keylet::ownerDir(account).key;
EXPECT_CALL(*backend_, doFetchLedgerObject(accountKk, _, _)).WillOnce(Return(Blob{'f', 'a', 'k', 'e'}));
ripple::STObject const ownerDir = createOwnerDirLedgerObject({ripple::uint256{kISSUANCE_INDEX1}}, kISSUANCE_INDEX1);
EXPECT_CALL(*backend_, doFetchLedgerObject(owneDirKk, _, _)).WillOnce(Return(ownerDir.getSerializer().peekData()));
auto const bbs = std::vector<Blob>{
createMptIssuanceObject(kACCOUNT, 1, std::nullopt, testParams.flag, 0).getSerializer().peekData()
};
EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs));
runSpawn([this, &testParams](auto yield) {
auto const input = json::parse(
fmt::format(
R"JSON({{
"account": "{}"
}})JSON",
kACCOUNT
)
);
auto const handler = AnyHandler{AccountMPTokenIssuancesHandler{this->backend_}};
auto const output = handler.process(input, Context{yield});
ASSERT_TRUE(output);
auto const& resultJson = (*output.result).as_object();
auto const& issuances = resultJson.at("mpt_issuances").as_array();
ASSERT_EQ(issuances.size(), 1);
auto const& issuanceJson = issuances[0].as_object();
EXPECT_TRUE(issuanceJson.contains(testParams.expectedJsonKey));
EXPECT_EQ(issuanceJson.at(testParams.expectedJsonKey), true);
});
}
struct SingleMutableFlagTest {
std::string testName;
uint32_t mutableFlag;
std::string expectedJsonKey;
};
struct AccountMPTokenIssuancesMutableFlagsTest : RPCAccountMPTokenIssuancesHandlerTest,
WithParamInterface<SingleMutableFlagTest> {};
static auto
generateSingleMutableFlagTests()
{
return std::vector<SingleMutableFlagTest>{
{"CanMutateCanLock", ripple::lsmfMPTCanMutateCanLock, "mpt_can_mutate_can_lock"},
{"CanMutateRequireAuth", ripple::lsmfMPTCanMutateRequireAuth, "mpt_can_mutate_require_auth"},
{"CanMutateCanEscrow", ripple::lsmfMPTCanMutateCanEscrow, "mpt_can_mutate_can_escrow"},
{"CanMutateCanTrade", ripple::lsmfMPTCanMutateCanTrade, "mpt_can_mutate_can_trade"},
{"CanMutateCanTransfer", ripple::lsmfMPTCanMutateCanTransfer, "mpt_can_mutate_can_transfer"},
{"CanMutateCanClawback", ripple::lsmfMPTCanMutateCanClawback, "mpt_can_mutate_can_clawback"},
{"CanMutateMetadata", ripple::lsmfMPTCanMutateMetadata, "mpt_can_mutate_metadata"},
{"CanMutateTransferFee", ripple::lsmfMPTCanMutateTransferFee, "mpt_can_mutate_transfer_fee"},
};
}
INSTANTIATE_TEST_SUITE_P(
RPCAccountMPTokenIssuancesMutableFlagsGroup,
AccountMPTokenIssuancesMutableFlagsTest,
ValuesIn(generateSingleMutableFlagTests()),
tests::util::kNAME_GENERATOR
);
TEST_P(AccountMPTokenIssuancesMutableFlagsTest, SingleMutableFlag)
{
auto const testParams = GetParam();
auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(ledgerHeader));
auto const account = getAccountIdWithString(kACCOUNT);
auto const accountKk = ripple::keylet::account(account).key;
auto const owneDirKk = ripple::keylet::ownerDir(account).key;
EXPECT_CALL(*backend_, doFetchLedgerObject(accountKk, _, _)).WillOnce(Return(Blob{'f', 'a', 'k', 'e'}));
ripple::STObject const ownerDir = createOwnerDirLedgerObject({ripple::uint256{kISSUANCE_INDEX1}}, kISSUANCE_INDEX1);
EXPECT_CALL(*backend_, doFetchLedgerObject(owneDirKk, _, _)).WillOnce(Return(ownerDir.getSerializer().peekData()));
auto const bbs = std::vector<Blob>{createMptIssuanceObject(
kACCOUNT,
1,
std::nullopt,
0,
0,
std::nullopt,
std::nullopt,
std::nullopt,
std::nullopt,
std::nullopt,
testParams.mutableFlag
)
.getSerializer()
.peekData()};
EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs));
runSpawn([this, &testParams](auto yield) {
auto const input = json::parse(
fmt::format(
R"JSON({{
"account": "{}"
}})JSON",
kACCOUNT
)
);
auto const handler = AnyHandler{AccountMPTokenIssuancesHandler{this->backend_}};
auto const output = handler.process(input, Context{yield});
ASSERT_TRUE(output);
auto const& resultJson = (*output.result).as_object();
auto const& issuances = resultJson.at("mpt_issuances").as_array();
ASSERT_EQ(issuances.size(), 1);
auto const& issuanceJson = issuances[0].as_object();
EXPECT_TRUE(issuanceJson.contains(testParams.expectedJsonKey));
EXPECT_EQ(issuanceJson.at(testParams.expectedJsonKey), true);
});
}

View File

@@ -64,12 +64,14 @@ constexpr uint64_t kTOKEN2_AMOUNT = 250;
// define expected JSON for mptokens
auto const kTOKEN_OUT1 = fmt::format(
R"JSON({{
"mpt_id": "{}",
"account": "{}",
"mpt_issuance_id": "{}",
"mpt_amount": {},
"locked_amount": {},
"mpt_locked": true
}})JSON",
kTOKEN_INDEX1,
kACCOUNT,
kISSUANCE_ID_HEX,
kTOKEN1_AMOUNT,
@@ -78,11 +80,13 @@ auto const kTOKEN_OUT1 = fmt::format(
auto const kTOKEN_OUT2 = fmt::format(
R"JSON({{
"mpt_id": "{}",
"account": "{}",
"mpt_issuance_id": "{}",
"mpt_amount": {},
"mpt_authorized": true
}})JSON",
kTOKEN_INDEX2,
kACCOUNT,
kISSUANCE_ID_HEX,
kTOKEN2_AMOUNT
@@ -262,7 +266,7 @@ TEST_F(RPCAccountMPTokensHandlerTest, NonExistLedgerViaLedgerIntIndex)
TEST_F(RPCAccountMPTokensHandlerTest, LedgerSeqOutOfRangeByHash)
{
auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, 31);
auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, 31);
EXPECT_CALL(*backend_, fetchLedgerByHash(ripple::uint256{kLEDGER_HASH}, _)).WillOnce(Return(ledgerHeader));
auto const input = json::parse(
@@ -311,7 +315,7 @@ TEST_F(RPCAccountMPTokensHandlerTest, LedgerSeqOutOfRangeByIndex)
TEST_F(RPCAccountMPTokensHandlerTest, NonExistAccount)
{
auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
EXPECT_CALL(*backend_, fetchLedgerByHash(ripple::uint256{kLEDGER_HASH}, _)).WillOnce(Return(ledgerHeader));
// fetch account object return empty
EXPECT_CALL(*backend_, doFetchLedgerObject).WillOnce(Return(std::optional<Blob>{}));
@@ -339,29 +343,31 @@ TEST_F(RPCAccountMPTokensHandlerTest, NonExistAccount)
TEST_F(RPCAccountMPTokensHandlerTest, DefaultParameters)
{
auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
ON_CALL(*backend_, fetchLedgerBySequence).WillByDefault(Return(ledgerHeader));
auto account = getAccountIdWithString(kACCOUNT);
auto accountKk = ripple::keylet::account(account).key;
auto owneDirKk = ripple::keylet::ownerDir(account).key;
auto const account = getAccountIdWithString(kACCOUNT);
auto const accountKk = ripple::keylet::account(account).key;
auto const owneDirKk = ripple::keylet::ownerDir(account).key;
ON_CALL(*backend_, doFetchLedgerObject(accountKk, _, _)).WillByDefault(Return(Blob{'f', 'a', 'k', 'e'}));
ripple::STObject const ownerDir =
createOwnerDirLedgerObject({ripple::uint256{kTOKEN_INDEX1}, ripple::uint256{kTOKEN_INDEX2}}, kTOKEN_INDEX1);
ON_CALL(*backend_, doFetchLedgerObject(owneDirKk, _, _)).WillByDefault(Return(ownerDir.getSerializer().peekData()));
std::vector<Blob> bbs;
auto const token1 = createMpTokenObject(
kACCOUNT, ripple::uint192(kISSUANCE_ID_HEX), kTOKEN1_AMOUNT, ripple::lsfMPTLocked, kTOKEN1_LOCKED_AMOUNT
);
auto const bbs = std::vector<Blob>{
createMpTokenObject(
kACCOUNT, ripple::uint192(kISSUANCE_ID_HEX), kTOKEN1_AMOUNT, ripple::lsfMPTLocked, kTOKEN1_LOCKED_AMOUNT
)
.getSerializer()
.peekData(),
auto const token2 = createMpTokenObject(
kACCOUNT, ripple::uint192(kISSUANCE_ID_HEX), kTOKEN2_AMOUNT, ripple::lsfMPTAuthorized, std::nullopt
);
bbs.push_back(token1.getSerializer().peekData());
bbs.push_back(token2.getSerializer().peekData());
createMpTokenObject(
kACCOUNT, ripple::uint192(kISSUANCE_ID_HEX), kTOKEN2_AMOUNT, ripple::lsfMPTAuthorized, std::nullopt
)
.getSerializer()
.peekData()
};
EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs));
@@ -385,7 +391,7 @@ TEST_F(RPCAccountMPTokensHandlerTest, DefaultParameters)
kTOKEN_OUT2
);
auto const input = json::parse(fmt::format(R"JSON({{"account": "{}"}})JSON", kACCOUNT));
auto handler = AnyHandler{AccountMPTokensHandler{this->backend_}};
auto const handler = AnyHandler{AccountMPTokensHandler{this->backend_}};
auto const output = handler.process(input, Context{yield});
ASSERT_TRUE(output);
EXPECT_EQ(json::parse(expected), *output.result);
@@ -395,22 +401,25 @@ TEST_F(RPCAccountMPTokensHandlerTest, DefaultParameters)
TEST_F(RPCAccountMPTokensHandlerTest, UseLimit)
{
constexpr int kLIMIT = 20;
auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
ON_CALL(*backend_, fetchLedgerBySequence).WillByDefault(Return(ledgerHeader));
auto account = getAccountIdWithString(kACCOUNT);
auto accountKk = ripple::keylet::account(account).key;
auto owneDirKk = ripple::keylet::ownerDir(account).key;
auto const account = getAccountIdWithString(kACCOUNT);
auto const accountKk = ripple::keylet::account(account).key;
auto const owneDirKk = ripple::keylet::ownerDir(account).key;
ON_CALL(*backend_, doFetchLedgerObject(accountKk, _, _)).WillByDefault(Return(Blob{'f', 'a', 'k', 'e'}));
std::vector<ripple::uint256> indexes;
std::vector<Blob> bbs;
for (int i = 0; i < 50; ++i) {
indexes.emplace_back(kTOKEN_INDEX1);
auto const token = createMpTokenObject(kACCOUNT, ripple::uint192(kISSUANCE_ID_HEX), i, 0, std::nullopt);
bbs.push_back(token.getSerializer().peekData());
}
auto const indexes = std::vector<ripple::uint256>(50, ripple::uint256{kTOKEN_INDEX1});
auto const bbs = [&]() {
std::vector<Blob> v;
v.reserve(50);
for (int i = 0; i < 50; ++i) {
v.push_back(createMpTokenObject(kACCOUNT, ripple::uint192(kISSUANCE_ID_HEX), i, 0, std::nullopt)
.getSerializer()
.peekData());
}
return v;
}();
ripple::STObject ownerDir = createOwnerDirLedgerObject(indexes, kTOKEN_INDEX1);
ownerDir.setFieldU64(ripple::sfIndexNext, 99);
@@ -432,7 +441,7 @@ TEST_F(RPCAccountMPTokensHandlerTest, UseLimit)
)
);
auto handler = AnyHandler{AccountMPTokensHandler{this->backend_}};
auto const handler = AnyHandler{AccountMPTokensHandler{this->backend_}};
auto const output = handler.process(input, Context{yield});
ASSERT_TRUE(output);
@@ -454,7 +463,7 @@ TEST_F(RPCAccountMPTokensHandlerTest, UseLimit)
)
);
auto handler = AnyHandler{AccountMPTokensHandler{this->backend_}};
auto const handler = AnyHandler{AccountMPTokensHandler{this->backend_}};
auto const output = handler.process(input, Context{yield});
ASSERT_TRUE(output);
EXPECT_EQ((*output.result).as_object().at("limit").as_uint64(), AccountMPTokensHandler::kLIMIT_MIN);
@@ -472,7 +481,7 @@ TEST_F(RPCAccountMPTokensHandlerTest, UseLimit)
)
);
auto handler = AnyHandler{AccountMPTokensHandler{this->backend_}};
auto const handler = AnyHandler{AccountMPTokensHandler{this->backend_}};
auto const output = handler.process(input, Context{yield});
ASSERT_TRUE(output);
EXPECT_EQ((*output.result).as_object().at("limit").as_uint64(), AccountMPTokensHandler::kLIMIT_MAX);
@@ -483,22 +492,25 @@ TEST_F(RPCAccountMPTokensHandlerTest, MarkerOutput)
{
constexpr auto kNEXT_PAGE = 99;
constexpr auto kLIMIT = 15;
auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(ledgerHeader));
auto account = getAccountIdWithString(kACCOUNT);
auto accountKk = ripple::keylet::account(account).key;
auto ownerDirKk = ripple::keylet::ownerDir(account).key;
auto ownerDir2Kk = ripple::keylet::page(ripple::keylet::ownerDir(account), kNEXT_PAGE).key;
auto const account = getAccountIdWithString(kACCOUNT);
auto const accountKk = ripple::keylet::account(account).key;
auto const ownerDirKk = ripple::keylet::ownerDir(account).key;
auto const ownerDir2Kk = ripple::keylet::page(ripple::keylet::ownerDir(account), kNEXT_PAGE).key;
ON_CALL(*backend_, doFetchLedgerObject(accountKk, _, _)).WillByDefault(Return(Blob{'f', 'a', 'k', 'e'}));
std::vector<Blob> bbs;
bbs.reserve(kLIMIT);
for (int i = 0; i < kLIMIT; ++i) {
bbs.push_back(createMpTokenObject(kACCOUNT, ripple::uint192(kISSUANCE_ID_HEX), i, 0, std::nullopt)
.getSerializer()
.peekData());
}
auto const bbs = [&]() {
std::vector<Blob> v;
v.reserve(kLIMIT);
for (int i = 0; i < kLIMIT; ++i) {
v.push_back(createMpTokenObject(kACCOUNT, ripple::uint192(kISSUANCE_ID_HEX), i, 0, std::nullopt)
.getSerializer()
.peekData());
}
return v;
}();
EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs));
std::vector<ripple::uint256> indexes1;
@@ -528,7 +540,7 @@ TEST_F(RPCAccountMPTokensHandlerTest, MarkerOutput)
kLIMIT
)
);
auto handler = AnyHandler{AccountMPTokensHandler{this->backend_}};
auto const handler = AnyHandler{AccountMPTokensHandler{this->backend_}};
auto const output = handler.process(input, Context{yield});
ASSERT_TRUE(output);
auto const& resultJson = (*output.result).as_object();
@@ -544,22 +556,25 @@ TEST_F(RPCAccountMPTokensHandlerTest, MarkerInput)
constexpr auto kNEXT_PAGE = 99;
constexpr auto kLIMIT = 15;
auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(ledgerHeader));
auto account = getAccountIdWithString(kACCOUNT);
auto accountKk = ripple::keylet::account(account).key;
auto const account = getAccountIdWithString(kACCOUNT);
auto const accountKk = ripple::keylet::account(account).key;
ON_CALL(*backend_, doFetchLedgerObject(accountKk, _, _)).WillByDefault(Return(Blob{'f', 'a', 'k', 'e'}));
auto ownerDirKk = ripple::keylet::page(ripple::keylet::ownerDir(account), kNEXT_PAGE).key;
auto const ownerDirKk = ripple::keylet::page(ripple::keylet::ownerDir(account), kNEXT_PAGE).key;
std::vector<Blob> bbs;
std::vector<ripple::uint256> indexes;
for (int i = 0; i < kLIMIT; ++i) {
indexes.emplace_back(kTOKEN_INDEX1);
bbs.push_back(createMpTokenObject(kACCOUNT, ripple::uint192(kISSUANCE_ID_HEX), i, 0, std::nullopt)
.getSerializer()
.peekData());
}
auto const indexes = std::vector<ripple::uint256>(kLIMIT, ripple::uint256{kTOKEN_INDEX1});
auto const bbs = [&]() {
std::vector<Blob> v;
v.reserve(kLIMIT);
for (int i = 0; i < kLIMIT; ++i) {
v.push_back(createMpTokenObject(kACCOUNT, ripple::uint192(kISSUANCE_ID_HEX), i, 0, std::nullopt)
.getSerializer()
.peekData());
}
return v;
}();
ripple::STObject ownerDir = createOwnerDirLedgerObject(indexes, kTOKEN_INDEX1);
ownerDir.setFieldU64(ripple::sfIndexNext, 0);
@@ -583,7 +598,7 @@ TEST_F(RPCAccountMPTokensHandlerTest, MarkerInput)
kNEXT_PAGE
)
);
auto handler = AnyHandler{AccountMPTokensHandler{this->backend_}};
auto const handler = AnyHandler{AccountMPTokensHandler{this->backend_}};
auto const output = handler.process(input, Context{yield});
ASSERT_TRUE(output);
auto const& resultJson = (*output.result).as_object();
@@ -594,29 +609,31 @@ TEST_F(RPCAccountMPTokensHandlerTest, MarkerInput)
TEST_F(RPCAccountMPTokensHandlerTest, LimitLessThanMin)
{
auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(ledgerHeader));
auto account = getAccountIdWithString(kACCOUNT);
auto accountKk = ripple::keylet::account(account).key;
auto owneDirKk = ripple::keylet::ownerDir(account).key;
ON_CALL(*backend_, doFetchLedgerObject(accountKk, _, _)).WillByDefault(Return(Blob{'f', 'a', 'k', 'e'}));
auto const account = getAccountIdWithString(kACCOUNT);
auto const accountKk = ripple::keylet::account(account).key;
auto const owneDirKk = ripple::keylet::ownerDir(account).key;
EXPECT_CALL(*backend_, doFetchLedgerObject(accountKk, _, _)).WillOnce(Return(Blob{'f', 'a', 'k', 'e'}));
ripple::STObject const ownerDir =
createOwnerDirLedgerObject({ripple::uint256{kTOKEN_INDEX1}, ripple::uint256{kTOKEN_INDEX2}}, kTOKEN_INDEX1);
ON_CALL(*backend_, doFetchLedgerObject(owneDirKk, _, _)).WillByDefault(Return(ownerDir.getSerializer().peekData()));
EXPECT_CALL(*backend_, doFetchLedgerObject).Times(2);
EXPECT_CALL(*backend_, doFetchLedgerObject(owneDirKk, _, _)).WillOnce(Return(ownerDir.getSerializer().peekData()));
std::vector<Blob> bbs;
auto const token1 = createMpTokenObject(
kACCOUNT, ripple::uint192(kISSUANCE_ID_HEX), kTOKEN1_AMOUNT, ripple::lsfMPTLocked, kTOKEN1_LOCKED_AMOUNT
);
bbs.push_back(token1.getSerializer().peekData());
auto const bbs = std::vector<Blob>{
createMpTokenObject(
kACCOUNT, ripple::uint192(kISSUANCE_ID_HEX), kTOKEN1_AMOUNT, ripple::lsfMPTLocked, kTOKEN1_LOCKED_AMOUNT
)
.getSerializer()
.peekData(),
auto const token2 = createMpTokenObject(
kACCOUNT, ripple::uint192(kISSUANCE_ID_HEX), kTOKEN2_AMOUNT, ripple::lsfMPTAuthorized, std::nullopt
);
bbs.push_back(token2.getSerializer().peekData());
createMpTokenObject(
kACCOUNT, ripple::uint192(kISSUANCE_ID_HEX), kTOKEN2_AMOUNT, ripple::lsfMPTAuthorized, std::nullopt
)
.getSerializer()
.peekData()
};
EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs));
@@ -651,7 +668,7 @@ TEST_F(RPCAccountMPTokensHandlerTest, LimitLessThanMin)
kTOKEN_OUT2
);
auto handler = AnyHandler{AccountMPTokensHandler{this->backend_}};
auto const handler = AnyHandler{AccountMPTokensHandler{this->backend_}};
auto const output = handler.process(input, Context{yield});
ASSERT_TRUE(output);
EXPECT_EQ(json::parse(correctOutput), *output.result);
@@ -660,29 +677,31 @@ TEST_F(RPCAccountMPTokensHandlerTest, LimitLessThanMin)
TEST_F(RPCAccountMPTokensHandlerTest, LimitMoreThanMax)
{
auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(ledgerHeader));
auto account = getAccountIdWithString(kACCOUNT);
auto accountKk = ripple::keylet::account(account).key;
auto owneDirKk = ripple::keylet::ownerDir(account).key;
ON_CALL(*backend_, doFetchLedgerObject(accountKk, _, _)).WillByDefault(Return(Blob{'f', 'a', 'k', 'e'}));
auto const account = getAccountIdWithString(kACCOUNT);
auto const accountKk = ripple::keylet::account(account).key;
auto const owneDirKk = ripple::keylet::ownerDir(account).key;
EXPECT_CALL(*backend_, doFetchLedgerObject(accountKk, _, _)).WillOnce(Return(Blob{'f', 'a', 'k', 'e'}));
ripple::STObject const ownerDir =
createOwnerDirLedgerObject({ripple::uint256{kTOKEN_INDEX1}, ripple::uint256{kTOKEN_INDEX2}}, kTOKEN_INDEX1);
ON_CALL(*backend_, doFetchLedgerObject(owneDirKk, _, _)).WillByDefault(Return(ownerDir.getSerializer().peekData()));
EXPECT_CALL(*backend_, doFetchLedgerObject).Times(2);
EXPECT_CALL(*backend_, doFetchLedgerObject(owneDirKk, _, _)).WillOnce(Return(ownerDir.getSerializer().peekData()));
std::vector<Blob> bbs;
auto const token1 = createMpTokenObject(
kACCOUNT, ripple::uint192(kISSUANCE_ID_HEX), kTOKEN1_AMOUNT, ripple::lsfMPTLocked, kTOKEN1_LOCKED_AMOUNT
);
bbs.push_back(token1.getSerializer().peekData());
auto const bbs = std::vector<Blob>{
createMpTokenObject(
kACCOUNT, ripple::uint192(kISSUANCE_ID_HEX), kTOKEN1_AMOUNT, ripple::lsfMPTLocked, kTOKEN1_LOCKED_AMOUNT
)
.getSerializer()
.peekData(),
auto const token2 = createMpTokenObject(
kACCOUNT, ripple::uint192(kISSUANCE_ID_HEX), kTOKEN2_AMOUNT, ripple::lsfMPTAuthorized, std::nullopt
);
bbs.push_back(token2.getSerializer().peekData());
createMpTokenObject(
kACCOUNT, ripple::uint192(kISSUANCE_ID_HEX), kTOKEN2_AMOUNT, ripple::lsfMPTAuthorized, std::nullopt
)
.getSerializer()
.peekData()
};
EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs));
@@ -717,7 +736,7 @@ TEST_F(RPCAccountMPTokensHandlerTest, LimitMoreThanMax)
kTOKEN_OUT2
);
auto handler = AnyHandler{AccountMPTokensHandler{this->backend_}};
auto const handler = AnyHandler{AccountMPTokensHandler{this->backend_}};
auto const output = handler.process(input, Context{yield});
ASSERT_TRUE(output);
EXPECT_EQ(json::parse(correctOutput), *output.result);
@@ -726,17 +745,16 @@ TEST_F(RPCAccountMPTokensHandlerTest, LimitMoreThanMax)
TEST_F(RPCAccountMPTokensHandlerTest, EmptyResult)
{
auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30);
EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(ledgerHeader));
auto account = getAccountIdWithString(kACCOUNT);
auto accountKk = ripple::keylet::account(account).key;
auto owneDirKk = ripple::keylet::ownerDir(account).key;
ON_CALL(*backend_, doFetchLedgerObject(accountKk, _, _)).WillByDefault(Return(Blob{'f', 'a', 'k', 'e'}));
auto const account = getAccountIdWithString(kACCOUNT);
auto const accountKk = ripple::keylet::account(account).key;
auto const owneDirKk = ripple::keylet::ownerDir(account).key;
EXPECT_CALL(*backend_, doFetchLedgerObject(accountKk, _, _)).WillOnce(Return(Blob{'f', 'a', 'k', 'e'}));
ripple::STObject const ownerDir = createOwnerDirLedgerObject({}, kTOKEN_INDEX1);
ON_CALL(*backend_, doFetchLedgerObject(owneDirKk, _, _)).WillByDefault(Return(ownerDir.getSerializer().peekData()));
EXPECT_CALL(*backend_, doFetchLedgerObject).Times(2);
EXPECT_CALL(*backend_, doFetchLedgerObject(owneDirKk, _, _)).WillOnce(Return(ownerDir.getSerializer().peekData()));
runSpawn([this](auto yield) {
auto const input = json::parse(
@@ -747,7 +765,7 @@ TEST_F(RPCAccountMPTokensHandlerTest, EmptyResult)
kACCOUNT
)
);
auto handler = AnyHandler{AccountMPTokensHandler{this->backend_}};
auto const handler = AnyHandler{AccountMPTokensHandler{this->backend_}};
auto const output = handler.process(input, Context{yield});
ASSERT_TRUE(output);
EXPECT_EQ((*output.result).as_object().at("mptokens").as_array().size(), 0);