How about we deprecate SIGHASH_UTXO?

What are you talking about?

If we look at this part of a sentence from the rationale which is the way to handle the vulnerability with SIGHASH_ALL:

signers could download and verify the spent output of each transaction referenced by each input prior to signing vulnerable transactions

How does this “boggles the mind”?

I think I just realized the problem here.
I’m talking about creation of signatures. What data that is needed for a wallet to sign an input. If a wallet needs to know, for instance, how many sats that are spent it eithers requires the prev transaction OR the utxo.
I think Tom is talking about validation of signatures and there he is right, no additional data then already was in the transaction is needed with SIGHASH_ALL, but the UTXO data is needed with SIGHASH_UTXOS.

If there is SPV wallets that do signature validation additional data is needed. Not sure which SPV wallets that does that, EC does not and considers a mined transaction as valid.

1 Like

With SIGHASH_ALL you need the correct UTXO data only for the 1 UTXO you’re signing, and correct prevot refs (TXID:n) of other inputs’ UTXOs.
With SIGHASH_UTXOS you need the correct UTXO data for all the UTXOs the TX is spending, even if they don’t belong to you.

If you don’t care about knowing the details of other inputs UTXOs, then you can just sign yours with SIGHASH_ALL and not fetch anything for other inputs.

If you do care, you can either:

  • fetch whole source TXs, verify against prevout TXIDs, and once confirmed you’re happy with what you’re interacting with - sign with SIGHASH_ALL, or you can
  • fetch just the source outputs, assume the data is legitimate, and sign with SIGHASH_UTXOS. If data was corrupt, the TX you signed will be invalid and won’t be accepted by the network, which protects you from interacting with some other UTXO under false pretenses.

TX validation requires all the prevouts. TX validation context is the TX + prevouts + height&MTP. But if someone gives you just the sighash, sure, you can verify the signature against the “message”, but you can’t verify whether it’s actually signing a valid TX, whether the “message” is a hash of valid TX.

If you get a SPV proof then you can have assurances that the TX has been found valid by others - as evidenced by accumulated PoW on top of the TX.

Could you clarify how much data per transaction or per use case are we talking about?

Tom failed to do it, instead he got angry.

Also when is this data sent? For example:

  • Every time wallet creates a TX or maybe
  • When wallet requests a TX from node software/fulcrum

Context is interaction with external UTXOs, which can mean a person signing their input in a CoinJoin transaction with someone else, or, what is currently more common, it can mean a dapp building a TX for you and adding some contract UTXO(s) and then sending the signing request to your wallet through WalletConnect.

How do WalletConnect dapps work? You give your address to the dapp, then the dapp does:

  1. Finds the current contract UTXOs
  2. Finds enough UTXOs belonging to your address to fund the interaction
  3. Builds a transaction spending both contract UTXOs and your UTXOs
  4. Gives your wallet the constructed transaction, and then your wallet signs it and broadcasts it

Here’s the thing: if you have just the raw TX, you only know the TXID:n of the supposed contract UTXO. You know your own UTXO(s) details because your wallet will normally have it cached.
With SIGHASH_ALL, you can still produce a signature, without ever learning about exact contents of the supposed dapp’s contract UTXO(s), because your signature covers contents only for your own UTXO and not of other input’s UTXOs, it covers other inputs only by their prevout reference (the TXID:n) so you’d be blindly accepting to interact with those, at this point they’re unknown UTXOs which you accept to pull into your TX.

What if the dapp is a fake dapp by some adversary, trying to trick you into interacting with some other contract? You can’t tell just from the raw TX given to you for signing. You can either trust the dapp, or you can look up other input’s UTXOs to check what exactly are you asked to interact with.

To verify the contents, you must look up the full source TX of the UTXO, and verify the hash matches the input’s prevout hash. This can be up to 1 MB per input.

With SIGHASH_UTXOS you can avoid this, because you can look up just the individual UTXO from some indexer like Fulcrum. You still can’t verify it without downloading the whole TX, BUT, you can skip verification if you’re signing with SIGHASH_UTXOS because you know that the signature will be failed by the network if the data you got was wrong, so you know that either: the data is correct and TX will go through, or if the data was wrong then TX will fail, so you will be saved from interacting under false pretenses.

So, at the edge, if you want to avoid interacting with unknown UTXOs, the difference can be 999968 bytes per input (1 MB of full TX vs 32 bytes for just 1 P2SH BCH UTXO).

1 Like

This alone seems like an useful feature. AND it is being used by some wallets, I understand.

