EigenLayer AllocationManager:分配管理器

  • Layr-Labs
  • 发布于 2025-05-30 23:14
  • 阅读 34

本文档详细介绍了 EigenLayer 的 AllocationManager 合约,该合约负责管理 AVS 元数据注册、Operator Set 的注册与注销、处理 Operator 可 Slash 权益的分配与 Slash,以及作为 AVS Slash Operator 的入口点。

AllocationManager(分配管理器)

文件 备注
AllocationManager.sol
AllocationManagerStorage.sol 状态变量
IAllocationManager.sol 接口

库和 Mixin:

文件 备注
PermissionControllerMixin.sol 账户委托
Deprecated_OwnableUpgradeable 已弃用的 ownable 逻辑
Pausable.sol
SlashingLib.sol slashing 数学
OperatorSetLib.sol 编码/解码 operator sets
Snapshots.sol 历史状态

预先阅读

概述

AllocationManager 管理 AVS 元数据注册,operator 向 operator sets 的注册和注销, 处理 operators 的可 slash stake 的分配和 slashing,并且是 AVS 用于 slash operator 的入口点。在 AllocationManager 的上下文中,AVS 被定义为实现 ServiceManagerBase 的合约的 address。出于 PermissionController 的目的,此 AVS 地址也是 AVS account

AllocationManager 的职责分为以下几个概念:

参数化

  • ALLOCATION_CONFIGURATION_DELAY:分配生效前的区块延迟。
    • 主网: 126000 blocks (17.5 天)。
    • 测试网: 75 blocks (15 分钟)。
  • DEALLOCATION_DELAY:取消分配生效前的区块延迟。
    • 主网: 100800 blocks (14 天)。
    • 测试网: 50 blocks (10 分钟)。

AVS 元数据

AVS 必须注册其元数据以声明自己的身份,这是第一步,然后才能创建 operator sets 或将 operators 注册到 operator sets 中。AllocationManager 跟踪已注册元数据的 AVS。

方法:

updateAVSMetadataURI
/**
 *  @notice 由 AVS 调用以发出一个 `AVSMetadataURIUpdated` 事件,指示信息已更新。
 *
 *  @param metadataURI 与 AVS 关联的元数据的 URI。
 *
 *  @dev 请注意,`metadataURI` **从不存储**,仅在 `AVSMetadataURIUpdated` 事件中发出。
 */
function updateAVSMetadataURI(
    address avs, 
    string calldata metadataURI
) 
    external
    checkCanCall(avs)

注意:此方法可以直接由 AVS 调用,也可以由 AVS 授权的调用者调用。 有关详细信息,请参见 PermissionController.md

调用此函数有效地将 AVS 地址“写入”核心协议。

以下是 AVS 最初更新其元数据 URI 时应使用的格式。 这不会在链上进行验证。

{
    "name": "AVS",
    "website": "https.avs.xyz/",
    "description": "Some description about",
    "logo": "http://github.com/logo.png",
    "twitter": "https://twitter.com/avs",
}

稍后,一旦 AVS 创建了 operator sets,就可以随后更新其元数据 URI 中的内容。

{
    "name": "AVS",
    "website": "https.avs.xyz/",
    "description": "Some description about",
    "logo": "http://github.com/logo.png",
    "twitter": "https://twitter.com/avs",
    "operatorSets": [
        {
            "name": "ETH Set",
            "id": "1", // 注意:我们使用此参数来匹配 Allocation Manager 中的 opSet id
            "description": "AVS 的 ETH operatorSet",
            "software": [
                {
                    "name": "NetworkMonitor",
                    "description": "",
                    "url": "https://link-to-binary-or-github.com"
                },
                {
                    "name": "ValidatorClient",
                    "description": "",
                    "url": "https://link-to-binary-or-github.com"
                }
            ],
            "slashingConditions": ["Condition A", "Condition B"]
        },
        {
            "name": "EIGEN Set",
            "id": "2", // 注意:我们使用此参数来匹配 Allocation Manager 中的 opSet id
            "description": "AVS 的 EIGEN operatorSet",
            "software": [
                {
                    "name": "NetworkMonitor",
                    "description": "",
                    "url": "https://link-to-binary-or-github.com"
                },
                {
                    "name": "ValidatorClient",
                    "description": "",
                    "url": "https://link-to-binary-or-github.com"
                }
            ],
            "slashingConditions": ["Condition A", "Condition B"]
        }
    ]
}

效果

  • 发出一个 AVSMetadataURIUpdated 事件,供链下服务使用

要求

  • 调用者必须经过授权,无论是作为 AVS 本身还是作为管理员/被任命者(请参见 PermissionController.md

Operator Sets

ELIP-002 中所述,Operator sets 对于 AVS 配置 operator 分组非常有用,这些分组可以分配不同的任务,根据其策略分配获得奖励,并根据不同的规则进行 slash。 Operator sets 在 libraries/OperatorSetLib.sol 中定义:

/**
 * @notice 由 AVS 地址和标识符标识的 operator 集
 * @param avs 此 operator 集所属的 AVS 的地址
 * @param id operator 集的唯一标识符
 */
struct OperatorSet {
    address avs;
    uint32 id;
}

AllocationManager 在以下映射中跟踪 operator sets 和 operator sets 成员:

/// @dev 列出 AVS 已创建的 operator set ids
mapping(address avs => EnumerableSet.UintSet) internal _operatorSets;

/// @dev 列出 AVS operator 集的成员
mapping(bytes32 operatorSetKey => EnumerableSet.AddressSet) internal _operatorSetMembers;

每个 OperatorSet 对应于单个 AVS,如 avs 参数所示。 创建时,AVS 提供一个 id(对该AVS是唯一的),以及 OperatorSet 包括的 strategies 列表。 avsid 一起构成唯一标识给定 OperatorSetkey 。 Operators 可以注册和注销 operator sets。 结合分配可 slash 的 magnitude, operator set 注册构成了 operator slash 能力的基础(在分配和 Slashing中进一步讨论)。 OperatorSets 有两种类型,重新分配的和非重新分配的。

概念:

方法:

注册状态

Operator 注册和注销在以下状态变量中跟踪:

/// @dev 列出 operator 注册的 operator sets。 请注意,operator
/// 可以注册而没有分配的 stake。 同样,operator 可以分配
/// 而无需注册。
mapping(address operator => EnumerableSet.Bytes32Set) internal registeredSets;

/**
 * @notice 包含 operator 针对 operator set 的注册详细信息
 * @param registered operator 当前是否注册了 operator set
 * @param slashableUntil 如果 operator 未注册,则在达到
 * 此区块之前仍然可以 slash。
 */
struct RegistrationStatus {
    bool registered;
    uint32 slashableUntil;
}

/// @dev 包含 operator 针对 operator set 的注册状态。
mapping(address operator => mapping(bytes32 operatorSetKey => RegistrationStatus)) internal registrationStatus;

对于每个 operator,registeredSets 保留了 operator 当前注册的 OperatorSet keys 列表。 每个 operator 注册和注销分别添加和删除给定 operator 的相关 key。 注册中的另一个因素是 operator 的 RegistrationStatus

RegistrationStatus.slashableUntil 值用于确保 operator 在启动注销后的一段时间内保持可 slash 状态。 这是为了防止 operator 犯下可 slash 的罪行并立即注销以避免处罚。 这意味着当 operator 从 operator set 注销时,他们的 RegistrationStatus.slashableUntil 值设置为 block.number + DEALLOCATION_DELAY

createOperatorSets
/**
 * @notice AVS 用于创建新 operator sets 的参数
 * @param operatorSetId 要创建的 operator set 的 id
 * @param strategies 要添加为可 slash 到 operator set 的 strategies
 */
struct CreateSetParams {
    uint32 operatorSetId;
    IStrategy[] strategies;
}

/**
 * @notice 允许 AVS 创建新的 operator sets,定义 operator 集使用的 strategies
 */
function createOperatorSets(
    address avs,
    CreateSetParams[] calldata params
)
    external
    checkCanCall(avs)

注意:此方法可以直接由 AVS 调用,也可以由 AVS 授权的调用者调用。 有关详细信息,请参见 PermissionController.md

AVS 使用此方法来创建新的 operator sets 。 AVS 可以根据自己的需求创建任意数量的 operator sets 。 创建后, operators 可以分配可 slash 的 stake 到注册到 这些 operator sets 。 redistributionRecipientDEFAULT_BURN_ADDRESS,被 slash 的资金将发送到那里。

创建时,avs 指定一个对 AVS 唯一的 operatorSetIdavs 地址和 operatorSetId 一起创建一个 key,该 key 在整个 AllocationManager 中唯一标识此 operator set 。

可选地, avs 可以提供一个 strategies 列表,指定哪些 strategies 将可以为新的 operator set 进行 slash 。 AVS 可以根据自己的需求创建具有各种 strategies 的 operator sets —— 并且 strategies 可以添加到多个 operator sets 中。

效果

  • 对于每个 CreateSetParams 元素:
    • 对于每个 params.strategies 元素:
      • strategy添加到 _operatorSetStrategies[operatorSetKey]
      • 发出 StrategyAddedToOperatorSet 事件

要求

  • 调用者必须经过授权,无论是作为 AVS 本身还是作为管理员/被任命者(请参见 PermissionController.md
  • AVS 必须通过调用 updateAVSMetadataURI 注册元数据
  • 对于每个 CreateSetParams 元素:
    • 每个 params.operatorSetId 均不得已存在于 _operatorSets[avs]
createRedistributingOperatorSets
/**
 * @notice 允许 AVS 创建新的正在重新分配的 operator sets,定义 operator set 使用的 strategies 和 redistribution recipient
 */
function createRedistributingOperatorSets(
    address avs,
    CreateSetParams[] calldata params,
    address[] calldata redistributionRecipients
)
    external
    checkCanCall(avs)

AVS 使用此方法来创建新的 redistributing operatorSets。 与之前的函数不同,此 operatorSet 的被 slash 的资金将发送到 redistributionRecipient。 此值仅在创建时设置一次。 请注意, redistributing operatorSets 可能没有 Native ETH,因为该协议不支持 native eth redistribution。 有关其他背景信息,请参见 ELIP-006

效果

  • 对于每个 CreateSetParams 元素:
    • 对于每个 params.strategies 元素:
      • strategy 添加到 _operatorSetStrategies[operatorSetKey]
      • 发出 StrategyAddedToOperatorSet 事件
    • 设置 operatorSet 的 redistributionRecipient
      • 发出 RedistributionAddressSet

要求

  • 调用者必须经过授权,无论是作为 AVS 本身还是作为管理员/被任命者(请参见 PermissionController.md
  • AVS 必须通过调用 updateAVSMetadataURI 注册元数据
  • redistributionRecipient 不得为 0 地址
  • 对于每个 CreateSetParams 元素:
    • 每个 params.operatorSetId 均不得已存在于 _operatorSets[avs]
addStrategiesToOperatorSet
/**
 * @notice 允许 AVS 向 operator set 添加 strategies
 * @dev Strategies 不得已存在于 operator set 中
 * @param avs 要设置 strategies 的 avs
 * @param operatorSetId 要添加 strategies 的 operator set
 * @param strategies 要添加的 strategies
 */
function addStrategiesToOperatorSet(
    address avs,
    uint32 operatorSetId,
    IStrategy[] calldata strategies
)
    external
    checkCanCall(avs)

注意:此方法可以直接由 AVS 调用,也可以由 AVS 授权的调用者调用。 有关详细信息,请参见 PermissionController.md

此函数允许 AVS 向给定的 operator set 添加可 slash 的 strategies 。 如果任何 strategy 已为给定的 operator set 注册,则整个调用将失败。

效果

  • 对于每个 strategies 元素:
    • 将 strategy 添加到 _operatorSetStrategies[operatorSetKey]
    • 发出 StrategyAddedToOperatorSet 事件

要求

  • 调用者必须经过授权,无论是作为 AVS 本身还是作为管理员/被任命者(请参见 PermissionController.md
  • 必须为 AVS 注册 operator set
  • 不得为 operator set 注册每个提议的 strategy
  • 如果 operatorSet 正在重新分配,则不能添加 BEACONCHAIN_ETH_STRAT,因为不支持 native eth 的重新分配
removeStrategiesFromOperatorSet
/**
 * @notice 允许 AVS 从 operator set 中删除 strategies
 * @dev Strategies 必须已存在于 operator set 中
 * @param avs 要删除 strategies 的 avs
 * @param operatorSetId 要从中删除 strategies 的 operator set
 * @param strategies 要删除的 strategies
 */
function removeStrategiesFromOperatorSet(
    address avs,
    uint32 operatorSetId,
    IStrategy[] calldata strategies
)
    external
    checkCanCall(avs)

注意:此方法可以直接由 AVS 调用,也可以由 AVS 授权的调用者调用。 有关详细信息,请参见 PermissionController.md

此函数允许 AVS 从给定的 operator set 中删除可 slash 的 strategies 。 如果任何 strategy 尚未为给定的 operator set 注册,则整个调用将失败。

效果

  • 对于每个 strategies 元素:
    • _operatorSetStrategies[operatorSetKey] 中删除 strategy
    • 发出 StrategyRemovedFromOperatorSet 事件

要求

  • 调用者必须经过授权,无论是作为 AVS 本身还是作为管理员/被任命者(请参见 PermissionController.md
  • 必须为 AVS 注册 operator set
  • 必须为 operator set 注册每个提议的 strategy
registerForOperatorSets
/**
 * @notice 用于注册 AVS operator sets 的参数
 * @param avs 要注册的 AVS
 * @param operatorSetIds 要注册的 AVS 中的 operator sets
 * @param data 要传递给 AVS 以完成注册的额外数据
 */
struct RegisterParams {
    address avs;
    uint32[] operatorSetIds;
    bytes data;
}

/**
 * @notice 允许 operator 为 AVS 的一个或多个 operator sets 注册。 如果 operator
 * 已将任何 stake 分配给这些 operator sets,则它会立即变成可 slash 的。
 * @dev 在 ALM 中注册后,此方法调用 AVS Registrar 的 `IAVSRegistrar。
 * registerOperator` 方法以完成注册。 此调用必须成功才能
 * 注册成功。
 */
function registerForOperatorSets(
    address operator,
    RegisterParams calldata params
)
    external
    onlyWhenNotPaused(PAUSED_OPERATOR_SET_REGISTRATION_AND_DEREGISTRATION)
    checkCanCall(operator)

注意:此方法可以直接由 operator 调用,也可以由 operator 授权的调用者调用。 有关详细信息,请参见 PermissionController.md

Operator 可以调用此函数以一次注册给定 AVS 的任意数量的 operator sets 。 有两个非常重要的细节要知道此方法:

  1. 作为注册的一部分,每个 operator set 都会添加到 operator 的 registeredSets 中。 请注意,对于每个新注册的 set,分配给 operator set 的任何 stake 都会立即变为可 slash 的
  2. 添加所有 set 后,将调用 AVS 的配置的 IAVSRegistrar 以确认和完成注册。 _此调用不得恢复,_因为 AVS 应该使用此调用来拒绝不符合资格的 operator(根据他们自己的自定义逻辑)。 请注意,如果 AVS 没有配置注册器,则会调用 avs 本身。

此方法对 IAVSRegistrar.registerOperator 方法进行外部调用,传入正在注册的 operator、正在注册的 operatorSetIds 以及注册期间提供的输入 params.data。 摘自 IAVSRegistrar.sol

/**
 * @notice 由 AllocationManager 在 operator 想要注册时调用
 * 一个或多个 operator sets。 如果注册失败,则此方法应恢复。
 * @param operator 注册 operator
 * @param operatorSetIds 正在注册的 operator set ids 列表
 * @param data operator 可以作为注册一部分提供的任意数据
 */
function registerOperator(address operator, uint32[] calldata operatorSetIds, bytes calldata data) external;

效果

  • 将提议的 operator sets 添加到 operator 的已注册 sets 列表 (registeredSets)
  • 将 operator 添加到每个 operator set 的 _operatorSetMembers
  • 将 operator 标记为已注册给定的 operator sets(在 registrationStatus 中)
  • 将注册的 params 传递给 AVS 的 AVSRegistrar,AVS Registrar 可以随意处理注册请求
  • 为每个 operator 发出 OperatorAddedToOperatorSet 事件

要求

  • 暂停状态不得设置:PAUSED_OPERATOR_SET_REGISTRATION_AND_DEREGISTRATION
  • operator 必须在 DelegationManager 中注册为 operator
  • 调用者必须经过授权,无论是 operator 本身还是管理员/被任命者(请参见 PermissionController.md
  • 给定的 AVS 必须存在每个 operatorSetId
  • Operator 不得已注册任何提议的 operator sets
  • 如果 operator 已注销,则 operator 不得再可 slash(即,必须已通过 DEALLOCATION_DELAY
  • 对 AVS 的配置的 IAVSRegistrar 的函数调用不得恢复
deregisterFromOperatorSets
/**
 * @notice 用于从 AVS operator sets 注销的参数
 * @param operator 正在注销的 operator
 * @param avs 要从中注销的 avs
 * @param operatorSetIds 要从中注销的 AVS 中的 operator sets
 */
struct DeregisterParams {
    address operator;
    address avs;
    uint32[] operatorSetIds;
}

/**
 * @notice 允许 operator 或 AVS 从 AVS 的一个或多个 operator sets 中注销 operator。
 * 如果 operator 已将任何可 slash 的 stake 分配给 AVS,则它将保持可 slash 状态,直到
 * DEALLOCATION_DELAY 已过去。
 * @dev 在 ALM 中注销后,此方法调用 AVS Registrar 的 `IAVSRegistrar。
 * deregisterOperator` 方法以完成注销。 此调用必须成功才能
 * 注销成功。
 */
function deregisterFromOperatorSets(
    DeregisterParams calldata params
)
    external
    onlyWhenNotPaused(PAUSED_OPERATOR_SET_REGISTRATION_AND_DEREGISTRATION)

注意:此方法可以直接由 operator/AVS 调用,也可以由 operator/AVS 授权的调用者调用。 有关详细信息,请参见 PermissionController.md

此方法可以由 EITHER operator 或者 AVS 调用, operator 已向其注册; 旨在允许注销由 EITHER 方触发。 此方法通常会反转 registerForOperatorSets 的效果,但有两个特定例外:

  1. 作为注销的一部分,每个 operator set 都会从 operator 的 registeredSets 中删除。 但是,分配给该 operator set 的任何 stake 都将在 DEALLOCATION_DELAY 区块内保持可 slash 状态。 在此函数调用已过去之前, operator 将不允许再次注册 operator set 。
  2. 删除所有集合后,将调用 AVS 的配置的 IAVSRegistrar 以完成 AVS 端的注销。

此方法对 IAVSRegistrar.deregisterOperator 方法进行外部调用,并传入注销的 operator 和要注销的 operatorSetIds。 摘自 IAVSRegistrar.sol

/**
 * @notice 由 AllocationManager 在从
 * 一个或多个 operator sets 注销 operator 时调用。 如果此方法恢复,则会被忽略。
 * @param operator 注销 operator
 * @param operatorSetIds 要从中注销的 operator set ids 列表
 */
function deregisterOperator(address operator, uint32[] calldata operatorSetIds) external;

效果

  • 从 operator 的已注册 sets 列表 (registeredSets) 中删除提议的 operator sets
  • 从每个 operator set 的 _operatorSetMembers 中删除 operator
  • 使用以下内容更新 operator 的 registrationStatus
    • registered: false
    • slashableUntil: block.number + DEALLOCATION_DELAY
      • 如上所述,这允许 AVS slash 注销的 operators,直到 block.number == slashableUntil
  • 为每个 operator 发出 OperatorRemovedFromOperatorSet 事件
  • operatoroperatorSetIds 传递给 AVS 的 AVSRegistrar,AVS Registrar 可以随意处理注销请求

要求: <!-- * 地址必须注册为 operator -->

  • 暂停状态不得设置:PAUSED_OPERATOR_SET_REGISTRATION_AND_DEREGISTRATION
  • 调用者必须经过授权,无论是 operator/AVS 本身还是管理员/被任命者(请参见 PermissionController.md
  • 给定的 AVS 必须存在每个 operator set ID
  • 必须为给定的 operator sets 注册 Operator
  • 对 AVS 的配置的 IAVSRegistrar 的函数调用不得恢复

分配和 Slashing

Operator set 注册是准备参与 AVS 的一个步骤。 当 operator 成功注册 operator set 时,是因为所讨论的 AVS 已准备好为其分配任务。 但是,由此得出结论,在向 operator 分配任务之前,AVS 会期望 operators 向 operator set 分配可 slash 的 stake,以便 AVS 具有一定的经济安全性。

因此,预计许多 AVS 将要求 operators 在注册 operator set 之前分配可 slash 的 stake。 这是由于 registerForOperatorSets 部分充当 AVS 的“同意机制”,因为调用 IAVSRegsitrar.registerOperator 允许 AVS 查询分配任务时 operator 可以提供的可 slash 的 stake 金额。

只有当 operator 注册了 operator set 并且 _已为此 operator set 进行了有效分配_后,关联的 AVS 才能从 operator slash 实际 stake。

有关其他背景信息,请参见 ELIP-002#Unique Stake Allocation & Deallocation

概念:

方法:

最大 Magnitude 与 Encumbered Magnitude

Operators 分配 Magnitude,用于表示他们 total stake 的一部分。 对于给定的 strategy,AllocationManager 跟踪两个量,最大 magnitudeencumbered magnitude

/**
 * @notice 包含特定 strategy 的分配信息
 * @param maxMagnitude 可在所有 operator sets 之间分配的最大 magnitude
 * @param encumberedMagnitude 当前分配给 strategy 的 magnitude
 */
struct StrategyInfo {
    uint64 maxMagnitude;
    uint64 encumberedMagnitude;
}

/// @dev 包含 operator 给定 strategy 的最大 magnitude 的历史记录
mapping(address operator => mapping(IStrategy strategy => Snapshots.DefaultWadHistory)) internal _maxMagnitudeHistory;

/// @dev 对于 strategy,包含 operator 已分配给 operator sets 的 magnitude 量
/// @dev  此值应谨慎读取,因为可完成但未完成的 deallocations
///      从队列中弹出,仍然包含在 encumbered magnitude 中
mapping(address operator => mapping(IStrategy strategy => uint64)) public encumberedMagnitude;
```一个 operator 的最大规模从 `1 WAD` (`1e18`) 开始,当他们被 slash 时会减少。最大规模代表可分配规模的 "100%"。当一个 operator 从一个策略向一个 operator set 分配规模时,他们对于该策略的受限规模会增加。一个 operator 不能分配 > 100%;因此,一个策略的受限规模永远不能超过该策略的最大规模。

##### 评估“当前”分配

如前一节所述,分配和取消分配都有延迟,因此 `Allocation` 结构体既有 `currentMagnitude`,也有 `pendingDiff` / `effectBlock` 字段:

```solidity
/**
 * @notice 定义从策略到 operator set,针对 operator 的分配信息
 * @param currentMagnitude 从策略到 operator set 当前分配的规模
 * @param pendingDiff 待定的规模变更,如果存在(否则为 0)
 * @param effectBlock 待定规模差异生效的区块
 */
struct Allocation {
    uint64 currentMagnitude;
    int128 pendingDiff;
    uint32 effectBlock;
}

虽然可以使用 allocations 映射直接获取 Allocation,但你会注意到在 AllocationManager 中使用 _getUpdatedAllocation 辅助函数的惯例,而不是直接使用。这个辅助函数读取现有的 Allocation,然后将 block.numberAllocation.effectBlock 进行比较,以确定是否应用 pendingDiff

  • 如果可以应用差异,则辅助函数返回一个 Allocation,其中包含更新后的 currentMagnitude 和清零的 pendingDiffeffectBlock 字段,就好像修改已经完成一样。
  • 否则,Allocation 会从存储中原封不动地返回。

一般来说,当本文档中提到 Allocation 时(或在 AllocationManager.sol 合约中使用时),我们指的是上面定义的“当前”Allocation

评估一个分配是否 "Slashable"

给定一个 operator 和一个从 strategy 到 AVS 的 OperatorSetAllocationAllocationManager 使用以下标准来确定 operator 的分配是否可以被 slash:

  1. operator 必须已注册到 operator set,或者如果他们已注销,他们仍然必须可以被 slash(请参阅注册状态)。
  2. AVS 必须已将 strategy 添加到 operator set(请参阅addStrategiesToOperatorSetremoveStrategiesFromOperatorSet
  3. 现有的 Allocation 必须具有非零的 Allocation.currentMagnitude

如果所有这些都为真,则 AllocationManager 将允许 AVS slash operator 的 Allocation

评估有多少分配是 "Slashable"

getMinimumSlashableStake 计算在指定的未来区块可以被 slash 的最小 stake 数量。此计算考虑了每个 operator 从 operator set 中不同策略分配的 stake。该函数考虑了可能随时间减少可 slash stake 的未决分配变更,从而确保了最小的保证值。因为这是一个预测,所以在任何给定时刻可 slash 的 stake 都是一个离散值,但是当展望未来的区块时,该函数提供了可能的最低金额,考虑了在指定时间范围内生效的任何计划的分配调整。

请参阅 IAllocationManager.sol:getMinimumSlashableStake 了解更多详情。

modifyAllocations
/**
 * @notice 用于修改分配给 operator set 的可 slash 规模的结构体
 * @param operatorSet 要修改分配的 operator set
 * @param strategies 要修改分配的策略
 * @param newMagnitudes 为每个策略分配给此 operator set 的新规模
 */
struct AllocateParams {
    OperatorSet operatorSet;
    IStrategy[] strategies;
    uint64[] newMagnitudes;
}

/**
 * @notice 修改从策略列表中分配给 operator set 的可 slash stake 的比例
 * 请注意,取消分配的可 slash 状态会保留 DEALLOCATION_DELAY 个区块,因此当它们被清除时,释放的可分配规模可能少于最初取消分配的规模。
 * @param operator 要修改分配的 operator
 * @param params 一个或多个 operator set 的规模调整数组
 * @dev 更新已更新策略的 encumberedMagnitude
 */
function modifyAllocations(
    address operator, 
    AllocateParams[] calldata params
) 
    external
    onlyWhenNotPaused(PAUSED_MODIFY_ALLOCATIONS)

注意:此方法可以直接由 operator 调用,也可以由 operator 授权的调用方调用。有关详细信息,请参阅 PermissionController.md

此函数由 operator 调用,用于增加或减少从策略分配给 operator set 的可 slash 规模。作为输入,operator 提供一个 operator set 作为目标,以及一个策略列表和相应的 newMagnitudes 来分配。newMagnitude 值将与 operator 当前针对该 operator set/策略的 Allocation 进行比较:

  • 如果 newMagnitude 大于 Allocation.currentMagnitude,这是一个分配
  • 如果 newMagnitude 小于 Allocation.currentMagnitude,这是一个取消分配
  • 如果 newMagnitude 等于 Allocation.currentMagnitude,这是无效的(revert)

分配修改根据几个因素而采用不同的规则。回想一下,在任何时候,策略的 encumberedMagnitude 都不得超过该策略的 maxMagnitude。此外,请注意,在处理策略的修改之前,首先清除该策略的 deallocationQueue。这确保了首先处理任何可完成的取消分配,从而释放规模以进行分配。在 clearDeallocationQueue 中进一步解释了这个过程。

最后,modifyAllocations 不要求分配考虑其对应的策略是否与所讨论的 operator set 相关。这主要是为了减少复杂性。因为 removeStrategiesFromOperatorSet 始终允许 AVS 删除 策略的考虑,我们始终需要确保 operator 可以针对此类策略启动取消分配。虽然在策略未包含在 operator set 中时进行分配没有明确的用例,但我们选择不检查这一点。AVS 可能会提前宣布要添加策略,专门为了鼓励提前分配。预计的行为是,AVS 将策略添加到 operator set 会立即使对该策略的任何现有分配都可被 slash。

如果我们要处理规模的增加(分配):

  • 规模的增加会立即添加到策略的 encumberedMagnitude 中。这确保了随后从同一策略向其他 operator set 进行的分配不会超过策略的 maxMagnitude
  • allocation.pendingDiff 被设置,allocation.effectBlock 等于当前区块加上 operator 配置的分配延迟。
  • 与取消分配不同,分配的 effectBlock 不会递增 1。这是为了允许即时分配。

如果我们要处理规模的减少(取消分配):

首先,评估 operator 的现有分配当前是否可以被 AVS slash。这很重要,因为 AVS 可能会使用现有分配来保护分配给此 operator 的任务。有关详细信息,请参阅评估分配是否 "Slashable"

接下来,如果现有分配_可以_被 slash

  • allocation.pendingDiff 被设置,allocation.effectBlock 等于当前区块加上 DEALLOCATION_DELAY + 1。这意味着现有分配 DEALLOCATION_DELAY 个区块内仍然可以被 slash
  • Operator set 将被推送到 operator 的策略的 deallocationQueue 中,表示此 (operatorSet, strategy) 存在待处理的取消分配。这是一个有序队列,强制按顺序处理取消分配,并且在此方法和 clearDeallocationQueue 中都使用。

或者,如果现有分配_不能_被 slash,则取消分配的金额将立即释放。它从策略的受限规模中减去,并且可以用于后续的分配。这是唯一不会导致“待处理的修改”的更新类型。这里的理由是,如果现有分配不可被 slash,则 AVS 不需要它来保护任务,因此不需要强制执行取消分配延迟。

注意:如果在取消分配排队后从 operatorSet 中删除策略,则取消分配仍然需要经过整个 DEALLOCATION_DELAY 区块。在这种情况下,取消分配不会立即完成

另一个需要考虑的点是涉及 slashing 事件的竞态条件以及 operator 发生的取消分配。考虑以下场景:operator 的分配规模为 500,并尝试取消分配将其设置为 250。但是在调用 modifyAllocations 之前同一区块中,operator 被 OperatorSet slash 了 100%,从而将当前规模设置为 0。现在,operator 的取消分配被视为分配,最终分配了 250 规模,而他们试图取消分配。这是恶意 AVS 的潜在恶意攻击媒介,也是一个已知的缺陷。在这种情况下,operator 应该简单地将其所有分配取消分配为 0,这样他们就不会意外分配更多可 slash 的 stake。通常,对于非恶意的 AVS,slashing 被认为是非常偶然的事件,并且此竞态条件不会影响 operator。

效果

  • 对于每个 AllocateParams 元素:
    • 完成任何现有的取消分配(请参阅clearDeallocationQueue
    • 根据上述规则更新 operator 的 encumberedMagnitudeallocationsdeallocationQueue。此外:
      • 如果 encumberedMagnitude 已更新,则发出 EncumberedMagnitudeUpdated
      • 如果创建了待处理的修改:
        • strategy 添加到 allocatedStrategies[operator][operatorSetKey](如果不存在)
        • operatorSetKey 添加到 allocatedSets[operator](如果不存在)
      • 如果分配现在具有 currentMagnitude 为 0:
        • allocatedStrategies[operator][operatorSetKey] 列表中删除 strategy
        • 如果此列表现在的长度为 0,则从 allocatedSets[operator] 中删除 operatorSetKey
    • 发出 AllocationUpdated 事件

要求

  • 暂停状态不得设置:PAUSED_MODIFY_ALLOCATIONS
  • 调用者必须经过授权,无论是作为 operator 本身还是管理员/被任命者(请参阅 PermissionController.md
  • Operator 必须已经设置了分配延迟(请参阅setAllocationDelay
  • 对于每个 AllocationParams 元素:
    • 对于给定的 AllocateParams 对象,提供的策略的长度必须等于提供的规模
    • 对于每个指定的 AVS,operator set 必须存在
    • Operator 不得有任何给定策略的待处理的、不可完成的修改
    • 新的规模不得与现有的规模匹配
    • 新的受限规模不得超过给定策略的 operator 的最大规模

注意:对于在 EigenPodManager 中具有负份额的 operator(来自 slashing 前的升级状态),我们建议不要进行分配,直到份额变为非零为止。

clearDeallocationQueue
/**
 * @notice 此函数采用策略列表,并针对每个策略,从 deallocationQueue 中删除
 * 直到最大 `numToClear` 个取消分配数量的所有可清除的取消分配,并根据需要更新 operator 的 encumberedMagnitude。
 *
 * @param operator 要清除取消分配的地址
 * @param strategies 要清除取消分配的策略列表
 * @param numToClear 要为每个策略清除的待处理取消分配的数量列表
 *
 * @dev 可以由任何人免许可调用
 */
function clearDeallocationQueue(
    address operator,
    IStrategy[] calldata strategies,
    uint16[] calldata numToClear
)
    external
    onlyWhenNotPaused(PAUSED_MODIFY_ALLOCATIONS)

此函数用于完成 operator 的任何符合条件的待处理取消分配。该函数接受一个 operator、一个策略列表以及要完成的相应数量的待处理取消分配。

清除待处理的取消分配在 modifyAllocations 中起着重要作用,因为可完成的取消分配表示可以释放的规模,以便重新分配到不同的 operator set。此方法的存在是为了方便希望将待处理的取消分配作为独立操作完成的 operator。但是,modifyAllocations自动在处理给定策略的分配修改时清除任何符合条件的取消分配。

对于每个策略,该方法会迭代 deallocationQueue[operator][strategy]

/// @dev 对于策略,保留一个已排序的、具有待处理取消分配的 operator set 队列
/// 必须按顺序完成这些操作才能释放规模以供将来分配
mapping(address operator => mapping(IStrategy strategy => DoubleEndedQueue.Bytes32Deque)) internal deallocationQueue;

此队列包含每个策略一个已排序的 operator set 列表,这些 operator set 由于 operator 之前对 modifyAllocations 的调用,其可 slash 的规模存在待处理的减少。对于队列中的每个 operator set,都会评估该 operator set 的相应分配。如果其 effectBlock 已达到,则取消分配完成,并通过从 encumberedMagnitude[operator][strategy] 中减去来释放取消分配的规模。然后,从队列的前面弹出相应的条目。

当队列为空时,达到无法完成的取消分配时,或者当它从队列中清除了 numToClear 个条目时,此方法停止迭代。

效果

  • 对于 deallocationQueue[operator][strategy] 中的每个 strategy可完成的取消分配:
    • deallocationQueue 中弹出相应的 operator set
    • allocation.currentMagnitude 减少取消分配的数量
    • allocation.pendingDiffallocation.effectBlock 设置为 0
    • 将取消分配的数量添加到策略的 encumberedMagnitude
    • 发出 EncumberedMagnitudeUpdated
    • 此外,如果取消分配使 allocation.currentMagnitude 等于零:
      • allocatedStrategies[operator][operatorSetKey] 列表中删除 strategy
      • 如果此列表现在的长度为 0,则从 allocatedSets[operator] 中删除 operatorSetKey

要求

  • 暂停状态不得设置:PAUSED_MODIFY_ALLOCATIONS
  • 策略列表的长度必须等于 numToClear 列表
slashOperator
/**
 * @notice 包含 slashing 参数的结构体
 * @param operator 要 slash 的地址
 * @param operatorSetId operator 被 slashing 的 operatorSet 的 ID
 * @param strategies 要 slash 的策略集
 * @param wadsToSlash 要 slash 的 1e18 中的份数,这将与 operator 在 operatorSet 的可 slash stake 分配成比例
 * @param description AVS 提供的 slashing 描述,以提高可读性
 */
struct SlashingParams {
    address operator;
    uint32 operatorSetId;
    IStrategy[] strategies;
    uint256[] wadsToSlash;
    string description;
}

/**
 * @notice 由 AVS 调用以在给定的 operator set 中 slash operator。Operator 必须已注册
 * 并且有分配给 operator set 的可 slash stake。
 *
 * @param avs 启动 slash 的 AVS 地址。
 * @param params Slash 参数,包含:
 *  - operator:要 slash 的 operator。
 *  - operatorSetId:operator 从中被 slash 的 operator set 的 ID。
 *  - strategies:用于 slash 分配的策略数组(必须按升序排列)。
 *  - wadsToSlash:要从每个策略中 slash 的比例数组(必须介于 0 和 1e18 之间)。
 *  - description:operator 被 slash 的原因的描述。
 *
 * @return slashId Slash 的 ID。
 * @return shares 每个策略被 slash 的份额数量。
 * 
 * @dev 对于每个策略:
 *      1. 按 wadToSlash 比例减少 operator 的当前分配规模。
 *      2. 按比例减少策略的最大和受限规模。
 *      3. 如果存在待处理的取消分配,则按比例减少它。
 *      4. 更新 DelegationManager 中 operator 的份额。
 *
 * @dev 由于
 *      舍入,小额 slashing 可能不会导致实际的 token 销毁
 *      ,这将导致少量 token 锁定在合约中
 *      ,而不是完全通过销毁机制进行销毁。
 */
function slashOperator(
    address avs,
    SlashingParams calldata params
)
    external
    onlyWhenNotPaused(PAUSED_OPERATOR_SLASHING)
    checkCanCall(avs)
    returns (uint256, uint256[] memory)

注意:此方法可以直接由 AVS 调用,也可以由 AVS 授权的调用方调用。有关详细信息,请参阅 PermissionController.md

AVS 使用 slashing 作为对不良行为的惩罚性威慑。有关 slashing 如何工作的详细信息和示例,请参阅 ELIP-002#Slashing of Unique Stake。请注意,AVS 决定的任何 slashing 标准,AllocationManager 强制执行的唯一标准是上面详述的标准(请参阅评估分配是否 "Slashable")。

为了 slash 符合条件的 operator,AVS 指定 operator 所属的 operator set、应对 operator 进行 slash 的 strategies,以及对于每个策略,应被 slash 的operator 分配规模的比例(由 wadsToSlash 给出)。可选的 description 字符串允许 AVS 向 slash 添加上下文。

一旦在 AllocationManager 中触发,slashing 是即时的且不可逆转的。对于每个被 slash 的策略,operator 的 maxMagnitudeencumberedMagnitude 都会减少,并且对给定 operator set 的分配也会减少其 currentMagnitude。有关如何计算被 slash 金额的详细信息,请参阅 [TODO - Accounting Doc]()。

此方法有两个需要注意的极端情况:

  1. 在对给定 strategyoperator 进行 slash 的过程中,如果被 slash 的 AllocationcurrentMagnitude 为 0,则调用将不会 revert。相反,将跳过 strategy,并且 slashing 继续处理列出的下一个 strategy。这是为了防止在取消分配的 effectBlock 上或附近发生 slashing 的极端情况——如果调用 revert,则整个 slash 将失败。跳过允许处理任何有效的 slash,而无需重新提交。
  2. 如果 operator 有一个待处理的、不可完成的取消分配,则取消分配的 pendingDiff 会与 slash 成比例地减少。这确保了在完成取消分配时,释放的 encumberedMagnitude 会更少。

一旦 обработка 完成了策略的 slashing,被 slash 的 stake 将通过 DelegationManager 销毁或重新分配

效果

  • 给定一个 operatoroperatorSet,然后对于每个 params.strategies 元素及其对应的 allocation
    • 通过将当前规模乘以提供的 wadsToSlash 来计算要 slash 的规模
    • allocation.currentMagnitude 减少被 slash 的规模
      • 发出 AllocationUpdated 事件
    • 将 operator 的 encumberedMagnitude 减少此策略的被 slash 的规模
      • 发出 EncumberedMagnitudeUpdated 事件
    • 将一个条目推送到 operator 的 maxMagnitudeHistory,将他们的 maxMagnitude 减少被 slash 的规模
      • 发出 MaxMagnitudeUpdated 事件
    • 如果 allocation 待处理的、不可完成的取消分配,则额外地按相同的比例减少 allocation.pendingDiff 并发出 AllocationUpdated 事件
    • 如果 allocation 现在具有 currentMagnitude 为 0:
      • allocatedStrategies[operator][operatorSetKey] 列表中删除 strategy
      • 如果此列表现在的长度为 0,则从 allocatedSets[operator] 中删除 operatorSetKey
    • 调用 DelegationManager.slashOperatorShares
  • 发出 OperatorSlashed 事件
  • 递增 operatorSet 的 slashId
  • 返回 slashId 和每个策略被 slash 的份额数量

要求

  • 暂停状态不得设置:PAUSED_OPERATOR_SLASHING
  • 调用者必须经过授权,无论是作为 AVS 本身还是管理员/被任命者(请参阅 PermissionController.md
  • 必须为 AVS 注册 operator set
  • Operator 必须可以被 slash,即:
    • Operator 已注册到 operator set,
    • Operator 的 DEALLOCATION_DELAY 尚未完成
  • params.strategies 必须按升序排列(以确保没有重复项)
  • params.strategies.length 必须等于 params.wadsToSlash.length
  • 对于每个 params.strategies 元素:
    • wadsToSlash 必须(0, 1e18] 范围内
    • Operator set 必须包含策略

配置

方法:

setAllocationDelay
/**
 * @notice 由 delegation manager 或 operator 调用以设置 operator 的分配延迟。
 * 这是在 operator 首次注册时设置的,并且是 operator
 * 将规模分配给 operator set 和规模变为可 slash 之间相隔的区块数。
 * @param operator 要代表其设置延迟的 operator。
 * @param delay 分配延迟(以区块为单位)
 */
function setAllocationDelay(
    address operator,
    uint32 delay
)
    external

注意:如果不是DelegationManager 调用,则此方法可以直接由 operator 调用,也可以由 operator 授权的调用方调用。有关详细信息,请参阅 PermissionController.md

此函数设置 operator 的分配延迟(以区块为单位)。一旦设置,operator 可以更新此延迟。此值的初始设置和任何进一步的更新_都需要 ALLOCATION_CONFIGURATION_DELAY 个区块_才能生效。由于拥有延迟是分配可 slash stake 的要求,这实际上意味着,一旦 slashing 发布生效,至少在 ALLOCATION_CONFIGURATION_DELAY 个区块内,任何人都无法分配可 slash stake。

DelegationManager 在为 slashing 发布后创建的所有新 operator 进行 operator 注册时调用此函数。对于在 slashing 发布之前存在于 DelegationManager 中的 operator,他们将需要调用此方法来配置分配延迟,然后才能将可 slash stake 分配给任何 AVS

分配延迟的主要目的是让委托给 operator 的 staker 有机会在 operator 将风险概况更改为他们不舒服的内容之前提取他们的 stake。但是,operator 可以选择以他们想要的任何方式配置此延迟,包括将其设置为 0。

效果

  • 将 operator 的 pendingDelay 设置为建议的 delay,并保存可以激活 pendingDelayeffectBlock
    • effectBlock = uint32(block.number) + ALLOCATION_CONFIGURATION_DELAY + 1
  • 如果 operator 具有 pendingDelay,并且如果 effectBlock 已经过去,则将 operator 的 delay 设置为 pendingDelay
    • 这也会将 isSet 布尔值设置为 true,以指示 operator 的 delay(即使为 0)也是有意设置的
  • 发出 AllocationDelaySet 事件

要求

  • 调用者必须是 DelegationManager 或注册的 operator
setAVSRegistrar
/**
 * @notice 由 AVS 调用以配置在 operator 从 AVS 的 operator set 注册或注销时调用的地址。
 * 如果未设置(或设置为 0),则默认为 AVS 的地址。
 * @param registrar 新的 Registrar 地址
 */
function setAVSRegistrar(
    address avs,
    IAVSRegistrar registrar
)
    external
    checkCanCall(avs)

注意:此方法可以直接由 AVS 调用,也可以由 AVS 授权的调用方调用。有关详细信息,请参阅 PermissionController.md

为给定的 avs 设置 registrar。请注意,如果将 Registrar 设置为 0,则 getAVSRegistrar 将返回 AVS 的地址。

当 operator 注册到或注销 operator set 时,将调用 AVS Registrar。从 IAVSRegistrar.sol 可以看到,AVS Registrar 应使用以下接口:

interface IAVSRegistrar {
    /**
     * @notice 当 operator 想要注册
     * 一个或多个 operator set 时,由 AllocationManager 调用。如果注册不成功,此方法应 revert。
     * @param operator 注册的 operator
     * @param operatorSetIds 要注册的 operator set ID 列表
     * @param data operator 可以作为注册的一部分提供的任意数据
     */
    function registerOperator(address operator, uint32[] calldata operatorSetIds, bytes calldata data) external;

    /**
     * @notice 当 operator 从
     * 一个或多个 operator set 中注销时,由 AllocationManager 调用。如果此方法 revert,则将被忽略。
     * @param operator 注销的 operator
     * @param operatorSetIds 要从中注销的 operator set ID 列表
     */
    function deregisterOperator(address operator, uint32[] calldata operatorSetIds) external;
}

请注意,当 operator 注册时,如果对 IAVSRegistrar 的调用 revert,则注册将失败。但是,当 operator 注销时,deregisterOperator 中的 revert 将被忽略。

效果

  • _avsRegistrar[avs] 设置为 registrar
  • 发出 AVSRegistrarSet 事件

要求

  • 调用者必须经过授权,无论是作为 AVS 本身还是管理员/被任命者(请参阅 PermissionController.md
  • 原文链接: github.com/Layr-Labs/eig...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Layr-Labs
Layr-Labs
江湖只有他的大名,没有他的介绍。