|
294 | 294 | # 01000000 00000000 = Apply fees rule |
295 | 295 | SStxRevFractionFlag = 0x4000 |
296 | 296 |
|
| 297 | +# compactSigSize is the size of a compact signature. It consists of a |
| 298 | +# compact signature recovery code byte followed by the R and S components |
| 299 | +# serialized as 32-byte big-endian values. 1+32*2 = 65. |
| 300 | +# for the R and S components. 1+32+32=65. |
| 301 | +compactSigSize = 65 |
| 302 | + |
| 303 | +# compactSigMagicOffset is a value used when creating the compact signature |
| 304 | +# recovery code inherited from Bitcoin and has no meaning, but has been |
| 305 | +# retained for compatibility. For historical purposes, it was originally |
| 306 | +# picked to avoid a binary representation that would allow compact |
| 307 | +# signatures to be mistaken for other components. |
| 308 | +compactSigMagicOffset = 27 |
| 309 | + |
| 310 | +# compactSigCompPubKey is a value used when creating the compact signature |
| 311 | +# recovery code to indicate the original public key was compressed. |
| 312 | +compactSigCompPubKey = 4 |
| 313 | + |
| 314 | +# pubKeyRecoveryCodeOddnessBit specifies the bit that indicates the oddess |
| 315 | +# of the Y coordinate of the random point calculated when creating a |
| 316 | +# signature. |
| 317 | +pubKeyRecoveryCodeOddnessBit = 1 << 0 |
| 318 | + |
| 319 | +# pubKeyRecoveryCodeOverflowBit specifies the bit that indicates the X |
| 320 | +# coordinate of the random point calculated when creating a signature was |
| 321 | +# >= N, where N is the order of the group. |
| 322 | +pubKeyRecoveryCodeOverflowBit = 1 << 1 |
| 323 | + |
| 324 | + |
297 | 325 | # A couple of hashing functions from the crypto module. |
298 | 326 | mac = crypto.mac |
299 | 327 | hashH = crypto.hashH |
@@ -2099,25 +2127,34 @@ def signRFC6979(privateKey, inHash): |
2099 | 2127 | """ |
2100 | 2128 | N = Curve.N |
2101 | 2129 | k = nonceRFC6979(privateKey, inHash, ByteArray(b""), ByteArray(b"")) |
| 2130 | + recoveryCode = 0 |
2102 | 2131 |
|
2103 | 2132 | inv = crypto.modInv(k, N) |
2104 | | - r = Curve.scalarBaseMult(k)[0] % N |
| 2133 | + kG = Curve.scalarBaseMult(k) |
| 2134 | + r = kG[0] % N |
2105 | 2135 |
|
2106 | 2136 | if r == 0: |
2107 | 2137 | raise DecredError("calculated R is zero") |
2108 | 2138 |
|
| 2139 | + if kG[1] & 1: |
| 2140 | + recoveryCode += 1 |
| 2141 | + if kG[0] > N: |
| 2142 | + recoveryCode += 4 |
| 2143 | + |
2109 | 2144 | e = hashToInt(inHash) |
2110 | 2145 | s = privateKey.int() * r |
2111 | 2146 | s += e |
2112 | 2147 | s *= inv |
2113 | 2148 | s = s % N |
2114 | 2149 |
|
2115 | | - if (N >> 1) > 1: |
2116 | | - s = N - s |
2117 | 2150 | if s == 0: |
2118 | 2151 | raise DecredError("calculated S is zero") |
2119 | 2152 |
|
2120 | | - return Signature(r, s) |
| 2153 | + if s > N / 2: |
| 2154 | + s = N - s |
| 2155 | + recoveryCode ^= 1 |
| 2156 | + |
| 2157 | + return Signature(r, s), recoveryCode |
2121 | 2158 |
|
2122 | 2159 |
|
2123 | 2160 | def putVarInt(val): |
@@ -2199,6 +2236,32 @@ def addData(data): |
2199 | 2236 | return b |
2200 | 2237 |
|
2201 | 2238 |
|
| 2239 | +def signCompact(key, inHash, isCompressedKey): |
| 2240 | + """ |
| 2241 | + SignCompact produces a compact signature of the data in hash with the given |
| 2242 | + private key on the secp256k1 curve. The isCompressedKey parameter specifies |
| 2243 | + if the given signature should reference a compressed public key or not. |
| 2244 | +
|
| 2245 | + Compact signature format: |
| 2246 | + <1-byte compact sig recovery code><32-byte R><32-byte S> |
| 2247 | +
|
| 2248 | + The compact sig recovery code is the value 27 + public key recovery code + 4 |
| 2249 | + if the compact signature was created with a compressed public key. |
| 2250 | + """ |
| 2251 | + # Create the signature and associated pubkey recovery code and calculate |
| 2252 | + # the compact signature recovery code. |
| 2253 | + sig, recoveryCode = signRFC6979(key, inHash) |
| 2254 | + compactSigRecoveryCode = compactSigMagicOffset + recoveryCode |
| 2255 | + if isCompressedKey: |
| 2256 | + compactSigRecoveryCode += compactSigCompPubKey |
| 2257 | + |
| 2258 | + # Output <compactSigRecoveryCode><32-byte R><32-byte S>. |
| 2259 | + b = ByteArray(compactSigRecoveryCode) |
| 2260 | + b += ByteArray(sig.r, length=32) |
| 2261 | + b += ByteArray(sig.s, length=32) |
| 2262 | + return b |
| 2263 | + |
| 2264 | + |
2202 | 2265 | def signatureScript(tx, idx, subscript, hashType, privKey, compress): |
2203 | 2266 | """ |
2204 | 2267 | SignatureScript creates an input signature script for tx to spend coins sent |
@@ -2236,8 +2299,8 @@ def rawTxInSignature(tx, idx, subScript, hashType, key): |
2236 | 2299 | versions. |
2237 | 2300 | """ |
2238 | 2301 | sigHash = calcSignatureHash(subScript, hashType, tx, idx, None) |
2239 | | - sig = signRFC6979(key, sigHash).serialize() |
2240 | | - return sig + ByteArray(hashType) |
| 2302 | + sig, _ = signRFC6979(key, sigHash) |
| 2303 | + return sig.serialize() + ByteArray(hashType) |
2241 | 2304 |
|
2242 | 2305 |
|
2243 | 2306 | def calcSignatureHash(script, hashType, tx, idx, cachedPrefix): |
@@ -2717,6 +2780,35 @@ def extractPkScriptAddrs(version, pkScript, netParams): |
2717 | 2780 | return NonStandardTy, [], 0 |
2718 | 2781 |
|
2719 | 2782 |
|
| 2783 | +def addrFromSStxPkScrCommitment(pkScript, netParams): |
| 2784 | + """ |
| 2785 | + AddrFromSStxPkScrCommitment extracts a P2SH or P2PKH address from a ticket |
| 2786 | + commitment pkScript. |
| 2787 | + """ |
| 2788 | + if len(pkScript) < SStxPKHMinOutSize: |
| 2789 | + raise DecredError("short read of sstx commit pkscript") |
| 2790 | + |
| 2791 | + # The MSB of the encoded amount specifies if the output is P2SH. Since |
| 2792 | + # it is encoded with little endian, the MSB is in final byte in the encoded |
| 2793 | + # amount. |
| 2794 | + # |
| 2795 | + # This is a faster equivalent of: |
| 2796 | + # |
| 2797 | + # amtBytes := script[22:30] |
| 2798 | + # amtEncoded := binary.LittleEndian.Uint64(amtBytes) |
| 2799 | + # isP2SH := (amtEncoded & uint64(1<<63)) != 0 |
| 2800 | + isP2SH = pkScript[29] & 0x80 != 0 |
| 2801 | + |
| 2802 | + # The 20 byte PKH or SH. |
| 2803 | + hashBytes = pkScript[2:22] |
| 2804 | + |
| 2805 | + # Return the correct address type. |
| 2806 | + if isP2SH: |
| 2807 | + return crypto.newAddressScriptHashFromHash(hashBytes, netParams) |
| 2808 | + |
| 2809 | + return crypto.newAddressPubKeyHash(hashBytes, netParams, crypto.STEcdsaSecp256k1) |
| 2810 | + |
| 2811 | + |
2720 | 2812 | def sign(chainParams, tx, idx, subScript, hashType, keysource, sigType): |
2721 | 2813 | scriptClass, addresses, nrequired = extractPkScriptAddrs( |
2722 | 2814 | DefaultScriptVersion, subScript, chainParams |
|
0 commit comments