土法炼钢兴趣小组的算法知识备份

【密码学百科】区块链密码学:共识、默克尔树与数字签名应用

目录

区块链(Blockchain)自 2008 年比特币白皮书发表以来,已从一项实验性技术演变为全球金融基础设施的重要组成部分。然而,很多人在讨论区块链时往往聚焦于”去中心化”和”不可篡改”等宏观特性,却忽略了支撑这些特性的底层密码学机制。事实上,区块链并非发明了新的密码学算法,而是将哈希函数、数字签名、承诺方案(Commitment Scheme)、可验证随机函数(Verifiable Random Function, VRF)等经典密码学原语以精妙的方式组合在一起,构建出一个无需信任第三方即可运转的分布式账本系统。本文将从密码学的视角,系统地剖析区块链中每一个关键环节所依赖的密码学组件,帮助读者建立从底层原语到上层应用的完整知识体系。

笔者认为,理解区块链密码学的关键在于始终追问一个核心问题:区块链究竟需要解决哪些密码学问题,以及它如何解决这些问题?这些问题可以归纳为四类:数据完整性(如何保证历史记录不被篡改)、身份与授权(如何在无中心机构的环境中确认操作者的合法性)、共识随机性(如何在不信任的参与者之间公平地选举领导者)以及隐私保护(如何在公开透明的账本上隐藏敏感信息)。本文的叙述将始终围绕这四个维度展开。从工程实践来看,区块链或许是密码学历史上最好的「营销事件」——它让哈希函数、数字签名、零知识证明等原本只在学术论文中出现的概念进入了公众视野;但它同时也是密码学最艰难的「工程场景」之一——链上计算的透明性、不可逆性和资源约束,对密码学方案的效率、安全性和可组合性提出了极其苛刻的要求。

一、区块链中的密码学原语全景

在深入具体技术之前,我们先从宏观层面审视区块链所使用的密码学工具箱。这些原语各司其职,共同构成了区块链安全性的基石。

哈希函数(Hash Function) 是区块链中使用最为广泛的密码学原语。比特币使用 SHA-256 作为核心哈希算法,以太坊则主要使用 Keccak-256。哈希函数提供三个关键安全属性:抗原像性(Preimage Resistance)保证从哈希值无法反推原始数据;抗第二原像性(Second Preimage Resistance)保证无法找到另一个输入产生相同哈希值;抗碰撞性(Collision Resistance)保证无法找到任意两个不同输入产生相同哈希值。这三个属性分别在区块链的不同场景中发挥作用:抗原像性支撑工作量证明(Proof of Work, PoW)挖矿;抗碰撞性保障默克尔树(Merkle Tree)的完整性验证。

数字签名(Digital Signature) 是区块链中实现身份认证和交易授权的核心工具。每一笔链上交易都必须由发送者的私钥签名,网络中的任何节点都可以使用对应的公钥验证签名的合法性。比特币最初采用 ECDSA(Elliptic Curve Digital Signature Algorithm)配合 secp256k1 椭圆曲线;后续的 Taproot 升级引入了 Schnorr 签名;以太坊 2.0 的信标链(Beacon Chain)则采用了 BLS(Boneh-Lynn-Shacham)签名方案,以支持高效的签名聚合。

默克尔树 是一种基于哈希函数构建的树形数据结构,用于高效地验证大规模数据集中某一元素的存在性。比特币用默克尔树组织区块内的交易,以太坊则将默克尔树扩展为默克尔帕特里夏树(Merkle Patricia Trie),用于存储和验证全局状态。

可验证随机函数(VRF) 在权益证明(Proof of Stake, PoS)共识中扮演着领导者选举(Leader Election)的关键角色。VRF 允许一个节点生成一个可验证的伪随机输出,其他节点可以验证该输出确实由该节点的私钥生成,但无法预测或篡改结果。Algorand 和以太坊 2.0 的验证者选择机制都依赖 VRF。

承诺方案(Commitment Scheme) 允许一方先承诺一个值(Commit),稍后再揭示(Reveal)。这一原语在区块链的多个场景中出现:从简单的哈希时间锁合约(Hash Time-Locked Contract, HTLC)到复杂的零知识证明系统,承诺方案都是不可或缺的构建模块。

