Skip to content

Appendix D: Solutions for Chapter 39

Solutions and editorial notes for Chapter 39: Consensus and staking signatures. Compute exercises have worked solutions; open-ended exercises have an editorial note describing what a strong answer addresses.

The fuller versions of the chapter’s routines are in the consensus_staking package under solutions/ch39-consensus-staking. From a clone of the companion repository, pytest tests/ch39 runs its suite. Appendix C has the setup.

Worked solution. Normalized per-attestation-set byte budget at N=1000000N = 1\,000\,000 active validators across the five-candidate set, where the protocol must represent all NN contributions in one payload, with the multiplicative factor against the BLS aggregate baseline.

The BLS aggregate at N=1000000N = 1\,000\,000 carries the 96-byte G2G_2 aggregate signature plus the participation bitmap of N/8=125000\lceil N / 8 \rceil = 125\,000 bytes. The total is 96+125000=12509696 + 125\,000 = 125\,096 bytes. The signature is constant in NN at 96 bytes, and the bitmap grows linearly at one bit per validator. The plain post-quantum candidates produce NN independent signatures totaling N×sigbytesN \times \text{sig}_\text{bytes}. Threshold ML-DSA collapses the partial signatures to one ML-DSA-65 signature regardless of TT and ships a per-validator participation bitmap in the BLS bitmap’s format (recorded for reward attribution, not an input to verification), totaling 3309+125000=1283093\,309 + 125\,000 = 128\,309 bytes at N=1000000N = 1\,000\,000.

CandidatePer-set bytes at N=106N = 10^6Factor vs BLS
BLS-BLS12-381125 0961.00
ML-DSA-653 309 000 00026 451.69
SLH-DSA-128s7 856 000 00062 799.77
FN-DSA-512666 000 0005 323.91
threshold-ML-DSA128 3091.03

The smallest per-set byte budget is BLS-BLS12-381 at 125 096 bytes. Threshold-ML-DSA sits at 128 309 bytes, a factor of 1.03 against BLS, since the participation bitmap dominates both payloads at one million validators.

The largest per-set byte budget is SLH-DSA-128s at 7.86 GB per attestation set (1000000×7856=7.856×1091\,000\,000 \times 7856 = 7.856 \times 10^9 bytes), a factor of 62 800 against the BLS aggregate baseline. The SLH-DSA-128s factor is the load-bearing operator constraint: the conservative-assumption preference for the long-lived validator-set key forces the largest byte tax on the consensus surface.

Among the NIST-finalized candidates with no aggregation, plus FN-DSA-512 at its round-3 Falcon sizes (FIPS 206 unpublished at chain-tip 2026), ML-DSA-65 (×26452\times 26\,452) sits below SLH-DSA-128s and above FN-DSA-512. FN-DSA-512 (×5324\times 5\,324) is the smallest plain post-quantum option once FIPS 206 reaches a final standard.

Worked solution. Two SSZ designs for switching the consensus signature off BLS, sharing the same baseline Attestation container.

Baseline. Ethereum’s current Attestation SSZ container carries four fields. The aggregation_bits is a Bitlist[MAX_VALIDATORS_PER_COMMITTEE * MAX_COMMITTEES_PER_SLOT] with one bit per validator across the committees attesting at the slot. The data is a 128-byte fixed-size AttestationData record carrying the slot, the index, the beacon-block root, and the source and target checkpoints. The signature is a 96-byte BLSSignature (the canonical G2G_2 aggregate). The committee_bits is a Bitvector[MAX_COMMITTEES_PER_SLOT] marking which committees contributed.

Design 1: naive per-validator ML-DSA-65 attestation. One Attestation per signing validator, with no committee-level aggregate. The aggregation_bits field collapses to nothing (each attestation has exactly one signer). The committee_bits field collapses to a single committee_index (uint64) identifying the validator’s slot committee. A new validator_index field (uint64, 8 bytes) appears so the verifier knows which BeaconState.validators[] public key to verify against. The signature field changes from a fixed 96-byte BLSSignature to a fixed 3309-byte MLDSASignature. The per-Attestation byte budget is 128+8+8+3309=3453128 + 8 + 8 + 3309 = 3453 bytes for data + committee_index + validator_index + signature, plus a small fixed SSZ wrapping overhead. The verification rule changes from BLS pairing-based aggregate verification against the aggregated committee public key to one ML-DSA-65 verification call per attestation against the indexed validator’s public key. Each validator attests once per epoch in the Ethereum design, so the per-epoch attestation footprint sums to N×3453N \times 3453 bytes across all 32 slots. At N=106N = 10^6 the per-epoch total is 3.45×1093.45 \times 10^9 bytes (approximately 3.45 GB across the 32 blocks of one epoch), with the per-slot fraction approximately 108 MB per block. Both numbers agree with the chapter’s normalized per-validator-set byte budget at the upper-bound level.

