UNPKG

@logosnetwork/logos-webwallet-sdk

Version:

Create Logos wallets with or without a full Logos node

542 lines (492 loc) 17.3 kB
import Blake2b from '../Utils/blake2b' import { randomBytes, createCipheriv, createDecipheriv } from 'crypto' import { Controller as RpcController, Settings as RpcSettings, Privileges as RpcPrivileges } from '@logosnetwork/logos-rpc-client/api' import { Controller, Settings, Privileges } from '../TokenAccount' export const minimumFee = '10000000000000000000000' export const EMPTY_WORK = '0000000000000000' export const GENESIS_HASH = '0000000000000000000000000000000000000000000000000000000000000000' export const MAXUINT128 = '340282366920938463463374607431768211455' export const defaultRPC = { proxy: 'https://pla.bs', nodeURL: '3.215.28.211', nodePort: '55000', wsPort: '18000' } export const defaultMQTT = 'wss://pla.bs:8443' export const Iso10126 = { pad: (dataBytes: Buffer, nBytesPerBlock: number): Buffer => { const nPaddingBytes = nBytesPerBlock - dataBytes.length % nBytesPerBlock const paddingBytes = randomBytes(nPaddingBytes - 1) const endByte = Buffer.from([nPaddingBytes]) return Buffer.concat([dataBytes, paddingBytes, endByte]) }, unpad: (dataBytes: Buffer): Buffer => { const nPaddingBytes = dataBytes[dataBytes.length - 1] return dataBytes.slice(0, -nPaddingBytes) } } export const convertObjectToArray = (myObject: Privileges | Settings): string[] => { const myArray = [] for (const key in myObject) { if (myObject[key] === true) { myArray.push(key) } } return myArray } export const deserializeController = (controller: RpcController | Controller): Controller => { const defaultPrivileges = { change_issuance: false, change_modify_issuance: false, change_revoke: false, change_modify_revoke: false, change_freeze: false, change_modify_freeze: false, change_adjust_fee: false, change_modify_adjust_fee: false, change_whitelist: false, change_modify_whitelist: false, issuance: false, revoke: false, freeze: false, adjust_fee: false, whitelist: false, update_issuer_info: false, update_controller: false, burn: false, distribute: false, withdraw_fee: false, withdraw_logos: false } const newController: Controller = {} newController.account = controller.account let privileges = defaultPrivileges if (controller.privileges instanceof Array) { if (controller.privileges.length > 0) { privileges.change_issuance = controller.privileges.indexOf('change_issuance') > -1 privileges.change_modify_issuance = controller.privileges.indexOf('change_modify_issuance') > -1 privileges.change_revoke = controller.privileges.indexOf('change_revoke') > -1 privileges.change_modify_revoke = controller.privileges.indexOf('change_modify_revoke') > -1 privileges.change_freeze = controller.privileges.indexOf('change_freeze') > -1 privileges.change_modify_freeze = controller.privileges.indexOf('change_modify_freeze') > -1 privileges.change_adjust_fee = controller.privileges.indexOf('change_adjust_fee') > -1 privileges.change_modify_adjust_fee = controller.privileges.indexOf('change_modify_adjust_fee') > -1 privileges.change_whitelist = controller.privileges.indexOf('change_whitelist') > -1 privileges.change_modify_whitelist = controller.privileges.indexOf('change_modify_whitelist') > -1 privileges.issuance = controller.privileges.indexOf('issuance') > -1 privileges.revoke = controller.privileges.indexOf('revoke') > -1 privileges.freeze = controller.privileges.indexOf('freeze') > -1 privileges.adjust_fee = controller.privileges.indexOf('adjust_fee') > -1 privileges.whitelist = controller.privileges.indexOf('whitelist') > -1 privileges.update_issuer_info = controller.privileges.indexOf('update_issuer_info') > -1 privileges.update_controller = controller.privileges.indexOf('update_controller') > -1 privileges.burn = controller.privileges.indexOf('burn') > -1 privileges.distribute = controller.privileges.indexOf('distribute') > -1 privileges.withdraw_fee = controller.privileges.indexOf('withdraw_fee') > -1 privileges.withdraw_logos = controller.privileges.indexOf('withdraw_logos') > -1 } } else if (typeof controller.privileges === 'object' && controller.privileges !== null) { privileges = controller.privileges } newController.privileges = privileges return newController } export const deserializeControllers = (controllers: RpcController[] | Controller[]): Controller[] => { const newControllers = [] for (const controller of controllers) { newControllers.push(deserializeController(controller)) } return newControllers } export const serializeController = (controllerObject: Controller): RpcController => { return { account: controllerObject.account, privileges: convertObjectToArray(controllerObject.privileges) as RpcPrivileges[] } } export const serializeControllers = (controllersObject: Controller[]): RpcController[] => { const controllers = [] for (const controller of controllersObject) { controllers.push(serializeController(controller)) } return controllers } export const deserializeSettings = (settings: RpcSettings[] | Settings): Settings => { const defaulSettings = { issuance: false, modify_issuance: false, revoke: false, modify_revoke: false, freeze: false, modify_freeze: false, adjust_fee: false, modify_adjust_fee: false, whitelist: false, modify_whitelist: false } if (settings instanceof Array) { if (settings.length > 0) { return { issuance: settings.indexOf('issuance') > -1, modify_issuance: settings.indexOf('modify_issuance') > -1, revoke: settings.indexOf('revoke') > -1, modify_revoke: settings.indexOf('modify_revoke') > -1, freeze: settings.indexOf('freeze') > -1, modify_freeze: settings.indexOf('modify_freeze') > -1, adjust_fee: settings.indexOf('adjust_fee') > -1, modify_adjust_fee: settings.indexOf('modify_adjust_fee') > -1, whitelist: settings.indexOf('whitelist') > -1, modify_whitelist: settings.indexOf('modify_whitelist') > -1 } } } else if (typeof settings === 'object' && settings !== null) { return settings } return defaulSettings } interface Options { mode?: 'aes-256-cbc' | 'aes-256-ofb' | 'aes-256-ecb'; padding?: { pad: (dataBytes: Buffer, nBytesPerBlock: number) => Buffer; unpad: (dataBytes: Buffer) => Buffer; }; } interface AES { CBC: 'aes-256-cbc'; OFB: 'aes-256-ofb'; ECB: 'aes-256-ecb'; encrypt: (dataBytes: Buffer, key: Buffer, salt: Buffer, options: Options) => Buffer; decrypt: (dataBytes: Buffer, key: Buffer, salt: Buffer, options: Options) => Buffer; } export const AES: AES = { CBC: 'aes-256-cbc', OFB: 'aes-256-ofb', ECB: 'aes-256-ecb', encrypt: (dataBytes: Buffer, key: Buffer, salt: Buffer, options: Options): Buffer => { options = options || {} const cipher = createCipheriv(options.mode || AES.CBC, key, salt || '') cipher.setAutoPadding(!options.padding) const BLOCK_BIT_LEN = 128 if (options.padding) dataBytes = options.padding.pad(dataBytes, BLOCK_BIT_LEN / 8) const encryptedBytes = Buffer.concat([cipher.update(dataBytes), cipher.final()]) return encryptedBytes }, decrypt: (dataBytes: Buffer, key: Buffer, salt: Buffer = null, options: Options): Buffer => { options = options || {} const decipher = createDecipheriv(options.mode || AES.CBC, key, salt || '') decipher.setAutoPadding(!options.padding) let decryptedBytes = Buffer.concat([decipher.update(dataBytes), decipher.final()]) if (options.padding) decryptedBytes = options.padding.unpad(decryptedBytes) return decryptedBytes } } /** * Encode provided Uint8Array using the Base-32 implementeation. * @param {Uint8Array} view Input buffer formatted as a Uint8Array * @returns {string} */ const encode = (view: Uint8Array): string => { const length = view.length const leftover = (length * 8) % 5 const offset = leftover === 0 ? 0 : 5 - leftover const alphabet = '13456789abcdefghijkmnopqrstuwxyz' let value = 0 let output = '' let bits = 0 for (let i = 0; i < length; i++) { value = (value << 8) | view[i] bits += 8 while (bits >= 5) { output += alphabet[(value >>> (bits + offset - 5)) & 31] bits -= 5 } } if (bits > 0) { output += alphabet[(value << (5 - (bits + offset))) & 31] } return output } const readChar = (char: string): number => { const alphabet = '13456789abcdefghijkmnopqrstuwxyz' const idx = alphabet.indexOf(char) if (idx === -1) { throw new Error('Invalid character found: ' + char) } return idx } /** * Decodes an Implementation Base32 encoded string into a Uint8Array * @param {string} input A Base32 encoded string * @returns {Uint8Array} */ const decode = (input: string): Uint8Array => { if (typeof input !== 'string') { throw new Error('Input must be a string!') } const length = input.length const leftover = (length * 5) % 8 const offset = leftover === 0 ? 0 : 8 - leftover let bits = 0 let value = 0 let index = 0 let output = new Uint8Array(Math.ceil(length * 5 / 8)) for (let i = 0; i < length; i++) { value = (value << 5) | readChar(input[i]) bits += 5 if (bits >= 8) { output[index++] = (value >>> (bits + offset - 8)) & 255 bits -= 8 } } if (bits > 0) { output[index++] = (value << (bits + offset - 8)) & 255 } if (leftover !== 0) { output = output.slice(1) } return output } export const stringFromHex = (hex: string): string => { const stringHex = hex.toString() // force conversion let str = '' for (let i = 0; i < stringHex.length; i += 2) { str += String.fromCharCode(parseInt(stringHex.substr(i, 2), 16)) } return str } export const stringToHex = (str: string): string => { let hex = '' for (let i = 0; i < str.length; i++) { hex += '' + str.charCodeAt(i).toString(16) } return hex } export const changeEndianness = (data: string): string => { const result = [] let len = data.length - 2 while (len >= 0) { result.push(data.substr(len, 2)) len -= 2 } return result.join('') } export const decToHex = (str: string | number, bytes: number = null): string => { const dec = str.toString().split('') const sum = [] const hex = [] let i let s while (dec.length) { s = parseInt(dec.shift()) for (i = 0; s || i < sum.length; i++) { s += (sum[i] || 0) * 10 sum[i] = s % 16 s = (s - sum[i]) / 16 } } while (sum.length) { hex.push(sum.pop().toString(16)) } let hexConcat = hex.join('') if (hexConcat.length % 2 !== 0) hexConcat = '0' + hexConcat if (bytes > hexConcat.length / 2) { const diff = bytes - hexConcat.length / 2 for (let i = 0; i < diff; i++) hexConcat = '00' + hexConcat } return hexConcat } export const hexToDec = (s: string): string => { const add = (x: string, y: string): string => { let c = 0 const r = [] const newX = x.split('').map(Number) const newY = y.split('').map(Number) while (newX.length || newY.length) { const s = (newX.pop() || 0) + (newY.pop() || 0) + c r.unshift(s < 10 ? s : s - 10) c = s < 10 ? 0 : 1 } if (c) r.unshift(c) return r.join('') } let dec = '0' s.split('').forEach((chr): void => { const n = parseInt(chr, 16) for (let t = 8; t; t >>= 1) { dec = add(dec, dec) if (n & t) dec = add(dec, '1') } }) return dec } export const hexToUint8 = (hex: string): Uint8Array => { const length = (hex.length / 2) | 0 const uint8 = new Uint8Array(length) for (let i = 0; i < length; i++) uint8[i] = parseInt(hex.substr(i * 2, 2), 16) return uint8 } export const uint8ToHex = (uint8: Uint8Array): string => { let hex = '' let aux for (let i = 0; i < uint8.length; i++) { aux = uint8[i].toString(16).toUpperCase() if (aux.length === 1) aux = '0' + aux hex += aux aux = '' } return hex } const equalArrays = (array1: Uint8Array, array2: Uint8Array): boolean => { for (let i = 0; i < array1.length; i++) { if (array1[i] !== array2[i]) return false } return true } export const byteCount = (s: string): number => { return encodeURI(s).split(/%(?:u[0-9A-F]{2})?[0-9A-F]{2}|./).length - 1 } export const isAlphanumeric = (s: string): boolean => { return /^[a-z0-9]+$/i.test(s) } export const isAlphanumericExtended = (s: string): boolean => { return /^[a-z0-9-_ ]+$/i.test(s) } export const isHexKey = (hex: string): boolean => { return /^[0-9A-Fa-f]{64}$/.test(hex) } export const isLogosAccount = (account: string): boolean => { if (/^lgs_[?:13]{1}[13-9-a-km-uw-z]{59}$/.test(account)) { const accountCrop = account.replace('lgs_', '') const keyBytes = decode(accountCrop.substring(0, 52)) const hashBytes = decode(accountCrop.substring(52, 60)) const blakeHash = (new Blake2b(5).update(keyBytes).digest() as Uint8Array).reverse() return equalArrays(hashBytes, blakeHash) } return false } export const accountFromHexKey = (hex: string): string => { if (isHexKey(hex)) { const keyBytes = hexToUint8(hex) const checksumBytes = (new Blake2b(5).update(keyBytes).digest() as Uint8Array).reverse() const checksum = encode(checksumBytes) const account = encode(keyBytes) return 'lgs_' + account + checksum } else if (isLogosAccount(hex)) { return hex } else { throw new Error(`Failed to execute 'accountFromHexKey' on '${hex}': The hex provided is not a valid hex.`) } } export const keyFromAccount = (account: string): string => { if (/^lgs_[?:13]{1}[13-9-a-km-uw-z]{59}$/.test(account)) { const accountCrop = account.replace('lgs_', '') const keyBytes = decode(accountCrop.substring(0, 52)) const hashBytes = decode(accountCrop.substring(52, 60)) const blakeHash = (new Blake2b(5).update(keyBytes).digest() as Uint8Array).reverse() if (equalArrays(hashBytes, blakeHash)) { return uint8ToHex(keyBytes).toUpperCase() } else { throw new Error(`Failed to execute 'keyFromAccount' on '${account}': The checksum of the address is not valid.`) } } else if (isHexKey(account)) { return account } else { throw new Error(`Failed to execute 'keyFromAccount' on '${account}': The account is not a valid logos address.`) } } export const testnetDelegates = { '172.31.80.176': '54.147.201.7', '172.31.80.245': '34.224.133.182', '172.31.80.249': '34.195.24.15', '172.31.81.11': '54.145.253.93', '172.31.81.153': '3.215.6.167', '172.31.81.156': '3.214.175.150', '172.31.81.162': '52.72.139.247', '172.31.81.173': '3.209.30.240', '172.31.81.25': '3.215.48.205', '172.31.81.54': '3.81.242.200', '172.31.81.76': '3.214.188.128', '172.31.82.117': '18.208.239.123', '172.31.82.20': '52.6.230.153', '172.31.82.245': '3.214.209.198', '172.31.82.91': '52.86.212.70', '172.31.84.148': '18.211.221.254', '172.31.84.206': '35.174.67.255', '172.31.84.231': '52.55.236.233', '172.31.84.250': '3.215.28.211', '172.31.85.161': '18.211.1.90', '172.31.85.198': '3.213.17.31', '172.31.85.94': '3.94.16.110', '172.31.86.144': '35.170.167.20', '172.31.86.168': '3.82.164.171', '172.31.86.18': '34.227.209.242', '172.31.86.224': '3.214.37.34', '172.31.86.80': '3.208.232.242', '172.31.87.122': '54.145.211.218', '172.31.87.214': '34.226.253.156', '172.31.87.229': '18.206.29.223', '172.31.87.9': '52.203.151.67', '172.31.89.100': '3.86.169.97', '172.31.89.165': '3.93.97.122', '172.31.89.169': '100.25.175.142', '172.31.89.235': '3.215.33.33', '172.31.89.241': '34.239.238.121', '172.31.89.248': '174.129.135.230', '172.31.89.4': '52.6.18.99', '172.31.89.74': '50.17.125.174', '172.31.89.83': '34.237.166.184', '172.31.89.91': '52.0.107.11', '172.31.90.39': '3.213.108.208', '172.31.90.42': '3.212.220.108', '172.31.90.64': '3.213.150.192', '172.31.90.80': '18.233.235.87', '172.31.91.0': '18.233.175.15', '172.31.91.247': '52.23.71.123', '172.31.91.254': '3.209.93.207', '172.31.91.32': '3.214.51.200', '172.31.92.10': '3.212.255.243', '172.31.92.201': '3.214.195.211', '172.31.93.13': '3.213.212.158', '172.31.93.159': '3.213.75.16', '172.31.93.179': '3.214.205.240', '172.31.93.224': '34.237.214.48', '172.31.94.105': '3.213.110.174', '172.31.94.148': '54.147.253.43', '172.31.94.238': '3.214.93.111', '172.31.94.88': '52.202.140.111', '172.31.94.93': '3.214.55.84', '172.31.95.15': '3.208.253.215', '172.31.95.23': '34.193.8.68', '172.31.95.235': '3.214.204.82', '172.31.95.73': '18.204.189.145' } export default { EMPTY_WORK, GENESIS_HASH, MAXUINT128, minimumFee, defaultRPC, defaultMQTT, testnetDelegates, Iso10126, AES, stringFromHex, stringToHex, decToHex, hexToDec, hexToUint8, uint8ToHex, changeEndianness, isAlphanumeric, isAlphanumericExtended, byteCount, deserializeController, deserializeControllers, deserializeSettings, serializeController, serializeControllers, convertObjectToArray, keyFromAccount, accountFromHexKey, isLogosAccount }