UNPKG

@radixdlt/account

Version:

A JavaScript client library for interacting with the Radix Distributed Ledger.

191 lines 9.47 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ResourceIdentifier = exports.isResourceIdentifierOrUnsafeInput = exports.isResourceIdentifierUnsafeInput = exports.isResourceIdentifier = void 0; const neverthrow_1 = require("neverthrow"); const util_1 = require("@radixdlt/util"); const crypto_1 = require("@radixdlt/crypto"); const primitives_1 = require("@radixdlt/primitives"); const bech32_1 = require("../bech32"); const encoding = bech32_1.Encoding.BECH32; const maxLength = undefined; // arbitrarily chosen const versionByteNativeToken = 0x01; const versionByteNonNativeToken = 0x03; const hrpSuffixFromNetwork = (network) => primitives_1.HRP[network].RRI_suffix; const networkFromHRPSuffix = (hrp) => hrp === primitives_1.HRP.mainnet.RRI_suffix ? (0, neverthrow_1.ok)(primitives_1.Network.MAINNET) : hrp === primitives_1.HRP.stokenet.RRI_suffix ? (0, neverthrow_1.ok)(primitives_1.Network.STOKENET) : hrp === primitives_1.HRP.localnet.RRI_suffix ? (0, neverthrow_1.ok)(primitives_1.Network.LOCALNET) : hrp === primitives_1.HRP.releasenet.RRI_suffix ? (0, neverthrow_1.ok)(primitives_1.Network.RELEASENET) : hrp === primitives_1.HRP.rcnet.RRI_suffix ? (0, neverthrow_1.ok)(primitives_1.Network.RCNET) : hrp === primitives_1.HRP.milestonenet.RRI_suffix ? (0, neverthrow_1.ok)(primitives_1.Network.MILESTONENET) : hrp === primitives_1.HRP.testnet6.RRI_suffix ? (0, neverthrow_1.ok)(primitives_1.Network.TESTNET6) : hrp === primitives_1.HRP.sandpitnet.RRI_suffix ? (0, neverthrow_1.ok)(primitives_1.Network.SANDPITNET) : (0, neverthrow_1.err)(new Error(`Failed to parse network from HRP ${hrp} for ValidatorAddress.`)); const __create = (input) => (Object.assign(Object.assign({}, input), { __witness: 'isRRI', equals: (other) => { if (!(0, exports.isResourceIdentifier)(other)) return false; const same = other.name === input.name && (0, util_1.buffersEquals)(other.hash, input.hash) && input.network === other.network; if (same) { if (other.toString() !== input.toString()) { const errMsg = `ResourceIdentifiers believed to be equal, but return different values when calling toString, (this)'${input.toString()}' vs other: '${other.toString()}'`; console.error(errMsg); throw new Error(errMsg); } } return same; } })); const fromBech32String = (bechString) => { // const hrpSuffix = hrpBetanetSuffix // TODO make dependent on Network! const decodingResult = bech32_1.Bech32.decode({ bechString, encoding, maxLength }); if (!decodingResult.isOk()) { const errMsg = `Failed to Bech32 decode RRI, underlying error: ${(0, util_1.msgFromError)(decodingResult.error)}`; return (0, neverthrow_1.err)(new Error(errMsg)); } const decoded = decodingResult.value; const hrp = decoded.hrp; if (!Object.keys(primitives_1.HRP).some(network => hrp.endsWith(primitives_1.HRP[network].RRI_suffix))) { const errMsg = `suffix found for hrp "${hrp}" not supported.`; return (0, neverthrow_1.err)(new Error(errMsg)); } const nameToValidate = hrp.split('_')[0]; const hrpSuffix = '_' + hrp.split('_')[1]; const networkResult = networkFromHRPSuffix(hrpSuffix); if (!networkResult.isOk()) { const errMsg = `Expected to get network from HRP suffix '${hrpSuffix}', but failed to get it.`; return (0, neverthrow_1.err)(new Error(errMsg)); } const network = networkResult.value; const nameValidationResult = validateCharsInName(nameToValidate); if (!nameValidationResult.isOk()) { return (0, neverthrow_1.err)(nameValidationResult.error); } const name = nameValidationResult.value; const processed = decoded.data; const combinedDataResult = bech32_1.Bech32.convertDataFromBech32(processed); if (!combinedDataResult.isOk()) { const errMsg = `Failed to convertDataFromBech32 data, underlying error: ${(0, util_1.msgFromError)(combinedDataResult.error)}`; console.error(errMsg); return (0, neverthrow_1.err)(new Error(errMsg)); } const combinedData = combinedDataResult.value; if (combinedData.length === 0) { const errMsg = `The data part of RRI should NEVER be empty, must at least contain 1 version byte ('${versionByteNativeToken}' for native token, or '${versionByteNonNativeToken}' for other tokens)`; console.error(errMsg); return (0, neverthrow_1.err)(new Error(errMsg)); } const versionByte = combinedData[0]; if (!(versionByte === versionByteNativeToken || versionByte === versionByteNonNativeToken)) { const errMsg = `The version byte must be either: '${versionByteNativeToken}' for native token, or '${versionByteNonNativeToken}' for other tokens, but got: ${versionByte}, bechString: '${bechString}'`; console.error(errMsg); return (0, neverthrow_1.err)(new Error(errMsg)); } const isNativeToken = versionByte === versionByteNativeToken; if (isNativeToken) { if (combinedData.length > 1) { const errMsg = `Expected data to be empty for native token, but got: #${combinedData.length - 1 // minus 1 because we substract the 'versionByte' } bytes`; console.error(errMsg); return (0, neverthrow_1.err)(new Error(errMsg)); } } else { if (combinedData.length <= 1) { const errMsg = `Expected data to be non empty for non native token`; console.error(errMsg); return (0, neverthrow_1.err)(new Error(errMsg)); } } return (0, neverthrow_1.ok)(__create({ hash: combinedData, network, name, toString: () => bechString, })); }; const validateCharsInName = (name) => { const regexLowerAlphaNumerics = new RegExp('^[a-z0-9]+$'); if (!regexLowerAlphaNumerics.test(name)) { const errMsg = `Illegal characters found in name`; // console.error(errMsg) return (0, neverthrow_1.err)(new Error(errMsg)); } return (0, neverthrow_1.ok)(name); }; const withNameRawDataAndVersionByte = (input) => { const { versionByte, hash, network } = input; const hrpSuffix = hrpSuffixFromNetwork(network); return validateCharsInName(input.name).andThen(name => { const hrp = `${name}${hrpSuffix}`; const combinedData = Buffer.concat([Buffer.from([versionByte]), hash]); return bech32_1.Bech32.convertDataToBech32(combinedData) .andThen(processed => bech32_1.Bech32.encode({ data: processed, hrp, encoding, maxLength, })) .map(bech32 => __create({ hash, network, name, toString: () => bech32.toString(), })); }); }; const systemRRIForNetwork = (input) => withNameRawDataAndVersionByte(Object.assign(Object.assign({}, input), { versionByte: versionByteNativeToken, hash: Buffer.alloc(0) })); const hashByteCount = 26; const pkToHash = (input) => { const { name, publicKey } = input; const nameBytes = Buffer.from(name, 'utf8'); const pubKeyBytes = publicKey.asData({ compressed: true }); const dataToHash = Buffer.concat([pubKeyBytes, nameBytes]); const hash = (0, crypto_1.sha256Twice)(dataToHash); return hash.slice(-hashByteCount); // last bytes }; const fromPublicKeyAndNameAndNetwork = (input) => withNameRawDataAndVersionByte(Object.assign(Object.assign({}, input), { versionByte: versionByteNonNativeToken, hash: pkToHash(input) })); const fromBuffer = (buffer) => { if (buffer.length === 1 && buffer[0] === 0x01) { return systemRRIForNetwork({ name: 'xrd', network: primitives_1.Network.MAINNET, // Yikes! }); } return (0, neverthrow_1.err)(new Error('Failed to create non XRD RRI because we do not have access to the HRP.')); }; const isResourceIdentifier = (something) => { const inspection = something; return ( // inspection.hash !== undefined && inspection.__witness !== undefined && inspection.__witness === 'isRRI' && inspection.name !== undefined && inspection.toString !== undefined && inspection.equals !== undefined); }; exports.isResourceIdentifier = isResourceIdentifier; const isResourceIdentifierUnsafeInput = (something) => typeof something === 'string' || Buffer.isBuffer(something); exports.isResourceIdentifierUnsafeInput = isResourceIdentifierUnsafeInput; const isResourceIdentifierOrUnsafeInput = (something) => (0, exports.isResourceIdentifier)(something) || (0, exports.isResourceIdentifierUnsafeInput)(something); exports.isResourceIdentifierOrUnsafeInput = isResourceIdentifierOrUnsafeInput; const fromUnsafe = (input) => (0, exports.isResourceIdentifier)(input) ? (0, neverthrow_1.ok)(input) : typeof input === 'string' ? fromBech32String(input) : fromBuffer(input); exports.ResourceIdentifier = { systemRRIForNetwork, fromPublicKeyAndNameAndNetwork, fromUnsafe, }; //# sourceMappingURL=resourceIdentifier.js.map