Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | 1x 1x 1x 1x 207x 1x 95x 1x 76x 1x 35x 35x 35x 35x 104x 35x 35x 1x 9x 38x 9x 9x 9x 9x 36x 36x 36x 36x 36x 6x 2x 4x 30x 30x 30x 30x 5x 2x | import { FundingInput } from '@node-dlc/messaging';
import { DualFundingTxFinalizer } from './TxFinalizer';
export interface UTXO {
txid: string;
vout: number;
value: number;
address: string;
derivationPath?: string;
}
const TX_INPUT_SIZE = {
LEGACY: 148,
P2SH: 92,
BECH32: 69,
};
const inputBytes = () => {
return BigInt(TX_INPUT_SIZE.BECH32);
};
export const dustThreshold = (feeRate: bigint): bigint => {
return BigInt(inputBytes()) * feeRate;
};
// order by descending value, minus the inputs approximate fee
const utxoScore = (x: UTXO, feeRate: bigint): bigint => {
return BigInt(x.value) - feeRate * inputBytes();
};
export const dualFees = (
feeRate: bigint,
numInputs: number,
numContracts: number,
): bigint => {
const input = new FundingInput();
input.maxWitnessLen = 108;
input.redeemScript = Buffer.from('', 'hex');
const fakeSPK = Buffer.from(
'0014663117d27e78eb432505180654e603acb30e8a4a',
'hex',
);
const offerInputs = Array.from({ length: numInputs }, () => input);
const acceptInputs = Array.from({ length: 1 }, () => input);
return new DualFundingTxFinalizer(
offerInputs,
fakeSPK,
fakeSPK,
acceptInputs,
fakeSPK,
fakeSPK,
feeRate,
numContracts,
).offerFees;
};
/**
* Selects UTXOs for dual funding
* @param utxos - UTXOs to select from
* @param collaterals - Collaterals to fund (just one for non-batch tx)
* @param feeRate - Fee rate in satoshis per byte
* @returns Inputs and fee
* @description
* Add inputs until we reach or surpass the target value (or deplete)
* Worst-case: O(n)
*/
export const dualFundingCoinSelect = (
utxos: UTXO[],
collaterals: bigint[], // in satoshis
feeRate: bigint,
): { inputs: UTXO[]; fee: bigint } => {
utxos = [...utxos].sort((a, b) =>
Number(utxoScore(b, feeRate) - utxoScore(a, feeRate)),
);
let inAccum = 0;
const inputs: UTXO[] = [];
const outAccum =
collaterals.reduce((acc, val) => acc + val, BigInt(0)) +
dustThreshold(feeRate); // sum of collaterals
for (let i = 0; i < utxos.length; ++i) {
const utxo = utxos[i];
const utxoBytes = inputBytes();
const utxoFee = feeRate * utxoBytes;
const utxoValue = utxo.value;
// skip detrimental input
if (utxoFee > utxo.value) {
if (i === utxos.length - 1)
return {
fee: dualFees(feeRate, 1, collaterals.length),
inputs: [],
};
continue;
}
inAccum += utxoValue;
inputs.push(utxo);
const fee = dualFees(feeRate, inputs.length, collaterals.length);
// go again?
if (inAccum < outAccum + fee) continue;
return { inputs, fee };
}
return {
fee: dualFees(feeRate, 1, collaterals.length),
inputs: [],
};
};
|