Solana笔记 05.解决常见报错问题

跟我一起从0开始学习Solana合约开发,一起实操,一起做项目。这是一个系列文章,系统地记录了我的学习笔记。

为了让知识点更有趣,也更贴近实际应用,接下来我们主动“制造”三个常见的报错,通过解决这些问题,熟悉程序开发中的调整和优化过程。初学者执行 anchor test 时,这些问题几乎百分百会遇到,而且很可能让人抓狂!现在,我们就一起来挑战吧。

如何处理 Program ID 不匹配问题?

删除 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。如果扩展后仍不足,可继续增加空间。

如何处理计算单位(CUs)超出限制的问题?

扩展账户数据空间后,再次运行测试,可能会出现以下错误:

"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]

总结

通过以上三个问题的解决,你已经具备了如下能力:

  1. 你掌握了 Program ID 的同步方法。
  2. 你学会了如何为程序账户扩展数据空间。
  3. 你了解了计算单位限制的优化技巧。

这些实践不仅能让你顺利的动手操作,还加深了对 Solana 开发的理解。解决问题的过程,就是提升技能的过程!我们继续加油,探索更多吧!如果你想提前看到我的更新,可以关注我的公众号:认知那些事

点赞 1
收藏 1
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
认知那些事
认知那些事
0x2b62...95a0
人立于天地之间,必然有我们的出路。