Solana 60 天课程

2025年02月27日更新 89 人订阅
原价: ¥ 66 限时优惠
专栏简介 开始 Solana - 安装与故障排除 Solana 和 Rust 中的算术与基本类型 Solana Anchor 程序 IDL Solana中的Require、Revert和自定义错误 Solana程序是可升级的,并且没有构造函数 Solidity开发者的Rust基础 Rust不寻常的语法 Rust 函数式过程宏 Rust 结构体与属性式和自定义派生宏 Rust 和 Solana 中的可见性与“继承” Solana时钟及其他“区块”变量 Solana 系统变量详解 Solana 日志、“事件”与交易历史 Tx.origin、msg.sender 和 onlyOwner 在 Solana 中:识别调用者 Solana 计算单元与交易费用介绍 在 Solana 和 Anchor 中初始化账户 Solana 计数器教程:在账户中读写数据 使用 Solana web3 js 和 Anchor 读取账户数据 在Solana中创建“映射”和“嵌套映射” Solana中的存储成本、最大存储容量和账户调整 在 Solana 中读取账户余额的 Anchor 方法:address(account).balance 功能修饰符(view、pure、payable)和回退函数在 Solana 中不存在的原因 在 Solana 上实现 SOL 转账及构建支付分配器 使用不同签名者修改账户 PDA(程序派生地址)与 Solana 中的密钥对账户 理解 Solana 中的账户所有权:从PDA中转移SOL Anchor 中的 Init if needed 与重初始化攻击 Solana 中的多重调用:批量交易与交易大小限制 Solana 中的所有者与权限 在Solana中删除和关闭账户与程序 在 Anchor 中:不同类型的账户 在链上读取另一个锚点程序账户数据 在 Anchor 中的跨程序调用(CPI) SPL Token 的运作方式 使用 Anchor 和 Web3.js 转移 SPL Token Solana 教程 - 如何实现 Token 出售 基础银行教程 Metaplex Token 元数据工作原理 使用Metaplex实施代币元数据 使用 LiteSVM 进行时间旅行测试 Solana Token-2022 标准规范 生息代币第一部分 计息代币第二部分 Solana 指令自省 Solana 中的 Ed25519 签名验证 Solana - Switchboard 预言机使用 原生Solana:程序入口与执行 原生 Solana :读取账户数据 原生 Solana :Borsh 序列化 原生 Solana:使用 invoke 和 invoke signed 进行跨程序调用 原生 Solana :创建存储账户 (一) 原生 Solana:创建存储账户 二 原生 Solana: 函数分发 原生 Solana:关键安全检查 Rust 程序到 SBF 编译 sBPF 虚拟机和指令集介绍 跟踪 sBPF 指令执行和计算成本 Solana 程序执行与输入序列化 指令处理器和运行时设置 sBPF 内存布局和寄存器约定 使用 sBPF 汇编读取 Solana 指令输入 Solana 系统调用:sBPF 汇编中的日志记录

原生 Solana :创建存储账户 (一)

这篇教程详细讲解了如何在Solana原生程序中使用密钥对创建账户来存储数据。内容涵盖了账户分配、初始化、Borsh数据序列化以及与System Program的交互,并通过Rust程序和TypeScript客户端示例,演示了账户创建、数据写入与验证的完整过程。

原生 Solana:创建账户以进行存储 (一)

在本分为两部分的教程中,我们将学习如何使用两种方法在原生 Solana 程序中创建用于存储数据的账户:密钥对(本部分)和程序派生地址 (PDA)(第二部分)。我们的目标是深入理解账户分配、初始化和数据序列化——这是 Anchor 的 #[account(init)] 所抽象出的底层逻辑。

对于这两种方法,我们都将构建一个程序,它创建一个账户并将数据写入其中,然后使用 TypeScript 客户端进行测试以验证数据是否正确写入。

设置密钥对存储程序

运行以下命令创建目录并使用 Cargo 初始化 Rust 项目:

Copymkdir solana-storage-write
cd solana-storage-write
cargo init --lib

Cargo.toml 更新为以下配置,它设置了 crate 类型并添加了 Solana 程序依赖项:

Copy[package]
name = "solana-storage-write"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib", "lib"]

[dependencies]
solana-program = "1.18"
borsh = "0.10"

请注意,我们添加了 borsh = "0.10" 用于序列化,这在创建账户时是必需的。我们已经在之前的教程中详细介绍了 Borsh 序列化。

现在我们来创建我们的程序。

在原生 Solana 程序中用密钥对创建账户

数据存储的账户结构

在深入代码之前,让我们了解一下存储数据的 Solana 账户是怎样的:

A diagram showing Solana’s account structure

data 字段是我们存储序列化结构体的地方(我们稍后会讲到)。当我们创建一个账户时,我们会指定该字段应容纳多少字节,系统程序会分配该空间。

创建基于密钥对的存储账户的步骤

