کنکاش در قرارداد اتریوم ABI و EVM Bytevode

قرارداد ABI

قرارداد اتریوم ABI و EVM Bytevode

از آنجایی که اتریوم از EVM (ماشین مجازی اتریوم) به عنوان قلب سیستم استفاده می کند، کد قرارداد هوشمند نوشته شده به زبان های سطح بالا باید در بایت کد EVM و برای اجرا در Contract ABI کامپایل شود، که درک آن ها هنگام تعامل با قرارداد هوشمند ضروری است.

با مطالعه این مقاله می توانید درک صحیحی از مفاهیم زیر داشته باشید:

  • درک قرارداد ABI و بایت کد EVM و روابط آن
  • نحوه تولید قرارداد بایت کد ABI و EVM با خط فرمان “solc”

بایت کد و ABI

از آنجایی که اتریوم از EVM (ماشین مجازی اتریوم) به عنوان یک جزء اصلی شبکه استفاده می کند، کد قرارداد هوشمند نوشته شده به زبان های سطح بالا باید در بایت کد EVM کامپایل شده تا اجرا شود. EMV Bytecode یک کد اجرایی در EVM است و Contract ABI یک رابط برای تعامل با بایت کد EVM است. به عنوان مثال، اگر می خواهید تابعی را در یک قرارداد هوشمند با کد جاوا اسکریپت خود فراخوانی کنید، ABI به عنوان یک واسطه بین کد جاوا اسکریپت و بایت کد EVM برای تعامل با یکدیگر ایفای نقش می کند. نمودار زیر معماری قرارداد ABI، بایت کد EVM و اجزای خارجی (DApp و شبکه) را نشان می دهد. سمت چپ نشان دهندۀ یک فرایند کامپایل است و سمت راست تعامل بین بایت کد و ABI را نشان می دهد.

معماری قرارداد ABI، بایت کد EVM و اجزای خارجی

بایت کد EVM (بایت کد)

بایت کد EVM یک زبان برنامه نویسی سطح پایین است که از یک زبان برنامه نویسی سطح بالا مانند solidity کامپایل شده است. EVM یک ماشین مجازی است که بین سیستم عامل و لایه برنامه قرار می گیرد تا وابستگی سیستم عامل را کاهش دهد. خوشبختانه EVM قرارداد هوشمند اتریوم را، می توان تقریباً روی هر کامپیوتری اجرا کرد. اگر شما یک توسعه دهنده جاوا هستید، می توانید JVM (ماشین مجازی جاوا) را به عنوان مکانیزم مشابه در نظر بگیرید. بایت کد EVM مانند زیر است. برای انسان قابل خواندن نیست اما برای دستگاه قابل خواندن است.

6080604052348015600f57600080fd5b5060878061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063037a417c14602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000600190509056fea265627a7a7230582050d33093e20eb388eec760ca84ba30ec42dadbdeb8edf5cd8b261e89b8d4279264736f6c634300050a0032

در نگاهی عمیق تر، هنگام کامپایل در Remix IDE، چهار فیلد مانند زیر را مشاهده می کنید. آن ها جزئیات بیشتری از بایت کد؛ مانند لینک مرجع، کدهای عملیاتی و نقشه منبع را نشان می دهند. “object” یک بایت کد EVM است.

  1. لینک مرجع (referenceLink): آدرس مستقر شده آدرس سایر قراردادهای هوشمند که قرارداد هوشمند فعلی به آن ها وابسته است.
  2. شیء (object): بایت کد قرارداد هوشمند فعلی
  3. کدهای عملیاتی (opcodes): کدهای عملیاتی که دستورالعمل های سطح پایین قابل خواندن توسط انسان هستند.
  4. sourceMap: منبع نقشه برای تطبیق هر دستورالعمل قرارداد با بخشی از کد منبع است که از آن تولید شده است.

قرارداد ABI

در علوم کامپیوتر، ABI (Application Binary Interface) یک رابط بین دو ماژول برنامه، اغلب بین یک سیستم عامل و برنامه های کاربر است. در اتریوم، Contract ABI رابطی است که یک طرح استاندارد از نحوه فراخوانی توابع در یک قرارداد هوشمند و بازگرداندن داده ها را تعریف می کند. Contract ABI برای استفاده خارجی طراحی شده است تا تعامل برنامه به قرارداد، و قرارداد با قرارداد را امکان پذیر کند. برای مثال، اگر می‌ خواهید یک تابع قرارداد هوشمند را از DApp خود فراخوانی کنید، از طریق Contract ABI تماس می‌ گیرید. قرارداد ABI با فرمت JSON مانند زیر نشان داده شده است.

[
2.	   {
    
3.	      "constant":true,
4.	      "inputs":[
5.	      ],
6.	      "name":"testFunc",
7.	      "outputs":[
8.	         {
9.	            "name":"",
10.	            "type":"int256"
11.	         }
12.	      ],
13.	      "payable":false,
14.	      "stateMutability":"pure",
15.	      "type":"function"
16.	   }
17.	]

