From 2c315fcb4926350d829be1bd9937c6a256479a32 Mon Sep 17 00:00:00 2001 From: pasta Date: Sat, 17 Jan 2026 01:50:35 -0600 Subject: [PATCH 1/8] feat: add DIP for Compact Quorum Proof Chains Introduces a new DIP specifying trustless verification of LLMQ public keys using ChainLocks and merkleRootQuorums. This enables light clients and the Platform SDK to cryptographically verify Platform quorum public keys without trusting external parties. Key features: - Compact proofs (~1 KB typical) using chainlock-based verification - No header chains needed - verifies against chainlocked block's cbtx - New P2P messages (GETQUORUMPROOFCHAIN, QUORUMPROOFCHAIN) - gRPC API for Platform SDK integration Co-Authored-By: Claude Opus 4.5 --- dip-pasta-compact-quorum-proofs.md | 503 +++++++++++++++++++++++++++++ 1 file changed, 503 insertions(+) create mode 100644 dip-pasta-compact-quorum-proofs.md diff --git a/dip-pasta-compact-quorum-proofs.md b/dip-pasta-compact-quorum-proofs.md new file mode 100644 index 00000000..13165848 --- /dev/null +++ b/dip-pasta-compact-quorum-proofs.md @@ -0,0 +1,503 @@ +
+  DIP: pasta-compact-quorum-proofs
+  Title: Compact Quorum Proof Chains for Trustless Platform Verification
+  Author(s): PastaPastaPasta
+  Special-Thanks:
+  Comments-Summary: No comments yet.
+  Status: Draft
+  Type: Standard
+  Created: 2026-01-17
+  License: MIT License
+
+ +## Table of Contents + +1. [Abstract](#abstract) +1. [Motivation](#motivation) +1. [Prior Work](#prior-work) +1. [Trusted Initial State](#trusted-initial-state) +1. [Quorum Proof Chain Data Structures](#quorum-proof-chain-data-structures) + 1. [ChainlockEntry](#chainlockentry) + 1. [QuorumCommitmentProof](#quorumcommitmentproof) + 1. [QuorumProofChainResponse](#quorumproofchainresponse) +1. [Verification Algorithm](#verification-algorithm) + 1. [Verifier State](#verifier-state) + 1. [ChainLock Verification](#chainlock-verification) + 1. [Quorum Commitment Verification](#quorum-commitment-verification) + 1. [Proof Processing Algorithm](#proof-processing-algorithm) +1. [Proof Construction](#proof-construction) + 1. [ChainLock Selection Strategy](#chainlock-selection-strategy) + 1. [Quorum Commitment Merkle Proof Construction](#quorum-commitment-merkle-proof-construction) +1. [P2P Messages](#p2p-messages) + 1. [GETQUORUMPROOFCHAIN](#getquorumproofchain) + 1. [QUORUMPROOFCHAIN](#quorumproofchain) +1. [gRPC API](#grpc-api) +1. [Proof Size Analysis](#proof-size-analysis) +1. [Security Considerations](#security-considerations) +1. [Backward Compatibility](#backward-compatibility) +1. [Reference Implementation](#reference-implementation) +1. [Copyright](#copyright) + +## Abstract + +This DIP defines Compact Quorum Proof Chains, a mechanism for trustlessly verifying LLMQ public keys using ChainLocks and the `merkleRootQuorums` field in coinbase transactions. This enables light clients and the Platform SDK to cryptographically verify Platform quorum public keys without trusting any external party. + +The key insight is that a ChainLock at height H proves block H, and that block's coinbase transaction contains a `merkleRootQuorums` covering ALL currently active quorums at that height. This allows verification of any active quorum's commitment using a single chainlocked block's data, resulting in compact proofs of approximately 1 KB in typical scenarios. + +## Motivation + +Platform proof verification currently requires two steps: + +1. **GroveDB Proof**: Verify data against a Merkle root (cryptographic) +2. **Tenderdash Signature**: Verify BLS signature from a Platform quorum (requires quorum public key) + +The quorum public key is currently obtained via trusted sources: + +| Method | Trust Model | +| ------ | ----------- | +| Dash Core RPC | Trust the node operator | +| TrustedHttpContextProvider | Trust centralized quorum servers | + +Both methods require trusting an external party, which undermines the trustless nature of the verification. + +This DIP enables verification of quorum public keys cryptographically using only: + +1. A hardcoded checkpoint (block hash + known quorum keys) embedded in the SDK +2. Proofs provided by any untrusted server (verified client-side) + +By leveraging the existing ChainLock infrastructure and `merkleRootQuorums` commitment in each block's coinbase, clients can build a cryptographic chain of trust from a known checkpoint to any currently active quorum. + +## Prior Work + +* [DIP-0002: Special Transactions](https://github.com/dashpay/dips/blob/master/dip-0002.md) +* [DIP-0004: Simplified Verification of Deterministic Masternode Lists](https://github.com/dashpay/dips/blob/master/dip-0004.md) +* [DIP-0006: Long-Living Masternode Quorums](https://github.com/dashpay/dips/blob/master/dip-0006.md) +* [DIP-0007: LLMQ Signing Requests / Sessions](https://github.com/dashpay/dips/blob/master/dip-0007.md) +* [DIP-0008: ChainLocks](https://github.com/dashpay/dips/blob/master/dip-0008.md) + +## Trusted Initial State + +Verification requires a trusted starting point embedded in client software. This checkpoint must contain: + +1. A block hash and height identifying a known-good block +2. The public keys of active ChainLock quorums at that height (identified by quorum hash and type) + +The specific serialization format of this checkpoint is an implementation detail left to client software. + +### Quorum Lifespans and Checkpoint Freshness + +The effectiveness of this verification scheme depends on overlapping quorum lifespans between the checkpoint and current chain tip. + +**Mainnet:** + +| Quorum Type | Purpose | DKG Interval | Active Count | Lifespan | +| ----------- | ------- | ------------ | ------------ | -------- | +| LLMQ_400_60 | ChainLocks | 288 blocks (~12 hours) | 4 | ~48 hours | +| LLMQ_100_67 | Platform | 24 blocks (~1 hour) | 24 | ~24 hours | + +**Testnet:** + +| Quorum Type | Purpose | DKG Interval | Active Count | Lifespan | +| ----------- | ------- | ------------ | ------------ | -------- | +| LLMQ_50_60 | ChainLocks | 288 blocks (~12 hours) | 4 | ~48 hours | +| LLMQ_25_67 | Platform | 24 blocks (~1 hour) | 24 | ~24 hours | + +With a checkpoint less than 36 hours old, at least one ChainLock quorum from the checkpoint will still be active, enabling direct verification with a single chainlock. Older checkpoints require bridging through intermediate chainlock quorums. + +## Quorum Proof Chain Data Structures + +### ChainlockEntry + +A ChainLock signature that proves a specific block is canonical. + +| Field | Type | Size | Description | +| ----- | ---- | ---- | ----------- | +| height | int32_t | 4 | Height of the chainlocked block | +| blockHash | uint256 | 32 | Hash of the chainlocked block | +| signature | BLSSig | 96 | Recovered threshold signature | +| signingQuorumHash | uint256 | 32 | Hash of the quorum that signed this chainlock | +| signingQuorumType | uint8_t | 1 | LLMQ type of the signing quorum | + +Total size: ~165 bytes + +**Note**: The signing quorum can be deterministically calculated using `SelectQuorumForSigning` given the height, but including it explicitly simplifies verification and enables the verifier to check if the signing quorum is known before attempting signature verification. + +### QuorumCommitmentProof + +A proof that a specific quorum commitment is included in a chainlocked block's `merkleRootQuorums`. + +| Field | Type | Size | Description | +| ----- | ---- | ---- | ----------- | +| commitment | CFinalCommitment | variable | The quorum final commitment (see [DIP-0006](https://github.com/dashpay/dips/blob/master/dip-0006.md)) | +| chainlockIndex | uint32_t | 4 | Index into the response's chainlocks array | +| quorumMerklePathLength | compactSize uint | 1-9 | Number of hashes in the merkle path | +| quorumMerklePath | uint256[] | 32 * length | Merkle path from commitment hash to `merkleRootQuorums` | +| coinbaseTx | CTransaction | variable | The coinbase transaction containing `merkleRootQuorums` | +| coinbaseMerkleProof | CPartialMerkleTree | variable | Merkle proof that coinbase is in the block | + +The `CFinalCommitment` structure is defined in [DIP-0006](https://github.com/dashpay/dips/blob/master/dip-0006.md) and contains the `quorumPublicKey` that this proof ultimately verifies. + +Estimated size: ~700-900 bytes (varies by quorum size and tree depth) + +### QuorumProofChainResponse + +The complete response containing all data needed to verify a target quorum. + +| Field | Type | Size | Description | +| ----- | ---- | ---- | ----------- | +| headerCount | compactSize uint | 1-9 | Number of block headers | +| headers | CBlockHeader[] | 80 * count | Block headers for each chainlock | +| chainlockCount | compactSize uint | 1-9 | Number of chainlock entries | +| chainlocks | ChainlockEntry[] | variable | ChainLock signatures in verification order | +| quorumProofCount | compactSize uint | 1-9 | Number of quorum proofs | +| quorumProofs | QuorumCommitmentProof[] | variable | Quorum commitment proofs | + +Headers and chainlocks are paired by index: `headers[i]` corresponds to `chainlocks[i]`. + +## Verification Algorithm + +### Verifier State + +The verifier maintains the following state: + +```text +H_verified: uint32 // Highest height cryptographically confirmed +Q_known: Map<(uint256, uint8), BLSPubKey> // Known quorum public keys + // Key: (quorumHash, quorumType) + // Value: quorumPublicKey +``` + +Initial state is populated from the checkpoint: + +* `H_verified = checkpoint.height` +* `Q_known = checkpoint.chainlockQuorums ∪ checkpoint.platformQuorums` + +### ChainLock Verification + +To verify a ChainLock against a known quorum: + +1. Verify the signing quorum is in `Q_known` +2. Retrieve the quorum's public key from `Q_known` +3. Calculate the message hash: + `msgHash = SHA256(llmqType, quorumHash, SHA256(height), blockHash)` +4. Verify the BLS signature against the quorum public key and message hash +5. Verify that `hash(header) == blockHash` + +If verification succeeds, update `H_verified = max(H_verified, chainlock.height)`. + +### Quorum Commitment Verification + +To verify a quorum commitment proof: + +1. Verify `chainlockIndex` references a chainlock with height <= `H_verified` +2. Retrieve the corresponding coinbase transaction and block header +3. Verify the coinbase merkle proof: + * The `coinbaseMerkleProof` must prove that `coinbaseTx` is in the block + * The block's merkle root must match `header.hashMerkleRoot` +4. Extract `merkleRootQuorums` from the coinbase transaction's extra payload +5. Calculate `commitmentHash = SHA256(serialize(commitment))` +6. Verify the quorum merkle path: + * Compute the merkle root from `commitmentHash` and `quorumMerklePath` + * The computed root must match `merkleRootQuorums` +7. Verify the commitment's `quorumSig`: + * This threshold signature proves the quorum members agreed on this public key + * Verification uses the aggregated public keys from the commitment's `signers` bitvector + +If verification succeeds, add the quorum to `Q_known`: +`Q_known[(commitment.quorumHash, commitment.llmqType)] = commitment.quorumPublicKey` + +### Proof Processing Algorithm + +```text +FUNCTION verify_quorum_proof(checkpoint, proof, targetQuorum) -> Result: + + // Initialize state from checkpoint + H_verified = checkpoint.height + Q_known = checkpoint.chainlockQuorums ∪ checkpoint.platformQuorums + + // Process proof iteratively until no more progress + LOOP: + made_progress = false + + // 1. Process chainlocks that can now be verified + FOR i, CL IN enumerate(proof.chainlocks): + IF CL.height <= H_verified: + CONTINUE // Already verified + + IF NOT Q_known.contains((CL.signingQuorumHash, CL.signingQuorumType)): + CONTINUE // Cannot verify yet + + // Verify chainlock signature + quorumPubKey = Q_known[(CL.signingQuorumHash, CL.signingQuorumType)] + msgHash = SHA256(CL.signingQuorumType, CL.signingQuorumHash, + SHA256(CL.height), CL.blockHash) + VERIFY_BLS(CL.signature, quorumPubKey, msgHash)? + + // Verify header matches chainlock + header = proof.headers[i] + ASSERT(hash(header) == CL.blockHash) + + // Extend verified horizon + H_verified = CL.height + made_progress = true + + // 2. Learn quorum commitments from verified blocks + FOR QP IN proof.quorumProofs: + clHeight = proof.chainlocks[QP.chainlockIndex].height + IF clHeight > H_verified: + CONTINUE // Chainlock not yet verified + + key = (QP.commitment.quorumHash, QP.commitment.llmqType) + IF Q_known.contains(key): + CONTINUE // Already known + + // Verify coinbase is in the chainlocked block + VERIFY_COINBASE_MERKLE_PROOF(QP.coinbaseTx, QP.coinbaseMerkleProof, + proof.headers[QP.chainlockIndex])? + + // Verify commitment is in merkleRootQuorums + merkleRootQuorums = extract_merkle_root_quorums(QP.coinbaseTx) + VERIFY_QUORUM_MERKLE_PATH(QP.commitment, QP.quorumMerklePath, + merkleRootQuorums)? + + // Verify commitment signature (proves DKG validity) + VERIFY_COMMITMENT_SIGNATURE(QP.commitment)? + + // Add to known quorums + Q_known[key] = QP.commitment.quorumPublicKey + made_progress = true + + IF NOT made_progress: + BREAK + + // 3. Return target quorum if found + targetKey = (targetQuorum.quorumHash, targetQuorum.quorumType) + RETURN Q_known.get(targetKey).ok_or(Error::InsufficientProof) +``` + +## Proof Construction + +This section describes how a node constructs proofs for requesting clients. + +### ChainLock Selection Strategy + +The goal is to find the shortest chain of chainlocks from the checkpoint to the target quorum. + +#### Case 1: Checkpoint quorum overlap exists + +When the checkpoint is less than approximately 48 hours old: + +1. Identify which checkpoint chainlock quorums are still active +2. Find the most recent chainlock signed by any of these quorums +3. This single chainlock can prove all currently active quorums + +#### Case 2: No overlap + +When the checkpoint is more than approximately 48 hours old: + +1. Identify the checkpoint chainlock quorum with the longest remaining lifespan at checkpoint time +2. Find the most recent chainlock signed by that quorum +3. Learn new chainlock quorums from that block's `merkleRootQuorums` +4. Repeat with newly learned quorums until reaching a chainlock whose block contains the target quorum + +### Quorum Commitment Merkle Proof Construction + +The `merkleRootQuorums` in each coinbase is calculated as follows (per [DIP-0004](https://github.com/dashpay/dips/blob/master/dip-0004.md)): + +1. Collect all final commitments from all active LLMQ sets at the block height +2. Calculate `hash = SHA256(serialize(commitment))` for each commitment +3. Sort hashes in ascending order +4. Calculate merkle root from the sorted list + +To construct a proof for a specific commitment: + +1. Retrieve all active commitments at the chainlock height +2. Compute all commitment hashes and sort them +3. Find the index of the target commitment's hash +4. Construct the merkle path from that index to the root + +## P2P Messages + +### GETQUORUMPROOFCHAIN + +Request a quorum proof chain from a peer. + +| Field | Type | Size | Description | +| ----- | ---- | ---- | ----------- | +| checkpointBlockHash | uint256 | 32 | Block hash of the client's checkpoint | +| checkpointHeight | uint32_t | 4 | Height of the checkpoint block | +| checkpointQuorumCount | compactSize uint | 1-9 | Number of known chainlock quorums | +| checkpointQuorums | QuorumEntry[] | variable | Known chainlock quorum entries from checkpoint | +| targetQuorumHash | uint256 | 32 | Hash of the target quorum to prove | +| targetQuorumType | uint8_t | 1 | LLMQ type of the target quorum | + +### QUORUMPROOFCHAIN + +Response containing the proof chain. + +The response uses the `QuorumProofChainResponse` structure defined in [Quorum Proof Chain Data Structures](#quorum-proof-chain-data-structures). + +| Field | Type | Size | Description | +| ----- | ---- | ---- | ----------- | +| response | QuorumProofChainResponse | variable | The complete proof chain | + +## gRPC API + +For Platform SDK integration, the following gRPC endpoint is defined: + +```protobuf +service Core { + rpc GetQuorumProofChain(GetQuorumProofChainRequest) + returns (GetQuorumProofChainResponse); +} + +message QuorumEntry { + bytes quorum_hash = 1; // 32 bytes + uint32 quorum_type = 2; + bytes quorum_public_key = 3; // 48 bytes +} + +message GetQuorumProofChainRequest { + bytes checkpoint_block_hash = 1; // 32 bytes + uint32 checkpoint_height = 2; + repeated QuorumEntry checkpoint_chainlock_quorums = 3; + bytes target_quorum_hash = 4; // 32 bytes + uint32 target_quorum_type = 5; +} + +message ChainlockEntry { + int32 height = 1; + bytes block_hash = 2; // 32 bytes + bytes signature = 3; // 96 bytes + bytes signing_quorum_hash = 4; // 32 bytes + uint32 signing_quorum_type = 5; +} + +message QuorumCommitmentProof { + bytes commitment = 1; // Serialized CFinalCommitment + uint32 chainlock_index = 2; + repeated bytes quorum_merkle_path = 3; // Each 32 bytes + bytes coinbase_tx = 4; // Serialized transaction + bytes coinbase_merkle_proof = 5; // Serialized CPartialMerkleTree +} + +message GetQuorumProofChainResponse { + repeated bytes headers = 1; // Each 80 bytes + repeated ChainlockEntry chainlocks = 2; + repeated QuorumCommitmentProof quorum_proofs = 3; +} +``` + +## Proof Size Analysis + +### Component Sizes + +| Component | Size | +| --------- | ---- | +| Block header | 80 bytes | +| ChainlockEntry | ~165 bytes | +| QuorumCommitmentProof | ~700-900 bytes | + +### Per-Chainlock Overhead + +Each chainlock in the proof requires: + +* 1 ChainlockEntry: ~165 bytes +* 1 Block header: 80 bytes +* **Subtotal: ~245 bytes** + +### Scenarios + +#### Fresh Checkpoint + +When the checkpoint is less than 36 hours old, a checkpoint chainlock quorum is still active. A single chainlock reaches the tip. + +| Component | Count | Size Each | Total | +| --------- | ----- | --------- | ----- | +| Chainlock + header | 1 | 245 B | 245 B | +| Target quorum proof | 1 | 800 B | 800 B | +| **Total** | | | **~1 KB** | + +#### Stale Checkpoint + +When the checkpoint is 2-4 days old, checkpoint quorums have expired. Need 1-2 bridging chainlock quorums. + +| Component | Count | Size Each | Total | +| --------- | ----- | --------- | ----- | +| Chainlocks + headers | 2-3 | 245 B | 490-735 B | +| Bridge CL quorum proofs | 1-2 | 800 B | 800-1,600 B | +| Target quorum proof | 1 | 800 B | 800 B | +| **Total** | | | **~2-3 KB** | + +#### Very Old Checkpoint + +When the checkpoint is 30+ days old, approximately 15 bridging chainlock quorums are needed (one per ~2 day interval). + +| Component | Count | Size Each | Total | +| --------- | ----- | --------- | ----- | +| Chainlocks + headers | ~15 | 245 B | ~3,700 B | +| Bridge CL quorum proofs | ~15 | 800 B | ~12,000 B | +| Target quorum proof | 1 | 800 B | 800 B | +| **Total** | | | **~16 KB** | + +### Summary + +| Checkpoint Age | Proof Size | +| -------------- | ---------- | +| < 36 hours | ~1 KB | +| 2-4 days | ~2-3 KB | +| 30+ days | ~16 KB | + +Checkpoints should be updated with each SDK release (monthly recommended) to maintain minimal proof sizes. + +## Security Considerations + +### Trust Assumptions + +| Assumption | Basis | +| ---------- | ----- | +| Checkpoint is correct | Code review, reproducible builds, distribution via official channels | +| BLS signatures unforgeable | Cryptographic hardness of BLS scheme | +| Merkle proofs sound | Collision resistance of SHA-256 | +| ChainLocks are secure | DIP-0008 security analysis | + +### Attack Resistance + +| Attack | Why It Fails | +| ------ | ------------ | +| Forge chainlock | Requires 240 of 400 masternode operator keys (60% threshold) | +| Forge quorum commitment | `quorumSig` is threshold signature requiring quorum threshold | +| Wrong block hash | Header hash must match chainlock's `blockHash` | +| Tampered coinbase | Coinbase merkle proof verification fails | +| Tampered commitment | Commitment merkle proof verification fails | +| Omit proof data | Verification fails, client retries or tries different server | + +### Failure Modes + +| Failure | Cause | Resolution | +| ------- | ----- | ---------- | +| No verifiable chainlock | Server omitted required data | Retry or try different server | +| Verification timeout | Large proof on slow device | Increase timeout or use fresher checkpoint | +| Checkpoint too old | SDK not updated in 30+ days | Ship new checkpoint in SDK update | + +## Backward Compatibility + +This DIP introduces new P2P messages (`GETQUORUMPROOFCHAIN`, `QUORUMPROOFCHAIN`) and gRPC endpoints that do not affect existing functionality. Nodes that do not implement this DIP will not respond to these messages. + +Clients implementing trustless verification should fall back to trusted verification methods if: + +1. No peers support the new messages +2. Proof verification fails repeatedly +3. The checkpoint is too old to construct a valid proof chain + +## Reference Implementation + +Reference implementation will be provided in: + +* Dash Core: Chainlock indexing and proof generation RPCs +* Platform: `rs-trustless-quorum-verifier` crate for Rust verification +* DAPI: gRPC endpoint wrapping Core RPCs + +## Copyright + +Copyright (c) 2026 Dash Core Group, Inc. [Licensed under the MIT License](https://opensource.org/licenses/MIT) From e378afa6502b0d464c6950f37b5066cc65df0fe8 Mon Sep 17 00:00:00 2001 From: pasta Date: Sat, 17 Jan 2026 11:02:22 -0600 Subject: [PATCH 2/8] docs: add Dash Core RPC methods, clarify P2P vs RPC communication Address review feedback: - Add Dash Core RPC section (getquorumproofchain, verifyquorumproofchain) - Clarify that Platform/DAPI uses RPC to local Core node - Note that P2P messages are for SPV light clients - Add protocol version requirement note for P2P messages Co-Authored-By: Claude Opus 4.5 --- dip-pasta-compact-quorum-proofs.md | 70 +++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/dip-pasta-compact-quorum-proofs.md b/dip-pasta-compact-quorum-proofs.md index 13165848..1f8870fa 100644 --- a/dip-pasta-compact-quorum-proofs.md +++ b/dip-pasta-compact-quorum-proofs.md @@ -28,8 +28,11 @@ 1. [Proof Construction](#proof-construction) 1. [ChainLock Selection Strategy](#chainlock-selection-strategy) 1. [Quorum Commitment Merkle Proof Construction](#quorum-commitment-merkle-proof-construction) +1. [Dash Core RPC Methods](#dash-core-rpc-methods) + 1. [getquorumproofchain](#getquorumproofchain) + 1. [verifyquorumproofchain](#verifyquorumproofchain) 1. [P2P Messages](#p2p-messages) - 1. [GETQUORUMPROOFCHAIN](#getquorumproofchain) + 1. [GETQUORUMPROOFCHAIN](#getquorumproofchain-1) 1. [QUORUMPROOFCHAIN](#quorumproofchain) 1. [gRPC API](#grpc-api) 1. [Proof Size Analysis](#proof-size-analysis) @@ -316,8 +319,71 @@ To construct a proof for a specific commitment: 3. Find the index of the target commitment's hash 4. Construct the merkle path from that index to the root +## Dash Core RPC Methods + +Platform nodes communicate with their local Dash Core node via RPC, not P2P. The following RPC methods are added to Dash Core for proof generation and verification. + +### getquorumproofchain + +Generates a quorum proof chain from a checkpoint to a target quorum. + +**Arguments:** + +| # | Name | Type | Description | +| - | ---- | ---- | ----------- | +| 1 | checkpointBlockHash | string | Block hash of the checkpoint (hex) | +| 2 | checkpointHeight | number | Height of the checkpoint block | +| 3 | checkpointQuorums | array | Array of known chainlock quorum objects | +| 4 | targetQuorumHash | string | Hash of the target quorum to prove (hex) | +| 5 | targetQuorumType | number | LLMQ type of the target quorum | + +Each checkpoint quorum object contains: + +* `quorumHash` (string): Quorum identifier (hex) +* `quorumType` (number): LLMQ type +* `quorumPublicKey` (string): Quorum public key (hex) + +**Result:** + +Returns the `QuorumProofChainResponse` structure with headers, chainlocks, and quorum proofs encoded as hex strings. + +### verifyquorumproofchain + +Verifies a quorum proof chain and returns the target quorum's public key if valid. + +**Arguments:** + +| # | Name | Type | Description | +| - | ---- | ---- | ----------- | +| 1 | checkpointBlockHash | string | Block hash of the checkpoint (hex) | +| 2 | checkpointHeight | number | Height of the checkpoint block | +| 3 | checkpointQuorums | array | Array of known chainlock quorum objects | +| 4 | proof | object | The proof chain to verify | +| 5 | targetQuorumHash | string | Hash of the target quorum (hex) | +| 6 | targetQuorumType | number | LLMQ type of the target quorum | + +**Result:** + +```json +{ + "valid": true, + "quorumPublicKey": "hexstring" +} +``` + +Or on failure: + +```json +{ + "valid": false, + "error": "error message" +} +``` + ## P2P Messages +These messages enable SPV light clients to request quorum proofs directly from peers without requiring a local Dash Core node. Nodes supporting these messages must advertise protocol version >= XXXXXX (to be assigned). + ### GETQUORUMPROOFCHAIN Request a quorum proof chain from a peer. @@ -343,7 +409,7 @@ The response uses the `QuorumProofChainResponse` structure defined in [Quorum Pr ## gRPC API -For Platform SDK integration, the following gRPC endpoint is defined: +DAPI exposes quorum proof functionality to remote clients via gRPC. Internally, DAPI calls the Dash Core RPC methods described above on its local Core node. ```protobuf service Core { From fa0fc4ef093afb2874f03f41767dbaa87eb114d7 Mon Sep 17 00:00:00 2001 From: pasta Date: Sat, 17 Jan 2026 11:29:31 -0600 Subject: [PATCH 3/8] docs: simplify RPC/P2P interfaces per review feedback Remove redundant fields from API - Core derives checkpoint height and active chainlock quorums from the checkpoint block hash. Co-Authored-By: Claude Opus 4.5 --- dip-pasta-compact-quorum-proofs.md | 39 +++++++----------------------- 1 file changed, 9 insertions(+), 30 deletions(-) diff --git a/dip-pasta-compact-quorum-proofs.md b/dip-pasta-compact-quorum-proofs.md index 1f8870fa..d11294d8 100644 --- a/dip-pasta-compact-quorum-proofs.md +++ b/dip-pasta-compact-quorum-proofs.md @@ -321,7 +321,7 @@ To construct a proof for a specific commitment: ## Dash Core RPC Methods -Platform nodes communicate with their local Dash Core node via RPC, not P2P. The following RPC methods are added to Dash Core for proof generation and verification. +Platform nodes communicate with their local Dash Core node via RPC, not P2P. The following RPC methods are added to Dash Core for proof generation and verification. Core derives the checkpoint height and active chainlock quorums from the checkpoint block hash. ### getquorumproofchain @@ -332,16 +332,8 @@ Generates a quorum proof chain from a checkpoint to a target quorum. | # | Name | Type | Description | | - | ---- | ---- | ----------- | | 1 | checkpointBlockHash | string | Block hash of the checkpoint (hex) | -| 2 | checkpointHeight | number | Height of the checkpoint block | -| 3 | checkpointQuorums | array | Array of known chainlock quorum objects | -| 4 | targetQuorumHash | string | Hash of the target quorum to prove (hex) | -| 5 | targetQuorumType | number | LLMQ type of the target quorum | - -Each checkpoint quorum object contains: - -* `quorumHash` (string): Quorum identifier (hex) -* `quorumType` (number): LLMQ type -* `quorumPublicKey` (string): Quorum public key (hex) +| 2 | targetQuorumHash | string | Hash of the target quorum to prove (hex) | +| 3 | targetQuorumType | number | LLMQ type of the target quorum | **Result:** @@ -356,11 +348,9 @@ Verifies a quorum proof chain and returns the target quorum's public key if vali | # | Name | Type | Description | | - | ---- | ---- | ----------- | | 1 | checkpointBlockHash | string | Block hash of the checkpoint (hex) | -| 2 | checkpointHeight | number | Height of the checkpoint block | -| 3 | checkpointQuorums | array | Array of known chainlock quorum objects | -| 4 | proof | object | The proof chain to verify | -| 5 | targetQuorumHash | string | Hash of the target quorum (hex) | -| 6 | targetQuorumType | number | LLMQ type of the target quorum | +| 2 | proof | object | The proof chain to verify | +| 3 | targetQuorumHash | string | Hash of the target quorum (hex) | +| 4 | targetQuorumType | number | LLMQ type of the target quorum | **Result:** @@ -386,14 +376,11 @@ These messages enable SPV light clients to request quorum proofs directly from p ### GETQUORUMPROOFCHAIN -Request a quorum proof chain from a peer. +Request a quorum proof chain from a peer. The serving node derives the checkpoint height and active chainlock quorums from the checkpoint block hash. | Field | Type | Size | Description | | ----- | ---- | ---- | ----------- | | checkpointBlockHash | uint256 | 32 | Block hash of the client's checkpoint | -| checkpointHeight | uint32_t | 4 | Height of the checkpoint block | -| checkpointQuorumCount | compactSize uint | 1-9 | Number of known chainlock quorums | -| checkpointQuorums | QuorumEntry[] | variable | Known chainlock quorum entries from checkpoint | | targetQuorumHash | uint256 | 32 | Hash of the target quorum to prove | | targetQuorumType | uint8_t | 1 | LLMQ type of the target quorum | @@ -417,18 +404,10 @@ service Core { returns (GetQuorumProofChainResponse); } -message QuorumEntry { - bytes quorum_hash = 1; // 32 bytes - uint32 quorum_type = 2; - bytes quorum_public_key = 3; // 48 bytes -} - message GetQuorumProofChainRequest { bytes checkpoint_block_hash = 1; // 32 bytes - uint32 checkpoint_height = 2; - repeated QuorumEntry checkpoint_chainlock_quorums = 3; - bytes target_quorum_hash = 4; // 32 bytes - uint32 target_quorum_type = 5; + bytes target_quorum_hash = 2; // 32 bytes + uint32 target_quorum_type = 3; } message ChainlockEntry { From b82325b54ba723378e5c80e4ae1d88ffae6f0fe6 Mon Sep 17 00:00:00 2001 From: pasta Date: Tue, 8 Sep 2026 17:20:46 -0500 Subject: [PATCH 4/8] docs(dip): specify mining-only snapshot proof format --- dip-pasta-compact-quorum-proofs.md | 718 ++++++++++------------------- 1 file changed, 247 insertions(+), 471 deletions(-) diff --git a/dip-pasta-compact-quorum-proofs.md b/dip-pasta-compact-quorum-proofs.md index d11294d8..f16c16e9 100644 --- a/dip-pasta-compact-quorum-proofs.md +++ b/dip-pasta-compact-quorum-proofs.md @@ -15,534 +15,310 @@ 1. [Abstract](#abstract) 1. [Motivation](#motivation) 1. [Prior Work](#prior-work) -1. [Trusted Initial State](#trusted-initial-state) -1. [Quorum Proof Chain Data Structures](#quorum-proof-chain-data-structures) - 1. [ChainlockEntry](#chainlockentry) - 1. [QuorumCommitmentProof](#quorumcommitmentproof) - 1. [QuorumProofChainResponse](#quorumproofchainresponse) -1. [Verification Algorithm](#verification-algorithm) - 1. [Verifier State](#verifier-state) - 1. [ChainLock Verification](#chainlock-verification) - 1. [Quorum Commitment Verification](#quorum-commitment-verification) - 1. [Proof Processing Algorithm](#proof-processing-algorithm) -1. [Proof Construction](#proof-construction) - 1. [ChainLock Selection Strategy](#chainlock-selection-strategy) - 1. [Quorum Commitment Merkle Proof Construction](#quorum-commitment-merkle-proof-construction) -1. [Dash Core RPC Methods](#dash-core-rpc-methods) - 1. [getquorumproofchain](#getquorumproofchain) - 1. [verifyquorumproofchain](#verifyquorumproofchain) -1. [P2P Messages](#p2p-messages) - 1. [GETQUORUMPROOFCHAIN](#getquorumproofchain-1) - 1. [QUORUMPROOFCHAIN](#quorumproofchain) -1. [gRPC API](#grpc-api) -1. [Proof Size Analysis](#proof-size-analysis) +1. [Trust Model](#trust-model) +1. [Wire Format](#wire-format) +1. [Verification](#verification) +1. [Construction and Serving](#construction-and-serving) +1. [SDK Integration](#sdk-integration) +1. [Size and Resource Limits](#size-and-resource-limits) 1. [Security Considerations](#security-considerations) -1. [Backward Compatibility](#backward-compatibility) -1. [Reference Implementation](#reference-implementation) +1. [Compatibility](#compatibility) 1. [Copyright](#copyright) ## Abstract -This DIP defines Compact Quorum Proof Chains, a mechanism for trustlessly verifying LLMQ public keys using ChainLocks and the `merkleRootQuorums` field in coinbase transactions. This enables light clients and the Platform SDK to cryptographically verify Platform quorum public keys without trusting any external party. +This proposal authenticates current Dash Core quorum keys and EvoNode records from +an application-supplied trusted snapshot. A relay provides ordinary ChainLock +certificates, quorum mining transactions, and Merkle paths. The SDK verifies them +locally before verifying Platform responses. Relays supply evidence and do not +supply trusted keys. No consensus change, zero-knowledge system, trusted setup, or +specialized proving hardware is required. -The key insight is that a ChainLock at height H proves block H, and that block's coinbase transaction contains a `merkleRootQuorums` covering ALL currently active quorums at that height. This allows verification of any active quorum's commitment using a single chainlocked block's data, resulting in compact proofs of approximately 1 KB in typical scenarios. +The format uses only mining-transaction handoffs (previously called route B). +When the mining block lacks a usable ChainLock, a later certificate authenticates +that block through consecutive X11 headers. There is no alternate quorum-root +handoff format. ## Motivation -Platform proof verification currently requires two steps: - -1. **GroveDB Proof**: Verify data against a Merkle root (cryptographic) -2. **Tenderdash Signature**: Verify BLS signature from a Platform quorum (requires quorum public key) - -The quorum public key is currently obtained via trusted sources: - -| Method | Trust Model | -| ------ | ----------- | -| Dash Core RPC | Trust the node operator | -| TrustedHttpContextProvider | Trust centralized quorum servers | - -Both methods require trusting an external party, which undermines the trustless nature of the verification. - -This DIP enables verification of quorum public keys cryptographically using only: - -1. A hardcoded checkpoint (block hash + known quorum keys) embedded in the SDK -2. Proofs provided by any untrusted server (verified client-side) - -By leveraging the existing ChainLock infrastructure and `merkleRootQuorums` commitment in each block's coinbase, clients can build a cryptographic chain of trust from a known checkpoint to any currently active quorum. +An SDK can distribute a small, fixed Core snapshot and network addresses with its +release, then acquire the evidence needed to authenticate newer Platform quorum +keys. The intended history window is three to twelve months. The proof and the +SDK verifier both contribute to download cost, so they must be measured together. ## Prior Work -* [DIP-0002: Special Transactions](https://github.com/dashpay/dips/blob/master/dip-0002.md) -* [DIP-0004: Simplified Verification of Deterministic Masternode Lists](https://github.com/dashpay/dips/blob/master/dip-0004.md) -* [DIP-0006: Long-Living Masternode Quorums](https://github.com/dashpay/dips/blob/master/dip-0006.md) -* [DIP-0007: LLMQ Signing Requests / Sessions](https://github.com/dashpay/dips/blob/master/dip-0007.md) -* [DIP-0008: ChainLocks](https://github.com/dashpay/dips/blob/master/dip-0008.md) - -## Trusted Initial State - -Verification requires a trusted starting point embedded in client software. This checkpoint must contain: - -1. A block hash and height identifying a known-good block -2. The public keys of active ChainLock quorums at that height (identified by quorum hash and type) - -The specific serialization format of this checkpoint is an implementation detail left to client software. - -### Quorum Lifespans and Checkpoint Freshness - -The effectiveness of this verification scheme depends on overlapping quorum lifespans between the checkpoint and current chain tip. - -**Mainnet:** - -| Quorum Type | Purpose | DKG Interval | Active Count | Lifespan | -| ----------- | ------- | ------------ | ------------ | -------- | -| LLMQ_400_60 | ChainLocks | 288 blocks (~12 hours) | 4 | ~48 hours | -| LLMQ_100_67 | Platform | 24 blocks (~1 hour) | 24 | ~24 hours | - -**Testnet:** - -| Quorum Type | Purpose | DKG Interval | Active Count | Lifespan | -| ----------- | ------- | ------------ | ------------ | -------- | -| LLMQ_50_60 | ChainLocks | 288 blocks (~12 hours) | 4 | ~48 hours | -| LLMQ_25_67 | Platform | 24 blocks (~1 hour) | 24 | ~24 hours | - -With a checkpoint less than 36 hours old, at least one ChainLock quorum from the checkpoint will still be active, enabling direct verification with a single chainlock. Older checkpoints require bridging through intermediate chainlock quorums. - -## Quorum Proof Chain Data Structures - -### ChainlockEntry - -A ChainLock signature that proves a specific block is canonical. - -| Field | Type | Size | Description | -| ----- | ---- | ---- | ----------- | -| height | int32_t | 4 | Height of the chainlocked block | -| blockHash | uint256 | 32 | Hash of the chainlocked block | -| signature | BLSSig | 96 | Recovered threshold signature | -| signingQuorumHash | uint256 | 32 | Hash of the quorum that signed this chainlock | -| signingQuorumType | uint8_t | 1 | LLMQ type of the signing quorum | - -Total size: ~165 bytes - -**Note**: The signing quorum can be deterministically calculated using `SelectQuorumForSigning` given the height, but including it explicitly simplifies verification and enables the verifier to check if the signing quorum is known before attempting signature verification. - -### QuorumCommitmentProof - -A proof that a specific quorum commitment is included in a chainlocked block's `merkleRootQuorums`. - -| Field | Type | Size | Description | -| ----- | ---- | ---- | ----------- | -| commitment | CFinalCommitment | variable | The quorum final commitment (see [DIP-0006](https://github.com/dashpay/dips/blob/master/dip-0006.md)) | -| chainlockIndex | uint32_t | 4 | Index into the response's chainlocks array | -| quorumMerklePathLength | compactSize uint | 1-9 | Number of hashes in the merkle path | -| quorumMerklePath | uint256[] | 32 * length | Merkle path from commitment hash to `merkleRootQuorums` | -| coinbaseTx | CTransaction | variable | The coinbase transaction containing `merkleRootQuorums` | -| coinbaseMerkleProof | CPartialMerkleTree | variable | Merkle proof that coinbase is in the block | +[DIP-0004](dip-0004.md) commits simplified masternode lists in coinbase transactions. +[DIP-0006](dip-0006.md) defines LLMQ commitments and +[DIP-0008](dip-0008.md) defines ChainLocks. -The `CFinalCommitment` structure is defined in [DIP-0006](https://github.com/dashpay/dips/blob/master/dip-0006.md) and contains the `quorumPublicKey` that this proof ultimately verifies. +The earlier version of this proposal carried intermediate coinbases and quorum +root openings. A mining transaction already commits the complete next quorum +commitment. Authenticating that transaction avoids the intermediate coinbase and +second tree opening. Only the final coinbase remains, because it supplies the +roots needed for current quorum and masternode record openings. -Estimated size: ~700-900 bytes (varies by quorum size and tree depth) +## Trust Model -### QuorumProofChainResponse +The application independently fixes a snapshot containing: -The complete response containing all data needed to verify a target quorum. +* Network (0 mainnet, 1 testnet). +* Core height and block hash. +* Simplified masternode-list Merkle root. +* Active quorum-list Merkle root. -| Field | Type | Size | Description | -| ----- | ---- | ---- | ----------- | -| headerCount | compactSize uint | 1-9 | Number of block headers | -| headers | CBlockHeader[] | 80 * count | Block headers for each chainlock | -| chainlockCount | compactSize uint | 1-9 | Number of chainlock entries | -| chainlocks | ChainlockEntry[] | variable | ChainLock signatures in verification order | -| quorumProofCount | compactSize uint | 1-9 | Number of quorum proofs | -| quorumProofs | QuorumCommitmentProof[] | variable | Quorum commitment proofs | +A response MUST exactly match this snapshot. A relay-provided snapshot MUST NOT +be promoted to trusted configuration merely because a proof is internally valid. +A successfully verified target can serve as the next session checkpoint. -Headers and chainlocks are paired by index: `headers[i]` corresponds to `chainlocks[i]`. +The design assumes historically authenticated ChainLock quorums do not sign false +certificates, including after leaving the active set. It proves a sequence of +statements by authenticated quorum keys. It does **not** independently reconstruct +DKG, full Core consensus, or the exact active signing-quorum selection for each +certificate. Membership of a key is not proof of its current signing authority. +These are deliberate constraints of this compact certificate trust model. -## Verification Algorithm +## Wire Format -### Verifier State +All proof framing integers are unsigned little-endian fixed-width integers. +Hashes are 32 bytes in Core serialization order, reversed from RPC display hex. +Nested transactions and commitments use their existing canonical Core consensus +serialization, including CompactSize where consensus requires it. There is no +protobuf or general-purpose object encoding on the proof wire. -The verifier maintains the following state: +A `blob` is `length:u32 || bytes[length]`. A `path` is: ```text -H_verified: uint32 // Highest height cryptographically confirmed -Q_known: Map<(uint256, uint8), BLSPubKey> // Known quorum public keys - // Key: (quorumHash, quorumType) - // Value: quorumPublicKey +index:u32 | leaf_count:u32 | sibling_count:u8 | siblings[32]... ``` -Initial state is populated from the checkpoint: - -* `H_verified = checkpoint.height` -* `Q_known = checkpoint.chainlockQuorums ∪ checkpoint.platformQuorums` - -### ChainLock Verification - -To verify a ChainLock against a known quorum: - -1. Verify the signing quorum is in `Q_known` -2. Retrieve the quorum's public key from `Q_known` -3. Calculate the message hash: - `msgHash = SHA256(llmqType, quorumHash, SHA256(height), blockHash)` -4. Verify the BLS signature against the quorum public key and message hash -5. Verify that `hash(header) == blockHash` - -If verification succeeds, update `H_verified = max(H_verified, chainlock.height)`. - -### Quorum Commitment Verification - -To verify a quorum commitment proof: - -1. Verify `chainlockIndex` references a chainlock with height <= `H_verified` -2. Retrieve the corresponding coinbase transaction and block header -3. Verify the coinbase merkle proof: - * The `coinbaseMerkleProof` must prove that `coinbaseTx` is in the block - * The block's merkle root must match `header.hashMerkleRoot` -4. Extract `merkleRootQuorums` from the coinbase transaction's extra payload -5. Calculate `commitmentHash = SHA256(serialize(commitment))` -6. Verify the quorum merkle path: - * Compute the merkle root from `commitmentHash` and `quorumMerklePath` - * The computed root must match `merkleRootQuorums` -7. Verify the commitment's `quorumSig`: - * This threshold signature proves the quorum members agreed on this public key - * Verification uses the aggregated public keys from the commitment's `signers` bitvector - -If verification succeeds, add the quorum to `Q_known`: -`Q_known[(commitment.quorumHash, commitment.llmqType)] = commitment.quorumPublicKey` - -### Proof Processing Algorithm +A `certificate` is exactly 180 bytes: ```text -FUNCTION verify_quorum_proof(checkpoint, proof, targetQuorum) -> Result: - - // Initialize state from checkpoint - H_verified = checkpoint.height - Q_known = checkpoint.chainlockQuorums ∪ checkpoint.platformQuorums - - // Process proof iteratively until no more progress - LOOP: - made_progress = false - - // 1. Process chainlocks that can now be verified - FOR i, CL IN enumerate(proof.chainlocks): - IF CL.height <= H_verified: - CONTINUE // Already verified - - IF NOT Q_known.contains((CL.signingQuorumHash, CL.signingQuorumType)): - CONTINUE // Cannot verify yet - - // Verify chainlock signature - quorumPubKey = Q_known[(CL.signingQuorumHash, CL.signingQuorumType)] - msgHash = SHA256(CL.signingQuorumType, CL.signingQuorumHash, - SHA256(CL.height), CL.blockHash) - VERIFY_BLS(CL.signature, quorumPubKey, msgHash)? - - // Verify header matches chainlock - header = proof.headers[i] - ASSERT(hash(header) == CL.blockHash) - - // Extend verified horizon - H_verified = CL.height - made_progress = true - - // 2. Learn quorum commitments from verified blocks - FOR QP IN proof.quorumProofs: - clHeight = proof.chainlocks[QP.chainlockIndex].height - IF clHeight > H_verified: - CONTINUE // Chainlock not yet verified - - key = (QP.commitment.quorumHash, QP.commitment.llmqType) - IF Q_known.contains(key): - CONTINUE // Already known - - // Verify coinbase is in the chainlocked block - VERIFY_COINBASE_MERKLE_PROOF(QP.coinbaseTx, QP.coinbaseMerkleProof, - proof.headers[QP.chainlockIndex])? - - // Verify commitment is in merkleRootQuorums - merkleRootQuorums = extract_merkle_root_quorums(QP.coinbaseTx) - VERIFY_QUORUM_MERKLE_PATH(QP.commitment, QP.quorumMerklePath, - merkleRootQuorums)? - - // Verify commitment signature (proves DKG validity) - VERIFY_COMMITMENT_SIGNATURE(QP.commitment)? - - // Add to known quorums - Q_known[key] = QP.commitment.quorumPublicKey - made_progress = true - - IF NOT made_progress: - BREAK - - // 3. Return target quorum if found - targetKey = (targetQuorum.quorumHash, targetQuorum.quorumType) - RETURN Q_known.get(targetKey).ok_or(Error::InsufficientProof) +height:u32 | core_block_header[80] | Basic_BLS_signature[96] ``` -## Proof Construction - -This section describes how a node constructs proofs for requesting clients. - -### ChainLock Selection Strategy - -The goal is to find the shortest chain of chainlocks from the checkpoint to the target quorum. - -#### Case 1: Checkpoint quorum overlap exists - -When the checkpoint is less than approximately 48 hours old: - -1. Identify which checkpoint chainlock quorums are still active -2. Find the most recent chainlock signed by any of these quorums -3. This single chainlock can prove all currently active quorums - -#### Case 2: No overlap - -When the checkpoint is more than approximately 48 hours old: - -1. Identify the checkpoint chainlock quorum with the longest remaining lifespan at checkpoint time -2. Find the most recent chainlock signed by that quorum -3. Learn new chainlock quorums from that block's `merkleRootQuorums` -4. Repeat with newly learned quorums until reaching a chainlock whose block contains the target quorum - -### Quorum Commitment Merkle Proof Construction - -The `merkleRootQuorums` in each coinbase is calculated as follows (per [DIP-0004](https://github.com/dashpay/dips/blob/master/dip-0004.md)): - -1. Collect all final commitments from all active LLMQ sets at the block height -2. Calculate `hash = SHA256(serialize(commitment))` for each commitment -3. Sort hashes in ascending order -4. Calculate merkle root from the sorted list - -To construct a proof for a specific commitment: - -1. Retrieve all active commitments at the chainlock height -2. Compute all commitment hashes and sort them -3. Find the index of the target commitment's hash -4. Construct the merkle path from that index to the root - -## Dash Core RPC Methods - -Platform nodes communicate with their local Dash Core node via RPC, not P2P. The following RPC methods are added to Dash Core for proof generation and verification. Core derives the checkpoint height and active chainlock quorums from the checkpoint block hash. +The main proof is: -### getquorumproofchain - -Generates a quorum proof chain from a checkpoint to a target quorum. - -**Arguments:** - -| # | Name | Type | Description | -| - | ---- | ---- | ----------- | -| 1 | checkpointBlockHash | string | Block hash of the checkpoint (hex) | -| 2 | targetQuorumHash | string | Hash of the target quorum to prove (hex) | -| 3 | targetQuorumType | number | LLMQ type of the target quorum | - -**Result:** - -Returns the `QuorumProofChainResponse` structure with headers, chainlocks, and quorum proofs encoded as hex strings. - -### verifyquorumproofchain - -Verifies a quorum proof chain and returns the target quorum's public key if valid. - -**Arguments:** - -| # | Name | Type | Description | -| - | ---- | ---- | ----------- | -| 1 | checkpointBlockHash | string | Block hash of the checkpoint (hex) | -| 2 | proof | object | The proof chain to verify | -| 3 | targetQuorumHash | string | Hash of the target quorum (hex) | -| 4 | targetQuorumType | number | LLMQ type of the target quorum | - -**Result:** - -```json -{ - "valid": true, - "quorumPublicKey": "hexstring" +```text +magic[8] = ASCII "DASHNC02" +snapshot { + network:u8 | height:u32 | block_hash[32] + masternode_root[32] | quorum_root[32] } -``` - -Or on failure: - -```json -{ - "valid": false, - "error": "error message" +seed_commitment:blob | seed_membership:path +handoff_count:u16 +handoffs[handoff_count] { + certificate + mining_transaction:blob | transaction_membership:path + ancestor_count:u16 | ancestor_headers[80]... } +final_certificate +final_coinbase:blob | coinbase_membership:path ``` -## P2P Messages - -These messages enable SPV light clients to request quorum proofs directly from peers without requiring a local Dash Core node. Nodes supporting these messages must advertise protocol version >= XXXXXX (to be assigned). - -### GETQUORUMPROOFCHAIN - -Request a quorum proof chain from a peer. The serving node derives the checkpoint height and active chainlock quorums from the checkpoint block hash. - -| Field | Type | Size | Description | -| ----- | ---- | ---- | ----------- | -| checkpointBlockHash | uint256 | 32 | Block hash of the client's checkpoint | -| targetQuorumHash | uint256 | 32 | Hash of the target quorum to prove | -| targetQuorumType | uint8_t | 1 | LLMQ type of the target quorum | - -### QUORUMPROOFCHAIN - -Response containing the proof chain. +The seed is the complete final quorum commitment, including its vector hash and +**both** embedded signatures. Its double-SHA256 hash is opened in the snapshot's +quorum root. Embedded commitment signatures are included in the authenticated +serialization; this verifier does not re-execute their DKG validation. -The response uses the `QuorumProofChainResponse` structure defined in [Quorum Proof Chain Data Structures](#quorum-proof-chain-data-structures). +Ancestor headers are ordered oldest first: mining block, then its descendants, +ending at the certificate's parent. Zero ancestors means the certificate signs +the mining block itself. The mining height equals certificate height minus +ancestor count. There is no independent relay-selected mining height. -| Field | Type | Size | Description | -| ----- | ---- | ---- | ----------- | -| response | QuorumProofChainResponse | variable | The complete proof chain | - -## gRPC API - -DAPI exposes quorum proof functionality to remote clients via gRPC. Internally, DAPI calls the Dash Core RPC methods described above on its local Core node. - -```protobuf -service Core { - rpc GetQuorumProofChain(GetQuorumProofChainRequest) - returns (GetQuorumProofChainResponse); -} - -message GetQuorumProofChainRequest { - bytes checkpoint_block_hash = 1; // 32 bytes - bytes target_quorum_hash = 2; // 32 bytes - uint32 target_quorum_type = 3; -} +The HTTP bootstrap envelope adds authenticated consensus records: -message ChainlockEntry { - int32 height = 1; - bytes block_hash = 2; // 32 bytes - bytes signature = 3; // 96 bytes - bytes signing_quorum_hash = 4; // 32 bytes - uint32 signing_quorum_type = 5; -} - -message QuorumCommitmentProof { - bytes commitment = 1; // Serialized CFinalCommitment - uint32 chainlock_index = 2; - repeated bytes quorum_merkle_path = 3; // Each 32 bytes - bytes coinbase_tx = 4; // Serialized transaction - bytes coinbase_merkle_proof = 5; // Serialized CPartialMerkleTree -} - -message GetQuorumProofChainResponse { - repeated bytes headers = 1; // Each 80 bytes - repeated ChainlockEntry chainlocks = 2; - repeated QuorumCommitmentProof quorum_proofs = 3; +```text +proof:blob +record_count:u8 +records[record_count] { + kind:u8 # 0 quorum commitment; 1 simplified masternode entry + consensus_leaf:blob + membership:path } ``` -## Proof Size Analysis - -### Component Sizes - -| Component | Size | -| --------- | ---- | -| Block header | 80 bytes | -| ChainlockEntry | ~165 bytes | -| QuorumCommitmentProof | ~700-900 bytes | - -### Per-Chainlock Overhead - -Each chainlock in the proof requires: - -* 1 ChainlockEntry: ~165 bytes -* 1 Block header: 80 bytes -* **Subtotal: ~245 bytes** - -### Scenarios - -#### Fresh Checkpoint - -When the checkpoint is less than 36 hours old, a checkpoint chainlock quorum is still active. A single chainlock reaches the tip. +A quorum leaf is the full commitment. A masternode leaf is exactly the +`CSimplifiedMNListEntry::CalcHash` preimage, excluding the network-only version +prefix. The decoder must consume the complete supported canonical serialization; +ambiguous or unsupported masternode encodings are rejected. + +## Verification + +1. Enforce all framing limits before allocation. Reject truncation, trailing + bytes, unknown tags, and retired format identifiers. +2. Require exact equality with the application's trusted snapshot. This version + supports the Basic BLS and coinbase-v3 era after buried v20 activation on + mainnet and testnet. +3. Parse the seed commitment canonically, require a nonzero subgroup-valid public + key, and verify its membership in the snapshot quorum root. +4. For each handoff, require a strictly increasing certificate height and the + network's ChainLock quorum type (2 mainnet, 1 testnet). Verify its Basic BLS + signature using the current key and Dash's existing ChainLock request/signing + hash construction, including the certificate height, quorum identity, and X11 + block hash. +5. Starting at that signed header, check every `hashPrevBlock` against the X11 + hash of the preceding supplied header. Verify the complete mining transaction + against the oldest header's transaction root. Transaction index must be + nonzero. Require a canonical v3 quorum-commitment transaction with no inputs, + outputs, or locktime, payload version 1, and the derived mining height. +6. Parse its full non-null commitment and install the authenticated next key. + Reject a handoff to the same quorum identity. The mining block may precede + the initial snapshot; certificate heights still advance beyond it. +7. Verify the final certificate with the last key. Open transaction index zero, + parse its complete v3 coinbase, and require coinbase payload height to equal + the signed height. Extract both final roots. +8. Enforce the caller's minimum target height. Verify every requested record's + double-SHA256 leaf hash against its corresponding final root. Require the + requested Platform quorum type and hash to match the authenticated commitment. +9. Publish the new state, key, and eligible EvoNode endpoints only after the entire + envelope succeeds. Then verify the Platform response signature and GroveDB + proof before returning application data or advancing Platform freshness state. + +Merkle verification consumes exactly the tree depth implied by leaf count. An +odd final node must use its own hash as the duplicate sibling. Equal siblings at +non-duplicate positions are rejected. Transaction and record leaves of exactly +64 bytes are rejected to prevent interpreting an internal tree node as a leaf. + +## Construction and Serving + +An unpruned Core node opts in with `-quorumproofindex`. Startup scans historical +blocks to archive coinbase-carried ChainLocks and quorum mining transaction +paths. Missing history causes indexing to fail explicitly. A new database prefix +separates this index from the retired format. Disconnect handling tracks the +carrier block, preserving evidence from an earlier carrier when a later repeated +certificate disconnects. + +Construction works backwards from the requested target signer to a quorum present +in the initial snapshot. For each needed quorum, the node locates its mining +transaction and a usable certificate at or after mining. Searching nearby +certificates minimizes serialized bytes per height advanced; the search can +expand when a nearby ChainLock is unavailable. This heuristic is not part of +verification or a claim of global minimum size. Construction fails explicitly if +history, a bridge, or the resource budget is unavailable. Multiple bounded +requests can advance a checkpoint over longer gaps. + +The Core RPC is: -| Component | Count | Size Each | Total | -| --------- | ----- | --------- | ----- | -| Chainlock + header | 1 | 245 B | 245 B | -| Target quorum proof | 1 | 800 B | 800 B | -| **Total** | | | **~1 KB** | - -#### Stale Checkpoint - -When the checkpoint is 2-4 days old, checkpoint quorums have expired. Need 1-2 bridging chainlock quorums. +```text +getquorumproofchain checkpoint_hash height=0 quorum_hash="" llmq_type=0 node_count=4 +``` -| Component | Count | Size Each | Total | -| --------- | ----- | --------- | ----- | -| Chainlocks + headers | 2-3 | 245 B | 490-735 B | -| Bridge CL quorum proofs | 1-2 | 800 B | 800-1,600 B | -| Target quorum proof | 1 | 800 B | 800 B | -| **Total** | | | **~2-3 KB** | +`height=0` chooses the latest archived certificate within the search budget. A +positive height is a minimum: the node searches for a certificate at or above +both it and snapshot height plus one. `quorum_hash` and `llmq_type` request one +quorum opening; `node_count` requests zero through fifteen eligible EvoNodes. +The result contains `proof_hex`, `bootstrap_hex`, and `target`. The bootstrap +field is empty if no records were requested. Generation supports mainnet/testnet. -#### Very Old Checkpoint +```text +verifyquorumproofchain checkpoint_object proof_hex minimum_height=0 +``` -When the checkpoint is 30+ days old, approximately 15 bridging chainlock quorums are needed (one per ~2 day interval). +The verification RPC takes all independently trusted snapshot fields and returns +`valid` plus either the authenticated `target` or an `error`. It does not consult +RPC metadata to obtain trust roots. -| Component | Count | Size Each | Total | -| --------- | ----- | --------- | ----- | -| Chainlocks + headers | ~15 | 245 B | ~3,700 B | -| Bridge CL quorum proofs | ~15 | 800 B | ~12,000 B | -| Target quorum proof | 1 | 800 B | 800 B | -| **Total** | | | **~16 KB** | +DAPI and quorum servers expose the same relay interface: -### Summary +```http +POST /proofs +Content-Type: application/json -| Checkpoint Age | Proof Size | -| -------------- | ---------- | -| < 36 hours | ~1 KB | -| 2-4 days | ~2-3 KB | -| 30+ days | ~16 KB | +{"checkpoint":"","height":1549547, + "quorumHash":"","llmqType":6,"nodeCount":4} +``` -Checkpoints should be updated with each SDK release (monthly recommended) to maintain minimal proof sizes. +Success is the binary bootstrap envelope with content type +`application/octet-stream`; HTTP gzip compression is permitted. Servers bound +request sizes, concurrent Core workers, cached bytes, and cache lifetime. +A timeout does not release a worker permit while its blocking RPC is still +running. Failure returns an HTTP error, never trusted fallback keys. + +## SDK Integration + +Mainnet/testnet network builders use the verified provider by default. Release +snapshots and untrusted seed addresses are embedded. Proof sources can be seeded +EvoNodes, quorum servers, or an explicitly supplied list of either. Authenticated +EvoNode records add connection candidates; addresses themselves never confer +signing authority. + +The synchronous Platform verifier reports a typed missing-quorum condition. The +SDK asynchronously obtains a bootstrap proof, verifies it, then repeats the +complete Platform verification. This applies to reads and transaction results, +and works without synchronous network calls in browser verification code. + +Applications can explicitly select trusted mode or supply their own context +provider. Trusted mode obtains quorum keys from its configured provider and skips +the additional Core bootstrap proof download and verification; Platform response +proof verification remains separately configurable. Failed verified mode MUST NOT +silently become trusted mode. + +## Size and Resource Limits + +| Item | Maximum | +| --- | ---: | +| Decoded proof or bootstrap HTTP response | 1,048,576 bytes | +| Certificates, including final certificate | 4,096 | +| Ancestor headers across the whole proof | 4,096 | +| Merkle leaf count | 100,000 | +| Merkle siblings | 17 | +| Transaction blob | 100,000 bytes | +| Seed commitment blob | 1,024 bytes | +| Bootstrap records | 16 | +| Record leaf | 4,096 bytes | + +These limits bound individual requests, not the duration of history. Certificate +availability and quorum cadence determine achievable history per request. + +Real testnet history encoded by the mining-only reference implementation measured +85,827 / 159,536 / 314,357 gzip bytes for 90 / 180 / 366 days respectively. +The shared short cross-implementation fixture is 3,469 raw proof bytes, or 4,506 +raw bytes with one quorum and one EvoNode opening. These are testnet observations, +not mainnet measurements or worst-case guarantees. Final record openings add to +the history-only figures. HTTP compression is a transport optimization and does +not change verification. + +The release requirement is less than 500,000 additional SDK download bytes. +A standalone verifier artifact cannot establish the integrated SDK delta; compare +matching release targets and compression settings before shipping a release. ## Security Considerations -### Trust Assumptions - -| Assumption | Basis | -| ---------- | ----- | -| Checkpoint is correct | Code review, reproducible builds, distribution via official channels | -| BLS signatures unforgeable | Cryptographic hardness of BLS scheme | -| Merkle proofs sound | Collision resistance of SHA-256 | -| ChainLocks are secure | DIP-0008 security analysis | - -### Attack Resistance - -| Attack | Why It Fails | -| ------ | ------------ | -| Forge chainlock | Requires 240 of 400 masternode operator keys (60% threshold) | -| Forge quorum commitment | `quorumSig` is threshold signature requiring quorum threshold | -| Wrong block hash | Header hash must match chainlock's `blockHash` | -| Tampered coinbase | Coinbase merkle proof verification fails | -| Tampered commitment | Commitment merkle proof verification fails | -| Omit proof data | Verification fails, client retries or tries different server | - -### Failure Modes - -| Failure | Cause | Resolution | -| ------- | ----- | ---------- | -| No verifiable chainlock | Server omitted required data | Retry or try different server | -| Verification timeout | Large proof on slow device | Increase timeout or use fresher checkpoint | -| Checkpoint too old | SDK not updated in 30+ days | Ship new checkpoint in SDK update | - -## Backward Compatibility +An attacker controlling all relays can withhold evidence, replay sufficiently +recent valid evidence, or exhaust a client's bounded request budget. Successful +verification establishes authenticity under the trust model, not that the target +is the globally newest block. Platform signed-time/height freshness policy and +caller minimum heights are required. Unauthenticated metadata must not advance a +freshness ratchet. -This DIP introduces new P2P messages (`GETQUORUMPROOFCHAIN`, `QUORUMPROOFCHAIN`) and gRPC endpoints that do not affect existing functionality. Nodes that do not implement this DIP will not respond to these messages. +The design does not protect against compromise of enough historical quorum keys +to forge this certificate chain. Stronger guarantees require a stronger trust +model or additional consensus evidence and have different size costs. -Clients implementing trustless verification should fall back to trusted verification methods if: +Snapshots are release trust material. Their hashes and roots require independent +release verification and network binding. Updating a snapshot from an unverified +HTTP response defeats the design. Persistent caches, if implemented, require the +same provenance and integrity protections as their original trust configuration. -1. No peers support the new messages -2. Proof verification fails repeatedly -3. The checkpoint is too old to construct a valid proof chain +No headers are treated as authenticated merely because a matching quorum key is +known. Every accepted statement is covered by the certificate chain under the +historical quorum honesty assumption above. Full state validity and exact signer +eligibility are deliberately outside this proof's statement. -## Reference Implementation +## Compatibility -Reference implementation will be provided in: +`DASHNC02` replaces the earlier proof encoding and RPC argument layout. Old proof +bytes are rejected; there is no route-A compatibility branch. Existing Dash +blocks, commitments, signatures, and consensus rules are unchanged. This proposal +does not assign new P2P inventory types or require P2P protocol changes. -* Dash Core: Chainlock indexing and proof generation RPCs -* Platform: `rs-trustless-quorum-verifier` crate for Rust verification -* DAPI: gRPC endpoint wrapping Core RPCs +Deploy index-enabled Core and relay endpoints before distributing SDK releases +that require them by default. Explicit trusted mode remains available to callers. +Devnet/regtest require explicit trust configuration; this version does not invent +release checkpoints for those networks. ## Copyright -Copyright (c) 2026 Dash Core Group, Inc. [Licensed under the MIT License](https://opensource.org/licenses/MIT) +Copyright (c) 2026 PastaPastaPasta. Licensed under the MIT License. From 6dfb54d8cebc600cc126ec71d392b47b87c6c243 Mon Sep 17 00:00:00 2001 From: pasta Date: Tue, 8 Sep 2026 19:55:37 -0500 Subject: [PATCH 5/8] docs: describe quorum proofs without draft compatibility history --- dip-pasta-compact-quorum-proofs.md | 58 +++++++++++++----------------- 1 file changed, 24 insertions(+), 34 deletions(-) diff --git a/dip-pasta-compact-quorum-proofs.md b/dip-pasta-compact-quorum-proofs.md index f16c16e9..84a496da 100644 --- a/dip-pasta-compact-quorum-proofs.md +++ b/dip-pasta-compact-quorum-proofs.md @@ -14,7 +14,7 @@ 1. [Abstract](#abstract) 1. [Motivation](#motivation) -1. [Prior Work](#prior-work) +1. [Protocol Foundations](#protocol-foundations) 1. [Trust Model](#trust-model) 1. [Wire Format](#wire-format) 1. [Verification](#verification) @@ -22,7 +22,6 @@ 1. [SDK Integration](#sdk-integration) 1. [Size and Resource Limits](#size-and-resource-limits) 1. [Security Considerations](#security-considerations) -1. [Compatibility](#compatibility) 1. [Copyright](#copyright) ## Abstract @@ -31,13 +30,12 @@ This proposal authenticates current Dash Core quorum keys and EvoNode records fr an application-supplied trusted snapshot. A relay provides ordinary ChainLock certificates, quorum mining transactions, and Merkle paths. The SDK verifies them locally before verifying Platform responses. Relays supply evidence and do not -supply trusted keys. No consensus change, zero-knowledge system, trusted setup, or -specialized proving hardware is required. +supply trusted keys. The proof uses existing Dash blocks, commitments, signatures, +and consensus rules. -The format uses only mining-transaction handoffs (previously called route B). +Each handoff authenticates the next quorum through its mining transaction. When the mining block lacks a usable ChainLock, a later certificate authenticates -that block through consecutive X11 headers. There is no alternate quorum-root -handoff format. +that block through consecutive X11 headers. ## Motivation @@ -46,17 +44,16 @@ release, then acquire the evidence needed to authenticate newer Platform quorum keys. The intended history window is three to twelve months. The proof and the SDK verifier both contribute to download cost, so they must be measured together. -## Prior Work +## Protocol Foundations [DIP-0004](dip-0004.md) commits simplified masternode lists in coinbase transactions. [DIP-0006](dip-0006.md) defines LLMQ commitments and [DIP-0008](dip-0008.md) defines ChainLocks. -The earlier version of this proposal carried intermediate coinbases and quorum -root openings. A mining transaction already commits the complete next quorum -commitment. Authenticating that transaction avoids the intermediate coinbase and -second tree opening. Only the final coinbase remains, because it supplies the -roots needed for current quorum and masternode record openings. +A mining transaction contains the complete next quorum commitment. A handoff +authenticates that transaction with a Merkle path to a ChainLock-authenticated +block. The final coinbase supplies the quorum and masternode roots used to +authenticate the requested records. ## Trust Model @@ -71,6 +68,9 @@ A response MUST exactly match this snapshot. A relay-provided snapshot MUST NOT be promoted to trusted configuration merely because a proof is internally valid. A successfully verified target can serve as the next session checkpoint. +This proposal defines mainnet and testnet snapshots and quorum parameters. +Devnet and regtest require application-supplied trust configuration. + The design assumes historically authenticated ChainLock quorums do not sign false certificates, including after leaving the active set. It proves a sequence of statements by authenticated quorum keys. It does **not** independently reconstruct @@ -147,10 +147,10 @@ ambiguous or unsupported masternode encodings are rejected. ## Verification 1. Enforce all framing limits before allocation. Reject truncation, trailing - bytes, unknown tags, and retired format identifiers. -2. Require exact equality with the application's trusted snapshot. This version - supports the Basic BLS and coinbase-v3 era after buried v20 activation on - mainnet and testnet. + bytes, unknown tags, and a magic value other than `DASHNC02`. +2. Require exact equality with the application's trusted snapshot. The snapshot + MUST be after v20 activation on mainnet or testnet; the proof uses Basic BLS + signatures and v3 coinbase payloads. 3. Parse the seed commitment canonically, require a nonzero subgroup-valid public key, and verify its membership in the snapshot quorum root. 4. For each handoff, require a strictly increasing certificate height and the @@ -185,10 +185,9 @@ non-duplicate positions are rejected. Transaction and record leaves of exactly An unpruned Core node opts in with `-quorumproofindex`. Startup scans historical blocks to archive coinbase-carried ChainLocks and quorum mining transaction -paths. Missing history causes indexing to fail explicitly. A new database prefix -separates this index from the retired format. Disconnect handling tracks the -carrier block, preserving evidence from an earlier carrier when a later repeated -certificate disconnects. +paths. Missing history causes indexing to fail explicitly. Disconnect handling +tracks the carrier block, preserving evidence from an earlier carrier when a +later repeated certificate disconnects. Construction works backwards from the requested target signer to a quorum present in the initial snapshot. For each needed quorum, the node locates its mining @@ -244,6 +243,9 @@ EvoNodes, quorum servers, or an explicitly supplied list of either. Authenticate EvoNode records add connection candidates; addresses themselves never confer signing authority. +Verified SDK operation requires reachable proof-serving endpoints backed by +index-enabled Core nodes. + The synchronous Platform verifier reports a typed missing-quorum condition. The SDK asynchronously obtains a bootstrap proof, verifies it, then repeats the complete Platform verification. This applies to reads and transaction results, @@ -272,7 +274,7 @@ silently become trusted mode. These limits bound individual requests, not the duration of history. Certificate availability and quorum cadence determine achievable history per request. -Real testnet history encoded by the mining-only reference implementation measured +Real testnet history encoded by the reference implementation measured 85,827 / 159,536 / 314,357 gzip bytes for 90 / 180 / 366 days respectively. The shared short cross-implementation fixture is 3,469 raw proof bytes, or 4,506 raw bytes with one quorum and one EvoNode opening. These are testnet observations, @@ -307,18 +309,6 @@ known. Every accepted statement is covered by the certificate chain under the historical quorum honesty assumption above. Full state validity and exact signer eligibility are deliberately outside this proof's statement. -## Compatibility - -`DASHNC02` replaces the earlier proof encoding and RPC argument layout. Old proof -bytes are rejected; there is no route-A compatibility branch. Existing Dash -blocks, commitments, signatures, and consensus rules are unchanged. This proposal -does not assign new P2P inventory types or require P2P protocol changes. - -Deploy index-enabled Core and relay endpoints before distributing SDK releases -that require them by default. Explicit trusted mode remains available to callers. -Devnet/regtest require explicit trust configuration; this version does not invent -release checkpoints for those networks. - ## Copyright Copyright (c) 2026 PastaPastaPasta. Licensed under the MIT License. From db23bb97774e2a49ee2b7a03c56b18081dc6e30e Mon Sep 17 00:00:00 2001 From: pasta Date: Tue, 8 Sep 2026 21:04:35 -0500 Subject: [PATCH 6/8] docs: require final quorum certificate checks --- dip-pasta-compact-quorum-proofs.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/dip-pasta-compact-quorum-proofs.md b/dip-pasta-compact-quorum-proofs.md index 84a496da..9161d39a 100644 --- a/dip-pasta-compact-quorum-proofs.md +++ b/dip-pasta-compact-quorum-proofs.md @@ -68,8 +68,8 @@ A response MUST exactly match this snapshot. A relay-provided snapshot MUST NOT be promoted to trusted configuration merely because a proof is internally valid. A successfully verified target can serve as the next session checkpoint. -This proposal defines mainnet and testnet snapshots and quorum parameters. -Devnet and regtest require application-supplied trust configuration. +This proposal supports mainnet and testnet only. Devnet and regtest are outside +its scope. The design assumes historically authenticated ChainLock quorums do not sign false certificates, including after leaving the active set. It proves a sequence of @@ -166,9 +166,14 @@ ambiguous or unsupported masternode encodings are rejected. 6. Parse its full non-null commitment and install the authenticated next key. Reject a handoff to the same quorum identity. The mining block may precede the initial snapshot; certificate heights still advance beyond it. -7. Verify the final certificate with the last key. Open transaction index zero, - parse its complete v3 coinbase, and require coinbase payload height to equal - the signed height. Extract both final roots. +7. Require the final certificate height to strictly exceed the last handoff's + certificate height, or the trusted snapshot height when there are no handoffs, + including when the caller's minimum target height is zero. Require the last + key's commitment to have the network's ChainLock quorum type (2 mainnet, + 1 testnet), and verify the final certificate's Basic BLS signature using that + key and the request/signing hash construction in step 4. Open transaction + index zero, parse its complete v3 coinbase, and require coinbase payload height + to equal the signed height. Extract both final roots. 8. Enforce the caller's minimum target height. Verify every requested record's double-SHA256 leaf hash against its corresponding final root. Require the requested Platform quorum type and hash to match the authenticated commitment. From 1295a0950ec0c88d555305afb47334c3ea524f81 Mon Sep 17 00:00:00 2001 From: pasta Date: Tue, 8 Sep 2026 21:10:00 -0500 Subject: [PATCH 7/8] docs(dip): describe on-demand historical proof construction --- dip-pasta-compact-quorum-proofs.md | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/dip-pasta-compact-quorum-proofs.md b/dip-pasta-compact-quorum-proofs.md index 9161d39a..c2e7a891 100644 --- a/dip-pasta-compact-quorum-proofs.md +++ b/dip-pasta-compact-quorum-proofs.md @@ -188,11 +188,21 @@ non-duplicate positions are rejected. Transaction and record leaves of exactly ## Construction and Serving -An unpruned Core node opts in with `-quorumproofindex`. Startup scans historical -blocks to archive coinbase-carried ChainLocks and quorum mining transaction -paths. Missing history causes indexing to fail explicitly. Disconnect handling -tracks the carrier block, preserving evidence from an earlier carrier when a -later repeated certificate disconnects. +Core reads historical evidence from block files on demand. ChainLock code locates +coinbase-carried certificates using exponential search followed by binary search: +consensus requires the certified height to never decrease, and a non-null +certificate cannot be followed by a null one. The existing mined-commitment +database identifies quorum mining blocks; their transactions and Merkle paths +are read and constructed as needed. No additional persistent index or startup +scan is required. A bounded certificate cache lasts for one RPC request. + +Construction uses a fixed view of the active chain. Disk reads and proof +verification run outside the main chain lock; existing quorum database queries +take short locks. Before returning, Core checks that the target certificate's +carrier remains on the active chain. A conflicting reorganization requires a +retry. Missing or pruned block data causes an explicit error rather than being +interpreted as an absent certificate. Proof-serving nodes should retain the +historical blocks needed by their supported checkpoints. Construction works backwards from the requested target signer to a quorum present in the initial snapshot. For each needed quorum, the node locates its mining @@ -209,7 +219,7 @@ The Core RPC is: getquorumproofchain checkpoint_hash height=0 quorum_hash="" llmq_type=0 node_count=4 ``` -`height=0` chooses the latest archived certificate within the search budget. A +`height=0` chooses the certificate carried by the current chain tip. A positive height is a minimum: the node searches for a certificate at or above both it and snapshot height plus one. `quorum_hash` and `llmq_type` request one quorum opening; `node_count` requests zero through fifteen eligible EvoNodes. From b24d999760789bfe971ddaa896d309f0ea866da4 Mon Sep 17 00:00:00 2001 From: pasta Date: Wed, 9 Sep 2026 16:45:36 -0500 Subject: [PATCH 8/8] docs: clarify compact proof construction semantics --- dip-pasta-compact-quorum-proofs.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/dip-pasta-compact-quorum-proofs.md b/dip-pasta-compact-quorum-proofs.md index c2e7a891..b4eaa2ad 100644 --- a/dip-pasta-compact-quorum-proofs.md +++ b/dip-pasta-compact-quorum-proofs.md @@ -164,8 +164,10 @@ ambiguous or unsupported masternode encodings are rejected. nonzero. Require a canonical v3 quorum-commitment transaction with no inputs, outputs, or locktime, payload version 1, and the derived mining height. 6. Parse its full non-null commitment and install the authenticated next key. - Reject a handoff to the same quorum identity. The mining block may precede - the initial snapshot; certificate heights still advance beyond it. + Reject a handoff to the same quorum identity. A verifier may accept a mining + block that precedes the initial snapshot, provided the certificate height still + advances beyond it. Core proof construction may impose a narrower search window + when selecting bridges; that is a serving limitation, not a wire-format rule. 7. Require the final certificate height to strictly exceed the last handoff's certificate height, or the trusted snapshot height when there are no handoffs, including when the caller's minimum target height is zero. Require the last @@ -219,8 +221,10 @@ The Core RPC is: getquorumproofchain checkpoint_hash height=0 quorum_hash="" llmq_type=0 node_count=4 ``` -`height=0` chooses the certificate carried by the current chain tip. A -positive height is a minimum: the node searches for a certificate at or above +`height=0` chooses the latest usable ChainLock at the current chain tip. The +signature may already be known to the live ChainLock manager before a later +coinbase carries it. A positive height is a minimum: the node searches for a +certificate at or above both it and snapshot height plus one. `quorum_hash` and `llmq_type` request one quorum opening; `node_count` requests zero through fifteen eligible EvoNodes. The result contains `proof_hex`, `bootstrap_hex`, and `target`. The bootstrap