UNPKG

factom

Version:

Library to build applications on the Factom blockchain

272 lines (241 loc) 8.41 kB
const base58 = require('base-58'), nacl = require('tweetnacl/nacl-fast'), { RCD_TYPE_1, secretToPublicKey, sha256d } = require('./util'); const { FACTOID_PUBLIC_PREFIX, FACTOID_PRIVATE_PREFIX, ENTRYCREDIT_PUBLIC_PREFIX, ENTRYCREDIT_PRIVATE_PREFIX, VALID_PREFIXES, PUBLIC_ADDRESS_VALID_PREFIXES, PRIVATE_ADDRESS_VALID_PREFIXES, EC_ADDRESS_VALID_PREFIXES, FCT_ADDRESS_VALID_PREFIXES } = require('./constant'); /** * Validate that an address is valid (well formed). * @param {string} address - Address to validate * @returns {boolean} - True if the address is valid. */ function isValidAddress(address) { try { if (!VALID_PREFIXES.has(address.slice(0, 2))) { return false; } const bytes = Buffer.from(base58.decode(address)); if (bytes.length !== 38) { return false; } const checksum = sha256d(bytes.slice(0, 34)).slice(0, 4); if (checksum.equals(bytes.slice(34, 38))) { return true; } return false; } catch (err) { return false; } } /** * Validate if an address is a valid public EC or FCT address. * @param {string} address - Address to validate. * @returns {boolean} - True if the address is a valid public EC or FCT address. */ function isValidPublicAddress(address) { return isValidAddress(address) && PUBLIC_ADDRESS_VALID_PREFIXES.has(address.substring(0, 2)); } /** * Validate if an address is a valid private EC or FCT address. * @param {string} address - Address to validate. * @returns {boolean} - True if the address is a valid private EC or FCT address. */ function isValidPrivateAddress(address) { return isValidAddress(address) && PRIVATE_ADDRESS_VALID_PREFIXES.has(address.substring(0, 2)); } /** * Validate if an address is a valid EC address (public or private). * @param {string} address - Address to validate. * @returns {boolean} - True if the address is a valid EC address. */ function isValidEcAddress(address) { return isValidAddress(address) && EC_ADDRESS_VALID_PREFIXES.has(address.substring(0, 2)); } /** * Validate if an address is a valid public EC address. * @param {string} address - Address to validate. * @returns {boolean} - True if the address is a valid public EC address. */ function isValidPublicEcAddress(address) { return isValidAddress(address) && address.substring(0, 2) === 'EC'; } /** * Validate if an address is a valid private EC address. * @param {string} address - Address to validate. * @returns {boolean} - True if the address is a valid private EC address. */ function isValidPrivateEcAddress(address) { return isValidAddress(address) && address.substring(0, 2) === 'Es'; } /** * Validate if an address is a valid FCT address (public or private). * @param {string} address - Address to validate. * @returns {boolean} - True if the address is a valid FCT address. */ function isValidFctAddress(address) { return isValidAddress(address) && FCT_ADDRESS_VALID_PREFIXES.has(address.substring(0, 2)); } /** * Validate if an address is a valid public FCT address. * @param {string} address - Address to validate. * @returns {boolean} - True if the address is a valid public FCT address. */ function isValidPublicFctAddress(address) { return isValidAddress(address) && address.substring(0, 2) === 'FA'; } /** * Validate if an address is a valid private FCT address. * @param {string} address - Address to validate. * @returns {boolean} - True if the address is a valid private FCT address. */ function isValidPrivateFctAddress(address) { return isValidAddress(address) && address.substring(0, 2) === 'Fs'; } /** * Get public address corresponding to an address. * @param {string} address - Any address. * @returns {string} - Public address. */ function getPublicAddress(address) { if (!isValidAddress(address)) { throw new Error(`Invalid address ${address}.`); } if (address[1] !== 's') { return address; } const secret = base58.decode(address).slice(2, 34); const pub = secretToPublicKey(secret); return address[0] === 'F' ? keyToPublicFctAddress(pub) : keyToPublicEcAddress(pub); } /** * Extract the key contained in an address. Cannot be used with public FCT address as those contain a RCD hash and not a key (See {@link addressToRcdHash}). * @param {string} address - Any address, except public FCT address. * @returns {Buffer} - Key contained in the address. */ function addressToKey(address) { if (!isValidAddress(address)) { throw new Error(`Invalid address ${address}.`); } if (address.startsWith('FA')) { throw new Error( 'A public Factoid address does not hold a public key but a RCD hash. Use addressToRcdHash function instead.' ); } return Buffer.from(base58.decode(address).slice(2, 34)); } /** * Extract the RCD hash from a public FCT address. * @param {string} address - Public FCT address. * @returns {Buffer} - RCD hash. */ function addressToRcdHash(address) { if (!isValidPublicFctAddress(address)) { throw new Error(`Address ${address} is not a valid public Factoid address`); } return Buffer.from(base58.decode(address).slice(2, 34)); } /** * Build a human readable public FCT address from a key. * @param {Buffer|string} key * @returns {string} - Public FCT address. */ function keyToPublicFctAddress(key) { return keyToAddress(key, FACTOID_PUBLIC_PREFIX, true); } /** * Build a human readable public FCT address from a RCD hash. * @param {Buffer|string} rcdHash * @returns {string} - Public FCT address. */ function rcdHashToPublicFctAddress(rcdHash) { return keyToAddress(rcdHash, FACTOID_PUBLIC_PREFIX); } /** * Build a human readable private FCT address from a 32-byte seed. * @param {Buffer|string} seed 32-byte seed. * @returns {string} - Private FCT address. */ function seedToPrivateFctAddress(seed) { return keyToAddress(seed, FACTOID_PRIVATE_PREFIX); } /** * Build a human readable public EC address from a 32-byte key. * @param {Buffer|string} key 32-byte key. * @returns {string} - Public EC address. */ function keyToPublicEcAddress(key) { return keyToAddress(key, ENTRYCREDIT_PUBLIC_PREFIX); } /** * Build a human readable private EC address from a 32-byte seed. * @param {Buffer|string} seed 32-byte seed. * @returns {string} - Private EC address. */ function seedToPrivateEcAddress(seed) { return keyToAddress(seed, ENTRYCREDIT_PRIVATE_PREFIX); } function keyToAddress(key, prefix, computeRCDHash) { const keyBuffer = Buffer.from(key, 'hex'); if (keyBuffer.length !== 32) { throw new Error(`Key ${keyBuffer} is not 32 bytes long.`); } const address = Buffer.concat([prefix, computeRCDHash ? keyToRCD1Hash(keyBuffer) : keyBuffer]); const checksum = sha256d(address).slice(0, 4); return base58.encode(Buffer.concat([address, checksum])); } function keyToRCD1Hash(key) { return sha256d(Buffer.concat([RCD_TYPE_1, key])); } /** * Generate a new random FCT address pair (private and public). * @returns {{public: string, private: string}} - Public and private FCT addresses. */ function generateRandomFctAddress() { const seed = nacl.randomBytes(32); const keyPair = nacl.sign.keyPair.fromSeed(seed); return { public: keyToPublicFctAddress(keyPair.publicKey), private: seedToPrivateFctAddress(seed) }; } /** * Generate a new random EC address pair (private and public). * @returns {{public: string, private: string}} - Public and private EC addresses. */ function generateRandomEcAddress() { const seed = nacl.randomBytes(32); const keyPair = nacl.sign.keyPair.fromSeed(seed); return { public: keyToPublicEcAddress(keyPair.publicKey), private: seedToPrivateEcAddress(seed) }; } module.exports = { isValidAddress, addressToKey, addressToRcdHash, isValidPublicAddress, isValidPrivateAddress, isValidEcAddress, isValidPublicEcAddress, isValidPrivateEcAddress, isValidFctAddress, isValidPublicFctAddress, isValidPrivateFctAddress, getPublicAddress, keyToPublicFctAddress, rcdHashToPublicFctAddress, seedToPrivateFctAddress, keyToPublicEcAddress, seedToPrivateEcAddress, generateRandomFctAddress, generateRandomEcAddress };