本文档介绍了OpenZeppelin Contracts库中用于访问控制的组件,包括Ownable、AccessControl和TimelockController。Ownable提供简单的所有者权限控制,AccessControl提供更细粒度的基于角色的访问控制,而TimelockController则在访问控制中引入时间锁机制,确保关键操作执行前有足够的时间让用户做出反应。
你当前阅读的不是此文档的最新版本。5.x 是当前版本。
在 https://docs.openzeppelin.com/contracts/api/access 处查看此文档效果更佳 |
此目录提供了限制谁可以访问合约功能或何时可以访问的方式。
AccessControl
提供了一种通用的基于角色的访问控制机制。可以创建多个分层角色,并将每个角色分配给多个帐户。
Ownable
是一种更简单的机制,具有一个可以分配给单个帐户的单个所有者“角色”。这种更简单的机制对于快速测试很有用,但具有生产问题的项目可能会超越它。
TimelockController
与上述两种机制之一结合使用。通过将角色分配给 TimelockController
合约的实例,对该角色控制的功能的访问将被延迟一段时间。
Ownable
合约模块,提供基本的访问控制机制,其中 存在一个帐户(所有者),可以被授予对 特定功能的独占访问权。
默认情况下,所有者帐户将是部署合约的帐户。这
可以稍后使用 transferOwnership
更改。
此模块通过继承使用。它将提供修饰符
onlyOwner
,可以将其应用于你的函数以将它们的使用限制为
所有者。
修饰符
函数
事件
onlyOwner()
修饰符如果由所有者以外的任何帐户调用,则抛出异常。
constructor()
内部函数初始化合约,将部署者设置为初始所有者。
owner() → address
public返回当前所有者的地址。
renounceOwnership()
public使合约没有所有者。将无法再调用
onlyOwner
函数。只能由当前所有者调用。
放弃所有权将使合约没有所有者,<br>从而删除任何仅对所有者可用的功能。 |
transferOwnership(address newOwner)
public将合约的所有权转移到新帐户(newOwner
)。
只能由当前所有者调用。
OwnershipTransferred(address previousOwner, address newOwner)
事件AccessControl
合约模块,允许子合约实现基于角色的访问 控制机制。
角色由其 bytes32
标识符引用。这些应该在
外部 API 中公开并且是唯一的。实现此目的的最佳方法是
使用 public constant
哈希摘要:
bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
角色可用于表示一组权限。要限制对
函数调用的访问,请使用 hasRole
:
function foo() public {
require(hasRole(MY_ROLE, msg.sender));
...
}
角色可以通过 grantRole
和
revokeRole
函数动态地授予和撤销。每个角色都有一个关联的管理角色,并且只有
具有角色管理角色的帐户才能调用 grantRole
和 revokeRole
。
默认情况下,所有角色的管理角色都是 DEFAULT_ADMIN_ROLE
,这意味着
只有具有此角色的帐户才能授予或撤销其他
角色。可以通过使用
_setRoleAdmin
创建更复杂的角色关系。
DEFAULT_ADMIN_ROLE 也是它自己的管理员:它有权<br>授予和撤销此角色。应采取额外的预防措施来保护<br>已被授予它的帐户。 |
函数
事件
hasRole(bytes32 role, address account) → bool
public如果 account
已被授予 role
,则返回 true
。
getRoleMemberCount(bytes32 role) → uint256
public返回拥有 role
的帐户数量。可以与
getRoleMember
一起使用来枚举角色的所有持有者。
getRoleMember(bytes32 role, uint256 index) → address
public返回拥有 role
的帐户之一。index
必须是
0 和 getRoleMemberCount
之间的值,不包括在内。
角色持有者不以任何特定方式排序,并且它们的排序可能 在任何时候更改。
当使用 getRoleMember 和 getRoleMemberCount 时,请确保<br>在同一区块上执行所有查询。请参阅以下<br>论坛帖子<br>以获取更多信息。 |
getRoleAdmin(bytes32 role) → bytes32
public返回控制 role
的管理角色。请参阅 grantRole
和
revokeRole
。
要更改角色的管理员,请使用 _setRoleAdmin
。
grantRole(bytes32 role, address account)
public将 role
授予 account
。
如果 account
尚未被授予 role
,则发出 RoleGranted
事件。
要求:
role
的管理角色。revokeRole(bytes32 role, address account)
public从 account
撤销 role
。
如果 account
已被授予 role
,则发出 RoleRevoked
事件。
要求:
role
的管理角色。renounceRole(bytes32 role, address account)
public从调用帐户撤销 role
。
角色通常通过 grantRole
和 revokeRole
管理:此函数
旨在提供一种机制,使帐户在
受到威胁时(例如,当可信设备放错地方时)失去其权限。
如果调用帐户已被授予 role
,则发出 RoleRevoked
事件。
要求:
account
。_setupRole(bytes32 role, address account)
internal将 role
授予 account
。
如果 account
尚未被授予 role
,则发出 RoleGranted
事件。请注意,与 grantRole
不同,此函数不会对
调用帐户执行任何检查。
仅应在设置<br>系统的初始角色时从构造函数中调用此函数。<br>以任何其他方式使用此函数实际上是在规避由 AccessControl 施加的管理<br>系统。 |
_setRoleAdmin(bytes32 role, bytes32 adminRole)
internal将 adminRole
设置为 role
的管理角色。
发出 RoleAdminChanged
事件。
RoleAdminChanged(bytes32 role, bytes32 previousAdminRole, bytes32 newAdminRole)
event当 newAdminRole
设置为 role
的管理角色时发出,替换 previousAdminRole
DEFAULT_ADMIN_ROLE
是所有角色的起始管理员,尽管
RoleAdminChanged
未发出信号表明这一点。
自 v3.1 起可用。
RoleGranted(bytes32 role, address account, address sender)
event当 account
被授予 role
时发出。
sender
是发起合约调用的帐户,是管理角色
持有者,除非使用 _setupRole
。
RoleRevoked(bytes32 role, address account, address sender)
event当 account
被撤销 role
时发出。
sender
是发起合约调用的帐户:
revokeRole
,则是管理角色持有者renounceRole
,则是角色持有者(即 account
)TimelockController
合约模块,充当时间锁定的控制器。当设置为
Ownable
智能合约的所有者时,它会对所有
onlyOwner
维护操作强制执行时间锁。这使受控合约的用户有时间在应用潜在危险的维护
操作之前退出。
默认情况下,此合约是自我管理的,这意味着管理任务
必须经过时间锁过程。提议者(resp 执行者)角色
负责提议(resp 执行)操作。一个常见的用例是
将此 TimelockController
定位为智能合约的所有者,
多重签名或 DAO 作为唯一提议者。
自 v3.3 起可用。
修饰符
函数
hashOperationBatch(targets, values, datas, predecessor, salt)
scheduleBatch(targets, values, datas, predecessor, salt, delay)
AccessControl
事件
AccessControl
onlyRole(bytes32 role)
修饰符修饰符,使函数只能由特定角色调用。除了
检查发送者的角色外,还考虑了 address(0)
的角色。将角色授予 address(0)
等同于为所有人启用
此角色。
constructor(uint256 minDelay, address[] proposers, address[] executors)
public使用给定的 minDelay
初始化合约。
receive()
external合约可能会接收/持有 ETH 作为维护过程的一部分。
isOperation(bytes32 id) → bool pending
public返回 id 是否对应于已注册的操作。这 包括挂起、准备就绪和完成的操作。
isOperationPending(bytes32 id) → bool pending
public返回操作是否挂起。
isOperationReady(bytes32 id) → bool ready
public返回操作是否准备就绪。
isOperationDone(bytes32 id) → bool done
public返回操作是否已完成。
getTimestamp(bytes32 id) → uint256 timestamp
public返回操作准备就绪的时间戳(未设置的操作为 0, 完成的操作为 1)。
getMinDelay() → uint256 duration
public返回操作变为有效所需的最小延迟。
可以通过执行调用 updateDelay
的操作来更改此值。
hashOperation(address target, uint256 value, bytes data, bytes32 predecessor, bytes32 salt) → bytes32 hash
public返回包含单个 事务的操作的标识符。
hashOperationBatch(address[] targets, uint256[] values, bytes[] datas, bytes32 predecessor, bytes32 salt) → bytes32 hash
public返回包含一批 事务的操作的标识符。
schedule(address target, uint256 value, bytes data, bytes32 predecessor, bytes32 salt, uint256 delay)
public计划包含单个事务的操作。
发出 CallScheduled
事件。
要求:
scheduleBatch(address[] targets, uint256[] values, bytes[] datas, bytes32 predecessor, bytes32 salt, uint256 delay)
public计划包含一批事务的操作。
为批处理中的每个事务发出一个 CallScheduled
事件。
要求:
cancel(bytes32 id)
public取消操作。
要求:
execute(address target, uint256 value, bytes data, bytes32 predecessor, bytes32 salt)
public执行包含单个事务的(就绪)操作。
发出 CallExecuted
事件。
要求:
executeBatch(address[] targets, uint256[] values, bytes[] datas, bytes32 predecessor, bytes32 salt)
public执行包含一批事务的(就绪)操作。
为批处理中的每个事务发出一个 CallExecuted
事件。
要求:
updateDelay(uint256 newDelay)
external更改未来操作的最小时间锁持续时间。
发出 MinDelayChange
事件。
要求:
CallScheduled(bytes32 id, uint256 index, address target, uint256 value, bytes data, bytes32 predecessor, uint256 delay)
event当调用计划为操作 id
的一部分时发出。
CallExecuted(bytes32 id, uint256 index, address target, uint256 value, bytes data)
event当调用作为操作 id
的一部分执行时发出。
Cancelled(bytes32 id)
event当操作 id
被取消时发出。
MinDelayChange(uint256 oldDuration, uint256 newDuration)
event当未来操作的最小延迟被修改时发出。
操作: 作为时间锁主题的事务(或一组事务)。它必须由提议者计划并由执行者执行。时间锁强制执行提议和执行之间的最短延迟(请参阅操作生命周期)。如果操作包含多个事务(批处理模式),则它们以原子方式执行。通过操作内容的哈希值来标识操作。
操作状态:
未设置: 不属于时间锁机制的操作。
挂起: 已计划但计时器尚未到期的操作。
准备就绪: 已计划且计时器已到期的操作。
完成: 已执行的操作。
前置: 操作之间的(可选)依赖关系。一个操作可以依赖于另一个操作(其前置操作),从而强制执行这两个操作的执行顺序。
角色:
提议者: 负责计划(和取消)操作的地址(智能合约或 EOA)。
执行者: 负责执行提议者计划的操作的地址(智能合约或 EOA)。
由 TimelockControler
执行的操作可以包含一个或多个后续调用。根据是否需要原子方式执行多个调用,可以使用简单操作或批处理操作。
两种操作都包含:
目标,时间锁应操作的智能合约的地址。
值,以 wei 为单位,应随事务一起发送。大多数情况下,这将为 0。以太币可以在执行事务之前或执行事务时沿途存入或传递。
数据,包含调用的编码函数选择器和参数。这可以使用多种工具生成。例如,使用 web3js 可以按如下方式编码将角色 ROLE
授予 ACCOUNT
的维护操作:
const data = timelock.contract.methods.grantRole(ROLE, ACCOUNT).encodeABI()
前置,指定操作之间的依赖关系。此依赖关系是可选的。如果操作没有任何依赖关系,请使用 bytes32(0)
。
盐,用于消除两个其他相同的操作的歧义。这可以是任何随机值。
在批处理操作的情况下,target
、value
和 data
被指定为数组,这些数组的长度必须相同。
时间锁操作由唯一的 id(它们的哈希值)标识,并遵循特定的生命周期:
Unset
→ Pending
→ Pending
+ Ready
→ Done
通过调用 schedule
(或 scheduleBatch
),提议者将操作从 Unset
状态移动到 Pending
状态。这将启动一个计时器,该计时器必须长于最小延迟。计时器在可以通过 getTimestamp
方法访问的时间戳到期。
计时器到期后,操作会自动进入 Ready
状态。此时,可以执行它。
通过调用 execute
(或 executeBatch
),执行者触发操作的基础事务并将其移动到 Done
状态。如果操作具有前置操作,则该前置操作必须处于 Done
状态才能使此转换成功。
cancel
允许提议者取消任何 Pending
操作。这将操作重置为 Unset
状态。因此,提议者可以重新计划已取消的操作。在这种情况下,当重新计划操作时,计时器会重新启动。
可以使用以下函数查询操作状态:
管理员负责管理提议者和执行者。为了使时间锁能够自我管理,此角色应仅授予时间锁本身。部署后,时间锁和部署者都具有此角色。在进一步配置和测试之后,部署者可以放弃此角色,以便所有进一步的维护操作都必须通过时间锁过程。
此角色由 TIMELOCK_ADMIN_ROLE 值标识:0x5f58e3a2316349923ce3780f8d587db2d72378aed66a8261c916544fa6846ca5
提议者负责计划(和取消)操作。这是一个关键角色,应授予治理实体。这可以是 EOA、多重签名或 DAO。
提议者之争: 拥有多个提议者虽然可以在一个提议者不可用时提供冗余,但这可能很危险。由于提议者对所有操作都有发言权,他们可以取消他们不同意的操作,包括删除他们的提议者操作。 |
此角色由 PROPOSER_ROLE 值标识:0xb09aa5aeb3702cfd50b6b62bc4532604938f21248a27a1d5ca736082b6819cc1
执行者负责在时间锁到期后执行提议者计划的操作。从逻辑上讲,作为提议者的多重签名或 DAO 也应该是执行者,以保证已计划的操作最终会被执行。但是,拥有额外的执行者可以降低成本(执行事务不需要提出它的多重签名或 DAO 的验证),同时确保负责执行的任何人都不能触发未由提议者计划的操作。
此角色由 EXECUTOR_ROLE 值标识:0xd8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e63
没有至少一个提议者和一个执行者的实时合约将被锁定。在部署者放弃其有利于时间锁合约本身的行政权利之前,请确保这些角色由可靠的实体担任。请参阅AccessControl 文档以了解有关角色管理的更多信息。 |
- 原文链接: docs.openzeppelin.com/co...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!