Conversation
Define tr0reloffer/tr0absoffer offer types for Taproot (P2TR, BIP341) CoinJoins: address-type negotiation via dedicated offer types, a Taproot variant of PoDLE that binds the BIP86 output key to the UTXO program, the Schnorr !sig wire encoding, script-type-aware fee sizing, mixed P2WPKH/P2TR maker wallets, and uniform output-type rules.
|
FYI @AdamISZ |
|
As per off-github convo, the obvious concern is the very broad-scale high-level one: in earlier "pit transitions" (from legacy to wrapped segwit, from wrapped segwit to native segwit), the consensus as far as I could tell was: even though it's annoying to split liquidity temporarily, we prefer to have the participants be reasonably comfortable that they are all sticking with the same coin type, to maximize anon set. You seem to be taking the other side of the argument here - and to be clear, it is definitely an argument, no 100% right answer imo - that to achieve more flexibility w.r.t. other protocols, it can be advantageous to not uniform-ize all inputs and outputs to segwit v1 taproot. My best guess is that the majority of users prefer the inflexible version. It is easier to reason about and it does not damage the already fragile (as you have already analyzed in some detail!) anonymity set gains achieved over time in the system. To be clear, the "enforcement" was never absolute; I would have to check the old code more carefully, but, for example, I know that non-segwit inputs were accepted in segwit input sets, for example. I guess the question of 'what is enforced' and 'what is the code intended and coded to do by default' are two slightly separate questions, another thing to consider.
Agreed on the commitment sent H(P2) should follow those rules, which would mean that the privkey wrt the NUMS base, P2, is the flipped privkey if necessary according to the above.
This is where mixed pits get a bit tricky. If you have a taproot wallet you just want to work in a taproot pit, right. I think a cleanly separate pit makes more sense. As noted, allowing silent payments involvement is not incompatible with a 'rigid' bip86 pit. But I think it's better to stick with that on the output side. That was a very old argument in Joinmarket - whether we really should just allow people to send funds to any address, because for some use cases that's "good", and the gut reaction "different address type means the coinjoin didn't anonymize" isn't necessarily correct (in fact Belcher used to argue that that's just wrong). In this case, for taproot outputs it should be quite a bit better. But if we start mixing all types of inputs and change outputs, it feels like it's not a good idea. Sorry to be a bit vague. |
Incorporate AdamISZ review feedback on PR #3: - Make the tr0 pit rigid and separate: all inputs, equal outputs, and change are BIP86 key-path P2TR. Drop the taker-chosen output type and the per-transaction P2WPKH/P2TR input mixing in favor of a clean, uniform Taproot pit, mirroring earlier legacy/segwit pit transitions. - Reframe mixed wallets as two independent pits (Separate Pits): a maker MAY serve sw0 and tr0 but never combines them in one CoinJoin. - Note silent payments stay compatible because their outputs are P2TR. - Rewrite the Taproot PoDLE section to normalize parity on 33-byte compressed points: negate the tweaked key when q*G is odd so the existing discrete-log algorithm and H(P2) commitment run unchanged. - Add a Rationale section documenting the rigid-pit decision and simplify the Signing and Fees sections to Taproot-only.
|
Thanks a lot for the feedback, very useful. Incorporated all suggestions: 1beb9da Does it have you ACK now? What about @takinbrrrr and @1440000bytes ? |
1440000bytes
left a comment
There was a problem hiding this comment.
The document does not mention anything about annex.
Does it need to? According to BIP341 we should not be using it. |
…sighash) Incorporate the second round of review feedback on PR #3: - Remove Silent Payments (BIP352): a receiver derives the output key from the sum of all inputs, but in a CoinJoin no party knows the other participants' input keys, so SP outputs cannot be detected or spent. Reframe the Motivation around future taproot protocols (MuSig2, PTLC Lightning, swaps) and explain the BIP352 incompatibility in Rationale. - Fix the Taproot PoDLE derivation: normalize the BIP341 internal key to even Y before applying the tweak (q = p + t is wrong for odd-Y internal keys). Clarify P/Q notation and drop the nonce-confusing 'k'. - Require SIGHASH_DEFAULT for all tr0 inputs; verifiers reject non-64-byte signatures, trailing 0x00, and any annex. - Condense the rigid-versus-flexible rationale (was repeated).
The Taproot !sig payload carries the 32-byte x-only output key after the 64-byte Schnorr signature (mirroring the P2WPKH layout) so the receiver can identify the input and verify the signature. The previous table omitted it, which would make an independent implementation produce an incompatible !sig. The key is not placed in the witness (a key-path witness is just the 64-byte signature).
…orcement optional Align the spec with the intended (and implemented) behavior: each participant MUST produce its own equal output and change as P2TR and MUST NOT mix types in a tx it builds, but enforcing uniformity on counterparties is RECOMMENDED, not mandatory (an implementation may tolerate a slightly non-uniform counterparty for liquidity, like accepting more fee than required). The security-critical rules (PoDLE binding, SIGHASH_DEFAULT, full-prevout signing, no annex) remain strict MUSTs. Add an 'enforcement is a target, not a consensus rule' note to the Rationale.
On a second thought, I think its okay to ignore annex in this doc. Although, some people are using it on-chain. |
Specifies a collaborative BIP352 ECDH protocol that lets a taker pay a silent payment address from a tr0 CoinJoin: blinded partial shares (Somsen blind DH) from each maker, proven with DLEQ (the PoDLE primitive), exchanged via new encrypted !spreq/!spshare commands. Requires no change to BIP352; documents the remaining limitations (no formal security proof, static DH oracle exposure).
QA hardening of the collaborative silent-payment derivation: - Bound the static Diffie-Hellman oracle exposure by the sum of !spreq group counts, not just one response per session (a single count=255 request yields 255 evaluations). Require distinct X_i and a small maker-side count cap. - Mirror BIP352's abort conditions that were implicit: reject infinite A_sum, and fail on invalid input_hash or t_k scalars, matching the validated single-party create_outputs primitive. - Clarify that the DLEQ binds X into the challenge (five-point preimage), giving per-base binding and separation from the four-point PoDLE proof. Math verified against a BIP352 reference receiver and adversarial probes (DLEQ soundness, blinding unlinkability, input-set independence).
- Add scripts/verify_sp_ecdh.py: a dependency-free check that the collaborative derivation matches an independent BIP352 reference receiver and resists the known attack vectors. Reference it from the spec. It verifies correctness, not security; it is not a proof. - State plainly that no formal proof for the collaborative setting exists or is claimed (still an open research question per BIP352 and Optech), and mark the feature experimental. - Note that BIP352's hashing of the ECDH secret (a KDF) weakens the Brown-Gallant static-DH concern relative to textbook ElGamal, while keeping the per-key evaluation bound. - Drop the 'planned for joinmarket-ng' reference-implementation line.
- Add a Rationale paragraph distinguishing what existing proofs cover. Additive-tweak safety (Groth-Shoup 2021/1330, deterministic wallets) and DL aggregate signatures (DahLIAS 2025/692) secure the signature side but do not cover the static-DH oracle (returning alpha*X for a chosen point), which is the actual open gap; the plausible route is transferring the Privacy Pass one-more-decryption analysis. - Note that restricting silent payment outputs to the taker sidesteps the multiparty same-address collision and label-renegotiation problem, since one party derives every silent payment output.
|
Hi @josibake @jonasnick @real-or-random We're trying to implement paying to a Silent Payment address from a JoinMarket CoinJoin and maybe you can help us with the protocol flow focusing on the security part. On jmp-0006.md, additive-tweak and aggregate-signature proofs cover the signing side. The collaborative SP construction additionally exposes a static-DH oracle (alpha·X for chosen X) on each participant's input key, with a DLEQ. Is there (or can there be) a one-more-DH/Privacy-Pass-style reduction that bounds the key-recovery risk for this exact construction, given BIP352 hashes the shared secret? |
|
The practical question for us (@AdamISZ @1440000bytes @takinbrrrr) now is if this is all worth it. In practical terms, we would be getting into some complex cryptography and with significant wire protocol changes for being able to pay to SP. And developing the Taproot pit just to be able to spend from SP is probably not worth it. Right now, one can just spend the SP received UTXOs to a P2WPKH address first and then coinjoin. And for spending the other way around. The additional cost for receiving is an additional transaction which has network fees and some privacy implications (timing, easier to track destination maker cluster for md0, ...). More or less the same applies for spending. My gut feeling is that we should not pursue this yet. But wait until a Taproot pit has more advantages. I'm thinking of a "mini" CoinJoinXT with swaps, PTLC based LN, ... What do you think? Thanks for all the feedback BTW. |
No from my side, sorry. I don't have the bandwidth. |
I think we should have taproot support in joinmarket-ng. Not sure about silent payments. |
|
Hi @m0wer , thanks for the ping and for taking a look at this. Pinging @SomsenRuben, as well. Silent payments was designed with CoinJoin support in mind, so it would be cool to see this materialise. I am fairly bandwidth constrained at the moment and not super familiar with how JoinMarket works, but I can try to be helpful and answer questions on the silent payments specific side. I also see some (appropriate) push back on implementing it, which I completely understand. Feel free to ping again in the future when this seems more tenable, if you all decide its not worth it right now. |
|
Hi @josibake, Thanks for chiming in! The main concern is the lack of a formal security proof as discussed at @RubenSomsen 's gist: https://gist.github.com/RubenSomsen/be7a4760dd4596d06963d67baf140406 The specifics of JoinMarket don't matter too much, can be reduced to building a transaction that pays to a Silent Payment address (and other non SP ones) with untrusted peers (each with their own keys). This is currently documented as not recommended on BIP352, but maybe some of you are working on it or have ideas about it. |
|
Regarding the lack of a formal security proof, I was writing a more formal, rigorous version of the silent payments spec with the goal of moving towards a formal security proof. This isn't the same as a security proof for the blinding step, but does feel like a prerequisite. Even without a security proof, a more formalised version of the spec will at least make it easier to reason about composing the protocol with other protocols such as blinding. I'd be more motivated to pick that work back up of it would help here. I agree that the specifics don't matter too much for JoinMarket specifically, but I do think silent payments in a collaborative transaction building protocol with untrusted peers is not trivial. There was some discussion around generalising this problem beyond silent payments in the past, but I'm not sure of the status of that work. All that being said, I think we have the right primitives (blinding, DLEQ) to be able to do this, but I don't want to hand wave over the complexity. |
But that's the ecash write up (that became cashu), not the silentpayments one? I guess just the wrong link? I'll repeat what I said earlier upthread, to @m0wer , I wouldn't be pursuing this, as there's enough other things to do for now. But, meh, just my 2c. |
It's linked in the BIP 352, reference 6: https://github.com/bitcoin/bips/blob/master/bip-0352.mediawiki#rationale-and-references There's a bit more of explanation there.
Agreed, thanks. |
Oh, I see what you mean now. You'd think I'd have understood, but nope 😆 |
Define
tr0reloffer/tr0absofferoffer types for Taproot (P2TR, BIP341) CoinJoins: address-type negotiation via dedicated offer types, a Taproot variant of PoDLE that binds the BIP86 output key to the UTXO program, the Schnorr!sigwire encoding, script-type-aware fee sizing, mixed P2WPKH/P2TR maker wallets, and uniform output-type rules.Specially motivated by Silent Payments (BIP352).