ما «بلاکچین هوشمند» را اینطور تعریف می کنیم؛ هر بلاکچینی که از قراردادهای هوشمند برای راستی آزمایی تراکنش ها استفاده کند، یک «بلاکچین هوشمند» است. ما قبلا در چند مقاله، مزیت های «بلاکچین هوشمند» را بررسی کردیم و جزئیات این ایده را برای استفاده در بلاکچین های آینده شرح دادیم. اما سوال بعدی این است که آیا می توانیم یک قرارداد هوشمند را روی یک بلاکچین فعال (مانند اتریوم) دیپلوی کنیم؛ تا راستی آزمایی تراکنش ها را بدون نیاز به ماینرها انجام دهد؟ جواب مثبت است. ما در همین مقاله تصمیم داریم که یک قرارداد هوشمند اتریوم ایجاد کنیم؛ که می تواند از «دوبار خرج کردن» توکن های خودش جلوگیری کند.


دراین مقاله ابتدا روش تولید توکن با استاندارد ERC20 را توضیح می دهیم و یک قرارداد هوشمند برای تولید این نوع توکن ها می نویسیم. سپس همین قرارداد هوشمند را توسعه خواهیم داد تا بتواند از بدو تولد توکن ها، مشخصات همه حساب ها را ذخیره کند. یعنی علاوه بر تراکنش ها، مشخصات همه حساب هایی که حداقل یکبار از این نوع توکن ها دریافت کرده اند، ذخیره می شود. به این ترتیب، این قرارداد هوشمند در طول زمان می تواند با استفاده از مشخصات تراکنش ها، بالانس همه حساب ها (یعنی دفترکل مخصوص این نوع توکن) را محاسبه کند. ولی نکته مهم این است که این قرارداد روی شبکه غیر متمرکز اتریوم دیپلوی شده است و استیت قرارداد های هوشمند در این شبکه، فقط پس از اجماع همه ماینرها و ساخت یک بلاک جدید اتریوم، آپدیت می شود.


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


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







Understanding "Smart Blockchain" by coding



گام اول - تولید توکن های ERC20



درابتدا می خواهیم تعداد یک ملیون توکن ERC20 با نام «AKA» تولید کنیم. برای انجام این کار، یک قرارداد هوشمند مطابق استاندارد ERC20 نوشته ایم. شما می توانید همه کدهای این مقاله را از اینجا دانلود نمایید. ولی توجه نمایید که نام فایل ما دراین مرحله، SmartCreator101.sol می باشد. در ادامه جزئیات این قرارداد را با هم بررسی می کنیم.


پرکاربردترین قراردادهای هوشمند شبکه اتریوم، قراردادهای تولید کننده توکن های ERC20 هستند. خوشبختانه مقالات و کتابخانه های خوبی در این زمینه منتشر شده اند. حتی استانداردهای دیگری مانند ERC223 ، ERC621 ، ERC777 ، ERC827 برای تکمیل و توسعه استاندارد ERC20 وجود دارند. اما برای اینکه درک جزئیات توسعه قرارداد، برای خوانندگان راحت تر باشد، ما از import کردن استانداردها و کتابخانه ها پرهیز کردیم. ولی درعین حال، کتابخانه ضروری SafeMath ، فانکشن های اجباری، و ایونت های استاندارد ERC20 ، همه در متن قرارداد SmartCreator101.sol گنجانده شده است.





فرضیات اولیه قرارداد SmartCreator101.sol به این شرح است:



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



فانکشن های قرارداد SmartCreator101.sol به این شرح است:



فانکشن های totalSupply و balanceOf از نوع فانکشن های view هستند. این نوع فانکشن ها، فقط مقادیر را از روی بلاکچین می خوانند و آن مقادیر را برمی گردانند. به همین دلیل حتی هزینه gas هم ندارند. یعنی اگر کسی totalSupply را کال کند، تعداد کل توکن ها را دریافت می کند و اگر فانکشن balanceOf راهمراه یک آدرس کال کند، بالانس همان آدرس را دریافت خواهد کرد. این دو فانکشن هیچ محاسباتی انجام نمی دهند، ولی به هرحال از فانکشن های اجباری استاندارد ERC20 هستند و حتما باید به همین شکل و بدون تغییر در قرارداد باشند. به عنوان مثال اگر بخواهید تغییری در فانکشن balanceOf ایجاد کنید و قبل از مقدار بالانس، نام توکن را هم در خروجی فانکشن اضافه کنید؛ احتمالا کیف های پول ERC20 درست کار نمی کنند.


فانکشن بعدی transfer است. همیشه شخص فرستنده، این فانکشن را کال می کند و همزمان آدرس گیرنده و تعداد توکن های ارسالی را مشخص می نماید. سپس این فانکشن همه کنترل های لازم را انجام می دهد و اگر مشکلی پیدا نکرد‌، بالانس فرستنده و گیرنده را آپدیت می کند و به اصطلاح، ترانسفر کامل می شود. چون فرستنده این فانکشن را کال می کند، پس ‌خودش باید هزینه gas را پرداخت کند. ضمنا چون در فانکشن های دیگر هم باید عملیات ترانسفر را به همین شکل مدل کنیم، پس بهتر دیدیم که یک فانکشن دیگر به نام transfer_ بصورت اینترنال اضافه نماییم. به این ترتیب، فقط یکبار همه کنترل ها و محاسبات مربوط به ترانسفر را تایپ می کنیم. همچنین برای کنترل Overflow (هنگام عملیات جمع برای متغییرهای uint256) نیز فانکشین دیگری به نام add اضافه کردیم. این فانکشن اینترنال و pure است. یعنی اطلاعات بلاکچین را نمی خواند و هیچ چیز هم در بلاکچین ذخیره نمی کند.


سه فانکشن هنوز بررسی نشده اند. فانکشن approve وقتی کاربرد دارد که کسی بخواهد برای خرج کردن تعدادی از توکن های خودش، به شخص دیگری وکالت بدهد. برای اینکار او باید این فانکشن را کال کند و همزمان آدرس اسپاندر و تعداد توکن ها (allowance) را مشخص نماید. سپس این فانکشن، آدرس هولدر، آدرس اسپاندر و همچنین تعداد توکن ها را دریک مپینگ تو در تو، ذخیره می کند. نام فانکشن بعدی transferFrom است. این فانکشن توسط یک اسپاندرکال می شود و او همزمان باید، آدرس هولدر، آدرس recipient و تعداد توکن های ارسالی (کمتر یا مساوی allowance) را مشخص کند. البته این فانکشن نیز همه کنترل های لازم را انجام خواهد داد و اگر مشکلی وجود نداشته باشد‌، این فاکشن اجرا می شود و بالانس فرستنده (هولدر)، بالانس recipient ، و مقدار allowance را آپدیت می کند. لطفا توجه کنید که برای اجرای فانکشن approve ، هزینه gas توسط خود هولدر پرداخت می شود. اما برای اجرای فانکشن transferFrom ، هزینه gas باید توسط اجرا کننده قرارداد، یعنی اسپاندر پرداخت شود. به عبارت دیگر، با این که بالانس اسپاندر هنگام اجرای فانکشن approve ، ثابت است و تغییر نمی کند، ولی هنگام اجرای فانکشن transferFrom ، به مقدار هزینه gas کاهش می یابد. آخرین فانکشن، allowance نام دارد که یک فانکشن از نوع view می باشد. هر کس بدون پرداخت هزینه gas می تواند این فانکشن را کال کند و مقدار باقیمانده allowance را از روی بلاکچین بخواند. بدیهی است که هنگام کال کردن این فانکشن، می بایست آدرس هولدر و آدرس اسپاندر مورد نظر خود را مشخص کند.



ایونت های قرارداد SmartCreator101.sol به این شرح است:



نام ایونت اول، Transfer می باشد. پس از هر تراکنش موفق، این ایونت به محیط خارج قرارداد پیام می فرستد و مشخصات تراکنش در بلاکچین ذخیره می شود. پارامترهای این ایونت، آدرس فرستنده، آدرس recipient ، و تعداد توکن های ارسالی می باشند. نام ایونت دوم، Approval است. هر بار که فانکشن approve با موفقیت اجرا می شود، این ایونت نیز در بلاکچین ذخیره می گردد. ایونت دوم هم سه پارامتر دارد. پارامترهای این ایونت به ترتیب، آدرس هولدر، آدرس اسپاندر و مقدار allowance هستند.



تست قرارداد SmartCreator101.sol



