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

[ZIP 244] Changes to transparent component of signature digest #577

Merged
merged 14 commits into from
Jan 13, 2022

Conversation

str4d
Copy link
Collaborator

@str4d str4d commented Jan 4, 2022

Closes #574.

str4d and others added 6 commits January 3, 2022 22:39
Co-authored-by: Daira Hopwood <daira@jacaranda.org>
Co-authored-by: Daira Hopwood <daira@jacaranda.org>
Co-authored-by: Daira Hopwood <daira@jacaranda.org>
This was committed to by the ZIP 143 and ZIP 243 transaction digest
algorithms, but had been accidentally omitted from ZIP 244. It is not a
security issue because the encoding of each layer uses sentinel values,
meaning we were indirectly committing to hash_type (unlike BIP 341, which
conditionally omits commitments based on hash_type and therefore needs to
directly commit to it). But not committing directly to hash_type would
complicate security analysis of the digest, and including it keeps the
transparent part of ZIP 244 closer to BIP 341.

We additionally import two new consensus rules from BIP 341 that apply
to hash_type.

Co-authored-by: Daira Hopwood <daira@jacaranda.org>
Co-authored-by: Kris Nuttycom <nuttycom@electriccoin.co>
This matches the order in which they are committed to in BIP 341 (and
also at the transaction level in S.2).
@krnak
Copy link

krnak commented Jan 4, 2022

Reviewed. I confirm this resolves #574. Good job! @str4d

@nuttycom nuttycom self-requested a review January 5, 2022 15:20
@nuttycom nuttycom added NU5 proposal S-committed Status: Planned work in a sprint labels Jan 5, 2022
zip-0244.rst Outdated Show resolved Hide resolved
zip-0244.rst Outdated Show resolved Hide resolved
zip-0244.rst Outdated Show resolved Hide resolved
Copy link
Contributor

@nuttycom nuttycom left a comment

Choose a reason for hiding this comment

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

One minor request for clarification.

zip-0244.rst Outdated Show resolved Hide resolved
zip-0244.rst Outdated Show resolved Hide resolved
Copy link
Collaborator

@daira daira left a comment

Choose a reason for hiding this comment

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

utACK

Co-authored-by: Kris Nuttycombe <kris.nuttycombe@gmail.com>
zip-0244.rst Outdated Show resolved Hide resolved
Co-authored-by: str4d <thestr4d@gmail.com>
Copy link
Contributor

@dconnolly dconnolly left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Contributor

@nuttycom nuttycom left a comment

Choose a reason for hiding this comment

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

ACK

zip-0244.rst Outdated
S.2d.iv: nSequence (4-byte unsigned little-endian)
S.2g.i: prevout (field encoding)
S.2g.ii: value (8-byte signed little-endian)
S.2g.iii: script_code (field encoding)

Choose a reason for hiding this comment

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

I am wondering whether there is a difference between script_code, pk_script (used in S.2d) and scriptPubKey (used in T.2c). In Bitcoin script_code and scriptPubKey have different meanings, one reason is SegWit BIP141 and the other is due to OP_CODESEPARATOR, but I think neither of these applies in Zcash. If they all have the same meaning, then why not call them all scriptPubKey in this spec? And similarly rename script_codes_sig_digest to scriptpubkeys_sig_digest, so that the naming is more similar to BIP341.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

All three names refer to the same field in CTxOut, and mean exactly the same thing: the transparent Bitcoin Script that the transaction output commits to. I've moved to calling it scriptCode (as Bitcoin did when they added SegWit), considering scriptPubKey to be the legacy inaccurate name (since it rarely if ever directly contained a public key in Bitcoin at the time Zcash forked, and in Zcash is almost exclusively either P2PKH or P2SH, containing only hash commitments). You're right that the ZIP shouldn't have both scriptPubKey and scriptCode; this should be fixed.

I don't know for certain why BIP 341 chose to revert from scriptCode back to scriptPubKey, but I suspect it's because Taproot is explicitly a combination of a public key and the hash of a script, so the field contains a public key directly again:

  • Let q be the 32-byte array containing the witness program (the second push in the scriptPubKey) which represents a public key according to BIP340.

