承接 01。01 说"密码学保证不可篡改 + 所有权",这篇把那三块 拆开讲清。沿用 01 的"村庄账本 / 私章 / 防伪封条"画面。结构见 README.md §1。


0. 一句话本质

三个工具合起来回答同一个问题:在一本人人可见、没有裁判的公共账本上,怎么证明"这笔操作确实是某地址的主人授权的、且内容没被动过手脚"——不暴露任何秘密、谁也伪造不了、改一个字就露馅。

哈希 = 防伪封条;私钥 = 只有你的私章;公钥/地址 = 公开备案的印鉴 + 户名;数字签名 = 把私章以"锁死这张单子内容"的方式盖上去。


1. 怎么产生的:它解决什么问题

01 留了三个坑,全靠这篇填:

  1. "改任何历史一个字节,全网立刻识别"——靠什么识别? → 哈希。
  2. "谁掌握私钥谁就拥有资产"——这个'拥有'在数学上怎么成立? → 公私钥。
  3. "付款人签字,全网能验真且签完改不了"——怎么做到既能验真又不泄密又防伪? → 数字签名。

核心难点:账本全公开(人人能看每笔流水),又没有裁判(没有银行帮你确认身份)。 传统世界靠"出示身份证 + 银行核验",区块链里没有这个角色。必须用纯数学,让 任何人都能独立验证"这是本人授权的、没被篡改的",同时不需要本人在场、不需要可信第三方、秘密永不离开本人


2. 它本质在做的"那一件事"

把"信任一个人/机构"替换成"验证一个数学事实"。

  • 不再"我信银行说这是你",而是"任何人用公开信息就能算出:能产生这个签名的人,必然掌握那个私钥;而那个私钥对应那个地址"。
  • 不再"我信账本没被改",而是"任何人重算哈希,对不上就是被改过"。

信任被外包给数学。这就是 01 说的 trustless 的密码学底座。


3. 类比(村庄账本视角,逐个深化 01 的小词典)

3.1 哈希 = 一台"魔法榨汁机"(防伪封条的本体)

把任意文件丢进榨汁机,吐出一杯固定容量、颜色独一无二的果汁:

  • 同样的文件 → 永远同样的颜色(确定性)。
  • 改一个标点 → 颜色完全变样(雪崩效应,avalanche)。
  • 看着果汁,没法还原出文件(单向,不可逆)。
  • 几乎不可能找到两份不同文件榨出同色(抗碰撞)。

账本每一页页脚写着"上一页榨出的果汁颜色"。想偷改第 100 页?它的颜色变了 → 第 101 页页脚记的"第 100 页颜色"对不上 → 你得重榨 101、102…一直到最新页, 还要比全村继续往后榨的速度快。这就是 01 "特殊胶水粘死每页"的数学真身。

关键:哈希不是加密。加密有钥匙、能解密;哈希没钥匙、设计上就不可逆。 以太坊用 Keccak-256,比特币用 SHA-256。我们项目 demo.tskeccak256(abi.encode(addr, 9)) 算 USDC 余额存储槽——用的就是这台榨汁机。

3.2 私钥 / 公钥 = 一枚刻不出第二枚的私章 + 它的公开印鉴

  • 私钥:一个 256 位的随机秘密数(≈ 在 2²⁵⁶ 个数里抽一个,比宇宙原子数还多)。 生成它不需要联网、不需要注册、不需要任何机构——你扔 256 次硬币就能造一个, 这串数本身就是私章。这是"自我保管(self-custody)"成立的根。
  • 公钥:由私钥经椭圆曲线乘法算出(Q = d × G,secp256k1 曲线)。 这一步单向:知私钥能秒算公钥;知公钥反推私钥在数学上不可行 (椭圆曲线离散对数难题)。好比:从私章能盖出印鉴,但拿着印鉴谁也刻不出那枚章
  • 地址:再把公钥丢进哈希榨汁机、取末 20 字节 = 你账本上的户名 (以太坊:address = 末20字节( Keccak256(公钥) ))。又一次单向

两段都不可逆,所以链路是:私钥 →(单向) 公钥 →(单向) 地址你能从私钥推出地址,永远不能从地址倒推私钥。 我们核查 0x6bdc / 0x65A8 就是只看得到地址(户名)和它的全部流水,但永远控制不了它——因为没有那把私钥。

