From e2655f553d958c34bca61717c7298cd525d5fec2 Mon Sep 17 00:00:00 2001 From: olalonde Date: Mon, 5 May 2014 15:20:59 +0800 Subject: [PATCH 01/15] Added EC Point multiplication to browser/Point.js --- lib/Armory.js | 41 +++++++++++++++++++++++++++++++++++++++++ lib/browser/Point.js | 24 ++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 lib/Armory.js diff --git a/lib/Armory.js b/lib/Armory.js new file mode 100644 index 0000000..03a7b10 --- /dev/null +++ b/lib/Armory.js @@ -0,0 +1,41 @@ +var Point = require('./browser/Point'), + twoSha256 = require('../util').twoSha256, + BigInteger = require('../browser/vendor-bundle.js').BigInteger; + +// TODO: use native modules instead of browser libraries + +/** + * For now, this class can only supports derivation from public key + * It doesn't support private key derivation (TODO). + * + * @example examples/Armory.js + */ +function Armory (chaincode, pubkey) { + this.chaincode = new Buffer(chaincode, 'hex'); + this.pubkey = new Buffer(pubkey, 'hex'); +} + +Armory.prototype.generatePubKey = function () { + var pubKey = this.pubkey; + var chainCode = this.chaincode; + var chainXor = twoSha256(pubKey); + + for (var i = 0; i < 32; i++) + chainXor[i] ^= chainCode[i]; + + var A = new BigInteger(chainXor.toString('hex'), 16); + + var pt = Point.fromUncompressedPubKey(pubKey); + pt = Point.multiply(pt, A); + + var new_pubkey = pt.toUncompressedPubKey(); + + return new_pubkey; +}; + +Armory.prototype.next = function () { + var next_pubkey = this.generatePubKey(); + return new Armory(this.chaincode, next_pubkey); +}; + +module.exports = Armory; diff --git a/lib/browser/Point.js b/lib/browser/Point.js index 1e935e0..5dd40b9 100644 --- a/lib/browser/Point.js +++ b/lib/browser/Point.js @@ -48,6 +48,30 @@ Point.add = function(p1, p2) { return point; }; +Point.multiply = function(p1, A) { + var ecparams = getSECCurveByName('secp256k1'); + + var p1xhex = p1.x.toBuffer({size: 32}).toString('hex'); + var p1x = new BigInteger(p1xhex, 16); + var p1yhex = p1.y.toBuffer({size: 32}).toString('hex'); + var p1y = new BigInteger(p1yhex, 16); + var p1px = new ECFieldElementFp(ecparams.getCurve().getQ(), p1x); + var p1py = new ECFieldElementFp(ecparams.getCurve().getQ(), p1y); + var p1p = new ECPointFp(ecparams.getCurve(), p1px, p1py); + + var p = p1p.multiply(A); + + var point = new Point(); + var pointxbuf = new Buffer(p.getX().toBigInteger().toByteArrayUnsigned()); + point.x = bignum.fromBuffer(pointxbuf, {size: pointxbuf.length}); + assert(pointxbuf.length <= 32); + var pointybuf = new Buffer(p.getY().toBigInteger().toByteArrayUnsigned()); + assert(pointybuf.length <= 32); + point.y = bignum.fromBuffer(pointybuf, {size: pointybuf.length}); + + return point; +}; + //convert the public key of a Key into a Point Point.fromUncompressedPubKey = function(pubkey) { var point = new Point(); From 0e1948b8f3b4036c6345eb494caae6a674430bce Mon Sep 17 00:00:00 2001 From: olalonde Date: Mon, 5 May 2014 15:21:26 +0800 Subject: [PATCH 02/15] Implemented Armory style key derivation from chain code and public key (WIP) --- examples/Armory.js | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 examples/Armory.js diff --git a/examples/Armory.js b/examples/Armory.js new file mode 100644 index 0000000..beee650 --- /dev/null +++ b/examples/Armory.js @@ -0,0 +1,30 @@ +var Armory = require('../lib/Armory'); +var Address = require('../lib/Address'); +var Point = require('../lib/Point'); +var bignum = require('bignum'); +var Key = require('../lib/Key'); + +// Chain code can be generated from paper backup +// on brainwallet.org/#chains +// +// Paper backup used for testing: +// +// aagh hjfj sihk ietj giik wwai awtd uodh hnji +// soss uaku egod utai itos fijj ihgi jhau jtoo +var chaincode = '84ac14bc4b388b33da099a0b4ee3b507284d99e1476639e36e5ca5e6af86481e'; + +// Initial public key can be retrieved from paper backup +//var PublicX = '9df5 23e7 18b9 1f59 a790 2d46 999f 9357 ccf8 7208 24d4 3076 4516 b809 f7ab ce4e' +//var PublicY = '66ba 5d21 4682 0dae 401d 9506 8437 2516 79f9 0c56 4186 cc50 07df c6d0 6989 1ff4'; +//var pubkey = '04' + PublicX.split(' ').join('') + PublicY.split(' ').join(''); + +// mmm... can't figure out how to arrive at same pubkey as brainwallet +var pubkey = '045a09a3286873a72f164476bde9d1d8e5c2bc044e35aa47eb6e798e325a86417f7c35b61d9905053533e0b4f2a26eca0330aadf21c638969e45aaace50e4c0c87'; + +var armory = new Armory(chaincode, pubkey); + +var pubkey; +for (var i = 0; i < 5; i++) { + armory = armory.next(); + console.log(Address.fromPubKey(armory.pubkey).as('base58')); +} From 468ebddc1e49881ed3a352fecb450f64feae1d45 Mon Sep 17 00:00:00 2001 From: olalonde Date: Mon, 5 May 2014 17:15:39 +0800 Subject: [PATCH 03/15] Implemented Armory initializing from seed --- examples/Armory.js | 12 ++++++++++ lib/Armory.js | 58 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/examples/Armory.js b/examples/Armory.js index beee650..f02553e 100644 --- a/examples/Armory.js +++ b/examples/Armory.js @@ -17,6 +17,8 @@ var chaincode = '84ac14bc4b388b33da099a0b4ee3b507284d99e1476639e36e5ca5e6af86481 //var PublicX = '9df5 23e7 18b9 1f59 a790 2d46 999f 9357 ccf8 7208 24d4 3076 4516 b809 f7ab ce4e' //var PublicY = '66ba 5d21 4682 0dae 401d 9506 8437 2516 79f9 0c56 4186 cc50 07df c6d0 6989 1ff4'; //var pubkey = '04' + PublicX.split(' ').join('') + PublicY.split(' ').join(''); +//console.log(pubkey); + // mmm... can't figure out how to arrive at same pubkey as brainwallet var pubkey = '045a09a3286873a72f164476bde9d1d8e5c2bc044e35aa47eb6e798e325a86417f7c35b61d9905053533e0b4f2a26eca0330aadf21c638969e45aaace50e4c0c87'; @@ -28,3 +30,13 @@ for (var i = 0; i < 5; i++) { armory = armory.next(); console.log(Address.fromPubKey(armory.pubkey).as('base58')); } + +// Derive stuff from root cod +var seed = [ + 'aagh hjfj sihk ietj giik wwai awtd uodh hnji', + 'soss uaku egod utai itos fijj ihgi jhau jtoo' +].join('\n'); + +console.log(seed); + +console.log(Armory.fromSeed(seed)); diff --git a/lib/Armory.js b/lib/Armory.js index 03a7b10..6cc0d85 100644 --- a/lib/Armory.js +++ b/lib/Armory.js @@ -1,4 +1,6 @@ var Point = require('./browser/Point'), + buffertools = require('buffertools'), + sha256 = require('../util').sha256, twoSha256 = require('../util').twoSha256, BigInteger = require('../browser/vendor-bundle.js').BigInteger; @@ -38,4 +40,60 @@ Armory.prototype.next = function () { return new Armory(this.chaincode, next_pubkey); }; +function decode (str) { + var from = '0123456789abcdef'; + var to = 'asdfghjkwertuion'; + var res = ''; + for (var i = 0; i < str.length; i++) + res += from.charAt(to.indexOf(str.charAt(i))); + return res; +} + +Armory.decodeSeed = function (seed) { + var keys = seed.split('\n'); + var lines = []; + + for (var i = 0; i < keys.length; i++) { + var k = keys[i].replace(' ',''); + var raw = new Buffer(decode(k), 'hex'); + var data = raw.slice(0, 16); + lines.push(data); + } + + var privKey = Buffer.concat([ lines[0], lines[1] ]); + var chainCode = (lines.length==4) ? + Buffer.concat([ lines[2], lines[3] ]) : Armory.deriveChaincode(privKey); + + return { + privKey: privKey, + chainCode: chainCode + }; +}; + +// Derive chain code from root key +Armory.fromSeed = function (seed) { + var res = Armory.decodeSeed(seed); + return res; +}; + +Armory.deriveChaincode = function (root) { + var msg = 'Derive Chaincode from Root Key'; + var hash = twoSha256(root); + + var okey = []; + var ikey = []; + for (var i = 0; i < hash.length; i++) { + okey.push(0x5c ^ hash[i]); + ikey.push(0x36 ^ hash[i]); + } + + okey = new Buffer(okey); + ikey = new Buffer(ikey); + + var m = new Buffer(msg, 'utf8'); + var a = sha256(Buffer.concat([ ikey, m ])); + var b = sha256(Buffer.concat([ okey, a ])); + return b; +}; + module.exports = Armory; From fa211f98bb72e2148c1ed20224d2bbc3dee3ec59 Mon Sep 17 00:00:00 2001 From: olalonde Date: Mon, 5 May 2014 17:28:24 +0800 Subject: [PATCH 04/15] Armory: cleaned up example --- examples/Armory.js | 35 +++++++++++++---------------------- lib/Armory.js | 9 ++++++++- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/examples/Armory.js b/examples/Armory.js index f02553e..61cd2c1 100644 --- a/examples/Armory.js +++ b/examples/Armory.js @@ -1,42 +1,33 @@ var Armory = require('../lib/Armory'); var Address = require('../lib/Address'); -var Point = require('../lib/Point'); -var bignum = require('bignum'); -var Key = require('../lib/Key'); - -// Chain code can be generated from paper backup -// on brainwallet.org/#chains -// -// Paper backup used for testing: -// -// aagh hjfj sihk ietj giik wwai awtd uodh hnji -// soss uaku egod utai itos fijj ihgi jhau jtoo -var chaincode = '84ac14bc4b388b33da099a0b4ee3b507284d99e1476639e36e5ca5e6af86481e'; // Initial public key can be retrieved from paper backup -//var PublicX = '9df5 23e7 18b9 1f59 a790 2d46 999f 9357 ccf8 7208 24d4 3076 4516 b809 f7ab ce4e' -//var PublicY = '66ba 5d21 4682 0dae 401d 9506 8437 2516 79f9 0c56 4186 cc50 07df c6d0 6989 1ff4'; -//var pubkey = '04' + PublicX.split(' ').join('') + PublicY.split(' ').join(''); -//console.log(pubkey); + +var PublicX = '9df5 23e7 18b9 1f59 a790 2d46 999f 9357 ccf8 7208 24d4 3076 4516 b809 f7ab ce4e'; +var PublicY = '66ba 5d21 4682 0dae 401d 9506 8437 2516 79f9 0c56 4186 cc50 07df c6d0 6989 1ff4'; +var pubkey = '04' + PublicX.split(' ').join('') + PublicY.split(' ').join(''); -// mmm... can't figure out how to arrive at same pubkey as brainwallet -var pubkey = '045a09a3286873a72f164476bde9d1d8e5c2bc044e35aa47eb6e798e325a86417f7c35b61d9905053533e0b4f2a26eca0330aadf21c638969e45aaace50e4c0c87'; +// Chain code can be generated by entering paper backup +// on brainwallet.org/#chains or by using Armory.fromSeed() below + +var chaincode = '84ac14bc4b388b33da099a0b4ee3b507284d99e1476639e36e5ca5e6af86481e'; var armory = new Armory(chaincode, pubkey); var pubkey; for (var i = 0; i < 5; i++) { - armory = armory.next(); console.log(Address.fromPubKey(armory.pubkey).as('base58')); + armory = armory.next(); } -// Derive stuff from root cod +// Derive first public key and chain code from seed var seed = [ 'aagh hjfj sihk ietj giik wwai awtd uodh hnji', 'soss uaku egod utai itos fijj ihgi jhau jtoo' ].join('\n'); -console.log(seed); -console.log(Armory.fromSeed(seed)); +var a = Armory.fromSeed(seed); + +console.log(Address.fromPubKey(a.next().pubkey).as('base58')); diff --git a/lib/Armory.js b/lib/Armory.js index 6cc0d85..07e15f2 100644 --- a/lib/Armory.js +++ b/lib/Armory.js @@ -1,4 +1,5 @@ var Point = require('./browser/Point'), + Key = require('./Key'), buffertools = require('buffertools'), sha256 = require('../util').sha256, twoSha256 = require('../util').twoSha256, @@ -73,7 +74,13 @@ Armory.decodeSeed = function (seed) { // Derive chain code from root key Armory.fromSeed = function (seed) { var res = Armory.decodeSeed(seed); - return res; + // generate first public key + var key = new Key(); + key.private = res.privKey; + key.compressed = false; + key.regenerateSync(); + + return new Armory(res.chainCode, key.public); }; Armory.deriveChaincode = function (root) { From da71cdf62da94a25e57a6f72bfc1ffc2779f4f80 Mon Sep 17 00:00:00 2001 From: olalonde Date: Mon, 5 May 2014 17:36:35 +0800 Subject: [PATCH 05/15] Armory: added descriptive log in examples/Armory.js --- examples/Armory.js | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/examples/Armory.js b/examples/Armory.js index 61cd2c1..1e11ac4 100644 --- a/examples/Armory.js +++ b/examples/Armory.js @@ -7,7 +7,6 @@ var PublicX = '9df5 23e7 18b9 1f59 a790 2d46 999f 9357 ccf8 7208 24d4 3076 4516 var PublicY = '66ba 5d21 4682 0dae 401d 9506 8437 2516 79f9 0c56 4186 cc50 07df c6d0 6989 1ff4'; var pubkey = '04' + PublicX.split(' ').join('') + PublicY.split(' ').join(''); - // Chain code can be generated by entering paper backup // on brainwallet.org/#chains or by using Armory.fromSeed() below @@ -15,7 +14,12 @@ var chaincode = '84ac14bc4b388b33da099a0b4ee3b507284d99e1476639e36e5ca5e6af86481 var armory = new Armory(chaincode, pubkey); -var pubkey; +console.log('Deriving public keys for'); +console.log('------------------------'); +console.log('Chain code: %s', chaincode); +console.log('Public key: %s', pubkey); +console.log(''); + for (var i = 0; i < 5; i++) { console.log(Address.fromPubKey(armory.pubkey).as('base58')); armory = armory.next(); @@ -25,9 +29,19 @@ for (var i = 0; i < 5; i++) { var seed = [ 'aagh hjfj sihk ietj giik wwai awtd uodh hnji', 'soss uaku egod utai itos fijj ihgi jhau jtoo' -].join('\n'); +]; +console.log(''); +console.log(''); +console.log('Deriving public keys for'); +console.log('------------------------'); +console.log('Seed: %s', seed.join(' ')); +console.log(''); -var a = Armory.fromSeed(seed); +// skip first public key +var a = Armory.fromSeed(seed.join('\n')).next(); -console.log(Address.fromPubKey(a.next().pubkey).as('base58')); +for (var i = 0; i < 5; i++) { + console.log(Address.fromPubKey(a.pubkey).as('base58')); + a = a.next(); +} From f647af50b7343c5d52702a875a2ab5bb0cddd11b Mon Sep 17 00:00:00 2001 From: olalonde Date: Mon, 5 May 2014 18:45:16 +0800 Subject: [PATCH 06/15] Fixed browser exception caused by lib/Curve.js --- lib/Curve.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/Curve.js b/lib/Curve.js index f2d140d..dfdef52 100644 --- a/lib/Curve.js +++ b/lib/Curve.js @@ -4,14 +4,20 @@ var bignum = imports.bignum || require('bignum'); var Point = imports.Point || require('./Point'); var n = bignum.fromBuffer(new Buffer("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 'hex'), {size: 32}); -var G = new Point(bignum.fromBuffer(new Buffer("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 'hex'), {size: 32}), - bignum.fromBuffer(new Buffer("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 'hex'), {size: 32})); -/* secp256k1 curve */ + var Curve = function() { }; +/* secp256k1 curve */ +var G; Curve.getG = function() { + // don't use Point in top scope, causes exception in browser + // when Point is not loaded yet + + // use cached version if available + G = G || new Point(bignum.fromBuffer(new Buffer("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 'hex'), {size: 32}), + bignum.fromBuffer(new Buffer("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 'hex'), {size: 32})); return G; }; From b26dd779117259b9f1faf9ebde7ce922ebed8fd6 Mon Sep 17 00:00:00 2001 From: olalonde Date: Mon, 5 May 2014 18:46:03 +0800 Subject: [PATCH 07/15] Armory: added to bitcore.js, build.js and browser example --- bitcore.js | 1 + browser/build.js | 1 + examples/browser/armory.html | 58 ++++++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 examples/browser/armory.html diff --git a/bitcore.js b/bitcore.js index c1f595a..66de69d 100644 --- a/bitcore.js +++ b/bitcore.js @@ -60,5 +60,6 @@ requireWhenAccessed('WalletKey', './lib/WalletKey'); requireWhenAccessed('PeerManager', './lib/PeerManager'); requireWhenAccessed('Message', './lib/Message'); requireWhenAccessed('Electrum', './lib/Electrum'); +requireWhenAccessed('Armory', './lib/Armory'); module.exports.Buffer = Buffer; diff --git a/browser/build.js b/browser/build.js index 46cbf07..e900bf8 100644 --- a/browser/build.js +++ b/browser/build.js @@ -24,6 +24,7 @@ var pack = function (params) { var modules = [ 'lib/Address', + 'lib/Armory', 'lib/Base58', 'lib/BIP32', 'lib/Block', diff --git a/examples/browser/armory.html b/examples/browser/armory.html new file mode 100644 index 0000000..4de54d5 --- /dev/null +++ b/examples/browser/armory.html @@ -0,0 +1,58 @@ + + + + + + + + Enter you paper wallet seed:
+ +
+ +
+

