AIGC 文本内容存证合约:Solidity 紧凑结构体与 Merkle Proof 验证设计
在针对 AIGC 大模型生成的文字作品、剧本、技术白皮书或合同草案进行区块链存证时,我们经常遇到高频、大批量的存证诉求:
- 一家企业每天通过自动化流水线生成数万篇产品文案或报告;
- 如果每一篇文本都单独向以太坊或 EVM 链发送一笔交易,不仅会产生昂贵的手续费(Gas),还会因为交易打包拥堵导致吞吐量严重受限。
如何既能保证万级文本在几秒钟内完成链上存证,又能让任何一篇文本在未来发生版权侵权或合规审计时,能够在链上以极低 Gas 进行不可辩驳的密码学真伪验证?
答案是:链下 Merkle Tree 聚合打包 + 链上 Merkle Root 紧凑存证 + Merkle Proof 单篇验证。
今天我们拆解这套经典高效的存证合约架构,并给出生产级 Solidity 合约与 Python 证明生成代码。
一、架构全景:从万级文本到单笔交易
flowchart TD Doc1[文本 1 哈希] --> Leaf1[Leaf 1] Doc2[文本 2 哈希] --> Leaf2[Leaf 2] Doc3[文本 3 哈希] --> Leaf3[Leaf 3] Doc4[文本 4 哈希] --> Leaf4[Leaf 4] Leaf1 & Leaf2 --> NodeA[Hash A] Leaf3 & Leaf4 --> NodeB[Hash B] NodeA & NodeB --> Root[Merkle Root 根哈希] Root --> Tx[向智能合约发送单笔存证交易 (仅存 32 字节 Root)] Tx --> Blockchain[(区块链不可篡改账本)]核心优势:
- Gas 开销降低 99.9%:无论一个批次包含 1,000 篇还是 100,000 篇文本,链上写入操作永远只有 1 次,写入一个 32 字节的
bytes32 merkleRoot; - 轻量自证(Zero-Storage Single Proof):当需要验证某篇特定文章时,用户只需提供该文章哈希与一条简短的 Merkle 证明路径(默克尔证明链,通常只有 10~15 个哈希),智能合约通过纯计算(
pure方法)即可在链上判定其真实性,完全不需要在链上存储具体内容。
二、生产级 Solidity 智能合约代码实现
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; contract BatchAIGCTextNotary { // 紧凑结构体对齐(恰好压入单个 32 字节 Storage Slot,极省 Gas) struct BatchMetadata { uint64 timestamp; // 批次存证时间戳 (8 字节) uint32 totalCount; // 本批次包含的文本总数 (4 字节) address notary; // 存证人钱包地址 (20 字节) } // merkleRoot => BatchMetadata mapping(bytes32 => BatchMetadata) public batches; event BatchCommitted( bytes32 indexed merkleRoot, uint32 totalCount, address indexed notary, uint64 timestamp ); event ProofVerified( bytes32 indexed merkleRoot, bytes32 indexed leafHash, bool isValid ); // 1. 批量存证主入口:写入 Merkle Root function commitBatch(bytes32 _merkleRoot, uint32 _totalCount) external { require(batches[_merkleRoot].timestamp == 0, "Error: Merkle Root already committed"); require(_totalCount > 0, "Error: Batch cannot be empty"); batches[_merkleRoot] = BatchMetadata({ timestamp: uint64(block.timestamp), totalCount: _totalCount, notary: msg.sender }); emit BatchCommitted(_merkleRoot, _totalCount, msg.sender, uint64(block.timestamp)); } // 2. 链上单篇验证:根据 Merkle Proof 验证叶子节点真实性 function verifyDocument( bytes32 _merkleRoot, bytes32 _documentHash, bytes32[] calldata _proof ) external view returns (bool isValid, uint64 notarizedAt, address notary) { BatchMetadata memory batch = batches[_merkleRoot]; require(batch.timestamp != 0, "Error: Batch does not exist"); // 使用 OpenZeppelin 标准算法验证默克尔证明 isValid = MerkleProof.verify(_proof, _merkleRoot, _documentHash); return (isValid, batch.timestamp, batch.notary); } }三、链下 Python 默克尔树构建与证明生成
在数据管线端,每小时定时汇总生成的文本,计算 SHA256 并生成证明文件:
import hashlib from typing import List def sha256_hash(data: bytes) -> bytes: return hashlib.sha256(data).digest() def build_merkle_tree(leaf_hashes: List[bytes]) -> List[List[bytes]]: """自底向上构建完整 Merkle Tree""" tree = [leaf_hashes] while len(tree[-1]) > 1: current_level = tree[-1] next_level = [] for i in range(0, len(current_level), 2): left = current_level[i] right = current_level[i + 1] if i + 1 < len(current_level) else left # 按字典序排序拼接哈希,防止碰撞漏洞 combined = sha256_hash(min(left, right) + max(left, right)) next_level.append(combined) tree.append(next_level) return tree def get_merkle_proof(tree: List[List[bytes]], leaf_index: int) -> List[str]: """提取单篇文档的 Merkle Proof 路径""" proof = [] idx = leaf_index for level in tree[:-1]: sibling_idx = idx + 1 if idx % 2 == 0 else idx - 1 if sibling_idx < len(level): proof.append("0x" + level[sibling_idx].hex()) else: proof.append("0x" + level[idx].hex()) idx //= 2 return proof四、方案权衡与降本建议
- 叶子节点双重哈希防前缀碰撞:在生成叶子节点时,建议对文本内容先做一次 SHA256,再将
sha256(hash)存为叶子节点,防止恶意的二次原像攻击(Second Preimage Attack); - 证明文件异步归档:将生成的每个文档对应的 Merkle Proof 和 Root 随业务结果一并写入 MongoDB 或 MySQL,当用户需要维权时,直接导出带有 Proof 的 PDF 存证证书;
- 结合 Layer2 极速确认:在 Arbitrum 或 Polygon 上,提交一次 32 字节 Merkle Root 的交易手续费不足 0.005 元人民币,即使每 5 分钟提交一个批次,全月链上成本也不超过 50 元。
用最精妙的密码学结构解决实际成本痛点,这才是工程落地的最佳实践。