الدرس رقم 1

مقدمة وعقود متعددة التوقيع

تعد العقود متعددة التوقيعات (multisig)، والمعروفة أيضًا باسم " عقود " M-of-n، آلية مهمة تستخدم لزيادة أمان ومرونة المعاملات في بيئة blockchain. تعمل هذه العقود على تغيير طريقة ممارسة السيطرة على الأصول من خلال طلب موافقة من أطراف متعددة قبل تنفيذ المعاملات. "يشير مصطلح " m-of-n إلى مطلب أن M من إجمالي N يجب أن توافق الأطراف على المعاملة حتى تكون صالحة.

نظرية العقود المتعددة

توفر عقود Multisig وسيلة لإنشاء سيطرة مشتركة على الأصول. تتضمن حالات الاستخدام النموذجية خدمات الضمان وإدارة حسابات الشركات والتوقيع المشترك على الاتفاقيات المالية والمزيد. هذه العقود مفيدة بشكل استثنائي للمنظمات أو المجموعات التي يكون فيها اتخاذ القرار الجماعي ضروريًا.

حسب التصميم، تكون العقود متعددة التوقيع مقاومة للعبث وتمنع نقاط الفشل الفردية. حتى في حالة اختراق مفاتيح أحد الأطراف، لا يمكن للمهاجم تنفيذ المعاملات دون موافقة من الأطراف الأخرى. هذا يضيف طبقة إضافية من الأمان.

يمكن اعتبار عقود Multisig بمثابة مكافئ رقمي لصندوق الإيداع الآمن الذي يتطلب عدة مفاتيح لفتحه. يتم الاتفاق على العدد الإجمالي للمفاتيح (N) والحد الأدنى لعدد المفاتيح المطلوبة لفتح المربع (M) عند إنشاء العقد.

يمكن أن تحتوي عقود Multisig على العديد من التكوينات المختلفة اعتمادًا على قيم M و N:

  • 1-of-n: يمكن لطرف واحد من الإجمالي الموافقة على المعاملة. هذا التكوين هو نفس المعاملة العادية بدون multisig. يمكن استخدامه عند وجود عدة مفاتيح للراحة، ولكن يمكن لأي منها الموافقة على المعاملات.
  • N-of-n: يجب على جميع الأطراف الموافقة على المعاملة. يوفر هذا التكوين أعلى مستوى من الأمان ولكن يمكن أن يصبح مشكلة إذا فقد أحد الأطراف مفتاحه أو رفض الموافقة على المعاملات.
  • m-of-n (حيث M < N): يجب أن توافق مجموعة فرعية من إجمالي الأطراف على المعاملة. غالبًا ما يتم استخدام هذا التكوين عمليًا لأنه يوازن بين الأمان والمرونة.

عقود متعددة التوقيع في بلوكتشين

وفي سياق بلوكتشين، تُستخدم العقود متعددة التوقيع على نطاق واسع لتعزيز أمن المعاملات، أو دعم آليات الحوكمة المعقدة، أو الحفاظ على التحكم المرن في أصول بلوكتشين. فيما يلي بعض الأمثلة:

  • المحافظ: تُستخدم محافظ Multisig لتأمين الأصول. فهي تتطلب من أطراف متعددة التوقيع على المعاملات، وبالتالي توفير أمان إضافي ضد السرقة والاختراق الخارجي والتهديدات الداخلية.
  • المنظمات المستقلة اللامركزية (DAOs): غالبًا ما تستخدم DAOs عقودًا متعددة التوقيع لفرض قواعد الحوكمة الخاصة بها. يتم تنفيذ التصويت على المقترحات كمعاملات متعددة التوقيع، مع قيام أعضاء DAO بدور الموقعين. يتم تنفيذ الاقتراح فقط إذا حصل على عدد كافٍ من الأصوات.
  • العمليات عبر السلاسل: في العمليات عبر السلاسل، يمكن للعقود متعددة التوقيع أن تعمل كأوصياء على الأصول. عندما يتم نقل الأصول من بلوكشين إلى آخر، يمكن أن يضمن العقد متعدد التوقيع على السلسلة الأصلية أن الأصول مغلقة بأمان حتى يتم تأكيد العملية على السلسلة الأخرى.
    وفي حين أن تنفيذ العقود متعددة التوقيع يمكن أن يختلف من بلوكتشين إلى آخر، إلا أن المفهوم الأساسي يظل هو نفسه - الحاجة إلى موافقة أطراف متعددة على المعاملة قبل تنفيذها. هذه الطبقة الإضافية من الأمان تجعل العقود متعددة التوقيع أداة أساسية في مجال بلوكتشين والعملات المشفرة.

مثال الترميز: كتابة العقود متعددة التوقيع ونشرها باستخدام SmartPy

بالنسبة لأمثلة التعليمات البرمجية الخاصة بنا، سننظر في ثلاثة تطبيقات مختلفة للعقود متعددة التوقيعات:

عقد لامدا

إنه متعدد الاستخدامات للغاية ويسمح بمجموعة واسعة من الاستخدامات. يتطلب الأمر توقيعات متعددة لتنفيذ وظائف lambda التعسفية.

الثعبان
الاستيراد بذكاء كسبا