حالا وقت این است که قرارداد خودمان را تست کنیم. برای این کار، ما از شبکه تست Rinkeby و MetaMask استفاده می کنیم. پس از کامپایل کردن قرارداد در ریمکیس، به بخش دیپلوی می رویم. سپس در باکس ENVIRONMENT ، باید گزینه Injected web3 را انتخاب کنیم. البته ما در شبکه تست Rinkeby چندین اکانت داریم. مثلا با اکانت شماره یک، قرارداد را دیپلوی میکنیم و مقادیر زیر را به قرارداد پاس می دهیم:



name = "AKA Token" ==> The name of the coins

symbol = "AKA" ==> Symbol of the coins

totalValue = 1000000 ==> Total number of coins





به این ترتیب آدرس اکانت شماره یک، owner این قرارداد خواهد بود و مطابق کدهای قرارداد، بالانس owner برابر یک میلیون آکا توکن ثبت می شود. قبل از اینکه تک تک فانکشن ها را تست کنیم، بهتر است آکا توکن را به کیف پول متامکس معرفی کنیم تا بتوانیم بالانس اکانت ها را همزمان در متامکس هم ببینیم. به اصطلاح باید آکا توکن را به کیف پول متامکس، add کنیم. برای این کار ابتدا از ریمکس، آدرس قرارداد دیپلوی شده را کپی کرده و در متامکس، این آدرس را روی باکس مخصوص پیست می کنیم. ضمنا سمبل توکن یعنی «AKA» را در باکس پائین تر تایپ می نمائیم. وقتی آکا توکن روی اکانت شماره یک add شود، متامکس بالانس این آکانت را یک میلیون آکا توکن نمایش می دهد.






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









گام دوم - توسعه قرارداد ERC20



همانطورکه قبلا گفته شد ما می خواهیم از بدو تولد توکن ها؛ علاوه بر اطلاعات تراکنش ها، سوابق همه آدرس هایی که حداقل یکبار «آکا توکن» دریافت کرده اند را ذخیره کنیم. به این ترتیب در طول زمان می توانیم با استفاده از مشخصات تراکنش ها، بالانس همه حساب ها را محاسبه نماییم. ضمنا خواهید دید که با استفاده از همین اطلاعات ذخیره شده، راستی آزمایی تراکنش ها و حل مشکل دوبار خرج کردن هم به سادگی انجام می شود. پس در این مرحله ما به استرات ها، مپینگ ها و محاسبات جدیدی نیاز داریم. از طرفی فراموش نکنیم که همواره باید استاندارد ERC20 رعایت شود.


برای انجام همه این کارها، یک روش این است که به صورت جداگانه یک قرارداد هوشمند سازنده بلاک BPSC را روی شبکه اتریوم دیپلوی نماییم و ازاین قرارداد در قرارداد موجود، استفاده کنیم. البته در این حالت دیپلوی قراردادها باید در دو مرحله انجام شود. (ابتدا مثلا باید قرارداد X دیپلوی شود و پس ازمشخص شدن آدرس آن، می توان این آدرس را در قرارداد Y ثبت کرد و نهایتا قرارداد Y را دیپلوی نمود) همچنین در این روش حتما باید موارد دیگری را نیز رعایت کنیم. اما هدف اصلی ما در این مقاله این است که درک جزئیات برای خوانندگان محترم ساده باشد. لذا برای گام دوم، ما یک قرارداد به نام SmartCreator102.sol آماده کرده ایم که برای ساحتن این فایل، همه موارد ضروری به متن قرارداد SmartCreator101.sol اضافه شده است و در حقیقت، فانکشن های فرارداد اول، در قرارداد دوم توسعه پیدا کرده اند.








مهمترین دیتاهای قرارداد جدید، در مپینگ accounts و استرات trxes ذخیره می شوند. مپینگ accounts همه اطلاعات مشتریان (بالانس و ....) را ذخیره می کند. حتی اگر آدرسی برای اولین بار «آکا توکن» دریافت کند و هنوز در این مپینگ عضو نباشد، بلافاصله همه مشخصات این اکانت به مپینگ accounts اضافه می گردد و این اکانت برای همیشه عضو می شود. استرات trxes مشخصات تراکنش ها را ذخیره می کند. عملکرد مپینگ تودرتو allowances در قرارداد جدید، دقیقا مانند قرارداد قبلی است. ضمنا تعدادی پارامتر دیگر هم به قرارداد جدید اضافه شده اند تا برنامه نویسی راحت تر انجام شود.


