Skip to content

Latest commit

 

History

History
227 lines (150 loc) · 9.99 KB

index.md

File metadata and controls

227 lines (150 loc) · 9.99 KB
title description slug date image math license hidden comments draft series categories tags
Foundry
使用 foundry 编写智能合约
2023-03-15 10:17:15 +0800
false
true
false
以太坊开发工具链
ethereum
foundry

本文介绍如何使用 Foundry 编写智能合约。

概述

foundry 是用 Rust 编写的用于以太坊应用程序开发的快速、可移植和模块化工具包。包括:

foundry 包含以下组件:

  • Forge: 以太坊测试框架(类似 Truffle, Hardhat 和 DappTools)。
  • Cast: 用于与 EVM 智能合约互动,发送交易和获取链上数据,可用于合约调试。
  • Anvil: 本地以太坊节点,类似于 Ganache、Hardhat 网络。
  • Chisel: 快速、实用、详细的 solidity REPL

特色

  • 快速 灵活的编译管道。
    • 自动检测和安装 Solidity 编译器版本。
    • 增量编译和缓存:只对更改的文件进行重新编译。
    • 并行编译。
    • 支持非标准目录结构(如 Hardhat repos)。
  • 用 Solidity 编写测试(与 DappTools 类似),可有效减少上下文切换。相比 hardhat+ethers 组合减少语言依赖和学习。
  • 通过缩小输入和打印反例进行快速模糊测试。
  • 快速远程 RPC 分叉模式,该功能利用类似 tokio 的 Rust 异步基础架构。
  • 灵活的调试日志
    • DappTools 风格,使用 DsTest 输出的日志。
    • Hardhat 风格,使用流行的 console.sol 合约
  • 便携(5-10MB)且易于安装,无需 Nix 或其他软件包管理器
  • 通过 Foundry GitHub 操作实现快速 CI。

官方手册 英文版, 登链社区中文版 更新不及时,谨慎参考。

安装

官方指南

curl -L https://foundry.paradigm.xyz | bash

这将安装 fourndryup,然后只需按照屏幕上的说明进行操作,执行 fourndryup 命令将安装 foundry 的其他组件,重新执行该命令也可以升级相关的组件。

配置

其他配置项参考 Config Reference

{{< gist phenix3443 333a25e2168cdba73ca7098efd95c8a4 >}}

自动补全

foundry 带有 shell 自动补全 功能。

添加 [zsh 自动补全]({{< ref "posts/zsh/#auto-completion" >}}):

forge completions zsh > $(brew --prefix)/share/zsh/site-functions/_forge
cast completions zsh > $(brew --prefix)/share/zsh/site-functions/_cast
anvil completions zsh > $(brew --prefix)/share/zsh/site-functions/_anvil

forge

init/clone

install

forge 默认使用 git 子模块 管理依赖项,现在可以使用 soldeer 来管理依赖,这是一个好现象,说明 solidity 终于要有统一的包管理工具了。当前使用情况下,部分仓库还需要手动修改 remapping.

build

test

一个好的做法是将 test_Revert[If|When]_ConditionexpectRevert cheatcode 结合使用(下一节将更详细地解释作弊代码)。

invariant test

不变式测试允许对一组不变式表达式进行测试,测试的对象是来自预定义合约的预定义函数调用随机序列。在执行每次函数调用后,都会对所有已定义的不变式进行断言。

不变式测试是暴露协议中不正确逻辑的有力工具。由于函数调用序列是随机的,并且有模糊输入,因此不变式测试可以揭示边缘情况和高度复杂协议状态中的错误假设和不正确逻辑。

不变式测试活动有两个维度:运行和深度:

运行:函数调用序列生成和运行的次数。 深度:特定运行中函数调用的次数。每次函数调用后,都会断言所有已定义的不变式。如果函数调用回退,深度计数器仍会递增。 此处将对这些变量和其他不变式配置方面进行说明。

与在 Foundry 中运行标准测试时在函数名前缀上 test 相似,不变式测试也是在函数名前缀上 invariant(例如,函数 invariant_A())。

配置不变式测试的执行 用户可通过 Forge 配置原语控制不变式测试的执行参数。配置可以全局应用,也可以按测试应用。有关此主题的详细信息,请参阅 📚 全局配置 和 📚 在线配置。

Differential Testing

Forge 可用于 differential testing 和 differential fuzzing。甚至可以使用 ffi cheatcode 对非 EVM 可执行文件进行测试。

背景

differential testing 通过比较每个函数的输出,交叉引用同一函数的多个实现。假设我们有一个函数规范 F(X),以及该规范的两个实现:f1(X) 和 f2(X)。我们希望 f1(x) == f2(x) 适用于输入空间中的所有 x。如果 f1(x) != f2(x),我们就知道至少有一个函数错误地实现了 F(X)。这个测试相等性和识别差异的过程是 differential testing 的核心。