+    
+  
+

From 708b59eb3ed43a35c9688467897371bdd998f2ee Mon Sep 17 00:00:00 2001
From: olalonde 
Date: Mon, 5 May 2014 18:48:46 +0800
Subject: [PATCH 08/15] Armory: fixed bug.. extra lines considered as real
 input

---
 lib/Armory.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/Armory.js b/lib/Armory.js
index 07e15f2..6cdb220 100644
--- a/lib/Armory.js
+++ b/lib/Armory.js
@@ -51,7 +51,7 @@ function decode (str) {
 }
 
 Armory.decodeSeed = function (seed) {
-  var keys = seed.split('\n');
+  var keys = seed.trim().split('\n');
   var lines = [];
 
   for (var i = 0; i < keys.length; i++) {

From 7800128b9b7cf23c7dd915abe727d4db35e4491e Mon Sep 17 00:00:00 2001
From: olalonde 
Date: Mon, 5 May 2014 18:59:55 +0800
Subject: [PATCH 09/15] Armory: from master public key

---
 examples/Armory.js | 19 +++++++++++++++++++
 lib/Armory.js      |  6 ++++++
 2 files changed, 25 insertions(+)

diff --git a/examples/Armory.js b/examples/Armory.js
index 1e11ac4..713cd8b 100644
--- a/examples/Armory.js
+++ b/examples/Armory.js
@@ -45,3 +45,22 @@ for (var i = 0; i < 5; i++) {
   console.log(Address.fromPubKey(a.pubkey).as('base58'));
   a = a.next();
 }
+
+
+var mpk = '045a09a3286873a72f164476bde9d1d8e5c2bc044e35aa47eb6e798e325a86417f7c35b61d9905053533e0b4f2a26eca0330aadf21c638969e45aaace50e4c0c8784ac14bc4b388b33da099a0b4ee3b507284d99e1476639e36e5ca5e6af86481e';
+
+console.log('');
+console.log('');
+console.log('Deriving public keys for');
+console.log('------------------------');
+console.log('Master Public Key: %s', mpk);
+console.log('');
+
+// skip first public key
+var b = Armory.fromMasterPublicKey(mpk).next();
+
+for (var i = 0; i < 5; i++) {
+  console.log(Address.fromPubKey(b.pubkey).as('base58'));
+  b = b.next();
+}
+
diff --git a/lib/Armory.js b/lib/Armory.js
index 6cdb220..4d9194f 100644
--- a/lib/Armory.js
+++ b/lib/Armory.js
@@ -41,6 +41,12 @@ Armory.prototype.next = function () {
   return new Armory(this.chaincode, next_pubkey);
 };
 
+Armory.fromMasterPublicKey = function (mpk) {
+  var pubkey = mpk.substr(0, 130);
+  var chaincode = mpk.substr(130, mpk.length);
+  return new Armory(chaincode, pubkey);
+};
+
 function decode (str) {
   var from = '0123456789abcdef';
   var to = 'asdfghjkwertuion';

From af942f9b064db0addbe8a8a3b0a2a1df17ef32b7 Mon Sep 17 00:00:00 2001
From: olalonde 
Date: Wed, 7 May 2014 08:38:45 +0800
Subject: [PATCH 10/15] Armory: added tests

---
 lib/Armory.js       |  9 +++++
 test/test.Armory.js | 92 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 101 insertions(+)
 create mode 100644 test/test.Armory.js

diff --git a/lib/Armory.js b/lib/Armory.js
index 4d9194f..2e5acc2 100644
--- a/lib/Armory.js
+++ b/lib/Armory.js
@@ -41,6 +41,15 @@ Armory.prototype.next = function () {
   return new Armory(this.chaincode, next_pubkey);
 };
 
+/**
+ * PS: MPK here represents the pubkey concatenated
+ * with the chain code. It is an unofficial standard.
+ *
+ * Armory will soon release an officially supported
+ * format:
+ *
+ * https://github.com/etotheipi/BitcoinArmory/issues/204#issuecomment-42217801
+ */
 Armory.fromMasterPublicKey = function (mpk) {
   var pubkey = mpk.substr(0, 130);
   var chaincode = mpk.substr(130, mpk.length);
diff --git a/test/test.Armory.js b/test/test.Armory.js
new file mode 100644
index 0000000..8be6fe3
--- /dev/null
+++ b/test/test.Armory.js
@@ -0,0 +1,92 @@
+'use strict';
+
+var chai = chai || require('chai');
+var bitcore = bitcore || require('../bitcore');
+
+var should = chai.should();
+
+var Armory = bitcore.Armory;
+var Address = bitcore.Address;
+
+/**
+ * This is the Armory root code that was used to generated the hard coded values in
+ * those tests:
+ */
+
+var seed = [
+  'aagh hjfj sihk ietj giik wwai awtd uodh hnji',
+  'soss uaku egod utai itos fijj ihgi jhau jtoo'
+].join('\n');
+
+/*
+ * It was retrieved by creating a wallet in Armory and creating a paper backup.
+ *
+ * This is the public key as presented on the generated Armory paper wallets:
+ */
+
+var PublicX = '9df5 23e7 18b9 1f59 a790 2d46 999f 9357 ccf8 7208 24d4 3076 4516 b809 f7ab ce4e';
+var PublicY = '66ba 5d21 4682 0dae 401d 9506 8437 2516 79f9 0c56 4186 cc50 07df c6d0 6989 1ff4';
+var pubkey = '04' + PublicX.split(' ').join('') + PublicY.split(' ').join('');
+
+/*
+ * This chain code was derived from the seed above:
+ */
+var chaincode = '84ac14bc4b388b33da099a0b4ee3b507284d99e1476639e36e5ca5e6af86481e';
+
+/*
+ * This is some addresses generated from the wallet:
+ */
+var address = [
+  '1PUzLkds8eHGjHPaW7v7h23bzmHjrRMVqz',
+  '1CGrip2uQUwhP2f3ARfbcrmtdwvWzELRmj',
+  '1BfBauMP4PX1ZBYrqH4K4R8KWrFfskrs7E',
+  '15emDCBVgBJLDP5cKxuwZ4Q77sfqEcwZvC',
+  '16tDJhMYBv1szZgRZCohWrzEvzX2bG7vEQ'
+];
+
+var instance, fromseed, first;
+
+describe('Armory', function() {
+  it('should initialze the main object', function() {
+    should.exist(Armory);
+  });
+
+  it('should be able to create instance from chaincode, pubkey', function() {
+    instance = new Armory(chaincode, pubkey);
+    should.exist(instance);
+  });
+
+  it('should be able to create instance from seed', function() {
+    fromseed = Armory.fromSeed(seed);
+    should.exist(fromseed);
+  });
+
+  it('fromseed should generate the expected chain code', function() {
+    should.equal(fromseed.chaincode.toString('hex'), chaincode.toString('hex'));
+    should.equal(fromseed.chaincode.toString('hex'), instance.chaincode.toString('hex'));
+  });
+
+  it('fromseed should be able to generate the first public key', function() {
+    first = fromseed.next();
+    should.exist(first);
+  });
+
+  it('instance created from chaincode,pubkey and the first instance generated by fromseed should match', function() {
+    should.equal(first.pubkey.toString('hex'), instance.pubkey.toString('hex'));
+    should.equal(first.chaincode.toString('hex'), instance.chaincode.toString('hex'));
+  });
+
+  it('armory should generate the expected addresses for the given chaincode,pubkey', function() {
+    var addr, a;
+    a = instance;
+    for (var i = 0; i < address.length; i++) {
+      should.equal(Address.fromPubKey(a.pubkey).as('base58'), address[i]);
+      a = a.next();
+    }
+  });
+});
+
+
+
+
+

From b5c1a7d387de1168ea8705d1e64736397b9bb7b1 Mon Sep 17 00:00:00 2001
From: olalonde 
Date: Wed, 7 May 2014 13:08:10 +0800
Subject: [PATCH 11/15] Eckey: implemented point multiplication

---
 lib/Point.js |  6 ++++
 src/eckey.cc | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/eckey.h  |  3 ++
 3 files changed, 91 insertions(+)

diff --git a/lib/Point.js b/lib/Point.js
index 64d4dd7..9231192 100644
--- a/lib/Point.js
+++ b/lib/Point.js
@@ -19,6 +19,12 @@ Point.add = function(p1, p2) {
   return Point.fromUncompressedPubKey(pubKey);
 };
 
+Point.multiply = function(p1, x) {
+  var u1 = p1.toUncompressedPubKey();
+  var pubKey = CPPKey.multiply(u1, x);
+  return Point.fromUncompressedPubKey(pubKey);
+};
+
 //convert the public key of a Key into a Point
 Point.fromUncompressedPubKey = function(pubkey) {
   var point = new Point();
diff --git a/src/eckey.cc b/src/eckey.cc
index 8f279a6..d864fd6 100644
--- a/src/eckey.cc
+++ b/src/eckey.cc
@@ -124,6 +124,7 @@ void Key::Init(Handle target)
   NODE_SET_METHOD(s_ct->GetFunction(), "generateSync", GenerateSync);
   NODE_SET_METHOD(s_ct->GetFunction(), "fromDER", FromDER);
   NODE_SET_METHOD(s_ct->GetFunction(), "addUncompressed", AddUncompressed);
+  NODE_SET_METHOD(s_ct->GetFunction(), "multiply", Multiply);
 
   target->Set(String::NewSymbol("Key"),
               s_ct->GetFunction());
@@ -486,6 +487,87 @@ Key::AddUncompressed(const Arguments& args)
   return scope.Close(rbuf->handle_);
 }
 
+Handle
+Key::Multiply(const Arguments& args)
+{
+  HandleScope scope;
+
+  if (args.Length() != 2) {
+    return VException("Two arguments expected: point0, x");
+  }
+  if (!Buffer::HasInstance(args[0])) {
+    return VException("Argument 'point0' must be of type Buffer");
+  }
+  if (Buffer::Length(args[0]) != 65) {
+    return VException("Argument 'point0' must have length 65");
+  }
+  if (!Buffer::HasInstance(args[1])) {
+    return VException("Argument 'x' must be of type Buffer");
+  }
+  if (Buffer::Length(args[1]) != 32) {
+    return VException("Argument 'x' must have length 32");
+  }
+
+  Handle point0_buf = args[0]->ToObject();
+  unsigned char *point0 = (unsigned char*) Buffer::Data(point0_buf);
+
+  Handle x_buf = args[1]->ToObject();
+  unsigned char *xval = (unsigned char*) Buffer::Data(x_buf);
+
+  EC_KEY *eckey = EC_KEY_new_by_curve_name(NID_secp256k1);
+  const EC_GROUP *group = EC_KEY_get0_group(eckey);
+
+  BN_CTX *ctx;
+  //EC_POINT *p0, *p1, *r;
+  EC_POINT *p0, *r;
+  //BIGNUM *p0x, *p0y, *p1x, *p1y, *rx, *ry;
+  BIGNUM *p0x, *p0y, *x, *rx, *ry;
+  Buffer *rbuf;
+
+  p0 = EC_POINT_new(group);
+  //p1 = EC_POINT_new(group);
+  r = EC_POINT_new(group);
+
+  p0x = BN_bin2bn(&point0[1], 32, BN_new());
+  p0y = BN_bin2bn(&point0[33], 32, BN_new());
+  x = BN_bin2bn(&xval[0], 32, BN_new());
+  //p1x = BN_bin2bn(&point1[1], 32, BN_new());
+  //p1y = BN_bin2bn(&point1[33], 32, BN_new());
+
+  ctx = BN_CTX_new();
+
+  EC_POINT_set_affine_coordinates_GFp(group, p0, p0x, p0y, ctx);
+  //EC_POINT_set_affine_coordinates_GFp(group, p1, p1x, p1y, ctx);
+
+  //int EC_POINTs_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *n, size_t num, const EC_POINT *p[], const BIGNUM *m[], BN_CTX *ctx);
+  //int EC_POINT_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *n, const EC_POINT *q, const BIGNUM *m, BN_CTX *ctx);
+  EC_POINT_mul(group, r, NULL, p0, x, ctx);
+
+  rx = BN_new();
+  ry = BN_new();
+  EC_POINT_get_affine_coordinates_GFp(group, r, rx, ry, ctx);
+
+  rbuf = Buffer::New(65);
+  EC_POINT_point2oct(group, r, POINT_CONVERSION_UNCOMPRESSED, (unsigned char *)Buffer::Data(rbuf), 65, ctx);
+
+  //free: eckey, p0, p1, r, p0x, p0y, p1x, p1y, ctx, rx, ry, /*rbuf,*/ rcx, rcy
+  BN_clear_free(ry);
+  BN_clear_free(rx);
+  //do not free rbuf - this is returned
+  BN_CTX_free(ctx);
+  BN_clear_free(p0x);
+  BN_clear_free(p0y);
+  BN_clear_free(x);
+  //BN_clear_free(p1x);
+  //BN_clear_free(p1y);
+  EC_POINT_free(r);
+  //EC_POINT_free(p1);
+  EC_POINT_free(p0);
+  EC_KEY_free(eckey);
+
+  return scope.Close(rbuf->handle_);
+}
+
 Handle
 Key::VerifySignature(const Arguments& args)
 {
diff --git a/src/eckey.h b/src/eckey.h
index 1c2b922..2507654 100644
--- a/src/eckey.h
+++ b/src/eckey.h
@@ -89,6 +89,9 @@ public:
   static Handle
     AddUncompressed(const Arguments& args);
 
+  static Handle
+    Multiply(const Arguments& args);
+
   static Handle
     VerifySignature(const Arguments& args);
 

From 59ee476b962382644b08e06f2bf554620c602d5d Mon Sep 17 00:00:00 2001
From: olalonde 
Date: Wed, 7 May 2014 13:08:35 +0800
Subject: [PATCH 12/15] Armory: use native multiplication in node and
 javascript implementation in browser

---
 lib/Armory.js        | 9 +++------
 lib/browser/Point.js | 6 ++++--
 2 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/lib/Armory.js b/lib/Armory.js
index 2e5acc2..e78ab33 100644
--- a/lib/Armory.js
+++ b/lib/Armory.js
@@ -1,9 +1,8 @@
-var Point = require('./browser/Point'),
+var Point = require('./Point'),
   Key = require('./Key'),
   buffertools = require('buffertools'),
   sha256 = require('../util').sha256,
-  twoSha256 = require('../util').twoSha256,
-  BigInteger = require('../browser/vendor-bundle.js').BigInteger;
+  twoSha256 = require('../util').twoSha256;
 
 // TODO: use native modules instead of browser libraries
 
@@ -26,10 +25,8 @@ Armory.prototype.generatePubKey = function () {
   for (var i = 0; i < 32; i++)
     chainXor[i] ^= chainCode[i];
 
-  var A = new BigInteger(chainXor.toString('hex'), 16);
-
   var pt = Point.fromUncompressedPubKey(pubKey);
-  pt = Point.multiply(pt, A);
+  pt = Point.multiply(pt, chainXor);
 
   var new_pubkey = pt.toUncompressedPubKey();
 
diff --git a/lib/browser/Point.js b/lib/browser/Point.js
index 5dd40b9..2579d12 100644
--- a/lib/browser/Point.js
+++ b/lib/browser/Point.js
@@ -48,7 +48,9 @@ Point.add = function(p1, p2) {
   return point;
 };
 
-Point.multiply = function(p1, A) {
+Point.multiply = function(p1, x) {
+  var x = new BigInteger(x.toString('hex'), 16);
+
   var ecparams = getSECCurveByName('secp256k1');
 
   var p1xhex = p1.x.toBuffer({size: 32}).toString('hex');
@@ -59,7 +61,7 @@ Point.multiply = function(p1, A) {
   var p1py = new ECFieldElementFp(ecparams.getCurve().getQ(), p1y);
   var p1p = new ECPointFp(ecparams.getCurve(), p1px, p1py);
 
-  var p = p1p.multiply(A);
+  var p = p1p.multiply(x);
 
   var point = new Point();
   var pointxbuf = new Buffer(p.getX().toBigInteger().toByteArrayUnsigned());

From 7d3307873605869f732662cc2562e410f6dcaa24 Mon Sep 17 00:00:00 2001
From: olalonde 
Date: Wed, 7 May 2014 13:20:22 +0800
Subject: [PATCH 13/15] Eckey: removed garbage comments

---
 src/eckey.cc | 11 -----------
 1 file changed, 11 deletions(-)

diff --git a/src/eckey.cc b/src/eckey.cc
index d864fd6..3f6926a 100644
--- a/src/eckey.cc
+++ b/src/eckey.cc
@@ -518,29 +518,21 @@ Key::Multiply(const Arguments& args)
   const EC_GROUP *group = EC_KEY_get0_group(eckey);
 
   BN_CTX *ctx;
-  //EC_POINT *p0, *p1, *r;
   EC_POINT *p0, *r;
-  //BIGNUM *p0x, *p0y, *p1x, *p1y, *rx, *ry;
   BIGNUM *p0x, *p0y, *x, *rx, *ry;
   Buffer *rbuf;
 
   p0 = EC_POINT_new(group);
-  //p1 = EC_POINT_new(group);
   r = EC_POINT_new(group);
 
   p0x = BN_bin2bn(&point0[1], 32, BN_new());
   p0y = BN_bin2bn(&point0[33], 32, BN_new());
   x = BN_bin2bn(&xval[0], 32, BN_new());
-  //p1x = BN_bin2bn(&point1[1], 32, BN_new());
-  //p1y = BN_bin2bn(&point1[33], 32, BN_new());
 
   ctx = BN_CTX_new();
 
   EC_POINT_set_affine_coordinates_GFp(group, p0, p0x, p0y, ctx);
-  //EC_POINT_set_affine_coordinates_GFp(group, p1, p1x, p1y, ctx);
 
-  //int EC_POINTs_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *n, size_t num, const EC_POINT *p[], const BIGNUM *m[], BN_CTX *ctx);
-  //int EC_POINT_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *n, const EC_POINT *q, const BIGNUM *m, BN_CTX *ctx);
   EC_POINT_mul(group, r, NULL, p0, x, ctx);
 
   rx = BN_new();
@@ -558,10 +550,7 @@ Key::Multiply(const Arguments& args)
   BN_clear_free(p0x);
   BN_clear_free(p0y);
   BN_clear_free(x);
-  //BN_clear_free(p1x);
-  //BN_clear_free(p1y);
   EC_POINT_free(r);
-  //EC_POINT_free(p1);
   EC_POINT_free(p0);
   EC_KEY_free(eckey);
 

From e06e556ca34cd7d134f4af27ba161f357bbf219a Mon Sep 17 00:00:00 2001
From: olalonde 
Date: Thu, 8 May 2014 08:10:07 +0800
Subject: [PATCH 14/15] Eckey: renamed multiply to multiplyUncompressed for
 consistency

---
 lib/Armory.js | 2 --
 lib/Point.js  | 2 +-
 src/eckey.cc  | 4 ++--
 src/eckey.h   | 2 +-
 4 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/lib/Armory.js b/lib/Armory.js
index e78ab33..1b9be60 100644
--- a/lib/Armory.js
+++ b/lib/Armory.js
@@ -4,8 +4,6 @@ var Point = require('./Point'),
   sha256 = require('../util').sha256,
   twoSha256 = require('../util').twoSha256;
 
-// TODO: use native modules instead of browser libraries
-
 /**
  * For now, this class can only supports derivation from public key
  * It doesn't support private key derivation (TODO).
diff --git a/lib/Point.js b/lib/Point.js
index 9231192..3864b80 100644
--- a/lib/Point.js
+++ b/lib/Point.js
@@ -21,7 +21,7 @@ Point.add = function(p1, p2) {
 
 Point.multiply = function(p1, x) {
   var u1 = p1.toUncompressedPubKey();
-  var pubKey = CPPKey.multiply(u1, x);
+  var pubKey = CPPKey.multiplyUncompressed(u1, x);
   return Point.fromUncompressedPubKey(pubKey);
 };
 
diff --git a/src/eckey.cc b/src/eckey.cc
index 3f6926a..02329f9 100644
--- a/src/eckey.cc
+++ b/src/eckey.cc
@@ -124,7 +124,7 @@ void Key::Init(Handle target)
   NODE_SET_METHOD(s_ct->GetFunction(), "generateSync", GenerateSync);
   NODE_SET_METHOD(s_ct->GetFunction(), "fromDER", FromDER);
   NODE_SET_METHOD(s_ct->GetFunction(), "addUncompressed", AddUncompressed);
-  NODE_SET_METHOD(s_ct->GetFunction(), "multiply", Multiply);
+  NODE_SET_METHOD(s_ct->GetFunction(), "multiplyUncompressed", MultiplyUncompressed);
 
   target->Set(String::NewSymbol("Key"),
               s_ct->GetFunction());
@@ -488,7 +488,7 @@ Key::AddUncompressed(const Arguments& args)
 }
 
 Handle
-Key::Multiply(const Arguments& args)
+Key::MultiplyUncompressed(const Arguments& args)
 {
   HandleScope scope;
 
diff --git a/src/eckey.h b/src/eckey.h
index 2507654..df447ba 100644
--- a/src/eckey.h
+++ b/src/eckey.h
@@ -90,7 +90,7 @@ public:
     AddUncompressed(const Arguments& args);
 
   static Handle
-    Multiply(const Arguments& args);
+    MultiplyUncompressed(const Arguments& args);
 
   static Handle
     VerifySignature(const Arguments& args);

From 0c6a0c61d3a1e178eb6ebc116cc1ccbf394eddce Mon Sep 17 00:00:00 2001
From: olalonde 
Date: Thu, 8 May 2014 08:46:09 +0800
Subject: [PATCH 15/15] Armory: removed unused buffertools dependency

---
 lib/Armory.js | 1 -
 1 file changed, 1 deletion(-)

diff --git a/lib/Armory.js b/lib/Armory.js
index 1b9be60..94be0e5 100644
--- a/lib/Armory.js
+++ b/lib/Armory.js
@@ -1,6 +1,5 @@
 var Point = require('./Point'),
   Key = require('./Key'),
-  buffertools = require('buffertools'),
   sha256 = require('../util').sha256,
   twoSha256 = require('../util').twoSha256;