一个经常被忽视的观点是,上述这些密码学原语没有一个是为区块链而发明的。区块链的真正创新在于组合方式而非单个组件——它将密码学家几十年来积累的工具,以一种前所未有的方式编排在一起,解决了「在完全不信任的环境中维护一致性状态」这一古老问题。理解这一点很重要:当我们评估一条新链的安全性时,关键不在于它使用了多么新奇的密码学算法,而在于这些算法的组合是否在安全模型下得到了严格的分析。

二、哈希链与区块结构

区块链最基本的数据结构是哈希链(Hash Chain)——每个区块的头部包含前一个区块头部的哈希值,形成一条从创世区块(Genesis Block)到最新区块的不可断裂的链条。

以比特币为例,一个区块头(Block Header)包含六个字段:协议版本号(Version)、前一区块头哈希(Previous Block Hash)、默克尔根(Merkle Root)、时间戳(Timestamp)、难度目标(Difficulty Target)和随机数(Nonce)。区块头的大小固定为 80 字节,对其进行两次 SHA-256 运算即可得到该区块的标识哈希值。

哈希链的安全性来源于一个简洁而深刻的原理:如果攻击者试图篡改历史区块中的某一笔交易,该区块的默克尔根会改变,进而导致区块头哈希改变;由于下一个区块头中记录了前一区块的哈希值,攻击者必须同时修改下一个区块;依此类推,攻击者需要重新计算从被篡改区块到链尾所有区块的工作量证明。在 PoW 机制下,这意味着攻击者需要拥有超过全网一半的算力——这就是著名的 51% 攻击门槛。从密码学角度看,哈希链将抗原像性这一局部安全属性转化为了全链范围的不可篡改性这一全局安全属性。

挖矿(Mining)本质上是一个哈希原像搜索问题:矿工需要找到一个 Nonce 值,使得区块头的双重 SHA-256 哈希值小于当前难度目标。用数学语言描述,即寻找 Nonce 使得 SHA256(SHA256(BlockHeader)) < Target。由于 SHA-256 被建模为随机预言机(Random Oracle),找到满足条件的 Nonce 没有比穷举搜索更好的策略。难度目标每 2016 个区块自动调整一次,以维持平均 10 分钟的出块间隔。这一机制巧妙地利用了哈希函数的单向性,将计算资源转化为区块链安全性的度量。

三、默克尔树与状态证明

默克尔树(Merkle Tree)由 Ralph Merkle 于 1979 年提出,是区块链中实现高效数据完整性验证的核心数据结构。

笔者认为,默克尔树是密码学历史上第一个真正意义上的「可验证数据结构」(verifiable data structure)——它将哈希函数的局部安全属性提升为对整个数据集的全局完整性保证。这个看似简单的发明比区块链早了近三十年,却预见了「不信任环境下高效验证」这一核心需求。今天我们在区块链中看到的所有状态证明机制——SPV、状态根、Verkle 树——追根溯源都是默克尔树思想的延伸。从某种意义上说,如果没有默克尔树,区块链的「轻节点」概念将无从谈起,而一个要求所有参与者存储全部数据的系统注定无法规模化。

二叉默克尔树(Binary Merkle Tree) 的构建过程如下:将所有叶子节点(通常是交易的哈希值)两两配对,计算每对节点的级联哈希值作为父节点;重复此过程直到只剩一个根节点——即默克尔根(Merkle Root)。如果叶子节点数量为奇数,则复制最后一个节点使其成对。

默克尔树的核心价值在于其高效的成员证明(Proof of Inclusion)机制。要证明某笔交易存在于一个包含 n 笔交易的区块中,只需提供从该叶子节点到根节点路径上的 log₂(n) 个兄弟节点哈希值。验证者可以从目标交易的哈希值出发,逐层向上计算,最终与区块头中的默克尔根比对。这一机制使得轻节点(Light Node)无需下载整个区块即可验证特定交易的存在性——这就是比特币白皮书中描述的简化支付验证(Simplified Payment Verification, SPV)。

