Rust分叉数据库管理库foundry-fork-db的使用,实现高效链上数据模拟与测试环境搭建
Rust分叉数据库管理库foundry-fork-db的使用,实现高效链上数据模拟与测试环境搭建
基本信息
- 版本: 0.16.0
- 许可证: MIT OR Apache-2.0
- 大小: 62.9 KiB
- 最低Rust版本: v1.85.0
安装方式
在项目目录中运行以下Cargo命令:
cargo add foundry-fork-db
或在Cargo.toml中添加:
foundry-fork-db = "0.16.0"
示例代码
以下是一个使用foundry-fork-db创建分叉数据库并模拟链上数据的完整示例:
use foundry_fork_db::{
fork::{Fork, ForkConfig},
opts::EvmOpts,
snapshot::Snapshots,
};
use std::sync::Arc;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 1. 配置分叉设置
let evm_opts = EvmOpts {
// 使用以太坊主网作为分叉源
fork_url: Some("https://mainnet.infura.io/v3/YOUR_API_KEY".to_string()),
// 从特定区块号分叉
fork_block_number: Some(15_000_000),
..Default::default()
};
// 2. 创建分叉配置
let fork_config = ForkConfig {
// 缓存目录用于存储分叉数据
cache_path: Some("./fork_cache".into()),
// 禁用持久化缓存
disable_cache: false,
// 设置请求超时
timeout: Some(std::time::Duration::from_secs(60)),
// 使用配置的EvmOpts
evm_opts,
};
// 3. 创建分叉实例
let fork = Fork::new(fork_config)?;
let fork = Arc::new(fork);
// 4. 创建快照管理器
let snapshots = Snapshots::new(fork.clone());
// 5. 创建一个新的快照
let snapshot_id = snapshots.create_snapshot().await?;
println!("Created snapshot with ID: {:?}", snapshot_id);
// 6. 模拟链上操作
// 这里可以执行合约调用、交易等操作
// 操作只会影响当前快照
// 7. 恢复到之前的状态
snapshots.revert_snapshot(snapshot_id).await?;
println!("Reverted to snapshot: {:?}", snapshot_id);
// 8. 创建另一个快照用于测试
let test_snapshot = snapshots.create_snapshot().await?;
println!("Created test snapshot with ID: {:?}", test_snapshot);
Ok(())
}
完整示例demo
以下是一个更完整的示例,展示了如何在实际测试中使用foundry-fork-db:
use foundry_fork_db::{
fork::{Fork, ForkConfig},
opts::EvmOpts,
snapshot::Snapshots,
};
use ethers::{
providers::{Provider, Http},
types::{Address, U256},
utils,
};
use std::{sync::Arc, str::FromStr};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 1. 配置分叉设置
let evm_opts = EvmOpts {
fork_url: Some("https://mainnet.infura.io/v3/YOUR_API_KEY".to_string()),
fork_block_number: Some(15_000_000),
..Default::default()
};
// 2. 创建分叉配置
let fork_config = ForkConfig {
cache_path: Some("./fork_cache".into()),
disable_cache: false,
timeout: Some(std::time::Duration::from_secs(60)),
evm_opts,
};
// 3. 创建分叉实例
let fork = Fork::new(fork_config)?;
let fork = Arc::new(fork);
// 4. 创建快照管理器
let snapshots = Snapshots::new(fork.clone());
// 5. 创建初始快照
let initial_snapshot = snapshots.create_snapshot().await?;
println!("初始快照ID: {:?}", initial_snapshot);
// 6. 连接到分叉的Provider
let provider = Provider::new(Http::from_str(&fork.endpoint())?);
// 7. 查询某个地址的ETH余额
let vitalik_address: Address = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045".parse()?;
let balance = provider.get_balance(vitalik_address, None).await?;
println!("Vitalik地址余额: {}", utils::format_units(balance, "ether")?);
// 8. 创建测试快照
let test_snapshot = snapshots.create_snapshot().await?;
println!("测试快照ID: {:?}", test_snapshot);
// 9. 执行测试交易
// 这里可以添加实际的合约调用或交易测试代码
// 10. 恢复到初始状态
snapshots.revert_snapshot(initial_snapshot).await?;
println!("已恢复到初始快照");
// 11. 验证余额是否恢复
let balance_after_revert = provider.get_balance(vitalik_address, None).await?;
println!("恢复后余额: {}", utils::format_units(balance_after_revert, "ether")?);
Ok(())
}
主要功能
- 分叉管理: 可以从任意以太坊兼容链分叉状态
- 快照系统: 创建和恢复状态快照,方便测试不同场景
- 缓存支持: 本地缓存分叉数据,提高测试效率
- 可配置性: 支持设置分叉区块号、RPC URL等参数
使用场景
- 智能合约测试
- 模拟链上交互
- 开发环境搭建
- 复杂的多步骤交易测试
注意事项
- 需要提供有效的RPC端点URL
- 分叉大型区块可能需要较多内存和存储空间
- 频繁创建快照可能影响性能
1 回复
Rust分叉数据库管理库foundry-fork-db的使用指南
概述
foundry-fork-db 是一个用于以太坊开发的高效链上数据模拟与测试环境搭建的Rust库,它是Foundry工具链的一部分。该库允许开发者在本地创建分叉(fork)的以太坊环境,模拟主网或测试网状态,便于进行合约测试和开发。
主要特性
- 支持从任意以太坊网络(主网、测试网)创建分叉
- 提供内存数据库实现,快速模拟链上状态
- 支持自定义区块号和链ID
- 与Foundry工具链无缝集成
- 高性能的链上数据缓存机制
安装方法
在Cargo.toml中添加依赖:
[dependencies]
foundry-fork-db = "0.1.0" # 请使用最新版本
基本使用方法
1. 创建分叉数据库
use foundry_fork_db::{
fork_db::ForkDb,
fork_factory::ForkFactory,
};
#[tokio::main]
async fn main() {
// 从以太坊主网在区块号15000000创建分叉
let fork_url = "https://mainnet.infura.io/v3/YOUR_INFURA_KEY";
let fork_block_number = Some(15000000);
let fork_factory = ForkFactory::new(fork_url);
let fork_db = fork_factory.create_fork(fork_block_number).await.unwrap();
println!("Fork created at block {}", fork_db.block_number());
}
2. 查询分叉状态
use ethers::types::Address;
// 继续上面的代码
let dai_address: Address = "0x6B175474E89094C44Da98b954EedeAC495271d0F".parse().unwrap();
let dai_balance = fork_db.get_balance(dai_address).await.unwrap();
println!("DAI合约在分叉区块的余额: {}", dai_balance);
3. 修改分叉状态
use ethers::types::{Address, U256};
let user_address: Address = "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B".parse().unwrap();
// 给用户地址充值100ETH
fork_db.set_balance(user_address, U256::from(100) * U256::exp10(18)).unwrap();
// 验证余额
let new_balance = fork_db.get_balance(user_address).await.unwrap();
println!("用户新余额: {} ETH", new_balance);
高级用法
1. 与测试框架集成
use foundry_fork_db::fork_factory::ForkFactory;
use foundry_evm::executor::Executor;
#[tokio::test]
async fn test_contract_on_fork() {
let fork_factory = ForkFactory::new("https://mainnet.infura.io/v3/YOUR_KEY");
let fork_db = fork_factory.create_fork(None).await.unwrap();
let mut executor = Executor::new(fork_db);
// 在这里部署和测试合约
// ...
}
2. 多分叉管理
use foundry_fork_db::fork_factory::ForkFactory;
#[tokio::main]
async fn main() {
let fork_factory = ForkFactory::new("https://mainnet.infura.io/v3/YOUR_KEY");
// 创建两个不同区块高度的分叉
let fork_db1 = fork_factory.create_fork(Some(15000000)).await.unwrap();
let fork_db2 = fork_factory.create_fork(Some(16000000)).await.unwrap();
// 比较两个分叉状态下某个合约的差异
// ...
}
性能优化技巧
-
使用本地缓存:配置分叉数据库使用本地缓存可以显著提高性能
let fork_factory = ForkFactory::new("https://mainnet.infura.io/v3/YOUR_KEY") .with_cache_dir("/path/to/cache");
-
限制获取的状态:只获取测试需要的状态数据
let fork_db = fork_factory.create_fork_with_state( Some(15000000), vec![address1, address2] // 只获取这些地址的状态 ).await.unwrap();
-
批量操作:使用批量查询和写入方法减少RPC调用
注意事项
- 需要稳定的RPC节点连接,推荐使用Infura或Alchemy等服务
- 分叉数据库会占用较多内存,特别是模拟大区块时
- 测试完成后应清理缓存文件,特别是运行大量测试时
foundry-fork-db为以太坊智能合约开发提供了强大的测试环境模拟能力,可以显著提高开发效率和测试覆盖率。
完整示例demo
下面是一个完整的foundry-fork-db使用示例,展示了创建分叉、查询状态和修改状态的全流程:
use foundry_fork_db::{fork_db::ForkDb, fork_factory::ForkFactory};
use ethers::types::{Address, U256};
use std::str::FromStr;
#[tokio::main]
async fn main() {
// 1. 创建分叉数据库
let fork_url = "https://mainnet.infura.io/v3/YOUR_INFURA_KEY";
let fork_block_number = Some(15000000);
let fork_factory = ForkFactory::new(fork_url)
.with_cache_dir("/tmp/fork_cache"); // 启用本地缓存
println!("正在创建分叉...");
let fork_db = fork_factory.create_fork(fork_block_number).await.unwrap();
println!("成功创建分叉,区块高度: {}", fork_db.block_number());
// 2. 查询状态
let dai_address = Address::from_str("0x6B175474E89094C44Da98b954EedeAC495271d0F").unwrap();
let dai_balance = fork_db.get_balance(dai_address).await.unwrap();
println!("DAI合约余额: {}", dai_balance);
// 3. 修改状态
let user_address = Address::from_str("0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B").unwrap();
let amount = U256::from(100) * U256::exp10(18); // 100 ETH
println!("修改用户余额...");
fork_db.set_balance(user_address, amount).unwrap();
// 验证修改
let new_balance = fork_db.get_balance(user_address).await.unwrap();
println!("用户新余额: {} ETH", new_balance);
// 4. 批量操作示例
let addresses = vec![
Address::from_str("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045").unwrap(), // vitalik.eth
Address::from_str("0x1db3439a222c519ab44bb1144fc28167b4fa6ee6").unwrap() // 随机地址
];
println!("批量查询余额...");
for addr in addresses {
let balance = fork_db.get_balance(addr).await.unwrap();
println!("地址 {} 的余额: {}", addr, balance);
}
}
这个完整示例展示了:
- 使用缓存目录创建分叉数据库
- 查询特定合约的余额
- 修改用户地址的余额
- 批量查询多个地址的余额
要运行此示例,请确保:
- 替换YOUR_INFURA_KEY为实际的Infura API密钥
- 确保已安装tokio运行时
- Cargo.toml中包含所有必要的依赖项