UNPKG

bitcore-wallet-client-dash

Version:
205 lines (163 loc) 5.72 kB
var $ = require('preconditions').singleton(); var _ = require('lodash'); var Bitcore = require('@dashevo/dashcore-lib'); var Common = require('./common'); var Utils = Common.Utils; var log = require('./log'); /** * @desc Verifier constructor. Checks data given by the server * * @constructor */ function Verifier(opts) {}; /** * Check address * * @param {Function} credentials * @param {String} address * @returns {Boolean} true or false */ Verifier.checkAddress = function(credentials, address) { $.checkState(credentials.isComplete()); var local = Utils.deriveAddress(address.type || credentials.addressType, credentials.publicKeyRing, address.path, credentials.m, credentials.network, credentials.coin); return (local.address == address.address && _.difference(local.publicKeys, address.publicKeys).length === 0); }; /** * Check copayers * * @param {Function} credentials * @param {Array} copayers * @returns {Boolean} true or false */ Verifier.checkCopayers = function(credentials, copayers) { $.checkState(credentials.walletPrivKey); var walletPubKey = Bitcore.PrivateKey.fromString(credentials.walletPrivKey).toPublicKey().toString(); if (copayers.length != credentials.n) { log.error('Missing public keys in server response'); return false; } // Repeated xpub kes? var uniq = []; var error; _.each(copayers, function(copayer) { if (error) return; if (uniq[copayers.xPubKey]++) { log.error('Repeated public keys in server response'); error = true; } // Not signed pub keys if (!(copayer.encryptedName || copayer.name) || !copayer.xPubKey || !copayer.requestPubKey || !copayer.signature) { log.error('Missing copayer fields in server response'); error = true; } else { var hash = Utils.getCopayerHash(copayer.encryptedName || copayer.name, copayer.xPubKey, copayer.requestPubKey); if (!Utils.verifyMessage(hash, copayer.signature, walletPubKey)) { log.error('Invalid signatures in server response'); error = true; } } }); if (error) return false; if (!_.includes(_.map(copayers, 'xPubKey'), credentials.xPubKey)) { log.error('Server response does not contains our public keys') return false; } return true; }; Verifier.checkProposalCreation = function(args, txp, encryptingKey) { function strEqual(str1, str2) { return ((!str1 && !str2) || (str1 === str2)); } if (txp.outputs.length != args.outputs.length) return false; for (var i = 0; i < txp.outputs.length; i++) { var o1 = txp.outputs[i]; var o2 = args.outputs[i]; if (!strEqual(o1.toAddress, o2.toAddress)) return false; if (!strEqual(o1.script, o2.script)) return false; if (o1.amount != o2.amount) return false; var decryptedMessage = null; try { decryptedMessage = Utils.decryptMessage(o2.message, encryptingKey); } catch (e) { return false; } if (!strEqual(o1.message, decryptedMessage)) return false; } var changeAddress; if (txp.changeAddress) { changeAddress = txp.changeAddress.address; } if (args.changeAddress && !strEqual(changeAddress, args.changeAddress)) return false; if (_.isNumber(args.feePerKb) && (txp.feePerKb != args.feePerKb)) return false; if (!strEqual(txp.payProUrl, args.payProUrl)) return false; var decryptedMessage = null; try { decryptedMessage = Utils.decryptMessage(args.message, encryptingKey); } catch (e) { return false; } if (!strEqual(txp.message, decryptedMessage)) return false; if (args.customData && !_.isEqual(txp.customData, args.customData)) return false; return true; }; Verifier.checkTxProposalSignature = function(credentials, txp) { $.checkArgument(txp.creatorId); $.checkState(credentials.isComplete()); var creatorKeys = _.find(credentials.publicKeyRing, function(item) { if (Utils.xPubToCopayerId(txp.coin || 'btc', item.xPubKey) === txp.creatorId) return true; }); if (!creatorKeys) return false; var creatorSigningPubKey; // If the txp using a selfsigned pub key? if (txp.proposalSignaturePubKey) { // Verify it... if (!Utils.verifyRequestPubKey(txp.proposalSignaturePubKey, txp.proposalSignaturePubKeySig, creatorKeys.xPubKey)) return false; creatorSigningPubKey = txp.proposalSignaturePubKey; } else { creatorSigningPubKey = creatorKeys.requestPubKey; } if (!creatorSigningPubKey) return false; var hash; if (parseInt(txp.version) >= 3) { var t = Utils.buildTx(txp); hash = t.uncheckedSerialize(); } else { throw new Error('Transaction proposal not supported'); } log.debug('Regenerating & verifying tx proposal hash -> Hash: ', hash, ' Signature: ', txp.proposalSignature); if (!Utils.verifyMessage(hash, txp.proposalSignature, creatorSigningPubKey)) return false; if (!Verifier.checkAddress(credentials, txp.changeAddress)) return false; return true; }; Verifier.checkPaypro = function(txp, payproOpts) { var toAddress, amount; if (parseInt(txp.version) >= 3) { toAddress = txp.outputs[0].toAddress; amount = txp.amount; } else { toAddress = txp.toAddress; amount = txp.amount; } return (toAddress == payproOpts.toAddress && amount == payproOpts.amount); }; /** * Check transaction proposal * * @param {Function} credentials * @param {Object} txp * @param {Object} Optional: paypro * @param {Boolean} isLegit */ Verifier.checkTxProposal = function(credentials, txp, opts) { opts = opts || {}; if (!this.checkTxProposalSignature(credentials, txp)) return false; if (opts.paypro && !this.checkPaypro(txp, opts.paypro)) return false; return true; }; module.exports = Verifier;