【FHE】(十一):密文链协议——两条不变式,和 112 与 2100 的分工

项目仓库

Gitee 主仓:https://gitee.com/pei-xiaoguang/kestrel-llm

GitHub 镜像:https://github.com/m13253246268-ship-it/kestrel-llm

相关文档

术语与数据口径 |

性能与基准 |

构建与复现 |

快速上手 |

架构总览

0. 一句话结论

整条链的契约窄到可以写在信封背面:

复制代码
layL :  u(L-1)r112  ──→  uL
bootL:  uL           ──→  u(L)r112

每一跳都是一个幂等的纯函数 :给定输入密文 + 编译好的驱动 + 同一把私钥,输出唯一。这就是"接力"在工程上能成立的全部理由------下一棒不需要知道上一棒是谁,也不需要信任他。


1. 两条不变式

不变式 内容 为什么重要
输入形态固定 每一跳的输入永远是"已刷新、链长 112"的密文 任一棒拿到的都是同一种东西,不用为上游差异写分支
输出形态固定 每一跳产出 8 枚密文(4 个 token 位置 × 2 个分量) 交接包结构统一,验证脚本可以通用

第二条里的"8"值得解释:这一层同时处理 4 个 token 位置 ,而 CKKS 密文有 2 个分量 (comps = 2,重线性化之后)。所以 4 × 2 = 8。


2. 阶段表:T23_PHASE

驱动用环境变量 T23_PHASE 选择"这一跳是哪一种",源码里的定义(原文):

c 复制代码
int phase = 0;   /* 0=l0 1=boot 2=l1(M2 layer1) 3=t26(layer26) 4=t27(layer27) 5=fin 6=lay */
int lay = -1;    /* phase==6: T23_LAY 指定中间层 0..25 */
T23_PHASE 含义 参考前缀 权重目录
0 l0(第一层,生成私钥) l0_ tail/w0
1 boot(自举刷新) --- ---
2 l1(M2 阶段的第一层) --- .tmp_tok/l1w
3 t26(第 26 层) l26_ tail/w26
4 t27(第 27 层) l27_ tail/w27
5 fin(收尾:norm / logits) --- ---
6 lay(中间层 ,用 T23_LAY 指定) l{L}_ tail/w{L}

中间层的层号由 T23_LAY 指定,合法范围 0, 27 (代码里有显式检查,越界直接退出)。阶段 3/4/6 走同一套参数:

c 复制代码
g_fold = 1024.0; g_chk = 4096.0; g_refpre = pref; g_causal = 1;

注意 g_causal = 1:中间层与尾层都是因果注意力 (这符合自回归语言模型的事实),而 g_fold / g_chk 是这一个阶段的折叠与校验尺度。


3. 为什么 lay 只要 112,而 boot 要 2100

这是最初最让人困惑的一点:同一层数据,为什么两个程序编的链长差近 20 倍?

程序 编译期链长 实际看到的链长 原因
t23lay 112 输入 112 → 输出 16 层内前向只需要"够用"的链
t23boot 2100 输入 16 → 输出 112 自举内部的 sin 折叠约需 31 层深度(本系列第 7 篇)

关键区分:

  • 编译期链长 = 该程序"支持的最大链长",也就是它的内存上界;
  • 实际链长 = 这一刻密文上真正剩多少素数。

所以 t23boot 编到 2100,不代表它跑的时候链上真有 2100 个素数------它只是必须准备那么多空间 ,因为 modraise 那一步(第 7 篇)会先把链抬满,再做几十层深的折叠。

这也直接解释了为什么 boot 比 lay 慢得多、吃内存多得多:

复制代码
lay0   : 1920 s,工作集 ≈1.0 GB
boot0  : 4285 s,链长 np=2100(建议内存 ≥8 GB)

4. 私钥从哪来:一把钥匙贯穿全链

这是个容易被忽略但很关键的设计点:

c 复制代码
if (phase == 0) ckks_sk_gen(&sk, (uint32_t)n);
else if (t23_sk_load(&sk, ".tmp_tok/chain/sk.bin") != 0) { printf("sk load fail\n"); return 1; }

只有 phase=0(第一层)生成私钥,其余所有跳都从 .tmp_tok/chain/sk.bin 读同一把。

含义有两面:

  • 好的一面 :接力者之间不需要传密钥分发协议------sk.bin 就在数据包里(也因此,这个包不能公开分发,它只适合熟人/内部接力);
  • 必须诚实的一面 :sk.bin 出现在接力包里,意味着任何拿到包的人都能解密整条链。这不是设计缺陷,而是"机制验证"这一阶段的必然------我们还没有做密钥托管或分段解密。

