区块链日记6 - Solana入门 - PDA增删改查数据1

发布于:2025-04-06 ⋅ 阅读:(21) ⋅ 点赞:(0)

在本节中,您将学习如何构建基本的 Create, Read, Update, Delete (CRUD) 程序。

本指南演示了一个简单的程序,用户可以在其中创建、更新和删除消息。每条消息都存在于一个帐户中,该帐户具有从程序本身派生的确定性地址(Program Derived Address 或 PDA)。

目录

1.开始代码

2.定义消息帐户类型

3.添加创建指令

4.添加更新指令

5.添加删除指令

代码整合


(PDA:我的理解是,代表一个账户)

本指南将引导您使用 Anchor 框架构建和测试 Solana 程序,同时演示程序派生地址 (PDA)。有关更多详细信息,请参阅 Program Derived Addresses 页面。

作为参考,您可以查看 完成 PDA 和跨程序调用 (CPI) 部分后的最终代码。

1.开始代码

首先打开这个 Solana Playground 链接 。然后单击 “Import” 按钮将程序添加到您的 Solana Playground 项目中。

在 lib.rs 文件中,您将找到一个带有 create, update 和 delete 说明,以在以下步骤中添加。

use anchor_lang::prelude::*;

declare_id!("8KPzbM2Cwn4Yjak7QYAEH9wyoQh86NcBicaLuzPaejdw");

#[program]
pub mod pda {
    use super::*;

    pub fn create(_ctx: Context<Create>) -> Result<()> {
        Ok(())
    }

    pub fn update(_ctx: Context<Update>) -> Result<()> {
        Ok(())
    }

    pub fn delete(_ctx: Context<Delete>) -> Result<()> {
        Ok(())
    }
}

#[derive(Accounts)]
pub struct Create {}

#[derive(Accounts)]
pub struct Update {}

#[derive(Accounts)]
pub struct Delete {}

#[account]
pub struct MessageAccount {}

在开始之前,在 Playground 终端中运行 build 以检查入门程序构建是否成功。

2.定义消息帐户类型

首先,定义程序创建的消息帐户的结构。此结构定义要存储在程序创建的帐户中的数据。

在 lib.rs 中,使用以下命令更新 MessageAccount 结构:

#[account]
pub struct MessageAccount {
    pub user: Pubkey,
    pub message: String,
    pub bump: u8,
}

解释:

Anchor 程序中的 #[account] 属性对表示帐户数据(要存储在 Account 数据字段中的数据类型)的结构进行注释。

在此示例中,MessageAccount 结构存储由用户创建的消息,其中包含三个字段:

user - 标识创建消息账户的用户的公钥 

message - 包含用户消息的字符串 

bump - 存储 “bump” 种子的 u8 用于派生程序派生地址 (PDA)。存储此值可节省 计算,无需在后面的说明中重新计算它。

创建帐户时,程序会序列化 MessageAccount 数据并将其存储在新帐户的 data 字段中。

稍后,当从帐户读取时,程序将此数据反序列化回 MessageAccount 数据类型。测试部分演示了创建和读取账户数据的过程。

通过在终端中运行 build 再次构建程序。

此代码定义要在消息帐户上存储的数据。接下来,您将添加程序说明。

3.添加创建指令

现在,添加创建并初始化 MessageAccount 的 API 中。

首先,通过更新 使用Create struct:

#[derive(Accounts)]
#[instruction(message: String)]
pub struct Create<'info> {
    #[account(mut)]
    pub user: Signer<'info>,

    #[account(
        init,
        seeds = [b"message", user.key().as_ref()],
        bump,
        payer = user,
        space = 8 + 32 + 4 + message.len() + 1
    )]
    pub message_account: Account<'info, MessageAccount>,
    pub system_program: Program<'info, System>,
}

 解释:

Anchor 程序中的 #[derive(Accounts)] 属性对定义指令所需帐户的结构进行注释。

结构中的每个字段都表示一个以两种方式验证的帐户:

a. 指定程序所需的帐户类型的帐户类型(如 Signer<'info> 或 Account<'info, T>

b. 定义额外要求的可选约束(如 #[account(mut)] 或 #[account(init)]

总之,这些使 Anchor 能够自动验证传递给指令的帐户并保护程序。

结构中的字段名称提供对程序代码中账户的访问,但不会影响验证。为清楚起见,您应该使用描述性名称。

我这边有一些疑问解答一下。

问:#[instruction(message: String)]有什么用?

答:#[instruction(message: String)]允许你在调用指令时传递参数。message是一个字符串参数,表示要创建的消息内容。这个参数会在调用指令时被传递给函数。

问:结构体 Create<'info>为什么后面要加上<'info>?

答:在Rust中,Create<'info>中的<'info>表示一个生命周期参数。生命周期是Rust的一种特性,用于确保引用的有效性,防止悬垂引用和数据竞争。

问:可以不加<'info>吗?

答:是否需要添加生命周期参数(如<'info>)取决于结构体的具体定义和使用场景。如果结构体中没有引用类型的字段,那么就不需要添加生命周期参数。

问:#[account(mut)]
    pub user: Signer<'info>,
为什么前面要加    #[account(mut)]?

答:#[account(mut)]是一个属性宏,用于标记一个账户(在这个例子中是user)为可变的。这意味着在合约执行期间,该账户的状态可以被修改。

问:pub system_program: Program<'info, System>,为什么后面加<'info, System>?还有两个?

答:在Rust的Anchor框架中,Program<'info, System>是一个泛型类型,用于表示一个与特定程序(在这里是System程序)交互的程序账户。这个类型的两个生命周期参数'infoSystem分别有不同的含义。

问:Anchor框架是用来干嘛的,可以不用吗?

答:可以不用,但不好。Anchor框架是一个用于构建Solana智能合约的开发框架,它提供了一系列工具和库,以简化Solana程序的开发过程。

接下来,通过更新 create 函数替换为以下内容:

pub fn create(ctx: Context<Create>, message: String) -> Result<()> {
    msg!("Create Message: {}", message);
    let account_data = &mut ctx.accounts.message_account;
    account_data.user = ctx.accounts.user.key();
    account_data.message = message;
    account_data.bump = ctx.bumps.message_account;
    Ok(())
}

重新生成程序。 build

问:为什么ctx里面有accounts、bumps?
答:accounts是一个结构体,包含了在当前上下文中所有需要的账户。它们是通过#[derive(Accounts)]宏定义的,表示在调用该指令时需要的账户。这些账户在执行create函数时会被自动填充,确保它们的状态和权限是正确的。
bumps是在编译时通过#[derive(Accounts)]宏生成的,并在运行时根据账户的种子(seeds)自动计算和填充的。种子是用于生成账户地址的基础,而bump值则是为了确保生成的地址是唯一的。

4.添加更新指令

接下来,添加update 指令以使用新消息更改 MessageAccount

与上一步一样,首先指定update 所需的账户 指令。

使用以下命令更新 Update 结构:

#[derive(Accounts)]
#[instruction(message: String)]
pub struct Update<'info> {
    #[account(mut)]
    pub user: Signer<'info>,

    #[account(
        mut,
        seeds = [b"message", user.key().as_ref()],
        bump = message_account.bump,
        realloc = 8 + 32 + 4 + message.len() + 1,
        realloc::payer = user,
        realloc::zero = true,
    )]
    pub message_account: Account<'info, MessageAccount>,
    pub system_program: Program<'info, System>,
}

接下来,添加 update 指令的逻辑。

pub fn update(ctx: Context<Update>, message: String) -> Result<()> {
    msg!("Update Message: {}", message);
    let account_data = &mut ctx.accounts.message_account;
    account_data.message = message;
    Ok(())
}

5.添加删除指令

接下来,添加 delete 指令以关闭 MessageAccount

使用以下命令更新 Delete 结构:

#[derive(Accounts)]
pub struct Delete<'info> {
    #[account(mut)]
    pub user: Signer<'info>,

    #[account(
        mut,
        seeds = [b"message", user.key().as_ref()],
        bump = message_account.bump,
        close= user,
    )]
    pub message_account: Account<'info, MessageAccount>,
}

接下来,添加 delete 指令的逻辑。

pub fn delete(_ctx: Context<Delete>) -> Result<()> {
    msg!("Delete Message");
    Ok(())
}

代码整合

use anchor_lang::prelude::*;

declare_id!("2T5oHSgS6sGrjqKAzjtnycPG4qxnGs5fCTdu9AeLpiSr");

#[program]
pub mod pda {
    use super::*;

    pub fn create(ctx: Context<Create>, message: String) -> Result<()> {
        msg!("Create Messsage: {}", message);
        let account_data = &mut ctx.accounts.message_account;
        account_data.user = ctx.accounts.user.key();
        account_data.message = message;
        account_data.bump = ctx.bumps.message_account;
        Ok(())
    }

    pub fn update(ctx: Context<Update>, message: String) -> Result<()> {
        msg!("Update Message: {}", message);
        let account_data = &mut ctx.accounts.message_account;
        account_data.message = message;
        Ok(())
    }

    pub fn delete(_ctx: Context<Delete>) -> Result<()> {
        msg!("Delete Message");
        Ok(())
    }
}

#[derive(Accounts)]
#[instruction(message: String)]
pub struct Create<'info> {
    #[account(mut)]
    pub user: Signer<'info>,

    #[account(
        init,
        seeds = [b"message", user.key().as_ref()],
        bump,
        payer = user,
        space = 8+32+4+message.len()+1
    )]
    pub message_account: Account<'info, MessageAccount>,
    pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
#[instruction(message: String)]
pub struct Update<'info> {
    #[account(mut)]
    pub user: Signer<'info>,

    #[account(
        mut,
        seeds = [b"message", user.key().as_ref()],
        bump = message_account.bump,
        realloc = 8+32+4+message.len()+1,
        realloc::payer = user,
        realloc::zero = true,
    )]
    pub message_account: Account<'info, MessageAccount>,
    pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct Delete<'info> {
    #[account(mut)]
    pub user: Signer<'info>,

    #[account(
        mut,
        seeds = [b"message", user.key().as_ref()],
        bump = message_account.bump,
        close = user,
    )]
    pub message_account: Account<'info, MessageAccount>,
}

#[account]
pub struct MessageAccount {
    pub user: Pubkey,
    pub message: String,
    pub bump: u8,
}

参考:链接


网站公告

今日签到

点亮在社区的每一天
去签到