@sp. وحدة
تعريف رئيسي ():
 تشغيل_لامدا: النوع = sp.lambda_ (وحدة sp.unit، وحدة sp.unit، مع _العمليات = صحيح)

 فئة MULTISIG Lambda (عقد SP):
 """يصوت العديد من الأعضاء لتنفيذ lambdas.

        يمكن إنشاء هذا العقد بقائمة العناوين وعدد من
 الأصوات المطلوبة. يمكن لأي عضو تقديم أكبر قدر ممكن من لامدا والتصويت
 للحصول على مقترحات نشطة. عندما تصل لامدا إلى الأصوات المطلوبة، يكون رمزها
 تم استدعاؤها ويتم تنفيذ عمليات الإخراج. هذا يسمح لهذا العقد بـ
 القيام بأي شيء يمكن أن يفعله العقد: نقل الرموز، وإدارة الأصول،
 إدارة عقد آخر...

        عند تطبيق لامدا، يتم تعطيل جميع لامدا المقدمة حتى الآن.
        لا يزال بإمكان الأعضاء تقديم لامدا جديدة.
        """

 def __init__(الذات، الأعضاء، التصويتات المطلوبة):
 """المُنشئ

 الفنون:
 الأعضاء (sp.set أو sp.address): الأشخاص الذين يمكنهم الإرسال والتصويت
 من أجل لامدا.
                الأصوات المطلوبة (sp.nat): عدد الأصوات المطلوبة
 """
 تأكيد التصويتات < المطلوبة= sp.len (
 أعضاء
 )، يجب أن " تكون الأصوات المطلوبة < = len (الأعضاء) "
 البيانات الذاتية.lambdas = sp.cast (
                sp.big_map ()، sp.big_map [sp.nat، عملية لامدا]
 )
 البيانات الذاتية.التصويتات = sp.cast (
                sp.big_map ()، sp.big_map [sp.nat، sp.set [عنوان sp.]
 )
 البيانات الذاتية. معرف التالي = 0
 البيانات الذاتية. غير نشطة من قبل = 0
 البيانات الذاتية.الأعضاء = sp.cast (الأعضاء، sp.set [عنوان sp.address])
            البيانات الذاتية. التصويتات المطلوبة = sp.cast (التصويتات المطلوبة، sp.nat)

 @sp. نقطة الدخول
 تعريف submit_lambda (الذات، لامدا _):
 """قم بتقديم لامدا جديدة للتصويت.

            إن تقديم اقتراح لا يعني التصويت لصالحه.

            الفنون:
 lambda_ (sp.lambda مع العمليات): اقترحت لامدا التصويت.
            يرفع:
 «أنت لست عضوًا»
 """
 تأكيد self.data.members.contains (sp.sender)، "أنت لست عضوًا "
 self.data.lambdas [self.data.nextid] = لامدا_
 self.data.votes [self.data.nextid] = sp.set ()
            البيانات الذاتية. المعرف التالي += 1

 @sp. نقطة الدخول
 تعريف vote_lambda (الذات، المعرف):
 """صوّت لصالح لامدا.

            الفنون:
 معرف (sp.nat): غطاء لامدا للتصويت له.
            يرفع:
 «أنت لست عضوًا»، «لامدا غير نشطة»، «لامدا غير موجودة»

 لا يوجد تصويت ضد أو تمرير. إذا كان شخص ما لا يوافق على لامدا
 يمكنهم تجنب التصويت.
            """
 تأكيد self.data.members.contains (sp.sender)، "أنت لست عضوًا "
 معرف التأكيد > = self.data.غير نشط من قبل، "لامدا غير نشطة "
 تأكيد self.data.lambdas. يحتوي على (معرف)، "لم يتم العثور على لامدا "
 self.data.votes [id] .add (sp.sender)
            إذا كان sp.len (self.data.votes [id]) >= البيانات الذاتية. التصويتات المطلوبة:
                self.data.lambdas [id] ()
                البيانات الذاتية. غير نشطة من قبل = البيانات الذاتية. المعرف التالي

 @sp .onchain_view ()
 تعريف get_lambda (الذات، المعرف):
 """قم بإرجاع لامدا المقابلة.

            الفنون:
 id (sp.nat): قم بتخليص لامدا للحصول عليه.

            الإرجاع:
 زوج من لامدا وبوليان يوضحان ما إذا كانت لامدا نشطة.
            """
 إرجاع (self.data.lambdas [id]، id > = البيانات الذاتية. غير نشطة من قبل)


# إذا لم " تكن " القوالب موجودة في __name__:


@sp. وحدة
اختبار def ():
 الفئة المُدارة (SP.Contract):
 def __init__(الذات، المشرف):
 البيانات الذاتية.admin = المشرف
 قيمة البيانات الذاتية = sp.int (0)

        @sp. نقطة الدخول
 قيمة def set_value (الذات، القيمة):
 تأكيد sp.sender = self.data.admin
 البيانات الذاتية.القيمة = القيمة


@sp .add_test (الاسم = سيناريو Lambda الأساسي " متعدد الإشارات، is_default= صحيح) "
سيناريو التعريف الأساسي ():
 """استخدم MultiSiglambda كمسؤول عن عقد نموذجي.

    الاختبارات:
 - النشأة
 - تقديم لامدا
 - تصويت لامدا
 """
 sc = سيناريو sp.test_( [رئيسي، اختبار])
 sc.h1 (السيناريو " الأساسي. ")

    العضو 1 = حساب sp.test_( العضو 1) " "
    العضو 2 = حساب sp.test_( العضو 2) " "
    العضو 3 = حساب sp.test_( العضو 3) " "
    الأعضاء = sp.set ([العضو 1. العنوان، عنوان العضو 2، عنوان العضو 3])

    sc.h2 (لامدا " متعددة الإشارات: النشأة) "
 c1 = لامدا الرئيسية متعددة اللغات (الأعضاء، 2)
 اس سي += ج1

 sc.h2 ("المُدار: الإنشاء) "
 c2 = الاختبار. المُدار (عنوان c1)
    اس سي += ج2

 sc.h2 (لامدا " متعددة الإشارات: submit_lambda) "

 معرف set_42 (المعلمات):
 تتم إدارته = sp.contract (sp.tint، c2. address، نقطة الإدخال = set_value) " "
 نقل البيانات (sp.int (42)، sp.tez (0)، المُدار. open_some ())

    لامدا_ = sp.build_lambda (set_42، مع _العمليات = صحيح)
 c1. submit_lambda (lambda_) .run (المرسل = العضو 1)

 sc.h2 (لامدا " متعددة اللغات: vote_lambda) "
 c1. vote_lambda (0) .run (المرسل = العضو 1)
    c1. vote_lambda (0) .run (المرسل = العضو 2)

    # يمكننا التحقق من أن العقد المُدار قد استلم التحويل.
    sc.verify (c2.data.value = 42)

عقد التوقيع المتعدد

يقدم مفهوم التصويت على المقترحات. في هذا العقد، يمكن للموقعين التصويت على إجراءات معينة يجب اتخاذها، وإذا تم الوصول إلى النصاب القانوني، يتم تنفيذ الإجراءات المقترحة.

الثعبان
الاستيراد بذكاء كسبا


@sp. وحدة
تعريف رئيسي ():
 # مواصفات نوع إجراء الإدارة الداخلية
 إجراء الإدارة الداخلية: النوع = sp.variant (
        إضافة الموقّعين = sp.list [sp.address]،
        تغيير النصاب القانوني = SP.NAT،
        إزالة التوقيعات = sp.list [sp.address]،
    )

 التوقيع المتعدد للفئة (عقد SP):
 """عقد يمكن استخدامه من قبل العديد من الموقعين لإدارة الآخرين
 عقود. تقوم العقود المُدارة بتنفيذ واجهة تجعلها
 من الممكن توضيح عملية الإدارة للمستخدمين غير الخبراء.

        يصوت الموقعون على المقترحات. الاقتراح هو قائمة الهدف مع قائمة بـ
 عمل. الإجراء عبارة عن بايت بسيط ولكن الغرض منه أن يكون قيمة حزمة لـ
 متغير. هذا النمط البسيط يجعل من الممكن بناء واجهة UX
 يعرض محتوى الاقتراح أو البناء.
        """

 الصم __init__(الرف، النصاب، الموقعون):
 البيانات الذاتية. غير نشطة من قبل = 0
 البيانات الذاتية. معرف التالي = 0
 البيانات الذاتية. المقترحات = sp.cast (
                sp.big_map ()،
 خريطة sp.big [
 sp.nat،
 sp.list [sp.record (الهدف = عنوان sp.sp، الإجراءات = sp.list [sp.bytes])]،
                ]،
 )
 self.data.quorum = sp.cast (النصاب القانوني، sp.nat)
 self.data.signers = sp.cast (الموقعون، sp.set [عنوان sp.address])
            البيانات الذاتية.التصويتات = sp.cast (
                sp.big_map ()، sp.big_map [sp.nat، sp.set [عنوان sp.]
 )

 @sp. نقطة الدخول
 ديف send_proposal (الذات، الاقتراح):
 """الموقّع فقط. قم بتقديم اقتراح للتصويت.

            الفنون:
 الاقتراح (sp.list أو sp.record للعنوان الهدف والإجراء): قائمة\
 من الإجراءات الإدارية المستهدفة والمرتبطة بها.
            """
 تأكيد self.data.signers.contains.contains (sp.sender)، "يمكن للموقعين فقط أن يقترحوا "
 مقترحات البيانات الذاتية [self.data.nextID] = اقتراح
 self.data.votes [self.data.nextid] = sp.set ()
            البيانات الذاتية. المعرف التالي += 1

 @sp. نقطة الدخول
 التصويت الذاتي (الذاتي، PID):
 """التصويت لمقترح واحد أو أكثر

 الفنون:
 رقم التعريف الشخصي (sp.nat): معرف الاقتراح.
            """
 تأكيد self.data.signers.contains.contains (sp.sender)، "يمكن للموقعين فقط التصويت "
 تأكيد self.data.votes. يحتوي على (معرف التعريف الشخصي)، "اقتراح غير معروف "
 تأكيد pID > = self.data.غير نشط من قبل، "الاقتراح غير نشط "
 البيانات الذاتية.التصويتات [pID] .add (sp.sender)

            إذا كان sp.len (self.data.votes.get) (معرف التعريف الشخصي)، الإعداد الافتراضي = sp.set ())) >= البيانات الذاتية.النصاب القانوني:
                الذات. _ غير معتمد (PID)

        @sp .private (مع_التخزين = القراءة والكتابة، مع _العمليات = صحيح") "
 def _onApproved (ذاتي، معرف التعريف الشخصي):
 """وظيفة مضمنة. يتم تطبيق المنطق عند الموافقة على الاقتراح. """
            الاقتراح = self.data.proposals.get (معرف التعريف الشخصي، الإعداد الافتراضي = [])
 بالنسبة إلى p_item في الاقتراح:
 العقد = sp.contract (sp.list [sp.bytes]، p_item.target)
                نقل البيانات (
 p_item. الإجراءات،
                    ملعقة صغيرة (0)،
 contract.unwrap_some (خطأ = هدف غير صالح)، " "
                )
 # قم بإلغاء تنشيط جميع المقترحات التي تم تقديمها بالفعل.
            البيانات الذاتية. غير نشطة من قبل = البيانات الذاتية. المعرف التالي

 @sp. نقطة الدخول
 مسؤول الدفاع (الذات، الإجراءات):
 """الاتصال الذاتي فقط. قم بإدارة هذا العقد.

            يجب استدعاء نقطة الدخول هذه من خلال نظام الاقتراح.

            الفنون:
 الإجراءات (sp.list من sp.bytes): قائمة المتغيرات المعبأة لـ\
 «الإدارة الداخلية» («إضافة الموقعين»، «تغيير النصاب القانوني»، «إزالة الموقعين»).
            """
 يجزم (
 sp.sender == sp.self_address ()
 )، يجب استدعاء نقطة الدخول " هذه من خلال نظام الاقتراح. "

 لإجراءات_packed_actions في الإجراءات:
 الإجراء = sp.unpack (الإجراءات_المعبأة)، إجراء الإدارة الداخلية) .unwrap_some (
 خطأ = تنسيق الإجراءات " السيئة "
 )
 مع sp.match (الإجراء):
 مع SP.Case. تغيير النصاب كنصاب قانوني:
 البيانات الذاتية.النصاب = النصاب القانوني
 مع الموقّعين على Sp.case.adds كما تمت إضافتهم:
 للموقّع أضاف:
 self.data.signers.add (الموقّع)
                    مع حقيبة SP. قم بإزالة التوقيعات كما تمت إزالته:
 بالنسبة للعنوان الذي تمت إزالته:
 self.data.signers.remove (العنوان)
                # تأكد من أن العقد لا يتطلب أبدًا النصاب القانوني أكثر من إجمالي الموقعين.
                تأكيد البيانات الذاتية.النصاب = sp.len (<
 توقيعات البيانات الذاتية
 )، النصاب القانوني " أكثر من الموقعين. "


إذا لم " تكن " القوالب موجودة في __name__:

 @sp .add_test (الاسم = السيناريو " الأساسي، is_default=صحيح) "
 اختبار def ():
 الموقّع 1 = sp.test_account (الموقّع 1) " "
        الموقّع 2 = sp.test_account (الموقّع 2) " "
        الموقّع 3 = sp.test_account (الموقّع 3) " "

        s = سيناريو sp.test_( رئيسي)
        s.h1 (السيناريو " " الأساسي)

 s.h2 ("الإنشاء) "
 c1 = تعدد الإشارات الرئيسي (
 النصاب القانوني =2،
 الموقعون = sp.set ([الموقع 1. العنوان، الموقع 2. العنوان])،
 )
 s += c1

 s.h2 ("اقتراح لإضافة موقّع جديد) "
 الهدف = sp.to_address (
            sp.contract (sp.tlist (sp.tbytes)، c1. العنوان، المسؤول) .open_some () " "
 )
 الإجراء = حزمة sp.pack (
            sp.set_type_expr (
 sp.variant ("يضيف الموقّعين، [signer3. " address])، الرئيسية - إجراءات الإدارة الداخلية
 )
 )
 c1.send_proposal ([sp.record) (الهدف = الهدف، الإجراءات = [الإجراء])]) .run (
 المرسل=الموقّع 1
 )

 s.h2 (صوت " الموقّع 1 لصالح الاقتراح) "
 c1. التصويت (0). run (المرسل = الموقّع 1)
        s.h2 (صوت " الموقّع 2 لصالح الاقتراح) "
 c1. التصويت (0). run (المرسل = الموقّع 2)

        s.verify (يحتوي c1.data.signers.على (الموقّع 3. العنوان))

عقد مولتيسيغفيو

كما أنها تستخدم آلية التصويت. يسمح هذا العقد للأعضاء بإرسال وحدات البايت العشوائية والتصويت عليها. بمجرد أن يحقق الاقتراح العدد المطلوب من الأصوات، يمكن تأكيد حالته من خلال العرض.

الثعبان
الاستيراد بذكاء كسبا


@sp. وحدة
تعريف رئيسي ():
 عرض متعدد الأشكال من الفئة (عقد SP):
 """يصوت العديد من الأعضاء لوحدات البايت العشوائية.

        يمكن إنشاء هذا العقد بقائمة العناوين وعدد من
 الأصوات المطلوبة. يمكن لأي عضو إرسال أكبر عدد من وحدات البايت كما يريد والتصويت
 للحصول على مقترحات نشطة.

        يمكن تأكيد أي وحدات بايت وصلت إلى الأصوات المطلوبة من خلال العرض.
        """

 def __init__(الذات، الأعضاء، التصويتات المطلوبة):
 """المُنشئ

 الفنون:
 الأعضاء (sp.set أو sp.address): الأشخاص الذين يمكنهم التقديم والتصويت لصالح
 لامدا.
                الأصوات المطلوبة (sp.nat): عدد الأصوات المطلوبة
 """
 تأكيد التصويتات < المطلوبة= sp.len (
 أعضاء
 )، يجب أن " تكون الأصوات المطلوبة < = len (الأعضاء) "
 مقترحات البيانات الذاتية = sp.cast (sp.big_map ()، خريطة sp.big_map [sp.bytes، sp.bool])
 البيانات الذاتية. الأصوات = sp.cast (
                sp.big_map ()، sp.big_map [sp.bytes، sp.set [عنوان sp.]
 )
 self.data.members = sp.cast (الأعضاء، sp.set [عنوان sp.address])
            البيانات الذاتية. التصويتات المطلوبة = sp.cast (التصويتات المطلوبة، sp.nat)

 @sp. نقطة الدخول
 تعريف submit_proposal (الذات، وحدات البايت):
 """قم بتقديم اقتراح جديد للتصويت.

            إن تقديم اقتراح لا يعني التصويت لصالحه.

            الفنون:
 بايت (sp.bytes): وحدات البايت المقترحة للتصويت.
            يرفع:
 «أنت لست عضوًا»
 """
 تأكيد self.data.members.contains (sp.sender)، "أنت لست عضوًا "
 مقترحات البيانات الذاتية [بايت] = خطأ
 self.data.votes [بايت] = sp.set ()

        @sp. نقطة الدخول
 تعريف vote_proposal (الذات، وحدات البايت):
 """التصويت على اقتراح.

            لا يوجد تصويت ضد أو تمرير. إذا لم يوافق المرء على الاقتراح، فإنهم
 يمكن تجنب التصويت. تحذير: المقترحات القديمة التي لم يتم التصويت عليها لن تصبح أبدًا
 عفا عليها الزمن.

            الفنون:
 معرف (sp.bytes): بايت من الاقتراح.
            يرفع:
 «أنت لست عضوًا»، «لم يتم العثور على الاقتراح»
 """
 تأكيد self.data.members.contains (sp.sender)، "أنت لست عضوًا "
 تأكيد self.data.proposals.contains (بايت)، "لم يتم العثور على الاقتراح "
 self.data.votes [بايت] .add (sp.sender)
            إذا كان sp.len (self.data.votes [بايت]) >= البيانات الذاتية. التصويتات المطلوبة:
                مقترحات البيانات الذاتية [بايت] = صحيح

 @sp .onchain_view ()
 تعريف is_voted (الذات، المعرف):
 """تُرجع قيمة منطقية تشير إلى ما إذا كان قد تم التصويت على الاقتراح.

            الفنون:
 id (sp.bytes): بايت من الاقتراح
 الإرجاع:
 (sp.bool): صحيح إذا تم التصويت على الاقتراح، خطأ خلاف ذلك.
            """
 إرجاع البيانات الذاتية.proposals.get (المعرف)، خطأ = لم يتم العثور " على " الاقتراح)


إذا لم " تكن " القوالب موجودة في __name__:

 @sp .add_test (الاسم = السيناريو الأساسي لـ " MultiSigView، is_default= صحيح) "
 سيناريو التعريف الأساسي ():
 """سيناريو مع التصويت على عقد MultiSigView.

        الاختبارات:
 - النشأة
 - تقديم الاقتراح
 - التصويت على الاقتراح
 """
 sc = سيناريو sp.test_( رئيسي)
        sc.h1 (السيناريو " الأساسي. ")

        العضو 1 = حساب sp.test_( العضو 1) " "
        العضو 2 = حساب sp.test_( العضو 2) " "
        العضو 3 = حساب sp.test_( العضو 3) " "
        الأعضاء = sp.set ([العضو 1. العنوان، عنوان العضو 2، عنوان العضو 3])

        sc.h2 (الإنشاء) " "
 c1 = العرض الرئيسي متعدد المواقع (الأعضاء، 2)
 اس سي += ج1

 sc.h2 (تقديم اقتراح) " "
 c1.submit_proposal (sp.bytes (""0x42)) .run (المرسل = العضو 1)

 sc.h2 (اقتراح التصويت) " "
 c1. vote_proposal (sp.bytes (0x42)) .run ("المرسل = العضو 1) "
 c1. vote_proposal (sp.bytes (0x42)) .run (المرسل = العضو 2) " "

 # يمكننا التحقق من صحة الاقتراح.
        sc.verify (c1.is_voted (sp.bytes (0x42))) " "

يوفر كل عقد آلية مختلفة لتحقيق التحكم في التوقيعات المتعددة، مما يوفر المرونة اعتمادًا على الاحتياجات المحددة لحالة استخدام blockchain الخاصة بك.

دليل خطوة بخطوة لتجربة العقد متعدد التوقيع على SmartPay عبر الإنترنت

لتجربة العقود متعددة التوقيع التي كتبناها في SmartPy، يمكنك اتباع الخطوات التالية:

  1. انتقل إلى واجهة برمجة تطبيقات SmartPY على https://smartpy.io/ide.

  2. قم بلصق كود العقد في المحرر. يمكنك استبدال الكود الموجود.

  3. لتنفيذ العقد، انقر فوق الزر «تشغيل» الموجود في اللوحة العلوية.

  4. بعد تشغيل العقد، يمكنك عرض تنفيذ السيناريو في لوحة «الإخراج» على اليمين. هنا، يمكنك الاطلاع على تفاصيل كل إجراء، بما في ذلك المقترحات والتصويتات والموافقات.

  5. لنشر العقد الخاص بك على شبكة Tezos، تحتاج أولاً إلى تجميعه. انقر فوق الزر «تجميع» في اللوحة العلوية.

  6. بعد التجميع، يمكنك نشر العقد على شبكة الاختبار بالنقر فوق «نشر عقد Michelson». ستحتاج إلى توفير مفتاح سري لحساب Tezos بأموال كافية لدفع تكاليف الغاز للنشر.

  7. بمجرد نشر العقد، سيتم تزويدك بعنوان العقد على بلوكتشين. يمكنك استخدام هذا العنوان للتفاعل مع العقد عبر المعاملات.

  8. لإرسال مقترحات أو التصويت على العقود، يمكنك استخدام نقاط الدخول المحددة في كود العقد، مثل submit_posal أو vote_posal. يمكن استدعاؤها مباشرة من المعاملات التي تقوم بإنشائها.

تذكر أنه في حين أن SmartPY IDE يسمح لك باختبار عقدك على بلوكتشين مُحاكاة، فإن نشر العقد على شبكة Tezos الفعلية سيؤدي إلى تكبد تكاليف الغاز، والتي يجب دفعها بعملة XTZ، وهي العملة المشفرة الأصلية لشبكة Tezos.

إخلاء المسؤولية
* ينطوي الاستثمار في العملات الرقمية على مخاطر كبيرة. فيرجى المتابعة بحذر. ولا تهدف الدورة التدريبية إلى تقديم المشورة الاستثمارية.
* تم إنشاء الدورة التدريبية من قبل المؤلف الذي انضم إلى مركز التعلّم في Gate. ويُرجى العلم أنّ أي رأي يشاركه المؤلف لا يمثّل مركز التعلّم في Gate.
الكتالوج
الدرس رقم 1

مقدمة وعقود متعددة التوقيع

تعد العقود متعددة التوقيعات (multisig)، والمعروفة أيضًا باسم " عقود " M-of-n، آلية مهمة تستخدم لزيادة أمان ومرونة المعاملات في بيئة blockchain. تعمل هذه العقود على تغيير طريقة ممارسة السيطرة على الأصول من خلال طلب موافقة من أطراف متعددة قبل تنفيذ المعاملات. "يشير مصطلح " m-of-n إلى مطلب أن M من إجمالي N يجب أن توافق الأطراف على المعاملة حتى تكون صالحة.

نظرية العقود المتعددة

توفر عقود Multisig وسيلة لإنشاء سيطرة مشتركة على الأصول. تتضمن حالات الاستخدام النموذجية خدمات الضمان وإدارة حسابات الشركات والتوقيع المشترك على الاتفاقيات المالية والمزيد. هذه العقود مفيدة بشكل استثنائي للمنظمات أو المجموعات التي يكون فيها اتخاذ القرار الجماعي ضروريًا.

حسب التصميم، تكون العقود متعددة التوقيع مقاومة للعبث وتمنع نقاط الفشل الفردية. حتى في حالة اختراق مفاتيح أحد الأطراف، لا يمكن للمهاجم تنفيذ المعاملات دون موافقة من الأطراف الأخرى. هذا يضيف طبقة إضافية من الأمان.

يمكن اعتبار عقود Multisig بمثابة مكافئ رقمي لصندوق الإيداع الآمن الذي يتطلب عدة مفاتيح لفتحه. يتم الاتفاق على العدد الإجمالي للمفاتيح (N) والحد الأدنى لعدد المفاتيح المطلوبة لفتح المربع (M) عند إنشاء العقد.

يمكن أن تحتوي عقود Multisig على العديد من التكوينات المختلفة اعتمادًا على قيم M و N:

  • 1-of-n: يمكن لطرف واحد من الإجمالي الموافقة على المعاملة. هذا التكوين هو نفس المعاملة العادية بدون multisig. يمكن استخدامه عند وجود عدة مفاتيح للراحة، ولكن يمكن لأي منها الموافقة على المعاملات.
  • N-of-n: يجب على جميع الأطراف الموافقة على المعاملة. يوفر هذا التكوين أعلى مستوى من الأمان ولكن يمكن أن يصبح مشكلة إذا فقد أحد الأطراف مفتاحه أو رفض الموافقة على المعاملات.
  • m-of-n (حيث M < N): يجب أن توافق مجموعة فرعية من إجمالي الأطراف على المعاملة. غالبًا ما يتم استخدام هذا التكوين عمليًا لأنه يوازن بين الأمان والمرونة.

عقود متعددة التوقيع في بلوكتشين

وفي سياق بلوكتشين، تُستخدم العقود متعددة التوقيع على نطاق واسع لتعزيز أمن المعاملات، أو دعم آليات الحوكمة المعقدة، أو الحفاظ على التحكم المرن في أصول بلوكتشين. فيما يلي بعض الأمثلة:

  • المحافظ: تُستخدم محافظ Multisig لتأمين الأصول. فهي تتطلب من أطراف متعددة التوقيع على المعاملات، وبالتالي توفير أمان إضافي ضد السرقة والاختراق الخارجي والتهديدات الداخلية.
  • المنظمات المستقلة اللامركزية (DAOs): غالبًا ما تستخدم DAOs عقودًا متعددة التوقيع لفرض قواعد الحوكمة الخاصة بها. يتم تنفيذ التصويت على المقترحات كمعاملات متعددة التوقيع، مع قيام أعضاء DAO بدور الموقعين. يتم تنفيذ الاقتراح فقط إذا حصل على عدد كافٍ من الأصوات.
  • العمليات عبر السلاسل: في العمليات عبر السلاسل، يمكن للعقود متعددة التوقيع أن تعمل كأوصياء على الأصول. عندما يتم نقل الأصول من بلوكشين إلى آخر، يمكن أن يضمن العقد متعدد التوقيع على السلسلة الأصلية أن الأصول مغلقة بأمان حتى يتم تأكيد العملية على السلسلة الأخرى.
    وفي حين أن تنفيذ العقود متعددة التوقيع يمكن أن يختلف من بلوكتشين إلى آخر، إلا أن المفهوم الأساسي يظل هو نفسه - الحاجة إلى موافقة أطراف متعددة على المعاملة قبل تنفيذها. هذه الطبقة الإضافية من الأمان تجعل العقود متعددة التوقيع أداة أساسية في مجال بلوكتشين والعملات المشفرة.

مثال الترميز: كتابة العقود متعددة التوقيع ونشرها باستخدام SmartPy

بالنسبة لأمثلة التعليمات البرمجية الخاصة بنا، سننظر في ثلاثة تطبيقات مختلفة للعقود متعددة التوقيعات:

عقد لامدا

إنه متعدد الاستخدامات للغاية ويسمح بمجموعة واسعة من الاستخدامات. يتطلب الأمر توقيعات متعددة لتنفيذ وظائف lambda التعسفية.

الثعبان
الاستيراد بذكاء كسبا


@sp. وحدة
تعريف رئيسي ():
 تشغيل_لامدا: النوع = sp.lambda_ (وحدة sp.unit، وحدة sp.unit، مع _العمليات = صحيح)

 فئة MULTISIG Lambda (عقد SP):
 """يصوت العديد من الأعضاء لتنفيذ lambdas.

        يمكن إنشاء هذا العقد بقائمة العناوين وعدد من
 الأصوات المطلوبة. يمكن لأي عضو تقديم أكبر قدر ممكن من لامدا والتصويت
 للحصول على مقترحات نشطة. عندما تصل لامدا إلى الأصوات المطلوبة، يكون رمزها
 تم استدعاؤها ويتم تنفيذ عمليات الإخراج. هذا يسمح لهذا العقد بـ
 القيام بأي شيء يمكن أن يفعله العقد: نقل الرموز، وإدارة الأصول،
 إدارة عقد آخر...

        عند تطبيق لامدا، يتم تعطيل جميع لامدا المقدمة حتى الآن.
        لا يزال بإمكان الأعضاء تقديم لامدا جديدة.
        """

 def __init__(الذات، الأعضاء، التصويتات المطلوبة):
 """المُنشئ

 الفنون:
 الأعضاء (sp.set أو sp.address): الأشخاص الذين يمكنهم الإرسال والتصويت
 من أجل لامدا.
                الأصوات المطلوبة (sp.nat): عدد الأصوات المطلوبة
 """
 تأكيد التصويتات < المطلوبة= sp.len (
 أعضاء
 )، يجب أن " تكون الأصوات المطلوبة < = len (الأعضاء) "
 البيانات الذاتية.lambdas = sp.cast (
                sp.big_map ()، sp.big_map [sp.nat، عملية لامدا]
 )
 البيانات الذاتية.التصويتات = sp.cast (
                sp.big_map ()، sp.big_map [sp.nat، sp.set [عنوان sp.]
 )
 البيانات الذاتية. معرف التالي = 0
 البيانات الذاتية. غير نشطة من قبل = 0
 البيانات الذاتية.الأعضاء = sp.cast (الأعضاء، sp.set [عنوان sp.address])
            البيانات الذاتية. التصويتات المطلوبة = sp.cast (التصويتات المطلوبة، sp.nat)

 @sp. نقطة الدخول
 تعريف submit_lambda (الذات، لامدا _):
 """قم بتقديم لامدا جديدة للتصويت.

            إن تقديم اقتراح لا يعني التصويت لصالحه.

            الفنون:
 lambda_ (sp.lambda مع العمليات): اقترحت لامدا التصويت.
            يرفع:
 «أنت لست عضوًا»
 """
 تأكيد self.data.members.contains (sp.sender)، "أنت لست عضوًا "
 self.data.lambdas [self.data.nextid] = لامدا_
 self.data.votes [self.data.nextid] = sp.set ()
            البيانات الذاتية. المعرف التالي += 1

 @sp. نقطة الدخول
 تعريف vote_lambda (الذات، المعرف):
 """صوّت لصالح لامدا.

            الفنون:
 معرف (sp.nat): غطاء لامدا للتصويت له.
            يرفع:
 «أنت لست عضوًا»، «لامدا غير نشطة»، «لامدا غير موجودة»

 لا يوجد تصويت ضد أو تمرير. إذا كان شخص ما لا يوافق على لامدا
 يمكنهم تجنب التصويت.
            """
 تأكيد self.data.members.contains (sp.sender)، "أنت لست عضوًا "
 معرف التأكيد > = self.data.غير نشط من قبل، "لامدا غير نشطة "
 تأكيد self.data.lambdas. يحتوي على (معرف)، "لم يتم العثور على لامدا "
 self.data.votes [id] .add (sp.sender)
            إذا كان sp.len (self.data.votes [id]) >= البيانات الذاتية. التصويتات المطلوبة:
                self.data.lambdas [id] ()
                البيانات الذاتية. غير نشطة من قبل = البيانات الذاتية. المعرف التالي

 @sp .onchain_view ()
 تعريف get_lambda (الذات، المعرف):
 """قم بإرجاع لامدا المقابلة.

            الفنون:
 id (sp.nat): قم بتخليص لامدا للحصول عليه.

            الإرجاع:
 زوج من لامدا وبوليان يوضحان ما إذا كانت لامدا نشطة.
            """
 إرجاع (self.data.lambdas [id]، id > = البيانات الذاتية. غير نشطة من قبل)


# إذا لم " تكن " القوالب موجودة في __name__:


@sp. وحدة
اختبار def ():
 الفئة المُدارة (SP.Contract):
 def __init__(الذات، المشرف):
 البيانات الذاتية.admin = المشرف
 قيمة البيانات الذاتية = sp.int (0)

        @sp. نقطة الدخول
 قيمة def set_value (الذات، القيمة):
 تأكيد sp.sender = self.data.admin
 البيانات الذاتية.القيمة = القيمة


@sp .add_test (الاسم = سيناريو Lambda الأساسي " متعدد الإشارات، is_default= صحيح) "
سيناريو التعريف الأساسي ():
 """استخدم MultiSiglambda كمسؤول عن عقد نموذجي.

    الاختبارات:
 - النشأة
 - تقديم لامدا
 - تصويت لامدا
 """
 sc = سيناريو sp.test_( [رئيسي، اختبار])
 sc.h1 (السيناريو " الأساسي. ")

    العضو 1 = حساب sp.test_( العضو 1) " "
    العضو 2 = حساب sp.test_( العضو 2) " "
    العضو 3 = حساب sp.test_( العضو 3) " "
    الأعضاء = sp.set ([العضو 1. العنوان، عنوان العضو 2، عنوان العضو 3])

    sc.h2 (لامدا " متعددة الإشارات: النشأة) "
 c1 = لامدا الرئيسية متعددة اللغات (الأعضاء، 2)
 اس سي += ج1

 sc.h2 ("المُدار: الإنشاء) "
 c2 = الاختبار. المُدار (عنوان c1)
    اس سي += ج2

 sc.h2 (لامدا " متعددة الإشارات: submit_lambda) "

 معرف set_42 (المعلمات):
 تتم إدارته = sp.contract (sp.tint، c2. address، نقطة الإدخال = set_value) " "
 نقل البيانات (sp.int (42)، sp.tez (0)، المُدار. open_some ())

    لامدا_ = sp.build_lambda (set_42، مع _العمليات = صحيح)
 c1. submit_lambda (lambda_) .run (المرسل = العضو 1)

 sc.h2 (لامدا " متعددة اللغات: vote_lambda) "
 c1. vote_lambda (0) .run (المرسل = العضو 1)
    c1. vote_lambda (0) .run (المرسل = العضو 2)

    # يمكننا التحقق من أن العقد المُدار قد استلم التحويل.
    sc.verify (c2.data.value = 42)

عقد التوقيع المتعدد

يقدم مفهوم التصويت على المقترحات. في هذا العقد، يمكن للموقعين التصويت على إجراءات معينة يجب اتخاذها، وإذا تم الوصول إلى النصاب القانوني، يتم تنفيذ الإجراءات المقترحة.

الثعبان
الاستيراد بذكاء كسبا


@sp. وحدة
تعريف رئيسي ():
 # مواصفات نوع إجراء الإدارة الداخلية
 إجراء الإدارة الداخلية: النوع = sp.variant (
        إضافة الموقّعين = sp.list [sp.address]،
        تغيير النصاب القانوني = SP.NAT،
        إزالة التوقيعات = sp.list [sp.address]،
    )

 التوقيع المتعدد للفئة (عقد SP):
 """عقد يمكن استخدامه من قبل العديد من الموقعين لإدارة الآخرين
 عقود. تقوم العقود المُدارة بتنفيذ واجهة تجعلها
 من الممكن توضيح عملية الإدارة للمستخدمين غير الخبراء.

        يصوت الموقعون على المقترحات. الاقتراح هو قائمة الهدف مع قائمة بـ
 عمل. الإجراء عبارة عن بايت بسيط ولكن الغرض منه أن يكون قيمة حزمة لـ
 متغير. هذا النمط البسيط يجعل من الممكن بناء واجهة UX
 يعرض محتوى الاقتراح أو البناء.
        """

 الصم __init__(الرف، النصاب، الموقعون):
 البيانات الذاتية. غير نشطة من قبل = 0
 البيانات الذاتية. معرف التالي = 0
 البيانات الذاتية. المقترحات = sp.cast (
                sp.big_map ()،
 خريطة sp.big [
 sp.nat،
 sp.list [sp.record (الهدف = عنوان sp.sp، الإجراءات = sp.list [sp.bytes])]،
                ]،
 )
 self.data.quorum = sp.cast (النصاب القانوني، sp.nat)
 self.data.signers = sp.cast (الموقعون، sp.set [عنوان sp.address])
            البيانات الذاتية.التصويتات = sp.cast (
                sp.big_map ()، sp.big_map [sp.nat، sp.set [عنوان sp.]
 )

 @sp. نقطة الدخول
 ديف send_proposal (الذات، الاقتراح):
 """الموقّع فقط. قم بتقديم اقتراح للتصويت.

            الفنون:
 الاقتراح (sp.list أو sp.record للعنوان الهدف والإجراء): قائمة\
 من الإجراءات الإدارية المستهدفة والمرتبطة بها.
            """
 تأكيد self.data.signers.contains.contains (sp.sender)، "يمكن للموقعين فقط أن يقترحوا "
 مقترحات البيانات الذاتية [self.data.nextID] = اقتراح
 self.data.votes [self.data.nextid] = sp.set ()
            البيانات الذاتية. المعرف التالي += 1

 @sp. نقطة الدخول
 التصويت الذاتي (الذاتي، PID):
 """التصويت لمقترح واحد أو أكثر

 الفنون:
 رقم التعريف الشخصي (sp.nat): معرف الاقتراح.
            """
 تأكيد self.data.signers.contains.contains (sp.sender)، "يمكن للموقعين فقط التصويت "
 تأكيد self.data.votes. يحتوي على (معرف التعريف الشخصي)، "اقتراح غير معروف "
 تأكيد pID > = self.data.غير نشط من قبل، "الاقتراح غير نشط "
 البيانات الذاتية.التصويتات [pID] .add (sp.sender)

            إذا كان sp.len (self.data.votes.get) (معرف التعريف الشخصي)، الإعداد الافتراضي = sp.set ())) >= البيانات الذاتية.النصاب القانوني:
                الذات. _ غير معتمد (PID)

        @sp .private (مع_التخزين = القراءة والكتابة، مع _العمليات = صحيح") "
 def _onApproved (ذاتي، معرف التعريف الشخصي):
 """وظيفة مضمنة. يتم تطبيق المنطق عند الموافقة على الاقتراح. """
            الاقتراح = self.data.proposals.get (معرف التعريف الشخصي، الإعداد الافتراضي = [])
 بالنسبة إلى p_item في الاقتراح:
 العقد = sp.contract (sp.list [sp.bytes]، p_item.target)
                نقل البيانات (
 p_item. الإجراءات،
                    ملعقة صغيرة (0)،
 contract.unwrap_some (خطأ = هدف غير صالح)، " "
                )
 # قم بإلغاء تنشيط جميع المقترحات التي تم تقديمها بالفعل.
            البيانات الذاتية. غير نشطة من قبل = البيانات الذاتية. المعرف التالي

 @sp. نقطة الدخول
 مسؤول الدفاع (الذات، الإجراءات):
 """الاتصال الذاتي فقط. قم بإدارة هذا العقد.

            يجب استدعاء نقطة الدخول هذه من خلال نظام الاقتراح.

            الفنون:
 الإجراءات (sp.list من sp.bytes): قائمة المتغيرات المعبأة لـ\
 «الإدارة الداخلية» («إضافة الموقعين»، «تغيير النصاب القانوني»، «إزالة الموقعين»).
            """
 يجزم (
 sp.sender == sp.self_address ()
 )، يجب استدعاء نقطة الدخول " هذه من خلال نظام الاقتراح. "

 لإجراءات_packed_actions في الإجراءات:
 الإجراء = sp.unpack (الإجراءات_المعبأة)، إجراء الإدارة الداخلية) .unwrap_some (
 خطأ = تنسيق الإجراءات " السيئة "
 )
 مع sp.match (الإجراء):
 مع SP.Case. تغيير النصاب كنصاب قانوني:
 البيانات الذاتية.النصاب = النصاب القانوني
 مع الموقّعين على Sp.case.adds كما تمت إضافتهم:
 للموقّع أضاف:
 self.data.signers.add (الموقّع)
                    مع حقيبة SP. قم بإزالة التوقيعات كما تمت إزالته:
 بالنسبة للعنوان الذي تمت إزالته:
 self.data.signers.remove (العنوان)
                # تأكد من أن العقد لا يتطلب أبدًا النصاب القانوني أكثر من إجمالي الموقعين.
                تأكيد البيانات الذاتية.النصاب = sp.len (<
 توقيعات البيانات الذاتية
 )، النصاب القانوني " أكثر من الموقعين. "


إذا لم " تكن " القوالب موجودة في __name__:

 @sp .add_test (الاسم = السيناريو " الأساسي، is_default=صحيح) "
 اختبار def ():
 الموقّع 1 = sp.test_account (الموقّع 1) " "
        الموقّع 2 = sp.test_account (الموقّع 2) " "
        الموقّع 3 = sp.test_account (الموقّع 3) " "

        s = سيناريو sp.test_( رئيسي)
        s.h1 (السيناريو " " الأساسي)

 s.h2 ("الإنشاء) "
 c1 = تعدد الإشارات الرئيسي (
 النصاب القانوني =2،
 الموقعون = sp.set ([الموقع 1. العنوان، الموقع 2. العنوان])،
 )
 s += c1

 s.h2 ("اقتراح لإضافة موقّع جديد) "
 الهدف = sp.to_address (
            sp.contract (sp.tlist (sp.tbytes)، c1. العنوان، المسؤول) .open_some () " "
 )
 الإجراء = حزمة sp.pack (
            sp.set_type_expr (
 sp.variant ("يضيف الموقّعين، [signer3. " address])، الرئيسية - إجراءات الإدارة الداخلية
 )
 )
 c1.send_proposal ([sp.record) (الهدف = الهدف، الإجراءات = [الإجراء])]) .run (
 المرسل=الموقّع 1
 )

 s.h2 (صوت " الموقّع 1 لصالح الاقتراح) "
 c1. التصويت (0). run (المرسل = الموقّع 1)
        s.h2 (صوت " الموقّع 2 لصالح الاقتراح) "
 c1. التصويت (0). run (المرسل = الموقّع 2)

        s.verify (يحتوي c1.data.signers.على (الموقّع 3. العنوان))

عقد مولتيسيغفيو

كما أنها تستخدم آلية التصويت. يسمح هذا العقد للأعضاء بإرسال وحدات البايت العشوائية والتصويت عليها. بمجرد أن يحقق الاقتراح العدد المطلوب من الأصوات، يمكن تأكيد حالته من خلال العرض.

الثعبان
الاستيراد بذكاء كسبا


@sp. وحدة
تعريف رئيسي ():
 عرض متعدد الأشكال من الفئة (عقد SP):
 """يصوت العديد من الأعضاء لوحدات البايت العشوائية.

        يمكن إنشاء هذا العقد بقائمة العناوين وعدد من
 الأصوات المطلوبة. يمكن لأي عضو إرسال أكبر عدد من وحدات البايت كما يريد والتصويت
 للحصول على مقترحات نشطة.

        يمكن تأكيد أي وحدات بايت وصلت إلى الأصوات المطلوبة من خلال العرض.
        """

 def __init__(الذات، الأعضاء، التصويتات المطلوبة):
 """المُنشئ

 الفنون:
 الأعضاء (sp.set أو sp.address): الأشخاص الذين يمكنهم التقديم والتصويت لصالح
 لامدا.
                الأصوات المطلوبة (sp.nat): عدد الأصوات المطلوبة
 """
 تأكيد التصويتات < المطلوبة= sp.len (
 أعضاء
 )، يجب أن " تكون الأصوات المطلوبة < = len (الأعضاء) "
 مقترحات البيانات الذاتية = sp.cast (sp.big_map ()، خريطة sp.big_map [sp.bytes، sp.bool])
 البيانات الذاتية. الأصوات = sp.cast (
                sp.big_map ()، sp.big_map [sp.bytes، sp.set [عنوان sp.]
 )
 self.data.members = sp.cast (الأعضاء، sp.set [عنوان sp.address])
            البيانات الذاتية. التصويتات المطلوبة = sp.cast (التصويتات المطلوبة، sp.nat)

 @sp. نقطة الدخول
 تعريف submit_proposal (الذات، وحدات البايت):
 """قم بتقديم اقتراح جديد للتصويت.

            إن تقديم اقتراح لا يعني التصويت لصالحه.

            الفنون:
 بايت (sp.bytes): وحدات البايت المقترحة للتصويت.
            يرفع:
 «أنت لست عضوًا»
 """
 تأكيد self.data.members.contains (sp.sender)، "أنت لست عضوًا "
 مقترحات البيانات الذاتية [بايت] = خطأ
 self.data.votes [بايت] = sp.set ()

        @sp. نقطة الدخول
 تعريف vote_proposal (الذات، وحدات البايت):
 """التصويت على اقتراح.

            لا يوجد تصويت ضد أو تمرير. إذا لم يوافق المرء على الاقتراح، فإنهم
 يمكن تجنب التصويت. تحذير: المقترحات القديمة التي لم يتم التصويت عليها لن تصبح أبدًا
 عفا عليها الزمن.

            الفنون:
 معرف (sp.bytes): بايت من الاقتراح.
            يرفع:
 «أنت لست عضوًا»، «لم يتم العثور على الاقتراح»
 """
 تأكيد self.data.members.contains (sp.sender)، "أنت لست عضوًا "
 تأكيد self.data.proposals.contains (بايت)، "لم يتم العثور على الاقتراح "
 self.data.votes [بايت] .add (sp.sender)
            إذا كان sp.len (self.data.votes [بايت]) >= البيانات الذاتية. التصويتات المطلوبة:
                مقترحات البيانات الذاتية [بايت] = صحيح

 @sp .onchain_view ()
 تعريف is_voted (الذات، المعرف):
 """تُرجع قيمة منطقية تشير إلى ما إذا كان قد تم التصويت على الاقتراح.

            الفنون:
 id (sp.bytes): بايت من الاقتراح
 الإرجاع:
 (sp.bool): صحيح إذا تم التصويت على الاقتراح، خطأ خلاف ذلك.
            """
 إرجاع البيانات الذاتية.proposals.get (المعرف)، خطأ = لم يتم العثور " على " الاقتراح)


إذا لم " تكن " القوالب موجودة في __name__:

 @sp .add_test (الاسم = السيناريو الأساسي لـ " MultiSigView، is_default= صحيح) "
 سيناريو التعريف الأساسي ():
 """سيناريو مع التصويت على عقد MultiSigView.

        الاختبارات:
 - النشأة
 - تقديم الاقتراح
 - التصويت على الاقتراح
 """
 sc = سيناريو sp.test_( رئيسي)
        sc.h1 (السيناريو " الأساسي. ")

        العضو 1 = حساب sp.test_( العضو 1) " "
        العضو 2 = حساب sp.test_( العضو 2) " "
        العضو 3 = حساب sp.test_( العضو 3) " "
        الأعضاء = sp.set ([العضو 1. العنوان، عنوان العضو 2، عنوان العضو 3])

        sc.h2 (الإنشاء) " "
 c1 = العرض الرئيسي متعدد المواقع (الأعضاء، 2)
 اس سي += ج1

 sc.h2 (تقديم اقتراح) " "
 c1.submit_proposal (sp.bytes (""0x42)) .run (المرسل = العضو 1)

 sc.h2 (اقتراح التصويت) " "
 c1. vote_proposal (sp.bytes (0x42)) .run ("المرسل = العضو 1) "
 c1. vote_proposal (sp.bytes (0x42)) .run (المرسل = العضو 2) " "

 # يمكننا التحقق من صحة الاقتراح.
        sc.verify (c1.is_voted (sp.bytes (0x42))) " "

يوفر كل عقد آلية مختلفة لتحقيق التحكم في التوقيعات المتعددة، مما يوفر المرونة اعتمادًا على الاحتياجات المحددة لحالة استخدام blockchain الخاصة بك.

دليل خطوة بخطوة لتجربة العقد متعدد التوقيع على SmartPay عبر الإنترنت

لتجربة العقود متعددة التوقيع التي كتبناها في SmartPy، يمكنك اتباع الخطوات التالية:

  1. انتقل إلى واجهة برمجة تطبيقات SmartPY على https://smartpy.io/ide.

  2. قم بلصق كود العقد في المحرر. يمكنك استبدال الكود الموجود.

  3. لتنفيذ العقد، انقر فوق الزر «تشغيل» الموجود في اللوحة العلوية.

  4. بعد تشغيل العقد، يمكنك عرض تنفيذ السيناريو في لوحة «الإخراج» على اليمين. هنا، يمكنك الاطلاع على تفاصيل كل إجراء، بما في ذلك المقترحات والتصويتات والموافقات.

  5. لنشر العقد الخاص بك على شبكة Tezos، تحتاج أولاً إلى تجميعه. انقر فوق الزر «تجميع» في اللوحة العلوية.

  6. بعد التجميع، يمكنك نشر العقد على شبكة الاختبار بالنقر فوق «نشر عقد Michelson». ستحتاج إلى توفير مفتاح سري لحساب Tezos بأموال كافية لدفع تكاليف الغاز للنشر.

  7. بمجرد نشر العقد، سيتم تزويدك بعنوان العقد على بلوكتشين. يمكنك استخدام هذا العنوان للتفاعل مع العقد عبر المعاملات.

  8. لإرسال مقترحات أو التصويت على العقود، يمكنك استخدام نقاط الدخول المحددة في كود العقد، مثل submit_posal أو vote_posal. يمكن استدعاؤها مباشرة من المعاملات التي تقوم بإنشائها.

تذكر أنه في حين أن SmartPY IDE يسمح لك باختبار عقدك على بلوكتشين مُحاكاة، فإن نشر العقد على شبكة Tezos الفعلية سيؤدي إلى تكبد تكاليف الغاز، والتي يجب دفعها بعملة XTZ، وهي العملة المشفرة الأصلية لشبكة Tezos.

إخلاء المسؤولية
* ينطوي الاستثمار في العملات الرقمية على مخاطر كبيرة. فيرجى المتابعة بحذر. ولا تهدف الدورة التدريبية إلى تقديم المشورة الاستثمارية.
* تم إنشاء الدورة التدريبية من قبل المؤلف الذي انضم إلى مركز التعلّم في Gate. ويُرجى العلم أنّ أي رأي يشاركه المؤلف لا يمثّل مركز التعلّم في Gate.