ソラナ:命令データを通してカスタム命令を送る方法
この記事では、我々はチェーンプログラム上のソラナにカスタム命令を送信するプロセスを歩いていきます.ソラナを変更しますexample helloworld 2つの命令を取る.
最後の結果を見てみましょう.
始めましょう.ここから拾うhelloworld チュートリアル私たちを残します.
プログラムのAPIを定義する
デコード
我々は
更新
我々がこれまでにしたことの速い要約: 我々のプログラムAPIを定義しました 未パックの デコードされた命令を記録した ましょう
エラーが発生しました:
更新
クリエイト
Th
正しい操作を行います.
すごい!もう少しで済んだ.を作成する
今、私たちがプログラムを変更しましょう
更新
お客様を経営しましょう.
クライアントの複数回を実行すると、グリーティングカウントがインクリメントされます.
Aを送りましょう
我々のプログラムは
おめでとう!我々は、プログラムがどのような操作を実行すべきかを指定するために、我々のプログラムを変更することができました.
ソラナの開発についてもっと読むために私に従ってください.
SayHello
and SayGoodbye
.最後の結果を見てみましょう.
始めましょう.ここから拾うhelloworld チュートリアル私たちを残します.
プログラムのAPIを定義する
// instruction.rs
#[derive(Debug)]
pub enum HelloInstruction {
SayHello,
SayBye,
}
私たちはenum
それは我々のプログラムが実行できる操作を含みます.instruction.rs
デコードの原因instruction_data
.デコード
instruction_data
// instruction.rs
+use solana_program::{program_error::ProgramError};
+
#[derive(Debug)]
pub enum HelloInstruction {
SayHello,
SayBye,
}
+impl HelloInstruction {
+ pub fn unpack(input: &[u8]) -> Result<Self, ProgramError> {
+ let (&tag, rest) = input
+ .split_first()
+ .ok_or(ProgramError::InvalidInstructionData)?;
+
+ Ok(match tag {
+ 0 => HelloInstruction::SayHello,
+ 1 => HelloInstruction::SayBye,
+ _ => return Err(ProgramError::InvalidInstructionData),
+ })
+ }
+}
関連関数を作成しますunpack
はinstruction_data
を返します.HelloInstruction
またはProgramError
.// --snip--
let (&tag, rest) = input
.split_first()
.ok_or(ProgramError::InvalidInstructionData)?;
// --snip--
私たちはinstruction_data
を含むタプルをスライスして受け取る&tag
これが最初の要素でありrest
残りのバイトはどれですか.&tag
(数値/U 8範囲;0〜255)は1:1と演算される.これはクライアント上の命令を作成するときにより明確になります.rest
は、操作が必要となる追加情報を含む残りのバイト数を持つ.我々は
Option
with .ok_or(ProgramError::InvalidInstructionData)?;
にResult
, if the Option
がNone
variantを返しますInvalidInstructionData
エラーです.もっと見るok_or メソッド.Ok(match tag {
0 => HelloInstruction::SayHello,
1 => HelloInstruction::SayBye,
_ => return Err(ProgramError::InvalidInstructionData),
})
私たちは今ではtag
どのような操作を知っているプログラムを実行する必要があります.既知の命令が一致しない場合、エラーを返します.更新
process_instruction
// lib.rs
// --snip--
+pub mod instruction;
+use crate::instruction::HelloInstruction;
// --snip--
pub fn process_instruction(
program_id: &Pubkey, // Public key of the account the hello world program was loaded into
accounts: &[AccountInfo], // The account to say hello to
instruction_data: &[u8],
) -> ProgramResult {
msg!("Hello World Rust program entrypoint");
+
+ let instruction = HelloInstruction::unpack(instruction_data)?;
+ msg!("Instruction: {:?}", instruction);
// Iterating accounts is safer then indexing
let accounts_iter = &mut accounts.iter();
// Get the account to say hello to
let account = next_account_info(accounts_iter)?;
// The account must be owned by the program in order to modify its data
if account.owner != program_id {
msg!("Greeted account does not have the correct program id");
return Err(ProgramError::IncorrectProgramId);
}
// Increment and store the number of times the account has been greeted
let mut greeting_account = GreetingAccount::try_from_slice(&account.data.borrow())?;
greeting_account.counter += 1;
greeting_account.serialize(&mut &mut account.data.borrow_mut()[..])?;
msg!("Greeted {} time(s)!", greeting_account.counter);
Ok(())
}
我々は、我々HelloInstruction
をuse crate::instruction::HelloInstruction;
と命令をデコードしようとするlet instruction = HelloInstruction::unpack(instruction_data)?;
今のところ、我々はちょうど命令を記録します.後でこの命令を使用するためにコードを変更します.我々がこれまでにしたことの速い要約:
HelloInstruction
enum
instruction_data
クライアントからの操作を取得するバイト配列build
and re-deploy
プログラム.あなたがこれをする方法がわからないならば、見てくださいexample helloworld docs . 我々は現在、クライアントを実行することができますし、ログメッセージが表示されます参照してください.エラーが発生しました:
Program log: Instruction Err(InvalidInstructionData)
我々のクライアントが有効な指示を通過していないので.それを直しましょう.更新
sayHello
// hello_world.ts
// --snip --
/**
* Say hello
*/
export async function sayHello(): Promise<void> {
console.log('Saying hello to', greetedPubkey.toBase58());
const instruction = new TransactionInstruction({
keys: [{pubkey: greetedPubkey, isSigner: false, isWritable: true}],
programId,
- data: Buffer.alloc(0),
+ data: createSayHelloInstructionData(),
});
await sendAndConfirmTransaction(
connection,
new Transaction().add(instruction),
[payer],
);
}
私たちは呼び出しをBuffer.alloc(0)
新しい機能でcreateSayHelloInstructionData()
を返します.Buffer
.クリエイト
createSayHelloInstructionData
このために二つのライブラリをインストールする必要があります.npm i @solana/buffer-layout buffer
// hello_world.ts
import * as BufferLayout from '@solana/buffer-layout';
import { Buffer } from 'buffer';
// --snip--
function createSayHelloInstructionData(): Buffer {
const dataLayout = BufferLayout.struct([
BufferLayout.u8('instruction')
]);
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode({
instruction: 0
}, data);
return data;
}
つのフィールドを持つバッファレイアウト構造を作成しますinstruction
. これは、我々が我々のプログラムで実行する操作をエンコードする場所ですu8
.const data = Buffer.alloc(dataLayout.span);
次に、以前に作成したバッファレイアウトからサイズを使用して新しいバッファーを割り当てます.dataLayout.encode({
instruction: 0
}, data);
最後に、命令をエンコードします.instruction: 0
) を返します.Th
instruction: 0
に対応するtag
の最初の要素で取得する変数instruction_data
スライス.// --snip--
let (&tag, rest) = input
.split_first()
.ok_or(ProgramError::InvalidInstructionData)?;
Ok(match tag {
0 => HelloInstruction::SayHello,
1 => HelloInstruction::SayBye,
// --snip--
今私たちのクライアントを実行すると、我々の指示が正しくデコードされていることがわかります.正しい操作を行います.
Program log: Instruction Ok(SayHello)
.すごい!もう少しで済んだ.を作成する
createSayByeInstructionData
関数.これはcreateSayHelloInstructionData
関数は、我々が送信する必要がある命令を除いて.今、私たちがプログラムを変更しましょう
SayBye
操作.更新
process_instruction
pub fn process_instruction(
program_id: &Pubkey, // Public key of the account the hello world program was loaded into
accounts: &[AccountInfo], // The account to say hello to
instruction_data: &[u8], // Ignored, all helloworld instructions are hellos
) -> ProgramResult {
msg!("Hello World Rust program entrypoint");
let instruction = HelloInstruction::unpack(instruction_data)?;
msg!("Instruction {:?}", instruction);
// Iterating accounts is safer then indexing
let accounts_iter = &mut accounts.iter();
// Get the account to say hello to
let account = next_account_info(accounts_iter)?;
// The account must be owned by the program in order to modify its data
if account.owner != program_id {
msg!("Greeted account does not have the correct program id");
return Err(ProgramError::IncorrectProgramId);
}
// Increment and store the number of times the account has been greeted
let mut greeting_account = GreetingAccount::try_from_slice(&account.data.borrow())?;
+
+ match instruction {
+ HelloInstruction::SayHello => {
greeting_account.counter += 1;
+ },
+ HelloInstruction::SayBye => {
+ greeting_account.counter -= 1;
+ },
+ _ => {}
+ }
+
greeting_account.serialize(&mut &mut account.data.borrow_mut()[..])?;
msg!("Greeted {} time(s)!", greeting_account.counter);
Ok(())
}
今、我々は我々のプログラムを変えました.我々は再び構築し、それを展開する必要があります.お客様を経営しましょう.
クライアントの複数回を実行すると、グリーティングカウントがインクリメントされます.
Aを送りましょう
SayBye
操作.// --snip--
export async function sayHello(): Promise<void> {
console.log('Saying hello to', greetedPubkey.toBase58());
const instruction = new TransactionInstruction({
keys: [{pubkey: greetedPubkey, isSigner: false, isWritable: true}],
programId,
- data: createSayHelloInstructionData(),
+ data: createSayByeInstructionData(),
});
await sendAndConfirmTransaction(
connection,
new Transaction().add(instruction),
[payer],
);
}
function createSayByeInstructionData(): Buffer {
const dataLayout = BufferLayout.struct([BufferLayout.u8('instruction')]);
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode(
{
instruction: 1,
},
data,
);
return data;
}
我々は再びクライアントを実行します.我々のプログラムは
SayBye
操作と我々の挨拶数を減らす.おめでとう!我々は、プログラムがどのような操作を実行すべきかを指定するために、我々のプログラムを変更することができました.
ソラナの開発についてもっと読むために私に従ってください.
Reference
この問題について(ソラナ:命令データを通してカスタム命令を送る方法), 我々は、より多くの情報をここで見つけました https://dev.to/cogoo/solana-how-to-send-custom-instructions-via-instruction-data-4g9gテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol