solana实战案例处理电影数据

本节内容,不再简单描述怎么在链上写HelloWorld,而是把难度增加一个,怎么去存储一个结构体数据。Rust基础知识在实现本节实战内容之前,我们需要对Rust基础知识进行学习变量let关键字表示变量,默认情况下是不可变的,添加mut关键字,意味着其中存储的值是可变的。结构体结

本节内容,不再简单描述怎么在链上写Hello World,而是把难度增加一个,怎么去存储一个结构体数据。

Rust基础知识

在实现本节实战内容之前,我们需要对Rust基础知识进行学习

  • 变量

let关键字表示变量,默认情况下是不可变的,添加mut关键字,意味着其中存储的值是可变的。

  • 结构体

结构体(Struct)是一种自定义数据类型,允许将多个相关不同类型的字段打包在一起,形成一个有意义的组

struct User{
    active: bool,
    email: String,
    age: u64
} 
  • 枚举类型

枚举类型是一种数据结构,允许通过列举变体来定义一个类型。

enum LightStatus {
    On, 
    Off
}
  • 匹配语句

match允许将一个值与一系列模式进行比较,然后根据匹配的模式执行代码。

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter
}

fn value_in_cents(coin: Coin) -> u8 {
    match coin {
        Coin::Penny => 1,
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter => 25
    }
}
  • 实现

在Rust中,impl关键字定义类型的实现。函数和常量都可以在实现中定义。

struct Example {
    number: i32
}

impl Example {
    fn boo() {
        println!("boo! Example::boo() was called!");
    }

    fn answer(&mut self) {
        self.number  += 42;
    }

    fn get_number(&self) -> i32 {
        self.number
    }
}
  • 特征和属性

特征描述了类型可实现的抽闲接口,如果特征定义了一个函数bark(), 而一个类型采用该特征,那么该类型必须实现bark()函数

反序列化指令数据

指令数据一般以字节数组的形式传递给程序,在之前的单元中,我们使用Borsh进行客户端的序列化和反序列化。要在程序端使用Borsh,我们使用borsh crate。这个Crate提供了BorshDeserialize和BorshSerialize的traits。

为了使反序列化指令数据简单化,可以创建一个表示数据的结构体,并使用derive将属性BorshDeserialize trait应用到结构体上。这将实现BorshDeserialize中定义的方法,包括我们将使用来反序列化指令数据的try_from_slice方法

#[derive(BorshDeserialize)]
struct NoteInstructionPayload {
id: u64,
title: String,
body: String
}

一旦创建这个结构体,可以为指令枚举创建一个实现,用于处理与反序列化指令数据相关的逻辑。通常会在一个名为unpack的函数内完成这个操作,该函数接收指令数据作为参数,并返回带有反序列化数据的适当枚举实例。

impl NodeInstruction {
    pub fn unpack(input: &[u8]) -> Result<Self, ProgramError> {
        let (&variant, rest) = input.split_first().ok_or(ProgramError::InvalidstructionData)?;
        let payload = NoteInstructionPayload::try_from_slice(rest).unwrap();

        Ok(match variant{
            0 => Self::CreateNode {
                title: payload.title,
                body: payload.body,
                id: payload.id
            },
            1 => Self::UpdateNode {
                title: payload.title,
                body: payload.body,
                id: payload.id
            },
            2=> Self::DeleteNode {
                title: payload.id
            },
            _ => return Err(ProgramError::InvalidInstructionData)
        })
    }
}

上面的示例主要包含三部分:

1.这个函数首先在input参数上使用split_first函数,返回一个元组。第一个元素variant是字节数组的第一个字节,第二个元素rest是字节数组的剩余部分。

2.然后,函数使用NoteInstructionPayload上的try_from_slice方法,将字节数组的剩余部分反序列化为一个名为payloadNoteInstructionPayload实例

3.函数使用match表达式在variant上创建并返回适当的枚举实例,使用payload中信息

程序逻辑

程序的核心逻辑,其实就是将数据反序列化为自定义Rust类型的方法,

例如,下面的代码

entrypoint!(process_instruction);

pub fn process_instruction(
    program_id: &Pubkey, 
    accounts: &[AccountInfo],
    instruction_data: &[u8]
) -> ProgramResult {
    let instruction = NoteInstruction::unpack(instruction_data)?;

    match instruction {
        NoteInstruction::CreateNode {title, body, id} => {
        },
        NoteInstruction::UpdateNode{title, body, id} =>{
        },
        NoteInstruction::DeleteNode{id} => {
        }
    }
}

程序代码

我们将最后的合约代码分为两部分,分别为lib.rs 和 instruction.rs

lib.rs

use borsh::{BorshDeserialize, BorshSerialize};

use solana_program::{
    account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, msg, pubkey::Pubkey,
};

pub mod instruction;
use instruction::MovieInstruction;

entrypoint!(process_instruction);

pub fn add_movie_review(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    title: String,
    rating: u8,
    description: String,
) -> ProgramResult {
    msg!("Adding movie review");
    msg!("Title: {} ", title);
    msg!("Rating: {}", rating);
    msg!("Description: {}", description);
    Ok(())
}

pub fn process_instruction(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    instruction_data: &[u8],
) -> ProgramResult {
    let instruction = MovieInstruction::unpack(instruction_data)?;

    match instruction {
        MovieInstruction::AddMovieReview {
            title,
            rating,
            description,
        } => add_movie_review(program_id, accounts, title, rating, description),
    }
}

instruciton.rs

use borsh::BorshDeserialize;
use solana_program::program_error::ProgramError;

pub enum MovieInstruction {
    AddMovieReview {
        title: String,
        rating: u8,
        description: String,
    },
}

#[derive(BorshDeserialize)]
struct MovieReviewPayload {
    title: String,
    rating: u8,
    description: String,
}

impl MovieInstruction {
    pub fn unpack(input: &[u8]) -> Result<Self, ProgramError> {
        let (&variant, rest) = input
            .split_first()
            .ok_or(ProgramError::InvalidInstructionData)?;
        let payload: MovieReviewPayload = MovieReviewPayload::try_from_slice(rest).unwrap();
        Ok(match variant {
            0 => Self::AddMovieReview {
                title: payload.title,
                rating: payload.rating,
                description: payload.description,
            },
            _ => return Err(ProgramError::InvalidInstructionData),
        })
    }
}

部署完成后,查看链上浏览器地址:

image.png

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

0 条评论

请先 登录 后评论
用户_18921
用户_18921
0xa10f...9ab5
江湖只有他的大名,没有他的介绍。