differential fuzzing 是 differential testing 的扩展。differential fuzzing 以编程方式生成许多 x 值,以发现人工选择的输入可能无法揭示的差异和边缘情况。

注意:这里的 == 运算符可以是自定义的相等定义。例如,如果测试浮点实现,可以使用具有一定容差的近似相等。

这类测试在现实生活中的一些应用包括:

  • 比价升级前后的实现
  • 根据已知参考实现测试代码
  • 确认与第三方工具和依赖关系的兼容性

以下是 Forge 用于差异测试的一些示例。

入门: ffi 作弊码

ffi 允许您执行任意 shell 命令并捕获输出。这是一个模拟示例:

script{#forge_script}

部署

可以通过 forge create 命令部署合约:

{{< gist phenix3443 868da315757b9f430b417d27b297b3a6 >}} {{< gist phenix3443 5ea8620ec1457eeaa5d3316180b7ba14 >}} {{< gist phenix3443 d4bd06898a1e22af839a777e970369ae >}}

但是 forge create 命令不支持动态链接:如果代码引入了库合约,应该预部署库合约,并通过 --libraries 手动指定库合约的地址,这无疑是件麻烦的事情。

Solidity Script 是一种使用 Solidity 声明式部署合约的方法,相比较forge create限制更少,也更加友好。

{{< gist phenix3443 b254a4c5718fbaba5cffa730eaca41d4 >}}

如果不加参数,会在本地内存环境模拟执行。

在本地内存区块链模拟

使用 anvil 搭建的测试环境和测试账号进行部署:

在本地部署

为了测试合约验证功能,在 holesky 使用自己的私钥和 infura endpoint 进行部署:

holesky 部署验证

verify-contract

出了上面的 script 命令,还可以通过 forge verify-contract 命令对已经部署的合约进行验证。

Cast

查看余额

cast balance --rpc-url http://127.0.0.1:8545 -e 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
9999.999590116000000000

查看交易

cast tx --rpc-url http://127.0.0.1:8545 0xab10eb28fa2bb1ecc0641c73a14a59e7d594f6c35efa322921b9158461eb6dec --json

查看上面合约部署的结果:

{
  "hash": "0xab10eb28fa2bb1ecc0641c73a14a59e7d594f6c35efa322921b9158461eb6dec",
  "nonce": "0x0",
  "blockHash": "0xfadb58bc05550d9e061a7b49982ce9dba7f84b0cc6af161b1115fc41919b3e51",
  "blockNumber": "0x1",
  "transactionIndex": "0x0",
  "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
  "to": null,
  "value": "0x0",
  "gasPrice": "0xee6b2800",
  "gas": "0x19e2c",
  "input": "0x608060405234801561001057600080fd5b5060e38061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80633acb315014602d575b600080fd5b604080518082018252600b81526a12195b1b1bc815dbdc9b1960aa1b60208201529051605891906061565b60405180910390f35b600060208083528351808285015260005b81811015608c578581018301518582016040015282016072565b506000604082860101526040601f19601f830116850101925050509291505056fea2646970667358221220b9ff3e936eee55cb18c5673b26d650f22c94dd400af97be41d3d51518c4a29ff64736f6c63430008140033",
  "v": "0x0",
  "r": "0x8de4144ce716a9a063c02631d46684ddca9bd96afe8950942355b224fa60d2c7",
  "s": "0x62738fce4538165b0b67f55c847e96642d28c817cb112cb0cc3a55cc4b8804ac",
  "type": "0x2",
  "accessList": [],
  "maxPriorityFeePerGas": "0xb2d05e00",
  "maxFeePerGas": "0x12a05f200",
  "chainId": "0x7a69"
}

调用函数

{{< gist phenix3443 7053c482e1b219e8830742ccc0a746bf >}}

更多详细信息参见 cast reference

Anvil

Anvil 是 Foundry 附带的本地测试网节点。可以使用它从前端测试您的合约或通过 RPC 进行交互。类似于 [hardhat network]({{< ref "posts/ethereum/hardhat#network" >}})

anvil 也支持 fork 其他 chain 来方便测试,更多参考 anvil Reference

Chisel

Chisel 是 Solidity REPL("读取-评估-打印循环 "的缩写),允许开发人员编写和测试 Solidity 代码片段。它为编写和执行 Solidity 代码提供了一个交互式环境,并为处理和调试代码提供了一套内置命令。这使它成为快速测试和试验 Solidity 代码的有用工具,而无需启动沙箱代工测试套件。

Next