در قرارداد جدید؛ اولین مشخصاتی که در مپینگ accounts ذخیره می شود (مشتری شماره صفر)، مشخصات owner است. و اولین مشخصاتی که در استرات trxes ذخیره می شود (تراکنش شماره صفر)، مساوی شدن بالانس owner با تعداد کل توکن ها می باشد. ضمنا بقیه فانکشن های قرارداد قدیم و جدید، بسیار شباهت دارند. ولی فانکشن transfer_ استثناء است و تغییرات زیادی داشته است. اکثر وظایفی که از قرارداد جدید انتظار داریم، در همین فانکشن اتفاق می افتد. ما در ادامه عملکرد این فانکشن را دقیقا بررسی می کنیم و نگاه نزدیکتری به کدهای این فانکشن خواهیم داشت.



مشخصات فانکشن transfer_ و بررسی عملکرد آن در قرارداد جدید





همانطور که در تصویر بالا می بینید؛ تقریبا کدهای ابتدایی فانکشن transfer_ تغییری نکرده است. یعنی مشخصات فانکشن، و require های ابتدایی، مثل قرارداد اول هستند. فقط ما یک modifier جدید به نام isLocked به مشخصات این فانکشن اضافه کردیم. این modifier اجازه نمی دهد که همزمان دو آدرس متفاوت، این فانکشن را کال کنند.





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


در بخش A ؛ بالانس همه اکانت های ذخیره شده در قرارداد، با هم جمع می شوند و حاصل جمع در متغییر totalChecking ذخیره می گردد. فراموش نکنید که همیشه مبنای محاسبات، استیت همان لحظه قرارداد است که با آخرین بلاک شبکه اتریوم، آپدیت شده است.


بخش های C ، B و D ؛ فقط زمانی فعال می شوند که مقدار بدست آمده برای متغییر totalChecking با تعداد واقعی توکن های قرارداد، یعنی مقدار totalValue ، برابر نباشد. در این حالت ما متوجه می شویم که اشخاصی توانسته اند از غیرمتمرکز بودن شبکه استفاده کنند و یک یا چند تراکنش نامعتبر از توکن ما را، در بلاکچین اتریوم ثبت نمایند. به عبارت دیگر، اشخاصی با تقلب موفق شده اند که توکن های خود را دوبار خرج کنند. بنابراین برای اینکه بالانس همه حساب های این قرارداد تصحیح شوند، ما باید تراکنش های نامعتبر را شناسایی و حذف کنیم.



در بخش B ؛ بالانس تک تک حساب های قرارداد را برابر صفر می کنیم. البته شاید این کار ترسناک به نظر برسد، ولی خوشبختانه هیچ مشکلی وجود ندارد و ما هنوز اطلاعات همه تراکنش ها را داریم.


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


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



در بخش D ؛ بالانس همه اکانت ها (که در بخش C اصلاح شده اند) با هم جمع می شوند و حاصل جمع درهمان متغییر totalChecking ذخیره می گردد. به عبارت دیگر، مقداری که در بخش A برای متغییر totalChecking محاسبه شده بود، اصلاح می شود.


در بخش E ؛ فقط یک require وجود دارد. این require یکبار دیگر، برابری مقدار بدست آمده برای متغییر totalChecking را با تعداد واقعی توکن های قرارداد (totalValue) کنترل می نماید. لطفا توجه کنید که این شرط حتما محقق خواهد شد. زیرا متغییر totalChecking یکبار در بخش A محاسبه شد و اگر برابرtotalValue نبود، در بخش های C ، B و D اصلاح گردید و حالا دیگر حتما این شرط برقرار است.





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


در بخش F ؛ اگر گیرنده توکن ها برای اولین بار «آکا توکن» دریافت کند، تمام مشخصات گیرنده در مپینگ accounts ذخیره می شود. ضمنا بلافاصله ایونت NewAccountWasOpened اجرا می گردد.


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


بخش H ؛ بخش پایانی فانکشن transfer_ است. در این بخش تمام مشخصات تراکنش جدید، در استرات trxes ذخیره می گردد. ضمنا بلافاصله ایونت Transfer اجرا می گردد.



