عناوینی که در این مقاله می خوانید
ماشین حالت (State Machine) یک روش ساده، کاربردی و مناسب برای اجرای گردش کار در قراردادهای هوشمند بر پایه بلاکچین اتریوم (Solidity) است. با استفاده از Solidity، میتوان ویژگی تابعی را که برای بررسی وضعیت فعلی قبل از تغییر وضعیت ماشین حالت، مناسب است، پیادهسازی کرد. در این مقاله، قصد داریم درباره ماشین حالت در Solidity صحبت کنیم.
ماشین حالت همواره در یک حالت قرار دارد و با تغییر ورودی یا خروجی، به حالتهای مختلف منتقل میشود. در قراردادهای هوشمند Solidity، برای ایجاد تغییر در وضعیت، یک پیام از یک حساب دیگر به تابع قرارداد ارسال میشود. اگر ورودی برای وضعیت فعلی معتبر باشد، ماشین حالت به وضعیت جدید منتقل میشود. به عبارت دیگر، ماشین حالت برای کنترل گردش کار در قراردادهای Solidity، روشی ایدهآل و مناسب است.
یک قرارداد را در نظر بگیرید که در طول عمر خود از یک حالت اولیه، از طریق چند حالت میانی، به حالت نهایی خود تغییر میکند. در هر یک از این حالتها، قرارداد باید با رفتاری متفاوت عمل کند و عملکردهای مختلفی را برای کاربرانش فراهم کند. این الگوی رفتار را میتوان در موارد مختلفی مشاهده کرد، از جمله حراج، قمار، تامین مالی گروهی و غیره. حتی مستندات Solidity، با ارائه آن به عنوان یکی از الگوهای رایج، قدرت عمومی این الگو را تأیید میکند. وجود راههای مختلفی برای تبدیل یک حالت به حالت دیگر وجود دارد. گاهی اوقات یک حالت با پایان یک تابع به پایان میرسد، در حالی که در مواقع دیگر، تغییر حالت پس از گذشت زمان مشخصی رخ میدهد.
این الگوی عملکردی که در بالا توضیح داده شد، قبلاً توسط گروه گاما و همکارانش (1995) فرموله شده است، اما اجرای آن روی یک زنجیره بلوکی بسیار جالب است. دلیل این امر این است که خود زنجیره بلوک یک سیستم انتقال حالت است، به طوری که یک حالت اولیه با یک تراکنش ترکیب شده و یک حالت جدید به عنوان خروجی به دست میآید.
مدل ماشین حالت در Solidity
جابجایی ماشین حالت در قراردادهای Solidity از طریق توابع انتقال بین حالتهای تعریف شده انجام میشود. در تصویر زیر، یک بخش از قرارداد Solidity توسعه یافته قابل مشاهده است:
میتوان دید که حالت فعلی، حالتهای جدید و توابع انتقال به صورت مستقیم در نمودار ماشین حالت وارد میشوند. فردی که اطلاعات را دارد (Data Owner)، به آن معتبر است که دادهها و اطلاعات را در اختیار دارد. صاحب قرارداد میتواند با فردی که اطلاعات را دارد، متفاوت باشد. درخواست کننده اطلاعات یا Data Requester نیز به طرفی گفته میشود که تمایل به استفاده از اطلاعات دارد. قراردادهایی که از این مدل بهره میبرند، قابلیت پشتیبانی از این تبادلات و انتقالها را دارند.
نقشهای موجود در قراردادهای رایج
در قراردادهای رایج، این نقشها وجود دارند. از این رو، میتوان از کلاسهای پایه برای این نقشها و نقشهای رایج در دامنه استفاده کرد. استفاده از این روش میتواند برای آزمایش کد مفید باشد، اما برای تولید کد نهایی توصیه نمیشود.
کلاس پایه DataOwner شامل اقدامات کلی اصلاح کننده تابع (onlyDataOwner) و دارنده اطلاعات مانند سازنده (Constructor) است.
توابع دیگر مانند changeDataOwner نیز میتوانند برای کمک به توسعه سریعتر قراردادهای آزمایشی ارائه شوند. کلاس پایه DataRequester و سایر کلاسهای پایه مشابه همدیگر هستند. همچنین، میتوان اصلاحکنندههای تابع را از کلاسهای پایه به قرارداد اضافه کرد.
کلاسهای پایه DataRequester و DataOwner باید حتماً در ابتدای سازنده قرارداد قرار بگیرند. همچنین، به عنوان مولفه قرارداد، میتوان آدرس هر دو طرف یا یکی از طرفین را وارد کرد. در صورتی که مقدار حساب شامل مولفهای نباشد (یعنی مقدار آن صفر باشد)، باید از حساب msg.sender که دارنده قرارداد است، استفاده شود. البته، منطقی نیست که DataRequester و DataOwner یک حساب باشند، بنابراین باید شرایطی را در کد سازنده بررسی کرد.
ذخیره سازی اطلاعات در بلاکچین
در این بخش، باید درخواستهای درخواست کننده اطلاعات را ذخیره کرده و منتظر ماندن دارنده اطلاعات تا درخواستها را دریافت کند. همانطور که در تصویر زیر مشاهده میشود، هر سوال و پاسخ در یک رشته اطلاعات مشترک در قرارداد ذخیره شده است:
همچنین، امکان استفاده از یک رشته اطلاعات مشترک نیز وجود دارد، زیرا ماشین حالت اطمینان حاصل میکند که تنها یک استفاده از رشته در هر لحظه لازم است. همچنین، به دلیل هزینه بالای ذخیرهسازی، استفاده حداقلی از فضای ذخیرهسازی صورت میگیرد. ماشین حالت سلسله مراتبی استفاده میشود.
در راهکار فعلی، یکی از مسائل موجود این است که به هیچ وجه امکان فسخ قرارداد وجود ندارد و نمیتوان سرمایه را به دارنده قرارداد بازگرداند.
این مورد به سادگی با استفاده از وراثت (inheritance) از کلاس پایه ContractOwner در قرارداد قابل پیادهسازی است. همچنین، دارنده قرارداد به صورت خودکار ثبت میشود و تابع onlyContractOwner را نیز ارائه میدهد. تابع selfdestruct با استفاده از فسخ تابع، اتر را به حساب مورد نظر برمیگرداند.
برای آزمایش قرارداد، میتوان آن را در یک بلاکچین اجرا کرد. برای این منظور، اجرای قرارداد در شبکه آزمایشی مناسب است. همچنین، سازنده قرارداد دو پارامتر حساب DataOwner و حساب DataRequester را دریافت میکند. با ایجاد حسابهای پروکسی، میتوان آزمایش را به صورت خودکار تسهیل کرد و از آنها برای انجام اقدامات DataOwner و DataRequester استفاده کرد.
قرارداد پروکسی DataRequester، توابع setQuestion و getAnswer را ارائه میدهد. ایجاد حسابهای پروکسی و سپس قرارداد اصلی، توسط خود قرارداد آزمایش انجام میشود.
راهکار جایگزین برای استفاده از رویدادها
اگر کاربران تصمیم بگیرند قرارداد اصلی خود را پس از دریافت سوال یا پاسخ، به جایگزینی رویدادها حذف کنند، میتوان با استفاده از ماشین حالت این فرآیند را سادهتر کرد. همچنین، مدیریت و کنترل رویدادها توسط کد برنامه غیرمتمرکز انجام میشود و نیازی به قرارداد دیگری ندارد.
مشکلات ماشین حالت
یکی از مشکلات ماشین حالت این است که اگر درخواست کننده اطلاعات تا زمانی که سوال اول پاسخ داده نشدهاست، بخواهد سوال دوم را بپرسد، فرآیند متوقف میشود. این موضوع باعث میشود قرارداد به دلیل مسدود شدن بلااستفاده شود. البته روشهای مختلفی برای حل این مشکل وجود دارد. به عنوان مثال، اجرای چند قرارداد با استفاده از صف سوالات و پاسخها یکی از راهحلهای ممکن است.
سخن پایانی
در این مقاله به بررسی کاربردهای ماشین حالت در سالیدیتی پرداختی و همچنین مشکلات ماشین حالت پرداخته شد و به این نتیجه رسیدیم که آزمایش ماشین حالت و ماشین سالیدیتی آسان است. همچنین، مصرف کمتری نسبت به هزینه ذخیرهسازی اطلاعات دارد. البته، ذخیرهسازی اطلاعات برون زنجیره ممکن است هزینه کمتری داشته باشد.
نظرات کاربران