@iden3/js-iden3-core
Version:
Low level API to create and manipulate iden3 Claims.
4 lines • 109 kB
Source Map (JSON)
{
"version": 3,
"sources": ["../../../src/index.ts", "../../../src/schemaHash.ts", "../../../src/constants.ts", "../../../src/utils.ts", "../../../src/elemBytes.ts", "../../../src/id.ts", "../../../src/claim.ts", "../../../src/did/did-helper.ts", "../../../src/did/types.ts", "../../../src/did/did-parser.ts", "../../../src/did/did.ts", "../../../src/registration.ts"],
"sourcesContent": ["export * from './claim';\nexport * from './constants';\nexport * from './did';\nexport * from './elemBytes';\nexport * from './id';\nexport * from './schemaHash';\nexport * from './utils';\nexport * from './registration';\n", "import { Hex } from '@iden3/js-crypto';\nimport { Constants } from './constants';\nimport { BytesHelper } from './elemBytes';\n\nexport class SchemaHash {\n // authSchemaHash predefined value of auth schema, used for auth claim during identity creation.\n // This schema is hardcoded in the identity circuits and used to verify user's auth claim.\n // Keccak256(https://schema.iden3.io/core/jsonld/auth.jsonld#AuthBJJCredential) last 16 bytes\n // Hex: cca3371a6cb1b715004407e325bd993c\n // BigInt: 80551937543569765027552589160822318028\n static readonly authSchemaHash = new SchemaHash(\n Uint8Array.from([204, 163, 55, 26, 108, 177, 183, 21, 0, 68, 7, 227, 37, 189, 153, 60])\n );\n\n private _bytes: Uint8Array = new Uint8Array(Constants.SCHEMA.HASH_LENGTH);\n /**\n * Constructor\n * @param bytes\n */\n constructor(bytes?: Uint8Array) {\n if (bytes) {\n this._bytes = bytes;\n }\n if (this.bytes.length !== Constants.SCHEMA.HASH_LENGTH) {\n throw new Error(`Schema hash must be ${Constants.SCHEMA.HASH_LENGTH} bytes long`);\n }\n }\n\n get bytes(): Uint8Array {\n return this._bytes;\n }\n\n /**\n * MarshalText returns HEX representation of SchemaHash.\n * @returns {Uint8Array} 32 bytes//\n */\n marshalTextBytes(): Uint8Array {\n return Hex.encode(this.bytes);\n }\n\n marshalText(): string {\n return Hex.encodeString(this.bytes);\n }\n\n /**\n * NewSchemaHashFromHex creates new SchemaHash from hex string\n * @param s\n * @returns {SchemaHash}\n */\n static newSchemaHashFromHex(s: string): SchemaHash {\n const schemaEncodedBytes = Hex.decodeString(s);\n\n if (schemaEncodedBytes.length !== Constants.SCHEMA.HASH_LENGTH) {\n throw new Error(`invalid schema hash length: ${schemaEncodedBytes.length}`);\n }\n\n return new SchemaHash(schemaEncodedBytes);\n }\n\n /**\n * NewSchemaHashFromInt creates new SchemaHash from big.Int\n * @param i\n * @returns\n */\n static newSchemaHashFromInt(i: bigint): SchemaHash {\n const bytes = BytesHelper.intToNBytes(i, Constants.SCHEMA.HASH_LENGTH);\n const start = Constants.SCHEMA.HASH_LENGTH - bytes.length;\n return new SchemaHash(BytesHelper.intToBytes(i).slice(start, Constants.SCHEMA.HASH_LENGTH));\n }\n\n /**\n * Convert SchemaHash to big.Int\n * @returns {bigint}\n */\n bigInt(): bigint {\n return BytesHelper.bytesToInt(this.bytes);\n }\n}\n", "export const Constants = Object.freeze({\n ERRORS: {\n // ErrDataOverflow means that given *big.Int value does not fit in Field Q\n // e.g. greater than Q constant:\n // Q constant: 21888242871839275222246405745257275088548364400416034343698204186575808495617\n DATA_OVERFLOW: new Error('data does not fits SNARK size'),\n // ErrIncorrectIDPosition means that passed position is not one of predefined:\n // IDPositionIndex or IDPositionValue\n INCORRECT_ID_POSITION: new Error('incorrect ID position'),\n // throws when ID not found in the Claim.\n NO_ID: new Error('ID is not set'),\n // throws when subject position flags sets in invalid value.\n INVALID_SUBJECT_POSITION: new Error('invalid subject position'),\n // ErrIncorrectMerklizePosition means that passed position is not one of predefined:\n // MerklizePositionIndex or MerklizePositionValue\n INCORRECT_MERKLIZED_POSITION: new Error('incorrect Merklize position'),\n // ErrNoMerklizedRoot returns when Merklized Root is not found in the Claim.\n NO_MERKLIZED_ROOT: new Error('Merklized root is not set'),\n NETWORK_NOT_SUPPORTED_FOR_DID: new Error('network in not supported for did'),\n UNSUPPORTED_BLOCKCHAIN_FOR_DID: new Error('not supported blockchain for did'),\n UNSUPPORTED_DID_METHOD: new Error('not supported DID method'),\n UNKNOWN_DID_METHOD: new Error('unknown DID method'),\n INCORRECT_DID: new Error('incorrect DID'),\n UNSUPPORTED_ID: new Error('unsupported Id')\n },\n SCHEMA: {\n HASH_LENGTH: 16\n },\n ETH_ADDRESS_LENGTH: 20,\n BYTES_LENGTH: 32,\n ELEM_BYTES_LENGTH: 4,\n NONCE_BYTES_LENGTH: 8,\n Q: BigInt('21888242871839275222246405745257275088548364400416034343698204186575808495617'),\n ID: {\n TYPE_DEFAULT: Uint8Array.from([0x00, 0x00]),\n TYPE_READONLY: Uint8Array.from([0b00000000, 0b00000001]),\n ID_LENGTH: 31\n },\n DID: {\n DID_SCHEMA: 'did'\n },\n GENESIS_LENGTH: 27\n});\n\nexport const Blockchain: { [k: string]: string } = {\n Ethereum: 'eth',\n Polygon: 'polygon',\n Privado: 'privado',\n Billions: 'billions',\n Linea: 'linea',\n Unknown: 'unknown',\n NoChain: '',\n ReadOnly: 'readonly'\n};\n\nexport const NetworkId: { [k: string]: string } = {\n Main: 'main',\n Mumbai: 'mumbai',\n Amoy: 'amoy',\n Goerli: 'goerli',\n Sepolia: 'sepolia',\n Zkevm: 'zkevm',\n Cardona: 'cardona',\n Test: 'test',\n Unknown: 'unknown',\n NoNetwork: ''\n};\n\nexport const DidMethod: { [k: string]: string } = {\n Iden3: 'iden3',\n PolygonId: 'polygonid',\n Other: ''\n};\n\n/**\n * Object containing chain IDs for various blockchains and networks.\n * @type { [key: string]: number }\n */\nexport const ChainIds: { [key: string]: number } = {\n [`${Blockchain.Ethereum}:${NetworkId.Main}`]: 1,\n [`${Blockchain.Ethereum}:${NetworkId.Goerli}`]: 5,\n [`${Blockchain.Ethereum}:${NetworkId.Sepolia}`]: 11155111,\n [`${Blockchain.Polygon}:${NetworkId.Main}`]: 137,\n [`${Blockchain.Polygon}:${NetworkId.Mumbai}`]: 80001,\n [`${Blockchain.Polygon}:${NetworkId.Amoy}`]: 80002,\n [`${Blockchain.Polygon}:${NetworkId.Zkevm}`]: 1101,\n [`${Blockchain.Polygon}:${NetworkId.Cardona}`]: 2442,\n [`${Blockchain.Privado}:${NetworkId.Main}`]: 21000,\n [`${Blockchain.Privado}:${NetworkId.Test}`]: 21001,\n [`${Blockchain.Linea}:${NetworkId.Main}`]: 59144,\n [`${Blockchain.Linea}:${NetworkId.Sepolia}`]: 59141,\n [`${Blockchain.Billions}:${NetworkId.Main}`]: 45056,\n [`${Blockchain.Billions}:${NetworkId.Test}`]: 6913\n};\n\nexport const DidMethodByte: { [key: string]: number } = {\n [DidMethod.Iden3]: 0b00000001,\n [DidMethod.PolygonId]: 0b00000010,\n [DidMethod.Other]: 0b11111111\n};\n\nconst blockchainNetworkMap = {\n [`${Blockchain.ReadOnly}:${NetworkId.NoNetwork}`]: 0b00000000,\n [`${Blockchain.Polygon}:${NetworkId.Main}`]: 0b0001_0000 | 0b0000_0001,\n [`${Blockchain.Polygon}:${NetworkId.Mumbai}`]: 0b0001_0000 | 0b0000_0010,\n [`${Blockchain.Polygon}:${NetworkId.Amoy}`]: 0b0001_0000 | 0b0000_0011,\n [`${Blockchain.Polygon}:${NetworkId.Zkevm}`]: 0b0001_0000 | 0b0000_0100,\n [`${Blockchain.Polygon}:${NetworkId.Cardona}`]: 0b0001_0000 | 0b0000_0101,\n [`${Blockchain.Ethereum}:${NetworkId.Main}`]: 0b0010_0000 | 0b0000_0001,\n [`${Blockchain.Ethereum}:${NetworkId.Goerli}`]: 0b0010_0000 | 0b0000_0010,\n [`${Blockchain.Ethereum}:${NetworkId.Sepolia}`]: 0b0010_0000 | 0b0000_0011,\n [`${Blockchain.Privado}:${NetworkId.Main}`]: 0b1010_0000 | 0b0000_0001,\n [`${Blockchain.Privado}:${NetworkId.Test}`]: 0b1010_0000 | 0b0000_0010,\n [`${Blockchain.Linea}:${NetworkId.Main}`]: 0b0100_0000 | 0b0000_1001,\n [`${Blockchain.Linea}:${NetworkId.Sepolia}`]: 0b0100_0000 | 0b0000_1000,\n [`${Blockchain.Billions}:${NetworkId.Main}`]: 0b1011_0000 | 0b0000_0001,\n [`${Blockchain.Billions}:${NetworkId.Test}`]: 0b1011_0000 | 0b0000_0010\n};\n\n// DIDMethodNetwork is map for did methods and their blockchain networks\nexport const DidMethodNetwork: {\n [k: string]: { [k: string]: number };\n} = {\n [DidMethod.Iden3]: {\n ...blockchainNetworkMap\n },\n [DidMethod.PolygonId]: {\n ...blockchainNetworkMap\n },\n [DidMethod.Other]: {\n [`${Blockchain.Unknown}:${NetworkId.Unknown}`]: 0b1111_1111\n }\n};\n", "import { poseidon } from '@iden3/js-crypto';\nimport { Constants } from './constants';\n// eslint-disable-next-line @typescript-eslint/no-var-requires\nexport const encoder = new TextEncoder();\n\nexport function fromLittleEndian(bytes: Uint8Array): bigint {\n const n256 = BigInt(256);\n let result = BigInt(0);\n let base = BigInt(1);\n bytes.forEach((byte) => {\n result += base * BigInt(byte);\n base = base * n256;\n });\n return result;\n}\n\nexport function fromBigEndian(bytes: Uint8Array): bigint {\n return fromLittleEndian(bytes.reverse());\n}\n\nexport function toLittleEndian(bigNumber: bigint, len = 31): Uint8Array {\n const n256 = BigInt(256);\n const result = new Uint8Array(len);\n let i = 0;\n while (bigNumber > BigInt(0)) {\n result[i] = Number(bigNumber % n256);\n bigNumber = bigNumber / n256;\n i += 1;\n }\n return result;\n}\n\nexport function toBigEndian(bigNumber: bigint, len = 31): Uint8Array {\n return toLittleEndian(bigNumber, len).reverse();\n}\n\nexport function putUint32(n: number): Uint8Array {\n const buf = new ArrayBuffer(4);\n const view = new DataView(buf);\n view.setUint32(0, n, true);\n return new Uint8Array(buf);\n}\n\nexport function getUint32(arr: Uint8Array): number {\n const buf = arr.buffer.slice(arr.byteOffset, arr.byteOffset + arr.byteLength);\n return new DataView(buf).getUint32(0, true);\n}\n\nexport function putUint64(n: bigint): Uint8Array {\n const buf = new ArrayBuffer(8);\n const view = new DataView(buf);\n view.setBigUint64(0, n, true);\n return new Uint8Array(buf);\n}\n\nexport function getUint64(arr: Uint8Array): bigint {\n const buf = arr.buffer.slice(arr.byteOffset, arr.byteOffset + arr.byteLength);\n return new DataView(buf).getBigUint64(0, true);\n}\n\nexport function getUnixTimestamp(d: Date): number {\n return Math.floor(d.getTime() / 1000);\n}\nexport function getDateFromUnixTimestamp(n: number): Date {\n return new Date(n * 1000);\n}\n\n// checkBigIntInField checks if given *big.Int fits in a Field Q element\nexport function checkBigIntInField(a: bigint): boolean {\n return a < Constants.Q;\n}\n\nexport function checkBigIntArrayInField(arr: bigint[]): boolean {\n return arr.every((n) => checkBigIntInField(n));\n}\n\n// IdenState calculates the Identity State from the Claims Tree Root,\n// Revocation Tree Root and Roots Tree Root.\nexport function idenState(clr: bigint, rer: bigint, ror: bigint): bigint {\n return poseidon.hash([clr, rer, ror]);\n}\n\nexport class StringUtils {\n static isNotValidIDChar(char: string): boolean {\n return (\n StringUtils.isNotAlpha(char) && StringUtils.isNotDigit(char) && char !== '.' && char !== '-'\n );\n }\n\n static isNotValidParamChar(char: string): boolean {\n return (\n StringUtils.isNotAlpha(char) &&\n StringUtils.isNotDigit(char) &&\n char !== '.' &&\n char !== '-' &&\n char !== '_' &&\n char !== ':'\n );\n }\n\n static isNotValidQueryOrFragmentChar(char: string): boolean {\n return StringUtils.isNotValidPathChar(char) && char !== '/' && char !== '?';\n }\n\n static isNotValidPathChar(char: string): boolean {\n return StringUtils.isNotUnreservedOrSubdelim(char) && char !== ':' && char !== '@';\n }\n\n static isNotUnreservedOrSubdelim(char: string): boolean {\n switch (char) {\n case '-':\n case '.':\n case '_':\n case '~':\n case '!':\n case '$':\n case '&':\n case \"'\":\n case '(':\n case ')':\n case '*':\n case '+':\n case ',':\n case ';':\n case '=':\n return false;\n default:\n if (StringUtils.isNotAlpha(char) && StringUtils.isNotDigit(char)) {\n return true;\n }\n return false;\n }\n }\n\n static isNotHexDigit(char: string): boolean {\n return (\n StringUtils.isNotDigit(char) &&\n (char < '\\x41' || char > '\\x46') &&\n (char < '\\x61' || char > '\\x66')\n );\n }\n\n static isNotDigit(char: string): boolean {\n // '\\x30' is digit 0, '\\x39' is digit 9\n return char < '\\x30' || char > '\\x39';\n }\n\n // StringUtils.isNotAlpha returns true if a byte is not a big letter between A-Z or small letter between a-z\n // https://tools.ietf.org/html/rfc5234#appendix-B.1\n static isNotAlpha(char: string): boolean {\n return StringUtils.isNotSmallLetter(char) && StringUtils.isNotBigLetter(char);\n }\n\n // isNotBigLetter returns true if a byte is not a big letter between A-Z\n // in US-ASCII http://www.columbia.edu/kermit/ascii.html\n // https://tools.ietf.org/html/rfc5234#appendix-B.1\n static isNotBigLetter(char: string): boolean {\n // '\\x41' is big letter A, '\\x5A' small letter Z\n return char < '\\x41' || char > '\\x5A';\n }\n\n // isNotSmallLetter returns true if a byte is not a small letter between a-z\n // in US-ASCII http://www.columbia.edu/kermit/ascii.html\n // https://tools.ietf.org/html/rfc5234#appendix-B.1\n static isNotSmallLetter(char: string): boolean {\n // '\\x61' is small letter a, '\\x7A' small letter z\n return char < '\\x61' || char > '\\x7A';\n }\n}\n\nexport const genesisFromEthAddress = (addr: Uint8Array) => {\n return Uint8Array.from([...new Uint8Array(7), ...addr]);\n};\n", "import { Constants } from './constants';\nimport { checkBigIntInField, fromLittleEndian, toLittleEndian, encoder } from './utils';\nimport { Hex, sha256 } from '@iden3/js-crypto';\nexport class BytesHelper {\n static intToBytes(int: bigint): Uint8Array {\n return BytesHelper.intToNBytes(int, Constants.BYTES_LENGTH);\n }\n\n static intToNBytes(int: bigint, n: number): Uint8Array {\n return Uint8Array.from(toLittleEndian(int, n));\n }\n\n static checkChecksum(bytes: Uint8Array): boolean {\n const { typ, genesis, checksum } = BytesHelper.decomposeBytes(bytes);\n if (!checksum.length || JSON.stringify(Uint8Array.from([0, 0])) === JSON.stringify(checksum)) {\n return false;\n }\n\n const c = BytesHelper.calculateChecksum(typ, genesis);\n return JSON.stringify(c) === JSON.stringify(checksum);\n }\n\n static decomposeBytes(b: Uint8Array): {\n typ: Uint8Array;\n genesis: Uint8Array;\n checksum: Uint8Array;\n } {\n const offset = 2;\n const len = b.length - offset;\n return {\n typ: b.slice(0, offset),\n genesis: b.slice(offset, len),\n checksum: b.slice(-offset)\n };\n }\n\n static calculateChecksum(typ: Uint8Array, genesis: Uint8Array): Uint8Array {\n const toChecksum = [...typ, ...genesis];\n const s: number = toChecksum.reduce((acc, cur) => acc + cur, 0);\n const checksum = [s >> 8, s & 0xff];\n return Uint8Array.from(checksum.reverse());\n }\n\n static hashBytes(str: string): Uint8Array {\n const hash = sha256(encoder.encode(str));\n return new Uint8Array(hash);\n }\n\n static hexToBytes(str: string): Uint8Array {\n return Hex.decodeString(str);\n }\n\n static bytesToHex(bytes: Uint8Array) {\n const hex: string[] = [];\n for (let i = 0; i < bytes.length; i++) {\n const current = bytes[i] < 0 ? bytes[i] + 256 : bytes[i];\n hex.push((current >>> 4).toString(16));\n hex.push((current & 0xf).toString(16));\n }\n return hex.join('');\n }\n\n static bytesToInt(bytes: Uint8Array): bigint {\n return fromLittleEndian(bytes);\n }\n}\n\nexport class ElemBytes {\n private _bytes = new Uint8Array(Constants.BYTES_LENGTH);\n\n constructor(bytes?: Uint8Array | null) {\n if (bytes) {\n this._bytes = bytes;\n }\n if (this._bytes.length !== Constants.BYTES_LENGTH) {\n throw new Error('Invalid bytes length');\n }\n }\n\n get bytes(): Uint8Array {\n return this._bytes;\n }\n\n set bytes(value: Uint8Array) {\n this._bytes = value;\n }\n\n toBigInt(): bigint {\n return BytesHelper.bytesToInt(this._bytes);\n }\n\n setBigInt(n: bigint): ElemBytes {\n if (!checkBigIntInField(n)) {\n throw Constants.ERRORS.DATA_OVERFLOW;\n }\n this._bytes = BytesHelper.intToBytes(n);\n return this;\n }\n\n slotFromHex(hex: string): ElemBytes {\n const bytes = Hex.decodeString(hex);\n if (bytes.length !== Constants.BYTES_LENGTH) {\n throw new Error('Invalid bytes length');\n }\n this._bytes.set(bytes, 0);\n return this;\n }\n\n hex(): string {\n return Hex.encodeString(this._bytes);\n }\n\n // ElemBytesToInts converts slice of ElemBytes to slice of *big.Int\n static elemBytesToInts(elements: ElemBytes[]): bigint[] {\n const result: bigint[] = [];\n\n for (let i = 0; i < elements.length; i++) {\n const element = elements[i];\n result.push(element.toBigInt());\n }\n\n return result;\n }\n\n static fromInt(i: bigint): ElemBytes {\n if (!checkBigIntInField(i)) {\n throw Constants.ERRORS.DATA_OVERFLOW;\n }\n const bytes = BytesHelper.intToBytes(i);\n return new ElemBytes(bytes);\n }\n}\n", "import { Constants } from './constants';\nimport { fromLittleEndian } from './utils';\nimport { BytesHelper, ElemBytes } from './elemBytes';\nimport { poseidon, base58ToBytes, base58FromBytes } from '@iden3/js-crypto';\n\n// ID is a byte array with\n// [ type | root_genesis | checksum ]\n// [2 bytes | 27 bytes | 2 bytes ]\n// where the root_genesis are the first 28 bytes from the hash root_genesis\n\nexport class Id {\n private _bytes: Uint8Array;\n private readonly _checksum: Uint8Array;\n\n constructor(typ: Uint8Array, genesis: Uint8Array) {\n this._checksum = BytesHelper.calculateChecksum(typ, genesis);\n this._bytes = Uint8Array.from([...typ, ...genesis, ...this._checksum]);\n }\n\n private static getFromBytes(bytes: Uint8Array): Id {\n const { typ, genesis }: { typ: Uint8Array; genesis: Uint8Array } =\n BytesHelper.decomposeBytes(bytes);\n return new Id(typ, genesis);\n }\n\n checksum(): Uint8Array {\n return this._checksum;\n }\n\n string(): string {\n return base58FromBytes(this._bytes);\n }\n\n get bytes(): Uint8Array {\n return this._bytes;\n }\n\n set bytes(b: Uint8Array) {\n this._bytes = b;\n }\n\n type(): Uint8Array {\n return this._bytes.slice(0, 2);\n }\n\n bigInt(): bigint {\n return fromLittleEndian(this._bytes);\n }\n\n equal(id: Id): boolean {\n return JSON.stringify(this._bytes) === JSON.stringify(id.bytes);\n }\n\n marshal(): Uint8Array {\n return new TextEncoder().encode(this.string());\n }\n\n static unMarshal(b: Uint8Array): Id {\n return Id.fromString(new TextDecoder().decode(b));\n }\n\n static fromBytes(b: Uint8Array): Id {\n const bytes = b ?? Uint8Array.from([]);\n if (bytes.length !== Constants.ID.ID_LENGTH) {\n throw new Error('fromBytes error: byte array incorrect length');\n }\n\n if (bytes.every((i: number) => i === 0)) {\n throw new Error('fromBytes error: byte array empty');\n }\n\n const id = Id.getFromBytes(bytes);\n\n if (!BytesHelper.checkChecksum(bytes)) {\n throw new Error('fromBytes error: checksum error');\n }\n\n return id;\n }\n\n static fromString(s: string): Id {\n const bytes = base58ToBytes(s);\n return Id.fromBytes(bytes);\n }\n\n static fromBigInt(bigInt: bigint): Id {\n const b = BytesHelper.intToNBytes(bigInt, Constants.ID.ID_LENGTH);\n return Id.fromBytes(b);\n }\n\n static profileId(id: Id, nonce: bigint): Id {\n const bigIntHash = poseidon.hash([id.bigInt(), nonce]);\n const { typ } = BytesHelper.decomposeBytes(id.bytes);\n const genesis = BytesHelper.intToNBytes(bigIntHash, 27);\n return new Id(typ, genesis);\n }\n\n // IdGenesisFromIdenState calculates the genesis ID from an Identity State.\n static idGenesisFromIdenState(\n typ: Uint8Array, //nolint:revive\n state: bigint\n ): Id {\n const idenStateData = ElemBytes.fromInt(state);\n\n // we take last 27 bytes, because of swapped endianness\n const idGenesisBytes = idenStateData.bytes.slice(idenStateData.bytes.length - 27);\n return new Id(typ, idGenesisBytes);\n }\n\n static ethAddressFromId(id: Id): Uint8Array {\n const isZeros = id.bytes.slice(2, 2 + 7).every((i: number) => i === 0);\n if (!isZeros) {\n throw new Error(\"can't get Ethereum address: high bytes of genesis are not zero\");\n }\n return id.bytes.slice(2 + 7).slice(0, Constants.ETH_ADDRESS_LENGTH);\n }\n}\n", "import { SchemaHash } from './schemaHash';\nimport { ElemBytes } from './elemBytes';\nimport { Constants } from './constants';\nimport { Id } from './id';\nimport {\n checkBigIntArrayInField,\n checkBigIntInField,\n getDateFromUnixTimestamp,\n getUint32,\n getUint64,\n getUnixTimestamp,\n putUint32,\n putUint64 as getBytesFromUint64\n} from './utils';\nimport { Hex, poseidon } from '@iden3/js-crypto';\n\n/*\nClaim structure\n\nIndex:\n i_0: [ 128 bits ] claim schema\n [ 32 bits ] option flags\n [3] Subject:\n 000: A.1 Self\n 001: invalid\n 010: A.2.i OtherIden Index\n 011: A.2.v OtherIden Value\n 100: B.i Object Index\n 101: B.v Object Value\n [1] Expiration: bool\n [1] Updatable: bool\n [3] Merklized: data is merklized root is stored in the:\n 000: none\n 001: C.i Root Index (root located in i_2)\n 010: C.v Root Value (root located in v_2)\n [24] 0\n [ 32 bits ] version (optional?)\n [ 61 bits ] 0 - reserved for future use\n i_1: [ 248 bits] identity (case b) (optional)\n [ 5 bits ] 0\n i_2: [ 253 bits] 0\n i_3: [ 253 bits] 0\nValue:\n v_0: [ 64 bits ] revocation nonce\n [ 64 bits ] expiration date (optional)\n [ 125 bits] 0 - reserved\n v_1: [ 248 bits] identity (case c) (optional)\n [ 5 bits ] 0\n v_2: [ 253 bits] 0\n v_3: [ 253 bits] 0\n*/\n\nexport enum SlotName {\n IndexA = 'IndexA',\n IndexB = 'IndexB',\n ValueA = 'ValueA',\n ValueB = 'ValueB'\n}\n\n// ErrSlotOverflow means some ElemBytes overflows Q Field. And wraps the name\n// of overflowed slot.\nexport class ErrSlotOverflow extends Error {\n constructor(msg: string) {\n super(`Slot ${msg} not in field (too large)`);\n Object.setPrototypeOf(this, ErrSlotOverflow.prototype);\n }\n}\n\n// subjectFlag for the time being describes the location of Id (in index or value\n// slots or nowhere at all).\n//\n// Values subjectFlagInvalid presents for backward compatibility and for now means nothing.\n\nexport enum SubjectFlag {\n Self = 0b0,\n Invalid = 0b1,\n OtherIdenIndex = 0b10,\n OtherIdenValue = 0b11\n}\n\nexport enum IdPosition {\n None = 0,\n Index = 1,\n Value = 2\n}\n\n// merklizedFlag for the time being describes the location of root (in index or value\n// slots or nowhere at all).\n//\n// Values merklizedFlagIndex indicates that root is located in index[2] slots.\n// Values merklizedFlagValue indicates that root is located in value[2] slots.\nexport enum MerklizedFlag {\n None = 0b00000000,\n Index = 0b00100000,\n Value = 0b01000000,\n Invalid = 0b10000000\n}\n\nexport enum MerklizedRootPosition {\n None = 0,\n Index = 1,\n Value = 2\n}\n\nexport enum Flags {\n ByteIdx = 16,\n ExpirationBitIdx = 3,\n UpdatableBitIdx = 4\n}\n\nexport class Claim {\n private _index: ElemBytes[] = [];\n private _value: ElemBytes[] = [];\n\n constructor() {\n for (let i = 0; i < Constants.ELEM_BYTES_LENGTH; i++) {\n this._index[i] = new ElemBytes();\n this._value[i] = new ElemBytes();\n }\n }\n\n // NewClaim creates new Claim with specified SchemaHash and any number of\n // options. Using options you can specify any field in claim.\n static newClaim(sh: SchemaHash, ...args: ClaimOption[]): Claim {\n const c = new Claim();\n c.setSchemaHash(sh);\n for (let i = 0; i < args.length; i++) {\n const fn = args[i];\n fn(c);\n }\n return c;\n }\n\n // GetSchemaHash return copy of claim's schema hash.\n getSchemaHash(): SchemaHash {\n return new SchemaHash(this._index[0].bytes.slice(0, Constants.SCHEMA.HASH_LENGTH));\n }\n\n get value(): ElemBytes[] {\n return this._value;\n }\n\n set value(value: ElemBytes[]) {\n this._value = value;\n }\n\n get index(): ElemBytes[] {\n return this._index;\n }\n\n set index(value: ElemBytes[]) {\n this._index = value;\n }\n\n // SetSchemaHash updates claim's schema hash.\n setSchemaHash(sh: SchemaHash) {\n this._index[0] = new ElemBytes(\n Uint8Array.from([...sh.bytes, ...new Array(Constants.SCHEMA.HASH_LENGTH).fill(0)])\n );\n }\n\n setSubject(s: SubjectFlag) {\n // clean first 3 bits\n this._index[0].bytes[Flags.ByteIdx] &= 0b11111000;\n this._index[0].bytes[Flags.ByteIdx] |= s;\n }\n\n private getSubject(): SubjectFlag {\n let sbj = this._index[0].bytes[Flags.ByteIdx];\n // clean all except first 3 bits\n sbj &= 0b00000111;\n return sbj as SubjectFlag;\n }\n\n private setFlagExpiration(val: boolean) {\n if (val) {\n this._index[0].bytes[Flags.ByteIdx] |= 0b1 << Flags.ExpirationBitIdx;\n } else {\n this._index[0].bytes[Flags.ByteIdx] &= ~(0b1 << Flags.ExpirationBitIdx);\n }\n }\n\n private getFlagExpiration(): boolean {\n const mask = 0b1 << Flags.ExpirationBitIdx;\n return (this._index[0].bytes[Flags.ByteIdx] & mask) > 0;\n }\n\n // GetIDPosition returns the position at which the Id is stored.\n getIdPosition(): IdPosition {\n switch (this.getSubject()) {\n case SubjectFlag.Self:\n return IdPosition.None;\n case SubjectFlag.OtherIdenIndex:\n return IdPosition.Index;\n case SubjectFlag.OtherIdenValue:\n return IdPosition.Value;\n default:\n throw Constants.ERRORS.INVALID_SUBJECT_POSITION;\n }\n }\n\n // SetValueDataInts sets data to value slots A & B.\n // Returns ErrSlotOverflow if slotA or slotB value are too big.\n setValueDataInts(slotA: bigint | null, slotB: bigint | null): void {\n this._value[2] = this.setSlotInt(slotA, SlotName.ValueA);\n this._value[3] = this.setSlotInt(slotB, SlotName.ValueB);\n }\n // SetValueDataBytes sets data to value slots A & B.\n // Returns ErrSlotOverflow if slotA or slotB value are too big.\n setValueDataBytes(slotA: Uint8Array, slotB: Uint8Array): void {\n this._value[2] = this.setSlotBytes(slotA, SlotName.ValueA);\n this._value[3] = this.setSlotBytes(slotB, SlotName.ValueB);\n }\n // SetValueData sets data to value slots A & B.\n // Returns ErrSlotOverflow if slotA or slotB value are too big.\n setValueData(slotA: ElemBytes, slotB: ElemBytes): void {\n const slotsAsInts: bigint[] = [slotA.toBigInt(), slotB.toBigInt()];\n if (!checkBigIntArrayInField(slotsAsInts)) {\n throw Constants.ERRORS.DATA_OVERFLOW;\n }\n this._value[2] = slotA;\n this._value[3] = slotB;\n }\n // SetIndexDataInts sets data to index slots A & B.\n // Returns ErrSlotOverflow if slotA or slotB value are too big.\n setIndexDataInts(slotA: bigint | null, slotB: bigint | null): void {\n this._index[2] = this.setSlotInt(slotA, SlotName.IndexA);\n this._index[3] = this.setSlotInt(slotB, SlotName.IndexB);\n }\n // SetIndexDataBytes sets data to index slots A & B.\n // Returns ErrSlotOverflow if slotA or slotB value are too big.\n setIndexDataBytes(slotA: Uint8Array | null, slotB: Uint8Array | null): void {\n this._index[2] = this.setSlotBytes(slotA, SlotName.IndexA);\n this._index[3] = this.setSlotBytes(slotB, SlotName.IndexB);\n }\n\n private setSlotBytes(value: Uint8Array | null, slotName: SlotName): ElemBytes {\n const slot = new ElemBytes(value);\n if (!checkBigIntInField(slot.toBigInt())) {\n throw new ErrSlotOverflow(slotName);\n }\n return slot;\n }\n\n setFlagMerklized(s: MerklizedRootPosition): void {\n let f: number;\n switch (s) {\n case MerklizedRootPosition.Index:\n f = MerklizedFlag.Index;\n break;\n case MerklizedRootPosition.Value:\n f = MerklizedFlag.Value;\n break;\n default:\n f = MerklizedFlag.None;\n }\n // clean last 3 bits\n this.index[0].bytes[Flags.ByteIdx] &= 0b00011111;\n this.index[0].bytes[Flags.ByteIdx] |= f;\n }\n\n private getMerklized(): MerklizedFlag {\n let mt = this.index[0].bytes[Flags.ByteIdx];\n // clean all except last 3 bits\n mt &= 0b11100000;\n return mt as MerklizedFlag;\n }\n\n // GetMerklizedPosition returns the position at which the Merklized flag is stored.\n getMerklizedPosition(): MerklizedRootPosition {\n switch (this.getMerklized()) {\n case MerklizedFlag.None:\n return MerklizedRootPosition.None;\n case MerklizedFlag.Index:\n return MerklizedRootPosition.Index;\n case MerklizedFlag.Value:\n return MerklizedRootPosition.Value;\n default:\n throw Constants.ERRORS.INCORRECT_MERKLIZED_POSITION;\n }\n }\n\n public setSlotInt(value: bigint | null, slotName: SlotName): ElemBytes {\n if (!value) {\n value = BigInt(0);\n }\n if (!checkBigIntInField(value)) {\n throw new ErrSlotOverflow(slotName);\n }\n return new ElemBytes().setBigInt(value);\n }\n // SetIndexData sets data to index slots A & B.\n // Returns ErrSlotOverflow if slotA or slotB value are too big.\n setIndexData(slotA: ElemBytes, slotB: ElemBytes) {\n const slotsAsInts: bigint[] = [slotA.toBigInt(), slotB.toBigInt()];\n if (!checkBigIntArrayInField(slotsAsInts)) {\n throw Constants.ERRORS.DATA_OVERFLOW;\n }\n this._index[2] = slotA;\n this._index[3] = slotB;\n }\n\n resetExpirationDate(): void {\n this.setFlagExpiration(false);\n const bytes = Array.from({ length: Constants.NONCE_BYTES_LENGTH }, () => 0);\n const arr = Array.from(this._value[0].bytes);\n arr.splice(Constants.NONCE_BYTES_LENGTH, Constants.NONCE_BYTES_LENGTH, ...bytes);\n this._value[0] = new ElemBytes(Uint8Array.from(arr));\n }\n\n // GetExpirationDate returns expiration date and flag. Flag is true if\n // expiration date is present, false if null.\n getExpirationDate(): Date | null {\n if (this.getFlagExpiration()) {\n const unixTimestamp = getUint64(this._value[0].bytes.slice(8, 16));\n return getDateFromUnixTimestamp(Number(unixTimestamp));\n }\n return null;\n }\n\n // SetExpirationDate sets expiration date to dt\n setExpirationDate(dt: Date) {\n this.setFlagExpiration(true);\n const bytes = getBytesFromUint64(BigInt(getUnixTimestamp(dt)));\n const arr = Array.from(this._value[0].bytes);\n arr.splice(Constants.NONCE_BYTES_LENGTH, Constants.NONCE_BYTES_LENGTH, ...bytes);\n this._value[0] = new ElemBytes(Uint8Array.from(arr));\n }\n\n // GetRevocationNonce returns revocation nonce\n getRevocationNonce(): bigint {\n return getUint64(this._value[0].bytes.slice(0, 8));\n }\n // SetRevocationNonce sets claim's revocation nonce\n setRevocationNonce(nonce: bigint): void {\n const bytes = getBytesFromUint64(nonce);\n if (bytes.length > Constants.NONCE_BYTES_LENGTH) {\n throw new Error('Nonce length is not valid');\n }\n const arr = Array.from(this._value[0].bytes);\n arr.splice(0, Constants.NONCE_BYTES_LENGTH, ...bytes);\n this._value[0] = new ElemBytes(Uint8Array.from(arr));\n }\n\n getValueId(): Id {\n return Id.fromBytes(this._value[1].bytes.slice(0, -1));\n }\n\n // SetValueId sets id to value. Removes id from index if any.\n setValueId(id: Id): void {\n this.resetIndexId();\n this.setSubject(SubjectFlag.OtherIdenValue);\n const arr = Array.from(this._index[1].bytes);\n arr.splice(0, id.bytes.length, ...id.bytes);\n this._value[1] = new ElemBytes(Uint8Array.from(arr));\n }\n\n private resetIndexId() {\n this._index[1] = new ElemBytes(new Uint8Array(Constants.BYTES_LENGTH).fill(0));\n }\n\n private resetValueId(): void {\n this._value[1] = new ElemBytes(new Uint8Array(Constants.BYTES_LENGTH).fill(0));\n }\n\n getIndexId(): Id {\n return Id.fromBytes(this._index[1].bytes.slice(0, -1));\n }\n\n // SetIndexId sets id to index. Removes id from value if any.\n setIndexId(id: Id): void {\n this.resetValueId();\n this.setSubject(SubjectFlag.OtherIdenIndex);\n const arr = Array.from(this._index[1].bytes);\n arr.splice(0, id.bytes.length, ...id.bytes);\n this._index[1] = new ElemBytes(Uint8Array.from(arr));\n }\n // SetVersion sets claim's version\n setVersion(ver: number) {\n const bytes = putUint32(ver);\n this._index[0].bytes[20] = bytes[0];\n this._index[0].bytes[21] = bytes[1];\n this._index[0].bytes[22] = bytes[2];\n this._index[0].bytes[23] = bytes[3];\n }\n // GetVersion returns claim's version\n getVersion(): number {\n return getUint32(this._index[0].bytes.slice(20, 24));\n }\n // SetFlagUpdatable sets claim's flag `updatable`\n setFlagUpdatable(val: boolean) {\n if (val) {\n this._index[0].bytes[Flags.ByteIdx] |= 0b1 << Flags.UpdatableBitIdx;\n } else {\n this._index[0].bytes[Flags.ByteIdx] &= ~(0b1 << Flags.UpdatableBitIdx);\n }\n }\n\n // HIndex calculates the hash of the Index of the Claim\n hIndex(): bigint {\n return poseidon.hash(ElemBytes.elemBytesToInts(this._index));\n }\n\n // GetFlagUpdatable returns claim's flag `updatable`\n getFlagUpdatable(): boolean {\n const mask = 0b1 << Flags.UpdatableBitIdx;\n return (this._index[0].bytes[Flags.ByteIdx] & mask) > 0;\n }\n\n // HValue calculates the hash of the Value of the Claim\n hValue(): bigint {\n return poseidon.hash(ElemBytes.elemBytesToInts(this._value));\n }\n\n // HiHv returns the HIndex and HValue of the Claim\n hiHv(): { hi: bigint; hv: bigint } {\n return { hi: this.hIndex(), hv: this.hValue() };\n }\n\n // SetIndexMerklizedRoot sets merklized root to index. Removes root from value[2] if any.\n setIndexMerklizedRoot(r: bigint): void {\n this.resetValueMerklizedRoot();\n this.setFlagMerklized(MerklizedRootPosition.Index);\n this.index[2] = this.setSlotInt(r, SlotName.IndexA);\n }\n\n resetIndexMerklizedRoot() {\n this._index[2] = new ElemBytes(new Uint8Array(Constants.BYTES_LENGTH).fill(0));\n }\n\n // SetValueMerklizedRoot sets merklized root to value. Removes root from index[2] if any.\n setValueMerklizedRoot(r: bigint): void {\n this.resetIndexMerklizedRoot();\n this.setFlagMerklized(MerklizedRootPosition.Value);\n this.value[2] = this.setSlotInt(r, SlotName.ValueA);\n }\n resetValueMerklizedRoot() {\n this._value[2] = new ElemBytes(new Uint8Array(Constants.BYTES_LENGTH).fill(0));\n }\n\n // GetMerklizedRoot returns merklized root from claim's index of value.\n // Returns error ErrNoMerklizedRoot if MerklizedRoot is not set.\n getMerklizedRoot(): bigint {\n switch (this.getMerklized()) {\n case MerklizedFlag.Index:\n return this.index[2].toBigInt();\n case MerklizedFlag.Value:\n return this.value[2].toBigInt();\n default:\n throw Constants.ERRORS.NO_MERKLIZED_ROOT;\n }\n }\n\n // resetId deletes Id from index and from value.\n resetId(): void {\n this.resetIndexId();\n this.resetValueId();\n this.setSubject(SubjectFlag.Self);\n }\n // GetId returns Id from claim's index of value.\n // Returns error ErrNoId if Id is not set.\n getId(): Id {\n switch (this.getSubject()) {\n case SubjectFlag.OtherIdenIndex:\n return this.getIndexId();\n case SubjectFlag.OtherIdenValue:\n return this.getValueId();\n default:\n throw Constants.ERRORS.NO_ID;\n }\n }\n // RawSlots returns raw bytes of claim's index and value\n rawSlots(): { index: ElemBytes[]; value: ElemBytes[] } {\n return {\n index: this._index,\n value: this._value\n };\n }\n // RawSlotsAsInts returns slots as []bigint\n rawSlotsAsInts(): bigint[] {\n return [...ElemBytes.elemBytesToInts(this._index), ...ElemBytes.elemBytesToInts(this._value)];\n }\n\n clone(): Claim {\n return JSON.parse(JSON.stringify(this));\n }\n\n marshalJson(): string[] {\n return this.rawSlotsAsInts().map((b) => b.toString());\n }\n\n unMarshalJson(b: string): Claim {\n const ints: bigint[] = JSON.parse(b).map((s: string) => BigInt(s));\n\n if (ints.length !== this._index.length + this._value.length) {\n throw new Error(\"invalid number of claim's slots\");\n }\n this._index = [];\n this._value = [];\n for (let i = 0, j = Constants.ELEM_BYTES_LENGTH; i < ints.length / 2; i++, j++) {\n this._index[i] = new ElemBytes();\n this._index[i].setBigInt(ints[i]);\n this._value[i] = new ElemBytes();\n this._value[i].setBigInt(ints[j]);\n }\n return this;\n }\n\n marshalBinary(): Uint8Array {\n const getBytes = (src: ElemBytes[]) =>\n src.reduce((acc: number[], cur: ElemBytes) => {\n return [...acc, ...cur.bytes];\n }, []);\n return Uint8Array.from(getBytes(this._index).concat(getBytes(this._value)));\n }\n\n // Hex returns hex representation of binary claim\n hex(): string {\n const b = this.marshalBinary();\n return Hex.encodeString(b);\n }\n\n fromHex(hex: string): Claim {\n const b = Hex.decodeString(hex);\n this.unMarshalBinary(b);\n return this;\n }\n\n unMarshalBinary(data: Uint8Array): void {\n const wantLen = 2 * Constants.ELEM_BYTES_LENGTH * Constants.BYTES_LENGTH;\n if (data.length !== wantLen) {\n throw new Error('unexpected length of input data');\n }\n this._index = [];\n this._value = [];\n for (let i = 0, j = Constants.ELEM_BYTES_LENGTH; i < Constants.ELEM_BYTES_LENGTH; i++, j++) {\n this._index[i] = new ElemBytes(\n data.slice(i * Constants.BYTES_LENGTH, (i + 1) * Constants.BYTES_LENGTH)\n );\n this._value[i] = new ElemBytes(\n data.slice(j * Constants.BYTES_LENGTH, (j + 1) * Constants.BYTES_LENGTH)\n );\n }\n }\n}\n\n// Option provides the ability to set different Claim's fields on construction\nexport type ClaimOption = (c: Claim) => void;\nexport class ClaimOptions {\n // WithFlagUpdatable sets claim's flag `updatable`\n static withFlagUpdatable(val: boolean): ClaimOption {\n return (c: Claim) => c.setFlagUpdatable(val);\n }\n\n // WithVersion sets claim's version\n static withVersion(ver: number): ClaimOption {\n return (c: Claim) => c.setVersion(ver);\n }\n\n // WithIndexId sets Id to claim's index\n static withIndexId(id: Id): ClaimOption {\n return (c: Claim) => c.setIndexId(id);\n }\n\n // WithValueId sets Id to claim's value\n static withValueId(id: Id): ClaimOption {\n return (c: Claim) => c.setValueId(id);\n }\n\n // WithFlagMerklized sets claim's flag `merklized`\n static withFlagMerklized(p: MerklizedRootPosition): ClaimOption {\n return (c: Claim) => c.setFlagMerklized(p);\n }\n\n // WithId sets Id to claim's index or value depending on `pos`.\n static withId(id: Id, pos: IdPosition): ClaimOption {\n return (c: Claim) => {\n switch (pos) {\n case IdPosition.Index:\n c.setIndexId(id);\n break;\n case IdPosition.Value:\n c.setValueId(id);\n break;\n default:\n throw Constants.ERRORS.INCORRECT_ID_POSITION;\n }\n };\n }\n\n // WithRevocationNonce sets claim's revocation nonce.\n static withRevocationNonce(nonce: bigint): ClaimOption {\n return (c: Claim) => c.setRevocationNonce(nonce);\n }\n\n // WithExpirationDate sets claim's expiration date to `dt`.\n static withExpirationDate(dt: Date): ClaimOption {\n return (c: Claim) => c.setExpirationDate(dt);\n }\n\n // WithIndexData sets data to index slots A & B.\n // Returns ErrSlotOverflow if slotA or slotB value are too big.\n static withIndexData(slotA: ElemBytes, slotB: ElemBytes): ClaimOption {\n return (c: Claim) => c.setIndexData(slotA, slotB);\n }\n\n // WithIndexDataBytes sets data to index slots A & B.\n // Returns ErrSlotOverflow if slotA or slotB value are too big.\n static withIndexDataBytes(slotA: Uint8Array | null, slotB: Uint8Array | null): ClaimOption {\n return (c: Claim) => c.setIndexDataBytes(slotA, slotB);\n }\n\n // WithIndexDataInts sets data to index slots A & B.\n // Returns ErrSlotOverflow if slotA or slotB value are too big.\n static withIndexDataInts(slotA: bigint | null, slotB: bigint | null): ClaimOption {\n return (c: Claim) => c.setIndexDataInts(slotA, slotB);\n }\n\n // WithValueData sets data to value slots A & B.\n // Returns ErrSlotOverflow if slotA or slotB value are too big.\n static withValueData(slotA: ElemBytes, slotB: ElemBytes): ClaimOption {\n return (c: Claim) => c.setValueData(slotA, slotB);\n }\n\n // WithValueDataBytes sets data to value slots A & B.\n // Returns ErrSlotOverflow if slotA or slotB value are too big.\n static withValueDataBytes(slotA: Uint8Array, slotB: Uint8Array): ClaimOption {\n return (c: Claim) => c.setValueDataBytes(slotA, slotB);\n }\n\n // WithValueDataInts sets data to value slots A & B.\n // Returns ErrSlotOverflow if slotA or slotB value are too big.\n static withValueDataInts(slotA: bigint | null, slotB: bigint | null): ClaimOption {\n return (c: Claim) => c.setValueDataInts(slotA, slotB);\n }\n\n // WithIndexMerklizedRoot sets root to index i_2\n // Returns ErrSlotOverflow if root value are too big.\n static withIndexMerklizedRoot(r: bigint): ClaimOption {\n return (c: Claim) => {\n c.setFlagMerklized(MerklizedRootPosition.Index);\n c.index[2] = c.setSlotInt(r, SlotName.IndexA);\n };\n }\n\n // WithValueMerklizedRoot sets root to value v_2\n // Returns ErrSlotOverflow if root value are too big.\n static withValueMerklizedRoot(r: bigint): ClaimOption {\n return (c: Claim) => {\n c.setFlagMerklized(MerklizedRootPosition.Value);\n c.value[2] = c.setSlotInt(r, SlotName.ValueA);\n };\n }\n\n // WithMerklizedRoot sets root to value v_2 or index i_2\n // Returns ErrSlotOverflow if root value are too big.\n static withMerklizedRoot(r: bigint, pos: MerklizedRootPosition): ClaimOption {\n return (c: Claim) => {\n switch (pos) {\n case MerklizedRootPosition.Index:\n c.setFlagMerklized(MerklizedRootPosition.Index);\n c.index[2] = c.setSlotInt(r, SlotName.IndexA);\n break;\n case MerklizedRootPosition.Value:\n c.setFlagMerklized(MerklizedRootPosition.Value);\n c.value[2] = c.setSlotInt(r, SlotName.ValueA);\n break;\n default:\n throw Constants.ERRORS.INCORRECT_MERKLIZED_POSITION;\n }\n };\n }\n}\n", "import { Constants, DidMethodByte, DidMethodNetwork } from '../constants';\n\n// DIDNetworkFlag is a structure to represent DID blockchain and network id\nexport class DIDNetworkFlag {\n constructor(public readonly blockchain: string, public readonly networkId: string) {}\n\n toString(): string {\n return `${this.blockchain}:${this.networkId}`;\n }\n\n static fromString(s: string): DIDNetworkFlag {\n const [blockchain, networkId] = s.split(':');\n return new DIDNetworkFlag(blockchain.replace('_', ''), networkId.replace('_', ''));\n }\n}\n\n// BuildDIDType builds bytes type from chain and network\nexport function buildDIDType(method: string, blockchain: string, network: string): Uint8Array {\n const fb = DidMethodByte[method];\n if (!fb) {\n throw Constants.ERRORS.UNSUPPORTED_DID_METHOD;\n }\n const methodFn = DidMethodNetwork[method];\n if (!methodFn) {\n throw Constants.ERRORS.NETWORK_NOT_SUPPORTED_FOR_DID;\n }\n\n const sb: number | undefined = methodFn[new DIDNetworkFlag(blockchain, network).toString()];\n\n if (typeof sb !== 'number') {\n throw new Error(\n `blockchain ${blockchain.toString() ?? '-'} and network ${\n network.toString() ?? '-'\n } is not defined in core lib`\n );\n }\n\n return Uint8Array.from([fb, sb]);\n}\n\n// FindNetworkIDForDIDMethodByValue finds network by byte value\nexport function findNetworkIDForDIDMethodByValue(method: string, byteNumber: number): string {\n const methodMap = DidMethodNetwork[method];\n if (!methodMap) {\n throw Constants.ERRORS.UNSUPPORTED_DID_METHOD;\n }\n for (const [key, value] of Object.entries(methodMap)) {\n if (value === byteNumber) {\n return DIDNetworkFlag.fromString(key).networkId;\n }\n }\n throw Constants.ERRORS.NETWORK_NOT_SUPPORTED_FOR_DID;\n}\n\n// findBlockchainForDIDMethodByValue finds blockchain type by byte value\nexport function findBlockchainForDIDMethodByValue(method: string, byteNumber: number): string {\n const methodMap = DidMethodNetwork[method];\n if (!methodMap) {\n throw new Error(\n `${Constants.ERRORS.NETWORK_NOT_SUPPORTED_FOR_DID}: did method ${method} is not defined in core lib`\n );\n }\n for (const [key, value] of Object.entries(methodMap)) {\n if (value === byteNumber) {\n return DIDNetworkFlag.fromString(key).blockchain;\n }\n }\n throw Constants.ERRORS.UNSUPPORTED_BLOCKCHAIN_FOR_DID;\n}\n\n// findDIDMethodByValue finds did method by its byte value\nexport function findDIDMethodByValue(byteNumber: number): string {\n for (const [key, value] of Object.entries(DidMethodByte)) {\n if (value === byteNumber) {\n return key;\n }\n }\n throw Constants.ERRORS.UNSUPPORTED_DID_METHOD;\n}\n", "export class Param {\n constructor(public name: string, public value: string) {}\n\n toString(): string {\n if (!this.name) {\n return '';\n }\n if (!this.value) {\n return this.name;\n }\n return `${this.name}=${this.value}`;\n }\n}\n\nexport interface IDID {\n method: string;\n id: string;\n idStrings: string[];\n params: Param[];\n path: string;\n pathSegments: string[];\n query: string;\n fragment: string;\n}\n\nexport const initDIDParams: IDID = Object.freeze({\n method: '',\n id: '',\n idStrings: [],\n params: [],\n path: '',\n pathSegments: [],\n query: '',\n fragment: ''\n});\n", "import { IDID, Param, initDIDParams } from './types';\nimport { StringUtils } from '../utils';\n\n// a step in the parser state machine that returns the next step\ntype ParserStep = () => ParserStep | null;\n\nexport class Parser {\n currentIndex = 0; // index in the input which the parser is currently processing:\n out: IDID = { ...initDIDParams }; // the output DID that the parser will assemble as it steps through its state machine // an error in the parser state machine\n\n constructor(private readonly input: string) {}\n\n checkLength(): ParserStep | null {\n const inputLength = this.input.length;\n\n if (inputLength < 7) {\n throw new Error('input length is less than 7');\n }\n\n return this.parseScheme.bind(this);\n }\n\n // parseScheme is a parserStep that validates that the input begins with 'did:'\n parseScheme(): ParserStep | null {\n const currentIndex = 3; // 4 bytes in 'did:', i.e index 3\n // the grammar requires `did:` prefix\n if (this.input.slice(0, currentIndex + 1) !== 'did:') {\n throw new Error(\"input does not begin with 'did:' prefix\");\n }\n\n this.currentIndex = currentIndex;\n return this.parseMethod.bind(this);\n }\n\n parseMethod(): ParserStep | null {\n const input = this.input;\n const inputLength = input.length;\n let currentIndex = this.currentIndex + 1;\n const startIndex = currentIndex;\n\n for (;;) {\n if (currentIndex === inputLength) {\n // we got to the end of the input and didn't find a second ':'\n throw new Error('input does not have a second `:` marking end of method name');\n }\n\n // read the input character at currentIndex\n const char = input[currentIndex];\n\n if (char === ':') {\n // we've found the second : in the input that marks the end of the method\n if (currentIndex === startIndex) {\n // return error is method is empty, ex- did::1234\n throw new Error(`method is empty, ${currentIndex}`);\n }\n break;\n }\n\n // as per the grammar method can only be made of digits 0-9 or small letters a-z\n if (StringUtils.isNotDigit(char) && StringUtils.isNotSmallLetter(char)) {\n throw new Error(`\"character is not a-z OR 0-9, ${currentIndex}`);\n }\n\n // move to the next char\n currentIndex = currentIndex + 1;\n }\n\n // set parser state\n this.currentIndex = currentIndex;\n this.out.method = input.slice(startIndex, currentIndex);\n\n // method is followed by specific-idstring, parse that next\n return this.parseId.bind(this);\n }\n\n parseId(): ParserStep | null {\n const input = this.input;\n const inputLength = input.length;\n let currentIndex = this.currentIndex + 1;\n const startIndex = currentIndex;\n\n let next: ParserStep | null = null;\n\n for (;;) {\n if (currentIndex === inputLength) {\n // we've reached end of input, no next state\n next = null;\n break;\n }\n\n const char = input[currentIndex];\n\n if (char === ':') {\n // encountered : input may have another idstring, parse ID again\n next = this.parseId;\n break;\n }\n\n if (char === ';') {\n // encountered ; input may have a parameter, parse that next\n next = this.parseParamName;\n break;\n }\n\n if (char === '/') {\n // encountered / input may have a path following specific-idstring, parse that next\n next = this.parsePath;\n break;\n }\n\n if (char === '?') {\n // encountered ? input may have a query following specific-idstring, parse that next\n next = this.parseQuery;\n break;\n }\n\n if (char