3.3 数字签名 = 把私章以"锁死这张单子"的方式盖上去

签名做两件事,一次完成:

  1. 证明是本人:只有持私钥者能生成对该消息有效的签名(ECDSA 算法)。
  2. 锁死内容:签名是对"这张单子的哈希"签的。改单子任何一处 → 哈希变 → 原签名验不过。签完想改金额?签名当场失效。

验证只需公开信息:任何节点拿签名 + 单子,用 ecrecover 反算出公钥 → 推出地址 → 看是否等于 from。等于 ⇒ "能签出这个的人必持有 from 的私钥, 且单子没被动过"。全程私钥从未离开你的设备,没有第三方参与。

注意:签名不加密交易。链上交易是明文公开的(金额、地址全看得见)。 签名只解决"是谁授权的 + 有没有被改",不解决保密。公链上几乎不用加密, 因为一切本就公开——这点极反直觉,务必记牢(见 §7 误区)。


4. 核心关键点(5 条)

  1. 哈希 = 单向防伪指纹。确定性、定长、雪崩、不可逆、抗碰撞。用于:区块链接、 交易 ID、地址派生、Merkle 树(03 待写)、合约存储槽、 CREATE 合约地址(= 哈希(部署者, nonce))。
  2. 非对称密钥 = 两段不可逆链路私钥 → 公钥 → 地址,正向易、反向不可行。 "拥有资产" 在数学上 = "掌握那把私钥",没有别的定义、没有别的凭证。
  3. 数字签名 = 认证 + 完整性,一次搞定:证明本人授权 + 锁死消息内容; 验证仅需公开信息;秘密永不外泄。
  4. 信任被外包给数学:不信人/机构,验数学事实。这是 01 trustless 的实现层。
  5. 没有"找回/重置":私钥是唯一所有权凭证。无中心机构 ⇒ 丢了=资产永久锁死、 被骗走=无人能追回。安全的全部重量压在"私钥/助记词保管"这一点上。

5. 必须先认识的前置名词(小词典)

名词 一句话 类比
Keccak-256 / SHA-256 以太坊 / 比特币用的哈希函数 两个牌子的魔法榨汁机
雪崩效应 (Avalanche) 输入改 1 bit,输出约一半 bit 翻转 改个标点,果汁全变色
抗碰撞 (Collision-resistance) 找两份不同输入同哈希,实际做不到 两份文件榨出同色,碰不到
非对称加密 (Asymmetric) 一对密钥:私钥保密、公钥公开,单向派生 私章 / 公开印鉴
secp256k1 比特币/以太坊用的椭圆曲线 印章雕刻所用的那套规格
ECDSA 基于椭圆曲线的签名算法 盖章 + 验章的具体工艺
ecrecover 由签名 + 消息反算出签名者公钥/地址 由印迹反查是谁的印鉴
助记词 / 种子 (Mnemonic, BIP-39) 把主私钥编码成 12/24 个人类可读单词 把私章拆成一句口诀,背得出就等于拿着章
HD 钱包 (BIP-32/44) 一个种子按路径派生出无数子私钥/地址 一句母口诀派生整套分号印章
nonce(签名场景) 每次签名用的一次性随机数;重复使用会泄露私钥 盖章时垫的一次性防伪纸,重复用会被看穿
地址校验和 (EIP-55) 地址里大小写混写用于防手误 户名里的防错字母

助记词那条解释了项目里反复的警告:任何让你把助记词/私钥输进网页/聊天/AI 的, 都是直接索取你的私章本身0x6bdc 那次"朋友给地址让你研究"也是同一类 社工的远端表亲。


6. 一笔转账里三原语怎么咬合(端到端,放大 01 §6 的"签名"那一步)

01 §6 第 2 步一笔带过的"签名",放大看其实是三原语连环:

① 私钥 d  :256-bit 随机秘密数(离线即可生成,无需任何机构)
② 公钥 Q  :Q = d × G        (椭圆曲线乘法;单向,Q 推不回 d)
③ 地址 A  :A = 末20( Keccak256(Q) )   (哈希;单向,A 推不回 Q)
   —— 你的"户名"。别人能往 A 打钱,但动不了 A 里的钱(没你的 d)

