اگر با دنیای ارزهای دیجیتال کمی آشنا باشید، حتما نام اتریوم را شنیدهاید. نداشتن پیشزمینه درباره بلاک چین اتریوم، شما را با عبارات و اصطلاحات گنگ بسیاری مواجه میکند که سد راه یادگیری شما درباره یکی از نوآوریهای خارقالعاده دنیای ارزهای دیجیتال میشود. در این مطلب قصد داریم نحوه کار اتریوم را با جزئیات آن توضیح داده و مفاهیم آن را توضیح دهیم. با ما همراه باشید.
اتریوم چیست؟
اتریوم در اصل یک پایگاه داده (دیتابیس) عمومی است که سوابق تراکنشهای دیجیتال را به شکل دائمی نگهداری میکند. این پایگاه داده به هیچ قدرت مرکزی برای حفظ و نگهداری نیاز ندارد، زیرا بهعنوان یک سیستم تبادلی «بدون نیاز به اعتماد» عمل میکند. چارچوبی که در آن افراد میتوانند تراکنشهای همتا به همتا را انجام دهند، بدون اینکه نیاز باشد به یکدیگر یا حتی به یک طرف سوم اعتماد کنند.
اگر گیج شدهاید نگران نباشید. هدف ما توضیح چگونگی عملکرد اتریوم در سطح فنی است، بدون اینکه درباره ریاضیات پیچیده آن و فرمولهایی که به نظر ترسناک میرسند صحبت کنیم. اگر برنامهنویس هم نباشید، میتوانید تا انتهای مطلب حداقل درک بهتری از این فناوری به دست آورید. اگر بخش فنی آن دشوار است، مشکلی نیست؛ زیرا قرار نیست تمام جزئیات آن را درک کنید. کافی است روی کلیات آن تمرکز کنید.
بسیاری از مباحثی که در این مطلب پوشش داده شدهاند، تجزیهوتحلیلی از مفاهیم موردبحث در گزارش فنی (یلو پیپر – Yellow Paper) اتریوم هستند. توضیحات و نمودارهایی را هم به آن اضافه کردهایم تا درک آن راحتتر شود. اما اگر شجاعت کافی دارید که با چالشهای فنی آن آشنا شوید، یلو پیپر اتریوم را بخوانید. بیایید شروع کنیم!
تعریف بلاک چین
بنا به تعریف فنی، بلاک چین یک «ماشین یکتای تبادلی ایمنشده با فنون رمزنگاری و دارای حالت اشتراکی» است. خیلی قلمبه سلمبه است نه؟ بیایید آن را ریز به ریز بررسی کنیم:
- ایمنشده با فنون رمزنگاری، یعنی ایجاد یک ارز دیجیتال که با الگوریتمهای پیچیده ریاضی ایمن شده است؛ بهطوریکه به راحتی قابل رمزگشایی نیست. میتوان گفت تقریبا چیزی شبیه فایروال است. این الگوریتمها تا حد زیادی تقلب در سیستم از جمله ایجاد تراکنشهای جعلی، پاککردن تراکنشها و نظایر آنها را ناممکن میسازند.
- ماشین یکتای تبادلی، یعنی نوعی ماشین استاندارد منحصربهفرد که مسئول همه تراکنشهای ایجادشده در سیستم است. به عبارت دیگر، این یک حقیقت جهانی یکتاست که هر کسی آن را باور دارد.
- دارای حالت اشتراکی، یعنی هر حالتی که در این ماشین ذخیره میشود برای همه افراد باز است و با همه آنها به اشتراک گذاشته میشود.
اتریوم چنین بلاک چینی را اجرا میکند.
توضیح بلاک چین اتریوم
بلاک چین اتریوم اساسا یک ماشین حالت (state machine) مبتنی بر تراکنش است. در علوم کامپیوتر، یک ماشین حالت به چیزی اشاره دارد که مجموعهای از ورودیها را میخواند و بر اساسِ همان ورودیها حالت خود را تغییر میدهد و به حالت جدید انتقال مییابد.
برای توضیح ماشین حالت اتریوم، با حالت صفر یا همان حالت جنسیس (genesis state) شروع میکنیم. حالت صفر شبیه یک لوح خالی است که نشان میدهد هنوز هیچ تراکنشی در شبکه انجام نشده است. هنگامی که تراکنشها انجام میشوند، حالت جنسیس به حالتهای بعدی انتقال مییابد. در هر نقطهای از زمان، آخرین حالت، نشاندهنده حالت فعلی اتریوم است.
هر حالت اتریوم میلیونها تراکنش دارد. این تراکنشها در «بلاکها» دستهبندی میشوند. یک بلاک حاوی یک سری از تراکنشهاست و هر بلاک با بلاک قبلی بهشکل زنجیره به هم متصلاند.
برای گذار از یک حالت به حالت بعدی، تراکنش باید معتبر باشد. برای اینکه اعتبار یک تراکنش مشخص شود، باید از نوعی فرایند اعتبارسنجی موسوم به استخراج (ماینینگ) عبور کند. استخراج وقتی انجام میشود که گروهی از نودها (کامپیوترهای متصل به شبکه) منابع محاسباتیشان (سخت افزار) را به ایجاد یک بلاک از تراکنشهای معتبر اختصاص دهند.
هر نود در شبکه که خودش را بهعنوان یک ماینر اعلام میکند، میتواند یک بلاک را ایجاد و اعتبارسنجی کند. تعداد زیادی از ماینرها از سراسر دنیا سعی میکنند بلاکهایی را ایجاد و اعتبارسنجی کنند. هر ماینر یک «گواه یا اثبات» ریاضی را در هنگام ارسال یک بلاک به بلاک چین ارائه میکند و این گواه بهعنوان یک ضمانت عمل میکند: اگر گواه وجود دارد پس بلاک باید معتبر باشد.
برای اضافه شدن یک بلاک به بلاک چین اصلی، یک ماینر باید سریعتر از همه ماینرهای رقیب آن را اثبات کند. فرایند اعتبارسنجی هر بلاک که یک ماینر برای آن گواه یا اثبات ریاضی را ارائه میکند به «اثبات کار» (proof of work) معروف است.
ماینری که یک بلاک جدید را اعتبارسنجی میکند، برای انجام کارش مقدار مشخصی ارزش پاداش میگیرد. این ارزش چیست؟
بلاک چین اتریوم از توکن دیجیتال اصلی خودش به نام «اتر» برای پاداشدهی به ماینرها استفاده میکند. هر بار که یک ماینر یک بلاک را تأیید میکند، توکنهای اتر جدید تولید میشوند و به ماینرها تعلق میگیرند.
اما چه تضمینی وجود دارد که هر فرد با یک زنجیره از بلاکها کار کند؟ چطور میتوانیم مطمئن باشیم که مجموعهای از ماینرها دور هم جمع نشوند که تصمیم بگیرند زنجیره بلاکهای خودشان را ایجاد کنند؟
پیشتر در تعریف بلاک چین گفتیم که بلاک چین یک ماشین یکتای تبادلی با حالت اشتراکی است. با استفاده از این تعریف، حالت درست فعلی یک حقیقت جهانی یکتاست که هر کسی باید آن را بپذیرد. داشتن حالتهای چندگانه (یا زنجیرههای متعدد) کل سیستم را خراب میکند. زیرا در این صورت توافق روی اینکه کدام زنجیره درست است، غیرممکن میشود.
اگر در زنجیرهها اختلاف وجود میداشت، نمیتوانستید بفهمید موجودی سکههایتان دقیقا چقدر است. یعنی ممکن بود در یک زنجیره ۱۰ سکه، در زنجیره دیگر ۲۰ سکه و در زنجیره دیگری ۴۰ سکه داشته باشید. در چنین سناریویی راهی وجود ندارد که بتوان تعیین کرد کدام زنجیره «معتبرتر» است.
هرگاه مسیرهای متعددی تولید میشوند، یک «فورک» رخ میدهد. ما معمولا میخواهیم از فورکها اجتناب کنیم، زیرا آنها سیستم اصلی را به دو بخش تبدیل میکنند و افراد را مجبور میکنند زنجیرهای را انتخاب کنند که آن را باور دارند.
برای تعیین معتبرترین مسیر و نیز جلوگیری از زنجیرههای متعدد، اتریوم از مکانیسمی به نام «پروتکل شبح (GHOST protocol)» استفاده میکند که کوتاهشده عبارت زیر است:
“GHOST” = “Greedy Heaviest Observed Subtree”
به زبان ساده، پروتکل GHOST میگوید که در صورت رخ دادن اختلاف نظر بین ماینرها، ما باید مسیری را انتخاب کنیم که بیشترین محاسبات روی آن انجام شده است. یک روش برای تعیین این مسیر، استفاده از شماره بلاک leaft (جدیدترین بلاک) است که نشاندهنده کل تعداد بلاکها در مسیر فعلی است (بدون احتساب بلاک جنسیس). هر چه شماره بلاک بزرگتر باشد، مسیر آن طولانیتر است و کار بیشتری برای رسیدن به جدیدترین بلاک روی آن انجام شده است. با استفاده از این استدلال میتوانیم روی نسخه اصلی حالت فعلی توافق کنیم. هر چه زنجیره بلندتر باشد، اما زنجیره معتبرتر است.
حالا بیایید بهشکل عمیقتر به واکاوی اجزای اصلی سیستم اتریوم بپردازیم. اجزای اصلی سیستم اتریوم عبارتاند از:
- حسابها (accounts)
- حالت (state)
- سوخت و کارمزدها (gas and fees)
- تراکنشها (transactions)
- بلاکها (blocks)
- انجام تراکنش (transaction execution)
- استخراج (mining)
- اثبات کار (proof of work)
قبل از شروع باید یک نکته را بدانید: هر بار که در این مطلب از «هش یا تابع درهمسازی» X صحبت میکنیم، منظورمان تابع هش KECCAK-۲۵۶ است که اتریوم از آن استفاده میکند.
حسابها
«حالت اشتراکیِ» جهانیِ اتریوم از تعداد بسیار زیادی جزء کوچک به نام «حساب» ساخته شده است که قادرند از طریق یک چارچوب انتقال پیام با یکدیگر تعامل برقرار کنند. هر حساب دارای حالت مخصوص به خودش و یک آدرس ۲۰ بایتی است. آدرس در اتریوم یک شناسه ۱۶۰ بیتی است که برای شناسایی حساب از آن استفاده میشود.
دو نوع حساب وجود دارد:
- حسابهای با مالکیت خارجی، که با کلیدهای خصوصی مدیریت میشوند و هیچ کد مخصوصی ندارند.
- حسابهای مبتنی بر قرارداد که با کد قراردادشان مدیریت میشوند و حاوی کد مرتبط با آنها هستند.
تفاوت بین حسابهای با مالکیت خارجی و حسابهای مبتنی بر قرارداد
دانستن تفاوت بنیادین بین حسابهای با مالکیت خارجی و حسابهای مبتنی بر قرارداد بسیار مهم است. یک حساب با مالکیت خارجی میتواند با استفاده از کلید خصوصیاش یک تراکنش انجام دهد، امضا کند و از این طریق پیامی به حساب با مالکیت خارجی یا حساب مبتنی بر قرارداد دیگری ارسال کند. پیام قابل انتقال بین دو حساب با مالکیت خارجی یک «انتقال ارزش» ساده است. اما پیامی که از یک حساب با مالکیت خارجی به حساب مبتنی بر قرارداد فرستاده میشود، فراتر از انتقال ارزش است. این پیام کد آن حساب را فعال میکند و بهاین ترتیب آن حساب میتواند کارهای مختلفی نظیر انتقال توکنها، نوشتن چیزی روی فضای ذخیرهسازی داخلی، ایجاد توکنهای جدید، انجام یک سری محاسبات و ایجاد قراردادهای جدید را انجام دهد.
برخلاف حسابهای با مالکیت خارجی، حسابهای مبتنی بر قرارداد نمیتوانند خودشان تراکنشهای جدید را وارد کنند و به اصطلاح آغازکننده باشند. این حسابها فقط میتوانند تراکنشها را در پاسخ به تراکنشهای دیگری که دریافت میکنند (از حسابهای با مالکیت خارجی یا حسابهای مبتنی بر قرارداد دیگر) ارسال کنند. در بخش «تراکنشها و پیامها» بیشتر درباره فراخوانی قرارداد به قرارداد توضیح خواهیم داد.
به این ترتیب هر عملی که در بلاک چین اتریوم انجام میشود، همیشه با تراکنشهایی که از حسابهای با مالکیت خارجی ارسال شدهاند، آغاز میشود.
حالت حساب
حالت حساب از هر نوعی که باشد، ۴ جزء دارد:
- نانس (nonce): اگر یک حساب از نوع مالکیت خارجی باشد، این عدد نشاندهنده تعداد تراکنشهای ارسالشده از آدرس آن حساب است. اگر این حساب مبتنی بر قرارداد باشد، نانس تعداد قراردادهای ایجاد شده توسط این حساب را نشان خواهد داد.
- موجودی (balance): تعداد وی (Wei) که در آن آدرس حساب موجود است. هر اتر برابر با ۱۰ به توان 18 وی است.
- ریشه ذخیره (storageRoot): هش نود ریشه (root node) از درخت مرکل پاتریسیا (درخت مرکل را بعدا توضیح خواهیم داد). این درخت، هش محتویات ذخیرهشده در حساب را رمزگذاری میکند و مقدار آن به طور پیشفرض خالی است.
- کدهش (codeHash): هش ماشین مجازی اتریوم (EMV) که متعلق به آن حساب است. برای حسابهای مبتنی بر قرارداد، کد مخصوص آنها درهمسازی شده و بهعنوان کدهش ذخیره میشود. برای حسابهای با مالکیت خارجی، بخش مربوط به «کدهش» خالی است.
حالت جهانی
حالت جهانی اتریوم شامل نگاشتی از آدرس حسابها به حالت حسابهاست. ساختار دادهی شکل ذخیره شده این نگاشت، به درخت مرکل پاتریسیا معروف است.
درخت مرکل نوعی درخت دودویی (باینری) است که از مجموعهای از نودها تشکیل شده است:
- تعداد زیادی نود برگ در انتهای درخت که حاوی دادههای زیرین هستند؛
- مجموعهای از نودهای میانی که در آن هر نود، حاصل درهمسازی دو نود فرزند خودش است؛
- یک نود ریشه تکی که از تابع درهمسازی دو نود فرزندش تشکیل شده است و نشاندهنده رأس درخت است.
کاملا مشخص است که درخت مرکل از پایین به بالا کار میکند. یعنی ابتدا دادههایی که قرار است ذخیره شوند در بین برگها (پایینترین قسمت درخت) پخش میشوند، سپس به یک مرحله بالاتر میروند و از درهمسازی این دادهها، دادههای بعدی در واحدهایی به نام «باکت» قرار میگیرند و سپس باکتها درهمسازی میشوند و این فرایند آنقدر تکرار میشود تا زمانی که از تعداد کل درهمسازیها یکی باقی بماند. آخرین هش باقیمانده از درهمسازیهای انجام شده، هش ریشه یا روت هش (root hash) نامیده میشود.
این درخت برای هر مقداری که در آن ذخیره میشود به یک کلید نیاز دارد. با شروع از نود ریشه درخت، این کلید به شما میگوید که مسیر کدام نود فرزند را باید برای رسیدن به ارزش موردنظر که در نودهای برگ ذخیره شده است دنبال کنید. در مورد اتریوم، نگاشت کلید/ارزش برای درخت حالت، نگاشتی از آدرسها به حسابهای متناظر با آنهاست که شامل موجودی، نانس، کدهش و ریشه ذخیره برای هر حساب است (که ریشه ذخیره خودش یک درخت است).
همین ساختار درخت برای ذخیره تراکنشها و رسیدها (receipt) هم استفاده میشود. بهطور خاص، هر بلاک دارای یک سربرگ (هِدِر) است که هش نود ریشه را از سه ساختار مختلف درخت مرکل ذخیره میکند. این سه درخت عبارتاند از:
- درخت حالت
- درخت تراکنشها
- درخت رسیدها
توانایی ذخیره همه این اطلاعات در درخت مرکل، برای لایت کلاینتها یا لایت نودها در اتریوم سودمند است. اگر یادتان باشد یک بلاک چین توسط تعدادی نود اداره میشود. بهطورکلی دو نوع نود داریم: نودهای کامل (فول نودها) و لایت نودها.
نودی که آرشیو کامل را میخواهد، بهمنظور همگامسازی بلاک چین باید کل زنجیره را از بلاک جنسیس تا بلاک فعلی دانلود کند و تمام تراکنشهای موجود در آن را اجرا کند. بهطورمعمول، ماینرها برای انجام فرایند استخراج باید کل آرشیو را ذخیره کنند، اما لزومی ندارد که همه تراکنشها را اجرا کنند. در هر صورت یک فول نود تمامی اطلاعات زنجیره را خواهد داشت.
اگر یک نود به انجام هر تراکنش یا تحقیق درباره دادههای قبلی نیازی نداشته باشد، مجبور نیست کل زنجیره را ذخیره کند. در اینجا مفهوم لایت نود وارد عمل میشود. لایت نودها به جای ذخیره کل زنجیره و اجرای تمام تراکنشها، فقط زنجیره سربرگها را از بلاک جنسیس تا بلاک فعلی دانلود میکنند. از آنجا که لایت نودها هم به سربرگهای بلاک دسترسی دارند که حاوی درهمسازیهای سه درخت است، میتوانند به راحتی پاسخهای قابلاطمینانی درباره تراکنشها، رویدادها، موجودیها و نظایر آنها ایجاد و دریافت کنند.
گفتیم که درخت مرکل از پایین به بالا حرکت میکند، بههمیندلیل درهمسازیها روبهبالا گسترش مییابند و بنابراین کل این سیستم بدون اشکال کار میکند. اگر یک کاربر متخلف تلاش کند که یک تراکنش جعلی را در پایین درخت مرکل وارد کند، این تغییر سبب میشود هشِ نودِ بالای آن هم تغییر کند که آن هم سبب تغییری دیگر در هشِ نودِ بالایی آن خواهد شد و همینطور تا انتها ادامه مییابد تا زمانی که در نهایت ریشه درخت تغییر کند.
هر نود که میخواهد بخشی از دادهها را تأیید کند، میتواند از مفهوم «اثبات مرکل» (Merkle Proof) استفاده کند. اثبات مرکل از موارد زیر تشکیل شده است:
- قطعهای از دادهها به همراه هشهایشان که باید تأیید شوند؛
- هش ریشه درخت؛
- شاخه (شامل همه درهمسازیهای موجود در طول مسیر از قطعه موردنظر به سمت ریشه)
هرکس که بخواهد عمل اثبات را انجام دهد، میتواند تأیید کند که درهمسازی این شاخه شامل تمام طول مسیر به سمت بالای درخت است و بنابراین قطعه موردنظر واقعا در جایگاه درست خودش در درخت قرار دارد.
در مجموع، مزیت اسفاده از درخت مرکل پاتریسیا این است که نود ریشه آن به وسیله رمزنگاری به دادههای زیرین ذخیرهشده در این درخت وابسته است. بنابراین هش نود ریشه میتواند ایمنی این دادهها را تضمین کند. ازآنجاکه سربرگ بلاک حاوی هش ریشه هر سه درخت حالت، تراکنشها و رسیدهاست، هر نود میتواند بخش کوچکی از حالت اتریوم را اعتبارسنجی کند و لزومی ندارد که کل حالتهای اتریوم را (که به احتمال زیاد اندازهاش میتواند نامحدود باشد) ذخیره کند.
سوخت و پرداخت
یکی از مهمترین مفاهیم اتریوم مفهوم کارمزدهاست. هر محاسبهای که برای اجرای یک تراکنش در شبکه اتریوم انجام میشود هزینهای دارد. اینجا هیچ چیز رایگان نیست! این هزینه با سوخت یا گَس (Gas) پرداخت میشود.
- سوخت واحدی است که برای اندازهگیری هزینهها و کارمزدهای موردنیاز برای محاسبات خاص استفاده میشود.
- قیمت سوخت (gas price) مقدار اتری است که برای هر واحد سوخت پرداخت میکنید و به صورت جیوی (GWei) اندازهگیری میشود. وی کوچکترین واحد اتر است. هر اتر ۱۰۱۸ وی است. یک جیوی برابر با یک میلیارد وی است.
کسی که تراکنش را ارسال میکند، باید حد سوخت (gas limit) و قیمت سوخت را برای آن تعیین کند. حاصلضرب این دو مقدار، نشاندهنده بیشترین مقدار وی است که فرستنده میخواهد برای اجرای این تراکنش بپردازد.
برای مثال، فرض کنیم فرستنده حد سوخت را ۵۰,۰۰۰ و قیمت سوخت را ۲۰ جیوی تعیین میکند. این بدان معناست که بیشترین مقدار کارمزدی که فرستنده میخواهد برای اجرای این تراکنش بپردازد ۰٫۰۰۱ اتر است.
۵۰,۰۰۰ ۲۰ × gwei = ۱,۰۰۰,۰۰۰,۰۰۰,۰۰۰,۰۰۰ Wei = ۰٫۰۰۱ Ether
به یاد داشته باشید که حد سوخت نشاندهنده بیشترین مقدار سوختی است که فرستنده میخواهد برای اجرای تراکنش خرج کند. اگر این فرد در حسابش به اندازه کافی اتر داشته باشد و این مقدار بیشینه را پوشش دهد، همه چیز خوب پیش خواهد رفت. در انتهای تراکنش، سوخت اضافی استفادهنشده به فرستنده بازگردانده میشود.
در شرایطی که فرستنده سوخت موردنیاز برای انجام تراکنش را به اندازه کافی تأمین نکرده باشد، تراکنش با پیغام خطای «اتمام سوخت» مواجه شده و نامعتبر در نظر گرفته میشود. در این صورت تراکنش لغو شده و هر تغییری که در حالت اتفاق افتاده است به حالت قبل از ارسال تراکنش برگردانده میشود. علاوه بر این، یک سابقه از تراکنش ناموفق ثبت میشود که نشان میدهد چه تراکنشی و در کجا ناموفق بوده است. از آنجا که ماشین تلاش کرده بود محاسبات را انجام دهد، منطقی است که سوخت مصرفشده را دوباره به فرستنده پس نمیدهند.
این سوخت دقیقا کجا میرود؟ کل مبلغی که فرستنده برای سوخت مصرف میکند، به آدرس «ذینفع» فرستاده میشود که به طور معمول آدرس یک ماینر است. زیرا این ماینرها هستند که تلاش میکنند محاسبات را انجام دهند و تراکنشها را اعتبارسنجی کنند، از این رو بهعنوان پاداش کارمزد سوخت را برمیدارند.
به طور معمول، هر چه فرستنده قیمت اتریوم سوخت بالاتری پرداخت کند ارزشی که ماینر از تراکنش بهدست میآورد بیشتر میشود. به همین دلیل احتمال بیشتری دارد که ماینرها این تراکنش را انتخاب کنند. ماینرها میتوانند به انتخاب خودشان تعدادی از تراکنشها را اعتبارسنجی کنند یا آنها را نادیده بگیرند. همچنین میتوانند با تبلیغِ کمترین قیمت سوخت برای اجرای تراکنش، افرادی را که نمیدانند چه قیمتی را برای سوخت در نظر بگیرند راهنمایی کنند.
برای فضای ذخیرهسازی هم هزینههایی وجود دارد
سوخت پرداختی فقط مختص مراحل محاسباتی نیست و برای میزان مصرفِ فضای ذخیرهسازی هم باید مبلغی در نظر گرفته شود. کل هزینه برای فضای ذخیرهسازی، متناسب با حداقل فضای مورد استفاده بوده که مضربی از ۳۲ بایت است.
هزینههای پرداختی برای فضای ذخیرهسازی، گاهی اختلافات جزئی با هم دارند. برای مثال، از آنجا که افزایش فضای ذخیرهسازی سبب میشود اندازه پایگاه داده حالت اتریوم در همه نودها افزایش یابد، مشوقهایی هم برای آزادکردن فضای ذخیرهسازی با کوچک نگهداشتن دادهها در نظر گرفته شده است. مثلا اگر یک تراکنش مرحلهای داشته باشد که سبب شود یک ورودی در فضای ذخیرهسازی پاک شود، بابت آزادسازی فضای ذخیرهسازی مبلغی به ارسالکننده آن تراکنش تعلق میگیرد.
هدف از این هزینهها و کارمزدها چیست؟
در روشی که اتریوم با آن کار میکند، هر عملیات تکی که توسط شبکه اجرا میشود همزمان توسط هر نود کامل انجام میشود. اما مراحل محاسباتی در ماشین مجازی اتریوم بسیار گران تمام میشوند، بنابراین قراردادهای هوشمند اتریوم برای انجام کارهای سادهای مانند اجرای کسبوکارهای ساده، تایید امضاها و نظایر آن بسیار سودمندتر هستند تا برای کاربردهای پیچیدهتری مانند ذخیرهسازی فایل، ایمیل یا یادگیری ماشین که به شبکه فشار وارد میکنند. همین هزینههای اِعمالشده هستند که اجازه نمیدهند کاربران بیشازحد از شبکه کار بکشند.
اتریوم یک زبان تورینگ کامل است. (بهطور خلاصه، یک ماشین تورینگ ماشینی است که میتواند هر الگوریتم کامپیوتری را شبیهسازی کند.) اگر با ماشین تورینگ آشنا نیستید، میتوانید آن را مطالعه کنید. این زبان امکان استفاده از حلقهها را فراهم میکند و بههمیندلیل ممکن است اتریوم با مشکل هالتینگ (ایجاد وقفهها) مواجه شود. هنگامی که این مشکل رخ میدهد، شما نمیتوانید تعیین کنید که آیا یک برنامه بهشکل نامحدود اجرا خواهد شد یا نه. بنابراین اگر هیچ هزینهای در کار نبود، یک فرد متخلف میتوانست با اجرای یک حلقه پایانناپذیر درون یک تراکنش به آسانی شبکه را تخریب کند و واقعا هیچچیز نمیتوانست جلوی آن را بگیرد. این هزینهها هستند که از شبکه در برابر حملات خرابکارانه محافظت میکنند.
شاید بگویید اگر این هزینهها برای انجام محاسبات ضروری است، چرا باید برای فضای ذخیرهسازی هم هزینه پرداخت کنیم؟ زیرا ذخیرهسازی روی شبکه اتریوم هم درست مانند محاسباتش هزینههایی دارد و کل شبکه باید بار نگهداری اطلاعات را بکشد.
تراکنشها و پیامها
گفتیم که اتریوم یک ماشین حالت مبتنی بر تراکنش است. بهعبارت دیگر، تراکنشهایی که بین حسابهای مختلف رخ میدهند حالت جهانی اتریوم را از یک حالت به حالت دیگر منتقل میکنند. یک تراکنش در اصل قطعه دستورالعملی است که امضای رمزنگاری دارد و یک حساب با مالکیت خارجی آن را ایجاد، مرتب و به بلاک چین ارسال میکند.
در بلاک چین اتریوم دو نوع تراکنش وجود دارد: تراکنش برای فراخوانی پیام (message calls) و تراکنش برای ایجاد قرارداد (یعنی تراکنشهایی که قراردادهای جدید اتریوم را ایجاد میکنند).
همه تراکنشها صرفنظر از نوعشان، دربردارنده اجزای زیر هستند:
- نانس: شمار تراکنشهای ارسالشده توسط فرستنده؛
- قیمت سوخت: تعداد وِی (Wei) که فرستنده میخواهد برای هر واحد سوخت که برای اجرای تراکنش نیاز است بپردازد؛
- حد سوخت: بیشترین مقدار سوخت که فرستنده میخواهد برای اجرای این تراکنش بپردازد. این مقدار قبل از اینکه محاسبات انجام شود، تنظیم و پرداخت میشود.
- به (to): این بخش شامل آدرسِ گیرنده است. در تراکنشی که برای ایجاد قرارداد انجام میشود، آدرس حساب مبتنی بر قرارداد هنوز وجود ندارد و بنابراین مقدار آن خالی است.
- ارزش یا مقدار (value): مقدار وی که قرار است از فرستنده به گیرنده انتقال یابد. در تراکنشهای ایجادکننده قرارداد، این مقدار بهعنوان موجودی اولیه در حساب مبتنی بر قرارداد تازه ایجادشده به کار میرود.
- v, r, s: برای ایجاد امضایی که هویت فرستنده تراکنش را شناسایی میکند به کار میرود.
- اینیت (فقط برای تراکنشهای ایجادکننده قرارداد وجود دارد): قطعه کد ماشین مجازی اتریوم که برای مقداردهی اولیه حساب مبتنی بر قرارداد جدید به کار میرود. کد اینیت (init) فقط یک بار اجرا میشود و سپس دور انداخته میشود. هنگامیکه اینیت برای اولین بار اجرا میشود، کد حساب را ایجاد میکند که قطعه کدی است که بهشکل دائمی با حساب مبتنی بر قرارداد در ارتباط است.
- داده (فیلد اختیاری که فقط برای تراکنشهای فراخوانی پیام وجود دارد): دادههای ورودی (یعنی پارامترهایی) که برای فراخوانی پیام به کار میروند. برای مثال، اگر یک قرارداد هوشمند بهعنوان یک سرویس ثبتنام دامنه عمل میکند، یک فراخوانی برای آن قرارداد ممکن است شامل فیلدهای ورودی برای وارد کردن دامنه و آدرس IP (آیپی) باشد.
در بخش «حسابها» یاد گرفتیم که تراکنشها (هر دو نوع آن) همیشه از سوی حسابهای با مالکیت خارجی آغاز و به بلاک چین فرستاده میشوند. برای درک بهتر، اینطور به آن فکر کنید که تراکنشها پلی از دنیای خارجی به حالت داخلی اتریوم هستند.
اما این بدان معنا نیست که آن قراردادها نمیتوانند با قراردادهای دیگر ارتباط برقرار کنند. قراردادهای روی اتریوم میتوانند با یکدیگر به شکل داخلی مذاکره کنند. روشی که آنها برای انجام این کار استفاده میکنند، از طریق «پیامها» یا «تراکنشهای داخلی» است. این پیامها یا تراکنشهای داخلی مشابه تراکنشهای معمولی هستند، با این تفاوت بزرگ که آنها توسط قراردادها ایجاد میشوند نه حسابهای با مالکیت خارجی. آنها اشیایی مجازی هستند که برخلاف تراکنشهای خارجی بهشکل سری مرتب نمیشوند و فقط در محیط اجرایی اتریوم وجود دارند.
هنگامیکه یک قرارداد، تراکنشی داخلی را به قرارداد دیگری میفرستد، کد مرتبط با آن که در حساب مبتنی بر قرارداد گیرنده موجود است اجرا میشود.
در اینجا نکته مهمی وجود دارد: تراکنشها یا پیامهای داخلی حاوی حد سوخت نیستند. اگر یادتان باشد گفته بودیم که حد سوخت را حساب با مالکیت خارجی تعیین میکند. بنابراین حساب با مالکیت خارجی که آغازگر ارسال تراکنش است، از همان ابتدا باید حد سوخت را به اندازه کافی تعیین کرده باشد تا اگر قرار است علاوه بر اجرای تراکنش در بین کار عملیات فرعی مانند تراکنشهای داخلی و پیامهای «قرارداد به قرارداد» اجرا شوند، سوخت کافی موجود باشد. اگر در زنجیره تراکنشها و پیامها اجرای یک پیامِ بخصوص با اتمام سوخت مواجه شود، اجرای آن پیام و نیز هر پیام بعدی که در نتیجه آن ایجاد شده است برگشت میخورد. اما اجرای تراکنشهای اصلی (خارجی) لغو نمیشود.
بلاکها
همه تراکنشها با هم در بلاکها گروهبندی میشوند. یک بلاک چین شامل یک سری از بلاکهای حاوی تراکنشهاست که زنجیروار به هم متصلاند.
در اتریوم، یک بلاک از مؤلفههای زیر تشکیل شده است:
- سربرگ بلاک؛
- اطلاعاتی درباره مجموعه تراکنشهایی که در آن بلاک قرار دارند؛
- مجموعهای از سربرگهای بلاکهای دیگر برای اومِرهای بلاک فعلی.
اومر (Ommer) چیست؟
اومر بلاکی است که پدر آن همان پدرِ پدرِ بلاک فعلی است. بیایید نگاهی سریع به کاربرد اومرها بیندازیم و ببینیم اصلا چرا یک بلاک باید حاوی سربرگ بلاکهای اومرها باشد.
در روشی که اتریوم از آن استفاده میکند، زمان استخراج بلاک خیلی کوتاهتر (در حدود ۱۵ ثانیه) از بلاک چینهای دیگر مانند بلاک چین بیت کوین (در حدود ۱۰ دقیقه) است. این امر سبب میشود بررسی تراکنشها خیلی سریعتر انجام شود. بااینحال، زمان استخراج بلاک کوتاهتر معایبی هم دارد و یکی از آنها این است که ماینرهای بیشتری همزمان راهحل بلاکها را پیدا میکنند. در این هنگام باید بین دو بلاک به نحوی تصمیم گرفته شود که کدام یک وارد زنجیره اصلی شود. پس از این کار، یکی از بلاکها که رد شده است خارج از زنجیره باقی میماند و این بلاک همان بلاک اورفان (orphan) یا بلاک یتیم است.
هدف از اومرها پاداشدهی به ماینرهایی است که این بلاکهای یتیم را وارد کردهاند. اومرهایی که ماینرها وارد میکنند باید معتبر باشند، یعنی باید از نسل ششم (۶ بلاک قبلتر) یا نسلهای تازهتر از آن باشند. دلیلش این است که پس از شش فرزند، بلاکهای یتیم قدیمی میشوند و دیگر نمیتوان به آنها مراجعه کرد (زیرا وارد کردن تراکنشهای قدیمیتر ممکن است همه چیز را کمی پیچیدهتر کند).
بلاکهای اومر نسبت به بلاکهای کامل پاداش کمتری دریافت میکنند. بااینحال، همین مقدار پاداش هم مشوقی است که میتواند ماینرها را ترغیب کند برای رسیدن به آن، این بلاکهای یتیم را وارد کنند.
سربرگ بلاک
بیایید یک لحظه به بلاکها برگردیم. قبلا گفتیم که هر بلاک یک سربرگ بلاک دارد، اما این سربرگ بلاک دقیقا چیست؟
سربرگ بلاک، بخشی از بلاک است که دارای اجزای زیر است:
- هش پدر (parentHash): هش سربرگ بلاک پدر (همان چیزی که مجموعه بلاکها را به یک زنجیره تبدیل میکند)؛
- هش اومر (ommersHash): هش فهرست اومرهای بلاک فعلی؛
- ذینفع (beneficiary): آدرس حسابی که کارمزدهای استخراج این بلاک را دریافت میکند؛
- ریشه حالت (stateRoot): هشِ نودِ ریشه از درخت مرکلِ حالت (گفتیم که درخت حالت در سربرگ ذخیره میشود و تأیید این حالت را برای لایت کلاینتها آسان میکند)؛
- ریشه تراکنشها (transactionsRoot): هشِ نود ریشه درخت که حاوی تمام تراکنشهای فهرستشده در این بلاک است؛
- ریشه رسیدها (receiptsRoot): هشِ نود ریشه درخت که حاوی رسیدهای تمام تراکنشهای فهرستشده در این بلاک است؛
- بلوم لاگها (logsBloom): یک فیلتر بلوم (ساختار داده) که حاوی گزارش اطلاعات است (کاربرد فیلتر بلوم در کامپیوتر، کاهش بار سرور و در عین حال استفاده اندک از حافظه است)؛
- سختی (difficulty): سطح دشواری استخراج یک بلاک؛
- شماره (number): شماره بلاک فعلی (بهعنوانمثال شماره بلاک جنسیس صفر است؛ برای رسیدن به شماره بلاک بعدی باید شماره هر بلاک را یک واحد اضافه کنیم)؛
- حد سوخت (gasLimit): حد سوخت فعلی به ازای هر بلاک؛
- سوخت مورداستفاده (gasUsed): مجموع کل مقدار سوخت استفادهشده توسط تراکنشهایی که در این بلاک هستند؛
- برچسب زمانی (timestamp): واحد زمانی این بلاک از آغاز؛
- داده اضافی (extraData): هر اطلاعات اضافی که به این بلاک مربوط است؛
- میکسهش (mixHash): نوعی هش که همراه با نانس ثابت میکنند که روی این بلاک محاسبات کافی انجام شده است؛
- نانس (nonce): نوعی هش که وقتی با میکسهش ترکیب میشود، اثبات میکند که روی این بلاک محاسبات کافی انجام شده است.
توجه کنید که هر سربرگ بلاک حاوی سه ساختار درختی به شکل زیر است:
- درخت حالت (ریشه حالت)؛
- درخت تراکنشها (ریشه تراکنشها)؛
- درخت رسیدها (ریشه رسیدها).
این ساختارهای درختی در واقع همان درختان پاتریسیا مرکل هستند که قبلا دربارهاش بحث کردیم.
چند اصطلاح مهم دیگر هم وجود دارند که بهتر است همینجا با توضیح آنها آشنا شوید.
لاگها
اتریوم این امکان را فراهم میکند که از طریق لاگها بتوان تراکنشها و پیامهای مختلف را پیگیری کرد. یک قرارداد میتواند با تعریف «رویدادهایی» که باید ثبت شوند یک لاگ ایجاد کند. لاگ شامل موارد زیر است:
- آدرس حساب ایجادکننده لاگ؛
- یک سری موضوعات که هر یک نشاندهنده رویدادهای متفاوتی هستند که توسط این تراکنش انجام شدهاند؛
- هر داده مرتبط با این رویدادها.
لاگها در یک فیلتر بلوم ذخیره میشوند که به روشی کارآمد میتواند حجم بیپایانی از دادههای لاگ را ذخیره کند.
رسید تراکنش
لاگهای ذخیرهشده در سربرگ، از اطلاعات لاگ که در رسید تراکنش موجود است ایجاد میشوند. همانطور که پس از خرید در یک فروشگاه رسید دریافت میکنید، خرید اتریوم هم برای هر تراکنش یک رسید صادر میکند. هر رسید حاوی اطلاعات معینی درباره تراکنش است و موارد زیر را دربرمیگیرد:
- شماره بلاک؛
- هش بلاک؛
- هش تراکنش؛
- سوخت بهکاررفته در تراکنش فعلی؛
- کل سوخت استفادهشده در بلاک فعلی پس از انجام تراکنش؛
- لاگهای ایجادشده در طی تراکنش فعلی؛
- و موارد دیگر.
سختی بلاک
سختی یک بلاک بهمنظور ایجاد هماهنگی در زمان موردنیاز برای اعتبارسنجی بلاکها به کار میرود. سختی بلاک جنسیس ۱۳۱,۰۷۲ است. برای تعیین سختی بلاکهای بعدی فرمول خاصی استفاده میشود. اگر یک بلاک معین خیلی سریعتر از بلاک قبلی اعتبارسنجی شود، پروتکل اتریوم سختی بلاک را افزایش میدهد.
سختی بلاک روی نانس تأثیر میگذارد. نانس نوعی هش است که با استفاده از الگوریتم اثبات کار، در هنگام استخراج یک بلاک باید محاسبه شود.
رابطه بین سختی بلاک و نانس از طریق فرمول ریاضی زیر به دست میآید:
که در آن Hd همان سختی است.
تنها راه برای یافتن نانس هماهنگ با آستانه سختی، استفاده از الگوریتم اثبات کار است. زمان موردانتظار برای یافتن راهحل، متناسب با سختی بلاک است. هر چه سختی بیشتر باشد، یافتن نانس دشوارتر میشود و بنابراین اعتبارسنجی بلاک هم سختتر میشود؛ این نیز به نوبه خود زمان موردنیاز برای اعتبارسنجی یک بلاک جدید را افزایش میدهد. بنابراین با تنظیم سختی یک بلاک، این پروتکل میتواند زمان لازم برای اعتبارسنجی یک بلاک را تنظیم کند.
از سوی دیگر اگر زمان اعتبارسنجی طولانیتر شود، پروتکل میزان سختی را کاهش میدهد. طبق این روش، زمان اعتبارسنجی خودبهخود برای حفظ سرعت یکنواخت تنظیم میشود که بهطور متوسط برای یک بلاک ۱۵ ثانیه است.
اجرای تراکنش
حالا به یکی از پیچیدهترین بخشهای پروتکل اتریوم رسیدهایم: اجرای یک تراکنش. فرض کنیم شما یک تراکنش را برای اجرا به شبکه اتریوم ارسال میکنید. چه اتفاقی میافتد تا حالت اتریوم تغییر کند و تراکنش شما به آن اضافه شود؟
قبل از هر چیز، همه تراکنشها باید مجموعهای از شرایط موردنیاز را فراهم کرده باشند تا اجرا شوند. برخی از این شرایط عبارتند از:
- این تراکنش باید به درستی در فرمت RLP ارسال شده باشد. RLP به معنای پیشوند طول بازگشتی (Recursive Length Prefix) است. این فرمت، نوعی فرمت داده است که برای کدگذاری آرایههای تودرتوی دادههای دودویی (باینری) به کار میرود. RLP فرمتی است که اتریوم از آن برای مرتبکردن اشیاء استفاده میکند.
- امضای تراکنش معتبر.
- نانس تراکنش معتبر. میدانید که نانس یک حساب، تعداد تراکنشهای ارسالشده از آن حساب است. نانس تراکنش برای معتبر بودن باید با نانس حساب فرستنده برابر باشد.
- حد سوخت تراکنش باید بزرگتر یا مساوی سوخت داخلی (intrinsic gas) استفاده شده توسط تراکنش باشد. سوخت داخلی شامل موارد زیر است:
- هزینه از پیش تعیینشده به مقدار ۲۱,۰۰۰ واحد سوخت (gas) برای اجرای تراکنش؛
- مقدار سوخت برای دادههای ارسال شده با این تراکنش (۴ واحد سوخت برای هر بایت داده یا کد که برابر با صفر باشد و ۶۸ سوخت برای هر بایت از داده یا کد که مقدار آن غیرصفر باشد)
- اگر این تراکنش از نوع تراکنش ایجادکننده قرارداد باشد ۳۲,۰۰۰ واحد سوخت اضافی هم باید در نظر گرفته شود.
در حساب فرستنده باید به اندازه کافی اتر موجود باشد تا این هزینهها را به طور کامل پوشش دهد. محاسبه مقدار سوخت ساده است: اول، حد سوخت تراکنش در قیمت سوخت ضرب میشود تا بیشینه مبلغ سوخت تعیین شود. سپس این هزینه بیشینه به کل مقداری که فرستنده قرار است به گیرنده ارسال کند اضافه میشود. درست همین مبلغ یا بیشتر باید در حساب فرستنده موجود باشد.
اگر یک تراکنش همه موارد بالا را داشته باشد، وارد مرحله بعدی میشود.
در مرحله بعد، ابتدا هزینه اجرای تراکنش را که باید از قبل پرداخت شود از موجودی حساب فرستنده کم میکنیم و نانس حساب فرستنده را یک واحد افزایش میدهیم. در اینجا، میتوانیم سوخت باقیمانده را حساب کنیم که برابر است با حد سوخت کل برای این تراکنش منهای مقدار سوخت داخلی که دربارهاش صحبت کردیم.
در مرحله بعد، تراکنش شروع به اجرا میکند. در طی اجرای یک تراکنش، اتریوم یک حالت جانبی را پیگیری میکند. این حالت جانبی روشی برای نگهداری اطلاعات بهدستآمده در فرایند اجرای تراکنش است که بلافاصله پس از کاملشدن تراکنش به آن نیاز خواهیم داشت. این حالت شامل موارد زیر نیز هست:
- مجموعه دورریزشونده خودکار (Self-destruct set): مجموعهای از حسابها (در صورت وجود) که پس از تکمیل تراکنش دور ریخته میشود.
- لاگها: نقاط بررسی (چکپوینتهای) بایگانیشده و قابل اندیسگذاری از فرایند اجرای کد ماشین مجازی.
- بازپرداختیها: مبالغی که پس از انجام تراکنش به حساب فرستنده بازگردانده میشود. گفتیم که فضای ذخیرهسازی در اتریوم هزینه دارد و در ازای آزادسازی این فضا مبلغی را به فرستنده پرداخت میکنند. اتریوم با یک شمارنده بازپرداخت، حساب این مقادیر را نگه میدارد. این شمارنده از صفر آغاز میشود و هر بار که قرارداد چیزی را در فضای ذخیرهسازی پاک میکند افزایش مییابد.
در مرحله بعد، محاسبات مختلفی که برای اجرای تراکنش ضروری است بررسی میشوند.
هنگامیکه همه مراحل بررسی شدند و دیگر هیچ حالت نامعتبری وجود نداشت، حالت جدید با تعیین مقدار سوخت دستنخوردهای که باید به فرستنده بازگردانده شود به پایان میرسد. علاوه بر سوخت اضافی، فرستنده مقداری سوخت هم از محل فضای ذخیرهسازی که در بالا از آن صحبت کردیم دریافت میکند.
علاوه بر اضافی مبلغ سوخت که به فرستنده بازگردانده میشود، موارد زیر هم انجام میشوند:
- معادل سوخت پرداختشده به ماینرها اتر داده میشود؛
- سوخت استفادهشده توسط تراکنش به شمارنده سوخت بلاک اضافه میشود (این شمارنده حساب کل مقدار سوختی را که توسط همه تراکنشهای موجود در بلاک مصرف شده است نگه میدارد و در تایید اعتبار یک بلاک از آن استفاده میشود)؛
- همه حسابهای مجموعه دورریز شونده (اگر چیزی از آنها باقی مانده باشد) پاک میشوند زیرا دیگر به آنها نیازی نیست.
در نهایت، حالت جدید و مجموعهای از لاگهای ایجادشده توسط این تراکنش را خواهیم داشت.
حالا که اصول اجرای تراکنش را یاد گرفتهایم میتوانیم به تفاوتهای بین تراکنشهای ایجادکننده قرارداد و تراکنشهای فراخوانی پیام بپردازیم.
ایجاد قرارداد
همانطور که گفته شد در اتریوم دو نوع حساب داریم: حسابهای مبتنی بر قرارداد و حسابهای با مالکیت خارجی. وقتی میگوییم یک تراکنش ایجادکننده قرارداد است، منظورمان این است که هدف آن تراکنش ایجاد یک حساب مبتنی بر قرارداد جدید است.
برای ایجاد یک حساب مبتنی بر قرارداد جدید، ابتدا با استفاده از فرمول خاصی آدرس حساب جدید را مشخص میکنیم. سپس حساب جدید را به طریق زیر ایجاد میکنیم:
- نانس را صفر قرار میدهیم؛
- اگر فرستنده مقداری اتر به عنوان ارزش مورد مبادله تراکنش ارسال کرده بود، موجودی حساب را روی آن مبلغ تنظیم میکنیم؛
- مبلغ اضافهشده به موجودی حساب جدید را از موجودی حساب فرستنده کم میکنیم؛
- فضای ذخیرهسازی خالی را برای آن درنظر میگیریم؛
- کدهش قرارداد را به عنوان هش یک رشته خالی تنظیم میکنیم.
پس از مقداردهی اولیه حساب، میتوانیم این حساب را با استفاده از اینیت کد (init code) که با تراکنش فرستاده شده است (قبلا درباره آن در بخش تراکنشها و پیامها توضیح دادیم) ایجاد کنیم. چیزی که در طی اجرای اینیت کد اتفاق میافتد، بسته به شرایط متفاوت است. ممکن است فضای ذخیرهسازی حساب بهروزرسانی شود، حسابهای مبتنی بر قرارداد دیگری ایجاد شوند، فراخوانی پیام دیگری ایجاد شوند و نظایر آنها.
هنگامیکه این کد برای مقداردهی اولیه یک قرارداد اجرا میشود، سوخت مصرف میکند. یک تراکنش نمیتواند بیش از مقدار باقیمانده سوخت از آن استفاده کند. اما اگر سوخت به پایان رسیده باشد و هنوز تراکنش کامل نشده باشد، اعلام میشود که سوخت به پایان رسیده است و اجرای قرارداد متوقف میشود. اگر تراکنش بهدلیل اتمام سوخت لغو شود، حالت فعلی بلافاصله به نقطه قبل از اجرای تراکنش برمیگردد و فرستنده دیگر نمیتواند مبلغ سوخت پرداختی برای اجرای تراکنش را پس بگیرد.
بااینحال هر مقدار اتری که فرستنده با تراکنش فرستاده باشد، حتی در صورت عدم ایجاد قرارداد و اجرای تراکنش باز هم به او بازگردانده میشود. در واقع شما فقط سوختی را از دست میدهید که برای اجرای فرایند بررسی تراکنش مصرف شده است و مبلغ اتر فرستاده شده با تراکنش در هر صورت مال خودتان است.
اگر کد آغازکننده با موفقیت اجرا شود، هزینه نهایی ایجاد قرارداد پرداخت میشود. این هزینه برای فضای ذخیرهسازی است و متناسب با اندازه کد قرارداد ایجاد شده است (هیچ چیز اینجا رایگان نیست!). اگر هیچ سوختی برای این هزینه نهایی باقی نمانده باشد باز هم تراکنش بهدلیل اتمام سوخت لغو میشود.
اگر همه چیز خوب پیش برود و مشکلی پیش نیاید، در نهایت سوخت باقیمانده به فرستنده اصلی بازگردانده میشود و حالت جدید جایگزینشده باقی میماند.
فراخوانی پیام
اجرای یک فراخوانی پیام همانند ایجاد قرارداد است اما با آن تفاوتهایی جزئی دارد.
اجرای یک فراخوانی پیام نیازی به اینیت کد ندارد، چرا که هیچ حساب جدیدی قرار نیست ایجاد شود. با این حال، میتواند شامل دادههای ورودی باشد، آن هم به شرطی که این دادهها توسط فرستنده تراکنش ارائه شده باشند. فراخوانی پیام در هنگام اجرا ممکن است یک جزء اضافی حاوی دادههای خروجی هم داشته باشد. این زمانی است که قرار باشد به دنبال این تراکنش عمل دیگری هم انجام شود که به آن دادههای خروجی نیاز داشته باشد.
درست همانند ایجاد قرارداد، اگر اجرای یک فراخوانی پیام قبل از اجرای کامل بهدلیل اتمام سوخت یا نامعتبر بودن تراکنش (به عنوان مثال به خاطر سرریز پشته، مقصد نامعتبر یا دستورالعمل نادرست) لغو شود، سوخت پرداختی دوباره به فراخواندهنده اصلی بازگردانده نمیشود و حالت بلافاصله به حالت قبل از انتقال موجودی برمیگردد.
تا قبل از بهروزرسانی بیزانس اتریوم، هیچ راهی برای متوقفکردن یا بازگرداندن اجرای یک تراکنش تا زمانی که تمام سوخت مصرف شود وجود نداشت. برای مثال، فرض کنیم شما قراردادی نوشته بودید، اما گیرنده مجاز به انجام بعضی تراکنشها نبود و بنابراین تراکنش شما با خطا مواجه میشد. در نسخههای قبلی اتریوم، مقدار سوخت تا آخرین لحظه مصرف میشد و دیگر هیچ سوختی به شما بازگردانده نمیشد. اما نسخه بیزانس اتریوم شامل یک کد «برگشت» جدید است که به فرستنده قرارداد اجازه میدهد اجرای تراکنش را متوقف کند، تغییرات حالت را برگرداند و به این ترتیب از هدر رفتن سوخت برای تراکنشی که میداند دارد با شکست مواجه میشود جلوگیری کند. اگر یک تراکنش به دلیل این برگشت لغو شود، فرستنده میتواند مابقی سوخت استفاده نشده را دوباره دریافت کند.
مدل اجرا
تا اینجا، مراحل اجرای یک تراکنش از آغاز تا پایان را یاد گرفتیم. حالا میخواهیم ببینیم یک تراکنش در ماشین مجازی واقعا چگونه انجام میشود.
بخشی از پروتکل که بررسی تراکنشها را انجام میدهد، EVM یا ماشین مجازی اتریوم (Ethereum Virtual Machine) است.
ماشین مجازی اتریوم یک ماشین مجازی تورینگ کامل است. تنها محدودیت این ماشین مجازی این است که بهشکل ذاتی محدود به سوخت است. بنابراین کل محاسباتی که این ماشین میتواند انجام دهد با مقدار سوخت تعیینشده محدود میشود.
علاوه بر این، معماری ماشین مجازی اتریوم مبتنی بر پشته است. یک ماشین مبتنی بر پشته، کامپیوتری است که برای نگهداری مقادیر موقت از پشتهای استفاده میکند که مبنای کارش LIFO (last-in first out) است. LIFO یعنی آخرین ورودی زودتر از بقیه خارج میشود.
اندازه هر بخش پشته در ماشین مجازی اتریوم ۲۵۶ بیت و بیشترین اندازه پشته ۱۰۲۴ بیت است.
ماشین مجازی اتریوم حافظهای دارد که در آن بخشها بهعنوان آرایههایی از آدرسهای کلمهای ذخیره میشوند. این حافظه ناپایدار است، یعنی دائمی نیست.
ماشین مجازی اتریوم فضای ذخیرهسازی هم دارد. برخلاف حافظه، فضای ذخیرهسازی پایدار است و بهعنوان بخشی از حالت سیستم است. ماشین مجازی اتریوم، کد برنامه را بهشکل جداگانه در یک حافظه فقط خواندنی (ROM) مجازی ذخیره میکند که فقط از طریق دستورالعملهای ویژه میتوان به آن دسترسی داشت. به این شیوه، ماشین مجازی اتریوم با معماری فون نویمان که در آن کد برنامه در حافظه یا فضای ذخیرهسازی ذخیره میشود متفاوت است.
ماشین مجازی اتریوم زبان مخصوص خودش را دارد: «بایتکد EVM»
هنگامی که یک برنامهنویس قراردادهای هوشمندی را مینویسد که روی اتریوم اجرا میشوند، معمولا کدها را با یک زبان سطح بالا مانند سالیدیتی مینویسد. سپس میتواند آن را به زبان بایتکد ماشین مجازی اتریوم کامپایل کند که برای این ماشین قابلفهم باشد.
حالا بیایید به اجرا برگردیم.
قبل از انجام محاسبهای خاص، پردازنده باید اطمینان یابد که اطلاعات زیر موجود و معتبر هستند:
- حالت سیستم؛
- سوخت موردنیاز برای محاسبه؛
- آدرس حسابی که مالک کد در حال اجراست؛
- آدرس فرستنده تراکنشی که مبدأ این اجراست؛
- آدرس حسابی که موجب اجرای این کد میشود (ممکن است متفاوت از فرستنده اصلی باشد)؛
- قیمت سوخت تراکنشی که مبدأ این اجراست؛
- دادههای ورودی برای این اجرا؛
- مقداری (بهشکل وی) که در این حساب بهعنوان بخشی از اجرای فعلی قرار داده شده است؛
- کد ماشین برای اجرا؛
- سربرگ بلاک از بلاک فعلی؛
- عمق پشته مربوط به فراخوانی پیام یا ایجاد قرارداد فعلی؛
در آغاز فرایند اجرا، حافظه و پشته خالی هستند و شمارنده برنامه صفر است.
سپس ماشین مجازی اتریوم بهشکل بازگشتی تراکنش را اجرا میکند و برای هر حلقه حالت سیستم و حالت ماشین را محاسبه میکند. حالت ماشین همان حالت جهانی اتریوم است. حالت ماشین متشکل از موارد زیر است:
- سوخت موجود؛
- شمارنده برنامه؛
- محتوای حافظه؛
- تعداد معلوم کلمات در حافظه؛
- محتوای پشته؛
بخشهای پشته از انتهاییترین بخش چپ سری اضافه یا حذف میشوند.
در هر چرخه، مقدار موردنیاز سوخت از سوخت باقیمانده کم میشود و شمارنده برنامه افزایش مییابد.
در انتهای هر حلقه، سه احتمال وجود دارد:
- ماشین به یکی از حالتهای استثنا (به عنوان مثال سوخت ناکافی، دستورالعملهای نامعتبر، کافینبودن آیتمهای پشته، سرریز شدن پشته، مقصد پرش (JUMP) نامعتبر و نظایر آنها) میرسد و باید متوقف شود. بنابراین فرایند اجرا بدون هیچ تغییری رد میشود.
- توالی به پردازش ادامه میدهد و به حلقه بعدی میرسد.
- ماشین به توقف کنترلشده میرسد (پایان فرایند اجرا).
اگر اجرا با هیچ یک از حالات استثنایی مواجه نشود و به توقف کنترلشده یا عادی برسد، آنگاه ماشین «حالت برایند»، «سوخت باقیمانده پس از این اجرا»، «حالت جانبی حاصلشده» و «خروجی برایند» را تولید میکند.
همین حالا یکی از پیچیدهترین بخشهای اتریوم را گذراندیم. اگر حتی به طور کامل این بخش را متوجه نشدید، ایرادی ندارد. اصلا نیازی نیست که تمام زیر و بم جزئیات اجرا را درک کنید؛ مگر اینکه بخواهید در سطح بسیار پیشرفتهتری کار کنید.
چگونه یک بلاک نهایی میشود؟
در نهایت، باید به این موضوع بپردازیم که چگونه بلاکی که حاوی تعداد زیادی تراکنش است، نهایی میشود.
نهاییشدن میتواند بسته به اینکه آیا این بلاک جدید است یا از قبل وجود دارد، به معنای دو چیز متفاوت باشد. اگر این یک بلاک جدید باشد، منظور از نهاییشدن فرایند استخراج این بلاک است. اما اگر این بلاک موجود بوده است، منظور فرایند اعتبارسنجی این بلاک است. در هر مورد، ۴ شرط برای نهاییشدن یک بلاک وجود دارد:
۱) اعتبارسنجی اومرها (برای استخراج: تعیین اومرها)
هر بلاک اومر در سربرگ بلاک، باید یک سربرگ معتبر باشد و حتما باید در محدوده نسل ششم به پایین (پنجم، چهارم و …) بلاک فعلی قرار گیرد.
۲) اعتبارسنجی تراکنشها (برای استخراج: تعیین تراکنشها)
مقدار سوخت استفاده شده در بلاک باید با مجموع سوخت مصرفشده توسط تراکنشهایی که در بلاک فهرست شدهاند برابر باشد (اگر یادتان باشد، هنگام اجرای یک تراکنش شمارنده سوخت بلاک را نگه میداریم که همه آنها در نهایت مقدار کل سوخت مصرفشده توسط همه تراکنشهای موجود در این بلاک را مشخص میکنند).
۳) پاداشدهی (فقط درباره استخراج)
برای استخراج بلاک ۲ واحد اتر پاداش به آدرس ذینفع ارسال میشود. علاوهبرآن ذینفعِ بلاک فعلی برای هر اومر، به اندازه یک سیودوم پاداش بلاک فعلی را هم میگیرد. در نهایت، ذینفع بلاک اومر هم مقدار مشخصی پاداش میگیرد (برای محاسبه این مقدار مشخص، فرمول ویژهای وجود دارد).
۴) تأیید حالت و نانس (در استخراج: محاسبه حالت و نانس معتبر)
این مرحله برای اطمینان از انجام همه تراکنشها و تغییرات حالت برایند است. پس از اینکه پاداش بلاک تراکنش نهایی اعمال شد، تعریف بلاک جدید بهعنوان حالت نهایی صورت میگیرد. برای تأیید باید حالت نهایی را با درخت حالتی که در سربرگ ذخیره شده است مطابقت داد.
اثبات کار و استخراج
در بخش «بلاکها» مختصری به مفهوم سختی بلاک پرداختیم. الگوریتمی که به سختی بلاک معنا میدهد اثبات کار (PoW) است.
الگوریتم اثبات کار اتریوم اتهش (Ethash) نامیده میشود (قبلا بهعنوان داگر – هاشیموتو نامیده میشد).
تعریف رسمی این الگوریتم بهشکل زیر است:
اگر m میکسهش (mixHash)، n نانس و Hn سربرگ بلاک جدید باشد (به استثنای نانس و تابع درهمسازی مختلط که باید محاسبه شوند)، آنگاه Hn نانس سربرگ بلاک است و d همان دَگ (گراف جهتدار غیرمدور) است که مجموعهای بزرگ از دادههاست.
در بخش «بلاکها» درباره بخشهای مختلف موجود در یک سربرگ بلاک صحبت کردیم. دو مورد از آن اجزاء میکسهش و نانس بودند. گفتیم که:
- میکسهش، هشی است که همراه با نانس اثبات میکند که روی این بلاک محاسبات کافی انجام شده است؛
- نانس نیز هشی است که وقتی با میکسهش ترکیب میشود، اثبات میکند که محاسبات کافی روی این بلاک انجام شده است.
تابع اثبات کار برای ارزیابی این دو بخش به کار میرود.
اینکه چطور میتوان با استفاده از تابع اثبات کار دقیقا میکسهش و نانس را محاسبه کرد تا حدی پیچیده است و توضیح آن در این مطلب نمیگنجد. اما نحوه کار به صورت کلی به شکل زیر است:
یک «عبارت بازیابی (seed)» برای هر بلاک محاسبه میشود. این عبارت بازیابی در هر «دوره» به طول ۳۰,۰۰۰ بلاک تغییر میکند. عبارت بازیابی برای اولین دوره، درهمسازیِ یک سری متشکل از ۳۲ بایت با مقدار صفر است. اما برای هر دوره بعدی، درهمسازیِ درهمسازیِ عبارت بازیابی قبلی است. با استفاده از این عبارت بازیابی، یک نود میتواند یک «کَش یا حافظه پنهان» شبه تصادفی را محاسبه کند.
این حافظه پنهان بینهایت مفید است زیرا با همین حافظه پنهان است که میتوانیم لایت نودها را داشته باشیم. بدون وجود آن همه نودها باید فول نود میبودند. یاد گرفتیم که لایت نودها قابلیت این را دارند که یک تراکنش را بهشکلی کارامد تأیید کنند بدون اینکه مجبور باشند کل مجموعه دادههای بلاک چین را ذخیره کنند. یک لایت نود میتواند اعتبار یک تراکنش را بر اساس همین حافظه پنهان تأیید کند؛ زیرا حافظه پنهان میتواند هر چیزی را که برای تأیید یک بلاک نیاز است دوباره تولید کند.
با استفاده از این حافظه پنهان، یک نود میتواند «مجموعه داده» گراف جهتدار غیرمدور را تولید کند. هر آیتم در این مجموعه دادهها، به تعداد کمی از آیتمهای انتخابشده بهشکل شبه تصادفی از این حافظه پنهان بستگی دارند. برای اینکه یک ماینر باشید، باید کل این مجموعه داده را تولید کنید. همه کلاینتها و ماینرها این مجموعه داده را ذخیره میکنند، بهاینترتیب این مجموعه داده بهشکل خطی در طول زمان رشد میکند.
ماینرها میتوانند بهشکل تصادفی هر بخش از این مجموعه داده را بگیرند و با استفاده از یک تابع ریاضی آنها را درهمسازی کنند. یک ماینر بهطور مکرر یک تابع درهمسازی مختلط را تولید میکند تا زمانی که خروجی به نانس موردنظر برسد. هنگامیکه خروجی به این مقدار رسید، این نانس معتبر در نظر گرفته میشود و بلاک میتواند به زنجیره اضافه شود.
حفظ امنیت سیستم با استخراج
بهطورکلی، هدف اثبات کار این است که به روشی ایمنشده با رمزنگاری ثابت کند که مقدار مشخصی از محاسبات برای تولید خروجی (یعنی نانس) انجام شده است. دلیلش این است که هیچ راه بهتری به جز همین راه شمارش همه احتمالات برای یافتن یک نانس که نزدیک به آستانه موردنیاز باشد وجود ندارد. خروجی توابع هش تکرارشونده، توزیع یکریختی دارند و بنابراین میتوانیم اطمینان داشته باشیم که به طور متوسط، زمان موردنیاز برای یافتن چنین نانسی به آستانه سختی بستگی دارد.
هر چه سختی بیشتر باشد، یافتن نانس بیشتر طول میکشد. بهاینترتیب، الگوریتم اثبات کار به مفهوم سختی که برای تقویت امنیت بلاک چین به کار میرود معنا میدهد.
منظور از امنیت بلاک چین چیست؟ خیلی ساده است، ما میخواهیم بلاک چینی ایجاد کنیم که هر کسی به آن اعتماد داشته باشد. گفتیم که اگر بیش از یک زنجیره وجود داشته باشد، کاربران اعتمادشان را از دست خواهند داد؛ زیرا نمیتوانند بهشکل منطقی تعیین کنند که کدام زنجیره معتبر است. برای اینکه گروهی از کاربران حالت اصلی را که روی یک بلاک چین ذخیره شده است بپذیرند، به یک بلاک چین استاندارد یکتا که گروهی از مردم آن را باور داشته باشند نیاز داریم.
این دقیقا همان کاری است که الگوریتم اثبات کار انجام میدهد: این الگوریتم تضمین میکند که یک بلاک چین مشخص در آینده بهشکل استاندارد باقی خواهد ماند و سبب میشود که حمله برای یک فرد متخلف بینهایت سخت شود؛ زیرا در این صورت باید بخش معینی از تاریخچه را دوباره بنویسند (برای مثال، با پاک کردن تراکنشها یا ایجاد تراکنشهای جعلی) یا از یک فورک حمایت کنند. یک مهاجم برای اعتبارسنجی بلاکش باید ابتدا بهشکل مداوم نانس را سریعتر از هر کس دیگری در شبکه پیدا کند تا شبکه باور کند که زنجیره این مهاجم سنگینترین زنجیره است (بر مبنای اصول پروتکل شبح که قبلا دربارهاش صحبت کردیم). این هم غیرممکن خواهد بود مگر اینکه مهاجم، کنترل بیش از نیمی از قدرت استخراج شبکه را به دست آورد؛ سناریویی که بهعنوان حمله ۵۱ درصدی معروف است.
توزیع ثروت با استخراج
اثبات کار به جز ارائه یک بلاک چینِ ایمن، روشی برای توزیع ثروت بین کسانی که تجهیزات محاسباتی خودشان را برای ارائه این امنیت صرف میکنند نیز هست. اگر یادتان باشد، گفتیم که یک ماینر پاداشی برای استخراج یک بلاک دریافت میکند که شامل موارد زیر است:
- یک پاداش ثابت ۲ اتری برای بلاک برنده؛
- هزینه سوخت مصرفشده در بلاک توسط تراکنشهای موجود در بلاک؛
- یک پاداش اضافی برای وارد کردن اومرها بهعنوان بخشی از بلاک.
برای اطمینان از اینکه استفاده از سازوکار اجماع اثبات کار برای امنیت و توزیع ثروت در درازمدت پایدار باشد، اتریوم تلاش میکند تا دو ویژگی زیر را بهتدریج به این الگوریتم اضافه کند:
- در دسترس بودن برای هر تعداد از افراد. هدف این است که افراد نیاز نداشته باشند از سختافزاری خاص یا غیرمعمول برای اجرای این الگوریتم استفاده کنند. مدل توزیع ثروت تا حد ممکن برای همه باز است تا هر کس بتواند هر مقدار از قدرت محاسباتیاش را در ازای اتر ارائه کند.
- کاهش امکان ایجاد مقدار بیرویه سود برای هر نود یکتا. دلیلش این است که هر نود (یا مجموعه کوچک) که بتواند مقدار بیرویهای سود ایجاد کند، ممکن است که نفوذ زیادی در تعیین بلاک چین استاندارد پیدا کند. این امر امنیت شبکه را کاهش میدهد.
در شبکه بلاک چین بیت کوین مشکلی وجود دارد که دقیقا با همین دو ویژگی بالا در ارتباط است: الگوریتم اثبات کار آن، تابع درهمسازی (هش) SHA256 است. نقطهضعف این نوع درهمسازی این است که با استفاده از سختافزارهای خاص که به ایسیکها معروف هستند بسیار راحت حل میشوند.
برای حل این مشکل، اتریوم الگوریتم اثبات کارش یعنی ethash را بهشکل متوالی روی حافظه سخت انجام میدهد. این بدان معناست که این الگوریتم بهشکلی مهندسی شده است که محاسبه نانس به مقدار زیادی حافظه و پهنای باند نیاز داشته باشد. نیاز به فضای حافظه زیاد موجب میشود که یک کامپیوتر به سختی بتواند از حافظهاش برای کشف همزمان نانسهای متعدد استفاده کند و نیاز به پهنای باند زیاد سبب میشود حتی برای سریعترین کامپیوترها هم کشف چند نانس بهشکل همزمان دشوار باشد. این امر خطر متمرکزسازی را کاهش میدهد و به این ترتیب نودهای بیشتری میتوانند در شبکه حضور داشته باشند تا قدرت دست یک سری افراد خاص نیفتد.
چیزی که در اینجا باید به آن توجه کنید این است که درحالحاضر، اتریوم در حال گذار از سازوکار اجماع اثبات کار به اثبات سهام است.
نتیجهگیری
این مطلب کمی سنگین بود، قبول داریم. اگر بتوانید آن را چندبار بخوانید کاملا متوجه خواهید شد قضیه از چه قرار است. نویسنده این مطلب چندین بار یلو پیپر، وایت پیپر و بخشهای مختلف کد اتریوم را مطالعه کرده است و این مطلب را تا حد امکان به شکل سادهتر نوشته است. بااینحال اگر دوست دارید کمی عمیقتر مطالعه کنید، بهتر است یلو پیپر اتریوم را بخوانید.