mirror of
https://github.com/XRPLF/clio.git
synced 2026-07-24 07:30:25 +00:00
feat: Support Confidential Transfer Fields (#3152)
Add Confidential Transfer Fields to MPToken, MPToken Issuance, and mpt_holders API
This commit is contained in:
@@ -139,8 +139,9 @@ struct Amendments {
|
||||
REGISTER(fixBatchInnerSigs);
|
||||
REGISTER(fixCleanup3_1_3);
|
||||
REGISTER(fixCleanup3_2_0);
|
||||
// These amendments are added because of libxrpl 3.3.0, but they are not yet supported in Clio.
|
||||
REGISTER(ConfidentialTransfer);
|
||||
|
||||
// These amendments are added because of libxrpl 3.3.0, but they are not yet supported in Clio.
|
||||
REGISTER(LendingProtocolV1_1);
|
||||
REGISTER(BatchV1_1);
|
||||
REGISTER(Sponsor);
|
||||
|
||||
@@ -55,6 +55,7 @@ AccountMPTokenIssuancesHandler::addMPTokenIssuance(
|
||||
setFlag(issuance.mptCanTrade, xrpl::lsfMPTCanTrade);
|
||||
setFlag(issuance.mptCanTransfer, xrpl::lsfMPTCanTransfer);
|
||||
setFlag(issuance.mptCanClawback, xrpl::lsfMPTCanClawback);
|
||||
setFlag(issuance.mptCanHoldConfidentialBalance, xrpl::lsfMPTCanHoldConfidentialBalance);
|
||||
|
||||
if (sle.isFieldPresent(xrpl::sfMutableFlags)) {
|
||||
auto const mutableFlags = sle.getFieldU32(xrpl::sfMutableFlags);
|
||||
@@ -95,6 +96,17 @@ AccountMPTokenIssuancesHandler::addMPTokenIssuance(
|
||||
if (sle.isFieldPresent(xrpl::sfDomainID))
|
||||
issuance.domainID = xrpl::strHex(sle.getFieldH256(xrpl::sfDomainID));
|
||||
|
||||
if (sle.isFieldPresent(xrpl::sfConfidentialOutstandingAmount)) {
|
||||
issuance.confidentialOutstandingAmount =
|
||||
sle.getFieldU64(xrpl::sfConfidentialOutstandingAmount);
|
||||
}
|
||||
|
||||
if (sle.isFieldPresent(xrpl::sfIssuerEncryptionKey))
|
||||
issuance.issuerEncryptionKey = xrpl::strHex(sle.getFieldVL(xrpl::sfIssuerEncryptionKey));
|
||||
|
||||
if (sle.isFieldPresent(xrpl::sfAuditorEncryptionKey))
|
||||
issuance.auditorEncryptionKey = xrpl::strHex(sle.getFieldVL(xrpl::sfAuditorEncryptionKey));
|
||||
|
||||
issuances.push_back(issuance);
|
||||
}
|
||||
|
||||
@@ -274,6 +286,15 @@ tag_invoke(
|
||||
setIfPresent("mpt_can_mutate_metadata", issuance.mptCanMutateMetadata);
|
||||
setIfPresent("mpt_can_mutate_transfer_fee", issuance.mptCanMutateTransferFee);
|
||||
|
||||
setIfPresent("mpt_can_hold_confidential_balance", issuance.mptCanHoldConfidentialBalance);
|
||||
setUint64IfPresent(
|
||||
"confidential_outstanding_amount",
|
||||
xrpl::sfConfidentialOutstandingAmount,
|
||||
issuance.confidentialOutstandingAmount
|
||||
);
|
||||
setIfPresent("issuer_encryption_key", issuance.issuerEncryptionKey);
|
||||
setIfPresent("auditor_encryption_key", issuance.auditorEncryptionKey);
|
||||
|
||||
jv = std::move(obj);
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,11 @@ public:
|
||||
std::optional<bool> mptCanMutateCanClawback;
|
||||
std::optional<bool> mptCanMutateMetadata;
|
||||
std::optional<bool> mptCanMutateTransferFee;
|
||||
|
||||
std::optional<bool> mptCanHoldConfidentialBalance;
|
||||
std::optional<std::uint64_t> confidentialOutstandingAmount;
|
||||
std::optional<std::string> issuerEncryptionKey;
|
||||
std::optional<std::string> auditorEncryptionKey;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -52,6 +52,31 @@ AccountMPTokensHandler::addMPToken(std::vector<MPTokenResponse>& mpts, xrpl::SLE
|
||||
setFlag(token.mptLocked, xrpl::lsfMPTLocked);
|
||||
setFlag(token.mptAuthorized, xrpl::lsfMPTAuthorized);
|
||||
|
||||
if (sle.isFieldPresent(xrpl::sfConfidentialBalanceInbox)) {
|
||||
token.confidentialBalanceInbox =
|
||||
xrpl::strHex(sle.getFieldVL(xrpl::sfConfidentialBalanceInbox));
|
||||
}
|
||||
|
||||
if (sle.isFieldPresent(xrpl::sfConfidentialBalanceSpending)) {
|
||||
token.confidentialBalanceSpending =
|
||||
xrpl::strHex(sle.getFieldVL(xrpl::sfConfidentialBalanceSpending));
|
||||
}
|
||||
|
||||
if (sle.isFieldPresent(xrpl::sfConfidentialBalanceVersion))
|
||||
token.confidentialBalanceVersion = sle.getFieldU32(xrpl::sfConfidentialBalanceVersion);
|
||||
|
||||
if (sle.isFieldPresent(xrpl::sfIssuerEncryptedBalance)) {
|
||||
token.issuerEncryptedBalance = xrpl::strHex(sle.getFieldVL(xrpl::sfIssuerEncryptedBalance));
|
||||
}
|
||||
|
||||
if (sle.isFieldPresent(xrpl::sfAuditorEncryptedBalance)) {
|
||||
token.auditorEncryptedBalance =
|
||||
xrpl::strHex(sle.getFieldVL(xrpl::sfAuditorEncryptedBalance));
|
||||
}
|
||||
|
||||
if (sle.isFieldPresent(xrpl::sfHolderEncryptionKey))
|
||||
token.holderEncryptionKey = xrpl::strHex(sle.getFieldVL(xrpl::sfHolderEncryptionKey));
|
||||
|
||||
mpts.push_back(token);
|
||||
}
|
||||
|
||||
@@ -203,6 +228,13 @@ tag_invoke(
|
||||
setIfPresent("mpt_locked", mptoken.mptLocked);
|
||||
setIfPresent("mpt_authorized", mptoken.mptAuthorized);
|
||||
|
||||
setIfPresent(JS(confidential_balance_inbox), mptoken.confidentialBalanceInbox);
|
||||
setIfPresent(JS(confidential_balance_spending), mptoken.confidentialBalanceSpending);
|
||||
setIfPresent(JS(confidential_balance_version), mptoken.confidentialBalanceVersion);
|
||||
setIfPresent(JS(issuer_encrypted_balance), mptoken.issuerEncryptedBalance);
|
||||
setIfPresent(JS(auditor_encrypted_balance), mptoken.auditorEncryptedBalance);
|
||||
setIfPresent(JS(holder_encryption_key), mptoken.holderEncryptionKey);
|
||||
|
||||
jv = std::move(obj);
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,13 @@ public:
|
||||
|
||||
std::optional<bool> mptLocked;
|
||||
std::optional<bool> mptAuthorized;
|
||||
|
||||
std::optional<std::string> confidentialBalanceInbox;
|
||||
std::optional<std::string> confidentialBalanceSpending;
|
||||
std::optional<uint32_t> confidentialBalanceVersion;
|
||||
std::optional<std::string> issuerEncryptedBalance;
|
||||
std::optional<std::string> auditorEncryptedBalance;
|
||||
std::optional<std::string> holderEncryptionKey;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerHeader.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STInteger.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
@@ -74,14 +75,50 @@ MPTHoldersHandler::process(MPTHoldersHandler::Input const& input, Context const&
|
||||
|
||||
mptJson[JS(account)] = toBase58(sle[xrpl::sfAccount]);
|
||||
mptJson[JS(flags)] = sle.getFlags();
|
||||
mptJson["mpt_amount"] = toBoostJson(
|
||||
mptJson[JS(mpt_amount)] = toBoostJson(
|
||||
xrpl::STUInt64{xrpl::sfMPTAmount, sle[xrpl::sfMPTAmount]}.getJson(
|
||||
JsonOptions::Values::None
|
||||
)
|
||||
);
|
||||
mptJson["mptoken_index"] =
|
||||
mptJson[JS(mptoken_index)] =
|
||||
xrpl::to_string(xrpl::keylet::mptoken(mptID, sle[xrpl::sfAccount]).key);
|
||||
|
||||
if (sle.isFieldPresent(xrpl::sfLockedAmount)) {
|
||||
mptJson["locked_amount"] = toBoostJson(
|
||||
xrpl::STUInt64{xrpl::sfLockedAmount, sle[xrpl::sfLockedAmount]}.getJson(
|
||||
JsonOptions::Values::None
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (sle.isFieldPresent(xrpl::sfConfidentialBalanceInbox)) {
|
||||
mptJson[JS(confidential_balance_inbox)] =
|
||||
xrpl::strHex(sle.getFieldVL(xrpl::sfConfidentialBalanceInbox));
|
||||
}
|
||||
|
||||
if (sle.isFieldPresent(xrpl::sfConfidentialBalanceSpending)) {
|
||||
mptJson[JS(confidential_balance_spending)] =
|
||||
xrpl::strHex(sle.getFieldVL(xrpl::sfConfidentialBalanceSpending));
|
||||
}
|
||||
|
||||
if (sle.isFieldPresent(xrpl::sfConfidentialBalanceVersion))
|
||||
mptJson[JS(confidential_balance_version)] = sle[xrpl::sfConfidentialBalanceVersion];
|
||||
|
||||
if (sle.isFieldPresent(xrpl::sfIssuerEncryptedBalance)) {
|
||||
mptJson[JS(issuer_encrypted_balance)] =
|
||||
xrpl::strHex(sle.getFieldVL(xrpl::sfIssuerEncryptedBalance));
|
||||
}
|
||||
|
||||
if (sle.isFieldPresent(xrpl::sfAuditorEncryptedBalance)) {
|
||||
mptJson[JS(auditor_encrypted_balance)] =
|
||||
xrpl::strHex(sle.getFieldVL(xrpl::sfAuditorEncryptedBalance));
|
||||
}
|
||||
|
||||
if (sle.isFieldPresent(xrpl::sfHolderEncryptionKey)) {
|
||||
mptJson[JS(holder_encryption_key)] =
|
||||
xrpl::strHex(sle.getFieldVL(xrpl::sfHolderEncryptionKey));
|
||||
}
|
||||
|
||||
output.mpts.push_back(mptJson);
|
||||
}
|
||||
|
||||
|
||||
@@ -1493,7 +1493,10 @@ createMptIssuanceObject(
|
||||
std::optional<std::uint64_t> maxAmount,
|
||||
std::optional<std::uint64_t> lockedAmount,
|
||||
std::optional<std::string_view> domainId,
|
||||
std::optional<std::uint32_t> mutableFlags
|
||||
std::optional<std::uint32_t> mutableFlags,
|
||||
std::optional<std::string_view> issuerEncryptionKey,
|
||||
std::optional<std::string_view> auditorEncryptionKey,
|
||||
std::optional<std::uint64_t> confidentialOutstandingAmount
|
||||
)
|
||||
{
|
||||
xrpl::STObject mptIssuance(xrpl::sfLedgerEntry);
|
||||
@@ -1522,6 +1525,19 @@ createMptIssuanceObject(
|
||||
mptIssuance.setFieldH256(xrpl::sfDomainID, xrpl::uint256{*domainId});
|
||||
if (mutableFlags.has_value())
|
||||
mptIssuance.setFieldU32(xrpl::sfMutableFlags, *mutableFlags);
|
||||
if (issuerEncryptionKey.has_value()) {
|
||||
xrpl::Slice const slice(issuerEncryptionKey->data(), issuerEncryptionKey->size());
|
||||
mptIssuance.setFieldVL(xrpl::sfIssuerEncryptionKey, slice);
|
||||
}
|
||||
if (auditorEncryptionKey.has_value()) {
|
||||
xrpl::Slice const slice(auditorEncryptionKey->data(), auditorEncryptionKey->size());
|
||||
mptIssuance.setFieldVL(xrpl::sfAuditorEncryptionKey, slice);
|
||||
}
|
||||
if (confidentialOutstandingAmount.has_value()) {
|
||||
mptIssuance.setFieldU64(
|
||||
xrpl::sfConfidentialOutstandingAmount, *confidentialOutstandingAmount
|
||||
);
|
||||
}
|
||||
|
||||
return mptIssuance;
|
||||
}
|
||||
@@ -1532,7 +1548,13 @@ createMpTokenObject(
|
||||
xrpl::uint192 issuanceID,
|
||||
std::uint64_t mptAmount,
|
||||
std::uint32_t flags,
|
||||
std::optional<uint64_t> lockedAmount
|
||||
std::optional<uint64_t> lockedAmount,
|
||||
std::optional<std::string_view> confidentialBalanceInbox,
|
||||
std::optional<std::string_view> confidentialBalanceSpending,
|
||||
std::optional<std::uint32_t> confidentialBalanceVersion,
|
||||
std::optional<std::string_view> issuerEncryptedBalance,
|
||||
std::optional<std::string_view> auditorEncryptedBalance,
|
||||
std::optional<std::string_view> holderEncryptionKey
|
||||
)
|
||||
{
|
||||
xrpl::STObject mptoken(xrpl::sfLedgerEntry);
|
||||
@@ -1549,6 +1571,23 @@ createMpTokenObject(
|
||||
if (lockedAmount.has_value())
|
||||
mptoken.setFieldU64(xrpl::sfLockedAmount, *lockedAmount);
|
||||
|
||||
auto const setVL = [&](xrpl::SField const& field,
|
||||
std::optional<std::string_view> const& value) {
|
||||
if (value.has_value()) {
|
||||
xrpl::Slice const slice(value->data(), value->size());
|
||||
mptoken.setFieldVL(field, slice);
|
||||
}
|
||||
};
|
||||
|
||||
setVL(xrpl::sfConfidentialBalanceInbox, confidentialBalanceInbox);
|
||||
setVL(xrpl::sfConfidentialBalanceSpending, confidentialBalanceSpending);
|
||||
setVL(xrpl::sfIssuerEncryptedBalance, issuerEncryptedBalance);
|
||||
setVL(xrpl::sfAuditorEncryptedBalance, auditorEncryptedBalance);
|
||||
setVL(xrpl::sfHolderEncryptionKey, holderEncryptionKey);
|
||||
|
||||
if (confidentialBalanceVersion.has_value())
|
||||
mptoken.setFieldU32(xrpl::sfConfidentialBalanceVersion, *confidentialBalanceVersion);
|
||||
|
||||
return mptoken;
|
||||
}
|
||||
|
||||
|
||||
@@ -466,7 +466,10 @@ createMptIssuanceObject(
|
||||
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::uint32_t> mutableFlags = std::nullopt
|
||||
std::optional<std::uint32_t> mutableFlags = std::nullopt,
|
||||
std::optional<std::string_view> issuerEncryptionKey = std::nullopt,
|
||||
std::optional<std::string_view> auditorEncryptionKey = std::nullopt,
|
||||
std::optional<std::uint64_t> confidentialOutstandingAmount = std::nullopt
|
||||
);
|
||||
|
||||
[[nodiscard]] xrpl::STObject
|
||||
@@ -475,7 +478,13 @@ createMpTokenObject(
|
||||
xrpl::uint192 issuanceID,
|
||||
std::uint64_t mptAmount = 1,
|
||||
std::uint32_t flags = 0,
|
||||
std::optional<uint64_t> lockedAmount = std::nullopt
|
||||
std::optional<uint64_t> lockedAmount = std::nullopt,
|
||||
std::optional<std::string_view> confidentialBalanceInbox = std::nullopt,
|
||||
std::optional<std::string_view> confidentialBalanceSpending = std::nullopt,
|
||||
std::optional<std::uint32_t> confidentialBalanceVersion = std::nullopt,
|
||||
std::optional<std::string_view> issuerEncryptedBalance = std::nullopt,
|
||||
std::optional<std::string_view> auditorEncryptedBalance = std::nullopt,
|
||||
std::optional<std::string_view> holderEncryptionKey = std::nullopt
|
||||
);
|
||||
|
||||
[[nodiscard]] xrpl::STObject
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/strHex.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/LedgerHeader.h>
|
||||
@@ -26,6 +27,7 @@
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
using namespace rpc;
|
||||
@@ -1080,6 +1082,92 @@ TEST_F(RPCAccountMPTokenIssuancesHandlerTest, MutableFlags)
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(RPCAccountMPTokenIssuancesHandlerTest, ConfidentialFields)
|
||||
{
|
||||
constexpr auto kIssuerEncryptionKey = "issuer-pubkey";
|
||||
constexpr auto kAuditorEncryptionKey = "auditor-pubkey";
|
||||
constexpr uint64_t kConfidentialOutstandingAmount = 42;
|
||||
|
||||
auto const ledgerHeader = createLedgerHeader(kLedgerHash, 30);
|
||||
EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(ledgerHeader));
|
||||
|
||||
auto const account = getAccountIdWithString(kAccount);
|
||||
auto const accountKk = xrpl::keylet::account(account).key;
|
||||
auto const ownerDirKk = xrpl::keylet::ownerDir(account).key;
|
||||
EXPECT_CALL(*backend_, doFetchLedgerObject(accountKk, _, _))
|
||||
.WillOnce(Return(Blob{'f', 'a', 'k', 'e'}));
|
||||
|
||||
xrpl::STObject const ownerDir =
|
||||
createOwnerDirLedgerObject({xrpl::uint256{kIssuanceIndeX1}}, kIssuanceIndeX1);
|
||||
EXPECT_CALL(*backend_, doFetchLedgerObject(ownerDirKk, _, _))
|
||||
.WillOnce(Return(ownerDir.getSerializer().peekData()));
|
||||
|
||||
auto const issuance = createMptIssuanceObject(
|
||||
kAccount,
|
||||
1,
|
||||
std::nullopt,
|
||||
xrpl::lsfMPTCanHoldConfidentialBalance,
|
||||
kIssuancE1OutstandingAmount,
|
||||
std::nullopt,
|
||||
std::nullopt,
|
||||
std::nullopt,
|
||||
std::nullopt,
|
||||
std::nullopt,
|
||||
std::nullopt,
|
||||
kIssuerEncryptionKey,
|
||||
kAuditorEncryptionKey,
|
||||
kConfidentialOutstandingAmount
|
||||
);
|
||||
auto const bbs = std::vector<Blob>{issuance.getSerializer().peekData()};
|
||||
EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs));
|
||||
|
||||
runSpawn([&](auto yield) {
|
||||
auto const input = boost::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": 1,
|
||||
"outstanding_amount": "{}",
|
||||
"mpt_can_hold_confidential_balance": true,
|
||||
"confidential_outstanding_amount": "{}",
|
||||
"issuer_encryption_key": "{}",
|
||||
"auditor_encryption_key": "{}"
|
||||
}}
|
||||
]
|
||||
}})JSON",
|
||||
kAccount,
|
||||
kLedgerHash,
|
||||
kIssuanceIndeX1,
|
||||
kAccount,
|
||||
kIssuancE1OutstandingAmount,
|
||||
kConfidentialOutstandingAmount,
|
||||
xrpl::strHex(std::string_view{kIssuerEncryptionKey}),
|
||||
xrpl::strHex(std::string_view{kAuditorEncryptionKey})
|
||||
);
|
||||
|
||||
auto const handler = AnyHandler{AccountMPTokenIssuancesHandler{this->backend_}};
|
||||
auto const output = handler.process(input, Context{yield});
|
||||
ASSERT_TRUE(output);
|
||||
EXPECT_EQ(boost::json::parse(correctOutput), *output.result);
|
||||
});
|
||||
}
|
||||
|
||||
struct SingleFlagTest {
|
||||
std::string testName;
|
||||
uint32_t flag;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/strHex.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/LedgerHeader.h>
|
||||
@@ -24,6 +25,7 @@
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
using namespace rpc;
|
||||
@@ -813,6 +815,82 @@ TEST_F(RPCAccountMPTokensHandlerTest, EmptyResult)
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(RPCAccountMPTokensHandlerTest, ConfidentialFields)
|
||||
{
|
||||
constexpr auto kConfidentialBalanceInbox = "inbox-ciphertext";
|
||||
constexpr auto kConfidentialBalanceSpending = "spending-ciphertext";
|
||||
constexpr auto kConfidentialBalanceVersion = 3u;
|
||||
constexpr auto kIssuerEncryptedBalance = "issuer-balance-ciphertext";
|
||||
constexpr auto kAuditorEncryptedBalance = "auditor-balance-ciphertext";
|
||||
constexpr auto kHolderEncryptionKey = "holder-pubkey";
|
||||
|
||||
auto const ledgerHeader = createLedgerHeader(kLedgerHash, 30);
|
||||
EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(ledgerHeader));
|
||||
|
||||
auto const account = getAccountIdWithString(kAccount);
|
||||
auto const accountKk = xrpl::keylet::account(account).key;
|
||||
auto const ownerDirKk = xrpl::keylet::ownerDir(account).key;
|
||||
EXPECT_CALL(*backend_, doFetchLedgerObject(accountKk, _, _))
|
||||
.WillOnce(Return(Blob{'f', 'a', 'k', 'e'}));
|
||||
|
||||
xrpl::STObject const ownerDir =
|
||||
createOwnerDirLedgerObject({xrpl::uint256{kTokenIndeX1}}, kTokenIndeX1);
|
||||
EXPECT_CALL(*backend_, doFetchLedgerObject(ownerDirKk, _, _))
|
||||
.WillOnce(Return(ownerDir.getSerializer().peekData()));
|
||||
|
||||
xrpl::STObject const mptoken = createMpTokenObject(
|
||||
kAccount,
|
||||
xrpl::uint192(kIssuanceIdHex),
|
||||
kTokeN1Amount,
|
||||
0,
|
||||
std::nullopt,
|
||||
kConfidentialBalanceInbox,
|
||||
kConfidentialBalanceSpending,
|
||||
kConfidentialBalanceVersion,
|
||||
kIssuerEncryptedBalance,
|
||||
kAuditorEncryptedBalance,
|
||||
kHolderEncryptionKey
|
||||
);
|
||||
auto const bbs = std::vector<Blob>{mptoken.getSerializer().peekData()};
|
||||
EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs));
|
||||
|
||||
runSpawn([&, this](auto yield) {
|
||||
auto const input =
|
||||
boost::json::parse(fmt::format(R"JSON({{"account": "{}"}})JSON", kAccount));
|
||||
auto const handler = AnyHandler{AccountMPTokensHandler{this->backend_}};
|
||||
auto const output = handler.process(input, Context{yield});
|
||||
ASSERT_TRUE(output);
|
||||
|
||||
auto const& mptokens = output.result->as_object().at("mptokens").as_array();
|
||||
ASSERT_EQ(mptokens.size(), 1);
|
||||
auto const& token = mptokens[0].as_object();
|
||||
|
||||
EXPECT_EQ(
|
||||
token.at("confidential_balance_inbox").as_string(),
|
||||
xrpl::strHex(std::string_view{kConfidentialBalanceInbox})
|
||||
);
|
||||
EXPECT_EQ(
|
||||
token.at("confidential_balance_spending").as_string(),
|
||||
xrpl::strHex(std::string_view{kConfidentialBalanceSpending})
|
||||
);
|
||||
EXPECT_EQ(
|
||||
token.at("confidential_balance_version").as_uint64(), kConfidentialBalanceVersion
|
||||
);
|
||||
EXPECT_EQ(
|
||||
token.at("issuer_encrypted_balance").as_string(),
|
||||
xrpl::strHex(std::string_view{kIssuerEncryptedBalance})
|
||||
);
|
||||
EXPECT_EQ(
|
||||
token.at("auditor_encrypted_balance").as_string(),
|
||||
xrpl::strHex(std::string_view{kAuditorEncryptedBalance})
|
||||
);
|
||||
EXPECT_EQ(
|
||||
token.at("holder_encryption_key").as_string(),
|
||||
xrpl::strHex(std::string_view{kHolderEncryptionKey})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Regression test: UInt64 amount fields must be serialized as base-10 JSON strings (as xrpld
|
||||
// does) so that values greater than 2^53 are not silently rounded by JSON parsers backed by
|
||||
// IEEE-754 doubles. 2^53 itself is still exactly representable as a double, but it must be emitted
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
using namespace rpc;
|
||||
@@ -479,6 +480,93 @@ TEST_F(RPCMPTHoldersHandlerTest, CustomAmounts)
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(RPCMPTHoldersHandlerTest, ConfidentialFields)
|
||||
{
|
||||
constexpr auto kConfidentialBalanceInbox = "inbox-ciphertext";
|
||||
constexpr auto kConfidentialBalanceSpending = "spending-ciphertext";
|
||||
constexpr auto kConfidentialBalanceVersion = 3u;
|
||||
constexpr auto kIssuerEncryptedBalance = "issuer-balance-ciphertext";
|
||||
constexpr auto kAuditorEncryptedBalance = "auditor-balance-ciphertext";
|
||||
constexpr auto kHolderEncryptionKey = "holder-pubkey";
|
||||
constexpr auto kLockedAmount = 5;
|
||||
|
||||
auto const currentOutput = fmt::format(
|
||||
R"JSON({{
|
||||
"mpt_issuance_id": "{}",
|
||||
"limit": 50,
|
||||
"ledger_index": 30,
|
||||
"mptokens": [{{
|
||||
"account": "{}",
|
||||
"flags": 0,
|
||||
"mpt_amount": "1",
|
||||
"mptoken_index": "D137F2E5A5767A06CB7A8F060ADE442A30CFF95028E1AF4B8767E3A56877205A",
|
||||
"locked_amount": "{}",
|
||||
"confidential_balance_inbox": "{}",
|
||||
"confidential_balance_spending": "{}",
|
||||
"confidential_balance_version": {},
|
||||
"issuer_encrypted_balance": "{}",
|
||||
"auditor_encrypted_balance": "{}",
|
||||
"holder_encryption_key": "{}"
|
||||
}}],
|
||||
"validated": true
|
||||
}})JSON",
|
||||
kMptId,
|
||||
kHoldeR1Account,
|
||||
kLockedAmount,
|
||||
xrpl::strHex(std::string_view{kConfidentialBalanceInbox}),
|
||||
xrpl::strHex(std::string_view{kConfidentialBalanceSpending}),
|
||||
kConfidentialBalanceVersion,
|
||||
xrpl::strHex(std::string_view{kIssuerEncryptedBalance}),
|
||||
xrpl::strHex(std::string_view{kAuditorEncryptedBalance}),
|
||||
xrpl::strHex(std::string_view{kHolderEncryptionKey})
|
||||
);
|
||||
|
||||
auto ledgerInfo = createLedgerHeader(kLedgerHash, 30);
|
||||
EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(ledgerInfo));
|
||||
auto const issuanceKk = xrpl::keylet::mptokenIssuance(xrpl::uint192(kMptId)).key;
|
||||
ON_CALL(*backend_, doFetchLedgerObject(issuanceKk, 30, _))
|
||||
.WillByDefault(Return(Blob{'f', 'a', 'k', 'e'}));
|
||||
|
||||
auto const mptoken = createMpTokenObject(
|
||||
kHoldeR1Account,
|
||||
xrpl::uint192(kMptId),
|
||||
1,
|
||||
0,
|
||||
kLockedAmount,
|
||||
kConfidentialBalanceInbox,
|
||||
kConfidentialBalanceSpending,
|
||||
kConfidentialBalanceVersion,
|
||||
kIssuerEncryptedBalance,
|
||||
kAuditorEncryptedBalance,
|
||||
kHolderEncryptionKey
|
||||
);
|
||||
std::vector<Blob> const mpts = {mptoken.getSerializer().peekData()};
|
||||
ON_CALL(*backend_, fetchMPTHolders)
|
||||
.WillByDefault(Return(MPTHoldersAndCursor{.mptokens = mpts, .cursor = {}}));
|
||||
EXPECT_CALL(
|
||||
*backend_,
|
||||
fetchMPTHolders(
|
||||
xrpl::uint192(kMptId), testing::_, testing::Eq(std::nullopt), Const(30), testing::_
|
||||
)
|
||||
)
|
||||
.Times(1);
|
||||
|
||||
auto const input = boost::json::parse(
|
||||
fmt::format(
|
||||
R"JSON({{
|
||||
"mpt_issuance_id": "{}"
|
||||
}})JSON",
|
||||
kMptId
|
||||
)
|
||||
);
|
||||
runSpawn([&, this](auto& yield) {
|
||||
auto handler = AnyHandler{MPTHoldersHandler{this->backend_}};
|
||||
auto const output = handler.process(input, Context{yield});
|
||||
ASSERT_TRUE(output);
|
||||
EXPECT_EQ(boost::json::parse(currentOutput), *output.result);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(RPCMPTHoldersHandlerTest, SpecificLedgerIndex)
|
||||
{
|
||||
auto const specificLedger = 20;
|
||||
|
||||
Reference in New Issue
Block a user