Skip to content

Commit

Permalink
Add AccountPermission
Browse files Browse the repository at this point in the history
  • Loading branch information
yinyiqian1 committed Nov 18, 2024
1 parent d5018e6 commit 04cf7ad
Show file tree
Hide file tree
Showing 20 changed files with 164 additions and 45 deletions.
10 changes: 8 additions & 2 deletions include/xrpl/protocol/Permissions.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#ifndef RIPPLE_PROTOCOL_PERMISSION_H_INCLUDED
#define RIPPLE_PROTOCOL_PERMISSION_H_INCLUDED

#include <xrpl/protocol/STTx.h>
#include <optional>
#include <string>
#include <unordered_map>
Expand Down Expand Up @@ -66,15 +67,20 @@ class Permission
std::unordered_map<std::string, GranularPermissionType>
granularPermissionMap;

std::unordered_map<GranularPermissionType, TxType> granularTxTypeMap;

public:
static Permission const&
getInstance();

std::optional<std::uint32_t>
getGranularValue(const std::string& name) const;
getGranularValue(std::string const& name) const;

std::optional<TxType>
getGranularTxType(GranularPermissionType const& gpType) const;

bool
isProhibited(const std::string& name) const;
isProhibited(std::string const& name) const;
};

} // namespace ripple
Expand Down
2 changes: 1 addition & 1 deletion include/xrpl/protocol/detail/features.macro
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
// If you add an amendment here, then do not forget to increment `numFeatures`
// in include/xrpl/protocol/Feature.h.

