From 142b84796815d24c38b134f3dd3f5ef25c3b3ead Mon Sep 17 00:00:00 2001
From: Wei Lu <luwei.here@gmail.com>
Date: Tue, 11 Mar 2014 22:22:03 +0800
Subject: [PATCH 01/10] HD wallet fromMaterHex allows specifying network

[#60]
---
 src/hdwallet.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/hdwallet.js b/src/hdwallet.js
index 309955d..89eb41b 100644
--- a/src/hdwallet.js
+++ b/src/hdwallet.js
@@ -36,9 +36,9 @@ function arrayEqual(a, b) {
 
 HDWallet.getChecksum = base58.getChecksum;
 
-HDWallet.fromMasterHex = function(hex) {
+HDWallet.fromMasterHex = function(hex, network) {
     var bytes = convert.hexToBytes(hex)
-    return new HDWallet(convert.bytesToString(bytes))
+    return new HDWallet(convert.bytesToString(bytes), network)
 }
 
 HDWallet.fromBase58 = function(input) {

From 44012b47b5c817fcebe566c4bfc841f1fb2b49bc Mon Sep 17 00:00:00 2001
From: Wei Lu <luwei.here@gmail.com>
Date: Tue, 11 Mar 2014 22:41:20 +0800
Subject: [PATCH 02/10] change hd wallet constructor to take bytes

original constructor -> fromSeedString, fromMasterHex -> fromSeedHex

[#60]
---
 src/hdwallet.js  | 11 +++++++----
 test/hdwallet.js | 41 ++++++++++++++++++++++++++++++++++-------
 2 files changed, 41 insertions(+), 11 deletions(-)

diff --git a/src/hdwallet.js b/src/hdwallet.js
index 89eb41b..43207a1 100644
--- a/src/hdwallet.js
+++ b/src/hdwallet.js
@@ -13,7 +13,7 @@ var Network = require('./network')
 var HDWallet = module.exports = function(seed, network) {
     if (seed === undefined) return
 
-    var seedWords = convert.bytesToWordArray(convert.stringToBytes(seed))
+    var seedWords = convert.bytesToWordArray(seed)
     var I = convert.wordArrayToBytes(HmacSHA512(seedWords, 'Bitcoin seed'))
     this.chaincode = I.slice(32)
     this.network = network || 'mainnet'
@@ -36,9 +36,12 @@ function arrayEqual(a, b) {
 
 HDWallet.getChecksum = base58.getChecksum;
 
-HDWallet.fromMasterHex = function(hex, network) {
-    var bytes = convert.hexToBytes(hex)
-    return new HDWallet(convert.bytesToString(bytes), network)
+HDWallet.fromSeedHex = function(hex, network) {
+    return new HDWallet(convert.hexToBytes(hex), network)
+}
+
+HDWallet.fromSeedString = function(string, network) {
+    return new HDWallet(convert.stringToBytes(string), network)
 }
 
 HDWallet.fromBase58 = function(input) {
diff --git a/test/hdwallet.js b/test/hdwallet.js
index 85d2ae4..f637535 100644
--- a/test/hdwallet.js
+++ b/test/hdwallet.js
@@ -32,19 +32,46 @@ describe('HDWallet', function() {
         })
     })
 
-    describe('ctor', function() {
-        it('creates from seed', function() {
-            var seed = 'crazy horse battery staple'
-            , hd = new HDWallet(seed)
+    describe('constructor & seed deserialization', function() {
+        var expectedPrivKey, seed;
 
-            assert(hd.priv)
+        beforeEach(function(){
+            expectedPrivKey = 'KwkW62Lzm4a7Eo5nPLezrVjWBGFh2KMfpyf4Swz9NmfsVaLoeXv9'
+            seed = [
+              99, 114, 97, 122, 121, 32, 104, 111, 114, 115, 101, 32, 98,
+              97, 116, 116, 101, 114, 121, 32, 115, 116, 97, 112, 108, 101
+            ]
+        })
+
+        it('creates from binary seed', function() {
+            var hd = new HDWallet(seed)
+
+            assert.equal(hd.priv, expectedPrivKey)
             assert(hd.pub)
         })
+
+        describe('fromSeedHex', function() {
+            it('creates from hex seed', function() {
+                var hd = HDWallet.fromSeedHex(b2h(seed))
+
+                assert.equal(hd.priv, expectedPrivKey)
+                assert(hd.pub)
+            })
+        })
+
+        describe('fromSeedString', function() {
+            it('creates from string seed', function() {
+                var hd = HDWallet.fromSeedString(convert.bytesToString(seed))
+
+                assert.equal(hd.priv, expectedPrivKey)
+                assert(hd.pub)
+            })
+        })
     })
 
     describe('Test vectors', function() {
         it('Test vector 1', function() {
-            var hd = HDWallet.fromMasterHex('000102030405060708090a0b0c0d0e0f')
+            var hd = HDWallet.fromSeedHex('000102030405060708090a0b0c0d0e0f')
 
             // m
             assert.equal(b2h(hd.getIdentifier()), '3442193e1bb70916e914552172cd4e2dbc9df811')
@@ -131,7 +158,7 @@ describe('HDWallet', function() {
         })
 
         it('Test vector 2', function() {
-            var hd = HDWallet.fromMasterHex('fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542')
+            var hd = HDWallet.fromSeedHex('fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542')
 
             // m
             assert.equal(b2h(hd.getIdentifier()), 'bd16bee53961a47d6ad888e29545434a89bdfe95')

From 1212099bfcafd04213f6d70797b7ee6fca1160e1 Mon Sep 17 00:00:00 2001
From: Wei Lu <luwei.here@gmail.com>
Date: Tue, 11 Mar 2014 23:42:01 +0800
Subject: [PATCH 03/10] Wallet address derivation follows bip32/bitcoinj
 keychain structure

Also wallet accepts bytes in constructor

[#60]
---
 src/wallet.js  | 29 ++++++++++++--------------
 test/wallet.js | 56 ++++++++++++++++++++++++++++++++++++--------------
 2 files changed, 54 insertions(+), 31 deletions(-)

diff --git a/src/wallet.js b/src/wallet.js
index d203a54..0af210d 100644
--- a/src/wallet.js
+++ b/src/wallet.js
@@ -6,7 +6,7 @@ var BigInteger = require('./jsbn/jsbn');
 var Transaction = require('./transaction').Transaction;
 var TransactionIn = require('./transaction').TransactionIn;
 var TransactionOut = require('./transaction').TransactionOut;
-var HDWallet = require('./hdwallet.js')
+var HDNode = require('./hdwallet.js')
 var SecureRandom = require('./jsbn/rng');
 var rng = new SecureRandom();
 
@@ -16,13 +16,6 @@ var Wallet = function (seed, options) {
     var options = options || {}
     var network = options.network || 'mainnet'
 
-    // HD first-level child derivation method (i.e. public or private child key derivation)
-    // NB: if not specified, defaults to private child derivation
-    // Also see https://bitcointalk.org/index.php?topic=405179.msg4415254#msg4415254
-    this.derivationMethod = options.derivationMethod || 'private'
-    assert(this.derivationMethod == 'public' || this.derivationMethod == 'private',
-        "derivationMethod must be either 'public' or 'private'");
-
     // Stored in a closure to make accidental serialization less likely
     var keys = [];
     var masterkey = null;
@@ -37,22 +30,26 @@ var Wallet = function (seed, options) {
     // Make a new master key
     this.newMasterKey = function(seed, network) {
         if (!seed) {
-            var seedBytes = new Array(32);
+            var seed= new Array(32);
             rng.nextBytes(seedBytes);
-            seed = convert.bytesToString(seedBytes)
         }
-        masterkey = new HDWallet(seed, network);
+        masterkey = new HDNode(seed, network);
         keys = []
     }
     this.newMasterKey(seed, network)
 
+    // HD first-level child derivation method (i.e. public or private child key derivation)
+    // NB: if not specified, defaults to private child derivation
+    // Also see https://bitcointalk.org/index.php?topic=405179.msg4415254#msg4415254
+    this.accountZero = masterkey.derivePrivate(0)
+    this.externalAccount = this.accountZero.derive(0)
+    this.internalAccount = this.accountZero.derive(1)
+
     // Add a new address
     this.generateAddress = function() {
-        if(this.derivationMethod == 'private')
-            keys.push(masterkey.derivePrivate(keys.length));
-        else
-            keys.push(masterkey.derive(keys.length));
-        this.addresses.push(keys[keys.length-1].getBitcoinAddress().toString())
+        var key = this.externalAccount.derive(keys.length)
+        keys.push(key); // consider removing this and derive on-demand for simplified encrypted keychain
+        this.addresses.push(key.getBitcoinAddress().toString())
         return this.addresses[this.addresses.length - 1]
     }
 
diff --git a/test/wallet.js b/test/wallet.js
index 650ad5c..0f6747c 100644
--- a/test/wallet.js
+++ b/test/wallet.js
@@ -1,10 +1,17 @@
 var Wallet = require('../src/wallet.js')
+var HDNode = require('../src/hdwallet.js')
+var convert = require('../src/convert.js')
 var assert = require('assert')
+var SHA256 = require('crypto-js/sha256')
+var Crypto = require('crypto-js')
 
 describe('Wallet', function() {
-  var seed = 'crazy horse battery staple'
+  var seed;
+  beforeEach(function(){
+    seed = convert.wordArrayToBytes(SHA256("don't use a string seed like this in real life"))
+  })
 
-  describe('default constructor', function() {
+  describe('constructor', function() {
     var wallet;
     beforeEach(function() {
       wallet = new Wallet(seed)
@@ -14,24 +21,43 @@ describe('Wallet', function() {
       assert.equal(wallet.getMasterKey().network, 'mainnet')
     })
 
-    it('defaults to private derivationMethod', function() {
-      assert.equal(wallet.derivationMethod, 'private')
+    it("generates m/0' as the main account", function() {
+      var mainAccount = wallet.accountZero
+      assert.equal(mainAccount.index, 0 + HDNode.HIGHEST_BIT)
+      assert.equal(mainAccount.depth, 1)
+    })
+
+    it("generates m/0'/0 as the external account", function() {
+      var account = wallet.externalAccount
+      assert.equal(account.index, 0)
+      assert.equal(account.depth, 2)
+    })
+
+    it("generates m/0'/1 as the internal account", function() {
+      var account = wallet.internalAccount
+      assert.equal(account.index, 1)
+      assert.equal(account.depth, 2)
+    })
+
+    describe('constructor options', function() {
+      var wallet;
+      beforeEach(function() {
+        wallet = new Wallet(seed, {network: 'testnet'})
+      })
+
+      it('uses the network if specified', function() {
+        assert.equal(wallet.getMasterKey().network, 'testnet')
+      })
     })
   })
 
-  describe('constructor options', function() {
+  describe('generateAddress', function(){
     var wallet;
-    beforeEach(function() {
-      wallet = new Wallet(seed, {network: 'testnet', derivationMethod: 'public'})
-    })
+    beforeEach(function() { wallet = new Wallet(seed, {network: 'testnet'}) })
 
-    it('uses the network if specified', function() {
-      assert.equal(wallet.getMasterKey().network, 'testnet')
-    })
-
-    it('uses the derivationMethod if specified', function() {
-      assert.equal(wallet.derivationMethod, 'public')
+    it('defaults to generating receiving addresses', function(){
+      assert.equal(wallet.generateAddress(), "n1GyUANZand9Kw6hGSV9837cCC9FFUQzQa")
+      assert.equal(wallet.generateAddress(), "n2fiWrHqD6GM5GiEqkbWAc6aaZQp3ba93X")
     })
   })
-
 })

From ab9e782d14dcf3edff682b98bdde2a9a0cbadc7a Mon Sep 17 00:00:00 2001
From: Wei Lu <luwei.here@gmail.com>
Date: Thu, 13 Mar 2014 17:43:35 +0800
Subject: [PATCH 04/10] private a method to generate change addresses

---
 src/wallet.js  | 16 ++++++++++------
 test/wallet.js | 24 +++++++++++++++++++-----
 2 files changed, 29 insertions(+), 11 deletions(-)

diff --git a/src/wallet.js b/src/wallet.js
index 0af210d..995a823 100644
--- a/src/wallet.js
+++ b/src/wallet.js
@@ -23,6 +23,7 @@ var Wallet = function (seed, options) {
 
     // Addresses
     this.addresses = [];
+    this.changeAddresses = [];
 
     // Transaction output data
     this.outputs = {};
@@ -38,21 +39,24 @@ var Wallet = function (seed, options) {
     }
     this.newMasterKey(seed, network)
 
-    // HD first-level child derivation method (i.e. public or private child key derivation)
-    // NB: if not specified, defaults to private child derivation
-    // Also see https://bitcointalk.org/index.php?topic=405179.msg4415254#msg4415254
+    // HD first-level child derivation method should be private
+    // See https://bitcointalk.org/index.php?topic=405179.msg4415254#msg4415254
     this.accountZero = masterkey.derivePrivate(0)
     this.externalAccount = this.accountZero.derive(0)
     this.internalAccount = this.accountZero.derive(1)
 
-    // Add a new address
     this.generateAddress = function() {
-        var key = this.externalAccount.derive(keys.length)
-        keys.push(key); // consider removing this and derive on-demand for simplified encrypted keychain
+        var key = this.externalAccount.derive(this.addresses.length)
         this.addresses.push(key.getBitcoinAddress().toString())
         return this.addresses[this.addresses.length - 1]
     }
 
+    this.generateChangeAddress = function() {
+        var key = this.internalAccount.derive(this.changeAddresses.length)
+        this.changeAddresses.push(key.getBitcoinAddress().toString())
+        return this.changeAddresses[this.changeAddresses.length - 1]
+    }
+
     // Processes a transaction object
     // If "verified" is true, then we trust the transaction as "final"
     this.processTx = function(tx, verified) {
diff --git a/test/wallet.js b/test/wallet.js
index 0f6747c..39a8500 100644
--- a/test/wallet.js
+++ b/test/wallet.js
@@ -52,12 +52,26 @@ describe('Wallet', function() {
   })
 
   describe('generateAddress', function(){
-    var wallet;
-    beforeEach(function() { wallet = new Wallet(seed, {network: 'testnet'}) })
+    it('generate receiving addresses', function(){
+      var wallet = new Wallet(seed, {network: 'testnet'})
+      var expectedAddresses = [
+        "n1GyUANZand9Kw6hGSV9837cCC9FFUQzQa",
+        "n2fiWrHqD6GM5GiEqkbWAc6aaZQp3ba93X"
+      ]
 
-    it('defaults to generating receiving addresses', function(){
-      assert.equal(wallet.generateAddress(), "n1GyUANZand9Kw6hGSV9837cCC9FFUQzQa")
-      assert.equal(wallet.generateAddress(), "n2fiWrHqD6GM5GiEqkbWAc6aaZQp3ba93X")
+      assert.equal(wallet.generateAddress(), expectedAddresses[0])
+      assert.equal(wallet.generateAddress(), expectedAddresses[1])
+      assert.deepEqual(wallet.addresses, expectedAddresses)
+    })
+  })
+
+  describe('generateChangeAddress', function(){
+    it('generates change addresses', function(){
+      var wallet = new Wallet(seed, {network: 'testnet'})
+      var expectedAddresses = ["mnXiDR4MKsFxcKJEZjx4353oXvo55iuptn"]
+
+      assert.equal(wallet.generateChangeAddress(), expectedAddresses[0])
+      assert.deepEqual(wallet.changeAddresses, expectedAddresses)
     })
   })
 })

From 9200479159bc43aa88fe24cd65284294c20172b4 Mon Sep 17 00:00:00 2001
From: Wei Lu <luwei.here@gmail.com>
Date: Thu, 13 Mar 2014 19:05:55 +0800
Subject: [PATCH 05/10] getPrivateKey derives key on the fly

also added getPrivateKeyFromAddress
---
 src/wallet.js  | 20 +++++++++++++++-----
 test/wallet.js | 43 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 58 insertions(+), 5 deletions(-)

diff --git a/src/wallet.js b/src/wallet.js
index 995a823..a7fccfc 100644
--- a/src/wallet.js
+++ b/src/wallet.js
@@ -182,13 +182,23 @@ var Wallet = function (seed, options) {
     this.getMasterKey = function() { return masterkey }
 
     this.getPrivateKey = function(index) {
-        if (typeof index == "string")
-            return keys.filter(function(i,k){ return addresses[i] == index })[0]
-        else
-            return keys[index]
+        return this.externalAccount.derive(index)
     }
 
-    this.getPrivateKeys = function() { return keys }
+    this.getInternalPrivateKey = function(index) {
+        return this.internalAccount.derive(index)
+    }
+
+    this.getPrivateKeyForAddress = function(address) {
+      var index;
+      if((index = this.addresses.indexOf(address)) > -1) {
+        return this.getPrivateKey(index)
+      } else if((index = this.changeAddresses.indexOf(address)) > -1) {
+        return this.getInternalPrivateKey(index)
+      } else {
+        throw new Error('Unknown address. Make sure the address is from the keychain and has been generated.')
+      }
+    }
 };
 
 module.exports = Wallet;
diff --git a/test/wallet.js b/test/wallet.js
index 39a8500..3b1a2ca 100644
--- a/test/wallet.js
+++ b/test/wallet.js
@@ -74,4 +74,47 @@ describe('Wallet', function() {
       assert.deepEqual(wallet.changeAddresses, expectedAddresses)
     })
   })
+
+  describe('getPrivateKey', function(){
+    it('returns the private key at the given index of external account', function(){
+      var wallet = new Wallet(seed, {network: 'testnet'})
+
+      assertPrivateKeyEqual(wallet.getPrivateKey(0), wallet.externalAccount.derive(0))
+      assertPrivateKeyEqual(wallet.getPrivateKey(1), wallet.externalAccount.derive(1))
+    })
+  })
+
+  describe('getInternalPrivateKey', function(){
+    it('returns the private key at the given index of internal account', function(){
+      var wallet = new Wallet(seed, {network: 'testnet'})
+
+      assertPrivateKeyEqual(wallet.getInternalPrivateKey(0), wallet.internalAccount.derive(0))
+      assertPrivateKeyEqual(wallet.getInternalPrivateKey(1), wallet.internalAccount.derive(1))
+    })
+  })
+
+  describe('getPrivateKeyForAddress', function(){
+    it('returns the private key for the given address', function(){
+      var wallet = new Wallet(seed, {network: 'testnet'})
+      wallet.generateChangeAddress()
+      wallet.generateAddress()
+      wallet.generateAddress()
+
+      assertPrivateKeyEqual(wallet.getPrivateKeyForAddress("n2fiWrHqD6GM5GiEqkbWAc6aaZQp3ba93X"),
+                   wallet.externalAccount.derive(1))
+      assertPrivateKeyEqual(wallet.getPrivateKeyForAddress("mnXiDR4MKsFxcKJEZjx4353oXvo55iuptn"),
+                   wallet.internalAccount.derive(0))
+    })
+
+    it('raises an error when address is not found', function(){
+      var wallet = new Wallet(seed, {network: 'testnet'})
+      assert.throws(function() {
+        wallet.getPrivateKeyForAddress("n2fiWrHqD6GM5GiEqkbWAc6aaZQp3ba93X")
+      }, Error, 'Unknown address. Make sure the address is from the keychain and has been generated.')
+    })
+  })
+
+  function assertPrivateKeyEqual(key1, key2){
+    assert.equal(key1.toString(), key2.toString())
+  }
 })

From 207163704b5e6b434e224e2268679db598d37313 Mon Sep 17 00:00:00 2001
From: Wei Lu <luwei.here@gmail.com>
Date: Thu, 13 Mar 2014 19:12:52 +0800
Subject: [PATCH 06/10] return private key rather than HD wallet node

---
 src/wallet.js  |  4 ++--
 test/wallet.js | 12 ++++++------
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/src/wallet.js b/src/wallet.js
index a7fccfc..212d8a8 100644
--- a/src/wallet.js
+++ b/src/wallet.js
@@ -182,11 +182,11 @@ var Wallet = function (seed, options) {
     this.getMasterKey = function() { return masterkey }
 
     this.getPrivateKey = function(index) {
-        return this.externalAccount.derive(index)
+        return this.externalAccount.derive(index).priv
     }
 
     this.getInternalPrivateKey = function(index) {
-        return this.internalAccount.derive(index)
+        return this.internalAccount.derive(index).priv
     }
 
     this.getPrivateKeyForAddress = function(address) {
diff --git a/test/wallet.js b/test/wallet.js
index 3b1a2ca..fe18b31 100644
--- a/test/wallet.js
+++ b/test/wallet.js
@@ -79,8 +79,8 @@ describe('Wallet', function() {
     it('returns the private key at the given index of external account', function(){
       var wallet = new Wallet(seed, {network: 'testnet'})
 
-      assertPrivateKeyEqual(wallet.getPrivateKey(0), wallet.externalAccount.derive(0))
-      assertPrivateKeyEqual(wallet.getPrivateKey(1), wallet.externalAccount.derive(1))
+      assertPrivateKeyEqual(wallet.getPrivateKey(0), wallet.externalAccount.derive(0).priv)
+      assertPrivateKeyEqual(wallet.getPrivateKey(1), wallet.externalAccount.derive(1).priv)
     })
   })
 
@@ -88,8 +88,8 @@ describe('Wallet', function() {
     it('returns the private key at the given index of internal account', function(){
       var wallet = new Wallet(seed, {network: 'testnet'})
 
-      assertPrivateKeyEqual(wallet.getInternalPrivateKey(0), wallet.internalAccount.derive(0))
-      assertPrivateKeyEqual(wallet.getInternalPrivateKey(1), wallet.internalAccount.derive(1))
+      assertPrivateKeyEqual(wallet.getInternalPrivateKey(0), wallet.internalAccount.derive(0).priv)
+      assertPrivateKeyEqual(wallet.getInternalPrivateKey(1), wallet.internalAccount.derive(1).priv)
     })
   })
 
@@ -101,9 +101,9 @@ describe('Wallet', function() {
       wallet.generateAddress()
 
       assertPrivateKeyEqual(wallet.getPrivateKeyForAddress("n2fiWrHqD6GM5GiEqkbWAc6aaZQp3ba93X"),
-                   wallet.externalAccount.derive(1))
+                   wallet.externalAccount.derive(1).priv)
       assertPrivateKeyEqual(wallet.getPrivateKeyForAddress("mnXiDR4MKsFxcKJEZjx4353oXvo55iuptn"),
-                   wallet.internalAccount.derive(0))
+                   wallet.internalAccount.derive(0).priv)
     })
 
     it('raises an error when address is not found', function(){

From d4f3398d91485fa185c7134dbbd57eeddb521533 Mon Sep 17 00:00:00 2001
From: Wei Lu <luwei.here@gmail.com>
Date: Fri, 14 Mar 2014 10:12:44 +0800
Subject: [PATCH 07/10] remove keys array and usage

---
 src/wallet.js | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/src/wallet.js b/src/wallet.js
index 212d8a8..908be55 100644
--- a/src/wallet.js
+++ b/src/wallet.js
@@ -17,7 +17,6 @@ var Wallet = function (seed, options) {
     var network = options.network || 'mainnet'
 
     // Stored in a closure to make accidental serialization less likely
-    var keys = [];
     var masterkey = null;
     var me = this;
 
@@ -35,7 +34,6 @@ var Wallet = function (seed, options) {
             rng.nextBytes(seedBytes);
         }
         masterkey = new HDNode(seed, network);
-        keys = []
     }
     this.newMasterKey(seed, network)
 
@@ -168,12 +166,8 @@ var Wallet = function (seed, options) {
         tx.ins.map(function(inp,i) {
             var inp = inp.outpoint.hash+':'+inp.outpoint.index;
             if (me.outputs[inp]) {
-                var address = me.outputs[inp].address,
-                    ind = me.addresses.indexOf(address);
-                if (ind >= 0) {
-                    var key = keys[ind]
-                    tx.sign(ind,key)
-                }
+                var address = me.outputs[inp].address
+                tx.sign(i, me.getPrivateKeyForAddress(address))
             }
         })
         return tx;

From b2f010428f6e7e9eb92cd51c8cc6677f3f9b4dd0 Mon Sep 17 00:00:00 2001
From: Wei Lu <luwei.here@gmail.com>
Date: Fri, 14 Mar 2014 10:31:26 +0800
Subject: [PATCH 08/10] store accounts in closure instead of on object

---
 src/wallet.js  | 20 +++++++++++++-------
 test/wallet.js | 18 +++++++++---------
 2 files changed, 22 insertions(+), 16 deletions(-)

diff --git a/src/wallet.js b/src/wallet.js
index 908be55..640576d 100644
--- a/src/wallet.js
+++ b/src/wallet.js
@@ -19,6 +19,9 @@ var Wallet = function (seed, options) {
     // Stored in a closure to make accidental serialization less likely
     var masterkey = null;
     var me = this;
+    var accountZero = null;
+    var internalAccount = null;
+    var externalAccount = null;
 
     // Addresses
     this.addresses = [];
@@ -39,18 +42,18 @@ var Wallet = function (seed, options) {
 
     // HD first-level child derivation method should be private
     // See https://bitcointalk.org/index.php?topic=405179.msg4415254#msg4415254
-    this.accountZero = masterkey.derivePrivate(0)
-    this.externalAccount = this.accountZero.derive(0)
-    this.internalAccount = this.accountZero.derive(1)
+    accountZero = masterkey.derivePrivate(0)
+    externalAccount = accountZero.derive(0)
+    internalAccount = accountZero.derive(1)
 
     this.generateAddress = function() {
-        var key = this.externalAccount.derive(this.addresses.length)
+        var key = externalAccount.derive(this.addresses.length)
         this.addresses.push(key.getBitcoinAddress().toString())
         return this.addresses[this.addresses.length - 1]
     }
 
     this.generateChangeAddress = function() {
-        var key = this.internalAccount.derive(this.changeAddresses.length)
+        var key = internalAccount.derive(this.changeAddresses.length)
         this.changeAddresses.push(key.getBitcoinAddress().toString())
         return this.changeAddresses[this.changeAddresses.length - 1]
     }
@@ -174,13 +177,16 @@ var Wallet = function (seed, options) {
     }
 
     this.getMasterKey = function() { return masterkey }
+    this.getAccountZero = function() { return accountZero }
+    this.getInternalAccount = function() { return internalAccount }
+    this.getExternalAccount = function() { return externalAccount }
 
     this.getPrivateKey = function(index) {
-        return this.externalAccount.derive(index).priv
+        return externalAccount.derive(index).priv
     }
 
     this.getInternalPrivateKey = function(index) {
-        return this.internalAccount.derive(index).priv
+        return internalAccount.derive(index).priv
     }
 
     this.getPrivateKeyForAddress = function(address) {
diff --git a/test/wallet.js b/test/wallet.js
index fe18b31..176be26 100644
--- a/test/wallet.js
+++ b/test/wallet.js
@@ -22,19 +22,19 @@ describe('Wallet', function() {
     })
 
     it("generates m/0' as the main account", function() {
-      var mainAccount = wallet.accountZero
+      var mainAccount = wallet.getAccountZero()
       assert.equal(mainAccount.index, 0 + HDNode.HIGHEST_BIT)
       assert.equal(mainAccount.depth, 1)
     })
 
     it("generates m/0'/0 as the external account", function() {
-      var account = wallet.externalAccount
+      var account = wallet.getExternalAccount()
       assert.equal(account.index, 0)
       assert.equal(account.depth, 2)
     })
 
     it("generates m/0'/1 as the internal account", function() {
-      var account = wallet.internalAccount
+      var account = wallet.getInternalAccount()
       assert.equal(account.index, 1)
       assert.equal(account.depth, 2)
     })
@@ -79,8 +79,8 @@ describe('Wallet', function() {
     it('returns the private key at the given index of external account', function(){
       var wallet = new Wallet(seed, {network: 'testnet'})
 
-      assertPrivateKeyEqual(wallet.getPrivateKey(0), wallet.externalAccount.derive(0).priv)
-      assertPrivateKeyEqual(wallet.getPrivateKey(1), wallet.externalAccount.derive(1).priv)
+      assertPrivateKeyEqual(wallet.getPrivateKey(0), wallet.getExternalAccount().derive(0).priv)
+      assertPrivateKeyEqual(wallet.getPrivateKey(1), wallet.getExternalAccount().derive(1).priv)
     })
   })
 
@@ -88,8 +88,8 @@ describe('Wallet', function() {
     it('returns the private key at the given index of internal account', function(){
       var wallet = new Wallet(seed, {network: 'testnet'})
 
-      assertPrivateKeyEqual(wallet.getInternalPrivateKey(0), wallet.internalAccount.derive(0).priv)
-      assertPrivateKeyEqual(wallet.getInternalPrivateKey(1), wallet.internalAccount.derive(1).priv)
+      assertPrivateKeyEqual(wallet.getInternalPrivateKey(0), wallet.getInternalAccount().derive(0).priv)
+      assertPrivateKeyEqual(wallet.getInternalPrivateKey(1), wallet.getInternalAccount().derive(1).priv)
     })
   })
 
@@ -101,9 +101,9 @@ describe('Wallet', function() {
       wallet.generateAddress()
 
       assertPrivateKeyEqual(wallet.getPrivateKeyForAddress("n2fiWrHqD6GM5GiEqkbWAc6aaZQp3ba93X"),
-                   wallet.externalAccount.derive(1).priv)
+                   wallet.getExternalAccount().derive(1).priv)
       assertPrivateKeyEqual(wallet.getPrivateKeyForAddress("mnXiDR4MKsFxcKJEZjx4353oXvo55iuptn"),
-                   wallet.internalAccount.derive(0).priv)
+                   wallet.getInternalAccount().derive(0).priv)
     })
 
     it('raises an error when address is not found', function(){

From a7a7999e3f10b4b4b1fc3fa2b30256d534bab458 Mon Sep 17 00:00:00 2001
From: Wei Lu <luwei.here@gmail.com>
Date: Sat, 15 Mar 2014 10:18:59 +0800
Subject: [PATCH 09/10] fix Wallet default constructor

---
 src/wallet.js  | 2 +-
 test/wallet.js | 7 +++++++
 2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/src/wallet.js b/src/wallet.js
index 640576d..6e579ec 100644
--- a/src/wallet.js
+++ b/src/wallet.js
@@ -34,7 +34,7 @@ var Wallet = function (seed, options) {
     this.newMasterKey = function(seed, network) {
         if (!seed) {
             var seed= new Array(32);
-            rng.nextBytes(seedBytes);
+            rng.nextBytes(seed);
         }
         masterkey = new HDNode(seed, network);
     }
diff --git a/test/wallet.js b/test/wallet.js
index 176be26..b1a1579 100644
--- a/test/wallet.js
+++ b/test/wallet.js
@@ -39,6 +39,13 @@ describe('Wallet', function() {
       assert.equal(account.depth, 2)
     })
 
+    describe('when seed is not specified', function(){
+      it('generates a seed', function(){
+        var wallet = new Wallet()
+        assert.ok(wallet.getMasterKey())
+      })
+    })
+
     describe('constructor options', function() {
       var wallet;
       beforeEach(function() {

From 31cfb111786ea7765f7526539b82dd0f37699b7a Mon Sep 17 00:00:00 2001
From: Wei Lu <luwei.here@gmail.com>
Date: Sun, 16 Mar 2014 13:31:46 +0800
Subject: [PATCH 10/10] new master key resets accounts and addresses

---
 src/wallet.js  | 16 +++++++++++-----
 test/wallet.js | 48 ++++++++++++++++++++++++++++++++++++++++--------
 2 files changed, 51 insertions(+), 13 deletions(-)

diff --git a/src/wallet.js b/src/wallet.js
index 6e579ec..698f9a3 100644
--- a/src/wallet.js
+++ b/src/wallet.js
@@ -37,14 +37,20 @@ var Wallet = function (seed, options) {
             rng.nextBytes(seed);
         }
         masterkey = new HDNode(seed, network);
+
+        // HD first-level child derivation method should be private
+        // See https://bitcointalk.org/index.php?topic=405179.msg4415254#msg4415254
+        accountZero = masterkey.derivePrivate(0)
+        externalAccount = accountZero.derive(0)
+        internalAccount = accountZero.derive(1)
+
+        me.addresses = [];
+        me.changeAddresses = [];
+
+        me.outputs = {};
     }
     this.newMasterKey(seed, network)
 
-    // HD first-level child derivation method should be private
-    // See https://bitcointalk.org/index.php?topic=405179.msg4415254#msg4415254
-    accountZero = masterkey.derivePrivate(0)
-    externalAccount = accountZero.derive(0)
-    internalAccount = accountZero.derive(1)
 
     this.generateAddress = function() {
         var key = externalAccount.derive(this.addresses.length)
diff --git a/test/wallet.js b/test/wallet.js
index b1a1579..b72e2d7 100644
--- a/test/wallet.js
+++ b/test/wallet.js
@@ -58,6 +58,34 @@ describe('Wallet', function() {
     })
   })
 
+  describe('newMasterKey', function(){
+    it('resets accounts', function(){
+      var wallet = new Wallet()
+      var oldAccountZero = wallet.getAccountZero()
+      var oldExternalAccount = wallet.getExternalAccount()
+      var oldInternalAccount = wallet.getInternalAccount()
+
+      wallet.newMasterKey(seed)
+      assertNotEqual(wallet.getAccountZero(), oldAccountZero)
+      assertNotEqual(wallet.getExternalAccount(), oldExternalAccount)
+      assertNotEqual(wallet.getInternalAccount(), oldInternalAccount)
+    })
+
+    it('resets addresses', function(){
+      var wallet = new Wallet()
+      wallet.generateAddress()
+      wallet.generateChangeAddress()
+      var oldAddresses = wallet.addresses
+      var oldChangeAddresses = wallet.changeAddresses
+      assert.notDeepEqual(oldAddresses, [])
+      assert.notDeepEqual(oldChangeAddresses, [])
+
+      wallet.newMasterKey(seed)
+      assert.deepEqual(wallet.addresses, [])
+      assert.deepEqual(wallet.changeAddresses, [])
+    })
+  })
+
   describe('generateAddress', function(){
     it('generate receiving addresses', function(){
       var wallet = new Wallet(seed, {network: 'testnet'})
@@ -86,8 +114,8 @@ describe('Wallet', function() {
     it('returns the private key at the given index of external account', function(){
       var wallet = new Wallet(seed, {network: 'testnet'})
 
-      assertPrivateKeyEqual(wallet.getPrivateKey(0), wallet.getExternalAccount().derive(0).priv)
-      assertPrivateKeyEqual(wallet.getPrivateKey(1), wallet.getExternalAccount().derive(1).priv)
+      assertEqual(wallet.getPrivateKey(0), wallet.getExternalAccount().derive(0).priv)
+      assertEqual(wallet.getPrivateKey(1), wallet.getExternalAccount().derive(1).priv)
     })
   })
 
@@ -95,8 +123,8 @@ describe('Wallet', function() {
     it('returns the private key at the given index of internal account', function(){
       var wallet = new Wallet(seed, {network: 'testnet'})
 
-      assertPrivateKeyEqual(wallet.getInternalPrivateKey(0), wallet.getInternalAccount().derive(0).priv)
-      assertPrivateKeyEqual(wallet.getInternalPrivateKey(1), wallet.getInternalAccount().derive(1).priv)
+      assertEqual(wallet.getInternalPrivateKey(0), wallet.getInternalAccount().derive(0).priv)
+      assertEqual(wallet.getInternalPrivateKey(1), wallet.getInternalAccount().derive(1).priv)
     })
   })
 
@@ -107,9 +135,9 @@ describe('Wallet', function() {
       wallet.generateAddress()
       wallet.generateAddress()
 
-      assertPrivateKeyEqual(wallet.getPrivateKeyForAddress("n2fiWrHqD6GM5GiEqkbWAc6aaZQp3ba93X"),
+      assertEqual(wallet.getPrivateKeyForAddress("n2fiWrHqD6GM5GiEqkbWAc6aaZQp3ba93X"),
                    wallet.getExternalAccount().derive(1).priv)
-      assertPrivateKeyEqual(wallet.getPrivateKeyForAddress("mnXiDR4MKsFxcKJEZjx4353oXvo55iuptn"),
+      assertEqual(wallet.getPrivateKeyForAddress("mnXiDR4MKsFxcKJEZjx4353oXvo55iuptn"),
                    wallet.getInternalAccount().derive(0).priv)
     })
 
@@ -121,7 +149,11 @@ describe('Wallet', function() {
     })
   })
 
-  function assertPrivateKeyEqual(key1, key2){
-    assert.equal(key1.toString(), key2.toString())
+  function assertEqual(obj1, obj2){
+    assert.equal(obj1.toString(), obj2.toString())
+  }
+
+  function assertNotEqual(obj1, obj2){
+    assert.notEqual(obj1.toString(), obj2.toString())
   }
 })