تست قرارداد SmartCreator102.sol



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


پیشنهاد ما این است که قبل از دیپلوی کردن قرارداد، تغییرات کوچکی در کدها ایجاد نماییم، تا بتوانیم بصورت مصنوعی پدیده دوبار خرج کردن را شبیه سازی کنیم. برای این کار، ما فایل SmartCreator102x.sol را ایجاد کرده ایم و در ادامه تغییرات ایجاد شده در این فایل را توضیح می دهیم. فراموش نکنید که این فایل صرفا برای تست است و فایل اصلی در این مرحله همان فایل SmartCreator102.sol می باشد. ضمنا برای اینکه توکن های این مرحله با توکن های مرحله اول متفاوت باشند، نام سمبل توکن های مرحله دوم را AKAx گذاشته ایم.



مثلا فرض کنید که بازهم تعداد کل توکن های مرحله دوم، یک میلیون توکن «آکا ایکس» باشد و owner قرارداد نیز آکانت شماره یک است. به این ترتیب پس از دیپلوی کردن قرارداد، بالانس آکانت شماره یک، برابر یک میلیون خواهد بود. در تراکنش اول، پانصد هزار توکن از آکانت اول به آکانت دوم ارسال می کنیم. در تراکنش دوم نیز، پانصد هزار توکن دیگر از آکانت اول به آکانت سوم ارسال می گردد. حالا فرض می کنیم که در تراکنش سوم تقلب یا اشتباهی صورت می گیرد و آکانت سوم موفق می شود که تمام پانصد هزار توکن خود را دوبار خرج کند. یعنی مثلا اکانت سوم بتواند یک میلیون توکن را به آکانت چهارم ارسال کند. بنابراین پس از تراکنش سوم، بالانس آکانت ها به شکل زیر خواهد بود:


Account balance number one = 0

Account balance number two = 500000

Account balance number three = 0

Account balance number four = 1000000


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


Account balance number one = 100000

Account balance number two = 400000

Account balance number three = 0

Account balance number four = 500000


البته با روش های مختلفی می توان امکان این نوع تقلب (یا اشتباه) را برای قرارداد ایجاد کرد. مثلا در فایل SmartCreator102x.sol برای اینکه تراکنش شماره سه، قابلیت دوبار خرج کردن را پیدا کند، ما فقط کدهای زیر را به فایل SmartCreator102.sol اضافه کرده ایم:





ما برای تست قرارداد در مرحله دوم؛ فایل SmartCreator102x.sol را مطابق مشخصاتی که قبلا گفته شد، روی شبکه تست Rinkeby دیپلوی کردیم. سپس مطابق توضیحات بالا، چهار تراکنش انجام دادیم. اگر به تصاویر زیر توجه کنید، خواهید دید که نتایج تست های انجام شده کاملا منطبق با پیش بینی ما هستند. البته شما هم می توانید تست های بیشتری انجام دهید تا از عملکرد این قرارداد مطمئن شوید.













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











گام سوم - اضافه کردن فانکشن های جدید و تکمیل قرارداد



برای تکمیل قرارداد، پنج فانکشن جدید به قرارداد مرحله دوم اضافه می کنیم. نام قرارداد ما در مرحله سوم SmartCreator103.sol است. توجه کنید که اطلاعات همه تراکنش ها، بالانس همه حساب ها و اطلاعات مهم دیگری در این قرارداد ذخیره می شوند. پس به همین دلیل سه تا از فانکشن های جدید از نوع view هستند و عملکرد آنها صرفا برگرداندن اطلاعات ذخیره شده در قرارداد است. ضمنا دو فانکشن دیگر نیز امکان فروش توکن ها براساس اتر را فراهم می کنند. در ادامه شرح مختصری از عملکرد این پنج فانکشن جدید را مطالعه کنید.





همانطورکه قبلا ملاحظه کردید؛ آدرس هایی که حداقل یکبار آکا توکن برای آنها ارسال شده است، به عنوان عضو شناخته می شوند و اطلاعات کامل حسابشان ( بالانس و ...) برای همیشه ذخیره شده و آپدیت می شود. حالا اگر یکی از اعضا، فانکشن myAccountInformation را کال کند، بدون پرداخت هزینه gas می تواند، بالانس حساب خودش، زمان عضو شدن خودش، زمان آخرین ارسال توکن توسط خودش و .... را دریافت کند.


