İşlemler ve Talimatlar

Solana'da, kullanıcılar ağ ile etkileşim kurmak için işlemler gönderirler. İşlemler, işlenecek operasyonları belirten bir veya daha fazla talimat içerir. Talimatlar için yürütme mantığı, Solana ağına dağıtılan programlarda saklanır ve her program kendi talimat setini tanımlar.

Aşağıda Solana işlem işleme hakkında önemli detaylar bulunmaktadır:

  • Bir işlem birden fazla talimat içeriyorsa, talimatlar işleme eklenen sırayla yürütülür.
  • İşlemler "atomiktir" - tüm talimatlar başarıyla işlenmeli, aksi takdirde tüm işlem başarısız olur ve hiçbir değişiklik gerçekleşmez.

Bir işlem, esasen bir veya daha fazla talimatı işleme koymak için bir istektir.

Basitleştirilmiş İşlemBasitleştirilmiş İşlem

Bir işlem, formlar içeren bir zarf gibidir. Her form, ağa ne yapması gerektiğini söyleyen bir talimattır. İşlemi göndermek, formların işlenmesi için zarfı postalamak gibidir.

Önemli Noktalar

  • Solana işlemleri, ağdaki programları çağıran talimatlar içerir.
  • İşlemler atomiktir - herhangi bir talimat başarısız olursa, tüm işlem başarısız olur ve hiçbir değişiklik gerçekleşmez.
  • Bir işlemdeki talimatlar sıralı olarak yürütülür.
  • İşlem boyutu limiti 1232 bayttır.
  • Her talimat üç parça bilgi gerektirir:
    1. Çağrılacak programın adresi
    2. Talimatın okuduğu veya yazdığı hesaplar
    3. Talimat tarafından gereken herhangi bir ek veri (örn. fonksiyon argümanları)

SOL Transfer Örneği

Aşağıdaki diyagram, bir göndericiden bir alıcıya SOL transferi için tek bir talimat içeren bir işlemi temsil etmektedir.

Solana'da, "cüzdanlar" System Program tarafından sahip olunan hesaplardır. Bir hesabın verilerini yalnızca program sahibi değiştirebildiği için, SOL transferi System Program'ı çağıran bir işlem göndermeyi gerektirir.

SOL TransferiSOL Transferi

Gönderici hesap, System Program'ın lamport bakiyesini düşürebilmesi için işlemi imzalamalıdır (is_signer). Gönderici ve alıcı hesaplar, lamport bakiyeleri değişeceği için yazılabilir olmalıdır (is_writable).

İşlem gönderildikten sonra, System Program transfer talimatını işler. Ardından System Program, hem gönderici hem de alıcı hesapların lamport bakiyelerini günceller.

SOL Transfer SüreciSOL Transfer Süreci

Aşağıdaki örnekler, bir hesaptan diğerine SOL aktaran bir işlemin nasıl gönderileceğini göstermektedir.

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 cluster
const rpc = createSolanaRpc("http://localhost:8899");
const rpcSubscriptions = createSolanaRpcSubscriptions("ws://localhost:8900");
// Generate sender and recipient keypairs
const 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 airdrop
await airdropFactory({ rpc, rpcSubscriptions })({
recipientAddress: sender.address,
lamports: lamports(LAMPORTS_PER_SOL), // 1 SOL
commitment: "confirmed"
});
// Check balance before transfer
const { 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 recipient
const transferInstruction = getTransferSolInstruction({
source: sender,
destination: recipient.address,
amount: transferAmount // 0.01 SOL in lamports
});
// Add the transfer instruction to a new transaction
const { 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 network
const signedTransaction =
await signTransactionMessageWithSigners(transactionMessage);
await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(
signedTransaction,
{ commitment: "confirmed" }
);
const transactionSignature = getSignatureFromTransaction(signedTransaction);
// Check balance after transfer
const { 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);
Click to execute the code.

İstemci kütüphaneleri genellikle program talimatları oluşturma detaylarını soyutlar. Bir kütüphane mevcut değilse, talimatı manuel olarak oluşturabilirsiniz. Bu, talimatın uygulama detaylarını bilmenizi gerektirir.

Aşağıdaki örnekler, transfer talimatının nasıl manuel olarak oluşturulacağını göstermektedir. Expanded Instruction sekmesi, işlevsel olarak Instruction sekmesine eşdeğerdir.

  • Kit
const transferAmount = 0.01; // 0.01 SOL
const transferInstruction = getTransferSolInstruction({
source: sender,
destination: recipient.address,
amount: transferAmount * LAMPORTS_PER_SOL
});
  • Legacy
const transferAmount = 0.01; // 0.01 SOL
const transferInstruction = SystemProgram.transfer({
fromPubkey: sender.publicKey,
toPubkey: receiver.publicKey,
lamports: transferAmount * LAMPORTS_PER_SOL
});
  • Rust
let transfer_amount = LAMPORTS_PER_SOL / 100; // 0.01 SOL
let transfer_instruction =
system_instruction::transfer(&sender.pubkey(), &recipient.pubkey(), transfer_amount);

Aşağıdaki bölümlerde, işlemler ve talimatların ayrıntılarını inceleyeceğiz.

Instructions

Solana programı üzerindeki bir instruction, Solana ağını kullanan herkes tarafından çağrılabilen genel bir fonksiyon olarak düşünülebilir.

Bir programın instruction'ını çağırmak için üç temel bilgi gereklidir:

  • Program ID: Instruction için yürütme mantığına sahip program
  • Accounts: Instruction'ın ihtiyaç duyduğu hesapların listesi
  • Instruction Data: Programda çağrılacak instruction'ı ve instruction tarafından gereken argümanları belirten bayt dizisi
Instruction
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>,
}

Transaction InstructionTransaction Instruction

AccountMeta

Bir instruction tarafından gereken her hesap, şunları içeren bir AccountMeta olarak sağlanmalıdır:

  • pubkey: Hesabın adresi
  • is_signer: Hesabın işlemi imzalaması gerekip gerekmediği
  • is_writable: Instruction'ın hesap verilerini değiştirip değiştirmediği
AccountMeta
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,
}

