文章介绍了如何在Solana程序中使用Anchor框架读取账户余额,并详细解释了UncheckedAccount
的使用及其安全性考虑。
要在Solana程序中读取地址的Solana余额,请使用以下代码:
use anchor_lang::prelude::*;
declare_id!("Gnf6u7S7fGJbqEGH9PuDE5Prq6f6ZrDxHY3jNJ4SYySQ");
#[program]
pub mod balance {
use super::*;
pub fn read_balance(ctx: Context<ReadBalance>) -> Result<()> {
let balance = ctx.accounts.acct.to_account_info().lamports();
msg!("余额以Lamports表示为 {}", balance);
Ok(())
}
}
#[derive(Accounts)]
pub struct ReadBalance<'info> {
/// CHECK: 尽管我们读取这个账户的余额,但我们并没有用到这个信息
pub acct: UncheckedAccount<'info>,
}
下面是触发它的web3 js代码:
import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { Balance } from "../target/types/balance";
describe("balance", () => {
// 配置客户端以使用本地集群。
anchor.setProvider(anchor.AnchorProvider.env());
const program = anchor.workspace.Balance as Program<Balance>;
// 以下是我们使用的Solana钱包
let pubkey = new anchor.web3.PublicKey("5jmigjgt77kAfKsHri3MHpMMFPo6UuiAMF19VdDfrrTj");
it("测试余额", async () => {
const tx = await program.methods.readBalance().accounts({ acct: pubkey }).rpc();
});
});
本示例中的某些项与以前的教程不同,特别是使用UncheckedAccount
。
UncheckedAccount
?UncheckedAccount
类型通知Anchor不检查正在读取的账户是否被该程序拥有。
注意,我们通过Context
结构传递的账户并不是该程序初始化的账户,因此该程序并不拥有它。
当Anchor读取#[derive(Accounts)]
中的Account
类型账户时,它会检查(在后台)该账户是否被该程序拥有。如果不拥有,执行将会停止。
这作为一个重要的安全检查。
如果恶意用户构造了一个该程序未创建的账户,并将其传递给Solana程序,而Solana程序盲目信任该账户中的数据,就可能发生严重错误。
例如,如果该程序是一个银行,而该账户存储了用户的余额,那么黑客可以提供一个不同的账户,其人工作高于实际余额。
不过,要实施这个黑客,用户必须在单独的交易中创建虚假账户,然后将其传递给Solana程序。然而,Anchor框架会在后台检查该账户是否不被程序拥有,并拒绝读取该账户。
UncheckedAccount
绕过了这个安全检查。
重要:AccountInfo
和UncheckedAccount
是彼此的别名,并且AccountInfo
具有相同的安全性考虑。
在我们的案例中,我们传递的账户显然不被程序拥有——我们想检查一个任意账户的余额。因此,我们必须确保移除这个安全检查后没有关键逻辑可以被篡改。
在我们的案例中,我们只是将余额记录到控制台,但大多数现实案例会有更复杂的逻辑。
/// CHECK:
?由于使用UncheckedAccount
的危险性,Anchor强制你包括此注释,以鼓励你不要忽视安全考虑。
练习:删除/// CHECK:
注释并运行anchor build
,你应该看到构建停止,并要求你添加回注释并解释为什么未经检查的账户是安全的。也就是说,读取不可信的账户可能是危险的,Anchor希望确保你不对账户中的数据执行任何关键操作。
#[account]
结构?#[account]
结构告诉Anchor如何反序列化持有数据的账户。例如,以下看似的账户结构将通知Anchor将存储在账户中的数据反序列化为一个单一的u64
:
#[account]
pub struct Counter {
counter: u64
}
然而,在我们的案例中,我们并不想从账户中读取数据——我们只想读取余额。这类似于我们可以读取以太坊地址的余额,而无法读取其代码。由于我们不想反序列化数据,因此我们不提供#[account]
结构。
回忆一下我们关于Solana账户租金的讨论,账户必须保持一定的SOL余额以便“免租”,否则运行时将删除该账户。仅仅因为账户中有“1 SOL”并不一定意味着该账户可以支配全部1 SOL。
例如,如果你正在构建一个质押或银行应用程序,其中用户存入的SOL存放在单独的账户中,那么简单地测量这些账户的SOL余额并不准确,因为租金将包含在余额中。
请参阅我们的Solana开发者课程以获取更多Solana材料。
最初发布于2024年2月29日
- 原文链接: rareskills.io/post/solan...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!