以下是默克尔树构建与证明验证的 Python 实现:

import hashlib
from typing import List, Tuple, Optional


def sha256(data: bytes) -> bytes:
    return hashlib.sha256(data).digest()


class MerkleTree:
    """二叉默克尔树的构建与证明验证。"""

    def __init__(self, leaves: List[bytes]):
        if not leaves:
            raise ValueError("叶子节点列表不能为空")
        self.leaves = [sha256(leaf) for leaf in leaves]
        self.layers: List[List[bytes]] = [self.leaves]
        self._build()

    def _build(self):
        current = self.leaves
        while len(current) > 1:
            next_layer = []
            for i in range(0, len(current), 2):
                left = current[i]
                right = current[i + 1] if i + 1 < len(current) else current[i]
                next_layer.append(sha256(left + right))
            self.layers.append(next_layer)
            current = next_layer

    @property
    def root(self) -> bytes:
        return self.layers[-1][0]

    def get_proof(self, index: int) -> List[Tuple[bytes, str]]:
        """获取指定叶子节点的默克尔证明路径。

        返回值为 (兄弟哈希, 方向) 的列表,方向为 'L' 或 'R',
        表示兄弟节点在当前节点的左侧还是右侧。
        """
        if index < 0 or index >= len(self.leaves):
            raise IndexError(f"索引 {index} 超出范围 [0, {len(self.leaves)})")
        proof = []
        for layer in self.layers[:-1]:
            if index % 2 == 0:
                sibling_index = index + 1
                direction = "R"
            else:
                sibling_index = index - 1
                direction = "L"
            if sibling_index < len(layer):
                proof.append((layer[sibling_index], direction))
            else:
                proof.append((layer[index], direction))
            index //= 2
        return proof

    @staticmethod
    def verify_proof(
        leaf_data: bytes,
        proof: List[Tuple[bytes, str]],
        expected_root: bytes,
    ) -> bool:
        """验证默克尔证明:从叶子哈希沿路径重算到根,与期望根比对。"""
        current = sha256(leaf_data)
        for sibling_hash, direction in proof:
            if direction == "R":
                current = sha256(current + sibling_hash)
            else:
                current = sha256(sibling_hash + current)
        return current == expected_root


if __name__ == "__main__":
    transactions = [f"tx_{i}".encode() for i in range(8)]
    tree = MerkleTree(transactions)
    print(f"默克尔根: {tree.root.hex()}")

    target_index = 3
    proof = tree.get_proof(target_index)
    print(f"\n交易 tx_{target_index} 的证明路径 ({len(proof)} 层):")
    for sibling, direction in proof:
        print(f"  {direction}: {sibling.hex()[:16]}...")

    valid = MerkleTree.verify_proof(transactions[target_index], proof, tree.root)
    print(f"\n验证结果: {'通过' if valid else '失败'}")

    tampered = b"tx_fake"
    invalid = MerkleTree.verify_proof(tampered, proof, tree.root)
    print(f"篡改后验证: {'通过' if invalid else '失败'}")

以太坊在默克尔树的基础上进一步引入了默克尔帕特里夏树(Merkle Patricia Trie, MPT)。与比特币仅用默克尔树组织交易列表不同,以太坊需要维护一个庞大的全局状态——包括每个账户的余额、Nonce、合约代码和存储。MPT 将十六叉前缀树(Hexary Trie)与默克尔哈希相结合:每个内部节点存储其所有子节点哈希的聚合值,任何叶子节点的变更都会沿路径向上传播到根节点。以太坊的区块头中包含三棵 MPT 的根哈希:状态根(State Root)、交易根(Transactions Root)和收据根(Receipts Root),分别对应全局状态、区块交易列表和交易执行结果。

MPT 不仅支持存在性证明(Proof of Inclusion),还支持不存在性证明(Proof of Exclusion)——即证明某个键值对不存在于树中。这一特性对于轻客户端验证”某账户不存在”或”某存储槽为空”等负面断言至关重要。需要指出的是,以太坊社区正在推进从 MPT 向 Verkle 树(Verkle Tree)的迁移。Verkle 树使用向量承诺(Vector Commitment)替代哈希聚合,可以将证明大小从 O(k·log n) 降低到 O(k),其中 k 是树的分支因子,n 是叶子节点数量。这一改进对于降低无状态客户端(Stateless Client)的带宽需求具有重要意义。