Design 2: hypothetical threshold-ML-DSA per-committee attestation. One combined ML-DSA-65 signature per committee plus a participation bitmap that keeps the aggregation_bits format. The aggregation_bits field retains its BLS-era shape (one bit per validator in the committee), as does committee_bits. The data field is unchanged. The signature field changes from a fixed 96-byte BLSSignature to a fixed 3309-byte threshold-MLDSASignature produced by the off-chain combine protocol. The per-Attestation on-chain byte budget is 128+C/8+committee_bits+3309128 + \lceil C / 8 \rceil + \text{committee\_bits} + 3309 where CC is the committee size. For a 512-validator committee the bitmap is 64 bytes and the on-chain total is roughly 128+64+33093501128 + 64 + 3309 \approx 3501 bytes per committee aggregate, before SSZ wrapping. The verification rule changes from BLS pairing-based aggregate verification to one standard ML-DSA-65 verification call per committee against the committee’s fixed joint public key. That joint key is produced once by the threshold scheme’s distributed key generation, which also deals secret shares to the committee members (Bienstock et al., 2025). There is no per-subset key combination, and the registered joint key replaces the per-validator keys as the verification input. Verification proves that an authorized threshold of the committee signed and does not identify the cosigners, so aggregation_bits becomes a coordinator claim the signature does not bind: attributing rewards or slashing to it needs a separate accountability mechanism, such as attributable partials in the combine transcript or an accountable-threshold extension. A per-committee joint key also means one distributed key generation per committee reshuffle, an off-chain cost with no BLS analogue. The combine round runs off-chain across the participating committee members and does not ship on-chain bytes. Unlike Design 1, these figures do not reduce to the chapter’s normalized per-validator-set budget, because one signature per committee multiplies the signature term by the committee count. At N=106N = 10^6 Ethereum’s committee formula caps at 2048 committees per epoch, putting the per-epoch signature-plus-bitmap total near 2048×3309+1250006.92048 \times 3309 + 125\,000 \approx 6.9 MB beside roughly 322 KB for per-committee BLS, a factor of about 21. The chapter’s 128,309-byte, 1.03-factor row is the normalized single-payload floor, not this design’s deployment price.

Slashing-condition rewrite (both designs). Under BLS, process_attester_slashing takes two conflicting IndexedAttestation objects, checks that the two AttestationData records are slashable, validates each aggregate signature against the public keys of exactly the listed attesting indices, and slashes the still-slashable validators in the intersection of the two index lists (Ethereum Foundation, 2024). Per-validator attribution comes from that validation step, because a valid aggregate proves every listed validator signed. Design 1 keeps that shape: two conflicting ML-DSA-65 signatures from the same validator are directly checkable evidence, and the verifier set updates to call the ML-DSA-65 verifier on each candidate slashing report. Design 2 cannot port the rule, because the committee’s joint-key signature proves an authorized threshold signed without identifying cosigners and the bitmap beside it is a coordinator claim the signature does not bind, so a pair of conflicting committee signatures attributes the conflict to no validator. Validator-level attester slashing under Design 2 therefore needs cryptographically attributable evidence, such as attributable partials from the combine transcript or an accountable-threshold extension, and a deployable design must state what evidence a slashing report carries. The BeaconState’s slashing-tracking fields stay at the same byte size (slashing tracks validator indices, not signature bytes). Threshold-ML-DSA also requires one further slashing rule for combine-round equivocation (a cosigner who contributes inconsistent partials across two combines for the same slot), which has no BLS analogue. The evidence for it lives in the off-chain combine transcript rather than in any on-chain signature.

Editorial note. A defensible answer applies the Ch 36 surface taxonomy to a proof-of-authority chain with a fixed validator set of 21 validators.

The proof-of-authority chain has the same five surfaces as the proof-of-stake setting: transaction signatures, consensus signatures, wallet-and-key-rotation signatures, the on-chain verifier, and governance signatures. Three differ in shape from the Ethereum proof-of-stake setting.

The consensus surface is small and fixed. With 21 validators, the per-attestation-set byte budget under any post-quantum candidate stays in the kilobyte range. ML-DSA-65 at N=21N = 21 produces 21×3309=6948921 \times 3309 = 69\,489 bytes per attestation set. That is larger than the 21-validator BLS aggregate (96+3=9996 + 3 = 99 bytes) by a factor of about 700, and smaller than the 125 KB BLS aggregate at one million validators in absolute terms. SLH-DSA-128s produces 21×7856=16497621 \times 7856 = 164\,976 bytes (under 165 KB). The operator can afford the conservative-assumption preference (SLH-DSA-128s) without paying the byte tax that breaks the design at one million validators.

The governance surface differs in shape because the validator set is fixed and rotated by off-chain coordination rather than by on-chain churn. A proof-of-authority chain typically uses a multisig or a similar M-of-N construction over the validator set’s identities. The chapter’s matrix on the governance surface (out of scope for Ch 39, covered in Ch 41) admits SLH-DSA-128s or threshold-ML-DSA without on-chain aggregation pressure.

The wallet surface (Ch 38) is unchanged. Validators on a proof-of-authority chain hold per-validator signing keys with the same custody-shape considerations as proof-of-stake validators. A defensible candidate for the consensus surface on a 21-validator proof-of-authority chain is SLH-DSA-128s, picking the conservative-assumption preference because the small validator count makes the byte budget non-binding.

Worked solution. Strand validator-key rotation cadence under Z=9Z = 9 given 100 thousand active validators, 5 minutes per rotation, and a Strand-specific churn ceiling of 8 validators per epoch (twice the 4 validators per epoch that Ethereum’s 128 ETH Electra churn floor permits at this validator count and balance).

The Strand consensus surface from Ch 36 has X=2X = 2 and Y=1Y = 1. Under Z=9Z = 9 the signed margin is X+YZ=2+19=6X + Y - Z = 2 + 1 - 9 = -6 years (cleared by six, with safe window 8 years).

Under no breach, no emergency validator signing-key rotation is required. The operator continues normal validator lifecycle management (activations and exits through Strand’s churn-limited queues) and prepares for the post-quantum registration window introduced by the migration fork. Under today’s Ethereum-style protocol, a forced signing-key rotation in place requires exit and re-entry rather than a single in-protocol key-change call. The chapter notes this explicitly.

If the operator nevertheless drives a full-set rotation through exit-and-redeposit at Strand’s 8-validators-per-epoch churn ceiling, the minimum chain-level wall-clock time is the following.

epochs to rotate all=100000 validators8 exits per epoch=12500 epochs.\text{epochs to rotate all} = \frac{100\,000 \text{ validators}}{8 \text{ exits per epoch}} = 12\,500 \text{ epochs.}

Each epoch is 32 slots of 12 seconds, totaling 384 seconds or 6.4 minutes. Total wall-clock time:

12500 epochs×6.4 minutes per epoch=80000 minutes55.6 days.12\,500 \text{ epochs} \times 6.4 \text{ minutes per epoch} = 80\,000 \text{ minutes} \approx 55.6 \text{ days.}

The 5-minute-per-rotation operational cost does not bind because the churn ceiling caps the rate at 8 validators per epoch regardless of operator capacity. With 8 validators per epoch at 5 minutes of serial work each, the 40 minutes of total work does not fit within the 6.4-minute epoch on a single operator thread. The operator runs 8 rotations in parallel during the epoch window, so each rotation completes inside its 5-minute serial budget and the epoch’s 8 rotations finish before the next epoch begins. Activation-queue delay on the re-entry side adds further wall-clock time not modeled here.

Comparison against the chapter framing: under Z=9Z = 9 no emergency rotation is required, so the 55.6-day figure is the minimum chain-level wall-clock for a voluntary exit-and-redeposit drive rather than a Mosca-driven obligation. The 55.6-day drive sits well inside the safe window of 8 years, so even a voluntary drive (or natural validator-set attrition over months) clears the Mosca window without operator intervention.

