本文详细介绍了在Solana链上程序中如何读取不属于自己的账户数据,通过创建data_holder
和data_reader
两个程序,展示了如何初始化并读取PDA中的数据,并探讨了Anchor框架下的数据反序列化机制及其限制。
在 Solidity 中,读取另一个合约的存储需要调用 view
函数或者存储变量是公共的。在 Solana 中,离线客户端可以直接读取存储账户。这个教程展示了一个链上 Solana 程序如何读取它不拥有的账户中的数据。
我们将设置两个程序: data_holder
和 data_reader
。 data_holder
将初始化并拥有一个 PDA,其数据将被 data_reader
读取。
data_holder
程序: Shell 1以下代码是一个基本的 Solana 程序,它初始化账户 Storage
,其中包含 u64
字段 x
,并在初始化时将值 9 存储在其中:
Typescript 代码:
import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { DataHolder } from "../target/types/data_holder";
describe("data-holder", () => {
anchor.setProvider(anchor.AnchorProvider.env());
const program = anchor.workspace.DataHolder as Program<DataHolder>;
it("Is initialized!", async () => {
const seeds = [];
const [storage, _bump] = anchor.web3.PublicKey.findProgramAddressSync(
seeds,
program.programId
);
await program.methods
.initialize()
.accounts({ storage: storage })
.rpc();
let storageStruct = await program.account.storage.fetch(
storage
);
console.log("The value of x is: ",storageStruct.x.toString());
console.log("Storage account address: ", storage.toBase58());
});
});
测试将打印出 PDA 的地址,我们稍后会提到这个地址:
为了让 data_reader
读取另一个账户,必须通过 Context
结构将该账户的公共密钥作为交易的一部分传递。这与传递任何其他类型的账户没有区别。
账户中的数据以序列化字节的形式存储。 为了反序列化账户,data_reader
程序需要读取它的 Rust 结构定义。我们需要以下账户定义,并且它与 data_holder
中的 Storage
结构完全相同:
#[account]
pub struct Storage {
x: u64,
}
这个结构与 data_reader
中的结构完全相同——连名称也必须相同(稍后我们会介绍为什么)。读取账户的代码在以下两行中:
let mut data_slice: &[u8] = &data_account.data.borrow();
let data_struct: Storage =
AccountDeserialize::try_deserialize(
&mut data_slice,
)?;
data_slice
是账户中数据的原始字节。如果你运行 solana account
<pda address>
(使用我们在部署 data_holder
时生成的 PDA 地址),你可以在那里看到数据,包括我们储存在红框中的数字 9:
黄框中前 8 个字节是账户鉴别符,稍后我们将对此进行描述。
反序列化发生在此步骤:
let data_struct: Storage =
AccountDeserialize::try_deserialize(
&mut data_slice,
)?;
在这里传递类型 Storage
(我们上面定义的结构),告诉 Solana 如何(尝试)反序列化数据。
现在让我们在新文件夹中创建一个单独的 anchor 项目 anchor new data_reader
。
完整的 Rust 代码如下:
use anchor_lang::prelude::*;
declare_id!("HjJ1Rqsth5uxA6HKNGy8VVRvwK4W7aFgmQsss7UxePBw");
#[program]
pub mod data_reader {
use super::*;
pub fn read_other_data(
ctx: Context<ReadOtherData>,
) -> Result<()> {
let data_account = &ctx.accounts.other_data;
if data_account.data_is_empty() {
return err!(MyError::NoData);
}
let mut data_slice: &[u8] = &data_account.data.borrow();
let data_struct: Storage =
AccountDeserialize::try_deserialize(
&mut data_slice,
)?;
msg!("The value of x is: {}", data_struct.x);
Ok(())
}
}
#[error_code]
pub enum MyError {
#[msg("No data")]
NoData,
}
#[derive(Accounts)]...
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!