默克尔树与状态证明的具体使用方式与区块链的底层交易模型密切相关。比特币采用 UTXO(Unspent Transaction Output)模型,以太坊采用账户(Account)模型,两者对签名方案和默克尔证明的依赖方式存在显著差异:

维度 UTXO 模型(比特币) 账户模型(以太坊)
状态表示 未花费交易输出的集合 全局账户状态映射(地址 → 余额/Nonce/存储)
默克尔树用途 二叉默克尔树组织区块内交易列表,支持 SPV 证明 MPT 维护三棵状态树(状态根、交易根、收据根),支持任意状态查询证明
签名粒度 每笔交易的每个输入都需独立签名,证明对特定 UTXO 的所有权 每笔交易整体签名一次,证明账户持有者的授权
多签实现 脚本层原生支持 m-of-n 多签(P2SH/P2WSH),Taproot 后支持 Schnorr 聚合 智能合约层实现(如 Gnosis Safe),或 ERC-4337 账户抽象
默克尔证明大小 O(log n),n 为区块内交易数(通常数千笔) O(k · log n),n 为全局状态条目数(数亿级),k 为树分支因子
隐私特性 天然支持一次性地址,交易图分析是主要隐私威胁 地址复用普遍,状态公开可查,隐私保护依赖应用层方案
并行验证 UTXO 之间天然独立,利于签名的并行批量验证 交易可能涉及共享状态,需要顺序执行以保证状态一致性

从工程实践来看,UTXO 模型对签名验证更友好——每个 UTXO 的花费可以独立验证,这使得 Schnorr 批量验证和 BLS 聚合等优化更容易部署。而账户模型的优势在于状态查询的灵活性——MPT 允许轻客户端验证任意账户的余额、合约存储甚至代码,这是简单的交易默克尔树无法提供的能力。

四、共识机制中的密码学

共识机制(Consensus Mechanism)决定了区块链网络如何就新区块的内容达成一致。不同的共识机制依赖不同的密码学组件,但都需要解决同一个核心问题:在缺乏可信第三方的情况下,如何防止恶意节点操纵区块的产生。

工作量证明(Proof of Work, PoW) 是中本聪在比特币中首创的共识机制。其密码学核心是哈希难题(Hash Puzzle):矿工需要找到一个 Nonce,使得区块头的哈希值满足特定的难度条件。这一过程的密码学安全性建立在哈希函数的抗原像性之上——在理想的随机预言机模型下,找到满足条件的输入的唯一方法是随机尝试。比特币使用 SHA-256,莱特币使用 Scrypt(一种内存困难型哈希函数,旨在抵抗 ASIC 矿机),以太坊 1.0 使用 Ethash(同样具有内存困难特性)。PoW 的安全性假设是:诚实节点控制全网大多数算力。当这一假设成立时,攻击者无法比诚实链更快地延伸一条替代链,从而保证了链的最终一致性。

权益证明(Proof of Stake, PoS) 用密码学随机选举取代了算力竞赛。以太坊 2.0 的 PoS 机制使用 RANDAO 协议结合 VRF 来选举每个时隙(Slot)的区块提议者(Block Proposer)。具体流程如下:每个验证者在其被分配到的时隙内使用私钥对当前纪元(Epoch)的随机种子进行 BLS 签名,签名值即为其 VRF 输出。由于 BLS 签名具有确定性(同一私钥对同一消息的签名唯一),任何验证者都可以验证该输出的正确性,但在计算完成之前无法预测结果。所有验证者的 VRF 输出通过异或(XOR)操作聚合为下一个纪元的随机种子——这就是 RANDAO 机制。这种设计确保了领导者选举的不可预测性和可验证性。

Algorand 的共识协议采用了更纯粹的 VRF 方案:每个节点独立计算 VRF,根据输出值和自身的权益份额决定是否被选为委员会成员。这种”密码学自选举”(Cryptographic Self-Selection)消除了通信开销,使得委员会成员的身份在其公布消息之前对其他节点完全保密,有效防止了针对性的拒绝服务攻击(Denial of Service, DoS)。

