Skip to content

Commit

Permalink
mtx: separate preferred and existing inputs.
Browse files Browse the repository at this point in the history
  • Loading branch information
nodech committed Dec 3, 2021
1 parent 3551bb0 commit 5c41f3d
Show file tree
Hide file tree
Showing 2 changed files with 628 additions and 67 deletions.
171 changes: 126 additions & 45 deletions lib/primitives/mtx.js
Original file line number Diff line number Diff line change
Expand Up @@ -1125,8 +1125,8 @@ class MTX extends TX {
// Select necessary coins.
const select = await this.selectCoins(coins, options);

// Make sure we empty the input array.
this.inputs.length = 0;
for (const coin of select.viewCoins)
this.view.addCoin(coin);

// Add coins to transaction.
for (const coin of select.chosen)
Expand Down Expand Up @@ -1413,6 +1413,27 @@ class MTX extends TX {
/**
* Coin Selector
* @alias module:primitives.CoinSelector
* @property {TX} tx - clone of the original mtx.
* @property {CoinView} view - reference to the original view.
* @property {Coin[]} coins - List of available coins.
* @property {Amount} outputValue - Total output value.
* @property {Coin[]} chosen - List of chosen coins to add.
* @property {Coin[]} viewCoins - List of coins to only apply to coinview.
* @property {Address} changeAddress - change address.
* @property {Amount} change - change value.
* @property {Amount} fee - Value of fee.
* @property {String} selection - selection type.
* @property {Boolean} subtractFee - whether to subtract fee from the output.
* @property {Number} subtractIndex - index of the output to subtract fee from.
* @property {Number} height - height of the chain. (to check spendability)
* @property {Number} depth - depth or confirmations for the coin.
* @property {Number} hardFee - fixed fee.
* @property {Number} rate - Rate of dollarydoo per kB.
* @property {Number} maxFee - maximum fee we are willing to pay.
* @property {Boolean} round - round to the nearest kilobyte.
* @property {Number} coinbaseMaturity - when do CBs become spendable.
* @property {Function} estimate - Input script size estimator.
* @property {Object[]} inputs - preferred inputs.
*/

class CoinSelector {
Expand All @@ -1430,6 +1451,7 @@ class CoinSelector {
this.outputValue = 0;
this.index = 0;
this.chosen = [];
this.viewCoins = [];
this.change = 0;
this.fee = CoinSelector.MIN_FEE;

Expand All @@ -1445,6 +1467,7 @@ class CoinSelector {
this.coinbaseMaturity = 400;
this.changeAddress = null;
this.inputs = new BufferMap();
this.preferredInputs = new BufferMap();

// Needed for size estimation.
this.estimate = null;
Expand Down Expand Up @@ -1553,7 +1576,7 @@ class CoinSelector {
const prevout = options.inputs[i];
assert(prevout && typeof prevout === 'object');
const {hash, index} = prevout;
this.inputs.set(Outpoint.toKey(hash, index), i);
this.preferredInputs.set(Outpoint.toKey(hash, index), i);
}
}

Expand Down Expand Up @@ -1586,7 +1609,6 @@ class CoinSelector {
this.chosen = [];
this.change = 0;
this.fee = CoinSelector.MIN_FEE;
this.tx.inputs.length = 0;

switch (this.selection) {
case 'all':
Expand Down Expand Up @@ -1689,47 +1711,8 @@ class CoinSelector {
*/

fund() {
// Ensure all preferred inputs first.
if (this.inputs.size > 0) {
const coins = [];

for (let i = 0; i < this.inputs.size; i++)
coins.push(null);

// check coinview first
for (const [key, index] of this.inputs.entries()) {
const prevout = Outpoint.fromKey(key);

if (this.view.hasEntry(prevout)) {
const coinEntry = this.view.getEntry(prevout);

coins[index] = coinEntry.toCoin(prevout);
this.inputs.delete(key);
}
}

// skip this if we resolved all inputs.
if (this.inputs.size > 0) {
for (const coin of this.coins) {
const {hash, index} = coin;
const key = Outpoint.toKey(hash, index);
const i = this.inputs.get(key);

if (i != null) {
coins[i] = coin;
this.inputs.delete(key);
}
}
}

if (this.inputs.size > 0)
throw new Error('Could not resolve preferred inputs.');

for (const coin of coins) {
this.tx.addCoin(coin);
this.chosen.push(coin);
}
}
this.resolveInputs();
this.resolvePreferred();

if (this.isFull())
return;
Expand All @@ -1751,6 +1734,104 @@ class CoinSelector {
}
}

/**
* Resolve coins for existing inputs.
*/

resolveInputs() {
if (this.inputs.size === 0)
return;

// Ensure we have coins for the existing inputs.
const view = this.tx.view;
const coins = [];

for (let i = 0; i < this.inputs.size; i++)
coins.push(null);

// check coinview first
for (const [key, index] of this.inputs.entries()) {
const prevout = Outpoint.fromKey(key);

if (this.view.hasEntry(prevout)) {
const coinEntry = this.view.getEntry(prevout);

coins[index] = coinEntry.toCoin(prevout);
this.inputs.delete(key);
}
}

// skip this if we resolved all inputs.
if (this.inputs.size > 0) {
for (const coin of this.coins) {
const {hash, index} = coin;
const key = Outpoint.toKey(hash, index);
const i = this.inputs.get(key);

if (i != null) {
coins[i] = coin;
this.inputs.delete(key);
}
}
}

if (this.inputs.size > 0)
throw new Error('Could not resolve existing inputs.');

for (const coin of coins) {
view.addCoin(coin);
this.viewCoins.push(coin);
}
}

/**
* Resolve preferred inputs.
*/

resolvePreferred() {
if (this.preferredInputs.size === 0)
return;

const coins = [];

for (let i = 0; i < this.preferredInputs.size; i++)
coins.push(null);

// check coinview first
for (const [key, index] of this.preferredInputs.entries()) {
const prevout = Outpoint.fromKey(key);

if (this.view.hasEntry(prevout)) {
const coinEntry = this.view.getEntry(prevout);

coins[index] = coinEntry.toCoin(prevout);
this.preferredInputs.delete(key);
}
}

// skip this if we resolved all inputs.
if (this.preferredInputs.size > 0) {
for (const coin of this.coins) {
const {hash, index} = coin;
const key = Outpoint.toKey(hash, index);
const i = this.preferredInputs.get(key);

if (i != null) {
coins[i] = coin;
this.preferredInputs.delete(key);
}
}
}

if (this.preferredInputs.size > 0)
throw new Error('Could not resolve preferred inputs.');

for (const coin of coins) {
this.tx.addCoin(coin);
this.chosen.push(coin);
}
}

/**
* Initiate selection from `coins`.
* @param {Coin[]} coins
Expand Down
Loading

0 comments on commit 5c41f3d

Please sign in to comment.