本文设计了一种基本的跨rollup捆绑方案,确保多方交易的原子性,强调签名操作仅为O(n)复杂度。尽管方案较为简单,但在解决交易执行的原子性问题时仍面临挑战,最终指出这一技术对用户并无太大价值,主要用于MEV提取。
阅读我关于 共享排序器 的文章。
很多人谈论共享排序器允许事务的原子性包含。然而,这需要对所有组件 rollup 进行协议更改。这些协议更改尚未明确说明,而且构想复杂并且奇怪。
我怀疑共享排序器行业的人们只是让它显得复杂得像是一个神秘的东西。因此,我决定坐下来设计一个基本的跨 rollup 打包方案。
幸运的是,我们有关于任意打包方案的先前艺术,所以我们知道要避免什么,通常如何构建它们。事实证明,修改每个 rollup 的过滤功能是很容易的。
应允许来自多个 EOAs 的多笔事务。
应该不可能打破一个打包(包括一个组件事务而不包括同一序列中的所有其他事务)。
不需要共享的 TX 格式。
签名应该便宜(在最坏情况下在事务数量上为 O(n)
,参见 二次签名哈希问题)
在所有情况下省略签名。
在事务中包括链 ID 或其他域绑定。
没有其他修改
对于每个组件 rollup,拥有一个数据结构,将 EOA 地址映射到来自该地址的事务列表。事务应该被序列化并表示为不透明的字节。签名者不需要理解内容。
struct Bundle {
arbitrum: Map<Address, Vec<OpaqueBytes>>,
optimism: Map<Address, Vec<OpaqueBytes>>,
}
我们对所有事务进行迭代,为 rollup 和发送者插入分隔符。这确保了一个事务绑定到特定的 rollup 和特定的 EOA,并且不会被误解。它还确保了 rollup 和 EOA 信息暴露给所有观察者,而不需要观察者理解事务结构。
以这种方式进行签名也确保所有签名都承诺于所有事务。这使得打包不可修改。它既不能延展,也不能解打包。
fn signing_hash(&self) -> [u8;32] {
let mut hasher = Keccak::new();
hasher.update(b"arb");
for (from, txns) in self.arbitrum.iter() {
hasher.update(&from);
txns.for_each(|tx| hasher.update(tx.hash()));
}
hasher.update(b"opt");
for (from, txns) in self.optimism.iter() {
hasher.update(&from);
txns.for_each(|tx| hasher.update(tx.hash()));
}
hasher.finalize()
}
fn sign(&self, key: &SigningKey) -> Signature {
key.sign_raw(self.signing_hash())
}
如果你愿意,也可以使用一些额外的域绑定,我不在乎。
验证可以在不理解事务内容的情况下进行。
我们通过以下步骤进行验证:
计算打包的签名哈希
检查打包声明的每个 EOA 是否对签名哈希有签名
struct SignedBundle {
bundle: Bundle,
signatures: Vec<Signature>,
}
fn verify(bundle: &SignedBundle) -> Result<()> {
let signing_hash = bundle.bundle.signing_hash();
// 收集包含的签名者
let addresses = bundle
.signatures
.map(|sig| sig.recover_raw(signing_hash))
.collect::<Result<HashSet<_>>>();
// 收集声明的 EOA 发送者
let eoas = bundle.arb.keys()
.chain(bundle.opt.keys())
.collect::<HashSet<_>>();
// 检查这些集合是否相等
if eoas != addresses {
return Err("缺少签名或额外签名或其他问题")
}
Ok(())
}
通过这种方式,我们确保所有签名覆盖所有事务(如果添加、删除或替换事务,则签名将无效),并且没有任何事务未被签名覆盖。进行额外检查的费用也不高。
管它的,没什么大不了的。只需做成那样。
组件 rollups 必须过滤那些验证失败的打包。注意,过滤不需要对事务的内容有任何了解,只需要检查它们的签名。
这意味着带有不正确或不足签名的打包会被包含在宿主历史中,但在 rollup 历史中会被过滤。它们不会对 rollup 链产生任何影响。今天,主网 rollups 中的排序器输出也会进行同样的处理,当然了。
这实现了多方打包的原子性包含。多个 EOAs 可以包含事务。打包不能被拆分,因为所有签名承诺于所有事务。签名的费用为事务数量的 O(n)
。验证的费用为 O(n + s)
,其中 s
是签名者的数量。如果你愿意,可以使用可聚合的签名方案来稍微改变这种权衡。
好吧。目标实现了。
那么,这样做对我们而言有什么不足呢?熟悉我的过去博客文章和烦人的推特线程的精明读者们可能意识到,这样的打包方式无法实现原子执行。没错。因此,你实际上无法利用这个构建任何互操作性方案,而无需进一步的机制/协议变更来处理执行部分。提取 MEV 的区块构建者通过成为区块顶部获得执行。但是你,终端用户,并不能提取东西。你只会被提取。
所以,是的,构建一个打包方案很简单,但只是因为我们将问题缩小到几乎无用的程度。
总之,原子性包含是无聊、简单,并且除了 MEV 提取之外没有什么用处。不要大惊小怪。
- 原文链接: prestwich.substack.com/p...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!