拜占庭容错(Byzantine Fault Tolerance, BFT) 类共识通常要求验证者对区块进行多轮投票签名。Tendermint 采用的实用拜占庭容错(Practical Byzantine Fault Tolerance, PBFT)协议中,每轮共识需要验证者集合中三分之二以上的签名才能确认一个区块。以太坊 2.0 的 Casper FFG(Friendly Finality Gadget)也遵循类似原则:当一个检查点(Checkpoint)收集到三分之二以上验证者权益的 BLS 签名时,该检查点被认定为”已确定”(Finalized),不可逆转。BLS 签名的聚合特性在此场景中至关重要——数千个验证者的签名可以聚合为一个固定大小的签名,大幅降低了链上的存储和验证开销。

五、链上签名方案

数字签名是区块链中连接”身份”与”行为”的纽带——每一笔交易的合法性都通过签名来证明。不同的区块链平台根据安全性、效率和功能性等考量选择了不同的签名方案。

比特币与 ECDSA/secp256k1。 比特币采用基于椭圆曲线的数字签名算法 ECDSA,使用 secp256k1 曲线。secp256k1 是一条 Koblitz 曲线,其参数选取具有特殊结构(即 a = 0, b = 7),使得标量乘法可以利用自同态(Endomorphism)加速,计算效率略高于随机选取参数的曲线。比特币的地址本质上是公钥哈希的编码:对公钥先进行 SHA-256 再进行 RIPEMD-160 运算,得到 20 字节的公钥哈希,再经过 Base58Check 编码(或后续的 Bech32 编码)生成最终地址。

以太坊与 ecrecover。 以太坊同样使用 ECDSA/secp256k1,但在签名验证方面引入了一个独特的设计——ecrecover 操作。标准的 ECDSA 验证需要三个输入:消息、签名和公钥。而以太坊的 ecrecover 接收消息和签名(包含恢复标识符 v),直接从中恢复出签名者的公钥。以太坊地址是公钥的 Keccak-256 哈希值的最后 20 字节。这一设计简化了智能合约中的签名验证逻辑,ecrecover 作为以太坊虚拟机(Ethereum Virtual Machine, EVM)的预编译合约(Precompiled Contract),Gas 成本仅为 3000,远低于通用计算。

Schnorr 签名与 Taproot。 2021 年比特币的 Taproot 升级引入了基于 secp256k1 曲线的 Schnorr 签名方案(BIP-340)。相比 ECDSA,Schnorr 签名具有线性性(Linearity)——多个签名可以简单地相加得到一个聚合签名,这为多签(Multisig)和批量验证(Batch Verification)提供了天然支持。具体而言,n 个参与者各自对同一消息签名,通过 MuSig2 协议交互生成一个聚合签名,在链上看来与单一签名无异。这不仅降低了交易大小和手续费,还增强了隐私性——外部观察者无法区分单签交易和多签交易。

BLS 签名与以太坊 2.0。 信标链选择了 BLS12-381 曲线上的 BLS 签名方案。BLS 签名的核心优势在于非交互式聚合(Non-interactive Aggregation):无需参与者之间的任何通信,任何人都可以将多个 BLS 签名聚合为一个固定大小(48 字节)的聚合签名,并且验证成本不随签名数量线性增长。在以太坊 2.0 中,每个时隙有数百乃至上千个验证者需要对区块投票,BLS 聚合使得这些投票可以被压缩为一个签名加一个位图(Bitfield),极大地降低了网络和存储负担。BLS 签名的安全性基于双线性配对(Bilinear Pairing)假设,具体而言是 co-CDH(Computational co-Diffie-Hellman)问题的困难性。

