Skip to content

Commit

Permalink
move interface fns to post.js, add web worker & browserify test
Browse files Browse the repository at this point in the history
  • Loading branch information
jpochyla authored and prusnak committed May 12, 2016
1 parent da0a2f8 commit 3c0176a
Show file tree
Hide file tree
Showing 5 changed files with 220 additions and 56 deletions.
12 changes: 8 additions & 4 deletions emscripten/Makefile
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
EMFLAGS = \
-O2 --closure 1 \
-Os --closure 1 \
--memory-init-file 0 \
--pre-js pre.js --post-js post.js \
-s EXPORTED_FUNCTIONS='["_hdnode_public_ckd", "_ecdsa_get_address"]' \
-s EXPORTED_FUNCTIONS='["_hdnode_public_ckd", "_ecdsa_get_address"]'

SRC = ../bignum.c ../ecdsa.c ../secp256k1.c ../hmac.c ../bip32.c \
../base58.c ../ripemd160.c ../sha2.c ../rand.c

test: node_modules trezor-crypto.js
test-node: node_modules trezor-crypto.js test.js
node test.js

test-browserify.js: node_modules trezor-crypto.js test.js
browserify test.js -o $@ --noparse=`pwd`/trezor-crypto.js
@echo "open test.html in your favourite browser"

trezor-crypto.js: $(SRC)
emcc $(EMFLAGS) -o $@ $^

node_modules:
npm install

clean:
rm -f trezor-crypto.js
rm -f trezor-crypto.js test-browserify.js
114 changes: 113 additions & 1 deletion emscripten/post.js
Original file line number Diff line number Diff line change
@@ -1 +1,113 @@
module.exports = Module;
/*
typedef struct {
uint32_t depth;
uint32_t fingerprint;
uint32_t child_num;
uint8_t chain_code[32];
uint8_t private_key[32];
uint8_t public_key[33];
} HDNode;
*/

var HEAPU8 = Module['HEAPU8'];
var _malloc = Module['_malloc'];
var _hdnode_public_ckd = Module['_hdnode_public_ckd'];
var _ecdsa_get_address = Module['_ecdsa_get_address'];
var Pointer_stringify = Module['Pointer_stringify'];

// HDNode struct global
var HDNODE_SIZE = 4 + 4 + 4 + 32 + 32 + 33;
var _hdnode = _malloc(HDNODE_SIZE);

// address string global
var ADDRESS_SIZE = 40; // maximum size
var _address = _malloc(ADDRESS_SIZE);

/*
* public library interface
*/

/**
* @param {HDNode} node HDNode struct, see the definition above
* @return {Uint8Array}
*/
function serializeNode(node) {
var b = new ArrayBuffer(HDNODE_SIZE);

var u32 = new Uint32Array(b, 0, 12);
u32[0] = node['depth'];
u32[1] = node['fingerprint'];
u32[2] = node['child_num'];

var u8 = new Uint8Array(b, 0, HDNODE_SIZE);
u8.set(node['chain_code'], 12);
u8.set(node['public_key'], 12 + 32 + 32);

return u8;
}

/**
* @param {Uint8Array} sn serialized node, see `serializeNode`
* @param {Number} index BIP32 index of the address
* @param {Number} version address version byte
* @return {String}
*/
function deriveAddress(sn, index, version) {
HEAPU8.set(sn, _hdnode);
_hdnode_public_ckd(_hdnode, index);
_ecdsa_get_address(_hdnode + 12 + 32 + 32, version, _address, ADDRESS_SIZE);
return Pointer_stringify(_address);
}

/**
* @param {HDNode} node HDNode struct, see the definition above
* @param {Number} from index of the first address
* @param {Number} to index of the last address
* @param {Number} version address version byte
* @return {Array<String>}
*/
function deriveAddressRange(node, from, to, version) {
var addresses = [];
var sn = serializeNode(node);
var i;
for (i = from; i <= to; i++) {
addresses.push(deriveAddress(sn, i, version));
}
return addresses;
}

if (typeof module !== 'undefined') {
module['exports'] = {
'serializeNode': serializeNode,
'deriveAddress': deriveAddress,
'deriveAddressRange': deriveAddressRange
};
}

/*
* Web worker processing
*/

function processMessage(event) {
var data = event['data'];
var type = data['type'];

switch (type) {
case 'deriveAddressRange':
var response = deriveAddressRange(
data['node'],
data['from'],
data['to'],
data['version']
);
postMessage(response);
break;

default:
throw new Error('Unknown message type: ' + type);
}
}

if (ENVIRONMENT_IS_WORKER) {
this['onmessage'] = processMessage;
}
9 changes: 9 additions & 0 deletions emscripten/pre.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// stub importScripts for the faulty detection of web worker env
if (typeof importScripts === 'undefined'
&& typeof WorkerGlobalScope !== 'undefined'
&& this instanceof WorkerGlobalScope
) {
this.importScripts = function () {
throw new Error('importScripts is a stub');
};
}
8 changes: 8 additions & 0 deletions emscripten/test.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<script async src="test-browserify.js"></script>
</body>
</html>
133 changes: 82 additions & 51 deletions emscripten/test.js
Original file line number Diff line number Diff line change
@@ -1,67 +1,98 @@
var crypto = require('./trezor-crypto');
var bitcoin = require('bitcoinjs-lib');

