İş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ın 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. Bir işlemi, formlar içeren bir zarf olarak düşünebilirsiniz. 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.
Basitleştirilmiş İşlem
Ö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 sınırı 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 eder.
Solana'da, "cüzdanlar" System Program tarafından sahip olunan hesaplardır. Yalnızca program sahibi bir hesabın verilerini değiştirebilir, bu nedenle SOL transferi yapmak System Program'ı çağıran bir işlem göndermeyi gerektirir.
SOL Transfer
Gönderici hesabın, System Program'ın lamport bakiyesini düşürebilmesi için
işlemi imzalaması (is_signer
) gerekir. Gönderici ve alıcı hesapların lamport
bakiyeleri değiştiği için yazılabilir (is_writable
) olmaları gerekir.
İşlemi gönderdikten 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 İşlemi
Aşağıdaki örnekler, bir hesaptan diğerine SOL aktaran bir işlemin nasıl gönderileceğini göstermektedir. System Program'ın transfer talimatı kaynak kodunu buradan inceleyebilirsiniz.
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.
const transferAmount = 0.01; // 0.01 SOLconst transferInstruction = getTransferSolInstruction({source: sender,destination: recipient.address,amount: transferAmount * LAMPORTS_PER_SOL});
Aşağıdaki bölümlerde, işlemler ve talimatların ayrıntılarını inceleyeceğiz.
Talimatlar
Solana'daki bir talimat, Solana ağını kullanan herkes tarafından çağrılabilen bir program üzerindeki genel bir fonksiyon olarak düşünülebilir.
Bir Solana programını, Solana ağında barındırılan bir web sunucusu olarak
düşünebilirsiniz; burada her talimat, kullanıcıların belirli eylemleri
gerçekleştirmek için çağırabileceği genel bir API uç noktası gibidir. Bir
talimatı çağırmak, bir API uç noktasına POST
isteği göndermeye benzer ve
kullanıcıların programın iş mantığını yürütmesine olanak tanır.
Solana'da bir programın talimatını çağırmak için, üç bilgi parçasıyla bir
Instruction
oluşturmanız gerekir:
- Program ID: Çağrılan talimat için iş mantığına sahip programın adresi.
- Hesaplar: Talimatın okuduğu veya yazdığı tüm hesapların listesi.
- Instruction Data: Programda hangi talimatın çağrılacağını ve talimat tarafından gereken argümanları belirten bir 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>,}
İşlem Talimatı
AccountMeta
Bir Instruction
oluştururken, her gerekli hesabı bir
AccountMeta
olarak sağlamanız gerekir. AccountMeta
aşağıdakileri belirtir:
- pubkey: Hesabın adresi
- is_signer: Hesabın işlemi imzalaması gerekip gerekmediği
- is_writable: Talimatın hesabın 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,}
Bir talimatın hangi hesapları okuduğunu veya yazdığını önceden belirterek, aynı hesapları değiştirmeyen işlemler paralel olarak yürütülebilir.
Bir talimatın hangi hesaplara ihtiyaç duyduğunu, hangilerinin yazılabilir, salt okunur olması gerektiğini veya işlemi imzalaması gerektiğini bilmek için, talimatın program tarafından tanımlandığı şekildeki uygulamasına başvurmanız gerekir.
Pratikte, genellikle bir Instruction
manuel olarak oluşturmanız gerekmez. Çoğu
program geliştiricisi, sizin için talimatları oluşturan yardımcı fonksiyonlar
içeren istemci kütüphaneleri sağlar.
AccountMeta
Örnek Talimat Yapısı
Bir SOL transfer talimatı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ıyı göstermektedir. Tam format SDK'ya bağlı olarak değişir, ancak her Solana talimatı aşağıdaki bilgileri gerektirir:
- Program ID: Talimatı yürütecek programın adresi.
- Hesaplar: Talimat tarafından 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 tarafından 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
Çağırmak istediğiniz talimatları oluşturduktan sonra, bir sonraki adım bir
Transaction
oluşturmak ve talimatları işleme eklemektir. Bir Solana
işlemi
şunlardan oluşur:
- İmzalar: İşlemdeki talimatlar için imzalayıcı olarak
gereken tüm hesaplardan alınan
imzaların
bir dizisi. İmza, işlemin
Message
hesabın özel anahtarıyla imzalanmasıyla oluşturulur. - Mesaj: İşlem mesajı atomik olarak işlenecek talimatların listesini içerir.
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 Karması: İşlem için 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 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 başlık) çı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 karması (32 bayt) ve talimatlar
İşlem Formatı
Mesaj Başlığı
Mesaj başlığı işlemdeki hesap için izinleri belirtir. Hangi hesapların imzalayıcı ve hangilerinin yazılabilir olduğunu belirlemek için kesin sıralı hesap adresleri ile birlikte çalışır.
- İşlemdeki tüm talimatlar için gereken imza sayısı.
- Salt okunur olan imzalı hesapların sayısı.
- Salt okunur olan 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ı
Bir işlem mesajındaki kompakt dizi, aşağıdaki formatta serileştirilmiş 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 tüm hesap adreslerinin tek bir listesini içerir. Dizi, kaç adres içerdiğini belirten bir compact-u16 sayısı ile başlar.
Alan tasarrufu sağlamak için, işlem her hesap için izinleri ayrı ayrı saklamaz.
Bunun yerine, izinleri belirlemek için MessageHeader
ve hesap adreslerinin
katı bir sıralaması kombinasyonuna dayanır.
Adresler her zaman aşağıdaki şekilde sıralanır:
- Yazılabilir ve imzalayan hesaplar
- Salt okunur ve imzalayan hesaplar
- Yazılabilir ve imzalamayan hesaplar
- Salt okunur ve imzalamayan hesaplar
MessageHeader
, her izin grubu için hesap sayısını belirlemek için kullanılan
değerleri sağlar.
Hesap adreslerinin kompakt dizisi
Son Blok Hash'i
Her işlem, iki amaca hizmet eden bir son blok hash'i gerektirir:
- İşlemin ne zaman oluşturulduğuna dair bir zaman damgası görevi 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, bu süreden sonra işlem süresi dolmuş kabul edilir ve işlenemez.
Geçerli blok hash'ini ve blok hash'inin geçerli olacağı son blok yüksekliğini
almak için getLatestBlockhash
RPC
yöntemini kullanabilirsiniz.
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, compact-u16 uzunluğu ile başlar ve ardından talimat verileri gelir. Her talimat şunları içerir:
- Program ID İndeksi: Hesap adresleri dizisindeki programın adresini işaret eden bir indeks. Bu, talimatı işleyecek programı belirtir.
- Hesap İndeksleri: Bu talimat için gerekli olan hesap adreslerini işaret eden indeksler dizisi.
- instruction data: Programda hangi talimatın çağrılacağını ve talimat tarafından 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 farklılık gösterir, 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 gönderdikten sonra, getTransaction RPC yöntemini kullanarak ayrıntılarını alabilirsiniz. Yanıt, aşağıdaki parçaya benzer bir yapıya sahip olacaktır. Alternatif olarak, Solana Explorer kullanarak işlemi inceleyebilirsiniz.
Bir "işlem imzası" Solana'daki bir işlemi benzersiz şekilde tanımlar. Bu imzayı, işlemin ağdaki ayrıntılarını aramak için kullanırsınız. İşlem imzası, basitçe işlemdeki ilk imzadır. İlk imzanın aynı zamanda işlem ücreti ödeyicisinin imzası olduğunu unutmayın.
{"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?