خط معالجة المعاملات

ملخص

تمر المعاملات عبر 8 مراحل: الاستقبال، والتحقق من التوقيع، والتعقيم، وفحوصات الميزانية/العمر، والتحقق من دافع الرسوم، وتحميل الحساب، وتنفيذ التعليمات، والتثبيت.

خط معالجة المعاملات

عندما تصل معاملة إلى validator، فإنها تمر عبر سلسلة من مراحل التحقق والتنفيذ. يصف ما يلي خط المعالجة الكامل من الاستقبال إلى التثبيت، مع مراجع ملفات المصدر في عميل validator agave.

1. الاستقبال وإلغاء التسلسل

يستقبل validator بايتات المعاملة عبر UDP/QUIC. يجب أن تتناسب البايتات الخام ضمن حزمة واحدة (PACKET_DATA_SIZE = 1,232 بايت). يتم إلغاء تسلسل البايتات إلى VersionedTransaction، والذي يحتوي على مصفوفة التوقيعات و VersionedMessage (إما قديم أو v0).

2. التحقق من التوقيع (sigverify)

يتم التحقق من التوقيعات في مرحلة sigverify قبل دخول المعاملة إلى مرحلة البنوك. لكل توقيع عند الفهرس i، يتحقق المدقق من Ed25519(signatures[i]، account_keys[i]، message_bytes). إذا كان أي توقيع غير صالح، يتم تجاهل الحزمة.

يتم التحقق بشكل متوازٍ: يقسم validator دفعات الحزم إلى أجزاء من VERIFY_PACKET_CHUNK_SIZE (128) ويعالجها بشكل متوازٍ.

3. التعقيم

يتم تعقيم المعاملة التي تم إلغاء تسلسلها لإنتاج SanitizedTransaction (أو RuntimeTransaction). يتحقق التعقيم من الثوابت الهيكلية:

  • عدد التوقيعات يطابق num_required_signatures في الرأس
  • جميع program_id_index و account_indices للتعليمات ضمن الحدود
  • دافع الرسوم (فهرس الحساب 0) هو موقّع قابل للكتابة

يخزن غلاف RuntimeTransaction البيانات الوصفية المحسوبة مسبقاً من TransactionMeta: تجزئة الرسالة، وعلامة معاملة التصويت، وعدد توقيعات التجميع المسبق (Ed25519/secp256k1/secp256r1)، وتفاصيل تعليمات ميزانية الحوسبة، وإجمالي طول بيانات التعليمات.

4. التحقق من ميزانية الحوسبة والعمر وذاكرة التخزين المؤقت للحالة

تقوم طريقة check_transactions بإجراء عدة فحوصات لكل معاملة:

ميزانية الحوسبة: يتم تحليل تعليمات ميزانية الحوسبة الخاصة بالمعاملة والتحقق من صحتها أولاً. يتم حساب تفاصيل الرسوم من حدود الميزانية ورسوم الأولوية. إذا كانت ميزانية الحوسبة غير صالحة أو متعارضة، تفشل المعاملة مع أخطاء تحليل ميزانية الحوسبة مثل DuplicateInstruction، أو InstructionError(..., InvalidInstructionData)، أو InvalidLoadedAccountsDataSizeLimit.

عمر تجزئة الكتلة: يتم البحث عن recent_blockhash الخاص بالمعاملة في BlockhashQueue. إذا تم العثور على التجزئة وكان عمرها ضمن MAX_PROCESSING_AGE (150 فتحة)، تستمر المعاملة. إذا لم يتم العثور عليها، يتحقق المدقق من وجود nonce دائم صالح.

ذاكرة التخزين المؤقت للحالة: يتم التحقق من تجزئة رسالة المعاملة مقابل ذاكرة التخزين المؤقت للحالة. إذا تم العثور عليها، يتم رفض المعاملة مع AlreadyProcessed.

5. التحقق من صحة nonce ودافع الرسوم

تتعامل طريقة validate_transaction_nonce_and_fee_payer في SVM مع عمليتي تحقق:

