From 5bed0455d203edcc28454e2074887304d146f7e7 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Thu, 8 Dec 2016 21:17:39 -0800 Subject: [PATCH] scriptnum: refactor. --- lib/script/scriptnum.js | 185 +++++++++++++++++++--------------------- 1 file changed, 86 insertions(+), 99 deletions(-) diff --git a/lib/script/scriptnum.js b/lib/script/scriptnum.js index e2a62473..2e19a46e 100644 --- a/lib/script/scriptnum.js +++ b/lib/script/scriptnum.js @@ -7,10 +7,8 @@ 'use strict'; var assert = require('assert'); -var util = require('../utils/util'); var ScriptError = require('../btc/errors').ScriptError; -var constants = require('../protocol/constants'); -var STACK_FALSE = new Buffer(0); +var EMPTY_ARRAY = new Buffer(0); /** * ScriptNum @@ -98,7 +96,6 @@ ScriptNum.prototype.isubn = function subn(value) { }; ScriptNum.prototype.imuln = function muln(value) { - assert(false); this.value *= value; return this; }; @@ -109,7 +106,6 @@ ScriptNum.prototype.idivn = function divn(value) { }; ScriptNum.prototype.iushln = function iushln(value) { - assert(value === 1); this.value *= Math.pow(2, value); return this; }; @@ -142,15 +138,41 @@ ScriptNum.prototype.toNumber = function toNumber() { return this.value; }; +ScriptNum.prototype.toString = function toString(base) { + var str; + + if (!base) + base = 10; + + if (base === 10 || base === 'dec') + return this.value.toString(10); + + if (base === 16 || base === 'hex') { + str = this.value.toString(16); + if (str.length % 2 !== 0) + str = '0' + str; + return str; + } + + assert(false, 'Base ' + base + ' not supported.'); +}; + +ScriptNum.prototype.toJSON = function toJSON() { + return this.toString(16); +}; + ScriptNum.prototype.fromString = function fromString(str, base) { var nonzero = 0; - var neg = false; + var negative = false; var i, ch; + if (!base) + base = 10; + if (str[0] === '-') { assert(str.length > 1, 'Non-numeric string passed.'); str = str.substring(1); - neg = true; + negative = true; } else { assert(str.length > 0, 'Non-numeric string passed.'); } @@ -176,7 +198,7 @@ ScriptNum.prototype.fromString = function fromString(str, base) { this.value += ch; } - if (neg) + if (negative) this.value = -this.value; return this; @@ -209,7 +231,7 @@ ScriptNum.prototype.fromString = function fromString(str, base) { this.value += ch; } - if (neg) + if (negative) this.value = -this.value; return this; @@ -222,38 +244,30 @@ ScriptNum.fromString = function fromString(str, base) { return new ScriptNum(0).fromString(str, base); }; -ScriptNum.prototype.fromRaw = function fromRaw(value, flags, size) { - var sign; +ScriptNum.prototype.fromRaw = function fromRaw(data, minimal, limit) { + if (minimal == null) + minimal = true; - assert(Buffer.isBuffer(value)); + if (limit == null) + limit = 4; - if (flags == null) - flags = constants.flags.STANDARD_VERIFY_FLAGS; + // We can't handle more than 6 bytes. + assert(limit <= 6, 'Number exceeds 48 bits.'); - if (size == null) - size = 4; + // Max size is 4 bytes by default, 6 bytes max. + if (data.length > limit) + throw new ScriptError('UNKNOWN_ERROR', 'Script number overflow.'); - assert(size <= 6, 'Number exceeds 48 bits.'); - - if (value.length === 0) { + // Empty arrays are always zero. + if (data.length === 0) { this.value = 0; return this; } - if (value.length > size) - throw new ScriptError('UNKNOWN_ERROR', 'Script number overflow.'); - - if (flags & constants.flags.VERIFY_MINIMALDATA) { - // If the low bits on the last byte are unset, - // fail if the value's second to last byte does - // not have the high bit set. A number can't - // justify having the last byte's low bits unset - // unless they ran out of space for the sign bit - // in the second to last bit. We also fail on [0] - // to avoid negative zero (also avoids positive - // zero). - if ((value[value.length - 1] & 0x7f) === 0) { - if (value.length === 1 || !(value[value.length - 2] & 0x80)) { + // Ensure minimal serialization. + if (minimal) { + if ((data[data.length - 1] & 0x7f) === 0) { + if (data.length === 1 || !(data[data.length - 2] & 0x80)) { throw new ScriptError( 'UNKNOWN_ERROR', 'Non-minimally encoded Script number.'); @@ -263,89 +277,64 @@ ScriptNum.prototype.fromRaw = function fromRaw(value, flags, size) { this.value = 0; - switch (value.length) { + // Read number (6 bytes max). + switch (data.length) { case 6: - this.value += value[5] * 0x10000000000; + this.value += data[5] * 0x10000000000; case 5: - this.value += value[4] * 0x100000000; + this.value += data[4] * 0x100000000; case 4: - this.value += value[3] * 0x1000000; + this.value += data[3] * 0x1000000; case 3: - this.value += value[2] * 0x10000; + this.value += data[2] * 0x10000; case 2: - this.value += value[1] * 0x100; + this.value += data[1] * 0x100; case 1: - this.value += value[0]; - break; - default: - assert(false); - break; + this.value += data[0]; } - // If the input vector's most significant byte is - // 0x80, remove it from the result's msb and return - // a negative. - // Equivalent to: - // -(result & ~(0x80 << (8 * (value.length - 1)))) - if (value[value.length - 1] & 0x80) { - switch (value.length) { + // Remove high bit and flip sign. + if (data[data.length - 1] & 0x80) { + switch (data.length) { case 1: case 2: case 3: case 4: - sign = 0x80 << (8 * (value.length - 1)); - this.value &= ~sign; - this.value = -this.value; + this.value &= ~(0x80 << (8 * (data.length - 1))); break; case 5: - case 6: - sign = 0x80 * Math.pow(2, 8 * (value.length - 1)); - if (this.value >= sign) - this.value -= sign; - this.value = -this.value; + this.value -= 0x8000000000; break; - default: - assert(false); + case 6: + this.value -= 0x800000000000; break; } + this.value = -this.value; } return this; }; -ScriptNum.fromRaw = function fromRaw(value, flags, size) { - return new ScriptNum(0).fromRaw(value, flags, size); +ScriptNum.fromRaw = function fromRaw(data, minimal, limit) { + return new ScriptNum(0).fromRaw(data, minimal, limit); }; ScriptNum.prototype.toRaw = function toRaw() { var value = this.value; - var neg = false; - var result, offset, size; + var negative = false; + var data, offset, size; + // Zeroes are always empty arrays. if (value === 0) - return STACK_FALSE; - - // If the most significant byte is >= 0x80 - // and the value is positive, push a new - // zero-byte to make the significant - // byte < 0x80 again. - - // If the most significant byte is >= 0x80 - // and the value is negative, push a new - // 0x80 byte that will be popped off when - // converting to an integral. - - // If the most significant byte is < 0x80 - // and the value is negative, add 0x80 to - // it, since it will be subtracted and - // interpreted as a negative when - // converting to an integral. + return EMPTY_ARRAY; + // Need to append sign bit. if (value < 0) { - neg = true; + negative = true; value = -value; } + // Gauge buffer size. if (value <= 0xff) { offset = (value & 0x80) ? 1 : 0; size = 1; @@ -368,33 +357,31 @@ ScriptNum.prototype.toRaw = function toRaw() { throw new ScriptError('UNKNOWN_ERROR', 'Script number overflow.'); } - result = new Buffer(size + offset); + // Write number. + data = new Buffer(size + offset); switch (size) { case 6: - result[5] = (value / 0x10000000000 | 0) & 0xff; + data[5] = (value / 0x10000000000 | 0) & 0xff; case 5: - result[4] = (value / 0x100000000 | 0) & 0xff; + data[4] = (value / 0x100000000 | 0) & 0xff; case 4: - result[3] = (value >>> 24) & 0xff; + data[3] = (value >>> 24) & 0xff; case 3: - result[2] = (value >> 16) & 0xff; + data[2] = (value >> 16) & 0xff; case 2: - result[1] = (value >> 8) & 0xff; + data[1] = (value >> 8) & 0xff; case 1: - result[0] = value & 0xff; - break; - default: - assert(false); - break; + data[0] = value & 0xff; } - if (result[size - 1] & 0x80) - result[result.length - 1] = neg ? 0x80 : 0; - else if (neg) - result[size - 1] |= 0x80; + // Append sign bit. + if (data[size - 1] & 0x80) + data[size] = negative ? 0x80 : 0; + else if (negative) + data[size - 1] |= 0x80; - return result; + return data; }; /*