المعاملات
للتفاعل مع شبكة سولانا، يجب عليك إرسال معاملة. يمكنك التفكير في المعاملة كمظروف يحتوي على عدة نماذج. كل نموذج هو تعليمة تخبر الشبكة بما يجب القيام به. إرسال المعاملة يشبه إرسال المظروف بالبريد حتى يمكن معالجة النماذج.
يوضح المثال أدناه نسخة مبسطة من معاملتين. عند معالجة المعاملة الأولى، ستنفذ تعليمة واحدة. عند معالجة المعاملة الثانية، ستنفذ ثلاث تعليمات بترتيب متسلسل: أولاً التعليمة 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: هاش كتلة يعمل كطابع زمني للمعاملة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 الخاصة بالرسالة قيمة تجزئة تعمل كطابع زمني للمعاملة
وتمنع المعاملات المكررة. تنتهي صلاحية كتلة التجزئة بعد
150 كتلة.
(ما يعادل دقيقة واحدة - بافتراض أن كل كتلة تستغرق 400 مللي ثانية.) بعد انتهاء
صلاحية الكتلة، تنتهي صلاحية المعاملة ولا يمكن معالجتها.
تتيح لك طريقة RPC getLatestBlockhash
الحصول على كتلة التجزئة الحالية وارتفاع آخر كتلة ستكون فيها كتلة التجزئة
صالحة.
التعليمات
تعتبر
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?