للتفاعل مع شبكة سولانا، يجب عليك إرسال معاملة. يمكنك التفكير في المعاملة كمظروف يحتوي على عدة نماذج. كل نموذج هو تعليمة تخبر الشبكة بما يجب القيام به. إرسال المعاملة يشبه إرسال المظروف بالبريد حتى يمكن معالجة النماذج.
يوضح المثال أدناه نسخة مبسطة من معاملتين. عند معالجة المعاملة الأولى، ستنفذ تعليمة واحدة. عند معالجة المعاملة الثانية، ستنفذ ثلاث تعليمات بترتيب متسلسل: أولاً التعليمة 1، تليها التعليمة 2، ثم التعليمة 3.
المعاملات ذرية: إذا فشلت تعليمة واحدة، ستفشل المعاملة بأكملها ولن تحدث أي تغييرات.
رسم توضيحي مبسط يوضح معاملتين
تتكون
Transaction
من المعلومات التالية:
signatures: مصفوفة من التوقيعاتmessage: معلومات المعاملة، بما في ذلك قائمة التعليمات المراد معالجتها
pub struct Transaction {#[wasm_bindgen(skip)]#[serde(with = "short_vec")]pub signatures: Vec<Signature>,#[wasm_bindgen(skip)]pub message: Message,}
رسم توضيحي يوضح جزأي المعاملة
تحتوي المعاملات على حد إجمالي للحجم يبلغ
1232
بايت. يشمل هذا الحد كلاً من مصفوفة signatures وبنية
message.
تم تصميم هذا الحد لتجنب تجزئة الحزم على البنية التحتية النموذجية للإنترنت. في حين أن IPv6 يدعم وحدات نقل البيانات (MTUs) الأكبر من 9000 بايت، إلا أن معظم أجهزة توجيه الإنترنت تستخدم وحدة نقل بيانات افتراضية تبلغ 1500 بايت (إيثرنت قياسي). لضمان ملاءمة المعاملات ضمن حزمة واحدة دون تجزئة، تستخدم سولانا 1280 بايت (الحد الأدنى لوحدة نقل البيانات المطلوبة لـ IPv6) ناقص 48 بايت لرؤوس الشبكة (40 بايت IPv6 + 8 بايت رأس التجزئة/UDP)، مما يؤدي إلى حد حجم المعاملة البالغ 1232 بايت.
رسم توضيحي يوضح تنسيق المعاملة وحدود الحجم
التوقيعات
تحتوي مصفوفة signatures الخاصة بالمعاملة على بنيات Signature. كل
Signature
يبلغ حجمه 64 بايت ويتم إنشاؤه من خلال توقيع Message الخاص بالمعاملة باستخدام
المفتاح الخاص للحساب. يجب توفير توقيع لكل حساب موقّع مضمّن
في أي من تعليمات المعاملة.
التوقيع الأول ينتمي إلى الحساب الذي سيدفع الرسوم الأساسية للمعاملة وهو توقيع المعاملة. يمكن استخدام توقيع المعاملة للبحث عن تفاصيل المعاملة على الشبكة.
الرسالة
يعتبر message الخاص بالمعاملة بنية
Message
تحتوي على المعلومات التالية:
header: رأس الرسالةaccount_keys: مصفوفة من عناوين الحسابات المطلوبة لتعليمات المعاملةrecent_blockhash: blockhash يعمل كطابع زمني للمعاملةinstructions: مصفوفة من التعليمات
لتوفير المساحة، لا تقوم المعاملة بتخزين الأذونات لكل حساب بشكل فردي. بدلاً من
ذلك، يتم تحديد أذونات الحساب باستخدام header و account_keys.
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 الخاص بالرسالة بنية
MessageHeader.
يحتوي على المعلومات التالية:
num_required_signatures: العدد الإجمالي للتوقيعات المطلوبة من قبل المعاملةnum_readonly_signed_accounts: العدد الإجمالي للحسابات للقراءة فقط التي تتطلب توقيعاتnum_readonly_unsigned_accounts: العدد الإجمالي للحسابات للقراءة فقط التي لا تتطلب توقيعات
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,}
رسم توضيحي يوضح الأجزاء الثلاثة لرأس الرسالة
عناوين الحسابات
إن
account_keys
الخاص بالرسالة هو مصفوفة من عناوين الحسابات، يتم إرسالها بتنسيق
مصفوفة مضغوطة. يشير بادئة
المصفوفة إلى طولها. كل عنصر في المصفوفة هو مفتاح عام، يشير إلى حساب تستخدمه
التعليمات الخاصة به. يجب أن تكون مصفوفة accounts_keys كاملة ومرتبة بشكل صارم،
على النحو التالي:
- موقّع + قابل للكتابة
- موقّع + للقراءة فقط
- ليس موقّعًا + قابل للكتابة
- ليس موقّعًا + للقراءة فقط
يسمح الترتيب الصارم بدمج مصفوفة account_keys مع المعلومات الموجودة في
header الخاص بالرسالة لتحديد الأذونات لكل حساب.
رسم توضيحي يوضح ترتيب مصفوفة عناوين الحسابات
كتلة التجزئة الأخيرة
إن recent_blockhash الخاص بالرسالة هو قيمة تجزئة تعمل كطابع زمني للمعاملة
وتمنع المعاملات المكررة. تنتهي صلاحية blockhash بعد
150 كتلة.
(ما يعادل دقيقة واحدة—بافتراض أن كل كتلة تستغرق 400 ميلي ثانية.) بعد انتهاء
صلاحية الكتلة، تنتهي صلاحية المعاملة ولا يمكن معالجتها.
تتيح لك طريقة getLatestBlockhash في RPC
الحصول على blockhash الحالي وآخر ارتفاع للكتلة الذي سيكون عنده blockhash
صالحاً.
التعليمات
إن
instructions
الخاص بالرسالة هو مصفوفة من جميع التعليمات المراد معالجتها، يتم إرسالها بتنسيق
مصفوفة مضغوطة. تشير بادئة
المصفوفة إلى طولها. كل عنصر في المصفوفة هو بنية
CompiledInstruction
وتتضمن المعلومات التالية:
program_id_index: فهرس يشير إلى عنوان في مصفوفةaccount_keys. تشير هذه القيمة إلى عنوان البرنامج الذي يعالج التعليمات.accounts: مصفوفة من الفهارس تشير إلى عناوين في مصفوفةaccount_keys. يشير كل فهرس إلى عنوان حساب مطلوب لهذه التعليمات.data: مصفوفة بايتات تحدد التعليمات المراد استدعاؤها على البرنامج. كما تتضمن أي بيانات إضافية مطلوبة للتعليمات. (على سبيل المثال، وسائط الدالة)
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>,}
مصفوفة مضغوطة من التعليمات
مثال على بنية المعاملة
يوضح المثال التالي بنية معاملة تحتوي على تعليمة واحدة لتحويل 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));
يوضح الكود أدناه الناتج من مقتطفات الكود السابقة. يختلف التنسيق بين مجموعات أدوات التطوير (SDKs)، ولكن لاحظ أن كل تعليمة تحتوي على نفس المعلومات المطلوبة.
{"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}}]}
بعد إرسال المعاملة، يمكنك استرداد تفاصيلها باستخدام توقيع المعاملة وطريقة RPC getTransaction. سيكون للاستجابة بنية مشابهة للمقتطف التالي.
يمكنك أيضًا العثور على المعاملة باستخدام مستكشف سولانا.
{"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?