feat(selection)!: declare PSBT_IN_SIGHASH_TYPE on Plan-derived inputs#74
feat(selection)!: declare PSBT_IN_SIGHASH_TYPE on Plan-derived inputs#74evanlinjin wants to merge 2 commits into
PSBT_IN_SIGHASH_TYPE on Plan-derived inputs#74Conversation
3ff0b79 to
f48fad7
Compare
|
Thanks for the detailed description. I was in favor of the original proposal in #60 - remove the |
What's the motivation for
|
There was a problem hiding this comment.
Concept ACK
I like the approach to defaulting to "safe" sighash ALL and leaving it up to the user to manually op-out of the default and set their non-typical sighash types.
The coding is well documented and easy enough to follow. But can you add a test for the mixed sighash type concern described in problem 2 ? I don't doubt it will pass but would be good to have a unit test to make sure it doesn't break in the future.
8a758f1 to
afff4c6
Compare
|
@notmandatory I added the mixed sighash test! |
This comment was marked as low quality.
This comment was marked as low quality.
|
@ValuedMammal Let me present my reasoning clearly so it's easier for you to address. Here are my claims:
Could you indicate which one you reject? Need to know what we are arguing about. |
|
@evanlinjin did you check how the core wallet handles this? |
Thanks for asking. I just checked and I noticed a few things.
In other words, sighash correctness is enforced by logic that is not mandated by BIP-174: BIP-174 only requires a finalizer to reject signatures that don't match a specified sighash. Core goes further — it defaults the expectation to My TakeawayAfter doing this research, I've reconsidered my stance on how we should enforce sighash correctness.
One caveatTakeaway 3 is load-bearing. Core can leave the field unset because it is its own strict finalizer. When To cover that case without re-introducing the field by default, I propose we keep @notmandatory @ValuedMammal would you be happy with this approach? |
nymius
left a comment
There was a problem hiding this comment.
Is the final concern for this field related to fee estimation?
IMHO, the exceptional case is the taproot default, if it wasn't because of it, this shouldn't be an issue, as all sighash flags would occupy the same single byte.
If the Finalizer spec already handles mismatches between the expected sighash and the given sighash, and Signer only uses the specified sighash if set and acceptable, should we handle this logic here at all?
Looking at the planning module, the reason why satisfaction weight doesn't overshoot the estimation is because it assumes the signer will use the cheapest signature in case of mixed assets. Also, it only cares about the signature itself.
Why we aren't aiming to fix this under-estimation at the selection level rather than setting some parameters that don't have much influence in the sat amount once selection is done?
Can we have a e2e test like synopsis.rs where the target feerate is undershoot?
|
Thanks @nymius, a few of the questions are already covered in the thread but I'll address them all for further clarity.
Not only. The root concern is that the spec allows a Signer to create a signature with a sighash of any type if This is dangerous in the following ways:
If purely following the spec, the Finalizer will only handle mismatches if Bitcoin Core's finalizer does not follow the spec. As mentioned in my comment here, Bitcoin Core's Finalizer goes beyond the spec to ensure the resultant tx is what the user expected.
Are you suggesting that we budget pessimistically? Over-funding the majority of taproot transactions to defend against a potential signer deviation seems like the wrong trade. We can solve the same problem with enforcement (either via the PSBT field or our hardened finalizer). This gets the same correctness without sacrificing on fees.
Good idea. Will add as a follow up once there is some sort of rough consensus on the right solution. My proposed solution is mentioned in this comment here. Let me know what you think. |
|
@evanlinjin thanks for the Core research and detailed analysis of what it is doing. I'm in favor of your update to follow Core's approach by hardening our finalizer and making the Also if I understand this right we'll need to carefully explain in future documentation/tutorials that if the user knows they'll be using a non-default taproot sighash then they MUST set the |
I was considering this the fee estimation case.
For this I would move the responsability to the psbt package and implement a sane signer like the one in core.
Ok, I think this is reason enough to always set the field when we are about to introduce ambiguity or possible errors, like this case.
No, that's simple but brittle. I was wondering if there were other alternatives that allowed coin selection to get some introspection in the sighash. It seems not.
I would prefer to leave the Finalizer details for the package we are using now, or will be used in the future ( For taproot inputs I would use the logic in this PR to extract the safest flag. If the user pretends to change the flag, he can do it later. I wouldn't prevent the user with more than a warning if he reintroduces the chances of the misestimation of fees after setting the flag. |
Yes. So purely based on the BIP-174 spec, there is no other way to communicate the correct sighash to the signer other than Either case, we should expect our Finalizer to ensure DEFAULT for Schnorr and ALL for everything else unless if stated otherwise by My new idea of the |
|
I prefer to not introduce |
Just to clarify, do you mean the user should set the exposed If so then that works for me as long as docs are added to make it clear this must be done when a taproot key spend input is going to be get a non-standard sighash post-selection. |
I meant to always set |
|
As a side note, |
Previously, we had a string constants for test descriptors and test keys where the test keys were also hardcoded in the test descriptors. We remove the hard coded test descriptor and rename variables for clarity.
Any Plan whose Schnorr witness template includes a 64B signature gets a DEFAULT sighash type (this includes mixed-size plans). All other plans require ALL sighash.
afff4c6 to
ca41645
Compare
|
@ValuedMammal @notmandatory @nymius The PR has been changed to always set |
PSBT_IN_SIGHASH_TYPE on Plan-derived inputs
Closes #60.
Supersedes #69.
Description
Two issues with how
PsbtParams::sighash_typeworked.1. The field itself wasn't very useful. It was a tx-wide uniform override. However, the realistic use cases for non-
ALLsighashes are inherently per-input.2. It didn't guard against the catastrophic Taproot case. Taproot signature size depends on sighash (
SIGHASH_DEFAULTis 64 bytes, other Taproot sighashes are 65 bytes). A miniscriptPlanhas the sighash type pre-baked andPlan::satisfaction_weight()is only accurate if the signer uses the assumed sighash. Leavingsighash_type = Nonelets a spec-compliant signer pick anything (BIP-174: "should sign using SIGHASH_ALL, but may use any sighash type they wish"), which can underfund the tx or weaken its commitment scheme (e.g. a signer choosingNONEwould let a third party replace outputs).This PR removes
PsbtParams::sighash_typeand instead writesPSBT_IN_SIGHASH_TYPEon every Plan-derived input with a safe default:sighash_typeTapSighashType::DefaultTapSighashType::AllEcdsaSighashType::AllPSBT-derived inputs (
Input::from_psbt_input) are never touched — they come in with their ownsighash_typealready set.Callers needing a non-
ALLsighash (SINGLE,NONE,*_ANYONECANPAY) mutatepsbt.inputs[i].sighash_typedirectly on the returned PSBT. Plans needing non-ALLsemantics on every key should be built with uniformTaprootCanSign::sighash_default = falseso the safety lock doesn't fire.Notes to the reviewers
This PR differs from #60's proposal in two places.
The original issue suggested auto-writing
DEFAULTonly on uniformly-64B Plans (a plan can require multiple signatures with different sighash types). In this PR, aPlanwith any 64B placeholder triggerssighash_type = DEFAULT.The original issue proposed writing
Noneas default for "weight-safe" inputs. This PR writesALLfor them.ALLcloses that loophole for spec-compliant signers —ALL/DEFAULTis what the super-majority of callers would want, and any deviation should be an explicit caller action (post-construction mutation), not a silent signer choice.Alternative (rejected) solutions
sighash_typefield in Plan-basedbdk_tx::Input— this is a PSBT-layer concern. An input's witness can also have multiple signatures.declare_sighash: boolflag toPsbtParams— earlier iterations of this PR explored this, but the simpler "always declare, mutate post-construction if you need otherwise" design landed instead (thanks @nymius for the suggestion). One less knob, same expressiveness.Changelog notice
Before submitting