笔者认为,BLS 聚合签名在以太坊 2.0 中的采用过程,深刻揭示了密码学验证效率的实践极限。理论上,BLS 聚合可以将 n 个签名压缩为一个,但这里有一个容易被忽略的代价:虽然聚合签名本身是固定大小的,验证时仍然需要 n 次配对运算(或至少需要访问所有 n 个公钥)。以太坊 2.0 的工程团队在实践中发现,当验证者数量从几百增长到数十万时,即使有聚合签名,验证的计算瓶颈也从签名大小转移到了公钥管理和配对运算上。这个教训具有普遍意义:在评估任何密码学方案的「可扩展性」时,我们不能只看单一维度的优化(如签名大小),而必须考虑整个验证流水线中的瓶颈迁移。BLS 的故事告诉我们,密码学的效率优化永远是一个系统工程问题,而非单点突破。

EdDSA 与新兴区块链。 一些新兴区块链平台(如 Solana、Cosmos、Near)选择了 EdDSA 签名方案,通常使用 Ed25519 曲线。EdDSA 的确定性签名生成过程消除了因随机数生成器缺陷导致的私钥泄露风险(ECDSA 曾因 PlayStation 3 的随机数复用漏洞而泄露私钥),同时其严格的签名验证规则避免了签名可锻造性(Signature Malleability)问题。

六、HD 钱包与密钥派生

现代区块链钱包面临一个实际问题:用户通常需要管理大量地址——出于隐私考虑,每笔交易应使用不同的地址。逐个备份每个私钥既不现实也不安全。分层确定性钱包(Hierarchical Deterministic Wallet, HD Wallet)通过密码学密钥派生解决了这一问题。

BIP-39:助记词生成种子。 BIP-39 定义了从助记词(Mnemonic)生成种子(Seed)的标准流程。首先,从密码学安全的随机数生成器获取 128 至 256 位的熵(Entropy),对其进行 SHA-256 运算取前若干位作为校验和(Checksum),将熵与校验和级联后按每 11 位分割,每段映射到一个包含 2048 个单词的词表中,得到 12 至 24 个助记词。随后,使用 PBKDF2-HMAC-SHA512 对助记词进行密钥拉伸(Key Stretching),迭代 2048 次,可选地加入用户自定义的口令(Passphrase)作为盐值(Salt),最终生成 512 位的种子。PBKDF2 的使用增加了暴力破解的计算成本,而可选口令则提供了一层额外的保护——即使助记词被泄露,没有口令也无法恢复私钥。

BIP-32:分层确定性密钥派生。 BIP-32 定义了从种子生成密钥树的算法。种子首先通过 HMAC-SHA512 生成主私钥(Master Private Key)和主链码(Master Chain Code)。此后,每一层的子密钥通过以下方式派生:将父密钥(或对应公钥)与子索引(Child Index)级联后进行 HMAC-SHA512 运算,输出的左 256 位与父私钥相加(模椭圆曲线阶 n)得到子私钥,右 256 位作为子链码。BIP-32 定义了两种派生模式:普通派生(Normal Derivation)和硬化派生(Hardened Derivation)。普通派生允许从父公钥和父链码直接推导子公钥,支持”只读钱包”场景——例如,商家收款服务器无需存储私钥即可生成新地址。硬化派生则要求使用父私钥,提供更强的安全隔离——即使一个子私钥和父链码同时泄露,也无法反推父私钥。

BIP-44:多币种路径结构。 BIP-44 在 BIP-32 的基础上定义了标准化的路径格式:m / purpose’ / coin_type’ / account’ / change / address_index。其中 purpose 固定为 44,coin_type 区分不同的加密货币(比特币为 0,以太坊为 60),account 允许用户在同一种子下管理多个逻辑账户,change 区分外部地址(用于接收)和内部地址(用于找零),address_index 是具体的地址序号。撇号(’)表示硬化派生。这一层次化的路径结构使得单个助记词可以确定性地管理跨多条链的无限数量的地址,同时保持良好的隔离性。

七、多签与门限钱包

单一私钥控制资产存在单点故障风险。多签(Multisig)和门限签名(Threshold Signature)技术通过密码学手段实现了密钥的分布式管理。

比特币原生多签。 比特币通过脚本系统支持 m-of-n 多签:一笔 UTXO(Unspent Transaction Output)的解锁条件被设定为”n 个公钥中至少 m 个对应的签名”。传统的 P2SH(Pay-to-Script-Hash)多签将完整的赎回脚本(Redeem Script)和所有签名放在交易的见证数据(Witness Data)中,交易大小随签名数量线性增长。