Note on Ethereum mainnet defaults: Ethereum’s 128 ETH Electra churn floor, which is 4 validators per epoch at 32 ETH each, would extend the figure to roughly 111 days at the same 100 thousand validator count. Strand uses 8 here as a chain-specific ceiling for the exercise.

A more aggressive cadence (an explicit rotation drive on top of natural attrition) does not change the breach decision. The margin is already 6-6 years, cleared by six. The over-provisioned safe window is reserved for tighter ZZ scenarios, not for tightening the cadence under the stated Z=9Z = 9.

Editorial note. A defensible mapping pairs each consensus-migration phase with a Ch 30 program phase and names the validator-coordinator work-stream owner per the same Ch 30 framework.

The consensus migration sits in discovery during the validator-key registration window. Validators register a post-quantum public key alongside their existing BLS public key. The registration runs through the Ethereum consensus-client team’s release schedule. The Ch 30 work-stream owner is the consensus-client team for each major Ethereum consensus client (Lighthouse, Prysm, Teku, Nimbus, Lodestar). The work-stream output is a per-client release with the post-quantum key-registration RPC and the per-validator candidate-key state.

It moves to first-wave as early-adopter validators register post-quantum keys and run the post-quantum verifier against test attestations. The Ch 30 work-stream owner is the validator-set operator (staking pool, solo validator, custody platform) running the candidate-key registration. The first-wave window measures the operational footprint of the post-quantum signature on validator infrastructure: signing throughput, key storage, and the per-attestation byte budget against the validator’s bandwidth.

It shifts to broad-rollout at the mainnet activation block. The chain governance forum coordinates the activation block per the Ethereum All Core Devs forum (Ch 41). The Ch 30 work-stream owner is the chain governance forum for the activation-block decision and the validator-set operator for the per-validator switchover. The slashing-monitor service watches the activation-block transition for an equivocation event in which a validator signs a BeaconBlock under both BLS and the post-quantum candidate at the activation slot. The cutover specification must define whether such a dual-signing event is slashable (the validator was supposed to switch atomically) or grace-allowed (the dual sign is a transitional artifact rather than a malicious double-vote).

It closes in end-of-migration with the BLS-key registration window closing for any new validators. The Ch 30 work-stream owner is the chain governance forum for the deprecation rule and the consensus-client team for the deprecation-block enforcement. The end-of-migration trigger is a stated deprecation block on Ethereum mainnet. After that block, validators with only a BLS key can no longer produce valid attestations and accrue inactivity penalties (and the inactivity leak if finality stalls). Inactivity is not a slashing offense, so the deprecation rule needs no new slashing condition.

What gates the broad-rollout tempo depends on which mechanism the answer assumes, and a strong answer says which. The chain’s hard-fork cadence is not the constraint either way: the All Core Devs forum can activate a hard fork in a coordinated single block. Under today’s protocol there is no in-place signing-key change, so a switchover is exit and re-entry and inherits the activation and exit churn limits, at most 256 ETH per epoch under Electra. Under the future fork this chapter models, a validator registers a post-quantum key alongside its existing BLS key and never leaves the set, so the pace is whatever registration rate that fork specifies and whatever the operators can staff. Neither of those is attrition. The chapter is explicit that the churn cap is a ceiling on turnover rather than a source of it: a validator may stay active indefinitely, committee reshuffling does not rotate its BLS key, and no half-life follows from the cap. Staging a registration over several epochs is a schedule, not a turnover rate. Whether the consensus surface runs longer than the transaction surface from Ch 37 therefore follows from the mechanism chosen and the hardware and staffing behind it, not from the churn limit.

Bienstock, A., de Castro, L., Escudero, D., Polychroniadou, A., & Takahashi, A. (2025). Quorus: Efficient, Scalable Threshold ML-DSA Signatures from MPC. IACR Cryptology ePrint Archive, Paper 2025/1163. https://eprint.iacr.org/2025/1163
Ethereum Foundation. (2024). Ethereum Consensus Specifications. GitHub repository at ethereum/consensus-specs. https://github.com/ethereum/consensus-specs