avoid using pbkdf2 when loading wallet. improve spvnode.

This commit is contained in:
Christopher Jeffrey 2016-03-10 03:23:21 -08:00
parent c844cf1c4b
commit 28e105c16e
4 changed files with 131 additions and 95 deletions

View File

@ -5,9 +5,10 @@ var utils = bcoin.utils;
var assert = utils.assert;
var node = bcoin.fullnode({
debug: true
debug: true,
passphrase: 'node'
});
node.on('error', function(err) {
utils.print(err.message);
utils.debug(err.message);
});

View File

@ -4,7 +4,6 @@
* https://github.com/indutny/bcoin
*/
var EventEmitter = require('events').EventEmitter;
var bcoin = require('../bcoin');
var bn = require('bn.js');
var constants = bcoin.protocol.constants;
@ -21,8 +20,6 @@ function Fullnode(options) {
if (!(this instanceof Fullnode))
return new Fullnode(options);
EventEmitter.call(this);
if (!options)
options = {};
@ -40,6 +37,7 @@ utils.inherits(Fullnode, bcoin.node);
Fullnode.prototype._init = function _init() {
var self = this;
var pending = 3;
var options;
this.loading = true;
@ -147,8 +145,13 @@ Fullnode.prototype._init = function _init() {
}
}
options = {
id: 'primary',
passphrase: this.options.passphrase
};
// Create or load the primary wallet.
this.createWallet({ id: 'primary', passphrase: 'node' }, function(err, wallet) {
this.createWallet(options, function(err, wallet) {
if (err)
throw err;
@ -193,6 +196,10 @@ Fullnode.prototype.createWallet = function createWallet(options, callback) {
});
};
Fullnode.prototype.getWallet = function getWallet(id, passphrase, callback) {
return this.walletdb.get(id, passphrase, callback);
};
Fullnode.prototype.scanWallet = function scanWallet(wallet, callback) {
wallet.scan(this.getTXByAddress.bind(this), callback);
};

View File

@ -70,20 +70,31 @@ function HDSeed(options) {
if (!(this instanceof HDSeed))
return new HDSeed(options);
options = options || {};
if (!options)
options = {};
this.bits = options.bits || 128;
this.entropy = options.entropy || bcoin.ec.random(this.bits / 8);
this.mnemonic = options.mnemonic || HDSeed._mnemonic(this.entropy);
this.seed = this.createSeed(options.passphrase);
this.entropy = options.entropy;
this.mnemonic = options.mnemonic;
this.passphrase = options.passphrase || '';
}
HDSeed.prototype.createSeed = function createSeed(passphrase) {
this.passphrase = passphrase || '';
return pbkdf2(this.mnemonic, 'mnemonic' + this.passphrase, 2048, 64);
if (this.seed)
return this.seed;
if (!this.entropy)
this.entropy = bcoin.ec.random(this.bits / 8);
if (!this.mnemonic)
this.mnemonic = this.createMnemonic(this.entropy);
this.seed = pbkdf2(this.mnemonic, 'mnemonic' + this.passphrase, 2048, 64);
return this.seed;
};
HDSeed._mnemonic = function _mnemonic(entropy) {
HDSeed.prototype.createMnemonic = function createMnemonic(entropy) {
var bin = '';
var mnemonic = [];
var i, wi;
@ -143,12 +154,12 @@ function HDPrivateKey(options) {
return new HDPublicKey(options);
this.network = options.network || network.type;
this.seed = options.seed;
if (options.seed) {
this.seed = options.seed;
data = this._seed(options.seed);
} else if (options.xkey) {
if (options.xkey) {
data = this._unbuild(options.xkey);
} else if (options.seed) {
data = this._seed(options.seed);
} else {
data = options.data;
}
@ -549,7 +560,7 @@ HDPrivateKey.prototype._seed = function _seed(seed) {
var hash;
if (seed instanceof HDSeed)
seed = seed.seed;
seed = seed.createSeed();
if (utils.isHex(seed))
seed = new Buffer(seed, 'hex');
@ -631,10 +642,13 @@ HDPrivateKey.prototype._unbuild = function _unbuild(xkey) {
if (!utils.isEqual(data.checksum, hash))
throw new Error('checksum mismatch');
if (data.version === network.main.prefixes.xprivkey)
this.network = 'main';
else
this.network = 'testnet';
network.types.some(function(type) {
if (utils.readU32BE(data.version) === network[type].prefixes.xprivkey) {
this.network = type;
return true;
}
return false;
}, this);
this.xprivkey = xkey;
@ -703,7 +717,7 @@ HDPrivateKey.prototype.derive = function derive(index, hardened) {
var off = 0;
if (typeof index === 'string')
return this.deriveString(index);
return this.derivePath(index);
cached = cache.get(this.xprivkey, index);
@ -808,7 +822,7 @@ HDPrivateKey.isValidPath = function isValidPath(path, hardened) {
return false;
};
HDPrivateKey.prototype.deriveString = function deriveString(path) {
HDPrivateKey.prototype.derivePath = function derivePath(path) {
var indexes, child;
if (!HDPrivateKey.isValidPath(path))
@ -825,10 +839,11 @@ HDPrivateKey.prototype.toJSON = function toJSON(passphrase) {
var json = {
v: 1,
name: 'hdkey',
encrypted: passphrase ? true : false
encrypted: false
};
if (this instanceof HDPrivateKey) {
json.encrypted = passphrase ? true : false;
if (this.seed) {
json.mnemonic = passphrase
? utils.encrypt(this.seed.mnemonic, passphrase)
@ -836,7 +851,6 @@ HDPrivateKey.prototype.toJSON = function toJSON(passphrase) {
json.passphrase = passphrase
? utils.encrypt(this.seed.passphrase, passphrase)
: this.seed.passphrase;
return json;
}
json.xprivkey = passphrase
? utils.encrypt(this.xprivkey, passphrase)
@ -850,6 +864,8 @@ HDPrivateKey.prototype.toJSON = function toJSON(passphrase) {
};
HDPrivateKey._fromJSON = function _fromJSON(json, passphrase) {
var data = {};
assert.equal(json.v, 1);
assert.equal(json.name, 'hdkey');
@ -857,24 +873,23 @@ HDPrivateKey._fromJSON = function _fromJSON(json, passphrase) {
throw new Error('Cannot decrypt address');
if (json.mnemonic) {
return {
seed: {
mnemonic: json.encrypted
? utils.decrypt(json.mnemonic, passphrase)
: json.mnemonic,
passphrase: json.encrypted
? utils.decrypt(json.passphrase, passphrase)
: json.passphrase
}
data.seed = {
mnemonic: json.encrypted
? utils.decrypt(json.mnemonic, passphrase)
: json.mnemonic,
passphrase: json.encrypted
? utils.decrypt(json.passphrase, passphrase)
: json.passphrase
};
if (!json.xprivkey)
return data;
}
if (json.xprivkey) {
return {
xprivkey: json.encrypted
? utils.decrypt(json.xprivkey, passphrase)
: json.xprivkey
};
data.xprivkey = json.encrypted
? utils.decrypt(json.xprivkey, passphrase)
: json.xprivkey;
return data;
}
if (json.xpubkey) {
@ -889,15 +904,16 @@ HDPrivateKey._fromJSON = function _fromJSON(json, passphrase) {
HDPrivateKey.fromJSON = function fromJSON(json, passphrase) {
json = HDPrivateKey._fromJSON(json, passphrase);
if (json.seed)
return HDPrivateKey.fromSeed(json.seed);
if (json.xprivkey) {
return new HDPrivateKey({
xkey: json.xprivkey
xkey: json.xprivkey,
seed: json.seed ? new HDSeed(json.seed) : null
});
}
if (json.seed)
return HDPrivateKey.fromSeed(json.seed);
if (json.xpubkey) {
return new HDPublicKey({
xkey: json.xpubkey
@ -1008,10 +1024,13 @@ HDPublicKey.prototype._unbuild = function _unbuild(xkey) {
if (!utils.isEqual(data.checksum, hash))
throw new Error('checksum mismatch');
if (data.version === network.main.prefixes.xpubkey)
this.network = 'main';
else
this.network = 'testnet';
network.types.some(function(type) {
if (utils.readU32BE(data.version) === network[type].prefixes.xprivkey) {
this.network = type;
return true;
}
return false;
}, this);
this.xpubkey = xkey;
@ -1066,7 +1085,7 @@ HDPublicKey.prototype.derive = function derive(index, hardened) {
var publicPoint, point, publicKey, child;
if (typeof index === 'string')
return this.deriveString(index);
return this.derivePath(index);
cached = cache.get(this.xpubkey, index);
@ -1126,7 +1145,7 @@ HDPublicKey.isValidPath = function isValidPath(arg) {
return false;
};
HDPublicKey.prototype.deriveString = function deriveString(path) {
HDPublicKey.prototype.derivePath = function derivePath(path) {
if (~path.indexOf('\''))
throw new Error('cannot derive hardened');
else if (!HDPublicKey.isValidPath(path))

View File

@ -4,7 +4,6 @@
* https://github.com/indutny/bcoin
*/
var EventEmitter = require('events').EventEmitter;
var bcoin = require('../bcoin');
var bn = require('bn.js');
var constants = bcoin.protocol.constants;
@ -21,22 +20,14 @@ function SPVNode(options) {
if (!(this instanceof SPVNode))
return new SPVNode(options);
EventEmitter.call(this);
if (!options)
options = {};
this.options = options;
if (this.options.debug)
bcoin.debug = this.options.debug;
if (this.options.network)
network.set(this.options.network);
bcoin.node.call(this, options);
this.pool = null;
this.chain = null;
this.wallet = null;
this.walletdb = null;
this.loading = false;
@ -45,70 +36,88 @@ function SPVNode(options) {
this._init();
}
utils.inherits(SPVNode, EventEmitter);
utils.inherits(SPVNode, bcoin.node);
SPVNode.prototype._init = function _init() {
var self = this;
var options;
this.loading = true;
if (!this.options.pool)
this.options.pool = {};
this.chain = new bcoin.chain(this, {
spv: true,
preload: true,
fsync: false
});
this.options.pool.spv = true;
this.options.pool.preload = this.options.pool.preload !== false;
this.pool = new bcoin.pool(this, {
witness: this.network.type === 'segnet',
spv: true
});
this.pool = new bcoin.pool(this.options.pool);
this.chain = this.pool.chain;
this.walletdb = new bcoin.walletdb(this.options.walletdb);
this.walletdb = new bcoin.walletdb(this);
this.pool.on('error', function(err) {
self.emit('error', err);
});
this.pool.on('tx', function(tx) {
self.wallet.addTX(tx);
this.chain.on('error', function(err) {
self.emit('error', err);
});
if (!this.options.wallet)
this.options.wallet = {};
this.walletdb.on('error', function(err) {
self.emit('error', err);
});
if (!this.options.wallet.id)
this.options.wallet.id = 'primary';
this.pool.on('tx', function(tx) {
self.walletdb.addTX(tx, function(err) {
if (err)
self.emit('error', err);
});
});
if (!this.options.wallet.passphrase)
this.options.wallet.passphrase = 'node';
options = {
id: 'primary',
passphrase: this.options.passphrase
};
this.walletdb.create(this.options.wallet, function(err, wallet) {
this.createWallet(options, function(err, wallet) {
if (err)
throw err;
self.wallet = wallet;
self.loading = false;
self.emit('load');
self.pool.startSync();
utils.debug('Node is loaded and syncing.');
});
};
SPVNode.prototype.createWallet = function createWallet(options, callback) {
var self = this;
callback = utils.ensure(callback);
this.walletdb.create(options, function(err, wallet) {
if (err)
return callback(err);
assert(wallet);
utils.debug('Loaded wallet with id=%s address=%s',
wallet.getID(), wallet.getAddress());
wallet.id, wallet.getAddress());
// Handle forks
self.chain.on('remove entry', function(entry) {
self.wallet.tx.getAll().forEach(function(tx) {
if (tx.block === entry.hash || tx.height >= entry.height)
self.wallet.tx.unconfirm(tx);
});
});
self.pool.addWallet(this.wallet, function(err) {
self.pool.addWallet(wallet, function(err) {
if (err)
throw err;
return callback(err);
self.pool.startSync();
self.loading = false;
self.emit('load');
return callback(null, wallet);
});
});
};
SPVNode.prototype.getWallet = function getWallet(id, passphrase, callback) {
return this.walletdb.get(id, passphrase, callback);
};
/**
* Expose
*/