MuSig2 与 Taproot 多签。 Taproot 升级带来了基于 Schnorr 签名的 MuSig2 协议。MuSig2 是一个两轮交互式多方签名协议:第一轮中,每个参与者独立生成两个随机 Nonce 并广播其承诺;第二轮中,参与者根据聚合 Nonce 和消息计算各自的部分签名(Partial Signature),部分签名之和即为最终聚合签名。MuSig2 生成的聚合签名在链上表现为一个标准的 Schnorr 签名,验证成本与单签相同,且外部观察者无法判断这是一个多签交易。这种”多签隐匿”特性同时降低了交易成本并提升了用户隐私。

门限 ECDSA。 对于使用 ECDSA 的区块链(如比特币的非 Taproot 交易和以太坊),门限签名方案(Threshold Signature Scheme, TSS)允许 n 个参与者共享一个 ECDSA 私钥,其中任意 t+1 个参与者可以协作生成合法签名,而任何 t 个或更少的参与者无法获得关于私钥的任何信息。GG18(Gennaro-Goldfeder 2018)和 GG20 是目前最广泛采用的门限 ECDSA 协议,已被多个机构级钱包(如 Fireblocks)和跨链桥(如 THORChain)部署。门限 ECDSA 的优势在于链上只出现一个标准签名,无需修改底层区块链协议。

账户抽象与智能合约钱包。 以太坊的 ERC-4337 账户抽象标准将签名验证逻辑从协议层移至智能合约层,允许开发者在合约中实现任意的验证逻辑——包括多签、社交恢复(Social Recovery)、密钥轮换(Key Rotation)等。这种方法的灵活性远超链上原生多签,但需要在 Gas 成本和安全审计方面付出额外代价。

八、智能合约中的密码学

智能合约(Smart Contract)是运行在区块链虚拟机上的可编程逻辑。由于所有合约执行对所有节点透明,智能合约中的密码学操作需要在”链上可验证”与”计算成本可控”之间取得平衡。

ECDSA 恢复与链上身份验证。 以太坊的 ecrecover 预编译合约是智能合约中最常用的密码学工具。典型应用场景包括:元交易(Meta Transaction)中继器验证用户签名后代付 Gas 费;EIP-712 结构化数据签名允许用户在钱包中预览并签署人类可读的操作描述,合约通过 ecrecover 验证签名者身份。这一模式将签名的生成放在链下(Off-chain),仅将验证放在链上,有效降低了 Gas 消耗。

哈希原像与支付通道。 哈希时间锁合约(HTLC)是闪电网络(Lightning Network)和跨链原子交换(Atomic Swap)的基础构件。HTLC 的核心逻辑是:Alice 选择一个秘密值 s,计算 h = SHA256(s),并创建一个合约——如果 Bob 在截止时间前提交 s 使得 SHA256(s) = h,则资金转给 Bob;否则资金退回 Alice。这里,哈希函数的抗原像性保证了 Bob 无法在不知道 s 的情况下取走资金。

默克尔证明验证。 智能合约可以在链上验证默克尔证明,用于多种场景:空投(Airdrop)合约通过默克尔树记录有资格领取代币的地址列表,用户提交默克尔证明领取自己的份额;跨链桥通过验证源链区块头中的默克尔证明确认交易的真实性。这种模式将大量数据存储在链下,链上只保留一个 32 字节的默克尔根,显著降低了存储成本。

零知识证明验证合约。 随着零知识证明技术的成熟,链上验证零知识证明已成为重要的密码学模式。zk-SNARK 验证合约接收一个证明和若干公开输入,通过配对运算(Pairing Check)确认证明的有效性。以太坊通过 EIP-196 和 EIP-197 引入了 BN254 曲线上的椭圆曲线加法和配对运算预编译合约,使得 Groth16 证明的链上验证成本降至约 20 万 Gas。zk-Rollup 方案(如 zkSync、StarkNet)正是基于这一能力,将大量交易的执行移至链下,仅在链上提交状态变更和有效性证明。