التحقق من صحة nonce (إن وجد): بالنسبة لمعاملات nonce، يقوم المدقق بتحميل حساب nonce والتحقق من:

  • أن الحساب مملوك لبرنامج النظام (System Program)
  • أنه يتم تحليله كـ State::Initialized
  • أن nonce الدائم المخزن يطابق recent_blockhash الخاص بالمعاملة
  • أنه يمكن تقديم nonce (يختلف nonce الدائم الحالي عن nonce الدائم التالي، أي أن nonce لم يتم استخدامه بالفعل في الكتلة الحالية)
  • أن سلطة nonce قد وقعت على المعاملة

إذا كان صالحاً، يتم تقديم nonce إلى قيمة nonce الدائم التالية. راجع validate_transaction_nonce.

التحقق من صحة دافع الرسوم: يتم تحميل حساب دافع الرسوم (دائماً الفهرس 0) والتحقق منه بواسطة validate_fee_payer:

  • يجب أن يكون الحساب موجوداً (lamports > 0)، وإلا AccountNotFound
  • يجب أن يكون الحساب حساب نظام أو حساب nonce، وإلا InvalidAccountForFee
  • يجب أن تغطي lamports قيمة min_balance + total_fee، حيث min_balance يساوي 0 لحسابات النظام أو rent.minimum_balance(NonceState::size()) لحسابات nonce؛ وإلا InsufficientFundsForFee
  • بعد خصم الرسوم، يجب أن يظل الحساب معفياً من الإيجار (لا يمكن الانتقال من معفى من الإيجار إلى دافع للإيجار)

يتم خصم الرسوم من دافع الرسوم في هذه المرحلة. يتم حفظ لقطة من دافع الرسوم بعد خصم الرسوم (والـ nonce المتقدم، إن وُجد) كـ RollbackAccounts، وهي الحسابات التي يتم تثبيتها حتى في حالة فشل التنفيذ.

6. تحميل الحسابات

تقوم load_transaction بتحميل جميع الحسابات المشار إليها في المعاملة. تقوم AccountLoader بتغليف مخزن الحسابات الخارجي وتحافظ على ذاكرة تخزين مؤقتة محلية للدفعة بحيث تكون الحسابات المعدلة بواسطة المعاملات السابقة في نفس الدفعة مرئية للمعاملات اللاحقة.

لكل حساب غير دافع الرسوم، يقوم المحمّل بـ:

  1. جلب الحساب من الذاكرة المؤقتة أو قاعدة بيانات الحسابات
  2. تحديث حالة الإعفاء من الإيجار إذا لزم الأمر
  3. تجميع حجم بيانات الحساب نحو loaded_accounts_data_size_limit (الافتراضي 64 ميجابايت). يتحمل كل حساب عبئاً أساسياً قدره TRANSACTION_ACCOUNT_BASE_SIZE (64 بايت) بالإضافة إلى طول بياناته

لكل برنامج يتم استدعاؤه بواسطة تعليمات المعاملة، يتحقق المحمّل من أن حساب البرنامج موجود ومملوك لمحمّل صالح (NativeLoader أو أحد PROGRAM_OWNERS). تفشل البرامج غير الصالحة مع ProgramAccountNotFound أو InvalidProgramForExecution.

تقوم برامج LoaderV3 (القابلة للترقية) بتحميل حساب programdata المرتبط بها ضمنياً، والذي يُحتسب أيضاً ضمن حد حجم البيانات المحملة.

إذا فشل تحميل الحساب ولكن تم التحقق من صحة دافع الرسوم بنجاح، تصبح المعاملة نتيجة FeesOnly: يتم تحصيل الرسوم ولكن لا يتم تنفيذ أي تعليمات.

7. تنفيذ التعليمات

تقوم execute_loaded_transaction بإنشاء TransactionContext مع جميع الحسابات المحملة وتستدعي process_message. يتم تنفيذ التعليمات بشكل تسلسلي بالترتيب الذي تظهر به في الرسالة. ينشئ كل استدعاء تعليمة InvokeContext ويستدعي البرنامج المستهدف.

تفاصيل معالجة التعليمات