قرارداد ABI نام تابع و انواع داده های آرگومان را تعریف می کند. برای رمزگذاری فراخوانی های قراردادی برای EVM و خواندن داده ها، از تراکنش ها استفاده می شود. مشخصات واضحی در مورد نحوه رمزگذاری و رمزگشایی Contract ABI وجود دارد. ما از تابع زیر برای توصیف مثال رمزگذاری استفاده خواهیم کرد.

function withdraw(uint withdraw_amount) public {}

ابتدا تابع ” withdraw” با keccak256 کدگذاری شده و 4 بایت اول به عنوان انتخابگر (selector) استفاده می شود. انتخابگر علامتی است که برای شناسایی تابعی که باید فراخوانی شود؛ استفاده می شود.

// Encode function with keccak256.
> web3.utils.sha3(“withdraw(uint256)”)
0x2e1a7d4d13322e7b96f9a57413e1525c250fb7a9021cf91d1540d5b69f16a49f// Extract first 4 bytes.
0x2c1a7d4d

در مرحله بعد، آرگومان به صورت هگزادسیمال کدگذاری می شود و به تابع رمزگذاری شده « withdraw» با 32 بایت padding اضافه می شود.

// Convert from ETH to Wei.
> withdraw_amount = web3.utils.toWei(“0.01", “ether”);
10000000000000000// Convert Wei with hexdecimal.
> withdraw_amount_hex = web3.toHex(withdraw_mount);
0x2386f26fc10000// Left padding.
> withdraw_amount_padleft = web3.utils.leftPad(withdraw_amount_hex, 32);
0x0000000000000000002386f26fc10000// Append to selector(encoded function).
“0x2c1a7d4d” + withdraw_amount_padleft// Final encoded ABI.
0x2c1a7d4d0x0000000000000000002386f26fc10000

داده ها تابع withdraw را فراخوانی می کنند و 0.01 را به عنوان آرگومان درخواست می کنند. اگر می‌خواهید جزئیات مربوط به مشخصات کدگذاری/رمزگشایی ABI را بدانید، لطفاً به مشخصات قرارداد ABI مراجعه کنید (https://docs.soliditylang.org/en/latest/abi-spec.html).

با یک نگاه عملی تر، در تعامل با قرارداد، می توانید از web3.js مانند زیر استفاده کنید. اول ایجاد یک قرارداد با Contract ABI، و بعد ایجاد یک نمونه با بایت کد EVM. این کد با موفقیت در کامپایل توسط Solidity REMIX تولید می شود.

1.var samplecontractContract = web3.eth.contract([{"constant":true,"inputs":[],"name":"testFunc","outputs":[{"name":"","type":"int256"}],"payable":false,"stateMutability":"pure","type":"function"}]);
2. var samplecontract = samplecontractContract.new(
3. {
4. from: web3.eth.accounts[0],
5. data: '0x6080604052348015600f57600080fd5b50607e8061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063037a417c14602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000600190509056fea165627a7a72305820e710d7394e9965c17ead6bb53757a23caee28d75a0a02b483638015a49dac6070029', gas: '4700000' }, function (e, contract){ console.log(e, contract); if (typeof contract.address !== 'undefined') { console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash); } })
6. gas: '4700000'
7. }, function (e, contract){
8. console.log(e, contract);

9.if(typeofcontract.address!=='undefined'){

10. console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);
11. }
12. })

دستورات با “solc”

نگاهی به چگونگی تولید بایت کد ABI و EVM با دستور solc بیندازیم. دستور Solc یکی از پرکاربردترین کامپایلرها است. آن را با مدیریت بسته npm نصب کنیم.

Install

$ npm install -g solc

ما از این کد منبع نمونه استفاده خواهیم کرد. نام فایل SampleToken.sol است.

1.	pragma solidity ^0.5.8;
2.	
3.	contract  SampleContract {
4.	
5.	function testFunc() public pure returns (int) {
6.	return 1;
7.	}
8.	}

خروجی EVM Bytecode

$solc--binSampleToken.sol
>=======SampleContract.sol:SampleContract=======
Binary:
6080604052348015600f57600080fd5b5060878061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063037a417c14602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000600190509056fea265627a7a7230582050d33093e20eb388eec760ca84ba30ec42dadbdeb8edf5cd8b261e89b8d4279264736f6c634300050a0032

خروجی قرارداد ABI

$ sold —abi SampleToken.sol
> ======= SampleContract.sol:SampleContract =======
Contract JSON ABI 
[{"constant":true,"inputs":[],"name":"testFunc","outputs":[{"name":"","type":"int256"}],"payable":false,"stateMutability":"pure","type":"function"}]

اگر می خواهید در یک دایرکتوری خاص خروجی بگیرید، می توانید با گزینه “-o” تنظیمات خود را انجام دهید. (شما نمی توانید نام فایل خروجی را تنظیم کنید)

$ mkdir build
$ solc --abi -o build SampleToken.sol

وقتی مجدد کامپایل را انجام دادید، حتما گزینه « — overwrite » را تنظیم کنید.

$ solc --abi -o build —overwrite SampleToken.sol

از دستور help استفاده کنید:

$ solc --help
coblocks-admin
ارسال دیدگاه

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *