首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >DeFi中的不变量测试(Invariant Test)

DeFi中的不变量测试(Invariant Test)

原创
作者头像
用户11711955
发布2025-06-20 14:54:37
发布2025-06-20 14:54:37
1510
举报

什么是不变量测试(Invariant Testing)?

不变量测试是一种软件测试方法,专注于验证系统在各种条件下某些属性或状态始终保持不变。在区块链和DeFi应用中,这种测试方法尤为重要,因为智能合约一旦部署到链上就无法更改,而且直接管理资产。不变量测试帮助开发者确保无论用户如何与合约交互,关键的安全性和功能性特性始终得到保持。

不变量测试与传统的单元测试不同:

  • 单元测试验证特定输入下的特定输出
  • 不变量测试验证在任何情况下都必须保持的系统属性

ERC20代币中的不变量测试

ERC20是以太坊上最广泛使用的代币标准之一。它定义了转账、授权等基本功能。接下来我们以ERC20为例来了解,如何对协议的关键属性进行不变量测试(资料完全来自Trail of Bits开源学习仓库)。

基本属性测试

总供应量与余额关系

  1. test_ERC20_constantSupply - 对于不可铸造和不可销毁的代币,总供应量应保持不变。
代码语言:javascript
复制
function test_ERC20_constantSupply() public virtual {    require(!isMintableOrBurnable);    assertEq(initialSupply, totalSupply(), "Token supply was modified");}

这个测试确保在没有铸造或销毁机制的代币中,总供应量始终保持不变。这是代币经济模型的基本保证。

2. test_ERC20_userBalanceNotHigherThanSupply - 任何用户的余额都不应大于代币的总供应量。

代码语言:javascript
复制
function test_ERC20_userBalanceNotHigherThanSupply() public {    assertLte(        balanceOf(msg.sender),        totalSupply(),        "User balance higher than total supply"    );}

这个不变量确保单个用户的余额永远不能超过代币的总供应量,这是逻辑上的限制,违反此规则将表明存在严重漏洞。

3. test_ERC20_usersBalancesNotHigherThanSupply - 用户余额总和不应大于代币的总供应量。

4. test_ERC20_zeroAddressBalance - 零地址的代币余额应为零。

代码语言:javascript
复制
function test_ERC20_zeroAddressBalance() public {    assertEq(        balanceOf(address(0)),        0,        "Address zero balance not equal to zero"    );}

零地址(0x0)是一个特殊地址,它不应该持有任何代币。这个测试确保零地址的余额始终为零,防止代币被意外"烧毁"到一个无法访问的地址。

转账相关不变量

5. test_ERC20_transferToZeroAddress - 不应允许向零地址进行transfer转账。

代码语言:javascript
复制
function test_ERC20_transferToZeroAddress() public {    uint256 balance = balanceOf(address(this));    require(balance > 0);
    bool r = transfer(address(0), balance);    assertWithMsg(r == false, "Successful transfer to address zero");}

这个测试验证向零地址的转账应该被拒绝,这是一个重要的安全机制,防止代币被永久锁定。

6. test_ERC20_transferFromToZeroAddress - 不应允许向零地址进行transferFrom转账。

7. test_ERC20_selfTransfer - 自我transfer转账不应破坏账户记录。

代码语言:javascript
复制
function test_ERC20_selfTransfer(uint256 value) public {    uint256 balance_sender = balanceOf(address(this));    require(balance_sender > 0);
    bool r = this.transfer(address(this), value % (balance_sender + 1));    assertWithMsg(r == true, "Failed self transfer");    assertEq(        balance_sender,        balanceOf(address(this)),        "Self transfer breaks accounting"    );}

这个测试确保向自己转账不会意外改变余额,这是确保账户系统一致性的重要保障。

8. test_ERC20_selfTransferFrom - 自我transferFrom转账不应破坏账户记录。

9. test_ERC20_transferMoreThanBalance - 不应允许转账金额超过账户余额的transfer操作。

10. test_ERC20_transferFromMoreThanBalance - 不应允许转账金额超过账户余额的transferFrom操作。

11. test_ERC20_transferZeroAmount - 零金额的transfer转账不应破坏账户记录。

12. test_ERC20_transferFromZeroAmount - 零金额的transferFrom转账不应破坏账户记录。

13. test_ERC20_transfer - 有效的transfer转账应正确更新账户记录。

代码语言:javascript
复制
function test_ERC20_transfer(address target, uint256 amount) public {    require(target != address(this));    uint256 balance_sender = balanceOf(address(this));    uint256 balance_receiver = balanceOf(target);    require(balance_sender > 2);    uint256 transfer_value = (amount % balance_sender) + 1;
    bool r = this.transfer(target, transfer_value);    assertWithMsg(r == true, "transfer failed");    assertEq(        balanceOf(address(this)),        balance_sender - transfer_value,        "Wrong source balance after transfer"    );    assertEq(        balanceOf(target),        balance_receiver + transfer_value,        "Wrong target balance after transfer"    );}

这个不变量测试确保转账后,发送方余额减少,接收方余额增加,且变动金额与转账金额完全一致。

14. test_ERC20_transferFrom - 有效的transferFrom转账应正确更新账户记录。

授权相关不变量

15. test_ERC20_setAllowance - 调用approve时应正确设置授权额度。

代码语言:javascript
复制
function test_ERC20_setAllowance(address target, uint256 amount) public {    bool r = this.approve(target, amount);    assertWithMsg(r == true, "Failed to set allowance via approve");    assertEq(        allowance(address(this), target),        amount,        "Allowance not set correctly"    );}

这个测试验证approve函数应该正确设置授权金额。

16. test_ERC20_setAllowanceTwice - 连续两次调用approve时应正确更新授权额度。

17. test_ERC20_spendAllowanceAfterTransfer - 调用transferFrom后,授权额度应正确更新。

代码语言:javascript
复制
function test_ERC20_spendAllowanceAfterTransfer(    address target,    uint256 amount) public {    require(target != address(this) && target != address(0));        require(target != msg.sender);        uint256 balance_sender = balanceOf(msg.sender);        uint256 current_allowance = allowance(msg.sender, address(this));        require(balance_sender > 0 && current_allowance > balance_sender);        uint256 transfer_value = (amount % balance_sender) + 1;
        bool r = this.transferFrom(msg.sender, target, transfer_value);        assertWithMsg(r == true, "transferFrom failed");
        // Some implementations take an allowance of 2**256-1 as infinite, and therefore don't update        if (current_allowance != type(uint256).max) {            assertEq(                allowance(msg.sender, address(this)),                current_allowance - transfer_value,                "Allowance not updated correctly"            );        }    }}

这个测试确保在使用transferFrom后,授权额度应该减少,除非使用的是特殊的"无限授权"值。

可销毁代币测试

18. test_ERC20_burn - 调用burn时,用户余额和总供应量应正确更新。

19. test_ERC20_burnFrom - 调用burnFrom时,用户余额和总供应量应正确更新。

20. test_ERC20_burnFromUpdateAllowance - 调用burnFrom时,授权额度应正确更新。

可铸造代币测试

21. test_ERC20_mintTokens - 铸造后,用户余额和总供应量应正确更新。

可暂停代币测试

22. test_ERC20_pausedTransfer - 启用暂停状态时,不应允许进行代币transfer转账。

23. test_ERC20_pausedTransferFrom - 启用暂停状态时,不应允许进行代币transferFrom转账。

增减授权额度测试

24. test_ERC20_setAndIncreaseAllowance - 调用increaseAllowance时,授权额度应正确更新。

25. test_ERC20_setAndDecreaseAllowance - 调用decreaseAllowance时,授权额度应正确更新。

不变量测试通过系统地测试ERC20代币的关键属性,不仅验证了基本功能是否正常工作,更重要的是确保了在各种边缘情况下系统的完整性和安全性。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是不变量测试(Invariant Testing)?
  • ERC20代币中的不变量测试
  • 基本属性测试
    • 总供应量与余额关系
    • 授权相关不变量
  • 可销毁代币测试
  • 18. test_ERC20_burn - 调用burn时,用户余额和总供应量应正确更新。
  • 可铸造代币测试
  • 可暂停代币测试
  • 增减授权额度测试
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档