wallet: tests passing.
This commit is contained in:
parent
55f5ff9493
commit
c6d7c43485
@ -199,10 +199,10 @@ TXDB.prototype.saveCredit = async function saveCredit(b, credit, path) {
|
||||
const coin = credit.coin;
|
||||
const raw = credit.toRaw();
|
||||
|
||||
await this.addOutpointMap(b, coin.hash, coin.index);
|
||||
|
||||
b.put(layout.c(coin.hash, coin.index), raw);
|
||||
b.put(layout.C(path.account, coin.hash, coin.index), null);
|
||||
|
||||
return this.addOutpointMap(b, coin.hash, coin.index);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -214,10 +214,10 @@ TXDB.prototype.saveCredit = async function saveCredit(b, credit, path) {
|
||||
TXDB.prototype.removeCredit = async function removeCredit(b, credit, path) {
|
||||
const coin = credit.coin;
|
||||
|
||||
await this.removeOutpointMap(b, coin.hash, coin.index);
|
||||
|
||||
b.del(layout.c(coin.hash, coin.index));
|
||||
b.del(layout.C(path.account, coin.hash, coin.index));
|
||||
|
||||
return this.removeOutpointMap(b, coin.hash, coin.index);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -247,94 +247,6 @@ TXDB.prototype.unspendCredit = function unspendCredit(b, tx, index) {
|
||||
b.del(layout.d(spender.hash, spender.index));
|
||||
};
|
||||
|
||||
/**
|
||||
* Write input record.
|
||||
* @param {TX} tx
|
||||
* @param {Number} index
|
||||
*/
|
||||
|
||||
TXDB.prototype.writeInput = function writeInput(b, tx, index) {
|
||||
const prevout = tx.inputs[index].prevout;
|
||||
const spender = Outpoint.fromTX(tx, index);
|
||||
b.put(layout.s(prevout.hash, prevout.index), spender.toRaw());
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove input record.
|
||||
* @param {TX} tx
|
||||
* @param {Number} index
|
||||
*/
|
||||
|
||||
TXDB.prototype.removeInput = function removeInput(b, tx, index) {
|
||||
const prevout = tx.inputs[index].prevout;
|
||||
b.del(layout.s(prevout.hash, prevout.index));
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolve orphan input.
|
||||
* @param {TX} tx
|
||||
* @param {Number} index
|
||||
* @param {Number} height
|
||||
* @param {Path} path
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
TXDB.prototype.resolveInput = async function resolveInput(b, state, tx, index, height, path, own) {
|
||||
const hash = tx.hash('hex');
|
||||
const spent = await this.getSpent(hash, index);
|
||||
|
||||
if (!spent)
|
||||
return false;
|
||||
|
||||
// If we have an undo coin, we
|
||||
// already knew about this input.
|
||||
if (await this.hasSpentCoin(spent))
|
||||
return false;
|
||||
|
||||
// Get the spending transaction so
|
||||
// we can properly add the undo coin.
|
||||
const stx = await this.getTX(spent.hash);
|
||||
assert(stx);
|
||||
|
||||
// Crete the credit and add the undo coin.
|
||||
const credit = Credit.fromTX(tx, index, height);
|
||||
credit.own = own;
|
||||
|
||||
this.spendCredit(b, credit, stx.tx, spent.index);
|
||||
|
||||
// If the spender is unconfirmed, save
|
||||
// the credit as well, and mark it as
|
||||
// unspent in the mempool. This is the
|
||||
// same behavior `insert` would have
|
||||
// done for inputs. We're just doing
|
||||
// it retroactively.
|
||||
if (stx.height === -1) {
|
||||
credit.spent = true;
|
||||
await this.saveCredit(b, credit, path);
|
||||
if (height !== -1)
|
||||
state.confirmed += credit.coin.value;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test an entire transaction to see
|
||||
* if any of its outpoints are a double-spend.
|
||||
* @param {TX} tx
|
||||
* @returns {Promise} - Returns Boolean.
|
||||
*/
|
||||
|
||||
TXDB.prototype.isDoubleSpend = async function isDoubleSpend(tx) {
|
||||
for (const {prevout} of tx.inputs) {
|
||||
const spent = await this.isSpent(prevout.hash, prevout.index);
|
||||
if (spent)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test a whether a coin has been spent.
|
||||
* @param {Hash} hash
|
||||
@ -461,23 +373,23 @@ TXDB.prototype.getBlock = async function getBlock(height) {
|
||||
|
||||
TXDB.prototype.addBlock = async function addBlock(b, hash, meta) {
|
||||
const key = layout.b(meta.height);
|
||||
|
||||
let data = await this.get(key);
|
||||
let block;
|
||||
const data = await this.get(key);
|
||||
|
||||
if (!data) {
|
||||
block = BlockRecord.fromMeta(meta);
|
||||
data = block.toRaw();
|
||||
const block = BlockRecord.fromMeta(meta);
|
||||
block.add(hash);
|
||||
b.put(key, block.toRaw());
|
||||
return;
|
||||
}
|
||||
|
||||
block = Buffer.allocUnsafe(data.length + 32);
|
||||
data.copy(block, 0);
|
||||
const raw = Buffer.allocUnsafe(data.length + 32);
|
||||
data.copy(raw, 0);
|
||||
|
||||
const size = block.readUInt32LE(40, true);
|
||||
block.writeUInt32LE(size + 1, 40, true);
|
||||
hash.copy(block, data.length);
|
||||
const size = raw.readUInt32LE(40, true);
|
||||
raw.writeUInt32LE(size + 1, 40, true);
|
||||
hash.copy(raw, data.length);
|
||||
|
||||
b.put(key, block);
|
||||
b.put(key, raw);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -504,10 +416,10 @@ TXDB.prototype.removeBlock = async function removeBlock(b, hash, height) {
|
||||
return;
|
||||
}
|
||||
|
||||
const block = data.slice(0, -32);
|
||||
block.writeUInt32LE(size - 1, 40, true);
|
||||
const raw = data.slice(0, -32);
|
||||
raw.writeUInt32LE(size - 1, 40, true);
|
||||
|
||||
b.put(key, block);
|
||||
b.put(key, raw);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -607,8 +519,7 @@ TXDB.prototype.add = async function add(tx, block) {
|
||||
TXDB.prototype.insert = async function insert(wtx, block) {
|
||||
const b = this.bucket();
|
||||
const state = this.state.clone();
|
||||
const tx = wtx.tx;
|
||||
const hash = wtx.hash;
|
||||
const {tx, hash} = wtx;
|
||||
const height = block ? block.height : -1;
|
||||
const details = new Details(this, wtx, block);
|
||||
const accounts = new Set();
|
||||
@ -620,35 +531,13 @@ TXDB.prototype.insert = async function insert(wtx, block) {
|
||||
// We need to potentially spend some coins here.
|
||||
for (let i = 0; i < tx.inputs.length; i++) {
|
||||
const input = tx.inputs[i];
|
||||
const prevout = input.prevout;
|
||||
const credit = await this.getCredit(prevout.hash, prevout.index);
|
||||
const {hash, index} = input.prevout;
|
||||
const credit = await this.getCredit(hash, index);
|
||||
|
||||
if (!credit) {
|
||||
// Maintain an stxo list for every
|
||||
// spent input (even ones we don't
|
||||
// recognize). This is used for
|
||||
// detecting double-spends (as best
|
||||
// we can), as well as resolving
|
||||
// inputs we didn't know were ours
|
||||
// at the time. This built-in error
|
||||
// correction is not technically
|
||||
// necessary assuming no messages
|
||||
// are ever missed from the mempool,
|
||||
// but shit happens.
|
||||
this.writeInput(b, tx, i);
|
||||
if (!credit)
|
||||
continue;
|
||||
}
|
||||
|
||||
const coin = credit.coin;
|
||||
|
||||
// Do some verification.
|
||||
if (!block) {
|
||||
if (!await this.verifyInput(tx, i, coin)) {
|
||||
this.clear();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const path = await this.getPath(coin);
|
||||
assert(path);
|
||||
|
||||
@ -705,13 +594,6 @@ TXDB.prototype.insert = async function insert(wtx, block) {
|
||||
details.setOutput(i, path);
|
||||
accounts.add(path.account);
|
||||
|
||||
// Attempt to resolve an input we
|
||||
// did not know was ours at the time.
|
||||
if (await this.resolveInput(b, state, tx, i, height, path, own)) {
|
||||
updated = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
const credit = Credit.fromTX(tx, i, height);
|
||||
credit.own = own;
|
||||
|
||||
@ -728,11 +610,8 @@ TXDB.prototype.insert = async function insert(wtx, block) {
|
||||
|
||||
// If this didn't update any coins,
|
||||
// it's not our transaction.
|
||||
if (!updated) {
|
||||
// Clear the spent list inserts.
|
||||
this.clear();
|
||||
if (!updated)
|
||||
return null;
|
||||
}
|
||||
|
||||
// Save and index the transaction record.
|
||||
b.put(layout.t(hash), wtx.toRaw());
|
||||
@ -821,7 +700,7 @@ TXDB.prototype.confirm = async function confirm(hash, block) {
|
||||
*/
|
||||
|
||||
TXDB.prototype._confirm = async function _confirm(wtx, block) {
|
||||
const b = this.batch();
|
||||
const b = this.bucket();
|
||||
const state = this.state.clone();
|
||||
const tx = wtx.tx;
|
||||
const hash = wtx.hash;
|
||||
@ -839,14 +718,14 @@ TXDB.prototype._confirm = async function _confirm(wtx, block) {
|
||||
// from the utxo state.
|
||||
for (let i = 0; i < tx.inputs.length; i++) {
|
||||
const input = tx.inputs[i];
|
||||
const prevout = input.prevout;
|
||||
const {hash, index} = input.prevout;
|
||||
|
||||
let credit = credits[i];
|
||||
|
||||
// There may be new credits available
|
||||
// that we haven't seen yet.
|
||||
if (!credit) {
|
||||
credit = await this.getCredit(prevout.hash, prevout.index);
|
||||
credit = await this.getCredit(hash, index);
|
||||
|
||||
if (!credit)
|
||||
continue;
|
||||
@ -904,10 +783,8 @@ TXDB.prototype._confirm = async function _confirm(wtx, block) {
|
||||
|
||||
// Update coin height and confirmed
|
||||
// balance. Save once again.
|
||||
const coin = credit.coin;
|
||||
coin.height = height;
|
||||
|
||||
state.confirmed += output.value;
|
||||
credit.coin.height = height;
|
||||
|
||||
await this.saveCredit(b, credit, path);
|
||||
}
|
||||
@ -970,8 +847,7 @@ TXDB.prototype.remove = async function remove(hash) {
|
||||
TXDB.prototype.erase = async function erase(wtx, block) {
|
||||
const b = this.bucket();
|
||||
const state = this.state.clone();
|
||||
const tx = wtx.tx;
|
||||
const hash = wtx.hash;
|
||||
const {tx, hash} = wtx;
|
||||
const height = block ? block.height : -1;
|
||||
const details = new Details(this, wtx, block);
|
||||
const accounts = new Set();
|
||||
@ -985,13 +861,8 @@ TXDB.prototype.erase = async function erase(wtx, block) {
|
||||
for (let i = 0; i < tx.inputs.length; i++) {
|
||||
const credit = credits[i];
|
||||
|
||||
if (!credit) {
|
||||
// This input never had an undo
|
||||
// coin, but remove it from the
|
||||
// stxo set.
|
||||
this.removeInput(b, tx, i);
|
||||
if (!credit)
|
||||
continue;
|
||||
}
|
||||
|
||||
const coin = credit.coin;
|
||||
const path = await this.getPath(coin);
|
||||
@ -1166,9 +1037,7 @@ TXDB.prototype.unconfirm = async function unconfirm(hash) {
|
||||
TXDB.prototype.disconnect = async function disconnect(wtx, block) {
|
||||
const b = this.bucket();
|
||||
const state = this.state.clone();
|
||||
const tx = wtx.tx;
|
||||
const hash = wtx.hash;
|
||||
const height = block.height;
|
||||
const {tx, hash, height} = wtx;
|
||||
const details = new Details(this, wtx, block);
|
||||
const accounts = new Set();
|
||||
|
||||
@ -1265,7 +1134,7 @@ TXDB.prototype.disconnect = async function disconnect(wtx, block) {
|
||||
this.state = state;
|
||||
|
||||
this.emit('unconfirmed', tx, details);
|
||||
this.emit('balance', this.pending.toBalance(), details);
|
||||
this.emit('balance', state.toBalance(), details);
|
||||
|
||||
return details;
|
||||
};
|
||||
@ -1352,24 +1221,6 @@ TXDB.prototype.removeConflicts = async function removeConflicts(tx, conf) {
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Attempt to verify an input.
|
||||
* @private
|
||||
* @param {TX} tx
|
||||
* @param {Number} index
|
||||
* @param {Coin} coin
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
TXDB.prototype.verifyInput = async function verifyInput(tx, index, coin) {
|
||||
const flags = Script.flags.MANDATORY_VERIFY_FLAGS;
|
||||
|
||||
if (!this.options.verify)
|
||||
return true;
|
||||
|
||||
return await tx.verifyInputAsync(index, coin, flags);
|
||||
};
|
||||
|
||||
/**
|
||||
* Lock all coins in a transaction.
|
||||
* @param {TX} tx
|
||||
@ -2173,7 +2024,7 @@ TXDB.prototype.hasSpentCoin = function hasSpentCoin(spent) {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
TXDB.prototype.updateSpentCoin = async function updateSpentCoin(tx, index, height) {
|
||||
TXDB.prototype.updateSpentCoin = async function updateSpentCoin(b, tx, index, height) {
|
||||
const prevout = Outpoint.fromTX(tx, index);
|
||||
const spent = await this.getSpent(prevout.hash, prevout.index);
|
||||
|
||||
@ -2187,7 +2038,7 @@ TXDB.prototype.updateSpentCoin = async function updateSpentCoin(tx, index, heigh
|
||||
|
||||
coin.height = height;
|
||||
|
||||
this.put(layout.d(spent.hash, spent.index), coin.toRaw());
|
||||
b.put(layout.d(spent.hash, spent.index), coin.toRaw());
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -42,12 +42,6 @@ const U32 = encoding.U32;
|
||||
* @alias module:wallet.WalletDB
|
||||
* @constructor
|
||||
* @param {Object} options
|
||||
* @param {String?} options.name - Database name.
|
||||
* @param {String?} options.location - Database file location.
|
||||
* @param {String?} options.db - Database backend (`"leveldb"` by default).
|
||||
* @param {Boolean?} options.verify - Verify transactions as they
|
||||
* come in (note that this will not happen on the worker pool).
|
||||
* @property {Boolean} loaded
|
||||
*/
|
||||
|
||||
function WalletDB(options) {
|
||||
|
||||
@ -19,6 +19,7 @@ const Input = require('../lib/primitives/input');
|
||||
const Outpoint = require('../lib/primitives/outpoint');
|
||||
const Script = require('../lib/script/script');
|
||||
const HD = require('../lib/hd');
|
||||
const U32 = encoding.U32;
|
||||
|
||||
const KEY1 = 'xprv9s21ZrQH143K3Aj6xQBymM31Zb4BVc7wxqfUhMZrzewdDVCt'
|
||||
+ 'qUP9iWfcHgJofs25xbaUpCps9GDXj83NiWvQCAkWQhVj5J4CorfnpKX94AZ';
|
||||
@ -26,15 +27,9 @@ const KEY1 = 'xprv9s21ZrQH143K3Aj6xQBymM31Zb4BVc7wxqfUhMZrzewdDVCt'
|
||||
const KEY2 = 'xprv9s21ZrQH143K3mqiSThzPtWAabQ22Pjp3uSNnZ53A5bQ4udp'
|
||||
+ 'faKekc2m4AChLYH1XDzANhrSdxHYWUeTWjYJwFwWFyHkTMnMeAcW4JyRCZa';
|
||||
|
||||
const workers = new WorkerPool({
|
||||
enabled: true
|
||||
});
|
||||
|
||||
const wdb = new WalletDB({
|
||||
db: 'memory',
|
||||
verify: true,
|
||||
workers
|
||||
});
|
||||
const enabled = true;
|
||||
const workers = new WorkerPool({ enabled });
|
||||
const wdb = new WalletDB({ workers });
|
||||
|
||||
let currentWallet = null;
|
||||
let importedWallet = null;
|
||||
@ -42,27 +37,32 @@ let importedKey = null;
|
||||
let doubleSpendWallet = null;
|
||||
let doubleSpendCoin = null;
|
||||
|
||||
let globalTime = util.now();
|
||||
let globalHeight = 1;
|
||||
function prevBlock(wdb) {
|
||||
assert(wdb.state.height > 0);
|
||||
return fakeBlock(wdb.state.height - 1);
|
||||
};
|
||||
|
||||
function nextBlock() {
|
||||
const height = globalHeight++;
|
||||
const time = globalTime++;
|
||||
function curBlock(wdb) {
|
||||
return fakeBlock(wdb.state.height);
|
||||
};
|
||||
|
||||
const prevHead = encoding.U32(height - 1);
|
||||
const prevHash = digest.hash256(prevHead);
|
||||
function nextBlock(wdb) {
|
||||
return fakeBlock(wdb.state.height + 1);
|
||||
}
|
||||
|
||||
const head = encoding.U32(height);
|
||||
const hash = digest.hash256(head);
|
||||
function fakeBlock(height) {
|
||||
const prev = digest.hash256(U32((height - 1) >>> 0));
|
||||
const hash = digest.hash256(U32(height >>> 0));
|
||||
const root = digest.hash256(U32((height | 0x80000000) >>> 0));
|
||||
|
||||
return {
|
||||
hash: hash.toString('hex'),
|
||||
height: height,
|
||||
prevBlock: prevHash.toString('hex'),
|
||||
time: time,
|
||||
merkleRoot: encoding.NULL_HASH,
|
||||
prevBlock: prev.toString('hex'),
|
||||
merkleRoot: root.toString('hex'),
|
||||
time: 500000000 + (height * (10 * 60)),
|
||||
bits: 0,
|
||||
nonce: 0,
|
||||
bits: 0
|
||||
height: height
|
||||
};
|
||||
}
|
||||
|
||||
@ -73,10 +73,7 @@ function dummyInput() {
|
||||
|
||||
async function testP2PKH(witness, nesting) {
|
||||
const flags = Script.flags.STANDARD_VERIFY_FLAGS;
|
||||
|
||||
const wallet = await wdb.create({
|
||||
witness
|
||||
});
|
||||
const wallet = await wdb.create({ witness });
|
||||
|
||||
const addr = Address.fromString(wallet.getAddress('string'));
|
||||
|
||||
@ -157,11 +154,9 @@ async function testP2SH(witness, nesting) {
|
||||
fund.addOutput(nesting ? nestedAddr1 : addr1, 5460 * 10);
|
||||
|
||||
// Simulate a confirmation
|
||||
const block = nextBlock();
|
||||
|
||||
assert.strictEqual(alice.account[receiveDepth], 1);
|
||||
|
||||
await wdb.addBlock(block, [fund.toTX()]);
|
||||
await wdb.addBlock(nextBlock(wdb), [fund.toTX()]);
|
||||
|
||||
assert.strictEqual(alice.account[receiveDepth], 2);
|
||||
assert.strictEqual(alice.account.changeDepth, 1);
|
||||
@ -205,9 +200,7 @@ async function testP2SH(witness, nesting) {
|
||||
|
||||
// Simulate a confirmation
|
||||
{
|
||||
const block = nextBlock();
|
||||
|
||||
await wdb.addBlock(block, [tx]);
|
||||
await wdb.addBlock(nextBlock(wdb), [tx]);
|
||||
|
||||
assert.strictEqual(alice.account[receiveDepth], 2);
|
||||
assert.strictEqual(alice.account.changeDepth, 2);
|
||||
@ -360,53 +353,39 @@ describe('Wallet', function() {
|
||||
// balance: 11000
|
||||
await alice.sign(f1);
|
||||
|
||||
const fake = new MTX();
|
||||
fake.addTX(t1, 1); // 1000 (already redeemed)
|
||||
fake.addOutput(alice.getAddress(), 500);
|
||||
|
||||
// Script inputs but do not sign
|
||||
await alice.template(fake);
|
||||
// Fake signature
|
||||
const input = fake.inputs[0];
|
||||
input.script.setData(0, encoding.ZERO_SIG);
|
||||
input.script.compile();
|
||||
// balance: 11000
|
||||
|
||||
// Fake TX should temporarily change output.
|
||||
{
|
||||
await wdb.addTX(fake.toTX());
|
||||
await wdb.addTX(t4.toTX());
|
||||
|
||||
const balance = await alice.getBalance();
|
||||
assert.strictEqual(balance.unconfirmed, 22500);
|
||||
}
|
||||
|
||||
{
|
||||
await wdb.addTX(t1.toTX());
|
||||
|
||||
const balance = await alice.getBalance();
|
||||
assert.strictEqual(balance.unconfirmed, 72500);
|
||||
}
|
||||
|
||||
{
|
||||
await wdb.addTX(t2.toTX());
|
||||
|
||||
const balance = await alice.getBalance();
|
||||
assert.strictEqual(balance.unconfirmed, 46500);
|
||||
}
|
||||
|
||||
{
|
||||
await wdb.addTX(t3.toTX());
|
||||
|
||||
const balance = await alice.getBalance();
|
||||
assert.strictEqual(balance.unconfirmed, 22000);
|
||||
}
|
||||
|
||||
{
|
||||
await wdb.addTX(t1.toTX());
|
||||
|
||||
const balance = await alice.getBalance();
|
||||
assert.strictEqual(balance.unconfirmed, 73000);
|
||||
}
|
||||
|
||||
{
|
||||
await wdb.addTX(t2.toTX());
|
||||
|
||||
const balance = await alice.getBalance();
|
||||
assert.strictEqual(balance.unconfirmed, 71000);
|
||||
}
|
||||
|
||||
{
|
||||
await wdb.addTX(t3.toTX());
|
||||
|
||||
const balance = await alice.getBalance();
|
||||
assert.strictEqual(balance.unconfirmed, 69000);
|
||||
}
|
||||
|
||||
{
|
||||
await wdb.addTX(f1.toTX());
|
||||
|
||||
const balance = await alice.getBalance();
|
||||
assert.strictEqual(balance.unconfirmed, 11000);
|
||||
assert.strictEqual(balance.unconfirmed, 58000);
|
||||
|
||||
const txs = await alice.getHistory();
|
||||
assert(txs.some((wtx) => {
|
||||
@ -423,11 +402,46 @@ describe('Wallet', function() {
|
||||
return wtx.tx.hash('hex') === f1.hash('hex');
|
||||
}));
|
||||
}
|
||||
|
||||
// Should recover from missed txs on block.
|
||||
await wdb.addBlock(nextBlock(wdb), [
|
||||
t1.toTX(),
|
||||
t2.toTX(),
|
||||
t3.toTX(),
|
||||
t4.toTX(),
|
||||
f1.toTX()
|
||||
]);
|
||||
|
||||
{
|
||||
const balance = await alice.getBalance();
|
||||
assert.strictEqual(balance.unconfirmed, 11000);
|
||||
assert.strictEqual(balance.confirmed, 11000);
|
||||
|
||||
const txs = await alice.getHistory();
|
||||
assert(txs.some((wtx) => {
|
||||
return wtx.hash === f1.hash('hex');
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
const balance = await bob.getBalance();
|
||||
assert.strictEqual(balance.unconfirmed, 10000);
|
||||
assert.strictEqual(balance.confirmed, 10000);
|
||||
|
||||
const txs = await bob.getHistory();
|
||||
assert(txs.some((wtx) => {
|
||||
return wtx.tx.hash('hex') === f1.hash('hex');
|
||||
}));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
it('should cleanup spenders after double-spend', async () => {
|
||||
const wallet = doubleSpendWallet;
|
||||
|
||||
// Reorg and unconfirm all previous txs.
|
||||
await wdb.removeBlock(curBlock(wdb));
|
||||
|
||||
{
|
||||
const txs = await wallet.getHistory();
|
||||
assert.strictEqual(txs.length, 5);
|
||||
@ -441,6 +455,7 @@ describe('Wallet', function() {
|
||||
{
|
||||
const balance = await wallet.getBalance();
|
||||
assert.strictEqual(balance.unconfirmed, 11000);
|
||||
assert.strictEqual(balance.confirmed, 0);
|
||||
}
|
||||
|
||||
{
|
||||
@ -468,14 +483,6 @@ describe('Wallet', function() {
|
||||
});
|
||||
|
||||
it('should handle missed txs without resolution', async () => {
|
||||
const wdb = new WalletDB({
|
||||
name: 'wallet-test',
|
||||
db: 'memory',
|
||||
verify: false
|
||||
});
|
||||
|
||||
await wdb.open();
|
||||
|
||||
const alice = await wdb.create();
|
||||
const bob = await wdb.create();
|
||||
|
||||
@ -534,20 +541,20 @@ describe('Wallet', function() {
|
||||
{
|
||||
await wdb.addTX(t2.toTX());
|
||||
const balance = await alice.getBalance();
|
||||
assert.strictEqual(balance.unconfirmed, 47000);
|
||||
assert.strictEqual(balance.unconfirmed, 71000);
|
||||
}
|
||||
|
||||
{
|
||||
await wdb.addTX(t3.toTX());
|
||||
const balance = await alice.getBalance();
|
||||
assert.strictEqual(balance.unconfirmed, 22000);
|
||||
assert.strictEqual(balance.unconfirmed, 69000);
|
||||
}
|
||||
|
||||
{
|
||||
await wdb.addTX(f1.toTX());
|
||||
|
||||
const balance = await alice.getBalance();
|
||||
assert.strictEqual(balance.unconfirmed, 11000);
|
||||
assert.strictEqual(balance.unconfirmed, 58000);
|
||||
|
||||
const txs = await alice.getHistory();
|
||||
assert(txs.some((wtx) => {
|
||||
@ -565,10 +572,14 @@ describe('Wallet', function() {
|
||||
}));
|
||||
}
|
||||
|
||||
await wdb.addTX(t2.toTX());
|
||||
await wdb.addTX(t3.toTX());
|
||||
await wdb.addTX(t4.toTX());
|
||||
await wdb.addTX(f1.toTX());
|
||||
// Should recover from missed txs on block.
|
||||
await wdb.addBlock(nextBlock(wdb), [
|
||||
t1.toTX(),
|
||||
t2.toTX(),
|
||||
t3.toTX(),
|
||||
t4.toTX(),
|
||||
f1.toTX()
|
||||
]);
|
||||
|
||||
{
|
||||
const balance = await alice.getBalance();
|
||||
@ -1067,9 +1078,7 @@ describe('Wallet', function() {
|
||||
t2.addOutput(alice.getAddress(), 5460);
|
||||
t2.addOutput(alice.getAddress(), 5460);
|
||||
|
||||
const block = nextBlock();
|
||||
|
||||
await wdb.addBlock(block, [t2.toTX()]);
|
||||
await wdb.addBlock(nextBlock(wdb), [t2.toTX()]);
|
||||
|
||||
{
|
||||
const coins = await alice.getSmartCoins();
|
||||
@ -1077,7 +1086,7 @@ describe('Wallet', function() {
|
||||
|
||||
for (let i = 0; i < coins.length; i++) {
|
||||
const coin = coins[i];
|
||||
assert.strictEqual(coin.height, block.height);
|
||||
assert.strictEqual(coin.height, wdb.state.height);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1104,7 +1113,7 @@ describe('Wallet', function() {
|
||||
assert(coin.value < 5460);
|
||||
found = true;
|
||||
} else {
|
||||
assert.strictEqual(coin.height, block.height);
|
||||
assert.strictEqual(coin.height, wdb.state.height);
|
||||
}
|
||||
total += coin.value;
|
||||
}
|
||||
@ -1136,7 +1145,7 @@ describe('Wallet', function() {
|
||||
assert(coin.value < 5460);
|
||||
found = true;
|
||||
} else {
|
||||
assert.strictEqual(coin.height, block.height);
|
||||
assert.strictEqual(coin.height, wdb.state.height);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1337,12 +1346,7 @@ describe('Wallet', function() {
|
||||
});
|
||||
|
||||
it('should recover from a missed tx', async () => {
|
||||
const wdb = new WalletDB({
|
||||
name: 'wallet-test',
|
||||
db: 'memory',
|
||||
verify: false
|
||||
});
|
||||
|
||||
const wdb = new WalletDB({ workers });
|
||||
await wdb.open();
|
||||
|
||||
const alice = await wdb.create({
|
||||
@ -1393,21 +1397,16 @@ describe('Wallet', function() {
|
||||
assert.strictEqual((await alice.getBalance()).unconfirmed, 30000);
|
||||
|
||||
// Bob sees t2 on the chain.
|
||||
await bob.add(t2.toTX());
|
||||
await bob.add(t2.toTX(), nextBlock(wdb));
|
||||
|
||||
// Bob sees t3 on the chain.
|
||||
await bob.add(t3.toTX());
|
||||
await bob.add(t3.toTX(), nextBlock(wdb));
|
||||
|
||||
assert.strictEqual((await bob.getBalance()).unconfirmed, 30000);
|
||||
});
|
||||
|
||||
it('should recover from a missed tx and double spend', async () => {
|
||||
const wdb = new WalletDB({
|
||||
name: 'wallet-test',
|
||||
db: 'memory',
|
||||
verify: false
|
||||
});
|
||||
|
||||
const wdb = new WalletDB({ workers });
|
||||
await wdb.open();
|
||||
|
||||
const alice = await wdb.create({
|
||||
@ -1468,10 +1467,10 @@ describe('Wallet', function() {
|
||||
assert.strictEqual((await alice.getBalance()).unconfirmed, 30000);
|
||||
|
||||
// Bob sees t2a on the chain.
|
||||
await bob.add(t2a.toTX());
|
||||
await bob.add(t2a.toTX(), nextBlock(wdb));
|
||||
|
||||
// Bob sees t3 on the chain.
|
||||
await bob.add(t3.toTX());
|
||||
await bob.add(t3.toTX(), nextBlock(wdb));
|
||||
|
||||
assert.strictEqual((await bob.getBalance()).unconfirmed, 30000);
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user