txdb: add block entries.

This commit is contained in:
Christopher Jeffrey 2016-11-04 12:04:55 -07:00
parent 704d9e1783
commit 86e1e35f9a
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
7 changed files with 151 additions and 40 deletions

View File

@ -320,6 +320,12 @@ CLI.prototype.getDetails = co(function* getDetails() {
this.log(details); this.log(details);
}); });
CLI.prototype.getWalletBlock = co(function* getWalletBlock() {
var height = this.argv[0] | 0;
var details = yield this.wallet.getBlock(height);
this.log(details);
});
CLI.prototype.retoken = co(function* retoken() { CLI.prototype.retoken = co(function* retoken() {
var result = yield this.wallet.retoken(); var result = yield this.wallet.retoken();
this.log(result); this.log(result);
@ -480,6 +486,8 @@ CLI.prototype.handleWallet = co(function* handleWallet() {
return yield this.zapWallet(); return yield this.zapWallet();
case 'tx': case 'tx':
return yield this.getDetails(); return yield this.getDetails();
case 'block':
return yield this.getWalletBlock();
case 'view': case 'view':
return yield this.viewTX(); return yield this.viewTX();
case 'import': case 'import':
@ -518,6 +526,7 @@ CLI.prototype.handleWallet = co(function* handleWallet() {
this.log(' $ sign [tx-hex]: Sign transaction.'); this.log(' $ sign [tx-hex]: Sign transaction.');
this.log(' $ zap [age?]: Zap pending wallet TXs.'); this.log(' $ zap [age?]: Zap pending wallet TXs.');
this.log(' $ tx [hash]: View transaction details.'); this.log(' $ tx [hash]: View transaction details.');
this.log(' $ block [height]: View wallet block.');
this.log(' $ view [tx-hex]: Parse and view transaction.'); this.log(' $ view [tx-hex]: Parse and view transaction.');
this.log(' $ import [wif|hex]: Import private or public key.'); this.log(' $ import [wif|hex]: Import private or public key.');
this.log(' $ watch [address]: Import an address.'); this.log(' $ watch [address]: Import an address.');

View File

@ -433,7 +433,7 @@ HTTPClient.prototype.none = function none() {
* Request the raw wallet JSON (will create wallet if it does not exist). * Request the raw wallet JSON (will create wallet if it does not exist).
* @private * @private
* @param {Object} options - See {@link Wallet}. * @param {Object} options - See {@link Wallet}.
* @returns {Promise} - Returns Object. * @returns {Promise}
*/ */
HTTPClient.prototype.createWallet = function createWallet(options) { HTTPClient.prototype.createWallet = function createWallet(options) {
@ -444,7 +444,7 @@ HTTPClient.prototype.createWallet = function createWallet(options) {
* Get the raw wallet JSON. * Get the raw wallet JSON.
* @private * @private
* @param {WalletID} id * @param {WalletID} id
* @returns {Promise} - Returns Object. * @returns {Promise}
*/ */
HTTPClient.prototype.getWallet = function getWallet(id) { HTTPClient.prototype.getWallet = function getWallet(id) {
@ -454,7 +454,7 @@ HTTPClient.prototype.getWallet = function getWallet(id) {
/** /**
* Get wallet transaction history. * Get wallet transaction history.
* @param {WalletID} id * @param {WalletID} id
* @returns {Promise} - Returns {@link TX}[]. * @returns {Promise}
*/ */
HTTPClient.prototype.getHistory = function getHistory(id, account) { HTTPClient.prototype.getHistory = function getHistory(id, account) {
@ -465,7 +465,7 @@ HTTPClient.prototype.getHistory = function getHistory(id, account) {
/** /**
* Get wallet coins. * Get wallet coins.
* @param {WalletID} id * @param {WalletID} id
* @returns {Promise} - Returns {@link Coin}[]. * @returns {Promise}
*/ */
HTTPClient.prototype.getCoins = function getCoins(id, account) { HTTPClient.prototype.getCoins = function getCoins(id, account) {
@ -476,7 +476,7 @@ HTTPClient.prototype.getCoins = function getCoins(id, account) {
/** /**
* Get all unconfirmed transactions. * Get all unconfirmed transactions.
* @param {WalletID} id * @param {WalletID} id
* @returns {Promise} - Returns {@link TX}[]. * @returns {Promise}
*/ */
HTTPClient.prototype.getPending = function getPending(id, account) { HTTPClient.prototype.getPending = function getPending(id, account) {
@ -487,7 +487,7 @@ HTTPClient.prototype.getPending = function getPending(id, account) {
/** /**
* Calculate wallet balance. * Calculate wallet balance.
* @param {WalletID} id * @param {WalletID} id
* @returns {Promise} - Returns {@link Balance}. * @returns {Promise}
*/ */
HTTPClient.prototype.getBalance = function getBalance(id, account) { HTTPClient.prototype.getBalance = function getBalance(id, account) {
@ -499,7 +499,7 @@ HTTPClient.prototype.getBalance = function getBalance(id, account) {
* Get last N wallet transactions. * Get last N wallet transactions.
* @param {WalletID} id * @param {WalletID} id
* @param {Number} limit - Max number of transactions. * @param {Number} limit - Max number of transactions.
* @returns {Promise} - Returns {@link TX}[]. * @returns {Promise}
*/ */
HTTPClient.prototype.getLast = function getLast(id, account, limit) { HTTPClient.prototype.getLast = function getLast(id, account, limit) {
@ -515,7 +515,7 @@ HTTPClient.prototype.getLast = function getLast(id, account, limit) {
* @param {Number} options.end - End time. * @param {Number} options.end - End time.
* @param {Number?} options.limit - Max number of records. * @param {Number?} options.limit - Max number of records.
* @param {Boolean?} options.reverse - Reverse order. * @param {Boolean?} options.reverse - Reverse order.
* @returns {Promise} - Returns {@link TX}[]. * @returns {Promise}
*/ */
HTTPClient.prototype.getRange = function getRange(id, account, options) { HTTPClient.prototype.getRange = function getRange(id, account, options) {
@ -534,20 +534,31 @@ HTTPClient.prototype.getRange = function getRange(id, account, options) {
* is available in the wallet history). * is available in the wallet history).
* @param {WalletID} id * @param {WalletID} id
* @param {Hash} hash * @param {Hash} hash
* @returns {Promise} - Returns {@link TX}[]. * @returns {Promise}
*/ */
HTTPClient.prototype.getWalletTX = function getWalletTX(id, hash) { HTTPClient.prototype.getWalletTX = function getWalletTX(id, hash) {
return this._get('/wallet/' + id + '/tx/' + hash); return this._get('/wallet/' + id + '/tx/' + hash);
}; };
/**
* Get wallet block.
* @param {WalletID} id
* @param {Number} height
* @returns {Promise}
*/
HTTPClient.prototype.getWalletBlock = function getWalletBlock(id, height) {
return this._get('/wallet/' + id + '/block/' + height);
};
/** /**
* Get unspent coin (only possible if the transaction * Get unspent coin (only possible if the transaction
* is available in the wallet history). * is available in the wallet history).
* @param {WalletID} id * @param {WalletID} id
* @param {Hash} hash * @param {Hash} hash
* @param {Number} index * @param {Number} index
* @returns {Promise} - Returns {@link Coin}[]. * @returns {Promise}
*/ */
HTTPClient.prototype.getWalletCoin = function getWalletCoin(id, account, hash, index) { HTTPClient.prototype.getWalletCoin = function getWalletCoin(id, account, hash, index) {
@ -562,7 +573,7 @@ HTTPClient.prototype.getWalletCoin = function getWalletCoin(id, account, hash, i
* @param {Object} options * @param {Object} options
* @param {Base58Address} options.address * @param {Base58Address} options.address
* @param {Amount} options.value * @param {Amount} options.value
* @returns {Promise} - Returns {@link TX}. * @returns {Promise}
*/ */
HTTPClient.prototype.send = function send(id, options) { HTTPClient.prototype.send = function send(id, options) {
@ -611,7 +622,7 @@ HTTPClient.prototype.setPassphrase = function setPassphrase(id, old, new_) {
* Create a transaction, fill. * Create a transaction, fill.
* @param {WalletID} id * @param {WalletID} id
* @param {Object} options * @param {Object} options
* @returns {Promise} - Returns {@link TX}. * @returns {Promise}
*/ */
HTTPClient.prototype.createTX = function createTX(id, options) { HTTPClient.prototype.createTX = function createTX(id, options) {
@ -636,7 +647,7 @@ HTTPClient.prototype.createTX = function createTX(id, options) {
* @param {WalletID} id * @param {WalletID} id
* @param {TX} tx * @param {TX} tx
* @param {Object} options * @param {Object} options
* @returns {Promise} - Returns {@link TX}. * @returns {Promise}
*/ */
HTTPClient.prototype.sign = function sign(id, tx, options) { HTTPClient.prototype.sign = function sign(id, tx, options) {
@ -654,7 +665,7 @@ HTTPClient.prototype.sign = function sign(id, tx, options) {
/** /**
* Fill a transaction with coins. * Fill a transaction with coins.
* @param {TX} tx * @param {TX} tx
* @returns {Promise} - Returns {@link TX}. * @returns {Promise}
*/ */
HTTPClient.prototype.fillCoins = function fillCoins(id, tx) { HTTPClient.prototype.fillCoins = function fillCoins(id, tx) {
@ -895,7 +906,7 @@ HTTPClient.prototype.createAccount = function createAccount(id, options) {
* Create address. * Create address.
* @param {WalletID} id * @param {WalletID} id
* @param {Object} options * @param {Object} options
* @returns {Promise} - Returns Array. * @returns {Promise}
*/ */
HTTPClient.prototype.createAddress = function createAddress(id, options) { HTTPClient.prototype.createAddress = function createAddress(id, options) {
@ -916,7 +927,7 @@ HTTPClient.prototype.createAddress = function createAddress(id, options) {
* Create address. * Create address.
* @param {WalletID} id * @param {WalletID} id
* @param {Object} options * @param {Object} options
* @returns {Promise} - Returns Array. * @returns {Promise}
*/ */
HTTPClient.prototype.createNested = function createNested(id, options) { HTTPClient.prototype.createNested = function createNested(id, options) {

View File

@ -917,6 +917,21 @@ HTTPServer.prototype._init = function _init() {
send(200, { success: true }); send(200, { success: true });
})); }));
// Get Block Record
this.get('/wallet/:id/block/:height', con(function* (req, res, send, next) {
var height = req.options.height;
var block;
enforce(height != null, 'Height is required.');
block = yield req.wallet.getBlock(height);
if (!block)
return send(404);
send(200, block.toJSON());
}));
// Add key // Add key
this.put('/wallet/:id/shared-key', con(function* (req, res, send, next) { this.put('/wallet/:id/shared-key', con(function* (req, res, send, next) {
var options = req.options; var options = req.options;

View File

@ -198,6 +198,14 @@ HTTPWallet.prototype.getTX = function getTX(hash) {
return this.client.getWalletTX(this.id, hash); return this.client.getWalletTX(this.id, hash);
}; };
/**
* @see Wallet#getBlock
*/
HTTPWallet.prototype.getBlock = function getBlock(height) {
return this.client.getWalletBlock(this.id, height);
};
/** /**
* @see Wallet#getCoin * @see Wallet#getCoin
*/ */

View File

@ -981,37 +981,38 @@ TXDB.prototype.removeBlockMap = co(function* removeBlockMap(tx, height) {
*/ */
TXDB.prototype.getBlock = co(function* getBlock(height) { TXDB.prototype.getBlock = co(function* getBlock(height) {
var data = yield this.db.get(layout.b(height)); var data = yield this.get(layout.b(height));
if (!data) if (!data)
return; return;
return BlockRecord.fromRaw(data); return BlockRecord.fromRaw(data);
}); });
/** /**
* Append to the global block record. * Append to the global block record.
* @param {TX} tx * @param {TX} tx
* @param {Number} height * @param {BlockMeta} entry
* @returns {Promise} * @returns {Promise}
*/ */
TXDB.prototype.addBlock = co(function* addBlock(tx, entry) { TXDB.prototype.addBlock = co(function* addBlock(tx, entry) {
var hash = tx.hash('hex'); var hash = tx.hash('hex');
var block = yield this.getBlock(entry.height); var height = tx.height;
var block = yield this.getBlock(height);
if (!block) if (!block)
block = new BlockRecord(entry.hash, entry.height, entry.ts); block = new BlockRecord(tx.block, tx.height, tx.ts);
if (block.hashes.indexOf(hash) !== -1) if (!block.add(hash))
return; return;
block.hashes.push(hash); this.put(layout.b(height), block.toRaw());
this.put(layout.b(entry.height), block.toRaw());
}); });
/** /**
* Remove from the global block record. * Remove from the global block record.
* @param {TX} * @param {TX} tx
* @param {Number} height * @param {Number} height
* @returns {Promise} * @returns {Promise}
*/ */
@ -1019,18 +1020,13 @@ TXDB.prototype.addBlock = co(function* addBlock(tx, entry) {
TXDB.prototype.removeBlock = co(function* removeBlock(tx, height) { TXDB.prototype.removeBlock = co(function* removeBlock(tx, height) {
var hash = tx.hash('hex'); var hash = tx.hash('hex');
var block = yield this.getBlock(height); var block = yield this.getBlock(height);
var index;
if (!block) if (!block)
return; return;
index = block.hashes.indexOf(hash); if (!block.remove(hash))
if (index === -1)
return; return;
block.hashes.splice(index, 1);
if (block.hashes.length === 0) { if (block.hashes.length === 0) {
this.del(layout.b(height)); this.del(layout.b(height));
return; return;
@ -1043,6 +1039,7 @@ TXDB.prototype.removeBlock = co(function* removeBlock(tx, height) {
* Add transaction, potentially runs * Add transaction, potentially runs
* `confirm()` and `removeConflicts()`. * `confirm()` and `removeConflicts()`.
* @param {TX} tx * @param {TX} tx
* @param {BlockMeta} block
* @returns {Promise} * @returns {Promise}
*/ */
@ -1127,6 +1124,7 @@ TXDB.prototype._add = co(function* add(tx, block) {
* Insert transaction. * Insert transaction.
* @private * @private
* @param {TX} tx * @param {TX} tx
* @param {BlockMeta} block
* @returns {Promise} * @returns {Promise}
*/ */
@ -1267,8 +1265,10 @@ TXDB.prototype.insert = co(function* insert(tx, block) {
this.put(layout.H(account, tx.height, hash), DUMMY); this.put(layout.H(account, tx.height, hash), DUMMY);
} }
if (tx.height !== -1) if (tx.height !== -1) {
yield this.addBlockMap(tx, tx.height); yield this.addBlockMap(tx, tx.height);
yield this.addBlock(tx, block);
}
// Update the transaction counter and // Update the transaction counter and
// commit the new state. This state will // commit the new state. This state will
@ -1296,6 +1296,7 @@ TXDB.prototype.insert = co(function* insert(tx, block) {
* Attempt to confirm a transaction. * Attempt to confirm a transaction.
* @private * @private
* @param {TX} tx * @param {TX} tx
* @param {BlockMeta} block
* @returns {Promise} * @returns {Promise}
*/ */
@ -1333,6 +1334,7 @@ TXDB.prototype.confirm = co(function* confirm(hash, block) {
* Attempt to confirm a transaction. * Attempt to confirm a transaction.
* @private * @private
* @param {TX} tx * @param {TX} tx
* @param {BlockMeta} block
* @returns {Promise} * @returns {Promise}
*/ */
@ -1437,8 +1439,10 @@ TXDB.prototype._confirm = co(function* confirm(tx, block) {
this.put(layout.H(account, tx.height, hash), DUMMY); this.put(layout.H(account, tx.height, hash), DUMMY);
} }
if (tx.height !== -1) if (tx.height !== -1) {
yield this.addBlockMap(tx, tx.height); yield this.addBlockMap(tx, tx.height);
yield this.addBlock(tx, block);
}
// Commit the new state. The balance has updated. // Commit the new state. The balance has updated.
this.put(layout.R, this.pending.commit()); this.put(layout.R, this.pending.commit());
@ -1567,8 +1571,10 @@ TXDB.prototype.erase = co(function* erase(tx) {
this.del(layout.H(account, tx.height, hash)); this.del(layout.H(account, tx.height, hash));
} }
if (tx.height !== -1) if (tx.height !== -1) {
yield this.removeBlockMap(tx, tx.height); yield this.removeBlockMap(tx, tx.height);
yield this.removeBlock(tx, tx.height);
}
// Update the transaction counter // Update the transaction counter
// and commit new state due to // and commit new state due to
@ -1740,6 +1746,7 @@ TXDB.prototype.disconnect = co(function* disconnect(tx) {
} }
yield this.removeBlockMap(tx, height); yield this.removeBlockMap(tx, height);
yield this.removeBlock(tx, height);
// We need to update the now-removed // We need to update the now-removed
// block properties and reindex due // block properties and reindex due
@ -3359,8 +3366,54 @@ function BlockRecord(hash, height, ts) {
this.height = height != null ? height : -1; this.height = height != null ? height : -1;
this.ts = ts || 0; this.ts = ts || 0;
this.hashes = []; this.hashes = [];
this.index = {};
} }
/**
* Add transaction to block record.
* @param {Hash} hash
* @returns {Boolean}
*/
BlockRecord.prototype.add = function add(hash) {
if (this.index[hash])
return false;
this.index[hash] = true;
this.hashes.push(hash);
return true;
};
/**
* Remove transaction from block record.
* @param {Hash} hash
* @returns {Boolean}
*/
BlockRecord.prototype.remove = function remove(hash) {
var index;
if (!this.index[hash])
return false;
delete this.index[hash];
// Fast case
if (this.hashes[this.hashes.length - 1] === hash) {
this.hashes.pop();
return true;
}
index = this.hashes.indexOf(hash);
assert(index !== -1);
this.hashes.splice(index, 1);
return true;
};
/** /**
* Instantiate wallet block from serialized tip data. * Instantiate wallet block from serialized tip data.
* @private * @private
@ -3369,12 +3422,17 @@ function BlockRecord(hash, height, ts) {
BlockRecord.prototype.fromRaw = function fromRaw(data) { BlockRecord.prototype.fromRaw = function fromRaw(data) {
var p = new BufferReader(data); var p = new BufferReader(data);
var hash;
this.hash = p.readHash('hex'); this.hash = p.readHash('hex');
this.height = p.readU32(); this.height = p.readU32();
this.ts = p.readU32(); this.ts = p.readU32();
while (p.left()) while (p.left()) {
this.hashes.push(p.readHash('hex')); hash = p.readHash('hex');
this.index[hash] = true;
this.hashes.push(hash);
}
return this; return this;
}; };

View File

@ -1984,6 +1984,16 @@ Wallet.prototype.getTX = function getTX(hash) {
return this.txdb.getTX(hash); return this.txdb.getTX(hash);
}; };
/**
* Get a block from the wallet.
* @param {Number} height
* @returns {Promise} - Returns {@link BlockRecord}.
*/
Wallet.prototype.getBlock = function getBlock(height) {
return this.txdb.getBlock(height);
};
/** /**
* Add a transaction to the wallets TX history. * Add a transaction to the wallets TX history.
* @param {TX} tx * @param {TX} tx

View File

@ -476,7 +476,7 @@ WalletDB.prototype.wipe = co(function* wipe() {
this.logger.warning('Wiping WalletDB TXDB...'); this.logger.warning('Wiping WalletDB TXDB...');
this.logger.warning('I hope you know what you\'re doing.'); this.logger.warning('I hope you know what you\'re doing.');
iter = db.iterator({ iter = this.db.iterator({
gte: new Buffer([0x00]), gte: new Buffer([0x00]),
lte: new Buffer([0xff]) lte: new Buffer([0xff])
}); });
@ -2002,15 +2002,15 @@ WalletDB.prototype.addTX = co(function* addTX(tx) {
block = yield this.getBlock(tx.height); block = yield this.getBlock(tx.height);
if (!block) if (!block)
throw new Error('WDB: Inserting confirmed transaction.'); throw new Error('WDB: Cannot insert tx from unknown chain.');
if (tx.block !== block.hash) if (tx.block !== block.hash)
throw new Error('WDB: Inserting confirmed transaction.'); throw new Error('WDB: Cannot insert tx from alternate chain.');
this.logger.warning('WalletDB is inserting confirmed transaction.'); this.logger.warning('WalletDB is inserting confirmed transaction.');
} }
return yield this._insert(tx); return yield this._insert(tx, block);
} finally { } finally {
unlock(); unlock();
} }