该文档详细阐述了一个在L1智能合约上管理支付通道的协议规范,涵盖了通道设置、用户支付、结算、代理争议处理和资金提现等整个生命周期。它利用默克尔树、Keccak256哈希函数、零知识证明或可信执行环境(TEE)进行验证,确保了链下支付的安全性和链上结算的准确性。
markdown
协议的核心是一个L1智能合约,用于管理通道生命周期。
状态:合约维护 ChannelID 与其存储结构之间的查找映射。
mapping(bytes32 => Channel) channels;结构体定义:
enum Status {
Active,
Settlement,
Closed
}
struct Channel {
Status status;
bytes32 merkleRoot; // Merkle 根
uint256 maxFundedAmount; // TotalMaxSpend 资金
address owner; // 通道控制器
bytes32 encryptedValuesHash; // H(加密金额, 加密信息)
}
资金:持有用户存入的 TotalMaxSpend。
验证:
$P_{\text{alloc}}$ 证明(通过代理进行链下检查,但合约持有承诺)。sequenceDiagram
participant User
participant Agent
participant Contract
Note over User, Contract: 设置阶段
User->>Contract: 1. 存入资金并提交 Merkle 根 (Mc)
Note over User, Agent: 支付阶段
User->>Agent: 2. 注册 (回执 + MerkleProof)
User->>Agent: 3. 支付 (累计回执)
Note over User, Contract: 结算阶段
User->>Contract: 4. 发起结算 (EncryptedValues[], ReturnAmount)
User->>Contract: 5. 证明 P_settle (有效求和)
Contract->>Contract: 6. 进入结算模式 (争议窗口开始)
Note over Agent, Contract: 争议阶段 (可选)
opt 用户少付 / 被排除
Agent->>Contract: 7. 争议 (回执 + MerkleProof + 密钥)
Contract->>Contract: 8. 验证叶子 & 对比 EncryptedValues
Contract->>Agent: 9. 直接支付代理 (如果有效)
end
Note over Agent, Contract: 提款阶段 (纪元后)
Contract->>Contract: 10. 纪元最终确定 (H(通道ID, Merkle根, 加密值哈希) 累积)
Agent->>Contract: 11. 声明 (所有纪元通道的元证明 P_claim)
Contract->>Agent: 12. 批量转账
概述: 设置操作由用户在每个通道生命周期内执行一次。目前不支持通道调整,因为根据当前协议设计,调整会泄露旧预算和新预算之间的差异,这可能被用于推断新增代理的预算,并可能允许推断实际代理。
MaxSpend)。MerkleRoot ($Mc$),向合约存入 TotalMaxSpend 资金,并提交证明 `$P{\text{alloc}}$`,证明有足够的资金覆盖所有代理的预算。$P_{\text{alloc}}$):用户生成一个证明,以验证 TotalMaxSpend 覆盖了所有代理分配。此证明由代理在链下验证,以验证用户的偿付能力。
实现说明: 在 TEE 路径中,
TeeUserSetup<H>在安全区内验证 Merkle 树的构建并签署结果。计划后续用执行相同逻辑的 ZK 电路来替代。
叶子结构:
$$ Leaf = H(\text{SessionKey}, \text{Address}, \text{MaxSpend}) $$
数据布局:
注意:
MaxSpend字段设计为可容纳 64 位,但为简化原型暂时存储在 256 位插槽中。Address字段同理(有效 160 位)。
可视化方案:
graph BT
%% 叶子构建详情
subgraph Construction [构建逻辑]
direction BT
SK["SessionKey<br/>(256位)"]
Addr["地址<br/>(256位)"]
MS["MaxSpend<br/>(64位)"]
SK & Addr & MS -->|H| Leaf["叶子哈希"]
end
%% Merkle 树金字塔 (平衡)
subgraph Tree [Merkle 树]
direction BT
L1[叶子 1] & L2[叶子 2] --> H1[节点哈希]
L3[叶子 3] & L4[叶子 4] --> H2[节点哈希]
H1 & H2 --> Root[Merkle 根]
end
%% 用于上下文的松散耦合
Leaf -.-> L1
电路例程:
证明结构遵循标准的 Merkle Sum Tree 验证流程。例程 1 和 2 的输入是私有见证。
MaxSpend。SessionKey,PackedData,$MS_i$。$LeafHash = H(\text{SessionKey}, \text{PackedData})$。MaxSpend 的正确提取。
$\text{PackedData} \ll 160 == MS_i \ll 160$。$MS_i < 2^{64}$。$(\text{LeafHash}, MS_i)$ 传递给例程 2。$(\text{LeftHash}, \text{LeftSum})$ -> 源自例程 1 或先前的例程 2。$(\text{RightHash}, \text{RightSum})$ -> 源自例程 1 或先前的例程 2。$ParentHash = H(\text{LeftHash}, \text{RightHash})$。$ParentSum = \text{LeftSum} + \text{RightSum}$。$(\text{ParentHash}, \text{ParentSum})$ 传递到下一层。MerkleRoot ($M_c$),TotalMaxSpend。$\text{FinalRoot}$,$\text{FinalSum}$(最终例程 2 执行的结果)。$\text{FinalRoot} == M_c$。$\text{FinalSum} == \text{TotalMaxSpend}$。概述: 支付是链下交互,用户向代理提供已签名的承诺。代理根据链上通道状态和首次交互时提供的 Merkle 证明验证这些承诺。
数据结构:
struct { bytes32 channelId; uint64 amount; bytes signature; }(channelId, amount) 的签名哈希。struct { bytes32[] merkleProof; LeafDetails leaf; },其中 LeafDetails = struct { uint256 sessionKey; uint256 packedData; }协议逻辑:
PaymentReceipt + RegistrationInfo。$\text{Channel}[\text{channelId}].\text{status} == \text{Active}$。$\text{ecrecover}(\text{signature})$ 必须匹配 $\text{Channel}[\text{channelId}].\text{owner}$。
$\text{RegistrationInfo}.\text{merkleProof}$ 将所声明的叶子连接到 $\text{Channel}[\text{channelId}].\text{merkleRoot}$。$LeafHash = H(\text{SessionKey}, \text{PackedData})$ 并与证明叶子匹配。
$\text{PackedData}$ 中的 $\text{Address}$ 匹配代理自己的地址。$\text{PackedData}$(位 160-223)中提取 $\text{MaxSpend}$ 并断言 $amount \le \text{MaxSpend}$。merkleProof,leaf 和 $\text{LatestAmount} = amount$。PaymentReceipt,其中 $newAmount > oldAmount$。$\text{Channel}[\text{channelId}].\text{status} == \text{Active}$。$\text{Channel}[\text{channelId}].\text{owner}$。$newAmount \le \text{MaxSpend}$(使用存储的 $\text{MaxSpend}$)。$\text{LatestAmount} = newAmount$。$\text{MaxSpend}$ 对于此纪元是不可变的。设计决策:
$X + Y$)。代理只需保留金额最大的回执,并允许用户签署新的总额。$\text{ChannelID}$ 查找隐式绑定。$\text{Active}$ 状态,以防止接受来自处于 $\text{Settlement}$ 或 $\text{Closed}$ 状态的通道的资金(在这种情况下,用户可能已经开始提款)。概述: 由用户触发,用于关闭通道并提取剩余资金。用户证明代理已花费的金额并收回差额。
旁注: 未来的迭代可能会考虑“部分关闭”以结算特定代理,同时保持其他代理活跃。
用户提交: 要进行结算,用户提交:
$Total - \sum \text{AgentSpent}$。$\text{EncryptedAmount}: Amount \oplus H(\text{SessionKey}, 0)$$\text{EncryptedInfo}: Address \oplus H(\text{SessionKey}, 1)$$P_{\text{settle}}$)。实现说明: 在 TEE 路径中,
TeeSettlement<H>在安全区内验证结算会计。加密值作为encryptedValuesHash = keccak256(abi.encode(encryptedAmounts, encryptedInfos))在链上提交。
链上承诺:
合约计算并存储 $$encryptedValuesHash = \text{keccak256}(\text{abi.encode}(\text{encryptedAmounts}, \text{encryptedInfos}))$$。此哈希随后在 finalizeSettlement() 期间包含在纪元哈希链条目中,将加密数据绑定到纪元哈希,并使 TEE/ZK 验证器能够在代理声明期间确认加密数据。
$P_{\text{settle}}$) - 电路例程:例程 1:解密和验证 (拆分加密)
$\text{EncryptedAmount}$,$\text{EncryptedInfo}$。$\text{SessionKey}$。$Amount = \text{EncryptedAmount} \oplus H(\text{SessionKey}, 0)$$Address = \text{EncryptedInfo} \oplus H(\text{SessionKey}, 1)$$Amount < 2^{64}$。$\text{SessionKey}$,解密后的 $Amount$ 很可能是一个随机的 256 位值,这会以极高的概率违反此约束($Prob \approx 1 - 2^{-192}$)。输出: $Amount$。例程 2:求和
$\text{Accumulator}$(内部),$Amount$(来自例程 1)。$\text{Accumulator} += Amount$。例程 3:最终余额
$\text{TotalMaxSpend}$,$\text{ReturnAmount}$。$\text{Accumulator}$(最终)。$\text{TotalMaxSpend} == \text{ReturnAmount} + \text{Accumulator}$。安全分析 (假密钥攻击):
恶意用户可能尝试使用伪造的 $\text{SessionKey}$ 将有效条目解密为更低的金额(从而减少 $\text{Accumulator}$ 并增加他们的 $\text{ReturnAmount}$)。
$Amount$ 并强制 $Amount < 2^{64}$,任何伪造密钥都会生成一个随机的 256 位“金额”,这会以极高的概率未能通过检查。$\text{SessionKey}$ 存在于原始 MerkleTree 中。$L \times \log(N)$ 次哈希(其中 $L$ 是可结算的代理数量,$N$ 是代理总数),这通常比拆分加密检查(其复杂度随 $L$ 线性增长)更昂贵。实现说明: 争议流程完全在链上。不涉及 TEE 或 ZK 证明; 合约直接验证 Merkle 证明、签名和解密金额。
概述: 如果用户尝试以不正确的金额关闭通道给代理(或完全排除代理),则会发生争议。
SessionKey 被泄露)。$\text{Settlement}$ 状态。代理提交:
代理通过以下参数调用合约上的 dispute():
$\{\text{channelId}, \text{amount}, \text{signature}\}$。$\text{Channel}[\text{channelId}].\text{merkleRoot}$ 中。$\text{SessionKey}$,$\text{Address}$,$\text{MaxSpend}$。合约逻辑 (链上验证):
$\text{Channel}[\text{channelId}].\text{status} == \text{Settlement}$。$\text{ecrecover}(\text{signature}) == \text{Channel}[\text{channelId}].\text{owner}$。
$amount$。$Leaf = H(\text{SessionKey}, \text{Address}, \text{MaxSpend})$。$\text{MerkleProof}$ 针对存储的 $\text{MerkleRoot}$ 验证 $Leaf$。$\text{Address}$ 匹配 $\text{msg.sender}$(代理)。$\text{EncryptedValues}[]$(在结算触发期间存储/承诺)。$DecryptedAddress = \text{EncryptedInfo} \oplus H(\text{SessionKey}, 1)$。$\text{DecryptedAddress}$ 是否匹配代理的地址。$\text{DecryptedAmount}$(来自 $\text{EncryptedAmount} \oplus H(\text{SessionKey}, 0)$)。
$\text{DecryptedAmount} < \text{claimedAmount}$:用户少付。争议有效。结果: 如果争议有效:
$\text{claimedAmount}$。概述:
代理从已成功完成 $\text{Settlement}$ 阶段(且无争议)的通道中提取资金。
mapping(uint256 => bytes32) epochHashes:每个纪元的滚动哈希链摘要(在最终确定时累积)。mapping(address => mapping(uint256 => bool)) epochClaimed:跟踪代理是否已针对给定纪元声明过。纪元哈希链:
当通道最终确定(通过 finalizeSettlement())时,其数据会累积到当前纪元的滚动哈希中:
$$ entryHash = H(\text{channelId}, \text{merkleRoot}, \text{encryptedValuesHash}) $$ $$ epochHash = H(\text{epochHash_prev}, \text{entryHash}) $$
其中 $$encryptedValuesHash = \text{keccak256}(\text{abi.encode}(\text{encryptedAmounts}, \text{encryptedInfos}))$$ 是在 initiateSettlement() 期间计算的加密结算数据的承诺。将 encryptedValuesHash 包含在纪元哈希条目中,将加密数据绑定到链上纪元哈希,防止代理在声明期间提供伪造的加密数据。
流程:
epochHash 中。$P_{\text{claim}}$)以一次性提取纪元中多个通道的资金。实现说明: 在 TEE 路径中,
TeeAgentClaim在安全区内验证纪元哈希 重建和每项声明检查,然后签署结果。TeeVerifier合约检查 TEE 签名,而未来的 ZK 验证器 将验证 SNARKs。
$P_{\text{claim}}$) - 验证例程:该证明汇总了跨通道列表的有效声明。验证器(TEE 或 ZK 电路)接收所有纪元条目以重建纪元哈希,以及代理拥有资金的通道的声明数据。
私有输入(每个纪元条目):
$\text{channelId}$,$\text{merkleRoot}$:通道结算状态。$\text{encryptedAmounts}[]$,$\text{encryptedInfos}[]$:此通道的完整加密结算数据。claim(可选):仅存在于代理有资金的通道:
$\text{claimAmount}$:代理声明的金额。balanceProof:代理叶子的 Merkle 包含证明(包含 $\text{sessionKey}$,$\text{agentId}$,$\text{allowance}$)。$\text{paymentSignature}$:用户对 $(\text{channelId}, \text{cumulativeAmount})$ 的 EIP-191 签名。公共输入:
$\text{AgentAddress}$:声明代理(左填充至 32 字节)。$\text{TotalClaimAmount}$:所有单个声明金额的总和。$\text{EpochHash}$:链上纪元哈希摘要。验证逻辑:
纪元哈希重建:
$$encryptedValuesHash = \text{keccak256}(\text{abi.encode}(\text{encryptedAmounts}, \text{encryptedInfos}))$$。$(\text{channelId}, \text{merkleRoot}, \text{encryptedValuesHash})$ 馈入哈希链。$\text{EpochHash}$ 匹配。通道排序:
$\text{channelIds}$ 严格递增(防止重复声明)。每项声明验证(对于每个带有声明的条目):
$\text{balanceProof.leaf.agentId} == \text{AgentAddress}$。$\text{balanceProof}$ 与条目的 $\text{merkleRoot}$。$\text{claimAmount} \le \text{balanceProof.leaf.allowance}$。$\text{leafIndex}$(代理在树中的位置)。$amount = \text{encryptedAmounts}[\text{leafIndex}] \oplus H(\text{sessionKey}, 0)$。$\text{decryptedAmount} \ge \text{claimAmount}$。$\text{totalClaimed} += \text{claimAmount}$。总余额:断言 $\text{totalClaimed} == \text{TotalClaimAmount}$。
链上验证:
$epochNumber < \text{currentEpoch}$。$\text{epochHashes}[\text{epochNumber}] \ne 0$(纪元有已结算的通道)。$\text{epochClaimed}[\text{agentAddress}][\text{epochNumber}] == \text{false}$。$P_{\text{claim}}$(公共输入:$\text{agentAddress}$,$\text{totalClaimAmount}$,$\text{epochHash}$)。$\text{totalClaimAmount}$ 转账给 $\text{agentAddress}$。$\text{epochClaimed}[\text{agentAddress}][\text{epochNumber}] = \text{true}$。旁注: 增量提款(未来扩展)。 通过跟踪每个代理的状态,允许无需等待纪元提款是可行的。
权衡:增加了状态增长/存储成本,但改善了代理的流动性。
此表将正式协议概念映射到它们在 Rust 和 Solidity 中的实现。
| 规范概念 | Rust | Solidity |
|---|---|---|
| Merkle 根 ($M_c$) | BalanceTree<H>.root() |
channels[id].merkleRoot |
| 叶子 = H(会话密钥, 地址, 最大花费) | BalanceEntry + Hasher::hash() |
setupChannel() input |
$P_{\text{alloc}}$ (设置证明) |
TeeUserSetup<H> |
IVerifier.verify() via setup flow |
$P_{\text{settle}}$ (结算证明) |
TeeSettlement<H> |
initiateSettlement() |
$P_{\text{claim}}$ (代理提款证明) |
TeeAgentClaim |
claim() |
| 加密金额 | BlindingScheme::blind() |
IBlindingScheme.unblind() |
| 通道结构体 | Channel<P> in user crate |
Channel struct in PrivateX402.sol |
| 支付回执 | PaymentReceipt in common |
EIP-191 ecrecover |
| 纪元哈希链 | HashChain<H> |
epochHashes mapping |
| 会话密钥 | session_key: [u8; 32] |
Blinding factors |
TEE 与 ZK 路径:当前的 MVP 使用 TEE (Trusted Execution Environment) 作为信任后端。 TEE 签署证明输出(选择器 + ECDSA 签名)而不是生成 ZK 证明。
TeeVerifier验证来自注册 TEE 密钥的 ECDSA 签名,而未来的 ZK 验证器 将验证 SNARKs。
- 原文链接: github.com/ConorNethermi...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!