Function Modifier

发布于:2025-07-23 ⋅ 阅读:(15) ⋅ 点赞:(0)

本节是《Solidity by Example》的中文翻译与深入讲解,专为零基础或刚接触区块链开发的小白朋友打造。我们将通过“示例 + 解说 + 提示”的方式,带你逐步理解每一段 Solidity 代码的实际用途与背后的逻辑。

Solidity 是以太坊等智能合约平台使用的主要编程语言,就像写网页要用 HTML 和 JavaScript,写智能合约就需要会 Solidity。

如果你从没写过区块链代码也没关系,只要你了解一点点编程概念,比如“变量”“函数”“条件判断”,我们就能从最简单的例子开始,一步步建立你的 Solidity 编程思维。

Function Modifier

函数修饰器
修饰器是一段可以在函数调用之前和/或之后运行的代码。

修饰器可用于:

  • 限制访问权限
  • 验证输入
  • 防止重入攻击

什么是 Solidity 函数修饰器?

函数修饰器(Modifier)是 Solidity 中一种代码复用机制,允许在函数执行前后插入逻辑,用于检查条件或执行额外操作。

  • 比喻:修饰器像门禁系统,在你进入房间(调用函数)前检查身份或条件,离开时可能清理现场。
  • 修饰器通过 _; 占位符指示函数主体的执行位置,_; 前后分别运行前置和后置逻辑。

修饰器的用途

  • 限制访问权限:如 onlyOwner 修饰器,确保只有合约拥有者能调用函数。
  • 验证输入:如 validAddress 修饰器,检查输入地址是否有效。
  • 防止重入攻击:如 noReentrancy 修饰器,锁定函数防止重复调用(重入)。
// SPDX-License-Identifier: MIT
// 使用 MIT 许可证,允许自由使用、修改和分发代码。

pragma solidity ^0.8.26;
// 指定 Solidity 编译器版本,必须为 0.8.26 或更高(但低于 0.9.0)。

contract FunctionModifier {
    // 定义一个名为 FunctionModifier 的智能合约,展示函数修饰器的用法。

    // We will use these variables to demonstrate how to use
    // modifiers.
    // 我们将使用这些变量来展示如何使用修饰器。
    address public owner;
    // 声明一个公共状态变量 owner,存储合约拥有者的地址(storage)。
    // public 自动生成 getter 函数(owner())。

    uint256 public x = 10;
    // 声明一个公共状态变量 x,初始值为 10(storage),用于测试修饰器效果。

    bool public locked;
    // 声明一个公共状态变量 locked,初始值为 false(storage),用于防止重入攻击。

    constructor() {
        // Set the transaction sender as the owner of the contract.
        // 将交易发送者设置为合约的拥有者。
        owner = msg.sender;
        // 在合约部署时,将调用者的地址(msg.sender)赋给 owner。
        // msg.sender 是部署合约的账户地址。
    }

    // Modifier to check that the caller is the owner of
    // the contract.
    // 检查调用者是否为合约拥有者的修饰器。
    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        // 使用 require 检查调用者(msg.sender)是否为 owner。
        // 如果不是,抛出错误“Not owner”,交易回滚。
        // Underscore is a special character only used inside
        // a function modifier and it tells Solidity to
        // execute the rest of the code.
        // 下划线是修饰器中使用的特殊字符,指示 Solidity 执行后续代码。
        _;
        // _; 表示执行被修饰函数的主体代码。
        // 本修饰器只有前置逻辑(检查权限),无后置逻辑。
    }

    // Modifiers can take inputs. This modifier checks that the
    // address passed in is not the zero address.
    // 修饰器可以接受输入参数。此修饰器检查传入的地址不是零地址。
    modifier validAddress(address _addr) {
        require(_addr != address(0), "Not valid address");
        // 使用 require 检查输入地址 _addr 是否为零地址(address(0))。
        // 如果是,抛出错误“Not valid address”,交易回滚。
        _;
        // _; 表示执行被修饰函数的主体代码。
        // 本修饰器只有前置逻辑(检查地址有效性)。
    }

    function changeOwner(address _newOwner) public onlyOwner validAddress(_newOwner)
    {
        // 定义一个公共函数 changeOwner,接受新拥有者地址 _newOwner。
        // public:可被外部和内部调用。
        // 应用 onlyOwner 和 validAddress 修饰器:
        // - onlyOwner:确保只有当前 owner 能调用。
        // - validAddress:确保 _newOwner 非零地址。
        owner = _newOwner;
        // 将 owner 更新为 _newOwner,修改 storage,消耗 Gas。
    }

    // Modifiers can be called before and / or after a function.
    // This modifier prevents a function from being called while
    // it is still executing.
    // 修饰器可以在函数调用前和/或后执行。
    // 此修饰器防止函数在执行期间被再次调用。
    modifier noReentrancy() {
        require(!locked, "No reentrancy");
        // 使用 require 检查 locked 是否为 false。
        // 如果 locked 为 true(函数正在执行),抛出错误“No reentrancy”,防止重入。
        locked = true;
        // 设置 locked 为 true,表示函数进入执行状态,锁定合约。
        _;
        // _; 表示执行被修饰函数的主体代码。
        locked = false;
        // 函数执行后,设置 locked 为 false,释放锁。
        // 本修饰器有前置逻辑(检查锁并加锁)和后置逻辑(解锁)。
    }

    function decrement(uint256 i) public noReentrancy {
        // 定义一个公共函数 decrement,接受参数 i,减少状态变量 x。
        // 应用 noReentrancy 修饰器,防止重入攻击。
        x -= i;
        // 将状态变量 x 减去 i,修改 storage,消耗 Gas。

        if (i > 1) {
            decrement(i - 1);
            // 如果 i > 1,递归调用 decrement,传入 i-1。
            // noReentrancy 修饰器确保递归调用不会导致重入。
        }
    }
}

