mirror of
https://github.com/XRPLF/rippled.git
synced 2026-06-22 10:07:10 +00:00
Compare commits
2 Commits
ximinez/fi
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6a1ad5bb3 | ||
|
|
772ea80a25 |
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include <xrpl/basics/IntrusivePointer.ipp>
|
#include <xrpl/basics/IntrusivePointer.ipp>
|
||||||
#include <xrpl/basics/TaggedCache.h>
|
#include <xrpl/basics/TaggedCache.h>
|
||||||
#include <xrpl/basics/scope.h>
|
|
||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
@@ -537,15 +536,8 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
|
|||||||
std::vector<key_type> v;
|
std::vector<key_type> v;
|
||||||
|
|
||||||
{
|
{
|
||||||
std::unique_lock lock(mutex_);
|
std::scoped_lock const lock(mutex_);
|
||||||
for (auto size = cache_.size(); v.capacity() < size; size = cache_.size())
|
v.reserve(cache_.size());
|
||||||
{
|
|
||||||
ScopeUnlock const unlock(lock);
|
|
||||||
v.reserve(size);
|
|
||||||
}
|
|
||||||
XRPL_ASSERT(lock.owns_lock(), "xrpl::TaggedCache::getKeys(): owns lock");
|
|
||||||
XRPL_ASSERT(
|
|
||||||
v.capacity() >= cache_.size(), "xrpl::TaggedCache::getKeys(): sufficient capacity");
|
|
||||||
for (auto const& _ : cache_)
|
for (auto const& _ : cache_)
|
||||||
v.push_back(_.first);
|
v.push_back(_.first);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,13 +23,9 @@ checkTxPermission(SLE::const_ref delegate, STTx const& tx);
|
|||||||
* @param delegate The delegate account.
|
* @param delegate The delegate account.
|
||||||
* @param type Used to determine which granted granular permissions to load,
|
* @param type Used to determine which granted granular permissions to load,
|
||||||
* based on the transaction type.
|
* based on the transaction type.
|
||||||
* @param granularPermissions Granted granular permissions tied to the
|
* @return the granted granular permissions tied to the transaction type.
|
||||||
* transaction type.
|
|
||||||
*/
|
*/
|
||||||
void
|
std::unordered_set<GranularPermissionType>
|
||||||
loadGranularPermission(
|
getGranularPermission(SLE::const_ref delegate, TxType const& type);
|
||||||
SLE::const_ref delegate,
|
|
||||||
TxType const& type,
|
|
||||||
std::unordered_set<GranularPermissionType>& granularPermissions);
|
|
||||||
|
|
||||||
} // namespace xrpl
|
} // namespace xrpl
|
||||||
|
|||||||
@@ -7,8 +7,13 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
|
class STTx;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We have both transaction type permissions and granular type permissions.
|
* We have both transaction type permissions and granular type permissions.
|
||||||
* Since we will reuse the TransactionFormats to parse the Transaction
|
* Since we will reuse the TransactionFormats to parse the Transaction
|
||||||
@@ -19,15 +24,15 @@ namespace xrpl {
|
|||||||
// Macro-generated, complex
|
// Macro-generated, complex
|
||||||
// NOLINTNEXTLINE(cppcoreguidelines-use-enum-class)
|
// NOLINTNEXTLINE(cppcoreguidelines-use-enum-class)
|
||||||
enum GranularPermissionType : std::uint32_t {
|
enum GranularPermissionType : std::uint32_t {
|
||||||
#pragma push_macro("PERMISSION")
|
#pragma push_macro("GRANULAR_PERMISSION")
|
||||||
#undef PERMISSION
|
#undef GRANULAR_PERMISSION
|
||||||
|
|
||||||
#define PERMISSION(type, txType, value) type = (value),
|
#define GRANULAR_PERMISSION(name, txType, value, ...) name = (value),
|
||||||
|
|
||||||
#include <xrpl/protocol/detail/permissions.macro>
|
#include <xrpl/protocol/detail/permissions.macro>
|
||||||
|
|
||||||
#undef PERMISSION
|
#undef GRANULAR_PERMISSION
|
||||||
#pragma pop_macro("PERMISSION")
|
#pragma pop_macro("GRANULAR_PERMISSION")
|
||||||
};
|
};
|
||||||
|
|
||||||
// Injected bare enumerators (xrpl::delegable / xrpl::notDelegable) are required by preprocessor
|
// Injected bare enumerators (xrpl::delegable / xrpl::notDelegable) are required by preprocessor
|
||||||
@@ -40,15 +45,30 @@ class Permission
|
|||||||
private:
|
private:
|
||||||
Permission();
|
Permission();
|
||||||
|
|
||||||
std::unordered_map<std::uint16_t, uint256> txFeatureMap_;
|
struct GranularPermissionEntry
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
TxType txType;
|
||||||
|
std::uint32_t permittedFlags;
|
||||||
|
SOTemplate permittedFields;
|
||||||
|
|
||||||
std::unordered_map<std::uint16_t, Delegation> delegableTx_;
|
GranularPermissionEntry(
|
||||||
|
std::string name,
|
||||||
|
TxType txType,
|
||||||
|
std::uint32_t permittedFlags,
|
||||||
|
std::vector<SOElement> fields);
|
||||||
|
};
|
||||||
|
|
||||||
std::unordered_map<std::string, GranularPermissionType> granularPermissionMap_;
|
struct TxDelegationEntry
|
||||||
|
{
|
||||||
|
uint256 amendment;
|
||||||
|
Delegation delegable{NotDelegable};
|
||||||
|
};
|
||||||
|
|
||||||
std::unordered_map<GranularPermissionType, std::string> granularNameMap_;
|
std::unordered_set<TxType> granularTxTypes_;
|
||||||
|
std::unordered_map<TxType, TxDelegationEntry> txDelegationMap_;
|
||||||
std::unordered_map<GranularPermissionType, TxType> granularTxTypeMap_;
|
std::unordered_map<std::string, GranularPermissionType> granularPermissionsByName_;
|
||||||
|
std::unordered_map<GranularPermissionType, GranularPermissionEntry> granularPermissions_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static Permission const&
|
static Permission const&
|
||||||
@@ -59,30 +79,52 @@ public:
|
|||||||
operator=(Permission const&) = delete;
|
operator=(Permission const&) = delete;
|
||||||
|
|
||||||
[[nodiscard]] std::optional<std::string>
|
[[nodiscard]] std::optional<std::string>
|
||||||
getPermissionName(std::uint32_t const value) const;
|
getPermissionName(std::uint32_t value) const;
|
||||||
|
|
||||||
[[nodiscard]] std::optional<std::uint32_t>
|
[[nodiscard]] std::optional<std::uint32_t>
|
||||||
getGranularValue(std::string const& name) const;
|
getGranularValue(std::string const& name) const;
|
||||||
|
|
||||||
[[nodiscard]] std::optional<std::string>
|
[[nodiscard]] std::optional<std::string>
|
||||||
getGranularName(GranularPermissionType const& value) const;
|
getGranularName(GranularPermissionType value) const;
|
||||||
|
|
||||||
[[nodiscard]] std::optional<TxType>
|
[[nodiscard]] std::optional<TxType>
|
||||||
getGranularTxType(GranularPermissionType const& gpType) const;
|
getGranularTxType(GranularPermissionType gpType) const;
|
||||||
|
|
||||||
|
// Returns a reference to avoid copying uint256 - 32 bytes. std::optional
|
||||||
|
// cannot hold references directly, so std::reference_wrapper is used.
|
||||||
[[nodiscard]] std::optional<std::reference_wrapper<uint256 const>>
|
[[nodiscard]] std::optional<std::reference_wrapper<uint256 const>>
|
||||||
getTxFeature(TxType txType) const;
|
getTxFeature(TxType txType) const;
|
||||||
|
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
isDelegable(std::uint32_t const& permissionValue, Rules const& rules) const;
|
isDelegable(std::uint32_t permissionValue, Rules const& rules) const;
|
||||||
|
|
||||||
|
[[nodiscard]] bool
|
||||||
|
hasGranularPermissions(TxType txType) const;
|
||||||
|
|
||||||
// for tx level permission, permission value is equal to tx type plus one
|
// for tx level permission, permission value is equal to tx type plus one
|
||||||
static uint32_t
|
[[nodiscard]] static uint32_t
|
||||||
txToPermissionType(TxType const& type);
|
txToPermissionType(TxType type);
|
||||||
|
|
||||||
// tx type value is permission value minus one
|
// tx type value is permission value minus one
|
||||||
static TxType
|
[[nodiscard]] static TxType
|
||||||
permissionToTxType(uint32_t const& value);
|
permissionToTxType(std::uint32_t value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Verifies a delegated transaction against its granular permission template.
|
||||||
|
*
|
||||||
|
* @note WARNING: Do not move this check before standard transaction-level
|
||||||
|
* format checks, which is in preclaim. This function assumes the transaction's
|
||||||
|
* base structural integrity (fees, sequence, signatures) has already been
|
||||||
|
* validated.
|
||||||
|
*
|
||||||
|
* @param tx The transaction to verify.
|
||||||
|
* @param heldPermissions The granular permissions that the sender hold.
|
||||||
|
* @return true if the transaction fields and flags comply with the granular template.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] bool
|
||||||
|
checkGranularSandbox(
|
||||||
|
STTx const& tx,
|
||||||
|
std::unordered_set<GranularPermissionType> const& heldPermissions) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace xrpl
|
} // namespace xrpl
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ XRPL_FEATURE(MPTokensV2, Supported::No, VoteBehavior::DefaultN
|
|||||||
XRPL_FIX (Cleanup3_1_3, Supported::Yes, VoteBehavior::DefaultYes)
|
XRPL_FIX (Cleanup3_1_3, Supported::Yes, VoteBehavior::DefaultYes)
|
||||||
XRPL_FIX (BatchInnerSigs, Supported::No, VoteBehavior::DefaultNo)
|
XRPL_FIX (BatchInnerSigs, Supported::No, VoteBehavior::DefaultNo)
|
||||||
XRPL_FEATURE(LendingProtocol, Supported::Yes, VoteBehavior::DefaultNo)
|
XRPL_FEATURE(LendingProtocol, Supported::Yes, VoteBehavior::DefaultNo)
|
||||||
XRPL_FEATURE(PermissionDelegationV1_1, Supported::No, VoteBehavior::DefaultNo)
|
XRPL_FEATURE(PermissionDelegationV1_1, Supported::Yes, VoteBehavior::DefaultNo)
|
||||||
XRPL_FIX (DirectoryLimit, Supported::Yes, VoteBehavior::DefaultNo)
|
XRPL_FIX (DirectoryLimit, Supported::Yes, VoteBehavior::DefaultNo)
|
||||||
XRPL_FIX (IncludeKeyletFields, Supported::Yes, VoteBehavior::DefaultNo)
|
XRPL_FIX (IncludeKeyletFields, Supported::Yes, VoteBehavior::DefaultNo)
|
||||||
XRPL_FEATURE(DynamicMPT, Supported::No, VoteBehavior::DefaultNo)
|
XRPL_FEATURE(DynamicMPT, Supported::No, VoteBehavior::DefaultNo)
|
||||||
|
|||||||
@@ -1,49 +1,74 @@
|
|||||||
#if !defined(PERMISSION)
|
#if !defined(GRANULAR_PERMISSION)
|
||||||
#error "undefined macro: PERMISSION"
|
#error "undefined macro: GRANULAR_PERMISSION"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PERMISSION(name, type, txType, value)
|
* GRANULAR_PERMISSION(name, txType, value, allowedFlags, allowedFields)
|
||||||
*
|
*
|
||||||
* This macro defines a permission:
|
* Defines a granular permission:
|
||||||
* name: the name of the permission.
|
* name: the granular permission name.
|
||||||
* type: the GranularPermissionType enum.
|
* txType: the corresponding TxType for this permission.
|
||||||
* txType: the corresponding TxType for this permission.
|
* value: the uint32 numeric value for the enum type.
|
||||||
* value: the uint32 numeric value for the enum type.
|
* allowedFlags: transaction flags permitted under this permission.
|
||||||
|
* allowedFields: transaction fields permitted under this permission.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** This permission grants the delegated account the ability to authorize a trustline. */
|
/** Grants the ability to authorize a trustline. */
|
||||||
PERMISSION(TrustlineAuthorize, ttTRUST_SET, 65537)
|
GRANULAR_PERMISSION(TrustlineAuthorize, ttTRUST_SET, 65537, tfUniversal | tfSetfAuth,
|
||||||
|
({{sfLimitAmount, SoeRequired}}))
|
||||||
|
|
||||||
/** This permission grants the delegated account the ability to freeze a trustline. */
|
/** Grants the ability to freeze a trustline. */
|
||||||
PERMISSION(TrustlineFreeze, ttTRUST_SET, 65538)
|
GRANULAR_PERMISSION(TrustlineFreeze, ttTRUST_SET, 65538, tfUniversal | tfSetFreeze,
|
||||||
|
({{sfLimitAmount, SoeRequired}}))
|
||||||
|
|
||||||
/** This permission grants the delegated account the ability to unfreeze a trustline. */
|
/** Grants the ability to unfreeze a trustline. */
|
||||||
PERMISSION(TrustlineUnfreeze, ttTRUST_SET, 65539)
|
GRANULAR_PERMISSION(TrustlineUnfreeze, ttTRUST_SET, 65539, tfUniversal | tfClearFreeze,
|
||||||
|
({{sfLimitAmount, SoeRequired}}))
|
||||||
|
|
||||||
/** This permission grants the delegated account the ability to set Domain. */
|
/** Grants the ability to set Domain. */
|
||||||
PERMISSION(AccountDomainSet, ttACCOUNT_SET, 65540)
|
GRANULAR_PERMISSION(AccountDomainSet, ttACCOUNT_SET, 65540, tfUniversal,
|
||||||
|
({{sfDomain, SoeOptional}}))
|
||||||
|
|
||||||
/** This permission grants the delegated account the ability to set EmailHashSet. */
|
/** Grants the ability to set EmailHash. */
|
||||||
PERMISSION(AccountEmailHashSet, ttACCOUNT_SET, 65541)
|
GRANULAR_PERMISSION(AccountEmailHashSet, ttACCOUNT_SET, 65541, tfUniversal,
|
||||||
|
({{sfEmailHash, SoeOptional}}))
|
||||||
|
|
||||||
/** This permission grants the delegated account the ability to set MessageKey. */
|
/** Grants the ability to set MessageKey. */
|
||||||
PERMISSION(AccountMessageKeySet, ttACCOUNT_SET, 65542)
|
GRANULAR_PERMISSION(AccountMessageKeySet, ttACCOUNT_SET, 65542, tfUniversal,
|
||||||
|
({{sfMessageKey, SoeOptional}}))
|
||||||
|
|
||||||
/** This permission grants the delegated account the ability to set TransferRate. */
|
/** Grants the ability to set TransferRate. */
|
||||||
PERMISSION(AccountTransferRateSet, ttACCOUNT_SET, 65543)
|
GRANULAR_PERMISSION(AccountTransferRateSet, ttACCOUNT_SET, 65543, tfUniversal,
|
||||||
|
({{sfTransferRate, SoeOptional}}))
|
||||||
|
|
||||||
/** This permission grants the delegated account the ability to set TickSize. */
|
/** Grants the ability to set TickSize. */
|
||||||
PERMISSION(AccountTickSizeSet, ttACCOUNT_SET, 65544)
|
GRANULAR_PERMISSION(AccountTickSizeSet, ttACCOUNT_SET, 65544, tfUniversal,
|
||||||
|
({{sfTickSize, SoeOptional}}))
|
||||||
|
|
||||||
/** This permission grants the delegated account the ability to mint payment, which means sending a payment for a currency where the sending account is the issuer. */
|
/** Grants the ability to mint payment (sending account is the issuer). Cross-currency payments are disallowed. */
|
||||||
PERMISSION(PaymentMint, ttPAYMENT, 65545)
|
GRANULAR_PERMISSION(PaymentMint, ttPAYMENT, 65545, tfUniversal,
|
||||||
|
({{sfDestination, SoeRequired},
|
||||||
|
{sfAmount, SoeRequired},
|
||||||
|
{sfSendMax, SoeOptional},
|
||||||
|
{sfInvoiceID, SoeOptional},
|
||||||
|
{sfDestinationTag, SoeOptional},
|
||||||
|
{sfCredentialIDs, SoeOptional}}))
|
||||||
|
|
||||||
/** This permission grants the delegated account the ability to burn payment, which means sending a payment for a currency where the destination account is the issuer */
|
/** Grants the ability to burn payment (destination account is the issuer). Cross-currency payments are disallowed. */
|
||||||
PERMISSION(PaymentBurn, ttPAYMENT, 65546)
|
GRANULAR_PERMISSION(PaymentBurn, ttPAYMENT, 65546, tfUniversal,
|
||||||
|
({{sfDestination, SoeRequired},
|
||||||
|
{sfAmount, SoeRequired},
|
||||||
|
{sfSendMax, SoeOptional},
|
||||||
|
{sfInvoiceID, SoeOptional},
|
||||||
|
{sfDestinationTag, SoeOptional},
|
||||||
|
{sfCredentialIDs, SoeOptional}}))
|
||||||
|
|
||||||
/** This permission grants the delegated account the ability to lock MPToken. */
|
/** Grants the ability to lock an MPToken. */
|
||||||
PERMISSION(MPTokenIssuanceLock, ttMPTOKEN_ISSUANCE_SET, 65547)
|
GRANULAR_PERMISSION(MPTokenIssuanceLock, ttMPTOKEN_ISSUANCE_SET, 65547, tfUniversal | tfMPTLock,
|
||||||
|
({{sfMPTokenIssuanceID, SoeRequired},
|
||||||
|
{sfHolder, SoeOptional}}))
|
||||||
|
|
||||||
/** This permission grants the delegated account the ability to unlock MPToken. */
|
/** Grants the ability to unlock an MPToken. */
|
||||||
PERMISSION(MPTokenIssuanceUnlock, ttMPTOKEN_ISSUANCE_SET, 65548)
|
GRANULAR_PERMISSION(MPTokenIssuanceUnlock, ttMPTOKEN_ISSUANCE_SET, 65548, tfUniversal | tfMPTUnlock,
|
||||||
|
({{sfMPTokenIssuanceID, SoeRequired},
|
||||||
|
{sfHolder, SoeOptional}}))
|
||||||
|
|||||||
@@ -222,8 +222,63 @@ public:
|
|||||||
return tesSUCCESS;
|
return tesSUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function can be overridden to introduce additional semantic constraints beyond the
|
||||||
|
* granular template validation for granular permissions. It is called by the base
|
||||||
|
* invokeCheckPermission method only after the transaction has successfully passed
|
||||||
|
* checkGranularSandbox.
|
||||||
|
*/
|
||||||
static NotTEC
|
static NotTEC
|
||||||
checkPermission(ReadView const& view, STTx const& tx);
|
checkGranularSemantics(
|
||||||
|
ReadView const& view,
|
||||||
|
STTx const& tx,
|
||||||
|
std::unordered_set<GranularPermissionType> const& heldGranularPermissions)
|
||||||
|
{
|
||||||
|
return tesSUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the transaction is authorized to be executed by the delegated account.
|
||||||
|
* This function enforces the strict permission check hierarchy. It is explicitly
|
||||||
|
* designed NOT to be overridden. Derived transactors must instead implement
|
||||||
|
* checkGranularSemantics to add custom validation logic for granular permissions.
|
||||||
|
*
|
||||||
|
* The evaluation proceeds as follows:
|
||||||
|
* - If transaction-level permission is granted, the function immediately returns tesSUCCESS.
|
||||||
|
* - If transaction-level permission is not granted, the function checks whether the transaction
|
||||||
|
* matches the granular permission template defined in permissions.macro. If it does, it then
|
||||||
|
* calls checkGranularSemantics to perform any additional, fine-grained validation.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
template <class T>
|
||||||
|
static NotTEC
|
||||||
|
invokeCheckPermission(ReadView const& view, STTx const& tx)
|
||||||
|
{
|
||||||
|
// heldGranularPermissions is passed by reference into checkPermission.
|
||||||
|
// It is populated with the sender’s granular permissions only when the sender
|
||||||
|
// lacks tx-level permission but has granular permissions that satisfy the
|
||||||
|
// granular permission template.
|
||||||
|
//
|
||||||
|
// - result is terNO_DELEGATE_PERMISSION: return immediately.
|
||||||
|
// - result is tesSUCCESS and heldGranularPermissions is empty: tx-level permission was
|
||||||
|
// granted, so we returned success before populating it.
|
||||||
|
// - result is tesSUCCESS and heldGranularPermissions is not empty: tx-level permission was
|
||||||
|
// not granted, but the held granular permissions passed checkGranularSandbox, so we proceed
|
||||||
|
// to checkGranularSemantics.
|
||||||
|
//
|
||||||
|
// WARNING: Do not simplify checkPermission to return only
|
||||||
|
// heldGranularPermissions or the ter code. Both the result and the
|
||||||
|
// populated set are required to enforce the strict permission hierarchy
|
||||||
|
// described above.
|
||||||
|
std::unordered_set<GranularPermissionType> heldGranularPermissions;
|
||||||
|
if (NotTEC const result = checkPermission(view, tx, heldGranularPermissions);
|
||||||
|
!isTesSuccess(result) || heldGranularPermissions.empty())
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return T::checkGranularSemantics(view, tx, heldGranularPermissions);
|
||||||
|
}
|
||||||
/////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Interface used by AccountDelete
|
// Interface used by AccountDelete
|
||||||
@@ -353,6 +408,12 @@ protected:
|
|||||||
unit::ValueUnit<Unit, T> min = unit::ValueUnit<Unit, T>{});
|
unit::ValueUnit<Unit, T> min = unit::ValueUnit<Unit, T>{});
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static NotTEC
|
||||||
|
checkPermission(
|
||||||
|
ReadView const& view,
|
||||||
|
STTx const& tx,
|
||||||
|
std::unordered_set<GranularPermissionType>& heldGranularPermissions);
|
||||||
|
|
||||||
std::pair<TER, XRPAmount>
|
std::pair<TER, XRPAmount>
|
||||||
reset(XRPAmount fee);
|
reset(XRPAmount fee);
|
||||||
|
|
||||||
|
|||||||
@@ -23,9 +23,6 @@ public:
|
|||||||
static NotTEC
|
static NotTEC
|
||||||
preflight(PreflightContext const& ctx);
|
preflight(PreflightContext const& ctx);
|
||||||
|
|
||||||
static NotTEC
|
|
||||||
checkPermission(ReadView const& view, STTx const& tx);
|
|
||||||
|
|
||||||
static TER
|
static TER
|
||||||
preclaim(PreclaimContext const& ctx);
|
preclaim(PreclaimContext const& ctx);
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,10 @@ public:
|
|||||||
preflight(PreflightContext const& ctx);
|
preflight(PreflightContext const& ctx);
|
||||||
|
|
||||||
static NotTEC
|
static NotTEC
|
||||||
checkPermission(ReadView const& view, STTx const& tx);
|
checkGranularSemantics(
|
||||||
|
ReadView const& view,
|
||||||
|
STTx const& tx,
|
||||||
|
std::unordered_set<GranularPermissionType> const& heldGranularPermissions);
|
||||||
|
|
||||||
static TER
|
static TER
|
||||||
preclaim(PreclaimContext const& ctx);
|
preclaim(PreclaimContext const& ctx);
|
||||||
|
|||||||
@@ -22,9 +22,6 @@ public:
|
|||||||
static NotTEC
|
static NotTEC
|
||||||
preflight(PreflightContext const& ctx);
|
preflight(PreflightContext const& ctx);
|
||||||
|
|
||||||
static NotTEC
|
|
||||||
checkPermission(ReadView const& view, STTx const& tx);
|
|
||||||
|
|
||||||
static TER
|
static TER
|
||||||
preclaim(PreclaimContext const& ctx);
|
preclaim(PreclaimContext const& ctx);
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,10 @@ public:
|
|||||||
preflight(PreflightContext const& ctx);
|
preflight(PreflightContext const& ctx);
|
||||||
|
|
||||||
static NotTEC
|
static NotTEC
|
||||||
checkPermission(ReadView const& view, STTx const& tx);
|
checkGranularSemantics(
|
||||||
|
ReadView const& view,
|
||||||
|
STTx const& tx,
|
||||||
|
std::unordered_set<GranularPermissionType> const& heldGranularPermissions);
|
||||||
|
|
||||||
static TER
|
static TER
|
||||||
preclaim(PreclaimContext const& ctx);
|
preclaim(PreclaimContext const& ctx);
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ ProtectHome=true
|
|||||||
PrivateTmp=true
|
PrivateTmp=true
|
||||||
User=xrpld
|
User=xrpld
|
||||||
Group=xrpld
|
Group=xrpld
|
||||||
|
StateDirectory=xrpld
|
||||||
|
StateDirectoryMode=0750
|
||||||
|
LogsDirectory=xrpld
|
||||||
|
LogsDirectoryMode=0750
|
||||||
LimitNOFILE=65536
|
LimitNOFILE=65536
|
||||||
SystemCallArchitectures=native
|
SystemCallArchitectures=native
|
||||||
|
|
||||||
|
|||||||
@@ -1,91 +1,136 @@
|
|||||||
#include <xrpl/protocol/Permissions.h>
|
#include <xrpl/protocol/Permissions.h>
|
||||||
|
|
||||||
#include <xrpl/basics/base_uint.h>
|
#include <xrpl/basics/base_uint.h>
|
||||||
|
#include <xrpl/basics/contract.h>
|
||||||
#include <xrpl/beast/utility/instrumentation.h>
|
#include <xrpl/beast/utility/instrumentation.h>
|
||||||
#include <xrpl/protocol/Feature.h> // IWYU pragma: keep
|
|
||||||
#include <xrpl/protocol/Rules.h>
|
#include <xrpl/protocol/Rules.h>
|
||||||
|
#include <xrpl/protocol/SField.h>
|
||||||
|
#include <xrpl/protocol/SOTemplate.h>
|
||||||
|
#include <xrpl/protocol/STTx.h>
|
||||||
|
#include <xrpl/protocol/TxFlags.h> // IWYU pragma: keep
|
||||||
#include <xrpl/protocol/TxFormats.h>
|
#include <xrpl/protocol/TxFormats.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <tuple>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
|
Permission::GranularPermissionEntry::GranularPermissionEntry(
|
||||||
|
std::string name,
|
||||||
|
TxType txType,
|
||||||
|
std::uint32_t permittedFlags,
|
||||||
|
std::vector<SOElement> permittedFields)
|
||||||
|
: name(std::move(name))
|
||||||
|
, txType(txType)
|
||||||
|
, permittedFlags(permittedFlags)
|
||||||
|
, permittedFields(std::move(permittedFields), TxFormats::getCommonFields())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
Permission::Permission()
|
Permission::Permission()
|
||||||
{
|
{
|
||||||
txFeatureMap_ = {
|
|
||||||
#pragma push_macro("TRANSACTION")
|
|
||||||
#undef TRANSACTION
|
|
||||||
|
|
||||||
#define TRANSACTION(tag, value, name, delegable, amendment, ...) {value, amendment},
|
|
||||||
|
|
||||||
#include <xrpl/protocol/detail/transactions.macro>
|
|
||||||
|
|
||||||
#undef TRANSACTION
|
|
||||||
#pragma pop_macro("TRANSACTION")
|
|
||||||
};
|
|
||||||
|
|
||||||
delegableTx_ = {
|
|
||||||
#pragma push_macro("TRANSACTION")
|
|
||||||
#undef TRANSACTION
|
|
||||||
|
|
||||||
#define TRANSACTION(tag, value, name, delegable, ...) {value, delegable},
|
|
||||||
|
|
||||||
#include <xrpl/protocol/detail/transactions.macro>
|
|
||||||
|
|
||||||
#undef TRANSACTION
|
|
||||||
#pragma pop_macro("TRANSACTION")
|
|
||||||
};
|
|
||||||
|
|
||||||
granularPermissionMap_ = {
|
|
||||||
#pragma push_macro("PERMISSION")
|
|
||||||
#undef PERMISSION
|
|
||||||
|
|
||||||
#define PERMISSION(type, txType, value) {#type, type},
|
|
||||||
|
|
||||||
#include <xrpl/protocol/detail/permissions.macro>
|
|
||||||
|
|
||||||
#undef PERMISSION
|
|
||||||
#pragma pop_macro("PERMISSION")
|
|
||||||
};
|
|
||||||
|
|
||||||
granularNameMap_ = {
|
|
||||||
#pragma push_macro("PERMISSION")
|
|
||||||
#undef PERMISSION
|
|
||||||
|
|
||||||
#define PERMISSION(type, txType, value) {type, #type},
|
|
||||||
|
|
||||||
#include <xrpl/protocol/detail/permissions.macro>
|
|
||||||
|
|
||||||
#undef PERMISSION
|
|
||||||
#pragma pop_macro("PERMISSION")
|
|
||||||
};
|
|
||||||
|
|
||||||
granularTxTypeMap_ = {
|
|
||||||
#pragma push_macro("PERMISSION")
|
|
||||||
#undef PERMISSION
|
|
||||||
|
|
||||||
#define PERMISSION(type, txType, value) {type, txType},
|
|
||||||
|
|
||||||
#include <xrpl/protocol/detail/permissions.macro>
|
|
||||||
|
|
||||||
#undef PERMISSION
|
|
||||||
#pragma pop_macro("PERMISSION")
|
|
||||||
};
|
|
||||||
|
|
||||||
XRPL_ASSERT(
|
|
||||||
txFeatureMap_.size() == delegableTx_.size(),
|
|
||||||
"xrpl::Permission : txFeatureMap_ and delegableTx_ must have same "
|
|
||||||
"size");
|
|
||||||
|
|
||||||
for ([[maybe_unused]] auto const& permission : granularPermissionMap_)
|
|
||||||
{
|
{
|
||||||
XRPL_ASSERT(
|
#pragma push_macro("TRANSACTION")
|
||||||
permission.second > UINT16_MAX,
|
#undef TRANSACTION
|
||||||
"xrpl::Permission::granularPermissionMap_ : granular permission "
|
|
||||||
"value must not exceed the maximum uint16_t value.");
|
#define TRANSACTION(tag, value, name, delegable, amendment, ...) \
|
||||||
|
txDelegationMap_[static_cast<TxType>(value)] = {amendment, delegable};
|
||||||
|
|
||||||
|
#include <xrpl/protocol/detail/transactions.macro>
|
||||||
|
|
||||||
|
#undef TRANSACTION
|
||||||
|
#pragma pop_macro("TRANSACTION")
|
||||||
|
}
|
||||||
|
|
||||||
|
granularPermissionsByName_ = {
|
||||||
|
#pragma push_macro("GRANULAR_PERMISSION")
|
||||||
|
#undef GRANULAR_PERMISSION
|
||||||
|
|
||||||
|
#define GRANULAR_PERMISSION(type, ...) {#type, type},
|
||||||
|
|
||||||
|
#include <xrpl/protocol/detail/permissions.macro>
|
||||||
|
|
||||||
|
#undef GRANULAR_PERMISSION
|
||||||
|
#pragma pop_macro("GRANULAR_PERMISSION")
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
#pragma push_macro("GRANULAR_PERMISSION")
|
||||||
|
#undef GRANULAR_PERMISSION
|
||||||
|
|
||||||
|
// NOLINTBEGIN(bugprone-macro-parentheses)
|
||||||
|
#define GRANULAR_PERMISSION(type, txType, value, flags, fields) \
|
||||||
|
granularPermissions_.emplace( \
|
||||||
|
std::piecewise_construct, \
|
||||||
|
std::forward_as_tuple(GranularPermissionType::type), \
|
||||||
|
std::forward_as_tuple( \
|
||||||
|
#type, txType, static_cast<std::uint32_t>(flags), std::vector<SOElement> fields));
|
||||||
|
// NOLINTEND(bugprone-macro-parentheses)
|
||||||
|
|
||||||
|
#include <xrpl/protocol/detail/permissions.macro>
|
||||||
|
|
||||||
|
#undef GRANULAR_PERMISSION
|
||||||
|
#pragma pop_macro("GRANULAR_PERMISSION")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (granularPermissionsByName_.size() != granularPermissions_.size())
|
||||||
|
{
|
||||||
|
// LCOV_EXCL_START
|
||||||
|
Throw<std::logic_error>(
|
||||||
|
"granularPermissionsByName_ and granularPermissions_ must have same size");
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto const& [name, type] : granularPermissionsByName_)
|
||||||
|
{
|
||||||
|
if (type <= UINT16_MAX)
|
||||||
|
{
|
||||||
|
// LCOV_EXCL_START
|
||||||
|
Throw<std::logic_error>(
|
||||||
|
"Granular permission value must exceed the maximum uint16_t value: " + name);
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto const& [type, entry] : granularPermissions_)
|
||||||
|
granularTxTypes_.insert(entry.txType);
|
||||||
|
|
||||||
|
// Validate that all fields listed in permissions.macro exist in the
|
||||||
|
// corresponding transaction type's format, catching typos at startup.
|
||||||
|
for (auto const& [type, entry] : granularPermissions_)
|
||||||
|
{
|
||||||
|
if (!txDelegationMap_.contains(entry.txType))
|
||||||
|
{
|
||||||
|
// LCOV_EXCL_START
|
||||||
|
Throw<std::logic_error>("Invalid granular permission txType in txDelegationMap_");
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const* fmt = TxFormats::getInstance().findByType(entry.txType);
|
||||||
|
if (fmt == nullptr)
|
||||||
|
{
|
||||||
|
// LCOV_EXCL_START
|
||||||
|
Throw<std::logic_error>("Invalid granular permission txType");
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto const& field : entry.permittedFields)
|
||||||
|
{
|
||||||
|
if (fmt->getSOTemplate().getIndex(field.sField()) == -1)
|
||||||
|
{
|
||||||
|
// LCOV_EXCL_START
|
||||||
|
Throw<std::logic_error>("Invalid granular permission field");
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,8 +142,11 @@ Permission::getInstance()
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::string>
|
std::optional<std::string>
|
||||||
Permission::getPermissionName(std::uint32_t const value) const
|
Permission::getPermissionName(std::uint32_t value) const
|
||||||
{
|
{
|
||||||
|
if (value == 0)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
auto const permissionValue = static_cast<GranularPermissionType>(value);
|
auto const permissionValue = static_cast<GranularPermissionType>(value);
|
||||||
if (auto const granular = getGranularName(permissionValue))
|
if (auto const granular = getGranularName(permissionValue))
|
||||||
return granular;
|
return granular;
|
||||||
@@ -114,90 +162,131 @@ Permission::getPermissionName(std::uint32_t const value) const
|
|||||||
std::optional<std::uint32_t>
|
std::optional<std::uint32_t>
|
||||||
Permission::getGranularValue(std::string const& name) const
|
Permission::getGranularValue(std::string const& name) const
|
||||||
{
|
{
|
||||||
auto const it = granularPermissionMap_.find(name);
|
auto const it = granularPermissionsByName_.find(name);
|
||||||
if (it != granularPermissionMap_.end())
|
if (it != granularPermissionsByName_.end())
|
||||||
return static_cast<uint32_t>(it->second);
|
return static_cast<uint32_t>(it->second);
|
||||||
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::string>
|
std::optional<std::string>
|
||||||
Permission::getGranularName(GranularPermissionType const& value) const
|
Permission::getGranularName(GranularPermissionType value) const
|
||||||
{
|
{
|
||||||
auto const it = granularNameMap_.find(value);
|
auto const it = granularPermissions_.find(value);
|
||||||
if (it != granularNameMap_.end())
|
if (it != granularPermissions_.end())
|
||||||
return it->second;
|
return it->second.name;
|
||||||
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<TxType>
|
std::optional<TxType>
|
||||||
Permission::getGranularTxType(GranularPermissionType const& gpType) const
|
Permission::getGranularTxType(GranularPermissionType gpType) const
|
||||||
{
|
{
|
||||||
auto const it = granularTxTypeMap_.find(gpType);
|
auto const it = granularPermissions_.find(gpType);
|
||||||
if (it != granularTxTypeMap_.end())
|
if (it != granularPermissions_.end())
|
||||||
return it->second;
|
return it->second.txType;
|
||||||
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Permission::hasGranularPermissions(TxType txType) const
|
||||||
|
{
|
||||||
|
return granularTxTypes_.contains(txType);
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<std::reference_wrapper<uint256 const>>
|
std::optional<std::reference_wrapper<uint256 const>>
|
||||||
Permission::getTxFeature(TxType txType) const
|
Permission::getTxFeature(TxType txType) const
|
||||||
{
|
{
|
||||||
auto const txFeaturesIt = txFeatureMap_.find(txType);
|
auto const it = txDelegationMap_.find(txType);
|
||||||
XRPL_ASSERT(
|
XRPL_ASSERT(
|
||||||
txFeaturesIt != txFeatureMap_.end(),
|
it != txDelegationMap_.end(),
|
||||||
"xrpl::Permissions::getTxFeature : tx exists in txFeatureMap_");
|
"xrpl::Permission::getTxFeature : tx exists in txDelegationMap_");
|
||||||
|
|
||||||
if (txFeaturesIt->second == uint256{})
|
if (it->second.amendment == uint256{})
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
return txFeaturesIt->second;
|
|
||||||
|
return std::optional{std::cref(it->second.amendment)};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Permission::isDelegable(std::uint32_t const& permissionValue, Rules const& rules) const
|
Permission::isDelegable(std::uint32_t permissionValue, Rules const& rules) const
|
||||||
{
|
{
|
||||||
auto const granularPermission =
|
if (permissionValue == 0)
|
||||||
getGranularName(static_cast<GranularPermissionType>(permissionValue));
|
return false; // LCOV_EXCL_LINE
|
||||||
if (granularPermission)
|
|
||||||
|
auto const amendmentEnabled = [&rules](TxDelegationEntry const& entry) {
|
||||||
|
return entry.amendment == uint256{} || rules.enabled(entry.amendment);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Granular permissions may authorize a limited subset of a tx type even
|
||||||
|
// when the full tx type is not delegable. They still require the
|
||||||
|
// underlying transaction amendment to be enabled.
|
||||||
|
if (auto const granularIt =
|
||||||
|
granularPermissions_.find(static_cast<GranularPermissionType>(permissionValue));
|
||||||
|
granularIt != granularPermissions_.end())
|
||||||
{
|
{
|
||||||
// granular permissions are always allowed to be delegated
|
auto const txIt = txDelegationMap_.find(granularIt->second.txType);
|
||||||
return true;
|
return txIt != txDelegationMap_.end() && amendmentEnabled(txIt->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const txType = permissionToTxType(permissionValue);
|
auto const txType = permissionToTxType(permissionValue);
|
||||||
auto const it = delegableTx_.find(txType);
|
auto const txIt = txDelegationMap_.find(txType);
|
||||||
|
|
||||||
if (it == delegableTx_.end())
|
// Tx-level permissions require the transaction type itself to be delegable, and
|
||||||
return false;
|
// the corresponding amendment enabled.
|
||||||
|
return txIt != txDelegationMap_.end() && txIt->second.delegable != NotDelegable &&
|
||||||
auto const txFeaturesIt = txFeatureMap_.find(txType);
|
amendmentEnabled(txIt->second);
|
||||||
XRPL_ASSERT(
|
|
||||||
txFeaturesIt != txFeatureMap_.end(),
|
|
||||||
"xrpl::Permissions::isDelegable : tx exists in txFeatureMap_");
|
|
||||||
|
|
||||||
// Delegation is only allowed if the required amendment for the transaction
|
|
||||||
// is enabled. For transactions that do not require an amendment, delegation
|
|
||||||
// is always allowed.
|
|
||||||
if (txFeaturesIt->second != uint256{} && !rules.enabled(txFeaturesIt->second))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (it->second == Delegation::NotDelegable)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t
|
uint32_t
|
||||||
Permission::txToPermissionType(TxType const& type)
|
Permission::txToPermissionType(TxType const type)
|
||||||
{
|
{
|
||||||
return static_cast<uint32_t>(type) + 1;
|
return static_cast<uint32_t>(type) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
TxType
|
TxType
|
||||||
Permission::permissionToTxType(uint32_t const& value)
|
Permission::permissionToTxType(uint32_t value)
|
||||||
{
|
{
|
||||||
|
XRPL_ASSERT(value > 0, "xrpl::Permission::permissionToTxType : value is greater than 0");
|
||||||
return static_cast<TxType>(value - 1);
|
return static_cast<TxType>(value - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Permission::checkGranularSandbox(
|
||||||
|
STTx const& tx,
|
||||||
|
std::unordered_set<GranularPermissionType> const& heldPermissions) const
|
||||||
|
{
|
||||||
|
// Build union of flags upfront to enable an early exit. Fields are not stored and
|
||||||
|
// grouped in advance to avoid heap allocation.
|
||||||
|
std::uint32_t unionFlags = 0;
|
||||||
|
for (auto const& gp : heldPermissions)
|
||||||
|
{
|
||||||
|
auto const it = granularPermissions_.find(gp);
|
||||||
|
if (it != granularPermissions_.end())
|
||||||
|
unionFlags |= it->second.permittedFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if flags are permitted
|
||||||
|
if ((tx.getFlags() & ~unionFlags) != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check if fields are permitted. Every present field must appear in at least one held
|
||||||
|
// permission's template. The common fields are included in the constructor.
|
||||||
|
for (auto const& field : tx)
|
||||||
|
{
|
||||||
|
if (field.getSType() == STI_NOTPRESENT)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!std::ranges::any_of(heldPermissions, [&](auto const& gp) {
|
||||||
|
auto const it = granularPermissions_.find(gp);
|
||||||
|
return it != granularPermissions_.end() &&
|
||||||
|
it->second.permittedFields.getIndex(field.getFName()) != -1;
|
||||||
|
}))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace xrpl
|
} // namespace xrpl
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ STTx::getFeePayer() const
|
|||||||
{
|
{
|
||||||
// If sfDelegate is present, the delegate account is the payer
|
// If sfDelegate is present, the delegate account is the payer
|
||||||
// note: if a delegate is specified, its authorization to act on behalf of the account is
|
// note: if a delegate is specified, its authorization to act on behalf of the account is
|
||||||
// enforced in `Transactor::checkPermission`
|
// enforced in `Transactor::invokeCheckPermission`
|
||||||
// cryptographic signature validity is checked separately (e.g., in `Transactor::checkSign`)
|
// cryptographic signature validity is checked separately (e.g., in `Transactor::checkSign`)
|
||||||
if (isFieldPresent(sfDelegate))
|
if (isFieldPresent(sfDelegate))
|
||||||
return getAccountID(sfDelegate);
|
return getAccountID(sfDelegate);
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#include <xrpl/protocol/Feature.h>
|
#include <xrpl/protocol/Feature.h>
|
||||||
#include <xrpl/protocol/Indexes.h>
|
#include <xrpl/protocol/Indexes.h>
|
||||||
#include <xrpl/protocol/LedgerFormats.h>
|
#include <xrpl/protocol/LedgerFormats.h>
|
||||||
|
#include <xrpl/protocol/Permissions.h>
|
||||||
#include <xrpl/protocol/Protocol.h>
|
#include <xrpl/protocol/Protocol.h>
|
||||||
#include <xrpl/protocol/PublicKey.h>
|
#include <xrpl/protocol/PublicKey.h>
|
||||||
#include <xrpl/protocol/Rules.h>
|
#include <xrpl/protocol/Rules.h>
|
||||||
@@ -46,6 +47,7 @@
|
|||||||
#include <exception>
|
#include <exception>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <unordered_set>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -175,6 +177,16 @@ Transactor::preflight1(PreflightContext const& ctx, std::uint32_t flagMask)
|
|||||||
|
|
||||||
if (ctx.tx[sfDelegate] == ctx.tx[sfAccount])
|
if (ctx.tx[sfDelegate] == ctx.tx[sfAccount])
|
||||||
return temBAD_SIGNER;
|
return temBAD_SIGNER;
|
||||||
|
|
||||||
|
auto const& perm = Permission::getInstance();
|
||||||
|
auto const txType = ctx.tx.getTxnType();
|
||||||
|
|
||||||
|
// If the transaction is not delegable and does not have granular permissions, fail earlier
|
||||||
|
// with temINVALID. This is to prevent transactions that are not delegable at all from
|
||||||
|
// being processed further in the invokeCheckPermission function.
|
||||||
|
if (!perm.isDelegable(Permission::txToPermissionType(txType), ctx.rules) &&
|
||||||
|
!perm.hasGranularPermissions(txType))
|
||||||
|
return temINVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto const ret = preflight0(ctx, flagMask))
|
if (auto const ret = preflight0(ctx, flagMask))
|
||||||
@@ -295,19 +307,33 @@ Transactor::preflightSigValidated(PreflightContext const& ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
NotTEC
|
NotTEC
|
||||||
Transactor::checkPermission(ReadView const& view, STTx const& tx)
|
Transactor::checkPermission(
|
||||||
|
ReadView const& view,
|
||||||
|
STTx const& tx,
|
||||||
|
std::unordered_set<GranularPermissionType>& heldGranularPermissions)
|
||||||
{
|
{
|
||||||
auto const delegate = tx[~sfDelegate];
|
auto const delegate = tx[~sfDelegate];
|
||||||
if (!delegate)
|
if (!delegate)
|
||||||
return tesSUCCESS;
|
return tesSUCCESS;
|
||||||
|
|
||||||
auto const delegateKey = keylet::delegate(tx[sfAccount], *delegate);
|
auto const sle = view.read(keylet::delegate(tx[sfAccount], *delegate));
|
||||||
auto const sle = view.read(delegateKey);
|
|
||||||
|
|
||||||
if (!sle)
|
if (!sle)
|
||||||
return terNO_DELEGATE_PERMISSION;
|
return terNO_DELEGATE_PERMISSION;
|
||||||
|
|
||||||
return checkTxPermission(sle, tx);
|
if (isTesSuccess(checkTxPermission(sle, tx)))
|
||||||
|
return tesSUCCESS;
|
||||||
|
|
||||||
|
if (!Permission::getInstance().hasGranularPermissions(tx.getTxnType()))
|
||||||
|
return terNO_DELEGATE_PERMISSION;
|
||||||
|
|
||||||
|
heldGranularPermissions = getGranularPermission(sle, tx.getTxnType());
|
||||||
|
if (heldGranularPermissions.empty())
|
||||||
|
return terNO_DELEGATE_PERMISSION;
|
||||||
|
|
||||||
|
if (!Permission::getInstance().checkGranularSandbox(tx, heldGranularPermissions))
|
||||||
|
return terNO_DELEGATE_PERMISSION;
|
||||||
|
|
||||||
|
return tesSUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
XRPAmount
|
XRPAmount
|
||||||
|
|||||||
@@ -181,7 +181,8 @@ invokePreclaim(PreclaimContext const& ctx)
|
|||||||
if (NotTEC const result = T::checkPriorTxAndLastLedger(ctx))
|
if (NotTEC const result = T::checkPriorTxAndLastLedger(ctx))
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
if (NotTEC const result = T::checkPermission(ctx.view, ctx.tx))
|
if (NotTEC const result =
|
||||||
|
Transactor::invokeCheckPermission<T>(ctx.view, ctx.tx))
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
if (NotTEC const result = T::checkSign(ctx))
|
if (NotTEC const result = T::checkSign(ctx))
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
#include <xrpl/basics/base_uint.h>
|
#include <xrpl/basics/base_uint.h>
|
||||||
#include <xrpl/ledger/ApplyView.h>
|
#include <xrpl/ledger/ApplyView.h>
|
||||||
#include <xrpl/ledger/ReadView.h>
|
#include <xrpl/ledger/ReadView.h>
|
||||||
#include <xrpl/ledger/helpers/DelegateHelpers.h>
|
|
||||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||||
#include <xrpl/protocol/Feature.h>
|
#include <xrpl/protocol/Feature.h>
|
||||||
#include <xrpl/protocol/Indexes.h>
|
#include <xrpl/protocol/Indexes.h>
|
||||||
@@ -20,13 +19,11 @@
|
|||||||
#include <xrpl/protocol/STTx.h>
|
#include <xrpl/protocol/STTx.h>
|
||||||
#include <xrpl/protocol/TER.h>
|
#include <xrpl/protocol/TER.h>
|
||||||
#include <xrpl/protocol/TxFlags.h>
|
#include <xrpl/protocol/TxFlags.h>
|
||||||
#include <xrpl/protocol/TxFormats.h>
|
|
||||||
#include <xrpl/protocol/XRPAmount.h>
|
#include <xrpl/protocol/XRPAmount.h>
|
||||||
#include <xrpl/tx/Transactor.h>
|
#include <xrpl/tx/Transactor.h>
|
||||||
#include <xrpl/tx/applySteps.h>
|
#include <xrpl/tx/applySteps.h>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <unordered_set>
|
|
||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
@@ -168,54 +165,6 @@ AccountSet::preflight(PreflightContext const& ctx)
|
|||||||
return tesSUCCESS;
|
return tesSUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
NotTEC
|
|
||||||
AccountSet::checkPermission(ReadView const& view, STTx const& tx)
|
|
||||||
{
|
|
||||||
// AccountSet is prohibited to be granted on a transaction level,
|
|
||||||
// but some granular permissions are allowed.
|
|
||||||
auto const delegate = tx[~sfDelegate];
|
|
||||||
if (!delegate)
|
|
||||||
return tesSUCCESS;
|
|
||||||
|
|
||||||
auto const delegateKey = keylet::delegate(tx[sfAccount], *delegate);
|
|
||||||
auto const sle = view.read(delegateKey);
|
|
||||||
|
|
||||||
if (!sle)
|
|
||||||
return terNO_DELEGATE_PERMISSION;
|
|
||||||
|
|
||||||
std::unordered_set<GranularPermissionType> granularPermissions;
|
|
||||||
loadGranularPermission(sle, ttACCOUNT_SET, granularPermissions);
|
|
||||||
|
|
||||||
auto const uSetFlag = tx.getFieldU32(sfSetFlag);
|
|
||||||
auto const uClearFlag = tx.getFieldU32(sfClearFlag);
|
|
||||||
// We don't support any flag based granular permission under
|
|
||||||
// AccountSet transaction. If any delegated account is trying to
|
|
||||||
// update the flag on behalf of another account, it is not
|
|
||||||
// authorized.
|
|
||||||
if (uSetFlag != 0 || uClearFlag != 0 || ((tx.getFlags() & tfUniversalMask) != 0u))
|
|
||||||
return terNO_DELEGATE_PERMISSION;
|
|
||||||
|
|
||||||
if (tx.isFieldPresent(sfEmailHash) && !granularPermissions.contains(AccountEmailHashSet))
|
|
||||||
return terNO_DELEGATE_PERMISSION;
|
|
||||||
|
|
||||||
if (tx.isFieldPresent(sfWalletLocator) || tx.isFieldPresent(sfNFTokenMinter))
|
|
||||||
return terNO_DELEGATE_PERMISSION;
|
|
||||||
|
|
||||||
if (tx.isFieldPresent(sfMessageKey) && !granularPermissions.contains(AccountMessageKeySet))
|
|
||||||
return terNO_DELEGATE_PERMISSION;
|
|
||||||
|
|
||||||
if (tx.isFieldPresent(sfDomain) && !granularPermissions.contains(AccountDomainSet))
|
|
||||||
return terNO_DELEGATE_PERMISSION;
|
|
||||||
|
|
||||||
if (tx.isFieldPresent(sfTransferRate) && !granularPermissions.contains(AccountTransferRateSet))
|
|
||||||
return terNO_DELEGATE_PERMISSION;
|
|
||||||
|
|
||||||
if (tx.isFieldPresent(sfTickSize) && !granularPermissions.contains(AccountTickSizeSet))
|
|
||||||
return terNO_DELEGATE_PERMISSION;
|
|
||||||
|
|
||||||
return tesSUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
TER
|
TER
|
||||||
AccountSet::preclaim(PreclaimContext const& ctx)
|
AccountSet::preclaim(PreclaimContext const& ctx)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -29,14 +29,12 @@ checkTxPermission(SLE::const_ref delegate, STTx const& tx)
|
|||||||
return terNO_DELEGATE_PERMISSION;
|
return terNO_DELEGATE_PERMISSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
std::unordered_set<GranularPermissionType>
|
||||||
loadGranularPermission(
|
getGranularPermission(SLE::const_ref delegate, TxType const& txType)
|
||||||
SLE::const_ref delegate,
|
|
||||||
TxType const& txType,
|
|
||||||
std::unordered_set<GranularPermissionType>& granularPermissions)
|
|
||||||
{
|
{
|
||||||
|
std::unordered_set<GranularPermissionType> granularPermissions;
|
||||||
if (!delegate)
|
if (!delegate)
|
||||||
return;
|
return granularPermissions;
|
||||||
|
|
||||||
auto const permissionArray = delegate->getFieldArray(sfPermissions);
|
auto const permissionArray = delegate->getFieldArray(sfPermissions);
|
||||||
for (auto const& permission : permissionArray)
|
for (auto const& permission : permissionArray)
|
||||||
@@ -47,6 +45,8 @@ loadGranularPermission(
|
|||||||
if (type && *type == txType)
|
if (type && *type == txType)
|
||||||
granularPermissions.insert(granularValue);
|
granularPermissions.insert(granularValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return granularPermissions;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace xrpl
|
} // namespace xrpl
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
#include <xrpl/ledger/ReadView.h>
|
#include <xrpl/ledger/ReadView.h>
|
||||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||||
#include <xrpl/ledger/helpers/DelegateHelpers.h>
|
|
||||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||||
#include <xrpl/ledger/helpers/PermissionedDEXHelpers.h>
|
#include <xrpl/ledger/helpers/PermissionedDEXHelpers.h>
|
||||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||||
@@ -29,7 +28,6 @@
|
|||||||
#include <xrpl/protocol/STTx.h>
|
#include <xrpl/protocol/STTx.h>
|
||||||
#include <xrpl/protocol/TER.h>
|
#include <xrpl/protocol/TER.h>
|
||||||
#include <xrpl/protocol/TxFlags.h>
|
#include <xrpl/protocol/TxFlags.h>
|
||||||
#include <xrpl/protocol/TxFormats.h>
|
|
||||||
#include <xrpl/protocol/UintTypes.h>
|
#include <xrpl/protocol/UintTypes.h>
|
||||||
#include <xrpl/protocol/XRPAmount.h>
|
#include <xrpl/protocol/XRPAmount.h>
|
||||||
#include <xrpl/protocol/jss.h>
|
#include <xrpl/protocol/jss.h>
|
||||||
@@ -273,38 +271,24 @@ Payment::preflight(PreflightContext const& ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
NotTEC
|
NotTEC
|
||||||
Payment::checkPermission(ReadView const& view, STTx const& tx)
|
Payment::checkGranularSemantics(
|
||||||
|
ReadView const& view,
|
||||||
|
STTx const& tx,
|
||||||
|
std::unordered_set<GranularPermissionType> const& heldGranularPermissions)
|
||||||
{
|
{
|
||||||
auto const delegate = tx[~sfDelegate];
|
|
||||||
if (!delegate)
|
|
||||||
return tesSUCCESS;
|
|
||||||
|
|
||||||
auto const delegateKey = keylet::delegate(tx[sfAccount], *delegate);
|
|
||||||
auto const sle = view.read(delegateKey);
|
|
||||||
|
|
||||||
if (!sle)
|
|
||||||
return terNO_DELEGATE_PERMISSION;
|
|
||||||
|
|
||||||
if (isTesSuccess(checkTxPermission(sle, tx)))
|
|
||||||
return tesSUCCESS;
|
|
||||||
|
|
||||||
std::unordered_set<GranularPermissionType> granularPermissions;
|
|
||||||
loadGranularPermission(sle, ttPAYMENT, granularPermissions);
|
|
||||||
|
|
||||||
auto const& dstAmount = tx.getFieldAmount(sfAmount);
|
auto const& dstAmount = tx.getFieldAmount(sfAmount);
|
||||||
auto const& amountAsset = dstAmount.asset();
|
auto const& amountAsset = dstAmount.asset();
|
||||||
|
|
||||||
// Granular permissions are only valid for direct payments.
|
// Granular permissions are only valid for direct payments.
|
||||||
if ((tx.isFieldPresent(sfSendMax) && tx[sfSendMax].asset() != amountAsset) ||
|
if (tx.isFieldPresent(sfSendMax) && tx[sfSendMax].asset() != amountAsset)
|
||||||
tx.isFieldPresent(sfPaths))
|
|
||||||
return terNO_DELEGATE_PERMISSION;
|
return terNO_DELEGATE_PERMISSION;
|
||||||
|
|
||||||
// PaymentMint and PaymentBurn apply to both IOU and MPT direct payments.
|
// PaymentMint and PaymentBurn apply to both IOU and MPT direct payments.
|
||||||
if (granularPermissions.contains(PaymentMint) && !isXRP(amountAsset) &&
|
if (heldGranularPermissions.contains(PaymentMint) && !isXRP(amountAsset) &&
|
||||||
amountAsset.getIssuer() == tx[sfAccount])
|
amountAsset.getIssuer() == tx[sfAccount])
|
||||||
return tesSUCCESS;
|
return tesSUCCESS;
|
||||||
|
|
||||||
if (granularPermissions.contains(PaymentBurn) && !isXRP(amountAsset) &&
|
if (heldGranularPermissions.contains(PaymentBurn) && !isXRP(amountAsset) &&
|
||||||
amountAsset.getIssuer() == tx[sfDestination])
|
amountAsset.getIssuer() == tx[sfDestination])
|
||||||
return tesSUCCESS;
|
return tesSUCCESS;
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#include <xrpl/beast/utility/instrumentation.h>
|
#include <xrpl/beast/utility/instrumentation.h>
|
||||||
#include <xrpl/core/ServiceRegistry.h>
|
#include <xrpl/core/ServiceRegistry.h>
|
||||||
#include <xrpl/ledger/ReadView.h>
|
#include <xrpl/ledger/ReadView.h>
|
||||||
#include <xrpl/ledger/helpers/DelegateHelpers.h>
|
|
||||||
#include <xrpl/protocol/Feature.h>
|
#include <xrpl/protocol/Feature.h>
|
||||||
#include <xrpl/protocol/Indexes.h>
|
#include <xrpl/protocol/Indexes.h>
|
||||||
#include <xrpl/protocol/LedgerFormats.h>
|
#include <xrpl/protocol/LedgerFormats.h>
|
||||||
@@ -16,14 +15,12 @@
|
|||||||
#include <xrpl/protocol/STTx.h>
|
#include <xrpl/protocol/STTx.h>
|
||||||
#include <xrpl/protocol/TER.h>
|
#include <xrpl/protocol/TER.h>
|
||||||
#include <xrpl/protocol/TxFlags.h>
|
#include <xrpl/protocol/TxFlags.h>
|
||||||
#include <xrpl/protocol/TxFormats.h>
|
|
||||||
#include <xrpl/protocol/XRPAmount.h>
|
#include <xrpl/protocol/XRPAmount.h>
|
||||||
#include <xrpl/tx/Transactor.h>
|
#include <xrpl/tx/Transactor.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <unordered_set>
|
|
||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
@@ -138,39 +135,6 @@ MPTokenIssuanceSet::preflight(PreflightContext const& ctx)
|
|||||||
return tesSUCCESS;
|
return tesSUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
NotTEC
|
|
||||||
MPTokenIssuanceSet::checkPermission(ReadView const& view, STTx const& tx)
|
|
||||||
{
|
|
||||||
auto const delegate = tx[~sfDelegate];
|
|
||||||
if (!delegate)
|
|
||||||
return tesSUCCESS;
|
|
||||||
|
|
||||||
auto const delegateKey = keylet::delegate(tx[sfAccount], *delegate);
|
|
||||||
auto const sle = view.read(delegateKey);
|
|
||||||
|
|
||||||
if (!sle)
|
|
||||||
return terNO_DELEGATE_PERMISSION;
|
|
||||||
|
|
||||||
if (isTesSuccess(checkTxPermission(sle, tx)))
|
|
||||||
return tesSUCCESS;
|
|
||||||
|
|
||||||
// this is added in case more flags will be added for MPTokenIssuanceSet
|
|
||||||
// in the future. Currently unreachable.
|
|
||||||
if ((tx.getFlags() & tfMPTokenIssuanceSetMask) != 0u)
|
|
||||||
return terNO_DELEGATE_PERMISSION; // LCOV_EXCL_LINE
|
|
||||||
|
|
||||||
std::unordered_set<GranularPermissionType> granularPermissions;
|
|
||||||
loadGranularPermission(sle, ttMPTOKEN_ISSUANCE_SET, granularPermissions);
|
|
||||||
|
|
||||||
if (tx.isFlag(tfMPTLock) && !granularPermissions.contains(MPTokenIssuanceLock))
|
|
||||||
return terNO_DELEGATE_PERMISSION;
|
|
||||||
|
|
||||||
if (tx.isFlag(tfMPTUnlock) && !granularPermissions.contains(MPTokenIssuanceUnlock))
|
|
||||||
return terNO_DELEGATE_PERMISSION;
|
|
||||||
|
|
||||||
return tesSUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
TER
|
TER
|
||||||
MPTokenIssuanceSet::preclaim(PreclaimContext const& ctx)
|
MPTokenIssuanceSet::preclaim(PreclaimContext const& ctx)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
#include <xrpl/core/ServiceRegistry.h>
|
#include <xrpl/core/ServiceRegistry.h>
|
||||||
#include <xrpl/ledger/ReadView.h>
|
#include <xrpl/ledger/ReadView.h>
|
||||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||||
#include <xrpl/ledger/helpers/DelegateHelpers.h>
|
|
||||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||||
#include <xrpl/protocol/AMMCore.h>
|
#include <xrpl/protocol/AMMCore.h>
|
||||||
#include <xrpl/protocol/AccountID.h>
|
#include <xrpl/protocol/AccountID.h>
|
||||||
@@ -21,7 +20,6 @@
|
|||||||
#include <xrpl/protocol/STTx.h>
|
#include <xrpl/protocol/STTx.h>
|
||||||
#include <xrpl/protocol/TER.h>
|
#include <xrpl/protocol/TER.h>
|
||||||
#include <xrpl/protocol/TxFlags.h>
|
#include <xrpl/protocol/TxFlags.h>
|
||||||
#include <xrpl/protocol/TxFormats.h>
|
|
||||||
#include <xrpl/protocol/UintTypes.h>
|
#include <xrpl/protocol/UintTypes.h>
|
||||||
#include <xrpl/protocol/XRPAmount.h>
|
#include <xrpl/protocol/XRPAmount.h>
|
||||||
#include <xrpl/tx/Transactor.h>
|
#include <xrpl/tx/Transactor.h>
|
||||||
@@ -124,51 +122,21 @@ TrustSet::preflight(PreflightContext const& ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
NotTEC
|
NotTEC
|
||||||
TrustSet::checkPermission(ReadView const& view, STTx const& tx)
|
TrustSet::checkGranularSemantics(
|
||||||
|
ReadView const& view,
|
||||||
|
STTx const& tx,
|
||||||
|
std::unordered_set<GranularPermissionType> const& heldGranularPermissions)
|
||||||
{
|
{
|
||||||
auto const delegate = tx[~sfDelegate];
|
|
||||||
if (!delegate)
|
|
||||||
return tesSUCCESS;
|
|
||||||
|
|
||||||
auto const delegateKey = keylet::delegate(tx[sfAccount], *delegate);
|
|
||||||
auto const sle = view.read(delegateKey);
|
|
||||||
|
|
||||||
if (!sle)
|
|
||||||
return terNO_DELEGATE_PERMISSION;
|
|
||||||
|
|
||||||
if (isTesSuccess(checkTxPermission(sle, tx)))
|
|
||||||
return tesSUCCESS;
|
|
||||||
|
|
||||||
// Currently we only support TrustlineAuthorize, TrustlineFreeze and
|
|
||||||
// TrustlineUnfreeze granular permission. Setting other flags returns
|
|
||||||
// error.
|
|
||||||
if ((tx.getFlags() & tfTrustSetPermissionMask) != 0u)
|
|
||||||
return terNO_DELEGATE_PERMISSION;
|
|
||||||
|
|
||||||
if (tx.isFieldPresent(sfQualityIn) || tx.isFieldPresent(sfQualityOut))
|
|
||||||
return terNO_DELEGATE_PERMISSION;
|
|
||||||
|
|
||||||
auto const saLimitAmount = tx.getFieldAmount(sfLimitAmount);
|
auto const saLimitAmount = tx.getFieldAmount(sfLimitAmount);
|
||||||
auto const sleRippleState = view.read(
|
auto const sleRippleState = view.read(
|
||||||
keylet::line(
|
keylet::line(
|
||||||
tx[sfAccount], saLimitAmount.getIssuer(), saLimitAmount.get<Issue>().currency));
|
tx[sfAccount], saLimitAmount.getIssuer(), saLimitAmount.get<Issue>().currency));
|
||||||
|
|
||||||
// if the trustline does not exist, granular permissions are
|
// granular permissions are not allowed to create a trustline
|
||||||
// not allowed to create trustline
|
|
||||||
if (!sleRippleState)
|
if (!sleRippleState)
|
||||||
return terNO_DELEGATE_PERMISSION;
|
return terNO_DELEGATE_PERMISSION;
|
||||||
|
|
||||||
std::unordered_set<GranularPermissionType> granularPermissions;
|
// updating LimitAmount is not allowed with granular permissions,
|
||||||
loadGranularPermission(sle, ttTRUST_SET, granularPermissions);
|
|
||||||
|
|
||||||
if (tx.isFlag(tfSetfAuth) && !granularPermissions.contains(TrustlineAuthorize))
|
|
||||||
return terNO_DELEGATE_PERMISSION;
|
|
||||||
if (tx.isFlag(tfSetFreeze) && !granularPermissions.contains(TrustlineFreeze))
|
|
||||||
return terNO_DELEGATE_PERMISSION;
|
|
||||||
if (tx.isFlag(tfClearFreeze) && !granularPermissions.contains(TrustlineUnfreeze))
|
|
||||||
return terNO_DELEGATE_PERMISSION;
|
|
||||||
|
|
||||||
// updating LimitAmount is not allowed only with granular permissions,
|
|
||||||
// unless there's a new granular permission for this in the future.
|
// unless there's a new granular permission for this in the future.
|
||||||
auto const curLimit = tx[sfAccount] > saLimitAmount.getIssuer()
|
auto const curLimit = tx[sfAccount] > saLimitAmount.getIssuer()
|
||||||
? sleRippleState->getFieldAmount(sfHighLimit)
|
? sleRippleState->getFieldAmount(sfHighLimit)
|
||||||
|
|||||||
@@ -5,8 +5,11 @@
|
|||||||
#include <test/jtx/acctdelete.h>
|
#include <test/jtx/acctdelete.h>
|
||||||
#include <test/jtx/amount.h>
|
#include <test/jtx/amount.h>
|
||||||
#include <test/jtx/balance.h>
|
#include <test/jtx/balance.h>
|
||||||
|
#include <test/jtx/batch.h>
|
||||||
#include <test/jtx/delegate.h>
|
#include <test/jtx/delegate.h>
|
||||||
|
#include <test/jtx/delivermin.h>
|
||||||
#include <test/jtx/did.h>
|
#include <test/jtx/did.h>
|
||||||
|
#include <test/jtx/domain.h>
|
||||||
#include <test/jtx/fee.h>
|
#include <test/jtx/fee.h>
|
||||||
#include <test/jtx/flags.h>
|
#include <test/jtx/flags.h>
|
||||||
#include <test/jtx/mpt.h>
|
#include <test/jtx/mpt.h>
|
||||||
@@ -22,7 +25,9 @@
|
|||||||
#include <test/jtx/ter.h>
|
#include <test/jtx/ter.h>
|
||||||
#include <test/jtx/trust.h>
|
#include <test/jtx/trust.h>
|
||||||
#include <test/jtx/txflags.h>
|
#include <test/jtx/txflags.h>
|
||||||
|
#include <test/jtx/vault.h>
|
||||||
|
|
||||||
|
#include <xrpl/basics/Number.h>
|
||||||
#include <xrpl/basics/Slice.h>
|
#include <xrpl/basics/Slice.h>
|
||||||
#include <xrpl/basics/base_uint.h>
|
#include <xrpl/basics/base_uint.h>
|
||||||
#include <xrpl/basics/strHex.h>
|
#include <xrpl/basics/strHex.h>
|
||||||
@@ -33,6 +38,7 @@
|
|||||||
#include <xrpl/ledger/helpers/DelegateHelpers.h>
|
#include <xrpl/ledger/helpers/DelegateHelpers.h>
|
||||||
#include <xrpl/protocol/Feature.h>
|
#include <xrpl/protocol/Feature.h>
|
||||||
#include <xrpl/protocol/Indexes.h>
|
#include <xrpl/protocol/Indexes.h>
|
||||||
|
#include <xrpl/protocol/Issue.h>
|
||||||
#include <xrpl/protocol/KeyType.h>
|
#include <xrpl/protocol/KeyType.h>
|
||||||
#include <xrpl/protocol/Permissions.h>
|
#include <xrpl/protocol/Permissions.h>
|
||||||
#include <xrpl/protocol/SField.h>
|
#include <xrpl/protocol/SField.h>
|
||||||
@@ -41,6 +47,7 @@
|
|||||||
#include <xrpl/protocol/SecretKey.h>
|
#include <xrpl/protocol/SecretKey.h>
|
||||||
#include <xrpl/protocol/TER.h>
|
#include <xrpl/protocol/TER.h>
|
||||||
#include <xrpl/protocol/TxFlags.h>
|
#include <xrpl/protocol/TxFlags.h>
|
||||||
|
#include <xrpl/protocol/XRPAmount.h>
|
||||||
#include <xrpl/protocol/jss.h>
|
#include <xrpl/protocol/jss.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -1063,6 +1070,93 @@ class Delegate_test : public beast::unit_test::Suite
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PaymentMint/PaymentBurn with sfSendMax of the same asset is allowed,
|
||||||
|
// same-asset SendMax is still a direct payment, not cross-currency.
|
||||||
|
{
|
||||||
|
Env env(*this, features);
|
||||||
|
Account const alice{"alice"};
|
||||||
|
Account const bob{"bob"};
|
||||||
|
Account const gw{"gw"};
|
||||||
|
auto const usd = gw["USD"];
|
||||||
|
env.fund(XRP(10000), alice, bob, gw);
|
||||||
|
env.trust(usd(200), alice);
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
env(delegate::set(gw, bob, {"PaymentMint"}));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
// sfSendMax with same asset as sfAmount, still a direct payment
|
||||||
|
env(pay(gw, alice, usd(50)), Sendmax(usd(50)), delegate::As(bob));
|
||||||
|
env.require(Balance(alice, usd(50)));
|
||||||
|
|
||||||
|
env(delegate::set(alice, bob, {"PaymentBurn"}));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
env(pay(alice, gw, usd(30)), Sendmax(usd(30)), delegate::As(bob));
|
||||||
|
env.require(Balance(alice, usd(20)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test invalid fields or flags not allowed in granular permission template
|
||||||
|
{
|
||||||
|
Env env(*this, features);
|
||||||
|
Account const alice{"alice"};
|
||||||
|
Account const bob{"bob"};
|
||||||
|
Account const gw{"gw"};
|
||||||
|
auto const usd = gw["USD"];
|
||||||
|
env.fund(XRP(10000), alice, bob, gw);
|
||||||
|
env.trust(usd(200), alice);
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
env(delegate::set(gw, bob, {"PaymentMint"}));
|
||||||
|
env(delegate::set(alice, bob, {"PaymentBurn"}));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
// sfDeliverMin (with tfPartialPayment) is not in the PaymentMint
|
||||||
|
// or PaymentBurn template.
|
||||||
|
env(pay(gw, alice, usd(100)),
|
||||||
|
DeliverMin(usd(50)),
|
||||||
|
Txflags(tfPartialPayment),
|
||||||
|
delegate::As(bob),
|
||||||
|
Ter(terNO_DELEGATE_PERMISSION));
|
||||||
|
env(pay(alice, gw, usd(50)),
|
||||||
|
DeliverMin(usd(25)),
|
||||||
|
Txflags(tfPartialPayment),
|
||||||
|
delegate::As(bob),
|
||||||
|
Ter(terNO_DELEGATE_PERMISSION));
|
||||||
|
|
||||||
|
// sfDomainID is not in the PaymentMint or PaymentBurn template.
|
||||||
|
env(pay(gw, alice, usd(100)),
|
||||||
|
Domain(uint256{1}),
|
||||||
|
delegate::As(bob),
|
||||||
|
Ter(terNO_DELEGATE_PERMISSION));
|
||||||
|
env(pay(alice, gw, usd(50)),
|
||||||
|
Domain(uint256{1}),
|
||||||
|
delegate::As(bob),
|
||||||
|
Ter(terNO_DELEGATE_PERMISSION));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delegate account holds no granular permissions for the tx type:
|
||||||
|
// getGranularPermission returns empty set.
|
||||||
|
{
|
||||||
|
Env env(*this, features);
|
||||||
|
Account const alice{"alice"};
|
||||||
|
Account const bob{"bob"};
|
||||||
|
Account const gw{"gw"};
|
||||||
|
auto const usd = gw["USD"];
|
||||||
|
env.fund(XRP(10000), alice, bob, gw);
|
||||||
|
env.trust(usd(200), alice);
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
// Bob holds only an AccountSet granular permission.
|
||||||
|
env(delegate::set(alice, bob, {"AccountDomainSet"}));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
// Payment has granular permissions defined in permissions.macro,
|
||||||
|
// but bob only holds AccountSet's granular permission,
|
||||||
|
// getGranularPermission returns empty.
|
||||||
|
env(pay(alice, gw, usd(50)), delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
|
||||||
|
}
|
||||||
|
|
||||||
// PaymentMint and PaymentBurn for MPT
|
// PaymentMint and PaymentBurn for MPT
|
||||||
{
|
{
|
||||||
std::string logs;
|
std::string logs;
|
||||||
@@ -1119,6 +1213,40 @@ class Delegate_test : public beast::unit_test::Suite
|
|||||||
BEAST_EXPECT(env.balance(bob, MPT) == bobMPT + MPT(100));
|
BEAST_EXPECT(env.balance(bob, MPT) == bobMPT + MPT(100));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify granular permissions of different tx types in the same SLE are scoped
|
||||||
|
// correctly. AccountSet permissions don't apply to Payment and vice versa
|
||||||
|
{
|
||||||
|
Env env(*this);
|
||||||
|
Account const alice{"alice"};
|
||||||
|
Account const bob{"bob"};
|
||||||
|
Account const gw{"gw"};
|
||||||
|
auto const usd = gw["USD"];
|
||||||
|
env.fund(XRP(10000), alice, bob, gw);
|
||||||
|
env.trust(usd(200), alice);
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
// Alice granted bob with both AccountDomainSet and PaymentMint.
|
||||||
|
env(delegate::set(alice, bob, {"AccountDomainSet", "PaymentMint"}));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
// PaymentMint fails at granular semantic check because alice is not the issuer.
|
||||||
|
env(pay(alice, gw, usd(50)), delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
|
||||||
|
|
||||||
|
// AccountDomainSet applies correctly to AccountSet
|
||||||
|
std::string const domain = "example.com";
|
||||||
|
auto jt = noop(alice);
|
||||||
|
jt[sfDomain] = strHex(domain);
|
||||||
|
jt[sfDelegate] = bob.human();
|
||||||
|
env(jt);
|
||||||
|
BEAST_EXPECT((*env.le(alice))[sfDomain] == makeSlice(domain));
|
||||||
|
|
||||||
|
// gw gives bob PaymentMint and bob can mint on gw's behalf
|
||||||
|
env(delegate::set(gw, bob, {"PaymentMint"}));
|
||||||
|
env.close();
|
||||||
|
env(pay(gw, alice, usd(50)), delegate::As(bob));
|
||||||
|
env.require(Balance(alice, usd(50)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -1301,6 +1429,34 @@ class Delegate_test : public beast::unit_test::Suite
|
|||||||
env(trust(gw, gw["USD"](0), alice, tfSetfAuth | tfFullyCanonicalSig),
|
env(trust(gw, gw["USD"](0), alice, tfSetfAuth | tfFullyCanonicalSig),
|
||||||
delegate::As(bob));
|
delegate::As(bob));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Env env(*this);
|
||||||
|
Account const gw{"gw"};
|
||||||
|
Account const alice{"alice"};
|
||||||
|
Account const bob{"bob"};
|
||||||
|
env.fund(XRP(10000), gw, alice, bob);
|
||||||
|
|
||||||
|
env(fset(gw, asfRequireAuth));
|
||||||
|
env.close();
|
||||||
|
env(trust(alice, gw["USD"](50)));
|
||||||
|
env.close();
|
||||||
|
env(delegate::set(gw, bob, {"TrustlineAuthorize"}));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
env(trust(gw, gw["USD"](0), alice, tfSetfAuth), delegate::As(bob));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
// sfQualityOut is a valid TrustSet field, but not permitted in granular template
|
||||||
|
json::Value txJson = trust(gw, gw["USD"](0), alice, tfSetfAuth);
|
||||||
|
txJson[sfQualityOut.jsonName] = 100;
|
||||||
|
env(txJson, delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
|
||||||
|
|
||||||
|
// tfSetNoRipple is a valid flag for TrustSet, but not permitted in granular template
|
||||||
|
env(trust(gw, gw["USD"](0), alice, tfSetfAuth | tfSetNoRipple),
|
||||||
|
delegate::As(bob),
|
||||||
|
Ter(terNO_DELEGATE_PERMISSION));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -1456,7 +1612,9 @@ class Delegate_test : public beast::unit_test::Suite
|
|||||||
env(jv2, Ter(terNO_DELEGATE_PERMISSION));
|
env(jv2, Ter(terNO_DELEGATE_PERMISSION));
|
||||||
}
|
}
|
||||||
|
|
||||||
// can not set AccountSet flags on behalf of other account
|
// can not set AccountSet flags on behalf of other account,
|
||||||
|
// in permissions.macro, the template for AccountSet does
|
||||||
|
// not allow any flag set or clear.
|
||||||
{
|
{
|
||||||
Env env(*this);
|
Env env(*this);
|
||||||
auto const alice = Account{"alice"};
|
auto const alice = Account{"alice"};
|
||||||
@@ -1552,6 +1710,71 @@ class Delegate_test : public beast::unit_test::Suite
|
|||||||
env(jt);
|
env(jt);
|
||||||
BEAST_EXPECT((*env.le(alice))[sfDomain] == makeSlice(domain));
|
BEAST_EXPECT((*env.le(alice))[sfDomain] == makeSlice(domain));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setting invalid field not in permissions.macro template will be rejected.
|
||||||
|
{
|
||||||
|
Env env(*this);
|
||||||
|
auto const alice = Account{"alice"};
|
||||||
|
auto const bob = Account{"bob"};
|
||||||
|
env.fund(XRP(10000), alice, bob);
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
// Alice gives Bob permission to set her Domain
|
||||||
|
env(delegate::set(alice, bob, {"AccountDomainSet"}));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
std::string const domain = "example.com";
|
||||||
|
auto txJson = noop(alice);
|
||||||
|
txJson[sfDomain] = strHex(domain);
|
||||||
|
txJson[sfDelegate] = bob.human();
|
||||||
|
|
||||||
|
// sfNFTokenMinter is a valid field in AccountSet tx, but
|
||||||
|
// it is not permitted for granular template
|
||||||
|
txJson[sfNFTokenMinter] = bob.human();
|
||||||
|
|
||||||
|
env(txJson, Ter(terNO_DELEGATE_PERMISSION));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delegated AccountSet with no fields and no flags is allowed,
|
||||||
|
// because it is allowed in the non-delegated case as well.
|
||||||
|
{
|
||||||
|
Env env(*this);
|
||||||
|
Account const alice{"alice"};
|
||||||
|
Account const bob{"bob"};
|
||||||
|
env.fund(XRP(10000), alice, bob);
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
env(delegate::set(alice, bob, {"AccountDomainSet"}));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
auto jt = noop(alice);
|
||||||
|
jt[sfDelegate] = bob.human();
|
||||||
|
env(jt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Revoking all permissions deletes the SLE and subsequent attempts are rejected.
|
||||||
|
{
|
||||||
|
Env env(*this);
|
||||||
|
Account const alice{"alice"};
|
||||||
|
Account const bob{"bob"};
|
||||||
|
env.fund(XRP(10000), alice, bob);
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
env(delegate::set(alice, bob, {"AccountDomainSet"}));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
std::string const domain = "example.com";
|
||||||
|
auto jt = noop(alice);
|
||||||
|
jt[sfDomain] = strHex(domain);
|
||||||
|
jt[sfDelegate] = bob.human();
|
||||||
|
env(jt);
|
||||||
|
|
||||||
|
// empty DelegateSet deletes the SLE
|
||||||
|
env(delegate::set(alice, bob, {}));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
env(jt, Ter(terNO_DELEGATE_PERMISSION));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -1672,6 +1895,37 @@ class Delegate_test : public beast::unit_test::Suite
|
|||||||
env.close();
|
env.close();
|
||||||
mpt.set({.account = alice, .flags = tfMPTLock | tfFullyCanonicalSig, .delegate = bob});
|
mpt.set({.account = alice, .flags = tfMPTLock | tfFullyCanonicalSig, .delegate = bob});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// field not permitted to exist in granular delegation
|
||||||
|
{
|
||||||
|
Env env(*this);
|
||||||
|
Account const alice{"alice"};
|
||||||
|
Account const bob{"bob"};
|
||||||
|
env.fund(XRP(100000), alice, bob);
|
||||||
|
|
||||||
|
MPTTester mpt(env, alice, {.fund = false});
|
||||||
|
mpt.create({.flags = tfMPTCanLock});
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
// alice gives granular permission to bob for MPTokenIssuanceLock
|
||||||
|
env(delegate::set(alice, bob, {"MPTokenIssuanceLock"}));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
// Field is not permitted, permitted fields for delegation is defined in
|
||||||
|
// permissions.macro.
|
||||||
|
mpt.set(
|
||||||
|
{.account = alice,
|
||||||
|
.mutableFlags = 2,
|
||||||
|
.delegate = bob,
|
||||||
|
.err = terNO_DELEGATE_PERMISSION});
|
||||||
|
|
||||||
|
// Notice: flags not defined in permissions.macro are not permitted for delegation.
|
||||||
|
// Since preflight will check invalid flag for the tx, it is not reachable.
|
||||||
|
// If any new flag is defined into the transaction in the future,
|
||||||
|
// but is not allowed for delegation, the transaction will be rejected with
|
||||||
|
// terNO_DELEGATE_PERMISSION. The set of permitted flags for delegation is defined in
|
||||||
|
// permissions.macro.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -2141,6 +2395,62 @@ class Delegate_test : public beast::unit_test::Suite
|
|||||||
for (auto const& tx : txRequiredFeatures)
|
for (auto const& tx : txRequiredFeatures)
|
||||||
txAmendmentEnabled(tx.first);
|
txAmendmentEnabled(tx.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Granular permissions also require the amendment for their underlying
|
||||||
|
// transaction type.
|
||||||
|
{
|
||||||
|
for (auto const permission : {"MPTokenIssuanceLock", "MPTokenIssuanceUnlock"})
|
||||||
|
{
|
||||||
|
Env env(*this, features - featureMPTokensV1);
|
||||||
|
|
||||||
|
Account const alice{"alice"};
|
||||||
|
Account const bob{"bob"};
|
||||||
|
env.fund(XRP(100000), alice, bob);
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
env(delegate::set(alice, bob, {permission}), Ter(temMALFORMED));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testGranularSandboxCheckOrder()
|
||||||
|
{
|
||||||
|
testcase("Make sure GranularSandbox is checked after transaction-level permission");
|
||||||
|
|
||||||
|
using namespace jtx;
|
||||||
|
|
||||||
|
Env env(*this);
|
||||||
|
Account const gw{"gw"};
|
||||||
|
Account const alice{"alice"};
|
||||||
|
Account const bob{"bob"};
|
||||||
|
env.fund(XRP(10000), gw, alice, bob);
|
||||||
|
|
||||||
|
env(fset(gw, asfRequireAuth));
|
||||||
|
env.close();
|
||||||
|
env(trust(alice, gw["USD"](50)));
|
||||||
|
env.close();
|
||||||
|
env(delegate::set(gw, bob, {"TrustlineAuthorize"}));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
env(trust(gw, gw["USD"](0), alice, tfSetfAuth), delegate::As(bob));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
// sfQualityOut is a valid TrustSet field, but not permitted in granular template
|
||||||
|
json::Value txJson = trust(gw, gw["USD"](0), alice, tfSetfAuth);
|
||||||
|
txJson[sfQualityOut.jsonName] = 100;
|
||||||
|
env(txJson, delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
|
||||||
|
|
||||||
|
// Now Alice grants Bob with transaction level permission
|
||||||
|
env(delegate::set(gw, bob, {"TrustlineAuthorize", "TrustSet"}));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
// NOTE: This case is to ensure that if a delegate possesses a
|
||||||
|
// transaction-level permission (e.g., TrustSet), the granular sandbox must not incorrectly
|
||||||
|
// block the transaction. The function checkGranularSandbox MUST be called after the
|
||||||
|
// transaction-level permission check. This test case is to avoid future refactor mistakes,
|
||||||
|
// modifying the order will fail here.
|
||||||
|
env(txJson, delegate::As(bob));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -2193,6 +2503,94 @@ class Delegate_test : public beast::unit_test::Suite
|
|||||||
"\n Action: Verify security requirements to interact with Delegation feature");
|
"\n Action: Verify security requirements to interact with Delegation feature");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testNonDelegableTxWithDelegate(FeatureBitset features)
|
||||||
|
{
|
||||||
|
testcase("non-delegable tx with sfDelegate is rejected at preflight");
|
||||||
|
using namespace jtx;
|
||||||
|
|
||||||
|
Env env(*this, features);
|
||||||
|
Account const alice{"alice"};
|
||||||
|
Account const bob{"bob"};
|
||||||
|
env.fund(XRP(10000), alice, bob);
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
// Transactions that are notDelegable and have no granular permissions
|
||||||
|
// will be rejected with temINVALID at preflight.
|
||||||
|
// Note: pseudo-transactions (EnableAmendment, SetFee and UNLModify) are also
|
||||||
|
// notDelegable but are excluded here — passesLocalChecks() blocks them
|
||||||
|
// before preflight1 is ever reached.
|
||||||
|
{
|
||||||
|
// SetRegularKey, SignerListSet, AccountDelete, DelegateSet.
|
||||||
|
env(regkey(alice, bob), delegate::As(bob), Ter(temINVALID));
|
||||||
|
env(signers(alice, 1, {{bob, 1}}), delegate::As(bob), Ter(temINVALID));
|
||||||
|
env(acctdelete(alice, bob), delegate::As(bob), Ter(temINVALID));
|
||||||
|
env(delegate::set(alice, bob, {"Payment"}), delegate::As(bob), Ter(temINVALID));
|
||||||
|
|
||||||
|
// SAV transactions.
|
||||||
|
{
|
||||||
|
Vault const vault{env};
|
||||||
|
auto [createTx, keylet] = vault.create({.owner = alice, .asset = xrpIssue()});
|
||||||
|
env(createTx, delegate::As(bob), Ter(temINVALID));
|
||||||
|
|
||||||
|
env(vault.set({.owner = alice, .id = keylet.key}),
|
||||||
|
delegate::As(bob),
|
||||||
|
Ter(temINVALID));
|
||||||
|
env(vault.del({.owner = alice, .id = keylet.key}),
|
||||||
|
delegate::As(bob),
|
||||||
|
Ter(temINVALID));
|
||||||
|
env(vault.deposit({.depositor = alice, .id = keylet.key, .amount = XRP(1)}),
|
||||||
|
delegate::As(bob),
|
||||||
|
Ter(temINVALID));
|
||||||
|
env(vault.withdraw({.depositor = alice, .id = keylet.key, .amount = XRP(1)}),
|
||||||
|
delegate::As(bob),
|
||||||
|
Ter(temINVALID));
|
||||||
|
env(vault.clawback({.issuer = alice, .id = keylet.key, .holder = bob}),
|
||||||
|
delegate::As(bob),
|
||||||
|
Ter(temINVALID));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Batch transaction: the outer Batch itself is non-delegable.
|
||||||
|
{
|
||||||
|
auto const seq = env.seq(alice);
|
||||||
|
auto const batchFee = batch::calcBatchFee(env, 0, 1);
|
||||||
|
env(batch::outer(alice, seq, batchFee, tfAllOrNothing),
|
||||||
|
batch::Inner(pay(alice, bob, XRP(1)), seq + 1),
|
||||||
|
delegate::As(bob),
|
||||||
|
Ter(temINVALID));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lending protocol transactions
|
||||||
|
{
|
||||||
|
Vault const vault{env};
|
||||||
|
auto [createTx, keylet] = vault.create({.owner = alice, .asset = xrpIssue()});
|
||||||
|
env(createTx);
|
||||||
|
|
||||||
|
env(loanBroker::set(alice, keylet.key), delegate::As(bob), Ter(temINVALID));
|
||||||
|
env(loanBroker::del(alice, keylet.key), delegate::As(bob), Ter(temINVALID));
|
||||||
|
env(loanBroker::coverDeposit(alice, keylet.key, XRP(1)),
|
||||||
|
delegate::As(bob),
|
||||||
|
Ter(temINVALID));
|
||||||
|
env(loanBroker::coverWithdraw(alice, keylet.key, XRP(1)),
|
||||||
|
delegate::As(bob),
|
||||||
|
Ter(temINVALID));
|
||||||
|
env(loanBroker::coverClawback(alice), delegate::As(bob), Ter(temINVALID));
|
||||||
|
|
||||||
|
env(loan::set(alice, keylet.key, Number(100)), delegate::As(bob), Ter(temINVALID));
|
||||||
|
env(loan::manage(alice, keylet.key, 0), delegate::As(bob), Ter(temINVALID));
|
||||||
|
env(loan::del(alice, keylet.key), delegate::As(bob), Ter(temINVALID));
|
||||||
|
env(loan::pay(alice, keylet.key, XRP(1)), delegate::As(bob), Ter(temINVALID));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccountSet is notDelegable at tx level but has granular permissions,
|
||||||
|
// so sfDelegate passes preflight and is rejected at invokeCheckPermission with
|
||||||
|
// terNO_DELEGATE_PERMISSION.
|
||||||
|
{
|
||||||
|
env(fset(alice, asfDefaultRipple), delegate::As(bob), Ter(terNO_DELEGATE_PERMISSION));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
testDelegateUtilsNullptrCheck()
|
testDelegateUtilsNullptrCheck()
|
||||||
{
|
{
|
||||||
@@ -2202,9 +2600,8 @@ class Delegate_test : public beast::unit_test::Suite
|
|||||||
STTx const tx{ttPAYMENT, [](STObject&) {}};
|
STTx const tx{ttPAYMENT, [](STObject&) {}};
|
||||||
BEAST_EXPECT(checkTxPermission(nullptr, tx) == terNO_DELEGATE_PERMISSION);
|
BEAST_EXPECT(checkTxPermission(nullptr, tx) == terNO_DELEGATE_PERMISSION);
|
||||||
|
|
||||||
// loadGranularPermission nullptr check
|
// getGranularPermission nullptr check
|
||||||
std::unordered_set<GranularPermissionType> granularPermissions;
|
auto const granularPermissions = getGranularPermission(nullptr, ttPAYMENT);
|
||||||
loadGranularPermission(nullptr, ttPAYMENT, granularPermissions);
|
|
||||||
BEAST_EXPECT(granularPermissions.empty());
|
BEAST_EXPECT(granularPermissions.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2234,7 +2631,9 @@ class Delegate_test : public beast::unit_test::Suite
|
|||||||
testSignForDelegated();
|
testSignForDelegated();
|
||||||
testPermissionValue(all);
|
testPermissionValue(all);
|
||||||
testTxRequireFeatures(all);
|
testTxRequireFeatures(all);
|
||||||
|
testGranularSandboxCheckOrder();
|
||||||
testTxDelegableCount();
|
testTxDelegableCount();
|
||||||
|
testNonDelegableTxWithDelegate(all);
|
||||||
testDelegateUtilsNullptrCheck();
|
testDelegateUtilsNullptrCheck();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user