AccountMetaAccountMeta

Bir instruction'ın hangi hesapları okuyup yazdığını önceden belirterek, aynı hesapları değiştirmeyen işlemler paralel olarak yürütülebilir.

Örnek Instruction Yapısı

Bir SOL transfer instruction'ının yapısını görmek için aşağıdaki örnekleri çalıştırın.

import { generateKeyPairSigner, lamports } from "@solana/kit";
import { getTransferSolInstruction } from "@solana-program/system";
// Generate sender and recipient keypairs
const sender = await generateKeyPairSigner();
const recipient = await generateKeyPairSigner();
// Define the amount to transfer
const 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 recipient
const transferInstruction = getTransferSolInstruction({
source: sender,
destination: recipient.address,
amount: transferAmount
});
console.log(JSON.stringify(transferInstruction, null, 2));
Click to execute the code.

Aşağıdaki örnekler, önceki kod parçacıklarından çıktıları göstermektedir. Tam format SDK'ya bağlı olarak değişir, ancak her Solana instruction'ı şu bilgileri gerektirir:

  • Program ID: Talimatı yürütecek programın adresi.
  • Hesaplar: Talimat için gereken hesapların listesi. Her hesap için talimat, adresini, işlemi imzalaması gerekip gerekmediğini ve yazılıp yazılmayacağını belirtmelidir.
  • Veri: Programa hangi talimatı yürüteceğini söyleyen ve talimat için gereken argümanları içeren bir bayt tamponu.
{
"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
}
}

İşlemler

Bir Solana işlemi şunlardan oluşur:

  1. İmzalar: İşleme dahil edilen imzaların bir dizisi.
  2. Mesaj: Atomik olarak işlenecek talimatların listesi.
Transaction
pub struct Transaction {
#[wasm_bindgen(skip)]
#[serde(with = "short_vec")]
pub signatures: Vec<Signature>,
#[wasm_bindgen(skip)]
pub message: Message,
}

İşlem Formatıİşlem Formatı

Bir işlem mesajının yapısı şunlardan oluşur:

  • Mesaj Başlığı: İmzalayan ve salt okunur hesap sayısını belirtir.
  • Hesap Adresleri: İşlemdeki talimatlar tarafından gereken hesap adreslerinin bir dizisi.
  • Son Blok Hash'i: İşlem için bir zaman damgası görevi görür.
  • Talimatlar: Yürütülecek talimatların bir dizisi.
Message
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>,
}

İşlem Mesajıİşlem Mesajı

İşlem Boyutu

Solana işlemleri 1232 baytlık bir boyut sınırına sahiptir. Bu sınır, IPv6 Maksimum İletim Birimi (MTU) boyutu olan 1280 bayttan, ağ başlıkları için 48 bayt (40 bayt IPv6 + 8 bayt parça başlığı) çıkarılarak elde edilir.

Bir işlemin toplam boyutu (imzalar ve mesaj) bu sınırın altında kalmalıdır ve şunları içerir:

  • İmzalar: Her biri 64 bayt
  • Mesaj: Başlık (3 bayt), hesap anahtarları (her biri 32 bayt), son blok hash'i (32 bayt) ve talimatlar

İşlem Formatıİşlem Formatı

Mesaj Başlığı

Mesaj başlığı hesap ayrıcalıklarını tanımlamak için üç bayt kullanır.

  1. Gerekli imzalar
  2. Salt okunur imzalı hesapların sayısı
  3. Salt okunur imzasız hesapların sayısı
MessageHeader
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,
}

Mesaj BaşlığıMesaj Başlığı

Kompakt Dizi Formatı

İşlem mesajındaki kompakt dizi, aşağıdaki formatta seri hale getirilmiş bir dizidir:

  1. Dizi uzunluğu (compact-u16 olarak kodlanmış)
  2. Dizi öğeleri art arda listelenmiş

Kompakt dizi formatıKompakt dizi formatı

Bu format, işlem mesajlarındaki Hesap Adresleri ve Talimatlar dizilerinin uzunluklarını kodlamak için kullanılır.

Hesap Adresleri Dizisi

Bir işlem mesajı, talimatları tarafından gereken hesap adreslerinin bir dizisini içerir. Dizi, kaç adres içerdiğini belirten bir compact-u16 sayısı ile başlar. Adresler daha sonra mesaj başlığı tarafından belirlenen ayrıcalıklarına göre sıralanır.

  • Yazılabilir ve imzalayıcı olan hesaplar
  • Salt okunur ve imzalayıcı olan hesaplar
  • Yazılabilir ve imzalayıcı olmayan hesaplar
  • Salt okunur ve imzalayıcı olmayan hesaplar

Hesap adreslerinin kompakt dizisiHesap adreslerinin kompakt dizisi

Son Blok Hash'i

Her işlem, iki amaca hizmet eden bir son blok hash'i gerektirir:

  1. Zaman damgası olarak işlev görür
  2. Yinelenen işlemleri önler

Bir blok hash'i, 150 bloktan sonra (400ms blok süreleri varsayılarak yaklaşık 1 dakika) sona erer ve bu süreden sonra işlem işlenemez.

Mevcut blok hash'ini ve blok hash'inin geçerli olacağı son blok yüksekliğini almak için getLatestBlockhash RPC yöntemini kullanabilirsiniz. İşte Solana Playground üzerinde bir örnek.

Talimatlar Dizisi

Bir işlem mesajı, CompiledInstruction tipinde talimatlar dizisi içerir. Talimatlar, bir işleme eklendiğinde bu tipe dönüştürülür.

Mesajdaki hesap adresleri dizisi gibi, bir compact-u16 uzunluğu ile başlar ve ardından talimat verileri gelir. Her talimat şunları içerir:

  1. Program ID İndeksi: Programın adresini hesap adresleri dizisinde gösteren bir u8 indeksi. Bu, talimatı işleyecek programı belirtir.
  2. Hesap İndeksleri: Bu talimat için gerekli hesap adreslerini gösteren u8 indekslerinden oluşan bir dizi.
  3. Instruction Data: Programda hangi talimatın çağrılacağını ve talimat için gereken ek verileri (örn. fonksiyon argümanları) belirten bir bayt dizisi.
CompiledInstruction
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>,
}

Talimatların Kompakt DizisiTalimatların Kompakt Dizisi

Örnek İşlem Yapısı

Tek bir SOL transfer talimatı içeren bir işlemin yapısını görmek için aşağıdaki örnekleri çalıştırın.

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 keypairs
const sender = await generateKeyPairSigner();
const recipient = await generateKeyPairSigner();
// Define the amount to transfer
const 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 recipient
const transferInstruction = getTransferSolInstruction({
source: sender,
destination: recipient.address,
amount: transferAmount
});
// Create transaction message
const transactionMessage = pipe(
createTransactionMessage({ version: 0 }),
(tx) => setTransactionMessageFeePayerSigner(sender, tx),
(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
(tx) => appendTransactionMessageInstructions([transferInstruction], tx)
);
const signedTransaction =
await signTransactionMessageWithSigners(transactionMessage);
// Decode the messageBytes
const compiledTransactionMessage =
getCompiledTransactionMessageDecoder().decode(signedTransaction.messageBytes);
console.log(JSON.stringify(compiledTransactionMessage, null, 2));
Click to execute the code.

Aşağıdaki örnekler, önceki kod parçacıklarından işlem mesajı çıktısını göstermektedir. Tam format SDK'ya bağlı olarak değişir, ancak aynı bilgileri içerir.

{
"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
}
}
]
}

Bir işlemi imzasını kullanarak ağa gönderdikten sonra aldığınızda, aşağıdaki yapıya sahip bir yanıt alacaksınız.

message alanı aşağıdaki alanları içerir:

  • header: accountKeys dizisindeki adresler için okuma/yazma ve imzalayan ayrıcalıklarını belirtir

  • accountKeys: İşlemin talimatlarında kullanılan tüm hesap adreslerinin dizisi

  • recentBlockhash: İşlemi zaman damgalamak için kullanılan blockhash

  • instructions: Yürütülecek talimatların dizisi. Her talimattaki account ve programIdIndex, indeks ile accountKeys dizisine referans verir.

  • signatures: İşlemdeki talimatlar tarafından imzalayan olarak gereken tüm hesaplar için imzaları içeren dizi. İmza, ilgili hesabın özel anahtarını kullanarak işlem mesajını imzalayarak oluşturulur.

Transaction Data
{
"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?

İçindekiler

Sayfayı Düzenle