FunctionModifier 合约展示修饰器的三种主要用途:

  • 限制访问onlyOwner 修饰器确保只有合约拥有者能调用函数。
  • 验证输入validAddress 修饰器检查输入地址非零。
  • 防止重入noReentrancy 修饰器通过锁机制防止重入攻击。
  • 合约包含状态变量(ownerxlocked)和两个函数(changeOwnerdecrement),演示修饰器的实际应用。

函数修饰器的本质

  • 函数修饰器是 Solidity 中一种代码复用工具,允许在函数执行前后插入逻辑,用于检查条件、限制访问或执行清理操作。
  • 核心功能:
    • 限制访问:如 onlyOwner,确保只有特定用户(如合约拥有者)能调用函数。
    • 验证输入:如 validAddress,检查输入参数有效性(如非零地址)。
    • 防止重入:如 noReentrancy,通过锁机制防止函数被重复调用(重入攻击)。
  • 优势:
    • 代码复用:将通用逻辑(如权限检查)封装到修饰器,避免重复编写。
    • 清晰性:使函数逻辑更简洁,检查条件集中管理。
    • 安全性:防止未授权访问或重入攻击。

代码功能

  • 合约 FunctionModifier
    • 状态变量:
      • owner:存储合约拥有者地址,初始化为部署者(msg.sender)。
      • x:存储一个计数器,初始值为 10,用于测试函数效果。
      • locked:布尔值,初始为 false,用于防止重入攻击。
    • 修饰器:
      • onlyOwner:检查调用者是否为 owner,否则抛出错误“Not owner”。
      • validAddress:检查输入地址是否非零,否则抛出错误“Not valid address”。
      • noReentrancy:检查并设置锁(locked),防止函数重入,执行后解锁。
    • 函数:
      • changeOwner:更新 owner 地址,需通过 onlyOwnervalidAddress 检查。
      • decrement:减少 x,支持递归调用,noReentrancy 防止重入攻击。

函数修饰器的注意事项

  • 执行顺序:
    • 多个修饰器按声明顺序执行(如 onlyOwner validAddress 先检查权限再验证地址)。
    • _; 前为前置逻辑,后为后置逻辑(如 noReentrancy 的加锁和解锁)。
  • Gas 成本:
    • 修饰器增加检查逻辑(如 require),消耗少量 Gas。
    • 修改 storage(如 locked)消耗约 20,000+ Gas,可用瞬态存储优化(参考历史对话)。
    • 错误抛出(如“Not owner”)消耗少量 Gas,但尽早抛出节省后续计算。
  • 安全性:
    • 检查关键条件(如 msg.senderaddress(0)),避免未授权访问。
    • 重入保护(如 noReentrancy)需确保锁状态正确重置。
    • 修饰器逻辑需简单,避免引入复杂 bug。
  • 局限性:
    • 修饰器不能直接访问函数参数或返回值,需通过输入参数(如 validAddress(address _addr))。
    • 递归调用(如 decrement)需确保修饰器(如 noReentrancy)支持。

网站公告

今日签到

点亮在社区的每一天
去签到