UNPKG

golos-lib-js

Version:

Golos-js the JavaScript library with API for GOLOS blockchain

279 lines (275 loc) 8.32 kB
"use strict"; var _promisify = require("../promisify"); var bigi = require('bigi'), bs58 = require('bs58'), ecurve = require('ecurve'), Point = ecurve.Point, secp256k1 = ecurve.getCurveByName('secp256k1'), config = require('../config'), operations = require('./serializer/src/operations'), Signature = require('./ecc/src/signature'), PrivateKey = require('./ecc/src/key_private'), PublicKey = require('./ecc/src/key_public'), session = require('./session'), multiSession = require('./multiSession'), golosApi = require('../api'), hash = require('./ecc/src/hash'); var Auth = {}; var transaction = operations.transaction; var signed_transaction = operations.signed_transaction; Auth.verify = function (name, password, auths) { var hasKey = false; var roles = []; for (var role in auths) { roles.push(role); } var pubKeys = this.generateKeys(name, password, roles); roles.forEach(function (role) { if (auths[role][0][0] === pubKeys[role]) { hasKey = true; } }); return hasKey; }; Auth.generateKeys = function (name, password, roles) { var pubKeys = {}; roles.forEach(function (role) { var seed = name + role + password; var brainKey = seed.trim().split(/[\t\n\v\f\r ]+/).join(' '); var hashSha256 = hash.sha256(brainKey); var bigInt = bigi.fromBuffer(hashSha256); var toPubKey = secp256k1.G.multiply(bigInt); var point = new Point(toPubKey.curve, toPubKey.x, toPubKey.y, toPubKey.z); var pubBuf = point.getEncoded(toPubKey.compressed); var checksum = hash.ripemd160(pubBuf); var addy = Buffer.concat([pubBuf, checksum.slice(0, 4)]); pubKeys[role] = config.get('address_prefix') + bs58.encode(addy); }); return pubKeys; }; /** @arg {string} name - blockchain account name @arg {string} password - very strong password typically no shorter than a private key @arg {array} roles - defaults to standard Golos blockchain-level roles */ Auth.getPrivateKeys = function (name, password) { let roles = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ['owner', 'active', 'posting', 'memo']; var privKeys = {}; roles.forEach(function (role) { privKeys[role] = this.toWif(name, password, role); privKeys[role + 'Pubkey'] = this.wifToPublic(privKeys[role]); }.bind(this)); return privKeys; }; Auth.isWif = function (privWif) { var isWif = false; try { var bufWif = new Buffer(bs58.decode(privWif)); var privKey = bufWif.slice(0, -4); var checksum = bufWif.slice(-4); var newChecksum = hash.sha256(privKey); newChecksum = hash.sha256(newChecksum); newChecksum = newChecksum.slice(0, 4); if (checksum.toString() == newChecksum.toString()) { isWif = true; } } catch (e) {} return isWif; }; Auth.loginAsync = function (name, password, callback) { try { let result = { owner: null, active: null, posting: null, memo: null, password: null }; const roles = Object.keys(result).slice(0, 4); let privateKeys = {}; let isPass = false; try { const pk = PrivateKey.fromWif(password); roles.map(role => privateKeys[role] = pk.toString()); } catch (err) { isPass = true; roles.map(role => privateKeys[role] = PrivateKey.fromSeed(`${name}${role}${password}`).toString()); } golosApi.getAccounts([name], (err, res) => { if (err) { callback(err, null); return; } if (res.length == 0) { callback('No such account', null); return; } if (res[0].frozen) { callback('Account is frozen', null); return; } roles.slice(0, 3).map(role => { let key_auths = res[0][role].key_auths; for (let i = 0; i < key_auths.length; i++) { if (this.wifIsValid(privateKeys[role], key_auths[i][0])) { result[role] = privateKeys[role]; break; } } }); if (this.wifIsValid(privateKeys.memo, res[0].memo_key)) result.memo = privateKeys.memo; if (isPass && result.posting) result.password = password; callback(null, result); }); } catch (err) { callback(err, null); } }; Auth.login = (0, _promisify.promisify)(Auth.loginAsync); // `keys` is object like: // { posting: "private posting key" } Auth.withNodeLogin = async function (_ref) { let { account, keys, call, dgp, api, sessionName } = _ref; if (!sessionName) sessionName = 'node_login'; if (!api) { api = golosApi; } let resp; const { MultiSession } = multiSession; const ms = new MultiSession(sessionName); const sessionData = ms.load(); let loginData = sessionData.getVal(account, 'login_data'); if (loginData) { let resp = await call(loginData); if (!resp.login_error) { return resp; } } let gprops; if (!dgp) { gprops = await api.getDynamicGlobalPropertiesAsync(); } else if (typeof dgp === 'function') { gprops = await dgp(); } else { gprops = dgp; } const { head_block_number, witness } = gprops; console.time('withNodeLogin - signData'); const signed = this.signData(head_block_number.toString(), keys); console.timeEnd('withNodeLogin - signData'); // TODO: only 1st, because node supports only 1 key const signature = Object.values(signed)[0]; loginData = { account, signed_data: { head_block_number, witness }, signature }; resp = await call(loginData); if (resp.login_error) { throw resp.login_error; } sessionData.setVal(account, 'login_data', loginData).save(); return resp; }; Auth.toWif = function (name, password, role) { var seed = name + role + password; var brainKey = seed.trim().split(/[\t\n\v\f\r ]+/).join(' '); var hashSha256 = hash.sha256(brainKey); var privKey = Buffer.concat([new Buffer([0x80]), hashSha256]); var checksum = hash.sha256(privKey); checksum = hash.sha256(checksum); checksum = checksum.slice(0, 4); var privWif = Buffer.concat([privKey, checksum]); return bs58.encode(privWif); }; Auth.wifIsValid = function (privWif, pubWif) { return this.wifToPublic(privWif) == pubWif; }; Auth.wifToPublic = function (privWif) { var pubWif = PrivateKey.fromWif(privWif); pubWif = pubWif.toPublic().toString(); return pubWif; }; Auth.isPubkey = function (pubkey, address_prefix) { return PublicKey.fromString(pubkey, address_prefix) != null; }; Auth.signTransaction = function (trx, keys) { var signatures = []; if (trx.signatures) { signatures = [].concat(trx.signatures); } var cid = new Buffer(config.get('chain_id'), 'hex'); var buf = transaction.toBuffer(trx); for (var key in keys) { var sig = Signature.signBuffer(Buffer.concat([cid, buf]), keys[key]); signatures.push(sig.toBuffer()); } return signed_transaction.toObject(Object.assign(trx, { signatures: signatures })); }; Auth.signData = function (data, keys) { const signatures = {}; const bufSha = hash.sha256(data); const sign = (role, d) => { if (!d) return; const sig = Signature.signBufferSha256(bufSha, d); signatures[role] = sig.toHex(); }; for (let key in keys) { sign(key, keys[key]); } return signatures; }; Auth.verifySignedData = function (data, signatures, account, authTypes) { const auth = {}; const bufSha = hash.sha256(data); const verify = (type, sigHex, pubkey, weight, weight_threshold) => { if (!sigHex) return; if (weight !== 1 || weight_threshold !== 1) { console.error(`Auth.verifySignedData unsupported ${type} auth configuration: ${account.name}`); } else { const parseSig = hexSig => { try { return Signature.fromHex(hexSig); } catch (e) { return null; } }; const sig = parseSig(sigHex); const public_key = PublicKey.fromString(pubkey); const verified = sig.verifyHash(bufSha, public_key); auth[type] = verified; } }; for (const authType of authTypes) { const { key_auths, weight_threshold } = account[authType]; const [[pubKey, weight]] = key_auths; verify(authType, signatures[authType], pubKey, weight, weight_threshold); } return auth; }; Auth = { ...Auth, ...session, ...multiSession }; module.exports = Auth;