mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-18 19:25:48 +00:00
Remarks (#24)
This commit is contained in:
@@ -42,6 +42,12 @@ export { Remit } from './remit'
|
||||
export { SetHookFlagsInterface, SetHookFlags, SetHook } from './setHook'
|
||||
export { SetFee, SetFeePreAmendment, SetFeePostAmendment } from './setFee'
|
||||
export { SetRegularKey } from './setRegularKey'
|
||||
export {
|
||||
SetRemarks,
|
||||
Remark,
|
||||
RemarkFlags,
|
||||
RemarkFlagsInterface,
|
||||
} from './setRemarks'
|
||||
export { SignerListSet } from './signerListSet'
|
||||
export { TicketCreate } from './ticketCreate'
|
||||
export { TrustSetFlagsInterface, TrustSetFlags, TrustSet } from './trustSet'
|
||||
|
||||
90
packages/xahau/src/models/transactions/setRemarks.ts
Normal file
90
packages/xahau/src/models/transactions/setRemarks.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import { ValidationError } from '../../errors'
|
||||
|
||||
import { BaseTransaction, validateBaseTransaction } from './common'
|
||||
|
||||
export enum RemarkFlags {
|
||||
tfImmutable = 0x00000001,
|
||||
}
|
||||
|
||||
export interface RemarkFlagsInterface {
|
||||
tfImmutable?: boolean
|
||||
}
|
||||
|
||||
export interface Remark {
|
||||
Remark: {
|
||||
RemarkName: string
|
||||
RemarkValue?: string
|
||||
Flags?: number | RemarkFlagsInterface
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A SetRemarks transaction assigns, changes, or removes the remarks associated with a object.
|
||||
*
|
||||
* @category Transaction Models
|
||||
*/
|
||||
export interface SetRemarks extends BaseTransaction {
|
||||
TransactionType: 'SetRemarks'
|
||||
ObjectID: string
|
||||
Remarks: Remark[]
|
||||
}
|
||||
|
||||
const HEX_REGEX = /^[0-9A-Fa-f]{64}$/u
|
||||
const MAX_REMARKS = 32
|
||||
const MAX_REMARK_NAME_LENGTH = 256
|
||||
const MAX_REMARK_VALUE_LENGTH = 256
|
||||
|
||||
/**
|
||||
* Verify the form and type of a SetRemarks at runtime.
|
||||
*
|
||||
* @param tx - A SetRemarks Transaction.
|
||||
* @throws When the SetRemarks is malformed.
|
||||
*/
|
||||
export function validateSetRemarks(tx: Record<string, unknown>): void {
|
||||
validateBaseTransaction(tx)
|
||||
|
||||
if (tx.ObjectID == null) {
|
||||
throw new ValidationError('SetRemarks: ObjectID is required')
|
||||
}
|
||||
|
||||
if (typeof tx.ObjectID !== 'string' || !HEX_REGEX.test(tx.ObjectID)) {
|
||||
throw new ValidationError(
|
||||
'SetRemarks: ObjectID must be a 256-bit (32-byte) hexadecimal value',
|
||||
)
|
||||
}
|
||||
|
||||
if (tx.Remarks == null) {
|
||||
throw new ValidationError('SetRemarks: Remarks is required')
|
||||
}
|
||||
|
||||
if (!Array.isArray(tx.Remarks)) {
|
||||
throw new ValidationError('SetRemarks: Remarks must be an array')
|
||||
}
|
||||
|
||||
if (tx.Remarks.length > MAX_REMARKS) {
|
||||
throw new ValidationError(
|
||||
`SetRemarks: maximum of ${MAX_REMARKS} remarks allowed in Remarks`,
|
||||
)
|
||||
}
|
||||
|
||||
for (const remark of tx.Remarks) {
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Should be a Remark
|
||||
const remarkObject = remark as Remark
|
||||
const { RemarkName, RemarkValue } = remarkObject.Remark
|
||||
|
||||
if (RemarkName.length > MAX_REMARK_NAME_LENGTH * 2) {
|
||||
throw new ValidationError(
|
||||
`SetRemarks: maximum of ${MAX_REMARK_NAME_LENGTH} bytes allowed in RemarkName`,
|
||||
)
|
||||
}
|
||||
|
||||
if (
|
||||
RemarkValue != null &&
|
||||
RemarkValue.length > MAX_REMARK_VALUE_LENGTH * 2
|
||||
) {
|
||||
throw new ValidationError(
|
||||
`SetRemarks: maximum of ${MAX_REMARK_VALUE_LENGTH} bytes allowed in RemarkValue`,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,7 @@ import { Remit, validateRemit } from './remit'
|
||||
import { SetFee } from './setFee'
|
||||
import { SetHook, validateSetHook } from './setHook'
|
||||
import { SetRegularKey, validateSetRegularKey } from './setRegularKey'
|
||||
import { SetRemarks, validateSetRemarks } from './setRemarks'
|
||||
import { SignerListSet, validateSignerListSet } from './signerListSet'
|
||||
import { TicketCreate, validateTicketCreate } from './ticketCreate'
|
||||
import { TrustSet, validateTrustSet } from './trustSet'
|
||||
@@ -80,6 +81,7 @@ export type SubmittableTransaction =
|
||||
| Remit
|
||||
| SetHook
|
||||
| SetRegularKey
|
||||
| SetRemarks
|
||||
| SignerListSet
|
||||
| TicketCreate
|
||||
| TrustSet
|
||||
@@ -262,6 +264,10 @@ export function validate(transaction: Record<string, unknown>): void {
|
||||
validateSetRegularKey(tx)
|
||||
break
|
||||
|
||||
case 'SetRemarks':
|
||||
validateSetRemarks(tx)
|
||||
break
|
||||
|
||||
case 'SignerListSet':
|
||||
validateSignerListSet(tx)
|
||||
break
|
||||
|
||||
@@ -12,6 +12,11 @@ import { OfferCreateFlags } from '../transactions/offerCreate'
|
||||
import { PaymentFlags } from '../transactions/payment'
|
||||
import { PaymentChannelClaimFlags } from '../transactions/paymentChannelClaim'
|
||||
import { SetHookFlagsInterface, SetHookFlags } from '../transactions/setHook'
|
||||
import {
|
||||
RemarkFlagsInterface,
|
||||
RemarkFlags,
|
||||
Remark,
|
||||
} from '../transactions/setRemarks'
|
||||
import type { Transaction } from '../transactions/transaction'
|
||||
import { TrustSetFlags } from '../transactions/trustSet'
|
||||
|
||||
@@ -72,6 +77,14 @@ export function setTransactionFlagsToNumber(tx: Transaction): void {
|
||||
SetHookFlags,
|
||||
)
|
||||
})
|
||||
} else if (tx.TransactionType === 'SetRemarks') {
|
||||
tx.Remarks.forEach((remark: Remark) => {
|
||||
remark.Remark.Flags = convertFlagsToNumber(
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- idk
|
||||
remark.Remark.Flags as RemarkFlagsInterface,
|
||||
RemarkFlags,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
tx.Flags = txToFlag[tx.TransactionType]
|
||||
|
||||
105
packages/xahau/test/models/setRemarks.test.ts
Normal file
105
packages/xahau/test/models/setRemarks.test.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import { assert } from 'chai'
|
||||
|
||||
import { validate, ValidationError } from '../../src'
|
||||
import { validateSetRemarks } from '../../src/models/transactions/setRemarks'
|
||||
|
||||
/**
|
||||
* SetRemarks Transaction Verification Testing.
|
||||
*
|
||||
* Providing runtime verification testing for each specific transaction type.
|
||||
*/
|
||||
describe('SetRemarks', function () {
|
||||
let tx: any
|
||||
|
||||
beforeEach(function () {
|
||||
tx = {
|
||||
TransactionType: 'SetRemarks',
|
||||
Account: 'rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn',
|
||||
Fee: '12',
|
||||
ObjectID:
|
||||
'0000000000000000000000000000000000000000000000000000000000000000',
|
||||
Remarks: [
|
||||
{
|
||||
Remark: {
|
||||
RemarkName: 'DEADBEEF',
|
||||
RemarkValue: 'DEADBEEF',
|
||||
},
|
||||
},
|
||||
],
|
||||
} as any
|
||||
})
|
||||
|
||||
it(`verifies valid SetRemarks`, function () {
|
||||
assert.doesNotThrow(() => validateSetRemarks(tx))
|
||||
assert.doesNotThrow(() => validate(tx))
|
||||
|
||||
tx.Remarks[0].Remark.Flags = { tfImmutable: true }
|
||||
assert.doesNotThrow(() => validateSetRemarks(tx))
|
||||
assert.doesNotThrow(() => validate(tx))
|
||||
|
||||
tx.Remarks = [{ Remark: { RemarkName: 'DEADBEEF' } }]
|
||||
assert.doesNotThrow(() => validateSetRemarks(tx))
|
||||
assert.doesNotThrow(() => validate(tx))
|
||||
})
|
||||
|
||||
it(`throws w/ invalid ObjectID`, function () {
|
||||
delete tx.ObjectID
|
||||
let errorMessage = 'SetRemarks: ObjectID is required'
|
||||
assert.throws(() => validateSetRemarks(tx), ValidationError, errorMessage)
|
||||
assert.throws(() => validate(tx), ValidationError, errorMessage)
|
||||
|
||||
tx.ObjectID = 'DEADBEEF'
|
||||
errorMessage =
|
||||
'SetRemarks: ObjectID must be a 256-bit (32-byte) hexadecimal value'
|
||||
assert.throws(() => validateSetRemarks(tx), ValidationError, errorMessage)
|
||||
assert.throws(() => validate(tx), ValidationError, errorMessage)
|
||||
})
|
||||
|
||||
it(`throws w/ invalid Remarks`, function () {
|
||||
delete tx.Remarks
|
||||
let errorMessage = 'SetRemarks: Remarks is required'
|
||||
assert.throws(() => validateSetRemarks(tx), ValidationError, errorMessage)
|
||||
assert.throws(() => validate(tx), ValidationError, errorMessage)
|
||||
|
||||
tx.Remarks = 'DEABEEF'
|
||||
errorMessage = 'SetRemarks: Remarks must be an array'
|
||||
assert.throws(() => validateSetRemarks(tx), ValidationError, errorMessage)
|
||||
assert.throws(() => validate(tx), ValidationError, errorMessage)
|
||||
|
||||
tx.Remarks = Array(33)
|
||||
.fill(null)
|
||||
.map((_, idx) => ({
|
||||
Remark: {
|
||||
RemarkName: `DEADBEEF${idx.toString(16)}`,
|
||||
RemarkValue: `DEADBEEF${idx.toString(16)}`,
|
||||
},
|
||||
}))
|
||||
errorMessage = `SetRemarks: maximum of 32 remarks allowed in Remarks`
|
||||
assert.throws(() => validateSetRemarks(tx), ValidationError, errorMessage)
|
||||
assert.throws(() => validate(tx), ValidationError, errorMessage)
|
||||
|
||||
tx.Remarks = [
|
||||
{
|
||||
Remark: {
|
||||
RemarkName: 'DEADBEEF'.repeat(256 / 4 + 1),
|
||||
RemarkValue: 'DEADBEEF',
|
||||
},
|
||||
},
|
||||
]
|
||||
errorMessage = `SetRemarks: maximum of 256 bytes allowed in RemarkName`
|
||||
assert.throws(() => validateSetRemarks(tx), ValidationError, errorMessage)
|
||||
assert.throws(() => validate(tx), ValidationError, errorMessage)
|
||||
|
||||
tx.Remarks = [
|
||||
{
|
||||
Remark: {
|
||||
RemarkName: 'DEADBEEF',
|
||||
RemarkValue: 'DEADBEEF'.repeat(256 / 4 + 1),
|
||||
},
|
||||
},
|
||||
]
|
||||
errorMessage = `SetRemarks: maximum of 256 bytes allowed in RemarkValue`
|
||||
assert.throws(() => validateSetRemarks(tx), ValidationError, errorMessage)
|
||||
assert.throws(() => validate(tx), ValidationError, errorMessage)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user