diff --git a/lib/node/node.js b/lib/node/node.js index f51f826e..d5379724 100644 --- a/lib/node/node.js +++ b/lib/node/node.js @@ -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 diff --git a/lib/wallet/masterkey.js b/lib/wallet/masterkey.js index e308f63b..3f249418 100644 --- a/lib/wallet/masterkey.js +++ b/lib/wallet/masterkey.js @@ -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; }); diff --git a/lib/wallet/wallet.js b/lib/wallet/wallet.js index ee5452e6..e43f97cf 100644 --- a/lib/wallet/wallet.js +++ b/lib/wallet/wallet.js @@ -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; } diff --git a/lib/wallet/walletdb.js b/lib/wallet/walletdb.js index f70a64c3..9f81527b 100644 --- a/lib/wallet/walletdb.js +++ b/lib/wallet/walletdb.js @@ -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