From a9ea4e5531a67bb832358a8180a17de8f5f6f50f Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Sat, 13 May 2017 15:13:42 -0700 Subject: [PATCH] bech32: refactor. --- bench/bech32.js | 6 ++-- lib/primitives/address.js | 23 ++++--------- lib/utils/bech32.js | 72 ++++++++++++++++++++++++++++++++++++--- test/bech32-test.js | 10 +++--- 4 files changed, 83 insertions(+), 28 deletions(-) diff --git a/bench/bech32.js b/bench/bech32.js index dd7a1e08..34aeb353 100644 --- a/bench/bech32.js +++ b/bench/bech32.js @@ -9,16 +9,18 @@ var i, end, addr; var addrs = []; end = bench('serialize'); -for (i = 0; i < 10000; i++) { +for (i = 0; i < 100000; i++) { addr = Address.fromProgram(0, crypto.randomBytes(20)); addrs.push(addr.toBech32()); } end(i); end = bench('parse'); -for (i = 0; i < 10000; i++) { +for (i = 0; i < 100000; i++) { addr = addrs[i]; addr = Address.fromBech32(addr); addrs[i] = addr; } end(i); + +console.error(addrs); diff --git a/lib/primitives/address.js b/lib/primitives/address.js index c95f122d..3e86d3ca 100644 --- a/lib/primitives/address.js +++ b/lib/primitives/address.js @@ -236,9 +236,7 @@ Address.prototype.toBech32 = function toBech32(network) { network = Network.get(network); hrp = network.addressPrefix.bech32; - data = bech32.bitsify(hash, 65, 8, 5, version, 0); - - return bech32.encode(hrp, data); + return bech32.encode(hrp, version, hash); }; /** @@ -389,23 +387,18 @@ Address.fromBase58 = function fromBase58(address, network) { /** * Inject properties from bech32 address. * @private - * @param {String} data + * @param {String} str * @param {Network?} network * @throws Parse error */ -Address.prototype.fromBech32 = function fromBech32(data, network) { +Address.prototype.fromBech32 = function fromBech32(str, network) { var type = Address.types.WITNESS; - var i, addr, hash, version; + var i, addr; - assert(typeof data === 'string'); + assert(typeof str === 'string'); - addr = bech32.decode(data); - - if (addr.data.length < 1) - throw new Error('Invalid bech32 data length.'); - - version = addr.data[0]; + addr = bech32.decode(str); if (network) { network = Network.get(network); @@ -421,9 +414,7 @@ Address.prototype.fromBech32 = function fromBech32(data, network) { assert(i < networks.types.length, 'Unknown bech32 address prefix.'); } - hash = bech32.bitsify(addr.data, 84, 5, 8, -1, 1); - - return this.fromHash(hash, type, version, network.type); + return this.fromHash(addr.hash, type, addr.version, network.type); }; /** diff --git a/lib/utils/bech32.js b/lib/utils/bech32.js index e1ee1ccd..7b8cbaee 100644 --- a/lib/utils/bech32.js +++ b/lib/utils/bech32.js @@ -29,6 +29,8 @@ 'use strict'; +var native = require('./native').binding; + /** * @module utils/bech32 */ @@ -70,7 +72,7 @@ function polymod(pre) { * @returns {String} */ -function encode(hrp, data) { +function serialize(hrp, data) { var str = ''; var chk = 1; var i, ch; @@ -124,7 +126,7 @@ function encode(hrp, data) { * @returns {Bech32Result} */ -function decode(str) { +function deserialize(str) { var chk = 1; var lower = false; var upper = false; @@ -211,7 +213,7 @@ function decode(str) { * @returns {Buffer} */ -function bitsify(data, size, frombits, tobits, pad, off) { +function convert(data, size, frombits, tobits, pad, off) { var acc = 0; var bits = 0; var maxv = (1 << tobits) - 1; @@ -248,6 +250,46 @@ function bitsify(data, size, frombits, tobits, pad, off) { return output.slice(0, j); } +/** + * Serialize data to bech32 address. + * @param {String} hrp + * @param {Number} version + * @param {Buffer} hash + * @returns {String} + */ + +function encode(hrp, version, hash) { + var data = convert(hash, 65, 8, 5, version, 0); + return serialize(hrp, data); +} + +if (native && native.toBech32) + encode = native.toBech32; + +/** + * Deserialize data from bech32 address. + * @param {String} str + * @returns {Object} + */ + +function decode(str) { + var result = deserialize(str); + var hrp = result.hrp; + var data = result.data; + var version, hash; + + if (data.length < 1) + throw new Error('Invalid bech32 data length.'); + + version = data[0]; + hash = convert(data, 84, 5, 8, -1, 1); + + return new AddrResult(hrp, version, hash); +} + +if (native && native.fromBech32) + decode = native.fromBech32; + /** * Bech32Result * @constructor @@ -263,10 +305,30 @@ function Bech32Result(hrp, data) { this.data = data; } +/** + * AddrResult + * @constructor + * @private + * @param {String} hrp + * @param {Number} version + * @param {Buffer} hash + * @property {String} hrp + * @property {Number} version + * @property {Buffer} hash + */ + +function AddrResult(hrp, version, hash) { + this.hrp = hrp; + this.version = version; + this.hash = hash; +} + /* * Expose */ -exports.decode = decode; +exports.deserialize = deserialize; +exports.serialize = serialize; +exports.convert = convert; exports.encode = encode; -exports.bitsify = bitsify; +exports.decode = decode; diff --git a/test/bech32-test.js b/test/bech32-test.js index 010dbb81..70b12927 100644 --- a/test/bech32-test.js +++ b/test/bech32-test.js @@ -99,13 +99,13 @@ describe('Bech32', function() { ]; function fromAddress(hrp, addr) { - var dec = bech32.decode(addr); + var dec = bech32.deserialize(addr); var data; if (dec.hrp !== hrp || dec.data.length < 1 || dec.data[0] > 16) throw new Error('Invalid bech32 prefix or data length.'); - data = bech32.bitsify(dec.data, 84, 5, 8, -1, 1); + data = bech32.convert(dec.data, 84, 5, 8, -1, 1); if (data.length < 2 || data.length > 40) throw new Error('Invalid witness program size.'); @@ -120,8 +120,8 @@ describe('Bech32', function() { } function toAddress(hrp, version, program) { - var data = bech32.bitsify(program, 65, 8, 5, version, 0); - var ret = bech32.encode(hrp, data); + var data = bech32.convert(program, 65, 8, 5, version, 0); + var ret = bech32.serialize(hrp, data); fromAddress(hrp, ret); @@ -135,7 +135,7 @@ describe('Bech32', function() { VALID_CHECKSUM.forEach(function(test) { it('should have valid checksum for ' + test, function() { - var ret = bech32.decode(test); + var ret = bech32.deserialize(test); assert(ret); }); });