Ringkasan
Transaksi memiliki tanda tangan + pesan. Pesan berisi header, alamat akun, blockhash terbaru, dan instruksi terkompilasi. Ukuran serialisasi maksimal: 1.232 byte.
Sebuah
Transaction
memiliki dua field tingkat atas:
signatures: Array tanda tanganmessage: Informasi transaksi, termasuk daftar instruksi yang akan diproses
pub struct Transaction {pub signatures: Vec<Signature>,pub message: Message,}
Diagram yang menunjukkan dua bagian dari transaksi
Total ukuran serialisasi transaksi tidak boleh melebihi
PACKET_DATA_SIZE
(1.232 byte). Batas ini sama dengan 1.280 byte (MTU minimum IPv6) dikurangi 48
byte untuk header jaringan (40 byte IPv6 + 8 byte fragment header). 1.232 byte
tersebut mencakup array signatures dan struct
message.
Diagram yang menunjukkan format transaksi dan batas ukuran
Tanda tangan
Field signatures adalah array compact-encoded dari nilai
Signature.
Setiap Signature adalah tanda tangan Ed25519 64-byte dari Message yang
diserialisasi, ditandatangani dengan kunci privat akun penandatangan. Satu tanda
tangan diperlukan untuk setiap akun penandatangan yang
direferensikan oleh instruksi transaksi.
Tanda tangan pertama dalam array adalah milik pembayar biaya, akun yang membayar biaya dasar dan biaya prioritas transaksi. Tanda tangan pertama ini juga berfungsi sebagai ID transaksi, digunakan untuk mencari transaksi di jaringan. ID transaksi umumnya disebut sebagai tanda tangan transaksi.
Persyaratan pembayar biaya:
- Harus menjadi akun pertama dalam pesan (indeks 0) dan penandatangan.
- Harus berupa akun milik System Program atau akun nonce (divalidasi oleh
validate_fee_payer). - Harus memiliki lamport yang cukup untuk menutupi
rent_exempt_minimum + total_fee; jika tidak, transaksi akan gagal denganInsufficientFundsForFee.
Pesan
Field message adalah struct
Message
yang berisi payload transaksi:
header: Header pesanaccount_keys: Array alamat akun yang diperlukan oleh instruksi transaksirecent_blockhash: Blockhash yang berfungsi sebagai timestamp untuk transaksiinstructions: Array instruksi
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>,}
Header
Field header adalah struct
MessageHeader
dengan tiga field u8 yang mempartisi array account_keys ke dalam kelompok
izin:
num_required_signatures: Jumlah total tanda tangan yang diperlukan oleh transaksi.num_readonly_signed_accounts: Jumlah akun yang ditandatangani yang bersifat read-only.num_readonly_unsigned_accounts: Jumlah akun yang tidak ditandatangani yang bersifat read-only.
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,}
Diagram yang menunjukkan tiga bagian dari header pesan
Alamat akun
Field
account_keys
adalah array public key yang dikodekan secara kompak. Setiap entri
mengidentifikasi akun yang digunakan oleh setidaknya satu instruksi transaksi.
Array harus mencakup setiap akun dan harus mengikuti urutan ketat ini:
- Penandatangan + Writable
- Penandatangan + Read-only
- Non-penandatangan + Writable
- Non-penandatangan + Read-only
Urutan ketat ini memungkinkan array account_keys digabungkan dengan tiga
hitungan dalam header pesan untuk menentukan izin untuk setiap
akun tanpa menyimpan flag metadata per-akun. Hitungan header mempartisi array
ke dalam empat kelompok izin yang tercantum di atas.
Diagram yang menunjukkan urutan array alamat akun
Blockhash terbaru
Field recent_blockhash adalah hash 32-byte yang memiliki dua tujuan:
- Timestamp: membuktikan bahwa transaksi dibuat baru-baru ini.
- Deduplikasi: mencegah transaksi yang sama diproses dua kali.
Blockhash kedaluwarsa setelah 150 slot. Jika blockhash tidak lagi valid saat
transaksi tiba, maka akan ditolak dengan BlockhashNotFound, kecuali jika
merupakan transaksi nonce tahan lama
yang valid.
Metode RPC getLatestBlockhash
memungkinkan Anda mendapatkan blockhash saat ini dan tinggi blok terakhir di
mana blockhash akan valid.
Instruksi
Field
instructions
adalah array compact-encoded dari struct
CompiledInstruction.
Setiap CompiledInstruction mereferensikan akun berdasarkan indeks ke dalam
array account_keys, bukan berdasarkan public key lengkap. Ini berisi:
program_id_index: Indeks ke dalamaccount_keysyang mengidentifikasi program yang akan dipanggil.accounts: Array indeks ke dalamaccount_keysyang menentukan akun yang akan diteruskan ke program.data: Array byte yang berisi diskriminator instruksi dan argumen yang diserialisasi.
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 compact dari instruksi
Format biner transaksi
Transaksi diserialisasi menggunakan skema encoding compact. Semua array dengan panjang variabel (signature, kunci akun, instruksi) diawali dengan encoding panjang compact-u16. Format ini menggunakan 1 byte untuk nilai 0-127 dan 2-3 byte untuk nilai yang lebih besar.
Layout transaksi legacy (on the wire):
| Field | Ukuran | Deskripsi |
|---|---|---|
num_signatures | 1-3 byte (compact-u16) | Jumlah signature |
signatures | num_signatures x 64 byte | Signature Ed25519 |
num_required_signatures | 1 byte | Field MessageHeader 1 |
num_readonly_signed | 1 byte | Field MessageHeader 2 |
num_readonly_unsigned | 1 byte | Field MessageHeader 3 |
num_account_keys | 1-3 byte (compact-u16) | Jumlah kunci akun statis |
account_keys | num_account_keys x 32 byte | Public key |
recent_blockhash | 32 byte | Blockhash |
num_instructions | 1-3 byte (compact-u16) | Jumlah instruksi |
instructions | variabel | Array instruksi yang dikompilasi |
Setiap instruksi yang dikompilasi diserialisasi sebagai:
| Field | Size | Description |
|---|---|---|
program_id_index | 1 byte | Indeks ke kunci akun |
num_accounts | 1-3 bytes (compact-u16) | Jumlah indeks akun |
account_indices | num_accounts x 1 byte | Indeks kunci akun |
data_len | 1-3 bytes (compact-u16) | Panjang instruction data |
data | data_len bytes | Instruction data opak |
Perhitungan ukuran
Dengan PACKET_DATA_SIZE = 1.232 bytes, ruang yang tersedia dapat dihitung:
Total = 1232 bytes- compact-u16(num_sigs) # 1 byte- num_sigs * 64 # signature bytes- 3 # message header- compact-u16(num_keys) # 1 byte- num_keys * 32 # account key bytes- 32 # recent blockhash- compact-u16(num_ixs) # 1 byte- sum(instruction_sizes) # per-instruction overhead + data
Contoh: transaksi transfer SOL
Diagram di bawah ini menunjukkan bagaimana transaksi dan instruksi bekerja sama untuk memungkinkan pengguna berinteraksi dengan jaringan. Dalam contoh ini, SOL ditransfer dari satu akun ke akun lainnya.
Metadata akun pengirim mengindikasikan bahwa akun tersebut harus menandatangani transaksi. Ini memungkinkan System Program mengurangi lamport. Baik akun pengirim maupun penerima harus dapat ditulis (writable), agar saldo lamport mereka dapat berubah. Untuk mengeksekusi instruksi ini, wallet pengirim mengirimkan transaksi yang berisi tanda tangannya dan pesan yang berisi instruksi transfer SOL.
SOL transfer diagram
Setelah transaksi dikirim, System Program memproses instruksi transfer dan memperbarui saldo lamport dari kedua akun.
SOL transfer process diagram
Contoh di bawah ini menunjukkan kode yang relevan dengan diagram di atas. Lihat
fungsi transfer
System Program.
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);
Contoh berikut menunjukkan struktur transaksi yang berisi 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));
Kode di bawah ini menampilkan output dari cuplikan kode sebelumnya. Formatnya berbeda antar SDK, namun perhatikan bahwa setiap instruksi berisi informasi wajib 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}}]}
Mengambil detail transaksi
Setelah pengiriman, ambil detail transaksi menggunakan signature transaksi dan metode RPC getTransaction.
Anda juga dapat menemukan transaksi menggunakan Solana Explorer.
{"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?