九、隐私区块链技术

公开区块链的交易数据对所有人可见,这在很多场景下是不可接受的。密码学为区块链隐私保护提供了多种技术路径。

保密交易(Confidential Transactions)。 由 Gregory Maxwell 提出的保密交易使用 Pedersen 承诺(Pedersen Commitment)隐藏交易金额。Pedersen 承诺 C = g^v · h^r 中,v 是交易金额,r 是随机致盲因子(Blinding Factor),g 和 h 是椭圆曲线上的两个生成元。Pedersen 承诺具有同态性(Homomorphism)——多个承诺的和等于对应金额之和的承诺——这使得节点可以在不知道具体金额的情况下验证交易的输入金额之和等于输出金额之和(即”不凭空创造货币”)。为了证明金额为非负值(防止整数溢出攻击),每笔交易还需附带一个范围证明(Range Proof)。Bulletproofs 是目前最高效的范围证明方案,证明大小为 O(log n),其中 n 是金额的位数。

环签名与门罗币(Monero)。 门罗币使用环签名(Ring Signature)隐藏交易发送者的身份。环签名允许签名者从一组公钥中选择若干个”诱饵”公钥,生成一个签名,验证者可以确认签名者是该组公钥之一的持有者,但无法确定具体是哪一个。门罗币将环签名与保密交易(RingCT)和隐身地址(Stealth Address)相结合,同时隐藏了交易的发送者、接收者和金额。隐身地址基于 Diffie-Hellman 密钥交换:发送者使用接收者的公钥和一个随机值生成一个一次性地址,只有接收者能够使用其私钥识别并花费发送到该地址的资金。

zk-SNARKs 与 Zcash。 Zcash 使用零知识简洁非交互式知识论证(Zero-Knowledge Succinct Non-Interactive Argument of Knowledge, zk-SNARK)实现完全的交易隐私。在 Zcash 的屏蔽交易(Shielded Transaction)中,发送者生成一个 zk-SNARK 证明,该证明能够在不泄露发送者、接收者和金额信息的前提下,向验证者证明以下事实:发送者拥有足够的资金(通过证明知道某个承诺的打开方式);交易金额平衡(输入等于输出加手续费);没有双重花费(通过公开一个唯一的废止符 Nullifier)。zk-SNARK 的”简洁性”体现在证明大小仅为数百字节且验证时间为毫秒级,与被证明的计算复杂度无关。Zcash 最初使用 Groth16 证明系统,需要可信设置(Trusted Setup);后续的 Halo 2 方案消除了这一要求,实现了无需可信设置的递归证明组合(Recursive Proof Composition)。

混币协议与 Tornado Cash。 Tornado Cash 是以太坊上的一个去中心化混币协议,使用增量默克尔树(Incremental Merkle Tree)和 zk-SNARK 实现存取款的解耦。用户存入固定面额的 ETH 时,合约记录一个承诺值(由秘密值和废止符的哈希构成)到默克尔树中。取款时,用户提交一个 zk-SNARK 证明,该证明表明用户知道树中某个叶子节点对应的秘密值和废止符,且该废止符之前未被使用过——但不揭示是哪个叶子节点。这样,取款操作在密码学上与任何一笔存款都无法关联。Tornado Cash 使用 Groth16 证明系统,电路基于 MiMC 或 Poseidon 等对 zk-SNARK 友好的哈希函数(即算术电路门数量较少),以降低证明生成的计算成本。

区块链密码学的发展远未止步。从后量子安全的签名方案到全同态加密(Fully Homomorphic Encryption, FHE)在链上计算中的应用,从可验证延迟函数(Verifiable Delay Function, VDF)到基于格(Lattice)的零知识证明系统,密码学研究的每一个前沿突破都在为区块链的下一代演进提供新的可能性。理解这些底层密码学机制,不仅有助于评估区块链系统的安全性边界,也为设计更高效、更隐私、更可扩展的去中心化系统奠定了坚实的理论基础。


密码学百科系列 · 第 48 篇

← 上一篇:国密算法体系 | 系列目录 | 下一篇:密码学与隐私


By .