Repository navigation
fix(ticket): only allow Exec_Genesis at height 0 - #1337
Merged
Merged
Conversation
Ticket.Exec_Genesis 缺少 coins/coinsx/token 都有的 height 校验:任意高度都能重入 GenesisInit,造出 isGenesis=true、3 段 ticketId 的“创世票”——既绕过 pubHash 校验 (3 段 id 的 privHash 恒为空),又绕过成熟期与提现等待。分叉生效后受害矿工会把这种 票当成自己的票挖出来,ExecDepositFrozen 把出块奖励增发到攻击者的 ReturnAddress, 攻击者可立即 Tclose 提走,本金零锁仓。 - 执行器层:高度 != 0 直接返回 ErrReRunGenesis;不依赖 chain33 侧新增的框架分叉, 在旧版本 chain33 上同样生效 - 回归用例:executor 层断言高度 1.7M 上的 Genesis 被拒绝、高度 0 的正常创世路径 不受影响(并直接调用执行器方法验证执行器自身的校验);testnode 端到端断言交易 可以进块但回执非 ExecOk、链上不产生伪造票、受害矿工挖不到伪造票 - 原 PoC 用例改为修复后的回归断言
vipwzw
force-pushed
the
fix/ticket-genesis-height-zero
branch
from
October 15, 2026 11:55
eb5634e to
1bb82e3
Compare
…s fork 接入 chain33 新增的系统分叉 ForkGenesisOnlyAtHeightZero(创世动作 Genesis 只允许在 高度 0 执行,由 DriverBase.checkGenesisHeight 在 Exec 与 ExecLocal/ExecDelLocal 派发 前统一拦截): - github.com/33cn/chain33 升到 v1.72.1-0.20261016021757-b12c753ff90b:该提交是在当前 pin 的 v1.72.0 之上只回移本次修复(chain33 分支 fix/genesis-only-height-zero-v1.72.0, 含 #1407 review 的补钉),避免把 chain33 master 上 30+ 个无关改动一并带入——实测直接 跳到 master 会让 ci_rgbx 的 TSS/DKG 场景稳定超时。上游修复见 33cn/chain33#1407 - 5 个含真实 [fork.system] 的配置补上 ForkGenesisOnlyAtHeightZero=0。chain33 的 initForkConfig 要求每个已注册的系统分叉都必须出现在配置里,缺键或未知键都会让节点 启动失败,因此版本升级与配置补键必须落在同一次提交 - 验证:go test ./plugin/dapp/ticket/... 全绿
vipwzw
force-pushed
the
fix/ticket-genesis-height-zero
branch
from
October 16, 2026 02:18
1bb82e3 to
91e5faf
Compare
vipwzw
force-pushed
the
fix/ticket-genesis-height-zero
branch
2 times, most recently
from
October 16, 2026 03:51
936508f to
8c996af
Compare
依赖从 v1.72.0 回移版换成 v1.72.7-0.20261016053907-a95e7f042582(chain33 分支 fix/dht-keep-established-identity,33cn/chain33#1408): - v1.72.6 已经包含本 PR 需要的系统分叉 ForkGenesisOnlyAtHeightZero(33cn/chain33#1407), 但仍带着 v1.72.1 引入的回归:钱包就绪后 dht 用钱包空投地址替换节点身份并 reStart(), 运行期更换 peer id; - plugin 的 ci_rgbx 正是被这条路径打断(重启前发现的 TSS peers 与运行身份零交集, auto DKG commit 超时),与创世分叉无关(把它配成 -1 仍复现); - 切到该修复后 ci_rgbx 通过(5m29s,重启前后 peer 名保持不变),因此不再需要 v1.72.0 回移分支,等 chain33 发布 v1.72.7 后可换成正式版本号 - go test ./plugin/dapp/ticket/... 全绿
…commit 依赖从 a95e7f042 跟到 e1bde75e2(v1.72.7-0.20261016082959-e1bde75e25d0): #1408 review 补了真 host 身份稳定性回归、收紧了用例(变异矩阵可拦下收窄/移动守卫), 并把钱包导入的 ErrPrivkeyExist 当成功、去掉名字冲突时多余的 1 秒 sleep。 chain33 依赖最终包含:ForkGenesisOnlyAtHeightZero(33cn/chain33#1407) + dht 身份不再运行期替换(33cn/chain33#1408)。 go test ./plugin/dapp/ticket/... 全绿。
…cate check 按维护者决定:系统分叉 ForkGenesisOnlyAtHeightZero 默认 0(创世即生效,本仓库各配置 均显式补键为 0),同一约束不再在合约里重复实现,避免两份实现各自漂移。 - Exec_Genesis 移除 height != 0 校验,保留注释说明该约束由 chain33 框架层 (DriverBase.checkGenesisHeight) 提供 - Test_TicketGenesisReRunRejected_PostFork:删去"直接调用执行器方法"的断言, 改为先 require 断言该分叉在测试高度已生效 —— 依赖由隐式变显式,分叉被配成 -1(→MaxHeight) 或链接旧 chain33 时用例直接变红 - testnode 端到端与 PoC 头注释同步为"框架层拦截" 验证: - go test ./plugin/dapp/ticket/executor/... ./plugin/dapp/ticket/testnode/... 全绿 - TestGenesisReentryRejectedOnTicketNode 1.76s 通过,证明拦截确实来自框架层 - 变异:testdata 的分叉值改成 -1 → 用例失败并提示"本用例依赖框架层分叉 ... 已生效", 随后回滚 - 取值探测:testdata=0;types.GetDefaultCfgString() 的 title=local → setLocalFork 全 0, 两处 IsFork(任意高度, ForkGenesisOnlyAtHeightZero) 均为 true
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #1337 +/- ##
==========================================
+ Coverage 23.86% 23.98% +0.11%
==========================================
Files 1122 1122
Lines 223699 223806 +107
==========================================
+ Hits 53395 53669 +274
+ Misses 164697 164491 -206
- Partials 5607 5646 +39 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Owner
|
🎉 This PR is included in version 1.76.2 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
问题
Ticket.Exec_Genesis缺少 coins/coinsx/token 都有的height != 0校验,任意高度都能重入GenesisInit:isGenesis=true、3 段ticketId的“创世票”——genPrivHash对 3 段 id 返回空,pubHash校验整段跳过;TicketMiner/TicketClose的时间校验只对!IsGenesis生效 → 免成熟期、免提现等待;MinerAddress上)立刻挖出,ExecDepositFrozen把出块奖励增发到攻击者的ReturnAddress,攻击者当场Tclose提走,本金零锁仓。PoC 在
height=1,700,000(ForkTicketId已生效)实测:Exec_Genesis返回ExecOk,500000000 奖励记到攻击者地址并立即关闭成功。修复
框架层单点拦截:chain33 新增系统分叉
ForkGenesisOnlyAtHeightZero(types/fork.go注册默认0),由DriverBase.Exec/callLocal在派发前调用checkGenesisHeight统一拦截,保护所有漏写该校验的执行器。上游 PR:33cn/chain33#1407(v1.72.6 起提供)。ticket/executor/exec.go的Exec_Genesis不再重复实现同一校验(此前那版会让同一约束存在两份实现、各自漂移),只保留注释指明约束来自框架层。这让保护完全依赖该分叉生效,因此把前提显式固定下来(而不是靠"默认应该是开的"):
[fork.system]配置都显式补该键为0(创世即生效):chain33.fork.toml、chain33.para.toml、plugin/consensus/qbft/chain33.qbft.toml、plugin/dapp/evm/cmd/ci2/chain33.proxyminer.toml、plugin/dapp/ticket/executor/testdata/chain33.cfg.toml;Test_TicketGenesisReRunRejected_PostFork先require.True(cfg.IsFork(height, ForkGenesisOnlyAtHeightZero))把"分叉已生效"断言出来:一旦被配成-1(→MaxHeight,即关闭)用例直接变红(已做变异验证),不存在"静默无保护但 CI 全绿";initForkConfig因system fork not exist直接启动失败,而不是静默放过。被拒绝的 Genesis 交易仍会进块,但不产生任何创世 KV:回执是
ExecPack加一条TyLogErr,MinTxFeeRate > 0时手续费照收(既有手续费行为)。链上状态不变;ExecLocal_Genesis/ExecDelLocal_Genesis也被框架守卫一并跳过(DriverBase.callLocal复用同一checkGenesisHeight),因此LODB-ticket-tl:*本地索引不会被污染。依赖
github.com/33cn/chain33→v1.72.7-0.20261016053907-a95e7f042582(分支fix/dht-keep-established-identity,33cn/chain33#1408)。ForkGenesisOnlyAtHeightZero自 v1.72.6 起提供(fix(dapp): only allow the genesis action at height 0 chain33#1407);合约层不再自带校验,所以链接旧 chain33 不再是"少一层保护",而是节点启动即失败;doGenAirDropKey用钱包空投地址替换节点身份并reStart(),节点在运行期换 peer id。ci_rgbx正是被它打断——重启前发现并写进 TSS peers 配置的 4 个 peer 名,在钱包就绪后与实际运行身份零交集,DKG 无法形成,auto DKG commit超时(把创世分叉配成-1复跑仍复现,与本 PR 的分叉无关);ci_rgbx通过(5m29s),重启前后 peer 名保持一致;等 chain33 发布 v1.72.7 后可换成正式版本号。chain33 的
initForkConfig要求每个已注册的系统分叉都必须出现在[fork.system],缺键或未知键都会让节点启动失败,因此版本升级与配置补键必须同一次提交:本 PR 已一并更新chain33.fork.toml、chain33.para.toml、plugin/consensus/qbft/chain33.qbft.toml、plugin/dapp/evm/cmd/ci2/chain33.proxyminer.toml、plugin/dapp/ticket/executor/testdata/chain33.cfg.toml。测试
Test_TicketGenesisReRunRejected_PostForkExec返回ErrReRunGenesis且无回执(变异验证:把该分叉配成-1时用例失败)Test_TicketGenesisAllowedAtHeightZeroTestGenesisReentryRejectedOnTicketNode(testnode 端到端)ExecOk;不产生伪造票;受害矿工挖不到(修复前同一用例需 ~96s,现为秒级)验证矩阵:
v1.72.7-0.2026...)go test ./plugin/dapp/ticket/executor/... ./plugin/dapp/ticket/testnode/...全绿;TestGenesisReentryRejectedOnTicketNode1.76s 通过(拦截确实来自框架层)ForkGenesisOnlyAtHeightZero=-1Test_TicketGenesisReRunRejected_PostFork失败并提示"本用例依赖框架层分叉 … 已生效"(随后回滚)testdata与types.GetDefaultCfgString()(title=local→ 全 0)IsFork(任意高度, ForkGenesisOnlyAtHeightZero)均为true[fork.system]配置加载原 PoC 用例已翻转为修复后的回归断言。