var crypto = require('crypto'); var bignum = require('bignum'); var Binary = require('binary'); var Put = require('bufferput'); var buffertools = require('buffertools'); var jssha = require('jssha'); var browser; var inBrowser = !process.versions; if (inBrowser) { browser = require('../browser/vendor-bundle.js'); } var sha256 = exports.sha256 = function(data) { return new Buffer(crypto.createHash('sha256').update(data).digest('binary'), 'binary'); }; var sha512hmac = exports.sha512hmac = function (data, key) { if (inBrowser) { var j = new jssha(data.toString('hex'), 'HEX'); var hash = j.getHMAC(key.toString('hex'), "HEX", "SHA-512", "HEX"); hash = new Buffer(hash, 'hex'); return hash; }; var hmac = crypto.createHmac('sha512', key); var hash = hmac.update(data).digest(); return hash; }; var ripe160 = exports.ripe160 = function (data) { if (!Buffer.isBuffer(data)) { throw new Error('arg should be a buffer'); } if (inBrowser) { var w = new browser.crypto31.lib.WordArray.init(Crypto.util.bytesToWords(data), data.length); var wordArray = browser.crypto31.RIPEMD160(w); var words = wordArray.words; var answer = []; for (var b = 0; b < words.length * 32; b += 8) { answer.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF); } return new Buffer(answer, 'hex'); } return new Buffer(crypto.createHash('rmd160').update(data).digest('binary'), 'binary'); }; var sha1 = exports.sha1 = function(data) { return new Buffer(crypto.createHash('sha1').update(data).digest('binary'), 'binary'); }; var twoSha256 = exports.twoSha256 = function(data) { return sha256(sha256(data)); }; var sha256ripe160 = exports.sha256ripe160 = function(data) { return ripe160(sha256(data)); }; /** * Format a block hash like the official client does. */ var formatHash = exports.formatHash = function(hash) { var hashEnd = new Buffer(10); hash.copy(hashEnd, 0, 22, 32); return buffertools.reverse(hashEnd).toString('hex'); }; /** * Display the whole hash, as hex, in correct endian order. */ var formatHashFull = exports.formatHashFull = function(hash) { var copy = new Buffer(hash.length); hash.copy(copy); var hex = buffertools.toHex(buffertools.reverse(copy)); return hex; }; /** * Format a block hash like Block Explorer does. * * Formats a block hash by removing leading zeros and truncating to 10 characters. */ var formatHashAlt = exports.formatHashAlt = function(hash) { var hex = formatHashFull(hash); hex = hex.replace(/^0*/, ''); return hex.substr(0, 10); }; var formatBuffer = exports.formatBuffer = function(buffer, maxLen) { // Calculate amount of bytes to display if (maxLen === null) { maxLen = 10; } if (maxLen > buffer.length || maxLen === 0) { maxLen = buffer.length; } // Copy those bytes into a temporary buffer var temp = new Buffer(maxLen); buffer.copy(temp, 0, 0, maxLen); // Format as string var output = buffertools.toHex(temp); if (temp.length < buffer.length) { output += "..."; } return output; }; var valueToBigInt = exports.valueToBigInt = function(valueBuffer) { if (Buffer.isBuffer(valueBuffer)) { return bignum.fromBuffer(valueBuffer, { endian: 'little', size: 8 }); } else { return valueBuffer; } }; var bigIntToValue = exports.bigIntToValue = function(valueBigInt) { if (Buffer.isBuffer(valueBigInt)) { return valueBigInt; } else { return valueBigInt.toBuffer({ endian: 'little', size: 8 }); } }; var fitsInNBits = function(integer, n) { // TODO: make this efficient!!! return integer.toString(2).replace('-', '').length < n; }; exports.bytesNeededToStore = bytesNeededToStore = function(integer) { if (integer === 0) return 0; return Math.ceil(((integer).toString(2).replace('-', '').length + 1) / 8); }; exports.negativeBuffer = negativeBuffer = function(b) { // implement two-complement negative var c = new Buffer(b.length); // negate each byte for (var i = 0; i < b.length; i++) { c[i] = ~b[i]; if (c[i] < 0) c[i] += 256; } // add one for (var i = b.length - 1; i >= 0; i--) { c[i] += 1; if (c[i] >= 256) c[i] -= 256; if (c[i] !== 0) break; } return c; }; /* * Transforms an integer into a buffer using two-complement encoding * For example, 1 is encoded as 01 and -1 is encoded as ff * For more info see: * http://en.wikipedia.org/wiki/Signed_number_representations#Two.27s_complement */ exports.intToBuffer2C = function(integer) { var size = bytesNeededToStore(integer); var buf = new Put(); var s = integer.toString(16); var neg = s[0] === '-'; s = s.replace('-', ''); for (var i = 0; i < size; i++) { var si = s.substring(s.length - 2 * (i + 1), s.length - 2 * (i)); if (si.lenght === 1) { si = '0' + si; } var pi = parseInt(si, 16); buf.word8(pi); } var ret = buf.buffer(); if (neg) { ret = buffertools.reverse(ret); ret = negativeBuffer(ret); ret = buffertools.reverse(ret); } return ret; }; var padSign = function(b) { var c; if (b[0] & 0x80) { c = new Buffer(b.length + 1); b.copy(c, 1); c[0] = 0; } else { c = b; } return c; } /* * Transforms an integer into a buffer using sign+magnitude encoding * For example, 1 is encoded as 01 and -1 is encoded as 81 * For more info see: * http://en.wikipedia.org/wiki/Signed_number_representations#Signed_magnitude_representation */ exports.intToBufferSM = function(v) { if ("number" === typeof v) { v = bignum(v); } var b, c; var cmp = v.cmp(0); if (cmp > 0) { b = v.toBuffer(); c = padSign(b); c = buffertools.reverse(c); } else if (cmp == 0) { c = new Buffer([]); } else { b = v.neg().toBuffer(); c = padSign(b); c[0] |= 0x80; c = buffertools.reverse(c); } return c; }; /* * Reverse of intToBufferSM */ exports.bufferSMToInt = function(v) { if (!v.length) { return bignum(0); } // Arithmetic operands must be in range [-2^31...2^31] if (v.length > 4) { throw new Error('Bigint cast overflow (> 4 bytes)'); } var w = new Buffer(v.length); v.copy(w); w = buffertools.reverse(w); var isNeg = w[0] & 0x80; if (isNeg) { w[0] &= 0x7f; return bignum.fromBuffer(w).neg(); } else { return bignum.fromBuffer(w); } }; var formatValue = exports.formatValue = function(valueBuffer) { var value = valueToBigInt(valueBuffer).toString(); var integerPart = value.length > 8 ? value.substr(0, value.length - 8) : '0'; var decimalPart = value.length > 8 ? value.substr(value.length - 8) : value; while (decimalPart.length < 8) { decimalPart = "0" + decimalPart; } decimalPart = decimalPart.replace(/0*$/, ''); while (decimalPart.length < 2) { decimalPart += "0"; } return integerPart + "." + decimalPart; }; var reFullVal = /^\s*(\d+)\.(\d+)/; var reFracVal = /^\s*\.(\d+)/; var reWholeVal = /^\s*(\d+)/; function padFrac(frac) { frac = frac.substr(0, 8); //truncate to 8 decimal places while (frac.length < 8) frac = frac + '0'; return frac; } function parseFullValue(res) { return bignum(res[1]).mul('100000000').add(padFrac(res[2])); } function parseFracValue(res) { return bignum(padFrac(res[1])); } function parseWholeValue(res) { return bignum(res[1]).mul('100000000'); } exports.parseValue = function parseValue(valueStr) { if (typeof valueStr !== 'string') valueStr = valueStr.toString(); var res = valueStr.match(reFullVal); if (res) return parseFullValue(res); res = valueStr.match(reFracVal); if (res) return parseFracValue(res); res = valueStr.match(reWholeVal); if (res) return parseWholeValue(res); return undefined; }; // Utility that synchronizes function calls based on a key var createSynchrotron = exports.createSynchrotron = function(fn) { var table = {}; return function(key) { var args = Array.prototype.slice.call(arguments); var run = function() { // Function fn() will call when it finishes args[0] = function next() { if (table[key]) { if (table[key].length) { table[key].shift()(); } else { delete table[key]; } } }; fn.apply(null, args); }; if (!table[key]) { table[key] = []; run(); } else { table[key].push(run); } }; }; /** * Generate a random 64-bit number. * * With ideas from node-uuid: * Copyright (c) 2010 Robert Kieffer * https://github.com/broofa/node-uuid/ * * @returns Buffer random nonce */ var generateNonce = exports.generateNonce = function() { var b32 = 0x100000000, ff = 0xff; var b = new Buffer(8), i = 0; // Generate eight random bytes r = Math.random() * b32; b[i++] = r & ff; b[i++] = (r = r >>> 8) & ff; b[i++] = (r = r >>> 8) & ff; b[i++] = (r = r >>> 8) & ff; r = Math.random() * b32; b[i++] = r & ff; b[i++] = (r = r >>> 8) & ff; b[i++] = (r = r >>> 8) & ff; b[i++] = (r = r >>> 8) & ff; return b; }; /** * Decode difficulty bits. * * This function calculates the difficulty target given the difficulty bits. */ var decodeDiffBits = exports.decodeDiffBits = function(diffBits, asBigInt) { diffBits = +diffBits; var target = bignum(diffBits & 0xffffff); /* * shiftLeft is not implemented on the bignum browser * * target = target.shiftLeft(8*((diffBits >>> 24) - 3)); */ var mov = 8*((diffBits >>> 24) - 3); while (mov-- > 0) target = target.mul(2); if (asBigInt) { return target; } // Convert to buffer var diffBuf = target.toBuffer(); var targetBuf = new Buffer(32); buffertools.fill(targetBuf, 0); diffBuf.copy(targetBuf, 32 - diffBuf.length); return targetBuf; }; /** * Encode difficulty bits. * * This function calculates the compact difficulty, given a difficulty target. */ var encodeDiffBits = exports.encodeDiffBits = function encodeDiffBits(target) { if (Buffer.isBuffer(target)) { target = bignum.fromBuffer(target); } else if ("function" === typeof target.toBuffer) { // duck-typing bignum // Nothing to do } else { throw new Error("Incorrect variable type for difficulty"); } var mpiBuf = target.toBuffer("mpint"); var size = mpiBuf.length - 4; var compact = size << 24; if (size >= 1) compact |= mpiBuf[4] << 16; if (size >= 2) compact |= mpiBuf[5] << 8; if (size >= 3) compact |= mpiBuf[6]; return compact; }; /** * Calculate "difficulty". * * This function calculates the maximum difficulty target divided by the given * difficulty target. */ var calcDifficulty = exports.calcDifficulty = function(target) { if (!Buffer.isBuffer(target)) { target = decodeDiffBits(target); } var targetBigint = bignum.fromBuffer(target, { order: 'forward' }); var maxBigint = bignum.fromBuffer(MAX_TARGET, { order: 'forward' }); return maxBigint.div(targetBigint).toNumber(); }; var reverseBytes32 = exports.reverseBytes32 = function(data) { if (data.length % 4) { throw new Error("Util.reverseBytes32(): Data length must be multiple of 4"); } var put = new Put(); var parser = Binary.parse(data); while (!parser.eof()) { var word = parser.word32le('word').vars.word; put.word32be(word); } return put.buffer(); }; var getVarIntSize = exports.getVarIntSize = function getVarIntSize(i) { if (i < 253) { // unsigned char return 1; } else if (i < 0x10000) { // unsigned short (LE) return 3; } else if (i < 0x100000000) { // unsigned int (LE) return 5; } else { // unsigned long long (LE) return 9; } }; var varIntBuf = exports.varIntBuf = function varIntBuf(n) { var buf = undefined; if (n < 253) { buf = new Buffer(1); buf.writeUInt8(n, 0); } else if (n < 0x10000) { buf = new Buffer(1 + 2); buf.writeUInt8(253, 0); buf.writeUInt16LE(n, 1); } else if (n < 0x100000000) { buf = new Buffer(1 + 4); buf.writeUInt8(254, 0); buf.writeUInt32LE(n, 1); } else { buf = new Buffer(1 + 8); buf.writeUInt8(255, 0); buf.writeInt32LE(n & -1, 1); buf.writeUInt32LE(Math.floor(n / 0x100000000), 5); } return buf; }; var varStrBuf = exports.varStrBuf = function varStrBuf(s) { return Buffer.concat([varIntBuf(s.length), s]); }; // Initializations exports.NULL_HASH = buffertools.fill(new Buffer(32), 0); exports.EMPTY_BUFFER = new Buffer(0); exports.ZERO_VALUE = buffertools.fill(new Buffer(8), 0); var INT64_MAX = new Buffer('ffffffffffffffff', 'hex'); exports.INT64_MAX = INT64_MAX; // How much of Bitcoin's internal integer coin representation // makes 1 BTC exports.COIN = 100000000; exports.MAX_TARGET = new Buffer('00000000FFFF0000000000000000000000000000000000000000000000000000', 'hex');