-
Notifications
You must be signed in to change notification settings - Fork 159
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
Conversation
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).
There was a problem hiding this 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.
There was a problem hiding this 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>
bd26520
to
bdfe15b
Compare
Co-authored-by: str4d <thestr4d@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this 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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 toscriptPubKey
That never happened. BIP 341 ceased to use scriptCode
as such.
even if we were deploying Taproot, I'd still prefer
scriptCode
overscriptPubKey
.
The established terminology might not be perfect, but mixing it up will not help anyone.
There was a problem hiding this comment.
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.)
There was a problem hiding this comment.
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 fromscriptSig
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 insideCTxOut
s)"
- Refers only to the format of the encoding.
- BIP 341:
- "the SHA256 of all spent outputs'
scriptPubKeys
, serialized as script insideCTxOut
."- "
scriptPubKey
of the previous output spent by this input, serialized as script insideCTxOut
."
- 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
, andpk_script
were all the same in this context. They are all identical for P2PKH (and in fact for every script except P2SH), while for P2SHscriptPubKey
andpk_script
are identical butscriptCode
is not. - We should rename
script_codes_sig_digest
toscriptpubkeys_sig_digest
, to match BIP 341. - [Consensus change]: We change the per-input data to commit to
scriptPubKey
instead ofscriptCode
- For P2PKH this is a no-op.
- For P2SH, this means committing to the Hash160 digest of the
redeemScript
instead of the actualredeemScript
. 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).
- An adversary trying to take a valid signature and use it with a different
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK
NACK until the terminology issue is resolved. |
76433bb
to
30ff9f6
Compare
Force-pushed to remove the incorrect |
Co-authored-by: Kris Nuttycombe <kris@nutty.land>
Pushed the fixes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
re-ACK 13ef7b6
13ef7b6
to
ec79a81
Compare
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.
ec79a81
to
1b30e57
Compare
Signed-off-by: Daira Hopwood <daira@jacaranda.org>
5a4aefb
to
2ae8fc6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK
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.
Closes #574.