refactor: remove extra properties from tx and block.
This commit is contained in:
parent
931a2a9398
commit
59645ac3ec
@ -41,6 +41,8 @@
|
||||
"laxbreak": true,
|
||||
"laxcomma": true,
|
||||
|
||||
"noyield": true,
|
||||
|
||||
"predef": [
|
||||
"it",
|
||||
"describe",
|
||||
|
||||
@ -127,7 +127,8 @@ function escape(html, encode) {
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
function addItem(tx) {
|
||||
function addItem(item, entry) {
|
||||
var height = entry ? entry.height : -1;
|
||||
var el;
|
||||
|
||||
if (items.length === 20) {
|
||||
@ -137,11 +138,11 @@ function addItem(tx) {
|
||||
}
|
||||
|
||||
el = create('<a style="display:block;" href="#'
|
||||
+ tx.rhash + '">' + tx.rhash + ' (' + tx.height
|
||||
+ ' - ' + kb(tx.getSize()) + ')</a>');
|
||||
+ item.rhash() + '">' + item.rhash() + ' (' + height
|
||||
+ ' - ' + kb(item.getSize()) + ')</a>');
|
||||
tdiv.appendChild(el);
|
||||
|
||||
setMouseup(el, tx);
|
||||
setMouseup(el, item);
|
||||
|
||||
items.push(el);
|
||||
|
||||
|
||||
@ -217,10 +217,7 @@ Chain.prototype._open = co(function* open() {
|
||||
|
||||
this.emit('tip', tip);
|
||||
|
||||
if (!this.synced && this.isFull()) {
|
||||
this.synced = true;
|
||||
this.emit('full');
|
||||
}
|
||||
this.maybeSync();
|
||||
});
|
||||
|
||||
/**
|
||||
@ -961,16 +958,11 @@ Chain.prototype.setBestChain = co(function* setBestChain(entry, block, prev) {
|
||||
try {
|
||||
result = yield this.verifyContext(block, prev);
|
||||
} catch (e) {
|
||||
// Couldn't verify block.
|
||||
// Revert the height.
|
||||
block.setHeight(-1);
|
||||
|
||||
if (e.type === 'VerifyError') {
|
||||
if (!e.malleated)
|
||||
this.setInvalid(entry.hash);
|
||||
this.emit('invalid', block, entry.height);
|
||||
}
|
||||
|
||||
throw e;
|
||||
}
|
||||
|
||||
@ -1002,16 +994,11 @@ Chain.prototype.saveAlternate = co(function* saveAlternate(entry, block, prev) {
|
||||
// as we can before saving.
|
||||
yield this.verify(block, prev);
|
||||
} catch (e) {
|
||||
// Couldn't verify block.
|
||||
// Revert the height.
|
||||
block.setHeight(-1);
|
||||
|
||||
if (e.type === 'VerifyError') {
|
||||
if (!e.malleated)
|
||||
this.setInvalid(entry.hash);
|
||||
this.emit('invalid', block, entry.height);
|
||||
}
|
||||
|
||||
throw e;
|
||||
}
|
||||
|
||||
@ -1215,7 +1202,7 @@ Chain.prototype.add = co(function* add(block) {
|
||||
Chain.prototype._add = co(function* add(block) {
|
||||
var ret = new VerifyResult();
|
||||
var initial = true;
|
||||
var hash, height, entry, prev;
|
||||
var hash, height, entry, prev, result;
|
||||
|
||||
while (block) {
|
||||
hash = block.hash('hex');
|
||||
@ -1306,11 +1293,6 @@ Chain.prototype._add = co(function* add(block) {
|
||||
}
|
||||
}
|
||||
|
||||
// Update the block height early
|
||||
// Some things in verifyContext may
|
||||
// need access to height on txs.
|
||||
block.setHeight(height);
|
||||
|
||||
// Create a new chain entry.
|
||||
entry = ChainEntry.fromBlock(this, block, prev);
|
||||
|
||||
@ -1338,16 +1320,19 @@ Chain.prototype._add = co(function* add(block) {
|
||||
// Try to resolve orphan chain.
|
||||
block = this.resolveOrphan(hash);
|
||||
initial = false;
|
||||
|
||||
if (!result)
|
||||
result = entry;
|
||||
}
|
||||
|
||||
// Failsafe for large orphan chains. Do not
|
||||
// allow more than 20mb stored in memory.
|
||||
this.pruneOrphans();
|
||||
|
||||
if (!this.synced && this.isFull()) {
|
||||
this.synced = true;
|
||||
this.emit('full');
|
||||
}
|
||||
// Check sync state.
|
||||
this.maybeSync();
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
/**
|
||||
@ -1763,6 +1748,18 @@ Chain.prototype.isFull = function isFull(force) {
|
||||
return !this.isInitial(force);
|
||||
};
|
||||
|
||||
/**
|
||||
* Potentially emit a `sync` event.
|
||||
* @private
|
||||
*/
|
||||
|
||||
Chain.prototype.maybeSync = function maybeSync() {
|
||||
if (!this.synced && this.isFull()) {
|
||||
this.synced = true;
|
||||
this.emit('full');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Test the chain to see if it is still in the initial
|
||||
* syncing phase. Mimic's bitcoind's `IsInitialBlockDownload()`
|
||||
|
||||
@ -25,9 +25,9 @@ var layout = require('./layout');
|
||||
var LRU = require('../utils/lru');
|
||||
var Block = require('../primitives/block');
|
||||
var Outpoint = require('../primitives/outpoint');
|
||||
var TX = require('../primitives/tx');
|
||||
var Address = require('../primitives/address');
|
||||
var ChainEntry = require('./chainentry');
|
||||
var TXMeta = require('../primitives/txmeta');
|
||||
var U8 = encoding.U8;
|
||||
var U32 = encoding.U32;
|
||||
var DUMMY = new Buffer([0]);
|
||||
@ -520,11 +520,7 @@ ChainDB.prototype.getState = co(function* getState() {
|
||||
ChainDB.prototype.saveGenesis = co(function* saveGenesis() {
|
||||
var genesis = this.network.genesisBlock;
|
||||
var block = Block.fromRaw(genesis, 'hex');
|
||||
var entry;
|
||||
|
||||
block.setHeight(0);
|
||||
|
||||
entry = ChainEntry.fromBlock(this.chain, block);
|
||||
var entry = ChainEntry.fromBlock(this.chain, block);
|
||||
|
||||
this.logger.info('Writing genesis block to ChainDB.');
|
||||
|
||||
@ -875,6 +871,34 @@ ChainDB.prototype.getCoinView = co(function* getCoinView(tx) {
|
||||
return view;
|
||||
});
|
||||
|
||||
/**
|
||||
* Get coin viewpoint (historical).
|
||||
* @param {TX} tx
|
||||
* @returns {Promise} - Returns {@link CoinView}.
|
||||
*/
|
||||
|
||||
ChainDB.prototype.getSpentView = co(function* getSpentView(tx) {
|
||||
var view = yield this.getCoinView(tx);
|
||||
var entries = view.toArray();
|
||||
var i, coins, meta;
|
||||
|
||||
for (i = 0; i < entries.length; i++) {
|
||||
coins = entries[i];
|
||||
|
||||
if (!coins.isEmpty())
|
||||
continue;
|
||||
|
||||
meta = yield this.getMeta(coins.hash);
|
||||
|
||||
if (!meta)
|
||||
continue;
|
||||
|
||||
view.addTX(meta.tx, meta.height);
|
||||
}
|
||||
|
||||
return view;
|
||||
});
|
||||
|
||||
/**
|
||||
* Get coins necessary to be resurrected during a reorg.
|
||||
* @param {Hash} hash
|
||||
@ -895,25 +919,12 @@ ChainDB.prototype.getUndoCoins = co(function* getUndoCoins(hash) {
|
||||
*/
|
||||
|
||||
ChainDB.prototype.getBlock = co(function* getBlock(hash) {
|
||||
var item, data, block;
|
||||
|
||||
if (this.options.spv)
|
||||
return;
|
||||
|
||||
item = yield this.getBoth(hash);
|
||||
|
||||
if (!item.hash)
|
||||
return;
|
||||
|
||||
data = yield this.db.get(layout.b(item.hash));
|
||||
var data = yield this.getRawBlock(hash);
|
||||
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
block = Block.fromRaw(data);
|
||||
block.setHeight(item.height);
|
||||
|
||||
return block;
|
||||
return Block.fromRaw(data);
|
||||
});
|
||||
|
||||
/**
|
||||
@ -979,12 +990,12 @@ ChainDB.prototype.getBlockView = co(function* getBlockView(block) {
|
||||
});
|
||||
|
||||
/**
|
||||
* Retrieve a transaction (not filled with coins).
|
||||
* Get a transaction with metadata.
|
||||
* @param {Hash} hash
|
||||
* @returns {Promise} - Returns {@link TX}.
|
||||
* @returns {Promise} - Returns {@link TXMeta}.
|
||||
*/
|
||||
|
||||
ChainDB.prototype.getTX = co(function* getTX(hash) {
|
||||
ChainDB.prototype.getMeta = co(function* getMeta(hash) {
|
||||
var data;
|
||||
|
||||
if (!this.options.indexTX)
|
||||
@ -995,7 +1006,20 @@ ChainDB.prototype.getTX = co(function* getTX(hash) {
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
return TX.fromExtended(data);
|
||||
return TXMeta.fromRaw(data);
|
||||
});
|
||||
|
||||
/**
|
||||
* Retrieve a transaction.
|
||||
* @param {Hash} hash
|
||||
* @returns {Promise} - Returns {@link TX}.
|
||||
*/
|
||||
|
||||
ChainDB.prototype.getTX = co(function* getTX(hash) {
|
||||
var meta = yield this.getMeta(hash);
|
||||
if (!meta)
|
||||
return;
|
||||
return meta.tx;
|
||||
});
|
||||
|
||||
/**
|
||||
@ -1010,28 +1034,6 @@ ChainDB.prototype.hasTX = function hasTX(hash) {
|
||||
return this.db.has(layout.t(hash));
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a transaction and fill it with coins (historical).
|
||||
* @param {Hash} hash
|
||||
* @returns {Promise} - Returns {@link TX}.
|
||||
*/
|
||||
|
||||
ChainDB.prototype.getFullTX = co(function* getFullTX(hash) {
|
||||
var tx;
|
||||
|
||||
if (!this.options.indexTX)
|
||||
return;
|
||||
|
||||
tx = yield this.getTX(hash);
|
||||
|
||||
if (!tx)
|
||||
return;
|
||||
|
||||
yield this.fillHistory(tx);
|
||||
|
||||
return tx;
|
||||
});
|
||||
|
||||
/**
|
||||
* Get all coins pertinent to an address.
|
||||
* @param {Address[]} addresses
|
||||
@ -1113,6 +1115,25 @@ ChainDB.prototype.getHashesByAddress = co(function* getHashesByAddress(addresses
|
||||
*/
|
||||
|
||||
ChainDB.prototype.getTXByAddress = co(function* getTXByAddress(addresses) {
|
||||
var mtxs = yield this.getMetaByAddress(addresses);
|
||||
var out = [];
|
||||
var i, mtx;
|
||||
|
||||
for (i = 0; i < mtxs.length; i++) {
|
||||
mtx = mtxs[i];
|
||||
out.push(mtx.tx);
|
||||
}
|
||||
|
||||
return out;
|
||||
});
|
||||
|
||||
/**
|
||||
* Get all transactions pertinent to an address.
|
||||
* @param {Address[]} addresses
|
||||
* @returns {Promise} - Returns {@link TXMeta}[].
|
||||
*/
|
||||
|
||||
ChainDB.prototype.getMetaByAddress = co(function* getTXByAddress(addresses) {
|
||||
var txs = [];
|
||||
var i, hashes, hash, tx;
|
||||
|
||||
@ -1126,7 +1147,7 @@ ChainDB.prototype.getTXByAddress = co(function* getTXByAddress(addresses) {
|
||||
|
||||
for (i = 0; i < hashes.length; i++) {
|
||||
hash = hashes[i];
|
||||
tx = yield this.getTX(hash);
|
||||
tx = yield this.getMeta(hash);
|
||||
if (tx)
|
||||
txs.push(tx);
|
||||
}
|
||||
@ -1670,7 +1691,7 @@ ChainDB.prototype.connectBlock = co(function* connectBlock(entry, block, view) {
|
||||
}
|
||||
|
||||
// Index the transaction if enabled.
|
||||
this.indexTX(tx, view);
|
||||
this.indexTX(tx, view, entry, i);
|
||||
}
|
||||
|
||||
// Commit new coin state.
|
||||
@ -1790,15 +1811,20 @@ ChainDB.prototype.saveOptions = function saveOptions() {
|
||||
* @private
|
||||
* @param {TX} tx
|
||||
* @param {CoinView} view
|
||||
* @param {ChainEntry} entry
|
||||
* @param {Number} index
|
||||
*/
|
||||
|
||||
ChainDB.prototype.indexTX = function indexTX(tx, view) {
|
||||
ChainDB.prototype.indexTX = function indexTX(tx, view, entry, index) {
|
||||
var hash = tx.hash();
|
||||
var i, input, output, prevout;
|
||||
var hashes, addr;
|
||||
var i, meta, input, output;
|
||||
var prevout, hashes, addr;
|
||||
|
||||
if (this.options.indexTX) {
|
||||
this.put(layout.t(hash), tx.toExtended());
|
||||
meta = TXMeta.fromTX(tx, entry, index);
|
||||
|
||||
this.put(layout.t(hash), meta.toRaw());
|
||||
|
||||
if (this.options.indexAddress) {
|
||||
hashes = tx.getHashes(view);
|
||||
for (i = 0; i < hashes.length; i++) {
|
||||
@ -1844,8 +1870,7 @@ ChainDB.prototype.indexTX = function indexTX(tx, view) {
|
||||
|
||||
ChainDB.prototype.unindexTX = function unindexTX(tx, view) {
|
||||
var hash = tx.hash();
|
||||
var i, input, output, prevout;
|
||||
var hashes, addr;
|
||||
var i, input, output, prevout, hashes, addr;
|
||||
|
||||
if (this.options.indexTX) {
|
||||
this.del(layout.t(hash));
|
||||
|
||||
1033
lib/http/rpc.js
1033
lib/http/rpc.js
File diff suppressed because it is too large
Load Diff
@ -655,7 +655,7 @@ HTTPServer.prototype._init = function _init() {
|
||||
|
||||
enforce(req.options.hash, 'Hash is required.');
|
||||
|
||||
tx = yield this.node.getTX(req.options.hash);
|
||||
tx = yield this.node.getMeta(req.options.hash);
|
||||
|
||||
if (!tx)
|
||||
return send(404);
|
||||
@ -669,7 +669,7 @@ HTTPServer.prototype._init = function _init() {
|
||||
|
||||
enforce(req.options.address, 'Address is required.');
|
||||
|
||||
txs = yield this.node.getTXByAddress(req.options.address);
|
||||
txs = yield this.node.getMetaByAddress(req.options.address);
|
||||
|
||||
send(200, txs.map(function(tx) {
|
||||
return tx.getJSON(this.network);
|
||||
@ -682,7 +682,7 @@ HTTPServer.prototype._init = function _init() {
|
||||
|
||||
enforce(req.options.address, 'Address is required.');
|
||||
|
||||
txs = yield this.node.getTXByAddress(req.options.address);
|
||||
txs = yield this.node.getMetaByAddress(req.options.address);
|
||||
|
||||
send(200, txs.map(function(tx) {
|
||||
return tx.getJSON(this.network);
|
||||
@ -692,7 +692,7 @@ HTTPServer.prototype._init = function _init() {
|
||||
// Block by hash/height
|
||||
this.get('/block/:block', con(function* (req, res, send, next) {
|
||||
var hash = req.options.hash || req.options.height;
|
||||
var block, view;
|
||||
var block, view, height;
|
||||
|
||||
enforce(hash != null, 'Hash or height required.');
|
||||
|
||||
@ -706,7 +706,9 @@ HTTPServer.prototype._init = function _init() {
|
||||
if (!view)
|
||||
return send(404);
|
||||
|
||||
send(200, block.getJSON(this.network, view));
|
||||
height = yield this.chain.db.getHeight(hash);
|
||||
|
||||
send(200, block.getJSON(this.network, view, height));
|
||||
}));
|
||||
|
||||
// Mempool snapshot
|
||||
@ -900,7 +902,7 @@ HTTPServer.prototype._init = function _init() {
|
||||
var options = req.options;
|
||||
var passphrase = options.passphrase;
|
||||
var tx = yield req.wallet.send(options, passphrase);
|
||||
var details = yield req.wallet.toDetails(tx);
|
||||
var details = yield req.wallet.getDetails(tx.hash('hex'));
|
||||
send(200, details.toJSON());
|
||||
}));
|
||||
|
||||
@ -1741,12 +1743,12 @@ ClientSocket.prototype.watchChain = function watchChain() {
|
||||
|
||||
this.watching = true;
|
||||
|
||||
this.bind(this.chain, 'connect', function(entry, block) {
|
||||
self.connectBlock(entry, block);
|
||||
this.bind(this.chain, 'connect', function(entry, block, view) {
|
||||
self.connectBlock(entry, block, view);
|
||||
});
|
||||
|
||||
this.bind(this.chain, 'disconnect', function(entry, block) {
|
||||
self.disconnectBlock(entry, block);
|
||||
this.bind(this.chain, 'disconnect', function(entry, block, view) {
|
||||
self.disconnectBlock(entry, block, view);
|
||||
});
|
||||
|
||||
this.bind(this.chain, 'reset', function(tip) {
|
||||
@ -1777,7 +1779,7 @@ ClientSocket.prototype.unwatchChain = function unwatchChain() {
|
||||
this.unbind(pool, 'tx');
|
||||
};
|
||||
|
||||
ClientSocket.prototype.connectBlock = function connectBlock(entry, block) {
|
||||
ClientSocket.prototype.connectBlock = function connectBlock(entry, block, view) {
|
||||
var raw = this.frameEntry(entry);
|
||||
var txs;
|
||||
|
||||
@ -1786,12 +1788,12 @@ ClientSocket.prototype.connectBlock = function connectBlock(entry, block) {
|
||||
if (!this.filter)
|
||||
return;
|
||||
|
||||
txs = this.filterBlock(block);
|
||||
txs = this.filterBlock(entry, block, view);
|
||||
|
||||
this.emit('block connect', raw, txs);
|
||||
};
|
||||
|
||||
ClientSocket.prototype.disconnectBlock = function disconnectBlock(entry, block) {
|
||||
ClientSocket.prototype.disconnectBlock = function disconnectBlock(entry, block, view) {
|
||||
var raw = this.frameEntry(entry);
|
||||
|
||||
this.emit('entry disconnect', raw);
|
||||
@ -1821,7 +1823,7 @@ ClientSocket.prototype.rescanBlock = function rescanBlock(entry, txs) {
|
||||
});
|
||||
};
|
||||
|
||||
ClientSocket.prototype.filterBlock = function filterBlock(block) {
|
||||
ClientSocket.prototype.filterBlock = function filterBlock(entry, block, view) {
|
||||
var txs = [];
|
||||
var i, tx;
|
||||
|
||||
@ -1831,7 +1833,7 @@ ClientSocket.prototype.filterBlock = function filterBlock(block) {
|
||||
for (i = 0; i < block.txs.length; i++) {
|
||||
tx = block.txs[i];
|
||||
if (this.filterTX(tx))
|
||||
txs.push(this.frameTX(tx));
|
||||
txs.push(this.frameTX(tx, view, entry, i));
|
||||
}
|
||||
|
||||
return txs;
|
||||
@ -1884,7 +1886,7 @@ ClientSocket.prototype.scanner = function scanner(entry, txs) {
|
||||
var i;
|
||||
|
||||
for (i = 0; i < txs.length; i++)
|
||||
raw[i] = this.frameTX(txs[i]);
|
||||
raw[i] = this.frameTX(txs[i], null, entry, i);
|
||||
|
||||
return this.rescanBlock(block, raw);
|
||||
};
|
||||
@ -1895,10 +1897,10 @@ ClientSocket.prototype.frameEntry = function frameEntry(entry) {
|
||||
return entry.toJSON();
|
||||
};
|
||||
|
||||
ClientSocket.prototype.frameTX = function frameTX(tx) {
|
||||
ClientSocket.prototype.frameTX = function frameTX(tx, view, entry, index) {
|
||||
if (this.raw)
|
||||
return tx.toRaw();
|
||||
return tx.getJSON(this.network);
|
||||
return tx.getJSON(this.network, view, entry, index);
|
||||
};
|
||||
|
||||
ClientSocket.prototype.join = function join(id) {
|
||||
|
||||
@ -23,6 +23,7 @@ var Locker = require('../utils/locker');
|
||||
var Outpoint = require('../primitives/outpoint');
|
||||
var TX = require('../primitives/tx');
|
||||
var Coin = require('../primitives/coin');
|
||||
var TXMeta = require('../primitives/txmeta');
|
||||
var MempoolEntry = require('./mempoolentry');
|
||||
var CoinView = require('../coins/coinview');
|
||||
var Coins = require('../coins/coins');
|
||||
@ -229,9 +230,6 @@ Mempool.prototype._removeBlock = co(function* removeBlock(block, txs) {
|
||||
if (this.hasTX(hash))
|
||||
continue;
|
||||
|
||||
tx = tx.clone();
|
||||
tx.unsetBlock();
|
||||
|
||||
try {
|
||||
yield this._addTX(tx);
|
||||
} catch (e) {
|
||||
@ -365,7 +363,7 @@ Mempool.prototype.limitOrphans = function limitOrphans() {
|
||||
/**
|
||||
* Retrieve a transaction from the mempool.
|
||||
* Note that this will not be filled with coins.
|
||||
* @param {TX|Hash} hash
|
||||
* @param {Hash} hash
|
||||
* @returns {TX}
|
||||
*/
|
||||
|
||||
@ -378,8 +376,7 @@ Mempool.prototype.getTX = function getTX(hash) {
|
||||
|
||||
/**
|
||||
* Retrieve a transaction from the mempool.
|
||||
* Note that this will not be filled with coins.
|
||||
* @param {TX|Hash} hash
|
||||
* @param {Hash} hash
|
||||
* @returns {MempoolEntry}
|
||||
*/
|
||||
|
||||
@ -509,6 +506,54 @@ Mempool.prototype.getTXByAddress = function getTXByAddress(addresses) {
|
||||
return txs;
|
||||
};
|
||||
|
||||
/**
|
||||
* Find all transactions pertaining to a certain address.
|
||||
* @param {Address[]} addresses
|
||||
* @returns {TXMeta[]}
|
||||
*/
|
||||
|
||||
Mempool.prototype.getMetaByAddress = function getMetaByAddress(addresses) {
|
||||
var txs = [];
|
||||
var i, j, tx, hash;
|
||||
|
||||
if (!Array.isArray(addresses))
|
||||
addresses = [addresses];
|
||||
|
||||
for (i = 0; i < addresses.length; i++) {
|
||||
hash = Address.getHash(addresses[i], 'hex');
|
||||
|
||||
if (!hash)
|
||||
continue;
|
||||
|
||||
tx = this.txIndex.getMeta(hash);
|
||||
|
||||
for (j = 0; j < tx.length; j++)
|
||||
txs.push(tx[j]);
|
||||
}
|
||||
|
||||
return txs;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve a transaction from the mempool.
|
||||
* Note that this will not be filled with coins.
|
||||
* @param {Hash} hash
|
||||
* @returns {TXMeta}
|
||||
*/
|
||||
|
||||
Mempool.prototype.getMeta = function getMeta(hash) {
|
||||
var entry = this.getEntry(hash);
|
||||
var meta;
|
||||
|
||||
if (!entry)
|
||||
return;
|
||||
|
||||
meta = TXMeta.fromTX(entry.tx);
|
||||
meta.ps = entry.ts;
|
||||
|
||||
return meta;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test the mempool to see if it contains a transaction.
|
||||
* @param {Hash} hash
|
||||
@ -1760,6 +1805,24 @@ AddressIndex.prototype.getTX = function getTX(address) {
|
||||
return out;
|
||||
};
|
||||
|
||||
AddressIndex.prototype.getMeta = function getMeta(address) {
|
||||
var items = this.index[address];
|
||||
var out = [];
|
||||
var i, hash, tx;
|
||||
|
||||
if (!items)
|
||||
return out;
|
||||
|
||||
for (i = 0; i < items.length; i++) {
|
||||
hash = items[i].toString('hex');
|
||||
tx = this.mempool.getMeta(hash);
|
||||
assert(tx);
|
||||
out.push(tx);
|
||||
}
|
||||
|
||||
return out;
|
||||
};
|
||||
|
||||
AddressIndex.prototype.addTX = function addTX(tx, view) {
|
||||
var key = tx.hash('hex');
|
||||
var hashes = tx.getHashes(view, 'hex');
|
||||
|
||||
@ -137,9 +137,9 @@ Miner.prototype._init = function _init() {
|
||||
self.attempt.destroy();
|
||||
});
|
||||
|
||||
this.on('block', function(block) {
|
||||
this.on('block', function(block, entry) {
|
||||
// Emit the block hex as a failsafe (in case we can't send it)
|
||||
self.logger.info('Found block: %d (%s).', block.height, block.rhash());
|
||||
self.logger.info('Found block: %d (%s).', entry.height, entry.rhash());
|
||||
self.logger.debug('Raw: %s', block.toRaw().toString('hex'));
|
||||
});
|
||||
|
||||
@ -195,7 +195,7 @@ Miner.prototype._close = co(function* close() {
|
||||
|
||||
Miner.prototype.start = co(function* start() {
|
||||
var self = this;
|
||||
var block;
|
||||
var block, entry;
|
||||
|
||||
assert(!this.running, 'Miner is already running.');
|
||||
|
||||
@ -237,7 +237,7 @@ Miner.prototype.start = co(function* start() {
|
||||
continue;
|
||||
|
||||
try {
|
||||
yield this.chain.add(block);
|
||||
entry = yield this.chain.add(block);
|
||||
} catch (e) {
|
||||
if (this.stopping)
|
||||
break;
|
||||
@ -248,7 +248,7 @@ Miner.prototype.start = co(function* start() {
|
||||
if (this.stopping)
|
||||
break;
|
||||
|
||||
this.emit('block', block);
|
||||
this.emit('block', block, entry);
|
||||
}
|
||||
|
||||
this.emit('done');
|
||||
@ -486,7 +486,7 @@ Miner.prototype.build = function build(attempt) {
|
||||
attempt.fees += item.fee;
|
||||
attempt.items.push(item);
|
||||
|
||||
block.txs.push(tx.clone());
|
||||
block.txs.push(tx);
|
||||
|
||||
deps = depMap[hash];
|
||||
|
||||
|
||||
@ -125,7 +125,6 @@ MinerBlock.prototype._init = function _init() {
|
||||
block.ts = Math.max(this.network.now(), this.tip.ts + 1);
|
||||
block.bits = this.bits;
|
||||
block.nonce = 0;
|
||||
block.height = this.height;
|
||||
|
||||
// Coinbase input.
|
||||
input = new Input();
|
||||
@ -300,7 +299,7 @@ MinerBlock.prototype.addTX = function addTX(tx, view) {
|
||||
this.fees += item.fee;
|
||||
|
||||
// Add the tx to our block
|
||||
this.block.txs.push(tx.clone());
|
||||
this.block.txs.push(tx);
|
||||
this.items.push(item);
|
||||
|
||||
// Update coinbase value
|
||||
|
||||
@ -338,7 +338,6 @@ CompactBlock.prototype.toBlock = function toBlock() {
|
||||
for (i = 0; i < this.available.length; i++) {
|
||||
tx = this.available[i];
|
||||
assert(tx, 'Compact block is not full.');
|
||||
tx.setBlock(block, i);
|
||||
block.txs[i] = tx;
|
||||
}
|
||||
|
||||
|
||||
@ -1718,7 +1718,7 @@ Peer.prototype._handleGetData = co(function* _handleGetData(packet) {
|
||||
var blocks = 0;
|
||||
var unknown = -1;
|
||||
var items = packet.items;
|
||||
var i, j, item, tx, block, result;
|
||||
var i, j, item, tx, block, result, height;
|
||||
|
||||
if (items.length > 50000)
|
||||
throw new Error('getdata size too large (' + items.length + ').');
|
||||
@ -1790,7 +1790,8 @@ Peer.prototype._handleGetData = co(function* _handleGetData(packet) {
|
||||
break;
|
||||
case constants.inv.CMPCT_BLOCK:
|
||||
// Fallback to full block.
|
||||
if (block.height < this.chain.tip.height - 10) {
|
||||
height = yield this.chain.db.getHeight(item.hash);
|
||||
if (height < this.chain.tip.height - 10) {
|
||||
result = yield this._sendBlock(item, this.compactWitness);
|
||||
if (!result) {
|
||||
notFound.push(item);
|
||||
@ -2335,7 +2336,7 @@ Peer.prototype._handleCmpctBlock = co(function* _handleCmpctBlock(packet) {
|
||||
|
||||
Peer.prototype._handleGetBlockTxn = co(function* _handleGetBlockTxn(packet) {
|
||||
var req = packet.request;
|
||||
var res, item, block;
|
||||
var res, item, block, height;
|
||||
|
||||
if (this.chain.db.options.spv)
|
||||
return;
|
||||
@ -2358,7 +2359,9 @@ Peer.prototype._handleGetBlockTxn = co(function* _handleGetBlockTxn(packet) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (block.height < this.chain.tip.height - 15) {
|
||||
height = yield this.chain.db.getHeight(req.hash);
|
||||
|
||||
if (height < this.chain.tip.height - 15) {
|
||||
this.logger.debug(
|
||||
'Peer sent a getblocktxn for a block > 15 deep (%s)',
|
||||
this.hostname);
|
||||
|
||||
@ -403,22 +403,55 @@ FullNode.prototype.getCoin = function getCoin(hash, index) {
|
||||
*/
|
||||
|
||||
FullNode.prototype.getCoinsByAddress = co(function* getCoinsByAddress(addresses) {
|
||||
var coins = this.mempool.getCoinsByAddress(addresses);
|
||||
var i, blockCoins, coin, spent;
|
||||
var mempool = this.mempool.getCoinsByAddress(addresses);
|
||||
var chain = yield this.chain.db.getCoinsByAddress(addresses);
|
||||
var out = [];
|
||||
var i, coin, spent;
|
||||
|
||||
blockCoins = yield this.chain.db.getCoinsByAddress(addresses);
|
||||
|
||||
for (i = 0; i < blockCoins.length; i++) {
|
||||
coin = blockCoins[i];
|
||||
for (i = 0; i < chain.length; i++) {
|
||||
coin = chain[i];
|
||||
spent = this.mempool.isSpent(coin.hash, coin.index);
|
||||
|
||||
if (spent)
|
||||
continue;
|
||||
|
||||
coins.push(coin);
|
||||
out.push(coin);
|
||||
}
|
||||
|
||||
return coins;
|
||||
for (i = 0; i < mempool.length; i++) {
|
||||
coin = mempool[i];
|
||||
out.push(coin);
|
||||
}
|
||||
|
||||
return out;
|
||||
});
|
||||
|
||||
/**
|
||||
* Retrieve transactions pertaining to an
|
||||
* address from the mempool or chain database.
|
||||
* @param {Address} addresses
|
||||
* @returns {Promise} - Returns {@link TXMeta}[].
|
||||
*/
|
||||
|
||||
FullNode.prototype.getMetaByAddress = co(function* getTXByAddress(addresses) {
|
||||
var mempool = this.mempool.getMetaByAddress(addresses);
|
||||
var chain = yield this.chain.db.getMetaByAddress(addresses);
|
||||
return chain.concat(mempool);
|
||||
});
|
||||
|
||||
/**
|
||||
* Retrieve a transaction from the mempool or chain database.
|
||||
* @param {Hash} hash
|
||||
* @returns {Promise} - Returns {@link TXMeta}.
|
||||
*/
|
||||
|
||||
FullNode.prototype.getMeta = co(function* getMeta(hash) {
|
||||
var meta = this.mempool.getMeta(hash);
|
||||
|
||||
if (meta)
|
||||
return meta;
|
||||
|
||||
return yield this.chain.db.getMeta(hash);
|
||||
});
|
||||
|
||||
/**
|
||||
@ -429,9 +462,16 @@ FullNode.prototype.getCoinsByAddress = co(function* getCoinsByAddress(addresses)
|
||||
*/
|
||||
|
||||
FullNode.prototype.getTXByAddress = co(function* getTXByAddress(addresses) {
|
||||
var mempool = this.mempool.getTXByAddress(addresses);
|
||||
var txs = yield this.chain.db.getTXByAddress(addresses);
|
||||
return mempool.concat(txs);
|
||||
var mtxs = yield this.getMetaByAddress(addresses);
|
||||
var out = [];
|
||||
var i, mtx;
|
||||
|
||||
for (i = 0; i < mtxs.length; i++) {
|
||||
mtx = mtxs[i];
|
||||
out.push(mtx.tx);
|
||||
}
|
||||
|
||||
return out;
|
||||
});
|
||||
|
||||
/**
|
||||
@ -440,14 +480,12 @@ FullNode.prototype.getTXByAddress = co(function* getTXByAddress(addresses) {
|
||||
* @returns {Promise} - Returns {@link TX}.
|
||||
*/
|
||||
|
||||
FullNode.prototype.getTX = function getTX(hash) {
|
||||
var tx = this.mempool.getTX(hash);
|
||||
|
||||
if (tx)
|
||||
return Promise.resolve(tx);
|
||||
|
||||
return this.chain.db.getTX(hash);
|
||||
};
|
||||
FullNode.prototype.getTX = co(function* getTX(hash) {
|
||||
var mtx = yield this.getMeta(hash);
|
||||
if (!mtx)
|
||||
return;
|
||||
return mtx.tx;
|
||||
});
|
||||
|
||||
/**
|
||||
* Test whether the mempool or chain contains a transaction.
|
||||
@ -462,20 +500,6 @@ FullNode.prototype.hasTX = function hasTX(hash) {
|
||||
return this.chain.db.hasTX(hash);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check whether a coin has been spent.
|
||||
* @param {Hash} hash
|
||||
* @param {Number} index
|
||||
* @returns {Promise} - Returns Boolean.
|
||||
*/
|
||||
|
||||
FullNode.prototype.isSpent = function isSpent(hash, index) {
|
||||
if (this.mempool.isSpent(hash, index))
|
||||
return Promise.resolve(true);
|
||||
|
||||
return this.chain.db.isSpent(hash, index);
|
||||
};
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
@ -32,7 +32,6 @@ var InvItem = require('./invitem');
|
||||
* @property {Number} bits
|
||||
* @property {Number} nonce
|
||||
* @property {Number} totalTX - Transaction count.
|
||||
* @property {Number} height - Block height (-1 if not present).
|
||||
* @property {TX[]} txs - Transaction vector.
|
||||
* @property {ReversedHash} rhash - Reversed block hash (uint256le).
|
||||
*/
|
||||
@ -48,14 +47,13 @@ function AbstractBlock(options) {
|
||||
this.bits = 0;
|
||||
this.nonce = 0;
|
||||
this.totalTX = 0;
|
||||
this.height = -1;
|
||||
|
||||
this.txs = null;
|
||||
this.mutable = false;
|
||||
this.memory = false;
|
||||
|
||||
this._valid = null;
|
||||
this._validHeaders = null;
|
||||
|
||||
this._hash = null;
|
||||
this._hhash = null;
|
||||
this._size = null;
|
||||
@ -65,6 +63,8 @@ function AbstractBlock(options) {
|
||||
this.parseOptions(options);
|
||||
}
|
||||
|
||||
AbstractBlock.prototype.memory = false;
|
||||
|
||||
/**
|
||||
* Inject properties from options object.
|
||||
* @private
|
||||
@ -92,11 +92,6 @@ AbstractBlock.prototype.parseOptions = function parseOptions(options) {
|
||||
this.totalTX = options.totalTX;
|
||||
}
|
||||
|
||||
if (options.height != null) {
|
||||
assert(util.isNumber(options.height));
|
||||
this.height = options.height;
|
||||
}
|
||||
|
||||
if (options.mutable != null)
|
||||
this.mutable = !!options.mutable;
|
||||
|
||||
@ -118,7 +113,6 @@ AbstractBlock.prototype.parseJSON = function parseJSON(json) {
|
||||
assert(util.isNumber(json.bits));
|
||||
assert(util.isNumber(json.nonce));
|
||||
assert(util.isNumber(json.totalTX));
|
||||
assert(util.isNumber(json.height));
|
||||
|
||||
this.version = json.version;
|
||||
this.prevBlock = util.revHex(json.prevBlock);
|
||||
@ -127,7 +121,6 @@ AbstractBlock.prototype.parseJSON = function parseJSON(json) {
|
||||
this.bits = json.bits;
|
||||
this.nonce = json.nonce;
|
||||
this.totalTX = json.totalTX;
|
||||
this.height = json.height;
|
||||
|
||||
return this;
|
||||
};
|
||||
@ -260,24 +253,6 @@ AbstractBlock.prototype.verifyPOW = function verifyPOW() {
|
||||
return btcutils.verifyPOW(this.hash(), this.bits);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the `height` property and the `height`
|
||||
* property of all transactions within the block.
|
||||
* @param {Number} height
|
||||
*/
|
||||
|
||||
AbstractBlock.prototype.setHeight = function setHeight(height) {
|
||||
var i;
|
||||
|
||||
this.height = height;
|
||||
|
||||
if (!this.txs)
|
||||
return;
|
||||
|
||||
for (i = 0; i < this.txs.length; i++)
|
||||
this.txs[i].height = height;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get little-endian block hash.
|
||||
* @returns {Hash}
|
||||
|
||||
@ -244,13 +244,11 @@ Block.prototype.hasWitness = function hasWitness() {
|
||||
/**
|
||||
* Add a transaction to the block's tx vector.
|
||||
* @param {TX} tx
|
||||
* @returns {TX}
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
Block.prototype.addTX = function addTX(tx) {
|
||||
var index = this.txs.push(tx) - 1;
|
||||
tx.setBlock(this, index);
|
||||
return index;
|
||||
return this.txs.push(tx) - 1;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -617,15 +615,16 @@ Block.prototype.inspect = function inspect() {
|
||||
/**
|
||||
* Inspect the block and return a more
|
||||
* user-friendly representation of the data.
|
||||
* @param {CoinView?} view
|
||||
* @param {CoinView} view
|
||||
* @param {Number} height
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
Block.prototype.format = function format(view) {
|
||||
Block.prototype.format = function format(view, height) {
|
||||
var commitmentHash = this.getCommitmentHash('hex');
|
||||
return {
|
||||
hash: this.rhash(),
|
||||
height: this.height,
|
||||
height: height != null ? height : -1,
|
||||
size: this.getSize(),
|
||||
virtualSize: this.getVirtualSize(),
|
||||
date: util.date(this.ts),
|
||||
@ -638,9 +637,9 @@ Block.prototype.format = function format(view) {
|
||||
ts: this.ts,
|
||||
bits: this.bits,
|
||||
nonce: this.nonce,
|
||||
txs: this.txs.map(function(tx) {
|
||||
return tx.inspect(view);
|
||||
})
|
||||
txs: this.txs.map(function(tx, i) {
|
||||
return tx.format(view, null, i);
|
||||
}, this)
|
||||
};
|
||||
};
|
||||
|
||||
@ -661,14 +660,15 @@ Block.prototype.toJSON = function toJSON() {
|
||||
* of little-endian uint256s.
|
||||
* @param {Network} network
|
||||
* @param {CoinView} view
|
||||
* @param {Number} height
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
Block.prototype.getJSON = function getJSON(network, view) {
|
||||
Block.prototype.getJSON = function getJSON(network, view, height) {
|
||||
network = Network.get(network);
|
||||
return {
|
||||
hash: this.rhash(),
|
||||
height: this.height,
|
||||
height: height,
|
||||
version: this.version,
|
||||
prevBlock: util.revHex(this.prevBlock),
|
||||
merkleRoot: util.revHex(this.merkleRoot),
|
||||
@ -676,9 +676,9 @@ Block.prototype.getJSON = function getJSON(network, view) {
|
||||
bits: this.bits,
|
||||
nonce: this.nonce,
|
||||
totalTX: this.totalTX,
|
||||
txs: this.txs.map(function(tx) {
|
||||
return tx.getJSON(network, view);
|
||||
})
|
||||
txs: this.txs.map(function(tx, i) {
|
||||
return tx.getJSON(network, view, this, i);
|
||||
}, this)
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -205,16 +205,6 @@ Coin.prototype.getJSON = function getJSON(network, minimal) {
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate an Coin from a jsonified coin object.
|
||||
* @param {Object} json - The jsonified coin object.
|
||||
* @returns {Coin}
|
||||
*/
|
||||
|
||||
Coin.fromJSON = function fromJSON(json) {
|
||||
return new Coin().fromJSON(json);
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject JSON properties into coin.
|
||||
* @private
|
||||
@ -236,11 +226,21 @@ Coin.prototype.fromJSON = function fromJSON(json) {
|
||||
this.script.fromJSON(json.script);
|
||||
this.coinbase = json.coinbase;
|
||||
this.hash = json.hash ? util.revHex(json.hash) : null;
|
||||
this.index = json.index;
|
||||
this.index = json.index != null ? json.index : -1;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate an Coin from a jsonified coin object.
|
||||
* @param {Object} json - The jsonified coin object.
|
||||
* @returns {Coin}
|
||||
*/
|
||||
|
||||
Coin.fromJSON = function fromJSON(json) {
|
||||
return new Coin().fromJSON(json);
|
||||
};
|
||||
|
||||
/**
|
||||
* Write the coin to a buffer writer.
|
||||
* @param {BufferWriter} bw
|
||||
|
||||
@ -209,6 +209,62 @@ Headers.fromBlock = function fromBlock(block) {
|
||||
return headers;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert the block to an object suitable
|
||||
* for JSON serialization.
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
Headers.prototype.toJSON = function toJSON() {
|
||||
return this.getJSON();
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert the block to an object suitable
|
||||
* for JSON serialization. Note that the hashes
|
||||
* will be reversed to abide by bitcoind's legacy
|
||||
* of little-endian uint256s.
|
||||
* @param {Network} network
|
||||
* @param {CoinView} view
|
||||
* @param {Number} height
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
Headers.prototype.getJSON = function getJSON(network, view, height) {
|
||||
return {
|
||||
hash: this.rhash(),
|
||||
height: height,
|
||||
version: this.version,
|
||||
prevBlock: util.revHex(this.prevBlock),
|
||||
merkleRoot: util.revHex(this.merkleRoot),
|
||||
ts: this.ts,
|
||||
bits: this.bits,
|
||||
nonce: this.nonce,
|
||||
totalTX: this.totalTX
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject properties from json object.
|
||||
* @private
|
||||
* @param {Object} json
|
||||
*/
|
||||
|
||||
Headers.prototype.fromJSON = function fromJSON(json) {
|
||||
this.parseJSON(json);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate a merkle block from a jsonified block object.
|
||||
* @param {Object} json - The jsonified block object.
|
||||
* @returns {Headers}
|
||||
*/
|
||||
|
||||
Headers.fromJSON = function fromJSON(json) {
|
||||
return new Headers().fromJSON(json);
|
||||
};
|
||||
|
||||
/**
|
||||
* Inspect the headers and return a more
|
||||
* user-friendly representation of the data.
|
||||
@ -216,9 +272,21 @@ Headers.fromBlock = function fromBlock(block) {
|
||||
*/
|
||||
|
||||
Headers.prototype.inspect = function inspect() {
|
||||
return this.format();
|
||||
};
|
||||
|
||||
/**
|
||||
* Inspect the headers and return a more
|
||||
* user-friendly representation of the data.
|
||||
* @param {CoinView} view
|
||||
* @param {Number} height
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
Headers.prototype.format = function format(view, height) {
|
||||
return {
|
||||
hash: this.rhash(),
|
||||
height: this.height,
|
||||
height: height != null ? height : -1,
|
||||
date: util.date(this.ts),
|
||||
version: util.hex32(this.version),
|
||||
prevBlock: util.revHex(this.prevBlock),
|
||||
|
||||
@ -15,3 +15,4 @@ exports.NetworkAddress = require('./netaddress');
|
||||
exports.Outpoint = require('./outpoint');
|
||||
exports.Output = require('./output');
|
||||
exports.TX = require('./tx');
|
||||
exports.TXMeta = require('./txmeta');
|
||||
|
||||
@ -248,7 +248,7 @@ Input.prototype.format = function format(coin) {
|
||||
redeem: this.getRedeem(coin),
|
||||
sequence: this.sequence,
|
||||
prevout: this.prevout,
|
||||
coin: coin
|
||||
coin: coin || null
|
||||
};
|
||||
};
|
||||
|
||||
@ -289,7 +289,7 @@ Input.prototype.getJSON = function getJSON(network, coin) {
|
||||
witness: this.witness.toJSON(),
|
||||
sequence: this.sequence,
|
||||
address: address,
|
||||
coin: coin ? coin.getJSON(network, true) : null
|
||||
coin: coin ? coin.getJSON(network, true) : undefined
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -33,50 +33,19 @@ var BufferReader = require('../utils/reader');
|
||||
* @exports MemBlock
|
||||
* @constructor
|
||||
* @param {NakedBlock} options
|
||||
* @property {Boolean} memory - Always true.
|
||||
* @property {Number} coinbaseHeight - The coinbase height which
|
||||
* was extracted by the parser (the coinbase is the only
|
||||
* transaction we parse ahead of time).
|
||||
* @property {Buffer} raw - The raw block data.
|
||||
*/
|
||||
|
||||
function MemBlock(options) {
|
||||
function MemBlock() {
|
||||
if (!(this instanceof MemBlock))
|
||||
return new MemBlock(options);
|
||||
return new MemBlock();
|
||||
|
||||
AbstractBlock.call(this, options);
|
||||
|
||||
this.memory = true;
|
||||
this.coinbaseHeight = -1;
|
||||
this.raw = null;
|
||||
|
||||
if (options)
|
||||
this.fromOptions(options);
|
||||
this._cbHeight = -1;
|
||||
this._raw = null;
|
||||
}
|
||||
|
||||
util.inherits(MemBlock, AbstractBlock);
|
||||
|
||||
/**
|
||||
* Inject properties from options object.
|
||||
* @private
|
||||
* @param {NakedBlock} options
|
||||
*/
|
||||
|
||||
MemBlock.prototype.fromOptions = function fromOptions(options) {
|
||||
this.coinbaseHeight = options.coinbaseHeight;
|
||||
this.raw = options.raw;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate memblock from options object.
|
||||
* @param {NakedBlock} options
|
||||
* @returns {MemBlock}
|
||||
*/
|
||||
|
||||
MemBlock.fromOptions = function fromOptions(options) {
|
||||
return new MemBlock().fromOptions(options);
|
||||
};
|
||||
MemBlock.prototype.memory = true;
|
||||
|
||||
/**
|
||||
* Serialize the block headers.
|
||||
@ -84,7 +53,7 @@ MemBlock.fromOptions = function fromOptions(options) {
|
||||
*/
|
||||
|
||||
MemBlock.prototype.abbr = function abbr() {
|
||||
return this.raw.slice(0, 80);
|
||||
return this._raw.slice(0, 80);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -93,7 +62,7 @@ MemBlock.prototype.abbr = function abbr() {
|
||||
*/
|
||||
|
||||
MemBlock.prototype.getSize = function getSize() {
|
||||
return this.raw.length;
|
||||
return this._raw.length;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -115,7 +84,7 @@ MemBlock.prototype._verify = function _verify(ret) {
|
||||
*/
|
||||
|
||||
MemBlock.prototype.getCoinbaseHeight = function getCoinbaseHeight() {
|
||||
return this.coinbaseHeight;
|
||||
return this._cbHeight;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -153,8 +122,8 @@ MemBlock.prototype.fromRaw = function fromRaw(data) {
|
||||
}
|
||||
}
|
||||
|
||||
this.coinbaseHeight = height;
|
||||
this.raw = br.data;
|
||||
this._cbHeight = height;
|
||||
this._raw = br.data;
|
||||
|
||||
return this;
|
||||
};
|
||||
@ -175,7 +144,7 @@ MemBlock.fromRaw = function fromRaw(data) {
|
||||
*/
|
||||
|
||||
MemBlock.prototype.toRaw = function toRaw() {
|
||||
return this.raw;
|
||||
return this._raw;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -184,7 +153,7 @@ MemBlock.prototype.toRaw = function toRaw() {
|
||||
*/
|
||||
|
||||
MemBlock.prototype.toNormal = function toNormal() {
|
||||
return this.raw;
|
||||
return this._raw;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -195,11 +164,11 @@ MemBlock.prototype.toNormal = function toNormal() {
|
||||
*/
|
||||
|
||||
MemBlock.prototype.toBlock = function toBlock() {
|
||||
var block = Block.fromRaw(this.raw);
|
||||
var block = Block.fromRaw(this._raw);
|
||||
block._hash = this._hash;
|
||||
block._cbHeight = this.coinbaseHeight;
|
||||
block._cbHeight = this._cbHeight;
|
||||
block._validHeaders = this._validHeaders;
|
||||
this.raw = null;
|
||||
this._raw = null;
|
||||
return block;
|
||||
};
|
||||
|
||||
|
||||
@ -100,7 +100,7 @@ MerkleBlock.prototype.getSize = function getSize() {
|
||||
/**
|
||||
* Add a transaction to the block's tx vector.
|
||||
* @param {TX} tx
|
||||
* @returns {TX}
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
MerkleBlock.prototype.addTX = function addTX(tx) {
|
||||
@ -108,9 +108,8 @@ MerkleBlock.prototype.addTX = function addTX(tx) {
|
||||
var index = this.map[hash];
|
||||
|
||||
this.txs.push(tx);
|
||||
tx.setBlock(this, index);
|
||||
|
||||
return index;
|
||||
return index != null ? index : -1;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -314,10 +313,21 @@ MerkleBlock.prototype.getCoinbaseHeight = function getCoinbaseHeight() {
|
||||
*/
|
||||
|
||||
MerkleBlock.prototype.inspect = function inspect() {
|
||||
return this.format();
|
||||
};
|
||||
|
||||
/**
|
||||
* Inspect the block and return a more
|
||||
* user-friendly representation of the data.
|
||||
* @param {CoinView} view
|
||||
* @param {Number} height
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
MerkleBlock.prototype.format = function format(view, height) {
|
||||
return {
|
||||
type: 'merkleblock',
|
||||
hash: this.rhash(),
|
||||
height: this.height,
|
||||
height: height != null ? height : -1,
|
||||
date: util.date(this.ts),
|
||||
version: util.hex32(this.version),
|
||||
prevBlock: util.revHex(this.prevBlock),
|
||||
@ -433,17 +443,29 @@ MerkleBlock.fromRaw = function fromRaw(data, enc) {
|
||||
|
||||
/**
|
||||
* Convert the block to an object suitable
|
||||
* for JSON serialization. Note that the hashes
|
||||
* will be reversed to abide by bitcoind's legacy
|
||||
* of little-endian uint256s.
|
||||
* for JSON serialization.
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
MerkleBlock.prototype.toJSON = function toJSON() {
|
||||
return this.getJSON();
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert the block to an object suitable
|
||||
* for JSON serialization. Note that the hashes
|
||||
* will be reversed to abide by bitcoind's legacy
|
||||
* of little-endian uint256s.
|
||||
* @param {Network} network
|
||||
* @param {CoinView} view
|
||||
* @param {Number} height
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
MerkleBlock.prototype.getJSON = function getJSON(network, view, height) {
|
||||
return {
|
||||
type: 'merkleblock',
|
||||
hash: this.rhash(),
|
||||
height: this.height,
|
||||
height: height,
|
||||
version: this.version,
|
||||
prevBlock: util.revHex(this.prevBlock),
|
||||
merkleRoot: util.revHex(this.merkleRoot),
|
||||
@ -468,7 +490,6 @@ MerkleBlock.prototype.fromJSON = function fromJSON(json) {
|
||||
var i, hash;
|
||||
|
||||
assert(json, 'MerkleBlock data is required.');
|
||||
assert.equal(json.type, 'merkleblock');
|
||||
assert(Array.isArray(json.hashes));
|
||||
assert(typeof json.flags === 'string');
|
||||
|
||||
@ -640,7 +661,6 @@ MerkleBlock.fromMatches = function fromMatches(block, matches) {
|
||||
merkle.bits = block.bits;
|
||||
merkle.nonce = block.nonce;
|
||||
merkle.totalTX = totalTX;
|
||||
merkle.height = block.height;
|
||||
merkle.hashes = hashes;
|
||||
merkle.flags = flags;
|
||||
merkle.txs = txs;
|
||||
|
||||
@ -33,7 +33,6 @@ var encoding = require('../utils/encoding');
|
||||
* @constructor
|
||||
* @param {Object} options
|
||||
* @param {Number?} options.version
|
||||
* @param {Number?} options.ps
|
||||
* @param {Number?} options.changeIndex
|
||||
* @param {Input[]?} options.inputs
|
||||
* @param {Output[]?} options.outputs
|
||||
@ -45,16 +44,6 @@ var encoding = require('../utils/encoding');
|
||||
* @property {Input[]} inputs
|
||||
* @property {Output[]} outputs
|
||||
* @property {Number} locktime - nLockTime
|
||||
* @property {Number} ts - Timestamp of the block the transaction
|
||||
* was included in (unix time).
|
||||
* @property {Hash|null} block - Hash of the block the transaction
|
||||
* was included in.
|
||||
* @property {Number} index - Transaction's index in the block tx vector.
|
||||
* @property {Number} ps - "Pending Since": The time at which the transaction
|
||||
* was first seen. Only non-zero on unconfirmed transactions.
|
||||
* @property {Number} changeIndex - Index of the change output (-1 if unknown).
|
||||
* @property {Number} height - Height of the block the
|
||||
* transaction was included in (-1 if unconfirmed).
|
||||
* @property {CoinView} view
|
||||
*/
|
||||
|
||||
@ -110,11 +99,6 @@ MTX.prototype.fromOptions = function fromOptions(options) {
|
||||
this.locktime = options.locktime;
|
||||
}
|
||||
|
||||
if (options.ps != null) {
|
||||
assert(util.isNumber(options.ps));
|
||||
this.ps = options.ps;
|
||||
}
|
||||
|
||||
if (options.changeIndex != null) {
|
||||
assert(util.isNumber(options.changeIndex));
|
||||
this.changeIndex = options.changeIndex;
|
||||
|
||||
@ -49,15 +49,6 @@ var BAD_NONSTD_P2WSH = 3;
|
||||
* @property {Input[]} inputs
|
||||
* @property {Output[]} outputs
|
||||
* @property {Number} locktime - nLockTime
|
||||
* @property {Number} ts - Timestamp of the block the transaction
|
||||
* was included in (unix time).
|
||||
* @property {Hash|null} block - Hash of the block the transaction
|
||||
* was included in.
|
||||
* @property {Number} index - Transaction's index in the block tx vector.
|
||||
* @property {Number} ps - "Pending Since": The time at which the transaction
|
||||
* was first seen. Only non-zero on unconfirmed transactions.
|
||||
* @property {Number} height - Height of the block the
|
||||
* transaction was included in (-1 if unconfirmed).
|
||||
*/
|
||||
|
||||
function TX(options) {
|
||||
@ -69,12 +60,6 @@ function TX(options) {
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
this.locktime = 0;
|
||||
|
||||
this.ts = 0;
|
||||
this.block = null;
|
||||
this.index = -1;
|
||||
this.ps = util.now();
|
||||
this.height = -1;
|
||||
this.mutable = false;
|
||||
|
||||
this._hash = null;
|
||||
@ -133,30 +118,6 @@ TX.prototype.fromOptions = function fromOptions(options) {
|
||||
this.locktime = options.locktime;
|
||||
}
|
||||
|
||||
if (options.ts != null)
|
||||
assert(util.isNumber(options.locktime));
|
||||
this.ts = options.ts;
|
||||
|
||||
if (options.block !== undefined) {
|
||||
assert(options.block === null || typeof options.block === 'string');
|
||||
this.block = options.block;
|
||||
}
|
||||
|
||||
if (options.index != null) {
|
||||
assert(util.isNumber(options.index));
|
||||
this.index = options.index;
|
||||
}
|
||||
|
||||
if (options.ps != null) {
|
||||
assert(util.isNumber(options.ps));
|
||||
this.ps = options.ps;
|
||||
}
|
||||
|
||||
if (options.height != null) {
|
||||
assert(util.isNumber(options.height));
|
||||
this.height = options.height;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
@ -179,30 +140,6 @@ TX.prototype.clone = function clone() {
|
||||
return new TX(this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the block the transaction was included in.
|
||||
* @param {Block|MerkleBlock} block
|
||||
* @param {Number} index
|
||||
*/
|
||||
|
||||
TX.prototype.setBlock = function setBlock(block, index) {
|
||||
this.ts = block.ts;
|
||||
this.block = block.hash('hex');
|
||||
this.height = block.height;
|
||||
this.index = index == null ? -1 : index;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove all relevant block data from the transaction.
|
||||
*/
|
||||
|
||||
TX.prototype.unsetBlock = function unsetBlock() {
|
||||
this.ts = 0;
|
||||
this.block = null;
|
||||
this.height = -1;
|
||||
this.index = -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Hash the transaction with the non-witness serialization.
|
||||
* @param {String?} enc - Can be `'hex'` or `null`.
|
||||
@ -1852,25 +1789,6 @@ TX.prototype.getRate = function getRate(view, size) {
|
||||
return btcutils.getRate(size, this.getFee(view));
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate current number of transaction confirmations.
|
||||
* @param {Number?} height - Current chain height. If not
|
||||
* present, network chain height will be used.
|
||||
* @returns {Number} confirmations
|
||||
*/
|
||||
|
||||
TX.prototype.getConfirmations = function getConfirmations(height) {
|
||||
assert(typeof height === 'number', 'Must pass in height.');
|
||||
|
||||
if (this.height === -1)
|
||||
return 0;
|
||||
|
||||
if (height < this.height)
|
||||
return 0;
|
||||
|
||||
return height - this.height + 1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all unique outpoint hashes.
|
||||
* @returns {Hash[]} Outpoint hashes.
|
||||
@ -1955,17 +1873,6 @@ TX.prototype.isWatched = function isWatched(filter) {
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get little-endian block hash.
|
||||
* @returns {Hash|null}
|
||||
*/
|
||||
|
||||
TX.prototype.rblock = function() {
|
||||
return this.block
|
||||
? util.revHex(this.block)
|
||||
: null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get little-endian tx hash.
|
||||
* @returns {Hash}
|
||||
@ -2025,12 +1932,18 @@ TX.prototype.inspect = function inspect() {
|
||||
* Inspect the transaction and return a more
|
||||
* user-friendly representation of the data.
|
||||
* @param {CoinView} view
|
||||
* @param {ChainEntry} entry
|
||||
* @param {Number} index
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
TX.prototype.format = function format(view) {
|
||||
TX.prototype.format = function format(view, entry, index) {
|
||||
var rate = 0;
|
||||
var fee = 0;
|
||||
var height = -1;
|
||||
var block = null;
|
||||
var ts = 0;
|
||||
var date = null;
|
||||
|
||||
if (view) {
|
||||
fee = this.getFee(view);
|
||||
@ -2041,21 +1954,30 @@ TX.prototype.format = function format(view) {
|
||||
rate = 0;
|
||||
}
|
||||
|
||||
if (entry) {
|
||||
height = entry.height;
|
||||
block = util.revHex(entry.hash);
|
||||
ts = entry.ts;
|
||||
date = util.date(ts);
|
||||
}
|
||||
|
||||
if (index == null)
|
||||
index = -1;
|
||||
|
||||
return {
|
||||
hash: this.txid(),
|
||||
witnessHash: this.wtxid(),
|
||||
size: this.getSize(),
|
||||
virtualSize: this.getVirtualSize(),
|
||||
height: this.height,
|
||||
value: Amount.btc(this.getOutputValue()),
|
||||
fee: Amount.btc(fee),
|
||||
rate: Amount.btc(rate),
|
||||
minFee: Amount.btc(this.getMinFee()),
|
||||
date: util.date(this.ts || this.ps),
|
||||
block: this.block ? util.revHex(this.block) : null,
|
||||
ts: this.ts,
|
||||
ps: this.ps,
|
||||
index: this.index,
|
||||
height: height,
|
||||
block: block,
|
||||
ts: ts,
|
||||
date: date,
|
||||
index: index,
|
||||
version: this.version,
|
||||
flag: this.flag,
|
||||
inputs: this.inputs.map(function(input) {
|
||||
@ -2084,12 +2006,13 @@ TX.prototype.toJSON = function toJSON() {
|
||||
* of little-endian uint256s.
|
||||
* @param {Network} network
|
||||
* @param {CoinView} view
|
||||
* @param {ChainEntry} entry
|
||||
* @param {Number} index
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
TX.prototype.getJSON = function getJSON(network, view) {
|
||||
var rate = 0;
|
||||
var fee = 0;
|
||||
TX.prototype.getJSON = function getJSON(network, view, entry, index) {
|
||||
var rate, fee, height, block, ts, date;
|
||||
|
||||
if (view) {
|
||||
fee = this.getFee(view);
|
||||
@ -2098,6 +2021,16 @@ TX.prototype.getJSON = function getJSON(network, view) {
|
||||
// Rate can exceed 53 bits in testing.
|
||||
if (!util.isSafeInteger(rate))
|
||||
rate = 0;
|
||||
|
||||
fee = Amount.btc(fee);
|
||||
rate = Amount.btc(rate);
|
||||
}
|
||||
|
||||
if (entry) {
|
||||
height = entry.height;
|
||||
block = util.revHex(entry.hash);
|
||||
ts = entry.ts;
|
||||
date = util.date(ts);
|
||||
}
|
||||
|
||||
network = Network.get(network);
|
||||
@ -2105,14 +2038,14 @@ TX.prototype.getJSON = function getJSON(network, view) {
|
||||
return {
|
||||
hash: this.txid(),
|
||||
witnessHash: this.wtxid(),
|
||||
height: this.height,
|
||||
block: this.rblock(),
|
||||
ts: this.ts,
|
||||
ps: this.ps,
|
||||
date: util.date(this.ts || this.ps),
|
||||
index: this.index,
|
||||
fee: Amount.btc(fee),
|
||||
rate: Amount.btc(rate),
|
||||
fee: fee,
|
||||
rate: rate,
|
||||
ps: util.now(),
|
||||
height: height,
|
||||
block: block,
|
||||
ts: ts,
|
||||
date: date,
|
||||
index: index,
|
||||
version: this.version,
|
||||
flag: this.flag,
|
||||
inputs: this.inputs.map(function(input) {
|
||||
@ -2136,23 +2069,11 @@ TX.prototype.fromJSON = function fromJSON(json) {
|
||||
var i, input, output;
|
||||
|
||||
assert(json, 'TX data is required.');
|
||||
assert.equal(json.type, 'tx');
|
||||
assert(util.isNumber(json.version));
|
||||
assert(util.isNumber(json.flag));
|
||||
assert(Array.isArray(json.inputs));
|
||||
assert(Array.isArray(json.outputs));
|
||||
assert(util.isNumber(json.locktime));
|
||||
assert(!json.block || typeof json.block === 'string');
|
||||
assert(util.isNumber(json.height));
|
||||
assert(util.isNumber(json.ts));
|
||||
assert(util.isNumber(json.ps));
|
||||
assert(util.isNumber(json.index));
|
||||
|
||||
this.block = json.block ? util.revHex(json.block) : null;
|
||||
this.height = json.height;
|
||||
this.ts = json.ts;
|
||||
this.ps = json.ps;
|
||||
this.index = json.index;
|
||||
|
||||
this.version = json.version;
|
||||
|
||||
@ -2455,87 +2376,6 @@ TX.isWitness = function isWitness(br) {
|
||||
&& br.data[br.offset + 5] !== 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Serialize a transaction to BCoin "extended format".
|
||||
* This is the serialization format BCoin uses internally
|
||||
* to store transactions in the database. The extended
|
||||
* serialization includes the height, block hash, index,
|
||||
* timestamp, and pending-since time.
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
TX.prototype.toExtended = function toExtended() {
|
||||
var bw = new BufferWriter();
|
||||
var height = this.height;
|
||||
var index = this.index;
|
||||
|
||||
if (height === -1)
|
||||
height = 0x7fffffff;
|
||||
|
||||
if (index === -1)
|
||||
index = 0x7fffffff;
|
||||
|
||||
this.toWriter(bw);
|
||||
|
||||
bw.writeU32(this.ps);
|
||||
|
||||
if (this.block) {
|
||||
bw.writeU8(1);
|
||||
bw.writeHash(this.block);
|
||||
} else {
|
||||
bw.writeU8(0);
|
||||
}
|
||||
|
||||
bw.writeU32(height);
|
||||
bw.writeU32(this.ts);
|
||||
bw.writeU32(index);
|
||||
|
||||
return bw.render();
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject properties from "extended" serialization format.
|
||||
* @private
|
||||
* @param {Buffer} data
|
||||
*/
|
||||
|
||||
TX.prototype.fromExtended = function fromExtended(data) {
|
||||
var br = new BufferReader(data);
|
||||
|
||||
this.fromReader(br);
|
||||
|
||||
this.ps = br.readU32();
|
||||
|
||||
if (br.readU8() === 1)
|
||||
this.block = br.readHash('hex');
|
||||
|
||||
this.height = br.readU32();
|
||||
this.ts = br.readU32();
|
||||
this.index = br.readU32();
|
||||
|
||||
if (this.height === 0x7fffffff)
|
||||
this.height = -1;
|
||||
|
||||
if (this.index === 0x7fffffff)
|
||||
this.index = -1;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate a transaction from a Buffer
|
||||
* in "extended" serialization format.
|
||||
* @param {Buffer} data
|
||||
* @param {String?} enc - One of `"hex"` or `null`.
|
||||
* @returns {TX}
|
||||
*/
|
||||
|
||||
TX.fromExtended = function fromExtended(data, enc) {
|
||||
if (typeof data === 'string')
|
||||
data = new Buffer(data, enc);
|
||||
return new TX().fromExtended(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether an object is a TX.
|
||||
* @param {Object} obj
|
||||
|
||||
280
lib/primitives/txmeta.js
Normal file
280
lib/primitives/txmeta.js
Normal file
@ -0,0 +1,280 @@
|
||||
/*!
|
||||
* txmeta.js - extended transaction object for bcoin
|
||||
* Copyright (c) 2014-2016, Christopher Jeffrey (MIT License).
|
||||
* https://github.com/bcoin-org/bcoin
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var util = require('../utils/util');
|
||||
var TX = require('./tx');
|
||||
var BufferWriter = require('../utils/writer');
|
||||
var BufferReader = require('../utils/reader');
|
||||
|
||||
/**
|
||||
* An extended transaction object.
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
function TXMeta(options) {
|
||||
if (!(this instanceof TXMeta))
|
||||
return new TXMeta(options);
|
||||
|
||||
this.tx = new TX();
|
||||
this.ps = util.now();
|
||||
this.height = -1;
|
||||
this.block = null;
|
||||
this.ts = 0;
|
||||
this.index = 0;
|
||||
|
||||
if (options)
|
||||
this.fromOptions(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject properties from options object.
|
||||
* @private
|
||||
* @param {Object} options
|
||||
*/
|
||||
|
||||
TXMeta.prototype.fromOptions = function fromOptions(options) {
|
||||
if (options.tx) {
|
||||
assert(options.tx instanceof TX);
|
||||
this.tx = options.tx;
|
||||
}
|
||||
|
||||
if (options.ps != null) {
|
||||
assert(util.isNumber(options.ps));
|
||||
this.ps = options.ps;
|
||||
}
|
||||
|
||||
if (options.height != null) {
|
||||
assert(util.isNumber(options.height));
|
||||
this.height = options.height;
|
||||
}
|
||||
|
||||
if (options.block !== undefined) {
|
||||
assert(options.block === null || typeof options.block === 'string');
|
||||
this.block = options.block;
|
||||
}
|
||||
|
||||
if (options.ts != null) {
|
||||
assert(util.isNumber(options.ts));
|
||||
this.ts = options.ts;
|
||||
}
|
||||
|
||||
if (options.index != null) {
|
||||
assert(util.isNumber(options.index));
|
||||
this.index = options.index;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate TXMeta from options.
|
||||
* @param {Object} options
|
||||
* @returns {TXMeta}
|
||||
*/
|
||||
|
||||
TXMeta.fromOptions = function fromOptions(options) {
|
||||
return new TXMeta().fromOptions(options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject properties from options object.
|
||||
* @private
|
||||
* @param {Object} options
|
||||
*/
|
||||
|
||||
TXMeta.prototype.fromTX = function fromTX(tx, entry, index) {
|
||||
this.tx = tx;
|
||||
if (entry) {
|
||||
this.height = entry.height;
|
||||
this.block = entry.hash;
|
||||
this.ts = entry.ts;
|
||||
this.index = index;
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate TXMeta from options.
|
||||
* @param {Object} options
|
||||
* @returns {TXMeta}
|
||||
*/
|
||||
|
||||
TXMeta.fromTX = function fromTX(tx, entry, index) {
|
||||
return new TXMeta().fromTX(tx, entry, index);
|
||||
};
|
||||
|
||||
/**
|
||||
* Inspect the transaction.
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
TXMeta.prototype.inspect = function inspect() {
|
||||
return this.format();
|
||||
};
|
||||
|
||||
/**
|
||||
* Inspect the transaction.
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
TXMeta.prototype.format = function format(view) {
|
||||
var data = this.tx.format(view, null, this.index);
|
||||
data.ps = this.ps;
|
||||
data.height = this.height;
|
||||
data.block = this.block ? util.revHex(this.block) : null;
|
||||
data.ts = this.ts;
|
||||
return data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert transaction to JSON.
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
TXMeta.prototype.toJSON = function toJSON() {
|
||||
return this.getJSON();
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert the transaction to an object suitable
|
||||
* for JSON serialization.
|
||||
* @param {Network} network
|
||||
* @param {CoinView} view
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
TXMeta.prototype.getJSON = function getJSON(network, view) {
|
||||
var json = this.tx.getJSON(network, view, null, this.index);
|
||||
json.ps = this.ps;
|
||||
json.height = this.height;
|
||||
json.block = this.block ? util.revHex(this.block) : null;
|
||||
json.ts = this.ts;
|
||||
return json;
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject properties from a json object.
|
||||
* @private
|
||||
* @param {Object} json
|
||||
*/
|
||||
|
||||
TXMeta.prototype.fromJSON = function fromJSON(json) {
|
||||
this.tx.fromJSON(json);
|
||||
|
||||
assert(util.isNumber(json.ps));
|
||||
assert(util.isNumber(json.height));
|
||||
assert(!json.block || typeof json.block === 'string');
|
||||
assert(util.isNumber(json.ts));
|
||||
assert(util.isNumber(json.index));
|
||||
|
||||
this.ps = json.ps;
|
||||
this.height = json.height;
|
||||
this.block = util.revHex(json.block);
|
||||
this.index = json.index;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate a transaction from a
|
||||
* jsonified transaction object.
|
||||
* @param {Object} json - The jsonified transaction object.
|
||||
* @returns {TX}
|
||||
*/
|
||||
|
||||
TXMeta.fromJSON = function fromJSON(json) {
|
||||
return new TXMeta().fromJSON(JSON);
|
||||
};
|
||||
|
||||
/**
|
||||
* Serialize a transaction to BCoin "extended format".
|
||||
* This is the serialization format BCoin uses internally
|
||||
* to store transactions in the database. The extended
|
||||
* serialization includes the height, block hash, index,
|
||||
* timestamp, and pending-since time.
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
TXMeta.prototype.toRaw = function toRaw() {
|
||||
var bw = new BufferWriter();
|
||||
|
||||
this.tx.toWriter(bw);
|
||||
|
||||
bw.writeU32(this.ps);
|
||||
|
||||
if (this.block) {
|
||||
bw.writeU8(1);
|
||||
bw.writeHash(this.block);
|
||||
bw.writeU32(this.height);
|
||||
bw.writeU32(this.ts);
|
||||
bw.writeU32(this.index);
|
||||
} else {
|
||||
bw.writeU8(0);
|
||||
}
|
||||
|
||||
return bw.render();
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject properties from "extended" serialization format.
|
||||
* @private
|
||||
* @param {Buffer} data
|
||||
*/
|
||||
|
||||
TXMeta.prototype.fromRaw = function fromRaw(data) {
|
||||
var br = new BufferReader(data);
|
||||
|
||||
this.tx.fromReader(br);
|
||||
|
||||
this.ps = br.readU32();
|
||||
|
||||
if (br.readU8() === 1) {
|
||||
this.block = br.readHash('hex');
|
||||
this.height = br.readU32();
|
||||
this.ts = br.readU32();
|
||||
this.index = br.readU32();
|
||||
if (this.index === 0x7fffffff)
|
||||
this.index = -1;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate a transaction from a Buffer
|
||||
* in "extended" serialization format.
|
||||
* @param {Buffer} data
|
||||
* @param {String?} enc - One of `"hex"` or `null`.
|
||||
* @returns {TX}
|
||||
*/
|
||||
|
||||
TXMeta.fromRaw = function fromRaw(data, enc) {
|
||||
if (typeof data === 'string')
|
||||
data = new Buffer(data, enc);
|
||||
return new TXMeta().fromRaw(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether an object is an TXMeta.
|
||||
* @param {Object} obj
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
TXMeta.isTXMeta = function isTXMeta(obj) {
|
||||
return obj
|
||||
&& Array.isArray(obj.inputs)
|
||||
&& typeof obj.locktime === 'number'
|
||||
&& typeof obj.ps === 'number';
|
||||
};
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
module.exports = TXMeta;
|
||||
@ -335,7 +335,7 @@ WalletClient.prototype.rescan = function rescan(start) {
|
||||
*/
|
||||
|
||||
function parseEntry(data, enc) {
|
||||
var br, block, hash;
|
||||
var br, block, hash, height;
|
||||
|
||||
if (typeof data === 'string')
|
||||
data = new Buffer(data, 'hex');
|
||||
@ -343,11 +343,10 @@ function parseEntry(data, enc) {
|
||||
br = new BufferReader(data);
|
||||
|
||||
block = Headers.fromAbbr(br);
|
||||
block.height = br.readU32();
|
||||
|
||||
height = br.readU32();
|
||||
hash = block.hash('hex');
|
||||
|
||||
return new BlockMeta(hash, block.height, block.ts);
|
||||
return new BlockMeta(hash, height, block.ts);
|
||||
}
|
||||
|
||||
function parseBlock(entry, txs) {
|
||||
@ -358,10 +357,6 @@ function parseBlock(entry, txs) {
|
||||
for (i = 0; i < txs.length; i++) {
|
||||
tx = txs[i];
|
||||
tx = parseTX(tx);
|
||||
tx.block = block.hash;
|
||||
tx.height = block.height;
|
||||
tx.ts = block.ts;
|
||||
tx.index = -1;
|
||||
out.push(tx);
|
||||
}
|
||||
|
||||
|
||||
@ -6,11 +6,12 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var util = require('../utils/util');
|
||||
var assert = require('assert');
|
||||
var util = require('../utils/util');
|
||||
var constants = require('../protocol/constants');
|
||||
var BufferReader = require('../utils/reader');
|
||||
var BufferWriter = require('../utils/writer');
|
||||
var TX = require('../primitives/tx');
|
||||
|
||||
/**
|
||||
* Chain State
|
||||
@ -470,6 +471,150 @@ PathMapRecord.fromRaw = function fromRaw(hash, data) {
|
||||
return new PathMapRecord(hash).fromRaw(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* TXRecord
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
function TXRecord(tx, block) {
|
||||
if (!(this instanceof TXRecord))
|
||||
return new TXRecord(tx, block);
|
||||
|
||||
this.tx = null;
|
||||
this.hash = null;
|
||||
this.ps = util.now();
|
||||
this.height = -1;
|
||||
this.block = null;
|
||||
this.index = -1;
|
||||
this.ts = 0;
|
||||
|
||||
if (tx)
|
||||
this.fromTX(tx, block);
|
||||
}
|
||||
|
||||
TXRecord.prototype.fromTX = function fromTX(tx, block) {
|
||||
this.tx = tx;
|
||||
this.hash = tx.hash('hex');
|
||||
|
||||
if (block)
|
||||
this.setBlock(block);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
TXRecord.fromTX = function fromTX(tx, block) {
|
||||
return new TXRecord().fromTX(tx, block);
|
||||
};
|
||||
|
||||
TXRecord.prototype.setBlock = function setBlock(block) {
|
||||
this.height = block.height;
|
||||
this.block = block.hash;
|
||||
this.ts = block.ts;
|
||||
};
|
||||
|
||||
TXRecord.prototype.unsetBlock = function unsetBlock() {
|
||||
this.height = -1;
|
||||
this.block = null;
|
||||
this.ts = 0;
|
||||
};
|
||||
|
||||
TXRecord.prototype.getBlock = function getBlock() {
|
||||
if (this.height === -1)
|
||||
return;
|
||||
return new BlockMeta(this.hash, this.height, this.ts);
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate current number of transaction confirmations.
|
||||
* @param {Number?} height - Current chain height. If not
|
||||
* present, network chain height will be used.
|
||||
* @returns {Number} confirmations
|
||||
*/
|
||||
|
||||
TXRecord.prototype.getConfirmations = function getConfirmations(height) {
|
||||
assert(typeof height === 'number', 'Must pass in height.');
|
||||
|
||||
if (this.height === -1)
|
||||
return 0;
|
||||
|
||||
if (height < this.height)
|
||||
return 0;
|
||||
|
||||
return height - this.height + 1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Serialize a transaction to BCoin "extended format".
|
||||
* This is the serialization format BCoin uses internally
|
||||
* to store transactions in the database. The extended
|
||||
* serialization includes the height, block hash, index,
|
||||
* timestamp, and pending-since time.
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
TXRecord.prototype.toRaw = function toRaw() {
|
||||
var bw = new BufferWriter();
|
||||
var index = this.index;
|
||||
|
||||
this.tx.toWriter(bw);
|
||||
|
||||
bw.writeU32(this.ps);
|
||||
|
||||
if (this.block) {
|
||||
if (index === -1)
|
||||
index = 0x7fffffff;
|
||||
|
||||
bw.writeU8(1);
|
||||
bw.writeHash(this.block);
|
||||
bw.writeU32(this.height);
|
||||
bw.writeU32(this.ts);
|
||||
bw.writeU32(index);
|
||||
} else {
|
||||
bw.writeU8(0);
|
||||
}
|
||||
|
||||
return bw.render();
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject properties from "extended" serialization format.
|
||||
* @private
|
||||
* @param {Buffer} data
|
||||
*/
|
||||
|
||||
TXRecord.prototype.fromRaw = function fromRaw(data) {
|
||||
var br = new BufferReader(data);
|
||||
|
||||
this.tx = new TX();
|
||||
this.tx.fromReader(br);
|
||||
this.hash = this.tx.hash('hex');
|
||||
|
||||
this.ps = br.readU32();
|
||||
|
||||
if (br.readU8() === 1) {
|
||||
this.block = br.readHash('hex');
|
||||
this.height = br.readU32();
|
||||
this.ts = br.readU32();
|
||||
this.index = br.readU32();
|
||||
if (this.index === 0x7fffffff)
|
||||
this.index = -1;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate a transaction from a Buffer
|
||||
* in "extended" serialization format.
|
||||
* @param {Buffer} data
|
||||
* @param {String?} enc - One of `"hex"` or `null`.
|
||||
* @returns {TX}
|
||||
*/
|
||||
|
||||
TXRecord.fromRaw = function fromRaw(data) {
|
||||
return new TXRecord().fromRaw(data);
|
||||
};
|
||||
|
||||
/*
|
||||
* Helpers
|
||||
*/
|
||||
@ -516,5 +661,6 @@ exports.BlockMapRecord = BlockMapRecord;
|
||||
exports.TXMapRecord = TXMapRecord;
|
||||
exports.OutpointMapRecord = OutpointMapRecord;
|
||||
exports.PathMapRecord = PathMapRecord;
|
||||
exports.TXRecord = TXRecord;
|
||||
|
||||
module.exports = exports;
|
||||
|
||||
@ -16,13 +16,14 @@ var BufferReader = require('../utils/reader');
|
||||
var BufferWriter = require('../utils/writer');
|
||||
var btcutils = require('../btc/utils');
|
||||
var Amount = require('../btc/amount');
|
||||
var TX = require('../primitives/tx');
|
||||
var CoinView = require('../coins/coinview');
|
||||
var Coin = require('../primitives/coin');
|
||||
var Outpoint = require('../primitives/outpoint');
|
||||
var records = require('./records');
|
||||
var layout = require('./layout').txdb;
|
||||
var BlockMapRecord = records.BlockMapRecord;
|
||||
var OutpointMapRecord = records.OutpointMapRecord;
|
||||
var TXRecord = records.TXRecord;
|
||||
var DUMMY = new Buffer([0]);
|
||||
|
||||
/**
|
||||
@ -345,7 +346,7 @@ TXDB.prototype.verifyInputs = co(function* verifyInputs(tx, block) {
|
||||
coin = yield this.getCoin(prevout.hash, prevout.index);
|
||||
|
||||
if (coin) {
|
||||
if (this.options.verify && tx.height === -1) {
|
||||
if (this.options.verify && !block) {
|
||||
if (!(yield tx.verifyInputAsync(i, coin, flags)))
|
||||
return false;
|
||||
}
|
||||
@ -364,7 +365,7 @@ TXDB.prototype.verifyInputs = co(function* verifyInputs(tx, block) {
|
||||
// decided to index for the hell
|
||||
// of it.
|
||||
if (coin) {
|
||||
if (this.options.verify && tx.height === -1) {
|
||||
if (this.options.verify && !block) {
|
||||
if (!(yield tx.verifyInputAsync(i, coin, flags)))
|
||||
return false;
|
||||
}
|
||||
@ -476,7 +477,7 @@ TXDB.prototype.resolveOutputs = co(function* resolveOutputs(tx, block, resolved)
|
||||
assert(input.prevout.index === i);
|
||||
|
||||
// We can finally verify this input.
|
||||
if (this.options.verify && orphan.tx.height === -1)
|
||||
if (this.options.verify && !orphan.block)
|
||||
valid = yield orphan.tx.verifyInputAsync(orphan.index, output, flags);
|
||||
|
||||
// If it's valid and fully resolved,
|
||||
@ -588,11 +589,12 @@ TXDB.prototype.removeInput = function removeInput(tx, index) {
|
||||
* Resolve orphan input.
|
||||
* @param {TX} tx
|
||||
* @param {Number} index
|
||||
* @param {Number} height
|
||||
* @param {Path} path
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
TXDB.prototype.resolveInput = co(function* resolveInput(tx, index, path) {
|
||||
TXDB.prototype.resolveInput = co(function* resolveInput(tx, index, height, path) {
|
||||
var hash = tx.hash('hex');
|
||||
var spent = yield this.getSpent(hash, index);
|
||||
var stx, credit;
|
||||
@ -611,9 +613,9 @@ TXDB.prototype.resolveInput = co(function* resolveInput(tx, index, path) {
|
||||
assert(stx);
|
||||
|
||||
// Crete the credit and add the undo coin.
|
||||
credit = Credit.fromTX(tx, index, tx.height);
|
||||
credit = Credit.fromTX(tx, index, height);
|
||||
|
||||
this.spendCredit(credit, stx, spent.index);
|
||||
this.spendCredit(credit, stx.tx, spent.index);
|
||||
|
||||
// If the spender is unconfirmed, save
|
||||
// the credit as well, and mark it as
|
||||
@ -624,7 +626,7 @@ TXDB.prototype.resolveInput = co(function* resolveInput(tx, index, path) {
|
||||
if (stx.height === -1) {
|
||||
credit.spent = true;
|
||||
yield this.saveCredit(credit, path);
|
||||
if (tx.height !== -1)
|
||||
if (height !== -1)
|
||||
this.pending.confirmed += credit.coin.value;
|
||||
}
|
||||
|
||||
@ -747,13 +749,12 @@ TXDB.prototype.removeOutpointMap = co(function* removeOutpointMap(hash, i) {
|
||||
|
||||
/**
|
||||
* Append to the global block record.
|
||||
* @param {TX} tx
|
||||
* @param {Hash} hash
|
||||
* @param {Number} height
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
TXDB.prototype.addBlockMap = co(function* addBlockMap(tx, height) {
|
||||
var hash = tx.hash('hex');
|
||||
TXDB.prototype.addBlockMap = co(function* addBlockMap(hash, height) {
|
||||
var block = yield this.walletdb.getBlockMap(height);
|
||||
|
||||
if (!block)
|
||||
@ -767,13 +768,12 @@ TXDB.prototype.addBlockMap = co(function* addBlockMap(tx, height) {
|
||||
|
||||
/**
|
||||
* Remove from the global block record.
|
||||
* @param {TX}
|
||||
* @param {Hash} hash
|
||||
* @param {Number} height
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
TXDB.prototype.removeBlockMap = co(function* removeBlockMap(tx, height) {
|
||||
var hash = tx.hash('hex');
|
||||
TXDB.prototype.removeBlockMap = co(function* removeBlockMap(hash, height) {
|
||||
var block = yield this.walletdb.getBlockMap(height);
|
||||
|
||||
if (!block)
|
||||
@ -820,20 +820,18 @@ TXDB.prototype.getBlock = co(function* getBlock(height) {
|
||||
|
||||
/**
|
||||
* Append to the global block record.
|
||||
* @param {TX} tx
|
||||
* @param {BlockMeta} entry
|
||||
* @param {Hash} hash
|
||||
* @param {BlockMeta} meta
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
TXDB.prototype.addBlock = co(function* addBlock(tx, entry) {
|
||||
var hash = tx.hash();
|
||||
var height = tx.height;
|
||||
var key = layout.b(height);
|
||||
TXDB.prototype.addBlock = co(function* addBlock(hash, meta) {
|
||||
var key = layout.b(meta.height);
|
||||
var data = yield this.get(key);
|
||||
var block, size;
|
||||
|
||||
if (!data) {
|
||||
block = new BlockRecord(tx.block, tx.height, tx.ts);
|
||||
block = BlockRecord.fromMeta(meta);
|
||||
data = block.toRaw();
|
||||
}
|
||||
|
||||
@ -849,13 +847,12 @@ TXDB.prototype.addBlock = co(function* addBlock(tx, entry) {
|
||||
|
||||
/**
|
||||
* Remove from the global block record.
|
||||
* @param {TX} tx
|
||||
* @param {Hash} hash
|
||||
* @param {Number} height
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
TXDB.prototype.removeBlock = co(function* removeBlock(tx, height) {
|
||||
var hash = tx.hash();
|
||||
TXDB.prototype.removeBlock = co(function* removeBlock(hash, height) {
|
||||
var key = layout.b(height);
|
||||
var data = yield this.get(key);
|
||||
var block, size;
|
||||
@ -881,34 +878,31 @@ TXDB.prototype.removeBlock = co(function* removeBlock(tx, height) {
|
||||
|
||||
/**
|
||||
* Append to the global block record.
|
||||
* @param {TX} tx
|
||||
* @param {BlockMeta} entry
|
||||
* @param {Hash} hash
|
||||
* @param {BlockMeta} meta
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
TXDB.prototype.addBlockSlow = co(function* addBlock(tx, entry) {
|
||||
var hash = tx.hash('hex');
|
||||
var height = tx.height;
|
||||
var block = yield this.getBlock(height);
|
||||
TXDB.prototype.addBlockSlow = co(function* addBlockSlow(hash, meta) {
|
||||
var block = yield this.getBlock(meta.height);
|
||||
|
||||
if (!block)
|
||||
block = new BlockRecord(tx.block, tx.height, tx.ts);
|
||||
block = BlockRecord.fromMeta(meta);
|
||||
|
||||
if (!block.add(hash))
|
||||
return;
|
||||
|
||||
this.put(layout.b(height), block.toRaw());
|
||||
this.put(layout.b(meta.height), block.toRaw());
|
||||
});
|
||||
|
||||
/**
|
||||
* Remove from the global block record.
|
||||
* @param {TX} tx
|
||||
* @param {Hash} hash
|
||||
* @param {Number} height
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
TXDB.prototype.removeBlockSlow = co(function* removeBlock(tx, height) {
|
||||
var hash = tx.hash('hex');
|
||||
TXDB.prototype.removeBlockSlow = co(function* removeBlockSlow(hash, height) {
|
||||
var block = yield this.getBlock(height);
|
||||
|
||||
if (!block)
|
||||
@ -960,6 +954,7 @@ TXDB.prototype.add = co(function* add(tx, block) {
|
||||
TXDB.prototype._add = co(function* add(tx, block) {
|
||||
var hash = tx.hash('hex');
|
||||
var existing = yield this.getTX(hash);
|
||||
var wtx;
|
||||
|
||||
assert(!tx.mutable, 'Cannot add mutable TX to wallet.');
|
||||
|
||||
@ -970,20 +965,16 @@ TXDB.prototype._add = co(function* add(tx, block) {
|
||||
|
||||
// The incoming tx won't confirm the
|
||||
// existing one anyway. Ignore.
|
||||
if (tx.height === -1)
|
||||
if (!block)
|
||||
return;
|
||||
|
||||
// Save the index (can't get this elsewhere).
|
||||
existing.height = tx.height;
|
||||
existing.block = tx.block;
|
||||
existing.ts = tx.ts;
|
||||
existing.index = tx.index;
|
||||
|
||||
// Confirm transaction.
|
||||
return yield this._confirm(existing, block);
|
||||
}
|
||||
|
||||
if (tx.height === -1) {
|
||||
wtx = TXRecord.fromTX(tx, block);
|
||||
|
||||
if (!block) {
|
||||
// We ignore any unconfirmed txs
|
||||
// that are replace-by-fee.
|
||||
if (yield this.isRBF(tx)) {
|
||||
@ -1007,20 +998,22 @@ TXDB.prototype._add = co(function* add(tx, block) {
|
||||
}
|
||||
|
||||
// Finally we can do a regular insertion.
|
||||
return yield this.insert(tx, block);
|
||||
return yield this.insert(wtx, block);
|
||||
});
|
||||
|
||||
/**
|
||||
* Insert transaction.
|
||||
* @private
|
||||
* @param {TX} tx
|
||||
* @param {TXRecord} wtx
|
||||
* @param {BlockMeta} block
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
TXDB.prototype.insert = co(function* insert(tx, block) {
|
||||
var hash = tx.hash('hex');
|
||||
var details = new Details(this, tx);
|
||||
TXDB.prototype.insert = co(function* insert(wtx, block) {
|
||||
var tx = wtx.tx;
|
||||
var hash = wtx.hash;
|
||||
var height = block ? block.height : -1;
|
||||
var details = new Details(this, wtx, block);
|
||||
var updated = false;
|
||||
var i, input, output, coin;
|
||||
var prevout, credit, path, account;
|
||||
@ -1067,7 +1060,7 @@ TXDB.prototype.insert = co(function* insert(tx, block) {
|
||||
this.pending.coin--;
|
||||
this.pending.unconfirmed -= coin.value;
|
||||
|
||||
if (tx.height === -1) {
|
||||
if (!block) {
|
||||
// If the tx is not mined, we do not
|
||||
// disconnect the coin, we simply mark
|
||||
// a `spent` flag on the credit. This
|
||||
@ -1104,17 +1097,17 @@ TXDB.prototype.insert = co(function* insert(tx, block) {
|
||||
|
||||
// Attempt to resolve an input we
|
||||
// did not know was ours at the time.
|
||||
if (yield this.resolveInput(tx, i, path)) {
|
||||
if (yield this.resolveInput(tx, i, height, path)) {
|
||||
updated = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
credit = Credit.fromTX(tx, i, tx.height);
|
||||
credit = Credit.fromTX(tx, i, height);
|
||||
|
||||
this.pending.coin++;
|
||||
this.pending.unconfirmed += output.value;
|
||||
|
||||
if (tx.height !== -1)
|
||||
if (block)
|
||||
this.pending.confirmed += output.value;
|
||||
|
||||
yield this.saveCredit(credit, path);
|
||||
@ -1130,15 +1123,14 @@ TXDB.prototype.insert = co(function* insert(tx, block) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Save and index the transaction in bcoin's
|
||||
// own "extended" transaction serialization.
|
||||
this.put(layout.t(hash), tx.toExtended());
|
||||
this.put(layout.m(tx.ps, hash), DUMMY);
|
||||
// Save and index the transaction record.
|
||||
this.put(layout.t(hash), wtx.toRaw());
|
||||
this.put(layout.m(wtx.ps, hash), DUMMY);
|
||||
|
||||
if (tx.height === -1)
|
||||
if (!block)
|
||||
this.put(layout.p(hash), DUMMY);
|
||||
else
|
||||
this.put(layout.h(tx.height, hash), DUMMY);
|
||||
this.put(layout.h(height, hash), DUMMY);
|
||||
|
||||
// Do some secondary indexing for account-based
|
||||
// queries. This saves us a lot of time for
|
||||
@ -1147,17 +1139,17 @@ TXDB.prototype.insert = co(function* insert(tx, block) {
|
||||
account = details.accounts[i];
|
||||
|
||||
this.put(layout.T(account, hash), DUMMY);
|
||||
this.put(layout.M(account, tx.ps, hash), DUMMY);
|
||||
this.put(layout.M(account, wtx.ps, hash), DUMMY);
|
||||
|
||||
if (tx.height === -1)
|
||||
if (!block)
|
||||
this.put(layout.P(account, hash), DUMMY);
|
||||
else
|
||||
this.put(layout.H(account, tx.height, hash), DUMMY);
|
||||
this.put(layout.H(account, height, hash), DUMMY);
|
||||
}
|
||||
|
||||
if (tx.height !== -1) {
|
||||
yield this.addBlockMap(tx, tx.height);
|
||||
yield this.addBlock(tx, block);
|
||||
if (block) {
|
||||
yield this.addBlockMap(hash, height);
|
||||
yield this.addBlock(tx.hash(), block);
|
||||
}
|
||||
|
||||
// Update the transaction counter and
|
||||
@ -1191,25 +1183,21 @@ TXDB.prototype.insert = co(function* insert(tx, block) {
|
||||
*/
|
||||
|
||||
TXDB.prototype.confirm = co(function* confirm(hash, block) {
|
||||
var tx = yield this.getTX(hash);
|
||||
var wtx = yield this.getTX(hash);
|
||||
var details;
|
||||
|
||||
if (!tx)
|
||||
if (!wtx)
|
||||
return;
|
||||
|
||||
if (tx.height !== -1)
|
||||
if (wtx.height !== -1)
|
||||
throw new Error('TX is already confirmed.');
|
||||
|
||||
assert(block);
|
||||
|
||||
tx.height = block.height;
|
||||
tx.block = block.hash;
|
||||
tx.ts = block.ts;
|
||||
|
||||
this.start();
|
||||
|
||||
try {
|
||||
details = yield this._confirm(tx, block);
|
||||
details = yield this._confirm(wtx, block);
|
||||
} catch (e) {
|
||||
this.drop();
|
||||
throw e;
|
||||
@ -1223,17 +1211,21 @@ TXDB.prototype.confirm = co(function* confirm(hash, block) {
|
||||
/**
|
||||
* Attempt to confirm a transaction.
|
||||
* @private
|
||||
* @param {TX} tx
|
||||
* @param {TXRecord} wtx
|
||||
* @param {BlockMeta} block
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
TXDB.prototype._confirm = co(function* confirm(tx, block) {
|
||||
var hash = tx.hash('hex');
|
||||
var details = new Details(this, tx);
|
||||
TXDB.prototype._confirm = co(function* confirm(wtx, block) {
|
||||
var tx = wtx.tx;
|
||||
var hash = wtx.hash;
|
||||
var height = block.height;
|
||||
var details = new Details(this, wtx, block);
|
||||
var i, account, output, coin, input, prevout;
|
||||
var path, credit, credits;
|
||||
|
||||
wtx.setBlock(block);
|
||||
|
||||
if (!tx.isCoinbase()) {
|
||||
credits = yield this.getSpentCredits(tx);
|
||||
|
||||
@ -1300,12 +1292,12 @@ TXDB.prototype._confirm = co(function* confirm(tx, block) {
|
||||
// spent in the mempool, we need to
|
||||
// update the undo coin's height.
|
||||
if (credit.spent)
|
||||
yield this.updateSpentCoin(tx, i);
|
||||
yield this.updateSpentCoin(tx, i, height);
|
||||
|
||||
// Update coin height and confirmed
|
||||
// balance. Save once again.
|
||||
coin = credit.coin;
|
||||
coin.height = tx.height;
|
||||
coin.height = height;
|
||||
|
||||
this.pending.confirmed += output.value;
|
||||
|
||||
@ -1318,20 +1310,20 @@ TXDB.prototype._confirm = co(function* confirm(tx, block) {
|
||||
// Save the new serialized transaction as
|
||||
// the block-related properties have been
|
||||
// updated. Also reindex for height.
|
||||
this.put(layout.t(hash), tx.toExtended());
|
||||
this.put(layout.t(hash), wtx.toRaw());
|
||||
this.del(layout.p(hash));
|
||||
this.put(layout.h(tx.height, hash), DUMMY);
|
||||
this.put(layout.h(height, hash), DUMMY);
|
||||
|
||||
// Secondary indexing also needs to change.
|
||||
for (i = 0; i < details.accounts.length; i++) {
|
||||
account = details.accounts[i];
|
||||
this.del(layout.P(account, hash));
|
||||
this.put(layout.H(account, tx.height, hash), DUMMY);
|
||||
this.put(layout.H(account, height, hash), DUMMY);
|
||||
}
|
||||
|
||||
if (tx.height !== -1) {
|
||||
yield this.addBlockMap(tx, tx.height);
|
||||
yield this.addBlock(tx, block);
|
||||
if (block) {
|
||||
yield this.addBlockMap(hash, height);
|
||||
yield this.addBlock(tx.hash(), block);
|
||||
}
|
||||
|
||||
// Commit the new state. The balance has updated.
|
||||
@ -1353,25 +1345,27 @@ TXDB.prototype._confirm = co(function* confirm(tx, block) {
|
||||
*/
|
||||
|
||||
TXDB.prototype.remove = co(function* remove(hash) {
|
||||
var tx = yield this.getTX(hash);
|
||||
var wtx = yield this.getTX(hash);
|
||||
|
||||
if (!tx)
|
||||
if (!wtx)
|
||||
return;
|
||||
|
||||
return yield this.removeRecursive(tx);
|
||||
return yield this.removeRecursive(wtx);
|
||||
});
|
||||
|
||||
/**
|
||||
* Remove a transaction from the
|
||||
* database. Disconnect inputs.
|
||||
* @private
|
||||
* @param {TX} tx
|
||||
* @param {TXRecord} wtx
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
TXDB.prototype.erase = co(function* erase(tx) {
|
||||
var hash = tx.hash('hex');
|
||||
var details = new Details(this, tx);
|
||||
TXDB.prototype.erase = co(function* erase(wtx, block) {
|
||||
var tx = wtx.tx;
|
||||
var hash = wtx.hash;
|
||||
var height = block ? block.height : -1;
|
||||
var details = new Details(this, wtx, block);
|
||||
var i, path, account, credits;
|
||||
var input, output, coin, credit;
|
||||
|
||||
@ -1405,7 +1399,7 @@ TXDB.prototype.erase = co(function* erase(tx) {
|
||||
this.pending.coin++;
|
||||
this.pending.unconfirmed += coin.value;
|
||||
|
||||
if (tx.height !== -1)
|
||||
if (block)
|
||||
this.pending.confirmed += coin.value;
|
||||
|
||||
this.unspendCredit(tx, i);
|
||||
@ -1424,12 +1418,12 @@ TXDB.prototype.erase = co(function* erase(tx) {
|
||||
|
||||
details.setOutput(i, path);
|
||||
|
||||
credit = Credit.fromTX(tx, i, tx.height);
|
||||
credit = Credit.fromTX(tx, i, height);
|
||||
|
||||
this.pending.coin--;
|
||||
this.pending.unconfirmed -= output.value;
|
||||
|
||||
if (tx.height !== -1)
|
||||
if (block)
|
||||
this.pending.confirmed -= output.value;
|
||||
|
||||
yield this.removeCredit(credit, path);
|
||||
@ -1441,29 +1435,29 @@ TXDB.prototype.erase = co(function* erase(tx) {
|
||||
// Remove the transaction data
|
||||
// itself as well as unindex.
|
||||
this.del(layout.t(hash));
|
||||
this.del(layout.m(tx.ps, hash));
|
||||
this.del(layout.m(wtx.ps, hash));
|
||||
|
||||
if (tx.height === -1)
|
||||
if (!block)
|
||||
this.del(layout.p(hash));
|
||||
else
|
||||
this.del(layout.h(tx.height, hash));
|
||||
this.del(layout.h(height, hash));
|
||||
|
||||
// Remove all secondary indexing.
|
||||
for (i = 0; i < details.accounts.length; i++) {
|
||||
account = details.accounts[i];
|
||||
|
||||
this.del(layout.T(account, hash));
|
||||
this.del(layout.M(account, tx.ps, hash));
|
||||
this.del(layout.M(account, wtx.ps, hash));
|
||||
|
||||
if (tx.height === -1)
|
||||
if (!block)
|
||||
this.del(layout.P(account, hash));
|
||||
else
|
||||
this.del(layout.H(account, tx.height, hash));
|
||||
this.del(layout.H(account, height, hash));
|
||||
}
|
||||
|
||||
if (tx.height !== -1) {
|
||||
yield this.removeBlockMap(tx, tx.height);
|
||||
yield this.removeBlockSlow(tx, tx.height);
|
||||
if (block) {
|
||||
yield this.removeBlockMap(hash, height);
|
||||
yield this.removeBlockSlow(hash, height);
|
||||
}
|
||||
|
||||
// Update the transaction counter
|
||||
@ -1482,12 +1476,13 @@ TXDB.prototype.erase = co(function* erase(tx) {
|
||||
* Remove a transaction and recursively
|
||||
* remove all of its spenders.
|
||||
* @private
|
||||
* @param {TX} tx - Transaction to be removed.
|
||||
* @param {TXRecord} wtx
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
TXDB.prototype.removeRecursive = co(function* removeRecursive(tx) {
|
||||
var hash = tx.hash('hex');
|
||||
TXDB.prototype.removeRecursive = co(function* removeRecursive(wtx) {
|
||||
var tx = wtx.tx;
|
||||
var hash = wtx.hash;
|
||||
var i, spent, stx, details;
|
||||
|
||||
for (i = 0; i < tx.outputs.length; i++) {
|
||||
@ -1507,7 +1502,7 @@ TXDB.prototype.removeRecursive = co(function* removeRecursive(tx) {
|
||||
this.start();
|
||||
|
||||
// Remove the spender.
|
||||
details = yield this.erase(tx);
|
||||
details = yield this.erase(wtx, wtx.getBlock());
|
||||
|
||||
assert(details);
|
||||
|
||||
@ -1547,30 +1542,31 @@ TXDB.prototype.unconfirm = co(function* unconfirm(hash) {
|
||||
*/
|
||||
|
||||
TXDB.prototype._unconfirm = co(function* unconfirm(hash) {
|
||||
var tx = yield this.getTX(hash);
|
||||
var wtx = yield this.getTX(hash);
|
||||
|
||||
if (!tx)
|
||||
if (!wtx)
|
||||
return;
|
||||
|
||||
return yield this.disconnect(tx);
|
||||
return yield this.disconnect(wtx, wtx.getBlock());
|
||||
});
|
||||
|
||||
/**
|
||||
* Unconfirm a transaction. Necessary after a reorg.
|
||||
* @param {Hash} hash
|
||||
* @param {TXRecord} wtx
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
TXDB.prototype.disconnect = co(function* disconnect(tx) {
|
||||
var hash = tx.hash('hex');
|
||||
var details = new Details(this, tx);
|
||||
var height = tx.height;
|
||||
TXDB.prototype.disconnect = co(function* disconnect(wtx, block) {
|
||||
var tx = wtx.tx;
|
||||
var hash = wtx.hash;
|
||||
var height = block.height;
|
||||
var details = new Details(this, wtx, block);
|
||||
var i, account, output, coin, credits;
|
||||
var input, path, credit;
|
||||
|
||||
assert(height !== -1);
|
||||
assert(block);
|
||||
|
||||
tx.unsetBlock();
|
||||
wtx.unsetBlock();
|
||||
|
||||
if (!tx.isCoinbase()) {
|
||||
// We need to reconnect the coins. Start
|
||||
@ -1616,12 +1612,12 @@ TXDB.prototype.disconnect = co(function* disconnect(tx) {
|
||||
|
||||
// Potentially update undo coin height.
|
||||
if (!credit) {
|
||||
yield this.updateSpentCoin(tx, i);
|
||||
yield this.updateSpentCoin(tx, i, height);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (credit.spent)
|
||||
yield this.updateSpentCoin(tx, i);
|
||||
yield this.updateSpentCoin(tx, i, height);
|
||||
|
||||
details.setOutput(i, path);
|
||||
|
||||
@ -1635,13 +1631,13 @@ TXDB.prototype.disconnect = co(function* disconnect(tx) {
|
||||
yield this.saveCredit(credit, path);
|
||||
}
|
||||
|
||||
yield this.removeBlockMap(tx, height);
|
||||
yield this.removeBlock(tx, height);
|
||||
yield this.removeBlockMap(hash, height);
|
||||
yield this.removeBlock(tx.hash(), height);
|
||||
|
||||
// We need to update the now-removed
|
||||
// block properties and reindex due
|
||||
// to the height change.
|
||||
this.put(layout.t(hash), tx.toExtended());
|
||||
this.put(layout.t(hash), wtx.toRaw());
|
||||
this.put(layout.p(hash), DUMMY);
|
||||
this.del(layout.h(height, hash));
|
||||
|
||||
@ -1674,14 +1670,15 @@ TXDB.prototype.disconnect = co(function* disconnect(tx) {
|
||||
* @returns {Promise} - Returns Boolean.
|
||||
*/
|
||||
|
||||
TXDB.prototype.removeConflict = co(function* removeConflict(tx) {
|
||||
TXDB.prototype.removeConflict = co(function* removeConflict(wtx) {
|
||||
var tx = wtx.tx;
|
||||
var details;
|
||||
|
||||
this.logger.warning('Handling conflicting tx: %s.', tx.txid());
|
||||
|
||||
this.drop();
|
||||
|
||||
details = yield this.removeRecursive(tx);
|
||||
details = yield this.removeRecursive(wtx);
|
||||
|
||||
this.start();
|
||||
|
||||
@ -1704,7 +1701,7 @@ TXDB.prototype.removeConflict = co(function* removeConflict(tx) {
|
||||
TXDB.prototype.removeConflicts = co(function* removeConflicts(tx, conf) {
|
||||
var hash = tx.hash('hex');
|
||||
var spends = [];
|
||||
var i, input, prevout, spent, spender;
|
||||
var i, input, prevout, spent, spender, block;
|
||||
|
||||
if (tx.isCoinbase())
|
||||
return true;
|
||||
@ -1726,8 +1723,9 @@ TXDB.prototype.removeConflicts = co(function* removeConflicts(tx, conf) {
|
||||
|
||||
spender = yield this.getTX(spent.hash);
|
||||
assert(spender);
|
||||
block = spender.getBlock();
|
||||
|
||||
if (conf && spender.height !== -1)
|
||||
if (conf && block)
|
||||
return false;
|
||||
|
||||
spends[i] = spender;
|
||||
@ -1841,14 +1839,11 @@ TXDB.prototype.filterLocked = function filterLocked(coins) {
|
||||
TXDB.prototype.getLocked = function getLocked() {
|
||||
var keys = Object.keys(this.locked);
|
||||
var outpoints = [];
|
||||
var i, key, hash, index, outpoint;
|
||||
var i, key;
|
||||
|
||||
for (i = 0; i < keys.length; i++) {
|
||||
key = keys[i];
|
||||
hash = key.slice(0, 64);
|
||||
index = +key.slice(64);
|
||||
outpoint = new Outpoint(hash, index);
|
||||
outpoints.push(outpoint);
|
||||
outpoints.push(Outpoint.fromKey(key));
|
||||
}
|
||||
|
||||
return outpoints;
|
||||
@ -2163,7 +2158,7 @@ TXDB.prototype.getHistory = function getHistory(account) {
|
||||
return this.values({
|
||||
gte: layout.t(constants.NULL_HASH),
|
||||
lte: layout.t(constants.HIGH_HASH),
|
||||
parse: TX.fromExtended
|
||||
parse: TXRecord.fromRaw
|
||||
});
|
||||
};
|
||||
|
||||
@ -2352,7 +2347,7 @@ TXDB.prototype.getAccountCoins = co(function* getAccountCoins(account) {
|
||||
});
|
||||
|
||||
/**
|
||||
* Fill a transaction with coins (all historical coins).
|
||||
* Get historical coins for a transaction.
|
||||
* @param {TX} tx
|
||||
* @returns {Promise} - Returns {@link TX}.
|
||||
*/
|
||||
@ -2381,6 +2376,60 @@ TXDB.prototype.getSpentCoins = co(function* getSpentCoins(tx) {
|
||||
return coins;
|
||||
});
|
||||
|
||||
/**
|
||||
* Get a coin viewpoint.
|
||||
* @param {TX} tx
|
||||
* @returns {Promise} - Returns {@link CoinView}.
|
||||
*/
|
||||
|
||||
TXDB.prototype.getCoinView = co(function* getCoinView(tx) {
|
||||
var view = new CoinView();
|
||||
var i, input, prevout, coin;
|
||||
|
||||
if (tx.isCoinbase())
|
||||
return view;
|
||||
|
||||
for (i = 0; i < tx.inputs.length; i++) {
|
||||
input = tx.inputs[i];
|
||||
prevout = input.prevout;
|
||||
coin = yield this.getCoin(prevout.hash, prevout.index);
|
||||
|
||||
if (!coin)
|
||||
continue;
|
||||
|
||||
view.addCoin(coin);
|
||||
}
|
||||
|
||||
return view;
|
||||
});
|
||||
|
||||
/**
|
||||
* Get historical coin viewpoint.
|
||||
* @param {TX} tx
|
||||
* @returns {Promise} - Returns {@link CoinView}.
|
||||
*/
|
||||
|
||||
TXDB.prototype.getSpentView = co(function* getSpentView(tx) {
|
||||
var view = new CoinView();
|
||||
var i, coins, coin;
|
||||
|
||||
if (tx.isCoinbase())
|
||||
return view;
|
||||
|
||||
coins = yield this.getSpentCoins(tx);
|
||||
|
||||
for (i = 0; i < coins.length; i++) {
|
||||
coin = coins[i];
|
||||
|
||||
if (!coin)
|
||||
continue;
|
||||
|
||||
view.addCoin(coin);
|
||||
}
|
||||
|
||||
return view;
|
||||
});
|
||||
|
||||
/**
|
||||
* Get TXDB state.
|
||||
* @returns {Promise}
|
||||
@ -2402,12 +2451,12 @@ TXDB.prototype.getState = co(function* getState() {
|
||||
*/
|
||||
|
||||
TXDB.prototype.getTX = co(function* getTX(hash) {
|
||||
var tx = yield this.get(layout.t(hash));
|
||||
var raw = yield this.get(layout.t(hash));
|
||||
|
||||
if (!tx)
|
||||
if (!raw)
|
||||
return;
|
||||
|
||||
return TX.fromExtended(tx);
|
||||
return TXRecord.fromRaw(raw);
|
||||
});
|
||||
|
||||
/**
|
||||
@ -2417,31 +2466,31 @@ TXDB.prototype.getTX = co(function* getTX(hash) {
|
||||
*/
|
||||
|
||||
TXDB.prototype.getDetails = co(function* getDetails(hash) {
|
||||
var tx = yield this.getTX(hash);
|
||||
var wtx = yield this.getTX(hash);
|
||||
|
||||
if (!tx)
|
||||
if (!wtx)
|
||||
return;
|
||||
|
||||
return yield this.toDetails(tx);
|
||||
return yield this.toDetails(wtx);
|
||||
});
|
||||
|
||||
/**
|
||||
* Convert transaction to transaction details.
|
||||
* @param {TX|TX[]} txs
|
||||
* @param {TXRecord[]} wtxs
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
TXDB.prototype.toDetails = co(function* toDetails(txs) {
|
||||
var i, out, tx, details;
|
||||
TXDB.prototype.toDetails = co(function* toDetails(wtxs) {
|
||||
var i, out, wtx, details;
|
||||
|
||||
if (!Array.isArray(txs))
|
||||
return yield this._toDetails(txs);
|
||||
if (!Array.isArray(wtxs))
|
||||
return yield this._toDetails(wtxs);
|
||||
|
||||
out = [];
|
||||
|
||||
for (i = 0; i < txs.length; i++) {
|
||||
tx = txs[i];
|
||||
details = yield this._toDetails(tx);
|
||||
for (i = 0; i < wtxs.length; i++) {
|
||||
wtx = wtxs[i];
|
||||
details = yield this._toDetails(wtx);
|
||||
|
||||
if (!details)
|
||||
continue;
|
||||
@ -2455,12 +2504,14 @@ TXDB.prototype.toDetails = co(function* toDetails(txs) {
|
||||
/**
|
||||
* Convert transaction to transaction details.
|
||||
* @private
|
||||
* @param {TX} tx
|
||||
* @param {TXRecord} wtx
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
TXDB.prototype._toDetails = co(function* _toDetails(tx) {
|
||||
var details = new Details(this, tx);
|
||||
TXDB.prototype._toDetails = co(function* _toDetails(wtx) {
|
||||
var tx = wtx.tx;
|
||||
var block = wtx.getBlock();
|
||||
var details = new Details(this, wtx, block);
|
||||
var coins = yield this.getSpentCoins(tx);
|
||||
var i, coin, path, output;
|
||||
|
||||
@ -2575,10 +2626,11 @@ TXDB.prototype.hasSpentCoin = function hasSpentCoin(spent) {
|
||||
* Update spent coin height in storage.
|
||||
* @param {TX} tx - Sending transaction.
|
||||
* @param {Number} index
|
||||
* @param {Number} height
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
TXDB.prototype.updateSpentCoin = co(function* updateSpentCoin(tx, index) {
|
||||
TXDB.prototype.updateSpentCoin = co(function* updateSpentCoin(tx, index, height) {
|
||||
var prevout = Outpoint.fromTX(tx, index);
|
||||
var spent = yield this.getSpent(prevout.hash, prevout.index);
|
||||
var coin;
|
||||
@ -2591,7 +2643,7 @@ TXDB.prototype.updateSpentCoin = co(function* updateSpentCoin(tx, index) {
|
||||
if (!coin)
|
||||
return;
|
||||
|
||||
coin.height = tx.height;
|
||||
coin.height = height;
|
||||
|
||||
this.put(layout.d(spent.hash, spent.index), coin.toRaw());
|
||||
});
|
||||
@ -2686,7 +2738,7 @@ TXDB.prototype.getAccountBalance = co(function* getAccountBalance(account) {
|
||||
TXDB.prototype.zap = co(function* zap(account, age) {
|
||||
var hashes = [];
|
||||
var now = util.now();
|
||||
var i, txs, tx, hash;
|
||||
var i, txs, wtx;
|
||||
|
||||
assert(util.isUInt32(age));
|
||||
|
||||
@ -2696,20 +2748,19 @@ TXDB.prototype.zap = co(function* zap(account, age) {
|
||||
});
|
||||
|
||||
for (i = 0; i < txs.length; i++) {
|
||||
tx = txs[i];
|
||||
hash = tx.hash('hex');
|
||||
wtx = txs[i];
|
||||
|
||||
if (tx.height !== -1)
|
||||
if (wtx.height !== -1)
|
||||
continue;
|
||||
|
||||
assert(now - tx.ps >= age);
|
||||
assert(now - wtx.ps >= age);
|
||||
|
||||
this.logger.debug('Zapping TX: %s (%s)',
|
||||
hash, this.wallet.id);
|
||||
wtx.txid(), this.wallet.id);
|
||||
|
||||
yield this.remove(hash);
|
||||
yield this.remove(wtx.hash);
|
||||
|
||||
hashes.push(hash);
|
||||
hashes.push(wtx.hash);
|
||||
}
|
||||
|
||||
return hashes;
|
||||
@ -3003,9 +3054,9 @@ Credit.fromTX = function fromTX(tx, index, height) {
|
||||
* @param {TX} tx
|
||||
*/
|
||||
|
||||
function Details(txdb, tx) {
|
||||
function Details(txdb, wtx, block) {
|
||||
if (!(this instanceof Details))
|
||||
return new Details(txdb, tx);
|
||||
return new Details(txdb, wtx, block);
|
||||
|
||||
this.wallet = txdb.wallet;
|
||||
this.network = this.wallet.network;
|
||||
@ -3014,17 +3065,22 @@ function Details(txdb, tx) {
|
||||
|
||||
this.chainHeight = txdb.walletdb.state.height;
|
||||
|
||||
this.hash = tx.hash('hex');
|
||||
this.size = tx.getSize();
|
||||
this.vsize = tx.getVirtualSize();
|
||||
this.tx = tx;
|
||||
this.hash = wtx.hash;
|
||||
this.tx = wtx.tx;
|
||||
this.ps = wtx.ps;
|
||||
this.size = this.tx.getSize();
|
||||
this.vsize = this.tx.getVirtualSize();
|
||||
|
||||
this.block = tx.block;
|
||||
this.height = tx.height;
|
||||
this.ts = tx.ts;
|
||||
this.index = tx.index;
|
||||
this.block = null;
|
||||
this.height = -1;
|
||||
this.ts = 0;
|
||||
this.index = -1;
|
||||
|
||||
this.ps = tx.ps;
|
||||
if (block) {
|
||||
this.block = block.hash;
|
||||
this.height = block.height;
|
||||
this.ts = block.ts;
|
||||
}
|
||||
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
@ -3181,10 +3237,10 @@ Details.prototype.toJSON = function toJSON() {
|
||||
rate: Amount.btc(rate),
|
||||
confirmations: this.getConfirmations(),
|
||||
inputs: this.inputs.map(function(input) {
|
||||
return input.toJSON(self.network);
|
||||
return input.getJSON(self.network);
|
||||
}),
|
||||
outputs: this.outputs.map(function(output) {
|
||||
return output.toJSON(self.network);
|
||||
return output.getJSON(self.network);
|
||||
}),
|
||||
tx: this.tx.toRaw().toString('hex')
|
||||
};
|
||||
@ -3207,13 +3263,22 @@ function DetailsMember() {
|
||||
this.path = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the member to a more json-friendly object.
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
DetailsMember.prototype.toJSON = function toJSON() {
|
||||
return this.getJSON();
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert the member to a more json-friendly object.
|
||||
* @param {Network} network
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
DetailsMember.prototype.toJSON = function toJSON(network) {
|
||||
DetailsMember.prototype.getJSON = function getJSON(network) {
|
||||
return {
|
||||
value: Amount.btc(this.value),
|
||||
address: this.address
|
||||
@ -3316,7 +3381,6 @@ BlockRecord.prototype.fromRaw = function fromRaw(data) {
|
||||
|
||||
/**
|
||||
* Instantiate wallet block from serialized data.
|
||||
* @param {Hash} hash
|
||||
* @param {Buffer} data
|
||||
* @returns {BlockRecord}
|
||||
*/
|
||||
@ -3360,6 +3424,29 @@ BlockRecord.prototype.toJSON = function toJSON() {
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate wallet block from block meta.
|
||||
* @private
|
||||
* @param {BlockMeta} block
|
||||
*/
|
||||
|
||||
BlockRecord.prototype.fromMeta = function fromMeta(block) {
|
||||
this.hash = block.hash;
|
||||
this.height = block.height;
|
||||
this.ts = block.ts;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate wallet block from block meta.
|
||||
* @param {BlockMeta} block
|
||||
* @returns {BlockRecord}
|
||||
*/
|
||||
|
||||
BlockRecord.fromMeta = function fromMeta(block) {
|
||||
return new BlockRecord().fromMeta(block);
|
||||
};
|
||||
|
||||
/*
|
||||
* Helpers
|
||||
*/
|
||||
|
||||
@ -1652,24 +1652,26 @@ Wallet.prototype._send = co(function* send(options, passphrase) {
|
||||
*/
|
||||
|
||||
Wallet.prototype.increaseFee = co(function* increaseFee(hash, rate, passphrase) {
|
||||
var tx = yield this.getTX(hash);
|
||||
var i, oldFee, fee, path, input, output, change;
|
||||
var wtx = yield this.getTX(hash);
|
||||
var i, tx, view, oldFee, fee, path, input, output, change;
|
||||
|
||||
if (!tx)
|
||||
if (!wtx)
|
||||
throw new Error('Transaction not found.');
|
||||
|
||||
tx = wtx.tx;
|
||||
|
||||
if (tx.isCoinbase())
|
||||
throw new Error('Transaction is a coinbase.');
|
||||
|
||||
yield this.fillHistory(tx);
|
||||
view = yield this.getSpentView(tx);
|
||||
|
||||
if (!tx.hasCoins())
|
||||
if (!tx.hasCoins(view))
|
||||
throw new Error('Not all coins available.');
|
||||
|
||||
if (!util.isUInt32(rate))
|
||||
throw new Error('Rate must be a number.');
|
||||
|
||||
oldFee = tx.getFee();
|
||||
oldFee = tx.getFee(view);
|
||||
fee = tx.getMinFee(null, rate);
|
||||
|
||||
if (fee > constants.tx.MAX_FEE)
|
||||
@ -1679,6 +1681,7 @@ Wallet.prototype.increaseFee = co(function* increaseFee(hash, rate, passphrase)
|
||||
throw new Error('Fee is not increasing.');
|
||||
|
||||
tx = MTX.fromRaw(tx.toRaw());
|
||||
tx.view = view;
|
||||
|
||||
for (i = 0; i < tx.inputs.length; i++) {
|
||||
input = tx.inputs[i];
|
||||
@ -1715,7 +1718,7 @@ Wallet.prototype.increaseFee = co(function* increaseFee(hash, rate, passphrase)
|
||||
if (change.value < 0)
|
||||
throw new Error('Fee is too high.');
|
||||
|
||||
if (change.isDust(constants.tx.MIN_RELAY)) {
|
||||
if (change.isDust()) {
|
||||
tx.outputs.splice(tx.changeIndex, 1);
|
||||
tx.changeIndex = -1;
|
||||
}
|
||||
@ -2080,23 +2083,33 @@ Wallet.prototype.sign = co(function* sign(tx, passphrase) {
|
||||
});
|
||||
|
||||
/**
|
||||
* Fill transaction with historical coins.
|
||||
* Get a coin viewpoint.
|
||||
* @param {TX} tx
|
||||
* @returns {Promise} - Returns {@link TX}.
|
||||
* @returns {Promise} - Returns {@link CoinView}.
|
||||
*/
|
||||
|
||||
Wallet.prototype.fillHistory = function fillHistory(tx) {
|
||||
return this.txdb.fillHistory(tx);
|
||||
Wallet.prototype.getCoinView = function getCoinView(tx) {
|
||||
return this.txdb.getCoinView(tx);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a historical coin viewpoint.
|
||||
* @param {TX} tx
|
||||
* @returns {Promise} - Returns {@link CoinView}.
|
||||
*/
|
||||
|
||||
Wallet.prototype.getSpentView = function getSpentView(tx) {
|
||||
return this.txdb.getSpentView(tx);
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert transaction to transaction details.
|
||||
* @param {TX} tx
|
||||
* @param {TXRecord} wtx
|
||||
* @returns {Promise} - Returns {@link Details}.
|
||||
*/
|
||||
|
||||
Wallet.prototype.toDetails = function toDetails(tx) {
|
||||
return this.txdb.toDetails(tx);
|
||||
Wallet.prototype.toDetails = function toDetails(wtx) {
|
||||
return this.txdb.toDetails(wtx);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -25,7 +25,6 @@ var Account = require('./account');
|
||||
var LDB = require('../db/ldb');
|
||||
var Bloom = require('../utils/bloom');
|
||||
var Logger = require('../node/logger');
|
||||
var TX = require('../primitives/tx');
|
||||
var Outpoint = require('../primitives/outpoint');
|
||||
var layouts = require('./layout');
|
||||
var records = require('./records');
|
||||
@ -35,6 +34,7 @@ var BlockMapRecord = records.BlockMapRecord;
|
||||
var BlockMeta = records.BlockMeta;
|
||||
var PathMapRecord = records.PathMapRecord;
|
||||
var OutpointMapRecord = records.OutpointMapRecord;
|
||||
var TXRecord = records.TXRecord;
|
||||
var U32 = encoding.U32;
|
||||
var cob = co.cob;
|
||||
var DUMMY = new Buffer([0]);
|
||||
@ -1467,10 +1467,12 @@ WalletDB.prototype.getPendingTX = co(function* getPendingTX() {
|
||||
WalletDB.prototype.resend = co(function* resend() {
|
||||
var keys = yield this.getPendingTX();
|
||||
var txs = [];
|
||||
var i, key, data, tx;
|
||||
var i, key, data, wtx;
|
||||
|
||||
if (keys.length > 0)
|
||||
this.logger.info('Rebroadcasting %d WalletDB transactions.', keys.length);
|
||||
if (keys.length === 0)
|
||||
return;
|
||||
|
||||
this.logger.info('Rebroadcasting %d WalletDB transactions.', keys.length);
|
||||
|
||||
for (i = 0; i < keys.length; i++) {
|
||||
key = keys[i];
|
||||
@ -1479,12 +1481,12 @@ WalletDB.prototype.resend = co(function* resend() {
|
||||
if (!data)
|
||||
continue;
|
||||
|
||||
tx = TX.fromExtended(data);
|
||||
wtx = TXRecord.fromRaw(data);
|
||||
|
||||
if (tx.isCoinbase())
|
||||
if (wtx.tx.isCoinbase())
|
||||
continue;
|
||||
|
||||
txs.push(tx);
|
||||
txs.push(wtx.tx);
|
||||
}
|
||||
|
||||
txs = btcutils.sortTX(txs);
|
||||
@ -2072,22 +2074,9 @@ WalletDB.prototype.rescanBlock = co(function* rescanBlock(entry, txs) {
|
||||
|
||||
WalletDB.prototype.addTX = co(function* addTX(tx) {
|
||||
var unlock = yield this.txLock.lock();
|
||||
var block;
|
||||
|
||||
try {
|
||||
if (tx.height !== -1) {
|
||||
block = yield this.getBlock(tx.height);
|
||||
|
||||
if (!block)
|
||||
throw new Error('WDB: Cannot insert tx from unknown chain.');
|
||||
|
||||
if (tx.block !== block.hash)
|
||||
throw new Error('WDB: Cannot insert tx from alternate chain.');
|
||||
|
||||
this.logger.warning('WalletDB is inserting confirmed transaction.');
|
||||
}
|
||||
|
||||
return yield this._insert(tx, block);
|
||||
return yield this._insert(tx);
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
|
||||
@ -299,16 +299,16 @@ describe('Wallet', function() {
|
||||
assert.equal(balance.unconfirmed, 11000);
|
||||
|
||||
txs = yield w.getHistory();
|
||||
assert(txs.some(function(tx) {
|
||||
return tx.hash('hex') === f1.hash('hex');
|
||||
assert(txs.some(function(wtx) {
|
||||
return wtx.hash === f1.hash('hex');
|
||||
}));
|
||||
|
||||
balance = yield f.getBalance();
|
||||
assert.equal(balance.unconfirmed, 10000);
|
||||
|
||||
txs = yield f.getHistory();
|
||||
assert(txs.some(function(tx) {
|
||||
return tx.hash('hex') === f1.hash('hex');
|
||||
assert(txs.some(function(wtx) {
|
||||
return wtx.tx.hash('hex') === f1.hash('hex');
|
||||
}));
|
||||
}));
|
||||
|
||||
@ -321,8 +321,8 @@ describe('Wallet', function() {
|
||||
|
||||
txs = yield w.getHistory();
|
||||
assert.equal(txs.length, 5);
|
||||
total = txs.reduce(function(t, tx) {
|
||||
return t + tx.getOutputValue();
|
||||
total = txs.reduce(function(t, wtx) {
|
||||
return t + wtx.tx.getOutputValue();
|
||||
}, 0);
|
||||
|
||||
assert.equal(total, 154000);
|
||||
@ -341,8 +341,8 @@ describe('Wallet', function() {
|
||||
txs = yield w.getHistory();
|
||||
assert.equal(txs.length, 2);
|
||||
|
||||
total = txs.reduce(function(t, tx) {
|
||||
return t + tx.getOutputValue();
|
||||
total = txs.reduce(function(t, wtx) {
|
||||
return t + wtx.tx.getOutputValue();
|
||||
}, 0);
|
||||
assert.equal(total, 56000);
|
||||
}));
|
||||
@ -449,16 +449,16 @@ describe('Wallet', function() {
|
||||
assert.equal(balance.unconfirmed, 11000);
|
||||
|
||||
txs = yield w.getHistory();
|
||||
assert(txs.some(function(tx) {
|
||||
return tx.hash('hex') === f1.hash('hex');
|
||||
assert(txs.some(function(wtx) {
|
||||
return wtx.tx.hash('hex') === f1.hash('hex');
|
||||
}));
|
||||
|
||||
balance = yield f.getBalance();
|
||||
assert.equal(balance.unconfirmed, 10000);
|
||||
|
||||
txs = yield f.getHistory();
|
||||
assert(txs.some(function(tx) {
|
||||
return tx.hash('hex') === f1.hash('hex');
|
||||
assert(txs.some(function(wtx) {
|
||||
return wtx.tx.hash('hex') === f1.hash('hex');
|
||||
}));
|
||||
|
||||
yield walletdb.addTX(t2);
|
||||
@ -667,10 +667,9 @@ describe('Wallet', function() {
|
||||
multisig = co(function* multisig(witness, bullshitNesting, cb) {
|
||||
var flags = constants.flags.STANDARD_VERIFY_FLAGS;
|
||||
var options, w1, w2, w3, receive, b58, addr, paddr, utx, send, change;
|
||||
var view;
|
||||
|
||||
var rec = bullshitNesting ? 'nested' : 'receive';
|
||||
var depth = bullshitNesting ? 'nestedDepth' : 'receiveDepth';
|
||||
var view, block;
|
||||
|
||||
if (witness)
|
||||
flags |= constants.flags.VERIFY_WITNESS;
|
||||
@ -728,11 +727,7 @@ describe('Wallet', function() {
|
||||
utx = utx.toTX();
|
||||
|
||||
// Simulate a confirmation
|
||||
var block = nextBlock();
|
||||
utx.height = block.height;
|
||||
utx.block = block.hash;
|
||||
utx.ts = block.ts;
|
||||
utx.index = 0;
|
||||
block = nextBlock();
|
||||
|
||||
assert.equal(w1.account[depth], 1);
|
||||
|
||||
@ -773,10 +768,6 @@ describe('Wallet', function() {
|
||||
|
||||
// Simulate a confirmation
|
||||
block = nextBlock();
|
||||
send.height = block.height;
|
||||
send.block = block.hash;
|
||||
send.ts = block.ts;
|
||||
send.index = 0;
|
||||
|
||||
yield walletdb.addBlock(block, [send]);
|
||||
|
||||
@ -924,7 +915,6 @@ describe('Wallet', function() {
|
||||
.addOutput(account.receive.getAddress(), 5460)
|
||||
.addOutput(account.receive.getAddress(), 5460);
|
||||
|
||||
t1.ps = 0xdeadbeef;
|
||||
t1.addInput(dummy());
|
||||
t1 = t1.toTX();
|
||||
|
||||
@ -1053,14 +1043,14 @@ describe('Wallet', function() {
|
||||
|
||||
it('should get range of txs', cob(function* () {
|
||||
var w = wallet;
|
||||
var txs = yield w.getRange({ start: 0xdeadbeef - 1000 });
|
||||
assert.equal(txs.length, 1);
|
||||
var txs = yield w.getRange({ start: util.now() - 1000 });
|
||||
assert.equal(txs.length, 2);
|
||||
}));
|
||||
|
||||
it('should get range of txs from account', cob(function* () {
|
||||
var w = wallet;
|
||||
var txs = yield w.getRange('foo', { start: 0xdeadbeef - 1000 });
|
||||
assert.equal(txs.length, 1);
|
||||
var txs = yield w.getRange('foo', { start: util.now() - 1000 });
|
||||
assert.equal(txs.length, 2);
|
||||
}));
|
||||
|
||||
it('should not get range of txs from non-existent account', cob(function* () {
|
||||
@ -1086,7 +1076,7 @@ describe('Wallet', function() {
|
||||
it('should import privkey', cob(function* () {
|
||||
var key = KeyRing.generate();
|
||||
var w = yield walletdb.create({ passphrase: 'test' });
|
||||
var options, k, t1, t2, tx;
|
||||
var options, k, t1, t2, wtx;
|
||||
|
||||
yield w.importKey('default', key, 'test');
|
||||
|
||||
@ -1106,9 +1096,9 @@ describe('Wallet', function() {
|
||||
|
||||
yield walletdb.addTX(t1);
|
||||
|
||||
tx = yield w.getTX(t1.hash('hex'));
|
||||
assert(tx);
|
||||
assert.equal(t1.hash('hex'), tx.hash('hex'));
|
||||
wtx = yield w.getTX(t1.hash('hex'));
|
||||
assert(wtx);
|
||||
assert.equal(t1.hash('hex'), wtx.hash);
|
||||
|
||||
options = {
|
||||
rate: 10000,
|
||||
@ -1120,7 +1110,7 @@ describe('Wallet', function() {
|
||||
t2 = yield w.createTX(options);
|
||||
yield w.sign(t2);
|
||||
assert(t2.verify());
|
||||
assert(t2.inputs[0].prevout.hash === tx.hash('hex'));
|
||||
assert(t2.inputs[0].prevout.hash === wtx.hash);
|
||||
|
||||
ewallet = w;
|
||||
ekey = key;
|
||||
@ -1159,15 +1149,15 @@ describe('Wallet', function() {
|
||||
|
||||
it('should get details', cob(function* () {
|
||||
var w = wallet;
|
||||
var txs = yield w.getRange('foo', { start: 0xdeadbeef - 1000 });
|
||||
var txs = yield w.getRange('foo', { start: util.now() - 1000 });
|
||||
var details = yield w.toDetails(txs);
|
||||
assert.equal(details[0].toJSON().outputs[0].path.name, 'foo');
|
||||
assert.equal(details[1].toJSON().outputs[0].path.name, 'foo');
|
||||
}));
|
||||
|
||||
it('should rename wallet', cob(function* () {
|
||||
var w = wallet;
|
||||
yield wallet.rename('test');
|
||||
var txs = yield w.getRange('foo', { start: 0xdeadbeef - 1000 });
|
||||
var txs = yield w.getRange('foo', { start: util.now() - 1000 });
|
||||
var details = yield w.toDetails(txs);
|
||||
assert.equal(details[0].toJSON().id, 'test');
|
||||
}));
|
||||
|
||||
Loading…
Reference in New Issue
Block a user