July 02, 2026
We present Cadence, a Byzantine fault-tolerant multi-proposer consensus protocol with arbitrarily low block intervals, optimal resilience, and optimal fast-path latency. Cadence divides time into equally spaced slots, one block per slot, each finalized in its own consensus instance. Blocks do not build directly on their predecessor, which lets these instances run independently, so none waits for an earlier block to finish or even to propagate over the network; we call this extreme pipelining, and it decouples the block interval from network latency. Cadence also removes the single-leader monopoly over transaction inclusion and ordering: under multiple concurrent proposers (MCP), several validators propose for each block, and it guarantees that, under synchrony, a transaction a correct proposer includes cannot be censored or deferred to a later block (short-term censorship resistance), and that no proposer can craft its proposal in reaction to the others’ (hiding).
To realize extreme pipelining, we introduce a framework that turns any one-shot consensus meeting our slot-consensus specification into a multi-shot protocol with arbitrarily low block intervals. It is general and of independent interest. We instantiate it for the MCP setting with two protocols of our own: Chorus, an MCP slot consensus whose fast path finalizes a block in an optimal three communication rounds, with speculative finality one round earlier, and Conductor, an orchestrator that opens slots at an even cadence in normal operation, and more slowly under asynchrony to keep the number of open slots bounded. To our knowledge, Cadence is the first MCP protocol to provide short-term censorship resistance and hiding at the fast-path latency of single-leader consensus. We prove safety, liveness, short-term censorship resistance, and hiding under partial synchrony with optimal resilience (\(n = 3f+1\)). Beyond the theory, we address the practical considerations of deploying Cadence and evaluate its latency in simulation: over Monad mainnet’s \(200\) globally distributed validators with five concurrent proposers per slot, the finalization latency averages \(219\) ms (\(167\) ms to speculative finality), and at a \(100\) ms block interval, a transaction waits on average only \(50\) ms to enter a proposal.
Blockchains increasingly host real-time financial markets, and serving them well places three requirements on the consensus layer. We motivate each, then present our design.
Requirement 1: no proposer monopoly. Currently, most deployed protocols use a rotating leader schedule, where there is a uniquely identified entity (the leader) with the short-lived outsized ability to control the inclusion, exclusion and ordering of transactions.2 Recent proposals have emerged to induce competition among the parties that contribute to a block, in what are called multiple concurrent proposer (MCP) designs. There, a proposer schedule rotates the role over time, much as a leader schedule does, but names a more diverse, small set of validators per block who are allowed to simultaneously make their proposals. A combination arising from these different simultaneous proposals ends up forming the final block. To this end, a number of properties form axes along which such designs can be evaluated: Short-term censorship resistance [1] guarantees that a correct proposer’s proposal is included in the block it contributes to, not dropped or deferred to a later block; so a transaction that reaches a correct proposer is included without delay, as long as the proposer has space for it. Hiding [1] keeps each proposal concealed from the other proposers until it is too late for them to react with a proposal of their own. In this way, a proposer cannot condition their proposal’s contents on the other ones, and proposals are truly simultaneous.3
Requirement 2: low latency. On-chain applications such as trading demand low latency. A transaction passes through several stages, each adding latency: the time to reach a proposer, the wait to enter a proposal (on average half the block interval), the propagation of that proposal across the network, the consensus voting rounds needed to reach finality (whether speculative or full), and finally the execution that produces the result the application observes.
Requirement 3: short economic ticks. An economic tick is the shortest time to inclusion of a transaction in any proposal. Since transactions might provide—among others—price updates, finer granularity in transaction inclusion enables more frequent state updating; for example, it might allow reducing arbitrage profits in decentralized exchanges. [2]
Most recent MCP designs [1], [3] run a separate proposal-and-aggregation phase before the consensus: each proposal is first attested by a quorum, and these attestations limit the discretion of a single leader who assembles the block for an off-the-shelf consensus. This added phase costs two extra communication rounds, sacrificing the desideratum of low latency. Our proposal aims to avoid that cost by incorporating the multiple proposers directly into the consensus.
We present Cadence, an MCP Byzantine fault-tolerant protocol that runs among \(n = 3f + 1\) validators, up to \(f\) of which may be Byzantine, under partial synchrony. Using synchronized clocks, Cadence divides time into slots, each contributing exactly one block to the ledger in order of their slots, and combines two components, illustrated in 1: Chorus, a single-shot MCP consensus run for each slot, and Conductor, which schedules the slots, normally at a regular interval \(\tau\) (the block interval). Within a slot, multiple proposers contribute concurrently, as in any MCP protocol; Cadence additionally produces blocks concurrently across slots, each decided by its own independent single-shot consensus with no chaining between them, so the slots overlap. We call the latter extreme pipelining. The protocol also provides short-term censorship resistance and hiding, the latter by threshold-encrypting each proposal and releasing the decryption shares at the deadline. On the fast path, it finalizes a slot in an optimal three communication rounds, even when some proposers are offline. A slot reaches speculative finality one round earlier, revertible only under provable equivocation. We target blockchains with asynchronous execution [4], where validators commit to digests of proposals, each a list of transactions; invalid proposals and transactions are discarded deterministically afterward, when the committed data is turned into an execution block, the ordered list of transactions to be executed.
Together, these features let Cadence meet all three requirements. Its multiple concurrent proposers remove the single-proposer monopoly, and short-term censorship resistance and hiding keep any one proposer from suppressing a transaction or reacting to the others’ proposals (Requirement 1). For low latency (Requirement 2), Cadence shortens several of the stages a transaction passes through: having many proposers means one is likely near each user, shortening the hop to a proposer; its arbitrarily low block intervals shorten the wait to enter a proposal; and its fast path reaches (speculative) finality with optimal latency. Execution results are certified separately from the main protocol, as soon as they are available (6.3). Those same low block intervals keep economic ticks short, since transactions enter proposals frequently (Requirement 3).
Five ideas underlie our design.
Idea 1: extreme pipelining. We make every slot an independent single-shot consensus instance, scheduled by Conductor using synchronized clocks. Slots do not chain4: no certificate or artifact from an earlier slot is needed for a slot to start, and every opened slot finalizes exactly one block. The usual way to shrink the block interval is pipelining, overlapping the consensus of consecutive blocks; but traditional pipelining chains each block to its predecessor, so the next cannot begin until the previous proposal has propagated, leaving the interval floored at the network delay \(\Delta\). Removing this dependency on the previous block lets the block interval drop below \(\Delta\), and because block production normally does not wait on any slot to finish, a slow or stalled slot does not hold up the rest. Traditional pipelining complicates protocols, in particular with multiple proposers.5 Extreme pipelining, however, makes the per-slot consensus simpler: each Chorus instance is single-shot rather than multi-shot, reasoning about only one block at a time.
Idea 2: deadlines rather than start times. Waiting for all proposers would let the slowest one set the pace. In most major proof-of-stake blockchains, stake concentrates in a few mutually well-connected regions, with a long tail of distant validators; with many concurrent proposers, at least one is likely to be drawn from that tail. In Cadence, rather than coordinating on when proposers start, validators rely on a synchronized clock to fix, for each slot, a common deadline by which its proposals must have arrived: a fixed point in time, the same for every validator and, under steady operation, known in advance. Each proposer chooses its own broadcast time to meet this deadline, according to its own network conditions: later when the network is fast, earlier when it detects delays. A shared start time would require a common timeout that accommodates the slowest proposer. A shared deadline instead fixes when proposals must arrive, so that the slowest proposer no longer sets the pace for proposal dissemination.
Idea 3: a three-round fast path that tolerates offline proposers. The simplest fast path would finalize a slot only when all of its proposers are online and correct. But in that case, a single missing proposer would make the fast path unavailable. Instead, in our protocol, validators judge each proposer separately at the deadline, voting yes or no on whether its proposal arrived. Once a quorum forms for each proposer, one way or the other, validators cast commit votes, and a quorum of commit votes finalizes the slot, two rounds after the deadline. This is the good case, where every proposer is either online and correct, or offline. Neither voting round uses a timeout, so a slot finalizes as fast as its votes propagate. The fast path is unavailable if some proposal reaches only some correct validators but not others (partial dissemination) or if a proposer sends conflicting proposals (equivocation). In that case, Chorus finalizes through a slower fallback path, which relies on an off-the-shelf agreement protocol.
Idea 4: agreement on digests, proposal dissemination in parallel. Following DispersedSimplex [5], validators agree on Merkle roots of encodings of the proposals, not on the proposals themselves. Traditionally, consensus disseminates the data before voting on it, which adds latency, in particular for large payloads. Because asynchronous execution lets validators vote on digests rather than on the payload, dissemination can instead run in parallel with consensus. A committed Merkle root comes with an explicit guarantee: its data can be recovered, or, if invalid, deterministically rejected by all honest validators.
Idea 5: bounding open slots under asynchrony. Conductor opens slots at a regular cadence, one every \(\tau\), without waiting to see how the open ones fare; since each slot runs independently, many can be open at once. Under asynchrony, slot production must be throttled when finalization stalls, or the open slots would accumulate without bound; Conductor therefore groups slots into fixed-size windows and opens a new window only once enough slots of the earlier windows have finalized, keeping the number of open slots bounded (boundedness). Once the network stabilizes, it returns to a steady cadence, with consecutive deadlines spaced \(\tau\) apart and known at least \(\Delta\) in advance (recovery).
Underlying Chorus and Conductor is our generic extreme-pipelining framework that composes any slot consensus, responsible for everything within a slot, with any orchestrator, responsible for scheduling the slots. The design is modular: either component can be swapped out without affecting the other. For example, instantiating the orchestrator with one that sets the block interval \(\tau\) adaptively does not affect the slot consensus. Conversely, instantiating the slot consensus with a \(5f{+}1\) protocol reduces the fast-path latency at the cost of resilience, while leaving the slot scheduling unchanged. The framework is not specific to MCP, though our correctness proof is specific to it: we show that any conforming pair solves the MCP problem (11).
We intend Cadence as a deployable protocol, not only a theoretical one: we show how to deploy it as part of a blockchain (6) and work through several further practical considerations (7). We also evaluate its network latency in simulation, using estimated delays between Monad mainnet’s \(200\) globally distributed validators, with five proposers per slot: at a block interval of \(100\) ms, the end-to-end latency, the sum of the inclusion and finalization latencies, averages about \(269\) ms, or \(217\) ms to speculative finality (8).
Our contributions are: (i) the extreme-pipelining framework, comprising the slot-consensus and orchestrator abstractions and a proof that any pair of conforming instantiations solves the MCP problem; (ii) Cadence, our instantiation of the framework, combining Chorus, an MCP slot consensus with a three-round fast path at optimal resilience, with Conductor, an orchestrator that schedules the slots at a regular cadence in normal operation and slows block production under asynchrony; and (iii) the practical considerations for deploying Cadence as a real blockchain rather than a purely theoretical protocol.
The rest of this paper is organized as follows. We present the problem and our design informally: the MCP problem (2), our extreme-pipelining framework (3) and its components in Cadence: Chorus(4) and Conductor(5). We then discuss deploying Cadence as part of a blockchain (6) and further practical considerations (7), evaluate its latency (8), and review related work (9). The appendix gives the formal treatment: the formal problem definition (10), the Cadence framework and its correctness (11), Chorus(12), and Conductor(13).
We begin with a description of the problem we aim to solve.
We consider a set of \(n\) validators, each of which maintains its own append-only ledger: an ordered sequence of blocks, where each block bundles transactions submitted by end users. The goal is for these ledgers to remain mutually consistent and to keep growing, so that, to the end users, they appear as a single, ever-growing ledger. We work in the standard Byzantine setting with \(n = 3f + 1\) validators, of which up to \(f > 0\) may be faulty and behave arbitrarily (Byzantine), while the rest are correct (or honest). We assume partial synchrony: the network may behave asynchronously, delaying messages arbitrarily, up to some unknown moment called the global stabilization time (\(\mathrm{GST}\)), after which it stabilizes and delivers messages within a known bound \(\Delta\). Throughout, \(\delta \leq \Delta\) denotes the actual network delay after \(\mathrm{GST}\). We further assume that validators have synchronized clocks, giving a global notion of time shared by all validators. Finally, we assume that every correct validator begins executing at global time \(0\).
Each block belongs to a slot: slots are numbered by the positive integers, validators process them in increasing order, and each slot contributes exactly one block to the ledger. A slot also has two associated attributes:
a fixed set of \(k\) proposers, the validators entitled to propose that slot’s contents;
a deadline (a point in global time), the cut-off by which proposers must ensure that validators have received sufficient information about their proposals for casting votes; the deadline is not hardcoded, but set by the protocol itself.6
The defining feature of the MCP problem, and what sets it apart from classical single-leader blockchains, is precisely that a slot may have several proposers at once, rather than one: each proposer contributes its own proposal, and the slot’s block should reflect the contributions of all of them. The validators must keep agreeing on, and extending, a ledger of such blocks despite the Byzantine faults and network asynchrony.
Our MCP protocol must provide the following guarantees, closely following those introduced by Garimidi et al. [1]. Two of them — censorship resistance and eventual stability — take effect only once the network has stabilized: there is a grace period \(\mathcal{G}\) such that both hold from time \(\mathrm{GST} + \mathcal{G}\) onwards.
Safety. The ledgers of any two honest validators are always consistent, i.e., at any point in time, one is a prefix of the other, so the ledgers never fork.
Liveness. Every slot eventually contributes a block to every honest validator’s ledger, so no slot stalls forever and the ledgers keep growing.
Short-term censorship resistance. An honest proposer’s proposal cannot be suppressed after the grace period: for every slot whose starting time is at least \(\mathrm{GST} + \mathcal{G}\), the proposals of all honest proposers become part of that slot’s block. Note that this notion is stronger than the usual notion of censorship resistance, which only requires that proposed transactions are eventually included in the ledger.7
Hiding. A faulty proposer cannot tailor its proposal to the honest proposers’ proposals for the same slot: an honest proposer’s proposal stays concealed until it is too late for a faulty proposer to build a new proposal and still have it included in that slot’s block.
Eventual stability. From time \(\mathrm{GST} + \mathcal{G}\) onwards, the slots come “without gaps”: successive slots follow one another promptly, with no undue gaps between their deadlines.
2 illustrates the problem together with these five guarantees.
The first two guarantees, safety and liveness, are the classical guarantees expected of any blockchain protocol: the validators’ ledgers must remain consistent, and they must keep growing. The other two, short-term censorship resistance and hiding, are specific to the economic nature of blockchains. Recall that the main goal of having several concurrent proposers is economic in the first place: the right to propose is spread across many parties precisely so that no single party controls what enters a block and profits from that control. This entails that no honest proposer can be silenced even for a single block (otherwise its contribution could simply be dropped). Furthermore, no faulty proposer can peek at others’ proposals before committing to its own (otherwise it could profit by reacting to what it sees). Censorship resistance guarantees the former, and hiding the latter. One timing aspect worth emphasizing: censorship resistance is required to hold not immediately at \(\mathrm{GST}\), but only after a grace period \(\mathcal{G}\) following it. In other words, slots whose starting time is within the grace period need not enjoy censorship resistance.
This grace period is not incidental : our formal definitions (10) parametrize the guarantees by its length, and the shorter the \(\mathcal{G}\) a protocol achieves, the sooner its guarantees kick in after network asynchrony — a direct measure of the protocol’s quality.
Hiding, in turn, rules out the adverse selection this peeking would otherwise enable: a Byzantine proposer that could see an honest proposal would react to the transactions it carries only when doing so is profitable, capturing the gains for itself and leaving their senders the losses. Since the adversary never sees an honest proposal in time, it cannot react selectively in this way. We note that our hiding property slightly relaxes that of Garimidi et al. [1] while still capturing its purpose: there, a proposal must remain concealed until the ledger up to and including its slot is irrevocably committed; we require concealment only until it is too late for a faulty proposer to react to the honest proposal.
The last guarantee, eventual stability, concerns the slot schedule and is particularly applicable when performing extreme pipelining across slots. Intuitively, after the grace period \(\mathcal{G}\), the proposals for slots should be created at a steady pace: there should be no undue gaps between slots, which gives the protocol a steady high-frequency “economic tick”. In Cadence, we make this intuition concrete by requiring that the slot deadlines eventually become \(\tau\)-spaced, for a fixed duration \(\tau\): from some point on, the deadlines of consecutive slots are separated by exactly \(\tau\).
We now describe our extreme-pipelining framework,8 which allows for producing blocks at an arbitrarily high rate, no matter how long any individual block takes to propagate or finalize.
The framework rests on splitting the problem into two largely independent concerns, each addressed by its own abstract building block. The first concern is what happens within a single slot: how the proposers of a slot get their proposals into that slot’s block, and how all validators come to agree on that block. The second concern is what happens across slots: how fast the slots follow one another (how we set their deadlines), and how many slots validators may have underway at once. We capture the first concern in a primitive we call slot consensus, and the second in a primitive we call the orchestrator. The framework itself is then little more than the glue that wires these two primitives together: it runs a single orchestrator and one instance of slot consensus per slot. This decomposition keeps the design modular and general: each primitive can be designed, analyzed, and swapped out on its own, and any valid pair of primitives composes into a correct MCP protocol. Cadence, our concrete MCP protocol, is this framework instantiated with concrete protocols for the two primitives: Chorus as the slot consensus and Conductor as the orchestrator.
At its core, slot consensus is a one-shot consensus primitive, responsible for a single slot: it has all honest validators agree on a single finalized block for the slot. It is also where the MCP-specific work occurs: slot consensus gathers the slot proposers’ proposals and enforces the two economic guarantees for that slot.
The interface of slot consensus is simple: the slot’s proposers \(\mathsf{propose}\) their proposals as input, and each validator may eventually \(\mathsf{finalize}\) a block for the slot as output. Roughly speaking, slot consensus is required to satisfy the following four guarantees:
Agreement. Honest validators never finalize conflicting blocks for the slot.
Termination. Every honest validator eventually finalizes a block for the slot.
Proposal inclusion. If the slot’s starting time (the slot’s deadline minus \(\Delta\)) is past \(\mathrm{GST}\), the finalized block contains the proposals of all honest proposers that proposed by the slot’s starting time.
Hiding. The proposals stay concealed until it is too late for a faulty proposer to react to them: it is impossible for a faulty proposer to first learn the honest proposals and then, equipped with that knowledge, craft a new proposal of its own that still gets included in the finalized block.
These four guarantees should already look familiar: they mirror, at the level of a single slot, every global MCP guarantee except eventual stability, and they are precisely what we will later rely on to argue that our framework solves the MCP problem. 3 collects the interface and the guarantees of slot consensus in one place.
While slot consensus looks at one slot in isolation, the orchestrator spans all of them. Its responsibility is to schedule the slots, that is, to determine their deadlines, and thereby the block interval. A smaller block interval means transactions wait less time before a slot is available to carry them, so the orchestrator directly governs how quickly a transaction can be picked up for inclusion. The slots are all the orchestrator knows about: proposals, blocks and other consensus-specific abstractions are entirely outside its view.
Like slot consensus before it, the orchestrator exposes a simple interface. As input, a validator notifies the orchestrator that a slot is \(\mathsf{complete}\), meaning that the validator’s work within that slot is done; in our framework, as we show in 3.3, this happens once the slot’s block is finalized. As output, the orchestrator \(\mathsf{schedules}\) new slots: it comes back to the validator with the deadlines of new slots. With the interface in place, we can state the two properties that the orchestrator must satisfy:
Boundedness. The orchestrator should keep validators from running arbitrarily far ahead: it imposes a fixed limit on how many slots a correct validator may have underway at any one time — slots whose deadlines it has already received but has not yet completed. It enforces this limit through its output, withholding the deadlines of new slots and throttling the pace until the validator reports that enough earlier slots are complete. Crucially, this limit need not be finite: taking it to be infinite imposes no constraint at all, recovering the most general orchestrator, which may let validators run arbitrarily far ahead. A finite limit is the stronger guarantee, and is what keeps each validator’s resource footprint in check.
Recovery. Once the network stabilizes (some grace period after \(\mathrm{GST}\)), the orchestrator must schedule the slots “properly”, and this requirement is twofold. First, the schedule must be steady: the deadlines of consecutive slots are spaced by exactly the fixed amount \(\tau\) (the same \(\tau\) with which we capture eventual stability in 2). Second, the schedule must be known in advance: every correct validator learns each deadline (i.e., receives it as the orchestrator’s output) at least \(\Delta\) time before the deadline itself. The first half is what keeps the slots coming without gaps; the second is what gives proposers room to act: in our framework, as we show in 3.3, it ensures that every honest proposer issues its proposal on time, i.e., by the slot’s starting time.
4 collects the interface and the guarantees of the orchestrator in one place.
The two properties of the orchestrator address two different regimes of the network. Boundedness is what protects the entire framework during an outage. While the network is asynchronous, slot consensus instances may be unable to finalize, since finalization requires timely message delivery [6]; in our framework, where a validator reports a slot complete exactly when its block is finalized, this means that the complete notifications dry up. If the orchestrator nevertheless kept opening fresh slots, the number of underway slots — scheduled, but not yet complete — would grow without bound for as long as the outage lasted, and with it the number of concurrently running slot consensus instances and the memory and computation each validator must devote to them. Boundedness rules this out: deprived of complete notifications, the orchestrator withholds the deadlines of new slots, so that no matter how long the network stays asynchronous, at most a bounded number of slot consensus instances ever run at once. Why do we care about bounding the number of concurrently running consensus instances at all?
The reason is memory: a bounded number of concurrent slots is necessary — though not sufficient — for bounded per-validator memory; one must also bound the memory each slot consensus instance uses (which in turn requires a primitive such as abortable broadcast [7]), and we return to it in 11.1.2.1. This lies beyond our scope.
Recovery, in contrast, is what the orchestrator owes once the network has stabilized, and its two halves serve two different ends. The first half, the steady \(\tau\)-spaced schedule, is precisely what establishes the MCP eventual stability property: eventual stability concerns the schedule rather than any individual slot, and the schedule is entirely the orchestrator’s doing — by no longer throttling and spacing the deadlines by exactly \(\tau\), the orchestrator has the slots advance at the steady, “gap-free” cadence that eventual stability demands. The second half, that every correct validator learns each deadline at least \(\Delta\) in advance, plays a quieter but equally important role: it ensures that a proposer knows the deadline already by the slot’s starting time, leaving it enough time to submit its proposal — exactly what the proposal inclusion of slot consensus presupposes.
Our framework composes these two primitives in the natural way; 5 illustrates the interaction. Each validator runs its local instance of the orchestrator, which drives the protocol forward by setting each slot’s deadline. At each slot’s starting time (\(\Delta\) time before its deadline), or as soon as it learns the slot’s deadline, if later, the validator begins participating in that slot’s consensus instance and, if it is one of the slot’s proposers, submits its proposal to that instance. Whenever a slot consensus instance finalizes a block, the validator records the block, notifies the orchestrator that the slot is complete (which may in turn let the orchestrator schedule further slots), and eventually appends the block to its ledger. Since slots may be finalized out of order, blocks are buffered and appended in slot-number order, so that each validator’s ledger grows as a clean, contiguous sequence with no slot missing.
With the entire framework in place, we can now explain how it achieves the extreme pipelining it is named after: a fixed block interval of \(\tau\), regardless of how long consensus on any individual slot takes. The key lies in both building blocks: the slots and their blocks are independent by construction (one slot consensus instance per slot), and the orchestrator is free to schedule them independently, constrained only by boundedness (it may keep opening new slots as long as the number of underway slots stays within the bound). To appreciate this freedom, let us explain why it was out of reach for earlier designs. In traditional BFT protocols, consecutive slots are chained to one another: a slot must wait for its predecessor to make enough progress before it can begin. In unpipelined protocols, such as PBFT [8], basic HotStuff [9], and SBFT [10], this chaining is the strictest possible: the next slot begins only after the current slot’s block is fully finalized. Pipelined protocols, such as pipelined HotStuff [9], FastHotStuff [11], and MonadBFT [12], loosen this coupling, but do not remove it: the next slot begins earlier, once the current one produces a quorum certificate, yet it still requires that certificate as input. In both cases, each slot passes a baton to the next, and the protocol can only advance as fast as the baton travels. Our framework drops this dependency altogether: under favorable network conditions, a new slot opens every \(\tau\) time, dictated solely by the global clock, and nothing from any earlier slot — no artifact, no certificate, no event — is needed for it to start. Indeed, nothing in the orchestrator’s interface ties a slot to its predecessor’s progress: complete notifications may throttle the pace (boundedness), but they are never required as input for a new slot to open. The slots no longer wait for one another; they simply follow the schedule. The block interval thus becomes decoupled from the per-slot consensus latency: slots can be scheduled arbitrarily close together, and hence blocks can be produced at an arbitrarily high rate, no matter how long any individual slot takes to certify or finalize.
Let us now explain why our framework solves the MCP problem as defined in 2, walking through its guarantees one by one. Safety follows from the agreement property of slot consensus, applied slot by slot. Agreement ensures that, for every slot, honest validators never finalize conflicting blocks; and since every validator appends the finalized blocks to its ledger in slot-number order, any two honest ledgers contain the same blocks in the same order, one being a prefix of the other. In short, if no single slot can produce disagreement, the ledgers have no way to diverge. Liveness follows from an alternation between the two primitives. Consider the slots currently underway. By the termination of slot consensus, every honest validator eventually finalizes a block for each of them and, having done so, reports these slots as complete to the orchestrator. Once these completions arrive, the orchestrator’s boundedness no longer stands in the way: the orchestrator schedules new slots, whose consensus instances again eventually terminate, and so on forever. Hence, every slot is eventually scheduled and eventually contributes a block to every honest validator’s ledger: no slot stalls forever, and the ledgers keep growing.
Hiding requires no work at all: the hiding of each individual slot consensus instance is precisely the global hiding property, read across all slots. Censorship resistance, in contrast, needs the two primitives to cooperate. After the grace period \(\mathcal{G}\) following \(\mathrm{GST}\), the recovery property of the orchestrator guarantees that every honest validator learns each upcoming deadline at least \(\Delta\) in advance, that is, by the slot’s starting time. Every honest proposer therefore has enough time to submit its proposal by the starting time, and from there the proposal inclusion property of slot consensus takes over: the on-time proposal makes it into the slot’s finalized block, and hence into the ledger.
Eventual stability is exactly the first half of the recovery property: once the network stabilizes (from time \(\mathrm{GST} + \mathcal{G}\) onward), the orchestrator spaces consecutive deadlines by exactly \(\tau\), so the slots advance at the steady cadence that eventual stability demands. Finally, one practical concern remains; it is not an MCP guarantee, but, as discussed, a prerequisite for feasibility: the number of slot consensus instances a validator must attend to at any point in time. This is where boundedness of the orchestrator comes in: at any moment, each honest validator has only a bounded number of slots underway, hence only a bounded number of slot consensus instances to run, keeping its memory and computation in check no matter how the network behaves.
We now present Chorus, the slot consensus protocol we use in Cadence. We describe Chorus through two modes of operation. The first, the fast path, handles the good case: it is essentially a classical single-leader consensus generalized to accommodate several proposers at once, finalizing a block quickly whenever the network is synchronous and the proposers behave. The second, the fallback path, takes over when the fast path fails to finalize, ensuring that the slot reaches agreement nonetheless. Note that the split into a fast and a fallback path is only expository: the two are interwoven into a single protocol (and not two modes that the protocol toggles between).
Before presenting the protocol itself, we settle two preliminaries: the cryptographic primitives Chorus builds on, and what its validators actually agree upon (which, as we will see, is not blocks of transactions).
Chorus relies on a handful of standard cryptographic primitives, which we use as follows.
Collision-resistant hashing, used to build Merkle commitments: a short root that binds a whole sequence of values, while any single value can later be proven to be among them with a logarithmic-size proof.
Digital signatures that can moreover be aggregated, so that many signatures on the same message collapse into a single short multi-signature.
Erasure coding, which encodes a piece of data into \(n\) chunks such that any \(f + 1\) of them suffice to reconstruct the original data.
A per-slot threshold encryption scheme, which encrypts a message so that it can be decrypted only once \(f + 1\) validators release their decryption shares for that slot. The shares are bound to the slot itself, not to any individual ciphertext, so a single set of \(f+1\) shares opens every message encrypted for that slot.
We now turn to the following question: what Chorus’s validators actually agree on. The answer is not a block, but short digests of the proposals. Much as a single-leader protocol can agree on a hash of the leader’s block rather than on the block itself, Chorus has its validators agree on a digest of each proposal rather than on the proposal itself. Concretely, each proposal goes through three steps before agreement (6): it is first encrypted; then erasure-coded into \(n\) chunks; and finally committed to by a single Merkle root, the digest the validators agree on. Recall from 1 (Idea 4) that committing to these digests rather than to the full proposals is exactly what lets dissemination run in parallel with consensus, following AVID [13], DispersedSimplex [5], and Deterministic RaptorCast [14]. Each proposal is disseminated as erasure-coded chunks committed by a Merkle root, and the deterministic encoding gives consistency, meaning all correct validators reach the same verdict on a root, either all recovering the same proposal or all rejecting it as invalidly encoded. Consistency is a property of the encoding alone. Availability, that the proposal behind a root can be recovered, is achieved once Chorus certifies the root, since the certificate guarantees that enough honest validators hold their chunks for the data to be reconstructible. Any erasure-coded dissemination scheme providing consistency composes with Chorus in this way. Chorus builds its object of agreement on Deterministic RaptorCast [14], whose deterministic encoding makes a root’s verdict unique (consistency), while Chorus’s certificates over these roots establish that the committed data is recoverable (availability). Both are established for Chorus in 12. With several proposers in a slot, this object of agreement grows into a vector of such roots: one entry per proposer, each committing to that proposer’s proposal, or to a designated empty value when the proposer contributes nothing.
Crucially, an entry is never agreed upon on its own: it always comes with a certificate.
For a positive entry (one carrying a Merkle root), the certificate is a proof of availability, attesting that the data behind the root is available: every honest validator can retrieve it and arrive at the same outcome, either reconstructing the committed proposal in full, or deterministically concluding that its encoding is broken (the chunks are inconsistent); crucially, whichever of the two it is, all honest validators conclude the same.
For a negative entry, the certificate is instead a proof that the proposer’s proposal is correctly excluded, for instance because the proposer failed to disseminate it on time.
We call this vector of certified entries a meta-block (7): one entry per proposer, each a Merkle root or the empty value \(\bot\). A meta-block includes a proposer’s proposal when its entry is positive — the root commits to the proposal — and excludes it when the entry is negative. From here on, then, Chorus’s goal is simply to agree on a meta-block’s entries — the Merkle roots and \(\bot\) values themselves, not the certificates that justify them. The fast path and the fallback path are two mechanisms for doing exactly that, and we describe both purely as procedures for agreeing on the aforementioned entries. How the finalized entries are turned into the slot’s block is described in 4.4.
The fast path of Chorus follows the familiar structure of single-leader consensus, in which a leader disseminates its proposal and the validators then run two voting rounds before finalizing. Chorus keeps this template and departs from it in only one respect: a slot has several proposers, so all of them act as leaders at once, each disseminating a proposal of its own.
Before dissemination, each proposer encrypts its proposal using the threshold encryption scheme, producing a ciphertext that can be decrypted only once \(f+1\) validators release their decryption shares for the slot. The proposer then disseminates this encrypted proposal using the chunked encoding of 6, sending each validator that validator’s chunk together with a Merkle proof against the root and the proposer’s signature over the slot number, its own identity, and the root. The root commits to the encrypted proposal rather than the plaintext one. An honest proposer disseminates by the slot’s starting time \(D_s- \Delta\), so that under synchrony its chunks reach every validator before the deadline.
The first voting round opens at the slot’s deadline, and in it each validator casts one vote per proposer \(p_j\): if it has received a valid chunk from \(p_j\), carrying \(p_j\)’s Merkle root \(r_j\), it broadcasts a positive vote \[\langle \mathrm{\small yes}, s, p_j, r_j, \sigma \rangle,\] where \(\sigma\) is its signature on \(\langle \mathrm{\small yes}, s, p_j, r_j \rangle\), so that \(2f+1\) such votes can later be combined into a single certificate on \(r_j\). Importantly, a validator also attaches to each positive vote the chunk it received; it does so to support the fallback path, as we explain in 4.3. If, instead, no valid chunk from \(p_j\) arrived by the deadline,9 the validator broadcasts a signed negative vote \[\langle \mathrm{\small no}, s, p_j, \sigma \rangle,\] where \(\sigma\) is now its signature on \(\langle \mathrm{\small no}, s, p_j \rangle\). Crucially, as already noted in 4.1, these shares are tied to the slot itself rather than to any individual proposal, so \(f+1\) of them suffice to decrypt every proposal of the slot.
Once a validator has gathered, for every proposer, a quorum of \(2f+1\) matching votes, that is, \(2f+1\) yes votes on the same root or \(2f+1\) no votes, it assembles a fast meta-block: for each proposer it records either the root \(r_j\) with its certificate of \(2f+1\) yes votes, or, when the quorum is negative, a negative entry with a certificate of \(2f+1\) no votes. (When such a quorum fails to form for some proposer, the fallback path takes over, as we describe in 4.3.) These certificates are exactly the certificates that the entries carry. A \(2f+1\) yes certificate is a proof of availability: at least \(f+1\) of its signers are honest, and each signed only after receiving and verifying its chunk, so at least \(f+1\) honest validators hold their chunks, enough to recover the data behind \(r_j\) and, as discussed above, to either reconstruct the proposal in full or unanimously deem it invalid (see 4.4). A \(2f+1\) no certificate, in turn, justifies excluding the proposer, since at least \(f+1\) honest validators saw no valid chunk by the deadline. Each validator that has assembled a fast meta-block casts a single fast vote on it as a whole, broadcasting the meta-block along with the vote. As soon as it collects \(2f+1\) fast votes on the same fast meta-block, it finalizes its entries. 8 traces this communication pattern for a slot with two proposers.
As we emphasized in 1, latency is a first-order concern, so we want the fast path to finalize as quickly as possible. Under favorable conditions it does: provided the slot occurs after \(\mathrm{GST}\), the fast path finalizes in just two voting rounds, by time \[D + 2\delta,\] where \(D\) is the slot’s deadline and \(\delta \leq \Delta\) is the actual message delay. This is optimal: no protocol can finalize in fewer rounds [15]. In addition, Chorus offers a faster but weaker notion of finalization, which applications and users can choose instead of the regular one when they prefer lower latency over the strongest guarantee. A validator need not wait for both voting rounds: following prior work [12], it may speculatively finalize the entries after a single round of voting, as soon as it constructs the fast meta-block. A speculative finalization may ultimately be reverted, but, as we show once the fallback path is in place, only if some validator equivocated, disseminating conflicting votes for the same proposer; such deliberate misbehavior is exceedingly rare in practice and can always be detected and punished.
The fast path succeeds whenever the network is synchronous and every proposer is either correct (sending the same Merkle root to all correct validators) or fully silent (sending nothing to any): all correct validators then vote the same way on each proposer (all yes, or all no), a quorum forms, and the slot commits on the fast path. Only partial dissemination or equivocation splits a proposer’s votes and forces the fallback. By casting explicit no votes rather than staying silent, validators keep the slot on the fast path even when a proposer is fully offline.
We now turn to the fallback path, which validators follow when the fast path fails.
At the end of the first voting round, once the yes and no votes have been disseminated, a correct validator that can assemble a fast meta-block (as in 4.2) simply issues its fast vote carrying it; as we saw, this happens whenever the network is synchronous and every proposer is correct or silent. A validator that cannot assemble a fast meta-block instead takes the fallback path, in three steps.
It broadcasts a signed no-fast-vote message, but only after a timeout has elapsed and it has heard first-round votes from at least \(2f+1\) validators: by then it has given the fast path enough time and still cannot assemble a fast meta-block, so it times out on it. It is a signed statement that the validator will not cast a fast vote. By quorum intersection, a \(2f+1\) no-fast-vote quorum and the \(2f+1\) fast votes a fast-path commit needs cannot both form; so once \(2f+1\) no-fast-votes exist, the fast path can no longer commit, and it is safe to enter the fallback path instead.
For a proposer \(p_j\), it checks whether it has collected at least \(f+1\) yes votes on the same Merkle root:
If it has, it can recover \(p_j\)’s proposal on its own: recall that each yes vote carries its sender’s chunk, and any \(f+1\) chunks suffice to reconstruct the proposal. (This is exactly why, in the first round, every positive vote carried its chunk.) To be sure the recovered data is genuine, the validator re-encodes it and checks that this reproduces the same Merkle root. If it does, it broadcasts a fallback-yes vote signing that root.
Otherwise (there are no \(f+1\) yes votes on a common root, or the re-encoding yields a different root), it broadcasts a fallback-no vote for \(p_j\).
In this second post-deadline round, the validator waits for \(2f+1\) messages. Each is either a fast vote, which carries a fast meta-block, or a bundle of fallback votes, namely the per-proposer fallback-yes/fallback-no votes together with the sender’s single no-fast-vote. Whichever way the round goes, the validator ends it by submitting a single meta-block as its proposal to the slot’s fallback agreement (see 9). Here Chorus relies on an off-the-shelf agreement primitive: for each slot, Chorus runs a single instance of such a consensus protocol and treats it purely as a black box. Any protocol will do, as long as it guarantees agreement (no two correct validators decide differently), termination (every correct validator eventually decides), and external validity (its decision always satisfies a fixed, predetermined predicate). Which meta-block the validator submits depends on the messages it gathered this round:
At least one fast vote is received in the second voting round. In this case, the validator adopts the fast meta-block that vote carries and proposes it to the fallback agreement. (Note that any validator that built a fast meta-block at the end of the first voting round holds its own fast vote, so it always falls into this case and proposes that fast meta-block.)
All \(2f+1\) messages are fallback votes. The validator instead assembles a fallback meta-block, a second kind of meta-block alongside the fast one, and proposes it to the fallback agreement. We now describe how a fallback meta-block is built. The validator first aggregates the \(2f+1\) received no-fast-votes into a single certificate; by quorum intersection, the mere existence of this certificate proves that no one could have finalized on the fast path. It then fills in one entry per proposer \(p_j\):
if it holds \(f+1\) fallback-yes votes on the same root \(r\), then \(p_j\)’s entry is that root \(r\), certified by those \(f+1\) fallback-yes votes;
if it sees two fallback-yes votes on different roots, then \(p_j\)’s entry is \(\bot\), certified by those two conflicting votes as an equivocation proof;10
otherwise, \(p_j\)’s entry is \(\bot\), certified by \(f+1\) fallback-no votes.
In short, the fallback meta-block bundles a single \(2f+1\) no-fast-vote certificate with, for each proposer, one of three entries: an \(f+1\) fallback-yes certificate on a root, an equivocation proof, or an \(f+1\) fallback-no certificate.
The proposed meta-block is either fast or fallback. The external validity predicate we use in the fallback agreement is therefore that the agreement may decide only a well-formed fast or fallback meta-block for that slot. The slot is then finalized by whatever meta-block the fallback agreement decides. 9 traces the whole fallback path end to end.
We have now seen both paths, but neither actually outputs a block of transactions: each finalizes only the entries of a meta-block, and an entry is just a digest (a Merkle root) of a proposal, not the proposal itself. Producing the slot’s block from these entries therefore takes two steps. First, every correct validator must recover the proposals that the positive entries point to. Second, it merges these recovered proposals into the slot’s block by a deterministic transformation: discarding invalid proposals, deduplicating transactions, and ordering the rest. This second step is a purely local computation that yields the same block at every correct validator, so the real work lies in the recovery, on which we now focus.
The recovery must ensure that, from a meta-block \(B\) whose entries are finalized, every correct validator eventually obtains the same proposal vector, one entry per proposer, each either that proposer’s recovered proposal or \(\bot\) (10), subject to the following two guarantees:
all correct validators recover the same proposal vector, each entry being either the same recovered proposal at every validator or discarded at every validator (the consistency of 4.1.2);
an honest proposer’s proposal is never lost: if \(B\) certifies the proposal \(P_i\) of a correct proposer \(p_i\) (i.e., \(B\) contains the root committing to the encryption of \(P_i\)), then every correct validator recovers exactly \(P_i\) in its proposal vector.
Both follow from how a single positive entry of \(B\), committing to a root \(r\), is recovered.
If \(B\) is a fast meta-block, then such an entry carries a \(2f+1\) yes certificate (see 4.2), and a validator casts a yes vote only while holding the chunk it received for \(r\); hence, at least \(f+1\) correct validators hold their chunk for \(r\). These honest validators re-disseminate their chunks, so every correct validator eventually collects \(f+1\) chunks committed under \(r\). Collecting them does not by itself guarantee a recoverable proposal, since a faulty proposer may have committed chunks that do not form a valid erasure encoding; this shows up in one of two ways. If the validator cannot decode the chunks at all, the encoding is certainly invalid and it discards the proposal. Otherwise it decodes a candidate ciphertext \(\mathcal{D}\) (the root commits to the ciphertext, not the plaintext), but a successful decode still does not guarantee a consistent encoding: the validator re-encodes \(\mathcal{D}\) and checks that this reproduces exactly \(r\). If it does, the encoding is valid and the validator decrypts \(\mathcal{D}\) with the slot’s \(f+1\) decryption shares (released at the slot’s deadline) to obtain the plaintext proposal.11 If the re-encoding yields any other root, the encoding is invalid and the validator discards the proposal.
If instead \(B\) is a fallback meta-block, such an entry carries an \(f+1\) fallback-yes certificate (see 4.3). At least one of its signers is honest, and an honest validator casts fallback-yes only after reconstructing the proposal and checking that it re-encodes to \(r\); as part of casting that vote (4.3) it sends each validator its assigned chunk, and the validators re-broadcast their chunks, so every correct validator obtains the \(f+1\) chunks needed to reconstruct the encrypted proposal \(\mathcal{D}\) behind \(r\). Recovery then proceeds exactly as in the fast case: re-encode \(\mathcal{D}\) to check it against \(r\) and, if it matches, decrypt it into the plaintext proposal, otherwise discarding it as invalid.
In both cases, once the root is certified, the certificate guarantees that \(f+1\) honest validators hold their chunks, so every correct validator can collect \(f+1\) chunks and reconstruct the data. This is the availability of 4.1.2, achieved through Chorus’s certificate over the disseminated root. Whether \(B\) is fast or fallback, recovery provides both guarantees:
holds because whether a proposal is discarded depends only on \(r\): it is discarded if and only if the chunks committed under \(r\) do not form a valid erasure encoding, regardless of which chunks a validator happens to collect. When they do form one, every correct validator decodes the same ciphertext and, decryption being deterministic, recovers the same plaintext. When they do not, every correct validator discards the proposal: even one whose own chunks were individually valid finds, on re-encoding, that they fail to reproduce \(r\), since validity is a property of the whole committed set, not of the chunks one happened to receive.
holds because a correct proposer encrypts a valid proposal and encodes the ciphertext into a valid erasure encoding; the chunks committed under \(r\) therefore re-encode to \(r\) at every correct validator, so the proposal is always recovered and never discarded.
Proposal recovery runs in parallel with voting, so it adds no latency. The latency claims of 4.2 (speculative finalization after one round, full finalization after two) are about the fast path and were stated for the meta-block; they carry over to the full block of transactions. On the fast path, every first-round positive vote already carries the sender’s chunk, and every validator bundles its slot decryption share into the same first-round message, so a validator that assembles a fast meta-block at the end of the first round holds, behind each positive entry, \(2f+1\) chunks (more than the \(f+1\) needed to reconstruct the ciphertext) and \(2f+1\) decryption shares (more than the \(f+1\) needed to decrypt it), and recovers the proposal with no further communication. The fallback path adds no extra latency from recovery either. A positive entry certified by \(2f+1\) yes votes is recovered exactly as on the fast path. For one certified by \(f+1\) fallback-yes votes, a fallback-yes voter sent each validator its assigned chunk when voting (4.3), so every correct validator holds its chunk for \(r\); once the fallback meta-block forms, each broadcasts that chunk, and one \(\Delta\) later every correct validator can decode the proposal, before the fallback path finalizes. Hiding adds no latency either: the slot’s \(f+1\) decryption shares are released at the deadline, bundled with the first-round votes, and open every ciphertext for the slot, so a validator decrypts a proposal as soon as it has decoded it.
Finally, we sketch why Chorus satisfies the four properties of slot consensus (see 3).
We must show that no two correct validators finalize different blocks. By recovery’s consistency guarantee (i), agreeing on the same entries yields the same block, so it suffices to show that no two correct validators finalize different entries. On the fast path this is immediate: each entry carries a certificate of \(2f+1\) signatures, so, by quorum intersection, no proposer can have two conflicting certified entries. On the fallback path it follows from the fallback agreement, whose agreement property makes all correct validators decide the same meta-block, and hence the same entries. It remains to rule out a split in which one validator finalizes on the fast path and another finalizes different entries on the fallback path. But if any validator finalizes on the fast path, it holds a quorum of \(2f+1\) fast votes, and, by quorum intersection, no \(2f+1\) no-fast-vote certificate can then exist; hence no fallback meta-block can be formed, and the fast-path entries are the only ones anyone can finalize.
Every correct validator eventually finalizes, and it suffices to show that every correct validator proposes a meta-block to the fallback agreement. We distinguish two cases. If some correct validator assembles a fast meta-block at the end of the first round (and broadcasts its fast vote), then every correct validator eventually receives it, adopts the (unique) fast meta-block, and proposes it. Otherwise, every correct validator eventually collects \(2f+1\) fallback bundles, builds a fallback meta-block, and proposes that. In either case all correct validators propose to the consensus, so, by its termination, it decides a meta-block, by which the slot is finalized.
Consider an honest proposer that submits its proposal on time (\(\Delta\) time before the slot’s deadline), while the network is synchronous, and let the proposal commit to root \(r\). Every correct validator then receives a valid chunk by the deadline and votes yes on root \(r\), so no correct validator votes no for this proposer. A fast meta-block can therefore only record \(r\) for this proposer, never excluding the proposal. The fallback path includes it just as well: every correct validator holds \(f+1\) yes votes on \(r\), reconstructs the proposal, and re-encodes it successfully (an honest proposal is correctly encoded), so it sends a fallback-yes on \(r\) and never a fallback-no. So no \(f+1\) fallback-no votes can ever form against it either. In both cases, then, the finalized entry for this proposer certifies the proposal under root \(r\) rather than excluding it; by recovery’s guarantee (ii), every correct validator therefore recovers the proposal into the slot’s block, which ensures proposal inclusion.
Proposals are revealed only at the deadline, by which point it is too late for the adversary to craft a competing proposal and still have it included, so it cannot make its own proposal depend on the honest proposers’ proposals for the slot.
We claimed in 4.2 that a speculatively finalized block can be reverted only if some validator equivocated; here is why. Suppose a correct validator speculatively finalizes the fast meta-block \(B\), but the fallback consensus ultimately decides a different meta-block \(B'\), so that the two assign some proposer \(p_j\) a different proposal in the slot’s block. Since \(B\) is a fast meta-block, it backs \(p_j\)’s entry with a first-round quorum: \(2f+1\) yes votes on one root \(r\), or \(2f+1\) no votes. There are two ways \(B'\) can disagree:
Conflicting roots. Say \(B\) records a Merkle root \(r\) for \(p_j\) while \(B'\) records a different root \(r'\). A positive entry always rests on first-round yes votes for its root — \(2f+1\) of them in a fast meta-block, and the \(f+1\) that every fallback-yes requires its caster to have collected. Since \((2f+1) + (f+1) > n = 3f+1\), the yes-voters for \(r\) and those for \(r'\) must overlap; as a correct validator yes-votes a single root, the validator in the overlap signed both — an equivocation.
Inclusion vs. exclusion. Say instead that \(B\) includes \(p_j\) under a root \(r\) while \(B'\) leaves \(p_j\) out. Including \(p_j\) rests on \(2f+1\) first-round yes votes on \(r\), since \(B\) is a fast meta-block. Excluding \(p_j\) requires a negative entry carrying \(f+1\) fallback-no votes, at least one of them from an honest validator — and an honest fallback-no is cast after its sender has collected \(f+1\) first-round no votes. Since \((2f+1) + (f+1) > n = 3f+1\), the two sets overlap, and the validator in the overlap cast both a yes on \(r\) and a no for \(p_j\) in the first round — contradictory votes for the same proposer, once again an equivocation. (There are two further ways an honest validator casts a fallback-no for \(p_j\): it gathers \(f+1\) yes votes for a root \(r\) whose chunks fail to re-encode to \(r\), or it receives conflicting yes votes and never gathers \(f+1\) on any single root. Either way the proposer is the culprit — committing to an invalidly encoded root or disseminating several distinct proposals — misbehavior all the same.)
This section introduces Conductor, the orchestrator Cadence employs. It fixes each window’s deadlines in advance, so block production needs no per-slot coordination; its one real decision is when to open the next window, which it makes by having the validators agree, once per window and off the critical path, on the next window’s first deadline, from which its remaining deadlines follow \(\tau\) apart, where \(\tau\) is the block interval.
In order to bound the number of open slots, Conductor groups slots into windows of \(W\) consecutive slots: window \(1\) is slots \(1, \ldots, W\), window \(2\) is slots \(W + 1, \ldots, 2W\), and so on. It schedules these windows one at a time, and the schedule of each depends on the progress of the earlier ones: window \(1\) is scheduled at genesis (global time \(0\)), and window \(\omega + 1\) is scheduled only once correct validators report that every slot of windows \(1, \ldots, \omega - 1\) and the first \(p\) slots of window \(\omega\) are complete. This is what keeps the number of open slots bounded as window \(\omega+1\) is withheld until the first \(p\) slots of window \(\omega\) are complete. Recall the two parameters of Conductor: the window size \(W\) and the threshold \(p \in \{0, \dots, W-1\}\).
We now describe the protocol from the perspective of a single correct validator \(p_i\); recall that the validators’ clocks are synchronized, so they all share a single global timeline. At genesis, \(p_i\) schedules window \(1\) by issuing its slots’ \(\tau\)-spaced deadlines: slot \(1\) at \(\Delta\) (so its starting time is \(\Delta - \Delta = 0\)), slot \(2\) at \(\Delta + \tau\), and so on up to slot \(W\) at \(\Delta + (W-1)\tau\). From then on, the only input that drives Conductor is the completion of slots. (In Cadence, as discussed in 3.3, \(p_i\) marks a slot complete once it has finalized that slot’s block.)
This is the crucial and most technical part of Conductor. We explain it for the transition from window \(1\) to window \(2\); every later transition, from a window \(\omega\) to \(\omega + 1\), works the same way (up to one minor change that we flag below).
As soon as \(p_i\) has completed all of the first \(p\) slots, it reads the current time \(\mathcal{T}\) and computes a proposed deadline for the first slot of window \(2\); the remaining slots of window \(2\) are then simply \(\tau\)-spaced after it, so this single deadline fixes the whole window. This is only \(p_i\)’s own proposal. Since correct validators complete the first \(p\) slots at different times, they compute different proposed deadlines and then reconcile them into a single agreed deadline through a sub-protocol described below; we first explain how \(p_i\) forms its proposal. Validator \(p_i\) forms its proposed deadline as follows (see 11):
If \(\mathcal{T}\) is earlier than the deadline of window \(1\)’s last slot (slot \(W\)) — i.e., \(p_i\) completed the first \(p\) slots before slot \(W\)’s deadline had even arrived — then the system is keeping up, and \(p_i\) proposes the deadline of slot \(W+1\) to be exactly \(\tau\) after the deadline of slot \(W\). In other words, \(p_i\) proposes that the global cadence runs on unbroken, with no gap between the two windows.
Otherwise, the first \(p\) slots completed only after slot \(W\)’s deadline had already passed; \(p_i\) reads this as the system falling behind, and its proposed deadline for slot \(W+1\) is the current time \(\mathcal{T}\) — a proposal that, being later than the cadence point, leaves a gap for the lagging slots to catch up.
These two cases capture exactly what Conductor wants to achieve: it preserves the steady \(\tau\)-cadence whenever the system is “healthy” (Conductor’s recovery property, which yields the eventual stability of Cadence), and stretches the schedule when it is not (Conductor’s boundedness property, which caps the slots in flight for each validator in Cadence).
Note that different correct validators may complete the first \(p\) slots at slightly different times, and so arrive at different proposed deadlines for window \(2\); they must nonetheless agree on a single one. To this end, the validators feed their proposed deadlines into this off-the-shelf ACS primitive, which outputs a common vector of \(2f+1\) proposed deadlines — at least \(f+1\) of them from honest validators — and guarantees agreement (all correct validators obtain the same vector) and termination (every correct validator eventually obtains one). Each validator then takes the median of the vector’s entries as the agreed deadline of slot \(W+1\), and opens window \(2\) accordingly. Taking the median is what tames Byzantine influence: among \(2f+1\) values of which at most \(f\) are Byzantine, the median is guaranteed to lie between the smallest and the largest honest proposed deadline, so faulty validators can never drag the deadline outside the range the honest ones proposed (12).
Every later window is handled the same way, with the one change flagged above: \(p_i\) must also wait for all earlier windows to be complete (a requirement that is vacuous for the transition from window \(1\) to window \(2\)). Concretely, once window \(\omega\)’s prerequisites are met — the slots of all earlier windows and the first \(p\) slots of window \(\omega\) are complete — validator \(p_i\) runs the same two-case computation (now against window \(\omega\)’s last deadline) and agrees on it through ACS and the median. This finally yields the bound promised above: when window \(\omega+1\) opens, only the unfinished part of window \(\omega\) and the fresh window \(\omega+1\) can be in flight, so at most \((W - p) + W = 2W - p\) slots are underway at any time.
We now argue that Conductor achieves the two properties specified in 3.2.
We already did the counting at the end of the previous subsection: a correct validator opens window \(\omega+1\) only once every slot of windows \(1, \ldots, \omega-1\) and the first \(p\) slots of window \(\omega\) are complete, so at most \(2W - p\) slots are ever underway at once.
Recovery asks that, within a bounded grace period after the network stabilizes, the schedule “heals”: deadlines become \(\tau\)-spaced again, with no gaps, and every correct validator learns each deadline at least \(\Delta\) in advance. We sketch why this happens within bounded time after \(\mathrm{GST}\).
Everything hinges on one question: once the network is running smoothly (after \(\mathrm{GST}\)), does such a “good” window leave enough time to schedule its successor before its own cadence ends? A window lasts \(W\tau\), and within it two things must happen (13). To begin, the first \(p\) slots must complete. Chorus finalizes a slot within some latency \(\ell_{\mathrm{\small Chorus}}\) after \(\mathrm{GST}\), so every correct validator finalizes all of the first \(p\) slots by time \(\mathcal{T}_p + \ell_{\mathrm{\small Chorus}}\) — the \(p\)-th slot’s deadline \(\mathcal{T}_p\) plus that latency — and by then all of them have proposed to the window’s ACS instance. Second, since every correct validator has proposed, the ACS decides within a further \(\ell_{\mathrm{\small acs}}\) time, fixing the next window’s first deadline. We pick the parameters so that this whole pipeline fits inside the window, i.e., \(\mathcal{T}_p + \ell_{\mathrm{\small Chorus}} + \ell_{\mathrm{\small acs}} \le W\tau\). Thus, in a good window, every correct validator opens the first \(p\) slots (outputs their deadlines) by \(\mathcal{T}_p\) and they all settle the next window’s deadline by \(\mathcal{T}_p + \ell_{\mathrm{\small Chorus}} + \ell_{\mathrm{\small acs}} \le W\tau\) — with no gap and well ahead of time. This is exactly what we needed: from the first good window onward, the schedule stays “smooth” forever.
Cadence is a consensus protocol, but in the context of a blockchain it provides a different interface than blockchains usually expect from their consensus layer, departing in three ways at once: consensus commits only a digest of each proposer’s encrypted proposal, not the transactions it contains, and without executing them; these digests carry no reference to a parent; and each proposer builds its proposal without seeing the current executed state or the other proposals its own will be merged with. This gives up machinery a blockchain normally relies on: linking blocks into a chain, committing to executed state, and validating transactions before they are included. We show how to recover each, so that the result behaves like an ordinary blockchain: 6.1 builds valid proposals under this uncertainty, 6.2 turns the committed entries into an execution block, and 6.3 certifies the resulting history and state. All of this is what it takes to behave like an ordinary blockchain; the further consequences and refinements of running with multiple proposers, which a deployment may want but does not need for that, we collect in 7.
Users submit each transaction to one or more of a slot’s proposers rather than to a single leader. We treat which proposers a user submits to, and the fee mechanism that would price this choice, as outside our scope.
We now follow one slot. A proposer selects transactions and assembles a proposal, validating each against a settled view of the executed state. It encrypts and erasure-codes the proposal and disseminates the chunks. The slot then commits the entries of a meta-block, one per proposer: the digest of its proposal if included, or empty if not. In parallel with consensus, the proposals behind the committed digests are recovered from their chunks and decrypted, and a deterministic step we call translation turns the committed meta-block into the slot’s execution block, the ordered list of transactions to execute. Execution runs asynchronously, trailing consensus by up to \(\xi\) slots, the execution lag, and the resulting state, together with the committed prefix, is certified for external verifiers.
A proposer builds its proposal under two kinds of uncertainty. First, execution is asynchronous [4], [16]: it trails consensus, so the proposer does not know the state its transactions will execute against. Second, it cannot see the proposals of the other proposers in its slot (the peer blind spot, from running multiple proposers) and those of recent slots due to extreme pipelining (the temporal blind spot).
A traditional blockchain validates each transaction before including it (i.e., synchronous execution), against the exact state the block builds upon. Cadence has no such state to give a proposer, so an implementation may instead fix a common reference for validity, such as the executed state after slot \(s-\xi\), a settled state that all validators share. A transaction is admitted only if it is valid against this reference; one already invalid against it, for instance with a reused nonce, is rejected. Because the reference is shared, the decision is the same for everyone: an honest proposer applies it when building, and translation applies it deterministically (6.2), so a transaction invalid against this reference is kept out of the execution block, even one a faulty proposer includes. A proposer also validates against its own recent proposals. Others cannot see them yet, in the temporal blind spot, but it can, so it builds on its own not-yet-settled transactions. By the time the slot is translated (6.2), these are revealed, and translation re-applies the same check.
Since the state reference is stale by \(\xi\) slots, a transaction admitted against it could be unpayable by the time it executes. To address this, a reserve-balance scheme [17] adapted to the multi-proposer setting can be used.
To prove that it validated its proposal against the right reference, a proposer attaches an execution certificate for slot \(s-\xi\) to its proposal, its ticket. This certificate pins the executed state it validated against (6.3). A proposer can form it only once \(2f+1\) validators certify that they have executed slot \(s-\xi\), so it cannot make a valid proposal while execution lags more than \(\xi\) slots behind: there is then no ticket to attach. This keeps proposals within \(\xi\) slots of certified execution, preventing fresh proposals from running too far ahead of it. This bounds proposal validity, not how many slot instances may be open at once; keeping that number bounded is instead Conductor’s role. A proposer that falls behind cannot form a ticket and contributes nothing, though its slot is still opened even when it ends up empty. Because proposals are encrypted and votes are cast on the encrypted proposals, the ticket cannot be checked at voting time: validators may vote on and finalize a proposal whose ticket is missing or invalid, even forcing the slot onto the fallback path. Translation then filters such a proposal out when assembling the execution block(6.2), so its transactions never execute, and it is itself evidence of misbehavior.
The consensus commitment fixes which proposals belong to slot \(s\), but not yet a block of transactions to run. Translation produces the execution block, and it must be deterministic: from the same committed data, every validator must compute the same execution block. Translation runs serially across slots: to translate slot \(s\), that slot must be committed, the proposals behind its committed digests must have been recovered and decrypted, and slots \(1\) through \(s-1\) must already be translated. Although the slots’ consensus instances run in parallel, translation therefore builds each execution block on a fixed prefix. Over that prefix, and against the certified executed state at slot \(s-\xi\), it merges the slot’s proposals into the execution block. Consensus thus agrees on the entries alone, and the execution block is what they deterministically yield.
The merge runs in stages. Translation first removes every invalid inclusion, leaving only valid transactions. It then deduplicates them, keeping a single copy of each. Validity already keeps a proposer from re-including a transaction it carried itself or one settled before the blind spot, so any surviving duplication is across proposers: building blind, several may each carry the same transaction, within a slot (the peer blind spot) or across recent slots (the temporal one). It finally orders what remains, for example by priority fee. Execution then runs each execution block asynchronously, once it is available.
However widely a transaction is duplicated, only one copy executes; the rest cost only bandwidth and block space. A deployment can shrink that residual: keeping the proposer set semi-stable, rotated slowly rather than replaced each slot, lets a proposer skip its own recent inclusions, and a transaction-fee mechanism can charge more for each additional inclusion while letting a user choose which proposers may carry a transaction, so a user pays for more inclusions only when it wants them for censorship resistance.
Validators that follow every slot already hold the committed history and, in time, the executed state. Other parties do not: light clients, bridges, and validators catching up after asynchrony or newly joining need to verify both from a small proof, rather than by replaying the chain. In a traditional blockchain every block carries a pointer to its parent, so the certificate finalizing slot \(s\) implicitly certifies the entire chain back to genesis, and each block commits to its executed state. Under extreme pipelining, neither holds. Because slots run concurrently, the proposer of slot \(s\), when it proposes, cannot in general know the outcome of slot \(s-1\): that slot may not yet have terminated, and even its proposals may not yet have been received. A slot therefore cannot point to its predecessor, and the certificate finalizing it certifies only that slot, carrying no executed state. We recover both with a mechanism that runs off the critical path, so the core protocol never waits on it.
A prefix certificate aggregates \(2f+1\) validator signatures12 attesting a predicate over a prefix of committed slots; since at least \(f+1\) of the signers are correct, the predicate holds. We instantiate it twice. A chain certificate attests that slots \(1, \dots, s\) are all committed; an execution certificate attests that slots \(1, \dots, s-\xi\) have all been committed and executed, with its \(2f+1\) signers agreeing on the resulting state root at slot \(s-\xi\). The execution certificate is a certificate on the Ethereum-style header that execution outputs, which carries the executed-state root and, through its parent hash, the committed prefix up to that slot; either certificate can also certify the recursive hash that links the committed blocks back to genesis, giving recursive verification of the whole prefix. A proposer attaches the slot-\((s-\xi)\) execution certificate to its proposal as its ticket (6.1). Either certificate may also be used for other purposes, such as external verification by light clients or bridges. Together, the two recover what a traditional block certificate covers at once: the committed history and the executed state.
To form these prefix certificates, a validator signs the corresponding attestation once it has observed the relevant slots committed (and, for an execution certificate, executed); \(2f+1\) such signatures make up the certificate. The proof trails finalization slightly and is fetched separately rather than carried in the slot’s commit.
This section collects consequences and refinements of Cadence’s design that matter for deployment but are not required to follow the core protocol. We discuss how a proposer that breaks the fast path can be held accountable (§7.1), questions of latency, scheduling, and economic ticks (§7.2), and transaction privacy (§7.3).
As we specified in §4.2, Chorus enjoys a fast path that finalizes a slot after only two voting rounds. That same section also tells us when this fast path is available: it triggers precisely when every proposer is either correct, disseminating its proposal to all correct validators, or fully silent, disseminating it to none. The difficulty lies in the intermediate case. A proposer that partially disseminates, delivering its proposal to some correct validators but not all, splits the validators’ views, and such a split can derail the fast path, forcing Chorus onto the slower fallback path (§4.3).13
If we must pay this latency, we would at least like to know which proposer caused it. We therefore equip Chorus with an accountability mechanism: whenever a proposer forces the fallback path, we single it out and hold it responsible for its partial dissemination, leaving correct validators a transferable proof of which proposer broke the fast path. This guarantee comes neither for free nor always: it holds only when the number of Byzantine validators is at most \(f/2\).14 Under this assumption (and after GST, since detecting a missed fast path is ultimately a matter of liveness), every fallback leaves correct validators holding enough signed evidence to reliably name every proposer that partially disseminated. Let us see why.
Recall the votes of the first round. At the deadline, each validator casts, for every proposer \(p\), a signed positive vote if it received \(p\)’s proposal in time, and a signed negative vote otherwise. A fast meta-block can be assembled only when, for every proposer, these votes converge: \(p\) must gather either a quorum of \(2f+1\) positive votes, or a quorum of \(2f+1\) negative votes. When neither quorum forms for some proposer, the fast path cannot proceed, and the slot falls back to the slower path.
Now suppose at most \(f/2\) validators are Byzantine, and consider a correct validator that fails to build the fast meta-block. After GST, under synchrony, this validator hears from every validator except the (at most \(f/2\)) faulty ones, that is, from \(n - f/2 = 2f+1+f/2\) validators, and hence collects at least that many votes for each proposer. Failing to build the meta-block means that, for some proposer \(p\), neither quorum has formed: among the votes it holds for \(p\), fewer than \(2f+1\) are positive and fewer than \(2f+1\) are negative. Thus \(p\) drew at most \(2f\) positive votes and at most \(2f\) negative votes; and since the two counts together sum to at least \(2f+1+f/2\), each of them is, in turn, at least \(f/2+1\).
This split is precisely the fingerprint of partial dissemination, and, crucially, one we can prove. Among the \(f/2+1\) positive votes, at most \(f/2\) can come from Byzantine validators, so at least one comes from a correct validator: one that genuinely received \(p\)’s proposal by the deadline, witnessing that \(p\) did disseminate to someone.15 Among the \(f/2+1\) negative votes, by the very same counting, at least one comes from a correct validator that genuinely did not receive it, witnessing that \(p\) did not disseminate to everyone. Because all votes are signed, these two witnesses together form a transferable certificate: anyone, knowing that at most \(f/2\) validators are faulty, can read off from \(f/2+1\) signed positive and \(f/2+1\) signed negative votes that some correct validator received \(p\)’s proposal while some other correct validator did not. That is exactly partial dissemination, so correct validators can hold \(p\) accountable.
The split-vote certificate is always a sound, transferable proof that \(p\) partially disseminated; what depends on synchrony is only reading a fallback as \(p\)’s fault, since under asynchrony even a correct proposer can miss the fast path. A single fallback is therefore no cause to act, but a proposer that provably and repeatedly partially disseminates can be penalized.
An honest proposer disseminates its chunks early enough that, under synchrony, every chunk reaches every validator before the deadline. The worst-case message delay \(\Delta\) is a conservative bound; a well-connected proposer reaches all validators within a smaller delay \(\delta < \Delta\) of its own, so it can disseminate as late as \(\delta\) before the deadline and still have its chunks arrive in time. Disseminating later lets a proposal include fresher transactions and so improves end-to-end latency for users, so each proposer sets its own dissemination time according to its connectivity. Disseminating too late—while potentially lucrative for rational proposers [18], [19]—risks reaching only some validators in time, which can push the slot onto the slower fallback path; our accountability mechanism (§7.1) deters this by leaving evidence of partial dissemination. A proposer that is entirely silent, by contrast, forces no fallback and leaves no such split-vote evidence: every validator simply records a negative entry and votes against it. Such a proposer needs no dedicated liveness incentive, since forgoing its proposal reward is penalty enough.
By default, a validator broadcasts its proposal vote at the slot deadline. As an optional optimization, which we call early voting, a validator may vote earlier in slot \(s\) once every entry for \(s\) is positive. So that this does not weaken hiding, a validator releases its decryption share only at the deadline, decoupled from its vote, so proposals are revealed no earlier than before. Recovering the proposals does not delay finalization: their chunks are disseminated together with the votes, so reconstruction needs no extra round, and only decryption waits for the shares released at the deadline. In our experiments, gathering the \(f+1\) shares completes around the time a slot is speculatively finalized, well before it is fully finalized, so a slot’s proposals are recovered in time even when its votes were cast early.
Early voting reduces finalization latency. A slot can finalize as soon as its proposals have arrived and gathered commit votes, rather than waiting for the deadline. How much it saves depends on all of the slot’s proposers together: a vote can be cast early only once every entry is positive, so the more conservatively they disseminate, leaving a margin before the deadline, the further ahead the slot can finalize.
Some blockchains decouple the on-chain economic tick from the consensus tick with cycles: on-chain activity advances in shorter cycles while consensus still runs once per slot, as in Constellation [3]. Cycles let activity such as auctions and oracle updates tick at the finer granularity, but they do not lower end-to-end latency, since a transaction is still finalized only once per slot. They are thus no substitute for extreme pipelining, which lowers the block interval itself. Their one advantage is message complexity, since consensus does not run more often. Chorus could support cycles as a straightforward extension: each slot carries \(K\) cycles, each proposer disseminates one part of its proposal before each cycle’s deadline, and a validator casts a single proposal vote per slot, positive for a proposer only if it received that proposer’s assigned chunk of every part in time. The positive vote then carries the combined Merkle root over the proposer’s \(K\) parts, so \(2f+1\) such votes certify its whole proposal exactly as without cycles.
Hiding conceals each proposal from everyone until the deadline, but a proposer a user submits to sees the transaction in plaintext. So a user trades off how many proposers to submit to: more improve inclusion and censorship resistance, while fewer reduce the chance that one of them leaks or front-runs the transaction. Client-side transaction encryption, where the user encrypts the transaction before submitting it, as in encrypted mempools [20]–[22], removes this trade-off, since the user can submit to many proposers without revealing the contents to any of them. It does not replace MCP: on its own it gives no short-term censorship resistance [23], since a proposer can censor a ciphertext without reading it. Nor does it replace proposal encryption: in a practical deployment, where proposers compete (say, for transaction fees), hiding protects each proposer’s payload from its rivals, which transaction encryption does not. The two can compose. For either scheme, both the timing of share release and the decryption threshold (\(f+1\) or more) are protocol choices that trade off latency and availability against the strength of hiding.
We estimate Cadence’s fast-path network latency, the common case in which every proposer is correct or fully silent, on the \(200\) stake-weighted, globally distributed validators of Monad mainnet. We define end-to-end latency as the time from when a transaction reaches a proposer until it is finalized. It has two parts: the finalization latency, the time from the transaction entering a proposal until it is finalized, and the inclusion latency, the wait until it enters a proposal. The finalization latency is, on average, \(219\) ms to full finality and \(167\) ms to speculative finalization after a single voting round. The inclusion latency is, on average, half a block interval, so, for example, \(50\) ms for a block interval of \(\tau= 100\) ms. Composing the two, the end-to-end latency averages \(269\) ms to full finality and \(217\) ms to speculative finalization. The early-voting optimization (7.2.0.2) improves these figures by about \(30\) ms: the finalization latency drops to \(191\) ms (full) and \(137\) ms (speculative), and the end-to-end latency to \(241\) ms and \(187\) ms. In the early-voting case, a slot’s decryption shares arrive on average \(5\) ms after its speculative finalization, well before full finalization. The user-to-proposer hop these figures omit tends to be smaller in MCP than in single-leader protocols, since a user is more likely to have a nearby proposer, which improves overall latency.
We obtain these figures from a simulation calibrated to RIPE Atlas round-trip measurements among the 83 (city, ASN) groups in which these validators run as of June 2026, with five concurrent proposers per slot. We set each proposer’s lead time, the interval by which it broadcasts ahead of the deadline, so that in \(99\%\) of trials at least \(90\%\) of validators receive its chunk before the deadline. Across proposers, this lead time averages \(104\) ms.
The simulated propagation delays are the dominant part of this end-to-end latency. The contributions it omits are small: local processing at validators, where execution and most validity checks occur asynchronously ([subsection:uncertainty]), and the bandwidth-induced queueing delay, which is at most a few milliseconds.16
Motivated by the goal of driving inter-proposal time down, much of the community’s effort has centered on pipelining. Protocols such as the HotStuff family [9], [24], [25], MonadBFT [12], and Jolteon [26] accomplish this by chaining proposals through quorum certificates (QCs): a leader is free to issue its next proposal as soon as it collects a QC for the previous one, which brings the inter-proposal time down to \(2\Delta\). Pushing this idea further, optimistic designs such as Moonshot [27] and Hydrangea++ [28] assume consecutive honest leaders and let a leader propose even before the prior proposal has been quorum-certified, bringing the inter-proposal time down to the network delay \(\Delta\) itself, though not below it. We go below this bound with extreme pipelining, which achieves sub-\(\Delta\) block intervals by running independent, non-chained single-shot consensus instances rather than chaining proposals at all.
A similar line of work reaches sub-\(\Delta\) intervals by staggering several multi-shot consensus instances in time and merging their outputs into one log: Shoal++ [29] interleaves three DAG protocols one message delay apart, Raptr [30] generalizes this to \(K\) instances offset by \(2/K\) message delays, and, concurrently with our work, Gatling [31] casts it as parallel composition over any atomic broadcast protocol. Where we run one sequence of independent single-shot slots, these interleave several instances to the same effect.
Constellation [3] introduces cycles, arbitrarily short economic ticks; complementary to extreme pipelining, they shorten the economic tick, while their underlying consensus produces blocks at a rate bounded by the network delay. We discuss cycles, and how they can be incorporated into Cadence, in 7.2.0.3.
Garimidi et al. [1] set out the goals of multiple concurrent proposers, including censorship resistance and hiding; these goals have since been pursued, through different mechanisms, by Prefix Consensus [32], Constellation [3], and AMP [33]. All of these designs provide some notion of censorship resistance. Only Garimidi et al. [1] incorporate hiding into their protocol design, though AMP notes that hiding is orthogonal to its core protocol and could be added. To our knowledge, Chorus is the first MCP design to offer both short-term censorship resistance (for every block after GST plus a grace period) and an optimal three-round good-case latency.
Prefix Consensus [32], a leaderless multi-proposer SMR, achieves good-case latency comparable to that of our fast path, while guaranteeing \(f\)-censorship-resistance: after \(\mathrm{GST}\), there may be up to \(f\) slots for which censorship resistance does not hold. Crucially, and in contrast to our work, these \(f\) slots are adversarially chosen; they need not be the first \(f\) slots after \(\mathrm{GST}\), but may be placed at any point. In Chorus, by contrast, censorship resistance holds for all slots beyond a bounded interval following \(\mathrm{GST}\): only slots falling within this bounded grace period may lack censorship resistance, while every subsequent slot is guaranteed it, and the adversary can do nothing to violate it.
Constellation [3] is a multiple-proposer protocol layered on Solana’s Alpenglow [34]. Proposers disseminate their transactions to a layer of attesters whose attestations constrain which proposals the leader must include, giving it selective censorship resistance. Because this inclusion layer sits on top of Alpenglow’s consensus, finalization takes two more communication rounds than the consensus alone.
AMP [33] is a multiple-proposer layer on the Tendermint consensus algorithm. Like Constellation, it separates dissemination from agreement, which costs two extra communication rounds: proposers broadcast transaction payloads, and validators agree only on their identifiers, carried in vote extensions that constrain which payloads the leader must include. Its central guarantee is bounded inclusion: a payload attested by all correct validators at one height is forced into the block at the next. AMP inherits Tendermint’s safety and liveness and its \(f < n/3\) threshold.
DAG-based protocols [35]–[38] are aimed at high throughput (and, in recent designs, low latency) rather than at the guarantees we require, and although several provide some notion of censorship resistance, they do not target the strict short-term censorship-resistance guarantee we require. Since every validator proposes each round, they are multiple-concurrent-proposer in a broad sense, but as Garimidi et al. [1] observe, amending them to provide short-term censorship resistance is not straightforward, as it appears to require giving up the optimistic responsiveness, which lets them commit at network speed in the good case (synchrony and an honest leader).
We thank Andrei Constantinescu, Andrea Canidio, and Babak Poorebrahim Gilkalaye for helpful discussions and feedback.
This appendix gives the formal treatment underlying the body. It restates the MCP problem precisely (10), defines our extreme-pipelining framework and proves that it solves MCP given conforming building blocks (11), and then instantiates the two building blocks: Chorus(12) and Conductor, together with its boundedness and recovery guarantees (13).
This section gives a precise, formal statement of the MCP problem that 2 introduced informally.
The MCP problem involves several abstract entities; we begin by introducing each of them precisely.
Each slot \(s\) carries three fields:
a number \(s.\mathsf{number}\), which is a positive integer;
a deadline \(s.\mathsf{deadline}\), which is a point in global time; and
a set of proposers \(s.\mathsf{proposers}\), which is a subset of the entire set of validators.
We emphasize that all three fields are read-only: a slot’s number, deadline, and proposers are fixed and never change. This is a deliberate, minor departure from 2, where we described the deadline as something the protocol determines. Treating deadlines as given — rather than as a protocol output — keeps the problem statement clean: otherwise the MCP problem definition would have to carry extra guarantees about how deadlines are produced. The protocol still adapts its timing not by moving deadlines but by skipping slots, as we make precise below.
For each \(i \in \mathbb{N}_{\geq 1}\), there exists exactly one slot \(s\) with \(s.\mathsf{number}= i\). Moreover, deadlines respect the slot order: for any two slots \(s_1, s_2\) with \(s_1.\mathsf{number}< s_2.\mathsf{number}\), we have \(s_1.\mathsf{deadline}\leq s_2.\mathsf{deadline}\). (For our extreme-pipelining framework, we additionally assume that consecutive deadlines are a fixed, known interval \(\tau\) apart, with \(\tau > 0\): that is, \(s.\mathsf{deadline}= s'.\mathsf{deadline}+ \tau\) for every slot \(s\) with \(s.\mathsf{number}> 1\), where \(s'\) is the slot with \(s'.\mathsf{number}= s.\mathsf{number}- 1\).) We sometimes refer to the starting time of a slot \(s\), by which we mean \(s.\mathsf{deadline}- \Delta\). Moreover, we require the first slot (the slot \(s\) with \(s.\mathsf{number}= 1\)) to have a deadline of at least \(\Delta\), so its starting time is non-negative; and since deadlines only grow with the slot number, every slot then has a non-negative starting time. (For our extreme-pipelining framework, we assume that the starting time of the first slot is \(0\).)
A proposal \(P\) carries three fields:
a slot \(P.\mathsf{slot}\);
a proposer \(P.\mathsf{proposer}\), which must be a proposer of the proposal’s slot (\(P.\mathsf{proposer}\in P.\mathsf{slot}.\mathsf{proposers}\)); and
a payload \(P.\mathsf{payload}\), an arbitrary string carrying the proposal’s content (e.g., its transactions).
We also introduce proposal vectors, which bundle one proposal per proposer of a slot. Concretely, a proposal vector \(V\) carries:
a slot \(V.\mathsf{slot}\); and
a mapping that assigns to each proposer \(p \in V.\mathsf{slot}.\mathsf{proposers}\) either a proposal or the special value \(\bot\) (meaning “no proposal”); we write \(V[p]\) for the proposal (or \(\bot\)) assigned to \(p\).
We impose one constraint on the aforementioned mapping: whenever \(V[p] \neq \bot\), the proposal \(V[p]\) must be \(p\)’s own proposal for that slot, i.e., \(V[p].\mathsf{slot}= V.\mathsf{slot}\) and \(V[p].\mathsf{proposer}= p\). Two proposal vectors are identical if and only if their slots are identical and their mappings are identical: \(V_1 = V_2\) exactly when \(V_1.\mathsf{slot}= V_2.\mathsf{slot}\) and \(V_1[p] = V_2[p]\) for every proposer \(p \in V_1.\mathsf{slot}.\mathsf{proposers}\). For our purposes a proposal vector is interchangeable with a block: a block can be obtained from a proposal vector by a fixed, deterministic transformation (ordering the proposals’ transactions, discarding invalid ones, and so on). Since this transformation is an application-level concern, irrelevant to consensus, and since proposal vectors make our later definitions cleaner, we phrase everything in terms of proposal vectors.
A log is a (possibly empty) ordered list of proposal vectors with strictly increasing slot numbers: if \(V_1\) precedes \(V_2\) in the log, then \(V_1.\mathsf{slot}.\mathsf{number}< V_2.\mathsf{slot}.\mathsf{number}\). For a log \(\mathcal{L}\), we write \(\mathcal{L}.\mathsf{length}\) to denote its number of proposal vectors, and \(\mathcal{L}[i]\) for its \(i\)-th proposal vector, where \(i \in [1, \mathcal{L}.\mathsf{length}]\). Notably, a log need not contain a proposal vector for every slot: some slots may be “skipped”, contributing no proposal vector to the log. In particular, \(\mathcal{L}.\mathsf{length}\) may be strictly less than \(\mathcal{L}[\mathcal{L}.\mathsf{length}].\mathsf{slot}.\mathsf{number}\). (Permitting such skips is the formal counterpart of the informal view in 2, where no slot is skipped and the schedule instead simply spaces consecutive slot deadlines apart in time. These two views — skipping slots here versus spacing deadlines there — are equivalent reformulations of the same underlying ledger, and we make the correspondence between them precise in 11.1.) For a proposal vector \(V\) and a log \(\mathcal{L}\), we write “\(V \in \mathcal{L}\)” to denote that \(V\) appears in \(\mathcal{L}\). Furthermore, we say that two logs \(\mathcal{L}_1\) and \(\mathcal{L}_2\) are consistent if and only if \(\mathcal{L}_1[i] = \mathcal{L}_2[i]\), for every \(i \in [1, \min(\mathcal{L}_1.\mathsf{length}, \mathcal{L}_2.\mathsf{length})]\); otherwise, they are inconsistent. (Any log is consistent with the empty log.)
The interface of the MCP problem is simple:
Input. For each slot \(s\), every proposer \(p \in s.\mathsf{proposers}\) holds a single proposal \(P\) for that slot (with \(P.\mathsf{slot}= s\) and \(P.\mathsf{proposer}= p\)).
Output. Each validator \(p_i\) maintains an append-only local log, denoted \(\mathsf{log}(p_i)\). Every correct validator initializes its local log to the empty log \([]\) at time \(0\), and we write \(\mathsf{log}(p_i, t)\) for the local log of \(p_i\) at time \(t\).
We now specify the correctness properties that any MCP protocol must satisfy, defining each in turn. The MCP problem inherits the standard guarantees of consensus — safety and liveness — and adds two more on top: censorship resistance and hiding.
As in any consensus protocol, the foundational property is safety: the logs of all correct validators must be mutually consistent at all times.
Definition 1 (Safety). A protocol satisfies safety* if and only if, for any two correct validators \(p_i, p_j\) and any two times \(t_1, t_2\), logs \(\mathsf{log}(p_i, t_1)\) and \(\mathsf{log}(p_j, t_2)\) are consistent.*
As noted in 10.1, logs may inherently skip slots: not every slot need contribute a proposal vector to a validator’s local log. However, liveness requires that this skipping cannot persist indefinitely after \(\mathrm{GST}\): there exists a post-\(\mathrm{GST}\) grace period after which every slot contributes a proposal vector to the local log of every correct validator. This precisely corresponds to the eventual stability guarantee we described informally in 2.
Definition 2 (\(\ell\)-Liveness). A protocol satisfies \(\ell\)-liveness, for some time duration \(\ell\), if and only if, for every slot \(s\) with \(s.\mathsf{deadline}- \Delta \geq \mathrm{GST} + \ell\) and every correct validator \(p_i\), there exist a proposal vector \(V\) and a time \(t\) such that (1) \(V.\mathsf{slot}= s\), and (2) \(V \in \mathsf{log}(p_i, t)\).
We underline that the parameter \(\ell\) is not a fixed universal constant but rather a measure of the quality of a protocol: it captures how quickly a protocol resumes producing proposal vectors after \(\mathrm{GST}\), and we naturally prefer protocols that achieve a smaller \(\ell\). Accordingly, \(\ell\) need not be a constant and may depend on the parameters of the model and the execution, such as \(n\), \(f\), \(\Delta\), and \(\mathrm{GST}\). For instance, a protocol whose grace period grows the longer the network remains asynchronous (e.g., under a linear or exponential backoff) is captured by an \(\ell\) that increases with \(\mathrm{GST}\).
Liveness ensures that slots are not skipped indefinitely after \(\mathrm{GST}\), but says nothing about the content of the finalized proposal vectors: a protocol that always finalizes empty proposal vectors (assigning \(\bot\) to every proposer) is live yet useless. We therefore additionally require censorship resistance: the proposal of a correct proposer for a slot cannot be suppressed by Byzantine validators, but must be included in the proposal vector finalized for that slot. Concretely, there exists a grace period \(c\) such that, for every slot \(s\) whose starting time is at least \(c\) after \(\mathrm{GST}\), the proposal vector finalized for \(s\) includes the proposals of all correct proposers of \(s\).
Definition 3 (\(c\)-Censorship Resistance). A protocol satisfies \(c\)-censorship resistance, for some time duration \(c\), if and only if, for every slot \(s\) with \(s.\mathsf{deadline}- \Delta \geq \mathrm{GST} + c\), every correct validator \(p_i\), and every correct proposer \(p_j \in s.\mathsf{proposers}\), there exist a proposal vector \(V\) and a time \(t\) such that (1) \(V.\mathsf{slot}= s\), (2) \(V[p_j] = P\), where \(P\) is the proposal of proposer \(p_j\) for slot \(s\), and (3) \(V \in \mathsf{log}(p_i, t)\).
As with the parameter \(\ell\) of \(\ell\)-liveness, the parameter \(c\) measures the quality of a protocol: it bounds how quickly the proposals of correct proposers are included after \(\mathrm{GST}\) (smaller is preferable), and may likewise depend on the model and execution parameters. We clarify the relationship between liveness and censorship resistance once more. The two differ in what they assume about the proposers of a slot. Liveness guarantees that, after the grace period, every slot eventually contributes a proposal vector to the log, regardless of whether its proposers are correct or faulty. Censorship resistance, in contrast, concerns the content of that proposal vector, and only constrains slots that have at least one correct proposer (since it speaks about proposals held by correct proposers). The two properties are thus complementary: liveness ensures that a slot contributes a proposal vector, while censorship resistance, for a slot with a correct proposer, ensures that the proposer’s proposal is included by that slot.
In defining the hiding property, the intuition we want to capture is that faulty proposers cannot submit proposals that depend on the contents of proposals submitted by honest proposers. However, there are some nuances that need to be addressed:
At the very least, such a property should imply “semantic security”, which would just say that the adversary does not learn the contents of the honest proposals before the deadline for submitting proposals passes.
Ideally, we want a stronger notion of security that captures “non-malleability”, which would say that the adversary cannot submit a proposal whose contents depend in some non-trivial way on an honest proposal. For example, suppose an honest proposal \(x\) is encrypted as \(y\). If the encryption scheme is malleable, an adversary may be able to compute an encryption \(y'\) of \(x+1\) without ever learning what \(x\) is. Our definition of security will capture this guarantee.
The most ambitious definition of security would say that each faulty proposer gets to submit (at most) one proposal (which does not depend on the honest proposals), and does not learn the contents of the honest proposals until the set of included proposals is fully decided. While such a definition could be realized, it would add significant latency to the protocol. For that reason, we propose a somewhat weaker definition that gives the adversary the ability to (1) have faulty proposers effectively submit multiple proposals (which do not depend on honest proposals), and (2) use the contents of the honest proposals to influence the decided set of included proposals.
To formalize the above, we make a simulation-based definition. This definition is in the spirit of Universal Composability (UC) [39], but we do not require the full machinery of the UC framework. Just as in the UC framework, we have a “real world” and an “ideal world”. In the real world, validators interact with the actual protocol. In the ideal world, there is an ideal functionality \(\mathcal{F}\) and a simulator (a.k.a.ideal-world adversary) \(\mathcal{S}\). Both worlds are driven by an environment \(\mathcal{Z}\) — an arbitrary efficient (i.e., probabilistic polynomial-time) entity that supplies the inputs to the honest validators and observes their outputs. The environment also plays the role of the adversary: in the real world it directly controls the faulty validators, whereas in the ideal world those faulty validators are instead controlled by the simulator \(\mathcal{S}\), with which \(\mathcal{Z}\) may interact. We assume static corruption: the set of faulty validators is fixed before the execution begins. The environment’s goal is to tell the two worlds apart. In the ideal world, all inputs from and outputs to honest validators pass directly between \(\mathcal{Z}\) and \(\mathcal{F}\), while inputs from and outputs to faulty validators pass between \(\mathcal{S}\) and \(\mathcal{F}\). Critically, the interface seen by the environment is the same in both worlds. It thus suffices to define the ideal functionality \(\mathcal{F}\) corresponding to our hiding property, which we do next. We describe \(\mathcal{F}\) for a single slot; the full functionality applies it to every slot.
Proposers, both honest and faulty, can input proposals to \(\mathcal{F}\), but only before the slot’s deadline.
Unlike an honest proposer, a faulty proposer may input several proposals (and not all at the same time).
When an honest proposer \(p_i\) inputs a proposal to \(\mathcal{F}\), \(\mathcal{F}\) informs \(\mathcal{S}\) that \(p_i\) has submitted a proposal, but divulges no information about the proposal to \(\mathcal{S}\) beyond the identity of \(p_i\) and the length of its proposal.
When the deadline passes, \(\mathcal{F}\) gives to \(\mathcal{S}\) the contents of all of the honest proposals.
Later, \(\mathcal{S}\) chooses the actual set \(S\) of proposals to be included in the proposal vector, and sends this to \(\mathcal{F}\). The set \(S\) must be a subset of the proposals that were input to the ideal functionality, and can include at most one proposal per proposer.
After sending \(S\) to \(\mathcal{F}\), \(\mathcal{S}\) may then instruct \(\mathcal{F}\) to output the proposal vector determined by \(S\) to individual validators (one at a time, in the order of its choosing).
Having specified \(\mathcal{F}\), we now state our hiding property: a protocol is hiding precisely when its real-world execution cannot be told apart from this ideal world.
Definition 4 (Hiding). A protocol satisfies hiding* if and only if there exists a simulator \(\mathcal{S}\) such that no efficient environment can distinguish the real world from the ideal world, where the ideal world is defined by the ideal functionality \(\mathcal{F}\) described above, applied to every slot.*
We now give the formal account of the extreme-pipelining framework, the framework we introduced informally in 3. Recall that Cadence, our concrete MCP protocol, is the specific instantiation of the extreme-pipelining framework obtained by taking Chorus as the slot consensus and Conductor as the orchestrator.
We now formally define the two abstract building blocks on which our extreme-pipelining framework relies: slot consensus and the orchestrator, introduced informally in 3.1 and 3.2, respectively.
The slot consensus primitive is specified formally in [mod:slotconsensus]; each of its instances is parameterized by a slot \(s\). The interface reflects the two roles a validator plays: a participant in the consensus process and, for designated slots, a proposer. On the participation side, validators explicitly signal when they start and stop contributing to an instance; between these two events we say that the validator is actively contributing. (We track this explicitly — though consensus protocols usually leave it implicit — because our analysis of the extreme-pipelining framework aims to bound how many instances a correct validator actively contributes to at any one time.) On the proposer side, designated validators additionally submit their proposals. Together, these participation signals and proposals form a validator’s inputs to the instance, while its sole output is a finalization: a validator may finalize a single proposal vector for slot \(s\).
The slot consensus primitive guarantees five correctness properties. Agreement and termination are the usual safety and liveness: correct validators never finalize conflicting proposal vectors, and if all correct validators start participating, then every correct validator eventually finalizes. Importantly, agreement constrains each validator individually as well: no correct validator finalizes two different proposal vectors, even on separate occasions. Slot safety requires that the finalized proposal vector \(V\) carry the correct slot identifier \(s\). (Slot safety is the one property we omitted from the informal overview in 3.1, as it is a self-evident condition that reveals nothing conceptually interesting about the primitive.) Proposal inclusion guarantees that every proposal submitted by a correct proposer by the slot’s starting time appears in \(V\) (under synchrony). Hiding, finally, is the slot-level counterpart of the MCP hiding guarantee (4), specialized to the instance’s slot \(s\).
Parameters:
\(s \in \mathsf{Slot}\)
Interface:
input \(\mathsf{participate}()\): a validator starts participating.
input \(\mathsf{abandon}()\): a validator stops participating.
input \(\mathsf{propose}(P \in \mathsf{Proposal})\): a proposer submits its proposal \(P\).
output \(\mathsf{finalize}(V \in \mathsf{PVector})\): a validator finalizes proposal vector \(V\).
Properties:
Agreement: If a correct validator \(p_i\) finalizes a proposal vector \(V_i\) and a correct validator \(p_j\) finalizes a proposal vector \(V_j\), then \(V_i = V_j\).
Termination: If every correct validator starts participating, then every correct validator eventually finalizes a proposal vector.
Slot safety: If a correct validator finalizes a proposal vector \(V\), then \(V.\mathsf{slot}= s\).
Proposal inclusion: If \(s.\mathsf{deadline}- \Delta \geq \mathrm{GST}\), a correct proposer \(p_j \in s.\mathsf{proposers}\) proposes its proposal \(P\) at time \(s.\mathsf{deadline}- \Delta\), and a correct validator finalizes a proposal vector \(V\), then \(V[p_j] = P\).
Hiding: Hiding as in 4, specialized to this instance’s single slot \(s\).
[mod:orchestrator952] provides the orchestrator’s full specification. Validators supply a single input: upon completing their work on a slot \(s\), a validator notifies the orchestrator by completing \(s\). The orchestrator, in turn, produces a single output: for an upcoming slot \(s'\), it may instruct a validator to open \(s'\); there is no separate “skip” output. Instead, we informally say that a slot a validator never opens is skipped. It is worth reconciling this interface with the informal overview (3.2), where we described the orchestrator’s job as determining the deadlines of slots. In the formal model, by contrast, slots — and all information about them, including their deadlines — are fixed and read-only (10.1). The two views are equivalent: instead of delaying a slot’s deadline, as the informal orchestrator would, the formal orchestrator simply skips the intervening slots and opens the slot whose (fixed) deadline already coincides with the desired, later time. Skipping slots is thus the formal counterpart of pushing deadlines back. Throughout the rest of the paper, we adopt the \(\tau\)-spaced deadline structure foreshadowed in 10.1: consecutive slots’ deadlines are a fixed, known interval \(\tau\) apart.
The orchestrator must satisfy five correctness properties. The first three establish the correctness baseline. Totality ensures that an opening propagates to all correct validators: if any correct validator opens a slot \(s\), then every correct validator eventually opens \(s\) as well. Integrity ensures that no correct validator opens the same slot more than once, and that no slot is opened before its starting time (though it may be opened after that time). Monotonicity ensures that validators open slots in strictly increasing order. Let us underline a stronger consequence of these three properties together: whenever a correct validator \(p_i\) opens a slot \(s\), every correct validator opens exactly the same set of slots with number at most \(s.\mathsf{number}\). To see this, consider any slot \(s'\) with \(s'.\mathsf{number}\leq s.\mathsf{number}\). First, note that \(s'\) can be opened at most once (due to the integrity property). If \(p_i\) opens \(s'\), then by totality every correct validator opens \(s'\) as well. Otherwise, \(p_i\) skips \(s'\); that is, it opens \(s\) without ever opening \(s'\) (here, \(s'.\mathsf{number}< s.\mathsf{number}\)). We argue that then no correct validator opens \(s'\) either: suppose some correct validator \(p_j\) did: by totality, \(p_i\) would open \(s'\) too, and by monotonicity — as \(s'.\mathsf{number}< s.\mathsf{number}\) — it would have to do so before opening \(s\), which is impossible, since \(p_i\) opened \(s\) without ever opening \(s'\). In both cases, \(p_i\) and every other correct validator agree on whether \(s'\) is opened, which establishes the claim.
The remaining two properties are the most involved and the most distinctive: rather than establishing a correctness baseline, as the three above do, they measure the quality of an orchestrator. The first, \(\mathcal{B}\)-boundedness, bounds how many slots may be simultaneously open but not yet completed at any given time; the second, \(\mathcal{R}\)-recovery, bounds how long after network stabilization it takes before the orchestrator opens all upcoming slots on time. We motivate \(\mathcal{B}\)-boundedness before stating it, since the reason to bound the open slots is memory.
In our extreme-pipelining framework (as we detail in 11.2), a validator opens a fresh slot consensus instance for every slot it does not skip, so the more slots stay open, the more memory it must devote to them; we therefore want to keep each validator’s memory bounded. Formally reasoning about memory, however, is delicate. To make a memory bound meaningful, one must work in a model with an unreliable network: under a reliable network, undelivered messages must be buffered until delivery, which is fundamentally at odds with bounding memory. Yet adopting an unreliable network would introduce considerable additional machinery (retransmission, garbage collection, checkpointing, and similar mechanisms), whose techniques are, at least in theory, standard and largely orthogonal to our contribution. As memory management is not where we innovate, we retain the reliable-network assumption throughout, and consequently do not reason about memory consumption directly. Instead, we capture the same concern through a clean abstraction: the number of slot consensus instances in which a validator is actively participating at any given time, namely those it has started but not yet abandoned. Bounding this number bounds the resources a validator devotes to ongoing instances, and thus serves as our proxy for memory. We call this property bounded concurrency. Reassuringly, it is not a guarantee we need to establish on its own: a validator participates in a slot’s consensus instance exactly while that slot is open but not yet completed, so bounded concurrency follows directly from the \(\mathcal{B}\)-boundedness property we define next — and with the very same bound \(\mathcal{B}\).
This property limits how far ahead a validator may open slots without completing earlier ones: at any point in time, only the last \(\mathcal{B}\) slots opened (by slot number) may still be pending completion, while all previously opened slots must already have been completed. In the context of our extreme-pipelining framework, since completing a slot means its proposal vector has been finalized, \(\mathcal{B}\)-boundedness implies that every correct validator has already finalized all but the last \(\mathcal{B}\) opened slots, yielding a complete log prefix that trails the frontier by at most \(\mathcal{B}\) slots. Here, \(\mathcal{B} \in \mathbb{N}_{\geq 0} \cup \{\infty\}\): a finite \(\mathcal{B}\) bounds the number of simultaneously open slots, whereas \(\mathcal{B} = \infty\) imposes no such bound and thus captures orchestrators that open slots arbitrarily far ahead without completing earlier ones. We underline once more that \(\mathcal{B}\) measures the quality of a protocol (smaller is preferable) and need not be a constant: it may depend on the model and execution parameters, such as \(n\), \(f\), \(\Delta\), and \(\mathrm{GST}\).
This property captures what we require once the network stabilizes after a period of asynchrony. Two demands must be met jointly. First, validators must stop skipping slots: once the network recovers, all upcoming slots are opened rather than skipped. Second, and more subtly, validators must open upcoming slots “on time”, namely at exactly their starting time. Opening a slot on time is a natural expectation: validators may have time-sensitive work to perform upon opening, making the precise timing of this event crucial. To see why this matters, we illustrate with the concrete example of our extreme-pipelining framework. There, the proposal-inclusion property of slot consensus guarantees that every proposal submitted by a correct proposer at the slot’s starting time appears in the finalized proposal vector. If the slot is opened late, the proposer misses the starting time, proposal inclusion no longer applies, and Byzantine validators may suppress correct proposers’ proposals entirely. This would cause slot consensus to finalize a proposal vector devoid of any correct proposer’s proposals — potentially an empty one — which, in the blockchain setting we target, leaves the application state unchanged and is therefore no more useful than a skipped slot, yet far costlier to produce. An orchestrator that merely guarantees eventual opening, without the timing condition, would therefore be insufficient. Combining both demands, we say the orchestrator satisfies \(\mathcal{R}\)-recovery if both conditions hold for every slot whose starting time is at least \(\mathcal{R}\) after \(\mathrm{GST}\). Unlike \(\mathcal{B}\), \(\mathcal{R}\) must be finite; but, like \(\mathcal{B}\), it captures protocol quality (smaller is preferable) and may be any function of the model and execution parameters.
Interface:
input \(\mathsf{complete}(s \in \mathsf{Slot})\): a validator completes slot \(s\).
output \(\mathsf{open}(s \in \mathsf{Slot})\): a validator opens slot \(s\).
Properties:
Totality: If some correct validator opens any slot \(s\), then every correct validator eventually opens \(s\).
Integrity: No correct validator opens the same slot \(s\) more than once. Moreover, no correct validator opens slot \(s\) before time \(s.\mathsf{deadline}- \Delta\).
Monotonicity: If a correct validator opens any two slots \(s\) and \(s'\) with \(s.\mathsf{number}< s'.\mathsf{number}\), then it opens \(s\) before opening \(s'\).
\(\mathcal{B}\)-Boundedness: Let \(\mathcal{B} \in \mathbb{N}_{\geq 0} \cup \{\infty\}\). For every correct validator \(p_i\) and every time \(t\), if \(p_i\) has opened \(k\) slots by time \(t\) (ordered by slot number as \(s_1, \ldots, s_k\)), then every \(s_j\) with \(j \leq k - \mathcal{B}\) has already been completed by \(p_i\).
\(\mathcal{R}\)-Recovery: Let \(\mathcal{R}\) be a time duration. For every slot \(s\) with \(s.\mathsf{deadline}- \Delta \geq \mathrm{GST} + \mathcal{R}\), every correct validator opens slot \(s\) and it does so at time \(s.\mathsf{deadline}- \Delta\).
We now describe how the slot consensus and the orchestrator compose into the extreme-pipelining framework. Each correct validator maintains a single orchestrator instance \(\mathcal{O}\) and one slot consensus instance \(\mathcal{S}[s]\) per slot \(s\), and the framework is simply the glue that wires these components together. Throughout, \(\mathcal{O}\) is assumed to achieve \(\mathcal{B}\)-boundedness and \(\mathcal{R}\)-recovery for some \(\mathcal{B} \in \mathbb{N}_{\geq 0} \cup \{\infty\}\) and some time duration \(\mathcal{R}\). The pseudocode is given in 14, traced from the perspective of a correct validator \(p_i\), with a complementary visual overview in 15.
Upon starting, \(p_i\) begins executing \(\mathcal{O}\) (line [line:start-orch]), which drives the protocol forward by deciding, for each slot \(s\), whether to open it; recall that the orchestrator has no explicit skip output, so a slot it never opens is implicitly skipped. When \(\mathcal{O}\) opens slot \(s\) (line [line:upon-open]), \(p_i\) records \(s\) as opened and starts participating in \(\mathcal{S}[s]\) (lines [line:opened-update]–[line:participate]); if \(p_i\) is a designated proposer (i.e., \(p_i \in s.\mathsf{proposers}\)), it additionally submits its proposal \(P_s\) for slot \(s\) to \(\mathcal{S}[s]\) (lines [line:proposer-check]–[line:propose]). Whereas opening is signaled explicitly by \(\mathcal{O}\), skipping is not: when \(p_i\) opens \(s\), it records every smaller-numbered slot it has not opened as skipped (line [line:implicit-skip]). For a skipped slot, no consensus instance is spawned and no proposal vector is produced.
Crucially, multiple slot consensus instances may run concurrently and may finalize out of order. Upon \(\mathcal{S}[s]\) finalizing a proposal vector \(V\), for some slot \(s\) (line [line:upon-finalize]), \(p_i\) places \(V\) in a pending set (line [line:pending-add]), notifies \(\mathcal{O}\) that slot \(s\) has been completed (line [line:complete]), and stops participating in \(\mathcal{S}[s]\) (line [line:abandon]). Pending proposal vectors are then appended to \(p_i\)’s local log in slot-number order: a pending proposal vector \(V'\) is appended (line [line:append]) as soon as every slot with a strictly smaller number than \(V'.\mathsf{slot}.\mathsf{number}\) has already been resolved, that is, either recorded as skipped or finalized and appended to \(\mathit{log}_i\) (line [line:func-ready-to-append-return]). This ordering condition ensures that \(\mathit{log}_i\) is indeed a log in the sense of 10.1: proposal vectors appear in strictly increasing order of slot number.
We wish to highlight that extreme pipelining is intentionally presented as a generic framework, not merely as a vehicle for our specific instantiation. The framework is deliberately minimal: it prescribes nothing more than the interaction between a slot consensus instance and an orchestrator, leaving both components entirely abstract and subject only to the properties stated in [mod:slotconsensus] [mod:orchestrator952]. As a direct consequence, every property of the framework, including its resilience to Byzantine faults, is inherited from the properties of the underlying components rather than hard-coded into the framework itself. In this paper, we indeed focus on the standard Byzantine fault model with \(n = 3f + 1\) validators. However, the framework imposes no such constraint: to obtain a different protocol, it suffices to supply a slot consensus and an orchestrator satisfying the respective module specifications, and the correctness of the resulting protocol follows immediately from their properties, with no additional argument needed. This flexibility extends well beyond the resilience threshold. For instance, one may target a different threshold, or trade resilience for another guarantee such as improved performance, simply by instantiating the two components accordingly. One may also depart from the multiple-concurrent-proposers setting entirely: instantiating the slot consensus with a single designated proposer per slot recovers a classical “single-leader” blockchain. Likewise, one is free to plug in a linear-communication slot consensus, and nothing else changes. In every case, the framework itself is untouched; only the two plugged-in components change.
In this subsection, we prove that the extreme-pipelining framework introduced above, composed of the slot consensus and the orchestrator, solves the MCP problem.
We first establish that the safety property is satisfied.
Lemma 1 (Safety). 14 satisfies safety.
Proof. Suppose, for contradiction, that two correct validators \(p_i, p_j\) and two times \(t_1, t_2\) exist such that \(\mathsf{log}(p_i, t_1)\) and \(\mathsf{log}(p_j, t_2)\) are inconsistent. Let \(k \in \mathbb{N}_{\geq 1}\) be the smallest index at which the two logs differ, i.e., \(\mathsf{log}(p_i, t_1)[k] \neq \mathsf{log}(p_j, t_2)[k]\); such an index exists by the definition of inconsistency. Let \(V_1 = \mathsf{log}(p_i, t_1)[k]\) and \(V_2 = \mathsf{log}(p_j, t_2)[k]\). We distinguish two possible cases:
Let \(V_1.\mathsf{slot}\neq V_2.\mathsf{slot}\). The \(\mathsf{ready\_to\_append}\) condition (line [line:func-ready-to-append]) ensures that a validator appends a proposal vector for a slot only after every smaller-numbered slot is either skipped or already covered by a proposal vector in its log; hence the slot at position \(k\) is determined by how each validator resolved all preceding slots. Since both logs agree at positions \(1, \ldots, k-1\), the two validators resolved all those slots identically. Assume, without loss of generality, that \(V_1.\mathsf{slot}.\mathsf{number}< V_2.\mathsf{slot}.\mathsf{number}\). Then, \(p_i\) appended a proposal vector for slot \(V_1.\mathsf{slot}\), and so opened that slot. Validator \(p_j\), in contrast, appended a proposal vector for the higher-numbered slot \(V_2.\mathsf{slot}\) at position \(k\) without ever appending one for \(V_1.\mathsf{slot}\), and so did not open \(V_1.\mathsf{slot}\). By the monotonicity property, having already opened the higher-numbered slot \(V_2.\mathsf{slot}\), \(p_j\) never opens \(V_1.\mathsf{slot}\). This contradicts the totality property of the orchestrator \(\mathcal{O}\): since the correct validator \(p_i\) opened \(V_1.\mathsf{slot}\), every correct validator — including \(p_j\) — must eventually open it.
Let \(V_1.\mathsf{slot}= V_2.\mathsf{slot}\). Since \(V_1 \neq V_2\) while \(V_1.\mathsf{slot}= V_2.\mathsf{slot}\), the two proposal vectors must differ in their mappings. Note that a validator appends a proposal vector only upon finalizing it (line [line:upon-finalize]), and by the slot safety property of slot consensus a proposal vector finalized for slot \(V_1.\mathsf{slot}\) comes from the instance \(\mathcal{S}[V_1.\mathsf{slot}]\). Thus \(p_i\) and \(p_j\) finalized \(V_1\) and \(V_2\), respectively, from the same instance \(\mathcal{S}[V_1.\mathsf{slot}]\), contradicting its agreement property, which forbids two correct validators from finalizing different proposal vectors. 0◻
◻
Next, we prove that the extreme-pipelining framework satisfies liveness.
Lemma 2 (Liveness). 14 satisfies \(\mathcal{R}\)-liveness.
Proof. Fix any slot \(s\) with \(s.\mathsf{deadline}- \Delta \geq \mathrm{GST} + \mathcal{R}\). By the \(\mathcal{R}\)-recovery property of the orchestrator \(\mathcal{O}\), every correct validator eventually opens \(s\) (line [line:upon-open]). By the monotonicity property of \(\mathcal{O}\), before opening \(s\), every correct validator has already resolved — opened or (implicitly) skipped — every slot \(s'\) with \(s'.\mathsf{number}< s.\mathsf{number}\). Moreover, by the totality property of \(\mathcal{O}\), for every slot \(s^{\star}\) with \(s^{\star}.\mathsf{number}\in [1, s.\mathsf{number}]\), either (1) every correct validator opens \(s^{\star}\) and begins participating in the slot consensus \(\mathcal{S}[s^{\star}]\) (line [line:participate]), or (2) no correct validator opens \(s^{\star}\), i.e., every correct validator skips it (line [line:implicit-skip]). In case (1), since every correct validator starts participating in \(\mathcal{S}[s^{\star}]\), the termination property of \(\mathcal{S}[s^{\star}]\) guarantees that every correct validator eventually finalizes a proposal vector for \(s^{\star}\) (line [line:upon-finalize]). Inductively, starting from slot number \(1\), each correct validator resolves slot \(s^{\star}\) (by either skipping it or finalizing and appending a proposal vector for it) before proceeding to slot \(s^{\star} + 1\): once slot \(s^{\star}\) is resolved, the \(\mathsf{ready\_to\_append}\) condition (line [line:func-ready-to-append-return]) for a proposal vector associated with slot \(s^{\star} + 1\) is satisfied, and the next proposal vector can be appended (line [line:append]). Applying this argument inductively up to slot number \(s.\mathsf{number}- 1\), every correct validator eventually resolves all preceding slots, at which point the \(\mathsf{ready\_to\_append}\) condition for a proposal vector associated with slot \(s\) is satisfied. Therefore, every correct validator eventually appends a proposal vector \(V\) with \(V.\mathsf{slot}= s\) (line [line:append]), establishing \(\mathcal{R}\)-liveness. 0◻ ◻
As noted earlier, safety and liveness are standard properties, not specific to the MCP problem. The fact that the extreme-pipelining framework satisfies both underscores its applicability beyond the MCP setting. We now turn to the MCP-specific properties.
We next prove the censorship resistance property.
Lemma 3 (Censorship resistance). 14 satisfies \(\mathcal{R}\)-censorship resistance.
Proof. Fix any slot \(s\) with \(s.\mathsf{deadline}- \Delta \geq \mathrm{GST} + \mathcal{R}\), any correct validator \(p_i\), and any correct proposer \(p_j \in s.\mathsf{proposers}\); let \(P_s\) denote \(p_j\)’s proposal for slot \(s\). To prove the lemma, we must exhibit a proposal vector \(V\) and a time \(t\) such that \(V.\mathsf{slot}= s\), \(V[p_j] = P_s\), and \(V \in \mathsf{log}(p_i, t)\).
By the \(\mathcal{R}\)-liveness of the extreme-pipelining framework (2), \(p_i\) eventually finalizes (line [line:upon-finalize]) and appends (line [line:append]) a proposal vector \(V\) with \(V.\mathsf{slot}= s\) to its local log, at some time \(t\). By the \(\mathcal{R}\)-recovery property of the orchestrator \(\mathcal{O}\), \(p_j\) opens slot \(s\) at time \(s.\mathsf{deadline}- \Delta\) (line [line:upon-open]) and, being a correct proposer, submits its proposal \(P_s\) to \(\mathcal{S}[s]\) at that same time (line [line:propose]). Since \(s.\mathsf{deadline}- \Delta \geq \mathrm{GST}\) and \(p_j\) proposes by the slot’s starting time, the proposal-inclusion property of \(\mathcal{S}[s]\) ensures that the proposal vector \(V\) finalized by \(p_i\) satisfies \(V[p_j] = P_s\), which concludes the proof. 0◻ ◻
Next, we prove hiding, which reduces to the hiding of each slot consensus instance.
Lemma 4 (Hiding). 14 satisfies hiding.
Proof. A correct proposer submits its proposal for slot \(s\) solely to \(\mathcal{S}[s]\) (line [line:propose]), and a correct validator appends a proposal vector for slot \(s\) only upon \(\mathcal{S}[s]\) finalizing it (lines [line:upon-finalize] and [line:append]). Proposal contents are thus observable only through the per-slot instances, so hiding follows by composing the hiding of each. 0◻ ◻
Recall from 11.1.2.1 that we do not reason about memory directly, but use the number of slot consensus instances a validator actively participates in as a proxy for its memory consumption. This number stays bounded:
Lemma 5 (Bounded concurrency). 14 satisfies \(\mathcal{B}\)-bounded concurrency. That is, for every correct validator \(p_i\) and every time \(t\), at most \(\mathcal{B}\) slot consensus instances \(\mathcal{S}[s]\) exist such that \(p_i\) has started participating in \(\mathcal{S}[s]\) by time \(t\) but has not yet abandoned \(\mathcal{S}[s]\) by time \(t\).
Proof. For any slot \(s\), validator \(p_i\) starts participating in \(\mathcal{S}[s]\) upon opening \(s\) (line [line:upon-open]–[line:participate]), and abandons \(\mathcal{S}[s]\) immediately after completing \(s\) (line [line:abandon]). Hence, \(\mathcal{S}[s]\) is active for \(p_i\) at time \(t\) if and only if \(p_i\) has opened \(s\) but not yet completed it by time \(t\). The bound of \(\mathcal{B}\) therefore follows directly from the \(\mathcal{B}\)-boundedness property of the orchestrator \(\mathcal{O}\). \(\square\) ◻
This section presents Chorus, the protocol that instantiates the slot-consensus primitive of Cadence. We introduced Chorus informally, and in some detail, in 4; here we give its full specification.
Chorus builds on several standard cryptographic primitives, whose notation we fix here.
We assume a collision-resistant cryptographic hash function \[H : \{0,1\}^* \rightarrow \{0,1\}^\lambda .\]
Each validator \(p_i\) holds a signing keypair \((sk_i,pk_i)\). We use the following operations:
\(\mathsf{Sign}_i(m)\): On input message \(m\), outputs a digital signature \(\sigma\) computed using \(sk_i\).
\(\mathsf{Verify}_{pk_i}(m,\sigma)\): Outputs \(\top\) if \(\sigma\) is a valid signature on \(m\) under public key \(pk_i\), and outputs \(\bot\) otherwise.
A set of signatures \(\{\sigma_i\}_{i \in \mathcal{I}}\) on a common message \(m\) can be aggregated into a short multi-signature \(\Sigma\) verifiable against \(\{pk_i\}_{i \in \mathcal{I}}\).
The encrypted proposal is erasure-coded to enable distributed availability. Throughout the section, we rely on the following operations:
\(\mathsf{Encode}(c)\): On input ciphertext \(c\), outputs \(n\) fragments \((d_1,\ldots,d_n)\) such that any subset of at least \(f+1\) fragments is sufficient to reconstruct \(c\).
\(\mathsf{Decode}(\{d_i\})\): On input a set of fragments \(\{d_i\}\) with \(|\{d_i\}| \ge f+1\), outputs the reconstructed ciphertext \(c\), or \(\bot\) if decoding fails.
Merkle trees are used to commit to encoded payload chunks. We assume the following interface:
\(\mathsf{MerkleRoot}(c_1,\ldots,c_n)\): Outputs the root \(\mathrm{\texttt{merkle\_root}}\) of the Merkle tree over leaves \(c_1,\ldots,c_n\).
\(\mathsf{MerkleProof}(i)\): Outputs a Merkle authentication path \(\pi_i\) for chunk \(c_i\).
\(\mathsf{VerifyMerkle}(\mathrm{\texttt{merkle\_root}},c_i,\pi_i)\): Outputs \(\top\) if \(\pi_i\) is a valid proof that \(c_i\) is a leaf in the Merkle tree with root \(\mathrm{\texttt{merkle\_root}}\), and outputs \(\bot\) otherwise.
For the simulation argument in §12.2, we model several hash functions as distinct random oracles, including the identity hash \(H_{\mathsf{id}}\), the pad-derivation hash \(H_{\mathsf{pad}}\), and the Merkle hash \(H_{\mathsf{Merkle}}\). The simulator programs \(H_{\mathsf{pad}}\); it does not program \(H_{\mathsf{Merkle}}\), but it inspects the adversary’s Merkle-oracle queries to recover the leaves committed by a submitted Merkle root. In an implementation, all such functions should be realized through a standard domain-separated hash API, using unambiguous encodings of all inputs. Untagged calls to a raw hash function, or ad hoc concatenation of values before hashing, should be avoided. The Merkle hash domain should be separate from the identity-hash and pad-derivation domains; ideally, the Merkle implementation should also distinguish leaf hashing from internal-node hashing.
This subsection describes the cryptographic mechanism used to hide proposal contents until the slot’s deadline. The important point is that the threshold operation is not proposal-specific decryption. Instead, validators release shares of the slot secret key: the identity secret key for an identity derived from the slot \(s\), with standard domain separation for the protocol instance. Once this slot key is reconstructed, anyone can open any well-formed proposal ciphertext for that slot.
We use an identity-based key encapsulation mechanism (IB-KEM) whose private-key extraction algorithm is implemented by a distributed private-key generator. The ordinary IB-KEM syntax is \[(\mathit{mpk},\mathit{msk}) \leftarrow \mathsf{Setup}(1^\lambda), \qquad \mathit{sk}_{\mathit{id}} \leftarrow \mathsf{Extract}(\mathit{msk},\mathit{id}),\] \[(Z,C_{\mathsf{kem}}) \leftarrow \mathsf{Encap}(\mathit{mpk},\mathit{id}), \qquad Z \leftarrow \mathsf{Decap}(\mathit{sk}_{\mathit{id}},\mathit{id},C_{\mathsf{kem}}).\] Here, \(Z\) is the KEM’s raw key and \(C_{\mathsf{kem}}\) is the public encapsulation. The raw key need not itself be a bit string; in the pairing-based instantiation below it is a target-group element. Bit strings used by the protocol are derived by hashing \(Z\) with appropriate domain separation.
The distributed version replaces only \(\mathsf{Extract}\). A \((t,n)\)-distributed extraction mechanism consists of algorithms \[(\mathit{mpk},\mathit{vk},\mathit{st}_1,\ldots,\mathit{st}_n) \leftarrow \mathsf{DSetup}(1^\lambda,n,t),\] \[\sigma_i \leftarrow \mathsf{ShareExtract}(\mathit{st}_i,\mathit{id}), \qquad b \leftarrow \mathsf{VerifyShare}(\mathit{mpk},\mathit{vk},\mathit{id},i,\sigma_i),\] \[\mathit{sk}_{\mathit{id}} \leftarrow \mathsf{CombineExtract}(\mathit{mpk},\mathit{vk},\mathit{id},\{(i,\sigma_i)\}_{i\in I}), \qquad |I|\ge t .\] After combining extraction shares to obtain \(\mathit{sk}_{\mathit{id}}\), decapsulation is the ordinary non-threshold operation. Thus extraction shares depend only on the identity \(\mathit{id}\), not on any particular proposal ciphertext. Correctness requires that honestly generated and verified shares combine to the same \(\mathit{sk}_{\mathit{id}}\) as the ideal extraction algorithm. Robustness requires that adversarial shares that pass verification contribute correctly, so that any verified qualified set either combines to the unique correct slot key or is rejected.
The security property we need is weaker than full KEM key indistinguishability. We require unpredictability: before enough valid extraction shares have been released for an identity \(\mathit{id}\), no efficient adversary corrupting fewer than the threshold number of extraction servers can compute the raw key \(Z\) belonging to a fresh encapsulation under \(\mathit{id}\), except with negligible probability. Equivalently, if a later layer derives a pad as a random-oracle value \(H(Z,\ldots)\), then any adversary that learns this pad before release must have queried the random oracle at the hidden point involving \(Z\).
A concrete instantiation is obtained from the Boneh–Franklin/BLS pairing construction. Let \(G\) be a generator, let \(a\) be the master secret, and let the master public key contain \(aG\). For identity \(\mathit{id}\), let \(Q_{\mathit{id}}=H_{\mathsf{id}}(\mathit{id})\) and \(\mathit{sk}_{\mathit{id}}=aQ_{\mathit{id}}\). A distributed private-key generator can Shamir-share \(a\): server \(i\) returns the verified extraction share \(\sigma_i=a_iQ_{\mathit{id}}\), and interpolation reconstructs \(aQ_{\mathit{id}}\). Encapsulation chooses \(r\) at random, publishes \(C_{\mathsf{kem}}=U=rG\), and computes \[Z=e(Q_{\mathit{id}},aG)^r .\] Decapsulation with the reconstructed identity secret key computes \[Z=e(\mathit{sk}_{\mathit{id}},U)=e(aQ_{\mathit{id}},rG).\] Unpredictability of this raw key follows, in the random-oracle model for the identity hash, from the standard computational-BDH assumption and the usual Boneh–Franklin proof strategy. The protocol never uses the target-group element \(Z\) directly as a symmetric key; it hashes \(Z\) with appropriate domain separation.
For a slot \(s\), let \(\mathit{id}_s\) be the canonical encoding of \(s\) as an IB-KEM identity. A proposer \(P\) encapsulates to this identity, obtaining \[(Z,C_{\mathsf{kem}}) \leftarrow \mathsf{Encap}(\mathit{mpk},\mathit{id}_s).\] It then derives a pad from the raw key, the slot, the proposer identity, and any additional public context bound to this encryption: \[K = H_{\mathsf{pad}}(Z,s,P,\mathsf{ctx}).\] The inputs \(Z\), \(s\), and \(P\) are essential. The value \(\mathsf{ctx}\) denotes any remaining public context for this encryption, and may include \(C_{\mathsf{kem}}\), protocol-version information, and deployment or chain identifiers. If the proposal payload is \(M\), the encrypted payload is the xor mask \[C_{\mathsf{pay}} = K \oplus M,\] where \(H_{\mathsf{pad}}\) is viewed as a hash-to-bytes or XOF-style oracle whose output is expanded as needed to match the payload length. We deliberately use this xor-with-random-oracle form, rather than an authenticated symmetric encryption scheme, because it gives the simulator the equivocation needed in the hiding proof: an honestly generated ciphertext can initially be a random bit string and later be made to open to the ideal-world proposal by programming \(H_{\mathsf{pad}}\) at the appropriate point.
The object encoded for data availability contains at least \((C_{\mathsf{kem}},C_{\mathsf{pay}})\) and the necessary public context. This object is erasure-coded, the fragments are committed by a Merkle tree, and the proposer signs the resulting Merkle root. Thus the submitted encrypted proposal is fixed by a signed Merkle root, not by a plaintext. A proposal is considered submitted before the deadline only if at least one honest validator receives, before \(s.\mathsf{deadline}\), a valid signature by the claimed proposer on the corresponding Merkle root. Signature unforgeability ensures that, except with negligible probability, the adversary cannot create a submitted proposal that appears to come from an honest proposer.
At the slot’s deadline, validators publish extraction shares \[\sigma_i \leftarrow \mathsf{ShareExtract}(\mathit{st}_i,\mathit{id}_s)\] for the slot identity. Once enough verified shares have been collected, anyone computes \[\mathit{sk}_{\mathit{id}_s} \leftarrow \mathsf{CombineExtract}(\mathit{mpk},\mathit{vk},\mathit{id}_s,\{(i,\sigma_i)\}_{i\in I}).\] The same reconstructed slot key opens all well-formed proposal encapsulations for slot \(s\): for each decoded proposal ciphertext, parties compute \[Z \leftarrow \mathsf{Decap}(\mathit{sk}_{\mathit{id}_s},\mathit{id}_s,C_{\mathsf{kem}}), \qquad M = C_{\mathsf{pay}} \oplus H_{\mathsf{pad}}(Z,s,P,\mathsf{ctx}).\] The released values are therefore extraction shares for the slot identity, not decryption shares tied to individual proposals.
We sketch why this realizes the hiding property (4). The proof is best viewed as an ordering argument at the deadline of a fixed slot \(s\); no infinitesimal notion of time is needed. At the deadline transition, corrupt proposals that were already submitted are fixed first. Only after those corrupt inputs have been determined does the simulator obtain the honest proposals from the ideal functionality.
Before the deadline, the simulator handles honest proposers as follows. For each honest proposer, it chooses the public KEM encapsulation as in the real protocol, but it chooses the encrypted payload \(C_{\mathsf{pay}}\) as a uniformly random bit string of the appropriate length. It erasure-codes the resulting encrypted object, computes the Merkle root, signs the root as the honest proposer, and delivers fragments as prescribed by the protocol. At this point the simulator does not yet know the honest plaintext proposal.
At the deadline transition, the simulator first identifies the corrupt submissions. A corrupt submission is eligible only if some honest validator received, before \(s.\mathsf{deadline}\), a valid signature on a Merkle root under the verification key of the claimed corrupt proposer. The simulator then walks back from this root through the transcript of queries to the Merkle random oracle \(H_{\mathsf{Merkle}}\). It must recover all leaves under the signed root. These leaves must be valid erasure-code fragments and must form a complete codeword encoding a well-formed encrypted proposal. If any leaves are missing, if the leaves do not form a codeword, or if the decoded object is malformed, the simulator does not treat the root as a properly submitted proposal. The simulator uses the Merkle oracle only in this inspection mode; unlike \(H_{\mathsf{pad}}\), it is not programmed.
For each well-formed corrupt ciphertext submitted by a corrupt proposer \(P'\), the simulator obtains the released slot key \(\mathit{sk}_{\mathit{id}_s}\), decapsulates \(C_{\mathsf{kem}}\), and obtains the raw key \(Z\). It then considers the pad-oracle point \[q=(Z,s,P',\mathsf{ctx}).\] If \(H_{\mathsf{pad}}(q)\) has already been defined, the simulator uses that existing value. If it has not been defined, the simulator programs it to a fresh random string. In either case it sets \[M' = C_{\mathsf{pay}} \oplus H_{\mathsf{pad}}(q)\] and submits this \(M'\) to the ideal functionality as the corrupt proposer’s input for slot \(s\). This is the only possible behavior consistent with the already fixed ciphertext and the random-oracle transcript: if the adversary queried the point, the answer is fixed; if it did not, a fresh random answer has exactly the distribution of a random oracle answer.
Only after all such corrupt inputs have been submitted does the simulator receive the honest proposals from the ideal functionality. For each honest proposer \(P\), the simulator now knows both the previously fixed random ciphertext \(C_{\mathsf{pay}}\) and the desired plaintext \(M\). It programs the corresponding pad-oracle point by setting \[H_{\mathsf{pad}}(Z,s,P,\mathsf{ctx}) := C_{\mathsf{pay}} \oplus M .\] This makes the already disseminated encrypted payload decrypt to the honest proposal supplied by the ideal functionality.
The simulation can fail only if the simulator is forced to program an oracle point that was already defined inconsistently, or if the adversary creates an ambiguity in what was submitted. For corrupt proposals, the simulator explicitly avoids inconsistency by using any existing pad-oracle value. For honest proposals, unpredictability of the IB-KEM raw key prevents the adversary from querying the honest pad-oracle point before the release transition, except with negligible probability; the deadline transition is ordered so that there is no intervening adversarial oracle query between release of the slot key and the simulator’s programming of honest points. Including the proposer identity in the pad-oracle input separates honest-proposer points from corrupt-proposer points, and signature unforgeability prevents the adversary from submitting roots under honest proposer identities. Finally, the Merkle/random-oracle and erasure-code checks ensure that a signed root determines a unique well-formed encrypted proposal; otherwise the root is treated as malformed. Conditioned on the complement of these bad events, the simulated transcript is distributed as in the real protocol, while corrupt proposals are fixed before honest proposal contents are revealed.
We now present the Chorus protocol. It is given as several algorithms, split across the subsubsections below only to separate its concerns; they form a single protocol and should be read as one. The Chorus protocol proceeds in three phases: proposers disseminate their proposals (Phase I), validators broadcast a single proposal vote at the slot’s deadline (or when they enter the slot, if later) (Phase II), and a meta-block’s entries are committed either via the fast path or via the fallback path (Phase III). 16 implements Phase I and runs at proposers only. 17 implements Phase II at every validator. Phase III is implemented by two modules running in parallel: 18 aggregates proposal votes into \(\mathsf{FastQC}\)s and runs the two-round fast commit on a fast meta-block, while 19 drives the fallback path when a fast meta-block cannot be formed: it harvests the strongest available evidence per proposer into a fallback meta-block, feeds it to a multi-valued validated Byzantine agreement (MVBA, [mod:mvba]) — a primitive in which validators agree on a single externally-valid value among those they propose — and commits the entries of the MVBA output. Cutting across all phases, the data-availability module (20) provides chunk validation, ciphertext reconstruction, decryption, and proposal recovery. Modules communicate through the shared local state declared in their “Local variables” blocks (e.g., \(\mathit{Ev}(j)\) and \(\mathit{pathVote}\) are shared between the fast-path and fallback-path modules). Throughout the pseudocode, we say that \(p_i\) is actively participating in \(s\) if it has invoked the slot-consensus input \(\mathsf{participate}()\) but has not yet invoked \(\mathsf{abandon}()\).
Each slot \(s\) is bounded by a deadline \(s.\mathsf{deadline}\). Each honest proposer in \(s.\mathsf{proposers}\) (1) constructs a proposal, (2) encrypts it, (3) erasure-codes the ciphertext into chunks (one per validator), and (4) disseminates them by time \(s.\mathsf{deadline}- \Delta\) (in the style of AVID [13] and DispersedSimplex [5])17. Under synchrony, every honest validator receives a chunk from every honest proposer by the deadline. Validators record which chunks arrived in time and, at the deadline (or when they enter the slot, if later), broadcast a proposal vote: a single message containing a signed positive or negative entry per proposer.
A proposal by proposer \(p_j\) in slot \(s\) is \[P = \langle s, j, \mathit{Payload}, \sigma \rangle,\] where \(s\) is the slot, \(j\) is the proposer index, \(\mathit{Payload}\) is the proposal’s transaction payload, and \(\sigma\) is \(p_j\)’s signature over \(\langle s, j, H(\mathit{Payload}) \rangle\). The triple \(\langle s, j, \mathit{Payload}\rangle\) is exactly a proposal in the sense of the problem definition (10.1); we simply spell out its fields here rather than carry it as a single object.
To preserve hiding, the proposer does not disseminate \(P\) directly: it encrypts \(P\) to produce an encrypted proposal \(EP\), decryptable only once \(f+1\) validators have released their decryption shares for the slot (after the deadline). We disseminate \(EP\) in place of \(P\).
To distribute the dissemination load, the proposer erasure-codes the encrypted proposal \(EP\) into \(n\) fragments \(d_1, \ldots, d_n\) (one per validator) and builds a Merkle tree over the leaves \(H(1, d_1), \ldots, H(n, d_n)\), yielding a root \(\rho\). The reconstruction threshold is chosen so that any \(f+1\) fragments suffice to reconstruct \(EP\). The proposer forms a chunk header common to all recipients, \[\mathit{ChunkHeader} = \langle s, j, \rho, \sigma \rangle,\] where \(\sigma\) is \(p_j\)’s signature over \((s, j, \rho)\), and sends a chunk \[\langle \mathrm{\small Chunk}, \mathit{ChunkHeader}, r, d_r, \pi_r \rangle\] to each validator \(p_r\), where \(\pi_r\) is a Merkle proof for \(H(r, d_r)\) under root \(\rho\). We refer to this message as the chunk assigned to \(p_r\).
Each validator \(p_i\) collects incoming chunks until the slot’s deadline \(s.\mathsf{deadline}\). For each proposer \(p_j\), \(p_i\) records an entry \[E = \langle s, j, \rho_{\!\bot}\rangle,\] where \(\rho_{\!\bot}\in \mathsf{MerkleRoot} \cup \{\bot\}\) is the Merkle root of the chunk assigned to \(p_i\) that it received from \(p_j\) (a positive entry), or \(\bot\) if no valid chunk arrived by the deadline (a negative entry). Validators vote on entries by signing them.
At the deadline \(s.\mathsf{deadline}\) (or when \(p_i\) enters the slot, if later), each validator \(p_i\) reports what it received for the slot by broadcasting a single proposal vote. This message does three things at once: it carries \(p_i\)’s signed positive/negative entry for each proposer, supplies the chunks backing the positive entries, and releases \(p_i\)’s decryption share for the slot. Concretely: \[\langle \mathrm{\small Vote}, s, \mathit{SignedEntries}, \mathit{Chunks}, \mathit{DecryptShare} \rangle,\] where \(\mathit{SignedEntries}\) contains a signed entry \(\langle E, \sigma\rangle\) per proposer in \(s.\mathsf{proposers}\) (with \(p_i\)’s signature \(\sigma\) on \(\langle\mathrm{\small vote}, E\rangle\)), \(\mathit{Chunks}\) contains the chunk messages \(p_i\) received and validated for each positive entry, and \(\mathit{DecryptShare}\) is \(p_i\)’s share toward decrypting slot-\(s\) proposals.
A fast-path quorum certificate (\(\mathsf{FastQC}\)) for an entry \(E\) aggregates \(2f+1\) signatures over \(\langle\mathrm{\small vote}, E\rangle\): \[\mathsf{FastQC}= \langle \mathrm{\small vote}, E, \Sigma \rangle.\] A \(\mathsf{FastQC}\) for a positive entry \(E = \langle s, j, \rho\rangle\) certifies that \(2f+1\) validators received and validated a chunk from \(p_j\) under root \(\rho\), so at least \(f+1\) honest validators hold chunks; this is what guarantees data availability for \(p_j\)’s proposal.
The fast path commits the slot in two voting rounds after the deadline, without needing a leader to drive progress, provided a \(\mathsf{FastQC}\) can be formed for every proposer (a fast meta-block). Recall that a \(\mathsf{FastQC}\) aggregates \(2f+1\) votes on the same entry. If a proposer disseminates early enough, every honest validator receives its assigned chunk before the deadline and records the same positive entry, so \(2f+1\) matching positive votes are cast. If a proposer does not propose at all, every honest validator records a negative entry, so \(2f+1\) negative votes are cast. If instead a proposer disseminates too late, only some validators receive a chunk before the deadline (partial dissemination): these vote positively and the rest negatively, so there may be neither \(2f+1\) positive nor \(2f+1\) negative votes, and no \(\mathsf{FastQC}\) forms. In that case, validators take the fallback path.
A fast meta-block for slot \(s\) is \[B = \langle CE_{j_1}, \ldots, CE_{j_k} \rangle,\] where each \(CE_j\) is a \(\mathsf{FastQC}\) for proposer \(p_j\in s.\mathsf{proposers}\). We write \(\mathsf{entries}(B)\) for the sequence of entries \(E\) embedded in the \(\mathsf{FastQC}\)s.
Once a validator \(p_i\) has a fast meta-block\(B\), it broadcasts a fast commit vote \[\langle \mathrm{\small CommitVote}, s, \mathsf{entries}(B), \sigma_i \rangle,\] where \(\sigma_i\) signs \(\langle \mathrm{\small Commit}, s, \mathsf{entries}(B)\rangle\). A \((2f{+}1)\)-aggregate of such votes forms a fast commit certificate \[\mathsf{CommitQC}= \langle \mathrm{\small Commit}, s, \mathsf{entries}(B), \Sigma \rangle,\] which finalizes the entries \(\mathsf{entries}(B)\): any validator that holds the \(\mathsf{CommitQC}\) recovers the underlying proposals via \(\hyperref[alg:da]{Alg~\ref*{alg:da}}.\mathsf{recoverProposals}(\mathsf{entries}(B))\) and commits them. The commit fixes the entries, not the certificates that \(B\) carries: across validators a slot’s entries agree, whereas the certificates justifying them may differ.
Once a validator has \(\mathsf{FastQC}\)s for every proposer, it can speculatively commit the proposal vector before waiting for the \(\mathsf{CommitQC}\).
If a \(\mathsf{FastQC}\) fails to form for some proposer, validators cannot assemble a fast meta-block and the fast path stalls. A \(\mathsf{FastQC}\) fails to form when the votes split between positive and negative, so that neither a positive nor a negative quorum forms (partial dissemination). This can result from the proposer disseminating too late or to too few validators, or from network asynchrony. A \(\mathsf{FastQC}\) can also fail to form under equivocation, when the proposer sends different proposals so that no single entry gathers a quorum. From time \(s.\mathsf{deadline}+ \Delta\) onwards, each validator that has received at least \(2f+1\) proposal votes but has not been able to form a fast meta-block casts a fallback vote in place of a fast commit vote; the two are mutually exclusive, so each validator casts at most one. On the fallback path, the validator builds a fallback meta-block by collecting, and where necessary contributing to, per-proposer evidence: a \(\mathsf{FastQC}\) when one exists, otherwise a \(\mathsf{FallbackQC}\) (a weaker certificate that the validators help assemble in this phase), or, in adversarial cases, an \(\mathsf{EquivCert}\) that pins a proposer to equivocation. It then feeds the fallback meta-block to a Multi-Value Byzantine Agreement (MVBA) primitive. The meta-block output by the MVBA is the meta-block committed by the slot consensus.
A fallback signed entry from validator \(p_r\) for proposer \(p_j\) is \[\langle E, \sigma_v\rangle \text{ (negative, } E.\rho_{\!\bot}= \bot\text{)} \quad \text{or} \quad \langle E, \sigma_v, \sigma_p\rangle \text{ (positive, } E.\rho_{\!\bot}= \rho\text{)},\] where \(\sigma_v\) is \(p_r\)’s signature over \(\langle\mathrm{\small fb}, E\rangle\) and, for the positive case, \(\sigma_p\) is the proposer’s signature on \(\langle s, j, \rho\rangle\) carried over from the chunk header. A validator constructs its own fallback signed entry for each proposer for which it holds no evidence yet: it sets \(\rho_{\!\bot}\gets \rho\) if it has accumulated \(f+1\) valid fast-path positive votes for the same root \(\rho\) and the data is locally available (\(\hyperref[alg:da]{Alg~\ref*{alg:da}}.\mathsf{isDecoded}(\rho)\) returns true), and \(\rho_{\!\bot}\gets \bot\) otherwise.
A fallback-path quorum certificate (\(\mathsf{FallbackQC}\)) for entry \(E\) aggregates \(f+1\) signatures over \(\langle\mathrm{\small fb}, E\rangle\): \[\mathsf{FallbackQC}= \langle \mathrm{\small fb}, E, \Sigma \rangle.\] The \(\mathrm{\small vote}\)/\(\mathrm{\small fb}\) tags domain-separate the universal proposal-vote signatures from the fallback-only signatures.
An equivocation certificate for proposer \(p_j\) is \[\mathsf{EquivCert}= \langle \mathrm{\small equiv}, s, j, \rho_1, \sigma_{p,1}, \rho_2, \sigma_{p,2} \rangle,\] where \(\rho_1 \neq \rho_2\) and each \(\sigma_{p,k}\) is \(p_j\)’s signature over \(\langle s, j, \rho_k\rangle\). The \(\mathsf{EquivCert}\) exists for liveness: a Byzantine proposer can split its chunks across multiple Merkle roots so that no single fallback signed entry attracts \(f+1\) matching votes, leaving a \(\mathsf{FallbackQC}\) unformable; pinning the proposer to two contradictory signed roots still certifies its exclusion.
Once time has reached \(s.\mathsf{deadline}+ \Delta\) and \(p_r\) has received at least \(2f+1\) proposal votes (the inputs needed to assemble the message), and provided it has not entered the fast commit path, \(p_r\) broadcasts a fallback vote: \[\langle \mathrm{\small FallbackVote}, s, \sigma_r, \mathit{Ev}_{j_1}, \ldots, \mathit{Ev}_{j_k} \rangle,\] where \(\sigma_r = \mathsf{Sign}_r(\langle\mathrm{\small fallback}, s\rangle)\) is \(p_r\)’s commitment to entering the fallback path, and each \(\mathit{Ev}_j\) is the strongest evidence \(p_r\) holds for \(p_j\), by precedence \(\mathsf{FastQC}\succ \mathsf{EquivCert}\succ \mathsf{FallbackQC}\succ\) fallback signed entry (the last being \(p_r\)’s own signed entry when no higher-tier evidence is available).
Once \(2f+1\) fallback votes are collected, their \(\sigma_r\) fields aggregate into a fallback certificate \[\mathsf{FBCert}_s= \langle \mathrm{\small fallback}, s, \Sigma \rangle,\] certifying that the fast path can no longer commit slot \(s\).
From \(2f+1\) fallback votes, each validator builds a fallback meta-block \[B = \langle CE_{j_1}, \ldots, CE_{j_k}, \mathsf{FBCert}_s\rangle,\] where each \(CE_j\) is a certified entry for \(p_j\): a \(\mathsf{FastQC}\), \(\mathsf{FallbackQC}\), or \(\mathsf{EquivCert}\) (the latter two assembled from the signed entries collected in fallback votes, when no \(\mathsf{FastQC}\) is available). Extending the convention from 12.3.2, \(\mathsf{entries}(B)\) contributes the embedded \(E\) for a \(\mathsf{FastQC}\) or \(\mathsf{FallbackQC}\), and the negative entry \(\langle s, j, \bot\rangle\) for an \(\mathsf{EquivCert}\) for \(p_j\) (equivocation excludes the proposer); \(\mathsf{FBCert}_s\) is not part of \(\mathsf{entries}(B)\). We call a meta-blockvalid if it is either a fast meta-block or a fallback meta-block in this sense.
The validator then proposes its meta-block \(B\) to \(\mathsf{MVBA}[s]\), the slot’s multi-valued Byzantine agreement instance, invoking it at most once; \(B\) is valid by construction. The MVBA we use ([mod:mvba]) is standard but for one twist: it agrees not on a meta-block directly but on a certificate for one — a compact, publicly verifiable token, recognized by the function \(\mathsf{Certifies}\), that attests to a meta-block.
Associated functions:
\(\mathsf{Certifies}(\mathit{cert}, B)\): a publicly computable function returning \(\mathsf{true}\) if the certificate \(\mathit{cert}\) certifies the meta-block \(B\), and \(\mathsf{false}\) otherwise. Each certificate certifies at most one meta-block, i.e., \(\mathsf{Certifies}(\mathit{cert}, B) = \mathsf{Certifies}(\mathit{cert}, B') = \mathsf{true}\) implies \(B = B'\).
Interface:
input \(\mathsf{propose}(B)\): a validator proposes a valid meta-block \(B\).
output \(\mathsf{decide}(B, \mathit{cert})\): a validator decides a valid meta-block \(B\) together with a certificate \(\mathit{cert}\) for it.
Properties:
Agreement: No two distinct meta-blocks are both certified: if certificates \(\mathit{cert}\) and \(\mathit{cert}'\) — obtained by any (possibly faulty) validators — satisfy \(\mathsf{Certifies}(\mathit{cert}, B) = \mathsf{Certifies}(\mathit{cert}', B') = \mathsf{true}\), then \(B = B'\).
\(\ell_{\mathsf{MVBA}}\)-Termination: If all correct validators propose by time \(t\), then every correct validator decides by time \(\max(t, \mathrm{GST}) + \ell_{\mathsf{MVBA}}\), where \(\ell_{\mathsf{MVBA}} \geq 2\Delta\).
Validity: If a correct validator decides \((B, \mathit{cert})\), then \(\mathsf{Certifies}(\mathit{cert}, B) = \mathsf{true}\).
When the MVBA outputs a meta-block\(B'\), the validator recovers the underlying proposals by calling \(\mathsf{recoverProposals}(\mathsf{entries}(B'))\) on the DA module and commits them. Even after invoking the MVBA, a validator still finalizes on the fast path if a \(\mathsf{CommitQC}\) arrives; whenever it finalizes, by either path, it broadcasts the commitment proof and concludes all activity for the slot, including its MVBA invocation.
The data-availability (DA) module is responsible for recovering the proposals originally embedded by each proposer in chunks.
On receiving a chunk, the DA module verifies the proposer’s signature and the chunk’s Merkle proof against its root \(\rho\). Two things then follow. First, if the chunk is the one assigned to this validator, the module rebroadcasts it and notifies the voting module, so the validator can record a positive entry for \(\rho\) before the deadline. Second, once \(f+1\) valid chunks for the same root \(\rho\) accumulate, the module decodes them into a candidate encrypted proposal \(c\) and re-encodes \(c\), comparing the resulting root against \(\rho\): if they match, \(c\) is confirmed; otherwise \(\rho\) is marked invalid, catching a proposer that disseminated inconsistent chunks under one root.
Because proposals are disseminated encrypted to preserve hiding, reconstructing \(c\) does not yet reveal the proposal. Each validator releases a decryption share for the slot together with its vote; once the module holds \(f+1\) valid shares, it decrypts \(c\) into the proposal, or marks \(\rho\) invalid if decryption fails.
At commit time (speculative or formal), a validator calls \(\mathsf{recoverProposals}(\mathsf{entries}(B))\) on the DA module. The call blocks until every positive entry \(\langle s, j, \rho\rangle \in \mathsf{entries}(B)\) has either been recovered to a proposal or marked invalid, then returns the recovered proposals (ordering their transactions into a block is a subsequent, application-level step). Both commit routes guarantee that this call eventually returns: a \(\mathsf{FastQC}\) for a positive entry implies that \(f+1\) honest validators contributed chunks (enough to decode by erasure coding); a \(\mathsf{FallbackQC}\) for a positive entry implies that at least one honest validator has already decoded the proposal locally and re-disseminates the chunks (Alg 19). These two facts are the formal counterpart of the availability of §4.1.2. Its companion consistency corresponds to the determinism of the re-encode-and-compare check in tryIngestChunk (Alg 20, line [line:da-reencode]), where the verdict on a root, whether to accept and decode it or mark it invalid, depends only on the chunks committed under it and not on which \(f+1\) a validator happens to collect. Every correct validator therefore reaches the same verdict, and on acceptance decodes the same ciphertext.
This subsection proves that, when run within Cadence, Chorus realizes the slot-consensus primitive (see [mod:slotconsensus]). We do so by establishing each of its properties — agreement, termination, slot safety, proposal inclusion, and hiding — in turn.
We begin with the hiding property.
Lemma 6 (Hiding). Chorus([alg:proposer-dissemination,alg:voting,alg:fast-path-certification,alg:fallback,alg:da]) satisfies hiding.
Proof. This is established by the simulation argument of §12.2. ◻
Next, we prove the slot safety property.
Lemma 7 (Slot safety). Chorus([alg:proposer-dissemination,alg:voting,alg:fast-path-certification,alg:fallback,alg:da]) satisfies slot safety.
Proof. Follows directly from line [line:da-recover-slot] of 20: a validator finalizes only the proposal vector returned by \(\mathsf{recoverProposals}\), which is a proposal vector for slot \(s\). ◻
We now prove that Chorus satisfies agreement: no two correct validators finalize different proposal vectors. A validator finalizes only when it holds a commitment proof for a meta-block — a fast commit certificate \(\mathsf{CommitQC}\) or the certificate the MVBA decides ([mod:mvba]) — whereupon it triggers \(\mathsf{finalize}\): in the fast path on a valid \(\mathsf{CommitQC}\) (18, line [line:fast-finalize]), and in the fallback path on the MVBA’s decision (19, lines [line:fb-finalize1] and [line:fb-finalize2]) or on a received commit message (19, line [line:fb-finalize3]). We argue in two steps: any two meta-blocks backed by a commitment proof carry the same entries, and the same entries yield the same proposal vector at every correct validator.
Consider any two (potentially faulty) validators \(p_i\) and \(p_j\) such that \(p_i\) obtains (i.e., stores in its local memory) a commitment proof for a meta-block \(B_i\) and \(p_j\) obtains a commitment proof for a meta-block \(B_j\). Then, \(\mathsf{entries}(B_i) = \mathsf{entries}(B_j)\).
Proof. A commitment proof is either a fast commit certificate \(\mathsf{CommitQC}\) (18) or an MVBA-decided certificate (19), so we distinguish three cases.
Both \(p_i\) and \(p_j\) hold a \(\mathsf{CommitQC}\). Then \(B_i\) and \(B_j\) are fast meta-blocks (18, line [line:fast-metablock]), each of whose entries is a \(\mathsf{FastQC}\) (line [line:fast-formqc]) — an aggregate of \(2f+1\) matching signed entries. Fix a proposer; the two supporting sets of \(2f+1\) signed entries (one from \(B_i\), one from \(B_j\)) intersect, among the \(n = 3f+1\) validators, in at least \(f+1\), hence in a correct validator. Since a correct validator signs a single entry per proposer (17, line [line:vote-broadcast]), the two entries for that proposer coincide. As this holds for every proposer, \(\mathsf{entries}(B_i) = \mathsf{entries}(B_j)\).
Both hold an MVBA-decided certificate. This holds due to the agreement property of the MVBA ([mod:mvba]).
One holds a \(\mathsf{CommitQC}\) and the other an MVBA certificate. Without loss of generality, \(p_i\) holds a \(\mathsf{CommitQC}\) for \(B_i\) and \(p_j\) an MVBA certificate for \(B_j\). A \(\mathsf{CommitQC}\) aggregates \(2f+1\) commit votes (18, line [line:fast-collect-commit]), so at least \(f+1\) correct validators broadcast a commit vote and thereby set \(\mathit{pathVote} = \mathrm{\small fast}\) (line [line:fast-pathvote]). A correct validator issues a fallback vote only while \(\mathit{pathVote} = \mathrm{\small none}\) (19, line [line:fb-pathvote-guard]), so these \(f+1\) correct validators never do; fewer than \(2f+1\) fallback votes can form, and hence no fallback meta-block — which requires a \(2f+1\)-fallback-vote certificate — is valid. The MVBA decides a valid meta-block, which here must be a fast meta-block, so \(B_j\) is a fast meta-block. As \(B_i\) is a fast meta-block as well, \(\mathsf{entries}(B_i) = \mathsf{entries}(B_j)\) due to the quorum intersection. 0◻
◻
For any set of entries \(E\), any two correct validators that recover proposal vectors \(V\) and \(V'\) from \(\mathsf{recoverProposals}(E)\) satisfy \(V = V'\).
Proof. We show that whenever two correct validators both return from \(\mathsf{recoverProposals}(E)\), they return the same vector. Negative entries recover to \(\bot\) at every validator, so fix a positive entry \(\langle s, j, \rho\rangle \in E\); we show that any two correct validators that resolve it recover the same value for \(p_j\). A correct validator returns a value for \(\langle s, j, \rho\rangle\) only once \(\mathsf{status}(\rho) \in \{\langle\mathsf{plain}, \cdot\rangle,\, \mathsf{invalid}\}\) (20, line [line:da-wait]). The verdict on \(\rho\) — accept and decode, or mark invalid — is fixed by the re-encode-and-compare check of \(\mathsf{tryIngestChunk}\) (20, line [line:da-reencode]): a validator decodes its \(f+1\) chunks to a candidate \(c\) and accepts \(\rho\) only if \(\mathsf{MerkleRoot}(\mathsf{Encode}(c)) = \rho\). The chunks admissible under \(\rho\) are pinned down by the root itself (collision resistance of the Merkle hash), and \(\mathsf{Decode}\) and \(\mathsf{Encode}\) are deterministic; hence the verdict depends only on \(\rho\) and not on which \(f+1\) chunks a validator happens to collect. Every correct validator that resolves \(\rho\) thus reaches the same verdict and, on acceptance, the same ciphertext \(c\). Decryption is likewise deterministic once the slot key is reconstructed, so all such validators recover the same proposal \(P\) for \(p_j\), or all mark \(\rho\) invalid (recovering \(\bot\)). As this holds for every proposer, the two validators recover the same proposal vector. 0◻ ◻
We are ready to prove the agreement property of Chorus.
Lemma 8 (Agreement). Chorus([alg:proposer-dissemination,alg:voting,alg:fast-path-certification,alg:fallback,alg:da]) satisfies agreement.
Proof. A correct validator finalizes only the proposal vector \(\mathsf{recoverProposals}(\mathsf{entries}(B))\) (20, line [line:da-recover-slot]) for a meta-block \(B\) for which it holds a commitment proof. Let \(V_i\) and \(V_j\) be proposal vectors finalized by correct validators, backed by commitment proofs for meta-blocks \(B_i\) and \(B_j\); thus \(V_i = \mathsf{recoverProposals}(\mathsf{entries}(B_i))\) and \(V_j = \mathsf{recoverProposals}(\mathsf{entries}(B_j))\). By [prop:agreement-entries], \(\mathsf{entries}(B_i) = \mathsf{entries}(B_j)\), and by [prop:recovery-consistency], applying \(\mathsf{recoverProposals}\) to these equal entries yields the same proposal vector at both validators. Hence \(V_i = V_j\). 0◻ ◻
We now prove the proposal inclusion property. We first show that an on-time honest proposer can never be excluded: in any certified meta-block, its entry is the positive entry carrying its proposal’s root.
Suppose \(s.\mathsf{deadline}- \Delta \geq \mathrm{GST}\) and a correct proposer \(p_j\in s.\mathsf{proposers}\) disseminates its proposal \(P\), with Merkle root \(\rho_P\), at time \(s.\mathsf{deadline}- \Delta\). Then in every certified meta-block, \(p_j\)’s entry is the positive entry \(\langle s, j, \rho_P\rangle\).
Proof. Since \(s.\mathsf{deadline}- \Delta \geq \mathrm{GST}\) and \(p_j\) disseminates by the slot’s starting time, every correct validator receives and validates a chunk under \(\rho_P\) by the deadline, setting its entry for \(p_j\) to the positive entry \(\langle s, j, \rho_P\rangle\) (17, line [line:vote-positive]). Hence no correct validator ever signs a negative entry \(\langle s, j, \bot\rangle\), nor an entry on a root \(\neq \rho_P\), for \(p_j\).
A \(\mathsf{FastQC}\) for \(p_j\) is an aggregate of \(2f+1\) matching signed entries (18, line [line:fast-formqc]); as at most \(f\) validators are Byzantine, no \(2f+1\) matching signed entries can carry \(\bot\) or a root \(\neq \rho_P\), so any \(\mathsf{FastQC}\) for \(p_j\) is the positive entry \(\langle s, j, \rho_P\rangle\).
The fallback path yields the same conclusion. A correct validator casts a fallback signed entry for \(p_j\) only after collecting \(2f+1\) first-round votes (19, line [line:fb-pathvote-guard]); at least \(f+1\) of these come from correct validators, each carrying the positive entry \(\langle s, j, \rho_P\rangle\) for \(p_j\) together with its chunk. The validator therefore holds \(f+1\) positive entries on \(\rho_P\) and can decode \(\rho_P\), so it casts a positive fallback signed entry on \(\rho_P\) — never a negative one, nor one on another root (19, line [line:fb-positive-entry]). Since a \(\mathsf{FallbackQC}\) aggregates \(f+1\) matching fallback signed entries and at most \(f\) validators are Byzantine, any \(\mathsf{FallbackQC}\) for \(p_j\) is likewise positive on \(\rho_P\), and no negative \(\mathsf{FallbackQC}\) can form; an \(\mathsf{EquivCert}\) would require \(p_j\) to sign two different roots, impossible for a correct proposer.
Thus every piece of evidence ever formed for \(p_j\) — a \(\mathsf{FastQC}\) or a \(\mathsf{FallbackQC}\) — is the positive entry \(\langle s, j, \rho_P\rangle\), which is therefore its entry in every certified meta-block. 0◻ ◻
With the proposer’s entry pinned down by [prop:honest-positive-entry], we can now establish proposal inclusion.
Lemma 9 (Proposal inclusion). Chorus([alg:proposer-dissemination,alg:voting,alg:fast-path-certification,alg:fallback,alg:da]) satisfies proposal inclusion.
Proof. Let \(\rho_P\) be the Merkle root of \(p_j\)’s encrypted proposal, and let \(B\) be the certified meta-block the validator finalizes, so that \(V = \mathsf{recoverProposals}(\mathsf{entries}(B))\). By [prop:honest-positive-entry], \(p_j\)’s entry in \(B\) is the positive entry \(\langle s, j, \rho_P\rangle\) — a \(\mathsf{FastQC}\) or a \(\mathsf{FallbackQC}\) on \(\rho_P\). Either way, the chunks under \(\rho_P\) are re-disseminated by honest validators: a \(\mathsf{FastQC}\) carries \(\geq f+1\) honest signers, each re-broadcasting its own chunk (20, line [line:da-rebroadcast]); a \(\mathsf{FallbackQC}\) has an honest caster that sends each validator its assigned chunk (19, line [line:fb-redisseminate]), which each then re-broadcasts (20, line [line:da-rebroadcast]). So every correct validator collects \(f+1\) valid chunks under \(\rho_P\), and \(\mathsf{recoverProposals}\) does not block on this entry (20, line [line:da-wait]). Because \(p_j\) is correct, its proposal is well-formed: the recovered chunks reconstruct the ciphertext committed by \(\rho_P\), which re-encodes to \(\rho_P\) and, once the slot key is released, decrypts to \(P\) (20, line [line:da-reencode]; §12.2); moreover \(P\)’s proposer is \(p_j\), so the entry is not marked invalid. Hence \(\mathsf{recoverProposals}\) recovers \(P\) for \(p_j\), i.e., \(V[p_j] = P\). 0◻ ◻
We first establish totality: once one correct validator finalizes, every correct validator finalizes within \(2\Delta\) (assuming synchrony). Totality serves a dual purpose — it is the key step in Chorus’s own termination, and Conductor relies on it in its own right (14) — so we record it as a standalone property.
If a correct validator finalizes a proposal vector for slot \(s\) at some time \(t\), then every correct validator does so by time \(\max(t, \mathrm{GST}) + 2\Delta\).
Proof. On finalizing a meta-block \(B\), a correct validator broadcasts a transferable commitment proof for \(B\) — a fast commit certificate \(\mathsf{CommitQC}\) (18, line [line:fast-rebroadcast-commitqc]) or the certificate the MVBA decides (19, lines [line:fb-commit-broadcast], [line:fb-commit-broadcast2], and [line:fb-commit-rebroadcast]; [mod:mvba]). A correct validator finalizes upon receiving any valid commitment proof (18, line [line:fast-recv-commitqc]; 19, line [line:fb-recv-commit]), by recovering and committing \(\mathsf{recoverProposals}(\mathsf{entries}(B))\) (20, line [line:da-recover-slot]). Suppose a correct validator finalizes at time \(t\) with such a proof for \(B\). After \(\mathrm{GST}\) its broadcast reaches every correct validator within \(\Delta\), so every correct validator holds a valid commitment proof for \(B\) by \(\max(t, \mathrm{GST}) + \Delta\). It remains to bound when recovery succeeds at every correct validator. Each positive entry of \(B\) is certified by a \(\mathsf{FastQC}\) or a \(\mathsf{FallbackQC}\), and the certificate is formed no later than \(t\) (it is part of \(B\)). For a \(\mathsf{FastQC}\), its \(2f+1\) positive signed entries include at least \(f+1\) from honest validators, each of which has broadcast the chunk assigned to it (20, line [line:da-rebroadcast]); these single-hop broadcasts reach every correct validator within \(\Delta\), hence by \(\max(t,\mathrm{GST})+\Delta\). For a \(\mathsf{FallbackQC}\), at least one honest validator reconstructed the proposal and sent every validator its assigned chunk (19, line [line:fb-redisseminate]) at some time \(r \leq t\); each correct validator then rebroadcasts the chunk it receives (20, line [line:da-rebroadcast]), so after this two-hop dissemination every correct validator holds \(f+1\) valid chunks by \(\max(r,\mathrm{GST}) + 2\Delta \leq \max(t,\mathrm{GST}) + 2\Delta\). The decryption shares, broadcast with the proposal votes, likewise reach everyone within \(\Delta\). Hence by \(\max(t, \mathrm{GST}) + 2\Delta\) every correct validator holds a commitment proof for \(B\) together with all the chunks and shares that \(\mathsf{recoverProposals}(\mathsf{entries}(B))\) needs, so it recovers the proposal vector (20, line [line:da-recover-slot]) and finalizes it. 0◻ ◻
With totality in hand, we bound when every correct validator finalizes assuming no correct validator stops participating.
Suppose all correct validators start participating in slot \(s\) by some time \(t\), and no correct validator stops participating before time \(T = \max(t, \mathrm{GST}) + 3\Delta + \ell_{\mathsf{MVBA}}\). Then every correct validator finalizes a proposal vector by time \(T\).
Proof. We trace the protocol round by round; all bounds are relative to \(\max(t, \mathrm{GST})\), after which every message between correct validators is delivered within \(\Delta\).
First-round votes, by \(\max(t, \mathrm{GST}) + \Delta\). Within Cadence a correct validator starts participating in \(s\) no earlier than \(s.\mathsf{deadline}- \Delta\) (by the integrity of Conductor).18 While actively participating, each correct validator issues its first-round vote on reaching the deadline — or as soon as it is past, if the validator entered the slot late (17, line [line:vote-broadcast]). As established above, no correct validator starts before \(s\)’s starting time, which lies exactly \(\Delta\) before the deadline; hence every correct validator issues its first-round vote by time \(\max(t, \mathrm{GST}) + \Delta\).
Second-round votes, by \(\max(t, \mathrm{GST}) + 2\Delta\). The first-round votes reach every correct validator within a further \(\Delta\), so each either forms a \(\mathsf{FastQC}\) and broadcasts a commit vote (18, line [line:fast-commitvote]) or else enters the fallback path and broadcasts a fallback vote (19, line [line:fb-pathvote-guard]) — in either case by \(\max(t, \mathrm{GST}) + 2\Delta\). Moreover, since each correct validator releases its decryption share together with its first-round vote, every correct validator holds at least \(f+1\) valid decryption shares — enough to reconstruct the slot key — by time \(\max(t, \mathrm{GST}) + 2\Delta\).
MVBA proposals, by \(\max(t, \mathrm{GST}) + 3\Delta\). On the fallback path the second-round votes again propagate within \(\Delta\), so by \(\max(t, \mathrm{GST}) + 3\Delta\) every correct validator has either already finalized on the fast path (on obtaining a \(\mathsf{CommitQC}\); 18) or assembled a valid meta-block and proposed it to \(\mathsf{MVBA}[s]\) (19).
Deciding, by \(T\). If some correct validator finalizes by time \(\max(t, \mathrm{GST}) + 3\Delta\), totality ([prop:chorus-totality]) carries every correct validator to finalization within a further \(2\Delta\), hence by \(T\) (as \(\ell_{\mathsf{MVBA}} \geq 2\Delta\)). Otherwise every correct validator proposed to \(\mathsf{MVBA}[s]\) by \(\max(t, \mathrm{GST}) + 3\Delta\); the \(\ell_{\mathsf{MVBA}}\)-termination of the MVBA ([mod:mvba]) then guarantees that every correct validator decides \((B', \mathit{cert}')\) by \(\max(t, \mathrm{GST}) + 3\Delta + \ell_{\mathsf{MVBA}} = T\).
Reconstruction does not add delay. To finalize, a validator recovers the proposal vector from the decided meta-block(20, line [line:da-recover-slot]), which needs \(f+1\) chunks under each positive entry’s root to reconstruct the ciphertext and \(f+1\) decryption shares to open it. For a \(\mathsf{FastQC}\) entry, at least \(f+1\) honest validators validated and broadcast their chunk by the deadline (or as soon as it is past, if the validator entered the slot late) (20, line [line:da-rebroadcast]); these single-hop broadcasts reach every correct validator by \(\max(t, \mathrm{GST}) + 2\Delta\). For a \(\mathsf{FallbackQC}\) entry, the chunks instead spread by a two-hop dissemination — the honest fallback caster sends each validator its chunk (19, line [line:fb-redisseminate]), which each then rebroadcasts (20, line [line:da-rebroadcast]) — completing by \(\max(t, \mathrm{GST}) + 4\Delta\). So the chunks are in place by \(\max(t, \mathrm{GST}) + 4\Delta\), and the \(f+1\) decryption shares needed to open each ciphertext arrived even earlier, by \(\max(t, \mathrm{GST}) + 2\Delta\) (as argued above). Both ingredients that \(\mathsf{recoverProposals}\) needs are therefore present by \(\max(t, \mathrm{GST}) + 4\Delta\), and since \(\ell_{\mathsf{MVBA}} \geq 2\Delta\) gives \(3\Delta + \ell_{\mathsf{MVBA}} \geq 4\Delta\), they are in place no later than the MVBA decision; hence recovery never blocks (20, line [line:da-wait]).
Therefore every correct validator finalizes a proposal vector for \(s\) by time \(T\). 0◻ ◻
We are finally ready to establish Chorus’s termination.
Lemma 10 (Termination). When run within Cadence, Chorus([alg:proposer-dissemination,alg:voting,alg:fast-path-certification,alg:fallback,alg:da]) satisfies \(\ell\)-termination with \(\ell = 5\Delta + \ell_{\mathsf{MVBA}}\): if all correct validators start participating by some time \(t\), then every correct validator finalizes a proposal vector by time \(\max(t, \mathrm{GST}) + 5\Delta + \ell_{\mathsf{MVBA}}\).
Proof. Within Cadence a correct validator invokes \(\mathsf{abandon}()\) only after it has finalized (14, line [line:abandon]); equivalently, once a correct validator starts participating it keeps participating until it finalizes. Let \(T_0 = \max(t, \mathrm{GST}) + 3\Delta + \ell_{\mathsf{MVBA}}\) and distinguish two cases.
Some correct validator finalizes by \(T_0\). By totality ([prop:chorus-totality]), every correct validator then finalizes within a further \(2\Delta\), hence by \(T_0 + 2\Delta = \max(t, \mathrm{GST}) + 5\Delta + \ell_{\mathsf{MVBA}}\).
No correct validator finalizes by \(T_0\). Then no correct validator has abandoned its instance by \(T_0\), so all correct validators remain participating throughout \([t, T_0]\); by [prop:chorus-finalization-time], every correct validator finalizes by \(T_0 \leq \max(t, \mathrm{GST}) + 5\Delta + \ell_{\mathsf{MVBA}}\).
In either case, every correct validator finalizes by \(\max(t, \mathrm{GST}) + 5\Delta + \ell_{\mathsf{MVBA}}\). 0◻ ◻
In this section, we present Conductor, a protocol that serves as the orchestrator primitive within Cadence. Recall that we described Conductor informally in 5.
Here, we introduce Conductor(21). Before presenting it, we describe the building block it relies on, agreement on a core set (ACS).
The full specification of ACS is given in [mod:acs]. In a nutshell, each of the \(n\) validators submits a proposal, and ACS outputs a single set, agreed upon by all correct validators, that contains at least \(n - f\) of the submitted proposals.
Parameters:
\(\mathit{id} \in \mathbb{N}_{\geq 1}\): a unique identifier.
Interface:
input \(\mathsf{propose}(s \in \mathsf{Slot})\): a validator proposes slot \(s\).
output \(\mathsf{decide}(\mathit{set} \in \mathsf{Set}(\mathsf{Validator} \times \mathsf{Slot}))\): a validator decides a set \(\mathit{set}\) of validator-slot pairs.
Properties:
Agreement: No two correct validators decide different sets.
\(\ell\)-Termination: If all correct validators propose by time \(t\), then every correct validator decides by time \(\max(t, \mathrm{GST}) + \ell\).
Validity: If a correct validator decides a set \(\mathit{set}\), then \(|\mathit{set}| \geq 2f + 1\), and for every validator-slot pair \((p_i, s_i) \in \mathit{set}\) such that \(p_i\) is a correct validator, \(p_i\) proposed slot \(s_i\).
Integrity: If a correct validator decides, then some correct validator has previously proposed.
\(\Delta\)-Totality: If a correct validator decides at some time \(t\), then all correct validators decide by time \(\max(t, \mathrm{GST}) + \Delta\).
21 gives Conductor‘s pseudocode, written from the perspective of a single correct validator \(p_i\); recall that validators’ clocks are synchronized, so they share one global timeline. As described informally in 5, Conductor groups slots into windows of \(W\) consecutive slots and schedules them one at a time, opening a window only once enough of the earlier ones have completed. Window \(1\) consists of slots \(1, \ldots, W\), and each subsequent window again spans \(W\) consecutive slots. Unlike in the informal description, however, consecutive windows need not be adjacent: a window’s first slot is chosen dynamically and may lie strictly beyond the previous window’s last slot, leaving a gap of intervening slots that are simply never opened. (This stems from the slight difference in the problem definition between the two parts: here, slots have fixed, read-only deadlines, whereas in the informal part the protocol determines them. The orchestrator therefore cannot freely place the next deadline; it can only select an existing slot whose fixed deadline matches, skipping the intervening ones.) Throughout, \(p_i\) tracks the window it is currently scheduling in \(\mathit{current\_window}_i\) (initially \(1\), line [line:current95window95init]), the slots it has opened in \(\mathit{opened}_i\), the slots reported complete in \(\mathit{completed}_i\), the last (highest-numbered) slot of each window in \(\mathit{last}_i\), and the windows for which it has already submitted an ACS proposal in \(\mathit{proposed}_i\).
Parameters & constants. Conductor takes two tunable parameters: the window size \(W \in \mathbb{N}_{\geq 1}\) and the readiness threshold \(p \in \{0, \dots, W-1\}\), which controls how much of a window must complete before the next one is opened. Its timing further involves several constants: \(\ell\), the termination latency of the underlying ACS primitive (line [line:acs-instances]); \(\ell_{\mathrm{\small chorus}}\), the termination latency of Chorus, the slot-consensus protocol whose finalizations complete Conductor’s slots within Cadence; \(\Delta_{\mathsf{tot}}= 2\Delta\) ([prop:chorus-totality]), the totality latency of Chorus— the time within which one correct validator’s completion of a slot propagates to all correct validators; \(\Delta\), the post-\(\mathrm{GST}\) bound on message delays; and \(\tau\), the spacing between consecutive slot deadlines. From these we derive the open-to-complete delay \(\Phi_{oc} = \ell_{\mathrm{\small chorus}} + \Delta_{\mathsf{tot}}\), which bounds the time between a correct validator opening a slot and completing it ([prop:conductor-open-to-complete]). For Conductor to achieve its recovery property, \(W\) and \(p\) must be chosen so that the following four assumptions hold (lines [line:assumption-one]–[line:assumption-four]):
\((p - 1)\tau + \Phi_{oc} + \ell \leq W\tau\);
\((p - 1)\tau + \Phi_{oc} \leq (W - 1)\tau\);
\(\Delta < \ell\); and
\(\Delta_{\mathsf{tot}}+ \ell \leq (p - 1)\tau\).
Intuitively, they ensure that all the work tied to a window — proposing to the next ACS instance, deciding, and completing the window’s slots — fits within its time span of \(W\tau\), so that, once the network is synchronous, one window follows the next with no gaps. These assumptions are crucial for recovery, as we show in 13.2.
Startup. Upon starting (line [line:conductor-startup]), \(p_i\) enters window \(1\) (line [line:enter95window951]) and schedules the opening of its \(W\) slots in increasing order (lines [line:startup-foreach]–[line:startup-opened-update]), recording slot \(W\) as that window’s last slot (line [line:startup-last]). Opening is asynchronous: \(\mathsf{schedule\_opening}(s)\) waits until the slot’s starting time \(s.\mathsf{deadline}- \Delta\) — or fires at once if it has already passed — and then outputs \(\mathsf{open}(s)\) (lines [line:conductor-wait-for-open]–[line:trigger-open]). Since deadlines are \(\tau\)-spaced, window \(1\)’s slots thus open at times \(0, \tau, \ldots, (W-1)\tau\).
Completions. After startup, the only input to Conductor is the completion of slots: upon \(\mathsf{completed}(s)\) (line [line:upon-completed]), \(p_i\) adds \(s\) to \(\mathit{completed}_i\) (line [line:completed-update]). Within Cadence, \(p_i\) completes a slot precisely when it finalizes that slot’s proposal vector.
Estimating the next window’s deadline. Conductor opens window \(\mathit{current\_window}_i + 1\) only once enough of the current schedule has completed, as captured by \(\mathsf{ready\_for\_next\_window}()\) (line [line:ready-check]): writing \(s_1 < \cdots < s_k\) for the opened slots, it returns \(\mathsf{true}\) once all but at most the last \(W - p\) of them are complete — equivalently, once every slot of the earlier windows and the first \(p\) slots of the current window are done. When this first holds for the next window (line [line:ready]), \(p_i\) estimates that window’s first slot: it reads the current time \(t_{\mathit{cur}}\) (line [line:ready-time]) and lets \(s^\star\) be the earliest slot whose starting time has not yet passed (line [line:sstar-compute]); if that slot still falls within the current window, it advances \(s^\star\) to the first slot beyond it (lines [line:sstar-guard]–[line:sstar-update]). These two cases are exactly the estimate of 11: when the system is keeping up, \(s^\star\) is the slot immediately after the current window, so the cadence continues with no gap; when it is lagging, \(s^\star\) is pushed to \(t_{\mathit{cur}}\), leaving a gap for the backlog to drain. Validator \(p_i\) then proposes \(s^\star\) to the next window’s agreement instance \(\mathcal{ACS}[\mathit{current\_window}_i + 1]\) (line [line:acs-propose]) and records that it has done so (line [line:proposed-update]), so that it proposes at most once per window.
Agreeing on and opening the next window. Different correct validators may complete the first \(p\) slots at different times and thus estimate different first slots for the next window, so they agree on one through ACS. Once \(\mathcal{ACS}[\mathit{current\_window}_i + 1]\) decides and \(p_i\) is ready (line [line:acs-decide]), \(p_i\) advances to the next window (lines [line:window-increment]–[line:enter95window95omega]) and takes its first slot \(s^\star\) to be the median of the decided estimates (line [line:median-compute]); since at most \(f\) of the \(2f+1\) decided values are faulty, the median lies between the smallest and largest honest estimate, bounding Byzantine influence. It then schedules the opening of the window’s \(W\) slots \(s^\star, \ldots, s^\star + W - 1\) in increasing order (lines [line:open-foreach]–[line:acs-opened-update]) and records \(s^\star + W - 1\) as the window’s last slot (line [line:last-update]). Because \(p_i\) opens a fresh window only when at most the last \(W - p\) slots of the current one remain incomplete (line [line:ready-check]), at most \((W - p) + W = 2W - p\) slots are ever in flight — precisely Conductor’s boundedness.
This section proves that, when run within Cadence, Conductor satisfies the orchestrator specification of [mod:orchestrator952]. Before proving the individual properties, we first establish a number of structural facts about Conductor’s windows; these underpin the entire subsequent analysis of the specific orchestrator properties.
We first establish that windows are entered at most once and in strictly increasing order.
For every correct validator \(p_i\) and every window \(\omega \in \mathbb{N}_{\geq 1}\), \(p_i\) enters window \(\omega\) at most once. Moreover, if \(\omega \geq 2\), then \(p_i\) enters window \(\omega\) only after having previously entered window \(\omega - 1\).
Proof. Let us first prove that \(p_i\) enters window \(\omega\) at most once. First, note that the variable \(\mathit{current\_window}_i\) of a correct validator never decreases: it is modified only at line [line:window-increment], where it is incremented by one. Moreover, any two successive enterings are separated by such an increment. Indeed, only the first entering occurs at startup (line [line:enter95window951]), and this happens exactly once since the startup handler fires only once; every later entering occurs at line [line:enter95window95omega], immediately after \(\mathit{current\_window}_i\) is incremented at line [line:window-increment]. Hence each entering corresponds to a distinct, strictly increasing value of \(\mathit{current\_window}_i\), so no window is entered more than once.
We now turn to the second claim. The handler that enters window \(\omega \geq 2\) (line [line:acs-decide]) fires only when \(\mathit{current\_window}_i\) equals \(\omega - 1\); it then increments \(\mathit{current\_window}_i\) to \(\omega\) (line [line:window-increment]) and enters window \(\omega\) (line [line:enter95window95omega]). If \(\omega - 1 = 1\), then \(\mathit{current\_window}_i = 1\) was set at initialization (line [line:current95window95init]), which coincides with \(p_i\) entering window \(1\) at startup (line [line:enter95window951]). If \(\omega - 1 \geq 2\), then \(\mathit{current\_window}_i\) reached \(\omega - 1\) exclusively by being incremented at line [line:window-increment]; this increment executes in the same handler that enters window \(\omega - 1\) (line [line:enter95window95omega]). In both cases, \(p_i\) must have previously entered window \(\omega - 1\). 0◻ ◻
For every correct validator \(p_i\) and every window \(\omega \in \mathbb{N}_{\geq 1}\) that \(p_i\) enters, we define the following set of slots: \[\mathsf{slots}_i(\omega) \mathrel{\vcenter{:}}= \{ s \in \mathsf{Slot}: p_i \text{ schedules the opening of } s \text{ upon entering window } \omega \}.\] Concretely, \(\mathsf{slots}_i(1)\) consists of the \(W\) slots scheduled to be opened at startup (lines [line:startup-foreach]–[line:startup-opened-update]). For \(\omega \geq 2\), \(\mathsf{slots}_i(\omega)\) consists of the \(W\) slots scheduled to be opened in the loop at lines [line:open-foreach]–[line:acs-opened-update].
Throughout the rest of the proof, we adopt the following notational convention. Recall that, in Conductor, each validator invokes the ACS primitive to decide a vector of validator-slot pairs (line [line:acs-decide]) and then extracts the slot whose number is the median over all slot numbers in the vector (line [line:median-compute]). For brevity, we say that the ACS primitive decides slot \(s\) to refer to the outcome of this combined decision and extraction step, where \(s\) is the resulting slot. Crucially, the slot number of \(s\) lies between the minimum and maximum slot numbers proposed by correct validators (since the decided vector contains at least \(f + 1\) pairs contributed by correct validators). We next establish that the slot sets of distinct windows are pairwise disjoint. To this end, we first establish that the ACS primitives never decide overlapping slots.
For every correct validator \(p_i\) and every window \(\omega \in \mathbb{N}_{\geq 2}\), the following holds:
If \(\omega = 2\) and \(p_i\) decides slot \(s\) from \(\mathcal{ACS}[2]\), then \(s.\mathsf{number} > W\).
If \(\omega > 2\) and \(p_i\) decides slot \(s\) from \(\mathcal{ACS}[\omega]\) and slot \(s'\) from \(\mathcal{ACS}[\omega - 1]\), then \(s.\mathsf{number} \geq s'.\mathsf{number} + W\).
Proof. We first address the case \(\omega = 2\). No correct validator proposes to \(\mathcal{ACS}[2]\) a slot with number \(\leq W\). To see this, observe that a correct validator \(p_k\) proposes to \(\mathcal{ACS}[2]\) only via the rule at line [line:ready], which fires when \(\mathsf{ready\_for\_next\_window()}\) returns \(\mathsf{true}\). At that point, \(s^{\star}\) is computed as the first slot whose starting time is at least the current local time (line [line:sstar-compute]). If \(s^{\star}.\mathsf{number} \leq \mathit{last}_k[1].\mathsf{number}\) (line [line:sstar-guard]), the guard at line [line:sstar-update] reassigns \(s^{\star}\) to the slot with number \(\mathit{last}_k[1].\mathsf{number} + 1\). Crucially, \(\mathit{last}_k[1].\mathsf{number} = W\) always holds, since \(\mathit{last}_k[1]\) is set exactly once (upon entering window \(1\)), at line [line:startup-last], to the slot with number \(W\). Hence, every correct proposal to \(\mathcal{ACS}[2]\) carries a slot number of at least \(W + 1\). Since the decided slot number is at least the minimum slot number among all correct proposals, we obtain \(s.\mathsf{number} \geq W + 1\), establishing the first point.
We now address the case \(\omega > 2\). First, we establish that no correct validator proposes to \(\mathcal{ACS}[\omega]\) a slot with number \(< s'.\mathsf{number} + W\). A correct validator \(p_k\) proposes to \(\mathcal{ACS}[\omega]\) only via the rule at line [line:ready], which fires when \(\mathsf{ready\_for\_next\_window()}\) returns \(\mathsf{true}\). At this point, \(\mathit{current\_window}_k + 1 = \omega\) (line [line:acs-propose]), so \(\mathit{current\_window}_k = \omega - 1 \geq 2\). Since \(\mathit{current\_window}_k\) is incremented only at line [line:window-increment], \(p_k\) must have previously decided from \(\mathcal{ACS}[\omega - 1]\) (line [line:acs-decide]). By the agreement property of \(\mathcal{ACS}[\omega-1]\), every correct validator that decides from \(\mathcal{ACS}[\omega - 1]\) does so with slot \(s'\); hence, line [line:last-update] sets \(\mathit{last}_k[\omega-1]\) to the slot with number \(s'.\mathsf{number} + W - 1\). Thus, when \(p_k\) computes \(s^{\star}\) at line [line:sstar-compute], if \(s^{\star}.\mathsf{number} \leq \mathit{last}_k[\omega - 1].\mathsf{number} = s'.\mathsf{number} + W - 1\) (line [line:sstar-guard]), line [line:sstar-update] reassigns \(s^{\star}\) to the slot with number \(s'.\mathsf{number} + W\). Hence, every correct proposal to \(\mathcal{ACS}[\omega]\) carries a slot number of at least \(s'.\mathsf{number} + W\). Since the decided slot number is at least the minimum slot number among all correct proposals, we obtain \(s.\mathsf{number} \geq s'.\mathsf{number} + W\), establishing the second point. 0◻ ◻
We now precisely characterize, for each window, the exact set of slots that a correct validator opens upon entering it, and establish that consecutive windows cover non-overlapping ranges of \(W\) slots.
For every correct validator \(p_i\) and every window \(\omega \in \mathbb{N}_{\geq 1}\) that \(p_i\) enters, the following holds:
If \(\omega = 1\), then \(\mathsf{slots}_i(1) = \{ s \in \mathsf{Slot}: s.\mathsf{number}\leq W \}\).
If \(\omega \geq 2\), let \(s_\omega\) denote the slot decided by \(p_i\) from \(\mathcal{ACS}[\omega]\), and let \(s_{\omega-1}\) denote the slot with the largest number in \(\mathsf{slots}_i(\omega - 1)\). Then, \(s_\omega.\mathsf{number}> s_{\omega-1}.\mathsf{number}\), and moreover: \[\mathsf{slots}_i(\omega) = \{ s \in \mathsf{Slot}: s.\mathsf{number}\in [s_\omega.\mathsf{number},\;s_\omega.\mathsf{number}+ W - 1] \}.\]
Proof. The first point follows directly from the startup handler (lines [line:startup-foreach]–[line:startup-open]), which schedules the opening of exactly the slots with number in \([1, W]\).
We now prove the second point. Fix any window \(\omega \geq 2\) that \(p_i\) enters. We first observe that \(\mathit{last}_i[\omega - 1]\) equals \(s_{\omega - 1}\), the largest slot in \(\mathsf{slots}_i(\omega - 1)\). Indeed, if \(\omega - 1 = 1\), then line [line:startup-last] sets \(\mathit{last}_i[1]\) to the slot with number \(W\), which is exactly the largest slot in \(\mathsf{slots}_i(1) = \{ s : s.\mathsf{number}\leq W \}\). If \(\omega - 1 \geq 2\), then line [line:last-update] sets \(\mathit{last}_i[\omega - 1]\) to the largest of the \(W\) slots opened upon entering window \(\omega - 1\) (lines [line:open-foreach]–[line:acs-opened-update]), which is exactly the largest slot in \(\mathsf{slots}_i(\omega - 1)\). Upon activating line [line:acs-decide] for \(\mathcal{ACS}[\omega]\), \(p_i\) opens every slot with number in \([s_\omega.\mathsf{number}, s_\omega.\mathsf{number}+ W - 1]\) (line [line:open-foreach]). By [prop:acs-nonoverlap], \(s_\omega.\mathsf{number}> s_{\omega-1}.\mathsf{number}\), establishing the ordering claim.
Let \(s'\) be the slot decided from \(\mathcal{ACS}[\omega]\), so \(s_\omega = s'\); the largest slot of \(\mathsf{slots}(\omega-1)\) has number \(s'_{\omega-1}.\mathsf{number}+ W - 1\), where \(s'_{\omega-1}\) is the slot decided from \(\mathcal{ACS}[\omega-1]\). By [prop:acs-nonoverlap], \(s'.\mathsf{number}\geq s'_{\omega-1}.\mathsf{number}+ W > s'_{\omega-1}.\mathsf{number}+ W - 1 = s_{\omega-1}.\mathsf{number}\), establishing the ordering claim. 0◻ ◻
For each correct validator \(p_i\) and each slot \(s\), we define \(\mathsf{window}_i(s)\) to be the window \(\omega\) to which \(s\) belongs, that is, the window \(\omega\) with \[s \in \mathsf{slots}_i(\omega).\] If no such window exists, we set \(\mathsf{window}_i(s) = \bot\). By [prop:acs-fate-range], whenever such a window exists, it is unique; hence, \(\mathsf{window}_i(s)\) is well-defined. We now establish that no two correct validators disagree on the window to which a slot belongs.
For every two correct validators \(p_i\) and \(p_j\) and every slot \(s\), if \(\mathsf{window}_i(s) \neq \bot\) and \(\mathsf{window}_j(s) \neq \bot\), then \(\mathsf{window}_i(s) = \mathsf{window}_j(s)\).
Proof. If \(s.\mathsf{number} \leq W\), both windows equal \(1\), so the claim holds trivially. Thus, assume \(s.\mathsf{number} > W\). By [lemma:window-entry], correct validators enter windows in increasing order. Therefore, validator \(p_i\) (resp., \(p_j\)) activates the rule at line [line:acs-decide] for \(\mathcal{ACS}[2], \ldots, \mathcal{ACS}[\mathsf{window}_i(s)]\) (resp., \(\mathcal{ACS}[2], \ldots, \mathcal{ACS}[\mathsf{window}_j(s)]\)) in increasing order. The agreement property of each ACS primitive together with [prop:acs-fate-range] then gives \(\mathsf{window}_i(s) = \mathsf{window}_j(s)\). 0◻ ◻
We now prove the integrity property specified in [mod:orchestrator952], which ensures that each correct validator opens each slot at most once, and that no slot is opened before its starting time.
Lemma 11 (Integrity). Conductor(21) satisfies integrity.
Proof. Fix a correct validator \(p_i\) and a slot \(s\). By construction of the \(\mathsf{schedule\_opening}\) function (line [line:conductor-wait-for-open]), \(p_i\) does not open \(s\) before time \(s.\mathsf{deadline}- \Delta\). It remains to show that \(p_i\) opens \(s\) at most once. We distinguish two cases:
If \(\mathsf{window}_i(s) = \bot\), then \(p_i\) never opens \(s\), and the claim holds trivially.
Otherwise, let \(\omega = \mathsf{window}_i(s)\), so that \(s \in \mathsf{slots}_i(\omega)\). By [lemma:window-entry], \(p_i\) enters window \(\omega\) at most once. Hence, \(p_i\) opens \(s\) exactly once in this case.
In either case, \(p_i\) opens \(s\) at most once, establishing the lemma. 0◻ ◻
We now establish that each correct validator schedules the opening of slots in strictly increasing order of slot number, which we leverage to prove monotonicity.
For every correct validator \(p_i\) and every two slots \(s, s'\) with \(s.\mathsf{number}< s'.\mathsf{number}\), if \(p_i\) schedules the opening of both \(s\) and \(s'\), then it schedules the opening of \(s'\) only after having previously scheduled the opening of \(s\).
Proof. Since \(p_i\) schedules the opening of both \(s\) and \(s'\), we have \(\mathsf{window}_i(s) \neq \bot\) and \(\mathsf{window}_i(s') \neq \bot\). Moreover, since \(s.\mathsf{number}< s'.\mathsf{number}\), [prop:acs-fate-range] gives \(\mathsf{window}_i(s) \leq \mathsf{window}_i(s')\). We distinguish two cases:
\(\mathsf{window}_i(s) = \mathsf{window}_i(s')\). Both slots are scheduled to be opened upon entering this common window, within the loop that processes slots in strictly increasing order of slot number (line [line:startup-foreach] for window \(1\), line [line:open-foreach] otherwise); since \(s.\mathsf{number}< s'.\mathsf{number}\), \(p_i\) schedules the opening of \(s\) before that of \(s'\).
\(\mathsf{window}_i(s) < \mathsf{window}_i(s')\). By [lemma:window-entry], \(p_i\) enters window \(\mathsf{window}_i(s)\) before window \(\mathsf{window}_i(s')\); as \(s\) is scheduled upon entering the former and \(s'\) upon entering the latter, \(p_i\) schedules the opening of \(s\) before that of \(s'\). 0◻
◻
We are now ready to prove that Conductor satisfies monotonicity (see [mod:orchestrator952]), which guarantees that correct validators open slots in strictly increasing order of slot number.
Lemma 12 (Monotonicity). Conductor(21) satisfies monotonicity.
Proof. Fix any correct validator \(p_i\) and any two slots \(s\) and \(s'\) that \(p_i\) opens. Without loss of generality, let \(s'.\mathsf{number}> s.\mathsf{number}\). Since \(s'.\mathsf{number} > s.\mathsf{number}\), the definition of slots implies \(s'.\mathsf{deadline} > s.\mathsf{deadline}\) (see 10). By [prop:fate-order], \(p_i\) schedules the opening of \(s\) prior to scheduling the opening of \(s'\). We distinguish two cases.
Suppose \(p_i\) opens \(s\) at the moment of scheduling its opening (i.e., it does not wait for \(s\)’s starting time). In this case, when opening \(s'\) (which cannot occur before the scheduling of its own opening), \(p_i\) has already opened \(s\).
Suppose \(p_i\) opens \(s\) after scheduling its opening. Hence, \(p_i\) opens \(s\) at time \(s.\mathsf{deadline} - \Delta\) (line [line:conductor-wait-for-open]). In this case, when opening \(s'\), which cannot occur before time \(s'.\mathsf{deadline} - \Delta > s.\mathsf{deadline} - \Delta\) (line [line:conductor-wait-for-open]), \(p_i\) has already opened \(s\). 0◻
◻
We say that a correct validator \(p_i\) is in window \(\omega \in \mathbb{N}_{\geq 1}\) from the moment it enters window \(\omega\) until the moment it enters the next window (which is \(\omega + 1\), by [lemma:window-entry]). The following proposition relates the window in which a correct validator currently resides to the number of slots it has accumulated in its local variable \(\mathit{opened}_i\).
For every correct validator \(p_i\) and every window \(\omega \in \mathbb{N}_{\geq 1}\), while \(p_i\) is in window \(\omega\), the local variable \(\mathit{opened}_i\) contains exactly \(\omega \cdot W\) slots.
Proof. We proceed by induction on \(\omega\).
Base case (\(\omega = 1\)). Validator \(p_i\) enters window \(1\) at startup, upon which the startup handler adds exactly the slots with number in \([1, W]\) to \(\mathit{opened}_i\) (lines [line:startup-foreach]–[line:startup-opened-update]). As \(\mathit{opened}_i\) is modified only upon entering a new window, the count remains \(1 \cdot W\) throughout the time \(p_i\) is in window \(1\).
Inductive step (\(\omega \geq 2\)). By the inductive hypothesis, \(|\mathit{opened}_i| = (\omega - 1) \cdot W\) while \(p_i\) is in window \(\omega - 1\). Validator \(p_i\) enters window \(\omega\) upon activating the rule at line [line:acs-decide], where the loop at lines [line:open-foreach]–[line:acs-opened-update] adds exactly \(W\) fresh slots (by [prop:acs-fate-range]) to \(\mathit{opened}_i\), yielding \(|\mathit{opened}_i| = (\omega - 1) \cdot W + W = \omega \cdot W\). Since \(\mathit{opened}_i\) is modified only upon entering a new window, the count remains \(\omega \cdot W\) throughout the time \(p_i\) is in window \(\omega\). 0◻ ◻
We are now ready to prove that Conductor satisfies \((2W - p)\)-boundedness (see [mod:orchestrator952]), which guarantees that at most \(2W - p\) slots are simultaneously open (that is, opened but not yet completed) at any point in time.
Lemma 13 (Boundedness). Conductor(21) satisfies \((2W - p)\)-boundedness.
Proof. Fix a correct validator \(p_i\) and a time \(t\). Let \(s_1 < s_2 < \cdots < s_k\) be the slots in \(\mathit{opened}_i\) at time \(t\), ordered by slot number, and let \(j^{\star}\) be the largest index such that \(p_i\) has completed each of \(s_1, \ldots, s_{j^{\star}}\) by time \(t\) (with \(j^{\star} = 0\) if no such index exists). It suffices to show that \(k - j^{\star} \leq 2W - p\), i.e., that at most \(2W - p\) of the slots \(s_1, \ldots, s_k\) remain uncompleted by \(p_i\) at time \(t\) (since every slot \(p_i\) has opened has been previously recorded in \(\mathit{opened}_i\)).
Let \(\omega = \mathsf{window}_i(s_k)\) be the window of slot \(s_k\). We know that \(p_i\) is in window \(\omega\) at time \(t\). Let us now distinguish two cases:
Suppose \(\omega = 1\). Then, \(s_k.\mathsf{number}\leq W\), so \(k \leq W\), and the bound \(k - j^{\star} \leq k \leq W \leq 2W - p\) holds immediately.
Suppose \(\omega \geq 2\). Validator \(p_i\) enters window \(\omega\) upon activating the rule at line [line:acs-decide] for \(\mathcal{ACS}[\omega]\), which fires only when \(\mathsf{ready\_for\_next\_window}()\) returns \(\mathsf{true}\). By the readiness condition (line [line:ready-check]), at the moment \(p_i\) enters window \(\omega\), at most the last \(W - p\) slots in \(\mathit{opened}_i\) (by slot number) remain uncompleted. Just before entering window \(\omega\), validator \(p_i\) was in window \(\omega - 1\), so \(|\mathit{opened}_i| = (\omega - 1) W\) by [prop:open-count-window]. Upon entering window \(\omega\), validator \(p_i\) adds the \(W\) slots of window \(\omega\) to \(\mathit{opened}_i\) (lines [line:open-foreach]–[line:acs-opened-update]). Hence, while \(p_i\) is in window \(\omega\), at most \((W - p) + W = 2W - p\) of the slots in \(\mathit{opened}_i\) remain uncompleted, and therefore \(k - j^{\star} \leq 2W - p\), establishing the lemma in this case. 0◻
◻
The previous three properties were established for Conductor in isolation: each constrains only how Conductor schedules and opens slots, and therefore holds regardless of how — or even whether — slots are subsequently completed. The remaining two, totality and recovery, are of a different nature. Both hinge on slots actually completing, and on doing so in a timely, coordinated fashion — something Conductor only reacts to, but does not itself bring about. We therefore establish totality and recovery only for Conductor run within Cadence, alongside Chorus(14), whose finalization supplies precisely the completion guarantees that these two properties rely on.
We begin with totality. In fact, we establish a stronger, quantitative form of it — \(\Delta_{\mathsf{tot}}\)-totality: if a correct validator opens a slot \(s\) at some time \(t\), then every correct validator opens \(s\) by time \(\max(t, \mathrm{GST}) + \Delta_{\mathsf{tot}}\). Here, \(\Delta_{\mathsf{tot}}= 2\Delta\) is Chorus’s totality latency (by [prop:chorus-totality]), which enters the argument because completions within Cadence are Chorus finalizations. This is more than the orchestrator’s totality property demands — that property asks only that an opening eventually reach every correct validator — but we prove the sharper \(\Delta_{\mathsf{tot}}\) bound because the recovery proof later relies on it, not merely on eventual agreement about which slots are opened.
Before the formal argument, we sketch its shape, as the proof proceeds by an intricate recursion over the windows. The crux is a feedback loop between opening and completing: a correct validator opens the slots of window \(\omega\) only once enough of the earlier windows have completed, so the openings of window \(\omega\) are synchronized across correct validators — within \(\Delta_{\mathsf{tot}}\) of one another — only if the completions of the earlier windows are; and completions, in turn, are synchronized only for slots whose openings were already synchronized. We untangle this circularity by induction on the window number, through a single per-window guarantee — that the completions of a window’s slots reach all correct validators within \(\Delta_{\mathsf{tot}}\) — that ratchets forward: the first window satisfies it outright, and if every window before \(\omega\) satisfies it, then the openings of window \(\omega\) are \(\Delta_{\mathsf{tot}}\)-synchronized, which in turn makes window \(\omega\) satisfy the guarantee. Chaining this implication across all windows yields \(\Delta_{\mathsf{tot}}\)-totality for every slot.
Let \(\mathcal{W}\) denote the set of windows entered by at least one correct validator. By [prop:acs-fate-range] and the agreement property of the underlying ACS instances, any two correct validators that both enter a window \(\omega\) agree on its slot set, i.e., \(\mathsf{slots}_i(\omega) = \mathsf{slots}_j(\omega)\). We may therefore drop the subscript and write \(\mathsf{slots}(\omega)\) for this common set throughout the rest of the proof.
We name the aforementioned per-window guarantee — a window’s totality precondition — explicitly.
Definition 5 (Totality precondition). A window \(\omega \in \mathcal{W}\) satisfies the totality precondition* if and only if, for every slot \(s \in \mathsf{slots}(\omega)\), if a correct validator completes \(s\) at time \(t\), then every correct validator completes \(s\) by time \(\max(t, \mathrm{GST}) + \Delta_{\mathsf{tot}}\).*
We first show that window \(1\) satisfies the totality precondition.
When Conductor is run within Cadence, window \(1\) satisfies the totality precondition.
Proof. Consider any slot \(s \in \mathsf{slots}(1)\), that is, any slot with \(s.\mathsf{number}\leq W\), and suppose some correct validator \(p_i\) completes \(s\) at a time \(t\). Within Cadence, a validator records a completion only upon finalizing the corresponding slot consensus instance, and the handler that does so fires only for slots it has already opened (line [line:upon-finalize] of 14); hence \(p_i\) finalizes \(\mathcal{S}[s]\) at time \(t\), having already opened \(s\). At startup, every correct validator schedules the opening of \(s\) (lines [line:startup-foreach]–[line:startup-open]) and thus opens it precisely at its starting time \(s.\mathsf{deadline}- \Delta \leq t\) (line [line:conductor-wait-for-open]). By the totality of Chorus([prop:chorus-totality]), every correct validator finalizes \(\mathcal{S}[s]\) by time \(\max(t, \mathrm{GST}) + \Delta_{\mathsf{tot}}\); since each has by then already opened \(s\), the guard on Cadence’s finalization handler (line [line:upon-finalize] of 14) is satisfied, so each completes \(s\) by time \(\max(t, \mathrm{GST}) + \Delta_{\mathsf{tot}}\). 0◻ ◻
We now turn to the remaining windows. For a window \(\omega > 1\), we cannot guarantee \(\Delta_{\mathsf{tot}}\)-synchronized openings outright; instead, the following proposition establishes them conditionally, assuming that every earlier window already satisfies the totality precondition.
Suppose Conductor is run within Cadence, and let \(\omega > 1\) be a window in \(\mathcal{W}\). If every window \(\omega' < \omega\) satisfies the totality precondition, then the following holds for every slot \(s \in \mathsf{slots}(\omega)\): if a correct validator opens \(s\) at some time \(t\), then every correct validator opens \(s\) by time \(\max(t, \mathrm{GST}) + \Delta_{\mathsf{tot}}\).
Proof. By hypothesis, every window \(\omega' < \omega\) satisfies the totality precondition (5). Let \(p_i\) be a correct validator that opens slot \(s \in \mathsf{slots}(\omega)\) at time \(t\). By line [line:conductor-wait-for-open], \(t \geq s.\mathsf{deadline}- \Delta\). Validator \(p_i\) schedules the opening of \(s\) upon entering window \(\omega\), at some time \(t' \leq t\). By [lemma:window-entry], \(p_i\) has previously entered all windows smaller than \(\omega\), which implies that \(p_i\) has activated the rule at line [line:acs-decide] for \(\mathcal{ACS}[\omega']\), for all \(\omega' \in [2, \omega]\), by time \(t'\). We now prove by induction on \(\omega'\) that, for each window \(\omega' \in [2, \omega]\), every correct validator enters window \(\omega'\) by time \(\max(t', \mathrm{GST}) + \Delta_{\mathsf{tot}}\).
Base case (\(\omega' = 2\)). Since \(p_i\) has entered window \(2\) by time \(t'\), the following two facts hold: (1) \(p_i\) decides from \(\mathcal{ACS}[2]\) by time \(t'\); and (2) \(p_i\) has completed every slot of window \(1\) except possibly the last \(W - p\) (by slot number) by time \(t'\). Consider any correct validator \(p_j\). By the \(\Delta\)-totality property of \(\mathcal{ACS}[2]\), \(p_j\) decides from \(\mathcal{ACS}[2]\) by time \(\max(t', \mathrm{GST}) + \Delta\). Moreover, since window \(1\) satisfies the totality precondition (5), every slot of window \(1\) that \(p_i\) completes by \(t'\) is completed by \(p_j\) by time \(\max(t', \mathrm{GST}) + \Delta_{\mathsf{tot}}\); hence, \(p_j\) likewise has all but the last \(W - p\) slots of window \(1\) completed by that time. Therefore, both conditions for activating the rule at line [line:acs-decide] for \(\mathcal{ACS}[2]\) hold at \(p_j\) by time \(\max(t', \mathrm{GST}) + \Delta_{\mathsf{tot}}\) (as \(\Delta_{\mathsf{tot}}= 2\Delta\), the later of the two bounds is \(\max(t', \mathrm{GST}) + \Delta_{\mathsf{tot}}\)), which implies that \(p_j\) enters window \(2\) by this time.
Inductive step (\(\omega' - 1 \to \omega'\)). Since \(p_i\) has entered window \(\omega'\) by time \(t'\), the following two facts hold: (1) \(p_i\) decides from \(\mathcal{ACS}[\omega']\) by time \(t'\); and (2) \(p_i\) has completed every slot in windows \(1, \ldots, \omega' - 1\) except possibly the last \(W - p\) by time \(t'\). Consider any correct validator \(p_j\). First, by the inductive hypothesis, \(p_j\) enters window \(\omega' - 1\) by time \(\max(t', \mathrm{GST}) + \Delta_{\mathsf{tot}}\), so that \(\mathit{current\_window}_j = \omega' - 1\) at that point (which is required for \(p_j\) to subsequently activate the rule at line [line:acs-decide] for \(\mathcal{ACS}[\omega']\)). By the \(\Delta\)-totality property of \(\mathcal{ACS}[\omega']\), \(p_j\) decides from \(\mathcal{ACS}[\omega']\) by time \(\max(t', \mathrm{GST}) + \Delta\). Finally, since windows \(1, \ldots, \omega' - 1\) all satisfy the totality precondition, every slot that \(p_i\) completes by \(t'\) is completed by \(p_j\) by time \(\max(t', \mathrm{GST}) + \Delta_{\mathsf{tot}}\); hence, \(p_j\) likewise has all but the last \(W - p\) slots completed by that time. Therefore, both conditions for activating the rule at line [line:acs-decide] for \(\mathcal{ACS}[\omega']\) hold at \(p_j\) by time \(\max(t', \mathrm{GST}) + \Delta_{\mathsf{tot}}\) (again using \(\Delta_{\mathsf{tot}}= 2\Delta\)), completing the induction.
Applying the claim with \(\omega' = \omega\), every correct validator \(p_j\) enters window \(\omega\) at some time \(t^{\star} \leq \max(t', \mathrm{GST}) + \Delta_{\mathsf{tot}}\leq \max(t, \mathrm{GST}) + \Delta_{\mathsf{tot}}\), and thereby schedules the opening of \(s\) (since \(s \in \mathsf{slots}(\omega)\)). We distinguish two cases.
If \(t^{\star} \geq s.\mathsf{deadline}- \Delta\), then \(p_j\) opens \(s\) at time \(t^{\star} \leq \max(t, \mathrm{GST}) + \Delta_{\mathsf{tot}}\), as required.
If \(t^{\star} < s.\mathsf{deadline}- \Delta\), then \(p_j\) opens \(s\) at time \(s.\mathsf{deadline}- \Delta\). Since \(t \geq s.\mathsf{deadline}- \Delta\), the claim holds in this case as well. 0◻
◻
The previous proposition synchronizes the openings of a window’s slots; the next one closes the loop, showing that synchronized openings yield synchronized completions — precisely what a window needs to satisfy the totality precondition.
When Conductor is run within Cadence, the following holds for every slot \(s\). Suppose the opening of \(s\) is \(\Delta_{\mathsf{tot}}\)-total: if a correct validator opens \(s\) at some time \(t\), then every correct validator opens \(s\) by time \(\max(t, \mathrm{GST}) + \Delta_{\mathsf{tot}}\). Then the completion of \(s\) is \(\Delta_{\mathsf{tot}}\)-total as well: if a correct validator completes \(s\) at some time \(t'\), then every correct validator completes \(s\) by time \(\max(t', \mathrm{GST}) + \Delta_{\mathsf{tot}}\).
Proof. Let \(p_i\) be a correct validator that completes \(s\) at time \(t'\). Within Cadence, a validator records a completion only upon finalizing the corresponding slot consensus instance, and the handler that does so fires only for slots it has already opened (line [line:upon-finalize] of 14); hence \(p_i\) finalizes \(\mathcal{S}[s]\) at time \(t'\), having opened \(s\) at some time \(t \leq t'\). Applying the assumption to \(p_i\)’s opening of \(s\) at time \(t\), all correct validators open \(s\) by time \(\max(t, \mathrm{GST}) + \Delta_{\mathsf{tot}}\leq \max(t', \mathrm{GST}) + \Delta_{\mathsf{tot}}\). By the totality of Chorus([prop:chorus-totality]), every correct validator finalizes \(\mathcal{S}[s]\) by time \(\max(t', \mathrm{GST}) + \Delta_{\mathsf{tot}}\); since each has by then already opened \(s\), the guard on Cadence’s finalization handler (line [line:upon-finalize] of 14) is satisfied, so each completes \(s\) by time \(\max(t', \mathrm{GST}) + \Delta_{\mathsf{tot}}\). 0◻ ◻
We are now ready to prove that Conductor satisfies \(\Delta_{\mathsf{tot}}\)-totality when run within Cadence.
Lemma 14 (Totality). When run within Cadence(14), Conductor(21) satisfies totality. More specifically, for every slot \(s\), if a correct validator opens \(s\) at some time \(t\), then every correct validator opens \(s\) by time \(\max(t, \mathrm{GST}) + \Delta_{\mathsf{tot}}\).
Proof. Consider any slot \(s\) that a correct validator \(p_i\) opens, and let \(\omega = \mathsf{window}_i(s)\). We first show, by strong induction on \(\omega'\), that every window \(\omega' < \omega\) satisfies the totality precondition (5). For \(\omega' = 1\), this is ensured by [prop:totality-precondition-window-1]. For \(\omega' \geq 2\), the induction hypothesis gives that every window smaller than \(\omega'\) satisfies the totality precondition; hence [lemma:delta-totality-window] applies and makes the opening of every slot in \(\mathsf{slots}(\omega')\) \(\Delta_{\mathsf{tot}}\)-total, whereupon [prop:completion-from-opening] shows that \(\omega'\) itself satisfies the totality precondition.
It remains to establish that the opening of \(s\) is \(\Delta_{\mathsf{tot}}\)-total. If \(\omega = 1\), then \(s.\mathsf{number}\leq W\), and every correct validator schedules the opening of \(s\) at startup, opening it exactly at its starting time \(s.\mathsf{deadline}- \Delta\) (lines [line:startup-foreach]–[line:startup-open], via line [line:conductor-wait-for-open]); since all do so at the same time, the opening of \(s\) is \(\Delta_{\mathsf{tot}}\)-total. Otherwise \(\omega > 1\), and since every window \(\omega' < \omega\) satisfies the totality precondition, [lemma:delta-totality-window] applied to \(\omega\) shows that the opening of \(s \in \mathsf{slots}(\omega)\) is \(\Delta_{\mathsf{tot}}\)-total. 0◻ ◻
Combining the two halves of the recursion — that openings are \(\Delta_{\mathsf{tot}}\)-total and that \(\Delta_{\mathsf{tot}}\)-total openings yield \(\Delta_{\mathsf{tot}}\)-total completions — we obtain that completions, too, propagate within \(\Delta_{\mathsf{tot}}\) for every slot.
Corollary 1. When run within Cadence(14), Conductor satisfies \(\Delta_{\mathsf{tot}}\)-totality of completions: for every slot \(s\), if a correct validator completes \(s\) at some time \(t\), then every correct validator completes \(s\) by time \(\max(t, \mathrm{GST}) + \Delta_{\mathsf{tot}}\).
Proof. By 14, the opening of every slot \(s\) is \(\Delta_{\mathsf{tot}}\)-total. [prop:completion-from-opening] then yields that the completion of \(s\) is \(\Delta_{\mathsf{tot}}\)-total as well. 0◻ ◻
Finally, we turn to Conductor’s recovery property, which we also show Conductor satisfies when run within Cadence. To this end, we first bound how long a correct validator takes to complete a slot it opens, in terms of Chorus’s termination latency \(\ell_{\mathrm{\small chorus}}\) — the parameter for which, if all correct validators start participating by some time \(t'\), then all finalize by time \(\max(t', \mathrm{GST}) + \ell_{\mathrm{\small chorus}}\).
When run within Cadence, the following holds for every slot \(s\): if a correct validator opens \(s\) at some time \(t\), then it completes \(s\) by time \(\max(t, \mathrm{GST}) + \Delta_{\mathsf{tot}}+ \ell_{\mathrm{\small chorus}}\).
Proof. Let \(p_i\) be a correct validator that opens slot \(s\) at time \(t\); upon doing so, it starts participating in \(\mathcal{S}[s]\) (line [line:participate] of 14). By the \(\Delta_{\mathsf{tot}}\)-totality of Conductor(14), every correct validator opens \(s\) — and hence starts participating in \(\mathcal{S}[s]\) — by time \(\max(t, \mathrm{GST}) + \Delta_{\mathsf{tot}}\). The \(\ell_{\mathrm{\small chorus}}\)-termination of Chorus(10) therefore guarantees that every correct validator finalizes \(\mathcal{S}[s]\) — and thus completes \(s\) (line [line:complete] of 14) — by time \(\max(t, \mathrm{GST}) + \Delta_{\mathsf{tot}}+ \ell_{\mathrm{\small chorus}}\). 0◻ ◻
Recall that \(\Phi_{oc} = \ell_{\mathrm{\small chorus}} + \Delta_{\mathsf{tot}}\), which is exactly the open-to-complete bound established just above ([prop:conductor-open-to-complete]). Throughout the remainder of this part of the proof, we focus exclusively on Conductor when run within Cadence. To avoid clutter, we leave this qualification implicit rather than restating it at every step. We now prove that every correct validator eventually enters every window.
Every correct validator eventually enters every window \(\omega \in \mathbb{N}_{\geq 1}\).
Proof. We prove the proposition by induction on the window number \(\omega\).
Base case (\(\omega = 1\)). This holds trivially: every correct validator enters window \(1\) upon starting the protocol (line [line:enter95window951]).
Inductive step (\(\omega - 1 \to \omega\)). By the inductive hypothesis, every correct validator eventually enters window \(\omega - 1\). We first show that every correct validator eventually decides from \(\mathcal{ACS}[\omega]\), distinguishing two cases:
Some correct validator decides from \(\mathcal{ACS}[\omega]\). By the totality property of \(\mathcal{ACS}[\omega]\) ([mod:acs]), every correct validator then decides from \(\mathcal{ACS}[\omega]\).
No correct validator decides from \(\mathcal{ACS}[\omega]\). Then no correct validator ever enters a window greater than \(\omega - 1\): entering window \(\omega\) requires activating the rule at line [line:acs-decide] for \(\mathcal{ACS}[\omega]\), which cannot fire if no correct validator decides from \(\mathcal{ACS}[\omega]\); and, by [lemma:window-entry], entering any larger window requires first entering \(\omega\). Fix any correct validator \(p_j\). By the inductive hypothesis it enters window \(\omega - 1\), and as it never advances beyond it, \(\mathit{current\_window}_j = \omega - 1\) from then on; by [prop:open-count-window], \(\mathit{opened}_j\) then contains exactly \((\omega - 1) W\) slots. Each such slot was opened by \(p_j\) (line [line:startup-open] or line [line:acs-decide-open]), so by [prop:conductor-open-to-complete], \(p_j\) eventually completes it. In particular, \(p_j\) eventually completes the first \((\omega - 2)W + p\) slots of \(\mathit{opened}_j\) (ordered by slot number), at which point \(\mathsf{ready\_for\_next\_window}()\) returns \(\mathsf{true}\) (line [line:ready-check]). Once it does, \(p_j\) proposes to \(\mathcal{ACS}[\omega]\) (lines [line:ready]–[line:acs-propose]), if it has not already. Hence every correct validator proposes to \(\mathcal{ACS}[\omega]\), so by the termination property of \(\mathcal{ACS}[\omega]\) ([mod:acs]) every correct validator decides from it.
In either case, every correct validator eventually decides from \(\mathcal{ACS}[\omega]\).
It remains to show that every correct validator \(p_i\) eventually enters window \(\omega\). While \(p_i\) is in window \(\omega - 1\) (i.e., \(\mathit{current\_window}_i = \omega - 1\)), it enters window \(\omega\) upon activating the rule at line [line:acs-decide] for \(\mathcal{ACS}[\omega]\) (line [line:enter95window95omega]), which fires once its two conditions hold simultaneously: (1) \(p_i\) decides from \(\mathcal{ACS}[\omega]\), which it eventually does, as just shown; and (2) \(\mathsf{ready\_for\_next\_window}()\) returns \(\mathsf{true}\) (line [line:ready-check]), which holds eventually — by the argument of the second case above, via [prop:conductor-open-to-complete] — and remains so, since \(\mathit{opened}_i\) does not change while \(p_i\) is in window \(\omega - 1\) and completed slots only accumulate. Therefore, both conditions eventually hold at \(p_i\), so \(p_i\) activates the rule at line [line:acs-decide] for \(\mathcal{ACS}[\omega]\) and enters window \(\omega\) (line [line:enter95window95omega]). 0◻ ◻
Having established that every correct validator enters every window, we now turn to timing: we show that, once the network stabilizes, correct validators enter each window at a precise time. These timing guarantees rely on the protocol parameters being configured to satisfy the following constraints (lines [line:assumption-one]–[line:assumption-four]):
\((p - 1)\tau + \Phi_{oc} + \ell \leq W\tau\) (line [line:assumption-one]);
\((p - 1)\tau + \Phi_{oc} \leq (W - 1)\tau\) (line [line:assumption-two]);
\(\Delta < \ell\) (line [line:assumption-three]); and
\(\Delta_{\mathsf{tot}}+ \ell \leq (p - 1)\tau\) (line [line:assumption-four]).
Recall that \(p \in \{0, \dots, W-1\}\) is the readiness threshold, \(W \in \mathbb{N}_{\geq 1}\) the window size, \(\ell\) the latency of the ACS primitive, \(\Phi_{oc} = \ell_{\mathrm{\small chorus}} + \Delta_{\mathsf{tot}}\) the open-to-complete delay, and \(\Delta\) the known upper bound on message delays after \(\mathrm{GST}\). Intuitively, these constraints ensure that all the work associated with a window, namely proposing to the next ACS instance, deciding, and completing its slots, fits within the window’s time span of \(W\tau\), so that one window follows the next without gaps once the network is synchronous.
Before proving the timing guarantee, we set up notation for the slots and starting times associated with each window. Note that, by [prop:enters-every-window], every correct validator enters every window \(\omega \in \mathbb{N}_{\geq 1}\), so \(\mathsf{slots}_i(\omega)\) is defined for every correct validator \(p_i\) and every window \(\omega\). By [prop:acs-fate-range], for every window \(\omega\), the common set \(\mathsf{slots}(\omega)\) consists of exactly \(W\) slots with consecutive slot numbers. For every window \(\omega\) and index \(x \in [1, W]\), we write \(\mathsf{slot}(\omega, x)\) for the \(x\)-th slot of \(\mathsf{slots}(\omega)\), ordered by slot number. Then, for every window \(\omega\) and index \(x \in [1, W]\), we denote by \(\mathcal{T}_x(\omega)\) the starting time of \(\mathsf{slot}(\omega, x)\), that is, \[\mathcal{T}_x(\omega) \;=\; s.\mathsf{deadline}- \Delta, \quad \text{where } s = \mathsf{slot}(\omega, x).\] Since consecutive slots are separated by exactly \(\tau\) time units, \(\mathcal{T}_x(\omega) = \mathcal{T}_1(\omega) + (x - 1)\tau\), for every \(x \in [1, W]\). Finally, we call a window \(\omega\) a post-\(\mathrm{GST}\) window if and only if \(\mathcal{T}_1(\omega) \geq \mathrm{GST}\), and a pre-\(\mathrm{GST}\) window otherwise.
We now show that every correct validator enters each window within \(\Delta_{\mathsf{tot}}+ \ell\) of its scheduled start (or of \(\mathrm{GST}\), if that is later).
Let \(\omega\) be any window. Then, every correct validator enters window \(\omega\) by time \[\max(\mathcal{T}_1(\omega), \mathrm{GST}) + \Delta_{\mathsf{tot}}+ \ell.\]
Proof. If \(\omega = 1\), every correct validator enters window \(1\) at time \(0 = \mathcal{T}_1(1)\), so the proposition holds trivially. Assume henceforth that \(\omega > 1\).
Suppose first that some correct validator enters window \(\omega\) by time \(\max(\mathcal{T}_1(\omega), \mathrm{GST}) + \Delta\). The proof of [lemma:delta-totality-window] shows that the entry of one correct validator into window \(\omega\) propagates to every correct validator within \(\Delta_{\mathsf{tot}}\) time. Hence, every correct validator enters window \(\omega\) by time \(\max(\mathcal{T}_1(\omega), \mathrm{GST}) + \Delta + \Delta_{\mathsf{tot}}\), which is earlier than \(\max(\mathcal{T}_1(\omega), \mathrm{GST}) + \Delta_{\mathsf{tot}}+ \ell\) (as \(\Delta < \ell\), line [line:assumption-three]); the claim follows. We may therefore assume that no correct validator enters window \(\omega\) by time \(\max(\mathcal{T}_1(\omega), \mathrm{GST}) + \Delta\); by [lemma:window-entry], no correct validator enters any window \(\geq \omega\) by that time.
Due to [prop:enters-every-window], every correct validator enters window \(\omega\), and hence decides from \(\mathcal{ACS}[\omega]\) (line [line:acs-decide]); by [prop:acs-fate-range], the decided slot is \(\mathsf{slot}(\omega, 1)\), whose starting time is \(\mathcal{T}_1(\omega)\). By the validity property of \(\mathcal{ACS}[\omega]\), at least \(f + 1\) correct validators proposed to \(\mathcal{ACS}[\omega]\), with median proposal \(\mathsf{slot}(\omega, 1)\); in particular, some correct validator \(p_k\) proposed a slot \(s'\) with \(s'.\mathsf{number}\leq \mathsf{slot}(\omega, 1).\mathsf{number}\) by time \(\mathcal{T}_1(\omega)\) (line [line:acs-propose]).
A correct validator proposes to \(\mathcal{ACS}[\omega]\) only when \(\mathsf{ready\_for\_next\_window}()\) returns \(\mathsf{true}\) (line [line:ready]). Since \(\mathit{current\_window}_k = \omega - 1\) when \(p_k\) proposes, \(\mathit{opened}_k\) contains exactly \((\omega - 1)W\) slots ([prop:open-count-window]), so \(p_k\) has completed the first \((\omega - 2)W + p\) of them by time \(\mathcal{T}_1(\omega)\). Due to 1, every correct validator completes each of these \((\omega - 2)W + p\) slots by time \(\max(\mathcal{T}_1(\omega), \mathrm{GST}) + \Delta_{\mathsf{tot}}\). Moreover, every correct validator has entered window \(\omega - 1\) by that time (follows from the proof of [lemma:delta-totality-window]). Therefore, \(\mathsf{ready\_for\_next\_window}()\) returns \(\mathsf{true}\) at every correct validator, and each proposes to \(\mathcal{ACS}[\omega]\) by time \(\max(\mathcal{T}_1(\omega), \mathrm{GST}) + \Delta_{\mathsf{tot}}\). By the termination property of \(\mathcal{ACS}[\omega]\), every correct validator then decides from \(\mathcal{ACS}[\omega]\) by time \(\max(\mathcal{T}_1(\omega), \mathrm{GST}) + \Delta_{\mathsf{tot}}+ \ell\), which proves the proposition. 0◻ ◻
We now establish the key timing invariant underlying recovery: if every correct validator enters a post-\(\mathrm{GST}\) window \(\omega\) by \(\mathcal{T}_p(\omega)\), then every correct validator opens window \(\omega + 1\) “on time” and there are no “gaps” between the two windows.
Fix any post-\(\mathrm{GST}\) window \(\omega\). If every correct validator enters window \(\omega\) by time \(\mathcal{T}_p(\omega)\), then:
\(\mathsf{slot}(\omega + 1, 1).\mathsf{number} = \mathsf{slot}(\omega, W).\mathsf{number} + 1\); and
every correct validator enters window \(\omega + 1\) by time \(\mathcal{T}_{1}(\omega + 1)\).
Proof. We prove each point in turn.
Point 1. We establish the first point by showing that every correct validator that proposes to \(\mathcal{ACS}[\omega + 1]\) does so with a slot \(s^{\star}\) with \(s^{\star}.\mathsf{number} = \mathsf{slot}(\omega, W).\mathsf{number} + 1\). Suppose, for contradiction, that some correct validator \(p_i\) proposes a slot \(s \neq s^{\star}\). Note that validator \(p_i\) opens each of the first \(p\) slots of window \(\omega\) by time \(\mathcal{T}_p(\omega)\) (at line [line:trigger-open], as each slot is opened either when scheduled or at the slot’s starting time). For \(p_i\) to propose to \(\mathcal{ACS}[\omega + 1]\) at line [line:acs-propose], \(\mathsf{ready\_for\_next\_window()}\) must return \(\mathsf{true}\) (line [line:ready]). By [prop:conductor-open-to-complete], \(p_i\) completes the first \((\omega - 1)W + p\) open slots by time \(\mathcal{T}_p(\omega) + \Phi_{oc}\), at which point \(\mathsf{ready\_for\_next\_window()}\) returns \(\mathsf{true}\) (line [line:ready-check]), triggering the rule at line [line:ready]. We now verify that the trigger fires before \(\mathsf{slot}(\omega, W).\mathsf{deadline} - \Delta\). By definition, \(\mathcal{T}_p(\omega) = \mathcal{T}_1(\omega) + (p - 1)\tau\) and \(\mathsf{slot}(\omega, W).\mathsf{deadline} - \Delta = \mathcal{T}_1(\omega) + (W - 1)\tau\). Hence, \(\mathcal{T}_p(\omega) + \Phi_{oc} = \mathcal{T}_1(\omega) + (p - 1)\tau + \Phi_{oc}\). By assumption at line [line:assumption-two], \((p - 1)\tau + \Phi_{oc} \leq (W - 1)\tau\), so: \[\mathcal{T}_p(\omega) + \Phi_{oc} = \mathcal{T}_1(\omega) + (p-1)\tau + \Phi_{oc} \leq \mathcal{T}_1(\omega) + (W-1)\tau = \mathcal{T}_W(\omega) = \mathsf{slot}(\omega, W).\mathsf{deadline} - \Delta.\] Consequently, \(p_i\) selects \(s^{\star}\) with \(s^{\star}.\mathsf{number} = \mathsf{slot}(\omega, W).\mathsf{number} + 1\) (lines [line:sstar-compute]–[line:sstar-update]) and invokes \(\mathcal{ACS}[\omega + 1].\mathsf{propose}(s^{\star})\) (line [line:acs-propose]), contradicting our assumption.
To conclude the first point, by the validity property of \(\mathcal{ACS}[\omega + 1]\), at least \(f + 1\) correct proposals appear in the decided set. Hence, the median slot number (line [line:median-compute]) equals \(\mathsf{slot}(\omega, W).\mathsf{number} + 1\), establishing \(\mathsf{slot}(\omega + 1, 1).\mathsf{number} = \mathsf{slot}(\omega, W).\mathsf{number} + 1\).
Point 2. We first show that every correct validator decides from \(\mathcal{ACS}[\omega + 1]\) by time \(\mathcal{T}_p(\omega) + \Phi_{oc} + \ell\). Since \(\mathcal{T}_p(\omega) = \mathcal{T}_1(\omega) + (p-1)\tau\) and \((p-1)\tau + \Phi_{oc} + \ell \leq W\tau\) (line [line:assumption-one]), we have \(\mathcal{T}_p(\omega) + \Phi_{oc} + \ell \leq \mathcal{T}_1(\omega) + W\tau = \mathcal{T}_1(\omega + 1)\), so this suffices to establish the claim. (Recall that every correct validator completes the first \((\omega - 1)W + p\) open slots by time \(\mathcal{T}_p(\omega) + \Phi_{oc}\); hence \(\mathsf{ready\_for\_next\_window}()\) returns \(\mathsf{true}\) strictly before time \(\mathcal{T}_p(\omega) + \Phi_{oc} + \ell\).) We distinguish two cases.
Suppose some correct validator decides from \(\mathcal{ACS}[\omega + 1]\) by time \(\mathcal{T}_p(\omega) + \Phi_{oc}\). By the totality property of \(\mathcal{ACS}[\omega + 1]\), every correct validator then decides by time \(\mathcal{T}_p(\omega) + \Phi_{oc} + \Delta < \mathcal{T}_p(\omega) + \Phi_{oc} + \ell\), and the claim holds.
Otherwise, no correct validator decides from \(\mathcal{ACS}[\omega + 1]\) by time \(\mathcal{T}_p(\omega) + \Phi_{oc}\). In particular, no correct validator has activated line [line:acs-decide] for \(\mathcal{ACS}[\omega + 1]\) by that time, so no correct validator has entered any window larger than \(\omega\) (line [line:enter95window95omega]). By [prop:conductor-open-to-complete], every correct validator completes all \((\omega - 1)W + p\) open slots by time \(\mathcal{T}_p(\omega) + \Phi_{oc}\), at which point \(\mathsf{ready\_for\_next\_window()}\) returns \(\mathsf{true}\) (line [line:ready-check]), triggering each correct validator to propose to \(\mathcal{ACS}[\omega + 1]\) (lines [line:ready]–[line:acs-propose]). Since \(\mathcal{ACS}[\omega + 1]\) has latency \(\ell\), every correct validator decides by time \(\mathcal{T}_p(\omega) + \Phi_{oc} + \ell\), and the claim holds. 0◻
◻
Next, we prove that from the second post-\(\mathrm{GST}\) window, the protocol “runs smoothly”.
Let \(\omega^{\star}\) denote the smallest post-\(\mathrm{GST}\) window. Then, for every window \(\omega > \omega^{\star}\), the following holds:
\(\mathsf{slot}(\omega, 1).\mathsf{number} = \mathsf{slot}(\omega - 1, W).\mathsf{number} + 1\); and
every correct validator opens every slot \(s \in \mathsf{slots}(\omega)\) at time \(s.\mathsf{deadline}- \Delta\).
Proof. We proceed by induction on \(\omega\), with base case \(\omega = \omega^{\star} + 1\). By [prop:window-open-time], every correct validator enters window \(\omega^{\star}\) by time \(\mathcal{T}_1(\omega^{\star}) + \Delta_{\mathsf{tot}}+ \ell\). Since \(\Delta_{\mathsf{tot}}+ \ell \leq (p - 1)\tau\), every correct validator enters window \(\omega^{\star}\) by time \(\mathcal{T}_p(\omega^{\star})\). Therefore, [prop:window-progression] gives us the following for window \(\omega = \omega^{\star} + 1\): (1) \(\mathsf{slot}(\omega^{\star} + 1, 1).\mathsf{number}= \mathsf{slot}(\omega^{\star}, W).\mathsf{number}+ 1\), and (2) every correct validator enters window \(\omega^{\star} + 1\) by time \(\mathcal{T}_1(\omega^{\star} + 1)\), which indeed implies that every correct validator opens every slot \(s \in \mathsf{slots}(\omega^{\star} + 1)\) at its starting time \(s.\mathsf{deadline}- \Delta\) (line [line:conductor-wait-for-open]). The inductive step is identical: applying [prop:window-progression] at each subsequent window yields the claim for all \(\omega > \omega^{\star} + 1\). 0◻ ◻
[prop:smooth-windows] tells us that the protocol runs smoothly from the second post-\(\mathrm{GST}\) window onward: there are no gaps between consecutive windows, and every slot is opened exactly at its starting time. It remains to understand when this smooth regime begins, i.e., when the second post-\(\mathrm{GST}\) window can start. To this end, we bound when the first post-\(\mathrm{GST}\) window can arise.
Let \(\omega\) denote the smallest post-\(\mathrm{GST}\) window. Then: \[\mathcal{T}_1(\omega) - \mathrm{GST} \leq W\tau.\]
Proof. If \(\omega = 1\), then \(\mathcal{T}_1(\omega) = 0 = \mathrm{GST}\), so the proposition holds trivially.
Hence, assume that \(\omega > 1\). Since \(\omega\) is the first (i.e., the smallest) post-\(\mathrm{GST}\) window and \(\omega > 1\) and correct validators enter window \(1\) at time \(0\), we have that there exists a pre-\(\mathrm{GST}\) window. Let \(\omega^{\star}\) denote the greatest pre-\(\mathrm{GST}\) window. Since \(\omega^{\star}\) is the greatest pre-\(\mathrm{GST}\) window and \(\omega\) is the first post-\(\mathrm{GST}\) window, we have \(\omega = \omega^{\star} + 1\).
We show that every correct validator \(p_i\) that proposes to \(\mathcal{ACS}[\omega]\) (line [line:acs-propose]) does so with a slot whose starting time is at most \(\mathrm{GST} + W\tau\). To propose to \(\mathcal{ACS}[\omega]\), \(p_i\) must have previously entered window \(\omega - 1 = \omega^{\star}\) (line [line:ready]). By [prop:window-open-time], \(p_i\) enters window \(\omega^{\star}\) by time \(\mathrm{GST} + \Delta_{\mathsf{tot}}+ \ell\). Since \(\omega^{\star}\) is a pre-\(\mathrm{GST}\) window, its first slot starts before \(\mathrm{GST}\), and since consecutive slots are \(\tau\) apart, \(p_i\) opens the first \(p\) slots of window \(\omega^{\star}\) by time \(\mathrm{GST} + (p-1)\tau\) (line [line:trigger-open]). By [prop:conductor-open-to-complete], \(p_i\) completes the first \((\omega^{\star} - 1)W + p\) slots by time \(\mathrm{GST} + (p-1)\tau + \Phi_{oc}\), which implies that \(p_i\) proposes to \(\mathcal{ACS}[\omega]\) by time \(\mathrm{GST} + (p-1)\tau + \Phi_{oc}\). The proposed slot \(s^{\star}\) is determined at lines [line:sstar-compute]–[line:sstar-update]; we show its starting time is strictly less than \(\mathrm{GST} + W\tau\). We distinguish two cases based on whether the guard at line [line:sstar-guard] holds. If it does not hold, \(p_i\) keeps \(s^{\star}\) as the first slot whose starting time meets or exceeds the current local time (line [line:sstar-compute]); since \(p_i\) proposes by time \(\mathrm{GST} + (p-1)\tau + \Phi_{oc}\), the starting time of \(s^{\star}\) is strictly less than \(\mathrm{GST} + (p-1)\tau + \Phi_{oc} + \tau = \mathrm{GST} + p\tau + \Phi_{oc}\), which is at most \(\mathrm{GST} + W\tau\) by assumption (2) (line [line:assumption-two]), since \((p-1)\tau + \Phi_{oc} \leq (W-1)\tau\) implies \(p\tau + \Phi_{oc} \leq W\tau\). If the guard holds, \(p_i\) reassigns \(s^{\star}\) to the slot immediately after the last slot of window \(\omega^{\star}\) (line [line:sstar-update]); since \(\omega^{\star}\) is a pre-\(\mathrm{GST}\) window, its last slot has starting time strictly less than \(\mathrm{GST} + (W-1)\tau\), and hence \(s^{\star}\) has starting time strictly less than \(\mathrm{GST} + W\tau\). Hence, by the median rule at line [line:median-compute], \(\mathcal{T}_1(\omega) \leq \mathrm{GST} + W\tau\), which proves the proposition. 0◻ ◻
We are finally ready to prove Conductor’s recovery.
Proof. Fix a correct validator \(p_i\). Let \(\omega\) denote the first post-\(\mathrm{GST}\) window. Consider any slot \(s\) opened via \(\mathcal{ACS}[\omega']\), for some \(\omega' \geq \omega + 1\). By [prop:smooth-windows], \(p_i\) opens \(s\) and it does so precisely at time \(s.\mathsf{deadline} - \Delta\). Notably, every slot with starting time \(\geq \mathcal{T}_1(\omega + 1)\) is resolved by \(\mathcal{ACS}[\omega']\), for some \(\omega' \geq \omega + 1\) (by [prop:acs-fate-range]). By [prop:smooth-windows], there are no gaps between windows after \(\omega\), and by [prop:first-post-gst-window-time], \(\mathcal{T}_1(\omega) - \mathrm{GST} \leq W\tau\); since each window spans \(W\tau\) time, we have that \(\mathcal{T}_1(\omega + 1) - \mathrm{GST} \leq 2W\tau\), which concludes the lemma. 0◻ ◻
Author order: alphabetical by last name.↩︎
Because existing blockchain protocols recognize this unique role of the leader, they typically rotate that role across blocks, thereby spreading power among validators over time; nonetheless, even short-lived control can enable rent extraction.↩︎
DAG-based consensus protocols also have multiple proposers, but these designs provide different properties since there can exist an advantage in conditioning one’s proposal on the others’.↩︎
We offer chain certificates as a practical add-on ([sec:chain-certification]), but they are not required for consensus.↩︎
If each proposal builds on an uncommitted parent, blocks that merge several proposals may build on different parents, so the merged block has no unique parent for the next block to chain to.↩︎
We occasionally refer to a slot’s starting time, which we define as its deadline minus \(\Delta\), where \(\Delta\) is the known bound on message delays after \(\mathrm{GST}\).↩︎
Henceforth, we write “censorship resistance” to mean this stronger notion when clear from the context.↩︎
This framework is not specific to the MCP problem: it supports “slot-based” consensus in general and may be of independent interest. We nevertheless present it through the lens of MCP, which is the focus of this work.↩︎
Validators also ignore chunks that arrive too long before \(D_s\), so a Byzantine proposer cannot flood them with proposals for far-future slots.↩︎
For this to pin the equivocation on proposer \(p_j\), each fallback-yes vote must carry the Merkle root as signed by \(p_j\); two such votes on different roots then amount to \(p_j\)’s own signatures on conflicting roots.↩︎
A ciphertext that does not decrypt to a well-formed proposal is discarded in the same way; this check, too, is deterministic, so all correct validators agree.↩︎
For safety, \(f+1\) signatures would suffice, since at least one is then from a correct validator; we use \(2f+1\) so that a supermajority signs, keeping the network in sync.↩︎
The fast path can also be derailed by equivocation, a proposer sending conflicting proposals to different validators; but two such signed proposals already form an equivocation certificate, so that case carries its own proof. We therefore set it aside and focus on partial dissemination, assuming below that no proposer equivocates.↩︎
In practice, this should be “good enough”: the actual number of faults is typically far below the worst-case bound \(f\) the protocol provisions for.↩︎
Since each positive vote carries the proposal’s Merkle root signed by the proposer, a single positive vote already proves that the proposer issued a proposal.↩︎
We assume that, in practice, chunk dissemination is decoupled from the more latency-critical vote dissemination: votes are sent first, before chunk re-dissemination begins.↩︎
In practice, the protocol instantiates this step using Deterministic RaptorCast [14].↩︎
Conductor does rely on Chorus for some of its own guarantees — namely its totality and recovery (see 14) — but the integrity property we invoke here holds unconditionally and depends on nothing about Chorus. Hence, there is no circularity.↩︎