'use strict'; var _BN = require('bn.js'); var $ = require('../util/preconditions'); var _ = require('lodash'); var BN = function BN(n, base) { if (!(this instanceof BN)) { return new BN(n, base); } _BN.apply(this, arguments); }; BN.prototype = _BN.prototype; var reversebuf = function(buf) { var buf2 = new Buffer(buf.length); for (var i = 0; i < buf.length; i++) { buf2[i] = buf[buf.length - 1 - i]; } return buf2; }; BN.fromNumber = function(n) { $.checkArgument(_.isNumber(n)); return BN(n); }; BN.prototype.toNumber = function() { return parseInt(this.toString(10), 10); }; BN.fromString = function(str) { $.checkArgument(_.isString(str)); return BN(str); }; BN.fromBuffer = function(buf, opts) { if (typeof opts !== 'undefined' && opts.endian === 'little') { buf = reversebuf(buf); } var hex = buf.toString('hex'); var bn = new BN(hex, 16); return bn; }; BN.trim = function(buf, natlen) { return buf.slice(natlen - buf.length, buf.length); }; BN.pad = function(buf, natlen, size) { var rbuf = new Buffer(size); for (var i = 0; i < buf.length; i++) { rbuf[rbuf.length - 1 - i] = buf[buf.length - 1 - i]; } for (i = 0; i < size - natlen; i++) { rbuf[i] = 0; } return rbuf; }; BN.prototype.toBuffer = function(opts) { var buf, hex; if (opts && opts.size) { hex = this.toString(16, 2); var natlen = hex.length / 2; buf = new Buffer(hex, 'hex'); if (natlen === opts.size) { buf = buf; } else if (natlen > opts.size) { buf = BN.trim(buf, natlen); } else if (natlen < opts.size) { buf = BN.pad(buf, natlen, opts.size); } } else { hex = this.toString(16, 2); buf = new Buffer(hex, 'hex'); } if (typeof opts !== 'undefined' && opts.endian === 'little') { buf = reversebuf(buf); } return buf; }; // signed magnitude buffer // most significant bit represents sign (0 = positive, -1 = negative) BN.fromSM = function(buf, opts) { var ret; if (buf.length === 0) { return BN.fromBuffer(new Buffer([0])); } var endian = 'big'; if (opts) { endian = opts.endian; } if (endian === 'little') { buf = reversebuf(buf); } if (buf[0] & 0x80) { buf[0] = buf[0] & 0x7f; ret = BN.fromBuffer(buf); ret.neg().copy(ret); } else { ret = BN.fromBuffer(buf); } return ret; }; BN.prototype.toSMBigEndian = function() { var buf; if (this.cmp(0) === -1) { buf = this.neg().toBuffer(); if (buf[0] & 0x80) { buf = Buffer.concat([new Buffer([0x80]), buf]); } else { buf[0] = buf[0] | 0x80; } } else { buf = this.toBuffer(); if (buf[0] & 0x80) { buf = Buffer.concat([new Buffer([0x00]), buf]); } } if (buf.length === 1 & buf[0] === 0) { buf = new Buffer([]); } return buf; }; BN.prototype.toSM = function(opts) { var endian = opts ? opts.endian : 'big'; var buf = this.toSMBigEndian(); if (endian === 'little') { buf = reversebuf(buf); } return buf; }; // This is analogous to the constructor for CScriptNum in bitcoind. Many ops in // bitcoind's script interpreter use CScriptNum, which is not really a proper // bignum. Instead, an error is thrown if trying to input a number bigger than // 4 bytes. We copy that behavior here. BN.fromScriptNumBuffer = function(buf, fRequireMinimal) { var nMaxNumSize = 4; $.checkArgument(buf.length <= nMaxNumSize, new Error('script number overflow')); if (fRequireMinimal && buf.length > 0) { // Check that the number is encoded with the minimum possible // number of bytes. // // If the most-significant-byte - excluding the sign bit - is zero // then we're not minimal. Note how this test also rejects the // negative-zero encoding, 0x80. if ((buf[buf.length - 1] & 0x7f) === 0) { // One exception: if there's more than one byte and the most // significant bit of the second-most-significant-byte is set // it would conflict with the sign bit. An example of this case // is +-255, which encode to 0xff00 and 0xff80 respectively. // (big-endian). if (buf.length <= 1 || (buf[buf.length - 2] & 0x80) === 0) { throw new Error('non-minimally encoded script number'); } } } return BN.fromSM(buf, { endian: 'little' }); }; // The corollary to the above, with the notable exception that we do not throw // an error if the output is larger than four bytes. (Which can happen if // performing a numerical operation that results in an overflow to more than 4 // bytes). BN.prototype.toScriptNumBuffer = function() { return this.toSM({ endian: 'little' }); }; var decorate = function decorate(name) { BN.prototype['_' + name] = BN.prototype[name]; var f = function(b) { if (typeof b === 'string') { b = new BN(b); } else if (typeof b === 'number') { b = new BN(b.toString()); } return this['_' + name](b); }; BN.prototype[name] = f; }; BN.prototype.gt = function(b) { return this.cmp(b) > 0; }; BN.prototype.lt = function(b) { return this.cmp(b) < 0; }; decorate('add'); decorate('sub'); decorate('mul'); decorate('mod'); decorate('div'); decorate('cmp'); decorate('gt'); decorate('lt'); module.exports = BN;