以下步骤展示了如何创建用于数据存储的基于密钥对的账户:

  1. 从客户端接收存储账户的公钥(客户端生成密钥对并签署交易)
  2. 创建我们想要存储的数据结构(一个带有 u64 计数字段的 CounterData 结构体),并用 Borsh 对其进行序列化
  3. 确定所需空间(设置账户数据字段大小的序列化数据的字节长度)。
  4. 计算该空间所需的 免租金 lamports(为保持账户存活而不被垃圾回收所需的最小 SOL 余额)。
  5. 使用系统程序以计算出的空间和 lamports 创建账户。
  6. 将序列化数据直接写入账户的数据字段。

现在让我们在代码中看看。将 src/lib.rs 文件中的代码替换为以下内容。我们添加了代码注释以显示我们在何处实现了上述每个步骤:

Copyuse borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
    account_info::{next_account_info, AccountInfo},
    entrypoint,
    entrypoint::ProgramResult,
    msg,
    program::invoke,
    program_error::ProgramError,
    pubkey::Pubkey,
    rent::Rent,
    system_instruction, system_program,
};

entrypoint!(process_instruction);

// 这代表我们将存储在账户中的数据
// 我们添加了 Borsh 派生宏用于序列化和反序列化
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct CounterData {
    pub count: u64,
}

pub fn process_instruction(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    _instruction_data: &[u8],
) -> ProgramResult {
    msg!("Storage Write Program: Creating storage account and writing data");

    let accounts_iter = &mut accounts.iter();

    // 步骤 1:获取我们需要的账户
        // next_account_info() 从迭代器中提取下一个 AccountInfo
    let storage_account = next_account_info(accounts_iter)?;
    let signer = next_account_info(accounts_iter)?;
    let system_program = next_account_info(accounts_iter)?;

    // 验证系统程序
    if system_program.key != &system_program::ID {
        msg!("Invalid system program");
        return Err(ProgramError::IncorrectProgramId);
    }

    // 验证签名者账户确实是签名者
    if !signer.is_signer {
        msg!("Signer must be a signer");
        return Err(ProgramError::MissingRequiredSignature);
    }

    // 步骤 2:创建我们的计数器数据
    let counter_data = CounterData { count: 42 };

    // 步骤 2:用 Borsh 序列化数据 (u64 变成 8 字节的小端格式)
    let serialized_data = counter_data.try_to_vec()?; // [42, 0, 0, 0, 0, 0, 0, 0]

    // 步骤 3:确定数据所需空间
    let space = serialized_data.len();

    msg!("Creating storage account with {} bytes", space);
    msg!("Serialized data: {:?}", serialized_data);

    // 步骤 4:计算免租金所需的 lamports
    let lamports = Rent::default().minimum_balance(space);

    // 步骤 5:使用系统程序创建账户
    let create_account_ix = system_instruction::create_account(
        signer.key,
        storage_account.key,
        lamports,
        space as u64,
        program_id,
    );

    // 步骤 5(续):执行 create_account 指令
    invoke(
        &create_account_ix,
        &[\
            signer.clone(),\
            storage_account.clone(),\
            system_program.clone(),\
        ],
    )?;

    msg!("Storage account created successfully");

    // 步骤 6:将序列化数据写入账户
    let mut account_data = storage_account.try_borrow_mut_data()?;
    account_data.copy_from_slice(&serialized_data);

    msg!("Data written to storage account");

    Ok(())
}

现在我们将看看账户创建背后的步骤。

密钥对账户创建在我们的 Rust 程序中如何工作

我们程序中的账户创建分两步进行。首先,我们使用 system_instruction::create_account() 构建指令——这是一个辅助函数,它用正确的账户和数据为账户创建构建 Instruction 结构体(在 CPI 教程中,我们手动构建了这个结构体)。

A Rust code snippet showing two steps to create a keypair account: building the create account instruction and performing a cross-program invocation.

其次,我们使用 invoke() 执行此指令,它对系统程序执行跨程序调用 (CPI)。然后系统程序实际在链上创建具有我们指定参数的账户。

create_account 指令

系统程序的 create_account 指令通过转移 lamports、分配数据空间和分配所有者来创建新的链上账户。它需要五个参数:

  1. 支付者:为新账户租金提供资金的账户
  2. 新账户地址:用于创建账户的地址(正在初始化的账户)
  3. Lamports:要转移到新账户的 SOL 数量(必须覆盖免租金)
  4. 空间:为账户数据分配的字节数
  5. 所有者:将拥有新账户的程序 ID(公钥)

由于系统程序要求新账户地址在 create_account 指令中是签名者,因此密钥对账户按预期工作:交易包含来自密钥对私钥的签名。PDA 没有私钥,因此创建它的程序必须通过 invoke_signed() 提供种子,运行时使用这些种子重新派生和验证 PDA,授予其签名权限(我们将在第 2 部分中看到)。

Solana 如何知道密钥对地址可用于账户创建?

客户端生成一个新的密钥对并在交易中传递其公钥。我们的程序通过 `let stor...

剩余50%的内容订阅专栏后可查看

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

0 条评论

请先 登录 后评论