|
| 1 | +--- |
| 2 | +layout: specification |
| 3 | +title: BUIP-HF Digest for replay protected signature verification across hard forks |
| 4 | +category: spec |
| 5 | +date: 2017-07-16 |
| 6 | +activation: 1501590000 |
| 7 | +version: 1.2 |
| 8 | +--- |
| 9 | + |
| 10 | +## Abstract |
| 11 | + |
| 12 | +This document describes proposed requirements and design for a reusable signing mechanism ensuring replay protection in the event of a chain split. It provides a way for users to create transactions which are invalid on forks lacking support for the mechanism and a fork-specific ID. |
| 13 | + |
| 14 | +The proposed digest algorithm is adapted from BIP143[[1]](#bip143) as it minimizes redundant data hashing in verification, covers the input value by the signature and is already implemented in a wide variety of applications[[2]](#bip143Motivation). |
| 15 | + |
| 16 | +The proposed digest algorithm is used when the `SIGHASH_FORKID` bit is set in the signature's sighash type. The verification of signatures which do not set this bit is not affected. |
| 17 | + |
| 18 | +## Specification |
| 19 | + |
| 20 | +### Activation |
| 21 | + |
| 22 | +The proposed digest algorithm is only used when the `SIGHASH_FORKID` bit in the signature sighash's type is set. It is defined as follows: |
| 23 | + |
| 24 | +````cpp |
| 25 | + // ... |
| 26 | + SIGHASH_SINGLE = 3, |
| 27 | + SIGHASH_FORKID = 0x40, |
| 28 | + SIGHASH_ANYONECANPAY = 0x80, |
| 29 | + // ... |
| 30 | +```` |
| 31 | + |
| 32 | +In presence of the `SIGHASH_FORKID` flag in the signature's sighash type, the proposed algorithm is used. |
| 33 | + |
| 34 | +Signatures using the `SIGHASH_FORKID` digest method must be rejected before [UAHF](https://github.com/bitcoincashorg/bitcoincash.org/blob/master/spec/uahf-technical-spec.md) is activated. |
| 35 | + |
| 36 | +In order to ensure proper activation, the reference implementation uses the `SCRIPT_ENABLE_SIGHASH_FORKID` flag when executing `EvalScript` . |
| 37 | + |
| 38 | +### Digest algorithm |
| 39 | + |
| 40 | +The proposed digest algorithm computes the double SHA256 of the serialization of: |
| 41 | +1. nVersion of the transaction (4-byte little endian) |
| 42 | +2. hashPrevouts (32-byte hash) |
| 43 | +3. hashSequence (32-byte hash) |
| 44 | +4. outpoint (32-byte hash + 4-byte little endian) |
| 45 | +5. scriptCode of the input (serialized as scripts inside CTxOuts) |
| 46 | +6. value of the output spent by this input (8-byte little endian) |
| 47 | +7. nSequence of the input (4-byte little endian) |
| 48 | +8. hashOutputs (32-byte hash) |
| 49 | +9. nLocktime of the transaction (4-byte little endian) |
| 50 | +10. sighash type of the signature (4-byte little endian) |
| 51 | + |
| 52 | +Items 1, 4, 7 and 9 have the same meaning as in the original algorithm[[3]](#OP_CHECKSIG). |
| 53 | + |
| 54 | +#### hashPrevouts |
| 55 | + |
| 56 | +* If the `ANYONECANPAY` flag is not set, `hashPrevouts` is the double SHA256 of the serialization of all input outpoints; |
| 57 | +* Otherwise, `hashPrevouts` is a `uint256` of `0x0000......0000`. |
| 58 | + |
| 59 | +#### hashSequence |
| 60 | + |
| 61 | +* If none of the `ANYONECANPAY`, `SINGLE`, `NONE` sighash type is set, `hashSequence` is the double SHA256 of the serialization of `nSequence` of all inputs; |
| 62 | +* Otherwise, `hashSequence` is a `uint256` of `0x0000......0000`. |
| 63 | + |
| 64 | +#### scriptCode |
| 65 | + |
| 66 | +In this section, we call `script` the script being currently executed. This means `redeemScript` in case of P2SH, or the `scriptPubKey` in the general case. |
| 67 | + |
| 68 | +* If the `script` does not contain any `OP_CODESEPARATOR`, the `scriptCode` is the `script` serialized as scripts inside `CTxOut`. |
| 69 | +* If the `script` contains any `OP_CODESEPARATOR`, the `scriptCode` is the `script` but removing everything up to and including the last executed `OP_CODESEPARATOR` before the signature checking opcode being executed, serialized as scripts inside `CTxOut`. |
| 70 | + |
| 71 | +Notes: |
| 72 | +1. Contrary to the original algorithm, this one does not use `FindAndDelete` to remove the signature from the script. |
| 73 | +2. Because of 1, it is not possible to create a valid signature within `redeemScript` or `scriptPubkey` as the signature would be part of the digest. This enforces that the signature is in `sigScript` . |
| 74 | +3. In case an opcode that requires signature checking is present in `sigScript`, `script` is effectively `sigScript`. However, for reason similar to 2, it is not possible to provide a valid signature in that case. |
| 75 | + |
| 76 | +#### value |
| 77 | + |
| 78 | +The 8-byte value of the amount of Bitcoin this input contains. |
| 79 | + |
| 80 | +#### hashOutputs |
| 81 | + |
| 82 | +* If the sighash type is neither `SINGLE` nor `NONE`, `hashOutputs` is the double SHA256 of the serialization of all output amounts (8-byte little endian) paired up with their `scriptPubKey` (serialized as scripts inside CTxOuts); |
| 83 | +* If sighash type is `SINGLE` and the input index is smaller than the number of outputs, `hashOutputs` is the double SHA256 of the output amount with `scriptPubKey` of the same index as the input; |
| 84 | +* Otherwise, `hashOutputs` is a `uint256` of `0x0000......0000`. |
| 85 | + |
| 86 | +Notes: |
| 87 | +1. In the original algorithm[[3]](#OP_CHECKSIG), a `uint256` of `0x0000......0001` is committed if the input index for a `SINGLE` signature is greater than or equal to the number of outputs. In this BIP a `0x0000......0000` is committed, without changing the semantics. |
| 88 | + |
| 89 | +#### sighash type |
| 90 | + |
| 91 | +The sighash type is altered to include a 24-bit *fork id* in its most significant bits. |
| 92 | + |
| 93 | +````cpp |
| 94 | + ss << ((GetForkID() << 8) | nHashType); |
| 95 | +```` |
| 96 | + |
| 97 | +This ensure that the proposed digest algorithm will generate different results on forks using different *fork ids*. |
| 98 | + |
| 99 | +## Implementation |
| 100 | + |
| 101 | +Addition to `SignatureHash` : |
| 102 | + |
| 103 | +````cpp |
| 104 | + if (nHashType & SIGHASH_FORKID) { |
| 105 | + uint256 hashPrevouts; |
| 106 | + uint256 hashSequence; |
| 107 | + uint256 hashOutputs; |
| 108 | + |
| 109 | + if (!(nHashType & SIGHASH_ANYONECANPAY)) { |
| 110 | + hashPrevouts = GetPrevoutHash(txTo); |
| 111 | + } |
| 112 | + |
| 113 | + if (!(nHashType & SIGHASH_ANYONECANPAY) && |
| 114 | + (nHashType & 0x1f) != SIGHASH_SINGLE && |
| 115 | + (nHashType & 0x1f) != SIGHASH_NONE) { |
| 116 | + hashSequence = GetSequenceHash(txTo); |
| 117 | + } |
| 118 | + |
| 119 | + if ((nHashType & 0x1f) != SIGHASH_SINGLE && |
| 120 | + (nHashType & 0x1f) != SIGHASH_NONE) { |
| 121 | + hashOutputs = GetOutputsHash(txTo); |
| 122 | + } else if ((nHashType & 0x1f) == SIGHASH_SINGLE && |
| 123 | + nIn < txTo.vout.size()) { |
| 124 | + CHashWriter ss(SER_GETHASH, 0); |
| 125 | + ss << txTo.vout[nIn]; |
| 126 | + hashOutputs = ss.GetHash(); |
| 127 | + } |
| 128 | + |
| 129 | + CHashWriter ss(SER_GETHASH, 0); |
| 130 | + // Version |
| 131 | + ss << txTo.nVersion; |
| 132 | + // Input prevouts/nSequence (none/all, depending on flags) |
| 133 | + ss << hashPrevouts; |
| 134 | + ss << hashSequence; |
| 135 | + // The input being signed (replacing the scriptSig with scriptCode + |
| 136 | + // amount). The prevout may already be contained in hashPrevout, and the |
| 137 | + // nSequence may already be contain in hashSequence. |
| 138 | + ss << txTo.vin[nIn].prevout; |
| 139 | + ss << static_cast<const CScriptBase &>(scriptCode); |
| 140 | + ss << amount; |
| 141 | + ss << txTo.vin[nIn].nSequence; |
| 142 | + // Outputs (none/one/all, depending on flags) |
| 143 | + ss << hashOutputs; |
| 144 | + // Locktime |
| 145 | + ss << txTo.nLockTime; |
| 146 | + // Sighash type |
| 147 | + ss << ((GetForkId() << 8) | nHashType); |
| 148 | + return ss.GetHash(); |
| 149 | + } |
| 150 | +```` |
| 151 | +
|
| 152 | +Computation of midstates: |
| 153 | +
|
| 154 | +````cpp |
| 155 | +uint256 GetPrevoutHash(const CTransaction &txTo) { |
| 156 | + CHashWriter ss(SER_GETHASH, 0); |
| 157 | + for (unsigned int n = 0; n < txTo.vin.size(); n++) { |
| 158 | + ss << txTo.vin[n].prevout; |
| 159 | + } |
| 160 | +
|
| 161 | + return ss.GetHash(); |
| 162 | +} |
| 163 | +
|
| 164 | +uint256 GetSequenceHash(const CTransaction &txTo) { |
| 165 | + CHashWriter ss(SER_GETHASH, 0); |
| 166 | + for (unsigned int n = 0; n < txTo.vin.size(); n++) { |
| 167 | + ss << txTo.vin[n].nSequence; |
| 168 | + } |
| 169 | +
|
| 170 | + return ss.GetHash(); |
| 171 | +} |
| 172 | +
|
| 173 | +uint256 GetOutputsHash(const CTransaction &txTo) { |
| 174 | + CHashWriter ss(SER_GETHASH, 0); |
| 175 | + for (unsigned int n = 0; n < txTo.vout.size(); n++) { |
| 176 | + ss << txTo.vout[n]; |
| 177 | + } |
| 178 | +
|
| 179 | + return ss.GetHash(); |
| 180 | +} |
| 181 | +```` |
| 182 | + |
| 183 | +Gating code: |
| 184 | + |
| 185 | +````cpp |
| 186 | + uint32_t nHashType = GetHashType(vchSig); |
| 187 | + if (nHashType & SIGHASH_FORKID) { |
| 188 | + if (!(flags & SCRIPT_ENABLE_SIGHASH_FORKID)) |
| 189 | + return set_error(serror, SCRIPT_ERR_ILLEGAL_FORKID); |
| 190 | + } else { |
| 191 | + // Drop the signature in scripts when SIGHASH_FORKID is not used. |
| 192 | + scriptCode.FindAndDelete(CScript(vchSig)); |
| 193 | + } |
| 194 | +```` |
| 195 | +
|
| 196 | +## Note |
| 197 | +
|
| 198 | +In the UAHF, a `fork id` of 0 is used (see [[4]](#uahfspec) REQ-6-2 NOTE 4), i.e. |
| 199 | +the GetForkID() function returns zero. |
| 200 | +In that case the code can be simplified to omit the function. |
| 201 | +
|
| 202 | +## References |
| 203 | +
|
| 204 | +<a name="bip143">[1]</a> https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki |
| 205 | +
|
| 206 | +<a name="bip143Motivation">[2]</a> https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki#Motivation |
| 207 | +
|
| 208 | +<a name="OP_CHECKSIG">[3]</a> https://en.bitcoin.it/wiki/OP_CHECKSIG |
| 209 | +
|
| 210 | +<a name="uahfspec">[4]</a> https://github.com/bitcoincashorg/bitcoincash.org/blob/master/spec/uahf-technical-spec.md |
0 commit comments