اما فانکشن systemInformation را هر آدرس شبکه اتریوم می تواند کال کند. این فانکشن اطلاعات کلی از وضعیت توکن ها را بر می گرداند. یعنی به ترتیب؛ نام و سمبل توکن ها، تعداد کل توکن ها، قیمت فروش توکن براساس اتر(Wei)، موجودی آدرس قرارداد بر اساس اتر، تعداد کل اعضاء و حتی تعداد کل تراکنش های انجام شده را بر می گرداند.


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


فانکشن buyTokens از نوع payable است. هر آدرسی می تواند این فانکشن را اجرا کند و با پرداخت اتر، تعدادی آکا توکن خریداری کند. البته همانطور که می دانید از ابتدا همه آکا توکن ها درحساب owner هستند و آدرس قرارداد، توکنی ندارد. این فانکشن پس از کنترل های لازم برای این تراکنش، ابتدا اتر را از مشتری دریافت می کند و به آدرس قرارداد واریز می نماید و سپس آکا توکن ها را از آدرس owner به آدرس مشتری ترانسفر می کند.


اما فانکشن payToOwner فقط می تواند توسط owner اجرا شود. اگر با فروش آکا توکن ها، مقداری اتر در آدرس قرارداد جمع شود، owner می تواند این فانکشن را اجرا کند. با اجرای این فانکشن، همه اترهای جمع شده در آدرس قرارداد، به آدرس owner انتقال می یابد.



تست قرارداد SmartCreator103.sol



شما می توانید دقیقا مانند مراحل قبلی، فایل SmartCreator103.sol را نیز روی شبکه تست Rinkeby دیپلوی کنید و این پنج فانکشن جدید را هم تست کنید. فقط هنگام تست فانکشن buyTokens توجه داشته باشید که در کدهای این قرارداد به عنوان پیش فرض، قیمت هر آکا توکن برابر 1e12 wei در نظر گرفته شده است. از طرف دیگر در این قرارداد مقدار decimals برابر صفر است و هیچ کسری برای آکا توکن تعریف نشده است. پس مبلغ پرداختی براساس wei حتما باید مضرب 1e12 باشد. در غیر اینصورت فانکشن buyTokens اجرا نمی شود.



خلاصه و جمع بندی:



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


فایل SmartCreator101.sol یک قرارداد هوشمند است که مطابق استاندارد ERC20 توکن تولید می کند.


فایل SmartCreator102.sol یک قرارداد هوشمند دیگر است که علاوه بر تولید توکن های ERC20 ، تمام مشخصات تراکنش ها و مشخصات حساب اعضا را ذخیره می کند. ضمن این که این قرارداد می تواند راستی آزمایی تراکنش توکن های خودش را انجام دهد، و اجازه دوبار خرج کردن را به هیچکس نمی دهد. به عبارت دیگر این قرارداد برای راستی آزمایی تراکنش ها، احتیاجی به خدمات ماینرهای شبکه اتریوم ندارد. منطق ریاضی کدهای این قرارداد، بسیار محکمتر از منطق انواع مکانیسم های اجماع است و دفتر کل این قرارداد هوشمند، کاملا قابل اطمینان می باشد.


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


فایل SmartCreator103.sol نسخه نهایی این قرارداد هوشمند است. این قرارداد، پنج فانکشن بیشتر از قرارداد مرحله دوم دارد و با این فانکشن ها، امکانات جدیدی به قرارداد اضافه شده است.



کلام آخر



محدودیت های قراردادهای هوشمند اتریوم بسیار زیاد هستند و معمولا برای کاربردهای دیگری استفاده می شوند. ولی با این حال ما توانستیم قرارداد هوشمندی را روی این شبکه دیپلوی کنیم که بتواند تراکنش های نامعتبر را شناسایی کند. البته امیدواریم که بلاکچین های آینده از همان ابتدا برای استفاده از قراردادهای BPSC ( قراردادهای هوشمند سازنده بلاک) برنامه ریزی شوند و این نوع قراردادهای هوشمند بتوانند جایگزین بسیار ارزانی برای روش های فعلی باشند. ما قبلا در مقاله دیگری پیشنهاد «بلاکچین هوشمند» را برای بلاکچین های آینده بررسی کرده ایم.


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