التحكم في الوصول هو مصطلح واسع. بشكل عام ، هذا يعني من يُسمح له بفعل ما في عقدك الذكي. من يُسمح له بسك الرموز الجديدة أو تحرير الأموال ، على سبيل المثال.
سننظر في التحكم في الوصول من وجهة نظر Solidity و البرمجة، ولكن التحكم في الوصول هو في الواقع نقطة محورية حيث تدخل blockchain العالم الحقيقي. في الأمثلة التالية ، سنكتب كوداً للسماح لعنوان معين بالقيام (أو عدم القيام) بإجراء معين. ومع ذلك ، قد يتوافق العنوان مع مستخدم في العالم الحقيقي ، مما يمنحه امتيازات معينة على العقد. إذا كان هذا العقد يحتوي على أموال لمجموعة ، فإن هذا الشخص هو الآن أمين صندوق المجموعة!
ضع ذلك في الاعتبار أثناء استعراضنا لهذه الأمثلة المختلفة.
تقييد الوصول والملكية
شكل بسيط للغاية من أشكال التحكم في الوصول هو جعل حالة العقد خاصة. لا يمكنك منع الأشخاص أو برامج الكمبيوتر من قراءة حالة عقودك. الحالة هي معلومات متاحة للجمهور لأي شخص لديه وصول إلى blockchain. ومع ذلك ، يمكنك تقييد وصول العقود الأخرى إلى الحالة من خلال جعل متغيرات الحالة خاصة ، كما في المثال الأساسي أدناه:
contract C1 { uint private internalNum; }
يمكن أن توفر هذه الكتابة البسيطة بعض جوانب الأمان من خلال التحكم في الوصول.
شكل أوسع من أشكال التحكم في الوصول هو نمط التصميم Ownable و الذي يعتبر بمثابة الــ “Hello، World!” للتحكم في الوصول ، بحيث. يعيّن عنوانًا أو عناوين معينة كـ “المالك” أو المسؤول عن العقد.
كما تعلمنا سابقًا ، تسمح لنا معدِّلات الوظائف (modifiers) بإعادة استخدام الكود وزيادة قابلية قراءة العقد. يمكننا أيضًا استخدامها لتقييد الوصول بناءً على نموذج الملكية:
address owner; constructor() payable public { owner = msg.sender; } modifier onlyOwner() { require(msg.sender == owner, "Not authorized."); _; } function withdraw(uint _amount) onlyOwner public { owner.transfer(_amount); }
يعلن الكود أعلاه عن مالك المتغير ويعين هذا الدور لمن يقوم بإنشاء العقد ، باستخدام المتغير الشامل msg.sender. نعلن بعد ذلك عن المُعدِّل onlyOwner () ، والذي يُنشئ فحصًا أمنيًا في الجزء العلوي من كل وظيفة نقوم بتعيينها لها. باستخدام صيغة المُعدِّل ، نطلب التحقق من هوية مرسل المعاملة لمعرفة ما إذا كانت تتطابق مع المالك المعلن.
ثم نضع هذا المُعدِّل أمام أي وظيفة نريد تقييد الوصول إليها. في هذه الحالة ، نضيفها إلى وظيفة withdraw() (وهذا أمر منطقي ، حيث لا نريد أن يتمكن أي شخص من استنزاف عقدنا ذي القيمة!). هذا يجعل المستخدم الوحيد الذي يمكنه الوصول إلى هذه الوظيفة المحددة هو المالك ، وستفشل جميع العناوين الأخرى.
العقد الشائع المستخدم لهذا هو OpenZeppelin Ownable.sol, ، والذي يتيح لك أيضًا نقل الملكية إلى مستخدم آخر أو التنازل عن الملكية إذا كان عقدك يتطلب سلطة مركزية فقط لفترة زمنية معينة.
يرجى ملاحظة أن النمط Ownable هش قليلاً: يمكن أن يصبح نقطة فشل واحدة وليس معقدًا للغاية من حيث التحكم في الوصول.
التوقف (Pausable)
شكل آخر من أشكال التحكم في الوصول يتعلق بتحويل العقد الذكي نفسه إلى آلة حالة. كما ذكرنا سابقًا في قسم الإجماع الموزع ، يمكن لآلة الحالة أن تكون في واحدة من عدد محدود من الحالات في أي وقت.
لجعل العقد الذكي آلة حالة ، سنستخدم نوع متغير التعداد لإنشاء سلسلة من الحالات الممكنة. للتحكم في الوصول ، سنقوم بعد ذلك بتعيين وظائف معينة فقط للتشغيل عندما يكون العقد في حالة معينة. إليك ما يبدو عليه هذا في Solidity ، حيث الحالات المحتملة هي الإيداعات والسحوبات وتتغير الحالة بعد فترة 30 يومًا من تاريخ إنشاء العقد:
enum Stages { Deposits, Withdraws } Stages stage = Stages.Deposits; mapping(address => uint) balances; uint creationTime = now; function deposit() payable public { require(stage == Stages.Deposits && msg.value > 0); balances[msg.sender] += msg.value; } function withdraw() public { if(stage != Stages.Withdraws && now >= creationTime + 30 days) { stage = Stages.Withdraws; } require(stage == Stages.Withdraws && balances[msg.sender] > 0); uint amount = balances[msg.sender]; balances[msg.sender] = 0; msg.sender.transfer(amount); }
لا يمكن استدعاء وظيفة deposit() إلا عندما يكون تعداد المرحلة (stage enum ) في Deposits. بعد مرور 30 يومًا ، ينتقل العقد إلى Withdraws كلما اتصل شخص ما withdraw().
للحصول على مثال لهذا المثال ، يتطلب عقد “DAO” مرور 27 يومًا بين طلب ناجح لتقسيم DAO والقدرة على القيام بذلك. هذا يضمن الاحتفاظ بالأموال ضمن العقد ، مما يزيد من احتمالية استردادها.
لدينا مثال آخر على هذا الشكل من التحكم في الوصول بنمط تصميم قاطع الدائرة (Circuit Breaker) ، ويسمى أيضًا إيقاف الطوارئ أو الإيقاف المؤقت. قواطع الدائرة هي أنماط تصميم تسمح بوظيفة العقد أن تتوقف. سيكون هذا مرغوبًا في المواقف التي يوجد فيها عقد مباشر حيث تم اكتشاف خطأ. سيكون تجميد العقد مفيدًا لتقليل الضرر قبل تنفيذ الإصلاح. إليك ما تبدو عليه في Solidity:
contract CircuitBreaker { bool public stopped = false; modifier stopInEmergency { require(!stopped); _; } modifier onlyInEmergency { require(stopped); _; } function deposit() stopInEmergency public { … } function withdraw() onlyInEmergency public { … } }
يمكن إعداد عقود قاطع الدائرة للسماح بوظائف معينة في مواقف معينة. على سبيل المثال ، إذا كنت تقوم بتنفيذ نمط سحب ، فقد ترغب في منع الأشخاص من إيداع الأموال في العقد إذا تم اكتشاف خطأ ، مع الاستمرار في السماح للحسابات ذات الأرصدة بسحب أموالهم. في مثل هذه الحالة ، قد ترغب أيضًا في تقييد الوصول إلى الحسابات التي يمكنها تعديل متغير الحالة المتوقفة ، ربما لمالك العقد (مثل محفظة multisig) أو مجموعة من المسؤولين.
إليك مثال آخر ، مع ثلاث حالات منفصلة تملي قواعد معينة يجب اتباعها:
bool isStopped = false; address owner; constructor() payable public { owner = msg.sender; } function stopContract() public { require(msg.sender == owner); isStopped = true; } function resumeContract() public { require(msg.sender == owner) isStopped = false; } function emergencyWithdraw() public { require(msg.sender == owner && isStopped); owner.transfer(this.balance); }
التحكم في الوصول المستند إلى الدور
التحكم في الوصول المستند إلى الدور (Role-based access control ) هو نهج طبقي أكثر للتحكم في الوصول لتلبية المتطلبات الأكثر تنوعًا للعقد الذكي أو التطبيق. يتبع اتجاهًا مشابهًا في الوصول إلى تطوير البرامج: بعض الأفراد هم إداريون ، وآخرون مساهمون ، ويمكن للآخرين عرض الكود فقط.
ينصح OpenZeppelin عن استخدام مكتبة التحكم في الوصول القائمة على الدور ، Roles.sol ، بدلاً من Ownable.sol. لقد طبقوا أيضًا التحكم في الوصول المستند إلى الأدوار في العقود ، مثل ERC20Mintable.sol ، الذي يحتوي على MinterRole المسموح له بإنشاء توكن جديدة. يمكنك قراءة المزيد حول نهجهم في التحكم في الوصول المستند إلى الدور في هذا المقال هنا.
يمكن أن يكون التحكم في الوصول المستند إلى الأدوار أمرًا بالغ الأهمية لتطوير الحوكمة القائمة على الكود ، كما هو الحال في DAO. ضع نمط التصميم هذا في الاعتبار عندما نناقش DAOs لاحقًا!
مصادر إضافية
- ويكي : Access Control مقالة شاملة من OpenZeppelin حول أنماط تصميم التحكم في الوصول وكيف تُسن العقود في مستودعاتها أنماطًا مختلفة.
- كود: OpenZeppelin’s Ownable.sol
إضافة تعليق