本文介绍了 Foundry 中 vm.prank cheatcode 的使用方法,该 cheatcode 允许开发者模拟任何 msg.sender,从而测试需要特定权限才能访问的合约逻辑,例如访问控制、多重签名和元交易路径。
学习如何使用 Foundry 的 vm.prank 来模拟任何 msg.sender 并测试有权限的合约逻辑。对于访问控制、多重签名和 meta-tx 路径至关重要。
2025-06-18 - 5 分钟阅读
Web3 安全
threesigma's twitterthreesigma's linkedinthreesigma's github
很高兴你回来参加 Foundry Cheatcodes 系列的第二期。在第 1 部分中,我们将 Foundry 与 Hardhat 进行了比较,并为更快的、Solidity 原生的测试奠定了基础;今天,我们将聚焦于 vm.prank
,这个技巧可以让你的测试伪装成任何地址,从而探测访问控制的边缘情况。
是否曾经需要测试访问控制或模拟来自不同用户的调用?Foundry 的 vm.prank
可以非常简单地模拟任何地址并验证你的合约逻辑。
vm.prank
以及为什么它很重要vm.prank(address)
cheatcode 使下一个合约调用看起来来自不同的地址(它设置了 msg.sender
)。 这在测试依赖于调用者的逻辑时至关重要。 例如,如果只有所有者才能调用某个函数,则你可以恶搞一个非所有者并验证它是否失败。 在 Foundry 的 forge-std
Test
合约中,你只需执行以下操作:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import {Test, console} from "forge-std/Test.sol";
contract PrankExample is Test {
address owner = address(0x1);
address attacker = address(0x2);
function testPrank() public {
// 设置 attacker 为后续调用的 msg.sender
vm.prank(attacker);
// 现在,任何后续的交易都将看起来像是从 attacker 发起的
// 例如,调用一个只有 owner 才能调用的函数,将会失败
}
}
vm.prank
进行访问控制测试让我们通过一个实际的例子来演示 vm.prank
。假设你有一个简单的银行合约,只有所有者才能提取资金。vm.prank
允许我们模拟非所有者并确保他们无法提取资金。
首先,我们创建一个基本的 Bank
合约:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract Bank {
address public owner;
uint256 public balance;
constructor() {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner, "Only owner can call this function");
_;
}
function deposit() public payable {
balance += msg.value;
}
function withdraw(uint256 amount) public onlyOwner {
require(balance >= amount, "Insufficient balance");
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Withdrawal failed");
balance -= amount;
}
}
现在,编写一个 Foundry 测试以验证只有所有者才能提取资金:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import {Test, console} from "forge-std/Test.sol";
import {Bank} from "../src/Bank.sol";
contract BankTest is Test {
Bank public bank;
address owner = address(0x1);
address attacker = address(0x2);
function setUp() public {
// 将 owner 设置为部署者,并使用 owner 的私钥部署合约
vm.deal(owner, 10 ether);
vm.prank(owner);
bank = new Bank();
}
function testWithdrawSuccess() public {
// 给 bank 存入一些钱
vm.deal(address(bank), 1 ether);
// 确认 owner 可以取款
vm.prank(owner);
bank.withdraw(0.5 ether);
assertEq(address(bank).balance, 0.5 ether, "Withdrawal failed");
}
function testWithdrawFail() public {
// 给 bank 存入一些钱
vm.deal(address(bank), 1 ether);
// 尝试模拟一个非所有者取款应该会失败
vm.prank(attacker);
vm.expectRevert("Only owner can call this function");
bank.withdraw(0.5 ether);
}
}
setUp
函数:将所有者设置为 address(0x1)
,并使用该地址部署 Bank
合约。testWithdrawSuccess
函数:验证所有者是否可以成功取款。我们使用 vm.prank(owner)
来确保调用来自所有者。testWithdrawFail
函数:尝试使用 vm.prank(attacker)
模拟由非所有者进行的取款。我们期望调用会因为 onlyOwner
修饰符而回滚,我们使用 vm.expectRevert
来验证这一点。vm.startPrank
和 vm.stopPrank
结合使用对于一些复杂的情况,你可能需要为一个区块范围内模拟一个 msg.sender
。vm.startPrank
和 vm.stopPrank
简化了这一点。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import {Test, console} from "forge-std/Test.sol";
import {Bank} from "../src/Bank.sol";
contract BankTest is Test {
Bank public bank;
address owner = address(0x1);
address attacker = address(0x2);
address randomUser = address(0x3);
function setUp() public {
// 将 owner 设置为部署者,并使用 owner 的私钥部署合约
vm.deal(owner, 10 ether);
vm.prank(owner);
bank = new Bank();
}
function testMultipleCallsWithPrank() public {
// 给 bank 存入一些钱
vm.deal(address(bank), 3 ether);
// 启动一个从 attacker 模拟的 prank
vm.startPrank(attacker);
// 即使我们模拟了 attacker,这个调用仍然有效,因为它是 msg.sender
// 这是因为存款不使用 onlyOwner 修饰符
bank.deposit{value: 1 ether}();
// 验证余额是否正确
assertEq(address(bank).balance, 4 ether, "Deposit failed");
// 停止 prank
vm.stopPrank();
// 尝试模拟一个非所有者取款应该会失败
vm.prank(randomUser);
vm.expectRevert("Only owner can call this function");
bank.withdraw(0.5 ether);
}
}
在此示例中,vm.startPrank(attacker)
意味着所有后续调用都将看起来来自攻击者,直到使用 vm.stopPrank()
为止。
vm.prank
是一个强大的工具,可用于在 Foundry 中进行访问控制测试。通过模拟不同的地址,你可以确保你的合约按照预期运行并避免未经授权的操作。
在接下来的文章中,我们将介绍 vm.deal
,它允许你直接设置任何地址的 ETH 余额。
Simeon 是一位对 Web3 安全、区块链技术和零知识证明充满热情的区块链安全工程师。在 Twitter (@threesigmaxyz) 或 Linkedin (threesigma) 上与他联系。
- 原文链接: threesigma.xyz/blog/web3...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!