mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 19:15:54 +00:00
WIP permissioned domain support
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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},
|
||||||
}))
|
}))
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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>>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user