跟我一起从0开始学习Solana合约开发,一起实操,一起做项目。这是一个系列文章,系统地记录了我的学习笔记。
为了让知识点更有趣,也更贴近实际应用,接下来我们主动“制造”三个常见的报错,通过解决这些问题,熟悉程序开发中的调整和优化过程。初学者执行 anchor test
时,这些问题几乎百分百会遇到,而且很可能让人抓狂!现在,我们就一起来挑战吧。
删除 target/deploy/<program_name>.json
文件后,执行以下命令:
anchor test --skip-local-validator
你可能会看到这样的报错:
Error: AnchorError occurred. Error Code: DeclaredProgramIdMismatch. Error Number: 4100. Error Message: The declared program id does not match the actual program id.
原因分析:
target/deploy/<program_name>.json
是 Anchor 在初次运行 anchor build
时自动生成的文件,其中存储了程序的密钥对(包括公钥,即 Program ID)。
当你删除这个文件后,下一次运行 anchor build 时,Anchor 会自动生成新的密钥对,并写入新的 Program ID。然而,此时你的 lib.rs 文件中通过 declare_id!("..."); 声明的 Program ID 仍然是旧的 ID,与刚生成的新 Program ID 不匹配,因此会导致上述错误。
解决方法:
使用以下命令同步 Program ID:
anchor keys sync
该命令会读取 target/deploy/<program_name>.json
文件中的新 Program ID,并自动更新 lib.rs 文件中 declare_id!("...")
; 的内容,使其与新生成的 Program ID 一致。
在上一篇文章最后,我们把学习的所有示例代码都加入到src/lib.rs
的初始化函数中了,如下所示:
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
msg!("Greetings from: {:?}", ctx.program_id);
// 简单类型
let pubkey = Pubkey::new_from_array([0; 32]);
let balance: u64 = 1000;
let is_active: bool = true;
msg!("pubkey: {:?}, balance: {}, is_active: {}", pubkey, balance, is_active);
// 映射类型
let mut hmap: HashMap<u64, u64> = HashMap::new();
hmap.insert(1, 1001);
let mut bmap: BTreeMap<u64, u64> = BTreeMap::new();
bmap.insert(2, 1002);
msg!("HashMap: {:?}, BTreeMap: {:?}", hmap, bmap);
// 数组类型(无默认值)
let mut data1 = vec![];
data1.push(42);
data1.push(7);
msg!("data1: {:?}", data1);
// 数组类型(有默认值)
let data2: Vec<u8> = vec![0; 1024];
msg!("data2: {:?}", data2);
Ok(())
}
运行以下命令:
anchor test --skip-local-validator
你可能会遇到以下报错:
Transaction simulation failed: Error processing Instruction 0: account data too small for instruction [3 log messages]
原因分析:
在你第一次部署并测试程序时,Solana 会根据字节码大小为程序账户分配存储空间。即使测试报错,Solana 也依然会分配存储空间。
而现在我们加入了大量的rust代码后,程序的字节码变多了,分配的数据空间不够用了,因此,导致了这个错误的发生。
注意
如果你更新了 Program ID,那可能不会报这个错,因为对于新的 Program ID,这是第一次部署,自然就会根据这个代码量来分配账户存储空间。
解决方法:
运行以下命令扩展数据空间:
solana program extend <PROGRAM_ID> <ADDITIONAL_BYTES>
例如,增加 1440 字节(具体数值可根据实际需求调整):
solana program extend 7pR9Lstthnphgx2bLFo6WW4gbRJmGiJDujL4wxYRFRWg 1440
注意:账户数据的上限为 10MB。如果扩展后仍不足,可继续增加空间。
扩展账户数据空间后,再次运行测试,可能会出现以下错误:
"Program 7pR9Lstthnphgx2bLFo6WW4gbRJmGiJDujL4wxYRFRWg consumed 200000 of 200000 compute units", "Program 7pR9Lstthnphgx2bLFo6WW4gbRJmGiJDujL4wxYRFRWg failed: exceeded CUs meter at BPF instruction"
原因分析:
Solana 程序默认的计算单位限制为 200,000 CUs,而我们增加的 vec! 代码里,有这么一行:
let data2: Vec<u8> = vec![0; 1024];
1024字节动态数组的计算量超出了限制,导致了这个错误的发生。
解决方法:
有两种方法可以解决:
1. 增加 CUs 限制
修改测试代码,在发送交易前增加计算单位限制(需要修改 tests/guide_1.ts 文件):
const tx = new Transaction().add(
ComputeBudgetProgram.setComputeUnitLimit({
units: 300000,
}),
);
2. 优化代码(推荐)
减少不必要的计算。例如,将数组大小从 1024 调整为 10:
let data2: Vec<u8> = vec![0; 10];
现在,我们修改代码之后,再执行测试,打开运行solana logs
的终端,如果你看到如下日志信息,就表示测试成功了。
Program log: Greetings from: 7pR9Lstthnphgx2bLFo6WW4gbRJmGiJDujL4wxYRFRWg
Program log: pubkey: 11111111111111111111111111111111, balance: 1000, is_active: true
Program log: HashMap: {1: 1001}, BTreeMap: {2: 1002}
Program log: data1: [42, 7]
Program log: data2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
通过以上三个问题的解决,你已经具备了如下能力:
这些实践不仅能让你顺利的动手操作,还加深了对 Solana 开发的理解。解决问题的过程,就是提升技能的过程!我们继续加油,探索更多吧!如果你想提前看到我的更新,可以关注我的公众号:认知那些事
。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!