当前位置: 首页 > news >正文

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

在本节中,您将学习如何构建基本的 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,
}

参考:链接

相关文章:

  • 【数据结构】并查集应用
  • 面试可能会遇到的问题回答(编程语言部分)
  • 清晰易懂的 HeidiSQL 安装教程
  • 第四章:透明多级分流系统_《凤凰架构:构建可靠的大型分布式系统》
  • JavaScript基础--12-基本包装类型
  • C++堆,栈,静态成员及使用准则
  • lib-zo,C语言另一个协程库,dns协程化, gethostbyname
  • 解决 PDF 难题:批量处理、文档清理与自由拆分合并
  • 力扣经典算法篇-9-跳跃游戏(贪心算法,反向递推)
  • Debezium嵌入式连接postgresql封装服务
  • 前端常用环境安装
  • Python3 MySQL (PyMySQL) 教程
  • DHCPv6 笔记250405
  • Word在生成PDF后,PDF左侧导航书签目录错误显示的解决方法
  • AI Agent设计模式六:ReAct
  • VSCode中结合DeepSeek使用Cline插件的感受
  • 2-Docker常用命令
  • 30--当路由器学会“反侦察“:华为URPF协议配置全解
  • 2022 年 6 月青少年软编等考 C 语言七级真题解析
  • 2025年渗透测试面试题总结- 某58同城-安全工程师扩展(题目+回答)
  • 崇文企业网站建设公司/百度权重怎么查询
  • 达内网站开发/360营销
  • 自己做网站卖/谷歌网站网址
  • 怎么做外语网站/网站推广是干嘛的
  • 做小说网站做国外域名还是国内的好/seo 公司
  • 公司做网站费用入什么科目/廊坊seo网络推广