Openzeppelin学习:AccessManager源码分析

AccessManager源码//SPDX-License-Identifier:MIT//OpenZeppelinContracts(lastupdatedv5.1.0)(access/manager/AccessManager.sol)pragmasolidity^

AccessManager 源码

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/manager/AccessManager.sol)

pragma solidity ^0.8.20;

import {IAccessManager} from "./IAccessManager.sol";
import {IAccessManaged} from "./IAccessManaged.sol";
import {Address} from "../../utils/Address.sol";
import {Context} from "../../utils/Context.sol";
import {Multicall} from "../../utils/Multicall.sol";
import {Math} from "../../utils/math/Math.sol";
import {Time} from "../../utils/types/Time.sol";

contract AccessManager is Context, Multicall, IAccessManager {
    using Time for *;

    /**
     * 设置某个目标合约地址的访问控制
     * allowedRoles: 每个 selector(函数选择器)映射到一个 roleId,只有拥有该 roleId 的用户可访问该函数
     * adminDelay: 管理员延迟,表示管理员对该目标合约的操作延迟
     * closed: 是否关闭该目标合约的访问控制
     */
    struct TargetConfig {
        mapping(bytes4 selector => uint64 roleId) allowedRoles;
        Time.Delay adminDelay;
        bool closed;
    }

    /**
     * 某个账户在某个角色中的访问情况(被授予的情况)
     * since: 该账户自何时起拥有该权限(since == 0 表示未被授权; since > now 表示已授权但尚未生效)
     * delay: 该账户执行操作的延迟时间
     */
    struct Access {
        uint48 since;
        Time.Delay delay;
    }

    /**
     * 存储每个角色相关权限设定的结构体
     * members: 每个用户的访问权限
     * admin: 该角色的管理员,拥有授予或撤销权限的能力
     * guardian: 该角色的监护人,拥有取消操作的能力
     * grantDelay: 用户被授予该角色后的延迟时间
     */
    struct Role {
        mapping(address user => Access access) members;
        uint64 admin;
        uint64 guardian;
        Time.Delay grantDelay;
    }

    /**
     * 一个预定操作(比如延迟授权、延迟执行)的时间安排
     * timepoint: 该操作可以被执行的时间戳
     * nonce: 操作的唯一标识符,用于允许第三方合约识别该操作
     */
    struct Schedule {
        uint48 timepoint;
        uint32 nonce;
    }

    // 定义了一个最高权限角色常量 ADMIN_ROLE, 是系统保留的角色,类似“超级管理员”
    // 系统中默认每个函数都只能由 ADMIN_ROLE 调用,除非设置了新的权限策略
    uint64 public constant ADMIN_ROLE = type(uint64).min; // 0
    // 定义一个公开角色,所有地址天然拥有的公开角色
    uint64 public constant PUBLIC_ROLE = type(uint64).max; // 2**64 - 1

    // 每个目标合约地址(target)拥有一组权限配置
    mapping(address target => TargetConfig mode) private _targets;

    // 每个 roleId 对应一组角色管理信息
    mapping(uint64 roleId => Role) private _roles;

    // 每个操作 ID(如延迟调用)对应一个时间调度
    mapping(bytes32 operationId => Schedule) private _schedules;

    /**
     * 当前正在执行的 operation 的 ID,用于防止重入攻击或嵌套调用
     * 标记当前正在执行的受管操作的 operationId,用于在执行过程中允许目标合约通过 AccessManager.onlyAuthorized() 检查是否“正在被 AccessManager 安全代理调用”。
     */
    bytes32 private _executionId;

    /**
     * 函数修饰器:只允许“经过授权”的调用者才能调用函数
     */
    modifier onlyAuthorized() {
        _checkAuthorized();
        _;
    }

    /**
     * 合约部署时,自动调用
     * @param initialAdmin
     */
    constructor(address initialAdmin) {
        // 禁止将零地址设为管理员
        if (initialAdmin == address(0)) {
            revert AccessManagerInvalidInitialAdmin(address(0));
        }

        // 授予初始管理员角色(直接生效,无延迟)
        // 即:部署合约后,初始管理员可以立即执行所有管理操作(如分配角色、设置目标限制等),无等待
        _grantRole(ADMIN_ROLE, initialAdmin, 0, 0);
    }

    /**
     * 判断某个 caller 是否能调用某个合约(target)的某个函数(由 selector 表示),并返回:
     *  ① immediate: 是否可以立即执行;
     *  ② delay: 若不能立即执行,需要延迟多少时间。
     */
    function canCall(
        address caller,
        address target,
        bytes4 selector
    ) public view virtual returns (bool immediate, uint32 delay) {
        // 判断目标合约是否已“关闭”
        if (isTargetClosed(target)) {
            // 如果是关闭状态,任何人都不能调用,直接返回 (false, 0)
            return (false, 0);
            // 判断调用者是否为 AccessManager 自身
        } else if (caller == address(this)) {
            /**
             * 如果调用者是 AccessManager 本身,那代表这是通过内部的 execute() 方法进行的受控调用。
             * 此时,execute() 已经预先检查过权限了,因此:
             *  _isExecuting(target, selector) 是一个确认该调用是在受控范围内执行的检查。
             * 如果为 true 表示可以执行,返回 (true, 0);否则为 (false, 0)。
             */
            return (_isExecuting(target, selector), 0);
        } else {
            uint64 roleId = getTargetFunctionRole(target, selector); // 获取调用该函数所需的角色 ID 【绑定关系: 目标合约 + 函数 -> roleId】
            (bool isMember, uint32 currentDelay) = hasRole(roleId, caller); // 检查当前调用者是否是该 roleId 的成员
            return isMember ? (currentDelay == 0, currentDelay) : (false, 0);
        }
    }

    // 返回角色有效期
    function expiration() public view virtual returns (uint32) {
        return 1 weeks; // 604800 秒
    }

    /**
     * 返回系统允许的最小“冷静期”延迟
     *  ※ 用途:这个冷静期可能被用在设置执行延迟或权限延迟变更的操作中,确保关键权限操作不能立刻生效,从而留给治理或管理员时间反应。
     *  ※ 意义:安全保障机制,比如某人试图快速提权,系统会强制延迟执行
     */
    function minSetback() public view virtual returns (uint32) {
        return 5 days;
    }

    /**
     * 查询某个目标合约是否被“关闭”
     * 含义:如果某个目标合约被关闭,则表示不允许再通过 AccessManager 管理调用(即 canCall 将拒绝所有请求)。
     * 实际用途:管理员可以永久冻结某个目标合约的所有受控权限,达到“废止权限控制”的效果
     * 合约仍然可以被调用,但不会经过 AccessManager 的权限检查
     */
    function isTargetClosed(address target) public view virtual returns (bool) {
        return _targets[target].closed;
    }

    /**
     * 返回某个目标合约(target)上某个函数(用 selector 标识)所需要的角色 ID
     * 关键理解:一个函数只能被具有对应 roleId(角色) 的账户调用
     */
    function getTargetFunctionRole(
        address target,
        bytes4 selector
    ) public view virtual returns (uint64) {
        return _targets[target].allowedRoles[selector];
    }

    /**
     * 获取指定目标合约(target)的“管理员延迟”(admin delay)
     */
    function getTargetAdminDelay(
        address target
    ) public view virtual returns (uint32) {
        return _targets[target].adminDelay.get();
    }

    /**
     * 获取某个角色的管理员角色
     */
    function getRoleAdmin(uint64 roleId) public view virtual returns (uint64) {
        return _roles[roleId].admin;
    }

    /**
     * 获取 roleId 的 guardian(监护人)角色
     */
    function getRoleGuardian(
        uint64 roleId
    ) public view virtual returns (uint64) {
        return _roles[roleId].guardian;
    }

    /**
     * 获取 roleId 的授权延迟(grantDelay)
     */
    function getRoleGrantDelay(
        uint64 roleId
    ) public view virtual returns (uint32) {
        return _roles[roleId].grantDelay.get();
    }

    /**
     * 返回某个 account 拥有某个 roleId 的详细权限信息,包括授权时间、当前延迟、待定延迟和延迟生效时间
     */
    function getAccess(
        uint64 roleId,
        address account
    )
        public
        view
        virtual
        returns (
            uint48 since,
            uint32 currentDelay,
            uint32 pendingDelay,
            uint48 effect
        )
    {
        // access 是某个用户对某个角色的访问记录,信息包含:
        // ① since:用户拥有该角色的起始时间(UNIX 时间戳)。
        // ② delay:延迟对象(自定义类型),用来管理执行调用时的 delay 策略
        Access storage access = _roles[roleId].members[account];

        since = access.since;
        (currentDelay, pendingDelay, effect) = access.delay.getFull();

        return (since, currentDelay, pendingDelay, effect);
    }

    /**
     * 判断某个账户是否拥有某个角色,并返回当前生效的执行延迟
     */
    function hasRole(
        uint64 roleId,
        address account
    ) public view virtual returns (bool isMember, uint32 executionDelay) {
        if (roleId == PUBLIC_ROLE) {
            return (true, 0);
        } else {
            (uint48 hasRoleSince, uint32 currentDelay, , ) = getAccess(
                roleId,
                account
            );
            return (
                hasRoleSince != 0 && hasRoleSince <= Time.timestamp(),
                currentDelay
            );
        }
    }

    /**
     * 为指定角色设置一个 label(用于展示、标识),不影响权限逻辑,仅用于界面友好性
     */
    function labelRole(
        uint64 roleId,
        string calldata label
    ) public virtual onlyAuthorized {
        if (roleId == ADMIN_ROLE || roleId == PUBLIC_ROLE) {
            revert AccessManagerLockedRole(roleId);
        }
        emit RoleLabel(roleId, label);
    }

    /**
     * 授予 account 一个指定的 roleId,并设定该角色下调用目标函数时的 executionDelay
     * 底层调用_grantRole 函数
     *  ① getRoleGrantDelay 获取该角色设定的“授予延迟”,用于控制何时该账户正式拥有角色权限。
     *  ② executionDelay 是未来实际执行操作时所使用的 delay,表示角色调用敏感函数是否必须等待
     */
    function grantRole(
        uint64 roleId,
        address account,
        uint32 executionDelay
    ) public virtual onlyAuthorized {
        _grantRole(roleId, account, getRoleGrantDelay(roleId), executionDelay);
    }

    /**
     * 撤销 account 对某 roleId 的访问权
     */
    function revokeRole(
        uint64 roleId,
        address account
    ) public virtual onlyAuthorized {
        _revokeRole(roleId, account);
    }

    /**
     * 调用者自己放弃对某个 roleId 的角色权限
     */
    function renounceRole(
        uint64 roleId,
        address callerConfirmation
    ) public virtual {
        // 用户主动“确认”自己正在撤销角色权限,即:是我自己想要放弃这个角色
        if (callerConfirmation != _msgSender()) {
            revert AccessManagerBadConfirmation();
        }
        _revokeRole(roleId, callerConfirmation);
    }

    /**
     * 为某个 roleId 设置 管理员角色,也就是说,只有这个 admin 角色的人才能授权/撤销此 roleId 的权限
     */
    function setRoleAdmin(
        uint64 roleId,
        uint64 admin
    ) public virtual onlyAuthorized {
        _setRoleAdmin(roleId, admin);
    }

    /**
     * 为 roleId 设置 Guardian(守护者)角色
     * guardian 的职责是在某些紧急或重要情况下冻结、延迟或否决对该角色的修改
     */
    function setRoleGuardian(
        uint64 roleId,
        uint64 guardian
    ) public virtual onlyAuthorized {
        _setRoleGuardian(roleId, guardian);
    }

    /**
     * 设置某个角色被授予时的延迟(授予延迟 grantDelay),即授予该角色时,要过多长时间才会真正生效
     * 实现 延迟授权机制:一旦授权发起,不会立刻生效,必须等待设定的时间
     */
    function setGrantDelay(
        uint64 roleId,
        uint32 newDelay
    ) public virtual onlyAuthorized {
        _setGrantDelay(roleId, newDelay);
    }

    /**
     * 授予角色
     * 向 account 授予 roleId 角色,并设定
     *  ① grantDelay: 从现在开始,要等待多久才能正式生效;
     *  ② executionDelay: 授予角色生效后,该账号调用受保护函数时所需的等待时间
     * 并返回该账号是否是第一次授权
     */
    function _grantRole(
        uint64 roleId,
        address account,
        uint32 grantDelay,
        uint32 executionDelay
    ) internal virtual returns (bool) {
        // 禁止对 PUBLIC_ROLE 授权
        if (roleId == PUBLIC_ROLE) {
            revert AccessManagerLockedRole(roleId);
        }
        // 判断该用户是否是新成员(即从未被授予过该角色)
        // since == 0: 表示 account 没有被授予过该角色,是新成员
        bool newMember = _roles[roleId].members[account].since == 0;
        // 声明记录角色正式“生效”的时间戳
        uint48 since;
        // 新成员处理
        if (newMember) {
            since = Time.timestamp() + grantDelay; // 设置生效时间为:当前区块时间 + 授权延迟
            // 用 Access 结构记录该成员的状态
            _roles[roleId].members[account] = Access({
                since: since,
                delay: executionDelay.toDelay()
            });
        } else {
            // 老成员处理(权限调整)
            // 对已存在的成员,只更新 executionDelay,不会重置整个状态
            // 若想“重置”,可手动执行 revoke + grant。
            (_roles[roleId].members[account].delay, since) = _roles[roleId]
                .members[account]
                .delay
                .withUpdate(executionDelay, 0);
        }
        // 发出授权事件
        emit RoleGranted(roleId, account, executionDelay, since, newMember);
        return newMember;
    }

    /**
     * 撤销角色
     * 从 account 身上撤销某个角色(即清除掉其在 _roles 中的数据)
     */
    function _revokeRole(
        uint64 roleId,
        address account
    ) internal virtual returns (bool) {
        // 禁止对 PUBLIC_ROLE 撤销角色
        if (roleId == PUBLIC_ROLE) {
            revert AccessManagerLockedRole(roleId);
        }
        // 该用户从未被授予过该角色,不需要撤销,直接返回 false
        if (_roles[roleId].members[account].since == 0) {
            return false;
        }
        // 删除 account 在该角色下的所有信息,彻底移除
        delete _roles[roleId].members[account];
        // 发出事件表示撤销成功
        emit RoleRevoked(roleId, account);
        return true;
    }

    /**
     * 为某个角色设置管理员角色,只有管理员拥有授予/撤销该角色权限的能力。
     */
    function _setRoleAdmin(uint64 roleId, uint64 admin) internal virtual {
        // 禁止对 ADMIN_ROLE 和 PUBLIC_ROLE 设置管理员
        if (roleId == ADMIN_ROLE || roleId == PUBLIC_ROLE) {
            revert AccessManagerLockedRole(roleId);
        }
        // 设置 roleId 的管理员
        _roles[roleId].admin = admin;

        emit RoleAdminChanged(roleId, admin);
    }

    /**
     * 为某个角色设置一个“监护人”角色,其成员可对 executionDelay 等关键参数进行“快速回退操作”
     * 监护角色的设计是为了更高安全性的 多签监管或延迟变更确认。
     */
    function _setRoleGuardian(uint64 roleId, uint64 guardian) internal virtual {
        if (roleId == ADMIN_ROLE || roleId == PUBLIC_ROLE) {
            revert AccessManagerLockedRole(roleId);
        }

        _roles[roleId].guardian = guardian;

        emit RoleGuardianChanged(roleId, guardian);
    }

    /**
     * 设定某角色从“授权”到“正式生效”的延迟时间
     * 用于阻止攻击者瞬间自我授权
     */
    function _setGrantDelay(uint64 roleId, uint32 newDelay) internal virtual {
        if (roleId == PUBLIC_ROLE) {
            revert AccessManagerLockedRole(roleId);
        }

        uint48 effect;
        (_roles[roleId].grantDelay, effect) = _roles[roleId]
            .grantDelay
            .withUpdate(newDelay, minSetback()); // 这里的 minSetback() 是为了防止攻击者瞬间自我授权

        emit RoleGrantDelayChanged(roleId, newDelay, effect);
    }

    /**
     * 给一个目标合约(target)的多个函数(selectors[])统一设置需要的角色 roleId
     */
    function setTargetFunctionRole(
        address target,
        bytes4[] calldata selectors,
        uint64 roleId
    ) public virtual onlyAuthorized {
        for (uint256 i = 0; i < selectors.length; ++i) {
            _setTargetFunctionRole(target, selectors[i], roleId);
        }
    }

    /**
     * 给一个目标合约(target)的一个函数(selector)设置需要的角色 roleId
     * 实际更新 _targets 映射中某个 target 合约的某个函数 selector 所需的角色权限。
     * 发出事件 TargetFunctionRoleUpdated,便于外部监听
     */
    function _setTargetFunctionRole(
        address target,
        bytes4 selector,
        uint64 roleId
    ) internal virtual {
        _targets[target].allowedRoles[selector] = roleId;
        emit TargetFunctionRoleUpdated(target, selector, roleId);
    }

    /**
     * 为目标合约 target 设置“管理员操作延迟”(admin delay)
     * 含义:对该目标合约进行权限修改类操作时,必须等这么长时间之后才可生效
     */
    function setTargetAdminDelay(
        address target,
        uint32 newDelay
    ) public virtual onlyAuthorized {
        _setTargetAdminDelay(target, newDelay);
    }

    function _setTargetAdminDelay(
        address target,
        uint32 newDelay
    ) internal virtual {
        uint48 effect;
        (_targets[target].adminDelay, effect) = _targets[target]
            .adminDelay
            .withUpdate(newDelay, minSetback());

        emit TargetAdminDelayUpdated(target, newDelay, effect);
    }

    /**
     * 设置目标合约是否关闭
     */
    function setTargetClosed(
        address target,
        bool closed
    ) public virtual onlyAuthorized {
        _setTargetClosed(target, closed);
    }

    function _setTargetClosed(address target, bool closed) internal virtual {
        _targets[target].closed = closed;
        emit TargetClosed(target, closed);
    }

    /**
     * 获取某个操作的调度时间
     */
    function getSchedule(bytes32 id) public view virtual returns (uint48) {
        uint48 timepoint = _schedules[id].timepoint;
        return _isExpired(timepoint) ? 0 : timepoint; // 若已经过期:返回 0(表示无效或已过期)
    }

    /**
     * 查询执行次数(版本号)
     */
    function getNonce(bytes32 id) public view virtual returns (uint32) {
        return _schedules[id].nonce;
    }

    /**
     * 判断当前是否正通过 execute() 函数对 target.selector 发起了受控调用
     * AccessManager 会在 execute() 时设置 _executionId = _hashExecutionId(target, selector),然后在 call 执行后再还原它。因此:
     *    如果当前 _executionId 等于传入目标函数的哈希,说明这是一次通过 AccessManager 调度执行的函数调用。
     *    用于 canCall() 中分支判断是否绕过权限验证(即,已由 execute() 统一验证权限)
     */
    function _isExecuting(
        address target,
        bytes4 selector
    ) private view returns (bool) {
        return _executionId == _hashExecutionId(target, selector);
    }

    /**
     * 判断某个 timepoint 是否已经过期
     */
    function _isExpired(uint48 timepoint) private view returns (bool) {
        // expiration() 返回调度任务的有效期,默认为 1 weeks
        // 若当前时间(Time.timestamp())超过 调度时间(timepoint) + expiration(),则该操作视为 过期
        return timepoint + expiration() <= Time.timestamp();
    }

    /**
     * 提取调用数据(calldata)的函数选择器(selector)
     */
    function _checkSelector(bytes calldata data) private pure returns (bytes4) {
        return bytes4(data[0:4]);
    }

    /**
     * 生成一个当前被执行函数调用的唯一标识符
     */
    function _hashExecutionId(
        address target,
        bytes4 selector
    ) private pure returns (bytes32) {
        return keccak256(abi.encode(target, selector));
    }

    /**
     * 检查操作是否已调度
     * 保一个操作不会在未过期的情况下被重复调度(避免重复添加定时操作)
     */
    function _checkNotScheduled(bytes32 operationId) private view {
        uint48 prevTimepoint = _schedules[operationId].timepoint;
        if (prevTimepoint != 0 && !_isExpired(prevTimepoint)) {
            // 未过期
            revert AccessManagerAlreadyScheduled(operationId);
        }
    }

    /**
     * 判断某调用是否允许执行的辅助函数
     *  为 schedule() 和 execute() 提供统一的权限判断入口
     *  单独处理内部函数权限 _canCallSelf
     */
    function _canCallExtended(
        address caller,
        address target,
        bytes calldata data
    ) private view returns (bool immediate, uint32 delay) {
        // 调用目标是 AccessManager 本身, _canCallSelf() 进行特殊权限判断
        if (target == address(this)) {
            return _canCallSelf(caller, data);
        } else {
            return
                data.length < 4
                    ? (false, 0)
                    : canCall(caller, target, _checkSelector(data));
        }
    }

    /**
     * 判断调用者是否有权限执行 AccessManager 自身的某个函数
     */
    function _canCallSelf(
        address caller,
        bytes calldata data
    ) private view returns (bool immediate, uint32 delay) {
        // 无法提取 selector,说明不是有效的函数调用,直接拒绝
        if (data.length < 4) {
            return (false, 0);
        }
        // 调用者是 AccessManager 自己
        if (caller == address(this)) {
            return (_isExecuting(address(this), _checkSelector(data)), 0);
        }
        // 获取函数的 Admin 权限
        (
            bool adminRestricted,
            uint64 roleId,
            uint32 operationDelay
        ) = _getAdminRestrictions(data);

        // 当前调用的函数不是管理函数且AccessManager已关闭,则拒绝调用
        if (!adminRestricted && isTargetClosed(address(this))) {
            return (false, 0);
        }
        // 获取调用者是否有权限
        // 通过 hasRole() 函数检查 caller 是否拥有 roleId 角色
        // 该函数会返回两个值:
        // ① inRole:是否拥有该角色
        // ② executionDelay:该角色的执行延迟
        (bool inRole, uint32 executionDelay) = hasRole(roleId, caller);
        if (!inRole) {
            return (false, 0);
        }

        // 计算最终的延迟时间 (函数级延迟(operationDelay)和 角色级延迟(executionDelay) 取最大值)
        delay = uint32(Math.max(operationDelay, executionDelay));
        return (delay == 0, delay);
    }

    /**
     * 分析某个函数调用的管理权限要求(即是否为管理员操作)和其延迟要求
     */
    function _getAdminRestrictions(
        bytes calldata data
    )
        private
        view
        returns (
            bool adminRestricted,
            uint64 roleAdminId,
            uint32 executionDelay
        )
    {
        // 无法提取 selector,说明不是有效的函数调用,直接拒绝
        if (data.length < 4) {
            return (false, 0, 0);
        }
        // 提取函数选择器
        bytes4 selector = _checkSelector(data);

        // 当前的调用函数是 labelRole、setRoleAdmin、setRoleGuardian、setGrantDelay、setTargetAdminDelay,只能由 ADMIN_ROLE 调用,且不需要额外的delay
        if (
            selector == this.labelRole.selector ||
            selector == this.setRoleAdmin.selector ||
            selector == this.setRoleGuardian.selector ||
            selector == this.setGrantDelay.selector ||
            selector == this.setTargetAdminDelay.selector
        ) {
            return (true, ADMIN_ROLE, 0);
        }

        // 当前调用函数是 updateAuthority、setTargetClosed、setTargetFunctionRole,只能由目标合约(target)的 ADMIN_ROLE 调用
        if (
            selector == this.updateAuthority.selector ||
            selector == this.setTargetClosed.selector ||
            selector == this.setTargetFunctionRole.selector
        ) {
            // data[0x04:0x24] 是函数第一个参数的位置.
            address target = abi.decode(data[0x04:0x24], (address));
            uint32 delay = getTargetAdminDelay(target);
            return (true, ADMIN_ROLE, delay);
        }

        // 当前调用函数是 grantRole、revokeRole,只能由 rodeId 的 admin 调用
        if (
            selector == this.grantRole.selector ||
            selector == this.revokeRole.selector
        ) {
            uint64 roleId = abi.decode(data[0x04:0x24], (uint64));
            return (true, getRoleAdmin(roleId), 0);
        }
        // 其他
        return (false, getTargetFunctionRole(address(this), selector), 0);
    }

    /**
     * 延迟执行机制的实现
     * 实现:授权用户对某个函数调用进行“排队调度(schedule)”的功能
     * @param target 目标合约地址
     * @param data 目标合约函数调用数据(包含 selector + 参数)
     * @param when 请求执行的时间戳
     * @return operationId 该操作的唯一标识符
     * @return nonce 该操作的 nonce (同一个 operationId 被多次调度时用于区分)
     *
     */
    function schedule(
        address target,
        bytes calldata data,
        uint48 when
    ) public virtual returns (bytes32 operationId, uint32 nonce) {
        address caller = _msgSender(); // 发起当前调用请求的账户
        // 获取该调用的延迟限制(检查此 caller 对目标合约(target)中某个函数(data)的权限配置,返回该调用所需的延迟 setback)
        // setback == 0,代表调用者没有调度该操作的权限(或权限立即生效)
        (, uint32 setback) = _canCallExtended(caller, target, data);

        uint48 minWhen = Time.timestamp() + setback;

        // 检查调用是否合法
        // 若 setback == 0:不允许调度(比如该函数必须立即执行或完全不允许)
        // 若 when < minWhen:调度时间早于所需延迟,禁止执行
        if (setback == 0 || (when > 0 && when < minWhen)) {
            revert AccessManagerUnauthorizedCall(
                caller,
                target,
                _checkSelector(data)
            );
        }

        // 修正when
        when = uint48(Math.max(when, minWhen)); // cast is safe: both inputs are uint48

        // 调用者 + 目标 + 函数数据 打包哈希,生成此操作的唯一标识符
        operationId = hashOperation(caller, target, data);

        // 检查该操作是否已经被调度过 (防止相同操作被重复调度)
        _checkNotScheduled(operationId);
        // 计算新的 nonce 并记录调度信息
        unchecked {
            nonce = _schedules[operationId].nonce + 1;
        }
        // 更新调度信息
        _schedules[operationId].timepoint = when;
        _schedules[operationId].nonce = nonce;
        emit OperationScheduled(operationId, nonce, when, caller, target, data);
    }

    /**
     * 实际执行调度或立即授权的函数调用 (核心执行入口)
     * @param target
     * @param data
     */
    function execute(
        address target,
        bytes calldata data
    ) public payable virtual returns (uint32) {
        address caller = _msgSender();

        // 获取该调用的延迟限制
        // 检查此 caller 对目标合约(target)中某个函数(data)的权限配置,
        // 返回该调用所需的延迟 setback, 是否立即执行 immediate
        (bool immediate, uint32 setback) = _canCallExtended(
            caller,
            target,
            data
        );

        // 检查调用是否合法
        // setback == 0,代表调用者没有调度该操作的权限(或权限立即生效)
        // immediate == false,代表调用者没有立即执行的权限
        if (!immediate && setback == 0) {
            revert AccessManagerUnauthorizedCall(
                caller,
                target,
                _checkSelector(data)
            );
        }
        // 调用者 + 目标 + 函数数据 打包哈希,生成此操作的唯一标识符
        bytes32 operationId = hashOperation(caller, target, data);
        uint32 nonce;
        // 只要设置了 delay,或者之前已经 schedule 过,就需要“消耗掉”一次调度
        if (setback != 0 || getSchedule(operationId) != 0) {
            nonce = _consumeScheduledOp(operationId);
        }

        //
        bytes32 executionIdBefore = _executionId;
        _executionId = _hashExecutionId(target, _checkSelector(data));

        // 真实调用目标函数
        Address.functionCallWithValue(target, data, msg.value);

        // 恢复 _executionId 状态
        _executionId = executionIdBefore;

        return nonce;
    }

    /**
     * 取消一个已经被调度(scheduled)但尚未执行的操作
     */
    function cancel(
        address caller,
        address target,
        bytes calldata data
    ) public virtual returns (uint32) {
        address msgsender = _msgSender(); // 发起当前调用请求的账户
        bytes4 selector = _checkSelector(data); // 提取函数选择器
        // 计算操作的唯一标识符
        bytes32 operationId = hashOperation(caller, target, data);
        if (_schedules[operationId].timepoint == 0) {
            // 操作未被调度
            revert AccessManagerNotScheduled(operationId);
        } else if (caller != msgsender) {
            // 操作的发起者不是当前调用者,那就只有 管理员 或 该操作角色的监护人 可以取消操作
            (bool isAdmin, ) = hasRole(ADMIN_ROLE, msgsender);
            (bool isGuardian, ) = hasRole(
                getRoleGuardian(getTargetFunctionRole(target, selector)),
                msgsender
            );
            if (!isAdmin && !isGuardian) {
                revert AccessManagerUnauthorizedCancel(
                    msgsender,
                    caller,
                    target,
                    selector
                );
            }
        }

        delete _schedules[operationId].timepoint; // 删除调度记录:调用取消时只删除调度时间点,保留 nonce
        uint32 nonce = _schedules[operationId].nonce;
        emit OperationCanceled(operationId, nonce);

        return nonce;
    }

    /**
     * 用于“消费”之前已经调度好(schedule)的操作
     */
    function consumeScheduledOp(
        address caller,
        bytes calldata data
    ) public virtual {
        address target = _msgSender(); // 发起当前调用请求的账户
        // target 必须实现 IAccessManaged 接口,并且其 isConsumingScheduledOp() 函数必须返回 IAccessManaged.isConsumingScheduledOp.selector。
        // 否则认为 target 并非在“调度执行流程”中,直接拒绝消费,避免恶意绕过调度机制。
        if (
            IAccessManaged(target).isConsumingScheduledOp() !=
            IAccessManaged.isConsumingScheduledOp.selector
        ) {
            revert AccessManagerUnauthorizedConsume(target);
        }
        _consumeScheduledOp(hashOperation(caller, target, data));
    }

    function _consumeScheduledOp(
        bytes32 operationId
    ) internal virtual returns (uint32) {
        uint48 timepoint = _schedules[operationId].timepoint; // 操作的调度时间
        uint32 nonce = _schedules[operationId].nonce; // 操作的版本号

        if (timepoint == 0) {
            // 该操作未被调度,直接抛出异常
            revert AccessManagerNotScheduled(operationId);
        } else if (timepoint > Time.timestamp()) {
            // 调度时间还未到达,直接抛出异常
            revert AccessManagerNotReady(operationId);
        } else if (_isExpired(timepoint)) {
            // 调度时间已过期,直接抛出异常
            revert AccessManagerExpired(operationId);
        }

        delete _schedules[operationId].timepoint; // 正常消费成功后,清除调度信息(timepoint),但 保留 nonce(避免重放攻击或重复执行)
        emit OperationExecuted(operationId, nonce);

        return nonce;
    }

    /**
     * 计算某个操作的哈希值
     * @param caller 调用者地址
     * @param target 目标合约地址
     * @param data 目标合约函数调用数据(包含 selector + 参数)
     */
    function hashOperation(
        address caller,
        address target,
        bytes calldata data
    ) public view virtual returns (bytes32) {
        return keccak256(abi.encode(caller, target, data));
    }

    function updateAuthority(
        address target,
        address newAuthority
    ) public virtual onlyAuthorized {
        IAccessManaged(target).setAuthority(newAuthority);
    }

    /**
     *
     */
    function _checkAuthorized() private {
        address caller = _msgSender(); // 发起当前调用请求的账户
        // 判断 caller 是否对当前合约发起的这个调用(通过 msgData 区分方法)
        (bool immediate, uint32 delay) = _canCallSelf(caller, _msgData());
        if (!immediate) {
            // 不立即执行
            if (delay == 0) {
                // delay == 0:说明完全没有授权,也不允许延迟执行
                (, uint64 requiredRole, ) = _getAdminRestrictions(_msgData());
                revert AccessManagerUnauthorizedAccount(caller, requiredRole); // 抛出错误:调用者缺少某个权限角色
            } else {
                // 调用者可以延迟执行该操作,且之前可能已经通过 schedule() 调度过操作
                _consumeScheduledOp(
                    hashOperation(caller, address(this), _msgData())
                );
            }
        }
    }
}
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Henry Wei
Henry Wei
Web3 探索者