Transaksi dan Instruksi
Di Solana, pengguna mengirim transaksi untuk berinteraksi dengan jaringan. Transaksi berisi satu atau lebih instruksi yang menentukan operasi yang akan diproses. Logika eksekusi untuk instruksi disimpan pada program yang di-deploy ke jaringan Solana, di mana setiap program mendefinisikan serangkaian instruksinya sendiri.
Berikut adalah detail penting tentang pemrosesan transaksi Solana:
- Jika transaksi mencakup beberapa instruksi, instruksi tersebut dieksekusi dalam urutan yang ditambahkan ke transaksi.
- Transaksi bersifat "atomik" - semua instruksi harus diproses dengan sukses, atau seluruh transaksi gagal dan tidak ada perubahan yang terjadi.
Transaksi pada dasarnya adalah permintaan untuk memproses satu atau lebih instruksi.
Transaksi Disederhanakan
Transaksi seperti amplop yang berisi formulir. Setiap formulir adalah instruksi yang memberi tahu jaringan apa yang harus dilakukan. Mengirim transaksi seperti mengirimkan amplop untuk memproses formulir-formulir tersebut.
Poin Penting
- Transaksi Solana mencakup instruksi yang memanggil program di jaringan.
- Transaksi bersifat atomik - jika ada instruksi yang gagal, seluruh transaksi gagal dan tidak ada perubahan yang terjadi.
- Instruksi pada transaksi dieksekusi secara berurutan.
- Batas ukuran transaksi adalah 1232 byte.
- Setiap instruksi memerlukan tiga bagian informasi:
- Alamat program yang akan dipanggil
- Akun yang dibaca atau ditulis oleh instruksi
- Data tambahan yang diperlukan oleh instruksi (misalnya, argumen fungsi)
Contoh Transfer SOL
Diagram di bawah ini menggambarkan transaksi dengan satu instruksi untuk mentransfer SOL dari pengirim ke penerima.
Di Solana, "dompet" adalah akun yang dimiliki oleh System Program. Hanya pemilik program yang dapat mengubah data akun, sehingga mentransfer SOL memerlukan pengiriman transaksi untuk memanggil System Program.
Transfer SOL
Akun pengirim harus menandatangani (is_signer
) transaksi untuk memungkinkan
System Program mengurangi saldo lamport-nya. Akun pengirim dan penerima harus
dapat ditulis (is_writable
) karena saldo lamport mereka berubah.
Setelah mengirim transaksi, System Program memproses instruksi transfer. System Program kemudian memperbarui saldo lamport dari kedua akun pengirim dan penerima.
Proses Transfer SOL
Contoh di bawah ini menunjukkan cara mengirim transaksi yang mentransfer SOL dari satu akun ke akun lainnya.
import {airdropFactory,appendTransactionMessageInstructions,createSolanaRpc,createSolanaRpcSubscriptions,createTransactionMessage,generateKeyPairSigner,getSignatureFromTransaction,lamports,pipe,sendAndConfirmTransactionFactory,setTransactionMessageFeePayerSigner,setTransactionMessageLifetimeUsingBlockhash,signTransactionMessageWithSigners} from "@solana/kit";import { getTransferSolInstruction } from "@solana-program/system";// Create a connection to clusterconst rpc = createSolanaRpc("http://localhost:8899");const rpcSubscriptions = createSolanaRpcSubscriptions("ws://localhost:8900");// Generate sender and recipient keypairsconst sender = await generateKeyPairSigner();const recipient = await generateKeyPairSigner();const LAMPORTS_PER_SOL = 1_000_000_000n;const transferAmount = lamports(LAMPORTS_PER_SOL / 100n); // 0.01 SOL// Fund sender with airdropawait airdropFactory({ rpc, rpcSubscriptions })({recipientAddress: sender.address,lamports: lamports(LAMPORTS_PER_SOL), // 1 SOLcommitment: "confirmed"});// Check balance before transferconst { value: preBalance1 } = await rpc.getBalance(sender.address).send();const { value: preBalance2 } = await rpc.getBalance(recipient.address).send();// Create a transfer instruction for transferring SOL from sender to recipientconst transferInstruction = getTransferSolInstruction({source: sender,destination: recipient.address,amount: transferAmount // 0.01 SOL in lamports});// Add the transfer instruction to a new transactionconst { value: latestBlockhash } = await rpc.getLatestBlockhash().send();const transactionMessage = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(sender, tx),(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),(tx) => appendTransactionMessageInstructions([transferInstruction], tx));// Send the transaction to the networkconst signedTransaction =await signTransactionMessageWithSigners(transactionMessage);await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(signedTransaction,{ commitment: "confirmed" });const transactionSignature = getSignatureFromTransaction(signedTransaction);// Check balance after transferconst { value: postBalance1 } = await rpc.getBalance(sender.address).send();const { value: postBalance2 } = await rpc.getBalance(recipient.address).send();console.log("Sender prebalance:",Number(preBalance1) / Number(LAMPORTS_PER_SOL));console.log("Recipient prebalance:",Number(preBalance2) / Number(LAMPORTS_PER_SOL));console.log("Sender postbalance:",Number(postBalance1) / Number(LAMPORTS_PER_SOL));console.log("Recipient postbalance:",Number(postBalance2) / Number(LAMPORTS_PER_SOL));console.log("Transaction Signature:", transactionSignature);
Library klien sering kali mengabstraksi detail untuk membangun instruksi program. Jika library tidak tersedia, Anda dapat membangun instruksi secara manual. Ini mengharuskan Anda mengetahui detail implementasi dari instruksi tersebut.
Contoh di bawah ini menunjukkan cara membangun instruksi transfer secara manual.
Tab Expanded Instruction
secara fungsional setara dengan tab Instruction
.
- Kit
const transferAmount = 0.01; // 0.01 SOLconst transferInstruction = getTransferSolInstruction({source: sender,destination: recipient.address,amount: transferAmount * LAMPORTS_PER_SOL});
- Legacy
const transferAmount = 0.01; // 0.01 SOLconst transferInstruction = SystemProgram.transfer({fromPubkey: sender.publicKey,toPubkey: receiver.publicKey,lamports: transferAmount * LAMPORTS_PER_SOL});
- Rust
let transfer_amount = LAMPORTS_PER_SOL / 100; // 0.01 SOLlet transfer_instruction =system_instruction::transfer(&sender.pubkey(), &recipient.pubkey(), transfer_amount);
Pada bagian di bawah ini, kita akan membahas detail tentang transaksi dan instruksi.
Instruksi
Sebuah instruction pada program Solana dapat dianggap sebagai fungsi publik yang dapat dipanggil oleh siapa saja yang menggunakan jaringan Solana.
Memanggil instruksi program memerlukan tiga informasi penting:
- Program ID: Program dengan logika eksekusi untuk instruksi tersebut
- Akun: Daftar akun yang dibutuhkan oleh instruksi
- Instruction Data: Array byte yang menentukan instruksi yang akan dipanggil pada program dan argumen yang diperlukan oleh instruksi
pub struct Instruction {/// Pubkey of the program that executes this instruction.pub program_id: Pubkey,/// Metadata describing accounts that should be passed to the program.pub accounts: Vec<AccountMeta>,/// Opaque data passed to the program for its own interpretation.pub data: Vec<u8>,}
Instruksi Transaksi
AccountMeta
Setiap akun yang diperlukan oleh instruksi harus disediakan sebagai AccountMeta yang berisi:
pubkey
: Alamat akunis_signer
: Apakah akun harus menandatangani transaksiis_writable
: Apakah instruksi memodifikasi data akun
pub struct AccountMeta {/// An account's public key.pub pubkey: Pubkey,/// True if an `Instruction` requires a `Transaction` signature matching `pubkey`.pub is_signer: bool,/// True if the account data or metadata may be mutated during program execution.pub is_writable: bool,}
AccountMeta
Dengan menentukan di awal akun mana yang dibaca atau ditulis oleh instruksi, transaksi yang tidak memodifikasi akun yang sama dapat dieksekusi secara paralel.
Contoh Struktur Instruksi
Jalankan contoh di bawah ini untuk melihat struktur instruksi transfer SOL.
import { generateKeyPairSigner, lamports } from "@solana/kit";import { getTransferSolInstruction } from "@solana-program/system";// Generate sender and recipient keypairsconst sender = await generateKeyPairSigner();const recipient = await generateKeyPairSigner();// Define the amount to transferconst LAMPORTS_PER_SOL = 1_000_000_000n;const transferAmount = lamports(LAMPORTS_PER_SOL / 100n); // 0.01 SOL// Create a transfer instruction for transferring SOL from sender to recipientconst transferInstruction = getTransferSolInstruction({source: sender,destination: recipient.address,amount: transferAmount});console.log(JSON.stringify(transferInstruction, null, 2));
Contoh berikut menunjukkan output dari cuplikan kode sebelumnya. Format persisnya berbeda tergantung pada SDK, tetapi setiap instruksi Solana memerlukan informasi berikut:
- Program ID: Alamat program yang akan mengeksekusi instruksi.
- Accounts: Daftar akun yang diperlukan oleh instruksi. Untuk setiap akun, instruksi harus menentukan alamatnya, apakah harus menandatangani transaksi, dan apakah akan ditulisi.
- Data: Buffer byte yang memberi tahu program instruksi mana yang akan dieksekusi dan mencakup argumen yang diperlukan oleh instruksi.
{"accounts": [{"address": "Hu28vRMGWpQXN56eaE7jRiDDRRz3vCXEs7EKHRfL6bC","role": 3,"signer": {"address": "Hu28vRMGWpQXN56eaE7jRiDDRRz3vCXEs7EKHRfL6bC","keyPair": {"privateKey": {},"publicKey": {}}}},{"address": "2mBY6CTgeyJNJDzo6d2Umipw2aGUquUA7hLdFttNEj7p","role": 1}],"programAddress": "11111111111111111111111111111111","data": {"0": 2,"1": 0,"2": 0,"3": 0,"4": 128,"5": 150,"6": 152,"7": 0,"8": 0,"9": 0,"10": 0,"11": 0}}
Transaksi
Sebuah transaksi Solana terdiri dari:
- Signatures: Array tanda tangan yang disertakan pada transaksi.
- Message: Daftar instruksi yang akan diproses secara atomik.
pub struct Transaction {#[wasm_bindgen(skip)]#[serde(with = "short_vec")]pub signatures: Vec<Signature>,#[wasm_bindgen(skip)]pub message: Message,}
Format Transaksi
Struktur pesan transaksi terdiri dari:
- Message Header: Menentukan jumlah penandatangan dan akun hanya-baca.
- Account Addresses: Array alamat akun yang diperlukan oleh instruksi pada transaksi.
- Recent Blockhash: Bertindak sebagai stempel waktu untuk transaksi.
- Instructions: Array instruksi yang akan dieksekusi.
pub struct Message {/// The message header, identifying signed and read-only `account_keys`.pub header: MessageHeader,/// All the account keys used by this transaction.#[serde(with = "short_vec")]pub account_keys: Vec<Pubkey>,/// The id of a recent ledger entry.pub recent_blockhash: Hash,/// Programs that will be executed in sequence and committed in/// one atomic transaction if all succeed.#[serde(with = "short_vec")]pub instructions: Vec<CompiledInstruction>,}
Pesan Transaksi
Ukuran Transaksi
Transaksi Solana memiliki batas ukuran 1232 byte. Batas ini berasal dari ukuran Maximum Transmission Unit (MTU) IPv6 sebesar 1280 byte, dikurangi 48 byte untuk header jaringan (40 byte IPv6 + 8 byte header fragmen).
Total ukuran transaksi (tanda tangan dan pesan) harus tetap di bawah batas ini dan mencakup:
- Signatures: 64 byte masing-masing
- Message: Header (3 byte), kunci akun (32 byte masing-masing), recent blockhash (32 byte), dan instruksi
Format Transaksi
Message Header
Message header menggunakan tiga byte untuk mendefinisikan hak istimewa akun.
- Tanda tangan yang diperlukan
- Jumlah akun hanya-baca yang ditandatangani
- Jumlah akun hanya-baca yang tidak ditandatangani
pub struct MessageHeader {/// The number of signatures required for this message to be considered/// valid. The signers of those signatures must match the first/// `num_required_signatures` of [`Message::account_keys`].pub num_required_signatures: u8,/// The last `num_readonly_signed_accounts` of the signed keys are read-only/// accounts.pub num_readonly_signed_accounts: u8,/// The last `num_readonly_unsigned_accounts` of the unsigned keys are/// read-only accounts.pub num_readonly_unsigned_accounts: u8,}
Header Pesan
Format Array Kompak
Array kompak dalam pesan transaksi adalah array yang diserialisasi dalam format berikut:
- Panjang array (dikodekan sebagai compact-u16)
- Item-item array yang tercantum satu per satu
Format array kompak
Format ini digunakan untuk mengkodekan panjang array Alamat Akun dan Instruksi dalam pesan transaksi.
Array Alamat Akun
Pesan transaksi berisi array alamat akun yang diperlukan oleh instruksinya. Array dimulai dengan angka compact-u16 yang menunjukkan berapa banyak alamat yang dikandungnya. Alamat-alamat tersebut kemudian diurutkan berdasarkan hak istimewanya, sebagaimana ditentukan oleh header pesan.
- Akun yang dapat ditulis dan penandatangan
- Akun yang hanya-baca dan penandatangan
- Akun yang dapat ditulis dan bukan penandatangan
- Akun yang hanya-baca dan bukan penandatangan
Array kompak alamat akun
Blockhash Terbaru
Setiap transaksi memerlukan blockhash terbaru yang memiliki dua tujuan:
- Bertindak sebagai stempel waktu
- Mencegah transaksi duplikat
Blockhash kedaluwarsa setelah 150 blok (sekitar 1 menit dengan asumsi waktu blok 400ms), setelah itu transaksi tidak dapat diproses.
Anda dapat menggunakan metode RPC
getLatestBlockhash
untuk mendapatkan
blockhash terkini dan tinggi blok terakhir di mana blockhash akan valid. Berikut
contohnya di
Solana Playground.
Array Instruksi
Pesan transaksi berisi array instruksi dalam tipe CompiledInstruction. Instruksi dikonversi ke tipe ini ketika ditambahkan ke transaksi.
Seperti array alamat akun dalam pesan, ini dimulai dengan panjang compact-u16 diikuti oleh data instruksi. Setiap instruksi berisi:
- Indeks ID Program: Indeks u8 yang menunjuk ke alamat program dalam array alamat akun. Ini menentukan program yang akan memproses instruksi.
- Indeks Akun: Array indeks u8 yang menunjuk ke alamat akun yang diperlukan untuk instruksi ini.
- Instruction Data: Array byte yang menentukan instruksi mana yang akan dipanggil pada program dan data tambahan yang diperlukan oleh instruksi (misalnya argumen fungsi).
pub struct CompiledInstruction {/// Index into the transaction keys array indicating the program account that executes this instruction.pub program_id_index: u8,/// Ordered indices into the transaction keys array indicating which accounts to pass to the program.#[serde(with = "short_vec")]pub accounts: Vec<u8>,/// The program input data.#[serde(with = "short_vec")]pub data: Vec<u8>,}
Array kompak dari Instruksi
Contoh Struktur Transaksi
Jalankan contoh di bawah ini untuk melihat struktur transaksi dengan satu instruksi transfer SOL.
import {createSolanaRpc,generateKeyPairSigner,lamports,createTransactionMessage,setTransactionMessageFeePayerSigner,setTransactionMessageLifetimeUsingBlockhash,appendTransactionMessageInstructions,pipe,signTransactionMessageWithSigners,getCompiledTransactionMessageDecoder} from "@solana/kit";import { getTransferSolInstruction } from "@solana-program/system";const rpc = createSolanaRpc("http://localhost:8899");const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();// Generate sender and recipient keypairsconst sender = await generateKeyPairSigner();const recipient = await generateKeyPairSigner();// Define the amount to transferconst LAMPORTS_PER_SOL = 1_000_000_000n;const transferAmount = lamports(LAMPORTS_PER_SOL / 100n); // 0.01 SOL// Create a transfer instruction for transferring SOL from sender to recipientconst transferInstruction = getTransferSolInstruction({source: sender,destination: recipient.address,amount: transferAmount});// Create transaction messageconst transactionMessage = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(sender, tx),(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),(tx) => appendTransactionMessageInstructions([transferInstruction], tx));const signedTransaction =await signTransactionMessageWithSigners(transactionMessage);// Decode the messageBytesconst compiledTransactionMessage =getCompiledTransactionMessageDecoder().decode(signedTransaction.messageBytes);console.log(JSON.stringify(compiledTransactionMessage, null, 2));
Contoh berikut menunjukkan output pesan transaksi dari cuplikan kode sebelumnya. Format persisnya berbeda tergantung pada SDK, tetapi mencakup informasi yang sama.
{"version": 0,"header": {"numSignerAccounts": 1,"numReadonlySignerAccounts": 0,"numReadonlyNonSignerAccounts": 1},"staticAccounts": ["HoCy8p5xxDDYTYWEbQZasEjVNM5rxvidx8AfyqA4ywBa","5T388jBjovy7d8mQ3emHxMDTbUF8b7nWvAnSiP3EAdFL","11111111111111111111111111111111"],"lifetimeToken": "EGCWPUEXhqHJWYBfDirq3mHZb4qDpATmYqBZMBy9TBC1","instructions": [{"programAddressIndex": 2,"accountIndices": [0, 1],"data": {"0": 2,"1": 0,"2": 0,"3": 0,"4": 128,"5": 150,"6": 152,"7": 0,"8": 0,"9": 0,"10": 0,"11": 0}}]}
Ketika Anda mengambil transaksi menggunakan tanda tangannya setelah mengirimkannya ke jaringan, Anda akan menerima respons dengan struktur berikut.
Bidang message
berisi bidang-bidang berikut:
-
header
: Menentukan hak istimewa baca/tulis dan penandatangan untuk alamat dalam arrayaccountKeys
-
accountKeys
: Array dari semua alamat akun yang digunakan dalam instruksi transaksi -
recentBlockhash
: Blockhash yang digunakan untuk memberi stempel waktu pada transaksi -
instructions
: Array instruksi yang akan dieksekusi. Setiapaccount
danprogramIdIndex
dalam instruksi mereferensikan arrayaccountKeys
berdasarkan indeks. -
signatures
: Array yang mencakup tanda tangan untuk semua akun yang diperlukan sebagai penandatangan oleh instruksi pada transaksi. Tanda tangan dibuat dengan menandatangani pesan transaksi menggunakan kunci privat yang sesuai untuk akun.
{"blockTime": 1745196488,"meta": {"computeUnitsConsumed": 150,"err": null,"fee": 5000,"innerInstructions": [],"loadedAddresses": {"readonly": [],"writable": []},"logMessages": ["Program 11111111111111111111111111111111 invoke [1]","Program 11111111111111111111111111111111 success"],"postBalances": [989995000, 10000000, 1],"postTokenBalances": [],"preBalances": [1000000000, 0, 1],"preTokenBalances": [],"rewards": [],"status": {"Ok": null}},"slot": 13049,"transaction": {"message": {"header": {"numReadonlySignedAccounts": 0,"numReadonlyUnsignedAccounts": 1,"numRequiredSignatures": 1},"accountKeys": ["8PLdpLxkuv9Nt8w3XcGXvNa663LXDjSrSNon4EK7QSjQ","7GLg7bqgLBv1HVWXKgWAm6YoPf1LoWnyWGABbgk487Ma","11111111111111111111111111111111"],"recentBlockhash": "7ZCxc2SDhzV2bYgEQqdxTpweYJkpwshVSDtXuY7uPtjf","instructions": [{"accounts": [0, 1],"data": "3Bxs4NN8M2Yn4TLb","programIdIndex": 2,"stackHeight": null}],"indexToProgramIds": {}},"signatures": ["3jUKrQp1UGq5ih6FTDUUt2kkqUfoG2o4kY5T1DoVHK2tXXDLdxJSXzuJGY4JPoRivgbi45U2bc7LZfMa6C4R3szX"]},"version": "legacy"}
Is this page helpful?