来源:登链社区
在这篇博客文章中,我们将带你了解 EigenLayer 协议的演变,介绍 EigenLayer 架构是如何从最初的概念中产生的。
本博客受 David Philipson 的 账户抽象系列[5] 启发。特别感谢社区的 Noam Horowitz[6]、Shiva[7]、NashQ[8]、Mike Neuder[9] 和 Sina[10] 对本文的评论和反馈。
虽然很多人熟悉 restaking 和 EigenLayer 这些术语,但只有少数人知道我们的核心合约包含数千行代码[11] ,其架构如下。
EigenLayer 简化架构
一个看似简单的想法为何变得如此复杂?
在这篇博客文章中,我们将通过介绍 EigenLayer 当前复杂架构是如何从最初概念中产生的,来带你了解该协议的演变。
本文的目标读者是对智能合约有基本了解并听说过 EigenLayer 或 restaking 的人。
由于本博客文章的目的是对 EigenLayer 的设计演变进行高层次解释,因此接口、变量和逻辑可能与当前 EigenLayer 核心合约[12]有所不同。
现在,让我们开始吧。
最终目标:使构建基础设施变得简单
首先,让我们先介绍 EigenLayer 要解决的问题。如果你已经熟悉这部分内容,请跳到后面的章节。
在以太坊上构建去中心化基础设施的开发人员面临着建立自己的经济安全的挑战。虽然以太坊为智能合约协议提供了经济安全,但桥或排序器等基础设施需要自己的经济安全,以实现节点的分布式网络达成共识。
共识机制对于促进这些节点之间的互动至关重要,无论是 L1、预言机网络还是桥。
工作量证明[13]耗能严重, 权威证明[14]过于集中化,因此权益证明[15] (PoS)已成为大多数基础设施项目的主要共识机制。
然而,启动新的 PoS 网络很困难。
首先,很难确定质押者(提供质押的人)在哪里。没有一个好的地方供开发人员找到质押者。
其次,质押者必须投入大量资金来获得新网络的质押,通常是通过购买网络的原生代币,他们通常是波动的且难以获得。
第三,质押者必须放弃其他的奖励机会,比如以太坊提供的 5% 奖励。
最后,当前的安全模型不理想,因为破坏任何 dApp 的成本只是破坏其最脆弱的基础设施依赖所需的成本。
暂时假设参与基础设施项目的质押者也负责操作离线软件以保证其安全。但我们将在文章后面更改这一假设。
EigenLayer 被创建来解决这些问题:
EigenLayer 的白皮书[16]对这些问题进行了深入探讨。
EigenLayer 泛化了提供经济安全的概念。
目标 1:创建连接质押者和基础设施开发人员的平台
EigenLayer 是一个平台,质押者可以为任何基础设施项目提供质押,基础设施项目可以在 EigenLayer 上向潜在质押者推介。该平台的支柱是使质押者能够为不同的基础设施做出可信承诺。
这些承诺适用于所有权益证明系统,不仅仅是 EigenLayer。L1 质押者通常承诺遵循协议规则,并冒着如果在同一区块高度签署冲突区块而失去质押的风险。
基础设施开发人员构建基础设施逻辑和软件,而质押者提供质押以保障基础设施。_这个 质押 作为 承诺 提供给基础设施的用户。_ 这个 承诺 是为了协议的顺利运行,反对特定的不端行为。
从概念上,当一个项目背后有 1 亿美元的质押时,这意味着如果它偏离了其承诺并表现出恶意行为,_这 1 亿美元的一部分将被削减_。简单来说,“削减”可以理解为销毁这笔资金。
这个数字越高,就越能为其用户提供更多的安全性和保障。
如果我们允许质押者为各种承诺分配质押,我们就可以在其上创建一个用户友好的平台。
我们需要一个无需信任且可编程的平台来执行不同的质押者承诺,以太坊是最适合的。 此外,以太坊持有最大的质押,有助于启动质押者市场。
这里的目标是,质押者应该能够通过 EigenLayer 在以太坊上为桥协议提供安全性。如果一个质押者在以太坊上恶意伪造消息并将其传输给 Gnosis,任何人都可以提交证据并削减该质押者。
由于质押者的质押和承诺执行发生在以太坊上,削减逻辑也是以以太坊智能合约的形式实现的。如果质押者违反了他们的承诺,任何人都可以向削减合约提供证据并没收恶意质押者的质押。
这构成了 EigenLayer 的基础 - 任何质押者都可以为任何基础设施协议做出可信承诺。
EigenLayer 的初始设计
现在,让我们尝试实现这一点。我们将从最简单的设计开始:质押者将代币质押到一个合约中,该合约包括一个函数,允许在提交证据并符合标准时削减质押者的代币。用户随后可以提取他们的余额。
其他质押者也可以将代币质押到这个合约中,增加基础设施的安全性。我们将称这个合约为 TokenPool
。
为了更清晰,这里对本文中使用的术语进行一些定义:
这个TokenPool
的接口可以表示如下:
contract TokenPool {
mapping(address => uint256) public balance;
mapping(address => address[]) public slasher;
function stake(uint256 amount) public;
function withdraw() public;
function enroll(address slasher) onlyOwner;
}
除了stake
、withdraw
和balance
函数和变量外,我们还引入了一个新函数和一个新变量。变量slasher
监视每个质押者当前注册的 AVS。函数enroll
允许质押者参与 AVS
因此,注册到 AVS 实际上是赋予“削减者”削减你的质押的能力。
通过这种修改,在向TokenPool
质押后,质押者可以通过调用enroll
函数加入特定的 AVS。该函数包含 特定 AVS 的削减者合约到slasher
映射中。
这里的 enroll 函数是受访问控制的,因为它可能使任何人都能够注册到任何削减者合约。为了确保这个 TokenPool 被安全管理,我们将假设它由一个受信任的第三方监督。
在提取过程中,TokenPool
合约可以要求每个slasher
确定质押者是否有资格提取。这通过slasher
合约中的isSlashed
函数进行验证。如果对于这个质押者,isSlashed
为TRUE
,则质押者无法提取他们的质押,因为他们已经被削减。
contract TokenPool {
mapping(address => uint256) public stakerBalance;
mapping(address => uint256) public operatorBalance;
mapping(address => address) public delegation;
mapping(address => address[]) public slasher;
function stake(uint256 amount) public;
function withdraw() public;
function delegateTo(address operator) public;
function enroll(address slasher) operatorOnly;
function exit(address slasher) operatorOnly;
}
我们已将余额变量分为两个不同的部分:一个用于运营者,一个用于质押者。此外,我们引入了一个delegation
映射,用于记录质押者和运营者之间的委托关系。
我们还对slasher
映射进行了微小修改。现在,它授予了slasher
合约削减运营者的权限,而不是质押者。
contract TokenPool {
mapping(address => uint256) public stakerBalance;
function stake(uint256 amount) public;
function withdraw() public;
}
TokenPool
合约将仅跟踪每个质押者的余额。AVS 的跟踪和加入将由DelegationManager
处理。
contract slasher {
mapping (address => bool) isSlashed;
function slash(address operator, ??? proof);
}
在清理合约结构并将每个组件模块化之后,架构现在如下所示:
EigenLayer 中间设计: 将运营者角色与质押者角色分离后。
目标:支持更多代币
到目前为止,我们开发的设计仅支持质押一个代币,因为我们只为质押者维护一个映射。
我们可以通过采用基于LP 份额的模型来解决这个问题,并创建特定于代币的TokenPool
。
为此,我们将创建一个名为TokenManager
的新合约。TokenManager
将是质押者质押和提款他们的代币的地方。
在TokenManager
下,每个代币将有一个TokenPool
。TokenManager
将充当所有代币的记账中心;它本身不会存储任何代币。每个TokenPool
将持有其自己对应的代币。
设计的新组件,现在可以跟踪质押者的不同代币。
当用户质押代币时,它会由TokenManager
处理。TokenManager
然后调用与该代币相关的相应TokenPool
的stake
函数。用户的代币将转移到TokenPool
。在函数结束时,totalShares
和stakerPoolShares
将被更新以反映新的质押。totalShares
跟踪TokenPool
发行的份额总数,而stakerPoolShares
记录每个TokenPool
中每个个体质押者持有的份额数量。
每个合约的接口将如下所示:
contract DelegationManager {
// ...
mapping(address => mapping(address => uint256)) operatorPoolShares;
// ...
}
现在,在相同的核心架构下,质押者可以将任何代币质押到 EigenLayer 以保护其他 AVS。
目标:扩展 AVS 设计
考虑以下情况:一个质押者在参与可削减行为后立即撤回他们的质押,在其他人削减他们的资产之前就进行撤回。
_例如_,假设有一个既是运营者又是质押者的运营者,在 AVS 中表现恶意。在任何其他人可以在链上削减之前,该运营者从 EigenLayer 合约中提取了他们的质押。这是可能的,因为在提款时,slasher
尚未更新以削减他们的资产。因此,AVS 不再能够削减恶意的运营者/质押者,因为没有更多的代币可以用于削减。
恶意运营者/质押者的潜在事件时间表。
因此,一个“安全”的 AVS 需要一个能够在发生事件的同一个区块中冻结恶意运营者的削减合约。这个限制极大地限制了 AVS 的设计,使大多数 AVS 都不安全。
一个解决方案是引入一个解绑期。我们不再允许质押者立即撤回他们的质押,而是在提款过程中引入一个称为解绑期的延迟。之后,质押者可以像以前一样提款。
当一个质押者决定从系统中提款时,他们的请求被放入一个队列。只有在运营者最长的解绑期到期后,这个排队的提款才会被处理。这是因为一个运营者可能管理多个 AVS,但一个质押者的提款只能与一个解绑期对齐。出于安全原因,系统将解绑期设置为最长的那个。
例如,如果一个质押者将他们的质押委托给参与三个 AVS 的运营者,这三个 AVS 的解绑期分别为六、五和七天,那么在从 EigenLayer 请求提款后,他们必须等待七天才能访问他们的质押。这是因为七天是这三个期限中最长的。
在七天期限结束后,质押者可以提款他们的质押。但是,如果在此期间他们委托的运营者被削减,那么待处理的提款也将被停止。
为了引入这一变化,DelegationManager
需要跟踪每个运营者的解绑期,并在运营者加入新的 AVS 时更新它。
contract TokenManager {
mapping(address => address) public tokenPoolRegistry;
mapping(address => mapping(address => uint256)) public stakerPoolShares;
mapping(address => uint256) public withdrawalCompleteTime;
function stakeToPool(address pool, uint256 amount) public;
function queueWithdrawal(address pool) public;
function completeWithdrawal(address pool) public;
}
当质押者排队提取时,TokenManager
将与 DelegationManager
验证质押者委托的操作员是否被 slash。如果操作员未被 slash,TokenManager
将根据 DelegationManager
的 unbondingPeriod
和当前时间更新质押者的 withdrawalCompleteTime
。
解除绑定期后,质押者可以通过 completeWithdrawal
完成其提取。该函数将验证 withdrawalCompleteTime
是否已过。如果已过,质押者的代币将按照之前的流程转出。
这个流程的设计复杂,并在我们的流程中经历了几次迭代。我们甚至可以就这个主题撰写一篇单独的文章!目前,我们使用一个时间跟踪系统来实现这种解绑跟踪。这部分仍在进行中!
模块化 slashers
由于我们正在使 slash 机制更安全,让我们也尝试使其更模块化和高效。
目前,在提取过程中,TokenManager
需要与每个单独的 slasher
进行检查,以查看操作员是否被 slash。这会给质押者增加 gas 开销,并可能显著降低较小质押者的奖励。
此外,由于 AVS 开发人员通常设计 slashers,将这个特定组件模块化可以简化各个 AVS 的开发流程。
与 TokenManager
类似,我们将为 slash 机制采用两部分设计。SlasherManager
维护每个操作员的状态。单独的 slasher
将处理每个 AVS 的 slash 逻辑。
更进一步模块化 slash 合约以减少质押者的 gas 成本。
contract slasher {
function slash(address operator, ??? proof) public;
}
slasher
将是特定于 AVS 的,很可能由 AVS 开发人员开发。它将与 SlasherManager
交互,以更新不同操作员的状态。
EigenLayer 已设计好!
回顾一下:EigenLayer 的目标是简化基础架构搭建。 我们从四个主要目标开始:
构建一个平台连接质押者和基础架构开发人员。
允许质押者使用任何代币提供经济安全性。
使质押者能够重新质押他们的质押,并在为其他基础架构提供安全性的同时赚取原生 ETH 奖励。
通过重新质押来池化安全性,而不是使其分散化。
经过几次迭代,我们开发了三个核心组件:TokenManager
、DelegationManager
和 SlasherManager
。每个组件都有特定的功能:
EigenLayer 的简化架构
TokenManager
:处理质押者的质押和提取。
DelegationManager
:允许操作员注册和跟踪操作员份额。
SlasherManager
:为 AVS 开发人员提供确定 slash 逻辑的接口。
这些核心组件还相互通信,以确保整个系统的安全性。
除了这些核心合约之外,还有许多其他功能和合约,增强整个堆栈。这些附加功能支持各种 AVS 设计,简化离线技术复杂性,并减少用户和操作员的 gas 费用。
要了解更多关于这些其他功能的信息,你可以访问我们的开源代码库:https://github.com/Layr-Labs/eigenlayer-contracts
附加 1:谁信任谁?
当系统是模块化的时候,跟踪协议中参与者之间的信任假设可能是具有挑战性的。 因此,明确概述协议中涉及的参与者之间的信任假设是至关重要的。
在 EigenLayer 中,有三个主要代理:质押者、操作员和 AVS 开发人员。
操作员依赖于 AVS 开发人员准确编写客户端软件和链上 slash 条件。如果 AVS 软件中存在 bug,最好的情况下,操作员可能会错过潜在的费用支付。最坏的情况下,操作员可能会因此被 slash 全部质押。
鉴于所涉及价值的重要性,确保整个系统在投入使用之前具有辅助训练轮是非常重要的。
否决委员会充当这些训练轮。它有权力撤销由于非恶意行为导致的 slash。否决委员会是质押者、操作员和 AVS 开发人员之间的相互信任方。
这样,对 AVS 开发人员的信任假设可以被移除。即使 AVS 中存在软件 bug,质押者和操作员也不会受到惩罚。
质押者信任他们委托的操作员。如果操作员行为不端,质押者可能会错过潜在的费用支付,甚至失去全部质押。这一信任假设与现有的验证服务(如币安质押和其他质押服务)相同。
AVS 开发人员依赖于操作员诚实操作。如果操作员不诚实,AVS 服务将显著下降,导致客户流失和其他后果。
在参与者之间,通过否决委员会,信任假设如下:
质押者信任操作员诚实行事,不端行为可能导致 slash。
AVS 开发人员信任操作员诚实操作 AVS 软件。
质押者、操作员和 AVS 开发人员信任否决委员会来撤销 slash。
附加 2:原生 Restaking
到目前为止,我们已经讨论了使用 LST 进行重新质押。但是,如果你不想通过流动质押协议对 EigenLayer 进行质押,你可以通过原生重新质押开始参与 EigenLayer。
让我们定义原生重新质押:这是使用验证器内的 ETH 进行额外承诺的过程。如果验证器偏离承诺,它们将失去其验证器内持有的 ETH。
这里的挑战在于,这些验证器内的 ETH 不是以 ERC20 代币的形式表示。相反,ETH 存在于信标链上。如果你对执行层或共识层(信标链)不熟悉, 这篇解释[17]是一个让你快速了解的好资源。
为了解决这个问题,我们可以使用 EigenPod
来跟踪以太坊验证器余额,并在必要时 slash 它们。
EigenPod
充当虚拟记账系统。通过 EigenPod
,我们可以监视每个重新质押验证器的 ETH 余额。
在高层次上,EigenPods
处理验证器的提取过程。当验证器从 EigenLayer 提取其质押时,ETH 首先通过 EigenPod
,以检查验证器是否已被 slash。如果验证器已被 slash,代币将在 EigenPod
合约内冻结,有效地 slash 它们。
实现 EigenPod
实现 EigenPod
是棘手的,因为以太坊验证器余额存储在信标链上,我们无法访问执行层上的信标链数据。
为了解决这个问题,我们利用一个预言机将信标链状态根传递到执行层。通过获取信标状态根,我们可以通过提供相应的默克尔证明来访问验证者的余额。
有了 EIP-4788[18] 的实施,我们可以移除这个预言机,并直接从执行层查询信标根。
为了封装记账系统,我们将采用类似TokenPool
和TokenManager
模型的模式,来模块化本地的再质押系统。每个EigenPod
将处理一个验证者的提款过程。EigenPodManager
将与其他核心合约协调,以跟踪每个操作员和质押者再质押的以太币数量。
<span ) 10px 10px / 40px no-repeat ;height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;'>contract EigenPodManager{
mapping(address => uint256) public restakerShares;
function createEigenPod(address owner) public;
function stakeToPod(address pod, uint256 amount) public;
function withdrawFromPod(address pod) public;
}
contract EigenPod{
address BEACON_CHAIN_ORACLE;
address podOwner;
uint256 restakedAmount;
function stake(uint256 amount) public;
function verifyRestakedBalance(uint256 amount, MerkleProof proof) public;
function withdraw() public;
}
EigenPodManager
跟踪每个质押者拥有的份额数量。它允许质押者创建EigenPod
,对其进行质押,并从中提款。
EigenPod
通过restakedBalance
变量跟踪信标链上各个验证者的余额。每当任何再质押验证者的余额发生变化时,任何人都可以通过调用verifyRestakedBalance()
函数来更新该特定验证者的余额。该函数将通过我们从BEACON_CHAIN_ORACLE
获取的信标状态根来检查更新后的余额是否正确。
这就是 EigenLayer 如何实现本地再质押。
参考资料
[1]登链翻译计划: https://github.com/lbc-team/Pioneer
[2]翻译小组: https://learnblockchain.cn/people/412
[3]Tiny 熊: https://learnblockchain.cn/people/15
[4]learnblockchain.cn/article…: https://learnblockchain.cn/article/7657
[5]账户抽象系列: https://learnblockchain.cn/article/5426
[6]Noam Horowitz: https://twitter.com/ProbablyNoam
[7]Shiva: https://twitter.com/ShivanshuMadan
[8]NashQ: https://twitter.com/NashQueue
[9]Mike Neuder: https://twitter.com/mikeneuder
[10]Sina: https://twitter.com/sina_eth_
[11]代码: https://github.com/Layr-Labs/eigenlayer-contracts/tree/master/src/contracts
[12]合约: https://github.com/Layr-Labs/eigenlayer-contracts/tree/master/src/contracts
[13]工作量证明: https://en.wikipedia.org/wiki/Proof_of_work
[14]权威证明: https://en.wikipedia.org/wiki/Proof_of_authority
[15]权益证明: https://en.wikipedia.org/wiki/Proof_of_stake
[16]白皮书: https://docs.eigenlayer.xyz/overview/whitepaper
[17]这篇解释: https://docs.prylabs.network/docs/concepts/nodes-networks
[18]EIP-4788: https://eips.ethereum.org/EIPS/eip-4788
[19]DeCert.me: https://decert.me/