That rationale doesn't apply to Zcash, but even if we were deploying Taproot, I'd still prefer scriptCode over scriptPubKey.

pk_script is only mentioned because we're referencing the Bitcoin Developer Reference for the definition of CTxOut, which uses that name.

Copy link
Contributor

Choose a reason for hiding this comment

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

https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_note-17, which we cite, refers to this as scriptPubKey, but we can refer to it as scriptCode internally and just keep the reference. Also, in looking at this I noticed that there's an off-by-one error in our cite_note links; I'm not sure how this happened but I'll fix it.

Choose a reason for hiding this comment

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

All three names refer to the same field in CTxOut, and mean exactly the same thing: the transparent Bitcoin Script that the transaction output commits to.

In than case, what you want is scriptPubKey.

I've moved to calling it scriptCode (as Bitcoin did when they added SegWit)

In Bitcoin scriptCode and scriptPubKey are two different things and Bitcoin never stopped using the term scriptPubKey, certainly not with SegWit, see BIP-143.

In addition to the two differences that I mentioned in my original comment, there is one more which is easy to illustrate. Consider a P2SH which consists of a 2-of-3 multisig. The scriptPubKey and scriptCode will look like this:
scriptPubKey: A9 14 <20-byte-scripthash> 87
scriptCode: 52 21 <pubkey1> 21 <pubkey2> 21 <pubkey3> 53 AE

I don't know for certain why BIP 341 chose to revert from scriptCode back to scriptPubKey

That never happened. BIP 341 ceased to use scriptCode as such.

even if we were deploying Taproot, I'd still prefer scriptCode over scriptPubKey.

The established terminology might not be perfect, but mixing it up will not help anyone.

Copy link
Collaborator

@daira daira Jan 10, 2022

Choose a reason for hiding this comment

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

I believe @andrewkozlik is correct, and I'm inclined to revert those parts of 76433bb. We need to check whether there are other incorrect usages.

