Continuity Chain Validation

Chain validation confirms the receipt sequence is complete, properly ordered, and tamper-free. This page covers the validation algorithm and how to handle anomalies.

What is the validation algorithm?

Chain validation follows a strict sequence. Each check must pass before proceeding.

function validateChain(receipts, chain_head):
  if receipts.length == 0:
    return FAIL("EMPTY_CHAIN")

  // Sort by counter
  receipts.sortBy(r => r.counter)

  // Check first receipt
  if receipts[0].chain.prev_receipt_hash != "0".repeat(64):
    return FAIL("INVALID_GENESIS")

  prev = null
  for receipt in receipts:
    // Counter continuity
    if prev != null:
      if receipt.counter != prev.counter + 1:
        return FAIL("COUNTER_GAP", receipt.counter)

    // Hash linkage
    if prev != null:
      if receipt.chain.prev_receipt_hash != prev.chain.this_receipt_hash:
        return FAIL("CHAIN_PREV_HASH_MISMATCH", receipt.counter)

    // Signature validity
    if !verifySignature(receipt):
      return FAIL("RECEIPT_SIGNATURE_INVALID", receipt.counter)

    // Hash integrity
    if !verifyReceiptHash(receipt):
      return FAIL("RECEIPT_HASH_MISMATCH", receipt.counter)

    prev = receipt

  // Chain head verification
  last = receipts[receipts.length - 1]
  if chain_head.head_receipt_hash != last.chain.this_receipt_hash:
    return FAIL("CHAIN_HEAD_MISMATCH")

  if chain_head.receipt_count != receipts.length:
    return FAIL("RECEIPT_COUNT_MISMATCH")

  return PASS

How are gaps detected?

Gaps occur when receipts are missing from the sequence. The validator detects this through counter discontinuity.

Valid chain:     [1] → [2] → [3] → [4] → [5]
                  ✓     ✓     ✓     ✓     ✓

Gap detected:    [1] → [2] → [4] → [5]
                  ✓     ✓     ✗ COUNTER_GAP(expected: 3, got: 4)

// Gap information includes:
{
  "error": "COUNTER_GAP",
  "expected_counter": 3,
  "received_counter": 4,
  "gap_size": 1,
  "prev_receipt": "receipts/0002.json",
  "next_receipt": "receipts/0004.json"
}

What are chain forks?

A fork occurs when two valid chains diverge from a common ancestor. This can happen if the signing key creates conflicting histories.

                    ┌─▶ [3a] → [4a] → [5a]  Chain A
                    │
[1] → [2] ──────────┤
                    │
                    └─▶ [3b] → [4b]         Chain B

// Both chains are cryptographically valid
// Both have same receipts 1-2
// They diverge at receipt 3
// This indicates a compromised key or malicious boundary

How are forks detected?

Fork detection requires comparing against external anchors or checkpoint commitments:

  • Checkpoint comparison: If two bundles claim different chain states at the same checkpoint, one is forked
  • External anchor mismatch: If chain state differs from anchored commitment, fork detected
  • Same counter, different hash: Two receipts with same counter but different hashes indicate fork

How do I handle validation failures?

Each failure type has specific remediation:

COUNTER_GAP

Receipts missing. Retrieve from source if available. If not, chain custody is broken for that period.

CHAIN_PREV_HASH_MISMATCH

Receipt modified or wrong order. Re-fetch receipts. If persists, indicates tampering.

RECEIPT_SIGNATURE_INVALID

Receipt was modified after signing or wrong public key used. Critical security event—investigate immediately.

CHAIN_HEAD_MISMATCH

Bundle manifest doesn't match actual chain. Bundle may be incomplete or corrupted.

What are ordering guarantees?

The chain provides these ordering guarantees:

  • Total order: All receipts have a defined position (counter)
  • Causal order: Each receipt depends on the previous (hash link)
  • Append-only: Cannot insert in middle without breaking links
  • No reorder: Cannot swap positions without breaking signatures

Frequently asked questions

Can validation be parallelized?

Signature verification can be parallel. Hash link verification must be sequential (each depends on previous). In practice, signature verification dominates so parallelization helps significantly.

What if validation is too slow?

Use checkpoint verification. Verify from the last trusted checkpoint instead of genesis. Each checkpoint commits to chain state, reducing full-chain verification to segment verification.

How do I validate partial chains?

Partial chains can be validated with caveats. The verifier notes the starting point isn't genesis and emits PASS_WITH_CAVEATS. Useful for incremental verification of ongoing runs.