university.alchemy3
你会注意到,receive 不使用 function 关键字。这是因为它是一个特殊函数(类似 constructor 构造函数)。当合约在未接收任何调用数据(calldata)的情况下收到以太币(ether),或者当调用数据与合约上的任何函数签名都不匹配时,receive 函数就会执行。
receive 函数必须满足以下要求:
- 可见性为
external(外部可见) - 包含
payable修饰符(支持接收以太币) - 不能接收参数
- 不能返回任何值
补充说明(针对Solidity语境):
- calldata:指外部调用合约时传入的数据,主要用于指定调用的函数及参数,是Solidity中常见的特殊数据位置。
- function signature:函数签名,由函数名和参数类型组成(如
transfer(address,uint256)),合约通过匹配签名确定要执行的函数,若无法匹配则触发receive函数(若未定义则交易会失败)。 - 该函数是Solidity中处理“无明确函数调用的以太币转账”的核心机制,常见于接收普通转账、处理合约间默认交互等场景。
外部可见性
我们已经讨论了公共可见性和私有可见性。那么什么是外部可见性呢?为什么接收函数需要外部可见性?
外部函数只能通过来自另一个合约或外部账户(EOA)的消息调用进行调用。相比之下,内部函数调用直接从合约的当前执行上下文中访问变量,无需像调用数据(calldata)这样的外部输入。
接收函数需要外部可见性,因为它的设计目的是通过消息调用接收以太币。其主要作用是为开发者提供一个函数体,在其中可以定义处理合约接收以太币的逻辑。
与此非常相似,回退函数也需要外部可见性。你可能会问,什么是回退函数?朋友,继续往下读吧!
回退函数
回退函数就好比是当其他所有函数都“不接电话”时,你会调用的那个函数!
不开玩笑了,它其实真就这么简单。
当智能合约收到的调用数据与它的任何函数签名都不匹配,或者调用数据为空时,回退函数就会被触发。这可能由多种原因导致,比如函数名拼写错误、参数类型不正确,甚至是为了有意存储调用数据以备将来使用(例如在后续的消息调用中)。如果合约不知道如何响应用户发送给它的数据,它就会调用回退函数。
回退函数也是一个特殊的函数,其形式如下:
contract Contract {
fallback() external {
// 做些事情
}
}
和接收函数一样,回退函数必须是外部的,它不能接受任何参数,也不能返回任何值。但与接收函数不同的是,回退函数不需要是可支付的(payable)。
通过一个可支付的回退函数,你基本上可以替代接收函数,但在大多数情况下,这不太可取。这两个函数有着不同的用途!
当你创建接收函数时,很明显你是在接受没有数据的交易中的以太币。
而当你创建回退函数时,通常是为了处理函数签名错误的情况。
Your Goal: Receive Ether
Add a function to the contract that will allow it to receive ether on a transaction without any calldata.
1 | |
资金转账
我们可以将任意常规函数设置为 payable(可接收以太币)。这样一来,我们就能区分转入智能合约的以太币所对应的不同用途。
举例来说,假设某合约中存储了两个地址,而我们希望能够分别向这两个地址进行转账:
1 | |
我们有两个支付方法 payA 和 payB,它们会将以太币划转至对应的地址。该方法接收一个以Wei为单位的无符号整数(uint)金额,并将这笔资金从合约账户划转至目标地址。
你的目标:划转小费
让我们创建一种向合约所有者支付小费的方式!
创建一个公共的(public)可接收以太币(payable)函数 tip,该函数需将其接收到的所有以太币划转至合约所有者地址。
答案:
1 | |
你的目标:慈善捐赠
让我们把所有传递给receive函数的资金都捐赠给慈善机构。我们将分两步来做。
- 首先,修改构造函数以接受一个新参数:慈善机构地址。
- 接下来,添加一个名为donate的新函数。当调用此函数时,将合约中所有剩余资金转移到慈善机构地址。
1 | |
自毁的影响
当你对合约账户调用selfdestruct(自毁)时,字节码会被清除。该合约将无法再对以太币转账做出响应。
如果你打算使用selfdestruct,你需要确保未来不会有人意外地向你的合约发送以太币。如果真的发生这种情况,可能没有办法收回那些以太币。未来发送到这个地址的资金可能会被永远锁定!
你可能会认为,一旦合约代码从某个地址中被清除,事情就结束了。然而,随着后来CREATE2操作码的引入,你现在能够将相同的代码重新部署到同一个地址。与依赖发送者地址和账户随机数的传统方法不同,CREATE2使用一个salt(盐值)和合约创建代码来确定合约地址。感兴趣吗?可以查看本教程了解更多内容。
你可以考虑设置存储变量,使任何人都无法调用函数,而不是让合约自毁。这样一来,如果未来有人尝试调用函数或发送以太币,交易就会被回滚!这可能是最安全的做法。
我们在讨论回滚时,会讲到如何实现这一点!
解析这段代码的意思
1 | |
这段 Solidity 合约代码定义了一个名为 Contract 的智能合约,其主要功能是实现一个倒计时器,并在倒计时结束时销毁合约并将合约余额转移给合约调用者(销毁的对象是 msg.sender)。下面是对这段代码的详细解析:
1. 定义倒计时变量
1 | |
这行代码定义了一个名为 _countdown 的无符号整数(uint),初始值设定为 10。这个变量表示倒计时的初始时间,从 10 开始。
2. 构造函数
1 | |
构造函数是合约创建时自动执行的函数。在此代码中,构造函数是 payable,这意味着该合约在部署时可以接收以太币(Ether)。但在这里,构造函数本身并没有执行任何操作,除非用户向合约发送以太币。
3. tick() 函数
1 | |
tick() 函数是合约的主要功能所在,它是一个公共的函数,任何人都可以调用。以下是该函数的执行逻辑:
_countdown--;: 每次调用tick()函数时,倒计时变量_countdown会减少 1。即每调用一次tick(),倒计时就会减少 1。if (_countdown == 0): 当_countdown变量减到 0 时,进入条件判断。此时合约会执行以下操作:selfdestruct(payable(msg.sender));:selfdestruct是 Solidity 中的内置函数,用来销毁当前合约,并将合约的剩余以太币余额转移到指定的地址。msg.sender是调用tick()函数的地址。由于selfdestruct需要接受一个address payable类型的地址,因此需要将msg.sender强制转换为payable类型(通过payable(msg.sender))。- 一旦合约销毁,合约内的所有以太币余额都会被发送给调用
tick()函数的地址(即msg.sender)。
4. 总结
倒计时机制:每次调用
tick()函数,_countdown会减 1。当_countdown达到 0 时,合约会执行selfdestruct,销毁合约,并将合约的余额转账给调用者。selfdestruct作用:selfdestruct不仅销毁合约,还会将合约剩余的以太币转移到指定的地址。该地址是msg.sender,即调用tick()函数的地址。
使用场景
- 这个合约的设计可能用于某些特定的时间敏感操作,比如设定一个倒计时,直到倒计时结束后,合约会自动销毁并将剩余的以太币发送给触发此操作的用户。
注意事项
selfdestruct是一种危险操作,可能会导致合约的永久删除,因此它必须谨慎使用。
你的目标:自毁
当调用donate函数时,触发合约中的自毁操作!
自毁操作会将所有剩余资金发送到传入的地址,因此或许可以用它来替代donate函数中现有的功能,将资金发送给慈善机构!只需确保像上面的示例那样,将地址转换为可支付地址即可。
答案:
1 | |
Your Goal: Require 1 Ether
Add a payable constructor method that requires a 1 ether deposit.
If at least 1 ether is not sent to the constructor, revert the transaction.
1 | |
Your Goal: Owner Withdrawal
Create a public function withdraw that will withdraw all funds from the contract and send them to the deployer of the contract.
Require that only the deployer of the contract be allowed to call this function. For all other addresses, this function should revert.
1 | |
Your Goal: Require Owner
You’ll notice that the onlyOwner modifier has been added to each of the configuration functions in this contract. Only problem is, it doesn’t currently do anything!
Update the onlyOwner modifier to require that only the owner address can call these functions without reverting.
1 | |
为什么solidity安全审计不用ai代替人类去审计?
核心结论:当前AI无法替代人类做Solidity安全审计,根源在于AI缺乏复杂逻辑推理、跨合约上下文理解、新型漏洞预判与合规责任判定能力,而这些正是DeFi等高风险场景审计的核心,AI更适合做人工审计的前置扫描与辅助工具。
一、AI的核心能力短板(导致无法独立审计)
复杂逻辑与跨合约推理不足
- 难以识别跨函数/跨合约重入、代理合约委托调用上下文、MEV相关执行顺序漏洞等非局部性风险,GPT - 4等模型对这类关系的推理仍有明显缺陷。
- 面对大型合约(如MonoX、Popsicle协议)时,因上下文窗口限制,易丢失关键逻辑关联,无法完成完整链路审计。
- 无法理解DeFi协议的经济模型设计漏洞(如套利攻击、闪电贷攻击路径),这类漏洞往往不源于代码语法,而源于业务逻辑与激励机制的缺陷。
新型漏洞与未知攻击向量盲区
AI依赖训练数据中的已知漏洞模式,对零日漏洞、组合式攻击(多合约联动触发)等无标注样本的威胁基本无法自主识别,而这类漏洞恰恰是黑客攻击的重灾区。人类审计可通过经验与创造性思维预判这类未知风险。可靠性与解释性缺陷
- 存在“幻觉”问题,可能生成不存在的漏洞报告或误判风险等级,对高敏感的区块链场景而言,错误结论的代价极高。
- 无法像人类一样提供可追责、可解释的审计结论,尤其在涉及合规、法律追责时,人工审计的签字与论证更具公信力。
治理与权限类漏洞识别乏力
合约所有权、权限控制、升级机制等高层级设计缺陷,需要结合链上生态与业务规则综合判断,AI难以理解这类非代码层面的安全边界。
二、人类审计的不可替代性(核心价值所在)
| 能力维度 | 人类审计优势 | AI局限 |
|---|---|---|
| 业务逻辑穿透 | 能结合DeFi、NFT等场景的行业规则,理解合约的商业目标与安全边界 | 仅能基于代码语法与标注漏洞做匹配,无行业常识与业务上下文理解 |
| 新型威胁预判 | 可通过漏洞机理推导、攻防演练,预判训练数据外的攻击路径 | 只能识别已知漏洞,对零样本新威胁无自主发现能力 |
| 风险优先级判定 | 能结合项目资金规模、用户基数、攻击成本,权衡漏洞修复优先级 | 按预设规则标注风险,无法做动态的商业风险 - 成本权衡 |
| 合规与责任背书 | 审计报告具备法律与行业认可度,可作为项目合规的重要凭证 | AI结论无法律追责主体,无法用于正式审计背书 |
| 漏洞修复指导 | 能提供符合业务需求的修复方案,避免“为修漏洞而破坏功能” | 多给出通用修复建议,无法适配定制化合约的特殊逻辑 |
三、人机协作才是当前最优解
AI的定位:前置自动化扫描工具
- 快速检出整数溢出、tx.origin钓鱼、未检查外部调用等基础漏洞,降低人工审计的重复工作量。
- 集成到CI/CD流程,实现代码提交时的实时安全检查,提前过滤低危问题。
- 辅助生成审计报告初稿,提升文档效率。
人类的核心职责:深度审查与决策
- 聚焦复杂业务逻辑、经济模型、权限治理等高风险模块的审计。
- 验证AI检出结果,排除误报,评估漏洞实际影响。
- 制定修复方案、签署审计报告,承担最终安全责任。
四、未来趋势
随着ZKML、符号执行与大模型结合,AI的审计能力会持续提升,但只要区块链合约涉及经济价值与合规责任,人类审计的决策与背书价值就不可替代。短期内,“AI前置扫描 + 人类深度审计 + 形式化验证补充”将是Solidity安全审计的标准范式。
需要我给你整理一份Solidity审计人机协作清单(AI前置扫描工具推荐、人工重点审查模块、漏洞分级判定标准),可直接用于项目审计落地吗?
Solidity审计人机协作清单
这份Solidity审计人机协作清单,按“AI前置扫描→人工深度审查→验证闭环→报告交付”的全流程设计,明确分工、可直接落地,兼顾效率与安全性。
一、协作流程总览(核心逻辑:AI扫基础,人类抓核心)
- AI前置自动化扫描(过滤低危、解放人力)
- 人工深度审查(聚焦高风险、复杂逻辑与业务边界)
- 交叉验证与漏洞复现(人机结果互证,排除误报/漏报)
- 修复验证与报告输出(责任背书,形成可追溯闭环)
二、AI前置扫描模块(工具选型+检测范围+操作规范)
| 工具类型 | 推荐工具 | 核心检测能力 | 使用规范 |
|---|---|---|---|
| 静态分析 | Slither、Aderyn | 整数溢出、重入、未初始化变量、权限控制基础漏洞 | 必扫SWC全库,输出漏洞清单与风险等级初判 |
| 符号执行 | Mythril、MythX | 路径可达性、边界条件触发漏洞 | 针对核心函数(转账、提现、权限变更)单独扫描 |
| AI辅助审计 | Solidity Sentinel、LightChaser | 已知漏洞模式匹配、代码异味检测 | 结合静态结果交叉验证,标注AI疑似“幻觉”点 |
| 模糊测试 | Foundry fuzz、Echidna | 极端输入下的逻辑崩溃、数据溢出 | 覆盖90%+代码路径,设置合理gas上限 |
AI扫描执行步骤
- 环境准备:接入项目Hardhat/Foundry工程,配置编译器版本(优先0.8.0+)。
- 批量扫描:执行Slither + Mythril联合扫描,生成JSON格式漏洞报告。
- 初步过滤:剔除重复告警,标记明确误报(如SafeMath已覆盖的溢出)。
- 结果标注:将可疑漏洞按模块归类,供人工快速定位。
三、人类深度审查模块(重点清单+审查方法+判定标准)
人类聚焦AI无法覆盖的高风险维度,按优先级排序执行:
1. 核心业务逻辑与经济模型(最高优先级)
- 审查DeFi协议的激励机制、套利路径、闪电贷攻击面,验证数学模型正确性(如AMM滑点、LP收益计算)。
- 检查跨合约交互逻辑(如代理合约委托调用、ERC - 4626资金流),确认遵循Checks - Effects - Interactions范式。
- 评估经济参数(如手续费、清算阈值)是否存在设计缺陷,可被恶意利用。
2. 权限与治理漏洞(高层级设计)
- 检查合约所有权、升级权限、 pausable 控制等,避免单点故障。
- 验证角色权限隔离(如minter、admin、user),杜绝越权操作路径。
- 评估升级机制安全性(如Timelock、多签校验),防止恶意合约替换。
3. 新型/组合式漏洞预判(人类核心价值)
- 基于行业漏洞库与实战经验,预判零日漏洞、多合约联动攻击路径。
- 分析MEV相关风险(如交易排序、三明治攻击),评估合约抗MEV能力。
- 审查链上生态依赖(如Oracle数据准确性、外部合约安全性)。
4. AI结果复核与漏洞定级
| 漏洞类型 | 风险等级判定标准 | 修复优先级 |
|---|---|---|
| 高危(如重入、权限失控) | 可直接导致资金损失,攻击成本低 | 立即修复,暂停上线 |
| 中危(如逻辑漏洞、数据泄露) | 需特定条件触发,影响范围有限 | 上线前修复 |
| 低危(如代码冗余、gas优化) | 不影响安全,仅影响效率 | 可延后修复 |
人工审查操作规范
- 对照AI漏洞清单,逐点复现并验证(编写PoC合约测试高危漏洞)。
- 绘制合约交互流程图,标记关键安全边界(如资金转入/转出点)。
- 针对治理模块,模拟攻击场景(如权限被盗后如何止损)。
- 记录审查结论,提供可解释的漏洞成因与修复方案。
四、交叉验证与修复闭环(确保审计有效性)
- 漏洞复现:对所有高危/中危漏洞,人工编写攻击合约验证可利用性。
- 修复验证:开发者修复后,AI重新扫描+人工二次审查,确认漏洞彻底消除。
- 形式化验证(可选,高资金项目必做):使用Certora Prover验证核心不变量(如总供应量守恒、权限隔离)。
- 链上模拟:通过Tenderly等工具模拟主网部署,测试极端条件下的合约行为。
五、报告交付与责任背书(合规+可追溯)
- 报告结构:包含AI扫描摘要、人工审查结论、漏洞详情、修复建议、风险声明。
- 责任标注:明确人工审计师签字,区分AI与人工发现的漏洞,避免法律风险。
- 交付物:提供PDF报告+漏洞跟踪表(记录修复状态与验证结果)。
六、协作效率提升Tips
- 集成CI/CD:将AI扫描嵌入代码提交流程,提前拦截基础漏洞。
- 建立知识库:沉淀团队审计经验,用于AI模型微调与人工审查参考。
- 定期复盘:分析历史漏洞,优化AI扫描规则与人工审查重点。
需要我把这份清单转化为可直接复制的Foundry/Hardhat自动化脚本,包含Slither+Mythril联合扫描命令与漏洞过滤规则吗?