Removing/Deprecating a useful feature that is already deployed does not look like a good idea in general.

What is your take @tom ?

Is the above incorrect (and in what way)?

Meta/Related topic:

You claimed confirmed you are an “argumentative bastard” in our discussions on Telegram.

The profession of being argumentative bastard requires a little thicker skin than the one you currently have.

Instead of ragequitting the moment you are confronted, I would strongly suggest to bear with us and actually explain your arguments.

Honesty about what you actually want will help with that a lot - just a suggestion.

Continued:

This is practically all correct.
The point is, in all cases, that if you want to check that UTXO you’re sigining, you need more information.

There are two ways: either you get the UTXO, or you get the full previous transaction which contains that UTXO.

If you get the full transaction then you can trivially check details to know what you’re sining. This works with SIGHASH_ALL.

If you get only the UTXO you can use the new signing method.

This is the technical choice, which path do you walk?

The downsides of picking sighash-utxo are completely removed if you just get the full transaction from the dapp. It’s a 1kb larger download for an average transaction. It should be a no-brainer to pick that route for any new systems design because it is simpler and safe.

2 Likes

Thanks for the more technical post, I really dislike social interactions, this discussion style is more enjoyable :+1:

So, if I understand this right, SIGHASH_ALL is a generally simplier option, but SIGHASH_UXTO is the more advanced, but also beneficial (less download data) option, correct?

If this is so, then logically I see many reasons Wallet creators would want to either pick one or use both of them, depending on situation and goals.

I would say: Both. Why take away one of the choices if (assuming I understand correctly) there are advantages and disadvantages of both ways?

It’s not that either is strictly superior for all possible cases.

sighash_utxo has no advantages and various disadvantages.

Having an 5% savings on data being sent in the avarage case is not something you realistically should consider an advantage. Load one jpeg or even an icon and you’re downloading more bytes.

The disadvantages are still:

  • anonimity is damaged.
  • dsproof doesn’t work.
  • if wallet connect requires this, all wallets that implement wallet-connect thus need to add this new signing method.
  • for more than 2 parties this signing method can result in actually bigger number of bytes being sent.

And to be clear, nobody actually does the checks for correctness that are talked about here. This signing method is thus, again, not giving any advantages to anyone.

1 Like

The one which requires me to do less API calls and transfer less data:

Which downsides? (rhetorical, below I will go through your list of downsides)
From dapp and user’s PoV there are no downsides, only upsides, which is why current dapps & WC2 wallets are happy users of SIGHASH_UTXOS.

If you insist, you’re free to use SIGHASH_ALL when interacting with dapps.
Even if you disagree about utility of SIGHASH_UTXOS, how is it a problem for you that others are using it?

Compared to what? Any non-p2pkh use already has “damaged anonymity”, so just using one signing method over the other is immaterial here - main usecase here is interaction with dapps, in which case your TX will stand out anyway because it will have some contract’s P2SH inputs alongside your wallet’s inputs.

It can be made to work by extending the protocol, it is just 32 more bytes in dsproof.
But even if this is not done, it is immaterial because SIGHASH_UTXOS TXs will typically have a P2SH input (some dapp’s UTXO), so the TX wouldn’t be covered by DSP anyway, even if signed with SIGHASH_ALL.

It doesn’t, it could transport UTXOs or whole inputs’ parent transactions just the same as part of the signing request package, BCH people are free to implement whatever. WC2 protocol just transfers whatever you need it to transfer.
People who implemented WC2 for BCH made the choice to transport just the UTXOs and use SIGHASH_UTXOS because it is superior choice to SIGHASH_ALL for that use case, because then they need to transport less data through WC2 while still offering protection against interacting with dapps under false pretenses.

Wallets could still fetch the source TXs by themselves and sign with SIGHASH_ALL, but why would they want to do that when a more compact option is available?

Right now the WC2 signing request actually shows input’s contract name and function. I’m not sure (pinging @MathieuG) if this is the dapp passing on CashScript artifacts through WC2 (you’re trusting the dapp) or the wallet itself doing a pattern match on the UTXOs’ locking scripts (you’re trusting the wallet’s whitelist of known contracts).

The above image is an example signing request from Cashonize wallet created by the wrapped.cash dapp. Notice how it shows the contract info and marks UTXOs which belong to the wallet.

You already acknowledged that it is less data to pass around, how is that not an advantage?

But then the dapp is required to either run a full node with full txindex or relying on altruism of someone else to do that. Running a pruned node with full UTXO set seems like a much more convenient way to deploy self-hosted infra for the dapp.

1 Like