测试依赖跨程序调用(CPI)的 Solana 程序,传统上需要开发者从主网导出账户和程序,然后将其上传到本地验证器。
这种方法适用于少量账户,但在测试像 Jupiter 这样复杂的程序时完全不可行,因为它可能依赖于 40 多个账户和 8 个以上的程序。
Surfpool 通过以下功能解决了这个问题,使开发者能够使用按需获取的主网账户在本地模拟程序:
Surfpool 是一个全面的 SDK 和工具套件,而 Surfnet 是它创建的本地 Solana 网络。可以将 Surfpool 视为您的开发环境,而 Surfnet 则是运行在其中的区块链网络。
Surfnet 是 solana-test-validator 的直接替代品,专为 Solana 开发者提供最佳开发体验而设计。
由 TxTx 团队开发,Surfpool 无缝将基础设施即代码(Infrastructure as Code)集成到基于 Anchor 的项目中,从而实现可复现、可审计且安全的部署,无论是到私有还是公共的 Solana 网络。
运行 Surfnet 所需的一切都包含在 Surfpool SDK 中。由于安装因操作系统而异,请根据您的具体设置,参阅官方的安装页面。
安装 Surfpool SDK 后,使用以下命令启动 Surfnet:
surfpool start
这将在标准本地验证器端口(http://127.0.0.1:8899)上启动 Surfnet,并显示一个终端用户界面,其中包括:
如果需要基于浏览器的体验,请通过此链接连接到 Surfpool Studio。
⚠️ Surfpool Studio 包含所有终端用户界面功能,并提供额外的功能,例如时间旅行和为任何铸币账户添加代币。
设置 Surfnet 后,部署您的程序并开始构建。
Anchor 程序:使用 anchor deploy,并将您的 Anchor.toml 配置为 [programs.localnet]。
Pinocchio 程序:通过 Solana CLI 部署,并将您的配置设置为 localnet:solana program deploy ./target/deploy/your_program.so --program-id ./target/deploy/your_program-keypair.json
⚠️ 在 Anchor Workspace 中运行
surfpool start,可提示您使用基础设施即代码(Infrastructure as Code),以便在 Surfnet 上无缝自动部署您的程序。
部署到 localnet 后,通过定位本地验证器连接到 Surfnet:
import { Connection } from "@solana/web3.js";
const connection = new Connection("http://localhost:8899", "confirmed");
Surfnet 不仅仅是将主网复制到本地网络,它还允许您通过更改账户、系统变量和区块链状态来增强和修改 Solana 的核心功能。
这些修改通过对 Surfnet 的 RPC 调用实现,既可以通过编程方式(用于自动化测试环境)进行更改,也可以通过终端命令(用于一次性操作)进行更改。
在代码中执行 RPC 调用,请使用:
const surfnetCall = {
"jsonrpc": "2.0",
"id": 1,
"method": "<surfnet-method>",
"params": [<surfnet-method-params]
}
await fetch(connection.rpcEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(surfnetCall)
});
在终端中执行 RPC 调用,请使用:
curl -X POST \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "<surfnet-method>",
"params": [<surfnet-method-params]
}' \
http://localhost:8899
使用 surfnet_timeTravel 方法跳转到区块链历史中的任意时间点。这对于测试您的程序在不同网络状态下的表现或重现特定时间发生的错误非常有价值。
指定以下之一:absoluteEpoch、absoluteSlot 或 absoluteTimestamp,如下所示:
{
"jsonrpc": "2.0",
"id": 1,
"method": "surfnet_timeTravel",
"params": [
{
"absoluteEpoch": 0
}
]
}
控制区块进程以逐步调试交易或测试时间敏感的逻辑。使用 surfnet_pauseClock 暂停区块生成,使用 surfnet_resumeClock 恢复:
{
"jsonrpc": "2.0",
"id": 1,
"method": "surfnet_pauseClock", // or "surfnet_resumeClock"
"params": []
}
配置 SOL 供应参数以测试经济场景或边界情况。
surfnet_setSupply 方法接受 circulating、nonCirculating、nonCirculatingAccounts 和 total 参数:
{
"jsonrpc": "2.0",
"id": 1,
"method": "surfnet_setSupply",
"params": [
{
"circulating": 1000000000,
"nonCirculating": 1000000000,
"nonCirculatingAccounts": [],
"total": 1000000000
}
]
}
使用 surfnet_cloneProgramAccount 克隆程序账户,从一个程序转移到另一个程序。这对于测试程序升级或在不同程序版本之间迁移状态非常有用。
需要同时提供 destinationProgramId 和 sourceProgramId:
{
"jsonrpc": "2.0",
"id": 1,
"method": "surfnet_cloneProgramAccount",
"params": [
{
"destinationProgramId": "string",
"sourceProgramId": "string"
}
]
}
⚠️ 使用以下方法更新已存在的账户将会覆盖账户状态
使用surfnet_setAccount修改任何账户的属性,包括 lamports、数据、所有者和可执行状态。
该方法需要一个pubkey,并接受以下可选字段:data、executable、lamports、owner 和 rentEpoch:
{
"jsonrpc": "2.0",
"id": 1,
"method": "surfnet_setAccount",
"params": [
"1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM",
{
"data": "0x123456",
"executable": false,
"lamports": 1000000000,
"owner": "11111111111111111111111111111111",
"rentEpoch": 0
}
]
}
此外,对于代币账户的修改,可以使用专门的surfnet_setTokenAccount方法,该方法简化了代币账户数据的更改。
需要owner和mint参数,并接受以下可选字段:amount、closeAuthority、delegate、delegateAmount、state 和 tokenProgram:
{
"jsonrpc": "2.0",
"id": 1,
"method": "surfnet_setTokenAccount",
"params": [
"1111111ogCyDbaRMvkdsHB3qfdyFYaG1WtRUAfdh",
"11111112D1oxKts8YPdTJRG5FzxTNpMtWmq8hkVx3",
{
"amount": 1000000000,
"closeAuthority": "111111131h1vYVSYuKP6AhS86fbRdMw9XHiZAvAaj",
"delegate": "11111112cMQwSC9qirWGjZM6gLGwW69X22mqwLLGP",
"delegatedAmount": 1000000000,
"state": "initialized"
},
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
]
}