تقوم دالة process_message في وقت التشغيل بالتكرار عبر كل تعليمة وتستدعي البرنامج المستهدف:

  1. لكل تعليمة، يستدعي وقت التشغيل prepare_next_top_level_instruction، والذي يبني InstructionContext. يحتوي هذا السياق على مراجع لحسابات التعليمة (المحلولة من الفهارس المجمعة)، وبيانات التعليمة، وفهرس حساب البرنامج.
  2. يتحقق وقت التشغيل مما إذا كان البرنامج مجمّعاً مسبقاً (Ed25519، Secp256k1، Secp256r1). يتم التحقق من البرامج المجمّعة مسبقاً مباشرة دون استدعاء BPF VM.
  3. لجميع البرامج الأخرى، يستدعي وقت التشغيل process_instruction، والذي يحمّل البرنامج من ذاكرة التخزين المؤقت وينفذه في الجهاز الافتراضي BPF.
  4. بعد اكتمال التعليمة، يقوم وقت التشغيل بالتحقق من أن إجمالي رصيد lamport عبر جميع حسابات التعليمة لم يتغير (فحص UnbalancedInstruction).
  5. إذا فشلت أي تعليمة، يتم التراجع عن المعاملة بأكملها. لا يتم تثبيت أي تغييرات حالة وسيطة.

تزيد كل تعليمة من تتبع التعليمات. يتضمن التتبع كلاً من التعليمات ذات المستوى الأعلى وأي CPIs تستدعيها. لا يمكن أن يتجاوز إجمالي طول التتبع (التعليمات ذات المستوى الأعلى بالإضافة إلى جميع CPIs المتداخلة) 64 (MAX_INSTRUCTION_TRACE_LENGTH). يؤدي تجاوز هذا الحد إلى إرجاع InstructionError::MaxInstructionTraceLengthExceeded.

بعد التنفيذ، يتحقق وقت التشغيل من أن:

  • مجموع lamports عبر جميع الحسابات لم يتغير
  • لم ينتقل أي حساب من معفي من الإيجار إلى دافع للإيجار

8. التثبيت أو التراجع

إذا نجح التنفيذ، تُكتب حالات الحسابات المعدلة من TransactionContext مرة أخرى إلى ذاكرة التخزين المؤقت المحلية للدفعة في AccountLoader. إذا فشل التنفيذ، تُكتب فقط RollbackAccounts (دافع الرسوم مع خصم الرسوم والتقدم في nonce) مرة أخرى. لا يزال يتم تحصيل الرسوم، ولكن يتم تجاهل جميع تغييرات الحسابات الأخرى.

ملخص خط الأنابيب

Receive packet (UDP/QUIC)
--> Deserialize into VersionedTransaction
--> Sigverify (parallel Ed25519 verification)
--> Sanitize (structural validation, metadata extraction)
--> Parse compute budget, calculate fees
--> Check blockhash age (or verify nonce account)
--> Check status cache (dedup)
--> Validate nonce authority and advanceability (if nonce transaction)
--> Validate fee payer (load, check balance, deduct fee)
--> Load all accounts (with data size limits)
--> Load programs (verify loaders)
--> Execute instructions sequentially
--> Verify post-conditions (lamport balance, rent state)
--> Commit account changes (or rollback on failure)

مرجع أخطاء المعاملات

يسرد الجدول التالي جميع متغيرات TransactionError وفي أي مرحلة من خط الأنابيب تحدث:

الخطأالمرحلةالسبب
AccountInUseالجدولةالحساب مقفل بالفعل بواسطة معاملة أخرى في نفس الدفعة
AccountLoadedTwiceالجدولةيظهر pubkey مرتين في account_keys الخاص بالمعاملة
AccountNotFoundالتحقق من دافع الرسومحساب دافع الرسوم غير موجود
ProgramAccountNotFoundتحميل الحسابالبرنامج المستدعى غير موجود
InsufficientFundsForFeeالتحقق من دافع الرسومدافع الرسوم لا يمكنه تغطية الرسوم + الحد الأدنى المعفى من rent
InvalidAccountForFeeالتحقق من دافع الرسومدافع الرسوم ليس حساب نظام أو حساب nonce
AlreadyProcessedذاكرة التخزين المؤقت للحالةتمت معالجة المعاملة بالفعل
BlockhashNotFoundفحص العمرBlockhash غير موجود في قائمة الانتظار وليس nonce صالحاً
InstructionErrorالتنفيذحدث خطأ أثناء معالجة تعليمة (يتضمن فهرس التعليمة و InstructionError المحدد)
CallChainTooDeepتحميل الحسابسلسلة استدعاء المحمل عميقة جداً
MissingSignatureForFeeالتعقيمالمعاملة تتطلب رسوماً لكن لا يوجد توقيع
InvalidAccountIndexالتعقيمالمعاملة تحتوي على مرجع حساب غير صالح
SignatureFailureالتحقق من التوقيعتوقيع Ed25519 لا يتحقق (يتم تجاهل الحزمة)
InvalidProgramForExecutionتحميل الحسابالبرنامج غير مملوك بواسطة محمل صالح
SanitizeFailureالتعقيمفشلت المعاملة في تعقيم إزاحات الحسابات بشكل صحيح
ClusterMaintenanceالجدولةالمعاملات معطلة حالياً بسبب صيانة الشبكة
AccountBorrowOutstandingالتنفيذمعالجة المعاملة تركت حساباً مع مرجع مستعار معلق
WouldExceedMaxBlockCostLimitالجدولةالمعاملة ستتجاوز الحد الأقصى لتكلفة الكتلة
UnsupportedVersionالتعقيمإصدار المعاملة غير مدعوم
InvalidWritableAccountتحميل الحسابالمعاملة تحمل حساباً قابلاً للكتابة لا يمكن الكتابة عليه
WouldExceedMaxAccountCostLimitالجدولةالمعاملة ستتجاوز الحد الأقصى لتكلفة الحساب داخل الكتلة
WouldExceedAccountDataBlockLimitالجدولةالمعاملة ستتجاوز حد بيانات الحساب داخل الكتلة
TooManyAccountLocksالجدولةالمعاملة قفلت عدداً كبيراً جداً من الحسابات
AddressLookupTableNotFoundتحميل الحسابحساب جدول البحث عن العناوين غير موجود
InvalidAddressLookupTableOwnerتحميل الحسابجدول البحث عن العناوين مملوك بواسطة برنامج خاطئ
InvalidAddressLookupTableDataتحميل الحسابجدول البحث عن العناوين يحتوي على بيانات غير صالحة
InvalidAddressLookupTableIndexتحميل الحسابالبحث في جدول العناوين يستخدم فهرساً غير صالح
InvalidRentPayingAccountالفحص بعد التنفيذانتقل الحساب من معفى من rent إلى دافع rent
WouldExceedMaxVoteCostLimitالجدولةالمعاملة ستتجاوز الحد الأقصى لتكلفة التصويت
WouldExceedAccountDataTotalLimitالجدولةالمعاملة ستتجاوز الحد الإجمالي لبيانات الحساب
DuplicateInstructionتحليل ميزانية الحوسبةمتغير تعليمة ميزانية الحوسبة مكرر في نفس المعاملة
InsufficientFundsForRentالفحص بعد التنفيذالحساب لا يحتوي على lamports كافية لتغطية rent لحجم بياناته
MaxLoadedAccountsDataSizeExceededتحميل الحسابإجمالي البيانات المحملة يتجاوز حد 64 ميجابايت
InvalidLoadedAccountsDataSizeLimitتحليل ميزانية الحوسبةSetLoadedAccountsDataSizeLimit تم تعيينه إلى 0
ResanitizationNeededالتعقيمالمعاملة اختلفت قبل/بعد تفعيل الميزة وتحتاج إعادة تعقيم
ProgramExecutionTemporarilyRestrictedتحميل الحسابتنفيذ البرنامج مقيد مؤقتاً على الحساب المشار إليه
UnbalancedTransactionالفحص بعد التنفيذإجمالي رصيد lamport قبل المعاملة لا يساوي الرصيد بعدها
ProgramCacheHitMaxLimitتحميل الحسابذاكرة التخزين المؤقت للبرنامج وصلت للحد الأقصى
CommitCancelledالالتزامتم إلغاء الالتزام داخلياً

Is this page helpful?

تدار بواسطة

© 2026 مؤسسة سولانا.
جميع الحقوق محفوظة.
تواصل معنا