mirror of
				https://github.com/Xahau/xahau.js.git
				synced 2025-11-04 04:55:48 +00:00 
			
		
		
		
	Remarks (#24)
This commit is contained in:
		@@ -1218,6 +1218,16 @@
 | 
			
		||||
        "type": "Hash256"
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    [
 | 
			
		||||
      "ObjectID",
 | 
			
		||||
      {
 | 
			
		||||
        "nth": 14,
 | 
			
		||||
        "isVLEncoded": false,
 | 
			
		||||
        "isSerialized": true,
 | 
			
		||||
        "isSigningField": true,
 | 
			
		||||
        "type": "Hash256"
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    [
 | 
			
		||||
      "BookDirectory",
 | 
			
		||||
      {
 | 
			
		||||
@@ -1898,6 +1908,26 @@
 | 
			
		||||
        "type": "Blob"
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    [
 | 
			
		||||
      "RemarkValue",
 | 
			
		||||
      {
 | 
			
		||||
        "nth": 98,
 | 
			
		||||
        "isVLEncoded": true,
 | 
			
		||||
        "isSerialized": true,
 | 
			
		||||
        "isSigningField": true,
 | 
			
		||||
        "type": "Blob"
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    [
 | 
			
		||||
      "RemarkName",
 | 
			
		||||
      {
 | 
			
		||||
        "nth": 99,
 | 
			
		||||
        "isVLEncoded": true,
 | 
			
		||||
        "isSerialized": true,
 | 
			
		||||
        "isSigningField": true,
 | 
			
		||||
        "type": "Blob"
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    [
 | 
			
		||||
      "Account",
 | 
			
		||||
      {
 | 
			
		||||
@@ -2288,6 +2318,16 @@
 | 
			
		||||
        "type": "STObject"
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    [
 | 
			
		||||
      "Remark",
 | 
			
		||||
      {
 | 
			
		||||
        "nth": 97,
 | 
			
		||||
        "isVLEncoded": false,
 | 
			
		||||
        "isSerialized": true,
 | 
			
		||||
        "isSigningField": true,
 | 
			
		||||
        "type": "STObject"
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    [
 | 
			
		||||
      "GenesisMint",
 | 
			
		||||
      {
 | 
			
		||||
@@ -2488,6 +2528,16 @@
 | 
			
		||||
        "type": "STArray"
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    [
 | 
			
		||||
      "Remarks",
 | 
			
		||||
      {
 | 
			
		||||
        "nth": 97,
 | 
			
		||||
        "isVLEncoded": false,
 | 
			
		||||
        "isSerialized": true,
 | 
			
		||||
        "isSigningField": true,
 | 
			
		||||
        "type": "STArray"
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    [
 | 
			
		||||
      "GenesisMints",
 | 
			
		||||
      {
 | 
			
		||||
@@ -2632,6 +2682,7 @@
 | 
			
		||||
    "tefPAST_IMPORT_SEQ": -178,
 | 
			
		||||
    "tefPAST_IMPORT_VL_SEQ": -177,
 | 
			
		||||
    "tefNONDIR_EMIT": -176,
 | 
			
		||||
    "tefIMPORT_BLACKHOLED": -175,
 | 
			
		||||
 | 
			
		||||
    "terRETRY": -99,
 | 
			
		||||
    "terFUNDS_SPENT": -98,
 | 
			
		||||
@@ -2724,6 +2775,8 @@
 | 
			
		||||
    "tecXCHAIN_SELF_COMMIT": 185,
 | 
			
		||||
    "tecXCHAIN_BAD_PUBLIC_KEY_ACCOUNT_PAIR": 186,
 | 
			
		||||
    "tecINSUF_RESERVE_SELLER": 187,
 | 
			
		||||
    "tecIMMUTABLE": 188,
 | 
			
		||||
    "tecTOO_MANY_REMARKS": 189,
 | 
			
		||||
    "tecLAST_POSSIBLE_ENTRY": 255
 | 
			
		||||
  },
 | 
			
		||||
  "TRANSACTION_TYPES": {
 | 
			
		||||
@@ -2761,6 +2814,7 @@
 | 
			
		||||
    "URITokenBuy": 47,
 | 
			
		||||
    "URITokenCreateSellOffer": 48,
 | 
			
		||||
    "URITokenCancelSellOffer": 49,
 | 
			
		||||
    "SetRemarks": 94,
 | 
			
		||||
    "Remit": 95,
 | 
			
		||||
    "GenesisMint": 96,
 | 
			
		||||
    "Import": 97,
 | 
			
		||||
 
 | 
			
		||||
@@ -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