Özet
Bir işlem, imzalar + bir mesajdan oluşur. Mesaj bir başlık, hesap adresleri, güncel blok hash'i ve derlenmiş talimatlar içerir. Maksimum serileştirilmiş boyut: 1.232 bayt.
Bir
Transaction
iki üst düzey alana sahiptir:
signatures: İmza dizisimessage: İşlenecek talimatların listesi dahil olmak üzere işlem bilgileri
pub struct Transaction {pub signatures: Vec<Signature>,pub message: Message,}
Bir işlemin iki bölümünü gösteren diyagram
Bir işlemin toplam serileştirilmiş boyutu
PACKET_DATA_SIZE
değerini (1.232 bayt) aşmamalıdır. Bu sınır, 1.280 bayta (IPv6 minimum MTU)
eşittir ve ağ başlıkları için 48 bayt (40 bayt IPv6 + 8 bayt fragment başlığı)
çıkarılmıştır. 1.232 bayt hem signatures dizisini hem de
message yapısını içerir.
İşlem formatını ve boyut sınırlarını gösteren diyagram
İmzalar
signatures alanı, kompakt kodlanmış bir
Signature
değerleri dizisidir. Her Signature, imzalayan hesabın özel anahtarıyla
imzalanmış, serileştirilmiş Message'ın 64 baytlık bir Ed25519 imzasıdır.
İşlemin talimatları tarafından referans verilen her
imzalayan hesap için bir imza gereklidir.
Dizideki ilk imza, işlem temel ücret ve önceliklendirme ücretini ödeyen hesap olan ücret ödeyicisine aittir. Bu ilk imza aynı zamanda ağda işlemi aramak için kullanılan işlem kimliği olarak da hizmet eder. İşlem kimliği genellikle işlem imzası olarak adlandırılır.
Ücret ödeyen gereksinimleri:
- Mesajdaki ilk hesap (indeks 0) olmalı ve imzalayan olmalıdır.
- System Program'a ait bir hesap veya nonce hesabı olmalıdır
(
validate_fee_payertarafından doğrulanır). rent_exempt_minimum + total_feetutarını karşılayacak yeterli lamport içermelidir; aksi takdirde işlemInsufficientFundsForFeehatası ile başarısız olur.
Mesaj
message alanı, işlemin yükünü içeren bir
Message
yapısıdır:
header: Mesaj başlığıaccount_keys: İşlemin talimatları için gerekli hesap adresleri dizisirecent_blockhash: İşlem için zaman damgası görevi gören bir blockhashinstructions: Talimatlar 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>,}
Başlık
header alanı, account_keys dizisini izin gruplarına bölen üç u8 alanına
sahip bir
MessageHeader
yapısıdır:
num_required_signatures: İşlem için gereken toplam imza sayısı.num_readonly_signed_accounts: Salt okunur olan imzalı hesap sayısı.num_readonly_unsigned_accounts: Salt okunur olan imzasız hesap 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ığının üç bölümünü gösteren diyagram
Hesap adresleri
account_keys
alanı, kompakt kodlanmış bir public key dizisidir. Her giriş, işlemin
talimatlarından en az biri tarafından kullanılan bir hesabı tanımlar. Dizi her
hesabı içermeli ve şu katı sıralamayı izlemelidir:
- İmzalayan + Yazılabilir
- İmzalayan + Salt okunur
- İmzalayan olmayan + Yazılabilir
- İmzalayan olmayan + Salt okunur
Bu katı sıralama, account_keys dizisinin mesajın header
alanındaki üç sayımla birleştirilerek, hesap başına metadata bayrakları
saklamadan her hesap için izinlerin belirlenmesini sağlar. Başlık sayımları,
diziyi yukarıda listelenen dört izin grubuna böler.
Hesap adresleri dizisinin sırasını gösteren diyagram
Son blok hash'i
recent_blockhash alanı, iki amaca hizmet eden 32 baytlık bir hash'tir:
- Zaman damgası: işlemin yakın zamanda oluşturulduğunu kanıtlar.
- Tekrar önleme: aynı işlemin iki kez işlenmesini önler.
Bir blok hash'i 150 slot sonra geçerliliğini yitirir. İşlem ulaştığında blok
hash'i artık geçerli değilse, geçerli bir
dayanıklı nonce işlemi olmadığı sürece
BlockhashNotFound ile reddedilir.
getLatestBlockhash RPC metodu, mevcut
blok hash'ini ve blok hash'inin geçerli olacağı son blok yüksekliğini almanızı
sağlar.
Talimatlar
instructions
alanı,
CompiledInstruction
yapılarının kompakt kodlanmış bir dizisidir. Her CompiledInstruction, tam
public key yerine account_keys dizisindeki indeks ile hesaplara referans
verir. Şunları içerir:
program_id_index: Çağrılacak programı tanımlayanaccount_keysiçindeki indeks.accounts: Programa iletilecek hesapları belirtenaccount_keysiçindeki indeksler dizisi.data: Talimat ayırıcısını ve serileştirilmiş argümanları içeren 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
İşlem ikili formatı
İşlemler kompakt bir kodlama şeması kullanılarak serileştirilir. Tüm değişken uzunluklu diziler (imzalar, hesap anahtarları, talimatlar) compact-u16 uzunluk kodlaması ile öneklenir. Bu format, 0-127 değerleri için 1 bayt ve daha büyük değerler için 2-3 bayt kullanır.
Legacy işlem düzeni (kablo üzerinde):
| Alan | Boyut | Açıklama |
|---|---|---|
num_signatures | 1-3 bayt (compact-u16) | İmza sayısı |
signatures | num_signatures x 64 bayt | Ed25519 imzaları |
num_required_signatures | 1 bayt | MessageHeader alan 1 |
num_readonly_signed | 1 bayt | MessageHeader alan 2 |
num_readonly_unsigned | 1 bayt | MessageHeader alan 3 |
num_account_keys | 1-3 bayt (compact-u16) | Statik hesap anahtarı sayısı |
account_keys | num_account_keys x 32 bayt | Public key'ler |
recent_blockhash | 32 bayt | Blok hash'i |
num_instructions | 1-3 bayt (compact-u16) | Talimat sayısı |
instructions | değişken | Derlenmiş talimatlar dizisi |
Her derlenmiş talimat şu şekilde serileştirilir:
| Alan | Boyut | Açıklama |
|---|---|---|
program_id_index | 1 bayt | Hesap anahtarlarına indeks |
num_accounts | 1-3 bayt (compact-u16) | Hesap indeksi sayısı |
account_indices | num_accounts x 1 bayt | Hesap anahtarı indeksleri |
data_len | 1-3 bayt (compact-u16) | Talimat verisi uzunluğu |
data | data_len bayt | Opak talimat verisi |
Boyut hesaplama
PACKET_DATA_SIZE = 1.232 bayt olduğunda, kullanılabilir alan şu şekilde
hesaplanabilir:
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
Örnek: SOL transfer işlemi
Aşağıdaki diyagram, kullanıcıların ağ ile etkileşime girmesini sağlamak için işlemlerin ve talimatların birlikte nasıl çalıştığını göstermektedir. Bu örnekte, SOL bir hesaptan diğerine aktarılmaktadır.
Gönderen hesabın metaverisi, işlem için imzalaması gerektiğini belirtir. Bu, System Program'ın lamport düşmesine izin verir. Lamport bakiyelerinin değişebilmesi için hem gönderen hem de alıcı hesapların yazılabilir olması gerekir. Bu talimatı yürütmek için gönderenin cüzdanı, imzasını ve SOL transfer talimatını içeren mesajı içeren işlemi gönderir.
SOL transfer diyagramı
İşlem gönderildikten sonra, System Program transfer talimatını işler ve her iki hesabın lamport bakiyesini günceller.
SOL transfer işlem diyagramı
Aşağıdaki örnek, yukarıdaki diyagramlarla ilgili kodu göstermektedir. System
Program'ın
transfer fonksiyonuna
bakın.
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);
Aşağıdaki örnek, tek bir SOL transfer talimatı içeren bir işlemin yapısını göstermektedir.
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 kod, önceki kod parçacıklarından elde edilen çıktıyı gösterir. Format SDK'lar arasında farklılık gösterir, ancak her talimatın aynı gerekli bilgileri içerdiğine dikkat edin.
{"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}}]}
İşlem detaylarını getirme
Gönderimden sonra, işlem imzasını ve getTransaction RPC metodunu kullanarak işlem detaylarını alın.
İşlemi Solana Explorer kullanarak da bulabilirsiniz.
{"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?