İş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ş İş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:
- Çağrılacak programın adresi
- Talimatın okuduğu veya yazdığı hesaplar
- 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 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ü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 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);
İ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 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);
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
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 Instruction
AccountMeta
Bir instruction tarafından gereken her hesap, şunları içeren bir AccountMeta olarak sağlanmalıdır:
pubkey
: Hesabın adresiis_signer
: Hesabın işlemi imzalaması gerekip gerekmediğiis_writable
: Instruction'ın hesap verilerini değiştirip değiştirmediği
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
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 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));
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:
- İmzalar: İşleme dahil edilen imzaların bir dizisi.
- Mesaj: Atomik olarak işlenecek talimatların listesi.
pub struct Transaction {#[wasm_bindgen(skip)]#[serde(with = "short_vec")]pub signatures: Vec<Signature>,#[wasm_bindgen(skip)]pub message: Message,}
İş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.
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 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ı
Mesaj Başlığı
Mesaj başlığı hesap ayrıcalıklarını tanımlamak için üç bayt kullanır.
- Gerekli imzalar
- Salt okunur imzalı hesapların sayısı
- Salt okunur imzasız hesapların sayısı
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ığı
Kompakt Dizi Formatı
İşlem mesajındaki kompakt dizi, aşağıdaki formatta seri hale getirilmiş bir dizidir:
- Dizi uzunluğu (compact-u16 olarak kodlanmış)
- Dizi öğeleri art arda listelenmiş
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 dizisi
Son Blok Hash'i
Her işlem, iki amaca hizmet eden bir son blok hash'i gerektirir:
- Zaman damgası olarak işlev görür
- 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:
- Program ID İndeksi: Programın adresini hesap adresleri dizisinde gösteren bir u8 indeksi. Bu, talimatı işleyecek programı belirtir.
- Hesap İndeksleri: Bu talimat için gerekli hesap adreslerini gösteren u8 indekslerinden oluşan bir dizi.
- 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.
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 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 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));
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 talimattakiaccount
veprogramIdIndex
, indeks ileaccountKeys
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.
{"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?