این مقاله، توسط Tal Kol، نوشته شده و توسط تیم تنرادار برای خوانندگان فارسی زبان ارائه شده است. محتوای متن، ترجمهای دقیق و با حفظ اساس محتوای اصلی است.
رسالت نهایی تن، این است که بلاکچین را به پذیرش انبوه برساند. تاکنون چند نفر در جهان از بلاکچین استفاده کردهاند؟ آمار اتریوم به چندین میلیون نفر اشاره دارد، بنابراین در نظر گرفتن رقم ۵۰ میلیون کاربر در سطح جهانی برای بلاکچین سخاوتمندانه به نظر میرسد. چگونه میتوانیم این عدد را به ۱ میلیارد برسانیم؟
نسخهی فعلی اتریوم روزانه حدود ۱ میلیون تراکنش را با حداکثر ظرفیت، پردازش میکند. در سال ۲۰۱۶، تلگرام، برنامهی پیامرسان با پذیرش انبوه، برای مقیاس مورد نظر ما، روزانه ۱۵ میلیارد پیام ارسال میکرد. این حجم عظیم از داده، منجر به انتخابهای طراحی معماری مورد استفاده در طراحی بلاکچین تن شد. افزایش دههزار برابری مقیاسپذیری سیستمها، معمولاً با پیشرفتهای پروتکل صرفا به دست نمیآید، این شاهکار مستلزم تغییر اساسی در رویکرد است.
مفهوم شاردینگ
شاردینگ مفهومی بالغ و کامل است که از طراحی پایگاه داده نشأت میگیرد. همچنبن شامل تقسیم و توزیع یک مجموعهي دیتای منطقی در چندین پایگاه داده است که هیچ وجه اشتراکی ندارند و قادر هستند در چندین سرور مستقر شوند. به عبارت ساده، شاردینگ اجازهی مقیاس پذیری افقی را میدهد – تقسیم دادهها به قطعات مجزا و مستقل که امکان پردازش موازی را فراهم میکند. این یک مفهوم کلیدی در عبور جهان از دیتا به کلان دیتا است. زمانی که مجموعهی دادهها به حدی بزرگ میشوند که نمیتوان آنها را با روشهای سنتی مدیریت کرد، هیچ راه دیگری برای مقیاسبندی وجود ندارد جز اینکه آنها را به تکههایی کوچکتر تقسیم کنیم.
تن از اولین شبکههایی نیست که شاردینگ را در بلاکچین اعمال میکند. اتریوم 2.0 از تعداد ثابت ۶۴ شارد پشتیبانی کرده و علاوه بر این رویکرد تن ریشهای و رادیکال است نه به این علت که تعداد شاردها بیشتر است، بلکه به ۲ دلیل تغییر مفهومی منحصر به فرد:
- تعداد شاردها ثابت نیست – شبکهی تن از افزودن بیشتر و بیشتر شاردها در صورت نیاز با جهش بالایی ۶۰^۲ (در هر زنجیره کار) پشتیبانی میکند. این تعداد عملاً نامحدود است، به اندازهای که برای هر فرد در جهان ۱۰۰ میلیون شارد اختصاص داده میشود و همچنان به عنوان یدک و اضافه موجود هست.
- تعداد شاردها الاستیک است – تن از تقسیم خودکار زنجیرهای شارد شده به ۲ قسمت، زمانی که بار زیاد است و سپس ادغام مجدد آنها با هم هنگامی که بار کم است پشتیبانی میکند. این تنها راه برای روبهرو شدن با نیازهای مقیاس بندی پویا است که پیش بینی آنها از قبل محال است.
دربارهی این ایدههای نو در وایتپیپر تن بیشتر مطالعه کنید.
تلاش برای تغییر جهان به طور اساسی، بدون بها به دست نمیآید. برای بهرهگیری از این رویکرد رادیکال، توسعه دهندگانِ قرارداد هوشمند تن، میبایست قراردادهای خود را به گونهای متفاوت طراحی کنند. اگر شما تجربهی قبلی از قراردادهای هوشمند را داشته باشید، برای مثال در زبان سالیدیتی، معماری تن، حسی غریب و بیگانه به شما منتقل میکند. توصیه میکنم پست قبلی که در مورد ۶ جنبهی منحصر به فرد بلاکچین تن که تسهیل دهندهی انتقال است و توسعه دهندگان سالیدیتی را شگفت زده خواهد کرد را مطالعه کنید.
شارد کردن قراردادهای هوشمند تن
قرار داد هوشمند نمونه ای از واحد اتمی بلاکچین تن است. یک نمونهی قرارداد هوشمند دارای آدرس، کد و سلولهای داده (وضعیت پایدار) است. ما این واحد را اتمی مینامیم، زیرا یک قرارداد هوشمند دائما به تمام حالت پایدار خود دسترسی همزمان اتمی دارد.
ارتباط بین نمونههای قرارداد هوشمند در شبکهی تن نه اتمی است و نه همزمان. به قراردادهای هوشمند در تن مانند میکروسرویسها فکر کنید. هر میکروسرویس فقط به داده های محلی خود دسترسی همزمان اتمی دارد. ارتباط بین دو میکروسرویس شامل ارسال پیامهای ناهمزمان از طریق شبکه است.
سیستم های بزرگتر نیاز به تغییر از معماری های یکپارچه به میکروسرویسها دارند؛ این نکتهای است که هر معمار سیستمی از آن آگاه است.اتخاذ این رویکرد توزیع شده تا حدی نیازمند تلاش است، اما چندین مزیت مطلوب را برای شما در پیش خواهد داشت. الگوی سیستمهای مدرن به ارکستراتوری مانند کوبرنتس متکی است تا گروهی از میکروسرویسهای کانتینری را دریافت کند و نمونههای جدید را بهطور خودکار در صورت وجود تقاضا راهاندازی کند (مقیاس خودکار) و همچنین آنها را به طور مؤثر در بین ماشینها تقسیم کند.
من قیاس کوبرنتس را دوست دارم زیرا این دقیقاً همان کاری است که تن انجام می دهد. با افزایش بار روی یک شاردچین خاص، به ۲ قسمت تقسیم میشود. از آنجایی که نمونه های قراردادهای هوشمند اتمی هستند، هرگز به نصف نمیرسند. این به این معنی است که برخی از نمونههای قرارداد هوشمند که زمانی روی ۱ شاردچین میزیستند، ممکن است روزی بر روی قسمت متفاوتی باشند!
به طور خلاصه، ماشین مجازی تن (TVM)، در حال استفاده از مفهوم میکروسرویسهای توزیع شده در مقابل ماشین مجازی یکپارچهی اتریوم.
طراحی قراردادهای هوشمند برای شاردینگ
پرسشی رایج توسط معماران مبتدی سیستمها این است که “حد و اندازهی میکروسرویسهای من باید چقدر باشد؟” – یا به بیانی دیگر، “چه زمانی یک میکروسرویس به مقدار زیادی یکپارچه است و باید به دو بخش تقسیم شود؟
هیچ پاسخی برای این سوال وجود ندارد، این یک هنر است. ایده این است که به کوبرنتس در انجام کارهای خودش کمک کنیم. هرچه میکروسرویسها کوچکتر باشند، کوبرنتس راحتتر میتواند سیستم را با ایجاد نمونههای جدید و جابجایی آنها برحسب تقاضا بهینهسازی کند. اما هرچه کوچکتر باشند، اجرای جریان های پیچیده برای توسعهدهنده سختتر میشوند زیرا اقدامات ناهمزمان افزایش مییابند.
من متوجه شدم که همان استدلال برای شاردینگ قرارداد تن کار میکند. ایده این است که به شاردینگ خودکار تن اجازه دهیم کار خود را انجام دهد – داده های حالت را به چند نمونه قرارداد هوشمند تقسیم کند تا زمانی که بار افزایش مییابد، بتوان آنها را به قطعات کوچکتر تقسیم کرد و به طور موثر به شاردچینهای مختلف انتقال داد. اما، اگر تهاجمی شارد میکنید، به دلیل افزایش ناهمزمانی، باید با پیچیدگی بیش از حد مواجه شوید.
یک مثال کاربردی – قرارداد ژتون تن
هر آنچه تا به این جا خواندید تا اکنون کاملا تئوری بوده است. حال، با هم روی کاربرد آن در دنیای واقعی و مثالی ملموستر تمرکز میکنیم تا این معماری را بهتر درک کنیم. مثالی که ما استفاده خواهیم کرد قرارداد هوشمند ژتون تن است. ژتون یک قرارداد هوشمند است که توکنی قابل تعویض (بسیار شبیه به خود تنکوین) را پیاده سازی میکند. این همان نسخهی تن، از استاندارد توکن محبوب اتریوم ERC20 است.
پیاده سازی توکن بسیار ساده است. ما به یک اقدام پایهای نیاز خواهیم داشت – انتقال – که اجازهی انتقال توکن از یک مالک به مالک دیگر را میدهد. ما همچنین به یک عمل ضرب اولیه/مینت نیاز خواهیم داشت – توانایی افزودن توکنهای جدید به گردش، و متضاد آن، سوزاندن آن توکنها است. – که توکنها را از گردش خارج میکند. وضعیت پایدار چطور؟ ما همچنین باید موجودی همهی کاربران را ذخیره کنیم. در اتریوم، این معمولاً به نقشه ای نیاز دارد که کلید آن آدرس کیف پول کاربر و ارزش آن، مقدار موجودی است.
به عنوان معماران این قرارداد هوشمند در تن، ما باید تصمیم بگیریم که اگر و چگونه این قرارداد هوشمند باید به چندین نمونه کوچکتر تقسیم شود تا به طور موثر از شاردینگ خودکار پشتیبانی کند. اگر ژتون ما ۱ میلیارد کاربر داشته باشد چه اتفاقی رخ میدهد؟ آیا معماری ما در این مورد دوام خواهد آورد؟
توزیع ژتون در چندین قرارداد هوشمند
بیایید استدلال ذکر شده را یافتن مقدار “صحیح” شاردینگ برای ژتون اعمال کنیم. من دریافتم که این کمی بیش از حد نظری است. خوشبختانه، یک آزمون بسیار کاربردی وجود دارد که به خوبی کار میکند:
اگر زمانی متوجه شدید که در حال طراحی یک قرارداد هوشمند با ساختار دادهی نامحدود هستید، احتمال زیادی وجود دارد که این قرارداد را به چند نمونه تقسیم کنید.
ساختارهای دیتای نامحدود، مجموعهها یا نقشههایی هستند که میتوانند به طور نامشخصی رشد کنند. تحت اتریوم، قرارداد هوشمند ما به نقشه ای نیاز دارد که تمام موجودی های کاربر را در خود نگه دارد. این نقشه قابلیت این را دارد که به صورت نامحدود رشد کند چون تعداد دارندگان توکن ما نامحدود است. حسابهای جدید را میتوان عملاً بهطور بینهایت ایجاد کرد و از آنجایی که دقت عددی بسیار بالاست، میتوان مقادیر ناچیزی از توکن را به تمامی این حسابها منتقل کرد.
زمان آن رسیده تا قانون کاربردی خود را اعمال کنیم. اگر بخواهیم همه موجودیها را در یک قرارداد هوشمند روی تن نگه داریم، ساختار دیتای نامحدودی خواهیم داشت. این بدان معناست که ما یک کاندید عالی برای شاردینگ داریم!
اکنون، چگونه شارد کنیم؟ خیلی ساده است! اگر نمیخواهیم همه موجودیها در یک نمونهی قرارداد هوشمند فهرست شوند، چه میشود اگر فهرست را به گونهای تقسیم کنیم که هر موجودی در نمونهی قرارداد هوشمند اختصاصی خودش نگهداری شود؟
معماری ژتون
بیایید فرض کنیم که نمونه ژتون ما برای یک توکن به نام Shiba-Inu یا به اختصار SHIB باشد. ما دو کاربر داریم که ارز (SHIB) را هولد میکنند. – آلیسون و بکی. قبلاً گفتیم که موجودی هر کاربر در نمونهی قرارداد خودش نگهداری میشود، به این معنی که ما ۲ نمونه داریم به اسم زیرمجموعه یا اصطلاحا (فرزندان). به نظر می رسد که ما همچنین می خواهیم نمونهی دیگری برای نگهداری اطلاعات مشترک جهانی درباره SHIB داشته باشد (والد).
این رویه، ما را به معماری زیر سوق میدهد:
تیم اصلی تن یک پیاده سازی رسمی از استاندارد ژتون دارد که میتوانید در اینجا مطالعه کنید.
می توانید در کد، ۲ قرارداد هوشمند FunC را مشاهده کنید:
- Jetton-minter.fc – به عنوان والد است که اطلاعات مشترک جهانی در مورد توکن، مانند نام و نماد آن را در اختیار دارد. تنها یک نمونه از والد وجود دارد. درست است که این قرارداد مسئول مینت کردن است، اما حتی در صورت غیرفعال شدن مینت، باز هم به آن نیاز دارید که تا حدودی گیج کننده است.
- jetton-wallet.fc – این زیرمجموعه یا اصطلاحا فرزند است که موجودی توکن را برای یک کاربر حفظ میکند. چندین نمونه از این قرارداد وجود دارد، یکی برای هر آدرس کاربر.
اگر توکن ما توسط ۱میلیون کاربر مختلف نگهداری شود، دقیقاً ۱ میلیون و۱ کاربر نمونه قرارداد دیپلوی خواهد شد. اینجاست که جادوی شاردینگ خودکار اتفاق می افتد. بهطور پیشفرض، تمام نمونههای قرارداد بر روی یک شاردچین پیدا میشوند. اما، اگر این کاربران شروع به صدور تعداد زیادی تراکنش کنند و همین یک عدد شاردچین تحت بار زیاد باشد، تن به طور خودکار آن را به شاردچین های کوچکتر تقسیم می کند. از نظر تئوری، سیستم میتواند به دائما تقسیم شدن ادامه دهد تا زمانی که هر نمونهی اسمارت کانترکت در یک بخش اختصاصی یافت شود. این رازی است که تن را قادر میسازد تا به میلیاردها کاربر برسد.
سناریوهای مختلف کاربران ژتون
اکنون که معماری پایه را درک کردیم، بیایید به چند سناریوی مختلف نگاهی داشته باشیم. به عنوان مثال، وقتی یک کاربر توکنها را به فرد دیگری منتقل میکند چه اتفاقی رخ میدهد. در تن نهادهای شرکتکننده همواره نمونههای قراردادهای هوشمند هستند. کدام قراردادها نقش خواهند داشت؟
شما قبلا با ۳ مورد اول آشنا شدهاید. کد منبع این موارد در مخزن ژتون یافت میشود. در مورد سه قرارداد سمت راست چطور؟ سناریوهای کاربری ما شامل سه کاربر مختلف میشود. آلیسون و بکی دارندگان SHIB هستند. کاربر ادمین، خالقی است که SHIB را دیپلوی کرده است. مدیر نقش ویژه ای دارد چون تنها کاربری است که می تواند SHIB جدید را وارد گردش کند (توکنهای SHIB جدید به این ترتیب تولید میشوند). این یک نقش قابل اعتماد است که معمولاً باید پس از شروع معاملهی توکن لغو شود (به آدرس صفر تغییر یابد) تا کل عرضهی ممکن محدود شود.
کاربران در تن نیز با قراردادهای هوشمند نمایش داده میشوند. اینها قراردادهای هوشمند برای کیف پول هستند که معمولاً توسط برنامههای ولت مانند تنکیپر برای کاربران دیپلوی میشوند. اگر با نحوهی کار قراردادهای کیف پول در تن آشنا نیستید، لطفاً پست قبلی من را بخوانید که کیف پول های تن چگونه کار می کنند و چگونه از جاوا اسکریپت به آنها دسترسی پیدا کنید.
آلیسون، بکی و ادمین هر کدام موجودی تنکوین خود را در این کیف پولها نگه میدارند. این کیف پولها به طور خاص به کد ژتون مربوط نمیشوند. در اینجا یک نمونه پیاده سازی برای چنین قرارداد کیف پولی از مخزن اصلی تن آورده شده است. تصور کنید که آلیسون تصمیم میگیرد اقداماتی را با ژتون SHIB انجام دهد. در این مورد، آلیسون تصمیم گرفته است که چند توکن برای شخصی بفرستد. آلیسون برنامهی کیف پول انتخابی خود را باز میکند (به عنوان مثال تنکیپر) و این عمل را تأیید میکند. هنگامی که این اتفاق می افتد، برنامه کیف پول یک تراکنش امضا شده را به قرارداد کیف پول آلیسون ارسال میکند.
در اینجا یک مثال پیاده سازی برای چنین قرارداد کیف پولی از مخزن اصلی تن آورده شده است.
سناریوی کاربر ۱ : آلیسون دارای SHIB است و تعدادی را برای شخصی میفرستد
سناریوهای ما همیشه با یکی از کاربران (در این مورد آلیسون) شروع میشود که تصمیم میگیرد اقداماتی را با SHIB Jetton انجام دهد. در این مورد، آلیسون تصمیم گرفته است که چند توکن برای بکی بفرستد. آلیسون برنامه کیف پول انتخابی خود را باز می کند (به عنوان مثال TonKeeper) و این عمل را تأیید می کند. هنگامی که این اتفاق در حال انجام است، برنامه کیف پول یک تراکنش امضا شده را به قرارداد کیف پول آلیسون ارسال می کند.
تراکنش شامل پیامی میباشد که برای برخی از قراردادهای مقصد در نظر گرفته شده است. پیامها نحوهی ارتباط قراردادهای هوشمند در تن هستند. پیامها به صورت کیسهای از سلولها رمزگذاری میشوند که در اصل، یک فرمت باینری بستهبندی شده هستند. یکی از فیلدهای کلیدی پیام یک عدد صحیح ۳۲ بیتی به نام op است که نوع عملیات این پیام را توصیف میکند. .
۱. در مثال ما، از آنجایی که آلیسون قصد دارد چند توکن ارسال کند، پیامی باop نوع transfer به نمونه قرارداد هوشمند ارسال میکند که موجودی SHIB خود را نگه میدارد. این پیام در تراکنشی که او به قرارداد کیف پول خود ارسال میکند کدگذاری میشود. هنگامی که قرارداد کیف پول او امضای تراکنش [کد] را تأیید کرد، پیام آلیسون را به مقصدی که [کد] درخواست کرده بود، ارسال میکند.
۲. هنگامی که پیام transfer به مقصد خود رسید [کد]، قراردادی که موجودی SHIB آلیسون را در اختیار دارد، این قرارداد پیام را پردازش میکند و حالت پایدار آن را تغییر میدهد (موجودی SHIB آلیسون را با مقدار ارسال شده کاهش میدهد [کد]). اگر قرارداد نیاز به تماس با قراردادهای دیگر را داشته باشد، ممکن است که پیامهای اضافی ارسال کند. در مورد ما، قرارداد یک پیام با نوع internal transfer داخلی به قرارداد دارای موجودی SHIB بکی را ارسال میکند[کد] .
۳. هنگامی که پیام internal transfer به مقصد خود رسید [کد]، این قرارداد اکنون پیام را پردازش میکند و حالت پایدار آن را تغییر میدهد (موجودی SHIB بکی را با مقدار ارسال شده [کد] افزایش می دهد). این قرارداد معمولاً آخرین پیام را با op نوع excesses ارسال میکند تا هرگونه سوخت شبکهی باقیمانده را به قرارداد کیف پول آلیسون بازپرداخت کند و به آن اطلاع دهد که انتقال کامل شده [کد]است
این روند پیامها است:
داستان کاربر ۲: آلیسون دارای SHIB است و تعدادی را برای بکی می فرستد و به بکی در مورد آن اطلاع میدهد.
اگر بکی گیرنده SHIB، چیزی بیش از یک شخص باشد، به فرض یک قرارداد فروشگاه آنلاین است که آیا باید هنگام پرداخت کاری انجام دهد؟ خوب میشد اگر میتوانستیم این قرارداد هوشمند را با یک پیام اختصاصی راه اندازی و فعال کنیم.
خوشبختانه پیام transfer از این عمل پشتیبانی میکند. این به فرستندهی اصلی این اجازه را میدهد تا مقداری بار اعلان را مشخص کند که قرار است به صاحب کیف پول گیرندهی SHIB ارسال شود.
پیامهای روی شبکهی تن ناهمزمان هستند. دقیقاً نمیدانیم چه زمانی به آنها رسیدگی میشود. این احتمال وجود دارد که همهی پیامها در یک بلوک مدیریت شوند و یا، هر پیام در یک بلوک متفاوت مدیریت شود. این بدان معنی است که پردازش انتقال ممکن است مدتی زمان بر باشد. حتی اگر اولین تراکنش با موفقیت تأیید شده باشد، انتقال ممکن است همچنان با شکست مواجه شود.
۴. جریان در این مورد تقریباً یکسان است، به جز در گام آخر. پیش از ارسال پیام با op excesses ،قراردادی که موجودی SHIB بکی را در اختیار دارد، ابتدا پیامی مبنی بر excesses به صاحب کیف پول SHIB بکی – قرارداد کیف پول بکی ارسال می کند[کد]. اگر نام “بکی” را به فروشگاه آنلاینی مانند “DNS-Superstore” تغییر دهید، این داستان منطقی تر خواهد بود.
در این صورت، قرارداد “DNS-Superstore” این اعلان را دریافت می کند زیرا مالک کیف پول SHIB برای “DNS-Superstore” است. این قرارداد، پس از دریافت پیام، رفتار تغییر مالکیت رکورد DNS را با توجه به داده های ارائه شده در پیام را پیاده سازی میکند.
این روند پیامها است:
چگونه میتوانید مطلع شوید که چه ویژگی های دیگری توسط پیام transfer پشتیبانی میشود؟ پیامها معمولاً به زبانی به نام TL-B کدگذاری میشوند. به عنوان بهترین روش، سازندهی قرارداد باید مشخصات TL-B را برای همهی پیامهایی که قراردادشان را مدیریت میکند، منتشر کند. در اینجا مشخصات TL-B مربوطه [کد] آمده است:
transfer query_id:uint64 amount:(VarUInteger 16) destination:MsgAddress
response_destination:MsgAddress custom_payload:(Maybe ^Cell)
forward_ton_amount:(VarUInteger 16) forward_payload:(Either Cell ^Cell)
= InternalMsgBody;
- مبلغ تعداد توکن های SHIB برای انتقال است.
- مقصد، آدرس قرارداد کیف پول بکی است.
- response_destination ،
آدرس دریافت کننده مازاد است (معمولاً قرارداد کیف پول آلیسون) - (forward_payload)
payload اعلان مورد استفاده برای «DNS-Superstore» است
سناریوی کاربر ۳: آلیسون مقداری SHIB دارد و آن را می سوزاند
در این سناریوی کاربری، آلیسون تصمیم میگیرد مقداری از SHIBهایی که دارد را بسوزاند. سوزاندن SHIB آن را از گردش خارج میکند. چرا او باید چنین کاری را انجام دهد؟ سوزاندن، عرضهی کل یک توکن را کاهش میدهد؛ عرضهی کل توکن برای کاربران حائر اهمیت است زیرا این مساله به محاسبهی ارزش نقدینگی توکن کمک میکند. سوزاندن توکنها مانند خرید سهم در بازار سهام است، باعث افزایش ارزش سهام میشود.
عرضهی کل کجا ذخیره میشود؟ همانطور که احتمالاً حدس زده اید، از آنجایی که این دادههای وضعیت پایدار در سطح جهانی به اشتراک گذاشته میشود، منطقی است که آن را در زیر jetton-minter والد ذخیره کنیم.
۱. برای شروع عملیات سوزاندن، آلیسون پیامی با عنوان burn به نمونهی قرارداد هوشمند که موجودی SHIB او را نگه میدارد ارسال میکند. این پیام مانند تراکنشی که او به قرارداد کیف پول خود ارسال میکند، رمزگذاری میشود، که پس از تأیید امضا، آن را به مقصد ارسال میکند.
۲. هنگامی که پیام burn به مقصد خود می رسد [کد]، قراردادی که موجودی SHIB آلیسون را نگه میدارد، این قرارداد پیام را پردازش کرده و حالت پایدار آن را تغییر میدهد (موجودی SHIB آلیسون را با مقدار سوزانده شده را کاهش میدهد[کد] ). سپس قرارداد پیامی با عنوان اعلان سوزاندن به قرارداد minter والد ارسال میکند[کد] .
۳. هنگامی که پیام burn notification به مقصد خود رسید [کد]، این قرارداد اکنون پیام را پردازش می کند و وضعیت پایدار آن را تغییر میدهد (مجموع عرضه را با مقدار سوزانده شده کاهش میدهد[کد]). این قرارداد معمولاً آخرین پیام را با مازاد نوع عملیات ارسال میکند تا هر سوخت باقیمانده را به قرارداد کیف پول آلیسون بازپرداخت کند و به آن اطلاع دهد که سوزاندن کامل شده است [کد].
قرارداد minter والد به کاربران اجازه میدهد تا با استفاده از روش getter [کد]، کل عرضهی توکن را query کنند.
سناریوی کاربر ۴: ادمین SHIB را برای بکی مینت می کند
وقتی همهی قراردادها در ابتدا دیپلوی میشوند، کل عرضهی SHIB صفر است و هیچ فردی هیچ توکنی ندارد. توکنها چگونه ایجاد میشوند؟ عمل ایجاد توکنهای جدید را Minting می گویند. این فقط توسط یک نقش ادمین خاص یعنی کاربر ادمین صورت می گیرد. ادمین یوزر همچنین میتواند امتیاز مدیریت را به هر آدرس دیگری منتقل کند. به عنوان بهترین روش، قبل از شروع معاملهی یک توکن، امتیاز مدیریت باید به آدرس صفر منتقل شود تا تضمین شود که هیچکس نمیتواند توکنهای جدید را ضرب کند و کل عرضه را افزایش دهد.
۱. برای شروع یک مینت، ادمین یک پیام با op نوع mint به jetton-minter والد ارسال میکند. این پیام در تراکنشی که ادمین به قرارداد کیف پول خود ارسال می کند، کدگذاری شده و پس از تأیید امضا، آن را به مقصد ارسال میکند.
۲. هنگامی که پیام mint به مقصد خود رسید[کد]، قرارداد minter والد، این قرارداد پیام را پردازش میکند و تأیید میکند که پیام واقعاً از ادمین منشا گرفته است [کد]. سپس، قرارداد حالت ثابت خود را تغییر میدهد (عرضهی کل را به میزان مینت شده افزایش می دهد [کد]). قرارداد یک پیام با نوع عملیات انتقال داخلی به قرارداد دارای موجودی SHIB بکی ارسال می کند [کد].
۳. هنگامی که پیام internal transfer به مقصد خود رسید [کد]، این قرارداد اکنون پیام را پردازش می کند و حالت پایدار آن را تغییر می دهد (موجودی SHIB بکی را با مقدار مینت شده افزایش می دهد [کد]). این قرارداد معمولاً آخرین پیام را با op نوع excesses ارسال میکند تا هرگونه سوخت شبکهی باقیمانده را به قرارداد کیف پول ادمین بازپرداخت کند و به او اطلاع دهد که مینت کامل شده است [کد].
این روند پیامها است:
آخرین مرحلهی این جریان تقریباً مشابه جریان انتقال است.همچنین ممکن است به گیرندهی SHIB با یک پیام اختصاصی اطلاع داده شود تا بتواند پرداخت را انجام دهد – مثال “DNS-Superstore” را به خاطر دارید؟ من یک سناریوی کامل از کاربر دیگری برای این مورد اضافه نمی کنم، اما در اینجا جریان پیام ها برای احتیاط گذاشته شده است.
چه شخصی قراردادهای فرزند را دیپلوی میکند؟
بیایید معماری قرارداد SHIB را به یاد بیاوریم – یک نمونهی دیپلوی شده از والد jetton-minter و یک نمونه مستقر برای هر دارندهی SHIB از قرارداد jetton-wallet:
قرارداد parent minter به طور طبیعی احتمالا توسط خالق SHIB، دیپلوی شده است. اما در مورد قراردادهای فرزند، چه کسی آنها را دیپلوی می کند؟ طراحی کارآمد است – قرارداد فرزند تنها زمانی اجرا می شود که صاحب آن، برای اولین بار SHIB را دریافت کند. این ممکن است کمی گیج کننده به نظر برسد، زیرا گیرنده لزوماً از ارسال SHIB آگاه نیست.
اگر سناریوی انتقال کاربری که ذکر شد را به خاطر بیاورید، دریافت SHIB با پیام internal transfer فعال می شود. اگر قرارداد فرزند گیرندهی این پیام، هرگز دیپلوی نشده باشد، فرستندهی این پیام باید فرزند را دیپلوی کند! میتوانید این اتفاق را در کد مشاهده کنید. بخشstate_init پیام در واقع مسئول دیپلوی می باشد. می توانید آن را در اینجا از سلول کد اولیه فرزند (بایت کد TVM کامپایل شده برای اجرای این قرارداد) و سلول داده اولیه آن محاسبه کنید.
از آنجایی که فرستنده پیام internal transfer هرگز مطمئن نیست که گیرنده دیپلوی شده است یا خیر، همیشه شامل بخش دیپلوی میشود. تن به اندازه کافی باهوش است که اگر قرارداد قبلاً دیپلوی شده باشد، بخش دیپلوی را نادیده بگیرد.
احراز هویت پیام های بین والدین و فرزندان
در داستانهای کاربر بالا، دیدیم که یک جریان کامل در چندین پیام توزیع میشود. به عنوان مثال، internal transfer پیام باعث می شود که گیرنده آن موجودی SHIB خود را افزایش دهد (همانطور که به یاد دارید، در پایان جریان انتقال ارسال شد). اگر یک مهاجم سعی کند این پیام را جعل کند و آن را به قراردادی بفرستد که موجودی SHIB خودش را دارد، چه اتفاقی میافتد؟ اگر مراقب نباشیم، چنین جعلهایی منجر به توانایی مهاجم برای تولید توکنهای جدید برای خود میشود!
برای تضمین قرارداد در برابر این جعل، باید تأیید کنیم که این پیامهای مهم که تعادل را تغییر میدهند در واقع از یک فرستنده معتبر سرچشمه میگیرند. شما می توانید کد اعتبار سنجی را اینجا ببینید
قرارداد فقط در صورتی به پیامی رسیدگی می کند که توسط والدین منتخب (به دلایلی با برچسب jetton_master_address) یا توسط یکی از فرزندان معتبر ارسال شده باشد.
این منجر به یک سوال بسیار جالب می شود – چگونه می توانیم بگوییم که برخی از آدرس های تصادفی یک آدرس ژتون فرزند معتبر است؟ کمی صبر کنید، اوایل که آلیسون می خواست برای بکی پیامی بفرستد، از کجا آدرس قرارداد بکی را میدانست؟
این، دوباره، یک طراحی سیستم جذاب است – آدرسهای قراردادهای هوشمند در تن از سلول کد اولیهی قرارداد (بایت کد TVM کامپایل شدهی اجرای آن) و سلول دیتای اولیهی قرارداد (وضعیت پایدار اولیهی ساخت و ساز آن پس از آن) مشتق شده است.).
اگر این دو مقدار را بدانیم، میتوانیم آدرس یک قرارداد را حتی قبل از دیپلوی شدن آن محاسبه کنیم. این محاسبه قطعی و غیرقابل تغییر است.
کد ژتون حاوی یک تابع کاربردی است که آدرس فرزند – به معنی آدرس قراردادی که موجودی SHIB آلیسون را نگه می دارد – بر اساس آدرس آلیسون محاسبه می کند. این تابع را می توانید اینجا ببینید. همانطور که می بینید، در واقع به سلول کد اولیه فرزند و سلول داده اولیه آن بستگی دارد.
کمی دشوار تا این مکانیزم امن را درک کنیم. چه چیزی مانع از این میشود که مهاجم به نحوی قرارداد مخرب خود را در آدرس یکی از فرزندان قانونی دیپلوی کند؟ برای قرار گرفتن در یک آدرس قانونی، قرارداد مخرب باید دارای سلول کد اولیه فرزندان رسمی باشد – این قبلاً توانایی مهاجم را برای افزودن کد مخرب به این پیاده سازی محدود کرده است. علاوه بر این، سلول داده اولیه تضمین می کند که فرزند فقط از والد دقیق اطاعت می کند زیرا سلول داده اولیه حاوی آدرس آن است.
مدیریت تغییرات جزئی زمانی که همه چیز اشتباه می شود
همانطور که به یاد می آورید، جریان انتقال بر روی چندین پیام ناهمزمان توزیع می شود. پیام اول موجودی SHIB فرستنده را کاهش می دهد و پیام دوم موجودی SHIB گیرنده را افزایش می دهد. وقتی همه چیز خوب پیش می رود، این در یک جریان همزمان منطقی است، اما اگر پیام دوم به نحوی شکست بخورد چه اتفاقی می افتد؟
اکثر ماشینهای قرارداد هوشمند، مانند EVM اتریوم، تراکنشها را به صورت کاملاً اتمی و همزمان پردازش میکنند – بنابراین اگر یکی از مراحل بعدی با شکست مواجه شود، کل تراکنش برمیگردد و تمام تغییرات حالت ناشی از این تراکنش نیز برگردانده میشوند. درک این مکانیسم در واقع بسیار آسان است. متأسفانه، از آنجایی که پیامهای موجود در تن نه اتمی هستند و نه همزمان، ما این بازگشت خودکار را از جعبه دریافت نمیکنیم.
پس چه می توانیم بکنیم؟ ما باید خودمان جریان برگشت را مدیریت کنیم. این مثالی است که توسعهی قراردادهای هوشمند را در تن کمی دشوارتر میکند.
هنگامی که مدیریت یک پیام در تن به دلیل هر استثنای پرتاب شده با شکست مواجه می شود، اگر flag bounced این پیام تنظیم شده باشد، سیستم به طور خودکار پیام ناموفق را با bounced falg set به فرستنده برمی گرداند. شما می توانید مشخصات این مکانیسم پیام را در اینجا بخوانید.
بیایید به مثال بالا برگردیم – شکست پیام دوم در جریان انتقال. این پیام پس از اینکه فرستندهی SHIB، موجودی SHIB خود را با مبلغ ارسالی کاهش داده است، ناموفق است.
برای ثابت نگه داشتن سیستم، باید به نحوی این کاهش در شکست را به حالت قبل در بیاوریم. چطور این کار صورت میگیرد؟ با فرض اینکه پیام دوم با bounceflag set ارسال شده است، میتوانیم کاهش فرستنده را هنگامی که message bounce دوم دریافت میشود، لغو کنیم. میتوانید کد رسمی ژتون را ببینید که پیامهای برگشتی را مدیریت میکند و کاهش را در اینجا لغو میکند.
این کار را با دقت انجام بدهید! هنگام طراحی جریانهای پیام پیچیده بر روی تن، یک تختهی سفید داشته برای خود داشته باشید و نمودارهای مختلف جریان پیام را مانند آنچه در این پست انجام دادم رسم کنید. ابزار مورد علاقه من برای این کار Excalidraw است که علاوه بر این فوق العاده و متن باز است. سپس، شروع به شبیهسازی یک شکست بالقوه و message bounce در هر مرحله از جریان کنید تا مطمئن شوید کد شما به درستی خنثیسازی را انجام میدهد.
موفق باشید!