mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-04 21:15:47 +00:00
PermissionedDomain XLS-80d (#2874)
This commit is contained in:
committed by
GitHub
parent
ce5ca316ca
commit
189abc1a26
@@ -189,3 +189,4 @@ fixInnerObjTemplate2
|
||||
fixEnforceNFTokenTrustline
|
||||
fixReducedOffersV2
|
||||
DynamicNFT
|
||||
PermissionedDomains
|
||||
|
||||
@@ -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
|
||||
```
|
||||
@@ -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
|
||||
@@ -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
|
||||
```
|
||||
|
||||
|
||||
@@ -1250,6 +1250,16 @@
|
||||
"type": "Hash256"
|
||||
}
|
||||
],
|
||||
[
|
||||
"DomainID",
|
||||
{
|
||||
"nth": 34,
|
||||
"isVLEncoded": false,
|
||||
"isSerialized": true,
|
||||
"isSigningField": true,
|
||||
"type": "Hash256"
|
||||
}
|
||||
],
|
||||
[
|
||||
"hash",
|
||||
{
|
||||
@@ -2530,6 +2540,15 @@
|
||||
"type": "STArray"
|
||||
}
|
||||
],
|
||||
[
|
||||
"AcceptedCredentials", {
|
||||
"nth": 28,
|
||||
"isVLEncoded": false,
|
||||
"isSerialized": true,
|
||||
"isSigningField": true,
|
||||
"type": "STArray"
|
||||
}
|
||||
],
|
||||
[
|
||||
"CloseResolution",
|
||||
{
|
||||
@@ -2863,6 +2882,7 @@
|
||||
"Oracle": 128,
|
||||
"Credential": 129,
|
||||
"PayChannel": 120,
|
||||
"PermissionedDomain": 130,
|
||||
"RippleState": 114,
|
||||
"SignerList": 83,
|
||||
"Ticket": 84,
|
||||
@@ -3094,6 +3114,8 @@
|
||||
"PaymentChannelClaim": 15,
|
||||
"PaymentChannelCreate": 13,
|
||||
"PaymentChannelFund": 14,
|
||||
"PermissionedDomainSet": 62,
|
||||
"PermissionedDomainDelete": 63,
|
||||
"SetFee": 101,
|
||||
"SetRegularKey": 5,
|
||||
"SignerListSet": 12,
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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'
|
||||
@@ -35,6 +36,7 @@ type LedgerEntry =
|
||||
| Offer
|
||||
| Oracle
|
||||
| PayChannel
|
||||
| PermissionedDomain
|
||||
| RippleState
|
||||
| SignerList
|
||||
| Ticket
|
||||
@@ -61,6 +63,7 @@ type LedgerEntryFilter =
|
||||
| 'offer'
|
||||
| 'oracle'
|
||||
| 'payment_channel'
|
||||
| 'permissioned_domain'
|
||||
| 'signer_list'
|
||||
| 'state'
|
||||
| 'ticket'
|
||||
|
||||
29
packages/xrpl/src/models/ledger/PermissionedDomain.ts
Normal file
29
packages/xrpl/src/models/ledger/PermissionedDomain.ts
Normal file
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
validateCredentialsList,
|
||||
validateOptionalField,
|
||||
validateRequiredField,
|
||||
MAX_AUTHORIZED_CREDENTIALS,
|
||||
} from './common'
|
||||
|
||||
/**
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) &&
|
||||
@@ -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
|
||||
@@ -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(
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
BaseTransaction,
|
||||
validateBaseTransaction,
|
||||
validateCredentialsList,
|
||||
MAX_AUTHORIZED_CREDENTIALS,
|
||||
} from './common'
|
||||
|
||||
/**
|
||||
@@ -72,6 +73,7 @@ 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(
|
||||
@@ -79,6 +81,7 @@ 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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
validateBaseTransaction,
|
||||
validateCredentialsList,
|
||||
validateRequiredField,
|
||||
MAX_AUTHORIZED_CREDENTIALS,
|
||||
} from './common'
|
||||
|
||||
/**
|
||||
@@ -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) {
|
||||
|
||||
@@ -106,3 +106,6 @@ export {
|
||||
XChainModifyBridgeFlags,
|
||||
XChainModifyBridgeFlagsInterface,
|
||||
} from './XChainModifyBridge'
|
||||
|
||||
export { PermissionedDomainSet } from './permissionedDomainSet'
|
||||
export { PermissionedDomainDelete } from './permissionedDomainDelete'
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
isNumber,
|
||||
Account,
|
||||
validateCredentialsList,
|
||||
MAX_AUTHORIZED_CREDENTIALS,
|
||||
} from './common'
|
||||
import type { TransactionMetadataBase } from './metadata'
|
||||
|
||||
@@ -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') {
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
GlobalFlags,
|
||||
validateBaseTransaction,
|
||||
validateCredentialsList,
|
||||
MAX_AUTHORIZED_CREDENTIALS,
|
||||
} from './common'
|
||||
|
||||
/**
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import { AuthorizeCredential } from '../common'
|
||||
|
||||
import {
|
||||
BaseTransaction,
|
||||
isString,
|
||||
validateBaseTransaction,
|
||||
validateOptionalField,
|
||||
validateRequiredField,
|
||||
validateCredentialsList,
|
||||
} from './common'
|
||||
|
||||
const MAX_ACCEPTED_CREDENTIALS = 10
|
||||
|
||||
export interface PermissionedDomainSet extends BaseTransaction {
|
||||
/* The transaction type (PermissionedDomainSet). */
|
||||
TransactionType: 'PermissionedDomainSet'
|
||||
|
||||
/* The domain to modify. Must be included if modifying an existing domain. */
|
||||
DomainID?: string
|
||||
|
||||
/* The credentials that are accepted by the domain. Ownership of one
|
||||
of these credentials automatically makes you a member of the domain.
|
||||
An empty array means deleting the field. */
|
||||
AcceptedCredentials: AuthorizeCredential[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a PermissionedDomainSet transaction.
|
||||
*
|
||||
* @param tx - The transaction to validate.
|
||||
* @throws {ValidationError} When the transaction is invalid.
|
||||
*/
|
||||
export function validatePermissionedDomainSet(
|
||||
tx: Record<string, unknown>,
|
||||
): void {
|
||||
validateBaseTransaction(tx)
|
||||
|
||||
validateOptionalField(tx, 'DomainID', isString)
|
||||
validateRequiredField(
|
||||
tx,
|
||||
'AcceptedCredentials',
|
||||
() => tx.AcceptedCredentials instanceof Array,
|
||||
)
|
||||
|
||||
validateCredentialsList(
|
||||
tx.AcceptedCredentials,
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- known from base check
|
||||
tx.TransactionType as string,
|
||||
// PermissionedDomainSet uses AuthorizeCredential nested objects only, strings are not allowed
|
||||
false,
|
||||
// PermissionedDomainSet uses at most 10 accepted credentials. This is different from Credential-feature transactions.
|
||||
MAX_ACCEPTED_CREDENTIALS,
|
||||
)
|
||||
}
|
||||
@@ -75,6 +75,14 @@ import {
|
||||
PaymentChannelFund,
|
||||
validatePaymentChannelFund,
|
||||
} from './paymentChannelFund'
|
||||
import {
|
||||
PermissionedDomainDelete,
|
||||
validatePermissionedDomainDelete,
|
||||
} from './permissionedDomainDelete'
|
||||
import {
|
||||
PermissionedDomainSet,
|
||||
validatePermissionedDomainSet,
|
||||
} from './permissionedDomainSet'
|
||||
import { SetFee } from './setFee'
|
||||
import { SetRegularKey, validateSetRegularKey } from './setRegularKey'
|
||||
import { SignerListSet, validateSignerListSet } from './signerListSet'
|
||||
@@ -153,6 +161,8 @@ export type SubmittableTransaction =
|
||||
| PaymentChannelClaim
|
||||
| PaymentChannelCreate
|
||||
| PaymentChannelFund
|
||||
| PermissionedDomainSet
|
||||
| PermissionedDomainDelete
|
||||
| SetRegularKey
|
||||
| SignerListSet
|
||||
| TicketCreate
|
||||
@@ -415,6 +425,14 @@ export function validate(transaction: Record<string, unknown>): void {
|
||||
validatePaymentChannelFund(tx)
|
||||
break
|
||||
|
||||
case 'PermissionedDomainSet':
|
||||
validatePermissionedDomainSet(tx)
|
||||
break
|
||||
|
||||
case 'PermissionedDomainDelete':
|
||||
validatePermissionedDomainDelete(tx)
|
||||
break
|
||||
|
||||
case 'SetRegularKey':
|
||||
validateSetRegularKey(tx)
|
||||
break
|
||||
|
||||
@@ -125,6 +125,7 @@ describe('server_info (rippled)', function () {
|
||||
'build_version',
|
||||
'node_size',
|
||||
'initial_sync_duration_us',
|
||||
'network_id',
|
||||
'git',
|
||||
]
|
||||
assert.deepEqual(
|
||||
|
||||
@@ -60,6 +60,7 @@ describe('server_state', function () {
|
||||
load_factor_fee_queue: 256,
|
||||
load_factor_fee_reference: 256,
|
||||
load_factor_server: 256,
|
||||
network_id: 63456,
|
||||
peer_disconnects: '0',
|
||||
peer_disconnects_resources: '0',
|
||||
peers: 0,
|
||||
@@ -117,6 +118,7 @@ describe('server_state', function () {
|
||||
'initial_sync_duration_us',
|
||||
'ports',
|
||||
'git',
|
||||
'network_id',
|
||||
]
|
||||
assert.deepEqual(
|
||||
omit(response.result.state, removeKeys),
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
import { stringToHex } from '@xrplf/isomorphic/utils'
|
||||
import { assert } from 'chai'
|
||||
|
||||
import {
|
||||
LedgerEntryRequest,
|
||||
PermissionedDomainDelete,
|
||||
PermissionedDomainSet,
|
||||
AuthorizeCredential,
|
||||
} from '../../../src'
|
||||
import PermissionedDomain from '../../../src/models/ledger/PermissionedDomain'
|
||||
import serverUrl from '../serverUrl'
|
||||
import {
|
||||
setupClient,
|
||||
teardownClient,
|
||||
type XrplIntegrationTestContext,
|
||||
} from '../setup'
|
||||
import { testTransaction } from '../utils'
|
||||
|
||||
// how long before each test case times out
|
||||
const TIMEOUT = 20000
|
||||
|
||||
describe('PermissionedDomainSet', function () {
|
||||
let testContext: XrplIntegrationTestContext
|
||||
|
||||
beforeEach(async () => {
|
||||
testContext = await setupClient(serverUrl)
|
||||
})
|
||||
afterEach(async () => teardownClient(testContext))
|
||||
|
||||
it(
|
||||
'Lifecycle of PermissionedDomain ledger object',
|
||||
async () => {
|
||||
const sampleCredential: AuthorizeCredential = {
|
||||
Credential: {
|
||||
CredentialType: stringToHex('Passport'),
|
||||
Issuer: testContext.wallet.classicAddress,
|
||||
},
|
||||
}
|
||||
|
||||
// Step-1: Test the PermissionedDomainSet transaction
|
||||
const pdSet: PermissionedDomainSet = {
|
||||
TransactionType: 'PermissionedDomainSet',
|
||||
Account: testContext.wallet.classicAddress,
|
||||
AcceptedCredentials: [sampleCredential],
|
||||
}
|
||||
|
||||
await testTransaction(testContext.client, pdSet, testContext.wallet)
|
||||
|
||||
// Step-2: Validate the ledger_entry, account_objects RPC methods
|
||||
// validate the account_objects RPC
|
||||
const result = await testContext.client.request({
|
||||
command: 'account_objects',
|
||||
account: testContext.wallet.classicAddress,
|
||||
type: 'permissioned_domain',
|
||||
})
|
||||
|
||||
assert.equal(result.result.account_objects.length, 1)
|
||||
const pd = result.result.account_objects[0] as PermissionedDomain
|
||||
|
||||
assert.equal(pd.Flags, 0)
|
||||
expect(pd.AcceptedCredentials).toEqual([sampleCredential])
|
||||
|
||||
// validate the ledger_entry RPC
|
||||
const ledgerEntryRequest: LedgerEntryRequest = {
|
||||
command: 'ledger_entry',
|
||||
// fetch the PD `index` from the previous account_objects RPC response
|
||||
index: pd.index,
|
||||
}
|
||||
const ledgerEntryResult = await testContext.client.request(
|
||||
ledgerEntryRequest,
|
||||
)
|
||||
assert.deepEqual(pd, ledgerEntryResult.result.node)
|
||||
|
||||
// Step-3: Test the PDDelete transaction
|
||||
const pdDelete: PermissionedDomainDelete = {
|
||||
TransactionType: 'PermissionedDomainDelete',
|
||||
Account: testContext.wallet.classicAddress,
|
||||
// fetch the PD `index` from the previous account_objects RPC response
|
||||
DomainID: pd.index,
|
||||
}
|
||||
|
||||
await testTransaction(testContext.client, pdDelete, testContext.wallet)
|
||||
},
|
||||
TIMEOUT,
|
||||
)
|
||||
})
|
||||
49
packages/xrpl/test/models/permissionedDomainDelete.test.ts
Normal file
49
packages/xrpl/test/models/permissionedDomainDelete.test.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { assert } from 'chai'
|
||||
|
||||
import { validate, ValidationError } from '../../src'
|
||||
import { validatePermissionedDomainDelete } from '../../src/models/transactions/permissionedDomainDelete'
|
||||
|
||||
/**
|
||||
* PermissionedDomainDelete Transaction Verification Testing.
|
||||
*
|
||||
* Providing runtime verification testing for each specific transaction type.
|
||||
*/
|
||||
describe('PermissionedDomainDelete', function () {
|
||||
let tx
|
||||
|
||||
beforeEach(function () {
|
||||
tx = {
|
||||
TransactionType: 'PermissionedDomainDelete',
|
||||
Account: 'rfmDuhDyLGgx94qiwf3YF8BUV5j6KSvE8',
|
||||
DomainID:
|
||||
'D88930B33C2B6831660BFD006D91FF100011AD4E67CBB78B460AF0A215103737',
|
||||
} as any
|
||||
})
|
||||
|
||||
it('verifies valid PermissionedDomainDelete', function () {
|
||||
assert.doesNotThrow(() => validatePermissionedDomainDelete(tx))
|
||||
assert.doesNotThrow(() => validate(tx))
|
||||
})
|
||||
|
||||
it(`throws w/ missing field DomainID`, function () {
|
||||
delete tx.DomainID
|
||||
const errorMessage = 'PermissionedDomainDelete: missing field DomainID'
|
||||
assert.throws(
|
||||
() => validatePermissionedDomainDelete(tx),
|
||||
ValidationError,
|
||||
errorMessage,
|
||||
)
|
||||
assert.throws(() => validate(tx), ValidationError, errorMessage)
|
||||
})
|
||||
|
||||
it(`throws w/ invalid DomainID`, function () {
|
||||
tx.DomainID = 1234
|
||||
const errorMessage = 'PermissionedDomainDelete: invalid field DomainID'
|
||||
assert.throws(
|
||||
() => validatePermissionedDomainDelete(tx),
|
||||
ValidationError,
|
||||
errorMessage,
|
||||
)
|
||||
assert.throws(() => validate(tx), ValidationError, errorMessage)
|
||||
})
|
||||
})
|
||||
92
packages/xrpl/test/models/permissionedDomainSet.test.ts
Normal file
92
packages/xrpl/test/models/permissionedDomainSet.test.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import { stringToHex } from '@xrplf/isomorphic/dist/utils'
|
||||
import { assert } from 'chai'
|
||||
|
||||
import { AuthorizeCredential, validate, ValidationError } from '../../src'
|
||||
|
||||
/**
|
||||
* PermissionedDomainSet Transaction Verification Testing.
|
||||
*
|
||||
* Providing runtime verification testing for each specific transaction type.
|
||||
*/
|
||||
describe('PermissionedDomainSet', function () {
|
||||
let tx
|
||||
const sampleCredential: AuthorizeCredential = {
|
||||
Credential: {
|
||||
CredentialType: stringToHex('Passport'),
|
||||
Issuer: 'rfmDuhDyLGgx94qiwf3YF8BUV5j6KSvE8',
|
||||
},
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
tx = {
|
||||
TransactionType: 'PermissionedDomainSet',
|
||||
Account: 'rfmDuhDyLGgx94qiwf3YF8BUV5j6KSvE8',
|
||||
DomainID:
|
||||
'D88930B33C2B6831660BFD006D91FF100011AD4E67CBB78B460AF0A215103737',
|
||||
AcceptedCredentials: [sampleCredential],
|
||||
} as any
|
||||
})
|
||||
|
||||
it('verifies valid PermissionedDomainSet', function () {
|
||||
assert.doesNotThrow(() => validate(tx))
|
||||
})
|
||||
|
||||
it(`throws with invalid field DomainID`, function () {
|
||||
// DomainID is expected to be a string
|
||||
tx.DomainID = 1234
|
||||
const errorMessage = 'PermissionedDomainSet: invalid field DomainID'
|
||||
assert.throws(() => validate(tx), ValidationError, errorMessage)
|
||||
})
|
||||
|
||||
it(`throws w/ missing field AcceptedCredentials`, function () {
|
||||
delete tx.AcceptedCredentials
|
||||
const errorMessage =
|
||||
'PermissionedDomainSet: missing field AcceptedCredentials'
|
||||
assert.throws(() => validate(tx), ValidationError, errorMessage)
|
||||
})
|
||||
|
||||
it('throws when AcceptedCredentials exceeds maximum length', function () {
|
||||
tx.AcceptedCredentials = Array(11).fill(sampleCredential)
|
||||
assert.throws(
|
||||
() => validate(tx),
|
||||
ValidationError,
|
||||
'PermissionedDomainSet: Credentials length cannot exceed 10 elements',
|
||||
)
|
||||
})
|
||||
|
||||
it('throws when AcceptedCredentials is empty', function () {
|
||||
tx.AcceptedCredentials = []
|
||||
assert.throws(
|
||||
() => validate(tx),
|
||||
ValidationError,
|
||||
'PermissionedDomainSet: Credentials cannot be an empty array',
|
||||
)
|
||||
})
|
||||
|
||||
it('throws when AcceptedCredentials is not an array type', function () {
|
||||
tx.AcceptedCredentials = 'AcceptedCredentials is not an array'
|
||||
assert.throws(
|
||||
() => validate(tx),
|
||||
ValidationError,
|
||||
'PermissionedDomainSet: invalid field AcceptedCredentials',
|
||||
)
|
||||
})
|
||||
|
||||
it('throws when AcceptedCredentials contains duplicates', function () {
|
||||
tx.AcceptedCredentials = [sampleCredential, sampleCredential]
|
||||
assert.throws(
|
||||
() => validate(tx),
|
||||
ValidationError,
|
||||
'PermissionedDomainSet: Credentials cannot contain duplicate elements',
|
||||
)
|
||||
})
|
||||
|
||||
it('throws when AcceptedCredentials contains invalid format', function () {
|
||||
tx.AcceptedCredentials = [{ Field1: 'Value1', Field2: 'Value2' }]
|
||||
assert.throws(
|
||||
() => validate(tx),
|
||||
ValidationError,
|
||||
'PermissionedDomainSet: Invalid Credentials format',
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -13,7 +13,9 @@ import {
|
||||
TrustSet,
|
||||
TrustSetFlags,
|
||||
} from '../../src'
|
||||
import { AuthorizeCredential } from '../../src/models/common'
|
||||
import { AccountRootFlags } from '../../src/models/ledger'
|
||||
import { containsDuplicates } from '../../src/models/transactions/common'
|
||||
import { isFlagEnabled } from '../../src/models/utils'
|
||||
import {
|
||||
setTransactionFlagsToNumber,
|
||||
@@ -28,6 +30,43 @@ import {
|
||||
* Provides tests for utils used in models.
|
||||
*/
|
||||
describe('Models Utils', function () {
|
||||
describe('validate containsDuplicates utility method', function () {
|
||||
it(`use nested-objects for input parameters, list contains duplicates`, function () {
|
||||
// change the order of the inner-objects in the list
|
||||
const list_with_duplicates: AuthorizeCredential[] = [
|
||||
{ Credential: { Issuer: 'alice', CredentialType: 'Passport' } },
|
||||
{ Credential: { CredentialType: 'Passport', Issuer: 'alice' } },
|
||||
]
|
||||
|
||||
assert.isTrue(containsDuplicates(list_with_duplicates))
|
||||
})
|
||||
|
||||
it(`use nested-objects for input parameters, no duplicates`, function () {
|
||||
const list_without_dups: AuthorizeCredential[] = [
|
||||
{ Credential: { Issuer: 'alice', CredentialType: 'Passport' } },
|
||||
{ Credential: { CredentialType: 'DMV_license', Issuer: 'bob' } },
|
||||
]
|
||||
|
||||
assert.isFalse(containsDuplicates(list_without_dups))
|
||||
})
|
||||
|
||||
it(`use string-IDs for input parameters`, function () {
|
||||
const list_without_dups: string[] = [
|
||||
'EA85602C1B41F6F1F5E83C0E6B87142FB8957BD209469E4CC347BA2D0C26F66A',
|
||||
'F9F89FBB1426210D58D6A06E5EEF1783D6A90EE403B79AEDF0FED36A6DE238D2',
|
||||
'5328F2D1D6EBBC6093DC10F1EA3DD630666F5B2491EB9BDD7DF9A6C45AC12C46',
|
||||
]
|
||||
|
||||
const list_with_duplicates: string[] = [
|
||||
'EA85602C1B41F6F1F5E83C0E6B87142FB8957BD209469E4CC347BA2D0C26F66A',
|
||||
'EA85602C1B41F6F1F5E83C0E6B87142FB8957BD209469E4CC347BA2D0C26F66A',
|
||||
]
|
||||
|
||||
assert.isFalse(containsDuplicates(list_without_dups))
|
||||
assert.isTrue(containsDuplicates(list_with_duplicates))
|
||||
})
|
||||
})
|
||||
|
||||
describe('isFlagEnabled', function () {
|
||||
let flags: number
|
||||
const flag1 = 0x00010000
|
||||
|
||||
Reference in New Issue
Block a user