diff --git a/_code-samples/delegate-permissions/README.md b/_code-samples/delegate-permissions/README.md new file mode 100644 index 0000000000..5c091e927c --- /dev/null +++ b/_code-samples/delegate-permissions/README.md @@ -0,0 +1,3 @@ +# Delegate Permissions + +Delegate permissions to another account, so that the account can send transactions on your behalf. diff --git a/_code-samples/delegate-permissions/js/README.md b/_code-samples/delegate-permissions/js/README.md new file mode 100644 index 0000000000..785166a81c --- /dev/null +++ b/_code-samples/delegate-permissions/js/README.md @@ -0,0 +1,30 @@ +# Delegate Permissions (JavaScript) Sample Code + +These code samples demonstrate how to delegate permissions to another account and how to send an a transaction as a delegate, using xrpl.js 4.3 in Node.js. + +## Usage + +1. Install dependencies. + + ```sh + npm i + ``` + +2. Run `delegate-permisions.js`. + + ```sh + node delegate-permissions.js + ``` + + If it runs successfully, it should output several things including "Delegate successfully set." followed by an [account_objects API method](https://xrpl.org/docs/references/http-websocket-apis/public-api-methods/account-methods/account_objects) response showing the delegate permissions. + + Take note of the **Delegator address** and **Delegate seed** from the output. + +3. Run `use-delegate-permissions.js` and provide both the delegator's address and the delegate's secret key that were output in the previous step. + + If it runs successfully, it should output various things ending in the following: + + ```text + Transaction successful. + Domain is example.com + ``` diff --git a/_code-samples/delegate-permissions/js/delegate-permissions.js b/_code-samples/delegate-permissions/js/delegate-permissions.js new file mode 100644 index 0000000000..4059c8ec67 --- /dev/null +++ b/_code-samples/delegate-permissions/js/delegate-permissions.js @@ -0,0 +1,55 @@ +const xrpl = require('xrpl') + +async function main() { + const client = new xrpl.Client("wss://s.devnet.rippletest.net:51233") + await client.connect() + + console.log("Funding new wallets from faucet...") + const delegator_wallet = (await client.fundWallet()).wallet + console.log("Delegator account:") + console.log(" Address:", delegator_wallet.address) + const delegate_wallet = (await client.fundWallet()).wallet + console.log("Delegate account:") + console.log(" Address:", delegate_wallet.address) + console.log(" Seed:", delegate_wallet.seed) + console.log("Please note these values for later.") + + // Define the transaction + const delegateset = { + "TransactionType": "DelegateSet", + "Account": delegator_wallet.address, + "Authorize": delegate_wallet.address, + "Permissions": [ + { + "Permission": { + "PermissionValue": "AccountDomainSet" + } + } + ] + } + + // Prepare, sign, and submit the transaction + const result = await client.submitAndWait(delegateset, { + wallet: delegator_wallet, + autofill: true + }) + + // Check transaction results + console.log(result) + if (result.result.meta.TransactionResult === "tesSUCCESS") { + console.log("Delegate successfully set.") + } + + // Confirm presence of Delegate ledger entry using account_objects + response = await client.request({ + "command": "account_objects", + "account": delegator_wallet.address, + "type": "delegate", + "ledger_index": "validated" + }) + console.log(JSON.stringify(response, null, 2)) + + client.disconnect() +} + +main() diff --git a/_code-samples/delegate-permissions/js/package.json b/_code-samples/delegate-permissions/js/package.json new file mode 100644 index 0000000000..9a27c14943 --- /dev/null +++ b/_code-samples/delegate-permissions/js/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "xrpl": "^4.3.0" + } +} diff --git a/_code-samples/delegate-permissions/js/use-delegated-permissions.js b/_code-samples/delegate-permissions/js/use-delegated-permissions.js new file mode 100644 index 0000000000..37fd7afadf --- /dev/null +++ b/_code-samples/delegate-permissions/js/use-delegated-permissions.js @@ -0,0 +1,80 @@ +const xrpl = require('xrpl') +const readline = require('readline').createInterface({ + input: process.stdin, + output: process.stdout +}) +const { stringToHex, hexToString } = require('@xrplf/isomorphic/utils') + +// Get delegator and delegate accounts from user input +readline.question(`Delegator's address? `, async function(delegator_address) { + readline.question(`Delegate's seed? `, async function(secret) { + const client = new xrpl.Client("wss://s.devnet.rippletest.net:51233") + await client.connect() + + const delegate_wallet = xrpl.Wallet.fromSeed(secret) + console.log(`Using delegate address ${delegate_wallet.address}`) + + // Check which permissions the delegate has been granted, if any + response = await client.request({ + "command": "account_objects", + "account": delegator_address, + "type": "delegate", + "ledger_index": "validated" + }) + let found_match = false + for (delegate_entry of response.result.account_objects) { + if (delegate_entry.Account == delegator_address && + delegate_entry.Authorize == delegate_wallet.address) { + + found_match = true + console.log("Delegate has the following permissions:") + for (perm of delegate_entry.Permissions) { + console.log(perm.Permission.PermissionValue) + } + break + } + } + if (!found_match) { + console.warn("Delegate appears not to have any permissions granted"+ + " by the delegator.") + console.warn("Make sure you ran delegate-permissions.js and input the"+ + " correct delegate/delegator values to this script.") + return + } + + // Use the AccountDomainSet granular permission to set the "Domain" field + // of the delegator + const set_domain_example = { + "TransactionType": "AccountSet", + "Account": delegator_address, + "Delegate": delegate_wallet.address, + "Domain": stringToHex("example.com") + } + + // Prepare, sign, and submit the transaction + console.log("Submitting transaction:") + console.log(set_domain_example) + const result = await client.submitAndWait(set_domain_example, { + wallet: delegate_wallet, + autofill: true + }) + + // Check transaction results and disconnect + console.log(result) + if (result.result.meta.TransactionResult === "tesSUCCESS") { + console.log("Transaction successful.") + } + + // Confirm that the account's Domain field has been set as expected + const acct_info_resp = await client.request({ + "command": "account_info", + "account": delegator_address, + "ledger_index": "validated" + }) + const domain_str = hexToString(acct_info_resp.result.account_data.Domain) + console.log(`Domain is ${domain_str}`) + + client.disconnect() + readline.close() + }) +}) diff --git a/docs/_snippets/common-links.md b/docs/_snippets/common-links.md index e8e4317696..2e2df23691 100644 --- a/docs/_snippets/common-links.md +++ b/docs/_snippets/common-links.md @@ -75,6 +75,10 @@ [DID entry]: /docs/references/protocol/ledger-data/ledger-entry-types/did.md [DeletableAccounts amendment]: /resources/known-amendments.md#deletableaccounts [DeepFreeze amendment]: /resources/known-amendments.md#deepfreeze +[Delegate ledger entry]: /docs/references/protocol/ledger-data/ledger-entry-types/delegate.md +[DelegateSet]: /docs/references/protocol/transactions/types/delegateset.md +[DelegateSet transaction]: /docs/references/protocol/transactions/types/delegateset.md +[DelegateSet transactions]: /docs/references/protocol/transactions/types/delegateset.md [DepositAuth amendment]: /resources/known-amendments.md#depositauth [DepositPreauth amendment]: /resources/known-amendments.md#depositpreauth [DepositPreauth entry]: /docs/references/protocol/transactions/types/depositpreauth.md @@ -125,6 +129,9 @@ [LedgerStateFix transactions]: /docs/references/protocol/transactions/types/ledgerstatefix.md [LedgerStateFix]: /docs/references/protocol/transactions/types/ledgerstatefix.md [Marker]: /docs/references/http-websocket-apis/api-conventions/markers-and-pagination.md +[MPTokenIssuanceSet]: /docs/references/protocol/transactions/types/mptokenissuanceset.md +[MPTokenIssuanceSet transaction]: /docs/references/protocol/transactions/types/mptokenissuanceset.md +[MPTokenIssuanceSet transactions]: /docs/references/protocol/transactions/types/mptokenissuanceset.md [MPTokensV1 amendment]: /resources/known-amendments.md#mptokensv1 [MultiSign amendment]: /resources/known-amendments.md#multisign [MultiSignReserve amendment]: /resources/known-amendments.md#multisignreserve @@ -402,6 +409,7 @@ [ripple_path_find command]: /docs/references/http-websocket-apis/public-api-methods/path-and-order-book-methods/ripple_path_find.md [ripple_path_find method]: /docs/references/http-websocket-apis/public-api-methods/path-and-order-book-methods/ripple_path_find.md [seconds since the Ripple Epoch]: /docs/references/protocol/data-types/basic-data-types.md#specifying-time +[server_definitions method]: /docs/references/http-websocket-apis/public-api-methods/server-info-methods/server_definitions.md [server_info command]: /docs/references/http-websocket-apis/public-api-methods/server-info-methods/server_info.md [server_info method]: /docs/references/http-websocket-apis/public-api-methods/server-info-methods/server_info.md [server_state command]: /docs/references/http-websocket-apis/public-api-methods/server-info-methods/server_state.md diff --git a/docs/concepts/accounts/deleting-accounts.md b/docs/concepts/accounts/deleting-accounts.md index b1a8414e43..3415119f5e 100644 --- a/docs/concepts/accounts/deleting-accounts.md +++ b/docs/concepts/accounts/deleting-accounts.md @@ -19,14 +19,22 @@ After an account has been deleted, it can be re-created in the ledger through th To be deleted, an account must meet the following requirements: - The account's `Sequence` number plus 256 must be less than the current [Ledger Index][]. -- The account must not be linked to any of the following types of [ledger entries](../../references/protocol/ledger-data/ledger-entry-types/index.md) (as a sender or receiver): - - `Escrow` - - `PayChannel` - - `RippleState` - - `Check` +- The account must not have any "deletion blockers" in its owner directory. This includes cases where the account is a sender _or_ receiver of funds. See below for a full list of deletion blockers. - The account must own fewer than 1000 objects in the ledger. - The transaction must pay a special [transaction cost][] equal to at least the [owner reserve](reserves.md) for one item (currently {% $env.PUBLIC_OWNER_RESERVE %}). +### Deletion Blockers + +The following [ledger entry types](../../references/protocol/ledger-data/ledger-entry-types/index.md) are deletion blockers: + +- `Escrow` +- `PayChannel` +- `RippleState` (trust line) +- `Check` +- `PermissionedDomain` _(Requires the [PermissionedDomains amendment][] {% not-enabled /%})_ + +Any other types of ledger entries that an account owns are automatically deleted along with the account. + ## Cost of Deleting {% admonition type="danger" name="Warning" %}The [AccountDelete transaction][]'s transaction cost always applies when the transaction is included in a validated ledger, even if the transaction failed because the account does not meet the requirements to be deleted. To greatly reduce the chances of paying the high transaction cost if the account cannot be deleted, use the `fail_hard` option when submitting an AccountDelete transaction.{% /admonition %} diff --git a/docs/concepts/accounts/permission-delegation.md b/docs/concepts/accounts/permission-delegation.md index 0ac735b126..d2181b16d3 100644 --- a/docs/concepts/accounts/permission-delegation.md +++ b/docs/concepts/accounts/permission-delegation.md @@ -7,102 +7,67 @@ labels: status: not_enabled --- # Permission Delegation -XRPL accounts can delegate both transaction permissions and granular permissions to other accounts, enhancing flexibility and enabling use cases such as implementing role-based access control. This delegation is managed using the [`DelegateSet`](../../references/protocol/transactions/types/delegateset.md) transaction. -_(Requires the [PermissionDelegation amendment][] {% not-enabled /%})_ +Permission delegation is the function of granting various permissions to another account to send permissions on behalf of your account. You can use permission delegation to enable flexible security paradigms such as role-based access control, instead of or alongside techniques such as [multi-signing](./multi-signing.md). -## Assigning Permissions +_(Requires the [PermissionDelegation amendment][] {% not-enabled /%}.)_ -You can assign permissions to another account by submitting a `DelegateSet` transaction. -```json -tx_json = { - "TransactionType": "DelegateSet", - "Account": "rDelegatingAccount", - "Authorize": "rDelegatedAccount", - "Permissions": [ - { - "Permission": { - "PermissionValue": "Payment" - } - } - ] -} -``` +## Background -| Field | Description | -|-------|-------------| -| `Account` | The address of the account that is delegating the permission(s). | -| `Authorize` | The address of the account that is being granted the permission(s). | -| `Permissions` | An array of permission objects, specifying the permissions to delegate. Each permission is defined within a `Permission` object, using the `PermissionValue` field. See [XLS-74d, Account Permissions] for a complete list of valid `PermissionValues`. | +Managing your [cryptographic keys](./cryptographic-keys.md) is one of the more challenging parts of using a blockchain. As part of a defense-in-depth strategy, a secure configuration should limit the damage that can occur if a secret key is compromised. One way to do this is to rotate keys regularly and to keep master keys off of computers that are always connected to the internet and serving user traffic. However, many use cases involve frequently and automatically signing transactions, which typically requires having secret keys on an internet-connected server. -## Revoking Permissions +Permission Delegation can reduce this problem by granting very limited permissions to separate accounts that have their keys available online for day-to-day tasks. Meanwhile, the keys with full control over the account can be kept offline, so that you only use them for special tasks, like issuing tokens. This is especially helpful when using compliance features like [Authorized Trust Lines](../tokens/fungible-tokens/authorized-trust-lines.md) that require a stablecoin issuer to individually approve each user after meeting regulatory requirements like Know Your Customer rules. With a proper configuration, you can minimize the consequences of a delegate's keys being compromized. -Permissions can be revoked using the `DelegateSet` transaction. There are two ways to revoke permissions: -### Revoke All Permissions +## How It Works -To revoke all permissions previously granted to a delegated account, send a `DelegateSet` transaction with an empty `Permissions` array: +The account on whose behalf transactions are being sent is called the _delegator_. The account sending the transactions is called the _delegate_. -```json -tx_json = { - "TransactionType": "DelegateSet", - "Account": "rDelegatingAccount", - "Authorize": "rDelegatedAccount", - "Permissions": [] -} -``` +The delegator first sends a [DelegateSet transaction][] to designate an account as its delegate and to specify which permissions the delegate has. The delegator can update or revoke the permissions at any time by sending another DelegateSet transaction. A delegator can have more than one delegate, and can grant different sets of permissions to each delegate. -### Revoke Specific Permissions +A delegate can send transactions that execute as if they were sent by the delegator. These transactions specify both the delegator's information as well as the address of the delegate who is sending the transaction. The delegate can sign these transactions with any of the following: -To revoke specific permissions, send a `DelegateSet` transaction that includes _only_ the permissions that should remain active. Any permissions previously granted to the `Authorize` account that aren't included in the `Permissions` array are revoked. +- The delegate's master key pair +- A regular key pair that the delegate has authorized +- A multi-signing list that the delegate has authorized -## Sending Transactions with Delegated Permissions +The delegate can only send transactions that match the permissions it has. Permissions come in two types: -When an account has been granted permissions, it can send transactions on behalf of the delegating account using the `Delegate` field. +- **Transaction Type Permissions** - Permission to send transactions of a specific [transaction type](/docs/references/protocol/transactions/types/index.md). Some types cannot be delegated. +- **Granular Permissions** - Permission to send transactions with a specific subset of functionality. -For example, if `rDelegatingAccount` has delegated the `TrustSet` permission to `rDelegatedAccount`, then `rDelegatedAccount` can submit a `TrustSet` transaction on behalf of `rDelegatingAccount` as follows: +For a complete list of transaction types that can or cannot be delegated as well as a list of granular permissions, see [Permission Values](/docs/references/protocol/data-types/permission-values.md). -```json -transaction_json = { - "TransactionType": "TrustSet", - "Account": "rDelegatingAccount", - "Delegate": "rDelegatedAccount", - "LimitAmount": { - "currency": "USD", - "issuer": "rIssuerAccount", - "value": "1000" - } -} -``` +### Limitations -| Field | Description | -|-------|-------------| -| `Account` | The address of the account that granted permission for the transaction (the _delegating_ account). | -| `Delegate` | The address of the account submitting and signing the transaction. This must be the account that was granted permission (the _delegated_ account). | +The main limiting factor on how many delegates you can have is that you must hold enough XRP to meet the [reserve requirement](./reserves.md). Each delegate's permissions are tracked with a [Delegate ledger entry][], which counts as one item towards the delegator's owner reserve. -The account that sends this transaction is _rDelegatedAccount_, although the Account field is the _rDelegatingAccount_. The secret for this transaction is the _rDelegatedAccount_ secret, which means _rDelegatedAccount_ signs the transaction. +Each delegate can be granted up to 10 permissions. -## Error Cases +Some permissions cannot be delegated, especially permissions that would allow the delegate to change cryptographic keys or grant additional permissions. -- If the `PermissionDelegation` feature is not enabled, return `temDISABLED`. +The available set of granular permissions is hard-coded, and the permissions cannot be customized. For example, you cannot grant permission to send only certain currencies and not others. -- If the _rDelegatedAccount_ is not authorized by the _rDelegatingAccount_ for the transaction type or satisfying the granular permissions given by _rDelegatingAccount_, the transaction returns `tecNO_DELEGATE_PERMISSION`. +## Comparison with Multi-Signing -- If the _rDelegatedAccount_ does not have enough balance to pay the transaction fee, the transaction returns `terINSUF_FEE_B` . (_rDelegatedAccount_ pays the fee, which is the sender in `Delegate` field, not the `Account` field). +Permission delegation is similar to multi-signing in that it allows other key pairs to sign transactions that "come from" your account. However, there are key differences in functionality between the two, as summarized in the following table: -- If the transaction creates a ledger object, but _rDelegatingAccount_ does not have enough balance to cover the reserve, the transaction returns `tecINSUFFICIENT_RESERVE`. +| | Permission Delegation | Multi-Signing | +|------------------|-----------------------|---------------| +| Transaction cost | Paid by the delegate | Paid by the account that owns the list | +| Permission control | Can only send transactions matching specific permissions granted | Can send any transactions except [specific cases that require the master key pair](./cryptographic-keys.md#special-permissions) | +| M-of-N permission | Not supported | Configurable quorum and weights with up to 32 signers | +| Unfunded accounts | Delegates must have funded accounts on ledger | Signers can be funded accounts or key pairs with no account on ledger. | +| Key management | Delegate manages their own keys, including multi-signing | Signers with funded accounts can manage their own keys but cannot perform nested multi-signing. | -- If the key used to sign this account does not match with _rDelegatedAccount_, the transaction returns `rpcBAD_SECRET`. -- If the `TradingFee` is invalid (non-XRP currency or negative value), return `temBAD_FEE`. +## See Also -Any other errors are the same as when the _rDelegatingAccount_ sends transaction for itself. - -{% admonition type="warning" name="Important" %} -* Delegating permissions grants significant control. Ensure you trust the delegated account. -* The account specified in the `Delegate` field is responsible for paying the transaction fee. -* A delegated account can only perform actions that have been explicitly permitted. -{% /admonition %} +- **References:** + - [DelegateSet transaction][] - Grant, update, or revoke permissions to a specific delegate. + - [Delegate ledger entry][] - Data structure on the ledger that records which permissions have been granted. +- **Code Samples:** + - {% repo-link path="_code-samples/delegate-permissions/" %}**Delegate Permissions**{% /repo-link %} {% raw-partial file="/docs/_snippets/common-links.md" /%} diff --git a/docs/references/protocol/data-types/permission-values.md b/docs/references/protocol/data-types/permission-values.md new file mode 100644 index 0000000000..02b5306ea1 --- /dev/null +++ b/docs/references/protocol/data-types/permission-values.md @@ -0,0 +1,84 @@ +--- +seo: + description: Format for permissions that can be granted to other accounts. +label: + - Permissions +--- +# Permission Values + +[Permission delegation](/docs/concepts/accounts/permission-delegation.md) defines permissions that can be granted to other accounts. These permissions fall into the following categories: + +- **Transaction Type Permissions** - Permission to send transactions with the specified [transaction type](../transactions/types/index.md). +- **Granular Permissions** - Permission to send transactions with a specific subset of functionality. + +_(Requires the [PermissionDelegation amendment][] {% not-enabled /%}.)_ + +## Numeric and String Values + +In the [canonical binary format](../binary-format.md) for transactions and ledger data, permission values are stored in a numeric form (specifically, as a 32-bit unsigned integer). However, in JSON they can be specified and returned in string format for convenience, similar to how transaction type names (`TransactionType` fields) work. + +When specifying a permission value in JSON, you can use either the numeric value or the string value. When serving data, the server supplies the string value if it is known, and falls back to the numeric value otherwise. + +{% admonition type="warning" name="Caution" %} +Not all client libraries support numeric PermissionValue types. In most cases, you should use the string names of the permissions you want to grant. +{% /admonition %} + +- For **transaction type permissions**, the string is the name of the transaction type exactly (case-sensitive). For example, a permission value of `"PaymentChannelClaim"` grants permission to send [PaymentChannelClaim transactions][]. +- For **granular permissions**, the string is the name of the granular permission (case-sensitive). For example, a permission value of `"TrustlineAuthorize"` grants permission to send TrustSet transactions that authorize trust lines (but not ones that modify other settings such as the trust line limit or freeze status). + +The numeric value `0` is reserved for "full permissions", meaning permission to send transactions of all types, but it is not possible to delegate full permissions. + +## Transaction Type Permissions + +Transaction Type Permissions have numeric values from 1 to 65536 (that is, 216), inclusive. They correspond with known transaction types, except you add 1 when specifying a transaction type as a permission value. For example, the string `"Payment"` corresponds to a `TransactionType` value of `0`, but a `PermissionValue` value of `1`. To grant permissions to make Payment transactions, you can specify either `"PermissionValue": "Payment"` or `"PermissionValue": 1`. + +For a mapping of transaction types known by a server and their corresponding numeric transaction type values, check the `TRANSACTION_TYPES` field in the [server_definitions method][]. + +### List of Non-Delegatable Permissions + +Some transaction types can't be delegated. If you attempt to grant these permissions to a delegate, the transaction fails with a [result code](../transactions/transaction-results/) such as `tecNO_PERMISSION`. This includes all transaction types that can be used to grant other permissions to different key pairs or accounts. Additionally, all [pseudo-transaction types](/docs/references/protocol/transactions/pseudo-transaction-types/pseudo-transaction-types) can't be delegated since they can't be sent by normal accounts anyway. + +The following permissions cannot be delegated: + +| Transaction Type | Permission Value | +|:--------------------|:-----------------| +| [AccountSet][] | `4` | +| [SetRegularKey][] | `6` | +| [SignerListSet][] | `13` | +| [AccountDelete][] | `22` | +| [LedgerStateFix][] | `54` | +| [DelegateSet][] | `65` | +| [EnableAmendment][] | `101` | +| [SetFee][] | `102` | +| [UNLModify][] | `103` | + +{% admonition type="warning" name="Known Issue" %} +With only the PermissionDelegation amendment, it's possible to assign permissions for transaction types that are reserved, unassigned, or part of amendments that are not currently enabled; it's also possible to assign PermissionValue `0` for full permissions. However, these values do not actually grant any permissions. This is a bug, and a future amendment will prevent assigning values outside of currently-enabled, delegatable transaction types or known granular permissions. +{% /admonition %} + +## Granular Permissions +[[Source]](https://github.com/XRPLF/rippled/blob/master/include/xrpl/protocol/detail/permissions.macro "Source") + +Granular Permissions have numeric types of 65537 and up, corresponding to specific names of permissions. Values that are not defined are not allowed. Each granular permission is a subset of a single transaction type's functionality. + +| Numeric Value | Name | Transaction Type | Description | +|:--------------|:-------------------------|:-----------------------|:------------| +| `65537` | `TrustlineAuthorize` | [TrustSet][] | Can [authorize individual trust lines](/docs/concepts/tokens/fungible-tokens/authorized-trust-lines). | +| `65538` | `TrustlineFreeze` | [TrustSet][] | Can [freeze individual trust lines](/docs/concepts/tokens/fungible-tokens/freezes). | +| `65539` | `TrustlineUnfreeze` | [TrustSet][] | Can [unfreeze individual trust lines](/docs/concepts/tokens/fungible-tokens/freezes). | +| `65540` | `AccountDomainSet` | [AccountSet][] | Can set the `Domain` field of the account. | +| `65541` | `AccountEmailHashSet` | [AccountSet][] | Can set the `EmailHash` field of the account. | +| `65542` | `AccountMessageKeySet` | [AccountSet][] | Can set the `MessageKey` field of the account. | +| `65543` | `AccountTransferRateSet` | [AccountSet][] | Can set the [transfer fee of fungible tokens issued by the account](/docs/concepts/tokens/transfer-fees). | +| `65544` | `AccountTickSizeSet` | [AccountSet][] | Can set the [tick size of fungible tokens issued by the account](/docs/concepts/tokens/decentralized-exchange/ticksize). | +| `65545` | `PaymentMint` | [Payment][] | Can send payments that mint new fungible tokens or MPTs. | +| `65546` | `PaymentBurn` | [Payment][] | Can send payments that burn fungible tokens or MPTs. | +| `65547` | `MPTokenIssuanceLock` | [MPTokenIssuanceSet][] | Can lock the balances of a particular MPT issued by the account. _(Requires the [MPTokensV1 amendment][] {% not-enabled /%}.)_ | +| `65548` | `MPTokenIssuanceUnlock` | [MPTokenIssuanceSet][] | Can unlock the balances of a particular MPT issued by the account. _(Requires the [MPTokensV1 amendment][] {% not-enabled /%}.)_ | + +### Limitations to Granular Permissions + +The set of granular permissions is hard-coded. No custom configurations are allowed. For example, you cannot add permissions based on specific currencies. Adding a new granular permission requires an amendment. + + +{% raw-partial file="/docs/_snippets/common-links.md" /%} diff --git a/docs/references/protocol/ledger-data/ledger-entry-types/delegate.md b/docs/references/protocol/ledger-data/ledger-entry-types/delegate.md new file mode 100644 index 0000000000..3ef3f17828 --- /dev/null +++ b/docs/references/protocol/ledger-data/ledger-entry-types/delegate.md @@ -0,0 +1,68 @@ +--- +seo: + description: A record of which permissions have been granted to another account. +labels: + - Accounts + - Permissions +--- +# Delegate +[[Source]](https://github.com/XRPLF/rippled/blob/1e01cd34f7a216092ed779f291b43324c167167a/include/xrpl/protocol/detail/ledger_entries.macro#L475-L482 "Source") + +A `Delegate` ledger entry stores a set of permissions that an account has delegated to another account. You create a `Delegate` entry by sending a [DelegateSet transaction][]. + +_(Requires the [PermissionDelegation amendment][] {% not-enabled /%}.)_ + +## Example {% $frontmatter.seo.title %} JSON + +```json +{ + "Account": "rG8uoRH9uA6AJ6NRj8P4cJG1HNfYcnMPrt", + "Authorize": "r9GAKojMTyexqvy8DXFWYq63Mod5k5wnkT", + "Flags": 0, + "LedgerEntryType": "Delegate", + "OwnerNode": "0", + "Permissions": [ + { + "Permission": { + "PermissionValue": "AccountDomainSet" + } + } + ], + "PreviousTxnID": "08DB1BD6ECFC9E8CBD8D954F4EFF6EFD155A392C5060D767B5621CE18951983A", + "PreviousTxnLgrSeq": 4748731, + "index": "749D3DCDF9F032DDDB8AC49641BACBFDD398C4B6C231C4AB325B7755962329A2" +} +``` + +## {% $frontmatter.seo.title %} Fields + +In addition to the [common fields](../common-fields.md), {% code-page-name /%} entries have the following fields: + +| Field | JSON Type | [Internal Type][] | Required? | Description | +|:--------------------|:---------------------|:------------------|:----------|:-------------| +| `Account` | String - [Address][] | AccountID | Yes | The account delegating permissions to another, also called the _delegating account_. | +| `Authorize` | String - [Address][] | AccountID | Yes | The account receiving permissions, also called the _delegate_. | +| `Permissions` | Array | Array | Yes | A list of permissions granted, with at least 1 and at most 10 items. Each item in the list is a [Permission Object](#permission-objects). | +| `OwnerNode` | String - Hexadecimal | UInt64 | Yes | A hint indicating which page of the delegating account's owner directory links to this object, in case the directory consists of multiple pages. +| `PreviousTxnID` | String - Hexadecimal | UInt256 | Yes | The identifying hash of the transaction that most recently modified this object. | +| `PreviousTxnLgrSeq` | Number | UInt32 | Yes |The [index of the ledger][Ledger Index] that contains the transaction that most recently modified this object. | + +### Permission Objects + +Each item in the `Permissions` array is an inner object with the following nested field: + +| Field | JSON Type | [Internal Type][] | Required? | Description | +|:------------------|:---------------------|:------------------|:----------|:----------------| +| `PermissionValue` | String or Number | UInt32 | Yes | A permission that has been granted to the delegate, which can be either a transaction type or a granular permission. See [Permission Values](../../data-types/permission-values.md) for a full list. | + +## {% $frontmatter.seo.title %} Flags + +There are no flags defined for {% code-page-name /%} entries. + +## {% $frontmatter.seo.title %} Reserve + +{% code-page-name /%} entries count as one item towards the owner reserve of the delegating account, as long as the entry is in the ledger, regardless of how many permissions are delegating. Removing all permissions deletes the entry and frees up the reserve. + +{% code-page-name /%} entries are not deletion blockers. If the owner (delegating) account is deleted, all such ledger entries are deleted along with them. However, the `Authorize` + +{% raw-partial file="/docs/_snippets/common-links.md" /%} diff --git a/docs/references/protocol/transactions/common-fields.md b/docs/references/protocol/transactions/common-fields.md index 651d5f3425..7ec6bdc52c 100644 --- a/docs/references/protocol/transactions/common-fields.md +++ b/docs/references/protocol/transactions/common-fields.md @@ -62,43 +62,14 @@ The [`Paths` field](types/payment.md#paths) of the [Payment transaction][] type ## Delegate -The `Delegate` ledger object stores a set of permissions that an XRPL account has delegated to another account. You create `Delegate` objects using the [`DelegateSet`](./types/delegateset.md) transaction. +If the `Delegate` field is provided, this transaction is being sent by a different account on behalf of the account in the `Account` field. The account in the `Account` is the _delegating account_ and the account in the `Delegate` field is the _delegate_ account. The transaction functions as if it was sent by the delegating account, with the following exceptions: -### Structure +- The signature must be valid for the delegate account. (It can by signed with a master key, regular key, or multi-signing list that is authorized by the delegate.) +- The transaction cost (in the `Fee` field) is paid by the delegate account. -A `Delegate` object has the following fields: +Sending a transaction this way is only possible if the delegating account has granted the appropriate transaction permissions to the delegate account. For more information, see [Permission Delegation](/docs/concepts/accounts/permission-delegation.md). -| Field Name | Required? | JSON Type | Internal Type | Description | -|------------|-----------|-----------|---------------|-------------| -| `LedgerIndex` | ✔️ | string | Hash256 | The unique ID of the ledger object. | -| `LedgerEntryType` | ✔️ | string | UInt16 | The ledger object's type (`Delegate`) | -| `Account` | ✔️ | string | AccountID | The account that delegates permissions to another account. | -| `Authorize` | ✔️ | string | AccountID | The account to which permissions are delegated. | -| `Permissions` | ✔️ | string | STArray | The transaction permissions that the `Authorize` account has been granted. | -| `OwnerNode` | ✔️ | string | UInt64 | A hint indicating which page of the sender's owner directory links to this object, in case the directory consists of multiple pages. | -| `PreviousTxnID` | ✔️ | string | Hash256 | The identifying hash of the transaction that most recently modified this object. | -| `PreviousTxnLgrSeqNumber`| ✔️ | number | UInt32 |The index of the ledger that contains the transaction that most recently modified this object. | - -### Retrieving Delegate Objects - -You can retrieve `Delegate` ledger objects using the `ledger_entry` RPC method. The unique ID of a `Delegate` object is a hash of the `Account` and `Authorize` fields, combined with the unique space key for Delegate objects. - -### Account Deletion - -A `Delegate` object is not a deletion blocker. This means that deleting an account removes any `Delegate` objects associated with it. - -### Example Delegate JSON - -This sample `Delegate` object shows that the _rISAAC_ account has delegated `TrustLineAuthorize` permission to the _rKYLIE_ account. - -```json -{ - "LedgerEntryType": "Delegate", - "Account": "rISAAC......", - "Authorize": "rKYLIE......", - "Permissions": [{"Permission": {"PermissionValue": "TrustlineAuthorize"}}], -} -``` +_(Requires the [PermissionDelegation amendment][] {% not-enabled /%}.)_ ## Flags Field diff --git a/docs/references/protocol/transactions/types/delegateset.md b/docs/references/protocol/transactions/types/delegateset.md index e04e46f1f5..a28af70af2 100644 --- a/docs/references/protocol/transactions/types/delegateset.md +++ b/docs/references/protocol/transactions/types/delegateset.md @@ -7,139 +7,71 @@ labels: - Delegate status: not_enabled --- +# DelegateSet +[[Source]](https://github.com/XRPLF/rippled/blob/1e01cd34f7a216092ed779f291b43324c167167a/src/xrpld/app/tx/detail/DelegateSet.cpp "Source") -# DelegateSet -The `DelegateSet` transaction creates, modifies, or deletes a `Delegate` ledger object, thereby granting, changing, or revoking delegated permissions between accounts. +[Delegate permissions](/docs/concepts/accounts/permission-delegation) to another account to send transactions on your behalf. This transaction type can grant, change, or revoke permissions; it creates, modifies, or deletes a [Delegate ledger entry][] accordingly. _(Requires the [PermissionDelegation amendment][] {% not-enabled /%}.)_ -## Example `DelegateSet` JSON - -```json -{ - "TransactionType": "DelegateSet", - "Account": "rDelegatingAccount", - "Authorize": "rDelegatedAccount", - "Permissions": [ - { - "Permission": { - "PermissionValue": "Payment" - } - }, - { - "Permission": { - "PermissionValue": "TrustSet" - } - } - ] -} -``` -## `DelegateSet` Fields - -In addition to the common fields, `DelegateSet` transactions have the following fields: - -| Field | Required? | JSON Type | Internal Type | Description | -|-------|-----------|-----------|---------------|-------------| -| `TransactionType` | Yes | string | UInt16 | The transaction type (DelegateSet). | -| `Account` | Yes | string | AccountID | The address of the account that is delegating the permission(s). | -| `Authorize`| Yes | string | AccountID | The address of the account that is being granted the permission(s). -| `Permissions` | Yes | string | STArray | An array of permission objects. Each object contains a `Permission` object with a `PermissionValue` field specifying the permission being granted. To modify permissions, include all desired permissions in the `Permissions` array. Omitted permissions are revoked. | - -## Updating Permissions - -Sending a new `DelegateSet` with the same `Account` and `Authorize` fields updates and replaces the permission list. - - -## Revoking Permissions - -Permissions are revoked using the `DelegateSet` transaction by specifying only the desired permissions and omitting any previous permissions that are no longer needed. - -### Revoke All Permissions - -To revoke all permissions, send a `DelegateSet` transaction with an empty `Permissions` array: +## Example {% $frontmatter.seo.title %} JSON ```json { - "TransactionType": "DelegateSet", - "Account": "rDelegatingAccount", - "Authorize": "rDelegatedAccount", - "Permissions": [] + "TransactionType": "DelegateSet", + "Account": "rw81qtsfF9rws4RbmYepf5394gp81TQv5Y", + "Authorize": "r9GAKojMTyexqvy8DXFWYq63Mod5k5wnkT", + "Fee": "1", + "Flags": 0, + "LastLedgerSequence": 4747822, + "Permissions": [ + { + "Permission": { + "PermissionValue": "AccountDomainSet" + } + } + ], + "Sequence": 4747802 } ``` -### Revoke Specific Permissions +{% tx-example txid="13E1C2CE2BCECB6223AEA39407169C5429FE9A126825CDD6952E3FF4C728F603" server="devnet" /%} -To revoke specific permissions, include only the permissions that should remain active in the `Permissions` array. +{% raw-partial file="/docs/_snippets/tx-fields-intro.md" /%} -## Security +| Field | Required? | JSON Type | Internal Type | Description | +|:--------------|-----------|----------------------|---------------|-------------| +| `Authorize` | Yes | String - [Address][] | AccountID | The account being granted permissions, also called the _delegate_. | +| `Permissions` | Yes | Array | Array | A list of up to 10 [Permission objects](#permission-objects) each specifying a different permission granted to the delegate. The delegate's permissions are updated to match this set of permissions exactly. To revoke all permissions, use an empty array. | -Giving permissions to other parties requires a high degree of trust, especially when the delegated account can potentially access funds (the `Payment` permission) or charge reserves (any transaction that can create objects). In addition, any account that has permissions for the entire `AccountSet`, `SetRegularKey`, or `SignerListSet` transactions can give themselves any permissions even if this was not originally part of the intention. +If a [Delegate ledger entry][] does not exist to record the granted permissions, this transaction creates one. If it already exists, the transaction updates the set of permissions to match the list in the transaction: any permissions not listed are revoked. If all permissions are revoked, the transaction deletes the Delegate ledger entry. -With granular permissions, however, users can give permissions to other accounts for only parts of transactions without giving them full control. This is especially helpful for managing complex transaction types like `AccountSet`. +{% admonition type="success" name="Tip" %} +If you want to delegate more than 10 permissions, consider using [multi-signing](/docs/concepts/accounts/multi-signing.md) instead. +{% /admonition %} -### Granular Permissions +### Permission Objects -These permissions support control over some smaller portion of a transaction, rather than being able to do all of the functionality that the transaction allows. +Each item in the `Permissions` array is an inner object with the following nested field: -These permissions fall into the gap between the size of the `UInt16` and the `UInt32` (the size of the `SignerListID` field). +| Field | JSON Type | [Internal Type][] | Required? | Description | +|:------------------|:---------------------|:------------------|:----------|:----------------| +| `PermissionValue` | String or Number | UInt32 | Yes | A permission to grant to the delegate, which can be either a transaction type or a granular permission. See [Permission Values](../../data-types/permission-values.md) for a full list. | -| Value | Name | Description | -|-------|-------|-------------| -|`65537`|`TrustlineAuthorize`|Authorize a trustline.| -|`65538`|`TrustlineFreeze`|Freeze a trustline.| -|`65539`|`TrustlineUnfreeze`|Unfreeze a trustline.| -|`65540`|`AccountDomainSet`|Modify the domain of an account.| -|`65541`|`AccountEmailHashSet`|Modify the `EmailHash` of an account.| -|`65542`|`AccountMessageKeySet`|Modify the `MessageKey` of an account.| -|`65543`|`AccountTransferRateSet`|Modify the transfer rate of an account.| -|`65544`|`AccountTickSizeSet`|Modify the tick size of an account.| -|`65545`|`PaymentMint`|Send a payment for a currency where the sending account is the issuer.| -|`65546`|`PaymentBurn`|Send a payment for a currency where the destination account is the issuer.| -|`65547`|`MPTokenIssuanceLock`|Use the `MPTIssuanceSet` transaction to lock (freeze) a holder.| -|`65548`|`MPTokenIssuanceUnlock`|Use the `MPTIssuanceSet` transaction to unlock (unfreeze) a holder.| - -For example, if an account is authorized by `TrustlineFreeze`, it can freeze a trust line by sending a `TrustSet` transaction. However, since it is only authorized to freeze trust lines, it cannot perform other `TrustSet` operations such as unfreezing a trust line, setting No Ripple, applying Deep Freeze, etc. -When an account is authorized by both `TrustlineFreeze` and `TrustSet`, the delegation is still valid, but the granular permission `TrustlineFreeze` has no effect, since the account is already permitted to perform all actions under `TrustSet`. - -For multi-signing a delegation transaction, which is sent by a delegated account, the multi signers must be the delegated account's signers instead of the delegating account's multi signers. - -### Limitations to Granular Permissions - -The set of permissions must be hard-coded. No custom configurations are allowed. For example, you cannot add permissions based on specific currencies. - -In addition, each permission needs to be implemented on its own in the source code. Adding a new permission requires an amendment. - - -## Failure Conditions - -The `DelegateSet` transaction fails if: - -- The `Permissions` array contains more than 10 entries. -- The `Permissions` array contains duplicate entries. -- Any of the specified `PermissionValues` are invalid. -- The `Authorize` account does not exist. - -## State Changes - -A successful `DelegateSet` transaction results in the creation, modification, or deletion of a `Delegate` ledger object. - -- If no `Delegate` object exists for the given `Account` and `Authorize` pair, a new one is created. -- If a `Delegate` object already exists, its `Permissions` field is updated. -- If the `Permissions` array is empty, the `Delegate` object is deleted. ## Error Cases -- If the `Account` is the same as `Authorize`, return `temMALFORMED`. +Besides errors that can occur for all transactions, {% $frontmatter.seo.title %} transactions can result in the following [transaction result codes](../transaction-results/index.md): -- If the `Authorize` account does not exist, return `tecNO_TARGET`. +| Error Code | Description | +|:--------------------------|:------------| +| `tecDIR_FULL` | The sender owns too many items in the ledger already. | +| `tecINSUFFICIENT_RESERVE` | The sender does not have enough XRP to meet the [reserve requirement](/docs/concepts/accounts/reserves.md) of creating a new Delegate ledger entry. | +| `tecNO_PERMISSION` | At least one permission in the `Permissions` list is not delegatable. See [Permission Values](../../data-types/permission-values.md) for which permissions are not delegatable. | +| `tecNO_TARGET` | The account specified in the `Authorize` field does not exist in the ledger. | +| `temARRAY_TOO_LARGE` | The `Permissions` list is too large. It cannot contain more than 10 entries. | +| `temDISABLED` | The [Permission Delegation amendment][] is not enabled. | +| `temMALFORMED` | The transaction was invalid. For example, the `Authorize` account is the same as the sender of the transaction, the `Permissions` list contains duplicate entries, or one of the permissions in the list is not a valid permission. | -- If the `Permissions` list size exceeds 10, return `temARRAY_TOO_LARGE`. - -- If `Permissions` contains a duplicate value, return `temMALFORMED`. - -- If `Permissions` contains transactions that are disabled for delegation, return `tecNO_PERMISSION`. -The transactions disabled for delegation include: `AccountSet`, `RegularKeySet`, `SignerListSet`, `AccountDelete`, `DelegateSet`, `EnableAmendment`, `SetFee`, `UNLModify`, `LedgerStateFix`. - -- If the Account does not have enough balance to meet the reserve requirement, (because `DelegateSet` will create a ledger object `ltDELEGATE`, whose owner is `Account`), return `tecINSUFFICIENT_RESERVE`. {% raw-partial file="/docs/_snippets/common-links.md" /%} diff --git a/sidebars.yaml b/sidebars.yaml index 809873e7de..a176098a19 100644 --- a/sidebars.yaml +++ b/sidebars.yaml @@ -336,6 +336,7 @@ - page: docs/references/protocol/data-types/base58-encodings.md - page: docs/references/protocol/data-types/currency-formats.md - page: docs/references/protocol/data-types/nftoken.md + - page: docs/references/protocol/data-types/permission-values.md - page: docs/references/protocol/ledger-data/index.md expanded: false items: @@ -350,6 +351,7 @@ - page: docs/references/protocol/ledger-data/ledger-entry-types/bridge.md - page: docs/references/protocol/ledger-data/ledger-entry-types/check.md - page: docs/references/protocol/ledger-data/ledger-entry-types/credential.md + - page: docs/references/protocol/ledger-data/ledger-entry-types/delegate.md - page: docs/references/protocol/ledger-data/ledger-entry-types/depositpreauth.md - page: docs/references/protocol/ledger-data/ledger-entry-types/did.md - page: docs/references/protocol/ledger-data/ledger-entry-types/directorynode.md