XRPL_FEATURE(AccountPermission, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(Credentials, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(AMMClawback, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (AMMv1_2, Supported::yes, VoteBehavior::DefaultNo)
Expand Down Expand Up @@ -98,7 +99,6 @@ XRPL_FIX (1513, Supported::yes, VoteBehavior::DefaultYe
XRPL_FEATURE(FlowCross, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FEATURE(Flow, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FEATURE(OwnerPaysFee, Supported::no, VoteBehavior::DefaultNo)
XRPL_FEATURE(AccountPermission, Supported::yes, VoteBehavior::DefaultYes)


// The following amendments are obsolete, but must remain supported
Expand Down
28 changes: 26 additions & 2 deletions src/libxrpl/protocol/Permissions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,20 @@ Permission::Permission()
{"PaymentBurn", gpPaymentBurn},
{"MPTokenIssuanceLock", gpMPTokenIssuanceLock},
{"MPTokenIssuanceUnlock", gpMPTokenIssuanceUnlock}};

granularTxTypeMap = {
{gpTrustlineAuthorize, ttTRUST_SET},
{gpTrustlineFreeze, ttTRUST_SET},
{gpTrustlineUnfreeze, ttTRUST_SET},
{gpAccountDomainSet, ttACCOUNT_SET},
{gpAccountEmailHashSet, ttACCOUNT_SET},
{gpAccountMessageKeySet, ttACCOUNT_SET},
{gpAccountTransferRateSet, ttACCOUNT_SET},
{gpAccountTickSizeSet, ttACCOUNT_SET},
{gpPaymentMint, ttPAYMENT},
{gpPaymentBurn, ttPAYMENT},
{gpMPTokenIssuanceLock, ttMPTOKEN_ISSUANCE_SET},
{gpMPTokenIssuanceUnlock, ttMPTOKEN_ISSUANCE_SET}};
}

Permission const&
Expand All @@ -49,7 +63,7 @@ Permission::getInstance()
}

std::optional<std::uint32_t>
Permission::getGranularValue(const std::string& name) const
Permission::getGranularValue(std::string const& name) const
{
auto const it = granularPermissionMap.find(name);
if (it != granularPermissionMap.end())
Expand All @@ -58,8 +72,18 @@ Permission::getGranularValue(const std::string& name) const
return std::nullopt;
}

std::optional<TxType>
Permission::getGranularTxType(GranularPermissionType const& gpType) const
{
auto const it = granularTxTypeMap.find(gpType);
if (it != granularTxTypeMap.end())
return it->second;

return std::nullopt;
}

bool
Permission::isProhibited(const std::string& name) const
Permission::isProhibited(std::string const& name) const
{
// We do not allow delegating the following transaction permissions to other
// accounts for security reason.
Expand Down
2 changes: 2 additions & 0 deletions src/test/app/NFTokenBurn_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -813,6 +813,7 @@ class NFTokenBurnBaseUtil_test : public beast::unit_test::suite
tapNONE,
false,
AccountID(0),
std::unordered_set<GranularPermissionType>{},
jlog};

// Verify that the last page is present and contains one NFT.
Expand Down Expand Up @@ -859,6 +860,7 @@ class NFTokenBurnBaseUtil_test : public beast::unit_test::suite
tapNONE,
false,
AccountID(0),
std::unordered_set<GranularPermissionType>{},
jlog};

// Verify that the middle page is present.
Expand Down
1 change: 1 addition & 0 deletions src/test/ledger/Invariants_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ class Invariants_test : public beast::unit_test::suite
tapNONE,
false,
AccountID(0),
std::unordered_set<GranularPermissionType>{},
jlog};

BEAST_EXPECT(precheck(A1, A2, ac));
Expand Down
12 changes: 10 additions & 2 deletions src/xrpld/app/tx/applySteps.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

#include <xrpld/ledger/ApplyViewImpl.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/protocol/Permissions.h>

namespace ripple {

Expand Down Expand Up @@ -209,26 +210,33 @@ struct PreclaimResult
/// From the input - the journal
beast::Journal const j;
/// If the transaction is a delegated transaction
bool const isDelegated; // todo: may remove
bool const isDelegated;
/// the account that the transaction is operated on
AccountID const account;

/// Intermediate transaction result
TER const ter;
/// granular permissions enabled for the transaction. If empty and
/// isDelegated=true, then the entire transaction is authorized.
std::unordered_set<GranularPermissionType> gpSet;
/// Success flag - whether the transaction is likely to
/// claim a fee
bool const likelyToClaimFee;

/// Constructor
template <class Context>
PreclaimResult(Context const& ctx_, TER ter_)
PreclaimResult(
Context const& ctx_,
TER ter_,
std::unordered_set<GranularPermissionType> gpSet_)
: view(ctx_.view)
, tx(ctx_.tx)
, flags(ctx_.flags)
, j(ctx_.j)
, isDelegated(ctx_.isDelegated)
, account(ctx_.account)
, ter(ter_)
, gpSet(std::move(gpSet_))
, likelyToClaimFee(ter == tesSUCCESS || isTecClaimHardFail(ter, flags))
{
}
Expand Down
2 changes: 2 additions & 0 deletions src/xrpld/app/tx/detail/ApplyContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,15 @@ ApplyContext::ApplyContext(
ApplyFlags flags,
bool isDelegated, // todo: may remove
AccountID const account,
std::unordered_set<GranularPermissionType> const gpSet,
beast::Journal journal_)
: app(app_)
, tx(tx_)
, preclaimResult(preclaimResult_)
, baseFee(baseFee_)
, isDelegated(isDelegated)
, account(account)
, gpSet(std::move(gpSet))
, journal(journal_)
, base_(base)
, flags_(flags)
Expand Down
3 changes: 3 additions & 0 deletions src/xrpld/app/tx/detail/ApplyContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <xrpld/ledger/ApplyViewImpl.h>
#include <xrpl/basics/XRPAmount.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/protocol/Permissions.h>
#include <xrpl/protocol/STTx.h>
#include <optional>
#include <utility>
Expand All @@ -44,6 +45,7 @@ class ApplyContext
ApplyFlags flags,
bool isDelegated,
AccountID const account,
std::unordered_set<GranularPermissionType> const gpSet,
beast::Journal = beast::Journal{beast::Journal::getNullSink()});

Application& app;
Expand All @@ -52,6 +54,7 @@ class ApplyContext
XRPAmount const baseFee;
bool isDelegated;
AccountID const account;
std::unordered_set<GranularPermissionType> gpSet;
beast::Journal const journal;

ApplyView&
Expand Down
2 changes: 1 addition & 1 deletion src/xrpld/app/tx/detail/CancelOffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ CancelOffer::preflight(PreflightContext const& ctx)
TER
CancelOffer::preclaim(PreclaimContext const& ctx)
{
auto const id = ctx.tx[sfAccount];
auto const id = ctx.account;
auto const offerSequence = ctx.tx[sfOfferSequence];

auto const sle = ctx.view.read(keylet::account(id));
Expand Down
2 changes: 1 addition & 1 deletion src/xrpld/app/tx/detail/Change.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Change::preflight(PreflightContext const& ctx)
if (!isTesSuccess(ret))
return ret;

auto account = ctx.tx.getAccountID(sfAccount);
auto account = ctx.account;
if (account != beast::zero)
{
JLOG(ctx.j.warn()) << "Change: Bad source id";
Expand Down
8 changes: 4 additions & 4 deletions src/xrpld/app/tx/detail/DeleteOracle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,17 @@ DeleteOracle::preflight(PreflightContext const& ctx)
TER
DeleteOracle::preclaim(PreclaimContext const& ctx)
{
if (!ctx.view.exists(keylet::account(ctx.tx.getAccountID(sfAccount))))
if (!ctx.view.exists(keylet::account(ctx.account)))
return terNO_ACCOUNT; // LCOV_EXCL_LINE

if (auto const sle = ctx.view.read(keylet::oracle(
ctx.tx.getAccountID(sfAccount), ctx.tx[sfOracleDocumentID]));
if (auto const sle = ctx.view.read(
keylet::oracle(ctx.account, ctx.tx[sfOracleDocumentID]));
!sle)
{
JLOG(ctx.j.debug()) << "Oracle Delete: Oracle does not exist.";
return tecNO_ENTRY;
}
else if (ctx.tx.getAccountID(sfAccount) != sle->getAccountID(sfOwner))
else if (ctx.account != sle->getAccountID(sfOwner))
{
// this can't happen because of the above check
// LCOV_EXCL_START
Expand Down
17 changes: 14 additions & 3 deletions src/xrpld/app/tx/detail/MPTokenIssuanceSet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ MPTokenIssuanceSet::preclaim(PreclaimContext const& ctx)
return tecNO_PERMISSION;

// ensure it is issued by the tx submitter
if ((*sleMptIssuance)[sfIssuer] != ctx.tx[sfAccount])
if ((*sleMptIssuance)[sfIssuer] != ctx.account)
return tecNO_PERMISSION;

if (auto const holderID = ctx.tx[~sfHolder])
Expand Down Expand Up @@ -102,9 +102,20 @@ MPTokenIssuanceSet::doApply()
std::uint32_t const flagsIn = sle->getFieldU32(sfFlags);
std::uint32_t flagsOut = flagsIn;

if (txFlags & tfMPTLock)
bool bLock = txFlags & tfMPTLock;
bool bUnlock = txFlags & tfMPTUnlock;
if (ctx_.isDelegated && !ctx_.gpSet.empty())
{
if (bLock && ctx_.gpSet.find(gpMPTokenIssuanceLock) == ctx_.gpSet.end())
return terNO_AUTH;
if (bUnlock &&
ctx_.gpSet.find(gpMPTokenIssuanceUnlock) == ctx_.gpSet.end())
return terNO_AUTH;
}

if (bLock)
flagsOut |= lsfMPTLocked;
else if (txFlags & tfMPTUnlock)
else if (bUnlock)
flagsOut &= ~lsfMPTLocked;

if (flagsIn != flagsOut)
Expand Down
18 changes: 18 additions & 0 deletions src/xrpld/app/tx/detail/Payment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,24 @@ Payment::doApply()

AccountID const dstAccountID(ctx_.tx.getAccountID(sfDestination));
STAmount const dstAmount(ctx_.tx.getFieldAmount(sfAmount));

if (ctx_.isDelegated && !ctx_.gpSet.empty())
{
bool authorized = false;
auto const amountIssue = dstAmount.issue();
if (isXRP(amountIssue))
return tecNO_AUTH;
if (amountIssue.account == account_ &&
ctx_.gpSet.find(gpPaymentMint) == ctx_.gpSet.end())
authorized = true;
if (amountIssue.account == dstAccountID &&
ctx_.gpSet.find(gpPaymentBurn) == ctx_.gpSet.end())
authorized = true;

if (!authorized)
return tecNO_AUTH;
}

bool const mptDirect = dstAmount.holds<MPTIssue>();
STAmount const maxSourceAmount =
getMaxSourceAmount(account_, dstAmount, sendMax);
Expand Down
12 changes: 5 additions & 7 deletions src/xrpld/app/tx/detail/SetOracle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,7 @@ SetOracle::preflight(PreflightContext const& ctx)
TER
SetOracle::preclaim(PreclaimContext const& ctx)
{
auto const sleSetter =
ctx.view.read(keylet::account(ctx.tx.getAccountID(sfAccount)));
auto const sleSetter = ctx.view.read(keylet::account(ctx.account));
if (!sleSetter)
return terNO_ACCOUNT; // LCOV_EXCL_LINE

Expand All @@ -92,8 +91,8 @@ SetOracle::preclaim(PreclaimContext const& ctx)
lastUpdateTimeEpoch > (closeTime + maxLastUpdateTimeDelta))
return tecINVALID_UPDATE_TIME;

auto const sle = ctx.view.read(keylet::oracle(
ctx.tx.getAccountID(sfAccount), ctx.tx[sfOracleDocumentID]));
auto const sle =
ctx.view.read(keylet::oracle(ctx.account, ctx.tx[sfOracleDocumentID]));

// token pairs to add/update
std::set<std::pair<Currency, Currency>> pairs;
Expand Down Expand Up @@ -185,8 +184,7 @@ SetOracle::preclaim(PreclaimContext const& ctx)
static bool
adjustOwnerCount(ApplyContext& ctx, int count)
{
if (auto const sleAccount =
ctx.view().peek(keylet::account(ctx.tx[sfAccount])))
if (auto const sleAccount = ctx.view().peek(keylet::account(ctx.account)))
{
adjustOwnerCount(ctx.view(), sleAccount, count, ctx.journal);
return true;
Expand Down Expand Up @@ -281,7 +279,7 @@ SetOracle::doApply()
// create

sle = std::make_shared<SLE>(oracleID);
sle->setAccountID(sfOwner, ctx_.tx.getAccountID(sfAccount));
sle->setAccountID(sfOwner, account_);
sle->setFieldVL(sfProvider, ctx_.tx[sfProvider]);
if (ctx_.tx.isFieldPresent(sfURI))
sle->setFieldVL(sfURI, ctx_.tx[sfURI]);
Expand Down
2 changes: 1 addition & 1 deletion src/xrpld/app/tx/detail/SetRegularKey.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ SetRegularKey::preflight(PreflightContext const& ctx)

if (ctx.rules.enabled(fixMasterKeyAsRegularKey) &&
ctx.tx.isFieldPresent(sfRegularKey) &&
(ctx.tx.getAccountID(sfRegularKey) == ctx.tx.getAccountID(sfAccount)))
(ctx.tx.getAccountID(sfRegularKey) == ctx.account))
{
return temBAD_REGKEY;
}
Expand Down
2 changes: 1 addition & 1 deletion src/xrpld/app/tx/detail/SetSignerList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ SetSignerList::preflight(PreflightContext const& ctx)
if (std::get<3>(result) == set)
{
// Validate our settings.
auto const account = ctx.tx.getAccountID(sfAccount);
auto const account = ctx.account;
NotTEC const ter = validateQuorumAndSignerEntries(
std::get<1>(result),
std::get<2>(result),
Expand Down
15 changes: 15 additions & 0 deletions src/xrpld/app/tx/detail/SetTrust.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,21 @@ SetTrust::doApply()
bool const bSetFreeze = (uTxFlags & tfSetFreeze);
bool const bClearFreeze = (uTxFlags & tfClearFreeze);

if (ctx_.isDelegated && !ctx_.gpSet.empty())
{
if (bSetNoRipple || bClearNoRipple)
return terNO_AUTH;
if (bSetAuth &&
ctx_.gpSet.find(gpTrustlineAuthorize) == ctx_.gpSet.end())
return terNO_AUTH;
if (bSetFreeze &&
ctx_.gpSet.find(gpTrustlineFreeze) == ctx_.gpSet.end())
return terNO_AUTH;
if (bClearFreeze &&
ctx_.gpSet.find(gpTrustlineUnfreeze) == ctx_.gpSet.end())
return terNO_AUTH;
}

auto viewJ = ctx_.app.journal("View");

// Trust lines to self are impossible but because of the old bug there are
Expand Down
Loading

0 comments on commit 04cf7ad

Please sign in to comment.