forked from trezor/trezor-crypto
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
move interface fns to post.js, add web worker & browserify test
- Loading branch information
Showing
5 changed files
with
220 additions
and
56 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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'); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
); | ||
} |