ربما سمعت ذلك Telegram . لكن ربما فاتتك الأخبار التي نشرتها Telegram منذ وقت ليس ببعيد لتنفيذ واحد أو أكثر من العقود الذكية لهذه المنصة.
لا يمكن لفريق Serokell، الذي يتمتع بخبرة واسعة في تطوير مشاريع blockchain الكبيرة، أن يقف جانبًا. قمنا بتفويض خمسة موظفين للمسابقة، وبعد أسبوعين حصلوا على المركز الأول فيها تحت اللقب العشوائي المتواضع مثير الحرباء. في هذه المقالة سأتحدث عن كيف فعلوا ذلك. نأمل أن تقرأ في الدقائق العشر القادمة قصة مثيرة للاهتمام على الأقل، وستجد فيها على الأكثر شيئًا مفيدًا يمكنك تطبيقه في عملك.
ولكن لنبدأ مع القليل من السياق.
المنافسة وشروطها
لذا، كانت المهام الرئيسية للمشاركين هي تنفيذ واحد أو أكثر من العقود الذكية المقترحة، بالإضافة إلى تقديم مقترحات لتحسين نظام TON البيئي. واستمرت المسابقة في الفترة من 24 سبتمبر إلى 15 أكتوبر، وتم إعلان النتائج في 15 نوفمبر فقط. منذ وقت طويل، مع الأخذ في الاعتبار أنه خلال هذا الوقت تمكنت Telegram من إجراء وإعلان نتائج المسابقات حول تصميم وتطوير التطبيقات في C++ لاختبار وتقييم جودة مكالمات VoIP في Telegram.
لقد اخترنا عقدين ذكيين من القائمة التي اقترحها المنظمون. بالنسبة لأحدهما، استخدمنا الأدوات الموزعة باستخدام TON، وتم تنفيذ الثاني بلغة جديدة طورها مهندسونا خصيصًا لـ TON وتم دمجها في Haskell.
اختيار لغة البرمجة الوظيفية ليس من قبيل الصدفة. في لدينا غالبًا ما نتحدث عن سبب اعتقادنا أن تعقيد اللغات الوظيفية يعد مبالغة كبيرة ولماذا نفضلها عمومًا على اللغات الموجهة للكائنات. بالمناسبة، فهو يحتوي أيضًا على .
لماذا قررنا المشاركة؟
باختصار، لأن تخصصنا عبارة عن مشاريع غير قياسية ومعقدة تتطلب مهارات خاصة وغالبًا ما تكون ذات قيمة علمية لمجتمع تكنولوجيا المعلومات. نحن ندعم بقوة تطوير المصادر المفتوحة ونعمل على نشرها ونتعاون أيضًا مع الجامعات الروسية الرائدة في مجال علوم الكمبيوتر والرياضيات.
كانت المهام المثيرة للاهتمام للمسابقة والمشاركة في مشروع Telegram المحبوب لدينا في حد ذاتها حافزًا ممتازًا، لكن صندوق الجائزة أصبح حافزًا إضافيًا. 🙂
أبحاث TON blockchain
نحن نراقب عن كثب التطورات الجديدة في مجال blockchain والذكاء الاصطناعي والتعلم الآلي ونحاول ألا نفوت أي إصدار مهم في كل مجال من المجالات التي نعمل فيها. لذلك، بحلول الوقت الذي بدأت فيه المنافسة، كان فريقنا على دراية بالأفكار الموجودة بالفعل . ومع ذلك، قبل البدء في العمل مع TON، لم نحلل الوثائق الفنية وكود المصدر الفعلي للمنصة، لذلك كانت الخطوة الأولى واضحة تمامًا - دراسة شاملة للوثائق الرسمية على و .
بحلول الوقت الذي بدأت فيه المسابقة، كان الكود قد تم نشره بالفعل، لذا لتوفير الوقت، قررنا البحث عن دليل أو ملخص مكتوب بواسطة المستخدمين. لسوء الحظ، لم يعط هذا أي نتائج - بصرف النظر عن التعليمات الخاصة بتجميع النظام الأساسي على Ubuntu، لم نجد أي مواد أخرى.
وقد تم بحث الوثائق نفسها جيدًا، ولكن كان من الصعب قراءتها في بعض المناطق. في كثير من الأحيان كان علينا العودة إلى نقاط معينة والتحول من الأوصاف عالية المستوى للأفكار المجردة إلى تفاصيل التنفيذ ذات المستوى المنخفض.
سيكون الأمر أسهل إذا لم تتضمن المواصفات وصفًا تفصيليًا للتنفيذ على الإطلاق. من المرجح أن تؤدي المعلومات حول كيفية تمثيل الجهاز الظاهري لمكدسه إلى تشتيت انتباه المطورين الذين يقومون بإنشاء عقود ذكية لمنصة TON بدلاً من مساعدتهم.
نيكس: تجميع المشروع معًا
في Serokell نحن معجبين كبار . نجمع مشاريعنا معها وننشرها باستخدام ، وتثبيتها على جميع خوادمنا . بفضل هذا، جميع إصداراتنا قابلة للتكرار وتعمل على أي نظام تشغيل يمكن تثبيت Nix عليه.
لذلك بدأنا بالإنشاء . وبمساعدتها، يصبح تجميع TON بسيطًا قدر الإمكان:
$ cd ~/.config/nixpkgs/overlays && git clone https://github.com/serokell/ton.nix
$ cd /path/to/ton/repo && nix-shell
[nix-shell]$ cmakeConfigurePhase && makeلاحظ أنك لا تحتاج إلى تثبيت أي تبعيات. سوف يقوم Nix بكل شيء نيابةً عنك بطريقة سحرية، سواء كنت تستخدم NixOS أو Ubuntu أو macOS.
البرمجة للطن
يعمل رمز العقد الذكي في شبكة TON على جهاز TON الظاهري (TVM). يعد TVM أكثر تعقيدًا من معظم الأجهزة الافتراضية الأخرى، وله وظائف مثيرة جدًا للاهتمام، على سبيل المثال، يمكنه العمل معها استمرار и روابط للبيانات.
علاوة على ذلك، قام فريق TON بإنشاء ثلاث لغات برمجة جديدة:
فيفت هي لغة برمجة مكدسة عالمية تشبه . قدرته الفائقة هي القدرة على التفاعل مع TVM.
FunC هي لغة برمجة العقود الذكية التي تشبه ويتم تجميعها إلى لغة أخرى - Five Assembler.
المجمع الخامس - المكتبة الخامسة لتوليد كود ثنائي قابل للتنفيذ لـ TVM. لا يحتوي المجمع الخامس على مترجم. هذا .
منافستنا تعمل
وأخيرا، حان الوقت للنظر في نتائج جهودنا.
قناة الدفع غير المتزامنة
قناة الدفع عبارة عن عقد ذكي يسمح لمستخدمين بإرسال مدفوعات خارج blockchain. ونتيجة لذلك، لا يمكنك توفير المال فقط (لا توجد عمولة)، ولكن أيضًا الوقت (لا يتعين عليك الانتظار حتى تتم معالجة الكتلة التالية). يمكن أن تكون المدفوعات صغيرة حسب الرغبة وكلما كان ذلك مطلوبًا. في هذه الحالة، ليس من الضروري أن يثق الطرفان ببعضهما البعض، حيث يتم ضمان عدالة التسوية النهائية من خلال العقد الذكي.
لقد وجدنا حلاً بسيطًا إلى حد ما لهذه المشكلة. يمكن لطرفين تبادل الرسائل الموقعة، كل منها يحتوي على رقمين – المبلغ الكامل الذي يدفعه كل طرف. يعمل هذان الرقمان مثل في الأنظمة الموزعة التقليدية وتعيين ترتيب "حدث من قبل" على المعاملات. وباستخدام هذه البيانات، سيكون العقد قادرًا على حل أي تعارض محتمل.
في الواقع، رقم واحد يكفي لتنفيذ هذه الفكرة، لكننا تركنا كليهما لأنه بهذه الطريقة يمكننا إنشاء واجهة مستخدم أكثر ملاءمة. بالإضافة إلى ذلك، قررنا تضمين مبلغ الدفع في كل رسالة. بدونها، إذا فقدت الرسالة لسبب ما، فرغم أن جميع المبالغ والحساب النهائي ستكون صحيحة، فقد لا يلاحظ المستخدم الخسارة.
لاختبار فكرتنا، بحثنا عن أمثلة لاستخدام بروتوكول قناة الدفع البسيط والموجز. والمثير للدهشة أننا وجدنا اثنين فقط:
- نهج مماثل، فقط في حالة القناة أحادية الاتجاه.
- ، والتي تصف نفس فكرتنا، ولكن دون شرح العديد من التفاصيل المهمة، مثل الصحة العامة وإجراءات حل النزاعات.
أصبح من الواضح أنه من المنطقي وصف بروتوكولنا بالتفصيل، مع إيلاء اهتمام خاص لصحته. وبعد عدة تكرارات، أصبحت المواصفات جاهزة، والآن يمكنك ذلك أيضًا. .
قمنا بتنفيذ العقد في FunC، وقمنا بكتابة أداة سطر الأوامر للتفاعل مع عقدنا بالكامل في Fift، كما أوصى المنظمون. كان بإمكاننا اختيار أي لغة أخرى لواجهة سطر الأوامر (CLI)، لكننا كنا مهتمين بتجربة تطبيق Fit لمعرفة مدى أدائه عمليًا.
بصراحة، بعد العمل مع Fift، لم نرى أي أسباب مقنعة لتفضيل هذه اللغة على اللغات الشائعة والمستخدمة بشكل نشط مع الأدوات والمكتبات المتطورة. البرمجة بلغة مكدسة غير سارة للغاية، حيث يتعين عليك الاحتفاظ باستمرار في رأسك بما هو موجود في المكدس، ولا يساعد المترجم في ذلك.
لذلك، في رأينا، المبرر الوحيد لوجود Fift هو دورها كلغة مضيفة لـ Fift Assembler. لكن أليس من الأفضل دمج مجمع TVM في بعض اللغات الموجودة، بدلاً من اختراع لغة جديدة لهذا الغرض الوحيد بشكل أساسي؟
TVM هاسكل eDSL
حان الوقت الآن للحديث عن عقدنا الذكي الثاني. قررنا تطوير محفظة متعددة التوقيعات، لكن كتابة عقد ذكي آخر في FunC سيكون مملًا للغاية. أردنا إضافة بعض النكهة، وكانت تلك هي لغة التجميع الخاصة بنا لـ TVM.
مثل Fift Assembler، تم تضمين لغتنا الجديدة، لكننا اخترنا Haskell كمضيف بدلاً من Fift، مما يسمح لنا بالاستفادة الكاملة من نظام الكتابة المتقدم الخاص بها. عند العمل مع العقود الذكية، حيث يمكن أن تكون تكلفة الخطأ البسيط مرتفعة للغاية، فإن الكتابة الثابتة، في رأينا، تعد ميزة كبيرة.
لتوضيح الشكل الذي يبدو عليه مجمع TVM المضمن في Haskell، قمنا بتطبيق محفظة قياسية عليه. فيما يلي بعض الأشياء التي يجب الانتباه إليها:
- يتكون هذا العقد من وظيفة واحدة، ولكن يمكنك استخدام أي عدد تريده. عندما تحدد وظيفة جديدة في اللغة المضيفة (أي هاسكل)، يتيح لك eDSL الخاص بنا اختيار ما إذا كنت تريد أن تصبح روتينًا منفصلاً في TVM أو ببساطة مضمنة عند نقطة الاتصال.
- مثل هاسكل، تحتوي الوظائف على أنواع يتم فحصها في وقت الترجمة. في eDSL الخاص بنا، نوع الإدخال للوظيفة هو نوع المكدس الذي تتوقعه الوظيفة، ونوع النتيجة هو نوع المكدس الذي سيتم إنتاجه بعد المكالمة.
- يحتوي الكود على تعليقات توضيحية
stacktype، واصفًا نوع المكدس المتوقع عند نقطة الاتصال. في عقد المحفظة الأصلي، كانت هذه مجرد تعليقات، ولكن في eDSL الخاصة بنا كانت في الواقع جزءًا من الكود ويتم فحصها في وقت التجميع. يمكن أن تكون بمثابة وثائق أو بيانات تساعد المطور في العثور على المشكلة إذا تغير الكود وتغير نوع المكدس. بالطبع، لا تؤثر مثل هذه التعليقات التوضيحية على أداء وقت التشغيل، نظرًا لعدم إنشاء كود TVM لها. - لا يزال هذا نموذجًا أوليًا تمت كتابته خلال أسبوعين، لذلك لا يزال هناك الكثير من العمل الذي يتعين القيام به في المشروع. على سبيل المثال، يجب إنشاء كافة مثيلات الفئات التي تراها في الكود أدناه تلقائيًا.
هذا هو الشكل الذي يبدو عليه تنفيذ محفظة multisig على خدمة eDSL الخاصة بنا:
main :: IO ()
main = putText $ pretty $ declProgram procedures methods
where
procedures =
[ ("recv_external", decl recvExternal)
, ("recv_internal", decl recvInternal)
]
methods =
[ ("seqno", declMethod getSeqno)
]
data Storage = Storage
{ sCnt :: Word32
, sPubKey :: PublicKey
}
instance DecodeSlice Storage where
type DecodeSliceFields Storage = [PublicKey, Word32]
decodeFromSliceImpl = do
decodeFromSliceImpl @Word32
decodeFromSliceImpl @PublicKey
instance EncodeBuilder Storage where
encodeToBuilder = do
encodeToBuilder @Word32
encodeToBuilder @PublicKey
data WalletError
= SeqNoMismatch
| SignatureMismatch
deriving (Eq, Ord, Show, Generic)
instance Exception WalletError
instance Enum WalletError where
toEnum 33 = SeqNoMismatch
toEnum 34 = SignatureMismatch
toEnum _ = error "Uknown MultiSigError id"
fromEnum SeqNoMismatch = 33
fromEnum SignatureMismatch = 34
recvInternal :: '[Slice] :-> '[]
recvInternal = drop
recvExternal :: '[Slice] :-> '[]
recvExternal = do
decodeFromSlice @Signature
dup
preloadFromSlice @Word32
stacktype @[Word32, Slice, Signature]
-- cnt cs sign
pushRoot
decodeFromCell @Storage
stacktype @[PublicKey, Word32, Word32, Slice, Signature]
-- pk cnt' cnt cs sign
xcpu @1 @2
stacktype @[Word32, Word32, PublicKey, Word32, Slice, Signature]
-- cnt cnt' pk cnt cs sign
equalInt >> throwIfNot SeqNoMismatch
push @2
sliceHash
stacktype @[Hash Slice, PublicKey, Word32, Slice, Signature]
-- hash pk cnt cs sign
xc2pu @0 @4 @4
stacktype @[PublicKey, Signature, Hash Slice, Word32, Slice, PublicKey]
-- pubk sign hash cnt cs pubk
chkSignU
stacktype @[Bool, Word32, Slice, PublicKey]
-- ? cnt cs pubk
throwIfNot SignatureMismatch
accept
swap
decodeFromSlice @Word32
nip
dup
srefs @Word8
pushInt 0
if IsEq
then ignore
else do
decodeFromSlice @Word8
decodeFromSlice @(Cell MessageObject)
stacktype @[Slice, Cell MessageObject, Word8, Word32, PublicKey]
xchg @2
sendRawMsg
stacktype @[Slice, Word32, PublicKey]
endS
inc
encodeToCell @Storage
popRoot
getSeqno :: '[] :-> '[Word32]
getSeqno = do
pushRoot
cToS
preloadFromSlice @Word32يمكن العثور على كود المصدر الكامل لعقد eDSL والمحفظة متعددة التوقيع على الموقع و اكثر حول اللغات المدمجة، زميلنا جورجي أجابوف.
استنتاجات حول المنافسة وTON
في المجمل، استغرق عملنا 380 ساعة (بما في ذلك التعرف على الوثائق والاجتماعات والتطوير الفعلي). شارك خمسة مطورين في مشروع المسابقة: CTO، قائد الفريق، متخصصو منصة blockchain ومطورو برامج Haskell.
لقد وجدنا موارد للمشاركة في المسابقة دون صعوبة، نظرًا لأن روح الهاكاثون والعمل الجماعي الوثيق والحاجة إلى الانغماس بسرعة في جوانب التقنيات الجديدة أمر مثير دائمًا. يتم تعويض العديد من الليالي الطوال لتحقيق أقصى قدر من النتائج في ظروف الموارد المحدودة بخبرة لا تقدر بثمن وذكريات ممتازة. بالإضافة إلى ذلك، يعد العمل في مثل هذه المهام دائمًا اختبارًا جيدًا لعمليات الشركة، حيث أنه من الصعب للغاية تحقيق نتائج لائقة حقًا دون تفاعل داخلي يعمل بشكل جيد.
كلمات الأغاني جانبًا: لقد تأثرنا بحجم العمل الذي قام به فريق TON. لقد تمكنوا من بناء نظام عمل معقد وجميل والأهم من ذلك. أثبتت TON أنها منصة ذات إمكانات كبيرة. ومع ذلك، من أجل تطوير هذا النظام البيئي، هناك الكثير الذي يتعين القيام به، سواء من حيث استخدامه في مشاريع blockchain أو من حيث تحسين أدوات التطوير. ونحن فخورون بأن نكون الآن جزءًا من هذه العملية.
إذا كان لا يزال لديك أي أسئلة أو لديك أفكار بعد قراءة هذا المقال حول كيفية استخدام TON لحل مشاكلك، - سنكون سعداء بمشاركة تجربتنا.
المصدر: www.habr.com
