Protocol Integrity
Proof Types Explained

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.

The Two Proof Systems

Quick Comparison

AspectTransaction ProofsPayload Proofs
PurposeConsensus validationDisplay/communication
Verified byAll network nodesExplorer/wallet UI
SecurityCryptographically secureMath check only
Can be faked?NoYes (by design)
Affects blockchain?YesNo
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 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-459. 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 98 -- 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 Payload Proofs Can Be Faked

The Design Reason

🔐

Privacy by Design: If payload proofs were cryptographically verifiable by third parties, it would break sender privacy. The entire point of ring signatures is that third parties CANNOT definitively prove who sent a transaction.

How Faking 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 position

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 amounts

This is not a bug - it's the consequence of privacy-preserving design.


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:98):

// Original code - vulnerable to wraparound
x.ScalarMult(crypto.G, new(big.Int).SetInt64(int64(amount)))
//                                  ↑ This cast is the issue

The Problem:

Input (uint64)After int64 CastResult
100100✅ Normal
2,200,0002,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) use proper validation with bit decomposition that catches negative values. See Negative Transfer Protection for the mathematical proof.

LayerAffected?Why
Transaction Proofs❌ NoBit decomposition catches negatives
Network Consensus❌ NoAll nodes verify with proper validation
Blockchain State❌ NoFake proofs don't affect state
Explorer Display✅ YesWas 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.

🛡️

Hologram-Exclusive: This fix is currently only implemented in Hologram's proof validation page (opens in a new tab). The standard derohe binaries from the official DERO repository 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:98):

// 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

AttackBefore FixAfter Fix
184 trillion DERO proof✓ "Verified"✗ "Amount exceeds maximum"
Wraparound to -2.2M✓ "Verified"✗ "Possible wraparound attack"
100M DERO fake✓ "Verified"✗ "Exceeds reasonable maximum"
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 > 22M DERO (above total supply)
  • ✅ Block amounts that would wrap to negative
  • ✅ 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 unverifiable by design. 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 parties

Verification 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

Why This Design?

The Privacy Trade-off

Option A: Verifiable payload proofs
  → Third parties can prove sender identity
  → Ring signatures become meaningless
  → Privacy: ❌ BROKEN

Option B: Unverifiable payload proofs (DERO's choice)
  → Third parties can only guess sender
  → Ring signatures provide real privacy
  → Privacy: ✅ PRESERVED

The Philosophy

🛡️

Privacy Over Verification: DERO prioritizes user privacy over third-party verification capabilities. Only YOU should be able to prove you sent a transaction. Third parties should only be able to guess.


Key Takeaways

Transaction Proofs

AspectStatus
PurposeConsensus validation
Verified byAll network nodes
Can be fakedNo
Trust levelCryptographically secure

Payload Proofs

AspectStatus
PurposeDisplay convenience
Verified byExplorer/wallet UI
Can be fakedYes (by design)
Trust levelMath 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 by design). 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:

Privacy Suite: