ULTRA TX - 可编程区块:一个交易满足统一和可扩展的以太坊-Layer 2 的所有需求

ULTRA TX 是一种实现可编程 L1 区块的方法,它通过 L1 meta tx bundler 和Based Rollup 的区块构建器,创建一个包含单个强大 L1 交易的 ULTRA TX,从而实现 L1 和 L2 之间的高效组合与聚合,实现 L1 和 L2 之间的无缝交互和数据共享,并为跨链互操作提供支持。

image\ image1024×1024 185 KB

是什么

ULTRA TX 是一种实现可编程 L1 区块的方式,它解锁了远超在 L1 区块中使用标准 L1 交易的能力。你可以说 ULTRA TX 对于区块来说,就像 account abstraction 对于 EOA 来说一样。

本文将重点关注这对 L2 以及 L1 和 L2 之间的互操作性意味着什么。其他可能的用例将不在此处探讨。

当你将 L1 meta tx bundler 和 (based) rollup 区块构建器结合起来时,你就有了一个我们称之为 master builder 的实体。该 master builder 将构建仅包含单个极其强大且庞大的 L1 交易的 L1 区块。这个单独的交易此后将被称为 ULTRA TX。

这种设置使得为 L2 和 L1 进行组合和聚合变得高效且直接。

为了方便 L1 组合,ULTRA TX 应该位于 L1 区块的顶部,以便直接获得最新的 L1 状态。

这种方法不需要对 L1 进行任何更改。

为什么

  • 跨交易强制执行是一场噩梦,而且通常根本不可能。
  • 使用 EOA 交易执行 L1 → L2 (如存款)非常不方便,尤其是对于同步组合而言。请参阅下文了解原因。
  • 所有(或几乎所有) L1 交易都将是 account abstraction 交易。 EOA 交易受到限制,应逐渐消失。不仅仅是为了 UX 的改进,也是为了效率。现在,拥有一个基于智能合约的账户,并带有 EIP-7702 也只是签名的问题,因此用户可以轻松选择加入。
  • L2 想要一起提议/结算(以共享 blobs 和 proof aggregation),并且频繁地(理想情况下每个 L1 区块)以提高效率和 UX。
  • 一个更具可扩展性的 L1 为更好的 UX 铺平了道路
  • L1 上的一个 account abstraction tx bundler 是最有效的。
  • 适用于所有 (based) rollups 的一个区块构建器是最强大的。

由于这些原因,我相信我们正在走向一个几乎所有事情都将通过单个 L1 交易完成的未来。 ULTRA TX 可以逐步采用,区块的其余部分可以用传统方式构建。

结合实时证明(在某种合理的范围内),这可以实现理想的未来,即以太坊真正感觉像一条单一的链,其中 L1 和所有 L2 可以相互调用,并且每个 L2 都可以(但不需要)在每个 L1 区块中结算,通常不会损失效率。

展望未来,Gwyneth 经常会被提及,以便更具体地说明事情的实际运作方式。这仅仅是因为我最熟悉它。

优点

  • L1 和 L2 之间无缝且简单的聚合和交互: L1 和 L2 交易可以实际上以相同的方式使用共享 mempool 来构建区块。这消除了必须不同地处理 L1 交易的复杂性和效率考虑因素。请参阅下文了解如何实现这一点。
  • 共享数据压缩和将数据聚合到 blobs 中: 所有 rollup 数据都可以共享并一起存储在 blobs 中。
  • 原子性: 现在可以有可编程逻辑来跨交易强制执行某些操作。所有逻辑都可以使用智能合约来实现。
  • 可证明性: 整个 ULTRA TX 都可以轻松证明,因为所有输入都可直接获得。任何需要证明其发生的事件都取决于它是否已被证明,否则整个 ULTRA TX 将会回滚。与无法跨交易强制执行某些操作的普通交易相比,这是一个重大改进。
  • 效率: 任何需要证明的内容都可以通过单个证明来证明。存储或发送消息没有开销。具有返回值的 L1 → L2 调用是一个例外,但这些调用可以使用相对便宜的瞬态存储。
  • 访问最新的 L1 状态: ULTRA TX 位于区块的顶部非常重要,以便最新的 L1 状态可以直接在先前区块的区块头中获得。这避免了在 L1 区块中的某个随机点获取/确保最新 L1 状态的困难/低效(例如,不需要 EIP-7814 能够在交易边界上进行推理)。像 EIP-7862 中那样的延迟状态根计算不应有太大影响,因为区块仍然可以在新的 L1 区块进入后立即构建。但是,为了能够证明 L1 状态,证明者必须具有针对该状态根的所有已用状态的 Merkle 证明。
  • 预确认: 例如,网关仅需要区块顶部的 L1 “包含”预确认,才能提供 L1 和 L2 执行预确认。可以使用 一个小技巧 在链上检查交易是否是区块中的第一个交易:将交易 gas limit 设置为区块 gas limit 并检查:block.gaslimit - gasleft() - intrinsic_gas_cost < 21000。这使得即使在今天强制执行 L1 上的这一要求也没有问题。
  • 可自定义的安全性: Dapp/用户可以通过在 L1 或 L2 上执行交易来轻松选择其安全级别。 L1 交易仍然可以以完整的 L1 安全性执行,这些交易的安全性仅取决于在使用扩展功能时证明的有效性。
  • 逐步采用: 交易仍然可以放置在 ULTRA TX 之后。这允许逐步采用这种捆绑交易的新方式。这也可以用于限制需要在 12 秒内证明的工作量。随着证明者变得更快更强大,ULTRA TX 可以包含越来越多的交易。区块中也可以没有 ULTRA TX 。或者 ULTRA TX 仅包含 L2 交易。对于额外工作要么无利可图要么根本不可能的区块(例如,L1 验证器必须构建自己的区块),仍然可以像往常一样构建 L1 区块。

缺点

  • 与 L1 同步组合需要实时证明: 目前尚无法在 < 10 秒内用 zk 证明 L1 区块。预计 zk 证明者将在一年或两年内具备实时证明的能力。在此之前,TEEs 和 AVS 之类的东西是合理的解决方案,让我们得以一窥未来。
  • L1 组合的区块顶部要求: 区块顶部是最有价值的区块空间,因此此要求可能会带来问题。但是,L1 meta 交易现在也可以轻松地包含在 ULTRA TX 中,因此高价值的 L1 交易仍然可以首先包含,而没有任何问题(构建者只需要小心这些状态更改如何影响后续交易)。

当区块不需要与 L1 同步组合时,可以删除区块顶部的要求。当仅在链上检查依赖的 L1 状态以确定是否为最新值时,也可以删除此要求(然后可以使用回滚保护将其提交给 L1 构建者)。这确实会对效率产生影响,并且可以轻松在链上读取的 L1 状态也存在限制。

  • 区块构建器的复杂性增加: 为了充分利用此系统,需要具有高硬件要求的复杂区块构建器。对于今天的区块构建器来说,这通常已经为真,但这种方法在证明要求和能够运行 L2 节点或至少能够协调它们的构建以保持竞争力方面走得更远。
  • 始终需要 EVM 等效性: 需要能够以与 L1 上执行方式完全相同的方式在 L2 上证明/模拟 L1 交易。这要求整个系统需要在以太坊硬分叉时完全同步更新。由于证明者现在能够获取现有的执行客户端并证明相同的代码,因此这不太可能成为问题。
  • 以太坊生态系统选择加入: 需要为大多数 L1 区块构建具有 ULTRA TX 的 L1 区块,以便 L1 互操作性能够可靠地为用户工作。
  • L1 交易证明开销: 包含在 ULTRA TX 中的 L1 交易也需要证明。可以使用其他方法来避免这种情况,尽管它也可以节省链上消息传递开销。假设 10 秒(如果不是数百秒)的 L2 将完成大部分交易,那么与收益相比,仅证明 1 个额外链的交易似乎并不是一个显着的开销。
  • 用户选择加入智能合约帐户: 此设计不一定要求用户在 L1 上拥有智能合约帐户,尽管在实践中,这可能是唯一得到良好支持的流程。如果用户不关心增加的收益,他们仍然可以继续使用旧版交易。但是,他们可能不会被视为一等公民。

EOA L1 → L2 限制

使用 EOA 交易做好 L1 → L2 非常困难。你可以在 L1 上执行的操作非常有限:

  • 你将一个实际的 L2 交易作为 L1 交易的一部分提议。从理论上讲这是可行的,但问题在于,通常在不首先执行交易的情况下,不知道这些 L1 交易最终是否会提议一个 L2 交易。然后,此 L2 交易还必须以某种方式进入 L2 区块,而不是 L2 区块构建过程的一部分。当单个预确认应该拥有唯一的提议权时,这也会有问题。
  • 如果 L1 → L2 调用需要返回值,则还需要在交易中提供证明。需要提供额外的数据,用户必须将其作为交易的一部分进行签名,并且此数据也可能在交易最终上链之前过期。这会导致糟糕的 UX 。如果有许多 L1 → L2 交互,这也意味着必须在链上验证许多证明,这使得事情非常低效。
  • Gwyneth 允许在 L2 上启动 L1 工作。但是,如果不使用所有者签名的真实 L1 交易,则无法执行某些操作,例如从帐户中转移 ETH 。 Account abstraction 解决了这个问题,因为现在实际上可以使用 L1 交易和 L2 交易来支持所有帐户修改。

