WEB3核心技术的一种实现:时间长河共识算法(TRC, Time-River Consensus)
副题 抽签分权 · 焊接定史 · 信用担保 · 追偿平复
版本 落地版 v4.2 状态 完成落地评审
结构 第一部分为讲解稿;第二部分为白皮书稿。节点规模交由部署决定,协议不再推算。
第一部分 讲解稿
0 它是什么
一条单父哈希链。每一环由一个人焊上去。
谁焊,不靠商量,靠抽签。
焊坏了怎么办,不靠投票否掉,靠举证、罚没、追偿。
整条链上没有一处需要"多数人点头":判定靠规则,收场靠顺延,处罚靠证据。
这是它和现有共识最根本的差别。
0.5 为什么叫时间长河
这个名字来自一个关于时间的猜测,不是修辞。
时间的特性,不是"在流",而是"改不动"。
我们感觉时间在流逝,其实是因为回不去。真正让人确认"过去存在"的,不是钟表的滴答,而是昨天做过的事,今天改不了。假如历史可以随意改写,"昨天"和"今天"就没有区别,时间也就不成立了。
所以让时间成其为时间的,是不可逆。
这条算法就是把这句话做成结构:
| 时间的特性 | 链上的实现 |
|---|---|
| 历史极难改变 | 焊接:改一环,后面每一环都得重焊 |
| 时间只向前 | 刻度只增,闭合过的不可回退 |
| 时间由事件显现 | 没有闭合,就没有下一格 |
| 静止期无公共时间 | 没有事件,就没有共同可见的刻度 |
四条特性,四条结构,一一对应。它叫时间长河,因为它要仿的就是时间的这四条性质,不是别的什么。
反过来说:如果为了活性和方便,允许历史被悄悄改写,那它就不再是一条时间长河,只是一本可以修改的账。
(说明:这是动机,不是论证。白皮书部分不依赖这段话成立;它只解释这套设计为什么长成这样。)
1 起点:出块人不能靠投票选
如果出块人靠投票选,就要先通信,再计票,再公布。通信需要时间,计票需要时间------而"时间"恰恰是这条链要解释的东西,不能先借来用。
所以出块人必须满足一个更强的条件:每个人各自在家里就能算出同一个答案,一句话都不用说。
这不是效率考虑,是逻辑顺序考虑。先有"共同",才有"时间";不能先有"时间",再造"共同"。
2 抽签名册:一本谁都改不了的书
有一本早已写定的书,翻到第几页就是第几页。所有人翻到同一页,看到同一个数。
它为什么合适?三个原因:
一、它没有作者。 没有人能说"这本书是我写的",也就没有人能偷偷改一页。这是它比"某个机构发布的随机表"强的地方------后者的中立性要靠信任,前者不靠。
二、它不用通信。 每个人手上都有同一本,翻页不需要问别人。出块人的身份因此是零消息确定的。
三、它是无限的。 链要走多久,书就有多长。
这一层的目的必须说准 :不是为了"让人猜不到下一个是谁",而是为了让人无法把自己封闭起来。
抽签是均匀的。一个占了几分之一的集团,无论占多少位置,被抽中的那一格仍有确定的概率落在外面的人身上。资源买得来概率,买不来确定。
所以这一层保证的不是"不能作弊",而是:
作弊无法成为稳定状态。
因为要稳定作弊,就得永远抽中自己人;而只要还有一个外人在名单上,这件事就做不到。
3 翻到哪一页,由交易量决定
如果按轮次翻页,节点可以挑时机------想让自己被翻到,就在那一页之前凑上去。
所以要换成一个已经长在历史里的量。交易量就是这样的量:它只能一笔一笔堆出来,而且堆在已经焊死的环里。想改它,就得改写历史,而历史改不动。
于是翻页的手不在任何人手里,在历史自己的累积里。人可以往里加交易,但加了之后翻到哪一页,他控制不了。
这就是"随机因子是交易量"的意思:交易量不是权重,是翻页的依据。
一个必须补的细节:读到的不能只是"一位数字"。一位数字只有十个取值,取模之后,永远只有十个位置有机会------这会把"人人有机会"变成"只有十个人有机会"。所以要从那一页起连续读若干位,凑成一个大数,再折进人数里。这一步不难,但漏了就把整个第一层废掉了。
还有一处要防 :翻页用的必须是过去的交易量,不能包含当轮正在打包的交易。否则出块人自己就能决定下一页翻到哪里,等于自己给自己抽签。
4 焊接:让历史无法被偷偷替换
新块头里写进上一环的哈希,再签上自己的名。
这动作叫焊接。焊上之后,谁想抹掉这一环,就得把后面每一环重焊一遍。
所以使一个人成为历史一部分的,不是同意,是遮挡------他在前面挡住了所有后来者。
这一步没有替代方案。它不是选择,是定义。换成"只写高度"或者"只写序号",遮挡关系就断了,历史立刻变成可以偷偷替换的东西。
5 时间:只在闭合处产生
这里要慢一点,因为它是整套东西的枢纽。
只有焊死之后,下一页才能翻开------因为翻页要用这一环的交易量,而这一环的交易量在它焊死之前并不存在。
所以在焊死之前,"下一环是谁"在物理上没有答案。不是没人知道,是那个数还不存在。
于是:
每一环的闭合,就是"下一刻"的诞生。
这跟我们的习惯是反的。习惯说时间先流过去,事件在里面发生。这里反过来:先有事件闭合,然后时间才有了下一格。
没有闭合,就没有下一格。静止期不是"时间在走但账本没动",是没有公共时间------因为没有任何东西被共同看见。
6 为什么不要投票:否决权与追责权是两种东西
投票在共识里干的事,说到底只有一件:否决。多数人说"我不接受这一块",这一块就作废。
但还有另一种办法:不否决,只追责。谁作弊,谁拿证据出来,谁挨罚。
否决需要多数,追责只需要证据。
证据如果能自证------比如同一个人在同一高度对两个不同区块签了名,两段签名摆出来,谁都能验------那就不需要任何人点头。规则即判决,即刻生效。
这就是"符合天道即刻判赢"的字面含义:合规就是赢,不合规就是不存在。 中间没有一段等待多数表态的时间。
撤掉投票,还顺带带来一个意外的好处:能罚的罪只剩一种。没有见证签名,整个系统只剩"焊接签名"和"报名签名"两种。而"同高双签"是其中唯一无歧义的罪------没有解释空间,也不可能栽赃。
签名面越小,冤案越少。而永久除名是不可逆的刑罚,这一点很要紧。
有人会问:抓到双签,谁来收场?
这里也不需要投票。
因为出块人是推导出来的,每个槽位后面跟着谁也推导得出来。双签一旦成立,那个槽位就在本高度作废,顺延到下一个下标------下一个是谁,人人算得出,不需要商量。
所以收场的人和出块的人一样,都是算出来的:
- 作废的槽位不重来;
- 顺延者补上这一块,块里必须处理作弊:查封、补平、除名;
- 不处理,就是共犯,按 §9 第四条办;
- 处理完,下一格恢复抽签。
所以整条链上没有一处需要多数人点头。判定靠规则,收场靠顺延,处罚靠证据。
7 作弊长什么样:一个账户变成负数
这是整套设计里最省力的一步。
如果作弊表现为"两条都声称自己是正统的历史",就必须有人来裁断哪条是真的。裁断一旦交给多数,多数就成了权力------这正是要躲开的东西。
(双签确实会造出两条分支,但它的收场不靠裁断,靠顺延:作废、跳到下一个下标、由下一个人处理。见 §4.9。)
而如果作弊表现为一个账户余额变成负数,就完全不需要裁断。它就是一笔算错的加法,任何人本地一算就能看见。
所以账本分两层处理:正常的超花在块内就被挡下 (余额不足,交易失败,谁也不用赔);绕过检查、把负数写进账本的,才叫作弊------那个负数就是痕迹。
这不是纵容,是分清两件事:失败是规则内的结果,负数才是规则被破坏的证据。前者不需要处理,后者一旦出现,就必须被记录、被追偿。
8 质押不是保险,是押金
每一笔交易由发起人指定一个守护者背书,守护者的质押就是它为这笔交易押上的钱。
它不担保"这笔钱一定付得出"------余额不足时交易直接失败,谁也不用赔。它担保的是自己不会配合做假:若它放一笔付不出的转账进块、把余额写成负数,那个负余额就是证据,质押要被全额查封去补。
所以质押量不是声望,是作恶成本的上限:押多少,最多就赔进去多少,一分不多。额度规则(0.9 × 质押)因此不是"信用额度",是"一次能造成的最大破坏"。
这两件事的区别很要紧:保险赔意外,押金罚作弊。 前者要收款方事后索赔,后者只要收款方做一件事------等块。块封了、交易在块里没标失败,钱就是真的;标了失败,就是没付。
9 四条硬规则,一条都不能省
一、足额背书才有效。
交易入块时必须校验:这个守护者当前还没结清的背书总额,加上本笔金额,不超过它的可用额度(0.9 × 质押 减去 已用敞口,§7.2)。
为什么必须有:额度就是作恶成本的上限。没有它,一个守护者可以配合把任意金额写成负数,破坏规模不再由质押锁死,"作弊不划算"这句话当场失效。
二、敞口上限。
一个守护者可以同时背书很多笔,质押必须覆盖全部未结清的背书,不是单笔。
为什么必须有:否则一个守护者给一百笔背书,一次罚没就把九十九笔悬在空中。那不是保险,是放大杠杆。
三、收款方要能验收。
付款方选守护者,收款方就该有权拒绝背书不足的款项。
为什么必须有:收款方的安全来自"等块"------块封了、交易没标失败,钱才真的到;标了失败,就是没付。验收的意义是把"注定失败"挡在交付之前:背书不足的款项,失败的概率更高,收款方有权不收。
四、查验即担保:不查等于共谋。
守护者在交易上签字,签的就是"我验过"。若这个块里出现了作弊------负余额、双签、状态根对不上------而签字放行,那就不是"疏忽",是共同作弊。
处罚一样重:全部罚没 。缺口先赔,赔完剩下的全部归举证者。
为什么这条能成立,而"不作为不可罚"在这里不成立:因为查验是一件做出来的事 ------你签字了。签字就是作为。"没查验"不是沉默,是签了一个假字,有签名可对照。
这一条把最大的口子补上了:审查、放水、装看不见,都得落笔;落笔就留证。
10 一个干净的结果
把这四条写进去,会看到一个巧合:
额度规则既是作恶成本的上限,也是破坏规模的上限。
因为能写进块的最大负数就是质押。作弊者能造成的破坏,不超过他被冻结的钱,所以净损益永远是负的。
同一条规则,两头都管。这种"一条规则顶两条"的地方,通常是设计对了的信号。
11 永久除名:一种不需要人执行的刑罚
这里必须先把两种事情分开,否则会把好人吓跑:
| 失职 | 欺诈 |
|---|---|
| 没来、没出块、断网 | 双签、负余额、放过没查验的块 |
| 只丢位置,不罚钱 | 全部罚没,余额归举证者 |
| 观察期 → 重复才除名 | 一次即除名 |
理由很实在:机房断电不该等同于作弊。若一次断电就永久除名,等于要求守护者做到百分之百可用------那会把准入抬到没人愿意进,最后名单里只剩大机构,反而更中心化。
钱归钱,位归位。
欺诈者被举证成立后,除了罚没,还永久失去被抽中的资格。
这一点比罚没更彻底,而且有个漂亮的特性:它不需要执行。
罚没要有人写进链,要按规则分配这笔钱。除名不用------身份表上把那个位置标死,抽签是规则,规则自己会绕开他。
不需要执法者,不需要投票,不需要任何人记住这件事。
而且它是结构性的:罚钱是一次性的,除名是永久的收入剥夺。天道不罚款,天道只是不再给他位置。
12 追偿:账会自己平
顺序是固定的:
- 作弊被记进历史:账上出现负数,成为一个缺口;
- 罚没的质押先进赔偿池,优先补那个变负的账户;
- 经济重新平衡;
- 结余用来奖励举证者;
- 缺口归零,事件关闭。
只有作弊才走这条流水线。失职不产生缺口,也就没有可赔的东西------它只丢位置;失败的交易也不产生缺口,它就是没付。
期间每一笔赔偿都是链上的事件,也就都是刻度。
这一步不是慈善,是让作弊从"一次性犯罪"变成"一笔长期负债"。
而且它承认了一件现实:负数一旦写进账本,就是既成事实。系统不能假装没发生,只能记账、追偿、平复。
13 两个市场,互不干扰
到这里,会有两个不同的分配机制同时存在:
- 出块权:抽签。 为了公平,防止资源垄断,保证不可封闭。而且出块不给报酬------维持网络是守护者的义务,不是一笔生意。
- 背书权:按额度。 谁质押多,谁的担保能力就大。费率固定,不竞价;客户端先筛掉"网络状态差"和"额度不够"的,再在剩下的里面等概率抽一个。
一个决定"谁有权力",一个决定"谁敢担保"。
权力靠抽,责任按额度,价格不动。 三件事分开,系统的性格就清楚了。
出块不发钱,这一点值得多说一句:一旦出块有奖励,抽签就会被经济动机污染------被抽中的人想多出,没被抽中的人想抢。抽签的公平性恰恰依赖"被抽中不划算、没被抽中也不亏"。把出块做成义务,抽签才真的只是抽签。
13.1 守护者靠什么赚钱
背书收手续费,费率固定,所以收入跟它背书的金额走。金额越大,收入越高。
而能背多少,受质押限制(见上节第一、二条)。所以:
想多赚,就得多押。
质押不是门槛,是产能 。押得越多,能承接的背书越多,收入越高。这是一条不需要任何人督促的加杠杆路径------只不过在这里,加的是责任。
于是"质押量代表可背书额度"这句话同时是经济激励:守护者有动力把额度做到最厚,因为那直接等于它的收入上限。
14 它不做什么
必须写在前面,否则会被误读:
- 不表达时长。 没有定时转账、没有计息、没有 T+1 结算。静止期没有公共时间,所以"过了多久"不可说。
- 不做分片。 整体性要求所有区块共享同一条历史。
- 不消灭作弊。 它让作弊不划算,并且可以被追平。这是比"不可能作弊"弱、但真正能做到的目标。
- 单笔支付的上限由质押决定,不由算法决定。 链能承载的单笔上限,就是守护者们锁在里面的钱。这是押金制度的本来面貌,不是缺陷。
- 不给做出块奖励。 出块是义务,不是生意。想靠出块赚钱的设计,会在抽签里长出竞价与抢槽。
- 分区后不自动合并。 协议只会挂起等待,合并与双花处置交给人。
15 故障与边界
15.1 节点不提供服务:这是常态,不是异常
先纠正一个看法:抽中的人没来,不是罕见事故,是常态。
一条均匀的时间河需要足够大的体量。节点越多,单点故障对全局的扰动越小;体量不够时,任何一次故障都像往浅水里扔石头。
所以这一条不是漏洞,是运行条件。它靠两道机制兜底。
第一道:准入。
时间守护节点不是想进就进。准入条件之一是网络稳定 ,而"稳定"不是自述,是由现存节点共同考验出来的:负载能力、抗 DDoS 能力、长期在线记录。过不了这一关,连名单都进不去。
这一道把"开一千个号"的成本抬到实打实的运维水平------原先担心的多开问题,在这里被堵住了。
第二道:顺延。
不可抗力真的发生了:机房断电、链路被切、整片区域断网。这时必须有办法把这一格让过去,否则整条河跟着停。
谁有权宣告故障:邻居,五个里三个。
邻居不是"网络上的邻居",是标识哈希最近的五个节点。按哈希距离取而不按索引邻接取,是为了打散地理位置:批量入网、索引连号的节点往往同机房,若互为邻居,一次断电就连作证的人一起消失。
门槛定在五取三:
- 一个,等于给它单方面抢槽位的权力,而且不留痕迹;
- 两个,合谋成本几乎为零;
- 三个,必须动员一个真实的小联盟,而三份签名会永久留在链上。
为什么还留两个缺席的余量:失败处置本来就发生在节点陆续掉线的时刻。若要求最近的三个必须都到场,第二个节点一挂,顺延就永远启动不了------系统反而更脆。
处置流程。
- 邻居中的三个共同签名,宣告"这一刻该槽位没有服务";
- 该刻所有登记的交易标记为失败------不转移、不继承,直接作废;
- 交易退回发起方,重选守护者,重走一遍;
- 该节点进入观察期(失职,不是欺诈,见 §11);
- 反复失职或明显故意,才永久拉黑准入资格。
这条指控的边界:只对空槽位有效。
不需要时间,只需要因果------如果那个槽位已经有块,任何人一查便知,指控自动无效。于是"诬告一个已经出了块的人"在结构上做不到;能诬告的,只剩"他还没出块",而那正是要处置的状态本身。
顺延者拿到的是什么。
拿的是出块的机会 ,不是那些交易的处置权。交易已经作废,退回发起方重发,吞不下去。
而出块本来就没有报酬,所以抢一个槽位的直接收益为零;能捞到的只有"让对手的一批交易失败"这种破坏,而每次破坏都要留下三份永久签名。
规矩写死:故障处置不产生任何财产转移,只产生"作废与重来"。
15.2 不查验就是共犯
块里出现了没被查验的作弊,放它过去的查验者与作弊者同罪,共同罚没,而且是全部罚没。
这一条看起来重,其实最省事:它把"装看不见"从一种追究不了的消极行为,变成了一种留了签名的积极行为。你不签,它进不了块;你签了,你跑不掉。
(细则见 §9 第四条。)
15.3 三条曾悬着的,现在都落了
一、双签之后谁来收场。 曾经想过"由下一任裁定",现在不需要裁定:槽位作废,顺延到下一个下标,由顺延者处理,处置必须写进块里。见 §4.9。
二、出块人和担保人是不是同一批人。 是同一批。出块权与质押绑定,不押钱不能出块------因为出块人唯一的罪是双签,而双签的罚则是全额罚没,没押钱就罚不出东西来。见 §4.10。
三、罚没与奖励怎么分。 已定:欺诈全部罚没,缺口先赔,余额全部归举证者;失职不罚钱,只丢位置。见 §7.6。
真正还悬着的只剩一处:小额市场里质押赚不到钱,安全预算可能随业务缩水。靠最低质押自动上调兜底,但阈值要实测(§7.5、§8.2)。
15.4 一句话
这套东西不消灭故障,也不消灭作弊。它做的是:让故障有处置流程,让作弊有价格,让价格能被追平。
16 概念自检清单
给审阅者:下面每条都是本稿的承重假设,任一条不成立,上层结论跟着塌。
| 编号 | 假设 | 若不成立 |
|---|---|---|
| A1 | 抽签均匀且不可被登记表操纵 | 资源可以买到确定,第一层失效 |
| A2 | 抽签结果零通信可算 | 必须先通信才能定人,退回投票结构 |
| A3 | 随机因子只来自已焊死的历史 | 出块人可操纵继任者 |
| A4 | 签字即担保:焊接与背书都要落笔 | 不作为无法追究,共犯无人可指 |
| A5 | 覆盖规则被严格执行 | 超花上限失控,损失外部化 |
| A6 | 罚没足以覆盖超花 | 缺口永久挂账,第三层失效 |
| A7 | 位置钉死、只滑不排 | 登记表浮动可被用来挤位 |
| A8 | 准入考验真实有效 | 多开节点无成本,抽签被稀释 |
| A9 | 邻居按哈希距离打散 | 同机房节点互为作证人,一次断电全灭 |
| A10 | 顺延门槛五取三 | 一或二则合谋冒充故障,故障处置被滥用 |
| A11 | 双签能被节点看见 | 网络被切成不相交两半时检测不到,退化为分区问题 |
(本表 A1--A11 是假设编号;§12 决策记录里的 A1--A5 是决策编号。两套并存,引用决策时一律写"§12 Ax"。)
第二部分 白皮书(Time-River Consensus)
本部分为实现级规范。第一部分是动机与讲解,不作为论证依据;凡论证均以本部分为准。
1 问题陈述
给定一个不允许外部时钟的对抗环境,构造一条单父哈希链,使其满足:
- C1 唯一性:每个高度至多存在一个合法区块,且任何人独立计算可得同一结果;
- C2 不可回退:已封闭的区块不能被任何合法扩展排除;
- C3 无通信定人:出块人由已固化历史唯一导出,不需任何协商或随机数广播;
- C4 结果判据可本地化:合规即成立;收场的人由顺延算出,也不需要多数表决;
- C5 欺诈有界且可追偿:任何资产缺口在有限步内被清偿,且作弊的期望收益为负。
C1--C3 决定"谁焊",C4 决定"何时算数",C5 决定"焊坏了怎么办"。
2 系统模型与假设
2.1 参与者
| 角色 | 职责 | 是否需质押 |
|---|---|---|
| 发起方 | 构造交易,指定背书守护者 | 否 |
| 中继 | 转发与预校验,无出块权 | 否 |
| 守护者 | 背书交易、查验、可被抽中出块 | 是 |
| 举证者 | 任意主体,提交自证材料 | 否 |
出块权与背书权是同一批人:只有质押中的守护者能被抽中,出块是它的义务,背书是它的生意(§4.10)。守护者集合即身份表。
2.2 网络
异步、可丢弃、可重放、可分区。不假设任何时间上界。节点间以 gossip 传播,连通性由准入条件保证(§7.4)。
2.3 故障
拜占庭节点可任意偏航,但签名不可伪造 、哈希抗第二原像。
本设计不假设任何"多数决定":判定靠规则,收场靠顺延。只保留一条较弱的假设------诚实验证者在名单中占多数,以便证据最终能被写进链;否则作弊永远罚不了,不是因为判不出来,而是因为没人愿意记。
2.4 密码学
Ed25519 或 secp256k1 签名;SHA-256 或 BLAKE3 哈希;Merkle 累加器用于状态根与证据集。
已定,不再二选一:签名 secp256k1(65 字节,必须 low-s),哈希 SHA-256,Merkle 树用于状态根与证据集(§3.7.1、§3.9)。混用两种曲线或两种哈希会让规范序列化分叉。
2.5 关键假设(承重)
| 编号 | 假设 | 若不成立 |
|---|---|---|
| H1 | 抽签输入只取 ≤ h−K 的已固化数据 | 出块人可操纵继任者 |
| H2 | 身份表在窗口内不可被单方改动 | 资源可买到确定 |
| H3 | 邻居按哈希距离选取且地理打散 | 同机房节点互为证人 |
| H4 | 准入考验真实有效 | 抽签被稀释 |
| H5 | 举证材料自证 | 举证退化为权力 |
2.6 术语:事件、刻度、高度
三个词指同一个量:一个区块 = 一个事件 = 一个刻度 = 一个高度单位。
| 词 | 用法 |
|---|---|
| 高度 h | 区块在链上的序号,创世为 0 |
| tick | 区块头字段,恒等于 height。独立保留,只为将来可能的分化留位 |
| 事件 / 刻度 | 高度增量。说"100 个事件"就是"100 个块",两者等量换算 |
为什么必须先定死。 §4.10 的解冻、§8.4 的暂停上限、§4.4 的观察期都按"事件数"计时。若允许各实现自行理解 tick 怎么推进,同一份状态会算出不同的解冻高度,链当场分裂。这也解释了本链"没有时钟"却还能计时:计的是区块,不是秒------所以计时器在静止期不走,这是特性,不是缺陷。
3 数据结构
3.1 身份表
Entry = { slot, pubkey, stake, status, admitted_at, strikes, resume_height, probation_until }
status ∈ { active, paused, probation, banned, unbonding }
- 表为追加式,slot 单调分配,永不复用、永不重排;
- banned 槽位保留,抽签时跳过;
- 表的根 registry_root 提交在区块头,取 h−K 处快照参与抽签。
3.2 区块头
Header = { version, chain_id, height, parent_hash, tick,
tx_root, state_root, registry_root, checkin_root,
gas_used, proposer_slot, round }
Block = { header, sig, tx_envelopes[] } // 签名在头之外
block_hash = SHA256(canonical(Header));签名单独存放,签的是block_hash(§3.9)。- 签名不进 header:否则"先算哈希、再签名"的顺序会因签名长度而改变哈希,且哈希覆盖签名自身,闭环不了。
version起始为 1;round只用于诊断,不参与任何校验。
约束:tx 集非空(无空块);tick = height(§2.6);gas_used ≤ 30,000,000(§9)。
3.3 交易
Tx = { version, chain_id, nonce, from, to, amount,
gas_limit, gas_price, data_len, data }
TxEnvelope = { tx, sig, guardian_slot, guardian_sig }
txid = SHA256(canonical(Tx)) // 不含任何签名
- 签名字段一律在
Tx之外 (TxEnvelope)。理由有两条:签名可变(可塑性),算进 txid 就会让同一笔交易有两个 id;更要紧的是背书签名要签txid,而txid若含背书签名,就成了"必须先知道自己的签名,才能算出要签的东西"的循环。 data_len显式前置长度;不设"省略默认值"的编码(§3.8)。- amount = 0 的交易用于系统事件(报名补录、举证、账单、参数触发、升级指令);
to决定交易类型,四类互斥,没有"既像转账又像调用"的编码:to = 0²⁰:系统交易,data 承载事件本体(§3.6);to ≠ 0且该地址已有代码:合约调用,gas_limit 必填;to ≠ 0、该地址尚无代码、且to = H(from ‖ nonce):合约部署,data 为字节码,部署成功后代码哈希写进该账户(§4.11);- 其余:普通转账,data 必须为空。
- gas_price 是发送方接受的最大系数 :打包高度上的当前系数必须
≤ gas_price,否则该交易无效;实收按当前系数算,差额随未用完的 gas_limit 一起退回。字段保留,是为了让系数调整期间旧交易仍可验证; - 手续费只有一个来源:gas。没有按金额的比例费------金额只决定敞口(§7.2),不决定价格。这是为了让"占一块状态"有明确标价:转 1 个单位和转 100 万个单位,占的区块空间与状态位是一样的。
- guardian_sig 是强制字段:无背书签名的交易无效;把无背书交易放进块,是共谋证据(§4.8)。
3.4 背书
Endorse = { txid, amount, expiry_height, sig }
expiry_height 是保留字段 ,当前不参与任何校验(与 §3.2 的 round 同类)。启用它等于改规则,须硬分叉。不要拿它做"过期即退出敞口"------那等于给背书者一条跑路通道:只要拖到过期,赔付责任就没了。
状态中为每个守护者维护:
exposure(g) = Σ 未结清背书金额
可用额度(g) = 0.9 × stake(g) − exposure(g)
3.5 报名
CheckIn = { tick, slot, sig }
-
每刻度签名一次,由任意节点打包进 checkin_root;
-
允许在后续 W 个刻度内补录(防提议者漏收);
-
补录不回写过去:记录只追加在未来块里,过去的状态根永不改变(§12 A4)。
-
缺席判定是一个纯函数。 按"连续 W 个刻度没有报名"算,且只取已经封口的刻度:
已封刻度 t* := h − W − 1 // 刻度 t 的补录最晚在 t+W 上链 缺席(slot) := 报名树中 [t*−W+1, t*] 没有任何该 slot 的叶 且该 slot 在 B_{h−K} 的 registry 快照中为 active 或 probation其余状态(paused / unbonding / banned)不参与判定,也不会因"连续 W 刻度未报名"被标记离线------暂停本来就不报名(§4.12),两层规则不叠加。
右端取 h−W−1 而不是 h−1:刻度 t 的补录最晚在 t+W 上链,取到这个边界时整段窗口都已封口,判定不会因消息先后而不同。代价是判定滞后约 W 个块------可以接受,顺延与剔除都不要求即时。
3.5.1 受理确认(账单)与到块回执
Bill = { height, slot, txids[], sig } // txids 可以为空
Ack = { height, block_hash, slot, sig } // "我收到了这一块"
- 每个守护者在每个高度交一份,内容是该高度它受理(背书)的交易清单;一笔都没有,也要交空账单。
- 提交方式是零额系统交易(
to = 0²⁰),不收 gas 费------守护行为是义务,不是生意。它仍占区块空间,所以不能无限塞。 - 出块人必须打包账单。 块内账单覆盖的槽位数少于 ⌈2N/3⌉ 时(N 取 B_{h−K} 的活跃槽位数),该块仍然有效,但出块人按下面的表降权。
- 账单里的 txid 必须入块,或在该块的失败清单里。 否则这份账单不计入覆盖------这条把"少打包"和"留账单"两条路一起堵死:想少打,就得丢覆盖;丢了覆盖,就得降权。
- 没交账单的槽位,下一高度不能参与抽签。 账单允许在后续 W 个高度内补录(与报名同窗口),补录只追加在未来块里,不回溯;缺席判定沿用 §3.5 的窗口函数,把"报名"换成"账单"。
- 收到块就回执,回执上链。 邻居收到某高度的块后,发一笔零额系统交易
Ack { height, block_hash, slot, sig }。它和账单一样走链上:可查、可核对。回执只证明"我收到了",不代表认可它。 - 回执可以指向任何块哈希,包括没被采纳的块。 它是"我收到了"的签名陈述,不是"我认可它"------被顺延掉的迟到块同样值得回执,因为那份回执就是矛盾的证据(§4.5)。任何人可以代为打包:举证人手里有那份签名,就能把它送进块。
- 缺回执记一次失职,同一段缺失只记一次。 连续多个高度都没回执、直到重新回执为止,算一段,只记一次------否则一次断网就等于除名,那正是本设计要避开的。失职进观察期 30 刻度(§4.12),观察期内再犯即除名(§7.6)。缺回执的槽位,下一高度不参与抽签,与没交账单同等对待。
- 回执与故障声明互斥 :回过执的槽位,不能再宣告该块"没服务"(§4.5)。回执由下一高度打包,与账单同一个窗口,补录规则也相同。
- 状态里按 (slot, height) 记一个标记与账单哈希,叶类型 0x07。
为什么要有账单。 出块是义务、没有报酬,"少打包"本来零成本:被抽中的节点可以每块只放 1 笔,合法地把吞吐压到 1 笔/块,也可以定向丢弃某个地址的交易,全程不留证据。账单把"我收到了什么、愿意为谁背书"变成一份签名清单------少打包就与账单对不上,不交账单就直接失去下一次抽签资格。打包从"没人管"变成"两边都要留痕"。
2/3 是门槛,不是分片。 缺三分之一以内的账单可以容忍(掉线、延迟),超过就说明出块人没有真的在收集,或者在有选择地收集。所以处罚落在出块人身上,不落在被漏掉的守护者身上------后者可以用补录窗口自证。
降权处罚。 记在身份表里,按累计次数 k 停权:
| 第几次 | 停权高度数 | 恢复方式 |
|---|---|---|
| 1 | 1 | 停满后自动回到 active,计数保留 |
| 2 | 2 | 同上 |
| k | k | 同上 |
停权期间该槽位不参与抽签,也不接受新背书。连续 10⁴ 个事件没有再次触发,计数清零。停权不是第六种状态 :status 仍是 active,靠身份表里的 resume_height 屏蔽抽签,这样五态表不用改,实现也少一个分支。为什么按次递增而不是一次重罚:漏收账单通常是网络问题,一次重罚会把偶发故障变成除名;但反复发生就是能力或意图问题,停权期一次比一次长,成本随次数上升。
3.6 证据
Evidence::DoubleSign = { h, blockA, blockB } 同一 slot 同高两块
Evidence::Overdraft = { h, txid, path } 账户在 h 后为负
Evidence::Complicity = { h, txid } 块内含无背书交易
Evidence::FalseFault = { h, block, acks[], fault_proof } // 回了执又宣告"没服务"
三者均自证:不含任何需要被信任的陈述。
3.7 状态
State = { balances, exposure, stakes, registry, deficits, insurance,
codes, code_hash → bytes, // 合约代码,按哈希存
storage, contract → (key → value) // 合约存储,独立前缀
}
3.7.1 叶类型与树形(冻结)
叶子键 = SHA256(tag ‖ key_bytes)。四类叶进状态树 (根 = state_root):
| tag | 类型 | key | value |
|---|---|---|---|
| 0x01 | 账户 | 地址 20 | balance u64 ‖ nonce u64 ‖ code_hash 32 |
| 0x02 | 合约存储 | 地址 20 ‖ slot 32 | value 32 |
| 0x03 | 质押 / 敞口 | slot_id u32 | stake u64 ‖ exposure u64 ‖ status u8 |
| 0x04 | 系统 | 名称哈希 32 | 任意字节 |
两类叶各进独立树 ,根分别提交在 registry_root 与 checkin_root:
| tag | 树 | key | value |
|---|---|---|---|
| 0x05 | 身份表 | slot u32 | pubkey 33 ‖ stake u64 ‖ status u8 ‖ admitted_at u64 ‖ strikes u16 ‖ resume_height u64 ‖ probation_until u64 |
| 0x06 | 报名 | slot u32 ‖ tick u64 | 签名 65(该刻度未报名则无此叶) |
| 0x07 | 账单 | slot u32 ‖ height u64 | 账单哈希 32(该高度未交账单则无此叶) |
为什么现在就把 tag 写死。 §3.8 规定叶前缀是冻结项,改动等于换链。身份表与报名在 M0 里恒为空树,但 M1 一上来就要写它们;若那时才定 tag,等于在每条节点的状态根上做一次硬分叉。
树形约定,四条都不能由实现自选:
-
深度 256,二进制;每层取键的一位,大端、从最高位开始(第 0 层取首字节最高位);
-
叶哈希 =
SHA256(0x00 ‖ 键 ‖ SHA256(value));内部节点 =SHA256(0x01 ‖ left ‖ right); -
空子树不用裸零,而是一条递推链:
EMPTY[0] = 32 个零字节 EMPTY[i+1] = SHA256(0x01 ‖ EMPTY[i] ‖ EMPTY[i]) 空树根 = EMPTY[256] -
三棵稀疏树(状态 / 身份表 / 报名)的空根一律是 EMPTY256,写成 32 个零是错的。
位序若两边相反,算出来的根一样是"合法哈希",值却完全不同,而且极难从结果反推原因------这类错误只能靠两套实现互相对照才抓得到。
tx_root 不在这三棵树里:它是一棵 2 的幂补零树,叶子是 txid,内部节点 = SHA256(0x02 ‖ left ‖ right),空位补 32 个零,空集根就是 32 个零(§4.2)。两套"空"不得混用:稀疏树用 EMPTY256,交易树用零。
证明格式(冻结)。 包含证明是从叶到根的 256 个兄弟哈希,按深度 0 → 255 排列,每项 32 字节,共 8192 字节。压缩版利用"第 d 层的空兄弟恒等于 EMPTY256−d−1"这一点,只传 32 字节位图(第 d 位为 1 表示该层兄弟非空)加逐项非空兄弟;压缩不改变根,因为根始终在完整的 256 层树上算,压缩只影响传输。非包含证明同理:把叶值当作空,走同一条路径,终点必须落在空位------"账户不存在"必须能被证明,否则无法拒绝伪造的余额。M0 只实现完整版,压缩版留到 M5。
3.8 规范序列化(冻结项)
两个实现必须算出同一个 state_root,因此编码不能有自由度:
| 项 | 规定 |
|---|---|
| 字段顺序 | 按本文档结构体声明顺序,不得重排 |
| 整数 | 大端、定长(u64 占 8 字节) |
| 变长 | 前置 u32 长度,再放字节 |
| 可选字段 | 前置 1 字节标记(0 = 无,1 = 有) |
| Merkle 叶 | 前缀单字节区分类型,取值与键值格式见 §3.7.1;已分配 0x01--0x06,新增只能追加 |
| 状态树 | 稀疏 Merkle 树,键为哈希,深度固定 |
| 哈希 | 单一算法,写死;不得在中途更换 |
以上七条是冻结项:改动等于换链。新增字段只能追加在末尾,且必须升版本号。
3.9 地址、签名与签名域
-
地址 =
SHA256(压缩公钥)[-20:]。全链只用一把哈希、一条曲线。 -
签名 = secp256k1,编码
r ‖ s ‖ v(65 字节),v ∈ {27,28},且必须s ≤ n/2(low-s)。非 low-s 的签名一律无效------否则同一笔交易有两个合法签名编码,§11.3 的签名索引就不唯一。 -
签名对象不是裸哈希,一律加域前缀,防跨语境重放:
sig_payload = SHA256("TRC:TX:" ‖ canonical(Tx)) sig_payload = SHA256("TRC:BLK:" ‖ block_hash) sig_payload = SHA256("TRC:ENDR:" ‖ txid ‖ amount ‖ guardian_slot) sig_payload = SHA256("TRC:CHK:" ‖ slot ‖ tick) sig_payload = SHA256("TRC:PAUSE:" ‖ canonical(PauseSelf)) sig_payload = SHA256("TRC:UPGR:" ‖ canonical(Upgrade)) -
合约里有一处必须提前知道 :Solidity 的
ecrecover用 keccak256 派生地址,与本链的 SHA256 派生不一致。所以合约不能靠ecrecover还原链上地址;需要鉴权的合约应接收公钥、自行算一次 SHA256 再比对。这是"全链一把哈希"换来的代价,写在这里,免得 M4 才发现。
3.10 创世块
创世块是唯一允许 tx 集为空的块(§3.2 的"无空块"从 h ≥ 1 起生效),字段固定:
Genesis = { version = 1, chain_id, height = 0, parent_hash = 0^32,
tick = 0, tx_root = 0^32, gas_used = 0,
proposer_slot = 0, round = 0 }
它的三个根不由"空"决定,而由创世表算出:
state_root:alloc(地址 → 余额)建出的状态树;registry_root:validators(地址、slot、质押、身份哈希)建出的身份表树------创世就有验证者,所以它不是空根;checkin_root:创世无报名,故为 EMPTY256。
创世表以 JSON 描述、由生成器转成规范编码:alloc、validators、params(fee_rate_bp、gas_price、block_gas_limit、registry_lag_k、checkin_window_w、min_stake)。三条硬要求:JSON 只用于生成、不用于运行;生成器必须确定性(键排序、数字用字符串、无浮点);genesis_hash 一经发布不得修改,改了就是新链。
为什么创世的 registry_root 必须现在就算清楚。 它是 §4.1 抽签第一次要读的字段。若让它为空,前 K 个块的抽签就没有名单可读,M1 的第一次出块会直接落空。
4 协议
4.1 抽签(零通信)
S_h = Σ txcount(B_i), i ≤ h − K
registry = load( H_{h−K}.registry_root ) ← 取 h−K 块头那个字段,不回溯状态树
ω = digits_8( π, (S_h + H(registry)) mod 10^6 )
idx0 = rejection(ω, N) ← 严格均匀
idx = 从 idx0 起,跳过 {状态不是 active 或 probation} ∪ {按 §3.5 判为缺席(报名)}
∪ {按 §3.5.1 判为缺席(账单)} ∪ {按 §3.5.1 判为缺席(回执)} ∪ {resume_height > h}
proposer = registry[idx]
h < K 的边界。 h−K 为负时一律取 0:抽签读创世表,S_h = 0。于是前 K 个块由同一个 proposer 产出------这是推导的结果,不是特例,也不需要额外规则。冷启动阶段没有传播抖动要覆盖,K 的缓冲用不上,正好。
拒绝采样:若 ω ≥ N·⌊10^8/N⌋,则取下一窗口 digits_8(π, p+8),直至落入;期望迭代 < 1.05,最坏界由 8 位窗口与 N ≤ 10^3 保证。
π 表就是圆周率的小数位。 取 π 的前 10⁶ 位十进制小数,每 8 位一窗。它没有作者、任何人可用任意精度库自行复算------所以"这张表从哪来、有没有被做过手脚"不是需要信任的问题,是算术问题。
发行版仍内嵌这张表(约 1 MB),只为省掉每次启动算一百万位小数的时间;附 SHA-256 校验和,启动时自检,不依赖任何在线源。
表的哈希在创世块里承诺(§8.1)。节点启动时拿本机表的哈希与创世块比对,不一致就拒绝启动;想自己算一遍再比也可以。这样"我的表对不对"不靠发布方声明,靠一条已经焊死的历史,加一次谁都能做的计算。
取位与越界。 位置 p = (S_h + H(registry)) mod 10⁶;拒绝采样每次后移 8 位,越过表尾则回卷到 0。窗口固定 8 位十进制(0 -- 99,999,999),足够覆盖 N ≤ 10³ 的取模。
4.2 出块
被抽中者取与 sh(B_{h−1}) 相容的交易(≥1 笔),收齐 §3.5.1 的账单(覆盖 ⌈2N/3⌉ 以上的槽位),计算状态根,焊接 parent_hash,广播。
余额不足的也要收进来,标成 failed。 块记录的是"收到过什么",不是"成功过什么":把付不出的交易也写进块、注明失败原因,出块人就无法悄悄丢掉背书人交来的东西;账单把这条约束钉死在 2/3 覆盖上(§3.5.1)。
没有任何一轮通信用于定人:所有节点在 B_{h−1} 落地后各自算出同一 proposer。
块内顺序:from 升序 → nonce 升序 → txid 升序。 这一条是强制的,不是约定------状态根必须只由交易集合决定,不由提议者的排列决定。否则提议者可以靠调换顺序吃掉价差(前跑),而且两个实现会算出不同的 state_root。
为什么不是单纯按 txid 排。 同一账户的 nonce 0 与 1,按 txid 排可能让 nonce 1 先执行;它因 nonce 不匹配被判无效,于是一笔本来合法的交易被排序规则弄死。先保证账户内 nonce 单调,跨账户再用 txid 定位,既不让提议者挑顺序,也不误杀合法交易。排序仍是交易集合的纯函数,所以"状态根只由集合决定"依然成立。
4.3 校验(合规即终局)
任一节点独立校验六项:父哈希连续、tx 逐笔有效、背书足额、state_root 可重放、抽签结果与自身计算一致、缺口处置义务已履行 (若 B_{h−1} 结束时存在缺口,本块必须含对应的清偿记录,§4.8)。六项通过即接受,不等任何确认。
4.4 报名与剔除
每刻度签名;连续 W 刻度无记录 → 从活跃集剔除(不拉黑,可随时恢复);banned 永久剔除。
4.5 顺延
块缺失且满足下列条件时,该槽位判为故障:
- 邻居集 N(i) = 与 slot_i 的标识哈希距离 最近的 5 个活跃槽位。三个量的取法写死:
- 标识哈希 =
SHA256(压缩公钥)(32 字节); - 距离 = 两个标识按 256 位无符号整数做 XOR,小者更近;并列时按 slot 升序取满 5 个;
- 活跃集取 B_{h−K} 块头
registry_root的那个快照(与 §4.1 抽签同一张表,不得另取)。
- 标识哈希 =
- N(i) 中任意 3 个签名宣告"该槽位无服务";
- 目标槽位在该高度上还没有被封的块:块已在链上,指控无效;块只是迟到、尚未入链,按下面的裁决规则处理。
处置:该刻度交易全部标记 failed 并退回;槽位记一次 miss;进入观察期。不发生任何财产转移。
同高两块的裁决。 若被判定故障的槽位后来又广播了它的块,同一高度就出现两块。规则:
携带三邻居故障证明的块优先,另一块作废,但不罚------它可能是网络问题,不是恶意。
这个优先是终局的,不因事后收到迟到块而翻转。 迟到块到达时,同一高度已经被封;回滚它就等于否定"合规即终局"(§4.3),而这条链的全部活性都建立在"看到合规块就接受、不等确认"上。所以这里只能二选一,选的是证明优先:宁可作废一个迟到块,也不让任何一块在被接受后还能被换掉。
迟到块是证据,但只罚矛盾,不罚迟到。 判据不在"块有没有出现",在"签名者有没有回过执":
- 回了执,又宣告"没服务"------自相矛盾,三签者按共谋罚没(§4.6);
- 没回执,宣告"没服务"------不算矛盾。它确实没收到,罚它等于罚网络抖动。
回执就是这条线的证据。 收到块的邻居回一份签名 Ack(§3.5.1);出块人手里于是有三样东西:自己签的块、三份故障签名、以及对方那份回执。三样凑齐就是矛盾,离线可验,不需要任何人的陈述。
不回执的代价落在链上。 缺回执记一次失职、进观察期,观察期内再犯即除名(§7.6);连续缺失只记一次。刻意不回执的人,每用一次假故障就得赔上一段观察期,用得多了就出局------成本从"没有"变成"信用"。
顺延块不罚。 它只是执行别人给的证明,自己无从判断真假;连它一起罚,真出故障时就没人敢接顺延。
谁举证,谁拿钱。 矛盾由原槽位节点举证,罚没全额归它------它的块已经作废,这笔钱是补偿。
顺延是单步接力,不跳级。 一个块只能证明"按抽签顺序排在它前面的那一个活跃槽位"无服务。前驱唯一,所以同一高度上不可能同时存在两个各带合法证明的块:若出现第二个,它声称的前驱此时必然已经出块,按上面第 3 条(有块则指控无效)当场作废。顺延者自己也不出块时,由再下一个活跃槽位接同一高度------接力的每一步只走一格,同一高度仍然只有一个接力者。
作废那块里的交易全部退回重发;出块权已经花掉,不回补。这条规则也适用 §4.9 的双签收场。
4.6 举证与罚没
证据可被任一交易携带。校验通过即执行:
slash(offender) = stake 全额
缺口优先由 slash 清偿;余额归举证者
FalseFault 没有缺口,罚没全额归举证者
同一证据被多人提交时,只认链序第一份,其余作废、不重复发奖(与"首次出现生效"一致)。这样既避免重复支付,也避免为了抢奖励而塞满区块。
4.7 追偿瀑布
deficit → ① 作弊者质押 → ② 全体验证者按质押比例分摊(单次不超过质押的 5%)
→ ③ 剩余缺口转为链上债券(不可转让),由后续罚没余额摊销
② 的上限按次算,不按年算。 这条链没有时钟,"一年"不是一个可计算的量;写成年上限,等于把一条无法执行的规则留在规范里。单次分摊封顶 5%,既限制单次冲击,又不需要任何时间单位。
债券只由罚没余额偿还,不从手续费里抽。 债券是受损账户名下的一笔只能收、不能转的余额。此后每次罚没,先补当次缺口,再按未偿余额比例偿还历史债券,全部清零后剩余才归举证者。没有专门的池子------赔付款只从作弊者身上出,不从日常交易里预扣。
4.8 失败与作弊:两件不同的事
余额不足的交易直接失败,不入账。 逐笔执行时先扣手续费:够付 gas 才进块;够 gas 但不够金额的,扣费、转账不发生、标记 failed。
失败不罚。 一笔支付没成,可能是付款方在别处先花了钱,也可能是一次没得手的攻击------两种情况都在规则之内,协议不做任何处置。协议只对破坏规则的行为动手。
负余额是作弊的痕迹,不是账本的一种状态。 它只有一个来源:有人跳过了余额检查,把一笔付不出的转账写进了块。它同时证明两件事:这一块违了规,签名者看到了却放过去了。所以它不是经济模型的一部分,是回击的起点。
失败的交易照样入块、照样付 gas、照样消耗 nonce。 三条都不能省:不入块,出块人就能悄悄丢掉背书人交来的交易;不付 gas,失败交易可以免费塞满块;不消耗 nonce,同一笔可以被无限重放。
处理义务在负余额出现时触发。 若 B_h 结束时存在缺口,则 B_{h+1} 必须包含处理该缺口的系统交易------查封签名者质押、清偿缺口账户、标记除名。三者由状态确定性推出,不含裁量。
为什么这不需要时钟。 块是一个封闭对象:里面有没有清偿记录,一查便知。所以"没处理"是一个可证的缺席------不需要多数声明,也不需要计时器。
这是全设计里唯一一处"缺席可证"的地方,原因是:缺席发生在块的内部,而不是网络之中。 网络里的沉默无法证明,块里的空白可以。
不处理的后果。 该块不合规,任何节点直接拒收;同时,这份被签名的块本身就是共谋证据------它证明签名者看到了缺口,却放过去了。口径与 §9 第四条一致:不查验即共犯,全部罚没。
分叉的处置。 若作弊者私下提交另一条分支以掩盖缺口,该分支一律判无效,判据是它没有处理缺口。处理了缺口的那条分支即正史;在正史上按规则处理的节点,不承担任何责任。
(边界:若两条分支都处理了缺口,则回到"同一高度两个合规块"的情形,由 §4.9 的顺延规则接管。)
4.9 双签的发现与处置(不投票)
见证。 每个节点收到一块后,向邻居互报一条极小的摘要:(height, proposer_slot, block_hash, sig)。这不是共识消息,只是互相看一眼。
检测。 若某节点在同一个 (height, proposer_slot) 上看到两个不同的 block_hash,且两份签名都属于该槽位,则双签成立。
证据自证:两个块、两段签名,凑在一起就够,不需要任何人的陈述,也不需要任何人被信任。
因为出块人是推导出来的(§4.1),只有被抽中的那一个能造成这种冲突,别人做不到。所以检测面极窄:每个高度,只有一个可能作弊的人。
前提。 检测成立要求两块的信息在网络上相遇------至少有一个节点同时看到两块,或者两个看到不同块的节点互相通信。
若攻击者把网络切成完全不相交的两半,任何一方都看不到另一方的块,此时检测不到,退化为 §6.4 的分区问题。这是假设,不是结论。
处置:作废并顺延。
- 双签成立 ⇒ 该槽位在本高度作废,两个块都不进入正史;
- 顺延到下一个活跃下标------跳过规则与 §4.1 完全一致(banned、未报名者跳过);
- 顺延者在该高度出块,块内必须 包含处置:查封双签者质押、补平缺口、标记除名;若该高度没有缺口,则只查封与除名,"补平"一项记为零额------字段照填、值为 0,不留"到底有没有缺口"的解释空间;
- 顺延者出了块却不处置 ⇒ 该块不合规,节点直接拒收;而这份签名本身即共谋证据,按 §9 第四条全部罚没;
- 顺延者不出块 ⇒ 走 §15.1 的故障流程(五取三邻居宣告),继续顺延;
- 处置完成,下一高度恢复抽签。
为什么这里不需要投票。
收场者是谁,是推导 的结果,不是选择的结果:槽位有序,下一个就是下一个,人人算得出。
投票唯一能多给的是"合法性感",代价是把多数裁决重新引进来------而那是这条链最不该有的东西。既然顺延已经给出唯一的答案,就没有再问一遍的必要。
边界。 顺延者仍须决定保留哪一支的普通交易;被舍弃那一支里的支付会变成债权,由 §4.7 的瀑布理赔。可审计,不可申诉。
与 §4.8 的关系。 §4.8 管"缺口必须被处理",§4.9 管"处理不了时谁来接手"。前者是义务,后者是接力。
4.10 质押、自交易上限与退出
绑定。 出块权与质押绑定:只有质押中的守护者才能被抽中。不存在"不押钱也能出块"的位置。
自交易上限。 同一账户既是付款方又是背书人时(自己给自己背书),其未结清交易总额不得超过自身质押的 2/3,超出部分无效。
为什么是 2/3 而不是 0.9:给自己背书时没有第二双眼睛,"查验"退化为自证。留出 1/3 的质押,等于强制它始终有一部分额度在为别人承担风险------自己不能把全部身家押在自己身上。
退出。 解除质押不能直接从链上抽走,必须三步:
- 发布暂停服务声明(一笔系统交易,签名即生效);
- 自声明生效起,该节点退出抽签、停止承接新背书,质押继续冻结;它原有的出块义务由后续槽位顺延承担;
- 100 个事件之后,若无未结清缺口或争议,质押解冻可取。
为什么必须留这 100 个事件:缺口是事后才被发现的------负余额要在下一块暴露,双签的证据要等两块相遇。若允许立刻取走质押,作弊者可以在事发前抽身,整套罚没就成了摆设。
100 个事件与 §4.8 的"下一块必须处理"是两层保护:前者保证退出者仍可被追责 ,后者保证在位者必须及时处理。
4.11 智能合约层
合约是本链的扩展,但它不能破坏会计模型。三条边界写死:
一、合约调用同样需要背书。 每一笔用户交易都要一个守护者签字,合约调用不例外------否则它就成了唯一不需要担责的入口。
二、合约内部转账不产生负余额。 合约执行是原子的,余额不足即 revert,不留痕迹。所以 §4.8 的"负余额 = 背书者失职"只适用于普通转账,不被合约污染。
三、合约不能作为背书者。 背书人必须是质押中的守护者;合约没有质押,也就没有可被罚没的东西。
费用只有一层:gas。 按金额收的比例费已经取消,理由见 §7.5:
手续费 = gas_used × 系数 (全部归背书者;系数见 §7.9)
背书者是唯一必须在签字前先执行一遍的人------不跑 VM 就算不出会不会 revert,也就签不了字。这是实打实的算力,手续费全归它。其他节点执行是在验证区块时重算,那是身为节点的义务,不另收。
单价写死为系数 69,000 个最小单位 / gas(暂定),不拍卖、不浮动;只有创世地址能调,约束见 §7.9.5。合约调用须预付上限,未用完退还。
确定性红线:禁用浮点、随机源、外部 I/O、依赖实现细节的 gas 计量。违反任一条,两个实现会算出不同 state_root,链当场分裂。
合约里的钱没有担保人。 额度规则管的是转账金额;合约账户里的余额不受任何质押约束------一笔 amount = 0 的调用可以把合约里的钱全部转走,协议不赔。这一点必须写在用户看得见的地方,不能只藏在白皮书里。
合约地址由哈希派生:H(部署者地址 ‖ nonce),不需要治理分配,也不需要抢注。可读名称交给链外的域名服务。
4.12 节点生命周期
五种状态,一张表定死,避免各实现各写一套:
| 状态 | 能否被抽中 | 能否背书 | 质押 | 是否继续报名 | 进入条件 | 离开条件 |
|---|---|---|---|---|---|---|
| active | 是 | 是 | 可动(受额度约束) | 是 | 入网考验通过 | 自暂停 / 除名 / 退出 |
| paused | 否 | 否 | 冻结 | 否 | 自暂停声明上链 | 满 100 事件且无缺口 |
| probation | 是 | 是 | 冻结 | 是 | 一次失职被顺延认定,或缺回执 | 满 30 刻度无再犯 |
| banned | 否 | 否 | 全额罚没 | 否 | 欺诈举证成立 | 永不恢复 |
| unbonding | 否 | 否 | 锁定待释放 | 否 | 自暂停满 100 事件 | 释放完成 |
三条容易搞错的细节:
- paused 期间不报名,所以也不会因"连续 W 刻度未报名"被标记离线------两层规则不叠加。
- probation 仍可被抽中,否则一次抖动就等于停业,惩罚过重。
- unbonding 不是独立状态,是 paused 的最后一段;文档里单列,只是为了让实现者知道"质押什么时候真的能取走"。
5 安全性
5.1 命题 1(唯一后继)
给定 B_{h−1} 与已固化状态,proposer(h) 唯一。因 S_h、registry、N 均取 h−K 处快照,与 B_h 的内容无关。
推论:同一高度不存在两个"合规但不同"的合法区块,除非出块人自身双签(可举证)。
5.2 命题 2(抽签均匀)
拒绝采样后 Pridx0 = i = 1/N 严格成立,误差为零(非近似)。故任一活跃槽位获得出块权的概率相同,与质押、年龄、位置无关。
5.3 命题 3(不可回退)
区块头含 parent_hash 且后继唯一 ⇒ 改动 B_i 需从 i 起重算全部哈希并重新取得全部后继的派生权,而派生权由不可改的 S 与 registry 决定。故回退代价为全链重算,且新分支在抽签上无任何优势。
5.4 命题 4(超花在块内被挡住,突破规则必留痕)
正常执行下,超花到不了账本:余额不足的交易在块内就被标成 failed,余额不会为负(§4.8)。所以作弊没有"卷走一笔钱"的常规路径。
它必须先突破执行规则------让一笔付不出的转账真的入账。一旦这么做,B_h 结束时账户为负,B_{h+1} 必须清偿(§4.8):查封质押、补平、除名。
因背书额度上限为 0.9·s,必有 A ≤ 0.9·s < s。所以突破的结果不是"卷走 A",而是"质押清零、位置永久剥夺"。
净收益为负,且缺口不会挂账。 这条命题保护的不是"不会失败",是"失败也不丢钱":正常交易失败只是没付成,突破规则才产生缺口,而缺口必有清偿。
5.5 命题 5(追偿收敛)
瀑布三级各有上界:质押封顶单人损失,分摊封顶单次冲击,债券只由后续罚没偿还 ⇒ 缺口单调递减至零。收敛速度取决于未来罚没的规模,与出块速度无关。
5.6 抽签研磨:为什么它买不到东西
出块人可以通过调整块内交易数来移动 S,从而移动下一次抽签的位置。这条通道存在,本节不假装它不存在。它是一条按次收费的通道,而它买到的东西价值为负。
"少打"这条路先被堵死。 账单(§3.5.1)要求出块人交齐 ≥2/3 守护者的受理清单,且账单里列的 txid 必须入块、或记为 failed;漏掉就丢覆盖,覆盖不足 ⌈2N/3⌉ 即降权(第 k 次停 k 个事件)。所以"只打一笔"不是免费选项,是拿自己的出块资格换的。
"多打"这条路明码标价。 位置 p = (S + H(registry)) mod 10⁶,多打一笔就移一位。要把偏置覆盖到任意目标,得把整块打满:30,000,000 ÷ 1,450 ≈ 20,688 笔,按当前系数约 20,700 TRC,是当前最低质押的二十倍。这不是顺手微调,是批发采购------而且整块的真实交易会被自己的 dust 挤掉,行为在链上极其显眼。
买到的又是什么。 出块是义务、没有奖励(§7.1),执行费归背书者、不归出块人。所以"偏到自己"不是抢收益,是给自己加活。剩下两个动机都不划算:
- 让对手多出块:他出一块的成本是带宽和 CPU,收益为零------花两万 TRC 让对方忙一下;
- 让对手漏块:漏块只进观察期,不罚钱(§7.6)------花两万 TRC 换对方一次观察期,而且你无法预知他何时离线。
反复做还要另一笔本钱。 要一次次拿到出块权,得长期握住大量槽位:准入考验、足额质押、每次被抽中都要打包。欺诈一旦举证成立,罚没全额、除名永久(§4.6)。收益上限被质押锁死,损失下限是全部质押。
所以这里不给概率上界,不是因为没有上界,而是因为上界不重要 :偏置的价格线性、公开、可复算,而它买到的东西价值为负。攻击的动机不是被规则消灭,是被成本结构消灭------这是设计选择,不是数学疏漏。
这条防线有一个价格锚:系数。 以上数字全部按 69,000 计。若系数被执行到下限 1,一个满块只要 0.3 TRC,整套论证当场失效。所以 §7.9.5 的下界不是防手滑,是这条防线的地基。
6 活性
6.1 单点故障
若 N(i) 中 ≥ 3 个在线,顺延在 1 个块周期内完成。N = 21 时,邻居全灭需 ≥ 3 个同机房节点相邻,由 H3 排除。
6.2 抽签遍历
设失职节点比例 p,连续 W 次抽中失职节点的概率 ≤ p^W;W = 10 时对 p ≤ 1/3 已降至 1.7×10^−5。
6.3 节点规模
节点数不是协议常数,由部署方按实际规模决定。协议不设下限、不做推算:抽签所用的 N 在运行时从已提交的登记表读取,N 变则结果变,机制不变。
落地优先,所以本文不再给出"需要多少节点"的推导------那是部署决策,交给部署方。
6.4 分区:挂起,交给人
网络断成两半时,测得出的事实只有一件:顺延要三个邻居,而邻居未必在同一片网络里。
于是每一侧都面临同一个选择------继续跑,还是停。继续跑就有作弊嫌疑(你不知道对侧出了什么块),停则全线冻结。
规则定为:停。
- 当前事件的守护者挂出一条「等待网络融合」的特殊事件,写明预计恢复时间;
- 该片区内的节点一律挂同一条事件(这是一次广播,不是一次投票);
- 挂着事件的这一片强制暂停出块与背书;
- 另一片保持出块,同时接收并暂存对侧的融合数据;
- 融合完成后,由人类技术委员会会议处置双花------追根源、处罚与补偿,或者把受害者一侧先行搁置。
这是本设计唯一需要人类介入的地方,而且必须极少发生。 它不是漏洞补丁,是设计者对"协议不该处理的事"划下的线:没有时钟,就没有办法判断两片网络谁更正统,与其让协议猜,不如让它停。
若两片都挂出了等待事件,则全链暂停,直到人恢复。这是可接受的:它比两条历史都自称正统要好。
7 经济
这一章讲的是作弊的价格,不是保险产品。质押、罚没、追偿瀑布存在的唯一目的,是让破坏规则的成本高于收益。正常的交易失败不在其中------失败了就是没付,没有赔付,也没有罚。
7.0 总量、单位与发行
总量 1 × 10^9 TRC(十亿),精度 10^-8,恒定,不增发。 最小单位 10^17,可安全落在 u64 之内。
没有出块奖励,也没有通胀:出块与背书的全部收入来自交易手续费。因此总量在创世那一刻就固定,此后只发生转移 ,不发生创造。罚没的钱也不会被销毁------先补缺口,余额给举证者,一分不烧。
为什么是十亿。
单笔支付的上限由单个守护者的质押决定,与总量之间只隔一个比例:
可背书额度 = 0.9 × 质押
押总量的千分之一(100 万),就能承接 90 万的支付;押千分之二,就是 180 万。所以总量决定的是货币单位的大小,不是单笔上限本身。
十亿的好处是整数好算:千分之一恰好 100 万,千分之二 200 万,不产生小数尾。落地阶段最怕参数带小数尾巴,十亿是能整除的最整齐的一档。
想要更大的单笔,只有两条路:某个守护者押更大份额,或发起方自行拆分(§7.8)。
创世分配的三条硬约束。
- 初始质押 ≥ 总量的 30%。 低于此,安全预算不足以覆盖目标支付额,链一开始就是纸糊的。
- 剩余部分不得集中在少数地址。 集中会立刻抬高 h,使"抽签均匀"在账面上变成"质押均匀"的假象------公平是抽签给的,不是分配给的。
7.1 唯一的收入:背书服务费
| 来源 | 比例 | 归属 |
|---|---|---|
| 手续费(gas_used × 系数) | 100% | 背书者(预执行的服务费,非租金) |
| 出块 | 无报酬 | 守护义务 |
| 罚没余额 | 缺口清偿后的剩余 | 100% 举证者 |
费用只由 gas 决定,不由发起方声明。 交易里没有手续费字段:扣多少完全由 gas_used 与系数算出,不存在"自报费用"或"两种取整"的空间。普通转账按 §7.9 的模型是 1,450 gas,合约调用与系统交易按各自的操作序列累加,上限由 gas_limit 预付、未用完退还。
扣费时点。 入块校验时先扣,扣不动即整笔无效------手续费永远不能把余额扣成负数,否则 §4.8 的"负余额 = 双花痕迹"就被污染了。
失败的交易照扣。 余额够付 gas、不够付金额的:gas 照扣、转账不发生、标 failed,gas 不退------算力已经花掉。余额连 gas 都不够的:整笔无效,不进块。
守护行为不收费。 报名、账单、回执、举证、自暂停声明、升级指令都是零额系统交易,一律不收手续费------它们是守护者的义务,不是生意,让义务自己掏钱等于给每个节点加一笔固定税。它们仍占区块空间、仍算进块 gas 上限,所以出块人有权按空间取舍,免费不等于无限。
执行失败不退费。 revert 或 out of gas,手续费照收:算力已经花掉,退款等于让全网为失败付费。
出块不发钱。 这是本设计与几乎所有主流链最不同的一点:出块不产生任何收益,它只是保护网络的义务。守护者的收入来自背书,来自它承担的风险,而不是来自它占据的位置。
理由:一旦出块有奖励,抽签就会被经济动机污染------被抽中的人想多出块,没被抽中的人想抢。抽签的公平性依赖"被抽中不划算、没被抽中也不亏"。把出块做成义务,抽签才真的只是抽签。
没有池子。 赔付的钱只从作弊者的质押里出,不够就由全体按质押比例分摊------不从日常手续费里预扣一分。
7.2 质押决定产能,也决定上限
第三方背书额度 = 0.9 × 质押 − 未结清
自交易总额 ≤ 2/3 × 质押
第二条单列,因为它最容易被绕开:一个人给自己背书时,背书人与付款人是同一个人,查验形同虚设。把自交易压在 2/3 以内,等于强制它留出三分之一质押去承担别人的风险。
7.3 客户端怎么挑节点
两段式:
- 筛选:网络状态达标(延迟与可达性在阈值内),且额度足够(可用额度 ≥ 本笔金额)。
- 均匀随机:在通过筛选的候选池里等概率抽一个。
筛选是阈值,不是排名。 这一点很要紧:若按"最快"或"额度最大"排序,所有客户端会涌向同几个节点,负载迅速集中,分散性当场作废。阈值加均匀,才能既筛掉不合格的,又把负载摊平。
这一层只是降低失败率,不是安全边界。入块时仍会按 §9 第一条重新校验额度,不合格的交易直接无效------客户端筛错了,损失的是它自己的时间,不是别人的钱。
7.4 单价为什么固定
手续费单价由协议固定,不随行情浮动,不由市场撮合。
竞价会把"可信"翻译成"便宜",而这套系统的信用来自质押,不来自价格。一旦允许竞价,低价者会挤走高额度者,而低价的背后通常是薄质押。
7.5 质押的回报从哪来
收入按笔计,不按金额计。 一笔 50 万的支付与一笔 50 的支付,背书者拿到的是同一笔 gas 费(1,450 × 69,000 = 100,050,000 个最小单位 ≈ 1 TRC)。这是取消比例费的直接结果。
它不禁止任何接单方式。 额度是唯一的稀缺资源:一笔 50 万占掉 0.9 × 质押 的全部额度,收益固定;同样额度接几百笔小额,收益高几个数量级。于是节点会优先接小单、把大单留给还有余量的节点,或者由付款方拆成多笔(§7.8)。这是自由市场行为------协议只管额度与单价,谁接什么单由节点自己判断;用接单策略去挤占别人的份额,也不禁止。
代价落在区块空间,而区块空间已经被定价。 一笔 50 万拆成 1000 笔 500,手续费合计 0.84 TRC(约占金额的 0.00017%),但要吃掉约 2100 万 gas,接近一个整块。所以拆单不是免费的市场行为:它用区块空间换额度效率,而 gas 正是给区块空间定价的东西。
真正会被费率挡住的是极小额的支付。 单笔手续费固定 84,000 个最小单位,一笔 0.001 TRC 的支付要付 84% 的费用。这类支付不适合上链------它占的区块空间与一笔大额支付完全一样。
残余风险(无法用参数消除)。 若业务以小额为主,额度不起分层作用,押多不赚多,理性节点会把质押压到下限。只能靠一条硬下限兜底:最低质押随全网交易额的移动平均自动上调(见 §8.2 的自动参数)。
7.6 失职与欺诈分离
| 失职 | 欺诈 | |
|---|---|---|
| 判据 | 顺延成立 | 三类自证证据 |
| 财产 | 无 | 全额罚没 |
| 资格 | 观察期 30 刻度,再犯除名 | 一次除名 |
若失职即除名,则要求守护者 100% 可用,准入将只剩机构,反而中心化。
7.7 激励相容
- 发起方:选背书不足者,交易被收款方拒收 ⇒ 被迫选足额者;
- 收款方:可拒收,故有动力查额度;
- 背书者:签名即表示查验过。正常失败(余额不足)只是没付成,不罚;一旦负余额出现------也就是有人跳过了余额检查------签名者按共犯全额罚没(§4.8),所以它有动力在签字前真的看一眼;
- 举证者:奖励为罚没余额,故有动力举证;
- 出块者:出块无收益,但漏出块会被顺延并记 miss,故有动力维持在线;双签则全部罚没,故有动力不双签。
五方各自的最优策略与系统目标一致。出块者这一方的激励是负向的(怕罚、怕除名),不是正向的(赚钱)------这是刻意的:出块权不该是生意。
7.8 大额支付:拆分,不叠加背书
单笔上限就是单守护者的可用额度(0.9 × 质押),协议不提供多签叠加,也不提供任何超额通道。
超过上限的支付由发起方自行拆分成若干笔,各自选背书、各自计费、各自生效。
这样定的理由:多签叠加需要一套跨守护者的责任划分(谁先赔、赔多少、如何追偿),而每多一条这样的规则,就多一处可以被钻的缝。单签单责,责任边界永远是一条直线。
代价要写清楚:拆分不保证原子性。 拆成三笔的付款,可能成功两笔、失败一笔,中间没有回滚。业务层需要自己处理这个缺口(分次确认、或者先付定金)。
7.9 gas 从哪来:一个锚,两张表,一个系数
先立锚。 1 gas ≡ 一次 SHA-256 压缩(64 字节块)。所有成本都换算成它的倍数,全链只此一个单位。
选它当锚:跨平台确定、可计数,而且是这套系统里出现最多的原语------写一个叶要 256 次哈希。
一条前提:gas 只能是"计数",不能是"计时"。 验证者要重放你的块,必须用同一套数;墙钟时间在别人机器上不一样,链会当场分裂。所以"真实计算量"指协议规定的操作次数,不指你的实现跑多快。哪天有人把树路径优化到快一半,gas 也不能跟着降------否则同一笔交易在优化版和普通版上扣不同的钱。
7.9.1 计算量表(可校准)
| 原语 | gas | 依据 |
|---|---|---|
| SHA-256 / 64 字节 | 1 | 锚 |
| secp256k1 验签 | 400 | 参考机实测约 45--55 µs,约 350--450 个哈希 |
| 序列化 / 32 字节 | 1 | |
| 内存 32 字节读写 | 1 | |
| 树路径 256 层 | 256 | 每层一次节点哈希 |
这张表靠参考机标定,只随算法改动而改,不随行情改。下表是暂定标定值:实测后只改这张表的数字,不动公式,也不动操作的价格构成。
7.9.2 存储定价表(政策,不是测量)
| 项 | gas | 理由 |
|---|---|---|
| 已有叶更新 | 256 | 只付路径重算 |
| 新叶写入 | +2,000 | 永久占用磁盘 |
两张表必须分开写。 混在一起,下一个人会问"为什么新叶比旧叶多两千倍的计算量"------它没有多,那是政策:写一个新叶和更新一个旧叶,计算量几乎一样(都是 256 次哈希),差别全在它永久占了一块地方。
7.9.3 操作的价格由模型算出,不按类型定额
| 操作 | gas | 组成 |
|---|---|---|
| 转账 | 1,450 | 验签 400 + 读 2 叶 512 + 写 2 叶 512 + 编码 26 |
| 报名 / 账单 / 回执 | 0(免费) | 仍占块 gas,用于容量核算,不计费 |
| 合约写一个新存储槽 | 3,170 | 验签 400 + 账户 256 + 新叶 2,256 + 回写 256 |
| 双签举证 | 3,600 | 2 验签 800 + 查封 256 + 赔付 256 + 证据去重新叶 2,256 |
| 合约部署(1 KB 代码) | 64,000 | 验签 400 + 新账户 2,256 + 代码 64,000(2,000 / 32 字节,与新叶同价) |
比值 1 : 2 : 2.2 : 2.5 (转账 : 报名/账单 : 合约写槽 : 双签举证)。差这么小,是因为它们都由同样两件事构成:一次验签加一两个叶。真实成本结构里没有"操作类型"这一维,所以按类型定额从方法上就是错的。
7.9.4 系数
手续费 = gas_used × 系数
系数只有一个,全链统一。暂定 69,000 个最小单位 / gas:
转账 1,450 × 69,000 = 100,050,000 个最小单位 ≈ 1.0005 TRC
一次转账约 1 个 TRC。取这个起点不是为了赚手续费,是为了让金额有直觉:一笔 1 TRC,一百笔 100 TRC,账算得清。
7.9.5 系数谁调、怎么调
系数由创世地址签署调整指令,走正常的零额系统交易上链:
GasPrice = { chain_id, new_coefficient, activation_height, genesis_sig }
四条约束,缺一条这套权限就会从"保险"变成"后门":
- 生效高度必须大于当前高度加冷却期(建议 100 个事件),让钱包与节点有时间换价;
- 上下界写死:不得低于 1,不得高于当前的 100 倍,越界即无效;
- 两次调整之间至少隔 1,000 个事件,防止高频调价把链变成事实上的拍卖场;
- 永久留痕:指令与生效高度进链,任何人可离线验证。
为什么偏偏这个参数可以调。 系数不是规则,是汇率:TRC 的对外价值会变,手续费必须跟着变,否则同一笔交易的实际成本会随时间漂移。把它放在 §8.1 的不可变清单之外,是刻意的例外------规则的稳定性与价格的灵活性是两件事。
7.9.6 暂定值的后果
- 按 1,450 gas 一笔,30,000,000 的块能装 ≈20,000 笔,不是旧模型下的 1,428 笔。块容量、满块收入、年吞吐这些推算,都要按这个模型重算。
- 一个满块的手续费 ≈ 30,000,000 × 69,000 = 2.07 × 10^12 个最小单位(约 20,700 TRC)。这个费率是高是低,取决于真实业务量,系数可调的意义正在这里。
- 最低质押必须跟系数成比例 :目标值是 1,000 笔转账的手续费(当前 ≈1,000 TRC),并随系数自动缩放。押 10^4 个最小单位去接 1 TRC 的单,是量纲错了,不是参数偏了。
8 治理
8.1 不可变
chain_id、π 表、抽签算法、哈希与签名算法、瀑布顺序。写入创世块,变更即硬分叉。
唯一的例外是手续费系数:它是汇率,不是规则,随 TRC 的对外价值调整,由创世地址签令改(§7.9.5)。除此之外,这一节没有任何东西可以改。
8.2 唯一会自动变的参数
只有一条参数会自己动,触发条件完全链上可算,无需任何表决:
- 最低质押。 设 a_h 为最近 10⁴ 个块内普通转账的单笔平均金额(没有转账时取 0),G₀ 为最低质押的基准值------1,000 笔转账的手续费,随系数同步缩放。目标值 = max(G₀, 10·a_h),即覆盖典型单笔支付一个数量级以上。实际值按迟滞留动:目标值高于当前值 10% 才上调,低于 10% 才下调。理由是 §7.5 的残余风险------业务以小额度为主时额度不分层,押多不赚多,理性节点会把质押压到下限,安全预算随之变薄,而这条参数是唯一能不靠竞价、不靠表决把它撑住的杠杆。
这条按上面的阈值留了缓冲,避免在临界点上来回抖动。
8.3 其余变更
协议内不提供参数表决通道。任何非自动参数变更只能通过硬分叉。理由:表决通道一旦存在,就会成为新的权力中心,与"无多数裁决"的立身之本冲突。
8.4 暂停只有两种,都不是链级
单节点暂停。 守护者要退出或停机维护,自己签一份声明:
PauseSelf = { chain_id, slot, reason, expect_blocks, sig }
自声明上链即生效:退出抽签、停止承接新背书、质押冻结;100 个事件后无未结清缺口则解冻(§4.10)。
签名者是它自己的密钥------所谓"需要验签",在这里就是自证身份,没有第三方授权。解冻以事件数计(高度差),不能用本地时间,否则各节点判断不一。
片区暂停。 分区时该片区的节点各自停手(§6.4)。它带签名,但不是投票:这是同一片区的节点分别声明"我这一侧停",不是多数通过后集体执行。
挂起是本地策略,链上不留事件。 因为要写事件就得先出块,出了块就不算暂停。恢复后由该片区补一条融合记录。
暂停有上限:1000 个事件,但只能由"对面"来数。 挂起不留链上事件(见上),所以计时不能靠被暂停的那一侧------它没有块,也就没有事件。规则是:该片区最后一次出块的高度之后,对侧每出一个块记一个事件;满 1000 个仍未融合,对侧在其块内打「暂停放弃」标记,全网可见。 没有上限的暂停,等于没有结束。
双侧同时挂起时,这条上限失效。 两侧都不出块,就没有事件可数,链处于静止,不存在"超时"这个量。此时只能由人介入(§6.4)。写清楚这一条,比假装协议能在静止中计时要好------一个没有时钟的系统,本来就不该声称自己能数清静止了多久。
本链没有链级暂停按钮。 一个能一键关停全网的设计,等于把整条链押在一把钥匙上。
两种暂停都不产生"维护块"。单节点暂停后链由别人继续,片区暂停后另一片区继续------不存在"全世界只剩我一个还得凑个块"的场景,所以 §4.2 的"无交易不出块"一个字都不用改。
8.5 升级:创世者签发,节点自行应用
升级指令是一条零额系统交易,由创世区块生成者签名:
Upgrade = { chain_id, from_version, to_version, code_hash,
activation_height, migration_script, rollback_hash,
genesis_sig }
它搭正常区块上链,因此不产生空块、不破坏"无交易不出块"。三条约束:
- activation_height 必须大于当前高度加冷却期(建议 100 个事件);
- migration_script 必须是可重放的确定性脚本,不能是任意代码;
- 必须附带 rollback_hash。
节点在自己的暂停窗口 里应用:先发自暂停声明退出抽签,停机换版本,重启后重新报名。先离开,再换装,再回来。 若允许在线热切换,同一高度会出现两个版本的 state_root,那是双签之外最难查的一种分叉。
8.6 拒绝升级:协议不罚,现实来罚
节点可以拒绝。协议不做任何惩罚------它没有能力判断"拒绝"是恶意还是谨慎。
后果全在链外,但后果是真实的:
- 被抛弃:跑旧版本的节点 state_root 与新链不一致,无法同步、无法被抽中,实际退出网络;
- 司法后果:这类升级主要发生在处置分区作弊的场景,拒绝方可能被视为妨碍调查,承担法律责任;
- 商业后果:托管方、交易所、支付方会切断往来。
怎么识别"拒绝":不看节点自报的版本,看 state_root。 同一高度算出不同状态根,就是没升级------这是客观比对,节点无法谎报。
所以这不是"协议内的权力",而是协议留了一个钩子,现实把绳子拴上去。
写清楚这一点的意义:它承认了这套系统的真相------最后一道防线不是算法,是人。算法负责把作弊变成不划算,把分区变成可识别;当这两件事都失效时,能用的只剩法律。
8.7 三条约束与密钥分工
对权力的约束:
- 范围 :升级指令只能改代码与迁移状态,不能凭空转移资产。这一条不是链上检查,是委员会裁定------见下;
- 时限:冷却期后未生效即作废,不追溯;
- 留痕:所有声明与指令永久上链,可离线验证。
三条缺一条,这套权力就会从"保险"变成"后门"。
范围这一条靠人守,不靠代码守。 要让链上自动判定"这次迁移没碰余额",就得先有一套能读懂任意迁移脚本语义的机制------那等于在共识层再实现一遍形式化验证,成本高于收益,而且判定逻辑本身一旦有洞,洞就在最底层。所以这里刻意留一个口子:
由委员会裁定。 委员会认定越界,该指令即无效;节点可以拒绝跟随,链就此分叉。
裁定以委员会签名的声明发布,链外传播,不进共识------它不改变任何状态,只改变人跟不跟。分叉不是事故,是这套设计给人类组织留的出口。 技术有边界,人的组织没有;就像物理法则不是由技术决定的。只要升级碰不到余额,它最坏也只能让链停下,不能让人丢钱------但这条边界靠人守,不靠代码守。
为什么不是漏洞。 漏洞是"说好要做却没做到"。这里说好的是"留给人裁定",做到的也是"留给人裁定"。真正的风险不是这条规则,而是人不裁、或裁了没人跟------那是组织问题,写不进白皮书。
密钥分工:
| 密钥 | 用途 | 归属 |
|---|---|---|
| 创世密钥 | 签署升级指令 | 离线、分片、多人共管 |
| 节点密钥 | 出块、背书、自暂停声明 | 节点自身,加密落盘 |
| 委员会密钥 | 分区处置、升级范围的裁定、证据包的链外签署 | 硬件模块,多签;不能停链 |
委员会密钥不能停链,是因为"能一键关停全网"的能力,无论交给谁,都会成为比作弊更难防的风险。
9 参数表
| 参数 | 取值 | 依据 |
|---|---|---|
| 总量 | 1 × 10^9 TRC | 十亿,千分位无尾数(§7.0);精度 10^-8,最小单位 10^17 |
| 增发 | 0 | 无出块奖励;收入全部来自手续费 |
| 初始质押占比 | ≥ 30% | 低于此安全预算不足(§7.0) |
| 验证者规模 N | 由部署决定 | 非协议常数;运行时从登记表读取(§6.3) |
| π 表 | π 的前 10⁶ 位十进制小数 | 无作者、谁都能复算;内嵌只为省算力,哈希进创世(§4.1) |
| 取位宽度 d | 8 位 | 10^8 ≥ 10^3·N,拒绝期望 < 1.05 |
| 滞后 K | 8 块 | 覆盖传播抖动,且使 S 与表根均落在已焊死区 |
| 报名窗口 W | 10 刻度 | 容忍连续十次抖动;对应遍历概率 1.7×10^−5 |
| 补录追溯 | W 刻度 | 防提议者漏收导致误判缺席 |
| 事件 / 刻度 | 1 个区块 = 1 个事件 = 1 个高度 | 计时的唯一单位;静止期不推进(§2.6) |
| 顺延邻居 k | 5 | 门槛 3 时容忍 2 个同时离线 |
| 顺延门槛 m | 3 | 一人可独断、二人可偶然合谋,三人须真实联盟 |
| 最低质押 | 1,000 笔转账的手续费(当前 ≈1,000 TRC),随系数缩放,并随全网交易额自动调整 | 覆盖典型单笔支付一个数量级以上(§8.2) |
| 敞口系数 α | 0.9 | 留 10% 缓冲吸收结算抖动 |
| 手续费 | 100% 归背书者 | 见 §7.1;只有转账与合约执行付费,守护行为免费 |
| 分成 | 100% 归背书者 | 出块无报酬;不设保险池,赔付只从罚没出(§7.1) |
| 欺诈罚没 | 100% | 使命题 4 成立的最小值 |
| 观察期 | 30 刻度 | 约两类故障间隔的一个量级 |
| 解绑期 | 先挂暂停服务声明,100 个事件后生效 | 覆盖事后发现缺口的窗口(§4.10) |
| 自交易上限 | 2/3 × 质押 | 自背书无第二双眼睛(§4.10) |
| 单块上限 | 30,000,000 gas / 4096 笔 / 2 MB,三者同时生效 | 一次转账 1,450 gas,故 gas 是唯一瓶颈(≈20,689 笔);4096 笔与 2 MB 是结构与传播上限,正常转账触不到 |
| 负余额 | 不设上限,须在下一块内清偿 | 负余额 = 双花痕迹 = 背书者失职证据(§4.8) |
| 多签背书 | 不支持 | 单签单责,责任边界不弯;超出即拆分(§7.8) |
| 升级签发者 | 创世区块生成者 | 零额系统交易,搭正常区块上链(§8.5) |
| 升级冷却期 | 100 个事件 | 给节点留出反应与准备时间(§8.5) |
| 节点暂停 | 自签声明,100 个事件后解冻 | 先离场再换装,避免热切换分叉(§8.4、§4.10) |
| 手续费系数 | 暂定 69,000 个最小单位 / gas(一次转账 ≈ 1.0005 TRC) | 创世地址可调:冷却 100 事件、间隔 ≥1,000 事件、上下界 1--100 倍(§7.9.5) |
| 地址派生 | SHA256(压缩公钥)-20: | 全链一把哈希(§3.9);代价是合约内不能用 ecrecover |
| 签名规范化 | secp256k1,65 字节,必须 low-s(s ≤ n/2) | 否则同一签名有两种合法编码,签名索引不唯一(§3.9) |
10 已知限制
五条限制无法用参数消除,留给部署、运营与社区,不留给实现:
- 小额市场里质押不赚多(§7.5)。额度分层只在大额单上起作用;业务以小额为主时押多不赚多,只能靠最低质押自动上调兜底,而阈值要实测。
- 分区两侧都出块时不自动合并。协议只挂起,合并与双花处置靠人(§6.4)。
- 顺延会误判"慢但活着"的节点。没有时钟就无法证伪"它只是慢",只能靠准入与声誉约束(§4.5)。
- 宣告故障押的是回执(§4.5)。回过执又宣告"没服务",即自相矛盾,三签罚没;没回执的宣告不算矛盾、不罚,但它拿不到那一块的状态,下一高度的账单做不出来,等于自动让位一次。想用假故障压块,三个身份得一起放弃下一轮。
- 冷启动没有协议内补贴。守护者早期收入只来自手续费,激活市场、把交易量拉起来是社区的责任------协议不承诺早期收益,也不为拉量而增发(§7.0)。
另有两处是刻意的取舍,不是遗留:大额支付不做多签、超出即拆分(§7.8);抽签偏置不设概率上界,靠成本结构压制,不靠对称博弈(§5.6)。
11 对外接口
11.1 三个面
| 面 | 用途 | 是否可写 |
|---|---|---|
| 浏览器 | 块、交易、账户、合约、守护者、事件 | 只读 |
| 检索 | 按数据 hash / 账户 / 签名反查 | 只读 |
| 运维 API | 暂停、升级、恢复 | 写,须验签 |
11.2 索引不是真相
浏览器与检索都建立在索引库上,而索引库是派生数据:它必须能由全节点数据完整重建。
三条硬要求:构建脚本开源;关键字段可用 Merkle 证明回验;任何聚合数都能点进去看原始交易。缺任何一条,浏览器就从"窗口"变成"喉舌"。
证明从哪里拿:向全节点要,不要向索引器要。 索引器只提供"去哪个块、哪个位置找",前端拿到定位后向全节点索取 Merkle 路径并本地验证。若证明也由索引器给,名义上验了,实际还是信任它。
11.3 签名索引
按签名反查交易,把"谁签过什么"变成可检索的事实。取证价值极高:拿到一段签名,可直接定位到具体交易与背书者。
代价是存储------65 字节乘交易数,一亿笔约 6.5 GB 原始数据,索引再加一倍。必须预先规划冷热分层。
11.4 运维 API 的验签
自暂停声明由节点自签 ,只需验"是不是它本人";升级指令由创世者签发,节点应用前独立验签,验不过就拒绝应用并记录。
两者都要带:签名、理由文本、生效高度(升级还要带回滚版本)。
拒绝应用不算违规------但会与主网分叉,后果见 §8.5。
12 决策记录
以下二十六条已定案。实施以本节为准;正文已按本节修正。若仍发现出入,是正文漏改,应回改正文,不得另立一套。
形状(决定块格式与状态布局)
| 编号 | 决定 | 落点 |
|---|---|---|
| A1 | 交易加 gas_limit / gas_price / data;块头加 gas_used;状态树加合约代码与合约存储两棵前缀;序列化七条冻结 | §3.2、§3.3、§3.7、§3.8 |
| A2 | registry_root 取第 h−K 块头的显式字段,不回溯状态树 | §4.1 |
| A3 | 同高两块:带三邻居故障证明者优先,另一块作废不罚 | §4.5 |
| A4 | 补录只追加在未来块;缺席判定向前看 W 块,过去不改写 | §3.5 |
| A5 | 节点五态表 active / paused / probation / banned / unbonding | §4.12 |
| A6 | 每个守护者每高度交一份受理确认(账单),可为空;未交者下一高度不参与抽签;出块人账单覆盖不足 ⌈2N/3⌉ 即按次降权 | §3.5.1 |
| A7 | 同高两块:带三邻居证明者优先、不因迟到块翻转;回执与故障声明互斥,回过执再宣告即矛盾------三签罚没、顺延块不罚、罚没归举证人(Evidence::FalseFault) |
§4.5、§3.5.1、§3.6 |
| A8 | 账单所列 txid 必须入块或记为 failed,否则该账单不计入 2/3 覆盖 | §3.5.1 |
行为
| 编号 | 决定 | 落点 |
|---|---|---|
| B1 | 入块校验时先扣费,扣不动即整笔无效;手续费永不产生负余额 | §7.1 |
| B2 | 合约执行失败不退手续费 | §7.1 |
| B3 | 片区暂停是纯本地策略,链上不留事件;恢复后补一条融合记录 | §8.4 |
| B4 | 合约账户余额不受任何质押担保,风险自担,须显著声明 | §4.11 |
| B5 | 债券当作一种余额记账,不可转让 | §4.7 |
| B6 | 同一证据只认链序第一份,其余作废不重复发奖 | §4.6 |
| B7 | 手续费只有 gas 一个来源,取消按金额的比例费;交易结构里没有手续费字段 | §3.3、§7.1 |
| B8 | 手续费 = gas × 系数;系数由创世地址签令调整(冷却 100、间隔 ≥1,000、上下界 1--100 倍),暂定 69,000,使一次转账 ≈1 TRC | §7.9.4、§7.9.5 |
| B9 | 余额不足的交易直接失败、不入账;失败的交易照样入块、扣 gas、消耗 nonce;负余额只在规则被击穿时出现,是回痕与回击依据 | §4.2、§4.8、§7.1 |
| B10 | 守护行为(报名、账单、回执、举证、自暂停、升级)不收手续费,只占块 gas;只有转账与合约执行付费 | §3.5.1、§7.1、§7.9 |
| B11 | 取消保险池:手续费 100% 归背书者;赔付只从罚没出------先质押、再全体分摊(单次 ≤5%)、余额转债券由后续罚没偿还 | §4.7、§7.1、§8.2 |
常量与交付
| 编号 | 决定 | 落点 |
|---|---|---|
| C1 | π 表内嵌发行版,附 SHA-256 校验和,启动自检 | §4.1 |
| C2 | Merkle 证明向全节点索取,索引器只提供定位 | §11.2 |
| C3 | 合约地址由 H(部署者 ‖ nonce) 派生,无治理分配 | §4.11 |
| C4 | π 表取圆周率前 10⁶ 位小数,任何人可复算;内嵌 + 创世哈希校验,越界回卷 | §4.1 |
运维
| 编号 | 决定 | 落点 |
|---|---|---|
| D1 | 暂停上限 1000 个事件,由对侧计数;满额由对侧打「升级放弃」标记;双侧同时挂起时上限失效,交人 | §8.4 |
| D2 | 拒绝升级以 state_root 不一致为准,不看自报版本 | §8.6 |
| D3 | 升级指令的"不能碰余额"由委员会裁定、不进共识;裁定不成即硬分叉。这是刻意留给人组织的口子,不是链上检查 | §8.7 |
规范主体与实施前置闭合。 余下三类不影响开工:实现细节(gas 表逐条 cost、索引库选型)、需实测才能定的数值(最低质押自动上调的阈值)、本质不可证伪的残余(顺延对"慢但活着"节点的误判)。