(The above doesn't depend on this, but I also believe that the origin of "scriptPubKey" is that scripts are analogous to public keys, not that they ever were directly public keys.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We've just had a meeting to figure out what was going on here, where my understanding went wrong, and where the above comments were correct or not.


BIP 143 defined scriptCode as being one of two things:

  • For P2WPKH, the equivalent of a scriptPubKey for P2PKH (the opcodes are AFAICT identical).
  • For P2WSH, the equivalent of a redeemScript extracted from scriptSig for P2SH (if you look at the non-OP_CODESEPARATOR branch).

and did not alter non-SegWit sighashes (i.e. P2PKH or P2SH).

BIP 341 defines two sets of script-related components:

  • At a transaction level, the scriptPubKey field for every input is digested (sha_scriptpubkeys).
  • At an input level, the scriptPubKey field of that input.

The wording Bitcoin chose to use in their BIPs was incidentally very confusing, using "serialized as" (metaphor) instead of "serialized like" (simile) or the more precise "seralized using the encoding of":

  • BIP 143: "scriptCode of the input (serialized as scripts inside CTxOuts)"
    • Refers only to the format of the encoding.
  • BIP 341:
    • "the SHA256 of all spent outputs' scriptPubKeys, serialized as script inside CTxOut."
    • "scriptPubKey of the previous output spent by this input, serialized as script inside CTxOut."
      • Refers to the format of the encoding, but is also valid to interpret as the actual encoded field values in the outputs.

In ZIP 143 (and maintained in ZIP 243), we mapped the SegWit P2WPKH and P2WSH semantics to P2PKH and P2SH, and defined scriptCode as:

  • redeemScript if P2SH.
  • scriptPubKey in all other cases.

This commits to the same data as if it just used scriptPubKey in all cases, because the way Bitcoin implemented P2SH, the consensus code only reaches that point if the scriptPubKey has a very specific format in which everything about it ends up being checked prior to using redeemScript. I believe at the time we kept the SegWit formulation because we wanted to stick close to the SegWit semantics, but in hindsight it would have been simpler to just use scriptPubKey everywhere.

Meanwhile, in this PR to ZIP 244 we are attempting to map BIP 341's Taproot semantics to P2PKH and P2SH. If we were to follow them closely, we would need to use scriptPubKey in the digests at both the transaction level (for all inputs) and the input level (per-input). However, in this PR (and in ZIP 244 prior to this PR), at the input level we continue to use scriptCode (i.e. for P2SH we use redeemScript instead of scriptPubKey in its digest).


So, conclusions:

  • I was partially incorrect to say that scriptPubKey, scriptCode, and pk_script were all the same in this context. They are all identical for P2PKH (and in fact for every script except P2SH), while for P2SH scriptPubKey and pk_script are identical but scriptCode is not.
  • We should rename script_codes_sig_digest to scriptpubkeys_sig_digest, to match BIP 341.
  • [Consensus change]: We change the per-input data to commit to scriptPubKey instead of scriptCode
    • For P2PKH this is a no-op.
    • For P2SH, this means committing to the Hash160 digest of the redeemScript instead of the actual redeemScript. We discussed this and believe it is fine, because:
      • An adversary trying to take a valid signature and use it with a different redeemScript would need to perform a preimage attack on the Hash160 digest, which doesn't have a square-root optimization anywhere.
      • Even if they could find a preimage, all that allows them to do is alter the input's scriptSig field; this doesn't alter the effecting data of the transaction, which by definition means the transaction has the same effect under consensus (spends the same inputs and produces the same outputs).

Copy link
Contributor

@nuttycom nuttycom left a comment

Choose a reason for hiding this comment

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

ACK

@daira
Copy link
Collaborator

daira commented Jan 10, 2022

NACK until the terminology issue is resolved.

@str4d str4d force-pushed the 574-changes-to-zip-244-transparent branch from 76433bb to 30ff9f6 Compare January 12, 2022 15:51
@str4d
Copy link
Collaborator Author

str4d commented Jan 12, 2022

Force-pushed to remove the incorrect scriptPubKey -> scriptCode rename. I'll fix the merge conflict and then make the correct fix.

@str4d
Copy link
Collaborator Author

str4d commented Jan 12, 2022

Pushed the fixes.

Copy link
Contributor

@nuttycom nuttycom left a comment

Choose a reason for hiding this comment

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

re-ACK 13ef7b6

@str4d str4d force-pushed the 574-changes-to-zip-244-transparent branch from 13ef7b6 to ec79a81 Compare January 12, 2022 21:57
This is a no-op for every scriptPubKey format except P2SH, where we now
commit to the digest of the redeemScript instead instead of redeemScript
directly.
@str4d str4d force-pushed the 574-changes-to-zip-244-transparent branch from ec79a81 to 1b30e57 Compare January 12, 2022 22:08
zip-0244.rst Outdated Show resolved Hide resolved
zip-0244.rst Outdated Show resolved Hide resolved
Signed-off-by: Daira Hopwood <daira@jacaranda.org>
@daira daira force-pushed the 574-changes-to-zip-244-transparent branch from 5a4aefb to 2ae8fc6 Compare January 13, 2022 14:30
Copy link
Collaborator

@daira daira left a comment

Choose a reason for hiding this comment

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

ACK

@daira daira merged commit ac9dd97 into zcash:main Jan 13, 2022
str4d added a commit to str4d/zips that referenced this pull request Jan 24, 2022
In zcash#577 we altered ZIP 244 to have shielded signatures commit
to the same data as transparent inputs, in transactions that contain
transparent components. However, the edge case of shielded coinbase was
not correctly handled; they contain both a consensus-required "dummy"
transparent input, and binding signatures which would be required to
commit to a `CTxOut` that does not exist.

We resolve this by partially reverting one of the zcash#577 changes,
by having S.2 for coinbase transactions be identical to T.2. This reverts
binding signatures in coinbase transactions to effectively signing the
transaction ID.

At the same time, we also revert the same change for transactions with no
transparent inputs but some transparent outputs; these also now revert to
using the transaction ID for all shielded signatures (like fully-shielded
transactions). The hardware wallet edge case does not apply here, as all
input values are shielded and therefore directly committed to.
@str4d str4d deleted the 574-changes-to-zip-244-transparent branch January 27, 2022 03:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
NU5 proposal S-committed Status: Planned work in a sprint
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[ZIP 244] preserves a bug from BIP-143, forcing hardware wallets to verify all transparent prevouts
6 participants