برنامه نویسی مبتنی بر شرط (COP) در زبان سالیدیتی (Solidity)
قبل از اینکه با برنامه نویسی مبتنی بر شرط (COP) آشنا شویم، ابتدا قرارداد هوشمند را از زبان انجمن تخصصی اتریوم خواهیم دید. سپس با مفهوم COP آشنا می شویم و در نهایت کاربرد COP در برنامه نویسی قراردادهای هوشمند را بررسی می کنیم.
قرارداد هوشمند چیست؟
انجمن اتریوم، قرارداد هوشمند (Smart Contract) را به این صورت تعریف کرده است:
“قرارداد هوشمند، یک برنامه ساده است که روی بلاک چین اجرا می شود. اسمارت کانترکت یک سورس کد به زبان سالیدیتی است که از دو بخش کد (Functions) و دیتا (State) تشکیل شده است. قرارداد هوشمند در یک آدرس مشخصی روی بلاک چین قرار می گیرد و نوعی اکانت اتریوم محسوب می شود که دارای موجودی بوده و می تواند روی شبکه بلاک چین تراکنش (Transaction) ارسال کند.”
بخش کد اسمارت کانترکت شامل فانکشن هایی می شود که در یک قرارداد سالیدیتی تعریف می شود و می توان آن فانکشن ها را فراخوانی کرد تا عمل خاصی را روی شبکه بلاک چین انجام دهند. فانشکن ها بر حسب کاری که انجام می دهند به دو دسته تقسیم می شوند:
- Call : خواندن اطلاعات بلاک چین
- Transaction: نوشتن اطلاعات در بلاک چین
با فراخوانی فانکشن هایی که دیتایی را روی بلاک چین می نویسند، یک تراکنش (Transaction ) روی شبکه بلاکچین ایجاد می شود.
مدلسازی اتریوم با ماشین حالت (State Machine)
ماشین حالت یک مدل ریاضیاتی برای محاسبات است. در این مدلسازی، یک مسئله محاسباتی به مجموعه ای از حالت (State) ها شکافته می شود. با انجام هر محاسبات، سیستم می تواند از یک state به state دیگر تغییر حالت (Transition) داشته باشد.
اتریوم به خاطر استفاده از ساختار قرارداد هوشمند، مثل بیت کوین از لجر توزیع شده (DLT) استفاده نمی کند. بلکه به جای آن از تکنولوژی ماشین حالت استفاده می کند. در اتریوم به جای DLT، با مفهوم ماشین حالت توزیع شده (DSM) سر و کار داریم. بنابراین کل دیتای موجود روی شبکه بلاک چین اتریوم را می توان به صورت یک ماشین حالت توزیع شده در نظر گرفت که این ساختمان داده پیشرفته Ethereum State نامیده می شود.
قراردادهای هوشمند تغییرات و تأثیرات اکانت ها روی Ethereum State را تحت کنترل دارند. اگر دیتای تحت کنترل یک قرارداد هوشمند، روی شبکه اتریوم را به صورت یک حالت (State) در نظر بگیریم، با صدور هر تراکنش (Transaction) به دیتای این قرارداد هوشمند، ماشین حالت، تغییر استیت (Transition) خواهد داشت.
برنامه نویسی مبتنی بر شرط (COP)
برنامه نویسی مبتنی بر شرط (Conditional-Oriented Programming) یا COP زیر دامنه ای از برنامه نویسی مبتنی بر قرارداد می باشد که می توان آن را ترکیبی از برنامه نویسی تابعی (Functional) و برنامه نویسی امری (imperative) دانست. COP اسم زبان برنامه نویسی خاصی نیست و قاعده و سیسنتکس خاصی را مشخص نمی کند. بلکه یک الگوی برنامه نویسی امن محسوب می شود. سبک COP، روش مطمئن برای بالا بردن امنیت قراردادهای هوشمند محسوب می شود. این سبک برنامه نویسی کمک می کند تا کد قرارداد شما به طور قابل توجهی قابلیت بازنگری (Audit) و اثبات درستی (Test) را داشته باشد و به صورت محاوره ای و سلیقه ای نباشد.
توصیه COP این است که بدنه تابع نباید حاوی مسیر شرطی (Conditional Path) باشد. مسیر شرطی به حالتی گفته می شود که مسیر اجرای برنامه (قرارداد هوشمند) توسط دستورات شرطی (if و …) استفاده شده داخل یک فانکشن از اسمارت کانترکت، قابل تغییر باشد. به عبارت دیگر نباید تغییر حالت (Transition) نباید توسط دستورات شرطی مثل if داخل فانکشن کنترل شود، بلکه مسیر شرطی باید از بدنه تابع جدا شده و به طور مستقل، خارج از بدنه تابع پیاده سازی شود.
با توجه به مدل سازی اتریوم در بخش قبلی مقاله، تراکنش ها (Transaction) یا توابعی که منجر به صدور یک تراکنش می شوند، حالت (State) اتریوم را تغییر می دهند. بنابراین کار آنها یک تغییر حالت (Transition) محسوب می شود و نباید تراکنش ها را توسط if داخل فانکشن کنترل کرد.
یکی از زبان های برنامه نویسی که پیشنهادات الگوی COP را پیاده سازی کرده، زبان برنامه نویسی Solidity است که برای برنامه نویسی قراردادهای هوشمند اتریوم ارائه شده است. در بخش های بعدی با زبان سالیدیتی و ابزارهای این زبان برای رعایت الگوی امن COP آشنا خواهیم شد.
زبان برنامه نویسی سالیدیتی
زبان سالیدیتی یک زبان شی گرا و سطح بالا برای توسعه قراردادهای هوشمند قابل اجرا روی پلتفرم یا معماری EVM است. بنابراین قراردادهای هوشمند نوشته شده به زبان سالیدیتی، علاوه بر شبکه بلاک چین اتریوم، روی تمام شبکه های بلاک چین EVM-Compatible قابل اجرا خواهد بود. یک قرارداد هوشمند ساده در زبان سالیدیتی به اینصورت پیاده سازی می شود.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.4.16 <0.9.0; contract SimpleStorage { uint storedData; function set(uint x) public { storedData = x; } function get() public view returns (uint) { return storedData; } } |
این قرارداد هوشمند دارای یک متغیر state به نام simpleStorage است. همچنین دو فانکشن به نام set و get در این قرارداد هوشمند تعریف شده است. سالیدیتی برای رعایت الگوی COP، ویژگی مادیفایر (modifier) را در اختیار برنامه نویس قرار می دهد. برای آشنایی بیشتر با مادیفایر و ویژگی های دیگر زبان سالیدیتی می توانید دوره رایگان سالیدیتی علوم نوین امیرکبیر را که توسط اساتید متخصص این مجموعه تهیه شده مشاهده نمایید.
رعایت اصول COP در زبان سالیدیتی
اگر قبلا دوره آموزش سالیدیتی را سپری کرده باشید و با زبان سالیدییی به صورت تخصصی کار کرده باشید، قطعاً ناخود آگاه، قواعد COP را رعایت کرده اید. ولی بد نیست با اصول COP در سالیدیت بیشتر آشنا شوید و با درک عمیق تری، الگوی COP را در برنامه نویسی قرارداد هوشمند و ساخت توکن به کار بگیرید.
به عنوان مثال، سورس کد یک توکن ساده در سالیدیتی را در نظر گرفته ایم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <=0.8.13; contract Token { mapping (address => uint) public balances; constructor() { balances[msg.sender] = 1000000; } function transfer(uint _amount, address _dest) public { balances[msg.sender] -= _amount; balances[_dest] += _amount; } } |
توجه داشته باشید که تابع transfer در این کد باگ دارد. sender برای اینکه بتواند مبلغی را برای گیرنده انتقال دهد باید موجودی کافی داشته باشد. پس قبل از اینکه انتقال صورت بگیرد ابتدا باید موجودی را چک کنیم. برای رفع این باگ در سینتکس امری، کافی است یک دستور if قرار دهیم که قبل از اجرای انتقال، موجودی حساب sender را بررسی کند:
1 2 3 |
if (balances[msg.sender] >= _amount) { ... } |
یا می توان به اینصورت نوشت:
1 2 |
if (balances[msg.sender] < _amount) return; |
توجه داشته باشید که هر دو راه حل بالا، بر خلاق استاندارد COP هستند. COP می گوید بدنه تابع نباید حاوی مسیرهای شرطی باشد. برای رعایت استاندار COP این شرط را در قالب یک مادیفایر پیاده سازی خواهیم کرد:
1 2 3 4 |
modifier only_with_at_least(uint x) { if (balances[msg.sender] >= x) _; } |
بنابر این پیاده سازی تابع transfer با استاندار COP به اینصورت خواهد بود:
1 2 3 4 |
function transfer(uint _amount, address _dest) public only_with_at_least(_amount) { balances[msg.sender] -= _amount; balances[_dest] += _amount; } |
در واقع شرط بررسی کافی بودن موجودی sender را از داخل تابع برداشته و به یک modifire انتقال دادیم. سپس از نام modifire نوشته شده در امضای تابع استفاده کردیم. سورس کد نهایی توکن پیاده سازی شده طبق استاندارد COP به اینصورت خواهد بود:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <=0.8.13; contract Token { mapping (address => uint) public balances; constructor() { balances[msg.sender] = 1000000; } modifier only_with_at_least(uint x) { if (balances[msg.sender] >= x) _; } function transfer(uint _amount, address _dest) public only_with_at_least(_amount) { balances[msg.sender] -= _amount; balances[_dest] += _amount; } } |
معرفی دوره آموزش بلاک چین
با آموزش های تخصصی پیاده سازی قرارداد هوشمند امن و بهینه با ما همراه باشید. برای یادگیری پیاده سازی اصولی قرارداد هوشمند و توسعه توکن در بستر اتریوم می توانید در دوره آموزش برنامه نویسی بلاکچین و قرارداد هوشمند که در مرکز آموزش علوم نوین امیرکبیر برگزار می شود شرکت نمایید.
درباره مجید شبیری
کارشناس ارشد فناوری اطلاعات از دانشگاه صنعتی امیرکبیر. مدیر و مؤسس "علوم نوین امیرکبیر"، متخصص برنامه نویسی، شبکه، لینوکس و امنیت. از سال 84 همزمان با شروع تحصیلات دانشگاهی، وارد حوزه تخصصی مهندسی نرم افزار شدم و اکنون مشغول تحقیق، توسعه و آموزش در حوزه بلاک چین هستم و معتقدم بلاکچین به زودی فضای کسب و کارها را منقلب خواهد کرد.
نوشته های بیشتر از مجید شبیری
دیدگاهتان را بنویسید