آرتا رسانه

تفاوت سطح دسترسی در سالیدیتی public , private , internal , external آپدیت 2023

زبان سالیدیتی یک زبان برنامه‌نویسی است که برای توسعه نرم‌افزارهای قابل اجرا بر روی پلتفرم‌های مختلف بلاک چینی مورد استفاده قرار می‌گیرد.

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

برنامه نویسی سالیدیتی یک زبان قوی و انعطاف‌پذیر است که از ویژگی‌هایی مانند متغیرها، توابع، مدیریت حافظه، مدیریت استثناء و ارث‌بری پشتیبانی می‌کند.

برخی از کاربردهای رایج سالیدیتی عبارتند از:

  • ساخت برنامه‌های غیرمتمرکز (dApps)
  • ایجاد قراردادهای هوشمند
  • ایجاد توکن‌های غیرقابل تعویض (NFTs)
  • ساخت برنامه‌های مالی غیرمتمرکز (DeFi)
  • ساخت بازی‌های بلاک‌چین

سطوح دسترسی در زبان سالیدیتی به چهار دسته تقسیم می شوند:

  • Public
  • private
  • internal
  • external

این سطوح دسترسی کنترلی را بر روی توابع و متغیرهای قرارداد فراهم می‌کنند. استفاده از سطوح دسترسی به ما امکان می‌دهد که کنترل بیشتری بر روی دسترسی به توابع و متغییرهای قرارداد داشته باشیم. این کنترل می‌تواند به امنیت بیشتر قرارداد کمک کند و از استفاده نادرست از توابع و متغییرها جلوگیری کند.

 در این مقاله درباره سطح دسترسی در سالیدیتی صحبت میکنیم

سطح دسترسی public در سالیدیتی

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

مثال 1: بیایید یک مثال کاربردی استفاده از متغییرهای public در solidity بزنیم. فرض کنید ما یک قرارداد برای یک حراجی ساده در بلاکچین ایجاد می‌کنیم. در این حراجی، می‌خواهیم بتوانیم پیشنهادات را بررسی کنیم و بالاترین پیشنهاد را ببینیم.

سطح دسترسی public در سالیدیتی
				
					// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract SimpleAuction {
    // متغییر public برای نگهداری بالاترین پیشنهاد
    uint public highestBid;

    // متغییر public برای نگهداری آدرس برنده فعلی
    address public highestBidder;

    constructor() {
        highestBid = 0;
    }

    // تابع برای ارائه یک پیشنهاد جدید
    function bid() public payable {
        require(msg.value > highestBid, "There already is a higher bid.");

        highestBid = msg.value;
        highestBidder = msg.sender;
    }
}
				
			
				
					contract ExtendedAuction is SimpleAuction {
    function getHighestBid() public view returns (uint) {
        return highestBid;
    }

    function getHighestBidder() public view returns (address) {
        return highestBidder;
    }
}

				
			

سطح دسترسی public در خارج قرارداد:

وقتی یک متغییر به عنوان publicتعریف می‌شود، Solidity به صورت خودکار یک تابع getter برای آن ایجاد می‌کند.

این تابع به ما اجازه می‌دهد که مقدار متغییر را از خارج قرارداد بخوانیم. برای مثال، اگر ما یک قرارداد دیگر به نام AuctionViewer داشته باشیم که یک نمونه از SimpleAuction را نگه می‌دارد، می‌توانیم به highestBid و highestBidder دسترسی داشته باشیم:

سطح دسترسی public در سالیدیتی
				
					contract AuctionViewer {
    SimpleAuction auction;

    function createAuction(address _auctionAddress) public {
        auction = SimpleAuction(_auctionAddress);
    }

    function getHighestBid() public view returns (uint) {
        return auction.highestBid();
    }

    function getHighestBidder() public view returns (address) {
        return auction.highestBidder();
    }
}
				
			

در این مثال، تابع createAuction یک آدرس قرارداد به عنوان ورودی می‌گیرد و یک نمونه جدید از SimpleAuction را با استفاده از آدرس مشخص می‌سازد. توابع getHighestBid و getHighestBidder همچنان می‌توانند از توابع getter خودکار استفاده کنند کهSolidity برایhighestBid و highestBidder ایجاد کرده است تا مقادیر آن‌ها را بخوانند.

مثال 2: بیایید یک مثال کاربردی استفاده از تابع public  در solidity بزنیم. فرض کنید ما یک قرارداد برای یک بانک ساده در بلاکچین ایجاد می‌کنیم. در این بانک، می‌خواهیم بتوانیم مقدار پول را در حساب‌های مختلف بررسی کنیم و امکان واریز و برداشت وجود داشته باشد.

				
					// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract SimpleBank {
    mapping(address => uint) public balances;

    function deposit() public payable {
        balances[msg.sender] += msg.value;
    }

    function withdraw(uint amount) public {
        require(balances[msg.sender] >= amount, "Insufficient balance.");
        balances[msg.sender] -= amount;
        payable(msg.sender).transfer(amount);
    }

    function getBalance() public view returns (uint) {
        return balances[msg.sender];
    }
     function depositAndCheckBalance() public payable {
        deposit();
        uint balance = getBalance();
        assert(balance >= msg.value);
    }
}
				
			

در این مثال،  SimpleBank یک قرارداد بانک ساده است. ما یک تابع  public به نام  deposit داریم که اجازه می‌دهد که کاربران پول واریز کنند، و یک تابع  public به نام  withdrawکه اجازه می‌دهد که کاربران پول برداشت کنند. همچنین، تابع  getBalance اجازه می‌دهد که کاربران مقدار پول خود را در بانک ببینند.

سطح دسترسی public در خود قرارداد:

تابع  depositAndCheckBalance در قرارداد  SimpleBank ابتدا تابع  deposit را فراخوانی می‌کند که مقدار اتر ارسالی را به موجودی کاربر اضافه می‌کند. سپس با فراخوانی تابع getBalance، موجودی جدید حساب کاربر را بررسی می‌کند و اطمینان حاصل می‌کند که حداقل برابر با مقدار اتر ارسالی است.

سطح دسترسی public در قرارداد مشتق شده:

اگر ما یک قرارداد دیگر به نام ExtendedBank داشته باشیم که از SimpleBank ارث­بری می­کند، می­توانیم به توابع withdraw ، deposit و getBalance  دسترسی داشته باسیم:

سطح دسترسی public در سالیدیتی
				
					contract ExtendedBank is SimpleBank {
   function checkBalance() public view returns (uint) {
        return getBalance();
    }
}
				
			

در این کد، تابع  checkBalance تابع  public getBalance را از  SimpleBank فراخوانی می‌کند. این تابع می‌تواند موجودی کاربر را بررسی کند.

سطح دسترسی public در خارج قرارداد:

وقتی کاربر تابع depositToBank را فراخوانی می­کند و اتری را به آن ارسال می­کند، مقدار ارسالی  به تابع  deposit در قرارداد SimpleBank ارسال می­شود. این تابع deposit مقدار ارسالی را به موجودی کاربر (که با آدرس او مشخص می­شود) اضافه می­کند.
به این ترتیب، تابع depositToBank به کاربر اجازه می­دهد تا اتر به قرارداد SimpleBank واریز کند و موجودی خود را افزایش دهد.

				
					contract BankUser {
     SimpleBank bank;

    function createBank(address _simpleBank) public {
        bank = SimpleBank(_simpleBank);
    }

    function depositToBank() public payable {
        bank.deposit{value: msg.value}();
    }

}
				
			

سطح دسترسی private در سالیدیتی:

سطح دسترسی private به این معنی است که فقط داخل قرارداد می‌تواند به توابع و متغیرها دسترسی داشته باشد. این سطح دسترسی برای توابع و متغیرهایی استفاده می‌شود که نباید برای همه قابل دسترسی باشند و فقط باید در داخل قرارداد استفاده شوند.

سطح دسترسی private در خود قرارداد:

مثال 3: در زیر یک مثال کاربردی استفاده از متغیرهای private در solidity را می بینید. در این مثال، ما یک قرارداد بانکی ساده ایجاد می کنیم که امکان واریز و برداشت اتر به حساب ها را فراهم می کند. ما یک متغییر privateبه نام balances تعریف می کنیم که میزان اتر در حساب هر کاربر را نگه می دارد.

				
					// SPDX-License-Identifier: MIT
pragma solidity ^0.8.6;

contract SimpleBank {
    mapping(address => uint) private balances;

    function deposit() public payable {
        balances[msg.sender] += msg.value;
    }

    function withdraw(uint amount) public {
        require(balances[msg.sender] >= amount, "Insufficient balance");
        balances[msg.sender] -= amount;
        payable(msg.sender).transfer(amount);
    }

    function getBalance() public view returns (uint) {
        return balances[msg.sender];
    }
}
				
			

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

				
					contract DerivedBank is SimpleBank {
    function checkBalance(address user) public view returns (uint) {
        // This will not compile because `balances` is private in SimpleBank
        // return balances[user];
    }
}
				
			

