perf: start using bcoin-native.

This commit is contained in:
Christopher Jeffrey 2016-09-12 13:33:00 -07:00
parent 27ba246027
commit 7f31a41e84
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
11 changed files with 244 additions and 110 deletions

View File

@ -7,6 +7,7 @@
'use strict'; 'use strict';
var assert = require('assert'); var assert = require('assert');
var native = require('../utils/native');
var BIG_ENDIAN = new Int8Array(Int16Array.of(1).buffer)[0] === 0; var BIG_ENDIAN = new Int8Array(Int16Array.of(1).buffer)[0] === 0;
@ -176,6 +177,9 @@ ChaCha20.prototype.getCounter = function getCounter() {
return lo; return lo;
}; };
if (native)
ChaCha20 = native.ChaCha20;
/* /*
* Helpers * Helpers
*/ */
@ -489,6 +493,9 @@ Poly1305.verify = function verify(mac1, mac2) {
return (dif & 1) !== 0; return (dif & 1) !== 0;
}; };
if (native)
Poly1305 = native.Poly1305;
/** /**
* AEAD (used for bip151) * AEAD (used for bip151)
* @exports AEAD * @exports AEAD
@ -527,7 +534,7 @@ AEAD.prototype.init = function init(key, iv) {
this.chacha20.encrypt(new Buffer(32)); this.chacha20.encrypt(new Buffer(32));
// Counter should be one. // Counter should be one.
assert(this.chacha20.state[12] === 1); assert(this.chacha20.getCounter() === 1);
// Expose for debugging. // Expose for debugging.
this.polyKey = polyKey; this.polyKey = polyKey;

View File

@ -9,6 +9,7 @@
var assert = require('assert'); var assert = require('assert');
var random = require('./random'); var random = require('./random');
var native = require('../utils/native');
var nativeCrypto, supersha, hash, aes; var nativeCrypto, supersha, hash, aes;
var isBrowser = var isBrowser =
@ -17,11 +18,6 @@ var isBrowser =
if (!isBrowser) { if (!isBrowser) {
nativeCrypto = require('crypto'); nativeCrypto = require('crypto');
try {
supersha = require('supersha');
} catch (e) {
;
}
} else { } else {
hash = require('hash.js'); hash = require('hash.js');
aes = require('./aes'); aes = require('./aes');
@ -47,6 +43,9 @@ crypto.hash = function _hash(alg, data) {
return nativeCrypto.createHash(alg).update(data).digest(); return nativeCrypto.createHash(alg).update(data).digest();
}; };
if (native)
crypto.hash = native.hash;
/** /**
* Hash with ripemd160. * Hash with ripemd160.
* @param {Buffer} data * @param {Buffer} data
@ -74,11 +73,12 @@ crypto.sha1 = function sha1(data) {
*/ */
crypto.sha256 = function sha256(data) { crypto.sha256 = function sha256(data) {
if (supersha)
return supersha.sha256(data);
return crypto.hash('sha256', data); return crypto.hash('sha256', data);
}; };
if (native)
crypto.sha256 = native.sha256;
/** /**
* Hash with sha256 and ripemd160 (OP_HASH160). * Hash with sha256 and ripemd160 (OP_HASH160).
* @param {Buffer} data * @param {Buffer} data
@ -89,6 +89,9 @@ crypto.hash160 = function hash160(data) {
return crypto.ripemd160(crypto.sha256(data)); return crypto.ripemd160(crypto.sha256(data));
}; };
if (native)
crypto.hash160 = native.hash160;
/** /**
* Hash with sha256 twice (OP_HASH256). * Hash with sha256 twice (OP_HASH256).
* @param {Buffer} data * @param {Buffer} data
@ -96,11 +99,12 @@ crypto.hash160 = function hash160(data) {
*/ */
crypto.hash256 = function hash256(data) { crypto.hash256 = function hash256(data) {
if (supersha)
return supersha.hash256(data);
return crypto.sha256(crypto.sha256(data)); return crypto.sha256(crypto.sha256(data));
}; };
if (native)
crypto.hash256 = native.hash256;
/** /**
* Create a sha256 checksum (common in bitcoin). * Create a sha256 checksum (common in bitcoin).
* @param {Buffer} data * @param {Buffer} data
@ -122,6 +126,14 @@ crypto.checksum = function checksum(data) {
crypto.hmac = function hmac(alg, data, salt) { crypto.hmac = function hmac(alg, data, salt) {
var hmac; var hmac;
if (native) {
if (typeof data === 'string')
data = new Buffer(data, 'utf8');
if (typeof salt === 'string')
salt = new Buffer(salt, 'utf8');
return native.hmac(alg, data, salt);
}
if (!nativeCrypto) { if (!nativeCrypto) {
hmac = hash.hmac(hash[alg], salt); hmac = hash.hmac(hash[alg], salt);
return new Buffer(hmac.update(data).digest()); return new Buffer(hmac.update(data).digest());
@ -472,6 +484,9 @@ crypto.buildMerkleTree = function buildMerkleTree(leaves) {
return tree; return tree;
}; };
if (native)
crypto.buildMerkleTree = native.buildMerkleTree;
/** /**
* Calculate merkle root from leaves. * Calculate merkle root from leaves.
* @param {Buffer[]} leaves * @param {Buffer[]} leaves

View File

@ -35,6 +35,8 @@
var utils = require('../utils/utils'); var utils = require('../utils/utils');
var crypto = require('./crypto'); var crypto = require('./crypto');
var native = require('../utils/native');
var U32Array = typeof Uint32Array === 'function' ? Uint32Array : Array;
/** /**
* Javascript scrypt implementation. Scrypt is * Javascript scrypt implementation. Scrypt is
@ -58,6 +60,9 @@ function scrypt(passwd, salt, N, r, p, len, callback) {
if (typeof salt === 'string') if (typeof salt === 'string')
salt = new Buffer(salt, 'utf8'); salt = new Buffer(salt, 'utf8');
if (native)
return native.scryptAsync(passwd, salt, N, r, p, len, callback);
if (r * p >= (1 << 30)) if (r * p >= (1 << 30))
return callback(new Error('EFBIG')); return callback(new Error('EFBIG'));
@ -86,8 +91,8 @@ function scrypt(passwd, salt, N, r, p, len, callback) {
} }
function salsa20_8(B) { function salsa20_8(B) {
var B32 = new Array(16); var B32 = new U32Array(16);
var x = new Array(16); var x = new U32Array(16);
var i; var i;
for (i = 0; i < 16; i++) for (i = 0; i < 16; i++)

View File

@ -34,6 +34,8 @@
'use strict'; 'use strict';
var crypto = require('./crypto'); var crypto = require('./crypto');
var native = require('../utils/native');
var U32Array = typeof Uint32Array === 'function' ? Uint32Array : Array;
/** /**
* Javascript scrypt implementation. Scrypt is * Javascript scrypt implementation. Scrypt is
@ -57,6 +59,9 @@ function scrypt(passwd, salt, N, r, p, len) {
if (typeof salt === 'string') if (typeof salt === 'string')
salt = new Buffer(salt, 'utf8'); salt = new Buffer(salt, 'utf8');
if (native)
return native.scrypt(passwd, salt, N, r, p, len);
if (r * p >= (1 << 30)) if (r * p >= (1 << 30))
throw new Error('EFBIG'); throw new Error('EFBIG');
@ -78,8 +83,8 @@ function scrypt(passwd, salt, N, r, p, len) {
} }
function salsa20_8(B) { function salsa20_8(B) {
var B32 = new Array(16); var B32 = new U32Array(16);
var x = new Array(16); var x = new U32Array(16);
var i; var i;
for (i = 0; i < 16; i++) for (i = 0; i < 16; i++)

View File

@ -9,6 +9,8 @@
'use strict'; 'use strict';
var native = require('../utils/native');
/** /**
* Javascript siphash implementation. Used for compact block relay. * Javascript siphash implementation. Used for compact block relay.
* @param {Buffer} data * @param {Buffer} data
@ -16,16 +18,17 @@
* @returns {Buffer} uint64le * @returns {Buffer} uint64le
*/ */
function siphash(data, key) { function siphash24(data, key, shift) {
var blocks = Math.ceil(data.length / 8); var blocks = Math.floor(data.length / 8);
var c0 = U64(0x736f6d65, 0x70736575); var c0 = U64(0x736f6d65, 0x70736575);
var c1 = U64(0x646f7261, 0x6e646f6d); var c1 = U64(0x646f7261, 0x6e646f6d);
var c2 = U64(0x6c796765, 0x6e657261); var c2 = U64(0x6c796765, 0x6e657261);
var c3 = U64(0x74656462, 0x79746573); var c3 = U64(0x74656462, 0x79746573);
var f0 = U64(blocks << 27, 0); var f0 = U64(blocks << (shift - 32), 0);
var f1 = U64(0, 0xff); var f1 = U64(0, 0xff);
var k0 = U64.fromRaw(key, 0); var k0 = U64.fromRaw(key, 0);
var k1 = U64.fromRaw(key, 8); var k1 = U64.fromRaw(key, 8);
var p = 0;
var i, d, v0, v1, v2, v3; var i, d, v0, v1, v2, v3;
// Init // Init
@ -36,13 +39,38 @@ function siphash(data, key) {
// Blocks // Blocks
for (i = 0; i < blocks; i++) { for (i = 0; i < blocks; i++) {
d = U64.fromRaw(data, i * 8); d = U64.fromRaw(data, p);
p += 8;
v3.xor(d); v3.xor(d);
sipround(v0, v1, v2, v3); sipround(v0, v1, v2, v3);
sipround(v0, v1, v2, v3); sipround(v0, v1, v2, v3);
v0.xor(d); v0.xor(d);
} }
switch (data.length & 7) {
case 7:
f0.hi |= data[p + 6] << 16;
case 6:
f0.hi |= data[p + 5] << 8;
case 5:
f0.hi |= data[p + 4] << 0;
case 4:
f0.lo |= data[p + 3] << 24;
case 3:
f0.lo |= data[p + 2] << 16;
case 2:
f0.lo |= data[p + 1] << 8;
case 1:
f0.lo |= data[p + 0] << 0;
if (f0.lo < 0)
f0.lo += 0x100000000;
if (f0.hi < 0)
f0.hi += 0x100000000;
break;
case 0:
break;
}
// Finalization // Finalization
v3.xor(f0); v3.xor(f0);
sipround(v0, v1, v2, v3); sipround(v0, v1, v2, v3);
@ -82,6 +110,19 @@ function sipround(v0, v1, v2, v3) {
v2.rotl(32); v2.rotl(32);
} }
function siphash(data, key) {
return siphash24(data, key, 56);
}
function siphash256(data, key) {
return siphash24(data, key, 59);
}
if (native) {
siphash = native.siphash;
siphash256 = native.siphash256;
}
/* /*
* Helpers * Helpers
*/ */
@ -186,7 +227,9 @@ U64.fromRaw = function fromRaw(data, off) {
* Expose * Expose
*/ */
exports = siphash; exports = siphash256;
exports.siphash = siphash;
exports.siphash256 = siphash256;
exports.U64 = U64; exports.U64 = U64;
module.exports = exports; module.exports = exports;

View File

@ -11,6 +11,9 @@ var constants = require('../protocol/constants');
var assert = require('assert'); var assert = require('assert');
var BufferReader = require('../utils/reader'); var BufferReader = require('../utils/reader');
var BufferWriter = require('../utils/writer'); var BufferWriter = require('../utils/writer');
var murmur3 = require('../utils/murmur3');
var sum32 = murmur3.sum32;
var mul32 = murmur3.mul32;
/* /*
* Constants * Constants
@ -408,94 +411,10 @@ RollingFilter.prototype.added = function added(val, enc) {
return false; return false;
}; };
/** /*
* Murmur3 hash. * Helpers
* @memberof Bloom
* @param {Buffer} data
* @param {Number} seed
* @returns {Number}
*/ */
function murmur3(data, seed) {
var tail = data.length - (data.length % 4);
var c1 = 0xcc9e2d51;
var c2 = 0x1b873593;
var h1 = seed;
var i, k1;
for (i = 0; i < tail; i += 4) {
k1 = (data[i + 3] << 24)
| (data[i + 2] << 16)
| (data[i + 1] << 8)
| data[i];
k1 = mul32(k1, c1);
k1 = rotl32(k1, 15);
k1 = mul32(k1, c2);
h1 ^= k1;
h1 = rotl32(h1, 13);
h1 = sum32(mul32(h1, 5), 0xe6546b64);
}
k1 = 0;
switch (data.length & 3) {
case 3:
k1 ^= data[tail + 2] << 16;
case 2:
k1 ^= data[tail + 1] << 8;
case 1:
k1 ^= data[tail + 0];
k1 = mul32(k1, c1);
k1 = rotl32(k1, 15);
k1 = mul32(k1, c2);
h1 ^= k1;
}
h1 ^= data.length;
h1 ^= h1 >>> 16;
h1 = mul32(h1, 0x85ebca6b);
h1 ^= h1 >>> 13;
h1 = mul32(h1, 0xc2b2ae35);
h1 ^= h1 >>> 16;
if (h1 < 0)
h1 += 0x100000000;
return h1;
}
function mul32(a, b) {
var alo = a & 0xffff;
var blo = b & 0xffff;
var ahi = a >>> 16;
var bhi = b >>> 16;
var r, lo, hi;
lo = alo * blo;
hi = (ahi * blo + bhi * alo) & 0xffff;
hi += lo >>> 16;
lo &= 0xffff;
r = (hi << 16) | lo;
if (r < 0)
r += 0x100000000;
return r;
}
function sum32(a, b) {
var r = (a + b) & 0xffffffff;
if (r < 0)
r += 0x100000000;
return r;
}
function rotl32(w, b) {
return (w << b) | (w >>> (32 - b));
}
function read(data, off) { function read(data, off) {
return { return {
hi: data.readUInt32LE(off + 4, true), hi: data.readUInt32LE(off + 4, true),

112
lib/utils/murmur3.js Normal file
View File

@ -0,0 +1,112 @@
/*!
* murmur3.js - murmur3 hash for bcoin
* Copyright (c) 2014-2015, Fedor Indutny (MIT License)
* Copyright (c) 2014-2016, Christopher Jeffrey (MIT License).
* https://github.com/bcoin-org/bcoin
*/
'use strict';
var native = require('./native');
/**
* Murmur3 hash.
* @memberof Bloom
* @param {Buffer} data
* @param {Number} seed
* @returns {Number}
*/
function murmur3(data, seed) {
var tail = data.length - (data.length % 4);
var c1 = 0xcc9e2d51;
var c2 = 0x1b873593;
var h1 = seed;
var i, k1;
for (i = 0; i < tail; i += 4) {
k1 = (data[i + 3] << 24)
| (data[i + 2] << 16)
| (data[i + 1] << 8)
| data[i];
k1 = mul32(k1, c1);
k1 = rotl32(k1, 15);
k1 = mul32(k1, c2);
h1 ^= k1;
h1 = rotl32(h1, 13);
h1 = sum32(mul32(h1, 5), 0xe6546b64);
}
k1 = 0;
switch (data.length & 3) {
case 3:
k1 ^= data[tail + 2] << 16;
case 2:
k1 ^= data[tail + 1] << 8;
case 1:
k1 ^= data[tail + 0];
k1 = mul32(k1, c1);
k1 = rotl32(k1, 15);
k1 = mul32(k1, c2);
h1 ^= k1;
}
h1 ^= data.length;
h1 ^= h1 >>> 16;
h1 = mul32(h1, 0x85ebca6b);
h1 ^= h1 >>> 13;
h1 = mul32(h1, 0xc2b2ae35);
h1 ^= h1 >>> 16;
if (h1 < 0)
h1 += 0x100000000;
return h1;
}
if (native)
murmur3 = native.murmur3;
function mul32(a, b) {
var alo = a & 0xffff;
var blo = b & 0xffff;
var ahi = a >>> 16;
var bhi = b >>> 16;
var r, lo, hi;
lo = alo * blo;
hi = (ahi * blo + bhi * alo) & 0xffff;
hi += lo >>> 16;
lo &= 0xffff;
r = (hi << 16) | lo;
if (r < 0)
r += 0x100000000;
return r;
}
function sum32(a, b) {
var r = (a + b) & 0xffffffff;
if (r < 0)
r += 0x100000000;
return r;
}
function rotl32(w, b) {
return (w << b) | (w >>> (32 - b));
}
/**
* Expose
*/
exports = murmur3;
exports.murmur3 = murmur3;
exports.mul32 = mul32;
exports.sum32 = sum32;
exports.rotl32 = rotl32;
module.exports = exports;

21
lib/utils/native.js Normal file
View File

@ -0,0 +1,21 @@
/*!
* native.js - native bindings for bcoin
* Copyright (c) 2016, Christopher Jeffrey (MIT License).
* https://github.com/bcoin-org/bcoin
*/
'use strict';
var isBrowser =
(typeof process !== 'undefined' && process.browser)
|| typeof window !== 'undefined';
module.exports = null;
if (!isBrowser && +process.env.BCOIN_NO_NATIVE !== 1) {
try {
module.exports = require('bcoin-native');
} catch (e) {
;
}
}

View File

@ -16,6 +16,7 @@
var utils = exports; var utils = exports;
var assert = require('assert'); var assert = require('assert');
var native = require('./native');
var bn = require('bn.js'); var bn = require('bn.js');
var util = require('util'); var util = require('util');
var Number, Math, Date; var Number, Math, Date;
@ -165,6 +166,9 @@ utils.toBase58 = function toBase58(data) {
return str; return str;
}; };
if (native)
utils.toBase58 = native.toBase58;
/** /**
* Decode a base58 string. * Decode a base58 string.
* @see https://github.com/bitcoin/bitcoin/blob/master/src/base58.cpp * @see https://github.com/bitcoin/bitcoin/blob/master/src/base58.cpp
@ -217,6 +221,9 @@ utils.fromBase58 = function fromBase58(str) {
return out; return out;
}; };
if (native)
utils.fromBase58 = native.fromBase58;
/** /**
* Test whether a string is base58 (note that you * Test whether a string is base58 (note that you
* may get a false positive on a hex string). * may get a false positive on a hex string).

View File

@ -40,11 +40,11 @@
"elliptic": "6.3.1" "elliptic": "6.3.1"
}, },
"optionalDependencies": { "optionalDependencies": {
"bcoin-native": "0.0.2",
"leveldown": "git://github.com/Level/leveldown.git#leveldb-1.19", "leveldown": "git://github.com/Level/leveldown.git#leveldb-1.19",
"secp256k1": "3.2.0", "secp256k1": "3.2.0",
"socket.io": "1.4.8", "socket.io": "1.4.8",
"socket.io-client": "1.4.8", "socket.io-client": "1.4.8"
"supersha": "0.0.1"
}, },
"devDependencies": { "devDependencies": {
"browserify": "13.1.0", "browserify": "13.1.0",
@ -67,7 +67,7 @@
"child_process": "./browser/empty.js", "child_process": "./browser/empty.js",
"os": "./browser/empty.js", "os": "./browser/empty.js",
"net": "./browser/empty.js", "net": "./browser/empty.js",
"supersha": "./browser/empty.js", "bcoin-native": "./browser/empty.js",
"secp256k1": "./browser/empty.js" "secp256k1": "./browser/empty.js"
} }
} }

View File

@ -50,7 +50,7 @@ describe('ChaCha20 / Poly1305 / AEAD', function() {
var aead = new AEAD(); var aead = new AEAD();
aead.init(key, nonce); aead.init(key, nonce);
assert.equal(aead.chacha20.state[12], 1); assert.equal(aead.chacha20.getCounter(), 1);
assert.deepEqual(aead.polyKey, pk); assert.deepEqual(aead.polyKey, pk);
aead.aad(aad); aead.aad(aad);
var plainenc = new Buffer(plain); var plainenc = new Buffer(plain);
@ -60,7 +60,7 @@ describe('ChaCha20 / Poly1305 / AEAD', function() {
var aead = new AEAD(); var aead = new AEAD();
aead.init(key, nonce); aead.init(key, nonce);
assert.equal(aead.chacha20.state[12], 1); assert.equal(aead.chacha20.getCounter(), 1);
assert.deepEqual(aead.polyKey, pk); assert.deepEqual(aead.polyKey, pk);
aead.aad(aad); aead.aad(aad);
aead.decrypt(ciphertext); aead.decrypt(ciphertext);