另外,只想生成密钥时可以走 T23_KEYONLY=1:驱动会打印 KEYONLY: sk.bin saved 后退出。这对搭建测试环境很有用(注意:生成目录必须先建好 ,驱动用裸 fopen 落盘、不会自建目录)。


5. 交接包与断点续跑

一跳的产物只有三样:

复制代码
chain/u{L}r112_{t}_{h}.ct      # 8 枚密文,单文件 ≈3.5 MB
relay_rerun/{lay,boot}{L}.out  # 日志
manifest.sha256                # 逐文件哈希,可自校验

而"跑层"脚本(rerun5_lay_boot.ps1)有一个很实用的性质:已有产物自动跳过。也就是说:

  • 跑到一半中断,重跑不会从头再来;
  • 层与层之间天然可并行(不同的人跑不同的层,互不阻塞)。

这是"横向切链"这个设计的直接红利------它不只是为了省内存,也是为了让人可以各跑一段。


6. 一处照实标注的未核实项

boot 的日志里会打出:

复制代码
out np=2083

按文档口径,这一行出现 8 次(每层 4 token × 2 分量)视为"刷回满链"的判据。

但**2083 与编译期 2100、落盘 112 之间的精确对应关系,本文不做断言** ------它涉及 modraise 抬升到多少、以及折叠过程中又消耗了多少个素数。这个数字需要以源码为准逐行确认,我不在这里凭推测解释它。

这条按本项目纪律标注:未核实的东西不写结论,宁可留一个洞,不给一个猜测。


7. 安全边界(务请读完)

复制代码
本文所述参数为机制验证级(n=2048、112 素数内层链、2100 素数自举链),
远低于 HE 参数标准的 128-bit 水平,不得用于保护真实数据。
本文主张的是:链协议的不变式与工具链行为。
本文不主张:安全强度、性能优越性。

特别提醒 §4 :sk.bin 与密文一起在接力包里流转------这套机制目前不具备"服务提供方看不到数据"的性质,请勿据此宣传任何隐私保证。


8. 这一篇的未解问题

  1. 协议没有版本号。如果引擎参数变了(比如链长从 112 改成 128),旧包和新包长得一样,但互不兼容。目前靠"文档约定 + 论文式对齐"来避免混用,没有机制上的防护。
  2. 交接包不带"输入指纹" 。下一棒无法验证"我拿到的 u{L-1}r112 就是上一棒声明的那个"------只能验哈希(如果上一棒把哈希公开的话)。这在本系列第 14 篇里作为验证学的第 4 条未解问题出现过。
  3. phase 2 (l1) 与 phase 6 (lay) 的语义有重叠。它们都做"中间层前向",区别来自开发阶段(M2 阶段只有一层)。对新人来说这是历史包袱。
  4. fin 阶段的门槛与前面几层不同(它的输出是对齐 logits 而不是密文链的下一跳),这一点在协议文档里描述得不够显眼------我们自己在复现时就撞过。

下一篇回到工程侧:为什么我们把 OpenMP 换成了自研线程池。

相关推荐
Zwawa1 小时前
把按键和 LED 状态显示出来:第一次看见程序到底在干什么
单片机·嵌入式
haliu1 小时前
【FHE】(十):密文里的 attention——没有 max,softmax 怎么办
人工智能·嵌入式·c·fhe·推理引擎·c11·边缘推理·同态加密推理
haliu1 小时前
【FHE】我们如何实现同态加密推理(八):SiLU 的密文化——两条路径,和一个 8 字节的开关
fhe·推理引擎·边缘推理·同态加密推理
不悔哥5 天前
开源OV-Watch:怎么做一个智能手表
单片机·开源·嵌入式
优信电子6 天前
AIT1001 非接触式红外测温模块 STM32 驱动开发实战:从引脚到代码全解析
stm32·嵌入式·温度传感器·红外测温·非接触式测温·ait1001
程序猿编码6 天前
告别改源码适配模型:纯 C++ 可配置 LLM 推理引擎,全格式全结构兼容
c++·大模型·llm·推理引擎
youcans_6 天前
【嵌入式软件AI编程】16. Claude Code 与 VS Code 的协同开发
stm32·mcu·嵌入式·ai编程·claude code
阿钱真强道6 天前
06 嵌入式操作系统 | vim 与软件管理:apt / 源码编译
ubuntu·vim·嵌入式·apt·源码编译
G.E.M.小白6 天前
【IMX6ULL ARM内核启动与时钟系统超详解】PLL/PFD/分频/根时钟配置
嵌入式·arm·时钟·imx6ull