مثال 4: بیایید یک مثال کاربردی از استفاده از تابع private در Solidity بزنیم. فرض کنید ما یک قرارداد برای یک بانک ساده داریم که کاربران می‌توانند در آن پول واریز کنند. اما ما می‌خواهیم که فقط در صورتی که مقدار واریزی بیش از یک حداقل مشخص باشد، واریز انجام شود. برای این منظور، می‌توانیم یک تابع private تعریف کنیم که این بررسی را انجام دهد:

سطح دسترسی private در سالیدیتی
				
					// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract SimpleBank {
    mapping(address => uint256) public balances;
    uint256 private minimumDeposit = 1 ether;

    function _isDepositEnough(uint256 amount) private view returns (bool) {
        return (amount >= minimumDeposit);
    }

    function deposit() public payable {
        require(_isDepositEnough(msg.value), "Deposit is not enough.");
        balances[msg.sender] += msg.value;
    }
}
				
			

تابع _isDepositEnough به عنوان یک تابع private تعریف شده است زیرا این تابع فقط برای استفاده داخلی در قرارداد SimpleBank طراحی شده است. این تابع یک جزء از منطق داخلی قرارداد است که بررسی می‌کند آیا مقدار واریزی کاربر بیش از حداقل مشخص شده است یا خیر.
استفاده از توابع private در این چنین مواردی می‌تواند به افزایش امنیت و تمیز بودن کد کمک کند. با محدود کردن دسترسی به توابعی که فقط باید داخل قرارداد فراخوانی شوند، می‌توانیم از این اطمینان حاصل شویم که کد قرارداد به شکل صحیح و امن کار می‌کند. همچنین، این کمک می‌کند تا کد را تمیزتر و قابل فهم‌تر بسازیم، زیرا مشخص می‌کند که کدام توابع فقط برای استفاده داخلی هستند.

سطح دسترسی internal در سالیدیتی:

سطح دسترسی internal شبیه به private است، با این تفاوت که قراردادهای مشتق شده نیز می‌توانند به توابع و متغیرها دسترسی داشته باشند. این سطح دسترسی برای توابع و متغیرهایی استفاده می‌شود که باید در داخل قرارداد و قراردادهای مشتق شده قابل دسترسی باشند.
مثال 5: در اینجا یک مثال از یک قرارداد “کتابخانه” را در نظر بگیرید که کتاب ها و تعداد آنها را نگه می دارد. در این مثال، ما یک متغییر internal به نام  bookCounts تعریف می کنیم که تعداد کتاب های موجود در کتابخانه را نگه می دارد.

تابع  addBook یک کتاب با نام مشخص شده را به کتابخانه اضافه می کند. و با هر بار فراخوانی، تعداد آن کتاب در کتابخانه یک واحد افزایش می یابد.  تابع  getBookCount تعداد کتاب های با نام مشخص شده موجود در کتابخانه را برمی گرداند.

				
					
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.6;

contract Library {
    mapping(string => uint256) internal bookCounts;

    function addBook(string memory bookName) public {
        bookCounts[bookName]++;
    }

    function getBookCount(string memory bookName)
        public
        view
        returns (uint256)
    {
        return bookCounts[bookName];
    }
}
				
			

سطح دسترسی internal در خود قرارداد:

در قرارداد Library متغییر bookCounts  به عنوان یک متغییر internal  تعریف شده است.در تابع  addBook ما به متغییر  bookCounts دسترسی داریم و می توانیم مقدار آن را افزایش دهیم.
همچنین، در تابع getBookCount ما به متغییر bookCounts  دسترسی داریم و می توانیم مقدار آن را برگردانیم.

سطح دسترسی internal در قرارداد مشتق شده:

در این قرارداد، متغییر  bookCounts به عنوان internal تعریف شده است، بنابراین از خارج قرارداد قابل دسترسی نیست. اما، این متغییر در قراردادهای مشتق شده قابل دسترسی است. برای نشان دادن این موضوع، می توانیم یک قرارداد مشتق شده ایجاد کنیم:

سطح دسترسی internal در سالیدیتی
				
					contract SpecialLibrary is Library {
    function donateBook(string memory bookName, uint256 count) public {
        bookCounts[bookName] += count;
    }
}
				
			

مثال 6: مثال زیر یک یک سیستم رای‌گیری ساده را ایجاد می‌کند. در این سیستم، هر نفر می‌تواند به یک گزینه رای دهد.
votes یک مپینگ است که آدرس‌های اتریوم را به اعداد صحیح نگاشت می‌کند. در این مورد، هر آدرس اتریوم به یک عدد صحیح نگاشت می‌شود که نشان‌دهنده گزینه‌ای است که آن آدرس به آن رای داده است.

