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},
{sfLossUnrealized, soeDEFAULT},
{sfMPTokenIssuanceID, soeREQUIRED}, // sfShare
{sfDomainID, soeOPTIONAL}, // PermissionedDomainID
// no ShareTotal ever (use MPTIssuance.sfOutstandingAmount)
// no WithdrawalPolicy yet
// no PermissionedDomainID yet
}))
#undef EXPAND

View File

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

View File

@@ -19,6 +19,9 @@
#include <xrpld/app/misc/CredentialHelpers.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 <unordered_set>
@@ -185,15 +188,41 @@ valid(PreclaimContext const& ctx, AccountID const& src)
}
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::vector<std::shared_ptr<SLE const>> lifeExtender;
lifeExtender.reserve(credIDs.size());
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
return tefINTERNAL;
@@ -204,11 +233,8 @@ authorized(ApplyContext const& ctx, AccountID const& dst)
lifeExtender.push_back(std::move(sleCred));
}
if (!ctx.view().exists(keylet::depositPreauth(dst, sorted)))
{
JLOG(ctx.journal.trace()) << "DepositPreauth doesn't exist";
if (!view.exists(keylet::depositPreauth(dst, sorted)))
return tecNO_PERMISSION;
}
return tesSUCCESS;
}
@@ -296,8 +322,12 @@ verifyDepositPreauth(
if (src != dst)
{
if (!ctx.view().exists(keylet::depositPreauth(dst, src)))
return !credentialsPresent ? tecNO_PERMISSION
: credentials::authorized(ctx, dst);
return !credentialsPresent
? tecNO_PERMISSION
: credentials::authorizedDepositPreauth(
ctx.view(),
ctx.tx.getFieldV256(sfCredentialIDs),
dst);
}
}

View File

@@ -53,10 +53,20 @@ checkFields(PreflightContext const& ctx);
TER
valid(PreclaimContext const& ctx, AccountID const& src);
// This function is only called when we about to return tecNO_PERMISSION because
// all the checks for the DepositPreauth authorization failed.
// Check if subject has any credentials maching given credential domain
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
std::set<std::pair<AccountID, Slice>>

View File

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