From 54f4edf5ef50fc02d956690f9acc20dea6eb7495 Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Fri, 1 Feb 2013 22:38:37 +0100 Subject: [PATCH] Create abstract UInt class for UInt160, UInt256, ... --- src/js/uint.js | 221 ++++++++++++++++++++++++++++++++++++++++++++++ src/js/uint160.js | 119 +++---------------------- src/js/uint256.js | 37 ++++++++ 3 files changed, 271 insertions(+), 106 deletions(-) create mode 100644 src/js/uint.js create mode 100644 src/js/uint256.js diff --git a/src/js/uint.js b/src/js/uint.js new file mode 100644 index 0000000000..b989b7e847 --- /dev/null +++ b/src/js/uint.js @@ -0,0 +1,221 @@ + +var sjcl = require('../../build/sjcl'); +var utils = require('./utils'); +var config = require('./config'); +var jsbn = require('./jsbn'); + +var BigInteger = jsbn.BigInteger; +var nbi = jsbn.nbi; + +var Base = require('./base').Base; + +// +// Abstract UInt class +// +// Base class for UInt??? classes +// + +var UInt = function () { + // Internal form: NaN or BigInteger + this._value = NaN; +}; + +UInt.json_rewrite = function (j) { + return this.from_json(j).to_json(); +}; + +// Return a new UInt from j. +UInt.from_generic = function (j) { + if (j instanceof this) { + return j.clone(); + } else { + return (new this()).parse_generic(j); + } +}; + +// Return a new UInt from j. +UInt.from_hex = function (j) { + if (j instanceof this) { + return j.clone(); + } else { + return (new this()).parse_hex(j); + } +}; + +// Return a new UInt from j. +UInt.from_json = function (j) { + if (j instanceof this) { + return j.clone(); + } else { + return (new this()).parse_json(j); + } +}; + +// Return a new UInt from j. +UInt.from_bits = function (j) { + if (j instanceof this) { + return j.clone(); + } else { + return (new this()).parse_bits(j); + } +}; + +// Return a new UInt from j. +UInt.from_bn = function (j) { + if (j instanceof this) { + return j.clone(); + } else { + return (new this()).parse_bn(j); + } +}; + +UInt.is_valid = function (j) { + return this.from_json(j).is_valid(); +}; + +UInt.prototype.clone = function () { + return this.copyTo(new this.constructor()); +}; + +// Returns copy. +UInt.prototype.copyTo = function (d) { + d._value = this._value; + + return d; +}; + +UInt.prototype.equals = function (d) { + return this._value instanceof BigInteger && d._value instanceof BigInteger && this._value.equals(d._value); +}; + +UInt.prototype.is_valid = function () { + return this._value instanceof BigInteger; +}; + +// value = NaN on error. +UInt.prototype.parse_generic = function (j) { + // Canonicalize and validate + if (config.accounts && j in config.accounts) + j = config.accounts[j].account; + + switch (j) { + case undefined: + case "0": + case this.constructor.STR_ZERO: + case this.constructor.ADDRESS_ZERO: + case this.constructor.HEX_ZERO: + this._value = nbi(); + break; + + case "1": + case this.constructor.STR_ONE: + case this.constructor.ADDRESS_ONE: + case this.constructor.HEX_ONE: + this._value = new BigInteger([1]); + + break; + + default: + if ('string' !== typeof j) { + this._value = NaN; + } + else if (j[0] === "r") { + this._value = Base.decode_check(Base.VER_ACCOUNT_ID, j); + } + else if (this.constructor.width === j.length) { + this._value = new BigInteger(utils.stringToArray(j), 256); + } + else if ((this.constructor.width*2) === j.length) { + // XXX Check char set! + this._value = new BigInteger(j, 16); + } + else { + this._value = NaN; + } + } + + return this; +}; + +UInt.prototype.parse_hex = function (j) { + if ('string' === typeof j && + j.length === (this.constructor.width * 2)) { + this._value = new BigInteger(j, 16); + } else { + this._value = NaN; + } + + return this; +}; + +UInt.prototype.parse_bits = function (j) { + if (sjcl.bitArray.bitLength(j) !== this.constructor.width * 8) { + this._value = NaN; + } else { + var bytes = sjcl.codec.bytes.fromBits(j); + this._value = new BigInteger(bytes, 256); + } + + return this; +}; + +UInt.prototype.parse_json = UInt.prototype.parse_hex; + +UInt.prototype.parse_bn = function (j) { + if (j instanceof sjcl.bn && + j.bitLength() <= this.constructor.width * 8) { + var bytes = sjcl.codec.bytes.fromBits(j.toBits()); + this._value = new BigInteger(bytes, 256); + } else { + this._value = NaN; + } + + return this; +}; + +// Convert from internal form. +UInt.prototype.to_bytes = function () { + if (!(this._value instanceof BigInteger)) + return null; + + var bytes = this._value.toByteArray(); + bytes = bytes.map(function (b) { return (b+256) % 256; }); + var target = this.constructor.width; + + // XXX Make sure only trim off leading zeros. + bytes = bytes.slice(-target); + while (bytes.length < target) bytes.unshift(0); + + return bytes; +}; + +UInt.prototype.to_hex = function () { + if (!(this._value instanceof BigInteger)) + return null; + + var bytes = this.to_bytes(); + + return sjcl.codec.hex.fromBits(sjcl.codec.bytes.toBits(bytes)).toUpperCase(); +}; + +UInt.prototype.to_json = UInt.prototype.to_hex; + +UInt.prototype.to_bits = function () { + if (!(this._value instanceof BigInteger)) + return null; + + var bytes = this.to_bytes(); + + return sjcl.codec.bytes.toBits(bytes); +}; + +UInt.prototype.to_bn = function () { + if (!(this._value instanceof BigInteger)) + return null; + + var bits = this.to_bits(); + + return sjcl.bn.fromBits(bits); +}; + +exports.UInt = UInt; diff --git a/src/js/uint160.js b/src/js/uint160.js index befe2f101c..b9165e34c3 100644 --- a/src/js/uint160.js +++ b/src/js/uint160.js @@ -1,118 +1,35 @@ +var sjcl = require('../../build/sjcl'); var utils = require('./utils'); var config = require('./config'); var jsbn = require('./jsbn'); +var extend = require('extend'); var BigInteger = jsbn.BigInteger; var nbi = jsbn.nbi; -var Base = require('./base').Base; +var UInt = require('./uint').UInt, + Base = require('./base').Base; // // UInt160 support // -var UInt160 = function () { +var UInt160 = extend(function () { // Internal form: NaN or BigInteger this._value = NaN; -}; +}, UInt); + +UInt160.width = 20; +UInt160.prototype = extend({}, UInt.prototype); +UInt160.prototype.constructor = UInt160; -UInt160.ZERO = utils.hexToString("0000000000000000000000000000000000000000"); -UInt160.ONE = utils.hexToString("0000000000000000000000000000000000000001"); var ADDRESS_ZERO = UInt160.ADDRESS_ZERO = "rrrrrrrrrrrrrrrrrrrrrhoLvTp"; var ADDRESS_ONE = UInt160.ADDRESS_ONE = "rrrrrrrrrrrrrrrrrrrrBZbvji"; var HEX_ZERO = UInt160.HEX_ZERO = "0000000000000000000000000000000000000000"; var HEX_ONE = UInt160.HEX_ONE = "0000000000000000000000000000000000000001"; - -UInt160.json_rewrite = function (j) { - return UInt160.from_json(j).to_json(); -}; - -// Return a new UInt160 from j. -UInt160.from_generic = function (j) { - return 'string' === typeof j - ? (new UInt160()).parse_generic(j) - : j.clone(); -}; - -// Return a new UInt160 from j. -UInt160.from_json = function (j) { - if ('string' === typeof j) { - return (new UInt160()).parse_json(j); - } else if (j instanceof UInt160) { - return j.clone(); - } else { - return new UInt160(); - } -}; - -UInt160.is_valid = function (j) { - return UInt160.from_json(j).is_valid(); -}; - -UInt160.prototype.clone = function () { - return this.copyTo(new UInt160()); -}; - -// Returns copy. -UInt160.prototype.copyTo = function (d) { - d._value = this._value; - - return d; -}; - -UInt160.prototype.equals = function (d) { - return this._value instanceof BigInteger && d._value instanceof BigInteger && this._value.equals(d._value); -}; - -UInt160.prototype.is_valid = function () { - return this._value instanceof BigInteger; -}; - -// value = NaN on error. -UInt160.prototype.parse_generic = function (j) { - // Canonicalize and validate - if (config.accounts && j in config.accounts) - j = config.accounts[j].account; - - switch (j) { - case undefined: - case "0": - case UInt160.ZERO: - case ADDRESS_ZERO: - case HEX_ZERO: - this._value = nbi(); - break; - - case "1": - case UInt160.ONE: - case ADDRESS_ONE: - case HEX_ONE: - this._value = new BigInteger([1]); - - break; - - default: - if ('string' !== typeof j) { - this._value = NaN; - } - else if (j[0] === "r") { - this._value = Base.decode_check(Base.VER_ACCOUNT_ID, j); - } - else if (20 === j.length) { - this._value = new BigInteger(utils.stringToArray(j), 256); - } - else if (40 === j.length) { - // XXX Check char set! - this._value = new BigInteger(j, 16); - } - else { - this._value = NaN; - } - } - - return this; -}; +var STR_ZERO = UInt160.STR_ZERO = utils.hexToString(HEX_ZERO); +var STR_ONE = UInt160.STR_ONE = utils.hexToString(HEX_ONE); // value = NaN on error. UInt160.prototype.parse_json = function (j) { @@ -133,22 +50,12 @@ UInt160.prototype.parse_json = function (j) { return this; }; -// Convert from internal form. // XXX Json form should allow 0 and 1, C++ doesn't currently allow it. UInt160.prototype.to_json = function () { if (!(this._value instanceof BigInteger)) return NaN; - var bytes = this._value.toByteArray().map(function (b) { return b ? b < 0 ? 256+b : b : 0}); - var target = 20; - - // XXX Make sure only trim off leading zeros. - var array = bytes.length < target - ? bytes.length - ? [].concat(utils.arraySet(target - bytes.length, 0), bytes) - : utils.arraySet(target, 0) - : bytes.slice(target - bytes.length); - var output = Base.encode_check(Base.VER_ACCOUNT_ID, array); + var output = Base.encode_check(Base.VER_ACCOUNT_ID, this.to_bytes()); return output; }; diff --git a/src/js/uint256.js b/src/js/uint256.js new file mode 100644 index 0000000000..ab5b6f70b6 --- /dev/null +++ b/src/js/uint256.js @@ -0,0 +1,37 @@ + +var sjcl = require('../../build/sjcl'); +var utils = require('./utils'); +var config = require('./config'); +var jsbn = require('./jsbn'); +var extend = require('extend'); + +var BigInteger = jsbn.BigInteger; +var nbi = jsbn.nbi; + +var UInt = require('./uint').UInt, + Base = require('./base').Base; + +// +// UInt256 support +// + +var UInt256 = extend(function () { + // Internal form: NaN or BigInteger + this._value = NaN; +}, UInt); + +UInt256.width = 32; +UInt256.prototype = extend({}, UInt.prototype); +UInt256.prototype.constructor = UInt256; + +// XXX Generate these constants (or remove them) +var ADDRESS_ZERO = UInt256.ADDRESS_ZERO = "XXX"; +var ADDRESS_ONE = UInt256.ADDRESS_ONE = "XXX"; +var HEX_ZERO = UInt256.HEX_ZERO = "00000000000000000000000000000000" + + "00000000000000000000000000000000"; +var HEX_ONE = UInt256.HEX_ONE = "00000000000000000000000000000000" + + "00000000000000000000000000000001"; +var STR_ZERO = UInt256.STR_ZERO = utils.hexToString(HEX_ZERO); +var STR_ONE = UInt256.STR_ONE = utils.hexToString(HEX_ONE); + +exports.UInt256 = UInt256;