UNPKG

@arcblock/did

Version:
244 lines (243 loc) 8.83 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.toChecksumAddress = exports.isEthereumDid = exports.isEthereumType = exports.toTypeInfoStr = exports.toTypeInfo = exports.fromTypeInfo = exports.DID_TYPE_PASSKEY = exports.DID_TYPE_ETHEREUM = exports.DID_TYPE_ARCBLOCK = void 0; exports.DidType = DidType; /* eslint-disable @typescript-eslint/no-use-before-define */ /* eslint-disable @typescript-eslint/ban-ts-comment */ const util_1 = require("@ocap/util"); const upperFirst_1 = __importDefault(require("lodash/upperFirst")); const isEqual_1 = __importDefault(require("lodash/isEqual")); const pick_1 = __importDefault(require("lodash/pick")); const mcrypto_1 = require("@ocap/mcrypto"); const util_2 = require("./util"); const mapping = { pk: 'key', address: 'encoding', }; const DID_TYPE_ARCBLOCK = { role: mcrypto_1.types.RoleType.ROLE_ACCOUNT, pk: mcrypto_1.types.KeyType.ED25519, hash: mcrypto_1.types.HashType.SHA3, address: mcrypto_1.types.EncodingType.BASE58, }; exports.DID_TYPE_ARCBLOCK = DID_TYPE_ARCBLOCK; const DID_TYPE_PASSKEY = { role: mcrypto_1.types.RoleType.ROLE_PASSKEY, pk: mcrypto_1.types.KeyType.PASSKEY, hash: mcrypto_1.types.HashType.SHA2, address: mcrypto_1.types.EncodingType.BASE58, }; exports.DID_TYPE_PASSKEY = DID_TYPE_PASSKEY; const DID_TYPE_ETHEREUM = { role: mcrypto_1.types.RoleType.ROLE_ACCOUNT, pk: mcrypto_1.types.KeyType.ETHEREUM, hash: mcrypto_1.types.HashType.KECCAK, address: mcrypto_1.types.EncodingType.BASE16, }; exports.DID_TYPE_ETHEREUM = DID_TYPE_ETHEREUM; const isEthereumType = (type) => { const props = ['pk', 'hash', 'address']; return (0, isEqual_1.default)((0, pick_1.default)(type, props), (0, pick_1.default)(DID_TYPE_ETHEREUM, props)); }; exports.isEthereumType = isEthereumType; /** * Checks if the given string is an address * * @method isEthereumDid * @param {String} address the given HEX address * @return {Boolean} */ const isEthereumDid = (did) => { const address = (0, util_1.toAddress)(did); // check if it has the basic requirements of an address if (!/^(0x)?[0-9a-f]{40}$/i.test(address)) { return false; } // If it's ALL lowercase or ALL upppercase if (/^(0x|0X)?[0-9a-f]{40}$/.test(address) || /^(0x|0X)?[0-9A-F]{40}$/.test(address)) { return true; } // Otherwise check each case return checkAddressChecksum(address); }; exports.isEthereumDid = isEthereumDid; /** * Checks if the given string is a checksummed address * * @method checkAddressChecksum * @param {String} address the given HEX address * @return {Boolean} */ const checkAddressChecksum = (address) => { // Check each case const origin = address.replace(/^0x/i, ''); const addressHash = mcrypto_1.Hasher.Keccak.hash256(origin.toLowerCase()).replace(/^0x/i, ''); for (let i = 0; i < 40; i++) { // the nth letter should be uppercase if the nth digit of casemap is 1 if ((parseInt(addressHash[i], 16) > 7 && origin[i].toUpperCase() !== origin[i]) || (parseInt(addressHash[i], 16) <= 7 && origin[i].toLowerCase() !== origin[i])) { return false; } } return true; }; /** * Converts to a checksum address * * @method toChecksumAddress * @param {String} address the given HEX address * @return {String} */ const toChecksumAddress = (address) => { if (typeof address === 'undefined') { return ''; } if (!/^(0x)?[0-9a-f]{40}$/i.test(address)) { throw new Error(`Given address "${address}" is not a valid Ethereum address.`); } const lower = address.toLowerCase().replace(/^0x/i, ''); const addressHash = mcrypto_1.Hasher.Keccak.hash256(lower).replace(/^0x/i, ''); let checksumAddress = '0x'; for (let i = 0; i < lower.length; i++) { // If ith character is 8 to f then make it uppercase if (parseInt(addressHash[i], 16) > 7) { checksumAddress += lower[i].toUpperCase(); } else { checksumAddress += lower[i]; } } return checksumAddress; }; exports.toChecksumAddress = toChecksumAddress; /** * Create an wallet type object that be used as param to create a new wallet */ function DidType(type = {}) { let input = {}; if (['default', 'arcblock'].includes(type)) { input = DID_TYPE_ARCBLOCK; } else if (['eth', 'ethereum'].includes(type)) { input = DID_TYPE_ETHEREUM; } else if (['passkey'].includes(type)) { input = DID_TYPE_PASSKEY; } else { input = { ...DID_TYPE_ARCBLOCK, ...type }; } const { role, pk, hash, address } = input; Object.keys(input).forEach((x) => { const key = (0, upperFirst_1.default)(`${mapping[x] || x}Type`); // @ts-ignore if (Object.values(mcrypto_1.types[key]).includes(input[x]) === false) { throw new Error(`Unsupported ${x} type: ${input[x]}`); } }); const output = { role, pk, hash, address }; // Overwrite hash methods for some role type if we are not eth-type if (isEthereumType(output) === false) { const sha2Roles = [ mcrypto_1.types.RoleType.ROLE_NODE, mcrypto_1.types.RoleType.ROLE_VALIDATOR, mcrypto_1.types.RoleType.ROLE_TETHER, mcrypto_1.types.RoleType.ROLE_SWAP, ]; if (sha2Roles.includes(output.role)) { output.hash = mcrypto_1.types.HashType.SHA2; } } return output; } DidType.toJSON = (type) => Object.keys(type).reduce((acc, x) => { const key = (0, upperFirst_1.default)(`${mapping[x] || x}Type`); // @ts-ignore const typeStr = Object.keys(mcrypto_1.types[key]); // @ts-ignore const typeValues = Object.values(mcrypto_1.types[key]); // @ts-ignore acc[x] = typeStr[typeValues.indexOf(type[x])]; return acc; }, {}); DidType.fromJSON = (json) => Object.keys(json).reduce((acc, x) => { const key = (0, upperFirst_1.default)(`${mapping[x] || x}Type`); // @ts-ignore const typeStr = Object.keys(mcrypto_1.types[key]); // @ts-ignore const typeValues = Object.values(mcrypto_1.types[key]); // @ts-ignore acc[x] = typeValues[typeStr.indexOf(json[x])]; return acc; }, {}); /** * Convert type info object to hex string * * @public * @static * @param {object} type - wallet type, {@see @arcblock/did#DidType} * @returns string */ const fromTypeInfo = (type) => { const info = DidType(type); const roleBits = (0, util_2.toBits)(info.role, 6); const keyBits = (0, util_2.toBits)(info.pk, 5); const hashBits = (0, util_2.toBits)(info.hash, 5); const infoBits = `${roleBits}${keyBits}${hashBits}`; const infoHex = (0, util_1.stripHexPrefix)((0, util_1.numberToHex)(parseInt(infoBits, 2))); return (0, util_2.toStrictHex)(infoHex, 4); }; exports.fromTypeInfo = fromTypeInfo; /** * Get type info from did */ const toTypeInfo = (did) => { let type = {}; try { if (isEthereumDid(did)) { type = DID_TYPE_ETHEREUM; } else { const bytes = (0, util_2.toBytes)(did); const typeBytes = bytes.slice(0, 2); const typeHex = (0, util_2.toStrictHex)(Buffer.from(typeBytes).toString('hex')); const typeBits = (0, util_2.toBits)(new util_1.BN(typeHex, 16), 16); const roleBits = typeBits.slice(0, 6); const keyBits = typeBits.slice(6, 11); const hashBits = typeBits.slice(11, 16); type = { role: parseInt(roleBits, 2), pk: parseInt(keyBits, 2), hash: parseInt(hashBits, 2), address: (0, util_1.isHex)((0, util_1.toAddress)(did)) ? mcrypto_1.types.EncodingType.BASE16 : mcrypto_1.types.EncodingType.BASE58, }; } // remove unsupported types Object.keys(type).forEach((x) => { // @ts-ignore const enums = Object.values(mcrypto_1.types[`${(0, upperFirst_1.default)(mapping[x] || x)}Type`]); if (enums.includes(type[x]) === false) { delete type[x]; } }); return type; } catch { return type; } }; exports.toTypeInfo = toTypeInfo; const toTypeInfoStr = (did) => { const type = toTypeInfo(did); return Object.keys(type).reduce((acc, x) => { // @ts-ignore const enums = mcrypto_1.types[`${(0, upperFirst_1.default)(mapping[x] || x)}Type`]; // @ts-ignore acc[x] = Object.keys(enums).find((d) => enums[d] === type[x]); return acc; }, {}); }; exports.toTypeInfoStr = toTypeInfoStr;