آلة Solana الافتراضية (SVM) تصبح معتمدة على نطاق واسع كطبقة التنفيذ لحلول Layer-2 (L2) المختلفة. ومع ذلك، القيد الرئيسي في التصميم الأصلي لـ SVM هو غموض جذر حالته العالمي. وهذا يشكل تحديات كبيرة لأنظمة ال Rollup، حيث تكون جذور الحالة العالمية أمرًا حاسمًا لضمان النزاهة وتمكين دلائل الغش ودعم العمليات عبر الطبقات.
في اللف، يقدم الشخص المقترح جذر الحالة L2 (جذر Merkle) إلى الطبقة-1 (L1) بانتظام، وإنشاء نقاط فحص لسلسلة L2. تسمح هذه النقاط الفحص بدلائل الاحتواء على أي حالة حساب، مما يمكن من تنفيذ بدون حالة من نقطة فحص إلى أخرى. تعتمد دلائل الاحتيال على هذا الآلية، حيث يمكن للمشاركين تقديم دلائل الاحتواء للتحقق من الإدخالات الصالحة خلال النزاعات. بالإضافة إلى ذلك، تعزز أشجار Merkle أمان الجسور الكنسية من خلال السماح للمستخدمين بتوليد دلائل الاحتواء على معاملات السحب، مما يضمن التفاعل بدون ثقة بين L2 و L1.
للتعامل مع هذه التحديات،شبكة SOONيقدم أشجار Merkle إلى طبقة تنفيذ SVM، مما يتيح للعملاء تقديم دلائل الاحتواء. يدمج SOON مع دليل السجل بواسطة استخدام إدخالات فريدة لتضمين جذور الحالة مباشرة في سلاسل الكتل القائمة على SVM. من خلال هذا الابتكار، يمكن لكومة SOON دعم تراكمات جديدة قائمة على SVM بأمان وقابلية للتوسيع وفائدة محسنة.
تم تصميم سولانا دائمًا بمعدل نقل عالٍ كهدف أساسي، مما يتطلب تضحية في التصميم العمدي — خاصة خلال مراحل تطويره المبكرة — لتحقيق أداءه الجديد. من بين هذه التضحيات، كانت إحدى القرارات الأكثر تأثيرًا تتمحور حول كيفية ومتى سيلقي سولانا بحالته.
في نهاية المطاف، خلقت هذه القرارات تحديات كبيرة للعملاء في إثبات الحالة العالمية وكذلك إدراج المعاملات والتحقق البسيط من الدفع (SPV). نقص الجذر الحالة المجزأ بشكل متسق الذي يمثل حالة SVM يشكل صعوبة كبيرة لمشاريع مثل العملاء الخفيفة والتجميعات.
لنلقِ نظرة على كيفية إجراء التحويل النضري على سلاسل أخرى ثم تحديد التحديات التي تطرحها هندسة بروتوكول سولانا.
في بيتكوين، يتم تخزين المعاملات في كتلة باستخدامشجرة ميركل, الجذر الذي يتم تخزينه في الرأس الكتلة. سيقوم بروتوكول بيتكوين بتجزئة مدخلات ومخرجات العملية (وكذلك بعض البيانات الوصفية الأخرى) إلى معرف المعاملة(TxID). من أجل إثبات الحالة على بيتكوين، يمكن للمستخدم ببساطة حساب دليل ميركل للتحقق من TxID ضد جذر الميركل للكتلة.
يقوم هذا العملية التحقق أيضًا بتحقق الحالة، حيث يكون معرف المعاملة فريدًا لمجموعة ما من المدخلات والمخرجات، وكلاهما يعكس تغييرات في حالة العنوان. يرجى ملاحظة أن معاملات بيتكوين يمكن أيضًا أن تحتوي علىنصوص تابروت، التي تنتج مخرجات المعاملات التي يمكن التحقق منها أثناء التحقق، غالبًا عن طريق إعادة تشغيل النص باستخدام مدخلات المعاملة وبيانات الشاهد النصي، والتحقق من مخرجاتها.
بالمثل لبيتكوين، تقوم إيثيريوم بتخزين المعاملات باستخدام هيكل بيانات مخصص (مستمد من شجرة ميركل) تسمى شجرة ميركل باتريشيا تراي(MPT). تم تصميم هيكل البيانات هذا للتحديثات السريعة والتحسين على مجموعات بيانات كبيرة. وبطبيعة الحال، يرجع ذلك إلى أن لدى إثيريوم مدخلات ومخرجات أكثر بكثير لإدارتها من بيتكوين.
البوابةآلة العقد الافتراضية لإيثريوم(EVM) يعمل كآلة حالة عالمية. EVM هو في الأساس بيئة حوسبة موزعة ضخمة تدعم التنفيذعقود ذكية، كل منها يحتفظ بمساحة عنوانه الخاصة في الذاكرة العالمية. ونتيجة لذلك، يجب على العملاء الذين يرغبون في التحقق من الحالة على إيثريوم أن يأخذوا في الاعتبار ليس فقط نتيجة للمعاملة (سجلات، رمز العودة، إلخ) ولكن أيضًا التغييرات في الحالة العالمية نتيجة للمعاملة.
لحسن الحظ، يستخدم EVM بذكاء ثلاثة هياكل trie مهمة، التي تخزن جذورها في كل رأس كتلة.
بناءً على عملية تحويل، يمكن للعميل إثبات إدراجها في كتلة عن طريق تقييم جذر شجرة العمليات (مثل Bitcoin)، ونتيجتها من خلال تقييم شجرة الإيصالات، والتغييرات على الحالة العامة من خلال تقييم شجرة الحالة.
أحد الأسباب وراء إنتاجية سولانا العالية هو أنها لا تمتلك الهيكل متعدد الأشجار الذي يمتلكه إثريوم. يقوم قادة سولانا بحساب أشجار ميركل عند إنشاء الكتل، ولكنها مهيأة بشكلٍ مختلف عن تلك الموجودة ضمن EVM. للأسف، هنا تكمن المشكلة بالنسبة للطبقات العلوية القائمة على SVM.
تدرج سولانا المعاملات في ما يُسمى بالإدخالات، ويمكن أن تكون هناك إدخالات متعددة في كل فتحة؛ وبالتالي، جذور معاملات متعددة في كل كتلة. علاوة على ذلك، تحسب سولانا جذرًا ميركليًا لحالة الحساب مرة واحدة فقط في كل حقبة (حوالي 2.5 يوم)، وهذا الجذر غير متوفر على دفتر الأستاذ.
في الواقع، رؤوس كتل Solana لا تحتوي على أي جذور Merkle على الإطلاق. بدلاً من ذلك، تحتوي على السابق والحاليرمز الكتلة, والتي يتم حسابها من خلال سولانا's دليل التاريخخوارزمية (PoH). يتطلب PoH من المحققين تسجيل "نقرات" باستمرار عن طريق تجزئة تكرارية لإدخالات الكتلة، التي يمكن أن تكون فارغة أو تحتوي على دفعات من المعاملات. النقرة النهائية (التجزئة) لخوارزمية PoH هي تجزئة تلك الكتلة.
المشكلة في دليل التاريخ هي أنه يجعل من الصعب جدًا إثبات الحالة من blockhash. تم تصميم Solana لبث تجزئة PoH من أجل الحفاظ على مفهوم الوقت المنقضي الخاص بها. جذر المعاملة متاح فقط لـ tick PoH الذي يحتوي على إدخال بالمعاملات، وليس الكتلة ككل، ولا يوجد جذر حالة مخزن في أي إدخال.
ومع ذلك، هناك تجاوز آخر يمكن للعملاء استخدامه: تجاوز البنك. يطلق عليه أحيانًا تجاوز الفتحة، وتكون تجاوزات البنك متاحة في الSlotHashes sysvarالحساب، الذي يمكن الاستعلام عنه من قبل العميل. يتم إنشاء تجزئة البنك لكل كتلة (فتحة) من مجموعة من المدخلات:
كما يمكن للمرء أن يرى ، فإن تجزئة البنك مثقلة بالعديد من مدخلات التجزئة ، مما يضيف تعقيدا للعملاء الذين يحاولون إثبات معلومات حول المعاملات أو الحالة. بالإضافة إلى ذلك ، فإن تجزئة البنك فقط للبنك الذي أجرى "تجزئة حسابات العصر" - تجزئة جميع الحسابات مرة واحدة لكل حقبة - سيتم تضمين هذا الجذر المحدد فيها. علاوة على ذلك ، يتم اقتطاع حساب SlotHashes sysvar إلى آخر 512 تجزئة بنكية.
شبكة SOONVM على Ethereum. أثناء دمج merklization في القريب العاجل، أعطينا الأولوية لاستخدام الحلول المثبتة والمعتمدة بدلاً من إعادة اختراع العجلة من أجل الاستقرار. في اتخاذ قرار بشأن الهيكل البيانات أو خوارزميات التجزئة لاستخدامها، اعتبرنا توافقها مع عقود L1 لـ تراكم التفاؤل, قدرتهم على التكامل بسلاسة في البنية التحتية لـ Solana، وما إذا كانوا قادرين على تحقيق أداء عالي بموجب نموذج الحسابات الخاص بـ Solana.
توصلنا إلى أنه مع زيادة عدد الحسابات، يعتمد نموذج تخزين Merkle Patricia Trie (MPT) شجرة LSMسيؤدي إلى مزيد من تضاعف قراءة/كتابة القرص، مما يؤدي إلى خسائر في الأداء. في النهاية، قررنا دمج الErigon MPTمن خلال الاعتماد على العمل الممتاز الذي تم إنجازه بواسطة Rust من قبلrETHفريق وإضافة دعم لنموذج حساب سولانا.
كما ذكر سابقا، فإن شجرة حالة SOON هي شجرة MPT مصممة لدعم حسابات Solana. وعلى هذا النحو، قمنا بتحديد نوع حساب متوافق مع SVM ليكون بيانات كل عقدة ورقة.
struct TrieSolanaAccount {
lamports: u64،
البيانات: Vec
قابل للتنفيذ: بول,
rent_epoch: u64،
owner: Pubkey,
}
لتمكين وحدة MPT من الاشتراك في أحدث حالة لحسابات SVM في الوقت الفعلي، قمنا بتقديم موجه الحساب. خلال مرحلة البنكية، يُعلم موجه الحساب وحدة MPT بتغييرات حالة الحساب، ويقوم MPT بتحديث هذه التغييرات تدريجيا في هيكل الشجرة.
من المهم أن نلاحظ أن وحدة MPT تقوم فقط بتحديث أشجارها الفرعية خلال كل 50 فتحة ولا تحسب جذر الحالة في نهاية كل فتحة. يتم اتباع هذا النهج لسببين. أولًا، فإن حساب جذر الحالة لكل فتحة سيؤثر بشكل كبير على الأداء. ثانيًا، الجذر الحالي مطلوب فقط عندما يقدم المقترح الخاص بنظامoutputRootللمرحلة L1. لذلك، يحتاج فقط إلى أن يُحسب بانتظام، استنادًا إلى تردد تقديم المقترح من قبل المقترح.
outputRoot = keccak256(version, state_root, withdraw_root, l2_block_hash)
يحتفظ وحدة MPT بنوعين من هياكل الشجرة معًا: شجرة الحالة للحالة العامة وشجرة السحب لمعاملات السحب. يقوم المقترح بانتظام بتوليد جذر الإخراج عن طريق جذر الحالة وجذر السحب، ويقدمه إلى L1.
حالياً، يقوم SOON بحساب جذر الحالة وجذر السحب مرة كل 450 فتحة ويقوم بإلحاقهما بسلسلة الكتل L2. نتيجة لذلك، يمكن ضمان تماثل بيانات MPT عبر العقد الآخر في الشبكة. ومع ذلك، لا يتضمن هيكل كتلة Solana رأس كتلة، مما يعني أنه لا يوجد مكان لتخزين جذر الحالة. دعنا نلقي نظرة أقرب على الهيكل الأساسي لسلسلة الكتل Solana ثم نستكشف كيف يقدم SOON المدخل الفريد لتخزين جذر الحالة.
يتكون سلسلة كتل سولانا من فتحات، التي تتم إنشاؤها بواسطة وحدة PoH. تحتوي الفتحة على مدخلات متعددة، حيث يتضمن كل مدخل علامات زمنية ومعاملات. في طبقات الشبكة والتخزين، يتم تخزين الفتحة ونقلها باستخدام قصاصاتكوحدة صغرى. يمكن تحويل الشرائح إلى ومن الإدخالات.
pub struct Entry {
عدد عمليات التجزئة منذ معرف الإدخال السابق.
pub num_hashes: u64,
/// تجزئة SHA-256 num_hashes
بعد رقم الإدخال السابق.
pub hash: Hash،
قائمة غير مرتبة بالمعاملات التي تمت ملاحظتها قبل معرف الإدخال
/// تم إنشاؤها. قد يكون قد لوحظت من قبل قبل معرف الإدخال السابق ولكن
/// تم دفعها مرة أخرى إلى هذه القائمة لضمان تفسير محدد للدفتر.
المعاملات العامة: Vec
}
لقد اتبعنا هيكل البلوكشين الذي تم إنشاؤه بواسطة PoH واحتفظنا ببنية الشَّريح، مما يتيح لنا إعادة استخدام طبقة تخزين سولانا الحالية وطبقة الشبكة وإطار عمل RPC. لتخزين البيانات الإضافية على بلوكشين L2، قمنا بإدخال الإدخال المميز. تتيح لنا هذه السمة تخصيص حمولة الإدخال وتحديد قواعد التحقق المستقلة للبيانات.
العمومي ثابت UNIQUE_ENTRY_NUM_HASHES_FLAG: u64 = 0x8000_0000_0000_0000;
الإدخال الفريد هو نوع إدخال خاص. الذي يكون مفيدًا عندما نحتاج إلى تخزين بعض البيانات في
/// blockstore ولكن لا نريد التحقق منها.
///
/// تخطيط num_hashes
is:
/// |…1 بت…|…63 بت…|
/// \ __/
/// \ \
/// حقل مخصص علم
pub السمة UniqueEntry: Sized {
fn encode_to_entries(&self) -> Vec
فك تشفير من الإدخالات(
الإدخالات: تحويل إلى مدخلات<العنصر = إدخال>،
) -> النتيجة
}
pub fn unique_entry(custom_field: u64, hash: Hash) -> Entry {
الدخول {
num_hashes: num_hashes(custom_field), hash, transactions: vec![],
}
}
النشر الوظيفي num_hashes (custom_field: u64) -> u64 {
assert!(custom_field < (1 << 63));
UNIQUE_ENTRY_NUM_HASHES_FLAG | custom_field
}
الحانة فن هو فريد (الدخول: &الدخول) -> بول {
entry.num_hashes & UNIQUE_ENTRY_NUM_HASHES_FLAG != 0
في UniqueEntry، يُستخدم num_hashes كتخطيط بت، حيث يُستخدم علم البت الأول للتمييز بين إدخال ومدخل فريد، ويتم استخدام البتات الـ 63 التالية لتحديد نوع الحمولة. يعمل حقل الهاش كالحمولة، حيث يحتوي على البيانات المخصصة المطلوبة.
دعونا نلقي نظرة على مثال استخدام ثلاث إدخالات فريدة لتخزين تجزئة الهاش، جذر الحالة، وجذر السحب.
/// MPT الجذر المدخل الفريد.
pub struct MptRoot {
فتحة النشر: Slot,
حالة الجذر: B256,
سحب الجذر: B256,
}
impl UniqueEntry for MptRoot {
fn encode_to_entries(&self) -> Vec
دعونا نقوم بإدخال الفتحة = الإدخال الفريد(0 ، الفتحة إلى تجزئة (الفتحة ذاتها)); دعونا نقوم بإدخال جذر الحالة = الإدخال الفريد(1 ، الحالة الجذرية.0.في( )); دعونا نقوم بإدخال جذر السحب = الإدخال الفريد(2 ، الجذر السحب.0.في( )); vec![فتحة الإدخال ، جذر الحالة الإدخال ، جذر السحب الإدخال]
}
فك تشفير من الإدخالات(
الإدخالات: تنفيذ IntoIterator
) -> نتيجة<ذاتي، فريدينتري خطأ> {
دع الإدخالات المتحولة = الإدخالات. إنتو_إيتر(); دع الإدخال يكون الإدخال. التالي(). حسنا_أور(UniqueEntryError::NoMoreEntries)?; دع الفتحة تكون الفتحة. التجزئة_إلى_فتحة(الإدخال. تجزئة); دع الإدخال يكون الإدخال. التالي(). حسنا_أور(UniqueEntryError::NoMoreEntries)?; دع جذر_الحالة يكون B256::from(الإدخال. تجزئة. إلى_بايت()); دع الإدخال يكون الإدخال. التالي(). حسنا_أور(UniqueEntryError::NoMoreEntries)?; دع جذر_الانسحاب يكون B256::from(الإدخال. تجزئة. إلى_بايت()); حسنا(MptRoot { الفتحة، جذر_الحالة، جذر_الانسحاب، })
}
}
نظرًا لأن UniqueEntry يعيد تعريف دلالة num_hashes، فلا يمكن معالجته أثناء مرحلة التحقق من PoH. لذلك، في بداية عملية التحقق، نقوم أولاً بتصفية الإدخالات الفريدة وتوجيهها إلى تدفق التحقق المخصص استنادًا إلى نوع حمولتها. تستمر الإدخالات العادية المتبقية في عملية التحقق الأصلية من PoH.
الجسر الأصلي هو قطعة أساسية من البنية التحتية لحلول الطبقة 2، مسؤول عن نقل الرسائل بين L1 و L2. تسمى الرسائل من L1 إلى L2 معاملات الإيداع، بينما تسمى الرسائل من L2 إلى L1 معاملات السحب.
تتم معاملات الإيداع عادةً عن طريق أنبوب الاشتقاقويتطلب فقط من المستخدم إرسال معاملة واحدة على ال L1. ومع ذلك، فإن معاملات السحب أكثر تعقيدا وتتطلب من المستخدم إرسال ثلاث معاملات لإكمال العملية:
دليل الاحتواء على عملية السحب يضمن أن السحب حدث على L2، مما يعزز بشكل كبير أمان الجسر الكانوني.
في Stack OP، يتم تخزين تجزئة صفقة سحب المستخدم في المتغير الحالة المقابل لها بوابة التفاؤلالعقد. ثم يتم استخدام واجهة برمجة التطبيقات RPC eth_getProof لتزويد المستخدمين بدليل على ادراج معاملة سحب محددة.
على عكس EVM، يتم تخزين بيانات عقد SVM في حقل البيانات للحسابات، وتوجد جميع أنواع الحسابات على نفس مستوى التسلسل الهرمي.
قدمت SOON برنامج جسر أصلي: Bridge1111111111111111111111111111111111111. كلما قام المستخدم ببدء عملية سحب، يولّد برنامج الجسر فهرس فريد عالميًا لكل عملية سحب ويستخدم هذا الفهرس كبذرة لإنشاء جديدةحساب مشتق من البرنامج(PDA) لتخزين عملية السحب المقابلة.
pub struct WithdrawalTransaction {
/// عداد السحب
pub nonce: U256,
/// المستخدم الذي يرغب في السحب
الناشر المرسل: Pubkey,
/// عنوان المستخدم في L1
الجهاز المستهدف: عنوان،
/// المبلغ المطلوب سحبه بوحدة اللمبورت
قيمة النشر: U256،
حد الغاز في L1
حانة gas_limit: U256,
/// بيانات سحب في L1
بيانات النشر: L1WithdrawalCalldata،
}
قمنا بتعريف هيكل WithdrawalTransaction لتخزين معاملات السحب في حقل البيانات لـ PDA.
يعمل هذا التصميم بطريقة مماثلة لـ VM Stack. بمجرد تقديم outputRoot الذي يحتوي على عملية السحب إلى L1، يمكن للمستخدم تقديم دليل الإدراج لعملية السحب إلى L1 لبدء فترة التحدي.
بعد أن يقدم المقترح outputRoot إلى L1، فإن ذلك يدل على أن حالة L2 قد تم تسويتها. إذا اكتشف المتحدي أن المقترح قد قدم حالة غير صحيحة، فيمكنهم بدء تحدي لحماية الأموال في الجسر.
أحد أهم جوانب بناء نظام مقاوم للأخطاء هو تمكين البلوكشين من الانتقال من الحالة S1 إلى الحالة S2 بطريقة عديمة الحالة. يضمن هذا أن يمكن لعقد الحكم على L1 إعادة تشغيل تعليمات البرنامج بطريقة عديمة الحالة لأداء الفِصل.
ومع ذلك، في بداية هذه العملية، يجب على المتحدي أن يثبت أن جميع الإدخالات الأولية في الحالة S1 صالحة. تشمل هذه الإدخالات الأولية حالات الحسابات المشاركة (على سبيل المثال، لامبورتس، بيانات، مالك، إلخ). على عكس EVM، يفصل SVM حالة الحساب عن الحساب. ومع ذلك، واجهة برمجة تطبيقات Anza’s SVMيسمح بتمرير الحسابات إلى SVM من خلال خاصية TransactionProcessingCallback، كما هو موضح أدناه.
pub trait TransactionProcessingCallback {
fn account_matches_owners(&self, account: &Pubkey, owners: &[Pubkey]) -> Option
fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option
fn add_builtin_account(&self, _name: &str, _program_id: &Pubkey) {}
}
وبالتالي، نحتاج فقط إلى استخدام الحالة S1 إلى جانب دلائل الاحتواء على حسابات الإدخال للتحقق من صحة مدخلات برنامج التحدي.
سون تمثل نقطة محورية في تطوير نظام الـ SVM. من خلال دمج الميركليزيشن، تعالج سون نقص سولانا في الجذر العالمي للحالة، مما يتيح ميزات أساسية مثل دلائل الاحتواء لدلائل الأخطاء، والسحوبات الآمنة، والتنفيذ بدون حالة.
علاوة على ذلك، يمكن أن يمكّن تصميم الإدماج العمودي في SVM الخاص بـ SOON العملاء الخفيفة على سلاسل SVM المستندة إلى، حتى وإن لم تدعم Solana نفسها حاليًا العملاء الخفيفة. حتى من الممكن أن يساعد بعض التصميم في جلب العملاء الخفيفة إلى السلسلة الرئيسية أيضًا.
باستخدام Merkle Patricia Tries (MPT) لإدارة الحالة، يتماشى SOON مع بنية Ethereum، معززًا التوافق وتقدم حلول SVM-based L2. تقوي هذه الابتكارات نظام البيئة SVM من خلال تحسين الأمان والقابلية للتوسيع والتوافق، مع تعزيز نمو التطبيقات اللامركزية.
Поділіться
آلة Solana الافتراضية (SVM) تصبح معتمدة على نطاق واسع كطبقة التنفيذ لحلول Layer-2 (L2) المختلفة. ومع ذلك، القيد الرئيسي في التصميم الأصلي لـ SVM هو غموض جذر حالته العالمي. وهذا يشكل تحديات كبيرة لأنظمة ال Rollup، حيث تكون جذور الحالة العالمية أمرًا حاسمًا لضمان النزاهة وتمكين دلائل الغش ودعم العمليات عبر الطبقات.
في اللف، يقدم الشخص المقترح جذر الحالة L2 (جذر Merkle) إلى الطبقة-1 (L1) بانتظام، وإنشاء نقاط فحص لسلسلة L2. تسمح هذه النقاط الفحص بدلائل الاحتواء على أي حالة حساب، مما يمكن من تنفيذ بدون حالة من نقطة فحص إلى أخرى. تعتمد دلائل الاحتيال على هذا الآلية، حيث يمكن للمشاركين تقديم دلائل الاحتواء للتحقق من الإدخالات الصالحة خلال النزاعات. بالإضافة إلى ذلك، تعزز أشجار Merkle أمان الجسور الكنسية من خلال السماح للمستخدمين بتوليد دلائل الاحتواء على معاملات السحب، مما يضمن التفاعل بدون ثقة بين L2 و L1.
للتعامل مع هذه التحديات،شبكة SOONيقدم أشجار Merkle إلى طبقة تنفيذ SVM، مما يتيح للعملاء تقديم دلائل الاحتواء. يدمج SOON مع دليل السجل بواسطة استخدام إدخالات فريدة لتضمين جذور الحالة مباشرة في سلاسل الكتل القائمة على SVM. من خلال هذا الابتكار، يمكن لكومة SOON دعم تراكمات جديدة قائمة على SVM بأمان وقابلية للتوسيع وفائدة محسنة.
تم تصميم سولانا دائمًا بمعدل نقل عالٍ كهدف أساسي، مما يتطلب تضحية في التصميم العمدي — خاصة خلال مراحل تطويره المبكرة — لتحقيق أداءه الجديد. من بين هذه التضحيات، كانت إحدى القرارات الأكثر تأثيرًا تتمحور حول كيفية ومتى سيلقي سولانا بحالته.
في نهاية المطاف، خلقت هذه القرارات تحديات كبيرة للعملاء في إثبات الحالة العالمية وكذلك إدراج المعاملات والتحقق البسيط من الدفع (SPV). نقص الجذر الحالة المجزأ بشكل متسق الذي يمثل حالة SVM يشكل صعوبة كبيرة لمشاريع مثل العملاء الخفيفة والتجميعات.
لنلقِ نظرة على كيفية إجراء التحويل النضري على سلاسل أخرى ثم تحديد التحديات التي تطرحها هندسة بروتوكول سولانا.
في بيتكوين، يتم تخزين المعاملات في كتلة باستخدامشجرة ميركل, الجذر الذي يتم تخزينه في الرأس الكتلة. سيقوم بروتوكول بيتكوين بتجزئة مدخلات ومخرجات العملية (وكذلك بعض البيانات الوصفية الأخرى) إلى معرف المعاملة(TxID). من أجل إثبات الحالة على بيتكوين، يمكن للمستخدم ببساطة حساب دليل ميركل للتحقق من TxID ضد جذر الميركل للكتلة.
يقوم هذا العملية التحقق أيضًا بتحقق الحالة، حيث يكون معرف المعاملة فريدًا لمجموعة ما من المدخلات والمخرجات، وكلاهما يعكس تغييرات في حالة العنوان. يرجى ملاحظة أن معاملات بيتكوين يمكن أيضًا أن تحتوي علىنصوص تابروت، التي تنتج مخرجات المعاملات التي يمكن التحقق منها أثناء التحقق، غالبًا عن طريق إعادة تشغيل النص باستخدام مدخلات المعاملة وبيانات الشاهد النصي، والتحقق من مخرجاتها.
بالمثل لبيتكوين، تقوم إيثيريوم بتخزين المعاملات باستخدام هيكل بيانات مخصص (مستمد من شجرة ميركل) تسمى شجرة ميركل باتريشيا تراي(MPT). تم تصميم هيكل البيانات هذا للتحديثات السريعة والتحسين على مجموعات بيانات كبيرة. وبطبيعة الحال، يرجع ذلك إلى أن لدى إثيريوم مدخلات ومخرجات أكثر بكثير لإدارتها من بيتكوين.
البوابةآلة العقد الافتراضية لإيثريوم(EVM) يعمل كآلة حالة عالمية. EVM هو في الأساس بيئة حوسبة موزعة ضخمة تدعم التنفيذعقود ذكية، كل منها يحتفظ بمساحة عنوانه الخاصة في الذاكرة العالمية. ونتيجة لذلك، يجب على العملاء الذين يرغبون في التحقق من الحالة على إيثريوم أن يأخذوا في الاعتبار ليس فقط نتيجة للمعاملة (سجلات، رمز العودة، إلخ) ولكن أيضًا التغييرات في الحالة العالمية نتيجة للمعاملة.
لحسن الحظ، يستخدم EVM بذكاء ثلاثة هياكل trie مهمة، التي تخزن جذورها في كل رأس كتلة.
بناءً على عملية تحويل، يمكن للعميل إثبات إدراجها في كتلة عن طريق تقييم جذر شجرة العمليات (مثل Bitcoin)، ونتيجتها من خلال تقييم شجرة الإيصالات، والتغييرات على الحالة العامة من خلال تقييم شجرة الحالة.
أحد الأسباب وراء إنتاجية سولانا العالية هو أنها لا تمتلك الهيكل متعدد الأشجار الذي يمتلكه إثريوم. يقوم قادة سولانا بحساب أشجار ميركل عند إنشاء الكتل، ولكنها مهيأة بشكلٍ مختلف عن تلك الموجودة ضمن EVM. للأسف، هنا تكمن المشكلة بالنسبة للطبقات العلوية القائمة على SVM.
تدرج سولانا المعاملات في ما يُسمى بالإدخالات، ويمكن أن تكون هناك إدخالات متعددة في كل فتحة؛ وبالتالي، جذور معاملات متعددة في كل كتلة. علاوة على ذلك، تحسب سولانا جذرًا ميركليًا لحالة الحساب مرة واحدة فقط في كل حقبة (حوالي 2.5 يوم)، وهذا الجذر غير متوفر على دفتر الأستاذ.
في الواقع، رؤوس كتل Solana لا تحتوي على أي جذور Merkle على الإطلاق. بدلاً من ذلك، تحتوي على السابق والحاليرمز الكتلة, والتي يتم حسابها من خلال سولانا's دليل التاريخخوارزمية (PoH). يتطلب PoH من المحققين تسجيل "نقرات" باستمرار عن طريق تجزئة تكرارية لإدخالات الكتلة، التي يمكن أن تكون فارغة أو تحتوي على دفعات من المعاملات. النقرة النهائية (التجزئة) لخوارزمية PoH هي تجزئة تلك الكتلة.
المشكلة في دليل التاريخ هي أنه يجعل من الصعب جدًا إثبات الحالة من blockhash. تم تصميم Solana لبث تجزئة PoH من أجل الحفاظ على مفهوم الوقت المنقضي الخاص بها. جذر المعاملة متاح فقط لـ tick PoH الذي يحتوي على إدخال بالمعاملات، وليس الكتلة ككل، ولا يوجد جذر حالة مخزن في أي إدخال.
ومع ذلك، هناك تجاوز آخر يمكن للعملاء استخدامه: تجاوز البنك. يطلق عليه أحيانًا تجاوز الفتحة، وتكون تجاوزات البنك متاحة في الSlotHashes sysvarالحساب، الذي يمكن الاستعلام عنه من قبل العميل. يتم إنشاء تجزئة البنك لكل كتلة (فتحة) من مجموعة من المدخلات:
كما يمكن للمرء أن يرى ، فإن تجزئة البنك مثقلة بالعديد من مدخلات التجزئة ، مما يضيف تعقيدا للعملاء الذين يحاولون إثبات معلومات حول المعاملات أو الحالة. بالإضافة إلى ذلك ، فإن تجزئة البنك فقط للبنك الذي أجرى "تجزئة حسابات العصر" - تجزئة جميع الحسابات مرة واحدة لكل حقبة - سيتم تضمين هذا الجذر المحدد فيها. علاوة على ذلك ، يتم اقتطاع حساب SlotHashes sysvar إلى آخر 512 تجزئة بنكية.
شبكة SOONVM على Ethereum. أثناء دمج merklization في القريب العاجل، أعطينا الأولوية لاستخدام الحلول المثبتة والمعتمدة بدلاً من إعادة اختراع العجلة من أجل الاستقرار. في اتخاذ قرار بشأن الهيكل البيانات أو خوارزميات التجزئة لاستخدامها، اعتبرنا توافقها مع عقود L1 لـ تراكم التفاؤل, قدرتهم على التكامل بسلاسة في البنية التحتية لـ Solana، وما إذا كانوا قادرين على تحقيق أداء عالي بموجب نموذج الحسابات الخاص بـ Solana.
توصلنا إلى أنه مع زيادة عدد الحسابات، يعتمد نموذج تخزين Merkle Patricia Trie (MPT) شجرة LSMسيؤدي إلى مزيد من تضاعف قراءة/كتابة القرص، مما يؤدي إلى خسائر في الأداء. في النهاية، قررنا دمج الErigon MPTمن خلال الاعتماد على العمل الممتاز الذي تم إنجازه بواسطة Rust من قبلrETHفريق وإضافة دعم لنموذج حساب سولانا.
كما ذكر سابقا، فإن شجرة حالة SOON هي شجرة MPT مصممة لدعم حسابات Solana. وعلى هذا النحو، قمنا بتحديد نوع حساب متوافق مع SVM ليكون بيانات كل عقدة ورقة.
struct TrieSolanaAccount {
lamports: u64،
البيانات: Vec
قابل للتنفيذ: بول,
rent_epoch: u64،
owner: Pubkey,
}
لتمكين وحدة MPT من الاشتراك في أحدث حالة لحسابات SVM في الوقت الفعلي، قمنا بتقديم موجه الحساب. خلال مرحلة البنكية، يُعلم موجه الحساب وحدة MPT بتغييرات حالة الحساب، ويقوم MPT بتحديث هذه التغييرات تدريجيا في هيكل الشجرة.
من المهم أن نلاحظ أن وحدة MPT تقوم فقط بتحديث أشجارها الفرعية خلال كل 50 فتحة ولا تحسب جذر الحالة في نهاية كل فتحة. يتم اتباع هذا النهج لسببين. أولًا، فإن حساب جذر الحالة لكل فتحة سيؤثر بشكل كبير على الأداء. ثانيًا، الجذر الحالي مطلوب فقط عندما يقدم المقترح الخاص بنظامoutputRootللمرحلة L1. لذلك، يحتاج فقط إلى أن يُحسب بانتظام، استنادًا إلى تردد تقديم المقترح من قبل المقترح.
outputRoot = keccak256(version, state_root, withdraw_root, l2_block_hash)
يحتفظ وحدة MPT بنوعين من هياكل الشجرة معًا: شجرة الحالة للحالة العامة وشجرة السحب لمعاملات السحب. يقوم المقترح بانتظام بتوليد جذر الإخراج عن طريق جذر الحالة وجذر السحب، ويقدمه إلى L1.
حالياً، يقوم SOON بحساب جذر الحالة وجذر السحب مرة كل 450 فتحة ويقوم بإلحاقهما بسلسلة الكتل L2. نتيجة لذلك، يمكن ضمان تماثل بيانات MPT عبر العقد الآخر في الشبكة. ومع ذلك، لا يتضمن هيكل كتلة Solana رأس كتلة، مما يعني أنه لا يوجد مكان لتخزين جذر الحالة. دعنا نلقي نظرة أقرب على الهيكل الأساسي لسلسلة الكتل Solana ثم نستكشف كيف يقدم SOON المدخل الفريد لتخزين جذر الحالة.
يتكون سلسلة كتل سولانا من فتحات، التي تتم إنشاؤها بواسطة وحدة PoH. تحتوي الفتحة على مدخلات متعددة، حيث يتضمن كل مدخل علامات زمنية ومعاملات. في طبقات الشبكة والتخزين، يتم تخزين الفتحة ونقلها باستخدام قصاصاتكوحدة صغرى. يمكن تحويل الشرائح إلى ومن الإدخالات.
pub struct Entry {
عدد عمليات التجزئة منذ معرف الإدخال السابق.
pub num_hashes: u64,
/// تجزئة SHA-256 num_hashes
بعد رقم الإدخال السابق.
pub hash: Hash،
قائمة غير مرتبة بالمعاملات التي تمت ملاحظتها قبل معرف الإدخال
/// تم إنشاؤها. قد يكون قد لوحظت من قبل قبل معرف الإدخال السابق ولكن
/// تم دفعها مرة أخرى إلى هذه القائمة لضمان تفسير محدد للدفتر.
المعاملات العامة: Vec
}
لقد اتبعنا هيكل البلوكشين الذي تم إنشاؤه بواسطة PoH واحتفظنا ببنية الشَّريح، مما يتيح لنا إعادة استخدام طبقة تخزين سولانا الحالية وطبقة الشبكة وإطار عمل RPC. لتخزين البيانات الإضافية على بلوكشين L2، قمنا بإدخال الإدخال المميز. تتيح لنا هذه السمة تخصيص حمولة الإدخال وتحديد قواعد التحقق المستقلة للبيانات.
العمومي ثابت UNIQUE_ENTRY_NUM_HASHES_FLAG: u64 = 0x8000_0000_0000_0000;
الإدخال الفريد هو نوع إدخال خاص. الذي يكون مفيدًا عندما نحتاج إلى تخزين بعض البيانات في
/// blockstore ولكن لا نريد التحقق منها.
///
/// تخطيط num_hashes
is:
/// |…1 بت…|…63 بت…|
/// \ __/
/// \ \
/// حقل مخصص علم
pub السمة UniqueEntry: Sized {
fn encode_to_entries(&self) -> Vec
فك تشفير من الإدخالات(
الإدخالات: تحويل إلى مدخلات<العنصر = إدخال>،
) -> النتيجة
}
pub fn unique_entry(custom_field: u64, hash: Hash) -> Entry {
الدخول {
num_hashes: num_hashes(custom_field), hash, transactions: vec![],
}
}
النشر الوظيفي num_hashes (custom_field: u64) -> u64 {
assert!(custom_field < (1 << 63));
UNIQUE_ENTRY_NUM_HASHES_FLAG | custom_field
}
الحانة فن هو فريد (الدخول: &الدخول) -> بول {
entry.num_hashes & UNIQUE_ENTRY_NUM_HASHES_FLAG != 0
في UniqueEntry، يُستخدم num_hashes كتخطيط بت، حيث يُستخدم علم البت الأول للتمييز بين إدخال ومدخل فريد، ويتم استخدام البتات الـ 63 التالية لتحديد نوع الحمولة. يعمل حقل الهاش كالحمولة، حيث يحتوي على البيانات المخصصة المطلوبة.
دعونا نلقي نظرة على مثال استخدام ثلاث إدخالات فريدة لتخزين تجزئة الهاش، جذر الحالة، وجذر السحب.
/// MPT الجذر المدخل الفريد.
pub struct MptRoot {
فتحة النشر: Slot,
حالة الجذر: B256,
سحب الجذر: B256,
}
impl UniqueEntry for MptRoot {
fn encode_to_entries(&self) -> Vec
دعونا نقوم بإدخال الفتحة = الإدخال الفريد(0 ، الفتحة إلى تجزئة (الفتحة ذاتها)); دعونا نقوم بإدخال جذر الحالة = الإدخال الفريد(1 ، الحالة الجذرية.0.في( )); دعونا نقوم بإدخال جذر السحب = الإدخال الفريد(2 ، الجذر السحب.0.في( )); vec![فتحة الإدخال ، جذر الحالة الإدخال ، جذر السحب الإدخال]
}
فك تشفير من الإدخالات(
الإدخالات: تنفيذ IntoIterator
) -> نتيجة<ذاتي، فريدينتري خطأ> {
دع الإدخالات المتحولة = الإدخالات. إنتو_إيتر(); دع الإدخال يكون الإدخال. التالي(). حسنا_أور(UniqueEntryError::NoMoreEntries)?; دع الفتحة تكون الفتحة. التجزئة_إلى_فتحة(الإدخال. تجزئة); دع الإدخال يكون الإدخال. التالي(). حسنا_أور(UniqueEntryError::NoMoreEntries)?; دع جذر_الحالة يكون B256::from(الإدخال. تجزئة. إلى_بايت()); دع الإدخال يكون الإدخال. التالي(). حسنا_أور(UniqueEntryError::NoMoreEntries)?; دع جذر_الانسحاب يكون B256::from(الإدخال. تجزئة. إلى_بايت()); حسنا(MptRoot { الفتحة، جذر_الحالة، جذر_الانسحاب، })
}
}
نظرًا لأن UniqueEntry يعيد تعريف دلالة num_hashes، فلا يمكن معالجته أثناء مرحلة التحقق من PoH. لذلك، في بداية عملية التحقق، نقوم أولاً بتصفية الإدخالات الفريدة وتوجيهها إلى تدفق التحقق المخصص استنادًا إلى نوع حمولتها. تستمر الإدخالات العادية المتبقية في عملية التحقق الأصلية من PoH.
الجسر الأصلي هو قطعة أساسية من البنية التحتية لحلول الطبقة 2، مسؤول عن نقل الرسائل بين L1 و L2. تسمى الرسائل من L1 إلى L2 معاملات الإيداع، بينما تسمى الرسائل من L2 إلى L1 معاملات السحب.
تتم معاملات الإيداع عادةً عن طريق أنبوب الاشتقاقويتطلب فقط من المستخدم إرسال معاملة واحدة على ال L1. ومع ذلك، فإن معاملات السحب أكثر تعقيدا وتتطلب من المستخدم إرسال ثلاث معاملات لإكمال العملية:
دليل الاحتواء على عملية السحب يضمن أن السحب حدث على L2، مما يعزز بشكل كبير أمان الجسر الكانوني.
في Stack OP، يتم تخزين تجزئة صفقة سحب المستخدم في المتغير الحالة المقابل لها بوابة التفاؤلالعقد. ثم يتم استخدام واجهة برمجة التطبيقات RPC eth_getProof لتزويد المستخدمين بدليل على ادراج معاملة سحب محددة.
على عكس EVM، يتم تخزين بيانات عقد SVM في حقل البيانات للحسابات، وتوجد جميع أنواع الحسابات على نفس مستوى التسلسل الهرمي.
قدمت SOON برنامج جسر أصلي: Bridge1111111111111111111111111111111111111. كلما قام المستخدم ببدء عملية سحب، يولّد برنامج الجسر فهرس فريد عالميًا لكل عملية سحب ويستخدم هذا الفهرس كبذرة لإنشاء جديدةحساب مشتق من البرنامج(PDA) لتخزين عملية السحب المقابلة.
pub struct WithdrawalTransaction {
/// عداد السحب
pub nonce: U256,
/// المستخدم الذي يرغب في السحب
الناشر المرسل: Pubkey,
/// عنوان المستخدم في L1
الجهاز المستهدف: عنوان،
/// المبلغ المطلوب سحبه بوحدة اللمبورت
قيمة النشر: U256،
حد الغاز في L1
حانة gas_limit: U256,
/// بيانات سحب في L1
بيانات النشر: L1WithdrawalCalldata،
}
قمنا بتعريف هيكل WithdrawalTransaction لتخزين معاملات السحب في حقل البيانات لـ PDA.
يعمل هذا التصميم بطريقة مماثلة لـ VM Stack. بمجرد تقديم outputRoot الذي يحتوي على عملية السحب إلى L1، يمكن للمستخدم تقديم دليل الإدراج لعملية السحب إلى L1 لبدء فترة التحدي.
بعد أن يقدم المقترح outputRoot إلى L1، فإن ذلك يدل على أن حالة L2 قد تم تسويتها. إذا اكتشف المتحدي أن المقترح قد قدم حالة غير صحيحة، فيمكنهم بدء تحدي لحماية الأموال في الجسر.
أحد أهم جوانب بناء نظام مقاوم للأخطاء هو تمكين البلوكشين من الانتقال من الحالة S1 إلى الحالة S2 بطريقة عديمة الحالة. يضمن هذا أن يمكن لعقد الحكم على L1 إعادة تشغيل تعليمات البرنامج بطريقة عديمة الحالة لأداء الفِصل.
ومع ذلك، في بداية هذه العملية، يجب على المتحدي أن يثبت أن جميع الإدخالات الأولية في الحالة S1 صالحة. تشمل هذه الإدخالات الأولية حالات الحسابات المشاركة (على سبيل المثال، لامبورتس، بيانات، مالك، إلخ). على عكس EVM، يفصل SVM حالة الحساب عن الحساب. ومع ذلك، واجهة برمجة تطبيقات Anza’s SVMيسمح بتمرير الحسابات إلى SVM من خلال خاصية TransactionProcessingCallback، كما هو موضح أدناه.
pub trait TransactionProcessingCallback {
fn account_matches_owners(&self, account: &Pubkey, owners: &[Pubkey]) -> Option
fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option
fn add_builtin_account(&self, _name: &str, _program_id: &Pubkey) {}
}
وبالتالي، نحتاج فقط إلى استخدام الحالة S1 إلى جانب دلائل الاحتواء على حسابات الإدخال للتحقق من صحة مدخلات برنامج التحدي.
سون تمثل نقطة محورية في تطوير نظام الـ SVM. من خلال دمج الميركليزيشن، تعالج سون نقص سولانا في الجذر العالمي للحالة، مما يتيح ميزات أساسية مثل دلائل الاحتواء لدلائل الأخطاء، والسحوبات الآمنة، والتنفيذ بدون حالة.
علاوة على ذلك، يمكن أن يمكّن تصميم الإدماج العمودي في SVM الخاص بـ SOON العملاء الخفيفة على سلاسل SVM المستندة إلى، حتى وإن لم تدعم Solana نفسها حاليًا العملاء الخفيفة. حتى من الممكن أن يساعد بعض التصميم في جلب العملاء الخفيفة إلى السلسلة الرئيسية أيضًا.
باستخدام Merkle Patricia Tries (MPT) لإدارة الحالة، يتماشى SOON مع بنية Ethereum، معززًا التوافق وتقدم حلول SVM-based L2. تقوي هذه الابتكارات نظام البيئة SVM من خلال تحسين الأمان والقابلية للتوسيع والتوافق، مع تعزيز نمو التطبيقات اللامركزية.