Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow exact match of target === input amount during accumulation. #106

Merged
merged 3 commits into from
Jul 31, 2024

Conversation

ph101pp
Copy link
Contributor

@ph101pp ph101pp commented Jul 24, 2024

Currently this example fails to create a valid selection even though the amount of sats available exactly matches the amount of sats needed.

const privKey = hex.decode('0101010101010101010101010101010101010101010101010101010101010101');
const pubKey = secp256k1.getPublicKey(privKey, true);
const spend = btc.p2wpkh(pubKey, regtest);
const utxo = [
  {
    ...spend, 
    txid: hex.decode('0af50a00a22f74ece24c12cd667c290d3a35d48124a69f4082700589172a3aa2'),
    index: 0,
    witnessUtxo: { script: spend.script, amount: 50_000n },  // same amount
  },
];
const outputs = [
  { address: '2MvpbAgedBzJUBZWesDwdM7p3FEkBEwq3n3', amount: 50_000n }, // same amount
];

const selected = btc.selectUTXO(utxo, outputs, 'default', {
  changeAddress: 'bcrt1pea3850rzre54e53eh7suwmrwc66un6nmu9npd7eqrhd6g4lh8uqsxcxln8',
  feePerByte: 0n, // no fee to remove variability
});

// result: selected === undefined
// expectation: valid selection of utxo[0] as input

This PR fixes the comparison so an exact match results in a valid selection.

@paulmillr
Copy link
Owner

Please add a test.

Comment on lines +973 to +1024

const t3 = (strategy) => {
const FEE = 0n // no fee to test exact amounts
const est = new btc._Estimator(inputs, [{ ...OUTPUTS[0], amount: inputsTotalAmount }], {
feePerByte: FEE,
changeAddress: '2MshuFeVGhXVdRv77UcJYvRBi2JyTNwgSR2',
network: regtest,
allowLegacyWitnessUtxo: true,
createTx: true,
allowSameUtxo: true,
});
const acc = est.result(strategy);
if (!acc) return;
const i = acc.inputs.map((i) => i.witnessUtxo.amount);
const o = acc.outputs.map((i) => i.amount);
const tx = acc.tx;
tx.sign(privKey1);
tx.finalize();
const expFee = BigInt(tx.vsize) * FEE;
return {
i,
o,
txFee: tx.fee,
expFee,
fee: acc.fee,
change: acc.change,
};
};
deepStrictEqual(t3('default'), {
i: inputs.map((i) => i.witnessUtxo.amount).reverse(),
o: [inputsTotalAmount],
txFee: 0n,
expFee: 0n,
fee: 0n,
change: false,
});
deepStrictEqual(t3('accumSmallest'), {
i: inputs.map((i) => i.witnessUtxo.amount),
o: [inputsTotalAmount],
txFee: 0n,
expFee: 0n,
fee: 0n,
change: false,
});
deepStrictEqual(t3('all'), {
i: inputs.map((i) => i.witnessUtxo.amount),
o: [inputsTotalAmount],
txFee: 0n,
expFee: 0n,
fee: 0n,
change: false,
});
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New tests to verify that input amounts === output amounts with 0 fees results in all inputs selected. This is logically sound with regard to how accumulate should work imo.

fee: 1691n,
});
est.amount = 321n;
est.amount = 322n;
Copy link
Contributor Author

@ph101pp ph101pp Jul 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Step 1 outputs -> 2 outputs is now 1 sat higher since exact match doesn't result in extra input

deepStrictEqual(t(est, est.biggest, true), {
amounts: [512n],
change: 221n,
change: 0n,
Copy link
Contributor Author

@ph101pp ph101pp Jul 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems right 321 + 191 = 512

@@ -677,7 +680,7 @@ describe('UTXO Select', () => {
for (const idx of indices) {
tx.addInput(inputs[idx]);
}
tx.addOutputAddress(OUTPUTS[0].address, 100n, regtest);
tx.addOutputAddress(OUTPUTS[0].address, est.amount, regtest);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed hardcoded amount here

@@ -715,8 +718,8 @@ describe('UTXO Select', () => {
est.amount = 256n;
deepStrictEqual(t(est, est.biggest, true), {
amounts: [512n],
change: 221n,
expFee: 191n,
change: 66n,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change explained by fix on L683

221 - ( 256 - 100) = 65

I'm not sure where the 1n fee difference comes from.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the difference comes from size of serialization:

length(varint(1)) != length(varint(2))

Comment on lines +731 to +732
change: 106n,
expFee: 1685n,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change explained by fix on L683

261 - ( 256 - 100) = 105

I'm not sure where the 1n fee difference comes from.

deepStrictEqual(t(est, est.biggest, true), {
amounts: [512n, 256n],
change: 329n,
change: 107n,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change explained by fix on L683

(512 + 256) - (322 + 339) = 107

329 - ( 322 - 100) = 107

Comment on lines +862 to +867
i: [256n],
o: [65n],
txFee: 191n,
expFee: 191n,
fee: 191n,
change: false,
Copy link
Contributor Author

@ph101pp ph101pp Jul 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems right 65 + 191 = 256

@ph101pp
Copy link
Contributor Author

ph101pp commented Jul 31, 2024

Friendly bump on this, is there anything I can do to help getting this merged and published?

@paulmillr
Copy link
Owner

Thanks

@paulmillr paulmillr merged commit 9a29c88 into paulmillr:main Jul 31, 2024
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants