第 11 课:实现 Cadence:单时隙版本
踏上构建生产级应用的旅程。
这节课要讲的是一个全新的协议:来自 Category Labs 的 Cadence。
Cadence 是一个共识协议(consensus protocol)。共识协议是区块链中负责处理验证者(validator)相关问题的那一部分:
- 一个验证者应该如何提议一个区块?
- Monad 的所有验证者应该如何就哪些区块有效达成一致?
- 以及更多。
学完这节课,你就会理解一个简化版的 Cadence 是如何工作的!你还会在自己的电脑上用 vibecode 写出一个简化版的 Cadence。我们鼓励你把你的作品分享到 X(Twitter)上!
Cadence 究竟是什么
在你动手之前,先讲讲整体的大图景,因为其中有几个概念比较微妙,值得先弄明白:
区块链需要它的验证者们就区块的顺序达成一致,即便其中一些验证者可能出故障、很慢、或者在主动作恶。Cadence 是一个**拜占庭容错(Byzantine-fault-tolerant,简称 BFT)**的共识协议:拜占庭容错意味着,只要作恶的验证者不超过 33%,它就能持续正确地运行。
但 Cadence 同时还解决了第二个问题:MEV / 抢先交易(front-running):
问题在于: 当交易待在一个公开的内存池(mempool)里等待被打包时,任何人都能看到你的交易并加以利用。比如,他们可以抢先于你的交易下单、对你的兑换做三明治攻击(sandwich),或者为了牟利而重新排列交易的顺序。
解决办法是一个加密内存池(encrypted mempool):交易会一直保持加密,直到它们的顺序被锁定的那一刻。如果在一笔交易的位置被决定期间没有人能读到它,那就没有人能抢先于它。Cadence 把这一点直接内建进了它的共识里。
所以 Cadence 的一个「时隙(slot)」同时完成两件事:
- 达成一致:确定哪些提案会进入区块(BFT 共识)。
- 揭示:只在这份一致意见形成的过程中,才把交易揭示出来。
其中的精妙之处在于这两件事是如何被编织在一起的——你在构建投票步骤时就会看到。
说 Cadence 具备拜占庭容错(BFT)是什么意思?
在一个加密内存池(encrypted mempool)里,你能看到其中的交易吗?
快速回顾一些词汇
这里有几个有用的术语值得记住:
验证者(Validator)。 参与共识的一个节点。
时隙(Slot)。 一个区块被提议并达成一致的那段时间。
截止时间(Deadline)。 验证者们拥有同步的时钟,所以全部 200 多个验证者对时间是有共识的!截止时间就是一个时隙的提案必须送达的那个时刻。
提议者(Proposer)。 一个有资格为某个时隙建议交易的验证者。大多数区块链每个区块只挑选一个提议者。Cadence 会挑选好几个。每个提议者提交它自己的一份交易列表,而该时隙的区块会把所有这些列表合并成一个。
法定人数(Quorum)。 法定人数是让一个决定变为最终所需的最少投票数。在 Cadence 中,这意味着超过三分之二的验证者做出了相同的投票。
加密是如何工作的
对 Cadence 来说,目标不只是「把交易藏起来」。它更强:目标是让验证者们在谁都无法读到里面交易内容的情况下,就提案的顺序达成一致。共识是盲着跑的。只有当顺序被锁定之后,才会有东西被解密。如果在一笔交易的位置已经固定之前你都读不到它,那你就无法抢先于它。
为了加密交易,Cadence 使用了门限加密(threshold encryption)。它的意思是,一把密钥被拆分成许多份,每个验证者持有密钥的一片。
为什么不干脆用一把解密密钥? 因为谁持有它,谁就掌握了全部的 MEV 权力——他可以提前解密、偷看接下来会发生什么,然后抢先于所有人。
门限加密。 取而代之的是,解密密钥在一次性的设置仪式(一次分布式密钥生成,即 Distributed Key Generation,简称 DKG)中被拆分成 N 份,每个验证者一份。没有任何人会持有完整的密钥。任意 f + 1 = 4 份就能重建出它;少于这个数则不行。f 是协议能容忍的故障节点的最大数量(33%)。
精妙的地方在于:这些份额搭着投票一起走。 一个验证者不会想什么时候释放自己的份额就什么时候释放。它会把自己的份额附在它的投票上。 所以份额只有在验证者投票时才会变得可用——也就是说,只有当区块顺序被决定时,交易才会被揭示。
于是这节课开头提到的那两件事——达成一致与揭示——是通过同一批消息发生的。一张投票同时表示「我把这个提案包含进来」以及「这是我那一片密钥」。
在你的模拟器里,这一切对你来说都是假定好的/不可见的。 没有真正的 DKG,也没有真正的密文。一个提案只是携带一个 encrypted 标志,每个验证者的「密钥份额」就是它自己的 id,而「解密」就是在有 4 片不同的密钥出现在投票里之后,把 encrypted 翻转成 decrypted。其中的逻辑——只有在 f + 1 个验证者参与之后才揭示——和真实的一模一样;只有密码学部分是伪造的。
在这节课里,先不用操心加密具体是怎么发生的,也不用操心共识的细枝末节。你只需要知道:
- 有一个加密内存池,里面装着你的私密交易
- 验证者(提议者)从内存池里取交易。此时交易仍然是私密的
- 交易只有在区块顺序被确定之后才会被揭示。
为什么要把解密密钥拆分成多个份额,而不是把整把密钥交给某一个验证者?
验证者在什么时候释放它手中那一份解密密钥?
Cadence 的一个时隙是如何工作的
下面是所有的步骤:
- 有若干个验证者。在这些验证者当中,有 3 个是提议者。随着一个个时隙被填满,提议者会轮换。
- 提议(Propose): 每个提议者都构建一个提案,也就是从加密内存池里取出的一捆加密交易。提议者对交易做哈希,把它拆成若干分片,然后给每个验证者发送一个分片。每个验证者都可以通过对照哈希来确认自己的分片没有被篡改。
- 首轮投票(First Vote)。 当截止时间过去后,每个验证者都会根据自己的收件箱为每个提议者各投一票:
每张投票都会带上两样东西:
- 这张投票是对可用性(「我及时看到了它」)和完整性(分片能与其 id 校验一致)的证明。它并不是对交易内容的检查——那些内容仍然是加密的。
- 每张投票都携带投票者的密钥碎片。记住,每个验证者都持有解密密钥的一片(门限加密)。一旦某个验证者集齐了 f + 1 = 4 片不同的碎片,它就能重建出解密密钥并解密提案。这个解密发生在区块顺序被决定之后(此时已经进行过若干次投票)。
3. 提交投票(Commit Vote)。 一旦某个验证者对每一个提议者都做出了裁定(推测性最终性,speculative finality),它就会就自己记录下来的结果广播一张提交投票。当有 2f + 1 张一致的提交投票落地(在我们的例子里是 7 张),该时隙就达到最终确定。
4. 区块。 达到最终性时,把所有被纳入的提案里的交易合并(去掉重复项,跳过那些被判「出局」的提案)成一个区块,并把它追加到链上。然后轮换提议者,进入下一个时隙。
重要提示: 从概念上讲,Cadence 被拆分为 Chorus 和 Conductor 两部分。步骤 2-4 被称为 Chorus。
时隙本身的调度由第二个组件 Conductor 负责:它按固定的间隔开启一个新时隙,无论上一个时隙是否已经完成,于是多个时隙会同时运行。这个技巧叫做极致流水线(extreme pipelining)。不用去背这些。这节课的目标只是建立一个基本的理解!
一次首轮投票实际上证明了什么?
在 Cadence 中,是什么让一个时隙达到最终确定?
现在我们来构建它。
动手实践:构建 Cadence
你会一次一步地给你的智能体(agent)下提示词。每一步的结尾都有一个检查点。如果它跑偏了或者过度构建,就打断它,把提示词再粘一遍,并且缩得更窄一些。
对于第 3-5 步,粘贴之前一定要先把提示词读一遍!在没弄懂上一步之前,尽量不要进入下一步。
Step 0:搭建你的项目
第 1-7 步里的提示词无论你用哪个智能体都是一样的。唯一的区别在于你把它们粘到哪里。
在 Replit 里新建一个 App,并打开 AI 面板。就这么简单。Replit 会替你处理好环境、开发服务器和预览窗格。
对于下面的每一步,把提示词粘进 AI 面板。当智能体完成后,对照该步骤的 Verify(验证)部分检查右侧的预览窗格。如果预览没有自动刷新,点一下窗格顶部的刷新图标。
Step 1:搭建仪表盘(含验证者滑块 + 悬停说明)
I'm building a browser simulator of Cadence, a BFT blockchain consensus
protocol. No backend, no real cryptography — all state in memory.
- Scaffold a Next.js app (App Router, TypeScript, Tailwind), one route at /.
- Dark dashboard, max-width ~1150px, single column, sans-serif.
- Put the data model in a lib file. Define: a Validator { id, inbox: [],
status: "Idle" }, and a SimState holding { slot: 1, deadlineMs: 150,
clock: 0, validators, proposers, log: [], chain: [] }.
- Proposers for a slot rotate by ONE: slot 1 -> [0,1,2], slot 2 -> [1,2,3],
wrapping mod N. Write proposersForSlot(slot, n). Slot 1 -> [0,1,2].
- Derive from N (compute, never hardcode):
f = floor((N-1)/3); Quorum = N - f; Rebuild = f + 1
For N=10: Quorum 7, Rebuild 4.
- TOP ROW: the page title on the left, and on the right a "Validators" range
slider (min 4, max 22, step 1, default 10) with the current value shown.
Changing it re-initializes the sim with that many validators; Quorum and
Rebuild recompute from the new N.
- HEADER: "Slot X", "Deadline: 150 ms", "Clock: 0 ms", "Quorum: 7",
"Rebuild: 4"; three stage buttons Propose / First Vote / Commit Vote (all
DISABLED for now); a Reset button.
Make "Quorum" and "Rebuild" HOVER TARGETS (dotted underline, help cursor).
On hover, show a small tooltip with the live N and f substituted in:
- Quorum: "Quorum = N - f = 7. With N=10 validators, up to f=(N-1)/3=3 can
be Byzantine. A quorum of matching votes finalizes a decision; any two
quorums overlap in an honest validator. Add validators -> f grows ->
quorum grows."
- Rebuild: "Rebuild = f + 1 = 4. A proposal splits into N chunks; any 4
reconstruct it, so at least one honest chunk is always included even if
f=3 withhold. Bigger set -> bigger f -> more chunks needed."
- VALIDATOR CARDS: a horizontal row, one per validator: "Validator N", a
"Proposer" badge if it's a proposer this slot, a status pill, "inbox 0".
- NETWORK LOG: a scrollable panel, "No messages yet."
- CHAIN: a horizontal strip, "No finalized blocks yet."
- Single useReducer for SimState (actions come next). No consensus logic yet.
Verify: header shows Quorum 7 / Rebuild 4; 10 cards with 0,1,2 badged; the
Validators slider shows 10 and, when dragged to e.g. 7, the card count and
Quorum/Rebuild update (7 -> Quorum 5, Rebuild 3); hovering Quorum/Rebuild
shows the explanation; three disabled buttons; empty log and chain.
Step 2:模拟网络与时钟
Add a simulated network and clock. Everything from here flows through it.
- Message model: { from, to, type, payload, sentAt, arrivesAt, label? }.
Delivery latency is 60ms +/- 30ms of SIMULATED time (random per message),
so [30,90]ms. Add a helper deliveryDelay().
- Keep an inFlight array of scheduled messages and a simulated clock (ms).
- A ticker (setInterval via useEffect) advances the clock while there is
anything to do, and dispatches a TICK. On each TICK: advance the clock a
small step, deliver every message whose arrivesAt <= clock (push into the
recipient's inbox), log each delivery. Freeze the clock when nothing is in
flight and no deadline is pending.
- Keep the reducer PURE for now: roll randomness (delays) in the action
creators and pass precomputed delays into actions. (We revisit this in
Step 6.)
- Log format: timestamp + text. Plain deliveries: "Validator X -> Validator Y:
<type>". Show a small green "active" pulse in the header while the clock is
advancing.
- Add send(from, to, type, payload) on the hook that schedules one message
through the delivery model. Nothing calls it yet.
Verify: nothing visibly changes yet, but the clock infrastructure is in place
and the app still renders and type-checks.
Step 3:提议(含提案标签 + 悬停)
Wire up "Propose" (enabled when no proposal exists yet this slot). When
clicked, each of the 3 proposers, simultaneously:
1. Creates a proposal: 3 fake transaction ids, marked locked: true (a stand-in
for threshold encryption). Compute a short 4-hex-char id by hashing the
proposer id + tx ids (a fake Merkle root).
2. Splits it into N chunks, one per validator: { proposerId, chunkIndex, id }.
3. Sends each validator its chunk through the delivery model.
Also:
- Set the slot deadline to 150ms after the click; show it counting down
(deadline - clock). Keep the clock advancing to the deadline even after all
chunks arrive, so the deadline actually passes.
- Proposer cards flip status to "Proposed".
- Under each proposer's card, show a small PROPOSAL CHIP: an "encrypted" badge
on one line, and below it the word "proposal" followed by "id <hex>". Make
the word "proposal" a hover target (dotted underline) with a tooltip:
"The transactions are hashed into this id, then split into chunks — each
chunk can be verified against the hash." Use a FIXED-position tooltip so the
scrolling card row doesn't clip it.
- Log each chunk delivery: "Proposer 0 -> Validator 7: chunk (id a3f2)".
- Enable "First Vote" once the deadline has passed.
Don't have validators react yet — chunks just land in inboxes (each validator
ends with 3, one per proposer).
Verify: click Propose; 30 chunk deliveries log, proposer cards show
"encrypted" chips (hovering "proposal" shows the tooltip), the deadline counts
down, First Vote enables at 0, each inbox reaches 3.
Step 4:首轮投票(投票、密钥碎片、解密)
Wire up "First Vote" (enabled once the deadline passed). When clicked, every
validator casts one vote PER PROPOSER, based on its inbox:
- If proposer P's chunk arrived before the deadline: { type:"yes", id, chunk }.
- Otherwise: { type:"no" }.
Every vote carries keyPiece = the voter's id (its share of the decryption key).
Broadcast every vote to ALL validators through the delivery model. Flip all
cards to "Voted".
As votes land, tally per proposer (dedupe by voter+proposer):
- Under each proposal chip, show a live tally "yes 8 / no 0".
- Green check once quorum (7) yes match; gray "OUT" once it can't reach quorum
(no >= N - Quorum + 1).
Decryption: each vote carries one key piece. Once a slot has collected Rebuild
(f+1 = 4) DISTINCT key pieces, the decryption key reconstructs and the
proposals decrypt: flip the chip "encrypted" -> "decrypted" and reveal the tx
ids. Log "4 key pieces collected — proposals decrypted".
Note: decryption (4 pieces) happens BEFORE quorum (7 votes) — different
thresholds.
Verify: after Propose + deadline, click First Vote. Tallies climb to yes 10 /
no 0 per proposer (chunks all arrived before the 150ms deadline since delivery
is <=90ms), each hits the green quorum check, and the chips flip to decrypted
with tx ids shown once 4 key pieces land.
Step 5:提交投票与最终确定
Wire up "Commit Vote". Introduce speculative finality first:
- A slot reaches SPECULATIVE FINALITY once every proposer is decided (quorum
yes = included, or can't-reach-quorum = out). Record the clock time. Enable
"Commit Vote" then.
- When Commit Vote is clicked: every validator broadcasts a commit vote
carrying its recorded result (a digest of the included-proposal set) to all
validators. Flip cards to "Committed".
- Tally commit votes. On quorum (7) MATCHING commit votes, FINALIZE the slot:
record the clock time.
On finalization, build the block:
- Merge the transactions of all included proposals (quorum-yes ones), dropping
duplicate tx ids, skipping OUT proposals.
- Append to the Chain strip. The block card shows: slot number, number of
transactions, and two times measured FROM THE DEADLINE — "spec: Xms" and
"final: Yms".
- Log "Slot 1 finalized. Included proposals merge into one block."
- Then advance: slot + 1, rotate proposers (slot 2 -> [1,2,3]), reset
validators to Idle with empty inboxes, clear the per-slot state, re-enable
Propose.
Verify: Propose -> (deadline) -> First Vote -> (speculative) -> Commit Vote.
A block appears with a tx count and both times. Run the cycle again and confirm
slot 2's block appears with proposers [1,2,3].
到这里,快速路径就完成了:一个时隙,全程手动驱动。
Step 6:走向并发——每时隙状态、Conductor 与自动运行
这是一次大重构。 单时隙的状态会变成每时隙的状态,并且会有一个调度器同时运行许多时隙。预计需要多次迭代。先把第 1-5 步做扎实,并且在这一步之后保持手动模式仍然可用。
Refactor to run many slots concurrently, then add Auto-Run.
DATA MODEL:
- Introduce SlotState { slot, proposers, deadlineAt, proposals, votes,
commitVotes, speculativeAt, finalAt, proposed, firstVoted, committed }.
SimState now holds: clock, tau (default 150), autoRun, outage, validators,
slots: SlotState[], chain, pending (finalized out of order), nextChainSlot,
inFlight, log, and conductor fields (nextSlotNumber, nextProposeAt).
- Validator becomes just { id, inbox } — DERIVE its display status from the
focus slot in the UI, not stored on the validator.
- Messages carry a `slot` field; route each delivery to its SlotState.
FOCUS SLOT (what the cards mirror): pick the MOST-ADVANCED in-flight slot
(committing > voting > proposing, tie-break by highest slot number) so the
encrypted -> decrypted -> voted progression is actually visible. Show a
"showing slot X" chip near the cards.
ARCHITECTURE:
- Move randomness (jitter, tx ids) INTO the tick now (the reducer becomes
impure), and disable React StrictMode (reactStrictMode: false in
next.config) so its double-invoke doesn't double-roll the randomness.
- Base each slot's tally on ONE representative validator's inbox (an
"observer", e.g. validator 0) — the votes addressed to it — so quorum forms
after a real network round, not an artificially-fast global first-arrival.
- SPECULATIVE FINALITY = all first votes for the slot delivered to the
observer (this makes finalization take longer than one tau).
HEADER ADDITIONS:
- A "tau" slider (50-500ms, default 150). It sets the deadline window: in
manual mode a slot's deadline is now clock + tau; in Auto-Run it's the block
interval. (Rename deadlineMs -> tau.)
- An "Auto-Run" toggle.
- An "in flight" counter = slots past their deadline, not yet finalized.
AUTO-RUN: when on, a Conductor schedules a new slot every tau WITHOUT waiting
for earlier slots. For each slot it auto-fires: propose (chunks sent tau before
the deadline), first votes at the deadline, and commit votes once speculatively
final. Disable the three stage buttons while Auto-Run is on. Keep MANUAL mode
(buttons) working when Auto-Run is off.
LOG: tag every line with its slot number (e.g. "S6"), and keep only a tail
(cap ~200 entries) since volume is high.
Verify: manually run a slot as before (still works). Then toggle Auto-Run:
blocks appear roughly every tau, the "in flight" counter sits low and steady,
the cards cycle through slots showing decryption, and every log line is
slot-tagged. Move the tau slider and watch the cadence change.
Step 7:有序的链、刹车机制与网络中断
Finish the pipeline with four behaviors.
ORDERED CHAIN: append blocks to the chain in SLOT ORDER even when finalization is out of order. Keep a contiguous `chain` plus a `pending` set. When a slot finalizes: if it's the next expected slot, push it and drain any pending that now extend the chain; otherwise hold it in pending. Render pending blocks DIMMED with "waiting for slot X" (the earliest missing slot) until the gap fills.
BRAKE: think of slots grouped into windows of 8, the next window opening only when 6 of the current 8 have finalized — which bounds unfinalized slots at 8 + (8 - 6) = 10. Implement it as: the Conductor won't schedule a new slot while there are already 10 unfinalized slots in flight. When it's blocked it STALLS; on resume it starts scheduling "from now" (not catching up), which leaves a visible GAP in the deadline schedule.
FAULTY PROPOSER: a "Faulty Proposer" button. Clicking it makes the proposer of the NEXT scheduled slot go silent: its proposal is never sent, so that slot cannot finalize normally. After the slot's deadline passes plus a timeout (say 2x tau), validators give up and finalize it as an empty SKIPPED block. Meanwhile the clock keeps running and later slots keep finalizing, so their blocks sit dimmed in pending with "waiting for slot X" until the skipped block lands and the chain drains.
OUTAGE: an "Outage" toggle. While on, NO messages are delivered — they queue in inFlight. But the clock KEEPS ADVANCING (wall-time passes even while messages sit queued), so slots still pass their deadlines and the Conductor still schedules up to the brake cap. When the toggle turns off, the queued messages deliver.
Verify:
- tau=150 -> a block every ~150ms while finalization takes longer than 150ms; in flight hovers ~1-2.
- tau=50 -> a block every ~50ms (faster than one 60ms delivery); in flight higher but steady.
- Clicking Faulty Proposer -> that one slot misses its deadline, the next few blocks show dimmed with "waiting for slot X", in flight bumps up slightly but stays well under 10, then the skipped block lands, the pending blocks drain in a burst, and the beat resumes. The brake never engages.
- During an outage, in flight climbs and STOPS at 10; on recovery there's a burst of blocks, one visible gap in the schedule, then the steady beat resumes.
这就是全部了——正是这个应用。
你刚刚构建了什么
退一步,看看正在运行的东西。
你实现了 Cadence 的快速路径——也就是这个协议真实的形态。一个时隙经过 提议 → 首轮投票 → 提交投票,从另一端出来的就是一个被最终确定的区块。
- 提议者提出加密的提案,用一个默克尔根(Merkle root)id 打上指纹,并被纠删码(erasure coding)切成分片——这样就没有任何单个发送者会成为瓶颈,而且任意 f+1 个分片就能重建出提案。
- 首轮投票身兼两职:它既是对可用性 + 完整性的 BFT 投票,又释放了每个验证者的密钥份额,因此提案恰好在网络对它做出承诺的那一刻被解密——绝不会更早。这就是加密内存池、抗 MEV 的保证,用一条消息就表达了出来。
- 推测性最终性(所有提议者都已裁定)是提交轮次的前提,而 7 张一致的提交投票让它变得不可逆——因为任意两个由 7 票组成的法定人数一定会在某个诚实的验证者上重叠。
- 区块把被纳入的提案合并起来,链则按时隙顺序增长。
每一个困难的概念都在语境中出现了一次:用于安全性的 2f+1 法定人数、既用于重建又用于解密门限的 f+1、用于完整性的默克尔根、以及用于顺序公平性的门限加密。
与生产环境的 Cadence 有何不同
你的模拟器抓住了协议的逻辑;而生产环境的部署会在此之上叠加真正的工程:
- 真正的密码学。 投票上有真正的 BLS 签名、一次真正的分布式密钥生成(DKG)产生真正的门限加密密钥份额,以及每个分片都带默克尔证明的真正纠删码。你的版本用的是一个
locked标志、keyPiece = 验证者 id,以及一个伪造的 4 字符哈希。 - 真正的网络。 你的是一个带随机延迟的投递模型;生产环境是一个真正的 p2p gossip 网络,有真正的网络分区和真正的对手。
- 完整的流水线。 生产环境用一个真正的调度器和背压(back-pressure)同时运行许多时隙(你可选的自动运行拓展就是尝到了一点这种滋味)。
- 真正的交易。 生产环境会把一把对称密钥加密进门限密文里,再用它来锁定完整的交易字节;你的版本直接加密的是小小的伪造 tx id。
- 对恶意验证者的处理。 生产环境会检测并罚没(slash)双签(equivocation)和无效份额;你的版本假定每个人都是诚实的。
密码学与协议的核心——提议者、基于法定人数的最终性、投票时先加密后解密、以及按序组装链——正是你所构建的东西。
你的模拟器和生产环境的 Cadence 之间最大的区别是什么?
X(Twitter)
限时活动:如果你把你构建出来的东西截图发到 X(Twitter)上,我们就会用 @buildanythingso 这个账号转发它!
术语表
- 验证者(Validator) —— 共识的参与者;共有 N 个。
- 提议者(Proposer) —— 在这个时隙里提出提案的验证者;每个时隙 3 个,每个时隙轮换一个。
- 时隙(Slot) —— 协议的一轮;最多产出一个区块。
- f —— 能容忍的拜占庭验证者的最大数量;
f = ⌊(N−1)/3⌋。 - 法定人数(Quorum) ——
N − f = 2f + 1;达到最终确定所需的一致投票数。任意两个法定人数总会在某个诚实的验证者上重叠。 - 重建数(Rebuild) ——
f + 1;重建一个提案所需的分片数,也是解密所需的密钥份额数。 - 分片(Chunk) —— 提案经纠删码切出的一片,每个验证者一片,携带提案 id。
- 默克尔根 / id(Merkle root / id) —— 一个提案的指纹;让验证者能够验证某个分片属于它、且未被改动。
- 门限加密(Threshold encryption) —— 提案被加密;解密密钥被拆分成 N 份,其中任意 f+1 份就能重建出它。
- 密钥碎片(Key piece) —— 一个验证者持有的那份解密密钥,通过附在投票上来释放。
- 首轮投票(First Vote) —— 为每个提议者各投一张 yes/no 票,证明可用性 + 完整性;同时携带一片密钥碎片。
- 推测性最终性(Speculative finality) —— 每个提议者都已裁定;一个验证者有把握了,但尚未提交。
- 提交投票(Commit vote) —— 一个验证者对它记录下来的结果的证明;7 张一致 = 最终性。
- 最终性(Finality) —— 不可逆的一致;区块被产出。
- tau —— 区块间隔 / 从提议到截止时间的时间窗口。
现在你对 Cadence 有了最直接的理解:不是通过读关于它的文章,而是通过构建一个能工作的模型。每一个共识协议,都是把你刚刚接起来的那些相同的活动部件——提议者、投票、法定人数、最终性——以不同的方式排列组合而成。有了这个基础,你读的下一个协议就会变得可接近,而不再晦涩难懂。
0/7 正确
0% — 全部答对即可完成