本文档详细介绍了 EigenLayer 的 AllocationManager 合约,该合约负责管理 AVS 元数据注册、Operator Set 的注册与注销、处理 Operator 可 Slash 权益的分配与 Slash,以及作为 AVS Slash Operator 的入口点。
文件 | 备注 |
---|---|
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 必须注册其元数据以声明自己的身份,这是第一步,然后才能创建 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
事件,供链下服务使用要求:
PermissionController.md
)如 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
列表。 avs
和 id
一起构成唯一标识给定 OperatorSet
的 key
。 Operators 可以注册和注销 operator sets。 结合分配可 slash 的 magnitude, operator set 注册构成了 operator slash 能力的基础(在分配和 Slashing中进一步讨论)。 OperatorSets 有两种类型,重新分配的和非重新分配的。
概念:
方法:
createOperatorSets
createRedistributingOperatorSets
addStrategiesToOperatorSet
removeStrategiesFromOperatorSet
registerForOperatorSets
deregisterFromOperatorSets
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 。 redistributionRecipient
是 DEFAULT_BURN_ADDRESS
,被 slash 的资金将发送到那里。
创建时,avs
指定一个对 AVS 唯一的 operatorSetId
。 avs
地址和 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
事件要求:
PermissionController.md
)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
事件redistributionRecipient
RedistributionAddressSet
要求:
PermissionController.md
)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
元素:
_operatorSetStrategies[operatorSetKey]
StrategyAddedToOperatorSet
事件要求:
PermissionController.md
)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]
中删除 strategyStrategyRemovedFromOperatorSet
事件要求:
PermissionController.md
)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 。 有两个非常重要的细节要知道此方法:
registeredSets
中。 请注意,对于每个新注册的 set,分配给 operator set 的任何 stake 都会立即变为可 slash 的 。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;
效果:
registeredSets
)_operatorSetMembers
registrationStatus
中)params
传递给 AVS 的 AVSRegistrar
,AVS Registrar 可以随意处理注册请求OperatorAddedToOperatorSet
事件要求:
PAUSED_OPERATOR_SET_REGISTRATION_AND_DEREGISTRATION
operator
必须在 DelegationManager
中注册为 operatorPermissionController.md
)operatorSetId
DEALLOCATION_DELAY
)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
的效果,但有两个特定例外:
registeredSets
中删除。 但是,分配给该 operator set 的任何 stake 都将在 DEALLOCATION_DELAY
区块内保持可 slash 状态。 在此函数调用已过去之前, operator 将不允许再次注册 operator set 。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;
效果:
registeredSets
) 中删除提议的 operator sets_operatorSetMembers
中删除 operatorregistrationStatus
:
registered: false
slashableUntil: block.number + DEALLOCATION_DELAY
block.number == slashableUntil
OperatorRemovedFromOperatorSet
事件operator
和 operatorSetIds
传递给 AVS 的 AVSRegistrar
,AVS Registrar 可以随意处理注销请求要求: <!-- * 地址必须注册为 operator -->
PAUSED_OPERATOR_SET_REGISTRATION_AND_DEREGISTRATION
PermissionController.md
)IAVSRegistrar
的函数调用不得恢复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。
概念:
方法:
Operators 分配 Magnitude,用于表示他们 total stake 的一部分。 对于给定的 strategy,AllocationManager
跟踪两个量,最大 magnitude 和 encumbered 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.number
与 Allocation.effectBlock
进行比较,以确定是否应用 pendingDiff
。
Allocation
,其中包含更新后的 currentMagnitude
和清零的 pendingDiff
和 effectBlock
字段,就好像修改已经完成一样。Allocation
会从存储中原封不动地返回。一般来说,当本文档中提到 Allocation
时(或在 AllocationManager.sol
合约中使用时),我们指的是上面定义的“当前”Allocation
。
给定一个 operator
和一个从 strategy
到 AVS 的 OperatorSet
的 Allocation
,AllocationManager
使用以下标准来确定 operator 的分配是否可以被 slash:
operator
必须已注册到 operator set,或者如果他们已注销,他们仍然必须可以被 slash(请参阅注册状态)。strategy
添加到 operator set(请参阅addStrategiesToOperatorSet
和 removeStrategiesFromOperatorSet
)Allocation
必须具有非零的 Allocation.currentMagnitude
如果所有这些都为真,则 AllocationManager
将允许 AVS slash operator 的
Allocation
。
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。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
)encumberedMagnitude
、allocations
和 deallocationQueue
。此外:
encumberedMagnitude
已更新,则发出 EncumberedMagnitudeUpdated
strategy
添加到 allocatedStrategies[operator][operatorSetKey]
(如果不存在)operatorSetKey
添加到 allocatedSets[operator]
(如果不存在)currentMagnitude
为 0:
allocatedStrategies[operator][operatorSetKey]
列表中删除 strategy
allocatedSets[operator]
中删除 operatorSetKey
AllocationUpdated
事件要求:
PAUSED_MODIFY_ALLOCATIONS
PermissionController.md
)setAllocationDelay
)AllocationParams
元素:
AllocateParams
对象,提供的策略的长度必须等于提供的规模注意:对于在 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 setallocation.currentMagnitude
减少取消分配的数量allocation.pendingDiff
和 allocation.effectBlock
设置为 0encumberedMagnitude
EncumberedMagnitudeUpdated
allocation.currentMagnitude
等于零:
allocatedStrategies[operator][operatorSetKey]
列表中删除 strategy
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 的 maxMagnitude
和 encumberedMagnitude
都会减少,并且对给定 operator set 的分配也会减少其 currentMagnitude
。有关如何计算被 slash 金额的详细信息,请参阅 [TODO - Accounting Doc]()。
此方法有两个需要注意的极端情况:
strategy
的 operator
进行 slash 的过程中,如果被 slash 的 Allocation
的 currentMagnitude
为 0,则调用将不会 revert。相反,将跳过 strategy
,并且 slashing 继续处理列出的下一个 strategy
。这是为了防止在取消分配的 effectBlock
上或附近发生 slashing 的极端情况——如果调用 revert,则整个 slash 将失败。跳过允许处理任何有效的 slash,而无需重新提交。operator
有一个待处理的、不可完成的取消分配,则取消分配的 pendingDiff
会与 slash 成比例地减少。这确保了在完成取消分配时,释放的 encumberedMagnitude
会更少。一旦 обработка 完成了策略的 slashing,被 slash 的 stake 将通过 DelegationManager
销毁或重新分配。
效果:
operator
和 operatorSet
,然后对于每个 params.strategies
元素及其对应的 allocation
:
wadsToSlash
来计算要 slash 的规模allocation.currentMagnitude
减少被 slash 的规模
AllocationUpdated
事件encumberedMagnitude
减少此策略的被 slash 的规模
EncumberedMagnitudeUpdated
事件maxMagnitudeHistory
,将他们的 maxMagnitude
减少被 slash 的规模
MaxMagnitudeUpdated
事件allocation
待处理的、不可完成的取消分配,则额外地按相同的比例减少 allocation.pendingDiff
并发出 AllocationUpdated
事件allocation
现在具有 currentMagnitude
为 0:
allocatedStrategies[operator][operatorSetKey]
列表中删除 strategy
allocatedSets[operator]
中删除 operatorSetKey
DelegationManager.slashOperatorShares
OperatorSlashed
事件slashId
slashId
和每个策略被 slash 的份额数量要求:
PAUSED_OPERATOR_SLASHING
PermissionController.md
)DEALLOCATION_DELAY
尚未完成params.strategies
必须按升序排列(以确保没有重复项)params.strategies.length
必须等于 params.wadsToSlash.length
params.strategies
元素:
wadsToSlash
必须在 (0, 1e18]
范围内方法:
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。
效果:
pendingDelay
设置为建议的 delay
,并保存可以激活 pendingDelay
的 effectBlock
effectBlock = uint32(block.number) + ALLOCATION_CONFIGURATION_DELAY + 1
pendingDelay
,并且如果 effectBlock
已经过去,则将 operator 的 delay
设置为 pendingDelay
值
isSet
布尔值设置为 true
,以指示 operator 的 delay
(即使为 0)也是有意设置的AllocationDelaySet
事件要求:
PermissionController.md
)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
事件要求:
PermissionController.md
)
- 原文链接: github.com/Layr-Labs/eig...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!