/* typedef struct {
uint32_t depth;
uint32_t fingerprint;
uint32_t child_num;
uint8_t chain_code[32];
uint8_t private_key[32];
uint8_t public_key[33];
} HDNode;
*/
var XPUB =
'xpub6AHA9hZDN11k2ijHMeS5QqHx2KP9aMBRhTDqANMnwVtdyw2TDYRm' +
'F8PjpvwUFcL1Et8Hj59S3gTSMcUQ5gAqTz3Wd8EsMTmF3DChhqPQBnU';
var node = bitcoin.HDNode.fromBase58(XPUB).derive(0);

var HDNODE_SIZE = 4 + 4 + 4 + 32 + 32 + 33;
var hdnode = crypto._malloc(HDNODE_SIZE);
var nodeStruct = {
depth: node.depth,
child_num: node.index,
fingerprint: node.parentFingerprint,
chain_code: node.chainCode,
public_key: node.pubKey.toBuffer()
};
var nodeSerialized = crypto.serializeNode(nodeStruct);

var ADDRESS_SIZE = 40; // maximum size
var address = crypto._malloc(ADDRESS_SIZE);
var suite;
var worker;

function prepareNode(n) {
var b = new ArrayBuffer(HDNODE_SIZE);
var u8 = new Uint8Array(b, 0, HDNODE_SIZE);
var u32 = new Uint32Array(b, 0, 12);
if (typeof Worker !== 'undefined') {
console.log('enabling web worker benchmark');
worker = new Worker('./trezor-crypto.js');
worker.onerror = function (error) {
console.error('worker:', error);
};
suite = [
// benchBitcoinJS,
// benchBrowserify,
benchWorker
];
} else {
suite = [
benchBitcoinJS,
benchBrowserify
];
}

u32[0] = n.depth;
u32[1] = n.parentFingerprint;
u32[2] = n.index;
u8.set(n.chainCode, 12);
u8.set(n.pubKey.toBuffer(), 12 + 32 + 32);
benchmark(suite, 1000, 1000);

return u8;
function benchmark(suite, delay, ops) {
(function cycle(i) {
setTimeout(function () {
var benchmark = suite[i];
runBenchmark(benchmark, ops, function (runtime) {
printResult(benchmark, ops, runtime);
cycle(i+1 < suite.length ? i+1 : 0);
});
}, delay);
}(0));
}

function deriveAddress(pn, i, version) {
crypto.HEAPU8.set(pn, hdnode);
crypto._hdnode_public_ckd(hdnode, i);
crypto._ecdsa_get_address(hdnode + 12 + 32 + 32, version, address, ADDRESS_SIZE);
return crypto.Pointer_stringify(address);
function benchBitcoinJS(ops, fn) {
var i;
for (i = 0; i < ops; i++) {
node.derive(i).getAddress();
}
fn();
}

// benching code

var bitcoin = require('bitcoinjs-lib');

var node = bitcoin.HDNode.fromBase58(
'xpub6AHA9hZDN11k2ijHMeS5QqHx2KP9aMBRhTDqANMnwVtdyw2TDYRm' +
'F8PjpvwUFcL1Et8Hj59S3gTSMcUQ5gAqTz3Wd8EsMTmF3DChhqPQBnU'
).derive(0);
function benchBrowserify(ops, fn) {
var i;
for (i = 0; i < ops; i++) {
crypto.deriveAddress(nodeSerialized, i, 0);
}
fn();
}

timeBitcoinjs(node);
timeTrezorCrypto(node);
function benchWorker(ops, fn) {
worker.onmessage = function (event) {
fn();
};
worker.postMessage({
type: 'deriveAddressRange',
node: nodeStruct,
from: 0,
to: ops - 1,
version: 0
});
}

function timeBitcoinjs(n) {
console.time('bitcoinjs')
for (var i = 0; i < 1000; i++) {
n.derive(i).getAddress()
}
console.timeEnd('bitcoinjs')
function runBenchmark(benchmark, ops, fn) {
var start = new Date();
benchmark(ops, function () {
var end = new Date();
fn(end - start);
});
}

function timeTrezorCrypto(n) {
var nP = prepareNode(n);
console.time('trezor-crypto')
for (var i = 0; i < 1000; i++) {
deriveAddress(nP, i, 0);
}
console.timeEnd('trezor-crypto')
function printResult(benchmark, ops, runtime) {
var opssec = (ops / runtime) * 1000;
console.log(
benchmark.name,
'ops #', ops,
'runtime', runtime / 1000,
'sec, ops/sec', opssec
);
}

0 comments on commit 3c0176a

Please sign in to comment.