-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Work in Progress] add AccountPermission #5164
base: develop
Are you sure you want to change the base?
Conversation
81fcb3f
to
34c5065
Compare
34c5065
to
2e9b7a6
Compare
gpMPTokenIssuanceUnlock = 65548, | ||
}; | ||
|
||
class Permission |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should probably delete copy and copy assignment constructors
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does this still needs to be done?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just added
6101542
to
bd7150e
Compare
04cf7ad
to
5c1d6a3
Compare
5c1d6a3
to
6bbe2ca
Compare
PR description should be added with a link to technical specifications if applicable. |
just added |
FYI: unit test failures are transient, which have passed locally. "ERR:Env Env::close() failed: no response from server" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I took a pass and left a few comments.
My big concern is how sfAccount
is obfuscated by sfOnBehalfOf
. sfAccount
pretty much should never be used in the transactors with some exceptions. It's easy to miss these kind of errors where sfAccount
is used instead of the account
included in the context.
LEDGER_ENTRY(ltACCOUNT_PERMISSION, 0x0082, AccountPermission, ({ | ||
{sfAccount, soeREQUIRED}, | ||
{sfAuthorize, soeREQUIRED}, | ||
{sfPermissions, soeREQUIRED}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it make sense to make this optional since it could be empty?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since being empty means deleting all the permissions, can we keep it as required? So that the user won't delete the permissions by accident when they mistakenly leave out the Permissions field. Giving an empty field is more clear that they want to delete all the permissions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an on-ledger object, not the transaction. It's empty if there is no permissions so it could be optional. Can save a bit of space this way, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes. make sense.
/** This transaction type delegates authorized account specified permissions */ | ||
TRANSACTION(ttACCOUNT_PERMISSION_SET, 61, AccountPermissionSet, ({ | ||
{sfAuthorize, soeREQUIRED}, | ||
{sfPermissions, soeREQUIRED}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it make sense to make it optional since it could be empty?
include/xrpl/protocol/Permissions.h
Outdated
*/ | ||
|
||
enum GranularPermissionType : std::uint32_t { | ||
gpTrustlineAuthorize = 65537, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is gp
prefix necessary? Can we just have TrustlineAuthorize
, etc?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gp
is to indicate it is a granular permission set's member. I'm ok with either way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just deleted gp
src/libxrpl/protocol/Indexes.cpp
Outdated
@@ -77,6 +77,7 @@ enum class LedgerNameSpace : std::uint16_t { | |||
MPTOKEN_ISSUANCE = '~', | |||
MPTOKEN = 't', | |||
CREDENTIAL = 'D', | |||
ACCOUNT_PERMISSION = 'P', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This value is already used by DEPOSIT_PREAUTH_CREDENTIALS
{ | ||
auto permissionObj = dynamic_cast<STObject const*>(&permission); | ||
|
||
if (!permissionObj || (permissionObj->getFName() != sfPermission)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this needed? If the array is invalid it should fail in the array parsing. And if the name is not Permission
then it should fail also before it gets to the transactor. Should have a unit-test fo this.
src/xrpld/app/tx/detail/Clawback.cpp
Outdated
@@ -197,7 +197,7 @@ preclaimHelper<MPTIssue>( | |||
TER | |||
Clawback::preclaim(PreclaimContext const& ctx) | |||
{ | |||
AccountID const issuer = ctx.tx[sfAccount]; | |||
AccountID const issuer = ctx.account; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be const&. Please check everywhere where an alias variable is used for ctx.account
. It should be AccountID const&
.
if (ctx_.isDelegated && !ctx_.gpSet.empty()) | ||
{ | ||
// if gpSet is not empty, granular delegation is happening. | ||
if (bLock && ctx_.gpSet.find(gpMPTokenIssuanceLock) == ctx_.gpSet.end()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can simplify here and below.
if (bLock && !ctx.gpSet.contains(gpMPTokenIssuanceLock))
auto const amountIssue = dstAmount.issue(); | ||
if (isXRP(amountIssue)) | ||
return tecNO_AUTH; | ||
if (amountIssue.account == account_ && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can simplify here and below:
if (amountIssue.account == account_ &&
ctx_.gpSet.contains(gpPaymentMint))
src/xrpld/app/tx/detail/SetTrust.cpp
Outdated
if (bSetNoRipple || bClearNoRipple || bQualityIn || bQualityOut) | ||
return terNO_AUTH; | ||
if (bSetAuth && | ||
ctx_.gpSet.find(gpTrustlineAuthorize) == ctx_.gpSet.end()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can simplify here and below:
if (bSetAuth &&
!ctx_.gpSet.contains(gpTrustlineAuthorize))
@@ -458,6 +458,7 @@ LedgerEntryTypesMatch::visitEntry( | |||
switch (after->getType()) | |||
{ | |||
case ltACCOUNT_ROOT: | |||
case ltACCOUNT_PERMISSION: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't leave a comment on unchanged lines so commenting here for ValidClawback::finalize:924, which is this line:
AccountID const issuer = tx.getAccountID(sfAccount);
The issuer
in this case could be either sfAccount
or sfOnBehalfOf
, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is missed out. I'll also check all the other similar places which is missed out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gregtatcam
I also need to make change to AcceptedLedgerTx.cpp, LedgerToJson.cpp, and NetworkOPs.cpp, because they also check the account id when the transaction type is ttOFFER_CREATE. I'm working on this as well
87bae6a
to
b41a4cb
Compare
dc2671a
to
5f5b265
Compare
{ | ||
} | ||
|
||
STTx |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be STTx const&
} | ||
|
||
template <class T> | ||
typename std::enable_if< |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should use require
instead here and below.
std::unordered_map<GranularPermissionType, TxType> granularTxTypeMap; | ||
|
||
public: | ||
static Permission const& |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They should be also static. There should be only one instance of those maps.
{ | ||
return tx_; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it also makes sense to add implicit conversion operator to STTx const&
. Then don't need to call tx.getTx() in many cases. Can just pass tx.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And perhaps change getTx() to something else, maybe getSTTx? tx.getTx() doesn't look right.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The unit-tests are missing for AccountPermissionSet
, all delegated transactions, and all granular permissions.
// delegated by another account by checking if the sfOnBehalfOf field is | ||
// present. If it is present, we need to return the sfOnBehalfOf field as the | ||
// account when calling getAccountID(sfAccount) and tx[sfAccount]. | ||
class STTxWr |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about renaming to STTxDelegated
?
//============================================================================== | ||
|
||
#include <xrpl/protocol/Permissions.h> | ||
#include <xrpl/protocol/SField.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This and next include are not used.
@@ -206,7 +206,9 @@ fillJsonTx( | |||
if ((fill.options & LedgerFill::ownerFunds) && | |||
txn->getTxnType() == ttOFFER_CREATE) | |||
{ | |||
auto const account = txn->getAccountID(sfAccount); | |||
auto const& account = txn->isFieldPresent(sfOnBehalfOf) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you about adding getDelegatingAccountID()
to STTx
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we give it a more precise name like getDelegatingOrOriginalAccountID()
or getEffectiveAccountID()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getEffectiveAccountID()
sounds good.
auto pfresult = preflight(app, view.rules(), tx, flags, j); | ||
bool const isDelegated = view.rules().enabled(featureAccountPermission) && | ||
tx.isFieldPresent(sfOnBehalfOf); | ||
STTxWr txWr(tx, isDelegated); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think if STTxWr
is hidden even further? I.e. it can be created on a fly in *Context and *Result types and those type will have STTxWr const
data member instead of STTWr const&
. STTxWr
is a lightweight class and it's not a problem instantiating a few instances. preflight
, preclaim
, *Context, and *Result signatures don't change then.
AccountPermissionSet::preclaim(PreclaimContext const& ctx) | ||
{ | ||
auto const account = ctx.view.read(keylet::account(ctx.tx[sfAccount])); | ||
if (!account) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should use exists
instead and simplify:
if (!ctx.view.exists(keylet::account(ctx.tx[sfAccount]))
@@ -126,6 +130,10 @@ preflight1(PreflightContext const& ctx) | |||
ctx.tx.isFieldPresent(sfAccountTxnID)) | |||
return temINVALID; | |||
|
|||
if (!ctx.rules.enabled(featureAccountPermission) && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is already checked at the beginning of the function.
STTx const& tx, | ||
std::unordered_set<GranularPermissionType>& permissions) | ||
{ | ||
if (!view.read(keylet::account(tx[sfOnBehalfOf]))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be exists
instead.
Should we check if sfOnBehalfOf
is included in tx
? checkPermissions
is so far only called in delegated but still might make sense to check.
keylet::accountPermission(tx[sfOnBehalfOf], tx[sfAccount]); | ||
auto const sle = view.read(accountPermissionKey); | ||
if (!sle) | ||
return temMALFORMED; // todo: change error code |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change the error code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how about tecNO_PERMISSION
because the permission does not exist?
@@ -247,6 +300,21 @@ TER | |||
Transactor::payFee() | |||
{ | |||
auto const feePaid = ctx_.tx[sfFee].xrp(); | |||
if (ctx_.tx.isDelegated()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this needed? We always need the account sender to update the balance. I.e. it's always sfAccount
. And if it's not delegated then account_
is sfAccount
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed the check here. But I still need to add
if (!ctx_.tx.isDelegated()) mSourceBalance -= feePaid;
to keep mSourceBalance value precise.
@@ -834,6 +869,7 @@ doLedgerEntry(RPC::JsonContext& context) | |||
{jss::index, parseIndex, ltANY}, | |||
{jss::account_root, parseAccountRoot, ltACCOUNT_ROOT}, | |||
// TODO: add amendments | |||
{jss::amm, parseAccountPermission, ltACCOUNT_PERMISSION}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not amm
, is it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
forgot to change when copying :(
spec:
XRPLF/XRPL-Standards#217
XRPLF/XRPL-Standards#218
This PR includes the following changes:
to enable an account(delegating account) delegate some permissions to another account(delegated account), so that the delegated account can send transactions on behalf of the delegating account.
The AccountPermissionSet transaction will create AccountPermission ledger object, with keylet(delegating account, delegated account)
a. transaction level permission
b. granular permission which will be part of a transaction
more details:
account
andisDelegated
in PreflightContext, PreclaimContext and ApplyContext. Theaccount
is the account which is the transaction being operated on.account_
is updated to the account which is the transaction being operated on.gpSet
is added to ApplyContext. It contains the granular permissions enabled for that transaction. But if the transaction is fully delegated, then even there are granular permissions related to that transaction, the gpSet will be cleared up. To be more specific:a. isDelegated=true && gpSet not empty: only granular permissions delegated to the transaction
b. isDelegated=true && gpSet is empty: the transaction is fully delegated
Permissions.cpp
are supported, so the unauthorized granular operations and the other parts under these transactions are prohibited under granular permission mode.High Level Overview of Change
Context of Change
Type of Change
.gitignore
, formatting, dropping support for older tooling)API Impact
libxrpl
change (any change that may affectlibxrpl
or dependents oflibxrpl
)