Monthly research note. Theme: Blockchain Protocols.

TL;DR

Light Clients: Trust Minimization Without Full Replication as an engineering constraint: write down assumptions, make invariants executable, and design operational recovery as part of correctness.

Key insight

Most failures are boundary failures: parsing, persistence, concurrency, retries, and upgrades.

Key takeaways

  • Upgrades must be compatibility-aware: mixed rulesets are a threat model.
  • Finality guarantees are user security guarantees—document and enforce them.
  • Topology attacks (eclipse/partition) change security outcomes; harden peer selection.
  • Prefer protocols and APIs that make invalid states hard to express.
  • Write assumptions down; treat them as interfaces.

Why this matters

  • State growth is a security problem: it impacts decentralization and verification.
  • Mempools are an attack surface: spam, pinning, and incentive manipulation.
  • Finality guarantees are user security guarantees; ambiguity is a UX vulnerability.
  • MEV turns protocol details into adversarial strategy.

Key questions

  • Where do you enforce resource limits (gas, bandwidth, storage, signature checks)?
  • What is the determinism story (byte-for-byte re-execution across platforms)?
  • What is the reorg budget for applications and how do you communicate it?
  • Which invariants need proofs (supply, balances, ordering, slashing)?
  • How do upgrades change security assumptions (fork choice, state transition rules)?
  • How do you defend against topology attacks (eclipse, partition, sybil)?

Assumptions

  • Attackers can buy bandwidth and compute; they can also bribe and censor.
  • Peers are untrusted; gossip can be manipulated for delay or isolation.
  • Nodes are heterogeneous; determinism must survive platform differences.
  • Users and apps rely on probabilistic finality until proven otherwise.

Non-goals

  • Assuming honest majority without defining the adversary’s budget.
  • Relying on client-side heuristics to paper over protocol ambiguity.
Attack surface

Negotiation and fallbacks are where security silently becomes optional—treat them as hostile.

Model & invariants

A ledger is a replicated state machine. Safety is uniqueness of finalized history:

h1,h2: Final(h1)Final(h2)h1h2  h2h1.\forall h_1,h_2:\ \mathrm{Final}(h_1)\wedge \mathrm{Final}(h_2)\Rightarrow h_1 \preceq h_2 \ \vee\ h_2 \preceq h_1.

Model the mempool as an adversarial scheduler: it chooses which work gets executed.

Explicitly model upgrade boundaries: old rules vs new rules during transition.

Invariant

Make the “impossible state” observable: a metric or alert that fires when invariants drift.

Security properties

  • Authenticity: actions are bound to identity and purpose.
  • Evidence: critical actions emit verifiable audit events.
  • Integrity: invalid transitions are rejected (and detectable).
  • Least authority: privileges are scoped by purpose and time.

Failure modes

  • Timeout ambiguity causing double-apply or partial state transitions.
  • Mixed-version behavior that violates assumptions silently.
  • Resource exhaustion (CPU/bandwidth/storage) turning into correctness failures.
  • Observability gaps during incidents (missing evidence).
Pitfall

Caches tend to become sources of truth unless you can recompute and validate them.

Design sketch

flowchart TD
  tx["Transaction"] --> mp["Mempool (admission + prioritization)"]
  mp --> prop["Block Proposal"]
  prop --> cons["Consensus / Finality"]
  cons --> exec["Deterministic Execution"]
  exec --> root["State Root Commitment"]

Implementation notes

Determinism is a boundary: every nondeterministic input is an attack surface.

Rule of thumb

Acknowledge only after durability (or make “ack” explicitly best-effort).

// Deterministic execution is a security boundary.
pub trait Executor {
  fn apply_block(&mut self, block: &[u8]) -> Result<(), String>;
  fn state_root(&self) -> [u8; 32];
}

// Avoid nondeterminism: time, RNG, unordered maps, floating-point.

Verification strategy

  • Formal invariants for supply/balance conservation where appropriate.
  • Cross-implementation tests when multiple clients exist.
  • Fuzzing transaction decoding and state transition edge cases.
  • Adversarial mempool tests: spam, pinning, worst-case signature patterns.
  • Determinism tests across architectures (x86/ARM) and OSes.

Operational notes

  • Keep execution resource limits explicit and enforced.
  • Measure invalid tx rejection reasons and rates (spam signature).
  • Protect peer tables against eclipse attempts (diversity, scoring, rotation).
  • Monitor reorg depth and frequency; treat increases as incidents.
  • Rehearse upgrades with mixed versions and rollback paths.
Operational note

Attach explicit rollout/rollback triggers to changes that touch security or correctness.

What to monitor

  • Authz failures and policy denials (unexpected spikes).
  • Retry/timeout rates by endpoint and client cohort.
  • Rollback events and the conditions that triggered them.
  • Error budget burn + tail latency under load.
  • Admission-control / rate-limit rejections (by reason).

Rollback plan

  • Define an explicit rollback trigger (metrics + thresholds).
  • Use canaries and staged rollout; stop early when signals degrade.
  • Keep dual-write / dual-verify windows where appropriate.
  • Prefer backward-compatible changes; avoid “flag day” upgrades.
  • Preserve evidence (configs, artifacts, audit logs) to reconstruct what changed.

Evidence

  • Designing Data-Intensive Applications (Kleppmann) (1) — The systems-engineering baseline for correctness, replication, and failure.
    • Evidence: Replication and consistency tradeoffs as engineering constraints; use as reference when naming guarantees.
  • Jepsen (2) — Fault injection and correctness testing for distributed systems.
    • Evidence: Turn faults into test cases; prioritize partition and clock-skew scenarios that violate user-visible guarantees.

Open questions

  • How do you communicate finality uncertainty to users without lying?
  • Which invariants should be proven vs tested vs monitored?
  • What is the worst-case work a single transaction can force?
  • Where does your implementation accidentally depend on local wall-clock time?

Checklist

  • Rollback plan rehearsed and automated.
  • Assumptions listed and reviewed.
  • Safety properties stated as invariants.
  • Failure modes enumerated with mitigations.
  • Telemetry captures correctness signals.
  • Costs bounded (CPU/memory/bandwidth) under adversarial inputs.

Further reading

1.
Kleppmann M. Designing Data-Intensive Applications [Internet]. O’Reilly Media; 2017. Available from: https://dataintensive.net/
2.
Jepsen. Jepsen: Distributed Systems Safety Analysis [Internet]. Web; Available from: https://jepsen.io/