test: add more coin selection tests to mtx and coinselection.
This commit is contained in:
parent
a49a68f87a
commit
cd3399b312
12 changed files with 804 additions and 151 deletions
|
|
@ -383,17 +383,17 @@ class Covenant extends bio.Struct {
|
|||
/**
|
||||
* Set covenant to BID.
|
||||
* @param {Hash} nameHash
|
||||
* @param {Number} start
|
||||
* @param {Number} height
|
||||
* @param {Buffer} rawName
|
||||
* @param {Hash} blind
|
||||
* @returns {Covenant}
|
||||
*/
|
||||
|
||||
setBid(nameHash, start, rawName, blind) {
|
||||
setBid(nameHash, height, rawName, blind) {
|
||||
this.type = types.BID;
|
||||
this.items = [];
|
||||
this.pushHash(nameHash);
|
||||
this.pushU32(start);
|
||||
this.pushU32(height);
|
||||
this.push(rawName);
|
||||
this.pushHash(blind);
|
||||
|
||||
|
|
|
|||
|
|
@ -231,6 +231,18 @@ class MTX extends TX {
|
|||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the change output.
|
||||
* @returns {AmountValue} value - Returns -1 if no change output.
|
||||
*/
|
||||
|
||||
getChangeValue() {
|
||||
if (this.changeIndex === -1)
|
||||
return -1;
|
||||
|
||||
return this.outputs[this.changeIndex].value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify all transaction inputs.
|
||||
* @param {VerifyFlags?} [flags=STANDARD_VERIFY_FLAGS]
|
||||
|
|
|
|||
|
|
@ -41,22 +41,27 @@ class AbstractCoinSource {
|
|||
}
|
||||
}
|
||||
|
||||
/** @typedef {'all'|'random'|'age'|'value'} SelectionType */
|
||||
/** @typedef {'all'|'random'|'age'|'value'} MemSelectionType */
|
||||
|
||||
/**
|
||||
* @typedef {Object} CoinSourceOptions
|
||||
* @property {SelectionType} [selection] - Selection type.
|
||||
* @property {MemSelectionType} [selection] - Selection type.
|
||||
* @property {Coin[]} [coins] - Coins to select from.
|
||||
*/
|
||||
|
||||
class CoinSource extends AbstractCoinSource {
|
||||
/**
|
||||
* Coin Source with coins.
|
||||
* @alias module:utils.CoinSource
|
||||
*/
|
||||
|
||||
class InMemoryCoinSource extends AbstractCoinSource {
|
||||
constructor(options = {}) {
|
||||
super();
|
||||
|
||||
/** @type {Coin[]} */
|
||||
this.coins = [];
|
||||
|
||||
/** @type {SelectionType} */
|
||||
/** @type {MemSelectionType} */
|
||||
this.selection = 'value';
|
||||
|
||||
this.index = -1;
|
||||
|
|
@ -166,7 +171,7 @@ class CoinSource extends AbstractCoinSource {
|
|||
|
||||
/**
|
||||
* Coin Selector
|
||||
* @alias module:primitives.CoinSelector
|
||||
* @alias module:utils.CoinSelector
|
||||
* @property {MTX} tx - clone of the original mtx.
|
||||
* @property {CoinView} view - reference to the original view.
|
||||
*/
|
||||
|
|
@ -174,7 +179,7 @@ class CoinSource extends AbstractCoinSource {
|
|||
class CoinSelector {
|
||||
/**
|
||||
* @param {MTX} tx
|
||||
* @param {CoinSource} source
|
||||
* @param {InMemoryCoinSource} source
|
||||
* @param {CoinSelectorOptions?} [options]
|
||||
*/
|
||||
|
||||
|
|
@ -506,7 +511,7 @@ class CoinSelector {
|
|||
|
||||
/**
|
||||
* Fill the transaction with inputs.
|
||||
* @returns {Promise<CoinSelector>}
|
||||
* @returns {Promise<this>}
|
||||
*/
|
||||
|
||||
async select() {
|
||||
|
|
@ -680,6 +685,6 @@ function sortValue(a, b) {
|
|||
}
|
||||
|
||||
exports.AbstractCoinSource = AbstractCoinSource;
|
||||
exports.CoinSource = CoinSource;
|
||||
exports.InMemoryCoinSource = InMemoryCoinSource;
|
||||
exports.CoinSelector = CoinSelector;
|
||||
exports.FundingError = FundingError;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ const WalletDB = require('../../../lib/wallet/walletdb');
|
|||
const MTX = require('../../../lib/primitives/mtx');
|
||||
const wutils = require('../../../test/util/wallet');
|
||||
const rules = require('../../../lib/covenants/rules');
|
||||
const {deterministicInput} = require('../../../test/util/primitives');
|
||||
|
||||
const layout = {
|
||||
wdb: {
|
||||
|
|
@ -66,16 +67,16 @@ let timeCounter = 0;
|
|||
|
||||
// fund wallets
|
||||
const mtx1 = new MTX();
|
||||
mtx1.addInput(wutils.deterministicInput(txID++));
|
||||
mtx1.addInput(deterministicInput(txID++));
|
||||
mtx1.addOutput(await wallet1.receiveAddress(0), 10e6);
|
||||
|
||||
const mtx2 = new MTX();
|
||||
mtx2.addInput(wutils.deterministicInput(txID++));
|
||||
mtx2.addInput(deterministicInput(txID++));
|
||||
mtx2.addOutput(await wallet1.receiveAddress(1), 10e6);
|
||||
|
||||
// fund second wallet.
|
||||
const mtx3 = new MTX();
|
||||
mtx3.addInput(wutils.deterministicInput(txID++));
|
||||
mtx3.addInput(deterministicInput(txID++));
|
||||
mtx3.addOutput(await wallet2.receiveAddress(), 10e6);
|
||||
|
||||
await wdb.addBlock(wutils.nextEntry(wdb), [
|
||||
|
|
|
|||
405
test/mtx-test.js
405
test/mtx-test.js
|
|
@ -1,13 +1,13 @@
|
|||
'use strict';
|
||||
|
||||
const assert = require('bsert');
|
||||
const random = require('bcrypto/lib/random');
|
||||
const CoinView = require('../lib/coins/coinview');
|
||||
const WalletCoinView = require('../lib/wallet/walletcoinview');
|
||||
const Coin = require('../lib/primitives/coin');
|
||||
const MTX = require('../lib/primitives/mtx');
|
||||
const Path = require('../lib/wallet/path');
|
||||
const MemWallet = require('./util/memwallet');
|
||||
const primutils = require('./util/primitives');
|
||||
const {randomP2PKAddress, makeCoin} = primutils;
|
||||
|
||||
const mtx1json = require('./data/mtx1.json');
|
||||
const mtx2json = require('./data/mtx2.json');
|
||||
|
|
@ -138,16 +138,318 @@ describe('MTX', function() {
|
|||
});
|
||||
});
|
||||
|
||||
describe('Fund', function() {
|
||||
describe('Fund with in memory coin selectors', function() {
|
||||
const createCoins = (values) => {
|
||||
return values.map(value => makeCoin({ value }));
|
||||
};
|
||||
|
||||
it('should fund with sorted values', async () => {
|
||||
const coins = createCoins([1e6, 2e6, 3e6, 4e6, 5e6]);
|
||||
|
||||
const mtx = new MTX();
|
||||
mtx.addOutput(randomP2PKAddress(), 7e6);
|
||||
|
||||
await mtx.fund(coins, {
|
||||
changeAddress: randomP2PKAddress(),
|
||||
hardFee: 0
|
||||
});
|
||||
|
||||
assert.strictEqual(mtx.inputs.length, 2);
|
||||
assert.strictEqual(mtx.outputs.length, 2);
|
||||
assert.strictEqual(mtx.outputs[0].value, 7e6);
|
||||
assert.strictEqual(mtx.outputs[1].value, 2e6);
|
||||
});
|
||||
|
||||
it('should fund with random selection', async () => {
|
||||
const coins = createCoins([1e6, 1e6, 1e6, 1e6, 1e6, 1e6, 1e6, 1e6]);
|
||||
|
||||
const mtx = new MTX();
|
||||
mtx.addOutput(randomP2PKAddress(), 5e6);
|
||||
|
||||
await mtx.fund(coins, {
|
||||
changeAddress: randomP2PKAddress(),
|
||||
hardFee: 1e5,
|
||||
selection: 'random'
|
||||
});
|
||||
|
||||
assert.strictEqual(mtx.inputs.length, 6);
|
||||
assert.strictEqual(mtx.outputs.length, 2);
|
||||
assert.strictEqual(mtx.outputs[0].value, 5e6);
|
||||
assert.strictEqual(mtx.getFee(), 1e5);
|
||||
assert.strictEqual(mtx.outputs[1].value, 9e5);
|
||||
});
|
||||
|
||||
it('should fund with all selection type', async () => {
|
||||
const coins = createCoins([1e6, 2e6, 3e6, 4e6, 5e6, 6e6]);
|
||||
|
||||
const mtx = new MTX();
|
||||
mtx.addOutput(randomP2PKAddress(), 2e6);
|
||||
|
||||
await mtx.fund(coins, {
|
||||
changeAddress: randomP2PKAddress(),
|
||||
hardFee: 0,
|
||||
selection: 'all'
|
||||
});
|
||||
|
||||
assert.strictEqual(mtx.inputs.length, 6);
|
||||
assert.strictEqual(mtx.outputs.length, 2);
|
||||
assert.strictEqual(mtx.outputs[0].value, 2e6);
|
||||
assert.strictEqual(mtx.getFee(), 0);
|
||||
assert.strictEqual(mtx.outputs[1].value, 19e6);
|
||||
});
|
||||
|
||||
it('should fund with age-based selection', async () => {
|
||||
const coins = [
|
||||
makeCoin({ value: 2e6, height: 100 }),
|
||||
makeCoin({ value: 3e6, height: 200 }),
|
||||
makeCoin({ value: 1e6, height: 50 })
|
||||
];
|
||||
|
||||
const mtx = new MTX();
|
||||
mtx.addOutput(randomP2PKAddress(), 1e6);
|
||||
|
||||
await mtx.fund(coins, {
|
||||
changeAddress: randomP2PKAddress(),
|
||||
hardFee: 1e5,
|
||||
selection: 'age'
|
||||
});
|
||||
|
||||
assert.strictEqual(mtx.inputs.length, 2);
|
||||
assert.strictEqual(mtx.outputs.length, 2);
|
||||
assert.strictEqual(mtx.getFee(), 1e5);
|
||||
// Should select the oldest (lowest height) coins first
|
||||
assert.strictEqual(mtx.inputs[0].prevout.hash.equals(coins[2].hash), true);
|
||||
assert.strictEqual(mtx.inputs[1].prevout.hash.equals(coins[0].hash), true);
|
||||
});
|
||||
|
||||
it('should fund with value-based selection', async () => {
|
||||
const coins = [
|
||||
makeCoin({ value: 1e6 }),
|
||||
makeCoin({ value: 5e6 }),
|
||||
makeCoin({ value: 2e6 })
|
||||
];
|
||||
|
||||
const mtx = new MTX();
|
||||
mtx.addOutput(randomP2PKAddress(), 4e6);
|
||||
|
||||
await mtx.fund(coins, {
|
||||
changeAddress: randomP2PKAddress(),
|
||||
hardFee: 1e5,
|
||||
selection: 'value'
|
||||
});
|
||||
|
||||
assert.strictEqual(mtx.inputs.length, 1);
|
||||
assert.strictEqual(mtx.outputs.length, 2);
|
||||
assert.strictEqual(mtx.getFee(), 1e5);
|
||||
// Should select the highest value coin first
|
||||
assert.strictEqual(mtx.inputs[0].prevout.hash.equals(coins[1].hash), true);
|
||||
});
|
||||
|
||||
it('should handle subtractFee option', async () => {
|
||||
const coins = createCoins([2e6, 3e6]);
|
||||
|
||||
const mtx = new MTX();
|
||||
mtx.addOutput(randomP2PKAddress(), 5e6);
|
||||
|
||||
await mtx.fund(coins, {
|
||||
changeAddress: randomP2PKAddress(),
|
||||
hardFee: 1e5,
|
||||
subtractFee: true
|
||||
});
|
||||
|
||||
assert.strictEqual(mtx.inputs.length, 2);
|
||||
assert.strictEqual(mtx.outputs.length, 1);
|
||||
assert.strictEqual(mtx.outputs[0].value, 4.9e6); // 5e6 - 1e5 = 4.9e6
|
||||
assert.strictEqual(mtx.getFee(), 1e5);
|
||||
});
|
||||
|
||||
it('should handle subtractIndex option', async () => {
|
||||
const coins = createCoins([3e6, 3e6]);
|
||||
|
||||
const mtx = new MTX();
|
||||
mtx.addOutput(randomP2PKAddress(), 3e6);
|
||||
mtx.addOutput(randomP2PKAddress(), 3e6);
|
||||
|
||||
await mtx.fund(coins, {
|
||||
changeAddress: randomP2PKAddress(),
|
||||
hardFee: 2e5,
|
||||
subtractFee: true,
|
||||
subtractIndex: 1
|
||||
});
|
||||
|
||||
assert.strictEqual(mtx.inputs.length, 2);
|
||||
assert.strictEqual(mtx.outputs.length, 2);
|
||||
assert.strictEqual(mtx.outputs[0].value, 3e6);
|
||||
assert.strictEqual(mtx.outputs[1].value, 2.8e6); // 3e6 - 2e5 = 2.8e6
|
||||
assert.strictEqual(mtx.getFee(), 2e5);
|
||||
});
|
||||
|
||||
it('should throw with insufficient funds', async () => {
|
||||
const coins = createCoins([1e6, 1e6]);
|
||||
|
||||
const mtx = new MTX();
|
||||
mtx.addOutput(randomP2PKAddress(), 5e6);
|
||||
|
||||
let err;
|
||||
try {
|
||||
await mtx.fund(coins, {
|
||||
changeAddress: randomP2PKAddress(),
|
||||
hardFee: 0
|
||||
});
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
|
||||
assert(err);
|
||||
assert.strictEqual(err.message.includes('Not enough funds'), true);
|
||||
});
|
||||
|
||||
it('should throw when fee is too high', async () => {
|
||||
const coins = createCoins([1e6, 1e6, 1e6]);
|
||||
|
||||
const mtx = new MTX();
|
||||
mtx.addOutput(randomP2PKAddress(), 2e6);
|
||||
|
||||
let err;
|
||||
try {
|
||||
await mtx.fund(coins, {
|
||||
changeAddress: randomP2PKAddress(),
|
||||
rate: 1e6, // Extremely high fee rate
|
||||
maxFee: 1e5 // But with a low maxFee
|
||||
});
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
|
||||
assert(err);
|
||||
assert.strictEqual(err.message.includes('Fee is too high'), true);
|
||||
});
|
||||
|
||||
it('should handle dust change', async () => {
|
||||
const coins = createCoins([1e6, 1e6]);
|
||||
|
||||
const mtx = new MTX();
|
||||
mtx.addOutput(randomP2PKAddress(), 1.999e6);
|
||||
|
||||
await mtx.fund(coins, {
|
||||
changeAddress: randomP2PKAddress(),
|
||||
hardFee: 1e3
|
||||
});
|
||||
|
||||
assert.strictEqual(mtx.inputs.length, 2);
|
||||
assert.strictEqual(mtx.outputs.length, 1);
|
||||
assert.strictEqual(mtx.getFee(), 1e3);
|
||||
assert.strictEqual(mtx.changeIndex, -1);
|
||||
});
|
||||
|
||||
it('should fund with exact amount needed', async () => {
|
||||
const coins = createCoins([1e6, 2e6, 3e6]);
|
||||
|
||||
const mtx = new MTX();
|
||||
mtx.addOutput(randomP2PKAddress(), 3e6);
|
||||
|
||||
await mtx.fund(coins, {
|
||||
changeAddress: randomP2PKAddress(),
|
||||
hardFee: 0
|
||||
});
|
||||
|
||||
assert.strictEqual(mtx.inputs.length, 1);
|
||||
assert.strictEqual(mtx.outputs.length, 1);
|
||||
assert.strictEqual(mtx.outputs[0].value, 3e6);
|
||||
assert.strictEqual(mtx.getFee(), 0);
|
||||
assert.strictEqual(mtx.changeIndex, -1);
|
||||
});
|
||||
|
||||
it('should add coin based on minimum required', async () => {
|
||||
const wallet = new MemWallet();
|
||||
const coins = [
|
||||
makeCoin({ address: wallet.getAddress(), value: 1e5 }),
|
||||
makeCoin({ address: wallet.getAddress(), value: 2e5 }),
|
||||
makeCoin({ address: wallet.getAddress(), value: 5e5 }),
|
||||
makeCoin({ address: wallet.getAddress(), value: 1e6 }),
|
||||
makeCoin({ address: wallet.getAddress(), value: 2e6 })
|
||||
];
|
||||
|
||||
const mtx = new MTX();
|
||||
mtx.addOutput(randomP2PKAddress(), 1.5e6);
|
||||
|
||||
await mtx.fund(coins, {
|
||||
changeAddress: wallet.getChange(),
|
||||
hardFee: 1e4
|
||||
});
|
||||
|
||||
// Should select the 2e6 coin (largest value first selection)
|
||||
assert.strictEqual(mtx.inputs.length, 1);
|
||||
assert.strictEqual(mtx.outputs.length, 2);
|
||||
assert.strictEqual(mtx.outputs[0].value, 1.5e6);
|
||||
assert.strictEqual(mtx.outputs[1].value, 2e6 - 1.5e6 - 1e4);
|
||||
assert.bufferEqual(mtx.inputs[0].prevout.hash, coins[4].hash);
|
||||
});
|
||||
|
||||
it('should combine multiple coins when necessary', async () => {
|
||||
const coins = createCoins([1e5, 2e5, 3e5, 4e5, 5e5]);
|
||||
|
||||
const mtx = new MTX();
|
||||
mtx.addOutput(randomP2PKAddress(), 1e6);
|
||||
|
||||
await mtx.fund(coins, {
|
||||
changeAddress: randomP2PKAddress(),
|
||||
hardFee: 5e4
|
||||
});
|
||||
|
||||
// Should need to combine multiple coins to reach 1e6 + 5e4
|
||||
assert.ok(mtx.inputs.length > 1);
|
||||
assert.strictEqual(mtx.outputs.length, 2);
|
||||
assert.strictEqual(mtx.outputs[0].value, 1e6);
|
||||
assert.strictEqual(mtx.getFee(), 5e4);
|
||||
});
|
||||
|
||||
it('should correctly set changeIndex', async () => {
|
||||
const coins = createCoins([5e6]);
|
||||
|
||||
const mtx = new MTX();
|
||||
mtx.addOutput(randomP2PKAddress(), 2e6);
|
||||
|
||||
await mtx.fund(coins, {
|
||||
changeAddress: randomP2PKAddress(),
|
||||
hardFee: 1e5
|
||||
});
|
||||
|
||||
assert.strictEqual(mtx.inputs.length, 1);
|
||||
assert.strictEqual(mtx.outputs.length, 2);
|
||||
assert.strictEqual(mtx.changeIndex, 1);
|
||||
assert.strictEqual(mtx.outputs[1].value, 2.9e6); // 5e6 - 2e6 - 1e5 = 2.9e6
|
||||
});
|
||||
|
||||
it('should handle fee rates properly', async () => {
|
||||
const coins = createCoins([1e6, 2e6, 3e6]);
|
||||
|
||||
const mtx = new MTX();
|
||||
mtx.addOutput(randomP2PKAddress(), 4e6);
|
||||
|
||||
await mtx.fund(coins, {
|
||||
changeAddress: randomP2PKAddress(),
|
||||
rate: 5000 // dollarydoos per kb
|
||||
});
|
||||
|
||||
// The exact fee will depend on the estimated tx size
|
||||
assert.strictEqual(mtx.inputs.length, 2);
|
||||
assert.strictEqual(mtx.outputs.length, 2);
|
||||
assert.ok(mtx.getFee() > 0);
|
||||
assert.ok(mtx.getFee() < 1e5); // Reasonable upper bound for test
|
||||
});
|
||||
});
|
||||
|
||||
describe('Fund preferred & existing', function() {
|
||||
const wallet1 = new MemWallet();
|
||||
const wallet2 = new MemWallet();
|
||||
|
||||
const coins1 = [
|
||||
dummyCoin(wallet1.getAddress(), 1000000),
|
||||
dummyCoin(wallet1.getAddress(), 1000000),
|
||||
dummyCoin(wallet1.getAddress(), 1000000),
|
||||
dummyCoin(wallet1.getAddress(), 1000000),
|
||||
dummyCoin(wallet1.getAddress(), 1000000)
|
||||
makeCoin({ address: wallet1.getAddress(), value: 1000000 }),
|
||||
makeCoin({ address: wallet1.getAddress(), value: 1000000 }),
|
||||
makeCoin({ address: wallet1.getAddress(), value: 1000000 }),
|
||||
makeCoin({ address: wallet1.getAddress(), value: 1000000 }),
|
||||
makeCoin({ address: wallet1.getAddress(), value: 1000000 })
|
||||
];
|
||||
|
||||
const last1 = coins1[coins1.length - 1];
|
||||
|
|
@ -239,7 +541,10 @@ describe('MTX', function() {
|
|||
|
||||
it('should fund with preferred inputs - view', async () => {
|
||||
const mtx = new MTX();
|
||||
const coin = dummyCoin(wallet1.getAddress(), 1000000);
|
||||
const coin = makeCoin({
|
||||
address: wallet1.getAddress(),
|
||||
value: 1000000
|
||||
});
|
||||
|
||||
mtx.addOutput(wallet2.getAddress(), 1500000);
|
||||
mtx.view.addCoin(coin);
|
||||
|
|
@ -266,7 +571,10 @@ describe('MTX', function() {
|
|||
|
||||
it('should fund with preferred inputs - coins && view', async () => {
|
||||
const mtx = new MTX();
|
||||
const viewCoin = dummyCoin(wallet1.getAddress(), 1000000);
|
||||
const viewCoin = makeCoin({
|
||||
address: wallet1.getAddress(),
|
||||
value: 1000000
|
||||
});
|
||||
const lastCoin = last1;
|
||||
|
||||
mtx.addOutput(wallet2.getAddress(), 1500000);
|
||||
|
|
@ -304,7 +612,10 @@ describe('MTX', function() {
|
|||
|
||||
it('should not fund with preferred inputs and no coin info', async () => {
|
||||
const mtx = new MTX();
|
||||
const coin = dummyCoin(wallet1.getAddress(), 1000000);
|
||||
const coin = makeCoin({
|
||||
address: wallet1.getAddress(),
|
||||
value: 1000000
|
||||
});
|
||||
|
||||
mtx.addOutput(wallet2.getAddress(), 1500000);
|
||||
|
||||
|
|
@ -357,7 +668,10 @@ describe('MTX', function() {
|
|||
|
||||
it('should fund with existing inputs view - view', async () => {
|
||||
const mtx = new MTX();
|
||||
const coin = dummyCoin(wallet1.getAddress(), 1000000);
|
||||
const coin = makeCoin({
|
||||
address: wallet1.getAddress(),
|
||||
value: 1000000
|
||||
});
|
||||
|
||||
mtx.addInput({
|
||||
prevout: {
|
||||
|
|
@ -388,7 +702,10 @@ describe('MTX', function() {
|
|||
|
||||
it('should fund with existing inputs view - coins && view', async () => {
|
||||
const mtx = new MTX();
|
||||
const viewCoin = dummyCoin(wallet1.getAddress(), 1000000);
|
||||
const viewCoin = makeCoin({
|
||||
address: wallet1.getAddress(),
|
||||
value: 1000000
|
||||
});
|
||||
const lastCoin = last1;
|
||||
|
||||
mtx.addInput({
|
||||
|
|
@ -434,7 +751,10 @@ describe('MTX', function() {
|
|||
|
||||
it('should not fund with existing inputs and no coin info', async () => {
|
||||
const mtx = new MTX();
|
||||
const coin = dummyCoin(wallet1.getAddress(), 1000000);
|
||||
const coin = makeCoin({
|
||||
address: wallet1.getAddress(),
|
||||
value: 1000000
|
||||
});
|
||||
|
||||
mtx.addInput({
|
||||
prevout: {
|
||||
|
|
@ -502,8 +822,14 @@ describe('MTX', function() {
|
|||
|
||||
it('should fund with preferred & existing inputs - view', async () => {
|
||||
const mtx = new MTX();
|
||||
const coin1 = dummyCoin(wallet1.getAddress(), 1000000);
|
||||
const coin2 = dummyCoin(wallet1.getAddress(), 1000000);
|
||||
const coin1 = makeCoin({
|
||||
address: wallet1.getAddress(),
|
||||
value: 1000000
|
||||
});
|
||||
const coin2 = makeCoin({
|
||||
address: wallet1.getAddress(),
|
||||
value: 1000000
|
||||
});
|
||||
|
||||
mtx.addInput({
|
||||
prevout: {
|
||||
|
|
@ -546,11 +872,17 @@ describe('MTX', function() {
|
|||
it('should fund with preferred & existing inputs', async () => {
|
||||
const mtx = new MTX();
|
||||
// existing
|
||||
const coin1 = dummyCoin(wallet1.getAddress(), 1000000);
|
||||
const coin1 = makeCoin({
|
||||
address: wallet1.getAddress(),
|
||||
value: 1000000
|
||||
});
|
||||
const coinLast1 = last1;
|
||||
|
||||
// preferred
|
||||
const coin2 = dummyCoin(wallet1.getAddress(), 1000000);
|
||||
const coin2 = makeCoin({
|
||||
address: wallet1.getAddress(),
|
||||
value: 1000000
|
||||
});
|
||||
const coinLast2 = last2;
|
||||
|
||||
mtx.addInput({
|
||||
|
|
@ -620,11 +952,17 @@ describe('MTX', function() {
|
|||
it('should not fund with missing coin info (both)', async () => {
|
||||
const mtx = new MTX();
|
||||
// existing
|
||||
const coin1 = dummyCoin(wallet1.getAddress(), 1000000);
|
||||
const coin1 = makeCoin({
|
||||
address: wallet1.getAddress(),
|
||||
value: 1000000
|
||||
});
|
||||
const coinLast1 = last1;
|
||||
|
||||
// preferred
|
||||
const coin2 = dummyCoin(wallet1.getAddress(), 1000000);
|
||||
const coin2 = makeCoin({
|
||||
address: wallet1.getAddress(),
|
||||
value: 1000000
|
||||
});
|
||||
const coinLast2 = last2;
|
||||
|
||||
mtx.addInput({
|
||||
|
|
@ -666,11 +1004,17 @@ describe('MTX', function() {
|
|||
it('should not fund with missing coin info(only existing)', async () => {
|
||||
const mtx = new MTX();
|
||||
// existing
|
||||
const coin1 = dummyCoin(wallet1.getAddress(), 1000000);
|
||||
const coin1 = makeCoin({
|
||||
address: wallet1.getAddress(),
|
||||
value: 1000000
|
||||
});
|
||||
const coinLast1 = last1;
|
||||
|
||||
// preferred
|
||||
const coin2 = dummyCoin(wallet1.getAddress(), 1000000);
|
||||
const coin2 = makeCoin({
|
||||
address: wallet1.getAddress(),
|
||||
value: 1000000
|
||||
});
|
||||
const coinLast2 = last2;
|
||||
|
||||
mtx.addInput({
|
||||
|
|
@ -713,11 +1057,17 @@ describe('MTX', function() {
|
|||
it('should not fund with missing coin info(only preferred)', async () => {
|
||||
const mtx = new MTX();
|
||||
// existing
|
||||
const coin1 = dummyCoin(wallet1.getAddress(), 1000000);
|
||||
const coin1 = makeCoin({
|
||||
address: wallet1.getAddress(),
|
||||
value: 1000000
|
||||
});
|
||||
const coinLast1 = last1;
|
||||
|
||||
// preferred
|
||||
const coin2 = dummyCoin(wallet1.getAddress(), 1000000);
|
||||
const coin2 = makeCoin({
|
||||
address: wallet1.getAddress(),
|
||||
value: 1000000
|
||||
});
|
||||
const coinLast2 = last2;
|
||||
|
||||
mtx.addInput({
|
||||
|
|
@ -758,10 +1108,3 @@ describe('MTX', function() {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
function dummyCoin(address, value) {
|
||||
const hash = random.randomBytes(32);
|
||||
const index = 0;
|
||||
|
||||
return new Coin({address, value, hash, index});
|
||||
}
|
||||
|
|
|
|||
213
test/util/primitives.js
Normal file
213
test/util/primitives.js
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
'use strict';
|
||||
|
||||
const assert = require('bsert');
|
||||
const blake2b = require('bcrypto/lib/blake2b');
|
||||
const random = require('bcrypto/lib/random');
|
||||
const rules = require('../../lib/covenants/rules');
|
||||
const Input = require('../../lib/primitives/input');
|
||||
const Address = require('../../lib/primitives/address');
|
||||
const Output = require('../../lib/primitives/output');
|
||||
const Outpoint = require('../../lib/primitives/outpoint');
|
||||
const Coin = require('../../lib/primitives/coin');
|
||||
const Covenant = require('../../lib/primitives/covenant');
|
||||
|
||||
/** @typedef {import('../../lib/types').Hash} Hash */
|
||||
|
||||
exports.coinbaseInput = () => {
|
||||
return Input.fromOutpoint(new Outpoint(Buffer.alloc(32), 0));
|
||||
};
|
||||
|
||||
exports.dummyInput = () => {
|
||||
const hash = random.randomBytes(32);
|
||||
return Input.fromOutpoint(new Outpoint(hash, 0));
|
||||
};
|
||||
|
||||
exports.deterministicInput = (id) => {
|
||||
const hash = blake2b.digest(fromU32(id));
|
||||
return Input.fromOutpoint(new Outpoint(hash, 0));
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {Object} CovenantOptions
|
||||
* @property {String} [name]
|
||||
* @property {Hash} [nameHash]
|
||||
* @property {Covenant.types} [type=Covenant.types.NONE]
|
||||
* @property {Number} [height]
|
||||
* @property {Array} [args] - leftover args for the covenant except
|
||||
* for nameHash, name and height.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} OutputOptions
|
||||
* @property {Number} value
|
||||
* @property {Address} [address]
|
||||
* @property {CovenantOptions} [covenant]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {OutputOptions} options
|
||||
* @returns {Output}
|
||||
*/
|
||||
|
||||
exports.makeOutput = (options) => {
|
||||
const address = options.address || exports.randomP2PKAddress();
|
||||
const output = new Output();
|
||||
output.address = address;
|
||||
output.value = options.value;
|
||||
|
||||
if (options.covenant)
|
||||
output.covenant = exports.makeCovenant(options.covenant);
|
||||
|
||||
return output;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {CovenantOptions} options
|
||||
* @returns {Covenant}
|
||||
*/
|
||||
|
||||
exports.makeCovenant = (options) => {
|
||||
const covenant = new Covenant();
|
||||
covenant.type = options.type || Covenant.types.NONE;
|
||||
|
||||
const args = options.args || [];
|
||||
const height = options.height || 0;
|
||||
let nameHash = options.nameHash;
|
||||
let name = options.name;
|
||||
|
||||
if (name) {
|
||||
nameHash = rules.hashName(name);
|
||||
} else if (!nameHash) {
|
||||
name = randomString(30);
|
||||
nameHash = rules.hashName(name);
|
||||
}
|
||||
|
||||
switch (covenant.type) {
|
||||
case Covenant.types.NONE:
|
||||
break;
|
||||
case Covenant.types.OPEN: {
|
||||
assert(args.length === 0, 'Pass `options.name` instead.');
|
||||
const rawName = Buffer.from(name, 'ascii');
|
||||
covenant.setOpen(nameHash, rawName);
|
||||
break;
|
||||
}
|
||||
case Covenant.types.BID: {
|
||||
assert(args.length < 1, 'Pass [blind?] instead.');
|
||||
const blind = args[0] || random.randomBytes(32);
|
||||
const rawName = Buffer.from(name, 'ascii');
|
||||
covenant.setBid(nameHash, height, rawName, blind);
|
||||
break;
|
||||
}
|
||||
case Covenant.types.REVEAL: {
|
||||
assert(args.length < 1, 'Pass [nonce?] instead.');
|
||||
const nonce = args[0] || random.randomBytes(32);
|
||||
covenant.setReveal(nameHash, height, nonce);
|
||||
break;
|
||||
}
|
||||
case Covenant.types.REDEEM: {
|
||||
assert(args.length === 0, 'No args for redeem.');
|
||||
covenant.setRedeem(nameHash, height);
|
||||
break;
|
||||
}
|
||||
case Covenant.types.REGISTER: {
|
||||
assert(args.length < 2, 'Pass [record?, blockHash?] instead.');
|
||||
const record = args[0] || Buffer.alloc(0);
|
||||
const blockHash = args[1] || random.randomBytes(32);
|
||||
covenant.setRegister(nameHash, height, record, blockHash);
|
||||
break;
|
||||
}
|
||||
case Covenant.types.UPDATE: {
|
||||
assert(args.length < 1, 'Pass [resource?] instead.');
|
||||
const resource = args[0] || Buffer.alloc(0);
|
||||
covenant.setUpdate(nameHash, height, resource);
|
||||
break;
|
||||
}
|
||||
case Covenant.types.RENEW: {
|
||||
assert(args.length < 1, 'Pass [blockHash?] instead.');
|
||||
const blockHash = args[0] || random.randomBytes(32);
|
||||
covenant.setRenew(nameHash, height, blockHash);
|
||||
break;
|
||||
}
|
||||
case Covenant.types.TRANSFER: {
|
||||
assert(args.length < 1, 'Pass [address?] instead.');
|
||||
const address = args[0] || exports.randomP2PKAddress();
|
||||
covenant.setTransfer(nameHash, height, address);
|
||||
break;
|
||||
}
|
||||
case Covenant.types.FINALIZE: {
|
||||
assert(args.length < 4, 'Pass [flags?, claimed?, renewal?, blockHash?] instead.');
|
||||
const rawName = Buffer.from(name, 'ascii');
|
||||
const flags = args[0] || 0;
|
||||
const claimed = args[1] || 0;
|
||||
const renewal = args[2] || 0;
|
||||
const blockHash = args[3] || random.randomBytes(32);
|
||||
|
||||
covenant.setFinalize(
|
||||
nameHash,
|
||||
height,
|
||||
rawName,
|
||||
flags,
|
||||
claimed,
|
||||
renewal,
|
||||
blockHash
|
||||
);
|
||||
break;
|
||||
}
|
||||
case Covenant.types.REVOKE: {
|
||||
assert(args.length === 0, 'No args for revoke.');
|
||||
covenant.setRevoke(nameHash, height);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new Error(`Invalid covenant type ${covenant.type}.`);
|
||||
}
|
||||
|
||||
return covenant;
|
||||
};
|
||||
|
||||
exports.randomP2PKAddress = () => {
|
||||
const key = random.randomBytes(33);
|
||||
return Address.fromPubkey(key);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Object} options
|
||||
* @param {String} [options.version=1]
|
||||
* @param {String} [options.height=-1]
|
||||
* @param {String} [options.value=0]
|
||||
* @param {String} [options.address]
|
||||
* @param {Object} [options.covenant]
|
||||
* @param {Boolean} [options.coinbase=false]
|
||||
* @param {Buffer} [options.hash]
|
||||
* @param {Number} [options.index=0]
|
||||
* @returns {Coin}
|
||||
*/
|
||||
|
||||
exports.makeCoin = (options) => {
|
||||
return Coin.fromOptions({
|
||||
hash: options.hash || random.randomBytes(32),
|
||||
address: options.address || Address.fromPubkey(random.randomBytes(33)),
|
||||
...options
|
||||
});
|
||||
};
|
||||
|
||||
function fromU32(num) {
|
||||
const data = Buffer.allocUnsafe(4);
|
||||
data.writeUInt32LE(num, 0, true);
|
||||
return data;
|
||||
}
|
||||
|
||||
function randomString(len) {
|
||||
assert((len >>> 0) === len);
|
||||
|
||||
let s = '';
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
const n = Math.random() * (0x7b - 0x61) + 0x61;
|
||||
const c = Math.floor(n);
|
||||
|
||||
s += String.fromCharCode(c);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
|
@ -2,10 +2,7 @@
|
|||
|
||||
const assert = require('bsert');
|
||||
const blake2b = require('bcrypto/lib/blake2b');
|
||||
const random = require('bcrypto/lib/random');
|
||||
const ChainEntry = require('../../lib/blockchain/chainentry');
|
||||
const Input = require('../../lib/primitives/input');
|
||||
const Outpoint = require('../../lib/primitives/outpoint');
|
||||
const {ZERO_HASH} = require('../../lib/protocol/consensus');
|
||||
|
||||
const walletUtils = exports;
|
||||
|
|
@ -35,16 +32,6 @@ walletUtils.fakeBlock = (height, prevSeed = 0, seed = prevSeed) => {
|
|||
};
|
||||
};
|
||||
|
||||
walletUtils.dummyInput = () => {
|
||||
const hash = random.randomBytes(32);
|
||||
return Input.fromOutpoint(new Outpoint(hash, 0));
|
||||
};
|
||||
|
||||
walletUtils.deterministicInput = (id) => {
|
||||
const hash = blake2b.digest(fromU32(id));
|
||||
return Input.fromOutpoint(new Outpoint(hash, 0));
|
||||
};
|
||||
|
||||
walletUtils.nextBlock = (wdb, prevSeed = 0, seed = prevSeed) => {
|
||||
return walletUtils.fakeBlock(wdb.state.height + 1, prevSeed, seed);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,10 +7,8 @@ const MTX = require('../lib/primitives/mtx');
|
|||
const WorkerPool = require('../lib/workers/workerpool');
|
||||
const WalletDB = require('../lib/wallet/walletdb');
|
||||
const wutils = require('./util/wallet');
|
||||
const {
|
||||
dummyInput,
|
||||
nextEntry
|
||||
} = wutils;
|
||||
const {nextEntry} = wutils;
|
||||
const {dummyInput} = require('./util/primitives');
|
||||
|
||||
const enabled = true;
|
||||
const size = 2;
|
||||
|
|
|
|||
|
|
@ -1,72 +1,130 @@
|
|||
'use strict';
|
||||
|
||||
const assert = require('bsert');
|
||||
const {BlockMeta} = require('../lib/wallet/records');
|
||||
const util = require('../lib/utils/util');
|
||||
const Network = require('../lib/protocol/network');
|
||||
const MTX = require('../lib/primitives/mtx');
|
||||
const Covenant = require('../lib/primitives/covenant');
|
||||
const WalletDB = require('../lib/wallet/walletdb');
|
||||
const policy = require('../lib/protocol/policy');
|
||||
const wutils = require('./util/wallet');
|
||||
const {nextBlock} = wutils;
|
||||
const primutils = require('./util/primitives');
|
||||
const {coinbaseInput, dummyInput} = primutils;
|
||||
|
||||
/** @typedef {import('../lib/wallet/wallet')} Wallet */
|
||||
/** @typedef {import('../lib/covenants/rules').types} covenantTypes */
|
||||
|
||||
// Use main instead of regtest because (deprecated)
|
||||
// CoinSelector.MAX_FEE was network agnostic
|
||||
const network = Network.get('main');
|
||||
|
||||
function dummyBlock(tipHeight) {
|
||||
const height = tipHeight + 1;
|
||||
const hash = Buffer.alloc(32);
|
||||
hash.writeUInt16BE(height);
|
||||
describe('Wallet Coin Selection', function () {
|
||||
const TX_START_BAK = network.txStart;
|
||||
/** @type {WalletDB?} */
|
||||
let wdb;
|
||||
/** @type {Wallet?} */
|
||||
let wallet;
|
||||
|
||||
const prevHash = Buffer.alloc(32);
|
||||
prevHash.writeUInt16BE(tipHeight);
|
||||
const beforeFn = async () => {
|
||||
network.txStart = 0;
|
||||
wdb = new WalletDB({ network });
|
||||
|
||||
const dummyBlock = {
|
||||
hash,
|
||||
height,
|
||||
time: util.now(),
|
||||
prevBlock: prevHash
|
||||
await wdb.open();
|
||||
await wdb.addBlock(nextBlock(wdb), []);
|
||||
wallet = wdb.primary;
|
||||
};
|
||||
|
||||
return dummyBlock;
|
||||
}
|
||||
const afterFn = async () => {
|
||||
network.txStart = TX_START_BAK;
|
||||
await wdb.close();
|
||||
|
||||
async function fundWallet(wallet, amounts) {
|
||||
assert(Array.isArray(amounts));
|
||||
wdb = null;
|
||||
wallet = null;
|
||||
};
|
||||
|
||||
const mtx = new MTX();
|
||||
const addr = await wallet.receiveAddress();
|
||||
for (const amt of amounts) {
|
||||
mtx.addOutput(addr, amt);
|
||||
}
|
||||
describe('Selection types', function () {
|
||||
beforeEach(beforeFn);
|
||||
afterEach(afterFn);
|
||||
|
||||
const dummy = dummyBlock(wallet.wdb.height);
|
||||
await wallet.wdb.addBlock(dummy, [mtx.toTX()]);
|
||||
}
|
||||
it('should select all spendable coins', async () => {
|
||||
const spendableCovs = [
|
||||
Covenant.types.NONE,
|
||||
Covenant.types.OPEN,
|
||||
Covenant.types.REDEEM
|
||||
];
|
||||
|
||||
const nonSpendableCovs = [
|
||||
Covenant.types.BID,
|
||||
Covenant.types.REVEAL,
|
||||
Covenant.types.REGISTER,
|
||||
Covenant.types.UPDATE,
|
||||
Covenant.types.RENEW,
|
||||
Covenant.types.TRANSFER,
|
||||
Covenant.types.FINALIZE,
|
||||
Covenant.types.REVOKE
|
||||
];
|
||||
|
||||
const mkopt = type => ({ value: 1e6, covenant: { type }});
|
||||
await fundWallet(wallet, [...nonSpendableCovs, ...spendableCovs].map(mkopt));
|
||||
|
||||
const coins = await wallet.getCoins();
|
||||
assert.strictEqual(coins.length, spendableCovs.length + nonSpendableCovs.length);
|
||||
|
||||
const mtx = new MTX();
|
||||
await wallet.fund(mtx, {
|
||||
selection: 'all'
|
||||
});
|
||||
|
||||
assert.strictEqual(mtx.inputs.length, spendableCovs.length);
|
||||
});
|
||||
|
||||
it('should select coin by descending value', async () => {
|
||||
const values = [5e6, 4e6, 3e6, 2e6, 1e6];
|
||||
await fundWallet(wallet, values.map(value => ({ value })));
|
||||
|
||||
const mtx = new MTX();
|
||||
mtx.addOutput(primutils.randomP2PKAddress(), 9e6);
|
||||
|
||||
await wallet.fund(mtx, {
|
||||
selection: 'value',
|
||||
hardFee: 0
|
||||
});
|
||||
|
||||
assert.strictEqual(mtx.inputs.length, 2);
|
||||
assert.strictEqual(mtx.outputs.length, 1);
|
||||
assert.strictEqual(mtx.outputs[0].value, 9e6);
|
||||
});
|
||||
|
||||
it('should select coins by descending age', async () => {
|
||||
const values = [1e6, 2e6, 3e6, 4e6, 5e6];
|
||||
|
||||
for (const value of values)
|
||||
await fundWallet(wallet, [{ value }]);
|
||||
|
||||
const mtx = new MTX();
|
||||
mtx.addOutput(primutils.randomP2PKAddress(), 9e6);
|
||||
await wallet.fund(mtx, {
|
||||
selection: 'age',
|
||||
hardFee: 0
|
||||
});
|
||||
|
||||
// 1 + 2 + 3 + 4 = 10
|
||||
assert.strictEqual(mtx.inputs.length, 4);
|
||||
assert.strictEqual(mtx.outputs.length, 2);
|
||||
assert.strictEqual(mtx.outputs[0].value, 9e6);
|
||||
assert.strictEqual(mtx.outputs[1].value, 1e6);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Wallet Coin Selection', function () {
|
||||
describe('Fees', function () {
|
||||
const wdb = new WalletDB({network});
|
||||
let wallet;
|
||||
|
||||
before(async () => {
|
||||
await wdb.open();
|
||||
wdb.height = network.txStart + 1;
|
||||
wdb.state.height = wdb.height;
|
||||
|
||||
const dummy = dummyBlock(network.txStart + 1);
|
||||
const record = BlockMeta.fromEntry(dummy);
|
||||
await wdb.setTip(record);
|
||||
wallet = wdb.primary;
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await wdb.close();
|
||||
});
|
||||
before(beforeFn);
|
||||
after(afterFn);
|
||||
|
||||
it('should fund wallet', async () => {
|
||||
await fundWallet(wallet, [100e6, 10e6, 1e6, 100000, 10000]);
|
||||
const vals = [100e6, 10e6, 1e6, 0.1e6, 0.01e6];
|
||||
await fundWallet(wallet, vals.map(value => ({ value })));
|
||||
const bal = await wallet.getBalance();
|
||||
assert.strictEqual(bal.confirmed, 111110000);
|
||||
assert.strictEqual(bal.confirmed, 111.11e6);
|
||||
});
|
||||
|
||||
it('should pay default fee rate for small tx', async () => {
|
||||
|
|
@ -121,16 +179,22 @@ describe('Wallet Coin Selection', function () {
|
|||
|
||||
it('should fail to pay absurd fee rate for small tx', async () => {
|
||||
const address = await wallet.receiveAddress();
|
||||
await assert.rejects(
|
||||
wallet.send({
|
||||
let err;
|
||||
|
||||
try {
|
||||
await wallet.send({
|
||||
outputs: [{
|
||||
address,
|
||||
value: 5e6
|
||||
}],
|
||||
rate: (policy.ABSURD_FEE_FACTOR + 1) * network.minRelay
|
||||
}),
|
||||
{message: 'Fee exceeds absurd limit.'}
|
||||
);
|
||||
});
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
|
||||
assert(err, 'Error not thrown.');
|
||||
assert.strictEqual(err.message, 'Fee exceeds absurd limit.');
|
||||
});
|
||||
|
||||
it('should pay fee just under the absurd limit', async () => {
|
||||
|
|
@ -177,3 +241,60 @@ describe('Wallet Coin Selection', function () {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* @typedef {Object} OutputInfo
|
||||
* @property {String} [address]
|
||||
* @property {Number} [value]
|
||||
* @property {covenantTypes} [covenant]
|
||||
* @property {Boolean} [coinbase=false]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {Wallet} wallet
|
||||
* @param {primutils.OutputOptions} outputInfo
|
||||
* @returns {Promise<Output>}
|
||||
*/
|
||||
|
||||
async function mkOutput(wallet, outputInfo) {
|
||||
if (!outputInfo.address)
|
||||
outputInfo.address = await wallet.receiveAddress();
|
||||
|
||||
return primutils.makeOutput(outputInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Wallet} wallet
|
||||
* @param {OutputInfo[]} outputInfos
|
||||
*/
|
||||
|
||||
async function fundWallet(wallet, outputInfos) {
|
||||
assert(Array.isArray(outputInfos));
|
||||
|
||||
let hadCoinbase = false;
|
||||
|
||||
const txs = [];
|
||||
for (const info of outputInfos) {
|
||||
const mtx = new MTX();
|
||||
|
||||
if (info.coinbase && hadCoinbase)
|
||||
throw new Error('Coinbase already added.');
|
||||
|
||||
if (info.coinbase && !hadCoinbase) {
|
||||
hadCoinbase = true;
|
||||
mtx.addInput(coinbaseInput());
|
||||
} else {
|
||||
mtx.addInput(dummyInput());
|
||||
}
|
||||
|
||||
const output = await mkOutput(wallet, info);
|
||||
mtx.addOutput(output);
|
||||
|
||||
if (output.covenant.isLinked())
|
||||
mtx.addInput(dummyInput());
|
||||
|
||||
txs.push(mtx.toTX());
|
||||
}
|
||||
|
||||
await wallet.wdb.addBlock(nextBlock(wallet.wdb), txs);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,10 +7,8 @@ const WalletDB = require('../lib/wallet/walletdb');
|
|||
const consensus = require('../lib/protocol/consensus');
|
||||
const util = require('../lib/utils/util');
|
||||
const wutils = require('./util/wallet');
|
||||
const {
|
||||
dummyInput,
|
||||
nextEntry
|
||||
} = wutils;
|
||||
const {nextEntry} = wutils;
|
||||
const {dummyInput} = require('./util/primitives');
|
||||
|
||||
/** @typedef {import('../lib/wallet/wallet')} Wallet */
|
||||
|
||||
|
|
|
|||
|
|
@ -28,12 +28,12 @@ const wutils = require('./util/wallet');
|
|||
const {ownership} = require('../lib/covenants/ownership');
|
||||
const {CachedStubResolver, STUB_SERVERS} = require('./util/stub');
|
||||
const {
|
||||
dummyInput,
|
||||
curBlock,
|
||||
nextBlock,
|
||||
curEntry,
|
||||
nextEntry
|
||||
} = wutils;
|
||||
const {dummyInput} = require('./util/primitives');
|
||||
|
||||
const KEY1 = 'xprv9s21ZrQH143K3Aj6xQBymM31Zb4BVc7wxqfUhMZrzewdDVCt'
|
||||
+ 'qUP9iWfcHgJofs25xbaUpCps9GDXj83NiWvQCAkWQhVj5J4CorfnpKX94AZ';
|
||||
|
|
@ -2224,23 +2224,16 @@ describe('Wallet', function() {
|
|||
// Store balance data before rescan to ensure rescan was complete
|
||||
let recipBalBefore, senderBalBefore;
|
||||
|
||||
// Hack required to focus test on txdb mechanics.
|
||||
// We don't otherwise need WalletDB or Blockchain
|
||||
// TODO: Remove this after #888 is merged.
|
||||
wdb.getRenewalBlock = () => {
|
||||
return network.genesis.hash;
|
||||
};
|
||||
|
||||
before(async () => {
|
||||
await wdb.open();
|
||||
await wdb.connect();
|
||||
wallet = await wdb.create();
|
||||
recip = await wdb.create();
|
||||
// rollout all names
|
||||
wdb.height = 52 * 144 * 7;
|
||||
network.names.noRollout = true;
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
network.names.noRollout = false;
|
||||
await wdb.disconnect();
|
||||
await wdb.close();
|
||||
});
|
||||
|
|
@ -2610,21 +2603,15 @@ describe('Wallet', function() {
|
|||
let start;
|
||||
let wallet;
|
||||
|
||||
// Hack required to focus test on txdb mechanics.
|
||||
// We don't otherwise need WalletDB or Blockchain
|
||||
// TODO: Remove this after #888 is merged.
|
||||
wdb.getRenewalBlock = () => {
|
||||
return network.genesis.hash;
|
||||
};
|
||||
|
||||
before(async () => {
|
||||
await wdb.open();
|
||||
wallet = await wdb.create();
|
||||
// rollout all names
|
||||
wdb.height = 52 * 144 * 7;
|
||||
network.names.noRollout = true;
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
network.names.noRollout = false;
|
||||
await wdb.close();
|
||||
});
|
||||
|
||||
|
|
@ -3293,21 +3280,15 @@ describe('Wallet', function() {
|
|||
let wallet;
|
||||
let unsentReveal;
|
||||
|
||||
// Hack required to focus test on txdb mechanics.
|
||||
// We don't otherwise need WalletDB or Blockchain
|
||||
// TODO: Remove this after #888 is merged.
|
||||
wdb.getRenewalBlock = () => {
|
||||
return network.genesis.hash;
|
||||
};
|
||||
|
||||
before(async () => {
|
||||
await wdb.open();
|
||||
wallet = await wdb.create();
|
||||
// rollout all names
|
||||
wdb.height = 52 * 144 * 7;
|
||||
network.names.noRollout = true;
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
network.names.noRollout = false;
|
||||
await wdb.close();
|
||||
});
|
||||
|
||||
|
|
@ -3748,13 +3729,6 @@ describe('Wallet', function() {
|
|||
const network = Network.get('regtest');
|
||||
const wdb = new WalletDB({ network });
|
||||
|
||||
// Hack required to focus test on txdb mechanics.
|
||||
// We don't otherwise need WalletDB or Blockchain
|
||||
// TODO: Remove this after #888 is merged.
|
||||
wdb.getRenewalBlock = () => {
|
||||
return network.genesis.hash;
|
||||
};
|
||||
|
||||
const mineBlocks = async (count) => {
|
||||
for (let i = 0; i < count; i++) {
|
||||
await wdb.addBlock(nextEntry(wdb), []);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ const WalletDB = require('../lib/wallet/walletdb');
|
|||
const Wallet = require('../lib/wallet/wallet');
|
||||
const Account = require('../lib/wallet/account');
|
||||
const wutils = require('./util/wallet');
|
||||
const {nextEntry, fakeEntry} = require('./util/wallet');
|
||||
const {nextEntry, fakeEntry} = wutils;
|
||||
const {dummyInput} = require('./util/primitives');
|
||||
const MemWallet = require('./util/memwallet');
|
||||
|
||||
/** @typedef {import('../lib/primitives/tx')} TX */
|
||||
|
|
@ -541,7 +542,7 @@ describe('Wallet Unit Tests', () => {
|
|||
|
||||
function fakeTX(addr) {
|
||||
const tx = new MTX();
|
||||
tx.addInput(wutils.dummyInput());
|
||||
tx.addInput(dummyInput());
|
||||
tx.addOutput({
|
||||
address: addr,
|
||||
value: 5460
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue