bitcore/lib/crypto/bn.js

206 lines
4.9 KiB
JavaScript

'use strict';
var _BN = require('bn.js');
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.prototype.fromNumber = function(n) {
var bn = BN(n);
bn.copy(this);
return this;
};
BN.prototype.toNumber = function() {
return parseInt(this['toString'](10), 10);
};
BN.prototype.fromString = function(str) {
var bn = BN(str);
bn.copy(this);
return this;
};
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.prototype.fromBuffer = function(buf, opts) {
var bn = BN.fromBuffer(buf, opts);
bn.copy(this);
return this;
};
BN.prototype.toBuffer = function(opts) {
var buf;
if (opts && opts.size) {
var 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 = buf.slice(natlen - buf.length, buf.length);
}
else if (natlen < opts.size) {
var rbuf = new Buffer(opts.size);
for (var i = 0; i < buf.length; i++)
rbuf[rbuf.length-1-i] = buf[buf.length-1-i];
for (var i = 0; i < opts.size - natlen; i++)
rbuf[i] = 0;
buf = rbuf;
}
}
else {
var 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.prototype.fromSM = function(buf, opts) {
if (buf.length === 0)
this.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;
this.fromBuffer(buf);
this.neg().copy(this);
} else {
this.fromBuffer(buf);
}
return this;
};
BN.prototype.toSM = function(opts) {
var endian = 'big';
if (opts)
endian = opts.endian;
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([]);
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.prototype.fromScriptNumBuffer = function(buf, fRequireMinimal) {
var nMaxNumSize = 4;
if (buf.length > nMaxNumSize)
throw 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 this.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(buf) {
return this.toSM({endian: 'little'});
};
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;