Proof Types Explained
Critical Distinction: DERO has two completely different proof systems that serve different purposes. Confusing them leads to fundamental misunderstandings about what can and cannot be verified.
A transaction proof is part of the chain, verified by every node. A payload proof is a sender-supplied display string that DERO's own code calls a helper "to detect and decode output amount for the tx." It was built to read transactions, not to prove them, which is why it's fakeable by design.
The Two Proof Systems
Quick Comparison
| Aspect | Transaction Proofs | Payload Proofs |
|---|---|---|
| Purpose | Consensus validation | Display/communication |
| Verified by | All network nodes | Explorer/wallet UI |
| Security | Cryptographically secure | Math check only |
| Can be faked? | Computationally infeasible | Yes (commitment-rearrangement) |
| Affects blockchain? | Yes | No |
| Third-party verifiable? | Yes (validity only) | No |
Transaction Proofs: The "Real" Proofs
What They Are
Transaction proofs are the cryptographic proofs that make transactions valid. They're generated when you create a transaction and verified by every node on the network.
Components:
- Six Sigma proofs (A_y, A_D, A_b, A_X, A_t, A_u)
- Inner product proof
- Challenge hash binding all components
How They Work
Security Guarantees
Verification (see cryptography/crypto/proof_verify.go):
The Verify() function (proof_verify.go:98) recomputes the challenge hash c from all six recovered sigma components (A_y, A_D, A_b, A_X, A_t, A_u) at lines 410-425, then verifies the inner product proof at lines 457-460. If any component was tampered with, the recomputed hash won't match and Verify() returns false.
Why they can't be faked:
- All proofs are cryptographically bound
- Faking one changes the hash
- Changed hash invalidates all responses
- Network consensus requires valid proofs
Payload Proofs: Display Convenience
What They Are
Payload proofs are wallet-level tools for showing transaction details in explorers and wallets. They're NOT used for consensus - they're just for display.
Purpose:
- Show transaction amounts in explorer
- Verify receipt of funds
- Sender/receiver communication
How They Work
The Math Check
What payload proof verification actually checks:
The verification in proof/proof.go performs a scalar multiplication to check math consistency: it takes the commitment C[position], subtracts a shared key derived from the proof, and checks whether the result equals amount × G. If the math is consistent, the amount is displayed.
The critical detail: this uses ScalarMult(crypto.G, new(big.Int).SetInt64(int64(amount))) at line 89 -- the int64 cast is where the wraparound issue occurs (see Technical Cause below).
What this does NOT check:
- ❌ Whether the sender actually had that amount
- ❌ Whether the sender is who they claim to be
- ❌ Whether the proof was generated legitimately
- ❌ Whether the amount is real vs. fabricated
Why fakeability isn't a flaw
Unforgeability is a deliberate property, not a default. Building it in requires cryptographic binding (signatures or commitments tied to keys), verification by a trusted party (consensus), and a formal soundness argument. DERO's designers added that machinery where the threat model required it — transaction proofs going to chain state. Payload proofs are a sender-supplied helper for reading what was sent; their threat model doesn't require unforgeability, so it wasn't built in. The absence isn't a flaw — it's the natural shape of an object whose use case never demanded the property.
Treating an object as if it should have a property it was never designed to have — for example, citing a "Verified ✓" payload proof as evidence of an on-chain mint — is a category error, not a finding.
How forgery works
Anyone can create a payload proof for any ring member:
Transaction has ring: [Addr_0, Addr_1, ..., Addr_15]
Amount: 100 DERO
For ANY position (0-15), you can create a proof that says:
"Address at position X sent 100 DERO"
The math will check out because:
- All ring members use the SAME amount in commitments
- The commitment math is public
- You can solve for any positionFor the worked example (with the actual fake string, screenshots, and reproducible code), see Forge a Fake Proof Yourself.
Demonstration
From security research (August 2025):
Transaction: c4fbc0d541c40abf6f5c818f3a0f8430e3ebb5c3f0811d74a4318892be4a4632
Fake proof created showing: 184,467,436,737,095 DERO (184 trillion)
Official DERO explorer result: "✓ Proof Verified Successfully"
Reality: DERO's emission model targets ~10.5 million total from mining
(source: transaction_execute.go:57 comment + CalcBlockReward halving schedule)
Conclusion: Payload proofs can show impossible amountsThis is not a bug — it's the consequence of an object built to read, not to prove.
The TX above (
c4fbc0d5…) is an independent security-research example. The forgery walkthrough used throughout the rest of this suite — including the reproducible Go code, the bech32 decode, and the patched-vs-unpatched explorer screenshots — is built around a different transaction (5bbe1b7e…) and lives at Inflation Claim § Part 3 — Forge a Fake Proof Yourself. Both transactions illustrate the same phenomenon: a payload proof is a user-supplied display object, not consensus state.
Technical Cause: The int64 Cast Issue
Why Fake Proofs "Validate"
The ability to create fake proofs showing impossible amounts (like 184 trillion DERO) stems from a specific technical issue in the explorer's proof verification code.
From Source Code (proof/proof.go:89):
// Original code - vulnerable to wraparound
x.ScalarMult(crypto.G, new(big.Int).SetInt64(int64(amount)))
// ↑ This cast is the issueThe Problem:
| Input (uint64) | After int64 Cast | Result |
|---|---|---|
100 | 100 | ✅ Normal |
2,200,000 | 2,200,000 | ✅ Normal |
9,223,372,036,854,775,808 (2^63) | -9,223,372,036,854,775,808 | ❌ Wraps negative |
18,446,744,073,707,351,616 | -2,200,000 | ❌ Wraps to -2.2M |
What Happens:
Why This Doesn't Affect Protocol Security
Protocol Is Secure: This issue only affects explorer DISPLAY. The actual transaction proofs (consensus level) are enforced by bulletproof soundness — the verifier rejects any committed value outside the proven range, including negative/wraparound amounts. See Negative Transfer Protection for the mathematical proof.
| Layer | Affected? | Why |
|---|---|---|
| Transaction Proofs | ❌ No | Bulletproof soundness rejects out-of-range commitments |
| Network Consensus | ❌ No | All nodes verify with proper validation |
| Blockchain State | ❌ No | Fake proofs don't affect state |
| Explorer Display | ✅ Yes | Was showing fake amounts as "verified" |
Explorer Mitigation
The Fix
To prevent explorers from displaying obviously fake amounts, validation was added to the payload proof verification.
Where this lives: Hologram's proof validation page (opens in a new tab), and — since PR #14 (opens in a new tab) landed — DEROFDN/derohe community-dev (opens in a new tab). The upstream main branch and other unpatched explorers do not include this validation. Hologram is an official community project developed by DHEBP.
New Validation (proof/proof_validation.go):
const (
// Maximum safe value for int64 conversion (2^63 - 1)
MAX_INT64_SAFE = 9223372036854775807
// Maximum reasonable amount (22M DERO in atomic units)
// ~5% above hard cap, allows buffer while blocking impossible amounts
MAX_REASONABLE_AMOUNT_ATOMIC = 22_000_000_000_000
)
func ValidatePayloadProofAmount(amount uint64) error {
// Check 1: Prevent int64 wraparound
if amount > MAX_INT64_SAFE {
return fmt.Errorf("amount exceeds int64 maximum - possible wraparound attack")
}
// Check 2: Reject impossibly large amounts
if amount > MAX_REASONABLE_AMOUNT_ATOMIC {
return fmt.Errorf("amount exceeds reasonable maximum - likely fake proof")
}
return nil
}Fixed int64 Cast (proof/proof.go:89):
// Before (vulnerable):
x.ScalarMult(crypto.G, new(big.Int).SetInt64(int64(amount)))
// After (safe):
x.ScalarMult(crypto.G, new(big.Int).SetUint64(amount))What This Prevents
| Attack | Before Fix | After Fix |
|---|---|---|
| 184 trillion DERO proof (wraparound display) | ✓ "Verified" | ✗ "Possible wraparound attack" |
| Negative amount wrapping to large positive (e.g., −1 DERO → 184 trillion display) | ✓ "Verified" | ✗ "Possible wraparound attack" |
| Legitimate 1M DERO | ✓ "Verified" | ✓ "Verified" |
Honest Limitations
What This Cannot Prevent: The fix blocks egregious fakes (184 trillion, negative wraparound) but cannot detect subtle fakes. A fake proof claiming 1,500 DERO instead of 1,000 DERO would still pass validation. This is a fundamental limitation of privacy-preserving design - full verification would require private keys.
What the fix CAN do:
- ✅ Block amounts large enough to trigger
int64wraparound (the actual demonstrated attack) - ✅ Block amounts that would wrap from negative back to an enormous positive display
- ✅ Prevent the specific attack used to create false narratives
- ✅ Improve user experience (no impossible amounts displayed)
What the fix CANNOT do:
- ❌ Verify the proof is genuine (would break privacy)
- ❌ Detect subtle inflation (e.g., 50% fake)
- ❌ Prove sender identity (by design)
- ❌ Replace proper wallet verification
The Bottom Line: This fix is a defense-in-depth enhancement for the display layer. It blocks the most egregious fake proofs while acknowledging that payload proofs are fundamentally forgeable as currently constructed — the commitment-rearrangement trick lets any ring member's commitment match any amount. For real verification, users should always check their wallet balance with their private keys.
The Critical Difference
What Each Proves
Transaction Proofs Prove:
✓ Transaction is valid
✓ Proofs are cryptographically correct
✓ Combined 128-bit value is valid (transfer + remaining balance)
✓ No transaction replay (nonce/height + key image binding)
✓ Sender has private key
Does NOT prove:
✗ Which ring member is sender
✗ The exact amount (only that it's valid)
✗ Sender identity to third partiesVerification Scope
Implications
For Users
When viewing transaction details:
- ✅ Transaction existence is real (on blockchain)
- ✅ Ring members are real (from blockchain)
- ⚠️ Displayed amounts may not be accurate
- ⚠️ Sender attribution may be wrong
To verify you received funds:
- ✅ Check your wallet balance (decrypted with your key)
- ❌ Don't rely solely on explorer display
For Developers
When building on DERO:
- ✅ Trust transaction proof validity (consensus level)
- ❌ Don't trust payload proof amounts (display level)
- ✅ Use wallet RPC to get real balances
- ❌ Don't use explorer proofs for verification logic
For Analysts
When analyzing transactions:
- ⚠️ Payload proofs can be fabricated
- ⚠️ Sender attribution is not cryptographically verifiable
- ⚠️ Amounts shown may be manipulated
- ✅ Transaction existence/validity is verifiable
Key Takeaways
Transaction Proofs
| Aspect | Status |
|---|---|
| Purpose | Consensus validation |
| Verified by | All network nodes |
| Can be faked | No |
| Trust level | Cryptographically secure |
Payload Proofs
| Aspect | Status |
|---|---|
| Purpose | Display convenience |
| Verified by | Explorer/wallet UI |
| Can be faked | Yes (commitment-rearrangement) |
| Trust level | Math check only |
The Bottom Line
Transaction Proofs = "Is this transaction valid?" (consensus)
Payload Proofs = "What does the sender claim?" (display)
Transaction proofs affect the blockchain.
Payload proofs are just metadata for display.
Don't confuse the two!Related: Payload Encryption (Separate Issue)
Distinct from payload proofs: This page covers payload proofs (display-level, fakeable due to the commitment-rearrangement construction). A separate issue involving payload encryption was disclosed in May 2024 and fixed in Release142.
The payload encryption issue involved randomness reuse between ElGamal ciphertexts and payload encryption keys. This allowed an attacker to perform trial decryption, potentially revealing sender, receiver, and amount for transactions made with vulnerable wallet software.
Key points:
- Fixed in Release142 via
ENCRYPTED_DEFAULT_PAYLOAD_CBOR_V2 - Affected: Wallet-layer privacy (sender/receiver/amount deanonymization)
- Not affected: Consensus layer, ledger integrity, transaction validity
- Historical transactions made before Release142 remain potentially affected
Recommendation: Use Release142+ wallets for hardened payload encryption.
Related Pages
Security Suite:
- Transaction Proofs - The six sigma system
- Ring Member Behavior - Why sender can't be identified
Privacy Suite:
- Ring Signatures - Sender anonymity
- Payload Proofs - Complete payload proof explanation