به عبارت دیگر، هر بار که یک کاربر به یک گزینه رای می‌دهد، عدد مربوط به آن گزینه در مپینگ votesذخیره می‌شود. (اگر گزینه‌های رای‌گیری شماره‌های 1 تا 5 باشند، کاربر با فراخوانی تابع  vote و ارسال یک عدد بین 1 تا 5، رای خود را ثبت می‌کند.)

				
					// SPDX-License-Identifier: MIT
pragma solidity ^0.8.6;

contract Voting {
    mapping(address => uint256) private votes;

    function recordVote(address voter, uint256 option) internal {
        votes[voter] = option;
    }

    function vote(uint256 option) public {
        recordVote(msg.sender, option);
    }
}
				
			

سطح دسترسی internal در خود قرارداد:

در این مورد، تابع  voteبه تابع recordVote  دسترسی دارد زیرا  recordVote به عنوان یک تابع   internal  تعریف شده است. این به این معنی است که تابع  vote می‌تواند  recordVote را در داخل قرارداد “Voting” فراخوانی کند.

سطح دسترسی internal در قرارداد مشتق شده:

تابع voteTwice  در کانترکت “AdvancedVoting” به کاربر اجازه می‌دهد که به دو موضوع متفاوت رای دهد. این تابع دو ورودی به نام‌های option1 و option2  دریافت می‌کند که هر کدام نمایانگر یک موضوع برای رای‌گیری هستند.

سطح دسترسی internal در سالیدیتی
				
					contract AdvancedVoting is Voting {
    function voteTwice(uint256 option1, uint256 option2) public {
        vote(option1);
        recordVote(msg.sender, option2);
    }
}
				
			
وقتی کاربر تابع voteTwice را فراخوانی می‌کند، ابتدا تابع vote  از کانترکت پدر یعنی “Voting” فراخوانی می‌شود و رای اول کاربر یعنی option1  ثبت می‌شود. سپس، تابع recordVote  که یک تابع internal در کانترکت “Voting” است، فراخوانی می‌شود و رای دوم کاربر یعنی  option2 ثبت می‌شود. به این ترتیب، با استفاده از یک بار فراخوانی تابع voteTwice، کاربر می‌تواند به دو موضوع متفاوت رای دهد.

سطح دسترسی external در سالیدیتی:

سطح دسترسی external به این معنی است که فقط از خارج قرارداد می‌توان به توابع دسترسی داشت. این سطح دسترسی برای توابعی استفاده می‌شود که باید فقط از خارج قرارداد قابل فراخوانی باشند. این سطح دسترسی فقط برای توابع قابل دسترسی است و نمی‌توان از آن برای متغیرها استفاده کرد.

یکی از کاربردهای متداول توابع external در قراردادهای هوشمند، استفاده از آنها برای ایجاد API های قابل دسترسی برای دیگر قراردادها یا کاربران است.

مثال 7: مثال زیر مربوط یک قرارداد ساده است که یک تابع double  دارد که یک عدد صحیح را به عنوان ورودی می‌گیرد و دو برابر آن را برمی‌گرداند.

سطح دسترسی external در سالیدیتی
				
					// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;


contract Simple {
    function double(uint256 number) external pure returns (uint256) {
        return number * 2;
    }
}

contract Extended {
    Simple public simple;

    function initialize(address _simpleAddress) public {
        simple = Simple(_simpleAddress);
    }

    function square(uint256 number) public view returns (uint256) {
        return simple.double(number);
    }
}
				
			

سطح دسترسی external در خارج قرارداد:

قرارداد Extended در Solidity یک نمونه از قرارداد Simple را نگه می‌دارد و تابع double آن را فراخوانی می‌کند. این قرارداد با استفاده از تابع initialize آدرس یک قرارداد Simple را دریافت و ذخیره می‌کند. سپس، تابع square یک عدد را به عنوان ورودی می‌گیرد، آن را به تابع double در قرارداد Simple می‌فرستد و خروجی را برمی‌گرداند.

در ادامه، یک جدول مقایسه‌ای از سطوح دسترسی در زبان سالیدیتی ارائه شده است. این مقایسه می‌تواند به برنامه‌نویسان کمک کند تا سطوح دسترسی متناسب با نیازهای پروژه خود انتخاب کنند.

مقایسه سطوح دسترسی در سالیدیتی

دیدگاه‌ خود را بنویسید

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

آرتا رسانه
آرتا رسانه
دیجیتال مارکتینگ چیست؟
Loading
/
پیمایش به بالا