تبدیل FunC به FunCtional با Haskell: چگونه سروکل در رقابت بلاک چین برنده شد

حتما این تلگرام را شنیده اید در شرف راه اندازی پلتفرم بلاک چین Ton است. اما شاید اخبار مربوط به چندی پیش تلگرام را از دست داده باشید مسابقه اعلام کرد برای اجرای یک یا چند قرارداد هوشمند برای این پلتفرم.

تیم سروکل، با تجربه گسترده در توسعه پروژه های بزرگ بلاک چین، نمی توانست کنار بماند. ما پنج کارمند را به مسابقه اعزام کردیم و دو هفته بعد آنها با نام مستعار تصادفی (غیر) تصادفی آفتاب پرست سکسی مقام اول را در آن کسب کردند. در این مقاله در مورد نحوه انجام این کار صحبت خواهم کرد. امیدواریم در ده دقیقه آینده حداقل یک داستان جالب بخوانید و حداکثر مطلب مفیدی در آن پیدا کنید که بتوانید در کار خود به کار ببرید.

اما اجازه دهید با کمی زمینه شروع کنیم.

رقابت و شرایط آن

بنابراین، وظایف اصلی شرکت کنندگان اجرای یک یا چند قرارداد هوشمند پیشنهادی و همچنین ارائه پیشنهادات برای بهبود اکوسیستم TON بود. این مسابقه از 24 سپتامبر تا 15 اکتبر برگزار شد و نتایج فقط در 15 نوامبر اعلام شد. با توجه به اینکه تلگرام در این مدت موفق به برگزاری و اعلام نتایج مسابقات طراحی و توسعه اپلیکیشن به زبان C++ برای تست و ارزیابی کیفیت تماس های VoIP در تلگرام شده است.

ما دو قرارداد هوشمند را از لیست پیشنهادی برگزارکنندگان انتخاب کردیم. برای یکی از آنها، از ابزارهای توزیع شده با TON استفاده کردیم، و دومی به زبان جدیدی که توسط مهندسان ما به طور خاص برای TON توسعه یافته و در Haskell ساخته شده است، پیاده سازی شد.

انتخاب یک زبان برنامه نویسی کاربردی تصادفی نیست. در ما وبلاگ شرکتی ما اغلب در مورد این صحبت می کنیم که چرا فکر می کنیم پیچیدگی زبان های کاربردی یک اغراق بزرگ است و چرا آنها را به طور کلی به زبان های شی گرا ترجیح می دهیم. به هر حال، آن را نیز شامل می شود اصل این مقاله.

اصلا چرا تصمیم گرفتیم شرکت کنیم؟

به طور خلاصه، زیرا تخصص ما پروژه های غیر استاندارد و پیچیده ای است که نیاز به مهارت های خاصی دارد و اغلب برای جامعه فناوری اطلاعات ارزش علمی دارد. ما به شدت از توسعه منبع باز حمایت می کنیم و درگیر محبوبیت آن هستیم و همچنین با دانشگاه های پیشرو روسیه در زمینه علوم کامپیوتر و ریاضیات همکاری می کنیم.

کارهای جالب مسابقه و مشارکت در پروژه تلگرامی عزیزمان به خودی خود انگیزه بسیار خوبی بود، اما صندوق جایزه یک مشوق اضافی شد. 🙂

تحقیق بلاک چین TON

ما پیشرفت‌های جدید در بلاک چین، هوش مصنوعی و یادگیری ماشین را به دقت رصد می‌کنیم و سعی می‌کنیم یک نسخه قابل توجه را در هر یک از حوزه‌هایی که در آن کار می‌کنیم از دست ندهیم. بنابراین، تا زمان شروع مسابقه، تیم ما از قبل با ایده هایی از آن آشنا شده بود کاغذ سفید TON. با این حال، قبل از شروع کار با TON، ما اسناد فنی و کد منبع واقعی پلت فرم را تجزیه و تحلیل نکردیم، بنابراین اولین قدم کاملا واضح بود - مطالعه کامل اسناد رسمی در مورد کاربران آنلاین حاضر در سایت " و مخازن پروژه.

تا زمان شروع مسابقه، کد قبلا منتشر شده بود، بنابراین برای صرفه جویی در زمان، تصمیم گرفتیم به دنبال یک راهنما یا خلاصه ای بگردیم که توسط کاربران. متأسفانه، این هیچ نتیجه ای نداشت - به غیر از دستورالعمل های مونتاژ پلت فرم در اوبونتو، ما مواد دیگری پیدا نکردیم.

خود مستندات به خوبی مورد تحقیق قرار گرفت، اما خواندن آن در برخی زمینه ها دشوار بود. غالباً مجبور بودیم به نقاط خاصی برگردیم و از توضیحات سطح بالای ایده‌های انتزاعی به جزئیات پیاده‌سازی سطح پایین برویم.

