WIP permissioned domain support

This commit is contained in:
Bronek Kozicki
2025-01-17 18:54:13 +00:00
parent c325b6c7f5
commit dbaa12aa1c
5 changed files with 72 additions and 15 deletions

View File

@@ -478,9 +478,9 @@ LEDGER_ENTRY(ltVAULT, 0x0083, Vault, vault, ({
{sfAssetMaximum, soeDEFAULT}, {sfAssetMaximum, soeDEFAULT},
{sfLossUnrealized, soeDEFAULT}, {sfLossUnrealized, soeDEFAULT},
{sfMPTokenIssuanceID, soeREQUIRED}, // sfShare {sfMPTokenIssuanceID, soeREQUIRED}, // sfShare
{sfDomainID, soeOPTIONAL}, // PermissionedDomainID
// no ShareTotal ever (use MPTIssuance.sfOutstandingAmount) // no ShareTotal ever (use MPTIssuance.sfOutstandingAmount)
// no WithdrawalPolicy yet // no WithdrawalPolicy yet
// no PermissionedDomainID yet
})) }))
#undef EXPAND #undef EXPAND

View File

@@ -470,7 +470,7 @@ TRANSACTION(ttVAULT_CREATE, 64, VaultCreate, ({
{sfAsset, soeREQUIRED, soeMPTSupported}, {sfAsset, soeREQUIRED, soeMPTSupported},
{sfAssetMaximum, soeOPTIONAL}, {sfAssetMaximum, soeOPTIONAL},
{sfMPTokenMetadata, soeOPTIONAL}, {sfMPTokenMetadata, soeOPTIONAL},
// no PermissionedDomainID yet {sfDomainID, soeOPTIONAL}, // PermissionedDomainID
// no WithdrawalPolicy yet // no WithdrawalPolicy yet
{sfData, soeOPTIONAL}, {sfData, soeOPTIONAL},
})) }))
@@ -479,7 +479,6 @@ TRANSACTION(ttVAULT_CREATE, 64, VaultCreate, ({
TRANSACTION(ttVAULT_SET, 65, VaultSet, ({ TRANSACTION(ttVAULT_SET, 65, VaultSet, ({
{sfVaultID, soeREQUIRED}, {sfVaultID, soeREQUIRED},
{sfAssetMaximum, soeOPTIONAL}, {sfAssetMaximum, soeOPTIONAL},
// no PermissionedDomainID yet
// no WithdrawalPolicy yet // no WithdrawalPolicy yet
{sfData, soeOPTIONAL}, {sfData, soeOPTIONAL},
})) }))

View File

@@ -19,6 +19,9 @@
#include <xrpld/app/misc/CredentialHelpers.h> #include <xrpld/app/misc/CredentialHelpers.h>
#include <xrpld/ledger/View.h> #include <xrpld/ledger/View.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/STVector256.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/digest.h> #include <xrpl/protocol/digest.h>
#include <unordered_set> #include <unordered_set>
@@ -185,15 +188,41 @@ valid(PreclaimContext const& ctx, AccountID const& src)
} }
TER TER
authorized(ApplyContext const& ctx, AccountID const& dst) authorizedDomain(
ReadView const& view,
uint256 domainID,
AccountID const& subject)
{
auto const sle = view.read(keylet::permissionedDomain(domainID));
if (!sle || !sle->isFieldPresent(sfAcceptedCredentials))
return tefINTERNAL;
for (auto const& h : sle->getFieldArray(sfAcceptedCredentials))
{
if (!h.isFieldPresent(sfIssuer) || !h.isFieldPresent(sfCredentialType))
return tefINTERNAL;
auto const issuer = h.getAccountID(sfIssuer);
auto const type = makeSlice(h.getFieldVL(sfCredentialType));
if (view.exists(keylet::credential(subject, issuer, type)))
return tesSUCCESS;
}
return tecNO_PERMISSION;
}
TER
authorizedDepositPreauth(
ApplyView const& view,
STVector256 const& credIDs,
AccountID const& dst)
{ {
auto const& credIDs(ctx.tx.getFieldV256(sfCredentialIDs));
std::set<std::pair<AccountID, Slice>> sorted; std::set<std::pair<AccountID, Slice>> sorted;
std::vector<std::shared_ptr<SLE const>> lifeExtender; std::vector<std::shared_ptr<SLE const>> lifeExtender;
lifeExtender.reserve(credIDs.size()); lifeExtender.reserve(credIDs.size());
for (auto const& h : credIDs) for (auto const& h : credIDs)
{ {
auto sleCred = ctx.view().read(keylet::credential(h)); auto sleCred = view.read(keylet::credential(h));
if (!sleCred) // already checked in preclaim if (!sleCred) // already checked in preclaim
return tefINTERNAL; return tefINTERNAL;
@@ -204,11 +233,8 @@ authorized(ApplyContext const& ctx, AccountID const& dst)
lifeExtender.push_back(std::move(sleCred)); lifeExtender.push_back(std::move(sleCred));
} }
if (!ctx.view().exists(keylet::depositPreauth(dst, sorted))) if (!view.exists(keylet::depositPreauth(dst, sorted)))
{
JLOG(ctx.journal.trace()) << "DepositPreauth doesn't exist";
return tecNO_PERMISSION; return tecNO_PERMISSION;
}
return tesSUCCESS; return tesSUCCESS;
} }
@@ -296,8 +322,12 @@ verifyDepositPreauth(
if (src != dst) if (src != dst)
{ {
if (!ctx.view().exists(keylet::depositPreauth(dst, src))) if (!ctx.view().exists(keylet::depositPreauth(dst, src)))
return !credentialsPresent ? tecNO_PERMISSION return !credentialsPresent
: credentials::authorized(ctx, dst); ? tecNO_PERMISSION
: credentials::authorizedDepositPreauth(
ctx.view(),
ctx.tx.getFieldV256(sfCredentialIDs),
dst);
} }
} }

