-
Notifications
You must be signed in to change notification settings - Fork 58
Confidential Transactions #161
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
base: master
Are you sure you want to change the base?
Conversation
|
@UdjinM6 @PastaPastaPasta I believe recent updates to the DIP have addressed all your review comments, so I am requesting another review |
|
also, it was not mentioned in review but since this DIP changes Consensus rules I changed the DIP Type to Consensus |
✅ Actions performedReview triggered.
|
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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.
Actionable comments posted: 2
🧹 Nitpick comments (1)
dip-0032.md (1)
100-106: Fix wording & duplicated verb.“This is possible is because curve operations are commutative”
Small typo and redundant wording; suggest:
-This is possible is because curve operations are commutative, +This is possible because curve operations are commutative,
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
README.md(1 hunks)dip-0032.md(1 hunks)
🧰 Additional context used
🪛 LanguageTool
dip-0032.md
[style] ~77-~77: ‘many different ways’ might be wordy. Consider a shorter alternative.
Context: ...d mathematical tool that can be used in many different ways with many different cryptographic syste...
(EN_WORDINESS_PREMIUM_MANY_DIFFERENT_WAYS)
[style] ~102-~102: This phrase is redundant. Consider writing “same”.
Context: ...g key (ConfidentialNonce) to derive the same exact seed used by Alice. This is possible is...
(SAME_EXACT)
[style] ~285-~285: ‘with respect to’ might be wordy. Consider a shorter alternative.
Context: ... ``` and taking the discrete logarithm with respect to G of both sides we get `Log Q = Log (...
(EN_WORDINESS_PREMIUM_WITH_RESPECT_TO)
[style] ~287-~287: ‘with respect to’ might be wordy. Consider a shorter alternative.
Context: ...is called the Discrete Logarithm of Q with respect to G (or the Discrete Logarithm of Q i...
(EN_WORDINESS_PREMIUM_WITH_RESPECT_TO)
🪛 markdownlint-cli2 (0.17.2)
dip-0032.md
221-221: Heading levels should only increment by one level at a time
Expected: h2; Actual: h4
(MD001, heading-increment)
🔇 Additional comments (2)
README.md (1)
48-49: DIP entry looks correct – no action required.The new row is properly ordered, the link path matches the filename, and the column values follow the existing conventions.
dip-0032.md (1)
6-8: Status label is inconsistent with the README table.The front-matter lists
Status: In Progress
whereas the README row added in the same PR marks the DIP as Proposed. Choose one term and use it consistently in both places (the project usually uses “Proposed” for drafts awaiting review).
| # TxInput | ||
|
|
||
| | Field | Required | Size | Data Type | Encoding | Notes | | ||
| | ----- | -------- | ---- | --------- | -------- | ----- | | ||
| | txid | Yes | 32 bytes | `hex` | See NOTE below | | | ||
| | outIndex | Yes | 4 bytes | `uint32_t` | Little-endian | **Input is a coinbase**: `0xffffffff` | | ||
| | scriptSig Length | Yes | Varies | `VarInt` | | | | ||
| | ScriptSig | Yes | Varies | `hex` | | | | ||
| | Sequence | Yes | 4 bytes | `uint32_t` | Little-endian | | | ||
|
|
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.
🛠️ Refactor suggestion
Heading level jumps break markdown structure.
# TxInput (H1) appears inside a section that is already at H3/H4 depth, which violates MD001 and renders poorly in many markdown renderers. Demote it (and the following # TxOutput) two levels, e.g.:
-# TxInput
+#### TxInputDo the same for TxOutput.
This also resolves the markdown-lint warning reported in CI.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| # TxInput | |
| | Field | Required | Size | Data Type | Encoding | Notes | | |
| | ----- | -------- | ---- | --------- | -------- | ----- | | |
| | txid | Yes | 32 bytes | `hex` | See NOTE below | | | |
| | outIndex | Yes | 4 bytes | `uint32_t` | Little-endian | **Input is a coinbase**: `0xffffffff` | | |
| | scriptSig Length | Yes | Varies | `VarInt` | | | | |
| | ScriptSig | Yes | Varies | `hex` | | | | |
| | Sequence | Yes | 4 bytes | `uint32_t` | Little-endian | | | |
| #### TxInput | |
| | Field | Required | Size | Data Type | Encoding | Notes | | |
| | ----------------- | -------- | ---------- | ----------- | ------------ | --------------------------------------- | | |
| | txid | Yes | 32 bytes | `hex` | See NOTE below | | | |
| | outIndex | Yes | 4 bytes | `uint32_t` | Little-endian | **Input is a coinbase**: `0xffffffff` | | |
| | scriptSig Length | Yes | Varies | `VarInt` | | | | |
| | ScriptSig | Yes | Varies | `hex` | | | | |
| | Sequence | Yes | 4 bytes | `uint32_t` | Little-endian | | |
🤖 Prompt for AI Agents
In dip-0032.md around lines 199 to 208, the heading `# TxInput` is incorrectly
using H1 level inside a deeper section, breaking markdown structure and causing
lint warnings. Change the heading level from H1 to H3 (e.g., from `# TxInput` to
`### TxInput`) to properly nest it within the existing section hierarchy. Apply
the same adjustment to the `TxOutput` heading to maintain consistency and fix
the markdown-lint issues.
Co-authored-by: thephez <thephez@users.noreply.github.com>
Co-authored-by: thephez <thephez@users.noreply.github.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.
Actionable comments posted: 2
♻️ Duplicate comments (2)
dip-0032.md (2)
193-196: Fix misleadingVector<TxInput>/Vector<TxOutput>sizes.Stating
33 * num…implies each serialized entry has a fixed length, which is false (e.g.,TxInputcarries a VarInt-prefixed scriptSig). This spec will mislead implementers. Please change those size cells tovaries(or spell out the full size formula).Apply:
-| txInputs| `Vector<TxInput>`| 33*numTxInputs bytes| Confidential Transaction inputs | -| txOutputs | `Vector<TxOutput>`| 33*numTxOutputs bytes| Confidential Transaction outputs | +| txInputs| `Vector<TxInput>`| varies | Confidential Transaction inputs | +| txOutputs | `Vector<TxOutput>`| varies | Confidential Transaction outputs |
199-218: DemoteTxInput/TxOutputheadings to match section depth.These sit inside an H4 subsection (
extra_payload), so using#breaks the heading hierarchy and markdownlint (MD001). Please switch both to####(or deeper) so the structure increments correctly.-# TxInput +#### TxInput … -# TxOutput +#### TxOutput
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
dip-0032.md(1 hunks)
🧰 Additional context used
🪛 markdownlint-cli2 (0.18.1)
dip-0032.md
228-228: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4
(MD001, heading-increment)
242-242: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4
(MD001, heading-increment)
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.
Actionable comments posted: 3
♻️ Duplicate comments (3)
dip-0032.md (3)
194-196: Vector sizes aren’t fixed at 33 × n.Each
TxInput/TxOutputelement is variable-length (scripts, proofs), so advertising33*numTx…is wrong and will mislead implementers. Please note the size as “varies” (or spell out the formula).
199-212: Fix heading levels forTxInput/TxOutput.These sections appear as H1s inside a deeper hierarchy, breaking Markdown lint (MD001) and rendering poorly. Demote them to the appropriate depth (e.g.,
###).
228-234: PromoteConfidentialNonceheading.
## ConfidentialAmountis followed by a level‑4#### ConfidentialNonce, skipping a level and triggering MD001. Change it to### ConfidentialNonce(and adjust child headings accordingly).
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
dip-0032.md(1 hunks)
🧰 Additional context used
🪛 markdownlint-cli2 (0.18.1)
dip-0032.md
228-228: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4
(MD001, heading-increment)
242-242: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4
(MD001, heading-increment)
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.
Actionable comments posted: 2
♻️ Duplicate comments (1)
dip-0032.md (1)
199-214: Fix heading levels forTxInput/TxOutput.
These sections still use#inside a deeper hierarchy, tripping MD001 and breaking the outline. Please demote them (e.g.,### TxInput/### TxOutput) to restore proper structure.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
dip-0032.md(1 hunks)
🧰 Additional context used
🪛 markdownlint-cli2 (0.18.1)
dip-0032.md
228-228: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4
(MD001, heading-increment)
242-242: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4
(MD001, heading-increment)
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.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
dip-0032.md(1 hunks)
🧰 Additional context used
🪛 markdownlint-cli2 (0.18.1)
dip-0032.md
228-228: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4
(MD001, heading-increment)
242-242: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4
(MD001, heading-increment)
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.
Actionable comments posted: 1
♻️ Duplicate comments (5)
dip-0032.md (5)
199-212: DemoteTxInput/TxOutputheadings to maintain hierarchy.Using top-level
#here breaks the markdown outline (MD001) and renders poorly. Please drop these to match the surrounding section depth (e.g.,###).
228-235: PromoteConfidentialNonceheading one level.
## ConfidentialAmountfollowed by#### ConfidentialNonceskips a level and triggers MD001. Change it to### ConfidentialNonce(and adjust any children accordingly).
242-244: PromotePedersen Commitmentsheading to keep increments by one.
####here jumps two levels under## ConfidentialProof. Please raise it to### Pedersen Commitmentsso the markdown hierarchy remains consistent.
154-158: Reconcile Bulletproof bit limits.Line 156 mandates N = 52 (0–21 M range) while Line 308 caps proofs at 40 bits (~11 k DASH). Both can’t hold simultaneously, leaving consensus rules ambiguous. Pick one range (e.g., keep 52 bits and drop/adjust the 40-bit cap) and update both sections so implementers know exactly which proofs to accept.
Also applies to: 307-308
305-307: Finish the block-header requirement.The bullet stops at “The new block header”, so validators have no instruction about what header data is required when CTs appear. Either spell out the exact field/value or remove the bullet entirely; as written the consensus rule is unusable.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
dip-0032.md(1 hunks)
🧰 Additional context used
🪛 markdownlint-cli2 (0.18.1)
dip-0032.md
228-228: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4
(MD001, heading-increment)
242-242: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4
(MD001, heading-increment)
| * bkvb - Blinding key version byte | ||
| * atvb - Address type version byte | ||
| * salt (AKA public blinding key) - 32 byte random value | ||
| * When Alice sends a transaction to Bob, this public blinding key is used in an ECDH operation by Alice to transmit the actual amount and blinding key of a transaction to Bob | ||
| * Note that older Elements documentation called this a "scanning key" | ||
| * Note that this value is not hashed; it is stored unchanged as part of the CT address | ||
| * pk - The compressed public key, a 33 byte secp256k1 curve point. | ||
| * Note that a hash of the public key is what is actually included in the address | ||
|
|
||
| Every confidential address contains a public blinding key used in ECDH. When Alice sends Bob a transaction, Alice chooses an ECDH ephemeral private key (the ConfidentialNonce in this DIP) and combines it with the public blinding key to derive a blinding seed. | ||
|
|
||
| When Bob receives a CT, they combine the public blinding key with the private blinding key (ConfidentialNonce) to derive the same exact seed used by Alice. | ||
| This is possible because curve operations are commutative, i.e., `A*B=B*A` , also known as Abelian. To show this is true, let Alice's keypair be denoted `(d_A, Q_A)` and Bob's keypair `(d_B, Q_B)` where `d_A` and `d_B` are private keys and `Q_A` and `Q_B` are public keys. The public keys are computed as `Q_A=d_A * G` and `Q_B=d_B * G` where G is a generator of the curve and the operation `x*G` means adding `G` to itself `x` times. | ||
|
|
||
| To show that ECDH allows Alice and Bob to derive the same seed, we must show that the point on the curve `d_A*Q_B` is the same as `d_B*Q_A` : | ||
|
|
||
| ```text | ||
| d_A*Q_B = d_A * d_B * G # definition of Q_B | ||
| = d_B * d_A * G # commutativity | ||
| = d_B * Q_A | ||
| ``` | ||
|
|
||
| Therefore, Alice and Bob can compute a shared secret `(x,y) = d_A*Q_B = d_B*Q_A` without ever actually transmitting the secret. | ||
|
|
||
| For HD wallets, a master 32 byte salt (blinding key) is stored from which all blinding keys for generated addresses are derived. A | ||
| blinding key for an address is generated as `HMAC_SHA256(master blinding key, <address ScriptPubKey>)` . See the reference SLIP-0077 for more details. | ||
|
|
||
| To clarify, the public key can actually be stored in 32 bytes and one bit because a point on the curve is a pair of 32 byte numbers (x,y). If x is known, y is almost uniquely identified since for each `x`, there are two `y` values. This happens for the same reason that `x^2 = 4` has two solutions, +2 and -2. Since secp256k1 is symmetric about the x-axis, one bit can be used to indicate if the y value is above or below the x-axis. | ||
|
|
||
| The salt is 32 bytes because it is used to "blind" the x value of a 32 byte (x,y) point on the curve. The compressed public key is 33 bytes (or 32 bytes and one bit) as described above and in the "Compressed Public Keys" section of Chapter 4 of "Mastering Bitcoin". | ||
|
|
||
| A bech32m encoded CT address can then be generated via: | ||
|
|
||
| ```text | ||
| address = bech32m( bkvb + atvb + salt + RIPEMD160( SHA256( pk ) ) ) | ||
| ``` | ||
|
|
||
| where `+` denotes concatenation. Since the salt is 32 bytes and the output of RIPEMD160 is 20 bytes, the above bech32m encoded address encodes 54 bytes of data. This is described in more detail in the section "Legacy Addresses for P2PKH" of Chapter 4 of "Mastering Bitcoin". This assumes Confidential UTXOs will be stored in P2PKH format. | ||
|
|
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.
Clarify CT address payload size—33 B pubkey vs 20 B hash mismatch.
The list claims the address payload includes a 33‑byte compressed pubkey, but the later encoding formula and 54‑byte total only fit a 20‑byte HASH160. Implementers cannot tell which layout is normative, so different nodes would encode incompatible addresses. Please resolve whether the full 33‑byte key or its 20‑byte hash is included and update both the bullet list and encoding description accordingly.
🤖 Prompt for AI Agents
In dip-0032.md around lines 91–129, the text inconsistently lists a 33‑byte
compressed pubkey in the address payload but the encoding formula uses
RIPEMD160(SHA256(pk)) (20 bytes), causing ambiguity; update the spec to be
normative that the CT address contains the 20‑byte HASH160 of the compressed
public key (and not the full 33‑byte pubkey), change the bullet for "pk" to say
"20 byte HASH160 of the compressed public key" while still documenting that the
compressed public key itself is 33 bytes off‑chain/elsewhere, and adjust any
phrasing about payload size and the bech32m encoding so the listed field sizes
and the 54‑byte total are consistent.
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.
Actionable comments posted: 3
♻️ Duplicate comments (2)
dip-0032.md (2)
199-218: Fix heading hierarchy forTxInput/TxOutput.The
#headings reset the document to H1 inside a nested section and trip markdownlint MD001. Drop them two levels (e.g., to###) so the structure increments by one level at a time.-# TxInput +# ### TxInput … -# TxOutput +# ### TxOutput
228-244: Align subheadings under## ConfidentialProof.
#### ConfidentialNonceand#### Pedersen Commitmentsjump two levels, violating MD001. Promote them to H3 to keep the hierarchy consistent.-#### ConfidentialNonce +### ConfidentialNonce … -#### Pedersen Commitments +### Pedersen Commitments
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
dip-0032.md(1 hunks)
🧰 Additional context used
🪛 markdownlint-cli2 (0.18.1)
dip-0032.md
228-228: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4
(MD001, heading-increment)
242-242: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4
(MD001, heading-increment)
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.
Actionable comments posted: 3
♻️ Duplicate comments (1)
dip-0032.md (1)
199-244: Restore proper heading hierarchy.Headings jump levels (
# TxInput,# TxOutput,#### ConfidentialNonce,#### Pedersen Commitments) breaching MD001 and harming structure. Demote/promote to increment by one level (e.g.,### TxInput,### TxOutput,### ConfidentialNonce,### Pedersen Commitments).
Based on static analysis
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
dip-0032.md(1 hunks)
🧰 Additional context used
🪛 markdownlint-cli2 (0.18.1)
dip-0032.md
228-228: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4
(MD001, heading-increment)
242-242: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4
(MD001, heading-increment)
| * A 33 byte Pedersen commitment to the amount being transferred for each output | ||
| * A BP rangeproof for each output that ensures the amount transferred is within a certain interval between 0 and 2^N - 1 | ||
| * To support all potential value transfers between 0 and 21M, the BP rangeproof N value must equal 52 | ||
| * The exact size of the proof depends on the number of inputs and outputs | ||
| * A fee output with an explicit amount and blank ScriptPubKey | ||
| * A list of input UTXOs | ||
| * A list of one or more outputs | ||
| * These may be normal or Confidential outputs | ||
|
|
||
| This DIP proposes using DIP-2 Special Transactions to store and implement Confidential Transactions. This means storing data in the `extra_payload` field of existing Dash transactions and Special Transaction `type` of 10, the currently next unused value of this field. If a transaction contains any non-Confidential inputs or outputs then that data is stored in normal (non-Special) transaction data. The following describes how the confidential inputs and outputs of a transaction can be stored via `extra_payload`. | ||
|
|
||
| #### Variable Length Integer (VarInt) | ||
|
|
||
| This data type allows an integer to be encoded with a variable length (which depends on the represented value) to save space. | ||
| Variable length integers always precede a vector of a data type that may vary in length and are used to indicate this length. | ||
| Longer numbers are encoded in little-endian. | ||
|
|
||
| | Value | Size | Format | Example | | ||
| | ----- | ---- | ------ | ------- | | ||
| | < `0xFD` | 1 byte | `uint8_t` | `0x0F` = 15 | | ||
| | <= `0xFFFF` | 3 bytes | `0xFD` followed by the number as a `uint16_t` | `0xFD 00FF` = 65 280 | | ||
| | <= `0xFFFF FFFF` | 5 bytes | `0xFE` followed by the number as a `uint32_t` | `0xFE 0000 00FF` = 4 278 190 080 | | ||
| | <= `0xFFFF FFFF FFFF FFFF` | 9 bytes | `0xFF` followed by the number as a `uint64_t` | `0xFF 0000 0000 0000 00FF` = 18 374 686 479 671 623 680 | | ||
|
|
||
| #### Vector\<Type\> | ||
|
|
||
| Each `Vector` begins with a `VarInt` describing the number of items it contains. | ||
|
|
||
| If the vector is of type `hex`, then the size / structure of each individual item is not known in advance. In this case, each item begins with a `VarInt` describing its size `s` in bytes, followed by `s` bytes which should be interpreted as the item itself. | ||
| Otherwise, size prefixes are omitted, and each item should be interpreted in accordance with the vector's type. | ||
|
|
||
| In other words, the vector is serialized as follows: `[Length (n)][Item #1][Item #2][...][Item #n]`. | ||
|
|
||
| #### extra_payload | ||
|
|
||
| The structure of `extra_payload` is : | ||
|
|
||
| | Field | Type | Size | Description | | ||
| | ----- | ---- | ---- | ----------- | | ||
| |numTxInputs| VarInt | varies | Number of confidential inputs| | ||
| | txInputs| `Vector<TxInput>`| varies | Confidential Transaction inputs | | ||
| |numTxOutputs| VarInt | varies | Number of confidential outputs | | ||
| | txOutputs | `Vector<TxOutput>`| varies | Confidential Transaction outputs | | ||
| | proof |ConfidentialProof| Varies | Confidential proof data | | ||
|
|
||
| # TxInput | ||
|
|
||
| | Field | Required | Size | Data Type | Encoding | Notes | | ||
| | ----- | -------- | ---- | --------- | -------- | ----- | | ||
| | txid | Yes | 32 bytes | `hex` | See NOTE below | | | ||
| | outIndex | Yes | 4 bytes | `uint32_t` | Little-endian | **Input is a coinbase**: `0xffffffff` | | ||
| | scriptSig Length | Yes | Varies | `VarInt` | | | | ||
| | ScriptSig | Yes | Varies | `hex` | | | | ||
| | Sequence | Yes | 4 bytes | `uint32_t` | Little-endian | | | ||
|
|
||
| NOTE: The hex encodings of hashes are byte-reversed, and so the bytes will need to be re-reversed to match the serialized data. This is the same situation as in Bitcoin. For example, the hash `1123...deff` would be displayed by the Bitcoin and Dash clients as `ffde...2311`. This is primarily for historical reasons: the Bitcoin and Dash clients have always interpreted and displayed hashes as little-endian integers and parsed their bytes in reverse order. | ||
|
|
||
| # TxOutput | ||
|
|
||
| | Field | Type | Size | Description | | ||
| | ----- | ---- | ---- | ----------- | | ||
| | amount|ConfidentialAmount| 33 bytes| The confidential amount being transferred | | ||
| | nonce |ConfidentialNonce| 33 bytes| The confidential nonce | | ||
| | proof |ConfidentialProof| Varies | The rangeproof that the confidential amount being transferred is valid | | ||
|
|
||
| The following data structures have been adapted from the Elements Project Transaction format: | ||
|
|
||
| ## ConfidentialAmount | ||
|
|
||
| | Field | Required | Size | Data Type | Encoding | Notes | | ||
| | ----- | -------- | ---- | --------- | -------- | ----- | | ||
| | Header | Yes | 1 byte | | | A header byte of `0x08` or `0x09` indicates a blinded value encoded as a compressed elliptic curve point. With the least significant bit of the header byte denoting the least significant bit of the y-coordinate, and the remaining 32 bytes denoting the x-coordinate (big-endian). The point must be a point on the secp256k1 curve. | | ||
| | Value | | 32 bytes | `hex` | Big-endian | | | ||
|
|
||
| #### ConfidentialNonce | ||
|
|
||
| | Field | Required | Size | Data Type | Encoding | Notes | | ||
| | ----- | -------- | ---- | --------- | -------- | ----- | | ||
| | Header | Yes | 1 byte | | | A header byte of `0x02` or `0x03` indicates a compressed elliptic curve point. The least significant bit of the header byte denotes the least significant bit of the y-coordinate, and the remaining 32 bytes denote the x-coordinate (big-endian). This point is not required to be on the curve. | | ||
| | Value | | 32 bytes | `hex` | Big-endian | | | ||
|
|
||
| ## ConfidentialProof | ||
|
|
||
| | Field | Required | Size | Data Type | Encoding | Notes | | ||
| | ----- | -------- | ---- | --------- | -------- | ----- | | ||
| | Length | Yes | Varies | `VarInt` | | | | ||
| | Value | | Varies | `hex` | Big-endian | Bulletproof which proves that the ConfidentialAmount is within the range of 0 and `2^52 - 1` | | ||
|
|
||
| #### Pedersen Commitments | ||
|
|
||
| A Pedersen commitment can be thought of as a sealed box which is tamper-proof and contains a secret. A physical example would be for Alice to seal a message `M` inside an envelope along with a piece of carbon paper, then get Bob to sign the outside of the envelope so that the carbon paper copies his signature onto the message `M`. Later, Alice can open the envelope and both Alice and Bob can be assured that the secret message `M` was not changed. | ||
|
|
||
| Mathematically, a Pedersen commitment (written in additive notation) is defined as: | ||
|
|
||
| ```text | ||
| P(v,s) = v*G + s*Q | ||
| ``` | ||
|
|
||
| where | ||
|
|
||
| * P(v,s) means P is a function of the variables v and s | ||
| * v is a value to be committed | ||
| * s is a salt AKA blinding factor | ||
| * G and Q are elliptic curve points on secp256k1 both known to committer and verifier | ||
| * The committer is the creator of a transaction | ||
| * The verifier is any node which processes the transaction to see if it is valid | ||
| * x*Y means multiplication of value x by curve point Y | ||
| * In some references multiplication is implicit, i.e. x*Y = xY | ||
|
|
||
| In multiplicative notation, the above would be `P(v,s) = G^v*Q^s` where `^` denotes exponentiation. Both notations describe the same equivalent mathematics where additive notation is using `+` as the group operator while multiplicative notation is using `*`. In additive notation, `n*G` is equivalent to `G^n` in multiplicative notation. | ||
|
|
||
| Capital letters are curve points (P, G, Q, Y above), while lowercase letters are arbitrary integers (v, s and x above). Some references, including the original paper by Pedersen, use multiplicative notation. Additive notation is usually used with Abelian Groups, i.e. those where `A + B = B + A` or `A*B=B*A`. Given two points on the elliptic curve secp256k1 `G` and `Q`, we can add them in any order, which is to say `G + Q = Q + G`. | ||
|
|
||
| G and Q MUST be randomly chosen curve points such that the `d` in | ||
|
|
||
| ```text | ||
| Q = d*G | ||
| ``` | ||
|
|
||
| is unknown, which is equivalent to | ||
|
|
||
| ```text | ||
| d = Log Q | ||
| ``` | ||
|
|
||
| is unknown. In multiplicative notation: | ||
|
|
||
| ```text | ||
| Q = G^d | ||
| ``` | ||
|
|
||
| and taking the discrete logarithm with respect to `G` of both sides we get `Log Q = Log (G^d) = d*Log(G) = d` where we have used the facts that `Log(a^b) = b*Log(a)` and `Log(G) = 1` in base `G`. | ||
|
|
||
| `d` is called the Discrete Logarithm of `Q` with respect to `G` (or the Discrete Logarithm of `Q` in base `G`). We use the notation `Log` instead of `log` to denote the fact that a discrete logarithm is a different function from the traditional logarithm denoted `log` . The function `Log` and `log` share the same types of properties and identities which is why `Log` is considered the discrete analog of `log`. | ||
|
|
||
| The Discrete Logarithm Problem (DLP) means that if `Q = d*G` (or `Q = G^d` in multiplicative notation), then while `d` does exist, it is computationally infeasible to calculate it. The security of Pedersen commitments is based on the hardness assumption that the DLP on appropriately chosen elliptic curves have no efficient algorithm to find a solution. | ||
|
|
||
| ### New consensus rules for CTs | ||
|
|
||
| These new consensus rules will be activated according to BIP9/DIP23. Assuming that the network signals support for this hardfork and the Masternode Hard Fork Signalling Transaction (as defined in DIP23) is mined, these consensus rules will activate at an `activation_height` designated `heightCT` in this DIP. If the block height of the full node is less than `heightCT`, then the following consensus rules will not be active. When the block reaches `heightCT`, these rules will become active. | ||
|
|
||
| #### Consensus rules for transactions | ||
|
|
||
| * If height is at least `heightCT` then the Coinbase Special Transaction must have version >= 4, which contains the new field `confidentialAddressBalance` which is a uint64_t (8 bytes) | ||
| * `confidentialAddressBalance` must be a valid unsigned 64 bit integer (which stores values in duffs not whole coin amounts) | ||
| * `confidentialAddressBalance` must equal the value of `confidentialAddressBalance` from the previous block plus the sum of confidential inputs minus the sum of confidential outputs, else invalid | ||
| * `confidentialAddressBalance` must be greater than or equal to 0, else invalid | ||
| * `confidentialAddressBalance` must be less than or equal to 1892000000000000, else invalid | ||
| * If height is at least `heightCT` and if at least one input of a CT is confidential, at least one of the outputs must also be confidential. This prevents metadata leaking about the exact amount in a confidential output. A confidential output may have an amount equal to zero. | ||
| * If height is at least `heightCT` and if all inputs are public, i.e. not confidential, the number of confidential outputs must be zero or greater than or equal to two, i.e. having all public inputs with a single confidential output is not allowed, as it leaks the metadata about exactly how much value is in the confidential output. | ||
| * If height is less than `heightCT` then Special Transaction type 10 is invalid | ||
| * If a block contains at least one CT then: | ||
| * The new block header | ||
| * If a block contains a CT then the associated bulletproof must have a size of at most 40 bits | ||
| * This enforces a maximum limit on the amount of a confidential UTXO to be `2^40/10^8 = 10995.11627776 DASH` |
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.
Fix the Bulletproof range bound contradiction.
Here we require N = 52 (full 0–21 M range) yet the consensus rule below caps proofs at 40 bits (≈10 995 DASH). Both cannot be enforced simultaneously. Please choose the normative bound and update every reference (text, tables, and consensus rule) so the document is self-consistent.
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
228-228: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4
(MD001, heading-increment)
242-242: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4
(MD001, heading-increment)
🤖 Prompt for AI Agents
In dip-0032.md around lines 154–308 there is a contradiction: the TxOutput/BP
rangeproof section sets N = 52 but the consensus rules later cap bulletproofs at
40 bits; pick the single normative range (either N=52 or N=40) and make the
document consistent by updating all occurrences — the BP rangeproof N value in
the TxOutput and header paragraphs, any explanatory text or table rows that
state the valid range (0..2^N-1), the ConfidentialProof/amount size notes, and
the consensus rule that currently enforces a 40‑bit limit (including any
converted DASH/duff examples); ensure numeric examples (like max DASH values)
and any related sizes/limits reflect the chosen N everywhere.
dip-0032.md
Outdated
| * If a block contains at least one CT then: | ||
| * The new block header | ||
| * If a block contains a CT then the associated bulletproof must have a size of at most 40 bits |
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.
Complete the block-header consensus rule.
The bullet stops at “The new block header …” without stating the actual requirement, leaving consensus behavior undefined. Either specify the exact condition or remove the bullet.
🤖 Prompt for AI Agents
In dip-0032.md around lines 305–307, the bullet "The new block header …" is
incomplete; replace it with an explicit consensus rule stating that when a block
contains at least one CT the block header MUST include a dedicated CT presence
flag and a CT commitment field (e.g., CT root/hash) that commits to all CTs in
the block, and that nodes MUST validate that the CTs match that commitment; keep
the existing rule that if a block contains a CT its associated bulletproof must
be at most 40 bits or remove the incomplete bullet entirely if you prefer not to
add the header requirement.
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.
Actionable comments posted: 4
♻️ Duplicate comments (5)
dip-0032.md (5)
199-208: Heading-level lint note was previously raised.This repeats an earlier comment about heading jumps around TxInput/TxOutput. Apply the demotion to resolve MD001.
228-234: Heading-level lint note was previously raised.Same as earlier feedback for ConfidentialNonce and Pedersen Commitments; align to avoid MD001.
Also applies to: 242-244
305-307: Incomplete consensus bullet (“The new block header …”).Specify the requirement or delete. As written, consensus behavior is undefined.
-* If a block contains at least one CT then: - * The new block header +* If a block contains at least one CT then: [SPECIFY header commitment/flag and validation procedure], or remove this bullet until defined.
97-100: Resolve CT address payload mismatch (33B pk vs 20B hash).Spec lists a 33‑byte compressed pubkey but later encodes HASH160(pk) (20 bytes) and claims a 54‑byte payload. Pick one normative layout. If HASH160 is intended, rename field and size accordingly.
-* pk - The compressed public key, a 33 byte secp256k1 curve point. - * Note that a hash of the public key is what is actually included in the address +* pkHash - 20 byte HASH160 of the compressed public key (RIPEMD160(SHA256(pk))) + * The compressed public key itself is 33 bytes; only its 20‑byte HASH160 is encoded in the address
123-129: Make encoding formula and note consistent; avoid implying P2PKH UTXOs.Align formula with pkHash and drop the misleading “stored in P2PKH format” phrasing.
-address = bech32m( bkvb + atvb + salt + RIPEMD160( SHA256( pk ) ) ) +address = bech32m( bkvb + atvb + salt + pkHash )-where `+` denotes concatenation. Since the salt is 32 bytes and the output of RIPEMD160 is 20 bytes, the above bech32m encoded address encodes 54 bytes of data. This is described in more detail in the section "Legacy Addresses for P2PKH" of Chapter 4 of "Mastering Bitcoin". This assumes Confidential UTXOs will be stored in P2PKH format. +where `+` denotes concatenation. Since the salt is 32 bytes and `pkHash` is 20 bytes, the bech32m-encoded address carries 54 bytes of data. This mirrors HASH160(pk) usage described in the “Legacy Addresses for P2PKH” section of Mastering Bitcoin; it does not imply CT UTXOs are P2PKH.
🧹 Nitpick comments (4)
dip-0032.md (4)
199-208: Demote “TxInput/TxOutput” headings to nest under “extra_payload”.Fix heading jumps (MD001). Under “#### extra_payload”, use one deeper level.
-# TxInput +##### TxInput ... -# TxOutput +##### TxOutputAlso applies to: 211-218
228-234: Promote “ConfidentialNonce” heading to avoid level jump.From H2 (ConfidentialAmount) you jump to H4. Use H3.
-#### ConfidentialNonce +### ConfidentialNonce
242-242: Promote “Pedersen Commitments” heading one level.Avoid skipping levels after “##/### ConfidentialProof”.
-#### Pedersen Commitments +### Pedersen Commitments
339-340: Fix Bulletproofs reference link.The zkp “rangeproof” module is Borromean. Link to the Bulletproofs module.
-* libsecp256k1 Bulletproofs: <https://github.com/BlockstreamResearch/secp256k1-zkp/tree/master/src/modules/rangeproof> +* libsecp256k1 Bulletproofs: <https://github.com/BlockstreamResearch/secp256k1-zkp/tree/master/src/modules/bulletproofs>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
dip-0032.md(1 hunks)
🧰 Additional context used
🪛 markdownlint-cli2 (0.18.1)
dip-0032.md
228-228: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4
(MD001, heading-increment)
242-242: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4
(MD001, heading-increment)
| * If height is at least `heightCT` then the Coinbase Special Transaction must have version >= 4, which contains the new field `confidentialAddressBalance` which is a uint64_t (8 bytes) | ||
| * `confidentialAddressBalance` must be a valid unsigned 64 bit integer (which stores values in duffs not whole coin amounts) | ||
| * `confidentialAddressBalance` must equal the value of `confidentialAddressBalance` from the previous block plus the sum of confidential inputs minus the sum of confidential outputs, else invalid | ||
| * `confidentialAddressBalance` must be greater than or equal to 0, else invalid | ||
| * `confidentialAddressBalance` must be less than or equal to 1892000000000000, else invalid | ||
| * If height is at least `heightCT` and if at least one input of a CT is confidential, at least one of the outputs must also be confidential. This prevents metadata leaking about the exact amount in a confidential output. A confidential output may have an amount equal to zero. | ||
| * If height is at least `heightCT` and if all inputs are public, i.e. not confidential, the number of confidential outputs must be zero or greater than or equal to two, i.e. having all public inputs with a single confidential output is not allowed, as it leaks the metadata about exactly how much value is in the confidential output. |
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.
Consensus rule relies on summing hidden amounts—define verifiable computation or remove.
Nodes cannot compute “sum of confidential inputs/outputs” numerically from commitments. Either:
- Redefine the rule to a verifiable commitment (e.g., per‑block CT balance commitment with a precise construction and validation procedure), or
- Remove the “confidentialAddressBalance” field and rely on standard CT balance checks (sum of input/output commitments equals fee commitment).
Would you like a proposed commitment scheme text for a per‑block accumulator?
🤖 Prompt for AI Agents
In dip-0032.md around lines 297–303, the consensus rule incorrectly requires
nodes to numerically sum confidential inputs/outputs (which they cannot verify);
fix by either (A) replacing the plain uint64 confidentialAddressBalance field
with a precise verifiable per-block CT balance commitment: specify the
commitment construction (e.g., a Pedersen-style commitment or Merkle-accumulator
over per-tx CT balance commitments), detail the exact serialized field, how it
is computed from transactions in the block, and the verification procedure nodes
must run to validate the commitment against transaction commitments, or (B)
remove the confidentialAddressBalance field entirely and update the rules to
rely on existing CT logic (require that sum of input/output commitments and fee
commitment balance per transaction/block verify using existing
rangeproof/commitment checks); update the text to include the chosen option,
clear algorithmic steps for producers and verifiers, and adjust validation
pseudocode and consensus rule language accordingly.
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.
Actionable comments posted: 5
♻️ Duplicate comments (3)
dip-0032.md (3)
199-217: DemoteTxInput/TxOutputheadings to maintain hierarchy.These sections appear as H1s inside a deeper subsection, tripping MD001 and rendering poorly. Promote them to match the surrounding depth (e.g.,
### TxInput,### TxOutput).
228-234: Fix heading jump forConfidentialNonce.
#### ConfidentialNonceskips a level after## ConfidentialAmount. Bump it to###(and adjust nested headings if any) to satisfy MD001.
242-244: Promote “Pedersen Commitments” heading.
#### Pedersen Commitmentsalso jumps a level; use###to keep the heading hierarchy consistent.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
dip-0032.md(1 hunks)
🧰 Additional context used
🪛 markdownlint-cli2 (0.18.1)
dip-0032.md
228-228: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4
(MD001, heading-increment)
242-242: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4
(MD001, heading-increment)
| This DIP documents how Dash can add CTs using Bulletproofs (BPs). The type of CT we propose using has two main parts: a signature and a Bulletproof. The Bulletproof (a type of range proof) proves that the signature is valid, in particular, that it's values are within a certain valid range. This prevents an underflow or overflow of values that could be used to subvert the system. Bulletproofs are a type of Non-Interactive Zero Knowledge proof. They prove that the values are valid or invalid without leaking any information about the values themselves. | ||
|
|
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.
Correct “its values” grammar.
Use the possessive “its” here (“its values”) to fix the typo.
🤖 Prompt for AI Agents
In dip-0032.md around lines 81 to 82, fix the grammar by changing “it's values”
to the possessive form “its values” so the sentence reads that the Bulletproof
proves that the signature is valid, in particular, that its values are within a
certain valid range.
|
@VirgileBa thanks for your review comments. My replies are inline below.
Agreed on both points. All privacy tech relies on more than a single person using it, DIP32 is no different. Your second point about masternodes is good to mention and I added that in a recent commit.
removed the "i.e." which wasn't needed here
From the Abstract of the BP paper: "they enable proving that a committed value is in a range using only
I don't have a strong preference on this and the current prefix of
I don't have a strong preference on what this is called. It is not specific to bulletproofs, the original BP paper calls things like this "blinding factors" and collections of them "blinding vectors".
Abelian operators form Abelian groups and these operations are commonly referred to as Abelian operations. Linking to the wiki page is a good idea, that has been done in a recent commit.
It is attempting to explain why the salt is 32 bytes vs some other size such as 4 or 16 bytes.
Agreed, removed the "derived from Bitcoin" in a recent commit.
Suggestions for improvement are welcome. The goal is some kind of intuitive explanation of what "commitment" means to developers since the word is ambiguous to everyone outside of certain mathematical circles.
Suggestions for improvement are welcome.
Added in a recent commit.
This requirement comes from the original Pedersen paper and you are correct that currently this DIP does not specify how to randomly choose them. That would make a good addition. The motivation to mention this is to make sure implementers don't do something like
I agree that context makes it clear but in the name of clarity and being able to tell the difference with a glance, I will keep it as is.
Specific suggestions for a change are welcome.
Yes, looks like a remnant from previous reviews. It has been removed.
Various Dash community members had questions about how this DIP would be affected by QCs so that is why that section exists.
Yes, the validity check "sum of all commitments in a transaction must be 0 for a tx to be valid" should be mentioned, probably in the list of consensus rule changes.
Additional review from you as the DIP solidifies is welcome. |
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.
Actionable comments posted: 4
♻️ Duplicate comments (3)
dip-0032.md (3)
199-212: Demote theTxInput/TxOutputheadingsThese sections sit inside a deeper hierarchy; using
#creates a heading jump and reintroduces the markdownlint MD001 warning noted earlier. Demote both headings (e.g.,### TxInput,### TxOutput) to keep the structure consistent.-# TxInput +### TxInput ... -# TxOutput +### TxOutput
228-228: Fix heading level jumps for Confidential sub-sections
#### ConfidentialNonceand#### Pedersen Commitmentsskip a level after## ConfidentialAmount, still triggering MD001. Promote both to###so heading depth only increases by one at a time.-#### ConfidentialNonce +### ConfidentialNonce ... -#### Pedersen Commitments +### Pedersen CommitmentsAlso applies to: 242-242
81-81: Fix typo in Bulletproof sentenceUse the possessive “its values” instead of “it's values” here.
-... proves that the signature is valid, in particular, that it's values ... +... proves that the signature is valid, in particular, that its values ...
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
dip-0032.md(1 hunks)
🧰 Additional context used
🪛 markdownlint-cli2 (0.18.1)
dip-0032.md
228-228: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4
(MD001, heading-increment)
242-242: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4
(MD001, heading-increment)
| * pk - The compressed public key, a 33 byte secp256k1 curve point. | ||
| * Note that a hash of the public key is what is actually included in the address | ||
|
|
||
| Every confidential address contains a public blinding key used in ECDH. When Alice sends Bob a transaction, Alice chooses an ECDH ephemeral private key (the ConfidentialNonce in this DIP) and combines it with the public blinding key to derive a blinding seed. | ||
|
|
||
| When Bob receives a CT, they combine the public blinding key with the private blinding key (ConfidentialNonce) to derive the same exact seed used by Alice. | ||
| This is possible because curve operations are commutative, i.e., `A*B=B*A` , also known as Abelian. To show this is true, let Alice's keypair be denoted `(d_A, Q_A)` and Bob's keypair `(d_B, Q_B)` where `d_A` and `d_B` are private keys and `Q_A` and `Q_B` are public keys. The public keys are computed as `Q_A=d_A * G` and `Q_B=d_B * G` where G is a generator of the curve and the operation `x*G` means adding `G` to itself `x` times. | ||
|
|
||
| To show that ECDH allows Alice and Bob to derive the same seed, we must show that the point on the curve `d_A*Q_B` is the same as `d_B*Q_A` : | ||
|
|
||
| ```text | ||
| d_A*Q_B = d_A * d_B * G # definition of Q_B | ||
| = d_B * d_A * G # commutativity | ||
| = d_B * Q_A | ||
| ``` | ||
|
|
||
| Therefore, Alice and Bob can compute a shared secret `(x,y) = d_A*Q_B = d_B*Q_A` without ever actually transmitting the secret. | ||
|
|
||
| For HD wallets, a master 32 byte salt (blinding key) is stored from which all blinding keys for generated addresses are derived. A | ||
| blinding key for an address is generated as `HMAC_SHA256(master blinding key, <address ScriptPubKey>)` . See the reference SLIP-0077 for more details. | ||
|
|
||
| To clarify, the public key can actually be stored in 32 bytes and one bit because a point on the curve is a pair of 32 byte numbers (x,y). If x is known, y is almost uniquely identified since for each `x`, there are two `y` values. This happens for the same reason that `x^2 = 4` has two solutions, +2 and -2. Since secp256k1 is symmetric about the x-axis, one bit can be used to indicate if the y value is above or below the x-axis. | ||
|
|
||
| The salt is 32 bytes because it is used to "blind" the x value of a 32 byte (x,y) point on the curve. The compressed public key is 33 bytes (or 32 bytes and one bit) as described above and in the "Compressed Public Keys" section of Chapter 4 of "Mastering Bitcoin". | ||
|
|
||
| A bech32m encoded CT address can then be generated via: | ||
|
|
||
| ```text | ||
| address = bech32m( bkvb + atvb + salt + RIPEMD160( SHA256( pk ) ) ) | ||
| ``` | ||
|
|
||
| where `+` denotes concatenation. Since the salt is 32 bytes and the output of RIPEMD160 is 20 bytes, the above bech32m encoded address encodes 54 bytes of data. This is described in more detail in the section "Legacy Addresses for P2PKH" of Chapter 4 of "Mastering Bitcoin". This assumes Confidential UTXOs will be stored in P2PKH format. |
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.
Clarify whether the address carries the full pubkey or the HASH160
This section is inconsistent: the bullet list says the payload stores the 33 B compressed public key, but the encoding formula uses RIPEMD160(SHA256(pk)), which is only 20 B. Different readers will implement incompatible address encodings. Please pick the canonical layout and make the whole subsection consistent (field name/size, total payload length, encoding formula, explanatory text, and examples). For example, if the HASH160 is intended, rename the field (e.g., pkHash), state it is 20 B, and remove claims that the full 33 B key is serialized.
🤖 Prompt for AI Agents
In dip-0032.md around lines 97-128, the subsection is inconsistent about whether
the address payload contains the full 33-byte compressed public key or the
20-byte HASH160: the bullet claims a 33 B pk while the address formula uses
RIPEMD160(SHA256(pk)) (20 B) and the total payload length is computed for 54 B.
Decide on one canonical layout (either store the full compressed public key (33
B) or store its HASH160 (20 B)), then make all fields, sizes and descriptions
consistent: update the bullet field name (e.g., pk or pkHash), update its
declared size, adjust the explanatory text about compression and salt usage,
recalc and update the total payload length and the bech32m encoding formula
accordingly, and update/remove any examples or references (e.g., “Compressed
Public Keys” and “Legacy Addresses for P2PKH”) so they match the chosen
representation.
This allows for smaller QR codes of addresses because case-insensitive encoding can be used
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.
Actionable comments posted: 3
♻️ Duplicate comments (1)
dip-0032.md (1)
81-81: Fix grammar: use possessive "its" not "it's".Line 81 uses the contraction "it's" where the possessive "its" is required: "that the signature is valid, in particular, that it's values are within a certain valid range."
-in particular, that it's values are within a certain valid range. +in particular, that its values are within a certain valid range.
🧹 Nitpick comments (3)
dip-0032.md (3)
97-98: Clarify address payload to avoid implementation ambiguity.Line 97 states "pk - The compressed public key, a 33 byte secp256k1 curve point," but line 98 then notes "a hash of the public key is what is actually included in the address." While the encoding formula (line 125) and size calculation (line 128) are consistent with storing the 20-byte HASH160, the field description is misleading. Readers might mistakenly think the full 33-byte key is in the address payload.
Clarify by renaming and rewording the field to make it explicit that only the hash is carried in the address:
-* pk - The compressed public key, a 33 byte secp256k1 curve point. - * Note that a hash of the public key is what is actually included in the address +* pkHash - The 20-byte HASH160 (RIPEMD160(SHA256(...))) of the compressed public key. + * Note: The full 33-byte compressed public key is derived off-chain from context; only its hash is stored in the address.
97-97: Add missing hyphens in compound adjectives.Lines 97, 120, and 122 use compound adjectives without hyphens (e.g., "33 byte", "32 byte", "bech32m encoded"). Standard English style requires hyphens when adjectives compound before a noun.
-* pk - The compressed public key, a 33 byte secp256k1 curve point. +* pk - The compressed public key, a 33-byte secp256k1 curve point. -The salt is 32 bytes because it is used to "blind" the x value of a 32 byte (x,y) point on the curve. The compressed public key is 33 bytes (or 32 bytes and one bit) as described above and in the "Compressed Public Keys" section of Chapter 4 of "Mastering Bitcoin". +The salt is 32 bytes because it is used to "blind" the x value of a 32-byte (x,y) point on the curve. The compressed public key is 33 bytes (or 32 bytes and one bit) as described above and in the "Compressed Public Keys" section of Chapter 4 of "Mastering Bitcoin". -A bech32m encoded CT address can then be generated via: +A bech32m-encoded CT address can then be generated via:Also applies to: 120-120, 122-122
102-102: Remove redundant "same exact" phrasing.Line 102 uses the redundant phrase "same exact seed" where simply "same seed" is clearer and more concise.
-When Bob receives a CT, they combine the public blinding key with the private blinding key (ConfidentialNonce) to derive the same exact seed used by Alice. +When Bob receives a CT, they combine the public blinding key with the private blinding key (ConfidentialNonce) to derive the same seed used by Alice.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
dip-0032.md(1 hunks)
🧰 Additional context used
🪛 LanguageTool
dip-0032.md
[style] ~77-~77: ‘many different ways’ might be wordy. Consider a shorter alternative.
Context: ...d mathematical tool that can be used in many different ways with many different cryptographic syste...
(EN_WORDINESS_PREMIUM_MANY_DIFFERENT_WAYS)
[grammar] ~97-~97: Use a hyphen to join words.
Context: ...s * pk - The compressed public key, a 33 byte secp256k1 curve point. * Note tha...
(QB_NEW_EN_HYPHEN)
[style] ~102-~102: This phrase is redundant. Consider writing “same”.
Context: ...g key (ConfidentialNonce) to derive the same exact seed used by Alice. This is possible be...
(SAME_EXACT)
[grammar] ~120-~120: Use a hyphen to join words.
Context: ...t is used to "blind" the x value of a 32 byte (x,y) point on the curve. The compr...
(QB_NEW_EN_HYPHEN)
[grammar] ~122-~122: Use a hyphen to join words.
Context: ...ter 4 of "Mastering Bitcoin". A bech32m encoded CT address can then be generated...
(QB_NEW_EN_HYPHEN)
[style] ~285-~285: ‘with respect to’ might be wordy. Consider a shorter alternative.
Context: ... ``` and taking the discrete logarithm with respect to G of both sides we get `Log Q = Log (...
(EN_WORDINESS_PREMIUM_WITH_RESPECT_TO)
[style] ~287-~287: ‘with respect to’ might be wordy. Consider a shorter alternative.
Context: ...is called the Discrete Logarithm of Q with respect to G (or the Discrete Logarithm of Q i...
(EN_WORDINESS_PREMIUM_WITH_RESPECT_TO)
🪛 markdownlint-cli2 (0.18.1)
dip-0032.md
228-228: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4
(MD001, heading-increment)
242-242: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4
(MD001, heading-increment)
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.
Actionable comments posted: 1
♻️ Duplicate comments (1)
dip-0032.md (1)
199-211: Fix markdown heading structure—H1 headings violate nesting rules.Lines 199 and 211 use
# TxInputand# TxOutput(H1) inside a section that is already at H4 depth (#### extra_payloadat line 187). This violates markdown structure (MD001) and will render incorrectly in many renderers. Promote these to H4 to maintain proper hierarchy.-# TxInput +#### TxInput-# TxOutput +#### TxOutput
🧹 Nitpick comments (4)
dip-0032.md (4)
87-87: Add hyphens to compound adjectives modifying nouns.Several instances of multi-word size/format modifiers lack hyphens. Per grammar conventions, compound adjectives should be hyphenated when they precede a noun.
-This corresponds to the human-readable part (HRP) of the bech32m encoded address. +This corresponds to the human-readable part (HRP) of the bech32m-encoded address.-A bech32m encoded CT address can then be generated via: +A bech32m-encoded CT address can then be generated via:Apply the same fix to similar instances throughout (e.g., lines 93, 97, 115, 118, 120, 154, 168, 298).
Also applies to: 122-122
102-102: Remove redundant "exact" phrase."Same exact seed" is redundant; "same" suffices.
-When Bob receives a CT, they combine the public blinding key with the private blinding key (ConfidentialNonce) to derive the same exact seed used by Alice. +When Bob receives a CT, they combine the public blinding key with the private blinding key (ConfidentialNonce) to derive the same seed used by Alice.
81-81: Fix possessive pronoun: "it's" → "its"."…that it's values are within…" uses the contraction "it's" (it is); should use the possessive "its".
-The Bulletproof (a type of range proof) proves that the signature is valid, in particular, that it's values are within a certain valid range. +The Bulletproof (a type of range proof) proves that the signature is valid, in particular, that its values are within a certain valid range.
154-156: Clarify bulletproof range bound—specify whether N=52 or N=40 is normative.The document states two different range bounds:
- Line 156: "BP rangeproof N value would need to be equal to 52" (to cover 0–21M DASH).
- Line 156 (continued): "Currently this DIP limits… by requiring N=40" (≈10,995 DASH).
- Line 305–306: "the associated bulletproof must have a size of at most 40 bits."
While the text acknowledges the trade-off, the phrasing could be clearer. Recommend explicitly stating that N=40 is the normative limit for this DIP (not N=52 as a baseline). Ensure tables, consensus rules, and examples consistently reflect N=40 throughout so there is no ambiguity for implementers.
-* A BP rangeproof for each output that ensures the amount transferred is within a certain interval between 0 and 2^N - 1 - * To support all potential value transfers between 0 and 21M, the BP rangeproof N value would need to be equal to 52. Currently this DIP limits the size of CT outputs to just under 11000 DASH by requiring N=40. +* A BP rangeproof for each output that ensures the amount transferred is within a certain interval between 0 and 2^N - 1 + * This DIP sets N = 40, limiting single-UTXO values to approximately 10,995 DASH (see Risks section for rationale).Also applies to: 305-306
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
dip-0032.md(1 hunks)
🧰 Additional context used
🪛 LanguageTool
dip-0032.md
[style] ~77-~77: ‘many different ways’ might be wordy. Consider a shorter alternative.
Context: ...d mathematical tool that can be used in many different ways with many different cryptographic syste...
(EN_WORDINESS_PREMIUM_MANY_DIFFERENT_WAYS)
[grammar] ~87-~87: Use a hyphen to join words.
Context: ...human-readable part (HRP) of the bech32m encoded address. The exact structure of...
(QB_NEW_EN_HYPHEN)
[grammar] ~97-~97: Use a hyphen to join words.
Context: ...s * pk - The compressed public key, a 33 byte secp256k1 curve point. * Note tha...
(QB_NEW_EN_HYPHEN)
[style] ~102-~102: This phrase is redundant. Consider writing “same”.
Context: ...g key (ConfidentialNonce) to derive the same exact seed used by Alice. This is possible be...
(SAME_EXACT)
[grammar] ~115-~115: Use a hyphen to join words.
Context: ...the secret. For HD wallets, a master 32 byte salt (blinding key) is stored from ...
(QB_NEW_EN_HYPHEN)
[grammar] ~118-~118: Use a hyphen to join words.
Context: ...use a point on the curve is a pair of 32 byte numbers (x,y). If x is known, y is ...
(QB_NEW_EN_HYPHEN)
[grammar] ~120-~120: Use a hyphen to join words.
Context: ...t is used to "blind" the x value of a 32 byte (x,y) point on the curve. The compr...
(QB_NEW_EN_HYPHEN)
[grammar] ~122-~122: Use a hyphen to join words.
Context: ...ter 4 of "Mastering Bitcoin". A bech32m encoded CT address can then be generated...
(QB_NEW_EN_HYPHEN)
[grammar] ~154-~154: Use a hyphen to join words.
Context: ...on contains the following data : * A 33 byte Pedersen commitment to the amount b...
(QB_NEW_EN_HYPHEN)
[grammar] ~168-~168: Use a hyphen to join words.
Context: ...presented value) to save space. Variable length integers always precede a vector ...
(QB_NEW_EN_HYPHEN)
[style] ~285-~285: ‘with respect to’ might be wordy. Consider a shorter alternative.
Context: ... ``` and taking the discrete logarithm with respect to G of both sides we get `Log Q = Log (...
(EN_WORDINESS_PREMIUM_WITH_RESPECT_TO)
[style] ~287-~287: ‘with respect to’ might be wordy. Consider a shorter alternative.
Context: ...is called the Discrete Logarithm of Q with respect to G (or the Discrete Logarithm of Q i...
(EN_WORDINESS_PREMIUM_WITH_RESPECT_TO)
[grammar] ~298-~298: Use a hyphen to join words.
Context: ...ressBalance` must be a valid unsigned 64 bit integer (which stores values in duff...
(QB_NEW_EN_HYPHEN)
🔇 Additional comments (1)
dip-0032.md (1)
297-301: Clarify howconfidentialAddressBalance(uint64_t) combines with confidential commitment sums.The rule at line 299 requires
confidentialAddressBalanceto equal the previous value plus the sum of confidential inputs minus the sum of confidential outputs. However, Pedersen commitments are additive and verifiable using point arithmetic—nodes sum commitments as curve points and verify (ΣC_in − ΣC_out − C_fee) = 0 without revealing hidden amounts.The issue is type incompatibility:
confidentialAddressBalanceis a plain uint64_t field, while confidential inputs/outputs are Pedersen commitments (curve points). These cannot be directly combined in arithmetic as stated. The specification must clarify:
- Does the "sum of confidential inputs/outputs" mean commitment point addition on the curve?
- How does a uint64_t accumulator relate to Pedersen commitment balance verification?
- Is this field meant to track the cumulative net CT flow, and if so, what validates it against commitment proofs?
Without explicit definition of computation and verification, consensus behavior is undefined.
| * bkvb - Blinding key version byte | ||
| * atvb - Address type version byte | ||
| * salt (AKA public blinding key) - 32 byte random value | ||
| * When Alice sends a transaction to Bob, this public blinding key is used in an ECDH operation by Alice to transmit the actual amount and blinding key of a transaction to Bob | ||
| * Note that older Elements documentation called this a "scanning key" | ||
| * Note that this value is not hashed; it is stored unchanged as part of the CT address | ||
| * pk - The compressed public key, a 33 byte secp256k1 curve point. | ||
| * Note that a hash of the public key is what is actually included in the address | ||
|
|
||
| Every confidential address contains a public blinding key used in ECDH. When Alice sends Bob a transaction, Alice chooses an ECDH ephemeral private key (the ConfidentialNonce in this DIP) and combines it with the public blinding key to derive a blinding seed. | ||
|
|
||
| When Bob receives a CT, they combine the public blinding key with the private blinding key (ConfidentialNonce) to derive the same exact seed used by Alice. | ||
| This is possible because curve operations are commutative, i.e., `A*B=B*A` , also known as Abelian. To show this is true, let Alice's keypair be denoted `(d_A, Q_A)` and Bob's keypair `(d_B, Q_B)` where `d_A` and `d_B` are private keys and `Q_A` and `Q_B` are public keys. The public keys are computed as `Q_A=d_A * G` and `Q_B=d_B * G` where G is a generator of the curve and the operation `x*G` means adding `G` to itself `x` times. | ||
|
|
||
| To show that ECDH allows Alice and Bob to derive the same seed, we must show that the point on the curve `d_A*Q_B` is the same as `d_B*Q_A` : | ||
|
|
||
| ```text | ||
| d_A*Q_B = d_A * d_B * G # definition of Q_B | ||
| = d_B * d_A * G # commutativity | ||
| = d_B * Q_A | ||
| ``` | ||
|
|
||
| Therefore, Alice and Bob can compute a shared secret `(x,y) = d_A*Q_B = d_B*Q_A` without ever actually transmitting the secret. | ||
|
|
||
| For HD wallets, a master 32 byte salt (blinding key) is stored from which all blinding keys for generated addresses are derived. A | ||
| blinding key for an address is generated as `HMAC_SHA256(master blinding key, <address ScriptPubKey>)` . See the reference SLIP-0077 for more details. | ||
|
|
||
| To clarify, the public key can actually be stored in 32 bytes and one bit because a point on the curve is a pair of 32 byte numbers (x,y). If x is known, y is almost uniquely identified since for each `x`, there are two `y` values. This happens for the same reason that `x^2 = 4` has two solutions, +2 and -2. Since secp256k1 is symmetric about the x-axis, one bit can be used to indicate if the y value is above or below the x-axis. | ||
|
|
||
| The salt is 32 bytes because it is used to "blind" the x value of a 32 byte (x,y) point on the curve. The compressed public key is 33 bytes (or 32 bytes and one bit) as described above and in the "Compressed Public Keys" section of Chapter 4 of "Mastering Bitcoin". | ||
|
|
||
| A bech32m encoded CT address can then be generated via: | ||
|
|
||
| ```text | ||
| address = bech32m( bkvb + atvb + salt + RIPEMD160( SHA256( pk ) ) ) | ||
| ``` | ||
|
|
||
| where `+` denotes concatenation. Since the salt is 32 bytes and the output of RIPEMD160 is 20 bytes, the above bech32m encoded address encodes 54 bytes of data. This is described in more detail in the section "Legacy Addresses for P2PKH" of Chapter 4 of "Mastering Bitcoin". This assumes Confidential UTXOs will be stored in P2PKH format. |
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.
🛠️ Refactor suggestion | 🟠 Major
🧩 Analysis chain
Resolve CT address payload ambiguity—33-byte pk vs. 20-byte hash mismatch.
The bullet list (lines 91–98) claims the address payload contains a "33 byte secp256k1 curve point" (the full compressed public key), but the encoding formula at line 125 uses RIPEMD160( SHA256( pk ) ), which is only 20 bytes. This inconsistency means implementers cannot tell which layout is canonical; different nodes could produce incompatible addresses.
The total payload size (line 128) is stated as 54 bytes, which equals 2 (version bytes) + 32 (salt) + 20 (hash output)—matching the 20-byte hash, not the 33-byte key.
Recommended fix: Clarify that the address payload stores a 20-byte HASH160 of the public key, not the full 33-byte key. Update the bullet for pk to read:
pk - 20 byte HASH160 of the compressed public key
and remove or revise the note "Note that a hash of the public key is what is actually included in the address" to state it directly in the field description. Ensure the explanatory text (e.g., around line 128) consistently reflects that the address encodes the hash, not the full key.
I'll verify the review comment by examining the actual file content and checking the mathematical consistency of the claims.
---
🏁 Script executed:
```shell
#!/bin/bash
# Find and examine dip-0032.md
fd -n "dip-0032.md" -x cat {} | head -150
Length of output: 230
🏁 Script executed:
#!/bin/bash
# Get lines 91-128 specifically with line numbers
fd -n "dip-0032.md" -x sed -n '91,128p' {}Length of output: 230
🏁 Script executed:
#!/bin/bash
# Search for the pk field definition and surrounding context
fd -n "dip-0032.md" -x sed -n '90,130p' {}Length of output: 230
---
🏁 Script executed:
```shell
#!/bin/bash
# Find dip-0032.md and extract lines 91-128
fd "dip-0032.md" --exec sed -n '91,128p' {}
Length of output: 3332
🏁 Script executed:
#!/bin/bash
# Also get broader context lines 85-135 to see full picture
fd "dip-0032.md" --exec sed -n '85,135p' {}Length of output: 4730
🏁 Script executed:
#!/bin/bash
# Search for any mention of "33 byte" and "20 byte" in the file
fd "dip-0032.md" --exec grep -n "byte" {}Length of output: 4139
Address payload definition needs structural clarification for implementer safety.
The file does clarify that the address encodes a 20-byte hash via a sub-bullet note (line 98: "Note that a hash of the public key is what is actually included in the address"). However, the primary pk bullet definition states "a 33 byte secp256k1 curve point" which, while technically referring to the key itself, could lead implementers to misread the address payload structure—especially since the encoding formula immediately clarifies the hash is what goes into the address.
The byte-count math is correct: 2 (version) + 32 (salt) + 20 (hash) = 54 bytes as stated.
Recommended: Restructure the pk bullet to lead with "20 byte HASH160 of the compressed public key" to make the address payload explicit at first read, and clarify in a follow-up note that this is a hash of the 33-byte compressed key, improving clarity for implementers using this DIP as a reference.
🧰 Tools
🪛 LanguageTool
[grammar] ~97-~97: Use a hyphen to join words.
Context: ...s * pk - The compressed public key, a 33 byte secp256k1 curve point. * Note tha...
(QB_NEW_EN_HYPHEN)
[style] ~102-~102: This phrase is redundant. Consider writing “same”.
Context: ...g key (ConfidentialNonce) to derive the same exact seed used by Alice. This is possible be...
(SAME_EXACT)
[grammar] ~115-~115: Use a hyphen to join words.
Context: ...the secret. For HD wallets, a master 32 byte salt (blinding key) is stored from ...
(QB_NEW_EN_HYPHEN)
[grammar] ~118-~118: Use a hyphen to join words.
Context: ...use a point on the curve is a pair of 32 byte numbers (x,y). If x is known, y is ...
(QB_NEW_EN_HYPHEN)
[grammar] ~120-~120: Use a hyphen to join words.
Context: ...t is used to "blind" the x value of a 32 byte (x,y) point on the curve. The compr...
(QB_NEW_EN_HYPHEN)
[grammar] ~122-~122: Use a hyphen to join words.
Context: ...ter 4 of "Mastering Bitcoin". A bech32m encoded CT address can then be generated...
(QB_NEW_EN_HYPHEN)
🤖 Prompt for AI Agents
In dip-0032.md around lines 91 to 128, the pk bullet currently describes the raw
33-byte compressed public key and risks confusing implementers about the actual
address payload; change the pk bullet to state first that the address contains a
20-byte HASH160 (RIPEMD160(SHA256)) of the compressed public key, and add a
short follow-up note clarifying that the HASH160 is derived from the 33-byte
compressed secp256k1 public key (33 bytes = 32 bytes + parity bit), preserving
the existing math and the example address encoding formula.
This is a draft DIP for adding Confidential Transactions (CTs) to Dash. I would like to request a DIP number be assigned.
Any and all feedback is welcome, including suggestions for a base58 prefix for CT addresses.
Summary by CodeRabbit
New Features
Documentation