④ 造交易 tx:{to, value, nonce, gas, data}      —— 明文,将公开
⑤ 指纹 h  :h = Keccak256( 序列化(tx) )          —— 榨汁机出指纹
⑥ 签名    :sig(r,s,v) = ECDSA_sign(h, d)         —— 必须有 d 才签得出
⑦ 广播    :把 (tx, sig) 发到全网 —— 明文,进 mempool(人人可见)

⑧ 全网每个节点独立验证:
     Q' = ecrecover(h, sig)         由签名+指纹反算公钥
     A' = 末20( Keccak256(Q') )
     检查 A' == tx.from ?
   是 ⇒ 能产生 sig 的人必持有 from 的私钥,且 tx 一字未改 ⇒ 接受
   (改 tx 任意一处 → h 变 → ecrecover 出错地址 → 拒绝)

全程 d 从未离开你的设备;没有银行、没有"登录"、没有第三方。 "所有权"在这套数学里被精确定义为:能对 from 地址的交易产出有效签名 ⇔ 掌握 d。 这就是为什么私钥即一切,也是为什么链上世界"无需信任"却能防赖账。


7. 常见误区(高频踩,逐条拍死)

误区 实情
"区块链把我的交易加密了" 没有。链上交易明文公开(金额/地址全可见)。签名 ≠ 加密,它只证授权+防篡改
"哈希是一种加密,能解密回去" 不能。哈希无密钥、设计上单向不可逆。它是指纹不是密文
"公钥能反推私钥" / "地址能反推公钥" 都不能。两段都是单向(ECC 离散对数 / 哈希)。这是整个所有权模型的根
"地址就是公钥" 不是。地址 = 哈希(公钥) 取末 20 字节,又单向一层
"生成私钥要联网/找平台注册" 不用。私钥就是个 256-bit 随机数,离线扔硬币都能造;地址本地可算。这正是自我保管的根
"签名会暴露私钥" 正确实现下不会。但同一私钥用重复 nonce 签两次会泄露私钥(历史上真实事故,§5 nonce 条)
"私钥丢了能找回 / 重置密码" 无中心机构 = 无重置。私钥/助记词是唯一凭证,丢=永久锁死,被骗走=无人能追
"我只是把助记词贴给客服核验一下" 等于把私章原件交出去。任何索要助记词/私钥的,无一例外是抢钱
"签名了就等于交易生效" 签名只证授权。生效仍要进 mempool→出块→确认(01 §6)

8. 与本项目实践的对应

你早就用过这三样,这篇补的是它们脚下的数学:

  • 私钥 → signerwatchernew Wallet(PRIVATE_KEY, provider)Wallet.createRandom()(demo-flashbots 的 authSigner)——就是 §6 ①②③。
  • anvil key 0xac09…:公开周知的测试私钥,对应地址 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266(= 末20(Keccak256(它的公钥)))。 我们全程敢硬编码它,正因为它无主网资金——私钥即所有权,没钱的章随便盖。
  • 数字签名实战flashbots.tssignBundle(签交易)+ X-Flashbots-Signature 认证头(用 authSigner 私钥对请求签名,让 relay 认得是同一 searcher)—— §3.3 的两段式签名活例。
  • 哈希实战demo.ts dealUsdckeccak256(abi.encode(recipient, 9)) 算 USDC balances[addr] 存储槽;合约 CREATE 地址 = 哈希(deployer, nonce) (Phase 4 部署得到的 0xDda8… / 0x0392… 就这么来的)。
  • 地址只读不可控0x6bdc(假)/ 0x65A8(真)我们能看全部流水却永远动不了 ——正因 §3.2 的单向链路:有地址,没私钥,免谈。

→ 回头看这些代码,从"调 API" 升级为"知道每个 Wallet/keccak256 在数学上担保了什么"。


