惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
博客园_首页
H
Hackread – Cybersecurity News, Data Breaches, AI and More
T
ThreatConnect
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 聂微东
H
Help Net Security
T
Threat Research - Cisco Blogs
Blog — PlanetScale
Blog — PlanetScale
A
Arctic Wolf
G
Google Developers Blog
量子位
U
Unit 42
I
InfoQ
V
V2EX
F
Fox-IT International blog
P
Privacy & Cybersecurity Law Blog
V
Visual Studio Blog
J
Java Code Geeks
大猫的无限游戏
大猫的无限游戏
C
CERT Recently Published Vulnerability Notes
博客园 - 三生石上(FineUI控件)
T
The Exploit Database - CXSecurity.com
T
Tailwind CSS Blog
SecWiki News
SecWiki News
Know Your Adversary
Know Your Adversary
MyScale Blog
MyScale Blog
宝玉的分享
宝玉的分享
The Hacker News
The Hacker News
Project Zero
Project Zero
Application and Cybersecurity Blog
Application and Cybersecurity Blog
月光博客
月光博客
Recent Commits to openclaw:main
Recent Commits to openclaw:main
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
G
GRAHAM CLULEY
C
Cisco Blogs
I
Intezer
Simon Willison's Weblog
Simon Willison's Weblog
O
OpenAI News
Recorded Future
Recorded Future
T
Tenable Blog
W
WeLiveSecurity
腾讯CDC
Stack Overflow Blog
Stack Overflow Blog
T
The Blog of Author Tim Ferriss
www.infosecurity-magazine.com
www.infosecurity-magazine.com
D
Docker
C
Cybersecurity and Infrastructure Security Agency CISA
PCI Perspectives
PCI Perspectives

文章列表

