HD/Wallet: use network objects, not strings
This commit is contained in:
parent
2df790e2ab
commit
4952c5f4e7
4 changed files with 66 additions and 44 deletions
|
@ -12,14 +12,11 @@ var networks = require('./networks')
|
||||||
var sec = require('./sec')
|
var sec = require('./sec')
|
||||||
var ecparams = sec("secp256k1")
|
var ecparams = sec("secp256k1")
|
||||||
|
|
||||||
function HDWallet(seed, networkString) {
|
function HDWallet(seed, network) {
|
||||||
if (seed == undefined) return; // FIXME: Boo, should be stricter
|
if (seed == undefined) return; // FIXME: Boo, should be stricter
|
||||||
|
|
||||||
this.network = networkString || 'bitcoin'
|
network = network || networks.bitcoin
|
||||||
|
assert(network.bip32, 'Unknown BIP32 constants for network')
|
||||||
if(!networks.hasOwnProperty(this.network)) {
|
|
||||||
throw new Error("Unknown network: " + this.network)
|
|
||||||
}
|
|
||||||
|
|
||||||
var I = crypto.HmacSHA512(seed, HDWallet.MASTER_SECRET)
|
var I = crypto.HmacSHA512(seed, HDWallet.MASTER_SECRET)
|
||||||
var IL = I.slice(0, 32)
|
var IL = I.slice(0, 32)
|
||||||
|
@ -28,6 +25,7 @@ function HDWallet(seed, networkString) {
|
||||||
// In case IL is 0 or >= n, the master key is invalid (handled by ECKey.fromBuffer)
|
// In case IL is 0 or >= n, the master key is invalid (handled by ECKey.fromBuffer)
|
||||||
var pIL = BigInteger.fromBuffer(IL)
|
var pIL = BigInteger.fromBuffer(IL)
|
||||||
|
|
||||||
|
this.network = network
|
||||||
this.priv = new ECKey(pIL, true)
|
this.priv = new ECKey(pIL, true)
|
||||||
this.pub = this.priv.pub
|
this.pub = this.priv.pub
|
||||||
|
|
||||||
|
@ -40,8 +38,8 @@ HDWallet.MASTER_SECRET = new Buffer('Bitcoin seed')
|
||||||
HDWallet.HIGHEST_BIT = 0x80000000
|
HDWallet.HIGHEST_BIT = 0x80000000
|
||||||
HDWallet.LENGTH = 78
|
HDWallet.LENGTH = 78
|
||||||
|
|
||||||
HDWallet.fromSeedHex = function(hex, networkString) {
|
HDWallet.fromSeedHex = function(hex, network) {
|
||||||
return new HDWallet(new Buffer(hex, 'hex'), networkString)
|
return new HDWallet(new Buffer(hex, 'hex'), network)
|
||||||
}
|
}
|
||||||
|
|
||||||
HDWallet.fromBase58 = function(string) {
|
HDWallet.fromBase58 = function(string) {
|
||||||
|
@ -66,14 +64,14 @@ HDWallet.fromBuffer = function(input) {
|
||||||
var version = input.readUInt32BE(0)
|
var version = input.readUInt32BE(0)
|
||||||
|
|
||||||
var type
|
var type
|
||||||
for(var name in networks) {
|
for (var name in networks) {
|
||||||
var network = networks[name]
|
var network = networks[name]
|
||||||
|
|
||||||
for(var t in network.bip32) {
|
for (var t in network.bip32) {
|
||||||
if (version != network.bip32[t]) continue
|
if (version != network.bip32[t]) continue
|
||||||
|
|
||||||
type = t
|
type = t
|
||||||
hd.network = name
|
hd.network = network
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,7 +125,7 @@ HDWallet.prototype.getAddress = function() {
|
||||||
|
|
||||||
HDWallet.prototype.toBuffer = function(priv) {
|
HDWallet.prototype.toBuffer = function(priv) {
|
||||||
// Version
|
// Version
|
||||||
var version = networks[this.network].bip32[priv ? 'priv' : 'pub']
|
var version = this.network.bip32[priv ? 'priv' : 'pub']
|
||||||
var buffer = new Buffer(HDWallet.LENGTH)
|
var buffer = new Buffer(HDWallet.LENGTH)
|
||||||
|
|
||||||
// 4 bytes: version bytes
|
// 4 bytes: version bytes
|
||||||
|
@ -254,7 +252,7 @@ HDWallet.prototype.derivePrivate = function(index) {
|
||||||
}
|
}
|
||||||
|
|
||||||
HDWallet.prototype.getKeyVersion = function() {
|
HDWallet.prototype.getKeyVersion = function() {
|
||||||
return networks[this.network].pubKeyHash
|
return this.network.pubKeyHash
|
||||||
}
|
}
|
||||||
|
|
||||||
HDWallet.prototype.toString = HDWallet.prototype.toBase58
|
HDWallet.prototype.toString = HDWallet.prototype.toBase58
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
var Address = require('./address')
|
|
||||||
var HDNode = require('./hdwallet.js')
|
|
||||||
var networks = require('./networks')
|
var networks = require('./networks')
|
||||||
var rng = require('secure-random')
|
var rng = require('secure-random')
|
||||||
|
|
||||||
|
var Address = require('./address')
|
||||||
|
var HDNode = require('./hdwallet')
|
||||||
var Transaction = require('./transaction').Transaction
|
var Transaction = require('./transaction').Transaction
|
||||||
|
|
||||||
function Wallet(seed, options) {
|
function Wallet(seed, network) {
|
||||||
if (!(this instanceof Wallet)) { return new Wallet(seed, options); }
|
if (!(this instanceof Wallet)) { return new Wallet(seed, options); }
|
||||||
|
|
||||||
var options = options || {}
|
network = network || networks.bitcoin
|
||||||
var network = options.network || 'bitcoin'
|
|
||||||
|
|
||||||
// Stored in a closure to make accidental serialization less likely
|
// Stored in a closure to make accidental serialization less likely
|
||||||
var masterkey = null
|
var masterkey = null
|
||||||
|
@ -28,7 +28,7 @@ function Wallet(seed, options) {
|
||||||
this.outputs = {}
|
this.outputs = {}
|
||||||
|
|
||||||
// Make a new master key
|
// Make a new master key
|
||||||
this.newMasterKey = function(seed, network) {
|
this.newMasterKey = function(seed) {
|
||||||
seed = seed || new Buffer(rng(32))
|
seed = seed || new Buffer(rng(32))
|
||||||
masterkey = new HDNode(seed, network)
|
masterkey = new HDNode(seed, network)
|
||||||
|
|
||||||
|
@ -43,7 +43,8 @@ function Wallet(seed, options) {
|
||||||
|
|
||||||
me.outputs = {}
|
me.outputs = {}
|
||||||
}
|
}
|
||||||
this.newMasterKey(seed, network)
|
|
||||||
|
this.newMasterKey(seed)
|
||||||
|
|
||||||
this.generateAddress = function() {
|
this.generateAddress = function() {
|
||||||
var key = externalAccount.derive(this.addresses.length)
|
var key = externalAccount.derive(this.addresses.length)
|
||||||
|
@ -143,7 +144,7 @@ function Wallet(seed, options) {
|
||||||
var address
|
var address
|
||||||
|
|
||||||
try {
|
try {
|
||||||
address = Address.fromScriptPubKey(txOut.script, networks[network]).toString()
|
address = Address.fromScriptPubKey(txOut.script, network).toString()
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
if (!(e.message.match(/has no matching Address/))) throw e
|
if (!(e.message.match(/has no matching Address/))) throw e
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
var assert = require('assert')
|
var assert = require('assert')
|
||||||
|
var networks = require('../src/networks')
|
||||||
|
|
||||||
var HDWallet = require('../src/hdwallet')
|
var HDWallet = require('../src/hdwallet')
|
||||||
var fixtures = require('./fixtures/hdwallet.json')
|
var fixtures = require('./fixtures/hdwallet.json')
|
||||||
|
@ -68,6 +69,15 @@ describe('HDWallet', function() {
|
||||||
buffer.writeUInt32BE(0xFFFFFFFF, 9)
|
buffer.writeUInt32BE(0xFFFFFFFF, 9)
|
||||||
assert.throws(function() { HDWallet.fromBuffer(buffer) }, /Invalid index/)
|
assert.throws(function() { HDWallet.fromBuffer(buffer) }, /Invalid index/)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('fails for an invalid network type', function() {
|
||||||
|
var buffer = new HDWallet(seed).toBuffer()
|
||||||
|
buffer.writeUInt32BE(0x00000000, 0)
|
||||||
|
|
||||||
|
assert.throws(function() {
|
||||||
|
HDWallet.fromBuffer(buffer)
|
||||||
|
}, /Could not find version 0/)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -119,24 +129,36 @@ describe('HDWallet', function() {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('network types', function() {
|
describe('network types', function() {
|
||||||
|
var seed
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
seed = new Buffer('foobar')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('ensure that a bitcoin wallet is the default', function() {
|
||||||
|
var wallet = new HDWallet(seed)
|
||||||
|
|
||||||
|
assert.equal(wallet.network, networks.bitcoin)
|
||||||
|
})
|
||||||
|
|
||||||
it('ensures that a bitcoin Wallet generates bitcoin addresses', function() {
|
it('ensures that a bitcoin Wallet generates bitcoin addresses', function() {
|
||||||
var wallet = new HDWallet(new Buffer('foobar'), 'bitcoin')
|
var wallet = new HDWallet(seed)
|
||||||
assert.equal(wallet.getAddress().toString(), '17SnB9hyGwJPoKpLb9eVPHjsujyEuBpMAA')
|
var address = wallet.getAddress().toString()
|
||||||
|
|
||||||
|
assert.equal(address, '17SnB9hyGwJPoKpLb9eVPHjsujyEuBpMAA')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('ensures that a testnet Wallet generates testnet addresses', function() {
|
it('ensures that a testnet Wallet generates testnet addresses', function() {
|
||||||
var wallet = new HDWallet(new Buffer('foobar'), 'testnet')
|
var wallet = new HDWallet(seed, networks.testnet)
|
||||||
assert.equal(wallet.getAddress().toString(), 'mmxjUCnx5xjeaSHxJicsDCxCmjZwq8KTbv')
|
var address = wallet.getAddress().toString()
|
||||||
|
|
||||||
|
assert.equal(address, 'mmxjUCnx5xjeaSHxJicsDCxCmjZwq8KTbv')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('throws an exception when unknown network type is passed in', function() {
|
it('throws an exception when unknown network type is passed in', function() {
|
||||||
assert.throws(function() { new HDWallet(new Buffer('foobar'), 'doge') }, /Unknown network: doge/)
|
assert.throws(function() {
|
||||||
})
|
new HDWallet(seed, {})
|
||||||
|
}, /Unknown BIP32 constants for network/)
|
||||||
it('throws an exception with bad network type using fromBuffer', function() {
|
|
||||||
var buffer = new HDWallet(new Buffer('foobar'), 'bitcoin').toBuffer()
|
|
||||||
buffer.writeUInt32BE(0x00000000, 0)
|
|
||||||
assert.throws(function() { HDWallet.fromBuffer(buffer) }, /Could not find version 0/)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
var assert = require('assert')
|
var assert = require('assert')
|
||||||
var crypto = require('../src/crypto')
|
var crypto = require('../src/crypto')
|
||||||
|
var networks = require('../src/networks')
|
||||||
var sinon = require('sinon')
|
var sinon = require('sinon')
|
||||||
|
|
||||||
var Address = require('../src/address')
|
var Address = require('../src/address')
|
||||||
|
@ -29,7 +30,7 @@ describe('Wallet', function() {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('defaults to Bitcoin network', function() {
|
it('defaults to Bitcoin network', function() {
|
||||||
assert.equal(wallet.getMasterKey().network, 'bitcoin')
|
assert.equal(wallet.getMasterKey().network, networks.bitcoin)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("generates m/0' as the main account", function() {
|
it("generates m/0' as the main account", function() {
|
||||||
|
@ -59,11 +60,11 @@ describe('Wallet', function() {
|
||||||
|
|
||||||
describe('constructor options', function() {
|
describe('constructor options', function() {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
wallet = new Wallet(seed, {network: 'testnet'})
|
wallet = new Wallet(seed, networks.testnet)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('uses the network if specified', function() {
|
it('uses the network if specified', function() {
|
||||||
assert.equal(wallet.getMasterKey().network, 'testnet')
|
assert.equal(wallet.getMasterKey().network, networks.testnet)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -98,7 +99,7 @@ describe('Wallet', function() {
|
||||||
|
|
||||||
describe('generateAddress', function(){
|
describe('generateAddress', function(){
|
||||||
it('generate receiving addresses', function(){
|
it('generate receiving addresses', function(){
|
||||||
var wallet = new Wallet(seed, {network: 'testnet'})
|
var wallet = new Wallet(seed, networks.testnet)
|
||||||
var expectedAddresses = [
|
var expectedAddresses = [
|
||||||
"n1GyUANZand9Kw6hGSV9837cCC9FFUQzQa",
|
"n1GyUANZand9Kw6hGSV9837cCC9FFUQzQa",
|
||||||
"n2fiWrHqD6GM5GiEqkbWAc6aaZQp3ba93X"
|
"n2fiWrHqD6GM5GiEqkbWAc6aaZQp3ba93X"
|
||||||
|
@ -112,7 +113,7 @@ describe('Wallet', function() {
|
||||||
|
|
||||||
describe('generateChangeAddress', function(){
|
describe('generateChangeAddress', function(){
|
||||||
it('generates change addresses', function(){
|
it('generates change addresses', function(){
|
||||||
var wallet = new Wallet(seed, {network: 'testnet'})
|
var wallet = new Wallet(seed, networks.testnet)
|
||||||
var expectedAddresses = ["mnXiDR4MKsFxcKJEZjx4353oXvo55iuptn"]
|
var expectedAddresses = ["mnXiDR4MKsFxcKJEZjx4353oXvo55iuptn"]
|
||||||
|
|
||||||
assert.equal(wallet.generateChangeAddress(), expectedAddresses[0])
|
assert.equal(wallet.generateChangeAddress(), expectedAddresses[0])
|
||||||
|
@ -122,7 +123,7 @@ describe('Wallet', function() {
|
||||||
|
|
||||||
describe('getPrivateKey', function(){
|
describe('getPrivateKey', function(){
|
||||||
it('returns the private key at the given index of external account', function(){
|
it('returns the private key at the given index of external account', function(){
|
||||||
var wallet = new Wallet(seed, {network: 'testnet'})
|
var wallet = new Wallet(seed, networks.testnet)
|
||||||
|
|
||||||
assertEqual(wallet.getPrivateKey(0), wallet.getExternalAccount().derive(0).priv)
|
assertEqual(wallet.getPrivateKey(0), wallet.getExternalAccount().derive(0).priv)
|
||||||
assertEqual(wallet.getPrivateKey(1), wallet.getExternalAccount().derive(1).priv)
|
assertEqual(wallet.getPrivateKey(1), wallet.getExternalAccount().derive(1).priv)
|
||||||
|
@ -131,7 +132,7 @@ describe('Wallet', function() {
|
||||||
|
|
||||||
describe('getInternalPrivateKey', function(){
|
describe('getInternalPrivateKey', function(){
|
||||||
it('returns the private key at the given index of internal account', function(){
|
it('returns the private key at the given index of internal account', function(){
|
||||||
var wallet = new Wallet(seed, {network: 'testnet'})
|
var wallet = new Wallet(seed, networks.testnet)
|
||||||
|
|
||||||
assertEqual(wallet.getInternalPrivateKey(0), wallet.getInternalAccount().derive(0).priv)
|
assertEqual(wallet.getInternalPrivateKey(0), wallet.getInternalAccount().derive(0).priv)
|
||||||
assertEqual(wallet.getInternalPrivateKey(1), wallet.getInternalAccount().derive(1).priv)
|
assertEqual(wallet.getInternalPrivateKey(1), wallet.getInternalAccount().derive(1).priv)
|
||||||
|
@ -140,7 +141,7 @@ describe('Wallet', function() {
|
||||||
|
|
||||||
describe('getPrivateKeyForAddress', function(){
|
describe('getPrivateKeyForAddress', function(){
|
||||||
it('returns the private key for the given address', function(){
|
it('returns the private key for the given address', function(){
|
||||||
var wallet = new Wallet(seed, {network: 'testnet'})
|
var wallet = new Wallet(seed, networks.testnet)
|
||||||
wallet.generateChangeAddress()
|
wallet.generateChangeAddress()
|
||||||
wallet.generateAddress()
|
wallet.generateAddress()
|
||||||
wallet.generateAddress()
|
wallet.generateAddress()
|
||||||
|
@ -156,7 +157,7 @@ describe('Wallet', function() {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('raises an error when address is not found', function(){
|
it('raises an error when address is not found', function(){
|
||||||
var wallet = new Wallet(seed, {network: 'testnet'})
|
var wallet = new Wallet(seed, networks.testnet)
|
||||||
assert.throws(function() {
|
assert.throws(function() {
|
||||||
wallet.getPrivateKeyForAddress("n2fiWrHqD6GM5GiEqkbWAc6aaZQp3ba93X")
|
wallet.getPrivateKeyForAddress("n2fiWrHqD6GM5GiEqkbWAc6aaZQp3ba93X")
|
||||||
}, /Unknown address. Make sure the address is from the keychain and has been generated./)
|
}, /Unknown address. Make sure the address is from the keychain and has been generated./)
|
||||||
|
@ -434,9 +435,9 @@ describe('Wallet', function() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('testnet', function(){
|
describe(networks.testnet, function(){
|
||||||
it('should create transaction', function(){
|
it('should create transaction', function(){
|
||||||
var wallet = new Wallet(seed, {network: 'testnet'})
|
var wallet = new Wallet(seed, networks.testnet)
|
||||||
var address = wallet.generateAddress()
|
var address = wallet.generateAddress()
|
||||||
|
|
||||||
wallet.setUnspentOutputs([{
|
wallet.setUnspentOutputs([{
|
||||||
|
@ -458,7 +459,7 @@ describe('Wallet', function() {
|
||||||
|
|
||||||
describe('changeAddress', function(){
|
describe('changeAddress', function(){
|
||||||
it('should allow custom changeAddress', function(){
|
it('should allow custom changeAddress', function(){
|
||||||
var wallet = new Wallet(seed, {network: 'testnet'})
|
var wallet = new Wallet(seed, networks.testnet)
|
||||||
var address = wallet.generateAddress()
|
var address = wallet.generateAddress()
|
||||||
|
|
||||||
wallet.setUnspentOutputs([{
|
wallet.setUnspentOutputs([{
|
||||||
|
|
Loading…
Add table
Reference in a new issue