diff --git a/src/hdwallet.js b/src/hdwallet.js
index 309955d..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) {
-    var bytes = convert.hexToBytes(hex)
-    return new HDWallet(convert.bytesToString(bytes))
+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/src/wallet.js b/src/wallet.js
index d203a54..698f9a3 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,20 +16,16 @@ 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;
     var me = this;
+    var accountZero = null;
+    var internalAccount = null;
+    var externalAccount = null;
 
     // Addresses
     this.addresses = [];
+    this.changeAddresses = [];
 
     // Transaction output data
     this.outputs = {};
@@ -37,25 +33,37 @@ var Wallet = function (seed, options) {
     // Make a new master key
     this.newMasterKey = function(seed, network) {
         if (!seed) {
-            var seedBytes = new Array(32);
-            rng.nextBytes(seedBytes);
-            seed = convert.bytesToString(seedBytes)
+            var seed= new Array(32);
+            rng.nextBytes(seed);
         }
-        masterkey = new HDWallet(seed, network);
-        keys = []
+        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)
 
-    // 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 = externalAccount.derive(this.addresses.length)
+        this.addresses.push(key.getBitcoinAddress().toString())
         return this.addresses[this.addresses.length - 1]
     }
 
+    this.generateChangeAddress = function() {
+        var key = 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) {
@@ -167,27 +175,36 @@ 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;
     }
 
     this.getMasterKey = function() { return masterkey }
+    this.getAccountZero = function() { return accountZero }
+    this.getInternalAccount = function() { return internalAccount }
+    this.getExternalAccount = function() { return externalAccount }
 
     this.getPrivateKey = function(index) {
-        if (typeof index == "string")
-            return keys.filter(function(i,k){ return addresses[i] == index })[0]
-        else
-            return keys[index]
+        return externalAccount.derive(index).priv
     }
 
-    this.getPrivateKeys = function() { return keys }
+    this.getInternalPrivateKey = function(index) {
+        return internalAccount.derive(index).priv
+    }
+
+    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/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')
diff --git a/test/wallet.js b/test/wallet.js
index 650ad5c..b72e2d7 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,139 @@ 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.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.getExternalAccount()
+      assert.equal(account.index, 0)
+      assert.equal(account.depth, 2)
+    })
+
+    it("generates m/0'/1 as the internal account", function() {
+      var account = wallet.getInternalAccount()
+      assert.equal(account.index, 1)
+      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() {
+        wallet = new Wallet(seed, {network: 'testnet'})
+      })
+
+      it('uses the network if specified', function() {
+        assert.equal(wallet.getMasterKey().network, 'testnet')
+      })
     })
   })
 
-  describe('constructor options', function() {
-    var wallet;
-    beforeEach(function() {
-      wallet = new Wallet(seed, {network: 'testnet', derivationMethod: 'public'})
+  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('uses the network if specified', function() {
-      assert.equal(wallet.getMasterKey().network, 'testnet')
-    })
+    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, [])
 
-    it('uses the derivationMethod if specified', function() {
-      assert.equal(wallet.derivationMethod, 'public')
+      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'})
+      var expectedAddresses = [
+        "n1GyUANZand9Kw6hGSV9837cCC9FFUQzQa",
+        "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)
+    })
+  })
+
+  describe('getPrivateKey', function(){
+    it('returns the private key at the given index of external account', function(){
+      var wallet = new Wallet(seed, {network: 'testnet'})
+
+      assertEqual(wallet.getPrivateKey(0), wallet.getExternalAccount().derive(0).priv)
+      assertEqual(wallet.getPrivateKey(1), wallet.getExternalAccount().derive(1).priv)
+    })
+  })
+
+  describe('getInternalPrivateKey', function(){
+    it('returns the private key at the given index of internal account', function(){
+      var wallet = new Wallet(seed, {network: 'testnet'})
+
+      assertEqual(wallet.getInternalPrivateKey(0), wallet.getInternalAccount().derive(0).priv)
+      assertEqual(wallet.getInternalPrivateKey(1), wallet.getInternalAccount().derive(1).priv)
+    })
+  })
+
+  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()
+
+      assertEqual(wallet.getPrivateKeyForAddress("n2fiWrHqD6GM5GiEqkbWAc6aaZQp3ba93X"),
+                   wallet.getExternalAccount().derive(1).priv)
+      assertEqual(wallet.getPrivateKeyForAddress("mnXiDR4MKsFxcKJEZjx4353oXvo55iuptn"),
+                   wallet.getInternalAccount().derive(0).priv)
+    })
+
+    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 assertEqual(obj1, obj2){
+    assert.equal(obj1.toString(), obj2.toString())
+  }
+
+  function assertNotEqual(obj1, obj2){
+    assert.notEqual(obj1.toString(), obj2.toString())
+  }
 })