本文へスキップ
BecomeCoder

Solanaコース · 第5章 ネイティブプログラムを書く · レッスン18

命令データを解く ― borshでデシリアライズ

ローカル実施

導入

instruction_data: &[u8] は、ただのバイト列です。この中に「カウンタを増やして」なのか「カウンタをリセットして」なのか、どんな指示が入っているのかをプログラム側で解釈する必要があります。その変換(デシリアライズ)によく使われるのが borsh というライブラリです。

説明

borsh(Binary Object Representation Serializer for Hashing) は、Rust の構造体や enum を、決まった手順でバイト列に変換(シリアライズ)・バイト列から復元(デシリアライズ)するためのクレートです。#[derive(...)] を付けるだけで、変換コードを自動生成してくれます。

use borsh::{BorshDeserialize, BorshSerialize};

// このプログラムが受け付ける命令の種類をenumで表現する
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub enum CounterInstruction {
    Initialize,           // カウンタを0で初期化する
    Increment { amount: u32 }, // カウンタをamountだけ増やす
}

instruction_data から、この enum を復元します。

use solana_program::{
    account_info::AccountInfo,
    entrypoint::ProgramResult,
    program_error::ProgramError,
    pubkey::Pubkey,
    msg,
};
use borsh::BorshDeserialize;

fn process_instruction(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    instruction_data: &[u8],
) -> ProgramResult {
    // バイト列 → CounterInstruction に復元する
    let instruction = CounterInstruction::try_from_slice(instruction_data)
        .map_err(|_| ProgramError::InvalidInstructionData)?;

    match instruction {
        CounterInstruction::Initialize => {
            msg!("カウンタを初期化します");
            // ここで初期化処理...
        }
        CounterInstruction::Increment { amount } => {
            msg!("カウンタを{}増やします", amount);
            // ここで加算処理...
        }
    }

    Ok(())
}
flowchart LR
    B["instruction_data: &[u8]<br/>(生のバイト列)"] -- "try_from_slice" --> E["CounterInstruction<br/>(enum)"]
    E --> M{どの種類?}
    M -- Initialize --> H1[初期化処理へ]
    M -- "Increment { amount }" --> H2[加算処理へ]

このように、1つの入り口 → デシリアライズ → 種類ごとに分岐という形は、ネイティブな Solana プログラムの定番パターンです。命令の種類が増えるほど match の分岐も増えていきます。クライアント側は、この enum と同じ並び・同じ構造でバイト列を作って送る必要があります(先頭のバイトが enum の何番目の値かを表す、といった約束事があります)。

演習

もし CounterInstruction に3つ目の命令 Decrement { amount: u32 }(カウンタを減らす)を追加するとしたら、enum の定義と match の中に、それぞれどんな行を足せばよいか書いてみましょう。

ヒント1を見る

enum には Increment と同じ形({ amount: u32 })のバリアントを追加します。

ヒント2を見る

match には CounterInstruction::Decrement { amount } => { ... } という腕(アーム)を追加します。