wallet: fix master key race conditions.
This commit is contained in:
parent
ea756026e7
commit
a4a408a18d
@ -271,6 +271,24 @@ Node.prototype.getTip = function getTip() {
|
||||
return Promise.resolve(this.chain.tip);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get chain entry.
|
||||
* @param {Hash} hash
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
Node.prototype.getEntry = co(function* getEntry(hash) {
|
||||
var entry = yield this.chain.db.get(hash);
|
||||
|
||||
if (!entry)
|
||||
return;
|
||||
|
||||
if (!(yield entry.isMainChain()))
|
||||
return;
|
||||
|
||||
return entry;
|
||||
});
|
||||
|
||||
/**
|
||||
* Send a transaction. Do not wait for promise.
|
||||
* @param {TX} tx
|
||||
|
||||
@ -335,10 +335,10 @@ MasterKey.prototype.destroy = co(function* destroy() {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
MasterKey.prototype.decrypt = co(function* decrypt(passphrase) {
|
||||
MasterKey.prototype.decrypt = co(function* decrypt(passphrase, aes) {
|
||||
var unlock = yield this.locker.lock();
|
||||
try {
|
||||
return yield this._decrypt(passphrase);
|
||||
return yield this._decrypt(passphrase, aes);
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
@ -351,16 +351,14 @@ MasterKey.prototype.decrypt = co(function* decrypt(passphrase) {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
MasterKey.prototype._decrypt = co(function* decrypt(passphrase) {
|
||||
MasterKey.prototype._decrypt = co(function* decrypt(passphrase, aes) {
|
||||
var key, data;
|
||||
|
||||
if (!this.encrypted) {
|
||||
assert(this.key);
|
||||
return;
|
||||
}
|
||||
if (!this.encrypted)
|
||||
throw new Error('Master key is not encrypted.');
|
||||
|
||||
if (!passphrase)
|
||||
return;
|
||||
throw new Error('No passphrase provided.');
|
||||
|
||||
this._lock();
|
||||
|
||||
@ -372,6 +370,11 @@ MasterKey.prototype._decrypt = co(function* decrypt(passphrase) {
|
||||
this.iv = null;
|
||||
this.ciphertext = null;
|
||||
|
||||
if (!aes) {
|
||||
key.fill(0);
|
||||
return;
|
||||
}
|
||||
|
||||
return key;
|
||||
});
|
||||
|
||||
@ -381,10 +384,10 @@ MasterKey.prototype._decrypt = co(function* decrypt(passphrase) {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
MasterKey.prototype.encrypt = co(function* encrypt(passphrase) {
|
||||
MasterKey.prototype.encrypt = co(function* encrypt(passphrase, aes) {
|
||||
var unlock = yield this.locker.lock();
|
||||
try {
|
||||
return yield this._encrypt(passphrase);
|
||||
return yield this._encrypt(passphrase, aes);
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
@ -397,14 +400,14 @@ MasterKey.prototype.encrypt = co(function* encrypt(passphrase) {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
MasterKey.prototype._encrypt = co(function* encrypt(passphrase) {
|
||||
MasterKey.prototype._encrypt = co(function* encrypt(passphrase, aes) {
|
||||
var key, data, iv;
|
||||
|
||||
if (this.encrypted)
|
||||
return;
|
||||
throw new Error('Master key is already encrypted.');
|
||||
|
||||
if (!passphrase)
|
||||
return;
|
||||
throw new Error('No passphrase provided.');
|
||||
|
||||
data = this.key.toExtended();
|
||||
iv = crypto.randomBytes(16);
|
||||
@ -419,6 +422,11 @@ MasterKey.prototype._encrypt = co(function* encrypt(passphrase) {
|
||||
this.iv = iv;
|
||||
this.ciphertext = data;
|
||||
|
||||
if (!aes) {
|
||||
key.fill(0);
|
||||
return;
|
||||
}
|
||||
|
||||
return key;
|
||||
});
|
||||
|
||||
|
||||
@ -157,7 +157,7 @@ Wallet.prototype.fromOptions = function fromOptions(options) {
|
||||
id = this.getID();
|
||||
|
||||
if (!token)
|
||||
token = this.getToken(this.master.key, this.tokenDepth);
|
||||
token = this.getToken(this.tokenDepth);
|
||||
|
||||
this.id = id;
|
||||
this.token = token;
|
||||
@ -398,17 +398,14 @@ Wallet.prototype.encrypt = co(function* encrypt(passphrase) {
|
||||
*/
|
||||
|
||||
Wallet.prototype._encrypt = co(function* encrypt(passphrase) {
|
||||
var key;
|
||||
|
||||
if (this.master.encrypted)
|
||||
throw new Error('Wallet is already encrypted.');
|
||||
var key = yield this.master.encrypt(passphrase, true);
|
||||
|
||||
this.start();
|
||||
|
||||
try {
|
||||
key = yield this.master.encrypt(passphrase);
|
||||
yield this.db.encryptKeys(this, key);
|
||||
} catch (e) {
|
||||
key.fill(0);
|
||||
this.drop();
|
||||
throw e;
|
||||
}
|
||||
@ -443,17 +440,14 @@ Wallet.prototype.decrypt = co(function* decrypt(passphrase) {
|
||||
*/
|
||||
|
||||
Wallet.prototype._decrypt = co(function* decrypt(passphrase) {
|
||||
var key;
|
||||
|
||||
if (!this.master.encrypted)
|
||||
throw new Error('Wallet is not encrypted.');
|
||||
var key = yield this.master.decrypt(passphrase, true);
|
||||
|
||||
this.start();
|
||||
|
||||
try {
|
||||
key = yield this.master.decrypt(passphrase);
|
||||
yield this.db.decryptKeys(this, key);
|
||||
} catch (e) {
|
||||
key.fill(0);
|
||||
this.drop();
|
||||
throw e;
|
||||
}
|
||||
@ -491,7 +485,7 @@ Wallet.prototype._retoken = co(function* retoken(passphrase) {
|
||||
var master = yield this.unlock(passphrase);
|
||||
|
||||
this.tokenDepth++;
|
||||
this.token = this.getToken(master, this.tokenDepth);
|
||||
this.token = this.getToken(this.tokenDepth);
|
||||
|
||||
this.start();
|
||||
this.save();
|
||||
@ -614,7 +608,7 @@ Wallet.prototype.unlock = function unlock(passphrase, timeout) {
|
||||
*/
|
||||
|
||||
Wallet.prototype.getID = function getID() {
|
||||
var key, p, hash;
|
||||
var p, key, hash;
|
||||
|
||||
assert(this.master.key, 'Cannot derive id.');
|
||||
|
||||
@ -645,12 +639,12 @@ Wallet.prototype.getID = function getID() {
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
Wallet.prototype.getToken = function getToken(master, nonce) {
|
||||
var key, p;
|
||||
Wallet.prototype.getToken = function getToken(nonce) {
|
||||
var p, key;
|
||||
|
||||
assert(master, 'Cannot derive token.');
|
||||
assert(this.master.key, 'Cannot derive token.');
|
||||
|
||||
key = master.derive(44, true);
|
||||
key = this.master.key.derive(44, true);
|
||||
|
||||
p = new BufferWriter();
|
||||
p.writeBytes(key.privateKey);
|
||||
@ -682,7 +676,7 @@ Wallet.prototype.createAccount = co(function* createAccount(options, passphrase)
|
||||
|
||||
Wallet.prototype._createAccount = co(function* createAccount(options, passphrase) {
|
||||
var name = options.name;
|
||||
var key, master, account, exists;
|
||||
var key, account, exists;
|
||||
|
||||
if (!name)
|
||||
name = this.accountDepth + '';
|
||||
@ -692,7 +686,7 @@ Wallet.prototype._createAccount = co(function* createAccount(options, passphrase
|
||||
if (exists)
|
||||
throw new Error('Account already exists.');
|
||||
|
||||
master = yield this.unlock(passphrase);
|
||||
yield this.unlock(passphrase);
|
||||
|
||||
if (this.watchOnly && options.accountKey) {
|
||||
key = options.accountKey;
|
||||
@ -706,7 +700,8 @@ Wallet.prototype._createAccount = co(function* createAccount(options, passphrase
|
||||
assert(key.network === this.network,
|
||||
'Network mismatch for watch only key.');
|
||||
} else {
|
||||
key = master.deriveAccount44(this.accountDepth);
|
||||
assert(this.master.key);
|
||||
key = this.master.key.deriveAccount44(this.accountDepth);
|
||||
key = key.hdPublicKey;
|
||||
}
|
||||
|
||||
|
||||
@ -1285,10 +1285,11 @@ WalletDB.prototype.encryptKeys = co(function* encryptKeys(wallet, key) {
|
||||
iv = new Buffer(path.hash, 'hex');
|
||||
iv = iv.slice(0, 16);
|
||||
|
||||
path = path.clone();
|
||||
path.data = crypto.encipher(path.data, key, iv);
|
||||
path.encrypted = true;
|
||||
|
||||
wallet.pathCache.set(path.hash, path);
|
||||
wallet.pathCache.push(path.hash, path);
|
||||
|
||||
batch.put(layout.P(wid, path.hash), path.toRaw());
|
||||
}
|
||||
@ -1317,10 +1318,11 @@ WalletDB.prototype.decryptKeys = co(function* decryptKeys(wallet, key) {
|
||||
iv = new Buffer(path.hash, 'hex');
|
||||
iv = iv.slice(0, 16);
|
||||
|
||||
path = path.clone();
|
||||
path.data = crypto.decipher(path.data, key, iv);
|
||||
path.encrypted = false;
|
||||
|
||||
wallet.pathCache.set(path.hash, path);
|
||||
wallet.pathCache.push(path.hash, path);
|
||||
|
||||
batch.put(layout.P(wid, path.hash), path.toRaw());
|
||||
}
|
||||
@ -1715,6 +1717,42 @@ WalletDB.prototype.rollback = co(function* rollback(height) {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Sync with chain height.
|
||||
* @param {Number} height
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
WalletDB.prototype.revert = co(function* revert(height) {
|
||||
var i, iter, item, height, block, tx;
|
||||
|
||||
iter = this.db.iterator({
|
||||
gte: layout.b(height + 1),
|
||||
lte: layout.b(0xffffffff),
|
||||
reverse: true
|
||||
});
|
||||
|
||||
for (;;) {
|
||||
item = yield iter.next();
|
||||
|
||||
if (!item)
|
||||
break;
|
||||
|
||||
try {
|
||||
height = layout.bb(item.key);
|
||||
block = BlockMapRecord.fromRaw(item.value);
|
||||
|
||||
for (i = block.txs.length - 1; i >= 0; i--) {
|
||||
tx = block.txs[i];
|
||||
yield this._unconfirm(tx);
|
||||
}
|
||||
} catch (e) {
|
||||
yield iter.end();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Add a block's transactions and write the new best hash.
|
||||
* @param {ChainEntry} entry
|
||||
|
||||
Loading…
Reference in New Issue
Block a user