我是如何解决将 c++ 编译成可以在 node.js 中使用的 *.node,中间出现的一大堆问题的(指纹浏览器基石篇) eSIM Plus 爱沙尼亚手机号彻底翻车?“永久有效”悄然变成了一年! 接码平台 SMS-Activate 余额可以转移到新平台使用,截止日期:2026年1月29日 是时候将 hugo-theme-kiwi 主题提交到 themes.gohugo.io 站点上了 Flux2 刚开源就凉了?Z-Image 本地部署狠狠打了个样 声音的未来:Chatterbox —— 用「夸张度旋钮」提升表现力的开源 TTS 向导 还以为那只是换个背景?Qwen-Image-Edit 在 ComfyUI 中能做到更离谱的事 Windows 结合最新版 ComfyUI 部署阿里最新开源的 Qwen-Image 图像大模型 从零样本到跨场景:Seed-VC语音转换技术的革命性突破 大语音模型轻量化革命:MegaTTS3 如何重新定义文本生成语音的技术边界(windows篇) 竞赛级编程大模型OlympicCoder-7B之本地部署(Windows篇) 阿里开源了端到端全模态大模型Qwen-2.5-Omini-7B之本地部署(windows篇) 语音识别之whisper本地部署(实时语音之开篇) 甭管是个人还是企业都能部署的Mistral-Small3.1,远超同级别的模型 文生音乐开源项目DiffRhythm,8G显存本地部署之Windows篇 阿里QwQ-32B本地部署指南:用Ollama轻松运行320亿参数大模型 基于Qwen2.5大模型的Spark-TTS,零样本语音克隆,CPU可运行之本地部署(Windows篇) 智谱开源了文生图CogView4-6B模型,支持中文提示词之本地部署(Windows篇) 基于歌词生成整首歌的开源AI音乐模型,支持中、英、日、韩等多种语言,本地化部署YuE(windows篇) 阿里云开源的文生视频万相 Wan2.1之本地部署Wan2.1-T2V-1.3B模型 互动式开源AI图像编辑神器,Windows11本地部署 MagicQuill 本地部署Qwen2.5-VL-7B-Instruct多模态视觉大模型(Windows篇) 保持角色一致性的绘本生成AI开源项目之Story-Adapter本地部署Windows篇 本地部署 Stable Diffusion 3.5(最新 ComfyUI记录篇) 谁说Win7安装不了Node.js最新版的呢?都2025年,还不更新系统到Win11 vs code远程调试Linux服务器上的php代码 浏览器定制 | Windows11 编译 Chromium 133.0.6885.0(截稿前Chromium最新版之编译篇[一]) 不说是彻底搞懂,至少让你不再惧怕c/c++指针,以及各种奇葩指针变种 解决windows下php8.x及以上版本,在Apache2.4中无法加载CURL扩展的问题 在 Windows8.1 下编译 Chromium (103.0.5060.68 之三) 安装 depot_tools 和 Windows 10 SDK 为在Windows下构建基于 chromium 的浏览器(103.0.5060.68 之二) Windows构建基于 Chromium 的浏览器之环境准备篇安装 Visual Studio(103.0.5060.68 之一) 为什么网站加载速度总是那么不尽如人意呢?(网站优化篇) 海外云服务器安装 Redis 6.2.x (Ubuntu 18.04 记录篇三) 海外云服务器安装 MariaDB 10.6.X (Ubuntu 18.04 记录篇二) 海外云服务器安装 JDK8 (Ubuntu 18.04 记录篇) 虚拟机 Linux 安装 JDK(Vagrant 之二 CentOS7 篇) 怎样快速搭建 Linux 虚拟机呢?(vagrant 篇) 个人站点迁移之gitlab.com pages 绑定自定义域名 不要你掏腰包,就能搭建个人网站之 gitlab.com Pages 托管服务 国内 gitee.com Pages 下线了,致使众多站长纷纷改用其他托管平台 canvas绘制文本时,该如何处理首行缩进、自动换行、多内容以省略号结束、竖排的呢? 其实,低成本甚至免费的,也是可以搭建个人网站的哦! 腾讯云COS托管静态网站,以及如何解决访问出现了下载网页的情况 错误分析 (Machine Learning 研习十九) 多类别分类器(Machine Learning 研习十八) 使用CSS计数器,在目录名称前加上了序号,让目录看起来更加井然有序 使用 golang 以及 Gin 框架,将上传的图片在不保存至本地的情况下添加水印,并上传至阿里云 OSS 绘制特征曲线-ROC(Machine Learning 研习十七) 网站引入 Prism,使得代码高亮显示,并一键复制代码块 精确率(召回率)的权衡(Machine Learning 研习十六) 对模型性能进行评估(Machine Learning 研习十五) 图像识别之入门案例之数字识别(Machine Learning 研习十四) 微调模型——续(Machine Learning 研习之十三) 微调模型(Machine Learning 研习之十二) 解决 github.com port 443: Timed out 的问题 选择和训练模型(Machine Learning 研习之十一) 回望这风雨飘摇的一年过后,我们终将要整束行囊继续前行 一套由 Hugo 驱动的博客主题 hugo-theme-kiwi 开源啦 机器学习中的 Transformation Pipelines(Machine Learning 研习之十) 特征缩放和转换以及自定义Transformers(Machine Learning 研习之九) 为机器学习算法准备数据(Machine Learning 研习之八) 端到端的机器学习项目之探索数据(Machine Learning 研习之七) 端到端的机器学习项目(Machine Learning 研习之六) 机机器学习的测试和验证(Machine Learning 研习之五) 机器学习的主要挑战和任务(Machine Learning 研习之四) 对于大量数据集的解决方案便是在线学习或是增量学习(Machine Learning 研习之三) 现实生活中机器学习的具体示例(Machine Learning 研习之二) 让机器学习不再是过门不入,带您一起详解机器学习(机器学习 Machine Learning 研习之一) 把握住golang中的template,方能驾驭得了Hugo主题的template 云服务器到期,站点迁移,Nginx配置SSL以备后续只需! 玩以太坊链上项目的必备技能(内联汇编 [inline assembly]-Solidity之旅十八) 玩以太坊链上项目的必备技能(库 [library]-Solidity之旅十七) 玩以太坊链上项目的必备技能(Constant 和 Immutable 状态变量-Solidity之旅十六) 玩以太坊链上项目的必备技能(错误处理以及异常-Solidity之旅十四) 玩以太坊链上项目的必备技能(函数及其可见性和状态可变性-Solidity之旅十三) 玩以太坊链上项目的必备技能(事件-Solidity之旅十二) 玩以太坊链上项目的必备技能(OOP-接口-Solidity之旅十一) 玩以太坊链上项目的必备技能(OOP-抽象合约-Solidity之旅十) 玩以太坊链上项目的必备技能(OOP-合约继承-Solidity之旅九) 玩以太坊链上项目的必备技能(流程控制-Solidity之旅八) 玩以太坊链上项目的必备技能(单位以及全局变量-Solidity之旅七) 玩以太坊链上项目的必备技能(基本类型转换以及推断-Solidity之旅六) 玩以太坊链上项目的必备技能(变量作用域-Solidity之旅五) 玩以太坊链上项目的必备技能(类型-映射类型-Solidity之旅四) 玩以太坊链上项目的必备技能(类型-引用类型-Solidity之旅三) 花了不到1块5,玩了下全网最火的ChatGPT 玩以太坊链上项目的必备技能(类型-值类型-Solidity之旅二) 玩以太坊链上项目的必备技能(初识智能合约语言-Solidity之旅一) 在构建 Web3 前,需先知道什么是区块链,毕竟 Web3 是基于区块链 Web3 来了,让我们展开双手拥抱它吧! Go 语言中的 Moduels 管理(Let's Go 三十四) Go 语言中的包(Let's Go 三十三) Go 语言中的错误处理(Let's Go 三十二) Go 语言中的带有缓冲 Channel(Let's Go 三十一) Go 语言中的单向 Channel(Let's Go 三十) Go 语言中的 Channel(Let's Go 二十九) Go 语言中的并发编程(Let's Go 二十八) Go 语言中的空接口(Let's Go 二十七) Go 语言中的类型断言(Let's Go 二十六)
玩以太坊链上项目的必备技能(修改器 [modifier]-Solidity之旅十五)
2022-12-23 · via

修改器(modifier)在 Solidity 中是一种特殊类型的函数,用于修改其它函数的行为。例如,开发人员可以使用修改器来检查在允许函数执行之前是否满足某个条件。

修改器(modifier)

在讲修改器(modifier)之前,我们使用前面几篇文章所学到的知识来实现一个简单的代币类合约。

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;

contract InheritanceModifierExample {
    //账户 => 金额 映射
    mapping(address => uint) public tokenBalance;
    //拥有者
    address owner;

    uint tokenPrice = 1 ether;

    constructor() {
        owner = msg.sender;
        tokenBalance[owner] = 100;
    }

    function createNewToken() public {
        //使用 require 检查是不是合约拥有着
        require(msg.sender == owner, "You are not allowed");
        tokenBalance[owner]++;
    }

    function burnToken() public {
        require(msg.sender == owner, "You are not allowed");
        tokenBalance[owner]--;
    }

    function purchaseToken() public payable {
        //检查账户中的代币是否足够
        require((tokenBalance[owner] * tokenPrice) / msg.value > 0, "not enough tokens");
        tokenBalance[owner] -= msg.value / tokenPrice;
        tokenBalance[msg.sender] += msg.value / tokenPrice;
    }

    function sendToken(address _to, uint _amount) public {
        require(tokenBalance[msg.sender] >= _amount, "Not enough tokens");
        tokenBalance[msg.sender] -= _amount;
        tokenBalance[_to] += _amount;
    }

}

img

我们拷贝部署合约的账户地址0x5B38Da6a701c568545dCfcB03FcB875f56beddC4

切换成其他账户,也拷贝出地址 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2

img

img

查看账户余额是否正确,复制 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2,将其填入 tokenBalance的输入栏,看看余额是否为1。

img

我们再来看看该账户burn with Account(账户烧钱)会发生什么样的状况呢?

img

从上述这个简单的代币类合约,聪慧如您,想必是看出它的不足了。

没错,就是上面合约中的函数都有类似的 require 语句,作为开发者的您会将这些 require 语句抽取到一个函数中,但在 Solidity 却引入了一种特别的函数——修改器(modifier)。

Solidity 修改器(modifier)用来做什么的?

修改器(modifier)在 Solidity 中是一种特殊类型的函数,用于修改其它函数的行为。例如,开发人员可以使用修改器来检查在允许函数执行之前是否满足某个条件。

修改器(modifier)与函数类似,因为它们可以接受参数并有一个返回类型。修改器(modifier)也可以被链在一起,这意味着你可以在一个函数上有多个修改器(modifier)

然而,修改器(modifier)只能修改合约逻辑,不能修改合约的存储,包括结构。修改器(modifier)减少了开发者必须编写模板代码的数量,并且可以使您的 Solidity 代码更加可读。

多个修改器(modifier)由空格分隔。修改器(modifier)的顺序很重要。列表中的第一个修改器(modifier)将被首先执行,第二个修改器(modifier)将被第二次应用,以此类推。

例如,如果您有一个修改器(modifier)检查用户是否经过认证,另一个修改器(modifier)检查用户是否被授权查看某个资源,那么这些修改器(modifier)的应用顺序将决定用户是否能够查看该资源。

Solidity 修改器(modifier)的不同类型

Solidity修改器(modifier)有四大类:闸门检查、先决条件、过滤器和防止重入攻击。

  1. 闸门检查

它在允许一个函数执行之前检查某个条件是否为真。

例如,您可能有一个允许用户从他们的账户中取钱的函数,但在函数执行之前,开发者可能想检查用户的账户中是否有足够的钱来进行取款。这种检查被认为是一个门检查修改器

另一个闸门检查的例子是,在允许用户查看某个资源之前,检查用户是否经过认证的函数。

  1. 先决条件

它为一个函数的执行设置了环境,而不是检查某个条件是否为真。

例如,一个 Solidity 开发者可能会使用一个需要一定数量的以太币来执行函数。在这种情况下,先决条件将是设置以太币平衡的函数。

  1. 滤波器

它检查某个条件是否为真,如果是,则允许函数执行。如果条件不为真,那么该函数将不会执行。

闸门检查不同,即使条件为真,也不会自动允许函数执行,而过滤器将允许函数在条件为真时执行。

  1. 重入式攻击的预防

递归攻击是一种攻击类型,恶意行为者试图通过递归调用多次执行一个函数,以利用它。

例如,想象一下,您有一个允许用户从他们的账户中提款的函数。一个重入式攻击者可能会试图多次调用该函数,以提取比他们账户中实际拥有的更多的钱。

为了防止重入式攻击,您可以使用一个修改器来检查该函数是否被递归调用。如果是,那么该函数将不会执行。

require 和 修改器(modifier)之间的关系

require 经常与修改器互换使用,因为它们都允许您在一个函数执行之前检查某个条件是否为真。如果指定的条件不为真,那么编译器就会抛出一个错误。

例如,下面的语句使用 require 关键字,以便只有所有者才能与一个函数进行交互。

modifier onlyOwner() {
    require(msg.sender == owner);
    _;
}

require 和 修改器(modifier)之间的区别。

1、修改器(modiffier) 可用于为一个函数的执行设置环境(如先决条件的情况下) 2、require 只能用于检查某个条件是否为真 3、修改器(modiffier)` 可以被重写 4、要求不能被覆盖

什么是修改器(modiffier) 重写?

virtual关键字可以用来表示可以在派生合约override(重写)

例如,一个带有修改器(modifier)的合约,而派生合约继承自它。

假使,基合约中的修改器(modifier)被标记了virtual,那么它便可以在派生合约中被override(重写)

继承可以让您扩展合约的属性,在修改器(modifier)的上下文中,继承允许您添加新的(modifier),或覆盖现有的(modifier)

下面的简单实现演示了继承和修改器如何一起工作。

contract A {
    modifier X virtual {
    }
}    

contract B is A {
    modifier X override {
    }
}

在这个例子中,合约 B 继承了合约 A,两个合约都有一个叫做 X的修改器。然而,在合约 B 中,该修饰语被标记为override,这表明它覆盖了合约 A 中的修改器。

如何使用修改器

当使用一个修改器时,您首先需要在合约中定义修改器函数。修改器使用一个特殊的符号_,只有当修改器的条件得到满足时,才会插入函数体。

下面的合同演示了如何使用一个修改器。

contract Owner {
   address public owner = msg.sender;
   modifier onlyOwner {
      require(msg.sender == owner);
      _;
   }
   modifier costs(uint price) {
      if (msg.value >= price) {
         _;
      }
   }
}

在上面的例子中,该合约有两个修改器:onlyOwner costs

第一个修改器检查msg.sender是否是合约的所有者,第二个修改器检查msg.value是否大于或等于某个价格。

这两个修饰符都可以用在合同中的任何函数上。

_ 放在哪里?

_ 被称为合并通配符。它将函数代码与 _ 所在的修改器代码合并。

换句话说,函数的主体(修改器所连接的)将被插入修改器的定义中出现 _ 的地方。

一个修改器必须在其主体内有 _ 才能执行。它是强制性的(如果不是这样的话,Solidity 会抛出一个错误吗)。

您写将 _ 符号的地方将决定该函数是在修改器代码之前、之间还是之后执行。

modifier SomethingBefore {
    require(/* check something first */);
    _; // 继续执行功能
}

modifier SomethingAfter {
    _; // 先运行函数
    require(/* then check something */)
}

如上面的例子所示,您可以把 _ 放在修饰语体的开头、中间或结尾。

在实践中,(特别是在您真正了解修改器的工作原理之前),最安全的使用模式是将 _ 放在最后。在这种情况下,修改器的作用是一致的验证检查,所以要先检查一个条件,然后再继续。下面的代码片段显示了这个例子。

function isOkay() public view returns(bool) {
    // 做一些验证检查
    return true;
}
function isAuthorised(address _user) public view returns(bool) {
    // 逻辑检查以及账户授权
    return true; 
}
modifier OnlyIfOkAndAuthorised {
    require(isOkay());
    require(isAuthorised(msg.sender));
    _;
}

向修改器传递参数

修改器也可以接受参数。像函数一样。

modifier Fee (uint _fee) {
    if (msg.value >= _fee) {
        _;
    }
}

使用上面的例子,您可以确保调用您的一个合约函数的用户(或合约)已经发送了一些以太币来支付预先要求的费用。

让我们用一个简单的例子来说明,这个合约就像一个金库。

您想确保每个想取出储存在合约金库中的钱的用户都要向合约支付最低2.5%的费用。

一个带有参数的修改器可以模拟这种行为。请看下面的代码。

// SPDX-License-Idendifier: GPL-3.0
pragma solidity ^0.8.0;
contract Vault {

    modifier fee(uint _fee) {
        if (msg.value != _fee) {
            revert("You must pay a fee to withdraw your ethers");
        } else {
            _;
        }
    }

    function deposit(address _user, uint _amount) external {
        // ...
    }

    function withdraw(uint _amount) external payable fee(0.025 ether) {
        // ...
    }
 }

修改器参数允许使用任意表达式,在这种情况下,所有从函数中可见的符号都在修改器中可见。

在一个函数上应用多个修改器。

多个修改器可以应用到一个函数,您可以这样做。

contract OwnerContract {    
    address public owner = msg.sender;
    uint public creationTime = now;    

    modifier onlyBy(address _account) {
        require(
            msg.sender == _account,
            "Sender not authorized.
        );
        _;
    }    

    modifier onlyAfter(uint _time) {
        require(
            now >= _time,
            "Function called too early."
        );
        _;
    }    

    function disown() public onlyBy(owner) onlyAfter(creationTime + 6 weeks) {
        delete owner;
    }
 }

修改器将按照它们被定义的顺序执行,所以从左到右。所以在上面的例子中,该函数将在运行前检查以下条件。

onlyBy(...) : 调用合同的地址是否为所有者?
onlyAfter(...) : 是否有超过6周的时间是该人的所有者?

带枚举的修改器

如果您的合约持有一个枚举类型的状态变量,您可以通过传递一个可用的选项作为修改器的参数来检查它持有的值。

enum State { Created, Locked, Inactive }
State state;modifier isState(State _expectedState) {
    require(state == _expectedState);
    _;
}