/** * Implements proof of work. * @see {@link https://bitmessage.org/wiki/Proof_of_work} * @module bitmessage/pow */ "use strict"; var objectAssign = Object.assign || require("object-assign"); var bmcrypto = require("./crypto"); var platform = require("./platform"); var util = require("./_util"); /** * Calculate target. * @param {Object} opts - Target options * @param {number} opts.ttl - Time to live of the message in seconds * @param {number} opts.payloadLength - Length of the message payload * (with nonce) * @param {Buffer} opts.payload - ...or payload itself * @param {number=} opts.nonceTrialsPerByte - This number is the average * number of nonce trials a node will have to perform to meet the Proof * of Work requirement. 1000 is the network minimum so any lower values * will be automatically raised to 1000. * @param {number=} opts.payloadLengthExtraBytes - This number is added * to the data length to make sending small messages more difficult. * 1000 is the network minimum so any lower values will be automatically * raised to 1000. * @return {number} Target. * @function * @static */ // Just a wrapper around platform-specific implementation. var getTarget = exports.getTarget = function(opts) { var payloadLength = opts.payloadLength || opts.payload.length; return platform.getTarget({ ttl: opts.ttl, payloadLength: payloadLength, nonceTrialsPerByte: util.getTrials(opts), payloadLengthExtraBytes: util.getExtraBytes(opts), }); }; /** * Check a POW. * @param {Object} opts - Proof of work options * @param {number} opts.target - Proof of work target or pass * [getTarget]{@link module:bitmessage/pow.getTarget} options to `opts` * to compute it * @param {Buffer} opts.payload - Message payload (with nonce) * @param {(number|Buffer)} opts.nonce - ...or already derived nonce * @param {Buffer} opts.initialHash - ...and initial hash * @return {boolean} Is the proof of work sufficient. */ exports.check = function(opts) { var initialHash; var nonce; var target = opts.target; if (target === undefined) { target = getTarget(opts); } if (opts.payload) { nonce = opts.payload.slice(0, 8); initialHash = bmcrypto.sha512(opts.payload.slice(8)); } else { if (typeof opts.nonce === "number") { nonce = Buffer.alloc(8); // High 32 bits. nonce.writeUInt32BE(Math.floor(opts.nonce / 4294967296), 0, true); // Low 32 bits. nonce.writeUInt32BE(opts.nonce % 4294967296, 4, true); } else { nonce = opts.nonce; } initialHash = opts.initialHash; } var targetHi = Math.floor(target / 4294967296); var targetLo = target % 4294967296; var dataToHash = Buffer.concat([nonce, initialHash]); var resultHash = bmcrypto.sha512(bmcrypto.sha512(dataToHash)); var trialHi = resultHash.readUInt32BE(0, true); if (trialHi > targetHi) { return false; } else if (trialHi < targetHi) { return true; } else { var trialLo = resultHash.readUInt32BE(4, true); return trialLo <= targetLo; } }; /** * Do a POW. * @param {Object} opts - Proof of work options * @param {Buffer} opts.data - Object message payload without nonce to * get the initial hash from * @param {Buffer} opts.initialHash - ...or already computed initial * hash * @param {number} opts.target - POW target * @param {number=} opts.poolSize - POW calculation pool size (by * default equals to number of cores) * @return {Promise.} A promise that contains computed nonce for * the given target when fulfilled. */ exports.doAsync = function(opts) { var initialHash; if (opts.data) { initialHash = bmcrypto.sha512(opts.data); } else { initialHash = opts.initialHash; } opts = objectAssign({}, opts, {initialHash: initialHash}); return platform.pow(opts); };