txdb: less confusing tx details.
This commit is contained in:
parent
9ed439b60c
commit
11b2f1a7bc
@ -1259,32 +1259,27 @@ HTTPServer.prototype._initIO = function _initIO() {
|
||||
});
|
||||
});
|
||||
|
||||
this.walletdb.on('tx', function(id, tx, info) {
|
||||
var details = info.toJSON();
|
||||
this.walletdb.on('tx', function(id, tx, details) {
|
||||
self.server.io.to(id).emit('wallet tx', details);
|
||||
self.server.io.to('!all').emit('wallet tx', id, details);
|
||||
});
|
||||
|
||||
this.walletdb.on('confirmed', function(id, tx, info) {
|
||||
var details = info.toJSON();
|
||||
this.walletdb.on('confirmed', function(id, tx, details) {
|
||||
self.server.io.to(id).emit('wallet confirmed', details);
|
||||
self.server.io.to('!all').emit('wallet confirmed', id, details);
|
||||
});
|
||||
|
||||
this.walletdb.on('unconfirmed', function(id, tx, info) {
|
||||
var details = info.toJSON();
|
||||
this.walletdb.on('unconfirmed', function(id, tx, details) {
|
||||
self.server.io.to(id).emit('wallet unconfirmed', details);
|
||||
self.server.io.to('!all').emit('wallet unconfirmed', id, details);
|
||||
});
|
||||
|
||||
this.walletdb.on('conflict', function(id, tx, info) {
|
||||
var details = info.toJSON();
|
||||
this.walletdb.on('conflict', function(id, tx, details) {
|
||||
self.server.io.to(id).emit('wallet conflict', details);
|
||||
self.server.io.to('!all').emit('wallet conflict', id, details);
|
||||
});
|
||||
|
||||
this.walletdb.on('balance', function(id, balance) {
|
||||
var json = balance.toJSON();
|
||||
self.server.io.to(id).emit('wallet balance', json);
|
||||
self.server.io.to('!all').emit('wallet balance', id, json);
|
||||
});
|
||||
|
||||
@ -331,11 +331,11 @@ TXDB.prototype.commit = co(function* commit() {
|
||||
* @private
|
||||
* @param {String} event
|
||||
* @param {Object} data
|
||||
* @param {PathInfo} info
|
||||
* @param {PathInfo} details
|
||||
*/
|
||||
|
||||
TXDB.prototype.emit = function emit(event, data, info) {
|
||||
this.events.push([event, data, info]);
|
||||
TXDB.prototype.emit = function emit(event, data, details) {
|
||||
this.events.push([event, data, details]);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -436,8 +436,22 @@ TXDB.prototype.values = function values(options) {
|
||||
* @returns {Promise} - Returns {@link PathInfo}.
|
||||
*/
|
||||
|
||||
TXDB.prototype.getPathInfo = function getPathInfo(tx) {
|
||||
return this.wallet.getPathInfo(tx);
|
||||
TXDB.prototype.getPath = function getPath(output) {
|
||||
if (!output)
|
||||
return Promise.resolve();
|
||||
return this.wallet.getPath(output.getAddress());
|
||||
};
|
||||
|
||||
/**
|
||||
* Map a transactions' addresses to wallet IDs.
|
||||
* @param {TX} tx
|
||||
* @returns {Promise} - Returns {@link PathInfo}.
|
||||
*/
|
||||
|
||||
TXDB.prototype.hasPath = function hasPath(output) {
|
||||
if (!output)
|
||||
return Promise.resolve();
|
||||
return this.wallet.hasPath(output.getAddress());
|
||||
};
|
||||
|
||||
/**
|
||||
@ -478,7 +492,7 @@ TXDB.prototype.verifyInputs = co(function* verifyInputs(tx) {
|
||||
var hasOrphans = false;
|
||||
var orphans = [];
|
||||
var i, input, prevout;
|
||||
var path, key, coin, spent;
|
||||
var key, coin, spent;
|
||||
|
||||
if (tx.isCoinbase())
|
||||
return true;
|
||||
@ -519,12 +533,8 @@ TXDB.prototype.verifyInputs = co(function* verifyInputs(tx) {
|
||||
continue;
|
||||
}
|
||||
|
||||
path = yield this.wallet.hasPath(input.getAddress());
|
||||
|
||||
if (!path)
|
||||
continue;
|
||||
|
||||
orphans[i] = true;
|
||||
if (yield this.hasPath(input))
|
||||
orphans[i] = true;
|
||||
}
|
||||
|
||||
for (i = 0; i < tx.inputs.length; i++) {
|
||||
@ -627,11 +637,10 @@ TXDB.prototype.resolveOutputs = co(function* resolveOutputs(tx, resolved) {
|
||||
* double spenders, and verify inputs.
|
||||
* @private
|
||||
* @param {TX} tx
|
||||
* @param {PathInfo} info
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
TXDB.prototype.removeConflicts = co(function* removeConflicts(tx, info) {
|
||||
TXDB.prototype.removeConflicts = co(function* removeConflicts(tx) {
|
||||
var hash = tx.hash('hex');
|
||||
var i, input, prevout, spent;
|
||||
|
||||
@ -660,18 +669,16 @@ TXDB.prototype.removeConflicts = co(function* removeConflicts(tx, info) {
|
||||
/**
|
||||
* Add transaction, runs `confirm()` and `verify()`.
|
||||
* @param {TX} tx
|
||||
* @param {PathInfo} info
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
TXDB.prototype.add = co(function* add(tx) {
|
||||
var info = yield this.getPathInfo(tx);
|
||||
var result;
|
||||
|
||||
this.start();
|
||||
|
||||
try {
|
||||
result = yield this._add(tx, info);
|
||||
result = yield this._add(tx);
|
||||
} catch (e) {
|
||||
this.drop();
|
||||
throw e;
|
||||
@ -686,43 +693,53 @@ TXDB.prototype.add = co(function* add(tx) {
|
||||
* Add transaction without a lock.
|
||||
* @private
|
||||
* @param {TX} tx
|
||||
* @param {PathInfo} info
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
TXDB.prototype._add = co(function* add(tx, info) {
|
||||
TXDB.prototype._add = co(function* add(tx) {
|
||||
var hash = tx.hash('hex');
|
||||
var path, account;
|
||||
var i, result, input, output, coin;
|
||||
var prevout, key, spender, raw;
|
||||
var path, account, existing;
|
||||
var i, input, output, coin;
|
||||
var prevout, key, spender, raw, details;
|
||||
|
||||
assert(!tx.mutable, 'Cannot add mutable TX to wallet.');
|
||||
|
||||
existing = yield this.getTX(hash);
|
||||
|
||||
if (existing) {
|
||||
// Existing tx is already confirmed. Ignore.
|
||||
if (existing.height !== -1)
|
||||
return;
|
||||
|
||||
// The incoming tx won't confirm the
|
||||
// existing one anyway. Ignore.
|
||||
if (tx.height === -1)
|
||||
return;
|
||||
|
||||
// Attempt to confirm tx before adding it.
|
||||
return yield this.confirm(tx, existing);
|
||||
}
|
||||
|
||||
if (tx.height === -1) {
|
||||
// We ignore double spends from the mempool.
|
||||
if (yield this.isDoubleSpend(tx))
|
||||
return false;
|
||||
return;
|
||||
|
||||
// We ignore any unconfirmed txs
|
||||
// that are replace-by-fee.
|
||||
if (yield this.isRBF(tx)) {
|
||||
this.put(layout.r(hash), DUMMY);
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// This potentially removes double-spenders.
|
||||
yield this.removeConflicts(tx, info);
|
||||
yield this.removeConflicts(tx);
|
||||
|
||||
// Delete the replace-by-fee record.
|
||||
this.del(layout.r(hash));
|
||||
}
|
||||
|
||||
// Attempt to confirm tx before adding it.
|
||||
result = yield this.confirm(tx, info);
|
||||
|
||||
// Ignore if we already have this tx.
|
||||
if (result)
|
||||
return true;
|
||||
details = new Details(this, tx);
|
||||
|
||||
this.put(layout.t(hash), tx.toExtended());
|
||||
|
||||
@ -733,19 +750,6 @@ TXDB.prototype._add = co(function* add(tx, info) {
|
||||
|
||||
this.put(layout.m(tx.ps, hash), DUMMY);
|
||||
|
||||
for (i = 0; i < info.accounts.length; i++) {
|
||||
account = info.accounts[i];
|
||||
|
||||
this.put(layout.T(account, hash), DUMMY);
|
||||
|
||||
if (tx.height === -1)
|
||||
this.put(layout.P(account, hash), DUMMY);
|
||||
else
|
||||
this.put(layout.H(account, tx.height, hash), DUMMY);
|
||||
|
||||
this.put(layout.M(account, tx.ps, hash), DUMMY);
|
||||
}
|
||||
|
||||
// Consume unspent money or add orphans
|
||||
if (!tx.isCoinbase()) {
|
||||
for (i = 0; i < tx.inputs.length; i++) {
|
||||
@ -757,9 +761,11 @@ TXDB.prototype._add = co(function* add(tx, info) {
|
||||
if (!coin)
|
||||
continue;
|
||||
|
||||
path = info.getPath(coin);
|
||||
path = yield this.getPath(coin);
|
||||
assert(path);
|
||||
|
||||
details.addInput(i, path, coin);
|
||||
|
||||
key = prevout.hash + prevout.index;
|
||||
|
||||
spender = Outpoint.fromTX(tx, i).toRaw();
|
||||
@ -786,7 +792,7 @@ TXDB.prototype._add = co(function* add(tx, info) {
|
||||
// Add unspent outputs or resolve orphans.
|
||||
for (i = 0; i < tx.outputs.length; i++) {
|
||||
output = tx.outputs[i];
|
||||
path = info.getPath(output);
|
||||
path = yield this.getPath(output);
|
||||
key = hash + i;
|
||||
|
||||
// Do not add unspents for
|
||||
@ -794,6 +800,8 @@ TXDB.prototype._add = co(function* add(tx, info) {
|
||||
if (!path)
|
||||
continue;
|
||||
|
||||
details.addOutput(i, path);
|
||||
|
||||
coin = Coin.fromTX(tx, i);
|
||||
raw = coin.toRaw();
|
||||
|
||||
@ -809,20 +817,33 @@ TXDB.prototype._add = co(function* add(tx, info) {
|
||||
this.coinCache.set(key, raw);
|
||||
}
|
||||
|
||||
for (i = 0; i < details.accounts.length; i++) {
|
||||
account = details.accounts[i];
|
||||
|
||||
this.put(layout.T(account, hash), DUMMY);
|
||||
|
||||
if (tx.height === -1)
|
||||
this.put(layout.P(account, hash), DUMMY);
|
||||
else
|
||||
this.put(layout.H(account, tx.height, hash), DUMMY);
|
||||
|
||||
this.put(layout.M(account, tx.ps, hash), DUMMY);
|
||||
}
|
||||
|
||||
this.pending.tx++;
|
||||
this.put(layout.R, this.pending.commit());
|
||||
|
||||
// Clear any locked coins to free up memory.
|
||||
this.unlockTX(tx);
|
||||
|
||||
this.emit('tx', tx, info);
|
||||
this.emit('tx', tx, details);
|
||||
|
||||
if (tx.height !== -1)
|
||||
this.emit('confirmed', tx, info);
|
||||
this.emit('confirmed', tx, details);
|
||||
|
||||
this.emit('balance', this.pending.toBalance(), info);
|
||||
this.emit('balance', this.pending.toBalance(), details);
|
||||
|
||||
return true;
|
||||
return details;
|
||||
});
|
||||
|
||||
/**
|
||||
@ -839,7 +860,7 @@ TXDB.prototype._add = co(function* add(tx, info) {
|
||||
|
||||
TXDB.prototype.removeConflict = co(function* removeConflict(hash, ref) {
|
||||
var tx = yield this.getTX(hash);
|
||||
var info;
|
||||
var details;
|
||||
|
||||
assert(tx);
|
||||
|
||||
@ -847,14 +868,16 @@ TXDB.prototype.removeConflict = co(function* removeConflict(hash, ref) {
|
||||
|
||||
this.drop();
|
||||
|
||||
info = yield this.removeRecursive(tx);
|
||||
details = yield this.removeRecursive(tx);
|
||||
|
||||
this.start();
|
||||
|
||||
this.logger.warning('Removed conflict: %s.', tx.rhash);
|
||||
|
||||
// Emit the _removed_ transaction.
|
||||
this.emit('conflict', tx, info);
|
||||
this.emit('conflict', tx, details);
|
||||
|
||||
return details;
|
||||
});
|
||||
|
||||
/**
|
||||
@ -867,7 +890,7 @@ TXDB.prototype.removeConflict = co(function* removeConflict(hash, ref) {
|
||||
|
||||
TXDB.prototype.removeRecursive = co(function* removeRecursive(tx) {
|
||||
var hash = tx.hash('hex');
|
||||
var i, spent, stx, info;
|
||||
var i, spent, stx, details;
|
||||
|
||||
for (i = 0; i < tx.outputs.length; i++) {
|
||||
spent = yield this.getSpent(hash, i);
|
||||
@ -886,13 +909,13 @@ TXDB.prototype.removeRecursive = co(function* removeRecursive(tx) {
|
||||
this.start();
|
||||
|
||||
// Remove the spender.
|
||||
info = yield this.lazyRemove(tx);
|
||||
details = yield this.lazyRemove(tx);
|
||||
|
||||
assert(info);
|
||||
assert(details);
|
||||
|
||||
yield this.commit();
|
||||
|
||||
return info;
|
||||
return details;
|
||||
});
|
||||
|
||||
/**
|
||||
@ -1012,32 +1035,16 @@ TXDB.prototype.isSpending = co(function* isSpending(hash, index) {
|
||||
* Attempt to confirm a transaction.
|
||||
* @private
|
||||
* @param {TX} tx
|
||||
* @param {AddressMap} info
|
||||
* @returns {Promise} - Returns Boolean. `false` if
|
||||
* the transaction should be added to the database, `true` if the
|
||||
* transaction was confirmed, or should be ignored.
|
||||
*/
|
||||
|
||||
TXDB.prototype.confirm = co(function* confirm(tx, info) {
|
||||
TXDB.prototype.confirm = co(function* confirm(tx, existing) {
|
||||
var hash = tx.hash('hex');
|
||||
var i, account, existing, output, coin;
|
||||
var i, account, output, coin;
|
||||
var input, prevout, path, spender, coins;
|
||||
var key, raw;
|
||||
|
||||
existing = yield this.getTX(hash);
|
||||
|
||||
// Haven't seen this tx before, add it.
|
||||
if (!existing)
|
||||
return false;
|
||||
|
||||
// Existing tx is already confirmed. Ignore.
|
||||
if (existing.height !== -1)
|
||||
return true;
|
||||
|
||||
// The incoming tx won't confirm the
|
||||
// existing one anyway. Ignore.
|
||||
if (tx.height === -1)
|
||||
return true;
|
||||
var key, raw, details;
|
||||
|
||||
// Inject block properties.
|
||||
existing.ts = tx.ts;
|
||||
@ -1046,17 +1053,13 @@ TXDB.prototype.confirm = co(function* confirm(tx, info) {
|
||||
existing.block = tx.block;
|
||||
tx = existing;
|
||||
|
||||
details = new Details(this, tx);
|
||||
|
||||
this.put(layout.t(hash), tx.toExtended());
|
||||
|
||||
this.del(layout.p(hash));
|
||||
this.put(layout.h(tx.height, hash), DUMMY);
|
||||
|
||||
for (i = 0; i < info.accounts.length; i++) {
|
||||
account = info.accounts[i];
|
||||
this.del(layout.P(account, hash));
|
||||
this.put(layout.H(account, tx.height, hash), DUMMY);
|
||||
}
|
||||
|
||||
// Consume unspent money or add orphans
|
||||
if (!tx.isCoinbase()) {
|
||||
coins = yield this.fillHistory(tx);
|
||||
@ -1082,10 +1085,14 @@ TXDB.prototype.confirm = co(function* confirm(tx, info) {
|
||||
|
||||
assert(coin.height !== -1);
|
||||
|
||||
input.coin = coin;
|
||||
|
||||
// Only bother if this input is ours.
|
||||
path = info.getPath(coin);
|
||||
path = yield this.getPath(coin);
|
||||
assert(path);
|
||||
|
||||
details.addInput(i, path, coin);
|
||||
|
||||
key = prevout.hash + prevout.index;
|
||||
|
||||
this.del(layout.S(prevout.hash, prevout.index));
|
||||
@ -1102,12 +1109,15 @@ TXDB.prototype.confirm = co(function* confirm(tx, info) {
|
||||
|
||||
for (i = 0; i < tx.outputs.length; i++) {
|
||||
output = tx.outputs[i];
|
||||
path = yield this.getPath(output);
|
||||
key = hash + i;
|
||||
|
||||
// Only update coins if this output is ours.
|
||||
if (!info.hasPath(output))
|
||||
if (!path)
|
||||
continue;
|
||||
|
||||
details.addOutput(i, path);
|
||||
|
||||
// Update spent coin.
|
||||
yield this.updateSpentCoin(tx, i);
|
||||
|
||||
@ -1127,16 +1137,22 @@ TXDB.prototype.confirm = co(function* confirm(tx, info) {
|
||||
this.coinCache.set(key, raw);
|
||||
}
|
||||
|
||||
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.R, this.pending.commit());
|
||||
|
||||
// Clear any locked coins to free up memory.
|
||||
this.unlockTX(tx);
|
||||
|
||||
this.emit('tx', tx, info);
|
||||
this.emit('confirmed', tx, info);
|
||||
this.emit('balance', this.pending.toBalance(), info);
|
||||
this.emit('tx', tx, details);
|
||||
this.emit('confirmed', tx, details);
|
||||
this.emit('balance', this.pending.toBalance(), details);
|
||||
|
||||
return true;
|
||||
return details;
|
||||
});
|
||||
|
||||
/**
|
||||
@ -1146,12 +1162,12 @@ TXDB.prototype.confirm = co(function* confirm(tx, info) {
|
||||
*/
|
||||
|
||||
TXDB.prototype.remove = co(function* remove(hash) {
|
||||
var result;
|
||||
var details;
|
||||
|
||||
this.start();
|
||||
|
||||
try {
|
||||
result = yield this._remove(hash);
|
||||
details = yield this._remove(hash);
|
||||
} catch (e) {
|
||||
this.drop();
|
||||
throw e;
|
||||
@ -1159,7 +1175,7 @@ TXDB.prototype.remove = co(function* remove(hash) {
|
||||
|
||||
yield this.commit();
|
||||
|
||||
return result;
|
||||
return details;
|
||||
});
|
||||
|
||||
/**
|
||||
@ -1171,21 +1187,21 @@ TXDB.prototype.remove = co(function* remove(hash) {
|
||||
|
||||
TXDB.prototype._remove = co(function* remove(hash) {
|
||||
var tx = yield this.getTX(hash);
|
||||
var info;
|
||||
var details;
|
||||
|
||||
if (!tx)
|
||||
return;
|
||||
|
||||
this.drop();
|
||||
|
||||
info = yield this.removeRecursive(tx);
|
||||
details = yield this.removeRecursive(tx);
|
||||
|
||||
this.start();
|
||||
|
||||
if (!info)
|
||||
if (!details)
|
||||
return;
|
||||
|
||||
return info;
|
||||
return details;
|
||||
});
|
||||
|
||||
/**
|
||||
@ -1197,25 +1213,22 @@ TXDB.prototype._remove = co(function* remove(hash) {
|
||||
*/
|
||||
|
||||
TXDB.prototype.lazyRemove = co(function* lazyRemove(tx) {
|
||||
var info = yield this.getPathInfo(tx);
|
||||
if (!info)
|
||||
return;
|
||||
|
||||
return yield this.__remove(tx, info);
|
||||
return yield this.__remove(tx);
|
||||
});
|
||||
|
||||
/**
|
||||
* Remove a transaction from the database. Disconnect inputs.
|
||||
* @private
|
||||
* @param {TX} tx
|
||||
* @param {AddressMap} info
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
TXDB.prototype.__remove = co(function* remove(tx, info) {
|
||||
TXDB.prototype.__remove = co(function* remove(tx) {
|
||||
var hash = tx.hash('hex');
|
||||
var i, path, account, key, prevout;
|
||||
var input, output, coin, coins, raw;
|
||||
var input, output, coin, coins, raw, details;
|
||||
|
||||
details = new Details(this, tx);
|
||||
|
||||
this.del(layout.t(hash));
|
||||
|
||||
@ -1226,19 +1239,6 @@ TXDB.prototype.__remove = co(function* remove(tx, info) {
|
||||
|
||||
this.del(layout.m(tx.ps, hash));
|
||||
|
||||
for (i = 0; i < info.accounts.length; i++) {
|
||||
account = info.accounts[i];
|
||||
|
||||
this.del(layout.T(account, hash));
|
||||
|
||||
if (tx.height === -1)
|
||||
this.del(layout.P(account, hash));
|
||||
else
|
||||
this.del(layout.H(account, tx.height, hash));
|
||||
|
||||
this.del(layout.M(account, tx.ps, hash));
|
||||
}
|
||||
|
||||
if (!tx.isCoinbase()) {
|
||||
coins = yield this.fillHistory(tx);
|
||||
|
||||
@ -1251,9 +1251,11 @@ TXDB.prototype.__remove = co(function* remove(tx, info) {
|
||||
if (!coin)
|
||||
continue;
|
||||
|
||||
path = info.getPath(coin);
|
||||
path = yield this.getPath(coin);
|
||||
assert(path);
|
||||
|
||||
details.addInput(i, path, coin);
|
||||
|
||||
this.pending.unconfirmed += coin.value;
|
||||
|
||||
this.del(layout.s(prevout.hash, prevout.index));
|
||||
@ -1277,11 +1279,13 @@ TXDB.prototype.__remove = co(function* remove(tx, info) {
|
||||
for (i = 0; i < tx.outputs.length; i++) {
|
||||
output = tx.outputs[i];
|
||||
key = hash + i;
|
||||
path = info.getPath(output);
|
||||
path = yield this.getPath(output);
|
||||
|
||||
if (!path)
|
||||
continue;
|
||||
|
||||
details.addOutput(i, path);
|
||||
|
||||
this.pending.coin--;
|
||||
this.pending.unconfirmed -= output.value;
|
||||
|
||||
@ -1294,13 +1298,26 @@ TXDB.prototype.__remove = co(function* remove(tx, info) {
|
||||
this.coinCache.remove(key);
|
||||
}
|
||||
|
||||
for (i = 0; i < details.accounts.length; i++) {
|
||||
account = details.accounts[i];
|
||||
|
||||
this.del(layout.T(account, hash));
|
||||
|
||||
if (tx.height === -1)
|
||||
this.del(layout.P(account, hash));
|
||||
else
|
||||
this.del(layout.H(account, tx.height, hash));
|
||||
|
||||
this.del(layout.M(account, tx.ps, hash));
|
||||
}
|
||||
|
||||
this.pending.tx--;
|
||||
this.put(layout.R, this.pending.commit());
|
||||
|
||||
this.emit('remove tx', tx, info);
|
||||
this.emit('balance', this.pending.toBalance(), info);
|
||||
this.emit('remove tx', tx, details);
|
||||
this.emit('balance', this.pending.toBalance(), details);
|
||||
|
||||
return info;
|
||||
return details;
|
||||
});
|
||||
|
||||
/**
|
||||
@ -1310,12 +1327,12 @@ TXDB.prototype.__remove = co(function* remove(tx, info) {
|
||||
*/
|
||||
|
||||
TXDB.prototype.unconfirm = co(function* unconfirm(hash) {
|
||||
var result;
|
||||
var details;
|
||||
|
||||
this.start();
|
||||
|
||||
try {
|
||||
result = yield this._unconfirm(hash);
|
||||
details = yield this._unconfirm(hash);
|
||||
} catch (e) {
|
||||
this.drop();
|
||||
throw e;
|
||||
@ -1323,7 +1340,7 @@ TXDB.prototype.unconfirm = co(function* unconfirm(hash) {
|
||||
|
||||
yield this.commit();
|
||||
|
||||
return result;
|
||||
return details;
|
||||
});
|
||||
|
||||
/**
|
||||
@ -1335,29 +1352,23 @@ TXDB.prototype.unconfirm = co(function* unconfirm(hash) {
|
||||
|
||||
TXDB.prototype._unconfirm = co(function* unconfirm(hash) {
|
||||
var tx = yield this.getTX(hash);
|
||||
var info, result;
|
||||
var details;
|
||||
|
||||
if (!tx)
|
||||
return false;
|
||||
|
||||
info = yield this.getPathInfo(tx);
|
||||
details = yield this.__unconfirm(tx);
|
||||
|
||||
if (!info)
|
||||
return false;
|
||||
|
||||
result = yield this.__unconfirm(tx, info);
|
||||
|
||||
return result;
|
||||
return details;
|
||||
});
|
||||
|
||||
/**
|
||||
* Unconfirm a transaction. This is usually necessary after a reorg.
|
||||
* @param {Hash} hash
|
||||
* @param {AddressMap} info
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
TXDB.prototype.__unconfirm = co(function* unconfirm(tx, info) {
|
||||
TXDB.prototype.__unconfirm = co(function* unconfirm(tx) {
|
||||
var hash = tx.hash('hex');
|
||||
var height = tx.height;
|
||||
var i, account, output, key, coin, coins;
|
||||
@ -1368,17 +1379,13 @@ TXDB.prototype.__unconfirm = co(function* unconfirm(tx, info) {
|
||||
|
||||
tx.unsetBlock();
|
||||
|
||||
details = new Details(this, tx);
|
||||
|
||||
this.put(layout.t(hash), tx.toExtended());
|
||||
|
||||
this.put(layout.p(hash), DUMMY);
|
||||
this.del(layout.h(height, hash));
|
||||
|
||||
for (i = 0; i < info.accounts.length; i++) {
|
||||
account = info.accounts[i];
|
||||
this.put(layout.P(account, hash), DUMMY);
|
||||
this.del(layout.H(account, height, hash));
|
||||
}
|
||||
|
||||
// Consume unspent money or add orphans
|
||||
if (!tx.isCoinbase()) {
|
||||
coins = yield this.fillHistory(tx);
|
||||
@ -1396,9 +1403,11 @@ TXDB.prototype.__unconfirm = co(function* unconfirm(tx, info) {
|
||||
|
||||
raw = coin.toRaw();
|
||||
|
||||
path = info.getPath(coin);
|
||||
path = yield this.getPath(coin);
|
||||
assert(path);
|
||||
|
||||
details.addInput(i, path, coin);
|
||||
|
||||
key = prevout.hash + prevout.index;
|
||||
|
||||
spender = Outpoint.fromTX(tx, i).toRaw();
|
||||
@ -1417,6 +1426,7 @@ TXDB.prototype.__unconfirm = co(function* unconfirm(tx, info) {
|
||||
|
||||
for (i = 0; i < tx.outputs.length; i++) {
|
||||
output = tx.outputs[i];
|
||||
path = yield this.getPath(output);
|
||||
key = hash + i;
|
||||
|
||||
// Update spent coin.
|
||||
@ -1427,6 +1437,8 @@ TXDB.prototype.__unconfirm = co(function* unconfirm(tx, info) {
|
||||
if (!coin)
|
||||
continue;
|
||||
|
||||
details.addOutput(i, path);
|
||||
|
||||
coin.height = -1;
|
||||
raw = coin.toRaw();
|
||||
|
||||
@ -1438,12 +1450,18 @@ TXDB.prototype.__unconfirm = co(function* unconfirm(tx, info) {
|
||||
this.coinCache.set(key, raw);
|
||||
}
|
||||
|
||||
for (i = 0; i < details.accounts.length; i++) {
|
||||
account = details.accounts[i];
|
||||
this.put(layout.P(account, hash), DUMMY);
|
||||
this.del(layout.H(account, height, hash));
|
||||
}
|
||||
|
||||
this.put(layout.R, this.pending.commit());
|
||||
|
||||
this.emit('unconfirmed', tx, info);
|
||||
this.emit('balance', this.pending.toBalance(), info);
|
||||
this.emit('unconfirmed', tx, details);
|
||||
this.emit('balance', this.pending.toBalance(), details);
|
||||
|
||||
return info;
|
||||
return details;
|
||||
});
|
||||
|
||||
/**
|
||||
@ -2105,7 +2123,8 @@ TXDB.prototype.getDetails = co(function* getDetails(hash) {
|
||||
*/
|
||||
|
||||
TXDB.prototype.toDetails = co(function* toDetails(tx) {
|
||||
var i, out, txs, details, info;
|
||||
var i, out, txs, details;
|
||||
var coins, coin, path, input, output;
|
||||
|
||||
if (Array.isArray(tx)) {
|
||||
out = [];
|
||||
@ -2124,14 +2143,23 @@ TXDB.prototype.toDetails = co(function* toDetails(tx) {
|
||||
return out;
|
||||
}
|
||||
|
||||
yield this.fillHistory(tx);
|
||||
details = new Details(this, tx);
|
||||
|
||||
info = yield this.getPathInfo(tx);
|
||||
coins = yield this.fillHistory(tx);
|
||||
|
||||
if (!info)
|
||||
throw new Error('Info not found.');
|
||||
for (i = 0; i < tx.inputs.length; i++) {
|
||||
coin = coins[i];
|
||||
path = yield this.getPath(coin);
|
||||
details.addInput(i, path, coin);
|
||||
}
|
||||
|
||||
return info.toDetails();
|
||||
for (i = 0; i < tx.outputs.length; i++) {
|
||||
output = tx.outputs[i];
|
||||
path = yield this.getPath(output);
|
||||
details.addOutput(i, path);
|
||||
}
|
||||
|
||||
return details;
|
||||
});
|
||||
|
||||
/**
|
||||
@ -2483,6 +2511,147 @@ function Orphan(tx, i) {
|
||||
this.index = i;
|
||||
}
|
||||
|
||||
function cmp(a, b) {
|
||||
return a - b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transaction Details
|
||||
* @constructor
|
||||
* @param {PathInfo} info
|
||||
*/
|
||||
|
||||
function Details(txdb, tx) {
|
||||
if (!(this instanceof Details))
|
||||
return new Details(txdb, tx);
|
||||
|
||||
this.db = txdb.walletdb;
|
||||
this.network = this.db.network;
|
||||
this.wid = txdb.wallet.wid;
|
||||
this.id = txdb.wallet.id;
|
||||
this.hash = tx.hash('hex');
|
||||
this.height = tx.height;
|
||||
this.block = tx.block;
|
||||
this.index = tx.index;
|
||||
this.confirmations = tx.getConfirmations(this.db.height);
|
||||
this.fee = tx.getFee();
|
||||
this.ts = tx.ts;
|
||||
this.ps = tx.ps;
|
||||
this.tx = tx;
|
||||
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
this.accounts = [];
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
Details.prototype.init = function init() {
|
||||
var i, input, output, member;
|
||||
|
||||
for (i = 0; i < this.tx.inputs.length; i++) {
|
||||
input = this.tx.inputs[i];
|
||||
member = new DetailsMember();
|
||||
member.address = input.getAddress();
|
||||
this.inputs.push(member);
|
||||
}
|
||||
|
||||
for (i = 0; i < this.tx.outputs.length; i++) {
|
||||
output = this.tx.outputs[i];
|
||||
member = new DetailsMember();
|
||||
member.value = output.value;
|
||||
member.address = output.getAddress();
|
||||
this.outputs.push(member);
|
||||
}
|
||||
};
|
||||
|
||||
Details.prototype.addInput = function addInput(i, path, coin) {
|
||||
var member = this.inputs[i];
|
||||
|
||||
if (coin) {
|
||||
member.value = coin.value;
|
||||
member.address = coin.getAddress();
|
||||
}
|
||||
|
||||
if (path) {
|
||||
member.path = path;
|
||||
utils.binaryInsert(this.accounts, path.account, cmp, true);
|
||||
}
|
||||
};
|
||||
|
||||
Details.prototype.addOutput = function addOutput(i, path) {
|
||||
var member = this.outputs[i];
|
||||
|
||||
if (path) {
|
||||
member.path = path;
|
||||
utils.binaryInsert(this.accounts, path.account, cmp, true);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert details to a more json-friendly object.
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
Details.prototype.toJSON = function toJSON() {
|
||||
var self = this;
|
||||
return {
|
||||
wid: this.wid,
|
||||
id: this.id,
|
||||
hash: utils.revHex(this.hash),
|
||||
height: this.height,
|
||||
block: this.block ? utils.revHex(this.block) : null,
|
||||
ts: this.ts,
|
||||
ps: this.ps,
|
||||
index: this.index,
|
||||
fee: utils.btc(this.fee),
|
||||
confirmations: this.confirmations,
|
||||
inputs: this.inputs.map(function(input) {
|
||||
return input.toJSON(self.network);
|
||||
}),
|
||||
outputs: this.outputs.map(function(output) {
|
||||
return output.toJSON(self.network);
|
||||
}),
|
||||
tx: this.tx.toRaw().toString('hex')
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Transaction Details Member
|
||||
* @constructor
|
||||
* @property {Number} value
|
||||
* @property {Address} address
|
||||
* @property {Path} path
|
||||
*/
|
||||
|
||||
function DetailsMember() {
|
||||
if (!(this instanceof DetailsMember))
|
||||
return new DetailsMember();
|
||||
|
||||
this.value = 0;
|
||||
this.address = null;
|
||||
this.path = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the member to a more json-friendly object.
|
||||
* @param {Network} network
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
DetailsMember.prototype.toJSON = function toJSON(network) {
|
||||
return {
|
||||
value: utils.btc(this.value),
|
||||
address: this.address
|
||||
? this.address.toBase58(network)
|
||||
: null,
|
||||
path: this.path
|
||||
? this.path.toJSON()
|
||||
: null
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
@ -1622,14 +1622,20 @@ Wallet.prototype.getOutputPaths = co(function* getOutputPaths(tx) {
|
||||
* (true if new addresses were allocated).
|
||||
*/
|
||||
|
||||
Wallet.prototype.syncOutputDepth = co(function* syncOutputDepth(info) {
|
||||
Wallet.prototype.syncOutputDepth = co(function* syncOutputDepth(details) {
|
||||
var derived = [];
|
||||
var accounts = {};
|
||||
var i, j, path, paths, account;
|
||||
var receive, change, nested, ring;
|
||||
|
||||
for (i = 0; i < info.paths.length; i++) {
|
||||
path = info.paths[i];
|
||||
if (!details)
|
||||
return derived;
|
||||
|
||||
for (i = 0; i < details.outputs.length; i++) {
|
||||
path = details.outputs[i].path;
|
||||
|
||||
if (!path)
|
||||
continue;
|
||||
|
||||
if (path.index === -1)
|
||||
continue;
|
||||
@ -1842,14 +1848,13 @@ Wallet.prototype._add = co(function* add(tx) {
|
||||
*/
|
||||
|
||||
Wallet.prototype._insert = co(function* insert(tx) {
|
||||
var info = yield this.getPathInfo(tx);
|
||||
var result, derived;
|
||||
var details, derived;
|
||||
|
||||
this.txdb.start();
|
||||
|
||||
try {
|
||||
result = yield this.txdb._add(tx, info);
|
||||
derived = yield this.syncOutputDepth(info);
|
||||
details = yield this.txdb._add(tx);
|
||||
derived = yield this.syncOutputDepth(details);
|
||||
} catch (e) {
|
||||
this.txdb.drop();
|
||||
throw e;
|
||||
@ -1862,7 +1867,7 @@ Wallet.prototype._insert = co(function* insert(tx) {
|
||||
this.emit('address', derived);
|
||||
}
|
||||
|
||||
return result;
|
||||
return details;
|
||||
});
|
||||
|
||||
/**
|
||||
@ -1986,27 +1991,6 @@ Wallet.prototype.getLocked = function getLocked() {
|
||||
return this.txdb.getLocked();
|
||||
};
|
||||
|
||||
/**
|
||||
* Map a transactions' addresses to wallet IDs.
|
||||
* @param {TX} tx
|
||||
* @returns {Promise} - Returns {@link PathInfo}.
|
||||
*/
|
||||
|
||||
Wallet.prototype.getPathInfo = co(function* getPathInfo(tx) {
|
||||
var hashes = tx.getHashes('hex');
|
||||
var paths = [];
|
||||
var i, hash, path;
|
||||
|
||||
for (i = 0; i < hashes.length; i++) {
|
||||
hash = hashes[i];
|
||||
path = yield this.getPath(hash);
|
||||
if (path)
|
||||
paths.push(path);
|
||||
}
|
||||
|
||||
return new PathInfo(this, tx, paths);
|
||||
});
|
||||
|
||||
/**
|
||||
* Get all transactions in transaction history (accesses db).
|
||||
* @param {(String|Number)?} acct
|
||||
|
||||
Loading…
Reference in New Issue
Block a user