اگر مشخصات اصلاً شامل شرح مفصلی از پیاده سازی نباشد، آسان تر خواهد بود. اطلاعات در مورد اینکه چگونه یک ماشین مجازی پشته خود را نشان می دهد به احتمال زیاد حواس توسعه دهندگان ایجاد قراردادهای هوشمند را برای پلت فرم TON منحرف می کند تا کمک به آنها.

نیکس: قرار دادن پروژه در کنار هم

در سروکل ما طرفداران بزرگی هستیم نیکس. ما پروژه های خود را با آن جمع آوری می کنیم و آنها را با استفاده از آن مستقر می کنیم NixOps، و بر روی تمام سرورهای ما نصب شده است سیستم عامل Nix. به لطف این، تمام ساخت‌های ما قابل تکرار هستند و روی هر سیستم عاملی که Nix می‌تواند روی آن نصب شود کار می‌کند.

بنابراین ما با ایجاد شروع کردیم پوشش Nix با بیان برای مونتاژ TON. با کمک آن، کامپایل 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، چه اوبونتو یا macOS استفاده کنید.

برنامه نویسی برای TON

کد قرارداد هوشمند در شبکه TON بر روی ماشین مجازی TON (TVM) اجرا می شود. TVM از اکثر ماشین های مجازی دیگر پیچیده تر است و عملکرد بسیار جالبی دارد، به عنوان مثال می تواند با آن کار کند ادامه и پیوند به داده ها.

علاوه بر این، بچه های TON سه زبان برنامه نویسی جدید ایجاد کردند:

پنج یک زبان برنامه نویسی پشته جهانی است که شبیه است چهارم. توانایی فوق العاده او توانایی تعامل با TVM است.

FunC یک زبان برنامه نویسی قرارداد هوشمند است که شبیه به C و به زبان دیگری - Fift Assembler - گردآوری شده است.

مونتاژ کننده پنجم - کتابخانه پنجم برای تولید کدهای اجرایی باینری برای TVM. اسمبلر پنجم کامپایلر ندارد. این زبان اختصاصی دامنه جاسازی شده (eDSL).

رقابت ما کار می کند

در نهایت، زمان آن رسیده است که به نتایج تلاش های خود نگاه کنیم.

کانال پرداخت ناهمزمان

کانال پرداخت یک قرارداد هوشمند است که به دو کاربر اجازه می‌دهد پرداخت‌های خود را خارج از بلاک چین ارسال کنند. در نتیجه، نه تنها در پول صرفه جویی می کنید (هیچ کمیسیون وجود ندارد)، بلکه در زمان نیز صرفه جویی می کنید (لازم نیست منتظر بمانید تا بلوک بعدی پردازش شود). پرداخت ها می تواند به اندازه دلخواه و به تعداد دفعات مورد نیاز باشد. در این حالت، طرفین مجبور نیستند به یکدیگر اعتماد کنند، زیرا عادلانه بودن تسویه حساب نهایی توسط قرارداد هوشمند تضمین می شود.

ما یک راه حل نسبتا ساده برای مشکل پیدا کردیم. دو طرف می توانند پیام های امضا شده ای را مبادله کنند که هر کدام شامل دو شماره است - مبلغ کامل پرداخت شده توسط هر طرف. این دو عدد مانند کار می کنند ساعت برداری در سیستم های توزیع شده سنتی و ترتیب "قبل از وقوع" را در معاملات تنظیم کنید. با استفاده از این داده ها، قرارداد قادر خواهد بود تا هرگونه تعارض احتمالی را حل کند.

در واقع یک عدد برای اجرای این ایده کافی است، اما ما هر دو را رها کردیم زیرا به این ترتیب می‌توانستیم رابط کاربری راحت‌تری ایجاد کنیم. علاوه بر این تصمیم گرفتیم در هر پیام مبلغ پرداختی را لحاظ کنیم. بدون آن، اگر پیام به دلایلی از بین برود، اگر چه تمام مبالغ و محاسبه نهایی صحیح خواهد بود، کاربر ممکن است متوجه ضرر نشود.

برای آزمایش ایده خود، نمونه هایی از استفاده از چنین پروتکل کانال پرداخت ساده و مختصری را جستجو کردیم. با کمال تعجب، ما فقط دو مورد را پیدا کردیم:

  1. شرح یک رویکرد مشابه، فقط برای مورد کانال یک طرفه.
  2. آموزش، که همان ایده ما را توصیف می کند، اما بدون توضیح بسیاری از جزئیات مهم، مانند صحت کلی و روش های حل تعارض.

مشخص شد که منطقی است که پروتکل خود را با جزئیات توصیف کنیم و به صحت آن توجه ویژه ای داشته باشیم. پس از چندین بار تکرار، مشخصات آماده شد و اکنون شما نیز می توانید. به آن دختر نگاه کن.

