使用 Fuel Rust SDK 连接 Fuel 节点:Provider、Testnet/本地 fuel-core 与测试用临时节点全指南
【免费下载链接】fuels-rsFuel Network Rust SDK项目地址: https://gitcode.com/GitHub_Trending/fu/fuels-rs
Fuel Rust SDK(fuels-rs)是构建基于 Sway 智能合约链上应用的核心工具链。要让 SDK 驱动 Fuel 虚拟机上的计算,前提是打通 SDK 与fuel-core节点之间的通信,而这正是Provider的职责。本文以 SDK 文档中"连接 Fuel 节点"章节为主线,梳理两类连接方式(连接已有节点 vs 启动临时测试节点)的适用场景、完整代码与底层实现原理,并延伸到短生命周期节点的 feature 配置、连接后的链上数据查询与请求重试策略,帮助你在应用开发与智能合约测试之间做出正确选择。
连接模型概述:为什么一切从 Provider 开始
Fuel Rust SDK 让开发者能够用 Rust 编写与 Sway 合约交互的应用,计算发生在 Fuel 虚拟机(FuelVM)上。要让这种交互成立,SDK 必须能和一个fuel-core节点通信,而Provider就是这条通信链路的入口。无论是查询余额、估算费用,还是发送交易、调用合约,几乎每个操作都经由Provider转发到节点。
从源码看,Provider的构造入口封装在 provider.rs 中:
// 源码位置:packages/fuels-accounts/src/provider.rs pub async fn connect(url: impl AsRef<str>) -> Result<Provider> { // ... } pub async fn connect_with_fallbacks(urls: &[impl AsRef<str>]) -> Result<Provider> { // ... }connect接受一个节点地址字符串(host 或IP:port),而connect_with_fallbacks更进一步,允许你同时给出多个候选地址做故障转移。这与文档描述的两类接入方式一一对应:
- 连接现有节点:使用测试网(Testnet)或自行运行一个
fuel-core节点,然后用Provider指向该节点的地址与端口; - 使用 SDK 内置的短生命周期节点:通过
launch_provider_and_get_wallet()在测试进程中临时拉起一个 Fuel 节点,用完即弃。
第二种方式非常适合智能合约测试——可以在不同测试用例之间快速启停节点;而构建真实应用时,应当采用第一种方式连接持久化的网络节点。
方式一:连接 Testnet 或外部 fuel-core 节点
当你的目标是构建面向真实网络的应用(或连接持续运行的开发节点)时,需要让Provider指向一个已经启动的fuel-core节点。
连接 Testnet 并创建钱包
以下示例来自 examples/providers/src/lib.rs 中的connect_to_fuel_node测试,展示了最完整的接入流程:连接 Testnet → 由私钥派生钱包 → 打印钱包地址:
use std::str::FromStr; use fuels::{crypto::SecretKey, prelude::*}; // 创建一个指向 testnet 的 provider。 let provider = Provider::connect("testnet.fuel.network").await.unwrap(); // 配置一个私钥 let secret = SecretKey::from_str( "a1447cd75accc6b71a976fd3401a1f6ce318d27ba660b0315ee6ac347bf39568", )?; // 用私钥创建钱包 let wallet = Wallet::new(PrivateKeySigner::new(secret), provider); // 获取钱包地址。该地址后续可配合 faucet 领取测试资产 dbg!(wallet.address().to_string());这里有一个实战要点需要注意:Testnet 上新创建的钱包默认没有任何资产。你需要在测试网获取钱包地址后,通过 Testnet faucet 为它充值基础资产;资产到账后,把私钥保存下来,就能在后续测试中复用同一个钱包,避免重复领水。区块浏览器也可以用来核对交易与余额状态。
连接本地或自定义节点:只改 URL
外部节点连接并不局限于 Testnet。如果换成自己用fuel-core起的本地节点,只需替换Provider::connect的地址参数。下面是同一测试文件中的本地节点示例:
// 使用一个指向本地节点的 URL let _provider = Provider::connect(format!("127.0.0.1:{port}")).await?;这段代码中port来自setup_test_provider启动的临时节点监听端口(示例中通过provider.url()解析得到),你完全可以用自己节点的实际端口(fuel-core默认 GraphQL 端口为 4000,具体以节点启动配置为准)替代。换言之,Provider::connect的入参可以是:
- 测试网域名,如
"testnet.fuel.network"; - 本地地址,如
"127.0.0.1:4000"; - 任意可达的
fuel-core节点IP:port。
适用场景
连接已有节点的方式适合所有非测试用途,包括本地开发调试、接入公共测试网/主网、以及部署面向真实用户的链上应用。它的优点是没有额外节点启停开销、状态可持续,但前提是目标节点必须处于运行状态且网络可达。
方式二:在 SDK 中拉起短生命周期 Fuel 节点
合约测试(尤其是#[tokio::test]用例)追求的是隔离性与速度:每个用例希望拿到干净链状态、快速执行完毕。为此 SDK 提供了三种由浅入深的内置起节点方式,全部实现在fuels-test-helperscrate 中。
手动方式:FuelService + Provider::from
最底层的方式是显式调用FuelService::start启动进程内节点,再通过Provider::from连接它。代码见 examples/contracts/src/lib.rs:
use fuels::prelude::{FuelService, Provider}; // 启动 fuel 节点。 let server = FuelService::start( NodeConfig::default(), ChainConfig::default(), StateConfig::default(), ) .await?; // 创建一个与上面节点通信的客户端。 let client = Provider::from(server.bound_address()).await?; assert!(client.healthy().await?);注意FuelService返回的server句柄必须被持有(不能直接丢弃)。从 fuels-test-helpers/src/lib.rs 的实现可以看到,SDK 内部通过tokio::spawn将一个长期持有的任务绑定在server上,一旦句柄被释放、任务终止,节点进程也会随之关闭。这也是它被称为"短生命周期节点"的原因——非常适合在测试用例之间快速启停。
测试辅助函数:setup_test_provider
如果手动管理FuelService仍嫌繁琐,可以直接调用测试辅助函数setup_test_provider。它的签名与行为定义在 fuels-test-helpers/src/lib.rs:
pub async fn setup_test_provider( coins: Vec<Coin>, messages: Vec<Message>, node_config: Option<NodeConfig>, chain_config: Option<ChainConfig>, ) -> Result<Provider> { let node_config = node_config.unwrap_or_default(); let chain_config = chain_config.unwrap_or_else(testnet_chain_config); // 将 coins/messages 组装成 StateConfig 并启动 FuelService, // 返回连接该节点的 Provider。 }典型用法来自 examples/wallets/src/lib.rs:
use fuels::prelude::*; // 使用测试辅助函数启动一个测试 provider。 let provider = setup_test_provider(vec![], vec![], None, None).await?; // 创建钱包。 let _wallet = Wallet::random(&mut thread_rng(), provider);setup_test_provider的四个参数分别为:预置的 coins(Coin列表)、预置的 messages(Message列表,跨链桥消息场景使用)、节点配置与链配置;后两个传入None时使用默认值。值得注意的一点是:当chain_config为None时,SDK 会使用 testnet_chain_config(同文件内定义)——它会基于默认共识参数调大交易与合约的体积上限(如TxParameters::with_max_size(10_000_000)、ContractParameters::with_contract_max_size(1_000_000)),模拟 Testnet 更宽松的容量约束,避免在测试大合约/大交易时撞上默认参数限制。
一键式:launch_provider_and_get_wallet
这是文档首推、也最常用的测试入口。launch_provider_and_get_wallet()把setup_test_provider与钱包创建"一步到位",实现位于 fuels-test-helpers/src/accounts.rs:
pub async fn launch_provider_and_get_wallet() -> Result<Wallet> { let mut wallets = launch_custom_provider_and_get_wallets(WalletsConfig::new(Some(1), None, None), None, None) .await?; Ok(wallets.pop().expect("should have one wallet")) }在测试代码中的使用方式极为简洁:
let wallet = launch_provider_and_get_wallet().await?;它会启动一个短生命周期节点、创建默认配置的 Provider、并返回一个已经预置了基础资产(默认数量的 base asset coins)的钱包,可直接用于部署合约、转账与调用。几乎每个合约集成测试(可参见 examples/contracts/src/lib.rs 中大量的launch_provider_and_get_wallet().await?用法)都以它作为起点。
进阶:launch_custom_provider_and_get_wallets
当需要多钱包、多种资产的测试场景时,使用更灵活的launch_custom_provider_and_get_wallets。它接收一个WalletsConfig描述钱包数量与各钱包的资产分布。看 accounts.rs 的实现可以发现两个工程细节:
- 每个钱包的私钥由序号确定性生成(
wallet_counter转成字节填充到密钥中),保证同一配置下每次测试的钱包地址一致、可复现; - 所有钱包的资产会汇总后交给
setup_test_provider预置到链上,再为每个签名者Wallet::new(signer, provider.clone())克隆共享同一个 Provider。
配套示例同样位于 examples/wallets/src/lib.rs:用WalletsConfig::new(Some(num_wallets), Some(coins_per_wallet), Some(coin_amount))配置 2 个钱包、每钱包 1 枚币、每枚金额 2,然后两两之间做转账。
适用场景
短生命周期节点专为测试设计:快速启动、状态完全隔离、随用例结束而销毁,不会污染任何持久化网络。用launch_provider_and_get_wallet()编写的测试用例可以在 CI 中反复运行且互不干扰。
两种方式的取舍小结
| 维度 | 连接 Testnet / 外部节点 | 启动临时测试节点 |
|---|---|---|
| 典型入口 | Provider::connect("testnet.fuel.network")等 | launch_provider_and_get_wallet() |
| 节点来源 | 外部已运行的fuel-core(Testnet/自建) | SDK 进程内启动,随测试结束销毁 |
| 状态 | 持久化,与真实网络一致 | 每次全新、可复现 |
| 前置条件 | 节点可达;Testnet 钱包需 faucet 领水 | 依赖 fuel-core 库或二进制(见下节 feature) |
| 适用场景 | 应用开发、联调、长期运行的程序 | 智能合约单元/集成测试 |
当你要测试的合约调用逻辑对链上既有状态有依赖(例如读取 Testnet 上已部署合约)时,可优先考虑方式一;反之,纯粹的"部署 + 调用 + 断言"类测试应一律使用方式二,以获得最快的反馈循环。
支撑 feature 配置:fuel-core-lib 与 rocksdb
前面提到方式二需要运行fuel-core,这引出一个关键配置问题:本机是否安装了fuel-core二进制?
fuel-core-lib:免二进制运行节点
默认情况下,SDK 起临时节点依赖本机安装的fuel-core可执行文件(相关分支见 fuels-test-helpers/src/service.rs 中按cfg(feature = "fuel-core-lib")分隔的两套FuelService实现,其中二进制模式的服务定义在 fuel_bin_service.rs)。若不想安装二进制,可以启用fuel-core-libfeature,让 SDK 以库的形式内嵌fuel-core——代价是需要随依赖下载并编译运行节点所需的全部依赖:
[dependencies] fuels = { version = "0.77", features = ["fuel-core-lib"] }说明:版本号需与当前所用 SDK 版本保持一致;具体 feature 的依赖声明可参考仓库中的 fuels/Cargo.toml(
fuel-core-lib = ["fuels-test-helpers?/fuel-core-lib"])与 fuels-test-helpers/Cargo.toml(fuel-core-lib = ["dep:fuel-core"]),feature 从fuels逐层转发到fuels-test-helpers。
rocksdb:本地持久化存储
rocksdb是另一个附加 feature,与fuel-core-lib组合使用时,可以让内嵌节点把区块链状态持久化到本地数据库,从而在节点重启后复用历史状态:
[dependencies] fuels = { version = "0.77", features = ["rocksdb"] }用法上,rocksdb文档给出了创建或复用本地数据库的模式:若指定路径的数据库不存在则新建。如果使用这一功能,要么本机有fuel-core二进制,要么同时开启fuel-core-lib与rocksdb两个 feature,否则相关辅助函数无法工作。该特性适用于需要在多次进程运行之间保留链状态的调试与回放场景。详见 rocksdb.md 与示例 create_or_use_rocksdb(示例位于 examples/cookbook crate)。
连接之后:Provider 提供的链上查询能力
一旦拿到Provider,即可与 Fuel 区块链交互。下面是在 examples/providers/src/lib.rs 的query_the_blockchain测试中验证过的三类基础查询(测试前通过setup_test_provider预置一枚基础资产):
获取某地址的全部未花费 coin(按指定资产 ID 过滤):
let consensus_parameters = provider.consensus_parameters().await?; let coins = provider .get_coins( &wallet_signer.address(), *consensus_parameters.base_asset_id(), ) .await?; assert_eq!(coins.len(), 1);获取某地址的可花费资源(用ResourceFilter指定目标地址、资产与金额,并可排除特定 UTXO/消息 ID):
let filter = ResourceFilter { from: wallet_signer.address(), amount: 1, ..Default::default() }; let spendable_resources = provider.get_spendable_resources(filter).await?;ResourceFilter中资产 ID 与排除列表均有默认值(分别解析为基础资产 ID 与空 ID 列表),所以可以用..Default::default()省略字段;其定义可查 fuels-accounts/src/provider.rs。
获取某地址全部资产的余额(只汇总各资产 UTXO 金额,不返回具体 coin 数据,语义上与get_coins有区别):
let _balances = provider.get_balances(&wallet_signer.address()).await?;完整的区块查询 API 说明见 querying.md。
连接可靠性:为 Provider 配置请求重试
网络环境不稳定时,Provider可以配置"收到io::Error即重试"。仓库当前将节点返回的各类错误统一封装为io::Error,因此一旦配置了重试,即便发生交易校验失败也会触发重试逻辑——配置前需评估自己的业务是否能接受这种语义。
通过RetryConfig可以同时控制最大尝试次数与退避(间隔)策略:
let retry_config = RetryConfig::new(3, Backoff::Fixed(Duration::from_secs(2)))?; let provider = setup_test_provider(coins.clone(), vec![], None, None) .await? .with_retry_config(retry_config);Backoff提供三种间隔策略(定义见 retry_util.rs,更完整的重试说明见 retrying.md):
Linear(Duration)(默认):每次尝试后等待时间线性递增;Exponential(Duration):每次尝试后等待时间翻倍;Fixed(Duration):各次尝试间使用固定等待时长。
总结
在 Fuel Rust SDK 中,"连接节点"是贯穿开发与测试的一条主线:面向应用,使用Provider::connect对接 Testnet 或自建的fuel-core节点,必要时配合connect_with_fallbacks与RetryConfig提升可用性;面向合约测试,则善用launch_provider_and_get_wallet/launch_custom_provider_and_get_wallets拉起进程内短生命周期节点,并通过fuel-core-lib(免装二进制)与rocksdb(持久化状态)两个 feature 按需裁剪运行环境。理解这两类连接方式的边界与内部实现,是写出可靠、可复现的 Fuel 链上应用与测试的第一步。相关进阶内容还可以继续阅读 external-node.md、short-lived.md、querying.md、retrying.md 与 rocksdb.md。
【免费下载链接】fuels-rsFuel Network Rust SDK项目地址: https://gitcode.com/GitHub_Trending/fu/fuels-rs
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考