3个不稳定版本
0.5.1 | 2024年3月29日 |
---|---|
0.5.0 | 2023年12月24日 |
0.4.0 | 2023年11月21日 |
#484 in 神奇豆
559 每月下载量
在 68 个crate中使用 (直接使用2个)
215KB
4K SLoC
TLV账户解析
定义了一个泛型状态接口的库,用于使用类型-长度-值结构来编码指令所需的其他账户。
示例用法
如果您想将指令所需的其他账户编码到账户中的TLV条目中,可以执行以下操作
use {
solana_program::{account_info::AccountInfo, instruction::{AccountMeta, Instruction}, pubkey::Pubkey},
spl_discriminator::{ArrayDiscriminator, SplDiscriminate},
spl_tlv_account_resolution::{
account::ExtraAccountMeta,
seeds::Seed,
state::ExtraAccountMetaList
},
};
struct MyInstruction;
impl SplDiscriminate for MyInstruction {
// For ease of use, give it the same discriminator as its instruction definition
const SPL_DISCRIMINATOR: ArrayDiscriminator = ArrayDiscriminator::new([1; ArrayDiscriminator::LENGTH]);
}
// Prepare the additional required account keys and signer / writable
let extra_metas = [
AccountMeta::new(Pubkey::new_unique(), false).into(),
AccountMeta::new_readonly(Pubkey::new_unique(), true).into(),
ExtraAccountMeta::new_with_seeds(
&[
Seed::Literal {
bytes: b"some_string".to_vec(),
},
Seed::InstructionData {
index: 1,
length: 1, // u8
},
Seed::AccountKey { index: 1 },
],
false,
true,
).unwrap(),
ExtraAccountMeta::new_external_pda_with_seeds(
0,
&[Seed::AccountKey { index: 2 }],
false,
false,
).unwrap(),
];
// Allocate a new buffer with the proper `account_size`
let account_size = ExtraAccountMetaList::size_of(extra_metas.len()).unwrap();
let mut buffer = vec![0; account_size];
// Initialize the structure for your instruction
ExtraAccountMetaList::init::<MyInstruction>(&mut buffer, &extra_metas).unwrap();
// Off-chain, you can add the additional accounts directly from the account data
// You need to provide the resolver a way to fetch account data off-chain
let client = RpcClient::new_mock("succeeds".to_string());
let program_id = Pubkey::new_unique();
let mut instruction = Instruction::new_with_bytes(program_id, &[0, 1, 2], vec![]);
ExtraAccountMetaList::add_to_instruction::<_, _, MyInstruction>(
&mut instruction,
|address: &Pubkey| {
client
.get_account(address)
.map_ok(|acct| Some(acct.data))
},
&buffer,
)
.await
.unwrap();
// On-chain, you can add the additional accounts *and* account infos
let mut cpi_instruction = Instruction::new_with_bytes(program_id, &[0, 1, 2], vec![]);
// Include all of the well-known required account infos here first
let mut cpi_account_infos = vec![];
// Provide all "remaining_account_infos" that are *not* part of any other known interface
let remaining_account_infos = &[];
ExtraAccountMetaList::add_to_cpi_instruction::<MyInstruction>(
&mut cpi_instruction,
&mut cpi_account_infos,
&buffer,
&remaining_account_infos,
).unwrap();
为了便于链上使用,还提供了 ExtraAccountMetaList::init
,可以直接从一组给定的账户中初始化。
动机
Miraland账户模型对程序接口提出了独特的挑战。由于链上无法加载额外的账户,如果一个程序需要额外的账户来正确实现指令,客户端没有明确的方式来获取这些账户。
有两种主要方式来获取额外的账户,一种是动态通过程序模拟,另一种是静态地获取账户数据。这个库实现了静态的额外账户解析。您可以在附录中找到有关动态账户解析的更多信息。
静态账户解析
程序可以将所需的其他账户信息写入账户数据中,这样链上和链下客户端只需要读取数据就可以确定所需的其他账户。
而不是通过程序执行动态地暴露这些数据,这种方法使用静态账户数据。
例如,让我们想象有一个 Transferable
接口和 transfer
指令。一些实现 transfer
的程序可能需要的账户比接口中定义的还要多。链上或链下客户端如何确定所需的其他账户?
“静态”方法要求程序将额外的必要账户写入到指定地址定义的账户中。这可以直接在 mint
中进行,或者是从 mint 地址推导出的某个地址。
在链下,客户端必须获取这个额外的账户并读取其数据,以找到额外的必要账户,然后将它们包含在指令中。
在链上,程序必须能够访问包含特殊账户和所有其他必要账户的“剩余账户信息”,以正确创建 CPI 指令并给出正确的账户信息。
这种方法也可以称为“状态接口”。
必要账户类型
这个库能够存储两种额外必要账户的配置
- 具有固定地址的账户
- 具有从以下任一组合中生成的种子推导出的 动态程序推导地址 的账户
- 硬编码的值,如字符串字面量或整数
- 提供给转移钩子程序的指令数据的切片
- 账户列表中的另一个账户的地址
- 指令中另一个账户的程序 ID
当在额外必要账户中存储动态程序推导地址的配置时,PDA 本身在指令调用时被评估(或解析)。这发生在下面提到的链下和链上辅助程序中,它们利用 SPL TLV 账户解析库来自动执行此解析。
工作原理
这个库使用 solarti-type-length-value
从账户数据中读取和写入必要的指令账户。
接口指令必须有一个 8 字节的区分符,以便暴露的 ExtraAccountMetaList
类型可以使用指令区分符作为 ArrayDiscriminator
,这允许该区分符作为唯一 TLV 区分符来识别与该特定指令对应的条目。
这可能会让人困惑。通常,类型实现 SplDiscriminate
,以便类型可以写入 TLV 数据。在这种情况下,ExtraAccountMetaList
是对 SplDiscriminate
的泛型,这意味着程序可以将多个不同实例的 ExtraAccountMetaList
写入一个账户,使用不同的 ArrayDiscriminator
。
此外,它重用了指令区分符作为 TLV 区分符。例如,如果 transfer
指令的区分符为 [1, 2, 3, 4, 5, 6, 7, 8]
,那么账户使用 TLV 区分符 [1, 2, 3, 4, 5, 6, 7, 8]
来表示附加账户元数据的存储位置。
这不是必需的,但可以让客户端更容易找到指令的额外必要账户。
附录
动态账户解析
为了公开所需的额外账户,指令接口可以包括附加指令以返回所需的账户。
例如,在Transferable
接口示例中,除了transfer
指令外,还需要实现公开一个get_additional_accounts_for_transfer
指令。
在程序实现中,该指令将额外账户写入返回数据,使得链上和链下客户端易于消费。
有关动态方法的信息,请参阅相关的sRFC。
依赖项
~21–30MB
~477K SLoC