View File

@@ -53,10 +53,20 @@ checkFields(PreflightContext const& ctx);
TER TER
valid(PreclaimContext const& ctx, AccountID const& src); valid(PreclaimContext const& ctx, AccountID const& src);
// This function is only called when we about to return tecNO_PERMISSION because // Check if subject has any credentials maching given credential domain
// all the checks for the DepositPreauth authorization failed.
TER TER
authorized(ApplyContext const& ctx, AccountID const& dst); authorizedDomain(
ReadView const& view,
uint256 domainID,
AccountID const& subject);
// This function is only called when we about to return tecNO_PERMISSION
// because all the checks for the DepositPreauth authorization failed.
TER
authorizedDepositPreauth(
ApplyView const& view,
STVector256 const& ctx,
AccountID const& dst);
// Sort credentials array, return empty set if there are duplicates // Sort credentials array, return empty set if there are duplicates
std::set<std::pair<AccountID, Slice>> std::set<std::pair<AccountID, Slice>>

View File

@@ -21,6 +21,7 @@
#include <xrpld/app/tx/detail/VaultCreate.h> #include <xrpld/app/tx/detail/VaultCreate.h>
#include <xrpld/ledger/View.h> #include <xrpld/ledger/View.h>
#include <xrpl/protocol/Feature.h> #include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/STNumber.h> #include <xrpl/protocol/STNumber.h>
#include <xrpl/protocol/TxFlags.h> #include <xrpl/protocol/TxFlags.h>
@@ -44,6 +45,10 @@ VaultCreate::preflight(PreflightContext const& ctx)
return temSTRING_TOO_LARGE; return temSTRING_TOO_LARGE;
} }
auto const domain = ctx.tx[~sfDomainID];
if (domain && *domain == beast::zero)
return temMALFORMED;
// This block is copied from `MPTokenIssuanceCreate::preflight`. // This block is copied from `MPTokenIssuanceCreate::preflight`.
if (auto const metadata = ctx.tx[~sfMPTokenMetadata]) if (auto const metadata = ctx.tx[~sfMPTokenMetadata])
{ {
@@ -70,12 +75,23 @@ VaultCreate::preclaim(PreclaimContext const& ctx)
{ {
auto mptID = asset.get<MPTIssue>().getMptID(); auto mptID = asset.get<MPTIssue>().getMptID();
auto issuance = ctx.view.read(keylet::mptIssuance(mptID)); auto issuance = ctx.view.read(keylet::mptIssuance(mptID));
if (!issuance)
return tecNO_ENTRY;
if (issuance->getFlags() & lsfMPTLocked) if (issuance->getFlags() & lsfMPTLocked)
return tecLOCKED; return tecLOCKED;
if ((issuance->getFlags() & lsfMPTCanTransfer) == 0) if ((issuance->getFlags() & lsfMPTCanTransfer) == 0)
return tecLOCKED; return tecLOCKED;
} }
auto const domain = ctx.tx[~sfDomainID];
if (domain)
{
auto const sleDomain =
ctx.view.read(keylet::permissionedDomain(*domain));
if (!sleDomain)
return tecNO_ENTRY;
}
return tesSUCCESS; return tesSUCCESS;
} }
@@ -136,6 +152,8 @@ VaultCreate::doApply()
vault->at(sfOwner) = ownerId; vault->at(sfOwner) = ownerId;
vault->at(sfAccount) = pseudoId; vault->at(sfAccount) = pseudoId;
vault->at(sfAsset) = tx[sfAsset]; vault->at(sfAsset) = tx[sfAsset];
if (tx.isFieldPresent(sfDomainID))
vault->setFieldH256(sfDomainID, tx.getFieldH256(sfDomainID));
// Leave default values for AssetTotal and AssetAvailable, both zero. // Leave default values for AssetTotal and AssetAvailable, both zero.
if (auto value = tx[~sfAssetMaximum]) if (auto value = tx[~sfAssetMaximum])
vault->at(sfAssetMaximum) = *value; vault->at(sfAssetMaximum) = *value;