9. 自检问题(答得出才算过)

  1. 哈希和加密的本质区别是什么?为什么说"哈希不可解密"不是缺陷而是设计目标?
  2. 私钥 → 公钥 → 地址 为什么是两段都不可逆?哪一步用椭圆曲线、哪一步用哈希?
  3. 数字签名同时保证了哪两件事?验证签名为什么需要公开信息?
  4. 为什么"生成私钥不需要任何中心机构"是自我保管成立的根?这对"私钥丢失/被骗"意味着什么?
  5. 链上交易是加密的吗?签名让交易保密了吗?(小心,这题专坑直觉)
  6. 我们项目里敢把 anvil 私钥 0xac09… 写进代码、推上 git,为什么不算泄密?
  7. 0x65A8 流水全公开、我们看得一清二楚,为什么仍然完全无法动它一分钱?

── Q&A / 更正记录区(按日期追加)──

规范见 README.md §0。质疑/反馈/纠错按日期累积,不抹错误轨迹。

  • (2026-05-19 建立。暂无 Q&A。)

  • (2026-07-26) 问:详细补充哈希函数的算法细节,包括 hash 过程怎么"求解"出前面几位固定值。

    答(补充 §3.1 直觉层面之下的算法内部细节,未更正原文):

    先澄清关键区分:哈希算法本身的计算是一次性的、确定性的运算,不含任何"搜索/求解"——给定输入走完固定步骤直接吐出输出。"求解前导零"是 PoW 协议在哈希算法外部加的暴力搜索循环,两者不能混为一谈(挖矿部分的完整展开见 04-共识机制.md 同日条目)。

    1. SHA-256 内部机制(比特币用,双重哈希):

      • 预处理:消息补 1 个比特 1,再补 0 到长度 ≡448 (mod 512),最后 64 位写原始消息比特长度——任意长度输入都能切成整数个 512 位块,是输出永远定长的机制层原因。
      • 初始值与常量:8 个初始哈希值 H0-H7 取自前 8 个质数平方根小数部分前 32 位;64 个轮常量 K 取自前 64 个质数立方根小数部分前 32 位——这些是"nothing-up-my-sleeve numbers",可公开复现,防设计者留后门。
      • 压缩函数(每 512 位块):消息扩展成 W0..W63;8 个工作变量 a..h 初始化为当前 H0-H7;跑 64 轮,每轮 T1 = h + Σ1(e) + Ch(e,f,g) + K[i] + W[i]T2 = Σ0(a) + Maj(a,b,c),然后变量整体右移一位、a = T1+T2;其中 Ch(e,f,g)=(e AND f) XOR (NOT e AND g)(e 是 1 听 f、e 是 0 听 g),Maj(a,b,c)是三者少数服从多数,Σ0/Σ1 是不同幅度的循环右移异或。64 轮跑完把 a..h 加回 H0-H7(mod 2³²)——这个"加回"+64 轮的持续移位异或传播,是雪崩效应真正的来源。
      • 双重哈希 SHA256(SHA256(x)):为了防 length-extension attack(单层 SHA-256 的内部结构使攻击者能在不知道原文时"续接"消息并算出合法哈希,双重哈希切断这个可能性)。
    2. Keccak-256 内部机制(以太坊用,算地址/状态树/几乎所有链上哈希):

      • 海绵结构(sponge):1600 位状态,分速率 r=1088 位(对外交互)+ 容量 c=512 位(内部混合、不对外暴露,c 越大抗碰撞/抗原像安全边界越高)。
      • 吸收阶段:消息切 1088 位一块,与状态 r 部分异或,整个 1600 位状态过一次 Keccak-f[1600] 置换,重复到吸收完。
      • Keccak-f[1600]:跑 24 轮,每轮 θ、ρ、π、χ、ι 五个步骤映射,在 5×5×64 的三维比特数组上做异或/循环移位/查表替换。
      • 挤出阶段:直接读状态 r 部分的 256 位作输出。
      • 常见混淆点:以太坊的"SHA3"实际是原始 Keccak 的填充规则,跟 NIST 定稿后的标准 SHA3 填充不同(NIST 定稿时多加了两位后缀区分用途)——keccak256 ≠ 严格意义的 sha3_256,本项目 demo.tskeccak256(abi.encode(addr, 9)) 算 USDC 余额存储槽用的就是这台"魔法榨汁机"。

    完整原始记录(含 PoW 概率推导与可运行 Python 示例)见 ../total/Q&A/2026-07-26.md Q2。