扩展 L1

image\ image1399×776 24 KB

可以通过在外部调用后面放置额外的功能来扩展 L1 功能(可能类似于本机 rollups 的可扩展方式)。对于 Gwyneth 而言,此调用是到 L2 的跨链调用。在构建和证明区块时,会创建该区块,就好像此额外功能也可在 L1 上使用一样。这些调用的输出会收集起来,并作为 ULTRA TX 的一部分发送到链上:

  • 在 L1 上调用的扩展程序生成的输出存储在 ExtensionOracle 合约中。这些值在作为 ULTRA TX 的一部分完成调用之前设置。 ExtensionOracle 只是一个简单的合约,它为 L1 实际上不支持的每个调用提供输出。此数据存储在瞬态存储中。
  • 现在我们可以实际执行调用。对扩展功能的每个调用都会检查该调用是否在其当前执行的环境中受支持:
    • 如果受支持,则调用会像往常一样发生。例如,该调用实际上是对目标合约进行的。这是构建器/证明器中遵循的路径。
    • 如果不受支持,则意味着该调用应改为重定向到 ExtensionOracle 智能合约,在该合约中将读取链下生成的调用输出。这是 L1 上遵循的路径。
  • 最后,验证证明,表明一切都按预期完成。

此额外数据由 master builder 生成和提供,而不是由用户生成和提供。用户无需签署任何其他数据或验证昂贵的证明。用户可以使用扩展功能以与用户与本机功能交互完全相同的方式与智能合约交互。

在其智能合约中使用扩展功能的开发人员也不必知道幕后实际发生的事情。

请注意,这种精确方法仅有效,因为 Gwyneth 可以“模拟” L1 交易的执行以将所有内容粘合在一起。在证明器中执行 L1 交易的方式与在提议 ULTRA TX 时在 L1 上执行的方式相同。这对于确保使用正确的输入来生成输出非常重要。

(同步)可组合性

image\ image1352×733 15.3 KB

我将再次使用 Gwyneth 的同步可组合性方法作为示例(你可以在 此处 快速阅读相关内容,也可以在 此处此处此处 阅读)。简而言之,在 L2 上添加了一个额外的预编译,允许为外部调用切换链。 Gwyneth 还可以模拟所有 L1 交易,然后在之后将状态更新应用回 L1 。

我在这里要做的假设是,每个 L1 帐户都是一个智能合约帐户,并且所有 L2 都是基于的(多么好的假设!)。

现在可以轻松地完成构建区块,如下所示:

  • 我们从每个链的后状态开始(包括 L1)。 L1 和 L2 交易之间没有区别(除非 L1 交易应该是 meta 交易,否则它们会在ULTRA TX 之后添加到 L1 区块)。
  • L1/L2 交易以构建者希望的任何顺序执行。
  • 对于 L1 交易,修改 EVM 执行,使 XCALLOPTIONS 预编译的工作方式与 L2 上完全相同(即,它实际上在目标 L2 上执行调用),如上面的扩展 L1 部分中所述。这允许 L1 交易调用 L2 ,这是我们需要支持真正同步可组合性的。
  • 对于任何 L1 → L2 调用,我们记录该调用的相应输出。
  • 一旦构建者在本地执行了所有必需的交易,我们就可以密封区块:
    • 对于 L2 ,无论交易还是状态增量都放置在 L1 上以实现数据可用性。这是为每个 L2 独立完成的,因此它们没有任何相互依赖性。区块哈希可以聚合到链上以节省 gas 。
    • 对于 L1 ,我们需要按预期顺序在链上应用所有状态更改,因为它们发生在构建器中。对于 Gwyneth ,这意味着按正确的顺序应用 L1 交易和 L1 状态增量(对于从 L2 完成的 L1 状态更改)。

可以根据需要重复构建过程任意次数以生成任意数量的区块。这对于支持以快于 L1 区块时间包含的交易的执行预确认非常重要。并行化区块构建也很重要(请参阅下文)。

最后,生成整个过程的单个证明(请注意,这可能包含子证明,请参阅下面的并行化部分)。

ULTRA TX 最终在链上提议。交易的所有输入都用作证明的输入,并且证明会得到验证。如果证明无效,则整个交易都会回滚。

请注意,构建者必须支持才能正确包含交易的任何其他要求都可以作为交易元数据的一部分。这样,构建者可以轻松查看他们是否能够包含该交易,而无需执行该交易。可以在链上强制执行这些规则。例如,对于跨链交易, meta tx 将包含允许访问的链的列表。如果此列表不完整,则允许该交易回滚,构建者将获得交易费用。

