avoid using pbkdf2 when loading wallet. improve spvnode.
This commit is contained in:
parent
c844cf1c4b
commit
28e105c16e
5
bin/node
5
bin/node
@ -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);
|
||||
});
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
107
lib/bcoin/hd.js
107
lib/bcoin/hd.js
@ -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))
|
||||
|
||||
@ -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
|
||||
*/
|
||||
|
||||
Loading…
Reference in New Issue
Block a user