-
Notifications
You must be signed in to change notification settings - Fork 58
docs: document the dash analogue of BIP143 #149
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
Open
panleone
wants to merge
10
commits into
dashpay:master
Choose a base branch
from
panleoneDash:master
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
adbf58f
docs: document the sighash algorithm
panleone 6cbb442
docs: add reference to dip0002 and add more info about special transa…
panleone 5d532f2
fix: remove extra backticks
panleone 80dfcef
fix: hashSequence has 32 bytes
panleone 7418304
docs: update activation method
panleone d10be07
docs: simplify implementation section
panleone 0c4e7cb
docs: update test example
panleone da32f62
docs: change author of the dip
panleone 9856abd
Apply review
panleone 0549690
fix: write Signature Hash in lower case
panleone File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,246 @@ | ||
| <pre> | ||
| DIP: tx-sighash-algorithm | ||
| Title: A new transaction signature hash algorithm | ||
| Authors: panleone | ||
| Status: Draft | ||
| Layer: Consensus (hard fork) | ||
| Created: 2024-02-24 | ||
| License: MIT License | ||
| </pre> | ||
|
|
||
| ## Table of Contents | ||
|
|
||
| * [Abstract](#abstract) | ||
| * [Prior Work](#prior-work) | ||
| * [Motivation](#motivation) | ||
| * [Specification](#specification) | ||
| * [Implementation](#implementation) | ||
| * [Deployment](#deployment) | ||
| * [Test](#test) | ||
| * [Copyright](#copyright) | ||
|
|
||
| ## Abstract | ||
|
|
||
| This DIP describes a new algorithm to compute the sighash of a transaction, which is the sequence of bytes that is signed using ECDSA. Compared to the previous algorithm, this one is faster and requires less data hashing. | ||
|
|
||
| ## Prior Work | ||
|
|
||
| * [BIP-0143: Transaction Signature Verification for Version 0 Witness Program](https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki) | ||
| * [DIP-0002: Special Transactions](https://github.com/dashpay/dips/blob/master/dip-0002.md) | ||
| * [DIP-0023: Enhanced Hard Fork Mechanism](https://github.com/dashpay/dips/blob/master/dip-0023.md) | ||
|
|
||
| ## Motivation | ||
|
|
||
| There are 4 ECDSA signature verification codes in the original DASH script system: `CHECKSIG`, `CHECKSIGVERIFY`, `CHECKMULTISIG`, `CHECKMULTISIGVERIFY` (“sigops”). According to the sighash type (`ALL`, `NONE`, `SINGLE`, `ANYONECANPAY`), a transaction digest is generated with a double SHA256 of a serialized subset of the transaction, and the signature is verified against this digest with a given public key. | ||
|
|
||
| Unfortunately, there are at least 2 weaknesses in the original signature hash transaction digest algorithm: | ||
|
|
||
| * For the verification of each signature, the amount of data hashing is proportional to the size of the transaction. Therefore, data hashing grows in O(n<sup>2</sup>) as the number of sigops in a transaction increases. This could be fixed by optimizing the digest algorithm by introducing some reusable “midstate”, so the time complexity becomes O(n). | ||
| * The algorithm does not involve the amount of Dash being spent by the input. This is usually not a problem for online network nodes as they could request the specified transaction to acquire the output value. For an offline transaction signing device (cold wallet), however, not knowing the input amount makes it impossible to calculate the exact amount being spent and the transaction fee. To cope with this problem, a cold wallet must also acquire the full transaction being spent, which could be a big obstacle in the implementation of a lightweight, air-gapped wallet. By including the input value as part of the transaction digest, a cold wallet may safely sign a transaction by learning the value from an untrusted source. In the case that a wrong value is provided and signed, the signature would be invalid and no funding would be lost. See [SIGHASH_WITHINPUTVALUE: Super-lightweight HW wallets and offline data](https://bitcointalk.org/index.php?topic=181734.0). | ||
|
|
||
| ## Specification | ||
|
|
||
| A new sighash type, `SIGHASH_DIP0143`, will be added and the proposed algorithm must be applied only to individual transaction inputs that contain it. The algorithm consists in computing the signature hash of a transaction as the double SHA256 of the serialization of: | ||
|
|
||
| 1. nVersion of the transaction (2-byte integer) | ||
| 2. nType of the transaction (2-byte integer) | ||
| 3. hashPrevouts (32-byte hash) | ||
| 4. hashSequence (32-byte hash) | ||
| 5. outpoint (32-byte hash + 4-byte index) | ||
| 6. scriptCode of the input (serialized as pk_script inside CTxOuts) | ||
| 7. value of the output spent by this input (8-byte int64_t) | ||
| 8. nSequence of the input (8-byte int64_t) | ||
| 9. hashOutputs (32-byte hash) | ||
| 10. vExtraPayload of the transaction (only if transaction is [special](https://github.com/dashpay/dips/blob/master/dip-0002.md#special-transactions)) | ||
| 11. nLockTime of the transaction (4-byte uint32_t) | ||
| 12. sighash type of the signature (4-byte uint32_t) | ||
|
|
||
| Items 1, 2, 8, 11, 12 are usual fields of any transaction. | ||
|
|
||
| Item 5 is the `outpoint` of the input being signed. | ||
|
|
||
| Item 7 is the `value` in dash spent by the input being signed. | ||
|
|
||
| Item 10 is the extra payload of a transaction: | ||
| * It must be included only if the transaction is special | ||
| * A transaction is special if its `nVersion` is greater or equal to `3` and its `nType` is different from `0`, see [DIP-0002](https://github.com/dashpay/dips/blob/master/dip-0002.md) for more details. | ||
|
|
||
| For item 6 let's call `script` the script being executed, so the `scriptPubKey` of the `UTXO` spent by the input being signed. Then the `scriptCode` is computed as: | ||
| * If the `script` does not contain any `OP_CODESEPARATOR`, the `scriptCode` is the `script` serialized as scripts inside `CTxOut`. | ||
| * 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`. | ||
| * Notice that, unlike the previous algorithm, all `OP_CODESEPARATOR` after the last executed one are not removed. | ||
|
|
||
| All other items are computed depending on the `sighash` type: | ||
|
|
||
| Item 3 `hashPrevouts`: | ||
|
|
||
| * If the `ANYONECANPAY` flag is not set, `hashPrevouts` is the double SHA256 of the serialization of all input `outpoints`; | ||
| * Otherwise, `hashPrevouts` is a `uint256` of `0x0000......0000`. | ||
|
|
||
| Item 4 `hashSequence`: | ||
|
|
||
| * If none of the `ANYONECANPAY`, `SINGLE`, `NONE` sighash type is set, `hashSequence` is the double SHA256 of the serialization of `nSequence` of all inputs; | ||
| * Otherwise, `hashSequence` is a `uint256` of `0x0000......0000`. | ||
|
|
||
| Item 9 `hashOutputs`: | ||
|
|
||
| * If the sighash type is neither `SINGLE` nor `NONE`, `hashOutputs` is the double SHA256 of the serialization of all output `values` (8-byte int64_t) paired up with their `scriptPubKey` (serialized as scripts inside `CTxOuts`); | ||
| * 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; | ||
| * Otherwise, `hashOutputs` is a `uint256` of `0x0000......0000`. | ||
|
|
||
| ## Implementation | ||
|
|
||
| The actual implementation of the new algorithm is the following: | ||
|
|
||
| ```cpp | ||
| int32_t n32bitVersion = txTo.nVersion | (txTo.nType << 16); | ||
| uint256 hashPrevouts; | ||
| uint256 hashSequence; | ||
| uint256 hashOutputs; | ||
|
|
||
| if (!(nHashType & SIGHASH_ANYONECANPAY)) { | ||
| hashPrevouts = GetPrevoutsHash(txTo); | ||
| } | ||
|
|
||
| if (!(nHashType & SIGHASH_ANYONECANPAY) && (nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) { | ||
| hashSequence = GetSequencesHash(txTo); | ||
| } | ||
|
|
||
| if ((nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) { | ||
| hashOutputs = GetOutputsHash(txTo); | ||
| } else if ((nHashType & 0x1f) == SIGHASH_SINGLE && nIn < txTo.vout.size()) { | ||
| CHashWriter ss(SER_GETHASH, 0); | ||
| ss << txTo.vout[nIn]; | ||
| hashOutputs = ss.GetHash(); | ||
| } | ||
|
|
||
| CHashWriter ss(SER_GETHASH, 0); | ||
|
|
||
| // Version and type | ||
| ss << n32bitVersion; | ||
| // Input prevouts/nSequence (none/all, depending on flags) | ||
| ss << hashPrevouts; | ||
| ss << hashSequence; | ||
| // The input being signed (replacing the scriptSig with scriptCode + amount) | ||
| // The prevout may already be contained in hashPrevout, and the nSequence | ||
| // may already be contain in hashSequence. | ||
| ss << txTo.vin[nIn].prevout; | ||
| ss << scriptCode; | ||
| ss << amount; | ||
| ss << txTo.vin[nIn].nSequence; | ||
| // Outputs (none/one/all, depending on flags) | ||
| ss << hashOutputs; | ||
| // Extra payload | ||
| if (txTo.IsSpecialTxVersion() && txTo.nType != TRANSACTION_NORMAL) { | ||
| ss << txTo.vExtraPayload; | ||
| } | ||
| // Locktime | ||
| ss << txTo.nLockTime; | ||
| // Sighash type | ||
| ss << nHashType; | ||
|
|
||
| return ss.GetHash(); | ||
| ``` | ||
|
|
||
| Where the three internal functions are defined as: | ||
|
|
||
| ```cpp | ||
| /** Compute the double SHA256 of the concatenation of all prevouts of a tx. */ | ||
| uint256 GetPrevoutsHash(const T& txTo){ | ||
| CHashWriter ss(SER_GETHASH, 0); | ||
| for (const auto& txin : txTo.vin) { | ||
| ss << txin.prevout; | ||
| } | ||
| return ss.GetHash(); | ||
| } | ||
|
|
||
| /** Compute the double SHA256 of the concatenation of all nSequences of a tx. */ | ||
| uint256 GetSequencesHash(const T& txTo){ | ||
| CHashWriter ss(SER_GETHASH, 0); | ||
| for (const auto& txin : txTo.vin) { | ||
| ss << txin.nSequence; | ||
| } | ||
| return ss.GetHash(); | ||
| } | ||
|
|
||
| /** Compute the double SHA256 of the concatenation of all txouts of a tx. */ | ||
| uint256 GetOutputsHash(const T& txTo){ | ||
| CHashWriter ss(SER_GETHASH, 0); | ||
| for (const auto& txout : txTo.vout) { | ||
| ss << txout; | ||
| } | ||
| return ss.GetHash(); | ||
| } | ||
| ``` | ||
|
|
||
| ## Deployment | ||
|
|
||
| This DIP will be deployed using an EHF-based hard fork as described in [DIP-0023](https://github.com/dashpay/dips/blob/master/dip-0023.md). | ||
| In particular a new script flag `SCRIPT_ENABLE_DIP0143` will be added and will be included in script verification only if the corresponding EHF is active. If the flag is not included, transactions with any input that contains `SIGHASH_DIP0143` must be rejected. | ||
|
|
||
| ## Test | ||
|
|
||
| To ensure consistency in consensus-critical behaviour, developers should test their implementations against the test below. | ||
|
|
||
| ```text | ||
| The following is an unsigned transaction: | ||
| 030001000201168d635fbfd7df3a380a30dda5a4cf2d160ad47cc9614ac9f4e827f8876e260000000000ffffffff012da8dc79c177e8deffc3e95308cda74baac13c5bcfbe4010af709dbe0c2bb10000000000ffffffff0100e87648170000001976a9143c6fcdbbbc624fa338ae6479c90a566c95aace2188ac00000000d101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffff010101010001bfdbab50700059afde9b7ec485793912b78c368182e0c153b845bc7c086b8ee9d2d070b163533faa2d51621ea054dc753095cb2753490d6afbc00aae77f80cefca4d9962bfdbab50700059afde9b7ec485793912b78c368100001976a9143c6fcdbbbc624fa338ae6479c90a566c95aace2188acb319b02a1a470620de59efc71832eea117579e4a80de0db2f3fafd1df0710b3e00 | ||
|
|
||
| nVersion: 0300 | ||
| nType: 0100 | ||
| txin: 02 01168d635fbfd7df3a380a30dda5a4cf2d160ad47cc9614ac9f4e827f8876e26 00000000 00 ffffffff | ||
| 012da8dc79c177e8deffc3e95308cda74baac13c5bcfbe4010af709dbe0c2bb1 00000000 00 ffffffff | ||
| txout: 01 00e8764817000000 1976a9143c6fcdbbbc624fa338ae6479c90a566c95aace2188ac | ||
| nLockTime: 00000000 | ||
| vExtraPayload: d101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffff010101010001bfdbab50700059afde9b7ec485793912b78c368182e0c153b845bc7c086b8ee9d2d070b163533faa2d51621ea054dc753095cb2753490d6afbc00aae77f80cefca4d9962bfdbab50700059afde9b7ec485793912b78c368100001976a9143c6fcdbbbc624fa338ae6479c90a566c95aace2188acb319b02a1a470620de59efc71832eea117579e4a80de0db2f3fafd1df0710b3e00 | ||
|
|
||
| Both input comes from an ordinary P2PKH and they have the same scriptPubKey and value: | ||
| scriptPubKey : 210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac | ||
| value: 50000000000 | ||
|
|
||
| Let's sign the first input with a nHashType of 1 + 64 (SIGHASH_ALL | SIGHASH_DIP0143): | ||
|
|
||
| hashPrevouts: | ||
| dSHA256(01168d635fbfd7df3a380a30dda5a4cf2d160ad47cc9614ac9f4e827f8876e2600000000012da8dc79c177e8deffc3e95308cda74baac13c5bcfbe4010af709dbe0c2bb100000000) | ||
| = b319b02a1a470620de59efc71832eea117579e4a80de0db2f3fafd1df0710b3e | ||
|
|
||
| hashSequence: | ||
| dSHA256(ffffffffffffffff) | ||
| = 752adad0a7b9ceca853768aebb6965eca126a62965f698a0c1bc43d83db632ad | ||
|
|
||
| hashOutputs: | ||
| dSHA256(00e87648170000001976a9143c6fcdbbbc624fa338ae6479c90a566c95aace2188ac) | ||
| = 4718f33fc0431a07c1dedd83a209093c217643cc61d7880c5780039f6b0d1fc8 | ||
|
|
||
| hash preimage: 03000100b319b02a1a470620de59efc71832eea117579e4a80de0db2f3fafd1df0710b3e752adad0a7b9ceca853768aebb6965eca126a62965f698a0c1bc43d83db632ad01168d635fbfd7df3a380a30dda5a4cf2d160ad47cc9614ac9f4e827f8876e260000000023210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac00743ba40b000000ffffffff4718f33fc0431a07c1dedd83a209093c217643cc61d7880c5780039f6b0d1fc8d101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffff010101010001bfdbab50700059afde9b7ec485793912b78c368182e0c153b845bc7c086b8ee9d2d070b163533faa2d51621ea054dc753095cb2753490d6afbc00aae77f80cefca4d9962bfdbab50700059afde9b7ec485793912b78c368100001976a9143c6fcdbbbc624fa338ae6479c90a566c95aace2188acb319b02a1a470620de59efc71832eea117579e4a80de0db2f3fafd1df0710b3e000000000041000000 | ||
|
|
||
| nVersion: 0300 | ||
| nType: 0100 | ||
| hashPrevouts: b319b02a1a470620de59efc71832eea117579e4a80de0db2f3fafd1df0710b3e | ||
| hashSequence: 752adad0a7b9ceca853768aebb6965eca126a62965f698a0c1bc43d83db632ad | ||
| outpoint: 01168d635fbfd7df3a380a30dda5a4cf2d160ad47cc9614ac9f4e827f8876e2600000000 | ||
| scriptCode: 23210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac | ||
| amount: 00743ba40b000000 | ||
| nSequence: ffffffff | ||
| hashOutputs: 4718f33fc0431a07c1dedd83a209093c217643cc61d7880c5780039f6b0d1fc8 | ||
| vExtraPayload: d101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffff010101010001bfdbab50700059afde9b7ec485793912b78c368182e0c153b845bc7c086b8ee9d2d070b163533faa2d51621ea054dc753095cb2753490d6afbc00aae77f80cefca4d9962bfdbab50700059afde9b7ec485793912b78c368100001976a9143c6fcdbbbc624fa338ae6479c90a566c95aace2188acb319b02a1a470620de59efc71832eea117579e4a80de0db2f3fafd1df0710b3e00 | ||
| nLockTime: 00000000 | ||
| nHashType: 41000000 | ||
|
|
||
| sigHash: fa269593a2ad3130a07591abff4c973f0ddb0da5651c2e072594586ec1f3811c | ||
| signature: 473044022013d7705c458dea1d1b125d1f8b1c46aa567f8e94eb2e1255d2cc0f774fee8875022054a7f616049ef7e61abf8c9792a49c22412186629674b81f08c6a88a5a14c64b41 | ||
|
|
||
| The serialized signed transaction is: 030001000201168d635fbfd7df3a380a30dda5a4cf2d160ad47cc9614ac9f4e827f8876e260000000048473044022013d7705c458dea1d1b125d1f8b1c46aa567f8e94eb2e1255d2cc0f774fee8875022054a7f616049ef7e61abf8c9792a49c22412186629674b81f08c6a88a5a14c64b41ffffffff012da8dc79c177e8deffc3e95308cda74baac13c5bcfbe4010af709dbe0c2bb10000000000ffffffff0100e87648170000001976a9143c6fcdbbbc624fa338ae6479c90a566c95aace2188ac00000000d101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffff010101010001bfdbab50700059afde9b7ec485793912b78c368182e0c153b845bc7c086b8ee9d2d070b163533faa2d51621ea054dc753095cb2753490d6afbc00aae77f80cefca4d9962bfdbab50700059afde9b7ec485793912b78c368100001976a9143c6fcdbbbc624fa338ae6479c90a566c95aace2188acb319b02a1a470620de59efc71832eea117579e4a80de0db2f3fafd1df0710b3e00 | ||
|
|
||
| nVersion: 0300 | ||
| nType: 0100 | ||
| txin: 02 01168d635fbfd7df3a380a30dda5a4cf2d160ad47cc9614ac9f4e827f8876e26 00000000 48473044022013d7705c458dea1d1b125d1f8b1c46aa567f8e94eb2e1255d2cc0f774fee8875022054a7f616049ef7e61abf8c9792a49c22412186629674b81f08c6a88a5a14c64b41 ffffffff | ||
| 012da8dc79c177e8deffc3e95308cda74baac13c5bcfbe4010af709dbe0c2bb1 00000000 00 ffffffff | ||
| txout: 01 00e8764817000000 1976a9143c6fcdbbbc624fa338ae6479c90a566c95aace2188ac | ||
| vExtraPayload: d101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffff010101010001bfdbab50700059afde9b7ec485793912b78c368182e0c153b845bc7c086b8ee9d2d070b163533faa2d51621ea054dc753095cb2753490d6afbc00aae77f80cefca4d9962bfdbab50700059afde9b7ec485793912b78c368100001976a9143c6fcdbbbc624fa338ae6479c90a566c95aace2188acb319b02a1a470620de59efc71832eea117579e4a80de0db2f3fafd1df0710b3e00 | ||
| nLockTime: 00000000 | ||
| ``` | ||
|
|
||
| ## Copyright | ||
|
|
||
| This document is licensed under the [MIT License](https://opensource.org/licenses/MIT). | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.