并行化

上述简化流程是严格按顺序进行的,以便所有链可以以最简单的方式自由且同步地相互交互。如果一组链不需要彼此同步,也可以并行地为它们构建区块。可以以几乎相同的效率提交多个区块。这允许打破顺序瓶颈并实现更高的吞吐量。

即使,例如,一个链具有 L1 状态依赖性,也可以并行地构建区块。只有该区块中使用的状态子集才是重要的,以便该区块有效的实际最新值。

在这些区块之上可能有一个额外的层跟踪全局状态,而每个区块仅直接依赖于此子状态。每个区块都单独证明,然后在之后与附加状态检查聚合在一起。聚合证明将跟踪跨区块的全局最新状态,并将检查区块中使用的本地状态是否与当前的最新全局状态匹配。构建者只需要确保在并行构建区块时这些假设成立。

泛化

关于如何将此(以及更多内容)用于所有 rollups (不仅仅是 Gwyneth rollups )的泛化将在第 2 部分中介绍。此框架将被称为 GLUE 。之前的草图已在 此处 完成。它将包含在合理深度上的链下和链上必要接口,以使所有 L1 扩展程序都可以使用所提出的设计。

代码

一些代码使事情更具体和有趣。为了简洁起见,省略了一些细节。

ULTRA TX 在链上的样子:

// 语言:自动检测

function proposeBlock(BlockMetadata[] calldata blocks) external payable {
    for (uint i = 0; i &lt; blocks.length; i++) {
        _proposeBlock(blocks[i]);
    }
    _prove(blocks);
}

function _proposeBlock(BlockMetadata calldata _block) private {
    for (uint i = 0; i &lt; _block.l1Block.transactions.length; i++) {
        Transaction calldata _tx = _block.l1Block.transactions[i];
        for (uint j = 0; j &lt; _tx.calls.length; j++) {
            Call calldata call = _tx.calls[j];
            // 在 ExtensionOracle 中设置返回数据
            if (call.returnData.length > 0) {
                (bool success, bytes memory result) = address(extensionOracle).call(abi.encode(call.returnData));
                require(success == true, "call to extension oracle failed"); // 调用扩展 oracle 失败
            }
            // L1 account abstraction 调用
            _tx.addr.call{value: call.value}(call.data);
        }
        // 如果有必要,应用 L1 状态差异
        if (_tx.slots.length > 0) {
            GwynethContract(_tx.addr).applyStateDelta(_tx.slots);
        }
    }
}

对于想要利用扩展功能的开发人员来说,它是这样的:

// 语言:scss

using EVM for address;

function xTransfer(uint256 fromChain, uint256 toChain, address to, uint256 value) public returns (uint256) {
    return on(fromChain)._xTransfer(msg.sender, toChain, to, value);
}

function ChainAddress(uint256 chainId, xERC20 contractAddr) internal view returns (xERC20) {
    return xERC20(address(contractAddr).onChain(chainId));
}

function on(uint256 chainId) internal view returns (xERC20) {
    return ChainAddress(chainId, this);
}

如何将扩展程序公开给开发人员:

// 语言:C#

library EVM {
    function xCallOptions(uint chainID) public view returns (bool)  {
        // 调用自定义预编译
        bytes memory input = abi.encodePacked(version, chainID);
        (bool success, bytes memory result) = xCallOptionsAddress.staticcall(input);
        return success && bytes4(result) == xCallOptionsMagic;
    }

    function onChain(address addr, uint chainID) internal view returns (address) {
        bool xCallOptionsAvailable = xCallOptions(chainID, false);
        if (xCallOptionsAvailable) {
            return addr;
        } else {
            return extensionOracle;
        }
    }
}

Extension Oracle 的样子:

// 语言:kotlin

contract ExtensionOracle {
    uint private transient returndataCounter;
    ReturnData[] private transient returndata;

    fallback() external payable {
        _returnData();
    }
    receive() external payable {
       _returnData();
    }

    function _returnData() internal {
        if (msg.sender == gwyneth) {
            returndata = abi.decode(msg.data, (GwynethData.ReturnData[]));
        } else {
            require(returndataCounter &lt; returndata.length, "invalid call pattern"); // 无效的调用模式
            ReturnData memory returnData = returndata[returndataCounter++];
            bytes memory data = returnData.data;
            if (returnData.isRevert) {
                assembly {
                    revert(add(data, 32), mload(data))
                }
            } else {
                assembly {
                    return(add(data, 32), mload(data))
                }
            }
        }
    }
}
  • 原文链接: ethresear.ch/t/ultra-tx-...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
以太坊中文
以太坊中文
以太坊中文, 用中文传播以太坊的最新进展