bip352: clarify/fix specification to disallow sender to create outputs the receiver can't find#2153
bip352: clarify/fix specification to disallow sender to create outputs the receiver can't find#2153edilmedeiros wants to merge 5 commits into
Conversation
|
Pinging BIP authors @RubenSomsen, @theStack and @josibake for feedback, please. |
The specification limits how many keys the receiver is allowed to scan. Thus, we should not allow the sender to generate more than that many keys/outputs to prevent loss of funds. Co-authored-by: themhv <themhv@proton.me> Co-authored-by: joaozinhom <joaomcr@proton.me> Co-authored-by: IsaqueFranklin <isaque@harlock.xyz> Co-authored-by: lucasdcf <lucasdcf@users.noreply.github.com> Co-authored-by: oleonardolima <oleonardolima@users.noreply.github.com> Co-authored-by: Lorenzo <maturanolorenzo@gmail.com>
Co-Authored-By: themhv <themhv@proton.me> Co-Authored-By: joaozinhom <joaomcr@proton.me> Co-Authored-By: IsaqueFranklin <isaque@harlock.xyz> Co-Authored-By: lucasdcf <lucasdcf@users.noreply.github.com> Co-Authored-By: oleonardolima <oleonardolima@users.noreply.github.com> Co-Authored-By: Lorenzo <maturanolorenzo@gmail.com>
The original implementation references does a trick to avoid generating more outputs thatn the receiver is allowed to scan: it clones the payment specification before calling the crate_outputs() function. Then, when creating groups, the function will see the same address many times and create an artifically big group. This is not a reasonable interpretation of the spec, i.e., if I want to send 2324 outputs for the same address, this should be interpretated as a single group containing a single address. This is what this commit implements. Note that this will NOT pass the unit tests. Next commit will fix this. Co-Authored-By: themhv <themhv@proton.me> Co-Authored-By: joaozinhom <joaomcr@proton.me> Co-Authored-By: IsaqueFranklin <isaque@harlock.xyz> Co-Authored-By: lucasdcf <lucasdcf@users.noreply.github.com> Co-Authored-By: oleonardolima <oleonardolima@users.noreply.github.com> Co-Authored-By: Lorenzo <maturanolorenzo@gmail.com>
Co-Authored-By: themhv <themhv@proton.me> Co-Authored-By: joaozinhom <joaomcr@proton.me> Co-Authored-By: IsaqueFranklin <isaque@harlock.xyz> Co-Authored-By: lucasdcf <lucasdcf@users.noreply.github.com> Co-Authored-By: oleonardolima <oleonardolima@users.noreply.github.com> Co-Authored-By: Lorenzo <maturanolorenzo@gmail.com>
58c4ac6 to
47ef418
Compare
|
Rebased after #1961. |
|
Thanks for taking a deeper look at this! Note that the sole reason for introducing a
and then there should be no need to do any other changes? |
Maybe, since this grouping operation is the source of the confusion. Feels like the specification change introduced by #2106 was redacted after the reference implementation (I was not there), not the other way around, and it leaked this grouping solution into the spec. I couldn't came out with an alternative wording that expresses the spirit of the grouping (limit the amount of outputs the sender can generate to a single receiver) without potentially introducing new edge cases. The cleanest I could get was adding the check on top of
Agree, but I still think the specification should specifically disallow the pathological cases we can anticipate (e.g. splitting a payment into many outputs for "no apparent reason"). I can imagine someone forcing a constant amount for each utxo trying to mimic a coinjoin tx, and thus splitting a single payment in more than one output even if not needed. During the deep dive, we were indeed trying to attack the protocol, not only understand it. |
Yes, I agree with that, however for clarity sake I think it would be better to just check the maximum number of outputs to be smaller than I think the main inconsistency this PR is trying to solve is that, on the spec, where the BIP is defining how to create the outputs, we have something like this:
And then the spec proceeds to a
We are checking for the number of recipients, but the most important check would be for the number of outputs for a single I think this leaves room for naive implementations (or dumb AI agents) to create more outputs for a single Also, I have been looking into some wallets that implement silent payments, specifically Cake Wallet, Blue Wallet and Wasabi. Blue Wallet and Wasabi did not event implement the check for the |
|
Hey, what’s the status here? |
|
I'm not convinced that the described scenario of potential BIP misinterpretation warrants a change in the reference implementation and/or test vectors. If anything, I'd prefer to clarify the grouping mechanism in text with a simple change. As stated earlier, the currently used (B_m, count) representation in the test vectors was introduced merely for a space-efficient encoding of .json file, and I wouldn't expect to find that being used in any real-world implementation (outside of tests). Looking at a few existing SP wallets/libraries and how they structure groups, it seems all of them represent the
This indicates that, at least in those implementations, repeated addresses within a group are not treated any differently, i.e. duplicates are fine. Under that interpretation, the current "if any of the grouping sizes exceed K_max, fail" check should already be sufficient to achieve the following "number of outputs for a B_scan" check:
I might still miss something though, happy to hear other opinions. [1] https://github.com/sparrowwallet/drongo/blob/077d2142cc3aad84f6f58868cf8f17fc61027fdc/src/main/java/com/sparrowwallet/drongo/silentpayments/SilentPaymentUtils.java#L279 |
|
I'm not sure what's the best way forward, probably to close the PR. It's not clear to me how the ownership model for the bips work. I'm bringing light to a potential problem and suggesting alterations to avoid it in good faith. If we failed to convince the authors of the problem, I don't know what more can be done. The first commit brings the suggested fix in the text alone, it can be cherry-picked. I couldn't figure out a better way to explain the group concept, it truly feels awkward. Anyways, it's goal is to limit the number of outputs one can generate for a single receiver, but as we argue, it does not achieve that. A specification is supposed to protect future implementations. The commits changing the reference code show a potential implementation that abuses a spec-compliant receiver capability (and propose how to fix it). And it does so by processing the input test vectors differently (more literally) from the expected (something that people do all the time…). I think this point is particularly relevant as there's no bip committee that will evaluate/attest candidate implementations for compliance. We have to achieve this by trying our best so that every developer interprets the spec in the same way. Finally, checking what current implementations do merely checks if there's a problem today (which we briefly checked before opening this here, otherwise we would have proposed fixes with discretion in each project prior to opening this). The specification is the source of correctness for the implementations, not the other way around. PS: just to not be misinterpreted by excess of emphasis, I truly want silent payments to be prevalent in the ecosystem, it's a step in the right direction. |
|
Hey @edilmedeiros , apologies for the slow response on this, been busy! A big thanks to you and the Vintuem team for digging into the spec, especially with a goal of hardening. Much appreciated. I do think the claims in the PR are a bit overblown, but will agree there is some ambiguity in the text. My intended reading of the spec is that the sender is grouping "output requests," i.e. "I wan't this many taproot outputs in the final transaction." It was not meant to read as unique silent payment addresses. We mention elsewhere in the BIP that the same silent payment address can be used repeatedly to get multiple unique outputs. With this understanding, the same However, just because this is clear to me doesn't mean it's clear in the text! I’d prefer if we kept this PR as a minimal text-only clarification. What if we change:
to:
And changing:
to:
With that wording, I think the intended algorithm is more clear. What do you think @edilmedeiros , @IsaqueFranklin and @theStack ? |
#2106 introduced a limit on how many keys the receiver is allowed to scan. But the sender specification still allows for creating outputs that exceed that limit, risking loosing funds. I tried to organize each commit so to make review as easy as possible:
Rationale
The specification of the sender and receiver seems to be out of sync, i.e. the sender can create a spec-compliant transaction such that a spec-compliant receiver will not find all funds received, implying the loss of funds. This is an edge case more theoretical than practical. I consulted @RubenSomsen about this possibility and we agreed opening a PR straight away would be safe for current users of silent payments.
#2106 introduced the
K_maxparameter which spirit is to limit how much effort the receiver is allowed to perform when scanning a candidate silent payment transaction.But things are not as clear in the sender side: the spec asks the sender to:
Let's analyze the case in which one is using a single silent payment address, but want to create 2324 (K_max + 1) outputs for the receiver. This is exactly scenario was covered by a new test case added together with #2106.
Well, there's one silent payment address, so that would make for one group with a single
B_mwithin. Then, the required check (group size should not exceedK_max) should not fail and we are going to proceed with output generation with no more additional checks required by the specification. We would end up with a transaction with 2324 outputs, but the receiver will only scan for 2323 (K_max) and miss one output.Note that the receiver could continue scanning anyhow and find all his funds, but version 1.1.x of the spec won't allow this.
About the changes in the reference implementation
Turns out that the test case expected result is that the described transaction creation should fail. And it does so in the reference implementation.
#2106 introduced a field
counton therecipientsspecification for tests and added a test case in which the sender is given a single silent payment address and wants to create 2324 outputs for it, just as described above.Before calling
create_outputs(), which is the reference implementation of this specification, the test harness will clone the silent payment addresscounttimes and pass those copies tocreate_outputs(). Thus, in the sender process, it will create a group with 2324 elements, all of which carry the same silent payment address (i.e. the sameB_m), and will fail as per the spec.I don't believe this to be what any developer would implement from reading the specification:
Indeed, I found this during a deep dive on the silent payments specification at Vinteum together with @lorenzolfm, @oleonardolima, @lucasdcf, @IsaqueFranklin, @joaozinhom, and @TheMhv and we all agreed we would not implement the specification like in the reference implementation.
We started looking at what wallets that support silent payments currently do and some didn't even implemented v1.1.0 yet, which introduced the described interpretation.