Skip to content

Commit

Permalink
PermissionedDomain XLS-80d (#2874)
Browse files Browse the repository at this point in the history
  • Loading branch information
ckeshava authored Feb 7, 2025
1 parent ce5ca31 commit 189abc1
Show file tree
Hide file tree
Showing 23 changed files with 496 additions and 14 deletions.
1 change: 1 addition & 0 deletions .ci-config/rippled.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -189,3 +189,4 @@ fixInnerObjTemplate2
fixEnforceNFTokenTrustline
fixReducedOffersV2
DynamicNFT
PermissionedDomains
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ From the top-level xrpl.js folder (one level above `packages`), run the followin
```bash
npm install
# sets up the rippled standalone Docker container - you can skip this step if you already have it set up
docker run -p 6006:6006 --rm -it --name rippled_standalone --volume $PWD/.ci-config:/etc/opt/ripple/ --entrypoint bash rippleci/rippled:2.3.0-rc1 -c 'rippled -a'
docker run -p 6006:6006 --rm -it --name rippled_standalone --volume $PWD/.ci-config:/etc/opt/ripple/ --entrypoint bash rippleci/rippled:develop -c 'rippled -a'
npm run build
npm run test:integration
```
Expand All @@ -76,7 +76,7 @@ Breaking down the command:
`--name rippled_standalone` is an instance name for clarity
* `--volume $PWD/.ci-config:/etc/opt/ripple/` identifies the `rippled.cfg` and `validators.txt` to import. It must be an absolute path, so we use `$PWD` instead of `./`.
* `rippleci/rippled` is an image that is regularly updated with the latest `rippled` releases
* `--entrypoint bash rippleci/rippled:2.3.0-rc1` manually overrides the entrypoint (for versions of rippled >= 2.3.0)
* `--entrypoint bash rippleci/rippled:develop` manually overrides the entrypoint (for the latest version of rippled on the `develop` branch)
* `-c 'rippled -a'` provides the bash command to start `rippled` in standalone mode from the manual entrypoint

### Browser Tests
Expand All @@ -92,7 +92,7 @@ This should be run from the `xrpl.js` top level folder (one above the `packages`
```bash
npm run build
# sets up the rippled standalone Docker container - you can skip this step if you already have it set up
docker run -p 6006:6006 --rm -it --name rippled_standalone --volume $PWD/.ci-config:/etc/opt/ripple/ --entrypoint bash rippleci/rippled:2.3.0-rc1 -c 'rippled -a'
docker run -p 6006:6006 --rm -it --name rippled_standalone --volume $PWD/.ci-config:/etc/opt/ripple/ --entrypoint bash rippleci/rippled:develop -c 'rippled -a'
npm run test:browser
```

Expand Down
22 changes: 22 additions & 0 deletions packages/ripple-binary-codec/src/enums/definitions.json
Original file line number Diff line number Diff line change
Expand Up @@ -1250,6 +1250,16 @@
"type": "Hash256"
}
],
[
"DomainID",
{
"nth": 34,
"isVLEncoded": false,
"isSerialized": true,
"isSigningField": true,
"type": "Hash256"
}
],
[
"hash",
{
Expand Down Expand Up @@ -2530,6 +2540,15 @@
"type": "STArray"
}
],
[
"AcceptedCredentials", {
"nth": 28,
"isVLEncoded": false,
"isSerialized": true,
"isSigningField": true,
"type": "STArray"
}
],
[
"CloseResolution",
{
Expand Down Expand Up @@ -2863,6 +2882,7 @@
"Oracle": 128,
"Credential": 129,
"PayChannel": 120,
"PermissionedDomain": 130,
"RippleState": 114,
"SignerList": 83,
"Ticket": 84,
Expand Down Expand Up @@ -3094,6 +3114,8 @@
"PaymentChannelClaim": 15,
"PaymentChannelCreate": 13,
"PaymentChannelFund": 14,
"PermissionedDomainSet": 62,
"PermissionedDomainDelete": 63,
"SetFee": 101,
"SetRegularKey": 5,
"SignerListSet": 12,
Expand Down
7 changes: 5 additions & 2 deletions packages/xrpl/HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ Subscribe to [the **xrpl-announce** mailing list](https://groups.google.com/g/xr

### Added
* Adds utility function `convertTxFlagsToNumber`
* Implementation of XLS-80d PermissionedDomain feature.
* Support for the `simulate` RPC ([XLS-69](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0069-simulate))

### Changed
* Deprecated `setTransactionFlagsToNumber`. Start using convertTxFlagsToNumber instead

### Added
* Support for the `simulate` RPC ([XLS-69](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0069-simulate))
### Fixed
* Include `network_id` field in the `server_state` response interface.


## 4.1.0 (2024-12-23)

Expand Down
3 changes: 3 additions & 0 deletions packages/xrpl/src/models/ledger/LedgerEntry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import NegativeUNL from './NegativeUNL'
import Offer from './Offer'
import Oracle from './Oracle'
import PayChannel from './PayChannel'
import PermissionedDomain from './PermissionedDomain'
import RippleState from './RippleState'
import SignerList from './SignerList'
import Ticket from './Ticket'
Expand All @@ -35,6 +36,7 @@ type LedgerEntry =
| Offer
| Oracle
| PayChannel
| PermissionedDomain
| RippleState
| SignerList
| Ticket
Expand All @@ -61,6 +63,7 @@ type LedgerEntryFilter =
| 'offer'
| 'oracle'
| 'payment_channel'
| 'permissioned_domain'
| 'signer_list'
| 'state'
| 'ticket'
Expand Down
29 changes: 29 additions & 0 deletions packages/xrpl/src/models/ledger/PermissionedDomain.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { AuthorizeCredential } from '../common'

import { BaseLedgerEntry, HasPreviousTxnID } from './BaseLedgerEntry'

export default interface PermissionedDomain
extends BaseLedgerEntry,
HasPreviousTxnID {
/* The ledger object's type (PermissionedDomain). */
LedgerEntryType: 'PermissionedDomain'

/* The account that controls the settings of the domain. */
Owner: string

/* The credentials that are accepted by the domain.
Ownership of one of these credentials automatically
makes you a member of the domain. */
AcceptedCredentials: AuthorizeCredential[]

/* Flag values associated with this object. */
Flags: 0

/* Owner account's directory page containing the PermissionedDomain object. */
OwnerNode: string

/* The Sequence value of the PermissionedDomainSet
transaction that created this domain. Used in combination
with the Account to identify this domain. */
Sequence: number
}
1 change: 1 addition & 0 deletions packages/xrpl/src/models/methods/serverState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export interface ServerStateResponse extends BaseResponse {
load_factor_fee_queue?: number
load_factor_fee_reference?: number
load_factor_server?: number
network_id: number
peer_disconnects?: string
peer_disconnects_resources?: string
peers: number
Expand Down
2 changes: 2 additions & 0 deletions packages/xrpl/src/models/transactions/accountDelete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
validateCredentialsList,
validateOptionalField,
validateRequiredField,
MAX_AUTHORIZED_CREDENTIALS,
} from './common'

/**
Expand Down Expand Up @@ -54,5 +55,6 @@ export function validateAccountDelete(tx: Record<string, unknown>): void {
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- known from base check
tx.TransactionType as string,
true,
MAX_AUTHORIZED_CREDENTIALS,
)
}
58 changes: 49 additions & 9 deletions packages/xrpl/src/models/transactions/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ import {
AuthorizeCredential,
Currency,
IssuedCurrencyAmount,
MPTAmount,
Memo,
Signer,
XChainBridge,
MPTAmount,
} from '../common'
import { onlyHasFields } from '../utils'

const MEMO_SIZE = 3
const MAX_CREDENTIALS_LIST_LENGTH = 8
export const MAX_AUTHORIZED_CREDENTIALS = 8
const MAX_CREDENTIAL_BYTE_LENGTH = 64
const MAX_CREDENTIAL_TYPE_LENGTH = MAX_CREDENTIAL_BYTE_LENGTH * 2

Expand Down Expand Up @@ -134,7 +134,9 @@ export function isIssuedCurrency(
* @param input - The input to check the form and type of
* @returns Whether the AuthorizeCredential is properly formed
*/
function isAuthorizeCredential(input: unknown): input is AuthorizeCredential {
export function isAuthorizeCredential(
input: unknown,
): input is AuthorizeCredential {
return (
isRecord(input) &&
isRecord(input.Credential) &&
Expand Down Expand Up @@ -455,13 +457,16 @@ export function validateCredentialType(tx: Record<string, unknown>): void {
* @param credentials An array of credential IDs to check for errors
* @param transactionType The transaction type to include in error messages
* @param isStringID Toggle for if array contains IDs instead of AuthorizeCredential objects
* @param maxCredentials The maximum length of the credentials array.
* PermissionedDomainSet transaction uses 10, other transactions use 8.
* @throws Validation Error if the formatting is incorrect
*/
// eslint-disable-next-line max-lines-per-function -- separating logic further will add unnecessary complexity
// eslint-disable-next-line max-lines-per-function, max-params -- separating logic further will add unnecessary complexity
export function validateCredentialsList(
credentials: unknown,
transactionType: string,
isStringID: boolean,
maxCredentials: number,
): void {
if (credentials == null) {
return
Expand All @@ -471,9 +476,9 @@ export function validateCredentialsList(
`${transactionType}: Credentials must be an array`,
)
}
if (credentials.length > MAX_CREDENTIALS_LIST_LENGTH) {
if (credentials.length > maxCredentials) {
throw new ValidationError(
`${transactionType}: Credentials length cannot exceed ${MAX_CREDENTIALS_LIST_LENGTH} elements`,
`${transactionType}: Credentials length cannot exceed ${maxCredentials} elements`,
)
} else if (credentials.length === 0) {
throw new ValidationError(
Expand All @@ -500,7 +505,42 @@ export function validateCredentialsList(
}
}

function containsDuplicates(objectList: object[]): boolean {
const objSet = new Set(objectList.map((obj) => JSON.stringify(obj)))
return objSet.size !== objectList.length
// Type guard to ensure we're working with AuthorizeCredential[]
// Note: This is not a rigorous type-guard. A more thorough solution would be to iterate over the array and check each item.
function isAuthorizeCredentialArray(
list: AuthorizeCredential[] | string[],
): list is AuthorizeCredential[] {
return typeof list[0] !== 'string'
}

/**
* Check if an array of objects contains any duplicates.
*
* @param objectList - Array of objects to check for duplicates
* @returns True if duplicates exist, false otherwise
*/
export function containsDuplicates(
objectList: AuthorizeCredential[] | string[],
): boolean {
// Case-1: Process a list of string-IDs
if (typeof objectList[0] === 'string') {
const objSet = new Set(objectList.map((obj) => JSON.stringify(obj)))
return objSet.size !== objectList.length
}

// Case-2: Process a list of nested objects
const seen = new Set<string>()

if (isAuthorizeCredentialArray(objectList)) {
for (const item of objectList) {
const key = `${item.Credential.Issuer}-${item.Credential.CredentialType}`
// eslint-disable-next-line max-depth -- necessary to check for type-guards
if (seen.has(key)) {
return true
}
seen.add(key)
}
}

return false
}
3 changes: 3 additions & 0 deletions packages/xrpl/src/models/transactions/depositPreauth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
BaseTransaction,
validateBaseTransaction,
validateCredentialsList,
MAX_AUTHORIZED_CREDENTIALS,
} from './common'

/**
Expand Down Expand Up @@ -72,13 +73,15 @@ export function validateDepositPreauth(tx: Record<string, unknown>): void {
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- confirmed in base transaction check
tx.TransactionType as string,
false,
MAX_AUTHORIZED_CREDENTIALS,
)
} else if (tx.UnauthorizeCredentials !== undefined) {
validateCredentialsList(
tx.UnauthorizeCredentials,
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- confirmed in base transaction check
tx.TransactionType as string,
false,
MAX_AUTHORIZED_CREDENTIALS,
)
}
}
Expand Down
2 changes: 2 additions & 0 deletions packages/xrpl/src/models/transactions/escrowFinish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
validateBaseTransaction,
validateCredentialsList,
validateRequiredField,
MAX_AUTHORIZED_CREDENTIALS,
} from './common'

/**
Expand Down Expand Up @@ -55,6 +56,7 @@ export function validateEscrowFinish(tx: Record<string, unknown>): void {
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- known from base check
tx.TransactionType as string,
true,
MAX_AUTHORIZED_CREDENTIALS,
)

if (tx.OfferSequence == null) {
Expand Down
3 changes: 3 additions & 0 deletions packages/xrpl/src/models/transactions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,6 @@ export {
XChainModifyBridgeFlags,
XChainModifyBridgeFlagsInterface,
} from './XChainModifyBridge'

export { PermissionedDomainSet } from './permissionedDomainSet'
export { PermissionedDomainDelete } from './permissionedDomainDelete'
2 changes: 2 additions & 0 deletions packages/xrpl/src/models/transactions/payment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
isNumber,
Account,
validateCredentialsList,
MAX_AUTHORIZED_CREDENTIALS,
} from './common'
import type { TransactionMetadataBase } from './metadata'

Expand Down Expand Up @@ -188,6 +189,7 @@ export function validatePayment(tx: Record<string, unknown>): void {
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- known from base check
tx.TransactionType as string,
true,
MAX_AUTHORIZED_CREDENTIALS,
)

if (tx.InvoiceID !== undefined && typeof tx.InvoiceID !== 'string') {
Expand Down
2 changes: 2 additions & 0 deletions packages/xrpl/src/models/transactions/paymentChannelClaim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
GlobalFlags,
validateBaseTransaction,
validateCredentialsList,
MAX_AUTHORIZED_CREDENTIALS,
} from './common'

/**
Expand Down Expand Up @@ -153,6 +154,7 @@ export function validatePaymentChannelClaim(tx: Record<string, unknown>): void {
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- known from base check
tx.TransactionType as string,
true,
MAX_AUTHORIZED_CREDENTIALS,
)

if (tx.Channel === undefined) {
Expand Down
28 changes: 28 additions & 0 deletions packages/xrpl/src/models/transactions/permissionedDomainDelete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {
BaseTransaction,
isString,
validateBaseTransaction,
validateRequiredField,
} from './common'

export interface PermissionedDomainDelete extends BaseTransaction {
/* The transaction type (PermissionedDomainDelete). */
TransactionType: 'PermissionedDomainDelete'

/* The domain to delete. */
DomainID: string
}

/**
* Verify the form and type of a PermissionedDomainDelete transaction.
*
* @param tx - The transaction to verify.
* @throws When the transaction is malformed.
*/
export function validatePermissionedDomainDelete(
tx: Record<string, unknown>,
): void {
validateBaseTransaction(tx)

validateRequiredField(tx, 'DomainID', isString)
}
Loading

0 comments on commit 189abc1

Please sign in to comment.