ما این قرارداد را در FunC پیاده‌سازی کردیم و ابزار خط فرمان را برای تعامل با قراردادمان به طور کامل در Fift نوشتیم، همانطور که برگزارکنندگان توصیه کردند. ما می توانستیم هر زبان دیگری را برای CLI خود انتخاب کنیم، اما علاقه مند بودیم Fit را امتحان کنیم تا ببینیم در عمل چگونه عمل می کند.

صادقانه بگویم، پس از کار با Fift، هیچ دلیل قانع‌کننده‌ای برای ترجیح این زبان به زبان‌های محبوب و فعال با ابزارها و کتابخانه‌های توسعه‌یافته ندیدیم. برنامه نویسی در یک زبان مبتنی بر پشته بسیار ناخوشایند است، زیرا شما باید دائماً آنچه را در پشته است در ذهن خود نگه دارید و کامپایلر در این مورد کمکی نمی کند.

بنابراین، به نظر ما، تنها توجیه وجود Fift، نقش آن به عنوان یک زبان میزبان برای Fift Assembler است. اما آیا بهتر نیست اسمبلر TVM را در برخی از زبان های موجود جاسازی کنیم، به جای اختراع یک اسمبلر جدید برای این هدف اساسا؟

TVM Haskell eDSL

اکنون زمان آن است که در مورد دومین قرارداد هوشمند خود صحبت کنیم. ما تصمیم گرفتیم یک کیف پول چند امضایی توسعه دهیم، اما نوشتن یک قرارداد هوشمند دیگر در FunC خیلی کسل کننده خواهد بود. ما می خواستیم کمی طعم اضافه کنیم و این زبان اسمبلی خودمان برای TVM بود.

مانند Fift Assembler، زبان جدید ما تعبیه شده است، اما ما Haskell را به‌عنوان میزبان به جای Fift انتخاب کردیم و به ما این امکان را می‌دهد که از سیستم نوع پیشرفته آن استفاده کامل کنیم. هنگام کار با قراردادهای هوشمند، جایی که هزینه یک خطای کوچک می تواند بسیار بالا باشد، به نظر ما تایپ استاتیک یک مزیت بزرگ است.

برای نشان دادن ظاهر اسمبلر TVM که در Haskell تعبیه شده است، یک کیف پول استاندارد را روی آن پیاده سازی کردیم. در اینجا چند نکته وجود دارد که باید به آنها توجه کرد:

  • این قرارداد از یک تابع تشکیل شده است، اما شما می توانید هر تعداد که دوست دارید استفاده کنید. هنگامی که یک تابع جدید را در زبان میزبان تعریف می‌کنید (یعنی Haskell)، eDSL ما به شما اجازه می‌دهد انتخاب کنید که آیا می‌خواهید به یک روال جداگانه در TVM تبدیل شود یا به سادگی در نقطه تماس خط‌بندی شود.
  • مانند Haskell، توابع دارای انواعی هستند که در زمان کامپایل بررسی می شوند. در 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، سرپرست تیم، متخصصان پلتفرم بلاک چین و توسعه دهندگان نرم افزار Haskell.

ما منابعی برای شرکت در مسابقه بدون مشکل پیدا کردیم، زیرا روحیه هکاتون، کار گروهی نزدیک و نیاز به غوطه ور شدن سریع در جنبه های فناوری های جدید همیشه هیجان انگیز است. چندین شب بی خوابی برای دستیابی به حداکثر نتایج در شرایط محدود منابع با تجربه ارزشمند و خاطرات عالی جبران می شود. علاوه بر این، کار بر روی چنین وظایفی همیشه آزمون خوبی برای فرآیندهای شرکت است، زیرا دستیابی به نتایج واقعاً مناسب بدون تعامل داخلی خوب بسیار دشوار است.

اشعار به کنار: ما تحت تاثیر میزان کار تیم TON قرار گرفتیم. آنها موفق شدند یک سیستم پیچیده، زیبا و از همه مهمتر کارآمد بسازند. TON ثابت کرده است که پلتفرمی با پتانسیل بالایی است. با این حال، برای توسعه این اکوسیستم، هم از نظر استفاده از آن در پروژه های بلاک چین و هم از نظر بهبود ابزارهای توسعه، کارهای بسیار بیشتری باید انجام شود. ما مفتخریم که اکنون بخشی از این روند هستیم.

اگر پس از خواندن این مقاله هنوز سؤالی دارید یا ایده ای در مورد نحوه استفاده از TON برای حل مشکلات خود دارید، برای ما بنویس - ما خوشحال خواهیم شد که تجربه خود را به اشتراک بگذاریم.

منبع: www.habr.com

اضافه کردن نظر